You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Dymos导出导数/雅可比矩阵时KeyError问题求助

导出Dymos轨迹梯度时出现KeyError问题的解决方法

我尝试从evtol_dymos_vectorized.py文件中导出梯度信息,参考相关方法并按OpenMDAO的指引记录导数,却遇到错误:KeyError: 'traj.phases.phase0->final_boundary_constraint->x'。

我的实现代码

import openmdao.api as om
import dymos as dm
import numpy as np
import matplotlib.pyplot as plt

import sys

sys.path.insert(0, "../ode")

from evtol_dynamics_comp_vectorized import Dynamics as DynamicsVectorized

import verify_data

if __name__ == '__main__':
    input_arg_1 = 0.0

    input_arg_2 = 'ns'

    # Some specifications
    prop_rad = 0.75
    wing_S = 9.
    wing_span = 6.
    num_blades = 3.
    blade_chord = 0.1
    num_props = 8

    # User-specified input dictionary
    input_dict = {'T_guess': 9.8 * 725 * 1.2,  # initial thrust guess
                  'x_dot_initial': 0.,  # initial horizontal speed
                  'y_dot_initial': 0.01,  # initial vertical speed
                  'y_initial': 0.01,  # initial vertical displacement
                  'A_disk': np.pi * prop_rad ** 2 * num_props,  # total propeller disk area
                  'AR': wing_span ** 2 / (0.5 * wing_S),  # aspect ratio of each wing
                  'e': 0.68,  # span efficiency factor of each wing
                  't_over_c': 0.12,  # airfoil thickness-to-chord ratio
                  'S': wing_S,  # total wing reference area
                  'CD0': 0.35 / wing_S,  # coefficient of drag of the fuselage, gear, etc.
                  'm': 725.,  # mass of aircraft
                  'a0': 5.9,  # airfoil lift-curve slope
                  'alpha_stall': 15. / 180. * np.pi,  # wing stall angle
                  'rho': 1.225,  # air density
                  'induced_velocity_factor': int(input_arg_1) / 100.,  # induced-velocity factor
                  'stall_option': input_arg_2,  # stall option: 's' allows stall, 'ns' does not
                  'R': prop_rad,  # propeller radius
                  'solidity': num_blades * blade_chord / np.pi / prop_rad,  # solidity
                  'omega': 136. / prop_rad,  # angular rotation rate
                  'prop_CD0': 0.012,  # CD0 for prop profile power
                  'k_elec': 0.9,  # electrical and mechanical losses factor
                  'k_ind': 1.2,  # induced-losses factor
                  'nB': num_blades,  # number of blades per propeller
                  'bc': blade_chord,  # representative blade chord
                  'n_props': num_props  # number of propellers
                  }

    p = om.Problem()

    traj = dm.Trajectory()
    p.model.add_subsystem('traj', traj)

    phase = dm.Phase(transcription=dm.GaussLobatto(num_segments=10, order=3, solve_segments=False,
                                                   compressed=False),
                     ode_class=DynamicsVectorized,
                     ode_init_kwargs={'input_dict': input_dict})

    traj.add_phase('phase0', phase)

    phase.set_time_options(fix_initial=True, duration_bounds=(5, 60), duration_ref=30)
    phase.add_state('x', fix_initial=True, rate_source='x_dot', ref0=0, ref=900, defect_ref=100)
    phase.add_state('y', fix_initial=True, rate_source='y_dot', ref0=0, ref=300, defect_ref=300)
    phase.add_state('vx', fix_initial=True, rate_source='a_x', ref0=0, ref=10)
    phase.add_state('vy', fix_initial=True, rate_source='a_y', ref0=0, ref=10)
    phase.add_state('energy', fix_initial=True, rate_source='energy_dot', ref0=0, ref=1E7, defect_ref=1E5)

    phase.add_control('power', lower=1e3, upper=311000, ref0=1e3, ref=311000, rate_continuity=False)
    phase.add_control('theta', lower=0., upper=3 * np.pi / 4, ref0=0, ref=3 * np.pi / 4,
                      rate_continuity=False)

    phase.add_timeseries_output(['CL', 'CD'])

    # Objective
    phase.add_objective('energy', loc='final', ref0=0, ref=1E7)

    # Boundary Constraints
    phase.add_boundary_constraint('y', loc='final', lower=305,
                                  ref=100)  # Constraint for the final vertical displacement
    phase.add_boundary_constraint('x', loc='final', equals=900,
                                  ref=100)  # Constraint for the final horizontal displacement
    phase.add_boundary_constraint('x_dot', loc='final', equals=67.,
                                  ref=100)  # Constraint for the final horizontal speed

    # Path Constraints
    phase.add_path_constraint('y', lower=0., upper=305,# was 305
                              ref=300)  # Constraint for the minimum vertical displacement
    phase.add_path_constraint('acc', upper=0.3,
                              ref=1.0)  # Constraint for the acceleration magnitude
    phase.add_path_constraint('aoa', lower=-np.radians(15), upper=np.radians(15), ref0=-np.radians(15),
                              ref=np.radians(15))  # Constraint for the angle of attack
    phase.add_path_constraint('thrust', lower=10, ref0=10,
                              ref=100)  # Constraint for the thrust magnitude

    # # Setup the driver
    driver = p.driver = om.pyOptSparseDriver()

    # p.driver.options['optimizer'] = 'SNOPT'
    # p.driver.opt_settings['Major optimality tolerance'] = 1e-4
    # p.driver.opt_settings['Major feasibility tolerance'] = 1e-6
    # p.driver.opt_settings['Major iterations limit'] = 1000
    # p.driver.opt_settings['Minor iterations limit'] = 100_000_000
    # p.driver.opt_settings['iSumm'] = 6

    driver.options['optimizer'] = 'IPOPT'
    driver.opt_settings['max_iter'] = 1000
    driver.opt_settings['alpha_for_y'] = 'safer-min-dual-infeas'
    driver.opt_settings['print_level'] = 5
    driver.opt_settings['nlp_scaling_method'] = 'gradient-based'
    driver.opt_settings['tol'] = 5.0E-5

    driver.declare_coloring(tol=1.0E-8)
    
    ## Added this to record the derivatives (gradient info)
    driver.recording_options['record_derivatives'] = True
    driver.add_recorder(om.SqliteRecorder('cases.sql'))

    p.setup()
    p.set_solver_print(0)

    p.set_val('traj.phase0.t_initial', 0.0)
    p.set_val('traj.phase0.t_duration', 30)
    p.set_val('traj.phase0.states:x', phase.interpolate(ys=[0, 900], nodes='state_input'))
    p.set_val('traj.phase0.states:y', phase.interpolate(ys=[0.01, 300], nodes='state_input'))
    p.set_val('traj.phase0.states:vx', phase.interpolate(ys=[0, 60], nodes='state_input'))
    p.set_val('traj.phase0.states:vy', phase.interpolate(ys=[0.01, 10], nodes='state_input'))
    p.set_val('traj.phase0.states:energy', phase.interpolate(ys=[0, 1E7], nodes='state_input'))

    p.set_val('traj.phase0.controls:power', phase.interpolate(xs=np.linspace(0, 28.368, 500),
                                                              ys=verify_data.powers.ravel(),
                                                              nodes='control_input'))
    p.set_val('traj.phase0.controls:theta', phase.interpolate(xs=np.linspace(0, 28.368, 500),
                                                              ys=verify_data.thetas.ravel(),
                                                              nodes='control_input'))

    p.set_val('traj.phase0.controls:power', 200000.0)
    p.set_val('traj.phase0.controls:theta', phase.interpolate(ys=[0.001, np.radians(85)], nodes='control_input'))


    dm.run_problem(p, run_driver=True, simulate=True)
    
    p.cleanup()

    
