Explanation for magic numbers in lattice Boltzmann simulation? - simulation

I recently came across this fluid dynamics simulation and have been trying to read up on the lattice Boltzmann method in order to better understand it. Most of the code there is pretty transparent, so between just reading the code and reading Wikipedia I have pretty much figured what everything does... except for the kinematic calculations in the core ``collide` function.
I've been able to figure out that the factors of 1/9, 4/9, and 1/36 are related to the lengths of the vectors connecting cell centers along different lattice directions, and I can even find resources that explain what different factors to use for different lattice types (with a D2Q9 lattice being used in this code). But I haven't been able to find anything that explains how you go from the generic vector equation which defines the collision step of the Lattice Boltzmann Algorithm to the specific nine lines of implementation arithmetic shown below. In particular, where do those factors of 3, 1.5, and 4.5 comes from?
The code used in the linked web page (with minor edits to remove free variables and improve readability) is as follows:
function collide(viscosity) { // kinematic viscosity coefficient in natural units
var omega = 1 / (3*viscosity + 0.5); // reciprocal of relaxation time
for (var y=1; y<ydim-1; y++) {
for (var x=1; x<xdim-1; x++) {
var i = x + y*xdim; // array index for this lattice site
var thisrho = n0[i]+nN[i]+nS[i]+nE[i]+nW[i]+nNW[i]+nNE[i]+nSW[i]+nSE[i];
// macroscopic horizontal velocity
var thisux = (nE[i]+nNE[i]+nSE[i]-nW[i]-nNW[i]-nSW[i])/thisrho;
// macroscopic vertical velocity
var thisuy = (nN[i]+nNE[i]+nNW[i]-nS[i]-nSE[i]-nSW[i])/thisrho;
var one9thrho = thisrho / 9;
var one36thrho = thisrho / 36;
var ux3 = 3 * thisux;
var uy3 = 3 * thisuy;
var ux2 = thisux * thisux;
var uy2 = thisuy * thisuy;
var uxuy2 = 2 * thisux * thisuy;
var u2 = ux2 + uy2;
var u215 = 1.5 * u2;
n0[i] += omega * (4/9*thisrho * (1 - u215) - n0[i]);
nE[i] += omega * ( one9thrho * (1 + ux3 + 4.5*ux2 - u215) - nE[i]);
nW[i] += omega * ( one9thrho * (1 - ux3 + 4.5*ux2 - u215) - nW[i]);
nN[i] += omega * ( one9thrho * (1 + uy3 + 4.5*uy2 - u215) - nN[i]);
nS[i] += omega * ( one9thrho * (1 - uy3 + 4.5*uy2 - u215) - nS[i]);
nNE[i] += omega * ( one36thrho * (1 + ux3 + uy3 + 4.5*(u2+uxuy2) - u215) - nNE[i]);
nSE[i] += omega * ( one36thrho * (1 + ux3 - uy3 + 4.5*(u2-uxuy2) - u215) - nSE[i]);
nNW[i] += omega * ( one36thrho * (1 - ux3 + uy3 + 4.5*(u2-uxuy2) - u215) - nNW[i]);
nSW[i] += omega * ( one36thrho * (1 - ux3 - uy3 + 4.5*(u2+uxuy2) - u215) - nSW[i]);
}
}
}

The code (which seems to be written in C#) starts by calculating the relaxation frequency omega from the viscosity that is set by enforcing a certain Reynolds number Re:= U L / nu. (Generally this is a constant and could thus also be calculated in advance instead of in every single loop.)
var omega = 1 / (3*viscosity + 0.5);
Then the code loops over the entire domain leaving out the first and last nodes of every direction which will be the boundary conditions which will be handled by a different function.
for (var y=1; y<ydim-1; y++) {
for (var x=1; x<xdim-1; x++) {`
For the storage of the populations it relies on global 1D arrays for every lattice direction \alpha and thus turns to linear indexing to select the correct node. The direction in this case are named 0 for the rest node and according to the corresponding direction (nord, east and so on) for the other populations. Generally codes use a numbering convention instead.
var i = x + y*xdim;
It reads the populations with linear indexing and sums them up to obtain density (zeroth-order momentum).
var thisrho = n0[i]+nN[i]+nS[i]+nE[i]+nW[i]+nNW[i]+nNE[i]+nSW[i]+nSE[i];
Then it determines the velocity by enforcing the first-order momentum \rho*u_i = \sum_\alpha e_{i \alpha} f_\alpha for the current lattice. In this case x points in horizontal direction (east west) and y points in vertical direction (north south), where east and north are the positive directions. This means \alpha = {north, north-east, north-west, south, south-east, south-west}.
var thisux = (nE[i]+nNE[i]+nSE[i]-nW[i]-nNW[i]-nSW[i])/thisrho;
var thisuy = (nN[i]+nNE[i]+nNW[i]-nS[i]-nSE[i]-nSW[i])/thisrho;
Then several variables are declared that will be re-used in the next section: one9thrho and one36thrho are a combination of weights and density for the horizontal/vertical and diagonal populations respectively. The confusing thing here is that ux3 means 3*ux while ux2 is intended to represent ux^2.
var one9thrho = thisrho / 9;
var one36thrho = thisrho / 36;
var ux3 = 3 * thisux;
var uy3 = 3 * thisuy;
var ux2 = thisux * thisux;
var uy2 = thisuy * thisuy;
var uxuy2 = 2 * thisux * thisuy;
var u2 = ux2 + uy2;
var u215 = 1.5 * u2;
The last step handles collision f_\alpha^t (where t stands for temporary as it will be followed up by streaming) = f_\alpha + \omega*(f_\alpha^{eq} - f_\alpha). Every single line is responsible for one discrete direction \alpha, so for a single entry of the vector equation. Note:
In this case f_\alpha^t is stored in f_\alpha again and therefore it is sufficient to increment (+=) f_\alpha (the arrays nNWSE) by \omega*(f_\alpha^{eq} - f_\alpha). The first term in the brackets is the equilibrium function while the last term corresponds to the current f_\alpha.
The incompressible equilibrium function is given by f_\alpha^{eq} = w_\alpha \rho (1 + e_{i \alpha} u_i/c_s^2 + (e_i \alpha u_i)^2/(2 c_s^4) - u_i^2/(2 c_s^2)) where we have to sum over all terms containing the index i twice. The lattice speed of sound c_s is given by 1/\sqrt(3)*dx/dx and therefore f_\alpha^{eq} = w_\alpha \rho (1 + 3 e_{i \alpha} u_i + 4.5 (e_i \alpha u_i)^2 - 1.5 u_i^2). The terms one9thrho and one36thrho correspond to the term before the brackets. The sum of ux3 and uy3 correspond to the second term inside the brackets 3 e_{i \alpha}. The second last term 4.5 (e_i \alpha u_i)^2 corresponds to 4.5 u_x^2 or 4.5 u_y^2 for the horizontal or vertical direction and to 4.5 (+-u_x +- uy)^2 for the diagonal direction as both components are present and thus leads to 4.5 (u_x^2 + u_y^2 +- u_x u_y). When subtracted u215 = 1.5*u2 = 1.5*(ux+uy) corresponds to the last term. You will have to take into account if the corresponding velocity projection e_{i \alpha} of lattice velocity \vec e_\alpha in direction i is either 0 or +-1. Latter is responsible for the signs
n0[i] += omega * (4/9*thisrho * (1 - u215) - n0[i]);
nE[i] += omega * ( one9thrho * (1 + ux3 + 4.5*ux2 - u215) - nE[i]);
nW[i] += omega * ( one9thrho * (1 - ux3 + 4.5*ux2 - u215) - nW[i]);
nN[i] += omega * ( one9thrho * (1 + uy3 + 4.5*uy2 - u215) - nN[i]);
nS[i] += omega * ( one9thrho * (1 - uy3 + 4.5*uy2 - u215) - nS[i]);
nNE[i] += omega * ( one36thrho * (1 + ux3 + uy3 + 4.5*(u2+uxuy2) - u215) - nNE[i]);
nSE[i] += omega * ( one36thrho * (1 + ux3 - uy3 + 4.5*(u2-uxuy2) - u215) - nSE[i]);
nNW[i] += omega * ( one36thrho * (1 - ux3 + uy3 + 4.5*(u2-uxuy2) - u215) - nNW[i]);
nSW[i] += omega * ( one36thrho * (1 - ux3 - uy3 + 4.5*(u2+uxuy2) - u215) - nSW[i]);
A code like this is unlikely to be very efficient. To speed the code up you should use two arrays for f_\alpha and f_\alpha^t and perform collision and streaming in one step instead of two. Additionally the equilibrium function can be rewritten further to recalculate as few terms as possible by combining omega and oneXXthrho and further rewrite the quadratic terms. Refer to the code that accompanies "The Lattice Boltzmann Method: Principles and Practice" for better written code. This should already increase performance by at least a factor of two. In order to simulate 3D on your machine you will have to apply several more difficult optimisations. Additionally there are better forums for this topic, for instance Palabos (University of Geneva) and OpenLB (Karlsruhe Institute of Technology).

