def prof_view(): """Called from a command line to generate an html viewer for profile data.""" parser = argparse.ArgumentParser() parser.add_argument('--noshow', action='store_true', dest='noshow', help="Don't pop up a browser to view the data.") parser.add_argument('-t', '--title', action='store', dest='title', default='Profile of Method Calls by Instance', help='Title to be displayed above profiling view.') parser.add_argument('rawfiles', metavar='rawfile', nargs='*', help='File(s) containing raw profile data to be processed. Wildcards are allowed.') options = parser.parse_args() if not options.rawfiles: print("No files to process.") sys.exit(0) call_graph, totals = process_profile(options.rawfiles) viewer = "icicle.html" code_dir = os.path.dirname(os.path.abspath(__file__)) with open(os.path.join(code_dir, viewer), "r") as f: template = f.read() graphjson = json.dumps(call_graph) outfile = 'profile_' + viewer with open(outfile, 'w') as f: f.write(Template(template).substitute(call_graph_data=graphjson, title=options.title)) if not options.noshow: webview(outfile)
def prof_view(): """Called from a command line to generate an html viewer for profile data.""" parser = argparse.ArgumentParser() parser.add_argument('--noshow', action='store_true', dest='noshow', help="Don't pop up a browser to view the data.") parser.add_argument('-t', '--title', action='store', dest='title', default='Profile of Method Calls by Instance', help='Title to be displayed above profiling view.') parser.add_argument( 'rawfiles', metavar='rawfile', nargs='*', help= 'File(s) containing raw profile data to be processed. Wildcards are allowed.' ) options = parser.parse_args() if not options.rawfiles: print("No files to process.") sys.exit(0) call_graph, totals = process_profile(options.rawfiles) viewer = "icicle.html" code_dir = os.path.dirname(os.path.abspath(__file__)) with open(os.path.join(code_dir, viewer), "r") as f: template = f.read() graphjson = json.dumps(call_graph) outfile = 'profile_' + viewer with open(outfile, 'w') as f: f.write( Template(template).substitute(call_graph_data=graphjson, title=options.title)) if not options.noshow: webview(outfile)
def view_model(problem_or_filename, outfile='visualization', show_browser=True): """ Generates a directory containing a tree viewer Optionally pops up a web browser to view the file. Parameters ---------- problem_or_filename : Either a Problem() or a string Problem() : The Problem (after problem.setup()) for the desired tree. string : The filename of the case recorder file containing the data required to build the tree. outfile : str, optional The path of the output folder. Defaults to current directory + 'visualization'. show_browser : bool, optional If True, pop up the system default web browser to view the generated html file. Defaults to True. """ cur_dir = os.getcwd() if outfile == 'visualization': outfile = os.path.join(cur_dir, outfile) model_data_filename = 'model_data.js' folder_name = outfile code_dir = os.path.dirname(os.path.abspath(__file__)) model_viewer_data = _get_viewer_data(problem_or_filename) model_data = json.dumps(model_viewer_data) if os.path.isdir(folder_name): shutil.rmtree(folder_name) shutil.copytree(src=code_dir + '/visualization', dst=folder_name) model_data_dir = os.path.join(folder_name, model_data_filename) with open(model_data_dir, 'w') as f: f.write('var modelData = %s' % model_data) if show_browser: from openmdao.devtools.webview import webview view_path = os.path.join(cur_dir, folder_name) view_path = os.path.join(view_path, 'index.html') webview(view_path)
def view_connections(root, outfile='connections.html', show_browser=True, src_filter='', tgt_filter='', precision=6): """ Generates a self-contained html file containing a detailed connection viewer. Optionally pops up a web browser to view the file. Args ---- root : system or Problem The root for the desired tree. outfile : str, optional The name of the output html file. Defaults to 'connections.html'. show_browser : bool, optional If True, pop up a browser to view the generated html file. Defaults to True. src_filter : str, optional If defined, use this as the initial value for the source system filter. tgt_filter : str, optional If defined, use this as the initial value for the target system filter. precision : int, optional Sets the precision for displaying array values. """ # since people will be used to passing the Problem as the first arg to # the N2 diagram funct, allow them to pass a Problem here as well. if isinstance(root, Problem): system = root.root else: system = root connections = system._probdata.connections to_prom = system._sysdata.to_prom_name src2tgts = {} units = {n: m.get('units','') for n,m in chain(iteritems(system._unknowns_dict), iteritems(system._params_dict))} vals = {} with printoptions(precision=precision, suppress=True, threshold=10000): for t, tmeta in iteritems(system._params_dict): if t in connections: s, idxs = connections[t] if idxs is not None: val = system.unknowns[to_prom[s]][idxs] else: val = system.unknowns[to_prom[s]] # if there's a unit conversion, express the value in the # units of the target if 'unit_conv' in tmeta: scale, offset = tmeta['unit_conv'] val = (val + offset) * scale if s not in src2tgts: src2tgts[s] = [t] else: src2tgts[s].append(t) else: # unconnected param val = system._params_dict[t]['val'] if isinstance(val, numpy.ndarray): val = numpy.array2string(val) else: val = str(val) vals[t] = val noconn_srcs = sorted((n for n in system._unknowns_dict if n not in src2tgts), reverse=True) for s in noconn_srcs: vals[s] = str(system.unknowns[to_prom[s]]) vals['NO CONNECTION'] = '' src_systems = set() tgt_systems = set() for s in src2tgts: parts = s.split('.') for i in range(len(parts)): src_systems.add('.'.join(parts[:i])) for t in connections: parts = t.split('.') for i in range(len(parts)): tgt_systems.add('.'.join(parts[:i])) # reverse sort so that "NO CONNECTION" shows up at the bottom src2tgts['NO CONNECTION'] = sorted([t for t in to_prom if t not in system._unknowns_dict and t not in connections], reverse=True) src_systems = [{'name':n} for n in sorted(src_systems)] src_systems.insert(1, {'name': "NO CONNECTION"}) tgt_systems = [{'name':n} for n in sorted(tgt_systems)] tgt_systems.insert(1, {'name': "NO CONNECTION"}) data = { 'src2tgts': [(s,ts) for s,ts in sorted(iteritems(src2tgts), reverse=True)], 'proms': to_prom, 'units': units, 'vals': vals, 'src_systems': src_systems, 'tgt_systems': tgt_systems, 'noconn_srcs': noconn_srcs, 'src_filter': src_filter, 'tgt_filter': tgt_filter, } viewer = 'connect_table.html' code_dir = os.path.dirname(os.path.abspath(__file__)) with open(os.path.join(code_dir, viewer), "r") as f: template = f.read() graphjson = json.dumps(data) with open(outfile, 'w') as f: s = template.replace("<connection_data>", graphjson) f.write(s) if show_browser: webview(outfile)
def view_connections(root, outfile='connections.html', show_browser=True): """ Generates a self-contained html file containing a detailed connection viewer. Optionally pops up a web browser to view the file. Args ---- root : system or Problem The root for the desired tree. outfile : str, optional The name of the output html file. Defaults to 'connections.html'. show_browser : bool, optional If True, pop up a browser to view the generated html file. Defaults to True. """ # since people will be used to passing the Problem as the first arg to # the N2 diagram funct, allow them to pass a Problem here as well. if isinstance(root, Problem): system = root.root else: system = root connections = system._probdata.connections to_prom = system._sysdata.to_prom_name src2tgts = {} units = { n: m.get('units', '') for n, m in chain(iteritems(system._unknowns_dict), iteritems(system._params_dict)) } sizes = {} for t, (s, idxs) in iteritems(connections): if idxs is not None: sizes[t] = len(idxs) else: sizes[t] = system._params_dict[t]['size'] if s not in src2tgts: src2tgts[s] = [t] else: src2tgts[s].append(t) src_systems = set() tgt_systems = set() for s in src2tgts: parts = s.split('.') for i in range(len(parts)): src_systems.add('.'.join(parts[:i])) for t in connections: parts = t.split('.') for i in range(len(parts)): tgt_systems.add('.'.join(parts[:i])) # reverse sort so that "NO CONNECTION" shows up at the bottom src2tgts['NO CONNECTION'] = sorted([ t for t in to_prom if t not in system._unknowns_dict and t not in connections ], reverse=True) src_systems = [{'name': n} for n in sorted(src_systems)] src_systems.insert(1, {'name': "NO CONNECTION"}) tgt_systems = [{'name': n} for n in sorted(tgt_systems)] tgt_systems.insert(1, {'name': "NO CONNECTION"}) data = { 'src2tgts': [(s, ts) for s, ts in sorted(iteritems(src2tgts), reverse=True)], 'proms': to_prom, 'units': units, 'sizes': sizes, 'src_systems': src_systems, 'tgt_systems': tgt_systems, 'noconn_srcs': sorted((n for n in system._unknowns_dict if n not in src2tgts), reverse=True), } viewer = 'connect_table.html' code_dir = os.path.dirname(os.path.abspath(__file__)) with open(os.path.join(code_dir, viewer), "r") as f: template = f.read() graphjson = json.dumps(data) with open(outfile, 'w') as f: s = template.replace("<connection_data>", graphjson) f.write(s) if show_browser: webview(outfile)
def view_connections(root, outfile='connections.html', show_browser=True, src_filter='', tgt_filter='', precision=6): """ Generates a self-contained html file containing a detailed connection viewer. Optionally pops up a web browser to view the file. Parameters ---------- root : system or Problem The root for the desired tree. outfile : str, optional The name of the output html file. Defaults to 'connections.html'. show_browser : bool, optional If True, pop up a browser to view the generated html file. Defaults to True. src_filter : str, optional If defined, use this as the initial value for the source system filter. tgt_filter : str, optional If defined, use this as the initial value for the target system filter. precision : int, optional Sets the precision for displaying array values. """ # since people will be used to passing the Problem as the first arg to # the N2 diagram funct, allow them to pass a Problem here as well. if isinstance(root, Problem): system = root.model else: system = root input_srcs = system._conn_global_abs_in2out connections = { tgt: src for tgt, src in iteritems(input_srcs) if src is not None } src2tgts = {} units = {n: data.get('units','') for n, data in iteritems(system._var_allprocs_abs2meta)} vals = {} with printoptions(precision=precision, suppress=True, threshold=10000): for t in system._var_abs_names['input']: tmeta = system._var_abs2meta[t] idxs = tmeta['src_indices'] flat = tmeta['flat_src_indices'] if idxs is None: idxs = np.arange(tmeta['size'], dtype=int) if t in connections: s = connections[t] val = system._outputs[s] if isinstance(val, np.ndarray): val = system._outputs[s].flatten()[idxs] else: val = system._outputs[s] # if there's a unit conversion, express the value in the # units of the target if units[t]: val = convert_units(val, units[s], units[t]) if s not in src2tgts: src2tgts[s] = [t] else: src2tgts[s].append(t) else: # unconnected param val = system._inputs[t] if isinstance(val, np.ndarray): val = np.array2string(val) else: val = str(val) vals[t] = val noconn_srcs = sorted((n for n in system._var_abs_names['output'] if n not in src2tgts), reverse=True) for s in noconn_srcs: vals[s] = str(system._outputs[s]) vals['NO CONNECTION'] = '' src_systems = set() tgt_systems = set() for s in system._var_abs_names['output']: parts = s.split('.') for i in range(len(parts)): src_systems.add('.'.join(parts[:i])) for t in system._var_abs_names['input']: parts = t.split('.') for i in range(len(parts)): tgt_systems.add('.'.join(parts[:i])) # reverse sort so that "NO CONNECTION" shows up at the bottom src2tgts['NO CONNECTION'] = sorted([t for t in system._var_abs_names['input'] if t not in connections], reverse=True) src_systems = [{'name':n} for n in sorted(src_systems)] src_systems.insert(1, {'name': "NO CONNECTION"}) tgt_systems = [{'name':n} for n in sorted(tgt_systems)] tgt_systems.insert(1, {'name': "NO CONNECTION"}) data = { 'src2tgts': sorted(iteritems(src2tgts)), 'proms': None, 'units': units, 'vals': vals, 'src_systems': src_systems, 'tgt_systems': tgt_systems, 'noconn_srcs': noconn_srcs, 'src_filter': src_filter, 'tgt_filter': tgt_filter, } viewer = 'connect_table.html' code_dir = os.path.dirname(os.path.abspath(__file__)) with open(os.path.join(code_dir, viewer), "r") as f: template = f.read() graphjson = json.dumps(data) with open(outfile, 'w') as f: s = template.replace("<connection_data>", graphjson) f.write(s) if show_browser: webview(outfile)
def view_connections(root, outfile='connections.html', show_browser=True): """ Generates a self-contained html file containing a detailed connection viewer. Optionally pops up a web browser to view the file. Args ---- root : system or Problem The root for the desired tree. outfile : str, optional The name of the output html file. Defaults to 'connections.html'. show_browser : bool, optional If True, pop up a browser to view the generated html file. Defaults to True. """ # since people will be used to passing the Problem as the first arg to # the N2 diagram funct, allow them to pass a Problem here as well. if isinstance(root, Problem): system = root.root else: system = root connections = system._probdata.connections to_prom = system._sysdata.to_prom_name src2tgts = {} units = {n: m.get('units','') for n,m in chain(iteritems(system._unknowns_dict), iteritems(system._params_dict))} sizes = {} for t, (s, idxs) in iteritems(connections): if idxs is not None: sizes[t] = len(idxs) else: sizes[t] = system._params_dict[t]['size'] if s not in src2tgts: src2tgts[s] = [t] else: src2tgts[s].append(t) src_systems = set() tgt_systems = set() for s in src2tgts: parts = s.split('.') for i in range(len(parts)): src_systems.add('.'.join(parts[:i])) for t in connections: parts = t.split('.') for i in range(len(parts)): tgt_systems.add('.'.join(parts[:i])) # reverse sort so that "NO CONNECTION" shows up at the bottom src2tgts['NO CONNECTION'] = sorted([t for t in to_prom if t not in system._unknowns_dict and t not in connections], reverse=True) src_systems = [{'name':n} for n in sorted(src_systems)] src_systems.insert(1, {'name': "NO CONNECTION"}) tgt_systems = [{'name':n} for n in sorted(tgt_systems)] tgt_systems.insert(1, {'name': "NO CONNECTION"}) data = { 'src2tgts': [(s,ts) for s,ts in sorted(iteritems(src2tgts), reverse=True)], 'proms': to_prom, 'units': units, 'sizes': sizes, 'src_systems': src_systems, 'tgt_systems': tgt_systems, 'noconn_srcs': sorted((n for n in system._unknowns_dict if n not in src2tgts), reverse=True), } viewer = 'connect_table.html' code_dir = os.path.dirname(os.path.abspath(__file__)) with open(os.path.join(code_dir, viewer), "r") as f: template = f.read() graphjson = json.dumps(data) with open(outfile, 'w') as f: s = template.replace("<connection_data>", graphjson) f.write(s) if show_browser: webview(outfile)
def view_tree(problem, outfile='partition_tree_n2.html', show_browser=True, offline=True, embed=False): """ Generates a self-contained html file containing a tree viewer of the specified type. Optionally pops up a web browser to view the file. Args ---- problem : Problem() The Problem (after problem.setup()) for the desired tree. outfile : str, optional The name of the output html file. Defaults to 'partition_tree_n2.html'. show_browser : bool, optional If True, pop up the system default web browser to view the generated html file. Defaults to True. offline : bool, optional If True, embed the javascript d3 library into the generated html file so that the tree can be viewed offline without an internet connection. Otherwise if False, have the html request the latest d3 file from https://d3js.org/d3.v4.min.js when opening the html file. Defaults to True. embed : bool, optional If True, export only the innerHTML that is between the body tags, used for embedding the viewer into another html file. If False, create a standalone HTML file that has the DOCTYPE, html, head, meta, and body tags. Defaults to False. """ component_execution_orders = {} component_execution_index = [0] #list so pass by ref tree = _system_tree_dict(problem.root, component_execution_orders, component_execution_index) viewer = 'partition_tree_n2.template' code_dir = os.path.dirname(os.path.abspath(__file__)) with open(os.path.join(code_dir, viewer), "r") as f: template = f.read() html_begin_tags = ("<!DOCTYPE html>\n" "<html>\n" "<head>\n" " <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n" "</head>\n" "<body>\n") html_end_tags = ("</body>\n" "</html>\n") if embed: html_begin_tags = html_end_tags = "" d3_library = "<script src=\"https://d3js.org/d3.v4.min.js\" charset=\"utf-8\"></script>" if offline: with open(os.path.join(code_dir, 'd3.v4.min.js'), "r") as f: d3_library = "<script type=\"text/javascript\"> %s </script>" % (f.read()) treejson = json.dumps(tree) connections_list = [] G = problem._probdata.relevance._sgraph scc = nx.strongly_connected_components(G) scc_list = [s for s in scc if len(s)>1] #list(scc) for tgt, (src, idxs) in iteritems(problem._probdata.connections): src_subsystem = src.rsplit('.', 1)[0] tgt_subsystem = tgt.rsplit('.', 1)[0] count = 0 edges_list = [] for li in scc_list: if src_subsystem in li and tgt_subsystem in li: count = count+1 if(count > 1): raise ValueError('Count greater than 1') exe_tgt = component_execution_orders[tgt_subsystem] exe_src = component_execution_orders[src_subsystem] exe_low = min(exe_tgt,exe_src) exe_high = max(exe_tgt,exe_src) subg = G.subgraph(li) for n in subg.nodes(): exe_order = component_execution_orders[n] if(exe_order < exe_low or exe_order > exe_high): subg.remove_node(n) src_to_tgt_str = src_subsystem + ' ' + tgt_subsystem for tup in subg.edges(): edge_str = tup[0] + ' ' + tup[1] if edge_str != src_to_tgt_str: edges_list.append(edge_str) if(len(edges_list) > 0): connections_list.append({'src':src, 'tgt':tgt, 'cycle_arrows': edges_list}) else: connections_list.append({'src':src, 'tgt':tgt}) connsjson = json.dumps(connections_list) with open(outfile, 'w') as f: f.write(template % (html_begin_tags, d3_library, treejson, connsjson, html_end_tags)) if show_browser: from openmdao.devtools.webview import webview webview(outfile)
def view_tree(problem, outfile='partition_tree_n2.html', show_browser=True, offline=True, embed=False): """ Generates a self-contained html file containing a tree viewer of the specified type. Optionally pops up a web browser to view the file. Args ---- problem : Problem() The Problem (after problem.setup()) for the desired tree. outfile : str, optional The name of the output html file. Defaults to 'partition_tree_n2.html'. show_browser : bool, optional If True, pop up the system default web browser to view the generated html file. Defaults to True. offline : bool, optional If True, embed the javascript d3 library into the generated html file so that the tree can be viewed offline without an internet connection. Otherwise if False, have the html request the latest d3 file from https://d3js.org/d3.v4.min.js when opening the html file. Defaults to True. embed : bool, optional If True, export only the innerHTML that is between the body tags, used for embedding the viewer into another html file. If False, create a standalone HTML file that has the DOCTYPE, html, head, meta, and body tags. Defaults to False. """ component_execution_orders = {} component_execution_index = [0] #list so pass by ref tree = _system_tree_dict(problem.root, component_execution_orders, component_execution_index) viewer = 'partition_tree_n2.template' code_dir = os.path.dirname(os.path.abspath(__file__)) with open(os.path.join(code_dir, viewer), "r") as f: template = f.read() html_begin_tags = ( "<!DOCTYPE html>\n" "<html>\n" "<head>\n" " <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n" "</head>\n" "<body>\n") html_end_tags = ("</body>\n" "</html>\n") if embed: html_begin_tags = html_end_tags = "" d3_library = "<script src=\"https://d3js.org/d3.v4.min.js\" charset=\"utf-8\"></script>" if offline: with open(os.path.join(code_dir, 'd3.v4.min.js'), "r") as f: d3_library = "<script type=\"text/javascript\"> %s </script>" % ( f.read()) treejson = json.dumps(tree) connections_list = [] G = problem._probdata.relevance._sgraph scc = nx.strongly_connected_components(G) scc_list = [s for s in scc if len(s) > 1] #list(scc) for tgt, (src, idxs) in iteritems(problem._probdata.connections): src_subsystem = src.rsplit('.', 1)[0] tgt_subsystem = tgt.rsplit('.', 1)[0] count = 0 edges_list = [] for li in scc_list: if src_subsystem in li and tgt_subsystem in li: count = count + 1 if (count > 1): raise ValueError('Count greater than 1') exe_tgt = component_execution_orders[tgt_subsystem] exe_src = component_execution_orders[src_subsystem] exe_low = min(exe_tgt, exe_src) exe_high = max(exe_tgt, exe_src) subg = G.subgraph(li) for n in subg.nodes(): exe_order = component_execution_orders[n] if (exe_order < exe_low or exe_order > exe_high): subg.remove_node(n) src_to_tgt_str = src_subsystem + ' ' + tgt_subsystem for tup in subg.edges(): edge_str = tup[0] + ' ' + tup[1] if edge_str != src_to_tgt_str: edges_list.append(edge_str) if (len(edges_list) > 0): connections_list.append({ 'src': src, 'tgt': tgt, 'cycle_arrows': edges_list }) else: connections_list.append({'src': src, 'tgt': tgt}) connsjson = json.dumps(connections_list) with open(outfile, 'w') as f: f.write( template % (html_begin_tags, d3_library, treejson, connsjson, html_end_tags)) if show_browser: from openmdao.devtools.webview import webview webview(outfile)
def view_model(problem_or_filename, outfile='partition_tree_n2.html', show_browser=True, offline=True, embed=False): """ Generates a self-contained html file containing a tree viewer of the specified type. Optionally pops up a web browser to view the file. Args ---- problem_or_filename : Either a Problem() or a string Problem() : The Problem (after problem.setup()) for the desired tree. string : The filename of the case recorder file containing the data required to build the tree. outfile : str, optional The name of the output html file. Defaults to 'partition_tree_n2.html'. show_browser : bool, optional If True, pop up the system default web browser to view the generated html file. Defaults to True. offline : bool, optional If True, embed the javascript d3 library into the generated html file so that the tree can be viewed offline without an internet connection. Otherwise if False, have the html request the latest d3 file from https://d3js.org/d3.v4.min.js when opening the html file. Defaults to True. embed : bool, optional If True, export only the innerHTML that is between the body tags, used for embedding the viewer into another html file. If False, create a standalone HTML file that has the DOCTYPE, html, head, meta, and body tags. Defaults to False. """ viewer = 'partition_tree_n2.template' code_dir = os.path.dirname(os.path.abspath(__file__)) with open(os.path.join(code_dir, viewer), "r") as f: template = f.read() html_begin_tags = ("<!DOCTYPE html>\n" "<html>\n" "<head>\n" " <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n" "</head>\n" "<body>\n") html_end_tags = ("</body>\n" "</html>\n") display_none_attr = "" if embed: html_begin_tags = html_end_tags = "" display_none_attr = " style=\"display:none\"" d3_library = "<script src=\"https://d3js.org/d3.v4.min.js\" charset=\"utf-8\"></script>" if offline: with open(os.path.join(code_dir, 'd3.v4.min.js'), "r") as f: d3_library = "<script type=\"text/javascript\"> %s </script>" % (f.read()) if isinstance(problem_or_filename, Problem): required_data = get_required_data_from_problem(problem_or_filename) else: raise ValueError("Filenames not supported yet!") tree_json = json.dumps(required_data['tree']) conns_json = json.dumps(required_data['connections_list']) with open(outfile, 'w') as f: f.write(template % (html_begin_tags, display_none_attr, d3_library, tree_json, conns_json, html_end_tags)) if show_browser: from openmdao.devtools.webview import webview webview(outfile)
def view_model(problem_or_filename, outfile='partition_tree_n2.html', show_browser=True, offline=True, embed=False): """ Generates a self-contained html file containing a tree viewer of the specified type. Optionally pops up a web browser to view the file. Args ---- problem_or_filename : Either a Problem() or a string Problem() : The Problem (after problem.setup()) for the desired tree. string : The filename of the case recorder file containing the data required to build the tree. outfile : str, optional The name of the output html file. Defaults to 'partition_tree_n2.html'. show_browser : bool, optional If True, pop up the system default web browser to view the generated html file. Defaults to True. offline : bool, optional If True, embed the javascript d3 library into the generated html file so that the tree can be viewed offline without an internet connection. Otherwise if False, have the html request the latest d3 file from https://d3js.org/d3.v4.min.js when opening the html file. Defaults to True. embed : bool, optional If True, export only the innerHTML that is between the body tags, used for embedding the viewer into another html file. If False, create a standalone HTML file that has the DOCTYPE, html, head, meta, and body tags. Defaults to False. """ viewer = 'partition_tree_n2.template' code_dir = os.path.dirname(os.path.abspath(__file__)) with open(os.path.join(code_dir, viewer), "r") as f: template = f.read() html_begin_tags = ( "<!DOCTYPE html>\n" "<html>\n" "<head>\n" " <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n" "</head>\n" "<body>\n") html_end_tags = ("</body>\n" "</html>\n") display_none_attr = "" if embed: html_begin_tags = html_end_tags = "" display_none_attr = " style=\"display:none\"" d3_library = "<script src=\"https://d3js.org/d3.v4.min.js\" charset=\"utf-8\"></script>" if offline: with open(os.path.join(code_dir, 'd3.v4.min.js'), "r") as f: d3_library = "<script type=\"text/javascript\"> %s </script>" % ( f.read()) if isinstance(problem_or_filename, Problem): model_viewer_data = get_model_viewer_data(problem_or_filename) else: # Do not know file type. Try opening to see what works file_type = None if is_valid_sqlite3_db(problem_or_filename): db = SqliteDict(filename=problem_or_filename, flag='r', tablename='metadata') file_type = "sqlite" else: try: hdf = h5py.File(problem_or_filename, 'r') file_type = 'hdf5' except: raise ValueError( "The given filename is not one of the supported file formats: sqlite or hdf5" ) if file_type == "sqlite": model_viewer_data = db['model_viewer_data'] elif file_type == "hdf5": metadata = hdf.get('metadata', None) model_viewer_data = pickle.loads( metadata.get('model_viewer_data').value) tree_json = json.dumps(model_viewer_data['tree']) conns_json = json.dumps(model_viewer_data['connections_list']) with open(outfile, 'w') as f: f.write(template % (html_begin_tags, display_none_attr, d3_library, tree_json, conns_json, html_end_tags)) if show_browser: from openmdao.devtools.webview import webview webview(outfile)
def view_model(problem_or_filename, outfile='n2.html', show_browser=True, embeddable=False, draw_potential_connections=True): """ Generates an HTML file containing a tree viewer. Optionally pops up a web browser to view the file. Parameters ---------- problem_or_filename : Either a Problem() or a string Problem() : The Problem (after problem.setup()) for the desired tree. string : The filename of the case recorder file containing the data required to build the tree. outfile : str, optional The name of the final output file show_browser : bool, optional If True, pop up the system default web browser to view the generated html file. Defaults to True. embeddable : bool, optional If True, gives a single HTML file that doesn't have the <html>, <DOCTYPE>, <body> and <head> tags. If False, gives a single, standalone HTML file for viewing. draw_potential_connections : bool, optional If true, allows connections to be drawn on the N2 that do not currently exist in the model. Defaults to True. """ html_begin_tags = """ <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> </head> <body>\n """ html_end_tags = """ </body> </html> """ code_dir = os.path.dirname(os.path.abspath(__file__)) vis_dir = os.path.join(code_dir, "visualization") libs_dir = os.path.join(vis_dir, "libs") src_dir = os.path.join(vis_dir, "src") style_dir = os.path.join(vis_dir, "style") #grab the libraries with open(os.path.join(libs_dir, "awesomplete.js"), "r") as f: awesomplete = f.read() with open(os.path.join(libs_dir, "d3.v4.min.js"), "r") as f: d3 = f.read() with open(os.path.join(libs_dir, "http.js"), "r") as f: http = f.read() with open(os.path.join(libs_dir, "jquery-3.2.1.min.js"), "r") as f: jquery = f.read() with open(os.path.join(libs_dir, "vkBeautify.js"), "r") as f: vk_beautify = f.read() #grab the src with open(os.path.join(src_dir, "constants.js"), "r") as f: constants = f.read() with open(os.path.join(src_dir, "draw.js"), "r") as f: draw = f.read() with open(os.path.join(src_dir, "legend.js"), "r") as f: legend = f.read() with open(os.path.join(src_dir, "modal.js"), "r") as f: modal = f.read() with open(os.path.join(src_dir, "ptN2.js"), "r") as f: pt_n2 = f.read() with open(os.path.join(src_dir, "search.js"), "r") as f: search = f.read() with open(os.path.join(src_dir, "svg.js"), "r") as f: svg = f.read() #grab the style with open(os.path.join(style_dir, "awesomplete.css"), "r") as f: awesomplete_style = f.read() with open(os.path.join(style_dir, "partition_tree.css"), "r") as f: partition_tree_style = f.read() with open(os.path.join(style_dir, "fontello.woff"), "rb") as f: encoded_font = str(base64.b64encode(f.read()).decode("ascii")) #grab the index.html with open(os.path.join(vis_dir, "index.html"), "r") as f: index = f.read() #grab the model viewer data model_viewer_data = 'var modelData = %s' % json.dumps(_get_viewer_data(problem_or_filename)) #add the necessary HTML tags if we aren't embedding if not embeddable: index = html_begin_tags + index + html_end_tags #put all style and JS into index index = index.replace('{{awesomplete_style}}', awesomplete_style) index = index.replace('{{partition_tree_style}}', partition_tree_style) index = index.replace('{{fontello}}', encoded_font) index = index.replace('{{d3_lib}}', d3) index = index.replace('{{awesomplete_lib}}', awesomplete) index = index.replace('{{vk_beautify_lib}}', vk_beautify) index = index.replace('{{model_data}}', model_viewer_data) index = index.replace('{{constants_lib}}', constants) index = index.replace('{{modal_lib}}', modal) index = index.replace('{{svg_lib}}', svg) index = index.replace('{{search_lib}}', search) index = index.replace('{{legend_lib}}', legend) index = index.replace('{{draw_lib}}', draw) index = index.replace('{{ptn2_lib}}', pt_n2) if draw_potential_connections: index = index.replace('{{draw_potential_connections}}', 'true') else: index = index.replace('{{draw_potential_connections}}', 'false') with open(outfile, 'w') as f: f.write(index) #open it up in the browser if show_browser: from openmdao.devtools.webview import webview webview(outfile)
def view_model(data_source, outfile='n2.html', show_browser=True, embeddable=False, draw_potential_connections=True): """ Generates an HTML file containing a tree viewer. Optionally opens a web browser to view the file. Parameters ---------- data_source : <Problem> or str The Problem or case recorder database containing the model or model data. outfile : str, optional The name of the final output file show_browser : bool, optional If True, pop up the system default web browser to view the generated html file. Defaults to True. embeddable : bool, optional If True, gives a single HTML file that doesn't have the <html>, <DOCTYPE>, <body> and <head> tags. If False, gives a single, standalone HTML file for viewing. draw_potential_connections : bool, optional If true, allows connections to be drawn on the N2 that do not currently exist in the model. Defaults to True. """ # grab the model viewer data model_viewer_data = _get_viewer_data(data_source) model_viewer_data = 'var modelData = %s' % json.dumps(model_viewer_data) # if MPI is active only display one copy of the viewer if MPI and MPI.COMM_WORLD.rank != 0: return code_dir = os.path.dirname(os.path.abspath(__file__)) vis_dir = os.path.join(code_dir, "visualization") libs_dir = os.path.join(vis_dir, "libs") src_dir = os.path.join(vis_dir, "src") style_dir = os.path.join(vis_dir, "style") # grab the libraries, src and style lib_dct = {'d3': 'd3.v4.min', 'awesomplete': 'awesomplete', 'vk_beautify': 'vkBeautify'} libs = read_files(itervalues(lib_dct), libs_dir, 'js') src_names = 'constants', 'draw', 'legend', 'modal', 'ptN2', 'search', 'svg' srcs = read_files(src_names, src_dir, 'js') styles = read_files(('awesomplete', 'partition_tree'), style_dir, 'css') style_elems = '\n\n'.join([write_style(content=s) for s in itervalues(styles)]) with open(os.path.join(style_dir, "fontello.woff"), "rb") as f: encoded_font = str(base64.b64encode(f.read()).decode("ascii")) # grab the index.html with open(os.path.join(vis_dir, "index.html"), "r") as f: index = f.read() # add the necessary HTML tags if we aren't embedding if embeddable: index = '\n\n'.join([style_elems, index]) else: meta = '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">' head = '\n\n'.join([meta, style_elems]) # Write styles to head index = head_and_body(head=head, body=index) # put all style and JS into index index = index.replace('{{fontello}}', encoded_font) for k, v in iteritems(lib_dct): index = index.replace('{{{}_lib}}'.format(k), write_script(libs[v], indent=4)) for name, code in iteritems(srcs): index = index.replace('{{{}_lib}}'.format(name.lower()), write_script(code, indent=4)) index = index.replace('{{model_data}}', write_script(model_viewer_data, indent=4)) index = index.replace('{{draw_potential_connections}}', str(draw_potential_connections).lower()) with open(outfile, 'w') as f: # write output file f.write(index) # open it up in the browser if show_browser: from openmdao.devtools.webview import webview webview(outfile)
def view_model(data_source, outfile='n2.html', show_browser=True, embeddable=False, draw_potential_connections=True): """ Generates an HTML file containing a tree viewer. Optionally opens a web browser to view the file. Parameters ---------- data_source : <Problem> or str The Problem or case recorder database containing the model or model data. outfile : str, optional The name of the final output file show_browser : bool, optional If True, pop up the system default web browser to view the generated html file. Defaults to True. embeddable : bool, optional If True, gives a single HTML file that doesn't have the <html>, <DOCTYPE>, <body> and <head> tags. If False, gives a single, standalone HTML file for viewing. draw_potential_connections : bool, optional If true, allows connections to be drawn on the N2 that do not currently exist in the model. Defaults to True. """ # grab the model viewer data model_viewer_data = _get_viewer_data(data_source) model_viewer_data = 'var modelData = %s' % json.dumps(model_viewer_data) # if MPI is active only display one copy of the viewer if MPI and MPI.COMM_WORLD.rank != 0: return code_dir = os.path.dirname(os.path.abspath(__file__)) vis_dir = os.path.join(code_dir, "visualization") libs_dir = os.path.join(vis_dir, "libs") src_dir = os.path.join(vis_dir, "src") style_dir = os.path.join(vis_dir, "style") # grab the libraries, src and style lib_dct = { 'd3': 'd3.v4.min', 'awesomplete': 'awesomplete', 'vk_beautify': 'vkBeautify' } libs = read_files(itervalues(lib_dct), libs_dir, 'js') src_names = 'constants', 'draw', 'legend', 'modal', 'ptN2', 'search', 'svg' srcs = read_files(src_names, src_dir, 'js') styles = read_files(('awesomplete', 'partition_tree'), style_dir, 'css') style_elems = '\n\n'.join( [write_style(content=s) for s in itervalues(styles)]) with open(os.path.join(style_dir, "fontello.woff"), "rb") as f: encoded_font = str(base64.b64encode(f.read()).decode("ascii")) # grab the index.html with open(os.path.join(vis_dir, "index.html"), "r") as f: index = f.read() # add the necessary HTML tags if we aren't embedding if embeddable: index = '\n\n'.join([style_elems, index]) else: meta = '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">' head = '\n\n'.join([meta, style_elems]) # Write styles to head index = head_and_body(head=head, body=index) # put all style and JS into index index = index.replace('{{fontello}}', encoded_font) for k, v in iteritems(lib_dct): index = index.replace('{{{}_lib}}'.format(k), write_script(libs[v], indent=4)) for name, code in iteritems(srcs): index = index.replace('{{{}_lib}}'.format(name.lower()), write_script(code, indent=4)) index = index.replace('{{model_data}}', write_script(model_viewer_data, indent=4)) index = index.replace('{{draw_potential_connections}}', str(draw_potential_connections).lower()) with open(outfile, 'w') as f: # write output file f.write(index) # open it up in the browser if show_browser: from openmdao.devtools.webview import webview webview(outfile)
def _write_xdsm(filename, viewer_data, optimizer=None, include_solver=False, cleanup=True, design_vars=None, responses=None, residuals=None, model_path=None, recurse=True, include_external_outputs=True, subs=_CHAR_SUBS, writer='pyXDSM', show_browser=False, add_process_conns=True, quiet=False, **kwargs): """ XDSM writer. Components are extracted from the connections of the problem. Parameters ---------- filename : str Filename (absolute path without extension) connections : list[(str, str)] Connections list optimizer : str or None, optional Optimizer name include_solver: bool, optional Defaults to False. cleanup : bool, optional Clean-up temporary files after making the diagram. Defaults to True. design_vars : OrderedDict or None Design variables responses : OrderedDict or None, , optional Responses model_path : str or None, optional Path to the subsystem to be transcribed to XDSM. If None, use the model root. recurse : bool, optional If False, treat the top level of each name as the source/target component. include_external_outputs : bool, optional If True, show externally connected outputs when transcribing a subsystem. Defaults to True. subs : tuple, optional Character pairs to be substituted. Forbidden characters or just for the sake of nicer names. writer: str, optional Writer is pyXDSM or XDSMjs. Defaults to pyXDSM. show_browser : bool, optional If True, pop up a browser to view the generated html file. Defaults to False. add_process_conns: bool Add process connections (thin black lines) Defaults to True quiet : bool Set to True to suppress output from pdflatex kwargs : dict Keyword arguments Returns ------- XDSM """ # TODO implement residuals writer_name = writer.lower() # making it case insensitive # Box appearance box_stacking = kwargs.pop('box_stacking', _DEFAULT_BOX_STACKING) box_width = kwargs.pop('box_width', _DEFAULT_BOX_WIDTH) box_lines = kwargs.pop('box_lines', _MAX_BOX_LINES) # In XDSMjs components are numbered by default, so only add for pyXDSM as an option add_component_indices = kwargs.pop('numbered_comps', True) and (writer_name == 'pyxdsm') number_alignment = kwargs.pop('number_alignment', 'horizontal') # nothing, space or new line def format_block(names, **kwargs): if writer == 'pyxdsm': return _format_block_string(var_names=names, stacking=box_stacking, box_width=box_width, box_lines=box_lines, **kwargs) else: return names def number_label(number, text, alignment): # Adds an index to the label either above or on the left side. number_str = '{}: '.format(number) if alignment == 'horizontal': txt = '{}{}'.format(number_str, text) if box_stacking == 'vertical': return _multiline_block(txt) else: return txt elif alignment == 'vertical': return _multiline_block(number_str, text) else: return text # In case of a wrong setting connections = viewer_data['connections_list'] tree = viewer_data['tree'] # Get the top level system to be transcripted to XDSM comps = _get_comps(tree, model_path=model_path, recurse=recurse) solvers = [] conns1, external_inputs1, external_outputs1 = _prune_connections( connections, model_path=model_path) conns2 = _process_connections(conns1, recurse=recurse, subs=subs) external_inputs2 = _process_connections(external_inputs1, recurse=recurse, subs=subs) external_outputs2 = _process_connections(external_outputs1, recurse=recurse, subs=subs) conns3 = _accumulate_connections(conns2) external_inputs3 = _accumulate_connections(external_inputs2) external_outputs3 = _accumulate_connections(external_outputs2) if writer_name == 'pyxdsm': # pyXDSM x = XDSMWriter() elif writer_name == 'xdsmjs': # XDSMjs x = XDSMjsWriter() else: msg = 'Undefined XDSM writer "{}"' raise ValueError(msg.format(writer_name)) if optimizer is not None: label = optimizer if add_component_indices: opt_index = len(comps) + len( solvers) + 2 # index of last block + 1 nr_comps = len(x.comps) index_str = '{}, {}$ \\rightarrow $ 2'.format( nr_comps + 1, opt_index, nr_comps + 2) label = number_label(index_str, label, number_alignment) x.add_optimizer(label=label) if include_solver: # Default "run once" solvers are ignored # Nonlinear solver has precedence msg = "Solvers in the XDSM diagram are not fully supported yet, and needs manual editing." warnings.warn(msg) solver_str = _format_solver_str(tree, stacking=box_stacking, add_indices=add_component_indices) if solver_str: # At least one non-default solver if add_component_indices: i = len(x.comps) + 1 solver_str = number_label(i, solver_str, number_alignment) solvers.append(solver_str) x.add_solver(solver_str) design_vars2 = _collect_connections(design_vars) responses2 = _collect_connections(responses) # Design variables for comp, conn_vars in iteritems(design_vars2): conn_vars = [_replace_chars(var, subs) for var in conn_vars] # Format var names opt_con_vars = [_opt_var_str(var) for var in conn_vars] # Optimal var names init_con_vars = [_init_var_str(var, writer_name) for var in conn_vars] # Optimal var names x.connect('opt', comp, format_block(conn_vars)) # Connection from optimizer x.add_output(comp, format_block(opt_con_vars), side='left') # Optimal design variables x.add_output('opt', format_block(opt_con_vars), side='left') # Optimal design variables x.add_input('opt', format_block(init_con_vars)) # Initial design variables # Responses for comp, conn_vars in iteritems(responses2): conn_vars = [_replace_chars(var, subs) for var in conn_vars] # Optimal var names opt_con_vars = [_opt_var_str(var) for var in conn_vars] x.connect(comp, 'opt', conn_vars) # Connection to optimizer x.add_output(comp, format_block(opt_con_vars), side='left') # Optimal output # Add components for comp in comps: # Driver is 1, so starting from 2 i = len(x.comps) + 1 label = _replace_chars(comp['name'], substitutes=subs) if add_component_indices: label = number_label(i, label, number_alignment) x.add_comp(name=comp['abs_name'], label=label) # Add the connections for src, dct in iteritems(conns3): for tgt, conn_vars in iteritems(dct): x.connect(src, tgt, format_block(conn_vars)) # Add the externally sourced inputs for src, tgts in iteritems(external_inputs3): for tgt, conn_vars in iteritems(tgts): formatted_conn_vars = [ _replace_chars(o, substitutes=subs) for o in conn_vars ] x.add_input(tgt, format_block(formatted_conn_vars)) # Add the externally connected outputs if include_external_outputs: for src, tgts in iteritems(external_outputs3): output_vars = set() for tgt, conn_vars in iteritems(tgts): output_vars |= set(conn_vars) formatted_outputs = [ _replace_chars(o, subs) for o in output_vars ] x.add_output(src, formatted_outputs, side='right') if add_process_conns: x.add_workflow() x.write(filename, cleanup=cleanup, quiet=quiet, **kwargs) if show_browser: # path will be specified based on the "out_format", if all required inputs where # provided for showing the results. if writer_name == 'pyxdsm': # pyXDSM ext = 'pdf' elif writer_name == 'xdsmjs': # XDSMjs ext = 'html' else: err_msg = '"{}" is an invalid writer name.' raise ValueError(err_msg.format(writer)) path = '.'.join([filename, ext]) webview(path) # Can open also PDFs return x
def view_model(data_source, outfile='n2.html', show_browser=True, embeddable=False, title=None): """ Generates an HTML file containing a tree viewer. Optionally opens a web browser to view the file. Parameters ---------- data_source : <Problem> or str The Problem or case recorder database containing the model or model data. outfile : str, optional The name of the final output file show_browser : bool, optional If True, pop up the system default web browser to view the generated html file. Defaults to True. embeddable : bool, optional If True, gives a single HTML file that doesn't have the <html>, <DOCTYPE>, <body> and <head> tags. If False, gives a single, standalone HTML file for viewing. title : str, optional The title for the diagram. Used in the HTML title and also shown on the page. """ # grab the model viewer data model_data = _get_viewer_data(data_source) model_data = 'var modelData = %s' % json.dumps(model_data, default=make_serializable) # if MPI is active only display one copy of the viewer if MPI and MPI.COMM_WORLD.rank != 0: return code_dir = os.path.dirname(os.path.abspath(__file__)) vis_dir = os.path.join(code_dir, "visualization") libs_dir = os.path.join(vis_dir, "libs") src_dir = os.path.join(vis_dir, "src") style_dir = os.path.join(vis_dir, "style") # grab the libraries, src and style lib_dct = {'d3': 'd3.v4.min', 'awesomplete': 'awesomplete', 'vk_beautify': 'vkBeautify'} libs = read_files(itervalues(lib_dct), libs_dir, 'js') src_names = 'constants', 'draw', 'legend', 'modal', 'ptN2', 'search', 'svg' srcs = read_files(src_names, src_dir, 'js') styles = read_files(('awesomplete', 'partition_tree'), style_dir, 'css') with open(os.path.join(style_dir, "fontello.woff"), "rb") as f: encoded_font = str(base64.b64encode(f.read()).decode("ascii")) if title: title = "OpenMDAO Model Hierarchy and N2 diagram: %s" % title else: title = "OpenMDAO Model Hierarchy and N2 diagram" h = DiagramWriter(filename=os.path.join(vis_dir, "index.html"), title=title, styles=styles, embeddable=embeddable) # put all style and JS into index h.insert('{{fontello}}', encoded_font) for k, v in iteritems(lib_dct): h.insert('{{{}_lib}}'.format(k), write_script(libs[v], indent=_IND)) for name, code in iteritems(srcs): h.insert('{{{}_lib}}'.format(name.lower()), write_script(code, indent=_IND)) h.insert('{{model_data}}', write_script(model_data, indent=_IND)) # Toolbar toolbar = h.toolbar group1 = toolbar.add_button_group() group1.add_button("Return To Root", uid="returnToRootButtonId", disabled="disabled", content="icon-home") group1.add_button("Back", uid="backButtonId", disabled="disabled", content="icon-left-big") group1.add_button("Forward", uid="forwardButtonId", disabled="disabled", content="icon-right-big") group1.add_button("Up One Level", uid="upOneLevelButtonId", disabled="disabled", content="icon-up-big") group2 = toolbar.add_button_group() group2.add_button("Uncollapse In View Only", uid="uncollapseInViewButtonId", content="icon-resize-full") group2.add_button("Uncollapse All", uid="uncollapseAllButtonId", content="icon-resize-full bigger-font") group2.add_button("Collapse Outputs In View Only", uid="collapseInViewButtonId", content="icon-resize-small") group2.add_button("Collapse All Outputs", uid="collapseAllButtonId", content="icon-resize-small bigger-font") group2.add_dropdown("Collapse Depth", button_content="icon-sort-number-up", uid="idCollapseDepthDiv") group3 = toolbar.add_button_group() group3.add_button("Clear Arrows and Connections", uid="clearArrowsAndConnectsButtonId", content="icon-eraser") group3.add_button("Show Path", uid="showCurrentPathButtonId", content="icon-terminal") group3.add_button("Show Legend", uid="showLegendButtonId", content="icon-map-signs") group3.add_button("Show Params", uid="showParamsButtonId", content="icon-exchange") group3.add_button("Toggle Solver Names", uid="toggleSolverNamesButtonId", content="icon-minus") group3.add_dropdown("Font Size", id_naming="idFontSize", options=_FONT_SIZES, option_formatter=lambda x: '{}px'.format(x), button_content="icon-text-height") group3.add_dropdown("Vertically Resize", id_naming="idVerticalResize", options=_MODEL_HEIGHTS, option_formatter=lambda x: '{}px'.format(x), button_content="icon-resize-vertical", header="Model Height") group4 = toolbar.add_button_group() group4.add_button("Save SVG", uid="saveSvgButtonId", content="icon-floppy") group5 = toolbar.add_button_group() group5.add_button("Help", uid="helpButtonId", content="icon-help") # Help help_txt = ('Left clicking on a node in the partition tree will navigate to that node. ' 'Right clicking on a node in the model hierarchy will collapse/uncollapse it. ' 'A click on any element in the N^2 diagram will allow those arrows to persist.') h.add_help(help_txt, footer="OpenMDAO Model Hierarchy and N^2 diagram") # Write output file h.write(outfile) # open it up in the browser if show_browser: from openmdao.devtools.webview import webview webview(outfile)
def view_model(data_source, outfile='n2.html', show_browser=True, embeddable=False, draw_potential_connections=True): """ Generates an HTML file containing a tree viewer. Optionally opens a web browser to view the file. Parameters ---------- data_source : <Problem> or str The Problem or case recorder database containing the model or model data. outfile : str, optional The name of the final output file show_browser : bool, optional If True, pop up the system default web browser to view the generated html file. Defaults to True. embeddable : bool, optional If True, gives a single HTML file that doesn't have the <html>, <DOCTYPE>, <body> and <head> tags. If False, gives a single, standalone HTML file for viewing. draw_potential_connections : bool, optional If true, allows connections to be drawn on the N2 that do not currently exist in the model. Defaults to True. """ # grab the model viewer data model_viewer_data = _get_viewer_data(data_source) model_viewer_data = 'var modelData = %s' % json.dumps(model_viewer_data) # if MPI is active only display one copy of the viewer if MPI and MPI.COMM_WORLD.rank != 0: return html_begin_tags = """ <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> </head> <body>\n """ html_end_tags = """ </body> </html> """ code_dir = os.path.dirname(os.path.abspath(__file__)) vis_dir = os.path.join(code_dir, "visualization") libs_dir = os.path.join(vis_dir, "libs") src_dir = os.path.join(vis_dir, "src") style_dir = os.path.join(vis_dir, "style") # grab the libraries with open(os.path.join(libs_dir, "awesomplete.js"), "r") as f: awesomplete = f.read() with open(os.path.join(libs_dir, "d3.v4.min.js"), "r") as f: d3 = f.read() with open(os.path.join(libs_dir, "vkBeautify.js"), "r") as f: vk_beautify = f.read() # grab the src with open(os.path.join(src_dir, "constants.js"), "r") as f: constants = f.read() with open(os.path.join(src_dir, "draw.js"), "r") as f: draw = f.read() with open(os.path.join(src_dir, "legend.js"), "r") as f: legend = f.read() with open(os.path.join(src_dir, "modal.js"), "r") as f: modal = f.read() with open(os.path.join(src_dir, "ptN2.js"), "r") as f: pt_n2 = f.read() with open(os.path.join(src_dir, "search.js"), "r") as f: search = f.read() with open(os.path.join(src_dir, "svg.js"), "r") as f: svg = f.read() # grab the style with open(os.path.join(style_dir, "awesomplete.css"), "r") as f: awesomplete_style = f.read() with open(os.path.join(style_dir, "partition_tree.css"), "r") as f: partition_tree_style = f.read() with open(os.path.join(style_dir, "fontello.woff"), "rb") as f: encoded_font = str(base64.b64encode(f.read()).decode("ascii")) # grab the index.html with open(os.path.join(vis_dir, "index.html"), "r") as f: index = f.read() # add the necessary HTML tags if we aren't embedding if not embeddable: index = html_begin_tags + index + html_end_tags # put all style and JS into index index = index.replace('{{awesomplete_style}}', awesomplete_style) index = index.replace('{{partition_tree_style}}', partition_tree_style) index = index.replace('{{fontello}}', encoded_font) index = index.replace('{{d3_lib}}', d3) index = index.replace('{{awesomplete_lib}}', awesomplete) index = index.replace('{{vk_beautify_lib}}', vk_beautify) index = index.replace('{{model_data}}', model_viewer_data) index = index.replace('{{constants_lib}}', constants) index = index.replace('{{modal_lib}}', modal) index = index.replace('{{svg_lib}}', svg) index = index.replace('{{search_lib}}', search) index = index.replace('{{legend_lib}}', legend) index = index.replace('{{draw_lib}}', draw) index = index.replace('{{ptn2_lib}}', pt_n2) if draw_potential_connections: index = index.replace('{{draw_potential_connections}}', 'true') else: index = index.replace('{{draw_potential_connections}}', 'false') with open(outfile, 'w') as f: f.write(index) # open it up in the browser if show_browser: from openmdao.devtools.webview import webview webview(outfile)
def view_connections(root, outfile='connections.html', show_browser=True, src_filter='', tgt_filter='', precision=6): """ Generates a self-contained html file containing a detailed connection viewer. Optionally pops up a web browser to view the file. Parameters ---------- root : system or Problem The root for the desired tree. outfile : str, optional The name of the output html file. Defaults to 'connections.html'. show_browser : bool, optional If True, pop up a browser to view the generated html file. Defaults to True. src_filter : str, optional If defined, use this as the initial value for the source system filter. tgt_filter : str, optional If defined, use this as the initial value for the target system filter. precision : int, optional Sets the precision for displaying array values. """ if MPI and MPI.COMM_WORLD.rank != 0: return # since people will be used to passing the Problem as the first arg to # the N2 diagram funct, allow them to pass a Problem here as well. if isinstance(root, Problem): system = root.model else: system = root input_srcs = system._conn_global_abs_in2out connections = { tgt: src for tgt, src in iteritems(input_srcs) if src is not None } src2tgts = defaultdict(list) units = {n: data.get('units','') for n, data in iteritems(system._var_allprocs_abs2meta)} vals = {} with printoptions(precision=precision, suppress=True, threshold=10000): for t in system._var_abs_names['input']: tmeta = system._var_abs2meta[t] idxs = tmeta['src_indices'] if t in connections: s = connections[t] val = _get_output(system, s, idxs) # if there's a unit conversion, express the value in the # units of the target if units[t] and val != "<on remote_proc>": val = convert_units(val, units[s], units[t]) src2tgts[s].append(t) else: # unconnected param val = _get_input(system, t, None) if isinstance(val, np.ndarray): val = np.array2string(val) else: val = str(val) vals[t] = val noconn_srcs = sorted((n for n in system._var_abs_names['output'] if n not in src2tgts), reverse=True) for s in noconn_srcs: vals[s] = str(system._outputs[s]) vals['NO CONNECTION'] = '' src_systems = set() tgt_systems = set() for s in system._var_abs_names['output']: parts = s.split('.') for i in range(len(parts)): src_systems.add('.'.join(parts[:i])) for t in system._var_abs_names['input']: parts = t.split('.') for i in range(len(parts)): tgt_systems.add('.'.join(parts[:i])) # reverse sort so that "NO CONNECTION" shows up at the bottom src2tgts['NO CONNECTION'] = sorted([t for t in system._var_abs_names['input'] if t not in connections], reverse=True) src_systems = [{'name':n} for n in sorted(src_systems)] src_systems.insert(1, {'name': "NO CONNECTION"}) tgt_systems = [{'name':n} for n in sorted(tgt_systems)] tgt_systems.insert(1, {'name': "NO CONNECTION"}) data = { 'src2tgts': sorted(iteritems(src2tgts)), 'proms': None, 'units': units, 'vals': vals, 'src_systems': src_systems, 'tgt_systems': tgt_systems, 'noconn_srcs': noconn_srcs, 'src_filter': src_filter, 'tgt_filter': tgt_filter, } viewer = 'connect_table.html' code_dir = os.path.dirname(os.path.abspath(__file__)) with open(os.path.join(code_dir, viewer), "r") as f: template = f.read() graphjson = json.dumps(data) with open(outfile, 'w') as f: s = template.replace("<connection_data>", graphjson) f.write(s) if show_browser: webview(outfile)
def view_model(problem_or_filename, outfile='n2.html', show_browser=True, embeddable=False): """ Generates an HTML file containing a tree viewer. Optionally pops up a web browser to view the file. Parameters ---------- problem_or_filename : Either a Problem() or a string Problem() : The Problem (after problem.setup()) for the desired tree. string : The filename of the case recorder file containing the data required to build the tree. outfile : str, optional The name of the final output file show_browser : bool, optional If True, pop up the system default web browser to view the generated html file. Defaults to True. embeddable : bool, optional If True, gives a single HTML file that doesn't have the <html>, <DOCTYPE>, <body> and <head> tags. If False, gives a single, standalone HTML file for viewing. """ html_begin_tags = """ <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> </head> <body>\n """ html_end_tags = """ </body> </html> """ code_dir = os.path.dirname(os.path.abspath(__file__)) vis_dir = os.path.join(code_dir, "visualization") libs_dir = os.path.join(vis_dir, "libs") src_dir = os.path.join(vis_dir, "src") style_dir = os.path.join(vis_dir, "style") #grab the libraries with open(os.path.join(libs_dir, "awesomplete.js"), "r") as f: awesomplete = f.read() with open(os.path.join(libs_dir, "d3.v4.min.js"), "r") as f: d3 = f.read() with open(os.path.join(libs_dir, "http.js"), "r") as f: http = f.read() with open(os.path.join(libs_dir, "jquery-3.2.1.min.js"), "r") as f: jquery = f.read() with open(os.path.join(libs_dir, "vkBeautify.js"), "r") as f: vk_beautify = f.read() #grab the src with open(os.path.join(src_dir, "constants.js"), "r") as f: constants = f.read() with open(os.path.join(src_dir, "context-menu.js"), "r") as f: context_menu = f.read() with open(os.path.join(src_dir, "draw.js"), "r") as f: draw = f.read() with open(os.path.join(src_dir, "legend.js"), "r") as f: legend = f.read() with open(os.path.join(src_dir, "modal.js"), "r") as f: modal = f.read() with open(os.path.join(src_dir, "ptN2.js"), "r") as f: pt_n2 = f.read() with open(os.path.join(src_dir, "search.js"), "r") as f: search = f.read() with open(os.path.join(src_dir, "svg.js"), "r") as f: svg = f.read() #grab the style with open(os.path.join(style_dir, "awesomplete.css"), "r") as f: awesomplete_style = f.read() with open(os.path.join(style_dir, "partition_tree.css"), "r") as f: partition_tree_style = f.read() with open(os.path.join(style_dir, "fontello.woff"), "rb") as f: encoded_font = str(base64.b64encode(f.read()).decode("ascii")) #grab the index.html with open(os.path.join(vis_dir, "index.html"), "r") as f: index = f.read() #grab the model viewer data model_viewer_data = 'var modelData = %s' % json.dumps( _get_viewer_data(problem_or_filename)) #add the necessary HTML tags if we aren't embedding if not embeddable: index = html_begin_tags + index + html_end_tags #put all style and JS into index index = index.replace('{{awesomplete_style}}', awesomplete_style) index = index.replace('{{partition_tree_style}}', partition_tree_style) index = index.replace('{{fontello}}', encoded_font) index = index.replace('{{d3_lib}}', d3) index = index.replace('{{awesomplete_lib}}', awesomplete) index = index.replace('{{vk_beautify_lib}}', vk_beautify) index = index.replace('{{model_data}}', model_viewer_data) index = index.replace('{{constants_lib}}', constants) index = index.replace('{{modal_lib}}', modal) index = index.replace('{{svg_lib}}', svg) index = index.replace('{{search_lib}}', search) index = index.replace('{{legend_lib}}', legend) index = index.replace('{{draw_lib}}', draw) index = index.replace('{{context_menu_lib}}', context_menu) index = index.replace('{{ptn2_lib}}', pt_n2) with open(outfile, 'w') as f: f.write(index) #open it up in the browser if show_browser: from openmdao.devtools.webview import webview webview(outfile)
def view_connections(root, outfile='connections.html', show_browser=True, src_filter='', tgt_filter='', precision=6): """ Generates a self-contained html file containing a detailed connection viewer. Optionally pops up a web browser to view the file. Args ---- root : system or Problem The root for the desired tree. outfile : str, optional The name of the output html file. Defaults to 'connections.html'. show_browser : bool, optional If True, pop up a browser to view the generated html file. Defaults to True. src_filter : str, optional If defined, use this as the initial value for the source system filter. tgt_filter : str, optional If defined, use this as the initial value for the target system filter. precision : int, optional Sets the precision for displaying array values. """ # since people will be used to passing the Problem as the first arg to # the N2 diagram funct, allow them to pass a Problem here as well. if isinstance(root, Problem): system = root.root else: system = root connections = system._probdata.connections to_prom = system._sysdata.to_prom_name src2tgts = {} units = { n: m.get('units', '') for n, m in chain(iteritems(system._unknowns_dict), iteritems(system._params_dict)) } vals = {} with printoptions(precision=precision, suppress=True, threshold=10000): for t, tmeta in iteritems(system._params_dict): if t in connections: s, idxs = connections[t] if idxs is not None: val = system.unknowns[to_prom[s]][idxs] else: val = system.unknowns[to_prom[s]] # if there's a unit conversion, express the value in the # units of the target if 'unit_conv' in tmeta: scale, offset = tmeta['unit_conv'] val = (val + offset) * scale if s not in src2tgts: src2tgts[s] = [t] else: src2tgts[s].append(t) else: # unconnected param val = system._params_dict[t]['val'] if isinstance(val, numpy.ndarray): val = numpy.array2string(val) else: val = str(val) vals[t] = val noconn_srcs = sorted( (n for n in system._unknowns_dict if n not in src2tgts), reverse=True) for s in noconn_srcs: vals[s] = str(system.unknowns[to_prom[s]]) vals['NO CONNECTION'] = '' src_systems = set() tgt_systems = set() for s in src2tgts: parts = s.split('.') for i in range(len(parts)): src_systems.add('.'.join(parts[:i])) for t in connections: parts = t.split('.') for i in range(len(parts)): tgt_systems.add('.'.join(parts[:i])) # reverse sort so that "NO CONNECTION" shows up at the bottom src2tgts['NO CONNECTION'] = sorted([ t for t in to_prom if t not in system._unknowns_dict and t not in connections ], reverse=True) src_systems = [{'name': n} for n in sorted(src_systems)] src_systems.insert(1, {'name': "NO CONNECTION"}) tgt_systems = [{'name': n} for n in sorted(tgt_systems)] tgt_systems.insert(1, {'name': "NO CONNECTION"}) data = { 'src2tgts': [(s, ts) for s, ts in sorted(iteritems(src2tgts), reverse=True)], 'proms': to_prom, 'units': units, 'vals': vals, 'src_systems': src_systems, 'tgt_systems': tgt_systems, 'noconn_srcs': noconn_srcs, 'src_filter': src_filter, 'tgt_filter': tgt_filter, } viewer = 'connect_table.html' code_dir = os.path.dirname(os.path.abspath(__file__)) with open(os.path.join(code_dir, viewer), "r") as f: template = f.read() graphjson = json.dumps(data) with open(outfile, 'w') as f: s = template.replace("<connection_data>", graphjson) f.write(s) if show_browser: webview(outfile)
def view_model(data_source, outfile='n2.html', show_browser=True, embeddable=False, title=None, use_declare_partial_info=False): """ Generates an HTML file containing a tree viewer. Optionally opens a web browser to view the file. Parameters ---------- data_source : <Problem> or str The Problem or case recorder database containing the model or model data. outfile : str, optional The name of the final output file show_browser : bool, optional If True, pop up the system default web browser to view the generated html file. Defaults to True. embeddable : bool, optional If True, gives a single HTML file that doesn't have the <html>, <DOCTYPE>, <body> and <head> tags. If False, gives a single, standalone HTML file for viewing. title : str, optional The title for the diagram. Used in the HTML title and also shown on the page. use_declare_partial_info: bool, optional If True, in the N2 matrix, component internal connectivity computed using derivative declarations, otherwise, derivative declarations ignored, so dense component connectivity is assumed. """ # grab the model viewer data model_data = _get_viewer_data(data_source) options = {} options['use_declare_partial_info'] = use_declare_partial_info model_data['options'] = options model_data = 'var modelData = %s' % json.dumps(model_data, default=make_serializable) # if MPI is active only display one copy of the viewer if MPI and MPI.COMM_WORLD.rank != 0: return code_dir = os.path.dirname(os.path.abspath(__file__)) vis_dir = os.path.join(code_dir, "visualization") libs_dir = os.path.join(vis_dir, "libs") src_dir = os.path.join(vis_dir, "src") style_dir = os.path.join(vis_dir, "style") # grab the libraries, src and style lib_dct = { 'd3': 'd3.v4.min', 'awesomplete': 'awesomplete', 'vk_beautify': 'vkBeautify' } libs = read_files(itervalues(lib_dct), libs_dir, 'js') src_names = 'constants', 'draw', 'legend', 'modal', 'ptN2', 'search', 'svg' srcs = read_files(src_names, src_dir, 'js') styles = read_files(('awesomplete', 'partition_tree'), style_dir, 'css') with open(os.path.join(style_dir, "fontello.woff"), "rb") as f: encoded_font = str(base64.b64encode(f.read()).decode("ascii")) if title: title = "OpenMDAO Model Hierarchy and N2 diagram: %s" % title else: title = "OpenMDAO Model Hierarchy and N2 diagram" h = DiagramWriter(filename=os.path.join(vis_dir, "index.html"), title=title, styles=styles, embeddable=embeddable) # put all style and JS into index h.insert('{{fontello}}', encoded_font) for k, v in iteritems(lib_dct): h.insert('{{{}_lib}}'.format(k), write_script(libs[v], indent=_IND)) for name, code in iteritems(srcs): h.insert('{{{}_lib}}'.format(name.lower()), write_script(code, indent=_IND)) h.insert('{{model_data}}', write_script(model_data, indent=_IND)) # Toolbar toolbar = h.toolbar group1 = toolbar.add_button_group() group1.add_button("Return To Root", uid="returnToRootButtonId", disabled="disabled", content="icon-home") group1.add_button("Back", uid="backButtonId", disabled="disabled", content="icon-left-big") group1.add_button("Forward", uid="forwardButtonId", disabled="disabled", content="icon-right-big") group1.add_button("Up One Level", uid="upOneLevelButtonId", disabled="disabled", content="icon-up-big") group2 = toolbar.add_button_group() group2.add_button("Uncollapse In View Only", uid="uncollapseInViewButtonId", content="icon-resize-full") group2.add_button("Uncollapse All", uid="uncollapseAllButtonId", content="icon-resize-full bigger-font") group2.add_button("Collapse Outputs In View Only", uid="collapseInViewButtonId", content="icon-resize-small") group2.add_button("Collapse All Outputs", uid="collapseAllButtonId", content="icon-resize-small bigger-font") group2.add_dropdown("Collapse Depth", button_content="icon-sort-number-up", uid="idCollapseDepthDiv") group3 = toolbar.add_button_group() group3.add_button("Clear Arrows and Connections", uid="clearArrowsAndConnectsButtonId", content="icon-eraser") group3.add_button("Show Path", uid="showCurrentPathButtonId", content="icon-terminal") group3.add_button("Show Legend", uid="showLegendButtonId", content="icon-map-signs") group3.add_button("Toggle Solver Names", uid="toggleSolverNamesButtonId", content="icon-minus") group3.add_dropdown("Font Size", id_naming="idFontSize", options=_FONT_SIZES, option_formatter=lambda x: '{}px'.format(x), button_content="icon-text-height") group3.add_dropdown("Vertically Resize", id_naming="idVerticalResize", options=_MODEL_HEIGHTS, option_formatter=lambda x: '{}px'.format(x), button_content="icon-resize-vertical", header="Model Height") group4 = toolbar.add_button_group() group4.add_button("Save SVG", uid="saveSvgButtonId", content="icon-floppy") group5 = toolbar.add_button_group() group5.add_button("Help", uid="helpButtonId", content="icon-help") # Help help_txt = ( 'Left clicking on a node in the partition tree will navigate to that node. ' 'Right clicking on a node in the model hierarchy will collapse/uncollapse it. ' 'A click on any element in the N^2 diagram will allow those arrows to persist.' ) h.add_help(help_txt, footer="OpenMDAO Model Hierarchy and N^2 diagram") if use_declare_partial_info: h.insert( '{{component_connectivity}}', 'Note: Component internal connectivity computed using derivative declarations' ) else: h.insert( '{{component_connectivity}}', 'Note: Derivative declarations ignored, so dense component connectivity is assumed' ) # Write output file h.write(outfile) # open it up in the browser if show_browser: from openmdao.devtools.webview import webview webview(outfile)
def view_model(problem_or_filename, outfile='partition_tree_n2.html', show_browser=True, offline=True, embed=False): """ Generates a self-contained html file containing a tree viewer of the specified type. Optionally pops up a web browser to view the file. Args ---- problem_or_filename : Either a Problem() or a string Problem() : The Problem (after problem.setup()) for the desired tree. string : The filename of the case recorder file containing the data required to build the tree. outfile : str, optional The name of the output html file. Defaults to 'partition_tree_n2.html'. show_browser : bool, optional If True, pop up the system default web browser to view the generated html file. Defaults to True. offline : bool, optional If True, embed the javascript d3 library into the generated html file so that the tree can be viewed offline without an internet connection. Otherwise if False, have the html request the latest d3 file from https://d3js.org/d3.v4.min.js when opening the html file. Defaults to True. embed : bool, optional If True, export only the innerHTML that is between the body tags, used for embedding the viewer into another html file. If False, create a standalone HTML file that has the DOCTYPE, html, head, meta, and body tags. Defaults to False. """ viewer = 'partition_tree_n2.template' code_dir = os.path.dirname(os.path.abspath(__file__)) with open(os.path.join(code_dir, viewer), "r") as f: template = f.read() html_begin_tags = ("<!DOCTYPE html>\n" "<html>\n" "<head>\n" " <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n" "</head>\n" "<body>\n") html_end_tags = ("</body>\n" "</html>\n") display_none_attr = "" if embed: html_begin_tags = html_end_tags = "" display_none_attr = " style=\"display:none\"" d3_library = "<script src=\"https://d3js.org/d3.v4.min.js\" charset=\"utf-8\"></script>" if offline: with open(os.path.join(code_dir, 'd3.v4.min.js'), "r") as f: d3_library = "<script type=\"text/javascript\"> %s </script>" % (f.read()) with open(os.path.join(code_dir, "fontello.woff"), "rb") as f: encoded_font = str(base64.b64encode(f.read()).decode("ascii")) with open(os.path.join(code_dir, 'awesomplete.css'), "r") as f: awesomplete_css = "%s" % (f.read()) with open(os.path.join(code_dir, 'awesomplete.js'), "r") as f: awesomplete_js = "%s" % (f.read()) if isinstance(problem_or_filename, Problem): model_viewer_data = get_model_viewer_data(problem_or_filename) else: # Do not know file type. Try opening to see what works file_type = None if is_valid_sqlite3_db(problem_or_filename): db = SqliteDict(filename=problem_or_filename, flag='r', tablename='metadata') file_type = "sqlite" else: try: hdf = h5py.File(problem_or_filename, 'r') file_type = 'hdf5' except: raise ValueError("The given filename is not one of the supported file formats: sqlite or hdf5") if file_type == "sqlite": model_viewer_data = db['model_viewer_data'] elif file_type == "hdf5": metadata = hdf.get('metadata', None) model_viewer_data = pickle.loads(metadata.get('model_viewer_data').value) tree_json = json.dumps(model_viewer_data['tree']) conns_json = json.dumps(model_viewer_data['connections_list']) with open(outfile, 'w') as f: f.write(template % (html_begin_tags, awesomplete_css, encoded_font, display_none_attr, d3_library, awesomplete_js, tree_json, conns_json, html_end_tags)) if show_browser: from openmdao.devtools.webview import webview webview(outfile)