aphirst

SOS2DFTN

Jun 5th, 2014
10,510
0
Never
2
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Fortran 10.46 KB | None | 0 0
  1. ! This file is part of SOS2DFTN.
  2. ! Copyright (C) 2014 Adam Hirst <[email protected]>
  3. !
  4. ! SOS2DFTN is free software: you can redistribute it and/or modify
  5. ! it under the terms of the GNU General Public License as published by
  6. ! the Free Software Foundation, either version 3 of the License, or
  7. ! (at your option) any later version.
  8. !
  9. ! SOS2DFTN is distributed in the hope that it will be useful,
  10. ! but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. ! GNU General Public License for more details.
  13. !
  14. ! You should have received a copy of the GNU General Public License
  15. ! along with SOS2DFTN. If not, see <http://www.gnu.org/licenses/>.
  16.  
  17. module Constants
  18.   implicit none
  19.  
  20.   real, parameter :: pi = 4 * atan(1.0)
  21.  
  22. end module Constants
  23.  
  24. module Types
  25.   use Constants
  26.   implicit none
  27.  
  28.   type Emitter
  29.     ! The extended emitter lies on the x-axis
  30.     ! edges of element are `-R` and `+R`, units in mm
  31.     ! `-R` also referred to as `R'` in literature
  32.     real :: R
  33.     ! prescribed response at the z-axis
  34.     real :: L_z
  35.     ! prescribed response is defined from the z-axis down to some minimum angle
  36.     real :: theta_min
  37.   contains
  38.     ! prescribed response in terms of the emitter's angular projected width
  39.     procedure :: L => Response
  40.   end type Emitter
  41.  
  42.   type SurfacePoint
  43.     ! points exist in x-z plane, units in mm
  44.     real :: P(2)
  45.     ! we also store the normal unit-vector
  46.     real :: N(2)
  47.   end type SurfacePoint
  48.  
  49.   type Surface
  50.     ! the surface points themselves
  51.     type(SurfacePoint), allocatable :: A(:)
  52.     ! total number of points comprising the surface
  53.     integer :: J
  54.   end type Surface
  55.  
  56.   type, extends(Surface) :: Lens
  57.     ! Refractive index of the lens.
  58.     real    :: n_1
  59.     ! Number of oval-segments for the initial lens portion, where # oval points = # oval segments + 1
  60.     integer :: N
  61.   contains
  62.     ! The surface is "seeded" with a Cartesian oval from the x-axis up to the emitter's minimum prescription angle.
  63.     procedure :: Seed => SeedLens
  64.     procedure :: Generate
  65.   end type Lens
  66.  
  67. contains
  68.  
  69.   pure function Reflect(i, n) result (rfx)
  70.     ! Returns the unit vector of a ray after reflection, given unit vectors of the `i`ncident ray and the surface `n`ormal.
  71.     ! "Introduction to Nonimaging Optics" (Chaves, 2008), Section 17.15
  72.     real, intent(in) :: i(2), n(2)
  73.     real             :: rfx(2)
  74.  
  75.     rfx = i - 2*dot_product(i,n)*n
  76.   end function Reflect
  77.  
  78.   pure function Refract(i, n_S, n_1) result (rfr)
  79.     ! Returns the unit-vector of a ray after refraction, given unit vectors of the `i`ncident ray and surface normal `n_S`, and the
  80.     ! refractive index of the incident medium `n_1`.
  81.     ! We assume that the second medium is air, i.e. that `n_2 = 1`.
  82.     ! "Introduction to Nonimaging Optics" (Chaves, 2008), Section 17.15
  83.     real, intent(in) :: i(2), n_S(2), n_1
  84.     real             :: rfr(2), n(2), delta
  85.  
  86.     if (dot_product(i,n_S) >= 0) then
  87.       n = n_S
  88.     else
  89.       n = -n_S
  90.     end if
  91.     delta = 1 - (n_1**2) * (1 - dot_product(i,n)**2)
  92.     if (delta > 0) then
  93.       rfr = i*n_1 + n*(sqrt(delta) - n_1*dot_product(i,n))
  94.     else
  95.       rfr = Reflect(i,n_S)
  96.     end if
  97.   end function Refract
  98.  
  99.   pure function IntersectLines(P, v, Q, u) result (isl)
  100.     ! Returns the point of intersection of two lines, each defined as a point (`P` and `Q`) and a direction (`v` and `u`).
  101.     ! "Introduction to Nonimaging Optics" (Chaves, 2008), Section 17.15
  102.     real, intent(in) :: P(2), v(2), Q(2), u(2)
  103.     real             :: isl(2), u_P(2)
  104.  
  105.     u_P = [-u(2), u(1)]
  106.     isl = P + dot_product((Q-P),u_P)/dot_product(v,u_P) * v
  107.   end function IntersectLines
  108.  
  109.   pure function RefractionNormal(i, r, n_1) result (rfrnrm)
  110.     ! Returns the surface normal unit-vector given that an `i`ncident ray is `r`efracted from a medium of index `n_1` into air.
  111.     ! "Introduction to Nonimaging Optics" (Chaves, 2008), Section 17.15
  112.     real, intent(in) :: i(2), r(2), n_1
  113.     real             :: rfrnrm(2)
  114.  
  115.     rfrnrm = n_1*i - r; rfrnrm = rfrnrm / norm2(rfrnrm)
  116.   end function RefractionNormal
  117.  
  118.   pure function CartesianOval(F, n_1, P, alpha, phi) result (cop)
  119.     ! Returns the surface point of the Cartesian oval which collimates rays from `F` in a medium of refractive index `n_1` into air
  120.     ! at an angle `alpha`, which intersects the point `P`, in terms of `phi` (the angle about `F` above `alpha`).
  121.     ! "Introduction to Nonimaging Optics" (Chaves, 2008), Section 17.15
  122.     real, intent(in) :: F, n_1, P, alpha, phi
  123.     ! Note that `F` and `P` are scalar, as are defined to lie on the x-axis.
  124.     ! We additionally assume that `F` lies on `-x` and that P lies on `+x`.
  125.     real             :: cop(2)
  126.  
  127.     cop = (P - F)*(n_1 - cos(alpha))*[cos(phi+alpha), sin(phi+alpha)] / (n_1 - cos(phi)) + [F,0.0]
  128.   end function CartesianOval
  129.  
  130.   pure function CartesianOvalNormal(F, n_1, P, alpha, phi) result (copn)
  131.     ! Returns the unit-vector of the normal to the surface of the above-described Cartesian oval at a given parameter `phi`.
  132.     real, intent(in) :: F, n_1, P, alpha, phi
  133.     real             :: copn(2)
  134.  
  135.     copn = (P - F)*(n_1 - cos(alpha))*[n_1*cos(phi+alpha) - cos(alpha), n_1*sin(phi+alpha) - sin(alpha)] / (n_1 - cos(phi))**2
  136.     copn = copn / norm2(copn)
  137.   end function CartesianOvalNormal
  138.  
  139.   function CartesianOvalProjection(F, n_1, P, alpha, L) result (phi)
  140.     ! Returns the parameter `phi` corresponding to the end of the Cartesian oval with projected width `L` in the direction `alpha`.
  141.     real, intent(in) :: F, n_1, P, alpha, L
  142.     real             :: phi, C, phis(2), phi_C
  143.  
  144.     C = (P - F)*(n_1 - cos(alpha)) / ((P - F)*sin(alpha) - L)
  145.     if (C**2 < n_1**2 - 1) then
  146.       stop 'Projection and oval do not intersect.'
  147.     end if
  148.     ! `[1,-1]` indicates `\pm`, as lines intersect closed curves twice.
  149.     phis(:) = 2 * atan( ([1, -1]*sqrt(C**2 - n_1**2 + 1.0) - C) / (n_1+1.0) )
  150.     ! The intersecting line necessarily has positive slope (`alpha` is always in the positive x-z quadrant), so take the
  151.     ! intersection closest to `phi = 0`.
  152.     phi = phis( minloc(abs(phis),1) )
  153.     ! `phi` must also correspond to a point on the oval within a valid range, namely that which permits refraction onto `F`.
  154.     phi_C = pi/2 - asin(min(1.0,n_1)/max(1.0,n_1))
  155.     if (abs(phi) > phi_C) then
  156.       stop 'Projection does not intersect with refractive portion of oval.'
  157.     else if (phi < -alpha) then
  158.       stop 'Projection does not intersect above the x-axis.'
  159.     end if
  160.   end function CartesianOvalProjection
  161.  
  162.   pure real function Response(this, theta) result (L)
  163.     ! Returns the prescribed angular response of the emitter at the given `theta`.
  164.     ! "Nonimaging Optics" (Winston et al., 2005), Section 7.4.2
  165.     class(Emitter), intent(in) :: this
  166.     real,           intent(in) :: theta
  167.  
  168.     if (theta >= this%theta_min) then
  169.       L = this%L_z * (1 + (2*theta - pi)/(2*this%theta_min - pi))
  170.     else
  171.       L = 0.0
  172.     end if
  173.   end function Response
  174.  
  175.   subroutine SeedLens(this, my_emitter, A_0)
  176.     class(Lens),   intent(in out) :: this
  177.     type(Emitter), intent(in)     :: my_emitter
  178.     real,          intent(in)     :: A_0
  179.     real                          :: phi_max
  180.     integer :: i
  181.  
  182.     associate (R => my_emitter%R, n_1 => this%n_1, alpha => my_emitter%theta_min, N => this%N)
  183.       phi_max = CartesianOvalProjection(-R, n_1, A_0, alpha, my_emitter%L(alpha))
  184.       allocate(this%A(0:N))
  185.       this%A(0)%P = [A_0, 0.0]
  186.       do concurrent (i = 1:N)
  187.         this%A(i)%P = CartesianOval(-R, n_1, A_0, alpha, i*(phi_max+alpha)/N - alpha)
  188.       end do
  189.       do concurrent (i = 0:N)
  190.         this%A(i)%N = CartesianOvalNormal(-R, n_1, A_0, alpha, i*(phi_max+alpha)/N - alpha)
  191.       end do
  192.       this%J = N
  193.     end associate
  194.   end subroutine SeedLens
  195.  
  196.   pure subroutine Generate(this, my_emitter)
  197.     class(Lens),         intent(in out)              :: this
  198.     type(Emitter),       intent(in)                  :: my_emitter
  199.     integer                                          :: i
  200.     real                                             :: i_i(2), r_i(2), theta_i
  201.     type(SurfacePoint)                               :: B
  202.     type(SurfacePoint),                  allocatable :: A_new(:)
  203.  
  204.     ! for `i = 1` upwards
  205.     i = 1
  206.     do
  207.       ! Refract from `R` through `A(i)` to get projection angle `theta_i` (and `r_i`, the unit-vector in that direction).
  208.       ! `i_i` and `r_i` are the `i`th `i`ncident and `r`efracted ray directions respectively
  209.       i_i = (this%A(i)%P - [my_emitter%R, 0.0]); i_i = i_i / norm2(i_i)
  210.       r_i = Refract(i_i, this%A(i)%N, this%n_1)
  211.       ! `r_i` is a unit vector, and unit vectors are equal to `[cos(theta), sin(theta)]`
  212.       theta_i = acos(r_i(1))
  213.       ! Intersect the line parallel to `r_i` perpendicularly separated from `A(i)` by `L(theta_i)` with the tangent to `A(N+i-1)` to
  214.       ! get the next point in the surface, `A(N+i)`.
  215.       associate (L => my_emitter%L(theta_i), P => this%A(i)%P, Q => this%A(this%N+i-1)%P, N => this%A(this%N+i-1)%N)
  216.         ! TODO: Use a different offset for `Q` to avoid the need to compute `sin(theta_i)`
  217.         B%P = IntersectLines(P - [L/sin(theta_i), 0.0], r_i, Q, [-N(2), N(1)])
  218.       end associate
  219.       ! Compute the normal to `A(N+i)`, from the fact that light from `R'` through `A(N+i)` must have resulting direction `theta_L`.
  220.       i_i = B%P + [my_emitter%R, 0.0]; i_i = i_i / norm2(i_i)
  221.       B%N = RefractionNormal(i_i, r_i, this%n_1)
  222.       ! if the new point has not crossed the z-axis, simply store it
  223.       if (B%P(1) >= 0.0) then
  224.         ! TODO: implement chunked reallocation of `type(SurfacePoint)` array
  225.         this%J = this%J + 1
  226.         allocate(A_new(0:this%J))
  227.         A_new(0:) = [this%A(0:), B]
  228.         call move_alloc(A_new, this%A)
  229.       else
  230.         ! TODO: store the intersection of the new surface segment with the z-axis, and then terminate
  231.         return
  232.       end if
  233.       ! keep increasing `i` until done
  234.       i = i + 1
  235.     end do
  236.   end subroutine Generate
  237.  
  238. end module Types
  239.  
  240. program SOS2DFTN
  241.   use Types
  242.   implicit none
  243.  
  244.   type(Emitter) :: LED = Emitter(1.0, 6.0, pi/6)
  245.   type(Lens)    :: glass
  246.   integer       :: i
  247.  
  248.   ! TODO: %Seed should also store `N` and `n_1` in the `type(Lens)` object
  249.   glass%N = 50; glass%n_1 = 1.49
  250.   call glass%Seed(LED, 15.0)
  251.   call glass%Generate(LED)
  252.   do i = 0, glass%J
  253.     print *, 'Point: ', glass%A(i)%P
  254.     if (i == glass%N) print *, '##########'
  255.   end do
  256.  
  257. end program SOS2DFTN
Advertisement