Поэтому мне было поручено использовать метод Рунге-Кутта 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, но я просто потерялся в этот момент Благодарю.
Что касается ответа на ваш вопрос... какой именно является ваш вопрос? Когда вы говорите "явно что-то идет не так", что вы имеете в виду? Вы получаете сообщение об ошибке? Если да, опубликуйте трассировку (полное сообщение об ошибке). Вы получаете неправильные результаты? Если да, покажите, как они отличаются от ваших ожидаемых результатов. Также укажите минимальный воспроизводимый пример.
я пытаюсь создать массив с диапазоном значений, чтобы я мог построить уравнение для демпфированного осциллятора, и я думаю, что это минимальный воспроизводимый пример, я полагаю, что есть проблема с лямбда-функцией, однако я предоставил минимальный количество другого связанного кода на случай, если проблема заключается в нем. Период — это метод, потому что задание, которое мне дали для решения проблемы, требовало этого.
Был ли ответ на ваш вопрос? Если да, отметьте его как принятый, чтобы ваш вопрос можно было удалить из очереди без ответа.
Я думаю, что может возникнуть некоторая путаница с термином внешнего воздействия и производной вспомогательной функцией Рунге-Кутты 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
, а параметр x
RungeKutta()
представляет время... Довольно запутанно.
Наконец, ваша реализация 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)"))
Выход:
Пара замечаний/вопросов: 1) имена классов обычно записываются в CamelCase в Python, поэтому ваш класс должен быть
HarmonicOscillator
, 2) почемуperiod
метод? Почему бы не рассчитать период один раз на основе фиксированных параметровm
иk
и задать его в конструкторе? 3) Вы можете выполнять проверки членства типов:if (type(m) in (int, float)) and (m > 0)
чтобы немного очистить ваш конструктор.