-   
- # Author: Alm99-collab 
- # Date: 25/10/2021 2:00 
- # Description: MSUT STANKIN Μyagkov Alexandr IDM - 21 - 03 
-   
-   
- """ 
- Программа для численного решения волнового уравнения в одномерном случае, 
- при разлинчых начальных и граничных условий, с помощью явной разностной 
- схемы типа "крест". 
-   
- Общий вид волнового уравнения: 
- u_tt = a**2*u_xx + f(x,t) (0,L) где u=0 на диапазоне x=0,L, for t in (0,T]. 
- Начальные условия в общем случае: u=I(x), u_t=V(x). 
- В случае неоднородного уравнения задается функция f(x,t). 
-   
- Синтакис основной функции решателя: 
- u, x, t = solver(I, V, f, a, L, dt, C, T, user_func) где: 
- I = I(x) - функция. 
- V = V(x) - функция. 
- f = f(x,t) - функция. 
- U_0, U_L, - условия на границе. 
- C - число Куранта (=a*dt/dx), зависящее от шага dx. Является криетрием 
- стабильности численных расчето, если соблюдено условие (<=1) 
- dt - шаг по времени. 
- dx - шаг по координате. 
- T - конечное время симуляции волнового процесса. 
- user_func - функция (u, x, t, n) для вызова пользовательских сценариев, 
- таких как анимация или вывод графика, запси данных в текстовый файл, 
- расчет ошибки (в случае если известно точное решение) и.т.д. 
- """ 
-   
- import numpy as np 
- import math 
- import matplotlib.pyplot as plt 
- import os 
- import time 
- import glob 
-   
-   
- # функция решатель для данного дифференциального уравнения 
- def solver(I, V, f, a, U_0, U_L, L, dt, C, T, 
-            user_func = None): 
-     """ 
-     Функция решения волнового уравнения u_tt = a**2*u_xx + f(x,t) (0,L), 
-     где u=0 на диапазоне x=0,L, for t in (0,T]. 
-     Начальные условия в общем случае: u=I(x), u_t=V(x). 
-     В случае неоднородного уравнения задается функция f(x,t). 
-     ------------------------------------------------------------------------ 
-     :param I: 
-     :param V: 
-     :param f: 
-     :param a: 
-     :param L: 
-     :param C: 
-     :param T: 
-     :param U_0: 
-     :param U_L: 
-     :param dt: 
-     :param user_func: 
-     :return: 
-     """ 
-   
-     nt = int(round(T / dt)) 
-     t = np.linspace(0, nt * dt, nt + 1)  # Узлы сетки по времени 
-     dx = dt * a / float(C) 
-     nx = int(round(L / dx)) 
-     x = np.linspace(0, L, nx + 1)  # Узлы сетки по координате 
-     C2 = C ** 2 
-     dt2 = dt * dt 
-   
-     # Проверка того, что  массивы являются элементами t,x 
-     dx = x[1] - x[0] 
-     dt = t[1] - t[0] 
-   
-     # Выбор и инициализация дополнительных параметров f, I, V, U_0, U_L если равны нулю 
-     # или не передаются 
-     if f is None or f == 0: 
-         f = lambda x, t: 0 
-     if I is None or I == 0: 
-         I = lambda x: 0 
-     if V is None or V == 0: 
-         V = lambda x: 0 
-     if U_0 is not None: 
-         if isinstance(U_0, (float, int)) and U_0 == 0: 
-             U_0 = lambda t: 0 
-         # иначе: U_0(t) является функцией 
-     if U_L is not None: 
-         if isinstance(U_L, (float, int)) and U_L == 0: 
-             U_L = lambda t: 0 
-         # иначе: U_L(t) является функцией 
-   
-     # ---  Выделяем память под значения решений  --- 
-     u = np.zeros(nx + 1)  # Массив решений в узлах сетки на  временном шаге u(i,n+1) 
-     u_n = np.zeros(nx + 1)  # Массив решений в узлах сетки на  временном шаге u(i,n) 
-     u_nm1 = np.zeros(nx + 1)  # Массив решений в узлах сетки на  временном шаге u(i,n-1) 
-   
-     # --- Проверка индексов для соблюдения размерностей массивов --- 
-     Ix = range(0, nx + 1) 
-     It = range(0, nt + 1) 
-   
-     # --- Запись начальных условий --- 
-     for i in Ix: 
-         u_n[i] = I(x[i]) 
-   
-     if user_func is not None: 
-         user_func(u_n, x, t, 0) 
-   
-     # --- Разностная формулма явной схемы "типа крест" на первом шаге --- 
-     for i in Ix[1:-1]: 
-         u[i] = u_n[i] + dt * V(x[i]) + 0.5 * C2 * (u_n[i - 1] - 2 * u_n[i] + u_n[i + 1]) + 0.5 * dt2 * f(x[i], t[0]) 
-   
-     i = Ix[0] 
-     if U_0 is None: 
-         # Запись граничных условий (x=0: i-1 -> i+1  u[i-1]=u[i+1] 
-         # где du/dn = 0, on x=L: i+1 -> i-1  u[i+1]=u[i-1]) 
-         ip1 = i + 1 
-         im1 = ip1  # i-1 -> i+1 
-         u[i] = u_n[i] + dt * V(x[i]) + 0.5 * C2 * (u_n[im1] - 2 * u_n[i] + u_n[ip1]) + 0.5 * dt2 * f(x[i], t[0]) 
-   
-     else: 
-         u[0] = U_0(dt) 
-   
-     i = Ix[-1] 
-     if U_L is None: 
-         im1 = i - 1 
-         ip1 = im1  # i+1 -> i-1 
-         u[i] = u_n[i] + dt * V(x[i]) + 0.5 * C2 * (u_n[im1] - 2 * u_n[i] + u_n[ip1]) + 0.5 * dt2 * f(x[i], t[0]) 
-     else: 
-         u[i] = U_L(dt) 
-   
-     if user_func is not None: 
-         user_func(u_n, x, t, 1) 
-   
-     # Обновление данных и подготовка к новому шагу 
-     u_nm1, u_n, u = u_n, u, u_nm1 
-   
-     # --- Симуляция (цикл прохода по времени) --- 
-     for n in It[1:-1]: 
-         # Обновление значений во внутренних узлах сетки 
-         for i in Ix[1:-1]: 
-             u[i] = - u_nm1[i] + 2 * u_n[i] + C2 * (u_n[i - 1] - 2 * u_n[i] + u_n[i + 1]) + dt2 * f(x[i], t[n]) 
-   
-         #  --- Запись граничных условий --- 
-         i = Ix[0] 
-         if U_0 is None: 
-             # Установка значений граничных условий 
-             # x=0: i-1 -> i+1  u[i-1]=u[i+1] где du/dn=0 
-             # x=L: i+1 -> i-1  u[i+1]=u[i-1] где du/dn=0 
-             ip1 = i + 1 
-             im1 = ip1 
-             u[i] = - u_nm1[i] + 2 * u_n[i] + C2 * (u_n[im1] - 2 * u_n[i] + u_n[ip1]) + dt2 * f(x[i], t[n]) 
-         else: 
-             u[0] = U_0(t[n + 1]) 
-   
-         i = Ix[-1] 
-         if U_L is None: 
-             im1 = i - 1 
-             ip1 = im1 
-             u[i] = - u_nm1[i] + 2 * u_n[i] + C2 * (u_n[im1] - 2 * u_n[i] + u_n[ip1]) + dt2 * f(x[i], t[n]) 
-         else: 
-             u[i] = U_L(t[n + 1]) 
-   
-         if user_func is not None: 
-             if user_func(u, x, t, n + 1): 
-                 break 
-   
-         # Обновление данных и подготовка к новому шагу 
-         u_nm1, u_n, u = u_n, u, u_nm1 
-   
-     # Присвоение значений требуемому узлу после прохода по сетке 
-     u = u_n 
-   
-     return u, x, t 
-   
-   
- # функция симуляции и анимации, сохранения данных в файл 
- def simulate(I, V, f, a, U_0, U_L, L, dt, C, T, umin, umax, animate = True): 
-   
-     """ 
-     Запуск решателя и анимации,сохранения данных в файл 
-     ---------------------------------------------------------------------------- 
-     :param I: 
-     :param V: 
-     :param f: 
-     :param a: 
-     :param L: 
-     :param dt: 
-     :param C: 
-     :param T: 
-     :param umin: 
-     :param umax: 
-     :param U_0: 
-     :param U_L: 
-     :param animate: 
-     :param mode:      # выбор режима работы (анимация и визуализация или запись данных в файл) 
-     :return: 
-     """ 
-   
-     if callable(U_0): 
-         bc_left = 'u(0,t)=U_0(t)' 
-     elif U_0 is None: 
-         bc_left = 'du(0,t)/dx=0' 
-     else: 
-         bc_left = 'u(0,t)=0' 
-     if callable(U_L): 
-         bc_right = 'u(L,t)=U_L(t)' 
-     elif U_L is None: 
-         bc_right = 'du(L,t)/dx=0' 
-     else: 
-         bc_right = 'u(L,t)=0' 
-   
-     class PlotMatplotlib: 
-         def __call__(self, u, x, t, n): 
-   
-             """ user_func для визуализации """ 
-   
-             if n == 0: 
-                 plt.ion() 
-                 self.lines = plt.plot(x, u, 'r-') 
-                 plt.xlabel('x') 
-                 plt.ylabel('u') 
-                 plt.axis([0, L, umin, umax]) 
-                 plt.legend(['t=%f' % t[n]], loc = 'lower left') 
-             else: 
-                 self.lines[0].set_ydata(u) 
-                 plt.legend(['t=%f' % t[n]], loc = 'lower left') 
-                 plt.draw() 
-                 plt.grid() 
-             time.sleep(1) if t[n] == 0 else time.sleep(0.5) 
-             plt.savefig('tmp_%04d.png' % n)  # для создания анимации из кадров 
-   
-     plot_u = PlotMatplotlib() 
-   
-     # Очистка фреймов для создания анимированного изображения 
-     for filename in glob.glob('frame_*.png'): 
-         os.remove(filename) 
-   
-     fps = 4  # frames per second 
-     codec2ext = dict(flv = 'flv', libx264 = 'mp4', libvpx = 'webm', 
-                      libtheora = 'ogg')  # video formats 
-     filespec = 'tmp_%04d.png' 
-     movie_program = 'ffmpeg'  # or 'avconv' 
-     for codec in codec2ext: 
-         ext = codec2ext[codec] 
-         cmd = '%(movie_program)s -r %(fps)d -i %(filespec)s ' \ 
-               '-vcodec %(codec)s movie.%(ext)s' % vars() 
-         os.system(cmd) 
-   
-     user_func = plot_u if animate else None 
-     u, x, t = solver(I, V, f, a, U_0, U_L, L, dt, C, T, user_func) 
-   
-     return u, x, t 
-   
-   
- # функция постановки задачи 
- def problem(): 
-     I = lambda x: 0.2 * (1 - x) * math.sin(math.pi * x) 
-     V = lambda x: 0 
-     f = lambda x, t: 0 
-     U_0 = lambda t: 0 
-     U_L = 0 
-     L = 1 
-     a = 1 
-     C = 0.75 
-     nx = 12 
-     dt = C * (L / nx) / a 
-     T = 1 
-     umin = -0.25 
-     umax = 0.25 
-   
-     u, x, t = simulate(I, V, f, a, U_0, U_L, L, dt, C, T, umin, umax, animate=True) 
-   
-     return u, x, t 
-   
- if __name__ == '__main__': 
-     u, x, t = problem() 
-     print(u,x,t) 
-