5050from .statesp import StateSpace
5151from .statefbk import gram
5252
53- __all__ = ['hsvd' , 'balred' , 'modred' , 'era' , 'markov' , 'minreal' ]
53+ __all__ = ['hsvd' , 'balred' , 'modred' , 'era' , 'markov' , 'minreal' , ]
5454
5555# Hankel Singular Value Decomposition
5656# The following returns the Hankel singular values, which are singular values
@@ -197,10 +197,17 @@ def modred(sys, ELIM, method='matchdc'):
197197 rsys = StateSpace (Ar ,Br ,Cr ,Dr )
198198 return rsys
199199
200- def balred (sys , orders , method = 'truncate' ):
200+ def balred (sys , orders , method = 'truncate' , alpha = None ):
201201 """
202202 Balanced reduced order model of sys of a given order.
203203 States are eliminated based on Hankel singular value.
204+ If sys has unstable modes, they are removed, the
205+ balanced realization is done on the stable part, then
206+ reinserted IAW reference below.
207+
208+ Reference: Hsu,C.S., and Hou,D., 1991,
209+ Reducing unstable linear control systems via real Schur transformation.
210+ Electronics Letters, 27, 984-986.
204211
205212 Parameters
206213 ----------
@@ -211,26 +218,46 @@ def balred(sys, orders, method='truncate'):
211218 of systems)
212219 method: string
213220 Method of removing states, either ``'truncate'`` or ``'matchdc'``.
221+ alpha: float
222+ Specifies the alpha-stability boundary for the eigenvalues
223+ of the state dynamics matrix A. For a continuous-time
224+ system, alpha <= 0 is the boundary value for the real parts
225+ of eigenvalues, while for a discrete-time system, 0 <= alpha <= 1
226+ represents the boundary value for the moduli of eigenvalues.
227+ The alpha-stability domain does not include the boundary.
214228
215229 Returns
216230 -------
217231 rsys: StateSpace
218- A reduced order model
232+ A reduced order model or a list of reduced order models if orders is a list
219233
220234 Raises
221235 ------
222236 ValueError
223- * if `method` is not ``'truncate'``
224- * if eigenvalues of `sys.A` are not all in left half plane
225- (`sys` must be stable)
237+ * if `method` is not ``'truncate'`` or ``'matchdc'``
226238 ImportError
227- if slycot routine ab09ad is not found
239+ if slycot routine ab09md or ab09nd is not found
240+
241+ ValueError
242+ if there are more unstable modes than any value in orders
228243
229244 Examples
230245 --------
231- >>> rsys = balred(sys, order , method='truncate')
246+ >>> rsys = balred(sys, orders , method='truncate')
232247
233248 """
249+ if method != 'truncate' and method != 'matchdc' :
250+ raise ValueError ("supported methods are 'truncate' or 'matchdc'" )
251+ elif method == 'truncate' :
252+ try :
253+ from slycot import ab09md
254+ except ImportError :
255+ raise ControlSlycot ("can't find slycot subroutine ab09md" )
256+ elif method == 'matchdc' :
257+ try :
258+ from slycot import ab09nd
259+ except ImportError :
260+ raise ControlSlycot ("can't find slycot subroutine ab09nd" )
234261
235262 #Check for ss system object, need a utility for this?
236263
@@ -241,34 +268,40 @@ def balred(sys, orders, method='truncate'):
241268 # dico = 'D'
242269 # else:
243270 dico = 'C'
244-
245- #Check system is stable
246- D ,V = np .linalg .eig (sys .A )
247- # print D.shape
248- # print D
249- for e in D :
250- if e .real >= 0 :
251- raise ValueError ("Oops, the system is unstable!" )
252-
253- if method == 'matchdc' :
254- raise ValueError ("MatchDC not yet supported!" )
255- elif method == 'truncate' :
256- try :
257- from slycot import ab09ad
258- except ImportError :
259- raise ControlSlycot ("can't find slycot subroutine ab09ad" )
260- job = 'B' # balanced (B) or not (N)
261- equil = 'N' # scale (S) or not (N)
271+ job = 'B' # balanced (B) or not (N)
272+ equil = 'N' # scale (S) or not (N)
273+ if alpha is None :
274+ if dico == 'C' :
275+ alpha = 0.
276+ elif dico == 'D' :
277+ alpha = 1.
278+
279+ rsys = [] #empty list for reduced systems
280+
281+ #check if orders is a list or a scalar
282+ try :
283+ order = iter (orders )
284+ except TypeError : #if orders is a scalar
285+ orders = [orders ]
286+
287+ for i in orders :
262288 n = np .size (sys .A ,0 )
263289 m = np .size (sys .B ,1 )
264290 p = np .size (sys .C ,0 )
265- Nr , Ar , Br , Cr , hsv = ab09ad (dico ,job ,equil ,n ,m ,p ,sys .A ,sys .B ,sys .C ,nr = orders ,tol = 0.0 )
266-
267- rsys = StateSpace (Ar , Br , Cr , sys .D )
291+ if method == 'truncate' :
292+ Nr , Ar , Br , Cr , Ns , hsv = ab09md (dico ,job ,equil ,n ,m ,p ,sys .A ,sys .B ,sys .C ,alpha = alpha ,nr = i ,tol = 0.0 )
293+ rsys .append (StateSpace (Ar , Br , Cr , sys .D ))
294+
295+ elif method == 'matchdc' :
296+ Nr , Ar , Br , Cr , Dr , Ns , hsv = ab09nd (dico ,job ,equil ,n ,m ,p ,sys .A ,sys .B ,sys .C ,sys .D ,alpha = alpha ,nr = i ,tol1 = 0.0 ,tol2 = 0.0 )
297+ rsys .append (StateSpace (Ar , Br , Cr , Dr ))
298+
299+ #if orders was a scalar, just return the single reduced model, not a list
300+ if len (orders ) == 1 :
301+ return rsys [0 ]
302+ #if orders was a list/vector, return a list/vector of systems
268303 else :
269- raise ValueError ("Oops, method is not supported!" )
270-
271- return rsys
304+ return rsys
272305
273306def minreal (sys , tol = None , verbose = True ):
274307 '''
0 commit comments