How should I write these coupled PDE's in FiPy? - pde

I'm trying to implement a phase field solidification model of a ternary alloy using FiPy. I have looked at most of the phase field examples provided on FiPy's website and my model is similar to examples.phase.quaternary.
The evolution equation for the concentrations looks like this:
Which should be solved for C_1 and C_2 (C_3 is solvent). The concentration equations are coupled with the phase evolution equation and we have D_i = D_i(phi), h = h(phi).
There are nonlinear dependencies of the variable solved for (C_i) in all three terms which makes me unsure on how to define them in FiPy. The first term (red) is a diffusion term with a nonlinear coefficient and that should be fine, but how should I define the counterdiffusion and the phasetransformation terms?
I tried defining them as diffusion and convection terms with nonlinear coefficients but without success. Therefore I am hoping for some advice on how I can define so that FiPy likes it.
Any help is highly appreciated, thanks!
/Anders

This set of equations can be solved in a coupled manner. The counter diffusion term can be defined as a coupled diffusion term and the phase transformation term can be defined as a convection term. The equations will be,
eqn1 = fipy.TransientTerm(var=C_1) == \
fipy.DiffusionTerm(D_1 - coeff_1 * (D_1 - D_3), var=C_1) \ # coupled
- fipy.DiffusionTerm(coeff_1 * (D_2 - D_3), var=C_2) \ # coupled
+ fipy.ConvectionTerm(conv_coeff_1, var=C_1)
where
coeff_1 = D_1 * C_1 / ((D_1 - D_3) * C_1 + (D_2 - D_3) * C_2 + D_3)
conv_coeff_1 = Vm / R * D_1 * h.faceGrad * inner_sum_1.faceValue
and inner_sum_1 is the complicated inner sum in the phase transformation term. The $\nabla h$ part has been taken out of the inner sum. You can either use (h.grad * inner_sum_1).faceValue or h.faceGrad * inner_sum_1.faceValue or use face values for the variables that constitute inner_sum_1. I don't know how much difference it makes. After both the C_1 and C_2 equations are defined in a similar manner, then combine them into one equation with
eqn = eqn1 & eqn2

Related

How do you iterate through multiple arrays and substitute values into an equation?

Don't know if I phrased the question correctly because it's sort of hard to explain. But I have three arrays which represent temperature, salinity, and depth. They're massive so I've put the simplified versions of them below just to get the gist:
t = (np.arange(26)[25:21:-1]).reshape(2,2)
s = (np.arange(34,35,0.25).reshape(2,2))
z = (np.arange(0,100,25).reshape(2,2))
I have this equation here (also stripped down for simplicity):
velocity = 1402.5 + 5*(t) - (5.44 * 10**(-2) * t**(-2)) + (2.1 * 10**(-4) * t**(3)) + 1.33*(s) - (1.56*10**(-2)*z)
What I want to do is to iterate through the values from the t,s,z arrays and have them substituted into the equation to calculate velocity for each case. I want the resultant value to then append into a new array with the same configuration - (2,2) in this case. I can't seem to figure out the best way to approach this, so any sort of feedback would be appreciated.
Cheers!
Just use the same equation as-is with one change:
velocity = 1402.5 + 5*(t) - (5.44 * 10**(-2.0) * t**(-2.0)) + (2.1 * 10**(-4) * t**(3)) + 1.33*(s) - (1.56*10**(-2)*z)
Change: t**(-2) has been changed to t**(-2.0). To better understand why we need to change the type of the exponent see the answer here: https://stackoverflow.com/a/43287598/13389591.
The above gives the output:
[[1576.00116296 1570.56544556]
[1565.15996716 1559.7834676 ]]

How to efficiently evaluate or approximate a road Clothoid?

