Now, in addition from my two previous posts ODE implements I try to refactro my code and fix some problems. And I decided, that logically create such classes: Solver
,Problem
.
So code for ODE_Solver and FE classes finally code and working.
# ODS.py
import numpy as np
class ODE_Solver(object):
def __init__(self, f):
if not callable(f):
raise TypeError('f is not %s, function' % type(f))
self.f = lambda u, x: np.asarray(f(u, x), float)
self.err_sch = None
def solver_st(self):
raise NotImplementedError
def err_st(self):
raise NotImplementedError
def set_initial_condition(self, u0):
if isinstance(u0, (float, int)):
self.neq = 1
u0 = float(u0)
else:
u0 = np.asarray(u0)
self.neq = u0.size
self.u0 = u0
try:
f0 = self.f(self.u0, 0)
except IndexError:
raise IndexError(
'index out of bounds f(u,x). correct index %s' % (str(range(self.neq))))
if f0.size != self.neq:
raise ValueError('f(u,x) returend %d elems, vector u has %d elems' % (f0.size, self.neq))
def solve(self, coord_points, terminate=None):
if terminate is None:
terminate = lambda u, x, step_no: False
if isinstance(coord_points, (float, int)):
raise TypeError('solve: x points not numpy array or numbers.')
self.x = np.asarray(coord_points)
if self.x.size <= 1:
raise ValueError('ODESolver.solve points of coords less than two')
n = self.x.size
if self.neq == 1: # ОДУ
self.u = np.zeros(n)
self.err_sch = np.zeros(n)
else:
self.u = np.zeros((n, self.neq))
self.err_sch = np.zeros((n, self.neq))
self.u[0] = self.u0
self.err_sch[0] = 0
for k in range(n - 1):
self.k = k
self.u[k 1] = self.solver_st()
self.err_sch[k 1] = self.err_st()
if terminate(self.u, self.x, self.k 1):
break
return self.u[:k 2], self.x[:k 2]
# ES.py
from ODS import ODE_Solver
import numpy as np
class FE(ODE_Solver):
def solver_st(self):
u, f, k, x = self.u, self.f, self.k, self.x
dx = x[k 1] - x[k]
u_new = u[k] dx * f(u[k], x[k])
return u_new
def err_st(self):
u, f, k, x, err_sch = self.u, self.f, self.k, self.x, self.err_sch
dx = x[k 1] - x[k]
err_sch = np.max(dx)**2
return err_sch
I try to implement class Problem
(return ODE and get initial conditions)
import numpy as np
class Problem(object):
def __init__(self, u0, End):
self.u0 = np.asarray(u0)
self.End = End # end point of coords
def __call__(self, u, x):
return (u[1], u[2], u[3], u[4],
- 15 * u[4] - 90 * u[3] - 270 * u[2] - 405 * u[1] - 243 * u[0])
And code class Solver
for call numerical scheme, plotting the final result, plot and evaluate error:
import numpy as np
import matplotlib as plt
import ES
import ODS
from ADS import ABM4
from ES import FE
from MLNS import MLN
from RKS import RK4
class Solver(object):
def __init__(self, problem, dx,
method=ES.FE): # choose FE scheme for tetsting
"""
"""
self.problem, self.dx = problem, dx
self.solver = method
@staticmethod
def choose_sch(type):
if type == 1:
method = FE
return method
elif type == 2:
method = RK4
return method
elif type == 3:
method = ABM4
return method
elif type == 4:
method = MLN
return method
else:
raise ValueError('not choose numerical scheme!')
def dsolve(self):
solver = self.method(self.problem)
solver.set_initial_condition(self.problem.u0)
n = int(round(self.problem.End / self.dx))
x_points = np.linspace(0, self.problem.End, n 1)
self.u, self.x = solver.solve(x_points)
if solver.k 1 == n:
self.plot()
raise ValueError('not converge this scheme,' % self.problem.End)
def plot(self):
plt.plot(self.x, self.u)
plt.show()
Now, when I call this Solver
and Problem
import numpy as np
from ODE_Problem import Problem
from SLV_Prob import Solver
def test():
problem = Problem(u0=[0, 3, -9, -8, 0], End=5)
solver = Solver(problem, dx=0.1)
solver.dsolve()
solver.plot()
if __name__ == '__main__':
test()
I get the error:
Traceback (most recent call last):
File "C:\Fin_Proj_ODE\test2.py", line 14, in <module>
test()
File "C:\Fin_Proj_ODE\test2.py", line 9, in test
solver.dsolve()
File "C:\Fin_Proj_ODE\SLV_Prob.py", line 37, in dsolve
solver = self.method(self.problem)
AttributeError: 'Solver' object has no attribute 'method'
And I dont' understand and suppose what reason of this bug...
So, I have 2 questions for implement this Solver:
- How to fix this bug?
- How to correct rewrite def choose_sch(type):, that I could to call solver and send args type ( and depending on it, a specific numerical scheme will already be started)?
CodePudding user response:
Question One:
Well, as the error states, your Solver class doesn't have an attribute called "method". Your attribute is actually "solver", so instead of calling
self.method(self.problem)
Try
self.solver(self.problem)
Question Two:
If I'm understanding you correctly, you want to know how you can call the choose_sch method from within the solver constructor and take in a type instead of a method directly. For that, simply do this:
class Solver(object):
def __init__(self, problem, dx, solver_type=1): # choose FE scheme for tetsting
"""
"""
self.problem, self.dx = problem, dx
self.solver = self._choose_sch(solver_type)
@staticmethod
def _choose_sch(solver_type):
methods = {1: FE, 2: RK4, 3: ABM4, 4: MLN}
if solver_type in methods:
return methods[solver_type]
else:
raise ValueError('not choose numerical scheme!')
The dictionary here is much better than the if statement for these kinds of tasks.
You can also alternatively not make _choose_ach a staticmethod if you don't need to call it from a static context and just make it set the solver directly.