## Added this section to access the recorded derivaives
cr = om.CaseReader('cases.sql')

# Get derivatives associated with the last iteration.
deriv_1 = cr.get_case(-1).derivatives

print("deriv_1 = ",deriv_1)

报错输出

File "/home/sps79w/anaconda3/envs/Aero_Optimize/lib/python3.9/site-packages/spyder_kernels/py3compat.py", line 356, in compat_exec
    exec(code, globals, locals)

  File "/home/sps79w/Documents/PhD_Code/RevHack2020-master/problems/evtol_trajectory/evtol_dymos/evtol_dymos_vectorized.py", line 153, in <module>
    cr = om.CaseReader('cases.sql')

  File "/home/sps79w/anaconda3/envs/Aero_Optimize/lib/python3.9/site-packages/openmdao/recorders/case_reader.py", line 26, in CaseReader
    return SqliteCaseReader(filename, pre_load, metadata_filename)

  File "/home/sps79w/anaconda3/envs/Aero_Optimize/lib/python3.9/site-packages/openmdao/recorders/sqlite_reader.py", line 168, in __init__
    self._load_cases()

  File "/home/sps79w/anaconda3/envs/Aero_Optimize/lib/python3.9/site-packages/openmdao/recorders/sqlite_reader.py", line 360, in _load_cases
    self._driver_cases._load_cases()

  File "/home/sps79w/anaconda3/envs/Aero_Optimize/lib/python3.9/site-packages/openmdao/recorders/sqlite_reader.py", line 1259, in _load_cases
    for case in self.cases(cache=True):

  File "/home/sps79w/anaconda3/envs/Aero_Optimize/lib/python3.9/site-packages/openmdao/recorders/sqlite_reader.py", line 1420, in cases
    case = Case('driver', row, self._prom2abs, self._abs2prom, self._abs2meta,

  File "/home/sps79w/anaconda3/envs/Aero_Optimize/lib/python3.9/site-packages/openmdao/recorders/case.py", line 191, in __init__
    self.derivatives = PromAbsDict(jacobian, prom2abs['output'], abs2prom['output'],

  File "/home/sps79w/anaconda3/envs/Aero_Optimize/lib/python3.9/site-packages/openmdao/recorders/case.py", line 938, in __init__
    abs_keys, prom_key = self._deriv_keys(key)

  File "/home/sps79w/anaconda3/envs/Aero_Optimize/lib/python3.9/site-packages/openmdao/recorders/case.py", line 984, in _deriv_keys
    abs_of = [of] if of in abs2prom else prom2abs[of]

KeyError: 'traj.phases.phase0->final_boundary_constraint->x'

解决方案

这个错误源于Dymos生成的边界约束内部名称与你尝试访问的路径不匹配,或是记录器配置时机不当导致元数据映射错误,可按以下步骤修复:

1. 调整记录器配置时机

将记录器的配置代码移到p.setup()之后执行,确保问题所有变量和约束的路径已正确生成:

p.setup()
p.set_solver_print(0)

# 移动记录器配置到setup之后
driver.recording_options['record_derivatives'] = True
driver.add_recorder(om.SqliteRecorder('cases.sql'))

2. 确认约束的实际绝对路径

在p.setup()之后打印所有约束的绝对路径,获取正确的访问键名:

p.setup()
# 打印所有约束的绝对路径
for con in p.model.list_constraints(return_format='dict').keys():
    print(f"Constraint absolute path: {con}")

3. 避免提前执行cleanup

p.cleanup()会清除问题的元数据,导致CaseReader无法正确解析记录的导数,直接移除该行代码即可。

4. 版本兼容性检查

确保OpenMDAO和Dymos版本兼容(例如OpenMDAO 3.x搭配Dymos 1.x),版本不匹配也可能导致此类路径映射问题。

修改后的核心代码片段:

# ... 其他代码保持不变 ...

p.setup()
p.set_solver_print(0)

# 配置记录器
driver.recording_options['record_derivatives'] = True
driver.add_recorder(om.SqliteRecorder('cases.sql'))

# 打印约束路径(可选)
for con in p.model.list_constraints(return_format='dict').keys():
    print(f"Constraint path: {con}")

# ... 变量初始化代码保持不变 ...

dm.run_problem(p, run_driver=True, simulate=True)

# 移除p.cleanup()

# 访问导数
cr = om.CaseReader('cases.sql')
deriv_1 = cr.get_case(-1).derivatives
print("deriv_1 = ", deriv_1)

内容的提问来源于stack exchange,提问作者MSTGrad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 23:55:37