cubicEqn.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2017 OpenFOAM Foundation
9 -------------------------------------------------------------------------------
10 License
11  This file is part of OpenFOAM.
12 
13  OpenFOAM is free software: you can redistribute it and/or modify it
14  under the terms of the GNU General Public License as published by
15  the Free Software Foundation, either version 3 of the License, or
16  (at your option) any later version.
17 
18  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
19  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21  for more details.
22 
23  You should have received a copy of the GNU General Public License
24  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
25 
26 \*---------------------------------------------------------------------------*/
27 
28 #include "linearEqn.H"
29 #include "quadraticEqn.H"
30 #include "cubicEqn.H"
31 
32 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
33 
35 {
36  /*
37 
38  This function solves a cubic equation of the following form:
39 
40  a*x^3 + b*x^2 + c*x + d = 0
41  x^3 + B*x^2 + C*x + D = 0
42 
43  The following two substitutions are used:
44 
45  x = t - B/3
46  t = w - P/3/w
47 
48  This reduces the problem to a quadratic in w^3.
49 
50  w^6 + Q*w^3 - P = 0
51 
52  Where Q and P are given in the code below.
53 
54  The properties of the cubic can be related to the properties of this
55  quadratic in w^3. If it has a repeated root a zero, the cubic has a tripl
56  root. If it has a repeated root not at zero, the cubic has two real roots,
57  one repeated and one not. If it has two complex roots, the cubic has three
58  real roots. If it has two real roots, then the cubic has one real root and
59  two complex roots.
60 
61  This is solved for the most numerically accurate value of w^3. See the
62  quadratic function for details on how to pick a value. This single value of
63  w^3 can yield up to three cube roots for w, which relate to the three
64  solutions for x.
65 
66  Only a single root, or pair of conjugate roots, is directly evaluated; the
67  one, or ones with the lowest relative numerical error. Root identities are
68  then used to recover the remaining roots, possibly utilising a quadratic
69  and/or linear solution. This seems to be a good way of maintaining the
70  accuracy of roots at very different magnitudes.
71 
72  */
73 
74  const scalar a = this->a();
75  const scalar b = this->b();
76  const scalar c = this->c();
77  const scalar d = this->d();
78 
79  if (a == 0)
80  {
81  return Roots<3>(quadraticEqn(b, c, d).roots(), roots::nan, 0);
82  }
83 
84  // This is assumed not to over- or under-flow. If it does, all bets are off.
85  const scalar p = c*a - b*b/3;
86  const scalar q = b*b*b*scalar(2)/27 - b*c*a/3 + d*a*a;
87  const scalar disc = p*p*p/27 + q*q/4;
88 
89  // How many roots of what types are available?
90  const bool oneReal = disc == 0 && p == 0;
91  const bool twoReal = disc == 0 && p != 0;
92  const bool threeReal = disc < 0;
93  //const bool oneRealTwoComplex = disc > 0;
94 
95  static const scalar sqrt3 = sqrt(3.0);
96 
97  scalar x;
98 
99  if (oneReal)
100  {
101  const Roots<1> r = linearEqn(a, b/3).roots();
102  return Roots<3>(r.type(0), r[0]);
103  }
104  else if (twoReal)
105  {
106  if (q*b > 0)
107  {
108  x = - 2*cbrt(q/2) - b/3;
109  }
110  else
111  {
112  x = cbrt(q/2) - b/3;
113  const Roots<1> r = linearEqn(- a, x).roots();
114  return Roots<3>(Roots<2>(r, r), linearEqn(x*x, a*d).roots());
115  }
116  }
117  else if (threeReal)
118  {
119  const scalar wCbRe = - q/2, wCbIm = sqrt(- disc);
120  const scalar wAbs = cbrt(hypot(wCbRe, wCbIm));
121  const scalar wArg = atan2(wCbIm, wCbRe)/3;
122  const scalar wRe = wAbs*cos(wArg), wIm = wAbs*sin(wArg);
123  if (b > 0)
124  {
125  x = - wRe - mag(wIm)*sqrt3 - b/3;
126  }
127  else
128  {
129  x = 2*wRe - b/3;
130  }
131  }
132  else // if (oneRealTwoComplex)
133  {
134  const scalar wCb = - q/2 - sign(q)*sqrt(disc);
135  const scalar w = cbrt(wCb);
136  const scalar t = w - p/(3*w);
137  if (p + t*b < 0)
138  {
139  x = t - b/3;
140  }
141  else
142  {
143  const scalar xRe = - t/2 - b/3, xIm = sqrt3/2*(w + p/3/w);
144  x = - a*a*d/(xRe*xRe + xIm*xIm);
145 
146  // This form of solving for the remaining roots seems more stable
147  // for this case. This has been determined by trial and error.
148  return
149  Roots<3>
150  (
151  linearEqn(- a, x).roots(),
152  quadraticEqn(a*x, x*x + b*x, - a*d).roots()
153  );
154  }
155  }
156 
157  return
158  Roots<3>
159  (
160  linearEqn(- a, x).roots(),
161  quadraticEqn(- x*x, c*x + a*d, d*x).roots()
162  );
163 }
164 
165 
166 // ************************************************************************* //
Foam::Roots::type
void type(const direction i, const roots::type t)
Set the type of the i-th root.
Definition: RootsI.H:122
p
volScalarField & p
Definition: createFieldRefs.H:8
cubicEqn.H
Foam::roots::nan
Definition: Roots.H:60
Foam::cubicEqn::b
scalar b() const
Definition: cubicEqnI.H:63
Foam::sin
dimensionedScalar sin(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:264
Foam::atan2
dimensionedScalar atan2(const dimensionedScalar &x, const dimensionedScalar &y)
Definition: dimensionedScalar.C:312
Foam::cubicEqn::roots
Roots< 3 > roots() const
Get the roots.
Definition: cubicEqn.C:34
Foam::sign
dimensionedScalar sign(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:166
Foam::cubicEqn::a
scalar a() const
Definition: cubicEqnI.H:57
Foam::linearEqn
Linear equation of the form a*x + b = 0.
Definition: linearEqn.H:50
Foam::hypot
dimensionedScalar hypot(const dimensionedScalar &x, const dimensionedScalar &y)
Definition: dimensionedScalar.C:327
Foam::linearEqn::roots
Roots< 1 > roots() const
Get the roots.
Definition: linearEqnI.H:91
linearEqn.H
Foam::cubicEqn::d
scalar d() const
Definition: cubicEqnI.H:75
Foam::cubicEqn::c
scalar c() const
Definition: cubicEqnI.H:69
Foam::sqrt
dimensionedScalar sqrt(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:144
Foam::mag
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
x
x
Definition: LISASMDCalcMethod2.H:52
Foam::Roots
Templated storage for the roots of polynomial equations, plus flags to indicate the nature of the roo...
Definition: Roots.H:70
quadraticEqn.H
Foam::cbrt
dimensionedScalar cbrt(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:155
Foam::quadraticEqn
Quadratic equation of the form a*x^2 + b*x + c = 0.
Definition: quadraticEqn.H:51
Foam::cos
dimensionedScalar cos(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:265