T_ADY_make_sys¶
test.ASC_SOLVER.T_ADY_make_sys
This File has the sole purpose of creating the ASC DHARD Y Model that is then exported to the buzz repository.
Functions
|
|
|
|
|
This is a pytest needing documentation |
|
This is a pytest needing documentation |
|
Create a state space model for an adder. |
|
sys should be a wield.control.SISO object |
|
This is a pytest needing documentation |
|
Create a state space model for the delay block. |
|
Plot multiple bode plots at once. |
|
Create a state space model for the null block. |
|
This is the main function that is called to create the model for the ASC DHARD Y. |
|
This is a pytest needing documentation |
|
Takes a python control zpk and returns a python control statespace. |
Details
- FBNSSS_Fit(gain=1, return_name=False, return_scale=False)[source]¶
code
docstring
""" """
1def FBNSSS_Fit(gain=1, return_name=False, return_scale = False): 2 3 if return_name: 4 name = "FOM BNS" 5 return name 6 7 if gain != 1: 8 NotImplementedError("This function does not support gain scaling") 9 10 BNS_zpk = load(fjoin('ExampleModels_newFOM/BNS_FOM.yml')) 11 print(BNS_zpk['z']) 12 F_z = np.array(BNS_zpk["z"], dtype=np.complex128) 13 #BNS_zpk["p"].append(BNS_zpk["p"][-1]) 14 F_p = np.array(BNS_zpk["p"], dtype=np.complex128) 15 F_k = BNS_zpk["k"] 16 17 #print(len(F_z)) 18 #print(len(F_p)) 19 F_mod = SISO.zpk(F_z, F_p, F_k).asSS 20 F_iod = {"FBNS.in": 0, "FBNS.out": 0} 21 F = ssutil.wieldSS(F_mod.A, F_mod.B, F_mod.C, F_mod.D, iod=F_iod) 22 F, scale = ssutil.normalize_gain(F, norm=1, return_scale=True) 23 if return_scale: 24 return scale 25 else: 26 return F
pytest information
This code is wrapped in a pytest function using conventions detailed in pytest_conventions. The full name of this test, as known by the documentation, is:
test.ASC_SOLVER.T_ADY_make_sys.FBNSSS_FitThe full name is useful when building documentation, to link a reference to this page using
:func:`name`, or directly include it with an autofunction directive. The collapse nodes below show every instance of the test run. There may only be one, but if the test was run multiple times through pytest parametrizations, the list can be longer.
- FBNSSS_Hand(gain=1, return_name=False, return_scale=False)[source]¶
code
docstring
""" """
1def FBNSSS_Hand(gain=1, return_name=False, return_scale = False): 2 3 if return_name: 4 name = "FOM BNS (Hand Fit)" 5 return name 6 7 if gain != 1: 8 NotImplementedError("This function does not support gain scaling") 9 10 #BNS_zpk = load('fjoin(ExampleModels_newFOM/BNS_FOM_handfit_gentleslope.yml')) 11 BNS_zpk = load(fjoin('ExampleModels_newFOM/BNS_FOM_handfit.yml')) 12 print(BNS_zpk['z']) 13 F_z = np.array(BNS_zpk["z"], dtype=np.complex128) 14 #BNS_zpk["p"].append(BNS_zpk["p"][-1]) 15 F_p = np.array(BNS_zpk["p"], dtype=np.complex128) 16 F_k = BNS_zpk["k"] 17 18 #print(len(F_z)) 19 #print(len(F_p)) 20 F_mod = SISO.zpk(F_z, F_p, F_k).asSS 21 F_iod = {"FBNS.in": 0, "FBNS.out": 0} 22 F = ssutil.wieldSS(F_mod.A, F_mod.B, F_mod.C, F_mod.D, iod=F_iod) 23 F, scale = ssutil.normalize_gain(F, norm=1, return_scale=True) 24 if return_scale: 25 return scale 26 else: 27 return F
pytest information
This code is wrapped in a pytest function using conventions detailed in pytest_conventions. The full name of this test, as known by the documentation, is:
test.ASC_SOLVER.T_ADY_make_sys.FBNSSS_HandThe full name is useful when building documentation, to link a reference to this page using
:func:`name`, or directly include it with an autofunction directive. The collapse nodes below show every instance of the test run. There may only be one, but if the test was run multiple times through pytest parametrizations, the list can be longer.
- FBNSsimpSS(gain=1, return_name=False, lo_ord=4, return_scale=False)[source]¶
This is a pytest needing documentation
code
1def FBNSsimpSS(gain=1, return_name=False, lo_ord=4, return_scale=False): 2 if return_name: 3 name = "FOM BNS Simple" 4 return name 5 6 if gain != 1: 7 NotImplementedError("This function does not support gain scaling") 8 9 F_Fq_lo = 40 * 2 * np.pi 10 # aggressive version 11 # F_Fq_lo = 30 * 2 * np.pi 12 F_Fq_hi = 300 * 2 * np.pi 13 hp_order = 1 #lo_ord 14 lp_order = 2 15 F_p = [] 16 F_z = [] 17 for ord in range(hp_order): 18 F_p.append(-F_Fq_lo + 0*1j*F_Fq_lo) 19 F_p.append(-F_Fq_lo - 0*1j*F_Fq_lo) 20 F_z.append(0) 21 F_z.append(0) 22 for ord in range(lp_order): 23 F_p.append(-F_Fq_hi) 24 F_k = 1 25 c_zpk = cheby2(8, 160, 4*2*np.pi, btype='high', analog=True, output='zpk') 26 # aggressive version 27 # c_zpk = cheby2(8, 120, 4*2*np.pi, btype='high', analog=True, output='zpk') 28 F_z.extend(c_zpk[0]) 29 F_p.extend(c_zpk[1]) 30 F_k *= c_zpk[2] 31 32 F_mod = SISO.zpk(F_z, F_p, F_k).asSS 33 F_iod = {"FBNS.in": 0, "FBNS.out": 0} 34 F = ssutil.wieldSS(F_mod.A, F_mod.B, F_mod.C, F_mod.D, iod=F_iod) 35 F, scale = ssutil.normalize_gain(F, norm=1, return_scale=True) 36 if return_scale: 37 return scale 38 else: 39 return F
pytest information
This code is wrapped in a pytest function using conventions detailed in pytest_conventions. The full name of this test, as known by the documentation, is:
test.ASC_SOLVER.T_ADY_make_sys.FBNSsimpSSThe full name is useful when building documentation, to link a reference to this page using
:func:`name`, or directly include it with an autofunction directive. The collapse nodes below show every instance of the test run. There may only be one, but if the test was run multiple times through pytest parametrizations, the list can be longer.
- FFlatSS(gain=1, return_name=False)[source]¶
This is a pytest needing documentation
code
1def FFlatSS(gain=1, return_name=False): 2 if return_name: 3 name = "FOM Flat" 4 return name 5 6 F2_A = np.zeros([0,0]) # Construct the A state matrix 7 F2_B = np.zeros([1,0]) # Construct the B state matrix 8 F2_C = np.zeros([0,1]) # Construct the C state matrix 9 F2_D = np.array([[gain]]) # Construct the D state matrix 10 F2_mod = control.ss(F2_A, F2_B, F2_C, F2_D) 11 F2_iod = {"F2.in": 0, "F2.out": 0} 12 F2 = ssutil.makeSys(F2_mod, F2_iod) 13 return F2
pytest information
This code is wrapped in a pytest function using conventions detailed in pytest_conventions. The full name of this test, as known by the documentation, is:
test.ASC_SOLVER.T_ADY_make_sys.FFlatSSThe full name is useful when building documentation, to link a reference to this page using
:func:`name`, or directly include it with an autofunction directive. The collapse nodes below show every instance of the test run. There may only be one, but if the test was run multiple times through pytest parametrizations, the list can be longer.
- addSS(namespace='T', dt=0.0001, sub=False, outSufx=None)[source]¶
Create a state space model for an adder. It has two inputs and one output.
- Parameters:
- Returns:
Returns the local name space for the function including the state space model as mod, input output dictionary as iod, and namespace.
- Return type:
wield.bunch
code
docstring
"""Create a state space model for an adder. It has two inputs and one output. Args: namespace (string): The namespace to use for the model i.e. the letter that comes at the beginning of the input and output names. Defaults to 'T'. dt (float, optional): The time step. Defaults to 1e-4. sub (bool, optional): If true, the model will be a subtractor. Defaults to False. Returns: wield.bunch: Returns the local name space for the function including the state space model as `mod`, input output dictionary as `iod`, and namespace. """
1def addSS(namespace='T', dt=1e-4, sub=False, outSufx = None): 2 3 A = np.zeros([0,0]) # Construct the A state matrix 4 B = np.zeros([2,0]) # Construct the B state matrix 5 C = np.zeros([0,1]) # Construct the C state matrix 6 if sub: 7 D = np.array([[1, -1]]) # Construct the D state matrix for a subtractor 8 else: 9 D = np.array([[1, 1]]) # Construct the D state matrix 10 if outSufx == None: 11 iod = {namespace+'.in.1': 0, namespace+'.in.2': 1, namespace+'.out': 0} # Input output dictionary 12 else: 13 iod = {namespace+'.in.1': 0, namespace+'.in.2': 1, namespace+'.out.'+outSufx: 0} # Input output dictionary 14 return ssutil.wieldSS(A, B, C, D, iod=iod)
pytest information
This code is wrapped in a pytest function using conventions detailed in pytest_conventions. The full name of this test, as known by the documentation, is:
test.ASC_SOLVER.T_ADY_make_sys.addSSThe full name is useful when building documentation, to link a reference to this page using
:func:`name`, or directly include it with an autofunction directive. The collapse nodes below show every instance of the test run. There may only be one, but if the test was run multiple times through pytest parametrizations, the list can be longer.
- bode(sys, axB=None, F_Hz=None, omega_limits=None, include_zp=True, label=None, **kwargs)[source]¶
sys should be a wield.control.SISO object
code
docstring
""" sys should be a wield.control.SISO object """
1def bode( 2 sys, 3 axB=None, 4 F_Hz=None, 5 omega_limits=None, 6 include_zp = True, 7 label=None, 8 **kwargs 9): 10 11 if axB is None: 12 axB = mplfigB(Nrows = 2) 13 14 if include_zp: 15 z, p = sys._zp 16 z = z[z.imag > 0] 17 p = p[p.imag > 0] 18 F_include = np.sort(np.concatenate( 19 [ 20 z.imag, 21 p.imag, 22 z.imag - abs(z.real) - 1e-6, 23 z.imag + abs(z.real) + 1e-6, 24 p.imag - abs(p.real) - 1e-6, 25 p.imag + abs(p.real) + 1e-6, 26 ] 27 )) / (2 * np.pi) 28 F_include = F_include[F_include > 0] 29 30 if omega_limits is None: 31 omega_limits = ( 32 (F_include[0] + 1e-6) / 3, 33 F_include[-1] * 3 34 ) 35 36 if F_Hz is None: 37 F_Hz = np.geomspace(omega_limits[0], omega_limits[1], 1000) 38 39 if include_zp: 40 print("F_include", F_include) 41 F_Hz = np.sort(np.concatenate([F_Hz, F_include])) 42 43 fr = sys.fresponse(f=F_Hz) 44 axB.ax0.loglog(*fr.fplot_mag, label=label, **kwargs) 45 if hasattr(axB, 'ax1'): 46 axB.ax1.semilogx(*fr.fplot_deg225, label=label, **kwargs) 47 return axB
pytest information
This code is wrapped in a pytest function using conventions detailed in pytest_conventions. The full name of this test, as known by the documentation, is:
test.ASC_SOLVER.T_ADY_make_sys.bodeThe full name is useful when building documentation, to link a reference to this page using
:func:`name`, or directly include it with an autofunction directive. The collapse nodes below show every instance of the test run. There may only be one, but if the test was run multiple times through pytest parametrizations, the list can be longer.
- delayDrive(namespace='D', Sp=None, delay=0.001, order=1, numins=1, dt=0.0001)[source]¶
This is a pytest needing documentation
code
1def delayDrive(namespace='D', Sp=None, delay=1e-3, order=1, numins=1, dt=1e-4): 2 D = delaySS(namespace=namespace, delay=delay, order=order, numins=numins, dt=dt) 3 4 if Sp is None or Sp == 1: 5 #assert(False) 6 return D 7 8 #rename the input for pending merge 9 D.iod[namespace + '.in.2'] = D.iod[namespace + '.in'] 10 del D.iod[namespace + '.in'] 11 12 #check that it only has 1 input and one output (THIS IS A HACKY WAY TO TEST) 13 assert(len(Sp.iod) == 2) 14 15 print(Sp.iod) 16 Sp.iod.clear() 17 Sp.iod[namespace + '.in'] = 0 18 Sp.iod[namespace + '.out.1'] = 0 19 print(Sp.iod) 20 21 Dx = ssutil.multiconnect([Sp, D], [[namespace+'.in.2', namespace+'.out.1']]) 22 print(Dx.iod) 23 return Dx
pytest information
This code is wrapped in a pytest function using conventions detailed in pytest_conventions. The full name of this test, as known by the documentation, is:
test.ASC_SOLVER.T_ADY_make_sys.delayDriveThe full name is useful when building documentation, to link a reference to this page using
:func:`name`, or directly include it with an autofunction directive. The collapse nodes below show every instance of the test run. There may only be one, but if the test was run multiple times through pytest parametrizations, the list can be longer.
- delaySS(namespace='D', delay=0.001, order=1, numins=1, dt=0.0001)[source]¶
Create a state space model for the delay block. It has one input and one output.
- Parameters:
namespace (str, optional) – The namespace for the delay block. Defaults to ‘D’.
delay (_type_, optional) – The delay in seconds. Defaults to 1e-3.
order (int, optional) – Order of the filter. Defaults to 1.
numins (int, optional) – Number of inputs. Defaults to 1.
dt (_type_, optional) – The discretization time step. Defaults to 1e-4.
- Returns:
Returns the local name space for the function including the state space model as mod, input output dictionary as iod, and namespace.
- Return type:
wield.bunch
code
docstring
"""Create a state space model for the delay block. It has one input and one output. Args: namespace (str, optional): The namespace for the delay block. Defaults to 'D'. delay (_type_, optional): The delay in seconds. Defaults to 1e-3. order (int, optional): Order of the filter. Defaults to 1. numins (int, optional): Number of inputs. Defaults to 1. dt (_type_, optional): The discretization time step. Defaults to 1e-4. Returns: wield.bunch: Returns the local name space for the function including the state space model as `mod`, input output dictionary as `iod`, and namespace. """
1def delaySS(namespace='D', delay=1e-3, order=1, numins=1, dt=1e-4): 2 3 if delay==0: 4 print('Delay is zero, using nullSS') 5 D_mod = nullSS().mod 6 else: 7 # take the poles of this normalized bessel filter (delay=1s) 8 z, p, k = scipy.signal.besselap(order, norm="delay") 9 10 # now rescale for desired delay 11 roots = p / delay * 2 12 if order % 2 == 0: 13 k = 1 14 else: 15 k = -1 16 17 #siso_rep = SISO.zpk(-roots.conjugate(), roots, k) 18 D_res = control.zpk(-roots.conjugate(), roots, k) 19 D_mod = control.tf2ss(D_res) 20 21 B_blocked = D_mod.B 22 D_blocked = D_mod.D 23 for ii in range(numins-1): 24 B_blocked = np.block([[B_blocked, D_mod.B]]) 25 D_blocked = np.block([[D_blocked, D_mod.D]]) 26 27 mod = control.ss(D_mod.A, B_blocked, D_mod.C, D_blocked) # Create the system 28 mod_dis = control.c2d(mod, dt) # Discretize the system 29 iod = dict({namespace+'.in': 0, namespace+'.out': 0}) # Input output dictionary 30 for ii in range(numins-1): 31 iod[namespace+'.in.'+str(ii+1)] = ii+1 32 33 return Bunch(locals())
pytest information
This code is wrapped in a pytest function using conventions detailed in pytest_conventions. The full name of this test, as known by the documentation, is:
test.ASC_SOLVER.T_ADY_make_sys.delaySSThe full name is useful when building documentation, to link a reference to this page using
:func:`name`, or directly include it with an autofunction directive. The collapse nodes below show every instance of the test run. There may only be one, but if the test was run multiple times through pytest parametrizations, the list can be longer.
- multi_bode(axB, F_Hz=None, include_zp=True, **kwargs)[source]¶
Plot multiple bode plots at once.
The main advantage is that the scale of the axes can be better auto-determined to capture all known poles and zeros. The input arguments should all be dictionaries containing the bode kwargs.
the usage should be
the kwargs on the top call are passed into all sub calls
- with multi_bode(axB=axB, **kw_cmn) as bode:
bode(sisoA, label=’A’, **kw) bode(sisoB, label=’B’, **kw) bode(sisoC, label=’C’, **kw)
axB.save(‘figname.pdf’)
- nullSS(namespace='N', gain=1)[source]¶
Create a state space model for the null block. It has one input and one output.
- Parameters:
- Returns:
Returns Wield MIMO statespace
- Return type:
MIMO Statespace
code
docstring
"""Create a state space model for the null block. It has one input and one output. Args: namespace (str, optional): The namespace to use for the model i.e. the letter that comes at the beginning of the input and output names. Defaults to 'N'. gain (float, optional): The gain of the null block. Defaults to 1. Returns: MIMO Statespace: Returns Wield MIMO statespace """
1def nullSS(namespace='N', gain=1): 2 3 4 A = np.zeros([0,0]) # Construct the A state matrix 5 B = np.zeros([1,0]) # Construct the B state matrix 6 C = np.zeros([0,1]) # Construct the C state matrix 7 D = np.array([[gain]]) # Construct the D state matrix 8 iod = dict({namespace+'.in': 0, namespace+'.out': 0}) # Input output dictionary 9 return ssutil.wieldSS(A, B, C, D, iod=iod)
pytest information
This code is wrapped in a pytest function using conventions detailed in pytest_conventions. The full name of this test, as known by the documentation, is:
test.ASC_SOLVER.T_ADY_make_sys.nullSSThe full name is useful when building documentation, to link a reference to this page using
:func:`name`, or directly include it with an autofunction directive. The collapse nodes below show every instance of the test run. There may only be one, but if the test was run multiple times through pytest parametrizations, the list can be longer.
- test_Make_ADY(folder)[source]¶
This is the main function that is called to create the model for the ASC DHARD Y.
code
docstring
""" This is the main function that is called to create the model for the ASC DHARD Y. """
1@pytest.mark.parametrize('folder', ['ExampleModels_rescale', 'ExampleModels', 'ExampleModels_working', 'ExampleModels_newFOM', 'ExampleModels_newFOM_F3']) 2@pytest.mark.gitlabCI 3@pytest.mark.ADY 4def test_Make_ADY(folder): 5 6 S = ssutil.loadSys(fjoin(folder, 'ADY_E_from_fit.mat')) 7 O = ssutil.loadSys(fjoin(folder, 'ADY_M_from_fit.mat')) 8 P_fname = 'ADY_plant_from_fit.mat' 9 P = ssutil.loadSys(fjoin(folder, P_fname)) 10 11 # this logic to add scaling helps the solver 12 if '_rescale' in folder: 13 S = (1e5 * S.siso('S.out', 'S.in')).mimo('S.out', 'S.in') 14 O = (1e5 * O.siso('O.out', 'O.in')).mimo('O.out', 'O.in') 15 16 if '_newFOM' in folder or '_newFOM_F3' in folder: 17 FBNS_func = FBNSSS_Hand 18 FBNS = FBNS_func() 19 FBNS_scale = FBNS_func(return_scale=True) 20 else: 21 FBNS_func = FBNSsimpSS 22 FBNS = FBNS_func() 23 #print("\n\n\nUsing Simplified FBNS Model\n\n\n") 24 FBNS_orig = FBNS 25 FBNS = (FBNS.siso('FBNS.out', 'FBNS.in') * FBNS.siso('FBNS.out', 'FBNS.in')).mimo('FBNS.out', 'FBNS.in') 26 FBNS_scale = FBNS_func(return_scale=True)**2 # square the gain because the fom is squared 27 28 omega_limits=[1e-2, 1e4] 29 axB = ssutil.bode(FBNS.siso('FBNS.out', 'FBNS.in'), omega_limits=omega_limits, label='Squared Simple FBNS') 30 axB = ssutil.bode(FBNS_orig.siso('FBNS.out', 'FBNS.in'), omega_limits=omega_limits, axB=axB, label='Simple FBNS') 31 axB = ssutil.bode(FBNSSS_Hand().siso('FBNS.out', 'FBNS.in'), omega_limits=omega_limits, axB=axB, label='Hand Fit FBNS') 32 axB = ssutil.bode(FBNSSS_Fit().siso('FBNS.out', 'FBNS.in'), omega_limits=omega_limits, axB=axB, label='FBNS From Data') 33 axB.save(tjoin('BNS_FOMS_Comp.pdf')) 34 axB.save(tjoin('BNS_FOMS_Comp.png')) 35 36 FFlat = FFlatSS() 37 38 Coup = ssutil.loadSys(fjoin(folder, 'A2L_coupling_with_cal.mat')) # This is the Angle to length coupling it would be multiplied by the BNS FOM 39 FBNS = ssutil.multiconnect([Coup, FBNS], [['FBNS.in', 'COUP.out']]) 40 # FBNS = ssutil.truncate_io(FBNS, ['COUP.in'], ['FBNS.out']) #commented out to display DARM Spectrum 41 FBNS = ssutil.rename_io(FBNS, 'COUP.out', 'FBNS.DARM.out') # to see DARM Spectrum 42 FBNS = ssutil.rename_io(FBNS, 'COUP.in', 'FBNS.in') 43 44 FBNS_Trash, Coup_scale = ssutil.normalize_gain(ssutil.truncate_io(FBNS, ['FBNS.in'], ['FBNS.out']), norm=1, return_scale=True) 45 FBNS = ssutil.scale_io(FBNS, 'FBNS.out', Coup_scale) # using this to scale preserves the DARM.out output 46 47 FBNS_scale = FBNS_scale * Coup_scale 48 np.savetxt(fjoin(folder, 'FBNS_scale.txt'), [FBNS_scale]) 49 print('FBNS scale', FBNS_scale) 50 51 omega_limits=[1e-2, 1e4] 52 53 omega_limits_noise = [1e-1, 1e3] 54 axN = ssutil.bode(ssutil.asSISO(S), omega_limits=omega_limits_noise, label='Seismic') 55 axN = ssutil.bode(ssutil.asSISO(O), axB=axN, omega_limits=omega_limits_noise, label='Meas. Noise') 56 axN = ssutil.bode(ssutil.asSISO(P), axB=axN, omega_limits=omega_limits_noise, label='Plant') 57 axN = ssutil.bode(ssutil.asSISO(S.siso('S.out', 'S.in') * P.siso('P.out', 'P.in')), axB=axN, omega_limits=omega_limits_noise, label='S*P') 58 axN.save(tjoin('Noise_bode.png')) 59 axN.save(tjoin('Noise_bode.pdf')) 60 61 62 # control.bode(S.mod, dB=False, Hz=True, omega_limits=omega_limits, label='Seismic') 63 # control.bode(O.mod, dB=False, Hz=True, omega_limits=omega_limits, label='Meas. Noise') 64 # control.bode(P.mod, dB=False, Hz=True, omega_limits=omega_limits, label='Plant') 65 # control.bode((S.siso('S.out', 'S.in') * P.siso('P.out', 'P.in')).mimo('out', 'in').mod, dB=False, Hz=True, omega_limits=omega_limits, label='S*P') 66 # plt.legend() 67 # plt.savefig(tjoin('Noise_bode.pdf'), bbox_inches='tight') 68 # plt.savefig(tjoin('Noise_bode.png'), bbox_inches='tight', dpi=300) 69 # plt.close() 70 71 omega_limits_fom = np.array([1e-1, 1e5])*2*np.pi 72 axFOM = ssutil.bode(FBNS.siso('FBNS.out', 'FBNS.in'), omega_limits=omega_limits_fom, label='FBNS') 73 axFOM = ssutil.bode(FFlat.siso(iodutil.listoutputs(FFlat)[0], iodutil.listinputs(FFlat)[0]), axB=axFOM, omega_limits=omega_limits_fom, label='FFlat') 74 axFOM.save(tjoin('FOM_bode.pdf')) 75 axFOM.save(tjoin('FOM_bode.png')) 76 # control.bode(FBNS.mod, dB=True, Hz=True, omega_limits=omega_limits, label='FBNS') 77 # control.bode(FFlat.mod, dB=True, Hz=True, omega_limits=omega_limits, label='FFlat') 78 # plt.legend() 79 # plt.savefig(tjoin('FOM_bode.pdf')) 80 # plt.savefig(tjoin('FOM_bode.png')) 81 # plt.close() 82 83 #make the SO 84 #conlist =[['T_SO.in.1', 'O.out'], ['T_SO.in.2','S.out']] 85 #T_SO = addSS(namespace='T_SO', sub=True) 86 #SO = ssutil.multiconnect([S, O, T_SO], conlist) 87 #SO = ssutil.truncate_io(SO, ['O.in', 'S.in'], ['T_SO.out', 'S.out']) 88 #SO = ssutil.rename_io(SO, 'T_SO.out', 'O.out') 89 90 #control.bode(SO.mod[SO.iod['S.out'], SO.iod['S.in']], dB=True, Hz=True, omega_limits=omega_limits, label='S to S') 91 #control.bode(SO.mod[SO.iod['O.out'], SO.iod['O.in']], dB=True, Hz=True, omega_limits=omega_limits, label='O to O') 92 #control.bode(SO.mod[SO.iod['O.out'], SO.iod['S.in']], dB=True, Hz=True, omega_limits=omega_limits, label='S to O', linestyle='--', linewidth=5) 93 #control.bode(S.mod, dB=True, Hz=True, omega_limits=[1e-2, 1e3], label='Orig. S', linestyle='dotted', linewidth=4) 94 #control.bode(O.mod, dB=True, Hz=True, omega_limits=[1e-2, 1e3], label='Orig. O', linestyle='dotted', linewidth=4) 95 #plt.legend(fontsize=8) 96 #plt.savefig(tjoin('SO_bode.pdf')) 97 #plt.close() 98 if '_F3' in folder: 99 extras = 'F3_withP' 100 else: 101 extras = None 102 103 SPOFF = ssutil.makeSPOFF(S, P, O, FBNS, FFlat, diagnostic=True, extras=extras, balance=True) 104 #print('SPOFF', SPOFF.iod) 105 #SPOFF = ssutil.balance_sys_gain(SPOFF) 106 omega_limits=[1e-2, 1e3] 107 108 axB = mplfigB(Nrows=2) 109 F_Hz = np.geomspace(1e-2, 20, 1000) 110 with multi_bode(axB=axB, F_Hz = F_Hz) as bode: 111 bode(P['P.out', 'P.in'], label='P alone', linewidth=2, color='dodgerblue') 112 bode(SPOFF['P.out', 'P.in'], label='P to P in SPOFF', color='orange') 113 bode(SPOFF['P.out', 'S.in'], label='S to P', color='green') 114 bode(SPOFF['S.out', 'S.in'], label='S to S', linestyle='dotted', linewidth=4, color='green') 115 bode(SPOFF['P.out', 'SA.in'], linestyle='--', label='P Recombined', linewidth=4, color='dodgerblue') 116 bode(SPOFF['S.out', 'SA.in'], label='P that moved to S', color='dodgerblue', linestyle='dotted', linewidth=6) 117 bode(SPOFF['G.out', 'S.in'], label='Orig. S (from SPOFF)', color='red') 118 bode(S['S.out', 'S.in'], label='Orig. S alone', linestyle='dotted', linewidth=4, color='red') 119 axB.ax1.legend(fontsize=5) 120 axB.save(tjoin('SPOFF_diagnostic_bode.pdf')) 121 axB.save(tjoin('SPOFF_diagnostic_bode.png')) 122 123 axB = mplfigB(Nrows=2) 124 F_Hz = np.geomspace(1e-2, 20, 1000) 125 with multi_bode(axB=axB, F_Hz = F_Hz) as bode: 126 bode(sys=SPOFF['P.out', 'P.in'], label='P to P') 127 bode(sys=SPOFF['P.out', 'S.in'], label='S to P') 128 bode(sys=SPOFF['S.out', 'S.in'], label='S to S', linestyle=':', linewidth=2) 129 bode(sys=S['S.out', 'S.in'], label='S to S (orig)', linestyle=':', linewidth=2) 130 bode(sys=SPOFF['T.out', 'O.in'], label='O to Meas. Out') 131 bode(sys=SPOFF['T.out', 'S.in'], label='S to Meas. Out') 132 133 axB.ax1.legend(fontsize=5) 134 axB.save(tjoin('SPOFF_bode.pdf')) 135 axB.save(tjoin('SPOFF_bode.png')) 136 137 #print('SPOFF', SPOFF.iod) 138 #print(SPOFF['T.out', 'S.in']._zp[1]) 139 140 #truncate_inputs = ['P.in', 'S.in', 'O.in', 'T.in.1', 'T.in.2', 'FBNS.in', 'F2.in', 'Zinf.in'] 141 #truncate_outputs = ['P.out', 'O.out', 'S.out', 'T.out', 'FBNS.out', 'F2.out'] 142 #SPOFF = ssutil.truncate_io(SPOFF, truncate_inputs, truncate_outputs) 143 144 control.bode(SPOFF.mod[SPOFF.iod['F2.out'], SPOFF.iod['P.in']], dB=True, Hz=True, omega_limits=omega_limits, label='P to Flat FOM') 145 control.bode(SPOFF.mod[SPOFF.iod['FBNS.out'], SPOFF.iod['P.in']], dB=True, Hz=True, omega_limits=omega_limits, label='P to BNS FOM') 146 control.bode(SPOFF.mod[SPOFF.iod['P.out'], SPOFF.iod['S.in']], dB=True, Hz=True, omega_limits=omega_limits, label='S to P') 147 control.bode(SPOFF.mod[SPOFF.iod['S.out'], SPOFF.iod['S.in']], dB=True, Hz=True, omega_limits=omega_limits, label='S to S', linestyle='dotted', linewidth=4) 148 control.bode(SPOFF.mod[SPOFF.iod['T.out'], SPOFF.iod['O.in']], dB=True, Hz=True, omega_limits=omega_limits, label='O to Meas. Out') 149 control.bode(SPOFF.mod[SPOFF.iod['T.out'], SPOFF.iod['S.in']], dB=True, Hz=True, omega_limits=omega_limits, label='S to Meas. Out') 150 plt.legend(fontsize=5) 151 plt.savefig(tjoin('SPOFF_all_bode.pdf')) 152 plt.savefig(tjoin('SPOFF_all_bode.png')) 153 plt.close() 154 155 SPOFF_bal = ssutil.balance_sys_gain(SPOFF) 156 157 axFOM_S = ssutil.bode(SPOFF.siso('FBNS.out', 'FBNS.in'), omega_limits=omega_limits_fom, label='FBNS', color='red') 158 axFOM_S = ssutil.bode(SPOFF.siso(iodutil.listoutputs(FFlat)[0], iodutil.listinputs(FFlat)[0]), axB=axFOM_S, omega_limits=omega_limits_fom, label='FFlat', color='dodgerblue') 159 axFOM_S = ssutil.bode(SPOFF_bal.siso('FBNS.out', 'FBNS.in'), omega_limits=omega_limits_fom, axB=axFOM_S, label='FBNS SPOFF bal', color='green') 160 axFOM_S = ssutil.bode(SPOFF_bal.siso(iodutil.listoutputs(FFlat)[0], iodutil.listinputs(FFlat)[0]), axB=axFOM_S, omega_limits=omega_limits_fom, label='FFlat SPOFF bal', color='green') 161 axFOM_S = ssutil.bode(FBNS.siso('FBNS.out', 'FBNS.in'), axB=axFOM_S, omega_limits=omega_limits_fom, label='FBNS orig.', linestyle='dotted', color='red', linewidth=4) 162 axFOM_S = ssutil.bode(FFlat.siso(iodutil.listoutputs(FFlat)[0], iodutil.listinputs(FFlat)[0]), axB=axFOM_S, omega_limits=omega_limits_fom, label='FFlat orig.', color='dodgerblue', linestyle='dotted', linewidth=4) 163 axFOM_S.save(tjoin('FOM_bode_from_SPOFF.pdf')) 164 axFOM_S.save(tjoin('FOM_bode_from_SPOFF.png')) 165 166 #SPOFF = ssutil.balance_sys_gain(SPOFF) 167 168 P_load2 = ssutil.loadSys(fjoin(folder, P_fname)) 169 assert(np.allclose(P_load2.A, P.A)) 170 assert(np.allclose(P_load2.B, P.B)) 171 assert(np.allclose(P_load2.C, P.C)) 172 assert(np.allclose(P_load2.D, P.D)) 173 174 ssutil.savesys(SPOFF, tjoin('ADY_Sanex.mat')) 175 ssutil.savesys(P, tjoin('ADY_plant.mat')) 176 177 ssutil.savesys(SPOFF, fjoin(folder, 'ADY_Sanex.mat')) 178 ssutil.savesys(P, fjoin(folder, 'ADY_plant.mat')) 179 180 return
pytest information
This code is wrapped in a pytest function using conventions detailed in pytest_conventions. The full name of this test, as known by the documentation, is:
test.ASC_SOLVER.T_ADY_make_sys.test_Make_ADYThe full name is useful when building documentation, to link a reference to this page using
:func:`name`, or directly include it with an autofunction directive. The collapse nodes below show every instance of the test run. There may only be one, but if the test was run multiple times through pytest parametrizations, the list can be longer.test_Make_ADY[ExampleModels_newFOM_F3]
output
['-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186'] ['-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186'] FBNS scale 2.106269599872551e-18 <IPython.core.display.Markdown object> <IPython.core.display.Markdown object> The plant is stable and has no non-minphase zeros. This means that the plant is strictly proper and no adjustments need to be made to the plant Adding F3 after the controller output P' should be flat because of how it was constructed. Not adding P' to F3 F3 poles: [-12566.37061436+0.00000000e+00j -1256.63706144+2.60881002e-07j -1256.63706144-2.60881002e-07j -113.50044269+1.36200546e+02j -113.50044269-1.36200546e+02j -113.50045853+1.36200535e+02j -113.50045853-1.36200535e+02j -181.60072098+0.00000000e+00j -55.01147525+5.68456373e+00j -55.01147525-5.68456373e+00j -55.00147458+4.34095268e+00j -55.00147458-4.34095268e+00j -54.32827259+6.55892811e+00j -54.32827259-6.55892811e+00j -53.3879285 +6.84871817e+00j -53.3879285 -6.84871817e+00j -52.4990243 +6.59501387e+00j -52.4990243 -6.59501387e+00j -53.67818507+3.23483577e+00j -53.67818507-3.23483577e+00j -51.88135166+5.91077702e+00j -51.88135166-5.91077702e+00j -51.7039626 +4.93694094e+00j -51.7039626 -4.93694094e+00j -52.17109935+3.84665442e+00j -52.17109935-3.84665442e+00j -36.32065049+0.00000000e+00j -36.31989105+4.38339629e-04j -36.31989105-4.38339629e-04j] F3 zs: [-45.24811282 +0.j -45.91089748 +6.72946623j -45.91089748 -6.72946623j -49.61634662+14.03424181j -49.61634662-14.03424181j] <IPython.core.display.Markdown object> <IPython.core.display.Markdown object> <IPython.core.display.Markdown object>test_Make_ADY[ExampleModels]
output
['-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186'] ['(-2178.2052157029675+0j)', '(-287.1654677213149+0j)', '(-1695.4502902414451+0j)', '(-12.88579675913225+5.603093166973548j)', '(-12.88579675913225-5.603093166973548j)', '(-12.890063158702414+5.596646900702218j)', '(-12.890063158702414-5.596646900702218j)', '(-12.890063158702372+5.596646900702337j)', '(-12.890063158702372-5.596646900702337j)', '(-12.890063158702388+5.5966469007023j)', '(-12.890063158702388-5.5966469007023j)', '(-12.890063158702446+5.596646900702153j)', '(-12.890063158702446-5.596646900702153j)', '(-12.89006315870248+5.5966469007020585j)', '(-12.89006315870248-5.5966469007020585j)', '(-12.890063158702388+5.596646900702294j)', '(-12.890063158702388-5.596646900702294j)', '(-12.894331029649276+5.590187234277706j)', '(-12.894331029649276-5.590187234277706j)', '(-41254.24545663495+74429.27280788597j)', '(-41254.24545663495-74429.27280788597j)', '(-393.2693078650807+641.5816869585774j)', '(-393.2693078650807-641.5816869585774j)', '(-270.2702244864787+531.5395656330295j)', '(-270.2702244864787-531.5395656330295j)', '(-0.1697459318944503+0.05397308658344683j)', '(-0.1697459318944503-0.05397308658344683j)', '(-0.17074996279987628+0.056468761593217655j)', '(-0.17074996279987628-0.056468761593217655j)', '(-0.35336968654430834+1.2037757200586874j)', '(-0.35336968654430834-1.2037757200586874j)', '(-0.3531584357281473+1.2030238621502156j)', '(-0.3531584357281473-1.2030238621502156j)', '(-0.06095148837221796+1.7758885072601485j)', '(-0.06095148837221796-1.7758885072601485j)', '(-0.07262696482159792+2.327687224237036j)', '(-0.07262696482159792-2.327687224237036j)', '(-82.12808426287661+279.8184014267577j)', '(-82.12808426287661-279.8184014267577j)', '(-12.890063158702372+5.596646900702337j)', '(-12.890063158702372-5.596646900702337j)'] <IPython.core.display.Markdown object> FBNS scale 8.139106765576328e+20 <IPython.core.display.Markdown object> <IPython.core.display.Markdown object> The plant is stable and has no non-minphase zeros. This means that the plant is strictly proper and no adjustments need to be made to the plant <IPython.core.display.Markdown object> <IPython.core.display.Markdown object> <IPython.core.display.Markdown object>
test_Make_ADY[ExampleModels_newFOM]
output
['-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186'] ['-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186'] FBNS scale 2.9605920931797355e-11 <IPython.core.display.Markdown object> <IPython.core.display.Markdown object> The plant is stable and has no non-minphase zeros. This means that the plant is strictly proper and no adjustments need to be made to the plant <IPython.core.display.Markdown object> <IPython.core.display.Markdown object> <IPython.core.display.Markdown object>
test_Make_ADY[ExampleModels_rescale]
output
['-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186'] ['(-2178.2052157029675+0j)', '(-287.1654677213149+0j)', '(-1695.4502902414451+0j)', '(-12.88579675913225+5.603093166973548j)', '(-12.88579675913225-5.603093166973548j)', '(-12.890063158702414+5.596646900702218j)', '(-12.890063158702414-5.596646900702218j)', '(-12.890063158702372+5.596646900702337j)', '(-12.890063158702372-5.596646900702337j)', '(-12.890063158702388+5.5966469007023j)', '(-12.890063158702388-5.5966469007023j)', '(-12.890063158702446+5.596646900702153j)', '(-12.890063158702446-5.596646900702153j)', '(-12.89006315870248+5.5966469007020585j)', '(-12.89006315870248-5.5966469007020585j)', '(-12.890063158702388+5.596646900702294j)', '(-12.890063158702388-5.596646900702294j)', '(-12.894331029649276+5.590187234277706j)', '(-12.894331029649276-5.590187234277706j)', '(-41254.24545663495+74429.27280788597j)', '(-41254.24545663495-74429.27280788597j)', '(-393.2693078650807+641.5816869585774j)', '(-393.2693078650807-641.5816869585774j)', '(-270.2702244864787+531.5395656330295j)', '(-270.2702244864787-531.5395656330295j)', '(-0.1697459318944503+0.05397308658344683j)', '(-0.1697459318944503-0.05397308658344683j)', '(-0.17074996279987628+0.056468761593217655j)', '(-0.17074996279987628-0.056468761593217655j)', '(-0.35336968654430834+1.2037757200586874j)', '(-0.35336968654430834-1.2037757200586874j)', '(-0.3531584357281473+1.2030238621502156j)', '(-0.3531584357281473-1.2030238621502156j)', '(-0.06095148837221796+1.7758885072601485j)', '(-0.06095148837221796-1.7758885072601485j)', '(-0.07262696482159792+2.327687224237036j)', '(-0.07262696482159792-2.327687224237036j)', '(-82.12808426287661+279.8184014267577j)', '(-82.12808426287661-279.8184014267577j)', '(-12.890063158702372+5.596646900702337j)', '(-12.890063158702372-5.596646900702337j)'] <IPython.core.display.Markdown object> FBNS scale 8.139106765576328e+20 <IPython.core.display.Markdown object> <IPython.core.display.Markdown object> The plant is stable and has no non-minphase zeros. This means that the plant is strictly proper and no adjustments need to be made to the plant <IPython.core.display.Markdown object> <IPython.core.display.Markdown object> <IPython.core.display.Markdown object>
test_Make_ADY[ExampleModels_working]
output
['-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186', '-2.4871403090745186'] ['(-2178.2052157029675+0j)', '(-287.1654677213149+0j)', '(-1695.4502902414451+0j)', '(-12.88579675913225+5.603093166973548j)', '(-12.88579675913225-5.603093166973548j)', '(-12.890063158702414+5.596646900702218j)', '(-12.890063158702414-5.596646900702218j)', '(-12.890063158702372+5.596646900702337j)', '(-12.890063158702372-5.596646900702337j)', '(-12.890063158702388+5.5966469007023j)', '(-12.890063158702388-5.5966469007023j)', '(-12.890063158702446+5.596646900702153j)', '(-12.890063158702446-5.596646900702153j)', '(-12.89006315870248+5.5966469007020585j)', '(-12.89006315870248-5.5966469007020585j)', '(-12.890063158702388+5.596646900702294j)', '(-12.890063158702388-5.596646900702294j)', '(-12.894331029649276+5.590187234277706j)', '(-12.894331029649276-5.590187234277706j)', '(-41254.24545663495+74429.27280788597j)', '(-41254.24545663495-74429.27280788597j)', '(-393.2693078650807+641.5816869585774j)', '(-393.2693078650807-641.5816869585774j)', '(-270.2702244864787+531.5395656330295j)', '(-270.2702244864787-531.5395656330295j)', '(-0.1697459318944503+0.05397308658344683j)', '(-0.1697459318944503-0.05397308658344683j)', '(-0.17074996279987628+0.056468761593217655j)', '(-0.17074996279987628-0.056468761593217655j)', '(-0.35336968654430834+1.2037757200586874j)', '(-0.35336968654430834-1.2037757200586874j)', '(-0.3531584357281473+1.2030238621502156j)', '(-0.3531584357281473-1.2030238621502156j)', '(-0.06095148837221796+1.7758885072601485j)', '(-0.06095148837221796-1.7758885072601485j)', '(-0.07262696482159792+2.327687224237036j)', '(-0.07262696482159792-2.327687224237036j)', '(-82.12808426287661+279.8184014267577j)', '(-82.12808426287661-279.8184014267577j)', '(-12.890063158702372+5.596646900702337j)', '(-12.890063158702372-5.596646900702337j)'] <IPython.core.display.Markdown object> FBNS scale 8.139106765576328e+20 <IPython.core.display.Markdown object> <IPython.core.display.Markdown object> The plant is stable and has no non-minphase zeros. This means that the plant is strictly proper and no adjustments need to be made to the plant <IPython.core.display.Markdown object> <IPython.core.display.Markdown object> <IPython.core.display.Markdown object>
- transposeSys(sys, FOM_ns)[source]¶
This is a pytest needing documentation
code
1def transposeSys(sys, FOM_ns): 2 # add Z_inf as an input 3 sys_inf_in_badname = ssutil.duplicate_io(sys, 'T.in.1') 4 sys_inf_in = ssutil.rename_io(sys_inf_in_badname, 'T.in.1.2', 'Zinf.in') 5 #print(sys.iod) 6 7 inputs= ['D.in', 'O.in', 'S.in', 'Zinf.in'] 8 outputs_no_inf = [fns + '.out' for fns in FOM_ns] + ['T.out'] 9 10 sys_red = ssutil.truncate_io(sys_inf_in, inputs, outputs_no_inf) 11 12 # Transpose the system 13 sys_transpose = ssutil.transpose(sys_red) 14 15 # Reorder the inputs and outputs 16 sys_transpose = ssutil.reorder_io(sys_transpose, 17 [fns + '.in' for fns in FOM_ns] + ['T.in'], [ 18 'Zinf.out', 19 'S.out', 20 'O.out', 21 'D.out' 22 ]) 23 24 return sys_transpose
pytest information
This code is wrapped in a pytest function using conventions detailed in pytest_conventions. The full name of this test, as known by the documentation, is:
test.ASC_SOLVER.T_ADY_make_sys.transposeSysThe full name is useful when building documentation, to link a reference to this page using
:func:`name`, or directly include it with an autofunction directive. The collapse nodes below show every instance of the test run. There may only be one, but if the test was run multiple times through pytest parametrizations, the list can be longer.
- ws_tf2ss(z, p, k, gain=1, mfactor=1, angular=True)[source]¶
Takes a python control zpk and returns a python control statespace. Uses more numerically stable wield computations to convert between.
code
docstring
""" Takes a python control zpk and returns a python control statespace. Uses more numerically stable wield computations to convert between. """
1def ws_tf2ss(z, p, k, gain=1, mfactor=1, angular=True): 2 3 ws_zpk = SISO.zpk(z, p, k * gain, angular=True, fiducial_rtol=1e-5, fiducial_atol=1e-10) 4 ws_SS = ws_zpk.asSS * mfactor 5 # print("ZPK: ", ws_SS.asZPK.z, ws_SS.asZPK.p) 6 7 # return control.ss(ws_SS.A.T, ws_SS.C.T, ws_SS.B.T, ws_SS.D.T) 8 return control.ss(ws_SS.A, ws_SS.B, ws_SS.C, ws_SS.D)
pytest information
This code is wrapped in a pytest function using conventions detailed in pytest_conventions. The full name of this test, as known by the documentation, is:
test.ASC_SOLVER.T_ADY_make_sys.ws_tf2ssThe full name is useful when building documentation, to link a reference to this page using
:func:`name`, or directly include it with an autofunction directive. The collapse nodes below show every instance of the test run. There may only be one, but if the test was run multiple times through pytest parametrizations, the list can be longer.