From c5e5c579031b3d06942c8cc4ef0aa336acda1690 Mon Sep 17 00:00:00 2001 From: Laura Sandoval Date: Thu, 5 Sep 2024 14:56:22 -0600 Subject: [PATCH 01/12] add multiple segments --- imap_processing/spice/kernels.py | 186 ++++++++++++++----------------- 1 file changed, 82 insertions(+), 104 deletions(-) diff --git a/imap_processing/spice/kernels.py b/imap_processing/spice/kernels.py index 0a3f18493..2d60ed6f5 100644 --- a/imap_processing/spice/kernels.py +++ b/imap_processing/spice/kernels.py @@ -173,7 +173,7 @@ def wrapper_ensure_spice(*args: Any, **kwargs: Any) -> Any: @contextmanager -def spice_ck_file(pointing_frame_path: str) -> Generator[int, None, None]: +def spice_ck_file(pointing_frame_path: Path) -> Generator[int, None, None]: """ Context manager for handling SPICE CK files. @@ -187,7 +187,10 @@ def spice_ck_file(pointing_frame_path: str) -> Generator[int, None, None]: handle : int Handle to the opened CK file. """ - handle = spice.ckopn(pointing_frame_path, "CK", 0) + if pointing_frame_path.exists(): + handle = spice.dafopw(str(pointing_frame_path)) + else: + handle = spice.ckopn(str(pointing_frame_path), "CK", 0) try: yield handle finally: @@ -195,7 +198,7 @@ def spice_ck_file(pointing_frame_path: str) -> Generator[int, None, None]: @ensure_spice -def create_pointing_frame(pointing_frame_path: Optional[Path] = None) -> Path: +def create_pointing_frame(pointing_frame_path: Path) -> None: """ Create the pointing frame. @@ -204,116 +207,91 @@ def create_pointing_frame(pointing_frame_path: Optional[Path] = None) -> Path: pointing_frame_path : Path Directory of where pointing frame will be saved. - Returns - ------- - pointing_frame_path : Path - Path to pointing frame. - References ---------- https://numpydoc.readthedocs.io/en/latest/format.html#references """ + # Get the CK kernel directory. ck_kernel, _, _, _ = spice.kdata(0, "ck") - # Get timerange for the pointing frame kernel. - et_start, et_end, et_times = _get_et_times(ck_kernel) - # Create a rotation matrix - rotation_matrix = _create_rotation_matrix(et_times) - - # Convert the rotation matrix to a quaternion. - # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.m2q - q_avg = spice.m2q(rotation_matrix) - - # TODO: come up with naming convention. - if pointing_frame_path is None: - pointing_frame_path = Path(ck_kernel).parent / "imap_dps.bc" - - # Open a new CK file, returning the handle of the opened file. - # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.ckopn - with spice_ck_file(str(pointing_frame_path)) as handle: - # Get the SCLK ID. - # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.gipool - id_imap_sclk = spice.gipool("CK_-43000_SCLK", 0, 1) - # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.sce2c - # Convert start and end times to SCLK. - sclk_begtim = spice.sce2c(int(id_imap_sclk), et_start) - sclk_endtim = spice.sce2c(int(id_imap_sclk), et_end) - - # Get the pointing frame ID. - # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.gipool - id_imap_dps = spice.gipool("FRAME_IMAP_DPS", 0, 1) - # TODO: Figure out how to write new pointings to same CK kernel. - # Create the pointing frame kernel. - # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.ckw02 - spice.ckw02( - # Handle of an open CK file. - handle, - # Start time of the segment. - sclk_begtim, - # End time of the segment. - sclk_endtim, - # Pointing frame ID. - int(id_imap_dps), - # Reference frame. - "ECLIPJ2000", # Reference frame - # Identifier. - "IMAP_DPS", - # Number of pointing intervals. - 1, - # Start times of individual pointing records within segment. - # Since there is only a single record this is equal to sclk_begtim. - np.array([sclk_begtim]), - # End times of individual pointing records within segment. - # Since there is only a single record this is equal to sclk_endtim. - np.array([sclk_endtim]), # Single stop time - # Average quaternion. - q_avg, - # 0.0 Angular rotation terms. - np.array([0.0, 0.0, 0.0]), - # Rates (seconds per tick) at which the quaternion and - # angular velocity change. - np.array([1.0]), - ) - - return pointing_frame_path - - -@ensure_spice -def _get_et_times(ck_kernel: str) -> tuple[float, float, np.ndarray]: - """ - Get times for pointing start and stop. - - Parameters - ---------- - ck_kernel : str - Path of ck_kernel used to create the pointing frame. - - Returns - ------- - et_start : float - Pointing start time. - et_end : float - Pointing end time. - et_times : numpy.ndarray - Array of times between et_start and et_end. - """ - # Get the spacecraft ID. + # Get the pointing frame ID. # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.gipool - id_imap_spacecraft = spice.gipool("FRAME_IMAP_SPACECRAFT", 0, 1) - - # TODO: Queried pointing start and stop times here. - # TODO removing the @ensure_spice decorator when using the repointing table. + id_imap_dps = spice.gipool("FRAME_IMAP_DPS", 0, 1) - # Get the coverage window - # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.ckcov - cover = spice.ckcov(ck_kernel, int(id_imap_spacecraft), True, "SEGMENT", 0, "TDB") - # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.wnfetd - et_start, et_end = spice.wnfetd(cover, 0) - # 1 spin/15 seconds; 10 quaternions / spin - num_samples = (et_end - et_start) / 15 * 10 - et_times = np.linspace(et_start, et_end, int(num_samples)) + # Get the SCLK ID. + # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.gipool + id_imap_sclk = spice.gipool("CK_-43000_SCLK", 0, 1) - return et_start, et_end, et_times + # If the pointing frame kernel already exists, find the last time. + if pointing_frame_path.exists(): + # Get the last time in the pointing frame kernel. + cover = spice.ckcov( + str(pointing_frame_path), int(id_imap_dps), True, "SEGMENT", 0, "TDB" + ) + num_segments = spice.wncard(cover) + _, et_end_pointing_frame = spice.wnfetd(cover, num_segments - 1) + + # TODO: Query for .csv file to get the pointing start and end times. + # TODO: Remove next two lines. + cover = spice.ckcov(ck_kernel, -43000, True, "INTERVAL", 0, "TDB") + num_intervals = spice.wncard(cover) + + with spice_ck_file(pointing_frame_path) as handle: + # TODO: this will change to the number of pointings. + for i in range(num_intervals): + et_start, et_end = spice.wnfetd(cover, i) + + # TODO: remove after query is added. + if et_start < et_end_pointing_frame: + break + + # Get times after the last pointing frame time. + num_samples = (et_end - et_start) / 15 * 10 + et_times = np.linspace(et_start, et_end, int(num_samples)) + + # Create a rotation matrix + rotation_matrix = _create_rotation_matrix(et_times) + + # Convert the rotation matrix to a quaternion. + # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.m2q + q_avg = spice.m2q(rotation_matrix) + + # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.sce2c + # Convert start and end times to SCLK. + sclk_begtim = spice.sce2c(int(id_imap_sclk), et_start) + sclk_endtim = spice.sce2c(int(id_imap_sclk), et_end) + + # Create the pointing frame kernel. + # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.ckw02 + spice.ckw02( + # Handle of an open CK file. + handle, + # Start time of the segment. + sclk_begtim, + # End time of the segment. + sclk_endtim, + # Pointing frame ID. + int(id_imap_dps), + # Reference frame. + "ECLIPJ2000", # Reference frame + # Identifier. + "IMAP_DPS", + # Number of pointing intervals. + 1, + # Start times of individual pointing records within segment. + # Since there is only a single record this is equal to sclk_begtim. + np.array([sclk_begtim]), + # End times of individual pointing records within segment. + # Since there is only a single record this is equal to sclk_endtim. + np.array([sclk_endtim]), # Single stop time + # Average quaternion. + q_avg, + # 0.0 Angular rotation terms. + np.array([0.0, 0.0, 0.0]), + # Rates (seconds per tick) at which the quaternion and + # angular velocity change. + np.array([1.0]), + ) @ensure_spice From ded073328fe765953a8f64a39ba7900fa02498da Mon Sep 17 00:00:00 2001 From: Laura Sandoval Date: Thu, 5 Sep 2024 16:23:10 -0600 Subject: [PATCH 02/12] update floor and ceil --- imap_processing/spice/kernels.py | 86 +++++++++++++++++++++++++------- 1 file changed, 67 insertions(+), 19 deletions(-) diff --git a/imap_processing/spice/kernels.py b/imap_processing/spice/kernels.py index 2d60ed6f5..f1f758a11 100644 --- a/imap_processing/spice/kernels.py +++ b/imap_processing/spice/kernels.py @@ -198,7 +198,7 @@ def spice_ck_file(pointing_frame_path: Path) -> Generator[int, None, None]: @ensure_spice -def create_pointing_frame(pointing_frame_path: Path) -> None: +def create_pointing_frame(pointing_frame_path: Path) -> Path: """ Create the pointing frame. @@ -207,47 +207,55 @@ def create_pointing_frame(pointing_frame_path: Path) -> None: pointing_frame_path : Path Directory of where pointing frame will be saved. + Returns + ------- + pointing_frame_path : Path + Path to pointing frame. + References ---------- https://numpydoc.readthedocs.io/en/latest/format.html#references """ - # Get the CK kernel directory. - ck_kernel, _, _, _ = spice.kdata(0, "ck") - - # Get the pointing frame ID. + # Get IDs. # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.gipool id_imap_dps = spice.gipool("FRAME_IMAP_DPS", 0, 1) - - # Get the SCLK ID. - # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.gipool id_imap_sclk = spice.gipool("CK_-43000_SCLK", 0, 1) # If the pointing frame kernel already exists, find the last time. if pointing_frame_path.exists(): # Get the last time in the pointing frame kernel. - cover = spice.ckcov( + pointing_cover = spice.ckcov( str(pointing_frame_path), int(id_imap_dps), True, "SEGMENT", 0, "TDB" ) - num_segments = spice.wncard(cover) - _, et_end_pointing_frame = spice.wnfetd(cover, num_segments - 1) + num_segments = spice.wncard(pointing_cover) + _, et_end_pointing_frame = spice.wnfetd(pointing_cover, num_segments - 1) + else: + et_end_pointing_frame = None # TODO: Query for .csv file to get the pointing start and end times. - # TODO: Remove next two lines. - cover = spice.ckcov(ck_kernel, -43000, True, "INTERVAL", 0, "TDB") - num_intervals = spice.wncard(cover) + # TODO: Remove next four lines once query is added. + ck_kernel, _, _, _ = spice.kdata(0, "ck") + id_imap_spacecraft = spice.gipool("FRAME_IMAP_SPACECRAFT", 0, 1) + ck_cover = spice.ckcov( + ck_kernel, int(id_imap_spacecraft), True, "INTERVAL", 0, "TDB" + ) + num_intervals = spice.wncard(ck_cover) with spice_ck_file(pointing_frame_path) as handle: # TODO: this will change to the number of pointings. - for i in range(num_intervals): - et_start, et_end = spice.wnfetd(cover, i) + for i in range(num_intervals - 1): + # TODO: this will change to pointing start and end time. + et_start, et_end = spice.wnfetd(ck_cover, i) # TODO: remove after query is added. - if et_start < et_end_pointing_frame: + if et_end_pointing_frame is not None and et_start < et_end_pointing_frame: break - # Get times after the last pointing frame time. + # 1 spin/15 seconds; 10 quaternions / spin. num_samples = (et_end - et_start) / 15 * 10 - et_times = np.linspace(et_start, et_end, int(num_samples)) + et_times = np.linspace( + np.ceil(et_start), np.floor(et_end), int(num_samples) + ) # Create a rotation matrix rotation_matrix = _create_rotation_matrix(et_times) @@ -293,6 +301,46 @@ def create_pointing_frame(pointing_frame_path: Path) -> None: np.array([1.0]), ) + return pointing_frame_path + + +@ensure_spice +def _get_et_times(ck_kernel: str) -> tuple[float, float, np.ndarray]: + """ + Get times for pointing start and stop. + + Parameters + ---------- + ck_kernel : str + Path of ck_kernel used to create the pointing frame. + + Returns + ------- + et_start : float + Pointing start time. + et_end : float + Pointing end time. + et_times : numpy.ndarray + Array of times between et_start and et_end. + """ + # Get the spacecraft ID. + # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.gipool + id_imap_spacecraft = spice.gipool("FRAME_IMAP_SPACECRAFT", 0, 1) + + # TODO: Queried pointing start and stop times here. + # TODO removing the @ensure_spice decorator when using the repointing table. + + # Get the coverage window + # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.ckcov + cover = spice.ckcov(ck_kernel, int(id_imap_spacecraft), True, "SEGMENT", 0, "TDB") + # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.wnfetd + et_start, et_end = spice.wnfetd(cover, 0) + # 1 spin/15 seconds; 10 quaternions / spin + num_samples = (et_end - et_start) / 15 * 10 + et_times = np.linspace(et_start, et_end, int(num_samples)) + + return et_start, et_end, et_times + @ensure_spice def _average_quaternions(et_times: np.ndarray) -> NDArray: From acab234645f37f1216ea6fe5319ff511f57dde9c Mon Sep 17 00:00:00 2001 From: Laura Sandoval Date: Fri, 6 Sep 2024 09:05:08 -0600 Subject: [PATCH 03/12] update tests --- imap_processing/spice/kernels.py | 58 ++++++++------------ imap_processing/tests/spice/test_kernels.py | 60 +++++++++++++++++---- 2 files changed, 70 insertions(+), 48 deletions(-) diff --git a/imap_processing/spice/kernels.py b/imap_processing/spice/kernels.py index f1f758a11..b6d744312 100644 --- a/imap_processing/spice/kernels.py +++ b/imap_processing/spice/kernels.py @@ -198,7 +198,7 @@ def spice_ck_file(pointing_frame_path: Path) -> Generator[int, None, None]: @ensure_spice -def create_pointing_frame(pointing_frame_path: Path) -> Path: +def create_pointing_frame(pointing_frame_path: Path): """ Create the pointing frame. @@ -207,11 +207,6 @@ def create_pointing_frame(pointing_frame_path: Path) -> Path: pointing_frame_path : Path Directory of where pointing frame will be saved. - Returns - ------- - pointing_frame_path : Path - Path to pointing frame. - References ---------- https://numpydoc.readthedocs.io/en/latest/format.html#references @@ -243,20 +238,19 @@ def create_pointing_frame(pointing_frame_path: Path) -> Path: with spice_ck_file(pointing_frame_path) as handle: # TODO: this will change to the number of pointings. - for i in range(num_intervals - 1): + for i in range(num_intervals): + # Get the coverage window # TODO: this will change to pointing start and end time. et_start, et_end = spice.wnfetd(ck_cover, i) + et_times = _get_et_times(et_start, et_end) # TODO: remove after query is added. - if et_end_pointing_frame is not None and et_start < et_end_pointing_frame: + if ( + et_end_pointing_frame is not None + and et_times[0] < et_end_pointing_frame + ): break - # 1 spin/15 seconds; 10 quaternions / spin. - num_samples = (et_end - et_start) / 15 * 10 - et_times = np.linspace( - np.ceil(et_start), np.floor(et_end), int(num_samples) - ) - # Create a rotation matrix rotation_matrix = _create_rotation_matrix(et_times) @@ -266,8 +260,8 @@ def create_pointing_frame(pointing_frame_path: Path) -> Path: # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.sce2c # Convert start and end times to SCLK. - sclk_begtim = spice.sce2c(int(id_imap_sclk), et_start) - sclk_endtim = spice.sce2c(int(id_imap_sclk), et_end) + sclk_begtim = spice.sce2c(int(id_imap_sclk), et_times[0]) + sclk_endtim = spice.sce2c(int(id_imap_sclk), et_times[-1]) # Create the pointing frame kernel. # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.ckw02 @@ -301,45 +295,35 @@ def create_pointing_frame(pointing_frame_path: Path) -> Path: np.array([1.0]), ) - return pointing_frame_path - -@ensure_spice -def _get_et_times(ck_kernel: str) -> tuple[float, float, np.ndarray]: +def _get_et_times(et_start, et_end) -> float: """ Get times for pointing start and stop. Parameters ---------- - ck_kernel : str - Path of ck_kernel used to create the pointing frame. - - Returns - ------- et_start : float Pointing start time. et_end : float Pointing end time. + + Returns + ------- et_times : numpy.ndarray Array of times between et_start and et_end. """ - # Get the spacecraft ID. - # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.gipool - id_imap_spacecraft = spice.gipool("FRAME_IMAP_SPACECRAFT", 0, 1) - # TODO: Queried pointing start and stop times here. # TODO removing the @ensure_spice decorator when using the repointing table. - # Get the coverage window - # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.ckcov - cover = spice.ckcov(ck_kernel, int(id_imap_spacecraft), True, "SEGMENT", 0, "TDB") - # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.wnfetd - et_start, et_end = spice.wnfetd(cover, 0) - # 1 spin/15 seconds; 10 quaternions / spin + # 1 spin/15 seconds; 10 quaternions / spin. num_samples = (et_end - et_start) / 15 * 10 - et_times = np.linspace(et_start, et_end, int(num_samples)) + # There were rounding errors when using spice.pxform so np.ceil and np.floor + # were used to ensure the start and end times were included in the array. + et_times = np.linspace( + np.ceil(et_start * 1e6) / 1e6, np.floor(et_end * 1e6) / 1e6, int(num_samples) + ) - return et_start, et_end, et_times + return et_times @ensure_spice diff --git a/imap_processing/tests/spice/test_kernels.py b/imap_processing/tests/spice/test_kernels.py index 0edddeaba..347052fa3 100644 --- a/imap_processing/tests/spice/test_kernels.py +++ b/imap_processing/tests/spice/test_kernels.py @@ -28,13 +28,29 @@ def pointing_frame_kernels(spice_test_data_path): return kernels +@pytest.fixture() +def multiple_pointing_kernels(spice_test_data_path): + """List SPICE kernels.""" + required_kernels = [ + "imap_science_0001.tf", + "imap_sclk_0000.tsc", + "IMAP_spacecraft_attitude.bc", + "imap_wkcp.tf", + "naif0012.tls", + ] + kernels = [str(spice_test_data_path / kernel) for kernel in required_kernels] + return kernels + + @pytest.fixture() def et_times(pointing_frame_kernels): """Tests get_et_times function.""" spice.furnsh(pointing_frame_kernels) - file, _, _, _ = spice.kdata(0, "ck") - et_start, et_end, et_times = _get_et_times(file) + ck_kernel, _, _, _ = spice.kdata(0, "ck") + ck_cover = spice.ckcov(ck_kernel, -43000, True, "INTERVAL", 0, "TDB") + et_start, et_end = spice.wnfetd(ck_cover, 0) + et_times = _get_et_times(et_start, et_end) return et_times @@ -127,19 +143,19 @@ def test_create_rotation_matrix(et_times, pointing_frame_kernels): np.testing.assert_allclose(rotation_matrix, rotation_matrix_expected, atol=1e-4) -def test_create_pointing_frame(spice_test_data_path, pointing_frame_kernels, tmp_path): +def test_create_pointing_frame( + spice_test_data_path, pointing_frame_kernels, tmp_path, et_times +): """Tests create_pointing_frame function.""" spice.furnsh(pointing_frame_kernels) - ck_kernel, _, _, _ = spice.kdata(0, "ck") - et_start, et_end, et_times = _get_et_times(ck_kernel) create_pointing_frame(pointing_frame_path=tmp_path / "imap_dps.bc") # After imap_dps.bc has been created. dps_kernel = str(tmp_path / "imap_dps.bc") spice.furnsh(dps_kernel) - rotation_matrix_1 = spice.pxform("ECLIPJ2000", "IMAP_DPS", et_start + 100) - rotation_matrix_2 = spice.pxform("ECLIPJ2000", "IMAP_DPS", et_start + 1000) + rotation_matrix_1 = spice.pxform("ECLIPJ2000", "IMAP_DPS", et_times[0] + 100) + rotation_matrix_2 = spice.pxform("ECLIPJ2000", "IMAP_DPS", et_times[0] + 1000) # All the rotation matrices should be the same. assert np.array_equal(rotation_matrix_1, rotation_matrix_2) @@ -159,12 +175,34 @@ def test_et_times(pointing_frame_kernels): """Tests get_et_times function.""" spice.furnsh(pointing_frame_kernels) - file, _, _, _ = spice.kdata(0, "ck") - et_start, et_end, et_times = _get_et_times(file) + ck_kernel, _, _, _ = spice.kdata(0, "ck") + ck_cover = spice.ckcov(ck_kernel, -43000, True, "INTERVAL", 0, "TDB") + et_start, et_end = spice.wnfetd(ck_cover, 0) + et_times = _get_et_times(et_start, et_end) - assert et_start == 802008069.184905 - assert et_end == 802015267.184906 assert et_times[0] == et_start assert et_times[-1] == et_end return et_times + + +@ensure_spice +def test_multiple_pointing(pointing_frame_kernels, tmp_path): + """Tests create_pointing_frame function with multiple pointing kernels.""" + spice.furnsh(pointing_frame_kernels) + + # Check that a single segment is added regardless of how many times + # create_pointing_frame is called. + create_pointing_frame(pointing_frame_path=tmp_path / "imap_dps.bc") + ck_cover = spice.ckcov( + str(tmp_path / "imap_dps.bc"), -43901, True, "INTERVAL", 0, "TDB" + ) + num_intervals = spice.wncard(ck_cover) + assert num_intervals == 1 + + create_pointing_frame(pointing_frame_path=tmp_path / "imap_dps.bc") + ck_cover = spice.ckcov( + str(tmp_path / "imap_dps.bc"), -43901, True, "INTERVAL", 0, "TDB" + ) + num_intervals = spice.wncard(ck_cover) + assert num_intervals == 1 From 65529110d8aac06542cbbd138c22c847464153cb Mon Sep 17 00:00:00 2001 From: Laura Sandoval Date: Fri, 6 Sep 2024 09:19:14 -0600 Subject: [PATCH 04/12] update tests --- imap_processing/spice/kernels.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imap_processing/spice/kernels.py b/imap_processing/spice/kernels.py index b6d744312..0ddca688a 100644 --- a/imap_processing/spice/kernels.py +++ b/imap_processing/spice/kernels.py @@ -296,7 +296,7 @@ def create_pointing_frame(pointing_frame_path: Path): ) -def _get_et_times(et_start, et_end) -> float: +def _get_et_times(et_start: float, et_end: float) -> float: """ Get times for pointing start and stop. From 76b75770488d18a6b08002e632a8a2aa067a9eeb Mon Sep 17 00:00:00 2001 From: Laura Sandoval Date: Fri, 6 Sep 2024 09:20:33 -0600 Subject: [PATCH 05/12] update tests --- imap_processing/spice/kernels.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imap_processing/spice/kernels.py b/imap_processing/spice/kernels.py index 0ddca688a..71045b8ba 100644 --- a/imap_processing/spice/kernels.py +++ b/imap_processing/spice/kernels.py @@ -198,7 +198,7 @@ def spice_ck_file(pointing_frame_path: Path) -> Generator[int, None, None]: @ensure_spice -def create_pointing_frame(pointing_frame_path: Path): +def create_pointing_frame(pointing_frame_path: Path) -> None: """ Create the pointing frame. @@ -296,7 +296,7 @@ def create_pointing_frame(pointing_frame_path: Path): ) -def _get_et_times(et_start: float, et_end: float) -> float: +def _get_et_times(et_start: float, et_end: float) -> NDArray[np.float64]: """ Get times for pointing start and stop. From f8edfbcce63523ce5a6fb08c6248da3350d83802 Mon Sep 17 00:00:00 2001 From: Laura Sandoval Date: Fri, 6 Sep 2024 10:36:21 -0600 Subject: [PATCH 06/12] update tests --- imap_processing/tests/spice/test_kernels.py | 37 ++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/imap_processing/tests/spice/test_kernels.py b/imap_processing/tests/spice/test_kernels.py index 347052fa3..77ca017c7 100644 --- a/imap_processing/tests/spice/test_kernels.py +++ b/imap_processing/tests/spice/test_kernels.py @@ -187,7 +187,7 @@ def test_et_times(pointing_frame_kernels): @ensure_spice -def test_multiple_pointing(pointing_frame_kernels, tmp_path): +def test_multiple_attempts(pointing_frame_kernels, tmp_path): """Tests create_pointing_frame function with multiple pointing kernels.""" spice.furnsh(pointing_frame_kernels) @@ -206,3 +206,38 @@ def test_multiple_pointing(pointing_frame_kernels, tmp_path): ) num_intervals = spice.wncard(ck_cover) assert num_intervals == 1 + + +@ensure_spice +def test_multiple_pointings(multiple_pointing_kernels, spice_test_data_path): + """Tests create_pointing_frame function with multiple pointing kernels.""" + spice.furnsh(multiple_pointing_kernels) + + create_pointing_frame( + pointing_frame_path=spice_test_data_path / "imap_pointing_frame.bc" + ) + ck_cover_pointing = spice.ckcov( + str(spice_test_data_path / "imap_pointing_frame.bc"), + -43901, + True, + "INTERVAL", + 0, + "TDB", + ) + num_intervals = spice.wncard(ck_cover_pointing) + et_start_pointing, et_end_pointing = spice.wnfetd(ck_cover_pointing, 365) + + ck_cover = spice.ckcov( + str(spice_test_data_path / "IMAP_spacecraft_attitude.bc"), + -43000, + True, + "INTERVAL", + 0, + "TDB", + ) + num_intervals_expected = spice.wncard(ck_cover) + et_start_expected, et_end_expected = spice.wnfetd(ck_cover, 365) + + assert num_intervals == num_intervals_expected + assert et_start_pointing == et_start_expected + assert et_end_pointing == et_end_expected From 1c7425d216ba1336b9ed2b9820ab349490187c9f Mon Sep 17 00:00:00 2001 From: Laura Sandoval Date: Fri, 6 Sep 2024 12:01:51 -0600 Subject: [PATCH 07/12] update tests --- imap_processing/tests/spice/test_kernels.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/imap_processing/tests/spice/test_kernels.py b/imap_processing/tests/spice/test_kernels.py index 77ca017c7..cb406195d 100644 --- a/imap_processing/tests/spice/test_kernels.py +++ b/imap_processing/tests/spice/test_kernels.py @@ -241,3 +241,11 @@ def test_multiple_pointings(multiple_pointing_kernels, spice_test_data_path): assert num_intervals == num_intervals_expected assert et_start_pointing == et_start_expected assert et_end_pointing == et_end_expected + + et_times = _get_et_times(et_start_pointing, et_end_pointing) + + spice.furnsh(str(spice_test_data_path / "imap_pointing_frame.bc")) + rotation_matrix_1 = spice.pxform("ECLIPJ2000", "IMAP_DPS", et_times[100]) + rotation_matrix_2 = spice.pxform("ECLIPJ2000", "IMAP_DPS", et_times[1000]) + + assert np.array_equal(rotation_matrix_1, rotation_matrix_2) From 68ac053748eee14b1686df5fe9dfc2545ad72f20 Mon Sep 17 00:00:00 2001 From: Laura Sandoval Date: Fri, 6 Sep 2024 12:02:26 -0600 Subject: [PATCH 08/12] added test data --- .../spice/test_data/IMAP_spacecraft_attitude.bc | Bin 0 -> 56320 bytes .../spice/test_data/imap_pointing_frame.bc | Bin 0 -> 62464 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100755 imap_processing/tests/spice/test_data/IMAP_spacecraft_attitude.bc create mode 100644 imap_processing/tests/spice/test_data/imap_pointing_frame.bc diff --git a/imap_processing/tests/spice/test_data/IMAP_spacecraft_attitude.bc b/imap_processing/tests/spice/test_data/IMAP_spacecraft_attitude.bc new file mode 100755 index 0000000000000000000000000000000000000000..589f2f08f59374d34712393e5140265ff8a02bbb GIT binary patch literal 56320 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 Date: Tue, 10 Sep 2024 17:12:08 -0600 Subject: [PATCH 09/12] pr response --- imap_processing/spice/kernels.py | 38 +++++++++++++++++---- imap_processing/tests/spice/test_kernels.py | 26 +++++++++----- 2 files changed, 50 insertions(+), 14 deletions(-) diff --git a/imap_processing/spice/kernels.py b/imap_processing/spice/kernels.py index 71045b8ba..85b932450 100644 --- a/imap_processing/spice/kernels.py +++ b/imap_processing/spice/kernels.py @@ -173,7 +173,7 @@ def wrapper_ensure_spice(*args: Any, **kwargs: Any) -> Any: @contextmanager -def spice_ck_file(pointing_frame_path: Path) -> Generator[int, None, None]: +def open_spice_ck_file(pointing_frame_path: Path) -> Generator[int, None, None]: """ Context manager for handling SPICE CK files. @@ -198,24 +198,51 @@ def spice_ck_file(pointing_frame_path: Path) -> Generator[int, None, None]: @ensure_spice -def create_pointing_frame(pointing_frame_path: Path) -> None: +def create_pointing_frame(pointing_frame_path: Path, ck_path: Path) -> None: """ Create the pointing frame. Parameters ---------- pointing_frame_path : Path - Directory of where pointing frame will be saved. + Location of pointing frame kernel. + ck_path : Path + Location of the CK kernel. References ---------- https://numpydoc.readthedocs.io/en/latest/format.html#references + + Notes + ----- + Kernels required to be furnished: + "imap_science_0001.tf", + "imap_sclk_0000.tsc", + "imap_sim_ck_2hr_2secsampling_with_nutation.bc" or + "IMAP_spacecraft_attitude.bc", + "imap_wkcp.tf", + "naif0012.tls" + + Assumptions: + - The MOC has removed timeframe in which nutation/procession are present. + TODO: We may come back and have a check for this. + - We will continue to append to the pointing frame kernel. + TODO: Figure out how we want to handle the file size becoming too large. + - For now we can only furnish a single ck kernel. + TODO: This will not be the case once we add the ability to query the .csv. """ # Get IDs. # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.gipool id_imap_dps = spice.gipool("FRAME_IMAP_DPS", 0, 1) id_imap_sclk = spice.gipool("CK_-43000_SCLK", 0, 1) + # Verify that only ck_path kernel is loaded. + count = spice.ktotal("ck") + loaded_ck_kernel, _, _, _ = spice.kdata(count - 1, "ck") + + if count != 1 or str(ck_path) != loaded_ck_kernel: + raise ValueError(f"Error: Expected CK kernel {ck_path}") + # If the pointing frame kernel already exists, find the last time. if pointing_frame_path.exists(): # Get the last time in the pointing frame kernel. @@ -229,14 +256,13 @@ def create_pointing_frame(pointing_frame_path: Path) -> None: # TODO: Query for .csv file to get the pointing start and end times. # TODO: Remove next four lines once query is added. - ck_kernel, _, _, _ = spice.kdata(0, "ck") id_imap_spacecraft = spice.gipool("FRAME_IMAP_SPACECRAFT", 0, 1) ck_cover = spice.ckcov( - ck_kernel, int(id_imap_spacecraft), True, "INTERVAL", 0, "TDB" + str(ck_path), int(id_imap_spacecraft), True, "INTERVAL", 0, "TDB" ) num_intervals = spice.wncard(ck_cover) - with spice_ck_file(pointing_frame_path) as handle: + with open_spice_ck_file(pointing_frame_path) as handle: # TODO: this will change to the number of pointings. for i in range(num_intervals): # Get the coverage window diff --git a/imap_processing/tests/spice/test_kernels.py b/imap_processing/tests/spice/test_kernels.py index cb406195d..996eb3591 100644 --- a/imap_processing/tests/spice/test_kernels.py +++ b/imap_processing/tests/spice/test_kernels.py @@ -147,8 +147,10 @@ def test_create_pointing_frame( spice_test_data_path, pointing_frame_kernels, tmp_path, et_times ): """Tests create_pointing_frame function.""" + spice.kclear() spice.furnsh(pointing_frame_kernels) - create_pointing_frame(pointing_frame_path=tmp_path / "imap_dps.bc") + create_pointing_frame(pointing_frame_path=tmp_path / "imap_dps.bc", + ck_path=spice_test_data_path / "imap_sim_ck_2hr_2secsampling_with_nutation.bc") # After imap_dps.bc has been created. dps_kernel = str(tmp_path / "imap_dps.bc") @@ -169,8 +171,15 @@ def test_create_pointing_frame( # Verify imap_dps.bc has been created. assert (tmp_path / "imap_dps.bc").exists() + # Tests error handling when incorrect kernel is loaded. + spice.furnsh(pointing_frame_kernels) + with pytest.raises(ValueError, match="Error: Expected CK kernel badname_kernel.bc"): # Replace match string with expected error message + create_pointing_frame( + pointing_frame_path=tmp_path / "imap_dps.bc", + ck_path="badname_kernel.bc" + ) + -@ensure_spice def test_et_times(pointing_frame_kernels): """Tests get_et_times function.""" spice.furnsh(pointing_frame_kernels) @@ -186,21 +195,22 @@ def test_et_times(pointing_frame_kernels): return et_times -@ensure_spice -def test_multiple_attempts(pointing_frame_kernels, tmp_path): +def test_multiple_attempts(pointing_frame_kernels, tmp_path, spice_test_data_path): """Tests create_pointing_frame function with multiple pointing kernels.""" spice.furnsh(pointing_frame_kernels) # Check that a single segment is added regardless of how many times # create_pointing_frame is called. - create_pointing_frame(pointing_frame_path=tmp_path / "imap_dps.bc") + create_pointing_frame(pointing_frame_path=tmp_path / "imap_dps.bc", + ck_path=spice_test_data_path / "imap_sim_ck_2hr_2secsampling_with_nutation.bc") ck_cover = spice.ckcov( str(tmp_path / "imap_dps.bc"), -43901, True, "INTERVAL", 0, "TDB" ) num_intervals = spice.wncard(ck_cover) assert num_intervals == 1 - create_pointing_frame(pointing_frame_path=tmp_path / "imap_dps.bc") + create_pointing_frame(pointing_frame_path=tmp_path / "imap_dps.bc", + ck_path=spice_test_data_path / "imap_sim_ck_2hr_2secsampling_with_nutation.bc") ck_cover = spice.ckcov( str(tmp_path / "imap_dps.bc"), -43901, True, "INTERVAL", 0, "TDB" ) @@ -208,13 +218,13 @@ def test_multiple_attempts(pointing_frame_kernels, tmp_path): assert num_intervals == 1 -@ensure_spice def test_multiple_pointings(multiple_pointing_kernels, spice_test_data_path): """Tests create_pointing_frame function with multiple pointing kernels.""" spice.furnsh(multiple_pointing_kernels) create_pointing_frame( - pointing_frame_path=spice_test_data_path / "imap_pointing_frame.bc" + pointing_frame_path=spice_test_data_path / "imap_pointing_frame.bc", + ck_path=spice_test_data_path / "IMAP_spacecraft_attitude.bc", ) ck_cover_pointing = spice.ckcov( str(spice_test_data_path / "imap_pointing_frame.bc"), From 5c743859030ddca8724f899be1d7de7e15a62586 Mon Sep 17 00:00:00 2001 From: Laura Sandoval Date: Tue, 10 Sep 2024 17:12:19 -0600 Subject: [PATCH 10/12] pr response --- imap_processing/tests/spice/test_kernels.py | 25 +++++++++++++-------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/imap_processing/tests/spice/test_kernels.py b/imap_processing/tests/spice/test_kernels.py index 996eb3591..d3f47e1e3 100644 --- a/imap_processing/tests/spice/test_kernels.py +++ b/imap_processing/tests/spice/test_kernels.py @@ -149,8 +149,10 @@ def test_create_pointing_frame( """Tests create_pointing_frame function.""" spice.kclear() spice.furnsh(pointing_frame_kernels) - create_pointing_frame(pointing_frame_path=tmp_path / "imap_dps.bc", - ck_path=spice_test_data_path / "imap_sim_ck_2hr_2secsampling_with_nutation.bc") + create_pointing_frame( + pointing_frame_path=tmp_path / "imap_dps.bc", + ck_path=spice_test_data_path / "imap_sim_ck_2hr_2secsampling_with_nutation.bc", + ) # After imap_dps.bc has been created. dps_kernel = str(tmp_path / "imap_dps.bc") @@ -173,10 +175,11 @@ def test_create_pointing_frame( # Tests error handling when incorrect kernel is loaded. spice.furnsh(pointing_frame_kernels) - with pytest.raises(ValueError, match="Error: Expected CK kernel badname_kernel.bc"): # Replace match string with expected error message + with pytest.raises( + ValueError, match="Error: Expected CK kernel badname_kernel.bc" + ): # Replace match string with expected error message create_pointing_frame( - pointing_frame_path=tmp_path / "imap_dps.bc", - ck_path="badname_kernel.bc" + pointing_frame_path=tmp_path / "imap_dps.bc", ck_path="badname_kernel.bc" ) @@ -201,16 +204,20 @@ def test_multiple_attempts(pointing_frame_kernels, tmp_path, spice_test_data_pat # Check that a single segment is added regardless of how many times # create_pointing_frame is called. - create_pointing_frame(pointing_frame_path=tmp_path / "imap_dps.bc", - ck_path=spice_test_data_path / "imap_sim_ck_2hr_2secsampling_with_nutation.bc") + create_pointing_frame( + pointing_frame_path=tmp_path / "imap_dps.bc", + ck_path=spice_test_data_path / "imap_sim_ck_2hr_2secsampling_with_nutation.bc", + ) ck_cover = spice.ckcov( str(tmp_path / "imap_dps.bc"), -43901, True, "INTERVAL", 0, "TDB" ) num_intervals = spice.wncard(ck_cover) assert num_intervals == 1 - create_pointing_frame(pointing_frame_path=tmp_path / "imap_dps.bc", - ck_path=spice_test_data_path / "imap_sim_ck_2hr_2secsampling_with_nutation.bc") + create_pointing_frame( + pointing_frame_path=tmp_path / "imap_dps.bc", + ck_path=spice_test_data_path / "imap_sim_ck_2hr_2secsampling_with_nutation.bc", + ) ck_cover = spice.ckcov( str(tmp_path / "imap_dps.bc"), -43901, True, "INTERVAL", 0, "TDB" ) From 6d5fd21d0f526c89ee6b7efe93b5216ecb2e952c Mon Sep 17 00:00:00 2001 From: Laura Sandoval Date: Wed, 11 Sep 2024 09:05:29 -0600 Subject: [PATCH 11/12] kernels code pr response --- imap_processing/spice/kernels.py | 10 ++++----- .../spice/test_data/imap_pointing_frame.bc | Bin 62464 -> 0 bytes ...t_attitude.bc => sim_1yr_imap_attitude.bc} | Bin imap_processing/tests/spice/test_kernels.py | 20 +++++++++--------- 4 files changed, 15 insertions(+), 15 deletions(-) delete mode 100644 imap_processing/tests/spice/test_data/imap_pointing_frame.bc rename imap_processing/tests/spice/test_data/{IMAP_spacecraft_attitude.bc => sim_1yr_imap_attitude.bc} (100%) diff --git a/imap_processing/spice/kernels.py b/imap_processing/spice/kernels.py index 85b932450..42a8b7b3a 100644 --- a/imap_processing/spice/kernels.py +++ b/imap_processing/spice/kernels.py @@ -209,17 +209,13 @@ def create_pointing_frame(pointing_frame_path: Path, ck_path: Path) -> None: ck_path : Path Location of the CK kernel. - References - ---------- - https://numpydoc.readthedocs.io/en/latest/format.html#references - Notes ----- Kernels required to be furnished: "imap_science_0001.tf", "imap_sclk_0000.tsc", "imap_sim_ck_2hr_2secsampling_with_nutation.bc" or - "IMAP_spacecraft_attitude.bc", + "sim_1yr_imap_attitude.bc", "imap_wkcp.tf", "naif0012.tls" @@ -230,6 +226,10 @@ def create_pointing_frame(pointing_frame_path: Path, ck_path: Path) -> None: TODO: Figure out how we want to handle the file size becoming too large. - For now we can only furnish a single ck kernel. TODO: This will not be the case once we add the ability to query the .csv. + + References + ---------- + https://numpydoc.readthedocs.io/en/latest/format.html#references """ # Get IDs. # https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.gipool diff --git a/imap_processing/tests/spice/test_data/imap_pointing_frame.bc b/imap_processing/tests/spice/test_data/imap_pointing_frame.bc deleted file mode 100644 index 8ab7f76e3cbd02277fe0d16747967808b26c4b94..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 62464 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 Date: Thu, 12 Sep 2024 14:41:18 -0600 Subject: [PATCH 12/12] added test pointing frame kernel and docstring --- imap_processing/spice/kernels.py | 3 +++ .../test_data/sim_1yr_imap_pointing_frame.bc | Bin 0 -> 62464 bytes 2 files changed, 3 insertions(+) create mode 100644 imap_processing/tests/spice/test_data/sim_1yr_imap_pointing_frame.bc diff --git a/imap_processing/spice/kernels.py b/imap_processing/spice/kernels.py index 42a8b7b3a..87d73945b 100644 --- a/imap_processing/spice/kernels.py +++ b/imap_processing/spice/kernels.py @@ -187,6 +187,9 @@ def open_spice_ck_file(pointing_frame_path: Path) -> Generator[int, None, None]: handle : int Handle to the opened CK file. """ + # TODO: We will need to figure out if ck kernel changes + # and how that will affect appending to the pointing + # frame kernel. if pointing_frame_path.exists(): handle = spice.dafopw(str(pointing_frame_path)) else: diff --git a/imap_processing/tests/spice/test_data/sim_1yr_imap_pointing_frame.bc b/imap_processing/tests/spice/test_data/sim_1yr_imap_pointing_frame.bc new file mode 100644 index 0000000000000000000000000000000000000000..8ab7f76e3cbd02277fe0d16747967808b26c4b94 GIT binary patch literal 62464 zcmeIb2{@PQ+W&2+R*F^{Xf&3JCQ~hx3r&VpXd)^Ul~&T625FW?LxrL=(TtKZ+@_2b zGKI{)->+yuBaQF5U*|eHp6jZ0AJ2Qd`@h$JANKKlA3nYx=W1E@_PyJ7CL2#3Gs!|p zNkU9atPA8{um6Aj*S~J)LzET7#HP=lKFVy$lqrB6|LXV%{5w7ZQ)kbznQg5vqu)(m z2L4Y>-&?=uBmF5eCPVS>`0M=_-|T1fKmX?Rj5;M>-gl(wwu!k?i{swr+a|kafB!BE z(!c&aE)MaA|91dlnH=8v81yHV8mjlTu%0hC0g#a7;9Y*uzf!7o@Oi^}f#8Hfnv?y+ z1E4ohE~_~3mi5BGiGY}=9PkQ+{*rQm?ZJ1f7YR-@=EwbN92*Zy$Qh?Bo~LYXe6D{$cOFZ}KV}`d(^2nL%G!PZAtyNWTnyxAV|n zRr8(_@{RRmz>$MQXJ|ECfZk}>=Co7aSx+9Ee|<5s&Qy231if&WhjFM_Ec5xN430YF zW2TBQ0{WxFmvdp_tfv8v4kSElfb(VOEk-y_JS)L^y5JZ>Y_faRUV;Ao2;2B@N!BCa zm_np-x-Gf}{S9?1z4M(|&kP)ENLr3W<#p)YHO%f@=*)UH;MhZU<$kie3BA!sW8F(# zSkD0*7l`SDH&3FV?>0&=CgL~Na|LHLr1!(dxiQd}jndS-+?Dk_!108bSsKq;GSS^vI;h6PNJIuBA4L9P{_M48 zLhppw^^sC{wNprEpW@k4<3;2VvT%Pp|Ln?-w>1-bPe@tjnmc_+A!En5k9HX)BC^QC zk$=4J&t7XL^zM-HF48;|mrCLi`*OCbA|i#Hg#5EBKi<|%=zVf-T=rFG&oojj_-HHW zCn6EZ`N%)s_h+v)6MDx)J+BM7ZIDhjNGr|m(nCZpAn!r`*_9t}YbNyGDN(=j;P97p zqAz^iDOo~9;*sYd|9Ib@z1B?VUDUsa)7uvb_sPC3sTr~#g+vb77x`ybe!Q)j(EDlX zj6cJ(w`P!CGm1ZcZWNMH$ic`z-uGv(H4}PgjU8%dG-N_1IeBCD=j~NOG8Fk7^3SgP zcv~}}_t?{qbH9E3lu351_ty_85Rx$DIOHGi`?J@Y3BB8P#$A7GbT^A+t&&%>N*5A; z6C95y>Esa4 zgKCYw5kf-q&#wG|AYMAvuJ62l;1Le!Q)j(EHOj>1B@=*9YYOL=)wfEkbf0IR^R1`~K{;W~~gnPe{{UdWG-f4uL{ zUTY@w?p5@QEo%#WL`ou8sg+p>i8=Be2#GWD z0^}d>`?J@Y3B9B5@0yWgDVa}t?vYQf9w8+6kQI@CcIC(0nhCwPvp%#2H7(31cfI;w zx9TS(#mJYCf4uL{UTY@wE_e9mDAc`|Pr}~STRC?TlEuiu$UnRC<894^-tRf<*10M6 zEg(sgqm=r-6OdTsC&)kE_h+v)6MEQ(rJhmU!@8nd@Z4~A^oSc*q}b;N zZ%KImhr~WAw~mG0bByw+Yu#AS7n}gdk-W$Gcc5Q7w*Qsu-B~XXoKVQB{G1tgp`WHH zJL0A^>xF?60hv{h`rsb)W3;3$Mai&UBskHKKOWyRPk?@aw)oJP9;_DwP6A{|VRTj^ z^xbvZ!f*Y~dWqm8G2p2|W0U8hAUwkPXlf|C!CDh^LihW?dqL+Bk@)++#~93oK? zY?2Cn%YyIqW<4P|uOX684jA5t{`(&V{Sx}HUK2R2 z5UH}A2^r9L)z3bj*q8O%z>(+#f4}9P`dQF-Gf3&f$+4a!IMNW6iZyZB(2tmK*DqO~ z^<==2gXlh8F)kN+GBK)WYCqPK2S*8FQR#H+0ra+pk%!azvz{_I>X4;Xc3O|1UqQ}F z-ygtw8sO+ac2-;8%!B@*QP6>mfvl$sjv?fHjrr&T=%b8}bh#r%pzk!)C)~4;duTiYSKOXv*fD4-{F?0UQ^|bfLOh zDfAvwJ;WX_Q^8m*a@=t$m>G<_^JYWCc@O=GG|2+NG z1_BwrxeD~-b)8j!GoA}X%Bx8G%#n$Zt@-?@e%aQr+(R(LA zeC6)Xdkcw=oZ-=s@)3IQdLOIhkakSaJGQlK%R`ucaPprpQ{V% z%*GXwFH2Nh?;FD3FLE0)-#vO~`43m9n{%j$=q}JI&L1uyfAKhEzI*f@Be%Xaw$>Ms zynS=VdiN3#9prLkzI*g;yZXZXp{9N@dA|SXtCG+4Bn0^rGT%LV-@VuvJnj3DVzR7d z=Dx>*deV%%6q)ZHy#r$vawHZDi%IYP!@Sl#tS70+U6J|j(R=aspL&O_^-D-U<=u-C zZ`YH%$bFFc?$Nul{}Js>Re#MF^*!TCiDFQJ$G(;IL=$;9GT%LV&)&RoDDJvXDX~~}dhi;XdU64I z4l>_8diVBfG|F&%T1qPJls)UBUr%}>KSk!dNAKg0XPobx8vTT*8JBIHs#H(@;$g^q z_vjt1rQz9q;f^OHbbZ<`%`WvsANeFQ-#vP7li2jd=L?>YZ3$ARvtHDZ8OUpp`R>uX zy!rP1HLFz0$Of*jlXO8HsY2e3%y*C8@2`pi<>J?rkzn_vrC)E?k@v{{$b9$co&Wd# zuiBAD?ag1`2cTzk>)8O6a(Hg2F?IZsufje*cz1^9f5^+{z0OoXKfv79^RX)H`GOMw z8Q$1Up%Qvq3#-<`A*>e&PAFvQ3yD)z&>xy^wxM_^>xF?60lD<@Q~w(1D=m%Rm8h{^ zBskHK##e7n)Iu*mQ*X_aVXPMeP6A|1Q)AzH=-p;%HkS=&y+m*_Ae)=(`~}cowi>aj zVg&1Df|CzPZz-1*LI2TO`Q=k})++#~93uPn@sVfH+sy92qDq7HD!>sy9N*>0G(dlT zj_mX5k*p^K=QSkyeX4IG^dIL+xzvtgy(Vy4AzeP+lX?lg<$Q5L-DuWp14jbBA+3dXRWBskKL=(fw9o1p({*HSGU%X%{4$U(Y&4&T)b{XF{yhi96sCl8Ji zWa*b+@i)-NIMh}=*J3?oaMU3wU;Vt^Lf>gonQfys>uG?a15x;X;Ojf+ofj9BzR+Pk zU2qH`+r)Nl`2c;YWA>a^x~xaQF@;o#Z)y7o{SfDr!q?+i&kP)Eh@s@#jcw5HU2@l| zc|7acfMX9i)5-1qC+MFqjmm5JgY_K1ae;K|;=J|?^p>uXmT&b~&lQ~25ZB)pw0wm= zaoO42clxa70gfl+pZ?s^@$2h&{_lAH|M};@|K{WJtFQbGQQ_l!Qp?E5FVecFg6l{Y z@?_*+yWs8qI`oU!>juVH%~iAN4Y=DSDliJt0d4Hp)dlO?LZTMM1) zhzznTGT%LVcNATk8T#gWIe8g&D*WiwI#P=K0-5g~y-y4V1@=h%QcgxEi`{=QqK+6L zk3{CXNAH*-^JJG!<`v{h>e;a$rRzu|@+oA#d-UGXNEq`p^LPbmt!$eg(^O0T;!ntY z_vl@8ENj8lF7*|}ASmvd(&JhZimZjqcaPpr-LKgVOdkD|Y?A5|LT=TPZpfRF`R>s> zOM05X{>YneQIG$CRHv{uGw}lynb#Q0Kp~mW)L1jLdhB-fjJ5 zbj0rWs3c~)le*>1uO*7eQ<3@Z(fdv_uS=HW{7UjZ;PbK=y;?FJ`7JWvJ$eUvC8r-< zf3}hg?6$;Z)__{F3t1kS?;gDuGsff|`rc4UZZ;;npZ!!r9w1*u=DSDl$}W%Y)qm5h zBCls>e!N&-Lu8N}k@@b?`*X(J&*#@{t|Fs7^fo)*t09iaLy-CI(K~h3#Oz15$yMaG z(}zt3K{aGO@=avEd-R^I5$v26)1{icY7E`?aZ?R>hWrAV?;gE-cdj2+y={6mnGtPa zQZ%oI>_@gh=DSDlW5?rF)^W$GNq)g2t7YSA$Wr72WWIa!j=s6{u8m_!H921~VcD%d zHRKlZIb^N!CG91GlL0x=YhbOENvw%rtz*2{ zB-YCWCm-^ycke~rp*LG^t0ys;^$Nf#hb-yay;2(bgbh}AB&V=m1vmmouAJn89?;8e zGSlrmmGy++yoQYJ_xZ{1&~Mpn9MfeQ>otMX3OUjL?Oa*tYdrNdyPC3I8#oeO#l$`h zcu~{~`dM2wZ%CQ3o+LQZkVS*)t@}WKY1@cV-OX7~1{^s^%HWFpzR*i~DPNJcU_E(o zlpsSD3unkf@3y1=h#u2fPZ=C_$R4HK2mPSW@s_>xyCv&sfTIHuDW{nafPVZgsiCqn zSWg!mL&y}>_^g4@@82zcuGdV~BjA`q!iU669Spr-Pn%MoS*&LUjy2@l&@1T*&|BaPu%0V8t0CRg{S8&2 zxAraTH()mFd4S^y>3F{W`Lk8W$ED-r^1pe${-=Mw{OT)zrnG@~s7DR4ntJTiRJm$G z^RM2o{q431q%|^q!E_H|ZxEQ$r-)9q8rhQ$>D9 z?u^WLkKP@e#I;@X3Tuez&>pkQOsYsS@>FELd-Oih)##o&>un7=dgyph|4)@uywrSqfVISe@tneQIGciv8)E@!7zOQI!C*aR=HBu&V1$b9$c zU1VPN&2_m|Ezy#!F0oarBo@dz$b9$c{S}BHUX09lkKSX(xANi>uhx=JbpyNk8$TuEk$& zx9-uLTB5CevB>H}1(85DLFT(h@4J%mh1YvMuO(9+g{=3xTtO<3O_2HS(K}G{-r35@ zo$JWs)!LWlEUX|p$PbbE?$LWu^IP-=HI+It|MbMcr(`S0IAlv?zI*hp%&^xPeaEnl zjIuZJaY-*H*~r6?`R>vC^Y{|?+aK)e$e*`Qhuz*#P6CnLk@@b?JN3r*=W&7!b>!|U z&A|tTmy@f=95UZMde7dud8KNHUmeM|(|jl>Ds-#vOqhiz(8aH+2&+@*R$)fZ35 zMdVmyzI*iE-Z#uJJoalH2|AeMaVGc)@j*U;%y*C8<<4P>U1H?xNy+vHhLL7Z;JLk> zE0Fo_(ffVb+3)a=vYr#3VFDe*wgK<2wg@BF{_f7OmNYH$AfJ^($V#Tvc|L*Tg~ z_+Y{DfpggB2X85O{)gNexl3OS`qo3)eFo2EJzsDFAis^?5;qKbvm+^f3iDVm5S&oR zyfJIXjey?g=-r-5^I0zpoCwIdv2M52p)WZWby(S!^&-KEhBRwBYmJ0n-9J)V)sFRI zz)67op}pYdDCk`R&K?-Dfb|l=$$)Ivu^BxE`q;pru4?wImkCZjBu&@i>R9Mok00AR z%z^a^z$u4x8b3*03;OXVeL9U;$a)pv2q2UGFo@8Geoc_~PW45sCj{p;#7j?GO&9vG z;LTzq7qeayIIWPo`WolQL0=o$QO+(M?RO$v{=*59s@zUjAjY6YEKWBMniW zIPi=f^ix6|J;yq;o(wp05G%vp3I@=7gxR)gE@3@+aFih1N%vC|pbt4?wLyC+>nVey z4!L9`IlvJ5Z-}r(RNC zF~N=X9Kdmb*i0*wnFjsbi2lzfu4Fw|a8^T{Omlrrp?8jybs>MUo(DLdkbnAfOUJLT z<2n4l={fvA_4)j(ul!zh*~Zt*>xuQ+0dt)%mXb}#w#dJFzxKB`6M831)++iE>0D3z zSEh%_&n_i1k@q3<-J|!!$`hxT%-UK{tiF8oa1bvgi;)*2^WCF&$Dy!K&0zucB)L*@ z*0}2>s9DUBNgxDZ+$b9$c9n<&AiM4;E*OP|j(OoWe zDIu!JWypN@=)H4ptK!BT74@Vnpen*Rx|qZuXCd?5qj%AFZ^^svZ|aG-czDA)hhp*t z`8Q;~d-Q(lW%&9*XDI<$b|_KWq-!zhgS-@(?;gFg4y@d3ZlWk4Q|&qP%h5&TD6%m! z-#vPd9sm91gCV*C61O#};JSSgIgHF9^WCF&+tHJ*Z?2jP$oYvY?$7E{M7ANvBJ=c^?;gDuRh<_f zQ}q#$MTPSOEn5N>8%y*C8pWE&)kNSF3 zK*9{(Y=1KAFac_Gx3doT|kGFoAQa}ulPa*T&qxZ3hyJ2tlML@1DJaonS zWj+xiUqj}*NAKtrmN&ba_7IY`X|W4u`sEW3|1neQIG-=hbw&L}q)lBFjnWd7MHk3=E6 zBJBb9io8epyOjw2FOx@a_)J|B!X&@%t>GUwK8` z$;6%Ye8CBTY_y2!Yzh6Mt8LYjRzRRL4bfjP zaM5z;v*R807Vly`8*uC)6$^S-u7KVn!S;^hZq{=E#|5&{zWaie(0@v>(ska$damHC zhDbX|mi-C+iA1xQC3{)V0~}Atf9iRpwNADef>jX8~YeI5gX`!=B zYc>f-jzQ+TNAIFhch`)mxF;kR;lDqSnwU*2kar>T-J|!@`&=JolPn?0AMaYY(L0NL zLVkeEcaPp#mri9Lol-0$*|T47TW~*<+(*8Q%y*C8V-thl_7TC?yvUyrIIno>d1We=$%?C z-}i{l7!m36C3WzT$0;NlSp%8x9=&H5AKDgl=?@X{JHKztuwE(f`(*8W2$}C5y?Zm# z!-HasMP$l#g;X1>WFmdPoqHkk-J|z$v2~A0^UdMUWp!Q^JX(n(&B!mE+L`Yjy`%H8 zLd*lL;m@JHa?I@csYD_|mO$paNAK;TMZrr~*og>fw2~_8mq1#O2O#s^qj$MPw@0R> zjv_L7!t80C1MZPeQtfPs%y*C8?`sb3xm2-CMAn6OI^ogdE;)v5jm&qC-uZv;|Ee`K zYH$AfJ^($V(+;2Ky2EpWT#~V-%Rct`!CMBN{~^N{zAah}{i!6q8?O6V&lj8k$hCzp ztk*z4jME&o+=ul7!3l+oT2!CE7W%W?h$|}&uwEEA5s-_EDrT&QzHhSfh?Tyq7YR-@ zWWeIW2OIv?C-=Yf=Rwws0Ve^ne{rt)Cg@)z%MNuv#CnO~WI&o0r)6!1enyJaxz&eR zFB6=6h?Qgf)Gg4TP7zmLbAd0i=`D6_f4I&q{3> zwEh_D3Bh>{v2i+=>;-*LYD4e_Kh|pkrxkL_Da6nl`l{60ew+MRuMHfD9%5p(PW}ly zp&yi1c6@UH>q&wm4N-9R)!z-hOk0~{SliL;y50qCElMM--Hv7RnC zhLHEp&NqFbmr0L2urrwT2sow?nI#KGAA(*f{cP9WA*^Qxjx|JSiOto+(2q)C)~4;i_{Lj5T8TIt6+?K{nS4&b;zw3bYYI0pTYbf2C3Ls`!ioYj!AOAOTfp;t)v z7CR8edLH0-LOPzqfBp>C@p0++xcobw!~avCSHJqoA1eB;wb5Nf&ZXGZs_ef*jw4@2 z{?+@nzrC5zJ3%dOYH;Lw5t;hD}IpXsw=^DQz1 zc?L4yJ$iS{85we;V26mDSv7v8OiMIbjC=x_?;gERHt!jIJ7bTCY`R`MtH-h^;)1*z zneQIGV|1$*PqR88A}T2%HyRRd5NTvPWWIa!-uWJr*4yK-h$M2lgZ(0|k^#5cIRu&S z9=(f7TYKgV^%D`U;nQt3|44EJ`86`%J$gUgF~}I?9w;I&7sqGEZM{gYBfmrDyGQRV ztzIE1(@%=Xr_DlfyQSyI5aiCD?aX(N-eY+e9(yDO!}HjV&O~TZXTfk! zEHbn%X1FbB+uP2H$b9$c{kdS^geot%w@>!_*EfM{I2Ys{$b9$cojT~U`@E0v@srW0 zu0EBphm$~7L*~0j?^)R)MZ?75o?AWTvH+Ry9=-Gb-v3o=Y}DTT^?d+(M(Rtn&j-MB z!{GGI+kMZl&kx?e!}C96@DdG`q&wm4H@s4 zwl5O;(qt*8GuK&91{^uaQ+6m8xvvR)Xn?-YZ$W_RE_vqabyi~a4);1A&E9(7b z>XR658?wx^cILZB?~}9-U9NxIC?eVAMG>c5ZgWqOZzA*EqjyZ*gfrL6JVaz}`Iwox z?_#;m6WduEneQIGcWR_YPM_i?B3omIZqD5q$Gt^ffy{T0-bITZZC!1=L_|!2n}!7T zyvHp?)<@>MNAIT*QF`4T*^5ZagloBqq469+E=A_MNAIj_EvhmvW{b#*e$gA9G!nTC zWHV&Gd-NX52^zQN}^JGqlT!PGZkKTb*kCQ)0Xo|>{F@fdo*HgH+$ic{b z_vpQ7=CQNO8#NIrYM5l%cU>yi5BUo+-#vO)ZqPm*?lA~He)AS?+pL$y?Llrv=DSDl z&qW?@YrSRR_djnvTz=wP8n=*QzI*ge-QVkKwMrKe(JFM3Uy+v1-9hez%y*C8v(K+J z-kbeNNE|NS3wXKjK39so8kz4Ny?Yzy?mI3I-+vPBH0Nvo=^5M?s>dPAt#i&;hR{qWmFmQID4+*0Jt$b9$cz5OoS@@{5^ zko4(qnZEXT7T1WZgv@u3-sNvXn>F*}gk;?YDf7X$+1wLkKV-gp^nPF4?c(kgmxUzt z^@nA419LcIWU;1p=DSDl{J-~q)lM>MZ~po|06nAN1#T^O;kn`Sy{O!qciHC$Z&`T$ zhrG9Qc8`aC*1brx=zFZ^3r+yU)NaA61n9%=p3RJjXT3mhLLnz?ZQPQee;yY!<#qz= zg@F?RshMxlz(KDRcPuS7k@X_MiG~cFKgl%(`W1J4jN_76F9w_hh~qp1VJh_JV!gS$ z9P1^5lK}~ttL>Z)ea`L86XTOvFB6=6NZuTc+WXLVzU>j8kivQe;FLqY%vN2L3H|6> z%k`2{S+4>d0c7axft6X%Tg5ou;nG-72+nJWsZH+%IncXD+v=vIvtAQ8tq>RM?q#{q zZ;i5wNxjc{ZQw}sg72@llAQMt`eQfEG}AL!PZAtyi2tn5MUS9AbHn(?{Y=)A0Y?sU zZsuF-eCY37*Bh0Y#d`AKC_%2zc#&TKeZn=(D_PmBrwoodB-*lmMj`Y`S4WJ<$zeSW zaC9Is(<>enL4W;<@}=Bd*3$*Y5OUL^(7Xit=*#_wK77D>1RPUHgn4dODfAa3WzRi& z$a-erSVMx%(x#R{9~dE}od1aRY{0RH>^F^1FNfail6Y7_9_u-P;{sVbEym<2^h+L_UYicaPpD zFW0wC7lWV6{ZM2T7+(F5D?=6`^WCF&%tAW_cPaR}+(*`wE|my-#MvUBK<2wg@12nm zqYq!u6Ov0i{uuPsA&*l+zKhIvkKRSWm8TvS4HJ^-($bUVdgOEA$kUMd?$P_HHP>y< ziM~RzO~-Or>&krYJMuxrY6qF>dPVy*)D zPh`G(^e&HAIymf6K|RrmE3OVHF6QEp$0PIIqxXAM{H+yl;_AsN7s27zqf5B!$g7e0 z?$JB{@BLr3Q;gc1zrGJZ&&XiPIm2prZs>o$phIa1tOohHJH+L;v~IUFqs4td|H*2ITVux0?;n zKM09BP*cWwnc(C@k_?>5?yaw2y$Wyy5Cc6M z_1Dl#o($?Fe9C%4a9%?`jJJqrf@FdB()akR|WcrJ+ETDHgF{R zh>0!MH8}qUdZhsG?G4qeCkc);q?eAi$~)-u{5F4StYJMFaO5DImd2U)(62h?;rX(b z_2j`(g1Bj_Dtv@q_2}}}S9PqX430Xa@7RH-TA|N5?6{$+p7k`q(SgK|?mgfW^s5fp zzH1h+o-R0skj10ApZpBH>_MwFZ-lHzz%hkLjFgo73jNIkX3cL!tY-#}HRPQ7Xa8@| z&+su`_5K;_*??mYF&^<&R_r7PI00FDc!a@dO_;tlZq4tq6Mw8Bf; za|LHLWP@70jASEx|Nov5&)eW7?RkLX3F&yg{`s?2$H%4P?;gE(*2~5X zfAPJJ*v=~$n(zOFTZFs`neQIGiyCZ7>zd%t=gw2i+u+^s1fECQ`3^GQJ$gS?82|p( zFSCx^j%(TwYgon=Ag3bp-J^HbwN!`nqRVw;tKx-QfBKhkSCPGt`R>ts%wUvW((Oa= z=edXN&$;ldjMJx>?;gF|7IW5S<5t5zkH+d*4hH2M&3yOheK+~p?4r5Wb;PhHG2icS zIX4a20GaO|y#vFWmzK)w)R7C$&*CT7mUHKk&mr^Oqxa&|#`@yTed|cRz-fhtb_G|6 zT#d|kkKUF2pC4Sf?F0NAMu{DRvCbHeg#-Be3!$*c&Y@7g65+*D*^ zWWIa!PL2Q4veoTQE!i7ru9>0sl#4_5MdrIl?^#{VX^ZEbs3q@uP0GyO@RZZ0nC~9F zdjmhUxjfqdzrNJrc_tZ8xgcaOWWIa!KGxJwHXk&%mTX)cZ9SrIB{vS)9hvVQy`vSU z8D)RasU=OKH}khSS8@u-Ey#TL=)HYmvee~`y=%!ypMYl*qAIy?8_vl?-R;3gd z)>1?Mko+L&@~x69MV^4ncaPrh&EdJ}?s+vNvA@f$59U?e8;bev(L4X|{a>}yjM|&O zz7IgpsOQiM-%c-LO(J(GyL^V1^z(yvUwHn9TvRQT>hco4Z+>Tg!54T*d%oZVKqji> z?)&W(d_VjSS*LIClJ)|@35Dbg1jxQYSG;7PAF!pxw$mHdO9UqaB0uol*B;PE zZf+>;{Fe1H!O4eQ>>skFC-jpy)z10t9qScbvWGALw7L&Ccuok@cFuX@ykv-m+E>de1c}mNKoZ*9MM+ z96X2jTH7KI{pZzpb9=P0o+LQZkk+1V?){+;bdNIY`HA&pz>$OO`Q7=|0O(a#MP|x= zW<7aulpwu(EN~kH{f(7pr}X~9ddlFaL(WLsGz^A*tXoi8pRcT^0geu2RCfzkMd)*v zA2XKw#(KKo7(x=HCJB|Gw_E1J$$w`(0*)!fysLq;3iORG-V^(a-C;gY&A_pSRCLj< zRfT@l(#`P$#97Y<9D9gcXN^Tep%+`?p*Ki^^&G%)fqa!ztyF_P)M@#h!IG@!3eIZC z5s85dhC{FE=%}mMiS<0d@r3--pIbV9eI3u?|4q-~|EbUCUw!2dnc{Kr`PCYdmUBk@ z)$uCsFXoVc^?vPdZzl9kF!p&lzWae1qP;(;b!uG|XF@UGJ$g^5T(~&ziAxP}*x++j zVpKJ!Ofla*dUxDhvQso`QVnq)Ia*I)V>K6zya<`^9=%WE-UMf?SF9nbABWvPkzCEi zBP$~F-J^F*V@^cOkq_0RcPIVS*iJRvbBg)y(R=6R4Og+F*Q$y4(S4PVhSzX3^WCF& z(QmP}8n2zJN&T#bMgG%kI7wsyGT%LVKMnNSSa+p=HSz41?j^aphEt%J?;gFgs%~E9 zavoI?HTgC#1^*hZ9C;Wr-#vPd?Xx~>HhmZTy!kG&QT4ZLxUIA6!YDqcc4nYKXXT)t|Xcl zwC1S~s^w_ryGQTE=rGgmrZX#v;%~>?{3h0NH;|Q)`R>uXGS_t1>|C)*;%g?XKfAD& z%RxSX%y*C8pN+2;`Nc&&C3#(1b?1B4a?QvOk@@b?J5{cS;E1f#Q<5ldnSU#+mK%T^ zkIZ+E-m{;ly-z5WeM$GfIZlnSEfV(Y%Jx|aKhtb@#V zkKV^knyr;l?iEBqae~I>Z?#-2azA9gd-RU3i(EG(dvFCAY?5~-XmA}@gRF$icaPrN z*Xs`I&(19;QBGf#Moy^X!jKb?`R>uX{BWH0``Mey$qlExPmAs9IA4nS?$P@_Z)fBm z4~LW!@lE5mj^9+r#UaZg^WCF&{@?q*YR!z=o4>vfK+i~3ym#3Mcy5SaWE-Q@nSFln zmWStmNZj{s^E9AexX?;dr3>r%f)fCl`AwpDB=pbi&2Fgv#(II^ghE7LK3R|cSHHk` z)X=W17Y0rQWXI<>`D370u+zJuCdGP@;6y_PerlYd34P{#%@M=9v0e-~36MLjbq}^5`C{R{@RyV)8aM{SW9{t)#-n{LXqpa9%?iTJD+XLmxg%e6VIu)@uT% z72@9T2rB)F*QYcVn5a+;Fv;Yh}PVk z2EEdhy9W&Wvz{3^){s8J6{F3de>y3utI+_~vjN8*^0eORsyX!VJ4<_w2eO_6I4+Qg zIy?30(C;xm+iB7u)^i1CHDpt*b%Z7Kc1A%vCl6*l4{$so|EcGZj=yflbA88i{m(xK z{v98eUw!4jnNt2CDZh+dOgr}GdSD%Q8u>Qzuimfy?ahSV2@{5gj2*ePjOYY?yqpnT z$BjoGkIZ+E-V;x9=$sPf=!*g3!aeilkUkCy{O~P zAdg1oyGQSnszZy;{ki1{dB5?)(yZU=xdLRdP3_EgkKQqhB4nB?hdd#NbEc2AQ?2Lz z;zP)M_vpRjZ@Bs^@BuCnNLSqjy^mqpZD!y-P^MIG>%q$@Sb@(#|Q!eD~;m zH)7XUBgK2gWOu>E7TdCVZWQuPWWIa!4lGN2c3y08F>x$DGAyN~o)aL?L*~0j??s!* zkAgRME+)^tF2yEG2{?lM37PL6y(@#C2w(eNE+VU>rW?#x7I1;c>yY{G(fd<1X|9dl z%p!7mikIEKKLngM@+oA#d-P5f|N3(2uD6Bcn`|;?Ia9!W`_j(hz1o@Y9=&Im%={YX zcA}8%ziBhz%2EMmfgFy^caPq^S!Z8WzSW0cpQq=5uq^`aDe`k{17yB?^e(qsxjAF){Q{ysXn#+OJOQ^9c`!2HJ$k?A?NB|e<61!C)?b>X zQ7_;gAjc!~-J^H@-}}F6EsWZmzrGJZ&&aaITx}*iH)xS#VpA2^=Lhfp@ca)MT5WQE z7W6WPKHH}$vYszE0T8h&eHClyYbJPqF;!x{KyX4KHBYt9*g&6du-VgGnf1cJiGZY6 zs4L8YK2G1G)k1~!BEgAOH5d8|ddoLhs_)Pl3^;O-yZMjz zErtH^nEp=oqgYQK93@CmUQTCM=)aAYt#KI5ddlFaL*741-L(w*A)}=hE*isn8sO+a z20pqcz5@DLqr{&s9?N>V;21)tJ&g8pgMP=zHd`l6)+6AULN-3Q{BN$QJJ)qw-qReWU zF6+61v-)4jKJb1G^y$M3@|KTdJr8g^Asx@*KYs@6__%a@T>c%;;s2@6t6zQPe>Q@= z4(nP#RL+pd-UE>(A0aQ z6ZnukGp|kENeR2 z#CnFP{QFKJ*Bf~eGT%LVXC2TEF*`9cm-LF6b@8;HkPAot9^KA-_vk&=W~iZh_h1eo zD>m$Ya8}5DLY{!kdym>}PsZM!yd*oDgcV4PICxXYb=7R=O~`!r=zTZ+n994;owLdG zVG}K%CJDJU$fJ@caPqSm*#%ER_yVR? z`R>s>T7Aov>4m*gNsFbN+dB;rr-xjP%y*C8+m9w!xSzI2Az$v=Z>`Z4ac7ao-)m>S zd-N_}`Cwk@klo4TW#3Lo3y6r@fLw{pcaPrhpRG&3m2Kk4s~M5QT}(xsicLEgAoJa$ zcmCh|ziKUw+MB<=4?xc-A#3N_b@1FEH$2- zJPcSb5}atrk^6QHTcH0iv3%5hx zqI!&5Z^(L?;N(MIq?tJHfZjvZXW~Y}dIjK=L)_BzYrUcWsNx;J$%yqTz!5;+q-rhN z^{-xKv!16h>j}Ym4cVTmUb!3kzA7GfwwSP96F999*;JJUd!fIkyj*wNB-U#KM`95C z{DhPNW&5C?uIw1IeKPAwf+GzvOX)Sw2l{3uTg@F)SWgBVImoNzZp8_tx9naVQP0!c=^v~yCedWJ2)}`sq^F(sVNa^hBOUW)m58%K zc0uO5NAH;OD!sv;y<p(6(z*NeC-$Qj6d_vpRTxagexa_?J2Z`h@!bGC@M8sy2y zeD~;G)bmM5S-+-eBIbT^@{=7RP8s>-s&?kPNAD-i?)QxR?4w9y(I4uKdqmuLtstn}mB=`yRXlH46NO7Vw9Ts?9+GT%LV zx2;quvKe6-N#<|Xnd0du;#MKgLFT(h@4Hl2gTf&y7s>90W+$=&MO+rL7c$>HdIv^& zsMz%R5>9UI%^7j=q=<_~zJ$zokKT*+iSN(#zZynr@~eCI4iRxm$T9`(%y*C8l{wo- z^_aFeh#XBn=kfb#5$A)Pi_CYA-k*+3hAUR|@h4AAS1$<(6>%$(bCCJ&(L1%SvSj)< ze;+cT@_Da2VIr;(c^op|J$lbZO}yss_I)$)HH?dJIwRsPAqODy-J^GJl)Z6GcXJIg z=18}>p>VGj`8Q;~d-Oh5Zxr;&oVtn3om(+u1^gVvZpiDA`R>s>x+Op8&E=^5#NfSF zP6qtk!_Uat$b9$cz5S%t;PstQKjNY4CVmTkPUZvT?Z|xh=v^+hdh%<7`jf;d;lLOz z_&JKbkgbsU?$P`G{=xf?r>KRIho@7I8pF>~JdFGSneQIG^Z(xGYybK_fU!o=N%xY2 z;JM+nf_TW$S?u$J_h5Mbhn!A|HVlD&heBIFKP%Sr1t$Pf-hX1zdg zLLobo!u3O;_flx+6KKPFVc-`fUnjJx|VI zy%=y3AihZlZiPdCP@&*(&|KC_1SbO$oU~KxJoMKUvZX`jv0f%P`H&k)o;NQ*pQw;> z;M9E9D*&e)lAE+<^d;zDDBSHDYRh^R;0PculU7`ffWEh4)ZQ>V))RvB8q$YzQojtn zj$&k|vkO?S37l5QB+f443iQhq&+ZJjXT3IXBoyGk=jN={u0elOF-YvZ1M5kGBMph- z%+FtkzFG0u_6rMHPX-)0$Xm`tl{`G(6Bk)guZt3{-{dfO9 z!O#EhI^O#K_ebDY|7mirr(!^5OgJ%Y?&(wqKR4YU*&6v*@7MnJWtsLc4Hu_mnpgL>OFVQ3gMEekAfsWWIa!?ig}Z>u!F}tHipO zLG73$A~F$~AoJa$_sLdmtAP4ZH%Jek^qPLY@N)o>pX_gEzI*hJ@m{od*@q!fq@k*> zZ@^v=*@G-Z=DW8u>3{B>ek;7A-J_$4(EtUNbKbD0m$$PMGT%LV7l|z5zRxAMNJJO= zhRIvu=guSFL*~0j@2A(F@9nyfdz;883{dmjAR=#&rI7jV(L3vj*UTBS=iMP|{=B3( zX|;&tBJV)vyGQS_bQ3uX`?@&dr0lPFX1RzwL7s)ocaPp}#lMM@*p>H)+`P9Q$DH8v z1z8c9?;gGH26WkyYWE?Y)QnGAD!%}J&Uxo{{)WtVkKTc*RYl`Xyb{TuhBlU3HX>qy zJQJDk9=#W}wXP>E`HdsDhPG|5GZzs9