libSUFR
a LIBrary of Some Useful Fortran Routines
All Classes Namespaces Files Functions Variables Pages
optics.f90
Go to the documentation of this file.
1!> \file optics.f90 Procedures to do computations in optics
2
3
4! Copyright (c) 2002-2025 Marc van der Sluys - Nikhef/Utrecht University - marc.vandersluys.nl
5!
6! This file is part of the libSUFR package,
7! see: http://libsufr.sourceforge.net/
8!
9! This is free software: you can redistribute it and/or modify it under the terms of the European Union
10! Public Licence 1.2 (EUPL 1.2). This software is distributed in the hope that it will be useful, but
11! WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12! PURPOSE. See the EU Public Licence for more details. You should have received a copy of the European
13! Union Public Licence along with this code. If not, see <https://www.eupl.eu/1.2/en/>.
14!
15!
16
17
18
19
20!***********************************************************************************************************************************
21!> \brief Procedures to do computations in optics
22
24 implicit none
25 save
26
27contains
28
29
30 !***********************************************************************************************************************************
31 !> \brief Compute the reflectance for the transition from a medium with refractive index Nref1 to one with
32 !! Nref2, under an incident angle ang. Optionally, compute the transmittance, and the polarised
33 !! components. The media are assumed to be non-magnetic.
34 !!
35 !! \param angI Angle of incidence (rad)
36 !! \param Nref1 Refractive index of first medium, incoming ray
37 !! \param Nref2 Refractive index of second medium, transmitted ray
38 !!
39 !! \param Runp Unpolarised reflectance (output)
40 !!
41 !! \param Tunp Unpolarised transmittance (output, optional)
42 !!
43 !! \param Rprp Perpendicular polarised reflectance (output, optional)
44 !! \param Rpar Parallel polarised reflectance (output, optional)
45 !! \param Tprp Perpendicular polarised transmittance (output, optional)
46 !! \param Tpar Parallel polarised transmittance (output, optional)
47 !!
48 !! \param angT Angle of transmittance (rad; optional) (output, optional)
49 !!
50 !! \see
51 !! - Hecht, Optics, 3rd Ed. (1998), p.113ff
52 !! - https://en.wikipedia.org/wiki/Fresnel_equations#Power_or_intensity_equations
53
54 elemental subroutine reflectance_transmittance(angI, Nref1,Nref2, Runp, Tunp, Rprp,Rpar, Tprp,Tpar, angT)
55 use sufr_kinds, only: double
56 use sufr_constants, only: pio2
57
58 implicit none
59 real(double), intent(in) :: angi, nref1,nref2
60 real(double), intent(out) :: runp
61 real(double), intent(out), optional :: rprp,rpar, tunp,tprp,tpar, angt
62 real(double) :: var, langt, cosangi,cosangt, lrprp,lrpar
63
64 var = nref1/nref2 * sin(angi) ! Argument for Snell's law
65 if(var.gt.1.d0 .or. abs(angi).gt.pio2) then ! Total internal reflection or an impossible input value
66 lrprp = 1.d0
67 lrpar = 1.d0
68 langt = 0.d0
69 else
70 langt = asin(var) ! Angle of transmittance - Snell's law - local variable
71
72 ! Reused variables:
73 cosangi = cos(angi)
74 cosangt = cos(langt)
75
76 lrprp = ( (nref1 * cosangi - nref2 * cosangt) / (nref1 * cosangi + nref2 * cosangt) )**2
77 lrpar = ( (nref1 * cosangt - nref2 * cosangi) / (nref1 * cosangt + nref2 * cosangi) )**2
78 end if
79
80 ! Unpolarised reflectance:
81 runp = 0.5d0 * (lrprp + lrpar)
82
83 ! Assign optional return values:
84 if(present(rprp)) rprp = lrprp
85 if(present(rpar)) rpar = lrpar
86
87 if(present(tunp)) tunp = 1.d0 - runp
88 if(present(tprp)) tprp = 1.d0 - lrprp
89 if(present(tpar)) tpar = 1.d0 - lrpar
90
91 if(present(angt)) angt = langt
92
93 end subroutine reflectance_transmittance
94 !***********************************************************************************************************************************
95
96
97
98
99 !*********************************************************************************************************************************
100 !> \brief Computes real index of refraction of air given wavelength, temperature, pressure and relative humidity
101 !!
102 !! \param wavelength Wavelength (nanometers)
103 !! \param temperature Temperature (degrees Celsius)
104 !! \param pressure Pressure (millibar)
105 !! \param humidity Relative humidity (%)
106 !!
107 !! \retval refractive_index_air Refractive index of air
108 !!
109 !! \see http://emtoolbox.nist.gov/Wavelength/Documentation.asp#AppendixA
110
111 pure function refractive_index_air(wavelength, temperature, pressure, humidity)
112 use sufr_kinds, only: double
113 implicit none
114
115 real(double), intent(in) :: wavelength, temperature, pressure, humidity
117 real(double) :: s,t, omega, a,b,c,x, a1,a2,theta,y, psv, pv
118 real(double) :: d,e,f,g, ns,ntp
119
120 t = temperature + 273.15d0 ! deg Celsius -> Kelvin
121
122 if(temperature.ge.0.d0) then ! Above 0deg Celsius; use IAPWS model:
123 omega = t - 2.38555575678d-1/(t-6.50175348448d2)
124 a = omega**2 + 1.16705214528d3*omega - 7.24213167032d5
125 b = -1.70738469401d1*omega**2 + 1.20208247025d4*omega - 3.23255503223d6
126 c = 1.49151086135d1*omega**2 - 4.82326573616d3*omega + 4.05113405421d5
127 x = -b + sqrt(b**2 - 4*a*c)
128
129 psv = 1.d6*(2*c/x)**4 ! saturation vapor pressure
130
131 else ! Freezing
132
133 a1 = -13.928169d0
134 a2 = 34.7078238d0
135 theta = t/273.16d0
136 y = a1*(1.d0 - theta**(-1.5d0)) + a2*(1.d0 - theta**(-1.25d0))
137 psv = 611.657d0 * exp(y) ! saturation vapor pressure
138 end if
139
140 pv = (humidity/100.d0)*psv ! Water-vapor partial pressure
141
142 ! Modified Edlen Equation
143 a = 8342.54d0
144 b = 2406147.d0
145 c = 15998.d0
146 d = 96095.43d0
147 e = 0.601d0
148 f = 0.00972d0
149 g = 0.003661d0
150
151 s = 1.d6/wavelength**2
152 ns = 1.d0 + 1.d-8 * (a + b/(130.d0-s) + c/(38.9d0-s))
153 x = (1.d0 + 1.d-8 * (e - f*temperature) * (100*pressure)) / (1.d0 + g*temperature)
154 ntp = 1.d0 + (100*pressure) * (ns-1.d0) * x/d
155
156 refractive_index_air = ntp - 1.d-10*(292.75d0/t) * (3.7345d0 - 0.0401d0*s) * pv
157
158 end function refractive_index_air
159 !*********************************************************************************************************************************
160
161
162 !*********************************************************************************************************************************
163 !> \brief Refractive index of PMMA as a function of wavelength
164 !!
165 !! \param wavelength Wavelength in nanometres
166 !!
167 !! \retval refractive_index_pmma Refractive index of PMMA
168 !!
169 !! \note
170 !! - valid for 23 degrees C
171 !! - valid between 404.7 and 1083 nm
172 !!
173 !! \see Marcin Szczurowski, http://refractiveindex.info/?shelf=3d&book=plastics&page=pmma
174
175 pure function refractive_index_pmma(wavelength)
176 use sufr_kinds, only: double
177 implicit none
178 real(double), intent(in) :: wavelength
179 real(double) :: refractive_index_pmma, wl
180
181 wl = wavelength/1000.d0 ! nm -> microns
182 refractive_index_pmma = sqrt(1.d0 + 0.99654d0/(1.d0-0.00787d0/wl**2) + 0.18964d0/(1.d0-0.02191d0/wl**2) + &
183 0.00411d0/(1.d0-3.85727d0/wl**2))
184
185 end function refractive_index_pmma
186 !*********************************************************************************************************************************
187
188
189 !*********************************************************************************************************************************
190 !> \brief Return an RGB-value representing the colour corresponding to a light ray with a given wavelength
191 !!
192 !! \param wl Wavelength in nm (390-770 nm)
193 !! \param df Dimming factor 0-1 to scale RGB values with (0-1; 0: black, 1: full colour)
194 !!
195 !! \retval wavelength2rgb RGB values: (0-1, 0-1, 0-1)
196 !!
197 !!
198 !! \note
199 !! - Assumed pure colour at centre of their wavelength bands:
200 !! Colours: Violet, blue, green, yellow, orange, red:
201 !! (1,0,1) (0,0,1) (0,1,0) (1,1,0) (1,0.5,0) (1,0,0)
202 !!
203 !! - Colours between 390 and 420nm and 696 and 770nm are darker due to 'fading in' and 'fading out' effects
204
205 function wavelength2rgb(wl, df)
206 use sufr_kinds, only: double
207 use sufr_system, only: warn
208
209 implicit none
210 real(double), intent(in) :: wl,df
211 integer, parameter :: nc = 6 ! Number of colours in the spectrum (violet, blue, green, yellow, orange, red)
212 integer :: ic
213 real(double) :: wavelength2rgb(3), xipol,dxipol(nc),xipol0(nc), cbbnd(nc+1),cbctr(0:nc),cbdst(nc+1), rgb(3)
214
215
216 ! Set the boundaries of the colour bands and compute their centres and mutual distances:
217 cbbnd = dble([390,450,492,577,597,622,770]) ! Colour-band boundaries: 1:UV-V, 2:VB, 3:BG, 4:GY, 5:YO, 6:OR, 7:R-IR (nc+1)
218 do ic=1,nc
219 cbctr(ic) = (cbbnd(ic) + cbbnd(ic+1))/2.d0 ! Centre of a colour band
220 if(ic.gt.1) cbdst(ic) = cbctr(ic) - cbctr(ic-1) ! Distance between colour bands
221 end do
222 cbdst(1) = cbctr(1)-cbbnd(1)
223 cbdst(nc+1) = cbbnd(nc+1)-cbctr(nc)
224
225
226 ! Convert wavelength to 0 <= x <= 4.5:
227 dxipol = dble([0.5,1.0,1.0,0.5,0.5,0.5])
228 xipol0 = dble([0.5,1.0,2.0,3.0,3.5,4.0])
229 xipol = (wl-390.d0)/30.d0*0.5d0 ! Black to violet xIpol: 0.0 - 0.5
230 do ic=1,nc
231 if(wl.gt.cbctr(ic)) xipol = (wl-cbctr(ic)) / cbdst(ic+1) * dxipol(ic) + xipol0(ic)
232 end do
233 xipol = min(4.5d0, max(0.d0, xipol) ) ! xIpol should be 0 <= xIpol <= 4.5
234
235
236 ! Convert xIpol to RGB. Use black as 'invisible', i.e. UV or IR:
237 rgb = [0.d0, 0.d0, 0.d0] ! Ensure always defined
238 if(xipol.ge.0.d0 .and. xipol.le.0.5d0) then
239 rgb = [xipol, 0.d0, xipol] ! Black to violet xIpol: 0.0 - 0.5
240 else if(xipol.le.1.0d0) then
241 rgb = [1.d0-xipol, 0.d0, xipol] ! Violet to blue xIpol: 0.5 - 1.0
242 else if(xipol.le.2.0d0) then
243 rgb = [0.d0, xipol-1.d0, 2.d0-xipol] ! Blue to green xIpol: 1.0 - 2.0
244 else if(xipol.le.3.0d0) then
245 rgb = [xipol-2.d0, 1.d0, 0.d0] ! Green to yellow xIpol: 2.0 - 3.0
246 else if(xipol.le.3.5d0) then
247 rgb = [1.d0, 4.d0-xipol, 0.d0] ! Yellow to orange xIpol: 3.0 - 3.5
248 else if(xipol.le.4.0d0) then
249 rgb = [1.d0, 4.d0-xipol, 0.d0] ! Orange to red xIpol: 3.5 - 4.0
250 else if(xipol.le.4.5d0) then
251 rgb = [(4.5d0-xipol)*2, 0.d0, 0.d0] ! Red to black xIpol: 4.0 - 4.5
252 else
253 call warn('SUFR_optics/wavelength2RGB(): xIpol out of bounds')
254 end if
255
256
257 ! Apply dimming and return:
258 wavelength2rgb = rgb*df
259
260 end function wavelength2rgb
261 !*********************************************************************************************************************************
262
263
264end module sufr_optics
265!***********************************************************************************************************************************
266
Provides all constants in the library, and routines to define them.
Definition constants.f90:23
real(double), parameter, public pio2
pi/2
Definition constants.f90:38
Provides kinds and related constants/routines.
Definition kinds.f90:26
integer, parameter double
Double-precision float. Precision = 15, range = 307.
Definition kinds.f90:35
Procedures to do computations in optics.
Definition optics.f90:23
elemental subroutine reflectance_transmittance(angi, nref1, nref2, runp, tunp, rprp, rpar, tprp, tpar, angt)
Compute the reflectance for the transition from a medium with refractive index Nref1 to one with Nref...
Definition optics.f90:55
pure real(double) function refractive_index_air(wavelength, temperature, pressure, humidity)
Computes real index of refraction of air given wavelength, temperature, pressure and relative humidit...
Definition optics.f90:112
pure real(double) function refractive_index_pmma(wavelength)
Refractive index of PMMA as a function of wavelength.
Definition optics.f90:176
real(double) function, dimension(3) wavelength2rgb(wl, df)
Return an RGB-value representing the colour corresponding to a light ray with a given wavelength.
Definition optics.f90:206
System-related procedures.
Definition system.f90:23
subroutine warn(message, unit)
Print a warning to StdOut or StErr.
Definition system.f90:646