• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

swryan / dymos / 14268934115

04 Apr 2025 03:36PM UTC coverage: 92.828% (-0.2%) from 93.071%
14268934115

push

github

swryan
Merge branch 'master' into docs_on_tag

5 of 13 new or added lines in 3 files covered. (38.46%)

557 existing lines in 26 files now uncovered.

33343 of 35919 relevant lines covered (92.83%)

5.64 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.53
/dymos/grid_refinement/grid_refinement_ode_system.py
1
import numpy as np
5✔
2
import openmdao.api as om
5✔
3

4
from ..phase.options import TimeOptionsDictionary
5✔
5
from ..utils.misc import get_rate_units
5✔
6
from ..utils.introspection import get_targets, _get_targets_metadata
5✔
7
from ..utils.ode_utils import _make_ode_system
5✔
8
from ..transcriptions.grid_data import GridData
5✔
9

10

11
class GridRefinementODESystem(om.Group):
5✔
12
    """
13
    Defines a group that performs grid refinement on an ODE.
14

15
    The Grid Refinement algorithms in Dymos use the following approach for computing
16
    errors in the transcription:
17

18
    1. The phase segmentation is reproduced, except each segment is of one polynomial order higher
19
    (for Radau) or two orders higher (for GaussLobatto).
20
    2. An interpolation matrix (L) and an integration matrix (I) is developed that takes the solution
21
    from 'all' nodes of the existing solution and interpolates it onto 'all' nodes of the higher-order segmentation.
22
    For states, this provides a reference set of interpolated values (x_hat).
23
    3. The ODE is evaluated at all nodes of the new, higher-order grid.  The resulting state-rate
24
    output values are used to integrate the state values onto 'all' nodes in the higher-order segmentation.
25
    These states are a new estimate of the state values at each node (x_prime).
26
    4. The error is computed by comparing x_hat to x_prime.
27

28
    This system is used to evaluate the ODE at all nodes within the higher-order segmentation.
29

30
    Parameters
31
    ----------
32
    **kwargs : dict
33
        Dictionary of optional arguments.
34
    """
35
    def initialize(self):
5✔
36
        """
37
        Declare options for this Group.
38
        """
39
        self.options.declare('grid_data', types=GridData, desc='Container object for grid info')
7✔
40

41
        self.options.declare('time', types=TimeOptionsDictionary,
7✔
42
                             desc='Time options for the phase')
43

44
        self.options.declare('states', types=dict,
7✔
45
                             desc='Dictionary of state names/options for the segments parent Phase')
46

47
        self.options.declare('controls', default=None, types=dict, allow_none=True,
7✔
48
                             desc='Dictionary of control names/options for the segments parent Phase.')
49

50
        self.options.declare('parameters', default=None, types=dict, allow_none=True,
7✔
51
                             desc='Dictionary of parameter names/options for the segments '
52
                                  'parent Phase.')
53

54
        self.options.declare('ode_class',
7✔
55
                             desc='System defining the ODE')
56

57
        self.options.declare('ode_init_kwargs', types=dict, default={},
7✔
58
                             desc='Keyword arguments provided when initializing the ODE System')
59

60
        self.options.declare('calc_exprs', types=dict, default={},
7✔
61
                             desc='Calculation expressions from the parent phase.')
62

63
    def setup(self):
5✔
64
        """
65
        Add the ODE subsystem.
66
        """
67
        grid_data = self.options['grid_data']
7✔
68
        num_nodes = grid_data.num_nodes
7✔
69
        ode_class = self.options['ode_class']
7✔
70
        ode_init_kwargs = self.options['ode_init_kwargs']
7✔
71
        calc_exprs = self.options['calc_exprs']
7✔
72

73
        # The ODE System
74
        if ode_class is not None:
7✔
75
            ode_sys = _make_ode_system(ode_class=ode_class,
7✔
76
                                       num_nodes=num_nodes,
77
                                       ode_init_kwargs=ode_init_kwargs,
78
                                       calc_exprs=calc_exprs,
79
                                       parameter_options=self.options['parameters'])
80
            self.add_subsystem('ode', subsys=ode_sys)
7✔
81

82
    def configure(self):
5✔
83
        """
84
        Promote variables from the ODE.
85
        """
86
        grid_data = self.options['grid_data']
7✔
87
        num_nodes = grid_data.num_nodes
7✔
88

89
        # Configure time
90
        options = self.options['time']
7✔
91

92
        # time
93
        targets = get_targets(self.ode, 'time', options['targets'])
7✔
94
        for tgt in targets:
