How to loop the potential? - matlab

I am currently working on a molecular dynamics simulation of polymers in solution, this is one of the subroutines which calculates the potential energy of the system and the force exerted on each monomer.
function eval_force()
% eval_force.m IS USED FOR EVALUATING FORCE
% THE STRATEGY USUALLY ADOPTED FOR A LENNARD-JONES OR AS A MATTER OF FACT
% ANY PAIR-WISE INTERACTING SYSTEM IS AS FOLLOWS:
% 1. EVALUATE THE DISTANCE BETWEEN TWO PAIRS OF ATOMS
% 2. ENSURE THAT MINIMUM IMAGE CONVENTION (MIC) IS FOLLOWED
% 3. IF THE DISTANCE OBTAINED THROUGH MIC IS GREATER THAN THE CUT OFF
% DISTANCE MOVE TO NEXT PAIR
% 4. ELSE EVALUATE POTENTIAL ENERGY AND CALCULATE FORCE COMPONENTS
% 5. F(i,j) = -F(j,i)
global MASS KB TEMPERATURE NUM_ATOMS LENGTH TSTEP;
global EPS SIG R_CUT GAMMA POT_E;
global POSITION VELOCITY FORCE STO;
dr = zeros(3,1);
drh = zeros(3,1);
FORCE(:) = 0.0;
POT_E = 0.0;
for ( i=1:NUM_ATOMS )
for ( j=i+1:NUM_ATOMS )
dist2 = 0.0; % VARIABLE dist2 STORES DISTANCE BETWEEN PAIR (i,j)
% FIRST FIND OUT THE DIFFERENCE IN X,Y AND Z COORDINATES
% VARIABLE dr IS USED FOR THIS PURPOSE
for(k = 1:3)
dr(k) = POSITION(i,k) - POSITION(j,k);
% THESE STEPS ENSURE MINIMUM IMAGE CONVENTION IS FOLLOWED
if(dr(k) > LENGTH/2.0)
dr(k) = dr(k) - LENGTH;
end
if(dr(k) < -LENGTH/2.0)
dr(k) = dr(k) + LENGTH;
end
% MINIMUM IMAGE CONVENTION ENDS HERE
dist2 = dist2 + dr(k)*dr(k); % dist2 IS BASED UPON MIC
end
if(dist2 <= R_CUT*R_CUT) % IF THE CUT OFF CRITERIA IS SATISFIED
dist2i = power(SIG,2)/dist2;
dist6i = power(dist2i,3);
dist12i = power(dist6i,2);
POT_E = POT_E + EPS * (dist12i - 2*dist6i) + 33.34 * EPS * power(sqrt(dist2) - SIG,2)/(2 * power(SIG,2)); % STORES THE POTENTIAL ENERGY
Ff = 12.0 * EPS * (dist12i-dist6i) - 33.34 * EPS * (sqrt(dist2) - SIG)/(dist2i * sqrt(dist2) * power(SIG,2));
Ff = Ff * dist2i;
for(k = 1:3)
FORCE(i,k) = FORCE(i,k) + Ff*dr(k)- GAMMA*VELOCITY(i,k);
FORCE(j,k) = FORCE(j,k) - Ff*dr(k)- GAMMA*VELOCITY(j,k);
end
end
end
end
end
How can I make a loop for the "33.34 * EPS * power(sqrt(dist2) - SIG,2)/(2 * power(SIG,2))" part under POT_E which is the harmonic potential, so it only evaluates the distance between atoms for the nearest ones (for j=i+1 to 4).

After having a quick look at your code, I'd suggest the following ideas:
Calculate all-to-all distances at once using pdist and store it in a matrix, say AllDist.
The conditions based on LENGTH can be applied directly on AllDist.
Since you need to find four nearest neighbors, you need to have a single loop iterating through the rows (or columns) of AllDist, which sorts the current row (or column) and gives you four nearest neighbors. Note that for each atom, you'd get 0 as the nearest distance as this is the "self-distance". Ignore this.
If you have access to Matlab's Parallel Computing Toolbox, try to use it (parfor) where appropriate to accelerate your simulation.

Related

How do I linearly interpolate past missing values using future values in a while loop?

I am using MATLAB R2020a on a MacOS. I am trying to remove outlier values in a while loop. This involves calculating an exponentially weighted moving mean and then comparing this a vector value. If the conditions are met, the vector input is then added to a separate vector of 'acceptable' values. The while loop then advances to the next input and calculates the new exponentially weighted moving average which includes the newly accepted vector input.
However, if the condition is not met, I written code so that, instead of adding the input sample, a zero is added to the vector of 'acceptable' values. Upon the next acceptable value being added, I currently have it so the zero immediately before is replaced by the mean of the 2 framing acceptable values. However, this only accounts for one past zero and not for multiple outliers. Replacing with a framing mean may also introduce aliaising errors.
Is there any way that the zeros can instead be replaced by linearly interpolating the "candidate outlier" point using the gradient based on the framing 2 accepted vector input values? That is, is there a way of counting backwards within the while loop to search for and replace zeros as soon as a new 'acceptable' value is found?
I would very much appreciate any suggestions, thanks in advance.
%Calculate exponentially weighted moving mean and tau without outliers
accepted_means = zeros(length(cycle_periods_filtered),1); % array for accepted exponentially weighted means
accepted_means(1) = cycle_periods_filtered(1);
k = zeros(length(cycle_periods_filtered),1); % array for accepted raw cycle periods
m = zeros(length(cycle_periods_filtered), 1); % array for raw periods for all cycles with outliers replaced by mean of framing values
k(1) = cycle_periods_filtered(1);
m(1) = cycle_periods_filtered(1);
tau = m/3; % pre-allocation for efficiency
i = 2; % index for counting through input signal
j = 2; % index for counting through accepted exponential mean values
n = 2; % index for counting through raw periods of all cycles
cycle_index3(1) = 1;
while i <= length(cycle_periods_filtered)
mavCurrent = (1 - 1/w(j))*accepted_means(j - 1) + (1/w(j))*cycle_periods_filtered(i);
if cycle_periods_filtered(i) < 1.5*(accepted_means(j - 1)) && cycle_periods_filtered(i) > 0.5*(accepted_means(j - 1)) % Identify high and low outliers
accepted_means(j) = mavCurrent;
k(j) = cycle_periods_filtered(i);
m(n) = cycle_periods_filtered(i);
cycle_index3(n) = i;
tau(n) = m(n)/3;
if m(n - 1) == 0
m(n - 1) = (k(j) + k(j - 1))/2;
tau(n - 1) = m(n)/3;
end
j = j + 1;
n = n + 1;
else
m(n) = 0;
n = n + 1;
end
i = i + 1;
end
% Scrap the tail
accepted_means(j - 1:end)=[];
k(j - 1:end) = [];

Selecting corresponding values from another loop to use in subsequent loop

