def show_mda(self, analysis_id, pbfile, name, outfile, batch, depth): options = { "--xdsm": True, "--name": name, "--dry-run": False, "--depth": depth, } xdsm = None if pbfile: start = time.time() try: info("XDSM info retrieval...") self.push_mda_cmd(pbfile, options) except AnalysisPushedException as pushed: xdsm = pushed.xdsm end = time.time() log("Retrieved in {:.2f}s".format(end - start)) source = os.path.basename(pbfile) else: mda_id = analysis_id or get_analysis_id() if mda_id is None: error( "Unknown analysis with id={} (maybe use wop pull <analysis-id>)".format( mda_id ) ) sys.exit(-1) url = self.endpoint("/api/v1/analyses/{}.xdsm".format(mda_id)) resp = self.session.get(url, headers=self.headers) resp.raise_for_status() xdsm = resp.json() source = f"{mda_id}@{self._url}" info("XDSM building...") generate_xdsm_html(source, xdsm, outfile) if pbfile: log("XDSM of analysis from {} generated in {}".format(pbfile, outfile)) else: log("XDSM of analysis {} generated in {}".format(mda_id, outfile)) if not batch: webview(outfile)
def view_connections(root, outfile='connections.html', show_browser=True, show_values=True, precision=6, title=None): """ Generate 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. show_values : bool, optional If True, retrieve the values and display them. precision : int, optional Sets the precision for displaying array values. title : str, optional Sets the title of the web page. """ 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 connections = system._problem_meta['model_ref']()._conn_global_abs_in2out src2tgts = defaultdict(list) units = defaultdict(lambda: '') for io in ('input', 'output'): for n, data in system._var_allprocs_abs2meta[io].items(): u = data.get('units', '') if u is not None: units[n] = u vals = {} prefix = system.pathname + '.' if system.pathname else '' all_vars = {} for io in ('input', 'output'): all_vars[io] = chain(system._var_abs2meta[io].items(), [(prefix + n, m) for n, m in system._var_discrete[io].items()]) with printoptions(precision=precision, suppress=True, threshold=10000): for t, meta in all_vars['input']: s = connections[t] if show_values: if s.startswith('_auto_ivc.'): val = system.get_val(t, flat=True, get_remote=True, from_src=False) else: val = system.get_val(t, flat=True, get_remote=True) # if there's a unit conversion, express the value in the # units of the target if units[t] and s in system._outputs: val = system.get_val(t, flat=True, units=units[t], get_remote=True) else: val = system.get_val(t, flat=True, get_remote=True) else: val = '' src2tgts[s].append(t) vals[t] = val NOCONN = '[NO CONNECTION]' vals[NOCONN] = '' src_systems = set() tgt_systems = set() for s, _ in all_vars['output']: parts = s.split('.') for i in range(len(parts)): src_systems.add('.'.join(parts[:i])) for t, _ in all_vars['input']: parts = t.split('.') for i in range(len(parts)): tgt_systems.add('.'.join(parts[:i])) src_systems = [{'name': n} for n in sorted(src_systems)] src_systems.insert(1, {'name': NOCONN}) tgt_systems = [{'name': n} for n in sorted(tgt_systems)] tgt_systems.insert(1, {'name': NOCONN}) tprom = system._var_allprocs_abs2prom['input'] sprom = system._var_allprocs_abs2prom['output'] table = [] idx = 1 # unique ID for use by Tabulator for tgt, src in connections.items(): usrc = units[src] utgt = units[tgt] if usrc != utgt: # prepend these with '!' so they'll be colored red if usrc: usrc = '!' + units[src] if utgt: utgt = '!' + units[tgt] row = {'id': idx, 'src': src, 'sprom': sprom[src], 'sunits': usrc, 'val': _val2str(vals[tgt]), 'tunits': utgt, 'tprom': tprom[tgt], 'tgt': tgt} table.append(row) idx += 1 # add rows for unconnected sources for src, _ in all_vars['output']: if src not in src2tgts: if show_values: v = _val2str(system._abs_get_val(src)) else: v = '' row = {'id': idx, 'src': src, 'sprom': sprom[src], 'sunits': units[src], 'val': v, 'tunits': '', 'tprom': NOCONN, 'tgt': NOCONN} table.append(row) idx += 1 if title is None: title = '' data = { 'title': title, 'table': table, 'show_values': show_values, } viewer = 'connect_table.html' code_dir = os.path.dirname(os.path.abspath(__file__)) libs_dir = os.path.join(os.path.dirname(code_dir), 'common', 'libs') style_dir = os.path.join(os.path.dirname(code_dir), 'common', 'style') with open(os.path.join(code_dir, viewer), "r") as f: template = f.read() with open(os.path.join(libs_dir, 'tabulator.min.js'), "r") as f: tabulator_src = f.read() with open(os.path.join(style_dir, 'tabulator.min.css'), "r") as f: tabulator_style = f.read() jsontxt = json.dumps(data) with open(outfile, 'w') as f: s = template.replace("<connection_data>", jsontxt) s = s.replace("<tabulator_src>", tabulator_src) s = s.replace("<tabulator_style>", tabulator_style) f.write(s) if notebook: # display in Jupyter Notebook if not colab: display(IFrame(src=outfile, width=1000, height=1000)) else: display(HTML(outfile)) elif show_browser: # open it up in the browser from openmdao.utils.webview import webview webview(outfile)
def view_timing(timing_file, outfile='timing_report.html', show_browser=True): """ Generate a self-contained html file containing an interactive table of timing data. Optionally pops up a web browser to view the file. Parameters ---------- timing_file : str The name of the file contining the timing data. 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. Returns ------- dict Data to used to generate html file. """ timing_table = [] tot_by_rank = {} max_rank = 0 parent_dict = {} # used for computing pct of parent timing # set up timing table data for rank, probname, cname, sname, level, parallel, nprocs, method, ncalls, avgtime, mintime, \ maxtime, tottime, globaltot in _timing_file_iter(timing_file): if rank > max_rank: max_rank = rank dct = { 'id': len(timing_table) + 1, # unique ID for use by Tabulator 'rank': rank, 'probname': probname, 'classname': cname, 'sysname': sname, 'level': level, 'parallel': parallel, 'nprocs': nprocs, 'method': method, 'ncalls': ncalls, 'avgtime': avgtime, 'mintime': mintime, 'maxtime': maxtime, 'tottime': tottime, 'pct': tottime / globaltot * 100., } timing_table.append(dct) tot_by_rank[rank] = globaltot parent_dict[(rank, probname, sname, method)] = dct data = { 'title': f"Total time: {max(tot_by_rank.values() if tot_by_rank else [0.]):12.6f} sec", 'timing_table': timing_table, 'tot_procs': max_rank + 1, } if MPI is None or MPI.COMM_WORLD.rank == 0: viewer = 'timing_table.html' code_dir = os.path.dirname(os.path.abspath(__file__)) libs_dir = os.path.join(os.path.dirname(code_dir), 'common', 'libs') style_dir = os.path.join(os.path.dirname(code_dir), 'common', 'style') with open(os.path.join(code_dir, viewer), "r") as f: template = f.read() with open(os.path.join(libs_dir, 'tabulator.min.js'), "r") as f: tabulator_src = f.read() with open(os.path.join(style_dir, 'tabulator.min.css'), "r") as f: tabulator_style = f.read() with open(os.path.join(libs_dir, 'd3.v6.min.js'), "r") as f: d3_src = f.read() jsontxt = json.dumps(data, default=default_noraise) with open(outfile, 'w') as f: s = template.replace("<tabulator_src>", tabulator_src) s = s.replace("<tabulator_style>", tabulator_style) s = s.replace("<d3_src>", d3_src) s = s.replace("<timing_data>", jsontxt) f.write(s) if show_browser: webview(outfile) return data
def n2(data_source, outfile='n2.html', show_browser=True, embeddable=False, title=None, use_declare_partial_info=False): """ Generate 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. 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) # if MPI is active only display one copy of the viewer if MPI and MPI.COMM_WORLD.rank != 0: return 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) import openmdao openmdao_dir = os.path.dirname(inspect.getfile(openmdao)) vis_dir = os.path.join(openmdao_dir, "visualization/n2_viewer") libs_dir = os.path.join(vis_dir, "libs") src_dir = os.path.join(vis_dir, "src") style_dir = os.path.join(vis_dir, "style") assets_dir = os.path.join(vis_dir, "assets") # grab the libraries, src and style lib_dct = { 'd3': 'd3.v5.min', 'awesomplete': 'awesomplete', 'vk_beautify': 'vkBeautify' } libs = read_files(lib_dct.values(), libs_dir, 'js') src_names = \ 'modal', \ 'utils', \ 'SymbolType', \ 'N2TreeNode', \ 'ModelData', \ 'N2Style', \ 'N2Layout', \ 'N2MatrixCell', \ 'N2Legend', \ 'N2Matrix', \ 'N2Arrow', \ 'N2Search', \ 'N2Toolbar', \ 'N2Diagram', \ 'N2UserInterface', \ 'defaults', \ 'ptN2' srcs = read_files(src_names, src_dir, 'js') style_names = \ 'partition_tree', \ 'icon', \ 'toolbar', \ 'nodedata', \ 'legend', \ 'awesomplete' styles = read_files((style_names), style_dir, 'css') with open(os.path.join(style_dir, "icomoon.woff"), "rb") as f: encoded_font = str(base64.b64encode(f.read()).decode("ascii")) with open(os.path.join(style_dir, "logo_png.b64"), "r") as f: logo_png = str(f.read()) 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) if (embeddable): h.insert("non-embedded-n2", "embedded-n2") # put all style and JS into index h.insert('{{fontello}}', encoded_font) h.insert('{{logo_png}}', logo_png) for k, v in lib_dct.items(): h.insert('{{{}_lib}}'.format(k), write_script(libs[v], indent=_IND)) for name, code in srcs.items(): h.insert('{{{}_lib}}'.format(name.lower()), write_script(code, indent=_IND)) h.insert('{{model_data}}', write_script(model_data, indent=_IND)) # 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/expand it. ' 'A click on any element in the N2 diagram will allow those arrows to persist.' ) help_diagram_svg_filepath = os.path.join(assets_dir, "toolbar_help.svg") h.add_help(help_txt, help_diagram_svg_filepath, footer="OpenMDAO Model Hierarchy and N2 diagram") # Write output file h.write(outfile) # open it up in the browser if show_browser: from openmdao.utils.webview import webview webview(outfile)
def view_connections(root, outfile='connections.html', show_browser=True, src_filter='', tgt_filter='', precision=6, title=None): """ Generate 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. title : str, optional Sets the title of the web page. """ 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 = {} for n, data in iteritems(system._var_allprocs_abs2meta): u = data.get('units', '') if u is None: u = '' units[n] = u 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 s in system._outputs: val = convert_units(val, units[s], units[t]) src2tgts[s].append(t) else: # unconnected param val = _get_input(system, t) vals[t] = val 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])) 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"}) tprom = system._var_allprocs_abs2prom['input'] sprom = system._var_allprocs_abs2prom['output'] table = [] idx = 1 # unique ID for use by Tabulator for tgt, src in iteritems(connections): usrc = units[src] utgt = units[tgt] if usrc != utgt: # prepend these with '!' so they'll be colored red if usrc: usrc = '!' + units[src] if utgt: utgt = '!' + units[tgt] row = { 'id': idx, 'src': src, 'sprom': sprom[src], 'sunits': usrc, 'val': _val2str(vals[tgt]), 'tunits': utgt, 'tprom': tprom[tgt], 'tgt': tgt } table.append(row) idx += 1 for t in system._var_abs_names['input']: if t not in connections: row = { 'id': idx, 'src': 'NO CONNECTION', 'sprom': 'NO CONNECTION', 'sunits': '', 'val': _val2str(vals[t]), 'tunits': units[t], 'tprom': tprom[t], 'tgt': t } table.append(row) idx += 1 for src in system._var_abs_names['output']: if src not in src2tgts: row = { 'id': idx, 'src': src, 'sprom': sprom[src], 'sunits': units[src], 'val': _val2str(system._outputs[src]), 'tunits': '', 'tprom': 'NO CONNECTION', 'tgt': 'NO CONNECTION' } table.append(row) idx += 1 if title is None: title = '' data = { 'title': title, 'table': table, 'src_filter': src_filter, 'tgt_filter': tgt_filter, } viewer = 'connect_table.html' code_dir = os.path.dirname(os.path.abspath(__file__)) libs_dir = os.path.join(code_dir, 'libs') style_dir = os.path.join(code_dir, 'style') with open(os.path.join(code_dir, viewer), "r") as f: template = f.read() with open(os.path.join(libs_dir, 'tabulator.min.js'), "r") as f: tabulator_src = f.read() with open(os.path.join(style_dir, 'tabulator.min.css'), "r") as f: tabulator_style = f.read() jsontxt = json.dumps(data) with open(outfile, 'w') as f: s = template.replace("<connection_data>", jsontxt) s = s.replace("<tabulator_src>", tabulator_src) s = s.replace("<tabulator_style>", tabulator_style) f.write(s) if show_browser: webview(outfile)
def n2(data_source, outfile='n2.html', case_id=None, show_browser=True, embeddable=False, title=None, use_declare_partial_info=False): """ Generate 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. case_id : int, str, or None Case name or index of case in SQL file if data_source is a database. 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. use_declare_partial_info : ignored This option is no longer used because it is now always true. Still present for backwards compatibility. """ # grab the model viewer data model_data = _get_viewer_data(data_source, case_id=case_id) # if MPI is active only display one copy of the viewer if MPI and MPI.COMM_WORLD.rank != 0: return options = {} model_data['options'] = options if use_declare_partial_info: warn_deprecation("'use_declare_partial_info' is now the" " default and the option is ignored.") raw_data = json.dumps(model_data, default=default_noraise).encode('utf8') b64_data = str(base64.b64encode(zlib.compress(raw_data)).decode("ascii")) model_data = 'var compressedModel = "%s";' % b64_data import openmdao openmdao_dir = os.path.dirname(inspect.getfile(openmdao)) vis_dir = os.path.join(openmdao_dir, "visualization/n2_viewer") libs_dir = os.path.join(vis_dir, "libs") src_dir = os.path.join(vis_dir, "src") style_dir = os.path.join(vis_dir, "style") assets_dir = os.path.join(vis_dir, "assets") # grab the libraries, src and style lib_dct = { 'd3': 'd3.v5.min', 'awesomplete': 'awesomplete', 'vk_beautify': 'vkBeautify', 'pako_inflate': 'pako_inflate.min', 'json5': 'json5_2.2.0.min' } libs = read_files(lib_dct.values(), libs_dir, 'js') src_names = \ 'utils', \ 'SymbolType', \ 'N2TreeNode', \ 'ModelData', \ 'N2Style', \ 'N2Window', \ 'N2Layout', \ 'N2MatrixCell', \ 'N2Legend', \ 'N2Matrix', \ 'N2Arrow', \ 'N2Search', \ 'N2Toolbar', \ 'N2Diagram', \ 'NodeInfo', \ 'N2UserInterface', \ 'defaults', \ 'ptN2' srcs = read_files(src_names, src_dir, 'js') style_names = \ 'window', \ 'partition_tree', \ 'n2toolbar-icons', \ 'toolbar', \ 'legend', \ 'awesomplete' styles = read_files((style_names), style_dir, 'css') with open(os.path.join(style_dir, "n2toolbar-icons-font.woff"), "rb") as f: encoded_font = str(base64.b64encode(f.read()).decode("ascii")) with open(os.path.join(style_dir, "logo_png.b64"), "r") as f: logo_png = str(f.read()) with open(os.path.join(assets_dir, "spinner.png"), "rb") as f: waiting_icon = str(base64.b64encode(f.read()).decode("ascii")) with open(os.path.join(assets_dir, "n2toolbar_screenshot_png.b64"), "r") as f: n2toolbar_png = str(f.read()) if title: title = "OpenMDAO Model Hierarchy and N2 diagram: %s" % title else: title = "OpenMDAO Model Hierarchy and N2 diagram" src_names = ('N2ErrorHandling',) head_srcs = read_files(src_names, src_dir, 'js') h = DiagramWriter(filename=os.path.join(vis_dir, "index.html"), title=title, styles=styles, embeddable=embeddable, head_srcs=head_srcs) if (embeddable): h.insert("non-embedded-n2", "embedded-n2") # put all style and JS into index h.insert('{{n2toolbar-icons}}', encoded_font) h.insert('{{logo_png}}', logo_png) h.insert('{{waiting_icon}}', waiting_icon) h.insert('{{n2toolbar_png}}', n2toolbar_png) h.insert('{{om_version}}', openmdao_version) for k, v in lib_dct.items(): h.insert('{{{}_lib}}'.format(k), write_script(libs[v], indent=_IND)) for name, code in srcs.items(): h.insert('{{{}_lib}}'.format(name.lower()), write_script(code, indent=_IND)) h.insert('{{model_data}}', write_script(model_data, indent=_IND)) # Write output file h.write(outfile) if notebook: # display in Jupyter Notebook if not colab: display(IFrame(src=outfile, width=1000, height=1000)) else: display(HTML(outfile)) elif show_browser: # open it up in the browser from openmdao.utils.webview import webview webview(outfile)
def view_connections(root, outfile='connections.html', show_browser=True, src_filter='', tgt_filter='', precision=6): """ Generate 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 n2(data_source, outfile='n2.html', show_browser=True, embeddable=False, title=None, use_declare_partial_info=False): """ Generate 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. 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) # if MPI is active only display one copy of the viewer if MPI and MPI.COMM_WORLD.rank != 0: return 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) import openmdao openmdao_dir = os.path.dirname(inspect.getfile(openmdao)) vis_dir = os.path.join(openmdao_dir, "visualization/n2_viewer") 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.v5.min', 'awesomplete': 'awesomplete', 'vk_beautify': 'vkBeautify' } libs = read_files(itervalues(lib_dct), libs_dir, 'js') src_names = \ 'modal', \ 'utils', \ 'SymbolType', \ 'N2TreeNode', \ 'ModelData', \ 'N2Style', \ 'N2Layout', \ 'N2MatrixCell', \ 'N2Legend', \ 'N2Matrix', \ 'N2Arrow', \ 'N2Search', \ 'N2Diagram', \ 'N2UserInterface', \ 'defaults', \ 'ptN2' 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") # Write output file h.write(outfile) # open it up in the browser if show_browser: from openmdao.utils.webview import webview webview(outfile)
def view_driver_scaling(driver, outfile='driver_scaling_report.html', show_browser=True, title=None, jac=True): """ Generate a self-contained html file containing a detailed connection viewer. Optionally pops up a web browser to view the file. Parameters ---------- driver : Driver The driver used for the scaling report. 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. title : str, optional Sets the title of the web page. jac : bool If True, show jacobian information. Returns ------- dict Data to used to generate html file. """ if MPI and MPI.COMM_WORLD.rank != 0: return dv_table = [] con_table = [] obj_table = [] dv_vals = driver.get_design_var_values(get_remote=True) obj_vals = driver.get_objective_values(driver_scaling=True) con_vals = driver.get_constraint_values(driver_scaling=True) mod_meta = driver._problem().model._var_allprocs_abs2meta['output'] if driver._problem( )._metadata['setup_status'] < _SetupStatus.POST_FINAL_SETUP: raise RuntimeError( "Driver scaling report cannot be generated before calling final_setup " "on the Problem.") default = '' idx = 1 # unique ID for use by Tabulator # set up design vars table data for name, meta in driver._designvars.items(): scaler = meta['total_scaler'] adder = meta['total_adder'] ref = meta['ref'] ref0 = meta['ref0'] lower = meta['lower'] upper = meta['upper'] dval = dv_vals[name] mval = _unscale(dval, scaler, adder, default) if dval.size == 1: index = meta['indices'] if index is not None: index = index[0] index = _getdef(index, '') else: index = '' dct = { 'id': idx, 'name': name, 'size': meta['size'], 'driver_val': _get_val_and_size(dval), 'driver_units': _getdef(meta['units'], default), 'model_val': _get_val_and_size(mval), 'model_units': _getdef(mod_meta[meta['ivc_source']]['units'], default), 'ref': _get_val_and_size(ref, default), 'ref0': _get_val_and_size(ref0, default), 'scaler': _get_val_and_size(scaler, default), 'adder': _get_val_and_size(adder, default), 'lower': _get_val_and_size(lower, default), # scaled 'upper': _get_val_and_size(upper, default), # scaled 'index': index, } dv_table.append(dct) _add_child_rows(dct, mval, dval, scaler=scaler, adder=adder, ref=ref, ref0=ref0, lower=lower, upper=upper, inds=meta['indices']) idx += 1 # set up constraints table data for name, meta in driver._cons.items(): scaler = meta['total_scaler'] adder = meta['total_adder'] ref = meta['ref'] ref0 = meta['ref0'] lower = meta['lower'] upper = meta['upper'] equals = meta['equals'] print(name, "EQUALS:", _get_val_and_size(meta['equals'], default)) dval = con_vals[name] mval = _unscale(dval, scaler, adder, default) if dval.size == 1: index = meta['indices'] if index is not None: index = index[0] index = _getdef(index, '') else: index = '' dct = { 'id': idx, 'name': name, 'size': meta['size'], 'index': index, 'driver_val': _get_val_and_size(dval), 'driver_units': _getdef(meta['units'], default), 'model_val': _get_val_and_size(mval), 'model_units': _getdef(mod_meta[meta['ivc_source']]['units'], default), 'ref': _get_val_and_size(meta['ref'], default), 'ref0': _get_val_and_size(meta['ref0'], default), 'scaler': _get_val_and_size(scaler, default), 'adder': _get_val_and_size(adder, default), 'lower': _get_val_and_size(meta['lower'], default), # scaled 'upper': _get_val_and_size(meta['upper'], default), # scaled 'equals': _get_val_and_size(meta['equals'], default), # scaled 'linear': meta['linear'], } con_table.append(dct) _add_child_rows(dct, mval, dval, scaler=scaler, adder=adder, ref=ref, ref0=ref0, lower=lower, upper=upper, equals=equals, inds=meta['indices']) idx += 1 # set up objectives table data for name, meta in driver._objs.items(): scaler = meta['total_scaler'] adder = meta['total_adder'] ref = meta['ref'] ref0 = meta['ref0'] dval = obj_vals[name] mval = _unscale(dval, scaler, adder, default) if dval.size == 1: index = meta['indices'] if index is not None: index = index[0] index = _getdef(index, '') else: index = '' dct = { 'id': idx, 'name': name, 'size': meta['size'], 'index': index, 'driver_val': _get_val_and_size(dval), 'driver_units': _getdef(meta['units'], default), 'model_val': _get_val_and_size(mval), 'model_units': _getdef(mod_meta[meta['ivc_source']]['units'], default), 'ref': _get_val_and_size(meta['ref'], default), 'ref0': _get_val_and_size(meta['ref0'], default), 'scaler': _get_val_and_size(scaler, default), 'adder': _get_val_and_size(adder, default), } obj_table.append(dct) _add_child_rows(dct, mval, dval, scaler=scaler, adder=adder, ref=ref, ref0=ref0, inds=meta['indices']) idx += 1 data = { 'title': _getdef(title, ''), 'dv_table': dv_table, 'con_table': con_table, 'obj_table': obj_table, 'oflabels': [], 'wrtlabels': [], 'var_mat_list': [], 'linear': { 'oflabels': [], } } if jac and not driver._problem().model._use_derivatives: print("\nCan't display jacobian because derivatives are turned off.\n") jac = False if jac: coloring = driver._get_static_coloring() if coloring_mod._use_total_sparsity and jac: if coloring is None and driver._coloring_info['dynamic']: coloring = coloring_mod.dynamic_total_coloring(driver) # assemble data for jacobian visualization data['oflabels'] = driver._get_ordered_nl_responses() data['wrtlabels'] = list(dv_vals) totals = driver._compute_totals(of=data['oflabels'], wrt=data['wrtlabels'], return_format='array') data['linear'] = lindata = {} lindata['oflabels'] = [ n for n, meta in driver._cons.items() if meta['linear'] ] lindata['wrtlabels'] = data[ 'wrtlabels'] # needs to mimic data structure # check for separation of linear constraints if lindata['oflabels']: if set(lindata['oflabels']).intersection(data['oflabels']): # linear cons are found in data['oflabels'] so they're not separated lindata['oflabels'] = [] lindata['wrtlables'] = [] full_response_vals = con_vals.copy() full_response_vals.update(obj_vals) response_vals = {n: full_response_vals[n] for n in data['oflabels']} _compute_jac_view_info(totals, data, dv_vals, response_vals, coloring) if lindata['oflabels']: # prevent reuse of nonlinear totals save = driver._total_jac driver._total_jac = None lintotals = driver._compute_totals(of=lindata['oflabels'], wrt=data['wrtlabels'], return_format='array') lin_response_vals = { n: full_response_vals[n] for n in lindata['oflabels'] } driver._total_jac = save _compute_jac_view_info(lintotals, lindata, dv_vals, lin_response_vals, None) if driver._problem().comm.rank == 0: viewer = 'scaling_table.html' code_dir = os.path.dirname(os.path.abspath(__file__)) libs_dir = os.path.join(os.path.dirname(code_dir), 'common', 'libs') style_dir = os.path.join(os.path.dirname(code_dir), 'common', 'style') with open(os.path.join(code_dir, viewer), "r") as f: template = f.read() with open(os.path.join(libs_dir, 'tabulator.min.js'), "r") as f: tabulator_src = f.read() with open(os.path.join(style_dir, 'tabulator.min.css'), "r") as f: tabulator_style = f.read() with open(os.path.join(libs_dir, 'd3.v6.min.js'), "r") as f: d3_src = f.read() jsontxt = json.dumps(data, default=default_noraise) with open(outfile, 'w') as f: s = template.replace("<tabulator_src>", tabulator_src) s = s.replace("<tabulator_style>", tabulator_style) s = s.replace("<d3_src>", d3_src) s = s.replace("<scaling_data>", jsontxt) f.write(s) if show_browser: webview(outfile) return data
def n2(data_source, outfile=_default_n2_filename, case_id=None, show_browser=True, embeddable=False, title=None, use_declare_partial_info=False, display_in_notebook=True): """ Generate 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. case_id : int, str, or None Case name or index of case in SQL file if data_source is a database. 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. use_declare_partial_info : ignored This option is no longer used because it is now always true. Still present for backwards compatibility. display_in_notebook : bool, optional If True, display the N2 diagram in the notebook, if this is called from a notebook. Defaults to True. """ # grab the model viewer data model_data = _get_viewer_data(data_source, case_id=case_id) # if MPI is active only display one copy of the viewer if MPI and MPI.COMM_WORLD.rank != 0: return options = {} model_data['options'] = options if use_declare_partial_info: warn_deprecation("'use_declare_partial_info' is now the" " default and the option is ignored.") import openmdao openmdao_dir = os.path.dirname(inspect.getfile(openmdao)) vis_dir = os.path.join(openmdao_dir, "visualization/n2_viewer") if title: title = f"OpenMDAO Model Hierarchy and N2 diagram: {title}" else: title = "OpenMDAO Model Hierarchy and N2 diagram" html_vars = { 'title': title, 'embeddable': "embedded-n2" if embeddable else "non-embedded-n2", 'openmdao_version': openmdao_version, 'model_data': model_data } HtmlPreprocessor(os.path.join(vis_dir, "index.html"), outfile, allow_overwrite=True, var_dict=html_vars, json_dumps_default=default_noraise, verbose=False).run() if notebook: if display_in_notebook: # display in Jupyter Notebook outfile = os.path.relpath(outfile) if not colab: display(IFrame(src=outfile, width="100%", height=700)) else: display(HTML(outfile)) elif show_browser: # open it up in the browser from openmdao.utils.webview import webview webview(outfile)