Как внести изменения в числовое решение для ODE после его решения? - PullRequest
1 голос
/ 05 июня 2019

У меня есть два ODE второго порядка, из которых я хочу вычислить ошибку между ними, используя (с Maple)

|| B(t) - A(w*t) || = sqrt(int(B(t) - A(w*t) )^2), t = 0..T)

Где A (t) - решение системы без какого-либо преобразования времени на входе, а B (t) - решение системы с преобразованием времени на входе (преобразование времени - wt (w обычно мало) ) и T - это число, а не переменная. Я бы изменил T, чтобы изучить ошибку с разными временными интервалами).

Например (чтобы помочь объяснить мой вопрос):

Оригинальный ODE:

diff(g(t), t, t) = -(diff(g(t), t))-sin(g(t))*cos(g(t))+sin(t)

Пусть A (t) - ЧИСЛЕННОЕ решение исходного ODE (поскольку клен не может решить его символически).

Теперь ОДА С преобразованием времени на входе:

diff(y(t), t, t) = -(diff(y(t), t))-sin(y(t))*cos(y(t))+sin(w*t)

Пусть B (t) является ЧИСЛЕННЫМ решением этого ODE (поскольку клен не может решить его символически).

Мой вопрос: есть ли способ решить ошибку для разных значений w? Для этого мне нужно изменить числовое решение A (t) на A (wt) после численного решения для A (t). Моя конечная цель состоит в том, чтобы подготовить ошибку против. частота, которая ш. Когда w равен 1, ошибки не должно быть, потому что в системе нет изменений.

Я все еще относительно новичок в кодировании. То, что я сделал, так как Maple не может решить это символически, это решает каждый из них численно (с определенным w. Однако я хотел бы сделать это для w в диапазоне [0..1.5]). Затем я нанес их на одну фигуру. Однако это дает мне числовое значение A (t), а не A (wt). И я не знаю, как вычесть их.

sol1 := dsolve([diff(g(t), t, t) = -(diff(g(t), t))- sin(g(t))*cos(g(t))+sin(t), g(0) = 0, (D(g))(0) = 0], numeric);

sol2 := dsolve([diff(y(t), t, t) = -(diff(y(t), t))-sin(y(t))*cos(y(t))+sin(.5*t), y(0) = 0, (D(y))(0) = 0], numeric);

S1 := plots[odeplot](sol1, 0 .. 10, color = red);
S2 := plots[odeplot](sol2, 0 .. 10, color = blue);
display(S1, S2);

Однако, это не полезно, потому что это только график A (t), а не A (wt). Точно так же, он только показывает это, но не показывает ошибку между ними.

Я ожидаю, что ошибка приблизится к 0, так как частота, w, приближается к 0. Я ожидаю, что ошибка увеличится, когда w будет между 0 и 1.

1 Ответ

1 голос
/ 05 июня 2019

Существует множество способов сделать это. Некоторые из них более эффективны, чем другие. Некоторые из них более удобны. Я покажу несколько.

Этот первый основной способ включает два вызова dsolve, аналогично вашему исходному коду, но с дополнительной опцией output=listprocedure. Это заставляет dsolve возвращать список скалярных процедур, а не одну процедуру (которая возвращает список значений). Это позволяет нам выбирать отдельные процедуры для y(t) и g(t) и использовать их отдельно.

restart;
sol1 := dsolve([diff(g(t), t, t)
                = -(diff(g(t), t))- sin(g(t))*cos(g(t))+sin(t),
                g(0) = 0, (D(g))(0) = 0], numeric,
                output=listprocedure):
sol2 := dsolve([diff(y(t), t, t)
                = -(diff(y(t), t))-sin(y(t))*cos(y(t))+sin(.5*t),
                y(0) = 0, (D(y))(0) = 0], numeric,
                output=listprocedure):

Вы все еще можете использовать plots:-odeplot здесь, если хотите.

S1 := plots:-odeplot(sol1, 0 .. 20, color = red, numpoints=200):
S2 := plots:-odeplot(sol2, 0 .. 20, color = blue, numpoints=200):
plots:-display(S1, S2, size=[400,150]);

enter image description here

Но вы также можете извлечь отдельные процедуры, построить их или отобразить их разницу.

G := eval(g(t),sol1):
Y := eval(y(t),sol2):

plot([G,Y], 0..20, color=[red,blue], size=[400,150]);

enter image description here

Различие между ними теперь легче построить.

plot(G-Y, 0..20, color=black, size=[400,150]);

enter image description here

Мы можем вычислить норму (ваш интеграл),

sqrt(evalf(Int( t -> ( G(t) - Y(t) )^2, 0..20, epsilon=1e-5)));
         8.74648880831735

Но теперь давайте сделаем более удобным рассмотрение w как параметра, который мы настраиваем на лету. (Мы не хотим нести расходы или неудобства при вызове dsolve для каждого значения w.)

solgen := dsolve([diff(y(t), t, t)
                 = -(diff(y(t), t))-sin(y(t))*cos(y(t))+sin(w*t),
                 y(0) = 0, (D(y))(0) = 0], numeric,
                 parameters = [w],
                 output=listprocedure):

 Ygen := eval(y(t),solgen):

 # set the parameter value
 Ygen(parameters=[w=0.5]);
                       [w = 0.5]

 # we could query the parameter value
 Ygen(parameters);
                       [w = 0.5]

 # Now call it at a particular value of t
 Ygen(3.2);
                   0.864744459594622