Related

How to draw line until middle point using quadraticBezierTo in flutter?

I'm trying to draw a line between three points using quadraticsBezierTo but here is my problem.
Current result:
Current result
Expected result:
Expected result
Code:
final path = Path();
path.moveTo(p1.x, p1.y);
path.quadraticBezierTo(p2.x, p2.y, p3.x, p3.y);
//Also tried: path.cubicTo(p2.x, p2.y, p2.x, p2.y, p3.x, p3.y);
Do you know how to achieved this result?
Maybe,
path.quadraticBezierTo(
2.0 * p2.x - 0.5 * (p1.x + p3.x), 2.0 * p2.y - 0.5 * (p1.y + p3.y),
p3.x, p3.y);
or
path.cubicTo(
(4.0 * p2.x - p3.x) / 3.0, (4.0 * p2.y - p3.y) / 3.0,
(4.0 * p2.x - p1.x) / 3.0, (4.0 * p2.y - p1.y) / 3.0,
p3.x, p3.y);
Appendix
If you prefer sharper/blunter curves, throw away quadratics Bezier curve and take cubic Bezier curve. Then shift the control points evenly inwards/outwards.
double epsilon = -0.1;
path.cubicTo(
(4.0 * p2.x - p3.x - epsilon * (p3.x - p1.x)) / 3.0, (4.0 * p2.y - p3.y - epsilon * (p3.y - p1.y)) / 3.0,
(4.0 * p2.x - p1.x + epsilon * (p3.x - p1.x)) / 3.0, (4.0 * p2.y - p1.y + epsilon * (p3.y - p1.y)) / 3.0,
p3.x, p3.y);
This breaks tangents at end points of the curve, though.
Bezier curves aren't designed to pass through their control points, but if that's what you want you can do it by modifying the center point. From Wikipedia
For quadratic Bézier curves one can construct intermediate points Q0 and Q1 such that as t varies from 0 to 1:
Point Q0(t) varies from P0 to P1 and describes a linear Bézier curve.
Point Q1(t) varies from P1 to P2 and describes a linear Bézier curve.
Point B(t) is interpolated linearly between Q0(t) to Q1(t) and describes a quadratic Bézier curve.
Thus,
q0 = p0 * (1 - t) + p1 * t and q1 = p1 * (1 - t) + p2 * t and the actual curve is
b = q0 * (1 - t) + q1 * t
= p0 * (1 - t)^2 + p1 * t * (t - 1) + p1 * t * (t - 1) + p2 * t^2
= p0 * (1 - t)^2 + 2 * p1 * t * (t - 1) + p2 * t^2
Note that by ^ I mean exponent, not xor.
So if you want your curve to pass through some point c at time t = 1/2, then we can find what p1 should be.
c = p0 * 0.25 + p1 * 0.5 + p2 * 0.25
So,
p1 = 2 * c - 0.5 * (p0 + p2)

