예제 #1
0
def test_find_container_embedded_scene():
    from pvtrace.algorithm.photon_tracer import find_container
    scene, world, box = make_embedded_scene()
    ray = Ray(
        position=(0.0, 0.0, -1.0),
        direction=(0.0, 0.0, 1.0),
        wavelength=555.0,
        is_alive=True
    )
    intersections = scene.intersections(ray.position, ray.direction)
    points, nodes = zip(*[(x.point, x.hit) for x in intersections])
    container = find_container(ray, nodes)
    assert container == world

    ray = Ray(
        position=(0.0, 0.0, -0.4),
        direction=(0.0, 0.0, 1.0),
        wavelength=555.0,
        is_alive=True
    )
    intersections = scene.intersections(ray.position, ray.direction)
    points, nodes = zip(*[(x.point, x.hit) for x in intersections])
    container = find_container(ray, nodes)
    assert container == box
    
    ray = Ray(
        position=(0.0, 0.0, 0.6),
        direction=(0.0, 0.0, 1.0),
        wavelength=555.0,
        is_alive=True
    )
    intersections = scene.intersections(ray.position, ray.direction)
    points, nodes = zip(*[(x.point, x.hit) for x in intersections])
    status = find_container(ray, nodes)
    assert status == world
예제 #2
0
def test_ray_status_embedded_scene():
    from pvtrace.algorithm.photon_tracer import ray_status
    ray = Ray(
        position=(0.0, 0.0, -1.0),
        direction=(0.0, 0.0, 1.0),
        wavelength=555.0,
        is_alive=True
    )
    scene, world, box = make_embedded_scene()
    intersections = scene.intersections(ray.position, ray.direction)
    points, nodes = zip(*[(x.point, x.hit) for x in intersections])
    container, to_node, surface_node = ray_status(ray, points, nodes)
    assert all([
        container == world,
        to_node == box,
        surface_node == box
        ]
    )
    
    ray = Ray(
        position=(0.0, 0.0, -0.4),
        direction=(0.0, 0.0, 1.0),
        wavelength=555.0,
        is_alive=True
    )
    intersections = scene.intersections(ray.position, ray.direction)
    points, nodes = zip(*[(x.point, x.hit) for x in intersections])
    container, to_node, surface_node = ray_status(ray, points, nodes)
    assert all([
        container == box,
        to_node == world,
        surface_node == box
        ]
    )
    
    ray = Ray(
        position=(0.0, 0.0, 0.6),
        direction=(0.0, 0.0, 1.0),
        wavelength=555.0,
        is_alive=True
    )
    intersections = scene.intersections(ray.position, ray.direction)
    points, nodes = zip(*[(x.point, x.hit) for x in intersections])
    container, to_node, surface_node = ray_status(ray, points, nodes)
    assert all([
        container == world,
        to_node == None,
        surface_node == world
        ]
    )
예제 #3
0
파일: light.py 프로젝트: SgtChrome/pvtrace
 def emit(self, num_rays=None) -> Iterator[Ray]:
     """ Returns a ray with wavelength, position and divergence sampled from the
         delegates.
         
         Parameters
         ----------
         num_rays : int of None
             The maximum number of rays this light source will generate. If set to
         None then the light will generate until manually terminated.
     """
     count = 0
     while True:
         count += 1
         if num_rays is not None and count > num_rays:
             break
         nanometers = self.wavelength_delegate()
         position = self.position_delegate()
         direction = (0.0, 0.0, 1.0)
         divergence = self.divergence_delegate()
         if not np.allclose((0.0, 0.0), divergence):
             (theta, phi) = divergence
             x = np.sin(phi) * np.cos(theta)
             y = np.sin(phi) * np.sin(theta)
             z = np.cos(phi)
             direction = (x, y, z)
         ray = Ray(wavelength=nanometers,
                   position=position,
                   direction=direction,
                   is_alive=True)
         yield ray