I am an Earth Scientist, and interested in the macro scale morphometric parameters in the river basins. I am building a simple graded Stream Gradient index from a excel sheet. I have wrote a simple code which follows:
data = 'SL.xlsx';
headers = xlsread(data);
lat = headers (:,1);
long = headers (:,2);
elevation = headers (:,3);
numberofelement = numel (lat);
% To calculate the intermideate distacne between two points
for i=2:numberofelement
intdistance (1)=0;
intdistance (i) = sqrt((lat (i)-lat (i-1))^2+ (long (i)-long (i-1))^2);
end
% Cumulative distacne in km
cumdist (1)=0
for j=2:numberofelement
cumdist(j)=cumdist(j-1)+intdistance (j);
cumdistkm (j)= cumdist(j)/1000;
end
% Average SL index (or graded river gradient
gradedslindex = (elevation (1)- elevation (numberofelement))/log(cumdistkm(numberofelement))
I am unable to to do the next steps. The next steps includes few calculations:
The hypothetical data looks like
Cumdist Elevation
0.25 500
2.1 480
4.2 470
6.8 450
7.5 430
8.2 420
9.1 410
10.1 400
1) In the cumdistkm variable I have to segment for every 5 km. If there is no 5 km value, I have to select the nearest lower value for cumdist. So for this data the 4.2 and 9.1 have to taken.
2) Then the calculation part would be elevation(last)-elevation(first)(for that particular reach) and divide by In(cumdistkm(last))-In(cumdist(first))(for the same row index).
I am unable to select these parameters for regular interval. A small hint is will be very helpful.
Thank you.
You can use the cumsum function to find cumdist
cumdist_km = [0; cumsum(intdistance)/1000]; %calculate cumdist in km
I would make a function and stick it in a file findIndicesBelowIncrement.m. There might be a slicker way to do this, but this should be quite fast and works (as I understand what you're asking for).
function indices = findIndicesBelowIncrement(cumdist_km, km_increment)
% indices = findIndicesForBelowIncrement(cumdist_km, km_increment)
% cumdist_km : sorted array of distances
% km_increment: we will find index of cumdist_km for each
% multiple of increment such that distance is
% at or below the multiple of the increment
indices = NaN(ceil(cumdist_km(end) / 5), 1); % we know endpoint, lets init smart
j = 1;
for i=2:length(cumdist_km) % iterate through array
if(cumdist_km(i) > j * km_increment) % we have just passed increment multiple
indices(j) = i - 1; % previous index is what we want
while(cumdist_km(i) > j * km_increment) % handle case value skips ahead
% note: well end up with a NaN in indices for every skip
j = j + 1 % normal is 1 increment. skip is > 1
end
end
end
indices(end) = length(cumdist_km);
Then back in your main script you could do:
five_km_indices = findIndicesBelowIncrement(cumdist_km, 5);
dist_5kminc = cumdist_km(five_km_indices);
elev_5kminc = elevations(five_km_indices);
Then do whatever calculations on dist_5kminc and elev_5kminc.

Matlab : Help in implementing an encoding for realizing a mapping function

An example : Consider the unimodal logistic map : x[n+1] = 4*x[n](1-x[n]). The map can be used to generate +1/-1 symbols using the technique
I want to extend the above concept using the map f(x) for 3 levels, each level corresponds to a symbol but I am unsure how I can do that.
To map a continuous range (obtained for example as the output of a pseudo-random number generator, or alternatively the logistic map) to a small set of discrete values, you would need to split the continuous range into regions, and assign an output value to each of those regions. The limits of those regions would determine the corresponding threshold values to use.
For example, in the binary case you start off with a continuous range of values in [0,1] which you split into two regions: [0,0.5] and (0.5,1]. Each of those region begin assigned an output symbol, namely -1 and +1. As you have noted, the boundary of the regions being set to the midpoint of your [0,1] input range gives you a threshold of 0.5. This could be implemented as:
if (x > 0.5)
symbol = +1;
else
symbol = -1;
end
As a more compact implementation, the formula 2*(x>0.5)-1 takes advantage of the fact that in Matlab a true condition (from the x>0.5 expression) has a value of 1, whereas false has a value of 0.
For 3 discrete output values, you'd similarly split your [0,1] input range into 3 regions: [0,1/3], (1/3,2/3] and (2/3,1]. The corresponding thresholds thus being 1/3 and 2/3.
Finally for 8 discrete output values, you would similarly split your [0,1] input range into 8 regions: [0,1/8], (1/8,2/8], (2/8,3/8], (3/8,4/8], (4/8,5/8], (5/8,6/8], (6/8,7/8] and (7/8,1]. The corresponding thresholds thus being 1/8, 2/8, 3/8, 4/8, 5/8, 6/8 and 7/8, as illustrated in the following diagram:
thresholding function input: |-----|-----|-----|-----|-----|-----|-----|-----|
0 | | | | | | | 1
thresholds: 1/8 2/8 3/8 4/8 5/8 6/8 7/8
| | | | | | | |
v v v v v v v v
generated symbol: -7 -5 -3 -1 +1 +3 +5 +7
This then gives the following symbol mapping implementation:
if (x < 1/8)
symbol = -7;
elseif (x < 2/8)
symbol = -5;
elseif (x < 3/8)
symbol = -3;
elseif (x < 4/8)
symbol = -1;
elseif (x < 5/8)
symbol = +1;
elseif (x < 6/8)
symbol = +3;
elseif (x < 7/8)
symbol = +5;
else
symbol = +7;
end
As a more compact implementation, you could similarly use the floor function to obtain discrete levels:
% x : some value in the [0,1] range
% s : a symbol in the {-7,-5,-3,-1,+1,+3,+5,+7} set
function s = threshold(x)
% Note on implementation:
% 8*x turns the input range from [0,1] to [0,8]
% floor(8*x) then turns that into values {0,1,2,3,4,5,6,7}
% then a linear transform (2*() - 7) is applied to map
% 0 -> -7, 1 -> -5, 2 -> -3, ..., 7 -> 7
% min/max finally applied just as a safety to make sure we don't overflow due
% to roundoff errors (if any).
s = min(7, max(-7, 2*floor(8*x) - 7));
end
Now if you want to generate complex symbols with 8 levels for the real part and 8 levels for the imaginary part, you'd simply combine them just like in the binary case. Mainly you'd generate a first value which gives you the real part, then a second value for the imaginary part:
x_real = rand(); % random input 0 <= x_real <= 1
x_imag = rand(); % another one
s = threshold(x_real) + sqrt(-1)*threshold(x_imag);
Addressing some points raised by a previous revision of the question:
One thing to note is that x[n+1] = 4*x[n](1-x[n]) maps values in [0,1] to the same range of values. This makes it possible to iteratively apply the mapping to obtain additional values, and correspondingly generate a binary sequence with the threshold application (x > 0.5). The function f(x) you provided (in an earlier edit of the question) on the other hand, maps values within a range with discontinuities (roughly covering [-7.5,7.5] depending on p) to [0,1]. In other words you would need to either modify f(x) or otherwise map its output back to the input domain of f(x). It would probably be easier to consider a general uniform pseudo-random number generator over the [-8,+8] range as input to the threshold function:
% x : some value in the [-8,8] range
% s : a symbol in the {-7,-5,-3,-1,+1,+3,+5,+7} set
function s = threshold_8PAM(x)
s = min(7, max(-7, 2*round(x/2 + 0.5) - 1));
end
To get the final 64-QAM symbols you would combine two 8-PAM symbols in quadrature (i.e. x64qam = xQ + sqrt(-1)*xI, where xQ and xI have both been generated with the above procedure).
That said, if the goal is to implement a digital communication system using 64-QAM symbols with additional chaotic modulation, you'd ultimately want to take into account the source of input data to transmit rather than randomly generating both the chaotic modulation and the source data in one shot. That is even if for performance evaluation you wind up generating the source data randomly, it is still a good idea to be generating it independently of the chaotic modulation.
Addressing those concerns, the paper An Enhanced Spectral Efficiency Chaos-Based Symbolic Dynamics Transceiver Design suggests a different approach based on the inverse map you provided, which can be implemented as:
function x = inverse_mapping(x,SymbIndex,p)
if (SymbIndex==0)
x = ((1-p)*x-14)/2;
elseif (SymbIndex==1)
x = ((1-p)*x-10)/2;
elseif (SymbIndex==2)
x = ((1-p)*x-6)/2;
elseif (SymbIndex==3)
x = ((1-p)*x-2)/2;
elseif (SymbIndex==4)
x = ((1-p)*x+2)/2;
elseif (SymbIndex==5)
x = ((1-p)*x+6)/2;
elseif (SymbIndex==6)
x = ((1-p)*x+10)/2;
elseif (SymbIndex==7)
x = ((1-p)*x+14)/2;
end
end
As you may notice, the function takes a symbol index (3 bits, which you'd get from the input source data) and the current state of the modulated output (which you may seed with any value within the convergence range of inverse_mapping) as two independent input streams. Note that you can compute the bounds of the convergence range of inverse_mapping by finding the limits of repeated application of the mapping using input symbol index s=0, and s=7 (using for example a seed of x=0). This should converge to [-14/(1+p), 14/(1+p)].
The chaotic modulation described in the above referenced paper can then be achieved with (setting the control parameter p=0.8 as an example):
% Simulation parameters
Nsymb = 10000;
p = 0.8;
M = 64;
% Source data generation
SymbolIndexQ = randi([0 sqrt(M)-1],Nsymb,1);
SymbolIndexI = randi([0 sqrt(M)-1],Nsymb,1);
% Modulation
xmax = 14/(1+p); % found by iterative application of inverse_mapping
xQ = xmax*(2*rand(1)-1); % seed initial state
xI = xmax*(2*rand(1)-1); % seed initial state
x = zeros(Nsymb,1);
for i=1:Nsymb
xQ = inverse_mapping(xQ, SymbolIndexQ(i), p);
xI = inverse_mapping(xI, SymbolIndexI(i), p);
x(i) = xQ + sqrt(-1)*xI;
end
% x holds the modulated symbols
plot(real(x), imag(x), '.');
% if you also need the unmodulated symbols you can get them from
% SymbolIndexQ and SymbolIndexI
s = (2*SymbolIndexQ-7) + sqrt(-1)*(2*SymbolIndexI-7);
with should produce the corresponding constellation diagram:
or with p=1 (which is essentially unmodulated):

Matlab Code to distribute points on plot

I have edited a code that i found online that helps me draw points somehow distributed on a graph based on the minimum distance between them
This is the code that i have so far
x(1)=rand(1)*1000; %Random coordinates of the first point
y(1)=rand(1)*1000;
minAllowableDistance = 30; %IF THIS IS TOO BIG, THE LOOP DOES NOT END
numberOfPoints = 300; % Number of points equivalent to the number of sites
keeperX = x(1); % Initialize first point
keeperY = y(1);
counter = 2;
for k = 2 : numberOfPoints %Dropping another point, and checking if it can be positioned
done=0;
trial_counter=1;
while (done~=1)
x(k)=rand(1)*1000;
y(k)=rand(1)*1000;
thisX = x(k); % Get a trial point.
thisY = y(k);
% See how far is is away from existing keeper points.
distances = sqrt((thisX-keeperX).^2 + (thisY - keeperY).^2);
minDistance = min(distances);
if minDistance >= minAllowableDistance
keeperX(k) = thisX;
keeperY(k) = thisY;
done=1;
trial_counter=trial_counter+1;
counter = counter + 1;
end
if (trial_counter>2)
done=1;
end
end
end
end
So this code is working fine, but sometimes matlab is freezing if the points are above 600. The problem is full , and no more points are added so matlab is doing the work over and over. So i need to find a way when the trial_counter is larger than 2, for the point to find a space that is empty and settle there.
The trial_counter is used to drop a point if it doesn't fit on the third time.
Thank you
Since trial_counter=trial_counter+1; is only called inside if minDistance >= minAllowableDistance, you will easily enter an infinite loop if minDistance < minAllowableDistance (e.g. if your existing points are quite closely packed).
How you do this depends on what your limitations are, but if you're looking at integer points in a set range, one possibility is to keep the points as a binary image, and use bwdist to work out the distance transform, then pick an acceptable point. So each iteration would be (where BW is your stored "image"/2D binary matrix where 1 is the selected points):
D = bwdist(BW);
maybe_points = find(D>minAllowableDistance); % list of possible locations
n = randi(length(maybe_points)); % pick one location
BW(maybe_points(n))=1; % add it to your matrix
(then add some checking such that if you can't find any allowable points the loop quits)

Attempted to access sym(67); index out of bounds because numel(sym)=2

I am receiving the following error message:
Attempted to access sym(67); index out of bounds because numel(sym)=2.
I have been working on this for three days. I looked for similar error, but it didn't help. My code is below:
filename='DriveCyclesCP.xlsx';
V=xlsread('DriveCyclesCP.xlsx',2,'C9:C774'); % Get the velocity values, they are in an array V.
N=length(V); % Find out how many readings
mass = 1700 ; % Vehicle mass+ two 70 kg passengers.
area_Cd = 0.75; % Frontal area in square metres
Crr=0.009; %rolling resistance
g=9.8; % gravity acceleration
T=774; %UDDS cycle time duration
V_ave = 21.5; % UDDS avearage speed im m/s
rd=0.3; % Effective tire radius
Qhv =12.22; % E85 low Heating value in kWh/kg
Vd = 2.189; % engine size in L
md=0.801; % mass density of Ethanol
mf =Vd*md; % mf is the fuel mass consumed per cycle
Per = zeros(1,N); % engine power for each point of the drive cycle
a = zeros(1,N); % acceleration
SFC = zeros(1,N); % specific fuel consumption
Wc = zeros (1,N); % mass flow rate
nf = zeros (1,N); %fuel efficiency
Pm = zeros (1,N); % motor power
Pt = zeros (1,N);
Te =zeros (1,N); % Engine Troque
Tt = zeros (1,N);
Tm =zeros (1,N);
we =zeros (1,N); % Engine rot speed
wt = zeros (1,N);
wm =zeros (1,N);
S =zeros (1,8);
int (sym ('C'));
for C=1:N
a(C)=V(C+1)-V(C);
Pt(C)= V(C)*(mass*g*Crr + (0.5*area_Cd*1.202*(V(C))^2) + mass*a(C))/1000;
Per(C)=(mass*g*Crr +0.5*area_Cd*1.202*(V(C))^2 +mass*g*0.03)/1000*0.85;% e
syms Te(C) Tt(C) Tm(C) wt(C) we(C) wm(C) k1 k2
S = solve( Pm(C)==Pt(C) - Per(C), Tt(C)*wt(C)== Pt(C), Tt(C)*wt(C)== Te(C)*we(C) + Tm(C)*wm(C), wt(C)==we(C)/k1, wt(C)==wm(C)/k2, Pm(C)==wm(C) *Tm(C), Per(C)==we(C) *Te(C), Tt == k1*Te + k2*Tm );
end
The problem is on the line
int (sym ('C'));
You have defined sym to be a matrix with 2 entries somewhere (either earlier in the code or in a previous mfile), thus it treats sym as a matrix instead of a function. Thus when Matlab gets to the statement sym('C') it first converts the character 'C' to its ASCII integer representation (this just happens to be the number 67), then it tries to calculate sym(67) which is impossible as sym only has 2 elements.
Thus you have to stop sym from being a matrix (variable) and let it be a function again. There are two ways to solve this, either you can start you file with the statement clear;, this will remove all variables in memory, which might not be what you want; or you can use a function instead of script, as this hides all variables that have been defined previously and prevents this sort of error.
Note the line numel(X) is a way to measure how many elements are in X. Thus numel(sym)=2 means that sym has 2 elements.
P.S. There is an error in the lines (notice that I only taken some of the lines of you code)
N=length(V); % Find out how many readings
for C=1:N
a(C)=V(C+1)-V(C);
end
When C becomes equal to N, then V(C+1) will generate an error.