function LV_competition_function_2species(N,L,D,P,dsc,pillarq,R,dx_mult,model,rep) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Nick Lowery and Tristan Ursell % 2018 % % Structured environments fundamentally alter dynamics and stability of ecological communities % https://www.biorxiv.org/content/early/2018/07/10/366559 % % This function runs a non-dimensionalized modified 2D Lotka-Volterra ecological % simulation of a two species mutual-killer community, and outputs images of % the simulation, mean abundance trajectories, and a file containing the associated % metadata. This version of the script is useful for creating trajectories, % visualizing the output. It is not suitable for large-scale computational analysis, % in part because this version *does not* use scale factors for the data. % % Input parameters: % N = number of time steps in simulation (N*dt = # doubling times) % L = size of the simulation box (pixels) % D = diffusion coefficient (dimensionless) % P = interaction strength (dimensionless) % dsc = discrete cutoff below which fields are set to 0 % pillarq = logical (1,0); should pillars be included? % R = pillar radius (pixels) % dx_mult = center-to-center pillar spacing (units of R) % model = which model to use; 1 = modified model in the text, 2 = standard LV model % rep = seed used for random number generator (integer) % stamp = UNIQUE replicate identifier % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %{ %Fig. 2 and SI Fig. 7 LV_competition_function_2species(120000,500,15,0.1,0.001,0,15,3.5,1,567) %no pillars, model 1, dsc = 0.001 LV_competition_function_2species(120000,500,15,0.1,0.001,0,15,3.5,2,567) %no pillars, model 2, dsc = 0.001 LV_competition_function_2species(120000,500,15,0.1,0.001,1,15,3.5,1,567) %pillars, model 1, dsc = 0.001 LV_competition_function_2species(120000,500,15,0.1,0.001,1,15,3.5,2,567) %pillars, model 2, dsc = 0.001 %} %******************************************* %********* INITIALIZATION ****************** %******************************************* rng(rep) %output images? imageq = 1; %time step in ND units dt = 0.01; %local concentration discrete cutoff (will be set to 0 below this value) %dsc = 0.001; %frequency to show results showt = 100; %intialize the density matrices A = 4/10 * rand(L,L); B = 4/10 * rand(L,L); %unique simulation stamp stamp1=num2str(round(now*1000000)); %diffusion convolution filter %<---- changed from 3 to 5 sigma width. cfilt_sz=7; sigma = sqrt(4*D*dt); if mod(ceil(cfilt_sz*sigma),2) == 0 win_sz = ceil(cfilt_sz*sigma) + 1; else win_sz = ceil(cfilt_sz*sigma); end Gauss = fspecial('gaussian', [win_sz,win_sz], sigma); rescale_filt = 1 ./ conv2(ones(size(A)), Gauss, 'same'); if L < win_sz warning('Diffusion window size is larger than simulation box.') end %tracking vectors (global mean, scale factor for each image) meanA = zeros(round(N/showt),1); meanB = zeros(round(N/showt),1); %******************************************* %********* PILLARS ************************* %******************************************* if pillarq == 1 %create spatial matrices / filters Xmat = ones(L,1)*(1:L); Ymat = Xmat'; %create centers for obstruction pattern dx = dx_mult*R; %form hexagonal grid position arrays x_cent0 = 0:dx:(L+dx); y_cent0 = [0:(sqrt(3)/2*dx):(L+dx*sqrt(3)/2)] + R; x_cent = zeros(length(y_cent0), length(x_cent0)); for i = 1:length(y_cent0) if mod(i,2) == 1 x_cent(i,:) = x_cent0 + dx/4; else x_cent(i,:) = x_cent0 - dx/4; end end y_cent = zeros(size(x_cent)); for j = 1:length(x_cent0) y_cent(:,j) = y_cent0'; end %create pillars filt_all = ones(size(Xmat)); for i = 1:length(x_cent0) for j = 1:length(y_cent0) filt_temp = ((Xmat - x_cent(j,i)) .^ 2 + (Ymat - y_cent(j,i)) .^ 2) >= R^2; filt_all = filt_all .* filt_temp; end end dbound = -L/2; filt_all_temp = zeros(size(filt_all)); filt_all_temp(1:round(L/2) - dbound,:) = filt_all(1:round(L/2) - dbound,:); filt_all_temp(round(L/2) - dbound + 1:end,:) = 1; filt_all = filt_all_temp; %prepare reflecting boundary conditions in grid conv_filt = conv2(filt_all, Gauss, 'same'); rescale_filt = filt_all ./ conv_filt; rescale_filt(isnan(rescale_filt)) = 0; else R = 0; dx = 0; filt_all = ones(L,L); end list1=(filt_all == 1); %calculate weight of non-pillar area for calcualting mean abundances filt_all_weight = sum(filt_all(:)); %file IO if imageq == 1 path0 = cd; file1 = ['2sp_LV_stamp-' num2str(stamp1)... '_N-' num2str(N) ... '_L-' num2str(L) ... '_D-' num2str(D) ... '_P-' num2str(P) ... '_pillars-' num2str(pillarq) ... '_R-' num2str(R) ... '_dx-' num2str(dx_mult*R) ... '_dsc-' num2str(dsc) ... '_model-' num2str(model) ... '_rep-' num2str(rep) ]; path1 = fullfile(path0, file1); mkdir(path1) end %******************************************* %********** SIMULATION ********************* %******************************************* q=0; for i = 1:N %diffusion convolutions A = conv2(A, Gauss, 'same') .* rescale_filt; B = conv2(B, Gauss, 'same') .* rescale_filt; if model==1 %***** MODIFIED LV MODEL *********************** %hard bound at local three-component carrying capacity L1 = (1 - (A+B)); L1(L1<0) = 0; %interspecies interactions dA = dt * A .* L1 .* (1 - B/P); dB = dt * B .* L1 .* (1 - A/P); %*********************************************** end if model==2 %***** CANONICAL LV MODEL *********************** %hard bound at local three-component carrying capacity L1 = (1 - (A+B)); L1(L1<0) = 0; %interspecies interactions dA = dt * ((A .* L1) - (A .* B)/P); dB = dt * ((B .* L1) - (A .* B)/P); %*********************************************** end %deterministic update populations A = A + dA; B = B + dB; %hard upper bound at carrying ccapacity A(A > 1) = 1; B(B > 1) = 1; %discrete cutoff (hard lower bound) A(A < dsc) = 0; B(B < dsc) = 0; %output images if mod(i,showt) == 1 q=q+1; disp(['step ' num2str(i) ' of ' num2str(N) ' for ' file1]) %calculate tracking vectors if pillarq == 1 meanA(q) = sum(A(list1)) / filt_all_weight; meanB(q) = sum(B(list1)) / filt_all_weight; else meanA(q) = mean(A(:)); meanB(q) = mean(B(:)); end %grey filter grey_filt = (A == 0) .* (B == 0); Atemp = A; Btemp = B; if pillarq == 1 Atemp(grey_filt == 1) = 1 / 2; Btemp(grey_filt == 1) = 1 / 2; end %save images im_out(:,:,1) = uint8(Atemp * 255); im_out(:,:,2) = uint8(Btemp * 255); im_out(:,:,3) = uint8(Atemp * 255); if imageq == 1 image_save(im_out, fullfile(path1, [file1 '_' sprintf('%04d',q) '.png'])) end %end simulation if it ceases to evolve if q>20 cond1=abs(meanA(q-10)-meanA(q))<(0.001)/L^2; cond2=abs(meanB(q-10)-meanB(q))<(0.001)/L^2; if and(cond1,cond2) disp(['Simulation terminated at N = ' num2str(i) ', due to slow evolution.']) break end end end end %******************************************* %********** METADATA *********************** %******************************************* save([file1 '_metadata.mat'], 'L', 'N', 'D', 'P', 'pillarq', 'model', 'R', 'dx', 'dt', 'dsc', ... 'meanA', 'meanB', 'rep', 'filt_all') disp(['Done with ' file1 '.']) end %********************************************************************** %Image Stack Saver %Tristan Ursell, February 2012 % % image_save(Im1,basename) % image_save(Im1,basename,fmax) % %For use with appended image stacks, e.g. most commonly stacked TIF files. %Some operating systems throw an error indicating that the file is %inaccessible at the time of write, which leads to a code fault and can be %very frustrating. This simple script fixes that issues, mainly on the %Windows operating system. % % Im1 = the current image (matrix) you want to add to the stack % % basename = is a string that specifices the file name, and potentially the % path of the stack to save to. Best practice is the put '.tif' at the end % of the file name. % % fmax = maximum number of allowed failures, i.e. if the script attempts to % write to the file 'fmax' times and fails, it will give up. This prevents % entering an infinite loop. The default value is fmax = 10. % % Any of the 'imwrite' parameters can be modified below on line 38. % function image_save(Im1,basename,varargin) if ~isempty(varargin) fmax=varargin{1}; else fmax=10; end er1=0; f=0; while and(er1==0,f