def test_properties(self):
        # Test setters/getters for cross class properties.
        # This implicitly tests to_tf() and to_zpk()

        # Getters
        s = StateSpace(1, 1, 1, 1, dt=0.05)
        assert_equal(s.poles, [1])
        assert_equal(s.zeros, [0])
        with warnings.catch_warnings():
            warnings.simplefilter("ignore", DeprecationWarning)
            assert_equal(s.gain, 1)
            assert_equal(s.num, [1, 0])
            assert_equal(s.den, [1, -1])

        # transfer function setters
        s2 = StateSpace(2, 2, 2, 2, dt=0.05)
        with warnings.catch_warnings():
            warnings.simplefilter("ignore", DeprecationWarning)
            s2.num = [1, 0]
            s2.den = [1, -1]
            self._compare_systems(s, s2)

        # zpk setters
        s2 = StateSpace(2, 2, 2, 2, dt=0.05)
        with warnings.catch_warnings():
            warnings.simplefilter("ignore", DeprecationWarning)
            s2.poles = 1
            s2.zeros = 0
            s2.gain = 1
            self._compare_systems(s, s2)
Example #2
0
 def test_initialization(self):
     # Check that all initializations work
     dt = 0.05
     s = StateSpace(1, 1, 1, 1, dt=dt)
     s = StateSpace([1], [2], [3], [4], dt=dt)
     s = StateSpace(np.array([[1, 2], [3, 4]]), np.array([[1], [2]]),
                    np.array([[1, 0]]), np.array([[0]]), dt=dt)
     s = StateSpace(1, 1, 1, 1, dt=True)
Example #3
0
    def test_conversion(self):
        # Check the conversion functions
        s = StateSpace(1, 2, 3, 4)
        assert_(isinstance(s.to_ss(), StateSpace))
        assert_(isinstance(s.to_tf(), TransferFunction))
        assert_(isinstance(s.to_zpk(), ZerosPolesGain))

        # Make sure copies work
        assert_(StateSpace(s) is not s)
        assert_(s.to_ss() is not s)
Example #4
0
def tfSim(inputType, mag, wn, zeta, init_xdot, init_x, step_increment,
          endtime):
    num = [0, 0, wn**2]
    den = [1, 2 * zeta * wn, wn**2]

    timesteps = np.arange(0, endtime, step_increment)

    if inputType == "sine":
        u = mag * np.sin(2 * np.pi * timesteps)
    elif inputType == "step":
        u = mag * np.ones(timesteps.size)
    else:
        raise ValueException("unrecognized input type")

    [A, B, C, D] = tf2ss(num, den)
    sys = StateSpace(A, B, C, D)
    print("C: {} ; shape: {}".format(C, C.shape))

    init_xdot = init_xdot / C[0][1]
    init_x = init_x / C[0][1]

    init_cond = np.array([init_xdot, init_x])
    print(init_cond)
    print("initial conditions shape: {}".format(init_cond.shape))

    y = lsim(sys, u, timesteps, init_cond)

    return y
Example #5
0
    def test_properties(self):
        # Test setters/getters for cross class properties.
        # This implicitly tests to_tf() and to_zpk()

        # Getters
        s = StateSpace(1, 1, 1, 1, dt=0.05)
        assert_equal(s.poles, [1])
        assert_equal(s.zeros, [0])
Example #6
0
    def test_properties(self):
        # Test setters/getters for cross class properties.
        # This implicitly tests to_tf() and to_zpk()

        # Getters
        s = StateSpace(1, 1, 1, 1, dt=0.05)
        assert_equal(s.poles, [1])
        assert_equal(s.zeros, [0])
        with warnings.catch_warnings():
            warnings.simplefilter("ignore", DeprecationWarning)
            assert_equal(s.gain, 1)
            assert_equal(s.num, [1, 0])
            assert_equal(s.den, [1, -1])

        # transfer function setters
        s2 = StateSpace(2, 2, 2, 2, dt=0.05)
        with warnings.catch_warnings():
            warnings.simplefilter("ignore", DeprecationWarning)
            s2.num = [1, 0]
            s2.den = [1, -1]
            self._compare_systems(s, s2)

        # zpk setters
        s2 = StateSpace(2, 2, 2, 2, dt=0.05)
        with warnings.catch_warnings():
            warnings.simplefilter("ignore", DeprecationWarning)
            s2.poles = 1
            s2.zeros = 0
            s2.gain = 1
            self._compare_systems(s, s2)
Example #7
0
    def test_conversion(self):
        # Check the conversion functions
        s = StateSpace(1, 2, 3, 4, dt=0.05)
        assert_(isinstance(s.to_ss(), StateSpace))
        assert_(isinstance(s.to_tf(), TransferFunction))
        assert_(isinstance(s.to_zpk(), ZerosPolesGain))

        # Make sure copies work
        assert_(StateSpace(s) is not s)
        assert_(s.to_ss() is not s)
Example #8
0
def get_model():

    # Build model: R3C3
    # =================
    # Constants
    Vi = 400.  # Indoor volume [m3]

    # Parameters
    Ri = 0.25 / (3. * Vi ** (2./3.))  # Int. wall resistance [K/W]
    Re1 = 1.00 / (3. * Vi ** (2./3.))  # Ext. wall resistance [K/W]
    Re2 = 0.25 / (3. * Vi ** (2./3.))  # Ext. wall resistance [K/W]
    Ci = 30.0 * Vi * 1.2 * 1005.  # Int. wall capacitance [J/K]
    Ce = 20.0 * Vi * 1.2 * 1005.  # Ext. wall capacitance [J/K]
    Cr = 10.0 * Vi * 1.2 * 1005.  # Zone capacitance [J/K]
    fsol = 1.8   # Solar coeff.
    qmet = 100.  # Metabolic heat gain [W/person]

    # States, inputs and outputs:
    # n (states)  : [Tr, Ti, Te]
    # p (inputs)  : [Tout, Hglo, qhvac, nocc]
    # q (outputs) : [Tr, Ti, Te, qhvac]

    # State matrix (n x n)
    A = np.array([
        [-1/Ri/Cr - 1/Re1/Cr, 1/Ri/Cr,             1/Re1/Cr],
        [1/Ri/Ci,            -1/Ri/Ci,                    0],
        [1/Re1/Ce,                  0, -1/Re1/Ce - 1/Re2/Ce]
    ])  # [Tr, Ti, Te]^T

    # Input matrix (n x p)
    B = np.array([
        [0.,       fsol/Cr, 1/Cr, qmet/Cr],
        [0.,            0.,   0.,      0.],
        [1/Re2/Ce,      0.,   0.,      0.]
    ])

    # Output matrix (q x n)
    C = np.array([
        [1., 0., 0.],  # Tr
        [0., 1., 0.],  # Ti
        [0., 0., 1.],  # Te
        [0., 0., 0.]   # qhvac
    ])

    # Feedthrough matrix (q x p)
    D = np.array([
        [0., 0., 0., 0.],  # Tr
        [0., 0., 0., 0.],  # Ti
        [0., 0., 0., 0.],  # Te
        [0., 0., 1., 0.]   # qhvac
    ])

    return StateSpace(A, B, C, D)
Example #9
0
def maxent(P,A,B,omega=None):
    assert P.ndim==2 and P.shape[0] == P.shape[1], 'P must be a square matrix'

    if omega is None:
        omega=linspace(0,2*pi,200)

    N = omega.size

    iP = inv(P)
    e  = inv(B.T @ iP @ B)
    C1 = e @ B.T @ iP
    n,m = B.shape

# Phi=C1*G(z); max entropy spectrum = Phi^{-1} e Phi^{-1}^*

    rA = A.real
    iA = A.imag
    AA = array([[rA, -iA],[iA, rA]])
    rB = B.real
    iB = B.imag
    BB = array([[rB, -iB],[iB, rB]])
    rC = C1.real
    iC = C1.imag
    CC1= array([[rC, -iC],[iC, rC]])

    Phi_inv = StateSpace(AA-BB*CC1*AA, BB, -CC1*AA, eye(2*m,2*m));

    HH = freqresp(Phi_inv,omega)
    H = HH[:m,:m,:] + 1j*HH[m:2*m,:m,:]

    if m==1:
        h = H.squeeze()
        spectrum = diag(h @ e @ h.T).real.T
    else:
        print('The spectrum is matricial m*m, where m=',m)
        spectrum = zeros((m,N*m))
        halfspectrum = zeros((m,N*m))
        [Ue,svdOmega] = svd(e)
        sqrtsvdOmega = sqrt(svdOmega)
        for i in range(N):
            temp=H[:,:,i]  #H(:,:,i)=temp*e*temp.T
            spectrum[:,(i-1)*m:i*m]     = temp @ e  @ temp.T
            halfspectrum[:,(i-1)*m:i*m] = temp @ Ue @ sqrtsvdOmega

    return spectrum, halfspectrum, omega
# In[14]:

Cp = C
print(Cp)

# In[15]:

Ep = array([E, dot((C1 - C2), X) + dot((E1 - E2), U)]).transpose()
print(Ep)

# Com as matrizes ``Ap``, ``Bp``, ``Cp``, ``Ep`` calculadas o modelo de espaço de estado é montado (a partir daqui o notebook assume o que o ``buckboost.mdl`` no Simulink faz)

# In[16]:

ss_buckboost = StateSpace(Ap, Bp, Cp, Ep)

# Um sinal de tempo (``t_in``) e os steps de entrada (``u`` e ``d``) são gerados utilizando a função [step](#step_function) criada.

# In[17]:

t_in = arange(0, t_final, t_step)

u = step(t_in, .03, 0, v_f, '$\hat{u}$')
d = step(t_in, .03, 0, d_f, '$\hat{d}$')

up = array([u, d]).transpose()

# Nesse momento, com as entradas definidas e o modelo pronto podemos evolui-lo no espaço de estados utilizando a função ``lsim``:

# In[18]:
Example #11
0
C = np.matrix([
    [1, 0, 0, 0],
    [0, 1, 0, 0],
])
#C = np.concatenate((C,np.zeros((4,4))),axis=1)
D = np.zeros((2, 2))

top = np.concatenate((A, np.zeros((4, 2))), axis=1)
bottom = np.concatenate((-C, np.zeros((2, 2))), axis=1)
Ahat = np.concatenate((top, bottom), axis=0)
Bhat = np.concatenate((B, np.zeros((2, 2))), axis=0)
Q = np.diag([1, 1, .0000001, .0000001, 1, 1]) * np.eye(6)
R = np.eye(2)

sys = StateSpace(Ahat, Bhat, np.concatenate((C, np.zeros((2, 2))), axis=1), D)
#sys_d = sys.to_discrete(1)
#Ahat, Bhat = sys_d.A, sys_d.B

K, S, E = lqr(Ahat, Bhat, Q, R)
""" Control """
x = np.matrix([[1], [-1], [2], [3], [0], [0]])  # initial state
ref = np.matrix([[0], [0]])
cont = 0
record_x = []
record_u = []
record_SP = []

y = [[0], [0]]
e_dot = [[0], [0], [0], [0]]
xsi = 0
Example #12
0
# This is the code for Exercise 5.
import numpy as np
from scipy.signal import StateSpace, dlsim
A = np.asarray([[0.,1.],
                [1.,1.]])
B = np.asarray([[0.],[0.]])
C = np.asarray([0.,0.])
D = np.asarray([0.])
plane_sys = StateSpace(A, B, C, D, dt = 1)
t = np.arange(0, 20, 1)
input = np.zeros(len(t))
_, y, x = dlsim(plane_sys, input, t, x0=[0, 1])
F20 = int(x[19, 1])
print("F20 is:", F20)
Example #13
0
 def test_initialization(self):
     # Check that all initializations work
     s = StateSpace(1, 1, 1, 1)
     s = StateSpace([1], [2], [3], [4])
     s = StateSpace(np.array([[1, 2], [3, 4]]), np.array([[1], [2]]),
                    np.array([[1, 0]]), np.array([[0]]))
Example #14
0
    def test_operators(self):
        # Test +/-/* operators on systems

        class BadType(object):
            pass

        s1 = StateSpace(np.array([[-0.5, 0.7], [0.3, -0.8]]),
                        np.array([[1], [0]]),
                        np.array([[1, 0]]),
                        np.array([[0]]),
                        )

        s2 = StateSpace(np.array([[-0.2, -0.1], [0.4, -0.1]]),
                        np.array([[1], [0]]),
                        np.array([[1, 0]]),
                        np.array([[0]])
                        )

        s_discrete = s1.to_discrete(0.1)
        s2_discrete = s2.to_discrete(0.2)

        # Impulse response
        t = np.linspace(0, 1, 100)
        u = np.zeros_like(t)
        u[0] = 1

        # Test multiplication
        for typ in six.integer_types + (float, complex, np.float32,
                                        np.complex128, np.array):
            assert_allclose(lsim(typ(2) * s1, U=u, T=t)[1],
                            typ(2) * lsim(s1, U=u, T=t)[1])

            assert_allclose(lsim(s1 * typ(2), U=u, T=t)[1],
                            lsim(s1, U=u, T=t)[1] * typ(2))

            assert_allclose(lsim(s1 / typ(2), U=u, T=t)[1],
                            lsim(s1, U=u, T=t)[1] / typ(2))

            with assert_raises(TypeError):
                typ(2) / s1

        assert_allclose(lsim(s1 * 2, U=u, T=t)[1],
                        lsim(s1, U=2 * u, T=t)[1])

        assert_allclose(lsim(s1 * s2, U=u, T=t)[1],
                        lsim(s1, U=lsim(s2, U=u, T=t)[1], T=t)[1],
                        atol=1e-5)

        with assert_raises(TypeError):
            s1 / s1

        with assert_raises(TypeError):
            s1 * s_discrete

        with assert_raises(TypeError):
            # Check different discretization constants
            s_discrete * s2_discrete

        with assert_raises(TypeError):
            s1 * BadType()

        with assert_raises(TypeError):
            BadType() * s1

        with assert_raises(TypeError):
            s1 / BadType()

        with assert_raises(TypeError):
            BadType() / s1

        # Test addition
        assert_allclose(lsim(s1 + 2, U=u, T=t)[1],
                        2 * u + lsim(s1, U=u, T=t)[1])

        # Check for dimension mismatch
        with assert_raises(ValueError):
            s1 + np.array([1, 2])

        with assert_raises(ValueError):
            np.array([1, 2]) + s1

        with assert_raises(TypeError):
            s1 + s_discrete

        with assert_raises(ValueError):
            s1 / np.array([[1, 2], [3, 4]])

        with assert_raises(TypeError):
            # Check different discretization constants
            s_discrete + s2_discrete

        with assert_raises(TypeError):
            s1 + BadType()

        with assert_raises(TypeError):
            BadType() + s1

        assert_allclose(lsim(s1 + s2, U=u, T=t)[1],
                        lsim(s1, U=u, T=t)[1] + lsim(s2, U=u, T=t)[1])

        # Test substraction
        assert_allclose(lsim(s1 - 2, U=u, T=t)[1],
                        -2 * u + lsim(s1, U=u, T=t)[1])

        assert_allclose(lsim(2 - s1, U=u, T=t)[1],
                        2 * u + lsim(-s1, U=u, T=t)[1])

        assert_allclose(lsim(s1 - s2, U=u, T=t)[1],
                        lsim(s1, U=u, T=t)[1] - lsim(s2, U=u, T=t)[1])

        with assert_raises(TypeError):
            s1 - BadType()

        with assert_raises(TypeError):
            BadType() - s1