Matlab - Plotting with for loop

I am new to matlab and am trying to loop through an equation from 1 to 1000 and plot the result, but every value is turning out the same and the plot is incrorrect.
Here is my current attempt, but the variable Rm is giving only one results (instead of 1000 separate ones):
Ds = 1.04e-10;
Gamma = 1.9;
Omega = 1.09e-29;
Deltas = 2.5e-10;
Boltzmann = 1.3806e-23;
T = 1123.15;
Beta = 0.83;
Zo = 6.7;
EtaNi = 0.39;
EtaYSZ = 0.61;
Rm0 = 0.29;
RmYSZ0 = 0.265;
lambda = 4.70e-3;
C = Ds * ((Gamma * Omega * Deltas) / (2 * Boltzmann * T)) * (Beta / ((1 - Beta^2) * ((1 + Beta^2)^0.5) * ((1 + Beta)^3))) * Zo * (EtaNi/((EtaNi/Rm0) + (EtaYSZ/RmYSZ0)));
tinit=1; % Initial time value (h)
tend=1000; % End time value (h)
tinc=1; % Increment in time value (h)
t= [tinit:tinc:tend]; % Time vector
n = 1000;
for i=1:n
Rm(i) = (((5*C)/lambda) * (1 - exp(-lambda*i)) + (Rm0)^5)^(1/5);
end
plot(t,Rm);
Expected result is an exponential curve, any help would be appreciated
Your term before R0 is an exponential that ranges from 0 to 4.5e-27. R0^5 is 0.0021. Floating point precision is not enough to preserve the first term when it is added to the second. So (5C/L*(...) + Rm0^5) == Rm0^5, so it is constant.

