Skip to content
Snippets Groups Projects
Commit c7a0276a authored by imcovangent's avatar imcovangent
Browse files

Merge branch 'CPACS_MCG__Andreas' of https://bitbucket.org/imcovangent/kadmos...

Merge branch 'CPACS_MCG__Andreas' of https://bitbucket.org/imcovangent/kadmos into CPACS_MCG__Andreas

# Conflicts:
#	.idea/workspace.xml
#	pyKADMOS/sellarProblem.py
parents a05b5b1e 6dcfdd9d
No related branches found
No related tags found
No related merge requests found
{"general_info":{ "name":"EMWET",
"type": "tool",
"version":1.0,
"creator":"Ali Elham",
"description": "provide description here."},
"executiion_info":{ "run time (s)":20,
"execution_info":{ "run time (s)":20,
"fidelity level":"L1",
"precision":0.05}
}
......
{"general_info":{ "name":"Aircraft aerodynamic performance",
{"general_info":{ "name":"Q3D_FLC",
"type": "tool",
"version":1.0,
"creator":"Mengmeng Zhang",
"description": "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 with other tools. A tool that fits well to this analysis is TUD's wing weight estimation tool EMWET."},
"executiion_info":{ "run time (s)":20,
"execution_info":{ "run time (s)":20,
"fidelity level":"L1",
"precision":0.05}
}
......
......@@ -46,15 +46,16 @@ class MDOproblem:
self._get_data_schema(KB_files)
# Get input and output files, save in instance
self._get_In_Out_Files(KB_files)
self._get_in_out_files(KB_files)
# Get Read-Write File, save in instance
self._get_base_file(KB_files)
if self.knowledge_base == "KB_CPACS":
# Get Read-Write File, save in instance
self._get_base_file(KB_files)
ignoreNodes = ['toolspecific']
self._check_base_against_schema(ignoreNodes)
ignoreNodes = ['toolspecific']
self._check_base_against_schema(ignoreNodes)
self._validate_InOutXmls()
self._validate_in_out_files()
self._get_function_data()
......@@ -84,7 +85,7 @@ class MDOproblem:
return
def _get_In_Out_Files(self, kb_files):
def _get_in_out_files(self, kb_files):
"""
This function saves all files in class instance. It ensures that all names
:param: kb_files
......@@ -109,12 +110,13 @@ class MDOproblem:
with open(self.knowledge_base + '/' + file) as info:
infoData = json.load(info)
# name assertion
if not isinstance(infoData["general_info"]["name"], basestring):
raise TypeError("Function name in {} must be a string.".format(file))
if len(infoData["general_info"]["name"]) < 1:
raise ValueError("Function name in {} must be non-empty string.".format(file))
# name assertion
if not isinstance(infoData["general_info"]["name"], basestring):
raise TypeError("Function name in {} must be a string.".format(file))
if len(infoData["general_info"]["name"]) < 1:
raise ValueError("Function name in {} must be non-empty string.".format(file))
# TODO: incorporte tool type for sellar and simple problems!
# # type assertion
# typeCond = False
# if not isinstance(infoData["general_info"]["type"], basestring):
......@@ -206,7 +208,7 @@ class MDOproblem:
return
def _validate_InOutXmls(self):
def _validate_in_out_files(self):
"""
Class method that validates all present input and output XML files by comparing each child node for
equvalence in base file.
......@@ -217,6 +219,8 @@ class MDOproblem:
leafNodesMissing = {}
baseTree = etree.parse(self.knowledge_base + '/' + self.baseFile)
print "Validating input and output XML files..."
fileType = ['input', 'output'] #
for type in fileType:
for xml_file in self.function_files[type]:
......@@ -239,8 +243,7 @@ class MDOproblem:
leafNodesMissing[xml_file] = []
leafNodesMissing[xml_file].append(fileTree.getpath(el))
#print "\n".join(str(i) for i in leafNodesMissing)
self.printNodes(leafNodesMissing)
self._printNodes(leafNodesMissing)
assert len(leafNodesMissing) == 0, "There are missing nodes in the base file!"
def _ensureEquivalentAncestors(self, treeNode, baseNode):
......@@ -271,22 +274,23 @@ class MDOproblem:
eq = True
return eq
def printNodes(self, obj):
def _printNodes(self, obj):
if type(obj) == dict:
for k, v in obj.items():
if hasattr(v, '__iter__'):
print k
self.printNodes(v)
self._printNodes(v)
else:
print '%s : %s' % (k, v)
elif type(obj) == list:
for v in obj:
if hasattr(v, '__iter__'):
self.printNodes(v)
self._printNodes(v)
else:
print v
else:
print obj
# TODO: include where this is from
def _get_function_data(self):
""""
......@@ -301,7 +305,7 @@ class MDOproblem:
,
"input": {
"allXpaths": [ {"xpath": str, "tag": str, "attributes": dict}, ... ], # list of all xpaths
"leafNodes": [ {"xpath": str, "tag": str, "value": str, "level": int}, ...] # list of all leafNodes
"leafNodes": [ {"xpath": str, "tag": str, "attributes": dict, "value": str, "level": int}, ...] # list of all leafNodes
},
"output": {
......@@ -325,8 +329,8 @@ class MDOproblem:
}
}
# get info file and add all the necessary info
# TODO: enforce naming conventions for general_info and execution_info; now only matching names included!
with open(self.knowledge_base + '/' + file) as info:
infoData = json.load(info)
......@@ -379,7 +383,7 @@ class MDOproblem:
if el.text is not None:
d2['value'] = el.text.strip()
else:
d2['value'] = el.text
d2['value'] = el.text # adding None if empty
d2['level'] = path.count('/') -1
dic['leafNodes'].append(d2)
......@@ -408,6 +412,7 @@ class MDOproblem:
return function_names
def analyze_function_files(self, print_in_log=True):
# TODO: This function should be phased out, use self.functionData instead!
"""
Function that analyzes all tool files (input, output, info) and returns all the xpaths in the xml file plus a
dictionary with the nodes that have a value.
......@@ -538,6 +543,94 @@ class MDOproblem:
idx += 1
return common_nodes
def _get_function_graph(self, functionName, inOut=None):
"""
This function builds a directed graph (object) for the specified function using the "networkx" package.
:param: functionName: function name for which the graph is generated; must be present in knowledge base.
:param: inOut: default = None; if specified, must be "input" or "output" string. Specification of this argument enables the generation of the function graph with only inout or output variables.
:return: functionGraph
"""
assert isinstance(functionName, basestring), 'Provided function name must be a string!'
# assert funcName exists and get index of function in self.functionData list
funcIndex = None
for idx, funcDict in enumerate(self.functionData):
if funcDict['info']['generalInfo']['name'] == functionName:
funcIndex = idx #funcIndex is index of the function in list
break
assert funcIndex is not None, "The provided function name can not be found in knowledge base."
# assert inOut, if defined, is string and either input or output
if inOut is not None:
assert isinstance(inOut, basestring), "inOut argument must be a string if specified."
assert inOut.lower() in ["input", "output"], "inOut argument must be either 'input' or 'output'."
# initiate directed graph and list of edges
DG, edges = nx.DiGraph(), []
# add edges to list, then to graph
if inOut is not None:
edges += self._create_edge_tuples(funcIndex, inOut, functionName)
else:
for io in ['input', 'output']:
edges += self._create_edge_tuples(funcIndex, io, functionName)
DG.add_edges_from(edges)
# add node attributes to graph
self._add_node_attribs(DG, funcIndex)
return DG
def _create_edge_tuples(self, funcIndex, inOut, functionName):
"""
This function creates a list of edge tuples in order to generate a graph.
:param funcIndex: index of function in list of tool dicts in self.functionData
:param inOut: specified whether input or output nodes, None adds all to graph
:return: graphEdges: list of edges to build graph
"""
graphEdges = []
fdata = self.functionData[funcIndex]
for leafNode in fdata[inOut.lower()]['leafNodes']:
if inOut == 'input':
tpl = (leafNode['xpath'], functionName) # (variable, tool)
else:
tpl = (functionName, leafNode['xpath']) # (tool, variable)
graphEdges.append(tpl)
# TODO: adjust these loops to include additional information in edges! check doc on networkx!
return graphEdges
def _add_node_attribs(self, G, funcIndex):
"""
Function that add node attributes to the nodes of the graph.
:param G: Considered graph
:param funcIndex: index of function in list of tool dicts in self.functionData
:return: Graph
"""
# TODO >>>>>> CONITNUE HERE <<<<<<<<
for node, data in G.nodes_iter(data=True):
if node == function_input_analysis['properties'][0]['attributes']['tool_name']: # replace this, rest looks okay!
G.node[node]['shape'] = 's' # square for functions
G.node[node]['category'] = 'function'
G.node[node]['label'] = function_input_analysis['properties'][0]['attributes']['tool_name']
G.node[node]['level'] = None
with open(self.knowledge_base + '/' + json_file_info) as data_file:
if 'execution time' in G.node[node]:
G.node[node]['execution time'] = int(1000 * json.load(data_file)['executing_info']
['run time (s)'])
else:
G.node[node]['execution time'] = 1
else:
G.node[node]['shape'] = 'o' # circle for variables
G.node[node]['category'] = 'variable'
G.node[node]['label'] = node.split('/')[-1]
G.node[node]['level'] = node.count('/') - 1
G.node[node]['execution time'] = 1
return G
def get_function_graph(self, function_name):
"""
Function to automatically create the digraph of the function element.
......@@ -552,9 +645,7 @@ class MDOproblem:
function_input_analysis = self.analyze_function_xml(xml_file_input)
function_output_analysis = self.analyze_function_xml(xml_file_output)
input_edges = [(function_input_analysis['valued_nodes'][i]['xpath'],
function_input_analysis['properties'][0]['attributes']['tool_name'])
for i in range(len(function_input_analysis['valued_nodes']))]
input_edges = [(function_input_analysis['valued_nodes'][i]['xpath'], function_input_analysis['properties'][0]['attributes']['tool_name']) for i in range(len(function_input_analysis['valued_nodes']))]
output_edges = [(function_output_analysis['properties'][0]['attributes']['tool_name'],
function_output_analysis['valued_nodes'][i]['xpath'])
for i in range(len(function_output_analysis['valued_nodes']))]
......@@ -594,6 +685,8 @@ class MDOproblem:
graph_list.append(self.get_function_graph(function_name))
return graph_list
# TODO: >>>>>>>>>>>>>>>>>> Cut the class here <<<<<<<<<<<<<<<<<<<<<<
def get_MCG(self):
"""
Function to create Maximal Connectivity Graph (Pate, 2014) by composing a list of graphs.
......
......@@ -282,6 +282,7 @@ def find_all_nodes(graph, category='all', subcategory='all', attr_cond=None, pri
>>> print_in_log=True)
"""
# Assert inputs
# TODO: We should collect these in a centralized place (head of parent class, for example) to make sure to find them quickly
possible_categories = ['all','variable','variable group','function','architecture element']
possible_subcategories = ['all','all inputs','all outputs','all couplings','all problematic nodes',
'all PSG blocks','all design variables', 'hole',
......
......@@ -4,5 +4,8 @@ from pyKADMOS.MDOproblem import MDOproblem
exProb = MDOproblem('KB_CPACS')
graph3 = exProb._get_function_graph('EMWET')
print "ALL GOOD!!!"
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment