Code covered by the BSD License  

Highlights from
Biohydrodynamics Toolbox

image thumbnail
from Biohydrodynamics Toolbox by Alexandre Munnier
Tool to simulate easily the motion of moving solids or swimming robots in a potential fluid flow.

bht_traject_compute1(varargin)
function [T,p,dp,varargout] = bht_traject_compute1(varargin)
% BHT_TRAJECT_COMPUTE
% 
% Compute articulated bodies' trajectories and return their positions and
% velocities at each time step.
% 
% Syntax
% 
%     [T,p,dp] =
%     BHT_TRAJECT_COMPUTE('DataFilename',filename1,'ControlsFilename',filename2)
% 
%     [...,q,dq,Q,dQ,JJ] =
%     BHT_TRAJECT_COMPUTE('DataFilename',filename1,'ControlsFilename',filename2)
%
%     [...] = BHT_TRAJECT_COMPUTE(...,options)
% 
% Description
% 
%     The function returns the time under the form of a linearly spaced
%     vector T. The second output variable p is a matrix whose rows are the
%     degrees of freedom of the bodies' first link (coordinates of its
%     center of mass and its orientation) and whose columns correspond to
%     the time steps ; dp are the related velocities. The function provides
%     also other ways of locating the bodies:
% 
%           o q is a matrix whose rows are the degrees of freedom of the
%             solids and dq the related velocities. 
%           o Q is a matrix whose rows are the coordinates of the center 
%             of mass and the orientation of the bodies; dQ being the related 
%             velocities.
% 
%     The function returns also JJ, a row vector whose coordinates are the
%     momenta of inertia of the articulated bodies at each time step.
%     Indeed, the momenta of inertia of the bodies depend on the bodies'
%     shapes and evolve in time.
% 
%     The input variable finelame1 is the filename of the DAT-File and
%     filename2 is the filename of the controls M-File. If this file
%     requires a parameter, use the option 
% 
%     [...] = BHT_TRAJECT_COMPUTE(...,'ControlsParameters',cont_parameter)
% 
%     where cont_parameter is a parameters matrix. The function
%     BHT_TRAJECT_COMPUTE can be ran only after BHT_DATA_COMPILE having
%     exited successfully for it requires the MAT-File generated and saved
%     on disk during the compilation of the DAT-File. The output variables
%     T,p,dp,q,dq,Q,dQ,JJ and input variables filename1, filename2 and
%     cont_parameter are automatically saved on disk at the end of the
%     computations in the MAT-File filename1_results. This saving can be
%     disabled by adding an optional field to the command line:
% 
%     [...] = BHT_TRAJECT_COMPUTE(...,'SaveResults','off')
%
%     You can specify the relative tolerance of the ODE solver (the ODE
%     solver is the MATLAB ODE solver ODE113) by adding the option 'reltol'
%     as follows:
%
%     [...] = BHT_TRAJECT_COMPUTE(...,'RelTol',reltol)
%
%     When this option is omitted, the default value used is reltol = 1e-4.
%     Two subfunctions are contained in BHT_TRAJECT_COMPUTE: BHT_EULER_RHS
%     which returns the right hand side of the main ODE and BHT_MNYSTROM
%     which solves the integral equations with Nystrm's method.
% 
% See also BHT_DATA_COMPILE, BHT_MNYSTROM
% 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                         %
%                      BIOHYDRODYNAMICS MATLAB TOOLBOX                    %
%                                                                         %
%                         A. MUNNIER and B. PINCON                        %
%                                                                         %
% alexandre.munnier@iecn.u-nancy.fr        bruno.pincon@iecn.u-nancy.fr   %
% http://www.iecn.u-nancy.fr/~munnier  http://www.iecn.u-nancy.fr/~pincon % 
%                                                                         %
%                      INSTITUT ELIE CARTAN, NANCY 1                      %
%                       http://www.iecn.u-nancy.fr/                       %
%                                                                         %
%                      INRIA Lorraine, Projet CORIDA                      %
%                    http://www.iecn.u-nancy.fr/~corida/                  %
%                                                                         %  
%                                                                         %  
%                                                                         % 
%                            August 15th 2008                             %
%                                                                         %
%                                               GNU GPL v3 licence        %
%                                                                         %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


%==========================================================================
warning off Matlab:dividebyzero; % turn off dividebyzero warning
%==========================================================================

%##########################################################################
% PART I
% reading the options from varargin 
%##########################################################################
% default values
cont_parameters = [];
save_results = 1;
rel_tol = 1e-4;
no_filename = 1;
no_contfilename = 1;
%==========================================================================
% reading options
%================
nb_varargin = numel(varargin);
kk = 1;

while kk <= nb_varargin
    if ~ischar(varargin{kk})
        error(['Unknown option ',num2str(varargin{kk})]) %#ok<ST2NM>
    end
    %------------------------------------------------------------------
    switch lower(varargin{kk})
        %--------------------------------------------------------------
        case 'datafilename'

            if ischar(varargin{kk+1})

                filename = varargin{kk+1};
                exten = regexp(filename,'\w*\.(\w*)', 'tokens');
                no_filename = 0;

                if isempty(exten)
                    filename = [filename,'.dat']; %#ok<AGROW>
                end

            else
                error('Unexpected expression for option DataFilename.')
            end
            kk = kk+2;
            %--------------------------------------------------------------
        case 'controlsfilename'

            if ischar(varargin{kk+1})

                cont_filename = varargin{kk+1};
                no_contfilename = 0;

            else
                error('Unexpected expression for option ControlsFilename.')
            end
            kk = kk+2;
            %----------------------------------------------------------
        case 'controlsparameters'
            if isfloat(varargin{kk+1})
                cont_parameters = varargin{kk+1};
            else
                error('Unexpected value for option ControlsParameters. Must be a matrix.')
            end
            kk = kk+2;
            %-------------
        case 'saveresults'
            if ischar(varargin{kk+1})
                if strcmpi(varargin{kk+1},'off')
                    save_results = 0;
                elseif strcmpi(varargin{kk+1},'on')
                    save_results = 1;
                else
                    error('Unexpected value for option SaveResults. Must be ''on'' or ''off''.')
                end
            else
                error('Unexpected value for option SaveResults. Must be ''on'' or ''off''.')
            end
            kk = kk+2;
            %----------------------------------------------------------
        case 'reltol'
            if isvector(varargin{kk+1})
                if length(varargin{kk+1}) == 1
                    rel_tol = varargin{kk+1};
                else
                    error('Unexpected value for option RelTol. Must be a real positive number.')
                end
            else
                error('Unexpected value for option RelTol. Must be a real positive number.')
            end
            kk = kk+2;
            %----------------------------------------------------------
        otherwise
            error(['Unknown option ''',varargin{kk},''''])
    end

end

% =========================================================================
% DataFilename is missing
if no_filename
    filename = input('Enter data file''s name (hit Return to browse): ','s');
    if isempty(filename);
        filename = uigetfile('*.dat','Select data file');
    end
end

% =========================================================================
% ControlsFilename is missing
if no_contfilename
    cont_filename = input('Enter controls file''s name (hit Return to browse): ','s');
    if isempty(cont_filename);
        cont_filename = uigetfile('*.m','Select controls m file');
        cont_filename = regexp(cont_filename,'(\w*)\.', 'tokens');
        cont_filename = cont_filename{1}{1};
    end
end


% building data kinematic filename ========================================
base_filename = regexp(filename,'(\w*)(?:\.)?', 'tokens');
base_filename = base_filename{1}{1};
kine_filename = [base_filename,'_kine'];


%##########################################################################
% PART II
% loading data compiled by datacompil.m
%##########################################################################

filename1 = [base_filename,'_data'];
filename2 = [base_filename,'_results']; % will be used later on

load(filename1,'gene_data','discret_data','bound_data','ode_data',...
    'phys_data','fish');

%##########################################################################
% PART III
% initializing variables
%##########################################################################
% to display progress time
rel_time1 =0;

% initial positions and velocities of the fishes ==========================
p0 = ode_data.initial_positions; 
dp0 = ode_data.initial_velocities; 
Tmax = ode_data.Tmax;              
dT = ode_data.dT;              

% physical data (densities and mass matrix) ===============================
rhos = phys_data.rhos;
rhof = phys_data.rhof;
Ms = phys_data.Ms;

% degrees of freedom of the system, nb of solids, fishes, boundaries, 
% collisions ==============================================================
deg_lib = 3*gene_data.nb_fishes;
nb_solids = gene_data.nb_solids;
nb_boundaries = gene_data.nb_boundaries;
if gene_data.collisions
    alpha = gene_data.alpha;
    epsilon = gene_data.epsilon;
end


% preallocating variables used later on ===================================
g = cell(nb_solids,1);
gg = cell(nb_solids,1);
G = cell(nb_solids,3);
Mf = zeros(3*nb_solids,3*nb_solids);

% ------------------------
S1 = zeros(3*nb_solids,1);
S2 = zeros(3*nb_solids,1);
S3 = zeros(3*nb_solids,1);
S4 = zeros(3*nb_solids,1);
B1 = zeros(3*nb_solids,1);

%-------------------------
X = cell(1,nb_boundaries);
N = cell(1,nb_boundaries);

for kk = nb_solids+1:nb_boundaries
    N{kk} = bound_data(kk).N;
    X{kk} = bound_data(kk).X;
end

%--------------------------
n = zeros(1,nb_boundaries);
m = zeros(1,nb_boundaries); 
dt = zeros(1,nb_boundaries); 

for kk = 1:nb_boundaries
    n(kk) = bound_data(kk).nbr_modes;
    m(kk) = bound_data(kk).nbr_points; 
    dt(kk) = bound_data(kk).dt;  
end

mtot = sum(m);
mc = [0,cumsum(m)];
        
A = zeros(mtot,mtot);
b = zeros(mtot,3*(nb_solids));

phi = cell(nb_solids,3);
toto = cell(nb_solids,3);


%-----------------------
disp('Computations in progress...')
disp('0%------20%-------40%-------60%-------80%------100%')
%-----------------------

%##########################################################################
% PART IV
% solving the ode
%##########################################################################

tic
options = odeset('reltol',rel_tol);
[T,P] = ode113(@euler_rhs,[0:dT:Tmax],[dp0;p0],options);%#ok<NBRAK> %,'options');

%##########################################################################
% PART V
% saving the results
%##########################################################################

dp = P(:,1:deg_lib)';           
p = P(:,deg_lib+1:2*deg_lib)'; 


%==========================================================================
% q, dq are the coordinates and velocities of the solids 

nb_time_step = length(T);

q = zeros(3*nb_solids,nb_time_step);
dq = zeros(3*nb_solids,nb_time_step);

for compt = 1:nb_time_step
    
    t = T(compt);
    pp = p(:,compt);
    dpp = dp(:,compt);
    
    [c,dc] = feval(cont_filename,t,cont_parameters);
    [q(:,compt),dpq,dcq] = feval(kine_filename,pp,dpp,c,dc);
    dq(:,compt) = dpq*dpp+dcq*dc;
end
varargout{1} = q;
varargout{2} = dq;
% =========================================================================
% Q, dQ are the coordinates and velocities of the fishes

array_fishes_solids = gene_data.array_fishes_solids;
Ms = phys_data.Ms;
mm = phys_data.fishes_mass;

[Q,dQ,JJ] = bht_gravity_center(array_fishes_solids,Ms,mm,dT,q,dq);

varargout{3} = Q;
varargout{4} = dQ;
varargout{5} = JJ;
%---------------------------------------------------------
fprintf('\n\n')
disp(['Computation completed in ',num2str(toc),' seconds'])
%---------------------------------------------------------

if save_results
    save(filename2,'cont_filename','cont_parameters','T','p','dp',...
                                                         'q','dq',...
                                                         'Q','dQ','JJ');
    disp(['Solution saved in file ''',filename2,'.mat'''])
end

%##########################################################################
% Appendix I
% function euler_rhs 
%##########################################################################

    function [dP] = euler_rhs(t,P)

        % displaying simulation's time ====================================
        %disp(['t = ',num2str(t)]); 
        rel_time = floor((t/Tmax)*50);
        string = repmat('|',1,rel_time-rel_time1);
        rel_time1 = max(rel_time,rel_time1);
        fprintf(1,string)
        
        % separating positions (= pp) and velocities ( = dpp) =============
        dpp = P(1:deg_lib);
        pp = P(deg_lib+1:2*deg_lib);

        % computing controls and kinematic ================================
        [c,dc,dttc] = feval(cont_filename,t,cont_parameters);
        [qq,dpq,dcq,dppqdp,dpcqdc,dccqdc] = feval(kine_filename,pp,dpp,c,dc);
        
        qq = reshape(qq,3,nb_solids);

        % moving solids ===================================================
        for k = 1:nb_solids
            h = qq(1:2,k);
            theta = qq(3,k);
            R = [cos(theta),-sin(theta);...
                sin(theta), cos(theta)];
            X{k} = R*bound_data(k).X + h*ones(1,bound_data(k).nbr_points);
            N{k} = R*bound_data(k).N;
        end

        % computing potentials using nystrom's method =====================
        [uu,duu] = mnistrom(X,N);

        % computing boundary conditions ===================================

        for k = 1:nb_solids

            g{k} = [N{k}(1,:)./bound_data(k).dl;...
                N{k}(2,:)./bound_data(k).dl;...
                bound_data(k).flux];

            gg{k} = bound_data(k).fflux;

            G{k,1} = [bound_data(k).K.*(g{k}(2,:).^2-g{k}(1,:).^2);...
                      -2*bound_data(k).K.*g{k}(1,:).*g{k}(2,:);...
                      -bound_data(k).K.*(g{k}(2,:).*gg{k}+g{k}(3,:).*g{k}(1,:))-g{k}(2,:)];

            G{k,2} = [-2*bound_data(k).K.*g{k}(1,:).*g{k}(2,:);...
                      bound_data(k).K.*(g{k}(1,:).^2-g{k}(2,:).^2);...
                      bound_data(k).K.*(gg{k}.*g{k}(1,:)-g{k}(3,:).*g{k}(2,:))+g{k}(1,:)];

            G{k,3} = [-bound_data(k).K.*(g{k}(2,:).*gg{k}+g{k}(3,:).*g{k}(1,:))-g{k}(2,:);...
                      bound_data(k).K.*(g{k}(1,:).*gg{k}-g{k}(2,:).*g{k}(3,:))+g{k}(1,:);...
                      bound_data(k).K.*(gg{k}.^2-g{k}(3,:).^2)+gg{k}];
        end

        % building added mass matrix ======================================

        for s=1:nb_solids
            for l=1:3
                for j=1:nb_solids
                    for k=1:3
                        Mf(3*(s-1)+l,3*(j-1)+k) =...
                     0.5*rhof*(bound_data(s).dt*...
                      sum(bound_data(s).dl.*g{s}(l,:).*uu{s}(:,3*(j-1)+k)')...
                             + bound_data(j).dt*...
                             sum(bound_data(j).dl.*g{j}(k,:).*uu{j}(:,3*(s-1)+l)'));
                    end
                end
            end
        end
        
        % Mass matrix of the system =======================================
        M = Ms + Mf;
        M1 = dpq'*M*dpq; % reduced mass matrix
        % =================================================================
        
        % computing other terms of the ode ================================
        W = dpq*dpp+dcq*dc;
        W1 = reshape(W,3,nb_solids);
        
        Z = dppqdp*dpp+2*dpcqdc*dpp+dccqdc*dc+dcq*dttc;
        
        %==================================================================
        % term <dqM,W>W-1/2W^t<dqM,.>W of the ode =========================

        for s = 1:nb_solids
            for l = 1:3
                S1p = 0;
                S2p = 0;
                for k = 1:nb_solids
                    S1p = S1p+bound_data(k).dt*...
                        sum(bound_data(k).dl.*duu{k}(:,3*(s-1)+l)'.*(duu{k}*W)'.*(W1(:,k)'*g{k}));
                    S2p = S2p+bound_data(k).dt*...
                        sum(bound_data(k).dl.*uu{k}(:,3*(s-1)+l)'.*...
                        (W1(:,k)'*(G{k,1}*W1(1,k)+G{k,2}*W1(2,k)+G{k,3}*W1(3,k))));
                end;
                S1(3*(s-1)+l) = S1p;
                S2(3*(s-1)+l) = S2p;
                S3(3*(s-1)+l) = bound_data(s).dt*...
                        sum(bound_data(s).dl.*g{s}(l,:).*((duu{s}*W).^2)');
                S4(3*(s-1)+l) = bound_data(s).dt*...
                    sum(bound_data(s).dl.*g{s}(l,:).*((W1(:,s)'*g{s}).^2));

                % buoyant force ==========================================
                B1(3*(s-1)+l) = 9.80665*(rhos(s)-rhof)*bound_data(s).dt*...
                               sum(bound_data(s).dl.*g{s}(l,:).*X{s}(2,:));
            end;
        end;
        %==================================================================
        % collisions
        V = zeros(1,3*nb_solids);
        
        if gene_data.collisions
            for ki = 1:nb_solids
                W2=zeros(3,nb_solids);
                for kj = 1:nb_boundaries
                    if kj ~= ki
                        dx1 = repmat(X{kj}(1,:),m(ki),1) - repmat((X{ki}(1,:))',1,m(kj));
                        dx2 = repmat(X{kj}(2,:),m(ki),1) - repmat((X{ki}(2,:))',1,m(kj));
                        dist2 =   dx1.^2  + dx2.^2;
                        big_mat =   (bound_data(ki).dl'*bound_data(kj).dl).*(dist2.^(-alpha)) ...
                            .*(2*alpha*( dx1.*repmat((N{ki}(1,:)./bound_data(ki).dl)',1,m(kj))...
                            + dx2.*repmat((N{ki}(2,:)./bound_data(ki).dl)',1,m(kj)))./dist2 ...
                            + repmat(bound_data(ki).K',1,m(kj)) );
                        for k = 1:3
                            W2(k,kj) = dt(ki)*dt(kj)*sum(sum(big_mat.*repmat(g{ki}(k,:)',1,m(kj))));
                        end
                    end
                    for k = 1:3
                        V(3*(ki-1)+k) = epsilon^alpha*sum(W2(k,:));
                    end
                end
            end
        end
        
        V1 = dpq'*V';
        %==================================================================
        % right hand side term
        
        F1 = rhof*dpq'*(-S1+0.5*S3+S2+0.5*S4);
        F2 = dpq'*M*Z;
        B = dpq'*B1;
        
        %++++++++++++++
        F = F1+F2-B+V1;
        %++++++++++++++

        %=========================================
        dP = [-M1\F;dpp];
        %=========================================
    end

%##########################################################################
% Appendix II
% function mnistrom_new
%##########################################################################

    function [uu,duu] = mnistrom(X,N)

       % this function computes the potentials and its tangential  
       % derivative on the boundaries of the fluid

        for j = 1:nb_solids
            phi{j,1} = N{j}(1,:);
            phi{j,2} = N{j}(2,:);
            phi{j,3} = bound_data(j).flux.*bound_data(j).dl;
            for k = 1:3
                toto{j,k} = real(ifft(fft(phi{j,k})./[1,1:n(j),n(j):-1:1]));
            end
        end

        for ki = 1:nb_boundaries

            for kj = 1:nb_boundaries

                % computing the matricial bloc A(ki,kj)
                dx1 = repmat(X{kj}(1,:),m(ki),1) - repmat((X{ki}(1,:))',1,m(kj));
                dx2 = repmat(X{kj}(2,:),m(ki),1) - repmat((X{ki}(2,:))',1,m(kj));
                dist2 =   dx1.^2  + dx2.^2;
                A(mc(ki)+1:mc(ki+1),mc(kj)+1:mc(kj+1)) = dt(kj)*(dx1.*repmat(N{kj}(1,:),m(ki),1)...
                    + dx2.*repmat(N{kj}(2,:),m(ki),1))./dist2;
                if ki == kj
                    % the diagonal bloc A(ki,kj) must be recomputed as
                    % follows :
                    id_diag = sub2ind([mtot,mtot],mc(ki)+1:mc(ki+1),mc(ki)+1:mc(ki+1));
                    A(id_diag) = -pi - 0.5*dt(ki)*bound_data(ki).K.*bound_data(ki).dl;
                end

                % second right hand side
                if kj <= nb_solids
                    if  kj ~= ki
                        J0 = 3*(kj-1);
                        for j=1:3
                            b(mc(ki)+1:mc(ki+1),J0+j) = 0.5*dt(kj)*log(dist2)*phi{kj,j}';
                        end
                    else  % ki == kj  the kernel has to be splited into two parts
                        temp = abs(sqrt(dist2)./(2*sin(0.5*(repmat(bound_data(ki).t,m(ki),1)...
                            -repmat(bound_data(ki).t',1,m(ki))))));
                        id_diag = sub2ind([m(ki),m(ki)],1:m(ki),1:m(ki));
                        temp(id_diag) = bound_data(ki).dl;
                        temp = log( exp(0.5)*temp );
                        J0 = 3*(ki-1);
                        for j=1:3
                            b(mc(ki)+1:mc(ki+1),J0+j) = dt(ki)* (temp * phi{ki,j}') - pi*toto{ki,j}';
                        end
                    end
                end
            end
        end

        % computation of the potentials
        if gene_data.pb_type == 1;
            nA = size(A,1);
            A(1:end-1,:) = A(1:end-1,:) - ones(nA-1,1)*A(end,:);
            b(1:end-1,:) = b(1:end-1,:) - ones(nA-1,1)*b(end,:);
            A(end,:) = discret_data.last_line;
            b(end,:) = 0;
        end;

        u = A\b;
        %------------------------------
        uu = cell(nb_solids,1); 
        duu = cell(nb_solids,1); 
        %==============================
        
        % computation of the tangiential derivative
        for j=1:nb_solids
            uu{j}=u(mc(j)+1:mc(j+1),:);
            duu{j}=ifft(i*([0:n(j),-n(j):-1]'*...
                ones(1,3*nb_solids)).*fft(uu{j}))./(bound_data(j).dl'*ones(1,3*nb_solids));
        end
    end
%##########################################################################

end

Contact us at files@mathworks.com