diff --git a/examples/grid_following/plot_gfl_10kva.py b/examples/grid_following/plot_gfl_10kva.py index 1efb68db6..933ca4e09 100644 --- a/examples/grid_following/plot_gfl_10kva.py +++ b/examples/grid_following/plot_gfl_10kva.py @@ -14,7 +14,7 @@ from motulator.common.utils import BaseValues, NominalValues from motulator.grid import model import motulator.grid.control.grid_following as control -from motulator.grid.utils import FilterPars, GridPars, plot_grid +from motulator.grid.utils import FilterPars, GridPars, plot # from motulator.grid.utils import plot_voltage_vector # from motulator.common.model import CarrierComparison # import numpy as np @@ -38,8 +38,7 @@ ac_filter = model.ACFilter(filter_par, grid_par) # AC grid model with constant voltage magnitude and frequency -grid_model = model.ThreePhaseVoltageSource( - w_g=grid_par.w_gN, abs_e_g=grid_par.u_gN) +grid_model = model.ThreePhaseVoltageSource(w_g=base.w, abs_e_g=base.u) # Inverter with constant DC voltage converter = VoltageSourceConverter(u_dc=650) @@ -54,8 +53,7 @@ # Configure the control system. # Control configuration parameters -cfg = control.GFLControlCfg( - grid_par=grid_par, filter_par=filter_par, max_i=1.5*base.i) +cfg = control.GFLControlCfg(grid_par, filter_par, max_i=1.5*base.i) # Create the control system ctrl = control.GFLControl(cfg) @@ -68,8 +66,8 @@ ctrl.ref.q_g = lambda t: (t > .04)*4e3 # Uncomment lines below to simulate a unbalanced fault (add negative sequence) -# mdl.grid_model.par.abs_e_g = 0.75*base.u -# mdl.grid_model.par.abs_e_g_neg = 0.25*base.u +# mdl.grid_model.par.abs_e_g = .75*base.u +# mdl.grid_model.par.abs_e_g_neg = .25*base.u # mdl.grid_model.par.phi_neg = -np.pi/3 # %% @@ -86,4 +84,4 @@ # Uncomment line below to plot locus of the grid voltage space vector # plot_voltage_vector(sim, base) -plot_grid(sim, base, plot_pcc_voltage=True) +plot(sim, base, plot_pcc_voltage=False) diff --git a/examples/grid_following/plot_gfl_dc_bus_10kva.py b/examples/grid_following/plot_gfl_dc_bus_10kva.py index afab30d8a..05b3380e6 100644 --- a/examples/grid_following/plot_gfl_dc_bus_10kva.py +++ b/examples/grid_following/plot_gfl_dc_bus_10kva.py @@ -15,7 +15,7 @@ from motulator.grid import model import motulator.grid.control.grid_following as control from motulator.grid.control import DCBusVoltageController -from motulator.grid.utils import FilterPars, GridPars, plot_grid +from motulator.grid.utils import FilterPars, GridPars, plot # %% # Compute base values based on the nominal values. @@ -35,12 +35,8 @@ # Create AC filter with given parameters ac_filter = model.ACFilter(filter_par, grid_par) -# AC-voltage magnitude (to simulate voltage dips or short-circuits) -abs_e_g_var = lambda t: base.u - # AC grid model with constant voltage magnitude and frequency -grid_model = model.ThreePhaseVoltageSource( - w_g=grid_par.w_gN, abs_e_g=abs_e_g_var) +grid_model = model.ThreePhaseVoltageSource(w_g=base.w, abs_e_g=base.u) # Inverter model with DC-bus dynamics included converter = VoltageSourceConverter(u_dc=600, C_dc=1e-3) @@ -51,14 +47,8 @@ # %% # Configure the control system. -# Control parameters -cfg = control.GFLControlCfg( - grid_par=grid_par, - C_dc=1e-3, - filter_par=filter_par, - max_i=1.5*base.i, -) # Create the control system +cfg = control.GFLControlCfg(grid_par, filter_par, max_i=1.5*base.i, C_dc=1e-3) ctrl = control.GFLControl(cfg) # Add the DC-bus voltage controller to the control system @@ -86,4 +76,4 @@ # By default results are plotted in per-unit values. By omitting the argument # `base` you can plot the results in SI units. -plot_grid(sim=sim, base=base, plot_pcc_voltage=True) +plot(sim, base) diff --git a/examples/grid_following/plot_gfl_lcl_10kva.py b/examples/grid_following/plot_gfl_lcl_10kva.py index f354da5eb..726bc9d0f 100644 --- a/examples/grid_following/plot_gfl_lcl_10kva.py +++ b/examples/grid_following/plot_gfl_lcl_10kva.py @@ -14,7 +14,7 @@ from motulator.common.utils import BaseValues, NominalValues from motulator.grid import model import motulator.grid.control.grid_following as control -from motulator.grid.utils import FilterPars, GridPars, plot_grid +from motulator.grid.utils import FilterPars, GridPars, plot # %% # Compute base values based on the nominal values. @@ -32,12 +32,8 @@ # DC-bus parameters ac_filter = model.ACFilter(filter_par, grid_par) -# AC-voltage magnitude (to simulate voltage dips or short-circuits) -abs_e_g_var = lambda t: grid_par.u_gN - # AC grid model with constant voltage magnitude and frequency -grid_model = model.ThreePhaseVoltageSource( - w_g=grid_par.w_gN, abs_e_g=abs_e_g_var) +grid_model = model.ThreePhaseVoltageSource(w_g=base.w, abs_e_g=base.u) # Inverter model with constant DC voltage converter = VoltageSourceConverter(u_dc=650) @@ -45,15 +41,11 @@ # Create system model mdl = model.GridConverterSystem(converter, ac_filter, grid_model) -# Uncomment line below to enable the PWM model -#mdl.pwm = CarrierComparison() - # %% # Configure the control system. # Control parameters -cfg = control.GFLControlCfg( - grid_par=grid_par, filter_par=filter_par, max_i=1.5*base.i) +cfg = control.GFLControlCfg(grid_par, filter_par, max_i=1.5*base.i) # Create the control system ctrl = control.GFLControl(cfg) @@ -74,7 +66,4 @@ # %% # Plot the results. -# By default results are plotted in per-unit values. By omitting the argument -# `base` you can plot the results in SI units. - -plot_grid(sim, base=base, plot_pcc_voltage=True) +plot(sim, base) diff --git a/examples/grid_forming/plot_gfm_obs_13kva.py b/examples/grid_forming/plot_gfm_obs_13kva.py index 9c2742739..c6aa9c488 100644 --- a/examples/grid_forming/plot_gfm_obs_13kva.py +++ b/examples/grid_forming/plot_gfm_obs_13kva.py @@ -14,7 +14,7 @@ from motulator.common.utils import BaseValues, NominalValues from motulator.grid import model import motulator.grid.control.grid_forming as control -from motulator.grid.utils import FilterPars, GridPars, plot_grid +from motulator.grid.utils import FilterPars, GridPars, plot # from motulator.common.model import CarrierComparison # %% @@ -38,8 +38,7 @@ ac_filter = model.ACFilter(filter_par, grid_par) # Grid voltage source with constant frequency and voltage magnitude -grid_model = model.ThreePhaseVoltageSource( - w_g=grid_par.w_gN, abs_e_g=grid_par.u_gN) +grid_model = model.ThreePhaseVoltageSource(w_g=base.w, abs_e_g=base.u) # Inverter with constant DC voltage converter = VoltageSourceConverter(u_dc=650) @@ -47,9 +46,6 @@ # Create system model mdl = model.GridConverterSystem(converter, ac_filter, grid_model) -# Uncomment the lines below to enable the PWM model -# mdl.pwm = CarrierComparison() - # %% # Configure the control system. @@ -58,11 +54,7 @@ # Set the configuration parameters cfg = control.ObserverBasedGFMControlCfg( - grid_par=grid_par_est, - filter_par=filter_par, - T_s=100e-6, - max_i=1.3*base.i, - R_a=.2*base.Z) + grid_par_est, filter_par, max_i=1.3*base.i, T_s=100e-6, R_a=.2*base.Z) # Create the control system ctrl = control.ObserverBasedGFMControl(cfg) @@ -71,19 +63,18 @@ # Set the references for converter output voltage magnitude and active power. # Converter output voltage magnitude reference -ctrl.ref.v_c = lambda t: grid_par.u_gN +ctrl.ref.v_c = lambda t: base.u # Active power reference -ctrl.ref.p_g = lambda t: ((t > .2)*(4.15e3) + (t > .5)*(4.15e3) + (t > .8)* - (4.2e3) - (t > 1.2)*(12.5e3)) +ctrl.ref.p_g = lambda t: ((t > .2)/3 + (t > .5)/3 + (t > .8)/3 - + (t > 1.2))*nom.P # Uncomment line below to simulate operation in rectifier mode -# ctrl.ref.p_g = lambda t: ((t > .2) - (t > .7)*2 + (t > 1.2))*12.5e3 +# ctrl.ref.p_g = lambda t: ((t > .2) - (t > .7)*2 + (t > 1.2))*nom.P # Uncomment lines below to simulate a grid voltage sag with constant ref.p_g -# mdl.grid_model.par.e_g_abs = lambda t: ( -# 1 - (t > .2)*(0.8) + (t > 1)*(0.8))*grid_par.u_gN -# ctrl.ref.p_g = lambda t: 12.5e3 +# mdl.grid_model.par.abs_e_g = lambda t: (1 - (t > .2)*.8 + (t > 1)*.8)*base.u +# ctrl.ref.p_g = lambda t: nom.P # %% # Create the simulation object and simulate it. @@ -94,7 +85,4 @@ # %% # Plot the results. -# By default results are plotted in per-unit values. By omitting the argument -# `base` you can plot the results in SI units. - -plot_grid(sim=sim, base=base, plot_pcc_voltage=False) +plot(sim, base) diff --git a/examples/grid_forming/plot_gfm_rfpsc_13kva.py b/examples/grid_forming/plot_gfm_rfpsc_13kva.py index 4751f1783..d42205cf1 100644 --- a/examples/grid_forming/plot_gfm_rfpsc_13kva.py +++ b/examples/grid_forming/plot_gfm_rfpsc_13kva.py @@ -12,7 +12,7 @@ from motulator.common.utils import BaseValues, NominalValues from motulator.grid import model import motulator.grid.control.grid_forming as control -from motulator.grid.utils import FilterPars, GridPars, plot_grid +from motulator.grid.utils import FilterPars, GridPars, plot # %% # Compute base values based on the nominal values. @@ -35,8 +35,7 @@ ac_filter = model.ACFilter(filter_par, grid_par) # Grid voltage source with constant frequency and voltage magnitude -grid_model = model.ThreePhaseVoltageSource( - w_g=grid_par.w_gN, abs_e_g=grid_par.u_gN) +grid_model = model.ThreePhaseVoltageSource(w_g=base.w, abs_e_g=base.u) # Inverter with constant DC voltage converter = VoltageSourceConverter(u_dc=650) @@ -49,11 +48,7 @@ # Control configuration parameters cfg = control.RFPSCControlCfg( - grid_par=grid_par, - filter_par=filter_par, - T_s=100e-6, - max_i=1.3*base.i, - R_a=.2*base.Z) + grid_par, filter_par, max_i=1.3*base.i, T_s=100e-6, R_a=.2*base.Z) # Create the control system ctrl = control.RFPSCControl(cfg) @@ -62,10 +57,10 @@ # Set the references for converter output voltage magnitude and active power. # Converter output voltage magnitude reference -ctrl.ref.v = lambda t: grid_par.u_gN +ctrl.ref.v_c = lambda t: base.u # Active power reference -ctrl.ref.p_g = lambda t: ((t > .2)*(1/3) + (t > .5)*(1/3) + (t > .8)*(1/3) - +ctrl.ref.p_g = lambda t: ((t > .2)/3 + (t > .5)/3 + (t > .8)/3 - (t > 1.2))*nom.P # %% @@ -77,7 +72,4 @@ # %% # Plot the results. -# By default results are plotted in per-unit values. By omitting the argument -# `base` you can plot the results in SI units. - -plot_grid(sim, base=base, plot_pcc_voltage=True) +plot(sim, base) diff --git a/motulator/common/control/_control.py b/motulator/common/control/_control.py index 227d2910c..79e001036 100644 --- a/motulator/common/control/_control.py +++ b/motulator/common/control/_control.py @@ -294,10 +294,8 @@ class ComplexPIController: 2DOF synchronous-frame complex-vector PI controller. This implements a discrete-time 2DOF synchronous-frame complex-vector PI - controller, based on a disturbance observer structure. The complex-vector - gain selection is based on [#Bri2000]_. The addition of the feedforward - signal is based on [#Har2009]_. The continuous-time counterpart of - the controller is:: + controller [#Bri2000]_. The continuous-time counterpart of the controller + is:: u = k_t*ref_i - k_p*i + (k_i + 1j*w*k_t)/s*(ref_i - i) + u_ff @@ -330,11 +328,9 @@ class ComplexPIController: """ def __init__(self, k_p, k_i, k_t=None): - # Gains self.k_p = k_p self.k_t = k_t if k_t is not None else k_p self.alpha_i = k_i/self.k_t # Inverse of the integration time T_i - # States self.v, self.u_i = 0, 0 def output(self, ref_i, i, u_ff=0): diff --git a/motulator/grid/control/grid_forming/_observer_gfm.py b/motulator/grid/control/grid_forming/_observer_gfm.py index cbb0678c1..1c737b380 100644 --- a/motulator/grid/control/grid_forming/_observer_gfm.py +++ b/motulator/grid/control/grid_forming/_observer_gfm.py @@ -1,4 +1,4 @@ -"""Disturbance observer-based grid-forming control for grid converters.""" +"""Disturbance-observer-based grid-forming control for grid converters.""" # %% from dataclasses import dataclass @@ -14,7 +14,7 @@ @dataclass class ObserverBasedGFMControlCfg: """ - Observer GFM control configuration. + Disturbance-observer-based grid-forming control configuration. Parameters ---------- @@ -27,15 +27,15 @@ class ObserverBasedGFMControlCfg: R_a : float Active resistance (Ω). T_s : float, optional - Sampling period of the controller (s). Default is 1/(16e3). + Sampling period of the controller (s). Default is 100e-6. k_v : float, optional - Voltage gain. Default is 1. + Voltage gain. The default is 1. alpha_c : float, optional Current control bandwidth (rad/s). The default is 2*pi*400. alpha_o : float, optional Observer gain (rad/s). The default is 2*pi*50. C_dc : float, optional - DC-bus capacitance (F). Default is None. + DC-bus capacitance (F). The default is None. """ @@ -59,26 +59,27 @@ def __post_init__(self): # %% class ObserverBasedGFMControl(GridConverterControlSystem): """ - Disturbance observer-based grid-forming control for grid converters. + Disturbance-observer-based grid-forming control for grid converters. - This implements the disturbance observer-based control method described in - [#Nur2024]_. More specifically, the grid-forming mode using RFPSC-type - gains is implemented, with transparent current control. + This implements the RFPSC-type grid-forming mode of the control method + described in [#Nur2024]_. Transparent current control is also implemented. Parameters ---------- cfg : ObserverBasedGFMControlCfg - Model and controller configuration parameters. - - Attributes - ---------- - observer : DisturbanceObserver - Disturbance observer. + Controller configuration parameters. + + Notes + ----- + In this implementation, the control system operates in synchronous + coordinates rotating at the nominal grid angular frequency, which is worth + noticing when plotting the results. For other implementation options, see + [#Nur2024]_. References ---------- .. [#Nur2024] Nurminen, Mourouvin, Hinkkanen, Kukkola, "Multifunctional - Grid-Forming Converter Control Based on a Disturbance Observer, "IEEE + grid-forming converter control based on a disturbance observer, "IEEE Trans. Power Electron., 2024, https://doi.org/10.1109/TPEL.2024.3433503 """ @@ -109,20 +110,17 @@ def output(self, fbk): # Get the reference signals ref = super().output(fbk) - if self.dc_bus_volt_ctrl: - ref.u_dc = self.ref.u_dc(ref.t) ref = super().get_power_reference(fbk, ref) - # Converter voltage magnitude reference ref.v_c = self.ref.v_c(ref.t) if callable( self.ref.v_c) else self.ref.v_c - # Calculation of complex gains (grid-forming) + # Complex gains for grid-forming mode abs_k_p = cfg.R_a/(1.5*ref.v_c) abs_v_c = np.abs(fbk.v_c) k_p = abs_k_p*fbk.v_c/abs_v_c if abs_v_c > 0 else 0 k_v = (1 - cfg.k_v*1j)*fbk.v_c/abs_v_c if abs_v_c > 0 else 0 - # Calculation of feedback correction term (grid-forming) + # Feedback correction term for grid-forming mode fbk.e_c = k_p*(ref.p_g - fbk.p_g) + k_v*(ref.v_c - abs_v_c) # Current limitation @@ -131,21 +129,13 @@ def output(self, fbk): ref.i_c_lim = ref.i_c/np.abs(ref.i_c)*cfg.max_i fbk.e_c = cfg.k_c*(ref.i_c_lim - fbk.i_c) - # Calculation of voltage reference + # Voltage reference ref.u_c = fbk.e_c + fbk.v_c + cfg.R*fbk.i_c ref.u_cs = np.exp(1j*fbk.theta_c)*ref.u_c # Duty ratios for PWM ref.d_abc = self.pwm(ref.T_s, ref.u_cs, fbk.u_dc, par.w_gN) - # Apply a coordinate transformation to values that are plotted in - # synchronous coordinates, as by default the coordinate system of the - # observer is not fixed to the converter voltage vector. - T = np.conj(fbk.v_c)/np.abs(fbk.v_c) if np.abs(fbk.v_c) > 0 else 0 - ref.u_c = T*ref.u_c - fbk.i_c = T*fbk.i_c - ref.i_c = T*ref.i_c - return ref def update(self, fbk, ref): @@ -158,14 +148,14 @@ class DisturbanceObserver: """ Disturbance observer. - This implements a disturbance observer, which estimates the converter - output voltage. The observer could be implemented in any coordinates. Here - coordinates rotating at the nominal grid angular frequency are used. + This implements a disturbance observer, which estimates the quasi-static + converter output voltage. Coordinates rotating at the nominal grid angular + frequency are used. Parameters ---------- w_g : float - Estimate of grid angular frequency (rad/s). + Nominal grid angular frequency (rad/s). L : float Estimate of total inductance (H) between converter and grid. alpha_o : float @@ -193,11 +183,10 @@ def output(self, fbk): # Active and reactive power fbk.p_g = 1.5*np.real(fbk.v_c*np.conj(fbk.i_c)) fbk.q_g = 1.5*np.imag(fbk.u_g*np.conj(fbk.i_c)) - return fbk def update(self, fbk, ref): - """Update the observer integral states.""" + """Update the observer states.""" self.v_cp += ref.T_s*(self.k_o + 1j*self.w_c)*fbk.e_c + 1j*( self.w_g - self.w_c)*self.v_cp self.theta_c += ref.T_s*self.w_c diff --git a/motulator/grid/control/grid_forming/_power_synchronization.py b/motulator/grid/control/grid_forming/_power_synchronization.py index 23029b5e8..e2e0a733d 100644 --- a/motulator/grid/control/grid_forming/_power_synchronization.py +++ b/motulator/grid/control/grid_forming/_power_synchronization.py @@ -24,8 +24,8 @@ class RFPSCControlCfg: Filter model parameters. max_i : float Maximum current modulus (A). - R_a : float, optional - Damping resistance (Ω). Default is 4.6. + R_a : float + Damping resistance (Ω). T_s : float, optional Sampling period of the controller (s). The default is 100e-6. w_b : float, optional @@ -96,20 +96,21 @@ def output(self, fbk): # Get the reference signals ref = super().output(fbk) ref = super().get_power_reference(fbk, ref) - ref.v = self.ref.v(ref.t) if callable(self.ref.v) else self.ref.v + ref.v_c = self.ref.v_c(ref.t) if callable( + self.ref.v_c) else self.ref.v_c # Calculation of power droop fbk.w_c = par.w_gN + cfg.k_p_psc*(ref.p_g - fbk.p_c) # Optionally, use of reference feedforward for d-axis current - ref.i_c = ref.p_g/(1.5*ref.v) + 1j*fbk.i_c_flt.imag + ref.i_c = ref.p_g/(1.5*ref.v_c) + 1j*fbk.i_c_flt.imag # ref.i_c = fbk.i_c_flt # Conventional PSC # Limit the current reference ref.i_c = self.current_limiter(ref.i_c) # Calculation of converter voltage output reference - ref.u_c = ref.v + cfg.R_a*(ref.i_c - fbk.i_c) + ref.u_c = ref.v_c + cfg.R_a*(ref.i_c - fbk.i_c) ref.u_cs = np.exp(1j*fbk.theta_c)*ref.u_c # Duty ratios for PWM diff --git a/motulator/grid/utils/__init__.py b/motulator/grid/utils/__init__.py index f3692917c..b20620dd6 100644 --- a/motulator/grid/utils/__init__.py +++ b/motulator/grid/utils/__init__.py @@ -1,7 +1,7 @@ """This module contains utility functions for grid converters.""" from motulator.grid.utils._plots import ( - plot_grid, + plot, plot_voltage_vector, ) from motulator.grid.utils._utils import FilterPars, GridPars @@ -9,6 +9,6 @@ __all__ = [ "FilterPars", "GridPars", - "plot_grid", + "plot", "plot_voltage_vector", ] diff --git a/motulator/grid/utils/_plots.py b/motulator/grid/utils/_plots.py index 7f187bad6..8d3ad6fce 100644 --- a/motulator/grid/utils/_plots.py +++ b/motulator/grid/utils/_plots.py @@ -8,6 +8,7 @@ from cycler import cycler from motulator.common.utils import complex2abc +#import motulator.grid.control.grid_forming as control # To check instance type # Plotting parameters plt.rcParams["axes.prop_cycle"] = cycler(color="brgcmyk") @@ -17,8 +18,7 @@ # %% -def plot_grid( - sim, base=None, plot_pcc_voltage=False, plot_w=False, t_span=None): +def plot(sim, base=None, plot_pcc_voltage=True, plot_w=False, t_span=None): """ Plot example figures of grid converter simulations. @@ -30,9 +30,9 @@ def plot_grid( Base values for scaling the waveforms. If not given, plots the figures in SI units. plot_pcc_voltage : bool, optional - If True, plot the phase voltage waveforms at the point of common - coupling (PCC). Otherwise, plot the grid voltage waveforms. The default - is False. + If True, the phase voltage waveforms are plotted at the point of common + coupling (PCC). Otherwise, the grid voltage waveforms are plotted. The + default is True. plot_w : bool, optional If True, plot the grid frequency. Otherwise, plot the phase angle. The default is False. @@ -58,7 +58,6 @@ def plot_grid( pu_vals = True # Three-phase quantities - #i_c_abc = complex2abc(mdl.converter.data.i_cs).T i_g_abc = complex2abc(mdl.ac_filter.data.i_gs).T e_g_abc = complex2abc(mdl.grid_model.data.e_gs).T u_g_abc = complex2abc(mdl.ac_filter.data.u_gs).T @@ -68,8 +67,16 @@ def plot_grid( np.real(mdl.ac_filter.data.e_gs*np.conj(mdl.ac_filter.data.i_gs))) q_g = 1.5*np.asarray( np.imag(mdl.ac_filter.data.e_gs*np.conj(mdl.ac_filter.data.i_gs))) - p_g_ref = np.asarray(ctrl.ref.p_g) - q_g_ref = np.asarray(ctrl.ref.q_g) + + # Coordinate transformation in the case of observer-based GFM control + if hasattr(sim.ctrl, "observer"): + # Convert quantities to converter-output-voltage coordinates + T = np.where( + np.abs(ctrl.fbk.v_c) > 0, + np.conj(ctrl.fbk.v_c)/np.abs(ctrl.fbk.v_c), 1) + ctrl.ref.u_c = T*ctrl.ref.u_c + ctrl.fbk.i_c = T*ctrl.fbk.i_c + ctrl.ref.i_c = T*ctrl.ref.i_c # %% fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(10, 7)) @@ -95,7 +102,7 @@ def plot_grid( # Subplot 1: DC-bus voltage ax1.plot( mdl.converter.data.t, - mdl.converter.data.u_dc.T/base.u, + mdl.converter.data.u_dc/base.u, label=r"$u_\mathrm{dc}$") ax1.plot( ctrl.t, @@ -174,12 +181,14 @@ def plot_grid( ax1.plot(mdl.ac_filter.data.t, p_g/base.p, label=r"$p_\mathrm{g}$") ax1.plot(mdl.ac_filter.data.t, q_g/base.p, label=r"$q_\mathrm{g}$") ax1.plot( - ctrl.t, (p_g_ref/base.p), + ctrl.t, + ctrl.ref.p_g/base.p, "--", label=r"$p_\mathrm{g,ref}$", ds="steps-post") ax1.plot( - ctrl.t, (q_g_ref/base.p), + ctrl.t, + ctrl.ref.q_g/base.p, "--", label=r"$q_\mathrm{g,ref}$", ds="steps-post")