Example #15
0
    def test_operators(self):
        # Test +/-/* operators on systems

        class BadType(object):
            pass

        s1 = StateSpace(
            np.array([[-0.5, 0.7], [0.3, -0.8]]),
            np.array([[1], [0]]),
            np.array([[1, 0]]),
            np.array([[0]]),
        )

        s2 = StateSpace(np.array([[-0.2, -0.1], [0.4, -0.1]]),
                        np.array([[1], [0]]), np.array([[1, 0]]),
                        np.array([[0]]))

        s_discrete = s1.to_discrete(0.1)
        s2_discrete = s2.to_discrete(0.2)
        s3_discrete = s2.to_discrete(0.1)

        # Impulse response
        t = np.linspace(0, 1, 100)
        u = np.zeros_like(t)
        u[0] = 1

        # Test multiplication
        for typ in (int, float, complex, np.float32, np.complex128, np.array):
            assert_allclose(
                lsim(typ(2) * s1, U=u, T=t)[1],
                typ(2) * lsim(s1, U=u, T=t)[1])

            assert_allclose(
                lsim(s1 * typ(2), U=u, T=t)[1],
                lsim(s1, U=u, T=t)[1] * typ(2))

            assert_allclose(
                lsim(s1 / typ(2), U=u, T=t)[1],
                lsim(s1, U=u, T=t)[1] / typ(2))

            with assert_raises(TypeError):
                typ(2) / s1

        assert_allclose(lsim(s1 * 2, U=u, T=t)[1], lsim(s1, U=2 * u, T=t)[1])

        assert_allclose(lsim(s1 * s2, U=u, T=t)[1],
                        lsim(s1, U=lsim(s2, U=u, T=t)[1], T=t)[1],
                        atol=1e-5)

        with assert_raises(TypeError):
            s1 / s1

        with assert_raises(TypeError):
            s1 * s_discrete

        with assert_raises(TypeError):
            # Check different discretization constants
            s_discrete * s2_discrete

        with assert_raises(TypeError):
            s1 * BadType()

        with assert_raises(TypeError):
            BadType() * s1

        with assert_raises(TypeError):
            s1 / BadType()

        with assert_raises(TypeError):
            BadType() / s1

        # Test addition
        assert_allclose(
            lsim(s1 + 2, U=u, T=t)[1], 2 * u + lsim(s1, U=u, T=t)[1])

        # Check for dimension mismatch
        with assert_raises(ValueError):
            s1 + np.array([1, 2])

        with assert_raises(ValueError):
            np.array([1, 2]) + s1

        with assert_raises(TypeError):
            s1 + s_discrete

        with assert_raises(ValueError):
            s1 / np.array([[1, 2], [3, 4]])

        with assert_raises(TypeError):
            # Check different discretization constants
            s_discrete + s2_discrete

        with assert_raises(TypeError):
            s1 + BadType()

        with assert_raises(TypeError):
            BadType() + s1

        assert_allclose(
            lsim(s1 + s2, U=u, T=t)[1],
            lsim(s1, U=u, T=t)[1] + lsim(s2, U=u, T=t)[1])

        # Test subtraction
        assert_allclose(
            lsim(s1 - 2, U=u, T=t)[1], -2 * u + lsim(s1, U=u, T=t)[1])

        assert_allclose(
            lsim(2 - s1, U=u, T=t)[1], 2 * u + lsim(-s1, U=u, T=t)[1])

        assert_allclose(
            lsim(s1 - s2, U=u, T=t)[1],
            lsim(s1, U=u, T=t)[1] - lsim(s2, U=u, T=t)[1])

        with assert_raises(TypeError):
            s1 - BadType()

        with assert_raises(TypeError):
            BadType() - s1

        s = s_discrete + s3_discrete
        assert_(s.dt == 0.1)

        s = s_discrete * s3_discrete
        assert_(s.dt == 0.1)

        s = 3 * s_discrete
        assert_(s.dt == 0.1)

        s = -s_discrete
        assert_(s.dt == 0.1)
import numpy as np
from scipy.signal import StateSpace, lsim, dlsim
from scipy.linalg import expm
import matplotlib.pyplot as plt

# Build the CT system.
A = np.asarray([[0., 1.],
                [-2., -2.]])
B = np.asarray([[1.],[1.]])
C = np.asarray([2.,3.])
D = np.asarray([0.])
t_CT = np.arange(0, 5.01, 0.01)
input_CT = np.ones(len(t_CT))
sys_CT = StateSpace(A, B, C, D)
_, y_CT, x_CT = lsim(sys_CT, input_CT, t_CT, X0=[0., 0.])
y5_CT = y_CT[-1]
print("In the CT system, y(5) is:", y5_CT)

# Calculate the DT system.
G = expm(A);
H = np.asarray([[0.,0.],[0.,0.]])
step = np.arange(0,1.001,0.001)
for i in step:
    H += expm(A*i) * 0.001
H = np.dot(H, B)

# Build the DT system.
sys_DT = StateSpace(G, H, C, D, dt = 1)
print("The discretized state space representation of this system is:")
print(sys_DT)
t_DT = np.arange(0, 6, 1);
Example #17
0
    def __init__(self, Y0, Y1, U0, q=10, u_nominal=1, dt=1):

        if Y0.shape[1] != U0.shape[1]:
            raise ValueError(
                "The number of snapshot pairs Y0, U0 should be equal to the number of training inputs."
            )

        if Y1.shape[1] != U0.shape[1]:
            raise ValueError(
                "The number of snapshot pairs Y1, U0 should be equal to the number of training inputs."
            )

        # Parameters
        self.ny = Y0.shape[0]  # Number of outputs
        self.nu = U0.shape[0]  # Number of inputs
        self.q = q  # Number of POD modes kept
        self.p = Y0.shape[1]  # Number of training snapshots available
        self.dt = dt  # Time step

        # Save snapshots
        self.Y0 = Y0
        self.Y1 = Y1
        self.U0 = U0

        # Compute POD Modes
        self.Uq = self.compute_POD_basis(Y0, self.q)

        # Project snapshots
        H0 = self.Uq.conj().T @ Y0
        H1 = self.Uq.conj().T @ Y1

        # POD projection error
        pod_error = np.linalg.norm((Y0 - self.Uq @ H0), ord='fro')/ \
                    np.linalg.norm(Y0, ord='fro')*100
        print('    POD projection error = ', pod_error, '%')

        # Run DMDc
        self.F, self.G = self.DMDc(H0, H1, U0)

        # Eigendecomposition of A
        lamb, self.W = np.linalg.eig(self.F)
        self.Lambda = np.diag(lamb)
        self.Gamma = np.linalg.inv(self.W) @ self.G

        # DMD modes
        self.Phi = self.Uq @ self.W

        # Setup matrices for sparsity-promoting optimization
        #self.R = np.zeros((self.q, self.p), dtype=np.complex)
        R = self.Lambda @ (
            np.linalg.pinv(self.Phi) @ self.Y0) + self.Gamma @ U0
        L = self.Phi
        #Y1 = self.Y[:,1:]

        # Now cast into quadratic form
        self.P = np.multiply(L.conj().T @ L, (R @ R.conj().T).conj())
        self.d = np.diag(R @ Y1.conj().T @ L).conj()
        self.s = np.trace(Y1.conj().T @ Y1)
        print('s.shape = ', self.s.shape)

        self.sys_pod = StateSpace(self.F, self.G, self.Uq)
        self.sys_dmd = StateSpace(self.Lambda, self.Gamma, self.Phi)

        print('DMD model error:')
        self.sys_dmd.error(Y0, Y1, U0)