I'm facing the problem of computing values of a clothoid in C in real-time.
First I tried using the Matlab coder to obtain auto-generated C code for the quadgk-integrator for the Fresnel formulas. This essentially works great in my test scnearios. The only issue is that it runs incredibly slow (in Matlab as well as the auto-generated code).
Another option was interpolating a data-table of the unit clothoid connecting the sample points via straight lines (linear interpolation). I gave up after I found out that for only small changes in curvature (tiny steps along the clothoid) the results were obviously degrading to lines. What a surprise...
I know that circles may be plotted using a different formula but low changes in curvature are often encountered in real-world-scenarios and 30k sampling points in between the headings 0° and 360° didn't provide enough angular resolution for my problems.
Then I tried a Taylor approximation around the R = inf point hoping that there would be significant curvatures everywhere I wanted them to be. I soon realized I couldn't use more than 4 terms (power of 15) as the polynom otherwise quickly becomes unstable (probably due to numerical inaccuracies in double precision fp-computation). Thus obviously accuracy quickly degrades for large t values. And by "large t values" I'm talking about every point on the clothoid that represents a curve of more than 90° w.r.t. the zero curvature point.
For instance when evaluating a road that goes from R=150m to R=125m while making a 90° turn I'm way outside the region of valid approximation. Instead I'm in the range of 204.5° - 294.5° whereas my Taylor limit would be at around 90° of the unit clothoid.
I'm kinda done randomly trying out things now. I mean I could just try to spend time on the dozens of papers one finds on that topic. Or I could try to improve or combine some of the methods described above. Maybe there even exists an integrate function in Matlab that is compatible with the Coder and fast enough.
This problem is so fundamental it feels to me I shouldn't have that much trouble solving it. any suggetions?
about the 4 terms in Taylor series - you should be able to use much more. total theta of 2pi is certainly doable, with doubles.
you're probably calculating each term in isolation, according to the full formula, calculating full factorial and power values. that is the reason for losing precision extremely fast.
instead, calculate the terms progressively, the next one from the previous one. Find the formula for the ratio of the next term over the previous one in the series, and use it.
For increased precision, do not calculate in theta by rather in the distance, s (to not lose the precision on scaling).
your example is an extremely flat clothoid. if I made no mistake, it goes from (25/22) pi =~ 204.545° to (36/22) pi =~ 294.545° (why not include these details in your question?). Nevertheless it should be OK. Even 2 pi = 360°, the full circle (and twice that), should pose no problem.
given: r = 150 -> 125, 90 degrees turn :
r s = A^2 = 150 s = 125 (s+x)
=> 1+(x/s) = 150/125 = 1 + 25/125 x/s = 1/5
theta = s^2/2A^2 = s^2 / (300 s) = s / 300 ; = (pi/2) * (25/11) = 204.545°
theta2 = (s+x)^2/(300 s) = (6/5)^2 s / 300 ; = (pi/2) * (36/11) = 294.545°
theta2 - theta = ( 36/25 - 1 ) s / 300 == pi/2
=> s = 300 * (pi/2) * (25/11) = 1070.99749554 x = s/5 = 214.1994991
A^2 = 150 s = 150 * 300 * (pi/2) * (25/11)
a = sqrt (2 A^2) = 300 sqrt ( (pi/2) * (25/11) ) = 566.83264608
The reference point is at r = Infinity, where theta = 0.
we have x = a INT[u=0..(s/a)] cos(u^2) d(u) where a = sqrt(2 r s) and theta = (s/a)^2. write out the Taylor series for cos, and integrate it, term-by-term, to get your Taylor approximation for x as function of distance, s, along the curve, from the 0-point. that's all.
next you have to decide with what density to calculate your points along the clothoid. you can find it from a desired tolerance value above the chord, for your minimal radius of 125. these points will thus define the approximation of the curve by line segments, drawn between the consecutive points.
I am doing my thesis in the same area right now.
My approach is the following.
at each point on your clothoid, calculate the following (change in heading / distance traveled along your clothoid), by this formula you can calculate the curvature at each point by this simple equation.
you are going to plot each curvature value, your x-axis will be the distance along the clothoid, the y axis will be the curvature. By plotting this and applying very easy linear regression algorithm (search for Peuker algorithm implementation in your language of choice)
you can easily identify where are the curve sections with value of zero (Line has no curvature), or linearly increasing or decreasing (Euler spiral CCW/CW), or constant value != 0 (arc has constant curvature across all points on it).
I hope this will help you a little bit.
You can find my code on github. I implemented some algorithms for such problems like Peuker Algorithm.

Compact storage coefficients of a multivariate polynomial

