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
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 ] )
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
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)
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)
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)
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)
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)
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
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)
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
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
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]) ])