utils.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698
  1. """
  2. **Contains**
  3. * refraction_angle
  4. * fresnel_coefficients
  5. * deviation
  6. * brewster_angle
  7. * critical_angle
  8. * lens_makers_formula
  9. * mirror_formula
  10. * lens_formula
  11. * hyperfocal_distance
  12. * transverse_magnification
  13. """
  14. __all__ = ['refraction_angle',
  15. 'deviation',
  16. 'fresnel_coefficients',
  17. 'brewster_angle',
  18. 'critical_angle',
  19. 'lens_makers_formula',
  20. 'mirror_formula',
  21. 'lens_formula',
  22. 'hyperfocal_distance',
  23. 'transverse_magnification'
  24. ]
  25. from sympy.core.numbers import (Float, I, oo, pi, zoo)
  26. from sympy.core.singleton import S
  27. from sympy.core.symbol import Symbol
  28. from sympy.core.sympify import sympify
  29. from sympy.functions.elementary.miscellaneous import sqrt
  30. from sympy.functions.elementary.trigonometric import (acos, asin, atan2, cos, sin, tan)
  31. from sympy.matrices.dense import Matrix
  32. from sympy.polys.polytools import cancel
  33. from sympy.series.limits import Limit
  34. from sympy.geometry.line import Ray3D
  35. from sympy.geometry.util import intersection
  36. from sympy.geometry.plane import Plane
  37. from sympy.utilities.iterables import is_sequence
  38. from .medium import Medium
  39. def refractive_index_of_medium(medium):
  40. """
  41. Helper function that returns refractive index, given a medium
  42. """
  43. if isinstance(medium, Medium):
  44. n = medium.refractive_index
  45. else:
  46. n = sympify(medium)
  47. return n
  48. def refraction_angle(incident, medium1, medium2, normal=None, plane=None):
  49. """
  50. This function calculates transmitted vector after refraction at planar
  51. surface. ``medium1`` and ``medium2`` can be ``Medium`` or any sympifiable object.
  52. If ``incident`` is a number then treated as angle of incidence (in radians)
  53. in which case refraction angle is returned.
  54. If ``incident`` is an object of `Ray3D`, `normal` also has to be an instance
  55. of `Ray3D` in order to get the output as a `Ray3D`. Please note that if
  56. plane of separation is not provided and normal is an instance of `Ray3D`,
  57. ``normal`` will be assumed to be intersecting incident ray at the plane of
  58. separation. This will not be the case when `normal` is a `Matrix` or
  59. any other sequence.
  60. If ``incident`` is an instance of `Ray3D` and `plane` has not been provided
  61. and ``normal`` is not `Ray3D`, output will be a `Matrix`.
  62. Parameters
  63. ==========
  64. incident : Matrix, Ray3D, sequence or a number
  65. Incident vector or angle of incidence
  66. medium1 : sympy.physics.optics.medium.Medium or sympifiable
  67. Medium 1 or its refractive index
  68. medium2 : sympy.physics.optics.medium.Medium or sympifiable
  69. Medium 2 or its refractive index
  70. normal : Matrix, Ray3D, or sequence
  71. Normal vector
  72. plane : Plane
  73. Plane of separation of the two media.
  74. Returns
  75. =======
  76. Returns an angle of refraction or a refracted ray depending on inputs.
  77. Examples
  78. ========
  79. >>> from sympy.physics.optics import refraction_angle
  80. >>> from sympy.geometry import Point3D, Ray3D, Plane
  81. >>> from sympy.matrices import Matrix
  82. >>> from sympy import symbols, pi
  83. >>> n = Matrix([0, 0, 1])
  84. >>> P = Plane(Point3D(0, 0, 0), normal_vector=[0, 0, 1])
  85. >>> r1 = Ray3D(Point3D(-1, -1, 1), Point3D(0, 0, 0))
  86. >>> refraction_angle(r1, 1, 1, n)
  87. Matrix([
  88. [ 1],
  89. [ 1],
  90. [-1]])
  91. >>> refraction_angle(r1, 1, 1, plane=P)
  92. Ray3D(Point3D(0, 0, 0), Point3D(1, 1, -1))
  93. With different index of refraction of the two media
  94. >>> n1, n2 = symbols('n1, n2')
  95. >>> refraction_angle(r1, n1, n2, n)
  96. Matrix([
  97. [ n1/n2],
  98. [ n1/n2],
  99. [-sqrt(3)*sqrt(-2*n1**2/(3*n2**2) + 1)]])
  100. >>> refraction_angle(r1, n1, n2, plane=P)
  101. Ray3D(Point3D(0, 0, 0), Point3D(n1/n2, n1/n2, -sqrt(3)*sqrt(-2*n1**2/(3*n2**2) + 1)))
  102. >>> round(refraction_angle(pi/6, 1.2, 1.5), 5)
  103. 0.41152
  104. """
  105. n1 = refractive_index_of_medium(medium1)
  106. n2 = refractive_index_of_medium(medium2)
  107. # check if an incidence angle was supplied instead of a ray
  108. try:
  109. angle_of_incidence = float(incident)
  110. except TypeError:
  111. angle_of_incidence = None
  112. try:
  113. critical_angle_ = critical_angle(medium1, medium2)
  114. except (ValueError, TypeError):
  115. critical_angle_ = None
  116. if angle_of_incidence is not None:
  117. if normal is not None or plane is not None:
  118. raise ValueError('Normal/plane not allowed if incident is an angle')
  119. if not 0.0 <= angle_of_incidence < pi*0.5:
  120. raise ValueError('Angle of incidence not in range [0:pi/2)')
  121. if critical_angle_ and angle_of_incidence > critical_angle_:
  122. raise ValueError('Ray undergoes total internal reflection')
  123. return asin(n1*sin(angle_of_incidence)/n2)
  124. # Treat the incident as ray below
  125. # A flag to check whether to return Ray3D or not
  126. return_ray = False
  127. if plane is not None and normal is not None:
  128. raise ValueError("Either plane or normal is acceptable.")
  129. if not isinstance(incident, Matrix):
  130. if is_sequence(incident):
  131. _incident = Matrix(incident)
  132. elif isinstance(incident, Ray3D):
  133. _incident = Matrix(incident.direction_ratio)
  134. else:
  135. raise TypeError(
  136. "incident should be a Matrix, Ray3D, or sequence")
  137. else:
  138. _incident = incident
  139. # If plane is provided, get direction ratios of the normal
  140. # to the plane from the plane else go with `normal` param.
  141. if plane is not None:
  142. if not isinstance(plane, Plane):
  143. raise TypeError("plane should be an instance of geometry.plane.Plane")
  144. # If we have the plane, we can get the intersection
  145. # point of incident ray and the plane and thus return
  146. # an instance of Ray3D.
  147. if isinstance(incident, Ray3D):
  148. return_ray = True
  149. intersection_pt = plane.intersection(incident)[0]
  150. _normal = Matrix(plane.normal_vector)
  151. else:
  152. if not isinstance(normal, Matrix):
  153. if is_sequence(normal):
  154. _normal = Matrix(normal)
  155. elif isinstance(normal, Ray3D):
  156. _normal = Matrix(normal.direction_ratio)
  157. if isinstance(incident, Ray3D):
  158. intersection_pt = intersection(incident, normal)
  159. if len(intersection_pt) == 0:
  160. raise ValueError(
  161. "Normal isn't concurrent with the incident ray.")
  162. else:
  163. return_ray = True
  164. intersection_pt = intersection_pt[0]
  165. else:
  166. raise TypeError(
  167. "Normal should be a Matrix, Ray3D, or sequence")
  168. else:
  169. _normal = normal
  170. eta = n1/n2 # Relative index of refraction
  171. # Calculating magnitude of the vectors
  172. mag_incident = sqrt(sum([i**2 for i in _incident]))
  173. mag_normal = sqrt(sum([i**2 for i in _normal]))
  174. # Converting vectors to unit vectors by dividing
  175. # them with their magnitudes
  176. _incident /= mag_incident
  177. _normal /= mag_normal
  178. c1 = -_incident.dot(_normal) # cos(angle_of_incidence)
  179. cs2 = 1 - eta**2*(1 - c1**2) # cos(angle_of_refraction)**2
  180. if cs2.is_negative: # This is the case of total internal reflection(TIR).
  181. return S.Zero
  182. drs = eta*_incident + (eta*c1 - sqrt(cs2))*_normal
  183. # Multiplying unit vector by its magnitude
  184. drs = drs*mag_incident
  185. if not return_ray:
  186. return drs
  187. else:
  188. return Ray3D(intersection_pt, direction_ratio=drs)
  189. def fresnel_coefficients(angle_of_incidence, medium1, medium2):
  190. """
  191. This function uses Fresnel equations to calculate reflection and
  192. transmission coefficients. Those are obtained for both polarisations
  193. when the electric field vector is in the plane of incidence (labelled 'p')
  194. and when the electric field vector is perpendicular to the plane of
  195. incidence (labelled 's'). There are four real coefficients unless the
  196. incident ray reflects in total internal in which case there are two complex
  197. ones. Angle of incidence is the angle between the incident ray and the
  198. surface normal. ``medium1`` and ``medium2`` can be ``Medium`` or any
  199. sympifiable object.
  200. Parameters
  201. ==========
  202. angle_of_incidence : sympifiable
  203. medium1 : Medium or sympifiable
  204. Medium 1 or its refractive index
  205. medium2 : Medium or sympifiable
  206. Medium 2 or its refractive index
  207. Returns
  208. =======
  209. Returns a list with four real Fresnel coefficients:
  210. [reflection p (TM), reflection s (TE),
  211. transmission p (TM), transmission s (TE)]
  212. If the ray is undergoes total internal reflection then returns a
  213. list of two complex Fresnel coefficients:
  214. [reflection p (TM), reflection s (TE)]
  215. Examples
  216. ========
  217. >>> from sympy.physics.optics import fresnel_coefficients
  218. >>> fresnel_coefficients(0.3, 1, 2)
  219. [0.317843553417859, -0.348645229818821,
  220. 0.658921776708929, 0.651354770181179]
  221. >>> fresnel_coefficients(0.6, 2, 1)
  222. [-0.235625382192159 - 0.971843958291041*I,
  223. 0.816477005968898 - 0.577377951366403*I]
  224. References
  225. ==========
  226. .. [1] https://en.wikipedia.org/wiki/Fresnel_equations
  227. """
  228. if not 0 <= 2*angle_of_incidence < pi:
  229. raise ValueError('Angle of incidence not in range [0:pi/2)')
  230. n1 = refractive_index_of_medium(medium1)
  231. n2 = refractive_index_of_medium(medium2)
  232. angle_of_refraction = asin(n1*sin(angle_of_incidence)/n2)
  233. try:
  234. angle_of_total_internal_reflection_onset = critical_angle(n1, n2)
  235. except ValueError:
  236. angle_of_total_internal_reflection_onset = None
  237. if angle_of_total_internal_reflection_onset is None or\
  238. angle_of_total_internal_reflection_onset > angle_of_incidence:
  239. R_s = -sin(angle_of_incidence - angle_of_refraction)\
  240. /sin(angle_of_incidence + angle_of_refraction)
  241. R_p = tan(angle_of_incidence - angle_of_refraction)\
  242. /tan(angle_of_incidence + angle_of_refraction)
  243. T_s = 2*sin(angle_of_refraction)*cos(angle_of_incidence)\
  244. /sin(angle_of_incidence + angle_of_refraction)
  245. T_p = 2*sin(angle_of_refraction)*cos(angle_of_incidence)\
  246. /(sin(angle_of_incidence + angle_of_refraction)\
  247. *cos(angle_of_incidence - angle_of_refraction))
  248. return [R_p, R_s, T_p, T_s]
  249. else:
  250. n = n2/n1
  251. R_s = cancel((cos(angle_of_incidence)-\
  252. I*sqrt(sin(angle_of_incidence)**2 - n**2))\
  253. /(cos(angle_of_incidence)+\
  254. I*sqrt(sin(angle_of_incidence)**2 - n**2)))
  255. R_p = cancel((n**2*cos(angle_of_incidence)-\
  256. I*sqrt(sin(angle_of_incidence)**2 - n**2))\
  257. /(n**2*cos(angle_of_incidence)+\
  258. I*sqrt(sin(angle_of_incidence)**2 - n**2)))
  259. return [R_p, R_s]
  260. def deviation(incident, medium1, medium2, normal=None, plane=None):
  261. """
  262. This function calculates the angle of deviation of a ray
  263. due to refraction at planar surface.
  264. Parameters
  265. ==========
  266. incident : Matrix, Ray3D, sequence or float
  267. Incident vector or angle of incidence
  268. medium1 : sympy.physics.optics.medium.Medium or sympifiable
  269. Medium 1 or its refractive index
  270. medium2 : sympy.physics.optics.medium.Medium or sympifiable
  271. Medium 2 or its refractive index
  272. normal : Matrix, Ray3D, or sequence
  273. Normal vector
  274. plane : Plane
  275. Plane of separation of the two media.
  276. Returns angular deviation between incident and refracted rays
  277. Examples
  278. ========
  279. >>> from sympy.physics.optics import deviation
  280. >>> from sympy.geometry import Point3D, Ray3D, Plane
  281. >>> from sympy.matrices import Matrix
  282. >>> from sympy import symbols
  283. >>> n1, n2 = symbols('n1, n2')
  284. >>> n = Matrix([0, 0, 1])
  285. >>> P = Plane(Point3D(0, 0, 0), normal_vector=[0, 0, 1])
  286. >>> r1 = Ray3D(Point3D(-1, -1, 1), Point3D(0, 0, 0))
  287. >>> deviation(r1, 1, 1, n)
  288. 0
  289. >>> deviation(r1, n1, n2, plane=P)
  290. -acos(-sqrt(-2*n1**2/(3*n2**2) + 1)) + acos(-sqrt(3)/3)
  291. >>> round(deviation(0.1, 1.2, 1.5), 5)
  292. -0.02005
  293. """
  294. refracted = refraction_angle(incident,
  295. medium1,
  296. medium2,
  297. normal=normal,
  298. plane=plane)
  299. try:
  300. angle_of_incidence = Float(incident)
  301. except TypeError:
  302. angle_of_incidence = None
  303. if angle_of_incidence is not None:
  304. return float(refracted) - angle_of_incidence
  305. if refracted != 0:
  306. if isinstance(refracted, Ray3D):
  307. refracted = Matrix(refracted.direction_ratio)
  308. if not isinstance(incident, Matrix):
  309. if is_sequence(incident):
  310. _incident = Matrix(incident)
  311. elif isinstance(incident, Ray3D):
  312. _incident = Matrix(incident.direction_ratio)
  313. else:
  314. raise TypeError(
  315. "incident should be a Matrix, Ray3D, or sequence")
  316. else:
  317. _incident = incident
  318. if plane is None:
  319. if not isinstance(normal, Matrix):
  320. if is_sequence(normal):
  321. _normal = Matrix(normal)
  322. elif isinstance(normal, Ray3D):
  323. _normal = Matrix(normal.direction_ratio)
  324. else:
  325. raise TypeError(
  326. "normal should be a Matrix, Ray3D, or sequence")
  327. else:
  328. _normal = normal
  329. else:
  330. _normal = Matrix(plane.normal_vector)
  331. mag_incident = sqrt(sum([i**2 for i in _incident]))
  332. mag_normal = sqrt(sum([i**2 for i in _normal]))
  333. mag_refracted = sqrt(sum([i**2 for i in refracted]))
  334. _incident /= mag_incident
  335. _normal /= mag_normal
  336. refracted /= mag_refracted
  337. i = acos(_incident.dot(_normal))
  338. r = acos(refracted.dot(_normal))
  339. return i - r
  340. def brewster_angle(medium1, medium2):
  341. """
  342. This function calculates the Brewster's angle of incidence to Medium 2 from
  343. Medium 1 in radians.
  344. Parameters
  345. ==========
  346. medium 1 : Medium or sympifiable
  347. Refractive index of Medium 1
  348. medium 2 : Medium or sympifiable
  349. Refractive index of Medium 1
  350. Examples
  351. ========
  352. >>> from sympy.physics.optics import brewster_angle
  353. >>> brewster_angle(1, 1.33)
  354. 0.926093295503462
  355. """
  356. n1 = refractive_index_of_medium(medium1)
  357. n2 = refractive_index_of_medium(medium2)
  358. return atan2(n2, n1)
  359. def critical_angle(medium1, medium2):
  360. """
  361. This function calculates the critical angle of incidence (marking the onset
  362. of total internal) to Medium 2 from Medium 1 in radians.
  363. Parameters
  364. ==========
  365. medium 1 : Medium or sympifiable
  366. Refractive index of Medium 1.
  367. medium 2 : Medium or sympifiable
  368. Refractive index of Medium 1.
  369. Examples
  370. ========
  371. >>> from sympy.physics.optics import critical_angle
  372. >>> critical_angle(1.33, 1)
  373. 0.850908514477849
  374. """
  375. n1 = refractive_index_of_medium(medium1)
  376. n2 = refractive_index_of_medium(medium2)
  377. if n2 > n1:
  378. raise ValueError('Total internal reflection impossible for n1 < n2')
  379. else:
  380. return asin(n2/n1)
  381. def lens_makers_formula(n_lens, n_surr, r1, r2, d=0):
  382. """
  383. This function calculates focal length of a lens.
  384. It follows cartesian sign convention.
  385. Parameters
  386. ==========
  387. n_lens : Medium or sympifiable
  388. Index of refraction of lens.
  389. n_surr : Medium or sympifiable
  390. Index of reflection of surrounding.
  391. r1 : sympifiable
  392. Radius of curvature of first surface.
  393. r2 : sympifiable
  394. Radius of curvature of second surface.
  395. d : sympifiable, optional
  396. Thickness of lens, default value is 0.
  397. Examples
  398. ========
  399. >>> from sympy.physics.optics import lens_makers_formula
  400. >>> from sympy import S
  401. >>> lens_makers_formula(1.33, 1, 10, -10)
  402. 15.1515151515151
  403. >>> lens_makers_formula(1.2, 1, 10, S.Infinity)
  404. 50.0000000000000
  405. >>> lens_makers_formula(1.33, 1, 10, -10, d=1)
  406. 15.3418463277618
  407. """
  408. if isinstance(n_lens, Medium):
  409. n_lens = n_lens.refractive_index
  410. else:
  411. n_lens = sympify(n_lens)
  412. if isinstance(n_surr, Medium):
  413. n_surr = n_surr.refractive_index
  414. else:
  415. n_surr = sympify(n_surr)
  416. d = sympify(d)
  417. focal_length = 1/((n_lens - n_surr) / n_surr*(1/r1 - 1/r2 + (((n_lens - n_surr) * d) / (n_lens * r1 * r2))))
  418. if focal_length == zoo:
  419. return S.Infinity
  420. return focal_length
  421. def mirror_formula(focal_length=None, u=None, v=None):
  422. """
  423. This function provides one of the three parameters
  424. when two of them are supplied.
  425. This is valid only for paraxial rays.
  426. Parameters
  427. ==========
  428. focal_length : sympifiable
  429. Focal length of the mirror.
  430. u : sympifiable
  431. Distance of object from the pole on
  432. the principal axis.
  433. v : sympifiable
  434. Distance of the image from the pole
  435. on the principal axis.
  436. Examples
  437. ========
  438. >>> from sympy.physics.optics import mirror_formula
  439. >>> from sympy.abc import f, u, v
  440. >>> mirror_formula(focal_length=f, u=u)
  441. f*u/(-f + u)
  442. >>> mirror_formula(focal_length=f, v=v)
  443. f*v/(-f + v)
  444. >>> mirror_formula(u=u, v=v)
  445. u*v/(u + v)
  446. """
  447. if focal_length and u and v:
  448. raise ValueError("Please provide only two parameters")
  449. focal_length = sympify(focal_length)
  450. u = sympify(u)
  451. v = sympify(v)
  452. if u is oo:
  453. _u = Symbol('u')
  454. if v is oo:
  455. _v = Symbol('v')
  456. if focal_length is oo:
  457. _f = Symbol('f')
  458. if focal_length is None:
  459. if u is oo and v is oo:
  460. return Limit(Limit(_v*_u/(_v + _u), _u, oo), _v, oo).doit()
  461. if u is oo:
  462. return Limit(v*_u/(v + _u), _u, oo).doit()
  463. if v is oo:
  464. return Limit(_v*u/(_v + u), _v, oo).doit()
  465. return v*u/(v + u)
  466. if u is None:
  467. if v is oo and focal_length is oo:
  468. return Limit(Limit(_v*_f/(_v - _f), _v, oo), _f, oo).doit()
  469. if v is oo:
  470. return Limit(_v*focal_length/(_v - focal_length), _v, oo).doit()
  471. if focal_length is oo:
  472. return Limit(v*_f/(v - _f), _f, oo).doit()
  473. return v*focal_length/(v - focal_length)
  474. if v is None:
  475. if u is oo and focal_length is oo:
  476. return Limit(Limit(_u*_f/(_u - _f), _u, oo), _f, oo).doit()
  477. if u is oo:
  478. return Limit(_u*focal_length/(_u - focal_length), _u, oo).doit()
  479. if focal_length is oo:
  480. return Limit(u*_f/(u - _f), _f, oo).doit()
  481. return u*focal_length/(u - focal_length)
  482. def lens_formula(focal_length=None, u=None, v=None):
  483. """
  484. This function provides one of the three parameters
  485. when two of them are supplied.
  486. This is valid only for paraxial rays.
  487. Parameters
  488. ==========
  489. focal_length : sympifiable
  490. Focal length of the mirror.
  491. u : sympifiable
  492. Distance of object from the optical center on
  493. the principal axis.
  494. v : sympifiable
  495. Distance of the image from the optical center
  496. on the principal axis.
  497. Examples
  498. ========
  499. >>> from sympy.physics.optics import lens_formula
  500. >>> from sympy.abc import f, u, v
  501. >>> lens_formula(focal_length=f, u=u)
  502. f*u/(f + u)
  503. >>> lens_formula(focal_length=f, v=v)
  504. f*v/(f - v)
  505. >>> lens_formula(u=u, v=v)
  506. u*v/(u - v)
  507. """
  508. if focal_length and u and v:
  509. raise ValueError("Please provide only two parameters")
  510. focal_length = sympify(focal_length)
  511. u = sympify(u)
  512. v = sympify(v)
  513. if u is oo:
  514. _u = Symbol('u')
  515. if v is oo:
  516. _v = Symbol('v')
  517. if focal_length is oo:
  518. _f = Symbol('f')
  519. if focal_length is None:
  520. if u is oo and v is oo:
  521. return Limit(Limit(_v*_u/(_u - _v), _u, oo), _v, oo).doit()
  522. if u is oo:
  523. return Limit(v*_u/(_u - v), _u, oo).doit()
  524. if v is oo:
  525. return Limit(_v*u/(u - _v), _v, oo).doit()
  526. return v*u/(u - v)
  527. if u is None:
  528. if v is oo and focal_length is oo:
  529. return Limit(Limit(_v*_f/(_f - _v), _v, oo), _f, oo).doit()
  530. if v is oo:
  531. return Limit(_v*focal_length/(focal_length - _v), _v, oo).doit()
  532. if focal_length is oo:
  533. return Limit(v*_f/(_f - v), _f, oo).doit()
  534. return v*focal_length/(focal_length - v)
  535. if v is None:
  536. if u is oo and focal_length is oo:
  537. return Limit(Limit(_u*_f/(_u + _f), _u, oo), _f, oo).doit()
  538. if u is oo:
  539. return Limit(_u*focal_length/(_u + focal_length), _u, oo).doit()
  540. if focal_length is oo:
  541. return Limit(u*_f/(u + _f), _f, oo).doit()
  542. return u*focal_length/(u + focal_length)
  543. def hyperfocal_distance(f, N, c):
  544. """
  545. Parameters
  546. ==========
  547. f: sympifiable
  548. Focal length of a given lens.
  549. N: sympifiable
  550. F-number of a given lens.
  551. c: sympifiable
  552. Circle of Confusion (CoC) of a given image format.
  553. Example
  554. =======
  555. >>> from sympy.physics.optics import hyperfocal_distance
  556. >>> round(hyperfocal_distance(f = 0.5, N = 8, c = 0.0033), 2)
  557. 9.47
  558. """
  559. f = sympify(f)
  560. N = sympify(N)
  561. c = sympify(c)
  562. return (1/(N * c))*(f**2)
  563. def transverse_magnification(si, so):
  564. """
  565. Calculates the transverse magnification, which is the ratio of the
  566. image size to the object size.
  567. Parameters
  568. ==========
  569. so: sympifiable
  570. Lens-object distance.
  571. si: sympifiable
  572. Lens-image distance.
  573. Example
  574. =======
  575. >>> from sympy.physics.optics import transverse_magnification
  576. >>> transverse_magnification(30, 15)
  577. -2
  578. """
  579. si = sympify(si)
  580. so = sympify(so)
  581. return (-(si/so))