Использование Рунге-Кутты 4-го порядка для решения дифференциального уравнения 2-го порядка затухающего осциллятора

Поэтому мне было поручено использовать метод Рунге-Кутта 4-го порядка для решения дифференциального уравнения 2-го порядка демпфирующего осциллятора.

моя функция для мяса рунге-кутта выглядит так

def RungeKutta(f,y0,x):
    y=np.zeros((len(x),len(y0)))
    y[0,:]=np.array(y0)
    h=x[1]-x[0]
    for i in range(0,len(x)-1):
        k1=h*np.array(f(y[i,:],x[i]))
        k2=h*np.array(f(y[i,:]+k1/2,x[i]+h/2))
        k3=h*np.array(f(y[i,:]+k2/2,x[i]+h/2))
        k4=h*np.array(f(y[i,:]+k3,x[i]+h))
        y[i+1,:]=y[i,:]+k1/6+k2/3+k3/3+k4/6
    return y

функция rungeKutta работает нормально, и я протестировал ее со списком примеров ввода, так что проблема не в этом.

мне дано параметры вопроса и нужно сделать класс, чтобы решить проблему

class harmonicOscillator:
    def __init__(self,m,c,k):
        if ((m>0) and ((type(m) == int) or (type(m) ==  float))):
            self.m = m 
        else:
            raise ValueError
        if ((c>0) and ((type(c) == int) or (type(c) ==  float))):
            self.c = c
        else:
            raise ValueError
        if ((k>0) and ((type(k) == int) or (type(k) ==  float))):
            self.k = k
        else:
            raise ValueError
    
    def period(self):
        self.T = 2 * np.pi * (self.m / self.k)**(0.5)
        return(self.T)

    def solve(self, func, y0):
        m = self.m
        c = self.c
        k = self.k
        T = self.T
        t = np.linspace(0,10*T,1000)

но я не уверен, где действительно прогрессировать. я пытался превратить дифференциальное уравнение 2-го порядка в лямбда-функцию, подобную такой

F = lambda X,t: [X[1], (-c) * X[1] + (-k) * X[0] + func(t)]

а затем передать это в мою функцию RungeKutta

result = RungeKutta(F, y0, t, func)
return(result)

но я не очень хорошо разбираюсь в лямбда-функциях и явно где-то ошибаюсь.

пример ввода, который он должен передать, будет примерно таким

####### example inputs #######
m=1
c=0.5
k=2
a = harmonicOscillator(m,c,k)
a.period()
x0 = [0,0]
tHO,xHO= a.solve(lambda t: omega0**2,x0)

был бы очень признателен за помощь. требования к вопросам заключаются в том, что я должен использовать вышеупомянутую функцию rungeKutta, но я просто потерялся в этот момент Благодарю.

Пара замечаний/вопросов: 1) имена классов обычно записываются в CamelCase в Python, поэтому ваш класс должен быть HarmonicOscillator, 2) почему period метод? Почему бы не рассчитать период один раз на основе фиксированных параметров m и k и задать его в конструкторе? 3) Вы можете выполнять проверки членства типов: if (type(m) in (int, float)) and (m > 0) чтобы немного очистить ваш конструктор.

ddejohn 18.03.2022 03:41

Что касается ответа на ваш вопрос... какой именно является ваш вопрос? Когда вы говорите "явно что-то идет не так", что вы имеете в виду? Вы получаете сообщение об ошибке? Если да, опубликуйте трассировку (полное сообщение об ошибке). Вы получаете неправильные результаты? Если да, покажите, как они отличаются от ваших ожидаемых результатов. Также укажите минимальный воспроизводимый пример.

ddejohn 18.03.2022 03:50

я пытаюсь создать массив с диапазоном значений, чтобы я мог построить уравнение для демпфированного осциллятора, и я думаю, что это минимальный воспроизводимый пример, я полагаю, что есть проблема с лямбда-функцией, однако я предоставил минимальный количество другого связанного кода на случай, если проблема заключается в нем. Период — это метод, потому что задание, которое мне дали для решения проблемы, требовало этого.

peter 18.03.2022 03:58

Был ли ответ на ваш вопрос? Если да, отметьте его как принятый, чтобы ваш вопрос можно было удалить из очереди без ответа.

ddejohn 19.03.2022 04:19
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
4
94
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я думаю, что может возникнуть некоторая путаница с термином внешнего воздействия и производной вспомогательной функцией Рунге-Кутты F. F в RK4 возвращает производную dX/dt системы дифференциальных уравнений первого порядка X. Вынуждающий член в демпфированном осцилляторе, к сожалению, также называется F, но это функция t.