예제 #4
0
def test_follow_lossy_embedded_scene_1():
    
    ray = Ray(
        position=(0.0, 0.0, -1.0),
        direction=(0.0, 0.0, 1.0),
        wavelength=555.0,
        is_alive=True
    )
    scene, world, box = make_embedded_lossy_scene()
    np.random.seed(0)
    path = follow(ray, scene)
    path, decisions = zip(*path)
    positions = [x.position for x in path]
    expected_positions = [
        (0.00, 0.00, -1.00), # Starting
        (0.00, 0.00, -0.50), # Hit box
        (0.00, 0.00, -0.50), # Reflection
        (0.00, 0.00, -0.3744069237034118), # Absorbed
    ]
    expected_decisions = [
        Decision.EMIT,
        Decision.TRAVEL,
        Decision.TRANSIT,
        Decision.ABSORB,
    ]
    for expected_point, point, expected_decision, decision in zip(
        expected_positions, positions, expected_decisions, decisions):
        assert expected_decision == decision
        assert np.allclose(expected_point, point, atol=EPS_ZERO)
예제 #5
0
    def probability(self, ray: Ray) -> float:
        """ Returns the probability that the interaction will occur.
        """
        context = self.context
        normal = np.array(context.normal)
        n1 = context.n1
        n2 = context.n2
        
        # Be flexible with how the normal is defined
        ray_ = ray.representation(context.normal_node.root, context.normal_node)
        if np.dot(normal, ray_.direction) < 0.0:
            normal = flip(normal)
        angle = angle_between(normal, np.array(ray_.direction))
        logger.debug("Incident angle {:.2f}".format(np.degrees(angle)))
        if angle < 0.0 or angle > 0.5 * np.pi:
            raise ValueError("The incident angle must be between 0 and pi/2.")

        # Catch TIR case
        if n2 < n1 and angle > np.arcsin(n2/n1):
            return 1.0
        c = np.cos(angle)
        s = np.sin(angle)
        k = np.sqrt(1 - (n1/n2 * s)**2)
        Rs1 = n1 * c - n2 * k
        Rs2 = n1 * c + n2 * k
        Rs = (Rs1/Rs2)**2
        Rp1 = n1 * k - n2 * c
        Rp2 = n1 * k + n2 * c
        Rp = (Rp1/Rp2)**2
        return 0.5 * (Rs + Rp)
예제 #6
0
def test_follow_embedded_scene_2():
    
    ray = Ray(
        position=(0.0, 0.0, -1.0),
        direction=(0.0, 0.0, 1.0),
        wavelength=555.0,
        is_alive=True
    )
    scene, world, box = make_embedded_scene(n1=100.0)
    np.random.seed(0)
    path = follow(ray, scene)
    path, decisions = zip(*path)
    positions = [x.position for x in path]
    expected_positions = [
        (0.00, 0.00, -1.00), # Starting
        (0.00, 0.00, -0.50), # Hit box
        (0.00, 0.00, -0.50), # Reflection
        (0.00, 0.00, -10.0),   # Hit world node
        (0.00, 0.00, -10.0)   # Ray killed
    ]
    expected_decisions = [
        Decision.EMIT,
        Decision.TRAVEL,
        Decision.RETURN,
        Decision.TRAVEL,
        Decision.KILL
    ]
    for expected_point, point, expected_decision, decision in zip(expected_positions, positions, expected_decisions, decisions):
        assert expected_decision == decision
        assert np.allclose(expected_point, point, atol=EPS_ZERO)
예제 #7
0
 def transform(self, ray: Ray, context: dict) -> Ray:
     """ Transform ray according to the physics of the interaction. An absorption
         event occurred at the path length along rays trajectory.
     """
     _check_required_keys(set(["distance"]), context)
     distance = context["distance"]
     new_ray = ray.propagate(distance)
     return new_ray