7✔
UNCOV
95
            self.promotes('ode', inputs=[(tgt, 'time')])
×
96
        if targets:
7✔
UNCOV
97
            self.set_input_defaults(name='time', val=np.ones(num_nodes), units=options['units'])
×
98

99
        # time_phase
100
        targets = get_targets(self.ode, 't_phase', options['time_phase_targets'])
7✔
101
        for tgt in targets:
7✔
UNCOV
102
            self.promotes('ode', inputs=[(tgt, 't_phase')])
×
103
        if targets:
7✔
UNCOV
104
            self.set_input_defaults(name='t_phase', val=np.ones(num_nodes), units=options['units'])
×
105

106
        # t_initial
107
        targets = get_targets(self.ode, 't_initial', options['t_initial_targets'])
7✔
108
        for tgt in targets:
7✔
UNCOV
109
            self.promotes('ode', inputs=[(tgt, 't_initial')])
×
110
        if targets:
7✔
UNCOV
111
            self.set_input_defaults(name='t_initial', val=np.ones(num_nodes), units=options['units'])
×
112

113
        # t_duration
114
        targets = get_targets(self.ode, 't_duration', options['t_duration_targets'])
7✔
115
        for tgt in targets:
7✔
UNCOV
116
            self.promotes('ode', inputs=[(tgt, 't_duration')])
×
117
        if targets:
7✔
UNCOV
118
            self.set_input_defaults(name='t_duration', val=np.ones(num_nodes), units=options['units'])
×
119

120
        # Configure the states
121
        for name, options in self.options['states'].items():
7✔
122
            targets = get_targets(self.ode, name, options['targets'])
7✔
123
            for tgt in targets:
7✔
124
                self.promotes('ode', inputs=[(tgt, f'states:{name}')])
7✔
125
            if targets:
7✔
126
                self.set_input_defaults(name=f'states:{name}',
7✔
127
                                        val=np.ones(num_nodes),
128
                                        units=options['units'])
129

130
        # Configure the controls
131
        for name, options in self.options['controls'].items():
7✔
132
            rate_units = get_rate_units(units=options['units'],
7✔
133
                                        time_units=self.options['time']['units'])
134
            rate2_units = get_rate_units(units=options['units'],
7✔
135
                                         time_units=self.options['time']['units'],
136
                                         deriv=2)
137

138
            targets = get_targets(self.ode, name, options['targets'])
7✔
139
            if targets:
7✔
140
                for tgt in targets:
7✔
141
                    self.promotes('ode', inputs=[(tgt, f'controls:{name}')])
7✔
142
                self.set_input_defaults(name=f'controls:{name}',
7✔
143
                                        val=np.ones(num_nodes),
144
                                        units=options['units'])
145

146
            targets = get_targets(self.ode, f'{name}_rate', options['rate_targets'])
7✔
147
            if targets:
7✔
148
                for tgt in targets:
×
149
                    self.promotes('ode', inputs=[(tgt, f'control_rates:{name}_rate')])
×
150
                self.set_input_defaults(name=f'control_rates:{name}_rate',
×
151
                                        val=np.ones(num_nodes),
152
                                        units=rate_units)
153

154
            targets = get_targets(self.ode, f'{name}_rate2', options['rate2_targets'])
7✔
155
            if targets:
7✔
UNCOV
156
                for tgt in targets:
×
UNCOV
157
                    self.promotes('ode', inputs=[(tgt, f'control_rates:{name}_rate2')])
×
UNCOV
158
                self.set_input_defaults(name=f'control_rates:{name}_rate2',
×
159
                                        val=np.ones(num_nodes),
160
                                        units=rate2_units)
161

162
        # Configure the parameters
163
        for name, options in self.options['parameters'].items():
7✔
164
            static_targets = options['static_targets']
7✔
165
            shape = options['shape']
7✔
166
            prom_name = f'parameters:{name}'
7✔
167
            targets = _get_targets_metadata(self.ode, name, options['targets'])
7✔
168
            for tgt, meta in targets.items():
7✔
169
                if tgt in static_targets:
7✔
170
                    self.promotes('ode', inputs=[(tgt, prom_name)])
4✔
171
                else:
172
                    self.promotes('ode', inputs=[(tgt, prom_name)],
7✔
173
                                  src_indices=om.slicer[np.zeros(num_nodes, dtype=int), ...])
174
            if targets:
7✔
175
                self.set_input_defaults(name=prom_name,
7✔
176
                                        src_shape=shape,
177
                                        units=options['units'])
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc