FRC continuation equilibrium point
Contents
- FRC_cont_ep
- Checking whether internal resonance indeed happens
- SSM computation of autonomous part
- SSM computation of non-autonomous part
- Construct COCO-compatible vector field
- continuation of reduced dynamics w.r.t. parName
- extract results of reduced dynamics at sampled frequencies
- FRC in physical domain
function varargout = FRC_cont_ep(obj,i,resModes,ORDER,mFreqs,parName,parRange)
FRC_cont_ep
This function performs continuation of equilibrium points of slow dynamics. Each equilibirum point corresponds to a periodic orbit in the regular time dynamics. The continuation here starts from the guess of initial solution.
FRC = FRC_CONT_EP(OBJ,OID,RESMODES,ORDER,MFREQS,PARNAME,PARRANGE)
i
: number of subinterval
resModes
: master subspace
order
: expansion order of SSM
mFreqs
: internal resonance relation vector
parName
: amp/freq continuation parameter
-
parRange
: continuation domain of parameter, which should be near the value of natural frequency with index 1 in themFreq
if the continuation parameter isfreq
m = numel(mFreqs); assert(numel(resModes)==2*m, 'The master subspace is not %dD.',2*m); % get options [nt,nPar,nCycle,sampStyle,initialSolver,outdof,saveIC,coordinates,p0,z0] = ... deal(obj.FRCOptions.nt, obj.FRCOptions.nPar, obj.FRCOptions.nCycle, ... obj.FRCOptions.sampStyle, obj.FRCOptions.initialSolver, ... obj.FRCOptions.outdof, obj.FRCOptions.saveIC, ... obj.FRCOptions.coordinates, obj.FRCOptions.p0, obj.FRCOptions.z0);
Checking whether internal resonance indeed happens
if isempty(obj.System.spectrum) [~,~,~] = obj.System.linear_spectral_analysis(); end % eigenvalues Lambda is sorted in descending order of real parts % positive imaginary part is placed first in each complex pair lambda = obj.System.spectrum.Lambda(resModes); lambdaRe = real(lambda); lambdaIm = imag(lambda); % check spectrum check_spectrum(lambdaRe,lambdaIm,mFreqs);
SSM computation of autonomous part
obj.choose_E(resModes) % compute autonomous SSM coefficients ORDER = sort(ORDER); [W_0,R_0] = obj.compute_whisker(ORDER(end)); for jj = 1:numel(ORDER)
order = ORDER(jj); oid = ['freqSubint',num2str(i),'Order',num2str(order)]; if jj>1 % take lowest order solution as initial guess old_oid = ['freqSubint',num2str(i),'Order',num2str(ORDER(1))]; sol_jminus1 = ep_read_solution('', [old_oid,'.ep'], 1); p0 = sol_jminus1.p; z0 = sol_jminus1.x; end % extract ind and coeff of expansion in reduced dynamics and check % consistency. Here beta is coeff and kappa denotes ind [beta,kappa] = extract_beta_kappa(m,order,R_0,mFreqs);
SSM computation of non-autonomous part
[iNonauto, rNonauto, kNonauto, W_1] = extract_red_nonauto(obj,mFreqs,lambdaIm,order,W_0,R_0,parName); % Here iNonauto,rNonauto and kNonauto gives the indices for resonant happens % value of leading order contribution and (pos) kappa indices with resonance
Construct COCO-compatible vector field
fdata = struct(); fdata.beta = beta; fdata.kappa = kappa; fdata.lamdRe = lambdaRe(1:2:end-1); fdata.lamdIm = lambdaIm(1:2:end-1); fdata.mFreqs = mFreqs; fdata.iNonauto = iNonauto; fdata.rNonauto = rNonauto; fdata.kNonauto = kNonauto; % put W_0 and W_1 in fdata is a bad idea because it will be stored in disk % for each saved continuation solution. As an alternative, we save W_0 and % W_1 in disk here under folder data. When needed, they will be loaded into % memory. % fdata.W_0 = W_0; % fdata.W_1 = W_1; data_dir = fullfile(pwd,'data'); if ~exist(data_dir, 'dir') mkdir(data_dir); end wdir = fullfile(data_dir,'SSM.mat'); SSMcoeffs = struct(); SSMcoeffs.W_0 = W_0(1:order); SSMcoeffs.W_1 = W_1; save(wdir, 'SSMcoeffs'); fdata.order = order; fdata.modes = resModes; ispolar = strcmp(coordinates, 'polar'); fdata.ispolar = ispolar; fdata.isbaseForce = obj.System.Options.BaseExcitation; if ispolar odefun = @(z,p) ode_2mDSSM_polar(z,p,fdata); else odefun = @(z,p) ode_2mDSSM_cartesian(z,p,fdata); end funcs = {odefun};
continuation of reduced dynamics w.r.t. parName
prob = coco_prob(); prob = cocoSet(obj.contOptions, prob); if isempty(p0) if ~isempty(obj.System.fext) p0 = [obj.System.Omega; obj.System.fext.epsilon]; else p0 = [obj.System.Omega; obj.System.Fext.epsilon]; end end if isempty(z0) if ispolar z0 = 0.1*ones(2*m,1); else z0 = zeros(2*m,1); end end % construct initial guess equilibrium points z0 = get_initial_sol(z0,p0,initialSolver,odefun,nCycle,ispolar); % call ep-toolbox prob = ode_isol2ep(prob, '', funcs{:}, z0, {'om' 'eps'}, p0); % define monitor functions to state variables - depends on ispolar, the % output args1/2 could be rho/theta or Real/Imag(z) [prob,args1,args2] = monitor_states(prob,ispolar,m); switch parName case 'freq' isomega = true; cont_args = [{'om'},args1(:)' ,args2(:)',{'eps'}]; case 'amp' isomega = false; cont_args = [{'eps'},args1(:)' ,args2(:)',{'om'}]; otherwise error('Continuation parameter should be freq or amp'); end % add events in the case of uniform sampling if strcmp(sampStyle, 'uniform') if isomega omSamp = linspace(parRange(1),parRange(2), nPar); prob = coco_add_event(prob, 'UZ', 'om', omSamp); else epSamp = linspace(parRange(1),parRange(2), nPar); prob = coco_add_event(prob, 'UZ', 'eps', epSamp); end end runid = coco_get_id(oid, 'ep'); fprintf('\n Run=''%s'': Continue equilibria along primary branch.\n', ... runid); coco(prob, runid, [], 1, cont_args, parRange);
extract results of reduced dynamics at sampled frequencies
FRC = ep_reduced_results(runid,sampStyle,ispolar,isomega,args1,args2,'plot-off');
FRC in physical domain
om = FRC.om; epsf = FRC.ep; state = FRC.z; numOm = numel(om); noutdof = numel(outdof); Zout_frc = cell(numOm,1); Znorm_frc = zeros(numOm,1); Aout_frc = zeros(numOm,noutdof); if saveIC Z0_frc = zeros(numOm,obj.System.N); % initial state end timeFRCPhysicsDomain = tic; % Loop around a resonant mode if obj.FRCOptions.nonAutoParRedCom nKappa = obj.System.nKappa; kappa_set = zeros(nKappa,1); F_kappa = zeros(obj.dimSystem,nKappa); % each column corresponds to one kappa for ka_idx = 1:nKappa kappa_set(ka_idx) = obj.System.Fext.data(ka_idx).kappa; F_kappa(:,ka_idx) = obj.System.Fext.data(ka_idx).F_n_k(1).coeffs; end A = obj.System.A; % A matrix B = obj.System.B; % B matrix W_M = obj.E.adjointBasis; % Right eigenvectors of the modal subspace V_M = obj.E.basis; % Left eigenvectors of the modal subspace reltol = obj.Options.reltol; % *Near external resonances and Leading order reduced dynamics* Lambda_M_vector = obj.E.spectrum; % transfer minimum data for reducing communication load W1 = leading_order_nonauto_SSM(A,B,W_M,V_M,Lambda_M_vector,kappa_set,F_kappa,reltol,kNonauto,iNonauto,rNonauto,om); % map it back to physical coordinates for j=1:numel(om) % Forced response in Physical Coordinates statej = state(j,:); % create compatible W1j idle = repmat(struct('coeffs',[],'ind',[]),1 , 1); W1j = repmat(struct('kappa' ,[],'W',idle),nKappa, 1); assert(norm(kappa_set-W1{j}.kappas)<1e-6,'the orders of kappa are not consistent'); for i = 1:nKappa W1j(i).kappa = kappa_set(i); W1j(i).W(1).coeffs = W1{j}.coeffs(:,i); W1j(i).W(1).ind = sparse(obj.dimManifold,1); end if obj.System.Options.BaseExcitation epsf(j) = epsf(j)*(om(j))^2; end [Aout, Zout, z_norm, Zic] = compute_full_response_2mD_ReIm(W_0(1:order), W1j, statej, epsf(j), nt, mFreqs, outdof); % collect output in array Aout_frc(j,:) = Aout; Zout_frc{j} = Zout; Znorm_frc(j) = z_norm; if saveIC Z0_frc(j,:) = Zic; % initial state end end else parfor j = 1:numel(om) % compute non-autonomous SSM coefficients if isomega set_omega(obj,om(j)); if obj.Options.contribNonAuto [W_1j, R_1] = obj.compute_perturbed_whisker(0,[],[]); R_10 = R_1(kNonauto).R.coeffs; assert(~isempty(R_10), 'Near resonance does not occur, you may tune tol'); f = R_10(2*iNonauto-1); assert(norm(f-rNonauto)<1e-3*norm(f), 'inner resonance assumption does not hold'); % fprintf('the forcing frequency %.4d is nearly resonant with the eigenvalue %.4d + i%.4d\n', om(j), real(lambda(1)),imag(lambda(1))) else W_1j = []; end else W_1j = W_1; end % Forced response in Physical Coordinates if obj.System.Options.BaseExcitation epsf(j) = epsf(j)*(om(j))^2; end statej = state(j,:); [Aout, Zout, z_norm, Zic] = compute_full_response_2mD_ReIm(W_0(1:order), W_1j, statej, epsf(j), nt, mFreqs, outdof); % collect output in array Aout_frc(j,:) = Aout; Zout_frc{j} = Zout; Znorm_frc(j) = z_norm; if saveIC Z0_frc(j,:) = Zic; % initial state end end end maptime = toc(timeFRCPhysicsDomain); fprintf(['Time spent on mapping the FRC to physical coordinates maptime ' num2str(maptime) ' seconds. \n'])
Record output
FRC.Aout_frc = Aout_frc; FRC.Zout_frc = Zout_frc; FRC.Znorm_frc = Znorm_frc; if saveIC FRC.Z0_frc = Z0_frc; % initial state end FRCinfo = struct(); FRCinfo.timeFRCPhysicsDomain = toc(timeFRCPhysicsDomain); FRCinfo.SSMorder = order; FRCinfo.SSMnonAuto = obj.Options.contribNonAuto; FRCinfo.SSMispolar = ispolar; % convert results to cell array FRC = array2structArray(FRC,saveIC); % Plot Plot FRC in system coordinates % if isomega % plot_FRC_full(FRC,outdof,order,'freq','lines'); % else % plot_FRC_full(FRC,outdof,order,'freq','lines'); % end FRCs{jj} = FRC;
end varargout{1} = FRCs; fdir = fullfile(data_dir,runid,'SSMep.mat'); save(fdir, 'FRC','FRCinfo');
end function check_spectrum(lambdaRe,lambdaIm,mFreqs) flags1 = abs(lambdaRe(1:2:end-1)-lambdaRe(2:2:end))<1e-6*abs(lambdaRe(1:2:end-1)); % same real parts flags1 = all(flags1); flags2 = abs(lambdaIm(1:2:end-1)+lambdaIm(2:2:end))<1e-6*abs(lambdaIm(1:2:end-1)); % opposite imag parts flags2 = all(flags2); freqs = lambdaIm(1:2:end-1); freqso = freqs - dot(freqs,mFreqs(:))*mFreqs(:)/sum(mFreqs.^2); flags3 = norm(freqso)<0.1*norm(freqs); assert(flags1, 'Real parts do not follow complex conjugate relation'); assert(flags2, 'Imaginary parts do not follow complex conjugate relation'); assert(flags3, 'Internal resonnace is not detected for given master subspace'); end function [beta,kappa] = extract_beta_kappa(m,order,R_0,mFreqs) beta = cell(m,1); % coefficients - each cell corresponds to one mode kappa = cell(m,1); % exponants Em = eye(m); for k = 2:order R = R_0(k); coeffs = R.coeffs; ind = R.ind; if ~isempty(coeffs) for i=1:m betai = coeffs(2*i-1,:); [~,ki,betai] = find(betai); kappai = ind(ki,:); % check resonant condition l = kappai(:,1:2:end-1); j = kappai(:,2:2:end); nk = numel(ki); rm = repmat(mFreqs(:)',[nk,1]); flagi = dot(l-j-repmat(Em(i,:),[nk,1]),rm,2); assert(all(flagi==0), 'Reduced dynamics is not consisent with desired IRs'); % assemble terms beta{i} = [beta{i} betai]; kappa{i} = [kappa{i}; kappai]; end end end end function [iNonauto, rNonauto, kNonauto, W_1] = extract_red_nonauto(obj,mFreqs,lambdaIm,order,W0,R0,parName) iNonauto = []; % indices for resonant happens rNonauto = []; % value of leading order contribution kNonauto = []; % (pos) kappa indices with resonance kappa_set= obj.System.kappas; % each row corresponds to one kappa kappa_pos = kappa_set(kappa_set>0); num_kappa = numel(kappa_pos); % number of kappa pairs for k=1:num_kappa kappak = kappa_pos(k); idm = find(mFreqs(:)==kappak); % idm could be vector if there are two frequencies are the same if strcmp(parName,'freq') obj.System.Omega = lambdaIm(2*idm(1)-1); end [W_1, R_1] = obj.compute_perturbed_whisker(0,[],[]); idk = find(kappa_set==kappak); R_10 = R_1(idk).R.coeffs; r = R_10(2*idm-1); iNonauto = [iNonauto; idm]; rNonauto = [rNonauto; r]; kNonauto = [kNonauto; idk]; end end function z0 = get_initial_sol(z0,p0,initialSolver,odefun,nCycle,ispolar) switch initialSolver case 'fsolve' % fsolve to approximate equilibrium fsolveOptions = optimoptions('fsolve','MaxFunctionEvaluations',100000,... 'MaxIterations',1000000,'FunctionTolerance', 1e-10,... 'StepTolerance', 1e-8, 'OptimalityTolerance', 1e-10); z0 = fsolve(@(z) odefun(z,p0),z0,fsolveOptions); case 'forward' % forward simulation to approach equilibirum tspan = [0 nCycle*2*pi/p0(1)]; %nCycle odefw = @(t,z,p) odefun(z,p); opts = odeset('RelTol',1e-2,'AbsTol',1e-4); [~,y0] = ode45(@(t,y) odefw(t,y,p0), tspan, z0, opts); [~,y] = ode45(@(t,y) odefw(t,y,p0), [0 2*pi/p0(1)], y0(end,:)); [~, warnId] = lastwarn; if any(isnan(y(:))) || strcmp(warnId,'MATLAB:ode45:IntegrationTolNotMet') warning('Reduced dynamics with IRs in polar form diverges with [0.1 0.1 0.1 0.1]'); else z0 = y(end,:)'; end end if ispolar % regularize initial solution if it is in polar form z0(2:2:end) = mod(z0(2:2:end),2*pi); % phase angles in [0,2pi] m = numel(z0)/2; for k=1:m if z0(2*k-1)<0 z0(2*k-1) = -z0(2*k-1); % positive amplitudes z0(2*k) = z0(2*k)+pi; end end end end function [prob,varargout] = monitor_states(prob,ispolar,m) if ispolar rhoargs = cell(m,1); thargs = cell(m,1); for k=1:m rhoargs{k} = strcat('rho',num2str(k)); thargs{k} = strcat('th',num2str(k)); end prob = coco_add_pars(prob, 'radius', 1:2:2*m-1, rhoargs(:)'); prob = coco_add_pars(prob, 'angle', 2:2:2*m, thargs(:)'); varargout{1} = rhoargs; varargout{2} = thargs; else Reargs = cell(m,1); Imargs = cell(m,1); for k=1:m Reargs{k} = strcat('Rez',num2str(k)); Imargs{k} = strcat('Imz',num2str(k)); end prob = coco_add_pars(prob, 'realParts', 1:2:2*m-1, Reargs(:)'); prob = coco_add_pars(prob, 'imagParts', 2:2:2*m, Imargs(:)'); varargout{1} = Reargs; varargout{2} = Imargs; end end function FRCy = array2structArray(FRCx,saveIC) om = FRCx.om; state = FRCx.z; rho = FRCx.rho; th = FRCx.th; ep = FRCx.ep; st = FRCx.st; SNidx = FRCx.SNidx; HBidx = FRCx.HBidx; Aout = FRCx.Aout_frc; Zout = FRCx.Zout_frc; Znorm = FRCx.Znorm_frc; if saveIC Zic = FRCx.Z0_frc; end numPts = numel(om); FRCy = cell(numPts,1); for i=1:numPts FRC = struct('rho', rho(i,:), 'stability', st(i), 'Omega', om(i) ,... 'epsilon', ep(i), 'Aout', Aout(i,:), 'Zout', Zout{i},... 'Znorm', Znorm(i), 'Zic', [], 'isSN', false, 'isHB', false, ... 'state', state(i,:), 'th', th(i,:)); if saveIC FRC.Zic = Zic(i,:); end if ismember(i,SNidx) FRC.isSN = true; end if ismember(i,HBidx) FRC.isHB = true; end FRCy{i} = FRC; end FRCy = cat(1,FRCy{:}); end function set_omega(obj,omega) obj.System.Omega = omega; end