<?xml version="1.0" encoding="UTF-8"?>
<!--
 * Scilab ( https://www.scilab.org/ ) - This file is part of Scilab
 * Copyright (C) 2008 - INRIA
 * Copyright (C) 2011 - DIGITEO - Michael Baudin
 * Copyright (C) 2012 - Scilab Enterprises - Adeline CARNIS
 * Copyright (C) 2016 - Samuel GOUGEON
 * Copyright (C) 2012 - 2016 - Scilab Enterprises
 *
 * This file is hereby licensed under the terms of the GNU GPL v2.0,
 * pursuant to article 5.3.4 of the CeCILL v.2.1.
 * This file was originally licensed under the terms of the CeCILL v2.1,
 * and continues to be available under such terms.
 * For more information, see the COPYING file which you should have received
 * along with this program.
 *
 -->
<refentry xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svg="http://www.w3.org/2000/svg" xmlns:ns5="http://www.w3.org/1999/xhtml" xmlns:mml="http://www.w3.org/1998/Math/MathML" xmlns:db="http://docbook.org/ns/docbook" xmlns:scilab="http://www.scilab.org" xml:id="ode" xml:lang="ru">
    <refnamediv>
        <refname>ode</refname>
        <refpurpose>
            программа решения обыкновенных дифференциальных уравнений
        </refpurpose>
    </refnamediv>
    <refsynopsisdiv>
        <title>Синтаксис</title>
        <synopsis>
            y = ode(y0, t0, t, f)
            [y, w, iw] = ode([type,]y0, t0, t [,rtol [,atol]],f [,jac] [,w, iw])
            [y, rd, w, iw] = ode("root", y0, t0, t [,rtol [,atol]], f [,jac], ng, g [, w, iw])
            y = ode("discrete", y0, k0, kvect, f)
        </synopsis>
    </refsynopsisdiv>
    <refsection role="parameters">
        <title>Аргументы</title>
        <variablelist>
            <varlistentry>
                <term>y0</term>
                <listitem>
                    <para>
                        вектор или матрица вещественных значений, начальные условия.
                    </para>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term>t0</term>
                <listitem>
                    <para>
                        вещественный скаляр, начальное время.
                    </para>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term>t</term>
                <listitem>
                    <para>
                        вещественный вектор, моменты времени для которых находится решение.
                    </para>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term>f</term>
                <listitem>
                    <para>
                        внешняя функция (функция, строка или список),
                        правая сторона дифференциального  уравнения.
                    </para>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term>type</term>
                <listitem>
                    <para>
                        строка, тип используемой программы решения. Имеются следующие типы
                        программ решения:   <literal>"adams"</literal>,
                        <literal>"stiff"</literal>, <literal>"rk"</literal>,
                        <literal>"rkf"</literal>, <literal>"fix"</literal>,
                        <literal>"discrete"</literal>, <literal>"root"</literal>.
                    </para>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term>atol, rtol</term>
                <listitem>
                    <para>
                        absolute and relative tolerances on the final solution
                        <varname>y</varname> (decimal numbers). If each is a
                        single value, it applies
                        to each component of <varname>y</varname>. Otherwise,
                        it must be a vector of same size as size(y), and
                        is applied element-wise to <varname>y</varname>.
                    </para>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term>jac</term>
                <listitem>
                    <para>
                        внешняя функция (функция, строка или список), якобиан функции
                        <varname>f</varname>.
                    </para>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term>ng</term>
                <listitem>
                    <para>целое число.</para>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term>g</term>
                <listitem>
                    <para>внешняя функция (функция, символьная строка или список).</para>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term>k0</term>
                <listitem>
                    <para>целое число (начальное время).</para>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term>kvect</term>
                <listitem>
                    <para>целочисленный вектор.</para>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term>y</term>
                <listitem>
                    <para>вещественный вектор или матрица (ВЫХОДНАЯ).</para>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term>rd</term>
                <listitem>
                    <para>вещественный вектор (ВЫХОДНОЙ).</para>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term>w, iw</term>
                <listitem>
                    <para>вещественные векторы (ВХОДНЫЕ/ВЫХОДНЫЕ).
                        See <link linkend="ode_optional_output">ode() optional output</link>
                    </para>
                </listitem>
            </varlistentry>
        </variablelist>
    </refsection>
    <refsection role="description">
        <title>Описание</title>
        <para>
            <function>ode</function> решает явные обыкновенные
            дифференциальные уравнения, определённые как:
        </para>
        <para>
            <latex>
                \begin{eqnarray}
                \begin{array}{l}
                \dfrac{dy}{dt} = f(t,y)\\
                y(t_0)=y_0
                \end{array}
                \end{eqnarray}
            </latex>
        </para>
        <para>
            Это интерфейс для различных программ решения, в частности для ODEPACK.
        </para>
        <para>
            В данной справке описывается использование
            <function>ode</function> для стандартных явных систем ОДУ.
        </para>
        <para>
            Самый простой способ вызова <function>ode</function>:
            <code>y = ode(y0, t0, t, f)</code>, где <varname>y0</varname> -
            вектор начальных условий, <varname>t0</varname> - начальное время,
            <varname>t</varname> - вектор моментов времени, для которых вычисляется
            решение <varname>y</varname> и <varname>y</varname> - матрица векторов
            решения <literal>y=[y(t(1)),y(t(2)),...]</literal>.
        </para>
        <para>
            Входной аргумент <varname>f</varname> определяет правую сторону
            дифференциального уравнения первого порядка. Этот аргумент является
            функцией с определённым заголовком.
        </para>
        <itemizedlist>
            <listitem>
                <para>
                    Если <varname>f</varname> является Scilab-функцией, то её
                    последовательность вызова должна быть:
                </para>
                <screen><![CDATA[
ydot = f(t,y)
]]></screen>
                <para>
                    где <literal>t</literal> - вещественный скаляр (время), а
                    <varname>y</varname> - вещественный вектор (состояние) и
                    <varname>ydot</varname> - вещественный вектор (производная первого
                    порядка dy/dt).
                </para>
            </listitem>
            <listitem>
                <para>
                    Если <varname>f</varname> - строка, то это - имя  компилированной
                    процедуры Fortran или функции C. Например, если вызывается
                    <code>ode(y0, t0, t, "fex")</code>, то вызывается подпрограмма
                    <literal>fex</literal>.
                </para>
                <para>
                    Процедура Fortran должна иметь заголовок:
                </para>
                <screen><![CDATA[
fex(n,t,y,ydot)
]]></screen>
                <para>
                    где <varname>n</varname> - целое число, <varname>t</varname> --
                    скаляр двойной точности, <varname>y</varname> и
                    <varname>ydot</varname> - векторы двойной точности.
                </para>
                <para>
                    Функция C должна иметь заголовок:
                </para>
                <screen><![CDATA[
fex(int *n,double *t,double *y,double *ydot)
]]></screen>
                <para>
                    где <varname>t</varname> - время, <varname>y</varname> - состояние,
                    а <varname>ydot</varname> - производная состояния (dy/dt).
                </para>
                <para>
                    Эта внешняя функция может быть собрана способом, независящим от ОС,
                    используя <link linkend="ilib_for_link">ilib_for_link</link> и
                    динамически связана с Scilab с помощью функции
                    <link linkend="link">link</link>.
                </para>
            </listitem>
            <listitem>
                <para>
                    Может случиться, что симулятору <varname>f</varname> потребуются
                    дополнительные аргументы. В этом случае можно использовать следующую
                    возможность. Аргумент <varname>f</varname> может также быть
                    списком <literal>lst=list(simuf,u1,u2,...un)</literal>, где
                    <literal>simuf</literal> является Scilab-функцией с синтаксисом:
                    <literal>ydot = f(t,y,u1,u2,...,un)</literal>, а
                    <literal>u1</literal>, <literal>u2</literal>, ...,
                    <literal>un</literal> - дополнительные аргументы, автоматически
                    передаваемые симулятору <literal>simuf</literal>.
                </para>
            </listitem>
        </itemizedlist>
        <para>
            Функция <varname>f</varname> может возвращать вместо
            вектора матрицу<literal>p x q</literal>. С этой матричной нотацией
            решается система <literal>n=p+q</literal> ОДУ
            <literal>dY/dt=F(t,Y)</literal>, где <literal>Y</literal> - матрица
            <literal>p x q</literal>.  Затем начальные условия,
            <literal>Y0</literal>, должны быть так же матрицей
            <literal>p x q</literal>, а результат <function>ode</function> - матрицей
            <literal>p x q(T+1)</literal>
            <literal>[Y(t_0),Y(t_1),...,Y(t_T)]</literal>.
        </para>
        <para>
            Допуски <varname>rtol</varname> и <varname>atol</varname> являются
            порогами для относительной и абсолютной оценённых ошибок. Оценённая
            ошибка <literal>y(i)</literal> равна:
            <literal>rtol(i)*abs(y(i))+atol(i)</literal> и интегрирование выполняется
            пока эта ошибка мала для каждого из элементов состояния. Если
            <varname>rtol</varname> и/или <varname>atol</varname> является
            константой, то <literal>rtol(i)</literal> и/или <literal>atol(i)</literal>
            являются набором констант. Значения по умолчанию для
            <varname>rtol</varname> и <varname>atol</varname> соответственно равны
            <literal>rtol=1.d-5</literal> и <literal>atol=1.d-7</literal> для
            большинства программ решения, а для <literal>"rfk"</literal> и
            <literal>"fix"</literal>  <literal>rtol=1.d-3</literal> и
            <literal>atol=1.d-4</literal>.
        </para>
        <para>
            Для жёстких задач лучше указывать якобиан функции правой стороны в
            качестве необязательного аргумента <varname>jac</varname>.
            Якобиан является внешней функцией, т. е. функцией со специальным
            синтаксисом, либо именем процедуры Fortran или функции C
            (символьная строка) с определённой последовательностью вызова, либо
            списком.
        </para>
        <itemizedlist>
            <listitem>
                <para>
                    Если <varname>jac</varname> является функцией, то синтаксис должен
                    быть <literal>J=jac(t,y)</literal>, где <literal>t</literal> --
                    вещественный скаляр (время), а <varname>y</varname> - вещественный
                    вектор (состояние). Результирующая матрица <literal>J</literal>
                    должна вычислять df/dx, т. е. <literal>J(k,i) = dfk/dxi</literal>, где
                    <literal>fk</literal> - <literal>k</literal>-тый элемент
                    <varname>f</varname>.
                </para>
            </listitem>
            <listitem>
                <para>
                    Если <varname>jac</varname> является символьной строкой, то она
                    ссылается на имя процедуры Fortran или функции C.
                </para>
                <para>
                    Процедура Fortran должна иметь заголовок:
                </para>
                <screen><![CDATA[
subroutine fex(n,t,y,ml,mu,J,nrpd)
integer n,ml,mu,nrpd
double precision t,y(*),J(*)
]]></screen>
                <para>
                    Функция C должна иметь заголовок:
                </para>
                <screen><![CDATA[
void fex(int *n,double *t,double *y,int *ml,int *mu,double *J,int *nrpd,)
]]></screen>
                <para>
                    В большинстве случаев не нужно ссылаться на <literal>ml</literal>,
                    <literal>mu</literal> и <literal>nrpd</literal>.
                </para>
            </listitem>
            <listitem>
                <para>
                    Если <varname>jac</varname> является списком, то для
                    <varname>f</varname> применяются те же договорённости.
                </para>
            </listitem>
        </itemizedlist>
        <para>
            Необязательные аргументы <varname>w</varname> и
            <varname>iw</varname> являются векторами для хранения информации,
            возвращаемой подпрограммой интегрирования (см. <link linkend="ode_optional_output">ode_optional_output</link>).
            Когда эти векторы указываются с правой стороны  <function>ode</function>,
            интегрирование начинается заново с теми же параметрами, что и до
            остановки.
        </para>
        <para>
            Программам решения ODEPACK можно передать больше опций с помощью
            переменной  <literal>%ODEOPTIONS</literal>. See <link linkend="odeoptions">odeoptions</link>.
        </para>
    </refsection>
    <refsection>
        <title>Программы решения</title>
        <para>
            Тип задачи, которую надо решить и используемый метод зависят от
            значения первого необязательного аргумента <varname>type</varname>,
            который может быть одной из следующих строк:
        </para>
        <variablelist>
            <varlistentry>
                <term>&lt;не задано&gt;:</term>
                <listitem>
                    <para>
                        Программа решения <literal>lsoda</literal> из пакета ODEPACK
                        вызывается по умолчанию. Она автоматически выбирает между
                        нежёстким  методом прогноза-исправления Адамса
                        (predictor-corrector Adams method) и жёстким методом обратной
                        дифференциальной формулой (ОДФ) ( Backward Differentiation
                        Formula (BDF) method). Изначально она применяет нежёсткий метод и
                        динамически проверяет данные для того, чтобы решить какой метод
                        использовать.
                    </para>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term>"adams":</term>
                <listitem>
                    <para>
                        Используется для нежёстких задач. Вызывается программа
                        решения <literal>lsode</literal> из пакета ODEPACK, и она
                        использует метод Адамса.
                    </para>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term>"stiff":</term>
                <listitem>
                    <para>
                        Это для жёстких задач. Вызывается программа решения
                        <literal>lsode</literal> из пакета ODEPACK, и она использует метод
                        ОДФ.
                    </para>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term>"rk":</term>
                <listitem>
                    <para>Адаптивный метод Рунге-Кутты 4-го порядка (RK4).</para>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term>"rkf":</term>
                <listitem>
                    <para>
                        Используется программа Шампайна и Уотса (Shampine и Watts),
                        основанная на методе Рунге-Кутты-Фелберга пары 4 и 5-го
                        порядка (RKF45). Она для нежёстких и среднежёстких задач,
                        когда получаемые вычисления не дороги. Этот метод не следует, в
                        общем случае, использовать, если пользователь требует
                        высокую точность.
                    </para>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term>"fix":</term>
                <listitem>
                    <para>
                        Та же программа решения, что и <literal>"rkf"</literal>, но
                        пользовательский интерфейс очень прост, т. е. программе решения
                        могут быть переданы только параметры <varname>rtol</varname> и
                        <varname>atol</varname>. Это самый простой метод для пробы.
                    </para>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term>"root":</term>
                <listitem>
                    <para>
                        Программа решения ОДУ с возможностью нахождения корней.
                        Используется программа решения <literal>lsodar</literal> из пакета
                        ODEPACK. Она является вариантом программы
                        решения <literal>lsoda</literal>, где она ищет корни заданной
                        векторной функции. См. справку по <link linkend="ode_root">ode_root</link>.
                    </para>
                </listitem>
            </varlistentry>
            <varlistentry>
                <term>"discrete":</term>
                <listitem>
                    <para>
                        Моделирование дискретного времени. См. справку по <link linkend="ode_discrete">ode_discrete</link>.
                    </para>
                </listitem>
            </varlistentry>
        </variablelist>
    </refsection>
    <refsection role="examples">
        <title>Примеры</title>
        <para>
            В следующем примере мы решим обыкновенное дифференциальное уравнение
            <literal>dy/dt=y^2-y*sin(t)+cos(t)</literal> с начальным условием
            <literal>y(0)=0</literal>. Используем программу решения по умолчанию.
        </para>
        <programlisting role="example"><![CDATA[
function ydot = f(t,y)
    ydot = y^2 - y*sin(t) + cos(t)
endfunction
y0 = 0;
t0 = 0;
t = 0:0.1:%pi;
y = ode(y0,t0,t,f);
plot(t,y)
     ]]></programlisting>
        <para>
            В следующем примере мы решаем уравнение <literal>dy/dt=A*y</literal>.
            Точное решение равно <literal>y(t)=expm(A*t)*y(0)</literal>, где
            <literal>expm</literal> - матричная экспонента.
            Неизвестное равно матрице  <literal>y(t)</literal> размером 2 на 1.
        </para>
        <programlisting role="example"><![CDATA[
function ydot = f(t,y)
    ydot = A*y
endfunction
function J = Jacobian(t,y)
    J = A
endfunction
A = [10,0;0,-1];
y0 = [0;1];
t0 = 0;
t = 1;
ode("stiff",y0,t0,t,f,Jacobian)
// Сравним с точным решением:
expm(A*t)*y0
     ]]></programlisting>
        <para>
            В следующем примере мы решаем ОДУ <literal>dx/dt = A*x(t) +
                B*u(t)
            </literal>
            с <literal>u(t)=sin(omega*t)</literal>.
            Обратите внимание, что дополнительные аргументы <varname>f</varname>:
            <literal>A</literal>, <literal>u</literal>, <literal>B</literal>,
            <literal>omega</literal> передаются в <varname>f</varname> в списке.
        </para>
        <programlisting role="example"><![CDATA[
function xdot = linear(t,x,A,u,B,omega)
    xdot = A*x + B*u(t,omega)
endfunction
function ut = u(t,omega)
    ut = sin(omega*t)
endfunction
A = [1 1;0 2];
B = [1;1];
omega = 5;
y0 = [1;0];
t0 = 0;
t = [0.1,0.2,0.5,1];
ode(y0,t0,t,list(linear,A,u,B,omega))
     ]]></programlisting>
        <para>
            В следующем примере мы решим дифференциальное уравнение Риккати
            <literal>dX/dt=A'*X + X*A - X'*B*X + C</literal>, где начальное
            условие  <literal>X(0)</literal> является единичной матрицей 2 на 2.
        </para>
        <programlisting role="example"><![CDATA[
function Xdot = ric(t,X,A,B,C)
    Xdot = A'*X + X*A - X'*B*X + C
endfunction
A = [1,1;0,2];
B = [1,0;0,1];
C = [1,0;0,1];
y0 = eye(A);
t0 = 0;
t = 0:0.1:%pi;
X = ode(y0,t0,t,list(ric,A,B,C))
     ]]></programlisting>
        <para>
            В следующем примере мы решаме дифференциальное уравнение
            <literal>dY/dt=A*Y</literal>, где неизвестная <literal>Y(t)</literal>
            является матрицей 2 на 2.  Точное решение равно <literal>expm</literal>
            <literal>Y(t)=expm(A*t)</literal>, где <literal>expm</literal> --
            матричная экспонента.
        </para>
        <programlisting role="example"><![CDATA[
function ydot = f(t,y,A)
    ydot = A*y;
endfunction
A = [1,1;0,2];
y0 = eye(A);
t0 = 0;
t = 1;
ode(y0,t0,t,list(f,A))
// Сравнение с точным решением
expm(A*t)
ode("adams",y0,t0,t,f)
     ]]></programlisting>
    </refsection>
    <refsection>
        <title>С компилятором</title>
        <para>
            Следующий пример требует компилятор C.
        </para>
        <programlisting role="example"><![CDATA[
// ---------- Простое одномерное ОДУ (внешняя функция на языке C)
ccode=['#include <math.h>'
       'void myode(int *n,double *t,double *y,double *ydot)'
       '{'
       '  ydot[0]=y[0]*y[0]-y[0]*sin(*t)+cos(*t);'
       '}']
mputl(ccode,TMPDIR+'/myode.c') // создаём C-файл
// Компилируем
cd TMPDIR
ilib_for_link('myode','myode.c',[],'c',[],'loader.sce');
exec('loader.sce') // пошаговая компоновка
y0 = 0;
t0 = 0;
t = 0:0.1:%pi;
y = ode(y0,t0,t,'myode');
     ]]></programlisting>
    </refsection>
    <refsection role="bibliography">
        <title>Литература</title>
        <para>
            Alan C. Hindmarsh, "lsode and lsodi, two new initial value ordinary
            differential equation solvers", ACM-Signum newsletter, vol. 15, no. 4
            (1980), pp. 10-11.
        </para>
    </refsection>
    <refsection role="references">
        <title>Используемые функции</title>
        <para>
            Связанные процедуры могут быть найдены в директории
            SCI/modules/differential_equations/src/fortran:
            lsode.f lsoda.f lsodar.f
        </para>
    </refsection>
    <refsection role="see also">
        <title>Смотрите также</title>
        <simplelist type="inline">
            <member>
                <link linkend="odeoptions">odeoptions</link>
            </member>
            <member>
                <link linkend="ode_optional_output">ode_optional_output</link>
            </member>
            <member>
                <link linkend="ode_root">ode_root</link>
            </member>
            <member>
                <link linkend="ode_discrete">ode_discrete</link>
            </member>
            <member>
                <link linkend="dae">dae</link>
            </member>
            <member>
                <link linkend="impl">impl</link>
            </member>
            <member>
                <link linkend="odedc">odedc</link>
            </member>
            <member>
                <link linkend="csim">csim</link>
            </member>
            <member>
                <link linkend="ltitr">ltitr</link>
            </member>
            <member>
                <link linkend="rtitr">rtitr</link>
            </member>
            <member>
                <link linkend="intg">intg</link>
            </member>
        </simplelist>
    </refsection>
</refentry>
