1

I try to implement ODE Solver contain deifferent numerical schemes for solve ODE 5-th order:

enter image description here

For example consider the Forward Euler scheme code implementation:

# ES.py

from abc import ABC
from ODS import ODE_Solver


class FE(ODE_Solver, ABC):
    """
    Attribute:
    x: array of x coords
    u: solution array y(x)
    k: number of steps
    f: right hand side ODE equation: du/dx = f(u, x)
    """


    @classmethod
    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

This method code by class and contain one method and based on superclass ODE_Solver:

#ODS.py

import numpy as np


class ODE_Solver(object):
    """
    Supercalss sover ODE-s 
    Attribute:
    x: array of x coords
    u: solution array y(x)
    k: number of steps
    f: right hand side ODE equation: du/dx = f(u, x)
    """


    def __init__(self, f):
        if not callable(f):
            # check correct function f(u, x)
            raise TypeError('f is %s, not function' % type(f))
        self.f = lambda u, x: np.asarray(f(u, x), float)

    def solver_st(self):
        """Implement numerical scheme"""
        raise NotImplementedError

    def set_initial_condition(self, u0):
        if isinstance(u0, (float, int)):  # ODE 1-th order
            self.neq = 1
            u0 = float(u0)
        else:  # ODE high order or system of ODE-s
            u0 = np.asarray(u0)  # (initial conds)
            self.neq = u0.size
        self.u0 = u0

        # check correct lenght of vector f
        try:
            f0 = self.f(self.u0, 0)
        except IndexError:
            raise IndexError(
                'index out of bounds f(u,x). right index %s' % (str(range(self.neq))))
        if f0.size != self.neq:
            raise ValueError('f(u,x) return %d elems,  u has %d elems' % (f0.size, self.neq))

    def solve(self, coord_points, terminate=None):
        """
        Solve equations. Default False 
        """
        if terminate is None:
            terminate = lambda u, x, step_no: False

        if isinstance(coord_points, (float, int)):
            raise TypeError('solve: array lists not iterable')
        self.x = np.asarray(coord_points)
        if self.x.size <= 1:
            raise ValueError('ODESolver.solve requre coords x array')

        n = self.x.size
        if self.neq == 1:  # ODE
            self.u = np.zeros(n)
        else:  
            self.u = np.zeros((n, self.neq))

        # Assume self.x[0] corresponds to self.u0
        self.u[0] = self.u0

        # looping for x coords
        for k in range(n - 1):
            self.k = k
            self.u[k + 1] = self.solver_st()
            if terminate(self.u, self.x, self.k + 1):
                break  
        return self.u[:k + 2], self.x[:k + 2]

And now I try to testing my code:


# test.py

import matplotlib.pyplot as plt
import numpy as np
from ODS import ODE_Solver


def f(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])


y0 = [0, 3, -9, -8, 0]
solver = FE(f)
solver.set_initial_condition(y0)
x_points = np.linspace(0, 5, 150)
u, x = solver.solve(x_points)
plt.plot(x, u)

So I consider the reccomendation in commentary I succes to call solver methods, but I get type error:

  File "C:\Fin_Proj_ODE\test1.py", line 19, in <module>
    u, x = solver.solve(x_points)
  File "C:\Fin_Proj_ODE\ODS.py", line 71, in solve
    self.u[k + 1] = self.solver_st()
  File "C:\Fin_Proj_ODE\ES.py", line 18, in solver_st
    u, f, k, x = self.u, self.f, self.k, self.x
AttributeError: type object 'FE' has no attribute 'u'

This post not help me to decide this troubleshooting

And I have 2 questions:

1) How to fix this error of call methods of required class? Any idea?

  1. How to rewrite class for calling methods in test file from EF class?

ImportError: cannot import name 'ODE_Solver' - this bug related with wrong call methods of class EF:

solver = ODE_Solver.solver_st(f)
solver.set_initial_condition(y0)
x_points = np.linspace(0, 5, 150)
u, x = solver.solve(x_points)
Alex
  • 195
  • 10
  • Do not import FE, leave the stepper method unimplemented in the basis class. Or make the basis class the Euler method, without a separate class for it. – Lutz Lehmann May 20 '22 at 13:24
  • @LutzLehmann In this case, if I leave the stepper method unimplemented, and try to call this function `solver = ODE_Solver.solver_st(f)` logically I get the raise exception `raise NotImplementedError NotImplementedError` – Alex May 20 '22 at 13:44
  • 1
    Of course you would then not take the unspecialized basis class for the solver, but the derived class that actually implements the method, `solver = FE(f)` and then use `solver.solve` to get the numerical approximation. – Lutz Lehmann May 20 '22 at 14:51
  • @LutzLehmann Thanks a lot for advice, though this implement not working, probably I should implement methods at one file... – Alex May 20 '22 at 15:04
  • The line in the error message does not correspond to a code line. Indeed at the time of instance initialization these attributes/fields are not declared. – Lutz Lehmann May 20 '22 at 17:46
  • @LutzLehmann Sorry, edit traceback output. This attributes declared in ODE_Solver. My class for numerical methods inherits all fileds, beside solve_st - this method is unique for every numerical scheme, so I don't understand why this call raise error... For example, at another impleemnation, similar with this [link](https://github.com/Alm99-collab/scipro-primer/blob/master/src/ode2/ODESolver.py) work... But I want to add some scheme and create a few classes... – Alex May 20 '22 at 18:11

1 Answers1

1

For fixing 2-nd problem should remove @classmethod in numerical scheme class implementation:

class FE(ODE_Solver, ABC):

    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
Alex
  • 195
  • 10