Example #18
0
class DMDcsp(object):
    """
    Sparsity-Promoting Dynamic Mode Decomposition with Control Class

    Initialization: Simply provide data matrices Y0, Y1, U0

    """
    def __init__(self, Y0, Y1, U0, q=10, u_nominal=1, dt=1):

        if Y0.shape[1] != U0.shape[1]:
            raise ValueError(
                "The number of snapshot pairs Y0, U0 should be equal to the number of training inputs."
            )

        if Y1.shape[1] != U0.shape[1]:
            raise ValueError(
                "The number of snapshot pairs Y1, U0 should be equal to the number of training inputs."
            )

        # Parameters
        self.ny = Y0.shape[0]  # Number of outputs
        self.nu = U0.shape[0]  # Number of inputs
        self.q = q  # Number of POD modes kept
        self.p = Y0.shape[1]  # Number of training snapshots available
        self.dt = dt  # Time step

        # Save snapshots
        self.Y0 = Y0
        self.Y1 = Y1
        self.U0 = U0

        # Compute POD Modes
        self.Uq = self.compute_POD_basis(Y0, self.q)

        # Project snapshots
        H0 = self.Uq.conj().T @ Y0
        H1 = self.Uq.conj().T @ Y1

        # POD projection error
        pod_error = np.linalg.norm((Y0 - self.Uq @ H0), ord='fro')/ \
                    np.linalg.norm(Y0, ord='fro')*100
        print('    POD projection error = ', pod_error, '%')

        # Run DMDc
        self.F, self.G = self.DMDc(H0, H1, U0)

        # Eigendecomposition of A
        lamb, self.W = np.linalg.eig(self.F)
        self.Lambda = np.diag(lamb)
        self.Gamma = np.linalg.inv(self.W) @ self.G

        # DMD modes
        self.Phi = self.Uq @ self.W

        # Setup matrices for sparsity-promoting optimization
        #self.R = np.zeros((self.q, self.p), dtype=np.complex)
        R = self.Lambda @ (
            np.linalg.pinv(self.Phi) @ self.Y0) + self.Gamma @ U0
        L = self.Phi
        #Y1 = self.Y[:,1:]

        # Now cast into quadratic form
        self.P = np.multiply(L.conj().T @ L, (R @ R.conj().T).conj())
        self.d = np.diag(R @ Y1.conj().T @ L).conj()
        self.s = np.trace(Y1.conj().T @ Y1)
        print('s.shape = ', self.s.shape)

        self.sys_pod = StateSpace(self.F, self.G, self.Uq)
        self.sys_dmd = StateSpace(self.Lambda, self.Gamma, self.Phi)

        print('DMD model error:')
        self.sys_dmd.error(Y0, Y1, U0)

    def DMDc(self, H0, H1, U0):
        """
        Inputs: 
            H0 : first snapshot matrix
            H1 : second snapshot matrix (after one time step)
            U0 : corresponding input

        Outputs:
            A, B : state and output matrices fitted to the reduced-order data

        """

        q = H0.shape[0]
        nu = U0.shape[0]
        p = U0.shape[1]

        U, Sig, VT = np.linalg.svd(np.vstack((H0, U0)), full_matrices=False)
        thres = 1.0e-10
        rtil = np.min((np.sum(np.diag(Sig) > thres), q))
        print('    rtil = ', rtil)
        Util = U[:, :rtil]
        Sigtil = np.diag(Sig)[:rtil, :rtil]
        Vtil = VT.T[:, :rtil]

        U_F = Util[:q, :]
        U_G = Util[q:q + nu, :]

        F = H1 @ Vtil @ np.linalg.inv(Sigtil) @ U_F.conj().T
        G = H1 @ Vtil @ np.linalg.inv(Sigtil) @ U_G.conj().T

        dmdc_error = np.linalg.norm((H1 - (F @ H0 + G @ U0)), ord='fro')/ \
                  np.linalg.norm(H1, ord='fro')*100
        print('    DMDc error = ', dmdc_error, '%')

        print('    Maximum eigenvalue: ', np.max(np.abs(np.linalg.eig(F)[0])))

        return F, G

    def sparse(self, gamma, niter):

        zero_thres = 1.e-6

        print("gamma = %e | number of modes =         " % (gamma))

        # Define and solve the sparsity-promoting optimization problem
        # Weighted L1 norm is updated iteratively
        alpha = cp.Variable(self.q)
        weights = np.ones(self.q)
        for i in range(niter):
            objective_sparse = cp.Minimize(
                cp.quad_form(alpha, self.P) -
                2. * cp.real(self.d.conj().T @ alpha) + self.s +
                gamma * cp.pnorm(np.diag(weights) @ alpha, p=1))
            prob_sparse = cp.Problem(objective_sparse)
            sol_sparse = prob_sparse.solve(verbose=False, solver=cp.SCS)

            alpha_sp = alpha.value  # Sparse solution
            if alpha_sp is None:
                alpha_sp = np.ones(self.q)

            # Update weights
            weights = 1.0 / (np.abs(alpha_sp) + np.finfo(float).eps)

            nonzero = np.abs(alpha_sp) > zero_thres  # Nonzero modes
            print("                               %d of %d" %
                  (np.sum(nonzero), self.q))

        J_sp = np.real(alpha_sp.conj().T @ self.P @ alpha_sp -
                       2 * np.real(self.d.conj().T @ alpha_sp) +
                       self.s)  # Square error
        nx = np.sum(
            nonzero
        )  # Number of nonzero modes - order of the sparse/reduced model

        Ez = np.eye(self.q)[:, ~nonzero]

        # Define and solve the refinement optimization problem
        #alpha = cp.Variable(self.q)
        objective_refine = cp.Minimize(
            cp.quad_form(alpha, self.P) -
            2. * cp.real(self.d.conj().T @ alpha) + self.s)
        if np.sum(~nonzero):
            constraint_refine = [Ez.T @ alpha == 0]
            prob_refine = cp.Problem(objective_refine, constraint_refine)
        else:
            prob_refine = cp.Problem(objective_refine)

        sol_refine = prob_refine.solve()

        alpha_ref = alpha.value
        J_ref = np.real(alpha_ref.conj().T @ self.P @ alpha_ref -
                        2 * np.real(self.d.conj().T @ alpha_ref) +
                        self.s)  # Square error

        P_loss = 100 * np.sqrt(J_ref / self.s)

        # Truncate modes
        E = np.eye(self.q)[:, nonzero]
        Lambda_bar = E.T @ self.Lambda @ E
        Beta_bar = E.T @ self.Gamma
        Phi_bar = self.Phi @ np.diag(alpha_ref) @ E

        # Save data
        stats = {}
        stats["nx"] = nx
        stats["alpha_sp"] = alpha_sp
        stats["alpha_ref"] = alpha_ref
        stats["z_0"] = (np.linalg.pinv(self.Phi) @ self.Y0[:, 0])[nonzero]
        stats["E"] = E
        stats["J_sp"] = J_sp
        stats["J_ref"] = J_ref
        stats["P_loss"] = P_loss

        if nx != 0:
            print("Rank of controllability matrix: %d of %d" %
                  (np.linalg.matrix_rank(control.ctrb(Lambda_bar,
                                                      Beta_bar)), nx))

        # Convert system from complex modal form to real block-modal form
        lambda_bar = np.diag(Lambda_bar)
        A = np.zeros((nx, nx))
        B = np.zeros((nx, self.nu))
        Theta = np.zeros((self.ny, nx))

        i = 0
        while i < nx:
            if np.isreal(lambda_bar[i]):
                A[i, i] = lambda_bar[i]
                B[i] = Beta_bar[i]
                Theta[:, i] = Phi_bar[:, i]
                i += 1
            elif i == nx - 1:
                # In case only one of the conjugate pairs is truncated - this rarely happens
                A[i, i] = np.real(lambda_bar[i])
                B[i] = np.real(Beta_bar[i])
                Theta[:, i] = np.real(Phi_bar[:, i])
                i += 1
            elif np.isreal(np.isreal(lambda_bar[i] + lambda_bar[i + 1])):
                A[i:i + 2, i:i + 2] = np.array(
                    [[np.real(lambda_bar[i]),
                      np.imag(lambda_bar[i])],
                     [-np.imag(lambda_bar[i]),
                      np.real(lambda_bar[i])]])
                B[i] = np.real(Beta_bar[i])
                B[i + 1] = -np.imag(Beta_bar[i])
                Theta[:, i] = 2 * np.real(Phi_bar[:, i])
                Theta[:, i + 1] = 2 * np.imag(Phi_bar[:, i])
                i += 2
            else:
                raise ValueError(
                    "Eigenvalues are not grouped in conjugate pairs")

    #return StateSpace(Lambda_bar, Beta_bar, Phi_bar), lambda_bar, stats
        return StateSpace(A, B, Theta), lambda_bar, stats

    def sparse_batch(self, gamma, niter):
        num = gamma.shape[0]

        self.rsys = num * [None]
        self.sys_eig = num * [None]

        stats = {}
        stats["alpha_sp"] = num * [None]
        stats["alpha_ref"] = num * [None]
        stats["z_0"] = num * [None]
        stats["E"] = num * [None]
        stats['nx'] = np.zeros(num)
        stats["J_sp"] = np.zeros(num)
        stats["J_ref"] = np.zeros(num)
        stats["P_loss"] = np.zeros(num)

        for i in range(num):
            print('Model # %d' % i)
            self.rsys[i], self.sys_eig[i], stats_tmp = self.sparse(
                gamma[i], niter)

            # Save stats
            stats["alpha_sp"][i] = stats_tmp["alpha_sp"]
            stats["alpha_ref"][i] = stats_tmp["alpha_ref"]
            stats["z_0"][i] = stats_tmp["z_0"]
            stats["E"][i] = stats_tmp["E"]
            stats['nx'][i] = stats_tmp['nx']
            stats["J_sp"][i] = stats_tmp["J_sp"]
            stats["J_ref"][i] = stats_tmp["J_ref"]
            stats["P_loss"][i] = stats_tmp["P_loss"]

        self.sp_stats = stats
        return stats

    def compute_noise_cov(self, sys_i, sens):

        # Measurement output matrix
        C = self.rsys[sys_i].C[sens, :]

        ThetaInv = np.linalg.pinv(self.rsys[sys_i].C)

        Qe = np.cov(ThetaInv @ self.Y1 -
                    self.rsys[sys_i].A @ (ThetaInv @ self.Y0 -
                                          self.rsys[sys_i].B @ self.U0))
        Re = np.cov(self.Y1[sens] - C @ (ThetaInv @ self.Y1))

        return C, Qe, Re

    def compute_POD_basis(self, Y, q):
        return np.linalg.svd(Y, full_matrices=False)[0][:, :q]

    """
    Plot functions
    """

    def plot_dmd_modes(self, grid, E=None):

        if E is None:
            E = np.eye(self.q)

        nlevels = 41
        wymin = -0.1
        wymax = 0.1

        nx = E.shape[1]

        X = grid.X()
        Z = grid.Z()
        xmax = np.max(X)

        def Phi(k):
            return np.real(self.Phi @ E)[:, k].reshape((grid.npx, grid.npz))

        fig, axs = plt.subplots(1,
                                figsize=(10, 5),
                                facecolor='w',
                                edgecolor='k')
        cont = axs.contourf(X,
                            Z,
                            Phi(0),
                            nlevels,
                            cmap='coolwarm',
                            vmin=wymin,
                            vmax=wymax)
        cbar = fig.colorbar(cont, ax=axs, orientation='vertical')
        cbar.set_ticks(np.linspace(wymin, wymax, num=6, endpoint=True))
        axs.set_title('$Phi_0$')
        axs.set_xlim([1, xmax])

        # Flat plate patch
        delx = 5.0 / 768.0
        delz = 2.0 / 384.0
        xc = 249 * delx
        zc = 192 * delz
        alpha = 20.0 * np.pi / 180.0
        DL = 80 * delx
        DT = 6 * delz
        flat_plate = patches.Rectangle(
            (xc - DL * np.cos(alpha) / 2. - DT * np.sin(alpha) / 2.,
             zc + DL * np.sin(alpha) / 2. - DT * np.cos(alpha) / 2.),
            DL,
            DT,
            angle=-(alpha * 180.0 / np.pi),
            linewidth=1,
            edgecolor='black',
            facecolor='black')
        axs.add_patch(flat_plate)

        def animate(k):
            axs.clear()
            cont = axs.contourf(X,
                                Z,
                                Phi(k),
                                nlevels,
                                cmap='coolwarm',
                                vmin=wymin,
                                vmax=wymax)
            axs.set_xlabel('$x$')
            axs.set_ylabel('$z$')
            axs.set_title('$Phi_{%d}$' % k)
            axs.set_aspect('equal', 'box')
            axs.add_patch(flat_plate)
            axs.set_xlim([1, xmax])

            return cont

        anim = animation.FuncAnimation(fig,
                                       animate,
                                       frames=range(0, nx, 1),
                                       interval=500)

        return anim

    def plot_model_response(self, sys, grid):

        x0 = np.linalg.pinv(sys.C) @ self.Y0[:, 0]
        xdmd, ydmd = sys.lsim(x0, self.U0)

        nlevels = 41
        wymin = -10
        wymax = 10

        X = grid.X()
        Z = grid.Z()
        xmax = np.max(X)

        def WY(k):
            return self.Y0[:, k].reshape((grid.npx, grid.npz))

        def WY_dmd(k):
            return ydmd[:, k].reshape((grid.npx, grid.npz))

        fig, axs = plt.subplots(2,
                                figsize=(10, 8),
                                facecolor='w',
                                edgecolor='k')
        cont = axs[0].contourf(X,
                               Z,
                               WY(0),
                               nlevels,
                               cmap='coolwarm',
                               vmin=wymin,
                               vmax=wymax)
        cont = axs[1].contourf(X,
                               Z,
                               WY_dmd(0),
                               nlevels,
                               cmap='coolwarm',
                               vmin=wymin,
                               vmax=wymax)
        #plt.clim(wymin, wymax)
        cbar = fig.colorbar(cont, ax=axs, orientation='vertical')
        #cbar.ax.set_autoscale_on(True)
        cbar.set_ticks(np.linspace(wymin, wymax, num=6, endpoint=True))

        # Flat plate patch
        delx = 5.0 / 768.0
        delz = 2.0 / 384.0
        xc = 249 * delx
        zc = 192 * delz
        alpha = 20.0 * np.pi / 180.0
        DL = 80 * delx
        DT = 6 * delz
        flat_plate = patches.Rectangle(
            (xc - DL * np.cos(alpha) / 2. - DT * np.sin(alpha) / 2.,
             zc + DL * np.sin(alpha) / 2. - DT * np.cos(alpha) / 2.),
            DL,
            DT,
            angle=-(alpha * 180.0 / np.pi),
            linewidth=1,
            edgecolor='black',
            facecolor='black')
        flat_plate_2 = patches.Rectangle(
            (xc - DL * np.cos(alpha) / 2. - DT * np.sin(alpha) / 2.,
             zc + DL * np.sin(alpha) / 2. - DT * np.cos(alpha) / 2.),
            DL,
            DT,
            angle=-(alpha * 180.0 / np.pi),
            linewidth=1,
            edgecolor='black',
            facecolor='black')
        axs[0].add_patch(flat_plate)
        axs[1].add_patch(flat_plate_2)

        axs[0].set_xlim([1, xmax])
        axs[1].set_xlim([1, xmax])
        axs[0].set_title('Time step $k = %d$' % (0))

        #cbar.ax.locator_params(nbins=6)

        def animate(k):

            # Clear axes
            axs[0].clear()
            axs[1].clear()

            # Training Data
            cont = axs[0].contourf(X,
                                   Z,
                                   WY(k),
                                   nlevels,
                                   cmap='coolwarm',
                                   vmin=wymin,
                                   vmax=wymax)
            axs[0].set_xlabel('$x$')
            axs[0].set_ylabel('$z$')
            axs[0].set_aspect('equal', 'box')
            axs[0].add_patch(flat_plate)
            axs[0].set_xlim([1, xmax])
            axs[0].set_title('Time step $k = %d$' % (k))

            # Simulation Results
            cont = axs[1].contourf(X,
                                   Z,
                                   WY_dmd(k),
                                   nlevels,
                                   cmap='coolwarm',
                                   vmin=wymin,
                                   vmax=wymax)
            axs[1].set_xlabel('$x$')
            axs[1].set_ylabel('$z$')
            axs[1].set_aspect('equal', 'box')
            axs[1].add_patch(flat_plate_2)
            axs[1].set_xlim([1, xmax])

            return cont

        anim = animation.FuncAnimation(fig,
                                       animate,
                                       frames=range(0, self.p, 4),
                                       interval=200)

        return anim