The setup
I am writing a code for dealing with polynomials of degree n over d-dimensional variable x and ran into a problem that others have likely faced in the past. Such polynomial can be characterized by coefficients c(alpha) corresponding to x^alpha, where alpha is a length d multi-index specifying the powers the d variables must be raised to.
The dimension and order are completely general, but known at compile time, and could be easily as high as n = 30 and d = 10, though probably not at the same time. The coefficients are dense, in the sense that most coefficients are non-zero.
The number of coefficients required to specify such a polynomial is n + d choose n, which in high dimensions is much less than n^d coefficients that could fill a cube of side length n. As a result, in my situation I have to store the coefficients rather compactly. This is at a price, because retrieving a coefficient for a given multi-index alpha requires knowing its location.
The question
Is there a (straightforward) function mapping a d-dimensional multi-index alpha to a position in an array of length (n + d) choose n?
Ordering combinations
A well-known way to order combinations can be found on this wikipedia page. Very briefly you order the combinations lexically so you can easily count the number of lower combinations. An explanation can be found in the sections Ordering combinations and Place of a combination in the ordering.
Precomputing the binomial coefficients will speed up the index calculation.
Associating monomials with combinations
If we can now associate each monomial with a combination we can effectively order them with the method above. Since each coefficient corresponds with such a monomial this would provide the answer you're looking for. Luckily if
alpha = (a[1], a[2], ..., a[d])
then the combination you're looking for is
combination = (a[1] + 0, a[1] + a[2] + 1, ..., a[1] + a[2] + ... + a[d] + d - 1)
The index can then readily be calculated with the formula from the wikipedia page.
A better, more object oriented solution, would be to create Monomial and Polynomial classes. The Polynomial class would encapsulate a collection of Monomials. That way you can easily model a pathological case like
y(x) = 1.0 + x^50
using just two terms rather than 51.
Another solution would be a map/dictionary where the key was the exponent and the value is the coefficient. That would only require two entries for my pathological case. You're in business if you have a C/C++ hash map.
Personally, I don't think doing it the naive way with arrays is so terrible, even with a polynomial containing 1000 terms. RAM is cheap; that array won't make or break you.

Freefem Fisher's equation

