Перевод из Matlab: новые задачи (комплексное уравнение Гинзбурга Ландау - PullRequest
0 голосов
/ 02 декабря 2019

Спасибо за всю вашу конструктивную критику моего последнего поста. Я сделал некоторые изменения, но, увы, мой код все еще не работает, и я не могу понять, почему. Когда я запускаю эту версию, я получаю предупреждение о недопустимых ошибках, встречающихся в matmul. Мой код указан как


from __future__ import division
import numpy as np
from scipy.linalg import eig
from scipy.linalg import toeplitz


def poldif(*arg):
    """
    Calculate differentiation matrices on arbitrary nodes.
    Returns the differentiation matrices D1, D2, .. DM corresponding to the
    M-th derivative of the function f at arbitrarily specified nodes. The
    differentiation matrices can be computed with unit weights or
    with specified weights.
    Parameters
    ----------
    x       : ndarray
              vector of N distinct nodes
    M       : int
              maximum order of the derivative, 0 < M <= N - 1
    OR (when computing with specified weights)
    x       : ndarray
              vector of N distinct nodes
    alpha   : ndarray
              vector of weight values alpha(x), evaluated at x = x_j.
    B       : int
              matrix of size M x N, where M is the highest derivative required.
              It should contain the quantities B[l,j] = beta_{l,j} =
              l-th derivative of log(alpha(x)), evaluated at x = x_j.
    Returns
    -------
    DM : ndarray
         M x N x N  array of differentiation matrices
    Notes
    -----
    This function returns  M differentiation matrices corresponding to the
    1st, 2nd, ... M-th derivates on arbitrary nodes specified in the array
    x. The nodes must be distinct but are, otherwise, arbitrary. The
    matrices are constructed by differentiating N-th order Lagrange
    interpolating polynomial that passes through the speficied points.
    The M-th derivative of the grid function f is obtained by the matrix-
    vector multiplication
    .. math::
    f^{(m)}_i = D^{(m)}_{ij}f_j
    This function is based on code by Rex Fuzzle
    https://github.com/RexFuzzle/Python-Library
    References
    ----------
    ..[1] B. Fornberg, Generation of Finite Difference Formulas on Arbitrarily
    Spaced Grids, Mathematics of Computation 51, no. 184 (1988): 699-706.
    ..[2] J. A. C. Weidemann and S. C. Reddy, A MATLAB Differentiation Matrix
    Suite, ACM Transactions on Mathematical Software, 26, (2000) : 465-519
    """
    if len(arg) > 3:
        raise Exception('number of arguments is either two OR three')

    if len(arg) == 2:
        # unit weight function : arguments are nodes and derivative order
        x, M = arg[0], arg[1]
        N = np.size(x)
        # assert M<N, "Derivative order cannot be larger or equal to number of points"
        if M >= N:
            raise Exception("Derivative order cannot be larger or equal to number of points")
        alpha = np.ones(N)
        B = np.zeros((M, N))

    elif len(arg) == 3:
        # specified weight function : arguments are nodes, weights and B  matrix
        x, alpha, B = arg[0], arg[1], arg[2]
        N = np.size(x)
        M = B.shape[0]

    I = np.eye(N)  # identity matrix
    L = np.logical_or(I, np.zeros(N))  # logical identity matrix
    XX = np.transpose(np.array([x, ] * N))
    DX = XX - np.transpose(XX)  # DX contains entries x(k)-x(j)
    DX[L] = np.ones(N)  # put 1's one the main diagonal
    c = alpha * np.prod(DX, 1)  # quantities c(j)
    C = np.transpose(np.array([c, ] * N))
    C = C / np.transpose(C)  # matrix with entries c(k)/c(j).
    Z = 1 / DX  # Z contains entries 1/(x(k)-x(j)
    Z[L] = 0  # eye(N)*ZZ;                # with zeros on the diagonal.
    X = np.transpose(np.copy(Z))  # X is same as Z', but with ...
    Xnew = X

    for i in range(0, N):
        Xnew[i:N - 1, i] = X[i + 1:N, i]

    X = Xnew[0:N - 1, :]  # ... diagonal entries removed
    Y = np.ones([N - 1, N])  # initialize Y and D matrices.
    D = np.eye(N)  # Y is matrix of cumulative sums

    DM = np.empty((M, N, N))  # differentiation matrices

    for ell in range(1, M + 1):
        Y = np.cumsum(np.vstack((B[ell - 1, :], ell * (Y[0:N - 1, :]) * X)), 0)  # diags
        D = ell * Z * (C * np.transpose(np.tile(np.diag(D), (N, 1))) - D)  # off-diags
        D[L] = Y[N - 1, :]
        DM[ell - 1, :, :] = D

    return DM

def herdif(N, M, b=1):
    """
    Calculate differentiation matrices using Hermite collocation.
    Returns the differentiation matrices D1, D2, .. DM corresponding to the
    M-th derivative of the function f, at the N Chebyshev nodes in the
    interval [-1,1].
    Parameters
    ----------
    N   : int
          number of grid points
    M   : int
          maximum order of the derivative, 0 < M < N
    b   : float, optional
          scale parameter, real and positive
    Returns
    -------
    x  : ndarray
         N x 1 array of Hermite nodes which are zeros of the N-th degree
         Hermite polynomial, scaled by b
    DM : ndarray
         M x N x N  array of differentiation matrices
    Notes
    -----
    This function returns  M differentiation matrices corresponding to the
    1st, 2nd, ... M-th derivates on a Hermite grid of N points. The
    matrices are constructed by differentiating N-th order Hermite
    interpolants.
    The M-th derivative of the grid function f is obtained by the matrix-
    vector multiplication
    .. math::
    f^{(m)}_i = D^{(m)}_{ij}f_j
    References
    ----------
    ..[1] B. Fornberg, Generation of Finite Difference Formulas on Arbitrarily
    Spaced Grids, Mathematics of Computation 51, no. 184 (1988): 699-706.
    ..[2] J. A. C. Weidemann and S. C. Reddy, A MATLAB Differentiation Matrix
    Suite, ACM Transactions on Mathematical Software, 26, (2000) : 465-519
    ..[3] R. Baltensperger and M. R. Trummer, Spectral Differencing With A
    Twist, SIAM Journal on Scientific Computing 24, (2002) : 1465-1487
    """
    if M >= N - 1:
        raise Exception('number of nodes must be greater than M - 1')

    if M <= 0:
        raise Exception('derivative order must be at least 1')

    x = herroots(N)  # compute Hermite nodes
    alpha = np.exp(-x * x / 2)  # compute Hermite  weights.

    beta = np.zeros([M + 1, N])

    # construct beta(l,j) = d^l/dx^l (alpha(x)/alpha'(x))|x=x_j recursively
    beta[0, :] = np.ones(N)
    beta[1, :] = -x

    for ell in range(2, M + 1):
        beta[ell, :] = -x * beta[ell - 1, :] - (ell - 1) * beta[ell - 2, :]

    # remove initialising row from beta
    beta = np.delete(beta, 0, 0)

    # compute differentiation matrix (b=1)
    DM = poldif(x, alpha, beta)
    # scale nodes by the factor b
    x = x / b

    # scale the matrix by the factor b
    for ell in range(M):
        DM[ell, :, :] = (b ** (ell + 1)) * DM[ell, :, :]

    return x, DM

def herroots(N):
    """
    Compute roots of the Hermite polynomial of degree N
    Parameters
     ----------
    N   : int
          degree of the Hermite polynomial
    Returns
    -------
    x  : ndarray
         N x 1 array of Hermite roots
    """

    # Jacobi matrix
    d = np.sqrt(np.arange(1, N))
    J = np.diag(d, 1) + np.diag(d, -1)

    # compute eigenvalues
    mu = eig(J)[0]

    # return sorted, normalised eigenvalues
    # real part only since all roots must be real.
    return np.real(np.sort(mu) / np.sqrt(2))


a = 1-1j
b = 2+0.2j
c1 = 0.34
c2 = 0.005

alpha1 = (4*c2/a)**0.25
alpha2 = b/2*a

Nx = 220;

# hermite differentiation matrices
[x,D] = herdif(Nx, 2, np.real(alpha1))
D1 = D[0,:]
D2 = D[1,:]

# integration weights
diff = np.diff(x)
#print(len(diff))
p = np.concatenate([np.zeros(1), diff])
q = np.concatenate([diff, np.zeros(1)])
w = (p + q)/2
Q = np.diag(w)

#Discretised operator
const = c1*np.diag(np.ones(len(x)))-c2*(np.diag(x)*np.diag(x))
#print(const)
A = a*D2 - b*D1 + const

##### Timestepping

tmax = 200
tmin = 0
dt = 1
n = (tmax - tmin)/dt
tvec = np.linspace(0,tmax,n, endpoint = True)

#(len(tvec))

q = np.zeros((Nx, len(tvec)),dtype=complex)
f = np.zeros((Nx, len(tvec)),dtype=complex)

q0 = np.ones(Nx)*10**4
q[:,0] = q0
#print(q[:,0])
#print(q0)

# qnew - qold = dt*Aqold + dt*N(qold,qold,qold)
# qnew - qold = dt*Aqnew - dt*N(qold,qold,qold)
# therefore qnew - qold = 0.5*dtAqold + 0.5*dt*Aqnew + dtN(qold,qold,qold)
# rearranging to give qnew( 1- 0.5Adt) = (1 + 0.5Adt) + dt N(qold,qold,qold)

from numpy.linalg import inv

inverted = inv(np.eye(Nx)-0.5*A*dt)
forqold = (np.eye(Nx) + 0.5*A*dt)
firstterm = np.matmul(inverted,forqold)

for t in range(0, len(tvec)-1):
    nl = abs(np.square(q[:,t]))*q[:,t]
    q[:,t+1] = np.matmul(firstterm,q[:,t]) - dt*np.matmul(inverted,nl)

, где матрицы дифференцировки можно найти в Интернете и находятся в другом файле. Этот код взрывается после пяти взаимодействий, которые я не могу понять, так как не вижу, как он отличается от найденного здесь matlab https://www.bagherigroup.com/research/open-source-codes/

Я был бы очень признателен за любую помощь.

1 Ответ

0 голосов
/ 02 декабря 2019

Ошибка в:

q[:,t+1] = inverted*forgold*np.array(q[:,t]) + inverted*dt*np.array(nl)

q[:, t+1] индексирует 2d массив (вероятно, не np.matrix, который больше похож на MATLAB). Это индексирование уменьшает число измерений на 1, следовательно, форма (220,) в сообщении об ошибке.

В сообщении об ошибке говорится, что RHS равно (220 220). Эта форма, вероятно, происходит от inverted и forgold. np.array(q[:,t]) - это 1 день. Умножение (220,220) на (220,) нормально, но вы не можете поместить этот квадратный массив в 1-й слот.

Оба использования np.array в строке ошибки излишни. Их аргументы уже ndarray.

Что касается цикла, это может быть необходимо. Похоже, что q[:,t+1] является функцией от q [:, t] , a serial, rather than parallel operation. Those are harder to render as 'vectorized' (unless you can use cumsum`оподобных операций).

Обратите внимание, что в numpy * - поэлементное умножение, .* в MATLAB,np.dot и @ используются для умножения матриц.

q[:,t+1]= invert@q[:,t]

будет работать

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...