There is no unique solution for data provided by @Pillsy, since boundary and initial conditions are inconsistent. To show it we just use exact solution in a form:
\[Mu][a_] := 1/80; u0[a_] := 1/80;
u[a_, t_] :=
E^(Inactive[Integrate][-\[Mu][K[1]], {K[1], 1, a}] -
Inactive[Integrate][-\[Mu][K[1]], {K[1], 1, a - t}])*u0[a - t]
u[0, t_] := Integrate[u[x, t], {x, 20, 45}]/25;
Now we can plot u[0,t]
as follows:
Plot[u[0, t], {t, 0, 100}]
So it is smooth function and we can make interpolation in a form
lst = Table[{t, u[0, t] // N}, {t, 0, 100, 1}];
ut = Interpolation[lst];
With ut
we can use NDSolve
directly
sol = NDSolveValue[{D[v[a, t], a] + D[v[a, t], t] == -\[Mu][a]*
v[a, t], v[a, 0] == u0[a], v[0, t] == ut[t]},
v, {a, 0, 100}, {t, 0, 100}]
Here we got message NDSolveValue::ibcinc: Warning: boundary and initial conditions are inconsistent.
Nevertheless numerical solution can be plot and it looks like waved function
Plot3D[sol[a, t], {a, 0, 100}, {t, 0, 100}, Mesh -> None,
ColorFunction -> "Rainbow", AxesLabel -> Automatic]
To avoid the inconsistent of boundary and initial conditions we put in the beginning of the code
u[0, t_] := Integrate[u[x, t], {x, 20, 45}]/25;
Then we get smooth numerical solution
Now we can use method of line implemented by Chris K with some appropriate modifications
Clear[u];
imax = 200;
da = 1/2;
f[a_] := If[20 <= a < 45, 1/25, 0];
\[Mu][a_] := 1/80;
u0[a_] := 1/80;
eqns = Join[{u[0]'[t] ==
da/2 Sum[(u[i + 1]'[t] f[(i + 1) da] + u[i]'[t] f[i da]), {i, 0,
imax - 1}]},
Table[u[i]'[
t] == -(u[i][t] - u[i - 1][t])/da - \[Mu][i da] u[i][t], {i, 1,
imax}]];
ics = Table[u[i][0] == u0[i da], {i, 0, imax}];
unks = Table[u[i], {i, 0, imax}];
tmax = 100;
sol1 = NDSolve[{eqns, ics}, unks, {t, 0, tmax}][[1]];
We make visualization of numerical solution of the system of ODEs and see identical picture as we got for PDE
ListPlot3D[
Flatten[Table[{i da, t, Evaluate[u[i][t] /. sol1]}, {i, 0, imax}, {t,
0, tmax, 1}], 1], AxesLabel -> {"a", "t", "u"},
ColorFunction -> "Rainbow", PlotRange -> All, Mesh -> None]
And more detailed comparison also shows agreement of two solutions
Show[Plot[{sol[10, t]}, {t, 0, 100}],
ListPlot[Table[{t, u[20][t] /. sol1}, {t, 0, 100, 2}],
PlotStyle -> Red]]
One example of growing population with consistent boundary and initial conditions:
\[Mu][a_] := 1/80; u0[a_] := Exp[-a/45];
f[a_] := Piecewise[{{1/10/1.2298542626633067, 20 <= x < 45}, {0,
True}}];
ue[a_, t_] :=
E^(Inactive[Integrate][-\[Mu][K[1]], {K[1], 1, a}] -
Inactive[Integrate][-\[Mu][K[1]], {K[1], 1, a - t}])*u0[a - t]
u1[t_] := NIntegrate[ue[x, t] f[x], {x, 0, 100}] // Quiet;
lst = Table[{t, u1[t]}, {t, 0, 100, 1}];
ut = Interpolation[lst];
sol = NDSolveValue[{D[v[a, t], a] + D[v[a, t], t] == -\[Mu][a]*
v[a, t], v[a, 0] == u0[a], v[0, t] == ut[t]},
v, {a, 0, 100}, {t, 0, 100}]
Visualisation
Plot3D[sol[a, t], {a, 0, 100}, {t, 0, 100}, Mesh -> None,
ColorFunction -> "Rainbow", AxesLabel -> Automatic]
And same solution with method of lines:
imax = 500;
da = 100/imax;
f[a_] := If[20 <= a < 45, 1/10/1.2298542626633067, 0];
\[Mu][a_] := 1/80;
u0[a_] := Exp[-a/45];
eqns = Join[{u[0]'[t] ==
da/2 Sum[(u[i + 1]'[t] f[(i + 1) da] + u[i]'[t] f[i da]), {i, 0,
imax - 1}]},
Table[u[i]'[
t] == -(u[i][t] - u[i - 1][t])/da - \[Mu][i da] u[i][t], {i, 1,
imax}]];
ics = Table[u[i][0] == u0[i da], {i, 0, imax}];
unks = Table[u[i], {i, 0, imax}];
tmax = 100;
sol1 = NDSolve[{eqns, ics}, unks, {t, 0, tmax}][[1]];
Let compare two solution and find out that they have small discrepancies (due to large da
)
Table[Show[
Plot[{sol[i da, t]}, {t, 0, 100}, AxesLabel -> Automatic,
PlotLabel -> Row[{"a = ", i da}]],
ListPlot[Table[{t, u[i][t] /. sol1}, {t, 0, 100, 2}],
PlotStyle -> Red]], {i, 0, imax, 20}]
Last example provided by Pillsy shows divergence of two solutions found out with two methods even if initial data and boundary conditions are consistent. First method:
\[Mu][a_] := 1/80; u0[a_] := If[0 <= a <= 325/6, 65/96 - a/80, 0];
f[a_] := Piecewise[{{1/10, 20 <= x < 45}, {0, True}}];
ue[a_, t_] :=
E^(Inactive[Integrate][-\[Mu][K[1]], {K[1], 1, a}] -
Inactive[Integrate][-\[Mu][K[1]], {K[1], 1, a - t}])*u0[a - t]
u1[t_] := NIntegrate[ue[x, t], {x, 20, 45}]/10 // Quiet;
lst = Table[{t, u1[t]}, {t, 0, 100, 1/4}];
ut = Interpolation[lst];
sol = NDSolveValue[{D[v[a, t], a] + D[v[a, t], t] == -\[Mu][a]*
v[a, t], v[a, 0] == u0[a], v[0, t] == ut[t]},
v, {a, 0, 100}, {t, 0, 100}];
Second method:
imax = 500;
da = 100/imax;
f[a_] := If[20 <= a < 45, 1/10, 0];
\[Mu][a_] := 1/80;
u0[a_] := If[0 <= a <= 325/6, 65/96 - a/80, 0];
eqns = Join[{u[0]'[t] ==
da/2 Sum[(u[i + 1]'[t] f[(i + 1) da] + u[i]'[t] f[i da]), {i, 0,
imax - 1}]},
Table[u[i]'[
t] == -(u[i][t] - u[i - 1][t])/da - \[Mu][i da] u[i][t], {i, 1,
imax}]];
ics = Table[u[i][0] == u0[i da], {i, 0, imax}];
unks = Table[u[i], {i, 0, imax}];
tmax = 100;
sol1 = NDSolve[{eqns, ics}, unks, {t, 0, tmax},
Method -> {"EquationSimplification" -> "Residual"}][[1]];
Now we plot solutions together and see divergence
Table[Show[
Plot[{sol[i da, t]}, {t, 0, 100}, AxesLabel -> Automatic,
PlotLabel -> Row[{"a = ", i da}]],
ListPlot[Table[{t, u[i][t] /. sol1}, {t, 0, 100, 2}],
PlotStyle -> Red, PlotRange -> All]], {i, 0, imax, 20}]
Nevertheless, we can consider all tests above as verification of numerical method of lines for this problem. Now we make next step to develop code with known error of $h^4$, where $h$ is step size. For this we use function GaussianQuadratureWeights[]
to integrate with n-point Gaussian formula for quadrature, and function FiniteDifferenceDerivative
for approximation of derivative $\frac {\partial u}{\partial x}$ with DifferenceOrder
of 4. First we call utilities:
Needs["DifferentialEquations`NDSolveProblems`"]
Needs["DifferentialEquations`NDSolveUtilities`"]
Get["NumericalDifferentialEquationAnalysis`"]
Second step, we define derivative matrix m
and integral vector int
:
np = 400; g = GaussianQuadratureWeights[np, 0, 100];
ugrid = g[[All, 1]]; weights = g[[All, 2]];
fd = NDSolve`FiniteDifferenceDerivative[Derivative[1], ugrid]; m =
fd["DifferentiationMatrix"]; vart =
Table[u[i][t], {i, Length[ugrid]}]; vart1 =
Table[u[i]'[t], {i, Length[ugrid]}]; ux = m.vart; var =
Table[u[i], {i, Length[ugrid]}];
f[a_] := If[20 <= a < 45, 1/10, 0]; int =
Table[f[ugrid[[i]]] weights[[i]], {i, np}];
\[Mu][a_] := 1/80;
u0[a_] := If[0 <= a <= 325/6, 65/96 - a/80, 0];
Third step, we define the system of equations:
eqns = Join[{D[u[1][t], t] == int.vart1},
Table[D[u[i][t], t] == -ux[[i]] - \[Mu][ugrid[[i]]] u[i][t], {i, 2,
Length[ugrid]}]];
ics = Table[u[i][0] == u0[ugrid[[i]]], {i, Length[ugrid]}];
Finally we solve system as
tmax = 100;
sol1 = NDSolve[{eqns, ics}, var, {t, 0, tmax},
Method -> {"EquationSimplification" -> "Residual"}];
With this code we made research to check how solution diverges with np
increasing:
{np, {u[1][100] /. sol1[[1]], u[np][100] /. sol1[[1]]}}
{100, {4.0455, 0.197089}}
{200, {3.791317314610565`, 0.19572819660924937`}};
{400, {3.6951293716506926`, 0.1949809561721866`}};
{800, {3.70082201902361`, 0.19456320959442788`}};
So numerical solution converges very fast with np
increasing, and for np=800
we have picture
lst1 = Flatten[
Table[{t, ugrid[[i]], u[i][t] /. sol1[[1]]}, {t, 0, 100, 2}, {i, 1,
Length[ugrid], 5}], 1];
ListPlot3D[lst1, Mesh -> None, ColorFunction -> Hue, PlotRange -> All,
AxesLabel -> {"t", "a"}]
We have run several tests with known exact solution and got a good agreement of the exact and numerical solution getting with the last code. Example 1 from Numerical methods for the Lotka–McKendrick’s equation (there are typos in this paper in equations (6.8),(6,9) I have corrected using Mathematica 12.1):
f[a_]:=2; \[Mu][a_] := 1/(1 - a);
p0[x_] := If[x <= 1/2, (1 - 2 x)^3 (1 - x), 31 (2 x - 1)^3 (1 - x)];
u0[a_] := p0[a];
With this data we have to compute u[a,t]
on {a,0,1},{t,0,1}
and then compare exact solution $u(0,t)=B(t)$ with numerical solution:
B[t_] := If[t <= 1/2,
217 - 186 t - 372 t^2 - 248 t^3 - 216 E^t Cos[t] + 396 E^t Sin[t],
1/(Sqrt[E] (Cos[1/2]^2 + Sin[1/2]^2)) (-7 Sqrt[E] Cos[1/2]^2 +
6 Sqrt[E] t Cos[1/2]^2 + 12 Sqrt[E] t^2 Cos[1/2]^2 +
8 Sqrt[E] t^3 Cos[1/2]^2 - 216 E^(1/2 + t) Cos[1/2]^2 Cos[t] +
768 E^t Cos[t] Sin[1/2] - 7 Sqrt[E] Sin[1/2]^2 +
6 Sqrt[E] t Sin[1/2]^2 + 12 Sqrt[E] t^2 Sin[1/2]^2 +
8 Sqrt[E] t^3 Sin[1/2]^2 - 216 E^(1/2 + t) Cos[t] Sin[1/2]^2 -
768 E^t Cos[1/2] Sin[t] + 396 E^(1/2 + t) Cos[1/2]^2 Sin[t] +
396 E^(1/2 + t) Sin[1/2]^2 Sin[t])];
In Figure 10 shown numerical solution (left) and exact solution (right, blue line) with numerical solution (red points):
Finally we have to compare absolute error $|B(t)-u(0,t)|$ for code by Chris K and my code to find out accuracy of two code. For Chris code it is obvious that error is of h
and for my code theoretically it should be of $h^3$. But since we solve PDE it is not so perfect. In Figure 11 shown absolute error for Chris code (left) and for my code (right) for imax=np=800. It looks like my code has error of $h^{3/2}$ not $h^3$.
To improve the accuracy of my code we add point a=0
to the grid for differentiation matrix and finally have
Needs["DifferentialEquations`NDSolveProblems`"];
Needs["DifferentialEquations`NDSolveUtilities`"]; \
Get["NumericalDifferentialEquationAnalysis`"];
np = 40; g = GaussianQuadratureWeights[np, 0, 1];
ugrid = g[[All, 1]]; weights = g[[All, 2]]; grid = Join[{0}, ugrid];
fd = NDSolve`FiniteDifferenceDerivative[Derivative[1], grid]; m =
fd["DifferentiationMatrix"]; vart =
Table[u[i][t], {i, Length[grid]}]; varti =
Table[u[i]'[t], {i, 2, Length[grid]}]; vart1 =
Table[u[i]'[t], {i, Length[grid]}]; ux = m.vart; var =
Table[u[i], {i, Length[grid]}];
\[Mu][a_] := 1/(1 - a);
p0[x_] := If[x <= 1/2, (1 - 2 x)^3 (1 - x), 31 (2 x - 1)^3 (1 - x)];
u0[a_] := p0[a];
f[a_] := 2; int = Table[f[ugrid[[i]]] weights[[i]], {i, np}]; eqns =
Join[{D[u[1][t], t] == int.varti},
Flatten[Table[
u[i]'[t] == -ux[[i]] - \[Mu][grid[[i]]] u[i][t], {i, 2,
Length[grid]}]]];
ics = Table[u[i][0] == u0[grid[[i]]], {i, Length[grid]}];
tmax = 1;
{bb, mm} = CoefficientArrays[eqns, vart1];
rhs = -Inverse[mm].bb;
sol1 = NDSolve[{Table[vart1[[i]] == rhs[[i]], {i, Length[vart1]}],
ics}, var, {t, 0, tmax}];
With this code we calculate absolute error in Example 1 for np=40
(left picture) and np=80
(right picture). For this code error is of $h^{5/2}$.
NDSolveValue::ibcinc: Warning: boundary and initial conditions are inconsistent.
$\endgroup$