|
""" |
|
**Contains** |
|
|
|
* refraction_angle |
|
* fresnel_coefficients |
|
* deviation |
|
* brewster_angle |
|
* critical_angle |
|
* lens_makers_formula |
|
* mirror_formula |
|
* lens_formula |
|
* hyperfocal_distance |
|
* transverse_magnification |
|
""" |
|
|
|
__all__ = ['refraction_angle', |
|
'deviation', |
|
'fresnel_coefficients', |
|
'brewster_angle', |
|
'critical_angle', |
|
'lens_makers_formula', |
|
'mirror_formula', |
|
'lens_formula', |
|
'hyperfocal_distance', |
|
'transverse_magnification' |
|
] |
|
|
|
from sympy.core.numbers import (Float, I, oo, pi, zoo) |
|
from sympy.core.singleton import S |
|
from sympy.core.symbol import Symbol |
|
from sympy.core.sympify import sympify |
|
from sympy.functions.elementary.miscellaneous import sqrt |
|
from sympy.functions.elementary.trigonometric import (acos, asin, atan2, cos, sin, tan) |
|
from sympy.matrices.dense import Matrix |
|
from sympy.polys.polytools import cancel |
|
from sympy.series.limits import Limit |
|
from sympy.geometry.line import Ray3D |
|
from sympy.geometry.util import intersection |
|
from sympy.geometry.plane import Plane |
|
from sympy.utilities.iterables import is_sequence |
|
from .medium import Medium |
|
|
|
|
|
def refractive_index_of_medium(medium): |
|
""" |
|
Helper function that returns refractive index, given a medium |
|
""" |
|
if isinstance(medium, Medium): |
|
n = medium.refractive_index |
|
else: |
|
n = sympify(medium) |
|
return n |
|
|
|
|
|
def refraction_angle(incident, medium1, medium2, normal=None, plane=None): |
|
""" |
|
This function calculates transmitted vector after refraction at planar |
|
surface. ``medium1`` and ``medium2`` can be ``Medium`` or any sympifiable object. |
|
If ``incident`` is a number then treated as angle of incidence (in radians) |
|
in which case refraction angle is returned. |
|
|
|
If ``incident`` is an object of `Ray3D`, `normal` also has to be an instance |
|
of `Ray3D` in order to get the output as a `Ray3D`. Please note that if |
|
plane of separation is not provided and normal is an instance of `Ray3D`, |
|
``normal`` will be assumed to be intersecting incident ray at the plane of |
|
separation. This will not be the case when `normal` is a `Matrix` or |
|
any other sequence. |
|
If ``incident`` is an instance of `Ray3D` and `plane` has not been provided |
|
and ``normal`` is not `Ray3D`, output will be a `Matrix`. |
|
|
|
Parameters |
|
========== |
|
|
|
incident : Matrix, Ray3D, sequence or a number |
|
Incident vector or angle of incidence |
|
medium1 : sympy.physics.optics.medium.Medium or sympifiable |
|
Medium 1 or its refractive index |
|
medium2 : sympy.physics.optics.medium.Medium or sympifiable |
|
Medium 2 or its refractive index |
|
normal : Matrix, Ray3D, or sequence |
|
Normal vector |
|
plane : Plane |
|
Plane of separation of the two media. |
|
|
|
Returns |
|
======= |
|
|
|
Returns an angle of refraction or a refracted ray depending on inputs. |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.physics.optics import refraction_angle |
|
>>> from sympy.geometry import Point3D, Ray3D, Plane |
|
>>> from sympy.matrices import Matrix |
|
>>> from sympy import symbols, pi |
|
>>> n = Matrix([0, 0, 1]) |
|
>>> P = Plane(Point3D(0, 0, 0), normal_vector=[0, 0, 1]) |
|
>>> r1 = Ray3D(Point3D(-1, -1, 1), Point3D(0, 0, 0)) |
|
>>> refraction_angle(r1, 1, 1, n) |
|
Matrix([ |
|
[ 1], |
|
[ 1], |
|
[-1]]) |
|
>>> refraction_angle(r1, 1, 1, plane=P) |
|
Ray3D(Point3D(0, 0, 0), Point3D(1, 1, -1)) |
|
|
|
With different index of refraction of the two media |
|
|
|
>>> n1, n2 = symbols('n1, n2') |
|
>>> refraction_angle(r1, n1, n2, n) |
|
Matrix([ |
|
[ n1/n2], |
|
[ n1/n2], |
|
[-sqrt(3)*sqrt(-2*n1**2/(3*n2**2) + 1)]]) |
|
>>> refraction_angle(r1, n1, n2, plane=P) |
|
Ray3D(Point3D(0, 0, 0), Point3D(n1/n2, n1/n2, -sqrt(3)*sqrt(-2*n1**2/(3*n2**2) + 1))) |
|
>>> round(refraction_angle(pi/6, 1.2, 1.5), 5) |
|
0.41152 |
|
""" |
|
|
|
n1 = refractive_index_of_medium(medium1) |
|
n2 = refractive_index_of_medium(medium2) |
|
|
|
|
|
try: |
|
angle_of_incidence = float(incident) |
|
except TypeError: |
|
angle_of_incidence = None |
|
|
|
try: |
|
critical_angle_ = critical_angle(medium1, medium2) |
|
except (ValueError, TypeError): |
|
critical_angle_ = None |
|
|
|
if angle_of_incidence is not None: |
|
if normal is not None or plane is not None: |
|
raise ValueError('Normal/plane not allowed if incident is an angle') |
|
|
|
if not 0.0 <= angle_of_incidence < pi*0.5: |
|
raise ValueError('Angle of incidence not in range [0:pi/2)') |
|
|
|
if critical_angle_ and angle_of_incidence > critical_angle_: |
|
raise ValueError('Ray undergoes total internal reflection') |
|
return asin(n1*sin(angle_of_incidence)/n2) |
|
|
|
|
|
|
|
return_ray = False |
|
|
|
if plane is not None and normal is not None: |
|
raise ValueError("Either plane or normal is acceptable.") |
|
|
|
if not isinstance(incident, Matrix): |
|
if is_sequence(incident): |
|
_incident = Matrix(incident) |
|
elif isinstance(incident, Ray3D): |
|
_incident = Matrix(incident.direction_ratio) |
|
else: |
|
raise TypeError( |
|
"incident should be a Matrix, Ray3D, or sequence") |
|
else: |
|
_incident = incident |
|
|
|
|
|
|
|
if plane is not None: |
|
if not isinstance(plane, Plane): |
|
raise TypeError("plane should be an instance of geometry.plane.Plane") |
|
|
|
|
|
|
|
if isinstance(incident, Ray3D): |
|
return_ray = True |
|
intersection_pt = plane.intersection(incident)[0] |
|
_normal = Matrix(plane.normal_vector) |
|
else: |
|
if not isinstance(normal, Matrix): |
|
if is_sequence(normal): |
|
_normal = Matrix(normal) |
|
elif isinstance(normal, Ray3D): |
|
_normal = Matrix(normal.direction_ratio) |
|
if isinstance(incident, Ray3D): |
|
intersection_pt = intersection(incident, normal) |
|
if len(intersection_pt) == 0: |
|
raise ValueError( |
|
"Normal isn't concurrent with the incident ray.") |
|
else: |
|
return_ray = True |
|
intersection_pt = intersection_pt[0] |
|
else: |
|
raise TypeError( |
|
"Normal should be a Matrix, Ray3D, or sequence") |
|
else: |
|
_normal = normal |
|
|
|
eta = n1/n2 |
|
|
|
mag_incident = sqrt(sum(i**2 for i in _incident)) |
|
mag_normal = sqrt(sum(i**2 for i in _normal)) |
|
|
|
|
|
_incident /= mag_incident |
|
_normal /= mag_normal |
|
c1 = -_incident.dot(_normal) |
|
cs2 = 1 - eta**2*(1 - c1**2) |
|
if cs2.is_negative: |
|
return S.Zero |
|
drs = eta*_incident + (eta*c1 - sqrt(cs2))*_normal |
|
|
|
drs = drs*mag_incident |
|
if not return_ray: |
|
return drs |
|
else: |
|
return Ray3D(intersection_pt, direction_ratio=drs) |
|
|
|
|
|
def fresnel_coefficients(angle_of_incidence, medium1, medium2): |
|
""" |
|
This function uses Fresnel equations to calculate reflection and |
|
transmission coefficients. Those are obtained for both polarisations |
|
when the electric field vector is in the plane of incidence (labelled 'p') |
|
and when the electric field vector is perpendicular to the plane of |
|
incidence (labelled 's'). There are four real coefficients unless the |
|
incident ray reflects in total internal in which case there are two complex |
|
ones. Angle of incidence is the angle between the incident ray and the |
|
surface normal. ``medium1`` and ``medium2`` can be ``Medium`` or any |
|
sympifiable object. |
|
|
|
Parameters |
|
========== |
|
|
|
angle_of_incidence : sympifiable |
|
|
|
medium1 : Medium or sympifiable |
|
Medium 1 or its refractive index |
|
|
|
medium2 : Medium or sympifiable |
|
Medium 2 or its refractive index |
|
|
|
Returns |
|
======= |
|
|
|
Returns a list with four real Fresnel coefficients: |
|
[reflection p (TM), reflection s (TE), |
|
transmission p (TM), transmission s (TE)] |
|
If the ray is undergoes total internal reflection then returns a |
|
list of two complex Fresnel coefficients: |
|
[reflection p (TM), reflection s (TE)] |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.physics.optics import fresnel_coefficients |
|
>>> fresnel_coefficients(0.3, 1, 2) |
|
[0.317843553417859, -0.348645229818821, |
|
0.658921776708929, 0.651354770181179] |
|
>>> fresnel_coefficients(0.6, 2, 1) |
|
[-0.235625382192159 - 0.971843958291041*I, |
|
0.816477005968898 - 0.577377951366403*I] |
|
|
|
References |
|
========== |
|
|
|
.. [1] https://en.wikipedia.org/wiki/Fresnel_equations |
|
""" |
|
if not 0 <= 2*angle_of_incidence < pi: |
|
raise ValueError('Angle of incidence not in range [0:pi/2)') |
|
|
|
n1 = refractive_index_of_medium(medium1) |
|
n2 = refractive_index_of_medium(medium2) |
|
|
|
angle_of_refraction = asin(n1*sin(angle_of_incidence)/n2) |
|
try: |
|
angle_of_total_internal_reflection_onset = critical_angle(n1, n2) |
|
except ValueError: |
|
angle_of_total_internal_reflection_onset = None |
|
|
|
if angle_of_total_internal_reflection_onset is None or\ |
|
angle_of_total_internal_reflection_onset > angle_of_incidence: |
|
R_s = -sin(angle_of_incidence - angle_of_refraction)\ |
|
/sin(angle_of_incidence + angle_of_refraction) |
|
R_p = tan(angle_of_incidence - angle_of_refraction)\ |
|
/tan(angle_of_incidence + angle_of_refraction) |
|
T_s = 2*sin(angle_of_refraction)*cos(angle_of_incidence)\ |
|
/sin(angle_of_incidence + angle_of_refraction) |
|
T_p = 2*sin(angle_of_refraction)*cos(angle_of_incidence)\ |
|
/(sin(angle_of_incidence + angle_of_refraction)\ |
|
*cos(angle_of_incidence - angle_of_refraction)) |
|
return [R_p, R_s, T_p, T_s] |
|
else: |
|
n = n2/n1 |
|
R_s = cancel((cos(angle_of_incidence)-\ |
|
I*sqrt(sin(angle_of_incidence)**2 - n**2))\ |
|
/(cos(angle_of_incidence)+\ |
|
I*sqrt(sin(angle_of_incidence)**2 - n**2))) |
|
R_p = cancel((n**2*cos(angle_of_incidence)-\ |
|
I*sqrt(sin(angle_of_incidence)**2 - n**2))\ |
|
/(n**2*cos(angle_of_incidence)+\ |
|
I*sqrt(sin(angle_of_incidence)**2 - n**2))) |
|
return [R_p, R_s] |
|
|
|
|
|
def deviation(incident, medium1, medium2, normal=None, plane=None): |
|
""" |
|
This function calculates the angle of deviation of a ray |
|
due to refraction at planar surface. |
|
|
|
Parameters |
|
========== |
|
|
|
incident : Matrix, Ray3D, sequence or float |
|
Incident vector or angle of incidence |
|
medium1 : sympy.physics.optics.medium.Medium or sympifiable |
|
Medium 1 or its refractive index |
|
medium2 : sympy.physics.optics.medium.Medium or sympifiable |
|
Medium 2 or its refractive index |
|
normal : Matrix, Ray3D, or sequence |
|
Normal vector |
|
plane : Plane |
|
Plane of separation of the two media. |
|
|
|
Returns angular deviation between incident and refracted rays |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.physics.optics import deviation |
|
>>> from sympy.geometry import Point3D, Ray3D, Plane |
|
>>> from sympy.matrices import Matrix |
|
>>> from sympy import symbols |
|
>>> n1, n2 = symbols('n1, n2') |
|
>>> n = Matrix([0, 0, 1]) |
|
>>> P = Plane(Point3D(0, 0, 0), normal_vector=[0, 0, 1]) |
|
>>> r1 = Ray3D(Point3D(-1, -1, 1), Point3D(0, 0, 0)) |
|
>>> deviation(r1, 1, 1, n) |
|
0 |
|
>>> deviation(r1, n1, n2, plane=P) |
|
-acos(-sqrt(-2*n1**2/(3*n2**2) + 1)) + acos(-sqrt(3)/3) |
|
>>> round(deviation(0.1, 1.2, 1.5), 5) |
|
-0.02005 |
|
""" |
|
refracted = refraction_angle(incident, |
|
medium1, |
|
medium2, |
|
normal=normal, |
|
plane=plane) |
|
try: |
|
angle_of_incidence = Float(incident) |
|
except TypeError: |
|
angle_of_incidence = None |
|
|
|
if angle_of_incidence is not None: |
|
return float(refracted) - angle_of_incidence |
|
|
|
if refracted != 0: |
|
if isinstance(refracted, Ray3D): |
|
refracted = Matrix(refracted.direction_ratio) |
|
|
|
if not isinstance(incident, Matrix): |
|
if is_sequence(incident): |
|
_incident = Matrix(incident) |
|
elif isinstance(incident, Ray3D): |
|
_incident = Matrix(incident.direction_ratio) |
|
else: |
|
raise TypeError( |
|
"incident should be a Matrix, Ray3D, or sequence") |
|
else: |
|
_incident = incident |
|
|
|
if plane is None: |
|
if not isinstance(normal, Matrix): |
|
if is_sequence(normal): |
|
_normal = Matrix(normal) |
|
elif isinstance(normal, Ray3D): |
|
_normal = Matrix(normal.direction_ratio) |
|
else: |
|
raise TypeError( |
|
"normal should be a Matrix, Ray3D, or sequence") |
|
else: |
|
_normal = normal |
|
else: |
|
_normal = Matrix(plane.normal_vector) |
|
|
|
mag_incident = sqrt(sum(i**2 for i in _incident)) |
|
mag_normal = sqrt(sum(i**2 for i in _normal)) |
|
mag_refracted = sqrt(sum(i**2 for i in refracted)) |
|
_incident /= mag_incident |
|
_normal /= mag_normal |
|
refracted /= mag_refracted |
|
i = acos(_incident.dot(_normal)) |
|
r = acos(refracted.dot(_normal)) |
|
return i - r |
|
|
|
|
|
def brewster_angle(medium1, medium2): |
|
""" |
|
This function calculates the Brewster's angle of incidence to Medium 2 from |
|
Medium 1 in radians. |
|
|
|
Parameters |
|
========== |
|
|
|
medium 1 : Medium or sympifiable |
|
Refractive index of Medium 1 |
|
medium 2 : Medium or sympifiable |
|
Refractive index of Medium 1 |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.physics.optics import brewster_angle |
|
>>> brewster_angle(1, 1.33) |
|
0.926093295503462 |
|
|
|
""" |
|
|
|
n1 = refractive_index_of_medium(medium1) |
|
n2 = refractive_index_of_medium(medium2) |
|
|
|
return atan2(n2, n1) |
|
|
|
def critical_angle(medium1, medium2): |
|
""" |
|
This function calculates the critical angle of incidence (marking the onset |
|
of total internal) to Medium 2 from Medium 1 in radians. |
|
|
|
Parameters |
|
========== |
|
|
|
medium 1 : Medium or sympifiable |
|
Refractive index of Medium 1. |
|
medium 2 : Medium or sympifiable |
|
Refractive index of Medium 1. |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.physics.optics import critical_angle |
|
>>> critical_angle(1.33, 1) |
|
0.850908514477849 |
|
|
|
""" |
|
|
|
n1 = refractive_index_of_medium(medium1) |
|
n2 = refractive_index_of_medium(medium2) |
|
|
|
if n2 > n1: |
|
raise ValueError('Total internal reflection impossible for n1 < n2') |
|
else: |
|
return asin(n2/n1) |
|
|
|
|
|
|
|
def lens_makers_formula(n_lens, n_surr, r1, r2, d=0): |
|
""" |
|
This function calculates focal length of a lens. |
|
It follows cartesian sign convention. |
|
|
|
Parameters |
|
========== |
|
|
|
n_lens : Medium or sympifiable |
|
Index of refraction of lens. |
|
n_surr : Medium or sympifiable |
|
Index of reflection of surrounding. |
|
r1 : sympifiable |
|
Radius of curvature of first surface. |
|
r2 : sympifiable |
|
Radius of curvature of second surface. |
|
d : sympifiable, optional |
|
Thickness of lens, default value is 0. |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.physics.optics import lens_makers_formula |
|
>>> from sympy import S |
|
>>> lens_makers_formula(1.33, 1, 10, -10) |
|
15.1515151515151 |
|
>>> lens_makers_formula(1.2, 1, 10, S.Infinity) |
|
50.0000000000000 |
|
>>> lens_makers_formula(1.33, 1, 10, -10, d=1) |
|
15.3418463277618 |
|
|
|
""" |
|
|
|
if isinstance(n_lens, Medium): |
|
n_lens = n_lens.refractive_index |
|
else: |
|
n_lens = sympify(n_lens) |
|
if isinstance(n_surr, Medium): |
|
n_surr = n_surr.refractive_index |
|
else: |
|
n_surr = sympify(n_surr) |
|
d = sympify(d) |
|
|
|
focal_length = 1/((n_lens - n_surr) / n_surr*(1/r1 - 1/r2 + (((n_lens - n_surr) * d) / (n_lens * r1 * r2)))) |
|
|
|
if focal_length == zoo: |
|
return S.Infinity |
|
return focal_length |
|
|
|
|
|
def mirror_formula(focal_length=None, u=None, v=None): |
|
""" |
|
This function provides one of the three parameters |
|
when two of them are supplied. |
|
This is valid only for paraxial rays. |
|
|
|
Parameters |
|
========== |
|
|
|
focal_length : sympifiable |
|
Focal length of the mirror. |
|
u : sympifiable |
|
Distance of object from the pole on |
|
the principal axis. |
|
v : sympifiable |
|
Distance of the image from the pole |
|
on the principal axis. |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.physics.optics import mirror_formula |
|
>>> from sympy.abc import f, u, v |
|
>>> mirror_formula(focal_length=f, u=u) |
|
f*u/(-f + u) |
|
>>> mirror_formula(focal_length=f, v=v) |
|
f*v/(-f + v) |
|
>>> mirror_formula(u=u, v=v) |
|
u*v/(u + v) |
|
|
|
""" |
|
if focal_length and u and v: |
|
raise ValueError("Please provide only two parameters") |
|
|
|
focal_length = sympify(focal_length) |
|
u = sympify(u) |
|
v = sympify(v) |
|
if u is oo: |
|
_u = Symbol('u') |
|
if v is oo: |
|
_v = Symbol('v') |
|
if focal_length is oo: |
|
_f = Symbol('f') |
|
if focal_length is None: |
|
if u is oo and v is oo: |
|
return Limit(Limit(_v*_u/(_v + _u), _u, oo), _v, oo).doit() |
|
if u is oo: |
|
return Limit(v*_u/(v + _u), _u, oo).doit() |
|
if v is oo: |
|
return Limit(_v*u/(_v + u), _v, oo).doit() |
|
return v*u/(v + u) |
|
if u is None: |
|
if v is oo and focal_length is oo: |
|
return Limit(Limit(_v*_f/(_v - _f), _v, oo), _f, oo).doit() |
|
if v is oo: |
|
return Limit(_v*focal_length/(_v - focal_length), _v, oo).doit() |
|
if focal_length is oo: |
|
return Limit(v*_f/(v - _f), _f, oo).doit() |
|
return v*focal_length/(v - focal_length) |
|
if v is None: |
|
if u is oo and focal_length is oo: |
|
return Limit(Limit(_u*_f/(_u - _f), _u, oo), _f, oo).doit() |
|
if u is oo: |
|
return Limit(_u*focal_length/(_u - focal_length), _u, oo).doit() |
|
if focal_length is oo: |
|
return Limit(u*_f/(u - _f), _f, oo).doit() |
|
return u*focal_length/(u - focal_length) |
|
|
|
|
|
def lens_formula(focal_length=None, u=None, v=None): |
|
""" |
|
This function provides one of the three parameters |
|
when two of them are supplied. |
|
This is valid only for paraxial rays. |
|
|
|
Parameters |
|
========== |
|
|
|
focal_length : sympifiable |
|
Focal length of the mirror. |
|
u : sympifiable |
|
Distance of object from the optical center on |
|
the principal axis. |
|
v : sympifiable |
|
Distance of the image from the optical center |
|
on the principal axis. |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.physics.optics import lens_formula |
|
>>> from sympy.abc import f, u, v |
|
>>> lens_formula(focal_length=f, u=u) |
|
f*u/(f + u) |
|
>>> lens_formula(focal_length=f, v=v) |
|
f*v/(f - v) |
|
>>> lens_formula(u=u, v=v) |
|
u*v/(u - v) |
|
|
|
""" |
|
if focal_length and u and v: |
|
raise ValueError("Please provide only two parameters") |
|
|
|
focal_length = sympify(focal_length) |
|
u = sympify(u) |
|
v = sympify(v) |
|
if u is oo: |
|
_u = Symbol('u') |
|
if v is oo: |
|
_v = Symbol('v') |
|
if focal_length is oo: |
|
_f = Symbol('f') |
|
if focal_length is None: |
|
if u is oo and v is oo: |
|
return Limit(Limit(_v*_u/(_u - _v), _u, oo), _v, oo).doit() |
|
if u is oo: |
|
return Limit(v*_u/(_u - v), _u, oo).doit() |
|
if v is oo: |
|
return Limit(_v*u/(u - _v), _v, oo).doit() |
|
return v*u/(u - v) |
|
if u is None: |
|
if v is oo and focal_length is oo: |
|
return Limit(Limit(_v*_f/(_f - _v), _v, oo), _f, oo).doit() |
|
if v is oo: |
|
return Limit(_v*focal_length/(focal_length - _v), _v, oo).doit() |
|
if focal_length is oo: |
|
return Limit(v*_f/(_f - v), _f, oo).doit() |
|
return v*focal_length/(focal_length - v) |
|
if v is None: |
|
if u is oo and focal_length is oo: |
|
return Limit(Limit(_u*_f/(_u + _f), _u, oo), _f, oo).doit() |
|
if u is oo: |
|
return Limit(_u*focal_length/(_u + focal_length), _u, oo).doit() |
|
if focal_length is oo: |
|
return Limit(u*_f/(u + _f), _f, oo).doit() |
|
return u*focal_length/(u + focal_length) |
|
|
|
def hyperfocal_distance(f, N, c): |
|
""" |
|
|
|
Parameters |
|
========== |
|
|
|
f: sympifiable |
|
Focal length of a given lens. |
|
|
|
N: sympifiable |
|
F-number of a given lens. |
|
|
|
c: sympifiable |
|
Circle of Confusion (CoC) of a given image format. |
|
|
|
Example |
|
======= |
|
|
|
>>> from sympy.physics.optics import hyperfocal_distance |
|
>>> round(hyperfocal_distance(f = 0.5, N = 8, c = 0.0033), 2) |
|
9.47 |
|
""" |
|
|
|
f = sympify(f) |
|
N = sympify(N) |
|
c = sympify(c) |
|
|
|
return (1/(N * c))*(f**2) |
|
|
|
def transverse_magnification(si, so): |
|
""" |
|
|
|
Calculates the transverse magnification upon reflection in a mirror, |
|
which is the ratio of the image size to the object size. |
|
|
|
Parameters |
|
========== |
|
|
|
so: sympifiable |
|
Lens-object distance. |
|
|
|
si: sympifiable |
|
Lens-image distance. |
|
|
|
Example |
|
======= |
|
|
|
>>> from sympy.physics.optics import transverse_magnification |
|
>>> transverse_magnification(30, 15) |
|
-2 |
|
|
|
""" |
|
|
|
si = sympify(si) |
|
so = sympify(so) |
|
|
|
return (-(si/so)) |
|
|