MATLAB: сохранение параметров внутри ode45 с использованием 'assignin'

Я запускаю набор ODE с ode45 в MATLAB, и мне нужно сохранить одну из переменных (это не производная) для дальнейшего использования. Я использую функцию assignin, чтобы назначить временную переменную в базовой рабочей области и обновлять ее на каждом этапе. Кажется, это работает, однако размер массива не соответствует размеру вектора решения, полученного из ode45. Например, у меня есть следующая вложенная функция:

function [Z,Y] = droplet_momentum(theta,K,G,P,zspan,Y0)

options = odeset('RelTol',1e-7,'AbsTol',1e-7);
[Z,Y] = ode45(@momentum,zspan,Y0,options);

function DY = momentum(z,y)

    DY = zeros(4,1);

    %Entrained Total Velocity
    Ve = sin(theta)*(y(4));

    %Total Relative Velocity
    Urs = sqrt((y(1) - y(4))^2 + (y(2) - Ve*cos(theta))^2 + (y(3))^2);

    %Coefficients
    PSI = K*Urs/y(1);
    PHI = P*Urs/y(1);

    %Liquid Axial Velocity
    DY(1) = PSI*sign(y(1) - y(4))*(1 + (1/6)*(abs(y(1) - y(4))*G)^(2/3));

    %Liquid Radial Velocity
    DY(2) = PSI*sign(y(2) - Ve*cos(theta))*(1 + (1/6)*(abs(y(2) - ...
        Ve*cos(theta))*G)^(2/3));

    %Liquid Tangential Velocity
    DY(3) = PSI*sign(y(3))*(1 + (1/6)*(abs(y(3))*G)^(2/3));

    %Gaseous Axial Velocity
    DY(4) = (1/z/y(4))*((PHI/z)*sign(y(1) - y(4))*(1 + ...
        (1/6)*(abs(y(1) - y(4))*G)^(2/3)) + Ve*Ve - y(4)*y(4));

    assignin('base','Ve_step',Ve);
    evalin('base','Ve_out(end+1) = Ve_step');
end

end

В приведенном выше коде тета (радианы), K (отрицательное значение), P и G являются константами и для данного примера могут быть приняты как любое значение. Zspan - это просто временной шаг интегрирования для решателя ODE, а Y0 - вектор начальных условий (4x1). Опять же, для этого примера они могут принимать любое разумное значение. Теперь в основном файле функция вызывается следующим образом:

Ve_out = 0;
[Z,Y] = droplet_momentum(theta,K,G,P,zspan,Y0);
Ve_out = Ve_out(2:end);

Этот метод работает без жалоб со стороны MATLAB, но проблема в том, что размер Ve_out не совпадает с размером Z или Y. Причина этого в том, что MATLAB вызывает функцию ODE несколько раз для своего алгоритма, поэтому решение будет немного меньше Ve_out. Как предложил am304, я мог бы просто вычислить DY, задав функции ode вектор Z и Y, такой как DY = momentum (Z, Y), однако мне нужно, чтобы это работало с помощью 'assignin' (или аналогичного метода), потому что другой версия этой проблемы имеет неявную зависимость между DY и Ve, и вычисление DY на каждой итерации было бы слишком затратным в вычислительном отношении (я буду запускать эту задачу для многих итераций).


person Kimusubi    schedule 22.05.2013    source источник
comment
Опять же, вам нужно показать автономный код. Что в zspan?   -  person Rasman    schedule 22.05.2013
comment
Код относительно длинный, его нельзя сократить, чтобы он оставался самодостаточным. Zspan - это просто шаг интегрирования по времени (или пространству) (аналогично tspan). Он просто сообщает программе ODE решать от t0 до tf. При необходимости я могу включить автономный код, но он намного длиннее.   -  person Kimusubi    schedule 22.05.2013
comment
В стороне: почему бы не сделать вашу целевую функцию вложенной функцией , а затем использовать переменную, видимую как целевой функции, так и вызывающей стороне? Это позволяет избежать использования evalin и семейства, которые особенно подвержены ошибкам.   -  person Joshua Barr    schedule 22.05.2013
comment
Этот алгоритм является частью большого итеративного цикла, и я помещаю их в отдельные файлы функций, чтобы все было в порядке. В противном случае он будет значительно загроможден. К сожалению, использование отдельных файлов функций сильно замедлило мой итерационный процесс.   -  person Kimusubi    schedule 22.05.2013
comment
Мне очень жаль, что вы обиделись, но вы спросили, что такое «zspan», и я объяснил это. Я не совсем уверен, в чем проблема.   -  person Kimusubi    schedule 22.05.2013
comment
@Rasman Я пошел дальше и включил автономный код, хотя не уверен, что это сильно поможет.   -  person Kimusubi    schedule 22.05.2013
comment
это поможет, если я смогу запустить [Z,Y] = droplet_momentum(theta,K,G,P,zspan,Y0); без сообщения об ошибке. И самодостаточный код не означает, что вам нужно показать мне свой код, скорее это означает, что вы можете показать упрощенный код, который воспроизводит проблему. Zspan можно выразить по-разному, и пока вы не скажете мне, что там, я не могу начать создавать для вас решение.   -  person Rasman    schedule 22.05.2013
comment
Я забыл добавить, что K - отрицательное значение. Я уже внес правку в основной пост.   -  person Kimusubi    schedule 22.05.2013