Intersection point between circle and line (Polar coordinates)

I'm wondering if there is a way of finding the intersection point between a line and a circle written in polar coordinates.
% Line
x_line = 10 + r * cos(th);
y_line = 10 + r * sin(th);
%Circle
circle_x = circle_r * cos(alpha);
circle_y = circle_r * sin(alpha);
So far I've tried using the intersect(y_line, circle_y) function without any success. I'm relatively new to MATLAB so bear with me.
I have generalised the below so that other values than a=10 can be used...
a = 10; % constant line offset
th = 0; % constant angle of line
% rl = ?? - variable to find
% Coordinates of line:
% [xl, yl] = [ a + rl * cos(th), a + rl * sin(th) ];
rc = 1; % constant radius of circle
% alpha = ?? - variable to find
% Coordinates of circle:
% [xc, yc] = [ rc * cos(alpha), rc * sin(alpha) ];
We want the intersection, so xl = xc, yl = yc
% a + rl * cos(th) = rc * cos(alpha)
% a + rl * sin(th) = rc * sin(alpha)
Square both sides of both equations and sum them. Simplifying sin(a)^2 + cos(a)^2 = 1. Expanding brackets and simplifying further gives
% rl^2 + 2 * a * rl * (cos(th) + sin(th)) + 2 * a - rc^2 = 0
Now you can use the quadratic formula to get the value of rl.
Test discriminant:
dsc = (2 * a * (cos(th) + sin(th)) )^2 - 4 * (2 * a - rc^2);
rl = [];
if dsc < 0
% no intersection
elseif dsc == 0
% one intersection at
rl = - cos(th) - sin(th);
else
% two intersection points
rl = -cos(th) - sin(th) + [ sqrt(dsc)/2, -sqrt(dsc)/2];
end
% Get alpha from an earlier equation
alpha = acos( ( a + rl .* cos(th) ) ./ rc );
Now you have 0, 1 or 2 points of intersection of the line with the circle, from certain known and unknown values about each line. Essentially this is just simultaneous equations, see the start of this article for a basis of the maths
https://en.wikipedia.org/wiki/System_of_linear_equations
Do you need to do it numerically? This problem would have an easy analytical solution: The point (10 + r*cos(th),10 + r*sin(th)) is on a circle with radius R iff
(10+r*cos(th))^2 + (10+r*sin(th))^2 == R^2
<=> 200+r^2 + 2*r*(cos(th)+sin(th)) == R^2
<=> r^2 + 2*r*sqrt(2)*sin(th+pi/4) + 200 - R^2 = 0
which is a quadratic equation in r. If the discriminant is positive, there are two solutions (corresponding to two intersection points), otherwise, there are none.
If you work out the math, the condition for intersection is 100*(sin(2*th)-1)+circle_r^2 >= 0 and the roots are -10*sqrt(2)*sin(th+pi/4)*[1,1] + sqrt(100*(sin(2*th)-1)+circle_r^2)*[1,-1].
Here is a Matlab plot as an example for th = pi/3 and circle_r = 15. The magenta markers are calculated in closed-form using the equation shown above.