I'm new with Freefem++, the problem I'm trying to solve is Fisher's equation:
du/dt = d^2u/dx^2 + d^2u/dy^2 + k * u * (1-u)
du/dn = 0 - border condition
I've tried to reformulate the problem in weak form, however Freefem shows a mistake with formula:
problem Fisher(uh, vh) = int2d(Th)(uh * vh/dt + Grad(uh)' * Grad(vh) ) - int2d(Th)(k * uh * vh) + int2d(Th)(uh0 * vh/dt) - int2d(Th)(k * uh * vh * uh);
Could you please tell what I do wrong? Something is wrong with last terms.
This is a 2D transient diffusion/conduction equation with a temperature-dependent, non-linear generation term.
If you leave off the non-linear generation term, the equations should look exactly like the weak form for the 2D transient diffusion/conduction equation.
How does freefem++ linearize that non-linear term? How did you plan to handle it?
You realize, of course, that the last term makes the solution a very different animal. You have to use iteration within time steps to solve it (e.g. a Newton-Raphson solver).
The algorithm becomes an iterative, non-linear one. You won't solve for u anymore; you'll solve for an increment du and iterate until convergence.
You linearize the last term like this:
d(k*u(1-u)) = k*du(1-u) - k*u*du = k*(1-2*u)*du ~ k*du
You still have a product u*du that's non-linear. What to do? Throw it away.
Now you're solving a non-linear transient equation for du.
The easiest way to model Fisher's equations is to linearize the nonlinear part so that the computational method stays stable. In our case it means that in discrete formulations we replace the term u_i(1 - u_i) with u_{i-1}(1 - u_i) (where i is time counter) and choose attentively the space and time steps. Here I provide an example of resulting code:
verbosity=0.;
real Dx=.1,Dy=.1;
mesh Th=square(floor(10./Dx),floor(10./Dy), [-5 + 10*x, -5 + 10*y]);
fespace Vh(Th,P1);
Vh uh, vh, uh0 = ((x)^2+(y)^2)<=1;
real mu = 0.1, dt=0.01, Tf=10., k = 3.0;
macro Grad(u)[dx(u),dy(u)]//
problem KFisher(uh,vh) = int2d(Th)(uh*vh/dt + Grad(uh)'*Grad(vh)*mu) - int2d(Th)(uh0*vh/dt) + int2d(Th)(k*uh0*uh*vh) - int2d(Th)(k*vh*uh0);
for (real t=0.;t<Tf;t+=dt)
{
KFisher;
uh0 = uh;
plot(uh0, cmm="t="+t+"[sec]", dim=2, fill=true, value=true, wait=0);
}

Uniformly sampling on hyperplanes

Given the vector size N, I want to generate a vector <s1,s2, ..., sn> that s1+s2+...+sn = S.
Known 0<S<1 and si < S. Also such vectors generated should be uniformly distributed.
Any code in C that helps explain would be great!
The code here seems to do the trick, though it's rather complex.
I would probably settle for a simpler rejection-based algorithm, namely: pick an orthonormal basis in n-dimensional space starting with the hyperplane's normal vector. Transform each of the points (S,0,0,0..0), (0,S,0,0..0) into that basis and store the minimum and maximum along each of the basis vectors. Sample uniformly each component in the new basis, except for the first one (the normal vector), which is always S, then transform back to the original space and check if the constraints are satisfied. If they are not, sample again.
P.S. I think this is more of a maths question, actually, could be a good idea to ask at http://maths.stackexchange.com or http://stats.stackexchange.com
[I'll skip "hyper-" prefix for simplicity]
One of possible ideas: generate many uniformly distributed points in some enclosing volume and project them on the target part of plane.
To get uniform distribution the volume must be shaped like the part of plane but with added margins along plane normal.
To uniformly generate points in such volumewe can enclose it in a cube and reject everything outside of the volume.
select margin, let's take margin=S for simplicity (once margin is positive it affects only performance)
generate a point in cube [-M,S+M]x[-M,S+M]x[-M,S+M]
if distance to the plane is more than M, reject the point and go to #2
project the point on the plane
check that projection falls into [0,S]x[0,S]x[0,S], if not - reject and go to #2
add this point to the resulting set and go to #2 is you need more points
The problem can be mapped to that of sampling on linear polytopes for which the common approaches are Monte Carlo methods, Random Walks, and hit-and-run methods (see https://www.jmlr.org/papers/volume19/18-158/18-158.pdf for examples a short comparison). It is related to linear programming, and can be extended to manifolds.
There is also the analysis of polytopes in compositional data analysis, e.g. https://link.springer.com/content/pdf/10.1023/A:1023818214614.pdf, which provide an invertible transformation between the plane and the polytope that can be used for sampling.
If you are working on low dimensions, you can use also rejection sampling. This means you first sample on the plane containing the polytope (defined by your inequalities). This later method is easy to implement (and wasteful, of course), the GNU Octave (I let the author of the question re-implement in C) code below is an example.
The first requirement is to get vector orthogonal to the hyperplane. For a sum of N variables this is n = (1,...,1). The second requirement is a point on the plane. For your example that could be p = (S,...,S)/N.
Now any point on the plane satisfies n^T * (x - p) = 0
we assume also that x_i >= 0
With these given you compute an orthonormal basis on the plane (the nullity of the vector n) and then create random combination on that bases. Finally you map back to the original space and apply your constraints on the generated samples.
# Example in 3D
dim = 3;
S = 1;
n = ones(dim, 1); # perpendicular vector
p = S * ones(dim, 1) / dim;
# null-space of the perpendicular vector (transposed, i.e. row vector)
# this generates a basis in the plane
V = null (n.');
# These steps are just to reduce the amount of samples that are rejected
# we build a tight bounding box
bb = S * eye(dim); # each column is a corner of the constrained region
# project on the null-space
w_bb = V \ (bb - repmat(p, 1, dim));
wmin = min (w_bb(:));
wmax = max (w_bb(:));
# random combinations and map back
nsamples = 1e3;
w = wmin + (wmax - wmin) * rand(dim - 1, nsamples);
x = V * w + p;
# mask the points inside the polytope
msk = true(1, nsamples);
for i = 1:dim
msk &= (x(i,:) >= 0);
endfor
x_in = x(:, msk); # inside the polytope (your samples)
x_out = x(:, !msk); # outside the polytope
# plot the results
scatter3 (x(1,:), x(2,:), x(3,:), 8, double(msk), 'filled');
hold on
plot3(bb(1,:), bb(2,:), bb(3,:), 'xr')
axis image

Resources