From ade2d4cb06c4f6e3e338071a581ead0a773f744e Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Fri, 10 Dec 2021 15:51:44 +0000 Subject: [PATCH 01/32] Andy's original torus implementation --- include/openmc/surface.h | 54 ++++ src/surface.cpp | 552 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 606 insertions(+) diff --git a/include/openmc/surface.h b/include/openmc/surface.h index 325c313aa4..f4d093c8bb 100644 --- a/include/openmc/surface.h +++ b/include/openmc/surface.h @@ -371,6 +371,60 @@ public: double A_, B_, C_, D_, E_, F_, G_, H_, J_, K_; }; +//============================================================================== +//! A toroidal surface described by the quartic torus lies in the x direction +// +//! \f$(x-x_0)^2 + (\sqrt{(y-y_0)^2 + (z-z_0)^2}-A)^2/C^2 -1 \f$ +//============================================================================== + +class SurfaceXTorus : public CSGSurface +{ + // (x-x0)^2/B^2 + (((y-y0)^2 + (z-z0)^2)^-1/2 -A))^2/C^2 -1 = 0 + double x0_, y0_, z0_, A_, B_, C_; +public: + explicit SurfaceXTorus(pugi::xml_node surf_node); + double evaluate(Position r) const; + double distance(Position r, Direction u, bool coincident) const; + Direction normal(Position r) const; + void to_hdf5_inner(hid_t group_id) const; +}; + +//============================================================================== +//! A toroidal surface described by the quartic torus lies in the y direction +// +//! \f$(x-x_0)^2 + (\sqrt{(y-y_0)^2 + (z-z_0)^2}-A)^2/C^2 -1 \f$ +//============================================================================== + +class SurfaceYTorus : public CSGSurface +{ + // (y-y0)^2/B^2 + (((x-x0)^2 + (z-z0)^2)^-1/2 -A))^2/C^2 -1 = 0 + double x0_, y0_, z0_, A_, B_, C_; +public: + explicit SurfaceYTorus(pugi::xml_node surf_node); + double evaluate(Position r) const; + double distance(Position r, Direction u, bool coincident) const; + Direction normal(Position r) const; + void to_hdf5_inner(hid_t group_id) const; +}; + +//============================================================================== +//! A toroidal surface described by the quartic torus lies in the z direction +// +//! \f$(x-x_0)^2 + (\sqrt{(y-y_0)^2 + (z-z_0)^2}-A)^2/C^2 -1 \f$ +//============================================================================== + +class SurfaceZTorus : public CSGSurface +{ + // (z-z0)^2/B^2 + (((x-x0)^2 + (y-y0)^2)^-1/2 -A))^2/C^2 -1 = 0 + double x0_, y0_, z0_, A_, B_, C_; +public: + explicit SurfaceZTorus(pugi::xml_node surf_node); + double evaluate(Position r) const; + double distance(Position r, Direction u, bool coincident) const; + Direction normal(Position r) const; + void to_hdf5_inner(hid_t group_id) const; +}; + //============================================================================== // Non-member functions //============================================================================== diff --git a/src/surface.cpp b/src/surface.cpp index 73a5aaaf08..273349ec0e 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -85,6 +85,28 @@ void read_coeffs(pugi::xml_node surf_node, int surf_id, double& c1, double& c2, } } +void read_coeffs(pugi::xml_node surf_node, int surf_id, double &c1, double &c2, + double &c3, double &c4, double &c5, double &c6) +{ + // Check the given number of coefficients. + std::string coeffs = get_node_value(surf_node, "coeffs"); + int n_words = word_count(coeffs); + if (n_words != 6) { + std::stringstream err_msg; + err_msg << "Surface " << surf_id << " expects 6 coeffs but was given " + << n_words; + fatal_error(err_msg); + } + + // Parse the coefficients. + int stat = sscanf(coeffs.c_str(), "%lf %lf %lf %lf %lf %lf", &c1, &c2, &c3, &c4, &c5, &\ +c6); + if (stat != 6) { + fatal_error("Something went wrong reading surface coeffs"); + } +} + + void read_coeffs(pugi::xml_node surf_node, int surf_id, double& c1, double& c2, double& c3, double& c4, double& c5, double& c6, double& c7, double& c8, double& c9, double& c10) @@ -983,6 +1005,527 @@ void SurfaceQuadric::to_hdf5_inner(hid_t group_id) const write_dataset(group_id, "coefficients", coeffs); } +//============================================================================== +//============================================================================== +// Generic functions for quadratic, cubic & quartic solver +//============================================================================== + +int quadratic_solve(double a, double b, double c, std::array &x) { + + double func = std::pow(b,2) - 4*a*c; + + if ( func < 0 ) { + // this would be imaginary + } else { + x[0] = -b/(2.*a) - std::sqrt(func)/(2.*a); + x[1] = -b/(2.*a) + std::sqrt(func)/(2.*a); + } + + return 0; +} + +const double M_2PI = 2*PI; +const double eps=FP_COINCIDENT; + +//typedef std::complex DComplex; + +//--------------------------------------------------------------------------- +// solve cubic equation x^3 + a*x^2 + b*x + c +// x - array of size 3 +// In case 3 real roots: => x[0], x[1], x[2], return 3 +// 2 real roots: x[0], x[1], return 2 +// 1 real root : x[0], x[1] ± i*x[2], return 1 +unsigned int solve_cubic(const double a, const double b, const double c, std::array &x) +{ + double a2 = a*a; + double q = (a2 - 3*b)/9; + double r = (a*(2*a2-9*b) + 27*c)/54; + double r2 = r*r; + double q3 = std::pow(q,3); + double A,B; + double a_prime = 0.; + if( r2 < q3 ) // 3 roots + { + double t=r/sqrt(q3); + if( t<-1) t=-1; + if( t> 1) t= 1; + t=std::acos(t); + a_prime = a/3.; + q=-2*std::sqrt(q); + x[0]=q*std::cos(t/3)-a_prime; + x[1]=q*std::cos((t+M_2PI)/3)-a_prime; + x[2]=q*std::cos((t-M_2PI)/3)-a_prime; + return 3; + } + else + { + A =-std::pow(std::fabs(r)+std::sqrt(r2-q3),1./3); + if( r<0 ) A=-A; + B = (0 == A ? 0 : q/A); + + a_prime = a/3; + x[0] =(A+B)-a_prime; + x[1] =-0.5*(A+B)-a_prime; + x[2] = 0.5*std::sqrt(3.)*(A-B); + // 2 real roots + if(std::fabs(x[2]) &real_roots) { + double a3 = -b; + double b3 = a*c -4.*d; + double c3 = -a*a*d - c*c + 4.*b*d; + + // cubic resolvent + // y^3 − b*y^2 + (ac−4d)*y − a^2*d−c^2+4*b*d = 0 + + std::array cube_roots; + unsigned int num_roots = solve_cubic(a3, b3, c3, cube_roots); + + double q1, q2, p1, p2, D, sqD, y; + + y = cube_roots[0]; + // The essence - choosing Y with maximal absolute value. + if(num_roots != 1) + { + if(std::fabs(cube_roots[1]) > std::fabs(y)) y = cube_roots[1]; + if(std::fabs(cube_roots[2]) > std::fabs(y)) y = cube_roots[2]; + } + + // h1+h2 = y && h1*h2 = d <=> h^2 -y*h + d = 0 (h === q) + + D = y*y - 4*d; + if(std::fabs(D) < eps) //in other words - D==0 + { + q1 = q2 = y * 0.5; + // g1+g2 = a && g1+g2 = b-y <=> g^2 - a*g + b-y = 0 (p === g) + D = a*a - 4*(b-y); + if(std::fabs(D) < eps) { + p1 = p2 = a * 0.5; + } + else + { + sqD = std::sqrt(D); + p1 = (a + sqD) * 0.5; + p2 = (a - sqD) * 0.5; + } + } + else + { + sqD = std::sqrt(D); + q1 = (y + sqD) * 0.5; + q2 = (y - sqD) * 0.5; + p1 = (a*q1-c)/(q1-q2); + p2 = (c-a*q2)/(q1-q2); + } + + std::array,4> roots; //the roots to return + + // solving quadratic eq. - x^2 + p1*x + q1 = 0 + D = p1*p1 - 4*q1; + if(D < 0.0) + { + roots[0].real( -p1 * 0.5 ); + roots[0].imag( std::sqrt(-D) * 0.5 ); + roots[1] = std::conj(roots[0]); + } + else + { + sqD = std::sqrt(D); + roots[0].real( (-p1 + sqD) * 0.5 ); + roots[1].real( (-p1 - sqD) * 0.5 ); + } + + // solving quadratic eq. - x^2 + p2*x + q2 = 0 + D = p2*p2 - 4*q2; + if(D < 0.0) + { + roots[2].real( -p2 * 0.5 ); + roots[2].imag( std::sqrt(-D) * 0.5 ); + roots[3] = std::conj(roots[2]); + } + else + { + sqD = std::sqrt(D); + roots[2].real( (-p2 + sqD) * 0.5 ); + roots[3].real( (-p2 - sqD) * 0.5 ); + } + + for ( int i = 0 ; i < 4 ; i++ ) { + if (roots[i].imag() == 0. ) + real_roots[i] = roots[i].real(); + else + real_roots[i] = 0.; + } + + std::sort(real_roots.begin(),real_roots.end()); + + return; +} + +//============================================================================== + +SurfaceXTorus::SurfaceXTorus(pugi::xml_node surf_node) + : CSGSurface(surf_node) +{ + read_coeffs(surf_node, id_, x0_, y0_, z0_, A_, B_, C_); +} + +void SurfaceXTorus::to_hdf5_inner(hid_t group_id) const +{ + write_string(group_id, "type", "x-torus", false); + std::array coeffs {{x0_, y0_, z0_, A_, B_, C_ }}; + write_dataset(group_id, "coefficients", coeffs); +} + +// todo should do some alebgra first to get rid of the sqrt + +double +SurfaceXTorus::evaluate(Position r) const +{ + const double x = r.x; + const double y = r.y; + const double z = r.z; + + const double x_coeff2 = std::pow(x-x0_,2); + const double y_coeff2 = std::pow(y-y0_,2); + const double z_coeff2 = std::pow(z-z0_,2); + + const double B2 = B_*B_; + const double C2 = C_*C_; + + return x_coeff2/B2 + std::pow(std::sqrt(y_coeff2 + z_coeff2)-A_,2)/C2 - 1.; +} + +double +SurfaceXTorus::distance(Position r, Direction ang, bool coincident) const +{ + double u = ang.x; + double v = ang.y; + double w = ang.z; + + double xr = r.x; + double yr = r.y; + double zr = r.z; + + double A = A_; + double B = B_; + double C = C_; + + double x0 = x0_ + xr; + double y0 = y0_ + yr; + double z0 = z0_ + zr; + + double a,b,c,d,e; // coefficients of quartic + + // a is always 1 maybe save the maths? + a = std::pow(v,4) + 2*std::pow(v,2)*std::pow(w,2) + std::pow(w,4) + 2*std::pow(C,2)*std::pow(u,2)*std::pow(v,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(u,2)*std::pow(w,2)/std::pow(B,2) + std::pow(C,4)*std::pow(u,4)/std::pow(B,4); + b = 4*std::pow(v,3)*y0 + 4*std::pow(v,2)*w*z0 + 4*v*std::pow(w,2)*y0 + 4*std::pow(w,3)*z0 + 4*std::pow(C,2)*std::pow(u,2)*v*y0/std::pow(B,2) + 4*std::pow(C,2)*std::pow(u,2)*w*z0/std::pow(B,2) + 4*std::pow(C,2)*u*std::pow(v,2)*x0/std::pow(B,2) + 4*std::pow(C,2)*u*std::pow(w,2)*x0/std::pow(B,2) + 4*std::pow(C,4)*std::pow(u,3)*x0/std::pow(B,4); + c = -2*std::pow(A,2)*std::pow(v,2) - 2*std::pow(A,2)*std::pow(w,2) + 2*std::pow(A,2)*std::pow(C,2)*std::pow(u,2)/std::pow(B,2) - 2*std::pow(C,2)*std::pow(v,2) - 2*std::pow(C,2)*std::pow(w,2) + 6*std::pow(v,2)*std::pow(y0,2) + 2*std::pow(v,2)*std::pow(z0,2) + 8*v*w*y0*z0 + 2*std::pow(w,2)*std::pow(y0,2) + 6*std::pow(w,2)*std::pow(z0,2) - 2*std::pow(C,4)*std::pow(u,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(u,2)*std::pow(y0,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(u,2)*std::pow(z0,2)/std::pow(B,2) + 8*std::pow(C,2)*u*v*x0*y0/std::pow(B,2) + 8*std::pow(C,2)*u*w*x0*z0/std::pow(B,2) + 2*std::pow(C,2)*std::pow(v,2)*std::pow(x0,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(w,2)*std::pow(x0,2)/std::pow(B,2) + 6*std::pow(C,4)*std::pow(u,2)*std::pow(x0,2)/std::pow(B,4); + d = -4*std::pow(A,2)*v*y0 - 4*std::pow(A,2)*w*z0 + 4*std::pow(A,2)*std::pow(C,2)*u*x0/std::pow(B,2) - 4*std::pow(C,2)*v*y0 - 4*std::pow(C,2)*w*z0 + 4*v*std::pow(y0,3) + 4*v*y0*std::pow(z0,2) + 4*w*std::pow(y0,2)*z0 + 4*w*std::pow(z0,3) - 4*std::pow(C,4)*u*x0/std::pow(B,2) + 4*std::pow(C,2)*u*x0*std::pow(y0,2)/std::pow(B,2) + 4*std::pow(C,2)*u*x0*std::pow(z0,2)/std::pow(B,2) + 4*std::pow(C,2)*v*std::pow(x0,2)*y0/std::pow(B,2) + 4*std::pow(C,2)*w*std::pow(x0,2)*z0/std::pow(B,2) + 4*std::pow(C,4)*u*std::pow(x0,3)/std::pow(B,4); + e = std::pow(A,4) - 2*std::pow(A,2)*std::pow(C,2) - 2*std::pow(A,2)*std::pow(y0,2) - 2*std::pow(A,2)*std::pow(z0,2) + 2*std::pow(A,2)*std::pow(C,2)*std::pow(x0,2)/std::pow(B,2) + std::pow(C,4) - 2*std::pow(C,2)*std::pow(y0,2) - 2*std::pow(C,2)*std::pow(z0,2) + std::pow(y0,4) + 2*std::pow(y0,2)*std::pow(z0,2) + std::pow(z0,4) - 2*std::pow(C,4)*std::pow(x0,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(x0,2)*std::pow(y0,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(x0,2)*std::pow(z0,2)/std::pow(B,2) + std::pow(C,4)*std::pow(x0,4)/std::pow(B,4); + + // + std::array roots; + quartic_solve(b,c,d,e,roots); + + if (coincident) r += ang*TINY_BIT; + + // special degerenate case two sets of repated + // roots + if ( b == 0.0 && d == 0) { + if ( roots[1] - roots[0] < 1e-5 ) return INFTY; + if ( roots[3] - roots[2] < 1e-5 ) return INFTY; + } + + for ( int i = 0 ; i < 4 ; i++ ) { + // need something better than just raw tolerance + // use fastQS to get back lost precision + if ( roots[i] > 1e-6) { + return roots[i]; + } + } + + // otherwise no hit + return INFTY; +} + +Direction +SurfaceXTorus::normal(Position r) const +{ + // reduce the expansion of the full form for torus + double x = r.x - x0_; + double y = r.y - y0_; + double z = r.z - z0_; + + double A2 = A_*A_; + double B2 = B_*B_; + double C2 = C_*C_; + + // coefficients for the x, x^2, x^3 and x^4 term + // but since we differeniate - we get x4 -> 4x3 + double dx_4 = 4*x*x*x*C2*C2/(B2*B2); + double dx_2 = 4*x*C2/B2*(z*z + 2*y*y - 2*C2 + 2*A2); + double dx = dx_4 + dx_2; + // similarly to y + double dy_4 = 4*y*y*y; + double dy_2 = 4*y*(z*z + C2*x*x/B2 - A2 - C2); + double dy = dy_2 + dy_4; + // and z + double dz_4 = 4*z*z*z; + double dz_2 = 4*z*(C2*x*x/B2 + y*y - C2 - A2); + double dz = dz_2 + dz_4; + + double length = std::sqrt(dx*dx + dy*dy + dz*dz); + + return {dx/length,dy/length,dz/length}; +} + +SurfaceYTorus::SurfaceYTorus(pugi::xml_node surf_node) + : CSGSurface(surf_node) +{ + read_coeffs(surf_node, id_, x0_, y0_, z0_, A_, B_, C_); +} + +void SurfaceYTorus::to_hdf5_inner(hid_t group_id) const +{ + write_string(group_id, "type", "y-torus", false); + std::array coeffs {{x0_, y0_, z0_, A_, B_, C_ }}; + write_dataset(group_id, "coefficients", coeffs); +} + +double +SurfaceYTorus::evaluate(Position r) const +{ + const double x = r.x; + const double y = r.y; + const double z = r.z; + + const double x_coeff2 = std::pow(x-x0_,2); + const double y_coeff2 = std::pow(y-y0_,2); + const double z_coeff2 = std::pow(z-z0_,2); + + const double B2 = B_*B_; + const double C2 = C_*C_; + + return y_coeff2/B2 + std::pow(std::sqrt(x_coeff2 + z_coeff2)-A_,2)/C2 - 1.; +} + +double +SurfaceYTorus::distance(Position r, Direction ang, bool coincident) const +{ + double u = ang.x; + double v = ang.y; + double w = ang.z; + + double xr = r.x; + double yr = r.y; + double zr = r.z; + + double A = A_; + double B = B_; + double C = C_; + + double x0 = x0_ + xr; + double y0 = y0_ + yr; + double z0 = z0_ + zr; + + double a,b,c,d,e; // coefficients of quartic + + a = std::pow(u,4) + 2*std::pow(u,2)*std::pow(w,2) + std::pow(w,4) + 2*std::pow(C,2)*std::pow(u,2)*std::pow(v,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(v,2)*std::pow(w,2)/std::pow(B,2) + std::pow(C,4)*std::pow(v,4)/std::pow(B,4); + b = 4*std::pow(u,3)*x0 + 4*std::pow(u,2)*w*z0 + 4*u*std::pow(w,2)*x0 + 4*std::pow(w,3)*z0 + 4*std::pow(C,2)*std::pow(u,2)*v*y0/std::pow(B,2) + 4*std::pow(C,2)*u*std::pow(v,2)*x0/std::pow(B,2) + 4*std::pow(C,2)*std::pow(v,2)*w*z0/std::pow(B,2) + 4*std::pow(C,2)*v*std::pow(w,2)*y0/std::pow(B,2) + 4*std::pow(C,4)*std::pow(v,3)*y0/std::pow(B,4); + c = -2*std::pow(A,2)*std::pow(u,2) - 2*std::pow(A,2)*std::pow(w,2) + 2*std::pow(A,2)*std::pow(C,2)*std::pow(v,2)/std::pow(B,2) - 2*std::pow(C,2)*std::pow(u,2) - 2*std::pow(C,2)*std::pow(w,2) + 6*std::pow(u,2)*std::pow(x0,2) + 2*std::pow(u,2)*std::pow(z0,2) + 8*u*w*x0*z0 + 2*std::pow(w,2)*std::pow(x0,2) + 6*std::pow(w,2)*std::pow(z0,2) - 2*std::pow(C,4)*std::pow(v,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(u,2)*std::pow(y0,2)/std::pow(B,2) + 8*std::pow(C,2)*u*v*x0*y0/std::pow(B,2) + 2*std::pow(C,2)*std::pow(v,2)*std::pow(x0,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(v,2)*std::pow(z0,2)/std::pow(B,2) + 8*std::pow(C,2)*v*w*y0*z0/std::pow(B,2) + 2*std::pow(C,2)*std::pow(w,2)*std::pow(y0,2)/std::pow(B,2) + 6*std::pow(C,4)*std::pow(v,2)*std::pow(y0,2)/std::pow(B,4); + d = -4*std::pow(A,2)*u*x0 - 4*std::pow(A,2)*w*z0 + 4*std::pow(A,2)*std::pow(C,2)*v*y0/std::pow(B,2) - 4*std::pow(C,2)*u*x0 - 4*std::pow(C,2)*w*z0 + 4*u*std::pow(x0,3) + 4*u*x0*std::pow(z0,2) + 4*w*std::pow(x0,2)*z0 + 4*w*std::pow(z0,3) - 4*std::pow(C,4)*v*y0/std::pow(B,2) + 4*std::pow(C,2)*u*x0*std::pow(y0,2)/std::pow(B,2) + 4*std::pow(C,2)*v*std::pow(x0,2)*y0/std::pow(B,2) + 4*std::pow(C,2)*v*y0*std::pow(z0,2)/std::pow(B,2) + 4*std::pow(C,2)*w*std::pow(y0,2)*z0/std::pow(B,2) + 4*std::pow(C,4)*v*std::pow(y0,3)/std::pow(B,4); + e = std::pow(A,4) - 2*std::pow(A,2)*std::pow(C,2) - 2*std::pow(A,2)*std::pow(x0,2) - 2*std::pow(A,2)*std::pow(z0,2) + 2*std::pow(A,2)*std::pow(C,2)*std::pow(y0,2)/std::pow(B,2) + std::pow(C,4) - 2*std::pow(C,2)*std::pow(x0,2) - 2*std::pow(C,2)*std::pow(z0,2) + std::pow(x0,4) + 2*std::pow(x0,2)*std::pow(z0,2) + std::pow(z0,4) - 2*std::pow(C,4)*std::pow(y0,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(x0,2)*std::pow(y0,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(y0,2)*std::pow(z0,2)/std::pow(B,2) + std::pow(C,4)*std::pow(y0,4)/std::pow(B,4); + + // + std::array roots; + quartic_solve(b,c,d,e,roots); + + if (coincident) r += ang*TINY_BIT; + + // special degerenate case two sets of repated + if ( b == 0.0 && d == 0) { + if ( roots[1] - roots[0] < 1e-5 ) return INFTY; + if ( roots[3] - roots[2] < 1e-5 ) return INFTY; + } + + for ( int i = 0 ; i < 4 ; i++ ) { + // need something better than just raw tolerance + // use fastQS to get back lost precision + if ( roots[i] > 1e-6) { + return roots[i]; + } + } + + // otherwise no hit + return INFTY; + + return 0; +} + +Direction +SurfaceYTorus::normal(Position r) const +{ + // reduce the expansion of the full form for torus + double x = r.x - x0_; + double y = r.y - y0_; + double z = r.z - z0_; + + double A2 = A_*A_; + double B2 = B_*B_; + double C2 = C_*C_; + + // a**4 - 2*a**2*c**2 - 2*a**2*x**2 - 2*a**2*z**2 + 2*a**2*c**2*y**2/b**2 + c**4 + // - 2*c**2*x**2 - 2*c**2*z**2 + x**4 + 2*x**2*z**2 + z**4 - 2*c**4*y**2/b**2 + + // 2*c**2*x**2*y**2/b**2 + 2*c**2*y**2*z**2/b**2 + c**4*y**4/b**4 + + + // coefficients for the x, x^2, x^3 and x^4 term + // but since we differeniate - we get x4 -> 4x3 + double dx_4 = 4*x*x*x; + double dx_2 = 4*x*(z*z + C2*y*y/B2 - A2 - C2); + double dx = dx_4 + dx_2; + // similarly to y + double dy_4 = 4*y*y*y*C2*C2/(B2*B2); + double dy_2 = 4*y*(z*z + C2*x*x/B2 - A2 - C2); + double dy = dy_2 + dy_4; + // and z + double dz_4 = 4*z*z*z; + double dz_2 = 4*z*(C2*y*y/B2 + x*x - C2 - A2); + double dz = dz_2 + dz_4; + + double length = std::sqrt(dx*dx + dy*dy + dz*dz); + + return {dx/length,dy/length,dz/length}; +} + +SurfaceZTorus::SurfaceZTorus(pugi::xml_node surf_node) + : CSGSurface(surf_node) +{ + read_coeffs(surf_node, id_, x0_, y0_, z0_, A_, B_, C_); +} + +void SurfaceZTorus::to_hdf5_inner(hid_t group_id) const +{ + write_string(group_id, "type", "z-torus", false); + std::array coeffs {{x0_, y0_, z0_, A_, B_, C_ }}; + write_dataset(group_id, "coefficients", coeffs); +} + +double +SurfaceZTorus::evaluate(Position r) const +{ + const double x = r.x; + const double y = r.y; + const double z = r.z; + + const double x_coeff2 = std::pow(x-x0_,2); + const double y_coeff2 = std::pow(y-y0_,2); + const double z_coeff2 = std::pow(z-z0_,2); + + const double B2 = B_*B_; + const double C2 = C_*C_; + + return z_coeff2/B2 + std::pow(std::sqrt(x_coeff2 + y_coeff2)-A_,2)/C2 - 1.; +} + +double +SurfaceZTorus::distance(Position r, Direction ang, bool coincident) const +{ + double u = ang.x; + double v = ang.y; + double w = ang.z; + + double xr = r.x; + double yr = r.y; + double zr = r.z; + + double A = A_; + double B = B_; + double C = C_; + + double x0 = x0_; + double y0 = y0_; + double z0 = z0_; + + double a,b,c,d,e; // coefficients of quartic + + // 4th order coefficient + a = std::pow(u,4) + 2*std::pow(u,2)*std::pow(v,2) + std::pow(v,4) + 2*std::pow(C,2)*std::pow(u,2)*std::pow(w,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(v,2)*std::pow(w,2)/std::pow(B,2) + std::pow(C,4)*std::pow(w,4)/std::pow(B,4); + // 3rd order coefficient + b = -4*std::pow(u,3)*x0 + 4*std::pow(u,3)*xr - 4*std::pow(u,2)*v*y0 + 4*std::pow(u,2)*v*yr - 4*u*std::pow(v,2)*x0 + 4*u*std::pow(v,2)*xr - 4*std::pow(v,3)*y0 + 4*std::pow(v,3)*yr - 4*std::pow(C,2)*std::pow(u,2)*w*z0/std::pow(B,2) + 4*std::pow(C,2)*std::pow(u,2)*w*zr/std::pow(B,2) - 4*std::pow(C,2)*u*std::pow(w,2)*x0/std::pow(B,2) + 4*std::pow(C,2)*u*std::pow(w,2)*xr/std::pow(B,2) - 4*std::pow(C,2)*std::pow(v,2)*w*z0/std::pow(B,2) + 4*std::pow(C,2)*std::pow(v,2)*w*zr/std::pow(B,2) - 4*std::pow(C,2)*v*std::pow(w,2)*y0/std::pow(B,2) + 4*std::pow(C,2)*v*std::pow(w,2)*yr/std::pow(B,2) - 4*std::pow(C,4)*std::pow(w,3)*z0/std::pow(B,4) + 4*std::pow(C,4)*std::pow(w,3)*zr/std::pow(B,4); + // 2nd order coefficient + c= -2*std::pow(A,2)*std::pow(u,2) - 2*std::pow(A,2)*std::pow(v,2) + 2*std::pow(A,2)*std::pow(C,2)*std::pow(w,2)/std::pow(B,2) - 2*std::pow(C,2)*std::pow(u,2) - 2*std::pow(C,2)*std::pow(v,2) + 6*std::pow(u,2)*std::pow(x0,2) - 12*std::pow(u,2)*x0*xr + 6*std::pow(u,2)*std::pow(xr,2) + 2*std::pow(u,2)*std::pow(y0,2) - 4*std::pow(u,2)*y0*yr + 2*std::pow(u,2)*std::pow(yr,2) + 8*u*v*x0*y0 - 8*u*v*x0*yr - 8*u*v*xr*y0 + 8*u*v*xr*yr + 2*std::pow(v,2)*std::pow(x0,2) - 4*std::pow(v,2)*x0*xr + 2*std::pow(v,2)*std::pow(xr,2) + 6*std::pow(v,2)*std::pow(y0,2) - 12*std::pow(v,2)*y0*yr + 6*std::pow(v,2)*std::pow(yr,2) - 2*std::pow(C,4)*std::pow(w,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(u,2)*std::pow(z0,2)/std::pow(B,2) - 4*std::pow(C,2)*std::pow(u,2)*z0*zr/std::pow(B,2) + 2*std::pow(C,2)*std::pow(u,2)*std::pow(zr,2)/std::pow(B,2) + 8*std::pow(C,2)*u*w*x0*z0/std::pow(B,2) - 8*std::pow(C,2)*u*w*x0*zr/std::pow(B,2) - 8*std::pow(C,2)*u*w*xr*z0/std::pow(B,2) + 8*std::pow(C,2)*u*w*xr*zr/std::pow(B,2) + 2*std::pow(C,2)*std::pow(v,2)*std::pow(z0,2)/std::pow(B,2) - 4*std::pow(C,2)*std::pow(v,2)*z0*zr/std::pow(B,2) + 2*std::pow(C,2)*std::pow(v,2)*std::pow(zr,2)/std::pow(B,2) + 8*std::pow(C,2)*v*w*y0*z0/std::pow(B,2) - 8*std::pow(C,2)*v*w*y0*zr/std::pow(B,2) - 8*std::pow(C,2)*v*w*yr*z0/std::pow(B,2) + 8*std::pow(C,2)*v*w*yr*zr/std::pow(B,2) + 2*std::pow(C,2)*std::pow(w,2)*std::pow(x0,2)/std::pow(B,2) - 4*std::pow(C,2)*std::pow(w,2)*x0*xr/std::pow(B,2) + 2*std::pow(C,2)*std::pow(w,2)*std::pow(xr,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(w,2)*std::pow(y0,2)/std::pow(B,2) - 4*std::pow(C,2)*std::pow(w,2)*y0*yr/std::pow(B,2) + 2*std::pow(C,2)*std::pow(w,2)*std::pow(yr,2)/std::pow(B,2) + 6*std::pow(C,4)*std::pow(w,2)*std::pow(z0,2)/std::pow(B,4) - 12*std::pow(C,4)*std::pow(w,2)*z0*zr/std::pow(B,4) + 6*std::pow(C,4)*std::pow(w,2)*std::pow(zr,2)/std::pow(B,4); + // the 1st order terms + d = 4*std::pow(A,2)*u*x0 - 4*std::pow(A,2)*u*xr + 4*std::pow(A,2)*v*y0 - 4*std::pow(A,2)*v*yr - 4*std::pow(A,2)*std::pow(C,2)*w*z0/std::pow(B,2) + 4*std::pow(A,2)*std::pow(C,2)*w*zr/std::pow(B,2) + 4*std::pow(C,2)*u*x0 - 4*std::pow(C,2)*u*xr + 4*std::pow(C,2)*v*y0 - 4*std::pow(C,2)*v*yr - 4*u*std::pow(x0,3) + 12*u*std::pow(x0,2)*xr - 12*u*x0*std::pow(xr,2) - 4*u*x0*std::pow(y0,2) + 8*u*x0*y0*yr - 4*u*x0*std::pow(yr,2) + 4*u*std::pow(xr,3) + 4*u*xr*std::pow(y0,2) - 8*u*xr*y0*yr + 4*u*xr*std::pow(yr,2) - 4*v*std::pow(x0,2)*y0 + 4*v*std::pow(x0,2)*yr + 8*v*x0*xr*y0 - 8*v*x0*xr*yr - 4*v*std::pow(xr,2)*y0 + 4*v*std::pow(xr,2)*yr - 4*v*std::pow(y0,3) + 12*v*std::pow(y0,2)*yr - 12*v*y0*std::pow(yr,2) + 4*v*std::pow(yr,3) + 4*std::pow(C,4)*w*z0/std::pow(B,2) - 4*std::pow(C,4)*w*zr/std::pow(B,2) - 4*std::pow(C,2)*u*x0*std::pow(z0,2)/std::pow(B,2) + 8*std::pow(C,2)*u*x0*z0*zr/std::pow(B,2) - 4*std::pow(C,2)*u*x0*std::pow(zr,2)/std::pow(B,2) + 4*std::pow(C,2)*u*xr*std::pow(z0,2)/std::pow(B,2) - 8*std::pow(C,2)*u*xr*z0*zr/std::pow(B,2) + 4*std::pow(C,2)*u*xr*std::pow(zr,2)/std::pow(B,2) - 4*std::pow(C,2)*v*y0*std::pow(z0,2)/std::pow(B,2) + 8*std::pow(C,2)*v*y0*z0*zr/std::pow(B,2) - 4*std::pow(C,2)*v*y0*std::pow(zr,2)/std::pow(B,2) + 4*std::pow(C,2)*v*yr*std::pow(z0,2)/std::pow(B,2) - 8*std::pow(C,2)*v*yr*z0*zr/std::pow(B,2) + 4*std::pow(C,2)*v*yr*std::pow(zr,2)/std::pow(B,2) - 4*std::pow(C,2)*w*std::pow(x0,2)*z0/std::pow(B,2) + 4*std::pow(C,2)*w*std::pow(x0,2)*zr/std::pow(B,2) + 8*std::pow(C,2)*w*x0*xr*z0/std::pow(B,2) - 8*std::pow(C,2)*w*x0*xr*zr/std::pow(B,2) - 4*std::pow(C,2)*w*std::pow(xr,2)*z0/std::pow(B,2) + 4*std::pow(C,2)*w*std::pow(xr,2)*zr/std::pow(B,2) - 4*std::pow(C,2)*w*std::pow(y0,2)*z0/std::pow(B,2) + 4*std::pow(C,2)*w*std::pow(y0,2)*zr/std::pow(B,2) + 8*std::pow(C,2)*w*y0*yr*z0/std::pow(B,2) - 8*std::pow(C,2)*w*y0*yr*zr/std::pow(B,2) - 4*std::pow(C,2)*w*std::pow(yr,2)*z0/std::pow(B,2) + 4*std::pow(C,2)*w*std::pow(yr,2)*zr/std::pow(B,2) - 4*std::pow(C,4)*w*std::pow(z0,3)/std::pow(B,4) + 12*std::pow(C,4)*w*std::pow(z0,2)*zr/std::pow(B,4) - 12*std::pow(C,4)*w*z0*std::pow(zr,2)/std::pow(B,4) + 4*std::pow(C,4)*w*std::pow(zr,3)/std::pow(B,4); + // the 0th order terms + e = std::pow(A,4) - 2*std::pow(A,2)*std::pow(C,2) - 2*std::pow(A,2)*std::pow(x0,2) + 4*std::pow(A,2)*x0*xr - 2*std::pow(A,2)*std::pow(xr,2) - 2*std::pow(A,2)*std::pow(y0,2) + 4*std::pow(A,2)*y0*yr - 2*std::pow(A,2)*std::pow(yr,2) + 2*std::pow(A,2)*std::pow(C,2)*std::pow(z0,2)/std::pow(B,2) - 4*std::pow(A,2)*std::pow(C,2)*z0*zr/std::pow(B,2) + 2*std::pow(A,2)*std::pow(C,2)*std::pow(zr,2)/std::pow(B,2) + std::pow(C,4) - 2*std::pow(C,2)*std::pow(x0,2) + 4*std::pow(C,2)*x0*xr - 2*std::pow(C,2)*std::pow(xr,2) - 2*std::pow(C,2)*std::pow(y0,2) + 4*std::pow(C,2)*y0*yr - 2*std::pow(C,2)*std::pow(yr,2) + std::pow(x0,4) - 4*std::pow(x0,3)*xr + 6*std::pow(x0,2)*std::pow(xr,2) + 2*std::pow(x0,2)*std::pow(y0,2) - 4*std::pow(x0,2)*y0*yr + 2*std::pow(x0,2)*std::pow(yr,2) - 4*x0*std::pow(xr,3) - 4*x0*xr*std::pow(y0,2) + 8*x0*xr*y0*yr - 4*x0*xr*std::pow(yr,2) + std::pow(xr,4) + 2*std::pow(xr,2)*std::pow(y0,2) - 4*std::pow(xr,2)*y0*yr + 2*std::pow(xr,2)*std::pow(yr,2) + std::pow(y0,4) - 4*std::pow(y0,3)*yr + 6*std::pow(y0,2)*std::pow(yr,2) - 4*y0*std::pow(yr,3) + std::pow(yr,4) - 2*std::pow(C,4)*std::pow(z0,2)/std::pow(B,2) + 4*std::pow(C,4)*z0*zr/std::pow(B,2) - 2*std::pow(C,4)*std::pow(zr,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(x0,2)*std::pow(z0,2)/std::pow(B,2) - 4*std::pow(C,2)*std::pow(x0,2)*z0*zr/std::pow(B,2) + 2*std::pow(C,2)*std::pow(x0,2)*std::pow(zr,2)/std::pow(B,2) - 4*std::pow(C,2)*x0*xr*std::pow(z0,2)/std::pow(B,2) + 8*std::pow(C,2)*x0*xr*z0*zr/std::pow(B,2) - 4*std::pow(C,2)*x0*xr*std::pow(zr,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(xr,2)*std::pow(z0,2)/std::pow(B,2) - 4*std::pow(C,2)*std::pow(xr,2)*z0*zr/std::pow(B,2) + 2*std::pow(C,2)*std::pow(xr,2)*std::pow(zr,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(y0,2)*std::pow(z0,2)/std::pow(B,2) - 4*std::pow(C,2)*std::pow(y0,2)*z0*zr/std::pow(B,2) + 2*std::pow(C,2)*std::pow(y0,2)*std::pow(zr,2)/std::pow(B,2) - 4*std::pow(C,2)*y0*yr*std::pow(z0,2)/std::pow(B,2) + 8*std::pow(C,2)*y0*yr*z0*zr/std::pow(B,2) - 4*std::pow(C,2)*y0*yr*std::pow(zr,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(yr,2)*std::pow(z0,2)/std::pow(B,2) - 4*std::pow(C,2)*std::pow(yr,2)*z0*zr/std::pow(B,2) + 2*std::pow(C,2)*std::pow(yr,2)*std::pow(zr,2)/std::pow(B,2) + std::pow(C,4)*std::pow(z0,4)/std::pow(B,4) - 4*std::pow(C,4)*std::pow(z0,3)*zr/std::pow(B,4) + 6*std::pow(C,4)*std::pow(z0,2)*std::pow(zr,2)/std::pow(B,4) - 4*std::pow(C,4)*z0*std::pow(zr,3)/std::pow(B,4) + std::pow(C,4)*std::pow(zr,4)/std::pow(B,4); + + // + std::array roots; + quartic_solve(b,c,d,e,roots); + + if (coincident) r += ang*TINY_BIT; + + // special degerenate case two sets of repated + // roots + if ( b == 0.0 && d == 0) { + if ( roots[1] - roots[0] < 1e-5 ) return INFTY; + if ( roots[3] - roots[2] < 1e-5 ) return INFTY; + } + + for ( int i = 0 ; i < 4 ; i++ ) { + // need something better than just raw tolerance + // use fastQS to get back lost precision + if ( roots[i] > 1e-6) { + return roots[i]; + } + } + + // otherwise no hit + return INFTY; +} + +Direction +SurfaceZTorus::normal(Position r) const +{ + // reduce the expansion of the full form for torus + double x = r.x - x0_; + double y = r.y - y0_; + double z = r.z - z0_; + + double A2 = A_*A_; + double B2 = B_*B_; + double C2 = C_*C_; + + + // coefficients for the x, x^2, x^3 and x^4 term + // but since we differeniate - we get x4 -> 4x3 + double dx_4 = 1.0; + double dx_2 = -2.*(A2 - C2 + y*y + C2*z*z/B2); + double dx = 4.*dx_4*x*x*x + 2.*dx_2*x; + // similarly to y + double dy_4 = 1.0; + double dy_2 = -2.*(A2 - C2 + x*x + C2*z*z/B2); + double dy = 4.*dy_4*y*y*y + 2.*dy_2*y; + // and z + double dz_4 = std::pow(C_,4)/std::pow(B_,4); + double dz_2 = 2.*(A2*C2/B2 - std::pow(C_,4)/B2 + C2*x*x/B2 + C2*y*y/B2); + double dz = 4.*dz_4*z*z*z + 2.*dz_2*z; + + double length = std::sqrt(dx*dx + dy*dy + dz*dz); + + return {dx/length,dy/length,dz/length}; +} + + + //============================================================================== void read_surfaces(pugi::xml_node node) @@ -1042,6 +1585,15 @@ void read_surfaces(pugi::xml_node node) } else if (surf_type == "quadric") { model::surfaces.push_back(make_unique(surf_node)); + } else if (surf_type == "x-torus") { + model::surfaces.push_back(std::make_unique(surf_node)); + + } else if (surf_type == "y-torus") { + model::surfaces.push_back(std::make_unique(surf_node)); + + } else if (surf_type == "z-torus") { + model::surfaces.push_back(std::make_unique(surf_node)); + } else { fatal_error(fmt::format("Invalid surface type, \"{}\"", surf_type)); } From 04bf63d0dec4cdb9f527a81d37d17ce156f2f656 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 17 Dec 2021 17:00:12 -0500 Subject: [PATCH 02/32] Run clang-format --- include/openmc/surface.h | 12 +- src/surface.cpp | 732 +++++++++++++++++++++++++-------------- 2 files changed, 486 insertions(+), 258 deletions(-) diff --git a/include/openmc/surface.h b/include/openmc/surface.h index f4d093c8bb..c1aaf2cc15 100644 --- a/include/openmc/surface.h +++ b/include/openmc/surface.h @@ -377,10 +377,10 @@ public: //! \f$(x-x_0)^2 + (\sqrt{(y-y_0)^2 + (z-z_0)^2}-A)^2/C^2 -1 \f$ //============================================================================== -class SurfaceXTorus : public CSGSurface -{ +class SurfaceXTorus : public CSGSurface { // (x-x0)^2/B^2 + (((y-y0)^2 + (z-z0)^2)^-1/2 -A))^2/C^2 -1 = 0 double x0_, y0_, z0_, A_, B_, C_; + public: explicit SurfaceXTorus(pugi::xml_node surf_node); double evaluate(Position r) const; @@ -395,10 +395,10 @@ public: //! \f$(x-x_0)^2 + (\sqrt{(y-y_0)^2 + (z-z_0)^2}-A)^2/C^2 -1 \f$ //============================================================================== -class SurfaceYTorus : public CSGSurface -{ +class SurfaceYTorus : public CSGSurface { // (y-y0)^2/B^2 + (((x-x0)^2 + (z-z0)^2)^-1/2 -A))^2/C^2 -1 = 0 double x0_, y0_, z0_, A_, B_, C_; + public: explicit SurfaceYTorus(pugi::xml_node surf_node); double evaluate(Position r) const; @@ -413,10 +413,10 @@ public: //! \f$(x-x_0)^2 + (\sqrt{(y-y_0)^2 + (z-z_0)^2}-A)^2/C^2 -1 \f$ //============================================================================== -class SurfaceZTorus : public CSGSurface -{ +class SurfaceZTorus : public CSGSurface { // (z-z0)^2/B^2 + (((x-x0)^2 + (y-y0)^2)^-1/2 -A))^2/C^2 -1 = 0 double x0_, y0_, z0_, A_, B_, C_; + public: explicit SurfaceZTorus(pugi::xml_node surf_node); double evaluate(Position r) const; diff --git a/src/surface.cpp b/src/surface.cpp index 273349ec0e..20b2e6ddb3 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -85,10 +85,10 @@ void read_coeffs(pugi::xml_node surf_node, int surf_id, double& c1, double& c2, } } -void read_coeffs(pugi::xml_node surf_node, int surf_id, double &c1, double &c2, - double &c3, double &c4, double &c5, double &c6) +void read_coeffs(pugi::xml_node surf_node, int surf_id, double& c1, double& c2, + double& c3, double& c4, double& c5, double& c6) { - // Check the given number of coefficients. + // Check the given number of coefficients. std::string coeffs = get_node_value(surf_node, "coeffs"); int n_words = word_count(coeffs); if (n_words != 6) { @@ -98,15 +98,14 @@ void read_coeffs(pugi::xml_node surf_node, int surf_id, double &c1, double &c2, fatal_error(err_msg); } - // Parse the coefficients. - int stat = sscanf(coeffs.c_str(), "%lf %lf %lf %lf %lf %lf", &c1, &c2, &c3, &c4, &c5, &\ -c6); + // Parse the coefficients. + int stat = sscanf( + coeffs.c_str(), "%lf %lf %lf %lf %lf %lf", &c1, &c2, &c3, &c4, &c5, &c6); if (stat != 6) { fatal_error("Something went wrong reading surface coeffs"); } } - void read_coeffs(pugi::xml_node surf_node, int surf_id, double& c1, double& c2, double& c3, double& c4, double& c5, double& c6, double& c7, double& c8, double& c9, double& c10) @@ -1010,24 +1009,25 @@ void SurfaceQuadric::to_hdf5_inner(hid_t group_id) const // Generic functions for quadratic, cubic & quartic solver //============================================================================== -int quadratic_solve(double a, double b, double c, std::array &x) { +int quadratic_solve(double a, double b, double c, std::array& x) +{ - double func = std::pow(b,2) - 4*a*c; - - if ( func < 0 ) { + double func = std::pow(b, 2) - 4 * a * c; + + if (func < 0) { // this would be imaginary } else { - x[0] = -b/(2.*a) - std::sqrt(func)/(2.*a); - x[1] = -b/(2.*a) + std::sqrt(func)/(2.*a); + x[0] = -b / (2. * a) - std::sqrt(func) / (2. * a); + x[1] = -b / (2. * a) + std::sqrt(func) / (2. * a); } return 0; } -const double M_2PI = 2*PI; -const double eps=FP_COINCIDENT; +const double M_2PI = 2 * PI; +const double eps = FP_COINCIDENT; -//typedef std::complex DComplex; +// typedef std::complex DComplex; //--------------------------------------------------------------------------- // solve cubic equation x^3 + a*x^2 + b*x + c @@ -1035,143 +1035,141 @@ const double eps=FP_COINCIDENT; // In case 3 real roots: => x[0], x[1], x[2], return 3 // 2 real roots: x[0], x[1], return 2 // 1 real root : x[0], x[1] ± i*x[2], return 1 -unsigned int solve_cubic(const double a, const double b, const double c, std::array &x) +unsigned int solve_cubic( + const double a, const double b, const double c, std::array& x) { - double a2 = a*a; - double q = (a2 - 3*b)/9; - double r = (a*(2*a2-9*b) + 27*c)/54; - double r2 = r*r; - double q3 = std::pow(q,3); - double A,B; + double a2 = a * a; + double q = (a2 - 3 * b) / 9; + double r = (a * (2 * a2 - 9 * b) + 27 * c) / 54; + double r2 = r * r; + double q3 = std::pow(q, 3); + double A, B; double a_prime = 0.; - if( r2 < q3 ) // 3 roots + if (r2 < q3) // 3 roots { - double t=r/sqrt(q3); - if( t<-1) t=-1; - if( t> 1) t= 1; - t=std::acos(t); - a_prime = a/3.; - q=-2*std::sqrt(q); - x[0]=q*std::cos(t/3)-a_prime; - x[1]=q*std::cos((t+M_2PI)/3)-a_prime; - x[2]=q*std::cos((t-M_2PI)/3)-a_prime; - return 3; - } - else - { - A =-std::pow(std::fabs(r)+std::sqrt(r2-q3),1./3); - if( r<0 ) A=-A; - B = (0 == A ? 0 : q/A); - - a_prime = a/3; - x[0] =(A+B)-a_prime; - x[1] =-0.5*(A+B)-a_prime; - x[2] = 0.5*std::sqrt(3.)*(A-B); + double t = r / sqrt(q3); + if (t < -1) + t = -1; + if (t > 1) + t = 1; + t = std::acos(t); + a_prime = a / 3.; + q = -2 * std::sqrt(q); + x[0] = q * std::cos(t / 3) - a_prime; + x[1] = q * std::cos((t + M_2PI) / 3) - a_prime; + x[2] = q * std::cos((t - M_2PI) / 3) - a_prime; + return 3; + } else { + A = -std::pow(std::fabs(r) + std::sqrt(r2 - q3), 1. / 3); + if (r < 0) + A = -A; + B = (0 == A ? 0 : q / A); + + a_prime = a / 3; + x[0] = (A + B) - a_prime; + x[1] = -0.5 * (A + B) - a_prime; + x[2] = 0.5 * std::sqrt(3.) * (A - B); // 2 real roots - if(std::fabs(x[2]) &real_roots) { - double a3 = -b; - double b3 = a*c -4.*d; - double c3 = -a*a*d - c*c + 4.*b*d; +// Attention - this function returns dynamically allocated array. It has to be +// released afterwards. +void quartic_solve( + double a, double b, double c, double d, std::array& real_roots) +{ + double a3 = -b; + double b3 = a * c - 4. * d; + double c3 = -a * a * d - c * c + 4. * b * d; // cubic resolvent // y^3 − b*y^2 + (ac−4d)*y − a^2*d−c^2+4*b*d = 0 - std::array cube_roots; + std::array cube_roots; unsigned int num_roots = solve_cubic(a3, b3, c3, cube_roots); - + double q1, q2, p1, p2, D, sqD, y; - + y = cube_roots[0]; // The essence - choosing Y with maximal absolute value. - if(num_roots != 1) - { - if(std::fabs(cube_roots[1]) > std::fabs(y)) y = cube_roots[1]; - if(std::fabs(cube_roots[2]) > std::fabs(y)) y = cube_roots[2]; + if (num_roots != 1) { + if (std::fabs(cube_roots[1]) > std::fabs(y)) + y = cube_roots[1]; + if (std::fabs(cube_roots[2]) > std::fabs(y)) + y = cube_roots[2]; } - + // h1+h2 = y && h1*h2 = d <=> h^2 -y*h + d = 0 (h === q) - - D = y*y - 4*d; - if(std::fabs(D) < eps) //in other words - D==0 + + D = y * y - 4 * d; + if (std::fabs(D) < eps) // in other words - D==0 { - q1 = q2 = y * 0.5; - // g1+g2 = a && g1+g2 = b-y <=> g^2 - a*g + b-y = 0 (p === g) - D = a*a - 4*(b-y); - if(std::fabs(D) < eps) { - p1 = p2 = a * 0.5; - } - else - { - sqD = std::sqrt(D); - p1 = (a + sqD) * 0.5; - p2 = (a - sqD) * 0.5; - } + q1 = q2 = y * 0.5; + // g1+g2 = a && g1+g2 = b-y <=> g^2 - a*g + b-y = 0 (p === g) + D = a * a - 4 * (b - y); + if (std::fabs(D) < eps) { + p1 = p2 = a * 0.5; + } else { + sqD = std::sqrt(D); + p1 = (a + sqD) * 0.5; + p2 = (a - sqD) * 0.5; + } + } else { + sqD = std::sqrt(D); + q1 = (y + sqD) * 0.5; + q2 = (y - sqD) * 0.5; + p1 = (a * q1 - c) / (q1 - q2); + p2 = (c - a * q2) / (q1 - q2); } - else - { - sqD = std::sqrt(D); - q1 = (y + sqD) * 0.5; - q2 = (y - sqD) * 0.5; - p1 = (a*q1-c)/(q1-q2); - p2 = (c-a*q2)/(q1-q2); - } - - std::array,4> roots; //the roots to return - + + std::array, 4> roots; // the roots to return + // solving quadratic eq. - x^2 + p1*x + q1 = 0 - D = p1*p1 - 4*q1; - if(D < 0.0) - { - roots[0].real( -p1 * 0.5 ); - roots[0].imag( std::sqrt(-D) * 0.5 ); + D = p1 * p1 - 4 * q1; + if (D < 0.0) { + roots[0].real(-p1 * 0.5); + roots[0].imag(std::sqrt(-D) * 0.5); roots[1] = std::conj(roots[0]); - } - else - { + } else { sqD = std::sqrt(D); - roots[0].real( (-p1 + sqD) * 0.5 ); - roots[1].real( (-p1 - sqD) * 0.5 ); + roots[0].real((-p1 + sqD) * 0.5); + roots[1].real((-p1 - sqD) * 0.5); } - + // solving quadratic eq. - x^2 + p2*x + q2 = 0 - D = p2*p2 - 4*q2; - if(D < 0.0) - { - roots[2].real( -p2 * 0.5 ); - roots[2].imag( std::sqrt(-D) * 0.5 ); + D = p2 * p2 - 4 * q2; + if (D < 0.0) { + roots[2].real(-p2 * 0.5); + roots[2].imag(std::sqrt(-D) * 0.5); roots[3] = std::conj(roots[2]); - } - else - { + } else { sqD = std::sqrt(D); - roots[2].real( (-p2 + sqD) * 0.5 ); - roots[3].real( (-p2 - sqD) * 0.5 ); + roots[2].real((-p2 + sqD) * 0.5); + roots[3].real((-p2 - sqD) * 0.5); } - - for ( int i = 0 ; i < 4 ; i++ ) { - if (roots[i].imag() == 0. ) + + for (int i = 0; i < 4; i++) { + if (roots[i].imag() == 0.) real_roots[i] = roots[i].real(); else real_roots[i] = 0.; } - std::sort(real_roots.begin(),real_roots.end()); + std::sort(real_roots.begin(), real_roots.end()); return; } //============================================================================== -SurfaceXTorus::SurfaceXTorus(pugi::xml_node surf_node) - : CSGSurface(surf_node) +SurfaceXTorus::SurfaceXTorus(pugi::xml_node surf_node) : CSGSurface(surf_node) { read_coeffs(surf_node, id_, x0_, y0_, z0_, A_, B_, C_); } @@ -1179,31 +1177,30 @@ SurfaceXTorus::SurfaceXTorus(pugi::xml_node surf_node) void SurfaceXTorus::to_hdf5_inner(hid_t group_id) const { write_string(group_id, "type", "x-torus", false); - std::array coeffs {{x0_, y0_, z0_, A_, B_, C_ }}; + std::array coeffs {{x0_, y0_, z0_, A_, B_, C_}}; write_dataset(group_id, "coefficients", coeffs); } // todo should do some alebgra first to get rid of the sqrt -double -SurfaceXTorus::evaluate(Position r) const +double SurfaceXTorus::evaluate(Position r) const { const double x = r.x; const double y = r.y; const double z = r.z; - const double x_coeff2 = std::pow(x-x0_,2); - const double y_coeff2 = std::pow(y-y0_,2); - const double z_coeff2 = std::pow(z-z0_,2); + const double x_coeff2 = std::pow(x - x0_, 2); + const double y_coeff2 = std::pow(y - y0_, 2); + const double z_coeff2 = std::pow(z - z0_, 2); - const double B2 = B_*B_; - const double C2 = C_*C_; + const double B2 = B_ * B_; + const double C2 = C_ * C_; - return x_coeff2/B2 + std::pow(std::sqrt(y_coeff2 + z_coeff2)-A_,2)/C2 - 1.; + return x_coeff2 / B2 + std::pow(std::sqrt(y_coeff2 + z_coeff2) - A_, 2) / C2 - + 1.; } -double -SurfaceXTorus::distance(Position r, Direction ang, bool coincident) const +double SurfaceXTorus::distance(Position r, Direction ang, bool coincident) const { double u = ang.x; double v = ang.y; @@ -1220,74 +1217,119 @@ SurfaceXTorus::distance(Position r, Direction ang, bool coincident) const double x0 = x0_ + xr; double y0 = y0_ + yr; double z0 = z0_ + zr; - - double a,b,c,d,e; // coefficients of quartic + + double a, b, c, d, e; // coefficients of quartic // a is always 1 maybe save the maths? - a = std::pow(v,4) + 2*std::pow(v,2)*std::pow(w,2) + std::pow(w,4) + 2*std::pow(C,2)*std::pow(u,2)*std::pow(v,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(u,2)*std::pow(w,2)/std::pow(B,2) + std::pow(C,4)*std::pow(u,4)/std::pow(B,4); - b = 4*std::pow(v,3)*y0 + 4*std::pow(v,2)*w*z0 + 4*v*std::pow(w,2)*y0 + 4*std::pow(w,3)*z0 + 4*std::pow(C,2)*std::pow(u,2)*v*y0/std::pow(B,2) + 4*std::pow(C,2)*std::pow(u,2)*w*z0/std::pow(B,2) + 4*std::pow(C,2)*u*std::pow(v,2)*x0/std::pow(B,2) + 4*std::pow(C,2)*u*std::pow(w,2)*x0/std::pow(B,2) + 4*std::pow(C,4)*std::pow(u,3)*x0/std::pow(B,4); - c = -2*std::pow(A,2)*std::pow(v,2) - 2*std::pow(A,2)*std::pow(w,2) + 2*std::pow(A,2)*std::pow(C,2)*std::pow(u,2)/std::pow(B,2) - 2*std::pow(C,2)*std::pow(v,2) - 2*std::pow(C,2)*std::pow(w,2) + 6*std::pow(v,2)*std::pow(y0,2) + 2*std::pow(v,2)*std::pow(z0,2) + 8*v*w*y0*z0 + 2*std::pow(w,2)*std::pow(y0,2) + 6*std::pow(w,2)*std::pow(z0,2) - 2*std::pow(C,4)*std::pow(u,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(u,2)*std::pow(y0,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(u,2)*std::pow(z0,2)/std::pow(B,2) + 8*std::pow(C,2)*u*v*x0*y0/std::pow(B,2) + 8*std::pow(C,2)*u*w*x0*z0/std::pow(B,2) + 2*std::pow(C,2)*std::pow(v,2)*std::pow(x0,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(w,2)*std::pow(x0,2)/std::pow(B,2) + 6*std::pow(C,4)*std::pow(u,2)*std::pow(x0,2)/std::pow(B,4); - d = -4*std::pow(A,2)*v*y0 - 4*std::pow(A,2)*w*z0 + 4*std::pow(A,2)*std::pow(C,2)*u*x0/std::pow(B,2) - 4*std::pow(C,2)*v*y0 - 4*std::pow(C,2)*w*z0 + 4*v*std::pow(y0,3) + 4*v*y0*std::pow(z0,2) + 4*w*std::pow(y0,2)*z0 + 4*w*std::pow(z0,3) - 4*std::pow(C,4)*u*x0/std::pow(B,2) + 4*std::pow(C,2)*u*x0*std::pow(y0,2)/std::pow(B,2) + 4*std::pow(C,2)*u*x0*std::pow(z0,2)/std::pow(B,2) + 4*std::pow(C,2)*v*std::pow(x0,2)*y0/std::pow(B,2) + 4*std::pow(C,2)*w*std::pow(x0,2)*z0/std::pow(B,2) + 4*std::pow(C,4)*u*std::pow(x0,3)/std::pow(B,4); - e = std::pow(A,4) - 2*std::pow(A,2)*std::pow(C,2) - 2*std::pow(A,2)*std::pow(y0,2) - 2*std::pow(A,2)*std::pow(z0,2) + 2*std::pow(A,2)*std::pow(C,2)*std::pow(x0,2)/std::pow(B,2) + std::pow(C,4) - 2*std::pow(C,2)*std::pow(y0,2) - 2*std::pow(C,2)*std::pow(z0,2) + std::pow(y0,4) + 2*std::pow(y0,2)*std::pow(z0,2) + std::pow(z0,4) - 2*std::pow(C,4)*std::pow(x0,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(x0,2)*std::pow(y0,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(x0,2)*std::pow(z0,2)/std::pow(B,2) + std::pow(C,4)*std::pow(x0,4)/std::pow(B,4); + a = std::pow(v, 4) + 2 * std::pow(v, 2) * std::pow(w, 2) + std::pow(w, 4) + + 2 * std::pow(C, 2) * std::pow(u, 2) * std::pow(v, 2) / std::pow(B, 2) + + 2 * std::pow(C, 2) * std::pow(u, 2) * std::pow(w, 2) / std::pow(B, 2) + + std::pow(C, 4) * std::pow(u, 4) / std::pow(B, 4); + b = 4 * std::pow(v, 3) * y0 + 4 * std::pow(v, 2) * w * z0 + + 4 * v * std::pow(w, 2) * y0 + 4 * std::pow(w, 3) * z0 + + 4 * std::pow(C, 2) * std::pow(u, 2) * v * y0 / std::pow(B, 2) + + 4 * std::pow(C, 2) * std::pow(u, 2) * w * z0 / std::pow(B, 2) + + 4 * std::pow(C, 2) * u * std::pow(v, 2) * x0 / std::pow(B, 2) + + 4 * std::pow(C, 2) * u * std::pow(w, 2) * x0 / std::pow(B, 2) + + 4 * std::pow(C, 4) * std::pow(u, 3) * x0 / std::pow(B, 4); + c = + -2 * std::pow(A, 2) * std::pow(v, 2) - 2 * std::pow(A, 2) * std::pow(w, 2) + + 2 * std::pow(A, 2) * std::pow(C, 2) * std::pow(u, 2) / std::pow(B, 2) - + 2 * std::pow(C, 2) * std::pow(v, 2) - 2 * std::pow(C, 2) * std::pow(w, 2) + + 6 * std::pow(v, 2) * std::pow(y0, 2) + + 2 * std::pow(v, 2) * std::pow(z0, 2) + 8 * v * w * y0 * z0 + + 2 * std::pow(w, 2) * std::pow(y0, 2) + + 6 * std::pow(w, 2) * std::pow(z0, 2) - + 2 * std::pow(C, 4) * std::pow(u, 2) / std::pow(B, 2) + + 2 * std::pow(C, 2) * std::pow(u, 2) * std::pow(y0, 2) / std::pow(B, 2) + + 2 * std::pow(C, 2) * std::pow(u, 2) * std::pow(z0, 2) / std::pow(B, 2) + + 8 * std::pow(C, 2) * u * v * x0 * y0 / std::pow(B, 2) + + 8 * std::pow(C, 2) * u * w * x0 * z0 / std::pow(B, 2) + + 2 * std::pow(C, 2) * std::pow(v, 2) * std::pow(x0, 2) / std::pow(B, 2) + + 2 * std::pow(C, 2) * std::pow(w, 2) * std::pow(x0, 2) / std::pow(B, 2) + + 6 * std::pow(C, 4) * std::pow(u, 2) * std::pow(x0, 2) / std::pow(B, 4); + d = -4 * std::pow(A, 2) * v * y0 - 4 * std::pow(A, 2) * w * z0 + + 4 * std::pow(A, 2) * std::pow(C, 2) * u * x0 / std::pow(B, 2) - + 4 * std::pow(C, 2) * v * y0 - 4 * std::pow(C, 2) * w * z0 + + 4 * v * std::pow(y0, 3) + 4 * v * y0 * std::pow(z0, 2) + + 4 * w * std::pow(y0, 2) * z0 + 4 * w * std::pow(z0, 3) - + 4 * std::pow(C, 4) * u * x0 / std::pow(B, 2) + + 4 * std::pow(C, 2) * u * x0 * std::pow(y0, 2) / std::pow(B, 2) + + 4 * std::pow(C, 2) * u * x0 * std::pow(z0, 2) / std::pow(B, 2) + + 4 * std::pow(C, 2) * v * std::pow(x0, 2) * y0 / std::pow(B, 2) + + 4 * std::pow(C, 2) * w * std::pow(x0, 2) * z0 / std::pow(B, 2) + + 4 * std::pow(C, 4) * u * std::pow(x0, 3) / std::pow(B, 4); + e = std::pow(A, 4) - 2 * std::pow(A, 2) * std::pow(C, 2) - + 2 * std::pow(A, 2) * std::pow(y0, 2) - + 2 * std::pow(A, 2) * std::pow(z0, 2) + + 2 * std::pow(A, 2) * std::pow(C, 2) * std::pow(x0, 2) / std::pow(B, 2) + + std::pow(C, 4) - 2 * std::pow(C, 2) * std::pow(y0, 2) - + 2 * std::pow(C, 2) * std::pow(z0, 2) + std::pow(y0, 4) + + 2 * std::pow(y0, 2) * std::pow(z0, 2) + std::pow(z0, 4) - + 2 * std::pow(C, 4) * std::pow(x0, 2) / std::pow(B, 2) + + 2 * std::pow(C, 2) * std::pow(x0, 2) * std::pow(y0, 2) / std::pow(B, 2) + + 2 * std::pow(C, 2) * std::pow(x0, 2) * std::pow(z0, 2) / std::pow(B, 2) + + std::pow(C, 4) * std::pow(x0, 4) / std::pow(B, 4); // - std::array roots; - quartic_solve(b,c,d,e,roots); - - if (coincident) r += ang*TINY_BIT; + std::array roots; + quartic_solve(b, c, d, e, roots); + + if (coincident) + r += ang * TINY_BIT; // special degerenate case two sets of repated // roots - if ( b == 0.0 && d == 0) { - if ( roots[1] - roots[0] < 1e-5 ) return INFTY; - if ( roots[3] - roots[2] < 1e-5 ) return INFTY; + if (b == 0.0 && d == 0) { + if (roots[1] - roots[0] < 1e-5) + return INFTY; + if (roots[3] - roots[2] < 1e-5) + return INFTY; } - for ( int i = 0 ; i < 4 ; i++ ) { + for (int i = 0; i < 4; i++) { // need something better than just raw tolerance // use fastQS to get back lost precision - if ( roots[i] > 1e-6) { + if (roots[i] > 1e-6) { return roots[i]; - } + } } // otherwise no hit return INFTY; } -Direction -SurfaceXTorus::normal(Position r) const +Direction SurfaceXTorus::normal(Position r) const { // reduce the expansion of the full form for torus double x = r.x - x0_; double y = r.y - y0_; double z = r.z - z0_; - double A2 = A_*A_; - double B2 = B_*B_; - double C2 = C_*C_; + double A2 = A_ * A_; + double B2 = B_ * B_; + double C2 = C_ * C_; // coefficients for the x, x^2, x^3 and x^4 term - // but since we differeniate - we get x4 -> 4x3 - double dx_4 = 4*x*x*x*C2*C2/(B2*B2); - double dx_2 = 4*x*C2/B2*(z*z + 2*y*y - 2*C2 + 2*A2); + // but since we differeniate - we get x4 -> 4x3 + double dx_4 = 4 * x * x * x * C2 * C2 / (B2 * B2); + double dx_2 = 4 * x * C2 / B2 * (z * z + 2 * y * y - 2 * C2 + 2 * A2); double dx = dx_4 + dx_2; // similarly to y - double dy_4 = 4*y*y*y; - double dy_2 = 4*y*(z*z + C2*x*x/B2 - A2 - C2); + double dy_4 = 4 * y * y * y; + double dy_2 = 4 * y * (z * z + C2 * x * x / B2 - A2 - C2); double dy = dy_2 + dy_4; // and z - double dz_4 = 4*z*z*z; - double dz_2 = 4*z*(C2*x*x/B2 + y*y - C2 - A2); + double dz_4 = 4 * z * z * z; + double dz_2 = 4 * z * (C2 * x * x / B2 + y * y - C2 - A2); double dz = dz_2 + dz_4; - double length = std::sqrt(dx*dx + dy*dy + dz*dz); + double length = std::sqrt(dx * dx + dy * dy + dz * dz); - return {dx/length,dy/length,dz/length}; + return {dx / length, dy / length, dz / length}; } -SurfaceYTorus::SurfaceYTorus(pugi::xml_node surf_node) - : CSGSurface(surf_node) +SurfaceYTorus::SurfaceYTorus(pugi::xml_node surf_node) : CSGSurface(surf_node) { read_coeffs(surf_node, id_, x0_, y0_, z0_, A_, B_, C_); } @@ -1295,29 +1337,28 @@ SurfaceYTorus::SurfaceYTorus(pugi::xml_node surf_node) void SurfaceYTorus::to_hdf5_inner(hid_t group_id) const { write_string(group_id, "type", "y-torus", false); - std::array coeffs {{x0_, y0_, z0_, A_, B_, C_ }}; + std::array coeffs {{x0_, y0_, z0_, A_, B_, C_}}; write_dataset(group_id, "coefficients", coeffs); } -double -SurfaceYTorus::evaluate(Position r) const +double SurfaceYTorus::evaluate(Position r) const { const double x = r.x; const double y = r.y; const double z = r.z; - const double x_coeff2 = std::pow(x-x0_,2); - const double y_coeff2 = std::pow(y-y0_,2); - const double z_coeff2 = std::pow(z-z0_,2); + const double x_coeff2 = std::pow(x - x0_, 2); + const double y_coeff2 = std::pow(y - y0_, 2); + const double z_coeff2 = std::pow(z - z0_, 2); - const double B2 = B_*B_; - const double C2 = C_*C_; + const double B2 = B_ * B_; + const double C2 = C_ * C_; - return y_coeff2/B2 + std::pow(std::sqrt(x_coeff2 + z_coeff2)-A_,2)/C2 - 1.; + return y_coeff2 / B2 + std::pow(std::sqrt(x_coeff2 + z_coeff2) - A_, 2) / C2 - + 1.; } -double -SurfaceYTorus::distance(Position r, Direction ang, bool coincident) const +double SurfaceYTorus::distance(Position r, Direction ang, bool coincident) const { double u = ang.x; double v = ang.y; @@ -1334,33 +1375,80 @@ SurfaceYTorus::distance(Position r, Direction ang, bool coincident) const double x0 = x0_ + xr; double y0 = y0_ + yr; double z0 = z0_ + zr; - - double a,b,c,d,e; // coefficients of quartic - a = std::pow(u,4) + 2*std::pow(u,2)*std::pow(w,2) + std::pow(w,4) + 2*std::pow(C,2)*std::pow(u,2)*std::pow(v,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(v,2)*std::pow(w,2)/std::pow(B,2) + std::pow(C,4)*std::pow(v,4)/std::pow(B,4); - b = 4*std::pow(u,3)*x0 + 4*std::pow(u,2)*w*z0 + 4*u*std::pow(w,2)*x0 + 4*std::pow(w,3)*z0 + 4*std::pow(C,2)*std::pow(u,2)*v*y0/std::pow(B,2) + 4*std::pow(C,2)*u*std::pow(v,2)*x0/std::pow(B,2) + 4*std::pow(C,2)*std::pow(v,2)*w*z0/std::pow(B,2) + 4*std::pow(C,2)*v*std::pow(w,2)*y0/std::pow(B,2) + 4*std::pow(C,4)*std::pow(v,3)*y0/std::pow(B,4); - c = -2*std::pow(A,2)*std::pow(u,2) - 2*std::pow(A,2)*std::pow(w,2) + 2*std::pow(A,2)*std::pow(C,2)*std::pow(v,2)/std::pow(B,2) - 2*std::pow(C,2)*std::pow(u,2) - 2*std::pow(C,2)*std::pow(w,2) + 6*std::pow(u,2)*std::pow(x0,2) + 2*std::pow(u,2)*std::pow(z0,2) + 8*u*w*x0*z0 + 2*std::pow(w,2)*std::pow(x0,2) + 6*std::pow(w,2)*std::pow(z0,2) - 2*std::pow(C,4)*std::pow(v,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(u,2)*std::pow(y0,2)/std::pow(B,2) + 8*std::pow(C,2)*u*v*x0*y0/std::pow(B,2) + 2*std::pow(C,2)*std::pow(v,2)*std::pow(x0,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(v,2)*std::pow(z0,2)/std::pow(B,2) + 8*std::pow(C,2)*v*w*y0*z0/std::pow(B,2) + 2*std::pow(C,2)*std::pow(w,2)*std::pow(y0,2)/std::pow(B,2) + 6*std::pow(C,4)*std::pow(v,2)*std::pow(y0,2)/std::pow(B,4); - d = -4*std::pow(A,2)*u*x0 - 4*std::pow(A,2)*w*z0 + 4*std::pow(A,2)*std::pow(C,2)*v*y0/std::pow(B,2) - 4*std::pow(C,2)*u*x0 - 4*std::pow(C,2)*w*z0 + 4*u*std::pow(x0,3) + 4*u*x0*std::pow(z0,2) + 4*w*std::pow(x0,2)*z0 + 4*w*std::pow(z0,3) - 4*std::pow(C,4)*v*y0/std::pow(B,2) + 4*std::pow(C,2)*u*x0*std::pow(y0,2)/std::pow(B,2) + 4*std::pow(C,2)*v*std::pow(x0,2)*y0/std::pow(B,2) + 4*std::pow(C,2)*v*y0*std::pow(z0,2)/std::pow(B,2) + 4*std::pow(C,2)*w*std::pow(y0,2)*z0/std::pow(B,2) + 4*std::pow(C,4)*v*std::pow(y0,3)/std::pow(B,4); - e = std::pow(A,4) - 2*std::pow(A,2)*std::pow(C,2) - 2*std::pow(A,2)*std::pow(x0,2) - 2*std::pow(A,2)*std::pow(z0,2) + 2*std::pow(A,2)*std::pow(C,2)*std::pow(y0,2)/std::pow(B,2) + std::pow(C,4) - 2*std::pow(C,2)*std::pow(x0,2) - 2*std::pow(C,2)*std::pow(z0,2) + std::pow(x0,4) + 2*std::pow(x0,2)*std::pow(z0,2) + std::pow(z0,4) - 2*std::pow(C,4)*std::pow(y0,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(x0,2)*std::pow(y0,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(y0,2)*std::pow(z0,2)/std::pow(B,2) + std::pow(C,4)*std::pow(y0,4)/std::pow(B,4); + double a, b, c, d, e; // coefficients of quartic + + a = std::pow(u, 4) + 2 * std::pow(u, 2) * std::pow(w, 2) + std::pow(w, 4) + + 2 * std::pow(C, 2) * std::pow(u, 2) * std::pow(v, 2) / std::pow(B, 2) + + 2 * std::pow(C, 2) * std::pow(v, 2) * std::pow(w, 2) / std::pow(B, 2) + + std::pow(C, 4) * std::pow(v, 4) / std::pow(B, 4); + b = 4 * std::pow(u, 3) * x0 + 4 * std::pow(u, 2) * w * z0 + + 4 * u * std::pow(w, 2) * x0 + 4 * std::pow(w, 3) * z0 + + 4 * std::pow(C, 2) * std::pow(u, 2) * v * y0 / std::pow(B, 2) + + 4 * std::pow(C, 2) * u * std::pow(v, 2) * x0 / std::pow(B, 2) + + 4 * std::pow(C, 2) * std::pow(v, 2) * w * z0 / std::pow(B, 2) + + 4 * std::pow(C, 2) * v * std::pow(w, 2) * y0 / std::pow(B, 2) + + 4 * std::pow(C, 4) * std::pow(v, 3) * y0 / std::pow(B, 4); + c = + -2 * std::pow(A, 2) * std::pow(u, 2) - 2 * std::pow(A, 2) * std::pow(w, 2) + + 2 * std::pow(A, 2) * std::pow(C, 2) * std::pow(v, 2) / std::pow(B, 2) - + 2 * std::pow(C, 2) * std::pow(u, 2) - 2 * std::pow(C, 2) * std::pow(w, 2) + + 6 * std::pow(u, 2) * std::pow(x0, 2) + + 2 * std::pow(u, 2) * std::pow(z0, 2) + 8 * u * w * x0 * z0 + + 2 * std::pow(w, 2) * std::pow(x0, 2) + + 6 * std::pow(w, 2) * std::pow(z0, 2) - + 2 * std::pow(C, 4) * std::pow(v, 2) / std::pow(B, 2) + + 2 * std::pow(C, 2) * std::pow(u, 2) * std::pow(y0, 2) / std::pow(B, 2) + + 8 * std::pow(C, 2) * u * v * x0 * y0 / std::pow(B, 2) + + 2 * std::pow(C, 2) * std::pow(v, 2) * std::pow(x0, 2) / std::pow(B, 2) + + 2 * std::pow(C, 2) * std::pow(v, 2) * std::pow(z0, 2) / std::pow(B, 2) + + 8 * std::pow(C, 2) * v * w * y0 * z0 / std::pow(B, 2) + + 2 * std::pow(C, 2) * std::pow(w, 2) * std::pow(y0, 2) / std::pow(B, 2) + + 6 * std::pow(C, 4) * std::pow(v, 2) * std::pow(y0, 2) / std::pow(B, 4); + d = -4 * std::pow(A, 2) * u * x0 - 4 * std::pow(A, 2) * w * z0 + + 4 * std::pow(A, 2) * std::pow(C, 2) * v * y0 / std::pow(B, 2) - + 4 * std::pow(C, 2) * u * x0 - 4 * std::pow(C, 2) * w * z0 + + 4 * u * std::pow(x0, 3) + 4 * u * x0 * std::pow(z0, 2) + + 4 * w * std::pow(x0, 2) * z0 + 4 * w * std::pow(z0, 3) - + 4 * std::pow(C, 4) * v * y0 / std::pow(B, 2) + + 4 * std::pow(C, 2) * u * x0 * std::pow(y0, 2) / std::pow(B, 2) + + 4 * std::pow(C, 2) * v * std::pow(x0, 2) * y0 / std::pow(B, 2) + + 4 * std::pow(C, 2) * v * y0 * std::pow(z0, 2) / std::pow(B, 2) + + 4 * std::pow(C, 2) * w * std::pow(y0, 2) * z0 / std::pow(B, 2) + + 4 * std::pow(C, 4) * v * std::pow(y0, 3) / std::pow(B, 4); + e = std::pow(A, 4) - 2 * std::pow(A, 2) * std::pow(C, 2) - + 2 * std::pow(A, 2) * std::pow(x0, 2) - + 2 * std::pow(A, 2) * std::pow(z0, 2) + + 2 * std::pow(A, 2) * std::pow(C, 2) * std::pow(y0, 2) / std::pow(B, 2) + + std::pow(C, 4) - 2 * std::pow(C, 2) * std::pow(x0, 2) - + 2 * std::pow(C, 2) * std::pow(z0, 2) + std::pow(x0, 4) + + 2 * std::pow(x0, 2) * std::pow(z0, 2) + std::pow(z0, 4) - + 2 * std::pow(C, 4) * std::pow(y0, 2) / std::pow(B, 2) + + 2 * std::pow(C, 2) * std::pow(x0, 2) * std::pow(y0, 2) / std::pow(B, 2) + + 2 * std::pow(C, 2) * std::pow(y0, 2) * std::pow(z0, 2) / std::pow(B, 2) + + std::pow(C, 4) * std::pow(y0, 4) / std::pow(B, 4); // - std::array roots; - quartic_solve(b,c,d,e,roots); - - if (coincident) r += ang*TINY_BIT; + std::array roots; + quartic_solve(b, c, d, e, roots); + + if (coincident) + r += ang * TINY_BIT; // special degerenate case two sets of repated - if ( b == 0.0 && d == 0) { - if ( roots[1] - roots[0] < 1e-5 ) return INFTY; - if ( roots[3] - roots[2] < 1e-5 ) return INFTY; + if (b == 0.0 && d == 0) { + if (roots[1] - roots[0] < 1e-5) + return INFTY; + if (roots[3] - roots[2] < 1e-5) + return INFTY; } - for ( int i = 0 ; i < 4 ; i++ ) { + for (int i = 0; i < 4; i++) { // need something better than just raw tolerance // use fastQS to get back lost precision - if ( roots[i] > 1e-6) { + if (roots[i] > 1e-6) { return roots[i]; - } + } } // otherwise no hit @@ -1369,44 +1457,42 @@ SurfaceYTorus::distance(Position r, Direction ang, bool coincident) const return 0; } -Direction -SurfaceYTorus::normal(Position r) const +Direction SurfaceYTorus::normal(Position r) const { // reduce the expansion of the full form for torus double x = r.x - x0_; double y = r.y - y0_; double z = r.z - z0_; - double A2 = A_*A_; - double B2 = B_*B_; - double C2 = C_*C_; - - // a**4 - 2*a**2*c**2 - 2*a**2*x**2 - 2*a**2*z**2 + 2*a**2*c**2*y**2/b**2 + c**4 - // - 2*c**2*x**2 - 2*c**2*z**2 + x**4 + 2*x**2*z**2 + z**4 - 2*c**4*y**2/b**2 + - // 2*c**2*x**2*y**2/b**2 + 2*c**2*y**2*z**2/b**2 + c**4*y**4/b**4 + double A2 = A_ * A_; + double B2 = B_ * B_; + double C2 = C_ * C_; + // a**4 - 2*a**2*c**2 - 2*a**2*x**2 - 2*a**2*z**2 + 2*a**2*c**2*y**2/b**2 + + // c**4 + // - 2*c**2*x**2 - 2*c**2*z**2 + x**4 + 2*x**2*z**2 + z**4 - 2*c**4*y**2/b**2 + // + 2*c**2*x**2*y**2/b**2 + 2*c**2*y**2*z**2/b**2 + c**4*y**4/b**4 // coefficients for the x, x^2, x^3 and x^4 term - // but since we differeniate - we get x4 -> 4x3 - double dx_4 = 4*x*x*x; - double dx_2 = 4*x*(z*z + C2*y*y/B2 - A2 - C2); + // but since we differeniate - we get x4 -> 4x3 + double dx_4 = 4 * x * x * x; + double dx_2 = 4 * x * (z * z + C2 * y * y / B2 - A2 - C2); double dx = dx_4 + dx_2; // similarly to y - double dy_4 = 4*y*y*y*C2*C2/(B2*B2); - double dy_2 = 4*y*(z*z + C2*x*x/B2 - A2 - C2); + double dy_4 = 4 * y * y * y * C2 * C2 / (B2 * B2); + double dy_2 = 4 * y * (z * z + C2 * x * x / B2 - A2 - C2); double dy = dy_2 + dy_4; // and z - double dz_4 = 4*z*z*z; - double dz_2 = 4*z*(C2*y*y/B2 + x*x - C2 - A2); + double dz_4 = 4 * z * z * z; + double dz_2 = 4 * z * (C2 * y * y / B2 + x * x - C2 - A2); double dz = dz_2 + dz_4; - double length = std::sqrt(dx*dx + dy*dy + dz*dz); + double length = std::sqrt(dx * dx + dy * dy + dz * dz); - return {dx/length,dy/length,dz/length}; + return {dx / length, dy / length, dz / length}; } -SurfaceZTorus::SurfaceZTorus(pugi::xml_node surf_node) - : CSGSurface(surf_node) +SurfaceZTorus::SurfaceZTorus(pugi::xml_node surf_node) : CSGSurface(surf_node) { read_coeffs(surf_node, id_, x0_, y0_, z0_, A_, B_, C_); } @@ -1414,29 +1500,28 @@ SurfaceZTorus::SurfaceZTorus(pugi::xml_node surf_node) void SurfaceZTorus::to_hdf5_inner(hid_t group_id) const { write_string(group_id, "type", "z-torus", false); - std::array coeffs {{x0_, y0_, z0_, A_, B_, C_ }}; + std::array coeffs {{x0_, y0_, z0_, A_, B_, C_}}; write_dataset(group_id, "coefficients", coeffs); } -double -SurfaceZTorus::evaluate(Position r) const +double SurfaceZTorus::evaluate(Position r) const { const double x = r.x; const double y = r.y; const double z = r.z; - const double x_coeff2 = std::pow(x-x0_,2); - const double y_coeff2 = std::pow(y-y0_,2); - const double z_coeff2 = std::pow(z-z0_,2); + const double x_coeff2 = std::pow(x - x0_, 2); + const double y_coeff2 = std::pow(y - y0_, 2); + const double z_coeff2 = std::pow(z - z0_, 2); - const double B2 = B_*B_; - const double C2 = C_*C_; + const double B2 = B_ * B_; + const double C2 = C_ * C_; - return z_coeff2/B2 + std::pow(std::sqrt(x_coeff2 + y_coeff2)-A_,2)/C2 - 1.; + return z_coeff2 / B2 + std::pow(std::sqrt(x_coeff2 + y_coeff2) - A_, 2) / C2 - + 1.; } -double -SurfaceZTorus::distance(Position r, Direction ang, bool coincident) const +double SurfaceZTorus::distance(Position r, Direction ang, bool coincident) const { double u = ang.x; double v = ang.y; @@ -1453,79 +1538,222 @@ SurfaceZTorus::distance(Position r, Direction ang, bool coincident) const double x0 = x0_; double y0 = y0_; double z0 = z0_; - - double a,b,c,d,e; // coefficients of quartic + + double a, b, c, d, e; // coefficients of quartic // 4th order coefficient - a = std::pow(u,4) + 2*std::pow(u,2)*std::pow(v,2) + std::pow(v,4) + 2*std::pow(C,2)*std::pow(u,2)*std::pow(w,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(v,2)*std::pow(w,2)/std::pow(B,2) + std::pow(C,4)*std::pow(w,4)/std::pow(B,4); + a = std::pow(u, 4) + 2 * std::pow(u, 2) * std::pow(v, 2) + std::pow(v, 4) + + 2 * std::pow(C, 2) * std::pow(u, 2) * std::pow(w, 2) / std::pow(B, 2) + + 2 * std::pow(C, 2) * std::pow(v, 2) * std::pow(w, 2) / std::pow(B, 2) + + std::pow(C, 4) * std::pow(w, 4) / std::pow(B, 4); // 3rd order coefficient - b = -4*std::pow(u,3)*x0 + 4*std::pow(u,3)*xr - 4*std::pow(u,2)*v*y0 + 4*std::pow(u,2)*v*yr - 4*u*std::pow(v,2)*x0 + 4*u*std::pow(v,2)*xr - 4*std::pow(v,3)*y0 + 4*std::pow(v,3)*yr - 4*std::pow(C,2)*std::pow(u,2)*w*z0/std::pow(B,2) + 4*std::pow(C,2)*std::pow(u,2)*w*zr/std::pow(B,2) - 4*std::pow(C,2)*u*std::pow(w,2)*x0/std::pow(B,2) + 4*std::pow(C,2)*u*std::pow(w,2)*xr/std::pow(B,2) - 4*std::pow(C,2)*std::pow(v,2)*w*z0/std::pow(B,2) + 4*std::pow(C,2)*std::pow(v,2)*w*zr/std::pow(B,2) - 4*std::pow(C,2)*v*std::pow(w,2)*y0/std::pow(B,2) + 4*std::pow(C,2)*v*std::pow(w,2)*yr/std::pow(B,2) - 4*std::pow(C,4)*std::pow(w,3)*z0/std::pow(B,4) + 4*std::pow(C,4)*std::pow(w,3)*zr/std::pow(B,4); + b = -4 * std::pow(u, 3) * x0 + 4 * std::pow(u, 3) * xr - + 4 * std::pow(u, 2) * v * y0 + 4 * std::pow(u, 2) * v * yr - + 4 * u * std::pow(v, 2) * x0 + 4 * u * std::pow(v, 2) * xr - + 4 * std::pow(v, 3) * y0 + 4 * std::pow(v, 3) * yr - + 4 * std::pow(C, 2) * std::pow(u, 2) * w * z0 / std::pow(B, 2) + + 4 * std::pow(C, 2) * std::pow(u, 2) * w * zr / std::pow(B, 2) - + 4 * std::pow(C, 2) * u * std::pow(w, 2) * x0 / std::pow(B, 2) + + 4 * std::pow(C, 2) * u * std::pow(w, 2) * xr / std::pow(B, 2) - + 4 * std::pow(C, 2) * std::pow(v, 2) * w * z0 / std::pow(B, 2) + + 4 * std::pow(C, 2) * std::pow(v, 2) * w * zr / std::pow(B, 2) - + 4 * std::pow(C, 2) * v * std::pow(w, 2) * y0 / std::pow(B, 2) + + 4 * std::pow(C, 2) * v * std::pow(w, 2) * yr / std::pow(B, 2) - + 4 * std::pow(C, 4) * std::pow(w, 3) * z0 / std::pow(B, 4) + + 4 * std::pow(C, 4) * std::pow(w, 3) * zr / std::pow(B, 4); // 2nd order coefficient - c= -2*std::pow(A,2)*std::pow(u,2) - 2*std::pow(A,2)*std::pow(v,2) + 2*std::pow(A,2)*std::pow(C,2)*std::pow(w,2)/std::pow(B,2) - 2*std::pow(C,2)*std::pow(u,2) - 2*std::pow(C,2)*std::pow(v,2) + 6*std::pow(u,2)*std::pow(x0,2) - 12*std::pow(u,2)*x0*xr + 6*std::pow(u,2)*std::pow(xr,2) + 2*std::pow(u,2)*std::pow(y0,2) - 4*std::pow(u,2)*y0*yr + 2*std::pow(u,2)*std::pow(yr,2) + 8*u*v*x0*y0 - 8*u*v*x0*yr - 8*u*v*xr*y0 + 8*u*v*xr*yr + 2*std::pow(v,2)*std::pow(x0,2) - 4*std::pow(v,2)*x0*xr + 2*std::pow(v,2)*std::pow(xr,2) + 6*std::pow(v,2)*std::pow(y0,2) - 12*std::pow(v,2)*y0*yr + 6*std::pow(v,2)*std::pow(yr,2) - 2*std::pow(C,4)*std::pow(w,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(u,2)*std::pow(z0,2)/std::pow(B,2) - 4*std::pow(C,2)*std::pow(u,2)*z0*zr/std::pow(B,2) + 2*std::pow(C,2)*std::pow(u,2)*std::pow(zr,2)/std::pow(B,2) + 8*std::pow(C,2)*u*w*x0*z0/std::pow(B,2) - 8*std::pow(C,2)*u*w*x0*zr/std::pow(B,2) - 8*std::pow(C,2)*u*w*xr*z0/std::pow(B,2) + 8*std::pow(C,2)*u*w*xr*zr/std::pow(B,2) + 2*std::pow(C,2)*std::pow(v,2)*std::pow(z0,2)/std::pow(B,2) - 4*std::pow(C,2)*std::pow(v,2)*z0*zr/std::pow(B,2) + 2*std::pow(C,2)*std::pow(v,2)*std::pow(zr,2)/std::pow(B,2) + 8*std::pow(C,2)*v*w*y0*z0/std::pow(B,2) - 8*std::pow(C,2)*v*w*y0*zr/std::pow(B,2) - 8*std::pow(C,2)*v*w*yr*z0/std::pow(B,2) + 8*std::pow(C,2)*v*w*yr*zr/std::pow(B,2) + 2*std::pow(C,2)*std::pow(w,2)*std::pow(x0,2)/std::pow(B,2) - 4*std::pow(C,2)*std::pow(w,2)*x0*xr/std::pow(B,2) + 2*std::pow(C,2)*std::pow(w,2)*std::pow(xr,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(w,2)*std::pow(y0,2)/std::pow(B,2) - 4*std::pow(C,2)*std::pow(w,2)*y0*yr/std::pow(B,2) + 2*std::pow(C,2)*std::pow(w,2)*std::pow(yr,2)/std::pow(B,2) + 6*std::pow(C,4)*std::pow(w,2)*std::pow(z0,2)/std::pow(B,4) - 12*std::pow(C,4)*std::pow(w,2)*z0*zr/std::pow(B,4) + 6*std::pow(C,4)*std::pow(w,2)*std::pow(zr,2)/std::pow(B,4); + c = + -2 * std::pow(A, 2) * std::pow(u, 2) - 2 * std::pow(A, 2) * std::pow(v, 2) + + 2 * std::pow(A, 2) * std::pow(C, 2) * std::pow(w, 2) / std::pow(B, 2) - + 2 * std::pow(C, 2) * std::pow(u, 2) - 2 * std::pow(C, 2) * std::pow(v, 2) + + 6 * std::pow(u, 2) * std::pow(x0, 2) - 12 * std::pow(u, 2) * x0 * xr + + 6 * std::pow(u, 2) * std::pow(xr, 2) + + 2 * std::pow(u, 2) * std::pow(y0, 2) - 4 * std::pow(u, 2) * y0 * yr + + 2 * std::pow(u, 2) * std::pow(yr, 2) + 8 * u * v * x0 * y0 - + 8 * u * v * x0 * yr - 8 * u * v * xr * y0 + 8 * u * v * xr * yr + + 2 * std::pow(v, 2) * std::pow(x0, 2) - 4 * std::pow(v, 2) * x0 * xr + + 2 * std::pow(v, 2) * std::pow(xr, 2) + + 6 * std::pow(v, 2) * std::pow(y0, 2) - 12 * std::pow(v, 2) * y0 * yr + + 6 * std::pow(v, 2) * std::pow(yr, 2) - + 2 * std::pow(C, 4) * std::pow(w, 2) / std::pow(B, 2) + + 2 * std::pow(C, 2) * std::pow(u, 2) * std::pow(z0, 2) / std::pow(B, 2) - + 4 * std::pow(C, 2) * std::pow(u, 2) * z0 * zr / std::pow(B, 2) + + 2 * std::pow(C, 2) * std::pow(u, 2) * std::pow(zr, 2) / std::pow(B, 2) + + 8 * std::pow(C, 2) * u * w * x0 * z0 / std::pow(B, 2) - + 8 * std::pow(C, 2) * u * w * x0 * zr / std::pow(B, 2) - + 8 * std::pow(C, 2) * u * w * xr * z0 / std::pow(B, 2) + + 8 * std::pow(C, 2) * u * w * xr * zr / std::pow(B, 2) + + 2 * std::pow(C, 2) * std::pow(v, 2) * std::pow(z0, 2) / std::pow(B, 2) - + 4 * std::pow(C, 2) * std::pow(v, 2) * z0 * zr / std::pow(B, 2) + + 2 * std::pow(C, 2) * std::pow(v, 2) * std::pow(zr, 2) / std::pow(B, 2) + + 8 * std::pow(C, 2) * v * w * y0 * z0 / std::pow(B, 2) - + 8 * std::pow(C, 2) * v * w * y0 * zr / std::pow(B, 2) - + 8 * std::pow(C, 2) * v * w * yr * z0 / std::pow(B, 2) + + 8 * std::pow(C, 2) * v * w * yr * zr / std::pow(B, 2) + + 2 * std::pow(C, 2) * std::pow(w, 2) * std::pow(x0, 2) / std::pow(B, 2) - + 4 * std::pow(C, 2) * std::pow(w, 2) * x0 * xr / std::pow(B, 2) + + 2 * std::pow(C, 2) * std::pow(w, 2) * std::pow(xr, 2) / std::pow(B, 2) + + 2 * std::pow(C, 2) * std::pow(w, 2) * std::pow(y0, 2) / std::pow(B, 2) - + 4 * std::pow(C, 2) * std::pow(w, 2) * y0 * yr / std::pow(B, 2) + + 2 * std::pow(C, 2) * std::pow(w, 2) * std::pow(yr, 2) / std::pow(B, 2) + + 6 * std::pow(C, 4) * std::pow(w, 2) * std::pow(z0, 2) / std::pow(B, 4) - + 12 * std::pow(C, 4) * std::pow(w, 2) * z0 * zr / std::pow(B, 4) + + 6 * std::pow(C, 4) * std::pow(w, 2) * std::pow(zr, 2) / std::pow(B, 4); // the 1st order terms - d = 4*std::pow(A,2)*u*x0 - 4*std::pow(A,2)*u*xr + 4*std::pow(A,2)*v*y0 - 4*std::pow(A,2)*v*yr - 4*std::pow(A,2)*std::pow(C,2)*w*z0/std::pow(B,2) + 4*std::pow(A,2)*std::pow(C,2)*w*zr/std::pow(B,2) + 4*std::pow(C,2)*u*x0 - 4*std::pow(C,2)*u*xr + 4*std::pow(C,2)*v*y0 - 4*std::pow(C,2)*v*yr - 4*u*std::pow(x0,3) + 12*u*std::pow(x0,2)*xr - 12*u*x0*std::pow(xr,2) - 4*u*x0*std::pow(y0,2) + 8*u*x0*y0*yr - 4*u*x0*std::pow(yr,2) + 4*u*std::pow(xr,3) + 4*u*xr*std::pow(y0,2) - 8*u*xr*y0*yr + 4*u*xr*std::pow(yr,2) - 4*v*std::pow(x0,2)*y0 + 4*v*std::pow(x0,2)*yr + 8*v*x0*xr*y0 - 8*v*x0*xr*yr - 4*v*std::pow(xr,2)*y0 + 4*v*std::pow(xr,2)*yr - 4*v*std::pow(y0,3) + 12*v*std::pow(y0,2)*yr - 12*v*y0*std::pow(yr,2) + 4*v*std::pow(yr,3) + 4*std::pow(C,4)*w*z0/std::pow(B,2) - 4*std::pow(C,4)*w*zr/std::pow(B,2) - 4*std::pow(C,2)*u*x0*std::pow(z0,2)/std::pow(B,2) + 8*std::pow(C,2)*u*x0*z0*zr/std::pow(B,2) - 4*std::pow(C,2)*u*x0*std::pow(zr,2)/std::pow(B,2) + 4*std::pow(C,2)*u*xr*std::pow(z0,2)/std::pow(B,2) - 8*std::pow(C,2)*u*xr*z0*zr/std::pow(B,2) + 4*std::pow(C,2)*u*xr*std::pow(zr,2)/std::pow(B,2) - 4*std::pow(C,2)*v*y0*std::pow(z0,2)/std::pow(B,2) + 8*std::pow(C,2)*v*y0*z0*zr/std::pow(B,2) - 4*std::pow(C,2)*v*y0*std::pow(zr,2)/std::pow(B,2) + 4*std::pow(C,2)*v*yr*std::pow(z0,2)/std::pow(B,2) - 8*std::pow(C,2)*v*yr*z0*zr/std::pow(B,2) + 4*std::pow(C,2)*v*yr*std::pow(zr,2)/std::pow(B,2) - 4*std::pow(C,2)*w*std::pow(x0,2)*z0/std::pow(B,2) + 4*std::pow(C,2)*w*std::pow(x0,2)*zr/std::pow(B,2) + 8*std::pow(C,2)*w*x0*xr*z0/std::pow(B,2) - 8*std::pow(C,2)*w*x0*xr*zr/std::pow(B,2) - 4*std::pow(C,2)*w*std::pow(xr,2)*z0/std::pow(B,2) + 4*std::pow(C,2)*w*std::pow(xr,2)*zr/std::pow(B,2) - 4*std::pow(C,2)*w*std::pow(y0,2)*z0/std::pow(B,2) + 4*std::pow(C,2)*w*std::pow(y0,2)*zr/std::pow(B,2) + 8*std::pow(C,2)*w*y0*yr*z0/std::pow(B,2) - 8*std::pow(C,2)*w*y0*yr*zr/std::pow(B,2) - 4*std::pow(C,2)*w*std::pow(yr,2)*z0/std::pow(B,2) + 4*std::pow(C,2)*w*std::pow(yr,2)*zr/std::pow(B,2) - 4*std::pow(C,4)*w*std::pow(z0,3)/std::pow(B,4) + 12*std::pow(C,4)*w*std::pow(z0,2)*zr/std::pow(B,4) - 12*std::pow(C,4)*w*z0*std::pow(zr,2)/std::pow(B,4) + 4*std::pow(C,4)*w*std::pow(zr,3)/std::pow(B,4); + d = 4 * std::pow(A, 2) * u * x0 - 4 * std::pow(A, 2) * u * xr + + 4 * std::pow(A, 2) * v * y0 - 4 * std::pow(A, 2) * v * yr - + 4 * std::pow(A, 2) * std::pow(C, 2) * w * z0 / std::pow(B, 2) + + 4 * std::pow(A, 2) * std::pow(C, 2) * w * zr / std::pow(B, 2) + + 4 * std::pow(C, 2) * u * x0 - 4 * std::pow(C, 2) * u * xr + + 4 * std::pow(C, 2) * v * y0 - 4 * std::pow(C, 2) * v * yr - + 4 * u * std::pow(x0, 3) + 12 * u * std::pow(x0, 2) * xr - + 12 * u * x0 * std::pow(xr, 2) - 4 * u * x0 * std::pow(y0, 2) + + 8 * u * x0 * y0 * yr - 4 * u * x0 * std::pow(yr, 2) + + 4 * u * std::pow(xr, 3) + 4 * u * xr * std::pow(y0, 2) - + 8 * u * xr * y0 * yr + 4 * u * xr * std::pow(yr, 2) - + 4 * v * std::pow(x0, 2) * y0 + 4 * v * std::pow(x0, 2) * yr + + 8 * v * x0 * xr * y0 - 8 * v * x0 * xr * yr - + 4 * v * std::pow(xr, 2) * y0 + 4 * v * std::pow(xr, 2) * yr - + 4 * v * std::pow(y0, 3) + 12 * v * std::pow(y0, 2) * yr - + 12 * v * y0 * std::pow(yr, 2) + 4 * v * std::pow(yr, 3) + + 4 * std::pow(C, 4) * w * z0 / std::pow(B, 2) - + 4 * std::pow(C, 4) * w * zr / std::pow(B, 2) - + 4 * std::pow(C, 2) * u * x0 * std::pow(z0, 2) / std::pow(B, 2) + + 8 * std::pow(C, 2) * u * x0 * z0 * zr / std::pow(B, 2) - + 4 * std::pow(C, 2) * u * x0 * std::pow(zr, 2) / std::pow(B, 2) + + 4 * std::pow(C, 2) * u * xr * std::pow(z0, 2) / std::pow(B, 2) - + 8 * std::pow(C, 2) * u * xr * z0 * zr / std::pow(B, 2) + + 4 * std::pow(C, 2) * u * xr * std::pow(zr, 2) / std::pow(B, 2) - + 4 * std::pow(C, 2) * v * y0 * std::pow(z0, 2) / std::pow(B, 2) + + 8 * std::pow(C, 2) * v * y0 * z0 * zr / std::pow(B, 2) - + 4 * std::pow(C, 2) * v * y0 * std::pow(zr, 2) / std::pow(B, 2) + + 4 * std::pow(C, 2) * v * yr * std::pow(z0, 2) / std::pow(B, 2) - + 8 * std::pow(C, 2) * v * yr * z0 * zr / std::pow(B, 2) + + 4 * std::pow(C, 2) * v * yr * std::pow(zr, 2) / std::pow(B, 2) - + 4 * std::pow(C, 2) * w * std::pow(x0, 2) * z0 / std::pow(B, 2) + + 4 * std::pow(C, 2) * w * std::pow(x0, 2) * zr / std::pow(B, 2) + + 8 * std::pow(C, 2) * w * x0 * xr * z0 / std::pow(B, 2) - + 8 * std::pow(C, 2) * w * x0 * xr * zr / std::pow(B, 2) - + 4 * std::pow(C, 2) * w * std::pow(xr, 2) * z0 / std::pow(B, 2) + + 4 * std::pow(C, 2) * w * std::pow(xr, 2) * zr / std::pow(B, 2) - + 4 * std::pow(C, 2) * w * std::pow(y0, 2) * z0 / std::pow(B, 2) + + 4 * std::pow(C, 2) * w * std::pow(y0, 2) * zr / std::pow(B, 2) + + 8 * std::pow(C, 2) * w * y0 * yr * z0 / std::pow(B, 2) - + 8 * std::pow(C, 2) * w * y0 * yr * zr / std::pow(B, 2) - + 4 * std::pow(C, 2) * w * std::pow(yr, 2) * z0 / std::pow(B, 2) + + 4 * std::pow(C, 2) * w * std::pow(yr, 2) * zr / std::pow(B, 2) - + 4 * std::pow(C, 4) * w * std::pow(z0, 3) / std::pow(B, 4) + + 12 * std::pow(C, 4) * w * std::pow(z0, 2) * zr / std::pow(B, 4) - + 12 * std::pow(C, 4) * w * z0 * std::pow(zr, 2) / std::pow(B, 4) + + 4 * std::pow(C, 4) * w * std::pow(zr, 3) / std::pow(B, 4); // the 0th order terms - e = std::pow(A,4) - 2*std::pow(A,2)*std::pow(C,2) - 2*std::pow(A,2)*std::pow(x0,2) + 4*std::pow(A,2)*x0*xr - 2*std::pow(A,2)*std::pow(xr,2) - 2*std::pow(A,2)*std::pow(y0,2) + 4*std::pow(A,2)*y0*yr - 2*std::pow(A,2)*std::pow(yr,2) + 2*std::pow(A,2)*std::pow(C,2)*std::pow(z0,2)/std::pow(B,2) - 4*std::pow(A,2)*std::pow(C,2)*z0*zr/std::pow(B,2) + 2*std::pow(A,2)*std::pow(C,2)*std::pow(zr,2)/std::pow(B,2) + std::pow(C,4) - 2*std::pow(C,2)*std::pow(x0,2) + 4*std::pow(C,2)*x0*xr - 2*std::pow(C,2)*std::pow(xr,2) - 2*std::pow(C,2)*std::pow(y0,2) + 4*std::pow(C,2)*y0*yr - 2*std::pow(C,2)*std::pow(yr,2) + std::pow(x0,4) - 4*std::pow(x0,3)*xr + 6*std::pow(x0,2)*std::pow(xr,2) + 2*std::pow(x0,2)*std::pow(y0,2) - 4*std::pow(x0,2)*y0*yr + 2*std::pow(x0,2)*std::pow(yr,2) - 4*x0*std::pow(xr,3) - 4*x0*xr*std::pow(y0,2) + 8*x0*xr*y0*yr - 4*x0*xr*std::pow(yr,2) + std::pow(xr,4) + 2*std::pow(xr,2)*std::pow(y0,2) - 4*std::pow(xr,2)*y0*yr + 2*std::pow(xr,2)*std::pow(yr,2) + std::pow(y0,4) - 4*std::pow(y0,3)*yr + 6*std::pow(y0,2)*std::pow(yr,2) - 4*y0*std::pow(yr,3) + std::pow(yr,4) - 2*std::pow(C,4)*std::pow(z0,2)/std::pow(B,2) + 4*std::pow(C,4)*z0*zr/std::pow(B,2) - 2*std::pow(C,4)*std::pow(zr,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(x0,2)*std::pow(z0,2)/std::pow(B,2) - 4*std::pow(C,2)*std::pow(x0,2)*z0*zr/std::pow(B,2) + 2*std::pow(C,2)*std::pow(x0,2)*std::pow(zr,2)/std::pow(B,2) - 4*std::pow(C,2)*x0*xr*std::pow(z0,2)/std::pow(B,2) + 8*std::pow(C,2)*x0*xr*z0*zr/std::pow(B,2) - 4*std::pow(C,2)*x0*xr*std::pow(zr,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(xr,2)*std::pow(z0,2)/std::pow(B,2) - 4*std::pow(C,2)*std::pow(xr,2)*z0*zr/std::pow(B,2) + 2*std::pow(C,2)*std::pow(xr,2)*std::pow(zr,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(y0,2)*std::pow(z0,2)/std::pow(B,2) - 4*std::pow(C,2)*std::pow(y0,2)*z0*zr/std::pow(B,2) + 2*std::pow(C,2)*std::pow(y0,2)*std::pow(zr,2)/std::pow(B,2) - 4*std::pow(C,2)*y0*yr*std::pow(z0,2)/std::pow(B,2) + 8*std::pow(C,2)*y0*yr*z0*zr/std::pow(B,2) - 4*std::pow(C,2)*y0*yr*std::pow(zr,2)/std::pow(B,2) + 2*std::pow(C,2)*std::pow(yr,2)*std::pow(z0,2)/std::pow(B,2) - 4*std::pow(C,2)*std::pow(yr,2)*z0*zr/std::pow(B,2) + 2*std::pow(C,2)*std::pow(yr,2)*std::pow(zr,2)/std::pow(B,2) + std::pow(C,4)*std::pow(z0,4)/std::pow(B,4) - 4*std::pow(C,4)*std::pow(z0,3)*zr/std::pow(B,4) + 6*std::pow(C,4)*std::pow(z0,2)*std::pow(zr,2)/std::pow(B,4) - 4*std::pow(C,4)*z0*std::pow(zr,3)/std::pow(B,4) + std::pow(C,4)*std::pow(zr,4)/std::pow(B,4); + e = std::pow(A, 4) - 2 * std::pow(A, 2) * std::pow(C, 2) - + 2 * std::pow(A, 2) * std::pow(x0, 2) + 4 * std::pow(A, 2) * x0 * xr - + 2 * std::pow(A, 2) * std::pow(xr, 2) - + 2 * std::pow(A, 2) * std::pow(y0, 2) + 4 * std::pow(A, 2) * y0 * yr - + 2 * std::pow(A, 2) * std::pow(yr, 2) + + 2 * std::pow(A, 2) * std::pow(C, 2) * std::pow(z0, 2) / std::pow(B, 2) - + 4 * std::pow(A, 2) * std::pow(C, 2) * z0 * zr / std::pow(B, 2) + + 2 * std::pow(A, 2) * std::pow(C, 2) * std::pow(zr, 2) / std::pow(B, 2) + + std::pow(C, 4) - 2 * std::pow(C, 2) * std::pow(x0, 2) + + 4 * std::pow(C, 2) * x0 * xr - 2 * std::pow(C, 2) * std::pow(xr, 2) - + 2 * std::pow(C, 2) * std::pow(y0, 2) + 4 * std::pow(C, 2) * y0 * yr - + 2 * std::pow(C, 2) * std::pow(yr, 2) + std::pow(x0, 4) - + 4 * std::pow(x0, 3) * xr + 6 * std::pow(x0, 2) * std::pow(xr, 2) + + 2 * std::pow(x0, 2) * std::pow(y0, 2) - 4 * std::pow(x0, 2) * y0 * yr + + 2 * std::pow(x0, 2) * std::pow(yr, 2) - 4 * x0 * std::pow(xr, 3) - + 4 * x0 * xr * std::pow(y0, 2) + 8 * x0 * xr * y0 * yr - + 4 * x0 * xr * std::pow(yr, 2) + std::pow(xr, 4) + + 2 * std::pow(xr, 2) * std::pow(y0, 2) - 4 * std::pow(xr, 2) * y0 * yr + + 2 * std::pow(xr, 2) * std::pow(yr, 2) + std::pow(y0, 4) - + 4 * std::pow(y0, 3) * yr + 6 * std::pow(y0, 2) * std::pow(yr, 2) - + 4 * y0 * std::pow(yr, 3) + std::pow(yr, 4) - + 2 * std::pow(C, 4) * std::pow(z0, 2) / std::pow(B, 2) + + 4 * std::pow(C, 4) * z0 * zr / std::pow(B, 2) - + 2 * std::pow(C, 4) * std::pow(zr, 2) / std::pow(B, 2) + + 2 * std::pow(C, 2) * std::pow(x0, 2) * std::pow(z0, 2) / std::pow(B, 2) - + 4 * std::pow(C, 2) * std::pow(x0, 2) * z0 * zr / std::pow(B, 2) + + 2 * std::pow(C, 2) * std::pow(x0, 2) * std::pow(zr, 2) / std::pow(B, 2) - + 4 * std::pow(C, 2) * x0 * xr * std::pow(z0, 2) / std::pow(B, 2) + + 8 * std::pow(C, 2) * x0 * xr * z0 * zr / std::pow(B, 2) - + 4 * std::pow(C, 2) * x0 * xr * std::pow(zr, 2) / std::pow(B, 2) + + 2 * std::pow(C, 2) * std::pow(xr, 2) * std::pow(z0, 2) / std::pow(B, 2) - + 4 * std::pow(C, 2) * std::pow(xr, 2) * z0 * zr / std::pow(B, 2) + + 2 * std::pow(C, 2) * std::pow(xr, 2) * std::pow(zr, 2) / std::pow(B, 2) + + 2 * std::pow(C, 2) * std::pow(y0, 2) * std::pow(z0, 2) / std::pow(B, 2) - + 4 * std::pow(C, 2) * std::pow(y0, 2) * z0 * zr / std::pow(B, 2) + + 2 * std::pow(C, 2) * std::pow(y0, 2) * std::pow(zr, 2) / std::pow(B, 2) - + 4 * std::pow(C, 2) * y0 * yr * std::pow(z0, 2) / std::pow(B, 2) + + 8 * std::pow(C, 2) * y0 * yr * z0 * zr / std::pow(B, 2) - + 4 * std::pow(C, 2) * y0 * yr * std::pow(zr, 2) / std::pow(B, 2) + + 2 * std::pow(C, 2) * std::pow(yr, 2) * std::pow(z0, 2) / std::pow(B, 2) - + 4 * std::pow(C, 2) * std::pow(yr, 2) * z0 * zr / std::pow(B, 2) + + 2 * std::pow(C, 2) * std::pow(yr, 2) * std::pow(zr, 2) / std::pow(B, 2) + + std::pow(C, 4) * std::pow(z0, 4) / std::pow(B, 4) - + 4 * std::pow(C, 4) * std::pow(z0, 3) * zr / std::pow(B, 4) + + 6 * std::pow(C, 4) * std::pow(z0, 2) * std::pow(zr, 2) / std::pow(B, 4) - + 4 * std::pow(C, 4) * z0 * std::pow(zr, 3) / std::pow(B, 4) + + std::pow(C, 4) * std::pow(zr, 4) / std::pow(B, 4); // - std::array roots; - quartic_solve(b,c,d,e,roots); - - if (coincident) r += ang*TINY_BIT; + std::array roots; + quartic_solve(b, c, d, e, roots); + + if (coincident) + r += ang * TINY_BIT; // special degerenate case two sets of repated // roots - if ( b == 0.0 && d == 0) { - if ( roots[1] - roots[0] < 1e-5 ) return INFTY; - if ( roots[3] - roots[2] < 1e-5 ) return INFTY; + if (b == 0.0 && d == 0) { + if (roots[1] - roots[0] < 1e-5) + return INFTY; + if (roots[3] - roots[2] < 1e-5) + return INFTY; } - for ( int i = 0 ; i < 4 ; i++ ) { + for (int i = 0; i < 4; i++) { // need something better than just raw tolerance // use fastQS to get back lost precision - if ( roots[i] > 1e-6) { + if (roots[i] > 1e-6) { return roots[i]; - } + } } // otherwise no hit return INFTY; } -Direction -SurfaceZTorus::normal(Position r) const +Direction SurfaceZTorus::normal(Position r) const { // reduce the expansion of the full form for torus double x = r.x - x0_; double y = r.y - y0_; double z = r.z - z0_; - double A2 = A_*A_; - double B2 = B_*B_; - double C2 = C_*C_; - + double A2 = A_ * A_; + double B2 = B_ * B_; + double C2 = C_ * C_; // coefficients for the x, x^2, x^3 and x^4 term - // but since we differeniate - we get x4 -> 4x3 + // but since we differeniate - we get x4 -> 4x3 double dx_4 = 1.0; - double dx_2 = -2.*(A2 - C2 + y*y + C2*z*z/B2); - double dx = 4.*dx_4*x*x*x + 2.*dx_2*x; + double dx_2 = -2. * (A2 - C2 + y * y + C2 * z * z / B2); + double dx = 4. * dx_4 * x * x * x + 2. * dx_2 * x; // similarly to y double dy_4 = 1.0; - double dy_2 = -2.*(A2 - C2 + x*x + C2*z*z/B2); - double dy = 4.*dy_4*y*y*y + 2.*dy_2*y; + double dy_2 = -2. * (A2 - C2 + x * x + C2 * z * z / B2); + double dy = 4. * dy_4 * y * y * y + 2. * dy_2 * y; // and z - double dz_4 = std::pow(C_,4)/std::pow(B_,4); - double dz_2 = 2.*(A2*C2/B2 - std::pow(C_,4)/B2 + C2*x*x/B2 + C2*y*y/B2); - double dz = 4.*dz_4*z*z*z + 2.*dz_2*z; + double dz_4 = std::pow(C_, 4) / std::pow(B_, 4); + double dz_2 = 2. * (A2 * C2 / B2 - std::pow(C_, 4) / B2 + C2 * x * x / B2 + + C2 * y * y / B2); + double dz = 4. * dz_4 * z * z * z + 2. * dz_2 * z; - double length = std::sqrt(dx*dx + dy*dy + dz*dz); + double length = std::sqrt(dx * dx + dy * dy + dz * dz); - return {dx/length,dy/length,dz/length}; + return {dx / length, dy / length, dz / length}; } - - //============================================================================== void read_surfaces(pugi::xml_node node) From d51878c04cd0c8c7b58918672564cda2cb7fa734 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 17 Dec 2021 17:27:37 -0500 Subject: [PATCH 03/32] Original Python classes for tori --- openmc/surface.py | 207 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 207 insertions(+) diff --git a/openmc/surface.py b/openmc/surface.py index 41477fd1c9..eb6058e472 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -2118,6 +2118,213 @@ class Quadric(QuadricMixin, Surface): return tuple(getattr(self, c) for c in self._coeff_keys) +class XTorus(Surface): + """description. + + Parameters + ---------- + surface_id : int, optional + Unique identifier for the surface. If not specified, an identifier will + automatically be assigned. + boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}, optional + Boundary condition that defines the behavior for particles hitting the + surface. Defaults to transmissive boundary condition where particles + freely pass through the surface. + x0, y0 ,z0, A, B, C, : float, optional + coefficients for the surface. All default to 0. + name : str, optional + Name of the surface. If not specified, the name will be the empty string. + + Attributes + ---------- + a, b, c, d, e, f, g, h, j, k : float + coefficients for the surface + boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'} + Boundary condition that defines the behavior for particles hitting the + surface. + coefficients : dict + Dictionary of surface coefficients + id : int + Unique identifier for the surface + name : str + Name of the surface + type : str + Type of the surface + + """ + + _type = 'x-torus' + _coeff_keys = ('x0', 'y0', 'z0', 'A', 'B', 'C') + + def __init__(self, surface_id=None, boundary_type='transmission', + x0=0., y0=0., z0=0., A=0., B=0., C=0., name=''): + super().__init__(surface_id, boundary_type, name=name) + self.x0 = x0 + self.y0 = y0 + self.z0 = z0 + self.a = A + self.b = B + self.c = C + + def translate(self, vector): + return self + + def evaluate(self, point): + x = point[0] - self.x0 + y = point[1] - self.y0 + z = point[2] - self.z0 + + x_coeff = x**2 + y_coeff = y**2 + z_coeff = z**2 + + B2 = self.b*self.b + C2 = self.c*self.c + + value = x_coeff/B2 + (np.sqrt(y_coeff+z_coeff)-self.a)**2/C2 - 1 + + return value + + +class YTorus(Surface): + """description. + + Parameters + ---------- + surface_id : int, optional + Unique identifier for the surface. If not specified, an identifier will + automatically be assigned. + boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}, optional + Boundary condition that defines the behavior for particles hitting the + surface. Defaults to transmissive boundary condition where particles + freely pass through the surface. + x0, y0 ,z0, A, B, C, : float, optional + coefficients for the surface. All default to 0. + name : str, optional + Name of the surface. If not specified, the name will be the empty string. + + Attributes + ---------- + a, b, c, d, e, f, g, h, j, k : float + coefficients for the surface + boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'} + Boundary condition that defines the behavior for particles hitting the + surface. + coefficients : dict + Dictionary of surface coefficients + id : int + Unique identifier for the surface + name : str + Name of the surface + type : str + Type of the surface + + """ + + _type = 'y-torus' + _coeff_keys = ('x0', 'y0', 'z0', 'A', 'B', 'C') + + def __init__(self, surface_id=None, boundary_type='transmission', + x0=0., y0=0., z0=0., A=0., B=0., C=0., name=''): + super().__init__(surface_id, boundary_type, name=name) + self.x0 = x0 + self.y0 = y0 + self.z0 = z0 + self.a = A + self.b = B + self.c = C + + def evaluate(self, point): + + x = point[0] - self.x0 + y = point[1] - self.y0 + z = point[2] - self.z0 + + x_coeff = x**2 + y_coeff = y**2 + z_coeff = z**2 + + B2 = self.b*self.b + C2 = self.c*self.c + + value = y_coeff/B2 + (np.sqrt(x_coeff+z_coeff)-self.a)**2/C2 - 1 + + return value + + def translate(self, vector): + return self + + +class ZTorus(Surface): + """description. + + Parameters + ---------- + surface_id : int, optional + Unique identifier for the surface. If not specified, an identifier will + automatically be assigned. + boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}, optional + Boundary condition that defines the behavior for particles hitting the + surface. Defaults to transmissive boundary condition where particles + freely pass through the surface. + x0, y0 ,z0, A, B, C, : float, optional + coefficients for the surface. All default to 0. + name : str, optional + Name of the surface. If not specified, the name will be the empty string. + + Attributes + ---------- + a, b, c, d, e, f, g, h, j, k : float + coefficients for the surface + boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'} + Boundary condition that defines the behavior for particles hitting the + surface. + coefficients : dict + Dictionary of surface coefficients + id : int + Unique identifier for the surface + name : str + Name of the surface + type : str + Type of the surface + + """ + + _type = 'z-torus' + _coeff_keys = ('x0', 'y0', 'z0', 'A', 'B', 'C') + + def __init__(self, surface_id=None, boundary_type='transmission', + x0=0., y0=0., z0=0., A=0., B=0., C=0., name=''): + super().__init__(surface_id, boundary_type, name=name) + self.x0 = x0 + self.y0 = y0 + self.z0 = z0 + self.a = A + self.b = B + self.c = C + + def evaluate(self, point): + + x = point[0] - self.x0 + y = point[1] - self.y0 + z = point[2] - self.z0 + + x_coeff = x**2 + y_coeff = y**2 + z_coeff = z**2 + + B2 = self.b*self.b + C2 = self.c*self.c + + value = z_coeff/B2 + (np.sqrt(x_coeff+y_coeff)-self.a)**2/C2 - 1 + + return value + + + def translate(self, vector): + return self + + class Halfspace(Region): """A positive or negative half-space region. From 4562c09e4d265e34e22118010779481abe1dd419 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 17 Dec 2021 23:42:16 -0500 Subject: [PATCH 04/32] Simplify torus Python classes --- openmc/surface.py | 136 +++++++++++++++------------------------------- 1 file changed, 43 insertions(+), 93 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index eb6058e472..f7e8f30be2 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -2118,22 +2118,40 @@ class Quadric(QuadricMixin, Surface): return tuple(getattr(self, c) for c in self._coeff_keys) -class XTorus(Surface): +class TorusMixin: + _coeff_keys = ('x0', 'y0', 'z0', 'a', 'b', 'c') + + def __init__(self, x0=0., y0=0., z0=0., a=0., b=0., c=0., **kwargs): + super().__init__(**kwargs) + for key, val in zip(self._coeff_keys, (x0, y0, z0, a, b, c)): + setattr(self, key, val) + + x0 = SurfaceCoefficient('x0') + y0 = SurfaceCoefficient('y0') + z0 = SurfaceCoefficient('z0') + a = SurfaceCoefficient('a') + b = SurfaceCoefficient('b') + c = SurfaceCoefficient('c') + + def translate(self, vector, inplace=False): + surf = self if inplace else self.clone() + surf.x0 += vector[0] + surf.y0 += vector[1] + surf.z0 += vector[2] + return surf + + def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False): + raise NotImplemented('Torus surfaces cannot be rotated.') + + +class XTorus(TorusMixin, Surface): """description. Parameters ---------- - surface_id : int, optional - Unique identifier for the surface. If not specified, an identifier will - automatically be assigned. - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}, optional - Boundary condition that defines the behavior for particles hitting the - surface. Defaults to transmissive boundary condition where particles - freely pass through the surface. - x0, y0 ,z0, A, B, C, : float, optional + x0, y0, z0, a, b, c : float, optional coefficients for the surface. All default to 0. - name : str, optional - Name of the surface. If not specified, the name will be the empty string. + kwargs Attributes ---------- @@ -2152,41 +2170,19 @@ class XTorus(Surface): Type of the surface """ - _type = 'x-torus' - _coeff_keys = ('x0', 'y0', 'z0', 'A', 'B', 'C') - - def __init__(self, surface_id=None, boundary_type='transmission', - x0=0., y0=0., z0=0., A=0., B=0., C=0., name=''): - super().__init__(surface_id, boundary_type, name=name) - self.x0 = x0 - self.y0 = y0 - self.z0 = z0 - self.a = A - self.b = B - self.c = C - - def translate(self, vector): - return self def evaluate(self, point): x = point[0] - self.x0 y = point[1] - self.y0 z = point[2] - self.z0 - - x_coeff = x**2 - y_coeff = y**2 - z_coeff = z**2 - - B2 = self.b*self.b - C2 = self.c*self.c - - value = x_coeff/B2 + (np.sqrt(y_coeff+z_coeff)-self.a)**2/C2 - 1 - - return value + a = self.a + b = self.b + c = self.c + return (x*x)/(b*b) + (math.sqrt(y*y + z*z) - a)**2/(c*c) - 1 -class YTorus(Surface): +class YTorus(TorusMixin, Surface): """description. Parameters @@ -2220,42 +2216,19 @@ class YTorus(Surface): Type of the surface """ - _type = 'y-torus' - _coeff_keys = ('x0', 'y0', 'z0', 'A', 'B', 'C') - - def __init__(self, surface_id=None, boundary_type='transmission', - x0=0., y0=0., z0=0., A=0., B=0., C=0., name=''): - super().__init__(surface_id, boundary_type, name=name) - self.x0 = x0 - self.y0 = y0 - self.z0 = z0 - self.a = A - self.b = B - self.c = C def evaluate(self, point): - x = point[0] - self.x0 y = point[1] - self.y0 z = point[2] - self.z0 - - x_coeff = x**2 - y_coeff = y**2 - z_coeff = z**2 - - B2 = self.b*self.b - C2 = self.c*self.c - - value = y_coeff/B2 + (np.sqrt(x_coeff+z_coeff)-self.a)**2/C2 - 1 - - return value - - def translate(self, vector): - return self + a = self.a + b = self.b + c = self.c + return (y*y)/(b*b) + (math.sqrt(x*x + z*z) - a)**2/(c*c) - 1 -class ZTorus(Surface): +class ZTorus(TorusMixin, Surface): """description. Parameters @@ -2291,38 +2264,15 @@ class ZTorus(Surface): """ _type = 'z-torus' - _coeff_keys = ('x0', 'y0', 'z0', 'A', 'B', 'C') - - def __init__(self, surface_id=None, boundary_type='transmission', - x0=0., y0=0., z0=0., A=0., B=0., C=0., name=''): - super().__init__(surface_id, boundary_type, name=name) - self.x0 = x0 - self.y0 = y0 - self.z0 = z0 - self.a = A - self.b = B - self.c = C def evaluate(self, point): - x = point[0] - self.x0 y = point[1] - self.y0 z = point[2] - self.z0 - - x_coeff = x**2 - y_coeff = y**2 - z_coeff = z**2 - - B2 = self.b*self.b - C2 = self.c*self.c - - value = z_coeff/B2 + (np.sqrt(x_coeff+y_coeff)-self.a)**2/C2 - 1 - - return value - - - def translate(self, vector): - return self + a = self.a + b = self.b + c = self.c + return (z*z)/(b*b) + (math.sqrt(x*x + y*y) - a)**2/(c*c) - 1 class Halfspace(Region): From 7ff95cfbcece2bfa64aed90fda620f78e93c3c65 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sat, 18 Dec 2021 10:38:19 -0500 Subject: [PATCH 05/32] Update Torus::evaluate methods --- src/surface.cpp | 54 ++++++++++++++----------------------------------- 1 file changed, 15 insertions(+), 39 deletions(-) diff --git a/src/surface.cpp b/src/surface.cpp index 20b2e6ddb3..f8e5f1fdf3 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -1185,19 +1185,11 @@ void SurfaceXTorus::to_hdf5_inner(hid_t group_id) const double SurfaceXTorus::evaluate(Position r) const { - const double x = r.x; - const double y = r.y; - const double z = r.z; - - const double x_coeff2 = std::pow(x - x0_, 2); - const double y_coeff2 = std::pow(y - y0_, 2); - const double z_coeff2 = std::pow(z - z0_, 2); - - const double B2 = B_ * B_; - const double C2 = C_ * C_; - - return x_coeff2 / B2 + std::pow(std::sqrt(y_coeff2 + z_coeff2) - A_, 2) / C2 - - 1.; + double x = r.x - x0_; + double y = r.y - y0_; + double z = r.z - z0_; + return (x * x) / (B_ * B_) + + std::pow(std::sqrt(y * y + z * z) - A_, 2) / (C_ * C_) - 1.; } double SurfaceXTorus::distance(Position r, Direction ang, bool coincident) const @@ -1343,19 +1335,11 @@ void SurfaceYTorus::to_hdf5_inner(hid_t group_id) const double SurfaceYTorus::evaluate(Position r) const { - const double x = r.x; - const double y = r.y; - const double z = r.z; - - const double x_coeff2 = std::pow(x - x0_, 2); - const double y_coeff2 = std::pow(y - y0_, 2); - const double z_coeff2 = std::pow(z - z0_, 2); - - const double B2 = B_ * B_; - const double C2 = C_ * C_; - - return y_coeff2 / B2 + std::pow(std::sqrt(x_coeff2 + z_coeff2) - A_, 2) / C2 - - 1.; + double x = r.x - x0_; + double y = r.y - y0_; + double z = r.z - z0_; + return (y * y) / (B_ * B_) + + std::pow(std::sqrt(x * x + z * z) - A_, 2) / (C_ * C_) - 1.; } double SurfaceYTorus::distance(Position r, Direction ang, bool coincident) const @@ -1506,19 +1490,11 @@ void SurfaceZTorus::to_hdf5_inner(hid_t group_id) const double SurfaceZTorus::evaluate(Position r) const { - const double x = r.x; - const double y = r.y; - const double z = r.z; - - const double x_coeff2 = std::pow(x - x0_, 2); - const double y_coeff2 = std::pow(y - y0_, 2); - const double z_coeff2 = std::pow(z - z0_, 2); - - const double B2 = B_ * B_; - const double C2 = C_ * C_; - - return z_coeff2 / B2 + std::pow(std::sqrt(x_coeff2 + y_coeff2) - A_, 2) / C2 - - 1.; + double x = r.x - x0_; + double y = r.y - y0_; + double z = r.z - z0_; + return (z * z) / (B_ * B_) + + std::pow(std::sqrt(x * x + y * y) - A_, 2) / (C_ * C_) - 1.; } double SurfaceZTorus::distance(Position r, Direction ang, bool coincident) const From 26322e6cd859ee816a122b17b9f7f52639481b45 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 20 Dec 2021 09:24:57 -0500 Subject: [PATCH 06/32] Add unimplemented TorusMixin._get_base_coeffs method --- openmc/surface.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openmc/surface.py b/openmc/surface.py index f7e8f30be2..19df0804ad 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -2143,6 +2143,9 @@ class TorusMixin: def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False): raise NotImplemented('Torus surfaces cannot be rotated.') + def _get_base_coeffs(self): + raise NotImplementedError + class XTorus(TorusMixin, Surface): """description. From badf9f33faace786038309e603c3ffbe2abb1630 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 20 Dec 2021 10:38:42 -0500 Subject: [PATCH 07/32] Eliminate std::pow in torus distance methods --- src/surface.cpp | 461 ++++++++++++++++++++++-------------------------- 1 file changed, 214 insertions(+), 247 deletions(-) diff --git a/src/surface.cpp b/src/surface.cpp index f8e5f1fdf3..82a0d53da6 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -1012,7 +1012,7 @@ void SurfaceQuadric::to_hdf5_inner(hid_t group_id) const int quadratic_solve(double a, double b, double c, std::array& x) { - double func = std::pow(b, 2) - 4 * a * c; + double func = (b * b) - 4 * a * c; if (func < 0) { // this would be imaginary @@ -1213,55 +1213,46 @@ double SurfaceXTorus::distance(Position r, Direction ang, bool coincident) const double a, b, c, d, e; // coefficients of quartic // a is always 1 maybe save the maths? - a = std::pow(v, 4) + 2 * std::pow(v, 2) * std::pow(w, 2) + std::pow(w, 4) + - 2 * std::pow(C, 2) * std::pow(u, 2) * std::pow(v, 2) / std::pow(B, 2) + - 2 * std::pow(C, 2) * std::pow(u, 2) * std::pow(w, 2) / std::pow(B, 2) + - std::pow(C, 4) * std::pow(u, 4) / std::pow(B, 4); - b = 4 * std::pow(v, 3) * y0 + 4 * std::pow(v, 2) * w * z0 + - 4 * v * std::pow(w, 2) * y0 + 4 * std::pow(w, 3) * z0 + - 4 * std::pow(C, 2) * std::pow(u, 2) * v * y0 / std::pow(B, 2) + - 4 * std::pow(C, 2) * std::pow(u, 2) * w * z0 / std::pow(B, 2) + - 4 * std::pow(C, 2) * u * std::pow(v, 2) * x0 / std::pow(B, 2) + - 4 * std::pow(C, 2) * u * std::pow(w, 2) * x0 / std::pow(B, 2) + - 4 * std::pow(C, 4) * std::pow(u, 3) * x0 / std::pow(B, 4); - c = - -2 * std::pow(A, 2) * std::pow(v, 2) - 2 * std::pow(A, 2) * std::pow(w, 2) + - 2 * std::pow(A, 2) * std::pow(C, 2) * std::pow(u, 2) / std::pow(B, 2) - - 2 * std::pow(C, 2) * std::pow(v, 2) - 2 * std::pow(C, 2) * std::pow(w, 2) + - 6 * std::pow(v, 2) * std::pow(y0, 2) + - 2 * std::pow(v, 2) * std::pow(z0, 2) + 8 * v * w * y0 * z0 + - 2 * std::pow(w, 2) * std::pow(y0, 2) + - 6 * std::pow(w, 2) * std::pow(z0, 2) - - 2 * std::pow(C, 4) * std::pow(u, 2) / std::pow(B, 2) + - 2 * std::pow(C, 2) * std::pow(u, 2) * std::pow(y0, 2) / std::pow(B, 2) + - 2 * std::pow(C, 2) * std::pow(u, 2) * std::pow(z0, 2) / std::pow(B, 2) + - 8 * std::pow(C, 2) * u * v * x0 * y0 / std::pow(B, 2) + - 8 * std::pow(C, 2) * u * w * x0 * z0 / std::pow(B, 2) + - 2 * std::pow(C, 2) * std::pow(v, 2) * std::pow(x0, 2) / std::pow(B, 2) + - 2 * std::pow(C, 2) * std::pow(w, 2) * std::pow(x0, 2) / std::pow(B, 2) + - 6 * std::pow(C, 4) * std::pow(u, 2) * std::pow(x0, 2) / std::pow(B, 4); - d = -4 * std::pow(A, 2) * v * y0 - 4 * std::pow(A, 2) * w * z0 + - 4 * std::pow(A, 2) * std::pow(C, 2) * u * x0 / std::pow(B, 2) - - 4 * std::pow(C, 2) * v * y0 - 4 * std::pow(C, 2) * w * z0 + - 4 * v * std::pow(y0, 3) + 4 * v * y0 * std::pow(z0, 2) + - 4 * w * std::pow(y0, 2) * z0 + 4 * w * std::pow(z0, 3) - - 4 * std::pow(C, 4) * u * x0 / std::pow(B, 2) + - 4 * std::pow(C, 2) * u * x0 * std::pow(y0, 2) / std::pow(B, 2) + - 4 * std::pow(C, 2) * u * x0 * std::pow(z0, 2) / std::pow(B, 2) + - 4 * std::pow(C, 2) * v * std::pow(x0, 2) * y0 / std::pow(B, 2) + - 4 * std::pow(C, 2) * w * std::pow(x0, 2) * z0 / std::pow(B, 2) + - 4 * std::pow(C, 4) * u * std::pow(x0, 3) / std::pow(B, 4); - e = std::pow(A, 4) - 2 * std::pow(A, 2) * std::pow(C, 2) - - 2 * std::pow(A, 2) * std::pow(y0, 2) - - 2 * std::pow(A, 2) * std::pow(z0, 2) + - 2 * std::pow(A, 2) * std::pow(C, 2) * std::pow(x0, 2) / std::pow(B, 2) + - std::pow(C, 4) - 2 * std::pow(C, 2) * std::pow(y0, 2) - - 2 * std::pow(C, 2) * std::pow(z0, 2) + std::pow(y0, 4) + - 2 * std::pow(y0, 2) * std::pow(z0, 2) + std::pow(z0, 4) - - 2 * std::pow(C, 4) * std::pow(x0, 2) / std::pow(B, 2) + - 2 * std::pow(C, 2) * std::pow(x0, 2) * std::pow(y0, 2) / std::pow(B, 2) + - 2 * std::pow(C, 2) * std::pow(x0, 2) * std::pow(z0, 2) / std::pow(B, 2) + - std::pow(C, 4) * std::pow(x0, 4) / std::pow(B, 4); + a = (v * v * v * v) + 2 * (v * v) * (w * w) + (w * w * w * w) + + 2 * (C * C) * (u * u) * (v * v) / (B * B) + + 2 * (C * C) * (u * u) * (w * w) / (B * B) + + (C * C * C * C) * (u * u * u * u) / (B * B * B * B); + b = 4 * (v * v * v) * y0 + 4 * (v * v) * w * z0 + 4 * v * (w * w) * y0 + + 4 * (w * w * w) * z0 + 4 * (C * C) * (u * u) * v * y0 / (B * B) + + 4 * (C * C) * (u * u) * w * z0 / (B * B) + + 4 * (C * C) * u * (v * v) * x0 / (B * B) + + 4 * (C * C) * u * (w * w) * x0 / (B * B) + + 4 * (C * C * C * C) * (u * u * u) * x0 / (B * B * B * B); + c = -2 * (A * A) * (v * v) - 2 * (A * A) * (w * w) + + 2 * (A * A) * (C * C) * (u * u) / (B * B) - 2 * (C * C) * (v * v) - + 2 * (C * C) * (w * w) + 6 * (v * v) * (y0 * y0) + + 2 * (v * v) * (z0 * z0) + 8 * v * w * y0 * z0 + 2 * (w * w) * (y0 * y0) + + 6 * (w * w) * (z0 * z0) - 2 * (C * C * C * C) * (u * u) / (B * B) + + 2 * (C * C) * (u * u) * (y0 * y0) / (B * B) + + 2 * (C * C) * (u * u) * (z0 * z0) / (B * B) + + 8 * (C * C) * u * v * x0 * y0 / (B * B) + + 8 * (C * C) * u * w * x0 * z0 / (B * B) + + 2 * (C * C) * (v * v) * (x0 * x0) / (B * B) + + 2 * (C * C) * (w * w) * (x0 * x0) / (B * B) + + 6 * (C * C * C * C) * (u * u) * (x0 * x0) / (B * B * B * B); + d = -4 * (A * A) * v * y0 - 4 * (A * A) * w * z0 + + 4 * (A * A) * (C * C) * u * x0 / (B * B) - 4 * (C * C) * v * y0 - + 4 * (C * C) * w * z0 + 4 * v * (y0 * y0 * y0) + 4 * v * y0 * (z0 * z0) + + 4 * w * (y0 * y0) * z0 + 4 * w * (z0 * z0 * z0) - + 4 * (C * C * C * C) * u * x0 / (B * B) + + 4 * (C * C) * u * x0 * (y0 * y0) / (B * B) + + 4 * (C * C) * u * x0 * (z0 * z0) / (B * B) + + 4 * (C * C) * v * (x0 * x0) * y0 / (B * B) + + 4 * (C * C) * w * (x0 * x0) * z0 / (B * B) + + 4 * (C * C * C * C) * u * (x0 * x0 * x0) / (B * B * B * B); + e = (A * A * A * A) - 2 * (A * A) * (C * C) - 2 * (A * A) * (y0 * y0) - + 2 * (A * A) * (z0 * z0) + 2 * (A * A) * (C * C) * (x0 * x0) / (B * B) + + (C * C * C * C) - 2 * (C * C) * (y0 * y0) - 2 * (C * C) * (z0 * z0) + + (y0 * y0 * y0 * y0) + 2 * (y0 * y0) * (z0 * z0) + (z0 * z0 * z0 * z0) - + 2 * (C * C * C * C) * (x0 * x0) / (B * B) + + 2 * (C * C) * (x0 * x0) * (y0 * y0) / (B * B) + + 2 * (C * C) * (x0 * x0) * (z0 * z0) / (B * B) + + (C * C * C * C) * (x0 * x0 * x0 * x0) / (B * B * B * B); // std::array roots; @@ -1362,55 +1353,46 @@ double SurfaceYTorus::distance(Position r, Direction ang, bool coincident) const double a, b, c, d, e; // coefficients of quartic - a = std::pow(u, 4) + 2 * std::pow(u, 2) * std::pow(w, 2) + std::pow(w, 4) + - 2 * std::pow(C, 2) * std::pow(u, 2) * std::pow(v, 2) / std::pow(B, 2) + - 2 * std::pow(C, 2) * std::pow(v, 2) * std::pow(w, 2) / std::pow(B, 2) + - std::pow(C, 4) * std::pow(v, 4) / std::pow(B, 4); - b = 4 * std::pow(u, 3) * x0 + 4 * std::pow(u, 2) * w * z0 + - 4 * u * std::pow(w, 2) * x0 + 4 * std::pow(w, 3) * z0 + - 4 * std::pow(C, 2) * std::pow(u, 2) * v * y0 / std::pow(B, 2) + - 4 * std::pow(C, 2) * u * std::pow(v, 2) * x0 / std::pow(B, 2) + - 4 * std::pow(C, 2) * std::pow(v, 2) * w * z0 / std::pow(B, 2) + - 4 * std::pow(C, 2) * v * std::pow(w, 2) * y0 / std::pow(B, 2) + - 4 * std::pow(C, 4) * std::pow(v, 3) * y0 / std::pow(B, 4); - c = - -2 * std::pow(A, 2) * std::pow(u, 2) - 2 * std::pow(A, 2) * std::pow(w, 2) + - 2 * std::pow(A, 2) * std::pow(C, 2) * std::pow(v, 2) / std::pow(B, 2) - - 2 * std::pow(C, 2) * std::pow(u, 2) - 2 * std::pow(C, 2) * std::pow(w, 2) + - 6 * std::pow(u, 2) * std::pow(x0, 2) + - 2 * std::pow(u, 2) * std::pow(z0, 2) + 8 * u * w * x0 * z0 + - 2 * std::pow(w, 2) * std::pow(x0, 2) + - 6 * std::pow(w, 2) * std::pow(z0, 2) - - 2 * std::pow(C, 4) * std::pow(v, 2) / std::pow(B, 2) + - 2 * std::pow(C, 2) * std::pow(u, 2) * std::pow(y0, 2) / std::pow(B, 2) + - 8 * std::pow(C, 2) * u * v * x0 * y0 / std::pow(B, 2) + - 2 * std::pow(C, 2) * std::pow(v, 2) * std::pow(x0, 2) / std::pow(B, 2) + - 2 * std::pow(C, 2) * std::pow(v, 2) * std::pow(z0, 2) / std::pow(B, 2) + - 8 * std::pow(C, 2) * v * w * y0 * z0 / std::pow(B, 2) + - 2 * std::pow(C, 2) * std::pow(w, 2) * std::pow(y0, 2) / std::pow(B, 2) + - 6 * std::pow(C, 4) * std::pow(v, 2) * std::pow(y0, 2) / std::pow(B, 4); - d = -4 * std::pow(A, 2) * u * x0 - 4 * std::pow(A, 2) * w * z0 + - 4 * std::pow(A, 2) * std::pow(C, 2) * v * y0 / std::pow(B, 2) - - 4 * std::pow(C, 2) * u * x0 - 4 * std::pow(C, 2) * w * z0 + - 4 * u * std::pow(x0, 3) + 4 * u * x0 * std::pow(z0, 2) + - 4 * w * std::pow(x0, 2) * z0 + 4 * w * std::pow(z0, 3) - - 4 * std::pow(C, 4) * v * y0 / std::pow(B, 2) + - 4 * std::pow(C, 2) * u * x0 * std::pow(y0, 2) / std::pow(B, 2) + - 4 * std::pow(C, 2) * v * std::pow(x0, 2) * y0 / std::pow(B, 2) + - 4 * std::pow(C, 2) * v * y0 * std::pow(z0, 2) / std::pow(B, 2) + - 4 * std::pow(C, 2) * w * std::pow(y0, 2) * z0 / std::pow(B, 2) + - 4 * std::pow(C, 4) * v * std::pow(y0, 3) / std::pow(B, 4); - e = std::pow(A, 4) - 2 * std::pow(A, 2) * std::pow(C, 2) - - 2 * std::pow(A, 2) * std::pow(x0, 2) - - 2 * std::pow(A, 2) * std::pow(z0, 2) + - 2 * std::pow(A, 2) * std::pow(C, 2) * std::pow(y0, 2) / std::pow(B, 2) + - std::pow(C, 4) - 2 * std::pow(C, 2) * std::pow(x0, 2) - - 2 * std::pow(C, 2) * std::pow(z0, 2) + std::pow(x0, 4) + - 2 * std::pow(x0, 2) * std::pow(z0, 2) + std::pow(z0, 4) - - 2 * std::pow(C, 4) * std::pow(y0, 2) / std::pow(B, 2) + - 2 * std::pow(C, 2) * std::pow(x0, 2) * std::pow(y0, 2) / std::pow(B, 2) + - 2 * std::pow(C, 2) * std::pow(y0, 2) * std::pow(z0, 2) / std::pow(B, 2) + - std::pow(C, 4) * std::pow(y0, 4) / std::pow(B, 4); + a = (u * u * u * u) + 2 * (u * u) * (w * w) + (w * w * w * w) + + 2 * (C * C) * (u * u) * (v * v) / (B * B) + + 2 * (C * C) * (v * v) * (w * w) / (B * B) + + (C * C * C * C) * (v * v * v * v) / (B * B * B * B); + b = 4 * (u * u * u) * x0 + 4 * (u * u) * w * z0 + 4 * u * (w * w) * x0 + + 4 * (w * w * w) * z0 + 4 * (C * C) * (u * u) * v * y0 / (B * B) + + 4 * (C * C) * u * (v * v) * x0 / (B * B) + + 4 * (C * C) * (v * v) * w * z0 / (B * B) + + 4 * (C * C) * v * (w * w) * y0 / (B * B) + + 4 * (C * C * C * C) * (v * v * v) * y0 / (B * B * B * B); + c = -2 * (A * A) * (u * u) - 2 * (A * A) * (w * w) + + 2 * (A * A) * (C * C) * (v * v) / (B * B) - 2 * (C * C) * (u * u) - + 2 * (C * C) * (w * w) + 6 * (u * u) * (x0 * x0) + + 2 * (u * u) * (z0 * z0) + 8 * u * w * x0 * z0 + 2 * (w * w) * (x0 * x0) + + 6 * (w * w) * (z0 * z0) - 2 * (C * C * C * C) * (v * v) / (B * B) + + 2 * (C * C) * (u * u) * (y0 * y0) / (B * B) + + 8 * (C * C) * u * v * x0 * y0 / (B * B) + + 2 * (C * C) * (v * v) * (x0 * x0) / (B * B) + + 2 * (C * C) * (v * v) * (z0 * z0) / (B * B) + + 8 * (C * C) * v * w * y0 * z0 / (B * B) + + 2 * (C * C) * (w * w) * (y0 * y0) / (B * B) + + 6 * (C * C * C * C) * (v * v) * (y0 * y0) / (B * B * B * B); + d = -4 * (A * A) * u * x0 - 4 * (A * A) * w * z0 + + 4 * (A * A) * (C * C) * v * y0 / (B * B) - 4 * (C * C) * u * x0 - + 4 * (C * C) * w * z0 + 4 * u * (x0 * x0 * x0) + 4 * u * x0 * (z0 * z0) + + 4 * w * (x0 * x0) * z0 + 4 * w * (z0 * z0 * z0) - + 4 * (C * C * C * C) * v * y0 / (B * B) + + 4 * (C * C) * u * x0 * (y0 * y0) / (B * B) + + 4 * (C * C) * v * (x0 * x0) * y0 / (B * B) + + 4 * (C * C) * v * y0 * (z0 * z0) / (B * B) + + 4 * (C * C) * w * (y0 * y0) * z0 / (B * B) + + 4 * (C * C * C * C) * v * (y0 * y0 * y0) / (B * B * B * B); + e = (A * A * A * A) - 2 * (A * A) * (C * C) - 2 * (A * A) * (x0 * x0) - + 2 * (A * A) * (z0 * z0) + 2 * (A * A) * (C * C) * (y0 * y0) / (B * B) + + (C * C * C * C) - 2 * (C * C) * (x0 * x0) - 2 * (C * C) * (z0 * z0) + + (x0 * x0 * x0 * x0) + 2 * (x0 * x0) * (z0 * z0) + (z0 * z0 * z0 * z0) - + 2 * (C * C * C * C) * (y0 * y0) / (B * B) + + 2 * (C * C) * (x0 * x0) * (y0 * y0) / (B * B) + + 2 * (C * C) * (y0 * y0) * (z0 * z0) / (B * B) + + (C * C * C * C) * (y0 * y0 * y0 * y0) / (B * B * B * B); // std::array roots; @@ -1518,158 +1500,143 @@ double SurfaceZTorus::distance(Position r, Direction ang, bool coincident) const double a, b, c, d, e; // coefficients of quartic // 4th order coefficient - a = std::pow(u, 4) + 2 * std::pow(u, 2) * std::pow(v, 2) + std::pow(v, 4) + - 2 * std::pow(C, 2) * std::pow(u, 2) * std::pow(w, 2) / std::pow(B, 2) + - 2 * std::pow(C, 2) * std::pow(v, 2) * std::pow(w, 2) / std::pow(B, 2) + - std::pow(C, 4) * std::pow(w, 4) / std::pow(B, 4); + a = (u * u * u * u) + 2 * (u * u) * (v * v) + (v * v * v * v) + + 2 * (C * C) * (u * u) * (w * w) / (B * B) + + 2 * (C * C) * (v * v) * (w * w) / (B * B) + + (C * C * C * C) * (w * w * w * w) / (B * B * B * B); // 3rd order coefficient - b = -4 * std::pow(u, 3) * x0 + 4 * std::pow(u, 3) * xr - - 4 * std::pow(u, 2) * v * y0 + 4 * std::pow(u, 2) * v * yr - - 4 * u * std::pow(v, 2) * x0 + 4 * u * std::pow(v, 2) * xr - - 4 * std::pow(v, 3) * y0 + 4 * std::pow(v, 3) * yr - - 4 * std::pow(C, 2) * std::pow(u, 2) * w * z0 / std::pow(B, 2) + - 4 * std::pow(C, 2) * std::pow(u, 2) * w * zr / std::pow(B, 2) - - 4 * std::pow(C, 2) * u * std::pow(w, 2) * x0 / std::pow(B, 2) + - 4 * std::pow(C, 2) * u * std::pow(w, 2) * xr / std::pow(B, 2) - - 4 * std::pow(C, 2) * std::pow(v, 2) * w * z0 / std::pow(B, 2) + - 4 * std::pow(C, 2) * std::pow(v, 2) * w * zr / std::pow(B, 2) - - 4 * std::pow(C, 2) * v * std::pow(w, 2) * y0 / std::pow(B, 2) + - 4 * std::pow(C, 2) * v * std::pow(w, 2) * yr / std::pow(B, 2) - - 4 * std::pow(C, 4) * std::pow(w, 3) * z0 / std::pow(B, 4) + - 4 * std::pow(C, 4) * std::pow(w, 3) * zr / std::pow(B, 4); + b = -4 * (u * u * u) * x0 + 4 * (u * u * u) * xr - 4 * (u * u) * v * y0 + + 4 * (u * u) * v * yr - 4 * u * (v * v) * x0 + 4 * u * (v * v) * xr - + 4 * (v * v * v) * y0 + 4 * (v * v * v) * yr - + 4 * (C * C) * (u * u) * w * z0 / (B * B) + + 4 * (C * C) * (u * u) * w * zr / (B * B) - + 4 * (C * C) * u * (w * w) * x0 / (B * B) + + 4 * (C * C) * u * (w * w) * xr / (B * B) - + 4 * (C * C) * (v * v) * w * z0 / (B * B) + + 4 * (C * C) * (v * v) * w * zr / (B * B) - + 4 * (C * C) * v * (w * w) * y0 / (B * B) + + 4 * (C * C) * v * (w * w) * yr / (B * B) - + 4 * (C * C * C * C) * (w * w * w) * z0 / (B * B * B * B) + + 4 * (C * C * C * C) * (w * w * w) * zr / (B * B * B * B); // 2nd order coefficient - c = - -2 * std::pow(A, 2) * std::pow(u, 2) - 2 * std::pow(A, 2) * std::pow(v, 2) + - 2 * std::pow(A, 2) * std::pow(C, 2) * std::pow(w, 2) / std::pow(B, 2) - - 2 * std::pow(C, 2) * std::pow(u, 2) - 2 * std::pow(C, 2) * std::pow(v, 2) + - 6 * std::pow(u, 2) * std::pow(x0, 2) - 12 * std::pow(u, 2) * x0 * xr + - 6 * std::pow(u, 2) * std::pow(xr, 2) + - 2 * std::pow(u, 2) * std::pow(y0, 2) - 4 * std::pow(u, 2) * y0 * yr + - 2 * std::pow(u, 2) * std::pow(yr, 2) + 8 * u * v * x0 * y0 - - 8 * u * v * x0 * yr - 8 * u * v * xr * y0 + 8 * u * v * xr * yr + - 2 * std::pow(v, 2) * std::pow(x0, 2) - 4 * std::pow(v, 2) * x0 * xr + - 2 * std::pow(v, 2) * std::pow(xr, 2) + - 6 * std::pow(v, 2) * std::pow(y0, 2) - 12 * std::pow(v, 2) * y0 * yr + - 6 * std::pow(v, 2) * std::pow(yr, 2) - - 2 * std::pow(C, 4) * std::pow(w, 2) / std::pow(B, 2) + - 2 * std::pow(C, 2) * std::pow(u, 2) * std::pow(z0, 2) / std::pow(B, 2) - - 4 * std::pow(C, 2) * std::pow(u, 2) * z0 * zr / std::pow(B, 2) + - 2 * std::pow(C, 2) * std::pow(u, 2) * std::pow(zr, 2) / std::pow(B, 2) + - 8 * std::pow(C, 2) * u * w * x0 * z0 / std::pow(B, 2) - - 8 * std::pow(C, 2) * u * w * x0 * zr / std::pow(B, 2) - - 8 * std::pow(C, 2) * u * w * xr * z0 / std::pow(B, 2) + - 8 * std::pow(C, 2) * u * w * xr * zr / std::pow(B, 2) + - 2 * std::pow(C, 2) * std::pow(v, 2) * std::pow(z0, 2) / std::pow(B, 2) - - 4 * std::pow(C, 2) * std::pow(v, 2) * z0 * zr / std::pow(B, 2) + - 2 * std::pow(C, 2) * std::pow(v, 2) * std::pow(zr, 2) / std::pow(B, 2) + - 8 * std::pow(C, 2) * v * w * y0 * z0 / std::pow(B, 2) - - 8 * std::pow(C, 2) * v * w * y0 * zr / std::pow(B, 2) - - 8 * std::pow(C, 2) * v * w * yr * z0 / std::pow(B, 2) + - 8 * std::pow(C, 2) * v * w * yr * zr / std::pow(B, 2) + - 2 * std::pow(C, 2) * std::pow(w, 2) * std::pow(x0, 2) / std::pow(B, 2) - - 4 * std::pow(C, 2) * std::pow(w, 2) * x0 * xr / std::pow(B, 2) + - 2 * std::pow(C, 2) * std::pow(w, 2) * std::pow(xr, 2) / std::pow(B, 2) + - 2 * std::pow(C, 2) * std::pow(w, 2) * std::pow(y0, 2) / std::pow(B, 2) - - 4 * std::pow(C, 2) * std::pow(w, 2) * y0 * yr / std::pow(B, 2) + - 2 * std::pow(C, 2) * std::pow(w, 2) * std::pow(yr, 2) / std::pow(B, 2) + - 6 * std::pow(C, 4) * std::pow(w, 2) * std::pow(z0, 2) / std::pow(B, 4) - - 12 * std::pow(C, 4) * std::pow(w, 2) * z0 * zr / std::pow(B, 4) + - 6 * std::pow(C, 4) * std::pow(w, 2) * std::pow(zr, 2) / std::pow(B, 4); + c = -2 * (A * A) * (u * u) - 2 * (A * A) * (v * v) + + 2 * (A * A) * (C * C) * (w * w) / (B * B) - 2 * (C * C) * (u * u) - + 2 * (C * C) * (v * v) + 6 * (u * u) * (x0 * x0) - 12 * (u * u) * x0 * xr + + 6 * (u * u) * (xr * xr) + 2 * (u * u) * (y0 * y0) - + 4 * (u * u) * y0 * yr + 2 * (u * u) * (yr * yr) + 8 * u * v * x0 * y0 - + 8 * u * v * x0 * yr - 8 * u * v * xr * y0 + 8 * u * v * xr * yr + + 2 * (v * v) * (x0 * x0) - 4 * (v * v) * x0 * xr + + 2 * (v * v) * (xr * xr) + 6 * (v * v) * (y0 * y0) - + 12 * (v * v) * y0 * yr + 6 * (v * v) * (yr * yr) - + 2 * (C * C * C * C) * (w * w) / (B * B) + + 2 * (C * C) * (u * u) * (z0 * z0) / (B * B) - + 4 * (C * C) * (u * u) * z0 * zr / (B * B) + + 2 * (C * C) * (u * u) * (zr * zr) / (B * B) + + 8 * (C * C) * u * w * x0 * z0 / (B * B) - + 8 * (C * C) * u * w * x0 * zr / (B * B) - + 8 * (C * C) * u * w * xr * z0 / (B * B) + + 8 * (C * C) * u * w * xr * zr / (B * B) + + 2 * (C * C) * (v * v) * (z0 * z0) / (B * B) - + 4 * (C * C) * (v * v) * z0 * zr / (B * B) + + 2 * (C * C) * (v * v) * (zr * zr) / (B * B) + + 8 * (C * C) * v * w * y0 * z0 / (B * B) - + 8 * (C * C) * v * w * y0 * zr / (B * B) - + 8 * (C * C) * v * w * yr * z0 / (B * B) + + 8 * (C * C) * v * w * yr * zr / (B * B) + + 2 * (C * C) * (w * w) * (x0 * x0) / (B * B) - + 4 * (C * C) * (w * w) * x0 * xr / (B * B) + + 2 * (C * C) * (w * w) * (xr * xr) / (B * B) + + 2 * (C * C) * (w * w) * (y0 * y0) / (B * B) - + 4 * (C * C) * (w * w) * y0 * yr / (B * B) + + 2 * (C * C) * (w * w) * (yr * yr) / (B * B) + + 6 * (C * C * C * C) * (w * w) * (z0 * z0) / (B * B * B * B) - + 12 * (C * C * C * C) * (w * w) * z0 * zr / (B * B * B * B) + + 6 * (C * C * C * C) * (w * w) * (zr * zr) / (B * B * B * B); // the 1st order terms - d = 4 * std::pow(A, 2) * u * x0 - 4 * std::pow(A, 2) * u * xr + - 4 * std::pow(A, 2) * v * y0 - 4 * std::pow(A, 2) * v * yr - - 4 * std::pow(A, 2) * std::pow(C, 2) * w * z0 / std::pow(B, 2) + - 4 * std::pow(A, 2) * std::pow(C, 2) * w * zr / std::pow(B, 2) + - 4 * std::pow(C, 2) * u * x0 - 4 * std::pow(C, 2) * u * xr + - 4 * std::pow(C, 2) * v * y0 - 4 * std::pow(C, 2) * v * yr - - 4 * u * std::pow(x0, 3) + 12 * u * std::pow(x0, 2) * xr - - 12 * u * x0 * std::pow(xr, 2) - 4 * u * x0 * std::pow(y0, 2) + - 8 * u * x0 * y0 * yr - 4 * u * x0 * std::pow(yr, 2) + - 4 * u * std::pow(xr, 3) + 4 * u * xr * std::pow(y0, 2) - - 8 * u * xr * y0 * yr + 4 * u * xr * std::pow(yr, 2) - - 4 * v * std::pow(x0, 2) * y0 + 4 * v * std::pow(x0, 2) * yr + - 8 * v * x0 * xr * y0 - 8 * v * x0 * xr * yr - - 4 * v * std::pow(xr, 2) * y0 + 4 * v * std::pow(xr, 2) * yr - - 4 * v * std::pow(y0, 3) + 12 * v * std::pow(y0, 2) * yr - - 12 * v * y0 * std::pow(yr, 2) + 4 * v * std::pow(yr, 3) + - 4 * std::pow(C, 4) * w * z0 / std::pow(B, 2) - - 4 * std::pow(C, 4) * w * zr / std::pow(B, 2) - - 4 * std::pow(C, 2) * u * x0 * std::pow(z0, 2) / std::pow(B, 2) + - 8 * std::pow(C, 2) * u * x0 * z0 * zr / std::pow(B, 2) - - 4 * std::pow(C, 2) * u * x0 * std::pow(zr, 2) / std::pow(B, 2) + - 4 * std::pow(C, 2) * u * xr * std::pow(z0, 2) / std::pow(B, 2) - - 8 * std::pow(C, 2) * u * xr * z0 * zr / std::pow(B, 2) + - 4 * std::pow(C, 2) * u * xr * std::pow(zr, 2) / std::pow(B, 2) - - 4 * std::pow(C, 2) * v * y0 * std::pow(z0, 2) / std::pow(B, 2) + - 8 * std::pow(C, 2) * v * y0 * z0 * zr / std::pow(B, 2) - - 4 * std::pow(C, 2) * v * y0 * std::pow(zr, 2) / std::pow(B, 2) + - 4 * std::pow(C, 2) * v * yr * std::pow(z0, 2) / std::pow(B, 2) - - 8 * std::pow(C, 2) * v * yr * z0 * zr / std::pow(B, 2) + - 4 * std::pow(C, 2) * v * yr * std::pow(zr, 2) / std::pow(B, 2) - - 4 * std::pow(C, 2) * w * std::pow(x0, 2) * z0 / std::pow(B, 2) + - 4 * std::pow(C, 2) * w * std::pow(x0, 2) * zr / std::pow(B, 2) + - 8 * std::pow(C, 2) * w * x0 * xr * z0 / std::pow(B, 2) - - 8 * std::pow(C, 2) * w * x0 * xr * zr / std::pow(B, 2) - - 4 * std::pow(C, 2) * w * std::pow(xr, 2) * z0 / std::pow(B, 2) + - 4 * std::pow(C, 2) * w * std::pow(xr, 2) * zr / std::pow(B, 2) - - 4 * std::pow(C, 2) * w * std::pow(y0, 2) * z0 / std::pow(B, 2) + - 4 * std::pow(C, 2) * w * std::pow(y0, 2) * zr / std::pow(B, 2) + - 8 * std::pow(C, 2) * w * y0 * yr * z0 / std::pow(B, 2) - - 8 * std::pow(C, 2) * w * y0 * yr * zr / std::pow(B, 2) - - 4 * std::pow(C, 2) * w * std::pow(yr, 2) * z0 / std::pow(B, 2) + - 4 * std::pow(C, 2) * w * std::pow(yr, 2) * zr / std::pow(B, 2) - - 4 * std::pow(C, 4) * w * std::pow(z0, 3) / std::pow(B, 4) + - 12 * std::pow(C, 4) * w * std::pow(z0, 2) * zr / std::pow(B, 4) - - 12 * std::pow(C, 4) * w * z0 * std::pow(zr, 2) / std::pow(B, 4) + - 4 * std::pow(C, 4) * w * std::pow(zr, 3) / std::pow(B, 4); + d = 4 * (A * A) * u * x0 - 4 * (A * A) * u * xr + 4 * (A * A) * v * y0 - + 4 * (A * A) * v * yr - 4 * (A * A) * (C * C) * w * z0 / (B * B) + + 4 * (A * A) * (C * C) * w * zr / (B * B) + 4 * (C * C) * u * x0 - + 4 * (C * C) * u * xr + 4 * (C * C) * v * y0 - 4 * (C * C) * v * yr - + 4 * u * (x0 * x0 * x0) + 12 * u * (x0 * x0) * xr - + 12 * u * x0 * (xr * xr) - 4 * u * x0 * (y0 * y0) + 8 * u * x0 * y0 * yr - + 4 * u * x0 * (yr * yr) + 4 * u * (xr * xr * xr) + 4 * u * xr * (y0 * y0) - + 8 * u * xr * y0 * yr + 4 * u * xr * (yr * yr) - 4 * v * (x0 * x0) * y0 + + 4 * v * (x0 * x0) * yr + 8 * v * x0 * xr * y0 - 8 * v * x0 * xr * yr - + 4 * v * (xr * xr) * y0 + 4 * v * (xr * xr) * yr - 4 * v * (y0 * y0 * y0) + + 12 * v * (y0 * y0) * yr - 12 * v * y0 * (yr * yr) + + 4 * v * (yr * yr * yr) + 4 * (C * C * C * C) * w * z0 / (B * B) - + 4 * (C * C * C * C) * w * zr / (B * B) - + 4 * (C * C) * u * x0 * (z0 * z0) / (B * B) + + 8 * (C * C) * u * x0 * z0 * zr / (B * B) - + 4 * (C * C) * u * x0 * (zr * zr) / (B * B) + + 4 * (C * C) * u * xr * (z0 * z0) / (B * B) - + 8 * (C * C) * u * xr * z0 * zr / (B * B) + + 4 * (C * C) * u * xr * (zr * zr) / (B * B) - + 4 * (C * C) * v * y0 * (z0 * z0) / (B * B) + + 8 * (C * C) * v * y0 * z0 * zr / (B * B) - + 4 * (C * C) * v * y0 * (zr * zr) / (B * B) + + 4 * (C * C) * v * yr * (z0 * z0) / (B * B) - + 8 * (C * C) * v * yr * z0 * zr / (B * B) + + 4 * (C * C) * v * yr * (zr * zr) / (B * B) - + 4 * (C * C) * w * (x0 * x0) * z0 / (B * B) + + 4 * (C * C) * w * (x0 * x0) * zr / (B * B) + + 8 * (C * C) * w * x0 * xr * z0 / (B * B) - + 8 * (C * C) * w * x0 * xr * zr / (B * B) - + 4 * (C * C) * w * (xr * xr) * z0 / (B * B) + + 4 * (C * C) * w * (xr * xr) * zr / (B * B) - + 4 * (C * C) * w * (y0 * y0) * z0 / (B * B) + + 4 * (C * C) * w * (y0 * y0) * zr / (B * B) + + 8 * (C * C) * w * y0 * yr * z0 / (B * B) - + 8 * (C * C) * w * y0 * yr * zr / (B * B) - + 4 * (C * C) * w * (yr * yr) * z0 / (B * B) + + 4 * (C * C) * w * (yr * yr) * zr / (B * B) - + 4 * (C * C * C * C) * w * (z0 * z0 * z0) / (B * B * B * B) + + 12 * (C * C * C * C) * w * (z0 * z0) * zr / (B * B * B * B) - + 12 * (C * C * C * C) * w * z0 * (zr * zr) / (B * B * B * B) + + 4 * (C * C * C * C) * w * (zr * zr * zr) / (B * B * B * B); // the 0th order terms - e = std::pow(A, 4) - 2 * std::pow(A, 2) * std::pow(C, 2) - - 2 * std::pow(A, 2) * std::pow(x0, 2) + 4 * std::pow(A, 2) * x0 * xr - - 2 * std::pow(A, 2) * std::pow(xr, 2) - - 2 * std::pow(A, 2) * std::pow(y0, 2) + 4 * std::pow(A, 2) * y0 * yr - - 2 * std::pow(A, 2) * std::pow(yr, 2) + - 2 * std::pow(A, 2) * std::pow(C, 2) * std::pow(z0, 2) / std::pow(B, 2) - - 4 * std::pow(A, 2) * std::pow(C, 2) * z0 * zr / std::pow(B, 2) + - 2 * std::pow(A, 2) * std::pow(C, 2) * std::pow(zr, 2) / std::pow(B, 2) + - std::pow(C, 4) - 2 * std::pow(C, 2) * std::pow(x0, 2) + - 4 * std::pow(C, 2) * x0 * xr - 2 * std::pow(C, 2) * std::pow(xr, 2) - - 2 * std::pow(C, 2) * std::pow(y0, 2) + 4 * std::pow(C, 2) * y0 * yr - - 2 * std::pow(C, 2) * std::pow(yr, 2) + std::pow(x0, 4) - - 4 * std::pow(x0, 3) * xr + 6 * std::pow(x0, 2) * std::pow(xr, 2) + - 2 * std::pow(x0, 2) * std::pow(y0, 2) - 4 * std::pow(x0, 2) * y0 * yr + - 2 * std::pow(x0, 2) * std::pow(yr, 2) - 4 * x0 * std::pow(xr, 3) - - 4 * x0 * xr * std::pow(y0, 2) + 8 * x0 * xr * y0 * yr - - 4 * x0 * xr * std::pow(yr, 2) + std::pow(xr, 4) + - 2 * std::pow(xr, 2) * std::pow(y0, 2) - 4 * std::pow(xr, 2) * y0 * yr + - 2 * std::pow(xr, 2) * std::pow(yr, 2) + std::pow(y0, 4) - - 4 * std::pow(y0, 3) * yr + 6 * std::pow(y0, 2) * std::pow(yr, 2) - - 4 * y0 * std::pow(yr, 3) + std::pow(yr, 4) - - 2 * std::pow(C, 4) * std::pow(z0, 2) / std::pow(B, 2) + - 4 * std::pow(C, 4) * z0 * zr / std::pow(B, 2) - - 2 * std::pow(C, 4) * std::pow(zr, 2) / std::pow(B, 2) + - 2 * std::pow(C, 2) * std::pow(x0, 2) * std::pow(z0, 2) / std::pow(B, 2) - - 4 * std::pow(C, 2) * std::pow(x0, 2) * z0 * zr / std::pow(B, 2) + - 2 * std::pow(C, 2) * std::pow(x0, 2) * std::pow(zr, 2) / std::pow(B, 2) - - 4 * std::pow(C, 2) * x0 * xr * std::pow(z0, 2) / std::pow(B, 2) + - 8 * std::pow(C, 2) * x0 * xr * z0 * zr / std::pow(B, 2) - - 4 * std::pow(C, 2) * x0 * xr * std::pow(zr, 2) / std::pow(B, 2) + - 2 * std::pow(C, 2) * std::pow(xr, 2) * std::pow(z0, 2) / std::pow(B, 2) - - 4 * std::pow(C, 2) * std::pow(xr, 2) * z0 * zr / std::pow(B, 2) + - 2 * std::pow(C, 2) * std::pow(xr, 2) * std::pow(zr, 2) / std::pow(B, 2) + - 2 * std::pow(C, 2) * std::pow(y0, 2) * std::pow(z0, 2) / std::pow(B, 2) - - 4 * std::pow(C, 2) * std::pow(y0, 2) * z0 * zr / std::pow(B, 2) + - 2 * std::pow(C, 2) * std::pow(y0, 2) * std::pow(zr, 2) / std::pow(B, 2) - - 4 * std::pow(C, 2) * y0 * yr * std::pow(z0, 2) / std::pow(B, 2) + - 8 * std::pow(C, 2) * y0 * yr * z0 * zr / std::pow(B, 2) - - 4 * std::pow(C, 2) * y0 * yr * std::pow(zr, 2) / std::pow(B, 2) + - 2 * std::pow(C, 2) * std::pow(yr, 2) * std::pow(z0, 2) / std::pow(B, 2) - - 4 * std::pow(C, 2) * std::pow(yr, 2) * z0 * zr / std::pow(B, 2) + - 2 * std::pow(C, 2) * std::pow(yr, 2) * std::pow(zr, 2) / std::pow(B, 2) + - std::pow(C, 4) * std::pow(z0, 4) / std::pow(B, 4) - - 4 * std::pow(C, 4) * std::pow(z0, 3) * zr / std::pow(B, 4) + - 6 * std::pow(C, 4) * std::pow(z0, 2) * std::pow(zr, 2) / std::pow(B, 4) - - 4 * std::pow(C, 4) * z0 * std::pow(zr, 3) / std::pow(B, 4) + - std::pow(C, 4) * std::pow(zr, 4) / std::pow(B, 4); + e = + (A * A * A * A) - 2 * (A * A) * (C * C) - 2 * (A * A) * (x0 * x0) + + 4 * (A * A) * x0 * xr - 2 * (A * A) * (xr * xr) - 2 * (A * A) * (y0 * y0) + + 4 * (A * A) * y0 * yr - 2 * (A * A) * (yr * yr) + + 2 * (A * A) * (C * C) * (z0 * z0) / (B * B) - + 4 * (A * A) * (C * C) * z0 * zr / (B * B) + + 2 * (A * A) * (C * C) * (zr * zr) / (B * B) + (C * C * C * C) - + 2 * (C * C) * (x0 * x0) + 4 * (C * C) * x0 * xr - 2 * (C * C) * (xr * xr) - + 2 * (C * C) * (y0 * y0) + 4 * (C * C) * y0 * yr - 2 * (C * C) * (yr * yr) + + (x0 * x0 * x0 * x0) - 4 * (x0 * x0 * x0) * xr + 6 * (x0 * x0) * (xr * xr) + + 2 * (x0 * x0) * (y0 * y0) - 4 * (x0 * x0) * y0 * yr + + 2 * (x0 * x0) * (yr * yr) - 4 * x0 * (xr * xr * xr) - + 4 * x0 * xr * (y0 * y0) + 8 * x0 * xr * y0 * yr - 4 * x0 * xr * (yr * yr) + + (xr * xr * xr * xr) + 2 * (xr * xr) * (y0 * y0) - 4 * (xr * xr) * y0 * yr + + 2 * (xr * xr) * (yr * yr) + (y0 * y0 * y0 * y0) - 4 * (y0 * y0 * y0) * yr + + 6 * (y0 * y0) * (yr * yr) - 4 * y0 * (yr * yr * yr) + (yr * yr * yr * yr) - + 2 * (C * C * C * C) * (z0 * z0) / (B * B) + + 4 * (C * C * C * C) * z0 * zr / (B * B) - + 2 * (C * C * C * C) * (zr * zr) / (B * B) + + 2 * (C * C) * (x0 * x0) * (z0 * z0) / (B * B) - + 4 * (C * C) * (x0 * x0) * z0 * zr / (B * B) + + 2 * (C * C) * (x0 * x0) * (zr * zr) / (B * B) - + 4 * (C * C) * x0 * xr * (z0 * z0) / (B * B) + + 8 * (C * C) * x0 * xr * z0 * zr / (B * B) - + 4 * (C * C) * x0 * xr * (zr * zr) / (B * B) + + 2 * (C * C) * (xr * xr) * (z0 * z0) / (B * B) - + 4 * (C * C) * (xr * xr) * z0 * zr / (B * B) + + 2 * (C * C) * (xr * xr) * (zr * zr) / (B * B) + + 2 * (C * C) * (y0 * y0) * (z0 * z0) / (B * B) - + 4 * (C * C) * (y0 * y0) * z0 * zr / (B * B) + + 2 * (C * C) * (y0 * y0) * (zr * zr) / (B * B) - + 4 * (C * C) * y0 * yr * (z0 * z0) / (B * B) + + 8 * (C * C) * y0 * yr * z0 * zr / (B * B) - + 4 * (C * C) * y0 * yr * (zr * zr) / (B * B) + + 2 * (C * C) * (yr * yr) * (z0 * z0) / (B * B) - + 4 * (C * C) * (yr * yr) * z0 * zr / (B * B) + + 2 * (C * C) * (yr * yr) * (zr * zr) / (B * B) + + (C * C * C * C) * (z0 * z0 * z0 * z0) / (B * B * B * B) - + 4 * (C * C * C * C) * (z0 * z0 * z0) * zr / (B * B * B * B) + + 6 * (C * C * C * C) * (z0 * z0) * (zr * zr) / (B * B * B * B) - + 4 * (C * C * C * C) * z0 * (zr * zr * zr) / (B * B * B * B) + + (C * C * C * C) * (zr * zr * zr * zr) / (B * B * B * B); // std::array roots; From 70fcfe4936fbd38787c3f78e0b4f9411224f8021 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 20 Dec 2021 17:52:38 -0500 Subject: [PATCH 08/32] Greatly simplify calculation of quartic coefficients --- src/surface.cpp | 317 +++++++++--------------------------------------- 1 file changed, 54 insertions(+), 263 deletions(-) diff --git a/src/surface.cpp b/src/surface.cpp index 82a0d53da6..899099719c 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -1197,64 +1197,27 @@ double SurfaceXTorus::distance(Position r, Direction ang, bool coincident) const double u = ang.x; double v = ang.y; double w = ang.z; + double x = r.x - x0_; + double y = r.y - y0_; + double z = r.z - z0_; - double xr = r.x; - double yr = r.y; - double zr = r.z; + // Coefficients for equation: (c2 t^2 + c1 t + c0)^2 = d2 t^2 + d1 t + d0 + double D = (C_ * C_) / (B_ * B_); + double c2 = D * u * u + v * v + w * w; + double c1 = 2 * (D * u * x + v * y + w * z); + double c0 = D * x * x + y * y + z * z + A_ * A_ - C_ * C_; + double four_A2 = 4 * A_ * A_; + double d2 = four_A2 * (v * v + w * w); + double d1 = 2 * four_A2 * (v * y + w * z); + double d0 = four_A2 * (y * y + z * z); - double A = A_; - double B = B_; - double C = C_; + // Coefficient for equation: a t^4 + b t^3 + c t^2 + d t + e = 0 + double a = c2 * c2; + double b = 2 * c1 * c2; + double c = c1 * c1 + 2 * c0 * c2 - d2; + double d = 2 * c0 * c1 - d1; + double e = c0 * c0 - d0; - double x0 = x0_ + xr; - double y0 = y0_ + yr; - double z0 = z0_ + zr; - - double a, b, c, d, e; // coefficients of quartic - - // a is always 1 maybe save the maths? - a = (v * v * v * v) + 2 * (v * v) * (w * w) + (w * w * w * w) + - 2 * (C * C) * (u * u) * (v * v) / (B * B) + - 2 * (C * C) * (u * u) * (w * w) / (B * B) + - (C * C * C * C) * (u * u * u * u) / (B * B * B * B); - b = 4 * (v * v * v) * y0 + 4 * (v * v) * w * z0 + 4 * v * (w * w) * y0 + - 4 * (w * w * w) * z0 + 4 * (C * C) * (u * u) * v * y0 / (B * B) + - 4 * (C * C) * (u * u) * w * z0 / (B * B) + - 4 * (C * C) * u * (v * v) * x0 / (B * B) + - 4 * (C * C) * u * (w * w) * x0 / (B * B) + - 4 * (C * C * C * C) * (u * u * u) * x0 / (B * B * B * B); - c = -2 * (A * A) * (v * v) - 2 * (A * A) * (w * w) + - 2 * (A * A) * (C * C) * (u * u) / (B * B) - 2 * (C * C) * (v * v) - - 2 * (C * C) * (w * w) + 6 * (v * v) * (y0 * y0) + - 2 * (v * v) * (z0 * z0) + 8 * v * w * y0 * z0 + 2 * (w * w) * (y0 * y0) + - 6 * (w * w) * (z0 * z0) - 2 * (C * C * C * C) * (u * u) / (B * B) + - 2 * (C * C) * (u * u) * (y0 * y0) / (B * B) + - 2 * (C * C) * (u * u) * (z0 * z0) / (B * B) + - 8 * (C * C) * u * v * x0 * y0 / (B * B) + - 8 * (C * C) * u * w * x0 * z0 / (B * B) + - 2 * (C * C) * (v * v) * (x0 * x0) / (B * B) + - 2 * (C * C) * (w * w) * (x0 * x0) / (B * B) + - 6 * (C * C * C * C) * (u * u) * (x0 * x0) / (B * B * B * B); - d = -4 * (A * A) * v * y0 - 4 * (A * A) * w * z0 + - 4 * (A * A) * (C * C) * u * x0 / (B * B) - 4 * (C * C) * v * y0 - - 4 * (C * C) * w * z0 + 4 * v * (y0 * y0 * y0) + 4 * v * y0 * (z0 * z0) + - 4 * w * (y0 * y0) * z0 + 4 * w * (z0 * z0 * z0) - - 4 * (C * C * C * C) * u * x0 / (B * B) + - 4 * (C * C) * u * x0 * (y0 * y0) / (B * B) + - 4 * (C * C) * u * x0 * (z0 * z0) / (B * B) + - 4 * (C * C) * v * (x0 * x0) * y0 / (B * B) + - 4 * (C * C) * w * (x0 * x0) * z0 / (B * B) + - 4 * (C * C * C * C) * u * (x0 * x0 * x0) / (B * B * B * B); - e = (A * A * A * A) - 2 * (A * A) * (C * C) - 2 * (A * A) * (y0 * y0) - - 2 * (A * A) * (z0 * z0) + 2 * (A * A) * (C * C) * (x0 * x0) / (B * B) + - (C * C * C * C) - 2 * (C * C) * (y0 * y0) - 2 * (C * C) * (z0 * z0) + - (y0 * y0 * y0 * y0) + 2 * (y0 * y0) * (z0 * z0) + (z0 * z0 * z0 * z0) - - 2 * (C * C * C * C) * (x0 * x0) / (B * B) + - 2 * (C * C) * (x0 * x0) * (y0 * y0) / (B * B) + - 2 * (C * C) * (x0 * x0) * (z0 * z0) / (B * B) + - (C * C * C * C) * (x0 * x0 * x0 * x0) / (B * B * B * B); - - // std::array roots; quartic_solve(b, c, d, e, roots); @@ -1338,63 +1301,27 @@ double SurfaceYTorus::distance(Position r, Direction ang, bool coincident) const double u = ang.x; double v = ang.y; double w = ang.z; + double x = r.x - x0_; + double y = r.y - y0_; + double z = r.z - z0_; - double xr = r.x; - double yr = r.y; - double zr = r.z; + // Coefficients for equation: (c2 t^2 + c1 t + c0)^2 = d2 t^2 + d1 t + d0 + double D = (C_ * C_) / (B_ * B_); + double c2 = u * u + D * v * v + w * w; + double c1 = 2 * (u * x + D * v * y + w * z); + double c0 = x * x + D * y * y + z * z + A_ * A_ - C_ * C_; + double four_A2 = 4 * A_ * A_; + double d2 = four_A2 * (u * u + w * w); + double d1 = 2 * four_A2 * (u * x + w * z); + double d0 = four_A2 * (x * x + z * z); - double A = A_; - double B = B_; - double C = C_; + // Coefficient for equation: a t^4 + b t^3 + c t^2 + d t + e = 0 + double a = c2 * c2; + double b = 2 * c1 * c2; + double c = c1 * c1 + 2 * c0 * c2 - d2; + double d = 2 * c0 * c1 - d1; + double e = c0 * c0 - d0; - double x0 = x0_ + xr; - double y0 = y0_ + yr; - double z0 = z0_ + zr; - - double a, b, c, d, e; // coefficients of quartic - - a = (u * u * u * u) + 2 * (u * u) * (w * w) + (w * w * w * w) + - 2 * (C * C) * (u * u) * (v * v) / (B * B) + - 2 * (C * C) * (v * v) * (w * w) / (B * B) + - (C * C * C * C) * (v * v * v * v) / (B * B * B * B); - b = 4 * (u * u * u) * x0 + 4 * (u * u) * w * z0 + 4 * u * (w * w) * x0 + - 4 * (w * w * w) * z0 + 4 * (C * C) * (u * u) * v * y0 / (B * B) + - 4 * (C * C) * u * (v * v) * x0 / (B * B) + - 4 * (C * C) * (v * v) * w * z0 / (B * B) + - 4 * (C * C) * v * (w * w) * y0 / (B * B) + - 4 * (C * C * C * C) * (v * v * v) * y0 / (B * B * B * B); - c = -2 * (A * A) * (u * u) - 2 * (A * A) * (w * w) + - 2 * (A * A) * (C * C) * (v * v) / (B * B) - 2 * (C * C) * (u * u) - - 2 * (C * C) * (w * w) + 6 * (u * u) * (x0 * x0) + - 2 * (u * u) * (z0 * z0) + 8 * u * w * x0 * z0 + 2 * (w * w) * (x0 * x0) + - 6 * (w * w) * (z0 * z0) - 2 * (C * C * C * C) * (v * v) / (B * B) + - 2 * (C * C) * (u * u) * (y0 * y0) / (B * B) + - 8 * (C * C) * u * v * x0 * y0 / (B * B) + - 2 * (C * C) * (v * v) * (x0 * x0) / (B * B) + - 2 * (C * C) * (v * v) * (z0 * z0) / (B * B) + - 8 * (C * C) * v * w * y0 * z0 / (B * B) + - 2 * (C * C) * (w * w) * (y0 * y0) / (B * B) + - 6 * (C * C * C * C) * (v * v) * (y0 * y0) / (B * B * B * B); - d = -4 * (A * A) * u * x0 - 4 * (A * A) * w * z0 + - 4 * (A * A) * (C * C) * v * y0 / (B * B) - 4 * (C * C) * u * x0 - - 4 * (C * C) * w * z0 + 4 * u * (x0 * x0 * x0) + 4 * u * x0 * (z0 * z0) + - 4 * w * (x0 * x0) * z0 + 4 * w * (z0 * z0 * z0) - - 4 * (C * C * C * C) * v * y0 / (B * B) + - 4 * (C * C) * u * x0 * (y0 * y0) / (B * B) + - 4 * (C * C) * v * (x0 * x0) * y0 / (B * B) + - 4 * (C * C) * v * y0 * (z0 * z0) / (B * B) + - 4 * (C * C) * w * (y0 * y0) * z0 / (B * B) + - 4 * (C * C * C * C) * v * (y0 * y0 * y0) / (B * B * B * B); - e = (A * A * A * A) - 2 * (A * A) * (C * C) - 2 * (A * A) * (x0 * x0) - - 2 * (A * A) * (z0 * z0) + 2 * (A * A) * (C * C) * (y0 * y0) / (B * B) + - (C * C * C * C) - 2 * (C * C) * (x0 * x0) - 2 * (C * C) * (z0 * z0) + - (x0 * x0 * x0 * x0) + 2 * (x0 * x0) * (z0 * z0) + (z0 * z0 * z0 * z0) - - 2 * (C * C * C * C) * (y0 * y0) / (B * B) + - 2 * (C * C) * (x0 * x0) * (y0 * y0) / (B * B) + - 2 * (C * C) * (y0 * y0) * (z0 * z0) / (B * B) + - (C * C * C * C) * (y0 * y0 * y0 * y0) / (B * B * B * B); - - // std::array roots; quartic_solve(b, c, d, e, roots); @@ -1419,8 +1346,6 @@ double SurfaceYTorus::distance(Position r, Direction ang, bool coincident) const // otherwise no hit return INFTY; - - return 0; } Direction SurfaceYTorus::normal(Position r) const @@ -1484,161 +1409,27 @@ double SurfaceZTorus::distance(Position r, Direction ang, bool coincident) const double u = ang.x; double v = ang.y; double w = ang.z; + double x = r.x - x0_; + double y = r.y - y0_; + double z = r.z - z0_; - double xr = r.x; - double yr = r.y; - double zr = r.z; + // Coefficients for equation: (c2 t^2 + c1 t + c0)^2 = d2 t^2 + d1 t + d0 + double D = (C_ * C_) / (B_ * B_); + double c2 = u * u + v * v + D * w * w; + double c1 = 2 * (u * x + v * y + D * w * z); + double c0 = x * x + y * y + D * z * z + A_ * A_ - C_ * C_; + double four_A2 = 4 * A_ * A_; + double d2 = four_A2 * (u * u + v * v); + double d1 = 2 * four_A2 * (u * x + v * y); + double d0 = four_A2 * (x * x + y * y); - double A = A_; - double B = B_; - double C = C_; + // Coefficient for equation: a t^4 + b t^3 + c t^2 + d t + e = 0 + double a = c2 * c2; + double b = 2 * c1 * c2; + double c = c1 * c1 + 2 * c0 * c2 - d2; + double d = 2 * c0 * c1 - d1; + double e = c0 * c0 - d0; - double x0 = x0_; - double y0 = y0_; - double z0 = z0_; - - double a, b, c, d, e; // coefficients of quartic - - // 4th order coefficient - a = (u * u * u * u) + 2 * (u * u) * (v * v) + (v * v * v * v) + - 2 * (C * C) * (u * u) * (w * w) / (B * B) + - 2 * (C * C) * (v * v) * (w * w) / (B * B) + - (C * C * C * C) * (w * w * w * w) / (B * B * B * B); - // 3rd order coefficient - b = -4 * (u * u * u) * x0 + 4 * (u * u * u) * xr - 4 * (u * u) * v * y0 + - 4 * (u * u) * v * yr - 4 * u * (v * v) * x0 + 4 * u * (v * v) * xr - - 4 * (v * v * v) * y0 + 4 * (v * v * v) * yr - - 4 * (C * C) * (u * u) * w * z0 / (B * B) + - 4 * (C * C) * (u * u) * w * zr / (B * B) - - 4 * (C * C) * u * (w * w) * x0 / (B * B) + - 4 * (C * C) * u * (w * w) * xr / (B * B) - - 4 * (C * C) * (v * v) * w * z0 / (B * B) + - 4 * (C * C) * (v * v) * w * zr / (B * B) - - 4 * (C * C) * v * (w * w) * y0 / (B * B) + - 4 * (C * C) * v * (w * w) * yr / (B * B) - - 4 * (C * C * C * C) * (w * w * w) * z0 / (B * B * B * B) + - 4 * (C * C * C * C) * (w * w * w) * zr / (B * B * B * B); - // 2nd order coefficient - c = -2 * (A * A) * (u * u) - 2 * (A * A) * (v * v) + - 2 * (A * A) * (C * C) * (w * w) / (B * B) - 2 * (C * C) * (u * u) - - 2 * (C * C) * (v * v) + 6 * (u * u) * (x0 * x0) - 12 * (u * u) * x0 * xr + - 6 * (u * u) * (xr * xr) + 2 * (u * u) * (y0 * y0) - - 4 * (u * u) * y0 * yr + 2 * (u * u) * (yr * yr) + 8 * u * v * x0 * y0 - - 8 * u * v * x0 * yr - 8 * u * v * xr * y0 + 8 * u * v * xr * yr + - 2 * (v * v) * (x0 * x0) - 4 * (v * v) * x0 * xr + - 2 * (v * v) * (xr * xr) + 6 * (v * v) * (y0 * y0) - - 12 * (v * v) * y0 * yr + 6 * (v * v) * (yr * yr) - - 2 * (C * C * C * C) * (w * w) / (B * B) + - 2 * (C * C) * (u * u) * (z0 * z0) / (B * B) - - 4 * (C * C) * (u * u) * z0 * zr / (B * B) + - 2 * (C * C) * (u * u) * (zr * zr) / (B * B) + - 8 * (C * C) * u * w * x0 * z0 / (B * B) - - 8 * (C * C) * u * w * x0 * zr / (B * B) - - 8 * (C * C) * u * w * xr * z0 / (B * B) + - 8 * (C * C) * u * w * xr * zr / (B * B) + - 2 * (C * C) * (v * v) * (z0 * z0) / (B * B) - - 4 * (C * C) * (v * v) * z0 * zr / (B * B) + - 2 * (C * C) * (v * v) * (zr * zr) / (B * B) + - 8 * (C * C) * v * w * y0 * z0 / (B * B) - - 8 * (C * C) * v * w * y0 * zr / (B * B) - - 8 * (C * C) * v * w * yr * z0 / (B * B) + - 8 * (C * C) * v * w * yr * zr / (B * B) + - 2 * (C * C) * (w * w) * (x0 * x0) / (B * B) - - 4 * (C * C) * (w * w) * x0 * xr / (B * B) + - 2 * (C * C) * (w * w) * (xr * xr) / (B * B) + - 2 * (C * C) * (w * w) * (y0 * y0) / (B * B) - - 4 * (C * C) * (w * w) * y0 * yr / (B * B) + - 2 * (C * C) * (w * w) * (yr * yr) / (B * B) + - 6 * (C * C * C * C) * (w * w) * (z0 * z0) / (B * B * B * B) - - 12 * (C * C * C * C) * (w * w) * z0 * zr / (B * B * B * B) + - 6 * (C * C * C * C) * (w * w) * (zr * zr) / (B * B * B * B); - // the 1st order terms - d = 4 * (A * A) * u * x0 - 4 * (A * A) * u * xr + 4 * (A * A) * v * y0 - - 4 * (A * A) * v * yr - 4 * (A * A) * (C * C) * w * z0 / (B * B) + - 4 * (A * A) * (C * C) * w * zr / (B * B) + 4 * (C * C) * u * x0 - - 4 * (C * C) * u * xr + 4 * (C * C) * v * y0 - 4 * (C * C) * v * yr - - 4 * u * (x0 * x0 * x0) + 12 * u * (x0 * x0) * xr - - 12 * u * x0 * (xr * xr) - 4 * u * x0 * (y0 * y0) + 8 * u * x0 * y0 * yr - - 4 * u * x0 * (yr * yr) + 4 * u * (xr * xr * xr) + 4 * u * xr * (y0 * y0) - - 8 * u * xr * y0 * yr + 4 * u * xr * (yr * yr) - 4 * v * (x0 * x0) * y0 + - 4 * v * (x0 * x0) * yr + 8 * v * x0 * xr * y0 - 8 * v * x0 * xr * yr - - 4 * v * (xr * xr) * y0 + 4 * v * (xr * xr) * yr - 4 * v * (y0 * y0 * y0) + - 12 * v * (y0 * y0) * yr - 12 * v * y0 * (yr * yr) + - 4 * v * (yr * yr * yr) + 4 * (C * C * C * C) * w * z0 / (B * B) - - 4 * (C * C * C * C) * w * zr / (B * B) - - 4 * (C * C) * u * x0 * (z0 * z0) / (B * B) + - 8 * (C * C) * u * x0 * z0 * zr / (B * B) - - 4 * (C * C) * u * x0 * (zr * zr) / (B * B) + - 4 * (C * C) * u * xr * (z0 * z0) / (B * B) - - 8 * (C * C) * u * xr * z0 * zr / (B * B) + - 4 * (C * C) * u * xr * (zr * zr) / (B * B) - - 4 * (C * C) * v * y0 * (z0 * z0) / (B * B) + - 8 * (C * C) * v * y0 * z0 * zr / (B * B) - - 4 * (C * C) * v * y0 * (zr * zr) / (B * B) + - 4 * (C * C) * v * yr * (z0 * z0) / (B * B) - - 8 * (C * C) * v * yr * z0 * zr / (B * B) + - 4 * (C * C) * v * yr * (zr * zr) / (B * B) - - 4 * (C * C) * w * (x0 * x0) * z0 / (B * B) + - 4 * (C * C) * w * (x0 * x0) * zr / (B * B) + - 8 * (C * C) * w * x0 * xr * z0 / (B * B) - - 8 * (C * C) * w * x0 * xr * zr / (B * B) - - 4 * (C * C) * w * (xr * xr) * z0 / (B * B) + - 4 * (C * C) * w * (xr * xr) * zr / (B * B) - - 4 * (C * C) * w * (y0 * y0) * z0 / (B * B) + - 4 * (C * C) * w * (y0 * y0) * zr / (B * B) + - 8 * (C * C) * w * y0 * yr * z0 / (B * B) - - 8 * (C * C) * w * y0 * yr * zr / (B * B) - - 4 * (C * C) * w * (yr * yr) * z0 / (B * B) + - 4 * (C * C) * w * (yr * yr) * zr / (B * B) - - 4 * (C * C * C * C) * w * (z0 * z0 * z0) / (B * B * B * B) + - 12 * (C * C * C * C) * w * (z0 * z0) * zr / (B * B * B * B) - - 12 * (C * C * C * C) * w * z0 * (zr * zr) / (B * B * B * B) + - 4 * (C * C * C * C) * w * (zr * zr * zr) / (B * B * B * B); - // the 0th order terms - e = - (A * A * A * A) - 2 * (A * A) * (C * C) - 2 * (A * A) * (x0 * x0) + - 4 * (A * A) * x0 * xr - 2 * (A * A) * (xr * xr) - 2 * (A * A) * (y0 * y0) + - 4 * (A * A) * y0 * yr - 2 * (A * A) * (yr * yr) + - 2 * (A * A) * (C * C) * (z0 * z0) / (B * B) - - 4 * (A * A) * (C * C) * z0 * zr / (B * B) + - 2 * (A * A) * (C * C) * (zr * zr) / (B * B) + (C * C * C * C) - - 2 * (C * C) * (x0 * x0) + 4 * (C * C) * x0 * xr - 2 * (C * C) * (xr * xr) - - 2 * (C * C) * (y0 * y0) + 4 * (C * C) * y0 * yr - 2 * (C * C) * (yr * yr) + - (x0 * x0 * x0 * x0) - 4 * (x0 * x0 * x0) * xr + 6 * (x0 * x0) * (xr * xr) + - 2 * (x0 * x0) * (y0 * y0) - 4 * (x0 * x0) * y0 * yr + - 2 * (x0 * x0) * (yr * yr) - 4 * x0 * (xr * xr * xr) - - 4 * x0 * xr * (y0 * y0) + 8 * x0 * xr * y0 * yr - 4 * x0 * xr * (yr * yr) + - (xr * xr * xr * xr) + 2 * (xr * xr) * (y0 * y0) - 4 * (xr * xr) * y0 * yr + - 2 * (xr * xr) * (yr * yr) + (y0 * y0 * y0 * y0) - 4 * (y0 * y0 * y0) * yr + - 6 * (y0 * y0) * (yr * yr) - 4 * y0 * (yr * yr * yr) + (yr * yr * yr * yr) - - 2 * (C * C * C * C) * (z0 * z0) / (B * B) + - 4 * (C * C * C * C) * z0 * zr / (B * B) - - 2 * (C * C * C * C) * (zr * zr) / (B * B) + - 2 * (C * C) * (x0 * x0) * (z0 * z0) / (B * B) - - 4 * (C * C) * (x0 * x0) * z0 * zr / (B * B) + - 2 * (C * C) * (x0 * x0) * (zr * zr) / (B * B) - - 4 * (C * C) * x0 * xr * (z0 * z0) / (B * B) + - 8 * (C * C) * x0 * xr * z0 * zr / (B * B) - - 4 * (C * C) * x0 * xr * (zr * zr) / (B * B) + - 2 * (C * C) * (xr * xr) * (z0 * z0) / (B * B) - - 4 * (C * C) * (xr * xr) * z0 * zr / (B * B) + - 2 * (C * C) * (xr * xr) * (zr * zr) / (B * B) + - 2 * (C * C) * (y0 * y0) * (z0 * z0) / (B * B) - - 4 * (C * C) * (y0 * y0) * z0 * zr / (B * B) + - 2 * (C * C) * (y0 * y0) * (zr * zr) / (B * B) - - 4 * (C * C) * y0 * yr * (z0 * z0) / (B * B) + - 8 * (C * C) * y0 * yr * z0 * zr / (B * B) - - 4 * (C * C) * y0 * yr * (zr * zr) / (B * B) + - 2 * (C * C) * (yr * yr) * (z0 * z0) / (B * B) - - 4 * (C * C) * (yr * yr) * z0 * zr / (B * B) + - 2 * (C * C) * (yr * yr) * (zr * zr) / (B * B) + - (C * C * C * C) * (z0 * z0 * z0 * z0) / (B * B * B * B) - - 4 * (C * C * C * C) * (z0 * z0 * z0) * zr / (B * B * B * B) + - 6 * (C * C * C * C) * (z0 * z0) * (zr * zr) / (B * B * B * B) - - 4 * (C * C * C * C) * z0 * (zr * zr * zr) / (B * B * B * B) + - (C * C * C * C) * (zr * zr * zr * zr) / (B * B * B * B); - - // std::array roots; quartic_solve(b, c, d, e, roots); From 99284afca317cf29b72cdd9f891a4d628edcac18 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 20 Dec 2021 21:49:20 -0500 Subject: [PATCH 09/32] Fix errors in torus normals --- src/surface.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/surface.cpp b/src/surface.cpp index 899099719c..69dcb9ac5a 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -1259,7 +1259,7 @@ Direction SurfaceXTorus::normal(Position r) const // coefficients for the x, x^2, x^3 and x^4 term // but since we differeniate - we get x4 -> 4x3 double dx_4 = 4 * x * x * x * C2 * C2 / (B2 * B2); - double dx_2 = 4 * x * C2 / B2 * (z * z + 2 * y * y - 2 * C2 + 2 * A2); + double dx_2 = 4 * x * C2 / B2 * (z * z + y * y - C2 + A2); double dx = dx_4 + dx_2; // similarly to y double dy_4 = 4 * y * y * y; @@ -1371,7 +1371,7 @@ Direction SurfaceYTorus::normal(Position r) const double dx = dx_4 + dx_2; // similarly to y double dy_4 = 4 * y * y * y * C2 * C2 / (B2 * B2); - double dy_2 = 4 * y * (z * z + C2 * x * x / B2 - A2 - C2); + double dy_2 = 4 * y * C2 / B2 * (z * z + x * x + A2 - C2); double dy = dy_2 + dy_4; // and z double dz_4 = 4 * z * z * z; @@ -1471,11 +1471,11 @@ Direction SurfaceZTorus::normal(Position r) const // coefficients for the x, x^2, x^3 and x^4 term // but since we differeniate - we get x4 -> 4x3 double dx_4 = 1.0; - double dx_2 = -2. * (A2 - C2 + y * y + C2 * z * z / B2); + double dx_2 = -2. * (A2 + C2 - y * y - C2 * z * z / B2); double dx = 4. * dx_4 * x * x * x + 2. * dx_2 * x; // similarly to y double dy_4 = 1.0; - double dy_2 = -2. * (A2 - C2 + x * x + C2 * z * z / B2); + double dy_2 = -2. * (A2 + C2 - x * x - C2 * z * z / B2); double dy = 4. * dy_4 * y * y * y + 2. * dy_2 * y; // and z double dz_4 = std::pow(C_, 4) / std::pow(B_, 4); From b557e62b5d0652754030310fc7f3228d05ec16a1 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 20 Dec 2021 21:55:18 -0500 Subject: [PATCH 10/32] Much simpler torus normals --- src/surface.cpp | 102 ++++++++++++++++-------------------------------- 1 file changed, 33 insertions(+), 69 deletions(-) diff --git a/src/surface.cpp b/src/surface.cpp index 69dcb9ac5a..c63b54b9ad 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -1252,27 +1252,17 @@ Direction SurfaceXTorus::normal(Position r) const double y = r.y - y0_; double z = r.z - z0_; - double A2 = A_ * A_; - double B2 = B_ * B_; - double C2 = C_ * C_; - - // coefficients for the x, x^2, x^3 and x^4 term - // but since we differeniate - we get x4 -> 4x3 - double dx_4 = 4 * x * x * x * C2 * C2 / (B2 * B2); - double dx_2 = 4 * x * C2 / B2 * (z * z + y * y - C2 + A2); - double dx = dx_4 + dx_2; - // similarly to y - double dy_4 = 4 * y * y * y; - double dy_2 = 4 * y * (z * z + C2 * x * x / B2 - A2 - C2); - double dy = dy_2 + dy_4; - // and z - double dz_4 = 4 * z * z * z; - double dz_2 = 4 * z * (C2 * x * x / B2 + y * y - C2 - A2); - double dz = dz_2 + dz_4; - - double length = std::sqrt(dx * dx + dy * dy + dz * dz); - - return {dx / length, dy / length, dz / length}; + // f(x,y,z) = x^2/B^2 + (sqrt(y^2 + z^2) - A)^2/C^2 - 1 + // ∂f/∂x = 2x/B^2 + // ∂f/∂y = 2y(g - A)/(g*C^2) where g = sqrt(y^2 + z^2) + // ∂f/∂z = 2z(g - A)/(g*C^2) + // Multiplying by g*C^2*B^2 / 2 gives: + double g = std::sqrt(y * y + z * z); + double nx = C_ * C_ * g * x; + double ny = y * (g - A_) * B_ * B_; + double nz = z * (g - A_) * B_ * B_; + Direction n(nx, ny, nz); + return n / n.norm(); } SurfaceYTorus::SurfaceYTorus(pugi::xml_node surf_node) : CSGSurface(surf_node) @@ -1355,32 +1345,17 @@ Direction SurfaceYTorus::normal(Position r) const double y = r.y - y0_; double z = r.z - z0_; - double A2 = A_ * A_; - double B2 = B_ * B_; - double C2 = C_ * C_; - - // a**4 - 2*a**2*c**2 - 2*a**2*x**2 - 2*a**2*z**2 + 2*a**2*c**2*y**2/b**2 + - // c**4 - // - 2*c**2*x**2 - 2*c**2*z**2 + x**4 + 2*x**2*z**2 + z**4 - 2*c**4*y**2/b**2 - // + 2*c**2*x**2*y**2/b**2 + 2*c**2*y**2*z**2/b**2 + c**4*y**4/b**4 - - // coefficients for the x, x^2, x^3 and x^4 term - // but since we differeniate - we get x4 -> 4x3 - double dx_4 = 4 * x * x * x; - double dx_2 = 4 * x * (z * z + C2 * y * y / B2 - A2 - C2); - double dx = dx_4 + dx_2; - // similarly to y - double dy_4 = 4 * y * y * y * C2 * C2 / (B2 * B2); - double dy_2 = 4 * y * C2 / B2 * (z * z + x * x + A2 - C2); - double dy = dy_2 + dy_4; - // and z - double dz_4 = 4 * z * z * z; - double dz_2 = 4 * z * (C2 * y * y / B2 + x * x - C2 - A2); - double dz = dz_2 + dz_4; - - double length = std::sqrt(dx * dx + dy * dy + dz * dz); - - return {dx / length, dy / length, dz / length}; + // f(x,y,z) = y^2/B^2 + (sqrt(x^2 + z^2) - A)^2/C^2 - 1 + // ∂f/∂x = 2x(g - A)/(g*C^2) where g = sqrt(x^2 + z^2) + // ∂f/∂y = 2y/B^2 + // ∂f/∂z = 2z(g - A)/(g*C^2) + // Multiplying by g*C^2*B^2 / 2 gives: + double g = std::sqrt(x * x + z * z); + double nx = x * (g - A_) * B_ * B_; + double ny = C_ * C_ * g * y; + double nz = z * (g - A_) * B_ * B_; + Direction n(nx, ny, nz); + return n / n.norm(); } SurfaceZTorus::SurfaceZTorus(pugi::xml_node surf_node) : CSGSurface(surf_node) @@ -1464,28 +1439,17 @@ Direction SurfaceZTorus::normal(Position r) const double y = r.y - y0_; double z = r.z - z0_; - double A2 = A_ * A_; - double B2 = B_ * B_; - double C2 = C_ * C_; - - // coefficients for the x, x^2, x^3 and x^4 term - // but since we differeniate - we get x4 -> 4x3 - double dx_4 = 1.0; - double dx_2 = -2. * (A2 + C2 - y * y - C2 * z * z / B2); - double dx = 4. * dx_4 * x * x * x + 2. * dx_2 * x; - // similarly to y - double dy_4 = 1.0; - double dy_2 = -2. * (A2 + C2 - x * x - C2 * z * z / B2); - double dy = 4. * dy_4 * y * y * y + 2. * dy_2 * y; - // and z - double dz_4 = std::pow(C_, 4) / std::pow(B_, 4); - double dz_2 = 2. * (A2 * C2 / B2 - std::pow(C_, 4) / B2 + C2 * x * x / B2 + - C2 * y * y / B2); - double dz = 4. * dz_4 * z * z * z + 2. * dz_2 * z; - - double length = std::sqrt(dx * dx + dy * dy + dz * dz); - - return {dx / length, dy / length, dz / length}; + // f(x,y,z) = z^2/B^2 + (sqrt(x^2 + y^2) - A)^2/C^2 - 1 + // ∂f/∂x = 2x(g - A)/(g*C^2) where g = sqrt(x^2 + y^2) + // ∂f/∂y = 2y(g - A)/(g*C^2) + // ∂f/∂z = 2z/B^2 + // Multiplying by g*C^2*B^2 / 2 gives: + double g = std::sqrt(x * x + y * y); + double nx = x * (g - A_) * B_ * B_; + double ny = y * (g - A_) * B_ * B_; + double nz = C_ * C_ * g * z; + Position n(nx, ny, nz); + return n / n.norm(); } //============================================================================== From 377ee77486a2631d1d3f006d81ee0bebf34eaab4 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 20 Dec 2021 09:28:21 -0500 Subject: [PATCH 11/32] Use quadric solver from ACM TOMS 46(2), pp1-28 (algorithm 1010) --- CMakeLists.txt | 4 + include/openmc/external/quartic_solver.h | 10 + src/external/LICENSE | 32 ++ src/external/quartic_solver.c | 621 +++++++++++++++++++++++ src/surface.cpp | 308 +++-------- 5 files changed, 732 insertions(+), 243 deletions(-) create mode 100644 include/openmc/external/quartic_solver.h create mode 100644 src/external/LICENSE create mode 100644 src/external/quartic_solver.c diff --git a/CMakeLists.txt b/CMakeLists.txt index d0c2569c34..9cfb55b7a3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -364,6 +364,10 @@ list(APPEND libopenmc_SOURCES src/xml_interface.cpp src/xsdata.cpp) +# Add bundled external dependencies +list(APPEND libopenmc_SOURCES + src/external/quartic_solver.c) + # For Visual Studio compilers if(MSVC) # Use static library (otherwise explicit symbol portings are needed) diff --git a/include/openmc/external/quartic_solver.h b/include/openmc/external/quartic_solver.h new file mode 100644 index 0000000000..98159afb07 --- /dev/null +++ b/include/openmc/external/quartic_solver.h @@ -0,0 +1,10 @@ +#ifndef OPENMC_EXTERNAL_QUARTIC_SOLVER_H +#define OPENMC_EXTERNAL_QUARTIC_SOLVER_H + +#include + +extern "C" { +void oqs_quartic_solver(double coeff[5], std::complex roots[4]); +} + +#endif // OPENMC_EXTERNAL_QUARTIC_SOLVER_H diff --git a/src/external/LICENSE b/src/external/LICENSE new file mode 100644 index 0000000000..ac09c7fcb7 --- /dev/null +++ b/src/external/LICENSE @@ -0,0 +1,32 @@ +The quartic solver was obtained from the paper: Alberto Giacomo Orellana and +Cristiano De Michele, "Algorithm 1010: Boosting Efficiency in Solving Quartic +Equations with No Compromise in Accuracy," ACM Transactions on Mathematical +Software, 46 (2), pp. 1-28. https://doi.org/10.1145/3386241 + +OpenMC developers contacted the authors, who have agreed to license their +software under the simplified BSD license, reproduced below: + +------------------------------------------------------------------------------- +Copyright (c) 2020 Alberto Giacomo Orellana and Cristiano De Michele +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE PYNE DEVELOPMENT TEAM ``AS IS'' AND ANY EXPRESS +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/external/quartic_solver.c b/src/external/quartic_solver.c new file mode 100644 index 0000000000..71b24b8a4a --- /dev/null +++ b/src/external/quartic_solver.c @@ -0,0 +1,621 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define Sqr(x) ((x) * (x)) +#ifndef CMPLX +#define CMPLX(x, y) (x) + (y)*I +#endif +const double cubic_rescal_fact = + 3.488062113727083E+102; //= pow(DBL_MAX,1.0/3.0)/1.618034; +const double quart_rescal_fact = + 7.156344627944542E+76; // = pow(DBL_MAX,1.0/4.0)/1.618034; +const double macheps = 2.2204460492503131E-16; // DBL_EPSILON +double oqs_max2(double a, double b) +{ + if (a >= b) + return a; + else + return b; +} +double oqs_max3(double a, double b, double c) +{ + double t; + t = oqs_max2(a, b); + return oqs_max2(t, c); +} + +void oqs_solve_cubic_analytic_depressed_handle_inf( + double b, double c, double* sol) +{ + /* find analytically the dominant root of a depressed cubic x^3+b*x+c + * where coefficients b and c are large (see sec. 2.2 in the manuscript) */ + double Q, R, theta, A, B, QR, QRSQ, KK, sqrtQ, RQ; + ; + const double PI2 = M_PI / 2.0, TWOPI = 2.0 * M_PI; + Q = -b / 3.0; + R = 0.5 * c; + if (R == 0) { + if (b <= 0) { + *sol = sqrt(-b); + } else { + *sol = 0; + } + return; + } + + if (fabs(Q) < fabs(R)) { + QR = Q / R; + QRSQ = QR * QR; + KK = 1.0 - Q * QRSQ; + } else { + RQ = R / Q; + KK = copysign(1.0, Q) * (RQ * RQ / Q - 1.0); + } + + if (KK < 0.0) { + sqrtQ = sqrt(Q); + theta = acos((R / fabs(Q)) / sqrtQ); + if (theta < PI2) + *sol = -2.0 * sqrtQ * cos(theta / 3.0); + else + *sol = -2.0 * sqrtQ * cos((theta + TWOPI) / 3.0); + } else { + if (fabs(Q) < fabs(R)) + A = -copysign(1.0, R) * cbrt(fabs(R) * (1.0 + sqrt(KK))); + else { + A = + -copysign(1.0, R) * cbrt(fabs(R) + sqrt(fabs(Q)) * fabs(Q) * sqrt(KK)); + } + if (A == 0.0) + B = 0.0; + else + B = Q / A; + *sol = A + B; + } +} +void oqs_solve_cubic_analytic_depressed(double b, double c, double* sol) +{ + /* find analytically the dominant root of a depressed cubic x^3+b*x+c + * (see sec. 2.2 in the manuscript) */ + double Q, R, theta, Q3, R2, A, B, sqrtQ; + Q = -b / 3.0; + R = 0.5 * c; + if (fabs(Q) > 1E102 || fabs(R) > 1E154) { + oqs_solve_cubic_analytic_depressed_handle_inf(b, c, sol); + return; + } + Q3 = Sqr(Q) * Q; + R2 = Sqr(R); + if (R2 < Q3) { + theta = acos(R / sqrt(Q3)); + sqrtQ = -2.0 * sqrt(Q); + if (theta < M_PI / 2) + *sol = sqrtQ * cos(theta / 3.0); + else + *sol = sqrtQ * cos((theta + 2.0 * M_PI) / 3.0); + } else { + A = -copysign(1.0, R) * pow(fabs(R) + sqrt(R2 - Q3), 1.0 / 3.0); + if (A == 0.0) + B = 0.0; + else + B = Q / A; + *sol = A + B; /* this is always largest root even if A=B */ + } +} +void oqs_calc_phi0( + double a, double b, double c, double d, double* phi0, int scaled) +{ + /* find phi0 as the dominant root of the depressed and shifted cubic + * in eq. (79) (see also the discussion in sec. 2.2 of the manuscript) */ + double rmax, g, h, gg, hh, aq, bq, cq, dq, s, diskr; + double maxtt, xxx, gx, x, xold, f, fold, df, xsq; + double ggss, hhss, dqss, aqs, bqs, cqs, rfact, rfactsq; + int iter; + diskr = 9 * a * a - 24 * b; + /* eq. (87) */ + if (diskr > 0.0) { + diskr = sqrt(diskr); + if (a > 0.0) + s = -2 * b / (3 * a + diskr); + else + s = -2 * b / (3 * a - diskr); + } else { + s = -a / 4; + } + /* eqs. (83) */ + aq = a + 4 * s; + bq = b + 3 * s * (a + 2 * s); + cq = c + s * (2 * b + s * (3 * a + 4 * s)); + dq = d + s * (c + s * (b + s * (a + s))); + gg = bq * bq / 9; + hh = aq * cq; + + g = hh - 4 * dq - 3 * gg; /* eq. (85) */ + h = (8 * dq + hh - 2 * gg) * bq / 3 - cq * cq - dq * aq * aq; /* eq. (86) */ + oqs_solve_cubic_analytic_depressed(g, h, &rmax); + if (isnan(rmax) || isinf(rmax)) { + oqs_solve_cubic_analytic_depressed_handle_inf(g, h, &rmax); + if ((isnan(rmax) || isinf(rmax)) && scaled) { + // try harder: rescale also the depressed cubic if quartic has been + // already rescaled + rfact = cubic_rescal_fact; + rfactsq = rfact * rfact; + ggss = gg / rfactsq; + hhss = hh / rfactsq; + dqss = dq / rfactsq; + aqs = aq / rfact; + bqs = bq / rfact; + cqs = cq / rfact; + ggss = bqs * bqs / 9.0; + hhss = aqs * cqs; + g = hhss - 4.0 * dqss - 3.0 * ggss; + h = (8.0 * dqss + hhss - 2.0 * ggss) * bqs / 3 - cqs * (cqs / rfact) - + (dq / rfact) * aqs * aqs; + oqs_solve_cubic_analytic_depressed(g, h, &rmax); + if (isnan(rmax) || isinf(rmax)) { + oqs_solve_cubic_analytic_depressed_handle_inf(g, h, &rmax); + } + rmax *= rfact; + } + } + /* Newton-Raphson used to refine phi0 (see end of sec. 2.2 in the manuscript) + */ + x = rmax; + xsq = x * x; + xxx = x * xsq; + gx = g * x; + f = x * (xsq + g) + h; + if (fabs(xxx) > fabs(gx)) + maxtt = fabs(xxx); + else + maxtt = fabs(gx); + if (fabs(h) > maxtt) + maxtt = fabs(h); + + if (fabs(f) > macheps * maxtt) { + for (iter = 0; iter < 8; iter++) { + df = 3.0 * xsq + g; + if (df == 0) { + break; + } + xold = x; + x += -f / df; + fold = f; + xsq = x * x; + f = x * (xsq + g) + h; + if (f == 0) { + break; + } + + if (fabs(f) >= fabs(fold)) { + x = xold; + break; + } + } + } + *phi0 = x; +} +double oqs_calc_err_ldlt( + double b, double c, double d, double d2, double l1, double l2, double l3) +{ + /* Eqs. (29) and (30) in the manuscript */ + double sum; + sum = (b == 0) ? fabs(d2 + l1 * l1 + 2.0 * l3) + : fabs(((d2 + l1 * l1 + 2.0 * l3) - b) / b); + sum += (c == 0) ? fabs(2.0 * d2 * l2 + 2.0 * l1 * l3) + : fabs(((2.0 * d2 * l2 + 2.0 * l1 * l3) - c) / c); + sum += (d == 0) ? fabs(d2 * l2 * l2 + l3 * l3) + : fabs(((d2 * l2 * l2 + l3 * l3) - d) / d); + return sum; +} +double oqs_calc_err_abcd_cmplx(double a, double b, double c, double d, + complex double aq, complex double bq, complex double cq, complex double dq) +{ + /* Eqs. (68) and (69) in the manuscript for complex alpha1 (aq), beta1 (bq), + * alpha2 (cq) and beta2 (dq) */ + double sum; + sum = (d == 0) ? cabs(bq * dq) : cabs((bq * dq - d) / d); + sum += + (c == 0) ? cabs(bq * cq + aq * dq) : cabs(((bq * cq + aq * dq) - c) / c); + sum += + (b == 0) ? cabs(bq + aq * cq + dq) : cabs(((bq + aq * cq + dq) - b) / b); + sum += (a == 0) ? cabs(aq + cq) : cabs(((aq + cq) - a) / a); + return sum; +} +double oqs_calc_err_abcd(double a, double b, double c, double d, double aq, + double bq, double cq, double dq) +{ + /* Eqs. (68) and (69) in the manuscript for real alpha1 (aq), beta1 (bq), + * alpha2 (cq) and beta2 (dq)*/ + double sum; + sum = (d == 0) ? fabs(bq * dq) : fabs((bq * dq - d) / d); + sum += + (c == 0) ? fabs(bq * cq + aq * dq) : fabs(((bq * cq + aq * dq) - c) / c); + sum += + (b == 0) ? fabs(bq + aq * cq + dq) : fabs(((bq + aq * cq + dq) - b) / b); + sum += (a == 0) ? fabs(aq + cq) : fabs(((aq + cq) - a) / a); + return sum; +} +double oqs_calc_err_abc( + double a, double b, double c, double aq, double bq, double cq, double dq) +{ + /* Eqs. (48)-(51) in the manuscript */ + double sum; + sum = + (c == 0) ? fabs(bq * cq + aq * dq) : fabs(((bq * cq + aq * dq) - c) / c); + sum += + (b == 0) ? fabs(bq + aq * cq + dq) : fabs(((bq + aq * cq + dq) - b) / b); + sum += (a == 0) ? fabs(aq + cq) : fabs(((aq + cq) - a) / a); + return sum; +} +void oqs_NRabcd(double a, double b, double c, double d, double* AQ, double* BQ, + double* CQ, double* DQ) +{ + /* Newton-Raphson described in sec. 2.3 of the manuscript for complex + * coefficients a,b,c,d */ + int iter, k1, k2; + double x02, errf, errfold, xold[4], x[4], dx[4], det, Jinv[4][4], fvec[4], + vr[4]; + x[0] = *AQ; + x[1] = *BQ; + x[2] = *CQ; + x[3] = *DQ; + vr[0] = d; + vr[1] = c; + vr[2] = b; + vr[3] = a; + fvec[0] = x[1] * x[3] - d; + fvec[1] = x[1] * x[2] + x[0] * x[3] - c; + fvec[2] = x[1] + x[0] * x[2] + x[3] - b; + fvec[3] = x[0] + x[2] - a; + errf = 0; + for (k1 = 0; k1 < 4; k1++) { + errf += (vr[k1] == 0) ? fabs(fvec[k1]) : fabs(fvec[k1] / vr[k1]); + } + for (iter = 0; iter < 8; iter++) { + x02 = x[0] - x[2]; + det = x[1] * x[1] + x[1] * (-x[2] * x02 - 2.0 * x[3]) + + x[3] * (x[0] * x02 + x[3]); + if (det == 0.0) + break; + Jinv[0][0] = x02; + Jinv[0][1] = x[3] - x[1]; + Jinv[0][2] = x[1] * x[2] - x[0] * x[3]; + Jinv[0][3] = -x[1] * Jinv[0][1] - x[0] * Jinv[0][2]; + Jinv[1][0] = x[0] * Jinv[0][0] + Jinv[0][1]; + Jinv[1][1] = -x[1] * Jinv[0][0]; + Jinv[1][2] = -x[1] * Jinv[0][1]; + Jinv[1][3] = -x[1] * Jinv[0][2]; + Jinv[2][0] = -Jinv[0][0]; + Jinv[2][1] = -Jinv[0][1]; + Jinv[2][2] = -Jinv[0][2]; + Jinv[2][3] = Jinv[0][2] * x[2] + Jinv[0][1] * x[3]; + Jinv[3][0] = -x[2] * Jinv[0][0] - Jinv[0][1]; + Jinv[3][1] = Jinv[0][0] * x[3]; + Jinv[3][2] = x[3] * Jinv[0][1]; + Jinv[3][3] = x[3] * Jinv[0][2]; + for (k1 = 0; k1 < 4; k1++) { + dx[k1] = 0; + for (k2 = 0; k2 < 4; k2++) + dx[k1] += Jinv[k1][k2] * fvec[k2]; + } + for (k1 = 0; k1 < 4; k1++) + xold[k1] = x[k1]; + + for (k1 = 0; k1 < 4; k1++) { + x[k1] += -dx[k1] / det; + } + fvec[0] = x[1] * x[3] - d; + fvec[1] = x[1] * x[2] + x[0] * x[3] - c; + fvec[2] = x[1] + x[0] * x[2] + x[3] - b; + fvec[3] = x[0] + x[2] - a; + errfold = errf; + errf = 0; + for (k1 = 0; k1 < 4; k1++) { + errf += (vr[k1] == 0) ? fabs(fvec[k1]) : fabs(fvec[k1] / vr[k1]); + } + if (errf == 0) + break; + if (errf >= errfold) { + for (k1 = 0; k1 < 4; k1++) + x[k1] = xold[k1]; + break; + } + } + *AQ = x[0]; + *BQ = x[1]; + *CQ = x[2]; + *DQ = x[3]; +} +void oqs_solve_quadratic(double a, double b, complex double roots[2]) +{ + double div, sqrtd, diskr, zmax, zmin; + diskr = a * a - 4 * b; + if (diskr >= 0.0) { + if (a >= 0.0) + div = -a - sqrt(diskr); + else + div = -a + sqrt(diskr); + + zmax = div / 2; + + if (zmax == 0.0) + zmin = 0.0; + else + zmin = b / zmax; + + roots[0] = CMPLX(zmax, 0.0); + roots[1] = CMPLX(zmin, 0.0); + } else { + sqrtd = sqrt(-diskr); + roots[0] = CMPLX(-a / 2, sqrtd / 2); + roots[1] = CMPLX(-a / 2, -sqrtd / 2); + } +} +void oqs_quartic_solver(double coeff[5], complex double roots[4]) +{ + /* USAGE: + * + * This routine calculates the roots of the quartic equation + * + * coeff[4]*x^4 + coeff[3]*x^3 + coeff[2]*x^2 + coeff[1]*x + coeff[0] = 0 + * + * if coeff[4] != 0 + * + * the four roots will be stored in the complex array roots[] + * + * */ + complex double acx1, bcx1, ccx1, dcx1, acx, bcx, ccx, dcx, cdiskr, zx1, zx2, + zxmax, zxmin, qroots[2]; + double l2m[12], d2m[12], res[12], resmin, bl311, dml3l3, err0 = 0, err1 = 0, + aq1, bq1, cq1, dq1; + double a, b, c, d, phi0, aq, bq, cq, dq, d2, d3, l1, l2, l3, errmin, errv[3], + aqv[3], cqv[3], gamma, del2; + int realcase[2], whichcase, k1, k, kmin, nsol; + double rfactsq, rfact = 1.0; + + if (coeff[4] == 0.0) { + printf("That's not a quartic!\n"); + return; + } + a = coeff[3] / coeff[4]; + b = coeff[2] / coeff[4]; + c = coeff[1] / coeff[4]; + d = coeff[0] / coeff[4]; + oqs_calc_phi0(a, b, c, d, &phi0, 0); + + // simple polynomial rescaling + if (isnan(phi0) || isinf(phi0)) { + rfact = quart_rescal_fact; + a /= rfact; + rfactsq = rfact * rfact; + b /= rfactsq; + c /= rfactsq * rfact; + d /= rfactsq * rfactsq; + oqs_calc_phi0(a, b, c, d, &phi0, 1); + } + l1 = a / 2; /* eq. (16) */ + l3 = b / 6 + phi0 / 2; /* eq. (18) */ + del2 = c - a * l3; /* defined just after eq. (27) */ + nsol = 0; + bl311 = 2. * b / 3. - phi0 - l1 * l1; /* This is d2 as defined in eq. (20)*/ + dml3l3 = d - l3 * l3; /* dml3l3 is d3 as defined in eq. (15) with d2=0 */ + + /* Three possible solutions for d2 and l2 (see eqs. (28) and discussion which + * follows) */ + if (bl311 != 0.0) { + d2m[nsol] = bl311; + l2m[nsol] = del2 / (2.0 * d2m[nsol]); + res[nsol] = oqs_calc_err_ldlt(b, c, d, d2m[nsol], l1, l2m[nsol], l3); + nsol++; + } + if (del2 != 0) { + l2m[nsol] = 2 * dml3l3 / del2; + if (l2m[nsol] != 0) { + d2m[nsol] = del2 / (2 * l2m[nsol]); + res[nsol] = oqs_calc_err_ldlt(b, c, d, d2m[nsol], l1, l2m[nsol], l3); + nsol++; + } + + d2m[nsol] = bl311; + l2m[nsol] = 2.0 * dml3l3 / del2; + res[nsol] = oqs_calc_err_ldlt(b, c, d, d2m[nsol], l1, l2m[nsol], l3); + nsol++; + } + + if (nsol == 0) { + l2 = d2 = 0.0; + } else { + /* we select the (d2,l2) pair which minimizes errors */ + for (k1 = 0; k1 < nsol; k1++) { + if (k1 == 0 || res[k1] < resmin) { + resmin = res[k1]; + kmin = k1; + } + } + d2 = d2m[kmin]; + l2 = l2m[kmin]; + } + whichcase = 0; + if (d2 < 0.0) { + /* Case I eqs. (37)-(40) */ + gamma = sqrt(-d2); + aq = l1 + gamma; + bq = l3 + gamma * l2; + + cq = l1 - gamma; + dq = l3 - gamma * l2; + if (fabs(dq) < fabs(bq)) + dq = d / bq; + else if (fabs(dq) > fabs(bq)) + bq = d / dq; + if (fabs(aq) < fabs(cq)) { + nsol = 0; + if (dq != 0) { + aqv[nsol] = (c - bq * cq) / dq; /* see eqs. (47) */ + errv[nsol] = oqs_calc_err_abc(a, b, c, aqv[nsol], bq, cq, dq); + nsol++; + } + if (cq != 0) { + aqv[nsol] = (b - dq - bq) / cq; /* see eqs. (47) */ + errv[nsol] = oqs_calc_err_abc(a, b, c, aqv[nsol], bq, cq, dq); + nsol++; + } + aqv[nsol] = a - cq; /* see eqs. (47) */ + errv[nsol] = oqs_calc_err_abc(a, b, c, aqv[nsol], bq, cq, dq); + nsol++; + /* we select the value of aq (i.e. alpha1 in the manuscript) which + * minimizes errors */ + for (k = 0; k < nsol; k++) { + if (k == 0 || errv[k] < errmin) { + kmin = k; + errmin = errv[k]; + } + } + aq = aqv[kmin]; + } else { + nsol = 0; + if (bq != 0) { + cqv[nsol] = (c - aq * dq) / bq; /* see eqs. (53) */ + errv[nsol] = oqs_calc_err_abc(a, b, c, aq, bq, cqv[nsol], dq); + nsol++; + } + if (aq != 0) { + cqv[nsol] = (b - bq - dq) / aq; /* see eqs. (53) */ + errv[nsol] = oqs_calc_err_abc(a, b, c, aq, bq, cqv[nsol], dq); + nsol++; + } + cqv[nsol] = a - aq; /* see eqs. (53) */ + errv[nsol] = oqs_calc_err_abc(a, b, c, aq, bq, cqv[nsol], dq); + nsol++; + /* we select the value of cq (i.e. alpha2 in the manuscript) which + * minimizes errors */ + for (k = 0; k < nsol; k++) { + if (k == 0 || errv[k] < errmin) { + kmin = k; + errmin = errv[k]; + } + } + cq = cqv[kmin]; + } + realcase[0] = 1; + } else if (d2 > 0) { + /* Case II eqs. (53)-(56) */ + gamma = sqrt(d2); + acx = CMPLX(l1, gamma); + bcx = CMPLX(l3, gamma * l2); + ccx = conj(acx); + dcx = conj(bcx); + realcase[0] = 0; + } else + realcase[0] = -1; // d2=0 + /* Case III: d2 is 0 or approximately 0 (in this case check which solution is + * better) */ + if (realcase[0] == -1 || (fabs(d2) <= macheps * oqs_max3(fabs(2. * b / 3.), + fabs(phi0), l1 * l1))) { + d3 = d - l3 * l3; + if (realcase[0] == 1) + err0 = oqs_calc_err_abcd(a, b, c, d, aq, bq, cq, dq); + else if (realcase[0] == 0) + err0 = oqs_calc_err_abcd_cmplx(a, b, c, d, acx, bcx, ccx, dcx); + if (d3 <= 0) { + realcase[1] = 1; + aq1 = l1; + bq1 = l3 + sqrt(-d3); + cq1 = l1; + dq1 = l3 - sqrt(-d3); + if (fabs(dq1) < fabs(bq1)) + dq1 = d / bq1; + else if (fabs(dq1) > fabs(bq1)) + bq1 = d / dq1; + err1 = oqs_calc_err_abcd(a, b, c, d, aq1, bq1, cq1, dq1); /* eq. (68) */ + } else /* complex */ + { + realcase[1] = 0; + acx1 = l1; + bcx1 = l3 + I * sqrt(d3); + ccx1 = l1; + dcx1 = conj(bcx1); + err1 = oqs_calc_err_abcd_cmplx(a, b, c, d, acx1, bcx1, ccx1, dcx1); + } + if (realcase[0] == -1 || err1 < err0) { + whichcase = 1; // d2 = 0 + if (realcase[1] == 1) { + aq = aq1; + bq = bq1; + cq = cq1; + dq = dq1; + } else { + acx = acx1; + bcx = bcx1; + ccx = ccx1; + dcx = dcx1; + } + } + } + if (realcase[whichcase] == 1) { + /* if alpha1, beta1, alpha2 and beta2 are real first refine + * the coefficient through a Newton-Raphson */ + oqs_NRabcd(a, b, c, d, &aq, &bq, &cq, &dq); + /* finally calculate the roots as roots of p1(x) and p2(x) (see end of + * sec. 2.1) */ + oqs_solve_quadratic(aq, bq, qroots); + roots[0] = qroots[0]; + roots[1] = qroots[1]; + oqs_solve_quadratic(cq, dq, qroots); + roots[2] = qroots[0]; + roots[3] = qroots[1]; + } else { + /* complex coefficients of p1 and p2 */ + if (whichcase == 0) // d2!=0 + { + cdiskr = acx * acx / 4 - bcx; + /* calculate the roots as roots of p1(x) and p2(x) (see end of sec. 2.1) + */ + zx1 = -acx / 2 + csqrt(cdiskr); + zx2 = -acx / 2 - csqrt(cdiskr); + if (cabs(zx1) > cabs(zx2)) + zxmax = zx1; + else + zxmax = zx2; + zxmin = bcx / zxmax; + roots[0] = zxmin; + roots[1] = conj(zxmin); + roots[2] = zxmax; + roots[3] = conj(zxmax); + } else // d2 ~ 0 + { + /* never gets here! */ + cdiskr = csqrt(acx * acx - 4.0 * bcx); + zx1 = -0.5 * (acx + cdiskr); + zx2 = -0.5 * (acx - cdiskr); + if (cabs(zx1) > cabs(zx2)) + zxmax = zx1; + else + zxmax = zx2; + zxmin = bcx / zxmax; + roots[0] = zxmax; + roots[1] = zxmin; + cdiskr = csqrt(ccx * ccx - 4.0 * dcx); + zx1 = -0.5 * (ccx + cdiskr); + zx2 = -0.5 * (ccx - cdiskr); + if (cabs(zx1) > cabs(zx2)) + zxmax = zx1; + else + zxmax = zx2; + zxmin = dcx / zxmax; + roots[2] = zxmax; + roots[3] = zxmin; + } + } + if (rfact != 1.0) { + for (k = 0; k < 4; k++) + roots[k] *= rfact; + } +} diff --git a/src/surface.cpp b/src/surface.cpp index c63b54b9ad..38a70fcdb1 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -1,6 +1,7 @@ #include "openmc/surface.h" #include +#include #include #include @@ -10,6 +11,7 @@ #include "openmc/array.h" #include "openmc/container_util.h" #include "openmc/error.h" +#include "openmc/external/quartic_solver.h" #include "openmc/hdf5_interface.h" #include "openmc/math_functions.h" #include "openmc/random_lcg.h" @@ -1004,169 +1006,6 @@ void SurfaceQuadric::to_hdf5_inner(hid_t group_id) const write_dataset(group_id, "coefficients", coeffs); } -//============================================================================== -//============================================================================== -// Generic functions for quadratic, cubic & quartic solver -//============================================================================== - -int quadratic_solve(double a, double b, double c, std::array& x) -{ - - double func = (b * b) - 4 * a * c; - - if (func < 0) { - // this would be imaginary - } else { - x[0] = -b / (2. * a) - std::sqrt(func) / (2. * a); - x[1] = -b / (2. * a) + std::sqrt(func) / (2. * a); - } - - return 0; -} - -const double M_2PI = 2 * PI; -const double eps = FP_COINCIDENT; - -// typedef std::complex DComplex; - -//--------------------------------------------------------------------------- -// solve cubic equation x^3 + a*x^2 + b*x + c -// x - array of size 3 -// In case 3 real roots: => x[0], x[1], x[2], return 3 -// 2 real roots: x[0], x[1], return 2 -// 1 real root : x[0], x[1] ± i*x[2], return 1 -unsigned int solve_cubic( - const double a, const double b, const double c, std::array& x) -{ - double a2 = a * a; - double q = (a2 - 3 * b) / 9; - double r = (a * (2 * a2 - 9 * b) + 27 * c) / 54; - double r2 = r * r; - double q3 = std::pow(q, 3); - double A, B; - double a_prime = 0.; - if (r2 < q3) // 3 roots - { - double t = r / sqrt(q3); - if (t < -1) - t = -1; - if (t > 1) - t = 1; - t = std::acos(t); - a_prime = a / 3.; - q = -2 * std::sqrt(q); - x[0] = q * std::cos(t / 3) - a_prime; - x[1] = q * std::cos((t + M_2PI) / 3) - a_prime; - x[2] = q * std::cos((t - M_2PI) / 3) - a_prime; - return 3; - } else { - A = -std::pow(std::fabs(r) + std::sqrt(r2 - q3), 1. / 3); - if (r < 0) - A = -A; - B = (0 == A ? 0 : q / A); - - a_prime = a / 3; - x[0] = (A + B) - a_prime; - x[1] = -0.5 * (A + B) - a_prime; - x[2] = 0.5 * std::sqrt(3.) * (A - B); - // 2 real roots - if (std::fabs(x[2]) < eps) { - x[2] = x[1]; - return 2; - } - // one real root - return 1; - } -} -//--------------------------------------------------------------------------- -// solve quartic equation x^4 + a*x^3 + b*x^2 + c*x + d -// Attention - this function returns dynamically allocated array. It has to be -// released afterwards. -void quartic_solve( - double a, double b, double c, double d, std::array& real_roots) -{ - double a3 = -b; - double b3 = a * c - 4. * d; - double c3 = -a * a * d - c * c + 4. * b * d; - - // cubic resolvent - // y^3 − b*y^2 + (ac−4d)*y − a^2*d−c^2+4*b*d = 0 - - std::array cube_roots; - unsigned int num_roots = solve_cubic(a3, b3, c3, cube_roots); - - double q1, q2, p1, p2, D, sqD, y; - - y = cube_roots[0]; - // The essence - choosing Y with maximal absolute value. - if (num_roots != 1) { - if (std::fabs(cube_roots[1]) > std::fabs(y)) - y = cube_roots[1]; - if (std::fabs(cube_roots[2]) > std::fabs(y)) - y = cube_roots[2]; - } - - // h1+h2 = y && h1*h2 = d <=> h^2 -y*h + d = 0 (h === q) - - D = y * y - 4 * d; - if (std::fabs(D) < eps) // in other words - D==0 - { - q1 = q2 = y * 0.5; - // g1+g2 = a && g1+g2 = b-y <=> g^2 - a*g + b-y = 0 (p === g) - D = a * a - 4 * (b - y); - if (std::fabs(D) < eps) { - p1 = p2 = a * 0.5; - } else { - sqD = std::sqrt(D); - p1 = (a + sqD) * 0.5; - p2 = (a - sqD) * 0.5; - } - } else { - sqD = std::sqrt(D); - q1 = (y + sqD) * 0.5; - q2 = (y - sqD) * 0.5; - p1 = (a * q1 - c) / (q1 - q2); - p2 = (c - a * q2) / (q1 - q2); - } - - std::array, 4> roots; // the roots to return - - // solving quadratic eq. - x^2 + p1*x + q1 = 0 - D = p1 * p1 - 4 * q1; - if (D < 0.0) { - roots[0].real(-p1 * 0.5); - roots[0].imag(std::sqrt(-D) * 0.5); - roots[1] = std::conj(roots[0]); - } else { - sqD = std::sqrt(D); - roots[0].real((-p1 + sqD) * 0.5); - roots[1].real((-p1 - sqD) * 0.5); - } - - // solving quadratic eq. - x^2 + p2*x + q2 = 0 - D = p2 * p2 - 4 * q2; - if (D < 0.0) { - roots[2].real(-p2 * 0.5); - roots[2].imag(std::sqrt(-D) * 0.5); - roots[3] = std::conj(roots[2]); - } else { - sqD = std::sqrt(D); - roots[2].real((-p2 + sqD) * 0.5); - roots[3].real((-p2 - sqD) * 0.5); - } - - for (int i = 0; i < 4; i++) { - if (roots[i].imag() == 0.) - real_roots[i] = roots[i].real(); - else - real_roots[i] = 0.; - } - - std::sort(real_roots.begin(), real_roots.end()); - - return; -} - //============================================================================== SurfaceXTorus::SurfaceXTorus(pugi::xml_node surf_node) : CSGSurface(surf_node) @@ -1212,37 +1051,31 @@ double SurfaceXTorus::distance(Position r, Direction ang, bool coincident) const double d0 = four_A2 * (y * y + z * z); // Coefficient for equation: a t^4 + b t^3 + c t^2 + d t + e = 0 - double a = c2 * c2; - double b = 2 * c1 * c2; - double c = c1 * c1 + 2 * c0 * c2 - d2; - double d = 2 * c0 * c1 - d1; - double e = c0 * c0 - d0; + double coeff[5]; + coeff[0] = c0 * c0 - d0; + coeff[1] = 2 * c0 * c1 - d1; + coeff[2] = c1 * c1 + 2 * c0 * c2 - d2; + coeff[3] = 2 * c1 * c2; + coeff[4] = c2 * c2; - std::array roots; - quartic_solve(b, c, d, e, roots); + std::complex roots[4]; + oqs_quartic_solver(coeff, roots); - if (coincident) - r += ang * TINY_BIT; - - // special degerenate case two sets of repated - // roots - if (b == 0.0 && d == 0) { - if (roots[1] - roots[0] < 1e-5) - return INFTY; - if (roots[3] - roots[2] < 1e-5) - return INFTY; - } - - for (int i = 0; i < 4; i++) { - // need something better than just raw tolerance - // use fastQS to get back lost precision - if (roots[i] > 1e-6) { - return roots[i]; + // Find smallest positive, real root. In the case where the particle is + // coincident with the surface, we are sure to have one root very close to + // zero but possibly small and positive. A tolerance is set to discard that + // zero. + double distance = INFTY; + double cutoff = coincident ? 1e-9 : 0.0; + for (int i = 0; i < 4; ++i) { + if (roots[i].imag() == 0) { + double root = roots[i].real(); + if (root > cutoff && root < distance) { + distance = root; + } } } - - // otherwise no hit - return INFTY; + return distance; } Direction SurfaceXTorus::normal(Position r) const @@ -1306,36 +1139,31 @@ double SurfaceYTorus::distance(Position r, Direction ang, bool coincident) const double d0 = four_A2 * (x * x + z * z); // Coefficient for equation: a t^4 + b t^3 + c t^2 + d t + e = 0 - double a = c2 * c2; - double b = 2 * c1 * c2; - double c = c1 * c1 + 2 * c0 * c2 - d2; - double d = 2 * c0 * c1 - d1; - double e = c0 * c0 - d0; + double coeff[5]; + coeff[0] = c0 * c0 - d0; + coeff[1] = 2 * c0 * c1 - d1; + coeff[2] = c1 * c1 + 2 * c0 * c2 - d2; + coeff[3] = 2 * c1 * c2; + coeff[4] = c2 * c2; - std::array roots; - quartic_solve(b, c, d, e, roots); + std::complex roots[4]; + oqs_quartic_solver(coeff, roots); - if (coincident) - r += ang * TINY_BIT; - - // special degerenate case two sets of repated - if (b == 0.0 && d == 0) { - if (roots[1] - roots[0] < 1e-5) - return INFTY; - if (roots[3] - roots[2] < 1e-5) - return INFTY; - } - - for (int i = 0; i < 4; i++) { - // need something better than just raw tolerance - // use fastQS to get back lost precision - if (roots[i] > 1e-6) { - return roots[i]; + // Find smallest positive, real root. In the case where the particle is + // coincident with the surface, we are sure to have one root very close to + // zero but possibly small and positive. A tolerance is set to discard that + // zero. + double distance = INFTY; + double cutoff = coincident ? 1e-9 : 0.0; + for (int i = 0; i < 4; ++i) { + if (roots[i].imag() == 0) { + double root = roots[i].real(); + if (root > cutoff && root < distance) { + distance = root; + } } } - - // otherwise no hit - return INFTY; + return distance; } Direction SurfaceYTorus::normal(Position r) const @@ -1399,37 +1227,31 @@ double SurfaceZTorus::distance(Position r, Direction ang, bool coincident) const double d0 = four_A2 * (x * x + y * y); // Coefficient for equation: a t^4 + b t^3 + c t^2 + d t + e = 0 - double a = c2 * c2; - double b = 2 * c1 * c2; - double c = c1 * c1 + 2 * c0 * c2 - d2; - double d = 2 * c0 * c1 - d1; - double e = c0 * c0 - d0; + double coeff[5]; + coeff[0] = c0 * c0 - d0; + coeff[1] = 2 * c0 * c1 - d1; + coeff[2] = c1 * c1 + 2 * c0 * c2 - d2; + coeff[3] = 2 * c1 * c2; + coeff[4] = c2 * c2; - std::array roots; - quartic_solve(b, c, d, e, roots); + std::complex roots[4]; + oqs_quartic_solver(coeff, roots); - if (coincident) - r += ang * TINY_BIT; - - // special degerenate case two sets of repated - // roots - if (b == 0.0 && d == 0) { - if (roots[1] - roots[0] < 1e-5) - return INFTY; - if (roots[3] - roots[2] < 1e-5) - return INFTY; - } - - for (int i = 0; i < 4; i++) { - // need something better than just raw tolerance - // use fastQS to get back lost precision - if (roots[i] > 1e-6) { - return roots[i]; + // Find smallest positive, real root. In the case where the particle is + // coincident with the surface, we are sure to have one root very close to + // zero but possibly small and positive. A tolerance is set to discard that + // zero. + double distance = INFTY; + double cutoff = coincident ? 1e-9 : 0.0; + for (int i = 0; i < 4; ++i) { + if (roots[i].imag() == 0) { + double root = roots[i].real(); + if (root > cutoff && root < distance) { + distance = root; + } } } - - // otherwise no hit - return INFTY; + return distance; } Direction SurfaceZTorus::normal(Position r) const From abe2247eb6ad71058ed0affe6bb70242e6475c5b Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 21 Dec 2021 14:47:48 -0500 Subject: [PATCH 12/32] Minimial conversion of quartic_solver.c to C++ --- CMakeLists.txt | 2 +- include/openmc/external/quartic_solver.h | 2 - .../{quartic_solver.c => quartic_solver.cpp} | 215 +++++++++--------- 3 files changed, 107 insertions(+), 112 deletions(-) rename src/external/{quartic_solver.c => quartic_solver.cpp} (73%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9cfb55b7a3..fb5da6f2a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -366,7 +366,7 @@ list(APPEND libopenmc_SOURCES # Add bundled external dependencies list(APPEND libopenmc_SOURCES - src/external/quartic_solver.c) + src/external/quartic_solver.cpp) # For Visual Studio compilers if(MSVC) diff --git a/include/openmc/external/quartic_solver.h b/include/openmc/external/quartic_solver.h index 98159afb07..9438e494ae 100644 --- a/include/openmc/external/quartic_solver.h +++ b/include/openmc/external/quartic_solver.h @@ -3,8 +3,6 @@ #include -extern "C" { void oqs_quartic_solver(double coeff[5], std::complex roots[4]); -} #endif // OPENMC_EXTERNAL_QUARTIC_SOLVER_H diff --git a/src/external/quartic_solver.c b/src/external/quartic_solver.cpp similarity index 73% rename from src/external/quartic_solver.c rename to src/external/quartic_solver.cpp index 71b24b8a4a..be881ddf36 100644 --- a/src/external/quartic_solver.c +++ b/src/external/quartic_solver.cpp @@ -1,16 +1,8 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#define Sqr(x) ((x) * (x)) -#ifndef CMPLX -#define CMPLX(x, y) (x) + (y)*I -#endif +#include +#include +#include +#include + const double cubic_rescal_fact = 3.488062113727083E+102; //= pow(DBL_MAX,1.0/3.0)/1.618034; const double quart_rescal_fact = @@ -42,35 +34,36 @@ void oqs_solve_cubic_analytic_depressed_handle_inf( R = 0.5 * c; if (R == 0) { if (b <= 0) { - *sol = sqrt(-b); + *sol = std::sqrt(-b); } else { *sol = 0; } return; } - if (fabs(Q) < fabs(R)) { + if (std::fabs(Q) < std::fabs(R)) { QR = Q / R; QRSQ = QR * QR; KK = 1.0 - Q * QRSQ; } else { RQ = R / Q; - KK = copysign(1.0, Q) * (RQ * RQ / Q - 1.0); + KK = std::copysign(1.0, Q) * (RQ * RQ / Q - 1.0); } if (KK < 0.0) { - sqrtQ = sqrt(Q); - theta = acos((R / fabs(Q)) / sqrtQ); + sqrtQ = std::sqrt(Q); + theta = std::acos((R / std::fabs(Q)) / sqrtQ); if (theta < PI2) - *sol = -2.0 * sqrtQ * cos(theta / 3.0); + *sol = -2.0 * sqrtQ * std::cos(theta / 3.0); else - *sol = -2.0 * sqrtQ * cos((theta + TWOPI) / 3.0); + *sol = -2.0 * sqrtQ * std::cos((theta + TWOPI) / 3.0); } else { - if (fabs(Q) < fabs(R)) - A = -copysign(1.0, R) * cbrt(fabs(R) * (1.0 + sqrt(KK))); + if (std::fabs(Q) < std::fabs(R)) + A = -std::copysign(1.0, R) * cbrt(std::fabs(R) * (1.0 + std::sqrt(KK))); else { - A = - -copysign(1.0, R) * cbrt(fabs(R) + sqrt(fabs(Q)) * fabs(Q) * sqrt(KK)); + A = -std::copysign(1.0, R) * + cbrt(std::fabs(R) + + std::sqrt(std::fabs(Q)) * std::fabs(Q) * std::sqrt(KK)); } if (A == 0.0) B = 0.0; @@ -86,21 +79,22 @@ void oqs_solve_cubic_analytic_depressed(double b, double c, double* sol) double Q, R, theta, Q3, R2, A, B, sqrtQ; Q = -b / 3.0; R = 0.5 * c; - if (fabs(Q) > 1E102 || fabs(R) > 1E154) { + if (std::fabs(Q) > 1e102 || std::fabs(R) > 1e154) { oqs_solve_cubic_analytic_depressed_handle_inf(b, c, sol); return; } - Q3 = Sqr(Q) * Q; - R2 = Sqr(R); + Q3 = Q * Q * Q; + R2 = R * R; if (R2 < Q3) { - theta = acos(R / sqrt(Q3)); - sqrtQ = -2.0 * sqrt(Q); + theta = std::acos(R / std::sqrt(Q3)); + sqrtQ = -2.0 * std::sqrt(Q); if (theta < M_PI / 2) - *sol = sqrtQ * cos(theta / 3.0); + *sol = sqrtQ * std::cos(theta / 3.0); else - *sol = sqrtQ * cos((theta + 2.0 * M_PI) / 3.0); + *sol = sqrtQ * std::cos((theta + 2.0 * M_PI) / 3.0); } else { - A = -copysign(1.0, R) * pow(fabs(R) + sqrt(R2 - Q3), 1.0 / 3.0); + A = -std::copysign(1.0, R) * + std::pow(std::fabs(R) + std::sqrt(R2 - Q3), 1.0 / 3.0); if (A == 0.0) B = 0.0; else @@ -120,7 +114,7 @@ void oqs_calc_phi0( diskr = 9 * a * a - 24 * b; /* eq. (87) */ if (diskr > 0.0) { - diskr = sqrt(diskr); + diskr = std::sqrt(diskr); if (a > 0.0) s = -2 * b / (3 * a + diskr); else @@ -139,9 +133,9 @@ void oqs_calc_phi0( g = hh - 4 * dq - 3 * gg; /* eq. (85) */ h = (8 * dq + hh - 2 * gg) * bq / 3 - cq * cq - dq * aq * aq; /* eq. (86) */ oqs_solve_cubic_analytic_depressed(g, h, &rmax); - if (isnan(rmax) || isinf(rmax)) { + if (std::isnan(rmax) || std::isinf(rmax)) { oqs_solve_cubic_analytic_depressed_handle_inf(g, h, &rmax); - if ((isnan(rmax) || isinf(rmax)) && scaled) { + if ((std::isnan(rmax) || std::isinf(rmax)) && scaled) { // try harder: rescale also the depressed cubic if quartic has been // already rescaled rfact = cubic_rescal_fact; @@ -158,7 +152,7 @@ void oqs_calc_phi0( h = (8.0 * dqss + hhss - 2.0 * ggss) * bqs / 3 - cqs * (cqs / rfact) - (dq / rfact) * aqs * aqs; oqs_solve_cubic_analytic_depressed(g, h, &rmax); - if (isnan(rmax) || isinf(rmax)) { + if (std::isnan(rmax) || std::isinf(rmax)) { oqs_solve_cubic_analytic_depressed_handle_inf(g, h, &rmax); } rmax *= rfact; @@ -171,14 +165,14 @@ void oqs_calc_phi0( xxx = x * xsq; gx = g * x; f = x * (xsq + g) + h; - if (fabs(xxx) > fabs(gx)) - maxtt = fabs(xxx); + if (std::fabs(xxx) > std::fabs(gx)) + maxtt = std::fabs(xxx); else - maxtt = fabs(gx); - if (fabs(h) > maxtt) - maxtt = fabs(h); + maxtt = std::fabs(gx); + if (std::fabs(h) > maxtt) + maxtt = std::fabs(h); - if (fabs(f) > macheps * maxtt) { + if (std::fabs(f) > macheps * maxtt) { for (iter = 0; iter < 8; iter++) { df = 3.0 * xsq + g; if (df == 0) { @@ -193,7 +187,7 @@ void oqs_calc_phi0( break; } - if (fabs(f) >= fabs(fold)) { + if (std::fabs(f) >= std::fabs(fold)) { x = xold; break; } @@ -206,26 +200,27 @@ double oqs_calc_err_ldlt( { /* Eqs. (29) and (30) in the manuscript */ double sum; - sum = (b == 0) ? fabs(d2 + l1 * l1 + 2.0 * l3) - : fabs(((d2 + l1 * l1 + 2.0 * l3) - b) / b); - sum += (c == 0) ? fabs(2.0 * d2 * l2 + 2.0 * l1 * l3) - : fabs(((2.0 * d2 * l2 + 2.0 * l1 * l3) - c) / c); - sum += (d == 0) ? fabs(d2 * l2 * l2 + l3 * l3) - : fabs(((d2 * l2 * l2 + l3 * l3) - d) / d); + sum = (b == 0) ? std::fabs(d2 + l1 * l1 + 2.0 * l3) + : std::fabs(((d2 + l1 * l1 + 2.0 * l3) - b) / b); + sum += (c == 0) ? std::fabs(2.0 * d2 * l2 + 2.0 * l1 * l3) + : std::fabs(((2.0 * d2 * l2 + 2.0 * l1 * l3) - c) / c); + sum += (d == 0) ? std::fabs(d2 * l2 * l2 + l3 * l3) + : std::fabs(((d2 * l2 * l2 + l3 * l3) - d) / d); return sum; } double oqs_calc_err_abcd_cmplx(double a, double b, double c, double d, - complex double aq, complex double bq, complex double cq, complex double dq) + std::complex aq, std::complex bq, std::complex cq, + std::complex dq) { /* Eqs. (68) and (69) in the manuscript for complex alpha1 (aq), beta1 (bq), * alpha2 (cq) and beta2 (dq) */ double sum; - sum = (d == 0) ? cabs(bq * dq) : cabs((bq * dq - d) / d); - sum += - (c == 0) ? cabs(bq * cq + aq * dq) : cabs(((bq * cq + aq * dq) - c) / c); - sum += - (b == 0) ? cabs(bq + aq * cq + dq) : cabs(((bq + aq * cq + dq) - b) / b); - sum += (a == 0) ? cabs(aq + cq) : cabs(((aq + cq) - a) / a); + sum = (d == 0) ? std::abs(bq * dq) : std::abs((bq * dq - d) / d); + sum += (c == 0) ? std::abs(bq * cq + aq * dq) + : std::abs(((bq * cq + aq * dq) - c) / c); + sum += (b == 0) ? std::abs(bq + aq * cq + dq) + : std::abs(((bq + aq * cq + dq) - b) / b); + sum += (a == 0) ? std::abs(aq + cq) : std::abs(((aq + cq) - a) / a); return sum; } double oqs_calc_err_abcd(double a, double b, double c, double d, double aq, @@ -234,12 +229,12 @@ double oqs_calc_err_abcd(double a, double b, double c, double d, double aq, /* Eqs. (68) and (69) in the manuscript for real alpha1 (aq), beta1 (bq), * alpha2 (cq) and beta2 (dq)*/ double sum; - sum = (d == 0) ? fabs(bq * dq) : fabs((bq * dq - d) / d); - sum += - (c == 0) ? fabs(bq * cq + aq * dq) : fabs(((bq * cq + aq * dq) - c) / c); - sum += - (b == 0) ? fabs(bq + aq * cq + dq) : fabs(((bq + aq * cq + dq) - b) / b); - sum += (a == 0) ? fabs(aq + cq) : fabs(((aq + cq) - a) / a); + sum = (d == 0) ? std::fabs(bq * dq) : std::fabs((bq * dq - d) / d); + sum += (c == 0) ? std::fabs(bq * cq + aq * dq) + : std::fabs(((bq * cq + aq * dq) - c) / c); + sum += (b == 0) ? std::fabs(bq + aq * cq + dq) + : std::fabs(((bq + aq * cq + dq) - b) / b); + sum += (a == 0) ? std::fabs(aq + cq) : std::fabs(((aq + cq) - a) / a); return sum; } double oqs_calc_err_abc( @@ -247,11 +242,11 @@ double oqs_calc_err_abc( { /* Eqs. (48)-(51) in the manuscript */ double sum; - sum = - (c == 0) ? fabs(bq * cq + aq * dq) : fabs(((bq * cq + aq * dq) - c) / c); - sum += - (b == 0) ? fabs(bq + aq * cq + dq) : fabs(((bq + aq * cq + dq) - b) / b); - sum += (a == 0) ? fabs(aq + cq) : fabs(((aq + cq) - a) / a); + sum = (c == 0) ? std::fabs(bq * cq + aq * dq) + : std::fabs(((bq * cq + aq * dq) - c) / c); + sum += (b == 0) ? std::fabs(bq + aq * cq + dq) + : std::fabs(((bq + aq * cq + dq) - b) / b); + sum += (a == 0) ? std::fabs(aq + cq) : std::fabs(((aq + cq) - a) / a); return sum; } void oqs_NRabcd(double a, double b, double c, double d, double* AQ, double* BQ, @@ -276,7 +271,7 @@ void oqs_NRabcd(double a, double b, double c, double d, double* AQ, double* BQ, fvec[3] = x[0] + x[2] - a; errf = 0; for (k1 = 0; k1 < 4; k1++) { - errf += (vr[k1] == 0) ? fabs(fvec[k1]) : fabs(fvec[k1] / vr[k1]); + errf += (vr[k1] == 0) ? std::fabs(fvec[k1]) : std::fabs(fvec[k1] / vr[k1]); } for (iter = 0; iter < 8; iter++) { x02 = x[0] - x[2]; @@ -318,7 +313,8 @@ void oqs_NRabcd(double a, double b, double c, double d, double* AQ, double* BQ, errfold = errf; errf = 0; for (k1 = 0; k1 < 4; k1++) { - errf += (vr[k1] == 0) ? fabs(fvec[k1]) : fabs(fvec[k1] / vr[k1]); + errf += + (vr[k1] == 0) ? std::fabs(fvec[k1]) : std::fabs(fvec[k1] / vr[k1]); } if (errf == 0) break; @@ -333,15 +329,15 @@ void oqs_NRabcd(double a, double b, double c, double d, double* AQ, double* BQ, *CQ = x[2]; *DQ = x[3]; } -void oqs_solve_quadratic(double a, double b, complex double roots[2]) +void oqs_solve_quadratic(double a, double b, std::complex roots[2]) { double div, sqrtd, diskr, zmax, zmin; diskr = a * a - 4 * b; if (diskr >= 0.0) { if (a >= 0.0) - div = -a - sqrt(diskr); + div = -a - std::sqrt(diskr); else - div = -a + sqrt(diskr); + div = -a + std::sqrt(diskr); zmax = div / 2; @@ -350,15 +346,15 @@ void oqs_solve_quadratic(double a, double b, complex double roots[2]) else zmin = b / zmax; - roots[0] = CMPLX(zmax, 0.0); - roots[1] = CMPLX(zmin, 0.0); + roots[0] = std::complex(zmax, 0.0); + roots[1] = std::complex(zmin, 0.0); } else { - sqrtd = sqrt(-diskr); - roots[0] = CMPLX(-a / 2, sqrtd / 2); - roots[1] = CMPLX(-a / 2, -sqrtd / 2); + sqrtd = std::sqrt(-diskr); + roots[0] = std::complex(-a / 2, sqrtd / 2); + roots[1] = std::complex(-a / 2, -sqrtd / 2); } } -void oqs_quartic_solver(double coeff[5], complex double roots[4]) +void oqs_quartic_solver(double coeff[5], std::complex roots[4]) { /* USAGE: * @@ -371,8 +367,8 @@ void oqs_quartic_solver(double coeff[5], complex double roots[4]) * the four roots will be stored in the complex array roots[] * * */ - complex double acx1, bcx1, ccx1, dcx1, acx, bcx, ccx, dcx, cdiskr, zx1, zx2, - zxmax, zxmin, qroots[2]; + std::complex acx1, bcx1, ccx1, dcx1, acx, bcx, ccx, dcx, cdiskr, zx1, + zx2, zxmax, zxmin, qroots[2]; double l2m[12], d2m[12], res[12], resmin, bl311, dml3l3, err0 = 0, err1 = 0, aq1, bq1, cq1, dq1; double a, b, c, d, phi0, aq, bq, cq, dq, d2, d3, l1, l2, l3, errmin, errv[3], @@ -381,7 +377,7 @@ void oqs_quartic_solver(double coeff[5], complex double roots[4]) double rfactsq, rfact = 1.0; if (coeff[4] == 0.0) { - printf("That's not a quartic!\n"); + std::cout << "That's not a quartic!\n"; return; } a = coeff[3] / coeff[4]; @@ -391,7 +387,7 @@ void oqs_quartic_solver(double coeff[5], complex double roots[4]) oqs_calc_phi0(a, b, c, d, &phi0, 0); // simple polynomial rescaling - if (isnan(phi0) || isinf(phi0)) { + if (std::isnan(phi0) || std::isinf(phi0)) { rfact = quart_rescal_fact; a /= rfact; rfactsq = rfact * rfact; @@ -445,17 +441,17 @@ void oqs_quartic_solver(double coeff[5], complex double roots[4]) whichcase = 0; if (d2 < 0.0) { /* Case I eqs. (37)-(40) */ - gamma = sqrt(-d2); + gamma = std::sqrt(-d2); aq = l1 + gamma; bq = l3 + gamma * l2; cq = l1 - gamma; dq = l3 - gamma * l2; - if (fabs(dq) < fabs(bq)) + if (std::fabs(dq) < std::fabs(bq)) dq = d / bq; - else if (fabs(dq) > fabs(bq)) + else if (std::fabs(dq) > std::fabs(bq)) bq = d / dq; - if (fabs(aq) < fabs(cq)) { + if (std::fabs(aq) < std::fabs(cq)) { nsol = 0; if (dq != 0) { aqv[nsol] = (c - bq * cq) / dq; /* see eqs. (47) */ @@ -507,18 +503,19 @@ void oqs_quartic_solver(double coeff[5], complex double roots[4]) realcase[0] = 1; } else if (d2 > 0) { /* Case II eqs. (53)-(56) */ - gamma = sqrt(d2); - acx = CMPLX(l1, gamma); - bcx = CMPLX(l3, gamma * l2); - ccx = conj(acx); - dcx = conj(bcx); + gamma = std::sqrt(d2); + acx = std::complex(l1, gamma); + bcx = std::complex(l3, gamma * l2); + ccx = std::conj(acx); + dcx = std::conj(bcx); realcase[0] = 0; } else realcase[0] = -1; // d2=0 /* Case III: d2 is 0 or approximately 0 (in this case check which solution is * better) */ - if (realcase[0] == -1 || (fabs(d2) <= macheps * oqs_max3(fabs(2. * b / 3.), - fabs(phi0), l1 * l1))) { + if (realcase[0] == -1 || + (std::fabs(d2) <= + macheps * oqs_max3(std::fabs(2. * b / 3.), std::fabs(phi0), l1 * l1))) { d3 = d - l3 * l3; if (realcase[0] == 1) err0 = oqs_calc_err_abcd(a, b, c, d, aq, bq, cq, dq); @@ -527,21 +524,21 @@ void oqs_quartic_solver(double coeff[5], complex double roots[4]) if (d3 <= 0) { realcase[1] = 1; aq1 = l1; - bq1 = l3 + sqrt(-d3); + bq1 = l3 + std::sqrt(-d3); cq1 = l1; - dq1 = l3 - sqrt(-d3); - if (fabs(dq1) < fabs(bq1)) + dq1 = l3 - std::sqrt(-d3); + if (std::fabs(dq1) < std::fabs(bq1)) dq1 = d / bq1; - else if (fabs(dq1) > fabs(bq1)) + else if (std::fabs(dq1) > std::fabs(bq1)) bq1 = d / dq1; err1 = oqs_calc_err_abcd(a, b, c, d, aq1, bq1, cq1, dq1); /* eq. (68) */ } else /* complex */ { realcase[1] = 0; acx1 = l1; - bcx1 = l3 + I * sqrt(d3); + bcx1 = l3 + std::complex(0., std::sqrt(d3)); ccx1 = l1; - dcx1 = conj(bcx1); + dcx1 = std::conj(bcx1); err1 = oqs_calc_err_abcd_cmplx(a, b, c, d, acx1, bcx1, ccx1, dcx1); } if (realcase[0] == -1 || err1 < err0) { @@ -575,37 +572,37 @@ void oqs_quartic_solver(double coeff[5], complex double roots[4]) /* complex coefficients of p1 and p2 */ if (whichcase == 0) // d2!=0 { - cdiskr = acx * acx / 4 - bcx; + cdiskr = 0.25 * acx * acx - bcx; /* calculate the roots as roots of p1(x) and p2(x) (see end of sec. 2.1) */ - zx1 = -acx / 2 + csqrt(cdiskr); - zx2 = -acx / 2 - csqrt(cdiskr); - if (cabs(zx1) > cabs(zx2)) + zx1 = -0.5 * acx + std::sqrt(cdiskr); + zx2 = -0.5 * acx - std::sqrt(cdiskr); + if (std::abs(zx1) > std::abs(zx2)) zxmax = zx1; else zxmax = zx2; zxmin = bcx / zxmax; roots[0] = zxmin; - roots[1] = conj(zxmin); + roots[1] = std::conj(zxmin); roots[2] = zxmax; - roots[3] = conj(zxmax); + roots[3] = std::conj(zxmax); } else // d2 ~ 0 { /* never gets here! */ - cdiskr = csqrt(acx * acx - 4.0 * bcx); + cdiskr = std::sqrt(acx * acx - 4.0 * bcx); zx1 = -0.5 * (acx + cdiskr); zx2 = -0.5 * (acx - cdiskr); - if (cabs(zx1) > cabs(zx2)) + if (std::abs(zx1) > std::abs(zx2)) zxmax = zx1; else zxmax = zx2; zxmin = bcx / zxmax; roots[0] = zxmax; roots[1] = zxmin; - cdiskr = csqrt(ccx * ccx - 4.0 * dcx); + cdiskr = std::sqrt(ccx * ccx - 4.0 * dcx); zx1 = -0.5 * (ccx + cdiskr); zx2 = -0.5 * (ccx - cdiskr); - if (cabs(zx1) > cabs(zx2)) + if (std::abs(zx1) > std::abs(zx2)) zxmax = zx1; else zxmax = zx2; From ddcc170bc03603090c059911657ddad633b98760 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 21 Dec 2021 14:48:37 -0500 Subject: [PATCH 13/32] Remove I/O in quartic_solver --- src/external/quartic_solver.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/external/quartic_solver.cpp b/src/external/quartic_solver.cpp index be881ddf36..df56444ce3 100644 --- a/src/external/quartic_solver.cpp +++ b/src/external/quartic_solver.cpp @@ -1,7 +1,6 @@ #include #include #include -#include const double cubic_rescal_fact = 3.488062113727083E+102; //= pow(DBL_MAX,1.0/3.0)/1.618034; @@ -376,10 +375,6 @@ void oqs_quartic_solver(double coeff[5], std::complex roots[4]) int realcase[2], whichcase, k1, k, kmin, nsol; double rfactsq, rfact = 1.0; - if (coeff[4] == 0.0) { - std::cout << "That's not a quartic!\n"; - return; - } a = coeff[3] / coeff[4]; b = coeff[2] / coeff[4]; c = coeff[1] / coeff[4]; From 117e44f1e221ae17147900c9a71ccf8df3520f51 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 21 Dec 2021 14:53:03 -0500 Subject: [PATCH 14/32] Use oqs namespace for quartic solver --- include/openmc/external/quartic_solver.h | 4 +- src/external/quartic_solver.cpp | 86 ++++++++++++------------ src/surface.cpp | 6 +- 3 files changed, 50 insertions(+), 46 deletions(-) diff --git a/include/openmc/external/quartic_solver.h b/include/openmc/external/quartic_solver.h index 9438e494ae..376a014af6 100644 --- a/include/openmc/external/quartic_solver.h +++ b/include/openmc/external/quartic_solver.h @@ -3,6 +3,8 @@ #include -void oqs_quartic_solver(double coeff[5], std::complex roots[4]); +namespace oqs { +void quartic_solver(double coeff[5], std::complex roots[4]); +} // end namespace oqs #endif // OPENMC_EXTERNAL_QUARTIC_SOLVER_H diff --git a/src/external/quartic_solver.cpp b/src/external/quartic_solver.cpp index df56444ce3..0a427f8e9f 100644 --- a/src/external/quartic_solver.cpp +++ b/src/external/quartic_solver.cpp @@ -2,27 +2,28 @@ #include #include +namespace oqs { + const double cubic_rescal_fact = 3.488062113727083E+102; //= pow(DBL_MAX,1.0/3.0)/1.618034; const double quart_rescal_fact = 7.156344627944542E+76; // = pow(DBL_MAX,1.0/4.0)/1.618034; const double macheps = 2.2204460492503131E-16; // DBL_EPSILON -double oqs_max2(double a, double b) +double max2(double a, double b) { if (a >= b) return a; else return b; } -double oqs_max3(double a, double b, double c) +double max3(double a, double b, double c) { double t; - t = oqs_max2(a, b); - return oqs_max2(t, c); + t = oqs::max2(a, b); + return oqs::max2(t, c); } -void oqs_solve_cubic_analytic_depressed_handle_inf( - double b, double c, double* sol) +void solve_cubic_analytic_depressed_handle_inf(double b, double c, double* sol) { /* find analytically the dominant root of a depressed cubic x^3+b*x+c * where coefficients b and c are large (see sec. 2.2 in the manuscript) */ @@ -71,7 +72,7 @@ void oqs_solve_cubic_analytic_depressed_handle_inf( *sol = A + B; } } -void oqs_solve_cubic_analytic_depressed(double b, double c, double* sol) +void solve_cubic_analytic_depressed(double b, double c, double* sol) { /* find analytically the dominant root of a depressed cubic x^3+b*x+c * (see sec. 2.2 in the manuscript) */ @@ -79,7 +80,7 @@ void oqs_solve_cubic_analytic_depressed(double b, double c, double* sol) Q = -b / 3.0; R = 0.5 * c; if (std::fabs(Q) > 1e102 || std::fabs(R) > 1e154) { - oqs_solve_cubic_analytic_depressed_handle_inf(b, c, sol); + oqs::solve_cubic_analytic_depressed_handle_inf(b, c, sol); return; } Q3 = Q * Q * Q; @@ -101,8 +102,7 @@ void oqs_solve_cubic_analytic_depressed(double b, double c, double* sol) *sol = A + B; /* this is always largest root even if A=B */ } } -void oqs_calc_phi0( - double a, double b, double c, double d, double* phi0, int scaled) +void calc_phi0(double a, double b, double c, double d, double* phi0, int scaled) { /* find phi0 as the dominant root of the depressed and shifted cubic * in eq. (79) (see also the discussion in sec. 2.2 of the manuscript) */ @@ -131,9 +131,9 @@ void oqs_calc_phi0( g = hh - 4 * dq - 3 * gg; /* eq. (85) */ h = (8 * dq + hh - 2 * gg) * bq / 3 - cq * cq - dq * aq * aq; /* eq. (86) */ - oqs_solve_cubic_analytic_depressed(g, h, &rmax); + oqs::solve_cubic_analytic_depressed(g, h, &rmax); if (std::isnan(rmax) || std::isinf(rmax)) { - oqs_solve_cubic_analytic_depressed_handle_inf(g, h, &rmax); + oqs::solve_cubic_analytic_depressed_handle_inf(g, h, &rmax); if ((std::isnan(rmax) || std::isinf(rmax)) && scaled) { // try harder: rescale also the depressed cubic if quartic has been // already rescaled @@ -150,9 +150,9 @@ void oqs_calc_phi0( g = hhss - 4.0 * dqss - 3.0 * ggss; h = (8.0 * dqss + hhss - 2.0 * ggss) * bqs / 3 - cqs * (cqs / rfact) - (dq / rfact) * aqs * aqs; - oqs_solve_cubic_analytic_depressed(g, h, &rmax); + oqs::solve_cubic_analytic_depressed(g, h, &rmax); if (std::isnan(rmax) || std::isinf(rmax)) { - oqs_solve_cubic_analytic_depressed_handle_inf(g, h, &rmax); + oqs::solve_cubic_analytic_depressed_handle_inf(g, h, &rmax); } rmax *= rfact; } @@ -194,7 +194,7 @@ void oqs_calc_phi0( } *phi0 = x; } -double oqs_calc_err_ldlt( +double calc_err_ldlt( double b, double c, double d, double d2, double l1, double l2, double l3) { /* Eqs. (29) and (30) in the manuscript */ @@ -207,7 +207,7 @@ double oqs_calc_err_ldlt( : std::fabs(((d2 * l2 * l2 + l3 * l3) - d) / d); return sum; } -double oqs_calc_err_abcd_cmplx(double a, double b, double c, double d, +double calc_err_abcd_cmplx(double a, double b, double c, double d, std::complex aq, std::complex bq, std::complex cq, std::complex dq) { @@ -222,7 +222,7 @@ double oqs_calc_err_abcd_cmplx(double a, double b, double c, double d, sum += (a == 0) ? std::abs(aq + cq) : std::abs(((aq + cq) - a) / a); return sum; } -double oqs_calc_err_abcd(double a, double b, double c, double d, double aq, +double calc_err_abcd(double a, double b, double c, double d, double aq, double bq, double cq, double dq) { /* Eqs. (68) and (69) in the manuscript for real alpha1 (aq), beta1 (bq), @@ -236,7 +236,7 @@ double oqs_calc_err_abcd(double a, double b, double c, double d, double aq, sum += (a == 0) ? std::fabs(aq + cq) : std::fabs(((aq + cq) - a) / a); return sum; } -double oqs_calc_err_abc( +double calc_err_abc( double a, double b, double c, double aq, double bq, double cq, double dq) { /* Eqs. (48)-(51) in the manuscript */ @@ -248,7 +248,7 @@ double oqs_calc_err_abc( sum += (a == 0) ? std::fabs(aq + cq) : std::fabs(((aq + cq) - a) / a); return sum; } -void oqs_NRabcd(double a, double b, double c, double d, double* AQ, double* BQ, +void NRabcd(double a, double b, double c, double d, double* AQ, double* BQ, double* CQ, double* DQ) { /* Newton-Raphson described in sec. 2.3 of the manuscript for complex @@ -328,7 +328,7 @@ void oqs_NRabcd(double a, double b, double c, double d, double* AQ, double* BQ, *CQ = x[2]; *DQ = x[3]; } -void oqs_solve_quadratic(double a, double b, std::complex roots[2]) +void solve_quadratic(double a, double b, std::complex roots[2]) { double div, sqrtd, diskr, zmax, zmin; diskr = a * a - 4 * b; @@ -353,7 +353,7 @@ void oqs_solve_quadratic(double a, double b, std::complex roots[2]) roots[1] = std::complex(-a / 2, -sqrtd / 2); } } -void oqs_quartic_solver(double coeff[5], std::complex roots[4]) +void quartic_solver(double coeff[5], std::complex roots[4]) { /* USAGE: * @@ -379,7 +379,7 @@ void oqs_quartic_solver(double coeff[5], std::complex roots[4]) b = coeff[2] / coeff[4]; c = coeff[1] / coeff[4]; d = coeff[0] / coeff[4]; - oqs_calc_phi0(a, b, c, d, &phi0, 0); + oqs::calc_phi0(a, b, c, d, &phi0, 0); // simple polynomial rescaling if (std::isnan(phi0) || std::isinf(phi0)) { @@ -389,7 +389,7 @@ void oqs_quartic_solver(double coeff[5], std::complex roots[4]) b /= rfactsq; c /= rfactsq * rfact; d /= rfactsq * rfactsq; - oqs_calc_phi0(a, b, c, d, &phi0, 1); + oqs::calc_phi0(a, b, c, d, &phi0, 1); } l1 = a / 2; /* eq. (16) */ l3 = b / 6 + phi0 / 2; /* eq. (18) */ @@ -403,20 +403,20 @@ void oqs_quartic_solver(double coeff[5], std::complex roots[4]) if (bl311 != 0.0) { d2m[nsol] = bl311; l2m[nsol] = del2 / (2.0 * d2m[nsol]); - res[nsol] = oqs_calc_err_ldlt(b, c, d, d2m[nsol], l1, l2m[nsol], l3); + res[nsol] = oqs::calc_err_ldlt(b, c, d, d2m[nsol], l1, l2m[nsol], l3); nsol++; } if (del2 != 0) { l2m[nsol] = 2 * dml3l3 / del2; if (l2m[nsol] != 0) { d2m[nsol] = del2 / (2 * l2m[nsol]); - res[nsol] = oqs_calc_err_ldlt(b, c, d, d2m[nsol], l1, l2m[nsol], l3); + res[nsol] = oqs::calc_err_ldlt(b, c, d, d2m[nsol], l1, l2m[nsol], l3); nsol++; } d2m[nsol] = bl311; l2m[nsol] = 2.0 * dml3l3 / del2; - res[nsol] = oqs_calc_err_ldlt(b, c, d, d2m[nsol], l1, l2m[nsol], l3); + res[nsol] = oqs::calc_err_ldlt(b, c, d, d2m[nsol], l1, l2m[nsol], l3); nsol++; } @@ -450,16 +450,16 @@ void oqs_quartic_solver(double coeff[5], std::complex roots[4]) nsol = 0; if (dq != 0) { aqv[nsol] = (c - bq * cq) / dq; /* see eqs. (47) */ - errv[nsol] = oqs_calc_err_abc(a, b, c, aqv[nsol], bq, cq, dq); + errv[nsol] = oqs::calc_err_abc(a, b, c, aqv[nsol], bq, cq, dq); nsol++; } if (cq != 0) { aqv[nsol] = (b - dq - bq) / cq; /* see eqs. (47) */ - errv[nsol] = oqs_calc_err_abc(a, b, c, aqv[nsol], bq, cq, dq); + errv[nsol] = oqs::calc_err_abc(a, b, c, aqv[nsol], bq, cq, dq); nsol++; } aqv[nsol] = a - cq; /* see eqs. (47) */ - errv[nsol] = oqs_calc_err_abc(a, b, c, aqv[nsol], bq, cq, dq); + errv[nsol] = oqs::calc_err_abc(a, b, c, aqv[nsol], bq, cq, dq); nsol++; /* we select the value of aq (i.e. alpha1 in the manuscript) which * minimizes errors */ @@ -474,16 +474,16 @@ void oqs_quartic_solver(double coeff[5], std::complex roots[4]) nsol = 0; if (bq != 0) { cqv[nsol] = (c - aq * dq) / bq; /* see eqs. (53) */ - errv[nsol] = oqs_calc_err_abc(a, b, c, aq, bq, cqv[nsol], dq); + errv[nsol] = oqs::calc_err_abc(a, b, c, aq, bq, cqv[nsol], dq); nsol++; } if (aq != 0) { cqv[nsol] = (b - bq - dq) / aq; /* see eqs. (53) */ - errv[nsol] = oqs_calc_err_abc(a, b, c, aq, bq, cqv[nsol], dq); + errv[nsol] = oqs::calc_err_abc(a, b, c, aq, bq, cqv[nsol], dq); nsol++; } cqv[nsol] = a - aq; /* see eqs. (53) */ - errv[nsol] = oqs_calc_err_abc(a, b, c, aq, bq, cqv[nsol], dq); + errv[nsol] = oqs::calc_err_abc(a, b, c, aq, bq, cqv[nsol], dq); nsol++; /* we select the value of cq (i.e. alpha2 in the manuscript) which * minimizes errors */ @@ -509,13 +509,13 @@ void oqs_quartic_solver(double coeff[5], std::complex roots[4]) /* Case III: d2 is 0 or approximately 0 (in this case check which solution is * better) */ if (realcase[0] == -1 || - (std::fabs(d2) <= - macheps * oqs_max3(std::fabs(2. * b / 3.), std::fabs(phi0), l1 * l1))) { + (std::fabs(d2) <= macheps * oqs::max3(std::fabs(2. * b / 3.), + std::fabs(phi0), l1 * l1))) { d3 = d - l3 * l3; if (realcase[0] == 1) - err0 = oqs_calc_err_abcd(a, b, c, d, aq, bq, cq, dq); + err0 = oqs::calc_err_abcd(a, b, c, d, aq, bq, cq, dq); else if (realcase[0] == 0) - err0 = oqs_calc_err_abcd_cmplx(a, b, c, d, acx, bcx, ccx, dcx); + err0 = oqs::calc_err_abcd_cmplx(a, b, c, d, acx, bcx, ccx, dcx); if (d3 <= 0) { realcase[1] = 1; aq1 = l1; @@ -526,15 +526,15 @@ void oqs_quartic_solver(double coeff[5], std::complex roots[4]) dq1 = d / bq1; else if (std::fabs(dq1) > std::fabs(bq1)) bq1 = d / dq1; - err1 = oqs_calc_err_abcd(a, b, c, d, aq1, bq1, cq1, dq1); /* eq. (68) */ - } else /* complex */ + err1 = oqs::calc_err_abcd(a, b, c, d, aq1, bq1, cq1, dq1); /* eq. (68) */ + } else /* complex */ { realcase[1] = 0; acx1 = l1; bcx1 = l3 + std::complex(0., std::sqrt(d3)); ccx1 = l1; dcx1 = std::conj(bcx1); - err1 = oqs_calc_err_abcd_cmplx(a, b, c, d, acx1, bcx1, ccx1, dcx1); + err1 = oqs::calc_err_abcd_cmplx(a, b, c, d, acx1, bcx1, ccx1, dcx1); } if (realcase[0] == -1 || err1 < err0) { whichcase = 1; // d2 = 0 @@ -554,13 +554,13 @@ void oqs_quartic_solver(double coeff[5], std::complex roots[4]) if (realcase[whichcase] == 1) { /* if alpha1, beta1, alpha2 and beta2 are real first refine * the coefficient through a Newton-Raphson */ - oqs_NRabcd(a, b, c, d, &aq, &bq, &cq, &dq); + oqs::NRabcd(a, b, c, d, &aq, &bq, &cq, &dq); /* finally calculate the roots as roots of p1(x) and p2(x) (see end of * sec. 2.1) */ - oqs_solve_quadratic(aq, bq, qroots); + oqs::solve_quadratic(aq, bq, qroots); roots[0] = qroots[0]; roots[1] = qroots[1]; - oqs_solve_quadratic(cq, dq, qroots); + oqs::solve_quadratic(cq, dq, qroots); roots[2] = qroots[0]; roots[3] = qroots[1]; } else { @@ -611,3 +611,5 @@ void oqs_quartic_solver(double coeff[5], std::complex roots[4]) roots[k] *= rfact; } } + +} // namespace oqs diff --git a/src/surface.cpp b/src/surface.cpp index 38a70fcdb1..69188fa359 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -1059,7 +1059,7 @@ double SurfaceXTorus::distance(Position r, Direction ang, bool coincident) const coeff[4] = c2 * c2; std::complex roots[4]; - oqs_quartic_solver(coeff, roots); + oqs::quartic_solver(coeff, roots); // Find smallest positive, real root. In the case where the particle is // coincident with the surface, we are sure to have one root very close to @@ -1147,7 +1147,7 @@ double SurfaceYTorus::distance(Position r, Direction ang, bool coincident) const coeff[4] = c2 * c2; std::complex roots[4]; - oqs_quartic_solver(coeff, roots); + oqs::quartic_solver(coeff, roots); // Find smallest positive, real root. In the case where the particle is // coincident with the surface, we are sure to have one root very close to @@ -1235,7 +1235,7 @@ double SurfaceZTorus::distance(Position r, Direction ang, bool coincident) const coeff[4] = c2 * c2; std::complex roots[4]; - oqs_quartic_solver(coeff, roots); + oqs::quartic_solver(coeff, roots); // Find smallest positive, real root. In the case where the particle is // coincident with the surface, we are sure to have one root very close to From 26e2093cc398837a79773ade7f572b7f512703c8 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 21 Dec 2021 14:56:16 -0500 Subject: [PATCH 15/32] Use constexpr for constants in quartic solver --- src/external/quartic_solver.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/external/quartic_solver.cpp b/src/external/quartic_solver.cpp index 0a427f8e9f..8407bb14c9 100644 --- a/src/external/quartic_solver.cpp +++ b/src/external/quartic_solver.cpp @@ -4,11 +4,12 @@ namespace oqs { -const double cubic_rescal_fact = - 3.488062113727083E+102; //= pow(DBL_MAX,1.0/3.0)/1.618034; -const double quart_rescal_fact = - 7.156344627944542E+76; // = pow(DBL_MAX,1.0/4.0)/1.618034; -const double macheps = 2.2204460492503131E-16; // DBL_EPSILON +constexpr double CUBIC_RESCAL_FACT = + 3.488062113727083e+102; //= pow(DBL_MAX,1.0/3.0)/1.618034; +constexpr double QUART_RESCAL_FACT = + 7.156344627944542e+76; // = pow(DBL_MAX,1.0/4.0)/1.618034; +constexpr double MACHEPS = std::numeric_limits::epsilon(); + double max2(double a, double b) { if (a >= b) @@ -137,7 +138,7 @@ void calc_phi0(double a, double b, double c, double d, double* phi0, int scaled) if ((std::isnan(rmax) || std::isinf(rmax)) && scaled) { // try harder: rescale also the depressed cubic if quartic has been // already rescaled - rfact = cubic_rescal_fact; + rfact = CUBIC_RESCAL_FACT; rfactsq = rfact * rfact; ggss = gg / rfactsq; hhss = hh / rfactsq; @@ -171,7 +172,7 @@ void calc_phi0(double a, double b, double c, double d, double* phi0, int scaled) if (std::fabs(h) > maxtt) maxtt = std::fabs(h); - if (std::fabs(f) > macheps * maxtt) { + if (std::fabs(f) > MACHEPS * maxtt) { for (iter = 0; iter < 8; iter++) { df = 3.0 * xsq + g; if (df == 0) { @@ -383,7 +384,7 @@ void quartic_solver(double coeff[5], std::complex roots[4]) // simple polynomial rescaling if (std::isnan(phi0) || std::isinf(phi0)) { - rfact = quart_rescal_fact; + rfact = QUART_RESCAL_FACT; a /= rfact; rfactsq = rfact * rfact; b /= rfactsq; @@ -509,7 +510,7 @@ void quartic_solver(double coeff[5], std::complex roots[4]) /* Case III: d2 is 0 or approximately 0 (in this case check which solution is * better) */ if (realcase[0] == -1 || - (std::fabs(d2) <= macheps * oqs::max3(std::fabs(2. * b / 3.), + (std::fabs(d2) <= MACHEPS * oqs::max3(std::fabs(2. * b / 3.), std::fabs(phi0), l1 * l1))) { d3 = d - l3 * l3; if (realcase[0] == 1) From 1fc20c9947f7f06fb897fe39244f2ed1608a2a92 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 21 Dec 2021 14:57:57 -0500 Subject: [PATCH 16/32] Remove max2 function in quartic solver --- src/external/quartic_solver.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/external/quartic_solver.cpp b/src/external/quartic_solver.cpp index 8407bb14c9..757cfb8afd 100644 --- a/src/external/quartic_solver.cpp +++ b/src/external/quartic_solver.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -10,18 +11,11 @@ constexpr double QUART_RESCAL_FACT = 7.156344627944542e+76; // = pow(DBL_MAX,1.0/4.0)/1.618034; constexpr double MACHEPS = std::numeric_limits::epsilon(); -double max2(double a, double b) -{ - if (a >= b) - return a; - else - return b; -} double max3(double a, double b, double c) { double t; - t = oqs::max2(a, b); - return oqs::max2(t, c); + t = std::max(a, b); + return std::max(t, c); } void solve_cubic_analytic_depressed_handle_inf(double b, double c, double* sol) From bf4a5fa4f8246c4be2e1469194a5cface1eeaa53 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 21 Dec 2021 15:04:42 -0500 Subject: [PATCH 17/32] Use return values where possible in quartic solver --- src/external/quartic_solver.cpp | 47 ++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/src/external/quartic_solver.cpp b/src/external/quartic_solver.cpp index 757cfb8afd..e4faf7ee65 100644 --- a/src/external/quartic_solver.cpp +++ b/src/external/quartic_solver.cpp @@ -18,7 +18,7 @@ double max3(double a, double b, double c) return std::max(t, c); } -void solve_cubic_analytic_depressed_handle_inf(double b, double c, double* sol) +double solve_cubic_analytic_depressed_handle_inf(double b, double c) { /* find analytically the dominant root of a depressed cubic x^3+b*x+c * where coefficients b and c are large (see sec. 2.2 in the manuscript) */ @@ -29,11 +29,10 @@ void solve_cubic_analytic_depressed_handle_inf(double b, double c, double* sol) R = 0.5 * c; if (R == 0) { if (b <= 0) { - *sol = std::sqrt(-b); + return std::sqrt(-b); } else { - *sol = 0; + return 0; } - return; } if (std::fabs(Q) < std::fabs(R)) { @@ -49,9 +48,9 @@ void solve_cubic_analytic_depressed_handle_inf(double b, double c, double* sol) sqrtQ = std::sqrt(Q); theta = std::acos((R / std::fabs(Q)) / sqrtQ); if (theta < PI2) - *sol = -2.0 * sqrtQ * std::cos(theta / 3.0); + return -2.0 * sqrtQ * std::cos(theta / 3.0); else - *sol = -2.0 * sqrtQ * std::cos((theta + TWOPI) / 3.0); + return -2.0 * sqrtQ * std::cos((theta + TWOPI) / 3.0); } else { if (std::fabs(Q) < std::fabs(R)) A = -std::copysign(1.0, R) * cbrt(std::fabs(R) * (1.0 + std::sqrt(KK))); @@ -64,10 +63,11 @@ void solve_cubic_analytic_depressed_handle_inf(double b, double c, double* sol) B = 0.0; else B = Q / A; - *sol = A + B; + return A + B; } } -void solve_cubic_analytic_depressed(double b, double c, double* sol) + +double solve_cubic_analytic_depressed(double b, double c) { /* find analytically the dominant root of a depressed cubic x^3+b*x+c * (see sec. 2.2 in the manuscript) */ @@ -75,8 +75,7 @@ void solve_cubic_analytic_depressed(double b, double c, double* sol) Q = -b / 3.0; R = 0.5 * c; if (std::fabs(Q) > 1e102 || std::fabs(R) > 1e154) { - oqs::solve_cubic_analytic_depressed_handle_inf(b, c, sol); - return; + return oqs::solve_cubic_analytic_depressed_handle_inf(b, c); } Q3 = Q * Q * Q; R2 = R * R; @@ -84,9 +83,9 @@ void solve_cubic_analytic_depressed(double b, double c, double* sol) theta = std::acos(R / std::sqrt(Q3)); sqrtQ = -2.0 * std::sqrt(Q); if (theta < M_PI / 2) - *sol = sqrtQ * std::cos(theta / 3.0); + return sqrtQ * std::cos(theta / 3.0); else - *sol = sqrtQ * std::cos((theta + 2.0 * M_PI) / 3.0); + return sqrtQ * std::cos((theta + 2.0 * M_PI) / 3.0); } else { A = -std::copysign(1.0, R) * std::pow(std::fabs(R) + std::sqrt(R2 - Q3), 1.0 / 3.0); @@ -94,10 +93,11 @@ void solve_cubic_analytic_depressed(double b, double c, double* sol) B = 0.0; else B = Q / A; - *sol = A + B; /* this is always largest root even if A=B */ + return A + B; /* this is always largest root even if A=B */ } } -void calc_phi0(double a, double b, double c, double d, double* phi0, int scaled) + +double calc_phi0(double a, double b, double c, double d, int scaled) { /* find phi0 as the dominant root of the depressed and shifted cubic * in eq. (79) (see also the discussion in sec. 2.2 of the manuscript) */ @@ -126,9 +126,9 @@ void calc_phi0(double a, double b, double c, double d, double* phi0, int scaled) g = hh - 4 * dq - 3 * gg; /* eq. (85) */ h = (8 * dq + hh - 2 * gg) * bq / 3 - cq * cq - dq * aq * aq; /* eq. (86) */ - oqs::solve_cubic_analytic_depressed(g, h, &rmax); + rmax = oqs::solve_cubic_analytic_depressed(g, h); if (std::isnan(rmax) || std::isinf(rmax)) { - oqs::solve_cubic_analytic_depressed_handle_inf(g, h, &rmax); + rmax = oqs::solve_cubic_analytic_depressed_handle_inf(g, h); if ((std::isnan(rmax) || std::isinf(rmax)) && scaled) { // try harder: rescale also the depressed cubic if quartic has been // already rescaled @@ -145,9 +145,9 @@ void calc_phi0(double a, double b, double c, double d, double* phi0, int scaled) g = hhss - 4.0 * dqss - 3.0 * ggss; h = (8.0 * dqss + hhss - 2.0 * ggss) * bqs / 3 - cqs * (cqs / rfact) - (dq / rfact) * aqs * aqs; - oqs::solve_cubic_analytic_depressed(g, h, &rmax); + rmax = oqs::solve_cubic_analytic_depressed(g, h); if (std::isnan(rmax) || std::isinf(rmax)) { - oqs::solve_cubic_analytic_depressed_handle_inf(g, h, &rmax); + rmax = oqs::solve_cubic_analytic_depressed_handle_inf(g, h); } rmax *= rfact; } @@ -187,8 +187,9 @@ void calc_phi0(double a, double b, double c, double d, double* phi0, int scaled) } } } - *phi0 = x; + return x; } + double calc_err_ldlt( double b, double c, double d, double d2, double l1, double l2, double l3) { @@ -202,6 +203,7 @@ double calc_err_ldlt( : std::fabs(((d2 * l2 * l2 + l3 * l3) - d) / d); return sum; } + double calc_err_abcd_cmplx(double a, double b, double c, double d, std::complex aq, std::complex bq, std::complex cq, std::complex dq) @@ -231,6 +233,7 @@ double calc_err_abcd(double a, double b, double c, double d, double aq, sum += (a == 0) ? std::fabs(aq + cq) : std::fabs(((aq + cq) - a) / a); return sum; } + double calc_err_abc( double a, double b, double c, double aq, double bq, double cq, double dq) { @@ -323,6 +326,7 @@ void NRabcd(double a, double b, double c, double d, double* AQ, double* BQ, *CQ = x[2]; *DQ = x[3]; } + void solve_quadratic(double a, double b, std::complex roots[2]) { double div, sqrtd, diskr, zmax, zmin; @@ -348,6 +352,7 @@ void solve_quadratic(double a, double b, std::complex roots[2]) roots[1] = std::complex(-a / 2, -sqrtd / 2); } } + void quartic_solver(double coeff[5], std::complex roots[4]) { /* USAGE: @@ -374,7 +379,7 @@ void quartic_solver(double coeff[5], std::complex roots[4]) b = coeff[2] / coeff[4]; c = coeff[1] / coeff[4]; d = coeff[0] / coeff[4]; - oqs::calc_phi0(a, b, c, d, &phi0, 0); + phi0 = oqs::calc_phi0(a, b, c, d, 0); // simple polynomial rescaling if (std::isnan(phi0) || std::isinf(phi0)) { @@ -384,7 +389,7 @@ void quartic_solver(double coeff[5], std::complex roots[4]) b /= rfactsq; c /= rfactsq * rfact; d /= rfactsq * rfactsq; - oqs::calc_phi0(a, b, c, d, &phi0, 1); + phi0 = oqs::calc_phi0(a, b, c, d, 1); } l1 = a / 2; /* eq. (16) */ l3 = b / 6 + phi0 / 2; /* eq. (18) */ From 21310c4450ff37812718589b311e680266035d4a Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 21 Dec 2021 16:25:43 -0500 Subject: [PATCH 18/32] Move variable declarations, make simplifications in quadric solver --- src/external/quartic_solver.cpp | 310 ++++++++++++++------------------ 1 file changed, 137 insertions(+), 173 deletions(-) diff --git a/src/external/quartic_solver.cpp b/src/external/quartic_solver.cpp index e4faf7ee65..6ebde9c7fd 100644 --- a/src/external/quartic_solver.cpp +++ b/src/external/quartic_solver.cpp @@ -5,53 +5,46 @@ namespace oqs { -constexpr double CUBIC_RESCAL_FACT = - 3.488062113727083e+102; //= pow(DBL_MAX,1.0/3.0)/1.618034; -constexpr double QUART_RESCAL_FACT = - 7.156344627944542e+76; // = pow(DBL_MAX,1.0/4.0)/1.618034; +// pow(DBL_MAX,1.0/3.0)/1.618034; +constexpr double CUBIC_RESCAL_FACT = 3.488062113727083e+102; +// pow(DBL_MAX,1.0/4.0)/1.618034; +constexpr double QUART_RESCAL_FACT = 7.156344627944542e+76; constexpr double MACHEPS = std::numeric_limits::epsilon(); double max3(double a, double b, double c) { - double t; - t = std::max(a, b); - return std::max(t, c); + return std::max(std::max(a, b), c); } double solve_cubic_analytic_depressed_handle_inf(double b, double c) { /* find analytically the dominant root of a depressed cubic x^3+b*x+c * where coefficients b and c are large (see sec. 2.2 in the manuscript) */ - double Q, R, theta, A, B, QR, QRSQ, KK, sqrtQ, RQ; - ; - const double PI2 = M_PI / 2.0, TWOPI = 2.0 * M_PI; - Q = -b / 3.0; - R = 0.5 * c; + double Q = -b / 3.0; + double R = 0.5 * c; if (R == 0) { - if (b <= 0) { - return std::sqrt(-b); - } else { - return 0; - } + return (b <= 0) ? std::sqrt(-b) : 0; } + double KK; if (std::fabs(Q) < std::fabs(R)) { - QR = Q / R; - QRSQ = QR * QR; + double QR = Q / R; + double QRSQ = QR * QR; KK = 1.0 - Q * QRSQ; } else { - RQ = R / Q; + double RQ = R / Q; KK = std::copysign(1.0, Q) * (RQ * RQ / Q - 1.0); } if (KK < 0.0) { - sqrtQ = std::sqrt(Q); - theta = std::acos((R / std::fabs(Q)) / sqrtQ); - if (theta < PI2) + double sqrtQ = std::sqrt(Q); + double theta = std::acos((R / std::fabs(Q)) / sqrtQ); + if (2.0 * theta < M_PI) return -2.0 * sqrtQ * std::cos(theta / 3.0); else - return -2.0 * sqrtQ * std::cos((theta + TWOPI) / 3.0); + return -2.0 * sqrtQ * std::cos((theta + 2.0 * M_PI) / 3.0); } else { + double A; if (std::fabs(Q) < std::fabs(R)) A = -std::copysign(1.0, R) * cbrt(std::fabs(R) * (1.0 + std::sqrt(KK))); else { @@ -59,10 +52,7 @@ double solve_cubic_analytic_depressed_handle_inf(double b, double c) cbrt(std::fabs(R) + std::sqrt(std::fabs(Q)) * std::fabs(Q) * std::sqrt(KK)); } - if (A == 0.0) - B = 0.0; - else - B = Q / A; + double B = (A == 0.0) ? 0.0 : Q / A; return A + B; } } @@ -71,28 +61,24 @@ double solve_cubic_analytic_depressed(double b, double c) { /* find analytically the dominant root of a depressed cubic x^3+b*x+c * (see sec. 2.2 in the manuscript) */ - double Q, R, theta, Q3, R2, A, B, sqrtQ; - Q = -b / 3.0; - R = 0.5 * c; + double Q = -b / 3.0; + double R = 0.5 * c; if (std::fabs(Q) > 1e102 || std::fabs(R) > 1e154) { return oqs::solve_cubic_analytic_depressed_handle_inf(b, c); } - Q3 = Q * Q * Q; - R2 = R * R; + double Q3 = Q * Q * Q; + double R2 = R * R; if (R2 < Q3) { - theta = std::acos(R / std::sqrt(Q3)); - sqrtQ = -2.0 * std::sqrt(Q); - if (theta < M_PI / 2) + double theta = std::acos(R / std::sqrt(Q3)); + double sqrtQ = -2.0 * std::sqrt(Q); + if (2.0 * theta < M_PI) return sqrtQ * std::cos(theta / 3.0); else return sqrtQ * std::cos((theta + 2.0 * M_PI) / 3.0); } else { - A = -std::copysign(1.0, R) * - std::pow(std::fabs(R) + std::sqrt(R2 - Q3), 1.0 / 3.0); - if (A == 0.0) - B = 0.0; - else - B = Q / A; + double A = -std::copysign(1.0, R) * + std::pow(std::fabs(R) + std::sqrt(R2 - Q3), 1.0 / 3.0); + double B = (A == 0.0) ? 0.0 : Q / A; return A + B; /* this is always largest root even if A=B */ } } @@ -101,45 +87,40 @@ double calc_phi0(double a, double b, double c, double d, int scaled) { /* find phi0 as the dominant root of the depressed and shifted cubic * in eq. (79) (see also the discussion in sec. 2.2 of the manuscript) */ - double rmax, g, h, gg, hh, aq, bq, cq, dq, s, diskr; - double maxtt, xxx, gx, x, xold, f, fold, df, xsq; - double ggss, hhss, dqss, aqs, bqs, cqs, rfact, rfactsq; - int iter; - diskr = 9 * a * a - 24 * b; + double diskr = 9 * a * a - 24 * b; /* eq. (87) */ + double s; if (diskr > 0.0) { diskr = std::sqrt(diskr); - if (a > 0.0) - s = -2 * b / (3 * a + diskr); - else - s = -2 * b / (3 * a - diskr); + s = -2 * b / (3 * a + std::copysign(diskr, a)); } else { s = -a / 4; } /* eqs. (83) */ - aq = a + 4 * s; - bq = b + 3 * s * (a + 2 * s); - cq = c + s * (2 * b + s * (3 * a + 4 * s)); - dq = d + s * (c + s * (b + s * (a + s))); - gg = bq * bq / 9; - hh = aq * cq; + double aq = a + 4 * s; + double bq = b + 3 * s * (a + 2 * s); + double cq = c + s * (2 * b + s * (3 * a + 4 * s)); + double dq = d + s * (c + s * (b + s * (a + s))); + double gg = bq * bq / 9; + double hh = aq * cq; - g = hh - 4 * dq - 3 * gg; /* eq. (85) */ - h = (8 * dq + hh - 2 * gg) * bq / 3 - cq * cq - dq * aq * aq; /* eq. (86) */ - rmax = oqs::solve_cubic_analytic_depressed(g, h); + double g = hh - 4 * dq - 3 * gg; /* eq. (85) */ + double h = + (8 * dq + hh - 2 * gg) * bq / 3 - cq * cq - dq * aq * aq; /* eq. (86) */ + double rmax = oqs::solve_cubic_analytic_depressed(g, h); if (std::isnan(rmax) || std::isinf(rmax)) { rmax = oqs::solve_cubic_analytic_depressed_handle_inf(g, h); if ((std::isnan(rmax) || std::isinf(rmax)) && scaled) { // try harder: rescale also the depressed cubic if quartic has been // already rescaled - rfact = CUBIC_RESCAL_FACT; - rfactsq = rfact * rfact; - ggss = gg / rfactsq; - hhss = hh / rfactsq; - dqss = dq / rfactsq; - aqs = aq / rfact; - bqs = bq / rfact; - cqs = cq / rfact; + double rfact = CUBIC_RESCAL_FACT; + double rfactsq = rfact * rfact; + double ggss = gg / rfactsq; + double hhss = hh / rfactsq; + double dqss = dq / rfactsq; + double aqs = aq / rfact; + double bqs = bq / rfact; + double cqs = cq / rfact; ggss = bqs * bqs / 9.0; hhss = aqs * cqs; g = hhss - 4.0 * dqss - 3.0 * ggss; @@ -154,27 +135,24 @@ double calc_phi0(double a, double b, double c, double d, int scaled) } /* Newton-Raphson used to refine phi0 (see end of sec. 2.2 in the manuscript) */ - x = rmax; - xsq = x * x; - xxx = x * xsq; - gx = g * x; - f = x * (xsq + g) + h; - if (std::fabs(xxx) > std::fabs(gx)) - maxtt = std::fabs(xxx); - else - maxtt = std::fabs(gx); + double x = rmax; + double xsq = x * x; + double xxx = x * xsq; + double gx = g * x; + double f = x * (xsq + g) + h; + double maxtt = std::max(std::fabs(xxx), std::fabs(gx)); if (std::fabs(h) > maxtt) maxtt = std::fabs(h); if (std::fabs(f) > MACHEPS * maxtt) { - for (iter = 0; iter < 8; iter++) { - df = 3.0 * xsq + g; + for (int iter = 0; iter < 8; iter++) { + double df = 3.0 * xsq + g; if (df == 0) { break; } - xold = x; + double xold = x; x += -f / df; - fold = f; + double fold = f; xsq = x * x; f = x * (xsq + g) + h; if (f == 0) { @@ -194,9 +172,8 @@ double calc_err_ldlt( double b, double c, double d, double d2, double l1, double l2, double l3) { /* Eqs. (29) and (30) in the manuscript */ - double sum; - sum = (b == 0) ? std::fabs(d2 + l1 * l1 + 2.0 * l3) - : std::fabs(((d2 + l1 * l1 + 2.0 * l3) - b) / b); + double sum = (b == 0) ? std::fabs(d2 + l1 * l1 + 2.0 * l3) + : std::fabs(((d2 + l1 * l1 + 2.0 * l3) - b) / b); sum += (c == 0) ? std::fabs(2.0 * d2 * l2 + 2.0 * l1 * l3) : std::fabs(((2.0 * d2 * l2 + 2.0 * l1 * l3) - c) / c); sum += (d == 0) ? std::fabs(d2 * l2 * l2 + l3 * l3) @@ -210,8 +187,7 @@ double calc_err_abcd_cmplx(double a, double b, double c, double d, { /* Eqs. (68) and (69) in the manuscript for complex alpha1 (aq), beta1 (bq), * alpha2 (cq) and beta2 (dq) */ - double sum; - sum = (d == 0) ? std::abs(bq * dq) : std::abs((bq * dq - d) / d); + double sum = (d == 0) ? std::abs(bq * dq) : std::abs((bq * dq - d) / d); sum += (c == 0) ? std::abs(bq * cq + aq * dq) : std::abs(((bq * cq + aq * dq) - c) / c); sum += (b == 0) ? std::abs(bq + aq * cq + dq) @@ -224,8 +200,7 @@ double calc_err_abcd(double a, double b, double c, double d, double aq, { /* Eqs. (68) and (69) in the manuscript for real alpha1 (aq), beta1 (bq), * alpha2 (cq) and beta2 (dq)*/ - double sum; - sum = (d == 0) ? std::fabs(bq * dq) : std::fabs((bq * dq - d) / d); + double sum = (d == 0) ? std::fabs(bq * dq) : std::fabs((bq * dq - d) / d); sum += (c == 0) ? std::fabs(bq * cq + aq * dq) : std::fabs(((bq * cq + aq * dq) - c) / c); sum += (b == 0) ? std::fabs(bq + aq * cq + dq) @@ -238,9 +213,8 @@ double calc_err_abc( double a, double b, double c, double aq, double bq, double cq, double dq) { /* Eqs. (48)-(51) in the manuscript */ - double sum; - sum = (c == 0) ? std::fabs(bq * cq + aq * dq) - : std::fabs(((bq * cq + aq * dq) - c) / c); + double sum = (c == 0) ? std::fabs(bq * cq + aq * dq) + : std::fabs(((bq * cq + aq * dq) - c) / c); sum += (b == 0) ? std::fabs(bq + aq * cq + dq) : std::fabs(((bq + aq * cq + dq) - b) / b); sum += (a == 0) ? std::fabs(aq + cq) : std::fabs(((aq + cq) - a) / a); @@ -251,9 +225,7 @@ void NRabcd(double a, double b, double c, double d, double* AQ, double* BQ, { /* Newton-Raphson described in sec. 2.3 of the manuscript for complex * coefficients a,b,c,d */ - int iter, k1, k2; - double x02, errf, errfold, xold[4], x[4], dx[4], det, Jinv[4][4], fvec[4], - vr[4]; + double xold[4], x[4], dx[4], det, Jinv[4][4], fvec[4], vr[4]; x[0] = *AQ; x[1] = *BQ; x[2] = *CQ; @@ -266,12 +238,12 @@ void NRabcd(double a, double b, double c, double d, double* AQ, double* BQ, fvec[1] = x[1] * x[2] + x[0] * x[3] - c; fvec[2] = x[1] + x[0] * x[2] + x[3] - b; fvec[3] = x[0] + x[2] - a; - errf = 0; - for (k1 = 0; k1 < 4; k1++) { + double errf = 0; + for (int k1 = 0; k1 < 4; k1++) { errf += (vr[k1] == 0) ? std::fabs(fvec[k1]) : std::fabs(fvec[k1] / vr[k1]); } - for (iter = 0; iter < 8; iter++) { - x02 = x[0] - x[2]; + for (int iter = 0; iter < 8; iter++) { + double x02 = x[0] - x[2]; det = x[1] * x[1] + x[1] * (-x[2] * x02 - 2.0 * x[3]) + x[3] * (x[0] * x02 + x[3]); if (det == 0.0) @@ -292,31 +264,31 @@ void NRabcd(double a, double b, double c, double d, double* AQ, double* BQ, Jinv[3][1] = Jinv[0][0] * x[3]; Jinv[3][2] = x[3] * Jinv[0][1]; Jinv[3][3] = x[3] * Jinv[0][2]; - for (k1 = 0; k1 < 4; k1++) { + for (int k1 = 0; k1 < 4; k1++) { dx[k1] = 0; - for (k2 = 0; k2 < 4; k2++) + for (int k2 = 0; k2 < 4; k2++) dx[k1] += Jinv[k1][k2] * fvec[k2]; } - for (k1 = 0; k1 < 4; k1++) + for (int k1 = 0; k1 < 4; k1++) xold[k1] = x[k1]; - for (k1 = 0; k1 < 4; k1++) { + for (int k1 = 0; k1 < 4; k1++) { x[k1] += -dx[k1] / det; } fvec[0] = x[1] * x[3] - d; fvec[1] = x[1] * x[2] + x[0] * x[3] - c; fvec[2] = x[1] + x[0] * x[2] + x[3] - b; fvec[3] = x[0] + x[2] - a; - errfold = errf; + double errfold = errf; errf = 0; - for (k1 = 0; k1 < 4; k1++) { + for (int k1 = 0; k1 < 4; k1++) { errf += (vr[k1] == 0) ? std::fabs(fvec[k1]) : std::fabs(fvec[k1] / vr[k1]); } if (errf == 0) break; if (errf >= errfold) { - for (k1 = 0; k1 < 4; k1++) + for (int k1 = 0; k1 < 4; k1++) x[k1] = xold[k1]; break; } @@ -329,25 +301,16 @@ void NRabcd(double a, double b, double c, double d, double* AQ, double* BQ, void solve_quadratic(double a, double b, std::complex roots[2]) { - double div, sqrtd, diskr, zmax, zmin; - diskr = a * a - 4 * b; + double diskr = a * a - 4 * b; if (diskr >= 0.0) { - if (a >= 0.0) - div = -a - std::sqrt(diskr); - else - div = -a + std::sqrt(diskr); - - zmax = div / 2; - - if (zmax == 0.0) - zmin = 0.0; - else - zmin = b / zmax; + double div = -a - std::copysign(std::sqrt(diskr), a); + double zmax = div / 2; + double zmin = (zmax == 0.0) ? 0.0 : b / zmax; roots[0] = std::complex(zmax, 0.0); roots[1] = std::complex(zmin, 0.0); } else { - sqrtd = std::sqrt(-diskr); + double sqrtd = std::sqrt(-diskr); roots[0] = std::complex(-a / 2, sqrtd / 2); roots[1] = std::complex(-a / 2, -sqrtd / 2); } @@ -366,37 +329,36 @@ void quartic_solver(double coeff[5], std::complex roots[4]) * the four roots will be stored in the complex array roots[] * * */ - std::complex acx1, bcx1, ccx1, dcx1, acx, bcx, ccx, dcx, cdiskr, zx1, - zx2, zxmax, zxmin, qroots[2]; - double l2m[12], d2m[12], res[12], resmin, bl311, dml3l3, err0 = 0, err1 = 0, - aq1, bq1, cq1, dq1; - double a, b, c, d, phi0, aq, bq, cq, dq, d2, d3, l1, l2, l3, errmin, errv[3], - aqv[3], cqv[3], gamma, del2; - int realcase[2], whichcase, k1, k, kmin, nsol; - double rfactsq, rfact = 1.0; + std::complex acx, bcx, ccx, dcx; + double l2m[12], d2m[12], res[12]; + double errv[3], aqv[3], cqv[3]; + int realcase[2]; - a = coeff[3] / coeff[4]; - b = coeff[2] / coeff[4]; - c = coeff[1] / coeff[4]; - d = coeff[0] / coeff[4]; - phi0 = oqs::calc_phi0(a, b, c, d, 0); + double a = coeff[3] / coeff[4]; + double b = coeff[2] / coeff[4]; + double c = coeff[1] / coeff[4]; + double d = coeff[0] / coeff[4]; + double phi0 = oqs::calc_phi0(a, b, c, d, 0); // simple polynomial rescaling + double rfact = 1.0; if (std::isnan(phi0) || std::isinf(phi0)) { rfact = QUART_RESCAL_FACT; a /= rfact; - rfactsq = rfact * rfact; + double rfactsq = rfact * rfact; b /= rfactsq; c /= rfactsq * rfact; d /= rfactsq * rfactsq; phi0 = oqs::calc_phi0(a, b, c, d, 1); } - l1 = a / 2; /* eq. (16) */ - l3 = b / 6 + phi0 / 2; /* eq. (18) */ - del2 = c - a * l3; /* defined just after eq. (27) */ - nsol = 0; - bl311 = 2. * b / 3. - phi0 - l1 * l1; /* This is d2 as defined in eq. (20)*/ - dml3l3 = d - l3 * l3; /* dml3l3 is d3 as defined in eq. (15) with d2=0 */ + double l1 = a / 2; /* eq. (16) */ + double l3 = b / 6 + phi0 / 2; /* eq. (18) */ + double del2 = c - a * l3; /* defined just after eq. (27) */ + int nsol = 0; + double bl311 = + 2. * b / 3. - phi0 - l1 * l1; /* This is d2 as defined in eq. (20)*/ + double dml3l3 = + d - l3 * l3; /* dml3l3 is d3 as defined in eq. (15) with d2=0 */ /* Three possible solutions for d2 and l2 (see eqs. (28) and discussion which * follows) */ @@ -420,11 +382,14 @@ void quartic_solver(double coeff[5], std::complex roots[4]) nsol++; } + double l2, d2; if (nsol == 0) { l2 = d2 = 0.0; } else { /* we select the (d2,l2) pair which minimizes errors */ - for (k1 = 0; k1 < nsol; k1++) { + double resmin; + int kmin; + for (int k1 = 0; k1 < nsol; k1++) { if (k1 == 0 || res[k1] < resmin) { resmin = res[k1]; kmin = k1; @@ -433,10 +398,11 @@ void quartic_solver(double coeff[5], std::complex roots[4]) d2 = d2m[kmin]; l2 = l2m[kmin]; } - whichcase = 0; + int whichcase = 0; + double aq, bq, cq, dq; if (d2 < 0.0) { /* Case I eqs. (37)-(40) */ - gamma = std::sqrt(-d2); + double gamma = std::sqrt(-d2); aq = l1 + gamma; bq = l3 + gamma * l2; @@ -463,7 +429,9 @@ void quartic_solver(double coeff[5], std::complex roots[4]) nsol++; /* we select the value of aq (i.e. alpha1 in the manuscript) which * minimizes errors */ - for (k = 0; k < nsol; k++) { + double errmin; + int kmin; + for (int k = 0; k < nsol; k++) { if (k == 0 || errv[k] < errmin) { kmin = k; errmin = errv[k]; @@ -487,7 +455,9 @@ void quartic_solver(double coeff[5], std::complex roots[4]) nsol++; /* we select the value of cq (i.e. alpha2 in the manuscript) which * minimizes errors */ - for (k = 0; k < nsol; k++) { + double errmin; + int kmin; + for (int k = 0; k < nsol; k++) { if (k == 0 || errv[k] < errmin) { kmin = k; errmin = errv[k]; @@ -498,7 +468,7 @@ void quartic_solver(double coeff[5], std::complex roots[4]) realcase[0] = 1; } else if (d2 > 0) { /* Case II eqs. (53)-(56) */ - gamma = std::sqrt(d2); + double gamma = std::sqrt(d2); acx = std::complex(l1, gamma); bcx = std::complex(l3, gamma * l2); ccx = std::conj(acx); @@ -511,11 +481,15 @@ void quartic_solver(double coeff[5], std::complex roots[4]) if (realcase[0] == -1 || (std::fabs(d2) <= MACHEPS * oqs::max3(std::fabs(2. * b / 3.), std::fabs(phi0), l1 * l1))) { - d3 = d - l3 * l3; + double d3 = d - l3 * l3; + double err0 = 0.0; if (realcase[0] == 1) err0 = oqs::calc_err_abcd(a, b, c, d, aq, bq, cq, dq); else if (realcase[0] == 0) err0 = oqs::calc_err_abcd_cmplx(a, b, c, d, acx, bcx, ccx, dcx); + double aq1, bq1, cq1, dq1; + std::complex acx1, bcx1, ccx1, dcx1; + double err1 = 0.0; if (d3 <= 0) { realcase[1] = 1; aq1 = l1; @@ -527,8 +501,8 @@ void quartic_solver(double coeff[5], std::complex roots[4]) else if (std::fabs(dq1) > std::fabs(bq1)) bq1 = d / dq1; err1 = oqs::calc_err_abcd(a, b, c, d, aq1, bq1, cq1, dq1); /* eq. (68) */ - } else /* complex */ - { + } else { + /* complex */ realcase[1] = 0; acx1 = l1; bcx1 = l3 + std::complex(0., std::sqrt(d3)); @@ -557,6 +531,7 @@ void quartic_solver(double coeff[5], std::complex roots[4]) oqs::NRabcd(a, b, c, d, &aq, &bq, &cq, &dq); /* finally calculate the roots as roots of p1(x) and p2(x) (see end of * sec. 2.1) */ + std::complex qroots[2]; oqs::solve_quadratic(aq, bq, qroots); roots[0] = qroots[0]; roots[1] = qroots[1]; @@ -565,49 +540,38 @@ void quartic_solver(double coeff[5], std::complex roots[4]) roots[3] = qroots[1]; } else { /* complex coefficients of p1 and p2 */ - if (whichcase == 0) // d2!=0 - { - cdiskr = 0.25 * acx * acx - bcx; + if (whichcase == 0) { // d2!=0 + auto cdiskr = 0.25 * acx * acx - bcx; /* calculate the roots as roots of p1(x) and p2(x) (see end of sec. 2.1) */ - zx1 = -0.5 * acx + std::sqrt(cdiskr); - zx2 = -0.5 * acx - std::sqrt(cdiskr); - if (std::abs(zx1) > std::abs(zx2)) - zxmax = zx1; - else - zxmax = zx2; - zxmin = bcx / zxmax; + auto zx1 = -0.5 * acx + std::sqrt(cdiskr); + auto zx2 = -0.5 * acx - std::sqrt(cdiskr); + auto zxmax = (std::abs(zx1) > std::abs(zx2)) ? zx1 : zx2; + auto zxmin = bcx / zxmax; roots[0] = zxmin; roots[1] = std::conj(zxmin); roots[2] = zxmax; roots[3] = std::conj(zxmax); - } else // d2 ~ 0 - { + } else { // d2 ~ 0 /* never gets here! */ - cdiskr = std::sqrt(acx * acx - 4.0 * bcx); - zx1 = -0.5 * (acx + cdiskr); - zx2 = -0.5 * (acx - cdiskr); - if (std::abs(zx1) > std::abs(zx2)) - zxmax = zx1; - else - zxmax = zx2; - zxmin = bcx / zxmax; + auto cdiskr = std::sqrt(acx * acx - 4.0 * bcx); + auto zx1 = -0.5 * (acx + cdiskr); + auto zx2 = -0.5 * (acx - cdiskr); + auto zxmax = (std::abs(zx1) > std::abs(zx2)) ? zx1 : zx2; + auto zxmin = bcx / zxmax; roots[0] = zxmax; roots[1] = zxmin; cdiskr = std::sqrt(ccx * ccx - 4.0 * dcx); zx1 = -0.5 * (ccx + cdiskr); zx2 = -0.5 * (ccx - cdiskr); - if (std::abs(zx1) > std::abs(zx2)) - zxmax = zx1; - else - zxmax = zx2; + zxmax = (std::abs(zx1) > std::abs(zx2)) ? zx1 : zx2; zxmin = dcx / zxmax; roots[2] = zxmax; roots[3] = zxmin; } } if (rfact != 1.0) { - for (k = 0; k < 4; k++) + for (int k = 0; k < 4; k++) roots[k] *= rfact; } } From b0fbb4e804f9243920d2d7a8a4bbe9c45b2c22bf Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 21 Dec 2021 17:11:01 -0500 Subject: [PATCH 19/32] Use single torus_distance function --- src/surface.cpp | 184 ++++++++++++++++-------------------------------- 1 file changed, 61 insertions(+), 123 deletions(-) diff --git a/src/surface.cpp b/src/surface.cpp index 69188fa359..d60b0c52ed 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -1006,6 +1006,53 @@ void SurfaceQuadric::to_hdf5_inner(hid_t group_id) const write_dataset(group_id, "coefficients", coeffs); } +//============================================================================== +// Torus helper functions +//============================================================================== + +double torus_distance(double x1, double x2, double x3, double u1, double u2, + double u3, double A, double B, double C, bool coincident) +{ + // Coefficients for equation: (c2 t^2 + c1 t + c0)^2 = d2 t^2 + d1 t + d0 + double D = (C * C) / (B * B); + double c2 = u1 * u1 + u2 * u2 + D * u3 * u3; + double c1 = 2 * (u1 * x1 + u2 * x2 + D * u3 * x3); + double c0 = x1 * x1 + x2 * x2 + D * x3 * x3 + A * A - C * C; + double four_A2 = 4 * A * A; + double d2 = four_A2 * (u1 * u1 + u2 * u2); + double d1 = 2 * four_A2 * (u1 * x1 + u2 * x2); + double d0 = four_A2 * (x1 * x1 + x2 * x2); + + // Coefficient for equation: a t^4 + b t^3 + c t^2 + d t + e = 0 + double coeff[5]; + coeff[0] = c0 * c0 - d0; + coeff[1] = 2 * c0 * c1 - d1; + coeff[2] = c1 * c1 + 2 * c0 * c2 - d2; + coeff[3] = 2 * c1 * c2; + coeff[4] = c2 * c2; + + std::complex roots[4]; + oqs::quartic_solver(coeff, roots); + + // Find smallest positive, real root. In the case where the particle is + // coincident with the surface, we are sure to have one root very close to + // zero but possibly small and positive. A tolerance is set to discard that + // zero. + double distance = INFTY; + double cutoff = coincident ? 1e-9 : 0.0; + for (int i = 0; i < 4; ++i) { + if (roots[i].imag() == 0) { + double root = roots[i].real(); + if (root > cutoff && root < distance) { + distance = root; + } + } + } + return distance; +} + +//============================================================================== +// SurfaceXTorus implementation //============================================================================== SurfaceXTorus::SurfaceXTorus(pugi::xml_node surf_node) : CSGSurface(surf_node) @@ -1031,51 +1078,12 @@ double SurfaceXTorus::evaluate(Position r) const std::pow(std::sqrt(y * y + z * z) - A_, 2) / (C_ * C_) - 1.; } -double SurfaceXTorus::distance(Position r, Direction ang, bool coincident) const +double SurfaceXTorus::distance(Position r, Direction u, bool coincident) const { - double u = ang.x; - double v = ang.y; - double w = ang.z; double x = r.x - x0_; double y = r.y - y0_; double z = r.z - z0_; - - // Coefficients for equation: (c2 t^2 + c1 t + c0)^2 = d2 t^2 + d1 t + d0 - double D = (C_ * C_) / (B_ * B_); - double c2 = D * u * u + v * v + w * w; - double c1 = 2 * (D * u * x + v * y + w * z); - double c0 = D * x * x + y * y + z * z + A_ * A_ - C_ * C_; - double four_A2 = 4 * A_ * A_; - double d2 = four_A2 * (v * v + w * w); - double d1 = 2 * four_A2 * (v * y + w * z); - double d0 = four_A2 * (y * y + z * z); - - // Coefficient for equation: a t^4 + b t^3 + c t^2 + d t + e = 0 - double coeff[5]; - coeff[0] = c0 * c0 - d0; - coeff[1] = 2 * c0 * c1 - d1; - coeff[2] = c1 * c1 + 2 * c0 * c2 - d2; - coeff[3] = 2 * c1 * c2; - coeff[4] = c2 * c2; - - std::complex roots[4]; - oqs::quartic_solver(coeff, roots); - - // Find smallest positive, real root. In the case where the particle is - // coincident with the surface, we are sure to have one root very close to - // zero but possibly small and positive. A tolerance is set to discard that - // zero. - double distance = INFTY; - double cutoff = coincident ? 1e-9 : 0.0; - for (int i = 0; i < 4; ++i) { - if (roots[i].imag() == 0) { - double root = roots[i].real(); - if (root > cutoff && root < distance) { - distance = root; - } - } - } - return distance; + return torus_distance(y, z, x, u.y, u.z, u.x, A_, B_, C_, coincident); } Direction SurfaceXTorus::normal(Position r) const @@ -1098,6 +1106,10 @@ Direction SurfaceXTorus::normal(Position r) const return n / n.norm(); } +//============================================================================== +// SurfaceYTorus implementation +//============================================================================== + SurfaceYTorus::SurfaceYTorus(pugi::xml_node surf_node) : CSGSurface(surf_node) { read_coeffs(surf_node, id_, x0_, y0_, z0_, A_, B_, C_); @@ -1119,51 +1131,12 @@ double SurfaceYTorus::evaluate(Position r) const std::pow(std::sqrt(x * x + z * z) - A_, 2) / (C_ * C_) - 1.; } -double SurfaceYTorus::distance(Position r, Direction ang, bool coincident) const +double SurfaceYTorus::distance(Position r, Direction u, bool coincident) const { - double u = ang.x; - double v = ang.y; - double w = ang.z; double x = r.x - x0_; double y = r.y - y0_; double z = r.z - z0_; - - // Coefficients for equation: (c2 t^2 + c1 t + c0)^2 = d2 t^2 + d1 t + d0 - double D = (C_ * C_) / (B_ * B_); - double c2 = u * u + D * v * v + w * w; - double c1 = 2 * (u * x + D * v * y + w * z); - double c0 = x * x + D * y * y + z * z + A_ * A_ - C_ * C_; - double four_A2 = 4 * A_ * A_; - double d2 = four_A2 * (u * u + w * w); - double d1 = 2 * four_A2 * (u * x + w * z); - double d0 = four_A2 * (x * x + z * z); - - // Coefficient for equation: a t^4 + b t^3 + c t^2 + d t + e = 0 - double coeff[5]; - coeff[0] = c0 * c0 - d0; - coeff[1] = 2 * c0 * c1 - d1; - coeff[2] = c1 * c1 + 2 * c0 * c2 - d2; - coeff[3] = 2 * c1 * c2; - coeff[4] = c2 * c2; - - std::complex roots[4]; - oqs::quartic_solver(coeff, roots); - - // Find smallest positive, real root. In the case where the particle is - // coincident with the surface, we are sure to have one root very close to - // zero but possibly small and positive. A tolerance is set to discard that - // zero. - double distance = INFTY; - double cutoff = coincident ? 1e-9 : 0.0; - for (int i = 0; i < 4; ++i) { - if (roots[i].imag() == 0) { - double root = roots[i].real(); - if (root > cutoff && root < distance) { - distance = root; - } - } - } - return distance; + return torus_distance(x, z, y, u.x, u.z, u.y, A_, B_, C_, coincident); } Direction SurfaceYTorus::normal(Position r) const @@ -1186,6 +1159,10 @@ Direction SurfaceYTorus::normal(Position r) const return n / n.norm(); } +//============================================================================== +// SurfaceZTorus implementation +//============================================================================== + SurfaceZTorus::SurfaceZTorus(pugi::xml_node surf_node) : CSGSurface(surf_node) { read_coeffs(surf_node, id_, x0_, y0_, z0_, A_, B_, C_); @@ -1207,51 +1184,12 @@ double SurfaceZTorus::evaluate(Position r) const std::pow(std::sqrt(x * x + y * y) - A_, 2) / (C_ * C_) - 1.; } -double SurfaceZTorus::distance(Position r, Direction ang, bool coincident) const +double SurfaceZTorus::distance(Position r, Direction u, bool coincident) const { - double u = ang.x; - double v = ang.y; - double w = ang.z; double x = r.x - x0_; double y = r.y - y0_; double z = r.z - z0_; - - // Coefficients for equation: (c2 t^2 + c1 t + c0)^2 = d2 t^2 + d1 t + d0 - double D = (C_ * C_) / (B_ * B_); - double c2 = u * u + v * v + D * w * w; - double c1 = 2 * (u * x + v * y + D * w * z); - double c0 = x * x + y * y + D * z * z + A_ * A_ - C_ * C_; - double four_A2 = 4 * A_ * A_; - double d2 = four_A2 * (u * u + v * v); - double d1 = 2 * four_A2 * (u * x + v * y); - double d0 = four_A2 * (x * x + y * y); - - // Coefficient for equation: a t^4 + b t^3 + c t^2 + d t + e = 0 - double coeff[5]; - coeff[0] = c0 * c0 - d0; - coeff[1] = 2 * c0 * c1 - d1; - coeff[2] = c1 * c1 + 2 * c0 * c2 - d2; - coeff[3] = 2 * c1 * c2; - coeff[4] = c2 * c2; - - std::complex roots[4]; - oqs::quartic_solver(coeff, roots); - - // Find smallest positive, real root. In the case where the particle is - // coincident with the surface, we are sure to have one root very close to - // zero but possibly small and positive. A tolerance is set to discard that - // zero. - double distance = INFTY; - double cutoff = coincident ? 1e-9 : 0.0; - for (int i = 0; i < 4; ++i) { - if (roots[i].imag() == 0) { - double root = roots[i].real(); - if (root > cutoff && root < distance) { - distance = root; - } - } - } - return distance; + return torus_distance(x, y, z, u.x, u.y, u.z, A_, B_, C_, coincident); } Direction SurfaceZTorus::normal(Position r) const From a57d7dcd7f52da2183eaf562a033d18a514f18cb Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 22 Dec 2021 08:57:44 -0500 Subject: [PATCH 20/32] Add documentation on torus distance/normal --- docs/source/io_formats/geometry.rst | 2 +- docs/source/io_formats/summary.rst | 3 +- docs/source/methods/geometry.rst | 101 +++++++++++++++++++++++++++- src/surface.cpp | 14 ++-- 4 files changed, 110 insertions(+), 10 deletions(-) diff --git a/docs/source/io_formats/geometry.rst b/docs/source/io_formats/geometry.rst index b04edca2b7..ff2f6eb74e 100644 --- a/docs/source/io_formats/geometry.rst +++ b/docs/source/io_formats/geometry.rst @@ -26,7 +26,7 @@ Each ```` element can have the following attributes or sub-elements: :type: The type of the surfaces. This can be "x-plane", "y-plane", "z-plane", "plane", "x-cylinder", "y-cylinder", "z-cylinder", "sphere", "x-cone", - "y-cone", "z-cone", or "quadric". + "y-cone", "z-cone", "quadric", "x-torus", "y-torus", or "z-torus". *Default*: None diff --git a/docs/source/io_formats/summary.rst b/docs/source/io_formats/summary.rst index 9b92b30187..c140d98ae3 100644 --- a/docs/source/io_formats/summary.rst +++ b/docs/source/io_formats/summary.rst @@ -55,7 +55,8 @@ The current version of the summary file format is 6.0. :Datasets: - **name** (*char[]*) -- Name of the surface. - **type** (*char[]*) -- Type of the surface. Can be 'x-plane', 'y-plane', 'z-plane', 'plane', 'x-cylinder', 'y-cylinder', - 'z-cylinder', 'sphere', 'x-cone', 'y-cone', 'z-cone', or 'quadric'. + 'z-cylinder', 'sphere', 'x-cone', 'y-cone', 'z-cone', 'quadric', + 'x-torus', 'y-torus', or 'z-torus'. - **coefficients** (*double[]*) -- Array of coefficients that define the surface. See :ref:`surface_element` for what coefficients are defined for each surface type. diff --git a/docs/source/methods/geometry.rst b/docs/source/methods/geometry.rst index 2c7b7ecfb2..c087290225 100644 --- a/docs/source/methods/geometry.rst +++ b/docs/source/methods/geometry.rst @@ -118,7 +118,19 @@ to fully define the surface. +----------------------+------------+------------------------------+-------------------------+ | General quadric | quadric | :math:`Ax^2 + By^2 + Cz^2 + | :math:`A \; B \; C \; D | | surface | | Dxy + Eyz + Fxz + Gx + Hy + | \; E \; F \; G \; H \; | - | | | Jz + K` | J \; K` | + | | | Jz + K = 0` | J \; K` | + +----------------------+------------+------------------------------+-------------------------+ + | Torus parallel to the| x-torus | :math:`(x-x_0)^2/B^2+\frac{( | :math:`x_0 \; y_0 \; | + | :math:`x`-axis | | \sqrt{(y-y_0)^2+(z-z_0)^2} - | z_0 \; A \; B \; C` | + | | | A)^2}{C^2} - 1 = 0` | | + +----------------------+------------+------------------------------+-------------------------+ + | Torus parallel to the| y-torus | :math:`(y-y_0)^2/B^2+\frac{( | :math:`x_0 \; y_0 \; | + | :math:`y`-axis | | \sqrt{(x-x_0)^2+(z-z_0)^2} - | z_0 \; A \; B \; C` | + | | | A)^2}{C^2} - 1 = 0` | | + +----------------------+------------+------------------------------+-------------------------+ + | Torus parallel to the| z-torus | :math:`(z-z_0)^2/B^2+\frac{( | :math:`x_0 \; y_0 \; | + | :math:`z`-axis | | \sqrt{(x-x_0)^2+(y-y_0)^2} - | z_0 \; A \; B \; C` | + | | | A)^2}{C^2} - 1 = 0` | | +----------------------+------------+------------------------------+-------------------------+ .. _universes: @@ -437,6 +449,72 @@ Defining the terms we then have the simple quadratic equation :math:`ad^2 + 2kd + c = 0` which can be solved as described in :ref:`cylinder_distance`. +Torus Parallel to an Axis +------------------------- + +The equation for a torus parallel to, for example, the x-axis is + +.. math:: + :label: dist-xtorus-sqrt + + \frac{(x-x_0)^2}{B^2} + \frac{(\sqrt{(y-y_0)^2 + (z-z_0)^2} - A)^2}{C^2} - + 1 = 0. + +First, it needs to be cast into a polynomial form. Rearranging terms, + +.. math:: + :label: dist-xtorus-1 + + (D\bar{x}^2 + \bar{y}^2 + \bar{z}^2 + A^2 - C^2)^2 = 4A^2(\bar{y}^2 + + \bar{z}^2) + +where :math:`D = (C/B)^2`, :math:`\bar{x} = x - x_0`, :math:`\bar{y} = y - y_0`, +and :math:`\bar{z} = z - z_0`. To find the distance to the surface, we thus need +to solve + +.. math:: + :label: dist-xtorus-2 + + (D(\bar{x} + du)^2 + (\bar{y} + dv)^2 + (\bar{z} + dw)^2 + A^2 - C^2)^2 = + 4A^2((\bar{y} + dv)^2 + (\bar{z} + dw)^2). + +Expanding and collecting like powers of :math:`d` yields + +.. math:: + :label: dist-xtorus-3 + + (c_2d^2 + c_1d + c_0)^2 = c_2'd^2 + c_1'd + c_0' + +where + +.. math:: + :label: dist-xtorus-4 + + \begin{align*} + c_2 &= Du^2 + v^2 + w^2 \\ + c_1 &= 2(Du\bar{x} + v\bar{y} + w\bar{z}) \\ + c_0 &= D\bar{x}^2 + \bar{y}^2 + \bar{z}^2 + A^2 - C^2 \\ + c_2' &= 4A^2 (v^2 + w^2) \\ + c_1' &= 8A^2 (v\bar{y} + w\bar{z}) \\ + c_0' &= 4A^2(\bar{y}^2 + \bar{z}^2). + \end{align*} + +Expanding the left-hand side and collecting like powers of :math:`d` on one +side, we obtain + +.. math:: + :label: dist-xtorus-5 + + (c_2^2)d^4 + (2c_1c_2)d^3 + (c_1^2 + 2c_0c_2 - c_2')d^2 + (2c_0c_1 - c_1')d + + (c_0^2 - c_0') = 0. + +The above equation is a fourth-order (quartic) polynomial equation. Although +there is an analytical solution to the general quartic equation, it can be +subject to roundoff errors when evaluated numerically. OpenMC uses an external +`quartic equation solver `_ developed by +Orellana and De Michele that is based on the decomposition of the quartic +polynomial into two quadratics. + .. _find-cell: ---------------------------- @@ -899,6 +977,27 @@ Dxy + Eyz + Fxz + Gx + Hy + Jz + K = 0`. Thus, the gradient to the surface is \nabla f = \left ( \begin{array}{c} 2Ax + Dy + Fz + G \\ 2By + Dx + Ez + H \\ 2Cz + Ey + Fx + J \end{array} \right ). +Torus Parallel to an Axis +------------------------- + +A cone parallel to, for example, the x-axis has the form + +.. math:: + :label: reflection-torus-1 + + f(x,y,z) = \frac{(x-x_0)^2}{B^2} + \frac{(\sqrt{(y-y_0)^2 + (z-z_0)^2} - + A)^2}{C^2} - 1. + +The gradient to the surface is therefore + +.. math:: + :label: reflection-torus-grad + + \nabla f = \left ( \begin{array}{c} 2\bar{x}/B^2 \\ 2\bar{y}(g - A)/(C^2g) + \\ 2\bar{z}(g - A)/(C^2g) \end{array} \right ) + +where :math:`g = \sqrt{\bar{y}^2 + \bar{z}^2}` and, as always, :math:`\bar{x} = +x - x_0`, :math:`\bar{y} = y - y_0`, and :math:`\bar{z} = z - z_0`. .. _white: diff --git a/src/surface.cpp b/src/surface.cpp index d60b0c52ed..9323629cdc 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -1013,21 +1013,21 @@ void SurfaceQuadric::to_hdf5_inner(hid_t group_id) const double torus_distance(double x1, double x2, double x3, double u1, double u2, double u3, double A, double B, double C, bool coincident) { - // Coefficients for equation: (c2 t^2 + c1 t + c0)^2 = d2 t^2 + d1 t + d0 + // Coefficients for equation: (c2 t^2 + c1 t + c0)^2 = c2' t^2 + c1' t + c0' double D = (C * C) / (B * B); double c2 = u1 * u1 + u2 * u2 + D * u3 * u3; double c1 = 2 * (u1 * x1 + u2 * x2 + D * u3 * x3); double c0 = x1 * x1 + x2 * x2 + D * x3 * x3 + A * A - C * C; double four_A2 = 4 * A * A; - double d2 = four_A2 * (u1 * u1 + u2 * u2); - double d1 = 2 * four_A2 * (u1 * x1 + u2 * x2); - double d0 = four_A2 * (x1 * x1 + x2 * x2); + double c2p = four_A2 * (u1 * u1 + u2 * u2); + double c1p = 2 * four_A2 * (u1 * x1 + u2 * x2); + double c0p = four_A2 * (x1 * x1 + x2 * x2); // Coefficient for equation: a t^4 + b t^3 + c t^2 + d t + e = 0 double coeff[5]; - coeff[0] = c0 * c0 - d0; - coeff[1] = 2 * c0 * c1 - d1; - coeff[2] = c1 * c1 + 2 * c0 * c2 - d2; + coeff[0] = c0 * c0 - c0p; + coeff[1] = 2 * c0 * c1 - c1p; + coeff[2] = c1 * c1 + 2 * c0 * c2 - c2p; coeff[3] = 2 * c1 * c2; coeff[4] = c2 * c2; From cd803764c57a9c4fa6ec233c7a1d595efa337761 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 22 Dec 2021 09:53:34 -0500 Subject: [PATCH 21/32] Add torus bounding_box methods and fix docstrings --- docs/source/pythonapi/base.rst | 3 + openmc/surface.py | 166 ++++++++++++++++++++++++--------- 2 files changed, 125 insertions(+), 44 deletions(-) diff --git a/docs/source/pythonapi/base.rst b/docs/source/pythonapi/base.rst index 342b4c1a64..496201ad77 100644 --- a/docs/source/pythonapi/base.rst +++ b/docs/source/pythonapi/base.rst @@ -80,6 +80,9 @@ Building geometry openmc.YCone openmc.ZCone openmc.Quadric + openmc.XTorus + openmc.YTorus + openmc.ZTorus openmc.Halfspace openmc.Intersection openmc.Union diff --git a/openmc/surface.py b/openmc/surface.py index 19df0804ad..e00dc8962e 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -648,7 +648,7 @@ class Plane(PlaneMixin, Surface): The 'C' parameter for the plane. Defaults to 0. d : float, optional The 'D' parameter for the plane. Defaults to 0. - boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional + boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. @@ -668,7 +668,7 @@ class Plane(PlaneMixin, Surface): The 'C' parameter for the plane d : float The 'D' parameter for the plane - boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} + boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'} Boundary condition that defines the behavior for particles hitting the surface. periodic_surface : openmc.Surface @@ -2064,7 +2064,7 @@ class Quadric(QuadricMixin, Surface): ---------- a, b, c, d, e, f, g, h, j, k : float, optional coefficients for the surface. All default to 0. - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'}, optional + boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. @@ -2078,7 +2078,7 @@ class Quadric(QuadricMixin, Surface): ---------- a, b, c, d, e, f, g, h, j, k : float coefficients for the surface - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'} + boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. coefficients : dict @@ -2119,6 +2119,7 @@ class Quadric(QuadricMixin, Surface): class TorusMixin: + """A Mixin class implementing common functionality for torus surfaces""" _coeff_keys = ('x0', 'y0', 'z0', 'a', 'b', 'c') def __init__(self, x0=0., y0=0., z0=0., a=0., b=0., c=0., **kwargs): @@ -2141,26 +2142,48 @@ class TorusMixin: return surf def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False): - raise NotImplemented('Torus surfaces cannot be rotated.') + raise NotImplementedError('Torus surfaces cannot be rotated.') def _get_base_coeffs(self): raise NotImplementedError class XTorus(TorusMixin, Surface): - """description. + """A torus of the form :math:`(x - x_0)^2/B^2 + (\sqrt{(y - y_0)^2 + (z - + z_0)^2} - A)^2/C^2 - 1 = 0`. Parameters ---------- - x0, y0, z0, a, b, c : float, optional - coefficients for the surface. All default to 0. - kwargs + x0 : float + x-coordinate of the center of the axis of revolution + y0 : float + y-coordinate of the center of the axis of revolution + z0 : float + z-coordinate of the center of the axis of revolution + a : float + Major radius of the torus + b : float + Minor radius of the torus (parallel to axis of revolution) + c : float + Minor radius of the torus (perpendicular to axis of revolution) + kwargs : dict + Keyword arguments passed to the :class:`Surface` constructor Attributes ---------- - a, b, c, d, e, f, g, h, j, k : float - coefficients for the surface - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'} + x0 : float + x-coordinate of the center of the axis of revolution + y0 : float + y-coordinate of the center of the axis of revolution + z0 : float + z-coordinate of the center of the axis of revolution + a : float + Major radius of the torus + b : float + Minor radius of the torus (parallel to axis of revolution) + c : float + Minor radius of the torus (perpendicular to axis of revolution) + boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. coefficients : dict @@ -2184,29 +2207,52 @@ class XTorus(TorusMixin, Surface): c = self.c return (x*x)/(b*b) + (math.sqrt(y*y + z*z) - a)**2/(c*c) - 1 + def bounding_box(self, side): + x0, y0, z0 = self.x0, self.y0, self.z0 + a, b, c = self.a, self.b, self.c + if side == '-': + return (np.array([x0 - b, y0 - a - c, z0 - a - c]), + np.array([x0 + b, y0 + a + c, z0 + a + c])) + elif side == '+': + return (np.array([-np.inf, -np.inf, -np.inf]), + np.array([np.inf, np.inf, np.inf])) class YTorus(TorusMixin, Surface): - """description. + """A torus of the form :math:`(y - y_0)^2/B^2 + (\sqrt{(x - x_0)^2 + (z - + z_0)^2} - A)^2/C^2 - 1 = 0`. Parameters ---------- - surface_id : int, optional - Unique identifier for the surface. If not specified, an identifier will - automatically be assigned. - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}, optional - Boundary condition that defines the behavior for particles hitting the - surface. Defaults to transmissive boundary condition where particles - freely pass through the surface. - x0, y0 ,z0, A, B, C, : float, optional - coefficients for the surface. All default to 0. - name : str, optional - Name of the surface. If not specified, the name will be the empty string. + x0 : float + x-coordinate of the center of the axis of revolution + y0 : float + y-coordinate of the center of the axis of revolution + z0 : float + z-coordinate of the center of the axis of revolution + a : float + Major radius of the torus + b : float + Minor radius of the torus (parallel to axis of revolution) + c : float + Minor radius of the torus (perpendicular to axis of revolution) + kwargs : dict + Keyword arguments passed to the :class:`Surface` constructor Attributes ---------- - a, b, c, d, e, f, g, h, j, k : float - coefficients for the surface - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'} + x0 : float + x-coordinate of the center of the axis of revolution + y0 : float + y-coordinate of the center of the axis of revolution + z0 : float + z-coordinate of the center of the axis of revolution + a : float + Major radius of the torus + b : float + Minor radius of the torus (parallel to axis of revolution) + c : float + Minor radius of the torus (perpendicular to axis of revolution) + boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. coefficients : dict @@ -2230,29 +2276,52 @@ class YTorus(TorusMixin, Surface): c = self.c return (y*y)/(b*b) + (math.sqrt(x*x + z*z) - a)**2/(c*c) - 1 + def bounding_box(self, side): + x0, y0, z0 = self.x0, self.y0, self.z0 + a, b, c = self.a, self.b, self.c + if side == '-': + return (np.array([x0 - a - c, y0 - b, z0 - a - c]), + np.array([x0 + a + c, y0 + b, z0 + a + c])) + elif side == '+': + return (np.array([-np.inf, -np.inf, -np.inf]), + np.array([np.inf, np.inf, np.inf])) class ZTorus(TorusMixin, Surface): - """description. + """A torus of the form :math:`(z - z_0)^2/B^2 + (\sqrt{(x - x_0)^2 + (y - + y_0)^2} - A)^2/C^2 - 1 = 0`. Parameters ---------- - surface_id : int, optional - Unique identifier for the surface. If not specified, an identifier will - automatically be assigned. - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}, optional - Boundary condition that defines the behavior for particles hitting the - surface. Defaults to transmissive boundary condition where particles - freely pass through the surface. - x0, y0 ,z0, A, B, C, : float, optional - coefficients for the surface. All default to 0. - name : str, optional - Name of the surface. If not specified, the name will be the empty string. + x0 : float + x-coordinate of the center of the axis of revolution + y0 : float + y-coordinate of the center of the axis of revolution + z0 : float + z-coordinate of the center of the axis of revolution + a : float + Major radius of the torus + b : float + Minor radius of the torus (parallel to axis of revolution) + c : float + Minor radius of the torus (perpendicular to axis of revolution) + kwargs : dict + Keyword arguments passed to the :class:`Surface` constructor Attributes ---------- - a, b, c, d, e, f, g, h, j, k : float - coefficients for the surface - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'} + x0 : float + x-coordinate of the center of the axis of revolution + y0 : float + y-coordinate of the center of the axis of revolution + z0 : float + z-coordinate of the center of the axis of revolution + a : float + Major radius of the torus + b : float + Minor radius of the torus (parallel to axis of revolution) + c : float + Minor radius of the torus (perpendicular to axis of revolution) + boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. coefficients : dict @@ -2263,7 +2332,6 @@ class ZTorus(TorusMixin, Surface): Name of the surface type : str Type of the surface - """ _type = 'z-torus' @@ -2277,6 +2345,16 @@ class ZTorus(TorusMixin, Surface): c = self.c return (z*z)/(b*b) + (math.sqrt(x*x + y*y) - a)**2/(c*c) - 1 + def bounding_box(self, side): + x0, y0, z0 = self.x0, self.y0, self.z0 + a, b, c = self.a, self.b, self.c + if side == '-': + return (np.array([x0 - a - c, y0 - a - c, z0 - b]), + np.array([x0 + a + c, y0 + a + c, z0 + b])) + elif side == '+': + return (np.array([-np.inf, -np.inf, -np.inf]), + np.array([np.inf, np.inf, np.inf])) + class Halfspace(Region): """A positive or negative half-space region. @@ -2520,7 +2598,7 @@ class Halfspace(Region): _SURFACE_CLASSES = {cls._type: cls for cls in Surface.__subclasses__()} -# Set virtual base classes for "casting" up the heirarchy +# Set virtual base classes for "casting" up the hierarchy Plane._virtual_base = Plane XPlane._virtual_base = Plane YPlane._virtual_base = Plane From 149180ec7fc3b70fd8b0c414650a7a91b40320dd Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 22 Dec 2021 11:32:03 -0500 Subject: [PATCH 22/32] Add unit and regression tests for torus --- tests/regression_tests/torus/__init__.py | 0 tests/regression_tests/torus/inputs_true.dat | 34 +++++ tests/regression_tests/torus/results_true.dat | 2 + tests/regression_tests/torus/test.py | 39 ++++++ tests/unit_tests/test_surface.py | 124 ++++++++++++++++++ tests/unit_tests/test_torus.py | 45 +++++++ 6 files changed, 244 insertions(+) create mode 100644 tests/regression_tests/torus/__init__.py create mode 100644 tests/regression_tests/torus/inputs_true.dat create mode 100644 tests/regression_tests/torus/results_true.dat create mode 100644 tests/regression_tests/torus/test.py create mode 100644 tests/unit_tests/test_torus.py diff --git a/tests/regression_tests/torus/__init__.py b/tests/regression_tests/torus/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/torus/inputs_true.dat b/tests/regression_tests/torus/inputs_true.dat new file mode 100644 index 0000000000..0045d7e466 --- /dev/null +++ b/tests/regression_tests/torus/inputs_true.dat @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 1000 + 10 + 5 + diff --git a/tests/regression_tests/torus/results_true.dat b/tests/regression_tests/torus/results_true.dat new file mode 100644 index 0000000000..42444ad09a --- /dev/null +++ b/tests/regression_tests/torus/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +7.628424E-01 2.557300E-02 diff --git a/tests/regression_tests/torus/test.py b/tests/regression_tests/torus/test.py new file mode 100644 index 0000000000..8970a5cc77 --- /dev/null +++ b/tests/regression_tests/torus/test.py @@ -0,0 +1,39 @@ +import openmc +import pytest + +from tests.testing_harness import PyAPITestHarness + + +@pytest.fixture +def model(): + model = openmc.Model() + fuel = openmc.Material() + fuel.set_density('g/cm3', 12.0) + fuel.add_nuclide('U235', 1.0) + al = openmc.Material() + al.set_density('g/cm3', 1.0) + al.add_nuclide('H1', 1.0) + model.materials.extend([fuel, al]) + + # 🍩🍩🍩 + zt = openmc.ZTorus(a=3, b=1.5, c=1) + xt = openmc.XTorus(x0=6, a=3, b=1.5, c=1) + yt = openmc.YTorus(x0=6, a=6, b=1, c=0.75) + box = openmc.model.RectangularParallelepiped(-5, 14, -5, 5, -8, 8, + boundary_type='vacuum') + + xt_cell = openmc.Cell(fill=fuel, region=-xt) + yt_cell = openmc.Cell(fill=fuel, region=-yt) + zt_cell = openmc.Cell(fill=fuel, region=-zt) + outer_cell = openmc.Cell(fill=al, region=-box & +xt & +yt & +zt) + model.geometry = openmc.Geometry([xt_cell, yt_cell, zt_cell, outer_cell]) + + model.settings.particles = 1000 + model.settings.batches = 10 + model.settings.inactive = 5 + return model + + +def test_torus(model): + harness = PyAPITestHarness('statepoint.10.h5', model) + harness.main() diff --git a/tests/unit_tests/test_surface.py b/tests/unit_tests/test_surface.py index e7d37c4e91..c24d6e3521 100644 --- a/tests/unit_tests/test_surface.py +++ b/tests/unit_tests/test_surface.py @@ -605,3 +605,127 @@ def test_cylinder_from_points_axis(): assert (d, e, f) == pytest.approx((0., 0., 0.)) assert (g, h, j) == pytest.approx((-4., 0., -10.)) assert k == pytest.approx(13.) + + +def torus_common(center, R, r1, r2, cls): + x, y, z = center + s = cls(x0=x, y0=y, z0=z, a=R, b=r1, c=r2) + assert s.x0 == x + assert s.y0 == y + assert s.z0 == z + assert s.a == R + assert s.b == r1 + assert s.c == r2 + + # evaluate method + assert s.evaluate((x, y, z)) > 0.0 + + # translate method + trans = np.array([1.0, 1.5, -2.0]) + st = s.translate(trans) + assert st.x0 == s.x0 + trans[0] + assert st.y0 == s.y0 + trans[1] + assert st.z0 == s.z0 + trans[2] + assert st.a == s.a + assert st.b == s.b + assert st.c == s.c + + # can't rotate at present + with pytest.raises(NotImplementedError): + s.rotate((0., 1., 0.)) + + # Check bounding box + ll, ur = (+s).bounding_box + assert np.all(np.isinf(ll)) + assert np.all(np.isinf(ur)) + + ll, ur = (-s).bounding_box + llt, urt = (-st).bounding_box + np.testing.assert_allclose(ll + trans, llt) + np.testing.assert_allclose(ur + trans, urt) + + # Make sure repr works + repr(s) + + return s + + +def test_xtorus(): + x, y, z = 2, -4, 5 + R, r1, r2 = 3, 1.5, 1 + s = torus_common((x, y, z), R, r1, r2, openmc.XTorus) + + # evaluate method (points inside torus) + assert s.evaluate((x, y + R, z)) < 0.0 + assert s.evaluate((x, y - R, z)) < 0.0 + assert s.evaluate((x, y, z + R)) < 0.0 + assert s.evaluate((x, y, z - R)) < 0.0 + + # evaluate method (points on torus) + assert s.evaluate((x, y + R + r2, z)) == pytest.approx(0.0) + assert s.evaluate((x, y - R - r2, z)) == pytest.approx(0.0) + assert s.evaluate((x, y, z + R - r2)) == pytest.approx(0.0) + assert s.evaluate((x, y, z - R + r2)) == pytest.approx(0.0) + assert s.evaluate((x + r1, y + R, z)) == pytest.approx(0.0) + + # evaluate method (points outside torus) + assert s.evaluate((x, y + R, z + R)) > 0.0 + assert s.evaluate((x, y - R, z + R)) > 0.0 + assert s.evaluate((x, y + R + r2 + 0.01, z)) > 0.0 + assert s.evaluate((x, y, z + R + r2 + 0.01)) > 0.0 + assert s.evaluate((x + r1 + 0.01, y, z + R)) > 0.0 + assert s.evaluate((x + r1 + 0.01, y + R, z)) > 0.0 + + +def test_ytorus(): + x, y, z = 2, -4, 5 + R, r1, r2 = 3, 1.5, 1 + s = torus_common((x, y, z), R, r1, r2, openmc.YTorus) + + # evaluate method (points inside torus) + assert s.evaluate((x + R, y, z)) < 0.0 + assert s.evaluate((x - R, y, z)) < 0.0 + assert s.evaluate((x, y, z + R)) < 0.0 + assert s.evaluate((x, y, z - R)) < 0.0 + + # evaluate method (points on torus) + assert s.evaluate((x + R + r2, y, z)) == pytest.approx(0.0) + assert s.evaluate((x - R - r2, y, z)) == pytest.approx(0.0) + assert s.evaluate((x, y, z + R - r2)) == pytest.approx(0.0) + assert s.evaluate((x, y, z - R + r2)) == pytest.approx(0.0) + assert s.evaluate((x + R, y + r1, z)) == pytest.approx(0.0) + + # evaluate method (points outside torus) + assert s.evaluate((x + R, y, z + R)) > 0.0 + assert s.evaluate((x - R, y, z + R)) > 0.0 + assert s.evaluate((x + R + r2 + 0.01, y, z)) > 0.0 + assert s.evaluate((x, y, z + R + r2 + 0.01)) > 0.0 + assert s.evaluate((x, y + r1 + 0.01, z + R)) > 0.0 + assert s.evaluate((x + R, y + r1 + 0.01, z)) > 0.0 + + +def test_ztorus(): + x, y, z = 2, -4, 5 + R, r1, r2 = 3, 1.5, 1 + s = torus_common((x, y, z), R, r1, r2, openmc.ZTorus) + + # evaluate method (points inside torus) + assert s.evaluate((x, y + R, z)) < 0.0 + assert s.evaluate((x, y - R, z)) < 0.0 + assert s.evaluate((x + R, y, z)) < 0.0 + assert s.evaluate((x - R, y, z)) < 0.0 + + # evaluate method (points on torus) + assert s.evaluate((x, y + R + r2, z)) == pytest.approx(0.0) + assert s.evaluate((x, y - R - r2, z)) == pytest.approx(0.0) + assert s.evaluate((x + R - r2, y, z)) == pytest.approx(0.0) + assert s.evaluate((x - R + r2, y, z)) == pytest.approx(0.0) + assert s.evaluate((x, y + R, z + r1)) == pytest.approx(0.0) + + # evaluate method (points outside torus) + assert s.evaluate((x + R, y + R, z)) > 0.0 + assert s.evaluate((x + R, y - R, z)) > 0.0 + assert s.evaluate((x, y + R + r2 + 0.01, z)) > 0.0 + assert s.evaluate((x + R + r2 + 0.01, y, z)) > 0.0 + assert s.evaluate((x + R, y, z + r1 + 0.01)) > 0.0 + assert s.evaluate((x, y + R, z + r1 + 0.01)) > 0.0 diff --git a/tests/unit_tests/test_torus.py b/tests/unit_tests/test_torus.py new file mode 100644 index 0000000000..925949afa7 --- /dev/null +++ b/tests/unit_tests/test_torus.py @@ -0,0 +1,45 @@ +from itertools import combinations +from random import uniform +import openmc + + +def get_torus_keff(cls, center=(0, 0, 0)): + model = openmc.Model() + mat = openmc.Material() + mat.add_nuclide('U235', 1.0) + mat.set_density('g/cm3', 10.0) + + x, y, z = center + torus = cls(x0=x, y0=y, z0=z, a=3, b=2, c=2) + sphere = openmc.Sphere(x0=x, y0=y, z0=z, r=5.5, boundary_type="vacuum") + torus_cell = openmc.Cell(fill=mat, region=-torus) + outer_cell = openmc.Cell(region=+torus & -sphere) + model.geometry = openmc.Geometry([torus_cell, outer_cell]) + + model.settings.source = openmc.Source(space=openmc.stats.Point(center)) + model.settings.batches = 10 + model.settings.inactive = 5 + model.settings.particles = 1000 + + sp_path = model.run() + with openmc.StatePoint(sp_path) as sp: + return sp.k_combined + + +def test_torus_keff(run_in_tmpdir): + random_point = lambda: (uniform(-5, 5), uniform(-5, 5), uniform(-5, 5)) + keffs = [ + get_torus_keff(openmc.XTorus), + get_torus_keff(openmc.XTorus, random_point()), + get_torus_keff(openmc.YTorus), + get_torus_keff(openmc.YTorus, random_point()), + get_torus_keff(openmc.ZTorus), + get_torus_keff(openmc.ZTorus, random_point()) + ] + + # For each combination of keff values, their difference should be within + # uncertainty (3 std dev) + for k1, k2 in combinations(keffs, 2): + print(k1, k2) + diff = k1 - k2 + assert abs(diff.n) < 3*diff.s From c21f03daa37adaa979995bcae99468b73969eb13 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 22 Dec 2021 11:38:42 -0500 Subject: [PATCH 23/32] Minor tweaks --- include/openmc/surface.h | 21 +++++++++------------ src/surface.cpp | 6 ++---- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/include/openmc/surface.h b/include/openmc/surface.h index c1aaf2cc15..a92550c605 100644 --- a/include/openmc/surface.h +++ b/include/openmc/surface.h @@ -374,55 +374,52 @@ public: //============================================================================== //! A toroidal surface described by the quartic torus lies in the x direction // -//! \f$(x-x_0)^2 + (\sqrt{(y-y_0)^2 + (z-z_0)^2}-A)^2/C^2 -1 \f$ +//! \f$(x-x_0)^2 + (\sqrt{(y-y_0)^2 + (z-z_0)^2} - A)^2/C^2 -1 \f$ //============================================================================== class SurfaceXTorus : public CSGSurface { - // (x-x0)^2/B^2 + (((y-y0)^2 + (z-z0)^2)^-1/2 -A))^2/C^2 -1 = 0 - double x0_, y0_, z0_, A_, B_, C_; - public: explicit SurfaceXTorus(pugi::xml_node surf_node); double evaluate(Position r) const; double distance(Position r, Direction u, bool coincident) const; Direction normal(Position r) const; void to_hdf5_inner(hid_t group_id) const; + + double x0_, y0_, z0_, A_, B_, C_; }; //============================================================================== //! A toroidal surface described by the quartic torus lies in the y direction // -//! \f$(x-x_0)^2 + (\sqrt{(y-y_0)^2 + (z-z_0)^2}-A)^2/C^2 -1 \f$ +//! \f$(x-x_0)^2 + (\sqrt{(y-y_0)^2 + (z-z_0)^2} - A)^2/C^2 -1 \f$ //============================================================================== class SurfaceYTorus : public CSGSurface { - // (y-y0)^2/B^2 + (((x-x0)^2 + (z-z0)^2)^-1/2 -A))^2/C^2 -1 = 0 - double x0_, y0_, z0_, A_, B_, C_; - public: explicit SurfaceYTorus(pugi::xml_node surf_node); double evaluate(Position r) const; double distance(Position r, Direction u, bool coincident) const; Direction normal(Position r) const; void to_hdf5_inner(hid_t group_id) const; + + double x0_, y0_, z0_, A_, B_, C_; }; //============================================================================== //! A toroidal surface described by the quartic torus lies in the z direction // -//! \f$(x-x_0)^2 + (\sqrt{(y-y_0)^2 + (z-z_0)^2}-A)^2/C^2 -1 \f$ +//! \f$(x-x_0)^2 + (\sqrt{(y-y_0)^2 + (z-z_0)^2} - A)^2/C^2 -1 \f$ //============================================================================== class SurfaceZTorus : public CSGSurface { - // (z-z0)^2/B^2 + (((x-x0)^2 + (y-y0)^2)^-1/2 -A))^2/C^2 -1 = 0 - double x0_, y0_, z0_, A_, B_, C_; - public: explicit SurfaceZTorus(pugi::xml_node surf_node); double evaluate(Position r) const; double distance(Position r, Direction u, bool coincident) const; Direction normal(Position r) const; void to_hdf5_inner(hid_t group_id) const; + + double x0_, y0_, z0_, A_, B_, C_; }; //============================================================================== diff --git a/src/surface.cpp b/src/surface.cpp index 9323629cdc..10a0026108 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -94,10 +94,8 @@ void read_coeffs(pugi::xml_node surf_node, int surf_id, double& c1, double& c2, std::string coeffs = get_node_value(surf_node, "coeffs"); int n_words = word_count(coeffs); if (n_words != 6) { - std::stringstream err_msg; - err_msg << "Surface " << surf_id << " expects 6 coeffs but was given " - << n_words; - fatal_error(err_msg); + fatal_error(fmt::format( + "Surface {} expects 6 coeffs but was given {}", surf_id, n_words)); } // Parse the coefficients. From d497d5496612e975d2ec5a30a860c4997fe21dc7 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 29 Dec 2021 09:37:05 -0500 Subject: [PATCH 24/32] Use std::abs instead of std::fabs in quartic solver --- src/external/quartic_solver.cpp | 79 ++++++++++++++++----------------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/src/external/quartic_solver.cpp b/src/external/quartic_solver.cpp index 6ebde9c7fd..cfefa02ea3 100644 --- a/src/external/quartic_solver.cpp +++ b/src/external/quartic_solver.cpp @@ -27,7 +27,7 @@ double solve_cubic_analytic_depressed_handle_inf(double b, double c) } double KK; - if (std::fabs(Q) < std::fabs(R)) { + if (std::abs(Q) < std::abs(R)) { double QR = Q / R; double QRSQ = QR * QR; KK = 1.0 - Q * QRSQ; @@ -38,19 +38,19 @@ double solve_cubic_analytic_depressed_handle_inf(double b, double c) if (KK < 0.0) { double sqrtQ = std::sqrt(Q); - double theta = std::acos((R / std::fabs(Q)) / sqrtQ); + double theta = std::acos((R / std::abs(Q)) / sqrtQ); if (2.0 * theta < M_PI) return -2.0 * sqrtQ * std::cos(theta / 3.0); else return -2.0 * sqrtQ * std::cos((theta + 2.0 * M_PI) / 3.0); } else { double A; - if (std::fabs(Q) < std::fabs(R)) - A = -std::copysign(1.0, R) * cbrt(std::fabs(R) * (1.0 + std::sqrt(KK))); + if (std::abs(Q) < std::abs(R)) + A = -std::copysign(1.0, R) * cbrt(std::abs(R) * (1.0 + std::sqrt(KK))); else { A = -std::copysign(1.0, R) * - cbrt(std::fabs(R) + - std::sqrt(std::fabs(Q)) * std::fabs(Q) * std::sqrt(KK)); + cbrt( + std::abs(R) + std::sqrt(std::abs(Q)) * std::abs(Q) * std::sqrt(KK)); } double B = (A == 0.0) ? 0.0 : Q / A; return A + B; @@ -63,7 +63,7 @@ double solve_cubic_analytic_depressed(double b, double c) * (see sec. 2.2 in the manuscript) */ double Q = -b / 3.0; double R = 0.5 * c; - if (std::fabs(Q) > 1e102 || std::fabs(R) > 1e154) { + if (std::abs(Q) > 1e102 || std::abs(R) > 1e154) { return oqs::solve_cubic_analytic_depressed_handle_inf(b, c); } double Q3 = Q * Q * Q; @@ -77,7 +77,7 @@ double solve_cubic_analytic_depressed(double b, double c) return sqrtQ * std::cos((theta + 2.0 * M_PI) / 3.0); } else { double A = -std::copysign(1.0, R) * - std::pow(std::fabs(R) + std::sqrt(R2 - Q3), 1.0 / 3.0); + std::pow(std::abs(R) + std::sqrt(R2 - Q3), 1.0 / 3.0); double B = (A == 0.0) ? 0.0 : Q / A; return A + B; /* this is always largest root even if A=B */ } @@ -140,11 +140,11 @@ double calc_phi0(double a, double b, double c, double d, int scaled) double xxx = x * xsq; double gx = g * x; double f = x * (xsq + g) + h; - double maxtt = std::max(std::fabs(xxx), std::fabs(gx)); - if (std::fabs(h) > maxtt) - maxtt = std::fabs(h); + double maxtt = std::max(std::abs(xxx), std::abs(gx)); + if (std::abs(h) > maxtt) + maxtt = std::abs(h); - if (std::fabs(f) > MACHEPS * maxtt) { + if (std::abs(f) > MACHEPS * maxtt) { for (int iter = 0; iter < 8; iter++) { double df = 3.0 * xsq + g; if (df == 0) { @@ -159,7 +159,7 @@ double calc_phi0(double a, double b, double c, double d, int scaled) break; } - if (std::fabs(f) >= std::fabs(fold)) { + if (std::abs(f) >= std::abs(fold)) { x = xold; break; } @@ -172,12 +172,12 @@ double calc_err_ldlt( double b, double c, double d, double d2, double l1, double l2, double l3) { /* Eqs. (29) and (30) in the manuscript */ - double sum = (b == 0) ? std::fabs(d2 + l1 * l1 + 2.0 * l3) - : std::fabs(((d2 + l1 * l1 + 2.0 * l3) - b) / b); - sum += (c == 0) ? std::fabs(2.0 * d2 * l2 + 2.0 * l1 * l3) - : std::fabs(((2.0 * d2 * l2 + 2.0 * l1 * l3) - c) / c); - sum += (d == 0) ? std::fabs(d2 * l2 * l2 + l3 * l3) - : std::fabs(((d2 * l2 * l2 + l3 * l3) - d) / d); + double sum = (b == 0) ? std::abs(d2 + l1 * l1 + 2.0 * l3) + : std::abs(((d2 + l1 * l1 + 2.0 * l3) - b) / b); + sum += (c == 0) ? std::abs(2.0 * d2 * l2 + 2.0 * l1 * l3) + : std::abs(((2.0 * d2 * l2 + 2.0 * l1 * l3) - c) / c); + sum += (d == 0) ? std::abs(d2 * l2 * l2 + l3 * l3) + : std::abs(((d2 * l2 * l2 + l3 * l3) - d) / d); return sum; } @@ -200,12 +200,12 @@ double calc_err_abcd(double a, double b, double c, double d, double aq, { /* Eqs. (68) and (69) in the manuscript for real alpha1 (aq), beta1 (bq), * alpha2 (cq) and beta2 (dq)*/ - double sum = (d == 0) ? std::fabs(bq * dq) : std::fabs((bq * dq - d) / d); - sum += (c == 0) ? std::fabs(bq * cq + aq * dq) - : std::fabs(((bq * cq + aq * dq) - c) / c); - sum += (b == 0) ? std::fabs(bq + aq * cq + dq) - : std::fabs(((bq + aq * cq + dq) - b) / b); - sum += (a == 0) ? std::fabs(aq + cq) : std::fabs(((aq + cq) - a) / a); + double sum = (d == 0) ? std::abs(bq * dq) : std::abs((bq * dq - d) / d); + sum += (c == 0) ? std::abs(bq * cq + aq * dq) + : std::abs(((bq * cq + aq * dq) - c) / c); + sum += (b == 0) ? std::abs(bq + aq * cq + dq) + : std::abs(((bq + aq * cq + dq) - b) / b); + sum += (a == 0) ? std::abs(aq + cq) : std::abs(((aq + cq) - a) / a); return sum; } @@ -213,11 +213,11 @@ double calc_err_abc( double a, double b, double c, double aq, double bq, double cq, double dq) { /* Eqs. (48)-(51) in the manuscript */ - double sum = (c == 0) ? std::fabs(bq * cq + aq * dq) - : std::fabs(((bq * cq + aq * dq) - c) / c); - sum += (b == 0) ? std::fabs(bq + aq * cq + dq) - : std::fabs(((bq + aq * cq + dq) - b) / b); - sum += (a == 0) ? std::fabs(aq + cq) : std::fabs(((aq + cq) - a) / a); + double sum = (c == 0) ? std::abs(bq * cq + aq * dq) + : std::abs(((bq * cq + aq * dq) - c) / c); + sum += (b == 0) ? std::abs(bq + aq * cq + dq) + : std::abs(((bq + aq * cq + dq) - b) / b); + sum += (a == 0) ? std::abs(aq + cq) : std::abs(((aq + cq) - a) / a); return sum; } void NRabcd(double a, double b, double c, double d, double* AQ, double* BQ, @@ -240,7 +240,7 @@ void NRabcd(double a, double b, double c, double d, double* AQ, double* BQ, fvec[3] = x[0] + x[2] - a; double errf = 0; for (int k1 = 0; k1 < 4; k1++) { - errf += (vr[k1] == 0) ? std::fabs(fvec[k1]) : std::fabs(fvec[k1] / vr[k1]); + errf += (vr[k1] == 0) ? std::abs(fvec[k1]) : std::abs(fvec[k1] / vr[k1]); } for (int iter = 0; iter < 8; iter++) { double x02 = x[0] - x[2]; @@ -282,8 +282,7 @@ void NRabcd(double a, double b, double c, double d, double* AQ, double* BQ, double errfold = errf; errf = 0; for (int k1 = 0; k1 < 4; k1++) { - errf += - (vr[k1] == 0) ? std::fabs(fvec[k1]) : std::fabs(fvec[k1] / vr[k1]); + errf += (vr[k1] == 0) ? std::abs(fvec[k1]) : std::abs(fvec[k1] / vr[k1]); } if (errf == 0) break; @@ -408,11 +407,11 @@ void quartic_solver(double coeff[5], std::complex roots[4]) cq = l1 - gamma; dq = l3 - gamma * l2; - if (std::fabs(dq) < std::fabs(bq)) + if (std::abs(dq) < std::abs(bq)) dq = d / bq; - else if (std::fabs(dq) > std::fabs(bq)) + else if (std::abs(dq) > std::abs(bq)) bq = d / dq; - if (std::fabs(aq) < std::fabs(cq)) { + if (std::abs(aq) < std::abs(cq)) { nsol = 0; if (dq != 0) { aqv[nsol] = (c - bq * cq) / dq; /* see eqs. (47) */ @@ -479,8 +478,8 @@ void quartic_solver(double coeff[5], std::complex roots[4]) /* Case III: d2 is 0 or approximately 0 (in this case check which solution is * better) */ if (realcase[0] == -1 || - (std::fabs(d2) <= MACHEPS * oqs::max3(std::fabs(2. * b / 3.), - std::fabs(phi0), l1 * l1))) { + (std::abs(d2) <= + MACHEPS * oqs::max3(std::abs(2. * b / 3.), std::abs(phi0), l1 * l1))) { double d3 = d - l3 * l3; double err0 = 0.0; if (realcase[0] == 1) @@ -496,9 +495,9 @@ void quartic_solver(double coeff[5], std::complex roots[4]) bq1 = l3 + std::sqrt(-d3); cq1 = l1; dq1 = l3 - std::sqrt(-d3); - if (std::fabs(dq1) < std::fabs(bq1)) + if (std::abs(dq1) < std::abs(bq1)) dq1 = d / bq1; - else if (std::fabs(dq1) > std::fabs(bq1)) + else if (std::abs(dq1) > std::abs(bq1)) bq1 = d / dq1; err1 = oqs::calc_err_abcd(a, b, c, d, aq1, bq1, cq1, dq1); /* eq. (68) */ } else { From 9a0f56a891ec127ffd1d0a407170be7e0f3794d6 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 29 Dec 2021 09:55:54 -0500 Subject: [PATCH 25/32] Bug fix in quartic solver when d2 is close to zero This bug was resulted in lost particles for torus models with R ~ r. Bug was reported to author of quartic solver and a fix was made in the quarticpp project: https://github.com/cridemichel/quarticpp/issues/1 --- src/external/quartic_solver.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/external/quartic_solver.cpp b/src/external/quartic_solver.cpp index cfefa02ea3..17410654fd 100644 --- a/src/external/quartic_solver.cpp +++ b/src/external/quartic_solver.cpp @@ -11,11 +11,6 @@ constexpr double CUBIC_RESCAL_FACT = 3.488062113727083e+102; constexpr double QUART_RESCAL_FACT = 7.156344627944542e+76; constexpr double MACHEPS = std::numeric_limits::epsilon(); -double max3(double a, double b, double c) -{ - return std::max(std::max(a, b), c); -} - double solve_cubic_analytic_depressed_handle_inf(double b, double c) { /* find analytically the dominant root of a depressed cubic x^3+b*x+c @@ -479,7 +474,7 @@ void quartic_solver(double coeff[5], std::complex roots[4]) * better) */ if (realcase[0] == -1 || (std::abs(d2) <= - MACHEPS * oqs::max3(std::abs(2. * b / 3.), std::abs(phi0), l1 * l1))) { + MACHEPS * (std::abs(2. * b / 3.) + std::abs(phi0) + l1 * l1))) { double d3 = d - l3 * l3; double err0 = 0.0; if (realcase[0] == 1) From 374e9f7107e8073a628faeed1c3f65bc5c39daa4 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 29 Dec 2021 09:57:50 -0500 Subject: [PATCH 26/32] Add case to torus unit test for R slightly bigger than r --- tests/unit_tests/test_torus.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/tests/unit_tests/test_torus.py b/tests/unit_tests/test_torus.py index 925949afa7..a5f49faae2 100644 --- a/tests/unit_tests/test_torus.py +++ b/tests/unit_tests/test_torus.py @@ -1,17 +1,18 @@ from itertools import combinations from random import uniform import openmc +import pytest -def get_torus_keff(cls, center=(0, 0, 0)): +def get_torus_keff(cls, R, r, center=(0, 0, 0)): model = openmc.Model() mat = openmc.Material() mat.add_nuclide('U235', 1.0) mat.set_density('g/cm3', 10.0) x, y, z = center - torus = cls(x0=x, y0=y, z0=z, a=3, b=2, c=2) - sphere = openmc.Sphere(x0=x, y0=y, z0=z, r=5.5, boundary_type="vacuum") + torus = cls(x0=x, y0=y, z0=z, a=R, b=r, c=r) + sphere = openmc.Sphere(x0=x, y0=y, z0=z, r=R + r + 1, boundary_type="vacuum") torus_cell = openmc.Cell(fill=mat, region=-torus) outer_cell = openmc.Cell(region=+torus & -sphere) model.geometry = openmc.Geometry([torus_cell, outer_cell]) @@ -26,15 +27,16 @@ def get_torus_keff(cls, center=(0, 0, 0)): return sp.k_combined -def test_torus_keff(run_in_tmpdir): +@pytest.mark.parametrize("R,r", [(2.1, 2.0), (3.0, 1.0)]) +def test_torus_keff(R, r, run_in_tmpdir): random_point = lambda: (uniform(-5, 5), uniform(-5, 5), uniform(-5, 5)) keffs = [ - get_torus_keff(openmc.XTorus), - get_torus_keff(openmc.XTorus, random_point()), - get_torus_keff(openmc.YTorus), - get_torus_keff(openmc.YTorus, random_point()), - get_torus_keff(openmc.ZTorus), - get_torus_keff(openmc.ZTorus, random_point()) + get_torus_keff(openmc.XTorus, R, r), + get_torus_keff(openmc.XTorus, R, r, random_point()), + get_torus_keff(openmc.YTorus, R, r), + get_torus_keff(openmc.YTorus, R, r, random_point()), + get_torus_keff(openmc.ZTorus, R, r), + get_torus_keff(openmc.ZTorus, R, r, random_point()) ] # For each combination of keff values, their difference should be within From b517d2967dbc1303f6db4f191fadf1819fad5697 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 29 Dec 2021 10:18:02 -0500 Subject: [PATCH 27/32] Respond to @pshriwise comments on #1933 --- docs/source/methods/geometry.rst | 2 +- docs/source/usersguide/geometry.rst | 15 ++++++++++++++- include/openmc/surface.h | 6 +++--- src/surface.cpp | 17 ++++++++++------- 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/docs/source/methods/geometry.rst b/docs/source/methods/geometry.rst index c087290225..2da0c02112 100644 --- a/docs/source/methods/geometry.rst +++ b/docs/source/methods/geometry.rst @@ -980,7 +980,7 @@ Dxy + Eyz + Fxz + Gx + Hy + Jz + K = 0`. Thus, the gradient to the surface is Torus Parallel to an Axis ------------------------- -A cone parallel to, for example, the x-axis has the form +A torus parallel to, for example, the x-axis has the form .. math:: :label: reflection-torus-1 diff --git a/docs/source/usersguide/geometry.rst b/docs/source/usersguide/geometry.rst index 2b6a6fef2f..68ecfce95c 100644 --- a/docs/source/usersguide/geometry.rst +++ b/docs/source/usersguide/geometry.rst @@ -78,10 +78,23 @@ classes are listed in the following table. | Cone parallel to the | :math:`(x-x_0)^2 + (y-y_0)^2 | :class:`openmc.ZCone` | | :math:`z`-axis | - R^2(z-z_0)^2 = 0` | | +----------------------+------------------------------+---------------------------+ - | General quadric | :math:`Ax^2 + By^2 + Cz^2 + | :class:`openmc.Quadric` | + | General quadric | :math:`Ax^2 + By^2 + Cz^2 + | :class:`openmc.Quadric` | | surface | Dxy + Eyz + Fxz + Gx + Hy + | | | | Jz + K = 0` | | +----------------------+------------------------------+---------------------------+ + | Torus parallel to the| :math:`(x-x_0)^2/B^2+\frac{( | :class:`openmc.XTorus` | + | :math:`x`-axis | \sqrt{(y-y_0)^2+(z-z_0)^2} - | | + | | A)^2}{C^2} - 1 = 0` | | + +----------------------+------------------------------+---------------------------+ + | Torus parallel to the| :math:`(y-y_0)^2/B^2+\frac{( | :class:`openmc.YTorus` | + | :math:`y`-axis | \sqrt{(x-x_0)^2+(z-z_0)^2} - | | + | | A)^2}{C^2} - 1 = 0` | | + +----------------------+------------------------------+---------------------------+ + | Torus parallel to the| :math:`(z-z_0)^2/B^2+\frac{( | :class:`openmc.ZTorus` | + | :math:`z`-axis | \sqrt{(x-x_0)^2+(y-y_0)^2} - | | + | | A)^2}{C^2} - 1 = 0` | | + +----------------------+------------------------------+---------------------------+ + Each surface is characterized by several parameters. As one example, the parameters for a sphere are the :math:`x,y,z` coordinates of the center of the diff --git a/include/openmc/surface.h b/include/openmc/surface.h index a92550c605..84a2ed1134 100644 --- a/include/openmc/surface.h +++ b/include/openmc/surface.h @@ -374,7 +374,7 @@ public: //============================================================================== //! A toroidal surface described by the quartic torus lies in the x direction // -//! \f$(x-x_0)^2 + (\sqrt{(y-y_0)^2 + (z-z_0)^2} - A)^2/C^2 -1 \f$ +//! \f$(x-x_0)^2/B^2 + (\sqrt{(y-y_0)^2 + (z-z_0)^2} - A)^2/C^2 -1 \f$ //============================================================================== class SurfaceXTorus : public CSGSurface { @@ -391,7 +391,7 @@ public: //============================================================================== //! A toroidal surface described by the quartic torus lies in the y direction // -//! \f$(x-x_0)^2 + (\sqrt{(y-y_0)^2 + (z-z_0)^2} - A)^2/C^2 -1 \f$ +//! \f$(y-y_0)^2/B^2 + (\sqrt{(x-x_0)^2 + (z-z_0)^2} - A)^2/C^2 -1 \f$ //============================================================================== class SurfaceYTorus : public CSGSurface { @@ -408,7 +408,7 @@ public: //============================================================================== //! A toroidal surface described by the quartic torus lies in the z direction // -//! \f$(x-x_0)^2 + (\sqrt{(y-y_0)^2 + (z-z_0)^2} - A)^2/C^2 -1 \f$ +//! \f$(z-z_0)^2/B^2 + (\sqrt{(x-x_0)^2 + (y-y_0)^2} - A)^2/C^2 -1 \f$ //============================================================================== class SurfaceZTorus : public CSGSurface { diff --git a/src/surface.cpp b/src/surface.cpp index 10a0026108..872bd6cb07 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -47,7 +47,8 @@ void read_coeffs(pugi::xml_node surf_node, int surf_id, double& c1) // Parse the coefficients. int stat = sscanf(coeffs.c_str(), "%lf", &c1); if (stat != 1) { - fatal_error("Something went wrong reading surface coeffs"); + fatal_error(fmt::format( + "Something went wrong reading coeffs for surface {}", surf_id)); } } @@ -65,7 +66,8 @@ void read_coeffs( // Parse the coefficients. int stat = sscanf(coeffs.c_str(), "%lf %lf %lf", &c1, &c2, &c3); if (stat != 3) { - fatal_error("Something went wrong reading surface coeffs"); + fatal_error(fmt::format( + "Something went wrong reading coeffs for surface {}", surf_id)); } } @@ -83,7 +85,8 @@ void read_coeffs(pugi::xml_node surf_node, int surf_id, double& c1, double& c2, // Parse the coefficients. int stat = sscanf(coeffs.c_str(), "%lf %lf %lf %lf", &c1, &c2, &c3, &c4); if (stat != 4) { - fatal_error("Something went wrong reading surface coeffs"); + fatal_error(fmt::format( + "Something went wrong reading coeffs for surface {}", surf_id)); } } @@ -102,7 +105,8 @@ void read_coeffs(pugi::xml_node surf_node, int surf_id, double& c1, double& c2, int stat = sscanf( coeffs.c_str(), "%lf %lf %lf %lf %lf %lf", &c1, &c2, &c3, &c4, &c5, &c6); if (stat != 6) { - fatal_error("Something went wrong reading surface coeffs"); + fatal_error(fmt::format( + "Something went wrong reading coeffs for surface {}", surf_id)); } } @@ -122,7 +126,8 @@ void read_coeffs(pugi::xml_node surf_node, int surf_id, double& c1, double& c2, int stat = sscanf(coeffs.c_str(), "%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf", &c1, &c2, &c3, &c4, &c5, &c6, &c7, &c8, &c9, &c10); if (stat != 10) { - fatal_error("Something went wrong reading surface coeffs"); + fatal_error(fmt::format( + "Something went wrong reading coeffs for surface {}", surf_id)); } } @@ -1065,8 +1070,6 @@ void SurfaceXTorus::to_hdf5_inner(hid_t group_id) const write_dataset(group_id, "coefficients", coeffs); } -// todo should do some alebgra first to get rid of the sqrt - double SurfaceXTorus::evaluate(Position r) const { double x = r.x - x0_; From fbd31e954d22bc7aaa0aee2ccc16e1f75ec9aa5b Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 29 Dec 2021 10:19:11 -0500 Subject: [PATCH 28/32] Update some links in documentation, fix license badge on README --- CONTRIBUTING.md | 9 ++++----- README.md | 12 ++++++------ 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index be506f919a..378ca346a8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,9 +13,8 @@ openmc@anl.gov. ## Resources - [GitHub Repository](https://github.com/openmc-dev/openmc) -- [Documentation](http://openmc.readthedocs.io/en/latest) -- [User's Mailing List](openmc-users@googlegroups.com) -- [Developer's Mailing List](openmc-dev@googlegroups.com) +- [Documentation](http://docs.openmc.org/en/latest) +- [Discussion Forum](https://openmc.discourse.group) - [Slack Community](https://openmc.slack.com/signup) (If you don't see your domain listed, contact openmc@anl.gov) @@ -37,10 +36,10 @@ development team will be happy to discuss it. All changes to OpenMC happen through pull requests. For a full overview of the process, see the developer's guide section on [Contributing to -OpenMC](http://openmc.readthedocs.io/en/latest/devguide/contributing.html). +OpenMC](https://docs.openmc.org/en/latest/devguide/contributing.html). ## Code Style Before you run off to make changes to the code, please have a look at our [style -guide](http://openmc.readthedocs.io/en/latest/devguide/styleguide.html), which +guide](https://docs.openmc.org/en/latest/devguide/styleguide.html), which is used when reviewing new contributions. diff --git a/README.md b/README.md index ebd7b050ae..186c1e5e03 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # OpenMC Monte Carlo Particle Transport Code -[![License](https://img.shields.io/github/license/openmc-dev/openmc.svg)](http://openmc.readthedocs.io/en/latest/license.html) +[![License](https://img.shields.io/badge/license-MIT-green)](https://docs.openmc.org/en/latest/license.html) [![GitHub Actions build status (Linux)](https://github.com/openmc-dev/openmc/workflows/CI/badge.svg?branch=develop)](https://github.com/openmc-dev/openmc/actions?query=workflow%3ACI) [![Code Coverage](https://coveralls.io/repos/github/openmc-dev/openmc/badge.svg?branch=develop)](https://coveralls.io/github/openmc-dev/openmc?branch=develop) [![dockerhub-publish-develop-dagmc](https://github.com/openmc-dev/openmc/workflows/dockerhub-publish-develop-dagmc/badge.svg)](https://github.com/openmc-dev/openmc/actions?query=workflow%3Adockerhub-publish-develop-dagmc) @@ -12,14 +12,14 @@ continuous-energy transport code that uses HDF5 format cross sections. The project started under the Computational Reactor Physics Group at MIT. Complete documentation on the usage of OpenMC is hosted on Read the Docs (both -for the [latest release](http://openmc.readthedocs.io/en/stable/) and -[developmental](http://openmc.readthedocs.io/en/latest/) version). If you are +for the [latest release](https://docs.openmc.org/en/stable/) and +[developmental](https://docs.openmc.org/en/latest/) version). If you are interested in the project, or would like to help and contribute, please get in touch on the OpenMC [discussion forum](https://openmc.discourse.group/). ## Installation Detailed [installation -instructions](http://openmc.readthedocs.io/en/stable/usersguide/install.html) +instructions](https://docs.openmc.org/en/stable/usersguide/install.html) can be found in the User's Guide. ## Citing @@ -35,7 +35,7 @@ citing the following publication: ## Troubleshooting If you run into problems compiling, installing, or running OpenMC, first check -the [Troubleshooting section](http://openmc.readthedocs.io/en/stable/usersguide/troubleshoot.html) in +the [Troubleshooting section](https://docs.openmc.org/en/stable/usersguide/troubleshoot.html) in the User's Guide. If you are not able to find a solution to your problem there, please post to the [discussion forum](https://openmc.discourse.group/). @@ -53,4 +53,4 @@ create an Issue on github. ## License OpenMC is distributed under the MIT/X -[license](http://openmc.readthedocs.io/en/stable/license.html). +[license](https://docs.openmc.org/en/stable/license.html). From ebb6736ba65f16afc0063427b0e4e8644db33180 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 29 Dec 2021 10:37:54 -0500 Subject: [PATCH 29/32] Add TorusMixin.rotate method that can handle simple rotations --- openmc/surface.py | 43 +++++++++++++++++++++++++++++++- tests/unit_tests/test_surface.py | 28 +++++++++++++++++++-- 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index e00dc8962e..2f9618224d 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -2142,7 +2142,48 @@ class TorusMixin: return surf def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False): - raise NotImplementedError('Torus surfaces cannot be rotated.') + pivot = np.asarray(pivot) + rotation = np.asarray(rotation, dtype=float) + + # Allow rotation matrix to be passed in directly, otherwise build it + if rotation.ndim == 2: + check_length('surface rotation', rotation.ravel(), 9) + Rmat = rotation + else: + Rmat = get_rotation_matrix(rotation, order=order) + + # Only can handle trivial rotation matrices + close = np.isclose + if not np.all(close(Rmat, -1.0) | close(Rmat, 0.0) | close(Rmat, 1.0)): + raise NotImplementedError('Torus surfaces cannot handle generic rotations') + + # Translate surface to pivot + surf = self.translate(-pivot, inplace=inplace) + + # Determine "center" of torus and a point above it (along main axis) + center = [surf.x0, surf.y0, surf.z0] + above_center = center.copy() + index = ['x-torus', 'y-torus', 'z-torus'].index(surf._type) + above_center[index] += 1 + + # Compute new rotated torus center + center = Rmat @ center + + # Figure out which axis should be used after rotation + above_center = Rmat @ above_center + new_index = np.where(np.isclose(np.abs(above_center - center), 1.0))[0][0] + cls = [XTorus, YTorus, ZTorus][new_index] + + # Create rotated torus + kwargs = { + 'boundary_type': surf.boundary_type, 'name': surf.name, + 'a': surf.a, 'b': surf.b, 'c': surf.c + } + if inplace: + kwargs['surface_id'] = surf.id + surf = cls(x0=center[0], y0=center[1], z0=center[2], **kwargs) + + return surf.translate(pivot, inplace=inplace) def _get_base_coeffs(self): raise NotImplementedError diff --git a/tests/unit_tests/test_surface.py b/tests/unit_tests/test_surface.py index c24d6e3521..12cd8c9d20 100644 --- a/tests/unit_tests/test_surface.py +++ b/tests/unit_tests/test_surface.py @@ -630,9 +630,15 @@ def torus_common(center, R, r1, r2, cls): assert st.b == s.b assert st.c == s.c - # can't rotate at present + # trivial rotations + for rotation in [(0., 0., 0.), (180., 0., 0.), (0., 180., 0.), (0., 0., 180.)]: + sr = s.rotate(rotation) + assert type(sr) == type(s) + assert (sr.a, sr.b, sr.c) == (s.a, s.b, s.c) + + # can't do generic rotate at present with pytest.raises(NotImplementedError): - s.rotate((0., 1., 0.)) + s.rotate((0., 45., 0.)) # Check bounding box ll, ur = (+s).bounding_box @@ -676,6 +682,12 @@ def test_xtorus(): assert s.evaluate((x + r1 + 0.01, y, z + R)) > 0.0 assert s.evaluate((x + r1 + 0.01, y + R, z)) > 0.0 + # rotation + sr = s.rotate((0., 0., 90.)) + assert isinstance(sr, openmc.YTorus) + sr = s.rotate((0., 90., 0.)) + assert isinstance(sr, openmc.ZTorus) + def test_ytorus(): x, y, z = 2, -4, 5 @@ -703,6 +715,12 @@ def test_ytorus(): assert s.evaluate((x, y + r1 + 0.01, z + R)) > 0.0 assert s.evaluate((x + R, y + r1 + 0.01, z)) > 0.0 + # rotation + sr = s.rotate((90., 0., 0.)) + assert isinstance(sr, openmc.ZTorus) + sr = s.rotate((0., 0., 90.)) + assert isinstance(sr, openmc.XTorus) + def test_ztorus(): x, y, z = 2, -4, 5 @@ -729,3 +747,9 @@ def test_ztorus(): assert s.evaluate((x + R + r2 + 0.01, y, z)) > 0.0 assert s.evaluate((x + R, y, z + r1 + 0.01)) > 0.0 assert s.evaluate((x, y + R, z + r1 + 0.01)) > 0.0 + + # rotation + sr = s.rotate((90., 0., 0.)) + assert isinstance(sr, openmc.YTorus) + sr = s.rotate((0., 90., 0.)) + assert isinstance(sr, openmc.XTorus) From d834f83d083ab25672ba209879de860f7f5693c0 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 3 Jan 2022 09:36:59 -0500 Subject: [PATCH 30/32] Reduce torus coincident zero root cutoff to 1e-10 --- src/surface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/surface.cpp b/src/surface.cpp index 872bd6cb07..dbfa0bf3dc 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -1042,7 +1042,7 @@ double torus_distance(double x1, double x2, double x3, double u1, double u2, // zero but possibly small and positive. A tolerance is set to discard that // zero. double distance = INFTY; - double cutoff = coincident ? 1e-9 : 0.0; + double cutoff = coincident ? 1e-10 : 0.0; for (int i = 0; i < 4; ++i) { if (roots[i].imag() == 0) { double root = roots[i].real(); From 67db1f5af0c32d83a0f40595132b6d4a3dac903c Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 3 Jan 2022 09:40:33 -0500 Subject: [PATCH 31/32] Force CI tests to use numpy<1.22 --- pyproject.toml | 2 +- tools/ci/gha-install.sh | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d5970617a7..ac67355937 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,2 +1,2 @@ [build-system] -requires = ["setuptools", "wheel", "numpy", "cython"] +requires = ["setuptools", "wheel", "numpy<1.22", "cython"] diff --git a/tools/ci/gha-install.sh b/tools/ci/gha-install.sh index 8c4762e3e4..aa40eb90b1 100755 --- a/tools/ci/gha-install.sh +++ b/tools/ci/gha-install.sh @@ -1,10 +1,13 @@ #!/bin/bash set -ex -# Upgrade pip, pytest, numpy before doing anything else +# Upgrade pip, pytest, numpy before doing anything else. +# TODO: numpy 1.22 results in several failing tests, so we force a lower version +# for now (similar change made in pyproject.toml). When this is removed, those +# tests will need to be updated. pip install --upgrade pip pip install --upgrade pytest -pip install --upgrade numpy +pip install --upgrade "numpy<1.22" # Install NJOY 2016 ./tools/ci/gha-install-njoy.sh From 20651b6c6a8a37f3c284e33b4193fc4e399d2f74 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 3 Jan 2022 13:22:51 -0500 Subject: [PATCH 32/32] Make sure Torus Python classes use raw strings for docstrings --- openmc/surface.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openmc/surface.py b/openmc/surface.py index 2f9618224d..4a174a23b0 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -2190,7 +2190,7 @@ class TorusMixin: class XTorus(TorusMixin, Surface): - """A torus of the form :math:`(x - x_0)^2/B^2 + (\sqrt{(y - y_0)^2 + (z - + r"""A torus of the form :math:`(x - x_0)^2/B^2 + (\sqrt{(y - y_0)^2 + (z - z_0)^2} - A)^2/C^2 - 1 = 0`. Parameters @@ -2259,7 +2259,7 @@ class XTorus(TorusMixin, Surface): np.array([np.inf, np.inf, np.inf])) class YTorus(TorusMixin, Surface): - """A torus of the form :math:`(y - y_0)^2/B^2 + (\sqrt{(x - x_0)^2 + (z - + r"""A torus of the form :math:`(y - y_0)^2/B^2 + (\sqrt{(x - x_0)^2 + (z - z_0)^2} - A)^2/C^2 - 1 = 0`. Parameters @@ -2328,7 +2328,7 @@ class YTorus(TorusMixin, Surface): np.array([np.inf, np.inf, np.inf])) class ZTorus(TorusMixin, Surface): - """A torus of the form :math:`(z - z_0)^2/B^2 + (\sqrt{(x - x_0)^2 + (y - + r"""A torus of the form :math:`(z - z_0)^2/B^2 + (\sqrt{(x - x_0)^2 + (y - y_0)^2} - A)^2/C^2 - 1 = 0`. Parameters