Convert output from symbolic math (sym) to float

My question is similar to this question but I believe it to be more general.
I use Matlab's symbolic math toolbox to solve an equation:
MAZ = 0.5;
MAU = 1.0;
XI = 1.0;
ALPHA = 2.0;
DRG = 0.5;
SRG = 1.0;
PHI = 1 / (2 * MAU);
syms L;
f = 1 - DRG - sqrt(1 + (ALPHA * XI - L / (2 * XI * PHI) ) ^ 2 ) / ...
sqrt(1 + (ALPHA * XI) ^ 2) + L / (4 * PHI * SRG * sqrt(1 + (ALPHA * XI)^2));
a = solve(f,L,'Real',true);
The answer is:
a =
5^(1/2)/3 + (10*((4*5^(1/2))/25 + 6/25)^(1/2))/3 + 8/3
5^(1/2)/3 - (10*((4*5^(1/2))/25 + 6/25)^(1/2))/3 + 8/3
How do I automatically convert these expressions for a - which do not contain any symbolic expressions - to floats so that I can access them later in my code?
Matlab's symbolic math toolbox contains a function called double.
This function converts the result of the solve function - which is a sym - to a double:
double(a)
ans =
5.98921078320145
0.834834535131742
While double is correct, it is limited to the precision of 64bit floating points. Use vpa if a higher precision is needed:
>> vpa(a)
ans =
5.9892107832014511063435699584181
0.83483453513174202459587915406938
>> digits(99)
>> vpa(a)
ans =
5.98921078320145110634356995841813862213621375395128614574627036653958858547362556353272837962692249
0.834834535131742024595879154069378868157531819123064337100994463734092031618244369410214559292265698

MATLAB: Why does solve return an empty sym object?

