|
| 1 | +module quadric_class |
| 2 | + |
| 3 | + use numPrecision |
| 4 | + use universalVariables, only : SURF_TOL, INF, X_AXIS, Y_AXIS, Z_AXIS |
| 5 | + use genericProcedures, only : fatalError, numToChar |
| 6 | + use dictionary_class, only : dictionary |
| 7 | + use surface_inter, only : surface, kill_super => kill |
| 8 | + |
| 9 | + implicit none |
| 10 | + private |
| 11 | + |
| 12 | + !! |
| 13 | + !! General quadratic surface - also known as a quadric |
| 14 | + !! |
| 15 | + !! F(x,y,z) = Ax^2 + By^2 + Cz^2 + Dxy + Eyz + Fzx + Gx + Hy + Iz + J |
| 16 | + !! |
| 17 | + !! Surface tolerance: 2 * max(coeffs) * SURF_TOL |
| 18 | + !! |
| 19 | + !! Sample dictionary input: |
| 20 | + !! quad { type quadric; |
| 21 | + !! id 3; |
| 22 | + !! coeffs (1 -2.3 0.4 20 7.7777 0.004 6E9 -4.20 0 11); |
| 23 | + !! // correspond to(A B C D E F G H I J) |
| 24 | + !! } |
| 25 | + !! |
| 26 | + !! Private Members: |
| 27 | + !! coeffs -> Quadric coefficients |
| 28 | + !! |
| 29 | + !! Interface: |
| 30 | + !! surface interface |
| 31 | + !! |
| 32 | + type, public, extends(surface) :: quadric |
| 33 | + private |
| 34 | + real(defReal), dimension(10) :: coeffs = ZERO |
| 35 | + contains |
| 36 | + ! Superclass procedures |
| 37 | + procedure :: myType |
| 38 | + procedure :: init |
| 39 | + procedure :: boundingBox |
| 40 | + procedure :: evaluate |
| 41 | + procedure :: distance |
| 42 | + procedure :: going |
| 43 | + procedure :: kill |
| 44 | + |
| 45 | + end type quadric |
| 46 | + |
| 47 | +contains |
| 48 | + |
| 49 | + !! |
| 50 | + !! Return surface type name |
| 51 | + !! |
| 52 | + !! See surface_inter for more details |
| 53 | + !! |
| 54 | + pure function myType(self) result(str) |
| 55 | + class(quadric), intent(in) :: self |
| 56 | + character(:), allocatable :: str |
| 57 | + |
| 58 | + str = 'quadric' |
| 59 | + |
| 60 | + end function myType |
| 61 | + |
| 62 | + !! |
| 63 | + !! Initialise quadric from a dictionary |
| 64 | + !! |
| 65 | + !! See surface_inter for more details |
| 66 | + !! |
| 67 | + !! Errors: |
| 68 | + !! fatalError if id < 0 or incorrect size of coeffs |
| 69 | + !! |
| 70 | + subroutine init(self, dict) |
| 71 | + class(quadric), intent(inout) :: self |
| 72 | + class(dictionary), intent(in) :: dict |
| 73 | + integer(shortInt) :: id |
| 74 | + real(defReal), dimension(:), allocatable :: coeffs |
| 75 | + character(100), parameter :: Here = 'init (quadric_class.f90)' |
| 76 | + |
| 77 | + ! Get from dictionary |
| 78 | + call dict % get(id, 'id') |
| 79 | + call dict % get(coeffs, 'coeffs') |
| 80 | + |
| 81 | + ! Check ID validity |
| 82 | + if (id < 1) call fatalError(Here,'Invalid surface id provided. ID must be > 1') |
| 83 | + |
| 84 | + ! Check origin size |
| 85 | + if (size(coeffs) /= 10) then |
| 86 | + call fatalError(Here,'coeffs needs to have size 10. Has: '//numToChar(size(coeffs))) |
| 87 | + end if |
| 88 | + |
| 89 | + call self % setID(id) |
| 90 | + self % coeffs = coeffs |
| 91 | + |
| 92 | + ! Set surface tolerance - what should this be? |
| 93 | + ! Could choose alternatively to be equivalent to the |
| 94 | + ! sphere case, i.e., 2 * coeffs(10) = 2*R |
| 95 | + call self % setTol( TWO * maxval(abs(coeffs)) * SURF_TOL) |
| 96 | + |
| 97 | + end subroutine init |
| 98 | + |
| 99 | + !! |
| 100 | + !! Return axis-aligned bounding box for the surface |
| 101 | + !! |
| 102 | + !! Not attempted - may be unbounded, depending on the coefficients. |
| 103 | + !! Would generally require checking the Jacobian of the surface and |
| 104 | + !! finding critical points. |
| 105 | + !! I think this only matters if we wanted to use this as a boundary. |
| 106 | + !! |
| 107 | + !! TODO: This |
| 108 | + !! See surface_inter for details |
| 109 | + !! |
| 110 | + pure function boundingBox(self) result(aabb) |
| 111 | + class(quadric), intent(in) :: self |
| 112 | + real(defReal), dimension(6) :: aabb |
| 113 | + |
| 114 | + aabb(1:3) = -INF |
| 115 | + aabb(4:6) = INF |
| 116 | + |
| 117 | + end function boundingBox |
| 118 | + |
| 119 | + !! |
| 120 | + !! Evaluate surface expression c = F(r) |
| 121 | + !! |
| 122 | + !! See surface_inter for details |
| 123 | + !! |
| 124 | + pure function evaluate(self, r) result(c) |
| 125 | + class(quadric), intent(in) :: self |
| 126 | + real(defReal), dimension(3), intent(in) :: r |
| 127 | + real(defReal) :: c |
| 128 | + |
| 129 | + associate(a => self % coeffs, x => r(1), y => r(2), z => r(3)) |
| 130 | + c = x * (a(1)*x + a(4)*y + a(6)*z + a(7)) + & |
| 131 | + y * (a(2)*y + a(5)*z + a(8)) + & |
| 132 | + z * (a(3)*z + a(9)) + a(10) |
| 133 | + end associate |
| 134 | + |
| 135 | + end function evaluate |
| 136 | + |
| 137 | + !! |
| 138 | + !! Return distance to the surface |
| 139 | + !! |
| 140 | + !! See surface_inter for details |
| 141 | + !! |
| 142 | + !! Converts the general quadric to a 1D quadratic to solve |
| 143 | + !! ad^2 + 2kd + c = 0 |
| 144 | + !! c = F(r) |
| 145 | + !! k = A*u1*r1 + B*u2*r2 + C*u3*r3 + |
| 146 | + !! (D(u1*r2 + u2*r1) + E(u2*r3 + u3*r2) + F(u3*r1 + u1*r3) |
| 147 | + !! + G*u1 + H*u2 + I*u3) / 2 |
| 148 | + !! a = A*u1*u1 + B*u2*u2 + C*u3*u3 + D*u1*u2 + E*u2*u3 + F*u1*u3 |
| 149 | + !! |
| 150 | + pure function distance(self, r, u) result(d) |
| 151 | + class(quadric), intent(in) :: self |
| 152 | + real(defReal), dimension(3), intent(in) :: r |
| 153 | + real(defReal), dimension(3), intent(in) :: u |
| 154 | + real(defReal) :: d |
| 155 | + real(defReal) :: a, c, k, delta |
| 156 | + |
| 157 | + c = self % evaluate(r) |
| 158 | + associate(cf => self % coeffs, x => r(1), y => r(2), z => r(3), & |
| 159 | + u1 => u(1), u2 => u(2), u3 => u(3)) |
| 160 | + k = cf(1)*u1*x + cf(2)*u2*y + cf(3)*u3*z + & |
| 161 | + HALF * (cf(4) * (u1*y + u2*x) + & |
| 162 | + cf(5) * (u2*z + u3*y) + cf(6) * (u3*x + u1*z) + & |
| 163 | + cf(7) * u1 + cf(8)*u2 + cf(9)*u3) |
| 164 | + a = u1 * (cf(1) * u1 + cf(4) * u2 + cf(6) * u3) + & |
| 165 | + u2 * (cf(2) * u2 + cf(5) * u3) + & |
| 166 | + cf(3) * u3 * u3 |
| 167 | + end associate |
| 168 | + delta = k*k - a*c ! Technically delta/4 - quadratic discriminant |
| 169 | + |
| 170 | + ! Calculate the distance |
| 171 | + if (delta < ZERO .or. a == ZERO) then ! No intersection |
| 172 | + d = INF |
| 173 | + |
| 174 | + else if (abs(c) < self % surfTol()) then ! Point at a surface |
| 175 | + if ( k >= ZERO) then |
| 176 | + d = INF |
| 177 | + else |
| 178 | + d = -k + sqrt(delta) |
| 179 | + d = d/a |
| 180 | + end if |
| 181 | + |
| 182 | + else if (c < ZERO) then ! Point inside the surface |
| 183 | + d = -k + sqrt(delta) |
| 184 | + d = d/a |
| 185 | + |
| 186 | + else ! Point outside the surface |
| 187 | + d = -k - sqrt(delta) |
| 188 | + d = d/a |
| 189 | + if (d <= ZERO) d = INF |
| 190 | + |
| 191 | + end if |
| 192 | + |
| 193 | + ! Cap distance at Infinity |
| 194 | + d = min(d, INF) |
| 195 | + |
| 196 | + end function distance |
| 197 | + |
| 198 | + !! |
| 199 | + !! Returns TRUE if particle is going into +ve halfspace |
| 200 | + !! |
| 201 | + !! See surface_inter for details |
| 202 | + !! |
| 203 | + pure function going(self, r, u) result(halfspace) |
| 204 | + class(quadric), intent(in) :: self |
| 205 | + real(defReal), dimension(3), intent(in) :: r |
| 206 | + real(defReal), dimension(3), intent(in) :: u |
| 207 | + logical(defBool) :: halfspace |
| 208 | + real(defReal), dimension(3) :: grad |
| 209 | + |
| 210 | + associate(a => self % coeffs) |
| 211 | + grad(1) = TWO * a(1) * r(1) + a(4) * r(2) + a(5) * r(3) |
| 212 | + grad(2) = TWO * a(2) * r(2) + a(4) * r(1) + a(6) * r(3) |
| 213 | + grad(3) = TWO * a(3) * r(3) + a(5) * r(1) + a(6) * r(2) |
| 214 | + end associate |
| 215 | + halfspace = dot_product(grad, u) >= ZERO |
| 216 | + |
| 217 | + end function going |
| 218 | + |
| 219 | + !! |
| 220 | + !! Return to uninitialised state |
| 221 | + !! |
| 222 | + elemental subroutine kill(self) |
| 223 | + class(quadric), intent(inout) :: self |
| 224 | + |
| 225 | + ! Superclass |
| 226 | + call kill_super(self) |
| 227 | + |
| 228 | + ! Local |
| 229 | + self % coeffs = ZERO |
| 230 | + |
| 231 | + end subroutine kill |
| 232 | + |
| 233 | +end module quadric_class |
0 commit comments