Skip to content

Commit 2b7fed5

Browse files
committed
Expanding SLC docstrings
1 parent 4deb91d commit 2b7fed5

2 files changed

Lines changed: 49 additions & 6 deletions

File tree

h2integrate/control/control_strategies/system_level/demand_following_control.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,24 @@
88
class DemandFollowingControl(SystemLevelControlBase):
99
"""Demand-following system-level controller.
1010
11-
Dispatch priority:
12-
1. Curtailable techs run at rated capacity (zero marginal cost).
13-
2. Storage absorbs surplus / provides deficit (set_point = net demand).
14-
3. Remaining demand is split equally across dispatchable techs.
11+
Dispatches technologies to meet a time-varying demand profile without
12+
considering costs. The demand is satisfied in a fixed three-step priority
13+
order, and each step's shortfall or surplus is passed to the next:
1514
16-
This strategy always attempts to meet demand exactly; it does not
17-
consider costs.
15+
1. **Curtailable techs** run at their full rated capacity. Their total
16+
output is subtracted from the demand, which may drive the residual
17+
demand negative (surplus).
18+
19+
2. **Storage techs** receive the residual demand (which may be positive
20+
or negative). When demand is positive the storage is commanded to
21+
discharge; when negative it is commanded to charge. If multiple
22+
storage techs produce the demanded commodity, the residual demand is
23+
split **evenly** across them (each receives ``demand / n_storage``).
24+
25+
3. **Dispatchable techs** cover any remaining positive demand after
26+
storage. The remaining demand (floored at zero) is split **evenly**
27+
across all dispatchable techs that produce the demanded commodity
28+
(each receives ``remaining_demand / n_dispatchable``).
1829
"""
1930

2031
def compute(self, inputs, outputs):

h2integrate/control/control_strategies/system_level/solver_options.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,25 @@
99

1010
@define(kw_only=True)
1111
class SLCSolverOptionsConfig(BaseConfig):
12+
"""Configuration for the nonlinear solver used by the system-level controller.
13+
14+
Controls which OpenMDAO nonlinear solver is applied to the plant group and
15+
how it converges. The ``convergence_tolerance`` sets both ``atol`` and ``rtol``
16+
by default; either can be overridden individually.
17+
18+
Attributes:
19+
solver_name: Solver type. One of ``"gauss_seidel"``, ``"newton"``, or
20+
``"block_jacobi"``.
21+
max_iter: Maximum number of nonlinear iterations.
22+
atol: Absolute convergence tolerance. Defaults to ``convergence_tolerance``.
23+
rtol: Relative convergence tolerance. Defaults to ``convergence_tolerance``.
24+
convergence_tolerance: Convenience value used to set both ``atol`` and ``rtol``
25+
when they are not specified individually.
26+
iprint: Solver print level (0 = silent, 2 = verbose).
27+
solver_option_kwargs: Additional keyword arguments passed directly to the
28+
solver's ``options`` dict.
29+
"""
30+
1231
solver_name: str = field(
1332
default="gauss_seidel", validator=contains(["gauss_seidel", "newton", "block_jacobi"])
1433
)
@@ -19,20 +38,31 @@ class SLCSolverOptionsConfig(BaseConfig):
1938
iprint: int = field(default=2)
2039
solver_option_kwargs: dict = field(default={})
2140

41+
# Maps user-facing solver names to OpenMDAO solver classes
2242
solver_map: ClassVar = {
2343
"gauss_seidel": om.NonlinearBlockGS,
2444
"newton": om.NewtonSolver,
2545
"block_jacobi": om.NonlinearBlockJac,
2646
}
2747

2848
def __attrs_post_init__(self):
49+
# Default atol/rtol to the shared convergence_tolerance if not set
2950
if self.atol is None:
3051
self.atol = self.convergence_tolerance
3152
if self.rtol is None:
3253
self.rtol = self.convergence_tolerance
3354

3455
def get_solver_options(self):
56+
"""Build the options dict to apply to the nonlinear solver.
57+
58+
Merges config attributes with any extra ``solver_option_kwargs`` and
59+
renames ``max_iter`` to ``maxiter`` (the OpenMDAO option name).
60+
61+
Returns:
62+
dict: Keyword arguments suitable for ``solver.options[k] = v``.
63+
"""
3564
d = self.as_dict()
65+
# These attrs configure *which* solver or are handled separately
3666
non_solver_option_attrs = [
3767
"solver_name",
3868
"solver_map",
@@ -41,10 +71,12 @@ def get_solver_options(self):
4171
"max_iter",
4272
]
4373
solver_options = {k: v for k, v in d.items() if k not in non_solver_option_attrs}
74+
# Merge extra kwargs and translate max_iter → maxiter for OpenMDAO
4475
solver_options_full = (
4576
solver_options | self.solver_option_kwargs | {"maxiter": self.max_iter}
4677
)
4778
return solver_options_full
4879

4980
def return_nonlinear_solver(self):
81+
"""Return the OpenMDAO nonlinear solver class for ``solver_name``."""
5082
return self.solver_map[self.solver_name]

0 commit comments

Comments
 (0)