Example #19
0
    def sparse(self, gamma, niter):

        zero_thres = 1.e-6

        print("gamma = %e | number of modes =         " % (gamma))

        # Define and solve the sparsity-promoting optimization problem
        # Weighted L1 norm is updated iteratively
        alpha = cp.Variable(self.q)
        weights = np.ones(self.q)
        for i in range(niter):
            objective_sparse = cp.Minimize(
                cp.quad_form(alpha, self.P) -
                2. * cp.real(self.d.conj().T @ alpha) + self.s +
                gamma * cp.pnorm(np.diag(weights) @ alpha, p=1))
            prob_sparse = cp.Problem(objective_sparse)
            sol_sparse = prob_sparse.solve(verbose=False, solver=cp.SCS)

            alpha_sp = alpha.value  # Sparse solution
            if alpha_sp is None:
                alpha_sp = np.ones(self.q)

            # Update weights
            weights = 1.0 / (np.abs(alpha_sp) + np.finfo(float).eps)

            nonzero = np.abs(alpha_sp) > zero_thres  # Nonzero modes
            print("                               %d of %d" %
                  (np.sum(nonzero), self.q))

        J_sp = np.real(alpha_sp.conj().T @ self.P @ alpha_sp -
                       2 * np.real(self.d.conj().T @ alpha_sp) +
                       self.s)  # Square error
        nx = np.sum(
            nonzero
        )  # Number of nonzero modes - order of the sparse/reduced model

        Ez = np.eye(self.q)[:, ~nonzero]

        # Define and solve the refinement optimization problem
        #alpha = cp.Variable(self.q)
        objective_refine = cp.Minimize(
            cp.quad_form(alpha, self.P) -
            2. * cp.real(self.d.conj().T @ alpha) + self.s)
        if np.sum(~nonzero):
            constraint_refine = [Ez.T @ alpha == 0]
            prob_refine = cp.Problem(objective_refine, constraint_refine)
        else:
            prob_refine = cp.Problem(objective_refine)

        sol_refine = prob_refine.solve()

        alpha_ref = alpha.value
        J_ref = np.real(alpha_ref.conj().T @ self.P @ alpha_ref -
                        2 * np.real(self.d.conj().T @ alpha_ref) +
                        self.s)  # Square error

        P_loss = 100 * np.sqrt(J_ref / self.s)

        # Truncate modes
        E = np.eye(self.q)[:, nonzero]
        Lambda_bar = E.T @ self.Lambda @ E
        Beta_bar = E.T @ self.Gamma
        Phi_bar = self.Phi @ np.diag(alpha_ref) @ E

        # Save data
        stats = {}
        stats["nx"] = nx
        stats["alpha_sp"] = alpha_sp
        stats["alpha_ref"] = alpha_ref
        stats["z_0"] = (np.linalg.pinv(self.Phi) @ self.Y0[:, 0])[nonzero]
        stats["E"] = E
        stats["J_sp"] = J_sp
        stats["J_ref"] = J_ref
        stats["P_loss"] = P_loss

        if nx != 0:
            print("Rank of controllability matrix: %d of %d" %
                  (np.linalg.matrix_rank(control.ctrb(Lambda_bar,
                                                      Beta_bar)), nx))

        # Convert system from complex modal form to real block-modal form
        lambda_bar = np.diag(Lambda_bar)
        A = np.zeros((nx, nx))
        B = np.zeros((nx, self.nu))
        Theta = np.zeros((self.ny, nx))

        i = 0
        while i < nx:
            if np.isreal(lambda_bar[i]):
                A[i, i] = lambda_bar[i]
                B[i] = Beta_bar[i]
                Theta[:, i] = Phi_bar[:, i]
                i += 1
            elif i == nx - 1:
                # In case only one of the conjugate pairs is truncated - this rarely happens
                A[i, i] = np.real(lambda_bar[i])
                B[i] = np.real(Beta_bar[i])
                Theta[:, i] = np.real(Phi_bar[:, i])
                i += 1
            elif np.isreal(np.isreal(lambda_bar[i] + lambda_bar[i + 1])):
                A[i:i + 2, i:i + 2] = np.array(
                    [[np.real(lambda_bar[i]),
                      np.imag(lambda_bar[i])],
                     [-np.imag(lambda_bar[i]),
                      np.real(lambda_bar[i])]])
                B[i] = np.real(Beta_bar[i])
                B[i + 1] = -np.imag(Beta_bar[i])
                Theta[:, i] = 2 * np.real(Phi_bar[:, i])
                Theta[:, i + 1] = 2 * np.imag(Phi_bar[:, i])
                i += 2
            else:
                raise ValueError(
                    "Eigenvalues are not grouped in conjugate pairs")

    #return StateSpace(Lambda_bar, Beta_bar, Phi_bar), lambda_bar, stats
        return StateSpace(A, B, Theta), lambda_bar, stats