I´m trying to solve this set of equations in MATLAB and I get an empty sym object:
equations = {'I2B+I2EQAB=I22B+I2EQBC',...
'I2A=I2EQAB+I2EQAC+I22A',...
'I2C+I2EQBC+I2EQAC=I22C',...
'I22B=IZB+IC1B',...
'IZB=IC2B+IZBB',...
'I22C=-I2C*Z2C*YC/2+IZC',...
'IZC=IC2C+IZCC',...
'I22A=IC1A+IZA1',...
'IC4A+IZA2=IZBB+IZCC',...
'IZB*Z2LB+IC2B*2/YB=IC1B*2/YB',...
'I2C*Z2C=-IC2C*2/YC+IZC*Z2LC',...
'IZA1*m*Z2LA+IC2A*2/(m*YA)=IC1A*2/(m*YA)',...
'IC4A*2/((1-m)*YA)=IC2A*2/(m*YA)+IZA2*(1-m)*Z2LA',...
'I2EQBC*Z2EQBC+IZC*Z2LC=IZB*Z2LB',...
'I2B*Z2B+IC1B*2/YB',...
'I2C*Z2C+IC1C*2/YC',...
'I2A*Z2A+IC1A*2/(m*YA)',...
'IZB*Z2LB+(1-m)*Z2LA*IZA2=IZA1*m*ZL2A-I2EQAB*Z2EQAB',...
'IZA1*m*Z2LA=IZA2*(1-m)*Z2LA+IZC*Z2LC+I2EQAC*Z2EQAC',...
'IC4A/((1-m)*YA)=IC2C/YC'};
variables = {'m','I2A','I2B','I2C','I2EQAB','I2EQAC','I2EQBC',...
'IZA1','IC1A','IC2A','IZA2','IC4A','IC1B','IZB',...
'IC2B','IZBB','IZC','IC2C','IZCC'};
LL = solve(equations{:},variables{:})
Can you help me figure out what's going wrong?
Warning: 20 equations in 19 variables.
> In solve at 139
Warning: Explicit solution could not be found.
> In solve at 170
LL =
[ empty sym ]
I think that's self explanatory, if not check out the documentation related to DSOLVE where:
Diagnostics If dsolve cannot find an
analytic solution for an equation, it
prints the warning: Warning: Explicit
solution could not be found. and
returns an empty sym object.
solve() has issues with variable names in capital letters, as yours.
Please refer to http://www.mathworks.com/matlabcentral/newsreader/view_thread/303201
I tried reformatting the equations and inputting directly into the symbolic toolbox, and the solve function just spits out all the equations, so it cannot solve for those variables as the current equations stand.
Do you have any knowledge about the domains or constraints for all those variables? If you do I'd look at specifying all those, perhaps it would allow the solver to find a solution for you.
To get you quickly up and running in the symbolic toolbox, here's your equations reformatted to fit:
equations := {
I2B + I2EQAB = I22B + I2EQBC,
I2A = I2EQAB + I2EQAC + I22A,
I2C + I2EQBC + I2EQAC = I22C,
I22B = IZB + IC1B,
IZB = IC2B + IZBB,
I22C = -I2C * Z2C * YC / 2 + IZC,
IZC = IC2C + IZCC,
I22A = IC1A + IZA1,
IC4A + IZA2 = IZBB + IZCC,
IZB * Z2LB + IC2B * 2 / YB = IC1B * 2 / YB,
I2C * Z2C = -IC2C * 2 / YC + IZC * Z2LC,
IZA1 * m * Z2LA + IC2A * 2 / (m * YA) = IC1A * 2 / (m * YA),
IC4A * 2 / ((1 - m) * YA) = IC2A * 2 / (m * YA) + IZA2 * (1 - m) * Z2LA,
I2EQBC * Z2EQBC + IZC * Z2LC = IZB * Z2LB,
I2B * Z2B + IC1B * 2 / YB,
I2C * Z2C + IC1C * 2 / YC,
I2A * Z2A + IC1A * 2 / (m * YA),
IZB * Z2LB + (1 - m) * Z2LA * IZA2 = IZA1 * m * ZL2A - I2EQAB * Z2EQAB,
IZA1 * m * Z2LA = IZA2 * (1 - m) * Z2LA + IZC * Z2LC + I2EQAC * Z2EQAC,
IC4A / ((1 - m) * YA) = IC2C / YC
}:
variables := {
m, I2A, I2B, I2C, I2EQAB, I2EQAC ,I2EQBC,
IZA1, IC1A, IC2A, IZA2, IC4A, IC1B, IZB,
IC2B, IZBB, IZC, IC2C, IZCC
}:
solve(equations, variables)
To specify that all your known variables are real numbers, use this command:
assume(variables, Type::Real)
Also note that I count 36 unique variables (unless I made a mistake) in the equations, you'd be getting a huge list of "what-if's" for those equations if the solver was able to produce a result. I'd look at your equations and see if you could group them out and solve them in smaller sets.
Matlab, symbolic solve: solve()
I think has issues with symbolic variables who's names are more than one character.
a-z works, but anytime i try to solve something with two letters or more it just spits
back out the empty set.
For instance, something as simple as solve('xy*10 = 1', 'xy') doesn't work :(