@@ -74,6 +74,8 @@ def siso_ss2_dtnone(self, siso_ss2):
7474 ss2 = siso_ss2 .sys
7575 T = TSys (StateSpace (ss2 .A , ss2 .B , ss2 .C , 0 , None ))
7676 T .t = np .arange (0 , 10 , 1. )
77+ T .ystep = np .array ([ 0. , 86. , - 72. , 230. , - 360. , 806. ,
78+ - 1512. , 3110. , - 6120. , 12326. ])
7779 return T
7880
7981 @pytest .fixture
@@ -136,12 +138,18 @@ def siso_dtf0(self):
136138 def siso_dtf1 (self ):
137139 T = TSys (TransferFunction ([1 ], [1 , 1 , 0.25 ], True ))
138140 T .t = np .arange (0 , 5 , 1 )
141+ T .ystep = np .array ([0. , 0. , 1. , 0. , 0.75 ])
139142 return T
140143
141144 @pytest .fixture
142145 def siso_dtf2 (self ):
143146 T = TSys (TransferFunction ([1 ], [1 , 1 , 0.25 ], 0.2 ))
144147 T .t = np .arange (0 , 5 , 0.2 )
148+ T .ystep = np .array ([0. , 0. , 1. , 0. , 0.75 , 0.25 ,
149+ 0.5625 , 0.375 , 0.4844 , 0.4219 , 0.457 , 0.4375 ,
150+ 0.4482 , 0.4424 , 0.4456 , 0.4438 , 0.4448 , 0.4443 ,
151+ 0.4445 , 0.4444 , 0.4445 , 0.4444 , 0.4445 , 0.4444 ,
152+ 0.4444 ])
145153 return T
146154
147155 @pytest .fixture
@@ -707,6 +715,58 @@ def test_forced_response_legacy(self):
707715 t , y = ct .forced_response (sys , T , U )
708716 t , y , x = ct .forced_response (sys , T , U , return_x = True )
709717
718+ @pytest .mark .parametrize (
719+ "tsystem, fr_kwargs, refattr" ,
720+ [pytest .param ("siso_ss1" ,
721+ {'X0' : [0.5 , 1 ], 'T' : np .linspace (0 , 1 , 10 )},
722+ 'yinitial' ,
723+ id = "ctime no T" ),
724+ pytest .param ("siso_dtf1" ,
725+ {'U' : np .ones (5 ,)}, 'ystep' ,
726+ id = "dt=True, no U" ),
727+ pytest .param ("siso_dtf2" ,
728+ {'U' : np .ones (25 ,)}, 'ystep' ,
729+ id = "dt=0.2, no U" ),
730+ pytest .param ("siso_ss2_dtnone" ,
731+ {'U' : np .ones (10 ,)}, 'ystep' ,
732+ id = "dt=None, no U" )],
733+ indirect = ["tsystem" ])
734+ def test_forced_response_T_U (self , tsystem , fr_kwargs , refattr ):
735+ """Test documented forced_response behavior for parameters T and U."""
736+ t , y = forced_response (tsystem .sys , ** fr_kwargs )
737+ np .testing .assert_allclose (t , tsystem .t )
738+ np .testing .assert_allclose (y , getattr (tsystem , refattr ), rtol = 1e-3 )
739+
740+ def test_forced_response_invalid (self , siso_ss1 , siso_dss2 ):
741+ """Test invalid parameters."""
742+ with pytest .raises (TypeError ,
743+ match = "StateSpace.*or.*TransferFunction" ):
744+ forced_response ("not a system" )
745+
746+ # ctime
747+ with pytest .raises (ValueError , match = "T.*is mandatory for continuous" ):
748+ forced_response (siso_ss1 .sys )
749+ with pytest .raises (ValueError , match = "time values must be equally "
750+ "spaced" ):
751+ forced_response (siso_ss1 .sys , [0 , 0.1 , 0.12 , 0.4 ])
752+
753+ # dtime with sys.dt > 0
754+ with pytest .raises (ValueError , match = "can't both be zero" ):
755+ forced_response (siso_dss2 .sys )
756+ with pytest .raises (ValueError , match = "must have same elements" ):
757+ forced_response (siso_dss2 .sys ,
758+ T = siso_dss2 .t , U = np .random .randn (1 , 12 ))
759+ with pytest .raises (ValueError , match = "must have same elements" ):
760+ forced_response (siso_dss2 .sys ,
761+ T = siso_dss2 .t , U = np .random .randn (12 ))
762+ with pytest .raises (ValueError , match = "must match sampling time" ):
763+ forced_response (siso_dss2 .sys , T = siso_dss2 .t * 0.9 )
764+ with pytest .raises (ValueError , match = "must be multiples of "
765+ "sampling time" ):
766+ forced_response (siso_dss2 .sys , T = siso_dss2 .t * 1.1 )
767+ # but this is ok
768+ forced_response (siso_dss2 .sys , T = siso_dss2 .t * 2 )
769+
710770
711771 @pytest .mark .parametrize ("u, x0, xtrue" ,
712772 [(np .zeros ((10 ,)),
0 commit comments