예제 #8
0
 def transform(self, ray: Ray) -> Ray:
     """ Transform ray according to the physics of the interaction. An absorption
         event occurred at the path length along rays trajectory.
     """
     if not isinstance(self._path_length, float):
         raise AppError("Path length has not yet be calculated.")
     if self._interaction_material is not None:
         raise AppError("Cannot reuse mechanisms. Interaction material already calculated.")
     material = self.context.container.geometry.material
     self._interaction_material = material.get_interaction_material(ray.wavelength)
     new_ray = ray.propagate(self._path_length)
     return new_ray
예제 #9
0
 def test_normal_reflection(self):
     n1 = 1.0
     n2 = 1.5
     normal = (0.0, 0.0, 1.0)
     angle = 0.0
     ray = Ray(position=(0.0, 0.0, 0.0),
               direction=(0.0, 0.0, 1.0),
               wavelength=None)
     fresnel = FresnelReflection()
     assert np.isclose(fresnel.reflectivity(angle, n1, n2), 0.04)
     new_ray = fresnel.transform(ray, {"normal": normal})
     assert np.allclose(flip(ray.direction), new_ray.direction)
예제 #10
0
 def transform(self, ray: Ray) -> Ray:
     """ Transform ray according to the physics of the interaction.
     """
     context = self.context
     normal = np.array(context.normal)
     ray_ = ray.representation(context.normal_node.root, context.normal_node)
     vec = np.array(ray_.direction)
     d = np.dot(normal, vec)
     reflected_direction = vec - 2 * d * normal
     new_ray_ = replace(ray_, direction=tuple(reflected_direction.tolist()))
     new_ray = new_ray_.representation(context.normal_node, context.normal_node.root)
     return new_ray  # back to world node
예제 #11
0
def test_touching_scene_intersections():
    print("test_touching_scene_intersections")
    ray = Ray(
        position=(0.0, 0.0, -1.0),
        direction=(0.0, 0.0, 1.0),
        wavelength=555.0,
        is_alive=True
    )
    scene, world, box1, box2, box3 = make_touching_scene()
    intersections = scene.intersections(ray.position, ray.direction)
    points, nodes = zip(*[(x.point, x.hit) for x in intersections])
    assert nodes == (box1, box1, box2, box2, box3, box3, world)
예제 #12
0
 def test_antinormal_reflection(self):
     """ FresnelReflection takes the smallest angle between the ray direction and 
     the normal. Thus the flipped normal will also work.
     """
     n1 = 1.0
     n2 = 1.5
     normal = (0.0, 0.0, -1.0)
     angle = 0.0
     ray = Ray(position=(0.0, 0.0, 0.0),
               direction=(0.0, 0.0, 1.0),
               wavelength=None)
     fresnel = FresnelReflection()
     assert np.isclose(fresnel.reflectivity(angle, n1, n2), 0.04)
     new_ray = fresnel.transform(ray, {"normal": normal})
     assert np.allclose(flip(ray.direction), new_ray.direction)
예제 #13
0
 def test_normal_reflection(self):
     n1 = 1.0
     n2 = 1.5
     normal = (0.0, 0.0, 1.0)
     angle = 0.0
     ray = Ray(position=(0.0, 0.0, 0.0),
               direction=(0.0, 0.0, 1.0),
               wavelength=None)
     fresnel = FresnelRefraction()
     new_ray = fresnel.transform(ray, {
         "n1": n1,
         "n2": n2,
         "normal": normal
     })
     assert np.allclose(ray.direction, new_ray.direction)
예제 #14
0
 def test_intersection_coordinate_system(self):
     root = Node(name="Root", geometry=Sphere(radius=10.0))
     a = Node(name="A", parent=root, geometry=Sphere(radius=1.0))
     a.translate((1.0, 0.0, 0.0))
     scene = Scene(root)
     initial_ray = Ray(
         position=(-2.0, 0.0, 0.0),
         direction=(1.0, 0.0, 0.0),
         wavelength=None,
         is_alive=True,
     )
     scene_intersections = scene.intersections(initial_ray.position,
                                               initial_ray.direction)
     a_intersections = tuple(map(lambda x: x.to(root), scene_intersections))
     assert scene_intersections == a_intersections