Ответы (2)


Хорошо, давайте начнем с быстрого примера SSCCE:

function [Z,Y] = khan

options = odeset('RelTol',1e-7,'AbsTol',1e-7);
[Z,Y] = ode45(@momentum,[0 12],[0 0],options);
end

function Dy = momentum(z,y)
Dy = [0 0]';

Dy(1) = 3*y(1) + 2* y(2) - 2;
Dy(2) = y(1) - y(2);

Ve = Dy(1)+ y(2);

    assignin('base','Ve_step',Ve);
    evalin('base','Ve_out(end+1) = Ve_step;');

    assignin('base','T_step',z);
    evalin('base','T_out(end+1) = T_step;');
end

Запустив [Z,Y] = khan в качестве командной строки, я получаю полный функциональный код, демонстрирующий вашу проблему, без всех связанных с этим головных болей. Мое терпение на этом закончилось: живи и учись.

Кажется, это работает, однако размер массива не соответствует размеру вектора решения, полученного из ode45.

Обратите внимание, что я добавил в ваш код две строки, извлекающие переменную времени. Из командной строки просто нужно запустить следующее, чтобы понять, что происходит:

Ve_out = [];
T_out = [];
[Z,Y] = khan;
size (Z)
size (T_out)
size (Ve_out)
plot (diff(T_out))

ans =
   109     1

ans =   
     1   163

ans =
     1   163

введите описание изображения здесь

По сути, ode45 - это итеративный алгоритм, что означает, что он будет регулярно исправлять ошибки (вот почему вы регулярно видите diff (T) = 0). Вы не можете заставить алгоритм делать то, что хотите, вы должны жить с этим.

Итак, ваши варианты:
1. Используйте алгоритм с фиксированным шагом
2. Имейте вызов функции, который воспроизводит то, что вы хотите, после того, как алгоритм ode45 выполнил свою грязную работу. (Решение am304)
3. Собирает данные с помощью функции времени, затем алгоритм анализирует все, чтобы удалить лишние данные.

person Rasman    schedule 22.05.2013
comment
Вы также можете создать временной ряд с вашими данными и использовать resample, чтобы получить Ve того же размера, что и Z и Y: mathworks.co.uk/help/matlab/ref/timeseries.resample.html - person am304; 23.05.2013
comment
Другая возможность - переформулировать ваши дифференциальные уравнения так, чтобы Ve был одним из выходов. Вам нужно будет добавить dY(5) = % equation for dVe. Тогда Ve будет возвращено как Y(5). - person am304; 23.05.2013
comment
@ am304 Да и нет. В итоге вы добавляете к этому несколько уровней сложности. в моем примере. dVe = d (dy (1)) + dy (2). Но d (dy (1)) не определен, поэтому вам понадобится другой дифференциальный параметр, который потребует переписывания ваших уравнений, чтобы принять его (их) во внимание (или рискует создать связанные уравнения) - person Rasman; 23.05.2013

Разве вы не можете сделать что-то подобное? Очевидно, проверьте правильность размеров матриц / векторов и внесите соответствующие поправки в код.

[Z,Y] = droplet_momentum2(theta,K,G,P,zspan,Y0);
DY = momentum(Z,Y);
Ve = sin(theta)*(0.5*z*DY(4) + y(4));

т.е. как только ОДУ решено, вычисляется производная DY как функция от Z и Y (которые только что были решены ОДУ) и, наконец, Ve.

person am304    schedule 22.05.2013
comment
Я действительно так и сделал, но я просто пытался понять, что делать с 'assignin' и 'evalin'. Причина, по которой я хотел это сделать, заключается в том, что мне нужно реализовать немного другую версию кода, где есть неявная зависимость от DY и VE, и было бы очень сложно решить для DY дважды (один раз для ODE и один раз снова получить Ве). Поскольку я выполняю много итераций для различных начальных условий, все эти дополнительные шаги будут суммироваться. - person Kimusubi; 22.05.2013
comment
В таком случае переформулируйте свой вопрос. Как есть, нет смысла злоупотреблять assignin и evalin, и вы, вероятно, не заставите его работать. - person horchler; 23.05.2013
comment
Другой альтернативой решению @am304 является вывод ode45 только одной переменной. Это будет структура. Затем эту структуру можно использовать вместе с функцией deval для оценки решение или подмножества решений в произвольные моменты времени с использованием соответствующей полиномиальной интерполяции. - person horchler; 23.05.2013
comment
Я использую assignin и evalin в соответствии с документом mathworks, предлагающим решения этой самой проблемы, но я согласен с вами, это не очень хороший метод. - person Kimusubi; 23.05.2013