BELFEM 0.9.3
Berkeley Lab Finite Element Framework
Loading...
Searching...
No Matches
fn_cardano.hpp
Go to the documentation of this file.
1/*
2 * BELFEM -- The Berkeley Lab Finite Element Framework
3 * Copyright (c) 2026, The Regents of the University of California,
4 * through Lawrence Berkeley National Laboratory (subject to receipt of any required
5 * approvals from the U.S. Dept. of Energy). All rights reserved.
6 *
7 * Developers: Christian Messe, Gregory Giard
8 *
9 * See the top-level LICENSE file for the complete license and disclaimer.
10 */
11
12#ifndef BELFEM_FN_CARDANO_HPP
13#define BELFEM_FN_CARDANO_HPP
14
15#include "typedefs.hpp"
16#include "assert.hpp"
17#include "cl_Vector.hpp"
18#include "fn_sort.hpp"
19#include "fn_sign.hpp"
20
21namespace belfem
22{
23//------------------------------------------------------------------------------
48
49 // forward declaration of the scalar interface, so that the vector
50 // wrapper below finds it by ordinary lookup, not just ADL
51 template < typename T >
52 void
53 cardano( const T a, const T b, const T c, const T d, Vector< T > & X );
54
55 template < typename T >
56 void
58 {
59 BELFEM_ASSERT( A.length() == 4, "coefficient vector needs to have a length of 4");
60 cardano( A( 0 ), A( 1 ), A( 2 ), A( 3 ), X );
61 }
62
63 template < typename T >
64 void
65 cardano( const T a, const T b, const T c, const T d, Vector< T > & X )
66 {
67 if ( std::abs( a ) < BELFEM_EPSILON )
68 {
69 if( std::abs( b ) < BELFEM_EPSILON )
70 {
71 if( std::abs( c ) > BELFEM_EPSILON )
72 {
73 X.set_size( 1 );
74
75 X = -d / c;
76 }
77 else
78 {
79 X.set_size( 0 );
80 }
81 }
82 else
83 {
84 T D = std::pow( c, 2 ) - 4.0 * b * d;
85
86 if( D > 0.0 )
87 {
88 // two distinct T roots
89 D = std::sqrt( D );
90 X.set_size( 2 );
91 X( 0 ) = ( -c - D ) / ( 2.0 * b );
92 X( 1 ) = ( -c + D ) / ( 2.0 * b );
93 sort( X );
94 }
95 else if( D == 0.0 )
96 {
97 // one distinct root (double root)
98 X.set_size( 1 );
99 X( 0 ) = -c / ( 2.0 * b );
100 }
101 else
102 {
103 X.set_size( 0 );
104 }
105 }
106 }
107 else
108 {
109
110 T p = ( 9.0*a*c-3.0*std::pow( b,2) )/(9.0*std::pow( a, 2 ));
111 T q = ( 2.0* std::pow( b, 3) - 9.0*a*b*c + 27.0*std::pow(a,2)*d)/(27.0*std::pow( a,3 ));
112 T r = b/(3*a);
113 T D = 0.25 * std::pow( q, 2 ) + std::pow( p, 3)/27.0;
114
115 if ( D > 0.0 )
116 {
117 X.set_size( 1 );
118 T u = -0.5*q + std::sqrt( D );
119 T v = -0.5*q - std::sqrt( D );
120 u = sign( u )*std::pow( std::abs( u ), 1.0/3.0 );
121 v = sign( v )*std::pow( std::abs( v ), 1.0/3.0 );
122
123 // note: u and v are real scalars, so x is real by
124 // construction and the first branch below is always taken;
125 // The complex fallback is unreachable. It remains as defensive
126 // code until a cleanup pass.
127 std::complex< T > x = u + v;
128
129 if( std::abs( std::imag( x ) ) < BELFEM_EPSILON )
130 {
131 X( 0 ) = std::real( x ) - r;
132 }
133 else
134 {
135 const std::complex< T > f1( -0.5, 0.5 * std::sqrt( 3.0 ));
136 const std::complex< T > f2( -0.5, -0.5 * std::sqrt( 3.0 ));
137
138 x = f1 * u + f2 * v;
139 if( std::abs( std::imag( x ) ) < BELFEM_EPSILON )
140 {
141 X( 0 ) = std::real( x ) - r;
142 }
143 else
144 {
145 x = f2*u + f1 * v;
146 if( std::abs( std::imag( x ) ) < BELFEM_EPSILON )
147 {
148 X( 0 ) = std::real( x ) - r;
149 }
150 else
151 {
152 BELFEM_ERROR( false, "Something went wrong while trying to solve cubic equation" );
153 }
154 }
155 }
156 }
157 else if( D < 0 )
158 {
159 std::complex< T > u = std::sqrt( -4.0/3.0*p );
160 std::complex< T > v = std::acos( -0.5*q*std::sqrt( -27.0/std::pow( p, 3 ) ) )/3.0;
161 const std::complex< T > w = 2.0*std::acos( 0.0 )/3.0;
162
163 X.set_size( 3 );
164 X( 0 ) = std::real( u * std::cos( v ) ) -r;
165 X( 1 ) = std::real( -u * std::cos( v + w ) ) -r;
166 X( 2 ) = std::real( -u * std::cos( v - w ) ) -r;
167 sort( X );
168 }
169 else
170 {
171 // D == 0: repeated roots
172 if( std::abs( p ) < BELFEM_EPSILON )
173 {
174 // p = q = 0 → triple root
175 X.set_size( 1 );
176 X( 0 ) = -r;
177 }
178 else
179 {
180 // double root + simple root — return distinct roots only
181 X.set_size( 2 );
182 X( 0 ) = -3.0 * q / ( 2.0 * p ) - r; // double root
183 X( 1 ) = 3.0 * q / p - r; // simple root
184 sort( X );
185 }
186 }
187 }
188
189 }
190//------------------------------------------------------------------------------
191}
192#endif //BELFEM_FN_CARDANO_HPP
#define BELFEM_ERROR(aCheck,...)
Definition assert.hpp:264
#define BELFEM_ASSERT(aCheck,...)
Definition assert.hpp:244
Column vector.
Definition cl_BZ_Vector.hpp:41
void set_size(const size_t aNumRows)
change the size of the vector
Definition cl_AR_Vector.hpp:215
size_t length() const
get the length of the vector
Definition cl_AR_Vector.hpp:235
Sorts a vector in place, ascending.
USER GUIDES:
Definition cl_Capacitor.cpp:16
@ T
Definition cl_Material.hpp:125
T sign(const T aX)
Definition fn_sign.hpp:18
void cardano(const T a, const T b, const T c, const T d, Vector< T > &X)
solve a cubic equation a*x^3 + b*x^2 + c*x + d = 0 for its real roots (Cardano / trigonometric method...
Definition fn_cardano.hpp:65
constexpr real BELFEM_EPSILON
Definition typedefs.hpp:89
void sort(Cell< T > &aCell)
Definition cl_Cell.hpp:431
@ q
Definition cl_Material.hpp:176