Одна из ваших проблем заключается в том, что арность (количество параметров) вашей функции RungeKutta() и ваш вызов этой функции не совпадают: вы пытались сделать RungeKutta(F, y0, t, func), но функция RungeKutta() принимает аргументы (f, y0, x) только в таком порядке.

Другими словами, параметр f в вашей текущей RungeKutta() функции должен инкапсулировать принудительную функцию F(t).

Вы можете сделать это с помощниками:

# A constant function in your case, but this can be any function of `t`
def applied_force(t):
    # Note, you did not provide a value for `omega0`
    return omega0 ** 2

def rk_derivative_factory(c, k, F):
    return lambda X, t: np.array([X[1], -c * X[1] - k * X[0] + F(t)])

rk_derivative_factory() — это функция, которая принимает коэффициент демпфирования, жесткость пружины и функцию принуждения F(t), а возвращает функцию принимает в качестве аргументов систему X и временной шаг t (поскольку это то, что требуется от вас реализацией RungeKutta()) .

Затем,

omega0 = 0.234
m, c, k = 1, 0.25, 2
oscillator = HarmonicOscillator(m, c, k)

f = rk_derivative_factory(oscillator, applied_force)
x_osc = oscillator.solve(f, [1, 0])

Где solve() определяется так:

def solve(self, func, y0):
    t = np.linspace(0,10 * self.period(), 1000)
    return RungeKutta(f, y0, t)

Кроме того, я настоятельно рекомендую быть более последовательным в именах переменных. Вы назвали начальное состояние вашего осциллятора x0 и передавали его RungeKutta() в качестве аргумента для параметра y0, а параметр xRungeKutta() представляет время... Довольно запутанно.

Полное решение

Наконец, ваша реализация RK4 была не совсем правильной, поэтому я исправил это и сделал несколько других небольших улучшений.

Обратите внимание, что одна вещь, которую вы, возможно, захотите рассмотреть, — заставить функцию HarmonicOscillator.solve() принимать решатель. Затем вы можете поиграть с разными интеграторами.

import numpy as np


def RungeKutta(f, y0, x):
    y = np.zeros((len(x), len(y0)))
    y[0, :] = np.array(y0)
    h = x[1] - x[0]
    for i in range(0, len(x) - 1):
        # Many slight changes below
        k1 = np.array(f(y[i, :], x[i]))
        k2 = np.array(f(y[i, :] + h * k1 / 2, x[i] + h / 2))
        k3 = np.array(f(y[i, :] + h * k2 / 2, x[i] + h / 2))
        k4 = np.array(f(y[i, :] + h * k3, x[i] + h))
        y[i + 1, :] = y[i, :] + (h / 6) * (k1 + 2 * k2 + 2 * k3 + k4)
    return y


# A constant function in your case, but this can be any function of `t`
def applied_force(t):
    # Note, you did not provide a value for `omega0`
    return omega0 ** 2


def rk_derivative_factory(osc, F):
    return lambda X, t: np.array([X[1], (F(t) - osc.c * X[1] - osc.k * X[0]) / osc.m])


class HarmonicOscillator:
    def __init__(self, m, c, k):
        if (type(m) in (int, float)) and (m > 0):
            self.m = m 
        else:
            raise ValueError("Parameter 'm' must be a positive number")
        if (type(c) in (int, float)) and (c > 0):
            self.c = c
        else:
            raise ValueError("Parameter 'c' must be a positive number")
        if (type(k) in (int, float)) and (k > 0):
            self.k = k 
        else:
            raise ValueError("Parameter 'k' must be a positive number")
        
        self.T = 2 * np.pi * (self.m / self.k)**(0.5)

    def period(self):
        return self.T

    def solve(self, func, y0):
        t = np.linspace(0, 10 * self.period(), 1000)
        return RungeKutta(func, y0, t)

Демо:

import plotly.graph_objects as go

omega0 = 0.234
m, c, k = 1, 0.25, 2
oscillator = HarmonicOscillator(m, c, k)

f = rk_derivative_factory(oscillator, applied_force)
x_osc = oscillator.solve(f, [1, 0])

x, dx = x_osc.T
t = np.linspace(0, 10 * oscillator.period(), 1000)

fig = go.Figure(go.Scatter(x=t, y=x, name = "x(t)"))
fig.add_trace(go.Scatter(x=t, y=dx, name = "x'(t)"))

Выход:

Runge Kutta demo

Другие вопросы по теме