BELFEM 0.9.0
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//------------------------------------------------------------------------------
49
50 // forward declaration of the scalar interface, so that the vector
51 // wrapper below finds it by ordinary lookup, not just ADL
52 template < typename T >
53 void
54 cardano( const T a, const T b, const T c, const T d, Vector< T > & X );
55
56 template < typename T >
57 void
59 {
60 BELFEM_ASSERT( A.length() == 4, "coefficient vector needs to have a length of 4");
61 cardano( A( 0 ), A( 1 ), A( 2 ), A( 3 ), X );
62 }
63
64 template < typename T >
65 void
66 cardano( const T a, const T b, const T c, const T d, Vector< T > & X )
67 {
68 if ( std::abs( a ) < BELFEM_EPSILON )
69 {
70 if( std::abs( b ) < BELFEM_EPSILON )
71 {
72 if( std::abs( c ) > BELFEM_EPSILON )
73 {
74 X.set_size( 1 );
75
76 X = -d / c;
77 }
78 else
79 {
80 X.set_size( 0 );
81 }
82 }
83 else
84 {
85 T D = std::pow( c, 2 ) - 4.0 * b * d;
86
87 if( D > 0.0 )
88 {
89 // two distinct T roots
90 D = std::sqrt( D );
91 X.set_size( 2 );
92 X( 0 ) = ( -c - D ) / ( 2.0 * b );
93 X( 1 ) = ( -c + D ) / ( 2.0 * b );
94 sort( X );
95 }
96 else if( D == 0.0 )
97 {
98 // one distinct root (double root)
99 X.set_size( 1 );
100 X( 0 ) = -c / ( 2.0 * b );
101 }
102 else
103 {
104 X.set_size( 0 );
105 }
106 }
107 }
108 else
109 {
110
111 T p = ( 9.0*a*c-3.0*std::pow( b,2) )/(9.0*std::pow( a, 2 ));
112 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 ));
113 T r = b/(3*a);
114 T D = 0.25 * std::pow( q, 2 ) + std::pow( p, 3)/27.0;
115
116 if ( D > 0.0 )
117 {
118 X.set_size( 1 );
119 T u = -0.5*q + std::sqrt( D );
120 T v = -0.5*q - std::sqrt( D );
121 u = sign( u )*std::pow( std::abs( u ), 1.0/3.0 );
122 v = sign( v )*std::pow( std::abs( v ), 1.0/3.0 );
123
124 // note: u and v are real scalars, so x is real by
125 // construction and the first branch below is always taken;
126 // the complex fallbacks are unreachable defensive code
127 // (audit 2026-07-23), kept until a cleanup pass
128 std::complex< T > x = u + v;
129
130 if( std::abs( std::imag( x ) ) < BELFEM_EPSILON )
131 {
132 X( 0 ) = std::real( x ) - r;
133 }
134 else
135 {
136 const std::complex< T > f1( -0.5, 0.5 * std::sqrt( 3.0 ));
137 const std::complex< T > f2( -0.5, -0.5 * std::sqrt( 3.0 ));
138
139 x = f1 * u + f2 * v;
140 if( std::abs( std::imag( x ) ) < BELFEM_EPSILON )
141 {
142 X( 0 ) = std::real( x ) - r;
143 }
144 else
145 {
146 x = f2*u + f1 * v;
147 if( std::abs( std::imag( x ) ) < BELFEM_EPSILON )
148 {
149 X( 0 ) = std::real( x ) - r;
150 }
151 else
152 {
153 BELFEM_ERROR( false, "Something went wrong while trying to solve cubic equation" );
154 }
155 }
156 }
157 }
158 else if( D < 0 )
159 {
160 std::complex< T > u = std::sqrt( -4.0/3.0*p );
161 std::complex< T > v = std::acos( -0.5*q*std::sqrt( -27.0/std::pow( p, 3 ) ) )/3.0;
162 const std::complex< T > w = 2.0*std::acos( 0.0 )/3.0;
163
164 X.set_size( 3 );
165 X( 0 ) = std::real( u * std::cos( v ) ) -r;
166 X( 1 ) = std::real( -u * std::cos( v + w ) ) -r;
167 X( 2 ) = std::real( -u * std::cos( v - w ) ) -r;
168 sort( X );
169 }
170 else
171 {
172 // D == 0: repeated roots
173 if( std::abs( p ) < BELFEM_EPSILON )
174 {
175 // p = q = 0 → triple root
176 X.set_size( 1 );
177 X( 0 ) = -r;
178 }
179 else
180 {
181 // double root + simple root — return distinct roots only
182 X.set_size( 2 );
183 X( 0 ) = -3.0 * q / ( 2.0 * p ) - r; // double root
184 X( 1 ) = 3.0 * q / p - r; // simple root
185 sort( X );
186 }
187 }
188 }
189
190 }
191//------------------------------------------------------------------------------
192}
193#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:237
size_t length() const
get the length of the vector
Definition cl_AR_Vector.hpp:257
Sorts a vector in place, ascending.
USER GUIDES:
Definition cl_Capacitor.cpp:16
@ T
Definition cl_Material.hpp:122
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:66
constexpr real BELFEM_EPSILON
Definition typedefs.hpp:90
void sort(Cell< T > &aCell)
Definition cl_Cell.hpp:455
@ q
Definition cl_Material.hpp:176