Closed-loop evidence diagnostics
The inverter-control evaluators are deliberately stateless: they map a local voltage measurement and an operating request to a current command. For a network study, that map can be embedded in a reduced feeder model, a quasi-static re-solve, or a measured/discrete-time experiment. This page provides two lightweight diagnostics for that outer loop:
fixed_point_oracleiterates a supplied map and reports convergence, residuals, and short repeated-state cycles.screen_fixed_point_gaincomputes a finite-difference Jacobian and reports its spectral radius and induced infinity norm.
These are evidence-generation tools, not formal dynamic stability proofs. A fixed point can be locally attractive while a different initial condition fails, and a local gain screen says nothing about response time, delays, sampling, ramps, protection state, or saturation outside the linearisation point. Those effects must be represented in the supplied map when they are material to the question.
Inverter-specific campaign adapters
inverter_control_fixed_point_oracle wraps the exact three-phase controller law around a supplied affine feeder sensitivity. It makes the reference operating voltage explicit and iterates
\[v_{k+1} = v_{ref} + Z\,[i_{exact}(v_k)-i_{exact}(v_{ref})].\]
This is the smallest useful equilibrium oracle for a campaign: it can expose cycling, multiple initial-condition basins, and the distance from a smooth equilibrium before a full feeder re-solve is commissioned. Z is still a study input, not an inferred network property; use a full network callback when voltage sensitivity varies materially over the operating range.
When a complete Dict network and ControlledDevice{AdvancedInverter} are available, solve_inverter_control_network_fixed_point performs that higher- fidelity callback internally. It solves the smooth controlled case first, then rebuilds the physical plant at every exact controller iterate with the selected converter- or grid-side current fixed. A non-publishable plant solve is retained as an equilibrium failure; it is not converted into a false convergence result. The returned and iterated voltages use the controller's declared sensing reference: phase-to-neutral when neutral is present, otherwise phase-to-ground.
For a selected fleet, use solve_controlled_inverter_fleet_network_fixed_point. All exact current targets are fixed in one re-solve, so voltage coupling between controlled devices and unselected native IBRs is preserved. The deterministic controlled_inverter_network_fixed_point_rows output is intended for campaign tables and includes smooth-versus-exact voltage/current and P/Q differences, iteration, cycle, and publishability fields.
Use solve_controlled_inverter_fleet_multistart when one initial condition is not enough evidence. Starts are named and sorted deterministically, and the smooth fleet solve is reused. controlled_inverter_fleet_multistart_rows reports convergence, cycles, residuals, and final voltage spread relative to the first converged start, plus the maximum pairwise spread across all converged starts. Non-converged iterates are not labelled as equilibria and receive NaN spread fields. A nonzero spread is evidence of initial-condition dependence; it is not by itself a proof of multiple physical equilibria.
inverter_control_network_voltage_sensitivity estimates the feeder matrix directly by perturbing the selected physical current target and rebuilding the plant. It records plus/minus SolveStatus values for every column and leaves failed columns as NaN; this makes current-limit boundaries visible in the loop-gain evidence instead of silently extrapolating through them. The result uses a 6×6 phase-rectangular matrix for four-leg devices and a 4×4 positive/negative-sequence matrix for three-leg converter-current targets, because independent phase-current perturbations violate the three-leg zero-neutral-current constraint. The reduced screen intentionally projects sensed phase voltage onto U₁/U₂; if a study's controller is materially sensitive to common-mode voltage, retain that channel in a separate phase-ground study rather than treating the 4×4 screen as a complete six-coordinate model.
For a selected fleet, controlled_inverter_fleet_network_voltage_sensitivity performs the same audit with all selected current targets fixed simultaneously. Its 6N × 6N four-leg or 4N × 4N three-leg converter-current matrix retains cross-device coupling, rather than assembling independent one-device sensitivities. Device blocks and perturbation columns are ordered by sorted device id, and failed columns remain NaN with their plus/minus statuses retained. Feed the resulting matrix to controlled_inverter_fleet_loop_gain together with the fleet's local voltage measurements to obtain a block-diagonal controller Jacobian composed with the physical network response. controlled_inverter_fleet_network_sensitivity_rows converts the matrix and its per-column solve provenance to deterministic long-form records for CSV, Arrow, or DataFrame campaign outputs.
The InverterControlScalingAudit returned by inverter_control_scaling_audit records the SI starts/scales for voltage, sequence voltage, current, apparent power, and the priority-capacity auxiliary, together with the rating-relative smoothing widths. Record it with each campaign manifest. In particular, the capacity auxiliary is scaled to the residual capability implied by priority_headroom_fraction, not to the full apparent-power rating.
Generic fixed-point oracle
The callable passed to fixed_point_oracle accepts and returns a real vector. The iteration is
\[x_{k+1} = (1-\alpha)x_k + \alpha F(x_k), \qquad 0 < \alpha \le 1.\]
Convergence is declared when the update residual is below an absolute-plus- relative tolerance. The optional trajectory includes the initial state and all iterates, making it suitable for small reproducibility plots or tabular evidence. A repeated state within cycle_window is reported as a cycle rather than being silently labelled a failed solve.
map(x) = feeder_resolve_or_measurement_map(x)
oracle = fixed_point_oracle(
map, initial_voltage;
relaxation=0.8,
max_iterations=100,
atol=1e-8,
rtol=1e-8,
)
oracle.converged, oracle.residual_norm, oracle.iterationsThe map may be a reduced sensitivity model or a complete small feeder re-solve. The oracle does not assume that the vector is voltage, nor does it assign physical time to an iteration.
Local loop-gain screen
For a map F, screen_fixed_point_gain(F, x₀) evaluates
\[J_F(x_0) = \frac{\partial F}{\partial x}(x_0).\]
The reported spectral_radius is the largest eigenvalue magnitude. A value below threshold is labelled local_contractive; this is explicitly the unrelaxed Picard screen and the margin is threshold - spectral_radius. maximum_real_eigenvalue and continuous_time_margin apply the ideal first-order response model, while alpha_max gives the largest update-to- response-time ratio predicted by the relaxed discrete iteration. These are local numerical screens, not certificates for a nonlinear or hybrid controller.
For a four-leg controller adapter, the state is six real rectangular coordinates in declared a-b-c order:
(Re Va, Im Va, Re Vb, Im Vb, Re Vc, Im Vc).inverter_control_current_jacobian differentiates the exact evaluator. A caller supplies a 6×6 real voltage_sensitivity matrix, Z, that maps a phase-current perturbation to a phase-voltage perturbation. The adapter then screens the local loop
\[J_{loop} = Z\,\frac{\partial i}{\partial v}.\]
For a three-leg converter-current target, use the reduced four-coordinate positive/negative-sequence basis (Re U₁, Im U₁, Re U₂, Im U₂) and the corresponding 4×4 network sensitivity. Independent phase-current perturbations are not physically realizable because the converter enforces zero neutral current. Grid-current targets may have a different realizable subspace when phase-to-ground shunts or an LCL are present; the sensitivity result records the coordinate system selected for the target.
The units of Z must match the phasor convention used by the supplied measurement and evaluator (normally V RMS per A RMS in rectangular coordinates). Its sign and reference direction are part of the caller's network convention; physical network sensitivities returned by inverter_control_network_voltage_sensitivity and its fleet counterpart use the network's positive-current injection convention and phase-to-neutral voltages whenever the inverter declares a neutral.
controller = SequenceController(PositiveSequenceVoltVarWatt(...))
measurement = InverterControlMeasurement(phase_voltage)
request = InverterControlRequest(
p_available=p_available, p_rated=p_rated, q_scale=q_scale)
ratings = InverterControlRatings(s_max=s_max, i_max=i_max)
J_i = inverter_control_current_jacobian(
controller, measurement, request, ratings)
screen = inverter_control_loop_gain(
controller, measurement, request, ratings, Z)
screen.spectral_radius, screen.maximum_real_eigenvalue,
screen.continuous_time_margin, screen.alpha_maxThe adapter uses the exact piecewise controller law, so a finite-difference point on a hard curve corner can be one-sided or numerically ambiguous. For evidence, record the finite-difference step and perturbation point, and use evaluate_smooth or a smooth feeder map when a differentiable surrogate is the intended object of study. The smooth surrogate is a numerical modelling choice; it does not change the firmware interpretation of the exact law.
Recommended evidence record
For each operating point, retain:
- the controller, topology, current-target, and limiter configuration;
- the voltage phasors and request/ratings used for the evaluation;
- the supplied feeder sensitivity or outer-loop map provenance;
iterations,residual_norm, and any detectedcycle_period;- finite-difference step, spectral radius, induced norm, threshold, and
local_contractive unrelaxed Picard result, maximum_real_eigenvalue, continuous_time_margin, and alpha_max response-rate screen;
- whether the point is inside a curve corner, current limit, or apparent-power limit.
Across a campaign, compare these diagnostics with the physical quantities in the network result: per-leg current, converter/grid terminal current, sequence power, zero/negative-sequence voltage, and DC-link ripple. This keeps the diagnostic layer reusable for general inverter studies while providing an auditable path to standards-oriented evidence when the study assumptions are fixed separately.
API
PowerOptLab.FixedPointIterationResult — Type
FixedPointIterationResultResult of fixed_point_oracle. trajectory stores column vectors, including the initial state, when trajectory storage is enabled.
This is an empirical equilibrium/convergence diagnostic. It is not a proof of global existence, uniqueness, or closed-loop stability.
PowerOptLab.FixedPointGainScreen — Type
Result of a finite-difference local fixed-point loop-gain screen.
PowerOptLab.InverterControlScalingAudit — Type
InverterControlScalingAuditSI-valued audit of the auxiliary starts, scales, and smoothing widths used by the smooth sequence-controller formulation. The report is intentionally pure numeric: it can be recorded with a study manifest without constructing a JuMP model.
PowerOptLab.InverterControlFixedPointResult — Type
Result of the reduced affine-feeder exact controller fixed-point oracle.
PowerOptLab.InverterControlNetworkFixedPointResult — Type
Result of a plant-backed exact-law equilibrium iteration.
PowerOptLab.fixed_point_oracle — Function
fixed_point_oracle(map, initial; kwargs...) -> FixedPointIterationResultIterate x ↦ map(x) with optional under-relaxation. The callable must accept a finite real vector and return a vector of the same length. Convergence uses the update residual with an absolute-plus-relative tolerance. A repeated state within cycle_tolerance is reported as a detected cycle when convergence has not already been reached.
The oracle is intentionally independent of a network solver. A caller may use a reduced feeder map, a full quasi-static re-solve, or a measured/discrete-time map. No claim about a physical response time follows unless the supplied map contains the relevant sampling, delay, filtering, and ramp dynamics.
PowerOptLab.finite_difference_jacobian — Function
Compute a central finite-difference Jacobian of a real vector map.
PowerOptLab.screen_fixed_point_gain — Function
screen_fixed_point_gain(map, point; step=1e-6, threshold=1.0)Return the finite-difference Jacobian and local screens for a fixed-point map. local_contractive and margin retain the unrelaxed Picard screen (spectral_radius < threshold). maximum_real_eigenvalue and continuous_time_margin = 1 - maximum_real_eigenvalue describe the ideal first-order response model $ẋ = (G-I)x$. alpha_max is the largest update-to-response-time ratio predicted by the discrete relaxed iteration, $minᵢ 2/|1-λᵢ(G)|$. These are local numerical diagnostics, not global or nonlinear stability certificates.
PowerOptLab.inverter_control_scaling_audit — Function
inverter_control_scaling_audit(controller, ratings; voltage_fallback=230)
-> InverterControlScalingAuditReturn the physical scales used by the smooth controller auxiliaries. Curve breakpoints determine the voltage anchor when present; otherwise voltage_fallback is used. The capacity-auxiliary start is the small residual capability at the declared P/Q-priority headroom, rather than the full apparent-power rating. This makes heterogeneous fleets auditable and exposes poorly conditioned starts before a network solve is attempted.
PowerOptLab.inverter_control_current_jacobian — Function
Finite-difference current-command Jacobian for the exact inverter law.
PowerOptLab.inverter_control_fixed_point_oracle — Function
inverter_control_fixed_point_oracle(controller, initial, request, ratings,
reference, voltage_sensitivity; kwargs...) -> InverterControlFixedPointResultRun the exact controller law against a supplied affine feeder sensitivity. The reduced map is
$v_next = v_ref + Z (i_exact(v) - i_exact(v_ref))$
in six real rectangular phase-voltage coordinates. reference is the voltage at which the feeder operating point and the reference controller current are defined; it is not inferred from the initial condition. This is a lightweight equilibrium oracle for campaign screening. It does not replace a full network re-solve, and its convergence has no physical time interpretation unless the supplied sensitivity/map is itself dynamic.
PowerOptLab.solve_inverter_control_network_fixed_point — Function
solve_inverter_control_network_fixed_point(net, controlled, request; kwargs...)
-> InverterControlNetworkFixedPointResultCompare the smooth controlled solve with a plant-backed fixed point of the exact controller law. Each iteration evaluates the exact local law, fixes the selected converter- or grid-side phase current in a fresh AdvancedInverter solve, and feeds the resulting POC voltage into the next iteration. Physical limits therefore remain part of the oracle rather than being replaced by a linear sensitivity.
The result retains non-publishable final solves and cycling diagnostics. This is a quasi-static equilibrium comparison: it is not a dynamic stability or response-time simulation.
PowerOptLab.ControlledInverterFleetNetworkFixedPointResult — Type
Result of a simultaneous plant-backed exact-law fleet iteration.
PowerOptLab.solve_controlled_inverter_fleet_network_fixed_point — Function
solve_controlled_inverter_fleet_network_fixed_point(net, spec; kwargs...)
-> ControlledInverterFleetNetworkFixedPointResultRun a simultaneous plant-backed fixed point for every selected fleet device. The smooth fleet solve is used as the initial state; each exact iteration fixes all selected converter/grid current targets in one fresh network solve. This preserves feeder coupling and native unselected IBRs, and is the campaign-scale equilibrium comparison for solve_controlled_inverter_fleet.
PowerOptLab.controlled_inverter_network_fixed_point_rows — Function
Deterministic per-device comparison rows for a fleet fixed-point result.
PowerOptLab.ControlledInverterFleetMultiStartResult — Type
Deterministic collection of exact fleet equilibrium runs from multiple starts.
PowerOptLab.solve_controlled_inverter_fleet_multistart — Function
solve_controlled_inverter_fleet_multistart(net, spec, starts; kwargs...)
-> ControlledInverterFleetMultiStartResultRun the simultaneous exact fleet equilibrium oracle from several named initial voltage states. starts is a dictionary from a reproducible string label to a dictionary keyed by controlled device id, with each value a three-phase phasor vector or InverterControlMeasurement. The smooth fleet solve is constructed once and reused for every start. This makes initial-condition dependence visible without conflating repeated smooth solves with equilibrium evidence.
PowerOptLab.controlled_inverter_fleet_multistart_rows — Function
Return one deterministic robustness row per multi-start initial condition.
PowerOptLab.InverterControlNetworkSensitivityResult — Type
Finite-difference feeder voltage-sensitivity result with solve provenance.
PowerOptLab.inverter_control_network_voltage_sensitivity — Function
inverter_control_network_voltage_sensitivity(net, controlled,
operating_current; step=1e-3, kwargs...) -> InverterControlNetworkSensitivityResultEstimate the physical POC voltage sensitivity to the selected converter/grid current target by central finite differences of fresh AdvancedInverter solves. Four-leg devices use a 6×6 real rectangular matrix with columns ordered (Re Ia, Im Ia, Re Ib, Im Ib, Re Ic, Im Ic). Three-leg converter-current targets use a 4×4 positive/negative-sequence matrix ordered (Re I₁, Im I₁, Re I₂, Im I₂), which stays inside the zero-neutral-current subspace. The perturbation is step * max(abs(component), 1 A) in amperes.
Failed plus/minus solves leave the corresponding column as NaN and retain their full SolveStatus; the function does not turn a physically infeasible perturbation into a numerical sensitivity. Use the returned matrix with inverter_control_loop_gain only when all required columns are finite.
PowerOptLab.ControlledInverterFleetNetworkSensitivityResult — Type
Finite-difference fleet voltage-sensitivity result with solve provenance.
PowerOptLab.controlled_inverter_fleet_network_voltage_sensitivity — Function
controlled_inverter_fleet_network_voltage_sensitivity(net, spec,
operating_currents; step=1e-3, kwargs...)
-> ControlledInverterFleetNetworkSensitivityResultEstimate the feeder voltage sensitivity for a selected fleet by central finite differences of simultaneous physical fleet solves. Four-leg fleets return a 6N × 6N phase-rectangular matrix; three-leg converter-current fleets return a 4N × 4N positive/negative-sequence matrix. Blocks are ordered by sorted device id. A column is retained only when both fresh solves are publishable; failed perturbations remain NaN while their SolveStatus values are preserved for auditability.
PowerOptLab.controlled_inverter_fleet_network_sensitivity_rows — Function
Return deterministic long-form rows for a fleet sensitivity matrix.
PowerOptLab.controlled_inverter_fleet_loop_gain — Function
controlled_inverter_fleet_loop_gain(spec, measurements,
voltage_sensitivity; kwargs...) -> FixedPointGainScreenScreen the simultaneous fleet loop Δv ↦ Z⋅diag(∂iₖ/∂vₖ)⋅Δv. Device blocks are ordered by sorted fleet id. The supplied voltage_sensitivity must be the 6N × 6N phase-rectangular matrix for four-leg devices or the 4N × 4N positive/negative-sequence matrix for a three-leg converter-current fleet, as returned by controlled_inverter_fleet_network_voltage_sensitivity, or an equivalent reduced/network map. This is a local quasi-static diagnostic, not a dynamic stability certificate.
PowerOptLab.inverter_control_loop_gain — Function
inverter_control_loop_gain(controller, measurement, request, ratings,
voltage_sensitivity; coordinate_system=:phase_rectangular, kwargs...)
-> FixedPointGainScreenScreen the local loop $Δv ↦ Z⋅(∂i/∂v)⋅Δv$. With coordinate_system=:phase_rectangular, voltage_sensitivity is a 6×6 real matrix in rectangular a-b-c coordinates. With coordinate_system=:positive_negative_sequence, it is a 4×4 real matrix in the real/imaginary positive- and negative-sequence coordinates. The latter is the physically realizable converter-current subspace of a three-leg inverter. The exact controller evaluator supplies the current-command Jacobian. This is a local diagnostic for the supplied quasi-static sensitivity; it does not include dynamics absent from Z or the controller map.