diff --git a/aviary/core/aviary_problem.py b/aviary/core/aviary_problem.py index f4b1f42566..9ee74f124b 100644 --- a/aviary/core/aviary_problem.py +++ b/aviary/core/aviary_problem.py @@ -88,7 +88,9 @@ def __init__( 'run_status', 'sizing_results', 'input_checks', - 'overridden_variables', + 'overridden_variables_setup', + 'overridden_variables_run_model', + 'overridden_variables_run_driver', 'list_options', ] for report in new_reports: diff --git a/aviary/docs/examples_unreviewed/multi_mission.ipynb b/aviary/docs/examples_unreviewed/multi_mission.ipynb index 16d983b418..605266f56e 100644 --- a/aviary/docs/examples_unreviewed/multi_mission.ipynb +++ b/aviary/docs/examples_unreviewed/multi_mission.ipynb @@ -568,7 +568,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.12" + "version": "3.12.3" } }, "nbformat": 4, diff --git a/aviary/interface/reports.py b/aviary/interface/reports.py index d7c874f846..f8931c1689 100644 --- a/aviary/interface/reports.py +++ b/aviary/interface/reports.py @@ -4,6 +4,7 @@ import time from pathlib import Path +import networkx as nx import numpy as np import pandas as pd from openmdao.utils.mpi import MPI @@ -80,7 +81,7 @@ def register_custom_reports(): ) register_report( - name='overridden_variables', + name='overridden_variables_setup', func=overridden_variables_report, desc='Generates a report on the overridden variables', class_name='AviaryProblem', @@ -88,6 +89,24 @@ def register_custom_reports(): pre_or_post='post', ) + register_report( + name='overridden_variables_run_model', + func=overridden_variables_report, + desc='Generates a report on the overridden variables', + class_name='AviaryProblem', + method='run_model', + pre_or_post='post', + ) + + register_report( + name='overridden_variables_run_driver', + func=overridden_variables_report, + desc='Generates a report on the overridden variables', + class_name='AviaryProblem', + method='run_driver', + pre_or_post='post', + ) + register_report( name='list_options', func=_list_options_report, @@ -675,6 +694,31 @@ def _overridden_variables_group_report(prob, group, mission_name, f): val = group.aviary_inputs.get_val(aircraft_variable_name, units=units) non_external_overridden_variables.append((aircraft_variable_name, val, units)) + var_abs = group.list_outputs(out_stream=None, val=False) + var_prom = [v['prom_name'] for k, v in var_abs] + prom2abs = prob.model._resolver.absnames + abs2prom = prob.model._resolver.abs2prom + graph = prob.model._relevance._graph + all_desvars = prob.driver._designvars + all_responses = [z['source'] for z in prob.driver._responses.values()] + + overridden_prom = [v for v in var_prom if 'OVERRIDE' in v] + + overriding_abs = {} + for prom_name in overridden_prom: + adh_prom = prom_name.partition(':')[-1] + + try: + targets = prom2abs(adh_prom, 'input') + except: + continue + try: + srcs = prom2abs(prom_name, 'output') + except: + continue + + overriding_abs[adh_prom] = (srcs, targets) + if MPI and prob.comm.rank != 0: # All collective calls are completed. Reports only generated on rank 0. return @@ -683,6 +727,64 @@ def _overridden_variables_group_report(prob, group, mission_name, f): if mission_name: f.write(f'# MULTIMISSION: {mission_name}\n\n') + f.write('## Potential Problems\n') + problems_found = False + for name, data in overriding_abs.items(): + srcs, targets = data + src = srcs[0] + + relevant_targets = [t.rpartition('.')[0] for t in targets] + + if relevant_targets: + # True variable relevancy check for design vars. + upstream_nodes = nx.ancestors(graph, src) + up_leaves = [abs2prom(node) for node in upstream_nodes if '_auto_ivc.' in node] + up_leaves = [node for node in up_leaves if node in all_desvars] + + if len(up_leaves) < 1: + continue + + # True variable relevancy check for responses. + abs_names = prom2abs(name) + downstream_nodes = set() + for abs_name in abs_names: + downstream_nodes.update(nx.descendants(graph, abs_name)) + + down_leaves = [node for node in downstream_nodes if node in all_responses] + + if len(down_leaves) < 1: + continue + + v1 = prob.get_val(name) + v2 = prob.get_val(src) + problems_found = True + + f.write('\n') + f.write(f'Override: **{name}**\n') + f.write('```\n') + f.write(f' Override Value:{v1}, Computed Value:{v2}\n') + f.write('\n') + + f.write(' depends on:\n') + for leaf in sorted(up_leaves): + f.write(f' {leaf}\n') + f.write('\n') + + f.write(' feeds:\n') + for target in sorted(relevant_targets): + f.write(f' {target}\n') + f.write('\n') + + f.write(' impacts:\n') + for leaf in sorted(down_leaves): + f.write(f' {leaf}\n') + f.write('```\n') + + if not problems_found: + f.write(' None\n\n') + else: + f.write('\n\n') + f.write('## Internal Overrides\n') if non_external_overridden_variables: f.write('| Name | Value | Units |\n') diff --git a/aviary/visualization/dashboard.py b/aviary/visualization/dashboard.py index 7c39aac36f..af5930357d 100644 --- a/aviary/visualization/dashboard.py +++ b/aviary/visualization/dashboard.py @@ -294,7 +294,7 @@ def create_report_frame(documentation, format, text_filepath): # need to deal with some encoding errors file_text = file_text.decode('latin-1') if format == 'markdown': - report_pane = pn.pane.Markdown(file_text) + report_pane = pn.pane.Markdown(file_text, hard_line_break=True) elif format == 'text': report_pane = pn.pane.Str(file_text) report_pane = pn.Column(