def test_issue_15129_trigsimp_methods(): t1 = Matrix([sin(Rational(1, 50)), cos(Rational(1, 50)), 0]) t2 = Matrix([sin(Rational(1, 25)), cos(Rational(1, 25)), 0]) t3 = Matrix([cos(Rational(1, 25)), sin(Rational(1, 25)), 0]) r1 = t1.dot(t2) r2 = t1.dot(t3) assert trigsimp(r1) == cos(Rational(1, 50)) assert trigsimp(r2) == sin(Rational(3, 50))
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) # check if an incidence angle was supplied instead of a ray 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) # Treat the incident as ray below # A flag to check whether to return Ray3D or not 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 provided, get direction ratios of the normal # to the plane from the plane else go with `normal` param. if plane is not None: if not isinstance(plane, Plane): raise TypeError( "plane should be an instance of geometry.plane.Plane") # If we have the plane, we can get the intersection # point of incident ray and the plane and thus return # an instance of Ray3D. 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 # Relative index of refraction # Calculating magnitude of the vectors mag_incident = sqrt(sum([i**2 for i in _incident])) mag_normal = sqrt(sum([i**2 for i in _normal])) # Converting vectors to unit vectors by dividing # them with their magnitudes _incident /= mag_incident _normal /= mag_normal c1 = -_incident.dot(_normal) # cos(angle_of_incidence) cs2 = 1 - eta**2 * (1 - c1**2) # cos(angle_of_refraction)**2 if cs2.is_negative: # This is the case of total internal reflection(TIR). return S.Zero drs = eta * _incident + (eta * c1 - sqrt(cs2)) * _normal # Multiplying unit vector by its magnitude drs = drs * mag_incident if not return_ray: return drs else: return Ray3D(intersection_pt, direction_ratio=drs)
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