import numpy as np class DiferansiyelDenklemCozucu: """ Skaler ve vektorel yapidaki adi diferansiyel denklemleri numerik yontemlerle cozmek icin yazilmis ust sinif du/dt = f(u, t) Oznitelikler: t: zaman degerleri dizisi u: t ani icin cozum degerlerinin dizisi k: en sondaki cozumun adim sayisi f: f(u, t) 'yu uyarlamak uzere cagrilabilir bir nesne """ def __init__(self, f): # Eger fonksiyona iliskin bilgi dizi seklinde gelmediyse # diziye donustur self.f = lambda u, t: np.asarray(f(u, t), float) def ileri(self): """Cozumu bir zaman basamagi ilerletir""" raise NotImplementedError def baslangic_kosulu(self, U0): if isinstance(U0, (float, int)): # denklem skaler self.neq = 1 U0 = float(U0) else: # denklem sistemi U0 = np.asarray(U0) self.neq = U0.size self.U0 = U0 # f dogru uzunlugu donduruyor mu kontrel et try: f0 = self.f(self.U0, 0) except IndexError: raise IndexError("f(u,t) fonksiyonunda endeks hatasi. Gecerli endeks degerleri %s" % ( str(range(self.neq)))) if f0.size != self.neq: raise ValueError( "f(u,t) %d bilesen donduruyor, ancak u'nun %d bileseni var" % (f0.size, self.neq)) def cozum(self, zaman_noktalari, donguyudurdur=None): """ donguyudurdur(u, t, adim) False urettigi ve oldugu surece cozumu tum zaman noktalarina bir dongu dahlinde uygula. donguyudurdur(u, t, adim) True ya da False donduren kullanici tanimli bir fonksiyondur. Varsayilan degeri False 'tur. """ if donguyudurdur is None: def donguyudurdur(u, t, adim): return False if isinstance(zaman_noktalari, (float, int)): raise TypeError( "cozum: zaman_noktalari bir dizi seklinde verilmemis") if zaman_noktalari.size <= 1: raise ValueError("DiferansiyelDenklemCozucu.cozum cozum icin zaman_noktalari dizisine ihtiyac duyar!") self.t = np.asarray(zaman_noktalari) n = self.t.size if self.neq == 1: # skaler denklem self.u = np.zeros(n) else: # denklem sistemi self.u = np.zeros((n,self.neq)) # self.t[0] self.U0 karsilik gelmek uzere self.u[0] = self.U0 # Dongu for k in range(n-1): self.k = k self.u[k+1] = self.ileri() if donguyudurdur(self.u, self.t, self.k+1): break # donguden cik return self.u[:k+2], self.t[:k+2] class IleriEuler(DiferansiyelDenklemCozucu): def ileri(self): u, f, k, t = self.u, self.f, self.k, self.t dt = t[k+1] - t[k] uyeni = u[k] + dt*f(u[k], t[k]) return uyeni class RungeKutta4(DiferansiyelDenklemCozucu): def ileri(self): u, f, k, t = self.u, self.f, self.k, self.t dt = t[k+1] - t[k] dt2 = dt/2.0 K1 = dt*f(u[k], t[k]) K2 = dt*f(u[k] + 0.5*K1, t[k] + dt2) K3 = dt*f(u[k] + 0.5*K2, t[k] + dt2) K4 = dt*f(u[k] + K3, t[k] + dt) uyeni = u[k] + (1/6.0)*(K1 + 2*K2 + 2*K3 + K4) return uyeni # Geri Euler Yontemi biraz daha komplike bir yontemedir ve Newton yontemine # de ihtiyac duyulur. Newton yontemi sinif_diferansiyeldenklem_Newton.py # icinde kodlanmis durumdadir try: from sinif_diferansiyeldenklem_Newton import Newton except ImportError: pass class GeriEuler(DiferansiyelDenklemCozucu): """Skaler denklemler icin cozum""" def __init__(self, f): DiferansiyelDenklemCozucu.__init__(self, f) # f fonksiyonunun skaler olup olmadigini check et. try: u = np.array([1]); t = 1 deger = f(u, t) except IndexError: # indeks hatas raise ValueError('f(u,t) float ya da int dondurmeli') def ileri(self): u, f, k, t = self.u, self.f, self.k, self.t dt = t[k+1] - t[k] def F(w): return w - dt*f(w, t[k+1]) - u[k] dFdw = Turev(F) w_baslangic = u[k] + dt*f(u[k], t[k]) # Ileri Euler adimi u_yeni, n, F_degeri = Newton(F, w_baslangic, dFdw, N=30) if k == 0: self.Newton_iter = [] self.Newton_iter.append(n) if n >= 30: print("Newton t=%g noktasinda "\ "(%d iterasyon) sonunda yakinsamadi" % (t[k+1], n)) return u_yeni # Bu basit kod yerine daha komplike olan ve pek cok yontem iceren # siniftan tercih ettiginiz yontemi de import edebilirsiniz! class Turev: def __init__(self, f, h=1E-9): self.f = f self.h = float(h) def __call__(self, x): f, h = self.f, self.h return (f(x+h) - f(x-h))/(2*h) # Testler ve Dogrulama def _f1(u, t): return 0.2 + (u - _u_cozum_f1(t))**5 def _u_cozum_f1(t): """Verilen _f1 durumu icin integralin tam degeri""" return 0.2*t + 3 def _dogrulama(f, tam, U0, T, n): t_noktalari = np.linspace(0, T, n) for cozum_yontemi in IleriEuler, RungeKutta4, GeriEuler: try: yontem = cozum_yontemi(f) except: continue yontem.baslangic_kosulu(U0) u, t = yontem.cozum(t_noktalari) print(cozum_yontemi.__name__,) u_tam = np.asarray(tam(t)).transpose() print('maksimum hata', (u_tam - u).max()) if cozum_yontemi is GeriEuler: print('Geri Euler Yontemi Iterasyonlari:', yontem.Newton_iter) if __name__ == '__main__': print('Numerik cozumler:') _dogrulama(_f1, _u_cozum_f1, U0=3, T=8, n=4) print('\nSalinim gosteren sistem:') _dogrulama(f=lambda u, t: [u[1], -u[0]], tam=lambda t: [np.sin(t), np.cos(t)], U0=[0,1], T=4*np.pi, n=20*4)