Community RSS Feed
http://community.wolfram.com
RSS Feed for Wolfram Community showing any discussions in tag Engineering sorted by activeChemical adsorption in fixed beds: PDE with boundary conditions
http://community.wolfram.com/groups/-/m/t/1470321
*NOTE: originally posted in response to:* http://community.wolfram.com/groups/-/m/t/1398247
----------
![Concentration Plot Zoomed][2]
# Related Notebooks
There are a couple of Mathematica notebooks related to chromatography that you may find useful. The first is from Professor Brian Higgins called [ChromatographyAnalysis.nb](https://sites.google.com/site/chemengwithmathematica/home/reaction-engineering/mathematica-notebooks-1/ChromatogaphyAnalysis.nb?attredirects=0&d=1) from his Ekaya Solutions website. The website has a plethora of Mathematica notebooks related to chemical engineering. If you have Wolfram SystemModeler, there is a [Chromatography Example Model](https://www.wolfram.com/system-modeler/examples/more/life-sciences/chromatography-column) from the Wolfram website.
# Basic Problem
To get the behavior that you desire, there is an implied step function that is missing from the equations that you posted. For "irreversible" adsorption, the time derivative of adsorbate loading must equal 0 at saturation. We also know that the solute concentration should be highest when the media is saturated. You will need to add a step function to drive the time derivative of W to be 0 at saturation. The result will be a phase boundary that moves and your equations are only valid for solid that is unsaturated. Therefore, the domain shape will be a function of time. I believe that this will mean that there are no analytical solutions available from DSolve and that you will need to use NDSolve to solve the system of PDEs. Traditionally, one would make a quasi-steady-state assumption and use a saturated moving boundary to obtain an approximate solution. I think an NDSolve solution will be instructive to show how the bed evolves over time and potentially give insights on simplified models.
# Dimensional Analysis
If you will indulge me for a moment, I would like to make the system of equations dimensionless so that we reduce the dependence to a minimum number of meaningful dimensionless groups with variables that have a nice scale.
There is a fluid phase and a solid phase in your system, so it makes sense that there should be a state variable for each phase. Our initial system of PDEs is given by:
$$\begin{gathered}
\varepsilon \frac{{\partial {c_d}}}{{\partial {t_d}}} + \left( {1 - \varepsilon } \right){\rho _p}\frac{{\partial {W_d}}}{{\partial {t_d}}} = - u\frac{{\partial {c_d}}}{{\partial {x_d}}} \qquad (1) \\
\left( {1 - \varepsilon } \right){\rho _p}\frac{{\partial {W_d}}}{{\partial {t_d}}} = {K_c}a\left( {{c_d} - c_d^*} \right) \qquad (2) \\
\end{gathered}$$
I added a subscript d to indicate that there are dimensions associated with those variables.
Next, to make variables dimensionless, simply divide the dimensioned variables by characteristic dimensions. For characteristic time, we will use the convective timescale or:
$$\begin{gathered}
{\tau _c} = \frac{{\varepsilon L}}{u} \\
t = \frac{{{t_d}}}{{{\tau _c}}};{t_d} = {\tau _c}t \\
x = \frac{{{x_d}}}{L};{x_d} = Lx \\
c = \frac{{{c_d}}}{{{c_0}}};{c_d} = {c_0}c \\
w = \frac{{{W_d}}}{{{W_{sat}}}};{W_d} = {W_{sat}}w \\
\end{gathered}$$
Now, let's return to equation (1) and make the appropriate substitutions.
$$\frac{\varepsilon }{{{\tau _c}}}{c_0}\frac{{\partial c}}{{\partial t}} + \left( {1 - \varepsilon } \right){\rho _p}\frac{{{W_{sat}}}}{{{\tau _c}}}\frac{{\partial W}}{{\partial t}} = - \frac{{u{c_0}}}{L}\frac{{\partial c}}{{\partial x}}\left\| {\frac{{\varepsilon L}}{u}} \right.\frac{1}{{\varepsilon {c_0}}}$$
Perform some algebra
$$\begin{gathered}
\frac{\varepsilon }{{{\tau _c}}}\frac{L}{{u{c_0}}}{c_0}\frac{{\partial c}}{{\partial t}} + \left( {1 - \varepsilon } \right){\rho _p}\frac{{{W_{sat}}}}{{{\tau _c}}}\frac{{\varepsilon L}}{u}\frac{1}{{\varepsilon {c_0}}}\frac{{\partial w}}{{\partial t}} = - \frac{{\partial c}}{{\partial x}} \\
\frac{{\partial c}}{{\partial t}} + \frac{{\left( {1 - \varepsilon } \right)}}{\varepsilon }\frac{{{\rho _p}}}{{{c_0}}}{W_{sat}}\frac{{\partial w}}{{\partial t}} = - \frac{{\partial c}}{{\partial x}} \\
m = \frac{{\left( {1 - \varepsilon } \right)}}{\varepsilon }\frac{{{\rho _p}}}{{{c_0}}}{W_{sat}} \\
\boxed{\frac{{\partial c}}{{\partial t}} + m\frac{{\partial w}}{{\partial t}} + \frac{{\partial c}}{{\partial x}} = 0} \\
\end{gathered}$$
The boxed formula represents the dimensionless version of (1). The dimensionless group, m, can be thought of as a loading factor (i.e., how much the adsorbant bed can adsorb versus the incoming concentration $c_0$. Now, we continue with equation (2).
$$\begin{gathered}
\left( {1 - \varepsilon } \right){\rho _p}\frac{{{W_{sat}}}}{{{\tau _c}}}\frac{{\partial w}}{{\partial t}} = {K_c}a{c_0}\left( {c - {c^*}} \right)\left\| {\frac{{{\tau _c}}}{{\varepsilon {c_0}}}} \right. \\
\frac{{\left( {1 - \varepsilon } \right)}}{\varepsilon }\frac{{{\rho _p}}}{{{c_0}}}{W_{sat}}\frac{{\partial w}}{{\partial t}} = \frac{{{K_c}a}}{\varepsilon }{\tau _c}\left( {c - {c^*}} \right) \\
m\frac{{\partial w}}{{\partial t}} = \frac{{{\tau _c}}}{{{\tau _a}}}\left( {c - {c^*}} \right);{\tau _a} = \frac{\varepsilon }{{{K_c}a}};n = \frac{{{\tau _c}}}{{{\tau _a}}} \\
\boxed{m\frac{{\partial w}}{{\partial t}} = n\left( {c - {c^*}} \right)} \\
\end{gathered} $$
The boxed formula represents the dimensionless version of (2). The new dimensionless group, n, can be thought of as the ratio of the convective time scale to the adsorption time scale.
Your system of PDEs for irreversible adsorption in dimensionless terms would be:
$$\begin{gathered}
\frac{{\partial c}}{{\partial t}} + m\frac{{\partial w}}{{\partial t}} + \frac{{\partial c}}{{\partial x}} = 0 \qquad (3) \\
m\frac{{\partial w}}{{\partial t}} = nc \qquad (4) \\
\end{gathered} $$
To correct equation (4) for the "equilibrium" condition of ${\left. {\frac{{\partial w}}{{\partial t}}} \right|_{w = 1}} = 0$ , we will need add a unit step function, $\theta$, to drive the time derivative to 0 at saturation and bringing over the left hand side of the equation or:
$$m\frac{{\partial w}}{{\partial t}} - nc\theta (1 - w)=0\qquad(4*)$$
# Bed Saturation Start-up Transient
If we start with a clean bed, it will take some time before the bed saturates at $x=0$. We can use equation (4) evaluated at $x=0$ to determine the time to initially saturate the front of the bed.
$$m{\left. {\frac{{\partial w}}{{\partial t}}} \right|_{x = 0}} = nc_0=n\qquad Conditions = \left\{ {\begin{array}{*{20}{c}}
{IC}&{w(t = 0,x = 0) = 0} \\
{FC}&{w(t = {t_{sat}},x = 0) = 1}
\end{array}} \right\}$$
$$w(t,x=0)=\frac{n}{m}t$$
$$\therefore t_{sat}=\frac{m}{n}$$
We can check our NDSolve solutions to verify that they approximate this relation.
# Mathematica Implementation
We should be able to use NDSolve to solve this system of PDEs. I had some performance issues with UnitStep\[\] so I opted to use the Tanh\[\] function to create a sharp, but differentially continuous approximation to the UnitStep\[\] function. Perhaps, there is a more elegant Mathematica implementation, but the Tanh\[\] functions seems to be working for $m$ and $n$ < 20.
Finally, we need two initial conditions (the bed and liquid is initially free of adsorbate species) and one boundary condition (a "UnitStep" of species at the inlet).
The initial conditions:
$$\begin{gathered}
c[0,x] = 0 \\
w[0,x] = 0 \\
\end{gathered} $$
The boundary condition:
$$c[t,0] = \tanh (200t)$$
This yields the following set of equations.
eqn1 = D[cp[t, x], t] + m D[wp[t, x], t] + D[cp[t, x], x] == 0;
eqn2 = m D[wp[t, x], t] -
n cp[t, x] (1 - (1 + Tanh[200 (wp[t, x] - 1)])/2) == 0;
ics = {wp[0, x] == 0, cp[0, x] == 0};
bcs = {cp[t, 0] == Tanh[200 t]};
eqns = {eqn1, eqn2} ~ Join ~ ics ~ Join ~ bcs;
opts = Method -> {"MethodOfLines",
"SpatialDiscretization" -> {"TensorProductGrid",
"MinPoints" -> 5000}};
The dimensional analysis says that this system of equations depends on only two dimensionless parameters suggesting that ParametricNDSolveValue\[\] might be the flavor of NDSolve\[\] to use. To resolve the sharp features, we will specify a large number of spatial points as shown in opts and a small timestep as shown below to return a parameteric function, _pfun_.
pfun = ParametricNDSolveValue[
eqns, {cp, wp}, {t, 0, 10}, {x, 0, 1.1}, {m, n}, opts,
MaxStepFraction -> 1/1000];
With a parametric function, it is easy to return an interpolation function, _ifun_, based on the parameters $m$ and $n$. We will set the loading factor, $m$, to 5 and the convective timescale to adsorption timescale, $n$, to 10 (we want the adsorption to be fast relative to convection). The following takes a couple of minutes to run on my machine.
ifun = pfun[5, 10];
c = ifun[[1]];
w = ifun[[2]];
We can plot the particular solution of the fluid solute concentration, $c(t,x)$, with Plot3D\[\].
Plot3D[Evaluate[{c[t, x]}], {x, 0, 1}, {t, 0, 10},
PlotRange -> All, PlotPoints -> {200, 200},
ColorFunction -> (ColorData["DarkBands"][#3] &),
MeshFunctions -> {#2 &}, Mesh -> 20, AxesLabel -> Automatic,
MeshStyle -> {Black, Thick}, ImageSize -> Large]
![Concentration Plot][1]
We see some discontinuities at small $t$ as the bed starts loading to saturation. We can zoom into the small $t$ and $x$ for a clearer picture.
Plot3D[Evaluate[{c[t,x]}], {x, 0, 0.35}, {t, 0, 1},
PlotRange -> All, PlotPoints -> {200, 200},
ColorFunction -> (ColorData["DarkBands"][#3] &),
MeshFunctions -> {#2 &}, Mesh -> 50, AxesLabel -> Automatic,
MeshStyle -> {Black, Thick}]
![Concentration Plot Zoomed][2]
We predicted that the bed at $x=0$ should staturate at $t=\frac{m}{n}=\frac{5}{10}=0.5$ and the plot does indicate that the bed does indeed begin to saturate at the predicted time. We also see that the bed saturation front grows linearly in time. Also, post saturation, the concentration falls off approximately exponentially with $x$ at constant time from the saturation boundary. Prior to initial saturation, the MeshFunctions curves do not appear that they can be described by simple functions. Even if a DSolve solution existed, it probably would not have a simple form.
A countour plot of $w$ and $c$ after initial saturation may give us some insights to a simplified model. The first contour plot is of the solid loading.
ContourPlot[
Evaluate[{w[t + m/n, x]}], {x, 0, 5/(m + 1.3)}, {t, 0, 5},
ColorFunction -> "DarkBands", Mesh -> 40, MeshFunctions -> {#3 &},
FrameLabel -> {"x", "t"}, ImageSize -> Large]
![Solids Loading][3]
A remarkably linear _Saturation Boundary_ forms after $t=\frac{m}{n}$. The contour lines look essentially parallel implying that we might model $w$ as function of a single variable perpendicular to the phase boundary. The concentration countour plot looks similar but there is an accumulation error that grows linearly in time.
ContourPlot[
Evaluate[{c[t + m/n, x]}], {x, 0, 5/(m + 1.3)}, {t, 0, 5},
ColorFunction -> "DarkBands", Mesh -> 40, MeshFunctions -> {#3 &},
PlotRange -> {0, 1}, FrameLabel -> {"x", "t"}, ImageSize -> Large]
![Fluid Concentration Contour Plot][4]
If we restrict the domain to only consider the unsaturated bed, then we can eliminate $w$ by combining equations (3) and (4) because the UnitStep function containing $w$ can be ignored.
$$\begin{gathered}
\frac{{\partial c}}{{\partial t}} + m\frac{{\partial w}}{{\partial t}} + \frac{{\partial c}}{{\partial x}} = 0 \qquad (3) \\
m\frac{{\partial w}}{{\partial t}} = nc \qquad (4) \\
\\
\frac{{\partial c}}{{\partial t}} + \frac{{\partial c}}{{\partial x}}+ nc = 0 \qquad (5)
\end{gathered} $$
You can use Mathematica to evaluate the partial derivatives somewhere in the unsaturated region to assess their magnitudes.
D[c[t, x], t] /. {t -> 2.5, x -> 0.4} (*0.8176759782035153`*)
D[c[t, x], x] /. {t -> 2.5, x -> 0.4} (*-4.995343977849113`*)
Mathematica suggests that the spatial derivative is much larger in absolute magnitude than the time derivative. We could make the pseudo steady-state assumption and (5) becomes a simple first order ODE as shown in (6).
$$\frac{{\partial c}}{{\partial x}} + nc = 0 \qquad (6) $$
More rigorously, but still approximately, we could try a transformed coordinate system as depicted in the diagram below so that the moving boundary line given by $t=m x + \frac{m}{n}$ coincides with the ${t}'$ axis. In this reference frame, (5) should depend only on one independent variable ${x}'$ and the moving boundary goes away.
![Transformed Boundary][5]
To transform $(x,t)\rightarrow ({x}',{t}')$ we can use Mathematica's Transform functions to first rotate the original coordinate system by $-\theta=ArcTan[m,1]$ then translate up to $\frac{m}{n}$. Transformations can always be confusing so it is good to have a check to see if the transform is valid. In the transformed coodinates, the moving boundary line should simply be ${x}'=0$ .
theta = ArcTan[m, 1];
rt = RotationTransform[-theta];
tt = TranslationTransform[{0, m/n}];
trf = tt@*rt;
tr = trf@{xp, tp} // Simplify;
rules = Thread[{x, t} -> tr];(*{x -> (tp + m*xp)/Sqrt[1 + m^2], t -> m*(n^(-1) + tp/Sqrt[1 + m^2]) - xp/Sqrt[1 + m^2]}*)
We can apply the transformation rules to the moving boundary line to show that indeed ${x}'=0$.
(t == m x + m/n) /. rules // Simplify (*Sqrt[1 + m^2]*xp == 0*)
But we want the inverse transform to get ${x}'$ in terms of $x$ and $t$, which we can obtain with the InverseFunction\[\].
invtrf = InverseFunction[trf];
invtr = invtrf@{x, t} // Simplify;
invrules = Thread[{xp, tp} -> invtr] // InputForm
(*{xp -> (m - n*t + m*n*x)/(Sqrt[1 + m^2]*n), tp -> (-m^2 + m*n*t + n*x)/(Sqrt[1 + m^2]*n)}*)
Revisting (5), we will combine the $x$ and $t$ variables into one independent variable ${x}'$ using the chain rule as shown below:
\begin{gathered}
\frac{{\partial c}}{{\partial t}} + \frac{{\partial c}}{{\partial x}} + nc = 0; \\
\frac{{\partial x'}}{{\partial t}}\frac{{\partial c}}{{\partial x'}} + \frac{{\partial x'}}{{\partial x}}\frac{{\partial c}}{{\partial x'}} + nc = 0; \\
\left( {\frac{1}{n}} \right)\left( {\frac{{\partial x'}}{{\partial t}} + \frac{{\partial x'}}{{\partial x}}} \right)\frac{{\partial c}}{{\partial x'}} + c = 0 \\
\end{gathered}
Mathematica can evaluate and simplify the change of variables derivatives as shown below.
1/n (D[#, t] + D[#, x]) & [xp /. invrules] // Simplify (*(-1 + m)/(Sqrt[1 + m^2]*n)*)
Leaving us with equation (7) and the corresponding solution (we could use DSolve, but the solution to this ODE is straightforward).
$$\begin{gathered}
\frac{{m - 1}}{{n\sqrt {1 + {m^2}} }}\frac{{dc}}{{dx'}} + c = 0 \qquad (7) \\
c({x}')=e^{-\frac{n\sqrt{1+m^{2}}}{m-1}{x}'} \\
\end{gathered}$$
We can substitute and simplify $x$ and $t$ for ${x}'$ using the $invrules$ previously defined.
E^(-((Sqrt[1 + m^2]*n*xp)/(-1 + m))) /. invrules // InputForm (* E^(-((m - n*t + m*n*x)/(-1 + m))) *)
We recognize that $c$ is only defined to the right of the moving boundary to obtain the following piecewise function.
$$c(t,x)=\begin{Bmatrix}
e^{\frac{n \left(\left(t-\frac{m}{n}\right)- m x\right)}{m-1}} & m x>t-\frac{m}{n}\\
1 & \text{True}
\end{Bmatrix}$$
If you wanted to wrap the whole process in one Mathematica call, you could do something like the following.
cc[xp /. invrules] /. First@DSolve[{
(1/n (D[#, t] + D[#, x]) & [xp /. invrules]) D[cc[xp], xp] +
cc[xp] == 0,
cc[0] == 1},
cc,
xp
] // InputForm (* E^(-((m-n*t+m*n*x)/(-1+m))) *)
We will now create a function called _breakthrough_ for the simplified model and a plotting function to compare the simple model to the result obtained by ParametricNDSolveValue\[\].
(* Simplified model *)
breakthrough[m_, n_, t_, x_] :=
Piecewise[{{Exp[n ((t - m/n) - m x)/(m - 1)], m x > (t - m/n)}, {1,
True}}]
(* Plot function to compare simplified model to NDSolve for given m,n, and t *)
pltfn := Plot[{Callout[breakthrough[#1, #2, #3, x], "Model", (
Log[2] (-1 + #1) - #1 + #2 #3)/(#1 #2),
CalloutMarker -> "CirclePoint"],
Callout[c[#3, x], "NDSolve", (
Log[2] (-1 + #1) - #1 + #2 #3)/(#1 #2),
CalloutMarker -> "CirclePoint"]}, {x, 0, 1},
PlotRange -> {0, 1.05}, Filling -> {1 -> {2}}, Exclusions -> None,
PlotStyle -> Thick, Frame -> True,
FrameLabel -> {Style["x", 18], Style["c(t,x)", 18],
Style[StringForm["m=``, n=``, and t=``", #1, #2,
NumberForm[#3, {2, 1}, NumberPadding -> {" ", "0"}]], 18]},
ImageSize -> Large] & ;
(* Create a series of images at different times for animation *)
plts := Table[pltfn[#1, #2, t], {t, #1/#2, 10, 0.1}] &;
Here is an animation of the current solution for $m=5$ and $n=10$.
Export["510.gif", plts[5,10], "AnimationRepetitions" -> \[Infinity]]
![m=5 n=10 animated gif][6]
The accumulation term causes NDSolve to lag our simple model. At larger $m$ and $n$ values, this lag is reduced. It is easy to insert $m=20$ and $n=10$ into our parametric function and animate the result to verify the accumulation lag is reduced. Now, the simple model approximates well the NDSolve solution.
ifun = pfun[20, 10];
c = ifun[[1]];
Export["2010.gif", plts[20, 10],
"AnimationRepetitions" -> \[Infinity]]
![m=20 n=10 animated gif][7]
# Summary
- One needs to add a "UnitStep" function to stop adsorption when the bed becomes saturated.
- This will create a moving boundary meaning that the shape of the domain changes with time.
- Probably means DSolve will not find a solution.
- Dimensional analysis simplifies the equations and reduces the number of parameters that the equations depend on.
- ParametricNDSolveValue\[\] can be used to solve the system of PDEs.
- Using Plot3D and ContourPlot, we gain insights on a simplified model for the moving boundary.
- We can use Mathematica's Transform functions to create a new coordinate system that is perpendicular to the moving saturation boundary creating a simple solvable ODE.
- We created a simple model that tracks the results of NDSolve and the model should only get better at larger $m$ and $n$ values.
[1]: http://community.wolfram.com//c/portal/getImageAttachment?filename=ConcentrationPlot.png&userId=1402928
[2]: http://community.wolfram.com//c/portal/getImageAttachment?filename=ConcentrationPlotStartUp.png&userId=1402928
[3]: http://community.wolfram.com//c/portal/getImageAttachment?filename=LoadingContour.png&userId=1402928
[4]: http://community.wolfram.com//c/portal/getImageAttachment?filename=ConcentrationContour.png&userId=1402928
[5]: http://community.wolfram.com//c/portal/getImageAttachment?filename=SaturationBoundary.png&userId=1402928
[6]: http://community.wolfram.com//c/portal/getImageAttachment?filename=510.gif&userId=1402928
[7]: http://community.wolfram.com//c/portal/getImageAttachment?filename=2010.gif&userId=1402928Tim Laska2018-09-20T18:39:38ZCoupled convective diffusive PDE in multiple regions with discontinuities
http://community.wolfram.com/groups/-/m/t/1470252
*NOTE: originally posted in response to:* http://community.wolfram.com/groups/-/m/t/1395518
----------
![enter image description here][10]
I looked at this problem as an opportunity to build some modeling and simulation skills within the _Mathematica_ framework and to brush up on an area of physics that I have not looked at in decades. Solving a coupled convective diffusive PDE in multiple regions with discontinuous physical properties can be quite challenging. In the end, I was pleasantly surprised that I was able to "model" this system and obtain reasonable results without too much difficulty. I am documenting my path here in the hopes that someone will find it useful.
# Preliminaries
I agree that there is no analytical solution to this system of coupled PDEs, and that a numerical solution is in order. The solution to the simplest convection-diffusion equation $$\rho C_pv_x\frac{\partial T}{\partial x}=k \frac{\partial^2 T}{\partial y^2}$$ for a parallel plate channel with fully developed uniform flow subjected to a uniform heat flux, [Sparrow and Siegel, 1960, pg 167](https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/20150018974.pdf) showed is an infinite series. For a coupled problem, I would presume that the infinite series coefficients would depend on the infinite series coefficients of the other variables making it intractable. [Ouyang _et al_](http://vafai.engr.ucr.edu/Publications/2013a/Ouyang.pdf) obtain an approximate solution with an infinite series solution to a thermally developing Darcy flow in porous media.
In the FEM, it is very easy to over constrain or under constrain a system leading to bizarre results. The developers of particular tools usually have developed a framework that facilitates the construction and solving of models. It is best to operate within that framework so that we can create a functioning model with least amount of effort.
Also, in the FEM, the mesh is critcal to finding an accurate solution. In your case, you have two regions with distinct physical properties. We expect that there will be extreme changes in behavior at the interface of the two regions so we will want to resolve that area with a finer mesh.
Finally, I would recommend to start small and build complexity verifying and validating along the way.
# System Description
I always like to begin with a picture of the system I am trying to model. My understanding is that there is a fluid flowing over a porous material as shown in the picture below. The flow in the x direction is assumed to be laminar parabolic flow in the fluid domain and constant uniform in the porous domain.
#### System Sketch
![enter image description here][1]
Physically, there must be an impermeable solid barrier between the two domains otherwise we would expect some flow in the Y direction. We will take advantage of this fact later.
# Porous Media Domain Only
### Porous Media Energy Transport Equations
As stated previously, the developers have created a framework to facilitate FEM modeling and simulation. For me, I find it often is easier to start over with that in mind than to start in the middle.
The equations below for energy transport with Local Themal Non-Equilibrium (LTNE) look formally similar to yours if we eliminate the transient and volumetric source terms shown in $\color{Red}{Red}$. If they are not, you should be able to follow the logic to develop your own.
$$\begin{gathered}
{\color{Red}{(1 - \varepsilon ){\left( {\rho {{\hat C}_p}} \right)_s}\frac{{\partial {T_s}}}{{\partial t}}}}+ {\left( {\rho {{\hat C}_p}} \right)_s}\left( {{\mathbf{v}}_s \cdot \nabla {T_s}} \right) - (1 - \varepsilon )\nabla \cdot {k_s}\nabla {T_s} - {\color{Red}{(1 - \varepsilon )q_s^{'''}}} - h({T_f} - {T_s}) = 0 \qquad (1*) \\
{\color{Red}{ \varepsilon {\left( {\rho {{\hat C}_p}} \right)_f}\frac{{\partial {T_f}}}{{\partial t}}}} + {\left( {\rho {{\hat C}_p}} \right)_f}\left( {{\mathbf{v}} \cdot \nabla {T_f}} \right) - \varepsilon \nabla \cdot {k_f}\nabla {T_f} - {\color{Red}{\varepsilon q_f^{'''}}} - h({T_s} - {T_f}) = 0 \qquad (2*)\\
\end{gathered}$$
Steady-state no volumetric source terms version
$$\begin{gathered}
{\left( {\rho {{\hat C}_p}} \right)_s}\left( {{\mathbf{v}}_s \cdot \nabla {T_s}} \right)- (1 - \varepsilon )\nabla \cdot {k_s}\nabla {T_s} - h({T_f} - {T_s}) = 0 \qquad (1) \\
+ {\left( {\rho {{\hat C}_p}} \right)_f}\left( {{\mathbf{v}} \cdot \nabla {T_f}} \right) - \varepsilon \nabla \cdot {k_f}\nabla {T_f} - h({T_s} - {T_f}) = 0 \qquad (2)\\
\end{gathered}$$
Normally, we assume that the solid is fixed $\left(v_s={0,0}\right)$, but in this case we will retain it to use it to create an interface mixing rule. Otherwise, the solid density and heat capacity drops out of the equation.
### Dimensional Analysis
When possible, I prefer to work with dimensionless equations. To make the system dimensionless, we divide the variables by characteristic variables and perform some factoring so the left hand and right hand side of the equations are dimensionless. Dimensionless temperatures and coordinates are defined as:
$$\begin{gathered}
{\Theta _s} = \frac{{{T_s} - {T_0}}}{{{T_c}}};{T_s} = {T_c}{\Theta _s} + T \\
{\Theta _f} = \frac{{{T_f} - {T_0}}}{{{T_c}}};{T_f} = {T_c}{\Theta _f} + T \\
xs = \frac{x}{D};x = D \cdot xs \\
ys = \frac{y}{D};y = D \cdot ys \\
T_c = \frac{q_{0}D}{k_f}
\end{gathered} $$
Making the appropriate substitutions, equations (1) and (2) become:
$$\begin{gathered}
\frac{{\left( {\rho {{\hat C}_p}} \right)_s}}{{\left( {\rho {{\hat C}_p}} \right)_f}}{{\mathbf{v}}^*}_s \cdot {\nabla ^*}{\Theta _s}- \left( {1 - \varepsilon } \right)\frac{{{k_s}}}{{{k_f}}}\frac{{{\tau _{conv}}}}{{{\tau _{cond}}}}{\nabla ^*}^2{\Theta _s} - \frac{{{\tau _{conv}}}}{{{\tau _{flux}}}}({\Theta _f} - {\Theta _s}) = 0 \qquad (3) \\
{{\mathbf{v}}^*} \cdot {\nabla ^*}{\Theta _f} - \varepsilon \frac{{{\tau _{conv}}}}{{{\tau _{cond}}}}{\nabla ^*}^2{\Theta _f} - \frac{{{\tau _{conv}}}}{{{\tau _{flux}}}}({\Theta _s} - {\Theta _f}) = 0 \qquad (4) \\
\end{gathered} $$
Where the various timescales are defined by:
$$\begin{gathered}
{\tau _{conv}} = \frac{D}{{{v_0}}} \\
{\tau _{cond}} = \frac{{{D^2}}}{\alpha };\alpha = \frac{{{k_f}}}{{{{\left( {\rho {{\hat C}_p}} \right)}_f}}} \\
{\tau _{flux}} = \frac{{{{\left( {\rho {{\hat C}_p}} \right)}_f}}}{h} \\
\end{gathered} $$
Consolidating the timescale ratios and noting that porosity and velocity are a function of the mesh region we are in.
$$\begin{gathered}
\kappa{{\mathbf{v}}^*}_s(\Omega ) \cdot {\nabla ^*}{\Theta _s}- \left( {1 - \varepsilon (\Omega )} \right)\sigma \gamma {\nabla ^*}^2{\Theta _s} - \eta ({\Theta _f} - {\Theta _s}) = 0 \qquad (5) \\
{{\mathbf{v}}^*}(\Omega ) \cdot {\nabla ^*}{\Theta _f} - \varepsilon (\Omega )\gamma {\nabla ^*}^2{\Theta _f} - \eta ({\Theta _s} - {\Theta _f}) = 0 \qquad (6) \\
\end{gathered} $$
Where
$$\begin{gathered}
\kappa = \frac{{\left( {\rho {{\hat C}_p}} \right)_s}}{{\left( {\rho {{\hat C}_p}} \right)_f}} \\
\sigma = \frac{{{k_s}}}{{{k_f}}} \\
\gamma = \frac{{{\tau _{conv}}}}{{{\tau _{cond}}}} \\
\eta = \frac{{{\tau _{conv}}}}{{{\tau _{flux}}}} \\
{{\mathbf{v}}^*} = \frac{{\mathbf{v}}}{{{v_0}}} \\
{{\mathbf{v}_s}^*} = \frac{{\mathbf{v}_s}}{{{v_0}}}
\end{gathered} $$
Now, we have a system of PDEs that depend on 5 dimensionless parameters.
### Neumann Boundary Condition (Flux) on Porous Boundary
When applying a flux on the porous boundary, there are different possible pathways through the fluid and solid. We must decide how to partition the heat flux. For parallel pathways, the effective conductivity is given by:
$${k_{eff}} = \left( {1 - \varepsilon } \right){k_s} + \varepsilon {k_f}$$
$$\frac{{{k_{eff}}}}{{{k_f}}} = \left( {1 - \varepsilon } \right)\sigma + \varepsilon $$
We will use this as a guide to partition the flux accordingly.
$$\begin{gathered}
{x_f} = \frac{\varepsilon }{{\left( {1 - \varepsilon } \right)\sigma + \varepsilon }} \\
{q_f} = {x_f}{q_{tot}} \\
{q_s} = (1 - {x_f}){q_{tot}} \\
\end{gathered} $$
## Modeling in _Mathematica_
With modeling, I am a firm believer that one should start simple, verify and validate the simulation results, and then build complexity. We will assume the convective diffusive heat equation has been previously validated.
Let's start with a model of the porous domain only.
### Mesh Creation
I followed the tutorial in the documentation for __[Element Mesh Creation](https://reference.wolfram.com/language/FEMDocumentation/tutorial/ElementMeshCreation.html)__ to generate a mesh tailored to the physical situation. You will need to load the FEM package like so
Needs["NDSolve`FEM`"]
Let's start with some definitions of a domain the is 1 unit high and 2 units long. We will also create some associations to collect surfaces and regions for clarity.
ht = 1;
len = 2;
top = ht;
bot = 0;
left = 0;
right = len;
bounds = <|inlet -> 1, hot -> 2|>;
regs = <|fluid -> 10, porous -> 20 , interface -> 15|>;
We can create a boundary mesh using the ToBoundaryMesh\[\] command and mark unique edges to be used as boundary conditions later. All other edges will be assigned the default boundary condtion of an insulated wall. The left edge colored $\color{Green}{Green}$ will describe the inlet condition and the bottom edge colored $\color{Red}{Red}$ will describe the heat flux condtion. All other boundaries will be the default Neumann zero flux condition.
bmesh = ToBoundaryMesh[
"Coordinates" -> {{left, bot}(*1*), {right, bot}(*2*), {right,
top}(*3*), {left, top}(*4*)},
"BoundaryElements" -> {LineElement[{{1, 2}(*bottom edge*)(*1*), {4,
1}(*left edge*)(*2*), {2, 3}(*3*), {3, 4}(*4*)}, {bounds[
hot], bounds[inlet], 3, 3}]}];
bmesh["Wireframe"["MeshElementMarkerStyle" -> Blue,
"MeshElementStyle" -> {Green, Red, Black}, ImageSize -> Large]]
#### Boundary Mesh Highlighting Special Boundaries
![enter image description here][2]
Now, we will create an element mesh that will have a $\color{Red}{Red}$ porous region with marker ID 20 (regs\[porous\]).
mesh = ToElementMesh[bmesh,
"RegionMarker" -> {{{(left + right)/2, (bot + top)/2},
regs[porous], 0.002}}];
mesh["Wireframe"["MeshElementStyle" -> {FaceForm[Red]},
ImageSize -> Large]]
#### Surface Mesh
![enter image description here][3]
# _Mathematica_ Implementation
By investing the time we did in mesh development, we can take advantage of some facilities provided by the Wolfram developers. First, we will convert our symbols to Latin for easier reading and define some initial parameters for testing.
$$\begin{gathered}
\varepsilon = eps = 0.4 \\
\sigma = s = 10 \\
\gamma = g = 2 \\
\eta = h = 0.2 \\
q_{tot} = 10 \\
v_{f} = \binom{6}{0} \\
v_{s} = \binom{0}{0}
\end{gathered} $$
eps = 0.4;
s = 10;
g = 2;
h = 1/5;
mu = 5000/h;
qtot = 10;
(* Derived Parameters *)
xf = eps/((1 - eps) s + eps);
hcapfrac = 5000;
qf = xf qtot;
qs = ( 1 - xf) qtot;
p = eps;
v = {6, 0};
vs = {0, 0};
fac = 1;
We can also set our Neumann/flux boundary conditions for each state variable on the hot wall.
nvsolid = NeumannValue[qs, ElementMarker == bounds[hot]];
nvfluid = NeumannValue[qf, ElementMarker == bounds[hot]];
Likewise, we can set the inlet boundary for each state variable to 0 with Dirichlet conditions
dcsolid =
DirichletCondition[ts[x, y] == 0, ElementMarker == bounds[inlet]];
dcfluid =
DirichletCondition[tf[x, y] == 0, ElementMarker == bounds[inlet]];
Recasting (5) and (6) into _Mathematica_ format (remember to put Neumann values on the right side of the equation where 0 was) we obtain:
fluideqn =
v.Inactive[Grad][tf[x, y], {x, y}] -
p g Inactive[Laplacian][tf[x, y], {x, y}] -
fac h (ts[x, y] - tf[x, y]) == nvfluid;
solideqn =
hcapfrac vs.Inactive[Grad][ts[x, y], {x, y}] - (1 -
p) g s Inactive[Laplacian][ts[x, y], {x, y}] -
fac h (tf[x, y] - ts[x, y]) == nvsolid;
Now, we can solve on the element mesh we created earlier and do a 3D plot of the solution (Solid Temperature is translucent and has dashed lines).
ifun = NDSolveValue[{fluideqn, solideqn, dcfluid, dcsolid}, {tf,
ts}, {x, y} \[Element] mesh];
opac = If[
ifun[[1]][0.5, 0.5] > ifun[[2]][0.5, 0.5], {0.25, 1}, {1, 0.25}];
pltf = Plot3D[ifun[[1]][x, y], {x, y} \[Element] mesh,
PlotRange -> All, PlotPoints -> {200, 200},
ColorFunction -> (Directive[Opacity[opac[[1]]],
ColorData["DarkBands"][#3]] &), MeshFunctions -> {#3 &},
Mesh -> 10, AxesLabel -> Automatic, MeshStyle -> {Black, Thick},
ImageSize -> Large];
plts = Plot3D[ifun[[2]][x, y], {x, y} \[Element] mesh,
PlotRange -> All, PlotPoints -> {200, 200},
ColorFunction -> (Directive[Opacity[opac[[2]]],
ColorData["DarkBands"][#3]] &), MeshFunctions -> {#3 &},
Mesh -> 10, AxesLabel -> Automatic,
MeshStyle -> {Black, Thick, Dashed}, ImageSize -> Large];
Grid[{{Show[{pltf, plts}, ViewProjection -> "Orthographic",
ViewPoint -> Front, ImageSize -> 450,
Background -> RGBColor[0.84`, 0.92`, 1.`], Boxed -> False],
Show[{pltf, plts}, ViewProjection -> "Orthographic",
ViewPoint -> Left , ImageSize -> 450,
Background -> RGBColor[0.84`, 0.92`, 1.`],
Boxed -> False] }, {Show[{pltf, plts},
ViewProjection -> "Orthographic", ViewPoint -> Top ,
ImageSize -> 450, Background -> RGBColor[0.84`, 0.92`, 1.`],
Boxed -> False],
Show[{pltf, plts}, ViewProjection -> "Perspective",
ViewPoint -> { Above, Right, Front} , ImageSize -> 450,
Background -> RGBColor[0.84`, 0.92`, 1.`], Boxed -> False]}},
Dividers -> Center]
#### Solution from Several Views
![enter image description here][4]
## Comparison to Another Code
Because we specified a low value of $\eta$, the heat transfer between the solid and fluid phase is poor leading to a large separation of temperatures. I think the plots look nice, but do they have any basis in reality? It is always good practice to validate models versus experiments or other codes when possible.
Fortunately, I have access to other codes and can say _Mathematica_ compares favorably as shown below ($T_{s} = Thick\ Lines$). Having such agreement gives me confidence to continue to the more complex model.
#### Results from Another Code for Porous Only Domain
![enter image description here][5]
We shoud note that ${\left. T_s\neq T_f \right|_{y = top}}$ at the top of the insulated wall.
# Porous Media + Fluid Domain
How energy is transferred through the interface between two types of media can be tricky business. Also, I don't believe that _Mathematica_ can have internal boundary conditions currently. However, _Mathematica_ provides other machinery to create an "interface". Recall, that I claimed that physically there must be a solid impermeable layer between the porous media and the fluid interface otherwise the porous media flow would short circuit into the fluid domain. So, let's include a thin layer in the model. This thin layer will allow us to equilibrate the solid and fluid temperatures by drastically increasing the fluid-solid heat transfer coefficient to the correct heat capacity weighted value and ramping the porosity to unity at the fluid interface so that we don't need to add another equation. A conceptual sketch is shown below.
#### Conceptual Model Zoomed into Interface Region
![enter image description here][6]
$$\begin{gathered}
\kappa{{\mathbf{v}}^*}_s(\Omega ) \cdot {\nabla ^*}{\Theta _s}- \left( {1 - \varepsilon (\Omega )} \right)\sigma \gamma {\nabla ^*}^2{\Theta _s} - -\mu(\Omega )\eta ({\Theta _f} - {\Theta _s}) = 0 \qquad (7) \\
{{\mathbf{v}}^*}(\Omega ) \cdot {\nabla ^*}{\Theta _f} - \varepsilon (\Omega )\gamma {\nabla ^*}^2{\Theta _f} -\mu(\Omega ) \eta ({\Theta _s} - {\Theta _f}) = 0 \qquad (8) \\
\end{gathered} $$
Where
$${{\mathbf{v}}^*}_s(\Omega )=
\begin{Bmatrix}
\binom{0}{0.001} & \Omega=Interface\\
\binom{0}{0} & True
\end{Bmatrix}$$
$${{\mathbf{v}}^*}(\Omega )=
\begin{Bmatrix}
\binom{6}{0} & \Omega=Porous\\
\binom{20 (1 - (\frac{y}{r})^2}{0} & \Omega=Fluid\\
\binom{0}{0.001} & \Omega=Interface\\
\end{Bmatrix}$$
$$\varepsilon(\Omega )=
\begin{Bmatrix}
\varepsilon_0 & \Omega=Porous\\
\frac{\left(1-\varepsilon_0\right) (r+thick+y)}{thick}+\varepsilon _0 & \Omega=Interface\\
1 & True
\end{Bmatrix}$$
$$\mu(\Omega )=
\begin{Bmatrix}
\frac{500,000}{\eta} & \Omega=Interface\\
1 & True
\end{Bmatrix}$$
### Mesh Creation for Porous, Fluid, and Coupling Interface
We will create a domain that is 1 unit in height by 2 units in length. The fluid ($\color{Green}{Green}$) will occupy the top 1/4 of the domain. The interface layer ($\color{Cyan}{Cyan}$) will be 1/40 the height. We will mark special boundaries and regions with unique IDs for boundary condition assignment and to change properties and mesh refine regions.
ht = 1;
len = 2;
r = ht/8;
top = r;
bot = r - ht;
left = 0;
right = len;
thick = ht/40;
interfacef = -r;
interfaces = -r - thick;
buffer = 1.5 thick;
(* Use associations for clearer assignment later *)
bounds = <|inlet -> 1, hot -> 2|>;
regs = <|porous -> 10, fluid -> 20, interface -> 15|>;
(* Meshing Definitions *)
(* Coordinates *)
crds = {{left, bot}(*1*), {right, bot}(*2*), {right, top}(*3*), {left,
top}(*4*), {left, interfacef}(*5*), {right,
interfacef}(*6*), {left, interfaces}(*7*), {right, interfaces}(*8*)};
(* Edges *)
lelms = {{1, 7}, {7, 5}, {5, 4}(*left edge*)(*1*), {1,
2}(*bottom edge*)(*2*), {2, 8}, {8, 6}, {6, 3}, {3, 4}, {5,
6}, {7, 8}(*3*)};
boundaryMarker = {bounds[inlet], bounds[inlet], bounds[inlet],
bounds[hot], 3, 3, 3, 3, 3, 3}; (* 3 will be a default boundary *)
bcEle = {LineElement[lelms, boundaryMarker]};
bmesh = ToBoundaryMesh["Coordinates" -> crds,
"BoundaryElements" -> bcEle];
bmesh["Wireframe"["MeshElementMarkerStyle" -> Blue,
"MeshElementStyle" -> {Green, Red, Black}, ImageSize -> Large]]
(* 2D Regions *)
(* Identify Center Points of Different Material Regions *)
fluidCenter = {(left + right)/2, 0};
fluidReg = {fluidCenter, regs[fluid], 0.0005};
interfaceCenter = {(left + right)/2, (interfaces + interfacef)/2};
interfaceReg = {interfaceCenter, regs[interface], 0.00005};
porousCenter = {(left + right)/2, (bot + interfaces)/2};
porousReg = {porousCenter, regs[porous], 0.002};
meshRegs = {fluidReg, interfaceReg, porousReg};
mesh = ToElementMesh[bmesh, "RegionMarker" -> meshRegs,
MeshRefinementFunction ->
Function[{vertices, area}, Block[{x, y}, {x, y} = Mean[vertices];
If[y > (interfaces + interfacef)/2 - buffer &&
y < (interfaces + interfacef)/2 + buffer, area > 0.00005,
area > 0.01]]]];
mesh["Wireframe"[
"MeshElementStyle" -> {FaceForm[Green], FaceForm[Yellow],
FaceForm[Red]}, ImageSize -> Large]]
Show[mesh[
"Wireframe"[
"MeshElementStyle" -> {FaceForm[Green], FaceForm[Yellow],
FaceForm[Red]}, ImageSize -> Large]],
PlotRange -> {{0,
0.5}, {(interfaces + interfacef)/2 -
4 buffer, (interfaces + interfacef)/2 + 4 buffer}}]
#### Boundary Mesh
![enter image description here][7]
#### Full Mesh
![enter image description here][8]
#### Mesh Zoomed into Interface Region
![enter image description here][9]
### NDSolve Setup and Solution
eps = 0.4;
s = 10;
g = 2;
h = 1/5;
mu = 500000/h;
qtot = 10;
xf = eps/((1 - eps) s + eps);
kappa = 500;
qf = xf qtot;
qs = ( 1 - xf) qtot;
(* Region Dependent Properties with Piecewise Functions *)
p = Evaluate[Piecewise[{{eps, ElementMarker == regs[porous]},
{(1 - eps)/thick (y + (r + thick)) + eps,
ElementMarker == regs[interface]},
{1, True}}]];
v = Evaluate[Piecewise[{{{6, 0}, ElementMarker == regs[porous]},
{{20 (1 - (y/r)^2), 0}, ElementMarker == regs[fluid]},
{{0, 0.0001}, ElementMarker == regs[interface]}}]];
vs = Evaluate[
Piecewise[{{{0, 0.0001}, ElementMarker == regs[interface]},
{{0, 0}, True}}]];
(* fac increases heat transfer coefficient in interface layer *)
fac = Evaluate[If[ElementMarker == regs[interface], mu, 1]];
(* Neumann Conditions for the Hot Bottom Wall *)
nvsolid = NeumannValue[qs, ElementMarker == bounds[hot]];
nvfluid = NeumannValue[qf, ElementMarker == bounds[hot]];
(* Dirichlet Conditions for the Left Wall *)
dcsolid =
DirichletCondition[ts[x, y] == 0, ElementMarker == bounds[inlet]];
dcfluid =
DirichletCondition[tf[x, y] == 0, ElementMarker == bounds[inlet]];
(* Balance Equations for Fluid and Solid Temperature *)
fluideqn =
v.Inactive[Grad][tf[x, y], {x, y}] -
p g Inactive[Laplacian][tf[x, y], {x, y}] -
fac h (ts[x, y] - tf[x, y]) == nvfluid;
solideqn =
kappa vs.Inactive[Grad][ts[x, y], {x, y}] - (1 - p) g s Inactive[
Laplacian][ts[x, y], {x, y}] - fac h (tf[x, y] - ts[x, y]) ==
nvsolid;
ifun = NDSolveValue[{fluideqn, solideqn, dcfluid, dcsolid}, {tf,
ts}, {x, y} \[Element] mesh];
(* Set opacity for higher temperature to be translucent so we can see \
the layer below *)
opac = If[
ifun[[1]][0.5, (-r + bot)/2] > ifun[[2]][0.5, (-r + bot)/2], {0.25,
1}, {1, 0.25}];
(* Plots *)
pltf = Plot3D[ifun[[1]][x, y], {x, y} \[Element] mesh,
PlotRange -> All, PlotPoints -> {200, 200},
ColorFunction -> (Directive[Opacity[opac[[1]]],
ColorData["DarkBands"][#3]] &), MeshFunctions -> {#3 &},
Mesh -> 18, AxesLabel -> Automatic, MeshStyle -> {Black, Thick},
ImageSize -> Large];
plts = Plot3D[ifun[[2]][x, y], {x, y} \[Element] mesh,
PlotRange -> All, PlotPoints -> {200, 200},
ColorFunction -> (Directive[Opacity[opac[[2]]],
ColorData["DarkBands"][#3]] &), MeshFunctions -> {#3 &},
Mesh -> 18, AxesLabel -> Automatic,
MeshStyle -> {Black, Thick, Dashed}, ImageSize -> Large];
Grid[{{Show[{pltf, plts}, ViewProjection -> "Orthographic",
ViewPoint -> Front, ImageSize -> 400,
Background -> RGBColor[0.84`, 0.92`, 1.`], Boxed -> False],
Show[{pltf, plts}, ViewProjection -> "Orthographic",
ViewPoint -> Left , ImageSize -> 400,
Background -> RGBColor[0.84`, 0.92`, 1.`],
Boxed -> False] }, {Show[{pltf, plts},
ViewProjection -> "Orthographic", ViewPoint -> Top ,
ImageSize -> 400, Background -> RGBColor[0.84`, 0.92`, 1.`],
Boxed -> False],
Show[{pltf, plts}, ViewProjection -> "Perspective",
ViewPoint -> { Above, Right, Front} , ImageSize -> 400,
Background -> RGBColor[0.84`, 0.92`, 1.`], Boxed -> False]}},
Dividers -> Center]
#### Full Model Porous + Interface + Fluid
![enter image description here][10]
## Comparison to Another Code
Again, as shown below, the _Mathematica_ solution compares qualitatively favorably to another code. The contour plots have similar shapes but _Mathematica_ has about 40% higher temperatures, which could indicate that I dropped or added a $\varepsilon$ somewhere in the setup of the other code, but a detailed debugging is probably in order. The implementation in the other code is slightly different due to different capabilities which I will elaborate. In the other code, the interface layer is a just a high thermal conductivity solid. I am able to set the porous-solid interface temperature to be equal to the porous temperature (solid-fluid bulk mix temperature).
#### Another Code Full Model Porous + Interface + Fluid
![enter image description here][11]
Finally, because I know that I can set an interface boundary condition in the other code, I can remove the interface region and set the fluid boundary condition to the porous mean temperature and judge the effect. As shown in the image below, it probably would not change conclusions much.
#### Another Code Full Model Porous + Fluid No Interface
![enter image description here][12]
# Future Enhancements
After looking at the documentation and reading the literature, I should recast the equations to conform to the coefficient form of the PDE so that I could additionally scale the $x$ coordinate by the Péclet number. This would allow for easier comparison to literature and keep a more reasonable aspect ratio for the domain.
$$\nabla \cdot\left( { - \mathbf{c}\nabla u - \alpha u + \gamma } \right) + \beta \cdot\nabla u + au - f = 0$$
# Summary
- An exact analytical solution to an LTNE problem coupled to a fluid domain seems improbable.
- _Mathematica_
- Currently, I don't think interface BCs can be defined in the FEM framework.
- Does provide meshing features that allow the construction of an interface with finite thickness that might be used a workaround to this limitation.
- Results
- Porous Media Only Domain.
- Quite easy to setup a mesh and identify unique boundaries and regions.
- Straightforward to translate our system of PDEs created by hand into equations suitable for _Mathematica_.
- Relatively easy to post process results using examples from documentation.
- Solution compares favorably to another PDE code.
- Combined Porous Media--Interface--Fluid Domains
- Quite easy to setup a mesh with appropriate refinement and identify unique boundaries and regions.
- Straightforward to translate our system of PDEs created by hand into equations suitable for _Mathematica_.
- Relatively easy to post process results using examples from documentation.
- Solution compares qualitatively favorably to another PDE code.
- Additional study/debugging will be required to fully harmonize the results.
- Could be a simple matter of missing a parameter or something deeper.
- Conclusion
- A prototype of a simple LTNE model was developed in _Mathematica_.
- In modeling, there are many ways to skin a cat and this is just one possible way that produces a solution in a resonable period. More elegant solutions are welcome.
- Additional model development and validation is necessary.
- _Mathematica_ provides enough features and tools that you can get close to what you need with some imagination.
# Attachment
The attached notebook has the complete code to produce a solution to study and modify as one sees fit. I did recast the equations into coefficient form because that appears to be better practice.
Storing the solution will increase notebook size >100MB.
[1]: http://community.wolfram.com//c/portal/getImageAttachment?filename=SystemDescription.png&userId=1402928
[2]: http://community.wolfram.com//c/portal/getImageAttachment?filename=porousonlyboundaries.png&userId=1402928
[3]: http://community.wolfram.com//c/portal/getImageAttachment?filename=porousonlymesh.png&userId=1402928
[4]: http://community.wolfram.com//c/portal/getImageAttachment?filename=porousonlyPlot3D.png&userId=1402928
[5]: http://community.wolfram.com//c/portal/getImageAttachment?filename=LTNEPorousOnly2.png&userId=1402928
[6]: http://community.wolfram.com//c/portal/getImageAttachment?filename=InterfaceLayer.png&userId=1402928
[7]: http://community.wolfram.com//c/portal/getImageAttachment?filename=fullmodelboundaries.png&userId=1402928
[8]: http://community.wolfram.com//c/portal/getImageAttachment?filename=fullmodelmesh.png&userId=1402928
[9]: http://community.wolfram.com//c/portal/getImageAttachment?filename=fullmodelzoom.png&userId=1402928
[10]: http://community.wolfram.com//c/portal/getImageAttachment?filename=PorousMediaSimulation.png&userId=1402928
[11]: http://community.wolfram.com//c/portal/getImageAttachment?filename=LTNEPConductorFluid4.png&userId=1402928
[12]: http://community.wolfram.com//c/portal/getImageAttachment?filename=LTNEPorousFluidNoInterface.png&userId=1402928Tim Laska2018-09-20T17:43:10ZSolver for unsteady flow with the use of Mathematica FEM
http://community.wolfram.com/groups/-/m/t/1433064
![fig7][331]
I started the discussion [here][1] but I also want to repeat on this forum.
There are many commercial and open code for solving the problems of unsteady flows.
We are interested in the possibility of solving these problems using Mathematica FEM. Previously proposed solvers for stationary incompressible isothermal flows:
Solving 2D Incompressible Flows using Finite Elements:
http://community.wolfram.com/groups/-/m/t/610335
FEM Solver for Navier-Stokes equations in 2D:
http://community.wolfram.com/groups/-/m/t/611304
Nonlinear FEM Solver for Navier-Stokes equations in 2D:
https://mathematica.stackexchange.com/questions/94914/nonlinear-fem-solver-for-navier-stokes-equations-in-2d/96579#96579
We give several examples of the successful application of the finite element method for solving unsteady problem including nonisothermal and compressible flows. We will begin with two standard tests that were proposed to solve this class of problems by
M. Schäfer and S. Turek, Benchmark computations of laminar ﬂow around a cylinder (With support by F. Durst, E. Krause and R. Rannacher). In E. Hirschel, editor, Flow Simulation with High-Performance Computers II. DFG priority research program results 1993-1995, number 52 in Notes Numer. Fluid Mech., pp.547–566. Vieweg, Weisbaden, 1996. https://www.uio.no/studier/emner/matnat/math/MEK4300/v14/undervisningsmateriale/schaeferturek1996.pdf
![fig8][332]
Let us consider the flow in a flat channel around a cylinder at Reynolds number = 100, when self-oscillations occur leading to the detachment of vortices in the aft part of cylinder. In this problem it is necessary to calculate drag coeﬃcient, lift coeﬃcient and pressure diﬀerence in the frontal and aft part of the cylinder as functions of time, maximum drag coeﬃcient, maximum lift coeﬃcient , Strouhal number and pressure diﬀerence $\Delta P(t)$ at $t = t0 +1/2f$. The frequency f is determined by the period of oscillations of lift coeﬃcient f=f(c_L). The data for this test, the code and the results are shown below.
H = .41; L = 2.2; {x0, y0, r0} = {1/5, 1/5, 1/20};
Ω = RegionDifference[Rectangle[{0, 0}, {L, H}], Disk[{x0, y0}, r0]];
RegionPlot[Ω, AspectRatio -> Automatic]
K = 2000; Um = 1.5; ν = 10^-3; t0 = .004;
U0[y_, t_] := 4*Um*y/H*(1 - y/H)
UX[0][x_, y_] := 0;
VY[0][x_, y_] := 0;
P0[0][x_, y_] := 0;
Do[
{UX[i], VY[i], P0[i]} =
NDSolveValue[{{Inactive[
Div][({{-μ, 0}, {0, -μ}}.Inactive[Grad][
u[x, y], {x, y}]), {x, y}] +
\!\(\*SuperscriptBox[\(p\),
TagBox[
RowBox[{"(",
RowBox[{"1", ",", "0"}], ")"}],
Derivative],
MultilineFunction->None]\)[x, y] + (u[x, y] - UX[i - 1][x, y])/t0 +
UX[i - 1][x, y]*D[u[x, y], x] +
VY[i - 1][x, y]*D[u[x, y], y],
Inactive[
Div][({{-μ, 0}, {0, -μ}}.Inactive[Grad][
v[x, y], {x, y}]), {x, y}] +
\!\(\*SuperscriptBox[\(p\),
TagBox[
RowBox[{"(",
RowBox[{"0", ",", "1"}], ")"}],
Derivative],
MultilineFunction->None]\)[x, y] + (v[x, y] - VY[i - 1][x, y])/t0 +
UX[i - 1][x, y]*D[v[x, y], x] +
VY[i - 1][x, y]*D[v[x, y], y],
\!\(\*SuperscriptBox[\(u\),
TagBox[
RowBox[{"(",
RowBox[{"1", ",", "0"}], ")"}],
Derivative],
MultilineFunction->None]\)[x, y] +
\!\(\*SuperscriptBox[\(v\),
TagBox[
RowBox[{"(",
RowBox[{"0", ",", "1"}], ")"}],
Derivative],
MultilineFunction->None]\)[x, y]} == {0, 0, 0} /. μ -> ν, {
DirichletCondition[{u[x, y] == U0[y, i*t0], v[x, y] == 0},
x == 0.],
DirichletCondition[{u[x, y] == 0., v[x, y] == 0.},
0 <= x <= L && y == 0 || y == H],
DirichletCondition[{u[x, y] == 0,
v[x, y] == 0}, (x - x0)^2 + (y - y0)^2 == r0^2],
DirichletCondition[p[x, y] == P0[i - 1][x, y], x == L]}}, {u, v,
p}, {x, y} ∈ Ω,
Method -> {"FiniteElement",
"InterpolationOrder" -> {u -> 2, v -> 2, p -> 1},
"MeshOptions" -> {"MaxCellMeasure" -> 0.001}}], {i, 1, K}];
{ContourPlot[UX[K/2][x, y], {x, y} ∈ Ω,
AspectRatio -> Automatic, ColorFunction -> "BlueGreenYellow",
FrameLabel -> {x, y}, PlotLegends -> Automatic, Contours -> 20,
PlotPoints -> 25, PlotLabel -> u, MaxRecursion -> 2],
ContourPlot[VY[K/2][x, y], {x, y} ∈ Ω,
AspectRatio -> Automatic, ColorFunction -> "BlueGreenYellow",
FrameLabel -> {x, y}, PlotLegends -> Automatic, Contours -> 20,
PlotPoints -> 25, PlotLabel -> v, MaxRecursion -> 2,
PlotRange -> All]} // Quiet
{DensityPlot[UX[K][x, y], {x, y} ∈ Ω,
AspectRatio -> Automatic, ColorFunction -> "BlueGreenYellow",
FrameLabel -> {x, y}, PlotLegends -> Automatic, PlotPoints -> 25,
PlotLabel -> u, MaxRecursion -> 2],
DensityPlot[VY[K][x, y], {x, y} ∈ Ω,
AspectRatio -> Automatic, ColorFunction -> "BlueGreenYellow",
FrameLabel -> {x, y}, PlotLegends -> Automatic, PlotPoints -> 25,
PlotLabel -> v, MaxRecursion -> 2, PlotRange -> All]} // Quiet
dPl = Interpolation[
Table[{i*t0, (P0[i][.15, .2] - P0[i][.25, .2])}, {i, 0, K, 1}]];
cD = Table[{t0*i, NIntegrate[(-ν*(-Sin[θ] (Sin[θ]
\!\(\*SuperscriptBox[\(UX[i]\),
TagBox[
RowBox[{"(",
RowBox[{"0", ",", "1"}], ")"}],
Derivative],
MultilineFunction->None]\)[x0 + r Cos[θ],
y0 + r Sin[θ]] + Cos[θ]
\!\(\*SuperscriptBox[\(UX[i]\),
TagBox[
RowBox[{"(",
RowBox[{"1", ",", "0"}], ")"}],
Derivative],
MultilineFunction->None]\)[x0 + r Cos[θ],
y0 + r Sin[θ]]) + Cos[θ] (Sin[θ]
\!\(\*SuperscriptBox[\(VY[i]\),
TagBox[
RowBox[{"(",
RowBox[{"0", ",", "1"}], ")"}],
Derivative],
MultilineFunction->None]\)[x0 + r Cos[θ],
y0 + r Sin[θ]] + Cos[θ]
\!\(\*SuperscriptBox[\(VY[i]\),
TagBox[
RowBox[{"(",
RowBox[{"1", ",", "0"}], ")"}],
Derivative],
MultilineFunction->None]\)[x0 + r Cos[θ],
y0 + r Sin[θ]]))*Sin[θ] -
P0[i][x0 + r Cos[θ], y0 + r Sin[θ]]*
Cos[θ]) /. {r -> r0}, {θ, 0, 2*Pi}]}, {i,
1000, 2000}]; // Quiet
cL = Table[{t0*i, -NIntegrate[(-ν*(-Sin[θ] (Sin[θ]
\!\(\*SuperscriptBox[\(UX[i]\),
TagBox[
RowBox[{"(",
RowBox[{"0", ",", "1"}], ")"}],
Derivative],
MultilineFunction->None]\)[x0 + r Cos[θ],
y0 + r Sin[θ]] + Cos[θ]
\!\(\*SuperscriptBox[\(UX[i]\),
TagBox[
RowBox[{"(",
RowBox[{"1", ",", "0"}], ")"}],
Derivative],
MultilineFunction->None]\)[x0 + r Cos[θ],
y0 + r Sin[θ]]) +
Cos[θ] (Sin[θ]
\!\(\*SuperscriptBox[\(VY[i]\),
TagBox[
RowBox[{"(",
RowBox[{"0", ",", "1"}], ")"}],
Derivative],
MultilineFunction->None]\)[x0 + r Cos[θ],
y0 + r Sin[θ]] + Cos[θ]
\!\(\*SuperscriptBox[\(VY[i]\),
TagBox[
RowBox[{"(",
RowBox[{"1", ",", "0"}], ")"}],
Derivative],
MultilineFunction->None]\)[x0 + r Cos[θ],
y0 + r Sin[θ]]))*Cos[θ] +
P0[i][x0 + r Cos[θ], y0 + r Sin[θ]]*
Sin[θ]) /. {r -> r0}, {θ, 0, 2*Pi}]}, {i,
1000, 2000}]; // Quiet
{ListLinePlot[cD,
AxesLabel -> {"t", "\!\(\*SubscriptBox[\(c\), \(D\)]\)"}],
ListLinePlot[cL,
AxesLabel -> {"t", "\!\(\*SubscriptBox[\(c\), \(L\)]\)"}],
Plot[dPl[x], {x, 0, 8}, AxesLabel -> {"t", "ΔP"}]}
f002 = FindFit[cL, a*.5 + b*.8*Sin[k*16*t + c*1.], {a, b, k, c}, t]
Plot[Evaluate[a*.5 + b*.8*Sin[k*16*t + c*1.] /. f002], {t, 4, 8},
Epilog -> Map[Point, cL]]
k0=k/.f002;
Struhalnumber = .1*16*k0/2/Pi
cLm = MaximalBy[cL, Last]
sol = {Max[cD[[All, 2]]], Max[cL[[All, 2]]], Struhalnumber,
dPl[cLm[[1, 1]] + Pi/(16*k0)]}
In Fig. 1 shows the components of the flow velocity and the required coefficients. Our solution of the problem and what is required in the test
{3.17805, 1.03297, 0.266606, 2.60427}
lowerbound= { 3.2200, 0.9900, 0.2950, 2.4600};
upperbound = {3.2400, 1.0100, 0.3050, 2.5000};
![Fig1][2]
Note that our results differ from allowable by several percent, but if you look at all the results of Table 4 from the cited article, then the agreement is quite acceptable.The worst prediction is for the Strouhal number. We note that we use the explicit Euler method, which gives an underestimate of the Strouhal number, as follows from the data in Table 4.
The next test differs from the previous one in that the input speed varies according to the `U0[y_, t_] := 4*Um*y/H*(1 - y/H)*Sin[Pi*t/8]`. It is necessary to determine the time dependence of the drag and lift parameters for a half-period of oscillation, as well as the pressure drop at the last moment of time. In Fig. 2 shows the components of the flow velocity and the required coefficients. Our solution of the problem and what is required in the test
sol = {3.0438934441256595`,
0.5073345082785012`, -0.11152933279750943`};
lowerbound = {2.9300, 0.4700, -0.1150};
upperbound = {2.9700, 0.4900, -0.1050};
![Fig2][3]
For this test, the agreement with the data in Table 5 is good. Consequently, the two tests are almost completely passed.
I wrote and debugged this code using Mathematics 11.01. But when I ran this code using Mathematics 11.3, I got strange pictures, for example, the disk is represented as a hexagon, the size of the area is changed.
![Fig3][4]
In addition, the numerical solution of the problem has changed, for example, test 2D2
{3.17805, 1.03297, 0.266606, 2.60427} v11.01
{3.15711, 1.11377, 0.266043, 2.54356} v11.03
The attached file contains the working code for test 2D3 describing the flow around the cylinder in a flat channel with a change in the flow velocity.
[1]: http://community.wolfram.com//c/portal/getImageAttachment?filename=test2D2.png&userId=1218692
[2]: http://community.wolfram.com//c/portal/getImageAttachment?filename=test2D2.png&userId=1218692
[3]: http://community.wolfram.com//c/portal/getImageAttachment?filename=test2D3.png&userId=1218692
[4]: http://community.wolfram.com//c/portal/getImageAttachment?filename=Math11.3.png&userId=1218692
[331]: http://community.wolfram.com//c/portal/getImageAttachment?filename=CylinderRe100test2D2.gif&userId=1218692
[332]: http://community.wolfram.com//c/portal/getImageAttachment?filename=2D2test.png&userId=1218692Alexander Trounev2018-08-31T11:44:04ZConnect Mathematica to a Bluetooth 4.0 device?
http://community.wolfram.com/groups/-/m/t/1454347
How does Mathematica connect to heart rate device via Bluetooth 4.0 and analyze the sampled data? The FindDevices command has tried but the device cannot found. Are there any things to be aware or other methods?
FindDevices[]
{DeviceObject[{"Camera", 1}], DeviceObject[{
"FunctionDemo", 1}], DeviceObject[{
"RandomSignalDemo", 1}], DeviceObject[{"WriteDemo", 1}]}Tsai Ming-Chou2018-09-13T09:37:35Z