Теперь мы строим процедуру как w, так и t. При вызове без числовых аргументов возвращается без оценки. При вызове с числовыми аргументами он проверяет, соответствует ли значение w текущему сохраненному значению параметра и, если он отличается, устанавливает хранимое значение. Затем он вызывает вычислительную процедуру Ygen с заданным значением t.

Yw := proc(t,w)
  if not [t,w]::list(numeric) then
    return 'procname'(args);
  end if;
  if w - eval(':-w',Ygen(parameters)) <> 0.0 then
    Ygen(':-parameters'=[':-w'=w]);
  end if;
  Ygen(t);
end proc:

Это может дать те же графики, что и раньше.

plots:-display(
  plot(Yw(t,0.5), t=0..20, color=red),
  plot(Yw(t,1.0), t=0..20, color=blue),
  size=[400,150]
);

enter image description here

# somewhat inefficient since for each t value it
# switches the value of the w parameter.
plot(Yw(t,1.0)-Yw(t,0.5), t=0..20, size=[400,150]);

enter image description here

Мы также можем сделать (соединенные линии) в точечных графиках, что более эффективно, поскольку все значения t вычисляются последовательно для любого заданного значения w. (То есть, оно не колеблется между w значениями.)

a,b := 0,20:
xpts := Vector(100,(i)->a+(b-a)*(i-1)/(100-1),datatype=float[8]):
plots:-display(
  plot(<xpts | map(t->Yw(t,0.5), xpts)>, color=red),
  plot(<xpts | map(t->Yw(t,1.0), xpts)>, color=blue),
  size=[400,150]
);

enter image description here

# more efficient since all the Yw values for w=1.0 are
# computed together, and all the Yw values for w=0.5 are
# computed together.
plot(<xpts | map(t->Yw(t,1.0), xpts) - map(t->Yw(t,0.5), xpts)>,
     size=[400,150]);

enter image description here

evalf([seq( ['w'=w, sqrt(Int( unapply( (Yw(t,1.0)
                                         - Yw(t,w))^2, t),
                              0..20, epsilon=1e-1))],
             w=0..1.0, 0.1 )]);

    [[w = 0., 2.97123678486962], [w = 0.1, 20.3172660599286], 

     [w = 0.2, 21.8005838723429], [w = 0.3, 13.0097728518328], 

     [w = 0.4, 9.28961600039575], [w = 0.5, 8.74643983270251], 

     [w = 0.6, 6.27082651159143], [w = 0.7, 5.38965679479886], 

     [w = 0.8, 5.21680809275065], [w = 0.9, 3.19786559349464], 

     [w = 1.0, 0.]]


plot(w->sqrt(Int( (Yw(t,1.0) - Yw(t,w))^2, t=0..20,
                  epsilon=1e-3, method=_d01ajc )),
     0..1, size=[400,150]);

enter image description here

plot3d( Yw(t,w), w=0..1.0, t=0..50, grid=[179,179] );

enter image description here

# For 3D plotting it's also faster to compute all t-values
# for each given w-value, rather than the other way round.
CodeTools:-Usage(
  plot3d( Yw(t,w), w=0..1.0, t=0..50, grid=[49,49] )
):

   memory used=37.51MiB, alloc change=0 bytes, cpu time=504.00ms,
   real time=506.00ms, gc time=127.31ms

CodeTools:-Usage(
  plot3d( Yw(t,w), t=0..50, w=0..1.0, grid=[49,49] ) ):

   memory used=124.13MiB, alloc change=0 bytes, cpu time=2.66s,
   real time=2.66s, gc time=285.47ms

[редактировать] Сюжет выше этой нормы не быстрый. Ниже я делаю три корректировки для лучшей производительности: 1) Используйте специальную процедуру Yw1 для w = 1.0, чтобы Yw не вызывался для установки параметра w дважды для каждой оценки подынтегрального выражения (в норме). 2) Используйте опцию запомнить эту процедуру Yw1. 3) Используйте опцию compile=true в двух dsolve вызовах.

Я также исправляю формулу в норме для вызова Yw1(w*t), чтобы она соответствовала исходной формулировке, о которой идет речь.

restart;
solw1 := dsolve([diff(y(t), t, t)
             = -(diff(y(t), t))-sin(y(t))*cos(y(t))+sin(t),
             y(0) = 0, (D(y))(0) = 0], numeric,
             compile=true,
             output=listprocedure):
Yw1raw := eval(y(t),solw1):
Yw1 := proc(t)
  option remember;
  if not t::numeric then return 'procname'(args); end if;
  []; # evalhf error that plot will catch
  Yw1raw(t);
end proc:
solgen := dsolve([diff(y(t), t, t)
             = -(diff(y(t), t))-sin(y(t))*cos(y(t))+sin(w*t),
             y(0) = 0, (D(y))(0) = 0], numeric,
             parameters = [w],
             compile=true,
             output=listprocedure):
Ygen := eval(y(t),solgen):
Yw := proc(t,w)
  if not [t,w]::list(numeric) then
    return 'procname'(args);
  end if;
  if w - eval(':-w',Ygen(parameters)) <> 0.0 then
    Ygen(':-parameters'=[':-w'=w]);
  end if;
  Ygen(t);
end proc:

CodeTools:-Usage(
  plot(w->sqrt(Int( (Yw1(w*t) - Yw(t,w))^2, t=0..20,
                    epsilon=1e-3, method=_d01ajc )),
       0..1, size=[400,150])
);

    memory used=0.76GiB, alloc change=205.00MiB,
    cpu time=8.22s, real time=7.78s, gc time=761.80ms

enter image description here

...