Files
mfem/linalg/ode.cpp
T
Tzanio ab468628a2 Maxwell miniapp developed internally at LLNL.
Based on commits by Mark Stowell:

- Adding time integrators for Hamiltonian systems. This family of integrators is
  taken from the paper "A Symplectic Integration Algorithm for Separable
  Hamiltonian Functions" by J. CANDY AND W. ROZMUS. They do a marvelous job of
  conserving energy in coupled first order PDEs.

- If others agree we should probably move these classes into the linalg/ode.?pp
  files (or other files nearby). First I would like to change the names of these
  integrators so if anyone has a suggestion...

- This is a first draft of a time domain electromagnetics code. It is not yet
  ready for trials but it will soon support two different current sources.

- There are many ways this could advance but we'll try to follow the example set
  in the statics codes and keep it simple.

- Renamed the symplectic integration solvers to follow a convention implied in
  the paper describing them. It also fits fairly well with the other ODE solvers
  in MFEM.

- Improved the way source terms are handled. Also fixed a problem with the field
  update.

- Moving the Symplectic integrator methods to the ode.?pp files.

- Changing the ess_tdof array argument to 'const' so that FormLinearSystem can
  be used inside an ODE integrator.

- Modified VectorFECurlIntegrator so that it can compute either the Curl of an
  H(curl) vector or the weak Curl of an H(div) vector.

- Adding a simple test of the symplectic integrator which integrates a scalar
  valued ODE in time. The test code writes a datafile and a gnuplot input file
  which can plot the datafile.

- Added support for Dirichlet boundary conditions. The code is still in a very
  immature state but various features have been successfully tested. It now
  remains to simplify and clean up the implementation and prepare a series of
  example problems.

- Making some changes to conform to the new makefile design (which is a big
  improvement by the way)

- First draft of a Halbach array example.

- Small post-merge changes
2017-05-02 16:24:57 -07:00

587 lines
13 KiB
C++

// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.
#include "operator.hpp"
#include "ode.hpp"
namespace mfem
{
void ForwardEulerSolver::Init(TimeDependentOperator &_f)
{
ODESolver::Init(_f);
dxdt.SetSize(f->Width());
}
void ForwardEulerSolver::Step(Vector &x, double &t, double &dt)
{
f->SetTime(t);
f->Mult(x, dxdt);
x.Add(dt, dxdt);
t += dt;
}
void RK2Solver::Init(TimeDependentOperator &_f)
{
ODESolver::Init(_f);
int n = f->Width();
dxdt.SetSize(n);
x1.SetSize(n);
}
void RK2Solver::Step(Vector &x, double &t, double &dt)
{
// 0 |
// a | a
// ---+--------
// | 1-b b b = 1/(2a)
const double b = 0.5/a;
f->SetTime(t);
f->Mult(x, dxdt);
add(x, (1. - b)*dt, dxdt, x1);
x.Add(a*dt, dxdt);
f->SetTime(t + a*dt);
f->Mult(x, dxdt);
add(x1, b*dt, dxdt, x);
t += dt;
}
void RK3SSPSolver::Init(TimeDependentOperator &_f)
{
ODESolver::Init(_f);
int n = f->Width();
y.SetSize(n);
k.SetSize(n);
}
void RK3SSPSolver::Step(Vector &x, double &t, double &dt)
{
// x0 = x, t0 = t, k0 = dt*f(t0, x0)
f->SetTime(t);
f->Mult(x, k);
// x1 = x + k0, t1 = t + dt, k1 = dt*f(t1, x1)
add(x, dt, k, y);
f->SetTime(t + dt);
f->Mult(y, k);
// x2 = 3/4*x + 1/4*(x1 + k1), t2 = t + 1/2*dt, k2 = dt*f(t2, x2)
y.Add(dt, k);
add(3./4, x, 1./4, y, y);
f->SetTime(t + dt/2);
f->Mult(y, k);
// x3 = 1/3*x + 2/3*(x2 + k2), t3 = t + dt
y.Add(dt, k);
add(1./3, x, 2./3, y, x);
t += dt;
}
void RK4Solver::Init(TimeDependentOperator &_f)
{
ODESolver::Init(_f);
int n = f->Width();
y.SetSize(n);
k.SetSize(n);
z.SetSize(n);
}
void RK4Solver::Step(Vector &x, double &t, double &dt)
{
// 0 |
// 1/2 | 1/2
// 1/2 | 0 1/2
// 1 | 0 0 1
// -----+-------------------
// | 1/6 1/3 1/3 1/6
f->SetTime(t);
f->Mult(x, k); // k1
add(x, dt/2, k, y);
add(x, dt/6, k, z);
f->SetTime(t + dt/2);
f->Mult(y, k); // k2
add(x, dt/2, k, y);
z.Add(dt/3, k);
f->Mult(y, k); // k3
add(x, dt, k, y);
z.Add(dt/3, k);
f->SetTime(t + dt);
f->Mult(y, k); // k4
add(z, dt/6, k, x);
t += dt;
}
ExplicitRKSolver::ExplicitRKSolver(int _s, const double *_a, const double *_b,
const double *_c)
{
s = _s;
a = _a;
b = _b;
c = _c;
k = new Vector[s];
}
void ExplicitRKSolver::Init(TimeDependentOperator &_f)
{
ODESolver::Init(_f);
int n = f->Width();
y.SetSize(n);
for (int i = 0; i < s; i++)
{
k[i].SetSize(n);
}
}
void ExplicitRKSolver::Step(Vector &x, double &t, double &dt)
{
// 0 |
// c[0] | a[0]
// c[1] | a[1] a[2]
// ... | ...
// c[s-2] | ... a[s(s-1)/2-1]
// --------+---------------------
// | b[0] b[1] ... b[s-1]
f->SetTime(t);
f->Mult(x, k[0]);
for (int l = 0, i = 1; i < s; i++)
{
add(x, a[l++]*dt, k[0], y);
for (int j = 1; j < i; j++)
{
y.Add(a[l++]*dt, k[j]);
}
f->SetTime(t + c[i-1]*dt);
f->Mult(y, k[i]);
}
for (int i = 0; i < s; i++)
{
x.Add(b[i]*dt, k[i]);
}
t += dt;
}
ExplicitRKSolver::~ExplicitRKSolver()
{
delete [] k;
}
const double RK6Solver::a[] =
{
.6e-1,
.1923996296296296296296296296296296296296e-1,
.7669337037037037037037037037037037037037e-1,
.35975e-1,
0.,
.107925,
1.318683415233148260919747276431735612861,
0.,
-5.042058063628562225427761634715637693344,
4.220674648395413964508014358283902080483,
-41.87259166432751461803757780644346812905,
0.,
159.4325621631374917700365669070346830453,
-122.1192135650100309202516203389242140663,
5.531743066200053768252631238332999150076,
-54.43015693531650433250642051294142461271,
0.,
207.0672513650184644273657173866509835987,
-158.6108137845899991828742424365058599469,
6.991816585950242321992597280791793907096,
-.1859723106220323397765171799549294623692e-1,
-54.66374178728197680241215648050386959351,
0.,
207.9528062553893734515824816699834244238,
-159.2889574744995071508959805871426654216,
7.018743740796944434698170760964252490817,
-.1833878590504572306472782005141738268361e-1,
-.5119484997882099077875432497245168395840e-3
};
const double RK6Solver::b[] =
{
.3438957868357036009278820124728322386520e-1,
0.,
0.,
.2582624555633503404659558098586120858767,
.4209371189673537150642551514069801967032,
4.405396469669310170148836816197095664891,
-176.4831190242986576151740942499002125029,
172.3641334014150730294022582711902413315
};
const double RK6Solver::c[] =
{
.6e-1,
.9593333333333333333333333333333333333333e-1,
.1439,
.4973,
.9725,
.9995,
1.,
};
const double RK8Solver::a[] =
{
.5e-1,
-.69931640625e-2,
.1135556640625,
.399609375e-1,
0.,
.1198828125,
.3613975628004575124052940721184028345129,
0.,
-1.341524066700492771819987788202715834917,
1.370126503900035259414693716084313000404,
.490472027972027972027972027972027972028e-1,
0.,
0.,
.2350972042214404739862988335493427143122,
.180855592981356728810903963653454488485,
.6169289044289044289044289044289044289044e-1,
0.,
0.,
.1123656831464027662262557035130015442303,
-.3885046071451366767049048108111244567456e-1,
.1979188712522045855379188712522045855379e-1,
-1.767630240222326875735597119572145586714,
0.,
0.,
-62.5,
-6.061889377376669100821361459659331999758,
5.650823198222763138561298030600840174201,
65.62169641937623283799566054863063741227,
-1.180945066554970799825116282628297957882,
0.,
0.,
-41.50473441114320841606641502701994225874,
-4.434438319103725011225169229846100211776,
4.260408188586133024812193710744693240761,
43.75364022446171584987676829438379303004,
.787142548991231068744647504422630755086e-2,
-1.281405999441488405459510291182054246266,
0.,
0.,
-45.04713996013986630220754257136007322267,
-4.731362069449576477311464265491282810943,
4.514967016593807841185851584597240996214,
47.44909557172985134869022392235929015114,
.1059228297111661135687393955516542875228e-1,
-.5746842263844616254432318478286296232021e-2,
-1.724470134262485191756709817484481861731,
0.,
0.,
-60.92349008483054016518434619253765246063,
-5.95151837622239245520283276706185486829,
5.556523730698456235979791650843592496839,
63.98301198033305336837536378635995939281,
.1464202825041496159275921391759452676003e-1,
.6460408772358203603621865144977650714892e-1,
-.7930323169008878984024452548693373291447e-1,
-3.301622667747079016353994789790983625569,
0.,
0.,
-118.011272359752508566692330395789886851,
-10.14142238845611248642783916034510897595,
9.139311332232057923544012273556827000619,
123.3759428284042683684847180986501894364,
4.623244378874580474839807625067630924792,
-3.383277738068201923652550971536811240814,
4.527592100324618189451265339351129035325,
-5.828495485811622963193088019162985703755
};
const double RK8Solver::b[] =
{
.4427989419007951074716746668098518862111e-1,
0.,
0.,
0.,
0.,
.3541049391724448744815552028733568354121,
.2479692154956437828667629415370663023884,
-15.69420203883808405099207034271191213468,
25.08406496555856261343930031237186278518,
-31.73836778626027646833156112007297739997,
22.93828327398878395231483560344797018313,
-.2361324633071542145259900641263517600737
};
const double RK8Solver::c[] =
{
.5e-1,
.1065625,
.15984375,
.39,
.465,
.155,
.943,
.901802041735856958259707940678372149956,
.909,
.94,
1.,
};
void BackwardEulerSolver::Init(TimeDependentOperator &_f)
{
ODESolver::Init(_f);
k.SetSize(f->Width());
}
void BackwardEulerSolver::Step(Vector &x, double &t, double &dt)
{
f->SetTime(t + dt);
f->ImplicitSolve(dt, x, k); // solve for k: k = f(x + dt*k, t + dt)
x.Add(dt, k);
t += dt;
}
void ImplicitMidpointSolver::Init(TimeDependentOperator &_f)
{
ODESolver::Init(_f);
k.SetSize(f->Width());
}
void ImplicitMidpointSolver::Step(Vector &x, double &t, double &dt)
{
f->SetTime(t + dt/2);
f->ImplicitSolve(dt/2, x, k);
x.Add(dt, k);
t += dt;
}
SDIRK23Solver::SDIRK23Solver(int gamma_opt)
{
if (gamma_opt == 0)
{
gamma = (3. - sqrt(3.))/6.; // not A-stable, order 3
}
else if (gamma_opt == 2)
{
gamma = (2. - sqrt(2.))/2.; // L-stable, order 2
}
else if (gamma_opt == 3)
{
gamma = (2. + sqrt(2.))/2.; // L-stable, order 2
}
else
{
gamma = (3. + sqrt(3.))/6.; // A-stable, order 3
}
}
void SDIRK23Solver::Init(TimeDependentOperator &_f)
{
ODESolver::Init(_f);
k.SetSize(f->Width());
y.SetSize(f->Width());
}
void SDIRK23Solver::Step(Vector &x, double &t, double &dt)
{
// with a = gamma:
// a | a
// 1-a | 1-2a a
// ------+-----------
// | 1/2 1/2
// note: with gamma_opt=3, both solve are outside [t,t+dt] since a>1
f->SetTime(t + gamma*dt);
f->ImplicitSolve(gamma*dt, x, k);
add(x, (1.-2.*gamma)*dt, k, y); // y = x + (1-2*gamma)*dt*k
x.Add(dt/2, k);
f->SetTime(t + (1.-gamma)*dt);
f->ImplicitSolve(gamma*dt, y, k);
x.Add(dt/2, k);
t += dt;
}
void SDIRK34Solver::Init(TimeDependentOperator &_f)
{
ODESolver::Init(_f);
k.SetSize(f->Width());
y.SetSize(f->Width());
z.SetSize(f->Width());
}
void SDIRK34Solver::Step(Vector &x, double &t, double &dt)
{
// a | a
// 1/2 | 1/2-a a
// 1-a | 2a 1-4a a
// ------+--------------------
// | b 1-2b b
// note: two solves are outside [t,t+dt] since c1=a>1, c3=1-a<0
const double a = 1./sqrt(3.)*cos(M_PI/18.) + 0.5;
const double b = 1./(6.*(2.*a-1.)*(2.*a-1.));
f->SetTime(t + a*dt);
f->ImplicitSolve(a*dt, x, k);
add(x, (0.5-a)*dt, k, y);
add(x, (2.*a)*dt, k, z);
x.Add(b*dt, k);
f->SetTime(t + dt/2);
f->ImplicitSolve(a*dt, y, k);
z.Add((1.-4.*a)*dt, k);
x.Add((1.-2.*b)*dt, k);
f->SetTime(t + (1.-a)*dt);
f->ImplicitSolve(a*dt, z, k);
x.Add(b*dt, k);
t += dt;
}
void SDIRK33Solver::Init(TimeDependentOperator &_f)
{
ODESolver::Init(_f);
k.SetSize(f->Width());
y.SetSize(f->Width());
}
void SDIRK33Solver::Step(Vector &x, double &t, double &dt)
{
// a | a
// c | c-a a
// 1 | b 1-a-b a
// -----+----------------
// | b 1-a-b a
const double a = 0.435866521508458999416019;
const double b = 1.20849664917601007033648;
const double c = 0.717933260754229499708010;
f->SetTime(t + a*dt);
f->ImplicitSolve(a*dt, x, k);
add(x, (c-a)*dt, k, y);
x.Add(b*dt, k);
f->SetTime(t + c*dt);
f->ImplicitSolve(a*dt, y, k);
x.Add((1.-a-b)*dt, k);
f->SetTime(t + dt);
f->ImplicitSolve(a*dt, x, k);
x.Add(a*dt, k);
t += dt;
}
void
SIASolver::Init(Operator &P, TimeDependentOperator & F)
{
P_ = &P; F_ = &F;
dp_.SetSize(F_->Height());
dq_.SetSize(P_->Height());
}
void
SIA1Solver::Step(Vector &q, Vector &p, double &t, double &dt)
{
F_->SetTime(t);
F_->Mult(q,dp_);
p.Add(dt,dp_);
P_->Mult(p,dq_);
q.Add(dt,dq_);
t += dt;
}
void
SIA2Solver::Step(Vector &q, Vector &p, double &t, double &dt)
{
P_->Mult(p,dq_);
q.Add(0.5*dt,dq_);
F_->SetTime(t+0.5*dt);
F_->Mult(q,dp_);
p.Add(dt,dp_);
P_->Mult(p,dq_);
q.Add(0.5*dt,dq_);
t += dt;
}
SIAVSolver::SIAVSolver(int order)
: order_(order)
{
a_.SetSize(order);
b_.SetSize(order);
switch (order_)
{
case 1:
a_[0] = 1.0;
b_[0] = 1.0;
break;
case 2:
a_[0] = 0.5;
a_[1] = 0.5;
b_[0] = 0.0;
b_[1] = 1.0;
break;
case 3:
a_[0] = 2.0/3.0;
a_[1] = -2.0/3.0;
a_[2] = 1.0;
b_[0] = 7.0/24.0;
b_[1] = 0.75;
b_[2] = -1.0/24.0;
break;
case 4:
a_[0] = (2.0+pow(2.0,1.0/3.0)+pow(2.0,-1.0/3.0))/6.0;
a_[1] = (1.0-pow(2.0,1.0/3.0)-pow(2.0,-1.0/3.0))/6.0;
a_[2] = a_[1];
a_[3] = a_[0];
b_[0] = 0.0;
b_[1] = 1.0/(2.0-pow(2.0,1.0/3.0));
b_[2] = 1.0/(1.0-pow(2.0,2.0/3.0));
b_[3] = b_[1];
break;
default:
MFEM_ASSERT(false, "Unsupported order in SInSolver");
};
}
void
SIAVSolver::Step(Vector &q, Vector &p, double &t, double &dt)
{
for (int i=0; i<order_; i++)
{
if ( b_[i] != 0.0 )
{
F_->SetTime(t);
F_->Mult(q,dp_);
p.Add(b_[i]*dt,dp_);
}
P_->Mult(p,dq_);
q.Add(a_[i]*dt,dq_);
t += a_[i]*dt;
}
}
}