예제 #15
0
def test_follow_touching_scene():
    
    ray = Ray(
        position=(0.0, 0.0, -1.0),
        direction=(0.0, 0.0, 1.0),
        wavelength=555.0,
        is_alive=True
    )
    scene, world, box1, box2, box3 = make_touching_scene()
    np.random.seed(0)
    path = follow(ray, scene)
    path, decisions = zip(*path)
    positions = [x.position for x in path]
    print(decisions)
    expected_positions = [
        (0.00, 0.00, -1.00), # Starting
        (0.00, 0.00, -0.50), # Hit box
        (0.00, 0.00, -0.50), # Refraction into box
        (0.00, 0.00,  0.50), # Hit box 
        (0.00, 0.00,  0.50), # Refraction
        (0.00, 0.00,  1.50), # Hit box 
        (0.00, 0.00,  1.50), # Refraction
        (0.00, 0.00,  2.50), # Hit box 
        (0.00, 0.00,  2.50), # Refraction
        (0.00, 0.00, 10.0),  # Hit world node
        (0.00, 0.00, 10.0)   # Kill
    ]
    expected_decisions = [
        Decision.EMIT,
        Decision.TRAVEL,
        Decision.TRANSIT,
        Decision.TRAVEL,
        Decision.TRANSIT,
        Decision.TRAVEL,
        Decision.TRANSIT,
        Decision.TRAVEL,
        Decision.TRANSIT,
        Decision.TRAVEL,
        Decision.KILL
    ]
    for expected_point, point, expected_decision, decision in zip(
        expected_positions, positions, expected_decisions, decisions):
        assert np.allclose(expected_point, point, atol=EPS_ZERO)
        assert expected_decision == decision
예제 #16
0
 def transform(self, ray: Ray) -> Ray:
     """ Transform ray according to the physics of the interaction.
     """
     context = self.context
     n1 = context.n1
     n2 = context.n2
     ray_ = ray.representation(context.normal_node.root, context.normal_node)
     normal = np.array(context.normal)
     vector = np.array(ray_.direction)
     n = n1/n2
     dot = np.dot(vector, normal)
     c = np.sqrt(1 - n**2 * (1 - dot**2))
     sign = 1
     if dot < 0.0:
         sign = -1
     refracted_direction = n * vector + sign*(c - sign*n*dot) * normal
     new_ray_ = replace(ray_, direction=tuple(refracted_direction.tolist()))
     new_ray = new_ray_.representation(context.normal_node, context.normal_node.root)
     return new_ray
예제 #17
0
 def emit(self, num_rays=None) -> Iterator[Ray]:
     """ Returns a ray with wavelength, position and divergence sampled from the
         delegates.
         
         Parameters
         ----------
         num_rays : int of None
             The maximum number of rays this light source will generate. If set to
         None then the light will generate until manually terminated.
     """
     if num_rays is None or num_rays == 0:
         return
     count = 0
     while True:
         count += 1
         if num_rays is not None and count > num_rays:
             break
         ray = Ray(wavelength=self.wavelength(),
                   position=self.position(),
                   direction=self.direction(),
                   is_alive=True,
                   source=self)
         yield ray
예제 #18
0
def test_follow_embedded_lumophore_scene_1():
    
    ray = Ray(
        position=(0.0, 0.0, -1.0),
        direction=(0.0, 0.0, 1.0),
        wavelength=555.0,
        is_alive=True
    )
    scene, world, box = make_embedded_lumophore_scene()
    np.random.seed(0)
    path = follow(ray, scene)
    path, decisions = zip(*path)
    positions = [x.position for x in path]
    # First two are before box
    expected_positions = [
        (0.00, 0.00, -1.00), # Starting
        (0.00, 0.00, -0.50), # Refraction into box
    ]
    assert len(expected_positions) < len(positions[:-1])
    print("Expected: {}".format(expected_positions))
    assert all([
        np.allclose(expected, actual, atol=EPS_ZERO)
        for (expected, actual) in zip(expected_positions, positions[0:2])
    ])