Home > Mobile >  AttributeError: 'Solver' object has no attribute 'method' at implementig class s
AttributeError: 'Solver' object has no attribute 'method' at implementig class s

Time:05-25

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:

  1. How to fix this bug?
  2. 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.

  • Related