Matlab/CUDA: ocean wave simulation - matlab
I've studied "Simulating Ocean Water" article by Jerry Tessendorf and tried to program the Statistical Wave Model but I didn't get correct result and I don't understand why.
In my program I tried only to create a wave height field at time t = 0 without any further changes in time. After execution of my program I got not what I was expecting:
Here's my source code:
clear all; close all; clc;
rng(11); % setting seed for random numbers
meshSize = 64; % field size
windDir = [1, 0]; % ||windDir|| = 1
patchSize = 64;
A = 1e+4;
g = 9.81; % gravitational constant
windSpeed = 1e+2;
x1 = linspace(-10, 10, meshSize+1); x = x1(1:meshSize);
y1 = linspace(-10, 10, meshSize+1); y = y1(1:meshSize);
[X,Y] = meshgrid(x, y);
H0 = zeros(size(X)); % height field at time t = 0
for i = 1:meshSize
for j = 1:meshSize
kx = 2.0 * pi / patchSize * (-meshSize / 2.0 + x(i)); % = 2*pi*n / Lx
ky = 2.0 * pi / patchSize * (-meshSize / 2.0 + y(j)); % = 2*pi*m / Ly
P = phillips(kx, ky, windDir, windSpeed, A, g); % phillips spectrum
H0(i,j) = 1/sqrt(2) * (randn(1) + 1i * randn(1)) * sqrt(P);
end
end
H0 = H0 + conj(H0);
surf(X,Y,abs(ifft(H0)));
axis([-10 10 -10 10 -10 10]);
And the phillips function:
function P = phillips(kx, ky, windDir, windSpeed, A, g)
k_sq = kx^2 + ky^2;
L = windSpeed^2 / g;
k = [kx, ky] / sqrt(k_sq);
wk = k(1) * windDir(1) + k(2) * windDir(2);
P = A / k_sq^2 * exp(-1.0 / (k_sq * L^2)) * wk^2;
end
Is there any matlab ocean simulation source code which could help me to understand my mistakes? Fast google search didn't get any results.
Here's a "correct" result I got from "CUDA FFT Ocean Simulation". I didn't achieve this behavior in Matlab yet but I've ploted "surf" in matlab using data from "CUDA FFT Ocean Simulation". Here's what it looks like:
I've made an experiment and got an interesting result:
I've taken generated h0 from "CUDA FFT Ocean Simulation". So I have to do ifft to transform from frequency domain to spatial domain to plot the graph. I've done it for the same h0 using matlab ifft and using cufftExecC2C from CUDA library. Here's the result:
CUDA ifft:
Matlab ifft:
Either I don't understand some aspects of realization of cufftExecC2C or cufftExecC2C and matlab ifft are different algorithms with different results.
By the way parameters for generating such surface are:
meshSize = 32
A = 1e-7
patchSize = 80
windSpeed = 10
Well that was definitely a funny exercise. This is a completely rewritten answer since you found the issues you were asking about by yourself.
Instead of deleting my answer, there is still merit in posting to help you vectorize and/or explain a few bits of code.
I completely rewrote the GUI I gave in my former answer in order to incorporate your changes and add a couple of options. It started to grew arms and legs so I won't put the listing here but you can find the full file there:
ocean_simulator.m.
This is completely self contained and it includes all the calculating functions I vectorized and list separately below.
The GUI will allow you to play with the parameters, animate the waves, export GIF file (and a few other options like the "preset", but they are not too ironed out yet). A few examples of what you can achieve:
Basic
This is what you get with the quick default settings, and a couple of rendering options. This uses a small grid size and a fast time step, so it runs pretty quickly on any machine.
I am quite limited at home (Pentium E2200 32bit), so I could only practice with limited settings. The gui will run even with the settings maxed but it will become to slow to really enjoy.
However, with a quick run of ocean_simulator at work (I7 64 bit, 8 cores, 16GB ram, 2xSSD in Raid), it makes it much more fun! Here are a few examples:
Although done on a much better machine, I didn't use any parallel functionality nor any GPU calculations, so Matlab was only using a portion of these specs, which means it could probably run just as good on any 64bit system with decent RAM
Windy lake
This is a rather flat water surface like a lake. Even high winds do not produce high amplitude waves (but still a lot of mini wavelets). If you're a wind surfer looking at that from your window on top of the hill, your heart is going to skip a beat and your next move is to call Dave "Man! gear up. Meet you in five on the water!"
Swell
This is you looking from the bridge of your boat on the morning, after having battled with the storm all night. The storm has dissipated and the long large waves are the last witness of what was definitely a shaky night (people with sailing experience will know ...).
T-Storm
And this was what you were up to the night before...
second gif done at home, hence the lack of detail ... sorry
To the bottom:
Finally, the gui will let you add a patch around the water domain. In the gui it is transparent so you could add objects underwater or a nice ocean bottom. Unfortunately, the GIF format cannot include an alpha channel so no transparency here (but if you export in a video then you should be ok).
Moreover, the export to GIF degrade the image, the joint between the domain border and the water surface is flawless if you run that in Matlab. In some case it also make Matlab degrade the rendering of the lighting, so this is definitely not the best option for export, but it allows more things to play within matlab.
Now onto the code:
Instead of listing the full GUI, which would be super long (this post is long enough already), I will just list here the re-written version of your code, and explain the changes.
You should notice a massive increase of speed execution (orders of magnitude), thanks to the remaining vectorization, but mostly for two reasons:
(i) A lot of calculations were repeated. Caching values and reusing them is much faster than recalculating full matrices in loops (during the animation part).
(ii) Note how I defined the surface graphic object. It is defined only once (empty even), then all the further calls (in the loop) only update the underlying ZData of the surface object (instead of re-creating a surface object at each iteration.
Here goes:
%% // clear workspace
clear all; close all; clc;
%% // Default parameters
param.meshsize = 128 ; %// main grid size
param.patchsize = 200 ;
param.windSpeed = 100 ; %// what unit ? [m/s] ??
param.winddir = 90 ; %// Azimuth
param.rng = 13 ; %// setting seed for random numbers
param.A = 1e-7 ; %// Scaling factor
param.g = 9.81 ; %// gravitational constant
param.xLim = [-10 10] ; %// domain limits X
param.yLim = [-10 10] ; %// domain limits Y
param.zLim = [-1e-4 1e-4]*2 ;
gridSize = param.meshsize * [1 1] ;
%% // Define the grid X-Y domain
x = linspace( param.xLim(1) , param.xLim(2) , param.meshsize ) ;
y = linspace( param.yLim(1) , param.yLim(2) , param.meshsize ) ;
[X,Y] = meshgrid(x, y);
%% // get the grid parameters which remain constants (not time dependent)
[H0, W, Grid_Sign] = initialize_wave( param ) ;
%% // calculate wave at t0
t0 = 0 ;
Z = calc_wave( H0 , W , t0 , Grid_Sign ) ;
%% // populate the display panel
h.fig = figure('Color','w') ;
h.ax = handle(axes) ; %// create an empty axes that fills the figure
h.surf = handle( surf( NaN(2) ) ) ; %// create an empty "surface" object
%% // Display the initial wave surface
set( h.surf , 'XData',X , 'YData',Y , 'ZData',Z )
set( h.ax , 'XLim',param.xLim , 'YLim',param.yLim , 'ZLim',param.zLim )
%% // Change some rendering options
axis off %// make the axis grid and border invisible
shading interp %// improve shading (remove "faceted" effect)
blue = linspace(0.4, 1.0, 25).' ; cmap = [blue*0, blue*0, blue]; %'// create blue colormap
colormap(cmap)
%// configure lighting
h.light_handle = lightangle(-45,30) ; %// add a light source
set(h.surf,'FaceLighting','phong','AmbientStrength',.3,'DiffuseStrength',.8,'SpecularStrength',.9,'SpecularExponent',25,'BackFaceLighting','unlit')
%% // Animate
view(75,55) %// no need to reset the view inside the loop ;)
timeStep = 1./25 ;
nSteps = 2000 ;
for time = (1:nSteps)*timeStep
%// update wave surface
Z = calc_wave( H0,W,time,Grid_Sign ) ;
h.surf.ZData = Z ;
pause(0.001);
end
%% // This block of code is only if you want to generate a GIF file
%// be carefull on how many frames you put there, the size of the GIF can
%// quickly grow out of proportion ;)
nFrame = 55 ;
gifFileName = 'MyDancingWaves.gif' ;
view(-70,40)
clear im
f = getframe;
[im,map] = rgb2ind(f.cdata,256,'nodither');
im(1,1,1,20) = 0;
iframe = 0 ;
for time = (1:nFrame)*.5
%// update wave surface
Z = calc_wave( H0,W,time,Grid_Sign ) ;
h.surf.ZData = Z ;
pause(0.001);
f = getframe;
iframe= iframe+1 ;
im(:,:,1,iframe) = rgb2ind(f.cdata,map,'nodither');
end
imwrite(im,map,gifFileName,'DelayTime',0,'LoopCount',inf)
disp([num2str(nFrame) ' frames written in file: ' gifFileName])
You'll notice that I changed a few things, but I can assure you the calculations are exactly the same. This code calls a few subfunctions but they are all vectorized so if you want you can just copy/paste them here and run everything inline.
The first function called is initialize_wave.m
Everything calculated here will be constant later (it does not vary with time when you later animate the waves), so it made sense to put that into a block on it's own.
function [H0, W, Grid_Sign] = initialize_wave( param )
% function [H0, W, Grid_Sign] = initialize_wave( param )
%
% This function return the wave height coefficients H0 and W for the
% parameters given in input. These coefficients are constants for a given
% set of input parameters.
% Third output parameter is optional (easy to recalculate anyway)
rng(param.rng); %// setting seed for random numbers
gridSize = param.meshsize * [1 1] ;
meshLim = pi * param.meshsize / param.patchsize ;
N = linspace(-meshLim , meshLim , param.meshsize ) ;
M = linspace(-meshLim , meshLim , param.meshsize ) ;
[Kx,Ky] = meshgrid(N,M) ;
K = sqrt(Kx.^2 + Ky.^2); %// ||K||
W = sqrt(K .* param.g); %// deep water frequencies (empirical parameter)
[windx , windy] = pol2cart( deg2rad(param.winddir) , 1) ;
P = phillips(Kx, Ky, [windx , windy], param.windSpeed, param.A, param.g) ;
H0 = 1/sqrt(2) .* (randn(gridSize) + 1i .* randn(gridSize)) .* sqrt(P); % height field at time t = 0
if nargout == 3
Grid_Sign = signGrid( param.meshsize ) ;
end
Note that the initial winDir parameter is now expressed with a single scalar value representing the "azimuth" (in degrees) of the wind (anything from 0 to 360). It is later translated to its X and Y components thanks to the function pol2cart.
[windx , windy] = pol2cart( deg2rad(param.winddir) , 1) ;
This insure that the norm is always 1.
The function calls your problematic phillips.m separately, but as said before it works even fully vectorized so you can copy it back inline if you like. (don't worry I checked the results against your versions => strictly identical). Note that this function does not output complex numbers so there was no need to compare the imaginary parts.
function P = phillips(Kx, Ky, windDir, windSpeed, A, g)
%// The function now accept scalar, vector or full 2D grid matrix as input
K_sq = Kx.^2 + Ky.^2;
L = windSpeed.^2 ./ g;
k_norm = sqrt(K_sq) ;
WK = Kx./k_norm * windDir(1) + Ky./k_norm * windDir(2);
P = A ./ K_sq.^2 .* exp(-1.0 ./ (K_sq * L^2)) .* WK.^2 ;
P( K_sq==0 | WK<0 ) = 0 ;
end
The next function called by the main program is calc_wave.m. This function finishes the calculations of the wave field for a given time. It is definitely worth having that on its own because this is the mimimun set of calculations which will have to be repeated for each given time when you want to animate the waves.
function Z = calc_wave( H0,W,time,Grid_Sign )
% Z = calc_wave( H0,W,time,Grid_Sign )
%
% This function calculate the wave height based on the wave coefficients H0
% and W, for a given "time". Default time=0 if not supplied.
% Fourth output parameter is optional (easy to recalculate anyway)
% recalculate the grid sign if not supplied in input
if nargin < 4
Grid_Sign = signGrid( param.meshsize ) ;
end
% Assign time=0 if not specified in input
if nargin < 3 ; time = 0 ; end
wt = exp(1i .* W .* time ) ;
Ht = H0 .* wt + conj(rot90(H0,2)) .* conj(wt) ;
Z = real( ifft2(Ht) .* Grid_Sign ) ;
end
The last 3 lines of calculations require a bit of explanation as they received the biggest changes (all for the same result but a much better speed).
Your original line:
Ht = H0 .* exp(1i .* W .* (t * timeStep)) + conj(flip(flip(H0,1),2)) .* exp(-1i .* W .* (t * timeStep));
recalculate the same thing too many times to be efficient:
(t * timeStep) is calculated twice on the line, at each loop, while it is easy to get the proper time value for each line when time is initialised at the beginning of the loop for time = (1:nSteps)*timeStep.
Also note that exp(-1i .* W .* time) is the same than conj(exp(1i .* W .* time)). Instead of doing 2*m*n multiplications to calculate them each, it is faster to calculate one once, then use the conj() operation which is much faster.
So your single line would become:
wt = exp(1i .* W .* time ) ;
Ht = H0 .* wt + conj(flip(flip(H0,1),2)) .* conj(wt) ;
Last minor touch, flip(flip(H0,1),2)) can be replaced by rot90(H0,2) (also marginally faster).
Note that because the function calc_wave is going to be repeated extensively, it is definitely worth reducing the number of calculations (as we did above), but also by sending it the Grid_Sign parameter (instead of letting the function recalculate it every iteration). This is why:
Your mysterious function signCor(ifft2(Ht),meshSize)), simply reverse the sign of every other element of Ht. There is a faster way of achieving that: simply multiply Ht by a matrix the same size (Grid_Sign) which is a matrix of alternated +1 -1 ... and so on.
so signCor(ifft2(Ht),meshSize) becomes ifft2(Ht) .* Grid_Sign.
Since Grid_Sign is only dependent on the matrix size, it does not change for each time in the loop, you only calculate it once (before the loop) then use it as it is for every other iteration. It is calculated as follow (vectorized, so you can also put it inline in your code):
function sgn = signGrid(n)
% return a matrix the size of n with alternate sign for every indice
% ex: sgn = signGrid(3) ;
% sgn =
% -1 1 -1
% 1 -1 1
% -1 1 -1
[x,y] = meshgrid(1:n,1:n) ;
sgn = ones( n ) ;
sgn(mod(x+y,2)==0) = -1 ;
end
Lastly, you will notice a difference in how the grids [Kx,Ky] are defined between your version and this one. They do produce slightly different result, it's just a matter of choice.
To explain with a simple example, let's consider a small meshsize=5. Your way of doing things will split that into 5 values, equally spaced, like so:
Kx(first line)=[-1.5 -0.5 0.5 1.5 2.5] * 2 * pi / patchSize
while my way of producing the grid will produce equally spaced values, but also centered on the domain limits, like so:
Kx(first line)=[-2.50 -1.25 0.0 1.25 2.50] * 2 * pi / patchSize
It seems to respect more your comment % = 2*pi*n / Lx, -N/2 <= n < N/2 on the line where you define it.
I tend to prefer symmetric solutions (plus it is also slightly faster but it is only calculated once so it is not a big deal), so I used my vectorized way, but it is purely a matter of choice, you can definitely keep your way, it only ever so slightly "offset" the whole result matrix, but it doesn't perturbate the calculations per se.
last remains of the first answer
Side programming notes:
I detect you come from the C/C++ world or family. In Matlab you do not need to define decimal number with a coma (like 2.0, you used that for most of your numbers). Unless specifically defined otherwise, Matlab by default cast any number to double, which is a 64 bit floating point type. So writing 2 * pi is enough to get the maximum precision (Matlab won't cast pi as an integer ;-)), you do not need to write 2.0 * pi. Although it will still work if you don't want to change your habits.
Also, (one of the great benefit of Matlab), adding . before an operator usually mean "element-wise" operation. You can add (.+), substract (.-), multiply (.*), divide (./) full matrix element wise this way. This is how I got rid of all the loops in your code. This also work for the power operator: A.^2 will return a matrix the same size as A with every element squared.
Here's the working program.
First of all - source code:
clear all; close all; clc;
rng(13); % setting seed for random numbers
meshSize = 128; % field size
windDir = [0.1,1];
patchSize = 200;
A = 1e-7;
g = 9.81; % gravitational constant
windSpeed = 100;
timeStep = 1/25;
x1 = linspace(-10, 10, meshSize+1); x = x1(1:meshSize);
y1 = linspace(-10, 10, meshSize+1); y = y1(1:meshSize);
[X,Y] = meshgrid(x,y); % wave field
i = 1:meshSize; j = 1:meshSize; % indecies
[I,J] = meshgrid(i,j); % field of indecies
Kx = 2.0 * pi / patchSize * (-meshSize / 2.0 + I); % = 2*pi*n / Lx, -N/2 <= n < N/2
Ky = 2.0 * pi / patchSize * (-meshSize / 2.0 + J); % = 2*pi*m / Ly, -M/2 <= m < M/2
K = sqrt(Kx.^2 + Ky.^2); % ||K||
W = sqrt(K .* g); % deep water frequencies (empirical parameter)
P = zeros(size(X)); % Cant compute P without loops
for i = 1:meshSize
for j = 1:meshSize
P(i,j) = phillips(Kx(i,j), Ky(i,j), windDir, windSpeed, A, g); % phillips spectrum
end
end
H0 = 1/sqrt(2) .* (randn(size(X)) + 1i .* randn(size(X))) .* sqrt(P); % height field at time t = 0
rotate3d on;
for t = 1:10000 % 10000 * timeStep (sec)
Ht = H0 .* exp(1i .* W .* (t * timeStep)) + ...
conj(flip(flip(H0,1),2)) .* exp(-1i .* W .* (t * timeStep));
[az,el] = view;
surf(X,Y,real(signCor(ifft2(Ht),meshSize)));
axis([-10 10 -10 10 -1e-4 1e-4]); view(az,el);
blue = linspace(0.4, 1.0, 25)'; map = [blue*0, blue*0, blue];
%shading interp; % improve shading (remove "faceted" effect)
colormap(map);
pause(1/60);
end
phillips.m: (I've tried to vectorize the computation of Phillips spectrum but I faced with a difficulty which I'll show further)
function P = phillips(kx, ky, windDir, windSpeed, A, g)
k_sq = kx^2 + ky^2;
if k_sq == 0
P = 0;
else
L = windSpeed^2 / g;
k = [kx, ky] / sqrt(k_sq);
wk = k(1) * windDir(1) + k(2) * windDir(2);
P = A / k_sq^2 * exp(-1.0 / (k_sq * L^2)) * wk^2;
if wk < 0
P = 0;
end
end
end
signCor.m: (This function is an absolutely mystery for me... I've copied it from "CUDA FFT Ocean Simulation" realization. Simulation works much worse without it. And again I don't know how to vectorize this function.)
function H = signCor(H1, meshSize)
H = H1;
for i = 1:meshSize
for j = 1:meshSize
if mod(i+j,2) == 0
sign = -1; % works fine if we change signs vice versa
else
sign = 1;
end
H(i,j) = H1(i,j) * sign;
end
end
end
The biggest mistake that I've done is that I used ifft instead of using ifft2, that's why CUDA ifft and Matlab ifft didn't match.
My second mistake was in this lines of code:
kx = 2.0 * pi / patchSize * (-meshSize / 2.0 + x(i)); % = 2*pi*n / Lx
ky = 2.0 * pi / patchSize * (-meshSize / 2.0 + y(j)); % = 2*pi*m / Ly
I should've write:
kx = 2.0 * pi / patchSize * (-meshSize / 2.0 + i); % = 2*pi*n / Lx
ky = 2.0 * pi / patchSize * (-meshSize / 2.0 + j); % = 2*pi*m / Ly
I've played a bit with parameters A, meshSize, patchSize and I came to the conclusion that:
Somehow plausible parameter of wave amplitude is A * (patchSize / meshSize), where A is nothing but a scaling factor.
For 'calm' patchSize / meshSize <= 0.5.
For 'tsunami' patchSize / meshSize >= 3.0.
Difficulty with a vectorization of Phillips spectrum:
I have 2 functions:
% non-vectorized spectrum
function P = phillips1(kx, ky, windDir, windSpeed, A, g)
k_sq = kx^2 + ky^2;
if k_sq == 0
P = 0;
else
L = windSpeed^2 / g;
k = [kx, ky] / sqrt(k_sq);
wk = k(1) * windDir(1) + k(2) * windDir(2);
P = A / k_sq^2 * exp(-1.0 / (k_sq * L^2)) * wk^2;
if wk < 0
P = 0;
end
end
end
% vectorized spectrum
function P = phillips2(Kx, Ky, windDir, windSpeed, A, g)
K_sq = Kx .^ 2 + Ky .^ 2;
L = -g^2 / windSpeed^4;
WK = (Kx ./ K_sq) .* windDir(1) + (Ky ./ K_sq) .* windDir(2);
P = (A ./ (K_sq .^ 2)) .* ( exp(L ./ K_sq) .* (WK .^ 2) );
P(K_sq == 0) = 0;
P(WK < 0) = 0;
P(isinf(P)) = 0;
end
After I compute P1 using phillips1 and P2 using phillips2 I plot their difference:
subplot(2,1,1); surf(X,Y,real(P2-P1)); title('Difference in real part');
subplot(2,1,2); surf(X,Y,imag(P2-P1)); title('Difference in imaginary part');
It perfectly illustrates that there's a huge difference between this 2 spectrums in real part.
Related
Code wont produce the value of a definite integral in MATLAB
I've had problems with my code as I've tried to make an integral compute, but it will not for the power, P2. I've tried using anonymous function handles to use the integral() function on MATLAB as well as just using int(), but it will still not compute. Are the values too small for MATLAB to integrate or am I just missing something small? Any help or advice would be appreciated to push me in the right direction. Thanks! The problem in the code is in the bottom of the section labelled "Power Calculations". My integral also gets quite messy if that makes a difference. %%%%%%%%%%% Parameters %%%%%%%%%%%% n0 = 1; %air n1 = 1.4; %layer 1 n2 = 2.62; %layer 2 n3 = 3.5; %silicon L0 = 650*10^(-9); %centre wavelength L1 = 200*10^(-9): 10*10^(-9): 2200*10^(-9); %lambda from 200nm to 2200nm x = ((pi./2).*(L0./L1)); %layer phase thickness r01 = ((n0 - n1)./(n0 + n1)); %reflection coefficient 01 r12 = ((n1 - n2)./(n1 + n2)); %reflection coefficient 12 r23 = ((n2 - n3)./(n2 + n3)); %reflection coefficient 23 t01 = ((2.*n0)./(n0 + n1)); %transmission coefficient 01 t12 = ((2.*n1)./(n1 + n2)); %transmission coefficient 12 t23 = ((2.*n2)./(n2 + n3)); %transmission coefficient 23 Q1 = [1 r01; r01 1]; %Matrix Q1 Q2 = [1 r12; r12 1]; %Matrix Q2 Q3 = [1 r23; r23 1]; %Matrix Q3 %%%%%%%%%%%% Graph of L vs R %%%%%%%%%%% R = zeros(size(x)); for i = 1:length(x) P = [exp(j.*x(i)) 0; 0 exp(-j.*x(i))]; %General Matrix P T = ((1./(t01.*t12.*t23)).*(Q1*P*Q2*P*Q3)); %Transmission T11 = T(1,1); %T11 value T21 = T(2,1); %T21 value R(i) = ((abs(T21./T11))^2).*100; %Percent reflectivity end plot(L1,R) title('Percent Reflectance vs. wavelength for 2 Layers') xlabel('Wavelength (m)') ylabel('Reflectance (%)') %%%%%%%%%%% Power Calculation %%%%%%%%%% syms L; %General lamda y = ((pi./2).*(L0./L)); %Layer phase thickness with variable Lamda P1 = [exp(j.*y) 0; 0 exp(-j.*y)]; %Matrix P with variable Lambda T1 = ((1./(t01.*t12.*t23)).*(Q1*P1*Q2*P1*Q3)); %Transmittivity matrix T1 I = ((6.16^(15))./((L.^(5)).*exp(2484./L) - 1)); %Blackbody Irradiance Tf11 = T1(1,1); %New T11 section of matrix with variable Lambda Tf2 = (((abs(1./Tf11))^2).*(n3./n0)); %final transmittivity P1 = Tf2.*I; %Power before integration L_initial = 200*10^(-9); %Initial wavelength L_final = 2200*10^(-9); %Final wavelength P2 = int(P1, L, L_initial, L_final) %Power production
I've refactored your code to make it easier to read to improve code reuse to improve performance to make it easier to understand Why do you use so many unnecessary parentheses?! Anyway, there's a few problems I saw in your code. You used i as a loop variable, and j as the imaginary unit. It was OK for this one instance, but just barely so. In the future it's better to use 1i or 1j for the imaginary unit, and/or m or ii or something other than i or j as the loop index variable. You're helping yourself and your colleagues; it's just less confusing that way. Towards the end, you used the variable name P1 twice in a row, and in two different ways. Although it works here, it's confusing! Took me a while to unravel why a matrix-producing function was producing scalars instead... But by far the biggest problem in your code is the numerical problems with the blackbody irradiance computation. The term L⁵ · exp(2484/L) - 1 for λ₀ = 200 · 10⁻⁹ m will require computing the quantity exp(1.242 · 10¹⁰) which, needless to say, is rather difficult for a computer :) Actually, the problem with your computation is two-fold. First, the exponentiation is definitely out of range of 64 bit IEEE-754 double precision, and will therefore result in ∞. Second, the parentheses are wrong; Planck's law should read C/L⁵ · 1/(exp(D) - 1) with C and D the constants (involving Planck's constant, speed of light, and Boltzmann constant), which you've presumably precomputed (I didn't check the values. I do know choice of units can mess these up, so better check). So, aside from the silly parentheses error, I suspect the main problem is that you simply forgot to rescale λ to nm. Changing everything in the blackbody equation to nm and correcting those parentheses gives the code I = 6.16^(15) / ( (L*1e+9)^5 * (exp(2484/(L*1e+9)) - 1) ); With this, I got a finite value for the integral of P2 = 1.052916498836486e-010 But, again, you'd better double-check everything. Note that I used quadgk(), because it's one of the better ones available on R2010a (which I'm stuck with), but you can just as easily replace this with integral() available on anything newer than R2012a. Here's the code I ended up with: function pwr = my_fcn() % Parameters n0 = 1; % air n1 = 1.4; % layer 1 n2 = 2.62; % layer 2 n3 = 3.5; % silicon L0 = 650e-9; % centre wavelength % Reflection coefficients r01 = (n0 - n1)/(n0 + n1); r12 = (n1 - n2)/(n1 + n2); r23 = (n2 - n3)/(n2 + n3); % Transmission coefficients t01 = (2*n0) / (n0 + n1); t12 = (2*n1) / (n1 + n2); t23 = (2*n2) / (n2 + n3); % Quality factors Q1 = [1 r01; r01 1]; Q2 = [1 r12; r12 1]; Q3 = [1 r23; r23 1]; % Initial & Final wavelengths L_initial = 200e-9; L_final = 2200e-9; % plot reflectivity for selected lambda range plot_reflectivity(L_initial, L_final, 1000); % Compute power production pwr = quadgk(#power_production, L_initial, L_final); % Helper functions % ======================================== % Graph of lambda vs reflectivity function plot_reflectivity(L_initial, L_final, N) L = linspace(L_initial, L_final, N); R = zeros(size(L)); for ii = 1:numel(L) % Transmission T = transmittivity(L(ii)); % Percent reflectivity R(ii) = 100 * abs(T(2,1)/T(1,1))^2 ; end plot(L, R) title('Percent Reflectance vs. wavelength for 2 Layers') xlabel('Wavelength (m)') ylabel('Reflectance (%)') end % Compute transmittivity matrix for a single wavelength function T = transmittivity(L) % Layer phase thickness with variable Lamda y = pi/2 * L0/L; % Matrix P with variable Lambda P1 = [exp(+1j*y) 0 0 exp(-1j*y)]; % Transmittivity matrix T1 T = 1/(t01*t12*t23) * Q1*P1*Q2*P1*Q3; end % Power for a specific wavelength. Note that this function % accepts vector-valued wavelengths; needed for quadgk() function pwr = power_production(L) pwr = zeros(size(L)); for ii = 1:numel(L) % Transmittivity matrix T1 = transmittivity(L(ii)); % Blackbody Irradiance I = 6.16^(15) / ( (L(ii)*1e+9)^5 * (exp(2484/(L(ii)*1e+9)) - 1) ); % final transmittivity Tf2 = abs(1/T1(1))^2 * n3/n0; % Power before integration pwr(ii) = Tf2 * I; end end end
Matlab : Confusion regarding unit of entropy to use in an example
Figure 1. Hypothesis plot. y axis: Mean entropy. x axis: Bits. This Question is in continuation to a previous one asked Matlab : Plot of entropy vs digitized code length I want to calculate the entropy of a random variable that is discretized version (0/1) of a continuous random variable x. The random variable denotes the state of a nonlinear dynamical system called as the Tent Map. Iterations of the Tent Map yields a time series of length N. The code should exit as soon as the entropy of the discretized time series becomes equal to the entropy of the dynamical system. It is known theoretically that the entropy of the system, H is log_e(2) or ln(2) = 0.69 approx. The objective of the code is to find number of iterations, j needed to produce the same entropy as the entropy of the system, H. Problem 1: My problem in when I calculate the entropy of the binary time series which is the information message, then should I be doing it in the same base as H? OR Should I convert the value of H to bits because the information message is in 0/1 ? Both give different results i.e., different values of j. Problem 2: It can happen that the probality of 0's or 1's can become zero so entropy correspondng to it can become infinity. To prevent this, I thought of putting a check using if-else. But, the loop if entropy(:,j)==NaN entropy(:,j)=0; end does not seem to be working. Shall be greateful for ideas and help to solve this problem. Thank you UPDATE : I implemented the suggestions and answers to correct the code. However, my logic of solving was not proper earlier. In the revised code, I want to calculate the entropy for length of time series having bits 2,8,16,32. For each code length, entropy is calculated. Entropy calculation for each code length is repeated N times starting for each different initial condition of the dynamical system. This appraoch is adopted to check at which code length the entropy becomes 1. The nature of the plot of entropy vs bits should be increasing from zero and gradually reaching close to 1 after which it saturates - remains constant for all the remaining bits. I am unable to get this curve (Figure 1). Shall appreciate help in correcting where I am going wrong. clear all H = 1 %in bits Bits = [2,8,16,32,64]; threshold = 0.5; N=100; %Number of runs of the experiment for r = 1:length(Bits) t = Bits(r) for Runs = 1:N x(1) = rand; for j = 2:t % Iterating over the Tent Map if x(j - 1) < 0.5 x(j) = 2 * x(j - 1); else x(j) = 2 * (1 - x(j - 1)); end % if end %Binarizing the output of the Tent Map s = (x >=threshold); p1 = sum(s == 1 ) / length(s); %calculating probaility of number of 1's p0 = 1 - p1; % calculating probability of number of 0'1 entropy(t) = -p1 * log2(p1) - (1 - p1) * log2(1 - p1); %calculating entropy in bits if isnan(entropy(t)) entropy(t) = 0; end %disp(abs(lambda-H)) end Entropy_Run(Runs) = entropy(t) end Entropy_Bits(r) = mean(Entropy_Run) plot(Bits,Entropy_Bits)
For problem 1, H and entropy can be in either nats or bits units, so long as they are both computed using the same units. In other words, you should use either log for both or log2 for both. With the code sample you provided, H and entropy are correctly calculated using consistant nats units. If you prefer to work in units of bits, the conversion of H should give you H = log(2)/log(2) = 1 (or using the conversion factor 1/log(2) ~ 1.443, H ~ 0.69 * 1.443 ~ 1). For problem 2, as #noumenal already pointed out you can check for NaN using isnan. Alternatively you could check if p1 is within (0,1) (excluding 0 and 1) with: if (p1 > 0 && p1 < 1) entropy(:,j) = -p1 * log(p1) - (1 - p1) * log(1 - p1); %calculating entropy in natural base e else entropy(:, j) = 0; end
First you just function [mean_entropy, bits] = compute_entropy(bits, blocks, threshold, replicate) if replicate disp('Replication is ON'); else disp('Replication is OFF'); end %% % Populate random vector if replicate seed = 849; rng(seed); else rng('default'); end rs = rand(blocks); %% % Get random trial_entropy = zeros(length(bits)); for r = 1:length(rs) bit_entropy = zeros(length(bits), 1); % H % Traverse bit trials for b = 1:(length(bits)) % N tent_map = zeros(b, 1); %Preallocate for memory management %Initialize tent_map(1) = rs(r); for j = 2:b % j is the iterator, b is the current bit if tent_map(j - 1) < threshold tent_map(j) = 2 * tent_map(j - 1); else tent_map(j) = 2 * (1 - tent_map(j - 1)); end % if end %Binarize the output of the Tent Map s = find(tent_map >= threshold); p1 = sum(s == 1) / length(s); %calculate probaility of number of 1's %p0 = 1 - p1; % calculate probability of number of 0'1 bit_entropy(b) = -p1 * log2(p1) - (1 - p1) * log2(1 - p1); %calculate entropy in bits if isnan(bit_entropy(b)) bit_entropy(b) = 0; end %disp(abs(lambda-h)) end trial_entropy(:, r) = bit_entropy; disp('Trial Statistics') data = get_summary(bit_entropy); disp('Mean') disp(data.mean); disp('SD') disp(data.sd); end % TO DO Compute the mean for each BIT index in trial_entropy mean_entropy = 0; disp('Overall Statistics') data = get_summary(trial_entropy); disp('Mean') disp(data.mean); disp('SD') disp(data.sd); %This is the wrong mean... mean_entropy = data.mean; function summary = get_summary(entropy) summary = struct('mean', mean(entropy), 'sd', std(entropy)); end end and then you just have to % Entropy Script clear all %% Settings replicate = false; % = false % Use true for debugging only. %H = 1; %in bits Bits = 2.^(1:6); Threshold = 0.5; %Tolerance = 0.001; Blocks = 100; %Number of runs of the experiment %% Run [mean_entropy, bits] = compute_entropy(Bits, Blocks, Threshold, replicate); %What we want %plot(bits, mean_entropy); %What we have plot(1:length(mean_entropy), mean_entropy);
Construct ternary grid, evaluate a function on the grid and contour plot in Matlab
I need to evaluate a function (say) Fxy = 2*x.^2 +3 *y.^2; on a ternary grid x-range (0 - 1), y-range (0-1) and 1-x-y (0 - 1). I am unable to construct the ternary grid on which I need to evaluate the above function. Also, once evaluated I need to plot the function in a ternary contour plot. Ideally, I need the axes to go counter clockwise in the sense (x -> y--> (1-x-y)). I have tried the function function tg = triangle_grid ( n, t ) ng = ( ( n + 1 ) * ( n + 2 ) ) / 2; tg = zeros ( 2, ng ); p = 0; for i = 0 : n for j = 0 : n - i k = n - i - j; p = p + 1; tg(1:2,p) = ( i * t(1:2,1) + j * t(1:2,2) + k * t(1:2,3) ) / n; end end return end for the number of sub intervals between the triangle edge coordinates n = 10 (say) and for the edge coordinates of an equilateral triangle t = tcoord = [0.0, 0.5, 1.0; 0.0, 1.0*sqrt(3)/2, 0.0]; This generated a triangular grid with the x-axis from 0-1 but the other two are not from 0-1. I need something like this: ... with the axes range 0-1 (0-100 would also do). In addition, I need to know the coordinate points for all intersections within the triangular grid. Once I have this I can proceed to evaluate the function in this grid. My final aim is to get something like this. This is a better representation of what I need to achieve (as compared to the previous plot which I have now removed) Note that the two ternary plots have iso-value contours which are different in in magnitude. In my case the difference is an order of magnitude, two very different Fxy's. If I can plot the two ternary plots on top of each other then and evaluate the compositions at the intersection of two iso-value contours on the ternary plane. The compositions should be as read from the ternary plot and not the rectangular grid on which triangle is defined. Currently there are issues (as highlighted in the comments section, will update this once the problem is closer to solution).
I am the author of ternplot. As you have correctly surmised, ternpcolor does not do what you want, as it is built to grid data automatically. In retrospect, this was not a particularly wise decision, I've made a note to change the design. In the mean time this code should do what you want: EDIT: I've changed the code to find the intersection of two curves rather than just one. N = 10; x = linspace(0, 1, N); y = x; % The grid intersections on your diagram are actually rectangularly arranged, % so meshgrid will build the intersections for us [xx, yy] = meshgrid(x, y); zz = 1 - (xx + yy); % now that we've got the intersections, we can evaluate the function f1 = #(x, y) 2*x.^2 + 3*y.^2 + 0.1; Fxy1 = f1(xx, yy); Fxy1(xx + yy > 1) = nan; f2 = #(x, y) 3*x.^2 + 2*y.^2; Fxy2 = f2(xx, yy); Fxy2(xx + yy > 1) = nan; f3 = #(x, y) (3*x.^2 + 2*y.^2) * 1000; % different order of magnitude Fxy3 = f3(xx, yy); Fxy3(xx + yy > 1) = nan; subplot(1, 2, 1) % This constructs the ternary axes ternaxes(5); % These are the coordinates of the compositions mapped to plot coordinates [xg, yg] = terncoords(xx, yy); % simpletri constructs the correct triangles tri = simpletri(N); hold on % and now we can plot trisurf(tri, xg, yg, Fxy1); trisurf(tri, xg, yg, Fxy2); hold off view([137.5, 30]); subplot(1, 2, 2); ternaxes(5) % Here we plot the line of intersection of the two functions contour(xg, yg, Fxy1 - Fxy2, [0 0], 'r') axis equal EDIT 2: If you want to find the point of intersection between two contours, you are effectively solving two simultaneous equations. This bit of extra code will solve that for you (notice I've used some anonymous functions in the code above now, as well): f1level = 1; f3level = 1000; intersection = fsolve(#(v) [f1(v(1), v(2)) - f1level; f3(v(1), v(2)) - f3level], [0.5, 0.4]); % if you don't have the optimization toolbox, this command works almost as well intersection = fminsearch(#(v) sum([f1(v(1), v(2)) - f1level; f3(v(1), v(2)) - f3level].^2), [0.5, 0.4]); ternaxes(5) hold on contour(xg, yg, Fxy1, [f1level f1level]); contour(xg, yg, Fxy3, [f3level f3level]); ternplot(intersection(1), intersection(2), 1 - sum(intersection), 'r.'); hold off
I have played a bit with the file exchange submission https://www.mathworks.com/matlabcentral/fileexchange/2299-alchemyst-ternplot. if you just do this: [x,y]=meshgrid(0:0.1:1); Fxy = 2*x.^2 +3 *y.^2; ternpcolor(x(:),y(:),Fxy(:)) You get: The thirds axis is created exactly as you say (1-x-y) inside the ternpcolor function. There are lots of things to "tune" here but I hope it is enough to get you started.
Here is a solution using R and my package ggtern. I have also included the points within proximity underneath, for the purpose of comparison. library(ggtern) Fxy = function(x,y){ 2*x^2 + 3*y^2 } x = y = seq(0,1,length.out = 100) df = expand.grid(x=x,y=y); df$z = 1 - df$x - df$y df = subset(df,z >= 0) df$value = Fxy(df$x,df$y) #The Intended Breaks breaks = pretty(df$value,n=10) #Create subset of the data, within close proximity to the breaks df.sub = ldply(breaks,function(b,proximity = 0.02){ s = b - abs(proximity)/2; f = b + abs(proximity)/2 subset(df,value >= s & value <= f) }) #Plot the ternary diagram ggtern(df,aes(x,y,z)) + theme_bw() + geom_point(data=df.sub,alpha=0.5,color='red',shape=21) + geom_interpolate_tern(aes(value = value,color=..level..), size = 1, n = 200, breaks = c(breaks,max(df$value) - 0.01,min(df$value) + 0.01), base = 'identity', formula = value ~ poly(x,y,degree=2)) + labs(title = "Contour Plot on Modelled Surface", x = "Left",y="Top",z="Right") Which produces the following:
how to detect if one signal is increasing in amplitude and another in decreasing at the same window
I have to signals as shown below and I need to detect if one signal is increasing in amplitude and another in decreasing at the a specific time window and vice versa. I tried findpeaks function but it didn't detect the big peak shown below .. it detects all the small peaks which I don't need them.. any help?
using sign(diff(signal)) can help, you can use sgolayfilt for noise reduction. L = 100; Fs = 50; t = (0 : L - 1) / Fs ; n = .3 * rand([1 L]); s1 = sin(2 * pi * .8 * t) + n; s2 = cos(2 * pi * .8 * t) + n; subplot(311) plot(t,s1,'b'); hold on plot(t,s2,'m'); subplot(312) s1 = sgolayfilt(s1,3,13); s2 = sgolayfilt(s1,3,13); t(end) = []; plot(t,sign(diff(s1)),'b') ylim([-2 2]) subplot(313) plot(t,sign(diff(s2)),'m') ylim([-2 2]) If your signals are clean then the results will be better.
Compute the slopes for the period and see whether they have opposing sign.
Evolution strategy with individual stepsizes
I'm trying to find a good solution with an evolution strategy for a 30 dimensional minimization problem. Now I have developed with success a simple (1,1) ES and also a self-adaptive (1,lambda) ES with one step size. The next step is to create a (1,lambda) ES with individual stepsizes per dimension. The problem is that my MATLAB code doesn't work yet. I'm testing on the sphere objective function: function f = sphere(x) f = sum(x.^2); end The plotted results of the ES with one step size vs. the one with individual stepsizes: The blue line is the performance of the ES with individual step sizes and the red one is for the ES with one step size. The code for the (1,lambda) ES with multiple stepsizes: % Strategy parameters tau = 1 / sqrt(2 * sqrt(N)); tau_prime = 1 / sqrt(2 * N); lambda = 10; % Initialize xp = (ub - lb) .* rand(N, 1) + lb; sigmap = (ub - lb) / (3 * sqrt(N)); fp = feval(fitnessfct, xp'); evalcount = 1; % Evolution cycle while evalcount <= stopeval % Generate offsprings and evaluate for i = 1 : lambda rand_scalar = randn(); for j = 1 : N Osigma(j,i) = sigmap(j) .* exp(tau_prime * rand_scalar + tau * randn()); end O(:,i) = xp + Osigma(:,i) .* rand(N,1); fo(i) = feval(fitnessfct, O(:,i)'); end evalcount = evalcount + lambda; % Select best [~, sortindex] = sort(fo); xp = O(:,sortindex(1)); fp = fo(sortindex(1)); sigmap = Osigma(:,sortindex(1)); end Does anybody see the problem?
Your mutations have a bias: they can only ever increase the parameters, never decrease them. sigmap is a vector of (scaled) upper minus lower bounds: all positive. exp(...) is always positive. Therefore the elements of Osigma are always positive. Then your change is Osigma .* rand(N,1), and rand(N,1) is also always positive. Did you perhaps mean to use randn(N,1) instead of rand(N,1)? With that single-character change, I find that your code optimizes rather than pessimizing :-).