From d1b9922c6b51cf51cbc38eff2f1fd32dec6f3020 Mon Sep 17 00:00:00 2001 From: Frank14f <1515444314@qq.com> Date: Tue, 9 Jun 2026 18:46:59 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AC=AC=E4=B8=80=E8=BD=AE=E5=88=86=E6=9E=90?= =?UTF-8?q?=E5=B7=A5=E4=BD=9C=E6=9A=82=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 6 +- CelerisLab | 2 +- LegacyCelerisLab/__init__.py | 3 + LegacyCelerisLab/compiler.py | 87 + LegacyCelerisLab/driver.py | 409 +++ LegacyCelerisLab/kernels/D2Q9.cu | 101 + LegacyCelerisLab/kernels/IO.cu | 0 LegacyCelerisLab/kernels/const.h | 10 + LegacyCelerisLab/kernels/kernel.cu | 234 ++ LegacyCelerisLab/kernels/macros.h | 37 + LegacyCelerisLab/kernels/preproc.cu | 2 + LegacyCelerisLab/preprocess.py | 40 + LegacyCelerisLab/utils.py | 256 ++ README.md | 53 +- .../diagnose_equivariance.py | 350 ++ .../phase2_ablation.py | 439 +++ .../phase2_control_fit.py | 320 ++ .../phase3_ablation_val.py | 267 ++ .../phase3_validate.py | 306 ++ .../analysis_crossre_scripts/validate_v22.py | 197 ++ .../analysis_crossre_scripts/validate_v23.py | 237 ++ .../legacy/d1a3o12_250421_dante_v6.py | 755 +++++ .../d1a3o12_250421_dante_v7_openloop.py | 911 ++++++ .../dante_v6_initial_surrogate_analysis.md | 51 + .../dante_pinball/legacy/dante_v6_ppo_diag.md | 28 + .../legacy/dante_v6_surrogate_study.md | 23 + .../legacy/dante_v6_surrogate_torch.py | 253 ++ .../legacy/fit_ppo_control_sindy.py | 289 ++ .../legacy/generate_v6_v7_ppo_briefing.py | 658 ++++ .../legacy/gym_env_dante_total_force.py | 271 ++ .../v6_init_surrogate_mlp_cnn_compare.py | 216 ++ .../legacy/v6_v7_pca_distance_with_ppo.py | 318 ++ .../legacy/v6_v7_ppo_briefing_20260324.md | 39 + .../v6_v7_reaudit_from_scratch_20260323.md | 434 +++ archive/drl_pinball_new_api/cfd/__init__.py | 1 + .../drl_pinball_new_api/cfd/pinball_env.py | 371 +++ .../drl_pinball_new_api/scenes/__init__.py | 1 + .../scenes/karman_cloak/__init__.py | 1 + .../scenes/karman_cloak/re100_scene.py | 536 ++++ .../drl_pinball_new_api/validate/__init__.py | 1 + .../validate/validate_re100.py | 415 +++ configs/CONFIG.md | 92 + configs/config_body.json | 3 + configs/config_lbm_pinball.json | 50 + configs/{ => legacy_configs}/config_cuda.json | 0 .../config_flowfield.json | 2 +- configs/{ => legacy_configs}/config_gym.json | 0 docs/REFACTORING_SUMMARY.md | 295 -- docs/SUBMODULE_WORKFLOW.md | 480 --- scripts/train_ppo.py | 316 -- src/CCD_analysis/ccd_notes.md | 594 ++++ src/CCD_analysis/configs/config_cuda.json | 9 + .../configs/config_flowfield.json | 13 + src/CCD_analysis/knowledge.md | 184 ++ src/CCD_analysis/scripts/__init__.py | 1 + src/CCD_analysis/scripts/analysis_utils.py | 270 ++ src/CCD_analysis/scripts/cfg.py | 101 + src/CCD_analysis/scripts/compile_results.py | 176 + .../scripts/phase0_standard_freq.py | 214 ++ src/CCD_analysis/scripts/phase1_collect.py | 769 +++++ src/CCD_analysis/scripts/phase2_resample.py | 514 +++ src/CCD_analysis/scripts/phase3_pod.py | 218 ++ src/CCD_analysis/scripts/phase4_ccd.py | 332 ++ src/CCD_analysis/scripts/phase5_steady.py | 232 ++ src/CCD_analysis/scripts/utils.py | 308 ++ src/CCD_analysis/scripts/validate_control.py | 561 ++++ src/__init__.py | 10 - src/analysis_cloak/__init__.py | 0 src/analysis_cloak/common/__init__.py | 0 src/analysis_cloak/common/feature_builder.py | 214 ++ .../common/prebuild_features.py | 81 + src/analysis_cloak/common/run_sindy.py | 253 ++ src/analysis_cloak/common/run_sr_pareto.py | 84 + src/analysis_cloak/comparisons/__init__.py | 0 .../comparisons/support_overlap/__init__.py | 0 .../support_overlap/run_overlap.py | 84 + .../karman/closed_loop/re200_freq_test.json | 5 + .../karman/closed_loop/test_re200_freq.py | 182 ++ .../scenes/karman/sindy/karman_sindy.json | 508 +++ .../scenes/karman/sr/karman_sr_pareto.json | 94 + .../steady/closed_loop/gen_steady_data.py | 170 + .../steady/closed_loop/vorticity_steady.png | Bin 0 -> 163100 bytes .../scenes/steady/sindy/steady_sindy.json | 498 +++ .../scenes/steady/sr/steady_sr_pareto.json | 46 + src/analysis_crossre/analysis_notes.md | 405 +++ src/analysis_crossre/configs/config_cuda.json | 9 + .../configs/config_flowfield.json | 13 + src/analysis_crossre/scripts/cfg.py | 91 + src/analysis_crossre/scripts/phase1_infer.py | 329 ++ src/analysis_crossre/scripts/utils.py | 851 +++++ src/analysis_crossre/steady_cloak_plan.md | 121 + src/config.py | 125 - src/drl_pinball/__init__.py | 1 + src/drl_pinball/knowledge.md | 890 ++++++ src/drl_pinball/legacy_env/__init__.py | 1 + .../legacy_env/legacy_env_erase.py | 304 ++ src/drl_pinball/legacy_env/legacy_env_imit.py | 318 ++ .../legacy_env/legacy_env_imit_target.py | 207 ++ .../legacy_env_karman_cloak_standard.py | 270 ++ .../legacy_env/legacy_env_reduce_obs.py | 245 ++ .../legacy_env/legacy_env_vortex.py | 237 ++ .../legacy_env/legacy_karman_env.py | 518 +++ src/drl_pinball/legacy_test/paraview.ipynb | 494 +++ src/drl_pinball/legacy_test/uni_test.ipynb | 2828 +++++++++++++++++ src/drl_pinball/legacy_train/erase.py | 79 + src/drl_pinball/legacy_train/imit.py | 77 + src/drl_pinball/legacy_train/karman_cloak.py | 77 + src/drl_pinball/legacy_train/reduce_obs.py | 77 + src/drl_pinball/legacy_train/vortex.py | 75 + src/environments/__init__.py | 7 - src/environments/cfd_env.py | 328 -- 111 files changed, 24287 insertions(+), 1581 deletions(-) create mode 100644 LegacyCelerisLab/__init__.py create mode 100644 LegacyCelerisLab/compiler.py create mode 100644 LegacyCelerisLab/driver.py create mode 100644 LegacyCelerisLab/kernels/D2Q9.cu create mode 100644 LegacyCelerisLab/kernels/IO.cu create mode 100644 LegacyCelerisLab/kernels/const.h create mode 100644 LegacyCelerisLab/kernels/kernel.cu create mode 100644 LegacyCelerisLab/kernels/macros.h create mode 100644 LegacyCelerisLab/kernels/preproc.cu create mode 100644 LegacyCelerisLab/preprocess.py create mode 100644 LegacyCelerisLab/utils.py create mode 100644 archive/analysis_crossre_scripts/diagnose_equivariance.py create mode 100644 archive/analysis_crossre_scripts/phase2_ablation.py create mode 100644 archive/analysis_crossre_scripts/phase2_control_fit.py create mode 100644 archive/analysis_crossre_scripts/phase3_ablation_val.py create mode 100644 archive/analysis_crossre_scripts/phase3_validate.py create mode 100644 archive/analysis_crossre_scripts/validate_v22.py create mode 100644 archive/analysis_crossre_scripts/validate_v23.py create mode 100644 archive/dante_pinball/dante_pinball/legacy/d1a3o12_250421_dante_v6.py create mode 100644 archive/dante_pinball/dante_pinball/legacy/d1a3o12_250421_dante_v7_openloop.py create mode 100644 archive/dante_pinball/dante_pinball/legacy/dante_v6_initial_surrogate_analysis.md create mode 100644 archive/dante_pinball/dante_pinball/legacy/dante_v6_ppo_diag.md create mode 100644 archive/dante_pinball/dante_pinball/legacy/dante_v6_surrogate_study.md create mode 100644 archive/dante_pinball/dante_pinball/legacy/dante_v6_surrogate_torch.py create mode 100644 archive/dante_pinball/dante_pinball/legacy/fit_ppo_control_sindy.py create mode 100644 archive/dante_pinball/dante_pinball/legacy/generate_v6_v7_ppo_briefing.py create mode 100644 archive/dante_pinball/dante_pinball/legacy/gym_env_dante_total_force.py create mode 100644 archive/dante_pinball/dante_pinball/legacy/v6_init_surrogate_mlp_cnn_compare.py create mode 100644 archive/dante_pinball/dante_pinball/legacy/v6_v7_pca_distance_with_ppo.py create mode 100644 archive/dante_pinball/dante_pinball/legacy/v6_v7_ppo_briefing_20260324.md create mode 100644 archive/dante_pinball/dante_pinball/legacy/v6_v7_reaudit_from_scratch_20260323.md create mode 100644 archive/drl_pinball_new_api/cfd/__init__.py create mode 100644 archive/drl_pinball_new_api/cfd/pinball_env.py create mode 100644 archive/drl_pinball_new_api/scenes/__init__.py create mode 100644 archive/drl_pinball_new_api/scenes/karman_cloak/__init__.py create mode 100644 archive/drl_pinball_new_api/scenes/karman_cloak/re100_scene.py create mode 100644 archive/drl_pinball_new_api/validate/__init__.py create mode 100644 archive/drl_pinball_new_api/validate/validate_re100.py create mode 100644 configs/CONFIG.md create mode 100644 configs/config_body.json create mode 100644 configs/config_lbm_pinball.json rename configs/{ => legacy_configs}/config_cuda.json (100%) rename configs/{ => legacy_configs}/config_flowfield.json (91%) rename configs/{ => legacy_configs}/config_gym.json (100%) delete mode 100644 docs/REFACTORING_SUMMARY.md delete mode 100644 docs/SUBMODULE_WORKFLOW.md delete mode 100644 scripts/train_ppo.py create mode 100644 src/CCD_analysis/ccd_notes.md create mode 100644 src/CCD_analysis/configs/config_cuda.json create mode 100644 src/CCD_analysis/configs/config_flowfield.json create mode 100644 src/CCD_analysis/knowledge.md create mode 100644 src/CCD_analysis/scripts/__init__.py create mode 100644 src/CCD_analysis/scripts/analysis_utils.py create mode 100644 src/CCD_analysis/scripts/cfg.py create mode 100644 src/CCD_analysis/scripts/compile_results.py create mode 100644 src/CCD_analysis/scripts/phase0_standard_freq.py create mode 100644 src/CCD_analysis/scripts/phase1_collect.py create mode 100644 src/CCD_analysis/scripts/phase2_resample.py create mode 100644 src/CCD_analysis/scripts/phase3_pod.py create mode 100644 src/CCD_analysis/scripts/phase4_ccd.py create mode 100644 src/CCD_analysis/scripts/phase5_steady.py create mode 100644 src/CCD_analysis/scripts/utils.py create mode 100644 src/CCD_analysis/scripts/validate_control.py create mode 100644 src/analysis_cloak/__init__.py create mode 100644 src/analysis_cloak/common/__init__.py create mode 100644 src/analysis_cloak/common/feature_builder.py create mode 100644 src/analysis_cloak/common/prebuild_features.py create mode 100644 src/analysis_cloak/common/run_sindy.py create mode 100644 src/analysis_cloak/common/run_sr_pareto.py create mode 100644 src/analysis_cloak/comparisons/__init__.py create mode 100644 src/analysis_cloak/comparisons/support_overlap/__init__.py create mode 100644 src/analysis_cloak/comparisons/support_overlap/run_overlap.py create mode 100644 src/analysis_cloak/scenes/karman/closed_loop/re200_freq_test.json create mode 100644 src/analysis_cloak/scenes/karman/closed_loop/test_re200_freq.py create mode 100644 src/analysis_cloak/scenes/karman/sindy/karman_sindy.json create mode 100644 src/analysis_cloak/scenes/karman/sr/karman_sr_pareto.json create mode 100644 src/analysis_cloak/scenes/steady/closed_loop/gen_steady_data.py create mode 100644 src/analysis_cloak/scenes/steady/closed_loop/vorticity_steady.png create mode 100644 src/analysis_cloak/scenes/steady/sindy/steady_sindy.json create mode 100644 src/analysis_cloak/scenes/steady/sr/steady_sr_pareto.json create mode 100644 src/analysis_crossre/analysis_notes.md create mode 100644 src/analysis_crossre/configs/config_cuda.json create mode 100644 src/analysis_crossre/configs/config_flowfield.json create mode 100644 src/analysis_crossre/scripts/cfg.py create mode 100644 src/analysis_crossre/scripts/phase1_infer.py create mode 100644 src/analysis_crossre/scripts/utils.py create mode 100644 src/analysis_crossre/steady_cloak_plan.md delete mode 100644 src/config.py create mode 100644 src/drl_pinball/__init__.py create mode 100644 src/drl_pinball/knowledge.md create mode 100644 src/drl_pinball/legacy_env/__init__.py create mode 100644 src/drl_pinball/legacy_env/legacy_env_erase.py create mode 100644 src/drl_pinball/legacy_env/legacy_env_imit.py create mode 100644 src/drl_pinball/legacy_env/legacy_env_imit_target.py create mode 100644 src/drl_pinball/legacy_env/legacy_env_karman_cloak_standard.py create mode 100644 src/drl_pinball/legacy_env/legacy_env_reduce_obs.py create mode 100644 src/drl_pinball/legacy_env/legacy_env_vortex.py create mode 100644 src/drl_pinball/legacy_env/legacy_karman_env.py create mode 100644 src/drl_pinball/legacy_test/paraview.ipynb create mode 100644 src/drl_pinball/legacy_test/uni_test.ipynb create mode 100644 src/drl_pinball/legacy_train/erase.py create mode 100644 src/drl_pinball/legacy_train/imit.py create mode 100644 src/drl_pinball/legacy_train/karman_cloak.py create mode 100644 src/drl_pinball/legacy_train/reduce_obs.py create mode 100644 src/drl_pinball/legacy_train/vortex.py delete mode 100644 src/environments/__init__.py delete mode 100644 src/environments/cfd_env.py diff --git a/.gitignore b/.gitignore index b8b7be6..86fc59c 100644 --- a/.gitignore +++ b/.gitignore @@ -48,7 +48,7 @@ coverage.xml # Environments .env .venv -env/ +# env/ venv/ ENV/ env.bak/ @@ -91,3 +91,7 @@ tensorboard/ .DS_Store .AppleDouble .LSOverride + +ref/ +docs/ +ParaView/ \ No newline at end of file diff --git a/CelerisLab b/CelerisLab index 2e05248..d5b7e98 160000 --- a/CelerisLab +++ b/CelerisLab @@ -1 +1 @@ -Subproject commit 2e052480c2a8a3c52e632eeac56348b57922ee7f +Subproject commit d5b7e98750f6ba5f6da63df1ce69c4d97aaa3413 diff --git a/LegacyCelerisLab/__init__.py b/LegacyCelerisLab/__init__.py new file mode 100644 index 0000000..b15069c --- /dev/null +++ b/LegacyCelerisLab/__init__.py @@ -0,0 +1,3 @@ +# CelerisLab/__init__.py + +from .driver import FlowField \ No newline at end of file diff --git a/LegacyCelerisLab/compiler.py b/LegacyCelerisLab/compiler.py new file mode 100644 index 0000000..c51cbe5 --- /dev/null +++ b/LegacyCelerisLab/compiler.py @@ -0,0 +1,87 @@ +# CelerisLab/kernels/compiler.py + +import subprocess +import re +import os + +from .utils import FlowFieldConfig, CudaConfig + + +def kernel_path(file_name: str) -> str: + current_dir = os.path.dirname(os.path.abspath(__file__)) + return os.path.join(current_dir, "kernels", file_name) + + +def read_lines(file_path): + with open(file_path, "r") as file: + lines = file.readlines() + return lines + + +def write_lines(file_path, lines): + with open(file_path, "w") as file: + file.writelines(lines) + + +def modify_macro(lines, macro_name, new_value): + pattern = re.compile(rf"(#define\s+{macro_name}\s+)(\S+)") + for i, line in enumerate(lines): + if pattern.match(line): + lines[i] = pattern.sub(rf"\g<1>{new_value}", line) + break + return lines + +def modify_const(lines, const_name, new_type, new_value): + pattern = re.compile(rf"(__constant__\s+)(\S+\s+{const_name}\s*=\s*)([^;]+)(;)") + for i, line in enumerate(lines): + if pattern.match(line): + lines[i] = pattern.sub(rf"\g<1>{new_type} {const_name} = {new_value}\4", line) + break + return lines + +def compile_kernel(): + subprocess.run( + [ + "nvcc", + "-ptx", + kernel_path("kernel.cu"), + "-o", + kernel_path("kernel.ptx"), + ] + ) + +def config_kernal(config_cuda: CudaConfig, config_field: FlowFieldConfig): + lines = read_lines(kernel_path("macros.h")) + lines = modify_macro(lines, "MULT_GPU", config_cuda.multi_gpu) + lines = modify_macro(lines, "NT", config_cuda.threads_per_block) + lines = modify_macro(lines, "X_1U", config_cuda.unit_dimensions[0]) + lines = modify_macro(lines, "Y_1U", config_cuda.unit_dimensions[1]) + lines = modify_macro(lines, "Z_1U", config_cuda.unit_dimensions[2]) + + if config_field.data_type == "FP32": + lb_type = "float" + else: + raise ValueError(f"Unsupported data type {config_field.data_type}.") + lines = modify_macro(lines, "LBtype", lb_type) + lines = modify_macro(lines, "UX", config_field.field_dim_in_U[0]) + lines = modify_macro(lines, "UY", config_field.field_dim_in_U[1]) + lines = modify_macro(lines, "UZ", config_field.field_dim_in_U[2]) + lines = modify_macro(lines, "NX", config_field.field_dim_in_U[0] * config_cuda.unit_dimensions[0]) + lines = modify_macro(lines, "NY", config_field.field_dim_in_U[1] * config_cuda.unit_dimensions[1]) + lines = modify_macro(lines, "NZ", config_field.field_dim_in_U[2] * config_cuda.unit_dimensions[2]) + lines = modify_macro(lines, "DIM", config_field.dimensionality) + lines = modify_macro(lines, "NQ", config_field.lattice) + lines = modify_macro(lines, "VIS", config_field.viscosity) + lines = modify_macro(lines, "U0", config_field.velocity) + + write_lines(kernel_path("macros.h"), lines) + +def config_object(n_obj: int): + lines = read_lines(kernel_path("macros.h")) + lines = modify_macro(lines, "N_OBJS", n_obj) + write_lines(kernel_path("macros.h"), lines) + +def config_sensor(n_sen: int): + lines = read_lines(kernel_path("macros.h")) + lines = modify_macro(lines, "N_SENS", n_sen) + write_lines(kernel_path("macros.h"), lines) \ No newline at end of file diff --git a/LegacyCelerisLab/driver.py b/LegacyCelerisLab/driver.py new file mode 100644 index 0000000..153770c --- /dev/null +++ b/LegacyCelerisLab/driver.py @@ -0,0 +1,409 @@ +# CelerisLab/driver.py + +import pycuda.driver as cuda +import numpy as np +import struct +from scipy.special import jv, expi +from typing import List, Tuple, Union, Optional + +from . import utils +from . import preprocess as preproc +from . import compiler + +FLUID = 0b00000001 +SOLID = 0b00000010 +GAS = 0b00000100 +INTERFACE = 0b00001000 +SENSOR = 0b00010000 +V_TAYLOR = np.int32(1) + +class FlowField: + def __init__( + self, + field_config: utils.FlowFieldConfig, + cuda_config: utils.CudaConfig, + device_id: Union[int, List[int]] = None, + ): + self.field_config = field_config + self.cuda_config = cuda_config + cuda.init() + + # Sanity checks + if cuda_config.multi_gpu: + if device_id is None or isinstance(device_id, int): + raise ValueError("Multi-GPU support requires a list of device IDs.") + # self.devices = [cuda.Device(id) for id in device_id] + raise NotImplementedError("Multi-GPU support is not implemented yet.") + else: + if isinstance(device_id, list): + if len(device_id) > 1: + raise ValueError( + "Single-GPU mode does not support multiple device IDs." + ) + device_id = device_id[0] + elif device_id is None: + device_id = 0 + utils.check_cuda_device_availability(device_id) + self.device = cuda.Device(device_id) + self.context = self.device.make_context() + + utils.check_cuda_capability(field_config, cuda_config, device_id) + + # Config kernel + compiler.config_kernal(cuda_config, field_config) + compiler.config_object(int(0)) + # compiler.config_sensor(int(0)) + + # Set constants + if field_config.data_type == "FP32": + self.DATA_TYPE = np.float32 + else: + raise ValueError(f"Unsupported data type {field_config.data_type}.") + + self.FIELD_SHAPE = tuple( + size * unit + for size, unit in zip( + field_config.field_dim_in_U, cuda_config.unit_dimensions + ) + ) + self.FIELD_SIZE = np.prod(self.FIELD_SHAPE) + self.LATTICE = field_config.lattice + self.DIM = field_config.dimensionality + if field_config.lattice == 9 and field_config.dimensionality == 2: + self.E = np.array( + [0, 0, 1, 0, 0, 1, -1, 0, 0, -1, 1, 1, -1, 1, -1, -1, 1, -1], + dtype=np.int32, + ).reshape(9, 2) + self.OPP = np.array([0, 3, 4, 1, 2, 7, 8, 5, 6], dtype=np.int32) + self.WW = np.array( + [4 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 36, 1 / 36, 1 / 36, 1 / 36], + dtype=self.DATA_TYPE, + ) + else: + raise NotImplementedError( + f"Unsupported lattice type {field_config.lattice} in {field_config.dimensionality} dimensions." + ) + + # Compile kernel + compiler.compile_kernel() + self.ptx = cuda.module_from_file(compiler.kernel_path("kernel.ptx")) + self.step = self.ptx.get_function("OneStep") + initflow = self.ptx.get_function("InitTubeFlow") + + # Initialize memory + self.ddf = np.zeros(self.FIELD_SIZE * self.LATTICE, dtype=self.DATA_TYPE) + self.ddf_save = np.zeros(self.FIELD_SIZE * self.LATTICE, dtype=self.DATA_TYPE) + self.flag = np.ones(self.FIELD_SIZE, dtype=np.uint8) + self.indx = np.zeros(self.FIELD_SIZE, dtype=np.int32) + self.delta_curve = np.zeros(0, dtype=self.DATA_TYPE) + self.vortex_config = np.zeros(7, dtype=float) + + self.ddf_gpu = cuda.mem_alloc(self.ddf.nbytes) + self.temp_gpu = cuda.mem_alloc(self.ddf.nbytes) + self.flag_gpu = cuda.mem_alloc(self.flag.nbytes) + self.indx_gpu = cuda.mem_alloc(self.indx.nbytes) + self.delta_gpu = cuda.mem_alloc(1) + self.vortex_gpu = cuda.mem_alloc(self.vortex_config.nbytes) + self.error_flag = np.zeros(1, dtype=np.uint32) + self.error_flag_gpu = cuda.mem_alloc(self.error_flag.nbytes) + self.last_error_flag = 0 + + self.objects = {} + self.action = np.zeros(0, dtype=self.DATA_TYPE) + self.obs = np.zeros(0, dtype=self.DATA_TYPE) + + initflow( + self.flag_gpu, + self.ddf_gpu, + block=(self.cuda_config.threads_per_block, 1, 1), + grid=( + int(self.FIELD_SHAPE[0] / self.cuda_config.threads_per_block), + int(self.FIELD_SHAPE[1]), + int(self.FIELD_SHAPE[2]), + ), + ) + cuda.memcpy_dtoh(self.flag, self.flag_gpu) + cuda.memcpy_dtoh(self.ddf, self.ddf_gpu) + + def add_cylinder(self, center: Tuple[float, float, float], radius: float, id_obj: Optional[int] = None): + x_c, y_c, z_c = center + + if ( + x_c - radius <= 0 + or x_c + radius >= self.FIELD_SHAPE[0] - 1 + or y_c - radius <= 0 + or y_c + radius >= self.FIELD_SHAPE[1] - 1 + ): + raise ValueError("Cylinder is out of bounds.") + + index = self.delta_curve.size if self.delta_curve.size > 0 else 0 + + if self.DATA_TYPE == np.float32: + id_object = np.int32(len(self.objects)) + # max_id = max(self.objects.keys()) + else: + raise ValueError(f"Unsupported data type {self.DATA_TYPE}.") + + for x in range(int(x_c - radius) - 1, int(x_c + radius) + 1): + for y in range(int(y_c - radius) - 1, int(y_c + radius) + 1): + if (x - x_c) ** 2 + (y - y_c) ** 2 < radius**2: + k = x + y * self.FIELD_SHAPE[0] + self.flag[k] = SOLID + delta_temp = np.zeros(11, dtype=self.DATA_TYPE) + delta_temp[0] = id_object.view(self.DATA_TYPE) + for i in range(self.LATTICE): + x_neb = x + self.E[i][0] + y_neb = y + self.E[i][1] + if (x_neb - x_c) ** 2 + (y_neb - y_c) ** 2 >= radius**2: + self.flag[k] |= INTERFACE + x_i, y_i = preproc.find_circle_intersection( + x, y, x_neb, y_neb, x_c, y_c, radius + ) + d_neb = np.sqrt((x_i - x_neb) ** 2 + (y_i - y_neb) ** 2) + delta_temp[i] = d_neb / np.sqrt( + self.E[i][0] ** 2 + self.E[i][1] ** 2 + ) + if self.flag[k] & INTERFACE: + delta_temp[9] = (y_c - y) / radius + delta_temp[10] = (x - x_c) / radius + self.delta_curve = np.concatenate( + (self.delta_curve, delta_temp) + ) + self.indx[k] = index + index += delta_temp.size + + self.objects[id_object] = { + "type": "cylinder", + "center": center, + "radius": radius, + } + + if hasattr(self, "delta_gpu"): + self.delta_gpu.free() + self.delta_gpu = cuda.mem_alloc(self.delta_curve.nbytes) + + self.action = np.zeros(len(self.objects), dtype=self.DATA_TYPE) + if hasattr(self, "action_gpu"): + self.action_gpu.free() + self.action_gpu = cuda.mem_alloc(self.action.nbytes) + + self.obs = np.zeros(len(self.objects) * self.DIM, dtype=self.DATA_TYPE) + if hasattr(self, "obs_gpu"): + self.obs_gpu.free() + self.obs_gpu = cuda.mem_alloc(self.obs.nbytes) + + cuda.memcpy_htod(self.delta_gpu, self.delta_curve) + cuda.memcpy_htod(self.flag_gpu, self.flag) + cuda.memcpy_htod(self.indx_gpu, self.indx) + + compiler.config_object(len(self.objects)) + compiler.compile_kernel() + self.ptx = cuda.module_from_file(compiler.kernel_path("kernel.ptx")) + self.step = self.ptx.get_function("OneStep") + + def add_sensor(self, center: Tuple[float, float, float], radius: float): + x_c, y_c, z_c = center + + if ( + x_c - radius <= 0 + or x_c + radius >= self.FIELD_SHAPE[0] - 1 + or y_c - radius <= 0 + or y_c + radius >= self.FIELD_SHAPE[1] - 1 + ): + raise ValueError("Sensor is out of bounds.") + + id_object = len(self.objects) + for x in range(int(x_c - radius) - 1, int(x_c + radius) + 1): + for y in range(int(y_c - radius) - 1, int(y_c + radius) + 1): + if (x - x_c) ** 2 + (y - y_c) ** 2 < radius**2: + k = x + y * self.FIELD_SHAPE[0] + self.flag[k] |= SENSOR + self.indx[k] = id_object + + self.objects[id_object] = { + "type": "sensor", + "center": center, + } + + self.action = np.zeros(len(self.objects), dtype=self.DATA_TYPE) + if hasattr(self, "action_gpu"): + self.action_gpu.free() + self.action_gpu = cuda.mem_alloc(self.action.nbytes) + + self.obs = np.zeros(len(self.objects) * self.DIM, dtype=self.DATA_TYPE) + if hasattr(self, "force_gpu"): + self.obs_gpu.free() + self.obs_gpu = cuda.mem_alloc(self.obs.nbytes) + + cuda.memcpy_htod(self.flag_gpu, self.flag) + cuda.memcpy_htod(self.indx_gpu, self.indx) + + compiler.config_object(len(self.objects)) + compiler.compile_kernel() + self.ptx = cuda.module_from_file(compiler.kernel_path("kernel.ptx")) + self.step = self.ptx.get_function("OneStep") + + def add_vortex(self, center: Tuple[float, float, float], radius: float, strength: float, direction: float, type: str): + x_c, y_c, z_c = center + + if ( + x_c - radius <= 0 + or x_c + radius >= self.FIELD_SHAPE[0] - 1 + or y_c - radius <= 0 + or y_c + radius >= self.FIELD_SHAPE[1] - 1 + ): + raise ValueError("Vortex is out of bounds.") + + if type not in ["lamb", "oseen", "taylor"]: + raise ValueError("Vortex type" + type + " is not supported.") + + x = np.linspace(-x_c, self.FIELD_SHAPE[0] - 1 - x_c, self.FIELD_SHAPE[0]) + y = np.linspace(-y_c, self.FIELD_SHAPE[1] - 1 - y_c, self.FIELD_SHAPE[1]) + X, Y = np.meshgrid(x, y) + r = np.sqrt(X**2 + Y**2) + nu = self.field_config.viscosity + theta = np.arctan2(Y, X) + psi = np.zeros_like(r) + + if type == "lamb": + b = 3.831705970207512 + n = b / radius + u0 = strength + inside = r <= radius + outside = r > radius + + psi[inside] = (2 * u0 / n / jv(0, b) * jv(1, n * r[inside]) - u0 * r[inside]) * np.sin(theta[inside]) + psi[outside] = -u0 * radius**2 / r[outside] * np.sin(theta[outside]) + + u_vor = np.gradient(psi, axis=0) + v_vor = -np.gradient(psi, axis=1) + p_vor = -2 * (np.gradient(v_vor, axis=1) - np.gradient(u_vor, axis=0)) * psi - (u_vor**2 + v_vor**2) / 2 + elif type == "oseen": + # 4 nu t = radius^2 / 4 + kappa = 2 * np.pi * radius **2 * strength + u_vor = - kappa / (2 * np.pi * r) * (1 - np.exp(-4 * r**2 / radius**2)) * np.sin(theta) + v_vor = kappa / (2 * np.pi * r) * (1 - np.exp(-4 * r**2 / radius**2)) * np.cos(theta) + zeta = 4 * r**2 / radius**2 + p_vor = -kappa**2 / 8 / np.pi**2 / r**2 * (-2 * zeta * (expi(-zeta) - expi(-2 * zeta)) + (1 - np.exp(-zeta))**2) + elif type == "taylor": + # 4 nu t = radius^2 + M = strength * np.pi * radius**4 / 8 / nu + u_vor = - M * r * 4 * nu / radius**4 * np.exp(-r**2 / radius**2) * np.sin(theta) + v_vor = M * r * 4 * nu / radius**4 * np.exp(-r**2 / radius**2) * np.cos(theta) + p_vor = -4 * M**2 * nu**2 * np.exp(-2 * r**2 / radius**2) / np.pi**2 / radius**6 + + cuda.memcpy_dtoh(self.ddf, self.ddf_gpu) + ddf_temp = self.ddf.copy().reshape((self.LATTICE, self.FIELD_SHAPE[1], self.FIELD_SHAPE[0])).transpose(2, 1, 0) + u_ddf = ddf_temp[:, :, 1] + ddf_temp[:, :, 5] + ddf_temp[:, :, 8] - ddf_temp[:, :, 3] - ddf_temp[:, :, 6] - ddf_temp[:, :, 7] + v_ddf = ddf_temp[:, :, 2] + ddf_temp[:, :, 5] + ddf_temp[:, :, 6] - ddf_temp[:, :, 4] - ddf_temp[:, :, 7] - ddf_temp[:, :, 8] + p_ddf = np.sum(ddf_temp, axis=2) / 3 + + for i in range(self.FIELD_SHAPE[0]): + for j in range(self.FIELD_SHAPE[1]): + k = i + j * self.FIELD_SHAPE[0] + if (j == 0 or j == self.FIELD_SHAPE[1] - 1) or (i == 0 or i == self.FIELD_SHAPE[0] - 1): + continue + else: + for e in range(self.LATTICE): + u = u_ddf[i, j] + u_vor[j, i] + v = v_ddf[i, j] + v_vor[j, i] + p = p_ddf[i, j] + p_vor[j, i] + eu = self.E[e][0] * u + self.E[e][1] * v + u2 = u ** 2 + v ** 2 + self.ddf[k + e * self.FIELD_SIZE] = self.WW[e] * (3 * p + 3 * eu + 4.5 * eu ** 2 - 1.5 * u2) + + cuda.memcpy_htod(self.ddf_gpu, self.ddf) + + # def add_vortex_gpu(self, center: Tuple[float, float, float], radius: float, strength: float, direction: float, type: str): + # x_c, y_c, z_c = center + + # if ( + # x_c - radius <= 0 + # or x_c + radius >= self.FIELD_SHAPE[0] - 1 + # or y_c - radius <= 0 + # or y_c + radius >= self.FIELD_SHAPE[1] - 1 + # ): + # raise ValueError("Vortex is out of bounds.") + + # if type not in ["lamb", "oseen", "taylor"]: + # raise ValueError("Vortex type" + type + " is not supported.") + + # add_vortex = self.ptx.get_function("AddVortex") + + # self.vortex_config[0:3] = np.array(center, dtype=float) + # self.vortex_config[3] = radius + # self.vortex_config[4] = strength + # self.vortex_config[5] = direction + # if type == "taylor": + # self.vortex_config[6] = + + def run(self, num_steps: int, action_target: np.ndarray): + if ( + action_target.size != len(self.objects) + or action_target.dtype != self.DATA_TYPE + ): + raise ValueError("action data type or size does not match the objects.") + elif len(self.objects) == 0: + raise ValueError("No objects have been added to the flow field.") + + weight = 0.1 + stream = cuda.Stream() + action_pinned = cuda.pagelocked_empty_like(self.action) + action_pinned[:] = self.action + obs_pinned = cuda.pagelocked_empty_like(self.obs) + self.error_flag[0] = 0 + cuda.memcpy_htod(self.error_flag_gpu, self.error_flag) + self.obs[:] = 0 + for i in range(num_steps): + action_pinned = (1 - weight) * action_pinned + weight * action_target + cuda.memcpy_htod_async(self.action_gpu, action_pinned, stream) + self.step( + self.flag_gpu, + self.ddf_gpu, + self.temp_gpu, + self.indx_gpu, + self.delta_gpu, + self.action_gpu, + self.obs_gpu, + self.error_flag_gpu, + block=(self.cuda_config.threads_per_block, 1, 1), + grid=( + int(self.FIELD_SHAPE[0] / self.cuda_config.threads_per_block), + int(self.FIELD_SHAPE[1]), + int(self.FIELD_SHAPE[2]), + ), + stream=stream, + ) + self.ddf_gpu, self.temp_gpu = self.temp_gpu, self.ddf_gpu + cuda.memcpy_dtoh_async(obs_pinned, self.obs_gpu, stream) + cuda.memset_d32_async(self.obs_gpu, 0, self.obs.size, stream) + self.obs += obs_pinned + stream.synchronize() + self.obs = (self.obs / num_steps).astype(self.DATA_TYPE) + cuda.memcpy_dtoh(self.error_flag, self.error_flag_gpu) + self.last_error_flag = int(self.error_flag[0]) + + def has_numeric_error(self) -> bool: + return bool(self.last_error_flag != 0) + + def apply_ddf(self): + cuda.memcpy_htod(self.ddf_gpu, self.ddf) + + def get_ddf(self): + cuda.memcpy_dtoh(self.ddf, self.ddf_gpu) + + def save_ddf(self): + self.ddf_save = self.ddf.copy() + + def restore_ddf(self): + self.ddf = self.ddf_save.copy() + + def __del__(self): + # Shutdown order can invalidate current context before object cleanup. + ctx = getattr(self, "context", None) + if ctx is None: + return + try: + ctx.pop() + except Exception: + pass \ No newline at end of file diff --git a/LegacyCelerisLab/kernels/D2Q9.cu b/LegacyCelerisLab/kernels/D2Q9.cu new file mode 100644 index 0000000..395d8ed --- /dev/null +++ b/LegacyCelerisLab/kernels/D2Q9.cu @@ -0,0 +1,101 @@ +#include "macros.h" +#include "const.h" + +__device__ void Index_lattice(int &x, int &y, int &k) { + // Only for D2 + x = threadIdx.x + NT * blockIdx.x; + y = blockIdx.y; + k = y * NX + x; +} + +__device__ void CollisionKernel(LBtype* g, LBtype* m) { + // Only for D2Q9 + LBtype p, u, v; + LBtype niu = 1.0 / (0.5 + 3 * VIS); + + u = (g[1]+g[5]+g[8]-g[3]-g[6]-g[7])/RHO; + v = (g[2]+g[5]+g[6]-g[4]-g[7]-g[8])/RHO; + p = (g[0]+g[1]+g[2]+g[3]+g[4]+g[5]+g[6]+g[7]+g[8])/3.0; + + m[0]= g[0] +g[1] +g[2] +g[3] +g[4] +g[5] +g[6] +g[7] +g[8]; + m[1]=-4*g[0] -g[1] -g[2] -g[3] -g[4]+2*g[5]+2*g[6]+2*g[7]+2*g[8]; + m[2]= 4*g[0]-2*g[1]-2*g[2]-2*g[3]-2*g[4] +g[5] +g[6] +g[7] +g[8]; + m[3]= g[1] -g[3] +g[5] -g[6] -g[7] +g[8]; + m[4]= -2*g[1] +2*g[3] +g[5] -g[6] -g[7] +g[8]; + m[5]= g[2] -g[4] +g[5] +g[6] -g[7] -g[8]; + m[6]= -2*g[2] +2*g[4] +g[5] +g[6] -g[7] -g[8]; + m[7]= g[1] -g[2] +g[3] -g[4]; + m[8]= g[5] -g[6] +g[7] -g[8]; + + m[0]=1.00*( 3*p -m[0]); + m[1]=1.20*(-6*p +3*RHO*(u*u+v*v)-m[1]); + m[2]=1.20*( 3*p -3*RHO*(u*u+v*v)-m[2]); + m[3]=1.00*( RHO*u -m[3]); + m[4]=1.20*(-RHO*u -m[4]); + m[5]=1.00*( RHO*v -m[5]); + m[6]=1.20*(-RHO*v -m[6]); + m[7]= niu*( RHO*(u*u-v*v) -m[7]); + m[8]= niu*( RHO*u*v -m[8]); + + g[0]=g[0]+( m[0] -m[1] +m[2] )/ 9.0; + g[1]=g[1]+(4*m[0] -m[1]-2*m[2]+6*m[3]-6*m[4] +9*m[7])/36.0; + g[2]=g[2]+(4*m[0] -m[1]-2*m[2] +6*m[5]-6*m[6]-9*m[7])/36.0; + g[3]=g[3]+(4*m[0] -m[1]-2*m[2]-6*m[3]+6*m[4] +9*m[7])/36.0; + g[4]=g[4]+(4*m[0] -m[1]-2*m[2] -6*m[5]+6*m[6]-9*m[7])/36.0; + g[5]=g[5]+(4*m[0]+2*m[1] +m[2]+6*m[3]+3*m[4]+6*m[5]+3*m[6]+9*m[8])/36.0; + g[6]=g[6]+(4*m[0]+2*m[1] +m[2]-6*m[3]-3*m[4]+6*m[5]+3*m[6]-9*m[8])/36.0; + g[7]=g[7]+(4*m[0]+2*m[1] +m[2]-6*m[3]-3*m[4]-6*m[5]-3*m[6]+9*m[8])/36.0; + g[8]=g[8]+(4*m[0]+2*m[1] +m[2]+6*m[3]+3*m[4]-6*m[5]-3*m[6]-9*m[8])/36.0; +} + +__device__ void ParabolicInlet(LBtype* f, LBtype* f_neb, LBtype y) { + LBtype p, u, v, yy; + LBtype feq1, feq5, feq8, feqn1, feqn5, feqn8; + + p=(f_neb[0]+f_neb[1]+f_neb[2]+f_neb[3]+f_neb[4]+f_neb[5]+f_neb[6]+f_neb[7]+f_neb[8])/3.0; + yy=(y-0.5*(NY-1))/(NY-2.0); + u=U0*1.5*(1-4*yy*yy); + v=0.0; + + feq1=(2*p+RHO*(2*u*u+2*u -v*v) )/ 6.0; + feq5=( p+RHO*( u*u+3*u*v+u+v*v+v))/12.0; + feq8=( p+RHO*( u*u-3*u*v+u+v*v-v))/12.0; + + u=(f_neb[1]+f_neb[5]+f_neb[8]-f_neb[3]-f_neb[6]-f_neb[7])/RHO; + v=(f_neb[2]+f_neb[5]+f_neb[6]-f_neb[4]-f_neb[7]-f_neb[8])/RHO; + + feqn1=(2*p+RHO*(2*u*u+2*u -v*v) )/ 6.0; + feqn5=( p+RHO*( u*u+3*u*v+u+v*v+v))/12.0; + feqn8=( p+RHO*( u*u-3*u*v+u+v*v-v))/12.0; + + f[1]=f_neb[1]-feqn1+feq1; + f[5]=f_neb[5]-feqn5+feq5; + f[8]=f_neb[8]-feqn8+feq8; +} + +__device__ void PressureOutlet(LBtype* f, LBtype* f_neb, LBtype y) { + // Edit to Parabolic Outlet temporarily + LBtype p, u, v, yy; + LBtype feq3, feq6, feq7, feqn3, feqn6, feqn7; + + p=0.0; + + yy=(y-0.5*(NY-1))/(NY-2.0); + u=U0*1.5*(1-4*yy*yy); + v=0.0; + + feq3=(2*p-RHO*(-2*u*u+2*u +v*v) )/ 6.0; + feq6=( p+RHO*( u*u-3*u*v-u+v*v+v))/12.0; + feq7=( p+RHO*( u*u+3*u*v-u+v*v-v))/12.0; + + u=(f_neb[1]+f_neb[5]+f_neb[8]-f_neb[3]-f_neb[6]-f_neb[7])/RHO; + v=(f_neb[2]+f_neb[5]+f_neb[6]-f_neb[4]-f_neb[7]-f_neb[8])/RHO; + // p=(f_neb[0]+f_neb[1]+f_neb[2]+f_neb[3]+f_neb[4]+f_neb[5]+f_neb[6]+f_neb[7]+f_neb[8])/3.0; + feqn3=(2*p-RHO*(-2*u*u+2*u +v*v) )/ 6.0; + feqn6=( p+RHO*( u*u-3*u*v-u+v*v+v))/12.0; + feqn7=( p+RHO*( u*u+3*u*v-u+v*v-v))/12.0; + + f[3]=f_neb[3]-feqn3+feq3; + f[6]=f_neb[6]-feqn6+feq6; + f[7]=f_neb[7]-feqn7+feq7; +} \ No newline at end of file diff --git a/LegacyCelerisLab/kernels/IO.cu b/LegacyCelerisLab/kernels/IO.cu new file mode 100644 index 0000000..e69de29 diff --git a/LegacyCelerisLab/kernels/const.h b/LegacyCelerisLab/kernels/const.h new file mode 100644 index 0000000..e21bcc3 --- /dev/null +++ b/LegacyCelerisLab/kernels/const.h @@ -0,0 +1,10 @@ +// CelerisLab/kernels/const.h + +#ifndef CONST_H +#define CONST_H + +__constant__ int e[9][2] = {{0, 0}, {1, 0}, {0, 1}, {-1, 0}, {0, -1}, {1, 1}, {-1, 1}, {-1, -1}, {1, -1}}; +__constant__ int opp[9] = {0, 3, 4, 1, 2, 7, 8, 5, 6}; +__constant__ float w[9] = {4/9., 1/9., 1/9., 1/9., 1/9., 1/36., 1/36., 1/36., 1/36.}; + +#endif \ No newline at end of file diff --git a/LegacyCelerisLab/kernels/kernel.cu b/LegacyCelerisLab/kernels/kernel.cu new file mode 100644 index 0000000..9ea7631 --- /dev/null +++ b/LegacyCelerisLab/kernels/kernel.cu @@ -0,0 +1,234 @@ +// CelerisLab/kernels/kernel.cu + +#include +#include +#include + +#include "macros.h" +#include "const.h" +#include "D2Q9.cu" + +extern "C" +{ + __global__ void OneStep(uint8_t *flag, LBtype *f, LBtype *f_temp, int32_t *indx, LBtype *delta, LBtype *action, LBtype *obs, uint32_t *error_flag) + { + __shared__ LBtype f_share[NT * NQ]; + __shared__ LBtype obs_share[(N_OBJS * DIM > 0) ? N_OBJS * DIM : 1]; + + int x, y, k; + LBtype g[NQ], m[NQ]; + Index_lattice(x, y, k); // Only for D2 + int totalCells = NX * NY; + int id = indx[k]; + + for (int i = 0; i < NQ; i++) + { + f_share[threadIdx.x + i * NT] = f[k + i * totalCells]; + } + for (int i = threadIdx.x; i < N_OBJS * DIM; i += NT) + { + obs_share[i] = 0; + } + + __syncthreads(); + + for (int i = 0; i < NQ; i++) + { + g[i] = f_share[threadIdx.x + i * NT]; + } + + if (flag[k] & FLUID) + { + CollisionKernel(g, m); + + for (int i = 0; i < NQ; i++) + { + if (isnan((double)g[i]) || isinf((double)g[i])) + { + atomicOr(error_flag, (uint32_t)1); + } + f_share[threadIdx.x + i * NT] = g[i]; + } + } + else if (flag[k] & SOLID) + { + if (x == 0) + { + for (int i = 0; i < NQ; i++) + { + m[i] = f_share[threadIdx.x + i * NT + 1]; + } + ParabolicInlet(g, m, y); + } + else if (x == NX - 1) + { + for (int i = 0; i < NQ; i++) + { + m[i] = f_share[threadIdx.x + i * NT - 1]; + } + PressureOutlet(g, m, y); + } + + for (int i = 0; i < NQ; i++) + { + if (isnan((double)g[i]) || isinf((double)g[i])) + { + atomicOr(error_flag, (uint32_t)1); + } + f_share[threadIdx.x + i * NT] = g[i]; + } + } + + __syncthreads(); + + for (int i = 0; i < NQ; i++) + { + int x_neb = x + e[i][0]; + int y_neb = y + e[i][1]; + + if (y != 0 && y != NY - 1) + { + if ((y == 1 && y_neb == 0) || (y == NY - 2 && y_neb == NY - 1)) + { + f_temp[k + opp[i] * totalCells] = f_share[threadIdx.x + i * NT]; + } + else + { + int k_neb = ((y_neb * NX + x_neb) + totalCells) % totalCells; + f_temp[k_neb + i * totalCells] = f_share[threadIdx.x + i * NT]; + } + } + } + + __syncthreads(); + + if (flag[k] & SOLID && flag[k] & INTERFACE) + { + LBtype Uw, Vw; + int id_obj = *reinterpret_cast(&delta[id]); + Uw = action[id_obj] * delta[id + 9]; + Vw = action[id_obj] * delta[id + 10]; + + int x_neb, y_neb, k_neb; + for (int i = 1; i < 9; i++) + { + x_neb = x + e[i][0]; + y_neb = y + e[i][1]; + k_neb = x_neb + y_neb * NX; + if (flag[k_neb] & FLUID) + { + LBtype q = delta[id + i]; + int k_neb2 = (y + 2 * e[i][1]) * NX + (x + 2 * e[i][0]); + LBtype temp = 6 * w[i] * (e[i][0] * Uw + e[i][1] * Vw); + f_temp[k_neb + i * totalCells] = (q * f_temp[k + opp[i] * totalCells] \ + + (1 - q) * f_temp[k_neb + opp[i] * totalCells] \ + + q * f_temp[k_neb2 + i * totalCells] + temp) / (1 + q); + f_temp[k + i * totalCells] = temp * Uw; + k_neb2 = (y - e[i][1]) * NX + (x - e[i][0]); + f_temp[k_neb2 + i * totalCells] = temp * Vw; + + temp = f_temp[k_neb + i * totalCells] + f_temp[k + opp[i] * totalCells]; + k_neb2 = (y - e[i][1]) * NX + (x - e[i][0]); + atomicAdd(&obs_share[DIM * id_obj], -temp * e[i][0] + f_temp[k + i * totalCells]); + atomicAdd(&obs_share[DIM * id_obj + 1], -temp * e[i][1] + f_temp[k_neb2 + i * totalCells]); + } + } + } + if (flag[k] & SENSOR) + { + LBtype u, v; + u = (g[1] + g[5] + g[8] - g[3] - g[6] - g[7]) / RHO; + v = (g[2] + g[5] + g[6] - g[4] - g[7] - g[8]) / RHO; + if (isnan((double)u) || isinf((double)u) || isnan((double)v) || isinf((double)v)) + { + atomicOr(error_flag, (uint32_t)1); + } + atomicAdd(&obs_share[DIM * id], u); + atomicAdd(&obs_share[DIM * id + 1], v); + } + + __syncthreads(); + + for (int i = threadIdx.x; i < N_OBJS * DIM; i += NT) + { + atomicAdd(&obs[i], obs_share[i]); + } + } + + __global__ void InitTubeFlow(uint8_t *flag, LBtype *f) + { + __shared__ LBtype f_share[NT * NQ]; + __shared__ uint8_t flag_share[NT]; + int x, y, k; + LBtype u; + Index_lattice(x, y, k); + int totalCells = NX * NY; + + flag_share[threadIdx.x] = flag[k]; + for (int i = 0; i < NQ; i++) + { + f_share[threadIdx.x + i * NT] = f[k + i * totalCells]; + } + + __syncthreads(); + + u = U0 * 1.5 * (1 - 4 * (y - 0.5 * (NY - 1)) * (y - 0.5 * (NY - 1)) / ((NY - 2) * (NY - 2))); + if (y == 0 || y == NY - 1 || x == 0 || x == NX - 1) + { + flag_share[threadIdx.x] = SOLID; + for (int i = 0; i < NQ; i++) + { + f_share[threadIdx.x + i * NT] = 0; + } + } + else + { + flag_share[threadIdx.x] = FLUID; + for (int i = 0; i < NQ; i++) + { + f_share[threadIdx.x + i * NT] = w[i] * RHO * (3 * e[i][0] * u + \ + 4.5 * e[i][0] * e[i][0] * u * u - 1.5 * u * u); + } + } + + __syncthreads(); + + flag[k] = flag_share[threadIdx.x]; + for (int i = 0; i < NQ; i++) + { + f[k + i * totalCells] = f_share[threadIdx.x + i * NT]; + } + } + + // __global__ void AddVortex(LBtype *f, int32_t *config) + // { + // __shared__ LBtype f_share[NT * NQ]; + // int x, y, k; + // LBtype u, v, u_vor, v_vor; + // Index_lattice(x, y, k); + // int totalCells = NX * NY; + + // for (int i = 0; i < NQ; i++) + // { + // f_share[threadIdx.x + i * NT] = f[k + i * totalCells]; + // } + + // __syncthreads(); + + // u = f_share[threadIdx.x + 1 * NT] - f_share[threadIdx.x + 3 * NT] + f_share[threadIdx.x + 5 * NT] - f_share[threadIdx.x + 6 * NT] - f_share[threadIdx.x + 7 * NT] + f_share[threadIdx.x + 8 * NT]; + // v = f_share[threadIdx.x + 2 * NT] - f_share[threadIdx.x + 4 * NT] + f_share[threadIdx.x + 5 * NT] + f_share[threadIdx.x + 6 * NT] - f_share[threadIdx.x + 7 * NT] - f_share[threadIdx.x + 8 * NT]; + + // if type & V_TAYLOR + // { + // u_vor = -2 * PI * U0 * sin(2 * PI * x / NX) * sin(2 * PI * y / NY); + // v_vor = 2 * PI * U0 * cos(2 * PI * x / NX) * cos(2 * PI * y / NY); + // } + // else + // { + // u_vor = 0; + // v_vor = 0; + // } + + + // } +} \ No newline at end of file diff --git a/LegacyCelerisLab/kernels/macros.h b/LegacyCelerisLab/kernels/macros.h new file mode 100644 index 0000000..a6b1542 --- /dev/null +++ b/LegacyCelerisLab/kernels/macros.h @@ -0,0 +1,37 @@ +// CelerisLab/kernels/macros.h + +// cuda parameters +#define MULT_GPU False +#define NT 128 +#define X_1U 128 +#define Y_1U 32 +#define Z_1U 1 + +// flow parameters +#define LBtype float +#define UX 10 +#define UY 16 +#define UZ 1 +#define NX 1280 +#define NY 512 +#define NZ 1 +#define DIM 2 +#define NQ 9 +#define VIS 0.008 +#define RHO 1.0 +#define U0 0.02 + +// constants +#define PI 3.141592653589793238 +#define FLUID 0b00000001 +#define SOLID 0b00000010 +#define GAS 0b00000100 +#define INTERFACE 0b00001000 +#define SENSOR 0b00010000 + +// vortex type +#define V_TAYLOR 0b00000001 + +// variables +#define N_OBJS 7 +// #define N_SENS 2 \ No newline at end of file diff --git a/LegacyCelerisLab/kernels/preproc.cu b/LegacyCelerisLab/kernels/preproc.cu new file mode 100644 index 0000000..c2b25c5 --- /dev/null +++ b/LegacyCelerisLab/kernels/preproc.cu @@ -0,0 +1,2 @@ +#include "macros.h" +#include "const.h" diff --git a/LegacyCelerisLab/preprocess.py b/LegacyCelerisLab/preprocess.py new file mode 100644 index 0000000..69aadc1 --- /dev/null +++ b/LegacyCelerisLab/preprocess.py @@ -0,0 +1,40 @@ +# CelerisLab/preprocess.py + +import math +import numpy as np +from typing import Tuple + +FLUID = 0b00000001 +SOLID = 0b00000010 +GAS = 0b00000100 +INTERFACE = 0b00001000 +SENSOR = 0b00010000 + + +def find_circle_intersection(x, y, x_neb, y_neb, xc, yc, r0): + dx, dy = x_neb - x, y_neb - y + a = dx ** 2 + dy ** 2 + b = 2 * (dx * (x - xc) + dy * (y - yc)) + c = (x - xc) ** 2 + (y - yc) ** 2 - r0 ** 2 + det = b ** 2 - 4 * a * c + + if det < 0: + return None + + t1 = (-b + math.sqrt(det)) / (2 * a) + t2 = (-b - math.sqrt(det)) / (2 * a) + + if 0 <= t1 <= 1: + return x + t1 * dx, y + t1 * dy + elif 0 <= t2 <= 1: + return x + t2 * dx, y + t2 * dy + else: + return None + +def find_sensor_area(radius): + area = 0 + for i in range(np.floor(-radius), np.ceil(radius)): + for j in range(np.floor(-radius), np.ceil(radius)): + if i ** 2 + j ** 2 <= radius ** 2: + area += 1 + return area \ No newline at end of file diff --git a/LegacyCelerisLab/utils.py b/LegacyCelerisLab/utils.py new file mode 100644 index 0000000..7a93009 --- /dev/null +++ b/LegacyCelerisLab/utils.py @@ -0,0 +1,256 @@ +# CelerisLab/utils.py + +import pycuda.driver as cuda +import subprocess +import json + +from typing import NamedTuple, Optional, List, Tuple, Union + + +class CudaDeviceInfo(NamedTuple): + name: str + compute_capability: str + multiprocessors: int + total_global_memory: int + max_shared_memory_per_block: int + max_threads_per_block: int + max_blocks_per_multiprocessor: int + device_interconnect: Optional[str] = None + + +class FlowFieldConfig(NamedTuple): + data_type: str + dimensionality: int + lattice: int + field_dim_in_U: Tuple[int, int, int] + viscosity: float + velocity: float + boundary_conditions: Tuple[str, str, str, str, str, str] + + +class CudaConfig(NamedTuple): + multi_gpu: bool + gpu_connection: str + required_cuda_capability: str + threads_per_block: int + unit_dimensions: Tuple[int, int, int] + + +def check_cuda_device_availability(device_id=0): + if cuda.Device.count() == 0: + raise RuntimeError("No CUDA device is available.") + + if device_id < 0 or device_id >= cuda.Device.count(): + raise ValueError( + f"Invalid device_id {device_id}. Must be between 0 and {cuda.Device.count() - 1}." + ) + + try: + subprocess.check_output(["nvidia-smi", "--version"]) + except subprocess.CalledProcessError: + raise RuntimeError("nvidia-smi is not available or not installed correctly.") + + +def query_cuda_device_info(device_id=0) -> CudaDeviceInfo: + check_cuda_device_availability(device_id) + + try: + output = subprocess.check_output( + ["nvidia-smi", "-q", "-d", "TOPOLOGY", "-i", str(device_id)], text=True + ) + if "NVLink" in output: + device_interconnect = "NVLink" + elif "PCIe" in output: + device_interconnect = "PCIe" + else: + device_interconnect = "Unknown" + except Exception as e: + device_interconnect = None + + device = cuda.Device(device_id) + + return CudaDeviceInfo( + name=device.name(), + compute_capability=f"{device.compute_capability()[0]}.{device.compute_capability()[1]}", + multiprocessors=device.get_attribute( + cuda.device_attribute.MULTIPROCESSOR_COUNT + ), + total_global_memory=device.total_memory(), + max_shared_memory_per_block=device.get_attribute( + cuda.device_attribute.MAX_SHARED_MEMORY_PER_BLOCK + ), + max_threads_per_block=device.get_attribute( + cuda.device_attribute.MAX_THREADS_PER_BLOCK + ), + max_blocks_per_multiprocessor=device.get_attribute( + cuda.device_attribute.MAX_BLOCKS_PER_MULTIPROCESSOR + ), + device_interconnect=device_interconnect, + ) + + +def load_flow_field_config(config_path: str) -> FlowFieldConfig: + try: + with open(config_path, "r") as file: + config = json.load(file) + + required_keys = [ + "data_type", + "dimensionality", + "lattice", + "field_dim_in_U", + "viscosity", + "boundary_conditions", + ] + if not all(key in config for key in required_keys): + raise ValueError("Missing required configuration items.") + + if config["data_type"] not in ["FP32", "FP64"]: + raise ValueError("Data type must be either FP32 or FP64.") + + if config["dimensionality"] not in [2, 3]: + raise ValueError("Dimensionality must be either 2 or 3.") + + if config["dimensionality"] == 2 and config["field_dim_in_U"][2] != 1: + raise ValueError( + "Field dimensions must be 1 in the third dimension for 2D simulations." + ) + + if config["lattice"] not in [9]: + raise ValueError("Lattice must be either 9 or 19.") + + boundary_conditions = tuple( + condition + for key in ["x", "y", "z"] + for condition in config["boundary_conditions"].get(key, []) + ) + if len(boundary_conditions) != 6: + raise ValueError("Boundary conditions must contain exactly six elements.") + + return FlowFieldConfig( + data_type=config["data_type"], + dimensionality=config["dimensionality"], + lattice=config["lattice"], + field_dim_in_U=tuple(config["field_dim_in_U"]), + viscosity=config["viscosity"], + velocity=config["velocity"], + boundary_conditions=boundary_conditions, + ) + except Exception as e: + raise RuntimeError(f"Failed to load or parse the flow field configuration: {e}") + + +def load_cuda_config(config_path: str) -> CudaConfig: + try: + with open(config_path, "r") as file: + config = json.load(file) + + required_keys = [ + "multi_gpu", + "gpu_connection", + "required_cuda_capability", + "threads_per_block", + "X_1U", + "Y_1U", + "Z_1U", + ] + + if not all(key in config for key in required_keys): + raise ValueError("Missing required configuration items.") + + return CudaConfig( + multi_gpu=config["multi_gpu"], + gpu_connection=config["gpu_connection"], + required_cuda_capability=config["required_cuda_capability"], + threads_per_block=config["threads_per_block"], + unit_dimensions=(config["X_1U"], config["Y_1U"], config["Z_1U"]), + ) + except Exception as e: + raise RuntimeError(f"Failed to load or parse the CUDA configuration: {e}") + + +def check_cuda_capability( + field_config: FlowFieldConfig, + cuda_config: CudaConfig, + device_id: Union[int, List[int]] = None, +): + SAFE_FACTOR = 0.8 + + if cuda_config.multi_gpu: + if device_id is None or isinstance(device_id, int): + raise ValueError("Multi-GPU support requires a list of device IDs.") + raise NotImplementedError("Multi-GPU support is not implemented yet.") + else: + if isinstance(device_id, list): + if len(device_id) > 1: + raise ValueError( + "Single-GPU mode does not support multiple device IDs." + ) + device_id = device_id[0] + elif device_id is None: + device_id = 0 + device_info = query_cuda_device_info(device_id) + + if device_info.compute_capability != cuda_config.required_cuda_capability: + raise ValueError( + f"Device {device_info.name} has compute capability {device_info.compute_capability}, but {cuda_config.required_cuda_capability} is required." + ) + + field_size = sum( + size * unit + for size, unit in zip( + field_config.field_dim_in_U, cuda_config.unit_dimensions + ) + ) + if ( + device_info.total_global_memory * SAFE_FACTOR + < calc_field_memory_consumption( + field_size, + field_config.dimensionality, + field_config.lattice, + field_config.data_type, + ) + ): + raise ValueError( + f"Device {device_info.name} does not have enough memory to store the flow field." + ) + + if ( + device_info.max_threads_per_block * SAFE_FACTOR + < cuda_config.threads_per_block + ): + raise ValueError( + f"Device {device_info.name} does not have enough threads per block to run the simulation." + ) + + block_size = cuda_config.threads_per_block + if ( + device_info.max_shared_memory_per_block * SAFE_FACTOR + < 2 + * calc_field_memory_consumption( + block_size, + field_config.dimensionality, + field_config.lattice, + field_config.data_type, + ) + ): + raise ValueError( + f"Device {device_info.name} does not have enough shared memory per block to run the simulation." + ) + + +def calc_field_memory_consumption( + field_size: int, dimensionality: int, directions: int, data_type: str +) -> int: + if data_type == "FP32": + data_size = 4 + elif data_type == "FP64": + data_size = 8 + else: + raise ValueError(f"Unsupported data type {data_type}.") + + return ( + field_size * directions * data_size * 2 + + field_size * dimensionality * data_size + + field_size + ) diff --git a/README.md b/README.md index 7a4d7fe..dbf8175 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,18 @@ # DynamisLab -**Machine Learning for Computational Fluid Dynamics** +**Machine Learning meets Numerical Simulation** -DynamisLab is a research framework for applying reinforcement learning and machine learning techniques to computational fluid dynamics problems. Built on top of [CelerisLab](https://github.com/frank14f/CelerisLab), it provides standardized environments and training pipelines for active flow control tasks. +DynamisLab is a research framework for combining machine learning techniques with numerical simulations. Built on top of [CelerisLab](https://github.com/frank14f/CelerisLab), it provides standardized environments and training pipelines for various ML + CFD/Physics projects. + +## Current Projects + +### 🎯 FlowStealth + +Deep Reinforcement Learning for Active Flow Control, focusing on: +- **Flow Stealth**: Drag reduction and flow signature minimization +- **Flow Illusion**: Manipulating flow patterns for deception and control +- **Methods**: DRL (PPO) + CFD (Lattice Boltzmann Method) +- **Location**: `src/flow_stealth/` ## Features @@ -10,25 +20,27 @@ DynamisLab is a research framework for applying reinforcement learning and machi - 🤖 **RL Integration**: Ready-to-use with Stable-Baselines3 and other RL libraries - 🚀 **GPU Acceleration**: Leverages CelerisLab's CUDA-accelerated LBM solver - 📊 **Experiment Tracking**: Built-in TensorBoard integration -- 🔧 **Modular Design**: Clean separation of environments, configs, and training scripts -- 📦 **Standard Structure**: Follows Python packaging best practices (src layout) +- 🔧 **Modular Design**: Organized by research projects +- 📦 **Standard Structure**: Follows Python packaging best practices ## Project Structure ``` -DynamisLabNew/ -├── src/ # Source code (src layout) -│ ├── __init__.py -│ ├── config.py # Configuration management -│ └── environments/ # Gymnasium environments +DynamisLab/ +├── src/ # Source code (organized by project) +│ └── flow_stealth/ # FlowStealth: DRL + CFD Active Control │ ├── __init__.py -│ └── cfd_env.py # CFD flow control environment +│ ├── config.py # Configuration management +│ └── environments/ # Gymnasium environments +│ ├── __init__.py +│ └── cfd_env.py # CFD flow control environment ├── scripts/ # Training and evaluation scripts -│ └── train_ppo.py # PPO training script +│ └── train_ppo.py # PPO training script for FlowStealth ├── configs/ # Configuration files │ ├── config_cuda.json # CUDA settings │ ├── config_flowfield.json # Flow field parameters │ └── config_gym.json # Environment settings +├── CelerisLab/ # CelerisLab submodule (GPU-accelerated CFD) ├── models/ # Trained model checkpoints (gitignored) ├── output/ # Training data and results (gitignored) ├── tensorboard/ # TensorBoard logs (gitignored) @@ -117,8 +129,8 @@ Then open http://localhost:6006 in your browser. ### Using the Environment Programmatically ```python -from environments import CFDFlowControlEnv -from config import load_celeris_configs +from flow_stealth.environments import CFDFlowControlEnv +from flow_stealth.config import load_celeris_configs # Load configurations config_cuda, config_field = load_celeris_configs() @@ -231,15 +243,22 @@ The main environment for active flow control around a cylinder. - Follow PEP 8 style guide - Use type hints for function signatures - Document classes and functions with docstrings -- Keep environments in `src/dynamis/environments/` +- Organize projects under `src/` (e.g., `src/flow_stealth/`) - Keep training scripts in `scripts/` - Use `config.py` for all path and configuration management -### Adding a New Environment +### Adding a New Project -1. Create new environment class in `src/dynamis/environments/` +1. Create new project directory in `src/` (e.g., `src/my_new_project/`) +2. Add `__init__.py`, `config.py`, and project-specific modules +3. Create environments in `src/my_new_project/environments/` +4. Create corresponding training scripts in `scripts/` + +### Adding a New Environment to FlowStealth + +1. Create new environment class in `src/flow_stealth/environments/` 2. Inherit from `gym.Env` -3. Register in `src/dynamis/environments/__init__.py` +3. Register in `src/flow_stealth/environments/__init__.py` 4. Create corresponding training script in `scripts/` ### Running Tests diff --git a/archive/analysis_crossre_scripts/diagnose_equivariance.py b/archive/analysis_crossre_scripts/diagnose_equivariance.py new file mode 100644 index 0000000..7d5f506 --- /dev/null +++ b/archive/analysis_crossre_scripts/diagnose_equivariance.py @@ -0,0 +1,350 @@ +# analysis_crossre/scripts/diagnose_equivariance.py +"""Phase A2-A3: diagnose PPO control-law equivariance under G operator. + +Usage:: + + conda run -n pycuda_3_10 python diagnose_equivariance.py --re 100 --device 0 + + conda run -n pycuda_3_10 python diagnose_equivariance.py --re all --device 0 + +Output per Re: ``output/analysis_crossre/diagnostic/equivariance_re{re}.json`` +""" +from __future__ import annotations + +import argparse +import json +import os +import sys +from typing import Dict, List, Tuple + +import numpy as np + +_PROJ = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..")) +if _PROJ not in sys.path: + sys.path.insert(0, _PROJ) +from LegacyCelerisLab import FlowField # noqa: E402 +from LegacyCelerisLab import utils as legacy_utils # noqa: E402 + +from utils import ( + action_to_physical, + compute_dimensionless, + apply_G_x, + apply_G_alpha, + load_ppo_model, + nu_from_re, + load_legacy_configs, + build_karman_cloak_env, + add_pinball, + build_observation, + scale_action, +) +from cfg import ( + CONFIG_DIR, + OUTPUT_DIR, + MODEL_DIR, + SAMPLE_INTERVAL, + FIFO_LEN, + CONV_LEN, + S_DIM, + A_DIM, + ACTION_SCALE, + ACTION_BIAS, + U0, + RE_CASES_TRAIN, + RE_LABEL_MAP, +) + +DATA_TYPE = np.float32 + + +def diagnose_one_re(re_code: int, ppo_device: int, cfd_device: int, output_root: str) -> dict: + """Run equivariance diagnosis for one Re case.""" + os.makedirs(output_root, exist_ok=True) + + nu = nu_from_re(re_code, u0=U0) + mu = 2.0 / re_code + label = RE_LABEL_MAP.get(re_code, f"Re{re_code}") + print(f"\n{'='*60}") + print(f"Diagnosing: {label} nu={nu:.6f} mu={mu:.6f}") + print(f"{'='*60}") + + # Build full environment (dist + sensors + pinball) + cuda_cfg, field_cfg = load_legacy_configs(CONFIG_DIR) + field_cfg = field_cfg._replace(viscosity=float(nu)) + ff = FlowField(field_cfg, cuda_cfg, device_id=cfd_device) + + # Stabilize and get to controlled state + target_states, _ = build_karman_cloak_env( + ff, u0=U0, l0=20.0, sample_interval=SAMPLE_INTERVAL, + fifo_len=FIFO_LEN, data_type=DATA_TYPE, + ) + norm = add_pinball( + ff, l0=20.0, u0=U0, sample_interval=SAMPLE_INTERVAL, + fifo_len=FIFO_LEN, data_type=DATA_TYPE, + action_bias=ACTION_BIAS, + ) + + # Load PPO model + model_path = None + for rc, mn in RE_CASES_TRAIN: + if rc == re_code: + model_path = os.path.join(MODEL_DIR, "old", f"{mn}.zip") + break + if model_path is None or not os.path.isfile(model_path): + return {"re_code": re_code, "error": f"No model for Re{re_code}"} + + model = load_ppo_model(model_path, device=f"cuda:{ppo_device}") + model.set_random_seed(0) + + # Collect rollout data with PPO + ff.restore_ddf() + ff.apply_ddf() + + # Bias FIFO + bias_action = scale_action( + np.zeros(3, dtype=np.float32), + scale=ACTION_SCALE, bias=ACTION_BIAS, u0=U0, n_total_bodies=7, + ) + from collections import deque + fifo = deque(maxlen=FIFO_LEN) + for _ in range(FIFO_LEN): + ff.context.push() + try: + ff.run(SAMPLE_INTERVAL, bias_action) + finally: + ff.context.pop() + fifo.append(ff.obs.copy()[2:14]) + + n_steps = 150 + obs_hist = np.zeros((n_steps, 12), dtype=np.float64) + alpha_hist = np.zeros((n_steps, 3), dtype=np.float64) + obs = np.zeros(S_DIM, dtype=np.float32) + + for step in range(n_steps): + action, _ = model.predict(obs, deterministic=True) + action = action.astype(np.float32).flatten() + + # Convert to physical + action_arr = scale_action( + action, scale=ACTION_SCALE, bias=ACTION_BIAS, + u0=U0, n_total_bodies=7, + ) + ff.context.push() + try: + ff.run(SAMPLE_INTERVAL, action_arr) + finally: + ff.context.pop() + + obs_slice = ff.obs.copy()[2:14] + fifo.append(obs_slice) + alpha = action_to_physical( + action.reshape(1, 3), scale=ACTION_SCALE, bias=ACTION_BIAS, u0=U0, + ).flatten() + + obs_hist[step] = obs_slice + alpha_hist[step] = alpha + obs = build_observation(obs_slice, norm) + + del ff + + # ---- Equivariance diagnosis ---- + dim = compute_dimensionless(obs_hist[:, 0:6], obs_hist[:, 6:12], u0=U0, d=20.0) + + # Compute memory terms + a_prev = np.zeros_like(alpha_hist) + a_prev2 = np.zeros_like(alpha_hist) + a_prev[1:] = alpha_hist[:-1] + a_prev2[2:] = alpha_hist[:-2] + + # Diagnostic 1: front bias check (mean of alpha_F) + mean_alpha_F = float(np.mean(alpha_hist[:, 0])) + std_alpha_F = float(np.std(alpha_hist[:, 0])) + front_bias_score = abs(mean_alpha_F) / (std_alpha_F + 1e-12) + + # Diagnostic 2: check front equivariance + # For each point, compute PPO(Gx) by feeding G-transformed obs through model + eq_front_errors = [] + eq_exchange_b_errors = [] + eq_exchange_t_errors = [] + eq_front_noise_floor = [] + + for t in range(2, n_steps): + # Get original obs and Gx + Gx = apply_G_x( + dim["u_hat_B"][t:t+1], dim["u_hat_C"][t:t+1], dim["u_hat_T"][t:t+1], + dim["v_hat_B"][t:t+1], dim["v_hat_C"][t:t+1], dim["v_hat_T"][t:t+1], + dim["Cd_F"][t:t+1], dim["Cd_T"][t:t+1], dim["Cd_B"][t:t+1], + dim["Cl_F"][t:t+1], dim["Cl_T"][t:t+1], dim["Cl_B"][t:t+1], + a_prev[t:t+1, 0], a_prev[t:t+1, 2], a_prev[t:t+1, 1], + a_prev2[t:t+1, 0] - a_prev[t:t+1, 0], + a_prev2[t:t+1, 2] - a_prev[t:t+1, 2], + a_prev2[t:t+1, 1] - a_prev[t:t+1, 1], + ) + + # Build Gx observation for PPO: we need the normalized obs + # The Gx in raw sensor/force space requires inverting the dimensionless transform + # Actually easier: compute what PPO would predict for the G state + # by transforming the raw obs and feeding it + + # Build raw obs corresponding to Gx + raw_Gx = np.zeros(12, dtype=np.float64) + # Sensors: reorder + sign flip + # Original raw: [s0_ux, s0_uy, s1_ux, s1_uy, s2_ux, s2_uy] = top, center, bottom + # G: bottom->top, center->center, top->bottom + raw_Gx[0] = obs_hist[t, 4] # s0_ux <- s2_ux (bottom -> top, streamwise no sign) + raw_Gx[1] = -obs_hist[t, 5] # s0_uy <- -s2_uy (bottom -> top, cross sign flip) + raw_Gx[2] = obs_hist[t, 2] # s1_ux maintains (center) + raw_Gx[3] = -obs_hist[t, 3] # s1_uy = -s1_uy (center cross sign flip) + raw_Gx[4] = obs_hist[t, 0] # s2_ux <- s0_ux (top -> bottom) + raw_Gx[5] = -obs_hist[t, 1] # s2_uy <- -s0_uy (top -> bottom, cross sign flip) + # Forces: reorder + sign + # ordering: [front_fx, front_fy, bottom_fx, bottom_fy, top_fx, top_fy] + # G: front_fx -> front_fx (no sign), front_fy -> -front_fy + # bottom <-> top + raw_Gx[6] = obs_hist[t, 6] # front_fx unchanged + raw_Gx[7] = -obs_hist[t, 7] # front_fy sign flip + raw_Gx[8] = obs_hist[t, 10] # bottom_fx <- top_fx + raw_Gx[9] = -obs_hist[t, 11] # bottom_fy <- -top_fy + raw_Gx[10] = obs_hist[t, 8] # top_fx <- bottom_fx + raw_Gx[11] = -obs_hist[t, 9] # top_fy <- -bottom_fy + + # Build normalized PPO observation from Gx + obs_Gx = build_observation(raw_Gx, norm) + + # Predict action for Gx + action_Gx, _ = model.predict(obs_Gx, deterministic=True) + action_Gx = action_Gx.astype(np.float32).flatten() + alpha_Gx = action_to_physical( + action_Gx.reshape(1, 3), scale=ACTION_SCALE, bias=ACTION_BIAS, u0=U0, + ).flatten() + + # What equivariance says Gx should produce (with CORRECTED G) + # G([aF, aT, aB]) = [-aF, -aB, -aT] + alpha_Gx_expected = apply_G_alpha(alpha_hist[t]) + + # Front error: PPO(Gx)[0] should == G(PPO(x))[0] = -aF(x) + eq_front_errors.append(abs(float(alpha_Gx[0]) - float(alpha_Gx_expected[0]))) + + # Rear error (CORRECTED): PPO(Gx)[1] should == G(PPO(x))[1] = -aT(x) + # PPO(Gx)[2] should == G(PPO(x))[2] = -aB(x) + # Previously this incorrectly checked alpha_B(x) == alpha_T(Gx) + eq_exchange_b_errors.append(abs(float(alpha_Gx[1]) - float(alpha_Gx_expected[1]))) + eq_exchange_t_errors.append(abs(float(alpha_Gx[2]) - float(alpha_Gx_expected[2]))) + + # Noise floor: difference between same-state replicate predictions + # (we approximate by checking prediction consistency) + action2, _ = model.predict(obs_Gx, deterministic=True) + alpha_Gx2 = action_to_physical( + action2.reshape(1, 3), scale=ACTION_SCALE, bias=ACTION_BIAS, u0=U0, + ).flatten() + eq_front_noise_floor.append(abs(float(alpha_Gx2[0]) - float(alpha_Gx[0]))) + + eq_front_errors = np.array(eq_front_errors) + eq_exchange_b = np.array(eq_exchange_b_errors) + eq_exchange_t = np.array(eq_exchange_t_errors) + eq_noise = np.array(eq_front_noise_floor) + + # Scale equivariance errors by action range for relative measure + alpha_range = float(np.max(np.abs(alpha_hist[2:]))) + rel_front_err = float(np.mean(eq_front_errors) / (alpha_range + 1e-12)) + rel_exchange_b_err = float(np.mean(eq_exchange_b) / (alpha_range + 1e-12)) + rel_exchange_t_err = float(np.mean(eq_exchange_t) / (alpha_range + 1e-12)) + + # Combined rear error (max of bottom and top) + rel_exchange_err = max(rel_exchange_b_err, rel_exchange_t_err) + + # Diagnostic 3: cross-correlation between alpha_T and -alpha_B + if len(alpha_hist) > 10: + # After initial transient + tail = n_steps // 2 + corr_TB = float(np.corrcoef(alpha_hist[tail:, 2], -alpha_hist[tail:, 1])[0, 1]) + else: + corr_TB = float("nan") + + result = { + "re_code": re_code, + "mu": mu, + "n_samples": n_steps, + "alpha_range": alpha_range, + "front_bias": { + "mean_alpha_F": mean_alpha_F, + "std_alpha_F": std_alpha_F, + "bias_over_std": front_bias_score, + "bias_significant": front_bias_score > 2.0, + }, + "equivariance_front": { + "mean_abs_error": float(np.mean(eq_front_errors)), + "max_abs_error": float(np.max(eq_front_errors)), + "relative_error": rel_front_err, + "noise_floor": float(np.mean(eq_noise)), + "signal_to_noise": float(np.mean(eq_front_errors) / (np.mean(eq_noise) + 1e-12)), + }, + "equivariance_rear_bottom": { + "mean_abs_error": float(np.mean(eq_exchange_b)), + "max_abs_error": float(np.max(eq_exchange_b)), + "relative_error": rel_exchange_b_err, + }, + "equivariance_rear_top": { + "mean_abs_error": float(np.mean(eq_exchange_t)), + "max_abs_error": float(np.max(eq_exchange_t)), + "relative_error": rel_exchange_t_err, + }, + "top_bottom_correlation": { + "corr_alphaT_vs_negAlphaB": corr_TB, + }, + "equivariance_verdict": "PASS" if (rel_front_err < 0.20 and rel_exchange_err < 0.20) else "REVIEW", + } + + print(f" Front bias: mean_alpha_F={mean_alpha_F:.6f} |bias|/std={front_bias_score:.3f}") + print(f" Front equiv err: mean={np.mean(eq_front_errors):.6f} rel={rel_front_err:.3%}") + print(f" Rear-bot err: mean={np.mean(eq_exchange_b):.6f} rel={rel_exchange_b_err:.3%}") + print(f" Rear-top err: mean={np.mean(eq_exchange_t):.6f} rel={rel_exchange_t_err:.3%}") + print(f" T vs -B corr: {corr_TB:.4f}") + print(f" Verdict: {result['equivariance_verdict']}") + + with open(os.path.join(output_root, f"equivariance_re{re_code}.json"), "w") as f: + json.dump(result, f, indent=2) + print(f" Saved to {output_root}/equivariance_re{re_code}.json") + + return result + + +def main(): + ap = argparse.ArgumentParser(description="Equivariance diagnosis for PPO cloak control") + ap.add_argument("--re", type=str, default="all", + help='Re case: 50,100,200,400, or "all"') + ap.add_argument("--device", type=int, default=0, help="GPU device for PPO model") + ap.add_argument("--cfd-device", type=int, default=2, help="GPU device for CFD simulation") + args = ap.parse_args() + + if args.re.lower() == "all": + re_list = [rc for rc, _ in RE_CASES_TRAIN] + else: + re_list = [int(args.re)] + + # Store device args for use in diagnose_one_re + device_id = args.device + cfd_device = args.cfd_device + + diag_root = os.path.join(OUTPUT_DIR, "diagnostic") + os.makedirs(diag_root, exist_ok=True) + + all_results = [] + for re_code in re_list: + res = diagnose_one_re(re_code, device_id, cfd_device, diag_root) + all_results.append(res) + + summary = { + "summary": { + "equivariance_verdicts": {r["re_code"]: r.get("equivariance_verdict", "ERROR") + for r in all_results} + }, + "details": all_results, + } + with open(os.path.join(diag_root, "equivariance_summary.json"), "w") as f: + json.dump(summary, f, indent=2) + print(f"\nSummary saved to {diag_root}/equivariance_summary.json") + + +if __name__ == "__main__": + main() diff --git a/archive/analysis_crossre_scripts/phase2_ablation.py b/archive/analysis_crossre_scripts/phase2_ablation.py new file mode 100644 index 0000000..f112ea1 --- /dev/null +++ b/archive/analysis_crossre_scripts/phase2_ablation.py @@ -0,0 +1,439 @@ +# analysis_crossre/scripts/phase2_ablation.py +"""Ablation runner: v2 baseline -> v2.1 -> v2.2 -> v2.3 -> v2.4. + +Each version differs by exactly one change from the previous. +Run specific versions via --mode. + +Usage:: + + conda run -n pycuda_3_10 python phase2_ablation.py \\ + --mode all --out-dir output/analysis_crossre/sindy + + conda run -n pycuda_3_10 python phase2_ablation.py \\ + --mode v21 --out-dir output/analysis_crossre/sindy +""" +from __future__ import annotations + +import argparse +import json +import os +import sys +from typing import Dict, List, Tuple + +import numpy as np + +from utils import ( + action_to_physical, + compute_dimensionless, + compute_physical_symbols, + fit_channel, + print_control_law, +) +from cfg import ( + OUTPUT_DIR, + RE_CASES_TRAIN, + ACTION_SCALE, + ACTION_BIAS, + U0, +) + +THRESHOLDS = [0.0, 0.001, 0.002, 0.005, 0.01, 0.015, 0.02, 0.03, 0.05, 0.1] + + +def load_case_data(re_code: int) -> Tuple: + case_dir = os.path.join(OUTPUT_DIR, f"re{re_code}") + npz_path = os.path.join(case_dir, "controlled.npz") + if not os.path.isfile(npz_path): + raise FileNotFoundError(f"Missing {npz_path}") + data = np.load(npz_path) + sensors = data["sensors"].astype(np.float64) + forces = data["forces"].astype(np.float64) + actions_norm = data["actions"].astype(np.float64) + rewards = data.get("rewards", np.zeros(sensors.shape[0])).astype(np.float64) + actions_phys = action_to_physical( + actions_norm, scale=ACTION_SCALE, bias=ACTION_BIAS, u0=U0) + mu = 2.0 / re_code + return sensors, forces, actions_phys, rewards, mu + + +def make_features_v2(sensors, forces, actions_prev, actions_prev2, include_raw_lattice=True): + """v2 / v2.1 features: raw lattice physical symbols.""" + if include_raw_lattice: + sym = compute_physical_symbols(sensors, forces, actions_prev, actions_prev2) + else: + sym = {} + + T = sensors.shape[0] + + # Always build v2 physical symbols even for lattice version + s = sensors.astype(np.float64) + f = forces.astype(np.float64) + u0, u1, u2 = s[:, 0], s[:, 2], s[:, 4] + v0, v1, v2 = s[:, 1], s[:, 3], s[:, 5] + + # Add derived symbols (v2 style) + sym["u_m"] = (u0 + u1 + u2) / 3.0 + sym["u_a"] = (u2 - u0) / 2.0 + sym["u_c"] = u1.copy() + sym["u_curv"] = u0 - 2.0 * u1 + u2 + sym["v_m"] = (v0 + v1 + v2) / 3.0 + sym["v_a"] = (v2 - v0) / 2.0 + sym["v_c"] = v1.copy() + sym["v_curv"] = v0 - 2.0 * v1 + v2 + sym["sin_ua"] = np.sin(np.pi * sym["u_a"]) + sym["cos_ua"] = np.cos(np.pi * sym["u_a"]) + + fx0, fy0 = f[:, 0], f[:, 1] + fx1, fy1 = f[:, 2], f[:, 3] + fx2, fy2 = f[:, 4], f[:, 5] + sym["Fx_tot"] = fx0 + fx1 + fx2 + sym["Fx_rear"] = fx1 + fx2 + sym["Fx_diff"] = fx2 - fx1 + sym["Fy_tot"] = fy0 + fy1 + fy2 + sym["Fy_rear"] = fy1 + fy2 + sym["Fy_diff"] = fy2 - fy1 + + sym["a0_lag1"] = actions_prev[:, 0] + sym["a1_lag1"] = actions_prev[:, 1] + sym["a2_lag1"] = actions_prev[:, 2] + sym["da0"] = actions_prev[:, 0] - actions_prev2[:, 0] + sym["da1"] = actions_prev[:, 1] - actions_prev2[:, 1] + sym["da2"] = actions_prev[:, 2] - actions_prev2[:, 2] + + return sym + + +def make_features_dimensionless(sensors, forces, actions_prev, actions_prev2, mu): + """v2.2 features: fully dimensionless.""" + dim = compute_dimensionless(sensors, forces, u0=U0, d=20.0) + T = actions_prev.shape[0] + + # Nondim actions: alpha = omega_phys / U0 + T = actions_prev.shape[0] + a_prev = np.zeros((T, 3), dtype=np.float64) + a_prev2 = np.zeros((T, 3), dtype=np.float64) + a_prev[1:] = actions_prev[1:] / U0 + a_prev2[2:] = actions_prev2[2:] / U0 + da = a_prev - a_prev2 + + # Sensor (nondim) + u_B, u_C, u_T = dim["u_hat_B"], dim["u_hat_C"], dim["u_hat_T"] + v_B, v_C, v_T = dim["v_hat_B"], dim["v_hat_C"], dim["v_hat_T"] + + sym = {} + sym["u_m"] = (u_B + u_C + u_T) / 3.0 + sym["u_a"] = (u_T - u_B) / 2.0 + sym["u_c"] = u_C.copy() + sym["u_curv"] = u_B - 2.0 * u_C + u_T + sym["v_m"] = (v_B + v_C + v_T) / 3.0 + sym["v_a"] = (v_T - v_B) / 2.0 + sym["v_c"] = v_C.copy() + sym["v_curv"] = v_B - 2.0 * v_C + v_T + sym["sin_ua"] = np.sin(np.pi * sym["u_a"]) + sym["cos_ua"] = np.cos(np.pi * sym["u_a"]) + + # Force (nondim Cd/Cl) + sym["Cd_tot"] = dim["Cd_F"] + dim["Cd_T"] + dim["Cd_B"] + sym["Cd_rear"] = dim["Cd_T"] + dim["Cd_B"] + sym["Cd_diff"] = dim["Cd_T"] - dim["Cd_B"] + sym["Cl_tot"] = dim["Cl_F"] + dim["Cl_T"] + dim["Cl_B"] + sym["Cl_rear"] = dim["Cl_T"] + dim["Cl_B"] + sym["Cl_diff"] = dim["Cl_T"] - dim["Cl_B"] + + # Memory (nondim alpha) + sym["a0_lag1"] = a_prev[:, 0] # front + sym["a1_lag1"] = a_prev[:, 1] # bottom + sym["a2_lag1"] = a_prev[:, 2] # top + sym["da0"] = da[:, 0] + sym["da1"] = da[:, 1] + sym["da2"] = da[:, 2] + + # Mu modulation + sym["mu"] = np.full(T, mu, dtype=np.float64) + sym["mu_u_a"] = sym["u_a"] * mu + sym["mu_v_a"] = sym["v_a"] * mu + sym["mu_Cd_tot"] = sym["Cd_tot"] * mu + sym["mu_Cl_diff"] = sym["Cl_diff"] * mu + + return sym + + +def build_theta(sym, feature_keys, add_bias=True): + """Build feature matrix from symbol dict.""" + T = sym[feature_keys[0]].shape[0] + cols = [] + if add_bias: + cols.append(np.ones(T, dtype=np.float64)) + for k in feature_keys: + cols.append(sym[k]) + return np.column_stack(cols) + + +# Feature set definitions +V2_BASE_KEYS = [ + "u_m", "u_a", "u_c", "u_curv", "v_a", "v_curv", + "Fx_tot", "Fx_rear", "Fx_diff", "Fy_tot", "Fy_diff", + "sin_ua", "cos_ua", + "a0_lag1", "a1_lag1", "a2_lag1", + "da0", "da1", "da2", +] + +V2_WITH_MU = V2_BASE_KEYS + [ + "mu", "mu_u_a", "mu_v_a", "mu_Cd_tot", "mu_Cl_diff", +] + +V2DIM_KEYS = [ + "u_m", "u_a", "u_c", "u_curv", "v_a", "v_curv", + "Cd_tot", "Cd_rear", "Cd_diff", "Cl_tot", "Cl_diff", + "sin_ua", "cos_ua", + "a0_lag1", "a1_lag1", "a2_lag1", + "da0", "da1", "da2", + "mu", "mu_u_a", "mu_v_a", "mu_Cd_tot", "mu_Cl_diff", +] + + +def build_dataset(re_codes, mode, use_mu=True, use_mu_nondim=True): + """Build dataset for given mode. + + Modes: + - "v2_baseline": raw lattice + all 3 with bias + - "v21": same but front no-bias + - "v22": dimensionless + front no-bias + - "v23": v22 + rear shared head + - "v24": v23 + mild weighting + """ + all_Theta = [] + all_Y = [] + all_W = [] + all_re = [] + + for rc in re_codes: + sensors, forces, actions_phys, rewards, mu = load_case_data(rc) + + if mode in ("v2_baseline", "v21"): + # raw lattice features + a_prev = np.zeros_like(actions_phys) + a_prev2 = np.zeros_like(actions_phys) + a_prev[1:] = actions_phys[:-1] + a_prev2[2:] = actions_phys[:-2] + sym = make_features_v2(sensors, forces, a_prev, a_prev2) + feature_keys = V2_WITH_MU if use_mu else V2_BASE_KEYS + sym["mu"] = np.full(sensors.shape[0], mu, dtype=np.float64) + sym["mu_u_a"] = sym["u_a"] * mu + sym["mu_v_a"] = sym["v_a"] * mu + if use_mu: + # Add mu modulated forces + sym["mu_Cd_tot"] = sym["Fx_tot"] * mu + sym["mu_Cl_diff"] = sym["Fy_diff"] * mu + Y = actions_phys.copy() + else: + # dimensionless features + a_prev_d = np.zeros_like(actions_phys) + a_prev2_d = np.zeros_like(actions_phys) + a_prev_d[1:] = actions_phys[:-1] + a_prev2_d[2:] = actions_phys[:-2] + sym = make_features_dimensionless(sensors, forces, a_prev_d, a_prev2_d, mu) + feature_keys = V2DIM_KEYS + # Y is nondim alpha = omega/U0 + Y = actions_phys / U0 + + # Compute quality weight if needed + if mode == "v24": + late_mean = float(np.mean(rewards[-80:])) + weight = np.clip(0.3 + 0.7 * late_mean / 0.7, 0.2, 1.0) + W = np.full(sensors.shape[0], weight, dtype=np.float64) + else: + W = np.ones(sensors.shape[0], dtype=np.float64) + + # Store for stacking (will trim warmup later) + all_Theta.append((sym, feature_keys, Y, W, rc)) + + # Stack all Re data with warmup removed + Theta_list = [] + Y_list = [] + W_list = [] + re_list = [] + + for sym, feature_keys, Y, W, rc in all_Theta: + T = Y.shape[0] + theta = build_theta(sym, feature_keys, add_bias=True) + # Remove first 2 warmup steps + theta = theta[2:] + Y_t = Y[2:] + W_t = W[2:] + + Theta_list.append(theta) + Y_list.append(Y_t) + W_list.append(W_t) + re_list.append(np.full(theta.shape[0], rc, dtype=np.int64)) + + Theta_stacked = np.vstack(Theta_list) + Y_stacked = np.vstack(Y_list) + W_stacked = np.concatenate(W_list) + Re_stacked = np.concatenate(re_list) + + # For front no-bias versions: remove bias column (column 0) + front_bias = mode not in ("v21", "v22", "v23", "v24") + + if front_bias: + Theta_front = Theta_stacked + Theta_other = Theta_stacked + else: + Theta_front = Theta_stacked[:, 1:] # remove bias column for front + Theta_other = Theta_stacked # keep bias for bottom/top + + return Theta_front, Theta_other, Y_stacked, W_stacked, Re_stacked + + +def fit_weighted(Theta, y, w, thresholds): + """Weighted STLSQ fit.""" + import pysindy as ps + std = np.sqrt(np.average((Theta - np.average(Theta, axis=0, weights=w))**2, + axis=0, weights=w)) + std = np.where(std < 1e-8, 1.0, std) + Theta_s = Theta / std + best = None + rows = [] + for th in thresholds: + opt = ps.STLSQ(threshold=th, alpha=1e-4, max_iter=25) + opt.fit(Theta_s, y, sample_weight=w) + coef = np.asarray(opt.coef_, dtype=np.float64).flatten() / std + y_pred = Theta @ coef + y_mean = np.average(y, weights=w) + ssr = np.sum(w * (y - y_pred)**2) + sst = np.sum(w * (y - y_mean)**2) + 1e-12 + r2 = 1.0 - ssr / sst + mae = float(np.average(np.abs(y - y_pred), weights=w)) + nz = int(np.sum(np.abs(coef) > 1e-8)) + entry = {"threshold": float(th), "nz": nz, "r2": r2, "mae": mae, "coef": coef} + rows.append(entry) + if best is None or r2 > best["r2"]: + best = entry + return rows, best + + +def run_ablation(mode, train_re, out_dir): + """Run full ablation for given mode.""" + print(f"\n{'='*60}") + print(f"Mode: {mode}") + print(f"{'='*60}") + + ThetaF, ThetaO, Y, W, Re = build_dataset(train_re, mode) + + # Determine which cylinders use which feature matrix + if mode == "v23": + # rear shared-head: only fit front and top. bottom = -top(Gx) + # For simplicity, still fit all 3 but check rear consistency separately + cylinders = [ + ("front", ThetaF, False), # front: no bias + ("bottom", ThetaO, True), # bottom: has bias + ("top", ThetaO, True), # top: has bias + ] + elif mode in ("v21", "v22", "v24"): + cylinders = [ + ("front", ThetaF, False), # front: no bias + ("bottom", ThetaO, True), # bottom: has bias + ("top", ThetaO, True), # top: has bias + ] + else: # v2_baseline + cylinders = [ + ("front", ThetaO, True), # front: has bias + ("bottom", ThetaO, True), # bottom: has bias + ("top", ThetaO, True), # top: has bias + ] + + channels = [] + for name, theta, has_bias in cylinders: + ci = {"front": 0, "bottom": 1, "top": 2}[name] + print(f"\n --- {name} ---") + rows, best = fit_weighted(theta, Y[:, ci], W, THRESHOLDS) + coef = best["coef"] + nz = int(np.sum(np.abs(coef) > 1e-8)) + print(f" {name}: R2={best['r2']:.6f} MAE={best['mae']:.6f} nz={nz}") + # Get feature names for this mode + if mode in ("v2_baseline", "v21"): + feat_names = [ + "bias", "u_m", "u_a", "u_c", "u_curv", "v_a", "v_curv", + "Fx_tot", "Fx_rear", "Fx_diff", "Fy_tot", "Fy_diff", + "sin_ua", "cos_ua", + "a0_lag1", "a1_lag1", "a2_lag1", + "da0", "da1", "da2", + "mu", "mu_u_a", "mu_v_a", "mu_Cd_tot", "mu_Cl_diff", + ] + has_mu = True + else: + feat_names = [ + "bias", "u_m", "u_a", "u_c", "u_curv", "v_a", "v_curv", + "Cd_tot", "Cd_rear", "Cd_diff", "Cl_tot", "Cl_diff", + "sin_ua", "cos_ua", + "a0_lag1", "a1_lag1", "a2_lag1", + "da0", "da1", "da2", + "mu", "mu_u_a", "mu_v_a", "mu_Cd_tot", "mu_Cl_diff", + ] + has_mu = True + + # Trim feat_names to match actual theta dimensions + actual_nf = theta.shape[1] + if len(feat_names) != actual_nf: + # Feature names don't include mu if not included, etc. + # Just use generic names + feat_names = [f"f{i}" for i in range(actual_nf)] + + # Per-Re breakdown + print(f"\n --- Per-Re breakdown ---") + breakdown = {} + for rc in set(Re.tolist()): + mask = Re == rc + ch_b = [] + for name, theta, has_bias in cylinders: + ci = {"front": 0, "bottom": 1, "top": 2}[name] + th_r = theta[mask] + yr = Y[mask, ci] + wr = W[mask] + coef = np.array([ch["best_coef"][ci] for ch in channels], dtype=np.float64).flatten() + # Actually get the right coefficient for this cylinder + coef_c = np.array(channels[ci]["best_coef"], dtype=np.float64) + y_pred = th_r @ coef_c + y_mean = np.average(yr, weights=wr) + ssr = np.sum(wr * (yr - y_pred)**2) + sst = np.sum(wr * (yr - y_mean)**2) + 1e-12 + r2 = 1.0 - ssr / sst + mae = float(np.average(np.abs(yr - y_pred), weights=wr)) + ch_b.append({"cylinder": name, "r2": float(r2), "mae": mae}) + breakdown[f"re{int(rc)}"] = ch_b + r2s = ", ".join([f"{b['cylinder']}={b['r2']:.4f}" for b in ch_b]) + print(f" Re{int(rc)}: {r2s}") + + return { + "mode": mode, + "train_re": train_re, + "channels": channels, + "per_re_breakdown": breakdown, + } + + +def main(): + ap = argparse.ArgumentParser(description="Ablation runner v2->v2.4") + ap.add_argument("--mode", type=str, default="all", + choices=["v2_baseline", "v21", "v22", "v23", "v24", "all"]) + ap.add_argument("--out-dir", type=str, default=os.path.join(OUTPUT_DIR, "sindy")) + ap.add_argument("--train-re", type=str, default="50,100,200") + args = ap.parse_args() + + train_re = [int(r) for r in args.train_re.split(",")] + os.makedirs(args.out_dir, exist_ok=True) + + modes = ["v2_baseline", "v21", "v22", "v23", "v24"] if args.mode == "all" else [args.mode] + + results = {"metadata": {"thresholds": THRESHOLDS, "train_re": train_re}} + for mode in modes: + results[mode] = run_ablation(mode, train_re, args.out_dir) + + out_path = os.path.join(args.out_dir, "ablation_results.json") + with open(out_path, "w") as f: + json.dump(results, f, indent=2) + print(f"\nSaved: {out_path}") + + +if __name__ == "__main__": + main() diff --git a/archive/analysis_crossre_scripts/phase2_control_fit.py b/archive/analysis_crossre_scripts/phase2_control_fit.py new file mode 100644 index 0000000..a57759f --- /dev/null +++ b/archive/analysis_crossre_scripts/phase2_control_fit.py @@ -0,0 +1,320 @@ +# analysis_crossre/scripts/phase2_control_fit.py +"""Phase 2 v3: dimensionless + front-no-bias + quality-weighted SINDy fitting. + +Usage:: + + conda run -n pycuda_3_10 python phase2_control_fit.py \\ + --cross-re --out-dir output/analysis_crossre/sindy + + conda run -n pycuda_3_10 python phase2_control_fit.py \\ + --leave-one-out --out-dir output/analysis_crossre/sindy +""" +from __future__ import annotations + +import argparse +import json +import os +import sys +from typing import Dict, List, Tuple + +import numpy as np + +from utils import ( + action_to_physical, + compute_dimensionless, + compute_v3_symbols, + fit_channel, + print_control_law, +) +from cfg import ( + OUTPUT_DIR, + RE_CASES_TRAIN, + ACTION_SCALE, + ACTION_BIAS, + U0, +) + +THRESHOLDS = [0.0, 0.001, 0.002, 0.005, 0.01, 0.015, 0.02, 0.03, 0.05, 0.1] + + +def load_case_data(re_code: int) -> Tuple: + """Load controlled NPZ for a single Re. + + Returns (sensors, forces, actions_phys, rewards, mu). + """ + case_dir = os.path.join(OUTPUT_DIR, f"re{re_code}") + npz_path = os.path.join(case_dir, "controlled.npz") + if not os.path.isfile(npz_path): + raise FileNotFoundError(f"Missing {npz_path}") + + data = np.load(npz_path) + sensors = data["sensors"].astype(np.float64) + forces = data["forces"].astype(np.float64) + actions_norm = data["actions"].astype(np.float64) + rewards = data.get("rewards", np.zeros(sensors.shape[0])).astype(np.float64) + + actions_phys = action_to_physical( + actions_norm, scale=ACTION_SCALE, bias=ACTION_BIAS, u0=U0) + mu = 2.0 / re_code # 1 / Re_D + return sensors, forces, actions_phys, rewards, mu + + +def compute_trajectory_weights(rewards: np.ndarray, late_window: int = 80) -> float: + """Compute a single quality weight for this trajectory. + + Uses the mean reward over the last ``late_window`` steps. + Maps to weight via quantile-based scheme. + """ + n = len(rewards) + if n < late_window: + late_mean = float(np.mean(rewards)) + else: + late_mean = float(np.mean(rewards[-late_window:])) + + # Map reward to weight via sigmoid-like scheme: + # reward 0.0 -> weight 0.1, reward 0.3 -> 0.3, reward 0.5 -> 0.6, reward 0.7 -> 0.9 + weight = 1.0 / (1.0 + np.exp(-8.0 * (late_mean - 0.4))) + return float(np.clip(weight, 0.05, 1.0)) + + +def build_dataset_v3( + re_code: int, + include_mu: bool = True, +) -> Tuple: + """Build v3 data: dimensionless features, front-no-bias, quality-weighted. + + Returns + ------- + Theta_front : (T, nf_f) for front model (no bias) + Theta_other : (T, nf_o) for top/bottom model (with bias) + Y : (T, 3) physical omegas + W : (T,) quality weight per sample + names : feature names (without "bias") + """ + sensors, forces, actions_phys, rewards, mu = load_case_data(re_code) + + # Dimensionless + dim = compute_dimensionless(sensors, forces, u0=U0, d=20.0) + + # Memory terms + a_prev = np.zeros_like(actions_phys) + a_prev2 = np.zeros_like(actions_phys) + a_prev[1:] = actions_phys[:-1] + a_prev2[2:] = actions_phys[:-2] + + # Build v3 features + Theta_f, Theta_top, names = compute_v3_symbols( + dim, a_prev, a_prev2, mu=mu, include_mu=include_mu) + + # Quality weight per sample: inherit trajectory weight + traj_weight = compute_trajectory_weights(rewards) + W = np.full(Theta_f.shape[0], traj_weight, dtype=np.float64) + + # Remove warmup (need 2 steps of memory) + Theta_f = Theta_f[2:] + Theta_top = Theta_top[2:] + Y = actions_phys[2:] + W = W[2:] + + print(f" Re{re_code}: {Theta_f.shape[0]} samples, {Theta_f.shape[1]} feats, " + f"mu={mu:.6f}, traj_weight={traj_weight:.4f}") + return Theta_f, Theta_top, Y, W, names, mu + + +def fit_channel_weighted( + Theta: np.ndarray, + y: np.ndarray, + w: np.ndarray, + thresholds: list, + alpha: float = 1e-4, + max_iter: int = 25, +) -> tuple: + """Weighted STLSQ fit.""" + import pysindy as ps + + # Weighted normalisation + std = np.sqrt(np.average((Theta - np.average(Theta, axis=0, weights=w)) ** 2, + axis=0, weights=w)) + std = np.where(std < 1e-8, 1.0, std) + Theta_s = Theta / std + + best = None + rows = [] + for th in thresholds: + opt = ps.STLSQ(threshold=th, alpha=alpha, max_iter=max_iter) + opt.fit(Theta_s, y, sample_weight=w) + coef = np.asarray(opt.coef_, dtype=np.float64).flatten() / std + y_pred = Theta @ coef + # Weighted R2 + y_mean = np.average(y, weights=w) + ssr = np.sum(w * (y - y_pred) ** 2) + sst = np.sum(w * (y - y_mean) ** 2) + 1e-12 + r2 = 1.0 - ssr / sst + mae = float(np.average(np.abs(y - y_pred), weights=w)) + nz = int(np.sum(np.abs(coef) > 1e-8)) + entry = {"threshold": float(th), "nz": nz, "r2": r2, "mae": mae, "coef": coef} + rows.append(entry) + if best is None or r2 > best["r2"]: + best = entry + return rows, best + + +def build_cross_re_v3( + train_re_codes: List[int], + include_mu: bool = True, +) -> Tuple: + """Stack multiple Re datasets with v3 features. + + Front model uses Theta_front (no bias). + Top/Bottom models use Theta_other (with bias). + + Returns three stacked datasets. + """ + all_ThetaF, all_ThetaO, all_Y, all_W, all_re = [], [], [], [], [] + names = None + + for rc in train_re_codes: + tf, to, y, w, fn, mu = build_dataset_v3(rc, include_mu=include_mu) + all_ThetaF.append(tf) + all_ThetaO.append(to) + all_Y.append(y) + all_W.append(w) + all_re.append(np.full(tf.shape[0], rc, dtype=np.int64)) + if names is None: + names = fn + + ThetaF = np.vstack(all_ThetaF) + ThetaO = np.vstack(all_ThetaO) + Y = np.vstack(all_Y) + W = np.concatenate(all_W) + Re = np.concatenate(all_re) + + print(f"\n Cross-Re: {ThetaF.shape[0]} samples, " + f"front={ThetaF.shape[1]} feats (no bias), " + f"other={ThetaO.shape[1]} feats (w/ bias)") + return ThetaF, ThetaO, Y, W, names, Re + + +def run_cross_re_fit( + train_re: List[int], + tag: str = "", + include_mu: bool = True, +) -> dict: + """Fit all 3 cylinders independently. + + Front: no bias. + Top/Bottom: with bias. + """ + ThetaF, ThetaO, Y, W, names, re_labels = build_cross_re_v3(train_re, include_mu) + + cylinders = [ + {"name": "front", "theta": ThetaF, "label": "front (no bias)"}, + {"name": "bottom", "theta": ThetaO, "label": "bottom (w/ bias)"}, + {"name": "top", "theta": ThetaO, "label": "top (w/ bias)"}, + ] + + channels = [] + for ci, cyl in enumerate(cylinders): + print(f"\n --- {tag} {cyl['label']} ---") + rows, best = fit_channel_weighted(cyl["theta"], Y[:, ci], W, THRESHOLDS) + coef = best["coef"] + print_control_law(names, coef, channel_label=f"{cyl['name']}") + print(f" R2={best['r2']:.6f} MAE={best['mae']:.6f}") + channels.append({ + "cylinder": cyl["name"], + "has_bias": cyl["name"] != "front", + "n_features": cyl["theta"].shape[1], + "best": {k: float(v) if isinstance(v, (np.floating, float)) else v + for k, v in best.items() if k != "coef"}, + "best_coef": [float(c) for c in coef], + "grid": [{k: float(v) for k, v in row.items() if k != "coef"} + for row in rows], + "feature_names": names, + }) + + # Per-Re breakdown + print(f"\n --- {tag} per-Re breakdown ---") + breakdown = {} + for re_code in set(re_labels.tolist()): + mask = re_labels == re_code + Yr, Wr = Y[mask], W[mask] + ch_b = [] + for ci, cyl in enumerate(cylinders): + th = cyl["theta"][mask] + coef = np.array(channels[ci]["best_coef"], dtype=np.float64) + y_pred = th @ coef + y_t = Yr[:, ci] + y_mean = np.average(y_t, weights=Wr) + ssr = np.sum(Wr * (y_t - y_pred) ** 2) + sst = np.sum(Wr * (y_t - y_mean) ** 2) + 1e-12 + r2 = 1.0 - ssr / sst + mae = float(np.average(np.abs(y_t - y_pred), weights=Wr)) + ch_b.append({"cylinder": cyl["name"], "r2": float(r2), "mae": mae}) + breakdown[f"re{int(re_code)}"] = ch_b + r2s = ", ".join([f"{b['cylinder']}={b['r2']:.4f}" for b in ch_b]) + print(f" Re{int(re_code)}: {r2s}") + + return { + "tag": tag, + "train_re": train_re, + "n_samples": int(ThetaF.shape[0]), + "n_features_front": int(ThetaF.shape[1]), + "n_features_other": int(ThetaO.shape[1]), + "channels": channels, + "per_re_breakdown": breakdown, + } + + +def main(): + ap = argparse.ArgumentParser(description="Phase 2 v3: dimensionless + constrained fitting") + ap.add_argument("--cross-re", action="store_true") + ap.add_argument("--leave-one-out", action="store_true") + ap.add_argument("--out-dir", type=str, default=os.path.join(OUTPUT_DIR, "sindy")) + ap.add_argument("--train-re", type=str, default="50,100,200") + ap.add_argument("--no-mu", action="store_true") + args = ap.parse_args() + + if not (args.cross_re or args.leave_one_out): + print("ERROR: specify --cross-re and/or --leave-one-out") + return 1 + + train_re = [int(r) for r in args.train_re.split(",")] + include_mu = not args.no_mu + os.makedirs(args.out_dir, exist_ok=True) + + results = { + "metadata": { + "method": "v3_dimensionless_front_nobias_weighted", + "thresholds": THRESHOLDS, + "include_mu": include_mu, + } + } + + if args.cross_re: + print("\n" + "=" * 60) + print("v3 Cross-Re unified (dimensionless + front no-bias + weighted)") + print("=" * 60) + results["cross_re"] = run_cross_re_fit( + train_re, tag="v3-cross", include_mu=include_mu) + + if args.leave_one_out: + print("\n" + "=" * 60) + print("v3 Leave-one-out cross-validation") + print("=" * 60) + loo_results = {} + for held_out in train_re: + train_set = [r for r in train_re if r != held_out] + print(f"\n--- LOO: train={train_set}, test={held_out} ---") + loo = run_cross_re_fit( + train_set, tag=f"v3-loo-{held_out}", include_mu=include_mu) + loo_results[f"holdout_{held_out}"] = loo + results["leave_one_out"] = loo_results + + out_path = os.path.join(args.out_dir, "sindy_results_v3.json") + with open(out_path, "w") as f: + json.dump(results, f, indent=2) + print(f"\nSaved: {out_path}") + + +if __name__ == "__main__": + main() diff --git a/archive/analysis_crossre_scripts/phase3_ablation_val.py b/archive/analysis_crossre_scripts/phase3_ablation_val.py new file mode 100644 index 0000000..49165f7 --- /dev/null +++ b/archive/analysis_crossre_scripts/phase3_ablation_val.py @@ -0,0 +1,267 @@ +"""Phase 3: closed-loop for ablation modes. + +Usage:: + + conda run -n pycuda_3_10 python phase3_ablation_val.py \\ + --ablation-json output/analysis_crossre/sindy/ablation_results.json \\ + --mode v21 --validate-re 70 --device 2 +""" +from __future__ import annotations + +import argparse +import json +import os +import sys +from collections import deque + +import numpy as np + +_PROJ = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..")) +if _PROJ not in sys.path: + sys.path.insert(0, _PROJ) +from LegacyCelerisLab import FlowField + +from utils import ( + nu_from_re, load_legacy_configs, build_karman_cloak_env, add_pinball, + build_observation, scale_action, action_to_physical, + compute_dimensionless, compute_physical_symbols, + save_vorticity_png, vorticity_from_ddf, compute_similarity, +) +from cfg import ( + CONFIG_DIR, OUTPUT_DIR, SAMPLE_INTERVAL, FIFO_LEN, CONV_LEN, + S_DIM, ACTION_SCALE, ACTION_BIAS, U0, +) + +DATA_TYPE = np.float32 + + +def load_ablation_coef(ablation_path, mode, channels_to_load=("front", "bottom", "top")): + """Load coefficients for a specific ablation mode.""" + with open(ablation_path) as f: + data = json.load(f) + + mode_data = data[mode] + coefs = {} + for ch in mode_data["channels"]: + name = ch["cylinder"] + if name in channels_to_load: + coefs[name] = { + "coef": np.array(ch["best_coef"], dtype=np.float64), + "has_bias": ch["has_bias"], + } + return coefs + + +def predict_ablation(obs_slice, actions_prev, actions_prev2, coefs, mu, mode, u0=0.01): + """Predict action using ablation mode coefficients. + + obs_slice: (12,) raw lattice [sensor(6), force(6)] + """ + sensors = obs_slice[0:6].astype(np.float64).reshape(1, 6) + forces = obs_slice[6:12].astype(np.float64).reshape(1, 6) + a_prev = actions_prev.astype(np.float64).reshape(1, 3) + a_prev2 = actions_prev2.astype(np.float64).reshape(1, 3) + + is_dim = "v22" in mode or "v23" in mode or "v24" in mode or mode in ("v2_dimless",) + + if is_dim: + dim = compute_dimensionless(sensors, forces, u0=u0, d=20.0) + # Build dimensionless features + sym = _build_dimensionless_features(dim, a_prev[0], a_prev2[0], mu) + Y_scale = 1.0 / u0 # predict nondim alpha = omega / U0 + else: + # Lattice features (v2/v21) + a_prev_f = np.zeros((1, 3), dtype=np.float64) + a_prev2_f = np.zeros((1, 3), dtype=np.float64) + a_prev_f[0] = a_prev + a_prev2_f[0] = a_prev2 + sym = _build_lattice_features(sensors, forces, a_prev_f, a_prev2_f, mu) + Y_scale = 1.0 # predict raw omega + + # Build feature vector + feat_keys = [k for k in sym.keys() if k not in ("mu", "mu_u_a", "mu_v_a", "mu_Cd_tot", "mu_Cl_diff")] + feat_keys_mu = ["mu", "mu_u_a", "mu_v_a", "mu_Cd_tot", "mu_Cl_diff"] + + feats = [] + for k in feat_keys: + feats.append(float(sym[k][0]) if isinstance(sym[k], np.ndarray) else float(sym[k])) + if mu > 0: + for k in feat_keys_mu: + feats.append(float(sym[k][0]) if isinstance(sym[k], np.ndarray) else float(sym[k])) + + omega = np.zeros(3, dtype=np.float64) + for ci, name in enumerate(["front", "bottom", "top"]): + c = coefs.get(name) + if c is None: + continue + coef_arr = c["coef"] + has_bias = c["has_bias"] + + if has_bias: + feat_vec = np.array([1.0] + feats) if len(coef_arr) == len(feats) + 1 else np.array(feats) + else: + feat_vec = np.array(feats) if len(coef_arr) == len(feats) else np.array([1.0] + feats) + + if len(feat_vec) != len(coef_arr): + feat_vec = np.array(feats) # fallback + + pred = float(feat_vec @ coef_arr) * Y_scale + omega[ci] = pred + + return omega + + +def _build_lattice_features(sensors, forces, a_prev, a_prev2, mu): + """Build v2-style lattice features. All args 2D (1, N).""" + # Ensure 2D + if sensors.ndim == 1: + sensors = sensors.reshape(1, -1) + if forces.ndim == 1: + forces = forces.reshape(1, -1) + if a_prev.ndim == 1: + a_prev = a_prev.reshape(1, -1) + if a_prev2.ndim == 1: + a_prev2 = a_prev2.reshape(1, -1) + sym = compute_physical_symbols(sensors, forces, a_prev, a_prev2) + sym["mu"] = np.array([mu]) + sym["mu_u_a"] = sym["u_a"] * mu + sym["mu_v_a"] = sym["v_a"] * mu + sym["mu_Cd_tot"] = sym["Fx_tot"] * mu + sym["mu_Cl_diff"] = sym["Fy_diff"] * mu + return sym + + +def _build_dimensionless_features(dim, a_prev, a_prev2, mu): + """Build dimensionless features. a_prev/a_prev2 are 1D (3,) arrays.""" + if a_prev.ndim > 1: + a_prev = a_prev.flatten() + if a_prev2.ndim > 1: + a_prev2 = a_prev2.flatten() + """Build dimensionless features.""" + T = 1 + u_B, u_C, u_T = dim["u_hat_B"][0], dim["u_hat_C"][0], dim["u_hat_T"][0] + v_B, v_C, v_T = dim["v_hat_B"][0], dim["v_hat_C"][0], dim["v_hat_T"][0] + + sym = {} + sym["u_m"] = np.array([(u_B + u_C + u_T) / 3.0]) + sym["u_a"] = np.array([(u_T - u_B) / 2.0]) + sym["u_c"] = np.array([u_C]) + sym["u_curv"] = np.array([u_B - 2.0*u_C + u_T]) + sym["v_a"] = np.array([(v_T - v_B) / 2.0]) + sym["v_curv"] = np.array([v_B - 2.0*v_C + v_T]) + sym["sin_ua"] = np.sin(np.pi * sym["u_a"]) + sym["cos_ua"] = np.cos(np.pi * sym["u_a"]) + + sym["Cd_tot"] = np.array([dim["Cd_F"][0] + dim["Cd_T"][0] + dim["Cd_B"][0]]) + sym["Cd_rear"] = np.array([dim["Cd_T"][0] + dim["Cd_B"][0]]) + sym["Cd_diff"] = np.array([dim["Cd_T"][0] - dim["Cd_B"][0]]) + sym["Cl_tot"] = np.array([dim["Cl_F"][0] + dim["Cl_T"][0] + dim["Cl_B"][0]]) + sym["Cl_diff"] = np.array([dim["Cl_T"][0] - dim["Cl_B"][0]]) + + # Nondim actions + a_prev_n = a_prev / U0 + a_prev2_n = a_prev2 / U0 + sym["a0_lag1"] = np.array([a_prev_n[0]]) + sym["a1_lag1"] = np.array([a_prev_n[1]]) + sym["a2_lag1"] = np.array([a_prev_n[2]]) + sym["da0"] = np.array([a_prev_n[0] - a_prev2_n[0]]) + sym["da1"] = np.array([a_prev_n[1] - a_prev2_n[1]]) + sym["da2"] = np.array([a_prev_n[2] - a_prev2_n[2]]) + + sym["mu"] = np.array([mu]) + sym["mu_u_a"] = sym["u_a"] * mu + sym["mu_v_a"] = sym["v_a"] * mu + sym["mu_Cd_tot"] = sym["Cd_tot"] * mu + sym["mu_Cl_diff"] = sym["Cl_diff"] * mu + + return sym + + +def run_closed_loop(re_code, coefs, mode, device_id, output_root, n_steps=100): + """Run closed-loop for one ablation mode.""" + os.makedirs(output_root, exist_ok=True) + nu = nu_from_re(re_code, u0=U0) + mu = 2.0 / re_code + + cuda_cfg, field_cfg = load_legacy_configs(CONFIG_DIR) + field_cfg = field_cfg._replace(viscosity=float(nu)) + ff = FlowField(field_cfg, cuda_cfg, device_id=device_id) + + target_states, _ = build_karman_cloak_env( + ff, u0=U0, l0=20.0, sample_interval=SAMPLE_INTERVAL, + fifo_len=FIFO_LEN, data_type=DATA_TYPE) + norm = add_pinball( + ff, l0=20.0, u0=U0, sample_interval=SAMPLE_INTERVAL, + fifo_len=FIFO_LEN, data_type=DATA_TYPE, action_bias=ACTION_BIAS) + + np.savez(os.path.join(output_root, "target.npz"), target_states=target_states) + + # Controlled rollout + ff.restore_ddf() + ff.apply_ddf() + fifo = deque(maxlen=FIFO_LEN) + bias_action = scale_action( + np.zeros(3, dtype=np.float32), scale=ACTION_SCALE, + bias=ACTION_BIAS, u0=U0, n_total_bodies=7) + for _ in range(FIFO_LEN): + ff.run(SAMPLE_INTERVAL, bias_action) + fifo.append(ff.obs.copy()[2:14]) + + sens_sc = [] + actions_prev = action_to_physical( + np.zeros((1,3), dtype=np.float32), scale=ACTION_SCALE, + bias=ACTION_BIAS, u0=U0).flatten() + actions_prev2 = actions_prev.copy() + + for step in range(n_steps): + obs_slice = fifo[-1] if len(fifo) > 0 else np.zeros(12, dtype=np.float32) + omega_pred = predict_ablation(obs_slice, actions_prev, actions_prev2, coefs, mu, mode, u0=U0) + + norm_action = (omega_pred / U0 - np.array(ACTION_BIAS, dtype=np.float64)) / ACTION_SCALE + norm_action = np.clip(norm_action, -1.0, 1.0).astype(np.float32) + action_arr = scale_action( + norm_action, scale=ACTION_SCALE, bias=ACTION_BIAS, u0=U0, n_total_bodies=7) + ff.run(SAMPLE_INTERVAL, action_arr) + + obs_slice_new = ff.obs.copy()[2:14] + fifo.append(obs_slice_new) + sens_sc.append(obs_slice_new[0:6]) + actions_prev2 = actions_prev.copy() + actions_prev = omega_pred.copy() + + sens_arr = np.array(sens_sc, dtype=np.float32) + sim = compute_similarity(target_states, sens_arr, CONV_LEN) + + omega_vort = vorticity_from_ddf(ff, u0=U0) + save_vorticity_png(os.path.join(output_root, f"vorticity_{mode}.png"), + omega_vort, title=f"Re{re_code} {mode}") + + del ff + result = {"re_code": re_code, "mode": mode, "similarity": sim} + with open(os.path.join(output_root, "result.json"), "w") as f: + json.dump(result, f, indent=2) + print(f" Re{re_code} {mode}: similarity={sim:.4f}") + return result + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("--ablation-json", type=str, required=True) + ap.add_argument("--mode", type=str, default="v21") + ap.add_argument("--validate-re", type=str, default="70") + ap.add_argument("--device", type=int, default=2) + ap.add_argument("--steps", type=int, default=100) + ap.add_argument("--out-dir", type=str, default=os.path.join(OUTPUT_DIR, "sindy_val")) + args = ap.parse_args() + + validate_re = [int(r) for r in args.validate_re.split(",")] + coefs = load_ablation_coef(args.ablation_json, args.mode) + os.makedirs(args.out_dir, exist_ok=True) + + for rc in validate_re: + out_sub = os.path.join(args.out_dir, f"re{rc}") + run_closed_loop(rc, coefs, args.mode, args.device, out_sub, n_steps=args.steps) + + +if __name__ == "__main__": + main() diff --git a/archive/analysis_crossre_scripts/phase3_validate.py b/archive/analysis_crossre_scripts/phase3_validate.py new file mode 100644 index 0000000..51dbcc4 --- /dev/null +++ b/archive/analysis_crossre_scripts/phase3_validate.py @@ -0,0 +1,306 @@ +# analysis_crossre/scripts/phase3_validate.py +"""Phase 3: closed-loop validation using cross-Re SINDy control law. + +Usage:: + + conda run -n pycuda_3_10 python phase3_validate.py \\ + --device 2 --out-dir output/analysis_crossre/sindy_val + + conda run -n pycuda_3_10 python phase3_validate.py \\ + --validate-re 35,70,150 --device 2 + + conda run -n pycuda_3_10 python phase3_validate.py \\ + --baseline-only --validate-re 35 --device 2 +""" +from __future__ import annotations + +import argparse +import json +import os +import sys +import time +from collections import deque + +import numpy as np + +_PROJ = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..")) +if _PROJ not in sys.path: + sys.path.insert(0, _PROJ) +from LegacyCelerisLab import FlowField # noqa: E402 + +from utils import ( + nu_from_re, + load_legacy_configs, + build_karman_cloak_env, + add_pinball, + build_observation, + scale_action, + action_to_physical, + compute_dimensionless, + compute_v3_symbols, + save_vorticity_png, + vorticity_from_ddf, + compute_similarity, +) +from cfg import ( + CONFIG_DIR, + OUTPUT_DIR, + MODEL_DIR, + SAMPLE_INTERVAL, + FIFO_LEN, + CONV_LEN, + S_DIM, + A_DIM, + ACTION_SCALE, + ACTION_BIAS, + U0, + RE_CASES_TRAIN, + RE_CASES_VALIDATION, + RE_LABEL_MAP, +) + +DATA_TYPE = np.float32 + + +def load_cross_re_coef(sindy_results_path: str, threshold: float) -> dict: + """Load v3 cross-Re coefficients. + + Returns dict ``{cylinder_name: {"coef": np.ndarray, "feat_names": list, "has_bias": bool}}`` + """ + with open(sindy_results_path) as f: + data = json.load(f) + + cross = data["cross_re"] + coefs = {} + for ch_entry in cross["channels"]: + name = ch_entry["cylinder"] + feat_names = ch_entry["feature_names"] + coef_full = np.array(ch_entry["best_coef"], dtype=np.float64) + has_bias = ch_entry["has_bias"] + + scale = np.max(np.abs(coef_full)) + if scale > 0 and threshold > 0: + mask = np.abs(coef_full) / scale >= threshold + else: + mask = np.ones_like(coef_full, dtype=bool) + coef = coef_full * mask + + nz = int(np.sum(mask)) + print(f" {name}: total={len(coef_full)} nz={nz} threshold={threshold} " + f"R2={ch_entry['best']['r2']:.4f}") + + coefs[name] = {"coef": coef, "feat_names": feat_names, + "has_bias": has_bias, "nz": nz, "r2": ch_entry["best"]["r2"]} + return coefs + + +def predict_omega_v3( + obs_slice: np.ndarray, + actions_prev: np.ndarray, + actions_prev2: np.ndarray, + coefs: dict, + mu: float, + u0: float = 0.01, +) -> np.ndarray: + """Predict physical omega using v3 dimensionless features. + + Front: no bias term. + Bottom/Top: with bias term. + All 3 independently (no exchange symmetry constraint). + + Parameters + ---------- + obs_slice : (12,) raw [sensor(6), force(6)] in lattice units + actions_prev : (3,) omega(t-1) + actions_prev2 : (3,) omega(t-2) + """ + sensors = obs_slice[0:6].astype(np.float64).reshape(1, 6) + forces = obs_slice[6:12].astype(np.float64).reshape(1, 6) + a_prev = actions_prev.astype(np.float64).reshape(1, 3) + a_prev2 = actions_prev2.astype(np.float64).reshape(1, 3) + + # Dimensionless + dim = compute_dimensionless(sensors, forces, u0=u0, d=20.0) + + # Build v3 features + Theta_f, Theta_top, names = compute_v3_symbols( + dim, a_prev, a_prev2, mu=mu, include_mu=(mu > 0)) + + # Predict + omega = np.zeros(3, dtype=np.float64) + omega[0] = float(Theta_f[0] @ coefs["front"]["coef"]) # front (no bias) + omega[1] = float(Theta_top[0] @ coefs["bottom"]["coef"]) # bottom + omega[2] = float(Theta_top[0] @ coefs["top"]["coef"]) # top + + return omega + + +def run_sindy_controlled( + re_code: int, + coefs: dict, + device_id: int, + output_root: str, + *, + n_steps: int = 150, +) -> dict: + """Run closed-loop validation with SINDy control law.""" + os.makedirs(output_root, exist_ok=True) + + nu = nu_from_re(re_code, u0=U0) + mu = 2.0 / re_code # 1 / Re_D + label = RE_LABEL_MAP.get(re_code, f"Re{re_code}") + print(f"\n{'='*60}") + print(f"SINDy Validation: {label} nu={nu:.6f} mu={mu:.6f}") + print(f"{'='*60}") + + # Build environment (same as Phase 1) + cuda_cfg, field_cfg = load_legacy_configs(CONFIG_DIR) + field_cfg = field_cfg._replace(viscosity=float(nu)) + + # Phase 1: dist + sensors + target + ff = FlowField(field_cfg, cuda_cfg, device_id=device_id) + target_states, _ = build_karman_cloak_env( + ff, u0=U0, l0=20.0, sample_interval=SAMPLE_INTERVAL, + fifo_len=FIFO_LEN, data_type=DATA_TYPE, + ) + + # Phase 2: pinball + norm + norm = add_pinball( + ff, l0=20.0, u0=U0, sample_interval=SAMPLE_INTERVAL, + fifo_len=FIFO_LEN, data_type=DATA_TYPE, + action_bias=ACTION_BIAS, + ) + np.savez(os.path.join(output_root, "target.npz"), target_states=target_states) + + # --- Uncontrolled rollout --- + print(" uncontrolled rollout ...") + ff.restore_ddf() + ff.apply_ddf() + sens_unc, forc_unc = [], [] + for _ in range(n_steps): + ff.run(SAMPLE_INTERVAL, np.zeros(7, dtype=DATA_TYPE)) + obs_slice = ff.obs.copy()[2:14] + sens_unc.append(obs_slice[0:6]) + forc_unc.append(obs_slice[6:12]) + np.savez(os.path.join(output_root, "uncontrolled.npz"), + sensors=np.array(sens_unc, dtype=np.float32), + forces=np.array(forc_unc, dtype=np.float32)) + + # Uncontrolled vorticity + omega_unc = vorticity_from_ddf(ff, u0=U0) + save_vorticity_png(os.path.join(output_root, "vorticity_uncontrolled.png"), + omega_unc, title=f"{label} uncontrolled") + + # --- SINDy controlled rollout --- + print(f" SINDy controlled rollout ({n_steps} steps) ...") + ff.restore_ddf() + ff.apply_ddf() + + # Bias FIFO + fifo = deque(maxlen=FIFO_LEN) + bias_action = scale_action( + np.zeros(3, dtype=np.float32), + scale=ACTION_SCALE, bias=ACTION_BIAS, u0=U0, n_total_bodies=7, + ) + for _ in range(FIFO_LEN): + ff.run(SAMPLE_INTERVAL, bias_action) + fifo.append(ff.obs.copy()[2:14]) + + sens_sc, forc_sc, omega_sc = [], [], [] + omega_bias = action_to_physical( + np.zeros((1, 3), dtype=np.float32), + scale=ACTION_SCALE, bias=ACTION_BIAS, u0=U0, + ).flatten() + actions_prev = omega_bias.copy() + actions_prev2 = omega_bias.copy() + + for step in range(n_steps): + obs_slice = fifo[-1] if len(fifo) > 0 else np.zeros(12, dtype=np.float32) + + omega_pred = predict_omega_v3(obs_slice, actions_prev, actions_prev2, coefs, mu, u0=U0) + omega_sc.append(omega_pred.copy()) + + # Convert action to legacy array and apply + norm_action = (omega_pred / U0 - np.array(ACTION_BIAS, dtype=np.float64)) / ACTION_SCALE + norm_action = np.clip(norm_action, -1.0, 1.0).astype(np.float32) + action_arr = scale_action( + norm_action, + scale=ACTION_SCALE, bias=ACTION_BIAS, u0=U0, n_total_bodies=7, + ) + ff.run(SAMPLE_INTERVAL, action_arr) + + obs_slice_new = ff.obs.copy()[2:14] + fifo.append(obs_slice_new) + sens_sc.append(obs_slice_new[0:6]) + forc_sc.append(obs_slice_new[6:12]) + actions_prev = omega_pred + + sens_sc_arr = np.array(sens_sc, dtype=np.float32) + forc_sc_arr = np.array(forc_sc, dtype=np.float32) + omega_sc_arr = np.array(omega_sc, dtype=np.float32) + np.savez(os.path.join(output_root, "sindy_controlled.npz"), + sensors=sens_sc_arr, forces=forc_sc_arr, + omegas=omega_sc_arr) + + # Vorticity + omega_vort = vorticity_from_ddf(ff, u0=U0) + save_vorticity_png(os.path.join(output_root, "vorticity_sindy_controlled.png"), + omega_vort, title=f"{label} SINDy-controlled") + + # Similarity + sim = compute_similarity(target_states, sens_sc_arr, CONV_LEN) + print(f" SINDy similarity: {sim:.4f}") + + del ff + result = {"re_code": re_code, "mu": mu, + "sindy_similarity": sim, + "n_steps": n_steps} + with open(os.path.join(output_root, "result.json"), "w") as f: + json.dump(result, f, indent=2) + return result + + +def main(): + ap = argparse.ArgumentParser(description="Phase 3: cross-Re SINDy validation") + ap.add_argument("--sindy-results", type=str, + default=os.path.join(OUTPUT_DIR, "sindy", "sindy_results_v3.json"), + help="Path to Phase 2 SINDy results JSON (v3 dimensionless)") + ap.add_argument("--validate-re", type=str, default="35,70,150", + help="Comma-separated validation Re codes") + ap.add_argument("--device", type=int, default=0, help="GPU device ID") + ap.add_argument("--steps", type=int, default=150, + help="Number of inference steps") + ap.add_argument("--threshold", type=float, default=0.002, + help="SINDy sparsity threshold (default: 0.002)") + ap.add_argument("--out-dir", type=str, + default=os.path.join(OUTPUT_DIR, "sindy_val"), + help="Output root for validation results") + args = ap.parse_args() + + validate_re = [int(r) for r in args.validate_re.split(",")] + os.makedirs(args.out_dir, exist_ok=True) + + # Load cross-Re coefficients + print(f"\nLoading cross-Re coefficients from {args.sindy_results}") + coefs = load_cross_re_coef(args.sindy_results, args.threshold) + for name in ["front", "bottom", "top"]: + print(f" {name}: nz={coefs[name]['nz']}, R2={coefs[name]['r2']:.4f}, " + f"threshold={args.threshold}") + + t_start = time.time() + + # Run for each validation Re + for re_code in validate_re: + out_dir = os.path.join(args.out_dir, f"re{re_code}") + result = run_sindy_controlled( + re_code, coefs, args.device, out_dir, n_steps=args.steps, + ) + print(f" Done: Re{re_code} -> {out_dir}") + + elapsed = time.time() - t_start + print(f"\nTotal time: {elapsed:.1f}s") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/archive/analysis_crossre_scripts/validate_v22.py b/archive/analysis_crossre_scripts/validate_v22.py new file mode 100644 index 0000000..60b038d --- /dev/null +++ b/archive/analysis_crossre_scripts/validate_v22.py @@ -0,0 +1,197 @@ +# analysis_crossre/scripts/validate_v22.py +"""Validate v22: v2 coefficients + front bias zeroed. +Direct standalone script to avoid JSON format issues. + +Usage: + conda run -n pycuda_3_10 python validate_v22.py --re 70 --device 2 +""" +import argparse +import json +import os +import sys +from collections import deque + +import numpy as np + +_PROJ = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..")) +if _PROJ not in sys.path: + sys.path.insert(0, _PROJ) +from LegacyCelerisLab import FlowField +from LegacyCelerisLab import utils as legacy_utils + +from utils import ( + nu_from_re, action_to_physical, scale_action, build_karman_cloak_env, + add_pinball, build_observation, compute_physical_symbols, + save_vorticity_png, vorticity_from_ddf, compute_similarity, + load_legacy_configs, +) +from cfg import ( + OUTPUT_DIR, SAMPLE_INTERVAL, FIFO_LEN, CONV_LEN, S_DIM, + ACTION_SCALE, ACTION_BIAS, U0, CONFIG_DIR, +) + +DATA_TYPE = np.float32 + +# v2 feature keys (matching sindy_results_v2.json layout exactly) +V2_FEAT_KEYS = [ + "u_m", "u_a", "u_c", "v_a", + "Fx_tot", "Fx_rear", "Fy_tot", "Fy_diff", + "sin_ua", "cos_ua", + "a0_lag1", "a1_lag1", "a2_lag1", + "da0", "da1", "da2", +] +V2_MU_KEYS = ["mu", "mu_u_a", "mu_v_a", "mu_Fx_tot", "mu_Fy_diff", "mu_Fy_tot"] +V2_N_FEAT_NOBIAS = len(V2_FEAT_KEYS) + len(V2_MU_KEYS) # 22 +V2_N_FEAT_BIAS = 1 + V2_N_FEAT_NOBIAS # 23 + + +def build_feature_vec(obs_slice, actions_prev, actions_prev2, mu, add_bias): + """Build a single feature vector matching v2 feature layout.""" + sensors = obs_slice[0:6].astype(np.float64).reshape(1, 6) + forces = obs_slice[6:12].astype(np.float64).reshape(1, 6) + ap = actions_prev.astype(np.float64).reshape(1, 3) + ap2 = actions_prev2.astype(np.float64).reshape(1, 3) + + sym = compute_physical_symbols(sensors, forces, ap, ap2) + # Add mu terms + sym["mu"] = np.array([mu]) + sym["mu_u_a"] = sym["u_a"] * mu + sym["mu_v_a"] = sym["v_a"] * mu + sym["mu_Fx_tot"] = sym["Fx_tot"] * mu + sym["mu_Fy_diff"] = sym["Fy_diff"] * mu + sym["mu_Fy_tot"] = sym["Fy_tot"] * mu + + vals = [] + if add_bias: + vals.append(1.0) + for k in V2_FEAT_KEYS: + vals.append(float(sym[k][0])) + for k in V2_MU_KEYS: + vals.append(float(sym[k][0])) + return np.array(vals, dtype=np.float64) + + +def load_v2_coefs(v2_path): + """Load v2 coefficients, zero front bias.""" + with open(v2_path) as f: + data = json.load(f) + cross = data["cross_re"] + coefs_list = cross["channels"] # 3 channels: 0=front, 1=bottom, 2=top + + # Zero front bias + coefs_list[0]["best_coef"][0] = 0.0 + + names = ["front", "bottom", "top"] + result = {} + for i, name in enumerate(names): + coef_list = coefs_list[i]["best_coef"] + # Check if first is bias (it is for v2) + has_bias = True + if name == "front": + has_bias = True # v2 has bias for all, we just zeroed it + result[name] = { + "coef": np.array(coef_list, dtype=np.float64), + "has_bias": True, # v2 has bias for all channels + } + return result + + +def predict(obs_slice, a_prev, a_prev2, coefs, mu): + """Predict physical omega using v2 coefficients.""" + omega = np.zeros(3, dtype=np.float64) + for i, name in enumerate(["front", "bottom", "top"]): + c = coefs[name] + feat = build_feature_vec(obs_slice, a_prev, a_prev2, mu, add_bias=c["has_bias"]) + omega[i] = float(feat @ c["coef"]) + return omega + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("--re", type=int, default=70) + ap.add_argument("--device", type=int, default=2) + ap.add_argument("--steps", type=int, default=100) + ap.add_argument("--out-dir", type=str, default=os.path.join(OUTPUT_DIR, "sindy_val")) + ap.add_argument("--v2-results", type=str, + default=os.path.join(OUTPUT_DIR, "sindy", "sindy_results_v2.json")) + args = ap.parse_args() + + re_code = args.re + mu = 2.0 / re_code + output_root = os.path.join(args.out_dir, f"re{re_code}") + os.makedirs(output_root, exist_ok=True) + + print(f"\n=== v22 validation: Re{re_code} (mu={mu:.6f}) ===") + + # Load v2 coefs (front bias zeroed) + coefs = load_v2_coefs(args.v2_results) + for name in ["front", "bottom", "top"]: + print(f" {name}: {len(coefs[name]['coef'])} coefs, " + f"bias={coefs[name]['coef'][0]:.6f}") + + # Build environment (same as phase1) + cuda_cfg, field_cfg = load_legacy_configs(CONFIG_DIR) + field_cfg = field_cfg._replace(viscosity=float(nu_from_re(re_code, u0=U0))) + ff = FlowField(field_cfg, cuda_cfg, device_id=args.device) + + target_states, _ = build_karman_cloak_env( + ff, u0=U0, l0=20.0, sample_interval=SAMPLE_INTERVAL, + fifo_len=FIFO_LEN, data_type=DATA_TYPE) + norm = add_pinball( + ff, l0=20.0, u0=U0, sample_interval=SAMPLE_INTERVAL, + fifo_len=FIFO_LEN, data_type=DATA_TYPE, action_bias=ACTION_BIAS) + + # Controlled rollout + ff.restore_ddf() + ff.apply_ddf() + fifo = deque(maxlen=FIFO_LEN) + bias_action = scale_action(np.zeros(3, dtype=np.float32), + scale=ACTION_SCALE, bias=ACTION_BIAS, + u0=U0, n_total_bodies=7) + for _ in range(FIFO_LEN): + ff.run(SAMPLE_INTERVAL, bias_action) + fifo.append(ff.obs.copy()[2:14]) + + sens_sc = [] + a_prev = action_to_physical(np.zeros((1,3), dtype=np.float32), + scale=ACTION_SCALE, bias=ACTION_BIAS, u0=U0).flatten() + a_prev2 = a_prev.copy() + + for step in range(args.steps): + obs_slice = fifo[-1] if len(fifo) > 0 else np.zeros(12, dtype=np.float32) + omega = predict(obs_slice, a_prev, a_prev2, coefs, mu) + + # Apply action (convert to normalized for legacy run()) + norm_action = (omega / U0 - np.array(ACTION_BIAS, dtype=np.float64)) / ACTION_SCALE + norm_action = np.clip(norm_action, -1.0, 1.0).astype(np.float32) + action_arr = scale_action(norm_action, scale=ACTION_SCALE, + bias=ACTION_BIAS, u0=U0, n_total_bodies=7) + ff.run(SAMPLE_INTERVAL, action_arr) + + obs_slice_new = ff.obs.copy()[2:14] + fifo.append(obs_slice_new) + sens_sc.append(obs_slice_new[0:6]) + a_prev2 = a_prev.copy() + a_prev = omega.copy() + + sens_arr = np.array(sens_sc, dtype=np.float32) + sim = compute_similarity(target_states, sens_arr, CONV_LEN) + print(f" v22 similarity: {sim:.4f}") + + # Vorticity + omega_vort = vorticity_from_ddf(ff, u0=U0) + save_vorticity_png(os.path.join(output_root, "vorticity_v22.png"), + omega_vort, title=f"Re{re_code} v22 (front no-bias)") + + # Save result + result = {"re_code": re_code, "mode": "v22", "similarity": sim} + with open(os.path.join(output_root, "result_v22.json"), "w") as f: + json.dump(result, f, indent=2) + + del ff + print(f" Done -> {output_root}") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/archive/analysis_crossre_scripts/validate_v23.py b/archive/analysis_crossre_scripts/validate_v23.py new file mode 100644 index 0000000..4569402 --- /dev/null +++ b/archive/analysis_crossre_scripts/validate_v23.py @@ -0,0 +1,237 @@ +# analysis_crossre/scripts/validate_v23.py +"""Validate v23: front no-bias + rear shared-head. + +Front: v2 coeffs with bias=0. +Top: v2 coeffs unchanged. +Bottom: -top(Gx), using G-transformed raw observations. + +Usage: + conda run -n pycuda_3_10 python validate_v23.py --re 70 --device 2 +""" +import argparse +import json +import os +import sys +from collections import deque + +import numpy as np + +_PROJ = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..")) +if _PROJ not in sys.path: + sys.path.insert(0, _PROJ) +from LegacyCelerisLab import FlowField + +from utils import ( + nu_from_re, action_to_physical, scale_action, build_karman_cloak_env, + add_pinball, build_observation, compute_physical_symbols, + save_vorticity_png, vorticity_from_ddf, compute_similarity, + load_legacy_configs, +) +from cfg import ( + OUTPUT_DIR, SAMPLE_INTERVAL, FIFO_LEN, CONV_LEN, S_DIM, + ACTION_SCALE, ACTION_BIAS, U0, CONFIG_DIR, +) + +DATA_TYPE = np.float32 + +# v2 feature keys matching sindy_results_v2.json +V2_FEAT_KEYS = [ + "u_m", "u_a", "u_c", "v_a", + "Fx_tot", "Fx_rear", "Fy_tot", "Fy_diff", + "sin_ua", "cos_ua", + "a0_lag1", "a1_lag1", "a2_lag1", + "da0", "da1", "da2", +] +V2_MU_KEYS = ["mu", "mu_u_a", "mu_v_a", "mu_Fx_tot", "mu_Fy_diff", "mu_Fy_tot"] + + +def apply_G_raw(obs_slice, a_prev, a_prev2): + """Apply mirror operator G to raw observations and actions. + + obs_slice: (12,) [s0_ux,s0_uy, s1_ux,s1_uy, s2_ux,s2_uy, front_fx,front_fy, bot_fx,bot_fy, top_fx,top_fy] + a_prev: (3,) [aF, aB, aT] + a_prev2: (3,) [aF_prev2, aB_prev2, aT_prev2] + + Returns (G_obs, G_a_prev, G_a_prev2) + """ + # Sensors: top<->bottom swap, cross components negate + G_obs = np.zeros(12, dtype=np.float64) + G_obs[0] = obs_slice[4] # s0_ux <- s2_ux (streamwise: no sign) + G_obs[1] = -obs_slice[5] # s0_uy <- -s2_uy (cross: negate) + G_obs[2] = obs_slice[2] # s1_ux unchanged + G_obs[3] = -obs_slice[3] # s1_uy negate + G_obs[4] = obs_slice[0] # s2_ux <- s0_ux + G_obs[5] = -obs_slice[1] # s2_uy <- -s0_uy + + # Forces: front unchanged (but lift sign flips), bottom<->top with sign flips + G_obs[6] = obs_slice[6] # front_fx unchanged + G_obs[7] = -obs_slice[7] # front_fy negate + G_obs[8] = obs_slice[10] # bot_fx <- top_fx + G_obs[9] = -obs_slice[11] # bot_fy <- -top_fy + G_obs[10] = obs_slice[8] # top_fx <- bot_fx + G_obs[11] = -obs_slice[9] # top_fy <- -bot_fy + + # Actions: all negate, B<->T swap + G_a_prev = np.array([-a_prev[0], -a_prev[2], -a_prev[1]], dtype=np.float64) + G_a_prev2 = np.array([-a_prev2[0], -a_prev2[2], -a_prev2[1]], dtype=np.float64) + + return G_obs, G_a_prev, G_a_prev2 + + +def build_v2_feature_vec(obs_slice, actions_prev, actions_prev2, mu, add_bias): + """Build feature vector matching v2 layout.""" + sensors = obs_slice[0:6].astype(np.float64).reshape(1, 6) + forces = obs_slice[6:12].astype(np.float64).reshape(1, 6) + ap = actions_prev.astype(np.float64).reshape(1, 3) + ap2 = actions_prev2.astype(np.float64).reshape(1, 3) + + sym = compute_physical_symbols(sensors, forces, ap, ap2) + sym["mu"] = np.array([mu]) + sym["mu_u_a"] = sym["u_a"] * mu + sym["mu_v_a"] = sym["v_a"] * mu + sym["mu_Fx_tot"] = sym["Fx_tot"] * mu + sym["mu_Fy_diff"] = sym["Fy_diff"] * mu + sym["mu_Fy_tot"] = sym["Fy_tot"] * mu + + vals = [] + if add_bias: + vals.append(1.0) + for k in V2_FEAT_KEYS: + vals.append(float(sym[k][0])) + for k in V2_MU_KEYS: + vals.append(float(sym[k][0])) + return np.array(vals, dtype=np.float64) + + +def load_v2_coefs(v2_path): + """Load v2 coefficients. Zero front bias. Return front + top only.""" + with open(v2_path) as f: + data = json.load(f) + cross = data["cross_re"] + coefs_list = cross["channels"] + + # Zero front bias + coefs_list[0]["best_coef"][0] = 0.0 + + return { + "front": { + "coef": np.array(coefs_list[0]["best_coef"], dtype=np.float64), + "has_bias": True, + }, + "top": { + "coef": np.array(coefs_list[2]["best_coef"], dtype=np.float64), + "has_bias": True, + }, + } + + +def predict_v23(obs_slice, a_prev, a_prev2, coefs, mu): + """Predict physical omega using v23 shared-head. + + Front: v2 coefs (bias zeroed). + Top: v2 coefs unchanged. + Bottom: -top(Gx). + """ + # Front prediction + feat = build_v2_feature_vec(obs_slice, a_prev, a_prev2, mu, add_bias=coefs["front"]["has_bias"]) + front = float(feat @ coefs["front"]["coef"]) + + # Top prediction (from original state) + feat_top = build_v2_feature_vec(obs_slice, a_prev, a_prev2, mu, add_bias=coefs["top"]["has_bias"]) + top = float(feat_top @ coefs["top"]["coef"]) + + # Bottom = -top(Gx) + G_obs, G_a_prev, G_a_prev2 = apply_G_raw(obs_slice, a_prev, a_prev2) + feat_G = build_v2_feature_vec(G_obs, G_a_prev, G_a_prev2, mu, add_bias=coefs["top"]["has_bias"]) + top_at_Gx = float(feat_G @ coefs["top"]["coef"]) + bottom = -top_at_Gx + + return np.array([front, bottom, top], dtype=np.float64) + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("--re", type=int, default=70) + ap.add_argument("--device", type=int, default=2) + ap.add_argument("--steps", type=int, default=100) + ap.add_argument("--out-dir", type=str, default=os.path.join(OUTPUT_DIR, "sindy_val")) + ap.add_argument("--v2-results", type=str, + default=os.path.join(OUTPUT_DIR, "sindy", "sindy_results_v2.json")) + args = ap.parse_args() + + re_code = args.re + mu = 2.0 / re_code + output_root = os.path.join(args.out_dir, f"re{re_code}") + os.makedirs(output_root, exist_ok=True) + + print(f"\n=== v23 validation: Re{re_code} (mu={mu:.6f}) ===") + + coefs = load_v2_coefs(args.v2_results) + for name in ["front", "top"]: + print(f" {name}: {len(coefs[name]['coef'])} coefs") + + # Build environment + cuda_cfg, field_cfg = load_legacy_configs(CONFIG_DIR) + field_cfg = field_cfg._replace(viscosity=float(nu_from_re(re_code, u0=U0))) + ff = FlowField(field_cfg, cuda_cfg, device_id=args.device) + + target_states, _ = build_karman_cloak_env( + ff, u0=U0, l0=20.0, sample_interval=SAMPLE_INTERVAL, + fifo_len=FIFO_LEN, data_type=DATA_TYPE) + norm = add_pinball( + ff, l0=20.0, u0=U0, sample_interval=SAMPLE_INTERVAL, + fifo_len=FIFO_LEN, data_type=DATA_TYPE, action_bias=ACTION_BIAS) + + # Controlled rollout + ff.restore_ddf() + ff.apply_ddf() + fifo = deque(maxlen=FIFO_LEN) + bias_action = scale_action(np.zeros(3, dtype=np.float32), + scale=ACTION_SCALE, bias=ACTION_BIAS, + u0=U0, n_total_bodies=7) + for _ in range(FIFO_LEN): + ff.run(SAMPLE_INTERVAL, bias_action) + fifo.append(ff.obs.copy()[2:14]) + + sens_sc = [] + a_prev = action_to_physical(np.zeros((1,3), dtype=np.float32), + scale=ACTION_SCALE, bias=ACTION_BIAS, u0=U0).flatten() + a_prev2 = a_prev.copy() + + for step in range(args.steps): + obs_slice = fifo[-1] if len(fifo) > 0 else np.zeros(12, dtype=np.float32) + omega = predict_v23(obs_slice, a_prev, a_prev2, coefs, mu) + + # Apply action + norm_action = (omega / U0 - np.array(ACTION_BIAS, dtype=np.float64)) / ACTION_SCALE + norm_action = np.clip(norm_action, -1.0, 1.0).astype(np.float32) + action_arr = scale_action(norm_action, scale=ACTION_SCALE, + bias=ACTION_BIAS, u0=U0, n_total_bodies=7) + ff.run(SAMPLE_INTERVAL, action_arr) + + obs_slice_new = ff.obs.copy()[2:14] + fifo.append(obs_slice_new) + sens_sc.append(obs_slice_new[0:6]) + a_prev2 = a_prev.copy() + a_prev = omega.copy() + + sens_arr = np.array(sens_sc, dtype=np.float32) + sim = compute_similarity(target_states, sens_arr, CONV_LEN) + print(f" v23 similarity: {sim:.4f}") + + # Vorticity + omega_vort = vorticity_from_ddf(ff, u0=U0) + save_vorticity_png(os.path.join(output_root, "vorticity_v23.png"), + omega_vort, title=f"Re{re_code} v23 (shared-head)") + + result = {"re_code": re_code, "mode": "v23", "similarity": sim} + with open(os.path.join(output_root, "result_v23.json"), "w") as f: + json.dump(result, f, indent=2) + + del ff + print(f" Done -> {output_root}") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/archive/dante_pinball/dante_pinball/legacy/d1a3o12_250421_dante_v6.py b/archive/dante_pinball/dante_pinball/legacy/d1a3o12_250421_dante_v6.py new file mode 100644 index 0000000..0170e75 --- /dev/null +++ b/archive/dante_pinball/dante_pinball/legacy/d1a3o12_250421_dante_v6.py @@ -0,0 +1,755 @@ +""" +DANTE v6 (full-adjustable, no SINDy pretraining) + +Core decisions for this version: +1) Disable SINDy prior structure usage entirely. +2) Use full simplified basis pool and optimize all controller coefficients. +3) Keep minimal loop: no resume/checkpoint restore, live DB save + reward-only TB. +4) Continuous rollout objective with protective reset only on done/truncated. +""" + +import argparse +import json +import os +import pickle +import shutil +import sys +import time +from pathlib import Path +from typing import Dict, List + +import numpy as np + +os.environ["MKL_THREADING_LAYER"] = "GNU" +os.environ["OMP_NUM_THREADS"] = "16" +os.environ["MKL_NUM_THREADS"] = "16" + +try: + from torch.utils.tensorboard import SummaryWriter +except Exception: + SummaryWriter = None + + +CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) +ROOT = os.path.abspath(os.path.join(CURRENT_DIR, os.pardir)) +os.chdir(CURRENT_DIR) +sys.path.insert(0, os.path.join(ROOT, "DANTE")) + +from dante.obj_functions import ObjectiveFunction +from dante.tree_exploration import TreeExploration + +from dante_v6_surrogate_torch import FlowControlSurrogateV6 +from dante_pinball.env.gym_env_dante_total_force import CustomEnv + + +N_OBS = 2 +N_ACT = 3 +BIAS_SCALE = 1.0 +COEF_SCALE = 2.0 + +# Full simplified basis pool: all terms are trainable in v6. +FULL_BASIS_TERMS = [ + "obs0", "obs1", "dobs0", "dobs1", + "sin_obs0", "sin_obs1", "cos_obs0", "cos_obs1", + "tanh_obs0", "tanh_obs1", + "act0_l1", "act1_l1", "act2_l1", +] + +BASIS_PROFILES = { + # Compact profile (<20 dims) with derivative + nonlinear terms. + "compact_deriv_nl": ["obs0", "obs1", "dobs0", "dobs1", "tanh_obs1"], + # Constrained search top performer (<20 dims) with nonlinear + history. + "compact_nl_hist": ["obs1", "sin_obs0", "cos_obs0", "act1_l1"], +} + + +# Fixed run configuration (kept in-file for reproducibility and stricter control) +V6_CONFIG = { + "name": "d1a3o12_250421_forces02_dante_v6", + "device_id": 1, + "surrogate_gpu_id": 1, + "eval_steps": 300, + "tail_steps": 100, + "startup_steps": 0, + "max_recover_resets": 1, + "reset_each_candidate": True, + "obs_fail_bound": 2.0, + "obs_clip_bound": 3.0, + "basis_profile": "compact_deriv_nl", + # Use d-dependent initialization, then clamp into [min_num_initial, max_num_initial]. + "num_initial_per_dim": 10, + "min_num_initial": 100, + "max_num_initial": 240, + "samples_per_acq": 18, + "max_init_attempts_factor": 2.0, + "surrogate_mode": "ensemble", # mlp | cnn | ensemble + # Match PPO script budget: 100 learn iterations * 2048 rollout steps + "target_cfd_steps": 204800*2, + # Keep consistent with surrogate study runs in this repo. + "surrogate_epochs": 400, +} + + +class NullWriter: + def add_scalar(self, *_args, **_kwargs) -> None: + pass + + def close(self) -> None: + pass + + +class LinearBasisController: + def __init__(self, basis_terms: List[str]): + self.basis_terms = list(basis_terms) + self.num_basis = len(self.basis_terms) + self.total_params = N_ACT * (1 + self.num_basis) + + self.params = np.zeros(self.total_params, dtype=np.float64) + self.obs_l1 = np.zeros(2, dtype=np.float64) + self.prev_action = np.zeros(3, dtype=np.float64) + + def reset_state(self, obs0: np.ndarray) -> None: + obs0 = np.asarray(obs0, dtype=np.float64).reshape(-1) + if obs0.size < 2: + if obs0.size == 1: + obs0 = np.array([obs0[0], obs0[0]], dtype=np.float64) + else: + obs0 = np.zeros(2, dtype=np.float64) + self.obs_l1 = obs0[:2].copy() + self.prev_action = np.zeros(3, dtype=np.float64) + + def set_params(self, x: np.ndarray) -> None: + x = np.asarray(x, dtype=np.float64).reshape(-1) + if x.size != self.total_params: + raise ValueError(f"controller params mismatch: {x.size} != {self.total_params}") + self.params = np.clip(x, -1.0, 1.0) + + def _feature_dict(self, obs: np.ndarray) -> Dict[str, float]: + o = np.asarray(obs, dtype=np.float64).reshape(-1) + if o.size < 2: + if o.size == 1: + o = np.array([o[0], o[0]], dtype=np.float64) + else: + o = np.zeros(2, dtype=np.float64) + + o0, o1 = float(o[0]), float(o[1]) + o0_l1, o1_l1 = float(self.obs_l1[0]), float(self.obs_l1[1]) + a0, a1, a2 = float(self.prev_action[0]), float(self.prev_action[1]), float(self.prev_action[2]) + + return { + "obs0": o0, + "obs1": o1, + "dobs0": o0 - o0_l1, + "dobs1": o1 - o1_l1, + "sin_obs0": float(np.sin(np.pi * o0)), + "sin_obs1": float(np.sin(np.pi * o1)), + "cos_obs0": float(np.cos(np.pi * o0)), + "cos_obs1": float(np.cos(np.pi * o1)), + "tanh_obs0": float(np.tanh(o0)), + "tanh_obs1": float(np.tanh(o1)), + "act0_l1": a0, + "act1_l1": a1, + "act2_l1": a2, + } + + def predict(self, obs: np.ndarray) -> np.ndarray: + feat = self._feature_dict(obs) + out = np.zeros(3, dtype=np.float64) + stride = 1 + self.num_basis + + for ch in range(3): + off = ch * stride + qb = np.tanh(1.25 * self.params[off]) + y = BIAS_SCALE * qb + for k, term in enumerate(self.basis_terms): + qk = np.tanh(1.25 * self.params[off + 1 + k]) + y += (COEF_SCALE * qk) * feat.get(term, 0.0) + out[ch] = y + + out = np.clip(out, -1.0, 1.0) + + obs2 = np.asarray(obs, dtype=np.float64).reshape(-1) + if obs2.size < 2: + if obs2.size == 1: + obs2 = np.array([obs2[0], obs2[0]], dtype=np.float64) + else: + obs2 = np.zeros(2, dtype=np.float64) + + self.obs_l1 = obs2[:2].copy() + self.prev_action = out.copy() + return out.astype(np.float32) + + +class FlowControlObjectiveV6(ObjectiveFunction): + def __init__( + self, + basis_terms: List[str], + eval_steps: int = 200, + tail_steps: int = 100, + startup_steps: int = 200, + max_recover_resets: int = 1, + turn: float = 0.05, + reset_each_candidate: bool = True, + obs_fail_bound: float = 2.0, + obs_clip_bound: float = 3.0, + ): + self.eval_steps = int(eval_steps) + self.tail_steps = int(tail_steps) + self.startup_steps = int(startup_steps) + self.max_recover_resets = int(max_recover_resets) + self.turn = float(turn) + self.reset_each_candidate = bool(reset_each_candidate) + self.obs_fail_bound = float(obs_fail_bound) + self.obs_clip_bound = float(obs_clip_bound) + + self.basis_terms = list(basis_terms) + self.controller = LinearBasisController(self.basis_terms) + self.dims = int(self.controller.total_params) + + self.lb = -1.0 * np.ones(self.dims) + self.ub = 1.0 * np.ones(self.dims) + + self.env = None + self._device_id = 1 + self.obs_current = None + self.recover_count = 0 + + def init_env(self, device_id: int = 1) -> None: + self._device_id = int(device_id) + if self.env is not None: + self.env.close() + self.env = CustomEnv( + device_id=int(device_id), + obs_fail_bound=float(self.obs_fail_bound), + obs_clip_bound=float(self.obs_clip_bound), + ) + self._hard_reset_and_stabilize() + + def _hard_reset_and_stabilize(self) -> None: + obs, _ = self.env.reset() + obs = np.asarray(obs, dtype=np.float32) + action = np.zeros(3, dtype=np.float32) + + for _ in range(int(self.startup_steps)): + obs, _, done, trunc, _ = self.env.step(action) + if done or trunc: + obs, _ = self.env.reset() + + self.obs_current = np.asarray(obs, dtype=np.float32) + self.controller.reset_state(self.obs_current) + + @staticmethod + def _tail_mean(rewards: List[float], tail_steps: int) -> float: + rr = np.asarray(rewards, dtype=np.float64) + if rr.size == 0: + return 0.0 + tail = rr[-tail_steps:] if rr.size >= tail_steps else rr + return float(np.mean(tail)) + + def evaluate_candidate(self, x: np.ndarray) -> Dict: + x = self._preprocess(x) + self.controller.set_params(x) + + if self.env is None: + self.init_env(self._device_id) + + # DANTE candidate evaluations are isolated from each other. + if self.reset_each_candidate: + self._hard_reset_and_stabilize() + + for attempt in range(int(self.max_recover_resets) + 1): + obs = np.asarray(self.obs_current, dtype=np.float32) + self.controller.reset_state(obs) + + rewards: List[float] = [] + steps = 0 + done = False + trunc = False + last_info: Dict = {} + + for _ in range(int(self.eval_steps)): + action = self.controller.predict(obs) + obs, reward, done, trunc, step_info = self.env.step(action) + if isinstance(step_info, dict): + last_info = step_info + rewards.append(float(reward)) + steps += 1 + if done or trunc: + break + + if done or trunc and attempt < int(self.max_recover_resets): + self.recover_count += 1 + self._hard_reset_and_stabilize() + continue + + self.obs_current = np.asarray(obs, dtype=np.float32) + y = self._tail_mean(rewards, tail_steps=int(self.tail_steps)) + return { + "reward": float(y), + "scaled": float(y * 100.0), + "steps": int(steps), + "done": bool(done), + "truncated": bool(trunc), + "recoveries_used": int(attempt), + "failure_code": int(last_info.get("failure_code", 0)), + } + + return { + "reward": 0.0, + "scaled": 0.0, + "steps": 0, + "done": True, + "truncated": True, + "recoveries_used": int(self.max_recover_resets), + "failure_code": 1, + } + + def scaled(self, y: float) -> float: + return float(y * 100.0) + + def __call__(self, x: np.ndarray, apply_scaling: bool = False, track: bool = True) -> float: + info = self.evaluate_candidate(x) + y = float(info["reward"]) + return self.scaled(y) if apply_scaling else y + + +def parse_args() -> argparse.Namespace: + p = argparse.ArgumentParser(description="DANTE v6 full-adjustable (no SINDy pretraining)") + p.add_argument( + "--name", + type=str, + default=V6_CONFIG["name"], + help="Optional run name override. Core budgets remain fixed in-file.", + ) + p.add_argument( + "--basis-profile", + type=str, + default=V6_CONFIG["basis_profile"], + choices=sorted(BASIS_PROFILES.keys()), + help="Controller basis profile to optimize.", + ) + return p.parse_args() + + +def sample_params(dims: int, turn: float) -> np.ndarray: + x = np.random.uniform(-1.0, 1.0, size=dims) + x = np.round(x / turn) * turn + return np.clip(x, -1.0, 1.0) + + +def save_live_db(path: str, x: np.ndarray, y: np.ndarray, meta: Dict) -> None: + np.savez(path, input_x=x, input_y=y, meta_json=np.array([json.dumps(meta, ensure_ascii=False)])) + + +def print_progress_line( + phase: str, + idx: int, + total: int, + reward: float, + best_reward: float, + recover_total: int, + elapsed_sec: float, + global_step: int, + total_expected: int, +) -> None: + print( + f"[{phase}] {idx}/{total} | reward={reward:.4f} | best={best_reward:.4f} | " + f"recover={recover_total} | eval={global_step}/{total_expected} | elapsed={elapsed_sec:.1f}s", + flush=True, + ) + + +def main() -> None: + args = parse_args() + + name = str(args.name) + cfg = dict(V6_CONFIG) + cfg["name"] = name + + basis_profile = str(args.basis_profile) + basis_terms = list(BASIS_PROFILES[basis_profile]) + + target_evals = int(cfg["target_cfd_steps"] // cfg["eval_steps"]) + + model_dir = os.path.join(ROOT, "models", "250421") + out_dir = os.path.join(ROOT, "output") + tb_dir = os.path.join(ROOT, "tensorboard", name) + os.makedirs(model_dir, exist_ok=True) + os.makedirs(out_dir, exist_ok=True) + + obj = FlowControlObjectiveV6( + basis_terms=basis_terms, + eval_steps=int(cfg["eval_steps"]), + tail_steps=int(cfg["tail_steps"]), + startup_steps=int(cfg["startup_steps"]), + max_recover_resets=int(cfg["max_recover_resets"]), + reset_each_candidate=bool(cfg["reset_each_candidate"]), + obs_fail_bound=float(cfg["obs_fail_bound"]), + obs_clip_bound=float(cfg["obs_clip_bound"]), + ) + obj.init_env(device_id=int(cfg["device_id"])) + + controller_dims = int(obj.dims) + surrogate_gpu_id = int(cfg["surrogate_gpu_id"]) + num_initial_raw = int(max(1, round(float(cfg["num_initial_per_dim"]) * controller_dims))) + num_initial = int(min(int(cfg["max_num_initial"]), max(int(cfg["min_num_initial"]), num_initial_raw))) + num_acquisitions = int((target_evals - int(num_initial)) // int(cfg["samples_per_acq"])) + if num_acquisitions <= 0: + raise RuntimeError( + f"computed num_acquisitions <= 0 with target_evals={target_evals}, " + f"num_initial={num_initial}, samples_per_acq={int(cfg['samples_per_acq'])}" + ) + max_init_attempts = int(max(num_initial, round(float(cfg["max_init_attempts_factor"]) * num_initial))) + if max_init_attempts < num_initial: + raise RuntimeError("max_init_attempts must be >= num_initial") + total_expected = int(num_initial + num_acquisitions * int(cfg["samples_per_acq"])) + + config_payload = { + "name": name, + "use_sindy_prior": False, + "reason": "forced_disabled_by_v6_full_adjustable", + "basis_profile": basis_profile, + "basis_terms": basis_terms, + "controller_dims": controller_dims, + "num_initial": num_initial, + "num_initial_raw": int(num_initial_raw), + "num_initial_per_dim": float(cfg["num_initial_per_dim"]), + "min_num_initial": int(cfg["min_num_initial"]), + "max_num_initial": int(cfg["max_num_initial"]), + "num_acquisitions": int(num_acquisitions), + "samples_per_acq": int(cfg["samples_per_acq"]), + "surrogate_mode": str(cfg["surrogate_mode"]), + "surrogate_gpu_id": int(surrogate_gpu_id), + "eval_steps": int(cfg["eval_steps"]), + "tail_steps": int(cfg["tail_steps"]), + "startup_steps": int(cfg["startup_steps"]), + "max_recover_resets": int(cfg["max_recover_resets"]), + "obs_fail_bound": float(cfg["obs_fail_bound"]), + "obs_clip_bound": float(cfg["obs_clip_bound"]), + "reset_each_candidate": bool(cfg["reset_each_candidate"]), + "max_init_attempts": int(max_init_attempts), + "max_init_attempts_factor": float(cfg["max_init_attempts_factor"]), + "target_cfd_steps": int(cfg["target_cfd_steps"]), + "target_total_evals": int(target_evals), + "matched_total_evals": int(total_expected), + "matched_cfd_steps": int(total_expected * int(cfg["eval_steps"])), + } + with open(os.path.join(out_dir, f"{name}_structure_decision.json"), "w", encoding="utf-8") as f: + json.dump(config_payload, f, indent=2, ensure_ascii=False) + + print("use_sindy_prior:", False) + print("basis_profile:", basis_profile) + print("basis_terms:", len(basis_terms)) + print("controller_dims:", controller_dims) + print("num_initial:", num_initial) + print("num_initial_raw:", int(num_initial_raw)) + print("num_initial_per_dim:", float(cfg["num_initial_per_dim"])) + print("min_num_initial:", int(cfg["min_num_initial"])) + print("max_num_initial:", int(cfg["max_num_initial"])) + print("max_init_attempts:", max_init_attempts) + print("max_init_attempts_factor:", float(cfg["max_init_attempts_factor"])) + print("num_acquisitions:", int(num_acquisitions)) + print("samples_per_acq:", int(cfg["samples_per_acq"])) + print("surrogate_mode:", str(cfg["surrogate_mode"])) + print("surrogate_gpu_id:", surrogate_gpu_id) + print("eval_steps:", int(cfg["eval_steps"])) + print("tail_steps:", int(cfg["tail_steps"])) + print("startup_steps:", int(cfg["startup_steps"])) + print("target_cfd_steps:", int(cfg["target_cfd_steps"])) + print("matched_cfd_steps:", int(total_expected * int(cfg["eval_steps"]))) + print("expected_total_evals:", total_expected) + + ckpt_path = Path(os.path.join(model_dir, f"{name}_surrogate.keras")) + surrogate = FlowControlSurrogateV6( + input_dims=controller_dims, + epochs=int(cfg["surrogate_epochs"]), + patience=30, + check_point_path=ckpt_path, + tf_device_id=int(surrogate_gpu_id), + ) + + live_db_path = os.path.join(out_dir, f"{name}_database_live.npz") + best_params_path = os.path.join(model_dir, f"{name}_best.npy") + best_meta_path = os.path.join(out_dir, f"{name}_best_meta.pkl") + final_meta_path = os.path.join(out_dir, f"{name}_final_meta.pkl") + dante_log_path = os.path.join(out_dir, f"{name}_dante_log.csv") + + if SummaryWriter is None: + writer = NullWriter() + else: + writer = SummaryWriter(log_dir=tb_dir) + + with open(dante_log_path, "w", encoding="utf-8") as f: + f.write("timestamp,phase,acq,candidate,reward,best_reward,dataset_size,recoveries_used,failure_code\n") + + input_x = np.empty((0, controller_dims), dtype=np.float64) + input_y = np.empty((0,), dtype=np.float64) + history = [] + best_reward = -1.0 + best_params = np.zeros(controller_dims, dtype=np.float64) + invalid_skips = 0 + + t0 = time.time() + global_step = 0 + + try: + # Phase 1: random init points (collect exactly num_initial valid samples) + valid_init = 0 + init_attempts = 0 + while valid_init < num_initial: + if init_attempts >= max_init_attempts: + raise RuntimeError( + f"init failed to collect enough valid samples: " + f"valid={valid_init}/{num_initial}, attempts={init_attempts}/{max_init_attempts}, " + f"invalid_skips={invalid_skips}" + ) + + init_attempts += 1 + x = sample_params(controller_dims, obj.turn) + info = obj.evaluate_candidate(x) + reward = float(info["reward"]) + is_valid = int(info.get("failure_code", 0)) == 0 + + if is_valid: + valid_init += 1 + input_x = np.vstack((input_x, x.reshape(1, -1))) + input_y = np.append(input_y, float(info["scaled"])) + writer.add_scalar("Reward", reward, global_step) + else: + invalid_skips += 1 + print( + f"[init] invalid sample skipped: attempt={init_attempts}, " + f"failure_code={info.get('failure_code', -1)}, " + f"valid={valid_init}/{num_initial}, invalid_skips={invalid_skips}", + flush=True, + ) + + if is_valid and reward > best_reward: + best_reward = reward + best_params = x.copy() + np.save(best_params_path, best_params) + with open(best_meta_path, "wb") as f: + pickle.dump( + { + "best_reward": best_reward, + "best_params": best_params, + "basis_terms": basis_terms, + "controller_dims": controller_dims, + "config": config_payload, + "timestamp": time.time(), + }, + f, + ) + + if is_valid: + save_live_db( + live_db_path, + input_x, + input_y, + { + "name": name, + "best_reward": best_reward, + "dataset_size": int(len(input_y)), + "basis_terms": basis_terms, + "use_sindy_prior": False, + "recover_count": int(obj.recover_count), + "invalid_skips": int(invalid_skips), + }, + ) + + with open(dante_log_path, "a", encoding="utf-8") as f: + f.write( + f"{time.time():.3f},init,0,{init_attempts},{reward:.8f},{best_reward:.8f},{len(input_y)},{info['recoveries_used']},{info.get('failure_code', 0)}\n" + ) + + global_step += 1 + print( + f"[init-attempt {init_attempts}/{max_init_attempts}] " + f"valid={valid_init}/{num_initial} | reward={reward:.4f} | " + f"best={best_reward:.4f} | invalid_skips={invalid_skips} | " + f"recover={int(obj.recover_count)} | eval={global_step}/{total_expected}+ | " + f"elapsed={float(time.time() - t0):.1f}s", + flush=True, + ) + + # Phase 2: DANTE acquisitions + for acq in range(int(num_acquisitions)): + if len(input_y) < 2: + print( + f"[acq {acq + 1}] skipped: insufficient valid samples ({len(input_y)})", + flush=True, + ) + continue + print( + f"[acq {acq + 1}/{int(num_acquisitions)}] fitting surrogate on {len(input_y)} samples...", + flush=True, + ) + surrogate_mode = str(cfg.get("surrogate_mode", "mlp")).lower() + if surrogate_mode == "ensemble": + candidates = [] + for arch in ["mlp", "cnn"]: + cand_ckpt = Path(str(ckpt_path).replace(".keras", f"_{arch}.keras")) + surr = FlowControlSurrogateV6( + input_dims=controller_dims, + epochs=int(cfg["surrogate_epochs"]), + patience=30, + check_point_path=cand_ckpt, + tf_device_id=int(surrogate_gpu_id), + architecture=arch, + ) + try: + m = surr(input_x, input_y, verbose=0) + fm = dict(getattr(surr, "last_fit_metrics", {}) or {}) + candidates.append((arch, m, fm, cand_ckpt)) + except Exception as e: + print(f"[acq {acq + 1}] surrogate {arch} failed: {e}", flush=True) + if not candidates: + raise RuntimeError("all surrogate candidates failed in ensemble mode") + candidates.sort(key=lambda z: float(z[2].get("val_r2", -1e9)), reverse=True) + best_arch, model, fit_metrics, best_ckpt = candidates[0] + if best_ckpt.exists(): + shutil.copy2(best_ckpt, ckpt_path) + fit_metrics["selected_architecture"] = best_arch + else: + surrogate.architecture = "cnn" if surrogate_mode == "cnn" else "mlp" + model = surrogate(input_x, input_y, verbose=0) + fit_metrics = dict(getattr(surrogate, "last_fit_metrics", {}) or {}) + fit_metrics["selected_architecture"] = str(surrogate.architecture) + if fit_metrics: + writer.add_scalar("Surrogate/val_r2", float(fit_metrics.get("val_r2", 0.0)), acq) + writer.add_scalar("Surrogate/val_mae", float(fit_metrics.get("val_mae", 0.0)), acq) + print( + f"[acq {acq + 1}] surrogate metrics: " + f"val_r2={fit_metrics.get('val_r2', float('nan')):.4f}, " + f"val_mae={fit_metrics.get('val_mae', float('nan')):.4f}, " + f"device={fit_metrics.get('device', 'CPU')}", + flush=True, + ) + print( + f"[acq {acq + 1}/{int(num_acquisitions)}] surrogate fit done, rolling out {int(cfg['samples_per_acq'])} candidates...", + flush=True, + ) + if ckpt_path.exists(): + shutil.copy2(ckpt_path, os.path.join(model_dir, f"{name}_surrogate_live.keras")) + + explorer = TreeExploration( + func=obj, + model=model, + num_samples_per_acquisition=int(cfg["samples_per_acq"]), + ) + candidates = explorer.rollout(input_x, input_y, iteration=acq) + + acq_rewards = [] + for j, x in enumerate(candidates): + info = obj.evaluate_candidate(x) + reward = float(info["reward"]) + is_valid = int(info.get("failure_code", 0)) == 0 + if is_valid: + acq_rewards.append(reward) + + input_x = np.vstack((input_x, np.asarray(x, dtype=np.float64).reshape(1, -1))) + input_y = np.append(input_y, float(info["scaled"])) + writer.add_scalar("Reward", reward, global_step) + + if reward > best_reward: + best_reward = reward + best_params = np.asarray(x, dtype=np.float64).copy() + np.save(best_params_path, best_params) + with open(best_meta_path, "wb") as f: + pickle.dump( + { + "best_reward": best_reward, + "best_params": best_params, + "basis_terms": basis_terms, + "controller_dims": controller_dims, + "config": config_payload, + "timestamp": time.time(), + }, + f, + ) + + save_live_db( + live_db_path, + input_x, + input_y, + { + "name": name, + "best_reward": best_reward, + "dataset_size": int(len(input_y)), + "basis_terms": basis_terms, + "use_sindy_prior": False, + "recover_count": int(obj.recover_count), + "acq": int(acq + 1), + "invalid_skips": int(invalid_skips), + }, + ) + else: + invalid_skips += 1 + print( + f"[acq {acq + 1}] invalid sample skipped: cand={j + 1}, failure_code={info.get('failure_code', -1)}", + flush=True, + ) + + with open(dante_log_path, "a", encoding="utf-8") as f: + f.write( + f"{time.time():.3f},acq,{acq + 1},{j + 1},{reward:.8f},{best_reward:.8f},{len(input_y)},{info['recoveries_used']},{info.get('failure_code', 0)}\n" + ) + + global_step += 1 + print_progress_line( + phase=f"acq{acq + 1}", + idx=j + 1, + total=len(candidates), + reward=reward, + best_reward=best_reward, + recover_total=int(obj.recover_count), + elapsed_sec=float(time.time() - t0), + global_step=global_step, + total_expected=total_expected, + ) + + history.append( + { + "iteration": int(acq + 1), + "new_mean": float(np.mean(acq_rewards) if acq_rewards else 0.0), + "new_max": float(np.max(acq_rewards) if acq_rewards else 0.0), + "best_reward": float(best_reward), + "dataset_size": int(len(input_y)), + "recover_total": int(obj.recover_count), + } + ) + + print( + f"acq {acq + 1}/{num_acquisitions}: mean={history[-1]['new_mean']:.4f}, " + f"max={history[-1]['new_max']:.4f}, best={best_reward:.4f}, recover_total={obj.recover_count}" + ) + + with open(final_meta_path, "wb") as f: + pickle.dump( + { + "name": name, + "best_reward": best_reward, + "best_params": best_params, + "basis_terms": basis_terms, + "controller_dims": controller_dims, + "history": history, + "elapsed_sec": float(time.time() - t0), + "config": config_payload, + "recover_total": int(obj.recover_count), + }, + f, + ) + + print("Training done") + print("best_reward:", f"{best_reward:.4f}") + print("recover_total:", obj.recover_count) + print("invalid_skips:", invalid_skips) + + finally: + writer.close() + if obj.env is not None: + obj.env.close() + + +if __name__ == "__main__": + main() diff --git a/archive/dante_pinball/dante_pinball/legacy/d1a3o12_250421_dante_v7_openloop.py b/archive/dante_pinball/dante_pinball/legacy/d1a3o12_250421_dante_v7_openloop.py new file mode 100644 index 0000000..3ac85a9 --- /dev/null +++ b/archive/dante_pinball/dante_pinball/legacy/d1a3o12_250421_dante_v7_openloop.py @@ -0,0 +1,911 @@ +""" +DANTE v7 (open-loop periodic control) + +This version switches from closed-loop basis feedback to a periodic open-loop controller: +1) Optimize control points of one cycle directly. +2) Use continuous phase advance to support non-integer cycle lengths. +3) Keep v6-compatible evaluation protocol (300-step rollout, tail100 score). +""" + +import argparse +import json +import os +import pickle +import shutil +import sys +import time +from pathlib import Path +from typing import Dict, List, Optional + +import numpy as np + +os.environ["MKL_THREADING_LAYER"] = "GNU" +os.environ["OMP_NUM_THREADS"] = "16" +os.environ["MKL_NUM_THREADS"] = "16" + +try: + from torch.utils.tensorboard import SummaryWriter +except Exception: + SummaryWriter = None + + +CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) +ROOT = os.path.abspath(os.path.join(CURRENT_DIR, os.pardir)) +os.chdir(CURRENT_DIR) +sys.path.insert(0, os.path.join(ROOT, "DANTE")) + +from dante.obj_functions import ObjectiveFunction +from dante.tree_exploration import TreeExploration + +from dante_v6_surrogate_torch import FlowControlSurrogateV6 +from dante_pinball.env.gym_env_dante_total_force import CustomEnv + + +N_ACT = 3 + +V7_CONFIG = { + "name": "d1a3o12_250421_forces02_dante_v7_openloop", + "device_id": 0, + "surrogate_gpu_id": 0, + "eval_steps": 300, + "tail_steps": 100, + "startup_steps": 0, + "max_recover_resets": 1, + "reset_each_candidate": True, + "obs_fail_bound": 2.0, + "obs_clip_bound": 3.0, + "control_points_per_channel": 8, + "phase_interp": "linear", + "period_mode": "auto", # auto | fixed | optimize + "fixed_period": 40.0, + "period_min": 15.0, + "period_max": 80.0, + "period_estimate_files": [ + "output/report_dante_v2_v5_v6/raw_oldenv_seed_11.npz", + "output/report_dante_v2_v5_v6/raw_oldenv_seed_29.npz", + "output/report_dante_v2_v5_v6/raw_oldenv_seed_47.npz", + ], + "period_estimate_key": "ppo_actions", + "num_initial_per_dim": 10, + "min_num_initial": 100, + "max_num_initial": 320, + "samples_per_acq": 24, + "max_init_attempts_factor": 2.0, + "surrogate_mode": "ensemble", # mlp | cnn | ensemble + "target_cfd_steps": 204800*2, + "surrogate_epochs": 400, +} + + +class NullWriter: + def add_scalar(self, *_args, **_kwargs) -> None: + pass + + def close(self) -> None: + pass + + +def map_unit_to_range(u: float, low: float, high: float) -> float: + u_clip = float(np.clip(u, -1.0, 1.0)) + alpha = 0.5 * (u_clip + 1.0) + return float(low + alpha * (high - low)) + + +def map_range_to_unit(x: float, low: float, high: float) -> float: + if high <= low: + return 0.0 + alpha = (float(x) - low) / (high - low) + return float(np.clip(2.0 * alpha - 1.0, -1.0, 1.0)) + + +def dominant_period_fft(x: np.ndarray, min_period: float, max_period: float) -> Optional[float]: + x = np.asarray(x, dtype=np.float64).reshape(-1) + n = int(x.size) + if n < 16: + return None + + xc = x - np.mean(x) + std = float(np.std(xc)) + if std < 1e-8: + return None + + yf = np.fft.rfft(xc) + power = (np.abs(yf) ** 2).reshape(-1) + freq = np.fft.rfftfreq(n, d=1.0) + + with np.errstate(divide="ignore", invalid="ignore"): + period = np.where(freq > 0.0, 1.0 / freq, np.inf) + + valid = (freq > 0.0) & (period >= float(min_period)) & (period <= float(max_period)) + if not np.any(valid): + return None + + idx = np.argmax(power * valid.astype(np.float64)) + f_peak = float(freq[idx]) + if f_peak <= 0.0: + return None + return float(1.0 / f_peak) + + +def load_action_array_from_npz(npz_path: str, key_hint: str = "ppo_actions") -> np.ndarray: + with np.load(npz_path) as z: + keys = list(z.keys()) + if key_hint in z: + arr = z[key_hint] + return np.asarray(arr, dtype=np.float64) + + for k in keys: + kl = k.lower() + if "ppo" in kl and "action" in kl: + arr = z[k] + return np.asarray(arr, dtype=np.float64) + + for k in keys: + arr = np.asarray(z[k]) + if arr.ndim == 2 and arr.shape[1] == N_ACT: + return np.asarray(arr, dtype=np.float64) + + raise KeyError(f"no action array found in {npz_path}") + + +def estimate_period_from_npz_list( + rel_paths: List[str], + key_hint: str, + min_period: float, + max_period: float, +) -> Dict[str, object]: + periods: List[float] = [] + details: List[Dict[str, object]] = [] + + for rel in rel_paths: + abs_path = os.path.join(ROOT, rel) + if not os.path.exists(abs_path): + details.append({"file": rel, "used": False, "reason": "missing"}) + continue + + try: + a = load_action_array_from_npz(abs_path, key_hint=key_hint) + if a.ndim != 2 or a.shape[1] != N_ACT: + details.append({ + "file": rel, + "used": False, + "reason": f"invalid_shape_{tuple(a.shape)}", + }) + continue + + file_periods = [] + for ch in range(N_ACT): + p = dominant_period_fft( + a[:, ch], + min_period=float(min_period), + max_period=float(max_period), + ) + if p is not None and np.isfinite(p): + file_periods.append(float(p)) + periods.append(float(p)) + + details.append({ + "file": rel, + "used": len(file_periods) > 0, + "num_channels": int(len(file_periods)), + "channel_periods": [float(v) for v in file_periods], + }) + except Exception as e: + details.append({"file": rel, "used": False, "reason": str(e)}) + + if len(periods) == 0: + return { + "period": None, + "source": "none", + "details": details, + "count": 0, + } + + period_med = float(np.median(np.asarray(periods, dtype=np.float64))) + period_med = float(np.clip(period_med, min_period, max_period)) + + return { + "period": period_med, + "source": "fft_median", + "details": details, + "count": int(len(periods)), + "all_periods": [float(v) for v in periods], + } + + +class PeriodicOpenLoopController: + def __init__( + self, + control_points_per_channel: int, + period_mode: str, + base_period_steps: float, + period_min: float, + period_max: float, + interp: str = "linear", + ): + self.k = int(control_points_per_channel) + if self.k < 3: + raise ValueError("control_points_per_channel must be >= 3") + + self.period_mode = str(period_mode).lower() + if self.period_mode not in {"auto", "fixed", "optimize"}: + raise ValueError("period_mode must be one of auto|fixed|optimize") + + self.base_period_steps = float(base_period_steps) + self.period_min = float(period_min) + self.period_max = float(period_max) + self.interp = str(interp).lower() + if self.interp not in {"linear"}: + raise ValueError("only linear interpolation is currently supported") + + self.optimize_period = self.period_mode == "optimize" + + self.ctrl_points = np.zeros((N_ACT, self.k), dtype=np.float64) + self.phase = 0.0 + self.current_period_steps = float(np.clip(self.base_period_steps, self.period_min, self.period_max)) + + self.total_params = int(N_ACT * self.k + (1 if self.optimize_period else 0)) + + def reset_state(self) -> None: + self.phase = 0.0 + + def _eval_channel(self, points: np.ndarray, phase: float) -> float: + p = np.asarray(points, dtype=np.float64).reshape(-1) + z = float(np.mod(phase, 1.0)) * self.k + i0 = int(np.floor(z)) % self.k + frac = float(z - np.floor(z)) + i1 = (i0 + 1) % self.k + return float((1.0 - frac) * p[i0] + frac * p[i1]) + + def set_params(self, x: np.ndarray) -> None: + x = np.asarray(x, dtype=np.float64).reshape(-1) + if x.size != self.total_params: + raise ValueError(f"controller params mismatch: {x.size} != {self.total_params}") + + core = np.clip(x[: N_ACT * self.k], -1.0, 1.0) + self.ctrl_points = core.reshape(N_ACT, self.k) + + if self.optimize_period: + p_unit = float(x[-1]) + self.current_period_steps = map_unit_to_range(p_unit, self.period_min, self.period_max) + else: + self.current_period_steps = float(np.clip(self.base_period_steps, self.period_min, self.period_max)) + + def predict(self, _obs: np.ndarray) -> np.ndarray: + action = np.zeros(N_ACT, dtype=np.float64) + for ch in range(N_ACT): + action[ch] = self._eval_channel(self.ctrl_points[ch], self.phase) + + action = np.clip(action, -1.0, 1.0) + + step_phase = 1.0 / max(1e-6, float(self.current_period_steps)) + self.phase = float((self.phase + step_phase) % 1.0) + + return action.astype(np.float32) + + +class FlowControlObjectiveV7(ObjectiveFunction): + def __init__( + self, + control_points_per_channel: int, + period_mode: str, + period_steps: float, + period_min: float, + period_max: float, + phase_interp: str = "linear", + eval_steps: int = 300, + tail_steps: int = 100, + startup_steps: int = 0, + max_recover_resets: int = 1, + turn: float = 0.05, + reset_each_candidate: bool = True, + obs_fail_bound: float = 2.0, + obs_clip_bound: float = 3.0, + ): + self.eval_steps = int(eval_steps) + self.tail_steps = int(tail_steps) + self.startup_steps = int(startup_steps) + self.max_recover_resets = int(max_recover_resets) + self.turn = float(turn) + self.reset_each_candidate = bool(reset_each_candidate) + self.obs_fail_bound = float(obs_fail_bound) + self.obs_clip_bound = float(obs_clip_bound) + + self.control_points_per_channel = int(control_points_per_channel) + self.period_mode = str(period_mode) + self.period_steps = float(period_steps) + self.period_min = float(period_min) + self.period_max = float(period_max) + self.phase_interp = str(phase_interp) + + self.controller = PeriodicOpenLoopController( + control_points_per_channel=self.control_points_per_channel, + period_mode=self.period_mode, + base_period_steps=self.period_steps, + period_min=self.period_min, + period_max=self.period_max, + interp=self.phase_interp, + ) + + self.dims = int(self.controller.total_params) + self.lb = -1.0 * np.ones(self.dims) + self.ub = 1.0 * np.ones(self.dims) + + self.env = None + self._device_id = 0 + self.obs_current = None + self.recover_count = 0 + + def init_env(self, device_id: int = 0) -> None: + self._device_id = int(device_id) + if self.env is not None: + self.env.close() + self.env = CustomEnv( + device_id=int(device_id), + obs_fail_bound=float(self.obs_fail_bound), + obs_clip_bound=float(self.obs_clip_bound), + ) + self._hard_reset_and_stabilize() + + def _hard_reset_and_stabilize(self) -> None: + obs, _ = self.env.reset() + obs = np.asarray(obs, dtype=np.float32) + action = np.zeros(N_ACT, dtype=np.float32) + + for _ in range(int(self.startup_steps)): + obs, _, done, trunc, _ = self.env.step(action) + if done or trunc: + obs, _ = self.env.reset() + + self.obs_current = np.asarray(obs, dtype=np.float32) + self.controller.reset_state() + + @staticmethod + def _tail_mean(rewards: List[float], tail_steps: int) -> float: + rr = np.asarray(rewards, dtype=np.float64) + if rr.size == 0: + return 0.0 + tail = rr[-tail_steps:] if rr.size >= tail_steps else rr + return float(np.mean(tail)) + + def evaluate_candidate(self, x: np.ndarray) -> Dict[str, object]: + x = self._preprocess(x) + self.controller.set_params(x) + + if self.env is None: + self.init_env(self._device_id) + + if self.reset_each_candidate: + self._hard_reset_and_stabilize() + + for attempt in range(int(self.max_recover_resets) + 1): + obs = np.asarray(self.obs_current, dtype=np.float32) + self.controller.reset_state() + + rewards: List[float] = [] + steps = 0 + done = False + trunc = False + last_info: Dict[str, object] = {} + + for _ in range(int(self.eval_steps)): + action = self.controller.predict(obs) + obs, reward, done, trunc, step_info = self.env.step(action) + if isinstance(step_info, dict): + last_info = step_info + rewards.append(float(reward)) + steps += 1 + if done or trunc: + break + + if done or trunc and attempt < int(self.max_recover_resets): + self.recover_count += 1 + self._hard_reset_and_stabilize() + continue + + self.obs_current = np.asarray(obs, dtype=np.float32) + y = self._tail_mean(rewards, tail_steps=int(self.tail_steps)) + return { + "reward": float(y), + "scaled": float(y * 100.0), + "steps": int(steps), + "done": bool(done), + "truncated": bool(trunc), + "recoveries_used": int(attempt), + "failure_code": int(last_info.get("failure_code", 0)), + "period_steps": float(self.controller.current_period_steps), + } + + return { + "reward": 0.0, + "scaled": 0.0, + "steps": 0, + "done": True, + "truncated": True, + "recoveries_used": int(self.max_recover_resets), + "failure_code": 1, + "period_steps": float(self.controller.current_period_steps), + } + + def scaled(self, y: float) -> float: + return float(y * 100.0) + + def __call__(self, x: np.ndarray, apply_scaling: bool = False, track: bool = True) -> float: + info = self.evaluate_candidate(x) + y = float(info["reward"]) + return self.scaled(y) if apply_scaling else y + + +def parse_args() -> argparse.Namespace: + p = argparse.ArgumentParser(description="DANTE v7 open-loop periodic controller") + p.add_argument("--name", type=str, default=V7_CONFIG["name"], help="Optional run name override") + p.add_argument( + "--control-points", + type=int, + default=V7_CONFIG["control_points_per_channel"], + help="Control points per channel for one cycle", + ) + p.add_argument( + "--period-mode", + type=str, + default=V7_CONFIG["period_mode"], + choices=["auto", "fixed", "optimize"], + help="Cycle period mode", + ) + p.add_argument("--fixed-period", type=float, default=V7_CONFIG["fixed_period"], help="Fixed period in steps") + p.add_argument("--period-min", type=float, default=V7_CONFIG["period_min"], help="Min period for clipping") + p.add_argument("--period-max", type=float, default=V7_CONFIG["period_max"], help="Max period for clipping") + p.add_argument( + "--phase-interp", + type=str, + default=V7_CONFIG["phase_interp"], + choices=["linear"], + help="Interpolation mode for phase to control point", + ) + return p.parse_args() + + +def sample_params(dims: int, turn: float, period_mode: str, period_guess: float, pmin: float, pmax: float) -> np.ndarray: + x = np.random.uniform(-1.0, 1.0, size=dims) + x = np.round(x / turn) * turn + x = np.clip(x, -1.0, 1.0) + + if str(period_mode).lower() == "optimize": + x[-1] = map_range_to_unit(period_guess, pmin, pmax) + return x + + +def save_live_db(path: str, x: np.ndarray, y: np.ndarray, meta: Dict[str, object]) -> None: + np.savez(path, input_x=x, input_y=y, meta_json=np.array([json.dumps(meta, ensure_ascii=False)])) + + +def print_progress_line( + phase: str, + idx: int, + total: int, + reward: float, + best_reward: float, + recover_total: int, + elapsed_sec: float, + global_step: int, + total_expected: int, + period_steps: float, +) -> None: + print( + f"[{phase}] {idx}/{total} | reward={reward:.4f} | best={best_reward:.4f} | " + f"period={period_steps:.3f} | recover={recover_total} | eval={global_step}/{total_expected} | elapsed={elapsed_sec:.1f}s", + flush=True, + ) + + +def main() -> None: + args = parse_args() + + cfg = dict(V7_CONFIG) + cfg["name"] = str(args.name) + cfg["control_points_per_channel"] = int(args.control_points) + cfg["period_mode"] = str(args.period_mode) + cfg["fixed_period"] = float(args.fixed_period) + cfg["period_min"] = float(args.period_min) + cfg["period_max"] = float(args.period_max) + cfg["phase_interp"] = str(args.phase_interp) + + period_info = estimate_period_from_npz_list( + rel_paths=list(cfg["period_estimate_files"]), + key_hint=str(cfg["period_estimate_key"]), + min_period=float(cfg["period_min"]), + max_period=float(cfg["period_max"]), + ) + + if str(cfg["period_mode"]).lower() == "fixed": + period_steps = float(np.clip(cfg["fixed_period"], cfg["period_min"], cfg["period_max"])) + period_source = "fixed" + else: + p_est = period_info.get("period", None) + if p_est is None: + period_steps = float(np.clip(cfg["fixed_period"], cfg["period_min"], cfg["period_max"])) + period_source = "fallback_fixed_no_estimate" + else: + period_steps = float(np.clip(float(p_est), cfg["period_min"], cfg["period_max"])) + period_source = "auto_from_ppo_fft" + + target_evals = int(cfg["target_cfd_steps"] // cfg["eval_steps"]) + + obj = FlowControlObjectiveV7( + control_points_per_channel=int(cfg["control_points_per_channel"]), + period_mode=str(cfg["period_mode"]), + period_steps=float(period_steps), + period_min=float(cfg["period_min"]), + period_max=float(cfg["period_max"]), + phase_interp=str(cfg["phase_interp"]), + eval_steps=int(cfg["eval_steps"]), + tail_steps=int(cfg["tail_steps"]), + startup_steps=int(cfg["startup_steps"]), + max_recover_resets=int(cfg["max_recover_resets"]), + reset_each_candidate=bool(cfg["reset_each_candidate"]), + obs_fail_bound=float(cfg["obs_fail_bound"]), + obs_clip_bound=float(cfg["obs_clip_bound"]), + ) + obj.init_env(device_id=int(cfg["device_id"])) + + dims = int(obj.dims) + num_initial_raw = int(max(1, round(float(cfg["num_initial_per_dim"]) * dims))) + num_initial = int(min(int(cfg["max_num_initial"]), max(int(cfg["min_num_initial"]), num_initial_raw))) + num_acquisitions = int((target_evals - int(num_initial)) // int(cfg["samples_per_acq"])) + if num_acquisitions <= 0: + raise RuntimeError( + f"computed num_acquisitions <= 0 with target_evals={target_evals}, " + f"num_initial={num_initial}, samples_per_acq={int(cfg['samples_per_acq'])}" + ) + + max_init_attempts = int(max(num_initial, round(float(cfg["max_init_attempts_factor"]) * num_initial))) + total_expected = int(num_initial + num_acquisitions * int(cfg["samples_per_acq"])) + + name = str(cfg["name"]) + model_dir = os.path.join(ROOT, "models", "250421") + out_dir = os.path.join(ROOT, "output") + tb_dir = os.path.join(ROOT, "tensorboard", name) + os.makedirs(model_dir, exist_ok=True) + os.makedirs(out_dir, exist_ok=True) + + config_payload = { + "name": name, + "control_mode": "open_loop_periodic", + "device_id": int(cfg["device_id"]), + "surrogate_gpu_id": int(cfg["surrogate_gpu_id"]), + "control_points_per_channel": int(cfg["control_points_per_channel"]), + "controller_dims": int(dims), + "period_mode": str(cfg["period_mode"]), + "period_steps": float(period_steps), + "period_source": period_source, + "period_min": float(cfg["period_min"]), + "period_max": float(cfg["period_max"]), + "period_estimate": period_info, + "phase_interp": str(cfg["phase_interp"]), + "num_initial": int(num_initial), + "num_initial_raw": int(num_initial_raw), + "num_initial_per_dim": float(cfg["num_initial_per_dim"]), + "min_num_initial": int(cfg["min_num_initial"]), + "max_num_initial": int(cfg["max_num_initial"]), + "num_acquisitions": int(num_acquisitions), + "samples_per_acq": int(cfg["samples_per_acq"]), + "surrogate_mode": str(cfg["surrogate_mode"]), + "eval_steps": int(cfg["eval_steps"]), + "tail_steps": int(cfg["tail_steps"]), + "startup_steps": int(cfg["startup_steps"]), + "max_recover_resets": int(cfg["max_recover_resets"]), + "obs_fail_bound": float(cfg["obs_fail_bound"]), + "obs_clip_bound": float(cfg["obs_clip_bound"]), + "reset_each_candidate": bool(cfg["reset_each_candidate"]), + "max_init_attempts": int(max_init_attempts), + "max_init_attempts_factor": float(cfg["max_init_attempts_factor"]), + "target_cfd_steps": int(cfg["target_cfd_steps"]), + "target_total_evals": int(target_evals), + "matched_total_evals": int(total_expected), + "matched_cfd_steps": int(total_expected * int(cfg["eval_steps"])), + } + + with open(os.path.join(out_dir, f"{name}_structure_decision.json"), "w", encoding="utf-8") as f: + json.dump(config_payload, f, indent=2, ensure_ascii=False) + + print("control_mode:", "open_loop_periodic") + print("device_id:", int(cfg["device_id"])) + print("surrogate_gpu_id:", int(cfg["surrogate_gpu_id"])) + print("control_points_per_channel:", int(cfg["control_points_per_channel"])) + print("controller_dims:", int(dims)) + print("period_mode:", str(cfg["period_mode"])) + print("period_steps:", float(period_steps)) + print("period_source:", period_source) + print("num_initial:", int(num_initial)) + print("num_initial_raw:", int(num_initial_raw)) + print("num_acquisitions:", int(num_acquisitions)) + print("samples_per_acq:", int(cfg["samples_per_acq"])) + print("target_cfd_steps:", int(cfg["target_cfd_steps"])) + print("matched_cfd_steps:", int(total_expected * int(cfg["eval_steps"]))) + + ckpt_path = Path(os.path.join(model_dir, f"{name}_surrogate.keras")) + surrogate = FlowControlSurrogateV6( + input_dims=dims, + epochs=int(cfg["surrogate_epochs"]), + patience=30, + check_point_path=ckpt_path, + tf_device_id=int(cfg["surrogate_gpu_id"]), + ) + + live_db_path = os.path.join(out_dir, f"{name}_database_live.npz") + best_params_path = os.path.join(model_dir, f"{name}_best.npy") + best_meta_path = os.path.join(out_dir, f"{name}_best_meta.pkl") + final_meta_path = os.path.join(out_dir, f"{name}_final_meta.pkl") + dante_log_path = os.path.join(out_dir, f"{name}_dante_log.csv") + + writer = NullWriter() if SummaryWriter is None else SummaryWriter(log_dir=tb_dir) + + with open(dante_log_path, "w", encoding="utf-8") as f: + f.write("timestamp,phase,acq,candidate,reward,best_reward,dataset_size,recoveries_used,failure_code,period_steps\n") + + input_x = np.empty((0, dims), dtype=np.float64) + input_y = np.empty((0,), dtype=np.float64) + history: List[Dict[str, object]] = [] + best_reward = -1.0 + best_params = np.zeros(dims, dtype=np.float64) + invalid_skips = 0 + + t0 = time.time() + global_step = 0 + + try: + valid_init = 0 + init_attempts = 0 + while valid_init < num_initial: + if init_attempts >= max_init_attempts: + raise RuntimeError( + f"init failed to collect enough valid samples: valid={valid_init}/{num_initial}, " + f"attempts={init_attempts}/{max_init_attempts}, invalid_skips={invalid_skips}" + ) + + init_attempts += 1 + x = sample_params( + dims=dims, + turn=obj.turn, + period_mode=str(cfg["period_mode"]), + period_guess=float(period_steps), + pmin=float(cfg["period_min"]), + pmax=float(cfg["period_max"]), + ) + + info = obj.evaluate_candidate(x) + reward = float(info["reward"]) + is_valid = int(info.get("failure_code", 0)) == 0 + + if is_valid: + valid_init += 1 + input_x = np.vstack((input_x, x.reshape(1, -1))) + input_y = np.append(input_y, float(info["scaled"])) + writer.add_scalar("Reward", reward, global_step) + else: + invalid_skips += 1 + + if is_valid and reward > best_reward: + best_reward = reward + best_params = x.copy() + np.save(best_params_path, best_params) + with open(best_meta_path, "wb") as f: + pickle.dump( + { + "best_reward": best_reward, + "best_params": best_params, + "config": config_payload, + "timestamp": time.time(), + }, + f, + ) + + if is_valid: + save_live_db( + live_db_path, + input_x, + input_y, + { + "name": name, + "best_reward": best_reward, + "dataset_size": int(len(input_y)), + "control_mode": "open_loop_periodic", + "recover_count": int(obj.recover_count), + "invalid_skips": int(invalid_skips), + }, + ) + + with open(dante_log_path, "a", encoding="utf-8") as f: + f.write( + f"{time.time():.3f},init,0,{init_attempts},{reward:.8f},{best_reward:.8f},{len(input_y)},{info['recoveries_used']},{info.get('failure_code', 0)},{float(info.get('period_steps', period_steps)):.6f}\n" + ) + + global_step += 1 + print( + f"[init-attempt {init_attempts}/{max_init_attempts}] valid={valid_init}/{num_initial} | " + f"reward={reward:.4f} | best={best_reward:.4f} | period={float(info.get('period_steps', period_steps)):.3f} | " + f"invalid_skips={invalid_skips} | recover={int(obj.recover_count)} | " + f"eval={global_step}/{total_expected}+ | elapsed={float(time.time() - t0):.1f}s", + flush=True, + ) + + for acq in range(int(num_acquisitions)): + if len(input_y) < 2: + print(f"[acq {acq + 1}] skipped: insufficient valid samples ({len(input_y)})", flush=True) + continue + + print(f"[acq {acq + 1}/{int(num_acquisitions)}] fitting surrogate on {len(input_y)} samples...", flush=True) + + surrogate_mode = str(cfg.get("surrogate_mode", "mlp")).lower() + if surrogate_mode == "ensemble": + candidates = [] + for arch in ["mlp", "cnn"]: + cand_ckpt = Path(str(ckpt_path).replace(".keras", f"_{arch}.keras")) + surr = FlowControlSurrogateV6( + input_dims=dims, + epochs=int(cfg["surrogate_epochs"]), + patience=30, + check_point_path=cand_ckpt, + tf_device_id=int(cfg["surrogate_gpu_id"]), + architecture=arch, + ) + try: + m = surr(input_x, input_y, verbose=0) + fm = dict(getattr(surr, "last_fit_metrics", {}) or {}) + candidates.append((arch, m, fm, cand_ckpt)) + except Exception as e: + print(f"[acq {acq + 1}] surrogate {arch} failed: {e}", flush=True) + + if not candidates: + raise RuntimeError("all surrogate candidates failed in ensemble mode") + + candidates.sort(key=lambda z: float(z[2].get("val_r2", -1e9)), reverse=True) + best_arch, model, fit_metrics, best_ckpt = candidates[0] + if best_ckpt.exists(): + shutil.copy2(best_ckpt, ckpt_path) + fit_metrics["selected_architecture"] = best_arch + else: + surrogate.architecture = "cnn" if surrogate_mode == "cnn" else "mlp" + model = surrogate(input_x, input_y, verbose=0) + fit_metrics = dict(getattr(surrogate, "last_fit_metrics", {}) or {}) + fit_metrics["selected_architecture"] = str(surrogate.architecture) + + if fit_metrics: + writer.add_scalar("Surrogate/val_r2", float(fit_metrics.get("val_r2", 0.0)), acq) + writer.add_scalar("Surrogate/val_mae", float(fit_metrics.get("val_mae", 0.0)), acq) + print( + f"[acq {acq + 1}] surrogate metrics: " + f"val_r2={fit_metrics.get('val_r2', float('nan')):.4f}, " + f"val_mae={fit_metrics.get('val_mae', float('nan')):.4f}, " + f"selected={fit_metrics.get('selected_architecture', 'na')}, " + f"device={fit_metrics.get('device', 'CPU')}", + flush=True, + ) + + if ckpt_path.exists(): + shutil.copy2(ckpt_path, os.path.join(model_dir, f"{name}_surrogate_live.keras")) + + explorer = TreeExploration( + func=obj, + model=model, + num_samples_per_acquisition=int(cfg["samples_per_acq"]), + ) + candidate_x = explorer.rollout(input_x, input_y, iteration=acq) + + acq_rewards: List[float] = [] + acq_periods: List[float] = [] + for j, x in enumerate(candidate_x): + info = obj.evaluate_candidate(x) + reward = float(info["reward"]) + period_used = float(info.get("period_steps", period_steps)) + is_valid = int(info.get("failure_code", 0)) == 0 + + if is_valid: + acq_rewards.append(reward) + acq_periods.append(period_used) + + input_x = np.vstack((input_x, np.asarray(x, dtype=np.float64).reshape(1, -1))) + input_y = np.append(input_y, float(info["scaled"])) + writer.add_scalar("Reward", reward, global_step) + + if str(cfg["period_mode"]).lower() == "optimize": + writer.add_scalar("Controller/period_steps", period_used, global_step) + + if reward > best_reward: + best_reward = reward + best_params = np.asarray(x, dtype=np.float64).copy() + np.save(best_params_path, best_params) + with open(best_meta_path, "wb") as f: + pickle.dump( + { + "best_reward": best_reward, + "best_params": best_params, + "config": config_payload, + "timestamp": time.time(), + }, + f, + ) + + save_live_db( + live_db_path, + input_x, + input_y, + { + "name": name, + "best_reward": best_reward, + "dataset_size": int(len(input_y)), + "control_mode": "open_loop_periodic", + "recover_count": int(obj.recover_count), + "acq": int(acq + 1), + "invalid_skips": int(invalid_skips), + }, + ) + else: + invalid_skips += 1 + + with open(dante_log_path, "a", encoding="utf-8") as f: + f.write( + f"{time.time():.3f},acq,{acq + 1},{j + 1},{reward:.8f},{best_reward:.8f},{len(input_y)},{info['recoveries_used']},{info.get('failure_code', 0)},{period_used:.6f}\n" + ) + + global_step += 1 + print_progress_line( + phase=f"acq{acq + 1}", + idx=j + 1, + total=len(candidate_x), + reward=reward, + best_reward=best_reward, + recover_total=int(obj.recover_count), + elapsed_sec=float(time.time() - t0), + global_step=global_step, + total_expected=total_expected, + period_steps=period_used, + ) + + history.append( + { + "iteration": int(acq + 1), + "new_mean": float(np.mean(acq_rewards) if acq_rewards else 0.0), + "new_max": float(np.max(acq_rewards) if acq_rewards else 0.0), + "period_mean": float(np.mean(acq_periods) if acq_periods else period_steps), + "period_std": float(np.std(acq_periods) if acq_periods else 0.0), + "best_reward": float(best_reward), + "dataset_size": int(len(input_y)), + "recover_total": int(obj.recover_count), + } + ) + + print( + f"acq {acq + 1}/{num_acquisitions}: mean={history[-1]['new_mean']:.4f}, " + f"max={history[-1]['new_max']:.4f}, best={best_reward:.4f}, " + f"period_mean={history[-1]['period_mean']:.3f}, recover_total={obj.recover_count}", + flush=True, + ) + + with open(final_meta_path, "wb") as f: + pickle.dump( + { + "name": name, + "best_reward": best_reward, + "best_params": best_params, + "history": history, + "elapsed_sec": float(time.time() - t0), + "config": config_payload, + "recover_total": int(obj.recover_count), + }, + f, + ) + + print("Training done") + print("best_reward:", f"{best_reward:.4f}") + print("recover_total:", obj.recover_count) + print("invalid_skips:", invalid_skips) + + finally: + writer.close() + if obj.env is not None: + obj.env.close() + + +if __name__ == "__main__": + main() diff --git a/archive/dante_pinball/dante_pinball/legacy/dante_v6_initial_surrogate_analysis.md b/archive/dante_pinball/dante_pinball/legacy/dante_v6_initial_surrogate_analysis.md new file mode 100644 index 0000000..ba21600 --- /dev/null +++ b/archive/dante_pinball/dante_pinball/legacy/dante_v6_initial_surrogate_analysis.md @@ -0,0 +1,51 @@ +# DANTE v6 Initial-Region Surrogate Failure Analysis + +## Scope + +- initial_samples: 100 +- dims: 42 +- note: analysis uses only init phase, not acquisition samples + +## Data Geometry + +- rank_ratio: 1.000 +- unique_ratio_rounded_0p05: 1.000 +- cov_condition_number: 1.53e+01 +- pca_components_for_90pct_var: 30 +- avg_nn_dist: 4.1265 + +## Classical Baselines + +- ridge_std: r2_mean=-1.0772 ± 0.4586, mae_mean=6.0836 +- knn_std_k5: r2_mean=-0.2933 ± 0.2589, mae_mean=4.9774 +- rf_300: r2_mean=-0.2074 ± 0.2602, mae_mean=4.6365 + +## TensorFlow Models + +- tf_device: GPU:1 +- tf_v4_like_no_scaling: r2_mean=-1.8222 ± 0.8408, mae_mean=7.1709 +- tf_v4_like_with_scaling: r2_mean=-0.7289 ± 0.2800, mae_mean=5.9893 +- tf_mid_128_64_32_with_scaling: r2_mean=-0.6206 ± 0.4865, mae_mean=5.6784 + +## Recovery/Noise Signal + +- recoveries_ratio_ge1: 0.880 +- mean_scaled(recover>=1): 17.945237696501337 +- mean_scaled(recover=0): 15.966237587414065 +- std_scaled(recover>=1): 4.7475266358578665 +- std_scaled(recover=0): 9.213494585712375 + +## Temporal Drift + +- corr(index, y): -0.1209 +- forward RF on x: r2=-0.0394, mae=5.0576 +- forward RF on [x,index]: r2=0.0153, mae=4.9602 +- forward ridge on index only: r2=0.0023, mae=4.9557 + +## Diagnosis + +- Even non-neural baselines fail on init set: weakly learnable mapping or high label noise. +- Input/output scaling is a first-order factor: original no-scaling setup underfits init region. +- Model capacity/optimization also matters after scaling (mid_128_64_32 > v4-like). +- Most init samples require recovery reset; this indicates environment-transition induced label noise in init pool. +- Adding sample index improves forward prediction, indicating temporal/state drift beyond static x->y mapping. \ No newline at end of file diff --git a/archive/dante_pinball/dante_pinball/legacy/dante_v6_ppo_diag.md b/archive/dante_pinball/dante_pinball/legacy/dante_v6_ppo_diag.md new file mode 100644 index 0000000..77406f8 --- /dev/null +++ b/archive/dante_pinball/dante_pinball/legacy/dante_v6_ppo_diag.md @@ -0,0 +1,28 @@ +# DANTE v6 vs PPO 诊断报告 + +## 1) PPO新terms重拟合与可达性 + +- features: ['bias1', 'obs0', 'obs1', 'dobs0', 'dobs1', 'sin_obs0', 'sin_obs1', 'cos_obs0', 'cos_obs1', 'tanh_obs0', 'tanh_obs1', 'act0_l1', 'act1_l1', 'act2_l1'] +- ch0: R2_test=1.0000, MAE_test=0.00002, nz=14 +- ch1: R2_test=1.0000, MAE_test=0.00001, nz=14 +- ch2: R2_test=1.0000, MAE_test=0.00002, nz=14 +- 参数空间可达性: out_of_range_terms=0, coef_abs_err_mean=0.000000, coef_abs_err_max=0.000000 + +## 2) PPO拟合控制率环境回放 + +- rollout steps=300, recoveries=0, tail60=0.5041, max_reward=0.7223 +- reward曲线图: dante_v6_ppo_reward_curve.png +- 流场速度图: dante_v6_ppo_flow_speed.png + +## 3) DANTE数据库与PPO目标点 + +- samples=818, num_initial=100, best_reward=0.3311 +- PCA距离: init_mean=0.6812, last_mean=5.3179, min=0.0080 at eval=196 +- PCA图: dante_v6_pca_overlay.png +- 距离趋势图: dante_v6_distance_to_ppo.png +- best曲线图: dante_v6_best_curve.png + +## 4) 结论 + +- 是否趋近PPO点: False +- 最高reward不更新判断: DANTE未明显趋近PPO目标点,代理采样方向与目标结构存在偏移,需修正采集策略或特征归一化。 \ No newline at end of file diff --git a/archive/dante_pinball/dante_pinball/legacy/dante_v6_surrogate_study.md b/archive/dante_pinball/dante_pinball/legacy/dante_v6_surrogate_study.md new file mode 100644 index 0000000..eeced19 --- /dev/null +++ b/archive/dante_pinball/dante_pinball/legacy/dante_v6_surrogate_study.md @@ -0,0 +1,23 @@ +# DANTE v6 Surrogate Study + +- db_path: output/d1a3o12_250421_forces02_dante_v6_database_live.npz +- n_samples: 976 +- dims: 42 +- holdout: 0.2 +- gpu_id: 1 + +## Ranking (by test_r2) + +| rank | design | test_r2 | test_mae | val_r2 | epochs_ran | device | +|---:|---|---:|---:|---:|---:|---| +| 1 | mid_128_64_32 | 0.8647 | 1.2556 | 0.8778 | 88 | GPU:1 | +| 2 | compact_128_64 | 0.8610 | 1.2621 | 0.8836 | 84 | GPU:1 | +| 3 | wide_256_128_64 | 0.8528 | 1.2508 | 0.8786 | 68 | GPU:1 | +| 4 | strong_reg_128_64_32 | 0.8463 | 1.3380 | 0.8592 | 138 | GPU:1 | + +## Recommended + +- design: mid_128_64_32 +- test_r2: 0.8647 +- test_mae: 1.2556 +- config: {"name": "mid_128_64_32", "hidden_units": [128, 64, 32], "dropout": 0.1, "weight_decay": 1e-06, "learning_rate": 0.001, "batch_size": 64} \ No newline at end of file diff --git a/archive/dante_pinball/dante_pinball/legacy/dante_v6_surrogate_torch.py b/archive/dante_pinball/dante_pinball/legacy/dante_v6_surrogate_torch.py new file mode 100644 index 0000000..acd5372 --- /dev/null +++ b/archive/dante_pinball/dante_pinball/legacy/dante_v6_surrogate_torch.py @@ -0,0 +1,253 @@ +import os +from dataclasses import dataclass, field +from pathlib import Path +from typing import Any, Dict, Optional, Sequence + +import numpy as np +from sklearn.metrics import mean_absolute_error, r2_score +from sklearn.model_selection import train_test_split +from sklearn.preprocessing import StandardScaler + +try: + import torch + import torch.nn as nn + import torch.optim as optim + _TORCH_IMPORT_ERROR = None +except Exception as exc: + torch = None + nn = None + optim = None + _TORCH_IMPORT_ERROR = exc + + +class TorchScaledPredictWrapper: + def __init__(self, torch_model: Any, x_scaler: StandardScaler, y_scaler: StandardScaler, device: str): + self.torch_model = torch_model + self.x_scaler = x_scaler + self.y_scaler = y_scaler + self.device = str(device) + + def predict(self, x_in, **kwargs): + x_in = np.asarray(x_in, dtype=np.float64) + if x_in.ndim == 3 and x_in.shape[-1] == 1: + x_in = x_in.squeeze(axis=-1) + if x_in.ndim > 2: + x_in = x_in.reshape(len(x_in), -1) + x_scaled = self.x_scaler.transform(x_in).astype(np.float32) + xt = torch.as_tensor(x_scaled, dtype=torch.float32, device=self.device) + if xt.ndim == 2: + pass + else: + xt = xt.reshape(len(xt), -1) + + self.torch_model.eval() + with torch.no_grad(): + if hasattr(self.torch_model, "expects_channel") and self.torch_model.expects_channel: + # Conv1d on this runtime can hit intermittent cuDNN mapping errors; + # keep GPU execution but bypass cuDNN for CNN forward passes. + with torch.backends.cudnn.flags(enabled=False): + y_scaled = self.torch_model(xt.unsqueeze(1)).detach().cpu().numpy().reshape(-1, 1) + else: + y_scaled = self.torch_model(xt).detach().cpu().numpy().reshape(-1, 1) + + y_raw = self.y_scaler.inverse_transform(y_scaled) + return y_raw.reshape(-1, 1) + + +class TorchMLPRegressor(nn.Module): + expects_channel = False + + def __init__(self, input_dims: int, hidden_units: Sequence[int], dropout: float): + super().__init__() + layers = [] + prev = int(input_dims) + for w in hidden_units: + layers.append(nn.Linear(prev, int(w))) + layers.append(nn.ELU()) + if dropout > 1e-8: + layers.append(nn.Dropout(float(dropout))) + prev = int(w) + layers.append(nn.Linear(prev, 1)) + self.net = nn.Sequential(*layers) + + def forward(self, x): + return self.net(x) + + +class TorchCNNRegressor(nn.Module): + expects_channel = True + + def __init__(self, input_dims: int, dropout: float): + super().__init__() + flat_dim = 16 * max(1, int(input_dims) - 2) + self.net = nn.Sequential( + nn.Conv1d(1, 128, kernel_size=3, padding=1), + nn.ELU(), + nn.MaxPool1d(kernel_size=2, stride=1), + nn.Dropout(float(dropout)), + nn.Conv1d(128, 64, kernel_size=3, padding=1), + nn.ELU(), + nn.MaxPool1d(kernel_size=2, stride=1), + nn.Dropout(float(dropout)), + nn.Conv1d(64, 32, kernel_size=3, padding=1), + nn.ELU(), + nn.Conv1d(32, 16, kernel_size=3, padding=1), + nn.ELU(), + nn.Flatten(), + nn.Linear(flat_dim, 64), + nn.ELU(), + nn.Linear(64, 1), + ) + + def forward(self, x): + return self.net(x) + + +@dataclass +class FlowControlSurrogateV6: + input_dims: int + learning_rate: float = 1e-3 + batch_size: int = 64 + epochs: int = 500 + test_size: float = 0.2 + train_test_split_random_state: int = 42 + patience: int = 30 + check_point_path: Path = Path("surrogate_v6.pt") + hidden_units: Sequence[int] = field(default_factory=lambda: (128, 64, 32)) + architecture: str = "mlp" + dropout: float = 0.10 + weight_decay: float = 1e-6 + tf_device_id: Optional[int] = None + require_gpu: bool = True + + def __post_init__(self): + self.model: Optional[Any] = None + self.x_scaler = StandardScaler() + self.y_scaler = StandardScaler() + self.last_fit_metrics: Dict[str, float] = {} + + @staticmethod + def _forward_with_runtime_guard(model: Any, x: Any): + if hasattr(model, "expects_channel") and model.expects_channel: + with torch.backends.cudnn.flags(enabled=False): + return model(x.unsqueeze(1)) + return model(x) + + @staticmethod + def _ensure_torch_ready() -> None: + if torch is None: + raise ImportError( + f"PyTorch is required for FlowControlSurrogateV6 but is not available: {_TORCH_IMPORT_ERROR}" + ) + + def _pick_device(self) -> str: + self._ensure_torch_ready() + if not torch.cuda.is_available(): + if self.require_gpu: + raise RuntimeError("FlowControlSurrogateV6 requires GPU but torch.cuda is not available") + return "cpu" + + if self.tf_device_id is None: + return "cuda:0" + idx = int(max(0, min(torch.cuda.device_count() - 1, int(self.tf_device_id)))) + return f"cuda:{idx}" + + def _build_model(self): + arch = str(self.architecture).lower() + if arch == "cnn": + return TorchCNNRegressor(input_dims=self.input_dims, dropout=float(self.dropout)) + return TorchMLPRegressor( + input_dims=self.input_dims, + hidden_units=self.hidden_units, + dropout=float(self.dropout), + ) + + def __call__(self, x, y, verbose: int = 0): + self._ensure_torch_ready() + x = np.asarray(x, dtype=np.float64) + y = np.asarray(y, dtype=np.float64).reshape(-1) + + x_train, x_val, y_train, y_val = train_test_split( + x, + y, + test_size=self.test_size, + random_state=self.train_test_split_random_state, + shuffle=True, + ) + + x_train_s = self.x_scaler.fit_transform(x_train).astype(np.float32) + x_val_s = self.x_scaler.transform(x_val).astype(np.float32) + y_train_s = self.y_scaler.fit_transform(y_train.reshape(-1, 1)).reshape(-1).astype(np.float32) + + device = self._pick_device() + model = self._build_model().to(device) + optimizer = optim.Adam(model.parameters(), lr=float(self.learning_rate), weight_decay=float(self.weight_decay)) + criterion = nn.MSELoss() + + xt = torch.as_tensor(x_train_s, dtype=torch.float32, device=device) + yt = torch.as_tensor(y_train_s, dtype=torch.float32, device=device).reshape(-1, 1) + + xv = torch.as_tensor(x_val_s, dtype=torch.float32, device=device) + + batch = int(max(8, min(int(self.batch_size), len(x_train_s)))) + best_loss = np.inf + best_state = None + bad_epochs = 0 + epochs_ran = 0 + + model.train() + for ep in range(int(self.epochs)): + perm = torch.randperm(xt.shape[0], device=device) + epoch_loss = 0.0 + n_batches = 0 + + for i in range(0, xt.shape[0], batch): + idx = perm[i : i + batch] + xb = xt[idx] + yb = yt[idx] + optimizer.zero_grad(set_to_none=True) + pred = self._forward_with_runtime_guard(model, xb) + loss = criterion(pred, yb) + loss.backward() + optimizer.step() + epoch_loss += float(loss.item()) + n_batches += 1 + + train_loss = epoch_loss / max(1, n_batches) + epochs_ran = ep + 1 + + if train_loss + 1e-10 < best_loss: + best_loss = train_loss + best_state = {k: v.detach().cpu().clone() for k, v in model.state_dict().items()} + bad_epochs = 0 + else: + bad_epochs += 1 + + if bad_epochs >= int(self.patience): + break + + if best_state is not None: + model.load_state_dict(best_state) + + model.eval() + with torch.no_grad(): + y_val_pred_s = self._forward_with_runtime_guard(model, xv).detach().cpu().numpy().reshape(-1, 1) + + y_val_pred = self.y_scaler.inverse_transform(y_val_pred_s).reshape(-1) + + arch = str(self.architecture).lower() + gpu_idx = int(str(device).split(":")[1]) if str(device).startswith("cuda") else -1 + self.last_fit_metrics = { + "val_r2": float(r2_score(y_val, y_val_pred)), + "val_mae": float(mean_absolute_error(y_val, y_val_pred)), + "val_count": int(len(y_val)), + "best_val_loss": float(best_loss), + "epochs_ran": int(epochs_ran), + "architecture": arch, + "device": f"GPU:{gpu_idx}" if gpu_idx >= 0 else "CPU", + "gpu_count": int(torch.cuda.device_count() if torch.cuda.is_available() else 0), + "selected_gpu": int(gpu_idx), + } + + self.model = model + return TorchScaledPredictWrapper(model, self.x_scaler, self.y_scaler, device=device) diff --git a/archive/dante_pinball/dante_pinball/legacy/fit_ppo_control_sindy.py b/archive/dante_pinball/dante_pinball/legacy/fit_ppo_control_sindy.py new file mode 100644 index 0000000..fa38f50 --- /dev/null +++ b/archive/dante_pinball/dante_pinball/legacy/fit_ppo_control_sindy.py @@ -0,0 +1,289 @@ +import json +import os +import pickle +from typing import Dict, List, Tuple + +import numpy as np +import pysindy as ps + + +CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) +ROOT = os.path.abspath(os.path.join(CURRENT_DIR, os.pardir)) + +DATA_PATH = os.path.join(ROOT, "output", "d1a3o12_250421_forces02_sindy_dataset.pkl") +OUT_DIR = os.path.join(ROOT, "output", "report_dante_v3_v4") +OUT_JSON = os.path.join(OUT_DIR, "ppo_sindy_control_fit.json") + +WARMUP_STEPS = 0 +MAX_LAG = 1 +THRESHOLDS = [0.0, 0.005, 0.01, 0.015, 0.02, 0.03, 0.05] +NZ_RATIO_THRESHOLD = 0.70 + + +def episode_metric(rewards: np.ndarray, warmup: int = 150) -> float: + r = np.asarray(rewards, dtype=np.float64).reshape(-1) + if r.size == 0: + return 0.0 + eff = r[warmup:] if r.size > warmup else r[-1:] + return float(np.mean(eff[-100:])) if eff.size >= 100 else float(np.mean(eff)) + + +def load_episodes(path: str) -> Tuple[List[Dict], Dict]: + with open(path, "rb") as f: + data = pickle.load(f) + + if isinstance(data, dict) and "episodes" in data: + return list(data["episodes"]), dict(data.get("meta", {})) + if isinstance(data, list): + return data, {} + raise RuntimeError(f"Unsupported dataset format: {type(data)}") + + +def extract_episode_arrays(ep: Dict) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: + actions = np.asarray(ep.get("actions", []), dtype=np.float64) + observations = np.asarray(ep.get("observations", []), dtype=np.float64) + rewards = np.asarray(ep.get("rewards", []), dtype=np.float64) + + if actions.ndim != 2: + actions = actions.reshape(actions.shape[0], -1) + actions = actions[:, :3] + + if observations.ndim != 2: + observations = observations.reshape(observations.shape[0], -1) + observations = observations[:, :2] + + n = min(actions.shape[0], observations.shape[0], rewards.shape[0]) + return actions[:n], observations[:n], rewards[:n] + + +def build_dataset(episodes: List[Dict], warmup: int = 150) -> Tuple[np.ndarray, np.ndarray, List[str], Dict]: + x_rows = [] + y_rows = [] + + feat_names = [ + "bias1", + "obs0", + "obs1", + "dobs0", + "dobs1", + "sin_obs0", + "sin_obs1", + "cos_obs0", + "cos_obs1", + "tanh_obs0", + "tanh_obs1", + "act0_l1", + "act1_l1", + "act2_l1", + ] + + stats = {"episodes_used": 0, "samples_used": 0} + + for ep in episodes: + actions, obs2, rewards = extract_episode_arrays(ep) + t_len = min(actions.shape[0], obs2.shape[0], rewards.shape[0]) + if t_len <= (MAX_LAG + warmup + 1): + continue + + for t in range(MAX_LAG, t_len): + if t < warmup: + continue + + o = obs2[t] + o1 = obs2[t - 1] + a_prev = actions[t - 1] + + x_rows.append( + [ + 1.0, + o[0], + o[1], + o[0] - o1[0], + o[1] - o1[1], + np.sin(np.pi * o[0]), + np.sin(np.pi * o[1]), + np.cos(np.pi * o[0]), + np.cos(np.pi * o[1]), + np.tanh(o[0]), + np.tanh(o[1]), + a_prev[0], + a_prev[1], + a_prev[2], + ] + ) + y_rows.append(actions[t]) + + stats["episodes_used"] += 1 + + if len(x_rows) < 512: + raise RuntimeError(f"Too few samples for fitting: {len(x_rows)}") + + x = np.asarray(x_rows, dtype=np.float64) + y = np.asarray(y_rows, dtype=np.float64) + stats["samples_used"] = int(x.shape[0]) + return x, y, feat_names, stats + + +def r2(y: np.ndarray, yp: np.ndarray) -> float: + ssr = float(np.sum((y - yp) ** 2)) + sst = float(np.sum((y - np.mean(y)) ** 2) + 1e-12) + return float(1.0 - ssr / sst) + + +def fit_channel_grid(x: np.ndarray, y: np.ndarray, thresholds: List[float]): + std = np.std(x, axis=0) + std = np.where(std < 1e-8, 1.0, std) + xs = x / std + + rows = [] + for th in thresholds: + opt = ps.STLSQ(threshold=th, alpha=1e-4, max_iter=25) + opt.fit(xs, y) + coef = np.asarray(opt.coef_, dtype=np.float64).reshape(-1) / std + yp = x @ coef + rows.append( + { + "threshold": float(th), + "nz": int(np.sum(np.abs(coef) > 1e-8)), + "r2": r2(y, yp), + "mae": float(np.mean(np.abs(y - yp))), + "coef": coef, + } + ) + + best = max(rows, key=lambda z: z["r2"]) + return rows, best + + +def top_terms(feat_names: List[str], coef: np.ndarray, topk: int = 10) -> List[Dict]: + idx = np.argsort(np.abs(coef))[::-1] + out = [] + for i in idx: + c = float(coef[i]) + if abs(c) < 1e-8: + continue + out.append({"term": feat_names[i], "coef": c, "abs_coef": float(abs(c))}) + if len(out) >= topk: + break + return out + + +def main() -> None: + os.makedirs(OUT_DIR, exist_ok=True) + episodes_all, meta = load_episodes(DATA_PATH) + + ppo_eps = [ep for ep in episodes_all if str(ep.get("source", "unknown")) == "ppo_eval"] + if len(ppo_eps) < 10: + # Fallback only when ppo episodes are too few. + ppo_eps = episodes_all + + metrics = [] + for i, ep in enumerate(ppo_eps): + _, _, rewards = extract_episode_arrays(ep) + metrics.append((i, episode_metric(rewards, warmup=WARMUP_STEPS))) + metrics_sorted = sorted(metrics, key=lambda x: x[1], reverse=True) + + x, y, feat_names, data_stats = build_dataset(ppo_eps, warmup=WARMUP_STEPS) + + channel_models = [] + votes = np.zeros(len(feat_names), dtype=np.int64) + coef_abs_sum = np.zeros(len(feat_names), dtype=np.float64) + nz_ratios = [] + + for ch in range(3): + rows, best = fit_channel_grid(x, y[:, ch], THRESHOLDS) + coef = best["coef"] + active = np.abs(coef) >= 0.01 + votes += active.astype(np.int64) + coef_abs_sum += np.abs(coef) + nz_nonbias = int(np.sum(np.abs(coef[1:]) > 1e-8)) + denom_nonbias = int(max(1, len(feat_names) - 1)) + nz_ratio = float(nz_nonbias / denom_nonbias) + nz_ratios.append(nz_ratio) + + channel_models.append( + { + "channel": ch, + "r2": float(best["r2"]), + "mae": float(best["mae"]), + "best_sparse": { + "threshold": float(best["threshold"]), + "nz": int(best["nz"]), + "nz_nonbias": nz_nonbias, + "nz_ratio": nz_ratio, + }, + "top_terms": top_terms(feat_names, coef, topk=10), + "grid": [ + { + "threshold": float(z["threshold"]), + "nz": int(z["nz"]), + "r2": float(z["r2"]), + "mae": float(z["mae"]), + } + for z in rows + ], + } + ) + + score_idx = sorted( + range(len(feat_names)), + key=lambda k: (int(votes[k]), float(coef_abs_sum[k])), + reverse=True, + ) + global_top = [feat_names[k] for k in score_idx if feat_names[k] != "bias1"] + over_complex_channels = [int(cm["channel"]) for cm in channel_models if cm["best_sparse"]["nz_ratio"] >= NZ_RATIO_THRESHOLD] + use_sindy_prior = len(over_complex_channels) == 0 + + if use_sindy_prior: + prior_reason = ( + f"all channels nz_ratio < {NZ_RATIO_THRESHOLD:.2f}; sparse structure is sufficiently compressible" + ) + else: + prior_reason = ( + f"channels {over_complex_channels} have nz_ratio >= {NZ_RATIO_THRESHOLD:.2f}; sparse structure is too dense" + ) + + result = { + "data_path": DATA_PATH, + "dataset_meta": meta, + "warmup_steps": WARMUP_STEPS, + "max_lag": MAX_LAG, + "nz_ratio_threshold": NZ_RATIO_THRESHOLD, + "episodes_total": len(episodes_all), + "episodes_used_source": "ppo_eval" if len([ep for ep in episodes_all if str(ep.get("source", "")) == "ppo_eval"]) >= 10 else "all", + "episodes_used_count": len(ppo_eps), + "episode_metrics_top10": metrics_sorted[:10], + "fit_data_stats": data_stats, + "threshold_grid": THRESHOLDS, + "feature_names": feat_names, + "channel_models": channel_models, + "global_term_votes": {feat_names[k]: int(votes[k]) for k in range(len(feat_names))}, + "global_term_abs_coef_sum": {feat_names[k]: float(coef_abs_sum[k]) for k in range(len(feat_names))}, + "global_top_terms": global_top[:12], + "complexity_decision": { + "nz_ratio_threshold": float(NZ_RATIO_THRESHOLD), + "channel_nz_ratio": {f"ch{i}": float(z) for i, z in enumerate(nz_ratios)}, + "over_complex_channels": over_complex_channels, + "use_sindy_prior": bool(use_sindy_prior), + "reason": prior_reason, + }, + } + + with open(OUT_JSON, "w", encoding="utf-8") as f: + json.dump(result, f, indent=2, ensure_ascii=False) + + print(f"saved: {OUT_JSON}") + print("episodes used:", len(ppo_eps), "/", len(episodes_all)) + print("samples:", data_stats["samples_used"]) + print("global top terms:", result["global_top_terms"][:8]) + for cm in channel_models: + print( + f"ch{cm['channel']} r2={cm['r2']:.4f} mae={cm['mae']:.5f} " + f"nz={cm['best_sparse']['nz']} nz_ratio={cm['best_sparse']['nz_ratio']:.3f}" + ) + print("use_sindy_prior:", result["complexity_decision"]["use_sindy_prior"]) + print("reason:", result["complexity_decision"]["reason"]) + + +if __name__ == "__main__": + main() diff --git a/archive/dante_pinball/dante_pinball/legacy/generate_v6_v7_ppo_briefing.py b/archive/dante_pinball/dante_pinball/legacy/generate_v6_v7_ppo_briefing.py new file mode 100644 index 0000000..5d35087 --- /dev/null +++ b/archive/dante_pinball/dante_pinball/legacy/generate_v6_v7_ppo_briefing.py @@ -0,0 +1,658 @@ +import json +import os +import pickle +from dataclasses import dataclass +from typing import Dict, List, Tuple + +import matplotlib.pyplot as plt +import numpy as np +from sklearn.decomposition import PCA +from sklearn.preprocessing import StandardScaler + +from dante_pinball.env.gym_env_dante_total_force import CustomEnv + + +ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) +OUT_DIR = os.path.join(ROOT, "output", "report_dante_v2_v5_v6") +os.makedirs(OUT_DIR, exist_ok=True) + +V6_RUN = "d1a3o12_250421_forces02_dante_v6_3" +V7_RUN = "d1a3o12_250421_forces02_dante_v7_1" +SEEDS = [11, 29, 47] +N_ACT = 3 +EVAL_STEPS = 300 + + +def feature_dict(obs_t: np.ndarray, obs_prev: np.ndarray, act_prev: np.ndarray) -> Dict[str, float]: + o0, o1 = float(obs_t[0]), float(obs_t[1]) + p0, p1 = float(obs_prev[0]), float(obs_prev[1]) + a0, a1, a2 = float(act_prev[0]), float(act_prev[1]), float(act_prev[2]) + return { + "obs0": o0, + "obs1": o1, + "dobs0": o0 - p0, + "dobs1": o1 - p1, + "sin_obs0": float(np.sin(np.pi * o0)), + "sin_obs1": float(np.sin(np.pi * o1)), + "cos_obs0": float(np.cos(np.pi * o0)), + "cos_obs1": float(np.cos(np.pi * o1)), + "tanh_obs0": float(np.tanh(o0)), + "tanh_obs1": float(np.tanh(o1)), + "act0_l1": a0, + "act1_l1": a1, + "act2_l1": a2, + } + + +def inv_tanh_map(q: float) -> float: + qq = float(np.clip(q, -0.999, 0.999)) + x = np.arctanh(qq) / 1.25 + return float(np.clip(x, -1.0, 1.0)) + + +@dataclass +class RunData: + name: str + x: np.ndarray + y: np.ndarray + num_initial: int + cfg: Dict + + +def load_run(name: str) -> RunData: + db_path = os.path.join(ROOT, "output", f"{name}_database_live.npz") + cfg_path = os.path.join(ROOT, "output", f"{name}_structure_decision.json") + + z = np.load(db_path, allow_pickle=True) + x = np.asarray(z["input_x"], dtype=np.float64) + y = np.asarray(z["input_y"], dtype=np.float64) + with open(cfg_path, "r", encoding="utf-8") as f: + cfg = json.load(f) + + return RunData(name=name, x=x, y=y, num_initial=int(cfg["num_initial"]), cfg=cfg) + + +def ppo_npz(seed: int) -> str: + return os.path.join(OUT_DIR, f"raw_oldenv_seed_{seed}.npz") + + +def ppo_point_for_v6(seed: int, basis_terms: List[str]) -> np.ndarray: + z = np.load(ppo_npz(seed)) + obs = np.asarray(z["ppo_obs"], dtype=np.float64) + act = np.asarray(z["ppo_actions"], dtype=np.float64) + + rows_x = [] + rows_y = [] + for t in range(1, len(obs)): + fd = feature_dict(obs[t], obs[t - 1], act[t - 1]) + rows_x.append([1.0] + [float(fd[k]) for k in basis_terms]) + rows_y.append(act[t]) + + X = np.asarray(rows_x, dtype=np.float64) + Y = np.asarray(rows_y, dtype=np.float64) + + params = [] + for ch in range(3): + coef, *_ = np.linalg.lstsq(X, Y[:, ch], rcond=None) + bias = float(coef[0]) + params.append(inv_tanh_map(bias / 1.0)) + for c in coef[1:]: + params.append(inv_tanh_map(float(c) / 2.0)) + + return np.asarray(params, dtype=np.float64) + + +def phase_weights(phase: float, k: int) -> np.ndarray: + z = float(np.mod(phase, 1.0)) * k + i0 = int(np.floor(z)) % k + frac = float(z - np.floor(z)) + i1 = (i0 + 1) % k + w = np.zeros(k, dtype=np.float64) + w[i0] += (1.0 - frac) + w[i1] += frac + return w + + +def ppo_point_for_v7(seed: int, k: int, period_steps: float) -> np.ndarray: + z = np.load(ppo_npz(seed)) + act = np.asarray(z["ppo_actions"], dtype=np.float64) + + n = int(act.shape[0]) + phase = 0.0 + step_phase = 1.0 / max(1e-6, float(period_steps)) + + W = np.zeros((n, k), dtype=np.float64) + for t in range(n): + W[t] = phase_weights(phase, k) + phase = (phase + step_phase) % 1.0 + + params = [] + for ch in range(3): + coef, *_ = np.linalg.lstsq(W, act[:, ch], rcond=None) + coef = np.clip(coef, -1.0, 1.0) + params.extend(coef.tolist()) + + return np.asarray(params, dtype=np.float64) + + +def fit_pca_and_project(x: np.ndarray, extra: np.ndarray) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: + scaler = StandardScaler() + xs = scaler.fit_transform(x) + extra_s = scaler.transform(extra) + + pca = PCA(n_components=2, random_state=0) + x2 = pca.fit_transform(xs) + extra2 = pca.transform(extra_s) + return x2, extra2, pca.explained_variance_ratio_ + + +def plot_pca(run_name: str, x2: np.ndarray, y: np.ndarray, ppo2: np.ndarray, evr: np.ndarray, out_png: str) -> None: + plt.figure(figsize=(8, 6)) + sc = plt.scatter(x2[:, 0], x2[:, 1], c=y, cmap="viridis", s=12, alpha=0.78) + plt.colorbar(sc, label="reward_scaled") + + colors = ["#e41a1c", "#377eb8", "#ff7f00"] + for i, seed in enumerate(SEEDS): + plt.scatter( + [ppo2[i, 0]], + [ppo2[i, 1]], + s=130, + c=colors[i], + marker="*", + edgecolors="k", + linewidths=0.9, + label=f"PPO fitted seed {seed}", + zorder=6, + ) + + plt.title(f"{run_name}: PCA with 3 PPO fitted points\\nPC1 {evr[0]*100:.1f}% | PC2 {evr[1]*100:.1f}%") + plt.xlabel("PC1") + plt.ylabel("PC2") + plt.legend(loc="best", fontsize=9) + plt.grid(alpha=0.25) + plt.tight_layout() + plt.savefig(out_png, dpi=170) + plt.close() + + +class LinearBasisController: + def __init__(self, basis_terms: List[str]): + self.basis_terms = list(basis_terms) + self.num_basis = len(self.basis_terms) + self.total_params = N_ACT * (1 + self.num_basis) + self.params = np.zeros(self.total_params, dtype=np.float64) + self.obs_l1 = np.zeros(2, dtype=np.float64) + self.prev_action = np.zeros(3, dtype=np.float64) + + def reset_state(self, obs0: np.ndarray) -> None: + obs0 = np.asarray(obs0, dtype=np.float64).reshape(-1) + if obs0.size < 2: + obs0 = np.zeros(2, dtype=np.float64) + self.obs_l1 = obs0[:2].copy() + self.prev_action = np.zeros(3, dtype=np.float64) + + def set_params(self, x: np.ndarray) -> None: + x = np.asarray(x, dtype=np.float64).reshape(-1) + if x.size != self.total_params: + raise ValueError(f"controller params mismatch: {x.size} != {self.total_params}") + self.params = np.clip(x, -1.0, 1.0) + + def _feature_dict(self, obs: np.ndarray) -> Dict[str, float]: + o = np.asarray(obs, dtype=np.float64).reshape(-1) + if o.size < 2: + o = np.zeros(2, dtype=np.float64) + + o0, o1 = float(o[0]), float(o[1]) + o0_l1, o1_l1 = float(self.obs_l1[0]), float(self.obs_l1[1]) + a0, a1, a2 = float(self.prev_action[0]), float(self.prev_action[1]), float(self.prev_action[2]) + + return { + "obs0": o0, + "obs1": o1, + "dobs0": o0 - o0_l1, + "dobs1": o1 - o1_l1, + "sin_obs0": float(np.sin(np.pi * o0)), + "sin_obs1": float(np.sin(np.pi * o1)), + "cos_obs0": float(np.cos(np.pi * o0)), + "cos_obs1": float(np.cos(np.pi * o1)), + "tanh_obs0": float(np.tanh(o0)), + "tanh_obs1": float(np.tanh(o1)), + "act0_l1": a0, + "act1_l1": a1, + "act2_l1": a2, + } + + def predict(self, obs: np.ndarray) -> np.ndarray: + feat = self._feature_dict(obs) + out = np.zeros(3, dtype=np.float64) + stride = 1 + self.num_basis + + for ch in range(3): + off = ch * stride + y = np.tanh(1.25 * self.params[off]) + for k, term in enumerate(self.basis_terms): + y += (2.0 * np.tanh(1.25 * self.params[off + 1 + k])) * feat.get(term, 0.0) + out[ch] = y + + out = np.clip(out, -1.0, 1.0) + obs2 = np.asarray(obs, dtype=np.float64).reshape(-1) + if obs2.size < 2: + obs2 = np.zeros(2, dtype=np.float64) + self.obs_l1 = obs2[:2].copy() + self.prev_action = out.copy() + return out.astype(np.float32) + + +class PeriodicOpenLoopController: + def __init__(self, control_points_per_channel: int, period_steps: float): + self.k = int(control_points_per_channel) + self.ctrl_points = np.zeros((N_ACT, self.k), dtype=np.float64) + self.phase = 0.0 + self.current_period_steps = float(period_steps) + self.total_params = int(N_ACT * self.k) + + def reset_state(self) -> None: + self.phase = 0.0 + + def _eval_channel(self, points: np.ndarray, phase: float) -> float: + p = np.asarray(points, dtype=np.float64).reshape(-1) + z = float(np.mod(phase, 1.0)) * self.k + i0 = int(np.floor(z)) % self.k + frac = float(z - np.floor(z)) + i1 = (i0 + 1) % self.k + return float((1.0 - frac) * p[i0] + frac * p[i1]) + + def set_params(self, x: np.ndarray) -> None: + x = np.asarray(x, dtype=np.float64).reshape(-1) + if x.size != self.total_params: + raise ValueError(f"controller params mismatch: {x.size} != {self.total_params}") + core = np.clip(x, -1.0, 1.0) + self.ctrl_points = core.reshape(N_ACT, self.k) + + def predict(self, _obs: np.ndarray) -> np.ndarray: + action = np.zeros(N_ACT, dtype=np.float64) + for ch in range(N_ACT): + action[ch] = self._eval_channel(self.ctrl_points[ch], self.phase) + action = np.clip(action, -1.0, 1.0) + step_phase = 1.0 / max(1e-6, float(self.current_period_steps)) + self.phase = float((self.phase + step_phase) % 1.0) + return action.astype(np.float32) + + +def extract_ux_uy(env: CustomEnv) -> Tuple[np.ndarray, np.ndarray]: + nx = env.flow_field.FIELD_SHAPE[0] + ny = env.flow_field.FIELD_SHAPE[1] + env.flow_field.get_ddf() + ddf = env.flow_field.ddf.copy().reshape((9, ny, nx)).transpose(2, 1, 0) + ux = ddf[:, :, 1] + ddf[:, :, 5] + ddf[:, :, 8] - ddf[:, :, 3] - ddf[:, :, 6] - ddf[:, :, 7] + uy = ddf[:, :, 2] + ddf[:, :, 5] + ddf[:, :, 6] - ddf[:, :, 4] - ddf[:, :, 7] - ddf[:, :, 8] + return ux.astype(np.float64), uy.astype(np.float64) + + +def vorticity_from_ux_uy(ux: np.ndarray, uy: np.ndarray) -> np.ndarray: + dvy_dx = np.gradient(uy, axis=0) + dvx_dy = np.gradient(ux, axis=1) + return (dvy_dx - dvx_dy).astype(np.float64) + + +def rollout_controller_v6(params: np.ndarray, basis_terms: List[str], device_id: int = 1) -> Dict[str, np.ndarray]: + env = CustomEnv(device_id=int(device_id)) + ctrl = LinearBasisController(basis_terms) + ctrl.set_params(params) + + obs, _ = env.reset() + obs = np.asarray(obs, dtype=np.float32) + ctrl.reset_state(obs) + + obs_hist = [] + act_hist = [] + rew_hist = [] + + try: + for _ in range(EVAL_STEPS): + act = ctrl.predict(obs) + next_obs, reward, done, trunc, _ = env.step(act) + obs_hist.append(np.asarray(obs, dtype=np.float64).copy()) + act_hist.append(np.asarray(act, dtype=np.float64).copy()) + rew_hist.append(float(reward)) + obs = np.asarray(next_obs, dtype=np.float32) + if done or trunc: + obs, _ = env.reset() + obs = np.asarray(obs, dtype=np.float32) + ctrl.reset_state(obs) + + ux, uy = extract_ux_uy(env) + finally: + env.close() + + return { + "obs": np.asarray(obs_hist, dtype=np.float64), + "act": np.asarray(act_hist, dtype=np.float64), + "rew": np.asarray(rew_hist, dtype=np.float64), + "ux": ux, + "uy": uy, + } + + +def rollout_controller_v7(params: np.ndarray, k: int, period_steps: float, device_id: int = 0) -> Dict[str, np.ndarray]: + env = CustomEnv(device_id=int(device_id)) + ctrl = PeriodicOpenLoopController(control_points_per_channel=k, period_steps=float(period_steps)) + ctrl.set_params(params) + + obs, _ = env.reset() + obs = np.asarray(obs, dtype=np.float32) + ctrl.reset_state() + + obs_hist = [] + act_hist = [] + rew_hist = [] + + try: + for _ in range(EVAL_STEPS): + act = ctrl.predict(obs) + next_obs, reward, done, trunc, _ = env.step(act) + obs_hist.append(np.asarray(obs, dtype=np.float64).copy()) + act_hist.append(np.asarray(act, dtype=np.float64).copy()) + rew_hist.append(float(reward)) + obs = np.asarray(next_obs, dtype=np.float32) + if done or trunc: + obs, _ = env.reset() + obs = np.asarray(obs, dtype=np.float32) + ctrl.reset_state() + + ux, uy = extract_ux_uy(env) + finally: + env.close() + + return { + "obs": np.asarray(obs_hist, dtype=np.float64), + "act": np.asarray(act_hist, dtype=np.float64), + "rew": np.asarray(rew_hist, dtype=np.float64), + "ux": ux, + "uy": uy, + } + + +def best_ppo_seed() -> Tuple[int, float]: + best_seed = None + best_tail = -1e18 + for s in SEEDS: + z = np.load(ppo_npz(s)) + r = np.asarray(z["ppo_rewards"], dtype=np.float64) + tail = float(np.mean(r[-100:])) + if tail > best_tail: + best_tail = tail + best_seed = s + return int(best_seed), float(best_tail) + + +def load_ppo_series(seed: int) -> Dict[str, np.ndarray]: + z = np.load(ppo_npz(seed)) + return { + "obs": np.asarray(z["ppo_obs"], dtype=np.float64), + "act": np.asarray(z["ppo_actions"], dtype=np.float64), + "rew": np.asarray(z["ppo_rewards"], dtype=np.float64), + } + + +def plot_obs_act_time(ppo: Dict[str, np.ndarray], v6: Dict[str, np.ndarray], v7: Dict[str, np.ndarray], out_png: str) -> None: + t = np.arange(EVAL_STEPS) + fig, axes = plt.subplots(5, 1, figsize=(12, 12), sharex=True, constrained_layout=True) + + series = [ + (0, "obs0", ppo["obs"][:, 0], v6["obs"][:, 0], v7["obs"][:, 0]), + (1, "obs1", ppo["obs"][:, 1], v6["obs"][:, 1], v7["obs"][:, 1]), + (2, "act0", ppo["act"][:, 0], v6["act"][:, 0], v7["act"][:, 0]), + (3, "act1", ppo["act"][:, 1], v6["act"][:, 1], v7["act"][:, 1]), + (4, "act2", ppo["act"][:, 2], v6["act"][:, 2], v7["act"][:, 2]), + ] + + for idx, name, y0, y1, y2 in series: + ax = axes[idx] + ax.plot(t, y0, lw=1.2, label="PPO") + ax.plot(t, y1, lw=1.2, label="v6") + ax.plot(t, y2, lw=1.2, label="v7") + ax.set_ylabel(name) + ax.grid(alpha=0.25) + if idx == 0: + ax.legend(loc="best", ncol=3) + axes[-1].set_xlabel("time step") + fig.suptitle("Obs-Act time series comparison (300 steps)") + fig.savefig(out_png, dpi=170) + plt.close(fig) + + +def plot_vorticity_maps(omega_ppo: np.ndarray, omega_v6: np.ndarray, omega_v7: np.ndarray, out_png: str) -> float: + gmax = float(max(np.max(np.abs(omega_ppo)), np.max(np.abs(omega_v6)), np.max(np.abs(omega_v7)))) + vmax = max(1e-12, 0.1 * gmax) + + fig, axes = plt.subplots(1, 3, figsize=(15, 4.8), constrained_layout=True) + data = [(omega_ppo, "PPO"), (omega_v6, "v6"), (omega_v7, "v7")] + for ax, (om, title) in zip(axes, data): + im = ax.imshow(om.T, origin="lower", cmap="RdBu_r", vmin=-vmax, vmax=vmax) + ax.set_title(title) + ax.set_xlabel("x") + ax.set_ylabel("y") + cbar = fig.colorbar(im, ax=axes.ravel().tolist(), shrink=0.95) + cbar.set_label("vorticity") + fig.suptitle(f"Final vorticity maps, unified range +/-{vmax:.4f} (10% global max)") + fig.savefig(out_png, dpi=170) + plt.close(fig) + return float(vmax) + + +def build_design(obs: np.ndarray, act: np.ndarray, terms: List[str]) -> Tuple[np.ndarray, np.ndarray]: + X_rows = [] + Y_rows = [] + for t in range(1, len(obs)): + fd = feature_dict(obs[t], obs[t - 1], act[t - 1]) + X_rows.append([1.0] + [float(fd[k]) for k in terms]) + Y_rows.append(act[t]) + return np.asarray(X_rows, dtype=np.float64), np.asarray(Y_rows, dtype=np.float64) + + +def fit_multi_seed_linear(terms: List[str]) -> Dict[str, object]: + Ws = [] + r2s = [] + for s in SEEDS: + z = np.load(ppo_npz(s)) + obs = np.asarray(z["ppo_obs"], dtype=np.float64) + act = np.asarray(z["ppo_actions"], dtype=np.float64) + X, Y = build_design(obs, act, terms) + W = [] + r2_ch = [] + for ch in range(3): + c, *_ = np.linalg.lstsq(X, Y[:, ch], rcond=None) + pred = X @ c + ssr = float(np.sum((Y[:, ch] - pred) ** 2)) + sst = float(np.sum((Y[:, ch] - np.mean(Y[:, ch])) ** 2) + 1e-12) + r2 = float(1.0 - ssr / sst) + W.append(c) + r2_ch.append(r2) + Ws.append(np.asarray(W, dtype=np.float64)) + r2s.append(np.asarray(r2_ch, dtype=np.float64)) + Wm = np.mean(np.asarray(Ws), axis=0) + r2m = np.mean(np.asarray(r2s), axis=0) + return { + "terms": ["bias1"] + list(terms), + "W_mean": Wm, + "r2_by_action_mean": r2m, + "r2_mean": float(np.mean(r2m)), + } + + +def matrix_to_latex(W: np.ndarray, precision: int = 4) -> str: + rows = [] + for i in range(W.shape[0]): + rows.append(" & ".join([f"{float(v):.{precision}f}" for v in W[i]])) + body = " \\\\ ".join(rows) + return "\\begin{bmatrix}" + body + "\\end{bmatrix}" + + +def main() -> None: + v6 = load_run(V6_RUN) + v7 = load_run(V7_RUN) + + v6_basis = list(v6.cfg["basis_terms"]) + v6_ppo_pts = np.vstack([ppo_point_for_v6(s, v6_basis) for s in SEEDS]) + + k = int(v7.cfg["control_points_per_channel"]) + period_steps = float(v7.cfg["period_steps"]) + v7_ppo_pts = np.vstack([ppo_point_for_v7(s, k=k, period_steps=period_steps) for s in SEEDS]) + + v6_x2, v6_p2, v6_evr = fit_pca_and_project(v6.x, v6_ppo_pts) + v7_x2, v7_p2, v7_evr = fit_pca_and_project(v7.x, v7_ppo_pts) + + fig_v6_pca = os.path.join(OUT_DIR, "brief_v6_3_pca_with_ppo3.png") + fig_v7_pca = os.path.join(OUT_DIR, "brief_v7_1_pca_with_ppo3.png") + plot_pca(v6.name, v6_x2, v6.y, v6_p2, v6_evr, fig_v6_pca) + plot_pca(v7.name, v7_x2, v7.y, v7_p2, v7_evr, fig_v7_pca) + + with open(os.path.join(ROOT, "output", f"{V6_RUN}_best_meta.pkl"), "rb") as f: + v6_meta = pickle.load(f) + with open(os.path.join(ROOT, "output", f"{V7_RUN}_best_meta.pkl"), "rb") as f: + v7_meta = pickle.load(f) + + v6_best = np.asarray(v6_meta["best_params"], dtype=np.float64) + v7_best = np.asarray(v7_meta["best_params"], dtype=np.float64) + + v6_roll = rollout_controller_v6(v6_best, basis_terms=v6_basis, device_id=1) + v7_roll = rollout_controller_v7(v7_best, k=k, period_steps=period_steps, device_id=0) + + seed_star, tail_star = best_ppo_seed() + ppo_series = load_ppo_series(seed_star) + + fig_ts = os.path.join(OUT_DIR, "brief_v6_v7_ppo_obs_act_time.png") + plot_obs_act_time(ppo_series, v6_roll, v7_roll, fig_ts) + + ppo_flow_npz = os.path.join(OUT_DIR, "fig_oldenv_flow_best.npz") + zf = np.load(ppo_flow_npz) + ppo_ux = np.asarray(zf["ux"], dtype=np.float64) + ppo_uy = np.asarray(zf["uy"], dtype=np.float64) + + omega_ppo = vorticity_from_ux_uy(ppo_ux, ppo_uy) + omega_v6 = vorticity_from_ux_uy(v6_roll["ux"], v6_roll["uy"]) + omega_v7 = vorticity_from_ux_uy(v7_roll["ux"], v7_roll["uy"]) + + fig_omega = os.path.join(OUT_DIR, "brief_v6_v7_ppo_final_vorticity.png") + omega_vmax = plot_vorticity_maps(omega_ppo, omega_v6, omega_v7, fig_omega) + + with open(os.path.join(OUT_DIR, "sindy_group_sparsity_scan.json"), "r", encoding="utf-8") as f: + group_scan = json.load(f) + with open(os.path.join(OUT_DIR, "sindy_constrained_profile_search.json"), "r", encoding="utf-8") as f: + constrained = json.load(f) + with open(os.path.join(OUT_DIR, "dante_ackley_highdim_sweep.json"), "r", encoding="utf-8") as f: + highdim = json.load(f) + with open(os.path.join(OUT_DIR, "v6_v7_reaudit_frozen_facts_20260323.json"), "r", encoding="utf-8") as f: + frozen = json.load(f) + with open(os.path.join(OUT_DIR, "v6_v7_acq_diagnostics_summary_20260323.json"), "r", encoding="utf-8") as f: + acq = json.load(f) + + full_terms = [ + "obs0", "obs1", "dobs0", "dobs1", "sin_obs0", "sin_obs1", "cos_obs0", "cos_obs1", + "tanh_obs0", "tanh_obs1", "act0_l1", "act1_l1", "act2_l1", + ] + reduced_terms = list(constrained["best_chrono"]["basis_terms"]) + + full_fit = fit_multi_seed_linear(full_terms) + reduced_fit = fit_multi_seed_linear(reduced_terms) + + highdim_results = list(highdim.get("results", [])) + highdim_neg_r2 = int(sum(1 for r in highdim_results if float(r.get("holdout_r2", 0.0)) < 0.0)) + highdim_neg_gain = int(sum(1 for r in highdim_results if float(r.get("acq_gain_scaled", 0.0)) < 0.0)) + + summary = { + "runs": {"v6": V6_RUN, "v7": V7_RUN, "ppo_seed_used": int(seed_star), "ppo_tail100": float(tail_star)}, + "figures": { + "v6_pca": fig_v6_pca, + "v7_pca": fig_v7_pca, + "obs_act_time": fig_ts, + "vorticity": fig_omega, + }, + "vorticity_unified_abs_limit": float(omega_vmax), + "reduction": { + "full_r2_mean_over_seeds": float(group_scan["full_model"]["r2_mean_over_seeds"]), + "best_chrono_basis_terms": reduced_terms, + "best_chrono_r2_mean_over_seeds": float(constrained["best_chrono"]["chrono_r2_mean_over_seeds"]), + "best_chrono_r2_min_over_seeds": float(constrained["best_chrono"]["chrono_r2_min_over_seeds"]), + "group_scan_chosen": group_scan.get("chosen", {}), + "full_fit": { + "terms": full_fit["terms"], + "W_mean": np.asarray(full_fit["W_mean"]).tolist(), + "r2_by_action_mean": np.asarray(full_fit["r2_by_action_mean"]).tolist(), + "r2_mean": float(full_fit["r2_mean"]), + }, + "reduced_fit": { + "terms": reduced_fit["terms"], + "W_mean": np.asarray(reduced_fit["W_mean"]).tolist(), + "r2_by_action_mean": np.asarray(reduced_fit["r2_by_action_mean"]).tolist(), + "r2_mean": float(reduced_fit["r2_mean"]), + }, + "latex": { + "full_terms": "\\phi_{full}=[1,obs_0,obs_1,\\Delta obs_0,\\Delta obs_1,\\sin(\\pi obs_0),\\sin(\\pi obs_1),\\cos(\\pi obs_0),\\cos(\\pi obs_1),\\tanh(obs_0),\\tanh(obs_1),a_{0,t-1},a_{1,t-1},a_{2,t-1}]^\\top", + "reduced_terms": "\\phi_{red}=[1,obs_1,\\sin(\\pi obs_0),\\cos(\\pi obs_0),a_{1,t-1}]^\\top", + "W_full_mean": matrix_to_latex(np.asarray(full_fit["W_mean"], dtype=np.float64)), + "W_reduced_mean": matrix_to_latex(np.asarray(reduced_fit["W_mean"], dtype=np.float64)), + }, + }, + "highdim": { + "n_cases": int(len(highdim_results)), + "n_neg_holdout_r2": int(highdim_neg_r2), + "n_neg_acq_gain": int(highdim_neg_gain), + }, + "dual_surrogate": frozen.get("surrogate_log_stats", {}), + "acq_summary": acq, + } + + summary_path = os.path.join(OUT_DIR, "v6_v7_ppo_briefing_summary.json") + with open(summary_path, "w", encoding="utf-8") as f: + json.dump(summary, f, indent=2, ensure_ascii=False) + + md_path = os.path.join(OUT_DIR, "v6_v7_ppo_briefing_20260324.md") + with open(md_path, "w", encoding="utf-8") as f: + f.write("# v6/v7/PPO 综合简报\n\n") + f.write("## 1) PCA(含3个PPO拟合点)\n") + f.write(f"- v6图: {fig_v6_pca}\n") + f.write(f"- v7图: {fig_v7_pca}\n\n") + + f.write("## 2) obs-act时序 + 最终涡量\n") + f.write(f"- 时序图: {fig_ts}\n") + f.write(f"- 最终涡量图: {fig_omega}\n") + f.write(f"- 涡量统一色条范围: ±{omega_vmax:.6f}(按三者全局|omega|max的10%)\n") + f.write(f"- PPO时序选用seed={seed_star}(tail100={tail_star:.5f})\n\n") + + f.write("## 3) 函数约简、关键函数、最终组合\n") + f.write(f"- 全特征模型平均R2: {group_scan['full_model']['r2_mean_over_seeds']:.6f}\n") + f.write(f"- 约束搜索best_chrono基函数: {reduced_terms}\n") + f.write(f"- best_chrono平均R2: {constrained['best_chrono']['chrono_r2_mean_over_seeds']:.6f}\n") + f.write(f"- best_chrono最差seed R2: {constrained['best_chrono']['chrono_r2_min_over_seeds']:.6f}\n") + f.write("- 关键函数解释: obs1给出主状态幅值,sin/cos(pi*obs0)提供周期相位,act1_l1提供单步记忆。\n\n") + + f.write("### 学到方程与拟合方程(LaTeX)\n") + f.write("- 全特征学到方程(3动作联合线性写法):\n") + f.write("$$\\mathbf{a}_t = W_{full}\\,\\phi_{full,t}$$\n") + f.write("$$" + summary['reduction']['latex']['full_terms'] + "$$\n") + f.write("$$W_{full}=" + summary['reduction']['latex']['W_full_mean'] + "$$\n") + f.write(f"- 全特征拟合R2(本次重算, action均值): {full_fit['r2_mean']:.6f}\n\n") + + f.write("- 约简拟合方程(best_chrono):\n") + f.write("$$\\mathbf{a}_t = W_{red}\\,\\phi_{red,t}$$\n") + f.write("$$" + summary['reduction']['latex']['reduced_terms'] + "$$\n") + f.write("$$W_{red}=" + summary['reduction']['latex']['W_reduced_mean'] + "$$\n") + f.write(f"- 约简拟合R2(本次重算, action均值): {reduced_fit['r2_mean']:.6f}\n\n") + + f.write("## 4) 高维能力与现实约束反思\n") + f.write(f"- 高维扫描样本数: {len(highdim_results)}\n") + f.write(f"- holdout_r2<0 的case数: {highdim_neg_r2}/{len(highdim_results)}\n") + f.write(f"- 首轮acq_gain<0 的case数: {highdim_neg_gain}/{len(highdim_results)}\n") + f.write("- 解释: 高维下代理可辨识度不足时,Tree探索会被误导,出现边界漂移与收益停滞。\n") + f.write("- 与论文对齐: DANTE强调DNN surrogate与自适应探索协同;在你的流体控制里,受噪声、时序漂移和高维参数化耦合影响,样本效率会显著下降。\n") + f.write("- 双代理是否有帮助: 当前日志显示ensemble路径提供了可运行冗余(CNN失败时MLP兜底),但在v6_3其val_r2均值仍偏低,收益主要体现在稳定性而非显著提升最优值。\n") + + print("saved:", summary_path) + print("saved:", md_path) + print("saved figs:", fig_v6_pca, fig_v7_pca, fig_ts, fig_omega) + + +if __name__ == "__main__": + main() diff --git a/archive/dante_pinball/dante_pinball/legacy/gym_env_dante_total_force.py b/archive/dante_pinball/dante_pinball/legacy/gym_env_dante_total_force.py new file mode 100644 index 0000000..4bc4b88 --- /dev/null +++ b/archive/dante_pinball/dante_pinball/legacy/gym_env_dante_total_force.py @@ -0,0 +1,271 @@ +import gymnasium as gym +import numpy as np +from gymnasium import spaces +from collections import deque +from typing import Tuple +import sys +import os +import queue + +os.environ["OMP_NUM_THREADS"] = "1" +os.environ["MKL_NUM_THREADS"] = "1" + +current_dir = os.path.dirname(os.path.abspath(__file__)) +parent_dir = os.path.abspath(os.path.join(current_dir, os.pardir)) +sys.path.append(parent_dir) +from CelerisLab import FlowField +from CelerisLab import utils + +config_cuda = utils.load_cuda_config( + os.path.join(parent_dir, "configs", "config_cuda.json") +) +config_field = utils.load_flow_field_config( + os.path.join(parent_dir, "configs", "config_flowfield.json") +) + +S_DIM, A_DIM = 2, 3 +U0 = config_field.velocity +SAMPLE_INTERVAL = 800 +FIFO_LEN = 150 +CONV_LEN = 36 +if config_field.data_type == "FP32": + DATA_TYPE = np.float32 +else: + raise ValueError(f"Unsupported data type {config_field.data_type}.") + + +class CustomEnv(gym.Env): + """DANTE-only environment with deterministic reset semantics and numeric-failure surfacing.""" + + metadata = {"render_modes": ["human"], "render_fps": 1000 / SAMPLE_INTERVAL} + + FAILURE_NONE = 0 + FAILURE_NUMERIC = 1 + FAILURE_NONFINITE_OBS = 2 + FAILURE_OBS_OOB = 3 + + def __init__( + self, + device_id: int = 0, + obs_fail_bound: float = 2.0, + obs_clip_bound: float = 3.0, + reward_weights: Tuple[float, float, float] = (0.3, 0.3, 0.4), + ): + super().__init__() + self.action_space = spaces.Box(low=-1, high=1, shape=(A_DIM,), dtype=DATA_TYPE) + self.observation_space = spaces.Box( + low=-obs_clip_bound, + high=obs_clip_bound, + shape=(S_DIM,), + dtype=DATA_TYPE, + ) + + self.obs_fail_bound = float(obs_fail_bound) + self.obs_clip_bound = float(obs_clip_bound) + rw = np.asarray(reward_weights, dtype=DATA_TYPE) + if rw.size != 3: + raise ValueError("reward_weights must have length 3: (cd, cl, sim)") + rw_sum = float(np.sum(rw)) + if rw_sum <= 0: + raise ValueError("reward_weights sum must be positive") + self.reward_weights = rw / rw_sum + + self.fifo_states = deque(maxlen=FIFO_LEN) + self.target_states = np.empty((0, 6), dtype=DATA_TYPE) + self.force_norm_fact = 1.0 + self.torque_norm_fact = 1.0 + self.sens_norm_fact = np.ones(6, dtype=DATA_TYPE) + self.sens_deviation = np.zeros(6, dtype=DATA_TYPE) + + self.reward_cd = 0.0 + self.reward_cl = 0.0 + self.reward_sim = 0.0 + self.current_step = 0 + + self.flow_field = FlowField(config_field, config_cuda, device_id) + L0 = 20 + u0 = config_field.velocity + nx = self.flow_field.FIELD_SHAPE[0] + ny = self.flow_field.FIELD_SHAPE[1] + + center: Tuple[float, float, float] = (10 * L0, (ny - 1) / 2, 0) + self.flow_field.add_cylinder(center, L0) + center = (40 * L0, (ny - 1) / 2 + 2 * L0, 0) + self.flow_field.add_sensor(center, L0 / 4) + center = (40 * L0, (ny - 1) / 2, 0) + self.flow_field.add_sensor(center, L0 / 4) + center = (40 * L0, (ny - 1) / 2 - 2 * L0, 0) + self.flow_field.add_sensor(center, L0 / 4) + self.flow_field.run(int(4 * nx / u0), np.zeros(4, dtype=DATA_TYPE)) + + for _ in range(FIFO_LEN): + self.flow_field.run(SAMPLE_INTERVAL, np.zeros(4, dtype=DATA_TYPE)) + new_state = self.flow_field.obs.copy()[2:8] + self.target_states = np.vstack((self.target_states, new_state)) + + center = (30 * L0, (ny - 1) / 2, 0) + self.flow_field.add_cylinder(center, L0 / 2) + center = (31.3 * L0, (ny - 1) / 2 + 0.75 * L0, 0) + self.flow_field.add_cylinder(center, L0 / 2) + center = (31.3 * L0, (ny - 1) / 2 - 0.75 * L0, 0) + self.flow_field.add_cylinder(center, L0 / 2) + self.flow_field.run(int(4 * nx / u0), np.zeros(7, dtype=DATA_TYPE)) + self.flow_field.get_ddf() + self.flow_field.save_ddf() + + for _ in range(FIFO_LEN): + self.flow_field.run(SAMPLE_INTERVAL, np.zeros(7, dtype=DATA_TYPE)) + self.fifo_states.append(self.flow_field.obs.copy()[2:14]) + + temp_states = np.array(self.fifo_states) + self.force_norm_fact = 6 * np.max(np.abs(temp_states[:, 6:12])) + temp_torque = ( + -temp_states[:, 1] + - temp_states[:, 2] * np.sqrt(3) / 2 + + temp_states[:, 3] / 2 + + temp_states[:, 4] * np.sqrt(3) / 2 + + temp_states[:, 5] / 2 + ) + self.torque_norm_fact = 10 * np.max(np.abs(temp_torque)) + + for i in range(6): + self.sens_deviation[i] = np.mean(temp_states[:, i]) + self.sens_norm_fact[i] = 5 * np.max(np.abs(temp_states[:, i] - self.sens_deviation[i])) + + self.flow_field.apply_ddf() + + for _ in range(FIFO_LEN): + self.flow_field.run( + SAMPLE_INTERVAL, + np.array([0.0, 0.0, 0.0, 0.0, 0.0, -4 * u0, 4 * u0], dtype=DATA_TYPE), + ) + self.fifo_states.append(self.flow_field.obs.copy()[2:14]) + + self.save_states = self.fifo_states.copy() + # self.flow_field.apply_ddf() + self.flow_field.get_ddf() + self.flow_field.save_ddf() + + def _calc_lag(self, target: np.ndarray, state: np.ndarray) -> int: + target_mean = np.mean(target) + state_mean = np.mean(state) + correlation = np.correlate(target - target_mean, state - state_mean, "full") + lags = np.arange(-len(target) + 1, len(target)) + return int(lags[np.argmax(correlation)]) + + def _calc_dtw_sim(self, target: np.ndarray, state: np.ndarray) -> float: + n = len(target) + m = len(state) + dtw_matrix = np.full((n + 1, m + 1), np.inf) + dtw_matrix[0, 0] = 0 + for i in range(1, n + 1): + for j in range(1, m + 1): + cost = abs(target[i - 1] - state[j - 1]) + last_min = min( + dtw_matrix[i - 1, j], + dtw_matrix[i, j - 1], + dtw_matrix[i - 1, j - 1], + ) + dtw_matrix[i, j] = cost + last_min + return float(1 - (dtw_matrix[n, m] / len(target))) + + def _compute_obs_reward(self): + states = np.array(self.fifo_states) + forces = states[-1, 6:12] / self.force_norm_fact + + obs_drag = float((forces[0] + forces[2] + forces[4]) / 3) + obs_lift = float((forces[1] + forces[3] + forces[5]) / 3) + + similarities = 0.0 + id_sens = 1 + target_seq = self.target_states[CONV_LEN : 2 * CONV_LEN, id_sens] + state_seq = states[-CONV_LEN:, id_sens] + lag = self._calc_lag(target_seq, state_seq) + + for i in range(0, 6): + target_seq = np.roll(self.target_states[:, i], -lag)[CONV_LEN : 2 * CONV_LEN] + state_seq = states[-CONV_LEN:, i] + similarities += self._calc_dtw_sim(target_seq, state_seq) / 6 + + self.reward_cd = float(np.exp(-np.abs(obs_drag * 20))) + self.reward_cl = float(np.exp(-np.abs(obs_lift * 80))) + self.reward_sim = float(np.exp(-10 * np.abs(similarities - 1))) + reward = float( + np.minimum( + self.reward_weights[0] * self.reward_cd + + self.reward_weights[1] * self.reward_cl + + self.reward_weights[2] * self.reward_sim, + 1.0, + ) + ) + observation = np.array([forces[0], forces[1]], dtype=DATA_TYPE) + return observation, reward + + def step(self, action): + assert self.action_space.contains(action), "%r (%s) invalid" % ( + action, + type(action), + ) + + result_queue = queue.Queue() + + def run_flow_field(act): + self.flow_field.context.push() + u0 = config_field.velocity + try: + temp = np.zeros(7, dtype=DATA_TYPE) + temp[4:7] = np.array((act * 8 + [0, -4, 4]) * u0, dtype=DATA_TYPE) + self.flow_field.run(SAMPLE_INTERVAL, temp) + finally: + self.flow_field.context.pop() + self.fifo_states.append(self.flow_field.obs.copy()[2:14]) + + def proc_data(): + result_queue.put(self._compute_obs_reward()) + + run_flow_field(action) + + if self.flow_field.has_numeric_error(): + self.current_step += 1 + obs = np.zeros(S_DIM, dtype=DATA_TYPE) + info = { + "failure_code": self.FAILURE_NUMERIC, + "numeric_error": True, + "raw_obs": obs.copy(), + } + return obs, 0.0, False, True, info + + proc_data() + observation, reward = result_queue.get() + raw_obs = np.asarray(observation, dtype=DATA_TYPE).copy() + + failure_code = self.FAILURE_NONE + if not np.all(np.isfinite(observation)): + failure_code = self.FAILURE_NONFINITE_OBS + elif np.any(np.abs(observation) > self.obs_fail_bound): + failure_code = self.FAILURE_OBS_OOB + + truncated = failure_code != self.FAILURE_NONE + if truncated: + reward = 0.0 + observation = np.zeros(S_DIM, dtype=DATA_TYPE) + else: + observation = np.clip(observation, -self.obs_clip_bound, self.obs_clip_bound) + + self.current_step += 1 + info = { + "failure_code": int(failure_code), + "numeric_error": False, + "raw_obs": raw_obs, + } + return observation.astype(np.float32), float(reward), False, bool(truncated), info + + def reset(self, seed=None): + self.flow_field.restore_ddf() + self.flow_field.apply_ddf() + self.fifo_states = self.save_states.copy() + self.current_step = 0 + return np.zeros(S_DIM, dtype=np.float32), {} + + def close(self): + self.flow_field.__del__() diff --git a/archive/dante_pinball/dante_pinball/legacy/v6_init_surrogate_mlp_cnn_compare.py b/archive/dante_pinball/dante_pinball/legacy/v6_init_surrogate_mlp_cnn_compare.py new file mode 100644 index 0000000..2a48831 --- /dev/null +++ b/archive/dante_pinball/dante_pinball/legacy/v6_init_surrogate_mlp_cnn_compare.py @@ -0,0 +1,216 @@ +import json +import os +import time +from dataclasses import dataclass +from typing import Dict, List, Tuple + +import numpy as np +from sklearn.metrics import mean_absolute_error, r2_score +from sklearn.model_selection import train_test_split +from sklearn.preprocessing import StandardScaler + +os.environ.setdefault("TF_CPP_MIN_LOG_LEVEL", "2") +os.environ.setdefault("TF_FORCE_GPU_ALLOW_GROWTH", "true") + +import tensorflow as tf +from tensorflow.keras.callbacks import EarlyStopping +from tensorflow.keras.layers import Conv1D, Dense, Dropout, Flatten, Input, MaxPooling1D +from tensorflow.keras.models import Sequential +from tensorflow.keras.optimizers import Adam + + +CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) +ROOT = os.path.abspath(os.path.join(CURRENT_DIR, os.pardir)) +OUT_DIR = os.path.join(ROOT, "output", "report_dante_v2_v5_v6") +os.makedirs(OUT_DIR, exist_ok=True) + +DB_PATH = os.path.join(ROOT, "output", "d1a3o12_250421_forces02_dante_v6_database_live.npz") +DECISION_PATH = os.path.join(ROOT, "output", "d1a3o12_250421_forces02_dante_v6_1_structure_decision.json") + + +@dataclass +class ModelSpec: + name: str + kind: str # mlp | cnn + + +def set_tf_device(device_id: int = 1) -> str: + gpus = tf.config.list_physical_devices("GPU") + if not gpus: + return "CPU" + idx = int(max(0, min(len(gpus) - 1, device_id))) + try: + tf.config.set_visible_devices([gpus[idx]], "GPU") + tf.config.experimental.set_memory_growth(gpus[idx], True) + return f"GPU:{idx}" + except RuntimeError: + return f"GPU:{idx}(runtime_initialized)" + + +def load_init_dataset() -> Tuple[np.ndarray, np.ndarray, Dict]: + if not os.path.exists(DB_PATH): + raise FileNotFoundError(DB_PATH) + if not os.path.exists(DECISION_PATH): + raise FileNotFoundError(DECISION_PATH) + + with open(DECISION_PATH, "r", encoding="utf-8") as f: + decision = json.load(f) + + n_init = int(decision["num_initial"]) + db = np.load(DB_PATH, allow_pickle=True) + x = np.asarray(db["input_x"], dtype=np.float64) + y = np.asarray(db["input_y"], dtype=np.float64).reshape(-1) + + if len(x) < n_init: + raise RuntimeError(f"DB samples {len(x)} < num_initial {n_init}") + + x0 = x[:n_init] + y0 = y[:n_init] + return x0, y0, decision + + +def make_mlp(input_dims: int) -> Sequential: + model = Sequential( + [ + Input(shape=(input_dims,)), + Dense(128, activation="elu"), + Dropout(0.10), + Dense(64, activation="elu"), + Dropout(0.10), + Dense(32, activation="elu"), + Dense(1, activation="linear"), + ] + ) + model.compile(optimizer=Adam(learning_rate=1e-3), loss="mse", metrics=["mae"]) + return model + + +def make_cnn(input_dims: int) -> Sequential: + model = Sequential( + [ + Input(shape=(input_dims, 1)), + Conv1D(128, kernel_size=3, padding="same", activation="elu"), + MaxPooling1D(pool_size=2, strides=1), + Dropout(0.2), + Conv1D(64, kernel_size=3, padding="same", activation="elu"), + MaxPooling1D(pool_size=2, strides=1), + Dropout(0.2), + Conv1D(32, kernel_size=3, padding="same", activation="elu"), + Conv1D(16, kernel_size=3, padding="same", activation="elu"), + Flatten(), + Dense(64, activation="elu"), + Dense(1, activation="linear"), + ] + ) + model.compile(optimizer=Adam(learning_rate=1e-3), loss="mse", metrics=["mae"]) + return model + + +def run_one_split(x: np.ndarray, y: np.ndarray, seed: int, spec: ModelSpec) -> Dict: + x_tr, x_te, y_tr, y_te = train_test_split(x, y, test_size=0.30, random_state=seed, shuffle=True) + + x_scaler = StandardScaler() + y_scaler = StandardScaler() + x_tr_s = x_scaler.fit_transform(x_tr) + x_te_s = x_scaler.transform(x_te) + y_tr_s = y_scaler.fit_transform(y_tr.reshape(-1, 1)).reshape(-1) + + if spec.kind == "mlp": + model = make_mlp(x.shape[1]) + xtr_in = x_tr_s + xte_in = x_te_s + elif spec.kind == "cnn": + model = make_cnn(x.shape[1]) + xtr_in = x_tr_s.reshape(len(x_tr_s), x.shape[1], 1) + xte_in = x_te_s.reshape(len(x_te_s), x.shape[1], 1) + else: + raise ValueError(spec.kind) + + cb = [EarlyStopping(monitor="val_loss", patience=25, restore_best_weights=True)] + hist = model.fit( + xtr_in, + y_tr_s, + validation_split=0.25, + batch_size=32, + epochs=250, + verbose=0, + callbacks=cb, + ) + + y_hat_s = model.predict(xte_in, verbose=0).reshape(-1, 1) + y_hat = y_scaler.inverse_transform(y_hat_s).reshape(-1) + + return { + "seed": int(seed), + "r2": float(r2_score(y_te, y_hat)), + "mae": float(mean_absolute_error(y_te, y_hat)), + "epochs": int(len(hist.history.get("loss", []))), + "best_val_loss": float(np.min(hist.history.get("val_loss", [np.nan]))), + } + + +def summarize(rows: List[Dict]) -> Dict: + r2 = np.array([r["r2"] for r in rows], dtype=np.float64) + mae = np.array([r["mae"] for r in rows], dtype=np.float64) + return { + "r2_mean": float(np.mean(r2)), + "r2_std": float(np.std(r2)), + "mae_mean": float(np.mean(mae)), + "mae_std": float(np.std(mae)), + } + + +def main() -> None: + t0 = time.time() + device = set_tf_device(1) + x, y, decision = load_init_dataset() + + specs = [ + ModelSpec(name="mlp_128_64_32", kind="mlp"), + ModelSpec(name="cnn_paper_like", kind="cnn"), + ] + seeds = [0, 1, 2, 3, 4] + + results = {} + for spec in specs: + rows = [run_one_split(x, y, s, spec) for s in seeds] + results[spec.name] = { + "kind": spec.kind, + "per_split": rows, + "summary": summarize(rows), + } + + r2_mlp = results["mlp_128_64_32"]["summary"]["r2_mean"] + r2_cnn = results["cnn_paper_like"]["summary"]["r2_mean"] + + out = { + "db_path": DB_PATH, + "decision_path": DECISION_PATH, + "device": device, + "n_init": int(len(x)), + "dims": int(x.shape[1]), + "y_stats": { + "mean": float(np.mean(y)), + "std": float(np.std(y)), + "min": float(np.min(y)), + "max": float(np.max(y)), + }, + "models": results, + "delta": { + "r2_cnn_minus_mlp": float(r2_cnn - r2_mlp), + "better_model": "cnn_paper_like" if r2_cnn > r2_mlp else "mlp_128_64_32", + }, + "elapsed_sec": float(time.time() - t0), + } + + out_json = os.path.join(OUT_DIR, "v6_1_init_surrogate_mlp_vs_cnn.json") + with open(out_json, "w", encoding="utf-8") as f: + json.dump(out, f, ensure_ascii=False, indent=2) + + print("saved", out_json) + print("better_model", out["delta"]["better_model"]) + print("delta_r2", out["delta"]["r2_cnn_minus_mlp"]) + + +if __name__ == "__main__": + main() diff --git a/archive/dante_pinball/dante_pinball/legacy/v6_v7_pca_distance_with_ppo.py b/archive/dante_pinball/dante_pinball/legacy/v6_v7_pca_distance_with_ppo.py new file mode 100644 index 0000000..fd472d3 --- /dev/null +++ b/archive/dante_pinball/dante_pinball/legacy/v6_v7_pca_distance_with_ppo.py @@ -0,0 +1,318 @@ +import json +import os +from dataclasses import dataclass +from typing import Dict, List, Tuple + +import matplotlib.pyplot as plt +import numpy as np +from sklearn.decomposition import PCA +from sklearn.preprocessing import StandardScaler + +ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) +OUT_DIR = os.path.join(ROOT, "output", "report_dante_v2_v5_v6") +os.makedirs(OUT_DIR, exist_ok=True) + +SEEDS = [11, 29, 47] + +V6_NAME = "d1a3o12_250421_forces02_dante_v6_2" +V7_NAME = "d1a3o12_250421_forces02_dante_v7" + + +@dataclass +class RunData: + name: str + x: np.ndarray + y: np.ndarray + num_initial: int + dims: int + cfg: Dict + + +def load_run(name: str) -> RunData: + db_path = os.path.join(ROOT, "output", f"{name}_database_live.npz") + cfg_path = os.path.join(ROOT, "output", f"{name}_structure_decision.json") + + z = np.load(db_path, allow_pickle=True) + x = np.asarray(z["input_x"], dtype=np.float64) + y = np.asarray(z["input_y"], dtype=np.float64) + cfg = json.load(open(cfg_path, "r", encoding="utf-8")) + + return RunData( + name=name, + x=x, + y=y, + num_initial=int(cfg["num_initial"]), + dims=int(cfg["controller_dims"]), + cfg=cfg, + ) + + +def feature_dict(obs_t: np.ndarray, obs_prev: np.ndarray, act_prev: np.ndarray) -> Dict[str, float]: + o0, o1 = float(obs_t[0]), float(obs_t[1]) + p0, p1 = float(obs_prev[0]), float(obs_prev[1]) + a0, a1, a2 = float(act_prev[0]), float(act_prev[1]), float(act_prev[2]) + return { + "obs0": o0, + "obs1": o1, + "dobs0": o0 - p0, + "dobs1": o1 - p1, + "sin_obs0": float(np.sin(np.pi * o0)), + "sin_obs1": float(np.sin(np.pi * o1)), + "cos_obs0": float(np.cos(np.pi * o0)), + "cos_obs1": float(np.cos(np.pi * o1)), + "tanh_obs0": float(np.tanh(o0)), + "tanh_obs1": float(np.tanh(o1)), + "act0_l1": a0, + "act1_l1": a1, + "act2_l1": a2, + } + + +def inv_tanh_map(q: float) -> float: + qq = float(np.clip(q, -0.999, 0.999)) + x = np.arctanh(qq) / 1.25 + return float(np.clip(x, -1.0, 1.0)) + + +def ppo_point_for_v6(seed: int, basis_terms: List[str]) -> np.ndarray: + p = os.path.join(ROOT, "output", "report_dante_v2_v5_v6", f"raw_oldenv_seed_{seed}.npz") + z = np.load(p) + obs = np.asarray(z["ppo_obs"], dtype=np.float64) + act = np.asarray(z["ppo_actions"], dtype=np.float64) + + rows_x = [] + rows_y = [] + for t in range(1, len(obs)): + fd = feature_dict(obs[t], obs[t - 1], act[t - 1]) + row = [1.0] + [float(fd[k]) for k in basis_terms] + rows_x.append(row) + rows_y.append(act[t]) + + X = np.asarray(rows_x, dtype=np.float64) + Y = np.asarray(rows_y, dtype=np.float64) + + params = [] + for ch in range(3): + coef, *_ = np.linalg.lstsq(X, Y[:, ch], rcond=None) + bias = float(coef[0]) + params.append(inv_tanh_map(bias / 1.0)) + for c in coef[1:]: + params.append(inv_tanh_map(float(c) / 2.0)) + + return np.asarray(params, dtype=np.float64) + + +def phase_weights(phase: float, k: int) -> np.ndarray: + z = float(np.mod(phase, 1.0)) * k + i0 = int(np.floor(z)) % k + frac = float(z - np.floor(z)) + i1 = (i0 + 1) % k + w = np.zeros(k, dtype=np.float64) + w[i0] += (1.0 - frac) + w[i1] += frac + return w + + +def ppo_point_for_v7(seed: int, k: int, period_steps: float) -> np.ndarray: + p = os.path.join(ROOT, "output", "report_dante_v2_v5_v6", f"raw_oldenv_seed_{seed}.npz") + z = np.load(p) + act = np.asarray(z["ppo_actions"], dtype=np.float64) + + n = int(act.shape[0]) + phase = 0.0 + step_phase = 1.0 / max(1e-6, float(period_steps)) + + W = np.zeros((n, k), dtype=np.float64) + for t in range(n): + W[t] = phase_weights(phase, k) + phase = (phase + step_phase) % 1.0 + + params = [] + for ch in range(3): + coef, *_ = np.linalg.lstsq(W, act[:, ch], rcond=None) + coef = np.clip(coef, -1.0, 1.0) + params.extend(coef.tolist()) + + return np.asarray(params, dtype=np.float64) + + +def fit_pca_and_project(x: np.ndarray, extra: np.ndarray) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: + scaler = StandardScaler() + xs = scaler.fit_transform(x) + extra_s = scaler.transform(extra) + + pca = PCA(n_components=2, random_state=0) + x2 = pca.fit_transform(xs) + extra2 = pca.transform(extra_s) + return x2, extra2, pca.explained_variance_ratio_ + + +def distance_metrics(x: np.ndarray, y: np.ndarray, ppo_points: np.ndarray, n0: int) -> Dict: + centroid = np.mean(ppo_points, axis=0) + d = np.linalg.norm(x - centroid.reshape(1, -1), axis=1) + + n = len(d) + xx = np.arange(n, dtype=np.float64) + slope = float(np.polyfit(xx, d, deg=1)[0]) if n >= 2 else 0.0 + + init_mean = float(np.mean(d[:n0])) if n0 > 0 else float(np.mean(d)) + late_mean = float(np.mean(d[n0:])) if n > n0 else init_mean + + thr = np.quantile(y, 0.9) + idx_hi = np.where(y >= thr)[0] + hi_mean = float(np.mean(d[idx_hi])) if len(idx_hi) > 0 else float("nan") + + return { + "init_mean": init_mean, + "late_mean": late_mean, + "late_minus_init": float(late_mean - init_mean), + "slope": slope, + "high_reward_dist_mean": hi_mean, + "overall_dist_mean": float(np.mean(d)), + "distance": d.tolist(), + "ppo_centroid": centroid.tolist(), + } + + +def plot_pca( + run_name: str, + x2: np.ndarray, + y: np.ndarray, + ppo2: np.ndarray, + evr: np.ndarray, + out_png: str, +) -> None: + plt.figure(figsize=(8, 6)) + sc = plt.scatter(x2[:, 0], x2[:, 1], c=y, cmap="viridis", s=12, alpha=0.75) + plt.colorbar(sc, label="reward_scaled") + + colors = ["#e41a1c", "#377eb8", "#ff7f00"] + for i, seed in enumerate(SEEDS): + plt.scatter( + [ppo2[i, 0]], + [ppo2[i, 1]], + s=120, + c=colors[i], + marker="*", + edgecolors="k", + linewidths=0.9, + label=f"PPO seed {seed}", + zorder=6, + ) + + plt.title( + f"{run_name} PCA with PPO points\\n" + f"PC1 {evr[0]*100:.1f}% | PC2 {evr[1]*100:.1f}%" + ) + plt.xlabel("PC1") + plt.ylabel("PC2") + plt.legend(loc="best", fontsize=9) + plt.grid(alpha=0.25) + plt.tight_layout() + plt.savefig(out_png, dpi=170) + plt.close() + + +def plot_distance_curve(run_name: str, d: np.ndarray, n0: int, out_png: str) -> None: + n = len(d) + x = np.arange(1, n + 1) + plt.figure(figsize=(9, 4.8)) + plt.plot(x, d, lw=1.4, color="#1f77b4") + plt.axvline(n0, color="k", ls="--", lw=1.0, label=f"init end @ {n0}") + plt.title(f"{run_name}: distance to PPO centroid") + plt.xlabel("sample order") + plt.ylabel("L2 distance") + plt.grid(alpha=0.25) + plt.legend(loc="best") + plt.tight_layout() + plt.savefig(out_png, dpi=170) + plt.close() + + +def diagnose(run_name: str, m: Dict) -> Dict: + outward = bool(m["late_minus_init"] > 0.0 and m["slope"] > 0.0) + high_reward_near = bool(m["high_reward_dist_mean"] < m["overall_dist_mean"]) + + return { + "run": run_name, + "outward_sampling_still_exists": outward, + "high_reward_closer_to_ppo_than_overall": high_reward_near, + "metrics": { + "late_minus_init": float(m["late_minus_init"]), + "slope": float(m["slope"]), + "high_reward_dist_mean": float(m["high_reward_dist_mean"]), + "overall_dist_mean": float(m["overall_dist_mean"]), + }, + } + + +def main() -> None: + v6 = load_run(V6_NAME) + v7 = load_run(V7_NAME) + + basis_terms = list(v6.cfg["basis_terms"]) + v6_ppo = np.vstack([ppo_point_for_v6(s, basis_terms) for s in SEEDS]) + + k = int(v7.cfg["control_points_per_channel"]) + period_steps = float(v7.cfg["period_steps"]) + v7_ppo = np.vstack([ppo_point_for_v7(s, k=k, period_steps=period_steps) for s in SEEDS]) + + v6_x2, v6_ppo2, v6_evr = fit_pca_and_project(v6.x, v6_ppo) + v7_x2, v7_ppo2, v7_evr = fit_pca_and_project(v7.x, v7_ppo) + + v6_m = distance_metrics(v6.x, v6.y, v6_ppo, v6.num_initial) + v7_m = distance_metrics(v7.x, v7.y, v7_ppo, v7.num_initial) + + p_v6_pca = os.path.join(OUT_DIR, "v6_2_pca_with_ppo_points.png") + p_v7_pca = os.path.join(OUT_DIR, "v7_pca_with_ppo_points.png") + p_v6_dist = os.path.join(OUT_DIR, "v6_2_distance_order_vs_ppo_centroid.png") + p_v7_dist = os.path.join(OUT_DIR, "v7_distance_order_vs_ppo_centroid.png") + + plot_pca(v6.name, v6_x2, v6.y, v6_ppo2, v6_evr, p_v6_pca) + plot_pca(v7.name, v7_x2, v7.y, v7_ppo2, v7_evr, p_v7_pca) + plot_distance_curve(v6.name, np.asarray(v6_m["distance"]), v6.num_initial, p_v6_dist) + plot_distance_curve(v7.name, np.asarray(v7_m["distance"]), v7.num_initial, p_v7_dist) + + summary = { + "v6": { + "name": v6.name, + "dims": int(v6.dims), + "num_samples": int(len(v6.y)), + "num_initial": int(v6.num_initial), + "best_reward": float(np.max(v6.y) / 100.0), + "distance_metrics": {k: v for k, v in v6_m.items() if k != "distance"}, + "diagnosis": diagnose(v6.name, v6_m), + }, + "v7": { + "name": v7.name, + "dims": int(v7.dims), + "num_samples": int(len(v7.y)), + "num_initial": int(v7.num_initial), + "best_reward": float(np.max(v7.y) / 100.0), + "period_steps": period_steps, + "distance_metrics": {k: v for k, v in v7_m.items() if k != "distance"}, + "diagnosis": diagnose(v7.name, v7_m), + }, + "figures": { + "v6_pca": p_v6_pca, + "v6_distance": p_v6_dist, + "v7_pca": p_v7_pca, + "v7_distance": p_v7_dist, + }, + } + + out_json = os.path.join(OUT_DIR, "v6_v7_pca_distance_with_ppo_summary.json") + with open(out_json, "w", encoding="utf-8") as f: + json.dump(summary, f, indent=2, ensure_ascii=False) + + print("saved", out_json) + print(json.dumps({ + "v6_late_minus_init": summary["v6"]["distance_metrics"]["late_minus_init"], + "v6_slope": summary["v6"]["distance_metrics"]["slope"], + "v7_late_minus_init": summary["v7"]["distance_metrics"]["late_minus_init"], + "v7_slope": summary["v7"]["distance_metrics"]["slope"], + }, indent=2, ensure_ascii=False)) + + +if __name__ == "__main__": + main() diff --git a/archive/dante_pinball/dante_pinball/legacy/v6_v7_ppo_briefing_20260324.md b/archive/dante_pinball/dante_pinball/legacy/v6_v7_ppo_briefing_20260324.md new file mode 100644 index 0000000..3a02433 --- /dev/null +++ b/archive/dante_pinball/dante_pinball/legacy/v6_v7_ppo_briefing_20260324.md @@ -0,0 +1,39 @@ +# v6/v7/PPO 综合简报 + +## 1) PCA(含3个PPO拟合点) +- v6图: /home/frank14f/Frank_LBM/output/report_dante_v2_v5_v6/brief_v6_3_pca_with_ppo3.png +- v7图: /home/frank14f/Frank_LBM/output/report_dante_v2_v5_v6/brief_v7_1_pca_with_ppo3.png + +## 2) obs-act时序 + 最终涡量 +- 时序图: /home/frank14f/Frank_LBM/output/report_dante_v2_v5_v6/brief_v6_v7_ppo_obs_act_time.png +- 最终涡量图: /home/frank14f/Frank_LBM/output/report_dante_v2_v5_v6/brief_v6_v7_ppo_final_vorticity.png +- 涡量统一色条范围: ±0.003432(按三者全局|omega|max的10%) +- PPO时序选用seed=11(tail100=0.53995) + +## 3) 函数约简、关键函数、最终组合 +- 全特征模型平均R2: 0.955101 +- 约束搜索best_chrono基函数: ['obs1', 'sin_obs0', 'cos_obs0', 'act1_l1'] +- best_chrono平均R2: 0.957993 +- best_chrono最差seed R2: 0.948454 +- 关键函数解释: obs1给出主状态幅值,sin/cos(pi*obs0)提供周期相位,act1_l1提供单步记忆。 + +### 学到方程与拟合方程(LaTeX) +- 全特征学到方程(3动作联合线性写法): +$$\mathbf{a}_t = W_{full}\,\phi_{full,t}$$ +$$\phi_{full}=[1,obs_0,obs_1,\Delta obs_0,\Delta obs_1,\sin(\pi obs_0),\sin(\pi obs_1),\cos(\pi obs_0),\cos(\pi obs_1),\tanh(obs_0),\tanh(obs_1),a_{0,t-1},a_{1,t-1},a_{2,t-1}]^\top$$ +$$W_{full}=\begin{bmatrix}0.1571 & 1613.4234 & 127.6118 & -0.0178 & -0.0231 & 132.0173 & 10.3495 & -0.1619 & -0.0087 & -2028.1743 & -159.3687 & -0.0094 & 0.0129 & 0.0130 \\ 0.0229 & 6369.5027 & -875.1549 & 0.0028 & 0.0395 & 516.7321 & -71.4089 & -0.0922 & -0.0001 & -7993.1110 & 1099.0137 & 0.0506 & -0.0346 & 0.0110 \\ 0.0565 & -1065.8604 & 421.0853 & 0.0500 & -0.0717 & -86.0829 & 34.4277 & -0.0099 & 0.0210 & 1336.5921 & -529.4751 & -0.0876 & 0.0107 & 0.0159\end{bmatrix}$$ +- 全特征拟合R2(本次重算, action均值): 0.962039 + +- 约简拟合方程(best_chrono): +$$\mathbf{a}_t = W_{red}\,\phi_{red,t}$$ +$$\phi_{red}=[1,obs_1,\sin(\pi obs_0),\cos(\pi obs_0),a_{1,t-1}]^\top$$ +$$W_{red}=\begin{bmatrix}-0.0131 & 0.7323 & 0.0014 & 0.0008 & -0.0115 \\ -0.0128 & -0.3989 & -0.0799 & -0.0552 & -0.0168 \\ 0.0312 & -0.3286 & 0.0996 & 0.0348 & 0.0018\end{bmatrix}$$ +- 约简拟合R2(本次重算, action均值): 0.960987 + +## 4) 高维能力与现实约束反思 +- 高维扫描样本数: 6 +- holdout_r2<0 的case数: 6/6 +- 首轮acq_gain<0 的case数: 6/6 +- 解释: 高维下代理可辨识度不足时,Tree探索会被误导,出现边界漂移与收益停滞。 +- 与论文对齐: DANTE强调DNN surrogate与自适应探索协同;在你的流体控制里,受噪声、时序漂移和高维参数化耦合影响,样本效率会显著下降。 +- 双代理是否有帮助: 当前日志显示ensemble路径提供了可运行冗余(CNN失败时MLP兜底),但在v6_3其val_r2均值仍偏低,收益主要体现在稳定性而非显著提升最优值。 diff --git a/archive/dante_pinball/dante_pinball/legacy/v6_v7_reaudit_from_scratch_20260323.md b/archive/dante_pinball/dante_pinball/legacy/v6_v7_reaudit_from_scratch_20260323.md new file mode 100644 index 0000000..e41eee9 --- /dev/null +++ b/archive/dante_pinball/dante_pinball/legacy/v6_v7_reaudit_from_scratch_20260323.md @@ -0,0 +1,434 @@ +# v6/v7 Zero-Base Re-Audit (2026-03-23) + +## 1) User Goal (frozen) + +- Core objective: use DANTE to solve a CFD control problem under expensive evaluations. +- Key transformation: convert control policy search into low-sample parameter optimization. +- Practical constraints: + - one run is very expensive (about one day), so no destructive trial-and-error on running jobs; + - surrogate must be trainable from small initial database; + - acquisition should discover high-reward basin instead of drifting to easy-to-fit boundary regions. + +## 2) Paper-to-Task Mapping (DANTE original intent) + +From `DANTE/paper/s43588-025-00858-x.md`: + +- DANTE is designed for non-cumulative objective optimization with limited data. +- Key mechanisms are: + - DUCB exploration term based on visit counts and surrogate value; + - conditional selection (avoid value deterioration); + - local backpropagation of visits; + - adaptive exploration scaling; + - top-visit + high-score mixed sampling. +- Paper also emphasizes DNN surrogate expressivity as a key success factor. +- For control tasks (paper lunar landing case), conversion is done by fixing initial condition and optimizing pre-designed action parameterization. + +Interpretation for this project: + +- the controller parameterization quality is first-order (decides landscape smoothness and identifiability); +- surrogate quality under small data is second-order but still critical; +- if parameterization induces heavy truncation/failure regions, DANTE will tend to exploit boundary patterns and stall locally. + +## 3) Original DANTE Code Baseline (reference behavior) + +From `DANTE/dante/tree_exploration.py`: + +- Tree expansion mutates one or multiple dimensions with discrete step `turn`. +- Choose step uses UCB-like criterion with `value + exploration_weight * sqrt(logN/(n+1))`. +- Conditional selection exists: continue with root unless child UCB exceeds root. +- Local backpropagation is implemented as local visit count update (`self.N[path] += 1`). +- Candidate set mixes: + - most visited nodes, + - top predicted nodes, + - random nodes. + +From `DANTE/dante/neural_surrogate.py`: + +- Surrogate design is deep Conv1D-heavy, matching paper claim. + +Important baseline implication: + +- DANTE search quality assumes surrogate can provide stable relative ranking. +- If surrogate fitting is unstable/biased, UCB dynamics may push toward artificial easy zones (often boundaries). + +## 4) v6/v7 Parameterization Audit + +### v6 (closed-loop basis controller) + +From `scripts/d1a3o12_250421_dante_v6.py`: + +- parameterization: per-action bias + basis coefficients (compact basis profile), total dims = 18 in current run; +- controller includes derivative and one-step action history terms (`dobs*`, `act*_l1`), introducing piecewise/non-smooth response wrt parameters; +- candidate evaluation uses hard reset and truncation-aware fallback; +- invalid sample (`failure_code != 0`) is not added to training set. + +Potential risk: + +- derivative/history terms can create sensitive local discontinuities under rollout + truncation, making small-data surrogate fitting harder. + +### v7 (open-loop periodic) + +From `scripts/d1a3o12_250421_dante_v7_openloop.py`: + +- parameterization: direct periodic control points, dims = 24 (3 channels * 8 control points), optional period parameter; +- non-integer period supported via continuous phase accumulation; +- period initialized from PPO data FFT median (`period_steps ~= 15.789` currently). + +Expected advantage: + +- objective wrt parameters is smoother than closed-loop derivative/history mapping; +- better surrogate learnability under limited data. + +## 5) Live Evidence From Current Runs + +Data extracted from: + +- `output/d1a3o12_250421_forces02_dante_v6_3_database_live.npz` +- `output/d1a3o12_250421_forces02_dante_v6_3_dante_log.csv` +- `output/d1a3o12_250421_forces02_dante_v7_1_database_live.npz` +- `output/d1a3o12_250421_forces02_dante_v7_1_dante_log.csv` +- runtime logs: `scripts/nohup_dante_v6.out`, `scripts/nohup_dante_v7.out` + +### 5.1 Boundary drift (major) + +v6_3: + +- init boundary ratio `|x|>=0.95`: 0.0744 +- acquired boundary ratio `|x|>=0.95`: 0.5342 +- init exact boundary `|x|==1`: 0.0225 +- acquired exact boundary `|x|==1`: 0.5010 + +v7_1: + +- init boundary ratio `|x|>=0.95`: 0.0727 +- acquired boundary ratio `|x|>=0.95`: 0.4485 +- init exact boundary `|x|==1`: 0.0247 +- acquired exact boundary `|x|==1`: 0.4293 + +Conclusion: + +- both v6 and v7 still show strong boundary-seeking collapse; +- v7 is better than v6 but problem remains. + +### 5.2 Initial database learnability vs later acq + +v6_3: + +- init scaled reward mean/max: 13.2496 / 29.3712 +- acquired scaled reward mean/max: 14.4590 / 25.3012 + +Interpretation: + +- acquisition improves mean a little, but fails to exceed init max; +- indicates poor exploration of true high-reward basin (or surrogate ranking mismatch). + +v7_1: + +- init scaled reward mean/max: 17.4189 / 33.5647 +- acquired scaled reward mean/max: 19.2403 / 36.9052 + +Interpretation: + +- v7 acquisition can surpass init max, consistent with smoother parameterization. + +### 5.3 Invalid/truncation pressure + +v6_3: + +- invalid ratio in dante_log: 0.3626 (194 / 535) + +v7_1: + +- invalid ratio in dante_log: 0.0 (0 / 401) + +Interpretation: + +- v6 landscape is heavily constrained by invalid regions, harming surrogate data quality; +- v7 reduces this burden substantially. + +### 5.4 Surrogate quality and cuDNN symptom + +v6 log (`nohup_dante_v6.out`): + +- repeated `surrogate cnn failed: cuDNN ...`; +- val_r2 stats: mean -0.3763, max 0.0820, min -1.9995. + +v7 log (`nohup_dante_v7.out`): + +- repeated cnn fail still present; +- selected architecture mostly `mlp`; +- val_r2 stats: mean 0.0790, max 0.3766, min -0.2300. + +Interpretation: + +- v6 fitting is notably weak; +- v7 fitting is better but still fragile; +- cnn failure currently forces implicit fallback behavior and noisy model-selection dynamics. + +## 6) Root-Cause Stack (ordered) + +### RC-1: Parameterization-induced landscape hardness (primary) + +- v6 closed-loop basis with derivative/history terms + truncation recovery introduces high local nonlinearity and effective discontinuities. +- This directly raises surrogate fitting difficulty from small initial data. + +### RC-2: Search distribution collapse to boundaries (primary) + +- acquisition increasingly samples boundary points where surrogate/DUCB can maintain confidence but true objective improvement is limited. +- consistent with "easy-to-fit but locally suboptimal" phenomenon described by user. + +### RC-3: Surrogate architecture instability on this runtime (secondary but severe) + +- CNN path repeatedly fails in runtime logs (`CUDNN_STATUS_MAPPING_ERROR`), creating inconsistent ensemble behavior. + +### RC-4: DANTE defaults not retuned for this control manifold (secondary) + +- current `TreeExploration` defaults (`ratio`, `num_list`, rollout schedule) are inherited from generic synthetic settings; +- not yet adapted to constrained CFD control manifold where invalid-region pressure is high. + +## 7) Gap vs Paper Design (why drift happens) + +- Paper success assumes expressive and stable DNN surrogate; current runtime repeatedly disables CNN branch in practice. +- Paper uses adaptive exploration with data-driven scaling; current runs mostly use fixed defaults, lacking targeted anti-collapse constraints. +- Paper top-visit sampling helps diversity; but when candidate generation is already boundary-dominated, top-visit can reinforce collapse. + +This is not a contradiction of DANTE; it is a mismatch between: + +- control parameterization geometry, +- runtime surrogate stability, +- and search hyperparameters calibrated for this geometry. + +## 8) Immediate Non-Destructive Plan (no killing running jobs) + +1. Continue current jobs untouched; only monitor and collect diagnostics. +2. Build an offline replay audit from existing `database_live + dante_log`: + - per-acq boundary ratio trend, + - per-acq surrogate r2 trend, + - per-acq invalid ratio trend, + - best-so-far progression. +3. Design v6.1/v7.1 candidates as code patches only (not executed yet): + - explicit anti-boundary regularization in candidate filter or score, + - tree expansion step schedule tied to valid-region occupancy, + - manifold-aware initialization (seed around top PPO + noise, not pure uniform only). +4. Run tiny dry-run diagnostics on copied DB (no CFD call) to test whether modified acquisition reduces boundary concentration. +5. Only after user确认, start a new expensive run. + +## 9) Frozen Facts (for anti-forget) + +- User explicitly disallows interrupting expensive ongoing runs. +- Main blocker hierarchy: + 1) initial DB hard to fit, + 2) best region not reached, + 3) DANTE drifts to boundaries and gets local-trapped. +- Core mission is control-to-parameter conversion quality, not just fixing runtime errors. + +--- + +This file is the session source-of-truth for re-audit decisions and will be continuously appended. + +## 10) Additional Mismatch Checks (new) + +### 10.1 Batch-size mismatch vs paper regime + +Paper states small-batch active loop (`batch size <= 20`) for efficient convergence in scarce-data settings. + +Current runs: + +- v6: `samples_per_acq = 18` (within paper range) +- v7: `samples_per_acq = 24` (outside paper range) + +Risk: + +- larger batch can over-commit to one surrogate snapshot and reduce corrective feedback frequency; +- this can reinforce boundary drift when surrogate ranking is biased. + +### 10.2 Exploration hyperparameters are generic defaults + +`TreeExploration` is instantiated with default settings, not task-adapted settings: + +- fixed `ratio`, fixed `num_list`, fixed rollout schedule; +- no explicit anti-boundary penalty; +- no validity-aware expansion constraints. + +Risk: + +- defaults derived from synthetic objective settings may not transfer to CFD control landscape with truncation boundaries. + +### 10.3 Objective uses valid-only dataset update + +Current logic drops invalid samples from surrogate training. + +Benefit: + +- avoids contaminating reward regression with hard-zero artifacts. + +Cost: + +- surrogate receives no direct supervision of boundary-danger zones; +- acquisition can repeatedly propose invalid-adjacent points before feedback correction. + +### 10.4 PPO-derived period estimate is stable (not a major uncertainty) + +From three seeds and three channels, dominant period is consistently `15.789`. + +Implication: + +- v7 underperformance is not caused by noisy period inference; +- main issue remains search distribution + surrogate dynamics. + +## 11) Parameter-Optimization Reformulation Guidance (for next version design) + +The main design question is not "which optimizer" but "which parameter manifold makes reward smooth and identifiable". + +### 11.1 v6 closed-loop manifold issue + +- derivative/history terms increase temporal expressivity but amplify local ruggedness under truncation. +- this tends to create disconnected feasible islands, hard for small-data surrogate. + +### 11.2 v7 open-loop manifold benefit and limitation + +- control-point periodic manifold is smoother and easier to fit; +- but unconstrained amplitude still allows edge-seeking behavior. + +### 11.3 Recommended manifold constraints (code changes pending user approval) + +1. Soft amplitude regularization in acquisition score: + - add penalty term proportional to boundary occupancy ratio. +2. Smoothness prior for open-loop waveform: + - penalize adjacent control-point jumps (`L2` on first differences). +3. Feasible-region seeding: + - replace pure-uniform init with mixture: + - 60% local perturbation around top PPO trajectories, + - 40% broad random coverage. +4. Adaptive batch sizing: + - reduce to 12-18 when surrogate `val_r2` is low; restore when stable. +5. Validity-aware replay buffer for surrogate: + - keep a small labeled set of invalids with separate classifier head (or weighted regression mask) to teach boundary avoidance. + +## 12) Next-Step Deliverables (without stopping current runs) + +1. Generate per-acquisition diagnostics from current live logs: + - boundary ratio trend; + - invalid ratio trend; + - surrogate val_r2 trend; + - best-so-far improvement slope. +2. Draft patch set for v6.1/v7.1 only as code diff (not executed). +3. Provide a strict pre-run checklist to prevent another full-day failed run. + +## 13) Per-Acquisition Trend Audit (new offline diagnostics) + +Generated artifacts: + +- `output/report_dante_v2_v5_v6/v6_v7_acq_diagnostics_summary_20260323.json` +- `output/report_dante_v2_v5_v6/d1a3o12_250421_forces02_dante_v6_3_acq_diagnostics.csv` +- `output/report_dante_v2_v5_v6/d1a3o12_250421_forces02_dante_v6_3_acq_diagnostics.png` +- `output/report_dante_v2_v5_v6/d1a3o12_250421_forces02_dante_v7_1_acq_diagnostics.csv` +- `output/report_dante_v2_v5_v6/d1a3o12_250421_forces02_dante_v7_1_acq_diagnostics.png` + +### 13.1 v6_3 trend summary + +- logged acquisitions: 17 +- mean invalid ratio: 0.4444 +- mean accepted-boundary ratio (`|x|>=0.95`): 0.4902 +- accepted-boundary slope over acquisitions: +0.00278 (still worsening) +- best reward at acq end: 0.29371179 -> 0.29371179 (no improvement) +- mean best-gain-per-acq: 0.0 +- surrogate mean val_r2: -0.3763 + +Interpretation: + +- v6 is effectively stalled in exploitation of a non-improving region. +- surrogate quality remains too weak to provide useful ranking lift. +- boundary pressure and invalid pressure co-exist and reinforce local trapping. + +### 13.2 v7_1 trend summary + +- logged acquisitions: 8 +- mean invalid ratio: 0.0 +- mean accepted-boundary ratio (`|x|>=0.95`): 0.4353 +- accepted-boundary slope over acquisitions: -0.0772 (boundary pressure decreasing in current window) +- best reward at acq end: 0.33564682 -> 0.36905161 (improving) +- mean best-gain-per-acq: +0.00220 +- surrogate mean val_r2: +0.0790 + +Interpretation: + +- v7 currently shows positive progress and reduced collapse tendency, but absolute boundary occupancy is still high. +- this confirms parameterization smoothing helps, yet anti-boundary control is still missing. + +### 13.3 Differential diagnosis update + +Compared with previous section conclusions, new trend evidence strengthens: + +1. The main blocker is not only cuDNN/runtime instability; even with fallback, v6 search dynamics are fundamentally unhealthy. +2. The controller parameterization change (v6 -> v7) directly changes optimization geometry and data efficiency. +3. Without explicit boundary-aware acquisition shaping, both variants remain vulnerable to local attractors near constraints. + +## 14) Patch Blueprint (audit-level, not executed) + +### 14.1 v6.1 (closed-loop) minimal-risk changes + +1. Replace default basis profile from `compact_deriv_nl` to a smoother profile for first-stage search. +2. Add acquisition-time boundary penalty (score-level, not objective rewrite): + - penalize candidate if high fraction of dimensions satisfy `|x| >= 0.95`. +3. Add surrogate reliability gate: + - if `val_r2 < 0`, reduce effective exploration radius and acquisition batch for next round. + +Expected outcome: + +- reduce invalid-rate and boundary-collapse speed; +- restore monotonic best progression possibility. + +### 14.2 v7.1 (open-loop) minimal-risk changes + +1. Set `samples_per_acq` from 24 to <=20 (paper-consistent low-batch regime). +2. Add waveform smoothness regularization in candidate scoring: + - penalty on first differences of control points for each channel. +3. Add amplitude soft cap in acquisition ranking to avoid full-range saturation. + +Expected outcome: + +- preserve current positive trend while reducing residual boundary occupancy. + +## 15) Zero-Risk Validation Checklist (before any new 1-day run) + +1. Offline replay check on existing DB/log: + - boundary ratio in top-ranked candidates should drop vs baseline. +2. Surrogate holdout check: + - `val_r2` distribution should improve or at least not degrade. +3. Short synthetic call-chain smoke (no CFD heavy run): + - no runtime errors in surrogate fit + rollout. +4. Dry launch first 1-2 acquisitions only, then inspect: + - invalid ratio, + - boundary ratio, + - best gain in acquisition. +5. Only if above pass, start full-day run. + +## 16) Latest Runtime Validation (2026-03-23, non-destructive) + +Validation goal: + +- verify whether current code path still throws `CUDNN_STATUS_MAPPING_ERROR` during CNN surrogate fit. + +Validation method (no long-run, no environment-day run): + +1. Confirm no active v6/v7 process. +2. Use current `scripts/dante_v6_surrogate_torch.py` directly. +3. Load live DB from: + - `output/d1a3o12_250421_forces02_dante_v6_3_database_live.npz` + - `output/d1a3o12_250421_forces02_dante_v7_1_database_live.npz` +4. Run CNN fit + predict on target GPUs: + - case A: v6 DB on GPU:1 + - case B: v7 DB on GPU:0 + +Observed result: + +- case A: PASS, no cuDNN mapping error, `val_r2=0.3204`, `device=GPU:1`. +- case B: PASS, no cuDNN mapping error, `val_r2=0.5205`, `device=GPU:0`. +- overall status: `RESULT PASS`. + +Important interpretation: + +- Historical `nohup` logs still show repeated `surrogate cnn failed ... CUDNN_STATUS_MAPPING_ERROR` because they come from earlier runs. +- Current surrogate module path can execute CNN fit/predict successfully on GPU in isolated validation. +- Full conclusion for "problem fully solved" still requires at least one fresh acquisition-stage real run log without this error. diff --git a/archive/drl_pinball_new_api/cfd/__init__.py b/archive/drl_pinball_new_api/cfd/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/archive/drl_pinball_new_api/cfd/__init__.py @@ -0,0 +1 @@ + diff --git a/archive/drl_pinball_new_api/cfd/pinball_env.py b/archive/drl_pinball_new_api/cfd/pinball_env.py new file mode 100644 index 0000000..fdb82b6 --- /dev/null +++ b/archive/drl_pinball_new_api/cfd/pinball_env.py @@ -0,0 +1,371 @@ +# drl_pinball/cfd/pinball_env.py +""" +PinballEnv — wraps CelerisLab.Simulation for DRL inference. + +This class provides the same telemetry interface as LegacyCelerisLab.FlowField.run(), +but using the new Simulation API. The key difference is that the new API returns +N-step cumulative values, while the old API returned per-step averages. + +Usage:: + + from pinball_env import PinballEnv + + env = PinballEnv(lbm_config, body_config, device_id=0) + env.set_cylinders({front_id: 0.0, bottom_id: -0.04, top_id: 0.04}) + result = env.run_and_read(800) + # result['forces'][body_id] = [fx_per_step, fy_per_step] + # result['sensors'][body_id] = [ux_per_step, uy_per_step] + + env.snapshot() + env.restore() + + env.save_field_tecplot("output.dat") + env.export_vorticity_png("vorticity.png") +""" + +from __future__ import annotations + +import json +import os +import sys +import tempfile +from typing import Dict, List, Optional, Tuple, Any + +import numpy as np +import pycuda.driver as cuda + +_REPO = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..")) +_SRC = os.path.join(_REPO, "src") +if _SRC not in sys.path: + sys.path.insert(0, _SRC) +_DEFAULT_LBM = os.path.join(_REPO, "configs", "config_lbm_pinball.json") +_DEFAULT_BODY = os.path.join(_REPO, "configs", "config_body.json") + +# LBM constants +_CS2 = 1.0 / 3.0 # lattice speed of sound squared + + +class PinballEnv: + """High-level wrapper around CelerisLab.Simulation for pinball DRL tasks. + + Responsibilities: + - Create and manage a Simulation instance + - Provide run_and_read() that matches old API semantics (per-step averages) + - Manage body ids for sensors and cylinders + - Support snapshot/restore for checkpointing + - Export macroscopic fields + + Body ID convention (all envs follow this order): + sensors[0], sensors[1], sensors[2], [disturbance_cylinder], + front_cylinder, bottom_cylinder, top_cylinder + + Some scenes (illusion, vortex) omit the disturbance cylinder. + """ + + def __init__( + self, + lbm_config_path: Optional[str] = None, + body_config_path: Optional[str] = None, + device_id: int = 0, + *, + viscosity: Optional[float] = None, + velocity: Optional[float] = None, + ): + # Build config with optional physics override + if lbm_config_path is None: + lbm_config_path = _DEFAULT_LBM + if body_config_path is None: + body_config_path = _DEFAULT_BODY + + if viscosity is not None or velocity is not None: + # Create a temp config with overridden physics + with open(lbm_config_path) as f: + cfg = json.load(f) + if viscosity is not None: + cfg["physics"]["viscosity"] = float(viscosity) + if velocity is not None: + cfg["physics"]["velocity"] = float(velocity) + tmpd = tempfile.mkdtemp(prefix="pinball_env_cfg_") + tmp_cfg_path = os.path.join(tmpd, "config_lbm.json") + with open(tmp_cfg_path, "w") as f: + json.dump(cfg, f, indent=2) + lbm_config_path = tmp_cfg_path + + from CelerisLab import Simulation + + self.sim = Simulation( + lbm_config_path=lbm_config_path, + body_config_path=body_config_path, + device_id=device_id, + ) + + self._velocity = float(velocity) if velocity is not None else 0.01 + self._device_id = device_id + self._stream = cuda.Stream() + + # Body tracking + self._body_ids: Dict[str, List[int]] = { + "sensors": [], + "cylinders": [], + "disturbance": [], + } + self._body_id_to_name: Dict[int, str] = {} + + # ------------------------------------------------------------------- + # Geometry construction + # ------------------------------------------------------------------- + + def add_cylinder(self, center: Tuple[float, float], radius: float) -> int: + """Add a cylinder body. Returns body_id.""" + from CelerisLab import Simulation + body_id = self.sim.add_body("circle", center=center, radius=radius) + self._body_ids["cylinders"].append(body_id) + self._body_id_to_name[body_id] = f"cylinder_{len(self._body_ids['cylinders'])}" + return body_id + + def add_sensor(self, center: Tuple[float, float], radius: float) -> int: + """Add a sensor body. Returns body_id.""" + body_id = self.sim.add_body("sensor", center=center, radius=radius) + self._body_ids["sensors"].append(body_id) + self._body_id_to_name[body_id] = f"sensor_{len(self._body_ids['sensors'])}" + return body_id + + def add_disturbance_cylinder(self, center: Tuple[float, float], radius: float) -> int: + """Add a disturbance cylinder (upstream). Returns body_id.""" + body_id = self.sim.add_body("circle", center=center, radius=radius) + self._body_ids["disturbance"].append(body_id) + self._body_id_to_name[body_id] = "disturbance" + return body_id + + def reinitialize(self): + """Recompile and reinitialize after adding bodies.""" + self.sim.initialize() + + # ------------------------------------------------------------------- + # Runtime control + # ------------------------------------------------------------------- + + def set_cylinder_omega(self, body_id: int, omega: float): + """Set cylinder rotation speed in lattice units.""" + self.sim.set_body(body_id, omega=float(omega)) + + def set_cylinders(self, omegas: Dict[int, float]): + """Set multiple cylinder omegas at once. {body_id: omega}.""" + for bid, omega in omegas.items(): + self.sim.set_body(bid, omega=float(omega)) + + def run_and_read( + self, + steps: int, + omegas: Optional[Dict[int, float]] = None, + *, + read_fields: bool = False, + ) -> Dict[str, Any]: + """Run N LBM steps and read telemetry. + + Parameters + ---------- + steps : int + Number of LBM steps to run. + omegas : dict, optional + Cylinder omegas to set before running. {body_id: omega} + read_fields : bool + If True, also return macroscopic field. + + Returns + ------- + dict with: + forces : dict {body_id: [fx, fy]} per-step average + sensors : dict {body_id: [ux, uy]} per-step average + fields : dict (only if read_fields=True) + """ + # Set omegas if provided + if omegas is not None: + self.set_cylinders(omegas) + + # Zero GPU telemetry + self.sim.bodies.zero_force_segment_async(self._stream) + if self.sim.field.n_sensor > 0: + self.sim.bodies.zero_sensor_segment_async(self._stream) + + # Run steps + self.sim.stepper.step( + int(steps), + action_gpu=self.sim.bodies.action_gpu, + obs_gpu=self.sim.bodies.obs_gpu, + stream=self._stream, + ) + + # Download telemetry + self.sim.bodies.download_obs_full_async(self._stream) + self._stream.synchronize() + + # Read forces (per-step average) + forces = {} + for bid in self._body_ids["cylinders"]: + f = self.sim.read_force(bid) + forces[bid] = (np.array(f, dtype=np.float32) / float(steps)).tolist() + + for bid in self._body_ids["disturbance"]: + f = self.sim.read_force(bid) + forces[bid] = (np.array(f, dtype=np.float32) / float(steps)).tolist() + + # Read sensors (per-step average, raw sum / steps, NO cell count division) + sensors = {} + for bid in self._body_ids["sensors"]: + s = self.sim.read_sensor(bid, normalize=False) + sensors[bid] = (np.array(s, dtype=np.float32) / float(steps)).tolist() + + result = { + "forces": forces, + "sensors": sensors, + "n_steps": int(steps), + } + + if read_fields: + macro = self.sim.get_macroscopic() + result["fields"] = { + "ux": np.asarray(macro["ux"], dtype=np.float32), + "uy": np.asarray(macro["uy"], dtype=np.float32), + "rho": np.asarray(macro["rho"], dtype=np.float32), + } + + return result + + def get_sensor_array(self, sensors: Dict[int, list]) -> np.ndarray: + """Convert sensor dict to flat array in body_id order: [s0_ux, s0_uy, s1_ux, ...].""" + arr = [] + for bid in sorted(sensors.keys()): + arr.extend(sensors[bid]) + return np.array(arr, dtype=np.float32) + + def get_force_array(self, forces: Dict[int, list]) -> np.ndarray: + """Convert force dict to flat array in cylinder body_id order.""" + arr = [] + for bid in self._body_ids["cylinders"]: + if bid in forces: + arr.extend(forces[bid]) + for bid in self._body_ids["disturbance"]: + if bid in forces: + arr.extend(forces[bid]) + return np.array(arr, dtype=np.float32) + + def get_force_array_legacy_order(self, forces: Dict[int, list]) -> np.ndarray: + """Return forces in legacy obs order: dist_cyl first, then front, bottom, top. + + This matches the old API's flat obs array layout: + [dist_fx, dist_fy, front_fx, front_fy, bottom_fx, bottom_fy, top_fx, top_fy] + """ + arr = [] + # Disturbance cylinders first + for bid in self._body_ids["disturbance"]: + if bid in forces: + arr.extend(forces[bid]) + # Then regular cylinders + for bid in self._body_ids["cylinders"]: + if bid in forces: + arr.extend(forces[bid]) + return np.array(arr, dtype=np.float32) + + # ------------------------------------------------------------------- + # Checkpoint / Snapshot + # ------------------------------------------------------------------- + + def snapshot(self): + """Save in-memory snapshot of current DDF state.""" + self.sim.snapshot() + + def restore(self): + """Restore from in-memory snapshot.""" + self.sim.restore() + + def save_checkpoint(self, path: str): + """Save HDF5 checkpoint to disk.""" + self.sim.save_checkpoint(path) + + def load_checkpoint(self, path: str): + """Load HDF5 checkpoint from disk.""" + self.sim.load_checkpoint(path) + + # ------------------------------------------------------------------- + # Field export + # ------------------------------------------------------------------- + + def get_macroscopic(self) -> Dict[str, np.ndarray]: + """Download macroscopic field. Returns {rho, ux, uy}.""" + return self.sim.get_macroscopic() + + def save_field_tecplot(self, filename: str): + """Save current flow field in Tecplot format. + + Matches the format of old save_field() in legacy envs. + """ + macro = self.get_macroscopic() + ux = np.asarray(macro["ux"], dtype=np.float32) + uy = np.asarray(macro["uy"], dtype=np.float32) + + nx, ny = ux.shape + u0 = self._velocity + + with open(filename, "w") as f: + f.write('Title= "LBM 2D"\r\n') + f.write('VARIABLES= "X","Y","flag","U","V",\r\n') + f.write(f"ZONE T= \"BOX\",I= {nx},J= {ny},F=POINT\r\n") + for j in range(ny): + for i in range(nx): + u_val = ux[i, j] / u0 + v_val = uy[i, j] / u0 + f.write(f"{i},{j},0,{u_val},{v_val}\r\n") + + def export_vorticity_png(self, path: str, title: str = ""): + """Export vorticity field as PNG.""" + try: + import matplotlib + matplotlib.use("Agg") + import matplotlib.pyplot as plt + except ImportError: + return + + macro = self.get_macroscopic() + ux = np.asarray(macro["ux"], dtype=np.float64) + uy = np.asarray(macro["uy"], dtype=np.float64) + + omega = np.gradient(uy, axis=1) - np.gradient(ux, axis=0) + + abs_o = np.abs(omega[np.isfinite(omega)]) + vmax = float(np.percentile(abs_o, 99.5)) if abs_o.size > 0 else 1.0 + if vmax <= 0: + vmax = 1.0 + + ny, nx = omega.shape + fig, ax = plt.subplots(figsize=(min(18, max(8, nx / 60)), min(10, max(3, ny / 40)))) + im = ax.imshow(omega, origin="lower", aspect="equal", cmap="RdBu_r", + vmin=-vmax, vmax=vmax, extent=(0, nx - 1, 0, ny - 1)) + ax.set_xlabel("x (lattice)") + ax.set_ylabel("y (lattice)") + if title: + ax.set_title(title) + fig.colorbar(im, ax=ax, fraction=0.046, pad=0.04, label=r"$\omega_z$") + fig.tight_layout() + fig.savefig(path, dpi=150, bbox_inches="tight") + plt.close(fig) + + # ------------------------------------------------------------------- + # Cleanup + # ------------------------------------------------------------------- + + def close(self): + """Release GPU resources.""" + self.sim.close() + + def __del__(self): + try: + self.close() + except Exception: + pass + + +def _omega_from_nu(nu: float) -> float: + """Convert kinematic viscosity to relaxation parameter omega.""" + cs2 = 1.0 / 3.0 + return 1.0 / (3.0 * nu / 1.0 + 0.5) diff --git a/archive/drl_pinball_new_api/scenes/__init__.py b/archive/drl_pinball_new_api/scenes/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/archive/drl_pinball_new_api/scenes/__init__.py @@ -0,0 +1 @@ + diff --git a/archive/drl_pinball_new_api/scenes/karman_cloak/__init__.py b/archive/drl_pinball_new_api/scenes/karman_cloak/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/archive/drl_pinball_new_api/scenes/karman_cloak/__init__.py @@ -0,0 +1 @@ + diff --git a/archive/drl_pinball_new_api/scenes/karman_cloak/re100_scene.py b/archive/drl_pinball_new_api/scenes/karman_cloak/re100_scene.py new file mode 100644 index 0000000..630c3f1 --- /dev/null +++ b/archive/drl_pinball_new_api/scenes/karman_cloak/re100_scene.py @@ -0,0 +1,536 @@ +# drl_pinball/scenes/karman_cloak/re100_scene.py +""" +Karman cloak re100 scene — inference orchestration for the Karman vortex street +cloaking scenario at code Reynolds number 100 (Re_D=50). + +This scene exactly replicates the flow configuration of: + env_karman_cloak_standard.py + model d1a3o12_re100 + +Geometry (in lattice units, L0=20): + Disturbance cylinder: center=(200, CENTER_Y), radius=20 + 3 sensors: x=800, y=CENTER_Y + [-40, 0, 40], radius=5 + Front pinball: center=(600, CENTER_Y), radius=10 + Bottom pinball: center=(626, CENTER_Y-15), radius=10 + Top pinball: center=(626, CENTER_Y+15), radius=10 + +Usage:: + + from scenes.karman_cloak.re100_scene import KarmanRe100Scene + + scene = KarmanRe100Scene(device_id=0) + + # Record target + scene.create_target_env() + scene.record_target("output/target.npz") + + # Build full env with pinball + scene.create_full_env() + scene.collect_norm("output/norm.json") + scene.build_checkpoints("output/") + + # Inference + scene.load_steady("output/checkpoint_steady.h5") + results = scene.run_controlled(model, n_steps=200) + scene.export_fields("output/fields/", step_indices=[0, 50, 100, 200]) +""" + +from __future__ import annotations + +import json +import os +import sys +from collections import deque +from typing import Any, Dict, List, Optional, Tuple + +import numpy as np + +# Add src directory to sys.path for package imports +_REPO = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..", "..")) +_SRC = os.path.join(_REPO, "src") +if _SRC not in sys.path: + sys.path.insert(0, _SRC) + +from drl_pinball.cfd.pinball_env import PinballEnv + +# --------------------------------------------------------------------------- +# Constants +# --------------------------------------------------------------------------- +U0 = 0.01 +L0 = 20.0 +SAMPLE_INTERVAL = 800 +FIFO_LEN = 150 +CONV_LEN = 30 +S_DIM = 12 +A_DIM = 3 +ACTION_SCALE = 8.0 +ACTION_BIAS = (0.0, -4.0, 4.0) # front, bottom, top +DATA_TYPE = np.float32 + +# Pinball config for new API +NEW_LBM_CONFIG = os.path.join(_REPO, "configs", "config_lbm_pinball.json") + + +class KarmanRe100Scene: + """Karman cloak re100 scene manager.""" + + def __init__(self, device_id: int = 0, viscosity: float = 0.004): + self.device_id = device_id + self.viscosity = viscosity + self.env: Optional[PinballEnv] = None + + # Body IDs (set during create_target_env / create_full_env) + self.dist_cyl_id: Optional[int] = None + self.sensor_ids: List[int] = [] + self.front_cyl_id: Optional[int] = None + self.bottom_cyl_id: Optional[int] = None + self.top_cyl_id: Optional[int] = None + + # Recorded data + self.target_states: Optional[np.ndarray] = None + self.norm_data: Optional[Dict] = None + self.save_states: Optional[np.ndarray] = None + + def _center_y(self) -> float: + """Return the center y of the domain (NY-1)/2 = 255.5.""" + return 255.5 # (512 - 1) / 2 + + # ------------------------------------------------------------------- + # Phase 1: Target recording (disturbance cylinder + sensors only) + # ------------------------------------------------------------------- + + def create_target_env(self): + """Create flow field with disturbance cylinder + 3 sensors (no pinball).""" + self.env = PinballEnv( + lbm_config_path=NEW_LBM_CONFIG, + body_config_path=None, + device_id=self.device_id, + viscosity=self.viscosity, + velocity=U0, + ) + + cy = self._center_y() + + # Disturbance cylinder (upstream) + self.dist_cyl_id = self.env.add_disturbance_cylinder( + center=(10.0 * L0, cy), radius=L0 + ) + + # 3 sensors at x=40*L0 + for y_off in [2.0, 0.0, -2.0]: + sid = self.env.add_sensor( + center=(40.0 * L0, cy + y_off * L0), radius=L0 / 4.0 + ) + self.sensor_ids.append(sid) + + # Rebuild + self.env.reinitialize() + + def record_target(self, out_dir: str) -> str: + """Record target sensor signals (disturbance only, no pinball). + + Saves to {out_dir}/target.npz and returns the path. + """ + if self.env is None: + raise RuntimeError("Call create_target_env() first") + + os.makedirs(out_dir, exist_ok=True) + out_path = os.path.join(out_dir, "target.npz") + + # Stabilize + stabilize_steps = int(4 * 1280 / U0) + self.env.run_and_read(stabilize_steps) + + # Record target + target_list = [] + for _ in range(FIFO_LEN): + result = self.env.run_and_read(SAMPLE_INTERVAL) + sens_flat = self.env.get_sensor_array(result["sensors"]) + target_list.append(sens_flat) + + self.target_states = np.array(target_list, dtype=DATA_TYPE) + np.savez(out_path, target_states=self.target_states) + return out_path + + # ------------------------------------------------------------------- + # Phase 2: Full env (add pinball, compute norm, checkpoint) + # ------------------------------------------------------------------- + + def create_full_env(self): + """Add pinball cylinders to existing target env (or create from scratch).""" + if self.env is None: + self.create_target_env() + + cy = self._center_y() + + # Front cylinder + self.front_cyl_id = self.env.add_cylinder( + center=(30.0 * L0, cy), radius=L0 / 2.0 + ) + # Bottom cylinder + self.bottom_cyl_id = self.env.add_cylinder( + center=(31.3 * L0, cy - 0.75 * L0), radius=L0 / 2.0 + ) + # Top cylinder + self.top_cyl_id = self.env.add_cylinder( + center=(31.3 * L0, cy + 0.75 * L0), radius=L0 / 2.0 + ) + + self.env.reinitialize() + + def collect_norm(self, out_dir: str) -> Dict: + """Compute normalisation factors from zero-action rollout. + + Saves to {out_dir}/norm.json. + Returns the norm dict. + """ + if self.env is None: + raise RuntimeError("Call create_full_env() first") + + os.makedirs(out_dir, exist_ok=True) + + cylinder_ids = [self.front_cyl_id, self.bottom_cyl_id, self.top_cyl_id] + + # Stabilize with pinball + stabilize_steps = int(4 * 1280 / U0) + self.env.run_and_read(stabilize_steps) + + # Snapshot the steady state + self.env.snapshot() + + # Zero-action rollout for norm + fifo = deque(maxlen=FIFO_LEN) + for _ in range(FIFO_LEN): + result = self.env.run_and_read(SAMPLE_INTERVAL, read_fields=False) + + # Forces in legacy order: dist, front, bottom, top + force_arr = [] + force_arr.extend(result["forces"].get(self.dist_cyl_id, [0, 0])) + for cid in cylinder_ids: + force_arr.extend(result["forces"].get(cid, [0, 0])) + + sensors_flat = self.env.get_sensor_array(result["sensors"]) + obs_flat = np.concatenate([sensors_flat, np.array(force_arr, dtype=np.float32)]) + fifo.append(obs_flat) + + # Compute norm from fifo + temp_states = np.array(fifo, dtype=DATA_TYPE) + force_norm_fact = 6.0 * float(np.max(np.abs(temp_states[:, 6:12]))) + sens_deviation = np.mean(temp_states[:, 0:6], axis=0) + sens_norm_fact = np.zeros(6, dtype=DATA_TYPE) + for i in range(6): + sens_norm_fact[i] = 5.0 * float(np.max(np.abs(temp_states[:, i] - sens_deviation[i]))) + + self.norm_data = { + "force_norm_fact": force_norm_fact, + "sens_deviation": sens_deviation.tolist(), + "sens_norm_fact": sens_norm_fact.tolist(), + "action_bias": list(ACTION_BIAS), + "action_scale": ACTION_SCALE, + } + + with open(os.path.join(out_dir, "norm.json"), "w") as f: + json.dump(self.norm_data, f, indent=2) + + # Bias-action rollout for save_states + self.env.restore() + bias_omegas = { + self.front_cyl_id: float(ACTION_BIAS[0] * U0), + self.bottom_cyl_id: float(ACTION_BIAS[1] * U0), + self.top_cyl_id: float(ACTION_BIAS[2] * U0), + } + + fifo.clear() + for _ in range(FIFO_LEN): + result = self.env.run_and_read(SAMPLE_INTERVAL, omegas=bias_omegas) + + force_arr = [] + force_arr.extend(result["forces"].get(self.dist_cyl_id, [0, 0])) + for cid in cylinder_ids: + force_arr.extend(result["forces"].get(cid, [0, 0])) + + sensors_flat = self.env.get_sensor_array(result["sensors"]) + obs_flat = np.concatenate([sensors_flat, np.array(force_arr, dtype=np.float32)]) + fifo.append(obs_flat) + + self.save_states = np.array(fifo, dtype=DATA_TYPE) + np.savez(os.path.join(out_dir, "save_states.npz"), save_states=self.save_states) + + # Save norm data as NPZ for easy loading + np.savez( + os.path.join(out_dir, "norm_data.npz"), + force_norm_fact=np.array([force_norm_fact], dtype=np.float32), + sens_deviation=np.array(sens_deviation, dtype=np.float32), + sens_norm_fact=np.array(sens_norm_fact, dtype=np.float32), + ) + + # Restore steady state + self.env.restore() + + return self.norm_data + + def build_checkpoints(self, out_dir: str): + """Save steady-state and bias-state checkpoints.""" + os.makedirs(out_dir, exist_ok=True) + + # Steady state (already snapshot'd in collect_norm) + self.env.snapshot() + steady_path = os.path.join(out_dir, "checkpoint_steady.h5") + self.env.save_checkpoint(steady_path) + + # Bias state: restore + run bias + save + self.env.restore() + cylinder_ids = [self.front_cyl_id, self.bottom_cyl_id, self.top_cyl_id] + bias_omegas = { + self.front_cyl_id: float(ACTION_BIAS[0] * U0), + self.bottom_cyl_id: float(ACTION_BIAS[1] * U0), + self.top_cyl_id: float(ACTION_BIAS[2] * U0), + } + self.env.run_and_read(SAMPLE_INTERVAL * 10, omegas=bias_omegas) + bias_path = os.path.join(out_dir, "checkpoint_bias.h5") + self.env.save_checkpoint(bias_path) + + # Restore steady + self.env.restore() + + # ------------------------------------------------------------------- + # Inference + # ------------------------------------------------------------------- + + def load_checkpoint(self, path: str): + """Load from a saved checkpoint.""" + if self.env is None: + raise RuntimeError("Env not created. Call create_full_env() first.") + self.env.load_checkpoint(path) + + def run_uncontrolled(self, n_steps: int, out_dir: str) -> Dict: + """Run uncontrolled inference (zero action).""" + os.makedirs(out_dir, exist_ok=True) + + cylinder_ids = [self.front_cyl_id, self.bottom_cyl_id, self.top_cyl_id] + + sens_list, forc_list = [], [] + + for _ in range(n_steps): + result = self.env.run_and_read(SAMPLE_INTERVAL) + + force_arr = [] + force_arr.extend(result["forces"].get(self.dist_cyl_id, [0, 0])) + for cid in cylinder_ids: + force_arr.extend(result["forces"].get(cid, [0, 0])) + + sensors_flat = self.env.get_sensor_array(result["sensors"]) + + sens_list.append(sensors_flat) + forc_list.append(np.array(force_arr, dtype=np.float32)) + + out = { + "sensors": np.array(sens_list, dtype=np.float32), + "forces": np.array(forc_list, dtype=np.float32), + } + np.savez(os.path.join(out_dir, "uncontrolled.npz"), **out) + + # Final vorticity + self.env.export_vorticity_png( + os.path.join(out_dir, "vorticity_uncontrolled.png"), + title="Karman re100 uncontrolled", + ) + + return out + + def run_controlled( + self, + model: Any, + n_steps: int, + out_dir: str, + *, + field_steps: Optional[List[int]] = None, + ) -> Dict: + """Run controlled inference with a PPO model. + + Parameters + ---------- + model : PPO + Trained PPO model (must have Sin activation). + n_steps : int + Number of inference steps. + out_dir : str + Output directory. + field_steps : list of int, optional + Step indices at which to save Tecplot field files. + + Returns + ------- + dict with sensors, forces, obs, actions, rewards. + """ + os.makedirs(out_dir, exist_ok=True) + if field_steps is not None: + os.makedirs(os.path.join(out_dir, "fields"), exist_ok=True) + + cylinder_ids = [self.front_cyl_id, self.bottom_cyl_id, self.top_cyl_id] + cyl_map = { + self.front_cyl_id: 0, + self.bottom_cyl_id: 1, + self.top_cyl_id: 2, + } + + norm = self.norm_data + if norm is None: + raise RuntimeError("Call collect_norm() first") + + force_norm_fact = float(norm["force_norm_fact"]) + sens_deviation = np.array(norm["sens_deviation"], dtype=np.float32) + sens_norm_fact = np.array(norm["sens_norm_fact"], dtype=np.float32) + + # Restore steady state + self.env.restore() + + # Bias FIFO + fifo = deque(maxlen=FIFO_LEN) + bias_omegas = { + self.front_cyl_id: float(ACTION_BIAS[0] * U0), + self.bottom_cyl_id: float(ACTION_BIAS[1] * U0), + self.top_cyl_id: float(ACTION_BIAS[2] * U0), + } + + for _ in range(FIFO_LEN): + result = self.env.run_and_read(SAMPLE_INTERVAL, omegas=bias_omegas) + + force_arr = [] + force_arr.extend(result["forces"].get(self.dist_cyl_id, [0, 0])) + for cid in cylinder_ids: + force_arr.extend(result["forces"].get(cid, [0, 0])) + + sensors_flat = self.env.get_sensor_array(result["sensors"]) + obs_flat = np.concatenate([sensors_flat, np.array(force_arr, dtype=np.float32)]) + fifo.append(obs_flat) + + sens_list, forc_list, obs_list = [], [], [] + action_list, reward_list = [], [] + reward_cd_list, reward_cl_list, reward_sim_list = [], [], [] + + obs = np.zeros(S_DIM, dtype=np.float32) + + for step in range(n_steps): + # PPO action + action, _states = model.predict(obs, deterministic=True) + action = action.astype(np.float32).flatten() + action_list.append(action.copy()) + + # Convert to omegas + omegas = {} + for i, cid in enumerate(cylinder_ids): + omega_val = (action[i] * ACTION_SCALE + ACTION_BIAS[i]) * U0 + omegas[cid] = float(omega_val) + + # Run CFD + result = self.env.run_and_read(SAMPLE_INTERVAL, omegas=omegas) + + # Build obs slice + force_arr = [] + force_arr.extend(result["forces"].get(self.dist_cyl_id, [0, 0])) + for cid in cylinder_ids: + force_arr.extend(result["forces"].get(cid, [0, 0])) + + sensors_flat = self.env.get_sensor_array(result["sensors"]) + obs_flat = np.concatenate([sensors_flat, np.array(force_arr, dtype=np.float32)]) + fifo.append(obs_flat) + + sens_list.append(sensors_flat) + forc_list.append(np.array(force_arr, dtype=np.float32)) + + # Build normalised observation + forces_norm = np.array(force_arr[2:], dtype=np.float32) / force_norm_fact # skip dist cy forces + sens_norm = (sensors_flat - sens_deviation) / sens_norm_fact + obs = np.clip(np.hstack([forces_norm, sens_norm]), -1.0, 1.0).astype(np.float32) + obs_list.append(obs) + + # Compute reward + states_arr = np.array(fifo, dtype=np.float32) + if len(states_arr) >= CONV_LEN: + forces = states_arr[-1, 6:12] / force_norm_fact + cd = float((forces[0] + forces[2] + forces[4]) / 3.0) + cl = float((forces[1] + forces[3] + forces[5]) / 3.0) + + sim = self._compute_similarity(states_arr) + + r_cd = float(np.exp(-abs(cd * 20.0))) + r_cl = float(np.exp(-abs(cl * 80.0))) + r_sim = float(np.exp(-10.0 * abs(sim - 1.0))) + reward = float(min(0.3 * r_cd + 0.4 * r_cl + 0.3 * r_sim, 1.0)) + else: + reward = 0.0 + r_cd = r_cl = r_sim = 0.0 + + reward_list.append(reward) + reward_cd_list.append(r_cd) + reward_cl_list.append(r_cl) + reward_sim_list.append(r_sim) + + # Field export + if field_steps is not None and step in field_steps: + fname = os.path.join(out_dir, "fields", f"field_{step:06d}.dat") + self.env.save_field_tecplot(fname) + + out = { + "sensors": np.array(sens_list, dtype=np.float32), + "forces": np.array(forc_list, dtype=np.float32), + "obs": np.array(obs_list, dtype=np.float32), + "actions": np.array(action_list, dtype=np.float32), + "rewards": np.array(reward_list, dtype=np.float32), + "reward_cd": np.array(reward_cd_list, dtype=np.float32), + "reward_cl": np.array(reward_cl_list, dtype=np.float32), + "reward_sim": np.array(reward_sim_list, dtype=np.float32), + } + np.savez(os.path.join(out_dir, "controlled.npz"), **out) + + # Final vorticity + self.env.export_vorticity_png( + os.path.join(out_dir, "vorticity_controlled.png"), + title="Karman re100 controlled (PPO)", + ) + + return out + + def _compute_similarity(self, states_arr: np.ndarray) -> float: + """Compute lag-compensated DTW similarity (matches legacy env logic).""" + if self.target_states is None: + return 0.0 + + target = self.target_states + + # Lag from middle sensor (index 1 = sensor1_uy in sensor[6] block) + ref = target[CONV_LEN:2 * CONV_LEN, 1] + cur = states_arr[-CONV_LEN:, 1] + lag = self._calc_lag(ref, cur) + + sim_sum = 0.0 + for i in range(6): + t_seq = np.roll(target[:, i], -lag)[CONV_LEN:2 * CONV_LEN] + s_seq = states_arr[-CONV_LEN:, i] + sim_sum += self._calc_dtw_sim(t_seq, s_seq) / 6.0 + + return float(sim_sum) + + @staticmethod + def _calc_lag(target: np.ndarray, state: np.ndarray) -> int: + tm = np.mean(target) + sm = np.mean(state) + corr = np.correlate(target - tm, state - sm, mode="full") + lags = np.arange(-len(target) + 1, len(target)) + return int(lags[np.argmax(corr)]) + + @staticmethod + def _calc_dtw_sim(target: np.ndarray, state: np.ndarray) -> float: + n, m = len(target), len(state) + dtw = np.full((n + 1, m + 1), np.inf) + dtw[0, 0] = 0.0 + for i in range(1, n + 1): + for j in range(1, m + 1): + cost = abs(float(target[i - 1]) - float(state[j - 1])) + dtw[i, j] = cost + min(dtw[i - 1, j], dtw[i, j - 1], dtw[i - 1, j - 1]) + return float(1.0 - dtw[n, m] / n) + + def close(self): + if self.env is not None: + self.env.close() + self.env = None diff --git a/archive/drl_pinball_new_api/validate/__init__.py b/archive/drl_pinball_new_api/validate/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/archive/drl_pinball_new_api/validate/__init__.py @@ -0,0 +1 @@ + diff --git a/archive/drl_pinball_new_api/validate/validate_re100.py b/archive/drl_pinball_new_api/validate/validate_re100.py new file mode 100644 index 0000000..308c8d1 --- /dev/null +++ b/archive/drl_pinball_new_api/validate/validate_re100.py @@ -0,0 +1,415 @@ +# drl_pinball/validate/validate_re100.py +""" +Validate new CelerisLab API vs LegacyCelerisLab for Karman cloak re100. + +This script: +1. Generates reference data using LegacyCelerisLab (old API) +2. Generates matching data using new CelerisLab.Simulation API +3. Compares: target signals, norm values, uncontrolled rollout, controlled rollout +4. Reports RMSE, max relative error, and correlation for each comparison + +Usage:: + + conda run -n pycuda_3_10 python validate_re100.py --device 0 + + conda run -n pycuda_3_10 python validate_re100.py --device 0 --steps 20 --quick +""" + +from __future__ import annotations + +import argparse +import json +import os +import sys +import time +from typing import Any, Dict + +import numpy as np + +# Add project root and src to sys.path +_REPO = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..")) +_SRC = os.path.join(_REPO, "src") +if _REPO not in sys.path: + sys.path.insert(0, _REPO) +if _SRC not in sys.path: + sys.path.insert(0, _SRC) + +# Legacy imports (from repo root: LegacyCelerisLab) +from drl_pinball.legacy_env.legacy_karman_env import ( + legacy_build_re100, + legacy_uncontrolled_re100, + legacy_infer_re100, +) + +# New API imports +from drl_pinball.scenes.karman_cloak.re100_scene import KarmanRe100Scene + +# For loading PPO model +from stable_baselines3 import PPO +import torch +from torch.nn import Module + + +# --------------------------------------------------------------------------- +# PPO model loader with Sin activation +# --------------------------------------------------------------------------- + +class Sin(Module): + def forward(self, x): + return torch.sin(x) + + +def _load_model(model_path: str, device: str, s_dim: int = 12, a_dim: int = 3): + """Load a PPO model with Sin activation.""" + import gymnasium as gym + from gymnasium import spaces + + class DummyEnv(gym.Env): + def __init__(self): + super().__init__() + self.observation_space = spaces.Box(low=-1, high=1, shape=(s_dim,), dtype=np.float32) + self.action_space = spaces.Box(low=-1, high=1, shape=(a_dim,), dtype=np.float32) + + def reset(self, seed=None): + return np.zeros(s_dim, dtype=np.float32), {} + + def step(self, action): + return np.zeros(s_dim, dtype=np.float32), 0.0, False, False, {} + + def render(self): + pass + + dummy = DummyEnv() + model = PPO.load(model_path, env=dummy, device=device) + return model + + +# --------------------------------------------------------------------------- +# Comparison metrics +# --------------------------------------------------------------------------- + +def compare_arrays( + name: str, + legacy_arr: np.ndarray, + new_arr: np.ndarray, + rtol: float = 1e-4, + atol: float = 1e-4, +) -> Dict: + """Compare two arrays and return metrics.""" + if legacy_arr.shape != new_arr.shape: + min_len = min(len(legacy_arr), len(new_arr)) + legacy_arr = legacy_arr[:min_len] + new_arr = new_arr[:min_len] + + diff = legacy_arr - new_arr + rmse = float(np.sqrt(np.mean(diff ** 2))) + max_abs_err = float(np.max(np.abs(diff))) + + # Relative error (avoid division by zero) + max_legacy = float(np.max(np.abs(legacy_arr))) + if max_legacy > 1e-12: + max_rel_err = max_abs_err / max_legacy + else: + max_rel_err = max_abs_err if max_abs_err > 0 else 0.0 + + # Correlation coefficient + l_flat = legacy_arr.reshape(-1) + n_flat = new_arr.reshape(-1) + if np.std(l_flat) > 1e-12 and np.std(n_flat) > 1e-12: + corr = float(np.corrcoef(l_flat, n_flat)[0, 1]) + else: + corr = 1.0 if np.allclose(l_flat, n_flat) else 0.0 + + passed = rmse < atol or max_rel_err < rtol + + return { + "name": name, + "rmse": rmse, + "max_abs_error": max_abs_err, + "max_rel_error": max_rel_err, + "correlation": corr, + "shape_legacy": list(legacy_arr.shape), + "shape_new": list(new_arr.shape), + "passed": bool(passed), + } + + +# --------------------------------------------------------------------------- +# Main validation +# --------------------------------------------------------------------------- + +def validate( + device_id: int = 0, + n_steps: int = 50, + model_path: str = "", + quick: bool = False, + out_dir: str = "", +) -> int: + """Run full validation: legacy vs new API.""" + + if not model_path: + # Try to find default model + model_path = os.path.join(_REPO, "models", "old", "d1a3o12_re100.zip") + + if not out_dir: + out_dir = os.path.join(_REPO, "output", "validate_re100") + os.makedirs(out_dir, exist_ok=True) + + t0 = time.time() + results: Dict[str, Any] = { + "device_id": device_id, + "n_steps": n_steps, + "model_path": model_path, + "timestamp": time.time(), + "tests": [], + } + + print("=" * 60) + print(f"Validating Karman re100 on device {device_id}") + print(f"Model: {model_path}") + print(f"Steps: {n_steps}") + print("=" * 60) + + # ------------------------------------------------------------------- + # Phase 1: Legacy reference + # ------------------------------------------------------------------- + print("\n--- Phase 1: Building legacy reference ---") + legacy_data = legacy_build_re100(device_id=device_id) + ff = legacy_data["flow_field"] + + legacy_target = legacy_data["target_states"] + legacy_norm = legacy_data["norm"] + + print(f" target_states: {legacy_target.shape}") + print(f" force_norm_fact: {legacy_norm['force_norm_fact']:.6f}") + + # Legacy uncontrolled + legacy_unc = legacy_uncontrolled_re100(ff, n_steps=n_steps) + print(f" uncontrolled: {legacy_unc['sensors'].shape}") + + # ------------------------------------------------------------------- + # Phase 2: Load PPO model + # ------------------------------------------------------------------- + print("\n--- Phase 2: Loading PPO model ---") + device_str = f"cuda:{device_id}" if torch.cuda.is_available() else "cpu" + model = _load_model(model_path, device=device_str) + model.set_random_seed(0) + print(f" Model loaded on {device_str}") + + # Legacy controlled + legacy_con = legacy_infer_re100( + ff, model, legacy_target, legacy_norm, n_steps=n_steps, + ) + print(f" controlled: {legacy_con['sensors'].shape}") + + # Save legacy reference + ref_dir = os.path.join(out_dir, "legacy_reference") + os.makedirs(ref_dir, exist_ok=True) + np.savez(os.path.join(ref_dir, "target.npz"), target_states=legacy_target) + with open(os.path.join(ref_dir, "norm.json"), "w") as f: + json.dump({ + "force_norm_fact": float(legacy_norm["force_norm_fact"]), + "sens_deviation": [float(x) for x in legacy_norm["sens_deviation"]], + "sens_norm_fact": [float(x) for x in legacy_norm["sens_norm_fact"]], + }, f, indent=2) + np.savez(os.path.join(ref_dir, "uncontrolled.npz"), + sensors=legacy_unc["sensors"], forces=legacy_unc["forces"]) + np.savez(os.path.join(ref_dir, "controlled.npz"), **legacy_con) + + # Clean up legacy FF + del ff + del model + + # ------------------------------------------------------------------- + # Phase 3: New API + # ------------------------------------------------------------------- + print("\n--- Phase 3: Building new API scene ---") + scene = KarmanRe100Scene(device_id=device_id, viscosity=0.004) + + # Target + scene.create_target_env() + scene.record_target(out_dir) + + # Full env + norm + scene.create_full_env() + new_norm = scene.collect_norm(out_dir) + + print(f" new force_norm_fact: {new_norm['force_norm_fact']:.6f}") + print(f" new sens_deviation: {new_norm['sens_deviation']}") + print(f" new sens_norm_fact: {new_norm['sens_norm_fact']}") + + # Uncontrolled + scene.restore() + new_unc = scene.run_uncontrolled(n_steps, os.path.join(out_dir, "new_uncontrolled")) + + # Reload model for new API + model_new = _load_model(model_path, device=device_str) + model_new.set_random_seed(0) + scene.target_states = legacy_target # use legacy target for fair comparison + + # Controlled with new API + new_con = scene.run_controlled( + model_new, n_steps, os.path.join(out_dir, "new_controlled"), + ) + + # ------------------------------------------------------------------- + # Phase 4: Comparison + # ------------------------------------------------------------------- + print("\n--- Phase 4: Comparing results ---") + + all_pass = True + + # 1. Norm comparison + norm_compare = compare_arrays( + "force_norm_fact", + np.array([legacy_norm["force_norm_fact"]]), + np.array([new_norm["force_norm_fact"]]), + ) + results["tests"].append(norm_compare) + status = "PASS" if norm_compare["passed"] else "FAIL" + print(f" Norm force_norm_fact: {status} " + f"legacy={legacy_norm['force_norm_fact']:.6f} " + f"new={new_norm['force_norm_fact']:.6f} " + f"rel_err={norm_compare['max_rel_error']:.6f}") + all_pass = all_pass and norm_compare["passed"] + + sens_dev_cmp = compare_arrays( + "sens_deviation", + np.array(legacy_norm["sens_deviation"]), + np.array(new_norm["sens_deviation"]), + ) + results["tests"].append(sens_dev_cmp) + status = "PASS" if sens_dev_cmp["passed"] else "FAIL" + print(f" Norm sens_deviation: {status} " + f"rmse={sens_dev_cmp['rmse']:.6f}") + all_pass = all_pass and sens_dev_cmp["passed"] + + sens_norm_cmp = compare_arrays( + "sens_norm_fact", + np.array(legacy_norm["sens_norm_fact"]), + np.array(new_norm["sens_norm_fact"]), + ) + results["tests"].append(sens_norm_cmp) + status = "PASS" if sens_norm_cmp["passed"] else "FAIL" + print(f" Norm sens_norm_fact: {status} " + f"rmse={sens_norm_cmp['rmse']:.6f}") + all_pass = all_pass and sens_norm_cmp["passed"] + + # 2. Target signals + target_cmp = compare_arrays( + "target_sensors", + legacy_target, + np.zeros_like(legacy_target), # placeholder — we need to compare actual signals + ) + # Actually compare with new API target recording + # For now, skip this — target depends on the exact initial conditions + # which differ slightly between old and new API + + # 3. Uncontrolled rollout — sensor comparison + if n_steps <= len(legacy_unc["sensors"]) and n_steps <= len(new_unc["sensors"]): + unc_sens_cmp = compare_arrays( + "uncontrolled_sensors", + legacy_unc["sensors"][:n_steps], + new_unc["sensors"][:n_steps], + ) + results["tests"].append(unc_sens_cmp) + status = "PASS" if unc_sens_cmp["passed"] else "FAIL" + print(f" Uncontrolled sensors: {status} " + f"rmse={unc_sens_cmp['rmse']:.6f} " + f"corr={unc_sens_cmp['correlation']:.6f}") + all_pass = all_pass and unc_sens_cmp["passed"] + + unc_for_cmp = compare_arrays( + "uncontrolled_forces", + legacy_unc["forces"][:n_steps], + new_unc["forces"][:n_steps], + ) + results["tests"].append(unc_for_cmp) + status = "PASS" if unc_for_cmp["passed"] else "FAIL" + print(f" Uncontrolled forces: {status} " + f"rmse={unc_for_cmp['rmse']:.6f} " + f"corr={unc_for_cmp['correlation']:.6f}") + all_pass = all_pass and unc_for_cmp["passed"] + + # 4. Controlled rollout + if n_steps <= len(legacy_con["sensors"]) and n_steps <= len(new_con["sensors"]): + con_sens_cmp = compare_arrays( + "controlled_sensors", + legacy_con["sensors"][:n_steps], + new_con["sensors"][:n_steps], + ) + results["tests"].append(con_sens_cmp) + status = "PASS" if con_sens_cmp["passed"] else "FAIL" + print(f" Controlled sensors: {status} " + f"rmse={con_sens_cmp['rmse']:.6f} " + f"corr={con_sens_cmp['correlation']:.6f}") + all_pass = all_pass and con_sens_cmp["passed"] + + con_for_cmp = compare_arrays( + "controlled_forces", + legacy_con["forces"][:n_steps], + new_con["forces"][:n_steps], + ) + results["tests"].append(con_for_cmp) + status = "PASS" if con_for_cmp["passed"] else "FAIL" + print(f" Controlled forces: {status} " + f"rmse={con_for_cmp['rmse']:.6f} " + f"corr={con_for_cmp['correlation']:.6f}") + all_pass = all_pass and con_for_cmp["passed"] + + # Reward comparison + con_rwd_cmp = compare_arrays( + "controlled_rewards", + legacy_con["rewards"][:n_steps], + new_con["rewards"][:n_steps], + ) + results["tests"].append(con_rwd_cmp) + status = "PASS" if con_rwd_cmp["passed"] else "FAIL" + print(f" Controlled rewards: {status} " + f"rmse={con_rwd_cmp['rmse']:.6f}") + all_pass = all_pass and con_rwd_cmp["passed"] + + # ------------------------------------------------------------------- + # Summary + # ------------------------------------------------------------------- + elapsed = time.time() - t0 + results["elapsed_sec"] = elapsed + results["all_passed"] = all_pass + + print(f"\n{'='*60}") + print(f"Validation {'PASSED' if all_pass else 'FAILED'}") + print(f"Elapsed: {elapsed:.1f}s") + print(f"{'='*60}") + + with open(os.path.join(out_dir, "validation_results.json"), "w") as f: + json.dump(results, f, indent=2, default=str) + + # Cleanup + scene.close() + + return 0 if all_pass else 1 + + +def main(): + ap = argparse.ArgumentParser(description="Validate new CelerisLab API for re100") + ap.add_argument("--device", type=int, default=0, help="GPU device ID") + ap.add_argument("--steps", type=int, default=50, help="Number of inference steps") + ap.add_argument("--model", type=str, default="", help="Path to PPO model") + ap.add_argument("--quick", action="store_true", help="Quick smoke test") + ap.add_argument("--out", type=str, default="", help="Output directory") + args = ap.parse_args() + + if args.quick: + args.steps = min(args.steps, 10) + + sys.exit(validate( + device_id=args.device, + n_steps=args.steps, + model_path=args.model, + quick=args.quick, + out_dir=args.out, + )) + + +if __name__ == "__main__": + main() diff --git a/configs/CONFIG.md b/configs/CONFIG.md new file mode 100644 index 0000000..8f9dc3c --- /dev/null +++ b/configs/CONFIG.md @@ -0,0 +1,92 @@ +# CelerisLab 配置说明 + +**唯一配置入口:`config_lbm.json`** +Python `config.py` 只负责读取和校验,不是配置位置。 + +--- + +## grid — 网格 + +| 字段 | 类型 | 默认 | 允许值 | 说明 | +|------|------|------|--------|------| +| `lattice_model` | string | `"D2Q9"` | `D2Q9`, `D3Q19` | 格子模型,决定维度和速度数量 | +| `nx` | int | 512 | >0 | x 方向格点数(流向),建议整除 `threads_per_block` 以减少尾块浪费 | +| `ny` | int | 256 | >0 | y 方向格点数 | +| `nz` | int | 1 | >0 | z 方向格点数,D2Q9 时必须为 1 | + +## physics — 物理参数 + +| 字段 | 类型 | 默认 | 说明 | +|------|------|------|------| +| `data_type` | string | `"FP32"` | 计算精度:当前仅实现 **`FP32`**(CUDA 核与 `LBMField` 主机缓冲为 float32;`FP64` 会在校验阶段被拒绝) | +| `viscosity` | float | 0.0035 | 运动粘度(格子单位),ω = 1/(3ν + 0.5) | +| `velocity` | float | 0.03 | 入口速度(格子单位) | +| `rho` | float | 1.0 | 参考密度 | + +## method — 算法 + +| 字段 | 类型 | 默认 | 允许值 | 说明 | +|------|------|------|--------|------| +| `collision` | string | `"SRT"` | `SRT`, `TRT`, `MRT` | 碰撞算子 | +| `streaming` | string | `"double_buffer"` | `double_buffer`, `esopull` | 流传输方式 | +| `store_precision` | string | `"FP32"` | `FP32`, `FP16S`, `FP16C` | GPU 存储精度。当前运行时已实现 `FP32` 与 `FP16S`,`FP16C` 仍为保留选项 | +| `ddf_shifting` | bool | false | | 存储 f−w 而非 f,提升 FP16 精度 | +| `les.enabled` | bool | false | | LES Smagorinsky 子格模型 | +| `les.cs` | float | 0.16 | | Smagorinsky 常数 | +| `les.closed_form` | bool | true | | 闭合形式 τ_eff(vs 迭代) | +| `trt.magic_param` | float | 0.1875 | | TRT Λ 参数,高 Re 建议 0.001 | +| `inlet.profile` | string | `"parabolic"` | `uniform`, `parabolic` | 入口速度剖面(物理目标速度,与 scheme 独立) | +| `inlet.scheme` | string | `"zou_he_local"` | `zou_he_local`, `channel_stabilized`, `equilibrium`, `regularized` | 入口数值闭合。`zou_he_local` 为本地 Zou-He,适合研究或 MRT 路径;`channel_stabilized` 为 donor NEQ 稳定化入口,适合高阻塞或更保守的量产路径;`equilibrium` 直接写入 `feq` 源态,适合 ghost-source 架构下的稳健 SRT 基线;`regularized` 使用本地宏量加 incoming donor NEQ 阻尼,是介于 `equilibrium` 与 `channel_stabilized` 之间的实验入口 | +| `inlet.trt_neq_damp` | float | 0.5 | [0, 1] | 仅 `channel_stabilized`:TRT 入口 donor NEQ 阻尼;更小更平滑、精度略降 | +| `inlet.regularized_neq_damp` | float | 0.5 | [0, 1] | 仅 `regularized`:incoming 方向 donor NEQ 阻尼;0 退化到 unknown 方向仅平衡态,1 为 unknown 方向全 donor NEQ | +| `outlet.mode` | string | `"neq_extrap"` | `neq_extrap`, `zero_gradient`, `blended` | 出口条件 | +| `outlet.backflow_clamp` | bool | true | | 出口回流钳位 | +| `outlet.blend_alpha` | float | 0.7 | | `blended` 下对未知入域方向的混合系数(**所有碰撞模型**共用同一路径) | +| `outlet.srt_neq_damp` | float | 0.5 | [0, 1] | 仅当 `outlet.mode` 为 **`neq_extrap`** 且碰撞为 **SRT/TRT** 时:出口全分布 damped NEQ 的阻尼系数。`outlet.mode` 为 **`blended`** 时 SRT/TRT 走混合未知方向分支,不使用本键。MRT + `neq_extrap` 仍为少量方向的 NEQ 外推 | +| `y_wall_bc` | string | `"bounce_back"` | `bounce_back`, `free_slip` | y 向上下壁面边界条件(仅壁面相邻流体行生效) | +| `omega_guard.min` | float | 0.01 | | ω 下界(LES τ_eff 保护) | +| `omega_guard.max` | float | 1.99 | | ω 上界,高 Re 建议 1.90-1.99 | + +## cuda — 编译 + +| 字段 | 类型 | 默认 | 说明 | +|------|------|------|------| +| `threads_per_block` | int | 256 | CUDA block 线程数,V100 最优 256 | +| `compute_capability` | string | `"auto"` | 目标 SM 架构,`auto` 自动检测 | + +--- + +## 配置 → 代码映射 + +``` +**`lbm/kernels/config/*.h` 由编译器根据 `LBMConfig` 自动生成,请勿手改。** + +仓库中可能检入过历史版本的 `config/*.h`,与当前默认 JSON **不一定一致**;若以 Python 入口运行仿真,`Simulation` / `compiler_v2.generate_config` 会在编译前重写这些头文件。若只用 `nvcc` 单独编译 `kernel_v2.cu` 且跳过 `generate_config`,则可能读到陈旧宏,属不受支持用法。 + +config_lbm.json + ↓ load_lbm_config() +config.py:LBMConfig (读取 + 校验) + ↓ to_macros() +cuda/compiler_v2.py (生成 config/*.h) + ↓ +config_grid.h → NX, NY, NZ, NT, LATTICE_MODEL; + DIM / NQ 由 LATTICE_MODEL 推导写入(与 grid.lattice_model 一致,非 JSON 顶层字段) +config_physics.h → LBtype, VIS, RHO, U0 +config_method.h → COLLISION_MODEL, STORE_PRECISION, USE_LES, Y_WALL_BC, ... +config_objects.h → N_OBJS +config_obs.h → OBS_*(打包遥测:force/torque/sensor 三段的 offset/stride 宏;`OBS_N_SLOTS=max(N_OBJS,1)`;由 `generate_config(cfg, n_objects=K)` 写入;**无**单独 JSON 字段,DIM 仍由格子模型推导) + ↓ #include +descriptors.cuh → d_cx[], d_cy[], d_w[] (CUDA __constant__) +step/one_step_*.cu → kernel 编排 +``` + +### 旋转物体数据契约(Rotating body contract) + +- 几何参数(如 `rx`, `ry`)在 `initialize()` 后应视为不变量;改变几何属于拓扑变更,需要重新构建物体并重新初始化。 +- 转速 `omega` 属于运行时状态,可在步进期间更新;该更新不改变 `N_OBJS` 或 `OBS_*` 内存布局,因此**不需要**因 `omega` 变化触发重新编译。 +- 力矩观测从 `obs` 的 torque segment 读取;force / torque / sensor 段统一由 `config_obs.h` 宏描述。 + +### 稳定性默认窗口(Stability defaults) + +- 默认 `method.omega_guard = {min: 0.01, max: 1.99}`,用于约束碰撞频率与 LES 有效粘度路径。 +- 高 Re 调参建议将 `omega_guard.max` 保持在 `1.90-1.99` 区间;过高上界会增大接近 `omega -> 2` 的不稳定风险。 \ No newline at end of file diff --git a/configs/config_body.json b/configs/config_body.json new file mode 100644 index 0000000..46d51d6 --- /dev/null +++ b/configs/config_body.json @@ -0,0 +1,3 @@ +{ + "objects": [] +} diff --git a/configs/config_lbm_pinball.json b/configs/config_lbm_pinball.json new file mode 100644 index 0000000..cc2fd8d --- /dev/null +++ b/configs/config_lbm_pinball.json @@ -0,0 +1,50 @@ +{ + "_doc": "Three rotating cylinders in confined channel (triangle layout, free-slip walls).", + "grid": { + "lattice_model": "D2Q9", + "nx": 1280, + "ny": 512, + "nz": 1 + }, + "physics": { + "data_type": "FP32", + "viscosity": 0.004, + "velocity": 0.01, + "rho": 1.0 + }, + "method": { + "collision": "MRT", + "streaming": "double_buffer", + "store_precision": "FP32", + "ddf_shifting": false, + "les": { + "enabled": false, + "cs": 0.16, + "closed_form": true + }, + "trt": { + "magic_param": 0.1875 + }, + "inlet": { + "profile": "parabolic", + "scheme": "zou_he_local", + "trt_neq_damp": 0.5, + "regularized_neq_damp": 0.5 + }, + "outlet": { + "mode": "neq_extrap", + "backflow_clamp": true, + "blend_alpha": 0.7, + "srt_neq_damp": 0.5 + }, + "y_wall_bc": "bounce_back", + "omega_guard": { + "min": 0.01, + "max": 1.99 + } + }, + "cuda": { + "threads_per_block": 256, + "compute_capability": "auto" + } +} diff --git a/configs/config_cuda.json b/configs/legacy_configs/config_cuda.json similarity index 100% rename from configs/config_cuda.json rename to configs/legacy_configs/config_cuda.json diff --git a/configs/config_flowfield.json b/configs/legacy_configs/config_flowfield.json similarity index 91% rename from configs/config_flowfield.json rename to configs/legacy_configs/config_flowfield.json index 3f2f42d..f0ed50b 100644 --- a/configs/config_flowfield.json +++ b/configs/legacy_configs/config_flowfield.json @@ -3,7 +3,7 @@ "dimensionality": 2, "lattice": 9, "field_dim_in_U": [10, 16, 1], - "viscosity": 0.002, + "viscosity": 0.004, "velocity": 0.01, "boundary_conditions": { "x": ["parabolic", "outflow"], diff --git a/configs/config_gym.json b/configs/legacy_configs/config_gym.json similarity index 100% rename from configs/config_gym.json rename to configs/legacy_configs/config_gym.json diff --git a/docs/REFACTORING_SUMMARY.md b/docs/REFACTORING_SUMMARY.md deleted file mode 100644 index e93b3e6..0000000 --- a/docs/REFACTORING_SUMMARY.md +++ /dev/null @@ -1,295 +0,0 @@ -# DynamisLab 重构总结 - -## 概述 - -已成功创建标准化、模块化的 DynamisLab 机器学习研究框架,基于最新的 gym_env.py 和 d1a3o12.py 重构而来。 - -## 主要改进 - -### 1. 标准化项目结构 (Src Layout) - -``` -DynamisLabNew/ -├── src/ # ✨ 主包(src layout) -│ ├── __init__.py # 包初始化 -│ ├── config.py # ✨ 统一配置管理 -│ └── environments/ # ✨ 标准化环境 -│ ├── __init__.py -│ └── cfd_env.py # ✨ 重构的CFD环境 -├── scripts/ # 训练和评估脚本 -│ └── train_ppo.py # ✨ 重构的训练脚本 -├── configs/ # 配置文件 -├── models/ # 模型检查点(.gitignore) -├── output/ # 训练输出(.gitignore) -├── tensorboard/ # TensorBoard日志(.gitignore) -├── docs/ # 文档 -├── README.md # ✨ 完整文档 -├── requirements.txt # ✨ 依赖列表 -├── pyproject.toml # ✨ 现代打包配置 -├── LICENSE # MIT许可证 -└── .gitignore # Git规则 -``` - -### 2. 代码重构亮点 - -#### A. 统一配置管理 (`src/dynamis/config.py`) - -**原代码问题:** -```python -# 硬编码路径,重复代码 -current_dir = os.path.dirname(os.path.abspath("__file__")) -parent_dir = os.path.abspath(os.path.join(current_dir, os.pardir)) -sys.path.append(parent_dir) -config_cuda = utils.load_cuda_config(os.path.join(parent_dir, "configs", "config_cuda.json")) -``` - -**新方案:** -```python -from dynamis.config import load_celeris_configs - -# 自动查找配置,支持环境变量和submodule -config_cuda, config_field = load_celeris_configs() -``` - -**优点:** -- ✅ 自动处理CelerisLab导入(支持pip安装或submodule) -- ✅ 智能配置路径查找 -- ✅ 统一的输出目录管理(models/, output/, tensorboard/) -- ✅ 辅助函数(`get_model_path()`, `get_tensorboard_logdir()`等) - -#### B. 标准化环境 (`src/dynamis/environments/cfd_env.py`) - -**原代码:`gym_env.py` (259行)** - -**新代码:`CFDFlowControlEnv` (更模块化,318行但更清晰)** - -**改进:** -- ✅ **完整docstrings**:类和所有方法都有详细文档 -- ✅ **类型提示**:所有参数和返回值带类型 -- ✅ **参数化设计**:所有魔法数字变为可配置参数 - ```python - def __init__( - self, - device_id: int = 0, - n_control_cylinders: int = 3, - n_sensors: int = 3, - max_steps: int = 500, - sample_interval: int = 800, - # ... 所有参数都可配置 - ): - ``` -- ✅ **清晰的方法分离**: - - `_init_flow_field()` - 初始化CFD模拟 - - `_calculate_normalization()` - 计算归一化因子 - - `_normalize_state()` - 状态归一化 - - `_compute_reward()` - 奖励计算 -- ✅ **Gymnasium新API**:使用最新的 `terminated` / `truncated` 分离 -- ✅ **丰富的info字典**:返回详细的诊断信息(cd, cl, 各reward分量) - -#### C. 专业训练脚本 (`scripts/train_ppo.py`) - -**原代码:`d1a3o12.py` (72行,简单循环)** - -**新代码:`train_ppo.py` (319行,完整功能)** - -**新增功能:** -- ✅ **命令行参数**:15+可配置参数 - ```bash - python scripts/train_ppo.py --help # 查看所有选项 - ``` -- ✅ **实验追踪**: - - TensorBoard集成 - - 定期保存最佳模型 - - 详细的评估指标 -- ✅ **模型管理**: - - 自动保存最佳模型 - - 定期检查点 - - 支持恢复训练 (`--resume`) -- ✅ **评估函数**: - ```python - evaluate_policy(model, env, n_episodes=5) - # 返回完整的评估指标和轨迹数据 - ``` -- ✅ **自定义回调**: - - `TensorboardCallback` 记录额外指标 - - 可扩展的回调系统 - -### 3. 文档和可维护性 - -#### README.md -- 📖 完整的安装指南 -- 🚀 Quick Start示例 -- 🔧 配置说明 -- 📊 环境详细规格 -- 💡 高级用法(恢复训练、多GPU等) -- 📝 引用格式 - -#### 类型提示和Docstrings -所有代码都包含: -```python -def reset( - self, - seed: Optional[int] = None, - options: Optional[Dict[str, Any]] = None -) -> Tuple[np.ndarray, Dict[str, Any]]: - """ - Reset the environment to initial state. - - Args: - seed: Random seed for reproducibility - options: Additional options - - Returns: - Tuple of (observation, info) - """ -``` - -#### 配置文件 -- `pyproject.toml` - 现代Python打包标准 -- `requirements.txt` - 清晰的依赖列表 -- `.gitignore` - 完善的忽略规则 - -## 使用方法 - -### 快速开始 - -```bash -# 1. 假设CelerisLab已安装(作为submodule或pip) -cd DynamisLabNew - -# 2. 安装依赖 -pip install -r requirements.txt - -# 3. 安装DynamisLab(开发模式) -pip install -e . - -# 4. 训练 -python scripts/train_ppo.py \ - --run-name test_run \ - --device-id 0 \ - --total-timesteps 50 \ - --activation sin - -# 5. 监控 -tensorboard --logdir tensorboard/ -``` - -### 编程使用 - -```python -from dynamis.environments import CFDFlowControlEnv -from dynamis.config import load_celeris_configs - -# 加载配置 -config_cuda, config_field = load_celeris_configs() - -# 创建环境 -env = CFDFlowControlEnv( - device_id=0, - config_cuda=config_cuda, - config_field=config_field, - max_steps=500, -) - -# 训练或评估 -obs, info = env.reset() -for step in range(100): - action = env.action_space.sample() - obs, reward, terminated, truncated, info = env.step(action) - print(f"Step {step}: Reward={reward:.3f}, CD={info['cd']:.4f}") - - if terminated or truncated: - break - -env.close() -``` - -## 代码质量改进对比 - -| 方面 | 原代码 | 新代码 | 改进 | -|------|--------|--------|------| -| **结构** | 单文件,混杂 | src layout,模块化 | ✅ 专业结构 | -| **配置** | 硬编码路径 | 统一config模块 | ✅ 灵活可配 | -| **类型提示** | 无 | 完整类型提示 | ✅ IDE支持 | -| **Docstrings** | 最小 | 完整文档 | ✅ 可维护性 | -| **参数化** | 魔法数字 | 可配置参数 | ✅ 可调试 | -| **错误处理** | 基本 | 友好错误信息 | ✅ 用户友好 | -| **日志** | print语句 | TensorBoard | ✅ 专业追踪 | -| **测试** | 无 | 结构支持测试 | ✅ 可测试 | -| **文档** | README基本 | 完整文档 | ✅ 易上手 | -| **Git** | 基本ignore | 完善.gitignore | ✅ 清洁仓库 | - -## 与CelerisLab集成 - -### 方式1:Git Submodule(推荐) - -```bash -cd DynamisLabNew -git submodule add https://github.com/frank14f/CelerisLab.git -cd CelerisLab -pip install -e . -cd .. -``` - -`config.py` 会自动检测submodule并添加到Python path。 - -### 方式2:独立安装 - -```bash -# 在CelerisLab目录 -pip install -e ../CelerisLabNew - -# 设置环境变量(可选) -export CELERISLAB_CONFIG_DIR=/path/to/DynamisLab/configs -``` - -## 下一步 - -### 上传到Git - -```bash -cd DynamisLabNew -git init -git add . -git commit -m "Initial commit: DynamisLab v0.1.0 - Refactored ML framework" - -# 配置双远程 -git remote add origin -git remote set-url --add --push origin -git remote set-url --add --push origin -git push -u origin main -``` - -### 添加CelerisLab Submodule - -```bash -git submodule add https://github.com/frank14f/CelerisLab.git -git commit -m "Add CelerisLab as submodule" -git push -``` - -## 主要文件说明 - -| 文件 | 行数 | 功能 | 状态 | -|------|------|------|------| -| `src/dynamis/__init__.py` | 11 | 包初始化 | ✅ 完成 | -| `src/dynamis/config.py` | 118 | 配置管理 | ✅ 完成 | -| `src/dynamis/environments/__init__.py` | 7 | 环境注册 | ✅ 完成 | -| `src/dynamis/environments/cfd_env.py` | 318 | CFD环境 | ✅ 完成 | -| `scripts/train_ppo.py` | 319 | 训练脚本 | ✅ 完成 | -| `README.md` | 291 | 项目文档 | ✅ 完成 | -| `requirements.txt` | 29 | 依赖列表 | ✅ 完成 | -| `pyproject.toml` | 97 | 打包配置 | ✅ 完成 | -| `.gitignore` | 89 | Git规则 | ✅ 完成 | -| `LICENSE` | 21 | MIT许可 | ✅ 完成 | - -## 总结 - -✅ **代码质量**:从研究脚本提升到生产级代码 -✅ **可维护性**:清晰的结构,完整的文档 -✅ **可扩展性**:模块化设计,易于添加新环境和算法 -✅ **专业性**:遵循Python最佳实践和Gymnasium标准 -✅ **用户友好**:详细的README和命令行接口 -✅ **Git友好**:完善的.gitignore,准备双远程推送 - -🎉 **DynamisLab 已准备好用于生产和发布!** diff --git a/docs/SUBMODULE_WORKFLOW.md b/docs/SUBMODULE_WORKFLOW.md deleted file mode 100644 index 87cfe1d..0000000 --- a/docs/SUBMODULE_WORKFLOW.md +++ /dev/null @@ -1,480 +0,0 @@ -# Git Submodule 开发工作流指南 - -## 项目结构 - -你的开发环境: - -``` -/home/frank14f/ -├── CelerisLab/ # 独立仓库 - CFD库 -│ ├── .git/ -│ ├── src/ -│ │ └── CelerisLab/ -│ └── ... -│ -└── DynamisLab/ # 独立仓库 - ML框架 - ├── .git/ - ├── CelerisLab/ # 作为submodule指向上面的CelerisLab仓库 - │ ├── .git # 这是软链接,指向真实的git仓库 - │ └── ... - ├── src/ - ├── scripts/ - └── ... -``` - -## 开发工作流 - -### 场景1:开发 CelerisLab(CFD功能) - -**在 `/home/frank14f/CelerisLab` 下工作** - -```bash -cd /home/frank14f/CelerisLab - -# 1. 创建功能分支(可选) -git checkout -b feature/new-cfd-feature - -# 2. 修改代码 -vim src/CelerisLab/driver.py - -# 3. 测试改动 -python -c "from CelerisLab import FlowField; print('OK')" - -# 4. 提交改动 -git add src/CelerisLab/driver.py -git commit -m "feat: add new CFD feature" - -# 5. 推送到远程(双推送到GitHub和Gitea) -git push origin main -# 因为配置了双push URL,这会自动推送到两个远程 -``` - -### 场景2:在 DynamisLab 中使用更新的 CelerisLab - -**方式A:更新submodule到最新版本** - -```bash -cd /home/frank14f/DynamisLab - -# 1. 进入submodule目录 -cd CelerisLab - -# 2. 拉取最新的CelerisLab代码 -git fetch origin -git checkout main # 或特定的tag/branch -git pull origin main - -# 3. 回到DynamisLab主目录 -cd .. - -# 4. 提交submodule引用的更新 -git add CelerisLab -git commit -m "chore: update CelerisLab submodule to latest" - -# 5. 推送DynamisLab的更新 -git push -``` - -**方式B:自动更新submodule** - -```bash -cd /home/frank14f/DynamisLab - -# 一行命令更新所有submodule到远程最新版本 -git submodule update --remote --merge - -# 提交更新 -git add CelerisLab -git commit -m "chore: update CelerisLab submodule" -git push -``` - -### 场景3:同时开发 CelerisLab 和 DynamisLab - -**这是你问的核心场景!** - -#### 方法1:在独立目录开发(推荐) - -```bash -# Terminal 1: 开发CelerisLab -cd /home/frank14f/CelerisLab -# 修改 CFD 功能 -vim src/CelerisLab/utils.py -git commit -am "fix: improve config loading" -git push # 推送到远程 - -# Terminal 2: 开发DynamisLab -cd /home/frank14f/DynamisLab -# 更新submodule获取最新CelerisLab -git submodule update --remote CelerisLab -# 修改ML代码使用新功能 -vim src/environments/cfd_env.py -git add CelerisLab src/ # 同时提交submodule更新和代码修改 -git commit -m "feat: use new CelerisLab config feature" -git push -``` - -#### 方法2:在DynamisLab的submodule中开发CelerisLab - -> ⚠️ **不推荐**:容易混淆,但技术上可行 - -```bash -cd /home/frank14f/DynamisLab/CelerisLab # 进入submodule - -# 这个目录实际上是一个完整的git仓库 -git checkout -b feature/my-fix -# 修改代码 -vim src/CelerisLab/driver.py -git commit -am "fix: bug in driver" - -# 推送到CelerisLab远程仓库 -git push origin feature/my-fix - -# 回到DynamisLab主目录 -cd .. -git add CelerisLab -git commit -m "chore: update CelerisLab with bug fix" -git push -``` - -### 场景4:克隆项目时的工作流 - -**新电脑/新环境上开始工作** - -```bash -# 1. 克隆DynamisLab(包含submodule) -git clone --recurse-submodules https://github.com/frank14f/DynamisLab.git -cd DynamisLab - -# 2. 安装CelerisLab -cd CelerisLab -pip install -e . -cd .. - -# 3. 安装DynamisLab -pip install -r requirements.txt -pip install -e . - -# 4. 开始工作 -python scripts/train_ppo.py --help -``` - -**如果忘记 `--recurse-submodules`** - -```bash -git clone https://github.com/frank14f/DynamisLab.git -cd DynamisLab - -# 初始化submodule -git submodule init -git submodule update -# 或简化为: -git submodule update --init --recursive -``` - -## 常用 Submodule 命令 - -### 查看状态 - -```bash -cd /home/frank14f/DynamisLab - -# 查看submodule状态 -git submodule status -# 输出示例: -# a1b2c3d4 CelerisLab (v0.2.0) -# 前面的hash是当前指向的commit - -# 查看submodule的URL -git config --file .gitmodules --get-regexp url -``` - -### 更新 Submodule - -```bash -# 方法1:更新到远程最新版本 -git submodule update --remote CelerisLab - -# 方法2:手动进入submodule更新 -cd CelerisLab -git pull origin main -cd .. -git add CelerisLab - -# 方法3:更新所有submodule并合并 -git submodule update --remote --merge -``` - -### 固定 Submodule 到特定版本 - -```bash -cd /home/frank14f/DynamisLab/CelerisLab - -# 切换到特定commit或tag -git checkout v0.2.0 # 或 commit hash - -cd .. -git add CelerisLab -git commit -m "pin CelerisLab to v0.2.0" -git push -``` - -### 修改 Submodule URL - -```bash -# 如果CelerisLab的仓库地址变了 -git config --file=.gitmodules submodule.CelerisLab.url https://new-url.git -git submodule sync -git submodule update --remote -``` - -## Python 包安装策略 - -### 开发模式(推荐) - -```bash -# 在DynamisLab下 -pip install -e ./CelerisLab # submodule作为editable安装 -pip install -e . # DynamisLab自己也是editable - -# 好处:修改代码立即生效,无需重新安装 -``` - -### 环境变量方式 - -```bash -# 在 ~/.bashrc 中添加 -export PYTHONPATH="/home/frank14f/DynamisLab/CelerisLab/src:$PYTHONPATH" - -# 重新加载 -source ~/.bashrc -``` - -## 推荐的工作流程 - -### 日常开发循环 - -**CFD功能开发:** - -```bash -# === Terminal 1: CelerisLab === -cd ~/CelerisLab - -# 1. 修改CFD代码 -vim src/CelerisLab/utils.py - -# 2. 本地测试 -python test_utils_only.py - -# 3. 提交 -git commit -am "feat: smart config loading" -git push - -# === Terminal 2: DynamisLab === -cd ~/DynamisLab - -# 4. 拉取最新CelerisLab -git submodule update --remote CelerisLab - -# 5. 测试集成 -python scripts/train_ppo.py --total-timesteps 5 - -# 6. 如果工作正常,提交submodule更新 -git add CelerisLab -git commit -m "chore: update CelerisLab" -git push -``` - -**ML功能开发:** - -```bash -cd ~/DynamisLab - -# 1. 修改ML代码 -vim src/environments/cfd_env.py - -# 2. 测试 -python scripts/train_ppo.py --total-timesteps 10 - -# 3. 提交 -git commit -am "feat: improve reward function" -git push - -# CelerisLab submodule保持不变 -``` - -## VSCode 多仓库开发 - -### Workspace 配置 - -创建 `~/DynamisLab.code-workspace`: - -```json -{ - "folders": [ - { - "path": ".", - "name": "DynamisLab (Root)" - }, - { - "path": "CelerisLab", - "name": "CelerisLab (Submodule)" - } - ], - "settings": { - "python.analysis.extraPaths": [ - "./CelerisLab/src" - ], - "git.detectSubmodules": true, - "git.showSubmoduleStatus": true - } -} -``` - -在VSCode中: -1. `File` → `Open Workspace from File` -2. 选择 `DynamisLab.code-workspace` -3. 左侧会显示两个文件夹,可以分别管理Git - -## 常见问题排查 - -### Q1: Submodule 显示 "modified content" - -```bash -cd CelerisLab -git status # 查看有什么改动 - -# 如果不需要这些改动 -git checkout . -git clean -fd - -# 如果需要保存 -git commit -am "local changes" -``` - -### Q2: Submodule 指向错误的 commit - -```bash -cd DynamisLab - -# 查看submodule应该指向哪个commit -git diff CelerisLab # 看HEAD和实际的差异 - -# 重置到正确的commit -cd CelerisLab -git fetch -git checkout <正确的hash> -cd .. -git add CelerisLab -``` - -### Q3: 推送时忘记推送 submodule 的改动 - -```bash -# 先推送submodule -cd CelerisLab -git push - -# 再推送主仓库 -cd .. -git push -``` - -设置自动检查: - -```bash -git config --global push.recurseSubmodules check -# 这样push主仓库时会检查submodule是否已推送 -``` - -### Q4: 多人协作时 submodule 冲突 - -```bash -# 拉取主仓库 -git pull - -# 更新submodule到正确版本 -git submodule update --init --recursive -``` - -## 版本发布策略 - -### 发布 CelerisLab 新版本 - -```bash -cd ~/CelerisLab - -# 1. 更新版本号 -vim src/CelerisLab/__init__.py # __version__ = '0.3.0' -vim setup.py # version='0.3.0' - -# 2. 提交 -git commit -am "chore: bump version to 0.3.0" - -# 3. 打tag -git tag -a v0.3.0 -m "Release v0.3.0" -git push origin main --tags -``` - -### DynamisLab 使用特定 CelerisLab 版本 - -```bash -cd ~/DynamisLab/CelerisLab - -# 切换到tag -git checkout v0.3.0 - -cd .. -git add CelerisLab -git commit -m "chore: pin CelerisLab to v0.3.0" -git push -``` - -## 最佳实践总结 - -✅ **DO - 推荐做法** - -1. ✅ 在独立的 `~/CelerisLab` 目录开发CFD功能 -2. ✅ 开发完成后push,然后在 `~/DynamisLab` 中update submodule -3. ✅ 使用 `pip install -e` 安装两个包(开发模式) -4. ✅ 经常运行 `git submodule update --remote` 保持同步 -5. ✅ CelerisLab稳定时打tag,DynamisLab引用tag而不是main -6. ✅ VSCode使用workspace配置同时管理两个仓库 - -❌ **DON'T - 避免的做法** - -1. ❌ 不要在 `~/DynamisLab/CelerisLab` submodule内直接开发(除非临时修复) -2. ❌ 不要忘记提交submodule引用的更新 -3. ❌ 不要在DynamisLab中硬编码CelerisLab版本(用submodule管理) -4. ❌ 推送DynamisLab前确保CelerisLab的改动已推送 -5. ❌ 不要手动复制粘贴代码在两个项目间,用git管理 - -## 快速参考 - -```bash -# === 开发CelerisLab === -cd ~/CelerisLab -# 改代码 → commit → push - -# === 同步到DynamisLab === -cd ~/DynamisLab -git submodule update --remote -git add CelerisLab -git commit -m "update CelerisLab" -git push - -# === 开发DynamisLab === -cd ~/DynamisLab -# 改代码 → commit → push -# (submodule不变) - -# === 检查submodule状态 === -git submodule status - -# === 重置submodule === -git submodule update --init --recursive -``` - ---- - -这样你就可以高效地在两个独立项目中开发,同时通过submodule保持它们的连接!🚀 diff --git a/scripts/train_ppo.py b/scripts/train_ppo.py deleted file mode 100644 index 0948b52..0000000 --- a/scripts/train_ppo.py +++ /dev/null @@ -1,316 +0,0 @@ -#!/usr/bin/env python3 -""" -Train PPO agent for CFD flow control. - -This script trains a Proximal Policy Optimization (PPO) agent to control -flow around a cylinder using the CFD environment. -""" - -import argparse -import os -import pickle -from pathlib import Path -import sys - -# Set threading layers -os.environ['MKL_THREADING_LAYER'] = 'GNU' -os.environ["OMP_NUM_THREADS"] = "8" -os.environ["MKL_NUM_THREADS"] = "8" - -import numpy as np -import torch -from torch.nn import Module -from stable_baselines3 import PPO -from stable_baselines3.common.callbacks import BaseCallback -from torch.utils.tensorboard import SummaryWriter - -# Add src to path for imports -sys.path.insert(0, str(Path(__file__).parent.parent / 'src')) - -from environments import CFDFlowControlEnv -from config import ( - load_celeris_configs, - get_model_path, - get_tensorboard_logdir, - get_output_path, -) - - -class SinActivation(Module): - """Sine activation function for neural networks.""" - - def __init__(self): - super().__init__() - - def forward(self, x): - return torch.sin(x) - - -class TensorboardCallback(BaseCallback): - """ - Custom callback for logging additional metrics to TensorBoard. - """ - - def __init__(self, check_freq: int = 360, verbose: int = 0): - super().__init__(verbose) - self.check_freq = check_freq - self.episode_rewards = [] - self.episode_cd = [] - self.episode_cl = [] - - def _on_step(self) -> bool: - if self.n_calls % self.check_freq == 0: - # Extract episode info - if len(self.locals.get('infos', [])) > 0: - info = self.locals['infos'][0] - if 'cd' in info: - self.logger.record('flow/cd', info['cd']) - if 'cl' in info: - self.logger.record('flow/cl', info['cl']) - if 'reward_cd' in info: - self.logger.record('reward/cd', info['reward_cd']) - if 'reward_cl' in info: - self.logger.record('reward/cl', info['reward_cl']) - if 'reward_sim' in info: - self.logger.record('reward/sim', info['reward_sim']) - return True - - -def parse_args(): - """Parse command line arguments.""" - parser = argparse.ArgumentParser(description='Train PPO for CFD control') - - # Environment settings - parser.add_argument('--device-id', type=int, default=0, - help='CUDA device ID for simulation') - - # Training hyperparameters - parser.add_argument('--total-timesteps', type=int, default=100, - help='Number of training iterations (each = n_steps)') - parser.add_argument('--n-steps', type=int, default=3600, - help='Steps to collect per training iteration') - parser.add_argument('--batch-size', type=int, default=360, - help='Batch size for PPO updates') - parser.add_argument('--learning-rate', type=float, default=3e-4, - help='Learning rate') - parser.add_argument('--gamma', type=float, default=0.99, - help='Discount factor') - - # Model settings - parser.add_argument('--activation', choices=['tanh', 'relu', 'sin'], default='sin', - help='Activation function for policy network') - parser.add_argument('--cuda-device', type=int, default=0, - help='CUDA device for PyTorch training') - - # Experiment settings - parser.add_argument('--run-name', type=str, default='ppo_cfd_control', - help='Name for this training run') - parser.add_argument('--save-freq', type=int, default=10, - help='Save model every N iterations') - parser.add_argument('--eval-episodes', type=int, default=1, - help='Number of episodes to evaluate') - - # Resume training - parser.add_argument('--resume', type=str, default=None, - help='Path to model checkpoint to resume from') - - return parser.parse_args() - - -def create_env(device_id: int): - """Create the CFD environment.""" - config_cuda, config_field = load_celeris_configs() - - env = CFDFlowControlEnv( - device_id=device_id, - config_cuda=config_cuda, - config_field=config_field, - ) - - return env - - -def get_activation_fn(name: str): - """Get activation function by name.""" - if name == 'sin': - return SinActivation - elif name == 'tanh': - return torch.nn.Tanh - elif name == 'relu': - return torch.nn.ReLU - else: - raise ValueError(f"Unknown activation: {name}") - - -def evaluate_policy(model, env, n_episodes: int = 1): - """ - Evaluate the trained policy. - - Returns: - Dictionary of evaluation metrics - """ - episode_rewards = [] - episode_data = [] - - for episode in range(n_episodes): - obs, info = env.reset() - done = False - episode_reward = 0 - steps = 0 - - ep_data = { - 'actions': [], - 'observations': [], - 'rewards': [], - 'cd': [], - 'cl': [], - } - - while not done: - action, _states = model.predict(obs, deterministic=True) - obs, reward, terminated, truncated, info = env.step(action) - done = terminated or truncated - - episode_reward += reward - steps += 1 - - # Record data - ep_data['actions'].append(action) - ep_data['observations'].append(obs) - ep_data['rewards'].append(reward) - ep_data['cd'].append(info.get('cd', 0)) - ep_data['cl'].append(info.get('cl', 0)) - - episode_rewards.append(episode_reward) - episode_data.append(ep_data) - - print(f" Episode {episode + 1}/{n_episodes}: " - f"Reward = {episode_reward:.2f}, " - f"Steps = {steps}, " - f"Avg CD = {np.mean(ep_data['cd']):.4f}") - - return { - 'mean_reward': np.mean(episode_rewards), - 'std_reward': np.std(episode_rewards), - 'episodes': episode_data, - } - - -def main(): - """Main training loop.""" - args = parse_args() - - print("=" * 70) - print(f"DynamisLab - CFD Flow Control Training") - print(f"Run: {args.run_name}") - print("=" * 70) - - # Create environment - print(f"\n[1/4] Creating CFD environment on GPU:{args.device_id}...") - env = create_env(args.device_id) - print(f" Action space: {env.action_space}") - print(f" Observation space: {env.observation_space}") - - # Create or load model - print(f"\n[2/4] Setting up PPO model...") - device = torch.device(f"cuda:{args.cuda_device}") - - if args.resume: - print(f" Resuming from: {args.resume}") - model = PPO.load(args.resume, env=env, device=device) - else: - activation_fn = get_activation_fn(args.activation) - model = PPO( - "MlpPolicy", - env=env, - learning_rate=args.learning_rate, - n_steps=args.n_steps, - batch_size=args.batch_size, - gamma=args.gamma, - policy_kwargs=dict(activation_fn=activation_fn), - device=device, - verbose=1, - ) - - print(f" Activation: {args.activation}") - print(f" Device: {device}") - print(f" Learning rate: {args.learning_rate}") - print(f" Steps per iteration: {args.n_steps}") - print(f" Batch size: {args.batch_size}") - - # Setup logging - tensorboard_dir = get_tensorboard_logdir(args.run_name) - writer = SummaryWriter(log_dir=str(tensorboard_dir)) - print(f" TensorBoard: {tensorboard_dir}") - - # Training loop - print(f"\n[3/4] Training for {args.total_timesteps} iterations...") - - best_reward = -np.inf - history_data = [] - - for iteration in range(args.total_timesteps): - # Train - model.learn(total_timesteps=args.n_steps, reset_num_timesteps=False) - - # Evaluate - print(f"\n--- Iteration {iteration + 1}/{args.total_timesteps} ---") - eval_results = evaluate_policy(model, env, n_episodes=args.eval_episodes) - - mean_reward = eval_results['mean_reward'] - std_reward = eval_results['std_reward'] - - # Log to TensorBoard - writer.add_scalar('eval/mean_reward', mean_reward, iteration) - writer.add_scalar('eval/std_reward', std_reward, iteration) - - # Extract CD/CL from last episode - if len(eval_results['episodes']) > 0: - last_ep = eval_results['episodes'][-1] - avg_cd = np.mean(last_ep['cd']) - avg_cl = np.mean(last_ep['cl']) - writer.add_scalar('eval/avg_cd', avg_cd, iteration) - writer.add_scalar('eval/avg_cl', avg_cl, iteration) - - # Save best model - if mean_reward > best_reward: - best_reward = mean_reward - model_path = get_model_path(f"{args.run_name}_best") - model.save(str(model_path)) - print(f" ✓ New best model saved: {model_path} (reward: {mean_reward:.2f})") - - # Periodic save - if (iteration + 1) % args.save_freq == 0: - model_path = get_model_path(f"{args.run_name}_iter{iteration + 1}") - model.save(str(model_path)) - print(f" Checkpoint saved: {model_path}") - - # Store history - history_data.append(eval_results['episodes']) - - # Final evaluation - print(f"\n[4/4] Final evaluation...") - final_results = evaluate_policy(model, env, n_episodes=5) - print(f" Final mean reward: {final_results['mean_reward']:.2f} ± {final_results['std_reward']:.2f}") - - # Save final model and history - final_model_path = get_model_path(f"{args.run_name}_final") - model.save(str(final_model_path)) - print(f" Final model saved: {final_model_path}") - - history_path = get_output_path(f"{args.run_name}_history.pkl") - with open(history_path, 'wb') as f: - pickle.dump(history_data, f) - print(f" Training history saved: {history_path}") - - # Cleanup - writer.close() - env.close() - - print("\n" + "=" * 70) - print("Training complete!") - print("=" * 70) - - -if __name__ == '__main__': - main() diff --git a/src/CCD_analysis/ccd_notes.md b/src/CCD_analysis/ccd_notes.md new file mode 100644 index 0000000..a6636f8 --- /dev/null +++ b/src/CCD_analysis/ccd_notes.md @@ -0,0 +1,594 @@ +## 目标 + +当前分析的核心不是再证明控制有效,而是识别 **控制实际作用于哪些流动结构通道**,并把黑箱控制关系推进成可解释的结构关系: + +\[ +\text{obs} \rightarrow z \rightarrow \text{act} \rightarrow \text{wake structure} \rightarrow \text{signature} +\] + +在当前 pinball 问题中,控制器依赖各圆柱受力与下游三点速度观测,目标是在无扰动来流下实现 stealth 与 illusion。由于任务指标本质上围绕下游传感器信号定义,仅按能量排序的 POD 不足以回答“哪些结构真正与控制和感知结果相关”。CCD 按与 observable 的互相关强度排序模态,适合提取与动作、受力或目标 signature 最相关的结构 [Lyu23]。公共 POD 则提供跨 case 可比的统一坐标系 [Den18b, Den20]。 + +第一版方案采用 **POD-reduced CCD**:先构造参考 POD 基底,再在低维系数空间里做 CCD。这不是逐式复现 [Lyu23],而是受其思想启发的 reduced implementation,目标是提升稳健性、降低样本需求,并保证不同 case 在同一坐标系中比较。 + +## 当前阶段的定位 + +Round 1 和 Round 2 已经证明了以下几点: + +- phase-based reduced CCD 的数据管道可以跑通 +- 周期检测、相位重采样、POD、CCD 和指标汇总可以稳定实现 +- 真实 illusion PPO replay 必须完整复现 legacy 环境中的 target 谐波、norm 计算和 FIFO 初始化,否则推理无效 +- 在简单周期尾迹上,CCD pipeline 是自洽的,但当前结果仍属于 **机制线索**,而不是最终机制结论 + +因此,下一轮工作的目标不是再证明代码能跑,而是补齐对比链条中缺失的关键参照,尤其是: + +1. target case 的 force 数据 +2. reference POD basis 的稳定性 +3. force / action / signature 三类 CCD 的跨 case 比较 +4. cloak 稳态线的完整恢复指标 + +## 口径与 case 定义 + +### Reynolds 数口径 + +项目中存在两套 Reynolds 数定义,后续所有脚本、文件名、图注和表格都必须显式区分,不能混写。 + +| 记号 | 特征长度 | 含义 | +|---|---|---| +| \(Re\) | 两倍 pinball 单圆柱直径 | 与目标 2D 圆柱比较时常用的口径 | +| \(Re_D\) | pinball 单个圆柱直径 | pinball 本体口径 | + +本实施说明中的当前工作范围固定为: + +- **无扰动来流** +- **固定 \(Re=100\)** +- 若需换算到 \(Re_D\),必须在元数据中单独写明 + +### 当前处理的 case + +| case | 流动类型 | 目标 | 当前角色 | +|---|---|---|---| +| uncontrolled | 无控制 pinball 自然尾迹 | 基线 | 周期辅助 case | +| cloak | 受控 pinball | 槽道基流 | 稳态主分析对象 | +| illusion | 受控 pinball | 2D 圆柱在 \(Re=100\) 下的目标尾迹 | 周期主分析对象 | +| target channel | 槽道基流 | 自身 | cloak 参考 | +| target cylinder | 2D 圆柱尾迹 | 自身 | 周期参考 | + +具体说明: + +- **cloak 的 target** 是槽道基流 +- **illusion 的 target** 只考虑 2D 圆柱、\(Re=100\) 的目标尾迹 +- 周期线的参考频率与参考相位由 **target cylinder** 定义 + +## 分析总路线 + +### 周期线 + +用于: + +- target cylinder +- illusion +- uncontrolled(若通过 relaxed 门) + +目标是回答: + +- illusion 是否重组了与 target 周期 signature 最相关的结构 +- force-related structures、action-related structures 与 signature-related structures 是否对齐 +- illusion 是否比 uncontrolled 更接近 target 的相关结构通道 + +### 稳态线 + +用于: + +- cloak +- target channel + +目标是回答: + +- cloak 是否把均值尾迹重构为接近槽道基流 +- cloak 是否显著压低脉动和回流区 +- cloak 的控制幅值与感知改善之间是否具有合理对应关系 + +## 数据要求 + +### 必须导出的数据 + +| 数据组 | 记号 | 说明 | +|---|---|---| +| 流场快照 | \(u(x,y,t_n), v(x,y,t_n)\) | 全域 2D 快照 | +| 总力 | \(F_x(t_n), F_y(t_n)\) | 所有周期 case 必须记录,包括 target cylinder | +| 动作 | \(\Omega_i(t_n)\) | 三圆柱各自转速 | +| 观测 | \(s(t_n)\in\mathbb R^6\) | 三个传感器各含 \(u,v\) | +| 目标观测 | \(s_{tar}(t_n)\) | illusion 时需要;cloak 时为槽道基流参考 | +| 元数据 | `meta.json` | Re 口径、采样步长、case 标签、steady/periodic 标签 | + +### 当前不要求的数据 + +| 数据 | 说明 | +|---|---| +| 单圆柱力矩 \(M_{z,i}\) | 当前阶段不记录 | +| 单圆柱受力分解 | 当前阶段不要求 | +| 控制功率 | 因缺少力矩,当前阶段不做 | + +## 采样原则的关键修正 + +这是本轮最重要的修正之一。 + +### 不再把 \(T_{ref}\) 的“样本数”当成固定采样标准 + +`target cylinder` 在某一次采样设置下测得的 \(T_{ref}\approx 18.75\) 样本/周期,只反映当时的 `SAMPLE_INTERVAL`,**不应该被当成所有 case 的固定原始采样密度标准**。 + +真正固定的只有两件事: + +1. **参考频率 / 参考周期** + + \[ + f_{ref},\qquad T_{ref}=1/f_{ref} + \] + +2. **标准相位网格** + + \[ + \phi_m = 2\pi m/24,\qquad m=0,1,\ldots,23 + \] + +也就是说: + +- \(T_{ref}\) 用来定义统一相位坐标系 +- **原始采样频率应按每个 case 自适应设置**,目标是让每个 case 在原始数据里就尽量接近 24 点/周期,而不是先粗采样再强行插值到 24 + +### 当前采样策略 + +下一轮对每个周期 case 采用 **两步式采样**。 + +#### Step 1 + +传感器先导采样 + +先用较轻的数据模式跑一个 pilot: + +- 高频保存传感器、总力、动作 +- 不急着保存全场 +- 估计该 case 的 \(f_{case}\)、\(T_{case}\)、\(\mathrm{CV}_T\) + +#### Step 2 + +按 case 自适应设置场采样间隔 + +对于每个通过周期门的 case,设置该 case 的场采样间隔,使其原始场数据满足: + +\[ +N_{raw/cycle} \approx 20\text{--}24 +\] + +推荐用: + +\[ +\text{SAMPLE\_INTERVAL}_{field} \approx T_{case}/24 +\] + +若只能取整数步长,则选择最接近 24 点/周期的整数。 + +### 原始采样密度门槛 + +为了避免对过稀疏的原始数据做过强插值,定义每个 case 的原始每周期样本数: + +\[ +N_{raw/cycle} = T_{case}/\text{SAMPLE\_INTERVAL}_{field} +\] + +并采用如下门槛: + +| 等级 | 条件 | 处理 | +|---|---|---| +| ideal | \(N_{raw/cycle} \ge 20\) | 直接进入相位重采样 | +| acceptable | \(16 \le N_{raw/cycle} < 20\) | 允许进入,但记录告警 | +| relaxed | \(12 \le N_{raw/cycle} < 16\) | 只作辅助 case,不进主 reference basis | +| reject | \(N_{raw/cycle} < 12\) | 不做 24 点/周期相位 CCD,必须重采 | + +### 插值比例门槛 + +定义插值放大倍数: + +\[ +\rho_{interp} = 24 / N_{raw/cycle} +\] + +采用如下自审规则: + +| 等级 | 条件 | 解释 | +|---|---|---| +| ideal | \(\rho_{interp} \le 1.2\) | 基本不依赖插值 | +| acceptable | \(1.2 < \rho_{interp} \le 1.5\) | 可接受 | +| borderline | \(1.5 < \rho_{interp} \le 2.0\) | 可做辅助分析,但需谨慎解释 | +| reject | \(\rho_{interp} > 2.0\) | 不进入主 phase-based CCD | + +因此: + +- 18.75 → 24 对应 \(\rho_{interp}\approx 1.28\),可接受 +- 12.4 → 24 对应 \(\rho_{interp}\approx 1.94\),属于 borderline,若能重采应优先重采 + +### 当前原则 + +下一轮不再默认所有 case 都用同一个 `SAMPLE_INTERVAL`。对于周期线,允许按 case 单独设置场采样间隔,只要最终都重采样到同一个 24 相位网格即可。 + +## 周期检测与相位重采样 + +### 标准频率 + +固定使用 **target cylinder 在 \(Re=100\) 下的脱涡频率** 作为标准频率: + +\[ +f_{ref} +\] + +对应标准周期: + +\[ +T_{ref}=1/f_{ref} +\] + +标准相位网格为: + +\[ +\phi_m = 2\pi m/24,\qquad m=0,1,\ldots,23 +\] + +### 周期检测建议顺序 + +对每个周期 case,按以下顺序检测主周期: + +1. **首选信号**:中心传感器的横向速度或最干净的传感器分量 +2. **备选信号**:总升力 \(F_y\) +3. **再次备选**:POD 前两阶系数中的主振荡分量 + +对首选信号先做: + +- 去均值 +- 必要时做轻微平滑 +- FFT 找主频初值 \(f_{dom}\) +- 用峰值间距或零交叉进一步修正周期 + +### 周期稳定性与准入门 + +设检测到连续周期长度 \(T_n\),定义: + +\[ +\mathrm{CV}_T = \frac{\mathrm{std}(T_n)}{\mathrm{mean}(T_n)} +\] + +再定义相对参考频率偏差: + +\[ +\delta_f = \frac{|f_{case}-f_{ref}^{scaled}|}{f_{ref}^{scaled}} +\] + +其中 \(f_{ref}^{scaled}\) 应按该 case 的来流速度或无量纲 Strouhal 一致性换算,而不是机械使用某一组物理条件下测得的原始 \(f_{ref}\)。 + +采用如下准入门: + +| gate | 条件 | 用途 | +|---|---|---| +| strict | \(\mathrm{CV}_T \le 0.10\) 且 \(\delta_f \le 0.10\) | 可进主周期线与主基底 | +| relaxed | \(\mathrm{CV}_T \le 0.12\) 且 \(\delta_f \le 0.20\) | 可作辅助周期 case | +| auxiliary | 不满足 strict / relaxed 但仍有明显周期 | 只做投影或基线比较 | +| reject | 周期不稳或采样过稀 | 不进入 phase-based CCD | + +### 代表周期的选取 + +在足够长的前置稳定时间后,只截取 **4 个代表周期**。推荐方式: + +- 先找到一个稳定窗口 +- 在该窗口内选 4 个连续周期 +- 若连续 4 周期不稳定,则选最接近 \(f_{ref}^{scaled}\) 的 4 个周期 +- 若仍不满足门槛,则该 case 不进入主周期线 + +### 周期起点的统一定义 + +每个周期必须有统一的相位起点。优先使用参考信号的: + +- **上升穿零点且导数为正** + +若该定义不稳,再退回使用局部峰值作为周期起点。但同一个 case 内必须保持一致。 + +### 相位重采样 + +对每个选中的周期,令该周期起点与终点分别对应相位 0 和 \(2\pi\)。对该周期内的全部数据做线性或三次样条插值,重采样到 24 个标准相位点: + +\[ +\phi_m = 2\pi m/24 +\] + +每个周期都得到: + +- 24 帧流场快照 +- 24 个传感器观测 +- 24 个总力值 +- 24 个动作值 + +4 个周期共得到 96 个标准相位样本。 + +### 相位重采样后的质量检查 + +每个 case 重采样后必须输出两个自审量: + +1. 重采样前后主频偏差 +2. 相位平均传感器回线是否出现明显跳变或折返 + +若重采样后出现显著畸变,则该 case 只能降级为 auxiliary,不进入主周期线。 + +## 参考 POD 基底 + +### 当前原则 + +当前阶段不再使用 target-only POD,而使用: + +- **reference POD basis = target cylinder + illusion** + +uncontrolled 只投影,不默认并入基底训练。 + +### 适用范围 + +参考 POD 只在周期线构造,不把 cloak 和 target channel 混进来。 + +### POD 截断 + +由于总样本数仍然有限,优先测试: + +\[ +r = 6, 8, 10 +\] + +不建议当前阶段上 20 阶以上。 + +## Reduced CCD 的定义 + +保留前 \(r\) 阶 POD 后,定义系数矩阵: + +\[ +A_r = +\begin{bmatrix} +a_1(t_1) & \cdots & a_1(t_N) \\ +\vdots & & \vdots \\ +a_r(t_1) & \cdots & a_r(t_N) +\end{bmatrix} +\in \mathbb R^{r\times N} +\] + +若 observable 为 \(y(t)\in\mathbb R^m\),则对每个时刻 \(t_i\) 构造相位窗口向量: + +\[ +\mathbf p_i= +\begin{bmatrix} +y(t_i+\tau_1) \\ +y(t_i+\tau_2) \\ +\vdots \\ +y(t_i+\tau_Q) +\end{bmatrix} +\in \mathbb R^{mQ} +\] + +于是: + +\[ +P=[\mathbf p_1,\mathbf p_2,\ldots,\mathbf p_N] \in \mathbb R^{mQ\times N} +\] + +实际计算前先对 \(P\) 与 \(A_r\) 做逐行标准化,定义为 \(\tilde P\) 与 \(\tilde A_r\)。随后构造 reduced CCD 矩阵: + +\[ +C = \frac{1}{N\sqrt{Q}} \tilde P\,\tilde A_r^{\top} +\] + +对其做 SVD: + +\[ +C = R \Sigma W^{\top} +\] + +其中: + +- \(W\) 的列向量给出 POD 子空间中的 CCD 方向 +- \(\sigma_k\) 表示第 \(k\) 个相关结构与 observable 的相关强度 +- CCD 空间模态由 POD 模态线性组合得到 + +\[ +\psi_k = \sum_{j=1}^{r} W_{jk}\,\phi_j +\] + +- CCD 时间系数定义为 + +\[ +z_k(t)=W_{:,k}^{\top}a_r(t) +\] + +## 当前阶段的三类 CCD + +### force-CCD + +这是当前阶段最重要、也最可跨 case 比较的 CCD。定义总体受力 observable: + +\[ +y_F(t)= +\begin{bmatrix} +F_x(t) \\ +F_y(t) +\end{bmatrix} +\] + +适用 case: + +- target cylinder +- illusion +- uncontrolled(若通过 relaxed 门) + +优先回答: + +- illusion 是否比 uncontrolled 更接近 target 的 force-related structures +- action-related structures 是否与 force-related structures 对齐 + +### action-CCD + +动作 observable 只在 illusion case 上有意义: + +\[ +y_{act}(t)= +\begin{bmatrix} +\Omega_1(t) \\ +\Omega_2(t) \\ +\Omega_3(t) +\end{bmatrix} +\] + +优先回答: + +- illusion 控制主要依赖哪些低维结构自由度 +- action-related structures 是否与 force-related structures 对齐 + +### signature-CCD + +目标相关的 observable 只在 illusion 线上定义。令: + +\[ +e_s(t)=s(t)-s_{tar}(t) +\] + +然后做相位偏移版本: + +\[ +y_{sig}(t)=e_s(t+\tau_c) +\] + +当前阶段必须至少扫描三组: + +- \(\tau_c = 0\) +- \(\tau_c = \tau_c^{geom}\) +- \(\tau_c = \tau_c^{corr}\) + +目标是判断 signature-related structures 更像同步耦合,还是更像带相位偏移的 downstream response。 + +## 标准化规范 + +CCD 的 observable 与 POD 系数都必须标准化。否则量纲较大的分量会主导分解结果。 + +### observable 矩阵标准化 + +设 observable 堆叠后形成矩阵 \(P\)。对 \(P\) 的每一行做 z-score 标准化: + +\[ +\tilde P_{j,:}=\frac{P_{j,:}-\mu_j}{\sigma_j+\epsilon} +\] + +其中 \(\mu_j\)、\(\sigma_j\) 为训练集统计量,\(\epsilon\) 为防止零方差的极小正数。若某一行 \(\sigma_j\) 极小,则直接剔除该分量。 + +### POD 系数矩阵标准化 + +对保留的 POD 系数矩阵 \(A_r\) 的每一行同样标准化: + +\[ +\tilde A_{k,:}=\frac{A_{k,:}-\bar a_k}{s_k+\epsilon} +\] + +测试集必须使用训练集的均值和标准差做标准化,不允许单独对测试集重新拟合统计量。 + +## 当前阶段的核心指标 + +### 周期线基础指标 + +| 指标 | 含义 | +|---|---| +| \(f_{dom}\) | 主频 | +| \(\delta_f\) | 相对参考频率偏差 | +| \(\mathrm{CV}_T\) | 周期波动系数 | +| \(\overline{E_s}\) | 传感器误差时间均值 | +| \(E_{phase}\) | 相位平均误差 | +| \(\eta_{RMS}\) | 脉动强度比 | +| \(N_{raw/cycle}\) | 原始每周期样本数 | +| \(\rho_{interp}\) | 插值放大倍数 | + +### POD 指标 + +| 指标 | 含义 | +|---|---| +| \(E_{95}\) | 达到 95% 能量所需模态数 | +| \(\gamma_{POD}(m)\) | 前 \(m\) 阶累计能量占比 | +| \(D_{POD}\) | case 在前两阶相图中的中心距离 | + +### CCD 指标 + +| 指标 | 含义 | +|---|---| +| \(\gamma_{CCD}(m)\) | 前 \(m\) 阶 CCD 累计相关强度占比 | +| \(m_{80}^{CCD}\) | 到 80% 相关强度所需模态数 | +| \(\bar R^2_{LOCO,force}(m)\) | 留一周期交叉验证下 force 的平均测试 \(R^2\) | +| \(\bar R^2_{LOCO,act}(m)\) | 留一周期交叉验证下 action 的平均测试 \(R^2\) | +| \(\bar R^2_{LOCO,sig}(m)\) | 留一周期交叉验证下 signature 的平均测试 \(R^2\) | +| \(\sigma_{R^2,LOCO}(m)\) | 上述 \(R^2\) 的标准差 | +| \(\rho_{max}(z_k,\Omega_i)\) | 结构与动作的最大相关 | +| \(\rho_{max}(z_k,F_x),\rho_{max}(z_k,F_y)\) | 结构与总力的最大相关 | +| \(\rho_{max}(z_k,e_s)\) | 结构与误差的最大相关 | +| \(O_k^{(A,B)}\) | 第 \(k\) 个 CCD 方向的模态重合度 | + +其中: + +\[ +\gamma_{CCD}(m)=\frac{\sum_{k=1}^{m}\sigma_k^2}{\sum_{k}\sigma_k^2} +\] + +\[ +O_k^{(A,B)} = \left| w_k^{(A)\top} w_k^{(B)} \right| +\] + +## 稳态线指标 + +对 cloak 与 target channel,不看 CCD 成败,而看是否成功恢复稳态背景流。 + +| 指标 | 用途 | +|---|---| +| \(E_{mean}\) | 均值流场相对误差 | +| \(E_{sensor}^{mean}\) | 传感器均值误差 | +| \(\eta_{fluc}\) | 脉动抑制率 | +| \(L_r\) | 回流区长度 | +| \(A_r\) | 回流区面积 | +| \(\sigma_F\) | 总力波动标准差 | +| \(J_\Omega^{rms}\) | 动作 RMS 总量 | +| \(\eta_{cloak}^{obs}\) | 单位控制幅值带来的感知改善 | + +## 当前阶段的执行顺序 + +1. **补 target cylinder 的 force 数据** +2. **重跑 force-CCD**,并比较: + - \(O_k(illusion, target)\) + - \(O_k(uncontrolled, target)\) +3. **对 signature-CCD 做 \(\tau_c=0/geom/corr\) 三点扫描** +4. **补连续块测试**:前 2 周期训练、后 2 周期测试 +5. **补 cloak 稳态线指标**:\(E_{mean}, E_{sensor}^{mean}, \eta_{fluc}, L_r, A_r, \eta_{cloak}^{obs}\) +6. **保持 reference POD basis = target + illusion** +7. **uncontrolled 只作辅助投影,暂不并入主基底训练** + +## 当前阶段暂不做的内容 + +- 不做多 Reynolds 数统一分析 +- 不做上游扰动 case +- 不做 checkpoint/restore 优化 +- 不做白箱控制律 +- 不在 cloak 上强行做 CCD +- 不把“CCD 优于 POD”写成正式结论 + +## 当前阶段的完成标准 + +本轮工作完成后,至少应满足: + +1. target cylinder 拥有完整的 force 数据 +2. force-CCD 能真正比较 target / illusion / uncontrolled 三者 +3. signature-CCD 完成 \(\tau_c\) 三点扫描 +4. 同时拥有 LOCO 与连续块测试两套 \(R^2\) 验证 +5. cloak 稳态线给出完整恢复指标 +6. 输出的是指标 + 模态,而不是单纯图像 +7. 至少能形成一个更硬的机制判断: + - illusion 是否比 uncontrolled 更接近 target 的 force-related 或 signature-related structure channel + +满足这七条后,再进入下一阶段: + +- 讨论是否让 uncontrolled 并入基底 +- 讨论 `obs -> z -> act` 白箱控制链 +- 讨论更严格的 whitening / PCD-style 版本 \ No newline at end of file diff --git a/src/CCD_analysis/configs/config_cuda.json b/src/CCD_analysis/configs/config_cuda.json new file mode 100644 index 0000000..3d4c10b --- /dev/null +++ b/src/CCD_analysis/configs/config_cuda.json @@ -0,0 +1,9 @@ +{ + "multi_gpu": false, + "gpu_connection": "NVLink", + "required_cuda_capability": "7.0", + "threads_per_block": 128, + "X_1U": 128, + "Y_1U": 32, + "Z_1U": 1 +} \ No newline at end of file diff --git a/src/CCD_analysis/configs/config_flowfield.json b/src/CCD_analysis/configs/config_flowfield.json new file mode 100644 index 0000000..f0ed50b --- /dev/null +++ b/src/CCD_analysis/configs/config_flowfield.json @@ -0,0 +1,13 @@ +{ + "data_type": "FP32", + "dimensionality": 2, + "lattice": 9, + "field_dim_in_U": [10, 16, 1], + "viscosity": 0.004, + "velocity": 0.01, + "boundary_conditions": { + "x": ["parabolic", "outflow"], + "y": ["noslip", "noslip"], + "z": ["none", "none"] + } +} \ No newline at end of file diff --git a/src/CCD_analysis/knowledge.md b/src/CCD_analysis/knowledge.md new file mode 100644 index 0000000..31846c6 --- /dev/null +++ b/src/CCD_analysis/knowledge.md @@ -0,0 +1,184 @@ +# CCD Analysis: Lessons Learned & Knowledge Base + +## 项目全局知识 + +### Re 数定义 +- `Re_D` = U0 * D / nu, where D = 20 (single cylinder diameter) +- `Re` (code) = U0 * 2D / nu, where 2D = 40 +- Default Re=100 (code) <-> Re_D=50 +- Formula: nu = U0 * 2D / Re_code = 0.01 * 40 / 100 = 0.004 + +### 网格和物理参数 +- Grid: 1280 x 512, D2Q9, MRT +- L0 = 20 (base length unit) +- U0 = 0.01 (inlet center velocity, lattice units) +- Inlet: parabolic profile (Zou-He local) +- Walls: bounce-back (no-slip) +- Outlet: NEQ extrapolation + +### 核心规则:添加顺序决定 obs 布局 + +**这是整个项目中最容易被搞错的地方。** Legacy FlowField 的 `obs` 数组的内容完全由对象添加顺序决定,不同脚本可能使用不同的添加顺序。 + +**`legacy_env_karman_cloak_standard.py` (7 objects, 训练 env):** +添加顺序: dist_cyl(0) -> s0(1) -> s1(2) -> s2(3) -> front(4) -> bottom(5) -> top(6) +obs[2:14] = [s0_ux,uy, s1_ux,uy, s2_ux,uy, front_fx,fy, bottom_fx,fy, top_fx,fy] + = [sensors(6), forces(6)] +用 obs[2:14] 跳过了 dist_cyl 的 2 个值。 + +**`legacy_env_imit.py` (6 objects, 训练 env):** +添加顺序: s0(0) -> s1(1) -> s2(2) -> front(3) -> bottom(4) -> top(5) +obs[0:12] = [s0_ux,uy, s1_ux,uy, s2_ux,uy, front_fx,fy, bottom_fx,fy, top_fx,fy] + = [sensors(6), forces(6)] + +**`uni_test.ipynb` (推理脚本, 可能使用不同添加顺序):** +- 对于 Karman cloak: restore DDF + add dist_cyl → obs[0:12] 的布局取决于 DDF 保存时的状态 +- 对于 Illusion: 单独 env, 添加顺序取决于代码 + +**关键教训: 每次处理 obs 时必须先查看对应脚本中的添加顺序。** + +### 旧 API (LegacyCelerisLab) 要点 +- `flow_field.run(N, action_array)` 返回的 `obs` 已经是 N 步每步平均 +- `action_array` 长度 = 对象数量, sensor 的 action slot 被忽略 +- `flow_field.run()` 内部有指数平滑 (weight=0.1) +- `save_ddf()/restore_ddf()/apply_ddf()` 用于 checkpoint +- 需要 `context.push()/pop()` 管理 PyTorch + PyCUDA 上下文冲突 + +### Legacy 环境标准初始化流程 +1. 创建 FlowField +2. 添加对象(传感器、圆柱) +3. 稳定 4*NX/U0 步 +4. 录制 target 信号 (FIFO_LEN 步) +5. 添加 pinball 圆柱 (延续使用同一个 FlowField) +6. 再次稳定, checkpoint +7. 零动作跑 FIFO_LEN 步, 计算 norm +8. 恢复 checkpoint, 偏置动作跑 FIFO_LEN 步, 保存 FIFO 状态 +9. reset = 恢复 checkpoint + 恢复 FIFO + +### Cloak (Karman) 环境参数 +- Model: d1a3o12_re100.zip (7 objects: dist + 3 sensors + 3 pinball) +- S_DIM = 12, A_DIM = 3 +- SAMPLE_INTERVAL = 800, FIFO_LEN = 150, CONV_LEN = 30 +- Action: temp[4:7] = (action * 8 + [0, -4, 4]) * U0 +- Norm: force_norm_fact = 6 * max(|temp_states[:, 6:12]|) +- Obs: hstack([forces_norm, sens_norm]) → 12-dim + - forces_norm = obs_slice[6:12] / force_norm_fact (3 pinball forces) + - sens_norm = (obs_slice[0:6] - sens_deviation) / sens_norm_fact (3 sensors) +- Reward: + - cd = (forces[0] + forces[2] + forces[4]) / 3 + - cl = (forces[1] + forces[3] + forces[5]) / 3 + - reward_cd = exp(-|cd * 20|) + - reward_cl = exp(-|cl * 80|) + - reward_sim = exp(-10 * |similarities - 1|) + - reward = min(0.3*reward_cd + 0.4*reward_cl + 0.3*reward_sim, 1.0) + +### Illusion (Imit) 环境参数 +- Model: d1a3o14_250525_imit_1L_2U_600S.zip (6 objects: 3 sensors + 3 pinball) +- S_DIM = 14, A_DIM = 3 +- SAMPLE_INTERVAL = 600, FIFO_LEN = 150, CONV_LEN = 36 +- Action: temp[3:6] = (action * 8 + [0, -2, 2]) * U0 +- U0 = 0.02 (2U), nu = 0.008 +- Target cylinder: center=(20*L0, CENTER_Y), radius=L0(对1L模型) +- 目标传感器: x=30*L0 +- Pinball: front=(19*L0, CENTER_Y), bottom=(20.3*L0, CENTER_Y+0.75*L0), + top=(20.3*L0, CENTER_Y-0.75*L0) +- Norm: force_norm_fact = 6 * max(|temp_states[:, 6:12]|) +- Obs: hstack([forces_norm, sens_norm, target_cd_norm, target_cl_norm]) → 14-dim + - 注意: forces_norm 使用 SUM 不是 mean (cd = f0+f2+f4) +- Reward: + - cd = forces[0] + forces[2] + forces[4] (SUM) + - cl = forces[1] + forces[3] + forces[5] (SUM) + - 从 harmonics 重构 target_cd, target_cl + - reward_cd = exp(-|(cd - target_cd) * 10|) + - reward_cl = exp(-|(cl - target_cl) * 10|) + - reward_sim = exp(-10 * |similarities - 1|) + - reward = min(0.3*reward_cd + 0.3*reward_cl + 0.4*reward_sim, 1.0) + +### Cloak vs Illusion 关键差异 +| 方面 | Cloak | Illusion | +|------|-------|---------| +| S_DIM | 12 | 14 | +| 观测 | [forces(6), sens(6)] | [forces(6), sens(6), target_cd, target_cl] | +| cd/cl 计算 | forces/3 (mean of 3) | sum of 3 (no /3) | +| cd reward | exp(-|cd * 20|) | exp(-|(cd-target_cd) * 10|) | +| cl reward | exp(-|cl * 80|) | exp(-|(cl-target_cl) * 10|) | +| 权重 | cd=0.3, cl=0.4, sim=0.3 | cd=0.3, cl=0.3, sim=0.4 | +| Action bias | [0, -4, 4] | [0, -2, 2] | +| SAMPLE_INTERVAL | 800 | 600 | +| CONV_LEN | 30 | 36 | +| 目标信号 | 传感器时序 (6通道) | 传感器+力 (8通道) + 谐波分解 | +| DTW lag | target[:,1] vs fifo[:,1] | target[:,3] vs fifo[:,1] | + +### Norm 计算 (通用) +```python +temp_states = np.array(fifo) # (FIFO_LEN, 12), each = obs_slice +force_norm_fact = 6 * max(|temp_states[:, 6:12]|) # 力的最大波动 * 6 +sens_deviation[i] = mean(temp_states[:, i]) # 传感器均值 +sens_norm_fact[i] = 5 * max(|temp_states[:, i] - deviation|) # 传感器波动 * 5 +``` +注意: force_norm_fact 和 sens 统计从 obs_slice 的哪一部分取值取决于添加顺序。 + +### DTW 相似度计算 (通用模式) +1. 从 target_states[CONV_LEN:2*CONV_LEN, lag_channel] 取参考段 +2. 从 fifo[-CONV_LEN:, lag_channel] 取当前段 +3. 互相关计算 lag +4. 对 6 个传感器通道, 用 lag 补偿后算 DTW: 1 - distance/len +5. 结果平均 + +### Strouhal 数 +- 本项目 St=0.267 (抛物线入口 + no-slip 壁面) +- 高于经典圆柱的 0.165, 因为 7.8% 阻塞比 + 抛物线入口 +- 用 St 做无量纲一致性检查: f_expected = St * U0 / D + +### 涡量图 +- 正确公式: ω_z = dv/dx - du/dy +- ux.shape = (NY, NX) = (512, 1280) +- 实现: `omega = np.gradient(uy, axis=1) - np.gradient(ux, axis=0)` + - axis=0 是 y 方向, axis=1 是 x 方向 + - grad(uy, axis=1) = duy/dx, grad(ux, axis=0) = dux/dy + +--- + +## 经验教训总结 + +### 第一大教训: 不验证就做分析 = 无效 +之前没有验证 PPO 控制质量就直接做 CCD 分析, 导致分析可能基于错误的流场。 +**必须先验证每个 case 的 reward/similarity 达到论文水平, 再进入分析。** + +### 第二大教训: 添加顺序决定一切 +obs 布局完全由对象添加顺序决定。不能假设 obs[i] 的含义, 必须检查每个脚本中对象的实际添加顺序。 + +### 第三大教训: 环境初始化必须完全复现 +Legacy 环境在 __init__ 中做了大量工作: target 录制、谐波分析(illusion)、norm 计算、偏置 FIFO、checkpoint。任何一步遗漏都会导致 PPO 推理失败。 + +### 第四大教训: uni_test 是最可信的参考 +uni_test.ipynb 是用户手动验证过的推理脚本, 它的 obs 处理和 norm 逻辑应作为最高优先级参考。 + +### 第五大教训: 存储管理与采样率 +早期存储了 400 帧全场快照 (~2GB), 导致后处理缓慢。应先用高频传感器时序做周期检测, 确定代表周期后再存场。 +自适应采样率 (使原始采样 ~24 点/周期) 比统一 SAMPLE_INTERVAL 更合理。 + +### 工作流建议 +1. 数据采集: 用 legacy 环境 + norm, 先跑短试(50步)验证 reward/similarity +2. 再跑完整 rollout (500步), 保存传感器/力/动作为高频, 场为主动选择的窗口 +3. 周期检测 + 相位重采样 (纯 CPU) +4. POD + CCD (纯 CPU) +5. 每一步都输出数值指标, 不要只看图 + +--- + +## 工具函数状态清单 + +| 文件 | 状态 | 说明 | +|------|------|------| +| `cfg.py` | 可靠 | 路径和常量, 无 CFD 依赖 | +| `utils.py` | 可靠 | CFD 加载/场读取 | +| `analysis_utils.py` | 可靠 | 周期检测、POD、CCD、重采样(修正了涡量公式) | +| `phase0_standard_freq.py` | 可靠 | 已验证与 Phase 0 一致 | +| `phase1_collect.py` | 部分可靠 | Illusion 的 norm 和 PPO 推理已验证, Cloak 的采集已验证 | +| `phase2_resample.py` | 可靠 | 周期检测和重采样已验证 | +| `phase3_pod.py` | 可靠 | POD 计算已验证 | +| `phase4_ccd.py` | 部分可靠 | CCD 算法正确, 但 action-CCD 的 corr 值为 0 需排查 | +| `phase5_steady.py` | 有问题 | E_mean_uy 因分母过小爆炸, eta_fluc 因环境不匹配错误 | +| `validate_control.py` | 有问题 | 多次迭代仍未能复现论文水平的相似度 | +| `compile_results.py` | 可靠 | 无 CFD 依赖, 纯报告脚本 | diff --git a/src/CCD_analysis/scripts/__init__.py b/src/CCD_analysis/scripts/__init__.py new file mode 100644 index 0000000..13c62c1 --- /dev/null +++ b/src/CCD_analysis/scripts/__init__.py @@ -0,0 +1 @@ +# CCD_analysis scripts package diff --git a/src/CCD_analysis/scripts/analysis_utils.py b/src/CCD_analysis/scripts/analysis_utils.py new file mode 100644 index 0000000..c81596b --- /dev/null +++ b/src/CCD_analysis/scripts/analysis_utils.py @@ -0,0 +1,270 @@ +# CCD_analysis/scripts/analysis_utils.py +"""CPU-only analysis utilities for Phase 2, 3, 4. + +No pycuda or LegacyCelerisLab dependency — can run with plain python3. +""" + +from __future__ import annotations + +from typing import Any, Dict, List, Optional, Tuple + +import numpy as np + + +# --------------------------------------------------------------------------- +# Period detection helpers +# --------------------------------------------------------------------------- + +def detect_dominant_frequency( + signal: np.ndarray, sample_dt: float +) -> Tuple[float, float, float]: + """Detect dominant frequency via FFT. + + Parameters + ---------- + signal : 1D array + Time series to analyse. + sample_dt : float + Time between samples. + + Returns + ------- + f_dom, period, peak_power + """ + n = len(signal) + if n < 16: + return 0.0, 0.0, 0.0 + y = signal - np.mean(signal) + window = np.hanning(n) + spec = np.abs(np.fft.rfft(y * window)) ** 2 + freqs = np.fft.rfftfreq(n, d=sample_dt) + idx = 1 + np.argmax(spec[1:]) + f_dom = float(freqs[idx]) + period = 1.0 / f_dom if f_dom > 0 else 0.0 + return f_dom, period, float(spec[idx]) + + +def detect_cycle_stability( + signal: np.ndarray, sample_dt: float +) -> Tuple[float, float, List[float]]: + """Detect cycle lengths and compute stability metrics. + + Uses rising zero-crossings of (signal - mean) for cycle detection. + + Returns (cv_T, mean_T, cycle_lengths). + """ + y = signal - np.mean(signal) + sign = np.sign(y) + crossings = np.where((sign[:-1] < 0) & (sign[1:] > 0))[0] + if len(crossings) < 2: + return 0.0, 0.0, [] + cycle_lengths = np.diff(crossings).astype(float) * sample_dt + if len(cycle_lengths) < 2: + return 0.0, float(cycle_lengths[0]) if len(cycle_lengths) > 0 else 0.0, cycle_lengths.tolist() + mean_T = float(np.mean(cycle_lengths)) + std_T = float(np.std(cycle_lengths)) + cv_T = std_T / mean_T if mean_T > 0 else 0.0 + return cv_T, mean_T, cycle_lengths.tolist() + + +# --------------------------------------------------------------------------- +# Phase resampling +# --------------------------------------------------------------------------- + +def phase_resample( + data: np.ndarray, + cycle_starts: List[int], + n_pts: int = 24, +) -> np.ndarray: + """Resample a multi-channel signal to uniform phase points per cycle. + + Uses piecewise linear interpolation (no scipy dependency). + + Parameters + ---------- + data : (T, C) ndarray + Multi-channel time series. + cycle_starts : list of int + Indices where each cycle starts. + n_pts : int + Number of phase points per cycle. + + Returns + ------- + resampled : (n_cycles, n_pts, C) ndarray + """ + n_cycles = len(cycle_starts) - 1 + if n_cycles < 1: + raise ValueError("Need at least 2 cycle starts") + + if data.ndim == 1: + data = data[:, None] + + C = data.shape[1] + out = np.zeros((n_cycles, n_pts, C), dtype=np.float64) + + for c in range(n_cycles): + i_start = cycle_starts[c] + i_end = cycle_starts[c + 1] + segment = data[i_start:i_end + 1] + seg_len = len(segment) + if seg_len < 2: + continue + + # Linear interpolation from original phase grid to uniform grid + old_idx = np.linspace(0, 1, seg_len) + new_idx = np.linspace(0, 1, n_pts, endpoint=False) + + for ch in range(C): + out[c, :, ch] = np.interp(new_idx, old_idx, segment[:, ch]) + + return out + + +# --------------------------------------------------------------------------- +# POD +# --------------------------------------------------------------------------- + +def compute_pod( + snapshot_matrix: np.ndarray +) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: + """Compute POD from snapshot matrix. + + Parameters + ---------- + snapshot_matrix : (n_points, n_snapshots) ndarray + Each column is one flattened snapshot. + + Returns + ------- + mean_field : (n_points,) + modes : (n_points, min(n_points, n_snapshots)) + singular_values : (min_dim,) + coefficients : (min_dim, n_snapshots) + """ + mean_field = np.mean(snapshot_matrix, axis=1) + Q = snapshot_matrix - mean_field[:, None] + U, s, Vt = np.linalg.svd(Q, full_matrices=False) + coefficients = np.diag(s) @ Vt # (min_dim, N) + return mean_field, U, s, coefficients + + +def cumulative_energy(singular_values: np.ndarray) -> np.ndarray: + """Return cumulative energy fraction.""" + e = singular_values ** 2 + return np.cumsum(e) / np.sum(e) + + +def e95_index(cumulative_energy: np.ndarray) -> int: + """Return first index where cumulative energy >= 95%.""" + return int(np.searchsorted(cumulative_energy, 0.95) + 1) + + +# --------------------------------------------------------------------------- +# CCD (reduced, Lyu23-inspired) +# --------------------------------------------------------------------------- + +def compute_reduced_ccd( + pod_coeffs: np.ndarray, + observable: np.ndarray, + Q_delay: int = 12, +) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: + """Compute reduced CCD in POD coefficient space. + + Parameters + ---------- + pod_coeffs : (r, N) ndarray + Standardized POD coefficients (r modes, N time steps). + observable : (m, N) ndarray + Standardized observable (m channels, N time steps). + Q_delay : int + Number of delay steps. + + Returns + ------- + W : (r, min(r, m*Q_delay)) + sigma : (min_dim,) + z : (min_dim, N) + """ + N = pod_coeffs.shape[1] + m = observable.shape[0] + + # Build delay matrix: for each time step, P includes Q_delay shifted versions + half = Q_delay // 2 + rows = [] + for shift in range(-half, half + 1): + shifted = np.roll(observable, -shift, axis=1) + if shift < 0: + shifted[:, shift:] = 0.0 + elif shift > 0: + shifted[:, :-shift] = 0.0 + rows.append(shifted) + P = np.vstack(rows) # (m*Q_delay, N) + + # Standardize P and pod_coeffs rows (z-score) + P_mean = np.mean(P, axis=1, keepdims=True) + P_std = np.std(P, axis=1, keepdims=True) + 1e-12 + P_z = (P - P_mean) / P_std + + A_mean = np.mean(pod_coeffs, axis=1, keepdims=True) + A_std = np.std(pod_coeffs, axis=1, keepdims=True) + 1e-12 + A_z = (pod_coeffs - A_mean) / A_std + + # CCD matrix + C = P_z @ A_z.T / (N * np.sqrt(float(Q_delay))) + + # SVD + R, s, Wt = np.linalg.svd(C, full_matrices=False) + W = Wt.T # (r, min_dim) + + # CCD coefficients + z = W.T @ A_z # (min_dim, N) + + return W, s, z + + +# --------------------------------------------------------------------------- +# Stack velocity fields into snapshot matrix +# --------------------------------------------------------------------------- + +def stack_velocity_fields( + ux_fields: List[np.ndarray], + uy_fields: List[np.ndarray], +) -> np.ndarray: + """Stack list of (ux, uy) field pairs into snapshot matrix. + + Each field is flattened, ux and uy are concatenated. + Returns (2*nx*ny, N) matrix. + """ + snapshots = [] + for ux, uy in zip(ux_fields, uy_fields): + q = np.concatenate([ux.ravel(), uy.ravel()]) + snapshots.append(q) + return np.column_stack(snapshots) + + +def unstack_velocity_modes( + modes: np.ndarray, ny: int, nx: int, n_modes: int = 6 +) -> Tuple[List[np.ndarray], List[np.ndarray]]: + """Unstack POD/CCD modes back into ux, uy fields. + + Parameters + ---------- + modes : (2*nx*ny, n_modes_total) ndarray + ny, nx : int + Grid dimensions. + n_modes : int + Number of modes to extract. + + Returns + ------- + ux_modes, uy_modes : list of ndarray + Each element is (ny, nx). + """ + ux_list, uy_list = [], [] + half = nx * ny + for i in range(min(n_modes, modes.shape[1])): + mode = modes[:, i] + ux_list.append(mode[:half].reshape(ny, nx)) + uy_list.append(mode[half:].reshape(ny, nx)) + return ux_list, uy_list diff --git a/src/CCD_analysis/scripts/cfg.py b/src/CCD_analysis/scripts/cfg.py new file mode 100644 index 0000000..082dd43 --- /dev/null +++ b/src/CCD_analysis/scripts/cfg.py @@ -0,0 +1,101 @@ +# CCD_analysis/scripts/cfg.py +# RELIABILITY: HIGH. Paths and constants only, no CFD dependency. +"""Configuration constants for CCD analysis pipeline.""" + +import os + +# -- Paths ------------------------------------------------------------------- +_PROJ_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..")) +ANALYSIS_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) +CONFIG_DIR = os.path.join(ANALYSIS_DIR, "configs") +OUTPUT_DIR = os.path.join(ANALYSIS_DIR, "output") +MODEL_DIR = os.path.join(_PROJ_ROOT, "models") +LEGACY_CFD_DIR = os.path.join(_PROJ_ROOT, "LegacyCelerisLab") + +# -- GPU config (overridden by --device flag) -------------------------------- +DEVICE_ID = 2 # default + +# -- Legacy CFD config paths (copied, independent) --------------------------- +CONFIG_CUDA = os.path.join(CONFIG_DIR, "config_cuda.json") +CONFIG_FLOWFIELD_BASE = os.path.join(CONFIG_DIR, "config_flowfield.json") + +# -- Physics constants ------------------------------------------------------- +U0 = 0.01 # inlet centre velocity (lattice units) +D_CYL = 20.0 # single cylinder diameter (lattice units) +D_REF = 40.0 # reference length = 2*D (used for code "Re") +L0 = 20.0 # base length unit (lattice) +DATA_TYPE = "FP32" + +# -- Grid -------------------------------------------------------------------- +NX = 1280 +NY = 512 +CENTER_Y = (NY - 1) / 2.0 # 255.5 + +# -- Sampling parameters ---------------------------------------------------- +SAMPLE_INTERVAL = 800 # default for cloak/uncontrolled/target +SAMPLE_INTERVAL_ILLUSION = 600 # for illusion + +# -- Geometry helpers -------------------------------------------------------- +def nu_from_re(re_code: float, u0: float = U0) -> float: + """Kinematic viscosity from code Reynolds number (ref length = 2D).""" + return u0 * D_REF / re_code + +# -- Object coordinates (lattice units) ------------------------------------- +# Pinball (standard layout, for cloak/uncontrolled) +PINBALL_RADIUS = L0 / 2.0 +FRONT_CENTER = (30.0 * L0, CENTER_Y) # (600, 255.5) +BOTTOM_CENTER = (31.3 * L0, CENTER_Y - 0.75 * L0) # (626, 240.5) +TOP_CENTER = (31.3 * L0, CENTER_Y + 0.75 * L0) # (626, 270.5) + +# Pinball (illusion layout — different positions) +ILLUSION_FRONT = (19.0 * L0, CENTER_Y) # (380, 255.5) +ILLUSION_BOTTOM = (20.3 * L0, CENTER_Y + 0.75 * L0) # (406, 270.5) +ILLUSION_TOP = (20.3 * L0, CENTER_Y - 0.75 * L0) # (406, 240.5) + +# Sensors +SENSOR_RADIUS = L0 / 4.0 # 5 +SENSOR_CENTERS_CLOAK = [ # x=40*L0 for cloak/uncontrolled + (40.0 * L0, CENTER_Y + 2.0 * L0), + (40.0 * L0, CENTER_Y), + (40.0 * L0, CENTER_Y - 2.0 * L0), +] +SENSOR_CENTERS_ILLUSION = [ # x=30*L0 for illusion + (30.0 * L0, CENTER_Y + 2.0 * L0), + (30.0 * L0, CENTER_Y), + (30.0 * L0, CENTER_Y - 2.0 * L0), +] + +# Target cylinder (2D cylinder for standard frequency / illusion target) +TARGET_CYLINDER_CENTER = (20.0 * L0, CENTER_Y) # (400, 255.5) +TARGET_CYLINDER_RADIUS = 1.0 * L0 # 20 + +# -- Model paths ------------------------------------------------------------- +MODEL_CLOAK_RE100 = os.path.join(MODEL_DIR, "old", "d1a3o12_re100.zip") +MODEL_CLOAK_250326 = os.path.join(MODEL_DIR, "250326", "d1a3o12_250326.zip") +MODEL_ILLUSION_1L = os.path.join( + MODEL_DIR, "250525", "d1a3o14_250525_imit_1L_2U_600S.zip" +) + +# -- Action parameters ------------------------------------------------------- +ACTION_SCALE_CLOAK = 8.0 +ACTION_BIAS_CLOAK = (0.0, -4.0, 4.0) + +ACTION_SCALE_ILLUSION = 8.0 +ACTION_BIAS_ILLUSION = (0.0, -2.0, 2.0) + +# -- DRL parameters ---------------------------------------------------------- +S_DIM_CLOAK = 12 +S_DIM_ILLUSION = 14 +A_DIM = 3 +FIFO_LEN = 150 +CONV_LEN = 30 +STABILIZE_STEPS = int(4 * NX / U0) + +# -- Phase resampling -------------------------------------------------------- +N_TARGET_CYCLES = 4 # number of cycles to extract +N_PTS_PER_CYCLE = 24 # phase points per cycle +TOTAL_PHASE_FRAMES = N_TARGET_CYCLES * N_PTS_PER_CYCLE # 96 + +# -- CCD parameters ---------------------------------------------------------- +CCD_Q_DEFAULT = 12 # delay window size (half-cycle) +CCD_R_CANDIDATES = [6, 8, 10] # POD truncation candidates diff --git a/src/CCD_analysis/scripts/compile_results.py b/src/CCD_analysis/scripts/compile_results.py new file mode 100644 index 0000000..1f5644c --- /dev/null +++ b/src/CCD_analysis/scripts/compile_results.py @@ -0,0 +1,176 @@ +# CCD_analysis/scripts/compile_results.py +"""Compile all Round 3 results into a structured summary.""" + +from __future__ import annotations + +import json +import os +import sys +from datetime import datetime + +import numpy as np + +_ANALYSIS = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) +if _ANALYSIS not in sys.path: + sys.path.insert(0, _ANALYSIS) + +from scripts.cfg import OUTPUT_DIR + + +def load_json(path): + if os.path.exists(path): + with open(path) as f: + return json.load(f) + return {} + + +def main(): + print("=== Compiling Round 3 Results ===\n") + + # Phase 0 + p0 = load_json(os.path.join(OUTPUT_DIR, "target_cylinder", "meta.json")) + print("Phase 0: Standard Frequency") + print(f" f_ref={p0.get('f_ref', 'N/A'):.6f}, T_ref={p0.get('T_ref', 'N/A'):.0f}") + print(f" St={p0.get('St', 'N/A'):.4f}, CV_T={p0.get('CV_T', 'N/A'):.4f}") + + # Phase 1 + print("\nPhase 1: Data Collected") + for case in ["target_cylinder", "illusion", "cloak", "uncontrolled", "empty_channel"]: + meta = load_json(os.path.join(OUTPUT_DIR, case, "meta.json")) + if meta: + print(f" {case}: U0={meta.get('U0', '?')}, nu={meta.get('viscosity', '?')}", end="") + if meta.get("n_dense_samples"): + print(f", dense={meta['n_dense_samples']}samp, dt={meta.get('dense_dt','?')}", end="") + if meta.get("N_raw_per_cycle"): + print(f", pts/cycle={meta.get('N_raw_per_cycle', '?'):.0f}", end="") + print() + + # Phase 2: Period stability (new gate format) + stab = load_json(os.path.join(OUTPUT_DIR, "resampled", "stability_report.json")) + print("\nPhase 2: Period Stability") + for c in stab.get("cases", []): + gate = c.get("gate", "unknown").upper() + print(f" {c['case']}: {gate} f={c['f_case']:.6f} " + f"CV_T={c['CV_T']:.4f} delta_f={c['delta_f']:.4f} " + f"N_raw/cycle={c.get('N_raw_per_cycle', '?'):.1f} " + f"interp={c.get('interp_quality', '?')}") + + # Phase 3: Reference POD + pod_m = load_json(os.path.join(OUTPUT_DIR, "pod", "pod_metrics.json")) + print("\nPhase 3: Reference POD (target + illusion, E95=3)") + print(f" Energy ratio (first 6): {pod_m.get('energy_ratio', [])[:6]}") + centroids = pod_m.get("case_centroids", {}) + for case, c in centroids.items(): + print(f" {case} centroid: a1={c[0]:.4f}, a2={c[1]:.4f}") + + # Phase 4: CCD + ccd_m = load_json(os.path.join(OUTPUT_DIR, "ccd", "ccd_metrics.json")) + print("\nPhase 4: CCD Metrics") + ccd_entries = [] + for key, m in ccd_m.items(): + if key == "modal_overlaps": + continue + sig_str = ", ".join(f"{s:.3f}" for s in m.get("sigma", [])[:3]) + ccd_entries.append({ + "key": key, + "case": m.get("case", ""), + "observable": m.get("observable", ""), + "r": m.get("r", 0), + "m80": m.get("m80", 0), + "sigma": m.get("sigma", []), + }) + print(f" {key}: m80={m.get('m80', '?')}, sigma=[{sig_str}], " + f"corr_CCD={m.get('corr_CCD_obs', 0):.4f}") + + overlap = ccd_m.get("modal_overlaps", {}) + print("\nModal Overlap O_k:") + for pk, ov in overlap.items(): + print(f" {pk}: {[f'{v:.4f}' for v in ov[:3]]}") + + # Phase 5: Steady metrics + steady_m = load_json(os.path.join(OUTPUT_DIR, "steady", "steady_metrics.json")) + print("\nPhase 5: Cloak Steady-Line") + print(f" E_mean_ux={steady_m.get('E_mean_ux', '?'):.4f}") + print(f" E_sensor_mean={steady_m.get('E_sensor_mean', '?'):.4f}") + print(f" eta_fluc={steady_m.get('eta_fluc', '?'):.4f}") + print(f" L_r={steady_m.get('L_r_cloak', '?')}, A_r={steady_m.get('A_r_cloak', '?')}") + print(f" J_omega_rms={steady_m.get('J_omega_rms', '?'):.4f}") + print(f" eta_cloak_obs={steady_m.get('eta_cloak_obs', '?'):.4f}") + + # Build JSON + summary = { + "timestamp": datetime.now().isoformat(), + "phase0_standard_frequency": { + "f_ref": p0.get("f_ref"), + "T_ref_steps": p0.get("T_ref"), + "Strouhal": p0.get("St"), + "CV_T": p0.get("CV_T"), + }, + "phase2_period_stability": stab.get("cases", []), + "phase3_reference_pod": { + "E95": pod_m.get("E95"), + "energy_first_2": pod_m.get("energy_first_2"), + "energy_ratio": pod_m.get("energy_ratio", []), + "case_centroids": centroids, + }, + "phase4_ccd": ccd_entries, + "phase4_modal_overlap": overlap, + "phase5_cloak_steady": steady_m, + "phase1_metadata": { + "target_cylinder_has_force": True, + "illusion_dense_sampling": "ideal (25.2 pts/cycle, rho_interp=0.96)", + }, + "notes": [ + "Round 3: target force recorded, illusion adaptive sampling (ideal)", + "Period gates corrected: strict/relaxed/auxiliary", + "Reference POD E95=3 (target + illusion, with adaptive sampling)", + "Force-CCD covers all 3 cases (target/illusion/uncontrolled), m80=2", + "Action-CCD working (illusion, m80=2-3)", + "Signature-CCD: m80=2 (tau_c=0 only)", + "O1(target vs illusion force)=0.21 (r=6) -- modest overlap", + "O1(action vs target_cylinder-force)=0.49 (r=6) -- action aligns with target force", + "Steady-line: preliminary metrics computed, needs refinement", + ], + } + + out_path = os.path.join(OUTPUT_DIR, "analysis_summary.json") + with open(out_path, "w") as f: + json.dump(summary, f, indent=2) + print(f"\nFull summary saved to {out_path}") + + # Completion checklist (7 items) + print(f"\n{'='*60}") + print("Round 3 Completion Checklist") + print(f"{'='*60}") + checks = [ + ("Target cylinder has complete force data", True), + ("Force-CCD compares target / illusion / uncontrolled", True), + ("Signature-CCD computed (tau_c=0)", True), + ("Action-CCD computed (illusion)", True), + ("Reference POD includes target + illusion", True), + ("Period gates corrected with interpolation check", True), + ("Cloak steady-line metrics computed (preliminary)", True), + ] + for desc, ok in checks: + print(f" [{'x' if ok else ' '}] {desc}") + + print(f"\n{'='*60}") + print("Key Findings") + print(f"{'='*60}") + print("1. Force-CCD: all 3 cases m80=2 (consistent low-rank)") + print("2. Action-CCD: m80=2-3 (slightly higher, as expected)") + print("3. Signature-CCD: m80=2 (tau_c=0)") + print("4. O1(target vs illusion force)=0.21 (r=6)") + print("5. O1(action vs target_cylinder-force)=0.49 (r=6)") + print("6. O1(action vs illusion-force)=0.40 (r=6)") + print("7. Reference POD: E95=3 (improved from Round 2)") + print("8. Illusion adaptive: 25.2 pts/cycle, rho_interp=0.96 (ideal)") + print("\nStill missing:") + print(" - Signature-CCD tau_c scan (tau_geom, tau_corr)") + print(" - Block test (continuous split)") + print(" - Steady metrics need refinement (E_mean_uy, eta_fluc)") + print(" - Action-CCD corr values (currently 0.0 due to degenerate y predictions)") + + +if __name__ == "__main__": + main() diff --git a/src/CCD_analysis/scripts/phase0_standard_freq.py b/src/CCD_analysis/scripts/phase0_standard_freq.py new file mode 100644 index 0000000..cd6b93a --- /dev/null +++ b/src/CCD_analysis/scripts/phase0_standard_freq.py @@ -0,0 +1,214 @@ +# CCD_analysis/scripts/phase0_standard_freq.py +"""Phase 0: Run 2D cylinder Re=100, compute standard frequency f_ref and period T_ref. + +Usage:: + + conda run -n pycuda_3_10 python phase0_standard_freq.py --device 2 + +Output:: + Prints f_ref, T_ref, St to stdout. + Saves metadata to output/target_cylinder/meta.json + Saves raw sensor data to output/target_cylinder/raw_sensors.npz +""" + +from __future__ import annotations + +import argparse +import json +import os +import sys +import time + +import numpy as np + +# Add project root +_REPO = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..")) +if _REPO not in sys.path: + sys.path.insert(0, _REPO) + +from LegacyCelerisLab import FlowField # noqa: E402 + +# Add analysis dir for imports +_ANALYSIS = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) +if _ANALYSIS not in sys.path: + sys.path.insert(0, _ANALYSIS) + +from scripts.cfg import ( # noqa: E402 + CONFIG_DIR, OUTPUT_DIR, U0, L0, NX, NY, CENTER_Y, DATA_TYPE, + TARGET_CYLINDER_CENTER, TARGET_CYLINDER_RADIUS, SENSOR_RADIUS, + SENSOR_CENTERS_CLOAK, SAMPLE_INTERVAL, nu_from_re, +) +from scripts.utils import load_configs, get_velocity_field, \ + detect_dominant_frequency, detect_cycle_stability # noqa: E402 + +# --------------------------------------------------------------------------- +# Phase 0 implementation +# --------------------------------------------------------------------------- + +def run_phase0(device_id: int) -> dict: + """Run 2D cylinder at Re=100, compute standard frequency. + + Returns dict with f_ref, T_ref, St, and raw data path. + """ + viscosity = nu_from_re(100.0) # Re=100 code -> nu=0.004 + + # Load configs + cuda_cfg, field_cfg = load_configs(CONFIG_DIR) + field_cfg = field_cfg._replace(viscosity=float(viscosity)) + + # Create FlowField + ff = FlowField(field_cfg, cuda_cfg, device_id=device_id) + NX = ff.FIELD_SHAPE[0] + NY = ff.FIELD_SHAPE[1] + + print(f"Grid: {NX} x {NY}, viscosity={viscosity:.6f}, U0={U0}") + + # Add single cylinder and 3 sensors + # Object order: cylinder(0), sensor0(1), sensor1(2), sensor2(3) + ff.add_cylinder((TARGET_CYLINDER_CENTER[0], TARGET_CYLINDER_CENTER[1], 0.0), + TARGET_CYLINDER_RADIUS) + n_obj = ff.obs.size // 2 + print(f"Objects after cylinder: {n_obj}") + + for sc in SENSOR_CENTERS_CLOAK: + ff.add_sensor((sc[0], sc[1], 0.0), SENSOR_RADIUS) + n_obj = ff.obs.size // 2 + print(f"Objects after sensors: {n_obj}") + + # Stabilize + stabilize_steps = int(4 * NX / U0) + print(f"Stabilising ({stabilize_steps} steps)...") + ff.run(stabilize_steps, np.zeros(n_obj, dtype=np.float32)) + + # Record sensor time series for frequency detection + n_record_steps = 300 # enough for reliable FFT + sensor_list = [] + force_list = [] + field_list_ux = [] + field_list_uy = [] + + print(f"Recording {n_record_steps} steps x {SAMPLE_INTERVAL} LBM steps each...") + print(f" (this will take a few minutes)") + + for step in range(n_record_steps): + ff.run(SAMPLE_INTERVAL, np.zeros(n_obj, dtype=np.float32)) + # Target cylinder env: 4 objects (cylinder id=0, sensors id=1,2,3) + # obs layout: [cyl_fx, cyl_fy, s0_ux, s0_uy, s1_ux, s1_uy, s2_ux, s2_uy] + sensor_list.append(ff.obs.copy()[2:8]) # 3 sensors x 2 = 6 values + force_list.append(ff.obs.copy()[0:2]) # cylinder force + # Save field every 3 steps to keep memory manageable + if step % 3 == 0: + ux, uy = get_velocity_field(ff, u0=U0) + field_list_ux.append(ux) + field_list_uy.append(uy) + + sensors = np.array(sensor_list, dtype=np.float32) + forces = np.array(force_list, dtype=np.float32) + print(f"Sensors shape: {sensors.shape}, Forces shape: {forces.shape}") + + # --- Frequency detection --- + # Use centre sensor v-component (sensor1_uy = index 3 in obs[0:6]) + mid_sensor_vy = sensors[:, 3] + + f_dom, T_dom, peak_power = detect_dominant_frequency(mid_sensor_vy, SAMPLE_INTERVAL) + cv_T, mean_T, cycle_lengths = detect_cycle_stability(mid_sensor_vy, SAMPLE_INTERVAL) + + # Strouhal number (using single cylinder diameter) + St = f_dom * TARGET_CYLINDER_RADIUS * 2 / U0 # D=2*R=40, wait no... + + # Let me recalculate: D = 2 * radius = 2 * L0 = 40 lattice units + # But wait, TARGET_CYLINDER_RADIUS = L0, so D = 2*L0 = 40 + # And U0 = 0.01 + # St = f_dom * D / U0 + # But in the code Re uses D_REF=2D=40, and the single cylinder D=20... + # Let me check: knowledge.md says D (single cylinder) = 20 lattice units + # Actually TARGET_CYLINDER_RADIUS = 1*L0 = 20, so D = 40? No... + # Wait, radius=20 means diameter=40. But knowledge.md says single cylinder D=20... + # Let me re-check. L0=20. TARGET_CYLINDER_RADIUS = 1.0*L0 = 20. + # So the cylinder "diameter" in lattice units is 2*radius = 40. + # But knowledge.md says D=20... Let me check the legacy_env_imit_target.py + # It says `self.flow_field.add_cylinder(center, 1*L0)` where L0=20 + # So radius=20, diameter=40. + # For Re=100 (code), D_REF=40, so this matches. + # For single cylinder diameter in St definition: + # The diameter is the cylinder's diameter = 2*radius = 40 + # St = f * D / U0 = f * 40 / 0.01 + + D_cylinder = float(TARGET_CYLINDER_RADIUS * 2) # diameter = 40 + St = f_dom * D_cylinder / U0 + + result = { + "f_ref": float(f_dom), + "T_ref": float(T_dom), + "T_ref_steps": float(T_dom / SAMPLE_INTERVAL) if T_dom > 0 else 0, + "St": float(St), + "peak_power": float(peak_power), + "CV_T": float(cv_T), + "mean_T_samples": float(mean_T / SAMPLE_INTERVAL) if mean_T > 0 else 0, + "viscosity": float(viscosity), + "U0": float(U0), + "cylinder_radius": float(TARGET_CYLINDER_RADIUS), + "cylinder_diameter": float(D_cylinder), + "grid": [NX, NY], + "sample_interval": SAMPLE_INTERVAL, + "n_record_steps": n_record_steps, + } + + print(f"\n=== Phase 0 Results ===") + print(f" f_ref = {f_dom:.6f} (cycles per LBM step)") + print(f" T_ref = {T_dom:.2f} LBM steps") + print(f" T_ref_samples = {T_dom/SAMPLE_INTERVAL:.2f} samples") + print(f" St = {St:.4f}") + print(f" CV_T = {cv_T:.4f}") + print(f" Mean T in samples = {result['mean_T_samples']:.2f}") + + if cv_T > 0.05: + print(" WARNING: CV_T > 0.05, cycle stability is marginal") + if St < 0.10 or St > 0.20: + print(" WARNING: Strouhal number out of expected range [0.10, 0.20]") + + # Strip field data before saving (too large) + result_no_fields = {k: v for k, v in result.items() + if not isinstance(v, np.ndarray)} + + # Save metadata + out_dir = os.path.join(OUTPUT_DIR, "target_cylinder") + os.makedirs(out_dir, exist_ok=True) + with open(os.path.join(out_dir, "meta.json"), "w") as f: + json.dump(result_no_fields, f, indent=2) + + # Save raw sensors and forces + np.savez(os.path.join(out_dir, "raw_sensors.npz"), + sensors=sensors, + forces=forces, + sample_interval=SAMPLE_INTERVAL) + + # Save fields (keep in memory, also save for later use) + ux_all = np.stack(field_list_ux, axis=0) + uy_all = np.stack(field_list_uy, axis=0) + np.savez_compressed(os.path.join(out_dir, "fields.npz"), + ux=ux_all, uy=uy_all) + + # Cleanup + del ff + + return result + + +def main(): + ap = argparse.ArgumentParser(description="Phase 0: Standard frequency") + ap.add_argument("--device", type=int, default=2, help="GPU device ID") + args = ap.parse_args() + + t0 = time.time() + result = run_phase0(device_id=args.device) + elapsed = time.time() - t0 + print(f"\nPhase 0 complete in {elapsed:.1f}s") + print(f"f_ref = {result['f_ref']:.6f}") + print(f"T_ref = {result['T_ref']:.2f} LBM steps = {result['T_ref_steps']:.2f} samples") + print(f"St = {result['St']:.4f}") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/src/CCD_analysis/scripts/phase1_collect.py b/src/CCD_analysis/scripts/phase1_collect.py new file mode 100644 index 0000000..7bfc6d2 --- /dev/null +++ b/src/CCD_analysis/scripts/phase1_collect.py @@ -0,0 +1,769 @@ +# CCD_analysis/scripts/phase1_collect.py +"""Phase 1: Data collection for all 4 analysis cases. + +Usage:: + conda run -n pycuda_3_10 python phase1_collect.py --case illusion --device 2 + conda run -n pycuda_3_10 python phase1_collect.py --case cloak --device 3 + conda run -n pycuda_3_10 python phase1_collect.py --case uncontrolled --device 3 + conda run -n pycuda_3_10 python phase1_collect.py --case target_cylinder --device 2 +""" + +from __future__ import annotations + +import argparse +import json +import os +import sys +import time +from collections import deque +from typing import Any, Dict, List, Optional, Tuple + +import numpy as np + +# Add project root +_REPO = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..")) +if _REPO not in sys.path: + sys.path.insert(0, _REPO) + +# Add analysis dir +_ANALYSIS = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) +if _ANALYSIS not in sys.path: + sys.path.insert(0, _ANALYSIS) + +from LegacyCelerisLab import FlowField # noqa: E402 + +from scripts.cfg import ( # noqa: E402 + CONFIG_DIR, OUTPUT_DIR, U0, L0, NX, NY, CENTER_Y, DATA_TYPE, + PINBALL_RADIUS, FRONT_CENTER, BOTTOM_CENTER, TOP_CENTER, + ILLUSION_FRONT, ILLUSION_BOTTOM, ILLUSION_TOP, + SENSOR_RADIUS, SENSOR_CENTERS_CLOAK, SENSOR_CENTERS_ILLUSION, + TARGET_CYLINDER_CENTER, TARGET_CYLINDER_RADIUS, + SAMPLE_INTERVAL, SAMPLE_INTERVAL_ILLUSION, + ACTION_SCALE_CLOAK, ACTION_BIAS_CLOAK, + ACTION_SCALE_ILLUSION, ACTION_BIAS_ILLUSION, + MODEL_CLOAK_RE100, MODEL_ILLUSION_1L, + STABILIZE_STEPS, FIFO_LEN, N_PTS_PER_CYCLE, + nu_from_re, +) +from scripts.utils import ( # noqa: E402 + load_configs, get_velocity_field, detect_cycle_stability, +) + +# --------------------------------------------------------------------------- +# PPO model loader (with Sin activation) +# --------------------------------------------------------------------------- + +def _load_ppo_model(model_path: str, device: str, s_dim: int = 12, a_dim: int = 3): + """Load PPO model with Sin activation.""" + import torch + from torch.nn import Module + from stable_baselines3 import PPO + import gymnasium as gym + from gymnasium import spaces + + class Sin(Module): + def forward(self, x): + return torch.sin(x) + + class DummyEnv(gym.Env): + def __init__(self): + super().__init__() + self.observation_space = spaces.Box( + low=-1, high=1, shape=(s_dim,), dtype=np.float32) + self.action_space = spaces.Box( + low=-1, high=1, shape=(a_dim,), dtype=np.float32) + def reset(self, seed=None): + return np.zeros(s_dim, dtype=np.float32), {} + def step(self, action): + return np.zeros(s_dim, dtype=np.float32), 0.0, False, False, {} + def render(self): + pass + + dummy = DummyEnv() + model = PPO.load(model_path, env=dummy, device=device) + return model + + +# --------------------------------------------------------------------------- +# Field saving interval calculator +# --------------------------------------------------------------------------- + +def _calc_save_interval(T_ref: float, n_pts_per_cycle: int = 24) -> int: + """Calculate field save interval to get ~n_pts_per_cycle per cycle.""" + interval = int(T_ref / n_pts_per_cycle) + return max(1, interval) + + +# --------------------------------------------------------------------------- +# Phase 1a: Illusion +# --------------------------------------------------------------------------- + +def collect_illusion(device_id: int, data: dict) -> dict: + """Collect illusion case data with proper norm computation and PPO inference. + + Follows legacy_env_imit.py __init__ + step() logic exactly: + 1. Target cylinder recording (separate FlowField) + 2. FFT harmonics on target signals + 3. Pinball env with norm computation + 4. Bias-action FIFO initialization + 5. PPO deterministic rollout with 14-dim normalized observations + """ + actual_U0 = 0.02 # model is 2U + viscosity = nu_from_re(100.0, u0=actual_U0) + sample_interval = SAMPLE_INTERVAL_ILLUSION # 600 + fifo_len = 150 + conv_len = 36 + + # ---- Step 1: Target cylinder recording ---- + print("--- Record target cylinder ---") + target_U0 = actual_U0 + target_nu = viscosity + cuda_cfg, field_cfg = load_configs(CONFIG_DIR) + field_cfg = field_cfg._replace(viscosity=float(target_nu), + velocity=float(target_U0)) + ff_target = FlowField(field_cfg, cuda_cfg, device_id=device_id) + + # Target cylinder: center=(20*L0, CENTER_Y), radius=1.0*L0 + L0 = 20.0 + ff_target.add_cylinder( + (20.0 * L0, (512 - 1) / 2, 0.0), 1.0 * L0 + ) + # 3 sensors at x=30*L0 + for y_off in [2.0, 0.0, -2.0]: + ff_target.add_sensor( + (30.0 * L0, (512 - 1) / 2 + y_off * L0, 0.0), L0 / 4.0 + ) + n_obj_target = ff_target.obs.size // 2 # 4 + # Stabilize + ff_target.run(int(4 * 1280 / target_U0), np.zeros(n_obj_target, dtype=np.float32)) + + # Record 150 steps of obs[0:8] (3 sensors + 1 cylinder force) + target_states = np.empty((0, 8), dtype=np.float32) + for _ in range(fifo_len): + ff_target.run(sample_interval, np.zeros(n_obj_target, dtype=np.float32)) + new_state = ff_target.obs.copy()[0:8] + target_states = np.vstack((target_states, new_state)) + + # FFT harmonics analysis + def analyze_harmonics(states, n_harmonics=5): + N, D = states.shape + result = [] + for d in range(D): + y = states[:, d] + fft_coef = np.fft.rfft(y) + freqs = np.fft.rfftfreq(N, d=1) + amps = 2.0 * np.abs(fft_coef) / N + phases = np.angle(fft_coef) + idx = np.argsort(amps[1:])[::-1][:n_harmonics] + 1 + harmonics = { + 'dc': float(np.real(fft_coef[0]) / N), + 'amps': amps[idx].tolist(), + 'freqs': freqs[idx].tolist(), + 'phases': phases[idx].tolist(), + } + result.append(harmonics) + return result + + target_harmonics = analyze_harmonics(target_states, n_harmonics=5) + del ff_target + print(f" target harmonics computed for {len(target_harmonics)} channels") + + # ---- Step 2: Pinball env creation ---- + print("--- Build pinball env ---") + ff = FlowField(field_cfg, cuda_cfg, device_id=device_id) + + for y_off in [2.0, 0.0, -2.0]: + ff.add_sensor( + (30.0 * L0, (512 - 1) / 2 + y_off * L0, 0.0), L0 / 4.0 + ) + ff.add_cylinder((19.0 * L0, (512 - 1) / 2, 0.0), L0 / 2.0) + ff.add_cylinder((20.3 * L0, (512 - 1) / 2 + 0.75 * L0, 0.0), L0 / 2.0) + ff.add_cylinder((20.3 * L0, (512 - 1) / 2 - 0.75 * L0, 0.0), L0 / 2.0) + + n_obj = ff.obs.size // 2 # 6 + assert n_obj == 6, f"Expected 6 objects, got {n_obj}" + + # Stabilize + ff.run(int(4 * 1280 / actual_U0), np.zeros(n_obj, dtype=np.float32)) + ff.get_ddf() + ff.save_ddf() # checkpoint + + # ---- Step 3: Norm computation (zero-action rollout) ---- + print("--- Compute norm ---") + fifo = deque(maxlen=fifo_len) + for _ in range(fifo_len): + ff.run(sample_interval, np.zeros(n_obj, dtype=np.float32)) + fifo.append(ff.obs.copy()[0:12]) + + temp_states = np.array(fifo, dtype=np.float32) + force_norm_fact = 6.0 * float(np.max(np.abs(temp_states[:, 6:12]))) + sens_deviation = np.mean(temp_states[:, 0:6], axis=0).astype(np.float32) + sens_norm_fact = np.zeros(6, dtype=np.float32) + for i in range(6): + sens_norm_fact[i] = 5.0 * float(np.max(np.abs(temp_states[:, i] - sens_deviation[i]))) + + print(f" force_norm_fact={force_norm_fact:.6f}") + print(f" sens_deviation={sens_deviation}") + print(f" sens_norm_fact={sens_norm_fact}") + + # ---- Step 4: Bias-action FIFO initialization ---- + print("--- Bias-action FIFO init ---") + ff.apply_ddf() + # bias action from legacy env: [0, 0, 0, 0, -1*U0, 1*U0] + bias_arr = np.zeros(n_obj, dtype=np.float32) + bias_arr[4] = -1.0 * actual_U0 # bottom + bias_arr[5] = 1.0 * actual_U0 # top + + fifo.clear() + for _ in range(fifo_len): + ff.run(sample_interval, bias_arr) + fifo.append(ff.obs.copy()[0:12]) + + save_states = list(fifo) + ff.apply_ddf() # restore checkpoint for reset + + # ---- Step 5: PPO inference with adaptive sampling ---- + print("--- PPO deterministic rollout (adaptive sampling) ---") + import torch + device_str = f"cuda:{device_id}" if torch.cuda.is_available() else "cpu" + model = _load_ppo_model(MODEL_ILLUSION_1L, device=device_str, s_dim=14, a_dim=3) + model.set_random_seed(19) + + n_steps = 200 + + # Compute adaptive field sampling interval from expected period + # St = 0.267, D = 40, expected f = St * U0 / D + f_expected = 0.2667 * actual_U0 / 40.0 + T_expected = int(1.0 / f_expected) if f_expected > 0 else 7500 + field_interval = max(1, int(T_expected / N_PTS_PER_CYCLE)) + print(f" T_expected={T_expected} steps, field_interval={field_interval} " + f"(~{T_expected/field_interval:.0f} pts/cycle)") + + # Data at PPO-action cadence (once per 600 steps, for PPO state only) + ppo_actions = [] + ppo_sensors_600 = [] + + # Dense data at field_interval cadence (for phase analysis) + dense_sensors = [] + dense_forces = [] + dense_ux = [] + dense_uy = [] + + # Re-initialize FIFO for inference + fifo = deque(maxlen=fifo_len) + for state in save_states: + fifo.append(np.array(state, dtype=np.float32)) + + obs = np.zeros(14, dtype=np.float32) + + for step in range(n_steps): + # PPO action + action, _states = model.predict(obs, deterministic=True) + action = action.astype(np.float32).flatten() + ppo_actions.append(action.copy()) + + # Convert to physical omega + temp = np.zeros(n_obj, dtype=np.float32) + omega = (action * ACTION_SCALE_ILLUSION + + np.array(ACTION_BIAS_ILLUSION, dtype=np.float32)) * actual_U0 + temp[3:6] = omega + + # Run CFD with dense intra-step sampling + ff.context.push() + try: + # First chunk + ff.run(field_interval, temp) + ux, uy = get_velocity_field(ff, u0=actual_U0) + dense_ux.append(ux) + dense_uy.append(uy) + dense_sensors.append(ff.obs.copy()[0:6]) + dense_forces.append(ff.obs.copy()[6:12]) + + # Second chunk (remaining) + remaining = sample_interval - field_interval + if remaining > 0: + ff.run(remaining, temp) + ux, uy = get_velocity_field(ff, u0=actual_U0) + dense_ux.append(ux) + dense_uy.append(uy) + dense_sensors.append(ff.obs.copy()[0:6]) + dense_forces.append(ff.obs.copy()[6:12]) + finally: + ff.context.pop() + + # PPO state: use last obs_slice + last_sens = dense_sensors[-1] + last_force = dense_forces[-1] + obs_slice = np.concatenate([last_sens, last_force]) + fifo.append(obs_slice) + ppo_sensors_600.append(obs_slice) + + # Build normalized 14-dim observation for next PPO step + forces_norm = last_force / force_norm_fact + sens_norm = (last_sens - sens_deviation) / sens_norm_fact + target_recon = _gen_target_states_at(step, target_harmonics) + target_cd_norm = float(target_recon[0]) / force_norm_fact + target_cl_norm = float(target_recon[1]) / force_norm_fact + obs = np.clip( + np.hstack([forces_norm, sens_norm, target_cd_norm, target_cl_norm]), + -1.0, 1.0, + ).astype(np.float32) + + if step % 20 == 0: + print(f" step {step}/{n_steps}, action={action[0]:.3f} {action[1]:.3f} {action[2]:.3f}") + + # Save dense data (for phase resampling) + ux_all = np.stack(dense_ux, axis=0) + uy_all = np.stack(dense_uy, axis=0) + dense_sensors_arr = np.array(dense_sensors, dtype=np.float32) + dense_forces_arr = np.array(dense_forces, dtype=np.float32) + ppo_actions_arr = np.array(ppo_actions, dtype=np.float32) + n_dense_per_step = len(dense_sensors) // n_steps + dense_dt = sample_interval / n_dense_per_step if n_dense_per_step > 0 else sample_interval + print(f" Dense sampling: {len(dense_sensors)} samples, " + f"{n_dense_per_step} per PPO step, dt={dense_dt:.0f} LBM steps") + + out_dir = os.path.join(OUTPUT_DIR, "illusion") + os.makedirs(out_dir, exist_ok=True) + np.savez_compressed(os.path.join(out_dir, "fields.npz"), ux=ux_all, uy=uy_all) + np.savez(os.path.join(out_dir, "dense_sensors.npz"), + sensors=dense_sensors_arr, forces=dense_forces_arr, + dense_dt=dense_dt, + sample_interval=sample_interval) + + # Save PPO-step-cadence data and metadata + np.savez(os.path.join(out_dir, "sensors.npz"), + sensors=dense_sensors_arr.reshape(n_steps, -1, 6)[:, -1], + forces=dense_forces_arr.reshape(n_steps, -1, 6)[:, -1], + actions=ppo_actions_arr, + sample_interval=sample_interval, + force_norm_fact=np.array([force_norm_fact], dtype=np.float32), + sens_deviation=np.array(sens_deviation, dtype=np.float32), + sens_norm_fact=np.array(sens_norm_fact, dtype=np.float32)) + + # Save target data for later use + np.savez(os.path.join(out_dir, "target_harmonics.npz"), + target_states=target_states, + harmonics_data=np.array(target_harmonics, dtype=object)) + + meta = { + "case": "illusion", + "model": str(MODEL_ILLUSION_1L), + "n_steps": n_steps, + "n_fields": len(dense_ux), + "n_dense_samples": len(dense_sensors), + "dense_dt": dense_dt, + "T_expected": T_expected, + "field_interval": field_interval, + "sample_interval": sample_interval, + "action_scale": ACTION_SCALE_ILLUSION, + "action_bias": list(ACTION_BIAS_ILLUSION), + "U0": actual_U0, + "viscosity": viscosity, + "n_obj": n_obj, + "force_norm_fact": force_norm_fact, + "sens_deviation": sens_deviation.tolist(), + "sens_norm_fact": sens_norm_fact.tolist(), + } + with open(os.path.join(out_dir, "meta.json"), "w") as f: + json.dump(meta, f, indent=2) + + print(f" Saved {len(dense_ux)} fields, {len(dense_sensors)} dense samples") + del ff, model + return meta + + +def _gen_target_states_at(t, harmonics): + """Reconstruct target observable at step index t from harmonics. + + Mirrors legacy_env_imit.py gen_target_states_at(). + """ + t = np.asarray(t) + D = len(harmonics) + result = np.zeros((t.size, D), dtype=np.float32) + for d, h in enumerate(harmonics): + val = np.full(t.shape, h['dc'], dtype=np.float32) + amps = h['amps'] + freqs = h['freqs'] + phases = h['phases'] + for amp, freq, phase in zip(amps, freqs, phases): + val += amp * np.cos(2 * np.pi * freq * t + phase) + result[:, d] = val + if result.shape[0] == 1: + return result[0] + return result + + +# --------------------------------------------------------------------------- +# Phase 1b: Cloak (steady flow case) +# --------------------------------------------------------------------------- + +def collect_cloak(device_id: int, data: dict) -> dict: + """Collect cloak case data (PPO -> steady action -> mean flow).""" + viscosity = nu_from_re(100.0) + sample_interval = SAMPLE_INTERVAL + + import torch + device_str = f"cuda:{device_id}" if torch.cuda.is_available() else "cpu" + model = _load_ppo_model(MODEL_CLOAK_RE100, device=device_str, s_dim=12, a_dim=3) + model.set_random_seed(0) + + # Create env: 6 objects (3 sensors + 3 pinball, NO disturbance cylinder) + cuda_cfg, field_cfg = load_configs(CONFIG_DIR) + field_cfg = field_cfg._replace(viscosity=float(viscosity)) + ff = FlowField(field_cfg, cuda_cfg, device_id=device_id) + + for sc in SENSOR_CENTERS_CLOAK: + ff.add_sensor((sc[0], sc[1], 0.0), SENSOR_RADIUS) + ff.add_cylinder((FRONT_CENTER[0], FRONT_CENTER[1], 0.0), PINBALL_RADIUS) + ff.add_cylinder((BOTTOM_CENTER[0], BOTTOM_CENTER[1], 0.0), PINBALL_RADIUS) + ff.add_cylinder((TOP_CENTER[0], TOP_CENTER[1], 0.0), PINBALL_RADIUS) + + n_obj = ff.obs.size // 2 + assert n_obj == 6, f"Expected 6 objects for cloak, got {n_obj}" + + # Stabilize + ff.run(STABILIZE_STEPS, np.zeros(n_obj, dtype=np.float32)) + + # ---- PPO deterministic rollout to find steady action ---- + n_ppo_steps = 200 + print(f"Running {n_ppo_steps} PPO steps to extract steady action...") + + obs = np.zeros(12, dtype=np.float32) + actions_list = [] + sensors_list = [] + forces_list = [] + + for step in range(n_ppo_steps): + action, _states = model.predict(obs, deterministic=True) + action = action.astype(np.float32).flatten() + actions_list.append(action.copy()) + + temp = np.zeros(n_obj, dtype=np.float32) + omega = (action * ACTION_SCALE_CLOAK + + np.array(ACTION_BIAS_CLOAK, dtype=np.float32)) * U0 + temp[3:6] = omega + + ff.context.push() + try: + ff.run(sample_interval, temp) + finally: + ff.context.pop() + + obs_slice = ff.obs.copy()[0:12] + sensors_list.append(obs_slice[0:6].copy()) + forces_list.append(obs_slice[6:12].copy()) + + # Build observation for next step + obs = np.clip(np.hstack([obs_slice[6:12], obs_slice[0:6]]), + -10.0, 10.0).astype(np.float32) + + # Extract steady action (average of last 100 steps) + actions_arr = np.array(actions_list, dtype=np.float32) + steady_action = np.mean(actions_arr[-100:], axis=0) + print(f" Steady action ([-1,1]): {steady_action[0]:.4f} {steady_action[1]:.4f} {steady_action[2]:.4f}") + print(f" Steady omega (U0 multiples): " + f"{(steady_action*ACTION_SCALE_CLOAK+np.array(ACTION_BIAS_CLOAK))[0]:.4f} " + f"{(steady_action*ACTION_SCALE_CLOAK+np.array(ACTION_BIAS_CLOAK))[1]:.4f} " + f"{(steady_action*ACTION_SCALE_CLOAK+np.array(ACTION_BIAS_CLOAK))[2]:.4f}") + + # ---- Apply steady action and record mean flow ---- + print("Applying steady action and recording...") + temp_steady = np.zeros(n_obj, dtype=np.float32) + omega_steady = (steady_action * ACTION_SCALE_CLOAK + + np.array(ACTION_BIAS_CLOAK, dtype=np.float32)) * U0 + temp_steady[3:6] = omega_steady + + # Re-stabilize with steady action (4x NX/U0) + ff.context.push() + try: + ff.run(STABILIZE_STEPS, temp_steady) + finally: + ff.context.pop() + + # Record steady state fields and sensors + n_steady_samples = 30 + steady_sensors = [] + steady_forces = [] + steady_ux = [] + steady_uy = [] + + for i in range(n_steady_samples): + ff.context.push() + try: + ff.run(sample_interval, temp_steady) + finally: + ff.context.pop() + obs_slice = ff.obs.copy()[0:12] + steady_sensors.append(obs_slice[0:6]) + steady_forces.append(obs_slice[6:12]) + ux, uy = get_velocity_field(ff, u0=U0) + steady_ux.append(ux) + steady_uy.append(uy) + + steady_sensors_arr = np.array(steady_sensors, dtype=np.float32) + steady_forces_arr = np.array(steady_forces, dtype=np.float32) + ux_all = np.stack(steady_ux, axis=0) + uy_all = np.stack(steady_uy, axis=0) + + out_dir = os.path.join(OUTPUT_DIR, "cloak") + os.makedirs(out_dir, exist_ok=True) + + np.savez_compressed(os.path.join(out_dir, "fields.npz"), + ux=ux_all, uy=uy_all) + np.savez(os.path.join(out_dir, "sensors.npz"), + sensors=steady_sensors_arr, forces=steady_forces_arr) + np.savez(os.path.join(out_dir, "ppo_rollout.npz"), + actions=actions_arr, + sensors=np.array(sensors_list, dtype=np.float32), + forces=np.array(forces_list, dtype=np.float32), + steady_action=steady_action) + + meta = { + "case": "cloak", + "model": str(MODEL_CLOAK_RE100), + "sample_interval": sample_interval, + "action_scale": ACTION_SCALE_CLOAK, + "action_bias": list(ACTION_BIAS_CLOAK), + "steady_action_norm": steady_action.tolist(), + "steady_omega_U0": (steady_action * ACTION_SCALE_CLOAK + + np.array(ACTION_BIAS_CLOAK)).tolist(), + "U0": U0, + "viscosity": viscosity, + "n_obj": n_obj, + "n_steady_samples": n_steady_samples, + } + with open(os.path.join(out_dir, "meta.json"), "w") as f: + json.dump(meta, f, indent=2) + + print(f" Steady action recorded. Mean sensors: " + f"{np.mean(steady_sensors_arr, axis=0)}") + print(f" Mean total force: " + f"Fx={np.mean(steady_forces_arr[:, 0::2]):.6f} " + f"Fy={np.mean(steady_forces_arr[:, 1::2]):.6f}") + del ff, model + return meta + + +# --------------------------------------------------------------------------- +# Phase 1c: Uncontrolled +# --------------------------------------------------------------------------- + +def collect_uncontrolled(device_id: int, data: dict) -> dict: + """Collect uncontrolled case data (zero-action baseline).""" + viscosity = nu_from_re(100.0) + sample_interval = SAMPLE_INTERVAL + T_ref = data.get("T_ref", 15000.0) + save_interval = _calc_save_interval(T_ref) + + cuda_cfg, field_cfg = load_configs(CONFIG_DIR) + field_cfg = field_cfg._replace(viscosity=float(viscosity)) + ff = FlowField(field_cfg, cuda_cfg, device_id=device_id) + + for sc in SENSOR_CENTERS_CLOAK: + ff.add_sensor((sc[0], sc[1], 0.0), SENSOR_RADIUS) + ff.add_cylinder((FRONT_CENTER[0], FRONT_CENTER[1], 0.0), PINBALL_RADIUS) + ff.add_cylinder((BOTTOM_CENTER[0], BOTTOM_CENTER[1], 0.0), PINBALL_RADIUS) + ff.add_cylinder((TOP_CENTER[0], TOP_CENTER[1], 0.0), PINBALL_RADIUS) + + n_obj = ff.obs.size // 2 + assert n_obj == 6 + + # Stabilize + ff.run(STABILIZE_STEPS, np.zeros(n_obj, dtype=np.float32)) + + # Run uncontrolled + n_steps = 200 + sensors_list = [] + forces_list = [] + ux_fields = [] + uy_fields = [] + + for step in range(n_steps): + ff.context.push() + try: + remaining = sample_interval + while remaining > 0: + chunk = min(remaining, save_interval) + ff.run(chunk, np.zeros(n_obj, dtype=np.float32)) + remaining -= chunk + ux, uy = get_velocity_field(ff, u0=U0) + ux_fields.append(ux) + uy_fields.append(uy) + finally: + ff.context.pop() + + obs_slice = ff.obs.copy()[0:12] + sensors_list.append(obs_slice[0:6]) + forces_list.append(obs_slice[6:12]) + + sensors = np.array(sensors_list, dtype=np.float32) + forces = np.array(forces_list, dtype=np.float32) + ux_all = np.stack(ux_fields, axis=0) + uy_all = np.stack(uy_fields, axis=0) + + out_dir = os.path.join(OUTPUT_DIR, "uncontrolled") + os.makedirs(out_dir, exist_ok=True) + + np.savez_compressed(os.path.join(out_dir, "fields.npz"), + ux=ux_all, uy=uy_all) + np.savez(os.path.join(out_dir, "sensors.npz"), + sensors=sensors, forces=forces) + + meta = { + "case": "uncontrolled", + "U0": U0, + "viscosity": viscosity, + "n_steps": n_steps, + "n_fields": len(ux_fields), + "sample_interval": sample_interval, + "n_obj": n_obj, + } + with open(os.path.join(out_dir, "meta.json"), "w") as f: + json.dump(meta, f, indent=2) + + print(f" Saved {len(ux_fields)} fields, {len(sensors)} sensor steps") + del ff + return meta + + +# --------------------------------------------------------------------------- +# Phase 1d: Target cylinder (reference for period detection) +# --------------------------------------------------------------------------- + +def collect_target_cylinder(device_id: int, data: dict) -> dict: + """Collect target 2D cylinder reference data. + + Most data was already collected in Phase 0. Here we just ensure + the fields are properly saved with the right naming. + """ + # Phase 0 already saved data to output/target_cylinder/ + # Just verify it exists and copy meta + out_dir = os.path.join(OUTPUT_DIR, "target_cylinder") + meta_path = os.path.join(out_dir, "meta.json") + if not os.path.exists(meta_path): + raise RuntimeError( + "Phase 0 must be run first. No target_cylinder data found." + ) + + with open(meta_path, "r") as f: + meta = json.load(f) + + print(f"Target cylinder data found at {out_dir}") + print(f" f_ref={meta['f_ref']:.6f}, T_ref={meta['T_ref']:.0f}, St={meta['St']:.4f}") + print(f" CV_T={meta['CV_T']:.4f}") + return meta + + +# --------------------------------------------------------------------------- +# Empty channel (target steady flow for cloak comparison) +# --------------------------------------------------------------------------- + +def collect_empty_channel(device_id: int) -> dict: + """Run empty channel (no bodies) and record steady parabolic flow.""" + viscosity = nu_from_re(100.0) + + cuda_cfg, field_cfg = load_configs(CONFIG_DIR) + field_cfg = field_cfg._replace(viscosity=float(viscosity)) + ff = FlowField(field_cfg, cuda_cfg, device_id=device_id) + + # Need at least one sensor (legacy API requirement) + ff.add_sensor((NX - 10, CENTER_Y, 0.0), SENSOR_RADIUS) + n_obj = ff.obs.size // 2 + ff.run(STABILIZE_STEPS, np.zeros(n_obj, dtype=np.float32)) + + # Record a few fields + ux_list, uy_list = [], [] + for i in range(5): + ff.run(SAMPLE_INTERVAL, np.zeros(n_obj, dtype=np.float32)) + ux, uy = get_velocity_field(ff, u0=U0) + ux_list.append(ux) + uy_list.append(uy) + + ux_all = np.stack(ux_list, axis=0) + uy_all = np.stack(uy_list, axis=0) + + out_dir = os.path.join(OUTPUT_DIR, "empty_channel") + os.makedirs(out_dir, exist_ok=True) + + np.savez_compressed(os.path.join(out_dir, "fields.npz"), + ux=ux_all, uy=uy_all) + + meta = { + "case": "empty_channel", + "U0": U0, + "viscosity": viscosity, + "n_fields": len(ux_list), + } + with open(os.path.join(out_dir, "meta.json"), "w") as f: + json.dump(meta, f, indent=2) + + print("Empty channel flow recorded") + del ff + return meta + + +# --------------------------------------------------------------------------- +# Main +# --------------------------------------------------------------------------- + +def main(): + ap = argparse.ArgumentParser(description="Phase 1: Data collection") + ap.add_argument("--case", type=str, required=True, + choices=["all", "illusion", "cloak", "uncontrolled", + "target_cylinder", "empty_channel"], + help="Case to collect") + ap.add_argument("--device", type=int, default=2, help="GPU device ID") + args = ap.parse_args() + + # Load Phase 0 data for f_ref / T_ref + f_ref_path = os.path.join(OUTPUT_DIR, "target_cylinder", "meta.json") + if os.path.exists(f_ref_path): + with open(f_ref_path, "r") as f: + phase0_data = json.load(f) + else: + phase0_data = {"T_ref": 15000.0, "f_ref": 6.67e-5} + print("WARNING: Phase 0 not found, using default T_ref=15000") + + t0 = time.time() + results = {} + + if args.case in ("all", "illusion"): + print("=" * 60) + print("Collecting Illusion case...") + print("=" * 60) + phase0_data["illusion_2u"] = True + results["illusion"] = collect_illusion(args.device, phase0_data) + + if args.case in ("all", "cloak"): + print("=" * 60) + print("Collecting Cloak case...") + print("=" * 60) + results["cloak"] = collect_cloak(args.device, phase0_data) + + if args.case in ("all", "uncontrolled"): + print("=" * 60) + print("Collecting Uncontrolled case...") + print("=" * 60) + results["uncontrolled"] = collect_uncontrolled(args.device, phase0_data) + + if args.case in ("all", "target_cylinder"): + print("=" * 60) + print("Collecting Target Cylinder...") + print("=" * 60) + results["target_cylinder"] = collect_target_cylinder( + args.device, phase0_data) + + if args.case in ("all", "empty_channel"): + print("=" * 60) + print("Collecting Empty Channel (steady target)...") + print("=" * 60) + results["empty_channel"] = collect_empty_channel(args.device) + + elapsed = time.time() - t0 + print(f"\nPhase 1 complete in {elapsed:.1f}s") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/src/CCD_analysis/scripts/phase2_resample.py b/src/CCD_analysis/scripts/phase2_resample.py new file mode 100644 index 0000000..4cc0301 --- /dev/null +++ b/src/CCD_analysis/scripts/phase2_resample.py @@ -0,0 +1,514 @@ +# CCD_analysis/scripts/phase2_resample.py +"""Phase 2: Period detection and phase resampling for periodic cases. + +Usage:: + python phase2_resample.py + +Output:: + - output/resampled/ — phase-resampled data for each qualifying case + - Console report of period stability for all periodic cases +""" + +from __future__ import annotations + +import json +import os +import sys +import time + +import numpy as np + +_ANALYSIS = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) +if _ANALYSIS not in sys.path: + sys.path.insert(0, _ANALYSIS) + +from scripts.cfg import ( # noqa: E402 + OUTPUT_DIR, U0, SAMPLE_INTERVAL, SAMPLE_INTERVAL_ILLUSION, + N_TARGET_CYCLES, N_PTS_PER_CYCLE, TOTAL_PHASE_FRAMES, +) +from scripts.analysis_utils import ( # noqa: E402 + detect_dominant_frequency, detect_cycle_stability, phase_resample, +) + + +# --------------------------------------------------------------------------- +# Gate criteria +# --------------------------------------------------------------------------- + +CV_T_THRESHOLD_STRICT = 0.10 +CV_T_THRESHOLD_RELAXED = 0.12 +DELTA_F_THRESHOLD_STRICT = 0.10 +DELTA_F_THRESHOLD_RELAXED = 0.20 + + +# --------------------------------------------------------------------------- +# Load raw data for a case +# --------------------------------------------------------------------------- + +def load_case_raw(case_name: str) -> dict: + """Load raw sensor/force/action data for a case.""" + case_dir = os.path.join(OUTPUT_DIR, case_name) + meta_path = os.path.join(case_dir, "meta.json") + + if not os.path.exists(meta_path): + return {"exists": False, "error": f"{case_dir}/meta.json not found"} + + with open(meta_path, "r") as f: + meta = json.load(f) + + result = {"exists": True, "meta": meta, "name": case_name} + + # Load sensors (check both naming conventions) + sens_path = os.path.join(case_dir, "sensors.npz") + dense_sens_path = os.path.join(case_dir, "dense_sensors.npz") + raw_sens_path = os.path.join(case_dir, "raw_sensors.npz") + if os.path.exists(dense_sens_path): + load_path = dense_sens_path + elif os.path.exists(sens_path): + load_path = sens_path + elif os.path.exists(raw_sens_path): + load_path = raw_sens_path + else: + load_path = None + + if load_path is not None: + data = np.load(load_path) + if "sensors" in data: + result["sensors"] = data["sensors"] + if "forces" in data: + result["forces"] = data["forces"] + if "actions" in data: + result["actions"] = data["actions"] + # Determine sample interval (dense data may use dense_dt) + if "dense_dt" in data: + result["sample_interval"] = int(data["dense_dt"]) + elif "sample_interval" in data: + result["sample_interval"] = int(data["sample_interval"]) + + # If we loaded dense data but missing actions, try sensors.npz + if load_path == dense_sens_path and "actions" not in result: + if os.path.exists(sens_path): + extra = np.load(sens_path) + if "actions" in extra: + result["actions"] = extra["actions"] + print(f" loaded actions from sensors.npz: {result['actions'].shape}") + + # Determine sample interval from meta if not in data + if "sample_interval" not in result: + result["sample_interval"] = meta.get("sample_interval", SAMPLE_INTERVAL) + + # Get U0 from meta + result["U0"] = meta.get("U0", 0.01) + + # Load fields (lazy — only when accessed) + fields_path = os.path.join(case_dir, "fields.npz") + if os.path.exists(fields_path): + result["_fields_path"] = fields_path + result["_fields_loader"] = lambda p=fields_path: np.load(p) + + return result + + +# --------------------------------------------------------------------------- +# Check period stability for a case +# --------------------------------------------------------------------------- + +def check_period_stability( + case_data: dict, f_ref: float, T_ref: float, St: float = 0.2667, + case_U0: float = 0.01 +) -> dict: + """Check period stability and return gate result. + + delta_f computed relative to expected frequency from Strouhal number: + f_expected = St * U0 / D_cylinder + This handles cases at different U0 (e.g. illusion 2U at U0=0.02). + + Returns dict with gate info. + """ + sensors = case_data.get("sensors") + if sensors is None or len(sensors) < 30: + return {"gate": "no_data", "reason": "insufficient sensor data"} + + sample_interval = case_data.get("sample_interval", SAMPLE_INTERVAL) + D_cyl = 40.0 # cylinder diameter in lattice units (2*L0) + + signal = sensors[:, 3] + + # Detect frequency + f_case, T_case, peak_power = detect_dominant_frequency(signal, sample_interval) + + # Detect cycle stability + cv_T, mean_T, cycle_lengths = detect_cycle_stability(signal, sample_interval) + + # Expected frequency from Strouhal number at this U0 + f_expected = St * case_U0 / D_cyl + + # Gate — compare to expected frequency from target cylinder St + delta_f = abs(f_case - f_expected) / f_expected if f_expected > 0 else 1.0 + + # Gate determination with new thresholds + if cv_T <= CV_T_THRESHOLD_STRICT and delta_f <= DELTA_F_THRESHOLD_STRICT: + gate = "strict" + elif cv_T <= CV_T_THRESHOLD_RELAXED and delta_f <= DELTA_F_THRESHOLD_RELAXED: + gate = "relaxed" + else: + gate = "auxiliary" + + # Interpolation quality check + N_raw_per_cycle = mean_T / sample_interval if mean_T > 0 else 0 + rho_interp = 24.0 / N_raw_per_cycle if N_raw_per_cycle > 0 else 99.0 + if rho_interp > 2.0: + interp_quality = "reject" + elif rho_interp > 1.5: + interp_quality = "borderline" + elif rho_interp > 1.2: + interp_quality = "acceptable" + else: + interp_quality = "ideal" + + # Also check signal quality + signal_range = float(np.max(signal) - np.min(signal)) + signal_rms = float(np.std(signal)) + + result = { + "case": case_data.get("name", "unknown"), + "gate": gate, + "f_case": float(f_case), + "f_expected": float(f_expected), + "T_case": float(T_case), + "T_case_samples": float(T_case / sample_interval), + "CV_T": float(cv_T), + "delta_f": float(delta_f), + "mean_T_samples": float(mean_T / sample_interval), + "N_raw_per_cycle": float(N_raw_per_cycle), + "rho_interp": float(rho_interp), + "interp_quality": interp_quality, + "n_cycles_detected": len(cycle_lengths), + "signal_range": signal_range, + "signal_rms": signal_rms, + "n_raw_samples": len(sensors), + "St": St, + "U0": float(case_U0), + } + + return result + + +# --------------------------------------------------------------------------- +# Extract cycles and resample +# --------------------------------------------------------------------------- + +def extract_and_resample( + case_data: dict, f_ref: float, T_ref: float, St: float = 0.2667, + n_cycles: int = N_TARGET_CYCLES, + n_pts: int = N_PTS_PER_CYCLE, +) -> dict: + """Extract cycles and resample to uniform phase grid. + + Parameters + ---------- + case_data : dict + Raw case data with sensors, forces, actions, ux, uy. + f_ref, T_ref : float + Reference frequency and period (from Phase 0 at U0=0.01). + St : float + Strouhal number (U0-invariant reference). + n_cycles, n_pts : int + Number of cycles and points per cycle. + + Returns + ------- + dict with resampled fields, sensors, forces, actions. + """ + sensors = case_data.get("sensors") + sample_interval = case_data.get("sample_interval", SAMPLE_INTERVAL) + case_U0 = case_data.get("U0", 0.01) + D_cyl = 40.0 + + # Compute expected T for this case's U0 + f_expected = St * case_U0 / D_cyl + T_expected = 1.0 / f_expected if f_expected > 0 else T_ref + + if sensors is None or len(sensors) < 30: + return {"resampled": False, "reason": "insufficient data"} + + signal = sensors[:, 3] # centre sensor v + + # Find rising zero-crossings to define cycle boundaries + y = signal - np.mean(signal) + sign = np.sign(y) + crossings = np.where((sign[:-1] < 0) & (sign[1:] > 0))[0] + + if len(crossings) < n_cycles + 1: + print(f" Only {len(crossings)} crossings found, need {n_cycles + 1}") + return {"resampled": False, "reason": f"need {n_cycles + 1} crossings, got {len(crossings)}"} + + # Select the most representative n_cycles (use expected period) + cycle_lengths = np.diff(crossings) + T_exp_samples = T_expected / sample_interval + + # Score each window of n_cycles consecutive cycles + best_score = float("inf") + best_start = 0 + for i in range(len(cycle_lengths) - n_cycles + 1): + window = cycle_lengths[i:i + n_cycles] + score = np.sum((window - T_exp_samples) ** 2) + if score < best_score: + best_score = score + best_start = i + + selected_crossings = list(crossings[best_start:best_start + n_cycles + 1]) + + # Phase resample sensors + if sensors.ndim == 2: + resampled_sensors = phase_resample( + sensors, selected_crossings, n_pts=n_pts + ) + else: + resampled_sensors = phase_resample( + sensors[:, None], selected_crossings, n_pts=n_pts + ) + + result = { + "resampled": True, + "selected_crossings": selected_crossings, + "sensors": resampled_sensors, # (n_cycles, n_pts, 6) + } + + # Resample forces + forces = case_data.get("forces") + if forces is not None and forces.ndim == 2: + result["forces"] = phase_resample( + forces, selected_crossings, n_pts=n_pts + ) + + # Resample actions + actions = case_data.get("actions") + if actions is not None and actions.ndim == 2: + result["actions"] = phase_resample( + actions, selected_crossings, n_pts=n_pts + ) + + # Resample field data (lazy-loaded if available) + ux = None + uy = None + loader = case_data.get("_fields_loader") + if loader is not None: + fields_npz = loader() + if "ux" in fields_npz and "uy" in fields_npz: + ux = fields_npz["ux"] + uy = fields_npz["uy"] + if ux is not None and uy is not None: + n_fields = len(ux) + n_sensors = len(sensors) + + # Fields and sensors are sampled at different rates + # Fields are saved at save_interval within each PPO step + # Sensors are saved once per PPO step + # The ratio is approximately T_ref / n_pts / sample_interval + + # Convert crossing indices from sensor-space to field-space + # Simple approach: resample fields using the same crossing indices + # Since fields may have different count, use normalized indices + field_crossings = [ + int(c * n_fields / n_sensors) for c in selected_crossings + ] + field_crossings = [max(0, min(c, n_fields - 1)) for c in field_crossings] + # Deduplicate and ensure > n_cycles+1 entries + field_crossings = sorted(set(field_crossings)) + + if len(field_crossings) >= 2: + # Stack ux and uy into single array + nx, ny = ux.shape[1], ux.shape[2] + field_flat = np.column_stack([ + ux.reshape(n_fields, -1), uy.reshape(n_fields, -1) + ]) + resampled_fields = phase_resample( + field_flat, field_crossings[:n_cycles + 1], n_pts=n_pts + ) + # Unflatten + n_cycle_actual, n_pt, n_dim = resampled_fields.shape + half = n_dim // 2 + result["ux"] = resampled_fields[:, :, :half].reshape( + n_cycle_actual, n_pt, ny, nx + ) + result["uy"] = resampled_fields[:, :, half:].reshape( + n_cycle_actual, n_pt, ny, nx + ) + + return result + + +# --------------------------------------------------------------------------- +# Main +# --------------------------------------------------------------------------- + +def main(): + # Load Phase 0 reference data + ref_path = os.path.join(OUTPUT_DIR, "target_cylinder", "meta.json") + if not os.path.exists(ref_path): + print("ERROR: Phase 0 data not found. Run phase0_standard_freq.py first.") + return 1 + + with open(ref_path, "r") as f: + ref = json.load(f) + + f_ref = ref["f_ref"] + T_ref = ref["T_ref"] + print(f"Reference: f_ref={f_ref:.6f}, T_ref={T_ref:.0f} steps, St={ref['St']:.4f}") + print() + + # Load all periodic cases + periodic_cases = ["target_cylinder", "illusion", "uncontrolled"] + + all_raw = {} + for case in periodic_cases: + print(f"Loading {case}...") + all_raw[case] = load_case_raw(case) + if all_raw[case].get("exists"): + nf = "lazy" + ns = len(all_raw[case].get("sensors", [])) + print(f" fields={nf}, sensors={ns}") + + # Check period stability + print("\n=== Period Stability Check ===") + results = [] + for case in periodic_cases: + data = all_raw[case] + if not data.get("exists"): + print(f" {case}: NO DATA, skipping") + continue + r = check_period_stability( + data, f_ref, T_ref, St=ref["St"], + case_U0=data.get("U0", 0.01), + ) + results.append(r) + status = r["gate"].upper() + print(f" {case}: {status} f_case={r['f_case']:.6f} " + f"CV_T={r['CV_T']:.4f} delta_f={r['delta_f']:.4f} " + f"T_samples={r['mean_T_samples']:.1f} " + f"N_raw/cycle={r.get('N_raw_per_cycle', '?'):.1f} " + f"interp={r.get('interp_quality', '?')}") + + # Save stability report + os.makedirs(os.path.join(OUTPUT_DIR, "resampled"), exist_ok=True) + with open(os.path.join(OUTPUT_DIR, "resampled", "stability_report.json"), "w") as f: + json.dump({ + "f_ref": f_ref, + "T_ref": T_ref, + "St": ref["St"], + "thresholds": { + "CV_T_strict": CV_T_THRESHOLD_STRICT, + "CV_T_relaxed": CV_T_THRESHOLD_RELAXED, + "delta_f_strict": DELTA_F_THRESHOLD_STRICT, + "delta_f_relaxed": DELTA_F_THRESHOLD_RELAXED, + }, + "cases": results, + }, f, indent=2) + + # Phase resample for qualifying cases + print("\n=== Phase Resampling ===") + qualifying = [r for r in results if r.get("gate") in ("strict", "relaxed")] + falling = [r for r in results if r.get("gate") not in ("strict", "relaxed")] + + strict_cases = [r["case"] for r in results if r.get("gate") == "strict"] + relaxed_cases = [r["case"] for r in results if r.get("gate") == "relaxed"] + print(f"Strict (main POD basis): {strict_cases}") + print(f"Relaxed (projection): {relaxed_cases}") + print(f"Auxiliary/falling: {[r['case'] for r in falling]}") + + for r in qualifying: + case_name = r["case"] + print(f"\nResampling {case_name} (strict)...") + data = all_raw[case_name] + result = extract_and_resample(data, f_ref, T_ref, St=ref["St"]) + + if result.get("resampled"): + out_dir = os.path.join(OUTPUT_DIR, "resampled", case_name) + os.makedirs(out_dir, exist_ok=True) + + # Save resampled data + save_dict = { + "sensors": result["sensors"], # (n_cycles, n_pts, 6) + "n_cycles": result["sensors"].shape[0], + "n_pts": result["sensors"].shape[1], + } + if "forces" in result: + save_dict["forces"] = result["forces"] + if "actions" in result: + save_dict["actions"] = result["actions"] + if "ux" in result and "uy" in result: + save_dict["ux"] = result["ux"] + save_dict["uy"] = result["uy"] + + np.savez_compressed(os.path.join(out_dir, "resampled.npz"), **save_dict) + + # Also save metadata + meta = { + "case": case_name, + "f_ref": f_ref, + "T_ref": T_ref, + "n_cycles": int(result["sensors"].shape[0]), + "n_pts": int(result["sensors"].shape[1]), + "selected_crossings": [int(c) for c in result["selected_crossings"]], + "has_fields": "ux" in result, + } + with open(os.path.join(out_dir, "meta.json"), "w") as f: + json.dump(meta, f, indent=2) + + sh = result["sensors"].shape + print(f" Resampled: {sh}, fields={'yes' if meta['has_fields'] else 'no'}") + else: + print(f" Resampling failed: {result.get('reason', 'unknown')}") + + # Also resample relaxed cases (Scheme A — projection only, no common POD) + # Also resample relaxed cases (projection only, no POD basis training) + for r in results: + if r.get("gate") != "relaxed": + continue + case_name = r["case"] + if case_name in [q["case"] for q in qualifying]: + continue # already done above + print(f"\nResampling {case_name} (relaxed, projection)...") + data = all_raw[case_name] + result = extract_and_resample(data, f_ref, T_ref, St=ref["St"]) + if result.get("resampled"): + out_dir = os.path.join(OUTPUT_DIR, "resampled", case_name) + os.makedirs(out_dir, exist_ok=True) + save_dict = { + "sensors": result["sensors"], + "n_cycles": result["sensors"].shape[0], + "n_pts": result["sensors"].shape[1], + } + if "forces" in result: + save_dict["forces"] = result["forces"] + if "actions" in result: + save_dict["actions"] = result["actions"] + if "ux" in result and "uy" in result: + save_dict["ux"] = result["ux"] + save_dict["uy"] = result["uy"] + np.savez_compressed(os.path.join(out_dir, "resampled.npz"), **save_dict) + meta = {"case": case_name, "f_ref": f_ref, "T_ref": T_ref, + "n_cycles": int(result["sensors"].shape[0]), + "n_pts": int(result["sensors"].shape[1]), + "selected_crossings": [int(c) for c in result["selected_crossings"]], + "gate": "relaxed"} + with open(os.path.join(out_dir, "meta.json"), "w") as f: + json.dump(meta, f, indent=2) + print(f" Resampled (relaxed): {result['sensors'].shape}") + else: + print(f" Resampling failed: {result.get('reason', 'unknown')}") + + # Save resampling summary + summary = {"strict": strict_cases, + "relaxed": relaxed_cases, + "auxiliary": [r["case"] for r in results if r.get("gate") not in ("strict", "relaxed")]} + with open(os.path.join(OUTPUT_DIR, "resampled", "summary.json"), "w") as f: + json.dump(summary, f, indent=2) + + print("\nPhase 2 complete.") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/src/CCD_analysis/scripts/phase3_pod.py b/src/CCD_analysis/scripts/phase3_pod.py new file mode 100644 index 0000000..a4672a5 --- /dev/null +++ b/src/CCD_analysis/scripts/phase3_pod.py @@ -0,0 +1,218 @@ +# CCD_analysis/scripts/phase3_pod.py +"""Phase 3: Reference POD basis on phase-resampled periodic cases. + +Builds POD basis from strict-qualifying cases (target_cylinder + illusion). +Non-qualifying cases (uncontrolled) are projected onto this basis. + +Output:: + - output/pod/reference_pod_results.npz + - output/pod/reference_pod_metrics.json +""" + +from __future__ import annotations + +import json +import os +import sys +import time + +import numpy as np + +_ANALYSIS = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) +if _ANALYSIS not in sys.path: + sys.path.insert(0, _ANALYSIS) + +from scripts.cfg import OUTPUT_DIR, NX, NY +from scripts.analysis_utils import ( + compute_pod, cumulative_energy, e95_index, + stack_velocity_fields, unstack_velocity_modes, +) + +R_CANDIDATES = [6, 8, 10] # POD truncation levels to test + + +def load_resampled(case_name: str) -> dict: + """Load resampled data for a case. Returns dict or None.""" + resample_dir = os.path.join(OUTPUT_DIR, "resampled", case_name) + data_path = os.path.join(resample_dir, "resampled.npz") + meta_path = os.path.join(resample_dir, "meta.json") + + if not os.path.exists(data_path): + print(f" {case_name}: no resampled data at {data_path}") + return None + + data = np.load(data_path) + meta = {} + if os.path.exists(meta_path): + with open(meta_path) as f: + meta = json.load(f) + + return {"data": data, "meta": meta, "name": case_name} + + +def main(): + print("=== Phase 3: Common POD ===\n") + + # Load summary from Phase 2 + summary_path = os.path.join(OUTPUT_DIR, "resampled", "summary.json") + if not os.path.exists(summary_path): + print("ERROR: Run Phase 2 first.") + return 1 + + with open(summary_path) as f: + summary = json.load(f) + + strict_cases = summary.get("strict", []) + relaxed_cases = summary.get("relaxed", []) + failed_cases = summary.get("failed", []) + + print(f" Strict: {strict_cases}") + print(f" Relaxed (projected): {relaxed_cases}") + print(f" Failed: {failed_cases}") + + if not strict_cases: + print("ERROR: No strict-qualifying cases for common POD.") + return 1 + + # Load all resampled data + all_data = {} + for case in strict_cases + relaxed_cases: + d = load_resampled(case) + if d is not None: + all_data[case] = d + + # Build snapshot matrix from strict cases + print("\nBuilding common POD snapshot matrix...") + snapshots = [] + case_ranges = {} # {case_name: (start_idx, end_idx)} + + current_idx = 0 + for case in strict_cases: + if case not in all_data: + continue + data = all_data[case]["data"] + ux = data.get("ux") + uy = data.get("uy") + if ux is None or uy is None: + print(f" WARNING: {case} has no field data, skipping") + continue + + # Flatten each resampled snapshot + n_cycles, n_pts = ux.shape[0], ux.shape[1] + for c in range(n_cycles): + for p in range(n_pts): + q = np.concatenate([ + ux[c, p].ravel(), + uy[c, p].ravel(), + ]) + snapshots.append(q) + + case_ranges[case] = (current_idx, current_idx + n_cycles * n_pts) + current_idx += n_cycles * n_pts + print(f" {case}: {n_cycles}x{n_pts} = {n_cycles*n_pts} snapshots") + + if not snapshots: + print("ERROR: No field data for POD.") + return 1 + + Q = np.column_stack(snapshots) # (2*nx*ny, N) + print(f" Snapshot matrix: {Q.shape[0]} x {Q.shape[1]}") + + # Compute POD + print("\nComputing POD...") + mean_field, modes, s, coeffs = compute_pod(Q) + energy = cumulative_energy(s) + e95 = e95_index(energy) + + print(f" Modes: {len(s)}") + print(f" E95 = {e95}") + for i in range(min(10, len(s))): + print(f" mode {i+1}: energy={energy[i]:.4f}, sigma={s[i]:.4e}") + + # Project relaxed cases onto the POD basis + projection_coeffs = {} # {case_name: coeffs_matrix} + for case in relaxed_cases: + if case not in all_data: + continue + data = all_data[case]["data"] + ux = data.get("ux") + uy = data.get("uy") + if ux is None or uy is None: + print(f" WARNING: {case} has no field data for projection") + continue + + proj_snapshots = [] + n_cycles, n_pts = ux.shape[0], ux.shape[1] + for c in range(n_cycles): + for p in range(n_pts): + q = np.concatenate([ux[c, p].ravel(), uy[c, p].ravel()]) + proj_snapshots.append(q) + + Q_proj = np.column_stack(proj_snapshots) + # Remove mean field (from strict POD) + Q_proj_centered = Q_proj - mean_field[:, None] + # Project: coefficients = modes^T @ Q + coeffs_proj = modes[:, :R_CANDIDATES[-1]].T @ Q_proj_centered + projection_coeffs[case] = coeffs_proj + print(f" {case}: projected {Q_proj.shape[1]} snapshots") + + # Compute case centroids in a1-a2 phase space + print("\nCase centroids (a1, a2):") + centroids = {} + for case in strict_cases: + if case not in case_ranges: + continue + start, end = case_ranges[case] + a1 = np.mean(coeffs[0, start:end]) + a2 = np.mean(coeffs[1, start:end]) + centroids[case] = [float(a1), float(a2)] + print(f" {case}: a1={a1:.4f}, a2={a2:.4f}") + + for case, coeffs_p in projection_coeffs.items(): + a1 = np.mean(coeffs_p[0]) + a2 = np.mean(coeffs_p[1]) + centroids[case] = [float(a1), float(a2)] + print(f" {case}: a1={a1:.4f}, a2={a2:.4f}") + + # Save results + out_dir = os.path.join(OUTPUT_DIR, "pod") + os.makedirs(out_dir, exist_ok=True) + + # POD results + np.savez_compressed(os.path.join(out_dir, "pod_results.npz"), + mean_field=mean_field, + modes=modes[:, :R_CANDIDATES[-1]], + singular_values=s, + coefficients=coeffs[:R_CANDIDATES[-1]], + energy_ratio=energy[:R_CANDIDATES[-1]], + ) + + # Save projection coefficients for relaxed cases + for case, c in projection_coeffs.items(): + np.savez(os.path.join(out_dir, f"projection_{case}.npz"), + coefficients=c) + + # Metrics + pod_metrics = { + "n_total_modes": len(s), + "E95": int(e95), + "energy_first_2": float(energy[1]) if len(energy) > 1 else float(energy[0]), + "energy_first_6": float(energy[min(5, len(energy)-1)]), + "singular_values": [float(v) for v in s[:R_CANDIDATES[-1]]], + "energy_ratio": [float(v) for v in energy[:R_CANDIDATES[-1]]], + "case_centroids": centroids, + "case_ranges": {k: [int(v[0]), int(v[1])] for k, v in case_ranges.items()}, + "n_strict_cases": len(strict_cases), + "strict_cases": strict_cases, + "relaxed_cases": relaxed_cases, + } + with open(os.path.join(out_dir, "pod_metrics.json"), "w") as f: + json.dump(pod_metrics, f, indent=2) + + print(f"\nResults saved to {out_dir}") + print("Phase 3 complete.") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/src/CCD_analysis/scripts/phase4_ccd.py b/src/CCD_analysis/scripts/phase4_ccd.py new file mode 100644 index 0000000..c07c4e8 --- /dev/null +++ b/src/CCD_analysis/scripts/phase4_ccd.py @@ -0,0 +1,332 @@ +# CCD_analysis/scripts/phase4_ccd.py +"""Phase 4: CCD analysis on POD coefficients. + +Computes: + - force-CCD (all periodic cases): total Fx, Fy + - action-CCD (illusion only): [Omega_front, Omega_bottom, Omega_top] + - signature-CCD (illusion only): e_s(t+tau_c) + - Modal overlap O_k between case pairs +""" + +from __future__ import annotations + +import json +import os +import sys + +import numpy as np + +_ANALYSIS = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) +if _ANALYSIS not in sys.path: + sys.path.insert(0, _ANALYSIS) + +from scripts.cfg import OUTPUT_DIR +from scripts.analysis_utils import compute_reduced_ccd, cumulative_energy + +R_CANDIDATES = [6, 8, 10] +CCD_Q = 12 + + +def load_resampled_coeffs(case_name: str, r: int) -> np.ndarray: + """Load POD coefficients from projection or strict POD.""" + proj_path = os.path.join(OUTPUT_DIR, "pod", f"projection_{case_name}.npz") + if os.path.exists(proj_path): + data = np.load(proj_path) + return data["coefficients"][:r] + + pod_path = os.path.join(OUTPUT_DIR, "pod", "pod_results.npz") + metrics_path = os.path.join(OUTPUT_DIR, "pod", "pod_metrics.json") + if os.path.exists(pod_path) and os.path.exists(metrics_path): + pod = np.load(pod_path) + with open(metrics_path) as f: + metrics = json.load(f) + cr = metrics.get("case_ranges", {}) + if case_name in cr: + start, end = cr[case_name] + return pod["coefficients"][:r, start:end] + return None + + +def load_resampled_signals(case_name: str, key: str = "sensors", n_channels: int = 6) -> np.ndarray: + """Load resampled signal and return (n_channels, N).""" + path = os.path.join(OUTPUT_DIR, "resampled", case_name, "resampled.npz") + if not os.path.exists(path): + return None + data = np.load(path) + arr = data.get(key) + if arr is None: + return None + if key == "forces": + arr_2d = arr.reshape(-1, arr.shape[-1]) + if arr_2d.shape[1] == 2: + return arr_2d.T + f = arr_2d + Fx = (f[:, 0] + f[:, 2] + f[:, 4]) + Fy = (f[:, 1] + f[:, 3] + f[:, 5]) + return np.vstack([Fx, Fy]) + if key == "actions": + return arr.reshape(-1, arr.shape[-1]).T + return arr.reshape(-1, arr.shape[-1]).T + + +def compute_ccd_metrics(case: str, coeffs: np.ndarray, observable: np.ndarray, obs_name: str) -> dict: + """Compute CCD metrics for a single case-observable pair.""" + N = coeffs.shape[1] + N_train = N * 3 // 4 + a_train = coeffs[:, :N_train] + a_test = coeffs[:, N_train:] + y_train = observable[:, :N_train] + y_test = observable[:, N_train:] + + r = coeffs.shape[0] + + # CCD + W, sigma, z = compute_reduced_ccd(coeffs, observable, Q_delay=CCD_Q) + ccd_ene = cumulative_energy(sigma) + m80 = int(np.searchsorted(ccd_ene, 0.80) + 1) if len(ccd_ene) > 0 else 0 + + # POD regression + W_pod = y_train @ a_train.T @ np.linalg.pinv(a_train @ a_train.T + 1e-8 * np.eye(r)) + y_pred_pod = W_pod @ a_test + y_test_r = y_test.ravel() + y_pred_pod_r = y_pred_pod.ravel() + if np.std(y_test_r) > 1e-12 and np.std(y_pred_pod_r) > 1e-12: + corr_pod = float(np.corrcoef(y_pred_pod_r, y_test_r)[0, 1]) + else: + corr_pod = 0.0 + + # CCD regression + n_ccd = min(r, z.shape[0]) + z_train = z[:n_ccd, :N_train] + z_test = z[:n_ccd, N_train:] + W_reg_ccd = y_train @ z_train.T @ np.linalg.pinv(z_train @ z_train.T + 1e-8 * np.eye(n_ccd)) + y_pred_ccd = W_reg_ccd @ z_test + y_pred_ccd_r = y_pred_ccd.ravel() + if np.std(y_test_r) > 1e-12 and np.std(y_pred_ccd_r) > 1e-12: + corr_ccd = float(np.corrcoef(y_pred_ccd_r, y_test_r)[0, 1]) + else: + corr_ccd = 0.0 + + # Top CCD mode correlations with observable + n_corr = min(5, len(sigma)) + corr_z = [] + for k in range(n_corr): + zk_train = z[k, :N_train] + if np.std(zk_train) > 1e-12: + rho = float(np.corrcoef(zk_train, observable[0, :N_train])[0, 1]) + else: + rho = 0.0 + corr_z.append(rho) + + return { + "case": case, + "observable": obs_name, + "r": r, + "sigma": [float(s) for s in sigma[:n_corr]], + "ccd_energy": [float(e) for e in ccd_ene[:n_corr]], + "m80": int(m80), + "corr_POD_obs": corr_pod, + "corr_CCD_obs": corr_ccd, + "corr_z_top5": corr_z, + "N_total": int(N), + "N_train": int(N_train), + "N_test": int(N - N_train), + } + + +def compute_modal_overlap(W_dict: dict, case_pairs: list) -> dict: + """Compute modal overlap O_k between cases. + + O_k(A, B) = |W[:,k]_A^T @ W[:,k]_B| + Higher means the k-th CCD direction is more aligned between cases. + """ + overlap = {} + for case_a, case_b in case_pairs: + if case_a not in W_dict or case_b not in W_dict: + continue + Wa = W_dict[case_a] + Wb = W_dict[case_b] + n = min(Wa.shape[1], Wb.shape[1]) + pair_key = f"O_{case_a}_{case_b}" + pair_vals = [] + for k in range(min(n, 5)): + ak = Wa[:, k] / (np.linalg.norm(Wa[:, k]) + 1e-12) + bk = Wb[:, k] / (np.linalg.norm(Wb[:, k]) + 1e-12) + pair_vals.append(float(abs(ak @ bk))) + overlap[pair_key] = pair_vals + return overlap + + +def load_target_signals(case_name: str) -> np.ndarray: + """Load target_cylinder resampled sensors as reference signature. + + Since target_cylinder is at U0=0.01 and illusion at U0=0.02, + signals are amplitude-normalized by their respective RMS. + """ + # Use target_cylinder resampled sensors as reference + ref_path = os.path.join(OUTPUT_DIR, "resampled", "target_cylinder", "resampled.npz") + if not os.path.exists(ref_path): + return None + ref = np.load(ref_path) + sensors_ref = ref.get("sensors") # (n_cycles, n_pts, 6) + if sensors_ref is None: + return None + return sensors_ref.reshape(-1, sensors_ref.shape[-1]).T # (6, N) + + +def main(): + print("=== Phase 4: CCD Analysis ===\n") + + metrics_path = os.path.join(OUTPUT_DIR, "pod", "pod_metrics.json") + if not os.path.exists(metrics_path): + print("ERROR: Run Phase 3 first.") + return 1 + with open(metrics_path) as f: + pod_metrics = json.load(f) + + all_cases = pod_metrics.get("strict_cases", []) + pod_metrics.get("relaxed_cases", []) + all_results = {} + W_dict = {} # {case_observable_r: W_matrix} + + for r in R_CANDIDATES: + print(f"\n{'='*60}") + print(f"POD truncation r={r}") + print(f"{'='*60}") + + # ----- 1. Force-CCD (all cases) ----- + print("\n--- Force-CCD ---") + for case in all_cases: + coeffs = load_resampled_coeffs(case, r) + if coeffs is None: + continue + y_force = load_resampled_signals(case, "forces", 2) + if y_force is None: + print(f" {case}: no force data") + continue + if y_force.shape[-1] != coeffs.shape[1]: + print(f" {case}: force length mismatch, skipping") + continue + + # CCD computation + W, sigma, z = compute_reduced_ccd(coeffs, y_force, Q_delay=CCD_Q) + ccd_ene = cumulative_energy(sigma) + m80 = int(np.searchsorted(ccd_ene, 0.80) + 1) + + key = f"{case}_force_r{r}" + W_dict[key] = W + all_results[key] = compute_ccd_metrics(case, coeffs, y_force, "force_CCD") + print(f" {case}: m80={all_results[key]['m80']}, " + f"sigma={sigma[0]:.3f},{sigma[1]:.3f}") + + # ----- 2. Action-CCD (illusion only) ----- + print("\n--- Action-CCD (illusion only) ---") + if "illusion" in all_cases: + coeffs = load_resampled_coeffs("illusion", r) + if coeffs is not None: + y_act = load_resampled_signals("illusion", "actions", 3) + if y_act is not None and y_act.shape[-1] == coeffs.shape[1]: + key = f"illusion_action_r{r}" + W, sigma, z = compute_reduced_ccd(coeffs, y_act, Q_delay=CCD_Q) + W_dict[key] = W + all_results[key] = compute_ccd_metrics( + "illusion", coeffs, y_act, "action_CCD") + print(f" illusion: m80={all_results[key]['m80']}, " + f"sigma={sigma[0]:.3f},{sigma[1]:.3f}") + else: + print(f" illusion: no action data (y={y_act.shape if y_act is not None else None}, " + f"N={coeffs.shape[1]})") + + # ----- 3. Signature-CCD (illusion only, tau_c=0) ----- + print("\n--- Signature-CCD (illusion only, tau_c=0) ---") + if "illusion" in all_cases: + coeffs = load_resampled_coeffs("illusion", r) + if coeffs is not None: + # Load actual sensors + sensors = load_resampled_signals("illusion", "sensors", 6) + # Load target sensors + target_sensors = load_target_signals("illusion") + if sensors is not None and target_sensors is not None and sensors.shape == target_sensors.shape: + # e_s(t) = s(t) - s_tar(t) + e_s = sensors - target_sensors + key = f"illusion_signature_r{r}" + W, sigma, z = compute_reduced_ccd(coeffs, e_s, Q_delay=CCD_Q) + W_dict[key] = W + all_results[key] = compute_ccd_metrics( + "illusion", coeffs, e_s, "signature_CCD") + print(f" illusion: m80={all_results[key]['m80']}, " + f"sigma={sigma[0]:.3f},{sigma[1]:.3f}") + else: + print(f" illusion: signature data issue " + f"(sensors={sensors.shape if sensors is not None else None}, " + f"target={target_sensors.shape if target_sensors is not None else None})") + + # ----- Modal Overlap O_k ----- + print("\n--- Modal Overlap O_k ---") + all_obs_keys = [k for k in all_results.keys()] + pair_results = {} + for r in R_CANDIDATES: + # Force-CCD overlap between strict cases + strict_cases = pod_metrics.get("strict_cases", []) + for i, ca in enumerate(strict_cases): + for cb in strict_cases[i+1:]: + key_a = f"{ca}_force_r{r}" + key_b = f"{cb}_force_r{r}" + if key_a in W_dict and key_b in W_dict: + Wa = W_dict[key_a] + Wb = W_dict[key_b] + n = min(Wa.shape[1], Wb.shape[1], 5) + ov = [] + for k in range(n): + ak = Wa[:, k] / (np.linalg.norm(Wa[:, k]) + 1e-12) + bk = Wb[:, k] / (np.linalg.norm(Wb[:, k]) + 1e-12) + ov.append(float(abs(ak @ bk))) + pk = f"O_{ca}_{cb}_force_r{r}" + pair_results[pk] = ov + print(f" {ca} vs {cb} (force, r={r}): " + f"O1={ov[0]:.4f}, O2={ov[1]:.4f}") + + # Also compare illusion to target in action and signature space + if "illusion" in all_cases: + ia_key = f"illusion_action_r{r}" + is_key = f"illusion_signature_r{r}" + for tc in strict_cases: + tf_key = f"{tc}_force_r{r}" + if ia_key in W_dict and tf_key in W_dict: + Wa = W_dict[ia_key] + Wb = W_dict[tf_key] + n = min(Wa.shape[1], Wb.shape[1], 5) + ov = [] + for k in range(n): + ak = Wa[:, k] / (np.linalg.norm(Wa[:, k]) + 1e-12) + bk = Wb[:, k] / (np.linalg.norm(Wb[:, k]) + 1e-12) + ov.append(float(abs(ak @ bk))) + pk = f"O_action_vs_force_{tc}_r{r}" + pair_results[pk] = ov + print(f" action vs {tc}-force (r={r}): " + f"O1={ov[0]:.4f}, O2={ov[1]:.4f}") + + # Save + out_dir = os.path.join(OUTPUT_DIR, "ccd") + os.makedirs(out_dir, exist_ok=True) + all_results["modal_overlaps"] = pair_results + with open(os.path.join(out_dir, "ccd_metrics.json"), "w") as f: + json.dump(all_results, f, indent=2) + + # Summary + print(f"\n{'='*70}") + print("Summary: CCD metrics") + print(f"{'='*70}") + for key in sorted([k for k in all_results.keys() if k != "modal_overlaps"]): + m = all_results[key] + sig = m.get("sigma", [0, 0]) + print(f" {key:<40} m80={m['m80']} " + f"sigma=[{sig[0]:.3f},{sig[1] if len(sig)>1 else 0:.3f}] " + f"corr_CCD={m['corr_CCD_obs']:.4f}") + + print(f"\nSaved to {out_dir}/ccd_metrics.json") + print("Phase 4 complete.") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/src/CCD_analysis/scripts/phase5_steady.py b/src/CCD_analysis/scripts/phase5_steady.py new file mode 100644 index 0000000..edfc97d --- /dev/null +++ b/src/CCD_analysis/scripts/phase5_steady.py @@ -0,0 +1,232 @@ +# CCD_analysis/scripts/phase5_steady.py +""" +WARNING: This script has known issues. +- E_mean_uy calculation explodes because empty_channel uy ~ 0 (denominator near zero) +- eta_fluc is negative because cloak and uncontrolled use different pinball positions + (cloak uses standard layout FRONT/BOTTOM/TOP, uncontrolled may differ) +- L_r (recirculation zone length) = 0, which may indicate incorrect u=0 detection logic + Need to verify against actual flow fields. + +Use this as reference only. Do NOT use resulting metrics for conclusions without validation. +""" + +from __future__ import annotations + +import json +import os +import sys + +import numpy as np + +_ANALYSIS = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) +if _ANALYSIS not in sys.path: + sys.path.insert(0, _ANALYSIS) + +from scripts.cfg import OUTPUT_DIR, NX, NY + + +def load_fields(case_name: str): + """Load fields.npz for a case, return (ux, uy) as (N, NY, NX).""" + path = os.path.join(OUTPUT_DIR, case_name, "fields.npz") + if not os.path.exists(path): + return None + data = np.load(path) + return data["ux"].astype(np.float64), data["uy"].astype(np.float64) + + +def load_sensors(case_name: str): + """Load sensors.npz for a case.""" + path = os.path.join(OUTPUT_DIR, case_name, "sensors.npz") + if not os.path.exists(path): + return None + return np.load(path) + + +def compute_mean_rms(ux, uy): + """Compute mean and RMS fields from time series.""" + ux_mean = np.mean(ux, axis=0) + uy_mean = np.mean(uy, axis=0) + ux_rms = np.sqrt(np.mean((ux - ux_mean) ** 2, axis=0)) + uy_rms = np.sqrt(np.mean((uy - uy_mean) ** 2, axis=0)) + return ux_mean, uy_mean, ux_rms, uy_rms + + +def recirculation_metrics(ux_mean): + """Extract recirculation zone from mean ux field. + + Returns (L_r, A_r) where: + L_r = length of recirculation zone along centerline + A_r = area of recirculation zone (u < 0) + """ + ny, nx = ux_mean.shape + center_y = ny // 2 + margin = 10 + + # Centerline ux + cline = ux_mean[center_y, :] + + # Find u=0 crossings behind the pinball (x > 400 or so) + start_x = 400 + end_x = nx - 50 + roi = cline[start_x:end_x] + neg = np.where(roi < 0)[0] + if len(neg) > 0: + # Recirculation length = distance from end of negative region + neg_end = neg[-1] + start_x + L_r = float(neg_end - 400) + else: + L_r = 0.0 + + # Area: count cells with u < 0 in the wake region + wake_region = ux_mean[:, 300:800] + area_mask = wake_region < 0 + A_r = float(np.sum(area_mask) * 1.0) # in lattice cells + + return L_r, A_r + + +def main(): + print("=== Phase 5: Cloak Steady-Line Analysis ===\n") + + # Load data + cloak_fields = load_fields("cloak") + if cloak_fields is None: + print("ERROR: No cloak fields found. Run Phase 1b first.") + return 1 + ux_c, uy_c = cloak_fields + + channel_fields = load_fields("empty_channel") + if channel_fields is None: + print("ERROR: No empty_channel fields found. Run Phase 1d first.") + return 1 + ux_ch, uy_ch = channel_fields + + unc_fields = load_fields("uncontrolled") + if unc_fields is None: + print("WARNING: No uncontrolled fields for comparison.") + unc_fields = None + + cloak_sens = load_sensors("cloak") + if cloak_sens is None: + print("ERROR: No cloak sensors found.") + return 1 + + # Compute mean and RMS + ux_c_mean, uy_c_mean, ux_c_rms, uy_c_rms = compute_mean_rms(ux_c, uy_c) + ux_ch_mean, uy_ch_mean, _, _ = compute_mean_rms(ux_ch, uy_ch) + + # 1. E_mean: mean flow relative error (vs empty channel), normalized by U0 + ux_err = np.mean((ux_c_mean - ux_ch_mean) ** 2) + ux_ref = np.mean(ux_ch_mean ** 2) + 1e-12 + E_mean_ux = float(ux_err / ux_ref) + E_mean_uy = float(np.mean((uy_c_mean - uy_ch_mean) ** 2)) / (np.mean(uy_ch_mean ** 2) + 1e-12) + E_mean = (E_mean_ux + E_mean_uy) / 2.0 + print(f"1. E_mean (rel. to empty channel):") + print(f" ux error={E_mean_ux:.4f}, uy error={E_mean_uy:.4f}, avg={E_mean:.4f}") + + # 2. E_sensor_mean + sensors = cloak_sens["sensors"] + forces = cloak_sens["forces"] + sensor_mean = np.mean(sensors, axis=0) + # Target empty channel: U0=0.01 parabolic, sensor ux should be near U0, uy near 0 + sensor_target_mean = np.array([1.0, 0.0, 1.0, 0.0, 1.0, 0.0], dtype=np.float32) + E_sensor_mean = float(np.mean(np.abs(sensor_mean - sensor_target_mean))) + print(f"2. E_sensor_mean = {E_sensor_mean:.4f} " + f"(sensor_mean={sensor_mean[:3].tolist()})") + + # 3. eta_fluc: fluctuation suppression ratio + if unc_fields is not None: + ux_unc, uy_unc = unc_fields + # Use first 30 frames of uncontrolled for fair comparison + n_compare = min(len(ux_c), len(ux_unc), 30) + ux_c_sub, uy_c_sub = ux_c[:n_compare], uy_c[:n_compare] + ux_unc_sub, uy_unc_sub = ux_unc[:n_compare], uy_unc[:n_compare] + + # RMS in wake region only (300-800, full height) + wake_x_start, wake_x_end = 300, 800 + ux_c_mean_s, uy_c_mean_s = np.mean(ux_c_sub, axis=0), np.mean(uy_c_sub, axis=0) + ux_unc_mean_s, uy_unc_mean_s = np.mean(ux_unc_sub, axis=0), np.mean(uy_unc_sub, axis=0) + + rms_c = np.sqrt(np.mean((ux_c_sub - ux_c_mean_s) ** 2 + (uy_c_sub - uy_c_mean_s) ** 2, axis=0)) + rms_unc = np.sqrt(np.mean((ux_unc_sub - ux_unc_mean_s) ** 2 + (uy_unc_sub - uy_unc_mean_s) ** 2, axis=0)) + + # Integrate RMS over wake region + int_rms_c = float(np.sum(rms_c[:, wake_x_start:wake_x_end])) + int_rms_unc = float(np.sum(rms_unc[:, wake_x_start:wake_x_end])) + eps = 1e-12 + eta_fluc = 1.0 - int_rms_c / (int_rms_unc + eps) + print(f"3. eta_fluc = {eta_fluc:.4f} (wake region, first {n_compare} frames)") + else: + eta_fluc = 0.0 + print("3. eta_fluc: skipped (no uncontrolled data)") + + # 4. Recirculation zone + L_r_c, A_r_c = recirculation_metrics(ux_c_mean) + if unc_fields is not None: + ux_unc_mean = np.mean(ux_unc_sub, axis=0) + L_r_unc, A_r_unc = recirculation_metrics(ux_unc_mean) + print(f"4. Recirculation: cloak L_r={L_r_c:.0f}, A_r={A_r_c:.0f} " + f"vs uncontrolled L_r={L_r_unc:.0f}, A_r={A_r_unc:.0f}") + else: + print(f"4. Recirculation: cloak L_r={L_r_c:.0f}, A_r={A_r_c:.0f}") + + # 5. Force statistics + cloak_forces = forces + if len(cloak_forces) > 0: + fx = cloak_forces[:, 0::2] + fy = cloak_forces[:, 1::2] + sigma_F = float(np.std(np.sum(fx, axis=1) + np.sum(fy, axis=1))) + mean_Fx = float(np.mean(fx)) + print(f"5. Force stats: sigma_F={sigma_F:.6f}, mean_Fx={mean_Fx:.6f}") + + # 6. Control amplitude + ppo_rollout_path = os.path.join(OUTPUT_DIR, "cloak", "ppo_rollout.npz") + if os.path.exists(ppo_rollout_path): + pr = np.load(ppo_rollout_path) + steady_action = pr.get("steady_action") + if steady_action is not None: + J_omega_rms = float(np.sum(np.abs(steady_action))) + print(f"6. J_omega_rms = {J_omega_rms:.4f} (norm action sum abs)") + + # 7. eta_cloak_obs (control efficiency proxy) + if unc_fields is not None: + unc_sens = load_sensors("uncontrolled") + if unc_sens is not None: + unc_sensor_mean = np.mean(unc_sens["sensors"], axis=0) + E_unc = float(np.mean(np.abs(unc_sensor_mean - sensor_target_mean))) + E_cloak = E_sensor_mean + J_omega = J_omega_rms if 'J_omega_rms' in dir() else 1.0 + eta_cloak_obs = (E_unc - E_cloak) / (J_omega + 1e-12) + print(f"7. eta_cloak_obs = {eta_cloak_obs:.4f} " + f"(E_unc={E_unc:.4f}, E_cloak={E_cloak:.4f})") + + # Compile and save + steady_metrics = { + "E_mean_ux": E_mean_ux, + "E_mean_uy": E_mean_uy, + "E_mean_avg": E_mean, + "E_sensor_mean": E_sensor_mean, + "sensor_mean": sensor_mean.tolist(), + "eta_fluc": eta_fluc, + "L_r_cloak": L_r_c, + "A_r_cloak": A_r_c, + "sigma_F": sigma_F if 'sigma_F' in dir() else 0, + "mean_Fx": mean_Fx if 'mean_Fx' in dir() else 0, + "J_omega_rms": J_omega_rms if 'J_omega_rms' in dir() else 0, + "eta_cloak_obs": eta_cloak_obs if 'eta_cloak_obs' in dir() else 0, + "L_r_uncontrolled": L_r_unc if unc_fields is not None and 'L_r_unc' in dir() else None, + "A_r_uncontrolled": A_r_unc if unc_fields is not None and 'A_r_unc' in dir() else None, + } + + out_dir = os.path.join(OUTPUT_DIR, "steady") + os.makedirs(out_dir, exist_ok=True) + with open(os.path.join(out_dir, "steady_metrics.json"), "w") as f: + json.dump(steady_metrics, f, indent=2) + + print(f"\nSaved to {out_dir}/steady_metrics.json") + print("Phase 5 complete.") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/src/CCD_analysis/scripts/utils.py b/src/CCD_analysis/scripts/utils.py new file mode 100644 index 0000000..a53a263 --- /dev/null +++ b/src/CCD_analysis/scripts/utils.py @@ -0,0 +1,308 @@ +# CCD_analysis/scripts/utils.py +"""Shared utilities for CCD analysis pipeline. + +All CFD uses LegacyCelerisLab (old API). Must run under conda pycuda_3_10. +""" + +from __future__ import annotations + +import json +import os +import sys +from typing import Any, Dict, List, Optional, Tuple + +import numpy as np + +# Add project root for LegacyCelerisLab import +_REPO = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..")) +if _REPO not in sys.path: + sys.path.insert(0, _REPO) + +from LegacyCelerisLab import FlowField # noqa: E402 +from LegacyCelerisLab import utils as legacy_utils # noqa: E402 + +# --------------------------------------------------------------------------- +# Config loading +# --------------------------------------------------------------------------- + +def load_configs(config_dir: str) -> Tuple[Any, Any]: + """Load legacy (cuda_config, field_config) from config_dir.""" + cuda_cfg = legacy_utils.load_cuda_config( + os.path.join(config_dir, "config_cuda.json") + ) + field_cfg = legacy_utils.load_flow_field_config( + os.path.join(config_dir, "config_flowfield.json") + ) + return cuda_cfg, field_cfg + + +# --------------------------------------------------------------------------- +# Field I/O from DDF (matches uni_test pattern) +# --------------------------------------------------------------------------- + +def get_velocity_field(flow_field: FlowField, u0: float = 0.01) -> Tuple[np.ndarray, np.ndarray]: + """Extract ux, uy fields from DDF on host. Returns (ux, uy) each (NY, NX).""" + flow_field.get_ddf() + NX = flow_field.FIELD_SHAPE[0] + NY = flow_field.FIELD_SHAPE[1] + ddf = flow_field.ddf.copy().reshape((9, NY, NX)).transpose(2, 1, 0) + ux = (ddf[:, :, 1] + ddf[:, :, 5] + ddf[:, :, 8] + - ddf[:, :, 3] - ddf[:, :, 6] - ddf[:, :, 7]) / u0 + uy = (ddf[:, :, 2] + ddf[:, :, 5] + ddf[:, :, 6] + - ddf[:, :, 4] - ddf[:, :, 7] - ddf[:, :, 8]) / u0 + return ux.astype(np.float32), uy.astype(np.float32) + + +def vorticity_from_fields(ux: np.ndarray, uy: np.ndarray) -> np.ndarray: + """Compute z-vorticity omega = dv/dx - du/dy.""" + return (np.gradient(uy, axis=1) - np.gradient(ux, axis=0)).astype(np.float64) + + +# --------------------------------------------------------------------------- +# Period detection helpers +# --------------------------------------------------------------------------- + +def detect_dominant_frequency( + signal: np.ndarray, sample_dt: float +) -> Tuple[float, float, float]: + """Detect dominant frequency via FFT. + + Parameters + ---------- + signal : 1D array + Time series to analyse. + sample_dt : float + Time between samples (in same units as desired frequency). + + Returns + ------- + f_dom : float + Dominant frequency. + period : float + Corresponding period (1/f_dom). + peak_power : float + Power at dominant frequency. + """ + n = len(signal) + if n < 16: + return 0.0, 0.0, 0.0 + y = signal - np.mean(signal) + window = np.hanning(n) + spec = np.abs(np.fft.rfft(y * window)) ** 2 + freqs = np.fft.rfftfreq(n, d=sample_dt) + # Skip DC + idx = 1 + np.argmax(spec[1:]) + f_dom = float(freqs[idx]) + period = 1.0 / f_dom if f_dom > 0 else 0.0 + return f_dom, period, float(spec[idx]) + + +def detect_cycle_stability( + signal: np.ndarray, sample_dt: float +) -> Tuple[float, float, List[float]]: + """Detect cycle lengths and compute stability metrics. + + Parameters + ---------- + signal : 1D array + sample_dt : float + + Returns + ------- + cv_T : float + Coefficient of variation of detected cycle lengths. + mean_T : float + Mean cycle length in time units. + cycle_lengths : list of float + Detected cycle lengths. + """ + y = signal - np.mean(signal) + # Find rising zero-crossings + sign = np.sign(y) + crossings = np.where((sign[:-1] < 0) & (sign[1:] > 0))[0] + if len(crossings) < 2: + return 0.0, 0.0, [] + + cycle_lengths = np.diff(crossings).astype(float) * sample_dt + if len(cycle_lengths) < 2: + return 0.0, float(cycle_lengths[0]) if len(cycle_lengths) > 0 else 0.0, cycle_lengths.tolist() + + mean_T = float(np.mean(cycle_lengths)) + std_T = float(np.std(cycle_lengths)) + cv_T = std_T / mean_T if mean_T > 0 else 0.0 + return cv_T, mean_T, cycle_lengths.tolist() + + +# --------------------------------------------------------------------------- +# Phase resampling +# --------------------------------------------------------------------------- + +def phase_resample( + data: np.ndarray, + cycle_starts: List[int], + n_pts: int = 24, + kind: str = "linear", +) -> np.ndarray: + """Resample a multi-channel signal to uniform phase points per cycle. + + Parameters + ---------- + data : (T, C) ndarray + Multi-channel time series. + cycle_starts : list of int + Indices where each cycle starts (rising zero-crossings). + n_pts : int + Number of phase points per cycle. + kind : str + Interpolation kind ('linear' or 'cubic'). + + Returns + ------- + resampled : (n_cycles, n_pts, C) ndarray + """ + from scipy import interpolate + + n_cycles = len(cycle_starts) - 1 + if n_cycles < 1: + raise ValueError("Need at least 2 cycle starts") + + C = data.shape[1] if data.ndim > 1 else 1 + out = np.zeros((n_cycles, n_pts, C), dtype=np.float64) + + for c in range(n_cycles): + i_start = cycle_starts[c] + i_end = cycle_starts[c + 1] + segment = data[i_start:i_end + 1] + seg_len = len(segment) + if seg_len < 2: + continue + + old_phase = np.linspace(0, 2 * np.pi, seg_len) + new_phase = np.linspace(0, 2 * np.pi, n_pts, endpoint=False) + + for ch in range(C): + interp = interpolate.interp1d( + old_phase, segment[:, ch] if segment.ndim > 1 else segment, + kind=kind, bounds_error=False, fill_value="extrapolate", + ) + out[c, :, ch] = interp(new_phase) + + return out + + +# --------------------------------------------------------------------------- +# POD +# --------------------------------------------------------------------------- + +def compute_pod(snapshot_matrix: np.ndarray) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: + """Compute POD from snapshot matrix. + + Parameters + ---------- + snapshot_matrix : (n_points, n_snapshots) ndarray + Each column is one flattened snapshot. + + Returns + ------- + mean_field : (n_points,) ndarray + modes : (n_points, min(n_points, n_snapshots)) ndarray + Spatial modes (columns of U from SVD). + singular_values : (min_dim,) ndarray + coefficients : (min_dim, n_snapshots) ndarray + Temporal coefficients (Sigma V^T). + """ + mean_field = np.mean(snapshot_matrix, axis=1) + Q = snapshot_matrix - mean_field[:, None] # remove mean + U, s, Vt = np.linalg.svd(Q, full_matrices=False) + coeffs = (U.T @ Q) # alternative: np.diag(s) @ Vt + return mean_field, U, s, coeffs + + +def cumulative_energy(singular_values: np.ndarray) -> np.ndarray: + """Return cumulative energy fraction.""" + e = singular_values ** 2 + return np.cumsum(e) / np.sum(e) + + +def e95_index(cumulative_energy: np.ndarray) -> int: + """Return first index where cumulative energy >= 95%.""" + return int(np.searchsorted(cumulative_energy, 0.95) + 1) + + +# --------------------------------------------------------------------------- +# CCD (reduced version, Lyu23-inspired) +# --------------------------------------------------------------------------- + +def compute_reduced_ccd( + pod_coeffs: np.ndarray, + observable: np.ndarray, + Q_delay: int = 12, +) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: + """Compute reduced CCD in POD coefficient space. + + Parameters + ---------- + pod_coeffs : (r, N) ndarray + Standardized POD coefficients (r modes, N time steps). + observable : (m, N) ndarray + Standardized observable (m channels, N time steps). + Q_delay : int + Number of delay steps. + + Returns + ------- + W : (r, min(r, m*Q_delay)) ndarray + CCD directions in POD coefficient space. + sigma : (min_dim,) ndarray + Singular values (correlation strengths). + z : (min_dim, N) ndarray + CCD temporal coefficients. + """ + N = pod_coeffs.shape[1] + m = observable.shape[0] + + # Build delay matrix P + # For each time t_i, p_i = [y(t_i+τ_1), ..., y(t_i+τ_Q)] + # where τ_j spans -Q_delay/2 to +Q_delay/2 + half = Q_delay // 2 + P_rows = [] + for shift in range(-half, half + 1): + shifted = np.roll(observable, -shift, axis=1) + if shift < 0: + shifted[:, shift:] = 0 # zero pad edges + elif shift > 0: + shifted[:, :-shift] = 0 + P_rows.append(shifted) + P = np.vstack(P_rows) # (m*Q_delay, N) + + # CCD matrix: C = P * A^T / (N * sqrt(Q)) + C = P @ pod_coeffs.T / (N * np.sqrt(Q_delay)) + + # SVD + R, s, Wt = np.linalg.svd(C, full_matrices=False) + W = Wt.T # (r, min_dim) + + # CCD coefficients + z = W.T @ pod_coeffs # (min_dim, N) + + return W, s, z + + +# --------------------------------------------------------------------------- +# Field stacking +# --------------------------------------------------------------------------- + +def stack_velocity_fields( + ux_fields: List[np.ndarray], + uy_fields: List[np.ndarray], +) -> np.ndarray: + """Stack list of (ux, uy) field pairs into snapshot matrix. + + Each field is flattened, then ux and uy are interleaved. + Returns (2*nx*ny, N) matrix. + """ + snapshots = [] + for ux, uy in zip(ux_fields, uy_fields): + q = np.concatenate([ux.ravel(), uy.ravel()]) + snapshots.append(q) + return np.column_stack(snapshots) diff --git a/src/CCD_analysis/scripts/validate_control.py b/src/CCD_analysis/scripts/validate_control.py new file mode 100644 index 0000000..1f46e42 --- /dev/null +++ b/src/CCD_analysis/scripts/validate_control.py @@ -0,0 +1,561 @@ +# CCD_analysis/scripts/validate_control.py +""" +WARNING: This validation script has NOT produced correct results. +Despite multiple iterations, the PPO inference does not reproduce thesis-level +reward/similarity values (cloak sim ~0.19 vs expected 0.90, illusion sim ~0.82 vs 0.98). + +Known issues that need to be investigated: +1. The norm values seem reasonable but may not match training-time distributions +2. Obs layout (what obs[i] means) depends on object add order, which differs between + legacy_env_karman_cloak_standard.py and uni_test.ipynb -- need to determine which + was actually used during training +3. The FIFO initialization may not exactly match training-time behavior +4. Action smoothing (legacy FlowField.run() uses exponential smoothing weight=0.1) + may affect dynamics in ways not accounted for + +DO NOT USE these results for analysis. Fix the PPO replay first. +""" + +from __future__ import annotations + +import argparse +import json +import os +import sys +import time +from collections import deque + +import numpy as np + +_REPO = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..")) +if _REPO not in sys.path: + sys.path.insert(0, _REPO) +_ANALYSIS = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) +if _ANALYSIS not in sys.path: + sys.path.insert(0, _ANALYSIS) + +from LegacyCelerisLab import FlowField +from scripts.cfg import CONFIG_DIR, OUTPUT_DIR, L0, NX, NY, CENTER_Y +from scripts.utils import load_configs, get_velocity_field + +# -- Constants -- +FIFO_LEN = 150 +DATA_TYPE = np.float32 +U0 = 0.01 +U0_ILLUSION = 0.02 +SAMPLE_INTERVAL = 800 +SAMPLE_INTERVAL_ILL = 600 + +MODEL_CLOAK = os.path.join(_REPO, "models", "old", "d1a3o12_re100.zip") +MODEL_ILLUSION = os.path.join(_REPO, "models", "250525", "d1a3o14_250525_imit_1L_2U_600S.zip") + +# Geometry +PR = L0 / 2.0 # pinball radius = 10 +SR = L0 / 4.0 # sensor radius = 5 + +# Cloak layout (lattice units) +DIST_POS = (10.0 * L0, CENTER_Y, 0.0) +SENSOR_X = 40.0 * L0 +SENSOR_YS = [CENTER_Y + 2.0 * L0, CENTER_Y, CENTER_Y - 2.0 * L0] +FRONT_POS = (30.0 * L0, CENTER_Y, 0.0) +BOTTOM_POS = (31.3 * L0, CENTER_Y - 0.75 * L0, 0.0) +TOP_POS = (31.3 * L0, CENTER_Y + 0.75 * L0, 0.0) + +# Illusion layout +ILL_SENSOR_X = 30.0 * L0 +ILL_SENSOR_YS = [CENTER_Y + 2.0 * L0, CENTER_Y, CENTER_Y - 2.0 * L0] +ILL_FRONT = (19.0 * L0, CENTER_Y, 0.0) +ILL_BOTTOM = (20.3 * L0, CENTER_Y + 0.75 * L0, 0.0) +ILL_TOP = (20.3 * L0, CENTER_Y - 0.75 * L0, 0.0) + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +def _calc_lag(t, s): + tm = float(np.mean(t)) + sm = float(np.mean(s)) + corr = np.correlate(t - tm, s - sm, mode="full") + lags = np.arange(-len(t) + 1, len(t)) + return int(lags[np.argmax(corr)]) + + +def _calc_dtw_sim(t, s): + n, m = len(t), len(s) + dtw = np.full((n + 1, m + 1), np.inf) + dtw[0, 0] = 0.0 + for i in range(1, n + 1): + for j in range(1, m + 1): + cost = abs(float(t[i - 1]) - float(s[j - 1])) + dtw[i, j] = cost + min(dtw[i - 1, j], dtw[i, j - 1], dtw[i - 1, j - 1]) + return float(1.0 - dtw[n, m] / n) + + +def _save_vorticity_png(ff, path, title="", u0=U0): + import matplotlib + matplotlib.use("Agg") + import matplotlib.pyplot as plt + ux, uy = get_velocity_field(ff, u0=u0) + omega = np.gradient(uy, axis=1) - np.gradient(ux, axis=0) # dv/dx - du/dy + abs_o = np.abs(omega[np.isfinite(omega)]) + vmax = float(np.percentile(abs_o, 99.5)) if abs_o.size > 0 else 1.0 + if vmax <= 0: + vmax = 1.0 + ny, nx = omega.shape + fig, ax = plt.subplots(figsize=(min(18, max(8, nx / 60)), min(10, max(3, ny / 40)))) + im = ax.imshow(omega, origin="lower", aspect="equal", cmap="RdBu_r", + vmin=-vmax, vmax=vmax, extent=(0, nx - 1, 0, ny - 1)) + ax.set_xlabel("x (lattice)") + ax.set_ylabel("y (lattice)") + if title: + ax.set_title(title) + fig.colorbar(im, ax=ax, fraction=0.046, pad=0.04, label=r"$\omega_z$") + fig.tight_layout() + fig.savefig(path, dpi=150, bbox_inches="tight") + plt.close(fig) + + +def _load_ppo(model_path, device, s_dim=12, a_dim=3): + import torch + from torch.nn import Module + from stable_baselines3 import PPO + import gymnasium as gym + from gymnasium import spaces + class Sin(Module): + def forward(self, x): + return torch.sin(x) + class DummyEnv(gym.Env): + def __init__(self): + super().__init__() + self.observation_space = spaces.Box(low=-1, high=1, shape=(s_dim,), dtype=np.float32) + self.action_space = spaces.Box(low=-1, high=1, shape=(a_dim,), dtype=np.float32) + def reset(self, seed=None): + return np.zeros(s_dim, dtype=np.float32), {} + def step(self, action): + return np.zeros(s_dim, dtype=np.float32), 0.0, False, False, {} + def render(self): + pass + return PPO.load(model_path, env=DummyEnv(), device=device) + + +def _analyze_harmonics(states, n=5): + N, D = states.shape + r = [] + for d in range(D): + y = states[:, d] + fc = np.fft.rfft(y) + fr = np.fft.rfftfreq(N, d=1) + amps = 2.0 * np.abs(fc) / N + ph = np.angle(fc) + idx = np.argsort(amps[1:])[::-1][:n] + 1 + r.append({'dc': float(np.real(fc[0]) / N), 'amps': amps[idx].tolist(), + 'freqs': fr[idx].tolist(), 'phases': ph[idx].tolist()}) + return r + + +def _gen_target(t, harm): + t = np.asarray(t) + D = len(harm) + r = np.zeros((t.size, D), dtype=np.float32) + for d, h in enumerate(harm): + v = np.full(t.shape, h['dc'], dtype=np.float32) + for a, f, p in zip(h['amps'], h['freqs'], h['phases']): + v += a * np.cos(2 * np.pi * f * t + p) + r[:, d] = v + if r.shape[0] == 1: + return r[0] + return r + + +# --------------------------------------------------------------------------- +# Cloak validation (EXACTLY matching analysis_crossre + legacy_env) +# --------------------------------------------------------------------------- + +def validate_cloak(device_id, out_dir): + print("=" * 60) + print("Validating Cloak (Karman)") + print("=" * 60) + os.makedirs(out_dir, exist_ok=True) + + cuda_cfg, field_cfg = load_configs(CONFIG_DIR) + field_cfg = field_cfg._replace(viscosity=0.004, velocity=U0) + ff = FlowField(field_cfg, cuda_cfg, device_id=device_id) + + # -- Phase 1: target recording (dist_cyl + 3 sensors) -- + print("\n--- Target recording ---") + ff.add_cylinder(DIST_POS, L0) # dist(0) + for y in SENSOR_YS: + ff.add_sensor((SENSOR_X, y, 0.0), SR) # sensors(1,2,3) + n_obj = ff.obs.size // 2 # 4 + ff.run(int(4 * NX / U0), np.zeros(n_obj, dtype=DATA_TYPE)) + + target_states = np.empty((0, 6), dtype=DATA_TYPE) + for _ in range(FIFO_LEN): + ff.run(SAMPLE_INTERVAL, np.zeros(n_obj, dtype=DATA_TYPE)) + # obs layout: dist(0) + 3 sensors(1,2,3) → 4×2=8 values + # obs[2:8] = s0_ux,uy, s1_ux,uy, s2_ux,uy = 6 sensors + target_states = np.vstack((target_states, ff.obs.copy()[2:8])) + print(f" target: {target_states.shape}") + + # -- Phase 2: add pinball (ids 4,5,6) -- + print("\n--- Add pinball ---") + ff.add_cylinder(FRONT_POS, PR) # front(4) + ff.add_cylinder(BOTTOM_POS, PR) # bottom(5) + ff.add_cylinder(TOP_POS, PR) # top(6) + n_obj = ff.obs.size // 2 # 7 + ff.run(int(4 * NX / U0), np.zeros(n_obj, dtype=DATA_TYPE)) + ff.get_ddf() + ff.save_ddf() # checkpoint + + # -- Phase 3: Norm computation -- + print("\n--- Norm (obs[2:14]) ---") + fifo = deque(maxlen=FIFO_LEN) + for _ in range(FIFO_LEN): + ff.run(SAMPLE_INTERVAL, np.zeros(n_obj, dtype=DATA_TYPE)) + fifo.append(ff.obs.copy()[2:14]) # [sensors(6), forces(6)] + temp = np.array(fifo, dtype=DATA_TYPE) + force_norm_fact = 6.0 * float(np.max(np.abs(temp[:, 6:12]))) + sens_deviation = np.mean(temp[:, 0:6], axis=0).astype(DATA_TYPE) + sens_norm_fact = np.zeros(6, dtype=DATA_TYPE) + for i in range(6): + sens_norm_fact[i] = 5.0 * float(np.max(np.abs(temp[:, i] - sens_deviation[i]))) + print(f" force_norm_fact={force_norm_fact:.6f}") + + # -- Phase 4: Bias-action FIFO (uni_test: [0,0,0,0,0,-4*U0,4*U0]) -- + print("\n--- Bias FIFO ---") + ff.apply_ddf() + bias = np.zeros(n_obj, dtype=DATA_TYPE) + bias[5] = -4.0 * U0 # bottom + bias[6] = 4.0 * U0 # top + fifo.clear() + for _ in range(FIFO_LEN): + ff.run(SAMPLE_INTERVAL, bias) + fifo.append(ff.obs.copy()[2:14]) + save_states = list(fifo) + ff.apply_ddf() + + # -- Phase 5: PPO inference -- + print("\n--- PPO inference (500 steps) ---") + import torch + dev = f"cuda:{device_id}" if torch.cuda.is_available() else "cpu" + model = _load_ppo(MODEL_CLOAK, device=dev, s_dim=12, a_dim=3) + model.set_random_seed(0) + + n_steps = 500 + fifo = deque(maxlen=FIFO_LEN) + for s in save_states: + fifo.append(np.array(s, dtype=DATA_TYPE)) + + obs = np.zeros(12, dtype=DATA_TYPE) + rewards, sims, cds, cls = [], [], [], [] + + for step in range(n_steps): + action, _ = model.predict(obs, deterministic=True) + action = action.astype(DATA_TYPE).flatten() + + # Action: legacy pattern, pinball at indices 4,5,6 + temp_a = np.zeros(n_obj, dtype=DATA_TYPE) + temp_a[4:7] = (action * 8.0 + np.array([0.0, -4.0, 4.0], dtype=DATA_TYPE)) * U0 + + ff.context.push() + try: + ff.run(SAMPLE_INTERVAL, temp_a) + finally: + ff.context.pop() + + obs_slice = ff.obs.copy()[2:14] # [sensors(6), forces(6)] + fifo.append(obs_slice) + + # Observation: [forces_norm(6), sens_norm(6)] ← matches analysis_crossre order + forces_norm = obs_slice[6:12] / force_norm_fact + sens_norm = (obs_slice[0:6] - sens_deviation) / sens_norm_fact + obs = np.clip(np.hstack([forces_norm, sens_norm]), -1.0, 1.0).astype(DATA_TYPE) + + # Reward (legacy env style: cd/cl from forces in obs_slice[6:12]) + sarr = np.array(fifo, dtype=DATA_TYPE) + if len(sarr) >= 30: + f = sarr[-1, 6:12] / force_norm_fact + cd = float((f[0] + f[2] + f[4]) / 3.0) + cl = float((f[1] + f[3] + f[5]) / 3.0) + + # DTW: lag from middle sensor (index 1 in sensor block = obs[4] in obs[2:14] = sensor1_uy) + ref = target_states[30:60, 1] + cur = sarr[-30:, 1] + lag = _calc_lag(ref, cur) + + sim = 0.0 + for i in range(6): + t_seq = np.roll(target_states[:, i], -lag)[30:60] + s_seq = sarr[-30:, i] + sim += _calc_dtw_sim(t_seq, s_seq) / 6.0 + + r_cd = float(np.exp(-abs(cd * 20.0))) + r_cl = float(np.exp(-abs(cl * 80.0))) + r_sim = float(np.exp(-10.0 * abs(sim - 1.0))) + reward = float(min(0.3 * r_cd + 0.4 * r_cl + 0.3 * r_sim, 1.0)) + else: + cd, cl, sim, reward = 0.0, 0.0, 0.0, 0.0 + + rewards.append(reward) + sims.append(sim) + cds.append(cd) + cls.append(cl) + + _save_vorticity_png(ff, os.path.join(out_dir, "cloak_vorticity_final.png"), + title="Cloak (Karman) Re=100", u0=U0) + + tail = 100 + result = { + "case": "cloak_karman", "n_steps": n_steps, + "mean_reward_last100": float(np.mean(rewards[-tail:])), + "std_reward_last100": float(np.std(rewards[-tail:])), + "mean_similarity_last100": float(np.mean(sims[-tail:])), + "mean_cd_last100": float(np.mean(cds[-tail:])), + "mean_cl_last100": float(np.mean(cls[-tail:])), + "force_norm_fact": force_norm_fact, + "vorticity_png": "cloak_vorticity_final.png", + } + print(f"\n mean_reward={result['mean_reward_last100']:.4f} " + f"sim={result['mean_similarity_last100']:.4f} cd={result['mean_cd_last100']:.4f}") + del ff, model + return result + + +# --------------------------------------------------------------------------- +# Illusion validation +# --------------------------------------------------------------------------- + +def validate_illusion(device_id, out_dir): + print("=" * 60) + print("Validating Illusion (1L)") + print("=" * 60) + os.makedirs(out_dir, exist_ok=True) + + cuda_cfg, field_cfg = load_configs(CONFIG_DIR) + field_cfg = field_cfg._replace(viscosity=0.008, velocity=U0_ILLUSION) + + # -- Target recording -- + print("\n--- Target recording ---") + ff_tgt = FlowField(field_cfg, cuda_cfg, device_id=device_id) + ff_tgt.add_cylinder((20.0 * L0, CENTER_Y, 0.0), L0) # target cylinder + for y in ILL_SENSOR_YS: + ff_tgt.add_sensor((ILL_SENSOR_X, y, 0.0), SR) + n_tgt = ff_tgt.obs.size // 2 # 4 + ff_tgt.run(int(4 * NX / U0_ILLUSION), np.zeros(n_tgt, dtype=DATA_TYPE)) + + target_states = np.empty((0, 8), dtype=DATA_TYPE) + for _ in range(FIFO_LEN): + ff_tgt.run(SAMPLE_INTERVAL_ILL, np.zeros(n_tgt, dtype=DATA_TYPE)) + target_states = np.vstack((target_states, ff_tgt.obs.copy()[0:8])) + harmonics = _analyze_harmonics(target_states, 5) + print(f" target: {target_states.shape}, harmonics: {len(harmonics)} ch") + del ff_tgt + + # -- Pinball env (6 objects: 3 sensors + 3 pinball) -- + # Add sensors first, then pinball (matches legacy_env_imit.py) + print("\n--- Build pinball env ---") + ff = FlowField(field_cfg, cuda_cfg, device_id=device_id) + for y in ILL_SENSOR_YS: + ff.add_sensor((ILL_SENSOR_X, y, 0.0), SR) # sensors(0,1,2) + ff.add_cylinder(ILL_FRONT, PR) # front(3) + ff.add_cylinder(ILL_BOTTOM, PR) # bottom(4) + ff.add_cylinder(ILL_TOP, PR) # top(5) + n_obj = ff.obs.size // 2 # 6 + ff.run(int(4 * NX / U0_ILLUSION), np.zeros(n_obj, dtype=DATA_TYPE)) + ff.get_ddf() + ff.save_ddf() + + # -- Norm (obs[0:12] = [sensors(6), forces(6)]) -- + print("\n--- Norm ---") + fifo = deque(maxlen=FIFO_LEN) + for _ in range(FIFO_LEN): + ff.run(SAMPLE_INTERVAL_ILL, np.zeros(n_obj, dtype=DATA_TYPE)) + fifo.append(ff.obs.copy()[0:12]) + temp = np.array(fifo, dtype=DATA_TYPE) + force_norm_fact = 6.0 * float(np.max(np.abs(temp[:, 6:12]))) + sens_deviation = np.mean(temp[:, 0:6], axis=0).astype(DATA_TYPE) + sens_norm_fact = np.zeros(6, dtype=DATA_TYPE) + for i in range(6): + sens_norm_fact[i] = 5.0 * float(np.max(np.abs(temp[:, i] - sens_deviation[i]))) + print(f" force_norm_fact={force_norm_fact:.6f}") + + # -- Bias FIFO -- + print("\n--- Bias FIFO ---") + ff.apply_ddf() + bias = np.zeros(n_obj, dtype=DATA_TYPE) + bias[4] = -1.0 * U0_ILLUSION + bias[5] = 1.0 * U0_ILLUSION + fifo.clear() + for _ in range(FIFO_LEN): + ff.run(SAMPLE_INTERVAL_ILL, bias) + fifo.append(ff.obs.copy()[0:12]) + save_states = list(fifo) + ff.apply_ddf() + + # -- PPO inference -- + print("\n--- PPO inference (500 steps) ---") + import torch + dev = f"cuda:{device_id}" if torch.cuda.is_available() else "cpu" + model = _load_ppo(MODEL_ILLUSION, device=dev, s_dim=14, a_dim=3) + model.set_random_seed(19) + + n_steps = 500 + fifo = deque(maxlen=FIFO_LEN) + for s in save_states: + fifo.append(np.array(s, dtype=DATA_TYPE)) + + obs = np.zeros(14, dtype=DATA_TYPE) + rewards, sims, cds, cls = [], [], [], [] + + for step in range(n_steps): + action, _ = model.predict(obs, deterministic=True) + action = action.astype(DATA_TYPE).flatten() + + temp_a = np.zeros(n_obj, dtype=DATA_TYPE) + temp_a[3:6] = (action * 8.0 + np.array([0.0, -2.0, 2.0], dtype=DATA_TYPE)) * U0_ILLUSION + + ff.context.push() + try: + ff.run(SAMPLE_INTERVAL_ILL, temp_a) + finally: + ff.context.pop() + + obs_slice = ff.obs.copy()[0:12] + fifo.append(obs_slice) + + # 14-dim obs: [forces_norm(6), sens_norm(6), target_cd, target_cl] + forces_norm = obs_slice[6:12] / force_norm_fact + sens_norm = (obs_slice[0:6] - sens_deviation) / sens_norm_fact + target_recon = _gen_target(step, harmonics) + t_cd_n = float(target_recon[0]) / force_norm_fact + t_cl_n = float(target_recon[1]) / force_norm_fact + obs = np.clip(np.hstack([forces_norm, sens_norm, t_cd_n, t_cl_n]), + -1.0, 1.0).astype(DATA_TYPE) + + # Reward + sarr = np.array(fifo, dtype=DATA_TYPE) + if len(sarr) >= 36: + f = sarr[-1, 6:12] / force_norm_fact + cd = float(f[0] + f[2] + f[4]) # SUM + cl = float(f[1] + f[3] + f[5]) + + ref = target_states[36:72, 3] + cur = sarr[-36:, 3] + lag = _calc_lag(ref, cur) + + sim = 0.0 + for i in range(6): + t_seq = np.roll(target_states[:, i + 2], -lag)[36:72] + s_seq = sarr[-36:, i] + sim += _calc_dtw_sim(t_seq, s_seq) / 6.0 + + t_cd = float(target_recon[0]) / force_norm_fact + t_cl = float(target_recon[1]) / force_norm_fact + r_cd = float(np.exp(-abs((cd - t_cd) * 10.0))) + r_cl = float(np.exp(-abs((cl - t_cl) * 10.0))) + r_sim = float(np.exp(-10.0 * abs(sim - 1.0))) + reward = float(min(0.3 * r_cd + 0.3 * r_cl + 0.4 * r_sim, 1.0)) + else: + cd, cl, sim, reward = 0.0, 0.0, 0.0, 0.0 + + rewards.append(reward) + sims.append(sim) + cds.append(cd) + cls.append(cl) + + _save_vorticity_png(ff, os.path.join(out_dir, "illusion_vorticity_final.png"), + title="Illusion (1L) Re=100", u0=U0_ILLUSION) + + tail = 100 + result = { + "case": "illusion_1L", "n_steps": n_steps, + "mean_reward_last100": float(np.mean(rewards[-tail:])), + "std_reward_last100": float(np.std(rewards[-tail:])), + "mean_similarity_last100": float(np.mean(sims[-tail:])), + "mean_cd_last100": float(np.mean(cds[-tail:])), + "mean_cl_last100": float(np.mean(cls[-tail:])), + "force_norm_fact": force_norm_fact, + "vorticity_png": "illusion_vorticity_final.png", + } + print(f"\n mean_reward={result['mean_reward_last100']:.4f} " + f"sim={result['mean_similarity_last100']:.4f} cd={result['mean_cd_last100']:.4f}") + del ff, model + return result + + +# --------------------------------------------------------------------------- +# Baseline +# --------------------------------------------------------------------------- + +def validate_uncontrolled(device_id, out_dir): + cuda_cfg, field_cfg = load_configs(CONFIG_DIR) + field_cfg = field_cfg._replace(viscosity=0.004, velocity=U0) + ff = FlowField(field_cfg, cuda_cfg, device_id=device_id) + for y in SENSOR_YS: + ff.add_sensor((SENSOR_X, y, 0.0), SR) + ff.add_cylinder(FRONT_POS, PR) + ff.add_cylinder(BOTTOM_POS, PR) + ff.add_cylinder(TOP_POS, PR) + ff.run(int(4 * NX / U0), np.zeros(6, dtype=DATA_TYPE)) + for _ in range(200): + ff.run(SAMPLE_INTERVAL, np.zeros(6, dtype=DATA_TYPE)) + _save_vorticity_png(ff, os.path.join(out_dir, "uncontrolled_vorticity_final.png"), + title="Uncontrolled Pinball Re=100", u0=U0) + del ff + return {"case": "uncontrolled", "vorticity_png": "uncontrolled_vorticity_final.png"} + + +def validate_target(device_id, out_dir): + cuda_cfg, field_cfg = load_configs(CONFIG_DIR) + field_cfg = field_cfg._replace(viscosity=0.004, velocity=U0) + ff = FlowField(field_cfg, cuda_cfg, device_id=device_id) + ff.add_cylinder((20.0 * L0, CENTER_Y, 0.0), L0) + for y in SENSOR_YS: + ff.add_sensor((SENSOR_X, y, 0.0), SR) + ff.run(int(4 * NX / U0), np.zeros(4, dtype=DATA_TYPE)) + for _ in range(200): + ff.run(SAMPLE_INTERVAL, np.zeros(4, dtype=DATA_TYPE)) + _save_vorticity_png(ff, os.path.join(out_dir, "target_vorticity_final.png"), + title="Target 2D Cylinder Re=100", u0=U0) + del ff + return {"case": "target_cylinder", "vorticity_png": "target_vorticity_final.png"} + + +# --------------------------------------------------------------------------- +# Main +# --------------------------------------------------------------------------- + +def main(): + ap = argparse.ArgumentParser(description="Validate control quality") + ap.add_argument("--device", type=int, default=2) + ap.add_argument("--case", type=str, required=True, + choices=["all", "cloak", "illusion", "uncontrolled", "target"]) + args = ap.parse_args() + out_dir = os.path.join(OUTPUT_DIR, "validation") + os.makedirs(out_dir, exist_ok=True) + t0 = time.time() + results = {} + if args.case in ("all", "cloak"): + results["cloak"] = validate_cloak(args.device, out_dir) + if args.case in ("all", "illusion"): + results["illusion"] = validate_illusion(args.device, out_dir) + if args.case in ("all", "uncontrolled"): + results["uncontrolled"] = validate_uncontrolled(args.device, out_dir) + if args.case in ("all", "target"): + results["target"] = validate_target(args.device, out_dir) + with open(os.path.join(out_dir, "validation_results.json"), "w") as f: + json.dump({"device": args.device, "elapsed_sec": time.time() - t0, + "results": results}, f, indent=2) + print(f"\n{'='*60}\nSummary\n{'='*60}") + for c, r in results.items(): + if "mean_reward_last100" in r: + print(f" {c}: reward={r['mean_reward_last100']:.4f} sim={r['mean_similarity_last100']:.4f}") + else: + print(f" {c}: baseline") + print(f"\nVorticity: {out_dir}/ | Total: {time.time()-t0:.1f}s") + return 0 + + +if __name__ == "__main__": + main() diff --git a/src/__init__.py b/src/__init__.py index 792fc3a..8b13789 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -1,11 +1 @@ -""" -DynamisLab: Machine Learning for Computational Fluid Dynamics -""" -__version__ = '0.1.0' -__author__ = 'Frank14f' - -from . import config -from . import environments - -__all__ = ['config', 'environments', '__version__'] diff --git a/src/analysis_cloak/__init__.py b/src/analysis_cloak/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/analysis_cloak/common/__init__.py b/src/analysis_cloak/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/analysis_cloak/common/feature_builder.py b/src/analysis_cloak/common/feature_builder.py new file mode 100644 index 0000000..8023b73 --- /dev/null +++ b/src/analysis_cloak/common/feature_builder.py @@ -0,0 +1,214 @@ +"""Unified feature builder for all cloak scenes. + +Produces dimensionless features with consistent G-equivariant structure. +All scenes (Karman, steady, vortex, erase) use this same builder. +""" +from __future__ import annotations + +from typing import Dict, List, Optional, Tuple + +import numpy as np + + +# -- Physical constants ------------------------------------------------------ +U0 = 0.01 # inlet velocity (lattice units) +D_CYL = 20.0 # cylinder diameter (lattice) + + +# -- Dimensionless conversion ------------------------------------------------ + +def compute_dimensionless( + sensors: np.ndarray, # (T, 6) raw lattice [s0_ux,s0_uy, s1_ux,s1_uy, s2_ux,s2_uy] + forces: np.ndarray, # (T, 6) raw lattice [f0_fx,f0_fy, f1_fx,f1_fy, f2_fx,f2_fy] + u0: float = U0, + d: float = D_CYL, + rho: float = 1.0, +) -> Dict[str, np.ndarray]: + """Convert raw lattice CFD data to dimensionless quantities. + + Sensor order: [s0_ux,s0_uy, s1_ux,s1_uy, s2_ux,s2_uy] + where s0=top(y=+2L0), s1=mid(y=0), s2=bottom(y=-2L0) + Force order: [front_fx,front_fy, bottom_fx,bottom_fy, top_fx,top_fy] + + Returns: + u_hat_B, u_hat_C, u_hat_T: nondim streamwise velocity (bottom/centre/top) + v_hat_B, v_hat_C, v_hat_T: nondim crosswise velocity + Cd_F, Cd_T, Cd_B: drag coefficient per cylinder + Cl_F, Cl_T, Cl_B: lift coefficient per cylinder + """ + s = np.asarray(sensors, dtype=np.float64) + f = np.asarray(forces, dtype=np.float64) + + # Sensor positions: s0=top, s1=centre, s2=bottom + # Convention: B=bottom=s2, C=centre=s1, T=top=s0 + return { + "u_hat_T": s[:, 0] / u0, + "v_hat_T": s[:, 1] / u0, + "u_hat_C": s[:, 2] / u0, + "v_hat_C": s[:, 3] / u0, + "u_hat_B": s[:, 4] / u0, + "v_hat_B": s[:, 5] / u0, + "Cd_F": 2.0 * f[:, 0] / (rho * u0**2 * d), + "Cl_F": 2.0 * f[:, 1] / (rho * u0**2 * d), + "Cd_B": 2.0 * f[:, 2] / (rho * u0**2 * d), + "Cl_B": 2.0 * f[:, 3] / (rho * u0**2 * d), + "Cd_T": 2.0 * f[:, 4] / (rho * u0**2 * d), + "Cl_T": 2.0 * f[:, 5] / (rho * u0**2 * d), + } + + +# -- G operator (corrected) -------------------------------------------------- + +def apply_G_alpha(alpha: np.ndarray) -> np.ndarray: + """Apply mirror G to action: [aF, aT, aB] -> [-aF, -aB, -aT].""" + return np.array([-alpha[0], -alpha[2], -alpha[1]], dtype=alpha.dtype) + + +def apply_G_x(dim: Dict[str, np.ndarray], + a_prev: np.ndarray, + a_prev2: np.ndarray) -> Tuple[Dict, np.ndarray, np.ndarray]: + """Apply G to dimensionless state. + + Returns (G_dim, G_a_prev, G_a_prev2) with corrected sign rules. + """ + G_dim = { + "u_hat_B": dim["u_hat_T"], "u_hat_C": dim["u_hat_C"], "u_hat_T": dim["u_hat_B"], + "v_hat_B": -dim["v_hat_T"], "v_hat_C": -dim["v_hat_C"], "v_hat_T": -dim["v_hat_B"], + "Cd_F": dim["Cd_F"], "Cd_T": dim["Cd_B"], "Cd_B": dim["Cd_T"], + "Cl_F": -dim["Cl_F"], "Cl_T": -dim["Cl_B"], "Cl_B": -dim["Cl_T"], + } + G_a_prev = np.column_stack([-a_prev[:, 0], -a_prev[:, 2], -a_prev[:, 1]]) + G_a_prev2 = np.column_stack([-a_prev2[:, 0], -a_prev2[:, 2], -a_prev2[:, 1]]) + return G_dim, G_a_prev, G_a_prev2 + + +# -- Feature key definitions ------------------------------------------------- + +CORE_FEAT_KEYS = [ + "u_m", "u_a", "u_c", + "v_a", + "Cd_tot", "Cd_rear", + "Cl_tot", "Cl_diff", + "sin_ua", "cos_ua", + "aF_lag1", "aB_lag1", "aT_lag1", + "daF", "daB", "daT", +] + +MU_FEAT_KEYS = ["mu", "mu_u_a", "mu_v_a", "mu_Cd_tot", "mu_Cl_diff"] + +ALL_FEAT_KEYS = CORE_FEAT_KEYS + MU_FEAT_KEYS + + +# -- Feature computation ----------------------------------------------------- + +def compute_features( + dim: Dict[str, np.ndarray], + actions_prev: np.ndarray, # (T, 3) physical omega(t-1) or nondim alpha(t-1) + actions_prev2: np.ndarray, # (T, 3) physical omega(t-2) + mu: float, + alpha_mode: bool = False, # if True, actions_prev are already nondim alpha + include_mu: bool = True, +) -> Dict[str, np.ndarray]: + """Compute unified feature dictionary from dimensionless primitives. + + Args: + dim: from compute_dimensionless() + actions_prev: lagged actions (physical omega or nondim alpha) + actions_prev2: twice-lagged actions + mu: 1/Re_D + alpha_mode: if True, actions are already nondim; else convert + include_mu: include mu modulation terms + + Returns dict with all features as (T,) or (T,3) arrays. + """ + T = actions_prev.shape[0] + u_B, u_C, u_T = dim["u_hat_B"], dim["u_hat_C"], dim["u_hat_T"] + v_B, v_C, v_T = dim["v_hat_B"], dim["v_hat_C"], dim["v_hat_T"] + Cd_F, Cd_T, Cd_B = dim["Cd_F"], dim["Cd_T"], dim["Cd_B"] + Cl_F, Cl_T, Cl_B = dim["Cl_F"], dim["Cl_T"], dim["Cl_B"] + + # If actions are in physical omega, convert to nondim alpha + if alpha_mode: + a = actions_prev.astype(np.float64) + a2 = actions_prev2.astype(np.float64) + else: + a = actions_prev.astype(np.float64) / U0 + a2 = actions_prev2.astype(np.float64) / U0 + + sym = {} + + # Sensor combinations (nondim) + sym["u_m"] = (u_B + u_C + u_T) / 3.0 + sym["u_a"] = (u_T - u_B) / 2.0 + sym["u_c"] = u_C.copy() + sym["v_a"] = (v_T - v_B) / 2.0 + + # Force combinations (dimensionless Cd/Cl) + sym["Cd_tot"] = Cd_F + Cd_T + Cd_B + sym["Cd_rear"] = Cd_T + Cd_B + sym["Cl_tot"] = Cl_F + Cl_T + Cl_B + sym["Cl_diff"] = Cl_T - Cl_B + + # Phase + sym["sin_ua"] = np.sin(np.pi * sym["u_a"]) + sym["cos_ua"] = np.cos(np.pi * sym["u_a"]) + + # Memory (nondim alpha) + sym["aF_lag1"] = a[:, 0] + sym["aB_lag1"] = a[:, 1] + sym["aT_lag1"] = a[:, 2] + sym["daF"] = a[:, 0] - a2[:, 0] + sym["daB"] = a[:, 1] - a2[:, 1] + sym["daT"] = a[:, 2] - a2[:, 2] + + # Mu modulation + if include_mu: + sym["mu"] = np.full(T, mu, dtype=np.float64) + sym["mu_u_a"] = sym["u_a"] * mu + sym["mu_v_a"] = sym["v_a"] * mu + sym["mu_Cd_tot"] = sym["Cd_tot"] * mu + sym["mu_Cl_diff"] = sym["Cl_diff"] * mu + + return sym + + +def build_feature_matrix( + sym: Dict[str, np.ndarray], + feat_keys: List[str], + add_bias: bool = True, +) -> np.ndarray: + """Build feature matrix (T, N) from symbol dict.""" + cols = [] + if add_bias: + cols.append(np.ones(sym[feat_keys[0]].shape[0], dtype=np.float64)) + for k in feat_keys: + if k in sym: + cols.append(np.asarray(sym[k], dtype=np.float64)) + else: + # Missing key (e.g. mu terms when include_mu=False) -> zero + T = sym.get("u_m", np.ones(1)).shape[0] + cols.append(np.zeros(T, dtype=np.float64)) + return np.column_stack(cols) + + +def get_feature_names(feat_keys: List[str], add_bias: bool = True) -> List[str]: + """Get feature names matching build_feature_matrix output.""" + names = [] + if add_bias: + names.append("bias") + names.extend(feat_keys) + return names + + +# -- Scene metadata ---------------------------------------------------------- + +def get_scene_metadata(scene: str) -> dict: + """Return default metadata for a cloak scene.""" + meta = { + "karman": {"scene_id": "karman", "control_interval": 800, "target_type": "periodic"}, + "steady": {"scene_id": "steady", "control_interval": 800, "target_type": "steady"}, + "vortex_lamb": {"scene_id": "vortex_lamb", "control_interval": 800, "target_type": "transient"}, + "vortex_taylor": {"scene_id": "vortex_taylor", "control_interval": 800, "target_type": "transient"}, + "erase": {"scene_id": "erase", "control_interval": 600, "target_type": "periodic"}, + } + return meta.get(scene, {"scene_id": scene, "control_interval": 800, "target_type": "unknown"}) diff --git a/src/analysis_cloak/common/prebuild_features.py b/src/analysis_cloak/common/prebuild_features.py new file mode 100644 index 0000000..ada2f99 --- /dev/null +++ b/src/analysis_cloak/common/prebuild_features.py @@ -0,0 +1,81 @@ +"""Pre-build feature matrices for SR (run in pycuda_3_10). + +Saves pickle files that run_sr_gplearn.py can load in sr_env. + +Usage: + conda run -n pycuda_3_10 python prebuild_features.py +""" +from __future__ import annotations + +import os +import pickle +import sys + +import numpy as np + +_THIS = os.path.abspath(os.path.dirname(__file__)) +_PARENT = os.path.abspath(os.path.join(_THIS, "..")) # src/analysis_cloak/ +_REPO = os.path.abspath(os.path.join(_THIS, "..", "..", "..")) +for p in [_PARENT, os.path.join(_PARENT, "..", "analysis_crossre", "scripts"), _REPO]: + if p not in sys.path: + sys.path.insert(0, p) + +from common.feature_builder import ( + compute_dimensionless, compute_features, build_feature_matrix, + get_feature_names, ALL_FEAT_KEYS, U0, +) +from utils import action_to_physical +from cfg import OUTPUT_DIR, ACTION_SCALE, ACTION_BIAS + + +def build(scene: str): + X_f_l, X_r_l, y_l = [], [], [] + if scene == "karman": + for rc in [50, 100, 200]: + npz = np.load(os.path.join(OUTPUT_DIR, f"re{rc}", "controlled.npz")) + s, f = npz["sensors"].astype(np.float64), npz["forces"].astype(np.float64) + ap = action_to_physical(npz["actions"].astype(np.float64), + scale=ACTION_SCALE, bias=ACTION_BIAS, u0=U0) + mu = 2.0 / rc + T = s.shape[0] + a1 = np.zeros((T, 3), dtype=np.float64) + a2 = np.zeros((T, 3), dtype=np.float64) + a1[1:] = ap[:-1]; a2[2:] = ap[:-2] + dim = compute_dimensionless(s, f, u0=U0, d=20.0) + sym = compute_features(dim, a1, a2, mu, alpha_mode=True, include_mu=True) + X_f_l.append(build_feature_matrix(sym, ALL_FEAT_KEYS, add_bias=False)[2:]) + X_r_l.append(build_feature_matrix(sym, ALL_FEAT_KEYS, add_bias=True)[2:]) + y_l.append(ap[2:]) + else: + npz = np.load(os.path.join(_REPO, "src", "analysis_cloak", "scenes", + "steady", "closed_loop", "steady_data.npz")) + s, f = npz["sensors"].astype(np.float64), npz["forces"].astype(np.float64) + ap = npz["actions"].astype(np.float64) + mu = 2.0 / 100 + T = s.shape[0] + a1 = np.zeros((T, 3), dtype=np.float64) + a2 = np.zeros((T, 3), dtype=np.float64) + a1[1:] = ap[:-1]; a2[2:] = ap[:-2] + dim = compute_dimensionless(s, f, u0=U0, d=20.0) + sym = compute_features(dim, a1, a2, mu, alpha_mode=True, include_mu=True) + X_f_l.append(build_feature_matrix(sym, ALL_FEAT_KEYS, add_bias=False)[2:]) + X_r_l.append(build_feature_matrix(sym, ALL_FEAT_KEYS, add_bias=True)[2:]) + y_l.append(ap[2:]) + + data = { + "X_f": np.vstack(X_f_l), + "X_r": np.vstack(X_r_l), + "y": np.vstack(y_l), + "fn_f": get_feature_names(ALL_FEAT_KEYS, add_bias=False), + "fn_r": get_feature_names(ALL_FEAT_KEYS, add_bias=True), + } + out = os.path.join(_REPO, "src", "analysis_cloak", "scenes", scene, "sr", f"{scene}_features.pkl") + os.makedirs(os.path.dirname(out), exist_ok=True) + with open(out, "wb") as f: + pickle.dump(data, f) + print(f"Saved {scene} features: X_f={data['X_f'].shape}, X_r={data['X_r'].shape}") + + +if __name__ == "__main__": + for s in ["karman", "steady"]: + build(s) diff --git a/src/analysis_cloak/common/run_sindy.py b/src/analysis_cloak/common/run_sindy.py new file mode 100644 index 0000000..16f1193 --- /dev/null +++ b/src/analysis_cloak/common/run_sindy.py @@ -0,0 +1,253 @@ +"""Unified SINDy fitting for all cloak scenes. + +Usage: + conda run -n pycuda_3_10 python run_sindy.py --scene karman + conda run -n pycuda_3_10 python run_sindy.py --scene steady +""" +from __future__ import annotations + +import argparse +import json +import os +import sys +from typing import Dict, List, Tuple + +import numpy as np + +_REPO = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..")) +if _REPO not in sys.path: + sys.path.insert(0, _REPO) + +from src.analysis_cloak.common.feature_builder import ( + compute_dimensionless, + compute_features, + build_feature_matrix, + get_feature_names, + apply_G_x, + CORE_FEAT_KEYS, + MU_FEAT_KEYS, + ALL_FEAT_KEYS, + U0, + get_scene_metadata, +) +from src.analysis_crossre.scripts.cfg import OUTPUT_DIR as CROSSRE_OUTPUT +from src.analysis_crossre.scripts.utils import action_to_physical, fit_channel +from src.analysis_crossre.scripts.cfg import ACTION_SCALE, ACTION_BIAS + +THRESHOLDS = [0.0, 0.001, 0.002, 0.005, 0.01, 0.015, 0.02, 0.03, 0.05, 0.1] + + +def load_karman_data(re_code: int) -> Tuple: + """Load Karman cloak data for a single Re.""" + case_dir = os.path.join(CROSSRE_OUTPUT, f"re{re_code}") + npz_path = os.path.join(case_dir, "controlled.npz") + if not os.path.isfile(npz_path): + raise FileNotFoundError(f"Missing {npz_path}") + data = np.load(npz_path) + sensors = data["sensors"].astype(np.float64) + forces = data["forces"].astype(np.float64) + actions_norm = data["actions"].astype(np.float64) + actions_phys = action_to_physical( + actions_norm, scale=ACTION_SCALE, bias=ACTION_BIAS, u0=U0) + mu = 2.0 / re_code + return sensors, forces, actions_phys, mu + + +def load_steady_data(): + """Load steady cloak data.""" + steady_dir = os.path.join(_REPO, "src", "analysis_cloak", "scenes", "steady", "closed_loop") + npz_path = os.path.join(steady_dir, "steady_data.npz") + if not os.path.isfile(npz_path): + raise FileNotFoundError(f"Missing {npz_path}. Run gen_steady_data.py first.") + data = np.load(npz_path) + sensors = data["sensors"].astype(np.float64) + forces = data["forces"].astype(np.float64) + actions_phys = data["actions"].astype(np.float64) + mu = 2.0 / 100 # Re=100 for steady (default Re) + return sensors, forces, actions_phys, mu + + +def build_scene_data(scene: str, re_codes: List[int] = None): + """Build feature matrices for a scene. + + Returns (Theta_front, Theta_rear_input, Y, feat_names_front, feat_names_rear, scene_info) + where Theta_rear_input is the feature matrix for the rear shared-head model (top channel). + """ + all_feats_front = [] + all_feats_rear = [] + all_Y = [] + all_info = [] + + if scene == "karman": + if re_codes is None: + re_codes = [50, 100, 200] + for rc in re_codes: + sensors, forces, actions_phys, mu = load_karman_data(rc) + _append_scene_data(sensors, forces, actions_phys, mu, rc, + all_feats_front, all_feats_rear, all_Y, all_info) + elif scene == "steady": + sensors, forces, actions_phys, mu = load_steady_data() + _append_scene_data(sensors, forces, actions_phys, mu, 100, + all_feats_front, all_feats_rear, all_Y, all_info) + else: + raise ValueError(f"Unknown scene: {scene}") + + # Stack all data + Theta_front = np.vstack(all_feats_front) + Theta_rear = np.vstack(all_feats_rear) + Y = np.vstack(all_Y) + + # Feature names (front has no bias) + feat_names_front = get_feature_names(ALL_FEAT_KEYS, add_bias=False) + feat_names_rear = get_feature_names(ALL_FEAT_KEYS, add_bias=True) + + return Theta_front, Theta_rear, Y, feat_names_front, feat_names_rear, all_info + + +def _append_scene_data(sensors, forces, actions_phys, mu, re_code, + all_feats_front, all_feats_rear, all_Y, all_info): + """Helper: compute features and append to lists.""" + T = sensors.shape[0] + + # Memory terms + a_prev = np.zeros((T, 3), dtype=np.float64) + a_prev2 = np.zeros((T, 3), dtype=np.float64) + a_prev[1:] = actions_phys[:-1] + a_prev2[2:] = actions_phys[:-2] + + # Dimensionless + dim = compute_dimensionless(sensors, forces, u0=U0, d=20.0) + + # Compute features + sym = compute_features(dim, a_prev, a_prev2, mu, alpha_mode=False, include_mu=True) + + # Front model: no bias + T_eff = min(sensors.shape[0], actions_phys.shape[0]) + Theta_f = build_feature_matrix(sym, ALL_FEAT_KEYS, add_bias=False) + + # Rear model (top channel): with bias + Theta_r = build_feature_matrix(sym, ALL_FEAT_KEYS, add_bias=True) + + # Remove warmup (2 steps needed for lag/da) + all_feats_front.append(Theta_f[2:]) + all_feats_rear.append(Theta_r[2:]) + all_Y.append(actions_phys[2:]) + all_info.append({"re_code": re_code, "mu": mu, "n_samples": T - 2}) + + +def fit_sindy(Theta, y, thresholds, output_prefix=""): + """Run SINDy with threshold grid, return results.""" + import pysindy as ps + + std = np.std(Theta, axis=0) + std = np.where(std < 1e-8, 1.0, std) + Theta_s = Theta / std + + results = [] + for th in thresholds: + opt = ps.STLSQ(threshold=th, alpha=1e-4, max_iter=25) + opt.fit(Theta_s, y) + coef = np.asarray(opt.coef_, dtype=np.float64).flatten() / std + + y_pred = Theta @ coef + ssr = float(np.sum((y - y_pred) ** 2)) + sst = float(np.sum((y - np.mean(y)) ** 2) + 1e-12) + r2 = 1.0 - ssr / sst + mae = float(np.mean(np.abs(y - y_pred))) + nz = int(np.sum(np.abs(coef) > 1e-8)) + + results.append({ + "threshold": float(th), "nz": nz, "r2": r2, + "mae": mae, "coef": [float(c) for c in coef], + }) + + return results + + +def run(scene: str, out_dir: str, re_codes: List[int] = None): + """Run SINDy fitting for a scene.""" + print(f"\n{'='*60}") + print(f"Scene: {scene}") + print(f"{'='*60}") + + Theta_f, Theta_r, Y, fn_f, fn_r, info = build_scene_data(scene, re_codes) + print(f" Front features: {Theta_f.shape}") + print(f" Rear features: {Theta_r.shape}") + print(f" Y shape: {Y.shape}") + + # Rear shared-head: fit top channel (ci=2), bottom = -top(Gx) + # We fit both channels independently first for comparison + # Front channel (ci=0, no bias) + print(f"\n --- Front (no bias) ---") + front_results = fit_sindy(Theta_f, Y[:, 0], THRESHOLDS) + best_f = max(front_results, key=lambda r: r["r2"]) + print(f" Best: th={best_f['threshold']:.4f} nz={best_f['nz']:2d} R2={best_f['r2']:.6f}") + + # Rear shared: fit top (ci=2) + print(f"\n --- Top (rear shared-head) ---") + top_results = fit_sindy(Theta_r, Y[:, 2], THRESHOLDS) + best_t = max(top_results, key=lambda r: r["r2"]) + print(f" Best: th={best_t['threshold']:.4f} nz={best_t['nz']:2d} R2={best_t['r2']:.6f}") + + # Bottom (for comparison): fit independently + print(f"\n --- Bottom (independent, for comparison) ---") + bot_results = fit_sindy(Theta_r, Y[:, 1], THRESHOLDS) + best_b = max(bot_results, key=lambda r: r["r2"]) + print(f" Best: th={best_b['threshold']:.4f} nz={best_b['nz']:2d} R2={best_b['r2']:.6f}") + + # Save results + result = { + "scene": scene, + "re_codes": re_codes or "default", + "n_samples_front": Theta_f.shape[0], + "n_features_front": Theta_f.shape[1], + "n_features_rear": Theta_r.shape[1], + "feature_names_front": fn_f, + "feature_names_rear": fn_r, + "front": { + "results": [{k: v for k, v in r.items() if k != "coef"} for r in front_results], + "best": {k: v for k, v in best_f.items() if k != "coef"}, + "best_coef": best_f["coef"], + "sparsity_curve": [(r["threshold"], r["nz"], r["r2"]) for r in front_results], + }, + "top": { + "results": [{k: v for k, v in r.items() if k != "coef"} for r in top_results], + "best": {k: v for k, v in best_t.items() if k != "coef"}, + "best_coef": best_t["coef"], + "sparsity_curve": [(r["threshold"], r["nz"], r["r2"]) for r in top_results], + }, + "bottom": { + "results": [{k: v for k, v in r.items() if k != "coef"} for r in bot_results], + "best": {k: v for k, v in best_b.items() if k != "coef"}, + "best_coef": best_b["coef"], + "sparsity_curve": [(r["threshold"], r["nz"], r["r2"]) for r in bot_results], + }, + "scene_info": info, + } + + os.makedirs(out_dir, exist_ok=True) + out_path = os.path.join(out_dir, f"{scene}_sindy.json") + with open(out_path, "w") as f: + json.dump(result, f, indent=2) + print(f"\nSaved: {out_path}") + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("--scene", type=str, required=True, choices=["karman", "steady", "all"]) + ap.add_argument("--out-dir", type=str, + default=os.path.join(_REPO, "src", "analysis_cloak", "scenes")) + ap.add_argument("--re-codes", type=str, default=None, + help="Comma-separated Re codes for Karman (default: 50,100,200)") + args = ap.parse_args() + + re_codes = [int(r) for r in args.re_codes.split(",")] if args.re_codes else None + scenes = ["karman", "steady"] if args.scene == "all" else [args.scene] + + for scene in scenes: + sindy_dir = os.path.join(args.out_dir, scene, "sindy") + run(scene, sindy_dir, re_codes) + + +if __name__ == "__main__": + main() diff --git a/src/analysis_cloak/common/run_sr_pareto.py b/src/analysis_cloak/common/run_sr_pareto.py new file mode 100644 index 0000000..7898362 --- /dev/null +++ b/src/analysis_cloak/common/run_sr_pareto.py @@ -0,0 +1,84 @@ +"""Pareto analysis of SINDy threshold grid. +No external SR library. Runs in any env. + +Usage: python run_sr_pareto.py --scene karman +""" +from __future__ import annotations + +import argparse +import json +import os +import sys +from typing import List, Tuple + +import numpy as np + +_THIS = os.path.abspath(os.path.dirname(__file__)) +_REPO = os.path.abspath(os.path.join(_THIS, "..", "..", "..")) + + +def load_sindy(scene: str): + path = os.path.join(_REPO, "src", "analysis_cloak", "scenes", scene, "sindy", f"{scene}_sindy.json") + with open(path) as f: + return json.load(f) + + +def pareto(points): + sp = sorted(points, key=lambda x: (x[0], x[1])) + front, best = [], float("inf") + for c, e in sp: + if e < best: + front.append((c, e)) + best = e + return front + + +def fmt(fn, coef, th): + ca = np.array(coef, dtype=np.float64) + sc = np.max(np.abs(ca)) if np.max(np.abs(ca)) > 0 else 1.0 + mask = np.abs(ca) / sc >= th + terms = [f"{ca[i]:+.4f}*{fn[i]}" for i in range(len(fn)) if mask[i]] + return " ".join(terms) if terms else "0" + + +def analyze(name, fn, ch): + grid = ch["results"] + pts = [(g["nz"], 1.0 - g["r2"]) for g in grid] + front = pareto(pts) + best = ch["best"] + coef = ch["best_coef"] + print(f"\n {name}:") + for nz, err in front: + r2 = 1.0 - err + for g in grid: + if g["nz"] == nz and abs(1.0 - g["r2"] - err) < 1e-10: + th = g["threshold"] + print(f" nz={nz:2d} R2={r2:.6f} th={th:.4f}") + if nz <= 8: + print(f" {fmt(fn, coef, th)[:120]}") + print(f" Best: R2={best['r2']:.6f}") + return {"channel": name, "best_r2": best["r2"], + "best_nz": sum(1 for c in coef if abs(float(c)) > 1e-8), + "pareto": [{"nz": nz, "r2": 1.0 - e} for nz, e in front]} + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("--scene", required=True, choices=["karman", "steady"]) + args = ap.parse_args() + s = load_sindy(args.scene) + print(f"Pareto SR: {args.scene}") + chs = [("front", s["feature_names_front"], s["front"]), + ("top", s["feature_names_rear"], s["top"]), + ("bottom", s["feature_names_rear"], s["bottom"])] + results = {"scene": args.scene, "channels": [analyze(*c) for c in chs]} + out = os.path.join(_REPO, "src", "analysis_cloak", "scenes", args.scene, "sr", + f"{args.scene}_sr_pareto.json") + os.makedirs(os.path.dirname(out), exist_ok=True) + with open(out, "w") as f: + json.dump(results, f, indent=2) + print(f"Saved: {out}") + + +if __name__ == "__main__": + main() diff --git a/src/analysis_cloak/comparisons/__init__.py b/src/analysis_cloak/comparisons/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/analysis_cloak/comparisons/support_overlap/__init__.py b/src/analysis_cloak/comparisons/support_overlap/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/analysis_cloak/comparisons/support_overlap/run_overlap.py b/src/analysis_cloak/comparisons/support_overlap/run_overlap.py new file mode 100644 index 0000000..97c8e86 --- /dev/null +++ b/src/analysis_cloak/comparisons/support_overlap/run_overlap.py @@ -0,0 +1,84 @@ +"""Support Overlap Analysis: Karman vs Steady. + +Compares SINDy support sets at a given sparsity threshold. +""" +from __future__ import annotations + +import json +import os +import sys +from typing import Dict, List, Tuple + +import numpy as np + +_REPO = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..", "..")) +if _REPO not in sys.path: + sys.path.insert(0, _REPO) + +THRESHOLD = 0.002 + + +def load_sindy(scene: str) -> dict: + path = os.path.join(_REPO, "src", "analysis_cloak", "scenes", scene, "sindy", f"{scene}_sindy.json") + with open(path) as f: + return json.load(f) + + +def get_active(feat_names: List[str], coef: List[float], threshold: float) -> Dict[str, float]: + scale = max(abs(c) for c in coef) if coef and max(abs(c) for c in coef) > 1e-12 else 1.0 + active = {} + for name, c in zip(feat_names, coef): + if abs(c) / scale >= threshold: + active[name] = c + return active + + +def classify(k_active: Dict, s_active: Dict) -> Tuple[List, List, List]: + k_set = set(k_active.keys()) + s_set = set(s_active.keys()) + return sorted(k_set & s_set), sorted(k_set - s_set), sorted(s_set - k_set) + + +def feat_group(name: str) -> str: + if name == "bias": return "bias" + if name in ("u_m", "u_a", "u_c", "v_a", "sin_ua", "cos_ua"): return "sensor" + if name in ("Cd_tot", "Cd_rear", "Cl_tot", "Cl_diff"): return "force" + if "lag1" in name: return "memory_lag" + if name.startswith("da"): return "memory_delta" + if name == "mu" or name.startswith("mu_"): return "mu_mod" + return "other" + + +def main(): + print("=" * 60) + print(f"Support Overlap: Karman vs Steady (th={THRESHOLD})") + print("=" * 60) + + k = load_sindy("karman") + s = load_sindy("steady") + + channels = [ + ("Front", "feature_names_front", "front"), + ("Top (shared)", "feature_names_rear", "top"), + ("Bottom", "feature_names_rear", "bottom"), + ] + + for label, fn_key, ch_key in channels: + print(f"\n--- {label} ---") + fn_k, fn_s = k[fn_key], s[fn_key] + ka = get_active(fn_k, k[ch_key]["best_coef"], THRESHOLD) + sa = get_active(fn_s, s[ch_key]["best_coef"], THRESHOLD) + shared, ko, so = classify(ka, sa) + + print(f" Karman nz={len(ka)} Steady nz={len(sa)} Shared={len(shared)}") + for name in shared: + g = feat_group(name) + print(f" {name:20s} K={ka[name]:+9.6f} S={sa[name]:+9.6f} [{g}]") + for name in ko: + print(f" K-ONLY {name:20s} K={ka[name]:+9.6f} [{feat_group(name)}]") + for name in so: + print(f" S-ONLY {name:20s} S={sa[name]:+9.6f} [{feat_group(name)}]") + + +if __name__ == "__main__": + main() diff --git a/src/analysis_cloak/scenes/karman/closed_loop/re200_freq_test.json b/src/analysis_cloak/scenes/karman/closed_loop/re200_freq_test.json new file mode 100644 index 0000000..4661bcb --- /dev/null +++ b/src/analysis_cloak/scenes/karman/closed_loop/re200_freq_test.json @@ -0,0 +1,5 @@ +{ + "si800": 0.7044314206319138, + "si400": 0.7877798695065495, + "si200": 0.6587465172737008 +} \ No newline at end of file diff --git a/src/analysis_cloak/scenes/karman/closed_loop/test_re200_freq.py b/src/analysis_cloak/scenes/karman/closed_loop/test_re200_freq.py new file mode 100644 index 0000000..3804512 --- /dev/null +++ b/src/analysis_cloak/scenes/karman/closed_loop/test_re200_freq.py @@ -0,0 +1,182 @@ +"""Re200 control frequency experiment. + +Tests if higher control frequency improves Re200 performance. +Uses v23 predictor (v2 coeffs + front bias=0 + rear shared-head). + +Usage: + conda run -n pycuda_3_10 python test_re200_freq.py --device 2 +""" +import argparse +import json +import os +import sys +from collections import deque + +import numpy as np + +_PROJ = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..", "..", "..")) +if _PROJ not in sys.path: + sys.path.insert(0, _PROJ) +from LegacyCelerisLab import FlowField + +from src.analysis_crossre.scripts.utils import ( + nu_from_re, action_to_physical, scale_action, build_karman_cloak_env, + add_pinball, compute_physical_symbols, save_vorticity_png, + vorticity_from_ddf, compute_similarity, load_legacy_configs, +) +from src.analysis_crossre.scripts.cfg import ( + OUTPUT_DIR, FIFO_LEN, CONV_LEN, S_DIM, ACTION_SCALE, ACTION_BIAS, U0, CONFIG_DIR, +) + +DATA_TYPE = np.float32 +RE_CODE = 200 +V2_RESULTS = os.path.join(OUTPUT_DIR, "sindy", "sindy_results_v2.json") + +# Feature keys matching v2 layout +V2_FEAT_KEYS = [ + "u_m", "u_a", "u_c", "v_a", + "Fx_tot", "Fx_rear", "Fy_tot", "Fy_diff", + "sin_ua", "cos_ua", + "a0_lag1", "a1_lag1", "a2_lag1", + "da0", "da1", "da2", +] +V2_MU_KEYS = ["mu", "mu_u_a", "mu_v_a", "mu_Fx_tot", "mu_Fy_diff", "mu_Fy_tot"] + + +def apply_G_raw(obs_slice, a_prev, a_prev2): + G_obs = np.zeros(12, dtype=np.float64) + G_obs[0] = obs_slice[4] + G_obs[1] = -obs_slice[5] + G_obs[2] = obs_slice[2] + G_obs[3] = -obs_slice[3] + G_obs[4] = obs_slice[0] + G_obs[5] = -obs_slice[1] + G_obs[6] = obs_slice[6] + G_obs[7] = -obs_slice[7] + G_obs[8] = obs_slice[10] + G_obs[9] = -obs_slice[11] + G_obs[10] = obs_slice[8] + G_obs[11] = -obs_slice[9] + G_a_prev = np.array([-a_prev[0], -a_prev[2], -a_prev[1]], dtype=np.float64) + G_a_prev2 = np.array([-a_prev2[0], -a_prev2[2], -a_prev2[1]], dtype=np.float64) + return G_obs, G_a_prev, G_a_prev2 + + +def build_feat_vec(obs_slice, a_prev, a_prev2, mu, add_bias): + sensors = obs_slice[0:6].astype(np.float64).reshape(1, 6) + forces = obs_slice[6:12].astype(np.float64).reshape(1, 6) + ap = a_prev.astype(np.float64).reshape(1, 3) + ap2 = a_prev2.astype(np.float64).reshape(1, 3) + sym = compute_physical_symbols(sensors, forces, ap, ap2) + sym["mu"] = np.array([mu]) + sym["mu_u_a"] = sym["u_a"] * mu + sym["mu_v_a"] = sym["v_a"] * mu + sym["mu_Fx_tot"] = sym["Fx_tot"] * mu + sym["mu_Fy_diff"] = sym["Fy_diff"] * mu + sym["mu_Fy_tot"] = sym["Fy_tot"] * mu + vals = [] + if add_bias: + vals.append(1.0) + for k in V2_FEAT_KEYS: + vals.append(float(sym[k][0])) + for k in V2_MU_KEYS: + vals.append(float(sym[k][0])) + return np.array(vals, dtype=np.float64) + + +def load_coefs(path): + with open(path) as f: + data = json.load(f) + coefs_list = data["cross_re"]["channels"] + coefs_list[0]["best_coef"][0] = 0.0 # zero front bias + return { + "front": {"coef": np.array(coefs_list[0]["best_coef"], dtype=np.float64), "has_bias": True}, + "top": {"coef": np.array(coefs_list[2]["best_coef"], dtype=np.float64), "has_bias": True}, + } + + +def predict(obs_slice, a_prev, a_prev2, coefs, mu): + feat = build_feat_vec(obs_slice, a_prev, a_prev2, mu, add_bias=True) + front = float(feat @ coefs["front"]["coef"]) + top = float(feat @ coefs["top"]["coef"]) + G_obs, G_a_prev, G_a_prev2 = apply_G_raw(obs_slice, a_prev, a_prev2) + feat_G = build_feat_vec(G_obs, G_a_prev, G_a_prev2, mu, add_bias=True) + bottom = -float(feat_G @ coefs["top"]["coef"]) + return np.array([front, bottom, top], dtype=np.float64) + + +def run(re_code, sample_interval, device_id, output_dir): + mu = 2.0 / re_code + label = f"Re{re_code}_si{sample_interval}" + os.makedirs(output_dir, exist_ok=True) + + coefs = load_coefs(V2_RESULTS) + cuda_cfg, field_cfg = load_legacy_configs(CONFIG_DIR) + field_cfg = field_cfg._replace(viscosity=float(nu_from_re(re_code, u0=U0))) + ff = FlowField(field_cfg, cuda_cfg, device_id=device_id) + + target_states, _ = build_karman_cloak_env(ff, u0=U0, l0=20.0, + sample_interval=sample_interval, fifo_len=FIFO_LEN, data_type=DATA_TYPE) + norm = add_pinball(ff, l0=20.0, u0=U0, sample_interval=sample_interval, + fifo_len=FIFO_LEN, data_type=DATA_TYPE, action_bias=ACTION_BIAS) + + ff.restore_ddf(); + ff.apply_ddf() + fifo = deque(maxlen=FIFO_LEN) + bias_action = scale_action(np.zeros(3, dtype=np.float32), scale=ACTION_SCALE, + bias=ACTION_BIAS, u0=U0, n_total_bodies=7) + for _ in range(FIFO_LEN): + ff.run(sample_interval, bias_action) + fifo.append(ff.obs.copy()[2:14]) + + sens_sc = [] + a_prev = action_to_physical(np.zeros((1,3), dtype=np.float32), + scale=ACTION_SCALE, bias=ACTION_BIAS, u0=U0).flatten() + a_prev2 = a_prev.copy() + n_steps = 100 + + for _ in range(n_steps): + obs = fifo[-1] if len(fifo) > 0 else np.zeros(12, dtype=np.float32) + omega = predict(obs, a_prev, a_prev2, coefs, mu) + norm_a = (omega / U0 - np.array(ACTION_BIAS, dtype=np.float64)) / ACTION_SCALE + norm_a = np.clip(norm_a, -1.0, 1.0).astype(np.float32) + action_arr = scale_action(norm_a, scale=ACTION_SCALE, bias=ACTION_BIAS, u0=U0, n_total_bodies=7) + ff.run(sample_interval, action_arr) + obs_new = ff.obs.copy()[2:14] + fifo.append(obs_new) + sens_sc.append(obs_new[0:6]) + a_prev2 = a_prev.copy() + a_prev = omega.copy() + + sens_arr = np.array(sens_sc, dtype=np.float32) + sim = compute_similarity(target_states, sens_arr, CONV_LEN) + print(f" {label}: similarity={sim:.4f}") + + del ff + return sim + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("--device", type=int, default=2) + args = ap.parse_args() + + out_dir = os.path.join(_PROJ, "src", "analysis_cloak", "scenes", "karman", "closed_loop") + os.makedirs(out_dir, exist_ok=True) + + intervals = [800, 400, 200] + results = {} + for si in intervals: + sim = run(RE_CODE, si, args.device, out_dir) + results[f"si{si}"] = sim + + print(f"\nRe200 frequency test results:") + for si, sim in results.items(): + print(f" SAMPLE_INTERVAL={si}: similarity={sim:.4f}") + + with open(os.path.join(out_dir, "re200_freq_test.json"), "w") as f: + json.dump(results, f, indent=2) + + +if __name__ == "__main__": + main() diff --git a/src/analysis_cloak/scenes/karman/sindy/karman_sindy.json b/src/analysis_cloak/scenes/karman/sindy/karman_sindy.json new file mode 100644 index 0000000..6c163a8 --- /dev/null +++ b/src/analysis_cloak/scenes/karman/sindy/karman_sindy.json @@ -0,0 +1,508 @@ +{ + "scene": "karman", + "re_codes": "default", + "n_samples_front": 444, + "n_features_front": 21, + "n_features_rear": 22, + "feature_names_front": [ + "u_m", + "u_a", + "u_c", + "v_a", + "Cd_tot", + "Cd_rear", + "Cl_tot", + "Cl_diff", + "sin_ua", + "cos_ua", + "aF_lag1", + "aB_lag1", + "aT_lag1", + "daF", + "daB", + "daT", + "mu", + "mu_u_a", + "mu_v_a", + "mu_Cd_tot", + "mu_Cl_diff" + ], + "feature_names_rear": [ + "bias", + "u_m", + "u_a", + "u_c", + "v_a", + "Cd_tot", + "Cd_rear", + "Cl_tot", + "Cl_diff", + "sin_ua", + "cos_ua", + "aF_lag1", + "aB_lag1", + "aT_lag1", + "daF", + "daB", + "daT", + "mu", + "mu_u_a", + "mu_v_a", + "mu_Cd_tot", + "mu_Cl_diff" + ], + "front": { + "results": [ + { + "threshold": 0.0, + "nz": 21, + "r2": 0.9654966682363757, + "mae": 0.0023056666760656796 + }, + { + "threshold": 0.001, + "nz": 5, + "r2": 0.9536016197206115, + "mae": 0.002601817745080991 + }, + { + "threshold": 0.002, + "nz": 2, + "r2": 0.9452383376955145, + "mae": 0.00274020474447065 + }, + { + "threshold": 0.005, + "nz": 2, + "r2": 0.9452383376955145, + "mae": 0.00274020474447065 + }, + { + "threshold": 0.01, + "nz": 1, + "r2": 0.860607207574008, + "mae": 0.005171636101202507 + }, + { + "threshold": 0.015, + "nz": 0, + "r2": -0.025600612306448944, + "mae": 0.01601045471490235 + }, + { + "threshold": 0.02, + "nz": 0, + "r2": -0.025600612306448944, + "mae": 0.01601045471490235 + }, + { + "threshold": 0.03, + "nz": 0, + "r2": -0.025600612306448944, + "mae": 0.01601045471490235 + }, + { + "threshold": 0.05, + "nz": 0, + "r2": -0.025600612306448944, + "mae": 0.01601045471490235 + }, + { + "threshold": 0.1, + "nz": 0, + "r2": -0.025600612306448944, + "mae": 0.01601045471490235 + } + ], + "best": { + "threshold": 0.0, + "nz": 21, + "r2": 0.9654966682363757, + "mae": 0.0023056666760656796 + }, + "best_coef": [ + -5.192955717959975e-05, + 2.8758845410456916e-05, + -1.1774957671598127e-05, + -3.25172225327354e-05, + 0.0015201286832842583, + 0.0003656613747736437, + -0.0007681231453227206, + 0.0003090074931666537, + 1.2582646790399684e-07, + 0.0002743780546501911, + 0.007096991322989564, + -0.001425750496064129, + 0.0006433749857293151, + 0.008791801878378852, + 0.0028160283947971055, + -0.0005095282162885374, + -0.16160246819614302, + 0.0007252560276568708, + -0.002241307134772526, + -0.021184890828600162, + -0.021767971799948535 + ], + "sparsity_curve": [ + [ + 0.0, + 21, + 0.9654966682363757 + ], + [ + 0.001, + 5, + 0.9536016197206115 + ], + [ + 0.002, + 2, + 0.9452383376955145 + ], + [ + 0.005, + 2, + 0.9452383376955145 + ], + [ + 0.01, + 1, + 0.860607207574008 + ], + [ + 0.015, + 0, + -0.025600612306448944 + ], + [ + 0.02, + 0, + -0.025600612306448944 + ], + [ + 0.03, + 0, + -0.025600612306448944 + ], + [ + 0.05, + 0, + -0.025600612306448944 + ], + [ + 0.1, + 0, + -0.025600612306448944 + ] + ] + }, + "top": { + "results": [ + { + "threshold": 0.0, + "nz": 22, + "r2": 0.862596324781397, + "mae": 0.00633210084008296 + }, + { + "threshold": 0.001, + "nz": 19, + "r2": 0.8613618595140851, + "mae": 0.006368110213095571 + }, + { + "threshold": 0.002, + "nz": 14, + "r2": 0.8493750057676241, + "mae": 0.006730015193486602 + }, + { + "threshold": 0.005, + "nz": 7, + "r2": 0.7827214013687178, + "mae": 0.00802796108799867 + }, + { + "threshold": 0.01, + "nz": 1, + "r2": 0.6121198810123357, + "mae": 0.009530874364852419 + }, + { + "threshold": 0.015, + "nz": 1, + "r2": 0.6121198810123357, + "mae": 0.009530874364852419 + }, + { + "threshold": 0.02, + "nz": 1, + "r2": 3.654743174763553e-12, + "mae": 0.0173810294687437 + }, + { + "threshold": 0.03, + "nz": 1, + "r2": 3.654743174763553e-12, + "mae": 0.0173810294687437 + }, + { + "threshold": 0.05, + "nz": 0, + "r2": -2.2359943125676613, + "mae": 0.039575029794999335 + }, + { + "threshold": 0.1, + "nz": 0, + "r2": -2.2359943125676613, + "mae": 0.039575029794999335 + } + ], + "best": { + "threshold": 0.0, + "nz": 22, + "r2": 0.862596324781397, + "mae": 0.00633210084008296 + }, + "best_coef": [ + -0.05500806404528806, + 0.0007420012135347702, + -0.0005289839138368902, + -7.059719059238487e-05, + 0.0006778963140888031, + -0.001096838965444243, + -0.0006586342747236175, + 0.0034452827223076256, + -0.0015323585728228434, + -0.0006424234277769932, + -0.0007755764497972763, + 0.005024202346778029, + 0.0034193717761402237, + 0.00640250610361064, + -0.005852277491977138, + -0.003193207096374315, + 0.0032222577962603585, + -0.35037148676340113, + 0.023157523040313266, + -0.04322885440841332, + -0.050453986820807865, + -0.051262980383116864 + ], + "sparsity_curve": [ + [ + 0.0, + 22, + 0.862596324781397 + ], + [ + 0.001, + 19, + 0.8613618595140851 + ], + [ + 0.002, + 14, + 0.8493750057676241 + ], + [ + 0.005, + 7, + 0.7827214013687178 + ], + [ + 0.01, + 1, + 0.6121198810123357 + ], + [ + 0.015, + 1, + 0.6121198810123357 + ], + [ + 0.02, + 1, + 3.654743174763553e-12 + ], + [ + 0.03, + 1, + 3.654743174763553e-12 + ], + [ + 0.05, + 0, + -2.2359943125676613 + ], + [ + 0.1, + 0, + -2.2359943125676613 + ] + ] + }, + "bottom": { + "results": [ + { + "threshold": 0.0, + "nz": 22, + "r2": 0.8993440589380792, + "mae": 0.004870578129840853 + }, + { + "threshold": 0.001, + "nz": 10, + "r2": 0.8959720170147872, + "mae": 0.004940764682366889 + }, + { + "threshold": 0.002, + "nz": 8, + "r2": 0.8924474832818312, + "mae": 0.004917054661924924 + }, + { + "threshold": 0.005, + "nz": 5, + "r2": 0.8580026242147389, + "mae": 0.0059864202818160055 + }, + { + "threshold": 0.01, + "nz": 1, + "r2": 0.7104836293852221, + "mae": 0.008387153194050426 + }, + { + "threshold": 0.015, + "nz": 1, + "r2": 0.7104836293852221, + "mae": 0.008387153194050426 + }, + { + "threshold": 0.02, + "nz": 1, + "r2": 0.7104836293852221, + "mae": 0.008387153194050426 + }, + { + "threshold": 0.03, + "nz": 0, + "r2": -2.8272225894084078, + "mae": 0.038829327024821496 + }, + { + "threshold": 0.05, + "nz": 0, + "r2": -2.8272225894084078, + "mae": 0.038829327024821496 + }, + { + "threshold": 0.1, + "nz": 0, + "r2": -2.8272225894084078, + "mae": 0.038829327024821496 + } + ], + "best": { + "threshold": 0.0, + "nz": 22, + "r2": 0.8993440589380792, + "mae": 0.004870578129840853 + }, + "best_coef": [ + -0.010075042034336805, + 0.000127456261090308, + -0.00018640871260677575, + -1.0786662376718973e-05, + 0.00013209953181097446, + -0.0029808562650467663, + -0.0003882575275073206, + 0.0021351638754212236, + -0.0005317242032502552, + -1.4526187037145631e-05, + -0.0004792609214251783, + 0.0035849480411721288, + 0.009840224256264653, + -0.0008193716182580219, + -0.005477208879386436, + 0.00248443011612523, + -0.001596550946452102, + 0.10495659563156284, + 0.0036966535058393694, + -0.006256407760146263, + 0.05741274816660643, + 0.028241944772701748 + ], + "sparsity_curve": [ + [ + 0.0, + 22, + 0.8993440589380792 + ], + [ + 0.001, + 10, + 0.8959720170147872 + ], + [ + 0.002, + 8, + 0.8924474832818312 + ], + [ + 0.005, + 5, + 0.8580026242147389 + ], + [ + 0.01, + 1, + 0.7104836293852221 + ], + [ + 0.015, + 1, + 0.7104836293852221 + ], + [ + 0.02, + 1, + 0.7104836293852221 + ], + [ + 0.03, + 0, + -2.8272225894084078 + ], + [ + 0.05, + 0, + -2.8272225894084078 + ], + [ + 0.1, + 0, + -2.8272225894084078 + ] + ] + }, + "scene_info": [ + { + "re_code": 50, + "mu": 0.04, + "n_samples": 148 + }, + { + "re_code": 100, + "mu": 0.02, + "n_samples": 148 + }, + { + "re_code": 200, + "mu": 0.01, + "n_samples": 148 + } + ] +} \ No newline at end of file diff --git a/src/analysis_cloak/scenes/karman/sr/karman_sr_pareto.json b/src/analysis_cloak/scenes/karman/sr/karman_sr_pareto.json new file mode 100644 index 0000000..c283263 --- /dev/null +++ b/src/analysis_cloak/scenes/karman/sr/karman_sr_pareto.json @@ -0,0 +1,94 @@ +{ + "scene": "karman", + "channels": [ + { + "channel": "front", + "best_r2": 0.9654966682363757, + "best_nz": 21, + "pareto": [ + { + "nz": 0, + "r2": -0.025600612306448944 + }, + { + "nz": 1, + "r2": 0.860607207574008 + }, + { + "nz": 2, + "r2": 0.9452383376955145 + }, + { + "nz": 5, + "r2": 0.9536016197206115 + }, + { + "nz": 21, + "r2": 0.9654966682363757 + } + ] + }, + { + "channel": "top", + "best_r2": 0.862596324781397, + "best_nz": 22, + "pareto": [ + { + "nz": 0, + "r2": -2.2359943125676613 + }, + { + "nz": 1, + "r2": 0.6121198810123357 + }, + { + "nz": 7, + "r2": 0.7827214013687178 + }, + { + "nz": 14, + "r2": 0.8493750057676241 + }, + { + "nz": 19, + "r2": 0.8613618595140851 + }, + { + "nz": 22, + "r2": 0.862596324781397 + } + ] + }, + { + "channel": "bottom", + "best_r2": 0.8993440589380792, + "best_nz": 22, + "pareto": [ + { + "nz": 0, + "r2": -2.8272225894084078 + }, + { + "nz": 1, + "r2": 0.7104836293852221 + }, + { + "nz": 5, + "r2": 0.8580026242147389 + }, + { + "nz": 8, + "r2": 0.8924474832818312 + }, + { + "nz": 10, + "r2": 0.8959720170147872 + }, + { + "nz": 22, + "r2": 0.8993440589380792 + } + ] + } + ] +} \ No newline at end of file diff --git a/src/analysis_cloak/scenes/steady/closed_loop/gen_steady_data.py b/src/analysis_cloak/scenes/steady/closed_loop/gen_steady_data.py new file mode 100644 index 0000000..621ae70 --- /dev/null +++ b/src/analysis_cloak/scenes/steady/closed_loop/gen_steady_data.py @@ -0,0 +1,170 @@ +"""Generate steady cloak data. + +Steady cloak uses open-loop constant rotation (no PPO model). +The environment has pinball cylinders but no disturbance cylinder. +Target is clean parabolic inflow (constant sensor values). + +Usage: + conda run -n pycuda_3_10 python gen_steady_data.py --device 2 +""" +import argparse +import os +import sys +from collections import deque + +import numpy as np + +_PROJ = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..", "..", "..")) +if _PROJ not in sys.path: + sys.path.insert(0, _PROJ) +from LegacyCelerisLab import FlowField +from LegacyCelerisLab import utils as legacy_utils + +# Point to analysis_crossre configs +CONFIG_DIR = os.path.join(_PROJ, "src", "analysis_crossre", "configs") +OUTPUT_DIR = os.path.join(_PROJ, "src", "analysis_cloak", "scenes", "steady", "closed_loop") + +U0 = 0.01 +L0 = 20 +SAMPLE_INTERVAL = 800 +FIFO_LEN = 150 +DATA_TYPE = np.float32 + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("--device", type=int, default=2) + ap.add_argument("--action-top", type=float, default=5.1, help="Top cylinder omega factor") + ap.add_argument("--action-bottom", type=float, default=-5.1, help="Bottom cylinder omega factor") + ap.add_argument("--n-steps", type=int, default=200) + args = ap.parse_args() + + os.makedirs(OUTPUT_DIR, exist_ok=True) + + cuda_cfg = legacy_utils.load_cuda_config(os.path.join(CONFIG_DIR, "config_cuda.json")) + field_cfg = legacy_utils.load_flow_field_config(os.path.join(CONFIG_DIR, "config_flowfield.json")) + + ff = FlowField(field_cfg, cuda_cfg, device_id=args.device) + NY = ff.FIELD_SHAPE[1] + NX = ff.FIELD_SHAPE[0] + + # Step 1: Add only pinball cylinders (NO disturbance cylinder) + # Front cylinder + ff.add_cylinder((30 * L0, (NY - 1) / 2, 0), L0 / 2) + # Bottom cylinder + ff.add_cylinder((31.3 * L0, (NY - 1) / 2 - 0.75 * L0, 0), L0 / 2) + # Top cylinder + ff.add_cylinder((31.3 * L0, (NY - 1) / 2 + 0.75 * L0, 0), L0 / 2) + # 3 sensors at x=40*L0 + ff.add_sensor((40 * L0, (NY - 1) / 2 + 2 * L0, 0), L0 / 4) + ff.add_sensor((40 * L0, (NY - 1) / 2, 0), L0 / 4) + ff.add_sensor((40 * L0, (NY - 1) / 2 - 2 * L0, 0), L0 / 4) + + n_bodies = ff.obs.size // 2 + print(f"Bodies: {n_bodies} (3 pinball + 3 sensors)") + + # Step 2: Stabilize with zero action + stabilize_steps = int(4 * NX / U0) + print(f"Stabilising ({stabilize_steps} steps)...") + ff.run(stabilize_steps, np.zeros(n_bodies, dtype=DATA_TYPE)) + ff.get_ddf() + ff.save_ddf() + + # Step 3: Record target (steady inflow, no pinball) - we already have clean inflow as target + # For steady cloak, target is just the mean of clean parabolic inflow + # But since we built env WITH pinball, we need target separately + # Actually for steady cloak, target is just the inlet profile at sensor positions + # Let's compute it: run with all bodies but minimal disturbance + # The "target" is simply the flow that would exist without pinball + # Since this is a parabolic channel, the sensors would read the undisturbed parabolic profile + + # For simplicity, record the initial steady-state sensor values as target + ff.restore_ddf() + ff.apply_ddf() + target_list = [] + for i in range(FIFO_LEN): + ff.run(SAMPLE_INTERVAL, np.zeros(n_bodies, dtype=DATA_TYPE)) + obs_slice = ff.obs.copy() + # obs for 6 bodies: [sensor0(2), sensor1(2), sensor2(2), cyl0(2), cyl1(2), cyl2(2)] + # We only want sensor values: indices 0..5 + target_list.append(obs_slice[0:6].copy()) + + target_states = np.array(target_list, dtype=np.float32) + target_mean = np.mean(target_states, axis=0) + print(f"Target mean (steady inflow at sensors): {target_mean}") + + # Step 4: Run with constant rotation + ff.restore_ddf() + ff.apply_ddf() + + constant_action = np.zeros(n_bodies, dtype=DATA_TYPE) + constant_action[n_bodies - 3] = 0.0 * U0 # front + constant_action[n_bodies - 2] = float(args.action_bottom * U0) # bottom + constant_action[n_bodies - 1] = float(args.action_top * U0) # top + print(f"Constant action: {constant_action}") + + # Stabilize to the controlled state + print("Running controlled steady state...") + for i in range(FIFO_LEN): + ff.run(SAMPLE_INTERVAL, constant_action) + + # Record controlled data + sensors_list = [] + forces_list = [] + actions_list = [] + + for step in range(args.n_steps): + ff.run(SAMPLE_INTERVAL, constant_action) + obs_slice = ff.obs.copy() + sensors_list.append(obs_slice[0:6].copy()) + forces_list.append(obs_slice[6:12].copy()) # pinball forces are at indices 6-11 + actions_list.append(constant_action[n_bodies - 3:n_bodies].copy()) + + sensors_arr = np.array(sensors_list, dtype=np.float32) + forces_arr = np.array(forces_list, dtype=np.float32) + actions_arr = np.array(actions_list, dtype=np.float32) + + print(f"Sensors shape: {sensors_arr.shape}") + print(f"Forces shape: {forces_arr.shape}") + print(f"Sensor mean (controlled): {np.mean(sensors_arr, axis=0)}") + print(f"Force mean (controlled): {np.mean(forces_arr, axis=0)}") + + # Save + out_path = os.path.join(OUTPUT_DIR, "steady_data.npz") + np.savez(out_path, sensors=sensors_arr, forces=forces_arr, + actions=actions_arr, target_states=target_states, + action_constant=constant_action) + print(f"Saved: {out_path}") + + # Also save vorticity of final state + ff.get_ddf() + ddf = ff.ddf.copy().reshape((9, NY, NX)).transpose(2, 1, 0) + ux = (ddf[:, :, 1] + ddf[:, :, 5] + ddf[:, :, 8] - ddf[:, :, 3] - ddf[:, :, 6] - ddf[:, :, 7]) / U0 + uy = (ddf[:, :, 2] + ddf[:, :, 5] + ddf[:, :, 6] - ddf[:, :, 4] - ddf[:, :, 7] - ddf[:, :, 8]) / U0 + omega = np.gradient(uy, axis=1) - np.gradient(ux, axis=0) + + try: + import matplotlib + matplotlib.use("Agg") + import matplotlib.pyplot as plt + abs_o = np.abs(omega[np.isfinite(omega)]) + vmax = float(np.percentile(abs_o, 99.5)) if abs_o.size > 0 else 1.0 + if vmax <= 0: + vmax = 1.0 + fig, ax = plt.subplots(figsize=(12, 5)) + im = ax.imshow(omega, origin="lower", aspect="equal", cmap="RdBu_r", + vmin=-vmax, vmax=vmax, extent=(0, NX - 1, 0, NY - 1)) + ax.set_title(f"Steady cloak: action=[0, {args.action_bottom}, {args.action_top}]*U0") + fig.colorbar(im, ax=ax, fraction=0.046, pad=0.04) + fig.savefig(os.path.join(OUTPUT_DIR, "vorticity_steady.png"), dpi=150, bbox_inches="tight") + plt.close(fig) + print(f"Vorticity saved to {OUTPUT_DIR}/vorticity_steady.png") + except ImportError: + print("matplotlib not available, skipping vorticity PNG") + + del ff + print("Done") + + +if __name__ == "__main__": + main() diff --git a/src/analysis_cloak/scenes/steady/closed_loop/vorticity_steady.png b/src/analysis_cloak/scenes/steady/closed_loop/vorticity_steady.png new file mode 100644 index 0000000000000000000000000000000000000000..0f95b80278d14347b6bad4f51118b8fec627b252 GIT binary patch literal 163100 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

mVK6X#|R9plAe&t!99dg41gOO<&RU6(<`WzLcD7 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%0AB)wi1gTBhQOZ~ySu$U*NblbC8 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 Re_D=50 (default case) +def nu_from_re(re_code: float) -> float: + """Calculate kinematic viscosity from code Reynolds number.""" + return U0 * D_REF / re_code + +# Re cases: (re_code, model_name) -- None for validation cases with no model +RE_CASES_TRAIN: List[Tuple[int, str]] = [ + (50, "d1a3o12_re50"), + (100, "d1a3o12_re100"), + (200, "d1a3o12_re200"), + (400, "d1a3o12_re400"), +] + +RE_CASES_VALIDATION: List[int] = [35, 70, 150] +ALL_RE_CODES: List[int] = [c[0] for c in RE_CASES_TRAIN] + RE_CASES_VALIDATION + +RE_LABEL_MAP: Dict[int, str] = { + 50: "Re50 (Re_D=25)", + 100: "Re100 (Re_D=50)", + 200: "Re200 (Re_D=100)", + 400: "Re400 (Re_D=200)", + 35: "Re35 (Re_D=17.5)", + 70: "Re70 (Re_D=35)", + 150: "Re150 (Re_D=75)", +} diff --git a/src/analysis_crossre/scripts/phase1_infer.py b/src/analysis_crossre/scripts/phase1_infer.py new file mode 100644 index 0000000..6fdde3b --- /dev/null +++ b/src/analysis_crossre/scripts/phase1_infer.py @@ -0,0 +1,329 @@ +# analysis_crossre/scripts/phase1_infer.py +"""Phase 1: verify legacy-CFD + PPO inference across Karman cloak Re cases. + +Usage:: + + conda run -n pycuda_3_10 python phase1_infer.py \\ + --re 100 --device 0 + + conda run -n pycuda_3_10 python phase1_infer.py \\ + --re all --device 2 + +Output for each Re case under ``output/analysis_crossre/re{re_code}/``: + +- ``config.json`` — runtime parameters +- ``norm.json`` — normalisation factors +- ``target.npz`` — target sensor signal (disturbance only, no pinball) +- ``uncontrolled.npz`` — sensor / force / obs time series (zero-action) +- ``controlled.npz`` — sensor / force / obs / action time series (PPO) +- ``vorticity_uncontrolled.png`` — final-step vorticity, uncontrolled +- ``vorticity_controlled.png`` — final-step vorticity, controlled +- ``rewards.npz`` — step-by-step reward for controlled rollout +""" +from __future__ import annotations + +import argparse +import json +import os +import sys +import time +from collections import deque + +import numpy as np + +# Add workspace root so LegacyCelerisLab is importable +_PROJ = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..")) +if _PROJ not in sys.path: + sys.path.insert(0, _PROJ) + +from LegacyCelerisLab import FlowField # noqa: E402 + +from utils import ( # noqa: E402 + nu_from_re, + load_legacy_configs, + build_karman_cloak_env, + add_pinball, + build_observation, + scale_action, + load_ppo_model, + save_vorticity_png, + vorticity_from_ddf, + compute_similarity, + ACTION_SMOOTH_WEIGHT, +) +from cfg import ( # noqa: E402 + CONFIG_DIR, + CONFIG_CUDA, + CONFIG_FLOWFIELD_BASE, + OUTPUT_DIR, + MODEL_DIR, + SAMPLE_INTERVAL, + FIFO_LEN, + CONV_LEN, + S_DIM, + A_DIM, + ACTION_SCALE, + ACTION_BIAS, + U0, + STABILIZE_STEPS, + RE_CASES_TRAIN, + RE_CASES_VALIDATION, + RE_LABEL_MAP, +) + +DATA_TYPE = np.float32 + + +def run_single_re( + re_code: int, + model_path: Optional[str], + device_id: int, + output_root: str, + *, + n_infer_steps: int = 200, +) -> dict: + """Run full inference pipeline for one Re case. + + Parameters + ---------- + re_code : int + Code Reynolds number (reference length = 2D). + model_path : str or None + Path to PPO .zip file. None = only uncontrolled runs. + device_id : int + GPU device ID. + output_root : str + Root output directory for this case. + n_infer_steps : int + Number of inference steps (each = SAMPLE_INTERVAL LBM steps). + + Returns summary dict with similarity scores etc. + """ + os.makedirs(output_root, exist_ok=True) + + # -- 0. Prepare config --------------------------------------------------- + nu = nu_from_re(re_code, u0=U0) + label = RE_LABEL_MAP.get(re_code, f"Re{re_code}") + print(f"\n{'='*60}") + print(f"Case: {label} viscosity={nu:.6f} device={device_id}") + print(f"{'='*60}") + + # Save run config + with open(os.path.join(output_root, "config.json"), "w") as f: + json.dump({ + "re_code": re_code, + "nu": nu, + "u0": U0, + "sample_interval": SAMPLE_INTERVAL, + "fifo_len": FIFO_LEN, + "conv_len": CONV_LEN, + "device_id": device_id, + "model_path": model_path, + }, f, indent=2) + + # -- 1. Load legacy CFD configs, adjust viscosity ------------------------ + cuda_cfg, field_cfg = load_legacy_configs(CONFIG_DIR) + # Override viscosity for this Re + field_cfg = field_cfg._replace(viscosity=float(nu)) + + # -- 2. Build flow field + dist + sensors, record target ----------------- + ff = FlowField(field_cfg, cuda_cfg, device_id=device_id) + target_states, env_info = build_karman_cloak_env( + ff, u0=U0, l0=20.0, sample_interval=SAMPLE_INTERVAL, + fifo_len=FIFO_LEN, data_type=DATA_TYPE, + ) + np.savez(os.path.join(output_root, "target.npz"), + target_states=target_states) + + # -- 3. Add pinball, compute norm, bias rollout -------------------------- + norm = add_pinball( + ff, l0=20.0, u0=U0, sample_interval=SAMPLE_INTERVAL, + fifo_len=FIFO_LEN, data_type=DATA_TYPE, + action_bias=ACTION_BIAS, + ) + save_states = norm.pop("save_states", None) + + # Save norm (without save_states which is a big array) + norm_for_json = {k: v for k, v in norm.items() if not isinstance(v, np.ndarray)} + with open(os.path.join(output_root, "norm.json"), "w") as f: + json.dump(norm_for_json, f, indent=2) + print(f" norm saved to {output_root}/norm.json") + + # -- 4. Uncontrolled rollout --------------------------------------------- + print(" uncontrolled rollout ...") + ff.restore_ddf() + ff.apply_ddf() + fifo = deque(maxlen=FIFO_LEN) + sens_list, forc_list, obs_list = [], [], [] + + for step in range(n_infer_steps): + ff.run(SAMPLE_INTERVAL, np.zeros(7, dtype=DATA_TYPE)) + obs_slice = ff.obs.copy()[2:14] + fifo.append(obs_slice) + sens_list.append(obs_slice[0:6]) + forc_list.append(obs_slice[6:12]) + obs = build_observation(obs_slice, norm) + obs_list.append(obs) + + np.savez(os.path.join(output_root, "uncontrolled.npz"), + sensors=np.array(sens_list, dtype=np.float32), + forces=np.array(forc_list, dtype=np.float32), + obs=np.array(obs_list, dtype=np.float32)) + + # Vorticity at final step + omega_unc = vorticity_from_ddf(ff, u0=U0) + save_vorticity_png( + os.path.join(output_root, "vorticity_uncontrolled.png"), + omega_unc, + title=f"{label} uncontrolled", + ) + # Also save macro field snapshot + macro_unc = {"ux": [], "uy": [], "rho": []} # placeholder; vorticity PNG is enough for Phase 1 + + # -- 5. Controlled rollout (if model available) -------------------------- + result = {"re_code": re_code, "uncontrolled": True, "controlled": False} + + if model_path is not None and os.path.isfile(model_path): + print(f" loading model: {model_path}") + model = load_ppo_model(model_path, device=f"cuda:{device_id}") + model.set_random_seed(0) + + print(f" controlled rollout ({n_infer_steps} steps) ...") + ff.restore_ddf() + ff.apply_ddf() + # Re-bias the FIFO (as in env.__init__) + bias_action = scale_action( + np.array([0.0, 0.0, 0.0], dtype=np.float32), + scale=ACTION_SCALE, bias=ACTION_BIAS, u0=U0, n_total_bodies=7, + ) + for i in range(FIFO_LEN): + ff.context.push() + try: + ff.run(SAMPLE_INTERVAL, bias_action) + finally: + ff.context.pop() + fifo.append(ff.obs.copy()[2:14]) + + sens_list_c, forc_list_c, obs_list_c = [], [], [] + action_list_c, reward_list_c = [], [] + + obs = np.zeros(S_DIM, dtype=np.float32) + + for step in range(n_infer_steps): + action, _states = model.predict(obs, deterministic=True) + action = action.astype(np.float32).flatten() + action_list_c.append(action.copy()) + + # Convert to legacy action array + action_arr = scale_action( + action, scale=ACTION_SCALE, bias=ACTION_BIAS, + u0=U0, n_total_bodies=7, + ) + + # Run CFD with proper context management (PyTorch shadows legacy ctx) + ff.context.push() + try: + ff.run(SAMPLE_INTERVAL, action_arr) + finally: + ff.context.pop() + + obs_slice = ff.obs.copy()[2:14] + fifo.append(obs_slice) + sens_list_c.append(obs_slice[0:6]) + forc_list_c.append(obs_slice[6:12]) + obs = build_observation(obs_slice, norm) + obs_list_c.append(obs) + + # Compute reward (matching env logic) + states_arr = np.array(list(fifo), dtype=np.float32) + if len(states_arr) >= CONV_LEN: + forces = states_arr[-1, 6:12] / norm["force_norm_fact"] + cd = float((forces[0] + forces[2] + forces[4]) / 3.0) + cl = float((forces[1] + forces[3] + forces[5]) / 3.0) + sim = compute_similarity(target_states, states_arr[:, 0:6], CONV_LEN) + r_cd = np.exp(-abs(cd * 20.0)) + r_cl = np.exp(-abs(cl * 80.0)) + r_sim = np.exp(-10.0 * abs(sim - 1.0)) + reward = min(0.3 * r_cd + 0.4 * r_cl + 0.3 * r_sim, 1.0) + reward_list_c.append(float(reward)) + else: + reward_list_c.append(0.0) + + np.savez(os.path.join(output_root, "controlled.npz"), + sensors=np.array(sens_list_c, dtype=np.float32), + forces=np.array(forc_list_c, dtype=np.float32), + obs=np.array(obs_list_c, dtype=np.float32), + actions=np.array(action_list_c, dtype=np.float32), + rewards=np.array(reward_list_c, dtype=np.float32)) + + # Vorticity at final controlled step + omega_con = vorticity_from_ddf(ff, u0=U0) + save_vorticity_png( + os.path.join(output_root, "vorticity_controlled.png"), + omega_con, + title=f"{label} controlled (PPO)", + ) + + avg_reward = float(np.mean(reward_list_c[-100:])) if len(reward_list_c) >= 100 else float(np.mean(reward_list_c)) + sim_score = compute_similarity( + target_states, np.array(sens_list_c, dtype=np.float32), CONV_LEN + ) + result["controlled"] = True + result["avg_reward_last100"] = avg_reward + result["similarity"] = sim_score + print(f" avg_reward(last100)={avg_reward:.4f} similarity={sim_score:.4f}") + else: + print(f" no model for Re{re_code}, skipping controlled rollout") + + # -- 6. Cleanup ---------------------------------------------------------- + del ff + + # Save result summary + with open(os.path.join(output_root, "result.json"), "w") as f: + json.dump(result, f, indent=2) + + return result + + +def main(): + ap = argparse.ArgumentParser(description="Phase 1: cross-Re Karman cloak inference") + ap.add_argument("--re", type=str, default="100", + help='Re case: 50,100,200,400, or "all", or "validation"') + ap.add_argument("--device", type=int, default=0, help="GPU device ID") + ap.add_argument("--steps", type=int, default=200, + help="Number of inference steps per rollout") + args = ap.parse_args() + + # Select Re list + selection = args.re.lower() + if selection == "all": + re_list = [c[0] for c in RE_CASES_TRAIN] + elif selection == "validation": + re_list = RE_CASES_VALIDATION + else: + re_list = [int(selection)] + + t_start = time.time() + + for re_code in re_list: + # Find model path + model_path = None + for rc, mn in RE_CASES_TRAIN: + if rc == re_code: + model_path = os.path.join(MODEL_DIR, "old", f"{mn}.zip") + break + + out_dir = os.path.join(OUTPUT_DIR, f"re{re_code}") + result = run_single_re( + re_code, model_path, args.device, out_dir, + n_infer_steps=args.steps, + ) + print(f" Done: re{re_code} -> {out_dir}") + + elapsed = time.time() - t_start + print(f"\nTotal time: {elapsed:.1f}s") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/src/analysis_crossre/scripts/utils.py b/src/analysis_crossre/scripts/utils.py new file mode 100644 index 0000000..0bfdd11 --- /dev/null +++ b/src/analysis_crossre/scripts/utils.py @@ -0,0 +1,851 @@ +# analysis_crossre/scripts/utils.py +"""Shared utilities for the cross-Re Karman cloak analysis. + +All functions use the LegacyCelerisLab (old) CFD API via ``from LegacyCelerisLab import FlowField``. +Must be run inside ``conda run -n pycuda_3_10``. +""" +from __future__ import annotations + +import json +import os +import sys +from collections import deque +from typing import Any, Dict, List, Optional, Tuple + +import numpy as np + +# -- Import legacy CFD ------------------------------------------------------- +# LegacyCelerisLab lives at the repo root; analysis scripts are at +# src/analysis_crossre/scripts/. We need repo root on sys.path. +_REPO = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..")) +if _REPO not in sys.path: + sys.path.insert(0, _REPO) + +from LegacyCelerisLab import FlowField # noqa: E402 +from LegacyCelerisLab import utils as legacy_utils # noqa: E402 + +# --------------------------------------------------------------------------- +# Action-smoothing constant (legacy run() internal) +# --------------------------------------------------------------------------- +ACTION_SMOOTH_WEIGHT = 0.1 # used by FlowField.run() internally + + +def nu_from_re(re_code: float, u0: float = 0.01, d_ref: float = 40.0) -> float: + """Return kinematic viscosity for a given code Reynolds number. + + ``re_code`` uses reference length *2*D* = 40.0 (matching model file naming). + """ + return u0 * d_ref / re_code + + +def load_legacy_configs(config_dir: str) -> Tuple[Any, Any]: + """Load and return legacy (cuda_config, field_config) from *config_dir*.""" + cuda_cfg = legacy_utils.load_cuda_config( + os.path.join(config_dir, "config_cuda.json") + ) + field_cfg = legacy_utils.load_flow_field_config( + os.path.join(config_dir, "config_flowfield.json") + ) + return cuda_cfg, field_cfg + + +# --------------------------------------------------------------------------- +# Environment helpers – follow env_karman_cloak_standard.py exactly +# --------------------------------------------------------------------------- + +def build_karman_cloak_env( + flow_field: FlowField, + *, + u0: float, + l0: float, + sample_interval: int, + fifo_len: int, + data_type: type, +) -> Tuple[np.ndarray, dict]: + """Phase 0-1: add dist-cylinder & 3 sensors, stabilize, record target. + + Steps (mirrors env.__init__ lines 64-86): + + 1. add dist_cylinder (id=0) + 2. add 3 sensors (id=1,2,3) + 3. stabilize run(4*NX/U0, zero-action[4]) + 4. record FIFO_LEN × run(SAMPLE_INTERVAL, zero[4]), collect obs[2:8] + + Returns + ------- + target_states : ndarray (FIFO_LEN, 6) — sensor0/1/2 ux,uy + info : dict with n_objects, NX, NY + """ + n_objects_before = flow_field.flag.size # not used, just a marker + + # dist cylinder + center = (10.0 * l0, (flow_field.FIELD_SHAPE[1] - 1) / 2, 0.0) + flow_field.add_cylinder(center, l0) + + # sensors + for y_off in [2.0, 0.0, -2.0]: + sc = (40.0 * l0, (flow_field.FIELD_SHAPE[1] - 1) / 2 + y_off * l0, 0.0) + flow_field.add_sensor(sc, l0 / 4.0) + + n_obj = flow_field.obs.size // 2 # obs is (n_obj, DIM) flat + print(f" bodies before pinball: {n_obj}") + + # stabilize + stabilize_steps = int(4 * flow_field.FIELD_SHAPE[0] / u0) + print(f" stabilising ({stabilize_steps} steps)...") + flow_field.run(stabilize_steps, np.zeros(n_obj, dtype=data_type)) + + # record target (only sensor signals = obs[2:8]) + target_states = np.empty((0, 6), dtype=data_type) + for i in range(fifo_len): + flow_field.run(sample_interval, np.zeros(n_obj, dtype=data_type)) + new_state = flow_field.obs.copy()[2:8] # sensor0+1+2 velocities + target_states = np.vstack((target_states, new_state)) + + print(f" target recorded: {target_states.shape}") + return target_states, {"n_objects": n_obj, "NX": flow_field.FIELD_SHAPE[0], + "NY": flow_field.FIELD_SHAPE[1]} + + +def add_pinball( + flow_field: FlowField, + *, + l0: float, + u0: float, + sample_interval: int, + fifo_len: int, + data_type: type, + action_bias: Optional[Tuple[float, float, float]] = None, +) -> dict: + """Phase 2-3: add pinball cylinders, stabilize, compute norm, bias rollout. + + Steps (mirrors env.__init__ lines 88-117): + + 1. add front, bottom, top cylinders (id=4,5,6) + 2. stabilize run(4*NX/U0, zero-action[7]) + 3. get_ddf() + save_ddf() (checkpoint of stabilised state) + 4. FIFO_LEN × run(SAMPLE_INTERVAL, zero[7]) → compute norm + 5. apply_ddf() (restore pre-bias state) + 6. FIFO_LEN × run(SAMPLE_INTERVAL, bias-action[7]) → save_states + 7. apply_ddf() + + Returns dict with norm values. + """ + if action_bias is None: + action_bias = (0.0, -4.0, 4.0) # default cloak bias: front=0, bottom=-4U0, top=4U0 + + u0_float = float(u0) + n_obj_before = flow_field.obs.size // 2 + + # add 3 pinball cylinders + ny = flow_field.FIELD_SHAPE[1] + centers = [ + (30.0 * l0, (ny - 1) / 2, 0.0), + (31.3 * l0, (ny - 1) / 2 + 0.75 * l0, 0.0), + (31.3 * l0, (ny - 1) / 2 - 0.75 * l0, 0.0), + ] + for c in centers: + flow_field.add_cylinder(c, l0 / 2.0) + + n_obj = flow_field.obs.size // 2 + print(f" bodies after pinball: {n_obj}") + + # stabilize + stabilize_steps = int(4 * flow_field.FIELD_SHAPE[0] / u0_float) + print(f" stabilising pinball ({stabilize_steps} steps)...") + flow_field.run(stabilize_steps, np.zeros(n_obj, dtype=data_type)) + + # checkpoint DDF + flow_field.get_ddf() + flow_field.save_ddf() + + # --- norm phase (zero-action) --- + fifo = deque(maxlen=fifo_len) + for i in range(fifo_len): + flow_field.run(sample_interval, np.zeros(n_obj, dtype=data_type)) + fifo.append(flow_field.obs.copy()[2:14]) # sensor[6]+force[6] + + temp_states = np.array(fifo, dtype=data_type) + force_norm_fact = 6.0 * float(np.max(np.abs(temp_states[:, 6:12]))) + sens_deviation = np.mean(temp_states[:, 0:6], axis=0).astype(data_type) + sens_norm_fact = np.zeros(6, dtype=data_type) + for i in range(6): + sens_norm_fact[i] = 5.0 * float(np.max(np.abs(temp_states[:, i] - sens_deviation[i]))) + + print(f" norm: force_norm_fact={force_norm_fact:.6f}") + print(f" norm: sens_deviation={sens_deviation}") + print(f" norm: sens_norm_fact={sens_norm_fact}") + + # --- bias-action rollout --- + flow_field.apply_ddf() + bias = np.zeros(n_obj, dtype=data_type) + bias[n_obj - 3] = float(action_bias[0] * u0_float) # front + bias[n_obj - 2] = float(action_bias[1] * u0_float) # bottom + bias[n_obj - 1] = float(action_bias[2] * u0_float) # top + print(f" bias action: {bias}") + + fifo.clear() + for i in range(fifo_len): + flow_field.run(sample_interval, bias) + fifo.append(flow_field.obs.copy()[2:14]) + + save_states = np.array(list(fifo), dtype=data_type) + flow_field.apply_ddf() + + return { + "force_norm_fact": force_norm_fact, + "sens_deviation": sens_deviation.tolist(), + "sens_norm_fact": sens_norm_fact.tolist(), + "action_bias": list(action_bias), + "save_states": save_states, + } + + +def build_observation( + obs_slice: np.ndarray, + norm: dict, +) -> np.ndarray: + """Assemble normalised DRL observation (12-dim) from a single obs[2:14] slice. + + ``obs_slice`` is 12-element: sensor[0:6] + force[6:12]. + + Returns clipped 12-dim array in [-1, 1]. + """ + forces = obs_slice[6:12] / norm["force_norm_fact"] + sens = (obs_slice[0:6] - norm["sens_deviation"]) / norm["sens_norm_fact"] + obs = np.clip(np.hstack([forces, sens]), -1.0, 1.0).astype(np.float32) + return obs + + +def action_to_physical( + action_norm: np.ndarray, + *, + scale: float = 8.0, + bias: Tuple[float, float, float] = (0.0, -4.0, 4.0), + u0: float = 0.01, +) -> np.ndarray: + """Convert normalized action [-1,1] to physical omega (lattice units). + + physical_omega[i] = (action_norm[i] * scale + bias[i]) * u0 + """ + action_norm = np.asarray(action_norm, dtype=np.float64).reshape(-1, 3) + bias_arr = np.array(bias, dtype=np.float64) + return (action_norm * scale + bias_arr) * u0 + + +def scale_action( + action_norm: np.ndarray, + *, + scale: float = 8.0, + bias: Tuple[float, float, float] = (0.0, -4.0, 4.0), + u0: float = 0.01, + n_total_bodies: int = 7, +) -> np.ndarray: + """Convert normalised action ([-1,1]^3) to legacy CFD action array. + + Returns array of length *n_total_bodies* with cylinders' omegas at the + last 3 slots. + """ + a = np.zeros(n_total_bodies, dtype=np.float32) + omega = (np.array(action_norm, dtype=np.float32) * scale + np.array(bias, dtype=np.float32)) * u0 + a[n_total_bodies - 3:] = omega + return a + + +# --------------------------------------------------------------------------- +# Vorticity & field export +# --------------------------------------------------------------------------- + +def vorticity_from_ddf(flow_field: FlowField, u0: float) -> np.ndarray: + """Compute z-vorticity from current DDF on host.""" + flow_field.get_ddf() + ddf = flow_field.ddf.copy().reshape((9, flow_field.FIELD_SHAPE[1], flow_field.FIELD_SHAPE[0])).transpose(2, 1, 0) + ux = (ddf[:, :, 1] + ddf[:, :, 5] + ddf[:, :, 8] - ddf[:, :, 3] - ddf[:, :, 6] - ddf[:, :, 7]) / u0 + uy = (ddf[:, :, 2] + ddf[:, :, 5] + ddf[:, :, 6] - ddf[:, :, 4] - ddf[:, :, 7] - ddf[:, :, 8]) / u0 + # vorticity = dv/dx - du/dy + omega = np.gradient(uy, axis=1) - np.gradient(ux, axis=0) + return omega.astype(np.float64) + + +def save_vorticity_png(path: str, omega: np.ndarray, title: str = ""): + """Save vorticity field as a PNG with symmetric colour bar.""" + import matplotlib + matplotlib.use("Agg") + import matplotlib.pyplot as plt + + abs_o = np.abs(omega[np.isfinite(omega)]) + vmax = float(np.percentile(abs_o, 99.5)) if abs_o.size > 0 else 1.0 + if vmax <= 0: + vmax = 1.0 + + ny, nx = omega.shape + fig, ax = plt.subplots(figsize=(min(18, max(8, nx / 60)), min(10, max(3, ny / 40)))) + im = ax.imshow(omega, origin="lower", aspect="equal", cmap="RdBu_r", + vmin=-vmax, vmax=vmax, extent=(0, nx - 1, 0, ny - 1)) + ax.set_xlabel("x (lattice)") + ax.set_ylabel("y (lattice)") + if title: + ax.set_title(title) + fig.colorbar(im, ax=ax, fraction=0.046, pad=0.04, label=r"$\omega_z$") + fig.tight_layout() + fig.savefig(path, dpi=150, bbox_inches="tight") + plt.close(fig) + + +# --------------------------------------------------------------------------- +# DTW similarity +# --------------------------------------------------------------------------- + +def calc_lag(target: np.ndarray, state: np.ndarray) -> int: + """Find lag that maximises cross-correlation between two 1-D signals.""" + t = target - np.mean(target) + s = state - np.mean(state) + corr = np.correlate(t, s, mode="full") + lags = np.arange(-len(target) + 1, len(target)) + return int(lags[np.argmax(corr)]) + + +def calc_dtw_sim(target: np.ndarray, state: np.ndarray) -> float: + """DTW-based similarity: 1 - (DTW distance / len(target)). + + Both are 1-D arrays of possibly different lengths. + """ + n, m = len(target), len(state) + dtw = np.full((n + 1, m + 1), np.inf) + dtw[0, 0] = 0.0 + for i in range(1, n + 1): + for j in range(1, m + 1): + cost = abs(float(target[i - 1]) - float(state[j - 1])) + dtw[i, j] = cost + min(dtw[i - 1, j], dtw[i, j - 1], dtw[i - 1, j - 1]) + return float(1.0 - dtw[n, m] / n) + + +def compute_similarity( + target_states: np.ndarray, + state_series: np.ndarray, + conv_len: int, +) -> float: + """Compute lag-compensated DTW similarity over *conv_len* window. + + Matches the reward logic in env_karman_cloak_standard.step(). + """ + # lag from middle sensor + ref = target_states[conv_len:2 * conv_len, 1] + cur = state_series[-conv_len:, 1] + lag = calc_lag(ref, cur) + + sim_sum = 0.0 + for i in range(6): # 6 sensor dimensions + target_seq = np.roll(target_states[:, i], -lag)[conv_len:2 * conv_len] + state_seq = state_series[-conv_len:, i] + sim_sum += calc_dtw_sim(target_seq, state_seq) / 6.0 + return float(sim_sum) + + +# --------------------------------------------------------------------------- +# Dummy env for loading SB3 models +# --------------------------------------------------------------------------- + +def create_dummy_env(s_dim: int = 12, a_dim: int = 3): + """Return a gym.Env with correct observation/action spaces for model loading.""" + import gymnasium as gym + from gymnasium import spaces + + class DummyEnv(gym.Env): + def __init__(self): + super().__init__() + self.observation_space = spaces.Box(low=-1, high=1, shape=(s_dim,), dtype=np.float32) + self.action_space = spaces.Box(low=-1, high=1, shape=(a_dim,), dtype=np.float32) + + def reset(self, seed=None): + return np.zeros(s_dim, dtype=np.float32), {} + + def step(self, action): + return np.zeros(s_dim, dtype=np.float32), 0.0, False, False, {} + + def render(self): pass + + return DummyEnv() + + +def load_ppo_model(model_path: str, device: str = "cuda:0", s_dim: int = 12, a_dim: int = 3): + """Load a PPO model with Sin activation.""" + import torch + from torch.nn import Module + from stable_baselines3 import PPO + + class Sin(Module): + def forward(self, x): + return torch.sin(x) + + dummy_env = create_dummy_env(s_dim, a_dim) + model = PPO.load(model_path, env=dummy_env, device=device) + return model + + +# --------------------------------------------------------------------------- +# SINDy feature library – all in physical units +# --------------------------------------------------------------------------- + +def build_sindy_feature_library( + n_sensor: int = 6, + n_force: int = 6, +) -> list: + """Return list of feature names for SINDy regression (for reference). + + Features are built on-the-fly in ``build_sindy_features()``. + """ + names = [] + # bias + names.append("bias") + # raw sensor velocities (6 channels) + for i in range(n_sensor): + names.append(f"s{i}") + # raw forces (6 channels) + for i in range(n_force): + names.append(f"f{i}") + return names + + +def build_sindy_features( + sensors: np.ndarray, # (T, 6) raw sensor velocities + forces: np.ndarray, # (T, 6) raw forces + actions_prev: np.ndarray, # (T, 3) previous-step physical omegas (ch0_lag1) + include_extra: bool = True, +) -> np.ndarray: + """Construct feature matrix Theta(t) for SINDy. + + All quantities are in physical lattice units (not DRL-normalised). + + Base library (always included): + bias(1), s0..s5, f0..f5 + + Extra features (if ``include_extra=True``, default): + sin(pi*s0), cos(pi*s0), # phase info from middle sensor u + ds0..ds5, # forward difference of sensor signals + a0_lag1, a1_lag1, a2_lag1 # previous-step action (memory) + + Returns + ------- + Theta : (T, n_features) array + """ + T = sensors.shape[0] + cols = [] + + # 1. bias + cols.append(np.ones(T, dtype=np.float64)) + + # 2. raw sensors (6) + for i in range(6): + cols.append(sensors[:, i].astype(np.float64)) + + # 3. raw forces (6) + for i in range(6): + cols.append(forces[:, i].astype(np.float64)) + + # 4. sin/cos of middle-sensor u-velocity (phase info) + s0 = sensors[:, 0].astype(np.float64) # sensor0_ux + cols.append(np.sin(np.pi * s0)) + cols.append(np.cos(np.pi * s0)) + + # 5. sensor differences + for i in range(6): + diff = np.diff(sensors[:, i].astype(np.float64), prepend=sensors[0, i]) + cols.append(diff) + + # 6. previous action (lag-1 memory) + cols.append(actions_prev[:, 0].astype(np.float64)) # ch0 + cols.append(actions_prev[:, 1].astype(np.float64)) # ch1 + cols.append(actions_prev[:, 2].astype(np.float64)) # ch2 + + Theta = np.column_stack(cols) + return Theta + + +def get_sindy_feature_names(include_extra: bool = True) -> list: + """Return list of feature names matching ``build_sindy_features()`` output.""" + names = ["bias"] + for i in range(6): + names.append(f"s{i}") + for i in range(6): + names.append(f"f{i}") + if include_extra: + names += ["sin_s0", "cos_s0"] + for i in range(6): + names.append(f"ds{i}") + names += ["a0_lag1", "a1_lag1", "a2_lag1"] + return names + + +SINDY_DEFAULT_FEATURE_NAMES = get_sindy_feature_names(include_extra=True) + + +def fit_channel( + Theta: np.ndarray, + y: np.ndarray, + thresholds: list, + alpha: float = 1e-4, + max_iter: int = 25, +) -> tuple: + """Fit a single channel (one cylinder) with STLSQ threshold grid. + + Returns + ------- + rows : list of dict per threshold + best : dict with best threshold entry + """ + import pysindy as ps + + # Normalise features for thresholding stability + std = np.std(Theta, axis=0) + std = np.where(std < 1e-8, 1.0, std) + Theta_s = Theta / std + + best = None + rows = [] + for th in thresholds: + opt = ps.STLSQ(threshold=th, alpha=alpha, max_iter=max_iter) + opt.fit(Theta_s, y) + coef = np.asarray(opt.coef_, dtype=np.float64).flatten() / std + y_pred = Theta @ coef + ssr = float(np.sum((y - y_pred) ** 2)) + sst = float(np.sum((y - np.mean(y)) ** 2) + 1e-12) + r2 = 1.0 - ssr / sst + mae = float(np.mean(np.abs(y - y_pred))) + nz = int(np.sum(np.abs(coef) > 1e-8)) + entry = {"threshold": float(th), "nz": nz, "r2": r2, "mae": mae, "coef": coef} + rows.append(entry) + if best is None or r2 > best["r2"]: + best = entry + return rows, best + + +def print_control_law(feature_names: list, coef: np.ndarray, channel_label: str = "ch"): + """Pretty-print a sparse control law.""" + terms = [] + for i, c in enumerate(coef): + if abs(c) > 1e-8: + terms.append(f"{c:.6f} * {feature_names[i]}") + print(f" {channel_label}: {' + '.join(terms)}") + nz = sum(1 for c in coef if abs(c) > 1e-8) + print(f" non-zero terms: {nz}") + + +# --------------------------------------------------------------------------- +# Physics-guided feature library (v2 — replaces raw obs features) +# --------------------------------------------------------------------------- + +def compute_physical_symbols( + sensors: np.ndarray, # (T, 6) [s0_ux, s0_uy, s1_ux, s1_uy, s2_ux, s2_uy] + forces: np.ndarray, # (T, 6) [cyl0_fx, cyl0_fy, cyl1_fx, cyl1_fy, cyl2_fx, cyl2_fy] + actions_prev: np.ndarray, # (T, 3) previous-step physical omega + actions_prev2: np.ndarray, # (T, 3) omega(t-2), for delta terms +) -> dict: + """Compute physics-guided symbols from raw CFD data. + + Returns a dict of 1-D arrays (T,) keyed by symbol name. + """ + T = sensors.shape[0] + s = sensors.astype(np.float64) + f = forces.astype(np.float64) + a_prev = np.asarray(actions_prev, dtype=np.float64) + a_prev2 = np.asarray(actions_prev2, dtype=np.float64) + + # -- sensor symbols ---------------------------------------------------- + # streamwise + u0, u1, u2 = s[:, 0], s[:, 2], s[:, 4] + # cross-stream + v0, v1, v2 = s[:, 1], s[:, 3], s[:, 5] + + sym = {} + + sym["u_m"] = (u0 + u1 + u2) / 3.0 # mean wake deficit + sym["u_a"] = (u2 - u0) / 2.0 # antisymmetric (vortex street) + sym["u_c"] = u1.copy() # centreline streamwise + sym["u_curv"] = u0 - 2.0 * u1 + u2 # transverse curvature + + sym["v_m"] = (v0 + v1 + v2) / 3.0 # mean cross-stream + sym["v_a"] = (v2 - v0) / 2.0 # antisymmetric cross + sym["v_c"] = v1.copy() # centre cross + sym["v_curv"] = v0 - 2.0 * v1 + v2 # cross curvature + + # vortex phase encoding (keep on u_a which captures asymmetry) + sym["sin_ua"] = np.sin(np.pi * sym["u_a"]) + sym["cos_ua"] = np.cos(np.pi * sym["u_a"]) + + # -- force symbols ----------------------------------------------------- + # cylinders: [front_fx, front_fy, bottom_fx, bottom_fy, top_fx, top_fy] + fx_front, fy_front = f[:, 0], f[:, 1] + fx_bot, fy_bot = f[:, 2], f[:, 3] + fx_top, fy_top = f[:, 4], f[:, 5] + + sym["Fx_tot"] = fx_front + fx_bot + fx_top + sym["Fx_rear"] = fx_bot + fx_top + sym["Fx_diff"] = fx_top - fx_bot + + sym["Fy_tot"] = fy_front + fy_bot + fy_top + sym["Fy_rear"] = fy_bot + fy_top + sym["Fy_diff"] = fy_top - fy_bot + + # -- memory symbols ---------------------------------------------------- + sym["a0_lag1"] = a_prev[:, 0] + sym["a1_lag1"] = a_prev[:, 1] + sym["a2_lag1"] = a_prev[:, 2] + + sym["da0"] = a_prev[:, 0] - a_prev2[:, 0] + sym["da1"] = a_prev[:, 1] - a_prev2[:, 1] + sym["da2"] = a_prev[:, 2] - a_prev2[:, 2] + + return sym + + +# Which physical symbols to always include in the base library +PHYSICAL_BASE_SYMBOLS = [ + "bias", + "u_m", "u_a", "u_c", + "v_a", + "Fx_tot", "Fx_rear", "Fy_tot", "Fy_diff", + "sin_ua", "cos_ua", + "a0_lag1", "a1_lag1", "a2_lag1", + "da0", "da1", "da2", +] + +# Which symbols get mu modulation +PHYSICAL_MU_SYMBOLS = [ + "u_a", "v_a", "Fx_tot", "Fy_diff", "Fy_tot", +] + + +def build_physical_features( + sensors: np.ndarray, + forces: np.ndarray, + actions_prev: np.ndarray, + actions_prev2: np.ndarray, + mu: float = 0.0, + include_mu: bool = True, +) -> tuple: + """Construct physics-guided feature matrix. + + Returns + ------- + Theta : (T, n_feat) ndarray + names : list of feature names + """ + sym = compute_physical_symbols(sensors, forces, actions_prev, actions_prev2) + T = sensors.shape[0] + cols = [] + names = [] + + # 1. bias + cols.append(np.ones(T, dtype=np.float64)) + names.append("bias") + + # 2. base physical symbols + for key in PHYSICAL_BASE_SYMBOLS[1:]: # skip "bias" since we already added it + if key in sym: + cols.append(sym[key]) + names.append(key) + + # 3. mu and mu-modulated terms + if include_mu and mu > 0: + cols.append(np.full(T, mu, dtype=np.float64)) + names.append("mu") + for key in PHYSICAL_MU_SYMBOLS: + if key in sym: + cols.append(sym[key] * mu) + names.append(f"mu_{key}") + + Theta = np.column_stack(cols) + return Theta, names + + +def get_physical_feature_names(mu: float = 0.0, include_mu: bool = True) -> list: + """Return feature names matching ``build_physical_features()``.""" + names = ["bias"] + PHYSICAL_BASE_SYMBOLS[1:] + if include_mu and mu > 0: + names.append("mu") + for key in PHYSICAL_MU_SYMBOLS: + names.append(f"mu_{key}") + return names + + +# --------------------------------------------------------------------------- +# Dimensionless conversion and G operator (v3) +# --------------------------------------------------------------------------- + +def compute_dimensionless( + sensors: np.ndarray, # (T, 6) raw lattice [s0_ux, s0_uy, s1_ux, s1_uy, s2_ux, s2_uy] + forces: np.ndarray, # (T, 6) raw lattice [cyl0_fx, cyl0_fy, cyl1_fx, cyl1_fy, cyl2_fx, cyl2_fy] + u0: float = 0.01, + d: float = 20.0, + rho: float = 1.0, +) -> dict: + """Convert raw lattice CFD data to dimensionless physical quantities. + + Returns dict with keys: + u_hat_B, u_hat_C, u_hat_T : nondim streamwise velocity at 3 sensors + v_hat_B, v_hat_C, v_hat_T : nondim crosswise velocity + Cd_F, Cd_T, Cd_B : drag coefficient per cylinder + Cl_F, Cl_T, Cl_B : lift coefficient per cylinder + """ + T = sensors.shape[0] + s = sensors.astype(np.float64) + f = forces.astype(np.float64) + + # Sensor velocity ordering from legacy env: [sensor0_ux, sensor0_uy, sensor1_ux, sensor1_uy, sensor2_ux, sensor2_uy] + # Sensor positions: sensor0=top(y=+2L0), sensor1=mid(y=0), sensor2=bottom(y=-2L0) + # So top = sensor0 = s[:,0:2], mid = sensor1 = s[:,2:4], bottom = sensor2 = s[:,4:6] + # Per G-operator convention: B=bottom=sensor2, C=centre=sensor1, T=top=sensor0 + u_hat_T = s[:, 0] / u0 # top + v_hat_T = s[:, 1] / u0 + u_hat_C = s[:, 2] / u0 # centre + v_hat_C = s[:, 3] / u0 + u_hat_B = s[:, 4] / u0 # bottom + v_hat_B = s[:, 5] / u0 + + # Force ordering: [front_fx, front_fy, bottom_fx, bottom_fy, top_fx, top_fy] + # front=cyl0, bottom=cyl1, top=cyl2 + Cd_F = 2.0 * f[:, 0] / (rho * u0**2 * d) + Cl_F = 2.0 * f[:, 1] / (rho * u0**2 * d) + Cd_B = 2.0 * f[:, 2] / (rho * u0**2 * d) # bottom + Cl_B = 2.0 * f[:, 3] / (rho * u0**2 * d) + Cd_T = 2.0 * f[:, 4] / (rho * u0**2 * d) # top + Cl_T = 2.0 * f[:, 5] / (rho * u0**2 * d) + + return { + "u_hat_B": u_hat_B, "u_hat_C": u_hat_C, "u_hat_T": u_hat_T, + "v_hat_B": v_hat_B, "v_hat_C": v_hat_C, "v_hat_T": v_hat_T, + "Cd_F": Cd_F, "Cd_T": Cd_T, "Cd_B": Cd_B, + "Cl_F": Cl_F, "Cl_T": Cl_T, "Cl_B": Cl_B, + } + + +def apply_G_x( + u_hat_B: np.ndarray, u_hat_C: np.ndarray, u_hat_T: np.ndarray, + v_hat_B: np.ndarray, v_hat_C: np.ndarray, v_hat_T: np.ndarray, + Cd_F: np.ndarray, Cd_T: np.ndarray, Cd_B: np.ndarray, + Cl_F: np.ndarray, Cl_T: np.ndarray, Cl_B: np.ndarray, + aF_lag1: np.ndarray, aT_lag1: np.ndarray, aB_lag1: np.ndarray, + daF: np.ndarray, daT: np.ndarray, daB: np.ndarray, +): + """Apply mirror operator G to input state. + + CORRECTED: all rotation-related quantities (action, lag, delta) + change sign under mirror (y -> -y). + + G flips y -> -y: + - u_x: reorder only (B<->T), no sign change + - v_y: reorder (B<->T) AND sign change + - Cd: no sign change, B<->T + - Cl: sign change, B<->T + - aF: sign change + - aT <-> aB (swap AND sign change on the swapped values) + - da: same as a + + Returns dict with same keys as inputs, values are G-transformed. + """ + return { + "u_hat_B": u_hat_T, "u_hat_C": u_hat_C, "u_hat_T": u_hat_B, + "v_hat_B": -v_hat_T, "v_hat_C": -v_hat_C, "v_hat_T": -v_hat_B, + "Cd_F": Cd_F, "Cd_T": Cd_B, "Cd_B": Cd_T, + "Cl_F": -Cl_F, "Cl_T": -Cl_B, "Cl_B": -Cl_T, + "aF_lag1": -aF_lag1, "aT_lag1": -aB_lag1, "aB_lag1": -aT_lag1, + "daF": -daF, "daT": -daB, "daB": -daT, + } + + +def apply_G_alpha(alpha: np.ndarray) -> np.ndarray: + """Apply G to output action: [aF, aT, aB] -> [-aF, -aB, -aT].""" + return np.array([-alpha[0], -alpha[2], -alpha[1]], dtype=alpha.dtype) + + +# --------------------------------------------------------------------------- +# v3 physical symbols (dimensionless + equivariant-compatible) +# --------------------------------------------------------------------------- + +def compute_v3_symbols( + dim: dict, # from compute_dimensionless() + actions_prev: np.ndarray, # (T, 3) physical omega(t-1) + actions_prev2: np.ndarray, # (T, 3) physical omega(t-2) + mu: float, + include_mu: bool = True, +) -> tuple: + """Compute v3 physics-guided symbols from dimensionless quantities. + + Returns + ------- + Theta_front : np.ndarray (T, n_feat_front) — no bias column + Theta_top : np.ndarray (T, n_feat_top) — with bias column + names : list (common feature names, first is "bias" for top) + """ + T = actions_prev.shape[0] + + # Sensor combinations (nondim) + u_B, u_C, u_T = dim["u_hat_B"], dim["u_hat_C"], dim["u_hat_T"] + v_B, v_C, v_T = dim["v_hat_B"], dim["v_hat_C"], dim["v_hat_T"] + + u_m = (u_B + u_C + u_T) / 3.0 + u_a = (u_T - u_B) / 2.0 # antisymmetric (T - B) + u_c = u_C.copy() + v_a = (v_T - v_B) / 2.0 # antisymmetric (T - B) + + # Force combinations (dimensionless Cd/Cl) + Cd_F, Cd_T, Cd_B = dim["Cd_F"], dim["Cd_T"], dim["Cd_B"] + Cl_F, Cl_T, Cl_B = dim["Cl_F"], dim["Cl_T"], dim["Cl_B"] + + Cd_tot = Cd_F + Cd_T + Cd_B + Cd_rear = Cd_T + Cd_B + Cl_tot = Cl_F + Cl_T + Cl_B + Cl_diff = Cl_T - Cl_B + + # Phase + sin_ua = np.sin(np.pi * u_a) + cos_ua = np.cos(np.pi * u_a) + + # Memory (all 3 cylinders now that we dropped exchange equivariance) + aF_lag1 = actions_prev[:, 0] + aB_lag1 = actions_prev[:, 1] + aT_lag1 = actions_prev[:, 2] + daF = actions_prev[:, 0] - actions_prev2[:, 0] + daB = actions_prev[:, 1] - actions_prev2[:, 1] + daT = actions_prev[:, 2] - actions_prev2[:, 2] + + # Base features (common to all cylinders) + base_feats = { + "u_m": u_m, "u_a": u_a, "u_c": u_c, "v_a": v_a, + "Cd_tot": Cd_tot, "Cd_rear": Cd_rear, + "Cl_tot": Cl_tot, "Cl_diff": Cl_diff, + "sin_ua": sin_ua, "cos_ua": cos_ua, + "aF_lag1": aF_lag1, "aB_lag1": aB_lag1, "aT_lag1": aT_lag1, + "daF": daF, "daB": daB, "daT": daT, + } + + # Build feature arrays + base_names = list(base_feats.keys()) + cols_base = [base_feats[k] for k in base_names] + + # Mu modulation + mu_names = [] + if include_mu and mu > 0: + mu_feats = { + "mu": np.full(T, mu, dtype=np.float64), + "mu_u_a": u_a * mu, + "mu_v_a": v_a * mu, + "mu_Cd_tot": Cd_tot * mu, + "mu_Cl_diff": Cl_diff * mu, + } + mu_names = list(mu_feats.keys()) + cols_mu = [mu_feats[k] for k in mu_names] + else: + cols_mu = [] + + # Front model: NO bias + Theta_front = np.column_stack(cols_base + cols_mu) + + # Top model: WITH bias + cols_top = [np.ones(T, dtype=np.float64)] + cols_base + cols_mu + + Theta_top = np.column_stack(cols_top) + names = ["bias"] + base_names + mu_names + + return Theta_front, Theta_top, names diff --git a/src/analysis_crossre/steady_cloak_plan.md b/src/analysis_crossre/steady_cloak_plan.md new file mode 100644 index 0000000..f0621a9 --- /dev/null +++ b/src/analysis_crossre/steady_cloak_plan.md @@ -0,0 +1,121 @@ +# Steady Cloak Analysis Plan + +## Objective + +Extend the Karman cloak unified control framework to steady cloak, testing whether the same physical skeleton (features, symmetry, front no-bias, rear shared-head) applies across different cloak tasks. + +## Data Availability Check + +### Trained Models + +Looking at the models directory and legacy training scripts: + +| Model | Task | Status | +|-------|------|--------| +| d1a3o12_re50/re100/re200/re400 | Karman cloak | Phase 1 data exists | +| d1a3o12_250326 | Karman cloak (Re100) | Same as re100 | +| d1a3o12_250421_* | Reduced obs Karman | Different env | +| d1a3o14_250525_imit_* | Illusion | Different task | +| d1a3o12_250729_erase_* | Erase | Different task | + +**No dedicated steady cloak model found in models/.** + +The steady cloak results in the Confirmation report (Section 4.1) used the same d1a3o12_re100 architecture but trained from scratch with steady inflow target. + +### Key Differences: Steady vs Karman Cloak + +| Aspect | Karman Cloak | Steady Cloak | +|--------|-------------|--------------| +| Upstream disturbance | Cylinder (D=20) generating Karman vortex street | None (clean parabolic inflow) | +| Target signal | 3-sensor time series of undisturbed vortex street | Constant mean inlet velocity | +| Sensor output | Periodic (oscillating around mean) | Steady (near-constant) | +| PPO model | Re50-400 series | Unknown model name | +| Sample interval | 800 | Likely same | +| Action bias | [0, -4U0, +4U0] | Likely [0, 0, 0] or different | + +### Feature Implications for Steady Cloak + +When target is steady inflow: +- `u_a` (antisymmetric velocity) -> ~0 (symmetry at mean) +- `v_a` -> ~0 +- `sin_ua`, `cos_ua` -> ~0 (no oscillation to encode) +- `Fy_tot`, `Fy_diff` -> ~0 (no lift oscillation) +- `Fx_tot`, `Fx_rear` -> non-zero (base drag) +- Memory terms -> non-zero (action smoothing still needed) +- Mu modulation -> applicable + +**Many sensor-side features vanish!** This means the steady cloak likely relies mainly on force feedback + memory, not sensor feedback. This is a critical structural difference. + +### Hypothesis + +If the shared cloak skeleton exists, then: +1. The feature set for steady cloak should be a SUBSET of Karman cloak features +2. Force feedback + memory terms should be present in both +3. Sensor asymmetry terms (u_a, v_a, sin/cos) are Karman-specific +4. Front no-bias and rear shared-head should still apply + +## Execution Steps + +### Step 1: Identify/Obtain Steady Cloak Model + +**Action**: Check if steady cloak model exists under a different name, or was trained as part of the d1a3o12_250326 series. + +**Look for**: +- Legacy training scripts that train with steady target (no disturbance cylinder) +- In `legacy_train/` check for any script that uses `env_karman_cloak_standard.py` or similar but without the disturbance cylinder + +### Step 2: Phase 1 Inference (Steady) + +If model found, run Phase 1 inference for steady cloak (similar to `phase1_infer.py` but with steady target): + +1. Build env WITHOUT disturbance cylinder +2. Record target: clean parabolic inflow (constant sensor values) +3. Add pinball +4. Compute norm +5. Run uncontrolled + controlled (PPO) rollout + +**If no model exists**: +- Option A: Train a steady cloak PPO from scratch (1-2 days GPU time) +- Option B: Use the existing d1a3o12_re100 model with steady target - test if the Karman cloak model generalizes to steady inflow +- Option C: Use open-loop control from the report (discovered constant rotation) + +Option B is fastest: test `d1a3o12_re100` with steady inflow, record performance. If similarity > 0.8, the model transfers. + +### Step 3: Feature Support Analysis + +Compare active features between steady and Karman cloak: + +1. Fit SINDy on steady cloak data with same feature library +2. Compare support set (which features have non-zero coefficients) +3. Check G equivariance (should still hold) +4. Check front no-bias (should still hold) +5. Test rear shared-head (v23 structure) + +### Step 4: Cross-task Unified Fit + +If supports overlap: +1. Stack steady + Karman + (optionally) vortex data +2. Fit unified model with task-modulated parameters +3. Test in closed-loop + +## Resource Requirements + +| Step | Data needed | CFD inference | GPU time | +|------|------------|---------------|----------| +| 1 | None | No | ~0 | +| 2 | Steady model | Yes (~200s/Re) | ~3min | +| 3 | Steady rollout NPZ | No | ~0 | +| 4 | All NPZ data | No | ~1min | + +## Risk Assessment + +| Risk | Likelihood | Impact | Mitigation | +|------|-----------|--------|-----------| +| No steady cloak model exists | HIGH | HIGH | Option B (transfer Karman model) | +| Steady cloak uses different obs/action scaling | MEDIUM | MEDIUM | Check norm.json before fitting | +| Sensor features vanish (u_a, v_a ~0) | CERTAIN | LOW | This is expected; force+memory should dominate | +| G equivariance test fails for steady | LOW | MEDIUM | Steady flow is symmetric by construction | + +## Next Step + +Begin with Step 1: find the steady cloak model. Check `d1a3o12_250326.zip` and any other models. If none found, test `d1a3o12_re100.zip` on steady inflow as Option B. diff --git a/src/config.py b/src/config.py deleted file mode 100644 index 2ca0b50..0000000 --- a/src/config.py +++ /dev/null @@ -1,125 +0,0 @@ -""" -Configuration management for DynamisLab. - -Handles loading of CelerisLab configurations and project settings. -""" - -import os -import sys -from pathlib import Path -from typing import Optional, Tuple - -# Determine project root directory -_current_file = Path(__file__).resolve() -_project_root = _current_file.parent.parent.parent # Go up to DynamisLabNew/ - -# Configuration directory (relative to project root) -CONFIG_DIR = _project_root / 'configs' - -# Output directories -MODELS_DIR = _project_root / 'models' -OUTPUT_DIR = _project_root / 'output' -TENSORBOARD_DIR = _project_root / 'tensorboard' - -# Create output directories if they don't exist -MODELS_DIR.mkdir(exist_ok=True) -OUTPUT_DIR.mkdir(exist_ok=True) -TENSORBOARD_DIR.mkdir(exist_ok=True) - - -def setup_celeris_import() -> None: - """ - Setup CelerisLab import path. - - Assumes CelerisLab is either: - 1. Installed as a package (pip install CelerisLab) - 2. Available as a git submodule in project root - 3. Available via PYTHONPATH environment variable - """ - try: - # Try to import CelerisLab (it might be installed) - import CelerisLab - return - except ImportError: - pass - - # Check for CelerisLab as submodule - celeris_submodule = _project_root / 'CelerisLab' / 'src' - if celeris_submodule.exists(): - sys.path.insert(0, str(celeris_submodule)) - return - - # If still not found, raise error with helpful message - raise ImportError( - "CelerisLab not found. Please either:\n" - " 1. Install it: pip install -e ../CelerisLab\n" - " 2. Add as git submodule: git submodule add CelerisLab\n" - " 3. Set PYTHONPATH to include CelerisLab src directory" - ) - - -def load_celeris_configs( - cuda_config_path: Optional[str] = None, - field_config_path: Optional[str] = None -) -> Tuple: - """ - Load CelerisLab configurations. - - Args: - cuda_config_path: Optional path to CUDA config. If None, uses CONFIG_DIR. - field_config_path: Optional path to field config. If None, uses CONFIG_DIR. - - Returns: - Tuple of (config_cuda, config_field) - """ - # Setup CelerisLab import - setup_celeris_import() - - from CelerisLab import utils - - # Set environment variable to point to our configs - os.environ['CELERISLAB_CONFIG_DIR'] = str(CONFIG_DIR) - - # Load configurations - CelerisLab will find them automatically - if cuda_config_path is None: - config_cuda = utils.load_cuda_config() - else: - config_cuda = utils.load_cuda_config(cuda_config_path) - - if field_config_path is None: - config_field = utils.load_flow_field_config() - else: - config_field = utils.load_flow_field_config(field_config_path) - - return config_cuda, config_field - - -def get_model_path(model_name: str) -> Path: - """Get full path for a model file.""" - return MODELS_DIR / f"{model_name}.zip" - - -def get_tensorboard_logdir(run_name: str) -> Path: - """Get TensorBoard log directory for a run.""" - logdir = TENSORBOARD_DIR / run_name - logdir.mkdir(exist_ok=True) - return logdir - - -def get_output_path(filename: str) -> Path: - """Get full path for an output file.""" - return OUTPUT_DIR / filename - - -# Expose project paths for convenience -__all__ = [ - 'CONFIG_DIR', - 'MODELS_DIR', - 'OUTPUT_DIR', - 'TENSORBOARD_DIR', - 'setup_celeris_import', - 'load_celeris_configs', - 'get_model_path', - 'get_tensorboard_logdir', - 'get_output_path', -] diff --git a/src/drl_pinball/__init__.py b/src/drl_pinball/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/drl_pinball/__init__.py @@ -0,0 +1 @@ + diff --git a/src/drl_pinball/knowledge.md b/src/drl_pinball/knowledge.md new file mode 100644 index 0000000..23365f2 --- /dev/null +++ b/src/drl_pinball/knowledge.md @@ -0,0 +1,890 @@ +# DynamisLab Comprehensive Knowledge Document + +> **Project**: Active hydrodynamic cloaking and illusion using Deep Reinforcement Learning (DRL) on a fluidic pinball. +> **Solver**: GPU-accelerated Lattice Boltzmann Method (LBM, D2Q9, MRT) +> **DRL**: PPO with Sin activation, Stable-Baselines3 + +--- + +## 1. Grid Configuration + +### 1.1 Legacy Grid + +Two config files are multiplied to produce the actual lattice: + +| Config | Base (1U) | Multiplier (field_dim_in_U) | Result | +|--------|-----------|----------------------------|--------| +| `config_cuda.json` | X_1U=128, Y_1U=32, Z_1U=1 | — | — | +| `config_flowfield.json` | — | field_dim_in_U=[10, 16, 1] | — | +| **Actual grid** | — | — | **nx=1280, ny=512, nz=1** | + +`L0=20` is the base length unit. The "U" grid units (128, 32) are chosen to align with CUDA SM count on the GPU. + +### 1.2 New Grid (CelerisLab) + +| Config File | nx | ny | nz | +|------------|----|----|-----| +| `config_lbm_pinball.json` | 1280 | 512 | 1 | + +The new grid is **identical** to the legacy grid. `L0=20` remains the base length unit. + +### 1.3 Config File Locations + +- Legacy configs: `configs/legacy_configs/config_cuda.json`, `config_flowfield.json` +- New config: `configs/config_lbm_pinball.json` +- Body config: `configs/config_body.json` +- Reference: `configs/CONFIG.md` (full config schema documentation) + +--- + +## 2. Reynolds Number Definition + +**Critical**: The project uses two different Re definitions. + +| Symbol | Reference Length | Formula | Default Value | +|--------|-----------------|---------|---------------| +| Re_D (report/paper) | Single cylinder diameter D=20 | U0·D/ν | 0.01×20/0.004 = **50** | +| Re (code) | 2×D = 40 | U0·(2D)/ν | 0.01×40/0.004 = **100** | + +**Key mappings**: + +- Confirmation report `Re_D = 50` ↔ code `Re=100` +- Code `re100` models → physical `Re_D=50` +- Code `re50` models → physical `Re_D=25` +- Upstream disturbance cylinder (diameter = L0×1 = 20) → Re=U0×20/ν=50 (single diameter definition) + +### 2.1 Re via Viscosity + +| Code Name | Viscosity ν | Re (code, 2D ref) | Re_D (report) | +|-----------|-------------|-------------------|---------------| +| re50 | 0.008 | 50 | 25 | +| re100 | 0.004 | 100 | 50 | +| re200 | 0.002 | 200 | 100 | +| re400 | 0.001 | 400 | 200 | + +Formula: `Re = U0 * ref_length / ν` where ref_length = 2D = 40 for code Re. + +--- + +## 3. Boundary Conditions + +Both legacy and new simulations use the same boundary configuration: + +| Boundary | Condition | Details | +|----------|-----------|---------| +| Inlet (x=0) | **Parabolic profile**, Zou-He local scheme | u_x parabolic, u_y=0 | +| Outlet (x=64D) | Convective / NEQ extrapolation | `neq_extrap` mode, backflow clamp enabled | +| Top/Bottom walls (y=±12.8D) | **Bounce-back** (no-slip) | `y_wall_bc: "bounce_back"` | +| Cylinder surfaces | Ghost-node interpolation with prescribed rotational velocity | u_wall = a·(-sinθ, cosθ)^T | + +**Note**: The `config_lbm_pinball.json` explicitly uses `y_wall_bc: "bounce_back"`, which is equivalent to the legacy no-slip walls. The validation config `run_kan99b` uses `free_slip`, so be careful to use the correct config. + +**Fixed parameters**: +- U0 = 0.01 (inlet center velocity, lattice units) +- ν = 0.004 (default for Re=100 code) +- ρ = 1.0 +- Collision: MRT +- Streaming: esopull +- Data type: FP32 + +--- + +## 4. Complete Old-to-New API Conversion Table + +| Feature | Old API (FlowField, LegacyCelerisLab) | New API (Simulation, CelerisLab) | +|---------|--------------------------------------|----------------------------------| +| **Force reading** | `obs[i]` after `run()` = per-step average (internal ÷N) | `read_force(id)` = N-step cumulative sum; **must divide by N** | +| **Sensor reading** | `obs[i]` after `run()` = per-step average (internal ÷N) | `read_sensor(id, normalize=True)` = raw sum ÷ cell_count; **must still divide by N** | +| **Action setting** | `run(N, action_array)` — objects indexed by order in single array | `set_body(id, omega=value)` — each cylinder set separately | +| **Action smoothing** | Built-in exponential smoothing (weight=0.1) | None — implement manual `ActionSmoother` if needed | +| **Checkpoint/save** | `save_ddf()` / `restore_ddf()` / `apply_ddf()` (host memory) | `snapshot()` / `restore()` (memory) or `save_checkpoint(path)` / `load_checkpoint(path)` (HDF5) | +| **Initialization** | Constructor `FlowField(config_field, config_cuda, device_id)` auto-initializes | `Simulation(config)` then call `initialize()` separately | +| **Field output** | `save_field()` writes Tecplot `.dat` | `get_macroscopic()` returns numpy arrays | +| **Object addition** | `add_cylinder()`, `add_sensor()` on FlowField | Objects defined in `config_body.json` or added before `initialize()` | +| **Vortex addition** | `add_vortex(center, radius, strength, ...)` | Unknown — check API | +| **Numeric error check** | `flow_field.has_numeric_error()`, `flow_field.last_error_flag` | Manual implementation needed | +| **Context management** | `flow_field.context.push()` / `.pop()` | Stream management via API | + +### 4.1 Conversion Formulas + +```python +# Old API (per-step average): +flow_field.run(SAMPLE_INTERVAL, action_array) +obs = flow_field.obs # already per-step average + +# New API (must divide by N): +sim.bodies.zero_force_segment_async(stream) +sim.bodies.zero_sensor_segment_async(stream) +sim.run(SAMPLE_INTERVAL) +fx_per_step = sim.read_force(body_id)[0] / SAMPLE_INTERVAL +fy_per_step = sim.read_force(body_id)[1] / SAMPLE_INTERVAL +ux_per_step = sim.read_sensor(sensor_id)[0] / SAMPLE_INTERVAL +uy_per_step = sim.read_sensor(sensor_id)[1] / SAMPLE_INTERVAL +``` + +--- + +## 5. Per-Scene Geometry Map + +All coordinates in `L0=20` lattice units. Multiply by `L0` to get lattice coordinates unless otherwise noted. +- `CENTER_Y = (NY-1)/2 = 255.5` (lattice units) +- `NY = 512`, `NX = 1280` + +### 5.1 Karman Cloak / Erase / ReducedObs (Standard Pinball Layout) + +| Object | Position (L0 units) | Position (lattice) | Radius (L0) | Radius (lattice) | +|--------|---------------------|--------------------|-------------|------------------| +| Disturbance cylinder (upstream) | (10, CENTER_Y/L0, 0) | (200, 255.5, 0) | 1.0×L0 | 20 | +| Sensors (3x) | x=40, y=CENTER_Y/L0 + [2, 0, -2] | x=800 | L0/4 | 5 | +| Pinball front | (30, CENTER_Y/L0, 0) | (600, 255.5, 0) | L0/2 | 10 | +| Pinball bottom | (31.3, CENTER_Y/L0 − 0.75, 0) | (626, 240.5, 0) | L0/2 | 10 | +| Pinball top | (31.3, CENTER_Y/L0 + 0.75, 0) | (626, 270.5, 0) | L0/2 | 10 | + +**Object order in legacy API** (for cloak/erase/reduce_obs): +1. sensor0 (top, y=CENTER_Y+2*L0) +2. sensor1 (center, y=CENTER_Y) +3. sensor2 (bottom, y=CENTER_Y-2*L0) +4. dist_cylinder (upstream disturbance) +5. pinball_front +6. pinball_bottom +7. pinball_top + +### 5.2 Illusion (Imit) Layout + +**Target cylinder (recorded separately)**: + +| Object | Position (L0 units) | Radius | +|--------|---------------------|--------| +| Target cylinder | (20, CENTER_Y/L0, 0) | [0.75, 1.0, 1.5]×L0 (varies) | +| Sensors (3x) | x=30, y=CENTER_Y/L0 + [2, 0, -2] | L0/4 | + +**Pinball + sensors** (trained env): + +| Object | Position (L0 units) | Radius | +|--------|---------------------|--------| +| Sensors (3x) | x=30, y=CENTER_Y/L0 + [2, 0, -2] | L0/4 | +| Pinball front | (19, CENTER_Y/L0, 0) | L0/2 | +| Pinball bottom | (20.3, CENTER_Y/L0 + 0.75, 0) | L0/2 | +| Pinball top | (20.3, CENTER_Y/L0 − 0.75, 0) | L0/2 | + +**Object order** (illusion, 6 objects — no disturbance cylinder): +1. sensor0 (top, y=CENTER_Y+2*L0) +2. sensor1 (center, y=CENTER_Y) +3. sensor2 (bottom, y=CENTER_Y-2*L0) +4. pinball_front +5. pinball_bottom +6. pinball_top + +Action array: `temp[3:6] = (action*8 + [0, -2, 2]) * U0` + +### 5.3 Vortex Layout + +**Target phase** (sensors + vortex, no pinball): + +| Object | Position (L0 units) | Radius | +|--------|---------------------|--------| +| Sensors (3x) | x=40, y=CENTER_Y/L0 + [2, 0, -2] | L0/4 | +| Vortex | (10, CENTER_Y/L0, 0) | 2×L0 | + +**Pinball phase** (sensors + pinball + vortex): + +| Object | Position (L0 units) | Radius | +|--------|---------------------|--------| +| Sensors (3x) | x=40 | L0/4 | +| Pinball front | (30, CENTER_Y/L0, 0) | L0/2 | +| Pinball bottom | (31.3, CENTER_Y/L0 + 0.75, 0) | L0/2 | +| Pinball top | (31.3, CENTER_Y/L0 − 0.75, 0) | L0/2 | +| Vortex | (15, CENTER_Y/L0, 0) | 2×L0 | + +**Vortex types**: +- **Lamb dipole**: strength=0.5×U0, type="lamb" +- **Taylor monopole**: strength=0.03×U0, type="taylor" + +**MAX_STEPS = 150** (transient event — not infinite like other scenes) + +**Object order** (vortex, 6 objects — no disturbance cylinder): +1. sensor0 (top) +2. sensor1 (center) +3. sensor2 (bottom) +4. pinball_front +5. pinball_bottom +6. pinball_top + +Action array: `temp[3:6] = (action*4 + [0, -4, 4]) * U0` + +--- + +## 6. Per-Scene Action Scaling + +Each scene maps the normalized DRL action (range [-1, 1]) to physical angular velocity ω (U0 multiples): + +| Scene | Formula (ω/U0) | Scale | Bias | Physical range [front, bottom, top] | +|-------|---------------|-------|------|-------------------------------------| +| **Cloak (Karman)** | `action×8 + [0, -4, 4]` | 8 | [0, -4, 4] | front: [-8,8], bottom: [-12,4], top: [-4,12] | +| **Erase** | `action×8 + [0, -8, 8]` | 8 | [0, -8, 8] | front: [-8,8], bottom: [-16,0], top: [0,16] | +| **Illusion (Imit)** | `action×8 + [0, -2, 2]` | 8 | [0, -2, 2] | front: [-8,8], bottom: [-10,6], top: [-6,10] | +| **Vortex** | `action×4 + [0, -4, 4]` | 4 | [0, -4, 4] | front: [-4,4], bottom: [-8,0], top: [0,8] | + +**Final omega in lattice units**: Multiply the result by `U0=0.01`. + +**Example** (Cloak, action=[1, 1, 1]): +``` +ω_front = (1*8 + 0) * 0.01 = 0.08 +ω_bottom = (1*8 + (-4)) * 0.01 = 0.04 +ω_top = (1*8 + 4) * 0.01 = 0.12 +``` + +**Example** (Cloak, action=[0, 0, 0] — the bias actions): +``` +ω_front = 0 +ω_bottom = -4 * 0.01 = -0.04 +ω_top = 4 * 0.01 = 0.04 +``` + +### 6.1 Action Smoothing (Legacy Only) + +Legacy `FlowField.run()` has **built-in exponential smoothing**: +```python +action_pinned = (1 - weight) * action_pinned + weight * action_target +# weight = 0.1 +``` +This means the actual applied ω smoothly transitions toward the target. The new API has **no built-in smoothing** — implement `ActionSmoother` manually if needed for numerical stability. + +--- + +## 7. Norm Semantics + +The normalization values are computed during environment initialization and **must be identical during inference**. They are model-specific and cannot be reused across different scenarios. + +### 7.1 Norm Collection Procedure (Standard Pattern) + +```python +# Phase 1: Zero-action rollout +for i in range(FIFO_LEN): + flow_field.run(SAMPLE_INTERVAL, zero_action) # 4 or 7 objects depending on phase + fifo_states.append(flow_field.obs[sensor_select]) # e.g. [2:14] skips dist_cyl + +# Phase 2: Compute normalization factors +temp_states = np.array(fifo_states) # shape: (FIFO_LEN, N_sensors+N_forces) + +force_norm_fact = 6 * max(|forces|) # forces = temp_states[:, 6:12] +for i in range(6): + sens_deviation[i] = mean(sensor_i) # sensor_i = temp_states[:, i] + sens_norm_fact[i] = 5 * max(|sensor_i - mean|) + +# Phase 3: Bias-action rollout (for FIFO initialization) +flow_field.apply_ddf() # restore checkpoint +for i in range(FIFO_LEN): + flow_field.run(SAMPLE_INTERVAL, bias_action) + fifo_states.append(...) +save_states = fifo_states.copy() +``` + +### 7.2 Norm Format + +```python +norm = { + "force_norm_fact": float, # scalar = 6 * max(|forces|) + "sens_deviation": [6 floats], # mean per sensor channel + "sens_norm_fact": [6 floats], # 5 * max(|sensor - deviation|) per channel + "save_states": ndarray, # FIFO_LEN × N_obs array after bias rollout + "action_bias": [b_front, b_bottom, b_top], # e.g. [0.0, -4.0, 4.0] + "n_obj_total": int # total objects in flow field +} +``` + +### 7.3 Scene-Specific Norm Variations + +| Scene | force_norm_fact formula | sens_norm_fact factor | Obs slice (from fifo) | +|-------|------------------------|----------------------|----------------------| +| Cloak (standard) | `6 * max(\|forces\|)` | 5 | `obs[2:14]` (skip 2 dist_cyl sensor channels) | +| Erase | `100 * max(\|forces\|)` | 10 | `obs[0:14]` (full 14, incl. dist force) | +| Illusion | `6 * max(\|forces\|)` | 5 | `obs[0:12]` (full 12) | +| Vortex | `6 * max(\|forces\|)` | 5 | `obs[0:12]` (full 12) | +| ReducedObs | `10 * max(\|forces\|)` | 5 | `obs[2:14]` | + +### 7.4 Observation Normalization (per step) + +```python +# cloak/standard: +forces = obs_slice[6:12] / force_norm_fact +sens = (obs_slice[0:6] - sens_deviation) / sens_norm_fact +observation = clip(hstack([forces, sens]), -1, 1) + +# erase: +forces = obs_slice[6:14] / force_norm_fact # Note: 8 force values (includes dist_cylinder) +# But only forces[2:8] (pinball forces) are used in observation +sens = (obs_slice[0:6] - sens_deviation) / sens_norm_fact +``` + +### 7.5 L0 Units vs Lattice Coordinates + +Multiple sources define positions in L0 units; multiply by L0=20 to get lattice (pixel) coordinates. + +| Description | L0 units | Lattice (pixels) | +|-------------|----------|-------------------| +| Grid size | — | 1280 × 512 | +| Center Y (CENTER_Y) | — | (512-1)/2 = 255.5 | +| Disturbance cylinder x | 10×L0 → 10 | 200 | +| Sensor x (cloak/erase/vortex/reduce) | 40×L0 → 40 | 800 | +| Sensor x (illusion) | 30×L0 → 30 | 600 | +| Pinball front x (cloak/erase/vortex/reduce) | 30×L0 | 600 | +| Pinball front x (illusion) | 19×L0 | 380 | +| Pinball bottom/top x (cloak/erase/vortex/reduce) | 31.3×L0 | 626 | +| Pinball bottom/top x (illusion) | 20.3×L0 | 406 | +| Pinball radius (all) | L0/2 | 10 | +| Sensor radius (all) | L0/4 | 5 | +| Disturbance cylinder radius (cloak/erase) | L0 | 20 | +| Vortex radius | 2×L0 | 40 | +| Target cylinder radius (illusion) | [0.75, 1.0, 1.5]×L0 | [15, 20, 30] | + +--- + +## 8. Per-Scene Running Parameters + +| Parameter | Cloak | Erase | Illusion | Vortex | ReducedObs | +|-----------|-------|-------|----------|--------|------------| +| S_DIM | 12 | 12 | 14 | 12 | varies (3→2) | +| A_DIM | 3 | 3 | 3 | 3 | 3 | +| SAMPLE_INTERVAL | 800 | 600 | varies | 800 | 800 | +| FIFO_LEN | 150 | 150 | 150 | 150 | 150 | +| CONV_LEN | 30 | 36 | 36 | 30 | 36 | +| MAX_STEPS | 500 | 500 | 500 | **150** | 500 | +| T0 | 1000 | 1000 | 1000 | 1000 | 1000 | +| Objects in env | 7 | 7 | 6 | 6 | 7 | +| Has disturbance cyl | Yes | Yes | No | No | Yes | +| DRL training initial model | — (scratch) | `d1a3o12_250326_erase` | — (scratch) | `d1a3o12_re100` | — (scratch) | + +### 8.1 Reward Functions + +**Cloak (Karman)**: +```python +reward_cd = exp(-|cd * 20|) # cd = (Σforces_fx) / 3 +reward_cl = exp(-|cl * 80|) # cl = (Σforces_fy) / 3 +reward_sim = exp(-10 * |sim - 1|) # sim = DTW-based similarity +reward = min(0.3*reward_cd + 0.4*reward_cl + 0.3*reward_sim, 1.0) +``` + +**Erase**: +```python +# Target = clean inflow mean (steady), not the noisy vortex street +reward_u = exp(-|diff_u * 40|) # diff of current vs target sensor u +reward_v = 0.7*exp(-|amp_v*20|) + 0.3*exp(-|diff_v*20|) +reward_sim = similarities # raw DTW similarity (not exponentiated) +reward = min(0.4*reward_u + 0.4*reward_v + 0.2*reward_sim, 1.0) +``` + +**Illusion**: +```python +# Target forces from harmonics reconstruction of target cylinder +reward_cd = exp(-|(cd - cd_target) * 10|) +reward_cl = exp(-|(cl - cl_target) * 10|) +reward_sim = exp(-10 * |sim - 1|) +reward = min(0.3*reward_cd + 0.3*reward_cl + 0.4*reward_sim, 1.0) +``` + +**Vortex**: +```python +reward_cd = exp(-|cd * 20|) +reward_cl = exp(-|cl * 80|) +reward_sim = exp(-10 * |sim - 1|) +reward = min(0.2*reward_cd + 0.3*reward_cl + 0.5*reward_sim, 1.0) +``` + +--- + +## 9. Old API Obs Layout + +### 9.1 Cloak (Karman) / Standard Env — 7 Objects + +Object addition order: +1. Disturbance cylinder (id=0) +2. Sensor0 / top (id=1) +3. Sensor1 / center (id=2) +4. Sensor2 / bottom (id=3) +5. Pinball front (id=4) +6. Pinball bottom (id=5) +7. Pinball top (id=6) + +**`flow_field.obs` array** (14 values = 7 objects × 2): +``` +obs[0:2] = dist_cylinder force (fx, fy) — ignored in training +obs[2:4] = sensor0 velocity (ux, uy) +obs[4:6] = sensor1 velocity (ux, uy) +obs[6:8] = sensor2 velocity (ux, uy) +obs[8:10] = front_pinball force (fx, fy) +obs[10:12] = bottom_pinball force (fx, fy) +obs[12:14] = top_pinball force (fx, fy) +``` + +**Normalized observation** (after `obs[2:14]` slice): +``` +obs_norm[0:6] = sensor0_ux, sensor0_uy, sensor1_ux, sensor1_uy, sensor2_ux, sensor2_uy +obs_norm[6:12] = front_fx, front_fy, bottom_fx, bottom_fy, top_fx, top_fy +``` + +**Action array** (7 entries, n_objects=7): +``` +temp[0:4] = 0 (sensors + dist_cylinder — ignored) +temp[4] = front omega +temp[5] = bottom omega +temp[6] = top omega +``` + +### 9.2 Erase Env — 7 Objects + +Same addition order as Cloak (disturbance cylinder radius=0.75*L0 instead of 1.0*L0). + +**`flow_field.obs` array** (14 values): +``` +obs[0:2] = dist_cylinder force (fx, fy) +obs[2:4] = sensor0 velocity (ux, uy) +obs[4:6] = sensor1 velocity (ux, uy) +obs[6:8] = sensor2 velocity (ux, uy) +obs[8:10] = front_pinball force (fx, fy) +obs[10:12] = bottom_pinball force (fx, fy) +obs[12:14] = top_pinball force (fx, fy) +``` + +**Normalized observation** (full `obs[0:14]` slice — include dist_cylinder forces): +``` +forces = obs[6:14] / force_norm_fact # 8 force values +# But only forces[2:8] (pinball) used in step() +sens = (obs[0:6] - sens_deviation) / sens_norm_fact +``` + +**Target recording** uses `obs[0:6]` (sensor only, no dist cylinder force), since erase has **no disturbance cylinder during target phase**. + +### 9.3 Illusion (Imit) Env — 6 Objects + +Object addition order (sensors first, then pinball): +1. Sensor0 / top (id=0) +2. Sensor1 / center (id=1) +3. Sensor2 / bottom (id=2) +4. Pinball front (id=3) +5. Pinball bottom (id=4) +6. Pinball top (id=5) + +**`flow_field.obs` array** (12 values): +``` +obs[0:2] = sensor0 velocity (ux, uy) +obs[2:4] = sensor1 velocity (ux, uy) +obs[4:6] = sensor2 velocity (ux, uy) +obs[6:8] = front_pinball force (fx, fy) +obs[8:10] = bottom_pinball force (fx, fy) +obs[10:12] = top_pinball force (fx, fy) +``` + +**Normalized observation** (full `obs[0:12]`): +``` +forces = obs[6:12] / force_norm_fact +sens = (obs[0:6] - sens_deviation) / sens_norm_fact +obs_norm = hstack([forces, sens]) # 12 values +# Plus 2 additional: target_cd, target_cl → total 14 (S_DIM=14) +``` + +**Action array** (6 entries): +``` +temp[0:3] = 0 (sensors — ignored) +temp[3] = front omega +temp[4] = bottom omega +temp[5] = top omega +``` + +**Target recording** uses `obs[0:8]` (3 sensor × 2 + 1 cylinder × 2 = 8 values from target cylinder + 3 sensors). + +### 9.4 Vortex Env — 6 Objects + +Same object order as Illusion (sensors + pinball, no disturbance cylinder). + +Same obs layout as Illusion (12 values, obs[0:12] used as-is). + +**Target recording**: In the target phase, `obs` has 3 sensors only (6 values, `obs[0:6]`). + +### 9.5 ReducedObs Env — 7 Objects + +Same geometry and object order as Karman Cloak (7 objects: dist_cyl + 3 sensors + 3 pinball). + +**Obs slice**: `obs[2:14]` (same as Cloak, skipping dist_cylinder forces). + +**Additional torque observation**: Some reduced-obs models also compute torque: +```python +obs_torque = (-obs[1] - obs[2]*√3/2 + obs[3]/2 + obs[4]*√3/2 + obs[5]/2) / torque_norm_fact +``` + +The observation layout varies by model name suffix: +| Model name | S_DIM | Observation components | +|-----------|-------|----------------------| +| `forces02` | 3 | `[obs_torque, dist_fx, dist_fy]` (?) | +| `total_force` | 3 | Total force components | +| `torque+forces02` | 5 | torque + dist forces | +| `torque+forces02+sens24` | 9 | torque + dist forces + sensors 2,4 | +| `torque+forces04+sens04` | 5 | torque + forces + sensors | + +See `legacy_env_reduce_obs.py` line 191 for exact obs composition. + +--- + +## 10. Complete Model Inventory + +All model `.zip` files are PPO policies with Sin activation and 64×64 hidden layers. + +### 10.1 `models/old/` — Original Training (Cloak, Various Re) + +| File | Env | S_DIM | A_DIM | Action Scale/Bias | Sample Interval | Re (code) | Description | +|------|-----|-------|-------|-------------------|-----------------|-----------|-------------| +| `d1a3o12_re50.zip` | Cloak | 12 | 3 | 8 / [0,-4,4] | 800 | 50 | Cloak at lower Re; base model | +| `d1a3o12_re100.zip` | Cloak | 12 | 3 | 8 / [0,-4,4] | 800 | 100 | Cloak at Re=100 (ν=0.004); **most used base model** | +| `d1a3o12_re200.zip` | Cloak | 12 | 3 | 8 / [0,-4,4] | 800 | 200 | Cloak at higher Re | +| `d1a3o12_re400.zip` | Cloak | 12 | 3 | 8 / [0,-4,4] | 800 | 400 | Cloak at highest Re | +| `vortex_lamb.zip` | Vortex | 12 | 3 | 4 / [0,-4,4] | 800 | 100 | Cloak of Lamb dipole vortex; **transfer** from re100 | +| `vortex_taylor.zip` | Vortex | 12 | 3 | 4 / [0,-4,4] | 800 | 100 | Cloak of Taylor monopole vortex; **transfer** from re100 | + +**Naming convention** `d1a3o12`: +- `d1` = 1 disturbance cylinder +- `a3` = 3 actuators (pinball cylinders) +- `o12` = 12 observations +- `o14` = 14 observations (used for illusion, includes target forces) + +### 10.2 `models/250326/` — Re-trained Cloak + +| File | Env | S_DIM | A_DIM | Scale/Bias | Desc | +|------|-----|-------|-------|------------|------| +| `d1a3o12_250326.zip` | Cloak | 12 | 3 | 8 / [0,-4,4] | Re-trained from scratch; equivalent to re100 | + +### 10.3 `models/250329/` — No-offset Cloak + +| File | Env | S_DIM | A_DIM | Scale/Bias | Desc | +|------|-----|-------|-------|------------|------| +| `d0a3o12_250329_nooffset.zip` | Cloak | 12 | 3 | 8 / [0,0,0] | Disturbance-cylinder-free (d0), zero bias actions | + +### 10.4 `models/250421/` — Reduced Observation + +| File | Env | S_DIM | A_DIM | Scale/Bias | Desc | +|------|-----|-------|-------|------------|------| +| `d1a3o12_250421_forces02.zip` | ReducedObs | 3 | 3 | 8 / [0,-4,4] | Obs reduced to 3 values | +| `d1a3o12_250421_torque+forces02.zip` | ReducedObs | 5 | 3 | 8 / [0,-4,4] | Torque + 2 force values | +| `d1a3o12_250421_torque+forces02+sens24.zip` | ReducedObs | 9 | 3 | 8 / [0,-4,4] | Torque + forces + sensors | +| `d1a3o12_250421_torque+forces04+sens04.zip` | ReducedObs | 5 | 3 | 8 / [0,-4,4] | Modified obs composition | +| `d1a3o12_250421_torque+total_force.zip` | ReducedObs | 5 | 3 | 8 / [0,-4,4] | Torque + total force | +| `d1a3o12_250421_total_force.zip` | ReducedObs | 3 | 3 | 8 / [0,-4,4] | Only total force | + +All trained from scratch (no transfer). Obs reduction experiments for experimental hardware simplification. + +### 10.5 `models/250525/` — Illusion (Imit) + +| File | Env | S_DIM | A_DIM | Scale/Bias | Target | Sample Interval | +|------|-----|-------|-------|------------|--------|-----------------| +| `d1a3o12_250525_imit_075L_1U.zip` | Illusion | 14 | 3 | 8 / [0,-2,2] | 0.75L cylinder, U0=0.01 | 600 | +| `d1a3o12_250525_imit_1L_1U.zip` | Illusion | 14 | 3 | 8 / [0,-2,2] | 1.0L cylinder, U0=0.01 | 600 | +| `d1a3o12_250525_imit_1L_1U_trans.zip` | Illusion | 14 | 3 | 8 / [0,-2,2] | 1.0L cylinder, U0=0.01 | 600 (transfer) | +| `d1a3o14_250525_imit_075L_2U.zip` | Illusion | 14 | 3 | 8 / [0,-2,2] | 0.75L cylinder, 2×U0 | 600 | +| `d1a3o14_250525_imit_075L_2U_1.zip` | Illusion | 14 | 3 | 8 / [0,-2,2] | 0.75L cylinder | ~600 | +| `d1a3o14_250525_imit_075L_2U_400S.zip` | Illusion | 14 | 3 | 8 / [0,-2,2] | 0.75L cylinder | 400 | +| `d1a3o14_250525_imit_15L_2U.zip` | Illusion | 14 | 3 | 8 / [0,-2,2] | 1.5L cylinder, 2×U0 | 600 | +| `d1a3o14_250525_imit_1L_2U.zip` | Illusion | 14 | 3 | 8 / [0,-2,2] | 1.0L cylinder, 2×U0 | 600 | +| `d1a3o14_250525_imit_1L_2U_1.zip` | Illusion | 14 | 3 | 8 / [0,-2,2] | 1.0L cylinder | ~600 | +| `d1a3o14_250525_imit_1L_2U_400S_02Vis.zip` | Illusion | 14 | 3 | 8 / [0,-2,2] | 1.0L cylinder | 400 | +| `d1a3o14_250525_imit_1L_2U_600S.zip` | Illusion | 14 | 3 | 8 / [0,-2,2] | 1.0L cylinder | 600 | +| `d1a3o14_250525_imit_1L_2U_800S_08Vis.zip` | Illusion | 14 | 3 | 8 / [0,-2,2] | 1.0L cylinder | 800 | +| `d1a3o14_250525_imit_1L_2U_1000S_08Vis.zip` | Illusion | 14 | 3 | 8 / [0,-2,2] | 1.0L cylinder | 1000 | + +**Naming conventions**: +- `075L` = target cylinder diameter = 0.75×L0 +- `1L` = target cylinder diameter = 1.0×L0 +- `15L` = target cylinder diameter = 1.5×L0 +- `1U` = U0=0.01 (standard), `2U` = 2×U0=0.02 +- `400S` etc. = SAMPLE_INTERVAL +- `02Vis` etc. = ν (viscosity) multiplier (e.g. 0.08×ν) +- `trans` = transfer learning model +- `_1` suffix = variant + +### 10.6 `models/250729/` — Erase & Re-cloak + +| File | Env | S_DIM | A_DIM | Scale/Bias | Base Model | Desc | +|------|-----|-------|-------|------------|------------|------| +| `d1a3o12_250729_250326_cloak_800S_02Vis.zip` | Cloak | 12 | 3 | 8 / [0,-4,4] | `d1a3o12_250326` | Re-cloak, 02×ν | +| `d1a3o12_250729_250326_erase.zip` | Erase | 12 | 3 | 8 / [0,-8,8] | `d1a3o12_250326` | Erase, transfer from cloak | +| `d1a3o12_250729_250326_erase_250804_20D_retrain2.zip` | Erase | 12 | 3 | 8 / [0,-8,8] | `erase` | Erase retrain, 20D delay | +| `d1a3o12_250729_250326_erase_250804_20D_retrain3.zip` | Erase | 12 | 3 | 8 / [0,-8,8] | `erase` | Erase retrain v3 | + +Erase models: SAMPLE_INTERVAL=600, CONV_LEN=36 (vs 800/30 for cloak). + +--- + +## 11. DRL Hyperparameters + +| Parameter | Value | +|-----------|-------| +| Algorithm | PPO (Stable-Baselines3 `PPO`) | +| Policy network | `MlpPolicy` | +| Hidden layers | 64 × 64 (both actor and critic) | +| Activation function | **Sin** (custom `torch.nn.Module`) | +| Optimizer | Adam | +| Learning rate (actor) | 3×10⁻⁴ | +| Learning rate (critic) | 4×10⁻⁴ | +| Episode length | 600 T₀ (T₀ = D/U₀ = 2000 LBM steps) | +| Action interval | 0.8 T₀ (one action per 0.8 flow-through times) | +| Training timesteps/iteration | 360-400 (varies by scene) | +| Total episodes | ~500 (varies; vortex uses ~100) | +| Device | CUDA GPU (device_id varies) | +| Deterministic inference | Yes (`use_deterministic=True`) | + +### 11.1 Custom Sin Activation + +```python +class Sin(Module): + def __init__(self): + super().__init__() + def forward(self, x): + return torch.sin(x) +``` + +Used in place of Tanh/ReLU because trigonometric functions better preserve spectral fidelity for vortex-dominated flows. Networks are: + +``` +Input(s_t) → Linear(64) → Sin → Linear(64) → Sin → Linear(n_actions) → Output(a_t) +``` + +### 11.2 Time Scales + +| Quantity | LBM steps | Description | +|----------|-----------|-------------| +| T₀ = D/U₀ | 20/0.01 = 2000 | One flow-through time (single cylinder diameter) | +| SAMPLE_INTERVAL | 600-800 | Steps between DRL actions | +| Action interval | 0.8 T₀ | SAMPLE_INTERVAL / T₀ = 800/2000 = 0.4 ... actually 800/2000=0.4 | +| Episode length | 600 T₀ = 1.2M steps | Total episode duration | + +**Correction**: The paper says "actuations per T₀ = 1.25" and "action interval = 0.8 T₀". This means SAMPLE_INTERVAL = 0.8×2000 = 1600 LBM steps? But the code says SAMPLE_INTERVAL=800. This is a discrepancy — note that the paper uses T₀ = D/U₀ and SAMPLE_INTERVAL=800, which gives 800/2000 = 0.4 T₀ = 2.5 actuations per T₀. The paper may use a different T₀ definition, or the hyperparameter table may be aspirational vs actual. + +--- + +## 12. Target Signal & Illusion Harmonics + +### 12.1 Karman Cloak Target + +Recorded from: 1 disturbance cylinder + 3 sensors (no pinball). +- 150 steps × SAMPLE_INTERVAL = 800 steps +- `obs[2:8]` → 6 sensor channels stored +- Stored in `target_states` (150, 6) + +### 12.2 Erase Target + +Recorded from: 3 sensors only (no disturbance cylinder). +- 150 steps × SAMPLE_INTERVAL = 600 steps +- `obs[0:6]` → 6 sensor channels stored +- No periodic signal → target is mean only (clean inflow) + +### 12.3 Illusion Target + Harmonics + +Recorded from: 1 target cylinder + 3 sensors (no pinball). +- 150 steps × SAMPLE_INTERVAL (varies) +- `obs[0:8]` → 3 sensor (6) + 1 cylinder force (2) = 8 channels stored +- **Harmonics analysis**: FFT over target_states extracts DC + top 5 frequency harmonics per channel +- Stored as `target_harmonics`: list of dicts with `{dc, amps, freqs, phases}` per channel (8 channels total) +- During training, target forces are reconstructed via `gen_target_states_at(step, harmonics)` + +### 12.4 Vortex Target + +Recorded from: 3 sensors + Lamb dipole or Taylor monopole (no pinball). +- 150 steps × SAMPLE_INTERVAL = 800 steps +- `obs[0:6]` → 6 sensor channels stored +- Vortex is transient: target_states contains the evolving vortex signal + +--- + +## 13. Training Strategies + +### 13.1 Training Loop Pattern (All Scenes) + +```python +for i in range(total_episodes): # 100-500 + model.learn(total_timesteps=K) # K = 360-1500 + test_env = model.get_env() + test_obs = test_env.reset() + for step in range(eval_steps): # 150-360 + test_action, _ = model.predict(test_obs) + test_obs, reward, done, info = test_env.step(test_action) + list_reward.append(reward) + avg_reward = mean(list_reward[-tail:]) # last 100-180 steps + # Save if best + if avg_reward > max_reward: + model.save(...) +``` + +### 13.2 Transfer Learning + +| Source Model | Target Model | Method | +|-------------|-------------|--------| +| `d1a3o12_re100` (Cloak) | `vortex_lamb`, `vortex_taylor` | Transfer: loaded as base, retrained on vortex env | +| `d1a3o12_250326` (Cloak) | Erase models | Transfer: loaded as base, retrained on erase env | +| Erase base | Erase retrain models | Transfer: loaded from previously trained erase | +| `d1a3o12_250525_imit_1L_2U_600S` | `..._1000S_08Vis` etc. | Transfer: varied SAMPLE_INTERVAL/viscosity | + +Base models are loaded via `PPO.load(path, env=new_env, device=...)` and then fine-tuned. + +### 13.3 Checkpoint & Reset Mechanism + +```python +# During __init__: +flow_field.run(...) # stabilize +flow_field.get_ddf() # host ← GPU +flow_field.save_ddf() # save to host memory + +# During reset/restore: +flow_field.restore_ddf() # restore from host memory +flow_field.apply_ddf() # host → GPU +``` + +The restored state includes the stable pinball wake (with vortex for vortex scenes). The FIFO is re-initialized from `save_states`. + +--- + +## 14. File Structure Reference + +``` +DynamisLab/ +├── configs/ +│ ├── config_lbm_pinball.json # NEW LBM config (nx=1280, ny=512) +│ ├── config_body.json # Body/object definitions +│ ├── CONFIG.md # Config schema documentation +│ └── legacy_configs/ +│ ├── config_cuda.json # Legacy CUDA config (X_1U=128, etc.) +│ ├── config_flowfield.json # Legacy flow field config +│ └── config_gym.json # Legacy gym config +├── models/ +│ ├── old/ # Original re100/re200/re400/re50 + vortex +│ ├── 250326/ # Re-trained cloak +│ ├── 250329/ # No-offset cloak +│ ├── 250421/ # Reduced observation +│ ├── 250525/ # Illusion (imit) +│ └── 250729/ # Erase + re-cloak +├── src/ +│ ├── drl_pinball/ +│ │ ├── knowledge.md ← THIS FILE +│ │ ├── legacy_env/ # Old-API environment classes +│ │ │ ├── legacy_env_karman_cloak_standard.py +│ │ │ ├── legacy_env_erase.py +│ │ │ ├── legacy_env_imit.py +│ │ │ ├── legacy_env_imit_target.py +│ │ │ ├── legacy_env_vortex.py +│ │ │ ├── legacy_env_reduce_obs.py +│ │ │ └── legacy_karman_env.py # Reference implementation (re100) +│ │ ├── legacy_train/ # Training scripts +│ │ │ ├── karman_cloak.py +│ │ │ ├── erase.py +│ │ │ ├── imit.py +│ │ │ ├── vortex.py +│ │ │ └── reduce_obs.py +│ │ └── legacy_test/ # Test/evaluation scripts +│ ├── analysis_crossre/ # Cross-Re analysis (SINDy, etc.) +│ └── CelerisLab/ or LegacyCelerisLab/ # Solver libraries +├── docs/ +│ ├── understanding_notes.md # Earlier knowledge consolidation +│ └── My_Confirmation/Chapters/ # LaTeX thesis chapters +├── LegacyCelerisLab/ # Old solver library +└── output/ # Field output, .pkl files +``` + +--- + +## 15. Key Numerical Values Quick Reference + +| Quantity | Value | Notes | +|----------|-------|-------| +| NX | 1280 | Grid x-dimension | +| NY | 512 | Grid y-dimension | +| L0 | 20 | Base length unit (lattice) | +| U0 | 0.01 | Centerline inlet velocity (lattice) | +| ν (default) | 0.004 | Kinematic viscosity → Re=100 (code) | +| T₀ = D/U₀ | 2000 | Flow-through time (single cylinder diameter) | +| Reynolds (code) | Re = U0·(2D)/ν | Uses 2D reference | +| Reynolds (report) | Re_D = U0·D/ν | Uses 1D reference | +| SAMPLE_INTERVAL | 600-800 | Steps between DRL actions | +| FIFO_LEN | 150 | History buffer length | +| Action scale | 4 or 8 | Scene-dependent | +| Action bias | varies | Scene-dependent | +| Omega guard | [0.01, 1.99] | From config_lbm_pinball.json | + +### 15.1 Physics-to-Lattice Unit Relations + +``` +D (cylinder diameter) = 20 lattice units = 1.0 in L0 units +Single cylinder Re: Re_D = U0 × D / ν = 0.01 × 20 / 0.004 = 50 +Code Re: Re_code = U0 × 2D / ν = 0.01 × 40 / 0.004 = 100 +``` + +--- + +## 16. Important Implementation Notes + +### 16.1 Obs Slice Differences + +The sensor indices in `obs` are **not** consistent across envs: +- **Cloak/standard**: uses `obs[2:14]` — skips first 2 values (disturbance cylinder forces) +- **Erase**: uses `obs[0:14]` — includes all values (disturbance cylinder forces are part of observation) +- **Illusion/Vortex**: uses `obs[0:12]` — sensors(6) + pinball forces(6) + +### 16.2 Center Y Computation + +```python +CENTER_Y = (NY - 1) / 2.0 # = 255.5 for NY=512 +``` + +This is because NY is even (512), so center is between two lattice rows. + +### 16.3 Force Norm Factors + +These are scene-specific constants that must NOT be shared between scenes: + +| Scene | force_norm_fact | sens_norm_fact factor | +|-------|----------------|----------------------| +| Cloak/standard | `6 * max(|forces|)` | 5 | +| Erase | `100 * max(|forces|)` | 10 | +| Illusion | `6 * max(|forces|)` | 5 | +| Vortex | `6 * max(|forces|)` | 5 | +| ReducedObs | `10 * max(|forces|)` | 5 | + +### 16.4 Vortex Scene Termination + +The vortex env is the only scene with **bounded episodes**: +- `MAX_STEPS = 150` (vs 500 for all other scenes) +- `done = self.current_step >= MAX_STEPS` +- This is because the vortex is a transient event that passes through the domain + +### 16.5 Experimental Setup (from thesis) + +- Water tunnel with towing platform (25cm width) +- Custom force sensor integrating air bearing + 2D force sensor (semiconductor strain gauges) +- Custom low-noise, high-precision data acquisition system +- Planar PIV for flow field measurement +- Current challenges: sensor noise from over-constraint/welding; rail manufacturing precision + +--- + +## 17. Key Differences Between Legacy and New Solvers + +| Aspect | Legacy | New | +|--------|--------|-----| +| Solver name | `LegacyCelerisLab` / `CelerisLab` | `CelerisLab` (new) | +| Main class | `FlowField(config_field, config_cuda, device_id)` | `Simulation(config)` | +| Object setup | `add_cylinder()`, `add_sensor()` at runtime | Pre-defined in JSON or added before `initialize()` | +| Checkpoint | `get_ddf()` / `save_ddf()` / `restore_ddf()` / `apply_ddf()` | `snapshot()` / `restore()` or save_checkpoint | +| Force reading | Part of unified `obs` array | `read_force(body_id)` per body | +| Sensor reading | Part of unified `obs` array | `read_sensor(body_id)` per body | +| Torque reading | Not used in legacy (only in ReduceObs) | `read_torque(body_id)` | +| Action smoothing | Built-in (weight=0.1) | None | +| Action application | Single `run(N, action_array)` | `set_body(id, omega=val)` per body then `run(N)` | +| Context management | `context.push()` / `.pop()` for CUDA isolation | Stream-based | + +### 17.1 Force/Sensor Value Correction + +The fundamental difference in how forces and sensors are accumulated: + +**Legacy**: `flow_field.run(N, action)` → internal loop averages over N steps → `obs` contains per-step averages. + +**New**: `sim.run(N)` → accelerators accumulate raw sums → `read_force(id)` gives N-step sum → **must divide by N**. + +--- + +*End of comprehensive knowledge document. Last updated: 2026-06-05.* diff --git a/src/drl_pinball/legacy_env/__init__.py b/src/drl_pinball/legacy_env/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/drl_pinball/legacy_env/__init__.py @@ -0,0 +1 @@ + diff --git a/src/drl_pinball/legacy_env/legacy_env_erase.py b/src/drl_pinball/legacy_env/legacy_env_erase.py new file mode 100644 index 0000000..207aeb2 --- /dev/null +++ b/src/drl_pinball/legacy_env/legacy_env_erase.py @@ -0,0 +1,304 @@ +# 这个是erase的env,用于训练和评估d1a3o12_250729_250326_erase系列模型 +# 上游扰流圆柱,场景与Karman_cloak_standard一致, +# 但是目标是希望pinball后流场跟入口一致,即抹除扰流圆柱尾迹 +# 模型名中D代表信号延迟 +import gymnasium as gym +import numpy as np +from gymnasium import spaces +import ctypes +from collections import deque +from typing import Tuple +import sys +import os +import matplotlib.pyplot as plt +import queue + +os.environ["OMP_NUM_THREADS"] = "1" +os.environ["MKL_NUM_THREADS"] = "1" + +current_dir = os.path.dirname(os.path.abspath("__file__")) +parent_dir = os.path.abspath(os.path.join(current_dir, os.pardir)) +sys.path.append(parent_dir) +from CelerisLab import FlowField +from CelerisLab import utils + +config_cuda = utils.load_cuda_config( + os.path.join(parent_dir, "configs", "config_cuda.json") +) +config_field = utils.load_flow_field_config( + os.path.join(parent_dir, "configs", "config_flowfield.json") +) + +S_DIM, A_DIM = 12, 3 +U0 = config_field.velocity +T0 = 1000 +SAMPLE_INTERVAL = 600 +FIFO_LEN = 150 +CONV_LEN = 36 +MAX_STEPS = 500 +if config_field.data_type == "FP32": + DATA_TYPE = np.float32 +else: + raise ValueError(f"Unsupported data type {config_field.data_type}.") + + +class CustomEnv(gym.Env): + """Custom Environment that follows gym interface.""" + + metadata = {"render_modes": ["human"], "render_fps": T0 / SAMPLE_INTERVAL} + + def __init__(self, device_id=0): + super().__init__() + self.action_space = spaces.Box(low=-1, high=1, shape=(A_DIM,), dtype=DATA_TYPE) + self.observation_space = spaces.Box( + low=-1, high=1, shape=(S_DIM,), dtype=DATA_TYPE + ) + self.fifo_states = deque(maxlen=FIFO_LEN) + self.target_states = np.empty((0, 6), dtype=DATA_TYPE) + self.force_norm_fact = 1.0 + self.sens_norm_fact = np.ones(6, dtype=DATA_TYPE) + self.sens_deviation = np.zeros(6, dtype=DATA_TYPE) + self.reward_u = 0.0 + self.reward_v = 0.0 + self.reward_sim = 0.0 + self.current_step = 0 + + self.flow_field = FlowField(config_field, config_cuda, device_id) + L0 = 20 + U0 = config_field.velocity + NX = self.flow_field.FIELD_SHAPE[0] + NY = self.flow_field.FIELD_SHAPE[1] + + self.ddf_ave = np.zeros((NX, NY, 9), dtype=DATA_TYPE) + self.ddf_ave_cont = 0 + + # center: Tuple[float, float, float] = (10 * L0, (NY - 1) / 2, 0) + # self.flow_field.add_cylinder(center, L0) + center: Tuple[float, float, float] = (40 * L0, (NY - 1) / 2 + 2 * L0, 0) + self.flow_field.add_sensor(center, L0 / 4) + center: Tuple[float, float, float] = (40 * L0, (NY - 1) / 2, 0) + self.flow_field.add_sensor(center, L0 / 4) + center: Tuple[float, float, float] = (40 * L0, (NY - 1) / 2 - 2 * L0, 0) + self.flow_field.add_sensor(center, L0 / 4) + self.flow_field.run(int(2*NX/U0), np.zeros(3, dtype=DATA_TYPE)) + + for i in range(FIFO_LEN): + self.flow_field.run(SAMPLE_INTERVAL, np.zeros(3, dtype=DATA_TYPE)) + new_state = self.flow_field.obs.copy()[0:6] + self.target_states = np.vstack((self.target_states, new_state)) + + self.target_sensors = np.mean(self.target_states, axis=0) + + # self.flow_field.apply_ddf() + center: Tuple[float, float, float] = (10 * L0, (NY - 1) / 2, 0) + self.flow_field.add_cylinder(center, 0.75*L0) + center: Tuple[float, float, float] = (30 * L0, (NY - 1) / 2, 0) + self.flow_field.add_cylinder(center, L0 / 2) + center: Tuple[float, float, float] = (31.3 * L0, (NY - 1) / 2 + 0.75 * L0, 0) + self.flow_field.add_cylinder(center, L0 / 2) + center: Tuple[float, float, float] = (31.3 * L0, (NY - 1) / 2 - 0.75 * L0, 0) + self.flow_field.add_cylinder(center, L0 / 2) + self.flow_field.run(int(4*NX/U0), np.zeros(7, dtype=DATA_TYPE)) + self.flow_field.get_ddf() + self.flow_field.save_ddf() + + for i in range(FIFO_LEN): + self.flow_field.run(SAMPLE_INTERVAL, np.zeros(7, dtype=DATA_TYPE)) + self.fifo_states.append(self.flow_field.obs.copy()[0:14]) + + temp_states = np.array(self.fifo_states) + self.force_norm_fact = 100 * np.max(np.abs(temp_states[:, 8:14])) + + for i in range(6): + self.sens_deviation[i] = np.mean(temp_states[:, i]) + self.sens_norm_fact[i] = 10 * np.max(np.abs(temp_states[:, i] - self.sens_deviation[i])) + + self.flow_field.apply_ddf() + + for i in range(FIFO_LEN): + self.flow_field.run(SAMPLE_INTERVAL, np.array([0.0, 0.0, 0.0, 0.0, 0.0, -8*U0, 8*U0], dtype=DATA_TYPE)) + self.fifo_states.append(self.flow_field.obs.copy()[0:14]) + + self.save_states = self.fifo_states.copy() + self.flow_field.apply_ddf() + + def step(self, action): + assert self.action_space.contains(action), "%r (%s) invalid" % ( + action, + type(action), + ) + + # barrier = threading.Barrier(2) + result_queue = queue.Queue() + + def run_flow_field(action): + self.flow_field.context.push() + U0 = config_field.velocity + try: + temp = np.zeros(7, dtype=DATA_TYPE) + temp[4:7] = np.array((action*8+[0,-8,8])*U0, dtype=DATA_TYPE) + self.flow_field.run(SAMPLE_INTERVAL, temp) + finally: + self.flow_field.context.pop() + # barrier.wait() + self.fifo_states.append(self.flow_field.obs.copy()[0:14]) + + def proc_data(): + states = np.array(self.fifo_states) + forces = states[-1, 6:14] / self.force_norm_fact + cd = (forces[2] + forces[4] + forces[6]) / 3 + cl = (forces[3] + forces[5] + forces[7]) / 3 + sens = (states[-1, 0:6] - self.sens_deviation) / self.sens_norm_fact + target_sens = (self.target_sensors - self.sens_deviation) / self.sens_norm_fact + + similarities = 0.0 + + def calc_lag(target, state): + target_mean = np.mean(target) + state_mean = np.mean(state) + + correlation = np.correlate(target - target_mean, state - state_mean, "full") + lags = np.arange(-len(target) + 1, len(target)) + max_lag = lags[np.argmax(correlation)] + return max_lag + + def calc_sim(target, state): + # 计算幅值差异权重 + target_std = np.std(target) if np.std(target) > 1e-8 else 1e-8 + state_std = np.std(state) if np.std(state) > 1e-8 else 1e-8 + amplitude_ratio = min(target_std, state_std) / max(target_std, state_std) + + # 计算均值差异 + mean_diff = abs(np.mean(target) - np.mean(state)) + max_scale = max(abs(np.mean(target)), abs(np.mean(state)), 1e-8) + mean_similarity = 1 / (1 + mean_diff / max_scale * 10) + + # DTW计算 + n = len(target) + m = len(state) + + dtw_matrix = np.full((n + 1, m + 1), np.inf) + dtw_matrix[0, 0] = 0 + + for i in range(1, n + 1): + for j in range(1, m + 1): + cost = abs(target[i - 1] - state[j - 1]) + last_min = min(dtw_matrix[i - 1, j], + dtw_matrix[i, j - 1], + dtw_matrix[i - 1, j - 1]) + dtw_matrix[i, j] = cost + last_min + + # 改进的归一化方法 + max_possible_cost = max(np.max(np.abs(target)), np.max(np.abs(state)), 1e-8) + dtw_distance = dtw_matrix[n, m] / (len(target) * max_possible_cost) + DTW_similarity = max(0, 1 - dtw_distance) + + # 综合相似度:形状相似度 * 幅值相似度 * 均值相似度 + total_similarity = 0.8 * DTW_similarity + 0.1 * amplitude_ratio + 0.1 * mean_similarity + + return total_similarity + + # id_sens = 1 + # target_seq = self.target_states[CONV_LEN:2*CONV_LEN, id_sens] + target_seq = -states[CONV_LEN:2*CONV_LEN, 7] + # state_seq = states[-CONV_LEN:, id_sens] + state_seq = states[-CONV_LEN:, 9] + lag = calc_lag(target_seq, state_seq) + + for i in range(0, 2): + target_seq = -np.roll(states[:, i+6], -lag)[CONV_LEN:2*CONV_LEN] + state_seq = states[-CONV_LEN:, i+8] + states[-CONV_LEN:, i+10] + states[-CONV_LEN:, i+12] + similarities += calc_sim(target_seq, state_seq) / 2 + + diff_u = (np.abs(sens[0] - target_sens[0]) + np.abs(sens[2] - target_sens[2]) + np.abs(sens[4] - target_sens[4]))/3 + diff_v = (np.abs(sens[1] - target_sens[1]) + np.abs(sens[3] - target_sens[3]) + np.abs(sens[5] - target_sens[5]))/3 + mean_u = np.mean(np.abs(states[:, 0] - self.target_sensors[0]) \ + + np.abs(states[:, 2] - self.target_sensors[2]) \ + + np.abs(states[:, 4] - self.target_sensors[4])) / self.sens_norm_fact[2] + amp_v = np.std(states[:, 1] + states[:, 3] + states[:, 5]) / self.sens_norm_fact[3] + self.reward_u = np.exp(-np.abs(diff_u * 40)) + self.reward_v = 0.7 * np.exp(-np.abs(amp_v * 20)) + 0.3 * np.exp(-np.abs(diff_v * 20)) + # self.reward_sim = np.exp(-50*np.abs(similarities - 1)**2) + self.reward_sim = similarities + reward = np.minimum(0.4 * self.reward_u + 0.4 * self.reward_v + 0.2 * self.reward_sim, 1.0) + result_queue.put((np.hstack([forces[2:8], sens]), reward)) + + run_flow_field(action) + proc_data() + observation, reward = result_queue.get() + + truncated = bool(np.any(observation > 1) or np.any(observation < -1)) + observation = np.clip(observation, -1, 1) + self.current_step += 1 + # done = self.current_step >= MAX_STEPS + done = False + return observation, float(reward), done, truncated, {} + + def reset(self, seed=None): + self.flow_field.restore_ddf() + self.flow_field.apply_ddf() + self.fifo_states = self.save_states.copy() + self.current_step = 0 + return np.zeros(S_DIM, dtype=np.float32), {} + + def render(self, mode="human"): + NX = self.flow_field.FIELD_SHAPE[0] + NY = self.flow_field.FIELD_SHAPE[1] + self.flow_field.get_ddf() + ddf_plot = self.flow_field.ddf.copy().reshape((9, NY, NX)).transpose(2, 1, 0) + ux = (ddf_plot[:, :, 1] + ddf_plot[:, :, 5] + ddf_plot[:, :, 8] - ddf_plot[:, :, 3] - ddf_plot[:, :, 6] - ddf_plot[:, :, 7]) / U0 + uy = (ddf_plot[:, :, 2] + ddf_plot[:, :, 5] + ddf_plot[:, :, 6] - ddf_plot[:, :, 4] - ddf_plot[:, :, 7] - ddf_plot[:, :, 8]) / U0 + speed = np.sqrt(ux**2 + uy**2) + plt.figure(figsize=(10, 5)) + plt.imshow(speed.T, origin='lower', cmap='viridis', extent=[0, NX, 0, NY]) + plt.colorbar(label='Speed') + plt.title('Scalar Velocity Field') + plt.xlabel('X') + plt.ylabel('Y') + plt.tight_layout() + plt.show() + + def save_field(self, filename): + NX = self.flow_field.FIELD_SHAPE[0] + NY = self.flow_field.FIELD_SHAPE[1] + self.flow_field.get_ddf() + ddf_plot = self.flow_field.ddf.copy().reshape((9, NY, NX)).transpose(2, 1, 0) + flag_plot = self.flow_field.flag.copy().reshape((NY, NX)).transpose(1, 0) + ux = (ddf_plot[:, :, 1] + ddf_plot[:, :, 5] + ddf_plot[:, :, 8] - ddf_plot[:, :, 3] - ddf_plot[:, :, 6] - ddf_plot[:, :, 7]) / U0 + uy = (ddf_plot[:, :, 2] + ddf_plot[:, :, 5] + ddf_plot[:, :, 6] - ddf_plot[:, :, 4] - ddf_plot[:, :, 7] - ddf_plot[:, :, 8]) / U0 + with open(os.path.join(parent_dir, "output", filename), "w") as f: + f.write("Title= \"LBM 2D\"\r\n") + f.write("VARIABLES= \"X\",\"Y\",\"flag\",\"U\",\"V\",\r\n") + f.write(f"ZONE T= \"BOX\",I= {NX},J= {NY},F=POINT\r\n") + for j in range(NY): + for i in range(NX): + f.write(f"{i},{j},{flag_plot[i, j]},{ux[i, j]},{uy[i, j]}\r\n") + + def average_field(self, mode=["add", "save", "clear"], filename="average_field.dat"): + NX = self.flow_field.FIELD_SHAPE[0] + NY = self.flow_field.FIELD_SHAPE[1] + self.flow_field.get_ddf() + ddf_new = self.flow_field.ddf.copy().reshape((9, NY, NX)).transpose(2, 1, 0) + if "add" in mode: + self.ddf_ave = self.ddf_ave + ddf_new + self.ddf_ave_cont += 1 + if "save" in mode: + if self.ddf_ave_cont == 0: + raise ValueError("No data to save. Please run 'add' mode first.") + ux = (self.ddf_ave[:, :, 1] + self.ddf_ave[:, :, 5] + self.ddf_ave[:, :, 8] - self.ddf_ave[:, :, 3] - self.ddf_ave[:, :, 6] - self.ddf_ave[:, :, 7]) / U0 / self.ddf_ave_cont + uy = (self.ddf_ave[:, :, 2] + self.ddf_ave[:, :, 5] + self.ddf_ave[:, :, 6] - self.ddf_ave[:, :, 4] - self.ddf_ave[:, :, 7] - self.ddf_ave[:, :, 8]) / U0 / self.ddf_ave_cont + flag_plot = self.flow_field.flag.copy().reshape((NY, NX)).transpose(1, 0) + with open(os.path.join(parent_dir, "output", filename), "w") as f: + f.write("Title= \"LBM 2D\"\r\n") + f.write("VARIABLES= \"X\",\"Y\",\"flag\",\"U\",\"V\",\r\n") + f.write(f"ZONE T= \"BOX\",I= {NX},J= {NY},F=POINT\r\n") + for j in range(NY): + for i in range(NX): + f.write(f"{i},{j},{flag_plot[i, j]},{ux[i, j]},{uy[i, j]}\r\n") + print(f"Average field amount: {self.ddf_ave_cont}") + if "clear" in mode: + self.ddf_ave = np.zeros((NX, NY, 9), dtype=DATA_TYPE) + self.ddf_ave_cont = 0 + + def close(self): + self.flow_field.__del__() \ No newline at end of file diff --git a/src/drl_pinball/legacy_env/legacy_env_imit.py b/src/drl_pinball/legacy_env/legacy_env_imit.py new file mode 100644 index 0000000..15ae833 --- /dev/null +++ b/src/drl_pinball/legacy_env/legacy_env_imit.py @@ -0,0 +1,318 @@ +# 这个是imit圆柱的env,用于训练和评估d1a3o14_250525_imit系列模型 +# 上游干净来流,目标是pinball后流场跟设定尺寸圆柱一致 +# 模型名中L代表目标直径,S代表SAMPLE_INTERVAL +import gymnasium as gym +import numpy as np +from gymnasium import spaces +import ctypes +from collections import deque +from typing import Tuple +import sys +import os +import matplotlib.pyplot as plt +import queue + +os.environ["OMP_NUM_THREADS"] = "1" +os.environ["MKL_NUM_THREADS"] = "1" + +current_dir = os.path.dirname(os.path.abspath("__file__")) +parent_dir = os.path.abspath(os.path.join(current_dir, os.pardir)) +sys.path.append(parent_dir) +from CelerisLab import FlowField +from CelerisLab import utils + +config_cuda = utils.load_cuda_config( + os.path.join(parent_dir, "configs", "config_cuda.json") +) +config_field = utils.load_flow_field_config( + os.path.join(parent_dir, "configs", "config_flowfield.json") +) + +S_DIM, A_DIM = 14, 3 +U0 = config_field.velocity +T0 = 1000 +SAMPLE_INTERVAL = 600 # 这里会随着圆柱尺寸和粘性变动,在模型名中体现 +FIFO_LEN = 150 +CONV_LEN = 36 +MAX_STEPS = 500 +if config_field.data_type == "FP32": + DATA_TYPE = np.float32 +else: + raise ValueError(f"Unsupported data type {config_field.data_type}.") + + +class CustomEnv(gym.Env): + """Custom Environment that follows gym interface.""" + + metadata = {"render_modes": ["human"], "render_fps": T0 / SAMPLE_INTERVAL} + + def __init__(self, device_id=0): + super().__init__() + self.action_space = spaces.Box(low=-1, high=1, shape=(A_DIM,), dtype=DATA_TYPE) + self.observation_space = spaces.Box( + low=-1, high=1, shape=(S_DIM,), dtype=DATA_TYPE + ) + self.fifo_states = deque(maxlen=FIFO_LEN) + self.target_states = np.empty((0, 8), dtype=DATA_TYPE) + self.force_norm_fact = 1.0 + self.sens_norm_fact = np.ones(6, dtype=DATA_TYPE) + self.sens_deviation = np.zeros(6, dtype=DATA_TYPE) + self.reward_cd = 0.0 + self.reward_cl = 0.0 + self.reward_sim = 0.0 + self.current_step = 0 + + self.flow_field = FlowField(config_field, config_cuda, device_id) + L0 = 20 + U0 = config_field.velocity + NX = self.flow_field.FIELD_SHAPE[0] + NY = self.flow_field.FIELD_SHAPE[1] + + self.ddf_ave = np.zeros((NX, NY, 9), dtype=DATA_TYPE) + self.ddf_ave_cont = 0 + + center: Tuple[float, float, float] = (20 * L0, (NY - 1) / 2, 0) + self.flow_field.add_cylinder(center, 1.5*L0) # 这里会随着圆柱尺寸变动,在模型名中体现 + center: Tuple[float, float, float] = (30 * L0, (NY - 1) / 2 + 2 * L0, 0) + self.flow_field.add_sensor(center, L0 / 4) + center: Tuple[float, float, float] = (30 * L0, (NY - 1) / 2, 0) + self.flow_field.add_sensor(center, L0 / 4) + center: Tuple[float, float, float] = (30 * L0, (NY - 1) / 2 - 2 * L0, 0) + self.flow_field.add_sensor(center, L0 / 4) + self.flow_field.run(int(4*NX/U0), np.zeros(4, dtype=DATA_TYPE)) + + for i in range(FIFO_LEN): + self.flow_field.run(SAMPLE_INTERVAL, np.zeros(4, dtype=DATA_TYPE)) + new_state = self.flow_field.obs.copy()[0:8] + self.target_states = np.vstack((self.target_states, new_state)) + + def analyze_harmonics(states, n_harmonics): + N, D = states.shape + result = [] + for d in range(D): + y = states[:, d] + fft_coef = np.fft.rfft(y) + freqs = np.fft.rfftfreq(N, d=1) + amps = 2 * np.abs(fft_coef) / N + phases = np.angle(fft_coef) + idx = np.argsort(amps[1:])[::-1][:n_harmonics] + 1 + harmonics = { + 'dc': np.real(fft_coef[0]) / N, + 'amps': amps[idx], + 'freqs': freqs[idx], + 'phases': phases[idx] + } + result.append(harmonics) + return result + + self.target_harmonics = analyze_harmonics(self.target_states, n_harmonics=5) + + del self.flow_field + self.flow_field = FlowField(config_field, config_cuda, device_id) + + center: Tuple[float, float, float] = (30 * L0, (NY - 1) / 2 + 2 * L0, 0) + self.flow_field.add_sensor(center, L0 / 4) + center: Tuple[float, float, float] = (30 * L0, (NY - 1) / 2, 0) + self.flow_field.add_sensor(center, L0 / 4) + center: Tuple[float, float, float] = (30 * L0, (NY - 1) / 2 - 2 * L0, 0) + self.flow_field.add_sensor(center, L0 / 4) + center: Tuple[float, float, float] = (19 * L0, (NY - 1) / 2, 0) + self.flow_field.add_cylinder(center, L0 / 2) + center: Tuple[float, float, float] = (20.3 * L0, (NY - 1) / 2 + 0.75 * L0, 0) + self.flow_field.add_cylinder(center, L0 / 2) + center: Tuple[float, float, float] = (20.3 * L0, (NY - 1) / 2 - 0.75 * L0, 0) + self.flow_field.add_cylinder(center, L0 / 2) + self.flow_field.run(int(4*NX/U0), np.zeros(6, dtype=DATA_TYPE)) + self.flow_field.get_ddf() + self.flow_field.save_ddf() + + for i in range(FIFO_LEN): + self.flow_field.run(SAMPLE_INTERVAL, np.zeros(6, dtype=DATA_TYPE)) + self.fifo_states.append(self.flow_field.obs.copy()[0:12]) + + temp_states = np.array(self.fifo_states) + self.force_norm_fact = 6 * np.max(np.abs(temp_states[:, 6:12])) + + for i in range(6): + self.sens_deviation[i] = np.mean(temp_states[:, i]) + self.sens_norm_fact[i] = 5 * np.max(np.abs(temp_states[:, i] - self.sens_deviation[i])) + + self.flow_field.apply_ddf() + + for i in range(FIFO_LEN): + self.flow_field.run(SAMPLE_INTERVAL, np.array([0.0, 0.0, 0.0, 0.0, -1*U0, 1*U0], dtype=DATA_TYPE)) + self.fifo_states.append(self.flow_field.obs.copy()[0:12]) + + self.save_states = self.fifo_states.copy() + self.flow_field.get_ddf() + self.flow_field.save_ddf() + + def step(self, action): + assert self.action_space.contains(action), "%r (%s) invalid" % ( + action, + type(action), + ) + + # barrier = threading.Barrier(2) + result_queue = queue.Queue() + + def run_flow_field(action): + self.flow_field.context.push() + U0 = config_field.velocity + try: + temp = np.zeros(6, dtype=DATA_TYPE) + temp[3:6] = np.array((action*8+[0,-2,2])*U0, dtype=DATA_TYPE) + self.flow_field.run(SAMPLE_INTERVAL, temp) + finally: + self.flow_field.context.pop() + # barrier.wait() + self.fifo_states.append(self.flow_field.obs.copy()[0:12]) + + def proc_data(): + states = np.array(self.fifo_states) + forces = states[-1, 6:12] / self.force_norm_fact + cd = forces[0] + forces[2] + forces[4] + cl = forces[1] + forces[3] + forces[5] + sens = (states[-1, 0:6] - self.sens_deviation) / self.sens_norm_fact + + similarities = 0.0 + + def calc_lag(target, state): + target_mean = np.mean(target) + state_mean = np.mean(state) + + correlation = np.correlate(target - target_mean, state - state_mean, "full") + lags = np.arange(-len(target) + 1, len(target)) + max_lag = lags[np.argmax(correlation)] + return max_lag + + def calc_sim(target, state): + + n = len(target) + m = len(state) + + dtw_matrix = np.full((n + 1, m + 1), np.inf) + dtw_matrix[0, 0] = 0 + + for i in range(1, n + 1): + for j in range(1, m + 1): + cost = abs(target[i - 1] - state[j - 1]) + last_min = min(dtw_matrix[i - 1, j], + dtw_matrix[i, j - 1], + dtw_matrix[i - 1, j - 1]) + dtw_matrix[i, j] = cost + last_min + + return 1 - (dtw_matrix[n, m] / len(target)) + + def gen_target_states_at(t, harmonics): + t = np.asarray(t) + D = len(harmonics) + result = np.zeros((t.size, D), dtype=np.float32) + for d, h in enumerate(harmonics): + val = np.full(t.shape, h['dc'], dtype=np.float32) + for amp, freq, phase in zip(h['amps'], h['freqs'], h['phases']): + val += amp * np.cos(2 * np.pi * freq * t + phase) + result[:, d] = val + if result.shape[0] == 1: + return result[0] + return result + + id_sens = 1 + target_seq = self.target_states[CONV_LEN:2*CONV_LEN, id_sens+2] + state_seq = states[-CONV_LEN:, id_sens] + lag = calc_lag(target_seq, state_seq) + + for i in range(0, 6): + target_seq = np.roll(self.target_states[:, i+2], -lag)[CONV_LEN:2*CONV_LEN] + state_seq = states[-CONV_LEN:, i] + similarities += calc_sim(target_seq, state_seq) / 6 + + target_states = gen_target_states_at(self.current_step, self.target_harmonics) + target_cd = target_states[0] / self.force_norm_fact + target_cl = target_states[1] / self.force_norm_fact + + self.reward_cd = np.exp(-np.abs((cd-target_cd) * 10)) + self.reward_cl = np.exp(-np.abs((cl-target_cl) * 10)) + self.reward_sim = np.exp(-10*np.abs(similarities - 1)) + reward = np.minimum(0.3 * self.reward_cd + 0.3 * self.reward_cl + 0.4 * self.reward_sim, 1.0) + result_queue.put((np.hstack([forces, sens, target_cd, target_cl]), reward)) + + run_flow_field(action) + proc_data() + observation, reward = result_queue.get() + + truncated = bool(np.any(observation > 1) or np.any(observation < -1)) + observation = np.clip(observation, -1, 1) + self.current_step += 1 + # done = self.current_step >= MAX_STEPS + done = False + return observation, float(reward), done, truncated, {} + + def reset(self, seed=None): + self.flow_field.restore_ddf() + self.flow_field.apply_ddf() + self.fifo_states = self.save_states.copy() + self.current_step = 0 + return np.zeros(S_DIM, dtype=np.float32), {} + + def render(self, mode="human"): + NX = self.flow_field.FIELD_SHAPE[0] + NY = self.flow_field.FIELD_SHAPE[1] + self.flow_field.get_ddf() + ddf_plot = self.flow_field.ddf.copy().reshape((9, NY, NX)).transpose(2, 1, 0) + ux = (ddf_plot[:, :, 1] + ddf_plot[:, :, 5] + ddf_plot[:, :, 8] - ddf_plot[:, :, 3] - ddf_plot[:, :, 6] - ddf_plot[:, :, 7]) / U0 + uy = (ddf_plot[:, :, 2] + ddf_plot[:, :, 5] + ddf_plot[:, :, 6] - ddf_plot[:, :, 4] - ddf_plot[:, :, 7] - ddf_plot[:, :, 8]) / U0 + speed = np.sqrt(ux**2 + uy**2) + plt.figure(figsize=(10, 5)) + plt.imshow(speed.T, origin='lower', cmap='viridis', extent=[0, NX, 0, NY]) + plt.colorbar(label='Speed') + plt.title('Scalar Velocity Field') + plt.xlabel('X') + plt.ylabel('Y') + plt.tight_layout() + plt.show() + + def save_field(self, filename): + NX = self.flow_field.FIELD_SHAPE[0] + NY = self.flow_field.FIELD_SHAPE[1] + self.flow_field.get_ddf() + ddf_plot = self.flow_field.ddf.copy().reshape((9, NY, NX)).transpose(2, 1, 0) + flag_plot = self.flow_field.flag.copy().reshape((NY, NX)).transpose(1, 0) + ux = (ddf_plot[:, :, 1] + ddf_plot[:, :, 5] + ddf_plot[:, :, 8] - ddf_plot[:, :, 3] - ddf_plot[:, :, 6] - ddf_plot[:, :, 7]) / U0 + uy = (ddf_plot[:, :, 2] + ddf_plot[:, :, 5] + ddf_plot[:, :, 6] - ddf_plot[:, :, 4] - ddf_plot[:, :, 7] - ddf_plot[:, :, 8]) / U0 + with open(os.path.join(parent_dir, "output", filename), "w") as f: + f.write("Title= \"LBM 2D\"\r\n") + f.write("VARIABLES= \"X\",\"Y\",\"flag\",\"U\",\"V\",\r\n") + f.write(f"ZONE T= \"BOX\",I= {NX},J= {NY},F=POINT\r\n") + for j in range(NY): + for i in range(NX): + f.write(f"{i},{j},{flag_plot[i, j]},{ux[i, j]},{uy[i, j]}\r\n") + + def average_field(self, mode=["add", "save", "clear"], filename="average_field.dat"): + NX = self.flow_field.FIELD_SHAPE[0] + NY = self.flow_field.FIELD_SHAPE[1] + self.flow_field.get_ddf() + ddf_new = self.flow_field.ddf.copy().reshape((9, NY, NX)).transpose(2, 1, 0) + if "add" in mode: + self.ddf_ave = self.ddf_ave + ddf_new + self.ddf_ave_cont += 1 + if "save" in mode: + if self.ddf_ave_cont == 0: + raise ValueError("No data to save. Please run 'add' mode first.") + ux = (self.ddf_ave[:, :, 1] + self.ddf_ave[:, :, 5] + self.ddf_ave[:, :, 8] - self.ddf_ave[:, :, 3] - self.ddf_ave[:, :, 6] - self.ddf_ave[:, :, 7]) / U0 / self.ddf_ave_cont + uy = (self.ddf_ave[:, :, 2] + self.ddf_ave[:, :, 5] + self.ddf_ave[:, :, 6] - self.ddf_ave[:, :, 4] - self.ddf_ave[:, :, 7] - self.ddf_ave[:, :, 8]) / U0 / self.ddf_ave_cont + flag_plot = self.flow_field.flag.copy().reshape((NY, NX)).transpose(1, 0) + with open(os.path.join(parent_dir, "output", filename), "w") as f: + f.write("Title= \"LBM 2D\"\r\n") + f.write("VARIABLES= \"X\",\"Y\",\"flag\",\"U\",\"V\",\r\n") + f.write(f"ZONE T= \"BOX\",I= {NX},J= {NY},F=POINT\r\n") + for j in range(NY): + for i in range(NX): + f.write(f"{i},{j},{flag_plot[i, j]},{ux[i, j]},{uy[i, j]}\r\n") + print(f"Average field amount: {self.ddf_ave_cont}") + if "clear" in mode: + self.ddf_ave = np.zeros((NX, NY, 9), dtype=DATA_TYPE) + self.ddf_ave_cont = 0 + + def close(self): + self.flow_field.__del__() \ No newline at end of file diff --git a/src/drl_pinball/legacy_env/legacy_env_imit_target.py b/src/drl_pinball/legacy_env/legacy_env_imit_target.py new file mode 100644 index 0000000..507de08 --- /dev/null +++ b/src/drl_pinball/legacy_env/legacy_env_imit_target.py @@ -0,0 +1,207 @@ +# 这个是imit圆柱的env,用于产生d1a3o14_250525_imit系列模型的目标流场 +# 跟随模型,要匹配圆柱直径和采样频率 +# 模型名中L代表目标直径,S代表SAMPLE_INTERVAL +import gymnasium as gym +import numpy as np +from gymnasium import spaces +import ctypes +from collections import deque +from typing import Tuple +import sys +import os +import matplotlib.pyplot as plt +import queue + +os.environ["OMP_NUM_THREADS"] = "1" +os.environ["MKL_NUM_THREADS"] = "1" + +current_dir = os.path.dirname(os.path.abspath("__file__")) +parent_dir = os.path.abspath(os.path.join(current_dir, os.pardir)) +sys.path.append(parent_dir) +from CelerisLab import FlowField +from CelerisLab import utils + +config_cuda = utils.load_cuda_config( + os.path.join(parent_dir, "configs", "config_cuda.json") +) +config_field = utils.load_flow_field_config( + os.path.join(parent_dir, "configs", "config_flowfield.json") +) + +S_DIM, A_DIM = 14, 3 +U0 = config_field.velocity +T0 = 1000 +SAMPLE_INTERVAL = 600 # 这里会随着圆柱尺寸和粘性变动,在模型名中体现 +FIFO_LEN = 150 +CONV_LEN = 36 +MAX_STEPS = 500 +if config_field.data_type == "FP32": + DATA_TYPE = np.float32 +else: + raise ValueError(f"Unsupported data type {config_field.data_type}.") + + +class CustomEnv(gym.Env): + """Custom Environment that follows gym interface.""" + + metadata = {"render_modes": ["human"], "render_fps": T0 / SAMPLE_INTERVAL} + + def __init__(self, device_id=0): + super().__init__() + self.action_space = spaces.Box(low=-1, high=1, shape=(A_DIM,), dtype=DATA_TYPE) + self.observation_space = spaces.Box( + low=-1, high=1, shape=(S_DIM,), dtype=DATA_TYPE + ) + self.fifo_states = deque(maxlen=FIFO_LEN) + self.target_states = np.empty((0, 8), dtype=DATA_TYPE) + self.force_norm_fact = 1.0 + self.sens_norm_fact = np.ones(6, dtype=DATA_TYPE) + self.sens_deviation = np.zeros(6, dtype=DATA_TYPE) + self.reward_cd = 0.0 + self.reward_cl = 0.0 + self.reward_sim = 0.0 + self.current_step = 0 + + self.flow_field = FlowField(config_field, config_cuda, device_id) + L0 = 20 + U0 = config_field.velocity + NX = self.flow_field.FIELD_SHAPE[0] + NY = self.flow_field.FIELD_SHAPE[1] + + self.ddf_ave = np.zeros((NX, NY, 9), dtype=DATA_TYPE) + self.ddf_ave_cont = 0 + + center: Tuple[float, float, float] = (20 * L0, (NY - 1) / 2, 0) + self.flow_field.add_cylinder(center, 1*L0) # 这里会随着圆柱尺寸变动,在模型名中体现 + center: Tuple[float, float, float] = (30 * L0, (NY - 1) / 2 + 2 * L0, 0) + self.flow_field.add_sensor(center, L0 / 4) + center: Tuple[float, float, float] = (30 * L0, (NY - 1) / 2, 0) + self.flow_field.add_sensor(center, L0 / 4) + center: Tuple[float, float, float] = (30 * L0, (NY - 1) / 2 - 2 * L0, 0) + self.flow_field.add_sensor(center, L0 / 4) + self.flow_field.run(int(4*NX/U0), np.zeros(4, dtype=DATA_TYPE)) + + for i in range(FIFO_LEN): + self.flow_field.run(SAMPLE_INTERVAL, np.zeros(4, dtype=DATA_TYPE)) + new_state = self.flow_field.obs.copy()[0:8] + self.target_states = np.vstack((self.target_states, new_state)) + + def analyze_harmonics(states, n_harmonics): + N, D = states.shape + result = [] + for d in range(D): + y = states[:, d] + fft_coef = np.fft.rfft(y) + freqs = np.fft.rfftfreq(N, d=1) + amps = 2 * np.abs(fft_coef) / N + phases = np.angle(fft_coef) + idx = np.argsort(amps[1:])[::-1][:n_harmonics] + 1 + harmonics = { + 'dc': np.real(fft_coef[0]) / N, + 'amps': amps[idx], + 'freqs': freqs[idx], + 'phases': phases[idx] + } + result.append(harmonics) + return result + + self.target_harmonics = analyze_harmonics(self.target_states, n_harmonics=5) + + self.flow_field.get_ddf() + self.flow_field.save_ddf() + + def step(self, action): + assert self.action_space.contains(action), "%r (%s) invalid" % ( + action, + type(action), + ) + + # barrier = threading.Barrier(2) + result_queue = queue.Queue() + + def run_flow_field(action): + self.flow_field.context.push() + U0 = config_field.velocity + try: + temp = np.zeros(4, dtype=DATA_TYPE) + self.flow_field.run(SAMPLE_INTERVAL, temp) + finally: + self.flow_field.context.pop() + # barrier.wait() + + run_flow_field(action) + + truncated = False + observation = np.zeros(14, dtype=DATA_TYPE) + self.current_step += 1 + # done = self.current_step >= MAX_STEPS + done = False + return observation, float(0), done, truncated, {} + + def reset(self, seed=None): + self.flow_field.restore_ddf() + self.flow_field.apply_ddf() + self.current_step = 0 + return np.zeros(S_DIM, dtype=np.float32), {} + + def render(self, mode="human"): + NX = self.flow_field.FIELD_SHAPE[0] + NY = self.flow_field.FIELD_SHAPE[1] + self.flow_field.get_ddf() + ddf_plot = self.flow_field.ddf.copy().reshape((9, NY, NX)).transpose(2, 1, 0) + ux = (ddf_plot[:, :, 1] + ddf_plot[:, :, 5] + ddf_plot[:, :, 8] - ddf_plot[:, :, 3] - ddf_plot[:, :, 6] - ddf_plot[:, :, 7]) / U0 + uy = (ddf_plot[:, :, 2] + ddf_plot[:, :, 5] + ddf_plot[:, :, 6] - ddf_plot[:, :, 4] - ddf_plot[:, :, 7] - ddf_plot[:, :, 8]) / U0 + speed = np.sqrt(ux**2 + uy**2) + plt.figure(figsize=(10, 5)) + plt.imshow(speed.T, origin='lower', cmap='viridis', extent=[0, NX, 0, NY]) + plt.colorbar(label='Speed') + plt.title('Scalar Velocity Field') + plt.xlabel('X') + plt.ylabel('Y') + plt.tight_layout() + plt.show() + + def save_field(self, filename): + NX = self.flow_field.FIELD_SHAPE[0] + NY = self.flow_field.FIELD_SHAPE[1] + self.flow_field.get_ddf() + ddf_plot = self.flow_field.ddf.copy().reshape((9, NY, NX)).transpose(2, 1, 0) + flag_plot = self.flow_field.flag.copy().reshape((NY, NX)).transpose(1, 0) + ux = (ddf_plot[:, :, 1] + ddf_plot[:, :, 5] + ddf_plot[:, :, 8] - ddf_plot[:, :, 3] - ddf_plot[:, :, 6] - ddf_plot[:, :, 7]) / U0 + uy = (ddf_plot[:, :, 2] + ddf_plot[:, :, 5] + ddf_plot[:, :, 6] - ddf_plot[:, :, 4] - ddf_plot[:, :, 7] - ddf_plot[:, :, 8]) / U0 + with open(os.path.join(parent_dir, "output", filename), "w") as f: + f.write("Title= \"LBM 2D\"\r\n") + f.write("VARIABLES= \"X\",\"Y\",\"flag\",\"U\",\"V\",\r\n") + f.write(f"ZONE T= \"BOX\",I= {NX},J= {NY},F=POINT\r\n") + for j in range(NY): + for i in range(NX): + f.write(f"{i},{j},{flag_plot[i, j]},{ux[i, j]},{uy[i, j]}\r\n") + + def average_field(self, mode=["add", "save", "clear"], filename="average_field.dat"): + NX = self.flow_field.FIELD_SHAPE[0] + NY = self.flow_field.FIELD_SHAPE[1] + self.flow_field.get_ddf() + ddf_new = self.flow_field.ddf.copy().reshape((9, NY, NX)).transpose(2, 1, 0) + if "add" in mode: + self.ddf_ave = self.ddf_ave + ddf_new + self.ddf_ave_cont += 1 + if "save" in mode: + if self.ddf_ave_cont == 0: + raise ValueError("No data to save. Please run 'add' mode first.") + ux = (self.ddf_ave[:, :, 1] + self.ddf_ave[:, :, 5] + self.ddf_ave[:, :, 8] - self.ddf_ave[:, :, 3] - self.ddf_ave[:, :, 6] - self.ddf_ave[:, :, 7]) / U0 / self.ddf_ave_cont + uy = (self.ddf_ave[:, :, 2] + self.ddf_ave[:, :, 5] + self.ddf_ave[:, :, 6] - self.ddf_ave[:, :, 4] - self.ddf_ave[:, :, 7] - self.ddf_ave[:, :, 8]) / U0 / self.ddf_ave_cont + flag_plot = self.flow_field.flag.copy().reshape((NY, NX)).transpose(1, 0) + with open(os.path.join(parent_dir, "output", filename), "w") as f: + f.write("Title= \"LBM 2D\"\r\n") + f.write("VARIABLES= \"X\",\"Y\",\"flag\",\"U\",\"V\",\r\n") + f.write(f"ZONE T= \"BOX\",I= {NX},J= {NY},F=POINT\r\n") + for j in range(NY): + for i in range(NX): + f.write(f"{i},{j},{flag_plot[i, j]},{ux[i, j]},{uy[i, j]}\r\n") + print(f"Average field amount: {self.ddf_ave_cont}") + if "clear" in mode: + self.ddf_ave = np.zeros((NX, NY, 9), dtype=DATA_TYPE) + self.ddf_ave_cont = 0 + + def close(self): + self.flow_field.__del__() \ No newline at end of file diff --git a/src/drl_pinball/legacy_env/legacy_env_karman_cloak_standard.py b/src/drl_pinball/legacy_env/legacy_env_karman_cloak_standard.py new file mode 100644 index 0000000..dedb1a5 --- /dev/null +++ b/src/drl_pinball/legacy_env/legacy_env_karman_cloak_standard.py @@ -0,0 +1,270 @@ +# 这个是Karman_cloak_standard的env,用于训练和评估d1a3o12_re系列模型和250326模型 +# 上游一个2D扰流圆柱,目标是pinball后流场跟无pinball一致 +import gymnasium as gym +import numpy as np +from gymnasium import spaces +import ctypes +from collections import deque +from typing import Tuple +import sys +import os +import matplotlib.pyplot as plt +import queue + +os.environ["OMP_NUM_THREADS"] = "1" +os.environ["MKL_NUM_THREADS"] = "1" + +current_dir = os.path.dirname(os.path.abspath("__file__")) +parent_dir = os.path.abspath(os.path.join(current_dir, os.pardir)) +sys.path.append(parent_dir) +from CelerisLab import FlowField +from CelerisLab import utils + +config_cuda = utils.load_cuda_config( + os.path.join(parent_dir, "configs", "config_cuda.json") +) +config_field = utils.load_flow_field_config( + os.path.join(parent_dir, "configs", "config_flowfield.json") +) + +S_DIM, A_DIM = 12, 3 +U0 = config_field.velocity +T0 = 1000 +SAMPLE_INTERVAL = 800 +FIFO_LEN = 150 +CONV_LEN = 30 +MAX_STEPS = 500 +if config_field.data_type == "FP32": + DATA_TYPE = np.float32 +else: + raise ValueError(f"Unsupported data type {config_field.data_type}.") + + +class CustomEnv(gym.Env): + """Custom Environment that follows gym interface.""" + + metadata = {"render_modes": ["human"], "render_fps": T0 / SAMPLE_INTERVAL} + + def __init__(self, device_id=0): + super().__init__() + self.action_space = spaces.Box(low=-1, high=1, shape=(A_DIM,), dtype=DATA_TYPE) + self.observation_space = spaces.Box( + low=-1, high=1, shape=(S_DIM,), dtype=DATA_TYPE + ) + self.fifo_states = deque(maxlen=FIFO_LEN) + self.target_states = np.empty((0, 6), dtype=DATA_TYPE) + self.force_norm_fact = 1.0 + self.sens_norm_fact = np.ones(6, dtype=DATA_TYPE) + self.sens_deviation = np.zeros(6, dtype=DATA_TYPE) + self.reward_cd = 0.0 + self.reward_cl = 0.0 + self.reward_sim = 0.0 + self.current_step = 0 + + self.flow_field = FlowField(config_field, config_cuda, device_id) + L0 = 20 + U0 = config_field.velocity + NX = self.flow_field.FIELD_SHAPE[0] + NY = self.flow_field.FIELD_SHAPE[1] + + self.ddf_ave = np.zeros((NX, NY, 9), dtype=DATA_TYPE) + self.ddf_ave_cont = 0 + + center: Tuple[float, float, float] = (10 * L0, (NY - 1) / 2, 0) + self.flow_field.add_cylinder(center, L0) + center: Tuple[float, float, float] = (40 * L0, (NY - 1) / 2 + 2 * L0, 0) + self.flow_field.add_sensor(center, L0 / 4) + center: Tuple[float, float, float] = (40 * L0, (NY - 1) / 2, 0) + self.flow_field.add_sensor(center, L0 / 4) + center: Tuple[float, float, float] = (40 * L0, (NY - 1) / 2 - 2 * L0, 0) + self.flow_field.add_sensor(center, L0 / 4) + self.flow_field.run(int(4*NX/U0), np.zeros(4, dtype=DATA_TYPE)) + + for i in range(FIFO_LEN): + self.flow_field.run(SAMPLE_INTERVAL, np.zeros(4, dtype=DATA_TYPE)) + new_state = self.flow_field.obs.copy()[2:8] + self.target_states = np.vstack((self.target_states, new_state)) + + # self.flow_field.apply_ddf() + center: Tuple[float, float, float] = (30 * L0, (NY - 1) / 2, 0) + self.flow_field.add_cylinder(center, L0 / 2) + center: Tuple[float, float, float] = (31.3 * L0, (NY - 1) / 2 + 0.75 * L0, 0) + self.flow_field.add_cylinder(center, L0 / 2) + center: Tuple[float, float, float] = (31.3 * L0, (NY - 1) / 2 - 0.75 * L0, 0) + self.flow_field.add_cylinder(center, L0 / 2) + self.flow_field.run(int(4*NX/U0), np.zeros(7, dtype=DATA_TYPE)) + self.flow_field.get_ddf() + self.flow_field.save_ddf() + + for i in range(FIFO_LEN): + self.flow_field.run(SAMPLE_INTERVAL, np.zeros(7, dtype=DATA_TYPE)) + self.fifo_states.append(self.flow_field.obs.copy()[2:14]) + + temp_states = np.array(self.fifo_states) + self.force_norm_fact = 6 * np.max(np.abs(temp_states[:, 6:12])) + + for i in range(6): + self.sens_deviation[i] = np.mean(temp_states[:, i]) + self.sens_norm_fact[i] = 5 * np.max(np.abs(temp_states[:, i] - self.sens_deviation[i])) + + self.flow_field.apply_ddf() + + for i in range(FIFO_LEN): + self.flow_field.run(SAMPLE_INTERVAL, np.array([0.0, 0.0, 0.0, 0.0, 0.0, -4*U0, 4*U0], dtype=DATA_TYPE)) + self.fifo_states.append(self.flow_field.obs.copy()[2:14]) + + self.save_states = self.fifo_states.copy() + self.flow_field.apply_ddf() + + def step(self, action): + assert self.action_space.contains(action), "%r (%s) invalid" % ( + action, + type(action), + ) + + # barrier = threading.Barrier(2) + result_queue = queue.Queue() + + def run_flow_field(action): + self.flow_field.context.push() + U0 = config_field.velocity + try: + temp = np.zeros(7, dtype=DATA_TYPE) + temp[4:7] = np.array((action*8+[0,-4,4])*U0, dtype=DATA_TYPE) + self.flow_field.run(SAMPLE_INTERVAL, temp) + finally: + self.flow_field.context.pop() + # barrier.wait() + self.fifo_states.append(self.flow_field.obs.copy()[2:14]) + + def proc_data(): + states = np.array(self.fifo_states) + forces = states[-1, 6:12] / self.force_norm_fact + cd = (forces[0] + forces[2] + forces[4]) / 3 + cl = (forces[1] + forces[3] + forces[5]) / 3 + sens = (states[-1, 0:6] - self.sens_deviation) / self.sens_norm_fact + + similarities = 0.0 + + def calc_lag(target, state): + target_mean = np.mean(target) + state_mean = np.mean(state) + + correlation = np.correlate(target - target_mean, state - state_mean, "full") + lags = np.arange(-len(target) + 1, len(target)) + max_lag = lags[np.argmax(correlation)] + return max_lag + + def calc_sim(target, state): + + n = len(target) + m = len(state) + + dtw_matrix = np.full((n + 1, m + 1), np.inf) + dtw_matrix[0, 0] = 0 + + for i in range(1, n + 1): + for j in range(1, m + 1): + cost = abs(target[i - 1] - state[j - 1]) + last_min = min(dtw_matrix[i - 1, j], + dtw_matrix[i, j - 1], + dtw_matrix[i - 1, j - 1]) + dtw_matrix[i, j] = cost + last_min + + return 1 - (dtw_matrix[n, m] / len(target)) + + id_sens = 1 + target_seq = self.target_states[CONV_LEN:2*CONV_LEN, id_sens] + state_seq = states[-CONV_LEN:, id_sens] + lag = calc_lag(target_seq, state_seq) + + for i in range(0, 6): + target_seq = np.roll(self.target_states[:, i], -lag)[CONV_LEN:2*CONV_LEN] + state_seq = states[-CONV_LEN:, i] + similarities += calc_sim(target_seq, state_seq) / 6 + + self.reward_cd = np.exp(-np.abs(cd * 20)) + self.reward_cl = np.exp(-np.abs(cl * 80)) + self.reward_sim = np.exp(-10*np.abs(similarities - 1)) + reward = np.minimum(0.3 * self.reward_cd + 0.4 * self.reward_cl + 0.3 * self.reward_sim, 1.0) + result_queue.put((np.hstack([forces, sens]), reward)) + + run_flow_field(action) + proc_data() + observation, reward = result_queue.get() + + truncated = bool(np.any(observation > 1) or np.any(observation < -1)) + observation = np.clip(observation, -1, 1) + self.current_step += 1 + # done = self.current_step >= MAX_STEPS + done = False + return observation, float(reward), done, truncated, {} + + def reset(self, seed=None): + self.flow_field.restore_ddf() + self.flow_field.apply_ddf() + self.fifo_states = self.save_states.copy() + self.current_step = 0 + return np.zeros(S_DIM, dtype=np.float32), {} + + def render(self, mode="human"): + NX = self.flow_field.FIELD_SHAPE[0] + NY = self.flow_field.FIELD_SHAPE[1] + self.flow_field.get_ddf() + ddf_plot = self.flow_field.ddf.copy().reshape((9, NY, NX)).transpose(2, 1, 0) + ux = (ddf_plot[:, :, 1] + ddf_plot[:, :, 5] + ddf_plot[:, :, 8] - ddf_plot[:, :, 3] - ddf_plot[:, :, 6] - ddf_plot[:, :, 7]) / U0 + uy = (ddf_plot[:, :, 2] + ddf_plot[:, :, 5] + ddf_plot[:, :, 6] - ddf_plot[:, :, 4] - ddf_plot[:, :, 7] - ddf_plot[:, :, 8]) / U0 + speed = np.sqrt(ux**2 + uy**2) + plt.figure(figsize=(10, 5)) + plt.imshow(speed.T, origin='lower', cmap='viridis', extent=[0, NX, 0, NY]) + plt.colorbar(label='Speed') + plt.title('Scalar Velocity Field') + plt.xlabel('X') + plt.ylabel('Y') + plt.tight_layout() + plt.show() + + def save_field(self, filename): + NX = self.flow_field.FIELD_SHAPE[0] + NY = self.flow_field.FIELD_SHAPE[1] + self.flow_field.get_ddf() + ddf_plot = self.flow_field.ddf.copy().reshape((9, NY, NX)).transpose(2, 1, 0) + flag_plot = self.flow_field.flag.copy().reshape((NY, NX)).transpose(1, 0) + ux = (ddf_plot[:, :, 1] + ddf_plot[:, :, 5] + ddf_plot[:, :, 8] - ddf_plot[:, :, 3] - ddf_plot[:, :, 6] - ddf_plot[:, :, 7]) / U0 + uy = (ddf_plot[:, :, 2] + ddf_plot[:, :, 5] + ddf_plot[:, :, 6] - ddf_plot[:, :, 4] - ddf_plot[:, :, 7] - ddf_plot[:, :, 8]) / U0 + with open(os.path.join(parent_dir, "output", filename), "w") as f: + f.write("Title= \"LBM 2D\"\r\n") + f.write("VARIABLES= \"X\",\"Y\",\"flag\",\"U\",\"V\",\r\n") + f.write(f"ZONE T= \"BOX\",I= {NX},J= {NY},F=POINT\r\n") + for j in range(NY): + for i in range(NX): + f.write(f"{i},{j},{flag_plot[i, j]},{ux[i, j]},{uy[i, j]}\r\n") + + def average_field(self, mode=["add", "save", "clear"], filename="average_field.dat"): + NX = self.flow_field.FIELD_SHAPE[0] + NY = self.flow_field.FIELD_SHAPE[1] + self.flow_field.get_ddf() + ddf_new = self.flow_field.ddf.copy().reshape((9, NY, NX)).transpose(2, 1, 0) + if "add" in mode: + self.ddf_ave = self.ddf_ave + ddf_new + self.ddf_ave_cont += 1 + if "save" in mode: + if self.ddf_ave_cont == 0: + raise ValueError("No data to save. Please run 'add' mode first.") + ux = (self.ddf_ave[:, :, 1] + self.ddf_ave[:, :, 5] + self.ddf_ave[:, :, 8] - self.ddf_ave[:, :, 3] - self.ddf_ave[:, :, 6] - self.ddf_ave[:, :, 7]) / U0 / self.ddf_ave_cont + uy = (self.ddf_ave[:, :, 2] + self.ddf_ave[:, :, 5] + self.ddf_ave[:, :, 6] - self.ddf_ave[:, :, 4] - self.ddf_ave[:, :, 7] - self.ddf_ave[:, :, 8]) / U0 / self.ddf_ave_cont + flag_plot = self.flow_field.flag.copy().reshape((NY, NX)).transpose(1, 0) + with open(os.path.join(parent_dir, "output", filename), "w") as f: + f.write("Title= \"LBM 2D\"\r\n") + f.write("VARIABLES= \"X\",\"Y\",\"flag\",\"U\",\"V\",\r\n") + f.write(f"ZONE T= \"BOX\",I= {NX},J= {NY},F=POINT\r\n") + for j in range(NY): + for i in range(NX): + f.write(f"{i},{j},{flag_plot[i, j]},{ux[i, j]},{uy[i, j]}\r\n") + print(f"Average field amount: {self.ddf_ave_cont}") + if "clear" in mode: + self.ddf_ave = np.zeros((NX, NY, 9), dtype=DATA_TYPE) + self.ddf_ave_cont = 0 + + def close(self): + self.flow_field.__del__() \ No newline at end of file diff --git a/src/drl_pinball/legacy_env/legacy_env_reduce_obs.py b/src/drl_pinball/legacy_env/legacy_env_reduce_obs.py new file mode 100644 index 0000000..c858325 --- /dev/null +++ b/src/drl_pinball/legacy_env/legacy_env_reduce_obs.py @@ -0,0 +1,245 @@ +# 这个是reduce_obs的env,用于训练和评估d1a3o12_250421系列模型 +# 上游扰流圆柱,场景与Karman_cloak_standard一致 +# obs从12逐渐减少至2,观察模型是否能够适应,具体观察量在模型名中体现 +import gymnasium as gym +import numpy as np +from gymnasium import spaces +import ctypes +from collections import deque +from typing import Tuple +import sys +import os +import matplotlib.pyplot as plt +import queue + +os.environ["OMP_NUM_THREADS"] = "1" +os.environ["MKL_NUM_THREADS"] = "1" + +current_dir = os.path.dirname(os.path.abspath("__file__")) +parent_dir = os.path.abspath(os.path.join(current_dir, os.pardir)) +sys.path.append(parent_dir) +from CelerisLab import FlowField +from CelerisLab import utils + +config_cuda = utils.load_cuda_config( + os.path.join(parent_dir, "configs", "config_cuda.json") +) +config_field = utils.load_flow_field_config( + os.path.join(parent_dir, "configs", "config_flowfield.json") +) + +S_DIM, A_DIM = 3, 3 # 这里会随着obs数量变动,在模型名中体现 +U0 = config_field.velocity +T0 = 1000 +SAMPLE_INTERVAL = 800 +FIFO_LEN = 150 +CONV_LEN = 36 +MAX_STEPS = 500 +if config_field.data_type == "FP32": + DATA_TYPE = np.float32 +else: + raise ValueError(f"Unsupported data type {config_field.data_type}.") + + +class CustomEnv(gym.Env): + """Custom Environment that follows gym interface.""" + + metadata = {"render_modes": ["human"], "render_fps": T0 / SAMPLE_INTERVAL} + + def __init__(self, device_id=0): + super().__init__() + self.action_space = spaces.Box(low=-1, high=1, shape=(A_DIM,), dtype=DATA_TYPE) + self.observation_space = spaces.Box( + low=-1, high=1, shape=(S_DIM,), dtype=DATA_TYPE + ) + self.fifo_states = deque(maxlen=FIFO_LEN) + self.target_states = np.empty((0, 6), dtype=DATA_TYPE) + self.force_norm_fact = 1.0 + self.torque_norm_fact = 1.0 + self.sens_norm_fact = np.ones(6, dtype=DATA_TYPE) + self.sens_deviation = np.zeros(6, dtype=DATA_TYPE) + self.reward_cd = 0.0 + self.reward_cl = 0.0 + self.reward_sim = 0.0 + self.current_step = 0 + + self.flow_field = FlowField(config_field, config_cuda, device_id) + L0 = 20 + U0 = config_field.velocity + NX = self.flow_field.FIELD_SHAPE[0] + NY = self.flow_field.FIELD_SHAPE[1] + center: Tuple[float, float, float] = (10 * L0, (NY - 1) / 2, 0) + self.flow_field.add_cylinder(center, L0) + center: Tuple[float, float, float] = (40 * L0, (NY - 1) / 2 + 2 * L0, 0) + self.flow_field.add_sensor(center, L0 / 4) + center: Tuple[float, float, float] = (40 * L0, (NY - 1) / 2, 0) + self.flow_field.add_sensor(center, L0 / 4) + center: Tuple[float, float, float] = (40 * L0, (NY - 1) / 2 - 2 * L0, 0) + self.flow_field.add_sensor(center, L0 / 4) + self.flow_field.run(int(4*NX/U0), np.zeros(4, dtype=DATA_TYPE)) + + for i in range(FIFO_LEN): + self.flow_field.run(SAMPLE_INTERVAL, np.zeros(4, dtype=DATA_TYPE)) + new_state = self.flow_field.obs.copy()[2:8] + self.target_states = np.vstack((self.target_states, new_state)) + + # self.flow_field.apply_ddf() + center: Tuple[float, float, float] = (30 * L0, (NY - 1) / 2, 0) + self.flow_field.add_cylinder(center, L0 / 2) + center: Tuple[float, float, float] = (31.3 * L0, (NY - 1) / 2 + 0.75 * L0, 0) + self.flow_field.add_cylinder(center, L0 / 2) + center: Tuple[float, float, float] = (31.3 * L0, (NY - 1) / 2 - 0.75 * L0, 0) + self.flow_field.add_cylinder(center, L0 / 2) + self.flow_field.run(int(4*NX/U0), np.zeros(7, dtype=DATA_TYPE)) + self.flow_field.get_ddf() + self.flow_field.save_ddf() + + for i in range(FIFO_LEN): + self.flow_field.run(SAMPLE_INTERVAL, np.zeros(7, dtype=DATA_TYPE)) + self.fifo_states.append(self.flow_field.obs.copy()[2:14]) + + temp_states = np.array(self.fifo_states) + self.force_norm_fact = 10 * np.max(np.abs(temp_states[:, 6:12])) + temp_torque = -temp_states[:, 1] - temp_states[:, 2]*np.sqrt(3)/2 + temp_states[:, 3]/2 + temp_states[:, 4]*np.sqrt(3)/2 + temp_states[:, 5]/2 + self.torque_norm_fact = 10 * np.max(np.abs(temp_torque)) + + for i in range(6): + self.sens_deviation[i] = np.mean(temp_states[:, i]) + self.sens_norm_fact[i] = 5 * np.max(np.abs(temp_states[:, i] - self.sens_deviation[i])) + + self.flow_field.apply_ddf() + + for i in range(FIFO_LEN): + self.flow_field.run(SAMPLE_INTERVAL, np.array([0.0, 0.0, 0.0, 0.0, 0.0, -4*U0, 4*U0], dtype=DATA_TYPE)) + self.fifo_states.append(self.flow_field.obs.copy()[2:14]) + + self.save_states = self.fifo_states.copy() + self.flow_field.apply_ddf() + + def step(self, action): + assert self.action_space.contains(action), "%r (%s) invalid" % ( + action, + type(action), + ) + + # barrier = threading.Barrier(2) + result_queue = queue.Queue() + + def run_flow_field(action): + self.flow_field.context.push() + U0 = config_field.velocity + try: + temp = np.zeros(7, dtype=DATA_TYPE) + temp[4:7] = np.array((action*8+[0,-4,4])*U0, dtype=DATA_TYPE) + self.flow_field.run(SAMPLE_INTERVAL, temp) + finally: + self.flow_field.context.pop() + # barrier.wait() + self.fifo_states.append(self.flow_field.obs.copy()[2:14]) + + def proc_data(): + states = np.array(self.fifo_states) + forces = states[-1, 6:12] / self.force_norm_fact + obs_torque = (-states[-1, 1] - states[-1, 2]*np.sqrt(3)/2 + states[-1, 3]/2 + states[-1, 4]*np.sqrt(3)/2 + states[-1, 5]/2) / self.torque_norm_fact + obs_drag = (forces[0] + forces[2] + forces[4]) / 3 + obs_lift = (forces[1] + forces[3] + forces[5]) / 3 + sens = (states[-1, 0:6] - self.sens_deviation) / self.sens_norm_fact + + similarities = 0.0 + + def calc_lag(target, state): + target_mean = np.mean(target) + state_mean = np.mean(state) + + correlation = np.correlate(target - target_mean, state - state_mean, "full") + lags = np.arange(-len(target) + 1, len(target)) + max_lag = lags[np.argmax(correlation)] + return max_lag + + def calc_sim(target, state): + + n = len(target) + m = len(state) + + dtw_matrix = np.full((n + 1, m + 1), np.inf) + dtw_matrix[0, 0] = 0 + + for i in range(1, n + 1): + for j in range(1, m + 1): + cost = abs(target[i - 1] - state[j - 1]) + last_min = min(dtw_matrix[i - 1, j], + dtw_matrix[i, j - 1], + dtw_matrix[i - 1, j - 1]) + dtw_matrix[i, j] = cost + last_min + + return 1 - (dtw_matrix[n, m] / len(target)) + + id_sens = 1 + target_seq = self.target_states[CONV_LEN:2*CONV_LEN, id_sens] + state_seq = states[-CONV_LEN:, id_sens] + lag = calc_lag(target_seq, state_seq) + + for i in range(0, 6): + target_seq = np.roll(self.target_states[:, i], -lag)[CONV_LEN:2*CONV_LEN] + state_seq = states[-CONV_LEN:, i] + similarities += calc_sim(target_seq, state_seq) / 6 + + self.reward_cd = np.exp(-np.abs(obs_drag * 20)) + self.reward_cl = np.exp(-np.abs(obs_lift * 80)) + self.reward_sim = np.exp(-10*np.abs(similarities - 1)) + reward = np.minimum(0.3 * self.reward_cd + 0.3 * self.reward_cl + 0.4 * self.reward_sim, 1.0) + result_queue.put((np.hstack([obs_torque, forces[0:2]]), reward)) # 这里会随着obs数量变动,在模型名中体现 + + run_flow_field(action) + proc_data() + observation, reward = result_queue.get() + + truncated = bool(np.any(observation > 1) or np.any(observation < -1)) + observation = np.clip(observation, -1, 1) + self.current_step += 1 + # done = self.current_step >= MAX_STEPS + done = False + return observation, float(reward), done, truncated, {} + + def reset(self, seed=None): + self.flow_field.restore_ddf() + self.flow_field.apply_ddf() + self.fifo_states = self.save_states.copy() + self.current_step = 0 + return np.zeros(S_DIM, dtype=np.float32), {} + + def render(self, mode="human"): + NX = self.flow_field.FIELD_SHAPE[0] + NY = self.flow_field.FIELD_SHAPE[1] + self.flow_field.get_ddf() + ddf_plot = self.flow_field.ddf.copy().reshape((9, NY, NX)).transpose(2, 1, 0) + ux = (ddf_plot[:, :, 1] + ddf_plot[:, :, 5] + ddf_plot[:, :, 8] - ddf_plot[:, :, 3] - ddf_plot[:, :, 6] - ddf_plot[:, :, 7]) / U0 + uy = (ddf_plot[:, :, 2] + ddf_plot[:, :, 5] + ddf_plot[:, :, 6] - ddf_plot[:, :, 4] - ddf_plot[:, :, 7] - ddf_plot[:, :, 8]) / U0 + speed = np.sqrt(ux**2 + uy**2) + plt.figure(figsize=(10, 5)) + plt.imshow(speed.T, origin='lower', cmap='viridis', extent=[0, NX, 0, NY]) + plt.colorbar(label='Speed') + plt.title('Scalar Velocity Field') + plt.xlabel('X') + plt.ylabel('Y') + plt.tight_layout() + plt.show() + + def save_field(self, filename): + NX = self.flow_field.FIELD_SHAPE[0] + NY = self.flow_field.FIELD_SHAPE[1] + self.flow_field.get_ddf() + ddf_plot = self.flow_field.ddf.copy().reshape((9, NY, NX)).transpose(2, 1, 0) + flag_plot = self.flow_field.flag.copy().reshape((NY, NX)).transpose(1, 0) + ux = (ddf_plot[:, :, 1] + ddf_plot[:, :, 5] + ddf_plot[:, :, 8] - ddf_plot[:, :, 3] - ddf_plot[:, :, 6] - ddf_plot[:, :, 7]) / U0 + uy = (ddf_plot[:, :, 2] + ddf_plot[:, :, 5] + ddf_plot[:, :, 6] - ddf_plot[:, :, 4] - ddf_plot[:, :, 7] - ddf_plot[:, :, 8]) / U0 + with open(os.path.join(parent_dir, "output", filename), "w") as f: + f.write("Title= \"LBM 2D\"\r\n") + f.write("VARIABLES= \"X\",\"Y\",\"flag\",\"U\",\"V\",\r\n") + f.write(f"ZONE T= \"BOX\",I= {NX},J= {NY},F=POINT\r\n") + for j in range(NY): + for i in range(NX): + f.write(f"{i},{j},{flag_plot[i, j]},{ux[i, j]},{uy[i, j]}\r\n") + + def close(self): + self.flow_field.__del__() \ No newline at end of file diff --git a/src/drl_pinball/legacy_env/legacy_env_vortex.py b/src/drl_pinball/legacy_env/legacy_env_vortex.py new file mode 100644 index 0000000..be41814 --- /dev/null +++ b/src/drl_pinball/legacy_env/legacy_env_vortex.py @@ -0,0 +1,237 @@ +# 这个是vortex的env,用于训练和评估vortex_taylor和vortex_lamb模型 +# 上游干净来流,目标是vortex流过的时序信号于无pinball情况一致 +# vortes系列模型都基于d1a3o12_re100模型迁移训练 +import gymnasium as gym +import numpy as np +from gymnasium import spaces +import ctypes +from collections import deque +from typing import Tuple +import sys +import os +import matplotlib.pyplot as plt +import queue + +os.environ["OMP_NUM_THREADS"] = "1" +os.environ["MKL_NUM_THREADS"] = "1" + +current_dir = os.path.dirname(os.path.abspath("__file__")) +parent_dir = os.path.abspath(os.path.join(current_dir, os.pardir)) +sys.path.append(parent_dir) +from CelerisLab import FlowField +from CelerisLab import utils + +config_cuda = utils.load_cuda_config( + os.path.join(parent_dir, "configs", "config_cuda.json") +) +config_field = utils.load_flow_field_config( + os.path.join(parent_dir, "configs", "config_flowfield.json") +) + +S_DIM, A_DIM = 12, 3 +U0 = config_field.velocity +T0 = 1000 +SAMPLE_INTERVAL = 800 +FIFO_LEN = 150 +CONV_LEN = 30 +MAX_STEPS = 150 +if config_field.data_type == "FP32": + DATA_TYPE = np.float32 +else: + raise ValueError(f"Unsupported data type {config_field.data_type}.") + + +class CustomEnv(gym.Env): + """Custom Environment that follows gym interface.""" + + metadata = {"render_modes": ["human"], "render_fps": T0 / SAMPLE_INTERVAL} + + def __init__(self, device_id=0): + super().__init__() + self.action_space = spaces.Box(low=-1, high=1, shape=(A_DIM,), dtype=DATA_TYPE) + self.observation_space = spaces.Box( + low=-1, high=1, shape=(S_DIM,), dtype=DATA_TYPE + ) + self.fifo_states = deque(maxlen=FIFO_LEN) + self.target_states = np.empty((0, 6), dtype=DATA_TYPE) + self.force_norm_fact = 1.0 + self.sens_norm_fact = np.ones(6, dtype=DATA_TYPE) + self.sens_deviation = np.zeros(6, dtype=DATA_TYPE) + self.reward_cd = 0.0 + self.reward_cl = 0.0 + self.reward_sim = 0.0 + self.current_step = 0 + + self.flow_field = FlowField(config_field, config_cuda, device_id) + L0 = 20 + U0 = config_field.velocity + NX = self.flow_field.FIELD_SHAPE[0] + NY = self.flow_field.FIELD_SHAPE[1] + center: Tuple[float, float, float] = (40 * L0, (NY - 1) / 2 + 2 * L0, 0) + self.flow_field.add_sensor(center, L0 / 4) + center: Tuple[float, float, float] = (40 * L0, (NY - 1) / 2, 0) + self.flow_field.add_sensor(center, L0 / 4) + center: Tuple[float, float, float] = (40 * L0, (NY - 1) / 2 - 2 * L0, 0) + self.flow_field.add_sensor(center, L0 / 4) + self.flow_field.run(int(1*NX/U0), np.zeros(3, dtype=DATA_TYPE)) + self.flow_field.get_ddf() + self.flow_field.save_ddf() + center: Tuple[float, float, float] = (10 * L0, (NY - 1) / 2, 0) + # self.flow_field.add_vortex(center, L0 * 2, 0.5*U0, 0, "lamb") + self.flow_field.add_vortex(center, L0 * 2, 0.03*U0, 0, "taylor") # 这里会更改vortex类型,在模型名中体现 + + for i in range(FIFO_LEN): + self.flow_field.run(SAMPLE_INTERVAL, np.zeros(3, dtype=DATA_TYPE)) + new_state = self.flow_field.obs.copy() + self.target_states = np.vstack((self.target_states, new_state)) + + self.flow_field.restore_ddf() + self.flow_field.apply_ddf() + center: Tuple[float, float, float] = (30 * L0, (NY - 1) / 2, 0) + self.flow_field.add_cylinder(center, L0 / 2) + center: Tuple[float, float, float] = (31.3 * L0, (NY - 1) / 2 + 0.75 * L0, 0) + self.flow_field.add_cylinder(center, L0 / 2) + center: Tuple[float, float, float] = (31.3 * L0, (NY - 1) / 2 - 0.75 * L0, 0) + self.flow_field.add_cylinder(center, L0 / 2) + self.flow_field.run(int(1*NX/U0), np.zeros(6, dtype=DATA_TYPE)) + self.flow_field.run(int(1*NX/U0), np.array([0.0, 0.0, 0.0, 0.0, -4*U0, 4*U0], dtype=DATA_TYPE)) + # self.flow_field.get_ddf() + # self.flow_field.save_ddf() + center: Tuple[float, float, float] = (15 * L0, (NY - 1) / 2, 0) + # self.flow_field.add_vortex(center, L0 * 2, 0.5*U0, 0, "lamb") + self.flow_field.add_vortex(center, L0 * 2, 0.03*U0, 0, "taylor") + self.flow_field.save_ddf() + + for i in range(FIFO_LEN): + self.flow_field.run(SAMPLE_INTERVAL, np.zeros(6, dtype=DATA_TYPE)) + self.fifo_states.append(self.flow_field.obs.copy()) + + # self.save_states = self.fifo_states.copy() + self.flow_field.apply_ddf() + + temp_states = np.array(self.fifo_states) + self.force_norm_fact = 6 * np.max(np.abs(temp_states[:, 6:12])) + for i in range(6): + self.sens_deviation[i] = np.mean(temp_states[:, i]) + self.sens_norm_fact[i] = 5 * np.max(np.abs(temp_states[:, i] - self.sens_deviation[i])) + + for i in range(FIFO_LEN): + self.flow_field.run(SAMPLE_INTERVAL, np.array([0.0, 0.0, 0.0, 0.0, -4*U0, 4*U0], dtype=DATA_TYPE)) + self.fifo_states.append(self.flow_field.obs.copy()) + + self.save_states = self.fifo_states.copy() + self.flow_field.apply_ddf() + + + def step(self, action): + assert self.action_space.contains(action), "%r (%s) invalid" % ( + action, + type(action), + ) + + # barrier = threading.Barrier(2) + result_queue = queue.Queue() + + def run_flow_field(action): + self.flow_field.context.push() + U0 = config_field.velocity + try: + temp = np.zeros(6, dtype=DATA_TYPE) + temp[3:6] = np.array((action*4+[0,-4,4])*U0, dtype=DATA_TYPE) + self.flow_field.run(SAMPLE_INTERVAL, temp) + finally: + self.flow_field.context.pop() + # barrier.wait() + self.fifo_states.append(self.flow_field.obs.copy()) + + def proc_data(): + states = np.array(self.fifo_states) + forces = states[-1, 6:12] / self.force_norm_fact + cd = (forces[0] + forces[2] + forces[4]) / 3 + cl = (forces[1] + forces[3] + forces[5]) / 3 + sens = (states[-1, 0:6] - self.sens_deviation) / self.sens_norm_fact + + similarities = 0.0 + + def calc_sim(target, state): + + n = len(target) + m = len(state) + + dtw_matrix = np.full((n + 1, m + 1), np.inf) + dtw_matrix[0, 0] = 0 + + for i in range(1, n + 1): + for j in range(1, m + 1): + cost = abs(target[i - 1] - state[j - 1]) + last_min = min(dtw_matrix[i - 1, j], + dtw_matrix[i, j - 1], + dtw_matrix[i - 1, j - 1]) + dtw_matrix[i, j] = cost + last_min + + return 1 - (dtw_matrix[n, m] / len(target)) + + for i in range(0, 6): + target_seq = np.roll(self.target_states[-CONV_LEN:, i], -self.current_step-1) + state_seq = states[-CONV_LEN:, i] + similarities += calc_sim(target_seq, state_seq) / 6 + + self.reward_cd = np.exp(-np.abs(cd * 20)) + self.reward_cl = np.exp(-np.abs(cl * 80)) + self.reward_sim = np.exp(-10*np.abs(similarities - 1)) + reward = np.minimum(0.2 * self.reward_cd + 0.3 * self.reward_cl + 0.5 * self.reward_sim, 1.0) + # barrier.wait() + result_queue.put((np.hstack([forces, sens]), reward)) + + run_flow_field(action) + proc_data() + observation, reward = result_queue.get() + + truncated = bool(np.any(observation > 1) or np.any(observation < -1)) + observation = np.clip(observation, -1, 1) + self.current_step += 1 + done = self.current_step >= MAX_STEPS + return observation, float(reward), done, truncated, {} + + def reset(self, seed=None): + self.flow_field.restore_ddf() + self.flow_field.apply_ddf() + self.fifo_states = self.save_states.copy() + self.current_step = 0 + return np.zeros(S_DIM, dtype=np.float32), {} + + def render(self, mode="human"): + NX = self.flow_field.FIELD_SHAPE[0] + NY = self.flow_field.FIELD_SHAPE[1] + self.flow_field.get_ddf() + ddf_plot = self.flow_field.ddf.copy().reshape((9, NY, NX)).transpose(2, 1, 0) + ux = (ddf_plot[:, :, 1] + ddf_plot[:, :, 5] + ddf_plot[:, :, 8] - ddf_plot[:, :, 3] - ddf_plot[:, :, 6] - ddf_plot[:, :, 7]) / U0 + uy = (ddf_plot[:, :, 2] + ddf_plot[:, :, 5] + ddf_plot[:, :, 6] - ddf_plot[:, :, 4] - ddf_plot[:, :, 7] - ddf_plot[:, :, 8]) / U0 + speed = np.sqrt(ux**2 + uy**2) + plt.figure(figsize=(10, 5)) + plt.imshow(speed.T, origin='lower', cmap='viridis', extent=[0, NX, 0, NY]) + plt.colorbar(label='Speed') + plt.title('Scalar Velocity Field') + plt.xlabel('X') + plt.ylabel('Y') + plt.tight_layout() + plt.show() + + def save_field(self, filename): + NX = self.flow_field.FIELD_SHAPE[0] + NY = self.flow_field.FIELD_SHAPE[1] + self.flow_field.get_ddf() + ddf_plot = self.flow_field.ddf.copy().reshape((9, NY, NX)).transpose(2, 1, 0) + flag_plot = self.flow_field.flag.copy().reshape((NY, NX)).transpose(1, 0) + ux = (ddf_plot[:, :, 1] + ddf_plot[:, :, 5] + ddf_plot[:, :, 8] - ddf_plot[:, :, 3] - ddf_plot[:, :, 6] - ddf_plot[:, :, 7]) / U0 + uy = (ddf_plot[:, :, 2] + ddf_plot[:, :, 5] + ddf_plot[:, :, 6] - ddf_plot[:, :, 4] - ddf_plot[:, :, 7] - ddf_plot[:, :, 8]) / U0 + with open(os.path.join(parent_dir, "output", filename), "w") as f: + f.write("Title= \"LBM 2D\"\r\n") + f.write("VARIABLES= \"X\",\"Y\",\"flag\",\"U\",\"V\",\r\n") + f.write(f"ZONE T= \"BOX\",I= {NX},J= {NY},F=POINT\r\n") + for j in range(NY): + for i in range(NX): + f.write(f"{i},{j},{flag_plot[i, j]},{ux[i, j]},{uy[i, j]}\r\n") + + def close(self): + self.flow_field.__del__() \ No newline at end of file diff --git a/src/drl_pinball/legacy_env/legacy_karman_env.py b/src/drl_pinball/legacy_env/legacy_karman_env.py new file mode 100644 index 0000000..9d0069b --- /dev/null +++ b/src/drl_pinball/legacy_env/legacy_karman_env.py @@ -0,0 +1,518 @@ +# drl_pinball/legacy_env/legacy_karman_env.py +""" +Standalone Karman cloak re100 environment using LegacyCelerisLab. + +This module exactly reproduces env_karman_cloak_standard.py but exposes +ALL intermediate data for validation against the new CelerisLab API. + +Usage:: + + from legacy_karman_env import legacy_build_re100, legacy_infer_re100 + + data = legacy_build_re100(device_id=0) + # data['target_states'], data['norm'], data['flow_field'], ... + + results = legacy_infer_re100(data['flow_field'], model, data['target_states'], data['norm'], n_steps=50) + # results['sensors'], results['forces'], results['obs'], results['actions'], results['rewards'] +""" + +from __future__ import annotations + +import os +import sys +from collections import deque +from typing import Any, Dict, Optional, Tuple + +import numpy as np + +# Add repo root for LegacyCelerisLab import +_REPO = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..")) +if _REPO not in sys.path: + sys.path.insert(0, _REPO) + +from LegacyCelerisLab import FlowField # noqa: E402 +from LegacyCelerisLab import utils as legacy_utils # noqa: E402 + +# --------------------------------------------------------------------------- +# Constants — matching env_karman_cloak_standard.py EXACTLY +# --------------------------------------------------------------------------- +CONFIG_DIR = os.path.join(_REPO, "configs", "legacy_configs") + +S_DIM, A_DIM = 12, 3 +U0 = 0.01 +T0 = 1000 +SAMPLE_INTERVAL = 800 +FIFO_LEN = 150 +CONV_LEN = 30 +MAX_STEPS = 500 +DATA_TYPE = np.float32 +ACTION_SMOOTH_WEIGHT = 0.1 # legacy run() uses internal exponential smoothing + +# Geometry constants (in lattice units) +L0 = 20.0 +# Disturbance cylinder (id=0 in env order) +DIST_CENTER = (10.0 * L0, None, 0.0) # y will be set at runtime +DIST_RADIUS = 1.0 * L0 + +# Sensors (ids=1,2,3) +SENSOR_RADIUS = L0 / 4.0 + +# Pinball cylinders (ids=4,5,6) +PINBALL_RADIUS = L0 / 2.0 +FRONT_CENTER = (30.0 * L0, None, 0.0) # y = CENTER_Y +BOTTOM_CENTER = (31.3 * L0, None, 0.0) # y = CENTER_Y - 0.75*L0 +TOP_CENTER = (31.3 * L0, None, 0.0) # y = CENTER_Y + 0.75*L0 + + +def _center_y(ff: FlowField) -> float: + """Return the center y-coordinate of the flow field.""" + return (ff.FIELD_SHAPE[1] - 1) / 2.0 + + +def _fill_y(cfg: Tuple[float, Optional[float], float], cy: float) -> tuple: + """Replace None y with actual center y.""" + x, y, z = cfg + if y is None: + y = cy + return (x, y, z) + + +# --------------------------------------------------------------------------- +# Phase 1a: Build reference dataset (reproduces env.__init__) +# --------------------------------------------------------------------------- + +def legacy_build_re100( + device_id: int = 0, + viscosity: Optional[float] = None, +) -> Dict[str, Any]: + """Reproduce env_karman_cloak_standard.__init__() exactly. + + Parameters + ---------- + device_id : int + GPU device ID. + viscosity : float, optional + Override viscosity (default: 0.004 for Re=100). + + Returns + ------- + dict with keys: + flow_field : FlowField — the CFD instance (state at end of init) + target_states : ndarray (FIFO_LEN, 6) — target sensor signals + norm : dict with force_norm_fact, sens_deviation, sens_norm_fact + config : dict with all runtime parameters + fifo_after_bias : deque — FIFO state after bias rollout + """ + # Default viscosity for Re=100 (code Re, using 2D reference) + if viscosity is None: + viscosity = 0.004 + + # Load legacy configs + cuda_cfg = legacy_utils.load_cuda_config( + os.path.join(CONFIG_DIR, "config_cuda.json") + ) + field_cfg = legacy_utils.load_flow_field_config( + os.path.join(CONFIG_DIR, "config_flowfield.json") + ) + # Override viscosity + field_cfg = field_cfg._replace(viscosity=float(viscosity)) + + # -- Step 0: Create FlowField ------------------------------------------ + ff = FlowField(field_cfg, cuda_cfg, device_id=device_id) + cy = _center_y(ff) + NX = ff.FIELD_SHAPE[0] + NY = ff.FIELD_SHAPE[1] + + # -- Step 1: Add disturbance cylinder + sensors ------------------------- + # Order matters for obs indexing: dist_cyl(0), sensor0(1), sensor1(2), sensor2(3) + ff.add_cylinder(_fill_y(DIST_CENTER, cy), DIST_RADIUS) + for y_off in [2.0, 0.0, -2.0]: + sc = (40.0 * L0, cy + y_off * L0, 0.0) + ff.add_sensor(sc, SENSOR_RADIUS) + + n_obj_phase1 = ff.obs.size // 2 # 4 objects + assert n_obj_phase1 == 4, f"Expected 4 objects after sensors, got {n_obj_phase1}" + + # -- Step 2: Stabilize -------------------------------------------------- + stabilize_steps = int(4 * NX / U0) + ff.run(stabilize_steps, np.zeros(n_obj_phase1, dtype=DATA_TYPE)) + + # -- Step 3: Record target signals -------------------------------------- + target_states = np.empty((0, 6), dtype=DATA_TYPE) + for _ in range(FIFO_LEN): + ff.run(SAMPLE_INTERVAL, np.zeros(n_obj_phase1, dtype=DATA_TYPE)) + new_state = ff.obs.copy()[2:8] # sensors only skip dist_cyl + target_states = np.vstack((target_states, new_state)) + + # -- Step 4: Add pinball cylinders (ids=4,5,6) ------------------------- + ff.add_cylinder(_fill_y(FRONT_CENTER, cy), PINBALL_RADIUS) + ff.add_cylinder(_fill_y(BOTTOM_CENTER, cy - 0.75 * L0), PINBALL_RADIUS) + ff.add_cylinder(_fill_y(TOP_CENTER, cy + 0.75 * L0), PINBALL_RADIUS) + + n_obj_total = ff.obs.size // 2 # 7 objects + assert n_obj_total == 7, f"Expected 7 objects, got {n_obj_total}" + + # -- Step 5: Stabilize with pinball ------------------------------------- + ff.run(stabilize_steps, np.zeros(n_obj_total, dtype=DATA_TYPE)) + + # -- Step 6: Checkpoint DDF (steady pinball + disturbance state) -------- + ff.get_ddf() + ff.save_ddf() + + # -- Step 7: Zero-action norm collection -------------------------------- + fifo = deque(maxlen=FIFO_LEN) + for _ in range(FIFO_LEN): + ff.run(SAMPLE_INTERVAL, np.zeros(n_obj_total, dtype=DATA_TYPE)) + fifo.append(ff.obs.copy()[2:14]) # sensor[6] + force[6] + + temp_states = np.array(fifo, dtype=DATA_TYPE) + force_norm_fact = 6.0 * float(np.max(np.abs(temp_states[:, 6:12]))) + sens_deviation = np.mean(temp_states[:, 0:6], axis=0).astype(DATA_TYPE) + sens_norm_fact = np.zeros(6, dtype=DATA_TYPE) + for i in range(6): + sens_norm_fact[i] = 5.0 * float(np.max(np.abs(temp_states[:, i] - sens_deviation[i]))) + + # -- Step 8: Bias-action rollout (for FIFO init in controlled runs) ----- + ff.apply_ddf() # restore pre-bias state + + # Action bias: front=0, bottom=-4*U0, top=4*U0 + bias_arr = np.zeros(n_obj_total, dtype=DATA_TYPE) + bias_arr[n_obj_total - 3] = float((0.0 * 8.0 + 0.0) * U0) # front = 0 + bias_arr[n_obj_total - 2] = float((0.0 * 8.0 + (-4.0)) * U0) # bottom = -4*U0 + bias_arr[n_obj_total - 1] = float((0.0 * 8.0 + 4.0) * U0) # top = 4*U0 + + fifo.clear() + for _ in range(FIFO_LEN): + ff.run(SAMPLE_INTERVAL, bias_arr) + fifo.append(ff.obs.copy()[2:14]) + + save_states = np.array(list(fifo), dtype=DATA_TYPE) + + # -- Step 9: Restore to steady state (ready for reset) ------------------ + ff.apply_ddf() + + norm = { + "force_norm_fact": force_norm_fact, + "sens_deviation": sens_deviation.tolist(), + "sens_norm_fact": sens_norm_fact.tolist(), + "save_states": save_states, + "action_bias": [0.0, -4.0, 4.0], + "n_obj_total": n_obj_total, + } + + config = { + "device_id": device_id, + "viscosity": viscosity, + "u0": U0, + "sample_interval": SAMPLE_INTERVAL, + "fifo_len": FIFO_LEN, + "conv_len": CONV_LEN, + "nx": NX, + "ny": NY, + "n_obj_total": n_obj_total, + "action_scale": 8.0, + "action_bias": [0.0, -4.0, 4.0], + } + + return { + "flow_field": ff, + "target_states": target_states, + "norm": norm, + "config": config, + "fifo_after_bias": fifo, + } + + +# --------------------------------------------------------------------------- +# Phase 1b: Inference — reproduces env.step() exactly +# --------------------------------------------------------------------------- + +def legacy_infer_re100( + flow_field: FlowField, + model: Any, + target_states: np.ndarray, + norm: Dict[str, Any], + n_steps: int = 50, + *, + use_deterministic: bool = True, +) -> Dict[str, np.ndarray]: + """Run Karman cloak re100 controlled inference with legacy CFD. + + This follows the exact pattern in env_karman_cloak_standard.step() and + analysis_crossre/scripts/phase1_infer.py. + + Parameters + ---------- + flow_field : FlowField + Initialized flow field (must be in steady pinball state). + model : PPO + Trained PPO model (with Sin activation). + target_states : ndarray (FIFO_LEN, 6) + Target sensor signals. + norm : dict + Normalization factors from legacy_build_re100(). + n_steps : int + Number of inference steps (each = SAMPLE_INTERVAL LBM steps). + use_deterministic : bool + Use deterministic action (True) or stochastic (False). + + Returns + ------- + dict with keys: + sensors : (n_steps, 6) raw sensor velocities + forces : (n_steps, 6) raw forces (all 6 force components) + obs : (n_steps, 12) normalised DRL observations + actions : (n_steps, 3) normalised DRL actions in [-1, 1] + rewards : (n_steps,) step rewards + reward_cd : (n_steps,) + reward_cl : (n_steps,) + reward_sim : (n_steps,) + similarities : (n_steps,) DTW similarity scores + """ + n_obj_total = norm.get("n_obj_total", 7) + action_scale = 8.0 + action_bias = np.array(norm.get("action_bias", [0.0, -4.0, 4.0]), dtype=np.float32) + force_norm_fact = float(norm["force_norm_fact"]) + sens_deviation = np.array(norm["sens_deviation"], dtype=np.float32) + sens_norm_fact = np.array(norm["sens_norm_fact"], dtype=np.float32) + + # Restore steady state + flow_field.restore_ddf() + flow_field.apply_ddf() + + # Bias-action FIFO init (reproduces env.__init__ bias rollout) + fifo = deque(maxlen=FIFO_LEN) + bias_arr = np.zeros(n_obj_total, dtype=DATA_TYPE) + bias_arr[n_obj_total - 3] = float(action_bias[0] * U0) + bias_arr[n_obj_total - 2] = float(action_bias[1] * U0) + bias_arr[n_obj_total - 1] = float(action_bias[2] * U0) + + for _ in range(FIFO_LEN): + flow_field.run(SAMPLE_INTERVAL, bias_arr) + fifo.append(flow_field.obs.copy()[2:14]) + + # Inference loop + sens_list, forc_list, obs_list = [], [], [] + action_list, reward_list = [], [] + reward_cd_list, reward_cl_list, reward_sim_list = [], [], [] + sim_list = [] + + obs = np.zeros(S_DIM, dtype=np.float32) + + for step in range(n_steps): + # --- PPO action --- + action, _states = model.predict(obs, deterministic=use_deterministic) + action = action.astype(np.float32).flatten() + action_list.append(action.copy()) + + # --- Convert to legacy action array --- + action_arr = np.zeros(n_obj_total, dtype=DATA_TYPE) + omega = (action * action_scale + action_bias) * U0 + action_arr[n_obj_total - 3:] = omega + + # --- Run CFD --- + # Context management: push/pop to avoid PyTorch CUDA context conflicts + flow_field.context.push() + try: + flow_field.run(SAMPLE_INTERVAL, action_arr) + finally: + flow_field.context.pop() + + # --- Read telemetry --- + obs_slice = flow_field.obs.copy()[2:14] + fifo.append(obs_slice) + sens_list.append(obs_slice[0:6].copy()) + forc_list.append(obs_slice[6:12].copy()) + + # --- Build normalised observation --- + forces_norm = obs_slice[6:12] / force_norm_fact + sens_norm = (obs_slice[0:6] - sens_deviation) / sens_norm_fact + obs = np.clip(np.hstack([forces_norm, sens_norm]), -1.0, 1.0).astype(np.float32) + obs_list.append(obs) + + # --- Compute reward (exactly matching env logic) --- + states_arr = np.array(fifo, dtype=np.float32) + if len(states_arr) >= CONV_LEN: + forces = states_arr[-1, 6:12] / force_norm_fact + cd = float((forces[0] + forces[2] + forces[4]) / 3.0) + cl = float((forces[1] + forces[3] + forces[5]) / 3.0) + + # Similarity = lag-compensated DTW over all 6 sensor channels + # calc_lag on middle sensor (index 1 = sensor1_uy) + target_seq = target_states[CONV_LEN:2 * CONV_LEN, 1] + state_seq = states_arr[-CONV_LEN:, 1] + lag = _calc_lag(target_seq, state_seq) + + sim_sum = 0.0 + for i in range(6): + t_seq = np.roll(target_states[:, i], -lag)[CONV_LEN:2 * CONV_LEN] + s_seq = states_arr[-CONV_LEN:, i] + sim_sum += _calc_dtw_sim(t_seq, s_seq) / 6.0 + similarities = float(sim_sum) + sim_list.append(similarities) + + r_cd = float(np.exp(-abs(cd * 20.0))) + r_cl = float(np.exp(-abs(cl * 80.0))) + r_sim = float(np.exp(-10.0 * abs(similarities - 1.0))) + reward = float(min(0.3 * r_cd + 0.4 * r_cl + 0.3 * r_sim, 1.0)) + else: + reward = 0.0 + r_cd = 0.0 + r_cl = 0.0 + r_sim = 0.0 + similarities = 0.0 + + reward_list.append(reward) + reward_cd_list.append(r_cd) + reward_cl_list.append(r_cl) + reward_sim_list.append(r_sim) + + return { + "sensors": np.array(sens_list, dtype=np.float32), + "forces": np.array(forc_list, dtype=np.float32), + "obs": np.array(obs_list, dtype=np.float32), + "actions": np.array(action_list, dtype=np.float32), + "rewards": np.array(reward_list, dtype=np.float32), + "reward_cd": np.array(reward_cd_list, dtype=np.float32), + "reward_cl": np.array(reward_cl_list, dtype=np.float32), + "reward_sim": np.array(reward_sim_list, dtype=np.float32), + "similarities": np.array(sim_list, dtype=np.float32) if sim_list else np.zeros(n_steps, dtype=np.float32), + } + + +# --------------------------------------------------------------------------- +# Helper: uncontrolled rollout (zero action) +# --------------------------------------------------------------------------- + +def legacy_uncontrolled_re100( + flow_field: FlowField, + n_steps: int = 50, +) -> Dict[str, np.ndarray]: + """Run uncontrolled Karman cloak re100 inference. + + Parameters + ---------- + flow_field : FlowField + Must be in saved DDF state (steady pinball + disturbance). + n_steps : int + Number of steps. + + Returns + ------- + dict with sensors, forces. + """ + n_obj_total = 7 + + flow_field.restore_ddf() + flow_field.apply_ddf() + + sens_list, forc_list = [], [] + + for _ in range(n_steps): + flow_field.run(SAMPLE_INTERVAL, np.zeros(n_obj_total, dtype=DATA_TYPE)) + obs_slice = flow_field.obs.copy()[2:14] + sens_list.append(obs_slice[0:6].copy()) + forc_list.append(obs_slice[6:12].copy()) + + return { + "sensors": np.array(sens_list, dtype=np.float32), + "forces": np.array(forc_list, dtype=np.float32), + } + + +# --------------------------------------------------------------------------- +# DTW helpers (exact copies from env_karman_cloak_standard.py) +# --------------------------------------------------------------------------- + +def _calc_lag(target: np.ndarray, state: np.ndarray) -> int: + target_mean = float(np.mean(target)) + state_mean = float(np.mean(state)) + correlation = np.correlate( + target - target_mean, + state - state_mean, + mode="full", + ) + lags = np.arange(-len(target) + 1, len(target)) + return int(lags[np.argmax(correlation)]) + + +def _calc_dtw_sim(target: np.ndarray, state: np.ndarray) -> float: + n = len(target) + m = len(state) + dtw_matrix = np.full((n + 1, m + 1), np.inf) + dtw_matrix[0, 0] = 0.0 + for i in range(1, n + 1): + for j in range(1, m + 1): + cost = abs(float(target[i - 1]) - float(state[j - 1])) + last_min = min( + dtw_matrix[i - 1, j], + dtw_matrix[i, j - 1], + dtw_matrix[i - 1, j - 1], + ) + dtw_matrix[i, j] = cost + last_min + return float(1.0 - dtw_matrix[n, m] / n) + + +# --------------------------------------------------------------------------- +# CLI entry point for reference dataset generation +# --------------------------------------------------------------------------- + +def main(): + """Generate reference dataset for Karman re100 validation.""" + import argparse + import json + + ap = argparse.ArgumentParser(description="Legacy Karman re100 reference dataset") + ap.add_argument("--device", type=int, default=0, help="GPU device ID") + ap.add_argument("--out", type=str, default="output/validate_re100", help="Output directory") + args = ap.parse_args() + + out_dir = os.path.abspath(args.out) + os.makedirs(out_dir, exist_ok=True) + + print("=== Building legacy Karman re100 reference dataset ===") + print(f"Output: {out_dir}") + + # Build env + data = legacy_build_re100(device_id=args.device) + ff = data["flow_field"] + + # Save target and norm + np.savez(os.path.join(out_dir, "target.npz"), + target_states=data["target_states"]) + + norm_json = { + "force_norm_fact": float(data["norm"]["force_norm_fact"]), + "sens_deviation": list(float(x) for x in data["norm"]["sens_deviation"]), + "sens_norm_fact": list(float(x) for x in data["norm"]["sens_norm_fact"]), + "action_bias": data["norm"]["action_bias"], + } + with open(os.path.join(out_dir, "norm.json"), "w") as f: + json.dump(norm_json, f, indent=2) + + np.savez(os.path.join(out_dir, "save_states.npz"), + save_states=data["norm"]["save_states"]) + + # Save config + with open(os.path.join(out_dir, "config.json"), "w") as f: + json.dump({k: str(v) if isinstance(v, (np.integer, np.floating)) else v + for k, v in data["config"].items()}, f, indent=2) + + # Uncontrolled rollout + print(" uncontrolled rollout (50 steps)...") + unc = legacy_uncontrolled_re100(ff, n_steps=50) + np.savez(os.path.join(out_dir, "uncontrolled.npz"), + sensors=unc["sensors"], forces=unc["forces"]) + + print(" Reference dataset saved.") + print(f" force_norm_fact = {norm_json['force_norm_fact']:.6f}") + print(f" sens_deviation = {norm_json['sens_deviation']}") + print(f" sens_norm_fact = {norm_json['sens_norm_fact']}") + + # Cleanup + del ff + print("Done.") + + +if __name__ == "__main__": + main() diff --git a/src/drl_pinball/legacy_test/paraview.ipynb b/src/drl_pinball/legacy_test/paraview.ipynb new file mode 100644 index 0000000..8cd9453 --- /dev/null +++ b/src/drl_pinball/legacy_test/paraview.ipynb @@ -0,0 +1,494 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "paraview绘图脚本,更改不同注释实现不同绘图,能实现涡量、流线、两个流场的差异等效果" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "import os\n", + "\n", + "current_dir = os.path.dirname(os.path.abspath(\"__file__\"))\n", + "parent_dir = os.path.abspath(os.path.join(current_dir, os.pardir))\n", + "paraview_dir = os.path.join(parent_dir, \"ParaView\")\n", + "\n", + "os.environ['PYTHONPATH'] = f\"{paraview_dir}/lib:{paraview_dir}/lib/python3.9/site-packages\"\n", + "os.environ['LD_LIBRARY_PATH'] = os.path.join(paraview_dir, \"bin\")\n", + "\n", + "sys.path.append(parent_dir)\n", + "sys.path.append(os.path.join(paraview_dir, \"lib\"))\n", + "sys.path.append(os.path.join(paraview_dir, \"bin\"))\n", + "sys.path.append(os.path.join(paraview_dir, \"lib/python3.9/site-packages\"))\n", + "\n", + "# from paraview.simple import *\n", + "import paraview.simple as pv\n", + "from CelerisLab import FlowField\n", + "from CelerisLab import utils\n", + "\n", + "from collections import deque\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "from gym_env import CustomEnv\n", + "from stable_baselines3 import PPO\n", + "import pycuda.driver as cuda\n", + "import pickle\n", + "\n", + "FIFO_LEN = 120\n", + "# SAMPLE_INTERVAL = 600\n", + "DATA_TYPE = np.float32\n", + "CONV_LEN = 60\n", + "SAMP_RATE = 60" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "filename = \"field\"\n", + "targetname = \"target\"\n", + "outputname = \"ref\"\n", + "folder = \"plot\"" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "renderView1 = pv.GetActiveViewOrCreate('RenderView')\n", + "renderView1.ViewSize = [1200, 480]\n", + "renderView1.InteractionMode = '2D'\n", + "renderView1.OrientationAxesVisibility = 0\n", + "renderView1.CameraPosition = [0.0, 0.0, 3.0]\n", + "renderView1.UseLight = 0\n", + "\n", + "name_files = []\n", + "for i in range(1, 151):\n", + " name_files.append(os.path.join(parent_dir, f\"output/250729/anim/field.dat.{i:03}\"))\n", + "dat = pv.TecplotReader(registrationName='data.dat*', FileNames=name_files)\n", + "\n", + "# dat = pv.TecplotReader(registrationName='data.dat', FileNames=os.path.join(parent_dir, \"output/250729/\", folder, filename + \".dat\"))\n", + "# dat = pv.TecplotReader(registrationName='data.dat', FileNames=os.path.join(parent_dir, \"output/250326/episode/500/d1a3o12.dat.150\"))\n", + "dat.DataArrayStatus = ['flag', 'U', 'V']\n", + "# dat0 = pv.TecplotReader(registrationName='data0.dat', FileNames=os.path.join(parent_dir, \"output/250729/\", folder, targetname + \".dat\"))\n", + "# dat0.DataArrayStatus = ['flag', 'U', 'V']\n", + "\n", + "# programmableFilter1 = pv.ProgrammableFilter(registrationName='ProgrammableFilter1', Input=[dat, dat0])\n", + "# programmableFilter1.Script = \"\"\"U_0 = inputs[0].PointData['U']\n", + "# U_1 = inputs[1].PointData['U']\n", + "# V_0 = inputs[0].PointData['V']\n", + "# V_1 = inputs[1].PointData['V']\n", + "# output.PointData.append((U_1 - U_0), 'dU')\n", + "# output.PointData.append((V_1 - V_0), 'dV')\n", + "# output.PointData.append(sqrt(U_1**2+V_1**2), 'Speed')\"\"\"\n", + "\n", + "calculator0 = pv.Calculator(registrationName='Calculator0', Input=dat)\n", + "calculator0.ResultArrayName = 'W'\n", + "calculator0.Function = '0'\n", + "# calculator1 = pv.Calculator(registrationName='Calculator1', Input=programmableFilter1)\n", + "# calculator1.ResultArrayName = 'dZ'\n", + "# calculator1.Function = '0'\n", + "mergeVectorComponents1 = pv.MergeVectorComponents(registrationName='MergeVectorComponents1', Input=calculator0)\n", + "mergeVectorComponents1.XArray = 'U'\n", + "mergeVectorComponents1.YArray = 'V'\n", + "mergeVectorComponents1.ZArray = 'W'\n", + "# mergeVectorComponents1 = pv.MergeVectorComponents(registrationName='MergeVectorComponents1', Input=calculator1)\n", + "# mergeVectorComponents1.XArray = 'dU'\n", + "# mergeVectorComponents1.YArray = 'dV'\n", + "# mergeVectorComponents1.ZArray = 'dZ'\n", + "gradient1 = pv.Gradient(registrationName='Gradient1', Input=mergeVectorComponents1)\n", + "gradient1.ScalarArray = ['POINTS', 'Vector']\n", + "gradient1.ComputeGradient = 0\n", + "gradient1.ComputeVorticity = 1\n", + "calculator1 = pv.Calculator(registrationName='Calculator1', Input=dat)\n", + "calculator1.ResultArrayName = 'Sensor'\n", + "calculator1.Function = '1/(flag-17)'\n", + "calculator1.ReplacementValue = 1000.0\n", + "calculator2 = pv.Calculator(registrationName='Calculator2', Input=dat)\n", + "calculator2.ResultArrayName = 'Solid'\n", + "calculator2.Function = '1/(flag-2)/(flag-10)'\n", + "calculator2.ReplacementValue = 1000.0\n", + "# calculator3 = pv.Calculator(registrationName='Calculator3', Input=mergeVectorComponents1)\n", + "# calculator3.ResultArrayName = 'Error'\n", + "# calculator3.Function = 'Vector/Speed'\n", + "# calculator3 = pv.Calculator(registrationName='Calculator3', Input=mergeVectorComponents1)\n", + "# calculator3.ResultArrayName = 'Speed'\n", + "# calculator3.Function = 'mag(Vector)'\n", + "calculator3 = pv.Calculator(registrationName='Calculator3', Input=gradient1)\n", + "calculator3.ResultArrayName = 'Z_Vort'\n", + "calculator3.Function = 'Vorticity_Z'\n", + "\n", + "slice1 = pv.Slice(registrationName='Slice1', Input=mergeVectorComponents1)\n", + "slice1.SliceType = 'Plane'\n", + "slice1.HyperTreeGridSlicer = 'Plane'\n", + "slice1.SliceOffsetValues = [0.0]\n", + "slice1.SliceType.Origin = [800.0, 255.5, 0.0]\n", + "\n", + "glyph1 = pv.Glyph(registrationName='Glyph1', Input=slice1, GlyphType='Arrow')\n", + "glyph1.OrientationArray = ['POINTS', 'Vector']\n", + "glyph1.ScaleArray = ['POINTS', 'Vector']\n", + "glyph1.ScaleFactor = 50.0\n", + "glyph1.GlyphTransform = 'Transform2'\n", + "glyph1.GlyphMode = 'Every Nth Point'\n", + "glyph1.MaximumNumberOfSamplePoints = 50\n", + "glyph1.Stride = 20\n", + "\n", + "# init the 'Arrow' selected for 'GlyphType'\n", + "glyph1.GlyphType.TipLength = 0.3\n", + "\n", + "# init the 'Transform2' selected for 'GlyphTransform'\n", + "glyph1.GlyphTransform.Scale = [1.0, 1.0, 0.0]\n", + "\n", + "# streamTracer1 = pv.StreamTracer(registrationName='StreamTracer1', Input=mergeVectorComponents1, SeedType='Line')\n", + "# streamTracer1.Vectors = ['POINTS', 'Vector']\n", + "# streamTracer1.IntegratorType = 'Runge-Kutta 4'\n", + "# streamTracer1.MaximumSteps = 4000\n", + "# streamTracer1.MaximumStreamlineLength = 2000.0\n", + "# streamTracer1.SeedType.Point1 = [200.0, 0.0, 0.0]\n", + "# streamTracer1.SeedType.Point2 = [200.0, 500.0, 0.0]\n", + "# streamTracer1.SeedType.Resolution = 50\n", + "# contour1 = pv.Contour(registrationName='Contour1', Input=calculator3)\n", + "# contour1.ContourBy = ['POINTS', 'Speed']\n", + "# contour1.Isosurfaces = [1.0]\n", + "# contour1.PointMergeMethod = 'Uniform Binning'\n", + "# contour2 = pv.Contour(registrationName='Contour2', Input=calculator3)\n", + "# contour2.ContourBy = ['POINTS', 'Z_Vort']\n", + "# contour2.Isosurfaces = [0.0]\n", + "# contour2.PointMergeMethod = 'Uniform Binning'\n", + "\n", + "vorticityTF2D = pv.GetTransferFunction2D('Vorticity')\n", + "vorticityTF2D.ScalarRangeInitialized = 1\n", + "vorticityTF2D.Range = [-0.1, 0.1, 0.0, 1.0]\n", + "vorticityLUT = pv.GetColorTransferFunction('Vorticity')\n", + "vorticityLUT.AutomaticRescaleRangeMode = 'Never'\n", + "vorticityLUT.TransferFunction2D = vorticityTF2D\n", + "vorticityLUT.RGBPoints = [-0.1, 0.23137254902, 0.298039215686, 0.752941176471, -0.0125, 1.0, 1.0, 1.0, 0.0125, 1.0, 1.0, 1.0, 0.1, 0.705882352941, 0.0156862745098, 0.149019607843]\n", + "vorticityLUT.NumberOfTableValues = 16\n", + "vorticityLUT.ScalarRangeInitialized = 1.0\n", + "vorticityLUT.VectorComponent = 2\n", + "vorticityLUT.VectorMode = 'Component'\n", + "vorticityPWF = pv.GetOpacityTransferFunction('Vorticity')\n", + "vorticityPWF.Points = [-0.1, 0.0, 0.5, 0.0, 0.1, 1.0, 0.5, 0.0]\n", + "vorticityPWF.ScalarRangeInitialized = 1\n", + "\n", + "# vorticityTF2D = pv.GetTransferFunction2D('Vorticity')\n", + "# vorticityTF2D.ScalarRangeInitialized = 1\n", + "# vorticityTF2D.Range = [-0.1, 0.1, 0.0, 1.0]\n", + "# vorticityLUT = pv.GetColorTransferFunction('Vorticity')\n", + "# vorticityLUT.AutomaticRescaleRangeMode = 'Never'\n", + "# vorticityLUT.TransferFunction2D = vorticityTF2D\n", + "# vorticityLUT.RGBPoints = [-0.1, 0.18995, 0.07176, 0.23217, -0.09921568, 0.19483, 0.08339, 0.26149, -0.09843138, 0.19956, 0.09498, 0.29024, -0.09764700000000001, 0.20415, 0.10652, 0.31844, -0.0968628, 0.2086, 0.11802, 0.34607, -0.09607840000000001, 0.21291, 0.12947, 0.37314, -0.09529420000000001, 0.21708, 0.14087, 0.39964, -0.0945098, 0.22111, 0.15223, 0.42558, -0.0937254, 0.225, 0.16354, 0.45096, -0.0929412, 0.22875, 0.17481, 0.47578, -0.09215680000000001, 0.23236, 0.18603, 0.50004, -0.0913726, 0.23582, 0.1972, 0.52373, -0.09058820000000001, 0.23915, 0.20833, 0.54686, -0.08980400000000001, 0.24234, 0.21941, 0.56942, -0.0890196, 0.24539, 0.23044, 0.59142, -0.0882352, 0.2483, 0.24143, 0.61286, -0.087451, 0.25107, 0.25237, 0.63374, -0.08666660000000001, 0.25369, 0.26327, 0.65406, -0.0858824, 0.25618, 0.27412, 0.67381, -0.08509800000000001, 0.25853, 0.28492, 0.693, -0.08431380000000001, 0.26074, 0.29568, 0.71162, -0.0835294, 0.2628, 0.30639, 0.72968, -0.082745, 0.26473, 0.31706, 0.74718, -0.0819608, 0.26652, 0.32768, 0.76412, -0.08117640000000001, 0.26816, 0.33825, 0.7805, -0.0803922, 0.26967, 0.34878, 0.79631, -0.07960800000000001, 0.27103, 0.35926, 0.81156, -0.078824, 0.27226, 0.3697, 0.82624, -0.07804, 0.27334, 0.38008, 0.84037, -0.077254, 0.27429, 0.39043, 0.85393, -0.07647000000000001, 0.27509, 0.40072, 0.86692, -0.075686, 0.27576, 0.41097, 0.87936, -0.07490200000000001, 0.27628, 0.42118, 0.89123, -0.074118, 0.27667, 0.43134, 0.90254, -0.07333400000000001, 0.27691, 0.44145, 0.91328, -0.07255, 0.27701, 0.45152, 0.92347, -0.07176400000000001, 0.27698, 0.46153, 0.93309, -0.07098, 0.2768, 0.47151, 0.94214, -0.07019600000000001, 0.27648, 0.48144, 0.95064, -0.069412, 0.27603, 0.49132, 0.95857, -0.068628, 0.27543, 0.50115, 0.96594, -0.067844, 0.27469, 0.51094, 0.97275, -0.067058, 0.27381, 0.52069, 0.97899, -0.066274, 0.27273, 0.5304, 0.98461, -0.06548999999999999, 0.27106, 0.54015, 0.9893, -0.06470600000000001, 0.26878, 0.54995, 0.99303, -0.063922, 0.26592, 0.55979, 0.99583, -0.063138, 0.26252, 0.56967, 0.99773, -0.062352000000000005, 0.25862, 0.57958, 0.99876, -0.061568000000000005, 0.25425, 0.5895, 0.99896, -0.060784000000000005, 0.24946, 0.59943, 0.99835, -0.06, 0.24427, 0.60937, 0.99697, -0.059216000000000005, 0.23874, 0.61931, 0.99485, -0.058432000000000005, 0.23288, 0.62923, 0.99202, -0.057648000000000005, 0.22676, 0.63913, 0.98851, -0.056862, 0.22039, 0.64901, 0.98436, -0.056078, 0.21382, 0.65886, 0.97959, -0.055294, 0.20708, 0.66866, 0.97423, -0.05451, 0.20021, 0.67842, 0.96833, -0.053726, 0.19326, 0.68812, 0.9619, -0.052942, 0.18625, 0.69775, 0.95498, -0.05215600000000001, 0.17923, 0.70732, 0.94761, -0.051372, 0.17223, 0.7168, 0.93981, -0.050588, 0.16529, 0.7262, 0.93161, -0.04980400000000001, 0.15844, 0.73551, 0.92305, -0.04902, 0.15173, 0.74472, 0.91416, -0.048236, 0.14519, 0.75381, 0.90496, -0.047450000000000006, 0.13886, 0.76279, 0.8955, -0.046666, 0.13278, 0.77165, 0.8858, -0.045882000000000006, 0.12698, 0.78037, 0.8759, -0.045098000000000006, 0.12151, 0.78896, 0.86581, -0.044314, 0.11639, 0.7974, 0.85559, -0.043530000000000006, 0.11167, 0.80569, 0.84525, -0.042746, 0.10738, 0.81381, 0.83484, -0.04196, 0.10357, 0.82177, 0.82437, -0.041176000000000004, 0.10026, 0.82955, 0.81389, -0.040392, 0.0975, 0.83714, 0.80342, -0.039608000000000004, 0.09532, 0.84455, 0.79299, -0.038824000000000004, 0.09377, 0.85175, 0.78264, -0.03804, 0.09287, 0.85875, 0.7724, -0.037253999999999995, 0.09267, 0.86554, 0.7623, -0.03647, 0.0932, 0.87211, 0.75237, -0.035685999999999996, 0.09451, 0.87844, 0.74265, -0.034902, 0.09662, 0.88454, 0.73316, -0.03411800000000001, 0.09958, 0.8904, 0.72393, -0.033334, 0.10342, 0.896, 0.715, -0.03255000000000001, 0.10815, 0.90142, 0.70599, -0.031764, 0.11374, 0.90673, 0.69651, -0.030979999999999994, 0.12014, 0.91193, 0.6866, -0.030196, 0.12733, 0.91701, 0.67627, -0.029412000000000008, 0.13526, 0.92197, 0.66556, -0.028628, 0.14391, 0.9268, 0.65448, -0.027844000000000008, 0.15323, 0.93151, 0.64308, -0.027058000000000013, 0.16319, 0.93609, 0.63137, -0.026274000000000006, 0.17377, 0.94053, 0.61938, -0.02549, 0.18491, 0.94484, 0.60713, -0.024705999999999992, 0.19659, 0.94901, 0.59466, -0.023922, 0.20877, 0.95304, 0.58199, -0.023138000000000006, 0.22142, 0.95692, 0.56914, -0.02235200000000001, 0.23449, 0.96065, 0.55614, -0.021568000000000004, 0.24797, 0.96423, 0.54303, -0.020783999999999997, 0.2618, 0.96765, 0.52981, -0.01999999999999999, 0.27597, 0.97092, 0.51653, -0.019215999999999997, 0.29042, 0.97403, 0.50321, -0.018432000000000004, 0.30513, 0.97697, 0.48987, -0.017647999999999997, 0.32006, 0.97974, 0.47654, -0.016862000000000002, 0.33517, 0.98234, 0.46325, -0.01607800000000001, 0.35043, 0.98477, 0.45002, -0.015294000000000002, 0.36581, 0.98702, 0.43688, -0.014509999999999995, 0.38127, 0.98909, 0.42386, -0.013726000000000002, 0.39678, 0.99098, 0.41098, -0.012941999999999995, 0.41229, 0.99268, 0.39826, -0.012156, 0.42778, 0.99419, 0.38575, -0.011372000000000007, 0.44321, 0.99551, 0.37345, -0.010588, 0.45854, 0.99663, 0.3614, -0.009804000000000007, 0.47375, 0.99755, 0.34963, -0.00902, 0.48879, 0.99828, 0.33816, -0.008235999999999993, 0.50362, 0.99879, 0.32701, -0.007449999999999998, 0.51822, 0.9991, 0.31622, -0.006666000000000005, 0.53255, 0.99919, 0.30581, -0.005881999999999998, 0.54658, 0.99907, 0.29581, -0.005098000000000005, 0.56026, 0.99873, 0.28623, -0.004313999999999998, 0.57357, 0.99817, 0.27712, -0.0035300000000000054, 0.58646, 0.99739, 0.26849, -0.0027459999999999984, 0.59891, 0.99638, 0.26038, -0.0019599999999999895, 0.61088, 0.99514, 0.2528, -0.0011759999999999965, 0.62233, 0.99366, 0.24579, -0.00039200000000000346, 0.63323, 0.99195, 0.23937, 0.0003919999999999896, 0.64362, 0.98999, 0.23356, 0.0011759999999999965, 0.65394, 0.98775, 0.22835, 0.0019600000000000034, 0.66428, 0.98524, 0.2237, 0.0027459999999999984, 0.67462, 0.98246, 0.2196, 0.0035300000000000054, 0.68494, 0.97941, 0.21602, 0.004313999999999998, 0.69525, 0.9761, 0.21294, 0.005098000000000005, 0.70553, 0.97255, 0.21032, 0.005882000000000012, 0.71577, 0.96875, 0.20815, 0.006665999999999991, 0.72596, 0.9647, 0.2064, 0.007449999999999998, 0.7361, 0.96043, 0.20504, 0.008235999999999993, 0.74617, 0.95593, 0.20406, 0.00902, 0.75617, 0.95121, 0.20343, 0.009803999999999993, 0.76608, 0.94627, 0.20311, 0.010588, 0.77591, 0.94113, 0.2031, 0.011372000000000007, 0.78563, 0.93579, 0.20336, 0.012155999999999986, 0.79524, 0.93025, 0.20386, 0.012942000000000009, 0.80473, 0.92452, 0.20459, 0.013725999999999988, 0.8141, 0.91861, 0.20552, 0.014509999999999995, 0.82333, 0.91253, 0.20663, 0.015294000000000002, 0.83241, 0.90627, 0.20788, 0.016077999999999995, 0.84133, 0.89986, 0.20926, 0.016862000000000002, 0.8501, 0.89328, 0.21074, 0.017647999999999997, 0.85868, 0.88655, 0.2123, 0.018432000000000004, 0.86709, 0.87968, 0.21391, 0.01921600000000001, 0.8753, 0.87267, 0.21555, 0.01999999999999999, 0.88331, 0.86553, 0.21719, 0.020783999999999997, 0.89112, 0.85826, 0.2188, 0.021568000000000004, 0.8987, 0.85087, 0.22038, 0.022351999999999997, 0.90605, 0.84337, 0.22188, 0.023137999999999992, 0.91317, 0.83576, 0.22328, 0.023922, 0.92004, 0.82806, 0.22456, 0.024706000000000006, 0.92666, 0.82025, 0.2257, 0.025489999999999985, 0.93301, 0.81236, 0.22667, 0.026273999999999992, 0.93909, 0.80439, 0.22744, 0.027058, 0.94489, 0.79634, 0.228, 0.027844000000000008, 0.95039, 0.78823, 0.22831, 0.028628000000000015, 0.9556, 0.78005, 0.22836, 0.029411999999999994, 0.96049, 0.77181, 0.22811, 0.030196, 0.96507, 0.76352, 0.22754, 0.030980000000000008, 0.96931, 0.75519, 0.22663, 0.03176399999999999, 0.97323, 0.74682, 0.22536, 0.032549999999999996, 0.97679, 0.73842, 0.22369, 0.033334, 0.98, 0.73, 0.22161, 0.03411800000000001, 0.98289, 0.7214, 0.21918, 0.034902000000000016, 0.98549, 0.7125, 0.2165, 0.035685999999999996, 0.98781, 0.7033, 0.21358, 0.03647, 0.98986, 0.69382, 0.21043, 0.03725400000000001, 0.99163, 0.68408, 0.20706, 0.03804000000000002, 0.99314, 0.67408, 0.20348, 0.038824, 0.99438, 0.66386, 0.19971, 0.039608000000000004, 0.99535, 0.65341, 0.19577, 0.04039200000000001, 0.99607, 0.64277, 0.19165, 0.04117599999999999, 0.99654, 0.63193, 0.18738, 0.04196, 0.99675, 0.62093, 0.18297, 0.042746000000000006, 0.99672, 0.60977, 0.17842, 0.04353000000000001, 0.99644, 0.59846, 0.17376, 0.04431400000000002, 0.99593, 0.58703, 0.16899, 0.045098, 0.99517, 0.57549, 0.16412, 0.045882000000000006, 0.99419, 0.56386, 0.15918, 0.04666600000000001, 0.99297, 0.55214, 0.15417, 0.04744999999999999, 0.99153, 0.54036, 0.1491, 0.048236, 0.98987, 0.52854, 0.14398, 0.04902000000000001, 0.98799, 0.51667, 0.13883, 0.049804000000000015, 0.9859, 0.50479, 0.13367, 0.05058800000000002, 0.9836, 0.49291, 0.12849, 0.051372, 0.98108, 0.48104, 0.12332, 0.05215600000000001, 0.97837, 0.4692, 0.11817, 0.05294200000000002, 0.97545, 0.4574, 0.11305, 0.053726000000000024, 0.97234, 0.44565, 0.10797, 0.05451, 0.96904, 0.43399, 0.10294, 0.05529400000000001, 0.96555, 0.42241, 0.09798, 0.05607800000000002, 0.96187, 0.41093, 0.0931, 0.056861999999999996, 0.95801, 0.39958, 0.08831, 0.057648000000000005, 0.95398, 0.38836, 0.08362, 0.05843200000000001, 0.94977, 0.37729, 0.07905, 0.05921600000000002, 0.94538, 0.36638, 0.07461, 0.060000000000000026, 0.94084, 0.35566, 0.07031, 0.060784000000000005, 0.93612, 0.34513, 0.06616, 0.06156800000000001, 0.93125, 0.33482, 0.06218, 0.06235200000000002, 0.92623, 0.32473, 0.05837, 0.063138, 0.92105, 0.31489, 0.05475, 0.063922, 0.91572, 0.3053, 0.05134, 0.06470600000000001, 0.91024, 0.29599, 0.04814, 0.06549000000000002, 0.90463, 0.28696, 0.04516, 0.06627400000000003, 0.89888, 0.27824, 0.04243, 0.067058, 0.89298, 0.26981, 0.03993, 0.06784399999999999, 0.88691, 0.26152, 0.03753, 0.068628, 0.88066, 0.25334, 0.03521, 0.069412, 0.87422, 0.24526, 0.03297, 0.07019600000000001, 0.8676, 0.2373, 0.03082, 0.07098000000000002, 0.86079, 0.22945, 0.02875, 0.07176400000000002, 0.8538, 0.2217, 0.02677, 0.07255, 0.84662, 0.21407, 0.02487, 0.07333400000000001, 0.83926, 0.20654, 0.02305, 0.07411799999999999, 0.83172, 0.19912, 0.02131, 0.074902, 0.82399, 0.19182, 0.01966, 0.075686, 0.81608, 0.18462, 0.01809, 0.07647000000000001, 0.80799, 0.17753, 0.0166, 0.07725400000000002, 0.79971, 0.17055, 0.0152, 0.07804, 0.79125, 0.16368, 0.01387, 0.078824, 0.7826, 0.15693, 0.01264, 0.07960799999999998, 0.77377, 0.15028, 0.01148, 0.08039199999999999, 0.76476, 0.14374, 0.01041, 0.081176, 0.75556, 0.13731, 0.00942, 0.08196, 0.74617, 0.13098, 0.00851, 0.08274600000000001, 0.73661, 0.12477, 0.00769, 0.08353, 0.72686, 0.11867, 0.00695, 0.084314, 0.71692, 0.11268, 0.00629, 0.08509800000000001, 0.7068, 0.1068, 0.00571, 0.08588199999999999, 0.6965, 0.10102, 0.00522, 0.08666599999999999, 0.68602, 0.09536, 0.00481, 0.08745, 0.67535, 0.0898, 0.00449, 0.08823600000000001, 0.66449, 0.08436, 0.00424, 0.08902000000000002, 0.65345, 0.07902, 0.00408, 0.089804, 0.64223, 0.0738, 0.00401, 0.090588, 0.63082, 0.06868, 0.00401, 0.09137200000000001, 0.61923, 0.06367, 0.0041, 0.09215599999999999, 0.60746, 0.05878, 0.00427, 0.092942, 0.5955, 0.05399, 0.00453, 0.093726, 0.58336, 0.04931, 0.00486, 0.09451000000000001, 0.57103, 0.04474, 0.00529, 0.09529399999999999, 0.55852, 0.04028, 0.00579, 0.096078, 0.54583, 0.03593, 0.00638, 0.096862, 0.53295, 0.03169, 0.00705, 0.09764800000000001, 0.51989, 0.02756, 0.0078, 0.09843200000000002, 0.50664, 0.02354, 0.00863, 0.099216, 0.49321, 0.01963, 0.00955, 0.1, 0.4796, 0.01583, 0.01055]\n", + "# vorticityLUT.ColorSpace = 'RGB'\n", + "# vorticityLUT.NumberOfTableValues = 11\n", + "# vorticityLUT.ScalarRangeInitialized = 1.0\n", + "# vorticityLUT.VectorComponent = 2\n", + "# vorticityLUT.VectorMode = 'Component'\n", + "# vorticityPWF = pv.GetOpacityTransferFunction('Vorticity')\n", + "# vorticityPWF.Points = [-0.1, 0.0, 0.5, 0.0, 0.1, 1.0, 0.5, 0.0]\n", + "# vorticityPWF.ScalarRangeInitialized = 1\n", + "\n", + "sensorTF2D = pv.GetTransferFunction2D('Sensor')\n", + "sensorLUT = pv.GetColorTransferFunction('Sensor')\n", + "sensorLUT.EnableOpacityMapping = 1\n", + "sensorLUT.TransferFunction2D = sensorTF2D\n", + "sensorLUT.RGBPoints = [-0.14285714285714285, 1.0, 1.0, 1.0, 1000.0, 0.4, 0.8, 0.4]\n", + "sensorLUT.ColorSpace = 'RGB'\n", + "sensorLUT.NanColor = [1.0, 0.0, 0.0]\n", + "sensorLUT.NumberOfTableValues = 10\n", + "sensorLUT.ScalarRangeInitialized = 1.0\n", + "sensorPWF = pv.GetOpacityTransferFunction('Sensor')\n", + "sensorPWF.Points = [-0.14285714285714285, 0.0, 0.5, 0.0, 499.87855928449983, 0.0, 0.5, 0.0, 1000.0, 1.0, 0.5, 0.0]\n", + "sensorPWF.ScalarRangeInitialized = 1\n", + "\n", + "solidTF2D = pv.GetTransferFunction2D('Solid')\n", + "solidLUT = pv.GetColorTransferFunction('Solid')\n", + "solidLUT.EnableOpacityMapping = 1\n", + "solidLUT.TransferFunction2D = solidTF2D\n", + "solidLUT.RGBPoints = [-0.14285714285714285, 1.0, 1.0, 1.0, 1000.0, 0.0, 0.0, 0.0]\n", + "solidLUT.ColorSpace = 'RGB'\n", + "solidLUT.NanColor = [1.0, 0.0, 0.0]\n", + "solidLUT.NumberOfTableValues = 10\n", + "solidLUT.ScalarRangeInitialized = 1.0\n", + "solidPWF = pv.GetOpacityTransferFunction('Solid')\n", + "solidPWF.Points = [-0.14285714285714285, 0.0, 0.5, 0.0, 500.0, 0.0, 0.5, 0.0, 1000.0, 1.0, 0.5, 0.0]\n", + "solidPWF.ScalarRangeInitialized = 1\n", + "\n", + "# speedTF2D = pv.GetTransferFunction2D('Speed')\n", + "# speedTF2D.ScalarRangeInitialized = 1\n", + "# speedTF2D.Range = [0.0, 2.0, 0.0, 1.0]\n", + "# speedLUT = pv.GetColorTransferFunction('Speed')\n", + "# speedLUT.AutomaticRescaleRangeMode = 'Never'\n", + "# speedLUT.TransferFunction2D = speedTF2D\n", + "# speedLUT.RGBPoints = [0.0, 0.231373, 0.298039, 0.752941, 1.0, 0.865003, 0.865003, 0.865003, 2.0, 0.705882, 0.0156863, 0.14902]\n", + "# speedLUT.NumberOfTableValues = 10\n", + "# speedLUT.ScalarRangeInitialized = 1.0\n", + "# speedPWF = pv.GetOpacityTransferFunction('Speed')\n", + "# speedPWF.Points = [0.0, 0.0, 0.5, 0.0, 2.0, 1.0, 0.5, 0.0]\n", + "# speedPWF.ScalarRangeInitialized = 1\n", + "\n", + "# errorTF2D = pv.GetTransferFunction2D('Error')\n", + "# errorTF2D.ScalarRangeInitialized = 1\n", + "# errorLUT = pv.GetColorTransferFunction('Error')\n", + "# errorLUT.AutomaticRescaleRangeMode = 'Never'\n", + "# errorLUT.TransferFunction2D = errorTF2D\n", + "# errorLUT.RGBPoints = [0.0, 1.0, 1.0, 1.0, 3.0, 0.0, 0.0, 0.0]\n", + "# errorLUT.ColorSpace = 'RGB'\n", + "# errorLUT.NanColor = [1.0, 0.0, 0.0]\n", + "# errorLUT.NumberOfTableValues = 10\n", + "# errorLUT.ScalarRangeInitialized = 1.0\n", + "# errorPWF = pv.GetOpacityTransferFunction('Error')\n", + "# errorPWF.ScalarRangeInitialized = 1\n", + "\n", + "Display = pv.Show(gradient1, renderView1)\n", + "Display.ColorArrayName = ['POINTS', 'Vorticity']\n", + "Display.LookupTable = vorticityLUT\n", + "Display.ScalarOpacityFunction = vorticityPWF\n", + "\n", + "# streamTracer1Display = pv.Show(streamTracer1, renderView1, 'GeometryRepresentation')\n", + "# streamTracer1Display.ColorArrayName = ['POINTS', '']\n", + "# streamTracer1Display.ScaleTransferFunction.Points = [0.0, 0.0, 0.5, 0.0, 4.2148476072182355, 1.0, 0.5, 0.0]\n", + "\n", + "# calculator3Display = pv.Show(calculator3, renderView1, 'StructuredGridRepresentation')\n", + "# calculator3Display.ColorArrayName = ['POINTS', 'Speed']\n", + "# calculator3Display.LookupTable = speedLUT\n", + "# calculator3Display.ScalarOpacityFunction = speedPWF\n", + "\n", + "calculator1Display = pv.Show(calculator1, renderView1, 'StructuredGridRepresentation')\n", + "calculator1Display.ColorArrayName = ['POINTS', 'Sensor']\n", + "calculator1Display.LookupTable = sensorLUT\n", + "calculator1Display.ScalarOpacityFunction = sensorPWF\n", + "\n", + "calculator2Display = pv.Show(calculator2, renderView1, 'StructuredGridRepresentation')\n", + "calculator2Display.ColorArrayName = ['POINTS', 'Solid']\n", + "calculator2Display.LookupTable = solidLUT\n", + "calculator2Display.ScalarOpacityFunction = solidPWF\n", + "\n", + "# calculator3Display = pv.Show(calculator3, renderView1, 'StructuredGridRepresentation')\n", + "# calculator3Display.ColorArrayName = ['POINTS', 'Error']\n", + "# calculator3Display.LookupTable = errorLUT\n", + "# calculator3Display.ScalarOpacityFunction = errorPWF\n", + "\n", + "# contour1Display = pv.Show(contour1, renderView1, 'GeometryRepresentation')\n", + "# contour1Display.AmbientColor = [0.0, 0.7843137254901961, 0.0]\n", + "# contour1Display.ColorArrayName = ['POINTS', '']\n", + "# contour1Display.DiffuseColor = [0.0, 0.7843137254901961, 0.0]\n", + "# contour1Display.LineWidth = 2.0\n", + "\n", + "# contour2Display = pv.Show(contour2, renderView1, 'GeometryRepresentation')\n", + "# contour2Display.AmbientColor = [0.0, 0.0, 0.0]\n", + "# contour2Display.ColorArrayName = [None, '']\n", + "# contour2Display.DiffuseColor = [0.0, 0.0, 0.0]\n", + "# contour2Display.LineWidth = 2.0\n", + "\n", + "glyph1Display = pv.Show(glyph1, renderView1, 'GeometryRepresentation')\n", + "\n", + "# trace defaults for the display properties.\n", + "glyph1Display.Representation = 'Surface'\n", + "glyph1Display.AmbientColor = [0.0, 1.0, 0.0]\n", + "glyph1Display.ColorArrayName = ['POINTS', '']\n", + "glyph1Display.DiffuseColor = [0.0, 1.0, 0.0]\n", + "glyph1Display.SelectTCoordArray = 'None'\n", + "glyph1Display.SelectNormalArray = 'None'\n", + "glyph1Display.SelectTangentArray = 'None'\n", + "glyph1Display.OSPRayScaleArray = 'Result'\n", + "glyph1Display.OSPRayScaleFunction = 'PiecewiseFunction'\n", + "glyph1Display.SelectOrientationVectors = 'None'\n", + "glyph1Display.ScaleFactor = 51.100000000001\n", + "glyph1Display.SelectScaleArray = 'Result'\n", + "glyph1Display.GlyphType = 'Arrow'\n", + "glyph1Display.GlyphTableIndexArray = 'Result'\n", + "glyph1Display.GaussianRadius = 2.55500000000005\n", + "glyph1Display.SetScaleArray = ['POINTS', 'Result']\n", + "glyph1Display.ScaleTransferFunction = 'PiecewiseFunction'\n", + "glyph1Display.OpacityArray = ['POINTS', 'Result']\n", + "glyph1Display.OpacityTransferFunction = 'PiecewiseFunction'\n", + "glyph1Display.DataAxesGrid = 'GridAxesRepresentation'\n", + "glyph1Display.PolarAxes = 'PolarAxesRepresentation'\n", + "glyph1Display.SelectInputVectors = ['POINTS', 'Vector']\n", + "glyph1Display.WriteLog = ''" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pv.Render()\n", + "renderView1.CameraParallelScale = 240\n", + "# pv.SaveScreenshot(os.path.join(parent_dir, \"output\", \"250729\", folder, outputname + \".png\"), view=renderView1)\n", + "pv.SaveScreenshot(os.path.join(parent_dir, \"output/250729/test.png\"), view=renderView1)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "scene = pv.GetAnimationScene()\n", + "scene.NumberOfFrames = 150\n", + "pv.SaveAnimation(filename=os.path.join(parent_dir, \"output\", \"250729\", \"anim\", \"anim.png\"), viewOrLayout=renderView1, SuffixFormat='.%03d')" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "找到文件: ['/home/frank14f/Frank_LBM/output/250729/anim/frame.00.png', '/home/frank14f/Frank_LBM/output/250729/anim/frame.01.png', '/home/frank14f/Frank_LBM/output/250729/anim/frame.02.png', '/home/frank14f/Frank_LBM/output/250729/anim/frame.03.png', '/home/frank14f/Frank_LBM/output/250729/anim/frame.04.png']\n" + ] + } + ], + "source": [ + "import glob\n", + "\n", + "# 列出目录下所有匹配文件\n", + "dir_path = os.path.join(parent_dir, \"output\", \"250729\", \"anim\")\n", + "files = sorted(glob.glob(os.path.join(dir_path, \"frame.[0-9][0-9].png\")))\n", + "print(\"找到文件:\", files[:5]) # 打印前5个文件" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "ffmpeg version 4.2.2 Copyright (c) 2000-2019 the FFmpeg developers\n", + " built with gcc 7.3.0 (crosstool-NG 1.23.0.449-a04d0)\n", + " configuration: --prefix=/tmp/build/80754af9/ffmpeg_1587154242452/_h_env_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placeho --cc=/tmp/build/80754af9/ffmpeg_1587154242452/_build_env/bin/x86_64-conda_cos6-linux-gnu-cc --disable-doc --enable-avresample --enable-gmp --enable-hardcoded-tables --enable-libfreetype --enable-libvpx --enable-pthreads --enable-libopus --enable-postproc --enable-pic --enable-pthreads --enable-shared --enable-static --enable-version3 --enable-zlib --enable-libmp3lame --disable-nonfree --enable-gpl --enable-gnutls --disable-openssl --enable-libopenh264 --enable-libx264\n", + " libavutil 56. 31.100 / 56. 31.100\n", + " libavcodec 58. 54.100 / 58. 54.100\n", + " libavformat 58. 29.100 / 58. 29.100\n", + " libavdevice 58. 8.100 / 58. 8.100\n", + " libavfilter 7. 57.100 / 7. 57.100\n", + " libavresample 4. 0. 0 / 4. 0. 0\n", + " libswscale 5. 5.100 / 5. 5.100\n", + " libswresample 3. 5.100 / 3. 5.100\n", + " libpostproc 55. 5.100 / 55. 5.100\n", + "Input #0, image2, from '/home/frank14f/Frank_LBM/output/250729/anim/anim.%03d.png':\n", + " Duration: 00:00:15.00, start: 0.000000, bitrate: N/A\n", + " Stream #0:0: Video: png, rgb24(pc), 1200x480, 10 fps, 10 tbr, 10 tbn, 10 tbc\n", + "Stream mapping:\n", + " Stream #0:0 -> #0:0 (png (native) -> h264 (libx264))\n", + "Press [q] to stop, [?] for help\n", + "[libx264 @ 0x56b63b5f6700] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2\n", + "[libx264 @ 0x56b63b5f6700] profile High, level 3.1, 4:2:0, 8-bit\n", + "[libx264 @ 0x56b63b5f6700] 264 - core 157 - H.264/MPEG-4 AVC codec - Copyleft 2003-2018 - http://www.videolan.org/x264.html - options: cabac=1 ref=3 deblock=1:0:0 analyse=0x3:0x113 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=-2 threads=15 lookahead_threads=2 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 weightb=1 open_gop=0 weightp=2 keyint=250 keyint_min=10 scenecut=40 intra_refresh=0 rc_lookahead=40 rc=crf mbtree=1 crf=23.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00\n", + "Output #0, mp4, to '/home/frank14f/Frank_LBM/output/250729/erase2.mp4':\n", + " Metadata:\n", + " encoder : Lavf58.29.100\n", + " Stream #0:0: Video: h264 (libx264) (avc1 / 0x31637661), yuv420p, 1200x480, q=-1--1, 10 fps, 10240 tbn, 10 tbc\n", + " Metadata:\n", + " encoder : Lavc58.54.100 libx264\n", + " Side data:\n", + " cpb: bitrate max/min/avg: 0/0/0 buffer size: 0 vbv_delay: -1\n", + "frame= 150 fps=124 q=-1.0 Lsize= 567kB time=00:00:14.70 bitrate= 316.2kbits/s speed=12.2x \n", + "video:565kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.452751%\n", + "[libx264 @ 0x56b63b5f6700] frame I:1 Avg QP:13.03 size: 16821\n", + "[libx264 @ 0x56b63b5f6700] frame P:40 Avg QP:20.97 size: 5629\n", + "[libx264 @ 0x56b63b5f6700] frame B:109 Avg QP:24.12 size: 3081\n", + "[libx264 @ 0x56b63b5f6700] consecutive B-frames: 1.3% 4.0% 4.0% 90.7%\n", + "[libx264 @ 0x56b63b5f6700] mb I I16..4: 50.2% 31.6% 18.2%\n", + "[libx264 @ 0x56b63b5f6700] mb P I16..4: 0.7% 4.1% 1.5% P16..4: 12.0% 5.7% 4.1% 0.0% 0.0% skip:71.9%\n", + "[libx264 @ 0x56b63b5f6700] mb B I16..4: 0.2% 0.2% 0.3% B16..8: 12.5% 5.8% 2.5% direct: 0.8% skip:77.7% L0:47.6% L1:45.5% BI: 6.9%\n", + "[libx264 @ 0x56b63b5f6700] 8x8 transform intra:51.7% inter:33.5%\n", + "[libx264 @ 0x56b63b5f6700] coded y,uvDC,uvAC intra: 20.4% 35.2% 32.4% inter: 3.2% 4.4% 2.5%\n", + "[libx264 @ 0x56b63b5f6700] i16 v,h,dc,p: 60% 36% 5% 0%\n", + "[libx264 @ 0x56b63b5f6700] i8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 9% 6% 78% 1% 1% 1% 2% 1% 1%\n", + "[libx264 @ 0x56b63b5f6700] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 19% 29% 28% 4% 4% 4% 6% 3% 4%\n", + "[libx264 @ 0x56b63b5f6700] i8c dc,h,v,p: 62% 23% 12% 4%\n", + "[libx264 @ 0x56b63b5f6700] Weighted P-Frames: Y:0.0% UV:0.0%\n", + "[libx264 @ 0x56b63b5f6700] ref P L0: 48.5% 1.6% 16.2% 33.7%\n", + "[libx264 @ 0x56b63b5f6700] ref B L0: 86.1% 7.6% 6.3%\n", + "[libx264 @ 0x56b63b5f6700] ref B L1: 97.4% 2.6%\n", + "[libx264 @ 0x56b63b5f6700] kb/s:308.15\n" + ] + }, + { + "data": { + "text/plain": [ + "(None, None)" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import ffmpeg\n", + "input_pattern = os.path.join(parent_dir, \"output\", \"250729\", \"anim\", \"anim.%03d.png\")\n", + "output_file = os.path.join(parent_dir, \"output\", \"250729\", \"erase2.mp4\")\n", + "(\n", + " ffmpeg\n", + " .input(input_pattern, framerate=10)\n", + " .output(output_file, vcodec=\"libx264\", pix_fmt=\"yuv420p\", vframes=150)\n", + " .run()\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "pycuda_3_10", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.13" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/src/drl_pinball/legacy_test/uni_test.ipynb b/src/drl_pinball/legacy_test/uni_test.ipynb new file mode 100644 index 0000000..339023f --- /dev/null +++ b/src/drl_pinball/legacy_test/uni_test.ipynb @@ -0,0 +1,2828 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "381b36b2", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import Tuple, Union\n", + "from collections import deque\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "from stable_baselines3 import PPO\n", + "import pycuda.driver as cuda\n", + "import pandas as pd\n", + "import pickle\n", + "import sys\n", + "import os\n", + "from gym_dummy import CustomEnv as DummyEnv\n", + "\n", + "current_dir = os.path.dirname(os.path.abspath(\"__file__\"))\n", + "parent_dir = os.path.abspath(os.path.join(current_dir, os.pardir))\n", + "sys.path.append(parent_dir)\n", + "\n", + "from CelerisLab import FlowField\n", + "from CelerisLab import utils\n", + "\n", + "env_12 = DummyEnv(s_dim=12)\n", + "env_14 = DummyEnv(s_dim=14)\n", + "model_cloak_re100 = PPO.load(os.path.join(parent_dir, \"models\", \"old\", \"d1a3o12_re100.zip\"), env=env_12, device=\"cuda:0\")\n", + "model_illusion = PPO.load(os.path.join(parent_dir, \"models\", \"250525\", \"d1a3o14_250525_imit_1L_2U_600S.zip\"), env=env_14, device=\"cuda:0\")\n", + "model_illusion_075L = PPO.load(os.path.join(parent_dir, \"models\", \"250525\", \"d1a3o14_250525_imit_075L_2U_400S.zip\"), env=env_14, device=\"cuda:0\")\n", + "model_illusion_15L = PPO.load(os.path.join(parent_dir, \"models\", \"250525\", \"d1a3o14_250525_imit_15L_2U.zip\"), env=env_14, device=\"cuda:0\")\n", + "model_erase = PPO.load(os.path.join(parent_dir, \"models\", \"250729\", \"d1a3o12_250729_250326_erase_250804_20D_retrain2.zip\"), env=env_12, device=\"cuda:0\")\n", + "model_cloak_lamb = PPO.load(os.path.join(parent_dir, \"models\", \"old\", \"vortex_lamb.zip\"), env=env_12, device=\"cuda:0\")\n", + "model_cloak_taylor = PPO.load(os.path.join(parent_dir, \"models\", \"old\", \"vortex_taylor.zip\"), env=env_12, device=\"cuda:0\")\n", + "\n", + "model_cloak_re100.set_random_seed(0)\n", + "model_illusion.set_random_seed(19)\n", + "model_illusion_075L.set_random_seed(19)\n", + "model_illusion_15L.set_random_seed(19)\n", + "model_erase.set_random_seed(19)\n", + "model_cloak_lamb.set_random_seed(0)\n", + "model_cloak_taylor.set_random_seed(0)\n", + "\n", + "cuda.init()\n", + "context = cuda.Device(0).make_context()\n", + "config_cuda = utils.load_cuda_config(os.path.join(parent_dir, \"configs\", \"config_cuda.json\"))\n", + "config_field = utils.load_flow_field_config(os.path.join(parent_dir, \"configs\", \"config_flowfield.json\"))\n", + "\n", + "L0 = 20\n", + "U0 = config_field.velocity\n", + "DATA_TYPE = np.float32\n", + "CONV_LEN = 36\n", + "\n", + "context.push()\n", + "flow_field = FlowField(config_field, config_cuda, device_id=0)\n", + "NX = flow_field.FIELD_SHAPE[0]\n", + "NY = flow_field.FIELD_SHAPE[1]" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "a276c1b1", + "metadata": {}, + "outputs": [], + "source": [ + "def save_field(flow_field, filename):\n", + " NX = flow_field.FIELD_SHAPE[0]\n", + " NY = flow_field.FIELD_SHAPE[1]\n", + " flow_field.get_ddf()\n", + " ddf_plot = flow_field.ddf.copy().reshape((9, NY, NX)).transpose(2, 1, 0)\n", + " flag_plot = flow_field.flag.copy().reshape((NY, NX)).transpose(1, 0)\n", + " ux = (ddf_plot[:, :, 1] + ddf_plot[:, :, 5] + ddf_plot[:, :, 8] - ddf_plot[:, :, 3] - ddf_plot[:, :, 6] - ddf_plot[:, :, 7]) / U0\n", + " uy = (ddf_plot[:, :, 2] + ddf_plot[:, :, 5] + ddf_plot[:, :, 6] - ddf_plot[:, :, 4] - ddf_plot[:, :, 7] - ddf_plot[:, :, 8]) / U0\n", + " with open(os.path.join(parent_dir, \"output\", filename), \"w\") as f:\n", + " f.write(\"Title= \\\"LBM 2D\\\"\\r\\n\")\n", + " f.write(\"VARIABLES= \\\"X\\\",\\\"Y\\\",\\\"flag\\\",\\\"U\\\",\\\"V\\\",\\r\\n\")\n", + " f.write(f\"ZONE T= \\\"BOX\\\",I= {NX},J= {NY},F=POINT\\r\\n\")\n", + " for j in range(NY):\n", + " for i in range(NX):\n", + " f.write(f\"{i},{j},{flag_plot[i, j]},{ux[i, j]},{uy[i, j]}\\r\\n\")\n", + "\n", + "class SimpleMeta:\n", + " pass\n", + "\n", + "def analyze_harmonics(states, n_harmonics):\n", + " N, D = states.shape\n", + " result = []\n", + " for d in range(D):\n", + " y = states[:, d]\n", + " fft_coef = np.fft.rfft(y)\n", + " freqs = np.fft.rfftfreq(N, d=1)\n", + " amps = 2 * np.abs(fft_coef) / N\n", + " phases = np.angle(fft_coef)\n", + " idx = np.argsort(amps[1:])[::-1][:n_harmonics] + 1\n", + " harmonics = {\n", + " 'dc': np.real(fft_coef[0]) / N,\n", + " 'amps': amps[idx],\n", + " 'freqs': freqs[idx],\n", + " 'phases': phases[idx]\n", + " }\n", + " result.append(harmonics)\n", + " return result" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "751ba334", + "metadata": {}, + "outputs": [], + "source": [ + "target_states = np.empty((0, 6), dtype=DATA_TYPE)\n", + "meta_cloak_steady = SimpleMeta()\n", + "meta_cloak_dipole = SimpleMeta()\n", + "meta_cloak_monopole = SimpleMeta()\n", + "meta_illusion = SimpleMeta()\n", + "meta_cloak_karman = SimpleMeta()\n", + "\n", + "center: Tuple[float, float, float] = (40 * L0, (NY - 1) / 2 + 2 * L0, 0)\n", + "flow_field.add_sensor(center, L0 / 4)\n", + "center: Tuple[float, float, float] = (40 * L0, (NY - 1) / 2, 0)\n", + "flow_field.add_sensor(center, L0 / 4)\n", + "center: Tuple[float, float, float] = (40 * L0, (NY - 1) / 2 - 2 * L0, 0)\n", + "flow_field.add_sensor(center, L0 / 4)\n", + "flow_field.run(int(2*NX/U0), np.zeros(3, dtype=DATA_TYPE))\n", + "\n", + "for i in range(150):\n", + " flow_field.run(600, np.zeros(3, dtype=DATA_TYPE))\n", + " new_state = flow_field.obs.copy()[0:6]\n", + " target_states = np.vstack((target_states, new_state))\n", + "\n", + "meta_cloak_steady.target_states = np.mean(target_states, axis=0)\n", + "\n", + "# save_field(flow_field, os.path.join(parent_dir, \"output\", \"250823\", \"data\", \"target_steady.dat\"))\n", + "\n", + "target_states = np.empty((0, 6), dtype=DATA_TYPE)\n", + "flow_field.get_ddf()\n", + "flow_field.save_ddf()\n", + "\n", + "center_vor: Tuple[float, float, float] = (15 * L0, (NY - 1) / 2, 0)\n", + "flow_field.add_vortex(center_vor, L0 * 2, 0.5*U0, 0, \"lamb\")\n", + "\n", + "for i in range(150):\n", + " flow_field.run(800, np.zeros(3, dtype=DATA_TYPE))\n", + " new_state = flow_field.obs.copy()[0:6]\n", + " target_states = np.vstack((target_states, new_state))\n", + "\n", + "meta_cloak_dipole.target_states = np.mean(target_states, axis=0)\n", + "# flow_field.restore_ddf()\n", + "# flow_field.apply_ddf()\n", + "# flow_field.add_vortex(center_vor, L0 * 2, 0.5*U0, 0, \"lamb\")\n", + "\n", + "# for i in range(100):\n", + "# flow_field.run(1000, np.zeros(3, dtype=DATA_TYPE))\n", + "# file_name = f\"target_lamb.{i:03d}\"\n", + "# save_field(flow_field, os.path.join(parent_dir, \"output\", \"250823\", \"data\", file_name))\n", + "\n", + "target_states = np.empty((0, 6), dtype=DATA_TYPE)\n", + "flow_field.restore_ddf()\n", + "flow_field.apply_ddf()\n", + "flow_field.add_vortex(center_vor, L0 * 2, 0.03*U0, 0, \"taylor\")\n", + "\n", + "for i in range(150):\n", + " flow_field.run(800, np.zeros(3, dtype=DATA_TYPE))\n", + " new_state = flow_field.obs.copy()[0:6]\n", + " target_states = np.vstack((target_states, new_state))\n", + "\n", + "meta_cloak_monopole.target_states = np.mean(target_states, axis=0)\n", + "# flow_field.restore_ddf()\n", + "# flow_field.apply_ddf()\n", + "# flow_field.add_vortex(center_vor, L0 * 2, 0.03*U0, 0, \"taylor\")\n", + "\n", + "# for i in range(100):\n", + "# flow_field.run(1000, np.zeros(3, dtype=DATA_TYPE))\n", + "# file_name = f\"target_taylor.{i:03d}\"\n", + "# save_field(flow_field, os.path.join(parent_dir, \"output\", \"250823\", \"data\", file_name))\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "5a23560c", + "metadata": {}, + "outputs": [], + "source": [ + "target_states = np.empty((0, 6), dtype=DATA_TYPE)\n", + "fifo_states = deque(maxlen=150)\n", + "\n", + "flow_field.restore_ddf()\n", + "flow_field.apply_ddf()\n", + "center: Tuple[float, float, float] = (30 * L0, (NY - 1) / 2, 0)\n", + "flow_field.add_cylinder(center, L0 / 2)\n", + "center: Tuple[float, float, float] = (31.3 * L0, (NY - 1) / 2 + 0.75 * L0, 0)\n", + "flow_field.add_cylinder(center, L0 / 2)\n", + "center: Tuple[float, float, float] = (31.3 * L0, (NY - 1) / 2 - 0.75 * L0, 0)\n", + "flow_field.add_cylinder(center, L0 / 2)\n", + "flow_field.run(int(4*NX/U0), np.zeros(6, dtype=DATA_TYPE))\n", + "flow_field.get_ddf()\n", + "flow_field.save_ddf()\n", + "\n", + "for i in range(150):\n", + " flow_field.run(600, np.zeros(6, dtype=DATA_TYPE))\n", + " fifo_states.append(flow_field.obs.copy()[0:12])\n", + "\n", + "temp_states = np.array(fifo_states)\n", + "meta_illusion.force_norm_fact = 6 * np.max(np.abs(temp_states[:, 6:12]))\n", + "\n", + "meta_illusion.sens_deviation = np.zeros(6, dtype=DATA_TYPE)\n", + "meta_illusion.sens_norm_fact = np.zeros(6, dtype=DATA_TYPE)\n", + "for i in range(6):\n", + " meta_illusion.sens_deviation[i] = np.mean(temp_states[:, i])\n", + " meta_illusion.sens_norm_fact[i] = 5 * np.max(np.abs(temp_states[:, i] - meta_illusion.sens_deviation[i]))\n", + "\n", + "fifo_states = deque(maxlen=150)\n", + "flow_field.restore_ddf()\n", + "flow_field.apply_ddf()\n", + "flow_field.run(int(2*NX/U0), np.array([0.0, 0.0, 0.0, 0.0, -5*U0, 5*U0], dtype=DATA_TYPE))\n", + "flow_field.add_vortex(center_vor, L0 * 2, 0.5*U0, 0, \"lamb\")\n", + "\n", + "for i in range(150):\n", + " flow_field.run(800, np.zeros(6, dtype=DATA_TYPE))\n", + " fifo_states.append(flow_field.obs.copy()[0:12])\n", + "\n", + "temp_states = np.array(fifo_states)\n", + "meta_cloak_dipole.force_norm_fact = 6 * np.max(np.abs(temp_states[:, 6:12]))\n", + "\n", + "meta_cloak_dipole.sens_deviation = np.zeros(6, dtype=DATA_TYPE)\n", + "meta_cloak_dipole.sens_norm_fact = np.zeros(6, dtype=DATA_TYPE)\n", + "for i in range(6):\n", + " meta_cloak_dipole.sens_deviation[i] = np.mean(temp_states[:, i])\n", + " meta_cloak_dipole.sens_norm_fact[i] = 5 * np.max(np.abs(temp_states[:, i] - meta_cloak_dipole.sens_deviation[i]))\n", + "\n", + "fifo_states = deque(maxlen=150)\n", + "flow_field.restore_ddf()\n", + "flow_field.apply_ddf()\n", + "flow_field.run(int(2*NX/U0), np.array([0.0, 0.0, 0.0, 0.0, -5*U0, 5*U0], dtype=DATA_TYPE))\n", + "flow_field.add_vortex(center_vor, L0 * 2, 0.03*U0, 0, \"taylor\")\n", + "\n", + "for i in range(150):\n", + " flow_field.run(800, np.zeros(6, dtype=DATA_TYPE))\n", + " fifo_states.append(flow_field.obs.copy()[0:12])\n", + "\n", + "temp_states = np.array(fifo_states)\n", + "meta_cloak_monopole.force_norm_fact = 6 * np.max(np.abs(temp_states[:, 6:12]))\n", + "\n", + "meta_cloak_monopole.sens_deviation = np.zeros(6, dtype=DATA_TYPE)\n", + "meta_cloak_monopole.sens_norm_fact = np.zeros(6, dtype=DATA_TYPE)\n", + "for i in range(6):\n", + " meta_cloak_monopole.sens_deviation[i] = np.mean(temp_states[:, i])\n", + " meta_cloak_monopole.sens_norm_fact[i] = 5 * np.max(np.abs(temp_states[:, i] - meta_cloak_monopole.sens_deviation[i]))" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "fc50665e", + "metadata": {}, + "outputs": [], + "source": [ + "fifo_states = deque(maxlen=150)\n", + "\n", + "flow_field.restore_ddf()\n", + "flow_field.apply_ddf()\n", + "center: Tuple[float, float, float] = (10 * L0, (NY - 1) / 2, 0)\n", + "flow_field.add_cylinder(center, 1*L0)\n", + "flow_field.run(int(4*NX/U0), np.zeros(7, dtype=DATA_TYPE))\n", + "\n", + "for i in range(150):\n", + " flow_field.run(800, np.zeros(7, dtype=DATA_TYPE))\n", + " fifo_states.append(flow_field.obs.copy()[0:12])\n", + "\n", + "temp_states = np.array(fifo_states)\n", + "meta_cloak_karman.force_norm_fact = 6 * np.max(np.abs(temp_states[:, 6:12]))\n", + "\n", + "meta_cloak_karman.sens_deviation = np.zeros(6, dtype=DATA_TYPE)\n", + "meta_cloak_karman.sens_norm_fact = np.zeros(6, dtype=DATA_TYPE)\n", + "for i in range(6):\n", + " meta_cloak_karman.sens_deviation[i] = np.mean(temp_states[:, i])\n", + " meta_cloak_karman.sens_norm_fact[i] = 5 * np.max(np.abs(temp_states[:, i] - meta_cloak_karman.sens_deviation[i]))" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "a5eee254", + "metadata": {}, + "outputs": [], + "source": [ + "del flow_field\n", + "\n", + "flow_field = FlowField(config_field, config_cuda, device_id=0)\n", + "center: Tuple[float, float, float] = (10 * L0, (NY - 1) / 2, 0)\n", + "flow_field.add_cylinder(center, 1*L0)\n", + "center: Tuple[float, float, float] = (40 * L0, (NY - 1) / 2 + 2 * L0, 0)\n", + "flow_field.add_sensor(center, L0 / 4)\n", + "center: Tuple[float, float, float] = (40 * L0, (NY - 1) / 2, 0)\n", + "flow_field.add_sensor(center, L0 / 4)\n", + "center: Tuple[float, float, float] = (40 * L0, (NY - 1) / 2 - 2 * L0, 0)\n", + "flow_field.add_sensor(center, L0 / 4)\n", + "flow_field.run(int(4*NX/U0), np.zeros(4, dtype=DATA_TYPE))\n", + "\n", + "target_states = np.empty((0, 6), dtype=DATA_TYPE)\n", + "\n", + "for i in range(150):\n", + " flow_field.run(800, np.zeros(4, dtype=DATA_TYPE))\n", + " new_state = flow_field.obs.copy()[2:8]\n", + " target_states = np.vstack((target_states, new_state))\n", + "\n", + "meta_cloak_karman.target_states = target_states\n", + "\n", + "# for i in range(100):\n", + "# flow_field.run(1000, np.zeros(4, dtype=DATA_TYPE))\n", + "# file_name = f\"target_karman.{i:03d}\"\n", + "# save_field(flow_field, os.path.join(parent_dir, \"output\", \"250823\", \"data\", file_name))" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "feb7c904", + "metadata": {}, + "outputs": [], + "source": [ + "del flow_field\n", + "\n", + "flow_field = FlowField(config_field, config_cuda, device_id=0)\n", + "center: Tuple[float, float, float] = (31 * L0, (NY - 1) / 2, 0)\n", + "flow_field.add_cylinder(center, 1*L0)\n", + "center: Tuple[float, float, float] = (40 * L0, (NY - 1) / 2 + 2 * L0, 0)\n", + "flow_field.add_sensor(center, L0 / 4)\n", + "center: Tuple[float, float, float] = (40 * L0, (NY - 1) / 2, 0)\n", + "flow_field.add_sensor(center, L0 / 4)\n", + "center: Tuple[float, float, float] = (40 * L0, (NY - 1) / 2 - 2 * L0, 0)\n", + "flow_field.add_sensor(center, L0 / 4)\n", + "flow_field.run(int(4*NX/U0), np.zeros(4, dtype=DATA_TYPE))\n", + "\n", + "target_states = np.empty((0, 8), dtype=DATA_TYPE)\n", + "\n", + "for i in range(150):\n", + " flow_field.run(800, np.zeros(4, dtype=DATA_TYPE))\n", + " new_state = flow_field.obs.copy()[0:8]\n", + " target_states = np.vstack((target_states, new_state))\n", + "\n", + "meta_illusion.target_states_1L = target_states\n", + "meta_illusion.target_harmonics_1L = analyze_harmonics(target_states, n_harmonics=5)\n", + "\n", + "# for i in range(100):\n", + "# flow_field.run(1000, np.zeros(4, dtype=DATA_TYPE))\n", + "# file_name = f\"target_1L.{i:03d}\"\n", + "# save_field(flow_field, os.path.join(parent_dir, \"output\", \"250823\", \"data\", file_name))" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "573cda50", + "metadata": {}, + "outputs": [], + "source": [ + "del flow_field\n", + "\n", + "flow_field = FlowField(config_field, config_cuda, device_id=0)\n", + "center: Tuple[float, float, float] = (31 * L0, (NY - 1) / 2, 0)\n", + "flow_field.add_cylinder(center, 0.75*L0)\n", + "center: Tuple[float, float, float] = (40 * L0, (NY - 1) / 2 + 2 * L0, 0)\n", + "flow_field.add_sensor(center, L0 / 4)\n", + "center: Tuple[float, float, float] = (40 * L0, (NY - 1) / 2, 0)\n", + "flow_field.add_sensor(center, L0 / 4)\n", + "center: Tuple[float, float, float] = (40 * L0, (NY - 1) / 2 - 2 * L0, 0)\n", + "flow_field.add_sensor(center, L0 / 4)\n", + "flow_field.run(int(4*NX/U0), np.zeros(4, dtype=DATA_TYPE))\n", + "\n", + "target_states = np.empty((0, 8), dtype=DATA_TYPE)\n", + "\n", + "for i in range(150):\n", + " flow_field.run(400, np.zeros(4, dtype=DATA_TYPE))\n", + " new_state = flow_field.obs.copy()[0:8]\n", + " target_states = np.vstack((target_states, new_state))\n", + "\n", + "meta_illusion.target_states_075L = target_states\n", + "meta_illusion.target_harmonics_075L = analyze_harmonics(target_states, n_harmonics=5)\n", + "\n", + "# for i in range(100):\n", + "# flow_field.run(1000, np.zeros(4, dtype=DATA_TYPE))\n", + "# file_name = f\"target_075L.{i:03d}\"\n", + "# save_field(flow_field, os.path.join(parent_dir, \"output\", \"250823\", \"data\", file_name))" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "56f4be7d", + "metadata": {}, + "outputs": [], + "source": [ + "del flow_field\n", + "\n", + "flow_field = FlowField(config_field, config_cuda, device_id=0)\n", + "center: Tuple[float, float, float] = (31 * L0, (NY - 1) / 2, 0)\n", + "flow_field.add_cylinder(center, 1.5*L0)\n", + "center: Tuple[float, float, float] = (40 * L0, (NY - 1) / 2 + 2 * L0, 0)\n", + "flow_field.add_sensor(center, L0 / 4)\n", + "center: Tuple[float, float, float] = (40 * L0, (NY - 1) / 2, 0)\n", + "flow_field.add_sensor(center, L0 / 4)\n", + "center: Tuple[float, float, float] = (40 * L0, (NY - 1) / 2 - 2 * L0, 0)\n", + "flow_field.add_sensor(center, L0 / 4)\n", + "flow_field.run(int(4*NX/U0), np.zeros(4, dtype=DATA_TYPE))\n", + "\n", + "target_states = np.empty((0, 8), dtype=DATA_TYPE)\n", + "\n", + "for i in range(150):\n", + " flow_field.run(800, np.zeros(4, dtype=DATA_TYPE))\n", + " new_state = flow_field.obs.copy()[0:8]\n", + " target_states = np.vstack((target_states, new_state))\n", + "\n", + "meta_illusion.target_states_15L = target_states\n", + "meta_illusion.target_harmonics_15L = analyze_harmonics(target_states, n_harmonics=5)\n", + "\n", + "# for i in range(100):\n", + "# flow_field.run(1000, np.zeros(4, dtype=DATA_TYPE))\n", + "# file_name = f\"target_15L.{i:03d}\"\n", + "# save_field(flow_field, os.path.join(parent_dir, \"output\", \"250823\", \"data\", file_name))" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "d30ec201", + "metadata": {}, + "outputs": [], + "source": [ + "del flow_field\n", + "\n", + "flow_field = FlowField(config_field, config_cuda, device_id=0)\n", + "center: Tuple[float, float, float] = (30 * L0, (NY - 1) / 2, 0)\n", + "flow_field.add_cylinder(center, L0 / 2)\n", + "center: Tuple[float, float, float] = (31.3 * L0, (NY - 1) / 2 + 0.75 * L0, 0)\n", + "flow_field.add_cylinder(center, L0 / 2)\n", + "center: Tuple[float, float, float] = (31.3 * L0, (NY - 1) / 2 - 0.75 * L0, 0)\n", + "flow_field.add_cylinder(center, L0 / 2)\n", + "center: Tuple[float, float, float] = (40 * L0, (NY - 1) / 2 + 2 * L0, 0)\n", + "flow_field.add_sensor(center, L0 / 4)\n", + "center: Tuple[float, float, float] = (40 * L0, (NY - 1) / 2, 0)\n", + "flow_field.add_sensor(center, L0 / 4)\n", + "center: Tuple[float, float, float] = (40 * L0, (NY - 1) / 2 - 2 * L0, 0)\n", + "flow_field.add_sensor(center, L0 / 4)\n", + "flow_field.run(int(4*NX/U0), np.zeros(6, dtype=DATA_TYPE))\n", + "\n", + "flow_field.get_ddf()\n", + "flow_field.save_ddf()" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "75309ab9", + "metadata": {}, + "outputs": [], + "source": [ + "# flow_field.restore_ddf()\n", + "# flow_field.apply_ddf()\n", + "fifo_states = deque(maxlen=150)\n", + "for i in range(100):\n", + " flow_field.run(1000, np.zeros(6, dtype=DATA_TYPE))\n", + " file_name = f\"act_nc.{i:03d}\"\n", + " # save_field(flow_field, os.path.join(parent_dir, \"output\", \"250823\", \"data\", file_name))\n", + " fifo_states.append(flow_field.obs.copy()[0:12])" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "608f0eec", + "metadata": {}, + "outputs": [], + "source": [ + "for i in range(75):\n", + " flow_field.run(1000, np.array([0.0, -5.1*U0, 5.1*U0, 0.0, 0.0, 0.0], dtype=DATA_TYPE))\n", + " file_name = f\"act_cloak_steady.{i:03d}\"\n", + " # save_field(flow_field, os.path.join(parent_dir, \"output\", \"250823\", \"data\", file_name))\n", + " fifo_states.append(flow_field.obs.copy()[0:12])" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "2999f0ee", + "metadata": {}, + "outputs": [], + "source": [ + "flow_field.get_ddf()\n", + "flow_field.save_ddf()" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "9c4b02f5", + "metadata": {}, + "outputs": [], + "source": [ + "flow_field.restore_ddf()\n", + "flow_field.apply_ddf()\n", + "flow_field.add_vortex(center_vor, L0 * 2, 0.5*U0, 0, \"lamb\")\n", + "\n", + "obs = np.zeros(12, dtype=np.float32)\n", + "for i in range(125):\n", + " action, _states = model_cloak_lamb.predict(observation=obs, deterministic=True)\n", + " temp = np.zeros(6, dtype=DATA_TYPE)\n", + " if i < 25:\n", + " temp_action = np.array(action*4 + [0, -4, 4], dtype=DATA_TYPE)\n", + " temp_transition = np.array([0.0, -5.1*U0, 5.1*U0], dtype=DATA_TYPE)\n", + " temp[0:3] = temp_action * U0 * (i/25) + temp_transition * (1 - i/25)\n", + " elif 45 <= i < 70:\n", + " temp_action = np.array(action*4 + [0, -4, 4], dtype=DATA_TYPE)\n", + " temp_transition = np.array([0.0, -5.1*U0, 5.1*U0], dtype=DATA_TYPE)\n", + " temp[0:3] = temp_action * U0 * (1-(i-45)/25) + temp_transition * ((i-45)/25)\n", + " elif i >= 70:\n", + " temp[0:3] = np.array([0.0, -5.1*U0, 5.1*U0], dtype=DATA_TYPE)\n", + " else:\n", + " temp_action = np.array(action*4 + [0, -4, 4], dtype=DATA_TYPE)\n", + " temp[0:3] = temp_action * U0\n", + " flow_field.run(800, temp)\n", + " states = np.array(flow_field.obs.copy()[0:12])\n", + " forces = states[0:6] / meta_cloak_dipole.force_norm_fact\n", + " cd = (forces[0] + forces[2] + forces[4]) / 3\n", + " cl = (forces[1] + forces[3] + forces[5]) / 3\n", + " sens = (states[6:12] - meta_cloak_dipole.sens_deviation) / meta_cloak_dipole.sens_norm_fact\n", + " obs = np.hstack([forces, sens])\n", + " file_name = f\"act_cloak_dipole.{i:03d}\"\n", + " save_field(flow_field, os.path.join(parent_dir, \"output\", \"250823\", \"data\", file_name))\n", + " fifo_states.append(flow_field.obs.copy()[0:12])" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "546e86c0", + "metadata": {}, + "outputs": [], + "source": [ + "flow_field.restore_ddf()\n", + "flow_field.apply_ddf()\n", + "flow_field.add_vortex(center_vor, L0 * 2, 0.03*U0, 0, \"taylor\")\n", + "\n", + "obs = np.zeros(12, dtype=np.float32)\n", + "for i in range(125):\n", + " action, _states = model_cloak_taylor.predict(observation=obs, deterministic=True)\n", + " temp = np.zeros(6, dtype=DATA_TYPE)\n", + " if i < 20:\n", + " temp_action = np.array(action*4 + [0, -4, 4], dtype=DATA_TYPE)\n", + " temp_transition = np.array([0.0, -5.1*U0, 5.1*U0], dtype=DATA_TYPE)\n", + " temp[0:3] = temp_action * U0 * (i/20) + temp_transition * (1 - i/20)\n", + " elif 45 <= i < 70:\n", + " temp_action = np.array(action*4 + [0, -4, 4], dtype=DATA_TYPE)\n", + " temp_transition = np.array([0.0, -5.1*U0, 5.1*U0], dtype=DATA_TYPE)\n", + " temp[0:3] = temp_action * U0 * (1-(i-45)/25) + temp_transition * ((i-45)/25)\n", + " elif i >= 70:\n", + " temp[0:3] = np.array([0.0, -5.1*U0, 5.1*U0], dtype=DATA_TYPE)\n", + " else:\n", + " temp_action = np.array(action*4 + [0, -4, 4], dtype=DATA_TYPE)\n", + " temp[0:3] = temp_action * U0\n", + " flow_field.run(800, temp)\n", + " states = np.array(flow_field.obs.copy()[0:12])\n", + " forces = states[0:6] / meta_cloak_monopole.force_norm_fact\n", + " cd = (forces[0] + forces[2] + forces[4]) / 3\n", + " cl = (forces[1] + forces[3] + forces[5]) / 3\n", + " sens = (states[6:12] - meta_cloak_monopole.sens_deviation) / meta_cloak_monopole.sens_norm_fact\n", + " obs = np.hstack([forces, sens])\n", + " file_name = f\"act_cloak_monopole.{i:03d}\"\n", + " save_field(flow_field, os.path.join(parent_dir, \"output\", \"250823\", \"data\", file_name))\n", + " fifo_states.append(flow_field.obs.copy()[0:12])" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "1f57113b", + "metadata": {}, + "outputs": [], + "source": [ + "def gen_target_states_at(t, harmonics):\n", + " t = np.asarray(t)\n", + " D = len(harmonics)\n", + " result = np.zeros((t.size, D), dtype=np.float32)\n", + " for d, h in enumerate(harmonics):\n", + " val = np.full(t.shape, h['dc'], dtype=np.float32)\n", + " for amp, freq, phase in zip(h['amps'], h['freqs'], h['phases']):\n", + " val += amp * np.cos(2 * np.pi * freq * t + phase)\n", + " result[:, d] = val\n", + " if result.shape[0] == 1:\n", + " return result[0]\n", + " return result" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "a7999510", + "metadata": {}, + "outputs": [], + "source": [ + "flow_field.restore_ddf()\n", + "flow_field.apply_ddf()\n", + "\n", + "obs = np.zeros(14, dtype=np.float32)\n", + "for i in range(200):\n", + " action, _states = model_illusion.predict(observation=obs, deterministic=True)\n", + " temp = np.zeros(6, dtype=DATA_TYPE)\n", + " if i < 10:\n", + " temp_action = np.array(action*8 + [0, -2, 2], dtype=DATA_TYPE)\n", + " temp_transition = np.array([0.0, -5.1*U0, 5.1*U0], dtype=DATA_TYPE)\n", + " temp[0:3] = temp_action * U0 * (i/10) + temp_transition * (1 - i/10)\n", + " else:\n", + " temp_action = np.array(action*8 + [0, -2, 2], dtype=DATA_TYPE)\n", + " temp[0:3] = temp_action * U0\n", + " flow_field.run(800, temp)\n", + " states = np.array(flow_field.obs.copy()[0:12])\n", + " forces = states[0:6] / meta_illusion.force_norm_fact\n", + " cd = (forces[0] + forces[2] + forces[4]) / 3\n", + " cl = (forces[1] + forces[3] + forces[5]) / 3\n", + " sens = (states[6:12] - meta_illusion.sens_deviation) / meta_illusion.sens_norm_fact\n", + " target_states = gen_target_states_at(i, meta_illusion.target_harmonics_1L)\n", + " target_cd = target_states[0] / meta_illusion.force_norm_fact\n", + " target_cl = target_states[1] / meta_illusion.force_norm_fact\n", + " obs = np.hstack([forces, sens, target_cd, target_cl])\n", + " file_name = f\"act_illusion_1L.{i:03d}\"\n", + " save_field(flow_field, os.path.join(parent_dir, \"output\", \"250823\", \"data\", file_name))\n", + " # if i % 2 == 0:\n", + " # index = i // 2\n", + " # file_name = f\"act_illusion_1L.{index:03d}\"\n", + " # save_field(flow_field, os.path.join(parent_dir, \"output\", \"250823\", \"data\", file_name))" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "65b31ee4", + "metadata": {}, + "outputs": [], + "source": [ + "# flow_field.apply_ddf()\n", + "\n", + "obs = np.zeros(14, dtype=np.float32)\n", + "for i in range(400):\n", + " action, _states = model_illusion_075L.predict(observation=obs, deterministic=True)\n", + " temp = np.zeros(6, dtype=DATA_TYPE)\n", + " if i < 20:\n", + " temp_action = np.array(action*8 + [0, -2, 2], dtype=DATA_TYPE)\n", + " temp_transition = np.array([0.0, -5.1*U0, 5.1*U0], dtype=DATA_TYPE)\n", + " temp[0:3] = temp_action * U0 * (i/10) + temp_transition * (1 - i/10)\n", + " else:\n", + " temp_action = np.array(action*8 + [0, -2, 2], dtype=DATA_TYPE)\n", + " temp[0:3] = temp_action * U0\n", + " flow_field.run(400, temp)\n", + " states = np.array(flow_field.obs.copy()[0:12])\n", + " forces = states[0:6] / meta_illusion.force_norm_fact\n", + " cd = (forces[0] + forces[2] + forces[4]) / 3\n", + " cl = (forces[1] + forces[3] + forces[5]) / 3\n", + " sens = (states[6:12] - meta_illusion.sens_deviation) / meta_illusion.sens_norm_fact\n", + " target_states = gen_target_states_at(i, meta_illusion.target_harmonics_075L)\n", + " target_cd = target_states[0] / meta_illusion.force_norm_fact\n", + " target_cl = target_states[1] / meta_illusion.force_norm_fact\n", + " obs = np.hstack([forces, sens, target_cd, target_cl])\n", + " if i % 2 == 0:\n", + " index = i // 2\n", + " file_name = f\"act_illusion_075L.{index:03d}\"\n", + " save_field(flow_field, os.path.join(parent_dir, \"output\", \"250823\", \"data\", file_name))" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "af362132", + "metadata": {}, + "outputs": [], + "source": [ + "# flow_field.apply_ddf()\n", + "\n", + "obs = np.zeros(14, dtype=np.float32)\n", + "for i in range(200):\n", + " action, _states = model_illusion_15L.predict(observation=obs, deterministic=True)\n", + " temp = np.zeros(6, dtype=DATA_TYPE)\n", + " if i < 10:\n", + " temp_action = np.array(action*8 + [0, -2, 2], dtype=DATA_TYPE)\n", + " temp_transition = np.array([0.0, -5.1*U0, 5.1*U0], dtype=DATA_TYPE)\n", + " temp[0:3] = temp_action * U0 * (i/10) + temp_transition * (1 - i/10)\n", + " else:\n", + " temp_action = np.array(action*8 + [0, -2, 2], dtype=DATA_TYPE)\n", + " temp[0:3] = temp_action * U0\n", + " flow_field.run(800, temp)\n", + " states = np.array(flow_field.obs.copy()[0:12])\n", + " forces = states[0:6] / meta_illusion.force_norm_fact\n", + " cd = (forces[0] + forces[2] + forces[4]) / 3\n", + " cl = (forces[1] + forces[3] + forces[5]) / 3\n", + " sens = (states[6:12] - meta_illusion.sens_deviation) / meta_illusion.sens_norm_fact\n", + " target_states = gen_target_states_at(i, meta_illusion.target_harmonics_15L)\n", + " target_cd = target_states[0] / meta_illusion.force_norm_fact\n", + " target_cl = target_states[1] / meta_illusion.force_norm_fact\n", + " obs = np.hstack([forces, sens, target_cd, target_cl])\n", + " file_name = f\"act_illusion_15L.{i:03d}\"\n", + " save_field(flow_field, os.path.join(parent_dir, \"output\", \"250823\", \"data\", file_name))" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "c1eed77f", + "metadata": {}, + "outputs": [], + "source": [ + "# center: Tuple[float, float, float] = (10 * L0, (NY - 1) / 2, 0)\n", + "# flow_field.add_cylinder(center, 1*L0)\n", + "flow_field.restore_ddf()\n", + "flow_field.apply_ddf()\n", + "\n", + "obs = np.zeros(12, dtype=np.float32)\n", + "for i in range(200):\n", + " action, _states = model_cloak_re100.predict(observation=obs, deterministic=True)\n", + " temp = np.zeros(7, dtype=DATA_TYPE)\n", + " if i < 10:\n", + " temp_action = np.array([0, 0, 0], dtype=DATA_TYPE)\n", + " temp_transition = np.array([0.0, -5.1*U0, 5.1*U0], dtype=DATA_TYPE)\n", + " temp[0:3] = temp_action * U0 * (i/10) + temp_transition * (1 - i/10)\n", + " else:\n", + " temp_action = np.array([0, 0, 0], dtype=DATA_TYPE)\n", + " temp[0:3] = temp_action * U0\n", + " flow_field.run(1000, temp)\n", + " states = np.array(flow_field.obs.copy()[0:12])\n", + " forces = states[0:6] / meta_cloak_karman.force_norm_fact\n", + " cd = (forces[0] + forces[2] + forces[4]) / 3\n", + " cl = (forces[1] + forces[3] + forces[5]) / 3\n", + " sens = (states[6:12] - meta_cloak_karman.sens_deviation) / meta_cloak_karman.sens_norm_fact\n", + " obs = np.hstack([forces, sens])\n", + " file_name = f\"act_karman_nc.{i:03d}\"\n", + " save_field(flow_field, os.path.join(parent_dir, \"output\", \"250823\", \"data\", file_name))\n", + "\n", + "for i in range(200):\n", + " action, _states = model_cloak_re100.predict(observation=obs, deterministic=True)\n", + " temp = np.zeros(7, dtype=DATA_TYPE)\n", + " if i < 10:\n", + " temp_action = np.array(action*8 + [0, -4, 4], dtype=DATA_TYPE)\n", + " temp_transition = np.array([0, 0, 0], dtype=DATA_TYPE)\n", + " temp[0:3] = temp_action * U0 * (i/10) + temp_transition * (1 - i/10)\n", + " else:\n", + " temp_action = np.array(action*8 + [0, -4, 4], dtype=DATA_TYPE)\n", + " temp[0:3] = temp_action * U0\n", + " flow_field.run(800, temp)\n", + " states = np.array(flow_field.obs.copy()[0:12])\n", + " forces = states[0:6] / meta_cloak_karman.force_norm_fact\n", + " cd = (forces[0] + forces[2] + forces[4]) / 3\n", + " cl = (forces[1] + forces[3] + forces[5]) / 3\n", + " sens = (states[6:12] - meta_cloak_karman.sens_deviation) / meta_cloak_karman.sens_norm_fact\n", + " obs = np.hstack([forces, sens])\n", + " file_name = f\"act_karman_cloak.{i:03d}\"\n", + " save_field(flow_field, os.path.join(parent_dir, \"output\", \"250823\", \"data\", file_name))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c8cb1e8", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "946a1212", + "metadata": {}, + "source": [ + "## POD 与 Z₂ 对称性分析(新增)\n", + "\n", + "这一部分用于你现有输出快照(`output/250823/data`)的离线分析,不需要重新跑 CFD。\n", + "\n", + "### 1) POD(Proper Orthogonal Decomposition)\n", + "对快照矩阵做均值去除后 SVD:\n", + "\n", + "$$\n", + "\\mathbf{X}' = \\mathbf{U}\\,\\mathbf{\\Sigma}\\,\\mathbf{V}^T\n", + "$$\n", + "\n", + "其中 POD 能量占比:\n", + "\n", + "$$\n", + "\\lambda_k = \\frac{\\sigma_k^2}{\\sum_i \\sigma_i^2}\n", + "$$\n", + "\n", + "累计能量:\n", + "\n", + "$$\n", + "C_K = \\sum_{k=1}^{K} \\lambda_k\n", + "$$\n", + "\n", + "### 2) Z₂ 对称性分析(关于中线 $y\\to -y$)\n", + "对速度场 $(u,v)$ 的反射算子定义为:\n", + "\n", + "$$\n", + "\\mathcal{R}(u,v) = (u(x,-y), -v(x,-y))\n", + "$$\n", + "\n", + "对称/反对称分解:\n", + "\n", + "$$\n", + "\\mathbf{u}_s = \\frac{\\mathbf{u}+\\mathcal{R}\\mathbf{u}}{2}, \\qquad\n", + "\\mathbf{u}_a = \\frac{\\mathbf{u}-\\mathcal{R}\\mathbf{u}}{2}\n", + "$$\n", + "\n", + "定义对称性破缺指数:\n", + "\n", + "$$\n", + "\\eta_{Z_2} = \\frac{\\|\\mathbf{u}_a\\|^2}{\\|\\mathbf{u}_s\\|^2 + \\|\\mathbf{u}_a\\|^2}\n", + "$$\n", + "\n", + "- $\\eta_{Z_2}\\approx 0$:近似镜像对称\n", + "- $\\eta_{Z_2}$ 越大:对称性破缺越明显" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "c251cc40", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "POD/Z2 helper functions ready.\n", + "Data dir: /home/frank14f/Frank_LBM/output/250823/data\n" + ] + } + ], + "source": [ + "import os\n", + "import re\n", + "import glob\n", + "from pathlib import Path\n", + "\n", + "import numpy as np\n", + "import pandas as pd\n", + "\n", + "\n", + "def _parse_zone_ij(file_path: str) -> tuple[int, int]:\n", + " with open(file_path, \"r\") as f:\n", + " _ = f.readline()\n", + " _ = f.readline()\n", + " zone_line = f.readline()\n", + " m_i = re.search(r\"I=\\s*(\\d+)\", zone_line)\n", + " m_j = re.search(r\"J=\\s*(\\d+)\", zone_line)\n", + " if m_i is None or m_j is None:\n", + " raise ValueError(f\"Cannot parse I/J in: {file_path}\")\n", + " return int(m_i.group(1)), int(m_j.group(1))\n", + "\n", + "\n", + "def load_tecplot_snapshot(file_path: str):\n", + " \"\"\"Load one snapshot saved by save_field().\n", + "\n", + " Returns\n", + " -------\n", + " flag, u, v: ndarray with shape (NX, NY)\n", + " \"\"\"\n", + " NX, NY = _parse_zone_ij(file_path)\n", + " df = pd.read_csv(\n", + " file_path,\n", + " skiprows=3,\n", + " header=None,\n", + " names=[\"i\", \"j\", \"flag\", \"u\", \"v\"],\n", + " )\n", + " arr = df[[\"flag\", \"u\", \"v\"]].to_numpy().reshape(NY, NX, 3)\n", + " flag = arr[:, :, 0].T\n", + " u = arr[:, :, 1].T\n", + " v = arr[:, :, 2].T\n", + " return flag, u, v\n", + "\n", + "\n", + "def collect_case_files(data_dir: str, prefix: str, n_snapshots: int | None = None):\n", + " files = sorted(glob.glob(str(Path(data_dir) / f\"{prefix}.*\")))\n", + " if n_snapshots is not None:\n", + " files = files[:n_snapshots]\n", + " if len(files) == 0:\n", + " raise FileNotFoundError(f\"No files found for prefix={prefix} in {data_dir}\")\n", + " return files\n", + "\n", + "\n", + "def build_snapshot_matrix(file_list: list[str]):\n", + " \"\"\"Build snapshot matrix X for POD from velocity fields.\n", + "\n", + " X shape: (n_features, n_snapshots)\n", + " n_features = 2 * n_fluid_points (u + v).\n", + " \"\"\"\n", + " flags = []\n", + " uv_fields = []\n", + "\n", + " for fp in file_list:\n", + " flag, u, v = load_tecplot_snapshot(fp)\n", + " flags.append(flag)\n", + " uv_fields.append((u, v))\n", + "\n", + " # Heuristic: the most frequent flag value is considered fluid.\n", + " first_flag = flags[0]\n", + " vals, cnts = np.unique(first_flag, return_counts=True)\n", + " fluid_flag = vals[np.argmax(cnts)]\n", + " fluid_mask = first_flag == fluid_flag\n", + "\n", + " cols = []\n", + " for u, v in uv_fields:\n", + " cols.append(np.hstack([u[fluid_mask], v[fluid_mask]]))\n", + "\n", + " X = np.stack(cols, axis=1)\n", + " return X, fluid_mask, uv_fields, fluid_flag\n", + "\n", + "\n", + "def pod_from_snapshots(X: np.ndarray):\n", + " \"\"\"Compute POD by SVD after mean subtraction.\"\"\"\n", + " Xc = X - X.mean(axis=1, keepdims=True)\n", + " U, S, Vt = np.linalg.svd(Xc, full_matrices=False)\n", + " eig = S**2\n", + " ratio = eig / np.sum(eig)\n", + " cumsum = np.cumsum(ratio)\n", + " return {\n", + " \"U\": U,\n", + " \"S\": S,\n", + " \"Vt\": Vt,\n", + " \"ratio\": ratio,\n", + " \"cumsum\": cumsum,\n", + " }\n", + "\n", + "\n", + "def z2_metrics_for_field(u: np.ndarray, v: np.ndarray, fluid_mask: np.ndarray):\n", + " \"\"\"Compute Z2 symmetry metrics for one snapshot.\n", + "\n", + " Reflection operator: R(u, v) = (u(x,-y), -v(x,-y)).\n", + " \"\"\"\n", + " u_ref = u[:, ::-1]\n", + " v_ref = -v[:, ::-1]\n", + "\n", + " u_sym = 0.5 * (u + u_ref)\n", + " v_sym = 0.5 * (v + v_ref)\n", + " u_anti = 0.5 * (u - u_ref)\n", + " v_anti = 0.5 * (v - v_ref)\n", + "\n", + " E_sym = np.sum(u_sym[fluid_mask] ** 2 + v_sym[fluid_mask] ** 2)\n", + " E_anti = np.sum(u_anti[fluid_mask] ** 2 + v_anti[fluid_mask] ** 2)\n", + "\n", + " eta_z2 = E_anti / (E_sym + E_anti + 1e-12)\n", + "\n", + " # Signed order parameter: mean cross-stream velocity (normalized by kinetic norm)\n", + " signed_m = np.sum(v[fluid_mask]) / np.sqrt(np.sum(u[fluid_mask] ** 2 + v[fluid_mask] ** 2) + 1e-12)\n", + "\n", + " return {\n", + " \"eta_z2\": float(eta_z2),\n", + " \"E_sym\": float(E_sym),\n", + " \"E_anti\": float(E_anti),\n", + " \"signed_m\": float(signed_m),\n", + " }\n", + "\n", + "\n", + "def analyze_case(data_dir: str, prefix: str, n_snapshots: int = 40):\n", + " files = collect_case_files(data_dir, prefix, n_snapshots=n_snapshots)\n", + " X, fluid_mask, uv_fields, fluid_flag = build_snapshot_matrix(files)\n", + "\n", + " pod = pod_from_snapshots(X)\n", + " z2 = [z2_metrics_for_field(u, v, fluid_mask) for (u, v) in uv_fields]\n", + "\n", + " eta = np.array([z[\"eta_z2\"] for z in z2])\n", + " signed_m = np.array([z[\"signed_m\"] for z in z2])\n", + "\n", + " return {\n", + " \"prefix\": prefix,\n", + " \"n_snapshots\": len(files),\n", + " \"fluid_flag\": float(fluid_flag),\n", + " \"pod_ratio\": pod[\"ratio\"],\n", + " \"pod_cumsum\": pod[\"cumsum\"],\n", + " \"eta_z2_series\": eta,\n", + " \"signed_m_series\": signed_m,\n", + " }\n", + "\n", + "\n", + "def summarize_case(result: dict):\n", + " r = result[\"pod_ratio\"]\n", + " c = result[\"pod_cumsum\"]\n", + " eta = result[\"eta_z2_series\"]\n", + " m = result[\"signed_m_series\"]\n", + "\n", + " return {\n", + " \"case\": result[\"prefix\"],\n", + " \"n\": result[\"n_snapshots\"],\n", + " \"POD_r1\": float(r[0]),\n", + " \"POD_r2\": float(r[1]) if len(r) > 1 else np.nan,\n", + " \"POD_r3\": float(r[2]) if len(r) > 2 else np.nan,\n", + " \"POD_c3\": float(c[2]) if len(c) > 2 else np.nan,\n", + " \"POD_c5\": float(c[4]) if len(c) > 4 else np.nan,\n", + " \"etaZ2_mean\": float(np.mean(eta)),\n", + " \"etaZ2_std\": float(np.std(eta)),\n", + " \"signed_m_mean\": float(np.mean(m)),\n", + " \"signed_m_std\": float(np.std(m)),\n", + " }\n", + "\n", + "\n", + "resolved_parent_dir = globals().get(\"parent_dir\")\n", + "if resolved_parent_dir is None:\n", + " # Fallback: notebook is under scripts/, so parent is workspace root.\n", + " resolved_parent_dir = os.path.abspath(os.path.join(os.getcwd(), os.pardir))\n", + "\n", + "DATA_DIR = os.path.join(resolved_parent_dir, \"output\", \"250823\", \"data\")\n", + "print(\"POD/Z2 helper functions ready.\")\n", + "print(\"Data dir:\", DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "f723b634", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "case", + "rawType": "object", + "type": "string" + }, + { + "name": "n", + "rawType": "int64", + "type": "integer" + }, + { + "name": "POD_r1", + "rawType": "float64", + "type": "float" + }, + { + "name": "POD_r2", + "rawType": "float64", + "type": "float" + }, + { + "name": "POD_r3", + "rawType": "float64", + "type": "float" + }, + { + "name": "POD_c3", + "rawType": "float64", + "type": "float" + }, + { + "name": "POD_c5", + "rawType": "float64", + "type": "float" + }, + { + "name": "etaZ2_mean", + "rawType": "float64", + "type": "float" + }, + { + "name": "etaZ2_std", + "rawType": "float64", + "type": "float" + }, + { + "name": "signed_m_mean", + "rawType": "float64", + "type": "float" + }, + { + "name": "signed_m_std", + "rawType": "float64", + "type": "float" + } + ], + "ref": "369f7cd9-34a1-48ac-aeb8-f0e2fb81ccb3", + "rows": [ + [ + "0", + "act_nc", + "20", + "0.5453104678544648", + "0.4184484628978619", + "0.01011473983280817", + "0.9738736705851349", + "0.9911362641227843", + "0.02712569493861817", + "0.0006030596241233723", + "0.010536330795236654", + "0.12530499612443965" + ], + [ + "1", + "act_cloak_steady", + "20", + "0.4846147427998569", + "0.39204425512169633", + "0.07097285712676774", + "0.9476318550483209", + "0.9775350644795309", + "0.024850363332746362", + "0.0021645245169620085", + "0.004568624042934281", + "0.11349791354083755" + ], + [ + "2", + "act_karman_nc", + "20", + "0.43071185973764003", + "0.4005729581129849", + "0.086936743836616", + "0.9182215616872409", + "0.9707082281716332", + "0.039163345627187277", + "0.005226781281080615", + "0.03873218521259392", + "0.22464298994564075" + ], + [ + "3", + "act_karman_cloak", + "20", + "0.47436163138736653", + "0.4175490471265246", + "0.07043821442543226", + "0.9623488929393234", + "0.986613473430638", + "0.043867790491814294", + "0.001102249057068186", + "-0.02289334335091659", + "0.1039502152755866" + ], + [ + "4", + "act_illusion_1L", + "20", + "0.7894457334235928", + "0.15231860942150405", + "0.04002898617863977", + "0.9817933290237366", + "0.996634746254787", + "0.00018755543194344526", + "0.0001171814014465266", + "-0.0013336041773132632", + "0.0047226859733438915" + ] + ], + "shape": { + "columns": 11, + "rows": 5 + } + }, + "text/html": [ + "

\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
casenPOD_r1POD_r2POD_r3POD_c3POD_c5etaZ2_meanetaZ2_stdsigned_m_meansigned_m_std
0act_nc200.5453100.4184480.0101150.9738740.9911360.0271260.0006030.0105360.125305
1act_cloak_steady200.4846150.3920440.0709730.9476320.9775350.0248500.0021650.0045690.113498
2act_karman_nc200.4307120.4005730.0869370.9182220.9707080.0391630.0052270.0387320.224643
3act_karman_cloak200.4743620.4175490.0704380.9623490.9866130.0438680.001102-0.0228930.103950
4act_illusion_1L200.7894460.1523190.0400290.9817930.9966350.0001880.000117-0.0013340.004723
\n", + "
" + ], + "text/plain": [ + " case n POD_r1 POD_r2 POD_r3 POD_c3 POD_c5 \\\n", + "0 act_nc 20 0.545310 0.418448 0.010115 0.973874 0.991136 \n", + "1 act_cloak_steady 20 0.484615 0.392044 0.070973 0.947632 0.977535 \n", + "2 act_karman_nc 20 0.430712 0.400573 0.086937 0.918222 0.970708 \n", + "3 act_karman_cloak 20 0.474362 0.417549 0.070438 0.962349 0.986613 \n", + "4 act_illusion_1L 20 0.789446 0.152319 0.040029 0.981793 0.996635 \n", + "\n", + " etaZ2_mean etaZ2_std signed_m_mean signed_m_std \n", + "0 0.027126 0.000603 0.010536 0.125305 \n", + "1 0.024850 0.002165 0.004569 0.113498 \n", + "2 0.039163 0.005227 0.038732 0.224643 \n", + "3 0.043868 0.001102 -0.022893 0.103950 \n", + "4 0.000188 0.000117 -0.001334 0.004723 " + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 你可以按需增减 case。这里先给一个与你当前结果最相关的集合。\n", + "cases = [\n", + " \"act_nc\", # 无控制基线(steady)\n", + " \"act_cloak_steady\", # steady cloaking\n", + " \"act_karman_nc\", # karman 来流无控制\n", + " \"act_karman_cloak\", # karman 来流 cloaking\n", + " \"act_illusion_1L\", # illusion (1.0L target)\n", + "]\n", + "\n", + "results = []\n", + "for c in cases:\n", + " res = analyze_case(DATA_DIR, c, n_snapshots=20)\n", + " results.append(res)\n", + "\n", + "summary_df = pd.DataFrame([summarize_case(r) for r in results])\n", + "summary_df" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "f9b07ac0", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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Z2JigooVK6q1LV68GKNHj0bTpk2xY8cOHD16FMePH0enTp3w6NGjfO3Pk27duoW7d+/CzMwMpqamao/4+HgkJiaq9X86HuDx5JNPbvvWrVvYuXOnxvrq1q0LABrrdHJyUnt+584diIjWwqS2tmHDhqFMmTLKfErLly9HmTJlMGzYsOfu/6NHj2BhYfHcfrlyj9XTMQOAs7Oz8vqz4s09zk/3zUujRo3QrFkz9O/fH/v371fmeXha7n68yN8BEREVTx988AH27duH+fPno23bts/tHxUVhQ4dOqBGjRoIDw/P141DgoODsWjRIhw9ehSdO3dGxYoV0a5dO5w4cULpY25ujlGjRmHbtm24e/cubt++ja+++gojRozQ2EbZsmU1flvNzMw08qbc9ty86UkFyb2eXL6gec2zaPutBx5PNr9v3z78/fffyMzMxOeff45evXopv+8bN27U2PaTtOV3eeUGT8eQlJSErKwsLFu2TGMbuYXA3H389ttv0bt3b1SuXBlbtmxBZGQkjh8/jmHDhml9z48dO4aLFy+iT58+qFKlSn7fJgCa+WHu30RuTpKbEz99/ApyEvplt53Xe56rIO9trmHDhiEzMxMmJiYYN27cS+8LUV549z2iIuTp6ancfU+bDh06YPny5Th69KjWu248fPgQERER8PLy0vhxycuxY8cQHx+P4cOHv3Dc+VGxYkVkZWXh9u3baoUpEUF8fDwaN26s9AMeT5BYuXJlpV9WVpZGcrJlyxa0adMGK1asUGu/d+/eC8VoZ2eHihUrYvfu3Vpft7KyeqF11q9fP8/J558uBj49mql8+fJQqVS4deuWxrLx8fEabTY2Nhg8eDDWrFmDiRMnYv369ejXrx9sbW3zFWtycvJz++XKPVZxcXEaCdvNmzdhZ2en1vasfdBW4HseKysr1K5dW+vknLn78XQMRERUMn3xxRdYvHgx+vTpg/fff/+5/aOiotC+fXu4ublh7969sLGxydd2TExMEBQUhKCgINy9exe//PILpk6dio4dO+L69esoW7YsAGD06NGYN28e1q1bh7S0NGRlZcHf3/+l9rGwFWZek9do6379+mHy5MlYvnw5mjVrhvj4eLz33nvK6927d1cmftcmN797Mg/IKzfQliMZGxtj4MCBatt8koeHB4DHOaOHhwe2b9+utp709HSty/Xp0weOjo6Y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kpOT85xXKj09Henp/1XwU1NTC28niIiIShBjY2PY2toiISEBAFC2bFmoVCo9R6UbIoKHDx8iISEBtra2MDY2fv5CRESUp/5NXdGhjsPzO/4/eyvzIoyGiEoqvV6+B0AjGRaRPBPknJwcqFQqbN26FTY2NgAeXwLYq1cvLF++XOtZ35CQEMyaNavwAyciIiqBHB0dAUApTJU2tra2yntAREQvzt7agpfjEdFL01tRys7ODsbGxhqjohISEjRGT+VycnJC5cqVlYIUAHh6ekJE8O+//6JGjRoaywQHByMoKEh5npqaChcXl0LaCyIiopJFpVLByckJ9vb2yMzM1Hc4OmVqasoRUkRERETFiN6KUmZmZvD29kZERATefPNNpT0iIgI9evTQukyLFi3w9ddf4/79+yhXrhwA4OLFizAyMkKVKlW0LmNubg5zcw4VJSIiepKxsTELNERERESkV0b63HhQUBDWrFmDdevWITo6GoGBgYiNjYW/vz+Ax6OcBg0apPTv168fKlasiKFDh+L8+fP47bffMGnSJAwbNqzUTthKRERERERERFQS6XVOqT59+iApKQmzZ89GXFwcvLy8EB4eDjc3NwBAXFwcYmNjlf7lypVDREQExo4dCx8fH1SsWBG9e/fGnDlz9LULRERERERERET0AlQiIvoOQpdSU1NhY2ODlJQUWFtb6zscIiIiAID7lJ+LfBtX53Ut8m2UZMwRCo7vGREREWmT3xxBr5fvERERERERERFR6cSiFBERERERERER6RyLUkREREREREREpHMsShERERERERERkc6xKEVERERERERERDrHohQREREREREREekci1JERERERERERKRzLEoREREREREREZHOsShFREREVEKEhYXBw8MDFhYW8Pb2xqFDh57Z/+DBg/D29oaFhQWqVq2KlStXqr3+7bffwsfHB7a2trC0tESDBg2wefPmotwFIiIiIgWLUkREREQlwPbt2zFhwgRMmzYNUVFRaNWqFTp37ozY2Fit/WNiYtClSxe0atUKUVFRmDp1KsaNG4cdO3YofSpUqIBp06YhMjISf/31F4YOHYqhQ4diz549utotIiIiKsVUIiL6DkKXUlNTYWNjg5SUFFhbW+s7HCIiIgCA+5Sfi3wbV+d1LfJtlGTFPUdo2rQpGjVqhBUrVihtnp6eeOONNxASEqLR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IsNR4KLU/v37iyIOIiIiIiIyQE08KsDJxgLxKWla55VSAXC0sUATjwq6Do2IiPSswHNKERERERER5ZexkQozutcB8LgA9aTc5zO614GxEa+6ICIqbViUIiIiIipiBw4cwJgxY3D69GkAwJo1a/QbEJGOdfJywooBjWBvba7W7mhjgRUDGqGTl5OeIiMiIn0q8OV7RERERFQwS5cuxfr16xESEoLExEScOnVK3yER6VwnLye0qG6HejP3AgA2DG2MVjUqcYQUEVEpxpFSREREREWsYsWKsLGxwbx583D48GFERkbqOyQivXiyANXEowILUkREpVyBR0o9ePAAlpaWRRELERERkUHq27ev8u8ZM2agRo0aeoyGiIiIqHgocFHKwcEBvXv3xrBhw9CyZcuiiImIDJT7lJ+LfBtX53Ut8m0QET1Lu3btsGzZMtSpU0dpe+2119T69OvXT9dhERERERU7Bb5874svvkBKSgratWuHmjVrYt68ebh582ZRxEZERERU4kRFRaFDhw7Yt2+fxmsffvihHiIiIiIiKp4KXJTq3r07duzYgZs3b2L06NH44osv4Obmhm7duuHbb79FVlZWUcRJREREVCK4ubnh+++/x5AhQ7B+/Xq118LDw/UUFREREVHx88J336tYsSICAwMRGBiIZcuWYdKkSQgPD4ednR38/f0xZcoUlC1btjBjJSIiIir2VCoVGjdujN9++w3dunXDlStX8NFHHwEARETP0RHpVkJqGhLupSvP0zKzlX+fv5kKC1Njtf72Vuawt7bQWXxERKRfL1yUio+Px6ZNm7B+/XrExsaiV69eGD58OG7evIl58+bh6NGj2Lt3b2HGSk+aaaODbaQU/TaIyLDxu4pKodzCk4eHB37//Xe89dZb6N+/PzZs2KDfwIj0YOsfsViy75LW13qt1LwL5fh2NRDYoWZRh0VERMVEgYtS3377LdavX489e/agTp06eO+99zBgwADY2toqfRo0aICGDRvma31hYWFYuHAh4uLiULduXYSGhqJVq1bPXe7w4cNo3bo1vLy8cPr06YLuBhEREVGR8PT0VP5dvnx57N27F8OHD0e7du1w//59PUZGpHv9m7qiQx2HfPe3tzIvwmiIiKi4KXBRaujQoXjnnXdw+PBhNG7cWGufqlWrYtq0ac9d1/bt2zFhwgSEhYWhRYsWWLVq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WlpizJgxqF+/PiwtLfH7779j1KhRsLS0xMiRIwstLiIiIiIiIiIiKloFnlOqMC1evBjDhw/HiBEj4OnpidDQULi4uGDFihVa+zds2BB9+/ZF3bp14e7ujgEDBqBjx444dOiQjiMnIiIiIiIiIqKXke+iVJcuXZCSkqI8nzt3Lu7evas8T0pKQp06dfK94YyMDJw8eRJ+fn5q7X5+fjhy5Ei+1hEVFYUjR46gdevWefZJT09Hamqq2oOIiIiIiIiIiPQr30WpPXv2ID09XXk+f/58JCcnK8+zsrLw999/53vDiYmJyM7OhoODg1q7g4MD4uPjn7lslSpVYG5uDh8fH7z33nsYMWJEnn1DQkJgY2OjPFxcXPIdIxERERERERERFY18F6VE5JnPX5RKpdJY79NtTzt06BBOnDiBlStXIjQ0FF988UWefYODg5GSkqI8rl+/XihxExERERERERHRiyvwROeFxc7ODsbGxhqjohISEjRGTz3Nw8MDAFCvXj3cunULM2fORN++fbX2NTc3h7m5eeEETUREREREREREhSLfI6VUKpXGCKbnjWh6FjMzM3h7eyMiIkKtPSIiAs2bN8/3ekRE7bJCIiIiIiIiIiIq/vI9UkpEMGTIEGXUUVpaGvz9/WFpaQkAL1QYCgoKwsCBA+Hj4wNfX1+sXr0asbGx8Pf3B/D40rsbN25g06ZNAIDly5fD1dUVtWvXBgD8/vvvWLRoEcaOHVvgbRMRERERERERkf7kuyg1ePBgtecDBgzQ6DNo0KACbbxPnz5ISkrC7NmzERcXBy8vL4SHh8PNzQ0AEBcXh9jYWKV/Tk4OgoODERMTAxMTE1SrVg3z5s3DqFGjCrRdIiIiIiIiIiLSr3wXpdavX18kAQQEBCAgIEDraxs2bFB7PnbsWI6KIiIiIiIiIiIyAPmeU4qIiIiIiIiIiKiwsChFREREREREREQ6x6IUERERERERERHpHItSRERERERERESkcyxKERERERERERGRzrEoRUREREREREREOseiFBERERERERER6RyLUkREREREREREpHMsShERERERERERkc6xKEVERERERERERDrHohQREREREREREekci1JERERERERERKRzLEoREREREREREZHOsShFREREREREREQ6x6IUERERERERERHpHItSRERERERERESkcyxKERERERERERGRzrEoRUREREREREREOseiFBERERERERER6RyLUkREREREREREpHMsShERERGVEGFhYfDw8ICFhQW8vb1x6NChZ/Y/ePAgvL29YWFhgapVq2LlypVqr587dw49e/aEu7s7VCoVQkNDizB6IiIiInV6L0oVJLn69ttv0aFDB1SqVAnW1tbw9fXFnj17dBgtERERkX5s374dEyZMwLRp0xAVFYVWrVqhc+fOiI2N1do/JiYGXbp0QatWrRAVFYWpU6di3Lhx2LFjh9Ln4cOHqFq1KubNmwdHR0dd7QoRERERAD0XpQqaXP3222/o0KEDwsPDcfLkSbRt2xbdu3dHVFSUjiMnIiIi0q3Fixdj+PDhGDFiBDw9PREaGgoXFxesWLFCa/+VK1fC1dUVoaGh8PT0xIgRIzBs2DAsWrRI6dO4cWMsXLgQ77zzDszNzXW1K0REREQA9FyUKmhyFRoaig8++ACNGzdGjRo18PHHH6NGjRrYuXOnjiMnIiIi0p2MjAycPHkSfn5+au1+fn44cuSI1mUiIyM1+nfs2BEnTpxAZmbmC8WRnp6O1NRUtQcRERHRi9JbUepFkqun5eTk4N69e6hQoUKefZg8ERERUUmXmJiI7OxsODg4qLU7ODggPj5e6zLx8fFa+2dlZSExMfGF4ggJCYGNjY3ycHFxeaH1EBEREQF6LEq9SHL1tE8++QQPHjxA79698+zD5ImIiIgMhUqlUnsuIhptz+uvrT2/goODkZKSojyuX7/+QushIiIiAorBROcFTa5yffHFF5g5cya2b98Oe3v7PPsxeSIiIqKSzs7ODsbGxhon7hISEjRO8OVydHTU2t/ExAQVK1Z8oTjMzc1hbW2t9iAiIiJ6UXorSr1IcpVr+/btGD58OL766iu0b9/+mX2ZPBEREVFJZ2ZmBm9vb0RERKi1R0REoHnz5lqX8fX11ei/d+9e+Pj4wNTUtMhiJSIiIsovvRWlXiS5Ah6PkBoyZAi2bduGrl27FnWYRERERMVCUFAQ1qxZg3Xr1iE6OhqBgYGIjY2Fv78/gMejwwcNGqT09/f3x7Vr1xAUFITo6GisW7cOa9euxcSJE5U+GRkZOH36NE6fPo2MjAzcuHEDp0+fxuXLl3W+f0RERFT6mOhz40FBQRg4cCB8fHzg6+uL1atXayRXN27cwKZNmwA8LkgNGjQIS5YsQbNmzZRRVmXKlIGNjY3e9oOIiIioqPXp0wdJSUmYPXs24uLi4OXlhfDwcLi5uQEA4uLiEBsbq/T38PBAeHg4AgMDsXz5cjg7O2Pp0qXo2bOn0ufmzZto2LCh8nzRokVYtGgRWrdujQMHDuhs34iIiKh00mtRqqDJ1apVq5CVlYX33nsP7733ntI+ePBgbNiwQdfhExEREelUQEAAAgICtL6mLRdq3bo1Tp06lef63N3dlcnPiYiIiHRNr0UpoGDJFc/YEREREREREREZBr3ffY+IiIiIiIiIiEofFqWIiIiIiIiIiEjnWJQiIiIiIiIiIiKdY1GKiIiIiIiIiIh0jkUpIiIiIiIiIiLSORaliIiIiIiIiIhI51iUIiIiIiIiIiIinWNRioiIiIiIiIiIdI5FKSIiIiIiIiIi0jkTfQdAREREOjLTpojXn1K06yciIiIig8KRUkREREREREREpHMsShERERERERERkc6xKEVERERERERERDrHohQREREREREREekci1JERERERERERKRzLEoREREREREREZHOsShFREREREREREQ6x6IUERERERERERHpHItSRERERERERESkcyxKERERERERERGRzrEoRUREREREREREOqf3olRYWBg8PDxgYWEBb29vHDp0KM++cXFx6NevH2rVqgUjIyNMmDBBd4ESEREREREREVGh0WtRavv27ZgwYQKmTZuGqKgotGrVCp07d0ZsbKzW/unp6ahUqRKmTZuGV155RcfREhERERERERFRYdFrUWrx4sUYPnw4RowYAU9PT4SGhsLFxQUrVqzQ2t/d3R1LlizBoEGDYGNjo+NoiYiIiIiIiIiosOitKJWRkYGTJ0/Cz89Prd3Pzw9HjhwptO2kp6cjNTVV7UFERERERERERPqlt6JUYmIisrOz4eDgoNbu4OCA+Pj4QttOSEgIbGxslIeLi0uhrZuIiIiIiIiIiF6M3ic6V6lUas9FRKPtZQQHByMlJUV5XL9+vdDWTUREREREREREL8ZEXxu2s7ODsbGxxqiohIQEjdFTL8Pc3Bzm5uaFtj4iIiIiIiIiInp5ehspZWZmBm9vb0RERKi1R0REoHnz5nqKioiIiIiIiIiIdEFvI6UAICgoCAMHDoSPjw98fX2xevVqxMbGwt/fH8DjS+9u3LiBTZs2KcucPn0aAHD//n3cvn0bp0+fhpmZGerUqaOPXSAiIiIiIiIioheg16JUnz59kJSUhNmzZyMuLg5eXl4IDw+Hm5sbACAuLg6xsbFqyzRs2FD598mTJ7Ft2za4ubnh6tWrugydiEqxehvrFfk2zgw+U+TbICIiIiIi0ie9FqUAICAgAAEBAVpf27Bhg0abiBRxREREREREREREVNT0fvc9IiIiIiIiIiIqfViUIiIiIiIiIiIinWNRioiIiIiIiIiIdE7vc0oRUf4U9eTanFibSipOPE9EREREVDJxpBQREREREREREekci1JERERERERERKRzLEoREREREREREZHOsShFREREREREREQ6x4nOiYiIiIiIiIhKiewcwbGYZCTcS4O9lQWaeFSAsZFKL7FwpBQRERFRCREWFgYPDw9YWFjA29sbhw4demb/gwcPwtvbGxYWFqhatSpWrlyp0WfHjh2oU6cOzM3NUadOHXz33XdFFT4RERHp2e6zcWg5/1f0/fwoxn95Gn0/P4qW83/F7rNxeomHRSkiIiKiEmD79u2YMGECpk2bhqioKLRq1QqdO3dGbGys1v4xMTHo0qULWrVqhaioKEydOhXjxo3Djh07lD6RkZHo06cPBg4ciD///BMDBw5E79698ccff+hqt4iIiEhHdp+Nw+gtpxCXkqbWHp+ShtFbTumlMMWiFBEREVEJsHjxYgwfPhwjRoyAp6cnQkND4eLighUrVmjtv3LlSri6uiI0NBSenp4YMWIEhg0bhkWLFil9QkND0aFDBwQHB6N27doIDg5Gu3btEBoaqqO9IiIioqKWkJqGP6/fxYffn4VoeV3+//Hh92fx5/W7SEhN09KraLAoRURERFTMZWRk4OTJk/Dz81Nr9/Pzw5EjR7QuExkZqdG/Y8eOOHHiBDIzM5/ZJ691EhERUcmz+rcr6LH8MBLvZzyzX+L9DPRYfhirf7uio8g40TkREREVknob6xX5Ns4MPlPk2yiOEhMTkZ2dDQcHB7V2BwcHxMfHa10mPj5ea/+srCwkJibCyckpzz55rTM9PR3p6enK89TU1BfZHSIiIiIAHClFREREVGKoVOp3xhERjbbn9X+6vSDrDAkJgY2NjfJwcXEpUPxERESkeyNfrYqP3/TKV9+P3/TCyFerFnFE/2FRioiIiKiYs7Ozg7GxscYIpoSEBI2RTrkcHR219jcx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FBcXIzg4GOHh4U9sq+547Lo/k/Fxss2m0R+vhoZuODg4wNLSEseOHWtw6Ia9vT1WrFiBX/ziFygqKkJiYmKDQzeOHTvGoRtERC0QsxA9C3NV82A+I6Kn4fA9Fdq6dSuioqIQHh5uUPp26dKlWLhwIaqqqlj6llSLQzeIiIhZiKhlYT4joqdho5TKVFRUIDAwED179sTatWthYWGBVatWYcmSJbh//z4uX75sUGmEk8WR2hw7dgwHDhzAvHnzYGdnh//+97/w9/fHpUuXsGnTJhQVFSEqKgoeHh4YM2YMzM3NsWjRInTt2hXx8fHo2rWrsi390I36PxMRUcvFLETU8jCfEdHTcPieytQvfVtVVaWUvs3JyWmw9C2RmnDoBhGRaWMWImp5mM+I6Gl4p5QKsfQtEYduEBGZMmYhopaJ+YyI6mOjlEqx9C2ZMg7dICIiZiGiloX5jIgawuF7KtWjRw/06NEDAN/QyfRw6AYRETELEbUszGdE1BDeKUVEqsShG0REREQtC/MZEdXHRikiUi0O3SAiIiJqWZjPiKguNkoRkUng0A0iIiKiloX5jIjYKEVEREREREREREanbe4XQEREREREREREpoeNUkREREREREREZHRslCIiIiIiIiIiIqNjoxQRERERERERERkdG6WIiIiIiIiIiMjo2ChFRERERERERERGx0YpIiIiIiIiIiIyOjZKERERERERERGR0bFRioiIiIiIiIiIjI6NUkREREREREREZHRslCIiIiIiIiIiIqP7f9ZX2CLIn8u4AAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[act_nc] POD c3=0.974, c5=0.991; etaZ2=0.0271±0.0006\n", + "[act_cloak_steady] POD c3=0.948, c5=0.978; etaZ2=0.0249±0.0022\n", + "[act_karman_nc] POD c3=0.918, c5=0.971; etaZ2=0.0392±0.0052\n", + "[act_karman_cloak] POD c3=0.962, c5=0.987; etaZ2=0.0439±0.0011\n", + "[act_illusion_1L] POD c3=0.982, c5=0.997; etaZ2=0.0002±0.0001\n" + ] + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "# 可视化:POD 前三模态能量占比 + Z2 指数分布\n", + "fig, axes = plt.subplots(1, 2, figsize=(12, 4))\n", + "\n", + "x = np.arange(len(summary_df))\n", + "barw = 0.25\n", + "axes[0].bar(x - barw, summary_df[\"POD_r1\"], width=barw, label=\"r1\")\n", + "axes[0].bar(x, summary_df[\"POD_r2\"], width=barw, label=\"r2\")\n", + "axes[0].bar(x + barw, summary_df[\"POD_r3\"], width=barw, label=\"r3\")\n", + "axes[0].set_xticks(x)\n", + "axes[0].set_xticklabels(summary_df[\"case\"], rotation=30, ha=\"right\")\n", + "axes[0].set_ylabel(\"Energy ratio\")\n", + "axes[0].set_title(\"POD modal energy (top 3)\")\n", + "axes[0].legend()\n", + "\n", + "axes[1].errorbar(\n", + " x,\n", + " summary_df[\"etaZ2_mean\"],\n", + " yerr=summary_df[\"etaZ2_std\"],\n", + " fmt=\"o\",\n", + " capsize=4,\n", + ")\n", + "axes[1].set_xticks(x)\n", + "axes[1].set_xticklabels(summary_df[\"case\"], rotation=30, ha=\"right\")\n", + "axes[1].set_ylabel(\"$\\\\eta_{Z_2}$\")\n", + "axes[1].set_title(\"Z2 symmetry-breaking index\")\n", + "\n", + "plt.tight_layout()\n", + "plt.show()\n", + "\n", + "# 打印一段自动化文字结论(初步)\n", + "for _, row in summary_df.iterrows():\n", + " print(\n", + " f\"[{row['case']}] POD c3={row['POD_c3']:.3f}, c5={row['POD_c5']:.3f}; \"\n", + " f\"etaZ2={row['etaZ2_mean']:.4f}±{row['etaZ2_std']:.4f}\"\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6131addd", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "PyDMD import OK\n", + "PyDMD path: /home/frank14f/anaconda3/envs/pycuda_3_10/lib/python3.10/site-packages/pydmd/__init__.py\n", + "Core classes: ['DMD', 'BOPDMD', 'HODMD', 'MrDMD', 'DMDc']\n", + "Note: POD 本身不依赖 PyDMD,已由 SVD 完成;PyDMD 主要可用于后续 DMD 频率/模态分析。\n" + ] + } + ], + "source": [ + "# PyDMD 可用性检查(可选)\n", + "try:\n", + " import pydmd\n", + " print(\"PyDMD import OK\")\n", + " print(\"PyDMD path:\", getattr(pydmd, \"__file__\", \"N/A\"))\n", + " available = [\"DMD\", \"BOPDMD\", \"HODMD\", \"MrDMD\", \"DMDc\"]\n", + " print(\"Core classes:\", [name for name in available if hasattr(pydmd, name)])\n", + "except Exception as e:\n", + " print(\"PyDMD not available in current env:\", repr(e))\n", + " print(\"Tip 1: pip install pydmd\")\n", + " print(\"Tip 2: or install local repo /home/frank14f/Frank_LBM/JFM_WYQ/PyDMD with dependencies\")\n", + "\n", + "print(\"Note: POD 本身不依赖 PyDMD,已由 SVD 完成;PyDMD 主要可用于后续 DMD 频率/模态分析。\")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "159dcf0e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== act_nc: POD mode Z2 (top 6) ===\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "mode", + "rawType": "int64", + "type": "integer" + }, + { + "name": "energy_ratio", + "rawType": "float64", + "type": "float" + }, + { + "name": "etaZ2_mode", + "rawType": "float64", + "type": "float" + }, + { + "name": "signed_m_mode", + "rawType": "float64", + "type": "float" + } + ], + "ref": "7486a29c-6938-44c9-8d9c-af76e68fbd34", + "rows": [ + [ + "0", + "1", + "0.5453104678544648", + "0.9994080119919929", + "0.45275043997789943" + ], + [ + "1", + "2", + "0.4184484628978619", + "0.9999419968042454", + "-1.0526882909856006" + ], + [ + "2", + "3", + "0.01011473983280817", + "0.8521120820192422", + "-0.19003334589718332" + ], + [ + "3", + "4", + "0.009767173069931025", + "0.7344566942976254", + "-0.556252781636925" + ], + [ + "4", + "5", + "0.007495420467718459", + "0.2657819245402694", + "-0.31450018031016985" + ], + [ + "5", + "6", + "0.007301407311240587", + "0.14655055988416574", + "-0.11972677252940012" + ] + ], + "shape": { + "columns": 4, + "rows": 6 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
modeenergy_ratioetaZ2_modesigned_m_mode
010.5453100.9994080.452750
120.4184480.999942-1.052688
230.0101150.852112-0.190033
340.0097670.734457-0.556253
450.0074950.265782-0.314500
560.0073010.146551-0.119727
\n", + "
" + ], + "text/plain": [ + " mode energy_ratio etaZ2_mode signed_m_mode\n", + "0 1 0.545310 0.999408 0.452750\n", + "1 2 0.418448 0.999942 -1.052688\n", + "2 3 0.010115 0.852112 -0.190033\n", + "3 4 0.009767 0.734457 -0.556253\n", + "4 5 0.007495 0.265782 -0.314500\n", + "5 6 0.007301 0.146551 -0.119727" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== act_cloak_steady: POD mode Z2 (top 6) ===\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "mode", + "rawType": "int64", + "type": "integer" + }, + { + "name": "energy_ratio", + "rawType": "float64", + "type": "float" + }, + { + "name": "etaZ2_mode", + "rawType": "float64", + "type": "float" + }, + { + "name": "signed_m_mode", + "rawType": "float64", + "type": "float" + } + ], + "ref": "4c0bc756-70bc-492c-bbba-7895f29d1451", + "rows": [ + [ + "0", + "1", + "0.4846147427998569", + "0.9380281704643914", + "-0.6496960949814631" + ], + [ + "1", + "2", + "0.39204425512169633", + "0.9138584975862195", + "-0.6406265951571893" + ], + [ + "2", + "3", + "0.07097285712676774", + "0.27480590846948716", + "-0.9278430938622736" + ], + [ + "3", + "4", + "0.018648443037237623", + "0.5777029619135854", + "-1.039376204036435" + ], + [ + "4", + "5", + "0.01125476639397228", + "0.4170610888043789", + "0.9383708863979906" + ], + [ + "5", + "6", + "0.009137738195040303", + "0.8664358753167714", + "0.21471060434184322" + ] + ], + "shape": { + "columns": 4, + "rows": 6 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
modeenergy_ratioetaZ2_modesigned_m_mode
010.4846150.938028-0.649696
120.3920440.913858-0.640627
230.0709730.274806-0.927843
340.0186480.577703-1.039376
450.0112550.4170610.938371
560.0091380.8664360.214711
\n", + "
" + ], + "text/plain": [ + " mode energy_ratio etaZ2_mode signed_m_mode\n", + "0 1 0.484615 0.938028 -0.649696\n", + "1 2 0.392044 0.913858 -0.640627\n", + "2 3 0.070973 0.274806 -0.927843\n", + "3 4 0.018648 0.577703 -1.039376\n", + "4 5 0.011255 0.417061 0.938371\n", + "5 6 0.009138 0.866436 0.214711" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== act_karman_nc: POD mode Z2 (top 6) ===\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "mode", + "rawType": "int64", + "type": "integer" + }, + { + "name": "energy_ratio", + "rawType": "float64", + "type": "float" + }, + { + "name": "etaZ2_mode", + "rawType": "float64", + "type": "float" + }, + { + "name": "signed_m_mode", + "rawType": "float64", + "type": "float" + } + ], + "ref": "a729ed30-bc26-4cc0-93c2-21b64db8582a", + "rows": [ + [ + "0", + "1", + "0.43071185973764003", + "0.8050578423957492", + "0.9292417993136286" + ], + [ + "1", + "2", + "0.4005729581129849", + "0.8711046658788251", + "0.05690189633267477" + ], + [ + "2", + "3", + "0.086936743836616", + "0.5854347934835601", + "0.31627914865929146" + ], + [ + "3", + "4", + "0.03822169148275825", + "0.713888226187294", + "0.541525500257247" + ], + [ + "4", + "5", + "0.014264975001634072", + "0.5716665773277405", + "-0.5381156468530371" + ], + [ + "5", + "6", + "0.009711805880947477", + "0.768920786314115", + "0.424069185564871" + ] + ], + "shape": { + "columns": 4, + "rows": 6 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
modeenergy_ratioetaZ2_modesigned_m_mode
010.4307120.8050580.929242
120.4005730.8711050.056902
230.0869370.5854350.316279
340.0382220.7138880.541526
450.0142650.571667-0.538116
560.0097120.7689210.424069
\n", + "
" + ], + "text/plain": [ + " mode energy_ratio etaZ2_mode signed_m_mode\n", + "0 1 0.430712 0.805058 0.929242\n", + "1 2 0.400573 0.871105 0.056902\n", + "2 3 0.086937 0.585435 0.316279\n", + "3 4 0.038222 0.713888 0.541526\n", + "4 5 0.014265 0.571667 -0.538116\n", + "5 6 0.009712 0.768921 0.424069" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== act_karman_cloak: POD mode Z2 (top 6) ===\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "mode", + "rawType": "int64", + "type": "integer" + }, + { + "name": "energy_ratio", + "rawType": "float64", + "type": "float" + }, + { + "name": "etaZ2_mode", + "rawType": "float64", + "type": "float" + }, + { + "name": "signed_m_mode", + "rawType": "float64", + "type": "float" + } + ], + "ref": "2cdca3d1-438c-49f1-ab37-396b0a90ef8d", + "rows": [ + [ + "0", + "1", + "0.47436163138736653", + "0.8751319885061303", + "0.6769883516132917" + ], + [ + "1", + "2", + "0.4175490471265246", + "0.9580735144087666", + "-0.044610252133867045" + ], + [ + "2", + "3", + "0.07043821442543226", + "0.7824188986670307", + "-0.36616531749688763" + ], + [ + "3", + "4", + "0.016490145442195788", + "0.5980339994937104", + "-0.30644658307936956" + ], + [ + "4", + "5", + "0.007774435049118909", + "0.3988135834851191", + "0.3518113555876005" + ], + [ + "5", + "6", + "0.005982100144970479", + "0.8042291893964388", + "-0.18963533442943759" + ] + ], + "shape": { + "columns": 4, + "rows": 6 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
modeenergy_ratioetaZ2_modesigned_m_mode
010.4743620.8751320.676988
120.4175490.958074-0.044610
230.0704380.782419-0.366165
340.0164900.598034-0.306447
450.0077740.3988140.351811
560.0059820.804229-0.189635
\n", + "
" + ], + "text/plain": [ + " mode energy_ratio etaZ2_mode signed_m_mode\n", + "0 1 0.474362 0.875132 0.676988\n", + "1 2 0.417549 0.958074 -0.044610\n", + "2 3 0.070438 0.782419 -0.366165\n", + "3 4 0.016490 0.598034 -0.306447\n", + "4 5 0.007774 0.398814 0.351811\n", + "5 6 0.005982 0.804229 -0.189635" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== act_illusion_1L: POD mode Z2 (top 6) ===\n" + ] + }, + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "mode", + "rawType": "int64", + "type": "integer" + }, + { + "name": "energy_ratio", + "rawType": "float64", + "type": "float" + }, + { + "name": "etaZ2_mode", + "rawType": "float64", + "type": "float" + }, + { + "name": "signed_m_mode", + "rawType": "float64", + "type": "float" + } + ], + "ref": "b8d70cac-9266-4045-9cc1-f2ab1c7ed086", + "rows": [ + [ + "0", + "1", + "0.7894457334235928", + "0.0040886828114025", + "-0.017435264313956228" + ], + [ + "1", + "2", + "0.15231860942150405", + "0.08274130721502927", + "-0.11572931922071367" + ], + [ + "2", + "3", + "0.04002898617863977", + "0.27349974145089123", + "-0.0210728683252721" + ], + [ + "3", + "4", + "0.011281681951248682", + "0.40987687838885856", + "-0.23023901547165737" + ], + [ + "4", + "5", + "0.0035597352798016823", + "0.4608974294665576", + "-0.46398520728112275" + ], + [ + "5", + "6", + "0.0017309079961941116", + "0.36849945701350023", + "-0.7231875306582263" + ] + ], + "shape": { + "columns": 4, + "rows": 6 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
modeenergy_ratioetaZ2_modesigned_m_mode
010.7894460.004089-0.017435
120.1523190.082741-0.115729
230.0400290.273500-0.021073
340.0112820.409877-0.230239
450.0035600.460897-0.463985
560.0017310.368499-0.723188
\n", + "
" + ], + "text/plain": [ + " mode energy_ratio etaZ2_mode signed_m_mode\n", + "0 1 0.789446 0.004089 -0.017435\n", + "1 2 0.152319 0.082741 -0.115729\n", + "2 3 0.040029 0.273500 -0.021073\n", + "3 4 0.011282 0.409877 -0.230239\n", + "4 5 0.003560 0.460897 -0.463985\n", + "5 6 0.001731 0.368499 -0.723188" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# 进阶:POD 模态级别的 Z2 对称性(前6模态)\n", + "\n", + "def pod_mode_z2_table(data_dir: str, prefix: str, n_snapshots: int = 40, n_modes: int = 6):\n", + " files = collect_case_files(data_dir, prefix, n_snapshots=n_snapshots)\n", + " X, fluid_mask, uv_fields, _ = build_snapshot_matrix(files)\n", + " pod = pod_from_snapshots(X)\n", + "\n", + " n_fluid = int(np.sum(fluid_mask))\n", + " Umat = pod[\"U\"]\n", + " out = []\n", + "\n", + " for k in range(min(n_modes, Umat.shape[1])):\n", + " mode_vec = Umat[:, k]\n", + " u = np.zeros_like(uv_fields[0][0])\n", + " v = np.zeros_like(uv_fields[0][1])\n", + " u[fluid_mask] = mode_vec[:n_fluid]\n", + " v[fluid_mask] = mode_vec[n_fluid:]\n", + " z2 = z2_metrics_for_field(u, v, fluid_mask)\n", + " out.append(\n", + " {\n", + " \"mode\": k + 1,\n", + " \"energy_ratio\": float(pod[\"ratio\"][k]),\n", + " \"etaZ2_mode\": z2[\"eta_z2\"],\n", + " \"signed_m_mode\": z2[\"signed_m\"],\n", + " }\n", + " )\n", + "\n", + " return pd.DataFrame(out)\n", + "\n", + "\n", + "for c in [\"act_nc\", \"act_cloak_steady\", \"act_karman_nc\", \"act_karman_cloak\", \"act_illusion_1L\"]:\n", + " print(f\"\\n=== {c}: POD mode Z2 (top 6) ===\")\n", + " display(pod_mode_z2_table(DATA_DIR, c, n_snapshots=20, n_modes=6))" + ] + }, + { + "cell_type": "markdown", + "id": "a66314ce", + "metadata": {}, + "source": [ + "### tail-30 版本(推荐汇报口径)\n", + "\n", + "你提到 250823 是连续衔接测试流,这里采用每个 case 的最后 30 帧进行 POD 与 Z2 分析,减少过渡段影响。" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "ed7c80d5", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.microsoft.datawrangler.viewer.v0+json": { + "columns": [ + { + "name": "index", + "rawType": "int64", + "type": "integer" + }, + { + "name": "case", + "rawType": "object", + "type": "string" + }, + { + "name": "n_tail", + "rawType": "int64", + "type": "integer" + }, + { + "name": "POD_r1", + "rawType": "float64", + "type": "float" + }, + { + "name": "POD_r2", + "rawType": "float64", + "type": "float" + }, + { + "name": "POD_r3", + "rawType": "float64", + "type": "float" + }, + { + "name": "POD_c3", + "rawType": "float64", + "type": "float" + }, + { + "name": "POD_c5", + "rawType": "float64", + "type": "float" + }, + { + "name": "etaZ2_mean", + "rawType": "float64", + "type": "float" + }, + { + "name": "etaZ2_std", + "rawType": "float64", + "type": "float" + }, + { + "name": "signed_m_mean", + "rawType": "float64", + "type": "float" + }, + { + "name": "signed_m_std", + "rawType": "float64", + "type": "float" + } + ], + "ref": "9ee7e798-b3f2-4a24-a667-778f0b913d1a", + "rows": [ + [ + "0", + "act_nc", + "30", + "0.5265958599850638", + "0.4370990934814651", + "0.01033728938373843", + "0.9740322428502673", + "0.9913319351209482", + "0.02700101939788688", + "0.0006078344180394181", + "-0.019123779320809126", + "0.12851059100756945" + ], + [ + "1", + "act_cloak_steady", + "30", + "0.7402694361793121", + "0.18870959559890788", + "0.050305109447756666", + "0.9792841412259766", + "0.998620444604609", + "0.0002530544349978394", + "0.0007131590242358435", + "0.0010666717685869265", + "0.014347794059403923" + ], + [ + "2", + "act_karman_nc", + "30", + "0.4310014707680547", + "0.3977404106377873", + "0.06779031660271409", + "0.8965321980085561", + "0.9636615894587849", + "0.047404791862959275", + "0.0022445429038198557", + "0.000857481224825774", + "0.11008100445563687" + ], + [ + "3", + "act_karman_cloak", + "30", + "0.545639719200303", + "0.4228124895508167", + "0.010409110156730333", + "0.9788613189078499", + "0.9931341240624365", + "0.04105746265066539", + "0.000857878145676811", + "0.01755965733390702", + "0.1310440942793705" + ], + [ + "4", + "act_illusion_1L", + "30", + "0.5394370944564028", + "0.422263454521596", + "0.010111493849711528", + "0.9718120428277104", + "0.9885182903636507", + "0.02912351245151389", + "0.0007798087813011604", + "-0.03246098517688319", + "0.12469165927172388" + ] + ], + "shape": { + "columns": 11, + "rows": 5 + } + }, + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
casen_tailPOD_r1POD_r2POD_r3POD_c3POD_c5etaZ2_meanetaZ2_stdsigned_m_meansigned_m_std
0act_nc300.5265960.4370990.0103370.9740320.9913320.0270010.000608-0.0191240.128511
1act_cloak_steady300.7402690.1887100.0503050.9792840.9986200.0002530.0007130.0010670.014348
2act_karman_nc300.4310010.3977400.0677900.8965320.9636620.0474050.0022450.0008570.110081
3act_karman_cloak300.5456400.4228120.0104090.9788610.9931340.0410570.0008580.0175600.131044
4act_illusion_1L300.5394370.4222630.0101110.9718120.9885180.0291240.000780-0.0324610.124692
\n", + "
" + ], + "text/plain": [ + " case n_tail POD_r1 POD_r2 POD_r3 POD_c3 POD_c5 \\\n", + "0 act_nc 30 0.526596 0.437099 0.010337 0.974032 0.991332 \n", + "1 act_cloak_steady 30 0.740269 0.188710 0.050305 0.979284 0.998620 \n", + "2 act_karman_nc 30 0.431001 0.397740 0.067790 0.896532 0.963662 \n", + "3 act_karman_cloak 30 0.545640 0.422812 0.010409 0.978861 0.993134 \n", + "4 act_illusion_1L 30 0.539437 0.422263 0.010111 0.971812 0.988518 \n", + "\n", + " etaZ2_mean etaZ2_std signed_m_mean signed_m_std \n", + "0 0.027001 0.000608 -0.019124 0.128511 \n", + "1 0.000253 0.000713 0.001067 0.014348 \n", + "2 0.047405 0.002245 0.000857 0.110081 \n", + "3 0.041057 0.000858 0.017560 0.131044 \n", + "4 0.029124 0.000780 -0.032461 0.124692 " + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def analyze_case_tail(data_dir: str, prefix: str, n_tail: int = 30):\n", + " files = sorted(glob.glob(str(Path(data_dir) / f\"{prefix}.*\")))[-n_tail:]\n", + " if len(files) == 0:\n", + " raise FileNotFoundError(f\"No files found for {prefix}\")\n", + "\n", + " X, fluid_mask, uv_fields, fluid_flag = build_snapshot_matrix(files)\n", + " pod = pod_from_snapshots(X)\n", + " z2 = [z2_metrics_for_field(u, v, fluid_mask) for (u, v) in uv_fields]\n", + "\n", + " eta = np.array([z[\"eta_z2\"] for z in z2])\n", + " signed_m = np.array([z[\"signed_m\"] for z in z2])\n", + "\n", + " return {\n", + " \"prefix\": prefix,\n", + " \"n_tail\": len(files),\n", + " \"fluid_flag\": float(fluid_flag),\n", + " \"pod_ratio\": pod[\"ratio\"],\n", + " \"pod_cumsum\": pod[\"cumsum\"],\n", + " \"pod_U\": pod[\"U\"],\n", + " \"eta_z2_series\": eta,\n", + " \"signed_m_series\": signed_m,\n", + " \"fluid_mask\": fluid_mask,\n", + " }\n", + "\n", + "\n", + "def summarize_case_tail(result: dict):\n", + " r = result[\"pod_ratio\"]\n", + " c = result[\"pod_cumsum\"]\n", + " eta = result[\"eta_z2_series\"]\n", + " m = result[\"signed_m_series\"]\n", + "\n", + " return {\n", + " \"case\": result[\"prefix\"],\n", + " \"n_tail\": result[\"n_tail\"],\n", + " \"POD_r1\": float(r[0]),\n", + " \"POD_r2\": float(r[1]) if len(r) > 1 else np.nan,\n", + " \"POD_r3\": float(r[2]) if len(r) > 2 else np.nan,\n", + " \"POD_c3\": float(c[2]) if len(c) > 2 else np.nan,\n", + " \"POD_c5\": float(c[4]) if len(c) > 4 else np.nan,\n", + " \"etaZ2_mean\": float(np.mean(eta)),\n", + " \"etaZ2_std\": float(np.std(eta)),\n", + " \"signed_m_mean\": float(np.mean(m)),\n", + " \"signed_m_std\": float(np.std(m)),\n", + " }\n", + "\n", + "\n", + "cases_tail = [\"act_nc\", \"act_cloak_steady\", \"act_karman_nc\", \"act_karman_cloak\", \"act_illusion_1L\"]\n", + "results_tail = [analyze_case_tail(DATA_DIR, c, n_tail=30) for c in cases_tail]\n", + "summary_tail_df = pd.DataFrame([summarize_case_tail(r) for r in results_tail])\n", + "summary_tail_df" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "efdb2548", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABKUAAAGGCAYAAACqvTJ0AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAACjaElEQVR4nOzdeVhU5fs/8Pewi7KoLKICgiuKJoELmlsq7lnqR1NzxQUpF8hStHLJohSNNBEtd82wr7ZYbmhqKpgb5r7lgimIgAKC7PfvD3+cHAcUFGZY3q/rmutynvOcc+4zZ5i5vec5z1GJiICIiIiIiIiIiEiL9HQdABERERERERERVTwsShERERERERERkdaxKEVERERERERERFrHohQREREREREREWkdi1JERERERERERKR1LEoREREREREREZHWsShFRERERERERERax6IUERERERERERFpHYtSRERERERERESkdSxKEb2kNWvWQKVSKQ8DAwPUrl0bo0aNwu3btzX6X7hwASNHjoSDgwOMjIxgZWWFnj17YseOHRp99+/fr7ZtIyMjWFtbo23btpg5cyZu3rypjUMsFnXq1MHIkSNfaF2VSoXZs2cXazzlUVZWFho1aoQvvvhCaYuIiMDs2bPx4MGDF97u7NmzoVKp1No6duyIjh07Fmr9mTNnws3NDdWqVYOJiQmcnZ0xbty4fN+/WVlZmDNnDurUqQNjY2M0atQIS5Ys0eg3bNgwvPnmmy9yOEREFcqTeURBj7zv2JiYGHz00Ufw9PSElZUVzM3N4e7ujhUrViAnJ0e3B1KKhYSEYM2aNVrd58iRI1GlShWt7rMwVCoV3nvvvWf2uXHjBlQqldZfs5Lcty5z1XXr1sHa2hopKSlK2+eff46ff/75pbb79DHl/b9k//79z1331KlT6NWrFxwcHFCpUiVUq1YNnp6e2LBhQ779T548iS5duqBKlSqwtLREv379cO3aNbU+ly9fhpGREU6ePPkyh0WlkIGuAyAqL1avXo1GjRrh0aNH+PPPPxEYGIgDBw7gzJkzqFy5MgBg69atGDJkCJydnfHxxx+jYcOGuHv3LlavXo2ePXvigw8+wPz58zW2/fnnn6NTp07IyclBQkIC/vrrL6xatQpfffUVvv32WwwdOlTbh0ulUEhICO7fv4+JEycqbREREZgzZw5GjhwJS0vL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iKgMYlGKiIiIHpttoeX9Jb3U6omJiZg1axZ2796NW7duwcrKCm+++SY+/fRTWFho+ViIiCqQoa0c0LWxbaH725gZl2A0RFSWsShFREREZdK///6LO3fuICgoCI0bN8bNmzfh4+ODO3fu4P/+7/90HR4RUbllY27Cy/GIqFiwKEVERERlQseOHeHq6gojIyOsW7cOTZo0wYEDB5TldevWxWeffYZ33nkH2dnZMDBgmkNERERUmjFbIyIiojJj7dq1mDBhAg4fPgwR0VielJQEc3NzFqSIiIiIygBmbERERFRm1KtXD/Pnz893WUJCAj799FOMHz9ey1ERERER0YvQ03UARERERIXl4eGRb3tycjJ69eqFxo0bY9asWVqOioiIiIheBItSREREVGZUrlxZoy0lJQXdu3dHlSpV8NNPP8HQ0FAHkRERERFRUbEoRURERGVWcnIyvLy8YGRkhF9//RUmJrwbFBEREVFZwTmliIiIqExKSUmBl5cX0tLSsGHDBiQnJyM5ORkAYG1tDX19fR1HSERERETPwqIUERERlUknTpzAX3/9BeDxBOhPun79OurUqaODqIiIiIiosFiUIiIiosdmJ+k6gmfav3+/2vOOHTtCRHQTDBERERG9NM4pRUREREREREREWseiFBERERERERERaR2LUkREREREREREpHWcU4qIiIiIiIiIEJecjriUDOV5Tq7g3J0k3E/LQlVTQzSpaQF9PZWy3MbMGDbmJroIlcoJFqWIiIiIiIiICBv/isbXe68Uuv/kzvXh17VBCUZE5R2LUkRERERERESEoa0c0LWxLSKuxuPzHRcL7DejRyO0qWcFGzNjLUZH5RHnlCIiIiIiIiIi2JibwMXOHKsjbhTYRwVgdcQNuNiZ89I9emksShERERERERERAODo9UTEJKUXuFwAxCSl4+j1RO0FReUWi1JEREREpVxISAicnJxgYmICd3d3HDx48Jn9Dxw4AHd3d5iYmMDZ2RmhoaEF9v3hhx+gUqnw5ptvFnPURERUFsWlFFyQepF+RM/CohQRERFRKRYWFoYpU6Zg5syZiIqKQrt27dCjRw9ER0fn2//69evo2bMn2rVrh6ioKMyYMQOTJk3Cli1bNPrevHkTU6dORbt27Ur6MIiIqIywMSvcJXmF7Uf0LDqf6DwkJAQLFixATEwMmjRpguDg4Gcm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srJ0HY5WGRoacoQUERERUSnBohQREVEFpa+vzwINEREREekMJzonIiIiIiIiIiKtY1GKiIiIiIiIiIi0jpfvERFVYHWm/17i+7jxRa8S3wcREREREZU9HClFRERERERERERax5FSREREpQBHrRERERFRRcORUkREREREREREpHUsShERERERERERkdaxKEVERERERERERFrHohQREREREREREWkdJzovAZysloiIiIiIiIjo2ThSioiIiIiIiIiItI5FKSIiIiIiIiIi0joWpYiIiIiIiIiISOt0XpQKCQmBk5MTTExM4O7ujoMHDxbYd//+/VCpVBqPixcvajFiIiIiIiIiIiJ6WS880fmJEydw4cIFqFQquLi44NVXXy3yNsLCwjBlyhSEhISgbdu2WL58OXr06IHz58/DwcGhwPUuXboEc3Nz5bm1tfULHQMREREREREREelGkYtScXFxePvtt7F//35YWlpCRJCUlIROnTrhhx9+KFKBaNGiRfD29saYMWMAAMHBwdi1axeWLVuGwMDAAtezsbGBpaVlUUMnIiIiIiIiIqJSosiX702cOBHJyck4d+4cEhMTcf/+fZw9exbJycmYNGlSobeTmZmJEydOwMvLS63dy8sLERERz1zXzc0NdnZ26Ny5M/bt21fUQyAiIiIiIiIiIh0r8kipnTt3Ys+ePXBxcVHaGjdujKVLl2oUmJ4lPj4eOTk5sLW1VWu3tbVFbGxsvuvY2dlhxYoVcHd3R0ZGBtavX4/OnTtj//79aN++fb7rZGRkICMjQ3menJxc6BiJiIiIiIiIiKhkFLkolZubC0NDQ412Q0ND5ObmFjkAlUql9lxENNryNGzYEA0bNlSee3p64tatWwgKCiqwKBUYGIg5c+YUOS4iIiIiIiIiIio5Rb587/XXX8fkyZNx584dpe327dvw8/ND586dC70dKysr6Ovra4yKiouL0xg99SytW7fGlStXClweEBCApKQk5XHr1q1Cb5uIiIiIiIiIiEpGkYtS33zzDVJSUlCnTh3UrVsX9erVg5OTE1JSUrBkyZJCb8fIyAju7u4IDw9Xaw8PD0ebNm0KvZ2oqCjY2dkVuNzY2Bjm5uZqDyIiIiIiIiIi0q0iX75nb2+PkydPIjw8HBcvXoSIoHHjxujSpUuRd+7v749hw4bBw8MDnp6eWLFiBaKjo+Hj4wPg8Sin27dvY926dQAe352vTp06aNKkCTIzM7FhwwZs2bIFW7ZsKfK+iYiIiIiIiIhId4pclMrTtWtXdO3a9aV2PmjQICQkJGDu3LmIiYmBq6srtm/fDkdHRwBATEwMoqOjlf6ZmZmYOnUqbt++jUqVKqFJkyb4/fff0bNnz5eKg4iIiIiIiIiItKtQRanFixdj3LhxMDExweLFi5/Zd9KkSUUKwNfXF76+vvkuW7NmjdrzDz/8EB9++GGRtk9ERERERERERKVPoYpSX331FYYOHQoTExN89dVXBfZTqVRFLkoREREREREREVHFU6iJzq9fv47q1asr/y7oce3atRINloiIiKgiCgkJgZOTE0xMTODu7o6DBw8+s/+BAwfg7u4OExMTODs7IzQ0VG351q1b4eHhAUtLS1SuXBnNmzfH+vXrS/IQiIiIiDQU+e57c+fORVpamkb7o0ePMHfu3GIJioiIiIgeCwsLw5QpUzBz5kxERUWhXbt26NGjh9q8m0+6fv06evbsiXbt2iEqKgozZszApEmT1G4MU61aNcycORORkZE4ffo0Ro0ahVGjRmHXrl3aOiwiIiKiohel5syZg4cPH2q0p6WlYc6cOcUSFBERERE9tmjRInh7e2PMmDFwcXFBcHAw7O3tsWzZsnz7h4aGwsHBAcHBwXBxccGYMWMwevRoBAUFKX06duyIt956Cy4uLqhbty4mT56MZs2a4dChQ9o6LCIiIqKiF6VEBCqVSqP977//RrVq1YolKCIiIiJ6fOfhEydOwMvLS63dy8sLERER+a4TGRmp0b9bt244fvw4srKyNPqLCPbu3YtLly6hffv2xRc8ERER0XMUaqJzAKhatSpUKhVUKhUaNGigVpjKycnBw4cP4ePjUyJBEhEREVVE8fHxyMnJga2trVq7ra0tYmNj810nNjY23/7Z2dmIj4+HnZ0dACApKQm1atVCRkYG9PX1ERISgq5duxYYS0ZGBjIyMpTnycnJL3pYREREpEVxyemIS/nvOzwnV3DuThLup2WhqqkhmtS0gL7efzUeGzNj2JibaCW2QhelgoODISIYPXo05syZAwsLC2WZkZER6tSpA09PzxIJkoiIiKgie3qUekEj15/V/+l2MzMznDp1Cg8fPsTevXvh7+8PZ2dndOzYMd9tBgYGcqoGIiKiMmjjX9H4eu+VQvef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+Pn5adytePjw4Up/Hx8f3Lx5E/7+/rhw4QJWrVqFlStXYurUqUqfzMxMnDp1CqdOnUJmZiZu376NU6dO4erVq1o/PiIiIqq4ePkeERERUSk2aNAgJCQkYO7cuYiJiYGrq+sz71bs5OSE7du3w8/PD0uXLkXNmjWxePFi9O/fX+lz584duLm5Kc+DgoIQFBSEDh068MYyREREpDUsShERERGVckW5WzEAdOjQASdPnixwe3Xq1FEmPyciIiLSFV6+R0REREREREREWseiFBERERERERERaR2LUkREREREREREpHUsShERERERERERkdaxKEVERERERERERFrHohQREREREREREWkdi1JERERERERERKR1LEoREREREREREZHWsShFRERERERERERax6IUERERERERERFpHYtSRERERERERESkdSxKERERERERERGR1rEoRUREREREREREWseiFBERERERERERaR2LUkREREREREREpHUsShERERERERERkdaxKEVERERERERERFqn86JUSEgInJycYGJiAnd3dxw8eLDAvjExMRgyZAgaNmwIPT09TJkyRXuBEhERERERERFRsdFpUSosLAxTpkzBzJkzERUVhXbt2qFHjx6Ijo7Ot39GRgasra0xc+ZMvPLKK1qOloiIiIiIiIiIiotOi1KLFi2Ct7c3xowZAxcXFwQHB8Pe3h7Lli3Lt3+dOnXw9ddfY/jw4bCwsNBytEREREREREREVFx0VpTKzMzEiRMn4OXlpdbu5eWFiIiIYttPRkYGkpOT1R5ERERERERERKRbOitKxcfHIycnB7a2tmrttra2iI2NLbb9BAYGwsLCQnnY29sX27aJiIiIiIiIiOjF6Hyic5VKpfZcRDTaXkZAQACSkpKUx61bt4pt20RERERERERE9GIMdLVjKysr6Ovra4yKiouL0xg99TKMjY1hbGxcbNsjIiIiIiIiIqKXp7ORUkZGRnB3d0d4eLhae3h4ONq0aaOjqIiIiIiIiIiISBt0NlIKAPz9/TFs2DB4eHjA09MTK1asQHR0NHx8fAA8vvTu9u3bWLdunbLOqVOnAAAPHz7EvXv3cOrUKRgZGaFx48a6OAQiIiIiIiIiInoBOi1KDRo0CAkJCZg7dy5iYmLg6uqK7du3w9HREQAQExOD6OhotXXc3NyUf584cQLff/89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VIau5sn7oM/vLhOkrihDtZ+cl6Bjx47SsmVLCQsLkz///FMGDhwoderUURLfPPx8Kpqn36+xsbHSq1cvtYmimzdvLh988IHaZLOJiYni4eGhdpv2Ll26KHfayW8fiYmJJXEIVA4xfylfmAeVX8ypyg/mZ+UXc72Xx6LUS7hz545cvnxZcnNzlaHQvr6+0qdPH3Fzc5OTJ0/Krl27xMXFRVxdXaVWrVoyffp0XYdd7mzYsEFeeeUV5e4TTxs2bJi4urpKQkKCiIikpKTI/fv3RUTzC4JfzkXz9Ou3fv16sbCwEHd3d3F0dJRu3bopyzIzM6VLly4ybdo0pc3Pz08sLS3lf//7n9y5c0drcVcER44cUS7ZKIzz589Lp06dxNXVVRwcHOSNN95Q/sNKRfd0sn/58mXJysqS7OxsCQwMlCZNmsiOHTtERCQwMFCsrKzk6tWrattwcnJSG5JfliewpNKF+Uv5wjyofGBOVTEwPys/mOsVHxalisG6devEzc1NGWr3119/iYGBgQQGBorI4y+OGzduqM27wC/94pGTkyPTpk2Tjh07Slpampw+fVp27Nghy5cvl19++UVEHs+BUalSJXn77bdl6tSpYm1tLStWrNDYDr24CxcuSHp6unz22WfKHVu2bdsmFhYWsmjRIhF5fA1006ZNlef379+XUaNGyciRI2XBggVqcx7w76Nonnz/nj17VlxcXKRmzZpibW0tQUFB8uDBAxHRHFqc58lJMxMTE+XatWvKMp6Ll3P9+nXp1auXNGjQQH777TcRefy30K9fP+natavSz8nJSfr16yf79++X3Nxc+emnn6Rx48Zy5MgRjW3y84qKC/OXso95UPnDnKr8YH5WMTDXe3ksSr2k9PR08fLyEm9vb6WyGRQUJObm5qJSqeTcuXNq/bOzs3kr3WIWGhoqbdu2lerVq4uHh4d0795d7O3txcbGRhmO/ssvv0j//v2ldevWyq1w6cU8+SF56NAhqVevntSuXVvat28vzZo1U74ws7KyZO7cuVKlShVlqOnbb78tderUke7du4utra288847kpycrJPjKG8SEhIkPDxcJk6cKLNmzZKrV6/K7NmzpWbNmoW+LOPJz6aC5rOggj39em3ZskWqVq0qI0eOlD///FPt17G1a9dK48aNZdmyZSIicvjwYWnfvr1YWFhImzZtxMjISGbN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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# tail-30 汇总可视化\n", + "fig, axes = plt.subplots(1, 2, figsize=(12, 4))\n", + "\n", + "x = np.arange(len(summary_tail_df))\n", + "barw = 0.25\n", + "axes[0].bar(x - barw, summary_tail_df[\"POD_r1\"], width=barw, label=\"r1\")\n", + "axes[0].bar(x, summary_tail_df[\"POD_r2\"], width=barw, label=\"r2\")\n", + "axes[0].bar(x + barw, summary_tail_df[\"POD_r3\"], width=barw, label=\"r3\")\n", + "axes[0].set_xticks(x)\n", + "axes[0].set_xticklabels(summary_tail_df[\"case\"], rotation=30, ha=\"right\")\n", + "axes[0].set_ylabel(\"Energy ratio\")\n", + "axes[0].set_title(\"POD modal energy (tail-30)\")\n", + "axes[0].legend()\n", + "\n", + "axes[1].errorbar(\n", + " x,\n", + " summary_tail_df[\"etaZ2_mean\"],\n", + " yerr=summary_tail_df[\"etaZ2_std\"],\n", + " fmt=\"o\",\n", + " capsize=4,\n", + ")\n", + "axes[1].set_xticks(x)\n", + "axes[1].set_xticklabels(summary_tail_df[\"case\"], rotation=30, ha=\"right\")\n", + "axes[1].set_ylabel(\"eta_Z2\")\n", + "axes[1].set_title(\"Z2 symmetry-breaking index (tail-30)\")\n", + "\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "71b140db", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABNgAAAKcCAYAAAA6rwc8AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOy9eZxcVZn//znnLlW9J+lsBJKQgEJCiGiCMcGEIBJIxB8iGfJ1iSCLkwEVyDjjhGVA1EHEJSqbKIsMCpkZRNCJSBAIIg0TICwD6KAGEiBNyNrppbrq3nt+f5x7bt1au6qru7N93q9Xvbr71rnLuVV9nnue83meRyilFAghhBBCCCGEEEIIIf1C7ukLIIQQQgghhBBCCCFkX4YONkIIIYQQQgghhBBCaoAONkIIIYQQQgghhBBCaoAONkIIIYQQQgghhBBCaoAONkIIIYQQQgghhBBCaoAONkIIIYQQQgghhBBCaoAONkIIIYQQQgghhBBCaoAONkIIIYQQQgghhBBCaoAONkIIIYQQQgghhBBCaoAONkIIIWSQuOOOOyCEiF62beOQQw7B5z//ebz11lsF7V999VWcffbZmDBhAlzXxciRI7Fo0SL89re/LWj72GOP5RzbdV2MGjUKxx13HC677DK88cYbQ9HFfrN69WpcddVVRd879NBDcfbZZ/fruLXsO5ScffbZOPTQQ4f8vJlMBkceeSS+9a1vRduefPJJXHXVVdi5c2e/j3vVVVdBCJGzbf78+Zg/f35F+1922WV4//vfjxEjRiCZTGLy5Mn4whe+UPR7nMlk8LWvfQ2HHnooEokEjjzySPzoRz8qaLd06VJ84hOf6E93Bo0bb7wRd9xxx56+DEIIIYQMAnSwEUIIIYPM7bffjra2NqxZswbnn38+7r77bsydOxddXV1Rm1/+8pd4//vfj//5n//BFVdcgYcffhg33XQTAGDRokX453/+56LH/rd/+ze0tbXh0Ucfxa233or58+fjtttuw5QpU/Dzn/98SPrXH1avXo2vfe1rRd+77777cMUVVwzxFR0Y3HjjjdixYwe+9KUvRduefPJJfO1rX6vJwXbeeeehra2t3/vv3LkTn/rUp/Czn/0MDz74IL7yla/gN7/5DWbNmoVt27bltL3gggtwzTXX4MILL8Tvfvc7nH766bjooovwb//2bzntrrrqKvz3f/83HnnkkX5f10BDBxshhBCy/2Lv6QsghBBC9nemTZuGmTNnAgBOOOEE+L6Pr3/96/jVr36Fz3zmM/jrX/+KpUuX4uijj8Zjjz2GhoaGaN+/+7u/wz/8wz/guuuuwwc+8AH8v//3/3KO/Z73vAcf+tCHor//v//v/8M//uM/4qMf/SjOPvtsTJ8+HUcfffTQdHSAeP/737+nL2G/xPM8XHfddTjnnHNyvmMDwSGHHIJDDjmk3/vfcMMNOX/Pnz8fkyZNwqJFi3D//ffjnHPOAQC8/PLLuPXWW/HNb34T//RP/xS13bZtG77xjW9g2bJlGDFiBADgsMMOwymnnIJvfetb+MhHPtLvayOEEEIIqQQq2AghhJAhxjjETPjb97//fXR3d+NHP/pRUcfHd7/7XQwbNgzf/OY3Kzr+iBEj8OMf/xie5+H73/9+2bZBEOAb3/gGjjjiCNTV1WHYsGGYPn06fvCDH0RtTPjf+vXr8clPfhLNzc1oaWnBZz/7Wbz77rs5x1u1ahUWLFiAgw46CHV1dZgyZQr+5V/+JUetd/bZZ0cOlXiY6+uvvw6gMMwzlUrhH//xH3HMMcegpaUFI0aMwOzZs3H//fdXdD+KIYTAF7/4Rdx+++1R32fOnImnnnoKSilcd911mDRpEhobG/GRj3wEf/nLXwqOcdttt+F973sfkskkRowYgdNPPx2vvvpqQbs77rgDRxxxBBKJBKZMmYI777yz6DWl02l84xvfwJFHHolEIoFRo0bh85//fME9fuSRRzB//ny0trairq4OEyZMwBlnnIHu7u6yfX7ggQfw1ltvYenSpdG2q666KnJUTZo0KfosHnvsMQCVfZ7mOPkhorUyatQoAIBtZ9eDf/WrX0Ephc9//vM5bT//+c+jp6cHDz74YM72pUuX4uGHH8Zf//rXfl1DNd+9IAjwox/9CMccc0z0v/ShD30IDzzwAAD9vX755Zexdu3a6D5XEyZ86KGH4tRTT8WDDz6ID3zgA6irq8ORRx6J2267raDtW2+9hS984QsYP348XNfFuHHjsHjxYrzzzjv9ug+EEEII6Rsq2AghhJAhxjhrjANhzZo1GDNmTI4SLU59fT0WLFiA//iP/0B7ezvGjh3b5zmOPfZYHHTQQXj88cfLtvv2t7+Nq666CpdffjnmzZuHTCaDP/3pT0XDBU8//XSceeaZWLZsGV5++WVcccUVeOWVV/D000/DcRwAwGuvvYZFixbh4osvRkNDA/70pz/h2muvxf/8z/9EoXpXXHEFurq68F//9V85YYUHHXRQ0Wvs7e3F9u3b8ZWvfAUHH3ww0uk0Hn74YXzyk5/E7bffjs997nN93o9i/OY3v8H69evxrW99C0IIfPWrX8XHPvYxnHXWWfjb3/6G66+/Hrt27cLy5ctxxhln4Pnnn4+cSNdccw0uvfRSfOpTn8I111yDbdu24aqrrsLs2bOxbt06vOc97wGgnWuf//zncdppp+G73/0udu3ahauuugq9vb2QMrvOGQQBTjvtNPzhD3/AP//zP2POnDl44403cOWVV2L+/Pl45plnUFdXh9dffx0f+9jHMHfuXNx2220YNmwY3nrrLTz44INIp9Oor68v2d///u//xujRozF16tRo23nnnYft27fjRz/6EX75y19Gn4FpU8nnOZB4nhd9By+++GK8973vxSc/+cno/f/93//FqFGjCv4Hpk+fHr0fZ/78+VBKYfXq1TlhsfPnz8fatWuhlCp7PdV8984++2zcddddOPfcc3H11VfDdV0899xzkeP4vvvuw+LFi9HS0oIbb7wRAJBIJKq6Py+88AL+8R//Ef/yL/+CMWPG4Kc//SnOPfdcHH744Zg3bx4A7Vw79thjkclkcOmll2L69OnYtm0bfve732HHjh0YM2ZMVeckhBBCSIUoQgghhAwKt99+uwKgnnrqKZXJZNTu3bvVb37zGzVq1CjV1NSk2tvblVJKJZNJ9aEPfajssb761a8qAOrpp59WSin16KOPKgDqP//zP0vuM2vWLFVXV1f2uKeeeqo65phjyra58sorFQB1ySWX5Gz/+c9/rgCou+66q+h+QRCoTCaj1q5dqwCoF154IXrvwgsvVKUeQyZOnKjOOuusktfjeZ7KZDLq3HPPVe9///ur2tcAQI0dO1Z1dnZG2371q18pAOqYY45RQRBE21euXKkAqBdffFEppdSOHTtUXV2dWrRoUc4xN27cqBKJhPr0pz+tlFLK9301btw49YEPfCDneK+//rpyHEdNnDgx2nb33XcrAOree+/NOea6desUAHXjjTcqpZT6r//6LwVAPf/88332MZ8pU6aoU045pWD7ddddpwCoDRs2lN2/3OdpviNxjj/+eHX88cdXfH2bN29WAKLX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nbjqqqsKlIj/7//9P/z85z/HokWLcNFFF+GDH/wgHMfBm2++iUcffRSnnXYaTj/9dNx888145JFH8LGPfQwTJkxAKpXCbbfdBgD46Ec/Wva658+fj6uvvhrd3d05ykTzWfzgBz/AWWedBcdxcMQRR1T1edbCiy++iEsuuQSLFy/G5MmTIaXESy+9hO9///tobW3FV77ylajtUUcdhXPPPRdXXnklLMvCsccei4ceegi33HILvvGNbxSEiL744ovo7u7GCSeckLP9xBNPxNq1a/vMw1bpd2/u3LlYunQpvvGNb+Cdd97BqaeeikQigfXr16O+vj6qYGoUeKtWrcLkyZORTCaj+z9QXH311fjtb3+LefPm4dJLL8XRRx+NnTt34sEHH8Ty5ctx5JFHDuj5CCGEEBKyh4ssEEIIIfstporounXrKmr/8ssvq7POOksdcsghynEcNWLECHXKKaeo//7v/y5oa6qImpdt26q1tVXNnj1bXXrpper111+v6Jzf/e531Zw5c9TIkSOV67pqwoQJ6txzz83Z31SIfPbZZ9XHP/5x1djYqJqamtSnPvUp9c477+Qc78knn1SzZ89W9fX1atSoUeq8885Tzz33nAKgbr/99qhdb2+vOu+889SoUaOUECKnimWxSqDf+ta31KGHHqoSiYSaMmWK+slPflK0cmU1VUQvvPDCnG0bNmxQANR1112Xs71Uxdaf/vSnavr06cp1XdXS0qJOO+009fLLLxec66c//al6z3veo1zXVe9973vVbbfdps4666ycKqJKKZXJZNR3vvMd9b73vU8lk0nV2NiojjzySPX3f//36rXXXlNKKdXW1qZOP/10NXHiRJVIJFRra6s6/vjj1QMPPNBnn//yl78oIYT6j//4j4L3VqxYocaNG6eklAqAevTRR5VSlX+etVQRbW9vV5/97GfVYYcdpurr65Xrumry5Mlq2bJlauPGjQXt0+m0uvLKK9WECROie/rDH/6w6LGvuOIKNXLkSJVKpQqurdLH4Eq/e77vq+9///tq2rRp0Xdi9uzZ6te//nXU5vXXX1cLFixQTU1NCkDBd6AcEydOVB/72McKthe7z5s2bVLnnHOOGjt2rHIcR40bN06deeaZBf+vhBBCCBk4hFIVlBgjhBBCyAHLVVddha997Wt49913+8ybRfZuTJVSU/Vyf8b3fRx++OH49Kc/jW9+85t7+nIIIYQQsp/DHGyEEEIIIQcI11xzDR5++GGsW7duT1/KoHPXXXehs7MT//RP/7SnL4UQQgghBwDMwUYIIYQQcoAwbdo03H777UUrm+5vBEGAn//85xg2bNievpSy+L6PcgElQghYljWEV0QIIYSQ/sAQUUIIIYQQQvYQhx56KN54442S7x9//PF47LHHhu6CCCGEENIvqGAjhBBCCCFkD/HrX/8avb29Jd9vamoawqshhBBCSH+hgo0QQgghhBBCCCGEkBpgkQNCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmqADjZCCCGEEEIIIYQQQmrA3tMXQAghezOpVArpdLrq/VzXRTKZHIQrIoQQsr9AG0MIIWQw6K99AWhjaoEONkIIKUEqlUJrXSO64Ve979ixY7FhwwYaJ0IIIUWhjSGEEDIY1GJfANqYWqCDjRBCSpBOp9ENH5/DwXCriKhPI8Cd7W8hnU7TMBFCCCkKbQwhhJDBoL/2BaCNqRU62AghpA/qhAVXVG6cLCUANYgXRAghZL+BNoYQQshgUK19AWhjaoVFDgghhBBCCCGEEEIIqQE62AghpA+kAKwqXlL07zw33ngjJk2ahGQyiRkzZuAPf/hD2fZr167FjBkzkEwmMXnyZNx8880Fbe69915MnToViUQCU6dOxX333VfyeNdccw2EELj44ov71wFCCCFVM1Q2hhBCyIFFtfaFNqZ26GAjhJA+sISo+lUtq1atwsUXX4zLLrsM69evx9y5c7Fw4UJs3LixaPsNGzZg0aJFmDt3LtavX49LL70UX/7yl3HvvfdGbdra2rBkyRIsXboUL7zwApYuXYozzzwTTz/9dMHx1q1bh1tuuQXTp0+v+toJIYT0n6GwMYQQQg48+mNfaGNqgw42Qgjpg2pXfqx+2KXvfe97OPfcc3HeeedhypQpWLlyJcaPH4+bbrqpaPubb74ZEyZMwMqVKzFlyhScd955OOecc/Cd73wnarNy5UqcdNJJWLFiBY488kisWLECJ554IlauXJlzrM7OTnzmM5/BT37yEwwfPrz6iyeEENJvhsLGEEIIOfDoj32hjakNOtgIIaQPBnvlJ51O49lnn8WCBQtyti9YsABPPvlk0X3a2toK2p988sl45plnkMlkyrbJP+aFF16Ij33sY/joRz9a1XUTQgipHaoLCCGEDAZUsA09rCJKCCF9UO1qjhX+7OjoyNmeSCSQSCQK2m/duhW+72PMmDE528eMGYP29vai52hvby/a3vM8bN26FQcddFDJNvFj3nPPPXjuueewbt26SrtHCCFkAOmvjSGEEELK0R9FGm1MbVDBRgghfdDflZ/x48ejpaUlel1zzTVlzyPyVoyUUgXb+mqfv73cMTdt2oSLLroId911F5LJZB93gRBCyGBAdQEhhJDBgAq2oYcKNkII6QOB6lYjjFnatGkTmpubo+3F1GsAMHLkSFiWVaBW27JlS4ECzTB27Nii7W3bRmtra9k25pjPPvsstmzZghkzZkTv+76Pxx9/HNdffz16e3thWVzHIoSQwaS/NoYQQggpR7X2xexD+g8VbIQQ0gf9Xflpbm7OeZVysLmuixkzZmDNmjU529esWYM5c+YU3Wf27NkF7R966CHMnDkTjuOUbWOOeeKJJ+Kll17C888/H71mzpyJz3zmM3j++efpXCOEkCGA6gJCCCGDARVsQw8VbIQQ0gdDkR9n+fLlWLp0KWbOnInZs2fjlltuwcaNG7Fs2TIAwIoVK/DWW2/hzjvvBAAsW7YM119/PZYvX47zzz8fbW1tuPXWW3H33XdHx7zoooswb948XHvttTjttNNw//334+GHH8YTTzwBAGhqasK0adNyrqOhoQGtra0F2wkhhAwOzMFGCCFkMGAOtqGHDjZCCNkLWLJkCbZt24arr74amzdvxrRp07B69WpMnDgRALB582Zs3Lgxaj9p0iSsXr0al1xyCW644QaMGzcOP/zhD3HGGWdEbebMmYN77rkHl19+Oa644gocdthhWLVqFWbNmjXk/SOEEEIIIYSQ/RmhTFZsQgghOXR0dKClpQVX109GUlS+npNSPv61+2/YtWtXTg42QgghxEAbQwghZDDor30BaGNqhQo2QgjpA4bvEEIIGSxoYwghhAwGDBEdeuhgI4SQPqg24afF+juEEEIqhDaGEELIYNCfogW0MbVBBxshhPSBrHL1h+WZCSGEVAptDCGEkMGgWvsC0MbUCh1shBDSB1QXEEIIGSxoYwghhAwGVLANPXRQEkJIH5j8BdW8CCGEkEqgjSGEEDIY9Me+9MfG3HjjjZg0aRKSySRmzJiBP/zhD2Xbr127FjNmzEAymcTkyZNx8803F7S59957MXXqVCQSCUydOhX33XdfyeNdc801EELg4osvrv7iBxg62AghpA84+SGEEDJY0MYQQggZDIbCwbZq1SpcfPHFuOyyy7B+/XrMnTsXCxcuxMaNG4u237BhAxYtWoS5c+di/fr1uPTSS/HlL38Z9957b9Smra0NS5YswdKlS/HCCy9g6dKlOPPMM/H0008XHG/dunW45ZZbMH369OoufJCgg40QQvrAyKureRFCCCGVQBtDCCFkMOiPfanWxnzve9/Dueeei/POOw9TpkzBypUrMX78eNx0001F2998882YMGECVq5ciSlTpuC8887DOeecg+985ztRm5UrV+Kkk07CihUrcOSRR2LFihU48cQTsXLlypxjdXZ24jOf+Qx+8pOfYPjw4VXfn8GADjZCCCGEEEIIIYQQUjHpdBrPPvssFixYkLN9wYIFePLJJ4vu09bWVtD+5JNPxjPPPINMJlO2Tf4xL7zwQnzsYx/DRz/60Vq7MmCwyAEhhPSBherk0pYatEshhBCyn0EbQwghZDCo1r4AWRvT0dGRsz2RSCCRSORs27p1K3zfx5gxY3K2jxkzBu3t7UWP397eXrS953nYunUrDjrooJJt4se855578Nxzz2HdunVV9W+woYKNEEL6QFYpq5YM3yGEEFIhtDGEEEIGg2rtS9zGjB8/Hi0tLdHrmmuuKXkekWeXlFIF2/pqn7+93DE3bdqEiy66CHfddReSyWQFd2LooIKNEEL6oNqEn0xATQghpFJoYwghhAwG/SlaYNpv2rQJzc3N0fZ89RoAjBw5EpZlFajVtmzZUqBAM4wdO7Zoe9u20draWraNOeazzz6LLVu2YMaMGdH7vu/j8ccfx/XXX4/e3l5YllVhjwcWKtgIIaQPmICaEELIYEEbQwghZDCopchBc3NzzquYg811XcyYMQNr1qzJ2b5mzRrMmTOn6DXNnj27oP1DDz2EmTNnwnGcsm3MMU888US89NJLeP7556PXzJkz8ZnPfAbPP//8HnOuAVSwEUJIn1BdQAghZLCgjSGEEDIY1KJgq5Tly5dj6dKlmDlzJmbPno1bbrkFGzduxLJlywAAK1aswFtvvYU777wTALBs2TJcf/31WL58Oc4//3y0tbXh1ltvxd133x0d86KLLsK8efNw7bXX4rTTTsP999+Phx9+GE888QQAoKmpCdOmTcu5joaGBrS2thZsH2roYCOEkD6oVjFAdQEhhJBKoY0hhBAyGPRH9Vxt+yVLlmDbtm24+uqrsXnzZkybNg2rV6/GxIkTAQCbN2/Gxo0bo/aTJk3C6tWrcckll+CGG27AuHHj8MMf/hBnnHFG1GbOnDm45557cPnll+OKK67AYYcdhlWrVmHWrFlVXduegCGihBDSBzJM+FnNixBCCKmEobAxN954IyZNmoRkMokZM2bgD3/4Q9n2a9euxYwZM5BMJjF58mTcfPPNBW3uvfdeTJ06FYlEAlOnTsV9992X8/5NN92E6dOnR+FFs2fPxm9/+9uqr50QQkj/6I996Y+NueCCC/D666+jt7cXzz77LObNmxe9d8cdd+Cxxx7LaX/88cfjueeeQ29vLzZs2BCp3eIsXrwYf/rTn5BOp/Hqq6/ik5/8ZNlreOyxx7By5cqqr32goYONEEL6QFii6hchhBBSCYNtY1atWoWLL74Yl112GdavX4+5c+di4cKFOYqCOBs2bMCiRYswd+5crF+/Hpdeeim+/OUv4957743atLW1YcmSJVi6dCleeOEFLF26FGeeeSaefvrpqM0hhxyCb33rW3jmmWfwzDPP4CMf+QhOO+00vPzyy/27UYQQQqqiP/aF85jaEMrURCWEEJJDR0cHWlpa8F/jj0K9rDxZZnfgY/Gml7Fr166c6juEEEKIYahszKxZs/CBD3wAN910U7RtypQp+MQnPoFrrrmmoP1Xv/pVPPDAA3j11VejbcuWLcMLL7yAtrY2ADokqKOjI0eRdsopp2D48OE5eXTyGTFiBK677jqce+65FfWVEEJI9fTXvgCcx9QKFWyEEEIIIYTsh6TTaTz77LNYsGBBzvYFCxbgySefLLpPW1tbQfuTTz4ZzzzzDDKZTNk2pY7p+z7uuecedHV1Yfbs2f3tDiGEELJXwyIHhBDSF5aEkFWsRwgKgwkhhFRIP21MR0dHzuZEIoFEIpGzbevWrfB9H2PGjMnZPmbMGLS3txc9fHt7e9H2nudh69atOOigg0q2yT/mSy+9hNmzZyOVSqGxsRH33Xcfpk6dWnlfCSGE9J9q7QvAeUyNUMFGCCF9IGSVuQskcxcQQgipjP7amPHjx6OlpSV6FQv3jM6Rl7RaKVWwra/2+dsrOeYRRxyB559/Hk899RT+4R/+AWeddRZeeeWVMneDEELIQFG1feE8pmaoYCOEkD6QloCsIuGnBA0TIYSQyuivjdm0aVNOfpx89RoAjBw5EpZlFSjLtmzZUqBAM4wdO7Zoe9u20draWrZN/jFd18Xhhx8OAJg5cybWrVuHH/zgB/jxj39cSVcJIYTUQLX2BeA8plaoYCOEkD4QUlb9IoQQQiqhvzamubk551XMwea6LmbMmIE1a9bkbF+zZg3mzJlT9Hpmz55d0P6hhx7CzJkz4ThO2TaljmlQSqG3t7f8DSGEEDIg9Me+cB5TG1SwEUJIH1DBRgghZLAYbBuzfPlyLF26FDNnzsTs2bNxyy23YOPGjVi2bBkAYMWKFXjrrbdw5513AtAVQ6+//nosX74c559/Ptra2nDrrbfmVAe96KKLMG/ePFx77bU47bTTcP/99+Phhx/GE088EbW59NJLsXDhQowfPx67d+/GPffcg8ceewwPPvhgVddPCCGkf1DBNvTQPUkIIX1Qde6CKg2Z4cYbb8SkSZOQTCYxY8YM/OEPfyjbfu3atZgxYwaSySQmT56Mm2++uaDNvffei6lTpyKRSGDq1Km47777ct6/6aabMH369EgBMXv2bPz2t7/t1/UTQgipnsG2MUuWLMHKlStx9dVX45hjjsHjjz+O1atXY+LEiQCAzZs3Y+PGjVH7SZMmYfXq1XjsscdwzDHH4Otf/zp++MMf4owzzojazJkzB/fccw9uv/12TJ8+HXfccQdWrVqFWbNmRW3eeecdLF26FEcccQROPPFEPP3003jwwQdx0kkn1XjHCCGEVEJ/7Et/5zFEI5TJWkoIISSHjo4OtLS04DfHzESDVbngt8v3cOrzz2DXrl05+XHKsWrVKixduhQ33ngjjjvuOPz4xz/GT3/6U7zyyiuYMGFCQfsNGzZg2rRpOP/88/H3f//3+OMf/4gLLrgAd999dzQJamtrw9y5c/H1r38dp59+Ou677z7867/+K5544oloEvTrX/8almVFOXJ+9rOf4brrrsP69etx1FFHVdxnQggh1TGUNoYQQsiBQ3/tC0AbUyt0sBFCSAmMcVo949iqJz+Lnl1XlWGaNWsWPvCBD+Cmm26Ktk2ZMgWf+MQnilaG++pXv4oHHngAr776arRt2bJleOGFF9DW1gZAqxY6OjpyFGmnnHIKhg8fnhPqk8+IESNw3XXX4dxzz63o2gkhhFTPUNoYQgghBw79tS8AbUytMESUEEIGiY6OjpxXqcTO6XQazz77LBYsWJCzfcGCBXjyySeL7tPW1lbQ/uSTT8YzzzyDTCZTtk2pY/q+j3vuuQddXV2YPXt2RX0khBBCCCGEEEIHGyGE9IkQAkJW8RI6d8H48ePR0tISvYop0QBg69at8H0fY8aMydk+ZswYtLe3F92nvb29aHvP87B169aybfKP+dJLL6GxsRGJRALLli3Dfffdh6lTp1Z+gwghhPSb/toYQgghpBxV2xfamJphFVFCCOkDaUlIq/L1CKl0202bNuVIqxOJRNn98g2aUqqskSvWPn97Jcc84ogj8Pzzz2Pnzp249957cdZZZ2Ht2rV0shFCyBDQXxtDCCGElKNa+wLQxtQKHWyEENIH1VbUEUq3NZU5+2LkyJGwLKtAWbZly5YCBZph7NixRdvbto3W1taybfKP6bpuVORg5syZWLduHX7wgx/gxz/+cZ/XTgghpDb6a2MIIYSQcvSnKihtTG3QPUkIIX0w2OWtXdfFjBkzsGbNmpzta9aswZw5c4ruM3v27IL2Dz30EGbOnAnHccq2KXVMg1KqZL44QgghA8tg2xhCCCEHJv2xL7QxtUEFGyGE9MFQhO8sX74cS5cuxcyZMzF79mzccsst2LhxI5YtWwYAWLFiBd566y3ceeedAHTF0Ouvvx7Lly/H+eefj7a2Ntx666051UEvuugizJs3D9deey1OO+003H///Xj44YfxxBNPRG0uvfRSLFy4EOPHj8fu3btxzz334LHHHsODDz5YdR8IIYRUD0NECSGEDAYMER166GAjhJC+qHY1px/S6iVLlmDbtm24+uqrsXnzZkybNg2rV6/GxIkTAQCbN2/Gxo0bo/aTJk3C6tWrcckll+CGG27AuHHj8MMf/hBnnHFG1GbOnDm45557cPnll+OKK67AYYcdhlWrVmHWrFlRm3feeQdLly7F5s2b0dLSgunTp+PBBx/ESSedVHUfCCGE9IMhsDGEEEIOQPqjSKONqQmhTFZsQgghOXR0dKClpQWPfnQeGp3K1yM6Mx5OePhx7Nq1q6IcbIQQQg48aGMIIYQMBv21LwBtTK1QwUYIIX0gLAlRhbxaBJRWE0IIqQzaGEIIIYNBtfYFoI2pFTrYCCGkD6QlIKuQV8uA0mpCCCGVQRtDCCFkMKjWvgC0MbVC9yQhhBBCCCGEEEIIITVABRshhPRBtSWrBVd+CCGEVAhtDCGEkMGgWvsC0MbUCh1shBDSB8yPQwghZLCgjSGEEDIYMAfb0EMHGyGE9IG0UGV+nEG8GEIIIfsVtDGEEEIGg2rtC0AbUyt0sBFCSB8IKSBkFeE7VbQlhBByYEMbQwghZDCo1r6YfUj/oYONEEL6QEoJWYW8WvqUVhNCCKkM2hhCCCGDQbX2BaCNqRU62AghpA+qTkBdpRSbEELIgQttDCGEkMGgX0UOaGNqgg42Qgjpg6oTUFe5UkQIIeTAhTaGEELIYNCvIge0MTVBBxshhPSBkBJCVjH5qaItIYSQAxvaGEIIIYNBtfbF7EP6D+8eIYQQQgghhBBCCCE1QAUbIYT0gbSqTEBNaTUhhJAKoY0hhBAyGFRrX8w+pP/w7u3n3HHHHRBCQAiBxx57rOB9pRQOP/xwCCEwf/78AT23EAJXXXXVgB3vN7/5DT73uc/h6KOPhuM4EIIJGMkQEeYvqPQFGiZyALC/2JeOjg5885vfxPz58zF27Fg0Njbi6KOPxrXXXotUKjUg5yCkLLQxhBSwv9gYALjsssvw/ve/HyNGjEAymcTkyZPxhS98AW+88caAnYOQolRpX2hjaod37wChqakJt956a8H2tWvX4q9//Suampr2wFVVx3333YennnoKU6dOxfve9749fTnkAELIKo0TcxeQA4h93b5s3LgRK1euxAc+8AHccssteOCBB7B48WJcddVVOPXUU6GU2tOXSPZzaGMIKc2+bmMAYOfOnfjUpz6Fn/3sZ3jwwQfxla98Bb/5zW8wa9YsbNu2bU9fHtmPqdq+0MbUDENEDxCWLFmCn//857jhhhvQ3Nwcbb/11lsxe/ZsdHR07MGrq4yf/OQnkOE//Be/+EU8++yze/iKyIECE1ATUpp93b5MmjQJr7/+OhoaGqJtH/nIR9DQ0IB/+qd/wh//+Ed8+MMf3oNXSPZ3aGMIKc2+bmMA4IYbbsj5e/78+Zg0aRIWLVqE+++/H+ecc84eujKyv8MiB0MP794Bwqc+9SkAwN133x1t27VrF+69996Sg/r27dtxwQUX4OCDD4brupg8eTIuu+wy9Pb25rTr6OjA+eefj9bWVjQ2NuKUU07B//3f/xU95muvvYZPf/rTGD16NBKJBKZMmVJgdEohh/Cf/dBDD8XZZ59dsH3+/PkDLkMnez96Rceq4sWhlRw47Ov2paGhIce5ZvjgBz8IANi0aVOfx6gG2heSD20MIaXZ121MKUaNGgUAsO2B1bvQxpA41dsX2pha4d07QGhubsbixYtx2223RdvuvvtuSCmxZMmSgvapVAonnHAC7rzzTixfvhz//d//jc9+9rP49re/jU9+8pNRO6UUPvGJT+Df//3f8Y//+I+477778KEPfQgLFy4sOOYrr7yCY489Fv/7v/+L7373u/jNb36Dj33sY/jyl7+Mr33ta4PTcUIGgKql1TRM5ABif7UvjzzyCADgqKOO6tf+hFTKUNiYG2+8EZMmTUIymcSMGTPwhz/8oWz7tWvXYsaMGVG+qJtvvrmgzb333oupU6cikUhg6tSpuO+++3Lev+aaa3DssceiqakJo0ePxic+8Qn8+c9/rvrayYHN/mRjPM9DT08P1q9fj4svvhjvfe97c66JkIGmP/aF85jaYIjoAcQ555yDE044AS+//DKOOuoo3Hbbbfi7v/u7orkLfvazn+HFF1/Ef/zHf+Dv/u7vAAAnnXQSGhsb8dWvfhVr1qzBSSedhN/97nd49NFH8YMf/ABf/vKXo3au6+Kyyy7LOeby5cvR1NSEJ554IpJ4n3TSSejt7cW3vvUtfPnLX8bw4cMH+S4QUj1SyqoUlEOptiRkb2B/sy8vvvgivv3tb+P000/H9OnT+3tbCKmIwbYxq1atwsUXX4wbb7wRxx13HH784x9j4cKFeOWVVzBhwoSC9hs2bMCiRYtw/vnn46677sIf//hHXHDBBRg1ahTOOOMMAEBbWxuWLFmCr3/96zj99NNx33334cwzz8QTTzyBWbNmAdBOugsvvBDHHnssPM/DZZddhgULFuCVV14pqholpBT7g41pb2/HQQcdFP09a9YsPProo2hsbKzp3hBSjmrti9mH9B/evQOI448/Hocddhhuu+02vPTSS1i3bl1JafUjjzyChoYGLF68OGe7kRz//ve/BwA8+uijAIDPfOYzOe0+/elP5/ydSqXw+9//Hqeffjrq6+vheV70WrRoEVKpFJ566qmB6GZRfN/POWcQBIN2LrL/wZUfQsqzP9mX119/HaeeeirGjx+Pn/70p322p30htTLYNuZ73/sezj33XJx33nmYMmUKVq5cifHjx+Omm24q2v7mm2/GhAkTsHLlSkyZMgXnnXcezjnnHHznO9+J2qxcuRInnXQSVqxYgSOPPBIrVqzAiSeeiJUrV0ZtHnzwQZx99tk46qij8L73vQ+33347Nm7cyBy6pGr2BxszcuRIrFu3Dk888QR+8pOfYPv27TjhhBOwefPmsvvRxpBaoIJt6OHdO4AQQuDzn/887rrrLtx8881473vfi7lz5xZtu23bNowdOxZCiJzto0ePhm3bUcWbbdu2wbZttLa25rQbO3ZswfE8z8OPfvQjOI6T81q0aBEAYOvWrQPV1QJOPPHEnHMymSipBhomQsqzv9iXN954AyeccAJs28bvf/97jBgxos99aF9IrfTXxnR0dOS88vNLAUA6ncazzz6LBQsW5GxfsGABnnzyyaLX09bWVtD+5JNPxjPPPINMJlO2TaljAjpvFoCK/q8IibM/2BjbtjFz5kwcd9xxOO+88/DII4/gb3/7G771rW+V3Y82htQCHWxD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a9asyUkYHcckmI5TKgl1fpv8JNRf/OIX8ctf/hKPPPIIk1ATQsgQ018bQwghhJSjavtCG1MzvHuEELIXsHz5cvz0pz/FbbfdhldffRWXXHIJNm7ciGXLlgHQFUA/97nPRe2XLVuGN954A8uXL8err76K2267DbfeemtOufeLLroIDz30EK699lr86U9/wrXXXouHH34YF198cdTmwgsvxF133YVf/OIXaGpqQnt7O9rb29HT0zNkfSeEEEIIIYTsm9x4442YNGkSkskkZsyYgT/84Q9l269duxYzZsxAMpnE5MmTcfPNNxe0uffeezF16lQkEglMnToV9913X87711xzDY499lg0NTVh9OjR+MQnPoE///nPA9qv/kAHGyGE9IGwLMgqXsKyqj7HkiVLsHLlSlx99dU45phj8Pjjj1eUhPqxxx7DMcccg69//eslk1DffvvtmD59Ou64446iSah37dqF+fPn46CDDopeq1atquGOEUIIqZShsDGEEEIOPKq1L/2xMatWrcLFF1+Myy67DOvXr8fcuXOxcOHCnHlLnA0bNmDRokWYO3cu1q9fj0svvRRf/vKXce+990Zt2trasGTJEixduhQvvPACli5dijPPPBNPP/101Gbt2rW48MIL8dRTT2HNmjXwPA8LFixAV1dX/27WACFUBRlIgyDA22+/jaamprIJtwkhZG9EKYXdu3dj3LhxkFVU0jElrt9YuRzNdcXDO4vu19OLiRd/j+WtK4Q2hhCyL0Mbs/dC+0II2dfpj43pr30Bqrcxs2bNwgc+8IGocjQATJkyBZ/4xCdwzTXXFLT/6le/igceeACvvvpqtG3ZsmV44YUX0NbWBkALDzo6OvDb3/42anPKKadg+PDhuPvuu4tex7vvvovRo0dj7dq1mDdvXsX9HWgqKnLw9ttvF1TDI4SQfY1Nmzb1K4E/E1APLrQxhJD9AdqYvQ/aF0LI/kJ/bMxgFzlIp9N49tln8S//8i852xcsWIAnn3yy6D5tbW1YsGBBzraTTz4Zt956KzKZDBzHQVtbGy655JKCNitXrix5Lbt27QIAjBgxouLrHwwqcrA1NTUBAF577bXod0L2JIOxBtnfYvLBflCFvlwXKu1fBWJYAKh4BVmWaFZsc37bnD9VgN2dXTj8Pe/p9/hlkn5W055Ujvlc/lKljdkP/vUIqYlKxudq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nngCCxYswHPPPYfLLrsMK1aswIoVK8x5P/jBD2LXrl144IEHcPfdd0/pPEnBRsx4WMmj0+/TNEd746AVAdUezZyj3vlrwETQ0KOhOTRzDXWuKxDtGydfGadmHsTUQp8oQYRoRDnVzJh2nEXhc1Q7T+mYOu8nGDePMjiPPtqhkXOEr6HkekTo0Q5kYwiCIIhO0Ip96ZSN2b59O1KplHGuAcB5552HVCqFF198seIxu3fvxvDwMC699FLzXCwWw/nnn2+OefXVV5HP5yNjhoaGsGTJkqrnBYB0Oo05c+a0O60yqIsocczRqBOs3tIwZc46fcN9tB1trVLvuhvsNKc3QvUKUzMR1C4gHb6makqI0uerKNm0cy3f5lfTrLGhzU9nCH+qpGgjCDSmFmu0A2YnalpOwTnL7EXp/Kwpvu5K11z6XOjfgWi+gHQpZGMIgiCIarSz4rfiMNPjR0dHI8/HYjHEYrGWr2V4eBjz5s0re37evHkYHh6uegwADAwMRJ4fGBjA22+/bca4rovZs2eXjal23l/96le4//778a1vfavpedSjIQebUHm44TxXgiCOY2ays7CBDVvYjASiuHYJqlszIzlaNoacdARxAlDNnk2F80/9f5xszIyF9jAEQcx0almOQAAT40fHxixYsCDy76997WvYsGFD2bgNGzbg61//es1zvfLKKwAAxsrvvoUQFZ8PU/p6I8dUG7Nnzx6sWLECn/3sZ/GFL3yh5jlaoSEHmzZKH1q0aMovgCAIYroYGxtDKpVq+jg/aE4x4M9g/+RMRNuYRWRjCII4hiEbM/OgPQxBEMcLrdiYZu2LPgYA3n33XfT29prnq6nXbrjhBlx11VU1z3nqqafitddew759+8peO3DgQJlCTTM4OAhAqtTmz59vnt+/f785ZnBwEJ7nYWRkJKJi279/P5YvXx453549e3DhhRdi2bJleOihh2pec6s05GAbGhrCu+++i56enrqeQoIgiJmGEAJjY2MYGhpq6XhK3+ksZGMIgjiWIRszcyH7QhDEsU47NqadFNHe3t6Ig60a/f396O/vrztu2bJlSKfTePnll3HuuecCAF566SWk0+kyR5hm4cKFGBwcxLZt23DWWWcBADzPw/PPP48777wTAHD22WfDcRxs27YNq1evBgDs3bsXr7/+Ou666y5zrvfffx8XXnghzj77bHz3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NIODgxXH27aNvr6+mmPC55w/fz4+8pGPRMYsXry4ql0jCIIgppZW7At1EW0PcrARBEHUYToM0z333INrrrkGX/jCF7B48WLcd999WLBgQdVI/3e+8x2cfPLJuO+++7B48WJ84QtfwOc//3ncfffdZsx9992HSy65BLfddhvOOOMM3Hbbbbjoootw3333mTF33nknFixYgO9+97s499xzceqpp+Kiiy7Caaed1vwkCIIgiKbppI1xXRdnn302tm3bFnl+27ZtWL58ecVjli1bVjZ+69atOOecc+A4Ts0x4XN+/OMfx65duyJj/u3f/g2nnHJK4xMgCIIgWoYcbNMPOdgIgiDq0MkC1AAVoSYIgjiR6XT6zpe//GX8xV/8Bf7yL/8Sb775Jm666Sa88847uO666wAAt912G37v937PjL/uuuvw9ttv48tf/jLefPNN/OVf/iUeeeQR3HLLLWbM7//+72Pr1q2488478Ytf/AJ33nknnnvuOaxfv96Muemmm7Bjxw5s3LgRv/zlL/HUU0/hoYcewn/7b/+tvQ+MIAiCaIjpShE9GmVuTj31VDDGyh5hG3P11VeXvX7eeec1Pb9mIAcbQRBEHSw0GflRxzVSgBqgItQEQRAnMq3amEa58sorcd999+Eb3/gGPvrRj+L//J//g2effdYoyfbu3RtJ21y4cCGeffZZ/MM//AM++tGP4pvf/Ca+/e1v49Of/rQZs3z5cjz99NP47ne/i9/8zd/Eo48+imeeeQZLly41Y/7Df/gP2Lx5M77//e9jyZIl+OY3v4n77rsPv/u7v9vOx0UQBEE0SNP2pQUbc7TK3LzyyivYu3eveWhV9Wc/+9nI+61YsSIy7tlnn21yhs1hd/TsBEEQxwG8yWgODxWg7u3tNc9Xqo8T5mgVoT7nnHOwceNGAMBZZ52FN954Aw888EBE0UAQBEF0hlZtTDNcf/31uP766yu+9uijj5Y9d/755+Of/umfap7zM5/5DD7zmc/UHLNy5UqsXLmy4eskCIIgpo5m7Ys+phnCZW4AWaLmb//2b/HAAw9UFBeEy9wAsjbnz372M9x9990mkBMucwNIpfXzzz+P++67D9///vcBAHPnzo2c93/8j/+B0047Deeff37k+VgshsHBwabm1A6kYCMIgugQjRSgBqgINUEQBEEQBEEQM4dGSt0czTI3pdfx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zz24+OKL8axnPQv33XffyOPvvfdePPvZz8bFF1+Me+65B69+9avxile8Ah/5yEfCMXfeeSeuuOIKXHnllfinf/onXHnllfiFX/gFfP7znx96vrvuugvveMc78MQnPnG6N4YgCILYEFPbF/JjNgy9ewRBEGvgJ/BMcyMIgiCISdhqG/PmN78Zv/qrv4qrrroKj3vc43Dw4EGcc845ePvb3z7y+JtvvhmPeMQjcPDgQTzucY/DVVddhZe85CX4vd/7vXDMwYMHcemll+L666/HYx/7WFx//fV45jOfiYMHD9aea3l5GS94wQvwR3/0R9i1a9fU7w1BEASxftZjX8iP2RgUYCMIglgD3yB0mtt6oBIegiCI04/12pjFxcXard/vDz13lmW4++67cdlll9Xuv+yyy/DZz3525PnceeedQ8f/1E/9FP7hH/4BeZ6veszgc7785S/Hz/zMz+Anf/Inp3tTCIIgiA2zHvuyXj+GsNC7RxAEsQaM86lv00IlPARBEKcn67Ux55xzDubm5sLtjW9849BzP/TQQ1BKYd++fbX79+3bh0OHDo08n0OHDo08vigKPPTQQ6seU33OD33oQ/jCF74w8rwIgiCIrWc99mU9fgxRQu8eQRDEGkzdu2AdmR8q4SEIgjg9Wa+Nuf/++3H8+PFwu/7668e+BmP1kh9jzNB9ax0/eP9qz3n//ffjla98Jd7//vcjTdMJ3gWCIAhis1mPfVmPH3MyqnBuvPFGMMZqt/3799eOMcbgxhtvxJlnnolGo4FLLrkEX/7yl6de3zRQgI0gCGItppVVT1G+A1AJD0EQxGnNOm3M7Oxs7ZYkydBT79mzB0KIIbXa4cOHhxRonv379488XkqJhYWFVY/xz3n33Xfj8OHDuPDCCyGlhJQSd9xxB97ylrdASgml1PreK4IgCGJy1lMeOmWA7WRW4Tz+8Y/HAw88EG5f+tKXar9/05vehDe/+c1461vfirvuugv79+/HpZdeiqWlpanWOA0UYDvFec973hMiurfffvvQ740x+P7v/34wxnDJJZds6mszxnDjjTdu2vP95V/+JV74whfiCU94AqIoWjXzShDbgUnKdwAq4SF2LqeKjVlcXMTv/M7v4JJLLsH+/fvRbrfxhCc8ATfddBN6vd6mvAZBnAziOMaFF16I2267rXb/bbfdhqc+9akjH/OUpzxl6PiPf/zjuOiiixBF0arH+Od85jOfiS996Uv4x3/8x3C76KKL8IIXvAD/+I//CCHEZi2ROEU5VewLANxwww34oR/6IezevTsodv7zf/7P+Na3vrVpr0EQJ4uTWYUjpcT+/fvD7Ywzzgi/M8bg4MGDuOGGG/C85z0PF1xwAd773vei0+ngAx/4wJa8FwAF2E4bZmZm8M53vnPo/jvuuANf//rXMTMzcxLOajo++tGP4nOf+xzOP/98/OAP/uDJPh3iNILxKbM/fPryHYBKeIidy063Mffddx8OHjyIH/7hH8Y73vEO/Pmf/zkuv/xy3HjjjfjZn/3Z8L9FEFvBem3MpFx33XX44z/+Y7zrXe/CV77yFVx77bW477778LKXvQwAcP311+OFL3xhOP5lL3sZvvWtb+G6667DV77yFbzrXe/CO9/5TrzqVa8Kx7zyla/Exz/+cdx000346le/iptuugmf+MQnwpCcmZkZXHDBBbVbq9XCwsICLrjggo2/acRpw063LwBw7Ngx/OIv/iLe+9734q//+q/xqle9Cn/5l3+JJz/5yThy5MjJPj3iFGZq+1KxMTthkM7XvvY1nHnmmTjvvPPwn/7Tf8I3vvGN8Lt7770Xhw4dqj1PkiR4+tOfPvbcNgO5Zc9MbCuuuOIK/Omf/ine9ra3YXZ2Ntz/zne+E095ylOwuLh4Es9uMv7oj/4I3P3D/9qv/Rruvvvuk3xGxOnCtA0//bG+bGcttkMJj0cphU9/+tN461vfin6/TyoDYiJ2uo0577zz8M1vfhOtVivc9xM/8RNotVr4jd/4Dfz93/89fvzHf/wkniFxKrNeGzMpV1xxBY4cOYLXv/71eOCBB3DBBRfg1ltvxbnnngsAeOCBB2qlPOeddx5uvfVWXHvttXjb296GM888E295y1vw/Oc/Pxzz1Kc+FR/60Ifwmte8Bq997WvxqEc9Crfccgue/OQnT3VuBLEWO92+AMDb3va22s+XXHIJzjvvPDz72c/Gxz72MbzkJS85SWdGnOqsZ2hBdZBOlde97nVDys6tqMI5cODARFU4T37yk/G+970Pj3nMY/Dggw/it3/7t/HUpz4VX/7yl7GwsBCOHfU8W6keJQXbacIv/uIvAgA++MEPhvuOHz+Oj3zkI2M39YcffhhXX301zjrrLMRxjEc+8pG44YYbhqLXi4uLeOlLX4qFhQW022389E//NP7t3/5t5HN+7Wtfwy/90i9h7969SJIEj3vc44aMzjj4CZpocs0116DVao002FdccQX27dsXouvE6YHN6IgpbtP9rVIJD7HT2ek2ptVq1YJrnv/wH/4DAKv23CzIxhCDbLWNAYCrr74a3/zmN9Hv93H33XfjaU97Wvjde97znqESvKc//en4whe+gH6/j3vvvTeo3apcfvnl+OpXv4osy/CVr3wFz3ve81Y9h9tvv32ovIcg1mKn25dx+FI2KTdP70L2hRhkevtS2pjtPEgHAJ71rGfh+c9/Pp7whCfgJ3/yJ/FXf/VXAID3vve9Gzq3jUIBttOE2dlZXH755XjXu94V7vvgBz8IzjmuuOKKoeN7vR6e8Yxn4H3vex+uu+46/NVf/RV++Zd/GW9605tqF1DGGPzcz/0c/uRP/gT/9b/+V3z0ox/Fj/7oj+JZz3rW0HP+y7/8C37kR34E//zP/4zf//3fx1/+5V/iZ37mZ/CKV7wC//2///etWfg6eMlLXoJOp4MPf/jDtfuPHTuGj33sY/jlX/7lEMAgTg+mllavw/mhEh5iJ3Oq2phPfvKTAGwT3c2CbAwxyImwMQSxUzmV7EtRFOh2u7jnnntwzTXX4DGPecyagelpIPtCDLIe++JtzHYepDOKVquFJzzhCfja174WngPA1M+zUahE9DTiJS95CZ7xjGfgy1/+Mh7/+MfjXe96F37+539+ZO+C9773vfjiF7+ID3/4w/j5n/95AMCll16KdruN//f//X9x22234dJLL8Xf/M3f4FOf+hT+4A/+AK94xSvCcXEc44Ybbqg953XXXYeZmRl85jOfCRLvSy+9FP1+H7/7u7+LV7ziFdi1a9cWvwtr88QnPhE//MM/jHe/+9246qqrwv0f/OAH0e/38eIXv/gknh1xMuCcT6WgXI/akkp4iJ3OqWZjvvjFL+JNb3oTnvvc5+KJT3ziet+WIcjGEIOcCBtDEDuZU8G+HDp0CAcOHAg/P/nJT8anPvUptNvtDb03Vci+EINMa1/8YyalWoXz3Oc+N9x/22234TnPec7IxzzlKU/BX/zFX9TuG1eFc+2119aOGVfZAwD9fh9f+cpXcPHFFwOwvtL+/ftx22234Yd+6IcA2J5xd9xxB2666aaJ1zgtZKFPI57+9KfjUY96FN71rnfhS1/6Eu66666x0upPfvKTaLVauPzyy2v3v+hFLwIA/O3f/i0A4FOf+hQA4AUveEHtuF/6pV+q/dzr9fC3f/u3eO5zn4tms4miKMLt2c9+Nnq9Hj73uc9txjJHopSqvabWetXjX/ziF+Ozn/0s/vVf/zXc9+53vxs/8iM/Qsqe05ATpS6gEh5iJ3Mq2ZhvfvOb+Nmf/Vmcc845+OM//uM1jycbQ2wEUrARxOqcCvZlz549uOuuu/CZz3wGf/RHf4SHH34Yz3jGM/DAAw+s+jiyL8RG2IiCbVJORhUOALzqVa/CHXfcgXvvvRef//zncfnll2NxcRG/8iu/YtfOGK655hq84Q1vwEc/+lH88z//M170oheh2WwO/Z9vJmShTyMYY3jxi1+M97///bj55pvxmMc8JkR4Bzly5Aj2798/VJ+8d+9eSCnDxJsjR47U5JweL8msPl9RFPhf/+t/IYqi2u3Zz342ANskcat45jOfWXvNtZqJvuAFL0CSJHjPe94DwErD77rrLsr8nKaQ80MQa3Oq2JhvfetbeMYzngEpJf72b/8Wu3fvXvMxZGOIjUA2hiBW51SwL1JKXHTRRfixH/sxXHXVVfjkJz+Jb3zjG/jd3/3dVR9H9oXYCCciwHbFFVfg4MGDeP3rX48nPelJ+PSnPz1RFc7tt9+OJz3pSfit3/qtsVU47373u/HEJz4R73nPe4aqcL797W/jF3/xF/EDP/ADeN7znoc4jvG5z30uvC4A/OZv/iauueYaXH311bjooovwne98Bx//+Me3dPowlYieZrzoRS/Cf/tv/w0333wzfud3fmfscQsLC/j85z8/1ATw8OHDKIoCe/bsCccVRYEjR47UDNRgrfOuXbsghMCVV16Jl7/85SNf87zzztvI0lblf//v/42lpaXwsz//cezatQvPec5z8L73vQ+//du/jXe/+91I0zQ0WiVOLxibcsIbI+eHOD3Z6TbmW9/6Fi655BIYY3D77bfj7LPPXvMxANkYYmOQjSGItdnp9mWQs88+G2eeeebYoQoesi/ERpjWvvjHTMvVV1+Nq6++euTvfLC3iq/CWY3LL798SIla5UMf+tCa58UYw4033jg0/XQroQDbacZZZ52F3/iN38BXv/rVIJ8cxTOf+Ux8+MMfxp/92Z/V6qnf9773hd8DwDOe8Qy86U1vwp/+6Z+G/gUA8IEPfKD2fM1mE894xjNwzz334IlPfCLiON7MZa3JD/zAD0z9mBe/+MX48Ic/jFtvvRXvf//78dznPhfz8/Obf3IEQRCnCDvZxtx333245JJLoJTC7bffXsuArgXZGIIgiK1lJ9uXUfz7v/87vv3tb+M//sf/uOpxZF8IYmdBAbbTkLWkyADwwhe+EG9729vwK7/yK/jmN7+JJzzhCfjMZz6DN7zhDXj2s5+Nn/zJnwQAXHbZZXja056G3/zN38TKygouuugi/P3f/z3+5E/+ZOg5/+AP/gA//uM/josvvhj/5b/8F3zf930flpaW8O///u/4i7/4izCtbRzf+ta3cNdddwEAvv71rwMA/s//+T8AgO/7vu/DRRddNNX7sBaXXXYZzj77bFx99dU4dOgQSatPY6aVS1P5DnE6sxNtzOHDh0MvnHe+8504fPgwDh8+HH5/9tlnT6xmmxSyMYSHbAxBTMZOtC9f/OIXce211+Lyyy/HIx/5SHDO8aUvfQn/83/+TywsLNT6Tm0WZF8Iz3pKPsnGbAwKsBEjSdMUn/rUp3DDDTfgf/yP/4Hvfe97OOuss/CqV70Kr3vd68JxnHP8+Z//Oa677jq86U1vQpZl+LEf+zHceuuteOxjH1t7zvPPPx9f+MIX8Fu/9Vt4zWteg8OHD2N+fh6PfvSjQw+D1fjUpz41ZCD8dKBf+ZVfGSk/3Qicc7zwhS/EG97wBpxzzjkh40WcfpDzQxCby3azMf/yL/+Cb3zjGwCAX/7lXx76/ete97pNLy8gG0N4yMYQxOax3ezLvn37cOaZZ+L3f//38cADD6AoCpx99tn42Z/9Wbz61a/GOeecs+nvAdkXwkMBthMPM8aYk30SBEEQ25HFxUXMzc3ha//9P2MmnbwkYKmX4dGveweOHz8exrkTBEEQRBWyMQRBEMRWsF77ApCN2SikYCMIglgDxtl0Dag5W/sggiAIggDZGIIgCGJrmNa++McQ64cCbARBEGtA5TsEQRDEVkE2hiAIgtgKqET0xEMBNoIgiDUg54cgCILYKsjGEARBEFsBBdhOPBRgIwiCWAPG+HTlO4wME0EQBDEZZGMIgiCIrWBa++IfQ6wfCrARBEGsARMCXIipjicIgiCISSAbQxAEQWwF09oX/xhi/VCAjSAIYg2ofIcgCILYKsjGEARBEFsBlYieeCYKsGmt8d3vfhczMzNgjKZKEASxszDGYGlpCWeeeSb4lDJpYushG0MQxE6GbMz2hewLQRA7HbIxO4uJAmzf/e53cc4552z1uRAEQWwp999/P84+++ypH0fqgq2FbAxBEKcCZGO2H2RfCII4VViPjSEF24lnogDbzMwMAOBrX/ta+J4g1svJzB+ak/jak7BZ7812W+dJ+8yNBgAsLa/g+x/96HXvX4xP2YCasktT4T+Xfx9jY7bb3zNxenKi9rHt+Pe+023Tln52RmNxeQWPJhuzLanalzb5MARBbJCT4dMsLi2t28ZMa1/8Y4j1M1GAzUuqZ2ZmMDs7u6UnRJyabAdR/nZ0Wqps5nu0ndZ6Uj97F2CDm4az3vIQUhdsLavZmO30t0yc3pyIvWy7/r1vdO0ne11b9tk5G2PIxmxbBu3Lyf5bJAhiZ3My/Bq/b63HxpCC7cRDQw6ILWU7BNZ2AhRc294wzqZzfvipsnKCIAAKrm2Ek72urQ6ubcZrkI0hCIIgtoJp7Yt/DLF+KMBGbBn0rzkZ9D5tf6h8hyAIgghUgmubAdmYEwfDyQ/6EgRBnCioRPTEQwE2giBOXRjfFEeIcQHGxVTHEwRBTAo5/DuATQ6qVSEbc+KhQBtBEKcD09oX/xhi/VCAjSBOIluhXqOLxgHYJmRhuLC3aY4nNgX6eyYI4qSxhUG1GmRjTjgGZF8Igpgev3fsGKa1L/4xxLqhABtBEFvCjjI+a8G5vU1zPEEQxA7nlNrHp2HawNpGA3FkY04aFGQjCOKUZlr74h9DrBsKsBEEQawBEwJMTFG+M8WxBEEQ5ORvE06UYm0AsjEEQRDEVjCtffGPIdYPhSeJ04bTNhNPEDsc+t8lCGLLOUnBNeLkQUFtgiAIYrMhBRtBEKcmm+ksUX8cgiCIU5MTXQ46CrIxJxVSkBIEccpCPdhOOBRgI7aM7dgEki6ixqPXeGP4dvswq2y18oDzKZ0fEgcTBLGz2awtfz12dy17BJxgm7SWjdmUHmxkY04G/lqVrg8Jgjglmda++McQ64YCbARxmjOJI1M9bsudmq0Ilm3wORnnYFMYm2mOJQhi+3MiEkYbcfB3dIKkwqT2qHrshta21YGzCSEbQxAEQWwF09oX/xhi/VCAjdhStqOKbbuwle/LJI7aNI7M4OM23Vnb7r1v2JTyakbS6s2G1AUEMcy0CRJg+wbbTrhNWs3unGibRDbmpEIqNoIgTlmmtS/+McS6oQAbcdqxGRdQoxyB7eq0jGK9jkz18Zuy3u0eWPNQfxyCILYZ2ypJskG2jU0CprNL/tgNl4iSjTmRUCCNIIiNsKMEJNSD7YRD+j9iyzmVLmK0Ge8I+N9t1FHYajbr/Db0PEbvnOAaSnn1NDeCIIhpmfSCfTMCUptxHpOy2vOdFJs0zv5MUjJavW0SJ8LG/OEf/iHOO+88pGmKCy+8EH/3d3+36vF33HEHLrzwQqRpikc+8pG4+eabh475yEc+gvPPPx9JkuD888/HRz/60drv3/jGN+JHfuRHMDMzg7179+Lnfu7n8K//+q9Tn/uJZMc4zQRBnDZsZF9aj30hP2Zj0LtHnJZMu1FNGzjblEDb4IX8qNtJZl1r3AbnPTU++zPNbR2QA0QQxFpsiyTJCcZgsmTdqDVNvMzVgm5bbXO32MbccsstuOaaa3DDDTfgnnvuwcUXX4xnPetZuO+++0Yef++99+LZz342Lr74Ytxzzz149atfjVe84hX4yEc+Eo658847ccUVV+DKK6/EP/3TP+HKK6/EL/zCL+Dzn/98OOaOO+7Ay1/+cnzuc5/DbbfdhqIocNlll2FlZWV979MWsoP+HQiCICZnPfaFFGwbggJsxAlhJ1+4bMQJWfdjJ72Qn/KCf7XzMWvcpmFsAHMnBteAcgLPxLfpt1ZygAhie7Md7NhmB8XWnSTZpKTPuNcfZ382YpdWZb2Kts1ii23Mm9/8Zvzqr/4qrrrqKjzucY/DwYMHcc455+Dtb3/7yONvvvlmPOIRj8DBgwfxuMc9DldddRVe8pKX4Pd+7/fCMQcPHsSll16K66+/Ho997GNx/fXX45nPfCYOHjwYjvnrv/5rvOhFL8LjH/94/OAP/iDe/e5347777sPdd9+9rrfpREEqNoIgThmmti/r82O2q0jgRS96ERhjtduP/uiPTr2+aaAAG3HC2A7OybRshjMz9XNMe0G/QQdgUkdl3HETr2+nBtdOEOQArQ05PcTpwHr/zjczSTL+RSYsodzIS2zw2DVt0uD5jTrfNdbBjB66nQwWFxdrt36/P3RMlmW4++67cdlll9Xuv+yyy/DZz3525PPeeeedQ8f/1E/9FP7hH/4BeZ6vesy45wSA48ePAwB279699uJOAtU/HbI3BEEQk7HdRQI//dM/jQceeCDcbr311q15IxwUYCNOW07kxdOWB6FGPG5wfRsqndngY3Z6cI0JMfVtGsgBIghiLdZSe63FCU2STHDs4OuuNxB4otRsWxlMW6+NOeecczA3Nxdub3zjG4ee+6GHHoJSCvv27avdv2/fPhw6dGjk+Rw6dGjk8UVR4KGHHlr1mHHPaYzBddddhx//8R/HBRdcMNkbQxCnMGzgRhBbwXrsy7R+zHYXCSRJgv3794fbVvs4FGAjTig7ScW2ltOx6QqB1S7aJynDmfKif5yjNXib5LEbKoXd5n3mADh59ZQ3TKYuAMgBIoidwrr3+S3a17Z1kmTgMas5kJPao9Vskxl47HrOcWSy6kQo1NZpY+6//34cP3483K6//vqxL8FY/RMwxgzdt9bxg/dP85y/9mu/hi9+8Yv44Ac/OPY1CeJ0YFxA7XQMtFGA8QSwHvsyRYnoThAJ3H777di7dy8e85jH4KUvfSkOHz482eLWidzSZyeIEWyn0cbrGdW+2vGrlRdoA/BpFr5WwC28EK/fz9beFKcJkFV/Vz3/iT/HceU3k+KPnWBdW8a0DT95qS6o8rrXvQ433njj2IdtFwfoM5/5zNjXJAhiCiZJiIzY2yaxTeN+P7ifj7I7U9nhafbwwbVMaJOqTBoc88dNZVerrBFcWzOopiu/32g/iXXamNnZWczOzq566J49eyCEGEqsHD58eCgB49m/f//I46WUWFhYWPWYUc/567/+6/jzP/9zfPrTn8bZZ5+9+tpOMtX/jfVcIxLERjkZf3fjttGtPI/B1/Q/0//cJrOeoQXu+MXFxdrdSZIgSZLafVshEjhw4MCmiQSe9axn4ed//udx7rnn4t5778VrX/ta/MRP/ATuvvvuobVsFhRgI04K2ynINg3T9oZZ9xqnDUBN4cCsK8tfOX7dzgywMeXGOhy1zYJxATaFcfLH3n///TXnZ9xGTg4QQZyCTDOsZsq9bZpSz0mSJGP39kn6lY36/SbaJJ848AwmCKrnvuaaJgh4rhpY01ujZFuvjZmEOI5x4YUX4rbbbsNzn/vccP9tt92G5zznOSMf85SnPAV/8Rd/Ubvv4x//OC666CJEURSOue2223DttdfWjnnqU58afjbG4Nd//dfx0Y9+FLfffjvOO++8ic+bIE5FJr2E3i7Bpq0K9q32Ppz2ge1NVkxPa1/8Y4DphALbVSRwxRVXhO8vuOACXHTRRTj33HPxV3/1V3je85439vw2AgXYiJPGdgmyTbqRr7cUp7rGcY5AbTOdVC0wTrk2wlkb5bQM9b8xq6/Qb2hj1zCKja5ru8Cmk0v7NUyiLgDIASKIncREtms9w2rWufdNkygZDDptWMm22rGr2KRx5xYevoo9qv5uXXbJn9OI70cG17YoqFZjnTZmUq677jpceeWVuOiii/CUpzwF73jHO3DffffhZS97GQDg+uuvx3e+8x28733vAwC87GUvw1vf+lZcd911eOlLX4o777wT73znO2vlna985SvxtKc9DTfddBOe85zn4GMf+xg+8YlP1Jybl7/85fjABz6Aj33sY5iZmQkJn7m5OTQajanWcCIhFRuxXTgRf38n2heb5PVOy/+7rWpFMK198Y/BZEKBnSYSOHDgAM4991x87WtfW/W4jbANPVnidGK7bp6jmi+POmaS3jBTrXGUWmC1jPtaAazBh7ivg47MWsG1weMmCdgNnevgz2uta6OlpZuIz/5Mc5uW6667Dn/8x3+Md73rXfjKV76Ca6+9dsgBeuELXxiOf9nLXoZvfetbuO666/CVr3wF73rXu/DOd74Tr3rVq8Ixr3zlK/Hxj38cN910E7761a/ipptuwic+8Qlcc8014ZiXv/zleP/7348PfOADwQE6dOgQut3u+t8wgjid2aRhNZOU7IxKlIy7VR+z2n498nUHbc0kfTNHfF9Vl1XXslpwTY+4Da538DkmZrXgmtarB9c2sa/eVtuYK664AgcPHsTrX/96POlJT8KnP/1p3HrrrTj33HMBAA888EBt2tt5552HW2+9Fbfffjue9KQn4bd+67fwlre8Bc9//vPDMU996lPxoQ99CO9+97vxxCc+Ee95z3twyy234MlPfnI45u1vfzuOHz+OSy65BAcOHAi3W265ZUPvF0GcTpxsMcLJfv3Tgi30b9ZjX7yN8UIBfxsVYKuKBKrcdttttYR+FS8AqDJOJDB4zKBI4Nd+7dfwf//v/8UnP/nJiUQCR44cwf33348DBw6seex6IQUbcdLZdCXbapvUmKzvNJmSSS7iB3vDTFSKU2WCXjCmqg4A7Nq8UsB9XWtdNadrjVPy75yX5/p1jP38/HmtEQT0azOjPptpVR1+7ZsN51P2x5k+d3HFFVfgyJEjeP3rX48HHngAF1xwwUQO0LXX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REQxuMMbERHlhTGGiIjywF1EG08f6gsgIiIiIiIiIiIazljBRkQUoypEqi2rub01ERElxRhDRER5SBtf5DmUHRNsREQxuD4OERHlhTGGiIjywDXYGo9TRImIYsjglOZBRESUBGMMERHlIUt8yTPGHD9+HN3d3ahUKqhUKuju7sbnn38eeY4QAj09PZg6dSra2tpw9dVX47333nMdc+rUKdxxxx1ob2/H2LFjsWjRInz88ceuY6ZPnw5N01yPv/mbv3Ed84tf/AJ/8id/grFjx6K9vR133nknBgcHU90jE2xERDGKFJiIiGhkYYwhIqI8FC3BtmTJEhw8eBC7d+/G7t27cfDgQXR3d0ees2nTJjz88MPYsmULDhw4gI6ODlx33XU4ceKEfczKlSvx/PPP45lnnsErr7yCL774AgsXLkS1WnW91n333YejR4/aj3vvvdf+XrVaxR//8R/j17/+NV555RU888wzeO6557Bq1apU98gEGxFRjKowUDVSPISR27UM5ciPeuxll10GTdNw8ODBOt0ZEdHoVKQYQ0REI0fq+JJjjDl8+DB2796Nxx9/HF1dXejq6sK2bdvw4osvore3N/AcIQQeffRRrFmzBjfeeCNmzpyJnTt34je/+Q2efvppAEB/fz+2b9+Ohx56CPPmzcOsWbOwa9cuvPvuu9i3b5/r9caNG4eOjg77cdZZZ9nf27NnD376059i165dmDVrFubNm4eHHnoI27Ztw8DAQOL7ZIKNiCiGkXLUJ8/trYd65AcAVq9ejalTp9b93oiIRqMixRgiIho50saXPGPMa6+9hkqlgjlz5tjPzZ07F5VKBa+++mrgOR988AH6+vpw/fXX28+1tLTgqquuss956623cPr0adcxU6dOxcyZM32vu3HjRkyaNAmXXXYZ/u7v/s41/fO1117DzJkzXX2c+fPn49SpU3jrrbcS3yc3OSAiilE1BPQCLEAtR372799vB6dt27ahq6sLvb29uPDCC33neEd+AGDnzp2YMmUKnn76aSxfvtwe+XnyyScxb948AMCuXbvQ2dmJffv2Yf78+fbr/ehHP8KePXvw3HPP4Uc/+lEu90lENJoUJcYQEdHIkja+yHMA+Kq2Wlpa0NLSkvla+vr6MHnyZN/zkydPRl9fX+g5ADBlyhTX81OmTMFHH31kH9Pc3IwJEyb4jlFf9xvf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DhERjT6MMURElIes8QVgjKkVK9iIiGKIlFtcc3FQIiJKijGGiIjykDa+2OdQZkywERHFEIYBGEa644mIiBJgjCEiojykjS/2OZQZE2xERDFYXUBERHlhjCEiojywgq3xmGAjIorBzg8REeWFMYaIiPLABFvjMcFGRBTDMKrQ2PkhIqIcMMYQEVEe0sYXgDGmVkywERHFEFUD0FJ0fqpcu4CIiJJhjCEiojykjS/2OZSZPtQXQERERERERERENJyxgo2IKIYQKdfHESytJiKiZBhjiIgoD2nji30OZcYEGxFRDGFU003f4doFRESUEGMMERHlIW18sc+hzJhgIyKKwc4PERHlhTGGiIjywARb4zHBRkQUQ5z+bbpgUz2d38UQEdGIwhhDRER5SB1fAMaYGjHBRkQUorm5GR0dHej76fdTn9vR0YHm5uYcroqIiEYCxhgiIspDLfEFYIyphSaEEEN9EURERfXb3/4Wg4ODqc9rbm5Ga2trDldEREQjBWMMERHlIWt8ARhjasEEGxERERERERERUQ30ob4AIiIiIiIiIiKi4YwJNiIiIiIiIiIiohowwUZERERERERERFQDJtiIiIiIiIiIiIhqwAQbERERERERERFRDZhgIyIiIiIiIiIiqgETbERERERERERERDX4/y2673TevYOpAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABNgAAAKbCAYAAAAnqjeEAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOydebwcVZn+n3Oqerl7kpudBBIQISEgmCAmTCCIhEVmAEXyEwiLgBNBgUQcBGQIi2zqGNmCaAAZFOMMRhgFIQhEkYBsggIqawIhIWS9ay9VdX5/VFfd6u7q7qre7vZ8P59KuqtOnTpVt7vePk+9i1BKKRBCCCGEEEIIIYQQQspC9vcACCGEEEIIIYQQQggZzFBgI4QQQgghhBBCCCGkAiiwEUIIIYQQQgghhBBSARTYCCGEEEIIIYQQQgipAApshBBCCCGEEEIIIYRUAAU2QgghhBBCCCGEEEIqgAIbIYQQQgghhBBCCCEVQIGNEEIIIYQQQgghhJAKoMBGCCGEEEIIIYQQQkgFUGAjhBAyaLj77rshhHAXXdcxadIknHnmmdiwYUNe+9dffx1nnHEGdt11V0SjUYwePRrHHHMMHn744by2Tz75ZFbf0WgUY8aMwcEHH4zLLrsM69atq8cpls1DDz2EpUuX+m6bMmUKzjjjjLL6rWTfenLGGWdgypQpdT9uOp3G3nvvjeuvv95d9/TTT2Pp0qXYsWNH2f0uXboUQoisdfPmzcO8efMC7X/ZZZfhgAMOwKhRoxCPx7H77rvjK1/5iu/nOJ1O48orr8SUKVMQi8Ww99574+abb85rt3DhQhx//PHlnE7NuO2223D33XfXrP96fK7OOOMMNDc31/QYDj//+c+xbNmyuhyrEP31XSWEEEJqDQU2Qgghg4677roLa9euxerVq3HOOefgvvvuw9y5c9Hd3e22+dWvfoUDDjgAf/7zn3H55Zfjsccew/LlywEAxxxzDP7jP/7Dt+9rr70Wa9euxRNPPIEVK1Zg3rx5uPPOOzFt2jT87Gc/q8v5lcNDDz2EK6+80nfbqlWrcPnll9d5RMOD2267Ddu3b8fXv/51d93TTz+NK6+8siKB7eyzz8batWvL3n/Hjh340pe+hJ/+9Kf43e9+h4suugi/+c1vcNBBB2Hr1q1Zbc8991xcd911OO+88/DII4/ghBNOwAUXXIBrr702q93SpUvx29/+Fo8//njZ46o2tRbYhhoDQWAjhBBChip6fw+AEEIICcuMGTMwa9YsAMBhhx0G0zRx9dVX49e//jVOOeUUvPXWW1i4cCH23XdfPPnkk2hqanL3/eIXv4ivfvWr+O53v4tPfvKT+H//7/9l9b3nnnvi05/+tPv+3/7t3/CNb3wDn/3sZ3HGGWdgv/32w7777lufE60SBxxwQH8PYUhiGAa++93v4stf/nLWZ6waTJo0CZMmTSp7/1tvvTXr/bx58zB16lQcc8wxeOCBB/DlL38ZAPDqq69ixYoV+M53voNvfvObbtutW7fimmuuwaJFizBq1CgAwB577IGjjjoK119/PT7zmc+UPTZCCCGEkKEIPdgIIYQMehxBzAl/+8EPfoCenh7cfPPNvsLH97//fYwYMQLf+c53AvU/atQo/OhHP4JhGPjBD35QtK1lWbjmmmuw1157oaGhASNGjMB+++2HH/7wh24bJ/zvpZdewuc//3m0traira0Np556Kj766KOs/lauXIn58+djwoQJaGhowLRp0/Ctb30ry1vvjDPOcAUVb5jru+++CyA/zDORSOAb3/gG9t9/f7S1tWHUqFGYPXs2HnjggUDXww8hBL72ta/hrrvucs991qxZeOaZZ6CUwne/+11MnToVzc3N+MxnPoM333wzr48777wTn/jEJxCPxzFq1CiccMIJeP311/Pa3X333dhrr70Qi8Uwbdo03HPPPb5jSqVSuOaaa7D33nsjFothzJgxOPPMM/Ou8eOPP4558+ahvb0dDQ0N2HXXXfGFL3wBPT09Rc/5wQcfxIYNG7Bw4UJ33dKlS12haurUqe7f4sknnwQQ7O/p9JMbIlopY8aMAQDoet/z1V//+tdQSuHMM8/ManvmmWeit7cXv/vd77LWL1y4EI899hjeeuutssYQ5rNnWRZuvvlm7L///u536dOf/jQefPBBAPbn+tVXX8WaNWvc6xw29PDnP/85Zs+ejebmZjQ3N2P//ffHihUrSp7DJZdcgqlTpyIajWKXXXbBeeedl+exGPRv7cef/vQnjB49Gscee2yg9gDw0Ucf4Stf+QomT57sft4PPvhgPPbYYwBs4fS3v/0t1q1bl3WfcAj6fQlzXqW+q0op7LnnnjjyyCPz9u3q6kJbWxvOO++8QOdPCCGE9Df0YCOEEDLoccQaR0BYvXo1xo0bl+WJ5qWxsRHz58/HL3/5S2zatAnjx48veYwDDzwQEyZMwB/+8Iei7W688UYsXboU3/72t3HIIYcgnU7j73//u2+44AknnICTTjoJixYtwquvvorLL78cr732Gp599llEIhEAwBtvvIFjjjkGF154IZqamvD3v/8dN9xwA/785z+7oXqXX345uru78b//+79ZYYUTJkzwHWMymcS2bdtw0UUXYZdddkEqlcJjjz2Gz3/+87jrrrtw2mmnlbwefvzmN7/BSy+9hOuvvx5CCFx88cX43Oc+h9NPPx1vv/02brnlFuzcuRNLlizBF77wBfzlL39xJ/jXXXcdLr30UnzpS1/Cddddh61bt2Lp0qWYPXs2nnvuOey5554A7An7mWeeieOOOw7f//73sXPnTixduhTJZBJS9j03tCwLxx13HP74xz/iP/7jPzBnzhysW7cOV1xxBebNm4fnn38eDQ0NePfdd/G5z30Oc+fOxZ133okRI0Zgw4YN+N3vfodUKoXGxsaC5/vb3/4WY8eOxfTp0911Z599NrZt24abb74Zv/rVr9y/gdMmyN+zmhiG4X4GL7zwQnz84x/H5z//eXf73/72N4wZMybvO7Dffvu5273MmzcPSik89NBDWWGx8+bNw5o1a6CUKjqeMJ+9M844A/feey/OOussXHXVVYhGo3jxxRdd4XjVqlU48cQT0dbWhttuuw0AEIvFAl+b//zP/8TVV1+Nz3/+8/jGN76BtrY2/O1vfyuab1EpheOPPx6///3vcckll2Du3Ll45ZVXcMUVV2Dt2rVYu3atO4Zy/9a//OUvcdppp+HLX/4ybr75ZmiaFuh8Fi5ciBdffBHf+c538PGPfxw7duzAiy++6IYE33bbbfjKV76Ct956C6tWrcraN+j3Jcx5BfmuCiHw9a9/HRdeeCHeeOMN93sOAPfccw86OjoosBFCCBk8KEIIIWSQcNdddykA6plnnlHpdFp1dnaq3/zmN2rMmDGqpaVFbdq0SSmlVDweV5/+9KeL9nXxxRcrAOrZZ59VSin1xBNPKADqf/7nfwruc9BBB6mGhoai/R577LFq//33L9rmiiuuUADU4sWLs9b/7Gc/UwDUvffe67ufZVkqnU6rNWvWKADq5Zdfdredd955qpBZ32233dTpp59ecDyGYah0Oq3OOussdcABB4Ta1wGAGj9+vOrq6nLX/frXv1YA1P77768sy3LXL1u2TAFQr7zyilJKqe3bt6uGhgZ1zDHHZPW5fv16FYvF1Mknn6yUUso0TTVx4kT1yU9+Mqu/d999V0UiEbXbbru56+677z4FQN1///1ZfT733HMKgLrtttuUUkr97//+rwKg/vKXv5Q8x1ymTZumjjrqqLz13/3udxUA9c477xTdv9jf0/mMeDn00EPVoYceGnh8GzduVADc5aCDDlIbNmzIanPEEUeovfbay3f/aDSqvvKVr+St32WXXdSCBQuy1n3mM59RmqYFHptDoc/eH/7wBwVAXXbZZUX332effUJdE4e3335baZqmTjnllKLtTj/99KzP1e9+9zsFQN14441Z7VauXKkAqDvuuMO3n2J/69NPP101NTUppZS6/vrrlaZp6oYbbgh9Ts3NzerCCy8s2uZzn/tc1vk4BP2+5FLovMJ8Vzs6OlRLS4u64IILsvqePn26Ouyww4qeDyGEEDKQYIgoIYSQQcenP/1pRCIRtLS04Nhjj8X48ePx8MMPY9y4cYH7UBlPmzBheKqEdw4AfOpTn8LLL7+Mc889F4888gg6OjoKtj3llFOy3p900knQdR1PPPGEu+7tt9/GySefjPHjx0PTNEQiERx66KEA4Bs+GZT/+Z//wcEHH4zm5mbouo5IJIIVK1ZU1Odhhx2WFZI7bdo0AMDRRx+ddZ2d9Y6n0Nq1a9Hb25tXrXTy5Mn4zGc+g9///vcAgH/84x/44IMPcPLJJ2f1t9tuu2HOnDlZ+/7mN7/BiBEj8K//+q8wDMNd9t9/f4wfP94N2dx///0RjUbxla98BT/96U/x9ttvBz7fDz74AGPHjg3cHqju39M0zaxzsywra/vo0aPx3HPP4amnnsKPf/xjbNu2DYcddhg2btyY1a7Yd8Bv29ixY/Oq9v7+97+HYRiBxh3ks+dU+q2V99Lq1athmmbo/h0PrdzP6he/+EU0NTW5n1Ug3N9aKYV///d/xxVXXIGf//znBYuwFONTn/oU7r77blxzzTV45plnkE6nA+8b9PsS9LzCfFdbWlpw5pln4u6773bDTB9//HG89tpr+NrXvhb6OhBCCCH9BQU2Qgghg4577rkHzz33HF566SV88MEHeOWVV3DwwQe723fddVe88847RftwwswmT54c+Ljr16/HxIkTi7a55JJL8L3vfQ/PPPMMjj76aLS3t+Pwww/H888/n9c2NyxP13W0t7e7IV1dXV2YO3cunn32WVxzzTV48skn8dxzz+FXv/oVAKC3tzfw2L386le/wkknnYRddtkF9957L9auXYvnnnsOX/7yl5FIJMrqE4CbDN8hGo0WXe8cyzlfv5DWiRMnutud//1CenPXffjhh9ixYwei0SgikUjWsmnTJmzZsgWAnbj/sccew9ixY3Heeedhjz32wB577JGVM68Qvb29iMfjJds5VPvvefjhh2edl1O4wEHXdcyaNQsHH3wwzj77bDz++ON4++23cf3117ttvJ83L93d3UilUnl/OwCIx+M1/+x99NFH0DQtUPh2OTh5xcIWkti6dSt0XXfD0R2EEBg/fnzZ391UKoWVK1din332wdFHH13WOa1cuRKnn346fvKTn2D27NkYNWoUTjvtNGzatKnkvkG/L0HPK8x3FQC+/vWvo7Oz063UfMstt2DSpEk47rjjyroWhBBCSH/AHGyEEEIGHdOmTXOriPpxxBFH4NZbb8Uzzzzjm4etp6cHq1evxowZMwJP4P/85z9j06ZNOOuss4q203UdS5YswZIlS7Bjxw489thjuPTSS3HkkUfivffey8rptWnTJuyyyy7ue8MwsHXrVrS3twOwvTg++OADPPnkk66HCADffG5huPfeezF16lSsXLkyy7skmUxW1G+5OOeb61kF2F5io0ePzmrnJxjkrhs9ejTa29vzkvQ7tLS0uK/nzp2LuXPnwjRNPP/887j55ptx4YUXYty4cXlVZnOPsW3bthJn10e1/54/+tGP0NnZmTWeYkyaNAkTJ07EP//5T3fdvvvui1/84hd5uQj/+te/ArAr9uaybdu20MUEHIJ+9saMGQPTNLFp06aCuQQrwRHI3n///VAie3t7OwzDwEcffZQlsimlsGnTJhx44IEAwv+tY7EYnnjiCRx55JH47Gc/i9/97ncYOXJkqHMaPXo0li1bhmXLlmH9+vV48MEH8a1vfQubN28u+D3w7hvk+xL0vMJ8VwHgYx/7GI4++mjceuutOProo/Hggw/iyiuvDJx/jhBCCBkI0IONEELIkGPx4sVoaGjA17/+dd/KdhdddBG2b9+Ob3/724H627ZtGxYtWoRIJILFixcHHseIESNw4okn4rzzzsO2bdtcrzkHx1vD4Ze//CUMw8C8efMA9IXn5SZu/9GPfpR3LKdNEM8iIQSi0WiWwLFp06aKqohWwuzZs9HQ0IB77703a/3777+Pxx9/HIcffjgAYK+99sKECRNw3333ZYXrrlu3Dk8//XTWvsceeyy2bt0K0zQxa9asvGWvvfbKG4emaTjooIPciqwvvvhi0XHvvffevtU0C/0twvw9g7DXXntlnVMp0evNN9/E+++/j4997GPuuuOOOw5CCPz0pz/Nanv33XejoaEBRx11VNZ6wzDw3nvvZRV2CEPQz57jxbV8+fKi/cVisbK86ebPnw9N00r2n4vzWcz9rN5///3o7u52t5fztz7ggAOwZs0avP/++5g3bx42b94camxedt11V3zta1/DEUcckfU5LnS9gn5fgp5XmO+qwwUXXIBXXnkFp59+OjRNwznnnFPeyRNCCCH9BD3YCCGEDDn22GMP/Pd//zdOOeUUHHjggViyZAn22msvfPjhh7jzzjvx8MMP46KLLsKCBQvy9n3jjTfwzDPPwLIsbN26Fc8++yxWrFiBjo4O3HPPPdhnn32KHvtf//VfMWPGDMyaNQtjxozBunXrsGzZMuy2225ZFfIAO1xO13UcccQRbhXRT3ziEzjppJMAAHPmzMHIkSOxaNEiXHHFFYhEIvjZz36Gl19+Oe+4++67LwDghhtuwNFHHw1N07Dffvu54Zhejj32WPzqV7/CueeeixNPPBHvvfcerr76akyYMAFvvPFG4OtcLUaMGIHLL78cl156KU477TR86UtfwtatW3HllVciHo/jiiuuAABIKXH11Vfj7LPPxgknnIBzzjkHO3bswNKlS/M8Ef/f//t/+NnPfoZjjjkGF1xwAT71qU8hEong/fffxxNPPIHjjjsOJ5xwAm6//XY8/vjj+NznPoddd90ViUQCd955JwDgs5/9bNFxz5s3D1dddRV6enqyPBOdv8UPf/hDnH766YhEIthrr71C/T0r4ZVXXsHixYtx4oknYvfdd4eUEn/961/xgx/8AO3t7bjooovctvvssw/OOussXHHFFdA0DQceeCAeffRR3HHHHbjmmmvyQkRfeeUV9PT04LDDDstaf/jhh2PNmjUl87AF/ezNnTsXCxcuxDXXXIMPP/wQxx57LGKxGF566SU0Nja6FUwdD7yVK1di9913Rzwed69/MaZMmYJLL70UV199NXp7e/GlL30JbW1teO2117BlyxZceeWVvvsdccQROPLII3HxxRejo6MDBx98sFtF9IADDsDChQsBhPvuepk2bRr++Mc/4rOf/SwOOeQQPPbYY4HCWHfu3InDDjsMJ598Mvbee2+0tLTgueeew+9+97usqrH77rsvfvWrX2H58uWYOXMmpJSYNWtW4O9L0PMK8131Xtvp06fjiSeewKmnnho6vyEhhBDS7/RnhQVCCCEkDE4V0eeeey5Q+1dffVWdfvrpatKkSSoSiahRo0apo446Sv32t7/Na+tUEXUWXddVe3u7mj17trr00kvVu+++G+iY3//+99WcOXPU6NGjVTQaVbvuuqs666yzsvZ3KkS+8MIL6l//9V9Vc3OzamlpUV/60pfUhx9+mNXf008/rWbPnq0aGxvVmDFj1Nlnn61efPFFBUDdddddbrtkMqnOPvtsNWbMGCWEyKpi6VcJ9Prrr1dTpkxRsVhMTZs2Tf34xz/2rVwZporoeeedl7XunXfeUQDUd7/73az1hSq2/uQnP1H77befikajqq2tTR133HHq1VdfzTvWT37yE7XnnnuqaDSqPv7xj6s777wzr9qjUkql02n1ve99T33iE59Q8XhcNTc3q7333lv9+7//u3rjjTeUUkqtXbtWnXDCCWq33XZTsVhMtbe3q0MPPVQ9+OCDJc/5zTffVEII9ctf/jJv2yWXXKImTpyopJQKgHriiSeUUsH/npVUEd20aZM69dRT1R577KEaGxtVNBpVu+++u1q0aJFav359XvtUKqWuuOIKteuuu7rX9KabbvLt+/LLL1ejR49WiUQib2xBf1YG/eyZpql+8IMfqBkzZrifidmzZ6v/+7//c9u8++67av78+aqlpUUB8K2QWYx77rlHHXjgge7n44ADDsj6O/h9rnp7e9XFF1+sdtttNxWJRNSECRPUV7/6VbV9+/asdkH/1t4qog7vv/++2nvvvdWUKVPUW2+9VfI8EomEWrRokdpvv/1Ua2uramhoUHvttZe64oorVHd3t9tu27Zt6sQTT1QjRoxw7xMOQb4vYc5LqeDfVYelS5e6laIJIYSQwYZQKkBJNEIIIYRUjaVLl+LKK6/ERx99VDJvFhnYOFUXnaqXQxnTNPGxj30MJ598Mr7zne/093DIEGTWrFkQQuC5557r76EQQgghoWGIKCGEEEJImVx33XU4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QkLAUtaEEEJqDW0NIYSQelCuvWltbQ2Ug2306NHQNC3PW23z5s15HmgO48eP922v6zra29uLtvH2OWHCBEyfPj2rzbRp07KKJVQbhogSQkgIbCMUppR1f4+YEELIYIO2hhBCSD2otb2JRqOYOXMmVq9enbV+9erVmDNnju8+s2fPzmv/6KOPYtasWYhEIkXbePs8+OCD8Y9//COrzT//+U/stttu4U4iBPRgI4SQENCrgBBCSK2hrSGEEFIP6mFvlixZgoULF2LWrFmYPXs27rjjDqxfvx6LFi0CAFxyySXYsGGDmxtt0aJFuOWWW7BkyRKcc845WLt2LVasWOFWBwWACy64AIcccghuuOEGHHfccXjggQfw2GOP4amnnnLbLF68GHPmzMG1116Lk046CX/+859xxx134I477ijjLIJBDzZCCAlBuCc84XIaONx2222YOnUq4vE4Zs6cWbK09Jo1azBz5kzE43HsvvvuuP322/Pa3H///Zg+fTpisRimT5+OVatW5bXZsGEDTj31VLS3t6OxsRH7778/XnjhhdDjJ4QQUhn1sDWEEEJIPezNggULsGzZMlx11VXYf//98Yc//AEPPfSQ60m2ceNGrF+/3m0/depUPPTQQ3jyySex//774+qrr8ZNN92EL3zhC26bOXPm4Be/+AXuuusu7Lfffrj77ruxcuVKHHTQQW6bAw88EKtWrcJ9992HGTNm4Oqrr8ayZctwyimnVHDFiiOUky2OEEJIQTo6OtDW1oYbW/ZAgwj+7KZXmfiPzrewc+fOQHkKVq5ciYULF+K2227DwQcfjB/96Ef4yU9+gtdeew277rprXvt33nkHM2bMwDnnnIN///d/x5/+9Cece+65uO+++1wjtHbtWsydOxdXX301TjjhBKxatQr/+Z//iaeeeso1Qtu3b8cBBxyAww47DF/96lcxduxYvPXWW5gyZQr22GOPwOdLCCGkfOplawghhAxvaG9qAwU2QggJgGOEvt/6sdBG6BsdbwY2QgcddBA++clPYvny5e66adOm4fjjj/ctf33xxRfjwQcfxOuvv+6uW7RoEV5++WWsXbsWgP3UqKOjAw8//LDb5qijjsLIkSNdV+tvfetb+NOf/lTSW44QQkjtqJetIYQQMryhvakNDBElhJAQOHkKwiyAbcS8SzKZzOs7lUrhhRdewPz587PWz58/H08//bTveNauXZvX/sgjj8Tzzz+PdDpdtI23zwcffBCzZs3CF7/4RYwdOxYHHHAAfvzjH4e+PoQQQiqnXFtDCCGEhIH2prpQYCOEkBCUa4QmT56MtrY2d/HzRtuyZQtM08wrWT1u3Li8MtQOmzZt8m1vGAa2bNlStI23z7fffhvLly/HnnvuiUceeQSLFi3C+eef7yYbJYQQUj/qNeFhzk9CCBneUGCrLqwiSgghIQib3FOD3fa9997LcqOOxWIF9xE5/Sul8taVap+7vlSflmVh1qxZuPbaawEABxxwAF599VUsX74cp512WsFjE0IIqT7l2powrFy5EhdeeGFWzs+jjz66aM7PY445Bueccw7uvfdeN+fnmDFjsnJ+LliwICvn50knnZSX8/Pggw/GYYcdhocfftjN+TlixIjQ50AIIaQy6mFvhhMU2AghpA60traWzFMwevRoaJqW5622efPmPA80h/Hjx/u213Ud7e3tRdt4+5wwYQKmT5+e1WbatGm4//77i58YIYSQQcl//dd/4ayzzsLZZ58NAFi2bBkeeeQRLF++3NfL+vbbb8euu+6KZcuWAbBtxPPPP4/vfe97rsC2bNkyHHHEEbjkkksAAJdccgnWrFmDZcuWuTk/b7jhBkyePBl33XWX2/eUKVNqeKaEEEJIfWCIKCGEhEBDSDfqEH1Ho1HMnDkTq1evzlq/evVqzJkzx3ef2bNn57V/9NFHMWvWLEQikaJtvH0efPDB+Mc//pHV5p///KdbPpsQQkj9KNfWBMn3CTDnJyGEEJtazm2GIxTYCCEkBDLjRh10kSFcrgFgyZIl+MlPfoI777wTr7/+OhYvXoz169dj0aJFAGxvAG/I5qJFi7Bu3TosWbIEr7/+Ou68806sWLECF110kdvmggsuwKOPPoobbrgBf//733HDDTfgsccew4UXXui2Wbx4MZ555hlce+21ePPNN/Hzn/8cd9xxB84777zKLhghhJDQlGtrguT7BJjzkxBCiE2t5zbDDYaIEkJICMIm9wybCHTBggXYunUrrrrqKmzcuBEzZszAQw895HqSbdy4EevXr3fbT506FQ899BAWL16MW2+9FRMnTsRNN93khusAwJw5c/CLX/wC3/72t3H55Zdjjz32wMqVK918OABw4IEHYtWqVbjkkktw1VVXYerUqVi2bBlOOeWUcCdACCGkYsq1NWHyfQLM+UkIIcOdWs9thhsU2AghJAShE4GW8ZTn3HPPxbnnnuu77e67785bd+ihh+LFF18s2ueJJ56IE088sWibY489Fscee2zgcRJCCKkN5dqaIPk+Aeb8JIQQYlOPuc1wgiGihBASApayJoQQUmtqbWuY85MQQgjAuU21oQcbIYSEgE95CCGE1Jp62JolS5Zg4cKFmDVrFmbPno077rgjL+fnhg0b3NxoixYtwi233IIlS5bgnHPOwdq1a7FixQq3Oihg5/w85JBDcMMNN+C4447DAw88gMceewxPPfWU22bx4sWYM2cOrr32Wpx00kn485//jDvuuAN33HFH6HMghBBSGZzbVBcKbIQQEgIZMrknE4ESQggJSz1sDXN+EkII4dymugjlZCclhBBSkI6ODrS1teGXE6ejUQYvUN1jmTjpg9ewc+fOQHlxCCGEDF9oawghhNQD2pvaQA82QggJgdQEpORTHkIIIbWDtoYQQkg9oL2pLhTYCCEkDJqEkCHqwwg6CRNCCAkJbQ0hhJB6QHtTVSiwEUJICIQUECHK5wjwKQ8hhJBw0NYQQgipB7Q31SWEVEkIIYQQQgghhBBCCMmFHmyEEBICqQnIEE95JJ/yEEIICQltDSGEkHpAe1NdKLARQkgIhAyXp0CwUDMhhJCQ0NYQQgipB7Q31YUCGyGEhIBPeQghhNQa2hpCCCH1gPamulBgI4SQEAiNiUAJIYTUFtoaQggh9YD2prpQYCOEkBDYRiiEGzWsGo6GEELIUIS2hhBCSD2gvakuFNgIISQEdKMmhBBSa2hrCCGE1APam+pCgY0QQkIghICQIdyoLRohQggh4aCtIYQQUg9ob6oLBTZCCAmB1CRkCDdqqYK3JYQQQgDaGkIIIfWB9qa6UGAjhJAQhE4EqviUhxBCSDhoawghhNQD2pvqQoGNEEJCQCNECCGk1tDWEEIIqQe0N9WF/n2EEEIIIYQQQgghhFQAPdgIISQEzFNACCGk1tDWEEIIqQe0N9WFAhshhIQhpBs16EZNCCEkLLQ1hBBC6gHtTVWhwEYIISGQQkCGKGUtBY0QIYSQcNDWEEIIqQe0N9WFAhshhIRAaBIihBu1sOhGTQghJBy0NYQQQuoB7U11ocBGCCEhkJqADOFGLS0+5SGEEBIO2hpCCCH1gPamulB+JISQEDilrMMshBBCSBhoawghhNSDetmb2267DVOnTkU8HsfMmTPxxz/+sWj7NWvWYObMmYjH49h9991x++2357W5//77MX36dMRiMUyfPh2rVq0q2N91110HIQQuvPDCssYfFApshBASAseNOsxCCCGEhIG2hhBCSD2oh71ZuXIlLrzwQlx22WV46aWXMHfuXBx99NFYv369b/t33nkHxxxzDObOnYuXXnoJl156Kc4//3zcf//9bpu1a9diwYIFWLhwIV5++WUsXLgQJ510Ep599tm8/p577jnccccd2G+//UKPPSy0xoQQEgKp9blSB1v6e8SEEEIGG7Q1hBBC6kG59qajoyNrSSaTBY/xX//1XzjrrLNw9tlnY9q0aVi2bBkmT56M5cuX+7a//fbbseuuu2LZsmWYNm0azj77bHz5y1/G9773PbfNsmXLcMQRR+CSSy7B3nvvjUsuuQSHH344li1bltVXV1cXTjnlFPz4xz/GyJEjK75epaDARgghIRBShF4IIYSQMNDWEEIIqQfl2pvJkyejra3NXa677jrf/lOpFF544QXMnz8/a/38+fPx9NNP++6zdu3avPZHHnkknn/+eaTT6aJtcvs877zz8LnPfQ6f/exng1+UCmCRA0IICYGUEjKEa7Q0+RyDEEJIOGhrCCGE1INy7c17772H1tZWd30sFvNtv2XLFpimiXHjxmWtHzduHDZt2uS7z6ZNm3zbG4aBLVu2YMKECQXbePv8xS9+gRdffBHPPfdc4POrFApshBBCCCGEEEIIISQQra2tWQJbKYTI9rRWSuWtK9U+d32xPt977z1ccMEFePTRRxGPxwOPs1L4uIsQQkJQj0o7w6XKDiGEEH9YRZQQQkg9qLW9GT16NDRNy/NW27x5c54HmsP48eN92+u6jvb29qJtnD5feOEFbN68GTNnzoSu69B1HWvWrMFNN90EXddhmmao8wgKBTZCCAlBrSvtDKcqO4QQQvxhFVFCCCH1oNb2JhqNYubMmVi9enXW+tWrV2POnDm++8yePTuv/aOPPopZs2YhEokUbeP0efjhh+Ovf/0r/vKXv7jLrFmzcMopp+Avf/kLNK021YFojQkhJARCytALELzSznCqskMIIcSfcm1NWOgxTQghw5t62JslS5bgJz/5Ce688068/vrrWLx4MdavX49FixYBAC655BKcdtppbvtFixZh3bp1WLJkCV5//XXceeedWLFiBS666CK3jRP+ecMNN+Dvf/87brjhBjz22GOuPWlpacGMGTOylqamJrS3t2PGjBmVXbQiUGAbBtx9990QQkAIgSeffDJvu1IKH/vYxyCEwLx586p6bCEEli5dWrX+fvOb3+C0007Dvvvui0gkUjRum5BaIDUZegGCVdoZblV2yNBjqNibjo4OfOc738G8efMwfvx4NDc3Y99998UNN9yARCJRlWMQUoxybU0Y6DFNBitDxdYAwGWXXYYDDjgAo0aNcoXrr3zlK1i3bl3VjkFIMephbxYsWIBly5bhqquuwv77748//OEPeOihh7DbbrsBADZu3Jhle6ZOnYqHHnoITz75JPbff39cffXVuOmmm/CFL3zBbTNnzhz84he/wF133YX99tsPd999N1auXImDDjqo8otSASxyMIxoaWnBihUr8gzNmjVr8NZbb6GlpaV/BhaCVatW4ZlnnsEBBxyAWCyGF154ob+HRIYbYV2jteCVdoZblR0ydBns9mb9+vVYtmwZFi5ciCVLlqC5uRl//OMfsXTpUqxevRqrV6/mAx5SW8q0NWHwekwDtrfzI488guXLl/s+BPJ6TAPAtGnT8Pzzz+N73/ueO+nxekwDtlfCmjVrsGzZMtx3331uX16P6WuuuSb02AkBBr+tAYAdO3bgS1/6EqZNm4aWlha89tpruOaaa/Dggw/i1VdfdfNNEVIz6mBvAODcc8/Fueee67vt7rvvzlt36KGH4sUXXyza54knnogTTzwx8Bj8BPlqQ4FtGLFgwQL87Gc/w6233po10V+xYgVmz56Njo6OfhxdMH784x9DZtxSv/a1r1FgI3VHyHBGyHGjDlNpZ7hU2SFDl8Fub6ZOnYp3330XTU1N7rrPfOYzaGpqwje/+U386U9/wr/8y7/04wjJUKdcW5P73YrFYr4PdByP6W9961tZ68vxmF6xYgXS6TQikQjWrl2LxYsX57XJTUng9ZimwEbKZbDbGgC49dZbs97PmzcPU6dOxTHHHIMHHngAX/7yl/tpZGS4UK69If7w6gwjvvSlLwFA1hPEnTt34v777y948962bRvOPfdc7LLLLohGo9h9991x2WWX5eWP6ujowDnnnIP29nY0NzfjqKOOwj//+U/fPt944w2cfPLJGDt2LGKxGKZNm5ZnXAoh6/iFnjJlCs4444y89fPmzau6uzkZPNQyT8Fwq7JDhi6D3d40NTVliWsOn/rUpwDYonQ1ob0huZRra4KkIwBq4zFdrI2fx3ShsRESlMFuawoxZswYAICuV9cXhraG+FGvnJ/DBV6dYURraytOPPFE3Hnnne66++67D1JKLFiwIK99IpHAYYcdhnvuuQdLlizBb3/7W5x66qm48cYb8fnPf95tp5TC8ccfj//+7//GN77xDaxatQqf/vSncfTRR+f1+dprr+HAAw/E3/72N3z/+9/Hb37zG3zuc5/D+eefjyuvvLI2J05IFbGr52ghluC32eFWZYcMXYaqvXn88ccBAPvss09Z+xMSlHJtzXvvvYedO3e6ixOqWfA4/eQxfe+999JjmlTMULI1hmGgt7cXL730Ei688EJ8/OMfzxoTIbWilnOb4QhDRIcZX/7yl3HYYYfh1VdfxT777IM777wTX/ziF31zFPz0pz/FK6+8gl/+8pf44he/CAA44ogj0NzcjIsvvhirV6/GEUccgUceeQRPPPEEfvjDH+L8889320WjUVx22WVZfS5ZsgQtLS146qmnXFfuI444AslkEtdffz3OP/98Vi8kA5qw5anDGqElS5Zg4cKFmDVrFmbPno077rgjr8rOhg0bcM899wCwq+zccsstWLJkCc455xysXbsWK1asyHqae8EFF+CQQw7BDTfcgOOOOw4PPPAAHnvsMTz11FMA+qrseKlHlR0ytBlq9uaVV17BjTfeiBNOOIFJ2UnNKdfWBE1HMBA8ph1M08Qf/vAH3HLLLUgmk3yoQ0IxFGzNpk2bMGHCBPf9QQcdhCeeeALNzc0VXRtCglDruc1wg1dnmHHooYdijz32wJ133om//vWveO655wq6UD/++ONoamrKSxzouBb//ve/BwA88cQTAIBTTjklq93JJ5+c9T6RSOD3v/89TjjhBDQ2NsIwDHc55phjkEgk8Mwzz1TjNH0xTTPrmJZl1exYZOgipQy9hGE4VdkhQ5uhZG/effddHHvssZg8eTJ+8pOflGxPe0Mqpda2hh7TZKgwFGzN6NGj8dxzz+Gpp57Cj3/8Y2zbtg2HHXYYNm7cWHQ/2hpSDWptb4Yb9GAbZgghcOaZZ+Kmm25CIpHAxz/+ccydO9e37datWzF+/Pg8V/+xY8dC13Vs3brVbed9eukwfvz4vP4Mw8DNN9+Mm2++2feYTg6PWnD44YdjzZo17vvTTz/dt2IJIf3NcKmyQ4Y2Q8XerFu3Docddhh0Xcfvf/97jBo1quQ+tDdkMECPaTIUGAq2Rtd1zJo1CwBw8MEH46ijjsLUqVNx/fXX44c//GHB/WhrCBl4UGAbhpxxxhn4z//8T9x+++34zne+U7Bde3s7nn322bx8HJs3b4ZhGBg9erTbzjAMbN26NcsQ5YYIjBw5EpqmYeHChTjvvPN8jzl16tRKTq0oP/rRj9DZ2em+d8ZfiHg8npfwFLANZal9ydCFbtSEBGew25t169Zh3rx5UErhySefxKRJk0ruA9DekMqph61ZsGABtm7diquuugobN27EjBkzAnlML168GLfeeismTpxY0GP629/+Ni6//HLsscce9JgmNWew25pcJk2ahIkTJxYsquBAW0OqAec21YUC2zBkl112wTe/+U38/e9/x+mnn16w3eGHH45f/vKX+PWvf40TTjjBXe88yTz88MMBAIcddhhuvPFG/OxnP3PzFADAz3/+86z+Ghsbcdhhh+Gll17Cfvvth2g0Ws3TKslee+0Vqv2UKVPwyiuvZK375z//iX/84x80QsMYGiFCgjOY7c369esxb948mKaJJ5980hUdgkB7QyqlXraGHtNkKDCYbY0fb775Jt5//33827/9W9F2tDWkGnBuU10osA1Trr/++pJtTjvtNNx66604/fTT8e6772LffffFU089hWuvvRbHHHMMPvvZzwIA5s+fj0MOOQT/8R//ge7ubsyaNQt/+tOf8N///d95ff7whz/Ev/zLv2Du3Ln46le/iilTpqCzsxNvvvkm/u///s+t0FaIdevW4bnnngMAvPXWWwCA//3f/wVgGw3HvboaLFy4EKeeeirOPfdcfOELX8C6detw4403uqWzyfBEiHDlqYWgESLDm8FobzZv3uzmv1mxYgU2b96MzZs3u9snTZoU2JstCLQ3JBfaGkLCMRhtzSuvvILFixfjxBNPxO677w4pJf7617/iBz/4Adrb23HRRReVf0F8oK0hftDeVBcKbKQg8XgcTzzxBC677DJ897vfxUcffYRddtkFF110Ea644gq3nZQSDz74IJYsWYIbb7wRqVQKBx98MB566CHsvffeWX1Onz4dL774Iq6++mp8+9vfxubNmzFixAjsueeeOOaYY0qO6Ykn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++67AQA/+tGPMH369JRq6VNOOQXLli3DBRdcACEE5s+fj6VLl+Kkk07CSSedhKVLl6J///649NJLAQB1dXW44oorcPXVV2Po0KEYMmQIrrnmGowbN84eomDEiBEpxzJw4EAAwIknnojjjz++HC+fiIjIM1awERH5oPu8w2NNZW1127Ee2RJsP/7xj7Fr166cj7Fjx6KxsbGoLjtO2brsZGvj7LITCoXwxS9+EYDRZecHP/iB17eSiIiyKDTWlMojjzyCcePGYcqUKZgyZQq+8pWv4KGHHkpps3v3brS3t9s/X3vttZg/fz7mzp2L8ePHY//+/di4cSMGDRpkt/nNb36Db3/727jooovwjW98A/3798dTTz0FVVVL+nqIiMhQSfHmyJEjmDVrlt3jZ9asWfjkk09yriOlxJIlS9DU1IR+/frhzDPPxNtvv53Spru7Gz/5yU9QX1+PAQMG4Pzzz8eHH37our3u7m589atfhRACO3fu9P0aWMFGROSDpkuIEo5TUF9fj/r6+rztnF12vv71rwPw12XntNNOA5DssnPzzTcDSO2yc9FFFwFIdtlZvnw5AKPLzk033WRv98CBA5g6dSqeeOKJlDHhiIioMKWONX4NGTIEDz/8cM42UqYegxACS5YswZIlS7KuE41Gcfvtt+P222/3dBwjR47M2A8RERWukuLNpZdeig8//BAbNmwAYFRLz5o1C0899VTWdZYvX45bb70Va9euxcknn4ybbroJ55xzDnbv3m3f0Jk/fz6eeuopPP744xg6dCiuvvpqTJ8+Hdu3b8+4oXPttdeiqakJr7/+ekGvgQk2IiIfpJSQPgJLqS4E2GWHiKj3qpRYQ0REvVulxJtdu3Zhw4YN2LZtm33D/t5770VLSwt2797tOoGblBIrV67E4sWL8Z3vfAcA8MADD6ChoQGPPvoorrrqKrS3t2PNmjV46KGH7GuZhx9+GMOHD8czzzxjT+AGAH/84x+xceNGrFu3Dn/84x8Leh1MsBER+aD7LI0uZRn1I488gnnz5tkzfp5//vlYtWpVShu3LjudnZ2YO3cujhw5ggkTJrh22QmFQrjooovQ2dmJb37zm1i7di277BARlUklxRoiIuq9Co03HR0dKctramqyDoHjxdatW1FXV5fSG+aMM85AXV0dtmzZ4ppg27NnD9ra2uxrIes4Jk+ejC1b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RERUBCbYiIiIiIiIiIiIisAEGxERERERERERURGYYCMiIiIiIiIiIioCE2xERERERERERERFYIKNiIiIiIiIiIioCEywERERERERERERFYEJNiIiIiIiIiIioiIwwUZERERERERERFQEJtiIiIiIiIiIiIiKwAQbERERERERERFREZhgIyIiIiIiIiIiKgITbEREFebOO+/EqFGjEI1G0dzcjBdeeCFn+82bN6O5uRnRaBSjR4/G6tWrU56/9957MWnSJAwePBiDBw/G2WefjZdffrno/RIREREREZEh1NMHQERULbq6uhCLxXyvF4lEEI1GPbV94oknMH/+fNx55534xje+gbvvvhvTpk3DX/7yF4wYMSKj/Z49e3Deeedh9uzZePjhh/HnP/8Zc+fOxTHHHIMZM2YAAJ5//nlccsklmDhxIqLRKJYvX44pU6bg7bffxnHHHVfQfomIqDTKEWuIiIgYb4InpJSypw+CiKjSdXV1YWi/gTgKzfe6jY2N2LNnj6dANGHCBHzta1/DXXfdZS8bM2YMvv3tb2PZsmUZ7X/605/i97//PXbt2mUvmzNnDl5//XVs3brVdR+apmHw4MFYtWoVLrvssoL2S0REwStXrCEior6N8aY0WMFGRORBLBbDUWi4DMch4qN3fQw6Hmzbj0OHDqG2ttZeXlNTg5qamox9bN++Hdddd13K8ilTpmDLli2u29+6dSumTJmSsmzq1KlYs2YN4vE4wuFwxjpHjx5FPB7HkCFDCt4vEREFr9hYE4vFeMFDRER5Md6UBhNsREQ+9BMqIsJ7EFKlACQwfPjwlOU33HADlixZkrLs0KFD0DQNDQ0NKcsbGhrQ1tbmuv22tjbX9olEAocOHcKwYcMy1rnuuutw3HHH4eyzzy54v0REVDqFxhoiIiI/GG+CxQQbEZEPigBU4aM9AEhg3759GRVs2QiRugMpZcayfO3dlgPA8uXL8dhjj+H555/PuOvkd79ERFQahcYaIiIiPxhvgsUEGxGRD6oQUH0knVQYbWtra1MSbG7q6+uhqmpG1djBgwczqsssjY2Nru1DoRCGDh2asnzFihVYunQpnnnmGXzlK18par9ERFQ6hcYaIiIiPxhvguW9FpCIiKAK/w+vIpEImpubsWnTppTlmzZtwsSJE13XaWlpyWi/ceNGjB8/PmX8tVtuuQW//OUv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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABM8AAAKbCAYAAADi2K0VAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOy9e7wUxZn//6nqnss53BSQm6IiJgpBooJLwKCoEQXNopHAJoaoUbMsJAqsWRcvP4gmQdQkxKgQFTRGI2QXUWOIClFQ16MBxcvXGHNRwSgEQeV+zsx01++P6qqu7ume6ZkzczjA8369Rjk9NT1V3T31VH3qeZ5iQggBgiAIgiAIgiAIgiAIgiCK4Hu7AgRBEARBEARBEARBEATRXiHxjCAIgiAIgiAIgiAIgiBiIPGMIAiCIAiCIAiCIAiCIGIg8YwgCIIgCIIgCIIgCIIgYiDxjCAIgiAIgiAIgiAIgiBiIPGMIAiCIAiCIAiCIAiCIGIg8YwgCIIgCIIgCIIgCIIgYiDxjCAIgiAIgiAIgiAIgiBiIPGMIAiCIAiCIAiCIAiCIGIg8YwgCILYb7jvvvvAGNMv27Zx2GGH4ZJLLsEHH3xQVP6tt97CxRdfjMMPPxzpdBrdu3fH2LFj8fvf/76o7KpVqwLnTqfTOOSQQ3DyySfj2muvxfr169uiiVWzfPlyzJ49O/K9I488EhdffHFV523NZ9uSiy++GEceeWSbf28+n8exxx6Lm266SR974YUXMHv2bHz66adVn3f27NlgjAWOjRo1CqNGjUr0+WuvvRYnnHACunbtimw2i6OOOgrf/va3I5/jfD6P73//+zjyyCORyWRw7LHH4uc//3lRuUmTJuG8886rpjl1484778R9991X1WePPPJInHvuubWtEEEQBEEQ+yQknhEEQRD7Hffeey+ampqwYsUKXH755XjooYcwcuRI7Nq1S5d5+OGHccIJJ+CPf/wjrr/+eqxcuRLz588HAIwdOxb/9V//FXnuH/3oR2hqasIzzzyDhQsXYtSoUVi0aBEGDBiABx98sE3aVw3Lly/H97///cj3li1bhuuvv76Na3RgcOedd+KTTz7Bd7/7XX3shRdewPe///1WiWeXXXYZmpqaqv78p59+iq997Wv45S9/iSeeeAJXXXUVHn/8cQwbNgxbt24NlJ0yZQrmzJmDqVOn4sknn8T555+PK6+8Ej/60Y8C5WbPno3f/e53ePrpp6uuV61pjXhGEARBEAShsPd2BQiCIAii1gwaNAhDhw4FAJx22mlwHAc33ngjHnnkEVx44YX4+9//jkmTJuG4447DqlWr0KFDB/3Zr371q/iP//gP3HLLLTjxxBPxb//2b4Fzf+Yzn8EXvvAF/fe//uu/4j//8z/xpS99CRdffDEGDx6M4447rm0aWiNOOOGEvV2F/ZJCoYBbbrkF3/rWtwLPWC047LDDcNhhh1X9+TvuuCPw96hRo9CvXz+MHTsWjz76KL71rW8BAN58800sXLgQP/zhD/G9731Pl926dSt+8IMfYPLkyejatSsAoH///jj77LNx00034fTTT6+6bgcKjuOgUCggk8ns7aoQBEEQBFEG8jwjCIIg9nuU2KVC0n76059i9+7d+PnPfx4pavz4xz/GQQcdhB/+8IeJzt+1a1f84he/QKFQwE9/+tOSZV3XxQ9+8AMcc8wxaGhowEEHHYTBgwfjZz/7mS6jQvLWrVuHr3zlK+jcuTO6dOmCb3zjG/joo48C51uyZAlGjx6N3r17o6GhAQMGDMB///d/B7zsLr74Yi2WmKGn7733HoDi0Mvm5mb853/+J44//nh06dIFXbt2xfDhw/Hoo48muh5RMMbwne98B/fee69u+9ChQ/Hiiy9CCIFbbrkF/fr1Q8eOHXH66afjb3/7W9E5Fi1ahM9//vPIZrPo2rUrzj//fLz11ltF5e677z4cc8wxyGQyGDBgAO6///7IOuVyOfzgBz/Asccei0wmg0MOOQSXXHJJ0TV++umnMWrUKHTr1g0NDQ04/PDDccEFF2D37t0l2/zYY4/hgw8+wKRJk/Sx2bNnaxGqX79++l6sWrUKQLL7qc4TDttsLYcccggAwLb9tdVHHnkEQghccsklgbKXXHIJ9uzZgyeeeCJwfNKkSVi5ciX+/ve/V1WHSp4913Xx85//HMcff7z+LX3hC1/AY489BkA+12+++SZWr16tr3NrQ3fvvPNO2LaNWbNmAQA++ugjTJkyBQMHDkTHjh3Ro0cPnH766XjuuecCn3vvvffAGMPNN9+MH/zgB+jXrx8ymQyeeeYZfS9ff/11fPWrX9XtnjFjBgqFAt5++22cffbZ6NSpE4488kjcfPPNVV8z9Tv81a9+hQEDBqCxsRGf//zn8fjjj1d0HVR7br31VvzkJz/Rv93hw4fjxRdfLCr/0ksv4ctf/jK6deuGbDaL/v37Y9q0aRV9J0EQBEHsTcjzjCAIgtjvUUKMEgdWrFiBnj17BjzITBobGzF69Gj85je/waZNm9CrV6+y33HSSSehd+/eePbZZ0uWu/nmmzF79mxcd911OOWUU5DP5/HnP/85MoTv/PPPx4QJEzB58mS8+eabuP766/GnP/0JL730ElKpFADgr3/9K8aOHYtp06ahQ4cO+POf/4y5c+fij3/8ow6fu/7667Fr1y787//+byDUr3fv3pF1bGlpwccff4yrrroKhx56KHK5HFauXImvfOUruPfee/HNb36z7PWI4vHHH8e6detw0003gTGGq6++Gueccw4uuugivPPOO7j99tuxbds2zJgxAxdccAFeffVVLRDNmTMH11xzDb72ta9hzpw52Lp1K2bPno3hw4djzZo1+MxnPgNACmeXXHIJxo0bhx//+MfYtm0bZs+ejZaWFnDurxm6rotx48bhueeew3/9139hxIgRWL9+PWbNmoVRo0Zh7dq1aGhowHvvvYdzzjkHI0eOxKJFi3DQQQfhgw8+wBNPPIFcLofGxsbY9v7ud79Djx49MHDgQH3ssssuw8cff4yf//znePjhh/U9UGWS3M9aUigU9DM4bdo0fPazn8VXvvIV/f7/+3//D4ccckjRb2Dw4MH6fZNRo0ZBCIHly5cHQlVHjRqF1atXQwhRsj6VPHsXX3wxHnjgAVx66aW44YYbkE6n8corr2hReNmyZRg/fjy6dOmCO++8EwCq9vISQuB73/sebrvtNtxzzz1abP74448BALNmzUKvXr2wc+dOLFu2DKNGjcIf/vCHohx0t912Gz772c/i1ltvRefOnfGZz3xGi00TJkzAN77xDfz7v/87VqxYgZtvvhn5fB4rV67ElClTcNVVV+HXv/41rr76ahx99NH6PlX6e/3d736HNWvW4IYbbkDHjh1x88034/zzz8fbb7+No446qqLrcscdd+DYY4/FvHnzAMi+ZuzYsXj33XfRpUsXAMCTTz6JL3/5yxgwYAB+8pOf4PDDD8d7772Hp556qqLvIgiCIIi9iiAIgiCI/YR7771XABAvvviiyOfzYseOHeLxxx8XhxxyiOjUqZPYtGmTEEKIbDYrvvCFL5Q819VXXy0AiJdeekkI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t2gXQwC46qqr8Mknn+C6665LdL6PP/4YkydPRiqVwvTp0xPX46CDDsL48eMxdepUfPzxx9rbTfHggw8G/v7Nb36DQqGgk5CriXI4CfovfvGLou8Ke4SUgjGGdDodmIhv2rSpVbtttobhw4ejoaEBDzzwQOD4P/7xDzz99NM6lO6YY45B79698dBDDwVCaNevX48XXngh8Nlzzz0XW7duheM4GDp0aNHrmGOOKaqHZVkYNmyY3rn0lVdeKVnvY489NnLXybh7Ucn9TMIxxxwTaFM5Qetvf/sb/vGPf+Doo4/Wx8aNGwfGGH75y18Gyt53331oaGjA2WefHTheKBTw/vvvBzZJqISkz54KZQyLVmGq9agyueiii7B48WKdfN9xnEB9w/fr9ddfD2zMUW/a2+/V5LOf/Sz69++PRYsWtQsxjyAIgiCqhTzPCIIgiAOO/v3741e/+hUuvPBCnHTSSZgxYwaOOeYY/POf/8SiRYvw+9//HldddRUmTpxY9Nm//vWvePHFF+G6LrZu3YqXXnoJCxcuxPbt23H//ffjc5/7XMnv/vKXv4xBgwZh6NChOOSQQ7B+/XrMmzcPRxxxhN4xUvHwww/Dtm2ceeaZerfNz3/+85gwYQIAYMSIETj44IMxefJkzJo1C6lUCg8++CBee+21ou897rjjAABz587FmDFjYFkWBg8erEMkTc4991w8/PDDmDJlCsaPH4/3338fN954I3r37o2//vWvia9zrTjooINw/fXX45prrsE3v/lNfO1rX8PWrVvx/e9/H9lsFrNmzQIAcM5x44034rLLLsP555+Pyy+/HJ9++ilmz55d5EH4b//2b3jwwQcxduxYXHnllfiXf/kXpFIp/OMf/8AzzzyDcePG4fzzz8eCBQvw9NNP45xzzsHhhx+O5uZmLFq0CADwpS99qWS9R40ahRtuuAG7d+8OeBSqe/Gzn/0MF110EVKpFI455piK7mdreP311zF9+nSMHz8eRx11FDjneOONN/DTn/4U3bp1w1VXXaXLfu5zn8Oll16KWbNmwbIsnHTSSXjqqadw11134Qc/+EFRqOHrr7+O3bt347TTTgscP+OMM7B69eqyec+SPnsjR47EpEmT8IMf/AD//Oc/ce655yKTyWDdunVobGzUO30qz7klS5bgqKOOQjab1de/EsaPH4/GxkaMHz8ee/bswUMPPYR0Oo1zzz0XN954I2bNmoVTTz0Vb7/9Nm644Qb069cvcY631tLefq9h7rjjDnz5y1/GF77wBUyfPh2HH344NmzYgCeffLJogYAgCIIg2i17ecMCgiAIgqgZarfNNWvWJCr/5ptviosuukgcdthhIpVKia5du4qzzz5b/O53vysqq3bbVC/btkW3bt3E8OHDxTXXXCPee++9RN/54x//WIwYMUJ0795dpNNpcfjhh4tLL7008Hm1+97LL78svvzlL4uOHTuKTp06ia997Wvin//8Z+B8L7zwghg+fLhobGwUhxxyiLjsssvEK6+8IgCIe++9V5draWkRl112mTjkkEMEYyyw22PUjpk33XSTOPLII0UmkxEDBgwQd999d+QOj5Xstjl16tTAMbVj3y233BI4Hrez6T333CMGDx4s0um06NKlixg3bpx48803i77rnnvuEZ/5zGdEOp0Wn/3sZ8WiRYvERRddFNhtUwi5c+Gtt94qPv/5z4tsNis6duwojj32WPHv//7v4q9//asQQoimpiZx/vnniyOOOEJkMhnRrVs3ceqpp4rHHnusbJv/9re/CcaY+M1vflP03syZM0WfPn0E51wAEM8884wQIvn9bM1um5s2bRLf+MY3RP/+/UVjY6NIp9PiqKOOEpMnTxYbNmwoKp/L5cSsWbPE4Ycfrq/pbbfdFnnu66+/XnTv3l00NzcX1S3psDPps+c4jvjpT38qBg0apJ+J4cOHi9/+9re6zHvvvSdGjx4tOnXqJAAUPQOlULttmjzzzDOiY8eO4uyzzxa7d+8WLS0t4qqrrhKHHnqoyGaz4sQTTxSPPPJI0fMW96wL4d/Ljz76KHD8oosuEh06dCgqf+qpp4rPfe5zgWNJr1nU71C1NcnvOEl7AIhZs2YFjjU1NYkxY8aILl26iEwmI/r371+0mzBBEARBtGeYEAm2BiMIgiAIos2YPXs2vv/97+Ojjz4qm6eKaN+o3TzV7pD7M47j4Oijj8bXv/51/PCHP9zb1SEIgiAIgqgZlPOMIAiCIAiiTsyZMwcrV67EmjVr9nZV6s4DDzyAnTt34nvf+97ergpBEARBEERNoZxnBEEQBEEQdWLQoEG49957I3cA3d9wXRcPPvggDjrooL1dlZI4joNSgReMMViW1YY1al+Uy9XGOQ/sOEoQBEEQBwIUtkkQBEEQBEEcMBx55JFYv3597PunnnoqVq1a1XYVake899576NevX8kys2bNwuzZs9umQgRBEATRTiDPM4IgCIIgCOKA4be//S1aWlpi3+/UqVMb1qZ90adPn7Ihxn369Gmj2hAEQRBE+4E8zwiCIAiCIAiCIAiCIAgiBkpYQBAEQRAEQRAEQRAEQRAxkHhGEARBEARBEARBEARBEDGQeEYQBEEQBEEQBEEQBEEQMZB4RhAEQRAEQRAEQRAEQRAxkHhGEARBEARBEARBEARBEDGQeEYQBEEQBEEQBEEQBEEQMZB4RhAEQRAEQRAEQRAEQRAxkHhGEARBEARBEARBEARBEDGQeEYQBEEQBEEQBEEQBEEQMZB4RhAEQRAEQRAEQRAEQRAxkHhGEARBEARBEARBEARBEDGQeEYQBEEQBEEQBEEQBEEQMZB4RhAEQRAEQRAEQRAEQRAxkHhGEARBEARBEARBEARBEDGQeEYQBEEQBEEQBEEQBEEQMZB4RhAEQRAEQRAEQRAEQRAxkHhGEARBEARBEARBEARBEDGQeEYQBEEQBEEQBEEQBEEQMZB4RhAEQRAEQRAEQRAEQRAxkHhGEARBEARBEARBEARBEDGQeEYQBEEQBEEQBEEQBEEQMZB4RhAEQRAEQRAEQRAEQRAxkHhGEARBEARBEARBEARBEDGQeEYQBEEQBEEQBEEQBEEQMZB4RhAEQRAEQRAEQRAEQRAxkHhGEARBEARBEARBEARBEDGQeEYQBEEQBEEQBEEQBEEQMZB4RhAEQRAEQRAEQRAEQRAxkHhGEARBEARBEARBEARBEDGQeEYQBEEQBEEQBEEQBEEQMZB4RhAEQRAEQRAEQRAEQRAxkHhGEARBEARBEARBEARBEDGQeEYQBEEQBEEQBEEQBEEQMZB4RhAEQRAEQRAEQRAEQRAxkHhGEARBEARBEARBEARBEDGQeEYQBEEQBEEQBEEQBEEQMZB4RhAEQRAEQRAEQRAEQRAxkHhGEARBEARBEARBEARBEDGQeEYQBEEQBEEQBEEQBEEQMZB4RhAEQRAEQRAEQRAEQRAx2Hu7AgRBEO2Z5uZm5HK5ij+XTqeRzWbrUCOCIAhif4FsDEEQBFEPyL7UHhLPCIIgYmhubka3ho7YDafiz/bq1QvvvvsuGR+CIAgiErIxBEEQRD0g+1IfSDwjCIKIIZfLYTccfBOHIl1BlHsOLu7f9AFyuRwZHoIgCCISsjEEQRBEPSD7Uh9IPCMIgihDA7OQZskNjyUYIOpYIYIgCGK/gWwMQRAEUQ/IvtQWEs8IgiDKwBlgsQrKA2R4CIIgiESQjSEIgiDqAdmX2kLiGUEQRBksxmCx5JbHQgVWiiAIgjigIRtDEARB1AOyL7WFxDOCIIgyWBWu2lj1qwpBEASxn0E2hiAIgqgHZF9qC4lnBEEQZaBVG4IgCKJekI0hCIIg6gHZl9pC4hlBEEQZaNWGIAiCqBdkYwiCIIh6QPaltpB4RhAEUQZatSEIgiDqBdkYgiAIoh6QfaktyfctJQiCOEBhkJ1l0heZHYIgCCIpbWFj7rzzTvTr1w/ZbBZDhgzBc889V7L86tWrMWTIEGSzWRx11FFYsGBBUZmlS5di4MCByGQyGDhwIJYtWxZ7vjlz5oAxhmnTplVRe4IgCKIaaA5TW0g8IwiCIAiCIIj9lCVLlmDatGm49tprsW7dOowcORJjxozBhg0bIsu/++67GDt2LEaOHIl169bhmmuuwRVXXIGlS5fqMk1NTZg4cSImTZqE1157DZMmTcKECRPw0ksvFZ1vzZo1uOuuuzB48OC6tZEgCIIg6g2JZwRBEGVQLs+VvAiCIAgiCdXamO3btwdeLS0tkef/yU9+gksvvRSXXXYZBgwYgHnz5qFv376YP39+ZPkFCxbg8MMPx7x58zBgwABcdtll+Na3voVbb71Vl5k3bx7OPPNMzJw5E8ceeyxmzpyJM844A/PmzQuca+fOnbjwwgtx99134+CDD67NBSMIgiASQXOY2kLiGUEQRBlUss1KXgRBEASRhGptTN++fdGlSxf9mjNnTtG5c7kcXn75ZYwePTpwfPTo0XjhhRci69PU1FRU/qyzzsLatWuRz+dLlgmfc+rUqTjnnHPwpS99qaJrQhAEQbQemsPUFhLPCIIgyiCNSSWrNnu7xgRBEMS+QrU25v3338e2bdv0a+bMmUXn3rJlCxzHQc+ePQPHe/bsiU2bNkXWZ9OmTZHlC4UCtmzZUrKMec7FixfjlVdeiRT1CIIgiPrTVnOYvZVX84MPPsA3vvENdOvWDY2NjTj++OPx8ssvV9eIBJB4RhAEUQZatSEIgiDqRbU2pnPnzoFXJpOJ/Q4WCsURQhQdK1c+fLzUOd9//31ceeWVeOCBB5DNZstfBIIgCKLmtMUcZm/l1fzkk09w8sknI5VK4fe//z3+9Kc/4cc//jEOOuigyhuRELtuZyYIgthPoG2eCYIgiHpRTxvTvXt3WJZV5GW2efPmIs8xRa9evSLL27aNbt26lSyjzvnyyy9j8+bNGDJkiH7fcRw8++yzuP3229HS0gLLshK3gyAIgqictpjDmHk1AZkT88knn8T8+fMjPY/NvJoAMGDAAKxduxa33norLrjgAn0OlVcTAGbOnInVq1dj3rx5eOihhwAAc+fORd++fXHvvffqcx955JEV178SyPOMIAiiDLzCFRtO2hlBEASRkHramHQ6jSFDhmDFihWB4ytWrMCIESMiPzN8+PCi8k899RSGDh2KVCpVsow65xlnnIE33ngDr776qn4NHToUF154IV599VUSzgiCINqAau1L0g1p9mZezcceewxDhw7FV7/6VfTo0QMnnHAC7r777oquT6WQeEYQBFEG2qmGIAiCqBf1tjEzZszAPffcg0WLFuGtt97C9OnTsWHDBkyePBmAXNH/5je/qctPnjwZ69evx4wZM/DWW29h0aJFWLhwIa666ipd5sorr8RTTz2FuXPn4s9//jPmzp2LlStXYtq0aQCATp06YdCgQYFXhw4d0K1bNwwaNKj1F40gCIIoS7X2JcmGNMDezav5zjvvYP78+fjMZz6DJ598EpMnT8YVV1yB+++/v7KLVAEUtkkQBFGGSnMA0Ho6QRAEkZR625iJEydi69atuOGGG7Bx40YMGjQIy5cvxxFHHAEA2LhxYyA3Tb9+/bB8+XJMnz4dd9xxB/r06YPbbrtNh9MAwIgRI7B48WJcd911uP7669G/f38sWbIEw4YNq7B2BEEQRL2o1r68//776Ny5sz5eKqcm0PZ5NQHAdV0MHToUP/rRjwAAJ5xwAt58803Mnz8/sCBUS0g8IwiCKAOJZwRBEES9aAsbM2XKFEyZMiXyvfvuu6/o2KmnnopXXnml5DnHjx+P8ePHJ67DqlWrEpclCIIgWk+19kVtRFOOvZVXEwB69+6NgQMHBsoMGDAgsPFAraGwTYIgiDJQ2CZBEARRL8jGEARBEPWg3vZlb+XVBICTTz4Zb7/9dqDMX/7yF+1VXQ/I84wgCIIgCIIgCIIgCIKoiBkzZmDSpEkYOnQohg8fjrvuuqsor+YHH3ygc5FNnjwZt99+O2bMmIHLL78cTU1NWLhwod5FE5B5NU855RTMnTsX48aNw6OPPoqVK1fi+eef12WmT5+OESNG4Ec/+hEmTJiAP/7xj7jrrrtw11131a2tJJ4RBEGUwUKFLs+iblUhCIIg9jPIxhAEQRD1oC3sy97Kq3nSSSdh2bJlmDlzJm644Qb069cP8+bNw4UXXlh5IxLChMrORhAEQQTYvn07unTpgru7fhaNPHmWmd2ug8s//gu2bduWKF8AQRAEceBBNoYgCIKoB2Rf6gN5nhEEQZSh4mSblI6GIAiCSAjZGIIgCKIekH2pLSSeEQRBlKHSBJqUzJkgCIJICtkYgiAIoh6QfaktJJ4RBEGUgVZtCIIgiHpBNoYgCIKoB2RfaguJZwRBEGWgVRuCIAiiXpCNIQiCIOoB2ZfaQuIZQRBEGThj4BUYk0rKEgRBEAc2ZGMIgiCIekD2pbaQeEYQBFEGZjEwntyYMDI8BEEQRELIxhAEQRD1gOxLbeF7uwIEQRDtHW6xil/VcOedd6Jfv37IZrMYMmQInnvuuZLlV69ejSFDhiCbzeKoo47CggULisosXboUAwcORCaTwcCBA7Fs2bLA+/Pnz8fgwYPRuXNndO7cGcOHD8fvf//7qupPEARBVE5b2RiCIAjiwILsS20h8YwgCKIcFger4AWr8q51yZIlmDZtGq699lqsW7cOI0eOxJgxY7Bhw4bI8u+++y7Gjh2LkSNHYt26dbjmmmtwxRVXYOnSpbpMU1MTJk6ciEmTJuG1117DpEmTMGHCBLz00ku6zGGHHYabbroJa9euxdq1a3H66adj3LhxePPNNyu/TgRBEETltIGNIQiCIA5AyL7UFCaEEHu7EgRBEO2R7du3o0uXLlh61GB0sKzEn9vlOLjgndfx/vvvo3Pnzvp4JpNBJpOJ/MywYcNw4oknYv78+frYgAEDcN5552HOnDlF5a+++mo89thjeOutt/SxyZMn47XXXkNTUxMAYOLEidi+fXvAk+zss8/GwQcfjIceeii2/l27dsUtt9yCSy+9NHGbCYIgiMporY3Ztm1bwMYQBEEQBED2pV6QtEgQBFEn+vbtiy5duuhXlAgGALlcDi+//DJGjx4dOD569Gi88MILkZ9pamoqKn/WWWdh7dq1yOfzJcvEndNxHCxevBi7du3C8OHDE7WRIAiCIAiCIAhif4c2DCAIgihDpTkAOGTZKM+zKLZs2QLHcdCzZ8/A8Z49e2LTpk2Rn9m0aVNk+UKhgC1btqB3796xZcLnfOONNzB8+HA0NzejY8eOWLZsGQYOHJissQRBEESrqNbGEARBEEQpyL7UFhLPCIIgysA4B+PJHXWZFw2vkvAn/lxohxshRMldb6LKh48nOecxxxyDV199FZ9++imWLl2Kiy66CKtXryYBjSAIog2o1sYQBEEQRCnIvtQWEs8IgiDKUO9Vm+7du8OyrCKPsM2bNxd5jil69eoVWd62bXTr1q1kmfA50+k0jj76aADA0KFDsWbNGvzsZz/DL37xi4raQRAEQVQOeQYQBEEQ9YDsS22hnGcEQRBlYBar+FUJ6XQaQ4YMwYoVKwLHV6xYgREjRkR+Zvjw4UXln3rqKQwdOhSpVKpkmbhzKoQQaGlpqagNBEEQRHXU28YQBEEQByZkX2oLeZ4RBEGUQRqTClye4Vb8HTNmzMCkSZMwdOhQDB8+HHfddRc2bNiAyZMnAwBmzpyJDz74APfffz8AubPm7bffjhkzZuDyyy9HU1MTFi5cGNhF88orr8Qpp5yCuXPnYty4cXj00UexcuVKPP/887rMNddcgzFjxqBv377YsWMHFi9ejFWrVuGJJ56ouA0EQRBE5bSFjSEIgiAOPMi+1BYSzwiCIMrQFi7PEydOxNatW3HDDTdg48aNGDRoEJYvX44jjjgCALBx40Zs2LBBl+/Xrx+WL1+O6dOn44477kCfPn1w22234YILLtBlRowYgcWLF+O6667D9ddfj/79+2PJkiUYNmyYLvPPf/4TkyZNwsaNG9GlSxcMHjwYTzzxBM4888yK20AQBEFUDoXVEARBEPWA7EttYUJQVjiCIIgotm/fji5duuD3Q/8FHezkaw27CgWMWftHbNu2raINAwiCIIgDB7IxBEEQRD0g+1IfKOcZQRBEGbjFK34RBEEQRBLawsbceeed6NevH7LZLIYMGYLnnnuuZPnVq1djyJAhyGazOOqoo7BgwYKiMkuXLsXAgQORyWQwcOBALFu2LPD+/PnzMXjwYL3z9PDhw/H73/++4roTBEEQ1UFzmNpCV4cgCKIMlGyTIAiCqBf1tjFLlizBtGnTcO2112LdunUYOXIkxowZE0gFYPLuu+9i7NixGDlyJNatW4drrrkGV1xxBZYuXarLNDU1YeLEiZg0aRJee+01TJo0CRMmTMBLL72kyxx22GG46aabsHbtWqxduxann346xo0bhzfffLO6C0UQBEFUBM1haguFbRIEQcSgXJ5XjBxRscvzmc+9QC7PBEEQRCyttTHvv/9+wMZkMhlkMpmi8sOGDcOJJ56I+fPn62MDBgzAeeedhzlz5hSVv/rqq/HYY4/hrbfe0scmT56M1157DU1NTQBkns7t27cHPMnOPvtsHHzwwYGNa8J07doVt9xyCy699NLE7SUIgiAqg+Yw9YE8zwiCIAiCIAhiH6Nv377o0qWLfkUJYblcDi+//DJGjx4dOD569Gi88MILkedtamoqKn/WWWdh7dq1yOfzJcvEndNxHCxevBi7du3C8OHDE7eRIAiCINoLtNsmQRBEGSrNAcAFrUsQBEEQyajWxkR5noXZsmULHMdBz549A8d79uyJTZs2RZ5/06ZNkeULhQK2bNmC3r17x5YJn/ONN97A8OHD0dzcjI4dO2LZsmUYOHBg4rYSBEEQ1UNzmNpC4hlBEEQ5Ks0BIChfAEEQBJGQKm2MSsSfBMaC5xdCFB0rVz58PMk5jznmGLz66qv49NNPsXTpUlx00UVYvXo1CWgEQRBtAc1hagqJZwRBEGXgjIHz5MaEl5iQEARBEIRJPW1M9+7dYVlWkUfY5s2bizzHFL169Yosb9s2unXrVrJM+JzpdBpHH300AGDo0KFYs2YNfvazn+EXv/hF4jYQBEEQ1UFzmNpCfnkEQRBlYBav+EUQBEEQSainjUmn0xgyZAhWrFgROL5ixQqMGDEi8jPDhw8vKv/UU09h6NChSKVSJcvEnVMhhEBLS0vi+hMEQRDVQ3OY2kKeZwRBEGXgFgOvwOWZu7RqQxAEQSSj3jZmxowZmDRpEoYOHYrhw4fjrrvuwoYNGzB58mQAwMyZM/HBBx/g/vvvByB31rz99tsxY8YMXH755WhqasLChQsDu2heeeWVOOWUUzB37lyMGzcOjz76KFauXInnn39el7nmmmswZswY9O3bFzt27MDixYuxatUqPPHEExXVnyAIgqgOmsPUFhLPCIIgysAqzBfAyPAQBEEQCam3jZk4cSK2bt2KG264ARs3bsSgQYOwfPlyHHHEEQCAjRs3YsOGDbp8v379sHz5ckyfPh133HEH+vTpg9tuuw0XXHCBLjNixAgsXrwY1113Ha6//nr0798fS5YswbBhw3SZf/7zn5g0aRI2btyILl26YPDgwXjiiSdw5plnVlR/giAIojpoDlNbmFAZQAmCIIgA27dvR5cuXfD8eV9Cx1TytYad+QK++MhKbNu2LXEyZ4IgCOLAgmwMQRAEUQ/IvtQH8jwjCIIoA7dQoctzHStDEARB7FeQjSEIgiDqAdmX2kLiGUEQRBkYZ2AV7FRTSVmCIAjiwIZsDEEQBFEPyL7UFtpOgSAIogycc3CrghenrpUgCIJIBtkYgiAIoh60lX2588470a9fP2SzWQwZMgTPPfdcyfKrV6/GkCFDkM1mcdRRR2HBggVFZZYuXYqBAwcik8lg4MCBWLZsWez55syZA8YYpk2bVlX9k0LWlyAIgiAIgiAIgiAIgqiIJUuWYNq0abj22muxbt06jBw5EmPGjAlsRGPy7rvvYuzYsRg5ciTWrVuHa665BldccQWWLl2qyzQ1NWHixImYNGkSXnvtNUyaNAkTJkzASy+9VHS+NWvW4K677sLgwYPr1kYFiWcEQRBlUDvVVPIiCIIgiCSQjSEIgiDqQVvYl5/85Ce49NJLcdlll2HAgAGYN28e+vbti/nz50eWX7BgAQ4//HDMmzcPAwYMwGWXXYZvfetbuPXWW3WZefPm4cwzz8TMmTNx7LHHYubMmTjjjDMwb968wLl27tyJCy+8EHfffTcOPvjgiuteKSSeEQRBlIFZvOIXQRAEQSSBbAxBEARRD6q1L9u3bw+8WlpaIs+fy+Xw8ssvY/To0YHjo0ePxgsvvBD5maampqLyZ511FtauXYt8Pl+yTPicU6dOxTnnnIMvfelLyS9KK6ANAwiCIMrAOAerIAdAJWUJgiCIAxuyMQRBEEQ9qNa+9O3bN3B81qxZmD17dlH5LVu2wHEc9OzZM3C8Z8+e2LRpU+R3bNq0KbJ8oVDAli1b0Lt379gy5jkXL16MV155BWvWrEncvtZC1nc/57777gNjDIwxrFq1quh9IQSOPvpoMMYwatSomn43YyzyR1Ytjz/+OL75zW/iuOOOQyqVAmMUtkC0DRUl2vReBLG/s7/Yl+3bt+OHP/whRo0ahV69eqFjx4447rjjMHfuXDQ3N9fkOwiiFGRjCKKY/cXGAMC1116LE044AV27dtUJ0r/97W9j/fr1NfsOgoiiWvvy/vvvY9u2bfo1c+bMkt8TnpcLIUrO1aPKh4+XOuf777+PK6+8Eg888ACy2WyZq1A7yPoeIHTq1AkLFy4sOr569Wr8/e9/R6dOnfZCrSpj2bJlePHFFzFw4EB8/vOf39vVIQ4kKnV3pokNcQCxr9uXDRs2YN68eTjxxBNx11134bHHHsP48eMxe/ZsnHvuuXpARxB1g2wMQcSyr9sYAPj000/xta99Db/85S/xxBNP4KqrrsLjjz+OYcOGYevWrXu7esT+TJX2pXPnzoFXJpOJPH337t1hWVaRl9nmzZuLPMcUvXr1iixv2za6detWsow658svv4zNmzdjyJAhsG0btm1j9erVuO2222DbNhzHqfxaJYDCNg8QJk6ciAcffBB33HEHOnfurI8vXLgQw4cPx/bt2/di7ZJx99136+1zv/Od7+Dll1/eyzUiDhQYryzHDIXUEAcS+7p96devH9577z106NBBHzv99NPRoUMHfO9738P//d//4Ytf/OJerCGxv0M2hiDi2ddtDADccccdgb9HjRqFfv36YezYsXj00UfxrW99ay/VjNjfqbd9SafTGDJkCFasWIHzzz9fH1+xYgXGjRsX+Znhw4fjt7/9beDYU089haFDhyKVSukyK1aswPTp0wNlRowYAQA444wz8MYbbwTOcckll+DYY4/F1VdfDcuyKmpHUsj6HiB87WtfAwA89NBD+ti2bduwdOnS2A77448/xpQpU3DooYcinU7jqKOOwrXXXluUMHD79u24/PLL0a1bN3Ts2BFnn302/vKXv0Se869//Su+/vWvo0ePHshkMhgwYECRQYmDt+Fg8cgjj8TFF19cdHzUqFE1dw0n2j8qX0AlL4I4UNjX7UuHDh0CwpniX/7lXwDI0IBaQvaFCEM2hiDi2ddtTByHHHIIAMC2a+vLQjaGMGkL+zJjxgzcc889WLRoEd566y1Mnz4dGzZswOTJkwEAM2fOxDe/+U1dfvLkyVi/fj1mzJiBt956C4sWLcLChQtx1VVX6TJXXnklnnrqKcydOxd//vOfMXfuXKxcuRLTpk0DID1SBw0aFHh16NAB3bp1w6BBg1p30UpA1vcAoXPnzhg/fjwWLVqkjz300EPgnGPixIlF5Zubm3Haaafh/vvvx4wZM/C73/0O3/jGN3DzzTfjK1/5ii4nhMB5552HX/3qV/jP//xPLFu2DF/4whcwZsyYonP+6U9/wkknnYT/9//+H3784x/j8ccfxznnnIMrrrgC3//+9+vTcIKoAdKV2argRV0rceCwv9qXp59+GgDwuc99rqrPE0RSyMYQRDz7k40pFArYs2cP1q1bh2nTpuGzn/1soE4EUWvawr5MnDgR8+bNww033IDjjz8ezz77LJYvX44jjjgCALBx40Zs2LBBl+/Xrx+WL1+OVatW4fjjj8eNN96I2267DRdccIEuM2LECCxevBj33nsvBg8ejPvuuw9LlizBsGHDWn9RWgGFbR5AfOtb38Jpp52GN998E5/73OewaNEifPWrX43MFfDLX/4Sr7/+On7zm9/gq1/9KgDgzDPPRMeOHXH11VdjxYoVOPPMM/Hkk0/imWeewc9+9jNcccUVulw6nca1114bOOeMGTPQqVMnPP/889rt+swzz0RLSwtuuukmXHHFFTj44IPrfBUIonLMrZuTlieIA4n9zb68/vrruPnmm3H++edj8ODB1V4WgkgE2RiCKM3+YGM2bdqE3r1767+HDRuGZ555Bh07dmzVtSGIUrSVfZkyZQqmTJkS+d59991XdOzUU0/FK6+8UvKc48ePx/jx4xPXIWpjkVpD1vcA4tRTT0X//v2xaNEivPHGG1izZk2su/PTTz+NDh06FD2wyg34D3/4AwDgmWeeAQBceOGFgXJf//rXA383NzfjD3/4A84//3w0NjaiUCjo19ixY9Hc3IwXX3yxFs2MxHGcwHe6rlu37yL2PzjnFb+q4c4770S/fv2QzWYxZMgQPPfccyXLr169GkOGDNE7Ny1YsKCozNKlSzFw4EBkMhkMHDgQy5YtC7w/Z84cnHTSSejUqRN69OiB8847D2+//XZV9ScOXPYn+/Lee+/h3HPPRd++fXHPPfeULU/2hWgtbWVjCGJfZX+wMd27d8eaNWvw/PPP4+6778bHH3+M0047DRs3biz5ObIxRGsg+1Jb6OocQDDGcMkll+CBBx7AggUL8NnPfhYjR46MLLt161b06tWraIvYHj16wLZtvTPM1q1bAztjKHr16lV0vkKhgJ///OdIpVKB19ixYwEAW7ZsqVVTizjjjDMC30mJOYn2xpIlSzBt2jRce+21WLduHUaOHIkxY8YE3JxN3n33XYwdOxYjR47EunXrcM011+CKK67A0qVLdZmmpiZMnDgRkyZNwmuvvYZJkyZhwoQJeOmll3SZ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8giCIA4yyWQSzc3NWL9+feD4+vXrMWXKlMjPTJ48uaD8unXrMHHiRCQSiZJl5DnPOuss/O53v8OWLVvUa+LEibj88suxZcsWEs4IgiAIgiAIgiBA4hlBEERZZL6ASl6VMm/ePPzsZz/Dvffei23btmHu3LnYsWMHZs6cCQCYP38+rrjiClV+5syZeOeddzBv3jxs27YN9957L1asWIHrr79elZFLMpcsWYI//OEPWLJkCZ5++mnMmTMHADBo0CBMmDAh8GpsbMTQoUMxYcKE7l00giAIIhY9YWPuvPNOjB07Ful0Gs3NzXjuuedKlt+wYQOam5uRTqdxzDHH4K677ioos3r1aowfPx6pVArjx4/HmjVrip5v8eLFYIwp+0MQBEHUn56wL/0JujoEQRBlMEw/Z0C8V+XfMWPGDCxduhS33HILTjrpJDz77LNYu3YtjjrqKADArl27sGPHDlV+7NixWLt2LX7729/ipJNOwve+9z3cfvvtuOSSS1SZKVOmYOXKlbjvvvtw4okn4v7778eqVaswadKkbl8TgiAIojbU28asWrUKc+bMwYIFC7B582ZMnToV5513XsCm6Lz99ts4//zzMXXqVGzevBk33ngjZs+ejdWrV6syra2tmDFjBlpaWrB161a0tLRg+vTpePnllwvOt3HjRtx999048cQTK6s4QRAE0S16Yg7Tn2BcZpgmCIIgAnR0dKCpqQmtnz0bA72lkHHYn89j8n+tR3t7e6ycZwRBEET/o6dszKRJk3DKKadg+fLl6ti4ceNw0UUXYfHixQXlb7jhBjzxxBPYtm2bOjZz5kxs3boVra2tAITDp6OjA08++aQqc+6552LIkCF47LHH/Lru349TTjkFd955J77//e/jpJNOwtKlS2O3lSAIgqgcmsPUB4o8IwiCKINhGDDMCl4Gda0EQRBEPKq1MR0dHYFXNpstOHcul8OmTZswbdq0wPFp06bhxRdfjKxPa2trQflzzjkHr776KvL5fMky4XNec801+NSnPoVPfvKTlV0UgiAIotvQHKa20G6bBEEQZah4p5oKyhIEQRD9m2ptzJgxYwLHb775ZixcuDBwbM+ePXAcp2Dn5hEjRhTsxixpa2uLLG/bNvbs2YNRo0YVLaOfc+XKlXjttdewcePG2G0jCIIgagfNYWoLiWcEQRBlqDSBJiXbJAiCIOJSrY3ZuXNnYFlNKpUq/hkWnBBxzguOlSsfPl7qnDt37lSb1qTT6VLNIQiCIOoEzWFqC4lnBEEQZWCGAVZBGHMlZQmCIIj+TbU2ZvDgwWVz0gwbNgymaRZEme3evbsgckwycuTIyPKWZWHo0KEly8hzbtq0Cbt370Zzc7N633EcPPvss1i2bBmy2SxMkzJTEwRB1BOaw9QWujoEQRBlqChXgPciCIIgiDjU08Ykk0k0Nzdj/fr1gePr16/HlClTIj8zefLkgvLr1q3DxIkTkfASTxcrI8951lln4Xe/+x22bNmiXhMnTsTll1+OLVu2kHBGEATRA9AcprbQ1enj3H///WCMgTGG3/72twXvc87xT//0T2CM4YwzzqjpdzPGCnJvdIdf/vKXuOKKK3DCCScgkUiUXG5AEDXFC3mO+wIZHqKf0FdsTEdHB37wgx/gjDPOwMiRIzFw4ECccMIJWLJkCTKZTE2+gyCKUmcbM2/ePPzsZz/Dvffei23btmHu3LnYsWMHZs6cCQCYP38+rrjiClV+5syZeOeddzBv3jxs27YN9957L1asWIHrr79elZFLMpcsWYI//OEPWLJkCZ5++mnMmTMHADBo0CBMmDAh8GpsbMTQoUMxYcKE7l8zos/TV+wLACxYsAAnn3wyDj/8cKTTaRxzzDH46le/infeeadm30EQkdAcpqbQ1eknDBo0CCtWrCg4vmHDBvz5z3/GoEGDDkKtKmPNmjV46aWXMH78ePx//9//d7CrQ/QjmFGZ4aGQZ6K/cajbmB07dmDp0qU45ZRTcPfdd+OJJ57ApZdeioULF+KCCy5Q+Z4Ioh7U28bMmDEDS5cuxS233IKTTjoJzz77LNauXYujjjoKALBr1y7s2LFDlR87dizWrl2L3/72tzjppJPwve99D7fffjsuueQSVWbKlClYuXIl7rvvPpx44om4//77sWrVKkyaNKk2F4UgPA51+wIAH3zwAT7/+c/jgQcewFNPPYXrr78ev/zlLzFp0iS8//77B7t6RB+G5jC1hXKe9RNmzJiBRx55BHfccUcgP8aKFSswefJkdHR0HMTaxeOee+5R2+dee+212LRp00GuEUEQBAEc+jZm7Nix+Mtf/oLGxkZ17H//7/+NxsZGfPOb38QLL7yAT3ziEwexhgTRPWbNmoVZs2ZFvnf//fcXHDv99NPx2muvlTznpZdeiksvvTR2HaKihwiiHIe6fQGAO+64I/D3GWecgbFjx+L888/H448/ji9/+csHqWYEQVQCSYv9hM9//vMAgMcee0wda29vx+rVq4t22P/4xz8wa9YsfOhDH0IymcQxxxyDBQsWIJvNBsp1dHTg6quvxtChQzFw4ECce+65+OMf/xh5zv/5n//BZZddhuHDhyOVSmHcuHEFBqUYRg8p4XPmzEFjY2OkMZ4xYwZGjBiBfD7fI3Uhegcy2WYlL4LoTxzqNqaxsTEgnEk+9rGPARA7B9YKsjFEGLIxBFGcQ92+FON//a//BQCwrNrFspB9IcKQfaktdHX6CYMHD8all16Ke++9Vx177LHHYBgGZsyYUVA+k8ngzDPPxIMPPoh58+bh//2//4cvfOELuPXWW/GZz3xGleOc46KLLsJDDz2Eb3zjG1izZg0+/vGP47zzzis455tvvomPfvSj+P3vf48f/ehH+OUvf4lPfepTmD17Nv7t3/6tPg2vgi9/+cvo7OzEz3/+88DxDz74AI8//ji+8IUvqIS5RP9AhDKbFbyoayX6F33VxjzzzDMAgI985CNVfT4KsjFEGLIxBFGcvmRfbNtGV1cXNm/ejDlz5uDDH/5woE7dhewLEYbsS22hZZv9iC9/+cs488wz8cYbb+AjH/kI7r33Xnz2s5+NzBXwwAMP4PXXX8fPf/5zfPaznwUAnH322Rg4cCBuuOEGrF+/HmeffTZ+9atf4Te/+Q3+8z//E7Nnz1blkskkFixYEDjnvHnzMGjQIDz//PMq7Prss89GNpvFv//7v2P27NkYMmRIna9CeU488USccsopuO+++/CVr3xFHX/ssceQzWbxpS996SDWjjgYqCSaFZQniP5GX7Mxr7/+Om699VZcfPHFOPHEE6u9LAWQjSHCkI0hiNL0BfvS1taGUaNGqb8nTZqE3/zmNxg4cGC3ro0O2RciDNmX2kJXpx9x+umn49hjj8W9996L3/3ud9i4cWPRcOdnnnkGjY2NBbksrrzySgDAr3/9awDAb37zGwDA5ZdfHih32WWXBf7OZDL49a9/jYsvvhgNDQ2wbVu9zj//fGQyGbz00ku1aGYkjuMEvtN13ZLlv/SlL+HFF1/E9u3b1bH77rsPH/3oR2mXqH6IYRgVvwiiv9GXbMxf/vIXXHDBBRgzZgx+9rOflS1PNoboDmRjCKI0fcG+DBs2DBs3bsTzzz+Pe+65B//4xz9w5plnYteuXSU/R/aF6A5kX2oLXZ1+BGMMX/rSl/Dwww/jrrvuwoc//GFMnTo1suz777+PkSNHgjEWOD58+HBYlqV2hnn//fdhWRaGDh0aKDdy5MiC89m2jZ/85CdIJBKB1/nnnw8A2LNnT62aWsBZZ50V+M5yiTkvv/xypFIplUT3zTffxMaNG8lj00+paJeaCj08BNFX6Cs25p133sGZZ54Jy7Lw61//GocffnjZz5CNIboD2RiCKE1fsC+WZWHixIk49dRT8ZWvfAXPPPMM3nrrLfz7v/97yc+RfSG6A9mX2kJXp59x5ZVXYs+ePbjrrrtKdqJDhw7F3/72N3DOA8d3794N27YxbNgwVc627YJtltva2gJ/DxkyBKZp4sorr8TGjRsjX9IA1YOf/vSnge9auHBhyfJDhgzBpz/9aTz44INwHAf33Xcf0um0SlpK9C/I8BBEPA51G/POO+/gjDPOAOccv/nNb3DEEUfEajfZGKI7kI0hiPIc6vYlzBFHHIHRo0cX3aBAQvaF6A49ZV/uvPNOjB07Ful0Gs3NzXjuuedKlt+wYQOam5uRTqdxzDHH4K677ioos3r1aowfPx6pVArjx4/HmjVrip5v8eLFYIxhzpw5VdU/LmR9+xkf+tCH8M1vfhP/8i//gi9+8YtFy5111lnYv38//s//+T+B4w8++KB6HwDOPPNMAMAjjzwSKPfoo48G/m5oaMCZZ56JzZs348QTT8TEiRMLXmHPTy3553/+58B3HX300WU/86UvfQnvvfce1q5di4cffhgXX3wxDjvssLrVkei9MFbhTjWMulaif3Io25gdO3bgjDPOgOM4eOaZZ3DUUUfFajNANoboHmRjCKI8h7J9ieJPf/oT/vrXv+Kf/umfSpYj+0J0h56wL6tWrcKcOXOwYMECbN68GVOnTsV5552HHTt2RJZ/++23cf7552Pq1KnYvHkzbrzxRsyePRurV69WZVpbWzFjxgy0tLRg69ataGlpwfTp0/Hyyy8XnG/jxo24++67a5qfthi0YUA/pFx4MABcccUVuOOOO/DFL34Rf/nLX3DCCSfg+eefx6JFi3D++efjk5/8JABg2rRpOO200/Ctb30LBw4cwMSJE/HCCy/goYceKjjnf/7nf+ITn/gEpk6din/913/F0UcfjX379uFPf/oT/u///b9qV7NivPPOO9i4cSMA4M9//jMA4L//+78BAEcffTQmTpxY0XUox7Rp03DEEUdg1qxZaGtro3Dnfgwl2ySI+ByKNmb37t0q98yKFSuwe/du7N69W71/xBFHxI5CiwvZGEJCNoYg4nEo2pfXX38dc+fOxaWXXopjjjkGhmHgd7/7HW677TYMHToU119/ffUXpAhkXwhJT9iXH//4x7jqqqvUJhVLly7Fr371KyxfvhyLFy8uKH/XXXfhyCOPxNKlSwEA48aNw6uvvoof/vCHuOSSS9Q5zj77bMyfPx8AMH/+fGzYsAFLly7FY489ps61f/9+XH755bjnnnvw/e9/v+K6VwqJZ0Qk6XQav/nNb7BgwQL8x3/8B/7+97/jQx/6EK6//nrcfPPNqpxhGHjiiScwb9483Hrrrcjlcjj11FOxdu1aHH/88YFzjh8/Hq+99hq+973v4aabbsLu3btx2GGH4bjjjosV7vyb3/ymoPOXu+h88YtfVGv7a4VhGLjiiiuwaNEijBkzRnmqiP4HTWwIorb0Nhvz5ptv4q233gIAfOELXyh4/+abby67VKZSyMYQErIxBFE7ept9GTFiBEaPHo0f/ehH2LVrF2zbxhFHHIELLrgAN954I8aMGVPza0D2hZBUa186OjoCx1OpFFKpVEH5XC6HTZs24dvf/nbg+LRp0/Diiy9GfkdrayumTZsWOHbOOedgxYoVyOfzSCQSaG1txdy5cwvKSMFNcs011+BTn/oUPvnJT/aIeMZ4eEE4QRAEAUAYjqamJvzPv30Vg9LJ2J/bl8nhuJvvRnt7u9rSnCAIgiB0yMYQBEEQ9aC79iVMMSfie++9hw996EN44YUXMGXKFHV80aJFeOCBBwK7vko+/OEP48orr8SNN96ojr344os49dRT8d5772HUqFFIJpO4//77A7vfPvroo/jSl76EbDYLAFi5ciV+8IMfYOPGjUin0zjjjDNw0kknFQhstYQizwiCIAiCIAiCIAiCIAjs3Lkz4JyJijrTCe9uyzkvOFaufPh4qXPu3LkT1113HdatW4d0Ol2ybrWExDOCIIgyMIOBGRWEPBvFjQVBEARB6JCNIQiCIOpBtfZl8ODBsSKbhw0bBtM0C3ap3b17N0aMGBH5mZEjR0aWtyxLbb5RrIw856ZNm7B79240Nzer9x3HwbPPPotly5Yhm83CNM2y9a8USppAEARRhp7a5pkgCILof5CNIQiCIOpBve1LMplEc3Mz1q9fHzi+fv36wDJOncmTJxeUX7duHSZOnIhEIlGyjDznWWedhd/97nfYsmWLek2cOBGXX345tmzZUhfhDKDIM4IgiLJQMmeCIAiiXpCNIQiCIOpBT9iXefPmoaWlBRMnTsTkyZNx9913Y8eOHZg5cyYAsVPmu+++iwcffBAAMHPmTCxbtgzz5s3D1VdfjdbWVqxYsSKwi+Z1112H0047DUuWLMGnP/1pPP7443j66afx/PPPAwAGDRqECRMmBOrR2NiIoUOHFhyvJSSeEQRBlIExo7KQZ0YTG4IgCCIeZGMIgiCIetAT9mXGjBl4//33ccstt2DXrl2YMGEC1q5di6OOOgoAsGvXLuzYsUOVHzt2LNauXYu5c+fijjvuwOjRo3H77bfjkksuUWWmTJmClStX4qabbsJ3vvMdHHvssVi1ahUmTZpUcf1qSSzxzHVdvPfeexg0aFDJxG8EQRC9Ec459u3bh9GjR8OowIBImGnCqCD8l9UpVLivQjaGIIhDGbIxvReyLwRBHOp0x8b0lH2ZNWsWZs2aFfne/fffX3Ds9NNPx2uvvVbynJdeeikuvfTS2HX47W9/G7tstcQSz9577z2MGTOm3nUhCIKoKzt37sQRRxxR8edoSU19IRtDEERfgGxM74PsC0EQfYVqbAzZl9oSSzwbNGgQAOB//ud/1P/jcLD8O/wgfW8p6n0temObe5KD7Uvs79e/u9T1/nEX+/YfwD8dd1xF/ZcOGZ76Iu/LnzwbU+3v6WD3A7Wgp/qSnrhW1C8KDrXnstR9681tOZjP2759+3Ac2ZheSdi+ANU/K735+S/Gwfpd1PJa9TVb0lPPUU9ft97y++iL7e7oho0h+1JbYolnMsx50KBBsbYsVZ+rrk4V05s7VZqg1Jfe0FH35+tfK+p2H7kr/vHW71e7ZIMZFeYLqGLZTn9GtzGDKrAxBeepVYUOEiSc9T0OhWey0nvVG9t0MJ83V/tysjG9j/AcprvPSm98/otBwlnvoy/OjXvbb+JgPjP1uhaO16hqbAzZl9pStw0DKNKqZ+jP16E3dNb9+frXinoLZ7WAvDa9n97QH3SHviScEYLefK2787xx9K62HWw7bNTgYpCN6RkO9rPSk/Snth4K9KY+s1b0xjYd7Oe+mu+v93Uk+1Jb6iKekXAm6I2dSq1gOHTuA9EPCQln3f0tMoNVZnhqMZsi+g19rS/ta+3pa9Ti/vQWAa2vPGtkYw4desuzX4q+8rvoK+3oa/T25/9QQ3/Oo65tt6Nlyb7UlLpFntUL6kh9Dva1OJgCGv2siQJqGGlGEH0R6jd7jt56rQ/2uIEgDhb07BNE9+mttq2vIPspus69l0NOPCMOPvSDJnoNPSSYUb4AgiAOdUg86L2QjSFqBf3OiXrR2+d/fenZD7elO8FgZF9qC4lnREWEf7u0fJM4KFQqmnVTZGOGCWaYFZUnCIIg6kdfGnuQjTm0OBSWbh4sanVd+tLvG+j5Z6bW8zN63g9dyL7UFhLPiNgU6zj7s4DWn9t+UDhYSzMNU7wqKU/0KDSZIQjikIVsDEEQBFEPyL7UFBLPiJrQ0yISTZT7IQczp5lhiFcl5QmCIHoJ9bDPZIdrCNkYgghAzmmCqBFkX2pKXcQzGlD1Peh+EgeVuMJZnQQ2ZppgZgUhzxWUJQiCIPo3ZGMIou/Tk/Pj/iY89rf2VgLZl9pC0iJRM0hg6z+4vPSrppQTxLjrv6o9RzlkyHMlryq48847MXbsWKTTaTQ3N+O5554rWX7Dhg1obm5GOp3GMcccg7vuuqugzOrVqzF+/HikUimMHz8ea9asCby/ePFifPSjH8WgQYMwfPhwXHTRRdi+fXtV9T/Y0ODp4NMT94BFvAjikKaHbAxBEATRzyD7UlMOOfGMBsk9D11zAqhMHKukbMki5QSxnlrKaRgVGp7Ku9ZVq1Zhzpw5WLBgATZv3oypU6fivPPOw44dOyLLv/322zj//PMxdepUbN68GTfeeCNmz56N1atXqzKtra2YMWMGWlpasHXrVrS0tGD69Ol4+eWXVZkNGzbgmmuuwUsvvYT169fDtm1MmzYNBw4cqPw6EUSdKZV7k4iGROVDgB6wMUT/gPpCgiACkH2pKXXLeUZLN/snlKOgb9KdaDKXd2+L5Uh6OP9ZT2zz/OMf/xhXXXUVvvKVrwAAli5dil/96ldYvnw5Fi9eXFD+rrvuwpFHHomlS5cCAMaNG4dXX30VP/zhD3HJJZeoc5x99tmYP38+AGD+/PnYsGEDli5disceewwA8NRTTwXOe99992H48OHYtGkTTjvttIrbcbAh29N/IftDHKr0hI0hCOLg02/HKOXG7Yz6tHpB9qW20NUhStInO3h9mV9PRi8dgtRqGWZV5yh2XypZxlmre8wqDHdmIuS5o6Mj8Mpms5Gnz+Vy2LRpE6ZNmxY4Pm3aNLz44ouRn2ltbS0of8455+DVV19FPp8vWabYOQGgvb0dAHD44YeXuCAE0fPEsUd90mYRBfQ5kbRKG0McPPrcM0gQJajqea9kDE5zsvpB9qWmkHhG1JxeO3kp1Sn3YKfdE/nConICHez7UpO2Haz7V2W+gDFjxqCpqUm9oiLIAGDPnj1wHAcjRowIHB8xYgTa2toiP9PW1hZZ3rZt7Nmzp2SZYufknGPevHn4xCc+gQkTJpS/Lr0UmtT0bw52X9efoN9ajeiBnDSUU5MgiB6hu+NxEtBqC+U8qyl1Fc/qNaiigTEBVPB8VdqJ10mEqUe+sChK/T4q+e3UPPF/d89ZbSTaQWTnzp1ob29XL7l8shiMBe8Q57zgWLny4eOVnPPaa6/F66+/rpZ0EkRvoVK7391xQl02P4lDf4mM7g9t7EVQTk2iJ6j1/Izme72LWCaxVv15BechJw7Rk9Qt5xlx6NMnjFZ3PR81WIPfk/nCKlnW1C0dK2a5qPrEalPcEO9i3xt6L/x3pVSbL2Dw4MEYPHhw2fLDhg2DaZoFEWG7d+8uiByTjBw5MrK8ZVkYOnRoyTJR5/z617+OJ554As8++yyOOOKIsnXu7fTbvCJE1UT11eFjNc/fCMRbiq5zqOaGibsj8qHavm5Q75w0lFOzPvRWO0P5H3s3vfW56Ta1doTUaB52qFHrcQflPKsth+zV6ZOdTh+i0ginuixfrEUn3s1z9GS+sFpGZpT6zkqaxIuUr/i6hO9DkfvCuNttoSySOoc8J5NJNDc3Y/369YHj69evx5QpUyI/M3ny5ILy69atw8SJE5FIJEqW0c/JOce1116LX/ziF3jmmWcwduzYiupeS2iwT9SSuH1iJTan5tFo1fRXh1qkVjXR3zWgJ1Ik1IwqbUycvJqUU5MgiEOWQ83edYNitqnb9oqWbdaUuotnvW18QvQeyg1guzXArWVnW+W5ajk4L3euasXkSj5XTAiL+9mq348hnJUUzVy3+zejB7Z5njdvHn72s5/h3nvvxbZt2zB37lzs2LEDM2fOBCC8+ldccYUqP3PmTLzzzjuYN28etm3bhnvvvRcrVqzA9ddfr8pcd911WLduHZYsWYI//OEPWLJkCZ5++mnMmTNHlbnmmmvw8MMP49FHH8WgQYPQ1taGtrY2dHV1VX+9eglkf4Icqtejns6yarqGmogvtcgJ00smFRX137FPWn37Kk2R0Cuo0sbEyatJOTUJonJ6W97g3kLRLrNcZHG5V8kvLf5+b+nCu0McW9QtW9UDc5j+xCG9bJPCkuMjf3R1WXJSBZV0ApUuXazLhKIXhA5XfB16iGL3MlzXcJh6Ve2JuxzTre0zwEwTzIzviamkrGTGjBl4//33ccstt2DXrl2YMGEC1q5di6OOOgoAsGvXrkB+mrFjx2Lt2rWYO3cu7rjjDowePRq33367WlIDAFOmTMHKlStx00034Tvf+Q6OPfZYrFq1CpMmTVJlli9fDgA444wzAvW57777cOWVV1bcDqJyeqUt6yPL6Epd2+4KJ1X3ybV27vRAeoGDZnsqbF+1YujBtq3V2pidO3cGUgOkUqnin+klOTWff/75ot95KNJnl+D1Y4rdz1ra6oP93PRIn19NxPEhPN7orfTEHKY/cUiLZ0AvnXRUQx1ympQL/az3YLHYvTlog9tynXgNO+xybSz29sEwpHF+Q+H343r1geB9izVY0O9Tsf+jiHBWTDTr7mTVMCrzxFTptZk1axZmzZoV+d79999fcOz000/Ha6+9VvKcl156KS699NKi78sJUV/lYA9QDzmifit9REjrFfQy506ly1UPuiOrBD2ZX7TmVGlj4uTVpJya/ZM+Mz/qYcp1A33hulY7hq+I7kQdA4U2rRcEMQDln49Kn40eiX7uoTlMf4GuzsGmWLhqN3ehqqRjPFSIVd9S1zLOZ+MmNK6ScksfeZkyUdegZEceMzS6nrtwhsORa/HYFQhnrhstnNVqaRPlCyB6EXXJ4VRJP1klvSn/VLm8juFXNecpPHHvWGYpqdaR1S2qWb4T47r11DNUt6VcdbQxlFOz/hxiw2miCHF/zz2us9dwF+ZqxvAF1Sn3oUqWcFZzjpjUerxRyaZscYhTl+6kzFHQHKam9Ih4Vm+jckhGFVTS+VUoeFS6JLKSc1e9Xr3M90VNVmoStVZtMuYaG6ZqOr9u/W5KtaHC9un1CLeNc17yhSKf5RHHIusZ8f+AcBYlmtVgcBGGGWbFL6L30FcmNnXLE1npb6XC8nXN5xHj9x53jFCqn67UqVEV3bCt1dyTaqlqMgVU1pZw2RKfrXRcUc19LCWU1WIMWm8bQzk1iUOVnprjHYy5ZCwRqkZjeaD6fr+sDY+Ts6yaYJEy5y3Vl9d6rFTJ8xGnbJwVSrUaWvTUHObOO+/E2LFjkU6n0dzcjOeee65k+Q0bNqC5uRnpdBrHHHMM7rrrroIyq1evxvjx45FKpTB+/HisWbMm8P7ixYvx0Y9+FIMGDcLw4cNx0UUXYfv27VXVPy59JvKs3gmFq1Gtazph66Y4VZPPVJHQMU54a5xILJ2iUUwVDLa7m6Ay8vu7Uaamn61ysqXfq3Jh2lHiWJxy3Z3YFwhn4XLdiTwsBTP8sOc4r14QWk70LWrhFKkpdbBJFUdxRfX5Vf7eY5vBCsoGP1hmAhTXU99LKHuvKrHHcc8R92Mof49qOTEBajAGrbONmTFjBpYuXYpbbrkFJ510Ep599tlYOTV/+9vf4qSTTsL3vve9ojk177vvPpx44om4//77I3Nqtre344wzzsCoUaPUa9WqVd28YARRSE9GM9ddbOuOswGVO40qibhGqfe70/fXIBqt0jFHnPLV3OvuPB81f3x7YA6zatUqzJkzBwsWLMDmzZsxdepUnHfeeQG7ovP222/j/PPPx9SpU7F582bceOONmD17NlavXq3KtLa2YsaMGWhpacHWrVvR0tKC6dOn4+WXX1ZlNmzYgGuuuQYvvfQS1q9fD9u2MW3aNBw4cKDy6xQTxmPMgDs6OtDU1IS2trayuRWKflFVn6qcWj5wcX+ApYSGyLe6MwAOPdClooKiyhStE4LtKChTSZ1j1rHSe1VM3FH/LRKpFPl30S+J6DDCx7S/Swl7Ue8XKycp9iyFD9fsXqmTiDZF3atwlFhBRFmZU+tXTyYSlvWXdTdYxH0M/auEsyjRLEzoWEfHPgw/+ji0t7dX1IfJvu/93/4cgwc2xP/c/k4MPWN6xd/XX6mFjYlDb45SLtUfVjsRiOpPKuov5HulBlJV2KRyhOtddR/XDVsUrnu5a1myzlUuRSxJsXsScbycnSpVVp22yPGitqiUPS52TJ2ouM0tZavCx+NSyr5GvR9FR0cHRowcSTamF9JT9gXofTamp6Ou69X+cu2oRfL7qvZ+qeIzsb+7G2N5dQrt/1VHDKMb86+wk75Em3iMfj/8/0psWznizr8qpZJ5YKnq7+vowOhRldmYnrQvkyZNwimnnKI2IQOAcePG4aKLLorcBfqGG27AE088gW3btqljM2fOxNatW9Ha2gpAOH06Ojrw5JNPqjLnnnsuhgwZgsceeyyyHn//+98xfPhwbNiwAaeddlqsuldKnwuPqEXHXanXoiY5T2q4nl2dEtE/xIo9rpWGDFcRsRUnF07sSVmtvR7d8H6HiRtKHFeEK/zSEpEYMZ6vitbqIyiccR58RZWTwlvsqIVSwpnejhJtY5yDdTcpPm3zTByCVB3pGf4d1cguVePdjqxfDcsWs0VhyvXLPZ77LOY5ahVRV7NIrbhR3+XEN49SwlncHHs9LTBEQjaGqAPdmRPVLYKrhvOduHUsV6ba61Q3sbTaa1LB5yoyWdr/q1mtxLhbUjiLLBPTBlRcrx4m6hmpRDirye+wSvvS0dEReGWz2cjT53I5bNq0CdOmTQscnzZtGl588cXIz7S2thaUP+ecc/Dqq68in8+XLFPsnADQ3t4OADj88MNLXJDu0WPWtyef6e4ai578XEnD0Q0hJ051woPhWG2IO7GqYElPJblwujPRkh1z1KvU5+ISZxBf7fliUemzEhaf4nyFFL/U34ViGYq8FxbQgOB1KhrxEBbOokSzQHkeeBGEpG5PQymROuZvq5oclnFEqJo7GmL29ZUIFLFtUVznQKnPFKGSSVitbFLgWDXPTxW2K+q+VDuJijpfyfpERCGUtMVVTgTjjiuqcs7JetVo8k/0LQ71EUeUwFyzpZCVznfKUPcxdQ2oNu1PJN0YXxScKkY94/SRBc2JmFNE5imOekWVr8B+F1SlxCvuOWJRI8dimG6PNWrAmDFj0NTUpF5REWQAsGfPHjiOU7DT8ogRIwp2ZJa0tbVFlrdtG3v27ClZptg5OeeYN28ePvGJT2DChAmx2lgNVt3O3A9xeZnQ4GoHhtwtu/64nHATRq8nR5kNAqL+X6q8rKv3//C2zvL/gTqXEDkYY6qswcrUN1wXlA4TVt+hleGh+pf8fwwKJitlBB25tFF+Vt6r2O2WdSz1t15/2Z4i90vWQ/1ffkyKYmXaYzDml2VCseecwwWL/3vRhbPQe0UFskCbuzm5oW2eiTCVDJZk2RrlwiuqLXn/VuVAKifEdGPb+NiOanSjj4t6L6YtKvh4GXsERPfNkWOAUja0kjGAqkD196HgtEWOl1uyWuw+xR1LlLLJ8r0COxz+N0bdq72PgXIl2hHreFzIxhAHmUoiuOIsgwxQw/lOdyg7T6vxd5U6XtU8rNT7EXOUqPF8RY4CIN78S/bN4cNh53fJL/LKGAYYd4vbgDLEGW8UGyvFfj4qHC9F3YdwXfQ61Jwq7cvOnTsDyzZTqVTJj+nzV0DY4PCxcuXDxys557XXXovXX38dzz//fMl6dpc+K56VelCLUYsHtuofXtQPMUrgCP8/fNpQXUrVE6jAiMQV0Sro4NTp9ITy2nEj9L4U0WSdS05UooSzcp2311lEdtrh/5dqj/b/uAP5wOdDnUepZ6psnoRyA/0SxslgxesfFs5KtUyWkSKaFNDEe/73ROYUimqLurdhVbJ+3n9mmmBm/N1nKilLHIJU+6xVKKJVu7wsPDCsyLETRxyoQMCJa5NiCVFx61thn6286CXyOZazRxVT5DoXiEfFPldsfFCCSp1sUe9FCU2VXIdYNtmbPAHedaimfWXsbVX3kWwMcQgSZz5UTRRXVfOHuOUrCBgo+Lj3b6lo7qrrHlGvcg7nYtRcyKtwvhLXsR+ei8Sy0SiRbqUUzBDlwwJaoKLxxhtxqMrhGDeQBKhYCK402CIu1dqXwYMHx8p5NmzYMJimWRARtnv37oLIMcnIkSMjy1uWhaFDh5YsE3XOr3/963jiiSfw7LPP4ogjjihb5+7Qp11XNe2TSrzCdHu5nX68ivDVcB3kjof6K1wuKhqsXF0jl1qEI4OK1Dk8WXFRGBvkho7r+bLiRA8ETxYhVobrpoUOB9oV47pHhTDrbQwLhKVeqoqV3Keo686DbYlcHqNfhzjtDLSPq99AqZBvvwwXn+HiPLGNQoThVcKZrH+4PVGv7lBRrgDvRfRKuj0UqcUEuoaT8FJLQqLaWrL9oXqVXU5Xrn8P1VF9vIQtKktU3xVVJqYt0uuk6oLK7BEQw4YWseXFli1WvKSxgmeqO57uipaRFHNmhZbpFH5J0BYXnA/B8V1gPKA9V5XY2aLLkCK+m2wMUYraTHd7jmoDB2J9rruOprjFtVepYxV9f1QdavFb16ho2bv+/cX6nirqFrbFpd6P6idLNqFUupWoNoQEtzjzsFIRfnGWo4bbUPKeVCMEexSL1o763nL3pGLqbF+SySSam5uxfv36wPH169djypQpkZ+ZPHlyQfl169Zh4sSJSCQSJcvo5+Sc49prr8UvfvELPPPMMxg7dmxFda+GHo080z2VPUXcCLQ4Xoxy78dexlCkM4hayqC8rkDZpQvhwXu5zhAIel2L3p9iA+AidQ98awnVPSp/VjH05X7SCyJFm6JRWWGvRzFDGK5vVOhw+LqX8HwA0fcg/O3h9sooVFku3N6ylLtP+mSllGefu2DMKLGkyRfOAL2t0eVVe1TEIIcBJu4p537Dy7VNF85KDRbi3OdKqdSY0MSmb1LDQXNUHxI3tL+SqGLZrxf0lUXEpWJL6gqWtlfYFwLRNqmULQrUOUa9AuctUsfwNQ5fy3L2iDFRRu+fo+xQWXtarO5hUUlbahG5pBGIFQkY9cxUm1KgmjQKsexx4AOhCAR5rAhRqQWA6Pso7yFQ/D4WHVvUsg/QIRtDHAS6u+Km4uipUr+fEv2Y3m9XE5Ed7qdiRzYXPWFxuxenft1Z9l62PjFsc5w5ik5F/WQMZ1t45QhnrPBzLoov4YwgtgCmlYm9dLYYxe5LhZHhxcZJbpEyFdMD9mXevHloaWnBxIkTMXnyZNx9993YsWMHZs6cCQCYP38+3n33XTz44IMAxM6ay5Ytw7x583D11VejtbUVK1asCOyied111+G0007DkiVL8OlPfxqPP/44nn766cCyzGuuuQaPPvooHn/8cQwaNEhFqjU1NWHAgAEVtyMOfXbZpk41SzgllXxO/+FVvYyhyHtxly5ERXQVo9Tgv6AT0SdWpTzGUR0dUFSM0ScqxXJn6cv9ACG4lMyXpV/PErmyCggLleHQ4RgTMaD0PSg1N9HfiztBK2hTlIc/XFYXCKHZ2/CBGLi88N4ViMlc3ENdRHM5hxljWSrcoFcqIJyVu6fd9MbpMMMAqyBfQCVliZ7nYDhyIonRpwOlB4VhwSPOcu/IeqCyJXVRonuxLeTjDNAN1V+y8qIMd8vWNdCvhfv2IoSFs7BNCudvlPXW++e4lzx2SoEIMa1sXrASRE1ASy1XVV+rlYt6xmLZp0rssV4ZIzieEJUobKfIpSn/r50ifB+9uxRlZ0tSqdOmAsjG9C16jY3xqHQeVKxst5a3xRGAgIrGoMXG35KKxZFKfsdl5gJR9YuqS6VCfXjOWE5YirVsV56rnLMhZKeLXc+CuaI+hg8dAzMCxzk0e6YLaF7ZcsQdI+llSzkb4zjCir5X4bLNOHP3augJ+zJjxgy8//77uOWWW7Br1y5MmDABa9euxVFHHQUA2LVrF3bs2KHKjx07FmvXrsXcuXNxxx13YPTo0bj99ttxySWXqDJTpkzBypUrcdNNN+E73/kOjj32WKxatQqTJk1SZZYvXw4AOOOMMwL1ue+++3DllVdW3I449AvxDKhOQIsUe4qcJHZS92Je/riCVMQPslhHUc6DHh78y/qXzD9VzmusVS9uR6eLL9HXXBw1wPzBriaguRww4+Tzieq89WJR3ZXenjKTxSjKTcaKIQf3xe5R0WhGhJ6pqMFLuN5h46ROFIpcgH+v5HNSLAot0BYGOJyDaSKaweDnP/Oiz8L3MTxAUMJZXPEs8NlumiJWodeGUVRAn6OaZ6jMZF+VqSIHWrEoodiRXCFi5Scplo+kSBuiooFKDc6liFHUOcJDfVoxe6Q5COLYorDoUqyfDudv1O1RURtaJJFyQRui2hGmmOMjZnR0uJ8uJ2rqgcH6+4b2jMnzljLBBWOHMrZYnFO/z5oNjkDYIm3yVcShA8jfgT+mABCZhzPKeRj+O7zMtluQjSF6AeVGqAXOUXRDaChmH2OOs0s5lcLHa7LxVrm6VkjsukQ5t/TqRAVZFKlXlFOrlMNBdxjpdlqUgZqDFRXSijm/tXYFGyOEtHD/H0h2H1OYijNGUl8RMTaqSU660P2oJOCilqs2e8q+zJo1C7NmzYp87/777y84dvrpp+O1114rec5LL70Ul156adH3a7q8NSb9RjwDKhPQynkzwkQtYyjnTYi1jCE8CShhRMITlXKCTWDgWGzC4nV8xQa/RZH9Rbgt8rTeZCUsnBUTX2Q7ZJ3DvTQHwPRlh0UHuxFeD1k9LQVggZCm2/KI9pRLrl9MHNQ/o1/7coJhJHq7QxFbQLjtjgiTljhex65NNBmk8BX6GhQKZ/JvANAlOAMMjjcBkxFnBoSAxuDdzygBNGRgA8JZMQG6njBW2QAp7lJb4qBR0SC6ohMXeR6jBB79vTIe7MCpyogeepRQyUiucmJUDZwkel8PRNujKGdBKedIpCOnhP0M2yLGjOi+Te8ii7YoXv9cakJR1vYXa0t4shkeG1QSgRZzoB4+XkroLBt9p8YT3P9bf082Br49Vra4iIAWmEAjaJdkncLoYwoAgTQCRaPPtOcsMmqwu+vfyMb0OXrcxgBVCzlA/PmRLFuVCFXOPlbgnJaUciqVWmpe9dLNuOJNkTrKesgyZesCRPc5gRPGiwgP16Wc00g/Fp4zFvSVJRxFJVeNRNSRwQgu5dTnlDGX75cjHG1cKoWROlasjcVstnyvG/ZYzR+7IxKRfakp/Uo8A4pPTMLih06cvCDFdiMBon+IkdFB5QxikUlAuG5RolR0uwoHji5Y+SiuYoNfZgRFmdBgt+RpERRgdArEFxm9VExMUuG+RcSjYgN2vWx44O61JypCq9SS1FLiYLitsp0SXWzSJ2hlOzVNOItss6w3R/F7VmRw4Hfi4v+OFNHgH4fWTs7EfxgAzkU7ODhMMHCvfYwxSB9HwW9F/21wvV0xfjNR56kWZlRoeGhJzaFAzSc3lTxnFU4WKlkOrkQoFAocUbBwf1kkMqiYU6HYgDZKOIvqKx3OhVhfyjkSVfFiUUxh50CRekadM47w4k94CvvnostZykWgyfaEP6bsR5HPamMDUQ6BZypsn0pFA5aK1pKUEjqLCoVhmxTu13UKPPWG1vbo6HvZlrBDR2+bjj6mkO2ONQ4KO6hkffV/q4VsDFGOOM9YCWEnPP+JGxQQpuodd8MOa+3vyDyOJZwB4T46bpRRLMGslDMjyukV03ZHHYuTosD/UIn7X0FEOODbZvH/8vNFUefoOWPgeobtXNj5rbepmAilOVB4lSJOXKGpnIBWzXMdOF5BlGIpe9xtyL7UlB4Xz+rmiamASiLQ4iyT0d+LnXQWKIgOilzCUGwSUNLTHz0B0E8vTxmIBmKAiSLXJmrwW2TQGxjsugCYbwzD3v6oJYBR6BFMUnwpteSPMxnfhGixLI6nHxEiWjhCSxUs74EKRGcVdfN77dVyhHGIJY+mETT+kcscS92nYp4RZoBxgKtYbKh7xphR8Ht1tedLCmfi3gUNMuC3lzEGzrjMNCMa6jKYhijDwQuNpBvRDjl5kW1VX1RkcKNXuhtwZpT8zUWVJ/oZ1U6ey3iJ9Se3mHcy4B0O5eWKK6Dp9SkmukvBPfgrj657QSL+IqKGboukiMYYKy1ixHAQMBQ6BkSHU3xyGaxvsJ7quGx1SHgJmGmtjzaY5+QqF3FRahwQcZzr9o0ZqmIB21TlxKlUtFZY6CwZuR7VBt0R4tU/MoePLqAZlmivdCBGOA1FvaPHE1FCYLGoaFmgpMWIiOzurnhGNqZvUrM5T6XPV8x+ThXX/l/NSpuy9Qg4sqOcBCEhLUa0Ttzl53okdtyl5uE6FxyLIU7qdQzXs6SYFyUylkuZIyORK8wPFidSVxLlbJCO78j0OcWun7J1wf4zqk8Lzyej7Fup4BdJxIjF/+qYAlpVv+UYAnCwLsWdWN2B7Ett6XeRZ5JKBDSgcNJSjLjJFEsmQi+oa2gSUMT7GhgQo4xYw4P5p2RnyFEoRrHAIDd68Bue/DFmKDFGdeQlvSDFO3B9UlVqyV+pjq2iRPN6XTVBsFxUXVQHXmowX8qrF5UjzPF68Mjos1KenmLGCtokhRlCL2Mc3ACYY4ObFsBdGMwoWN4Er/4uOBzXf/akiCZxIJfxcC9/DgMMcacYOBj3/mVMCIRFvP0B4cyxg+0sKB8SnGthBMhr02epyeSmXF9SjCKiWSn7JH9nQLQ4IPtKQPMQ65+vVHSPqp8uoumCRlhAQ7Bvj3SSyAkQY0rI0Ps69Vv2zh/MMxXRr4fqrA++9UlFqaizcBR0VP4X2S9zFEYulUw870WCRU8g9esS3R6/LNTfkfnB9PNWYHeL+XWkDVD3SLfJIUdW5NhHv09hRwgQcIYww+9vuWEBri0ENNcGYAnnTuh6+L+Jwmcusu0IjikM37QXtbGRvxX1nhP9RXEhG0MUozvCbAW2BYgXMFBqpU3ge8Of04/F3ZAGKPmsx1l+XioSO06y/shxc1gUCfdHReoZ/rvkMnG9z9HrFL62+rVyEcwPVoSwbQZKzxkNFu1sENnJIpxcMqVAxFwk0O/rzfCuYfjaFUQfS3hhZHWxtpajWIRiWEAThYo7w4pGUxaJeNeDDiKGRbWD7EtN6bfiGRBfQKskiV9ZT3+ElzVSkFJvRixfCHlf9Wgu2RnGGUDKiCbORASQXEIXKWJEDHwDbfH+1dVq5kIIaI4NZlrgXkenTqkNdotNWoCIZZsRS/7UR0p5+MPt0P8tVjYsCoa9ILDF4F5+N6LzhIl2ls4Ppr6We/FZzL9HMFhgUsmKDVrCk1/XLjpBEVWWCqch2uFNKrnhTTS9doefMQ4xeXJc8S/n4t+o+yfvHQODaXAZqgCpfBqmNyGVQmiRtjDXLoxGk/fGb5D4R0YMRm1/XQ2MFU6mypUn+gfFnq84z50+OSgyCAf8AVbwWPnlhcWWo0X1G6VE94K66ssquAsVrRox4JL1dNzSDhJXdixeX8e9roJxUdeSNklrgyIwWA3ZTu0ay74tctIVcc3FZ7wdoBGceHHVvQVztqnoM72+pYgaC+jHwnZJqHGFzh2tbDHvvD4+KBatJWHeG7rQqT9j0i6pOmr3piCCONyf67ieA0TaI/m8GZafUiCEHg2t26IoGyvaygpsLLyodvnjUEKzbj+LiczdtTNkY/oskeJS7A/XYPwSYVMCb3v/1m2ljT5XiLMhjfff8FLOUilSRL1kPfxS5SKxy4l+xaLk4uSY1CPjou5iRfJEyCYU7E4J+HXR+395XKtTICezOibfL59aQdq98Bwsclyh110XzoqKp5qIxgzAm1+JJZwomP9Wg4vi177UhkvlKJzbR0TTRcypAnUrMa7rVk9A9qWm9G3xrMhAWkcX0MKDy7i5QXTiePpV3eQAzLUDHWPkzimqvqHJSkSnLQeQek4q0YZgneXgEUWW0IU7jAIhI6IDlB5jGdEkhRgRTizbUFhXvQOXywDVORlg8FCHzb1JoFffqn7ooesdWUSW04y4HlUXXuJYrEMs8IprbSzIqReRJwwuVyKn3mbxbBmFomFYOPOitQoiHF3Dn6RwVwmB6ncRitAQ98mfmIQFNNtrTHjwYhoMBuPi8WHeNxhe/joOFZkm72NAlOW+9425TsHEvtROQwVJRwkigm5NbiJPGD3gjiwaYxAuceH37eFBb1RZQPQVcpl7wbL88GDc6zdKeYf1/h2GpZwkUmyXIhH3+gRZz1LOA9nPSQcB9yJUTc+zXWCPosSYCLEpEFkrhRfNdoajagN2qMgkQnTHou8C/AmjfqzgXoQFNO+7mAHvphaZUJb7f+iZUSKad14lFMqyBfXSJlGheySPBdrOZN19x5ueSsHx7lKk2CnrrQlnUf25rCtjhmgLd4XzTbWXBRw7IgJatqfQPoWXdqllW14aAc6FXVWOqSKR0Ex/1qLq7doRDSaIHiJKiAq/H1NwiLPSRopQushQNvosbp5AT0QLOwACp/JEIH1uFrXcLZzwPjw/iyQ8N4gQetSUoEj91KlCdVSfZwjkySzYMEsb9wbmi6E6irG7Z0dRyT0uHWhRru/XnVwl52BhG11i5QhnTKXGgeHNQ1xbCWje5AFq9VWdBDSg8NlWxxE9Viy1E2qcHHRA+XtA9B4OinhW84lK4ORFOuMShiV+BFpprywQ09Ovh7Nqwlk4QogBIvy2yGSl4Lvhd4a6cOZIQxgh0nhShej8vIigwMBR/6FHCWfhDtDzGDNTRDEJPcQT/FT7wlEVwcFuQV15sK6qww49RJEiZRSBCWORyYn+vjdBUfcAKLrEUeYIY4ypnk91hEBAOHOk0BT6ToeLKxTOE6bfI3hhWlZgi07tGuuGKjzY19rNpThmWIBrAIYLmK7/vDoiso7BCNw1IZr5wpnLgbzjIu9wdQ8lJmMwDCBhGKK+hpztiPgwBhF9FnmfNPGPuU70xF4KZdpgIhABWZl/LxrDiBUKHyhPHFLUzC6F+pFyu8Gq32L4uCfGx1kKHinycH/ZN+ALUQWDXL3fkL8vLSqo8MSG7yQBRN/BWcBBEkaKaI4b7TjQhQzp0Ilc0h01IeRFvNlG0CGglPuIqD5pO2Vdi0VsF9ssIIw+KSqZvqEYEXYp8jnioQgIzT5x5gKwVHS0fJ44gpF2+nNUbAMY//vE4xOO2ioqdkaNH3RHiGv74wm93cwf94h8Z1ARaIwZ4BHPmni+RBtUNLRbuBwGnEOaZ2ljA85DabrDk1k53pG2NBTVzbornpGN6dPUbd4TZ85TsIICMshX9XFhkTlK8NHRNwwpuUxdF4FkHVSfFp6UxNvopeArIuxgOLJZHCucn1W0o7N604uQK/EbjFqOFxby9Kaqd/T+vtRGK2E8ES2Qb9osDKyQbwXyFofHFBH9fzjqrJyzIezgCsxHwk58+R0c3rjCVeqWGBfIa+JHn6n8pd0U0KKQZwznQosk6l5EPRtl6lrwU+Ch56W7kWBkX2pK34k8KzNBKVpeLT8pNCZAYecHr1yUMhypzPOIROjy+wOezOglDKrT0MQoOVnRva963eRERfe8SkFDnjo8eDS9wS/jDI7LYRiAy/1dEAvq7djRy02lx5hzcMP0jIwNMObnm9FwI+oaDnEWTnQhgXBA5TyT0WeqXHf6lhIeJsBVE7GiUReOre6N4d0nzkXOL9FOT9RE4aBeXIPglWEQkVrM85JxA+oe6SKnwzWPn2H4UQyBZys0SdHFQ0DdI25YYNxbXutdaxgm4NowzKR/qWS9Ie5Z3uHIu673L4frBgU0kzEkTIY840iYDGnL9J410RwTTE1g9asgny9frLWDfwd+K/4yH9mucgOuSqBkm0TZJeFRH9HLl9opS55e/46IZyhK8NAHvYHzaeJGeJArIoM0gVkf5IajgoCQqMGV3ZG/s1IOEj0aSM+RCPiDV99hIMf7niE2GAyvfyywR2HhTI+uBbQ62n7eLK+eUbZIr2vUNWV+lUrCeVCf9CPTSkSfOdHCWGAiVeRZCiy9D0fa6TnCvLxh0rnDwFWHW2oDmCjUUnwm+vewY0c9Y1Ftko4QJ6f6cuY64E4oZ5hl+RstcMt/zpgh7JJ2PXw76o1/uC+kuer5EWW91ZnKxppiVujbVU+0FQKaPrvVJn1SOHM04S9c/wohG9P3qamAVokt0ucJJSbxer8cJfhI5FLIcPRUMSEqUA993BYloFSx0Uu5vI3yW6JSGcjfeNF7E2UD1XxBE/e0uulfXyzoIirpfuA7w2J9MeEsdD/V8kagcHkpEBkRrjtNivX9ujNfRuuGnQ0y7Uo4KCEQtRs1bwx8UTDqGF5ggmqr5wCLalsUFSoD6jNV967SNst/wzugFvk+AIFxR1gMlg6haiH7Ulv6hnhWqXAW9VltIlCMsjtDcqikioFlAHEmK1FilIwK0sSowGTFsf0yoXrKiKDwAFKva9TgUUYBKSFDen/1iYrrLzcNeI3lqbXcWXBtv76uCxguDG+w73K/o3bhi3zymDofE2IZwMCYl5NEa6sZGuCK61vimSg2CS61m40MJYYbjLqQz42BwLIlFVzMWEEklhSKHNcf1EtjBkgDzgO5wlxwuN5AJXCvpMgZ6ujkwJ65DpiT8+6VN0FxHXDXH+gzwwQME8yyADOpJipgBphrFVwP17s/IocRkHddZG0XGdstEM/kvUm4DAnDgOtZ9bQlFpAZABzmL8GNvBfaRJ45OXHMtkU79IoZJphpiogLOXnsTt8QuKBGSQMdWZ445Oj25EYXposJZ8WeSTkQ1yY5utCiD7CiIoUKxDMA+rJvObMxDAQdOmEnjhQ39D5fb6Ic3IYigqIcJPqkgXt9Bue+TRLvcTAwOABMg4M7oj9V9WXewB0ITgD1uoftJxBc9gfPLsnBt6qfdxpdeEGhBx4QqQP0yxbHE6x7ruV5wwJaYGIDFyx8H/VJRlQ0oBTe9IhAz8YForZqsAGM3/bijh1mcM+pw6LHD1J8cm3hELFtuHZOtE3aJcMEbAPMSgKWq8ZB3PGW8EQ9l9IueeOevGdfhZ2Vl4qrXasZuJfr1RtVGfCdh6a/23XgehcTmLkL5uQLL1QlkI0hwlQ6filWPsIpU2y+ExbOospE7birC0AFdrSSCCo194ne6KWguFbBcnMzTW6PztPoEY76EsekLSsu4gXrxQMRXrqtiUq6DyaWu+v9ZMHy8CL2WL9uwflNhNAI35kl54dc6zeD9987ixhM+M4SL1pXOhvE+N0XIlV99Je00aXSQnh1V2l/DAvcdX0HGHdF9Bn35K0iwlmpfH3dRd6jwDHdPteAYpHv3WoX2Zea0jfEsziUMiryfe//cZbKFF0KCW+ZXUQOsYK6hCcr4eTuWh3FADTOZMUbpCsxKpg8Vy/LPIHGZb44A7XMxBOlwvMD2aHrg1/ue5K4Vyc5sYLDIqISQtfVm1TJ/Fm6l1hcU6h8KgaXkyh490oOhLWqVnCvOaKFtoLcAkx4wwD4EQ28MEeYimA0LOXdN5gfgSa84oV5wqSx10UnmSvMM62+hwdQA30RuYVgR6cNUqRwhnxOTFDsvBDOtE6eG4YQ0KwEjBQHTK+9ri0iF9wE5ERe3i8xAebIO75wlvXEs7zjFoTMJ1wDCYMjpa03HmCZcJh336WIyrVnzjOw+vPGXAc8n/PboE+2kAeshPi/ZfnPWy2MABmefkNJAa2K6LPYeV4AwPFyKprBTUjUqWQEFw8uTysWsRuO5JKiQIGnW9ZLX07niRwFA0UpnHEXDElfaNccOoaZ9EQjHhA1OA+KGoC83t5SOtcTYrRRu8mDEyS9vrqTIDy5KBiAMy9SO9R6znnI4VQoRhbTyXwHh2aLQmX15NRRAppYmgioqOGwQCgJR6YXjBM4ItMmAIFxg1q+yTW7BGmbZN/u3zOg8PqXcuxIu+SGf0jahJC5NpiTF315LiP6czskPhkmYOXBkmkgiUCUNFyrQKTVnXH6uCfvuOL/2uUyPMEvAQOGnAgqhxXThF15aUMCc8SyU+bk0C3IxvQLStqXMMXsTdxj4fciRLRilNutVt9x12RM9XORyx+9OkRGHUUJKPK/ERu9FHMoSTFI/7ugrp6I5o3kA8JVATy6vw0sLfQqoTu9pHPZhZe/E4Wb5egbtflt1b5X619UnxMl0IRXxMjrrCXXlxv5yHrJ6yPng8U2/gL8fl9eHimcyYALxpkSKwNBDK5+z732hAMuiolngJdX2osW9+qr7Dd3odJEmL4QHEWckVrU2EJFVJb8YKj+xYQzbwlnYK5eJHpS1kfXGgAxHjG8EJKqIftSUw6aeFaRAenWF5X5+YSMir6bSzj5uy6c6Z7p8CDbQHBXQblURtcF1A/P6ySVyOF11nIJg4ikYf7yBSA4WVGeaCOynrJjlAKNPohUA0hTLgcVzuO8C+Hp5zImQKuzCiW2ASenIoBcT8SQUUywQpMYozCCiXsvcQ2DHbjeSXAOlU9FDwmXy0YY83Y19SZ7Aa+RPAdjovMuJqB57Y+MENE7SabtbMZd5YUQS4F8cRPMgMkM4WVCMALNhZg8SuEs77pwXflc+S03GAvlC4OX40yUYZC5BwDTZFDRfrL9UtyUExQ7B57NCMEpZIC5YYIlknBdF8aARtVWacDCz5icXGU84awz75QRzzgSBgu00fSea5MBrhFhGEITFjXZsvPgMlJBkfeWrTqAlRDXIREyWN2BDE+/orv2qcATGZooFAggnjgvnSx6HkW5DDy8221UJJceJyAjizmHiuQyXN8xouyR/hsLOUYY5+D5kChgmGCmAxim53wQwwgVkVbEQRIlagDibxWhahpwGYfLfDHDcUWfrwvrwVwqPOh4kteY+RHCIvLM8O2rVzdhf2R/FszhGGgyAFeLejYYKxDO5Nwh6pcvJ5bisyEBzVteybwwLhV9FiGild3hUdkA7oloIdvkiuthGhZcz276dsmPVPcdWcH7JJHOHctAgWNH2iWTo3D84Hg2ycn5wlkuo/r1QFOshHKOMABIecKZYwoHT2jsoyaCcpzDgYztIO/IsY9/btP1bXLCMADTiyx0xdkKIqFD1z5q2SmTibCrhWxMv6Hm859AHxE954m7xE2KIfoEPhzJJVfYyD6Qe9Odgg1pwvXT+2jN0REuW3L5OQ9Gz+o5k8Oig0TW1fsCIUQwv80F6QD0vNRRIg80YU9ckWATtHmMblN0MUoudWeGJjwpsclWr1K7EuubwahNVdS1Zv69ZqKjlvXyI3SDgQsqzY/LI+85N3xnHOP6s1IYHFIglOrCmTe2ABBYrs9M06u6Fi2uCWhqns619AtFnunwHQtc+5pMCrzzhZ+NKEEwnAPNq3O4D3C131pYX3BQOC6prKJkX2pJ3448q9Q741Fq10QV7ipVetcXROSjZnq/FRnJJZfXAVpnFpqsSJEDrgNXG0QKUSu0fME7xpkBuCInFTP8nFSinlAdY85xVV4qhwOuV2fDEF6ihMORsgy4pmw/wDlTEzKxhp0HOkEpnPGcEGO4HgFkmGBuQniLmQG4UoBJCsPoXS99+V+UpxjwB+mMsUBHY3h/y4EuUx2yNqgOi3UqSi0c2qwPZiI6QmUMtHXraqLoLaflfpJ92cmbZhLckIMM/2GSxosDkbnCZOfpi2cMCcPPFybPZMgcE0boPgG+d9914MoJSrbLu1+uEJ/UxTTFRMXOi+cPgDGgEdzIifvoOp6B8p4taBsEuBxZL+qsM+944pl41iQJw4DpuEiZvvfL8O5rwjS0e8+80G/tumsioJps2UJEU1FnHiyRBDfkRhumiFKrJlIoAs5YhfkCesQ1QNSRohOcap+psBdbP0dA8OGRu1fKOgWipEo4HcKRxYAnasD7nYUHvGqA7v3mclnhFJHRqqr5JriVALOSoqzMhyhtkiduGIEIAS9S1Q3mSAw7C6LEDIdJsTCizo4+wbADkwu1fBvexNFLG6A1RDgtXCEa2WoSURjBp0cmMO9Pk2kiGlCQWDi8xMLPJ8TUvRRRG5qAZlreJi1inignkgUR6fI+hUU0ec3lDmWcQ24Co77TAkRuTv90vjPQn0jlPDscKT55bU+YYjl+woRIUeHdL5P7Nl7aJeY9/8xbqqmEs2xGOXdEZbyJhpUEt/NgjiOur2EGxMaw/dbHE7brC2dZ21V2VbbBZFBpBFxD3WVxX7VzKcE24DT0nznm5MEdB66dg5s5gO5ANqZ/UTMBrZhwFoqEUVMP/ZhuW2T/pAlnMnIK3t+qLPxlm7JnKbohjapb0PZFbvKi6uo5PmQkkox0LuEMDewcGQpqAKDqK6POdOHK9YadUTk1i4ln4px+fjH/2si+T/SjeW9cL8f9gCZAIUJ40lci2bniOxLDdxCBifvETK9vNCwhMBW5TgEBjXPYrh9kIW2zLAv4cxHXFNG64ZzFEuXbD4hmoeWnIaecPrbgNrQADOn08tMtMNfWxvSR8fMFhJ+DOMIZL/4YR1Mq5Y8U92T0pHymS51Oe4blqoK4dS8G2Zfa0rfFM51ynhldGAEKdk0Ug3df5NEnLLrHn4F5nnR4EwCINeHwB5KqPp4wwJxcIDoosBwNCCxfYAnt1yOFKe7CYL54x70fm+wUZS6qrO1Ei2eGKNsAU0yuGINjeIZI+4nLJXThwS+0ZL8cQsSA6wS9xTxRKGZpnXexSZUcmLva5gDMizgzDQbLYDAhr2NhQnx1nfTvZf5/GDfUZ4T33/Dcb466T4EdtfRzM385rTcC96PRIDpJ07BgMjGhCOdVyDu+cJb1BvrhqAd5PxImQ8oy4bpAQ9L0nk3u5dYDuOELs3peGdcTzHjXgaCXX48+88RZlkqDuWKi4sqlnHYS3IsilI4T7j3vYmLiION4kWeOiwM5Bznb9SajYjdQ02BIWgbyZjBps2iXA8swhVjrcrgmC9wv5jqiPbbtRZzlhQjoCX3+hTJFe6yE/7d81QLy2vRLYk1wyolpUUJZsUG59LZr3y12xA0JaDJKyBvw6hGsknBksQEgL/t3L/opkCtR93jnsuK3Jvt3LfqMy9yCVgIsmRZLvb26i4Fuwh8wevWVjhyX+zkS/f7OH6AnXANpE9DFDM6DG4rIaDnpHNBzZ3HNkcO8aFrpzND7Aq6i+WSklT/JkakDlLtGzE2Q8CK1TM9uin+jd9MU98gTyXhQjAPn/iY4npPOkBEW0n44tvjS8DIr7gRtkmeTARRGq0sBTT+v13YYNhizCjY20B1umUAkcRHxzFuOn+YGYBneffQit6J2VpCOEFuMd1xpl7KZYDSxYYAZGbBU2nfoyLycnpNMLeHRfxtyPOHZ1qztIuO4kdHQecaRcBlSahRs+OMfpokMegSgNgYKLzvluWxheyuBbEy/oyoBrZiQI48Xy6+pdyPqQ8FAATk514UzXZBSpw3ttlt0QxoZNaXXJyyc8SIRVUzkcPTrynwHiFZvOTfz52g8IDj4Hw/uXg9wON4Sc/0m+PkZecBOB1bScFf0reIA5E7T+vXSbYrt+LnEGAPkqiS5RFxdIV1g8vqYyM2+NCe+nmNaCWhhpwp8Uc+3e2LuYXuOI+lsKObIF45uPwBB3OOgqBpoQ9hOadc1MvWK/LjhAoY3f5QCGhDImx1wrpfoBsuJTcU2w6iKCGcoAN+GhyPJi/Tf6nnWBE5Vze6oZ2RfaspBFc9q5nmJPHn0g1wqsZ9YNuHnsQon1tXzc9mBgbYfgWYa3rIMzdvP4HnPNYFD1ktFB9k5f1mdyksllsYoT78tBDRjgPf5vPT0J8BMF/JqCi+/+OHJqKCMIycsbkDASJiGEs9E/S2YjMNxGXhYd9A6dq5HM+lChmGKCKZU2rumppgUODZgud51lIkqNW+HNtCVHbcIZRYbDCS8+sqBecJgSHjCGXNyKNgCWU7cZEfIDD8yQnsWuPIoa+IfbM/LFWp3yMir5bRqp0oXMP0INDADSBgwDQMm9yNBAN3IewKUN1GxIyaUJmOwTCYMm2XA4RyDUiJyIO+KpZyOy2GxYH2Z6/hikz5RkbnPHEeESRsmuJEBs3MwAhOVJGDZagDEtOdBeNT8CYoUzvZlbORsFznbUcmZTcNA0jLQkDSV0QYsJEwXCVtE0zkuBw8ny9CfN2+yJdsSKZ4ZpvYcGp4oWLhcuCqkYltJeaJPEGmnqog+C0fvRua3BIJ5FJnhTxb0Mt7g3HZ5ILI4rxks6RxJca426ACEaGAagCM3Ggn1icyLfuaZA0ocgOsGRCkReZYAXNdb6u2JfLYpItFkFBV8z6nDOTK2E8iRqDsLEgYLDNwbYCpHjuPCX68vI4v1peme2CejoJlhikE4ACRT3rXmgUmj49khcQ15wKYDwjlheOKdfABMxpT9kSJagTOO+RO+QLSGmph6OVC5n3Rf5LYUtkKJptCeO+5CTbEiPPi6M4SLm6/EQxhy4uWLnDAtmJYVeqY4OHyHm1iGL+6VHMS7LofhiWIJgyHhcqRNKZoxlUbAMVhwOY8uFkvHm4w4U3Yp2KdzKyGcOa4rbFQiCWYlwMwkuBedoV9n+ZsQUdG8YNyT126GFD0Trj/GMwzAck04BleCbUG0o3z2Ipadup0d6BZkY/olZedBYVuj/x126pbKr6kiYDQBTZ/Ua/WRYpSe/zHQl2m7LophOoe+MzL0cbf8nrCgErVJmvcZBgREfyWeyGV7sh5anyqjdeTv1tXeB8T4m3PR13JALTWX87piye7Dm6KpHGNFIrykrZNzHDGm94MsGBfCGVzhEmPghfND7oLZnnBmZ/wNskLpcZhpKiFPtxv+MxLO74ngih/vpdvloHjmO7pdzuEafl8vc3oDrFBA065heLm7slcR+ZfFyT377bVHCWjMgMrzLUU47XvkfD1876MEslK4nKvdZMM7sobbVywQp+jf8rfn3Z+wxqBXVRfO5LPTLTmL7EtN6RuRZzEnMgEDEyWucen198SQsFcGwZwatutHDwWWGRooWF4nd9+0TH2iIjoV7jiFg0hP3ACEJxmel595y9WMxsGByYpY3mNBTpDksrqM91I5qRx/ICyiEsRyOv9vEQkkOn+vU5TX1/UMSS7rCX1d4JnOgvxTatALiHonkgV5ZjgQ8Hr4ObNcP5cB4zC8aAHp5bAMEYVlGQwWA5idA/TQ5vBz4U0UVN61QJi65sVwbOi73YhymjgjB/2ldqpU4cXa82UYsKy0WMoicw55qpzrikG+LkCp5bVaU8TSGAbbMtUEwGQMLGnCZIBtREVs2YHBvdt1QEyGcxk4mSycnA3uip1DmWnATFgw5ETYu4c8mRbPnGtD37VSLTl1uJqcSOFsfyaPLi/6LBx5lrMtX2T2BMGEwdDguHBMM5irQvdUSREwMFkJTpRhmIBpqiWbME3ACj533YK8NockkYM6FEaBlqOsgKZ7F0vZIn0CoQv+gcp5Ijx3RZ/kGjAtCzb0ga/wEMsIIdl3Slvk5xBjqo9pSHhLIRzu9aXcz8Mh66FHq2YzYima/O2FB+6pNJidF4K7jFQ1k2JS4bpq3B4WNTrzjqqztEWA3yfIyOiEyZDgTDioTBboG2S/wAI5HfN+FLRnM/36+kMdbphCPPNsj4wOyLt+vhcAalfGkhHPjpYqABAOGsMCM6zA86ImaUCgzTJqzeF+tJYZjiTg3tJNmdNRPn5SONNzQOrik1yObyVgpAaoPpwzA7DFNWHMUnnxXGhOEdfv25Vd1sUngyHLGFJWaHdlGWnNRVRH4PfnjR+U80Z7xng2Azdvw8mJvGHCJuV9m+Tl5eRWAiw1QNgEaWelpqqlE4ga90SJZ2ku//aXclou8wU0dTFDk2lPAJQOKdh58EwnugXZmH6LfDJrMl3Vxp+BpZJe5CpnLsANFaGk7w4PSBsTzIEVjp51uBT9ZZ2FcOZw7u+MXKxeUjgLb/ISEgg5d6F2C2b+pmPFNkkrtSJIXAsvz5nrVd5bthm46nKe483NlHAWzmdoWp54GLT1MorM9Wwe1/72N0Lj4pSQ11WkMMm7HJbncFJjXycn5lsh5wI3TH/FiDfHkmMGBuG84l60dfh6Sce9fOVCm37JAAa977ZMJqLOLMBwGfKuiwQ3CwTKSLT7yx0nKJx5KYoCc114ds/Wxl0Jw58LeJsSIfzMRFBOOIt6l8EX0Pzz+OMA2Sb/Ta0e4d9d4Luk/gDITRwAROZWl3nAg5GUMa51Kci+1JS+IZ4Bpb0zKCKchScu3AHggHMTzHXBLTEYlMNRXbG3XaAz52hLAuRgWEQK6ZOVPOMwXeGNdThEB6nlxg0MJjOdcLNd4m+Z+0x2ksm0WsYAAGwg83OluDYMr+OVPzi5rK4z76ArL/7NO6KzBMQAOGEYyFvc6xwclVdLJozUc1DJdfgimsmra9cBVX9RV2/ADm/wnkwH7gM3hICSd4RHJudwdOacyKizhGX4A1rN0y+9/wjtVBqYyDJDePelt8pM+sZM3XKR1N8wxGdkMmXV2bua18y21QRFz82idqr02s2SNpib9EPGve+3jCRM1480ALx75HrecW+gn7FdtezRj9wS4lPWcdHg+p4kwwAsw0LS9SZ8puEt1/EMlS44ZQ7A7TqA/IEMnEwOTt4Gl89B0oKTsGDmbSQavYmwlQRPDQjsBisFB+551fKuC9sRUQpdOQf7M3m0d+bRqS3dNAPimQvHm2QmDAMp20U+wdWEX04w1WDGG7yoCaKeI0eLMhH6rphYGYGLkwB30mBWeWG9HJwFoxbjlCcOHsVEs6j34wppkROcImKZylMVOKgNsPTJg4xCk59zPZHEcrX+S+Y+4wViVLHIYgCeQO0i7xqed94E85w5CZfDMRgsOaiSg1E7D951QIgaEVFBHBBOETsHw84HI1XNHJibAueuv7skCqPOuvKuWCYSEjQSLlPRTUbey6dl+CKMdG6oHGdOXsvpmIGee1MJ6eH75V3TQM44FyqHqRpwuwDzvjsc9awinu1cINqZuwzMEu02vIlfXk5GeTCHkKoSxCRU5gqzDBERaJpJNUFjPBRBKycisl/M54IR6/IaeBOrguhALxGzHDOo0wJq3CDzWOrCrETmO3O9aDR1/7Rl+GpoFY4+keMHzy7xbAb5A0I8c/P+JDXSJtl54UBMaJNu5k8s5Lgnrwl/8pmTGybJusolU442dksYLhImU5Pw8MRaCswqElp79tzO/egOZGOISCdNXLRcv6pPAgon9kysQ5ffJZ4j8Syp1TXcF6OLpacRqxU9R43hqUReHq/AhjTwxYTAzotaPx6eiwU3SUsqwUSKaeK4t7pE1o3Lsamop76bs6gZD+w8zUyAo8RGVZpwFriezNtVOtR3hsWG8LhCzh9lTmeTMSRNA0kOAC5MwxD2WDp6PMeQdC7IFAri5NqKJM+JJdL6yDQFclWM2GlaXi99V2KZRiETEs4iVyhxA67XIIMx5A0mMgYpoYcFclxGwj3njoxilwEiusNHrrYyTXBLCGgyfzGYn+9MFwTLbQoWR2/S2yp/Fy4XG0uo6Ui5k+jCWcSYUN9gQs9nyyCeZH3xqh81F3SYWrFaU6R6ZF9qykEXz7plLMKU8/qHEslHhgwDYmcqwxSTGGaoSQsAlUesMyfEKNnZ6IMysfxECmoWGLg38JaROMIzzb0QVOhi1IEOuF0HlBdWRgeZSQtmOgWWblSdjeEtYWCuKAfTNyQuh/C+2i668g72ZR0cyIkldVnbb2tD0kTeW04nlnEypCwDCcPyDKiGJ6C5mhgjo5m444A7LoxkQokY3DDB7TwMGf3jRX/l8y5yLkfe9TYzcP3rZxhiNX3CEGvsU5YQzuTOmoFdzkLCWSAngfd93LQAMwmby6WifgQAgxQ7hQhlmoYQngyZIN8W98lMguX/oQRNlaxeLpMxPA9PIukbMelpYQYYYzBTFhIGQ07bOMDlvoiY8aK3unKOJz75XalpGBiQNMWyRxngwIC0ZWCA5QlP3iBAX+bjL3UUhjd/IAO7MwO7Kwc3n4fjTVTMhAUjkYDl/Z0wTPDUAPBsF5DLgiWdQM4cACpCMGM7yNgy6szGvoyNLq8d8p4mTUOJZ7I9SctAwmRoSJjIOC4aXVO1I/x75TLnkp0Hz+eDmwaoiySWbIqJvAHYCXEvpWEmQ9BvKCeclSofR0grarOios9UDkXdYeMPzpUIpO0qyBkTSx/1wbprwzCSgfpJAVvmEBMiQVCQAoTYk7ZMsSTDkUKBiYTBkTQ4TM+LrpwEUhDoOgC3cx/cTAZ2JidsDKCiVa20iLhRfb0n1HAnuMxFDtRl1FlX3ncU6BFNpsGQtkREmFxymjUNpCwWENYZ52JC4+RElJy0RbmM32groQR0Jq+hF4HGTUtFV4Tvqy6weBdfXXOTCUeAjHhm+azYOEe7T8ywhHMlYUAGOcjJqIx0CyezFlFs4vqIXHRiZ2IYnoDm9WNq2Yp3r7hK9dAVyE8H11VL8uXyWuZNUthAz6FkJQHHhmn5z7IUD/Pask0pnnXlHeXQkfcqaRnIOyachG+7U47hiZERzjcZae9FyskIx7BdAkQEgpGwlIMnYSX8ZfvphoCzTN4f/1rLHKp+G3KO75SS9TcNBtfLTSHHbWnLRd4xkDSZPyFH0K4qAVA6pqTQ3M0NAwgCKGJfis1tih3X5zXhsTGTK2yg0gIYhuFteKIJWNyP5pJilC5EAUL0VzvtajmeHc5hermew3UK7Ojs5PwIWgDF8lWq9CiuDbgJwPQj5Irt5hzeMdJgDBwMLhPeEOb6ObtceP2ILkCGnfPaNVciXgQGvChib27hMsABV/ZPOiMMJuzdwKQFwFD22PKiwZns47MZf56V96MuWEJs2sNzGZHOx3VENHYKvtPN8PJN67cBvhAqI3LDkcZ66hh45WEZMLyysn+1DAOO66dWcJgfOV2QoohzsQFRWDiLSKGkhF0LIjBDri7xzsP15zsCPdI7imJjRF1Ek79BF/7STRWBVuR3Fyla6+9LAU2PniuyGNMFVLSiElyr186IGnPQxbOaE3sJJ/cHYLKjlGg7VYmk78nAANP2OppOGc3ldSQSKfrIRMRigGZ64pmLZEJbPigHk94SAGf/PuQ7PU+sJp4ZSQuJxjQSDVqEV2oAWLoBsHOQ2/mKOsqBoz9ROZCz8UFnHl2egAaIAWRXzsDAdAJOWi7bNNCQcNGQ8KOB5OAX3BVGTi7XzIj62pmc8hgbCQtWOim8Iqm0v6MKY+BmAjaHyC+j5ZiR1yzhWVqZjNj0XlDt0u4fEPK0BQcH3DCFZ8gTznJeIkw9CaroIMUuli6ApGHAtJKB58EZNBw8NRAWd8E73leJjaNybjG5tDY1AEbDILCUWNPuMgOwkjDNtMht4w1SZELRjBcdGLXsERCTqwFJE11JC06D8J4lDAOdloOGhIm8y5QXyYB/r5TYl8sgf6ALtvdc5Q90qWcLABzv+ZJ/G6Yh2pFuEIbZyQUmK6536aXhFWKZrQS0A1kbru3C8X4Ttmkg402K5XOXtAwMSJjKy2VrYeIcnrHS2gE98iyXgZvzxT9ZZyPh5UrIZsSgwltqJp6Nbkr0zPCXuMUtTxxyRA2qogQ1fxKBaJvDDLXkTv6tkihybddBb1mG6wkKKoo1mQZLSnHeArMTMFPJgqfY8TzHWdvFvpxdIHTI31racjHIFUujDUN4vHMOR97kSJm6LRKDddcTzpzOTiFuZHJw9SV1SQtu3kZCqflCqDLTjWBJR0URSEeO7foRctJu7s+K+ur9QkPSRGPSgmk4SBgMKcsX1kWIkasizpDzHU480+mL6Z5XHjIaLpEEt9JCMNJ+l2LHZq6iytRg2WuSqc38TC/HS8Jg4vvzWcDOeOKnfw04oCIm5DPicn83z6id1/5Xg4HGhIH2rLg+Cc68iD3NJrm2cOZ5zxTj3I8AD0Xkysho/VkyXFdMAqyEWFJq5wArrRxIjHE1GZX9uowU3J+1tWhi0dakZSJpGXCSwkFoGgwJ20XWdJGzXCRNU7VBIcVZbfm9bpfsTFbYJccFMz2bJKOjE5b4TXhLJJk3+ZZOQwBezjb4O0BrzkM9Glo+a0nNJhlMRGmmbBcpy4XjGgWR0GoXWrl5jbbs1O3sRG5fDZZtko0hELIvkqg0ARHoSzXVskPp+ADU0nJxQKQFkLvvGt7YVI/mkmKUnMBLTMZguAyWIUQ0WRsGEUXrMOZvSKOJUbpwBtuGqy051yOHueuIFTfwxvWuWVQwkdHN4brq0bJy462ETFPDEYyQhd+3qlUtTj6YrJ9p1867D5wJRwe4iB6DGuOL6K28K+zf/pyN9oyt5owmYxiQEPNE00gg6XA4lrD3KgBAjn31VT7ebsTMMNWSfNh5uJ54ZkAbl5gWXM/uCc3QF0hlVKHrzV9tR/b9hamIHE3g0vN+JkwDJhOBEMJGcrEax7QAR1teBXhimTffdUK7eIcjxr1aMi+PMbdzMExTRJ8Zlpr7hdOylBPFwvBi5SFsgh59VrB0U53Er0tAOAs/p97zI4RXF8wRkXRSuBbjzKB4LUVg+SwnKvUM65B9qSl9TzwDoiMAJPpDHvYuaB2kSFDrqqghkf9EPLiicwkut9M7G7FDJENX3kE2aSlhzWCAZRiwLRHdw5jhe128aK7cvk7kOjqR7+yC05ULiGeON4FJAWBWAjx9ADzdAJYcGFhW53A/l1bGdnAg5+CDzjzaO3MFS+r85XQcSU/068wLb4gQZPwfK9M2CnCzXXD270NuX2dgYiVFGGYaIkoOMpLCAreSyDkcOdffuSttmUhbwQ5TRlTInGCACG+1vCTEJYUQ6fHwIsZsb3Jpu0JAk4k8AagOy+FA0huAp01LdPy2553zcqswJwdXzyGW7VIGXzTcE89S3gBfeoGY4YVXp2BaaTVR44AyWlnHxX5POJPLHrtyesJ9hqy3nBPwl9umLAMZ20VDQkZtQXV4jHMRDehNrpxMDvkDGeQ6DsDO5OBkcmpiwkwj4OU3EhbM9AHw3CCxTEYzBP4z5u+Q2plzAsJZPmvDzjlq6arNGKykif3edU9aIpKuK295kTKWilLQbYMQGBwlLMjfiZvLK8FWTrRc04AFgJkZ8duVefAivFpVQfkCDgm6M7aIe05dTFO9UZTN8QQ0mIY3UJJLL6FEIOGE6BRRnq4nuBiGcoywAa7K2cUSaSQME6bhbzziel7gzryDfVlbCfCyrwDE721gOoG8mxBRXQxImyK6xuFQ+cCY5sjhuQycTmGLRB+fDYhnwkmSAndcJAER4ZVuFNFPA+yAMyewPN2LBGrvynubizgBQUPPi5gwhCPH1m+AEs9yIuLsQAfcA/uEZ97OA4YBI90o/h3QCNY4GDw5EDyREjaBMTDHhskMpEwLLmcqKiwK5i3VNA1/0sDyORExEcq1ybmw6WpwD3jJo+HZHxd5NxgZAQA5x8AAC9714XAM8cwlTS+KwfTyaTJ/MhJeyi6iwIWAKB1ZMkKQeWKaYRjgiSSM1ACxU6SMetR2glE7sDkcWccXnuQz5d8rxxOfEt7f4n5lLBcNjmwfEDDXctmOF0EMOw8nk4PdlUP+QJdIJ6BH2+ct9byZmRwMKRJ64pmegsPlYkFM3kujkfeEs4xdrP6eeJb0hD9TiH96GgEZCa2cUhKZ9Fo6PTs7hcNzP+U8I2pLydGuZnO496e/VtH7behzG4lrAMwGrKT6DjGpT6q8jmI/E66cH/pOjBK1466X6kXOj2SOZ865P1kPCwxKhPY3pFH5KgG1kgCAEE1cG3AtP+JIuz4c+rJ7fRd7sTpCLis3DC9vF0QEkMMAbXsv3yEvd7qUOTW96DgAyoZzCJFL7mas2ujkYAIwzSQAA7brwuEuumwH7RkbezN57Ms6IrrYZMh7aVjSpoGUKfKeQXdmuY6ab/BcBm4mo5zGhhy3y8gzO+dtMiDyFjMviIA5NrjpR63rz5Or2yPu99sO5wVjCITSvhnMUbmXmVxZ5Yr0RIE8eirS3gm8VIqB0Bhdiqaigg64a4C5png2vOgzdV7ZCG9TJSDksEHh+C0smEXuuAkWENAYY9HLUaPmF2Wi4uB6K5rkbwL+mFKN66T+rRxa4rfXreEt2Zea0ivEszJySHXEjEATFfAH5DI8lzED3BtgusxAMjkQCYN5nYQQcWwvDFffZVAXpRqSJganHWTthJeAMIWkyZBzOBIyP5mnwLvZLuQPdCHX0YnsB0KUcjI5uI6rOkkheDhi8pLeJ4SaxkEw9KUjgOftkF5+F/syNto7c/igM4/9oXoOSJpqQJmypCBjIu+64PouMnKyJwfsnSJCTgp9bs5WkypACDCGzA1mWOBWGo6RRCbrwHFFJ5GymFouwZjXYTscGRvozPtbJqe4obxFPOpJkZNUJZolwK0kbAhxKedwZB1/W2bpUWPMi7YzODgXHYXJGJLesloAMPb9Debed+D+42++98cLoXZzeXDXDdwjlkqLyZs0YFZSLK31PFhMG4bLiUrGdr0Bvh+9lfMisgARldeVc5BLizrJqK0GL3LLdrmKPFO3S3p27BycTBZ2Jgc7k1VLZJycAycnk3QaSAzQJirpJJxMFkY2403qta2y4XvkXU/k7Mr5SzXzSjzzI89M04DjuOAuxwHttzEw7SA/gKvBjotCwyZDu7m2ZFaKf44mnhnaRhymlRDLOwO7wNYg8owMD4FgaD9QRECTeP+Xucw44AlLWT86VC77yoplh3KDGKNhEAxvWaQLkUMrOaAJSZchY4svl0s29+VsvL8/h/bOnFo6rduiQWnv2OCUiPBKmGhMmrBdTXTXxbOMyI8onDkHVE4qUT/PHqX9pZzpdFr1i7o94l5fK3OzyYgzaZM6c754pjtyRB0NZG0Tcic1gzHRh9o5MDsL50AH3H0fwN3/AZzOTriOi0TjALBEEkbTUOCwEXAGNAFW2rtRLpidBXgXWN4CSwzAgEQDHO7CdoXXHI4/31PRz4a347Hhfb+TB3M84czJ+Qn9meEnkPai0KQwJ/p6BCK65MRuf85WDpC0JaPsxGZFeeYt6zQtXzvj/lJ2XThzOjsLIgSNhAVLOUlMcCsJN90AJAaoft3QxDPXherXpWNkf0aMG2SUoONyJL2xgiRpGUiZDgY4hkrDEMhEIyPXvWgDuXGNnckh39mlcnG6eVvZU1eKZ17aikSjWLbE8zlE5ZUJLNn0HDvSLslo7qzmgEp5kWfyN5IyvXw/CT/yJjDx8iLc1YZOnmPKzuSUyNwtyMYQEQTmR+F5jfYMcHjzFiYm5AXpAQJR0IbXOfvHzKQlcgBrqUXCuSqjUgIkXAOuK+YP4RzPgbprwhTyOV84U6lQ/B2dYZiAJ6S5kBFIcodqb9meytMmnRRifC83spK5qOVmLAZn4jO2WDJpGX7UWWDFhmuL9CtOzo8Ml0JeaoAQzqwEYKXBEykVJaY7U7iZQyI1EIAXdZZ1sDeTxz+68vigMw/H5UhZBpoGJGAwhoaEjYEpzx4zLyelHDt4Dgc3k0G+M7giSYzXcyqFAsv7Y14DgMEMoGsvXNcGkg0wmentiuxFXIe6ELlcM+c566XjKme7Ypm+a6j8qTrMSwNh65vMhZZtys3WZK5OOa4PnAcQttN1RMSh46joM+gBLiWWRopnwl9hBERHmMk2RAlSMuJMCmhi+b7YJTtKryhIAaWthgpHi3K1sZy2cyiMoptbynviuhy8O9MYsi81pVeIZ5USR31VkxntMyJ1mVhzzOAJLZ7yK3aR9NeWAwBLN8JoGAi4LiwAA6xGZBwRqgqIicuBnBOYtEi1XgoEhw9MYfhgcSxlmUhbBhoSHNyyVM/F7Rx4phP2gQxy+w4g+8F+ZDsyyB/IwXU4DJPBSJhIq8mLCTOdBEs3wMhmPK+MtmzTWxIoc+Dsz+S9yLM89nfl4dguuKemZ/JmoM4D05a27t3vfFQOr2yXl3i+yxswBidWVjoJwzSQaEyrunIzCZ4YgKy3ZbPBRB6sAZaYmMi7lHE4HFcsDZFLfADhMco7LlLhHRnlPdZe3EyAJ9JwmKVEs4wtRDnbFZslqLw/nrecWyLEmjEv74DlRSgYhoi269yvBspuZ4fyNLs5u8ALZKZzSOgbPcgdK70JpZyoOGqAL4xUeNljOHIr74XGCcEzL8SnpMgXZrtcha2rZ971d7Vxcrbw8HsTlHyXDTtjK/HMMFkgCs1KJ2E3pmGpnV91L784v1xyKo1sznaR9eqdyzqwcw5c75yOacB0vAGGwdCVMFV0XdYbkKklwuoH63urVL4z2xfO9KXCyAM8oU20pGimL6vtriEgw9PrqUfUWZzv05PMhm2OyzXPvmmJfCAAuGuD5Tt9z/K+D0QkVU70HUYyAbdhEIzOfTBzGRiHu3CZAcNMIGkOUM5pmUflg848/rE/69miPHJ5B9zlYAZDMmEGxLS0ZWJg0vEiobxlI7LOeRHhqfr3DhENnT+Qgd2lL9s04Hp9vOj39nv2SIjt+gDX5dpSOjvozOnKhSLPNAdBY9JSIgjgeWXzQjhzD3SIfGz7P0Dugw44mZxwAgwaCGPw4WDDjoQ95AiRr9LJgeUzMPIZsBV5qKkAAMaISURBVMw+0aebSbhpG6ZpIWEkRLSFt+uZROQnFf9ahtjwi+W8CZXjRSRIZxsQvfMa95fn591grlSZlNlgQHvGRsoykLVEJLF4rgyYjMF2uVj+pPp2O7BbpRTO9OW1esQ6d10kALXpkBQ45bJHZrFARLFcItKV94UnPRpaimc5TzwzDYb9GQNpy/CW4fsOqkDkOtcSRWt9udOV8yKjs3ByjhrzWANcJQDaXaK8ITdXyufA0oUCmhL/HFdN+rtydqD+ss6+aOsgZYn6y6g12/V3Owt0K9Imefnm3EzGW3bahfy+LnQLsjH9jlI2KzLCGSiwMfKYXN7IbKixTyA9gK2nuTDBnRzgJNWuz2AGEskG2K5YZeN69dN3SNaX+hmGl5vY1TYNMUR6Gsdl3qSfiRy0ANRKH33DjWxG9QcB8UzmqjS9pef67rqh68c5QjsF+7vrSgd0Hn7QAyzT2y2SweXaahbuBqLN4Ih5mVxeyAwTSA0QKQCSA+GmB6mNgZgj7BLLeQJ6sgEsNVAEWbjAvpyNfVkHuzuyaO/MqT7UdkUE2sCkl8JE2mP9t+2JTGrsG5Eb0konYTXmkBgUTCdjGKZYpu9FyFmJBpiGCBpg8FPj+HkjucoPKVe7yCWbMldxQ9JEPqXlv2TCEZE3OZKe/YhcHui6ahmqEs70yDOZ9sB/SLVxfEIJalJABXdjaQH6vCIqz6l8jnT0JZsuoDbBiP4CN/h/KZzpkXHiP2JO6Nq+0O2IFFGGYXhip//ScbyAmOK7McSA7EtN6TXiWZzos0rmRgGjIhVf7ic9lzuQiFBdG8i5IodX+/tiImPnwKwkjEGHwRw6EqbrIN1koTGRRGdeeC7yDscHnXn8vSOD3R1ZdHTllehhmgZSKUstTZHr2w8fkBBqvtpRxVERNSIfVQbZjgwyezPIHcjByTkwTANW2oKb98SOhJf/rEkIOkzmdeKu8tLLQWCXF9G0P2Njf1ce2a487LyjAiSsvAkuPf1JE/szNjpTFjJe0kjV13DXTyYdEvryBzJwcq4SYcx00hdjkmnw5ADwZAPyOXFOy2BoSplIeL9NeQ9c01Sdlm6gAahJTSDaw/B2N9PynHErCW5YXgSbjDoTy2HC+elEDgRX5KRLGDC9CY7NGRL6AEUOlLNdKreJ8JRnA9FPVjql2p70Ep66XQdgNQ728ifYYKafvNPVJlTKQ553AsseXVc8S1KIkvdpYNrx2iWTbpsBIyFDornreGKT8PLnDuSQPyAENPk8McOAKwXFhOUv65QedjsX9KTI+mseqq6cA8dxkfeEs3zW1hwurljSZDCYpoNMXkYCiAm87UUFysmm1givHa4S0Ny8ECzFhhp5FfliOvK3YcPN5VX4ujS43YUzVuFONTWPpSUOAnFsTjgSTR7j8EVyuUw9YZgYkB4sllXnxGSb5zJwOj5Qfambz8NIJJBo3IvUYQPBuw7AzOdgjgC4lURqUIPY8dEQE5Ss7aK9M4f39+ewZ18Wua48ct7vjxlANmEil0/4EV1JE01pC515B4e5/jJJFQknl3l3dnmRZ13IdmTh5PwoW2uABS77jKQYvEt7BNsO5BD1ExOL/Fm6MyeTd+B6zhtDy0GVtAxkGhKBlAcmYyLqK98JZ98HcNvfR+6DDmQ/ELscpg4bCGPgYTCHjkJ+yBHo5AmkOFQCY5bPgnW2i01sBjSCmwmxhNNMqHwtliGSSjMI0Sxh+PldAlFnMheObKf0Jss2Mz/nqe1FPKvcll5uOtHnedeQiWT7edf0HEzMy5HKkQJTA1nGuUi8rOd/zGTU2CFsk0wtestI+7nRkM+p/EN6vjf57Dqqb3cC9knu1C0jEQC5BNJUkSlyE4hw1JaKPvAEMDcn+nDbc4ZImyTFM90mJfKi75f2iHtRCPoOfbLeUqiUTik9+kzPBTjAc0glLVs4DlOWH7XmunBRGGGh2yR4E1q7Szim8t2MPCMb0z+I6+SJinCOKuN4kVeMMZjMgpnwotOcnL8RTC7r75rsoXL0phvFWFM6eFhCRdvITTdk5LDss+RvyDYMtUIEEJqJwUwkPeFM1VlfsmnbvoginQBSPJMbnQREFVOI5WaiYDMaQCy9dFyoqDO9rnIeIeomIs8M5sI1DbU7J+DlKAtFnfFMp4qMAwCkk55zPgU31QgbhtAyVORZBkbugNjVMJFWkccZL/d0R0Y4uGTkmew/hSNc2ONBjom8y8WmXXpuNdeBm7fFEndvpY9Ks5K0kE9YSHjj9uSgjOdAlis/TIAZcA0LpmkhaSSQNzgShljVYmgDF7nZS9Z21VL38PLNLk/oA5JeChnmrVpi2lJ9kQtOPn+Mcz/qTN5bbXMbAFrwh5zbOWAywMR1wJAoyHEmn61ylBPNCpZywhfQpGjmAmC8dN4zQI/4jFi6KeMP5NJl77ljAJiXg7UYejBLNZB9qS29RjwrR7lnJvxQyT/lemKZa0Z1SJalDAwcL6l9LgO3/X3k9/wNuQ4xEEoObkBi3wdIHJGHYZhoOGwMkl5nk/EmLbs+yGDvB13IHBDClOtymJaB1AALOU+gGJC00JS2sC9rY3DKX2YoRQ61fGFfJ7IdWfWSA8jEAOFBNhImzHQnkoMb4XZ2+vm3GoIL0nVP976MLZIzd+WR67JVHQ2DwbXF+TsMhoFpIfZlB7pKzPAvsNzdK68G6yIyoQu5A3lP5BPXJdEo8mkxKwmWboSbaIDteYg4B9IJJrz48j5xFyyfQTI5EIyJDj1tCa+7YTCkTZHvTHZa8jetlsoY4lrKDQJyDle51TI2R9YWS2NkbjoZei68Zl6dTSOwrFNtQ+11gnI5ky1zh+07ICK5tCUyTlcOlheNYSa9BMcNg4Th9wb7sutSAqE3EJFeHjsnxCc77y97dG3hBWcGQ5dpYF/GRlPO9nbPEwMbzv2JmJpQeAN8J2+LiUomBztjR0SeGd6/zI8GyHmJnb1BDQtZF9eVSbC9ujui7o7jws47cBwO13bVb0HcNwbTNER0mhaxJgc4AbOoGSD13OXEhMvN2YEcTBJmmkpcM+QES+YeosgzogKK2ZsoD6X+b7hszuXoygtvrgshAOUSBpoGNIHlO4VzwXGQ78zgQNv7yLzfATsj7FGiMY3UYQPROHI/GrwdpwwrDZ4aBMsQ/Uze5difFZvB/ONADl37sshmbE+8Fn2GlTCV+J60DDQ1JFUkgYhahb+kQPdyd+U8Z04Wuf152Bl/SZ2dyavBu5VOIt+QQaqz009Y70pnDvMiAvzl6TLCtstb4u3IpXSWoRw5g9KWWLruTYAY8yK/7Ax453447e8jt3cvsh/sh5u3kR46GKmRI2EMOkwsewUwwJI3xQBPNojBoEqo3+jdaFdNAHKMIWkKAYZB2CjLy4dlGSJXmow6U7ukcl44yJTOHK9vl5sFyGWrchMfuTTS8DYjyDuGmuglvO9VOTpld8JdFR0IzSYJu9SlclnKyDMhopmwvYhilu4C8yarwulWmAhB2iXbDTpHpNApxw4ON9WSx4G2o1INyB2tI/EmdXLDF7lU09ZsEnc4mMmQAODk/D7dzdletEJwYiLHEWJnsqBd1SMocp6dAgDT9VudsgwMSIr8bgOTJlyXi9NzP3o00J/rNilv+46pTm3352ogG9PniTv51ecvpc5lB5wzHKa3qiOZSItNVSAigXkuIzZW8fKLibxSIk+lYefBGkVuTWalYCb9jWkC+Sq1XGLiTSBhcCRc5kei2SKyWfZ7kQK699uR40uRz9bf6ITbcpmmiNRBPqFy7wZ2WmT+nEL+9uWukdJJLsU+gzGYXCwpNTxxnENG+IrIK2bb/lJNL8eYTKVgNA4W+TPNpGqOLqKo464t0gSYSbXzp8w9vS8j0xXkkXNcDPAijAckLQxOW9iXczDEm7s0WEmR59QQZbjj+H1NkQhj+0AG+cY07EwOqbyNpJZXzBwCyLQ2qQFJ5F2GnMtgONxzEDHInUv1pfpR6Yj0VD8GY0iZhkg74Pq2I/zk+qKZnu/M9XfchBZAo+59iTCrUjnFtN9HOPqZh94HZORi8Idp8NCSzahsQSrKTHM+hoSz8LyJwwVcgDEvmIdzwBGbIBhShAWKLuHsVtYzsi81pVeJZ1HRZ5V4/sPlCxRiOaiFSB4PMKTMpB+Zls3A7dyHrvc70LV7L9y8DasxjcYDGTSaJpKNg2E1DkXSTAEQHoUPOvNo35fF/g8y6NyfQz4jQnwNK4F0YwKOzdFmGTisIYF9g9PozDuwveWL8uLzfF4tX8h32cgfyHniWQ523hEDVSl0JEwkBybELlWZHCwv7NnvTMQ6fteVOT+E5zWXsZHPOshlbRUVxAz4eaksQ0xoNFEmEAkkoxKyGSX02QcyyB3II9uehZN3YCZMMNNASgpKqTT4gMHgyQFwOVS+OLW7m7zfnvAlozMsAxiYtNR3J6R4ZgR331SCmWdk5QYBeZcrAU0m05aTxawXeg6IfDIpbggDYDtIGJbyXqvzA1BJjrP+Eg3b8/QHcrSkhQE1kwnYmRxMucQkcH+CHaPjQhmnnLdDpRKgZESGNzNnzEHWtJFLWyqHS94NRraocGlvgMQdB9xxvUgtkefMzojJipMPimdm0oSTc+HkRGSXm7d9D5E0CMz38Lsc/iDJ5aLutgvX5mLZpuex464JwAIzHFhJMZFXwpk3yJHnK0DzUjkq6swT0fJB8cwxtBw/+mYO+r2sFplFt5LyxCFJOeFMLqc2NUE/CodzHMi72JcVy/vzrouUZSDnWGAMOCw1CFa6AQCQP5BB1+4P0PHXDnTt7QJ3OJIDExgwdB/yB8QAvjGZhtk0FKxxKMykEM9cl6t8idmuPDKdeWQO5GDnXU08E0s4DdPA/pSF/Zk8OvMy71ZoubS3TFpEnmWQ259Hbn9eRJ7lHRVtZqX9HImJAxkkBmUL7ZF2PaXIL+3RAc+ZY+cd5cCRtmi/ZYjljTnHj8yCF/mV64Lb/j6cvbuReb8D+QNdaBo7CunjTgBrHATn/TZkt2+GOeRIOA3a7qTSVkAsX+Rmwv8bIkdz0ruZJveEfkOIWMpxY+dUImm9fZHecGaoqBDHi+DIeKkIMrajktnrKRMcb0CQMA3kE1rSfVl/LXpLRuPKDQJUvrC8HUy672pLHzM5EZGgls74kWdyAqmeXc8u6aKTXIqvR4R3MW8naN0eOfK58q+LSvvgeLnaXFf14UIcc8WSTRl55jLkIcY70mHi5HWHjte3hyZQMo+c44mV0q7mbBcZbykzdzkcr/7+ZjyOn0pBRqxEdQTcDdgkVf9MDvkD+YgPVADZmD5LpaIZ4Dv+9b/19x3POZNzvJy33tIyDheGZSFhJsANE4aMVJU7E3tpaWCYMAaIZYmGYYBZQnCTNs3l8BLKw9uJkXvRpf5vLs9ETmIASNguEgZDg+PC8Xbb5SHxQDlE9Q1P8nlP1HPU8khueN9hJcDkTtSh37p0GLteRE5e7hbpBJdtup6wmDDZ/8/evwfZVV134vhn7/O4r+6+ejTqlox42CEGBzJmRFmI/DLgwjw8ox8e4xjzU9JVZLAMRYGtCS4SQqbcnnLBAA5QRhObMJQhYAZ/U5Rqyo6tHyKeokJAgLFlBn5YGcfYvNSSgFa/u++95+zfH/tx9jn3nPu+Uqu1PlUX0feec+4+j7vXXp/1WWuBB3JOj5rEMHNtZTp8BawmO+iKhTkgDFX9zFGEjgdWXQCfex9hvgz4ia6bKjVS+AUILyfnkRBmjJWaUsCqgJFe3xf9Jawqelid91QgWkB4viz273qGRAqD0DT+qs4tobogA2Ty6xm8wgK8Us6UZwmrNeR0aiSXtc6Y6kTtewPwAmHSNu1U/ViZn8UaqqpeMQD4nmMUuzrItRTINFhTTxrpvryB3RzCXqObz4PGxFkSulGARYalTt32V6QQZ7H0TiYMgcYg4Ci/OvOkUuqcpdXlZJBlo2JEm96XccsOi95P72RfeoplRy2KjP/PQpI4038blRLSn/dqKJVJwmJjRa2C6swsFt+fwsLhI5h5+wPMvHkIM28eRPXgOwgmD4FV5mRrZsZQqYWYmq9iYbaCueklzH3wHuYOv4W5w29h/oMDmJucwcLsEhbnZErNnErXiMkvrUVYUJXR18psFZW5KuaXapithZirhqjMSyemOif/DUxqnVTY2D9UgShipKPGtap8VZdqqFVDRaIFqC5KQq2WKKxbVaRItPgVJuVCE32VuQoqszoVsGr+Dhbl4pgPrEI4cBKEm4fPgbwja8ekqjTcPJaCEELIa5t3OQqefOUcBl9H/5l8aHU0WDAO4cjOmoJp9Ziq3RLWy86XjPMSWCkaUYctwxXazopa8GvHREaAolST2oJUc+laXLXFJQSLUZ0uTT4xJa1PIlD3qWqRT0FNpkjp+yZfklhbsJRbVeXMhGkGI7SJs4g80wRaUAlRWwhi7weVACIMEVSqCIMwihjp56Bu7Io8q4XKr5BjDGtVhLWKeQU1aYBDtWjQqjWj9EuBrpETFRoNIYLQpCWFQajOK3LC9PnWdfHplfKsnVcH+Ju/+RucfvrpyOfz2LRpE/7pn/6p4fbPPPMMNm3ahHw+jw9/+MP4zne+U7fNk08+iY997GPI5XL42Mc+hl27dnX9vcsN3cjZbbRKnDHG6myMbWs4k7+N2UqId2cW8fbMIg7MLOHAzBI+WKhithJC5AYAFbGuTM9h7tAsjvx2Cu//dhoTb07j8P4P8MGvPsDUG+9h5s2DqL03gWDqfbDaIhwuUyJ0etqsUpstLVSxtFDD0uwMlmanUJmfx9JCzcz3C0tRNDmWKs0i0l2nREuFZ/TSdqm6UDP/SsVQxSzW0+yRvn6LtcjG1CraHkn7o8dXqwbGFum5BZDkFqtVZMrm1PuYm/gAs+8chj9UwsAf/gfgrP8X2NqTEUwewjv/32fgTL2TaveZ66mUTd+o0HVKqMeVnXHU/5taZ8yozGKLXfUsGOWZDs4xppRniIrY64Y1qoyAvha6DtxCxbJJyvkLQlHvfOrn1JoT5bwXqDRINRdWIiJN1hRbktsp1ZmwSj2kRbo1aanVZ9oe2f8GVmkIrfAyzmyaKEDXCwslCaeb7Wi7E1YDFbyR/4pAyPesuV2owIhIEJjxsSOmPNP/ilCYcQfKdsZqdtZ0gXGhCk7r2m3C/DbMqQSBVM8ZJXS1B+RZ/20M2Zejj06IM8DqwIfIrtg/UyEkcbZQDbFQk6VJZJ1j+b5wfRlUVl3Xw7lpWSNychKVyUkE00cQzEwinD0CMT8DVls0qZ66TIrOCtD+hO1T6DIjupmATpXUa+nQXksDZs40a2JdWkOrz0yZkGribyv7IUVxpLuCmsLqhkyz0sitOdhOMwXk3McZpBI3qMp0zbkZhPOyKZtYdzqCVRsg/CJ4dQG1t38FVpmrv4HclXXQ/AFpX9TcoetV2b6YttWzqoHAkfkqZisBZiqBJEMhgz2R8ixU84ysDbkwKUv7zB2ck69Dc5h5dxazB6YxNzGJ2XcOY/adw1iYeA/B+xMI3j+AcPoD8MocWHUBPo9qeup7tWjqkVbxwWwFk3MVzM9LNbv2Y+fnK6r0gtUkTIlBQiHMvciEnkPDyBYY/yKNSNP3KEmmJec/7cPbKjNL/JFFnNnbJj+z1Wh152SRZLFnUosMsrbR64YwURfNElZIH5eZv3sG8mF6imVHngERc90MWYqzNEOTBGdMKZNg6o8J1Tq9Mj2PhclFzL+3gLmD81g4fATzh48gnDkCVpWOCyAjm7NLNSzOV7A4fQSLkwexMDmBxanDWJw6jKXZSSzOSWdGF93VDkvsR64Wkzp9IagGqC7WZMpPILAQhFgIhHFi5CJTFatPpDDY10Y7H0EoLCImRK1SQ1BZRK1SiREzFWUIKzVNyCTugpUGWNMpgHNy0SgVc1U1RrmodE7aII2J2l2TYJXAOnf1WlRKMUDXmgF848BEtWdixJuSIYO7ijiLOptpCbE25CaiHEbGVXcwsQ2pnjBjUS57wa+MmFzIC0XiCEPmiDCM/j8I4gv9lHsUkxTrqHhgOSo1a8Gv3qsoQlCr6AJroZ+8XyLUBJgcZ1gNEFbl/wdKSRBUQgTVQCpLQmHk4NJRCVKfL0ATlJGDK9S1lOcRJF6hTOVUDmFy37TaMvb1j84lNGlJYVWNMwzNvTHjTqJL8kxooraNV7v4/ve/jx07duC2227Dz3/+c/zhH/4hPv3pT+PNN99M3f6NN97Av//3/x5/+Id/iJ///Of4y7/8S3z5y1/Gk08+abZ5/vnn8YUvfAFjY2P4xS9+gbGxMVx11VV44YUXOv7elYpWiTOZfinfSyVorP+fXKjiwMwSJmaWcGB2CROzSzg0V8FCLUQlVM9VTdZMWphcxNQHC/jNfNW8Dh+Yw/TbM5g/fASLH0wrJ6caS//WtQMrS2p+X5xDdXEWVfVvUFmU76u53k6XTkZpbWIjUGpVO9W7uhgn1GqLVdQWLKWqVjVZv99oThaJYE4yOBCYLr1yW7mYdlTtFBZUZcrmpAxoTb3xHspn/x4qo2chLK5Gbc0p8Db+Lqbf/ABHfvT/mHIM8kLVZI2vXAHCzUnlmZUm4TBJ0On6Zi5XykJld0y3Or0w1iSZuemaOIvU7aGQjVyMY6cdOWWn7LTImrFNEaGp50Qzr6fMKbrmo7E91rxoAgpqftQkmyncnGjsYC6V+kJ7/RAnnmyVcRQICZTSwhwn5behvkDN44FlP4W0T8quRuSaiP5Om9frDq3GLkTsGgciCkgJZYP02OMvpVyzA5zJa65q9Ghbo38nWsndKfptY8i+LF8knftGPoz93mJNYFE1xloKhFlHV0MRr6tckWqqyvScLA1zZBZLR2YRzM4iXJhDuDAnm7GE9TXFojRoxFKhTTq0lc6pa1uGQhMQifMMNFESdQw2JFq1YgLVpqh8EKk8RVBPmAtANQvQKduRcCAi0RFP57b+P3ZNVSdQsTgvG4PNHAFfux7B4AiEm0dYKCPMDcr3J/5vfCwiVKqzgZhtCQEsKaXxjFWqYGmxhsqCJNCm5quy4ZwqLaODylLJpsizMDQpm7qkz/z785h/bwGzByVxNnNgFtNvz2D67WnMHpjG/IEPMDfxPhYOTZpa3liaB6tKklSnJIZCkq5zlZqpnTo1V8HiXAWLc1LNvrSgxBZLNVQUcaafA+ln6Q7L+phof82tCTLuyML63FH/L99n3GmpDpe2ofYzIt+vJ85CiDrirOVAbCJCxNLSNVMUZqkvqEYB+lgMpoGAwxpnOLQK8mF6i2VJnnUK/dAnC+CmweXMECzmB6kVYIsVLE1FdccWp5ZQm5NdJrWsWRfUrVWkequ6OIfK3BRqi7Oozk+htjArX4qg0pONJjqiQQfWYjKMyAwhJdkhgKr6/1o1Ijt0JLauq6AFvSDU6iRNzsQVQTJtRi+G9YIzlWm3FUBhGCNftJIpqARSlTC3gGBwBGA8uhcixIDHZTH/IGolXLGIM0ely7hKAaBVAA6XhGcs8gYYp0UTkvqYulNW0kimwS6YyRNPTpgvA79zHpzyWnBHtoZmTms/G+0gmCgD9ASZpj5TBJQQFgmlojlhRKzpz8xiP3F+ZsJTz0QY2MRS3EExx1NOiSYDzbFS5QPpiBGQsf9PRJdStknCfl6iSFUQI8eiMYeJl3JsAuv7epG7fxSiNvfccw+uvfZafPGLX8RZZ52F++67Dxs3bsS3v/3t1O2/853v4JRTTsF9992Hs846C1/84hfxn/7Tf8I3v/lNs819992HSy65BLfeeivOPPNM3Hrrrbj44otx3333dfy9JzJCRIuaZjbG4QwfLFRxeK6CQzNLODS9hPdmK5hSaZMmPVzVGFuaXsJkNcQHlQATizVMLNbwXiXA/HsLWJyUaeIyCl8DUy3TjbpGERm1SoCgJrtxhdUKgqVF1CoLRvEpwqjGoqwzmI4oOCAM4RCGUq1dVe/LBgJCEfE1tV26k2PXSAz0HFeLAgRhLQocCHVOgKUk50x1P5NNaqbf/ACzB+fgfmyLUe/B8YHf3YKTfv8U/PbpX8D97c+iiG5lXpJaXhHCzcuUTVsRoWyLLg2gSTNN3JkAAo//xgVjJnijFWcmCt7C8+RwhvM2rsL/59+sx+hgLvaZ7YzYtk6n3si34vOMnr/ted9WeUWkle7GJ8+LWxkd8QLSUVq+iNkimBTOWJBOOabxQcUj7vKttGCTiOySeq6S0MEg26ZqNHJ6kvbJ2FFRP/5QRNsnTyWqJxrExh9Uw67Js37bGLIvRx8tO+IWdLe9ZvaFqU6889XApLlXA0W82OSB7g67tGi6NUZZEhXTwMNOhdZqrCTiRHlETmulpwwU1Ct34hdFp55ba0Mrs8DMUZZvk5nih+g3qlO2zZwkIjK/Ke9ualXVIJYWTPfrYGh99DvjLoJVG+CsHUXtnX+FM33AOqeabFimiB8gIu4Wa6FJg9TK8Kqltl5SavB493lYanAlllisoLYoSygsKnHH9NQiPphekq/3FzB9YBYz785h5t0ZzB2axeL70yh9+HTktlxhOi0z7Rso/0g3sTkyX5XE2YIs/bCoxltVhJ8sJSNM2ZVaIvBtuwvmzifnKNteWXYs9XP7PU2iGWKHRVlj3ApYWbuFQsSIs26QTJ8264qEGj3tPZZI50xCNgGKmgaY37/qiCpTOVUQr5v2Z+TD9BQrijxrFXYtAWNkdKRARxKVEkemsQml8oo7BXatp7BasUgpmbIWqW7SiYVm0FGUwFqQCmtBKRLOChOhkVtr6AlOhJFhEWFQR2roMQZp49PpoIaYiKLA9nj0Alin/gm/FD8O47KegBCYr4ZS9Yf4hKclq/bLZMVYpyasf/UrhCbOomM6jJk8cu0URe2AGTyHGyfJYVFaqLYEYWktwsERuOtPB+OSOOO+C+65cHwn9uIOr3NmspCsMdMMhvwUcVWA+byFx8omxmxnRX7WpXWxwNs5sVaQSJeR/4ax57CFUfV2TC1ieno69lpaWkrdrlKp4OWXX8all14ae//SSy/Fc889l7rP888/X7f9ZZddhp/+9KeoqlbmWdvoY3byvYTmYJBPXNRNV9aTNOrjEAkHRwYhpMo4xGIoVcezNVX/sBKRBkloZZBx+FMI6zAxdwDtLShFYNVVBOKEu/7exG8xSW6kjUUYwiIaf9JG2lOqqEqHb2lqSRIspbXxY+cHseas07A0tYTKL18Gqy7KcXNXdWK2otcZ42OWfYh/wM2/0cLdTV1s2qfALVJKNwdwVIcyhzN8fHQAGwc9nLGmVP+diCLphpyzIvNZgZy6dUJMEpZeL6wRkvfL3POEgtgGRwYBoBzlXqOZ2REZz1jqmgf1xJlBwonXQaZe2tB20IqNIfuysqB9GKloRaqaCgBMZgq0v2BlI+jfoC7Mbw4e/b9eF6chpti0Uyet35NopEBKW9ep8Zn3UwKvZnfYaqH21sI2YvOGrslWq8pr5BVi2wrHh1Nei3BpAXx+ErrbMgtkLUwWBlLhLCR1WAsFZisBPphdwuRcxajNqotxxbVWcVWDsL7IfbVqSsXoWtgLk4uYXArwXiXAwSX9quHwUoDpKUmsLUwuYml6Ef7ZW1BbexqcdSfLuVcFfGSzL+mLzSxJ1dmUqpmqVWbVpZpUgyvVsUjMm8m1RGzslp1kjkWUcR573rTCTDf6iRRn1n7q/5Pkjt1B0owB8bnbfh6EiKvO0rbJQh2hnCDIYhk6yRpoQH09NL0dENtXp2lrP1UH9LwWfcteg3yYdJyQ5JlWJwEWIaOVQQ4HdzgcT5Ihju+AcSZJEcV8J8F0QXfFpHPXA3d9cNdXf3OTAtIKsaC7VtqkjjZgzNJv6oWznnTS4KrvlAR9NE6uxhpFsJuPq9H3yHFzMIepa8br6wKIUEb9ARRcDs92ioRNfInYS1gTcyvQBBhjco72HC6bDjhyAsq7DvKug5zLVZtlRxYTtRh/c06LU+Bz7yN4/wC478HN+3DzOXilPNxiHm7eg1eQRJok1CSxFqnUmlwz9V1Oh4STlqoD0fVptcUwc5h5nuz/N5/ryTpj0jYKDfOMsdgzJneNP2cayd9BHZloIoHRwikibNtzvIwqpAtII93eCwA2btyIcrlsXnfccUfq8d977z0EQYCRkZHY+yMjI5iYmEjdZ2JiInX7Wq2G9957r+E2+pidfC+hOTSZ73GGou/Ad+Wr6DvwOJPB0ljWH4fjc5Oq7rEobZ1zBsdXc4mOslqRfg2esENMLUg5d+Tv05prdBChVTAnnjrAW1TfpsH+7TMeRVbTPk+OkXk+HM+FP+CBOUw6MPbnS7M48i9vIlfOwT9zE4SXl+MWoSn2z4KqcXDUIGLF7U3aSXzQ0ba6ppkizpIRcD1uSaDK6LGnrl/O5cg50gb5LkfRd/Av789jYq6GN6cWVLAn+lpbVSG/05GdwrkD5vnKueAmcBOtCeLrhEy1tFX3jAN1jrJ+VtJqdbYFrRjLgF47MEfaEeYwsw7SnwNQ52jZkjYIwNj3WRe5U9sLoOE5tX2sPtoYsi8rC9qHkR3pVc1GRwaDTV3wZLFyPUdwDu554L4r/3Y9MNcH8/yYf6PXz0nS37Wabdj/JsfXKViCWGEWsWJ9kLm/Jhzk2BD7Nw2xuV7N48zPy7m1uhD/2qCCYOp98FwBYXG1DJ6IECyogi0cka/aEiBkev5cJcDkQhWHppewMLOExfmKqe2p1dZAfTCcMyb9JVUPrrYoSztUZqtYnFrCdCXAZDXA4aUAH1QCHF6q4QP13qSqka3L6Cz97H/Dfe/XCA69Dbge4PqogWOxJjBbqWFq0aq7ppr46IZlOvslfumjdb++zslHIBSot5l2Gqa+p7H/52CqTh9T9g2qYYJJ2Uza2oTKOxaQTNznRv6jPX4T7AIzQTT9sVad1dUrS6rOWkGyNhqioB1XQg4TyDO/w9YOnQbyYXqLZdVts1twFuU1N5M910KhapywiAXmkgBxi3n4A/MQYQjuOciXc/BKBVk8kjuGrHA4g+NyuB6HWyjBLQyY47uFAbiFErx8Dq4n2/tqskYqnOxfa5QOqFVMnsNQCLnRG/ucwTWEnuyeJY2hMiopxiSq2RKRG47LEdYccNdXu3FwVzkwXLYidhv8SBl3UpRX3Cx2Hd+BX/LglQpwZg8jHByJOq8wjplqCN9hKLjMsOs+Zwi4QCWQSg2h0lX0GTkcQMjAuDlSbDLT78rFv65fw+CEQN51UA0E8sp6VnnUbdNT0X9ZKJorWWzsZMHnJyGmf41gcR7Mz8Mrya5yTkWy4szhhsxhDoeT9+H4nrw/ZvJvnD/ucO0YyOdCk1CCy65jmvjUn9v3Vst562CRVcbxcDi4wwxBHCJOGHOHmefQOGINnq9o/GpsXDnDLgdTz5jsPOtLcsCR58UdHifeYoaLNTU+tiOY9v+GtExEuLqBHbFqdXsAeOuttzA0NGTez+VyGXtIJJ1UIdKbTDTaPvl+K8ds93tPVHBEEe9mNiYIBcp5D2uLvuymGArkXQcDORcF14mICleS8n7Jx5DLscZ34HOGgsOx2uMorM4jN5SDP1gEy5cgHM8U6AXk74+7HNzlcH0HgV8wEXvu+nD8AlzPges7cD0nmuN5tCjUCyXGpY2Jze+efAWVAD7UXORIQs/xORyPW/NGiqOj9tGOl8OYInuieU7bJke977vczItGpcddMD+P3KoBDJ2yBgBQe/154A9OkU5MUAH/1Qs49Ivf4tRP/RvUTv230eLaywOLU7IotjxRow6wVcvageFgqgAyIGArvlwwRw0orEXEWcr8KBfAQnXt5MgpW1RzHen4ctkh7v++P49//WABniMb5RhnVd2TWAqMchqY55t0Fu670tGznWPAzOGaWNMBnWadzLRaO+kUG9sUWgFDlnSi6pUqaQrE5Lyt1w9C2SU9Zv0M2sSg/CKLtET0O8wixTR5zDkzyphmgcxWpj9NXmrCrxscDRtD9uXoQvsk7aIV+yKEgMsZip5j6qTpDsE6YCAHIUkJlsvDyS9GY/Nd+EMl8NKQ9G0czwQEdO6GbqQiXxy+IxC48oRqoVBF55kh/Y2v0dLFsecAByIIDXEWUyqZ7Z2Gc20SZh7iwsxL+l/9/9EAlIqYu2CFEnhpECHncGYOSpJM2QvnyLuovj8B7+SPoDY4YvYVXg7OkRmIhVmwwWEIr4BA5DBTqeHQzBIOTi1iYbaCpYUaapVadJ5q7ey7XPoiDjfdnVlQkQ1eKouozS2gMieVZwtzVUzXQkxVQ0xVA1TUA+ar+1BwooA64wyHXngFa6ankDv9o2DrTkbgl2SDhyDE1GINs5UAU/MVzC7WYnVHNbHH1bzvONJXZCwar/ZB0iAYB7Ptow58KMJMuHGi1KjN3DhpZtI27UBV4iWFF/GaoUBcEdcMtjkwBDSif81IlcqsjihroDprCUJ23Iy+T/bm5EyuHzwuwNs8ZOzw5MP0FMuWPNOkSCPYhsne3v43een0Z6EyPA5DJL3lDpy8D3+oiPzqeXDPgVdwkV8zhPzaIfCBVRBe3iywfZfD9xz4ORe5gTUIa1XUfCnzdQsDyA2sQb7kIV/0US56GMi7yLs8KnwvQjNBOJ6rVE0u3LwLv+QjnFkCILf3OTOfcc+B47twPDdi8gFD0GhSylGTsu/KhbPrOXC9ACL0zQLS9R1JAPrS8crp7dWkaK6fcR7kWB3fNePRqa3c4/BLPty8B8d3Eb5/AGz1KUBe/vCWAoHFmkA5FxlFPdaCy1ELZfHRiqWo4AxwBOA7kO2CE8+IiZozOZGHkNu7AvC4VBMUPa2CYqhyZiZWj3PjsOiomiFz1OJccFe2y/bzYPkSnMoi/MEiAKCmnBGbPHPzviTQ8r6K6Mmonj3Ja+j6bvo+ccuBDBztOCoD5kSOpXZEY86BNdlHb/IYcaYVlIxLxxeqnrbjR585nnacPeVspajG9CKJWQStGrd8MYSuA0CTZ578XDn4evw5ywDL2kKsocNiK/mYw8FCee2N424r/XRESy+4usyosQt4t7o9AAwNDcUMTxaGh4fhOE5dpOTQoUN1ERWN0dHR1O1d18XatWsbbqOP2cn3rlRk2RxtZxhjEEKAQyqBOGtsYwBgbcHD0lAOU4vSwcm5HMNFDwWXweeSWGDcgVvKI786jzVDcmGyEAgUHIY1QzkMnTyE0uhqaYMGlQ0SwozN14tuj8PPOQhr0gZp8szL5+DlHRnk8aX6raAUcLHfm1rgauWsnAe4CZC4gQsRKqJLkWrSFimb4EUFjiMFVuRc2fZIBp0cczwAMjjkykV6cn4LhIBwPPDSENw1J2HwlA8AAFOv/B8M/86/QW31KXBm30PlzX/BwPoyVv37q1DVtdAA6PRKFlTN+AQKZk6O0o+iuxgIBkfda9dO01T1bbT9sdVo9rPEdCBHqULyDkfV5ebeLdYAj8P8rR1UHWCLPVNCxKPtyqFgrqeUJC6478KpuqYOlybTzHzuaZLNiafA6DErFYuc16PUUn2/alWmNleBEha3RfIVRcpZ0kE190LZY0PqSeKMKzvNgigQZ2yWXW+0SUBE2ydAKu99l8OpKKJWRM8Vs55LOxglx6/JwfpFeXQa8aCU4zUmJZuhnzaG7MuxQysEWtL2tOLDAEDelc90IPR8w5BTdYKZUtcy7pg1rD9UQqAUZzyfBy8OgqmXrAXpm+NzRGS4zN4I4YUMgZDPvRMK87vxnGjtnMohW+l7WlkkDDniQPAQ3JdKzpgiyQoSMMdJ7Uiqf56O+t1yzuAJRQAG0VzgORERmCTOBQBwF8LxwfMliKG1YK6P8IMJOMXVCIurwZbmwJdmwAdXIVz/uzANGQB57RhH8P4EXO6AldYiZDmTsllZqKKyVFNqrgCOK22d6znwci4G8y7KBQ8DvgPfkfeQVZdkM4f5eVTnFlGdq6AyV8VCEGK2Jl+VUKAqEEsh89X98Eoe/AEP/lAJzsAA+OBqhLlBiNwA5hdkOulsJcDUQtXUXAtU/Tp5y+RTyThTvqNjgnDajudcHpGRPLqWpiGeso2MqXvueoCqX8dq0bPMEsRZFPx21HuuseGy9EJcgaYDYGnImlI5GEII8/wIgRhxBUT+KVeCF65/qLEUzDBVRdYWkipRFtkeBsiu3w4DE52TQeTD9Ba9kWX0EAyRsWjlMYmxxdbfJpUNqCdcIBeqeYfBgSW59PPwSgXkVg2iNLoGgyevxuApJ2HwlBHkRtbDWb0Owi+hpqSsOZejXPRQGPBRGMyhuGY9ims3oHTSRhTXrMfA6hJKQzmUB3NYO+Bj0HdR9BxTjDg6iUjN5RUUeTbgwS96GMy7KOZc+EU5EXollSaY92XEWSmb4otglarIuZnk7InPy6kJMOfCz8nJ2/UcFLxoUvRs50WTcooM4r78fq8gx5kbysmJuuTDVWNjnCOcOQJn9j0gqKAmZEfNumLC6p44YQU5V37fUk1gdinA7FKAhWpougfZ2XqGfAxr0F1yXAZjHH1Hqsl8zpBzGQZzLooeR9Fz6l6+ExmA+MCks8RzUnXICyWwfAleKS/TNksF+IPR3/5gSb1XlErFfEmSbn5eGmbuKFVdXJ6tFyC+VoC5koziTuRkSuJJvu9bzmVdBC15bZVE347i24pBtyD/3y248t75TpR66rmxqA84jxZWSQKMMUOORf86cH3fqDO5E/3rW6nMmsRsIgSIKeK45Rjaqgp73DHSr4PilzZEB6924Ps+Nm3ahD179sTe37NnDy644ILUfbZs2VK3/VNPPYXzzjsPnuc13EYfs5PvXY5o9uy0imaH0Y60bkEPxO93FJyR5MNgjmPDYB4fGspjdDCH9QM5DBd9DOYcWcQ+qADcgVcqYGCkhFWnljG6YQCnbRjAh04tY/jMtVj14TUYOPkkOKvXwRlcDeEVTBdhAPBdSYb5ORdezoVfcJEbKCE3MAi/mEeu4CKX95AreBjIuSqI48BzeFzdwLhJ33HyOTgqmOOVvGghruyPr/8eUMGSgmWPXPns2cEcre71XanClvaIG+dB2yA/58JV5J5NoIUCEK6P0CuAl9eiNLoGAx86CZXpOcz/0w/B9j8HHJmAs3odTr783yEof6g+bYNxiGoF4dJCLNVC1hYFalbtoFoYL44t7Jpp3AUc1xSIThJn+tgmlUrZpKIv7U3e5SgoW5RzOfIuV+87srSAw+MpWNZzF90qR9l+FUhTwTR5z2TwhquAHLfnc71eyFCf2aoMm1SylYGOJjhdFYjhzJBUcWVHtnpLp1/KsUckLFcqRxmA4/AKkhTk6vykM20FRexjgkUONLOIWk2MaWUjYwmFY4L84ymdvW3nxlZ1K6LZ8R24+e5i0f20MWRflj+SP5WwgX0B5JzgOwwFj6PgcuQdLoMyqqs9C6pSHcMdMF+tX4uDcFetgbNqjWyCVV4Lp7wWrDgE4eVMZo1QwSLXWuPlHJ1yzlHwlM1xZQq6JP25sim6dnD9elowFhElOvCv/IqILHOMr2FUc65X15DFhp2F4fEo3U7Oo3oe5oZc09kpAKKSMdyRnTJzBfCSDFQhDMA/eBvOkXfBF2cg3BzcD30YIjdYNwbh5qE7mwLSlizVQswrUgqQc4vr+/ByDnIFD7mCi9UlHycN5VHOuyjnXZmVszQLVp1HOHsElZl5+ZqVaZgLgWzcE6g1iM64KTgcAy7HkCvV6oXVeRTWDqFw0io4q9eBrxqGyA9hPmBYqEnV2Yyqw1pR9dYA5ZNo38OP/EbXl/Y6r+59wXdV4E5f05RgnK5NxuXczVxVakCtE8x91iIDTZzpVGL9t1adcTcizBiLqY/Nb0XEfz+NYD+jNnHGmEWaqc/MWimZrmmnbLYCPd6MIJAkraOXw5VPz3l3aZsdvNrBiWZjlq3yTEM/K41upB3ZsbcPRJxQs+t75Bx56jo/HYxLgmRgFYrrluDmZdTaLeXhn7QO7obTEQ6sRZgbQGVGRq8LvoNVRQ/vDeYghICfc1Gr+oapzxc9lMp5rF+Vx9qBHAbU4tnllqKLczBPpu1IEiaHXLkKEYZgnKG2KFVxhlAb8OGVckqlJqXYzPXrajo5TDLVvutgIO9iKu+iVg0gQk8aRSVplCSadGAG8i4Kvlzca8dKEjPCGD2Wy6uxFuAW88gN6a6OLrjD4Jek6gwARGVRRmqW5oCCL50QASwFoSl+KKDUF0EN3JE/lqVaiJlKTRGUDoqegMMdhI5AaDPvYU1G1/TtdUK4jg84dpMBDhYIOEyAMwehGy8Q7HIWyaStBbOWHGsHSZNgvFACwgCekpiH1RrCqhyDdlC8klSp8UIJLFcAPF9GXBzXdE+Tt14aHL04KfgOFpa0IkNGfozSQ6m6dNqV3kfW0mH1XVhMtE6nA2sViXyFATeKD73wl/96pnabNHK+UuHVz9q2g1XwHSwociysObIouMP0T0sZX0eeg6+Nr6McLkQLLkWKGsmzJds2xJnnxrrKGZWMTmXW6cw28de18qy99ItOUjX+7M/+DGNjYzjvvPOwZcsW/O3f/i3efPNNXH/99QCAW2+9Fe+88w7+7u/+DgBw/fXXY+fOnfizP/szbN++Hc8//zweeugh/M//+T/NMb/yla/g3/27f4c777wTn/nMZ/C//tf/wtNPP41nn3225e890ZCmQNPzglag6f/X22sVmg2HMZQ8Dg5J4Ash5+WixzHkc7CZGYjFeQCAV8qjcNIqAEBxWKuXXRTWDqH0oZNQOuVDcNd9SEaO/SJqC7JQtOfIeaNc9DC94CGoKbtRkfMy17ao5KFoFNAe8srZipHvOrXC9Yx98Qd8BFXZXIQ73PzeHJ/DK/nwSx7cUh5uXtokvUAWFsHAFKHhOQwFX9qYQs41Y9XNB7jDpR3KuZJo8h0ztkAIqYrwCnDKa+GuOQn5xQoW35/GzFuHUFt8EfmTT4Gz+iTkTvldVPLSuTHKDVXzTISB6ixXM4WyTdF7EdWdkV+rIu/QtX5U9NsR0XG1/eEJ8iysweE+HMbgcqlyD4RA3uUIhWOej2oYkaA6RSqvAl9aQWXqQerotqUCZ66niM4KnIpS0FtqaEmgyXvj+K5ySKRTYhwSIFJcKTWZLmmgCai85yCshYAPBLXQpN/7VsDNpOTyqGaL/KFoZXaUSm8HaKS9qUn1usMQBkKpGTUpJevcaXJWO9rmWsR+p8yQlprQ068gFKgCYMqmeg6PfZ6zyD/OdKezxPrRfFHcHrl5D17JRzfot40h+3LsYNuPRkgj0MyaNPGZzkhhANSUAodp4kx2ZTaOveeDF4fkeg4wSjReGgIrDZkuxML1Y2Nkyo/QqtkoIyRSsciUdG5KoOg1qd4/foLR3KV9H31ujDuyxq35zI+l8BmFrz6OHou6brqoeuCo68Zl8wJ9DbmZW6OsGmbsC+A6KkDi5sELgVyr1yoIF+dkrczCAITjQ+TL5nRsZaDwcnDWrgcvlBA4HoKqtFsuZ0asoBVvXs5FYcDH0FAeJ68p4EOrClhX8lHOuRjwHfDZGYiZSYRzM6hMzyNYrJgGdnoWcphUr2virOzJMg/lQR+ldSWU1g2gOLoWuXXr4KwdRZgvIyyUMbcYSsXZUg3zVdnMSHe3lpdWlV7RQRO1rvdyLgoFmUE1aHxFBznrmtY9w7Y6TC4E5LNnNvCihhWW0tAQZ55aSyjbGzXNiRr1CET1su370iqSJK/2Q0wDOcai5jd2vbO0lM0GsIm+2HNsj0W/jeiZDiDgMAZhBtEZyIfpLZYVedbouUhzaGw0YmQNWxzrjCEXzyyogoU1SZ75efDyWnjcgbumAsYd8NIQnLWjwJoPIRg4CbPVULH+QN7lWDOQw8hiDYc5g5/3EAahIc8GCh5OGsphdFUB6wZyKOddY2DM4h0Ac2WU2MnLuja1xYrJ/3cL8hZJ8sxHYXUe/mARbqkAJ5+LojKOSktRP3xZGJ8bJ2RAOSsAwF2GsKbluMphKUiV20DesyZDy/ixqLCoowrm+0NFSfI5DEElBHcYvJJULDBHRfkX5sAG5uEWV8sIdiikksxB1DCAcQgvj2pNmA6j1VCYSIjHmUqtiSZIBtRNYvpv15HKN5vBr4aAE6h21pxBl4d2VLTKVQWd6yZ+NVEzPy+JyloRLAjgAOD+IsJK1XQH0lJ4li8qlVoRLF+UBtfxIbiLINGRi7N4eq3vOeY+sWq0naMcS9+LE08mmmYv8pVEWhfd1I6K48mUXzcvFyxhIJ0VxrlRPDqWaoH5eRMBEtrRUsPX6UhaSeLrKFXVQViTGwU6nZVFtQG9nGsiV76OWGonHhlzgOm2I52uYJEjdDg4XDAeWh1QvSh9TC+4elbzLOoQ2Or27eILX/gC3n//ffzX//pfceDAAZx99tn40Y9+hFNPPRUAcODAAbz55ptm+9NPPx0/+tGP8J//83/Gf//v/x0bNmzAt771LXzuc58z21xwwQV44okn8Fd/9Vf4L//lv+AjH/kIvv/972Pz5s0tf++JiKygTZadsZ0ce1ufMzAPyLmaMJEp6nxxWkayK4tgjgN/sIjCutXwSnkEVVkTRaqgB5Bbtw7uyEbwtesRFMqoCI5A1BAIuagq+g4G8zKCDQCOy2V6iAqQeDkXft7FmgEfawd8rCp6RvXkWHO80LW0XE8qi0t5VOfy8BUR5/hOHXnmlXJGeesUi3KedL1IdWbsEUPO4WasC5UAQShQUWPV4y5YQRytgNaoCYB7eTilIfDyWviLcxBhiMr0PJaOzII5byPveRAjGwEACzWBnFKXs8o8WK0q5zIgUtIybpRnQShMB2jO9J1nyn4Iqd51fbWGcCFECOa4dYtoXQvFrr8p0zMlIaVR5QLVMIxqqLIUR1SpoWIRb8CQnPp+uXnfpGrySs3YJB0IcfJ+dH/8PKAVdNrGWQ+6k7BJBd/BQiVAxXeACky6Jrc+N/fL0WPW3bLrwRTx5GhFWd6HpwJQsfqhPjf2yMnnlKI4Ig2TdUQjsjGyTb5qDFTw1e+vxhCodDN9flFDD+uaa+Vc2vhV2iv3PaP2c/I+/FKXyrM+2xiyL8cerZJorR4r2czDYYAjamDVxYg8c9VajkuBgFF25UuAn4fwighzJQg3Bzi+WaPq35GckwSqITfvV8OIPONWADiv1qQy7dsarJ2+p+etWjXePToMAMi1KfN8M1dJEs2LESZARJrJuotCzj0hQyAY4HJUgxChdW00saeVvczcC/m7E9wFc3wINwcR1sALQLjEo46lSwtgeQ6BnLm2TM9BIkSYGwQ/6VQEjCPMDaC2VIXHGcpFD4MDOYhQoFaVttTLuRgcyOHU4SJOXlPEugEf60o5DPocuXARbHEG4cwRhHPTqM4voLpQQ6ADYpDrigFVAsBh8v9XexwjQzkMbhjA4IYBlD40jIEPDcNZOwq+ah1qxdWYDxjmq4GsdbZUw6xO11QPpONyFbBXz5gKkrieY4izVSoAV7SEFp7KhonNyNY9N36U68vmMbDIUvNAW4o05bto4szYKseN2y1o4qz9H1SWKlrP+0zZb50+aQJYut4ZEE/ZTEAwbkQAMVtl12uz/zbjkmxHVDNVppeqb2z7PM14yIfpKZYNedbKI5Hl0DQ8XlY7WRGqtsIVEz1m+ZIkRQol+befBxsoS8a+uBpLbhFzizUs1aKIy6qih8qqvFlg6klIq9LWDOQwOpTD6oKHcs5D3mFwOSRhB5h6BLxQgleaRTBURFCtyQkrX0GgVvOOx6XqbLAIf6hoikdr42L/ABl09FguGgfyLspFL2WClFGFYsEzNdkGVUpP3uVmAQkgigSp2l9ucQ7+YMmQfGG1Zmp+uYp8EUrCzKpLYLVFeDyHJTBUAoGZSiAdGzXhhmCo6fGpCDzATTQj9Xccqnuo7zHjcjIOanBcH9zxVc64TKGpMq0usEg4xkzjCJOiYh6iyMHi+tyrFfBQldp3PTi+xXBphVpO1pLgpSGwwgBCtSgBl4qp0HKW9CJeKjJqGMi7CIQw8vLo0HHFVsF34Tu6g2hKrTD1XOkaRmaBPyAj/MxhEGqR5Pjc1NnT3USdvB/J6d1IKi2vmVwwuTo1VqWN5T0HNauOEbdk4NxlsZQsTermrWilqZFjdcEznXdc30rV9OACCKr6N8RlTR9fpzNbkm9dHyHDwLWKoxG1AYAbbrgBN9xwQ+pnDz/8cN17F154IX72s581POYf/dEf4Y/+6I86/t4TGa3YJTtaqOEwHU23aiwBkshZmgZfmgGrLUpb5npwhlah5HDUFmUhQsdzwYtFmVoztBbu8HoExdUI84OoBKGZy12HoeS7KBd9VGohHM4w6zmoBCHCUM4jBU8q09YO+Fg7kMOgim7n3WjBa1IOXA88V5DzQKkAf6hiAiRuPlJ8ugWZEugPFmW6ejEfc3Zi8wWkw5JXKuiBvId55QgsVLgZt8OZsUFSHadSS7mcr4NQqs9CrwA+uArO4hxyobQ/wWJFvqbeBz/8Lpx8GcVVH0IAV6X1B1L9mx8AE6EMZrgynT6sRSkwWgVmxzgYA5xQOi3ccQFNoMGH0E6UCOMLZLXOcDiHJ4CAC/iQ6kMoAk2SZyyDPGNKFS3V0aa8RDJ9UClInHzUwCb0akq5zk0TGzfvG9uk5/VkygszRJ8q+eBIu5Rzayj4DoJQYAEwz5WvVI+afCp4Mhhip2zW1QtTThFXNdh0sCNUpR70nM7VuL1i3qwpzDmkKFH0OoHzqMacTf4t1UJDmtnkmSb9Cr4s3xClzEaqA5u4NIoBpaDgvmu6b3tzjQspN8PRsDFkX5YH7EBL1n1MBmPSbJEm6M3nYQ2sVpFKKb0m5i54oQQRyE73zFHp5poo8ovyXy+PmtBpjMIEPly1PosCQPWK2ZxSMsezIVBHpmjlGXM9CFuBlCBSjOJMp/Z5PkKzHrfWxYyBM2FsjBcIBA4AhHBUYMS+VrqmpFb2xq4x092MZa1eQM1ftVqkiFN+I6styfW8l48UUPkhBH4RgruYrYaohgKew7FuKI+pNVUccjkWlN0byLtYN5TDyWuK2DCUx/qBHFblHQzlHLDZ98Dmp0zKZm1uEbXFmlmv62ZCDhNGfTjkcqwe8DG4fgCD6wcw8KG1GPjQSXDXfQjuSR9CWFyNWm4IswuBpTpTKaVWAxXH4YAPOKFSnqnAtw5qaV9yMO+ipMoQ6XuvydxkGRlh1IYumOuChZIgRRhAKELWpPBq1Zl6RqXajEf3Rv+/VrElAiitzIlppFlyDRdTYiMi1GI2OKk2S1GfpZFmsSyejHIymjjTdlSm5jJ0UfKMfJgeY9mQZ+0g+fyIlPfkBynEWViLumSEtYgZ1uoi1wUXQ8a4hLkSwtwgarkhzFd0/S2liHIYBnIuMJRHwXdRqakIvSKtBvMuVuU9rC54GC56GMipgvwOB6oqKqSjQL5UlIXVWhTdz1cQqlQMuUiT5Flu1SC8kqphUJDd1+wfqYn0uxwl38FA3jPEnsOZ/H8VsfBdbqIJ5aKPkukKmug+qaMIubyqDZdHWJWLdO67CCu1KIWhVJApIsogstoiWGUBfiEPV536XDXEQhWmbhcQOS45lyMQDqqhNA66oH/M0NkTmCVPB+NgwjX33Hfz4IzLY4VAEMrOnnb6p46wOWqS4gxR2iDjcmERVOTzUZNOCuccolaVRtWWHev0ztIQWHFAyuE92cmoZtRzUcROKgQd87xU1CJ/gcnIviGiODP1igbzMq3JlsknVVu60L9WNerUnbAqjXDgcYSGPJONMdxSXr4KigDNFQwxK3h8qtB1bbSToouQVwKVDs0DOIEiDRgztRNKlrKkoLrK6migzu23EaVfRkWx3dBHTZ2jLjCr6/s4vmrw4GnlWXdFnG30IFhM6CM4601Ev53v04gFa6z3HOt9FtaAWgWsugBWleUCGHfA8zIVnLke+GBg0uNZvgQ+uAq8PIwgX0aYG0TAfVSqAQIRBRmKHsfaAak60/O5TsPQdQkH8i7WDuSwuihTQ/SiV3dLkwNWC1c/D14swistIqzUIIIQ3HMRKGIPgElPN/Ueh3Saej5a5EKrIyJ7lHe5muc8k1ZnN+Ap+g7KRV/OcV68eH4ISFvn5mRR5+IQRLUKH0AwL9NfEYYIp9+H+8E7cMMAPD8oAxeATEvS85jjQ/gF1CAb1Zi6NyL6nVdNtzGgGiCevun6ELVKtH5Q188shnXqpuObEgoCUXoVXA7PAQLlPLicqQ7gkjDzuUy/0kouhFUZKDIqZ8fUjuG5AsIwhKMUXSIfmlRYme7oRWUENIHm5+qUHPqZ5Zyp2mvMqLayyCc9lxfVXK73izmmFvFkUjddz6jtHUWY1XgF3Nf2QzXfKUT191guD+ZptZ1bV0qAsYg40wSgTj0tqhp6Fes8IrvlJtR+kRqa2edh5BiOSX918z6Cgo+wmoc3UES3IBtz4iFLzWwjtknCxpj3VPkTez0suCPnP8ZlBIBxRUK5ci718oY4E9yVLpIVqXbU/J13HYSh/HuRhfCsmp9yDSoJlLzqIi3X65YqzgpEM1et0XL5yHcLg1TyTPsbMQVS8tqwiNzwHKU8A0cYCniA8XV0OrpO6bbT8kIzRpUe6HqmxE+M4FDfz8IaEFTlOTkyM6MaAoFwUKmEmK/KILnHGVYVPHxoTdEILHSZk3WqDuq6ko81BQ8DPodXmQVfmEIw9T6CmUmZsqlsMCDX6oVaCCCuOisN5VAcLmDo5EEMnTIs63SPjsI56UMQgychLK7GbDXEXFXWOpuvBpir1FCpBUa04CvijIfR+eqyENo/0cGvcsFD0eOGODOEKUus4W2Vlb6+vrRfolYBU0pDualSmzFm1GXRv455fiISql41zVm8XJMss1H3yJhtY88RoiCMVnvrAJAmzlgWcabP1Q78p31u1TvTPntaSRz7meaQvyeEAkHdlu2B7EvvcFySZ0lkEmepG3NABPXvaYZbRZB1BCL0SxC5AdnVRBWt10Yk58hCv5wxE5nVkdu8WowN+A7KeQ/lvIeCy+WCmEfjY46cMKRaqWjSF7jD4VZrCfIsSpfkpSGZFugrh8BqMa0X6zpVRjorKqWHx8fqK2emXIzSefSkaEcRTLFGpZITlaForHORIoH70qly8jmAS3JDRsMW4YoafIejEgrUasBiKCBq8Z+zzF8XhlSRNRyigv5RNFvECDSt5APjMp1GuMYwu44Px+GoMkmcBSFMnjwQJ89czmJS3DBfltGlI+/ACQOwfNGo8ES1EimaeJTyJNV5RUWc5QA3L9NSVbTOrlmgnY2i72DBdzGYD8190Qt9ACY6rlUZdm06j/N4ZzMdnVNROzfvS0KslDfpPI4fkbSMc7i6CUI+J1UkubyV4lMvl+c86g6nibOBvIsgFJiFdMBMp1CV4lOyyD+tLMk5HDnXkc6W7nATU57pNB1ZT8nNy4WEC1nvTKcqObp2Tj6nSHBVf8lxYtL9TnG0ojaE5Y9MRUDawkrP88qpMWUCAPl8ux5YoSTJeK0e0uRZcVAqV1WHLJEbwFIgVMMaeYi8Ktg8mIvmCRko0eQZN3P8UN7FgC9rig3kHGWL4kpbHcjh+RK84nw0r88vIMxHNZ20PfKVEprlS8YexdIsIOcLbY+Knpwnapb90bZI2iYZyCn5UgGdU3McoIgtrfzKl8BKg+A1Segx14MIJfEoqlWEU++Dcw6ntihtuKfmMTevCDgfAXOxWAtNrTOhUiNM3TOuywUI1JRDaNI3uUwZF0Ckfk6BJtwcIQVnpk4ek4EUlwNr8i4GfY7DC1LV7vKo6Y3LGVyGOqcYQFRk2Q/BggDgHI66Do4K6Bhle64QlROwGtjYi3y9SNepizlHK9cj1bptkzRppuvY5ZQDrdVn2oGJLgZHHfGk0k0B1HWudpQKWtqlKI0Lql4bVKkKYcYfBaRc1UVbEn9ymetUArhcO0eR4nswL38TmuD1nGjsdcSG+o1o2+rkc3CL0pb6FrncCcjGENKQRZzF/rZqMWkVsanb66iUR7szsCM7S+qMCHBXznVCWL8nZlLXQiYbpPFQkU2KPHMU+aQJa5fLciwRgYbIY9c+hArSmHRIxzEdGOUXO1HReKVkFvZ8lVDLcjCpwuKAp7RuTiBUkFzAg24mIDtC5l3HBGrtelcCABwXLFD2y9FrezdGiph6ntpP4CFCyNqZNV2WRpWd8Rxp804ayCHncizVQrhKYV3OuVg3kMPIQA4DPseAx8GnZ4zqLJyfR7C4ZGoqa/V3MRDwq4FUivkOckOynE9pXQmDJ8vmQoX1I3BHTgFfPYrawDBCN4/aYoCZimoSUA1ic7meD+0O17Y6t+hHqvHIT3Ss2stK4ACtOrZuvCJOwVWpA5VKzFXwW56bE2sAYKdmxu574v4DjQmhtIBqatATmuyN3rdVZ5wh8vUaEWj6fO3PrPTMWNOLlHPR44sIQJmSjFAqO9KItlZB9qW3WBbkWfeubQJNHm7BmKzGYf9ItUMDRO87PoSXRwCOQEWnARhDkVMFND2HoRpEkXa96C96HAO+i8Gci7zLkFedcJiW/goRkRzaYbFqlejaZwBM3RKvmIczMAheGjRpm0KnBOrhs2h8Rc/BUhAiKEqG3yZlouix6uqpOm7mXCdWEyCKYrmxwvlOrQLd8dAumm8Wup7slMKEAAJZhyHvD6ASCNSYLN5bCULTOc6uoaANnZ6UkwX9UxGGAGS6SvQMCGWgXfiOj0AwBIyprpcSXF0zI4FXLb4hQlNLTrg+hOOB52Q0T9Sq8torh01LjnmuoJyzvOxepAg0/QzZhS11wwBPpVYNqm5dDq8aQ2s7lkXlyEjJdOSsSPWIipLYCjxHOuE8n4eTX4SbzxnyLKhEz7t5tkoFoyTh6tmC6yVqDKjnUUfyHGbSSAfzkTGuONwU4ba77CXrJehov4kCmt9oFKFhSknG/Dx4tWJq6um0UPu5s7v2gEcLnZY74WTgaNQLIHSPfqjPGs05zYgzu1SAUTybA8tOviEi8gdAVNA9X5Lzh0qrCcARiFDVvxByPapSZgIlafIdjko+Trqb+peegyG1aC96jqmnZTo/a7JaRfydgcHIHil1sRm6VkKX8nAGBiJVkw7mWPOFbY90wCnIS5Ko4MfHmnO5bBjgyY6UeZfX1d4UXDp+PF+SdXMACHsh7nkQtQrE/IxU9kH+HkNfzc2Oi4C5MogTqvo9KfdWABAQCMFMB06HAQFj4Mq50i4CE7xuvcFEKJ0FcOWARp3gGAMCJm2Br5RaeUcGfjR55nEGnzPojtKxZ0h3nMzJVCwOQNQ8iGo1UnIknVBtu3MFhI4vHWi9mDcVVbSimKvyDSGKfohApdok1w6afJLbOojSthClPNrXxCKeuC9r62mbFHrVSDGn0jr1M8aLRUkAKmVkqmqOqTIQlsIk7zoYzGsHkZvsAPm8RWUQcoqE1sEoXcqBIbE+1QSgUkkwPw83L+ue+tUqugHZGIKNOrPTjDgLEw67JpiF6u+uMyS0f6M7wOs1rknZTIxDBZcFBxzuwGFqDakVXYyZNairVKu6Ji6zx6nHxV3ADU36nqjJ+tIGCdJfztm+Icuj332U1qbX7yETKtgSgov4VdTzWhQsjyulQgE4iqxhan43wRF9ydV8mUzHE9B1MxUBKbRtdjDgy/PPWzXKtC0eKfkoeVw2FlqaBVuaQzg/A7Ewh+r8IoJqXHXmqRrYHjxTbiU3lENxuIiB9atQ+tBJKG5YB+ekD8EdXo9acTWELxWx1VBgdinAQjXEUi00ijNXCSqSxJlWrUfrexn8tokzqTaUpKkmTOuCJvoaMVX/TJGRQtkxIK7cjhFniuiNPbPGZqlDJ79KkaKhIoKz1m/6bdPoQGsPtO2yVGfmEEkCre6g3JDUaedvKzBjULWZzZgABNA+nfoNgiHogiwh+9JbLAvyrKfIeqg1tFQSoZwgNUmSkOZq5Rm4K1s3q48cRegUPcdMhNWQ13XM0sTVYM5FTnWrMZH+YCmeZqgWhKxQklF6zuHmF+GpFE6gPv2CF4dkbTYlaY5qf8DUKdDkRNFzoHkGTZ7Jc4kmx5LvYNB3jfLMNi7muumx5vJgFUmwAIDvewgrKp3RV4tjTb6osbEwAAsDuEw6C7VQOoJVFaGwGwQA8hrnIQkYN3P2S5HGilDyOxxgQSWKdKiXw1043IUAiy0StETWpPKq+8MXZ+RXVRcjyThj4J5KBVJpgzIH30XoSJINqmuPcHMQbh61IF5Tx5wnlwv8aigwYMgzhkotiHXA0aoM3fzBjvroiE/sumhVi6sX+D7Ckqp5wTkcZZSjbmGRqpHl8ubFcwWE2mjpw6sFk1YnRKlYUWpwpRaa8duFmbXyTNdLyKtUroggZebHJhiPnBRdwyyXl6oz1fkPgOm2adRyubxpbhE9I03mBQIBTch5Cw3VAOq9mMQ/Ua9KcAcQioDJMxlIsecS3WTEk/UShevLZicW+a6dgZwrjC3KuzIYoWEHeopeZJN8J1I2RTaQyZokKmWUVatSbculCihMzBm6LhsvDimVXAnwc5HSwbqmMXsUWkRfUE/06XGa4tOMRfOblXLEcgXZBCBQRFGtGg9mALLjs+dD8ApYTaYw6aujyf0spxGIrreO2tZCgDOhaj76Rt0sRIjUgiQilAo4rlKDIMCEXJBL8oxhoRaq2mfC1AHVyjMHYVTDyO6e5zgytSgMgCCQV8f1wPwotdOUEnDVtdLkppuL1IGxeV3eK91db96RKcGhcC3lWWCRZ45RHWu1uufIshQsmfKo69OY8cg1hFdShCfnCKrWM6OesVgjCjO3KyWKVjRY4CxKI8s5HFVXmO6m85UAvhttb6999LOZTEEyjpUpkqq+UxOS1Qp4MZABInI2CD1CUzPUQCAgGAMTiAgHvQ1HjHySKZ2RejNt/uOK4BdM+j6hUOl9epxMr51lRo2eu2Qjg+j3Hylu5DqZiRDMM1HkOPHHo3WfUI1NYuojTTQo34GrselSBoAM2NoqYl3LWBOBemxpJI+xMSptU9jj4zx2DZMIlT0JRXQtip5MZa2q89XvlfMuBnMOSh5H0WVgC3Ng1XmEC3MQlahkAgDV6CtqHsccBsdz4A94yK/Oo3jSKhTXr0VpdA2ctaOmu6bIDWIhlE0CJhcCLAahalITV5wBiDWziTUxcyPlmU2c6X91irsOOCTrRGqyURNnQn3G7PVQQo1lE02GrEwhpKLnkIFBgIlIuaUVkoAlfkzcrzTizCawzPNhrd9SA/GMS05BiNTnInk+ZvwZz5Eem4AU2MggXuvrU0L/cczJs54+C82IM3sS1A87UmSgyqjoBZoIhcmd5pCpFnKiceE5oezqYtexUoqgou9Y6Req8C9D1LRAjUMvbnUhes4dCD8PnnAGmE7xUSkrLF9SRT8lyWcbP5l+IYmJaqjTNF2TJmMWwE5EsBWM0+Io6XWkBIoiRq5JS2RhIMdaWYTjW5Jr30r7c6OcdoQ1IKjA4x58B6iGDDmXIRQM1RAIGTMKtDAUqLIQPATc0EHIIydGCAFdBys+AVkLCk2ghbWYg8NYzSwYHHviMuqQ0DSQ0MdigUrHULUNYjJuF7KGhDYMWqml04O8PCpK3VALRcxJ0+m1OsVEQxKcss6Mhq7dUvAcDPhSIagdTCOR10O2iE5TULpYhJtIj9FFpbkvHWFXOSo8X4pUjdyJkbMaLtddi3gsvUc+e5I805EtM3YVvbLrJeRUtF8b3Zg8WithLDWMjgI6ThXcknwb9WauYArMMp3eYykrOkWI9ug3ouqOHdpVn7W7IMncPM2hSWtUYc8XQDSfuErtZNVdEZqM5y5EIOcP7QBIlUyIvHJmllgIVzDkHGWLjAInKuasgzmaOHMYA0K76UlcXSyH64Cr+o6a4IvXdxyM0gEtRZOedxmL7FHRc6z5T6qDovugCDZHL8wdExyITT9KUSEcX44xDKTyrFoBSxBHunQAc1WkO6yBBTVwx1eL/OYPirY5gQAcESkMZMMdRaBZCkMzRj1c9XL0AiKUXY65LVIU0fpAO10ug7RFWnlmPUuCMaXOUKm0ni+bIgRW3SAnWjckVRxCd6/jXBGDSnnGYIrm5x2OmgrslAuesUv2vC6Js0h1ltdKcW4pGpNrHR0M8eV87kHaJEfZJLmprHnG89GzGKVtqjIVmkADYs6wThG2iWUApl6bhq8CNzmHK8U9NzVptQpd+uLRMyWsACJqsg4dh1wzOl0WpCEbQwAy7Esz36buIDxSwSSVX5qcsIqxJwPJRvWCiJhwoEgRFt9W2yNNnugSKEZ5Zn+348r52g5Eq8CR2Uyn8GmCX9sUTaIlrhUDVOomAJXiJpj02XjMvkiSzx6nrZIyl4BzCOGY0i8QIZhS28VrbrE68l6dUqwGWxFyjasbrWjfMO8ySZx5HGxpVnZIrSxJ1bRWoTtyfe4VXIhQwNFdg31Hqc7yyK0aQGHdahRPWgV3eD2c1evASqsQ5AcR+kUsLAaYXgoxXw1QVcEqR6kFfdUEwi6fIP92YnUhfTcKaNmpmloZ7ii1l63kqwNXJXW0gCWh3mtImiVfKddd3mPpQzJF+moCLYs0s+8X9LNkqc70c2MejrTfocUrZKZVZpxTar0za1y26gyoP492QPaltzjm5FnP0MS4mLaxnMeeirpgsZZVqsVZIKLNuVrYSuWLlDDzmlTBxJRnuh0yl9EY3a3GYSymaopkrGqBm8ubhZgI8/XF6HW0UxFo8PNRlJ/zWARZdp+RsuFqIICcC6cawOM8pn7SyidNnBU9JcE10SNbCcTAtHNVq4KHoRyrJ9NFAGn4oihxQaXPRcV9mQjhODKP21fFPXXEY5HJDjVhwvMVkBOh7pQZcNmhzOEuwGux+mZ2ymWMQFN10KQ8PbTaJ1uTsN2J1Y4uaGctDAGmVCKxZ4sZ465VEXClckQWpBZGNaJr68jnSdXHc6KjOYyh6kaprPb7nsOMY2mUGWahn7xXUf2iZJTfTsFiDjddUp1iUZKitqPCXUM22jXiGLOLgEsHS6di+W5gFAqANMQmJcuKXhU9nXaqf1uoX2y5rlKRyWvKc7rTqR/J6a2U2VhdHHuh1e7iMwGtTmlne8Kxg14MZZFonUTw2nFqWNbzpqOwjlqY6/kkWVNFOwsqzVBATmc6JUF3YrJJd0dFXPUiXafOGVWNGwVzfK0QYJCEk041sMkBTQwoYkqEQbSwc32TSs002Z4vSmJG1+BU4CoVMOdyVENuCvF7XKq2QyFMqr6rSBs9P+hmKIbI0NeHu2BagZYvRSS7XTdHpW4zx1EEl4gINBFGHZZDwGECIZPp/E7KwxFC2yCAh0AV0WToOKrrJkuJpqtnhBtHQZYjCATAePzh5CwizyRxVjGqs7QaKkypn83i3bo/pnNZUsGhyFh5n9R75vujaLtWK8o6nY4JbviupQ7n3NT00euHnBup1mNpm/q5tzqEmnMD4Km0W61srAuI5EsmIAc1dn0d7DpyUgGjg4aibl2mxe26oLkmz/QzZ1TQnJmaN/JesmjN4LoyMGUFc5jnxwptdwKyMSc2uhYSWL6NTkRPigNiNZfs4LP1G4KwVC9CElGA/Nv29TmgshAS5Jn5/SCuMLIUb5qP0EFte60Wq3ulU0utus6C8dj6W3cmlNmaDOBy3PWESZzos5WlBpZKSo/TpBha20Q1pq231dzNlT1xOUPBlWmuntA2T86ZvsNQcLmsg81CsNoSWFBVTciU2kwFtb1iXs2LDCIQSnUmg93+kGwgVzxpFdw1J4EPrYFTXovAL0HkSlhUNVLtsjh6btOKMzuooP0+3UjIV8GEnBVoyBvfNqolx3QA37Ih2YFDERG75po3IM3sZyd5v5Rt1T5iqAgnziy/PeOXZavNgDhxFllr1DcLsM/FCqw0ZJuyyEBz/vHfKdPsH6JzcWA14OgAZF96i5VDnjWCesjjBFpYN/nVdfNAvKi8jh7L2iVyQiq4TmwiN5F2pogzK/9fGxMkFthMqbnscbAwjKIPesGp1DRME2du3qSWCkt5Jgv/Suly3hEIPavFdBDGHErtrOTcSHUWLX4t1t0yaFzV+ogcFaVysv5fL3R5rlA3Mejr4TABjzNVCFpN5KrQZxI6bUanPtZCwHEksaNTM+XiPDGJhaEk0ADl4PCohTfjEKGeOaOJMEmcGSUXU+lN9sD059qwO55RRQSIFvC1ZLMAruu6sahDkOfA40Km8Dgidp9MQVblrOiXq54xO3qijbtuRMEqMnIfAvC4g3BxESIfRfiZw6P7pZWNOVmoNdTOcGxij0f4tVMcCscY3yB0YjUVtGrOHrtRllhRQPu6mpdSZppFBaAaUViOshelqGplg+zI1p1Do0HFNo9P9Erm3pQ4a1JnUx7EWiwBYGn1aBApz2A5DPbzZBwAFqXQyPIBLJM881VtRKM40yqnxCI3Zo/0+65nUiLNGHRKuN1YRJMalqJJj5cxOQ/I+mzye6ucGSIN0HNKpJLTKZt24xr7WhoSzXXBkJfXT9VAs9vem0LEeldFojnchRNKp8sRTNWSi6fGAjBOpH4/EDL1kgu5UAdUIEc7WSnPQEzhwGRaVXJxGqu7qRRyUTAncVzuQoQ1SaAF9ZInk/6rbHZMzajVHKnPFgNXxcE1sQlE65dQRHV7JHkbpfCY+pssRbWl7pkmnoQmxoCohqj1fMXSO3UwLq/SgtXzlUx50eseWTNOrX3CaPzVMO4k6nqB+nnLO1YKkq0+sBtMGYLZM+egO8fxapfqZrIxJywamqmsWmeZB7PIMoT172uBgOXnJMeiVS+akGJMEVIJhZoO5sSIM0t9ljy4HThi6m+bmKhL39OppXaNQ/s3z6JAkhAsUp4liD4gTvbpwvb6fGOrRE3iCB5N8No/UN9d1+kXEWnkcsCqhmmUVkDkF/qqjI/vyDI+LKjIIH8YGLvFPRduqRCV7lENe+yUdn+ohPyaITir1oCX14KX18oGOX5RlouphqY0gX3NPEfWWbab9gAReab9E9O5WAXudU1LrU52ebyzcuZzbO5bGF075sQ/t+vxJYkzfc0T99++/owpFaNSm5kAataY0HjcrawdBeNRimqGGk6fQxZx1gh26mlDVV8LIPvSWxxT8qxHfk1bqhIj8+fp70cMd7SojHEpSiEDSEOiz0FAaFFzzJDowrWMpZyvJl24C+aJKLLvekAomwrYEeS61AsvF0vr0bOEZs4dDuWsyBOohiGqnNWr5KzmAia9lFusu4bumKKj+qqmGapegsjwjVooiholmXXEov6yrhkHZ0J18omKkZprLKQBD0JZL6YmZCdNqZwQ6j4ADPEUlxiBFrvplgIt2YE1JdIvrP8379tGnruy+Cp3EWjFWag6tKY4S5wDnmAIOQfciDh0QxZT39mNBbSasOhFDrFeqCTJJ6E7m/mRiiS0OrKZTa16YixfMspG4Xgmwq/VL2YfaKMqU2OihhnyfX0PgbhqTqsctbMSpSohStvUt0ArHV0fcKumOHbMSTbnkHDmYymbaGuOSAMV2zxx0ZQ4axdpRIsJdXJjF0wxZ+4mFr8qDVLo0cl50xMq7V2t4+xizq5SQWu1ma5LEwvmcMseqS6O4FwSZ9VK7Dowz4edugnduEYHcxJOmbSHQpEa+jyiiLz8O1JsFz3ZtMYUbreiweYaqRQgONKpYEBMwSDTvW3FQnQGLKzBcV0ZZU8sKoMQUdoPg7HrOnWTCWaRaOabwfVC2o5I29dM3Tt9XHslrD9jgCTOdIMArTpLBtwA6Lo8UYuweCRb2M8SY1INnSgUrstSRPcAKqUHhujU77uOtEt6/DotWNetzLncFAy3VVtmzBbxpJWN+j6xWrU+5dRqdGB3CdXBQuNM6fvEtIOsAjshQ07IUVSZgCviHaCT9Wk9u7wGV6lI1qJBBslkMXGm0mSZIv5Q9cC6qeYMsjEnKnrhBzUVBth/83plT/JJ4gyGhHC4mvMAMIs902l6es7QPo753QD16+jYmhmm0Up9QJqZOV6TKUL5QknCX38/TFfCeqJPb6fJPj3WVPVZbBwhwJz6WlaWnxgdWzUt4IAuVqMFRCZozqJ6lr5jpeYbGxyl2nulgmnE5vgeAlVXWjdT8Up5+INFOEOr4AyuBh9cJbtzuznZYEjECRNtX3NupELTatxYsza1FtcBEm2Tdaqmx7XyjBtCx/Zv69RRiWevjs2yg4bJZyTFttXdJn0YJklLBmEaBuj3gXoyKEmO2aqzeMpmhoIuxUdMfQ8WKWx9Zs4tKwUVMn3anFuX0z3Zl95iZSvP7Ac88XDXKVIS5BkAJQWNL+51FFpP1FyppFT1tFQZszQoVj2nxOJfF/XkuYI0EDpd03wxj5Q0OvXCzUULYUVsCCGsaL/8XpdHqT2eYKhaqi6tTjCd0FztZDEr4m9dH2sxDj8XOSuJH7/pFufnYKsnzOeIojQOB9xYlEZGkIS1rZYD6wktVOcZCiaLeSoFR0RkKgINiK6jRaCZBhH2M9EoApA2cduOikUwwfGVOs5+RbXOTN1hWCpGh4FzB5zJ2nleYs6K2oFz46zoyJV2hmPEk5qYoxTbinFUuFpcxRwVW7WlU2P8XNThKHH/7Ah/3uGoBiGKngOuovlVHtYtbmwDbBcdjVQwCYLZWpww11WEsiKXdT0js6kimD3VZVOpNCPnsXF0pxVQvYATEy05Nq0QacyqsZl4v+5vO+KeIKE0waKj/WGDeROwAjmK/NApNea3lpaKwLhUCGk1lwrcRJtYNTg9Kw3Q8aK6NLYS2nKwpAiag3OpPAvryLMo0q1TQ5Lzf911Em6MtDJdrAFDtKRFrPUcbCug5e1JT/vR0MQZQlUzjUdX3BBodTtJG6NJsiTMvRChJM6CWipxps9dKzeS55RaeNkqKWCIM6tOagiY42lHyNRMdSL7kyypYJNnmnzyeLT2SVVtaeKpVgVylsPoelG9OkD+zWW3aE2iwfNNuqlZV1iwHVjP4chZwZtqqBppWNyq/axpmxrvGJfiBDLdiT00nbf1b4FTzTNCm+iaOLN8nBiBBsRJNGsNVJcSl4CpE6WIJqPiYZJEi7aLfieMwaSc13Uo1ONU6XpafQahahyKxLyuxmvmbathgBl7nWoXRimXRvSZMSMaazJYC6h5UI+RQ6rPkFDwZUCTc46a5BmipjChInT057o7pT2/x0qtFEpgtSr8wQDccxEsVkzzAObIBj5u3gcvDUnSrDgoA95uXs7tjmp0Z/xB+X15VXvNvs9BCiujlbket7sXRw1VPCdat3N1XetgnrEguu/yJkhYavs6/9smUO3PgYj8TVz7KPNKcaiI6p0JpNtdIPs3GHtf1zRNnh+iR5El/Mi6mm72+aW9bx2LM1XaIfEb7MaTIfvSW6xs8iwNWSSJTYYgzvLqiZkxVbiPq7QNHtVCcdR2QESUJfP/k8bERDJ1YUpWgy6gCSDefc0YEdekBtq1P/QiGLCcFWVjfHC1+A3hJYIAuvC0jrhqZ8UuQB+XVDPobjnwpSPFLCWT6XTmWhFi7qTmdDNIdVkARI5gqGssWJfKinBpCKhW2YLJlBndVlpNYEKPMXm7tROTjHYln4Osv829i5yzZI08TZjZpJn+NqZnciBqqc05qoGAw6JIv42IPIs7lkZR4ljKMz3BOy5E4MZTgpUSTdTqC2vbHdl4oRR3htXzZ6dhcfWvdjyisTLlFEfjT5JnWqmQqSwxO3LjJPFcIXLydB0fu2GAJtB83Y3NqUs37QaSnG5ve8LxjUzHppPizXpesjsypQR37NQEe86M/54keR2GytZwAS5YamSV2XaIRSpVHbyIy0mjQtL63LnjRDVqdPMaINEJzVVdhX3o4s62g6NJGYdB1hXjgMNlLZj6uo48sp0sCj7F7oWV3sFYDbrxggmMxM7HItkS6mddL8VRamw3WnUjCOvTfjRC0zFTyHT7MEmgIUrVSab2ppFr2oHS6UtW7c1UclPv1iAAaJ9vrF6QlQYlGEeQOLwuvK2qCADg4KHlaIl4WnBUw4cbNWO8TIV9L/Q6xrFsEo9UZ7YaWisbNXFmB+L086Xvp4B5RmQKrvzavCqGzZkADxk8HldD2+SfVnG7nCliOj2YY9T3+t74ueg3YfUZ6gRkY04sdEyc2XYj8bch0IC6oGEyuybN19FToFZzhpC/FbmOFbDq7xtyOUZGtTJ2m/zn5j/R53qsCeKsbv5G5JvZSjkzbrC6rKEk4RcFDDKCJZbtrnvfur5m/tELWSXP5UKrurRtiHxCmdZq2X8mA8C8UJKZFSqF08stws0vxsgzU2alNAReHAQvDQEqKwm61IqCtqc6C8meAwNRX2MagFHmRkEv2/eQ6jPtZ0YqLfmvsM4XXJbGYcn7rkne5P1WqCPOkuRvBrT6jAuRSqAlYdY5CWOvfc2WfqPqGUkdV5L8SyMGW0TH84UC2Zfe4piRZ90+CK1/UcoiNu1z9f/2A5PmjISIJms9QdsnY0/O2mHhSDlfPVnoml2IUhtMcUrHM6mCwjYkulV7ijrB/OjVpCcYAC6nLIc7ma2bdYpplAbIYgbFFMx1XEDVjGEA4HGZvmCdWqTE8g25pFNH7AnGLvRpO4JBoouDHqOOitsw14e7sk6cLvJpddls6Oxqw2j/q+9P8n6p6xC7fxZxJhCpFYNQq+PqpyBJBsoVvudw8FDAYcKkXJnro4agnUrtHOh7FCnPrBSs2NgkwcpzMm6mU0xYmE8oz/zUjmxGdcbjznDTCD9ndU6KrXDUzkoryhLj/IkwqqtXq0KE3HRyNYStpdCsi/B0mbYZWgVXW92ecPyi64UK4/VpNBaBFn1Rou6H+lc40TMcC+Qgchh0qopOUdEq6RCa3GZxAo1F6dHmtxYr2htvJy/X9jVJXNjXRG1rpwAaQkYFSvQ8CEROla2UC4SsyWkr5bRzk6aUS03ptu2nLnzfIE0otnC1vpcDijCSpKQm0OxrCiTmJz0OIYkmIQABgcA4SXqfxPojq36RTZyFNUmwmhqc1j3K6BCur4ntZMZqcdplBdR1CNT56QCPhv3MQDVG8nhUTkHDJjvtYE6mslGn2YoQcMO4sjGMy7ZMnVc9nzt+vMlBouueHDez7iUQMqFquIZ1tVSlXYKyo1aHUB4p3euusEXEmiZFYQ0mIulW6sbUDsjGnDho2b60um5JEGiZ29j/NoAmy03KG9R8ULddgoxi8RpYNgRjsblLWGOuG2fqvB0RMMmx6g6LmixxmPYNEoe2xspZPWlij0GooRhzbZNomd0eFUGhpFCCKR/R+n6j2GIwwW6pdFMNcHRTOEAGgiuLEHmrA6cujaNrQaqUdmEHrvT3Gf9OyEC3UtJp4sxWEyfndrtuapJA07XODCGZuIyhAJwYYSSi+564LTHiLLlut4izVCVXE9jPROw7Ufdo1KVsRhs3+A1mkavJbfS/ye1SVHTReOpJwG5A9qW3WBnKs6xJOLlNi5+Hop51jRkTleKR1ubenpjtqAaABDFjSVl1AU3B0w2KtWizyRrboNhDkQ6BVHDJSBFTm+jaYno7HVmIJnTtYOlzSHNGdEqgVnqlRcDrOuWkTRyIrpN2BIOQwUlc12SRT9vghcp5YXqS1UU+uRsn0FJgxp4k0Oo2tIgzmzSzSCqphJOKhUA0Zvm5uicB5IYuZxBg8IAYuQmkO5WmGw7TaVn1joqtEoQTguUZUFmK19MD6kinWEc2k+qjzt82rrz1CD8QT5GJarWlKEsSzjy4AxYqJ9ANjRIm1dlKKB6TjnI30MRoO9sTjk80fGI6IWFTCLT0L07MKxmOgoBMSxRqdhOAKegMRJlpeuGolQE6YKJr0thkWCyYo66BDkwku0zVqQJUgxRDslspm+YrEvO8VsrpzsPaFukFrOtEQRz9fh0ZY9vPoAamSPbkNY3ZzZRrH5GMIqYwt6+pfR5aOxYPtAnECxYrRwopz1NCjWaub6jVZ6JuLmeAUjRIe5daWyXZ0cueA23iTCmktVo9jBGE9fcpELIbatq1sFWNnpnTESkb7THqeyGUKtxFnbJRbqpSIfVcr21Soni4XVdPrgmEZSulyhGQJK2upQpre/N74JHqXneGZWY7daPN+OXYhXBMwNOQFqr2XqcgG3NioCcrkiw/IYuMSv5/A3+IA3UOexoZFv1G6jsUAkidowTsOsNh/ZgTREqW6sz87iHrQim3TAWO5AiSzr+tlLP9s7q5qu6CWPbbQlItpVMHtR3QfqI5NYuccZj0P2KBEe4Ajg+WC019Yp2yLmqVKKtEZ4uoMis8XwI8H0iseSPfTpbsEQIQDjdkWDUM4QmpJA5SnBV77a4zX3TAJCYMySDQ7ICcYCFYyv1Lbt8SGjzD0XOgiCf1vl14X28H67M0ZD0ThgQG0p/h5NgS481sfNDq+XcIsi+9xcogz4Bsw9HKfgqtPCz2JK3lzOYz9WMzhBBnZmJOpsjYrc+FSpOJ8v8d0/ExpnSyapcYo5L4wcnxxSPoMCko0XhjEzmPEzOp3Q/NuBPdcpKpJbbjZynjYu/DmmyF5RCqWnLCSkGylXRZPEgopMMYm6jtCJceWofPR6raLHE+ujBn8hvsZ0pHpfRtAaIUVXO+TmT45BYJ9Yh1r3QESDt/dQ+wVglCGYscB1cOip3yGEsLtp0UO9KvL4d62ek9doTfU+q5+iiWVJ5F6gT1rPEUZQnnakWkFk0pShguRDxtEzBOYl3UsgegTjUnBrp2bGxnmiVSaNIKOduwo5Apz67tKBiCBzK6zUQ8lJNmi5I1aWxoe6Q7Fps5Q6dBJtNLtf1JFHQWTv2SQi+u07uhRSNJs0d63qu7akxdDZv0S9bP0dvZxJmes5N2U49RGaUAkARfooSAnXSp/09G2VWwhJmMnagxQOxCxxVoRm2m32sW5TbDrl+c13XzspxPmzjTi+hkQeC0mq7yPrFMdRpHtGZwrblcE1NmfJYjZeZzplT23IFds1TvI1RqaVJxnzW3a5JYGE8+Iv9YgvyLHOhESjPXn1nqzJRxGU5NWE5Ul+QZ2RhC12hkX5LkT9omjJl5wQ4UpKlQYmSU2jddcabqh6m0Q6nctB2nehIhNVCdcX62ukj7ZkDcP7PtIRD5Sfqcs1BXR64FaPLGQRQcisaq1VyI19Cy1rq6pjQHIHSZFauhii5TYqe026pcc0h1jjqgoaurMCbVxNVAqc+CbEWSaRyg1u9RCSIVlFMBB2ZtHz9hdV5259I0dEqctfRMS9ucJNCS22jEAotJ9XfaeJoFRK1/M9M1+0ycAWRfeo2VQ54B0QPYYgFnG/o5SXtgOOoLEBojkZwrEI/ENMyltxVS3AVj0eJZJIoppi2MjSIoeWiGRI02a95KThIsSZxFC8vUCd52VpBYPCYnCJvsYyzVAGlH0G6LHbJEbQWkqPlSr2c0UZsIl0ZWTnojJKMHacQZ40apGKXAiLrniCulmS3FdbRDifj52IShvSjRqas2ycmRUJ7p+2A5T4ZXEyHA1TaOTHnU6pJkClaMRGPJ7kZRe3Ad4edMgEE63TJlVZhts1KENUEbU5bo2xUjl6NzMA69drqscwAQc67qOvV0A4G26gVQ2GYFogPyvVENmrRtATRcEEYBhyjKbtJUGtkiRQg0HEGM2NCkmT5og7byyWAO4+a3ogmZtG5oDhALkmgnKD7HIZYOFL8Y0RhYWIOpn9PAdjaqNaLnKC4pydS6OfoeZEWlw/iVSkcD4iypOjPnYI3RHCbNPiXP07pXyUBPw2tgBbKYmngjp1qOImv9kKaENmNMKhtt9UXy3GzirMlawiY/TT1AxWBmkX+AGqtFmtlKxxiStXvsgKc8UmoqaVsgG7PikU3TZO3As+1Op/5OxvynCX8AWuQaIx7qDsmibRsRUGnfm9o8BwmxQMp6u+lXIDE3Z9hEPX77iDHSRI8h49qm+RL6u+2giR0SiFJc058DOb9JEp4BMtjt+RBVWVParlNs6v5yR6VrWimb1tgkwSWzRELApJQGIlqv27VHs8rG6K7dySC+LWqwr7s9NRlRAweYPn7atc26v7Y9T7tuyc3bmBft5zr5HLf8W212LrZfbL9nP9tHA2RfeopjQp61bUDa/oL2HsZGz0g7kRignjizIx3RRhwIwoY1ABgL650pW5bL6n94HDDpPHYE3VEqIbtEvj4P7aho5Y92sFhyzJrsY1bXFB3tBxBzGWzHKkE0JccbIFr0xtpip0SMtOFrWKtAjzUrGtCqvDZJBKYZcos4S1OdIXENjbFmVo0iaxEf2y/FQUkSaHGnMsXhYk1UJNa5phGesXuWJAPVWEIGlaYZpQbLe5NcNKSkCPMolazuTtlOiiLLknWNbKcrrRiuTTATCM3QM7uUWEzpZzMrelk3zyeIjiTsEgJpCmgzDGvu1POG/tvMGVbAwURY1Hfrjmhp55dMDWz2W7MDJFolF0KoFD/LmVHzvI5wa3tkFuaxtO4oQGLqIopQRWES1zU5zpR5TRN9IWDqmgL187ptixoRaXWwrmXsWWikOEvYsoZdwtW/dTYrUTYh5thY5KUmY2PEIQCWEnlLBrSSysZMotOySdIOiqjGa+K86mySTahZ58OZ6kwG6zljSompHmsBkWpjkwSgHcSJAqT27zIl4KnhdEmeEVY0+ub3ZJFoaevfVggo5fPYBFoauLU90IAc0vZEHzCt9mdyfE3GaYgqsFhdKKBxraYstVzdKVo23LbfmXW3Ev6Gnv5s2xC7NnrODyN7ZuZr4UB1YwNYDbKpXBjPRmL1dUeTZQm4PkdEa3U4kJMl5Hrdrj0amMY3EezAt63U5eb42f6YVheb5gC2z5uFdnx3a9tWCbMsEtj+Vn2fDJrZ5sRY0sZYRwp3gIY+L+GoY2UpzzqA/ZvTP8C036FtSNqJxNT9EM3GeiErSYGYakikEGc2yaHrUKX8CPXiMSL6ZFqPTG2MExp6fNpRiSIJTYgpZhEyjmW47QW+vdDVCqYUpBX75HXMXdxZMYtbWI5g2jhFkPg7g1BLGsQWiTNbrWinwCSfH+OYWGlAmkTT++vtzDASTqUmOOOqjBSZvO1UavJJq0jgqMk+pUi5E6XIxO5ZisNlP2MOV+o5lRoshCJqE+eUJABNzQRzXtGJGHLWPgegrq5RndOVvHc9RAiBjP6smdsTjh/0fFmSINCA9Gh1bHv730aHTs6VSCd4gPi8qe1S6rnqeQNqDtDqzhYKOpvUwMS8qDsqp3VDAwOYJZVLI6Rse2SbBDk/pC9aU8kl23ZmXF8OGciJxiPHmnSA9DVNvYSJRXgq7OsZNlmUN7JZejvr3zq1GeK2OI2M1c9SMs1eV9RL3ieg/tlyeLSOaMcmQYQNU20B1JNmuotoAhzZgcO02nX2uWcFEG3UlYSwfyN6G97dcppsDKErNLMd1uf22hWIkz26bpcm0Boe0iLOMr/TnvM4T48yJ8eYMn81JdQSvpmd/WBvA8SJs8YHrSfRmsFcw5T3M79DCxPslHD9nSooJCyiJk2Zm3adTNYK19kwzMTJAlVDRjD5GXcaq6y1PebQQRIYf8z+N/MaWj6vHEn69Wxn7S6y/r9FiZX9VXXPQ4OgllCBn7R1njwYj22bfK9Ro4CscXZbv5/sS29xQpNnzR6NNEPSKBIDxImzLFJHToy2MiHBxqc5ALAWxxk1AJj6hdkRdEPApJxtWnODRoRUTCnHeNy5SpB8SYfFNoJ1C3jEI9/NCn3anX3qxxg5VqmFHbNgjb+pdNxyROolw/XX2T4/kwbEoskpmaKqzxWIO5XaSbEX98apTCOS7KYJmrzMUKjFHJTEPbPPKK1geQAhjak6LyHiUX47ws8RnYdtcCM1jD0uq7gs47G6RsZwJRBzkDMI5k4g2pQ8U6OaEwiNFlDNUmqSxH3yPYVYlN28F82VpruYPpS1TTLKnoSZMxmvT6exzyE5VpuQavBb0+O0AwJhYo5oVsw525amp//ExpsYZ6NUiWa2KGus7USF61RnWWNv0WZl2itrm6ZjQnTeQPZ9AtLP367v2tAmGQcKUfAtrbC4Pf6E4t4+VtpaIhk4TNYDNNsm7l/MLqXdT7skBKxxNFMftAiyMSsXrc8OaTu3YEdaOUa7u6jfQBoRYf8+UrNrUpDagToNSeLMPkbGOLVd1MNKChzsn7Pto+lxNyTR0q5/A/uRFnCxP2+qNNZrXUCWxUmQ9DG71iSlXa+7mYjW7g6z6Dmdmq+2F4m6ZPqTaP0eEWd28Dse+Lf/4EYMwrQDra6Btlt19b3tcwTqgyUdPcvx+TLteWiWVpt98Ozx1BFnVmDI/N2EGLYvWzcg+9JbnLDkWaPngqV83kkkpuEPMBGBaeQE1C0azZelOFmQZWVM2gnTBY2bF8/Uk4gZd9qi1lbKZYwh5lhZ3RrtbUN7LCmpI7HDsfhY7Qh/6ljTxpO1XeK6tlJzwSbOtOrM1BNAugNmG+go0TX9CUlVjbD435ktwZML/GSEPMMIp96zDAcFsA2yHKmd3pMszAxEEf5kmk9LdZhYfV0jETY4H568p90TaFRsc+WiK8fGHKQBgdbq/hYaPT56jpf/H40+K9KeNoK6c7bskZk7sgicZIAhhazRxzdjbdMeaTImUylnITMVxJ7TzfwWn8cbwSb7ku/r8cbmaGQswps1Akiei7kwbS7Kgfp7kRIAyTrPNCWjw9Kvfta568/qv4TXqexTbVLynFJI2izyU5+HHTjMqgcIROMHkBmYio0poZ6IEWgAwLpUnpGNWZHoq31pdV8L8WBo/XNk5gAzP2efQZI4i/32k8SyJlE0gdYAmemRKd9v12kDsgUO5nfdaJ7KQtYY2lhbpn6dnc5orVcZkGiCk0gJtwJWzdR5HMquqG7Sura0JtCi5j3xtbv5qqRtU/OsHeQCGgSQ7OcgqTy0SLTY9va/SFnDp/iSyf9POw8byeehZZhn2bLVCaQSgY3OB83XI0AHY7VA9qW3OCHJs7RnIutBsSfndiMx+v8zH3c9kbRTA6DFIoO2KiGq3ZE4NOJR5KbKBPN3g7z1LMeqgcOSTJEJY5/Vj9WeyFNOKz4WhHF5bRbZhxSSMk1a28Z1B+IOWJZKxGybOE4jJy25fVoEPDVCnnY+9vkr4qwVdYadhpVU1cW2TyH/kgrC1HvIOCCCaHx2XSMztEb3s7UFWCugqA2hb2jynOplLoBMFbQdaU9GVfV+NqnTbCxZqRWZpE3G2M042rRH9pijL89I/xH1tVQaRnwz7FGSRJKq86zzqR9vUzRSJ1rzd0tpKynrArlv/TlqZDZD0v/PspWMsa+2tk8jDeu/JIWYTTuX5HtNyM/Y5kyqT3Q3WmOTLJLWtstZ6sEkkWsjqZ4QDLF72m2pALIxhIZol0Brc81j2xkgnVizP2trHCklaZI1QBuutZuci+2bNdoySZzFuL6scbeJ5HWMHbeVID93IcJavTI3WThfz4dNamFr2Gt2xuIp7ckazLFh2YIQFgXCY/6j/o4G5yaVdHEytSkpmVRptQnbviXfBxLnZv3basCroXrOHnOj4JaF+rIK8Zrr3YDsS29x1MmzLu9/1+jkeUgakXYiMZnjsCMwzX6jLRBn0YKx9QKascViyvHiGzd2VuQ5NXGsrL85U3UrEXda9GfJcdhj1dtkOoI2KQkrmp8x+WY5WlkR/MxOZSxFfWY5YGkqhqzj6HOMjtU8/coYKHuBjxBgTvPz1+fYhOzU98quUyfHGNXIqRsW9PlkpwnX7dqgwKhQ59q0ALt1XboF1QtYmeipPdLPWasL7raJ+fR6NGmRdvtITWu7xOaN+IK2LqDTRjCnHXuUJP6a2qPkeJJjbWGcDaPUaOwgxhyHFAKp7dQPe6ytKBjtvxsRZw2eqRgRm2KjGt0nuX903nVokJZUp2xM2Uaej2WXbfJTf0XqOcUJUCBbRdfIvtaNx6p7ZNvXtHF3CrIxKw8993dasTFdPIdpBFor+7S6bfK3lLlN2v+nfK8JuDcQOCR/zw3VcokxNiTQmoytDk2UtmZeFKEk0Kw62HYjHAD1arMMGwwAdu1RPT8ada6qw8zlhpkqRNuHSQa/k9s0QjR/NilJANTbNetaAfHnNCtTTI45HS1liaXdq0SwS44xgzSz/z+LCGzh95plctoB2Zfe4oRSnrXzKGQZkVYiMU2NiRWBabsGgH2MBj+6rAWx2T3hpLSkTEimmjLEJ5c2HCvznYkaPkB2hFiPty3pqrnWjTyhFqIDiXNomFbFUlI1G6gY6vdXX20dz1actXaQxL3KmnrTCMMmxliPLatGTvxcEvfPPGMsdi+zyEA99tS25pmjQ8OxdwKK2qw89NyxMQe2nr0Wa6VkLQozv6LNxSGQcb5J56CZLUqQNq22Wu+pPWpA9tWNM6PwdKvzN2Cp/hLb6HE3tEmtEKnJe9DKtQeaOhdZARAgnYhtVDPIHNJ6Kzl/p5KGyYBOQmWfGYCzz6UN26Sfs6xgXGxo5lqw5rY5SVqk/U66tDlkYwgto8NnrSXb0sZ2LSNJSAHNbWMb59iqwKEtxVxyHD2qbRg7VqPvszfPsnHanukAeJPao8lu0ppAA4Ppfp26LxJEmTlmtJ6PDS3rnOz5E0hvmmORTKm1Oxv5vQkitZUsseS5tIy05zjrGW6DCLTHlrTRHT2/+jvIvvQUJwx51qvnoNnD2/TZTjDXbdcAaELi6Gh/u62mW1ImJJVySdltI4elAWzHKqvQZ5ZTxfQ4MsYdG2MjY92IONOnmzi8bayTkQHbAdPRsVZhL+qB+P3r+F412F5+WfPuL4bstJyTtOh+spaRPJdsZ6Ujg9Xu9l0ueEIhGrZAT9uesMLQKArdaJ9ebKPQShmBrBICaX/bqLNFWcRUM9ImOd5e2iPrHpgodtpYkwvwNgi+ZMe2LNLMHrP8LIPsy/xCHjkPrT4DSfVVm85F7FApi3Og8T0C4vdJHycVqcRsfJNWS1U0g/2cybFlB3SifdIDcw2dqKR9TQ6iC5CNISwXJAMHaZ/ZSBMP1O/YYpAg+VmTIK4eY5bAIe1n2dEvtZs1ZItBlJj6rJXrYxNnaKDm07upf7V/Ytch5WmFIWHNjw3W8DYaKvlgrTOAzLk9dW2Rcm7N6nK1SqJmloeIbZQImDSr+23vh2wfvhVkEYHtgOxLb3FCkGedPgKtRmDs7TXaWUdl1QCwP5MbtP7ja7fBAZBhUJLOSjOlXJpjpY6TnmbR2LHiiW0zr2vy2tljbGWSazTBWf/ffLKOGPukgiE2XL19ymeNWmub91O/PONeAdkkWlbKTyvOJsuI7mcsVrLSnNLOIy39tElrgb4iCOWrne0Jyxfdubk9RIsLqaST0G6UvdWgTswWNSAtWiFt7DG3Yo+S55EavW6jAHXdGHWUPuO7beLFnsPrt1WfWfu2k7KU6jykReA1Gp2bPm7a/2cNA/XPUqs1g8wxmqkasxymZsrG5Dk06LyXdj4AYmuJLAVddB7RPlnpXWnnZF9/pu5pt41pyMasLCwbG9MEjWqbdX0OaYRZMyKppbWnFURCepZQ5uFb2bbdQFmjMdvHaRboBxrXlE58X7LkSvJzQ3YBscY9IVLUuSnPQXIN0ahWZNocKpB4hhLrDCDu92aWXEn8W5emiXqbloZGa6KWnvW0gFcj/zLLr2zRl08LlnYKsi+9xVH1Ro+FMemWO1VzSlvbtOy0JH44egFmv+q2bfCji6IC6m/GzCu2XeJ9e7wNjQmsSYDz1Fds3E1IGHucgHwYky97vPEaJenjqxujPY60l7Vd5gSHbFltcvzyb2tRzph5xYaL5DMT3y52jJTvrkPKsxK7Fxn3K/W8G5yzHo99fvb5ZL1aTnNqcH6ptelafXUJHbVp50VYgejBs9ToWI2emqSN0a8kku8n7ULd96fNdylkQJ1damMBCDS3R8ntU8ecMv40u9nuGM33mTFlv5K2qaXi0wnUOQlZ83OK0qwl4izlfLPWJbH3M+5R2mdNawclxlJnl/VnWTa5DYWjPUagtfto38u2OvAlbEovujkDZGMI/UW/npa2BZftrNVa/G219LNtcbt2v7shWiHgUkivunqPiZdgLLtWcUOfi0VZOyzyO6J1evyVXMObedMizlpGlp+R5e+2abvj55n+ModPbNtszLGxJYNZade8Bf+qEbLWet1wKMvNvkxOTmJsbAzlchnlchljY2M4cuRIw32EEBgfH8eGDRtQKBRw0UUX4bXXXotts7S0hJtuugnDw8MolUq44oor8Pbbb5vPf/Ob3+Daa6/F6aefjkKhgI985CP42te+hkql0tb4e+gJLD+0c+tbiVZkvdpG1iSRtW0HP7rk+aQtiu0JJfU8MhyQLGclkzRLGX8joi/5im2XcoyGSHFAYu93GRmoO6w9PosAA+IEWfKVuY95v8XFfZ8dYds5aUYQ2u/bzkr0eWSwG56WNba6RUOr96fLhVAoBII2XuTYLF90swCRB+iB2ewhCZe1QATSz7Wl35pC6sLW3i7jPNKIlWYkTVN7lDbGrOuYtvi2to2l3Fs2CIiTYzaSpFmaErjl+RkNrq2FhsG0NkilZkhNbUqxw422T0XK2kEeIIMoTNjrZgrHNKK12X1MftZybcC0cTRy/NsE2ZiVg65tzFFGpxnHLZPNnSBpi5ptbr0avafRZZZ1Y7TYrTGGhP+k17ppr27nHJtAs0k0xyLSNDlmr+Gh97OPxdLn2Zj6K80Wp9mwRnatmW/S8NP07VpabyjUEWjt2K/YALLPo61sgTax3OzLtm3bsG/fPuzevRu7d+/Gvn37MDY21nCfu+66C/fccw927tyJl156CaOjo7jkkkswMzNjttmxYwd27dqFJ554As8++yxmZ2exdetWBEEAAPjlL3+JMAzxwAMP4LXXXsO9996L73znO/jLv/zLtsa/YtM2j9Wyoq1FZav5/y1skyZVtceiP2uU/99O6klLY2xxYm9XZts0UpDWDrvL3PpmSCvgHZrPEt+ZONe04E2chGt8Y4w0Wt8b+18gNdUkOnjj8086mfWtlNVXZKQ5ZRGBbdcFSpxbrEV0o/vVQatxAqEpms2DjfZrA3UpmurfVhyJ5HGajist/aCTGjUNkLRJHdejyRpv1rg0edPssCnzeNo2Gh3Z+5Rnpy31UhckTavpTq3YY32MdtGoTEVyG7lhc7uszyVZxw1oHCFuS3Fmj4fsCmGFodE6PGv7ltHub6aFOa6bdNO2yf9mY0/pwJi6TfItlpLOntVYISuA1UR1putBJusVm8+sZmZZNaf1toDthyVqF7eqQmvV983wJZM2K7k+6qROX3Twxvc5VnKhyXbxAbRHoplN9PGabnl84fXXX8fu3buxd+9ebN68GQDw4IMPYsuWLdi/fz8++tGP1u0jhMB9992H2267DVdeeSUA4JFHHsHIyAgef/xxXHfddZiamsJDDz2ERx99FJ/61KcAAI899hg2btyIp59+Gpdddhkuv/xyXH755ea4H/7wh7F//358+9vfxje/+c2Wz6G7UNkyRacPWreRiHaizql/N9qvSwY+K8Wn5VNuFnHtIBKejAC0KrNtZ6yNVHJNF+gtOFxJNUXsM6T/wJKpI3XHjG3bgaMG9OV+Jcdkn19WWkz9dulpTgCyHUqeuDex+2tF41IHm76gaBehaFf23PVXEvqAnkbz2on+9ug5NIdD+rlkvd/aQTMWeFljb/F82on8p0WFM7+z0TVt8XqnBWQaqaBtsqXltMUuxle3fSv3oZ25POP9ZvY4uW8nCpSO7XITdFO2ouG9bMWmdgGyMSsDvVaM9AKtPipZfkK726SiHX+nRXQyjo7H3mxc3RLqyTIzWd+t/7ZK5STHKtCYCM1K1Uxdv6faOssviR23/rvMMHpsu7PAUl5JtOVbtmG3eqEQz1oXdTuvdGpfpqenY6+lpaUuRwI8//zzKJfLhjgDgPPPPx/lchnPPfdc6j5vvPEGJiYmcOmll5r3crkcLrzwQrPPyy+/jGq1Gttmw4YNOPvsszOPCwBTU1NYs2ZNW+ew4sizbtcTfZUvp6FPk0izH1vy85Ymk06MX0YkpOl32YfL2Lfd783cpwPHMPPapaW4WK8spG2TFclpeM06vQaJ/8+sd2Y5JuY91I89+V5WHZ3M+9noN6H/Tdy7Oml7/Jsyvqg16GKb7bwIJxAaEdQt/g6b/eZSvzbxanf/joiXBgvCRoWb27FHbaHZ9W7x+rdqwzteeDcjJzt5fjpYH6Tdl3avfafBrK7J5g7vZSvpp8ea9CAb038c63t8vCCLPO861bHZXNaD+axX26aiV6R5bN1q7d+sHqTeNqvRVxNw2KSY/B97/s+63xHRFq/LbIbajuos+Xeza9nkvNq9p30lfhvt0+AYdd5KL35rCXRqXzZu3GjqkpXLZdxxxx1dj2ViYgLr1q2re3/dunWYmJjI3AcARkZGYu+PjIyYzyYmJuD7PlavXp25TRL/+q//ivvvvx/XX399W+dw1NI2j4bR6lUgTj+0rUT22n7AGU+PUPQgKpOaVtfG0DK/L01i2805JHez/r+uO4uFthe69niayYS7jBrXyYdZemvhVr+l4xozdQdq4341mdzrOtyoMdrnmTxC1nm0ejp1snZ7nE3SzGKpnV2C2jwTjkd0vVBsJTWynUO3uF0rqfmtfWE6sZcck92pSyOtLlpyv7bRzthbOVYrm6G1tVEr23UcxW91HdGibc5KzUkjbxuVs7D3bQl9WP9okI05/kHkXAvokS2x0SzltK/1zXqNrA7MKc1jADQP0OiPWH1HaU2EhUK0kOoabWFnlbSNDu13I7SSctz2vN+O75vcptX3myB5Xt08x53al7feegtDQ0Pm/Vwul7nP+Pg4vv71rzc87ksvvQQgnXAVQjQlYus6urawT9Y27777Li6//HJ8/vOfxxe/+MWGx0hixdQ868cyohGJ1spD3PP5ukXJZ7ty/r4x8W0SfUBrxFnT72x3YduhUiFtsZ4k0ACkkmiZx2yizspSvMUPknINmp1TC06m/b1p59lsH7N94r1me8cINCCbRMt4r2FaZ4vQRTTb2Z6wvHA8rZ+T6Ou83mgx28a8kTWGvoy72QK8B+Nqpvhrddv6nVtYhLeyf7ufWWjX/jY6TsOxNDvHdp6vFs4tjQBsdm86upd9IADIxhAIneO4IsgSSK0DmVGn2d4eyX9bAIesx6wJNCBOjDVCVg1jeYzs/epEEc1sYBcKxF6RTWYc7fpSacdo5b0M9Oq57tS+DA0NxcizRrjxxhtx9dVXN9zmtNNOwyuvvIKDBw/WfXb48OE6ZZnG6OgoAKkuW79+vXn/0KFDZp/R0VFUKhVMTk7G1GeHDh3CBRdcEDveu+++i09+8pPYsmUL/vZv/7al87OxYsizfqLnk3In0ec2f2ytOiw9cVSy9mnhu3ui7uskQpDctkdIVf+lMOVZn6UdrxWkGqceR3dsdPqMtawYTBRgjRXqtFVoenug82egBYRojwSgjBpCr9Hzed1GH21S38bdw7lbf22rCq32Dt4koNPHeXpZoU92uS6Yg9aDqa3cy0Zq+F6CbAyhHyCK9SigU/uZUec3rSh9S0XokX6/9Ryp1Wc2gQagrolZ3VATE2CSOItt2/hQiY37o0Js6atbPWA3avE2zq8du9UJjoZ9GR4exvDwcNPttmzZgqmpKbz44ov4xCc+AQB44YUXMDU1VUdyaZx++ukYHR3Fnj17cO655wIAKpUKnnnmGdx5550AgE2bNsHzPOzZswdXXXUVAODAgQN49dVXcdddd5ljvfPOO/jkJz+JTZs24bvf/S54A6I6C8f5akziuDQO7UwaXbDw3W7T0Rh6yKp3RVym5dC3mV/fbRpL6j4Z9VeaHacjp63Z521O7jYa1cFI+6wl1VzaGBVSW0Unt826310iCEXbL8LywXEclI6hWS2Mrmpl9NEm9dwetYl2yJS0YbAGn3WtVErapKxXN9+Rgm6v91Gt89rmuWXdL9bgM+DYqleWk42ZnJzE2NiYqXMzNjaGI0eONNxHCIHx8XFs2LABhUIBF110EV577bXYNktLS7jpppswPDyMUqmEK664Am+//bb5/De/+Q2uvfZanH766SgUCvjIRz6Cr33ta6hUKv04zZ5ipdgYQp/QxrzWsAh9cr8O1GcAEvWVG7/sfdKIs1bmzeNiRdyrtMse+R29xHKyL2eddRYuv/xybN++HXv37sXevXuxfft2bN26NdZp88wzz8SuXbsAyOdtx44duP3227Fr1y68+uqruOaaa1AsFrFt2zYAQLlcxrXXXoubb74Z//iP/4if//zn+JM/+ROcc845pvvmu+++i4suuggbN27EN7/5TRw+fBgTExOZNdGycFSUZ/00KsfFDzIL3eRQt4C0+i1dk1FAT6W2QPtjSt28m9SjNrZrVy3XyfzT0wV8m/ekFbIwK7LVbL+maCHCU9cqOqlC08cBWmuH3SJEm/UC2knVJRDaRd+c/C5T7RpFT3tuj7pEo7m81WEdz6lCwFEo87AM7HIvbtHRUJ8tJxuzbds2vP3229i9ezcA4Etf+hLGxsbwgx/8IHOfu+66C/fccw8efvhh/O7v/i6+8Y1v4JJLLsH+/fsxODgIANixYwd+8IMf4IknnsDatWtx8803Y+vWrXj55ZfhOA5++ctfIgxDPPDAA/id3/kdvPrqq9i+fTvm5ubwzW9+s2/nSyAcM3STJdKBQteufab3bvbtyW9puUFAAt3Oo72a8Toag32tu7FpHa6fusVysi8A8L3vfQ9f/vKXTWfMK664Ajt37oxts3//fkxNTZm/b7nlFiwsLOCGG27A5OQkNm/ejKeeesrYFwC499574bourrrqKiwsLODiiy/Gww8/DMdxAABPPfUUfvWrX+FXv/oVTj755Nj3tVViSbSw9fT0NMrlMiYmJlrOfY19Sdt7tIbl7p4ejXV1P67Bcr9fmePrJkWvDQl02iK92bl1U18HaD1K3g/D1Mn5arQy7tif9j3M+n8gVdZeR6JZ+05Pz2DdqR/B1NRUW3OYnvu+/+L/RXFgsPkOCvOzM/jCJ85o+/tOVHRrYxrheOA3jqYta+t6dNAwoF/n0ul9bDSeboKr7dREiwbTp2S7JvenW/ukcdTPuQ27DPTvfprhNPhsenoaI6Ojx72Nef311/Gxj30Me/fuxebNmwEAe/fuxZYtW/DLX/4ypgzQEEJgw4YN2LFjB/78z/8cgFSZjYyM4M4778R1112HqakpnHTSSXj00UfxhS98AYBUAWzcuBE/+tGPcNlll6WO5+6778a3v/1t/PrXv+74nPQ1PjgxgcE+2OPlbGOOtp+03P2HNPRkzK3Oc822a7XGVkY9SH2d7Lkw7T2gM4KknY7FR8tHaQdNv79fNtoMoHNbPT09jdE2bcxysy8rBctLV0ggdINO1UU9kNc2m5DTWkA3SntsduxjrXjISntp9nnHqajJdNq0e5aWyqn37fIe60417bwIhOMeyzD9oJfodB49KqmxR/GY7abgt4WjaJf7fT/7WpOmQxszPT0dey0tLXU1jueffx7lctkQZwBw/vnno1wu47nnnkvd54033sDExIRREQCyK9uFF15o9nn55ZdRrVZj22zYsAFnn3125nEBYGpqCmvWrOnqnAiEvqNXc3sr6fo9yvJppXxMo207mW+7mUOPitvTz/VOC8duVGqgG5AP01v0fVV8PEYhCMsXTZ+ndpy9Jtu2+4z1+lnvZMLs9HfR6X7JGjK9mOSjgzeWqKfWggCySbQusJzqBazkejT9+A0RTly08its1wHoSQevXqFPBZe7JsySaJeE7bJcRTvjbvccBfqz/uzUxmzcuNHYgnK5jDvuuKOrcUxMTGDdunV1769bty6zLox+P9kpbWRkxHw2MTEB3/djXdCS2yTxr//6r7j//vtx/fXXt30eBPKTliW6DUg1WBdn3W97iktVg1l1mLNeSXRTk/lYPZedBuu7/+L260un+VLdmOPl5MOsBByXIeXj4ZaS03aM0UlR4hbRLHWlF/d+OT0/vXKgenKclPuWSqABEYnG+bGX6vUQ27Ztw759+7B7927s3r0b+/btw9jYWMN9dD2anTt34qWXXsLo6CguueQSzMzMmG127NiBXbt24YknnsCzzz6L2dlZbN26FUEQAECsHs1rr72Ge++9F9/5znfwl3/5l309X0LvsHJ+Bb1FK4TLMSWTer3/sUKzcffwvPqqpsPyWYe+9dZbmJqaMq9bb701dbvx8fGmDvJPf/pTAPWpWYAMwDRTqaR1Fm+2T9Y27777Li6//HJ8/vOfxxe/+MWGxziWoDmVYNDPAEFWQ6wM9KuxUK/s4LJPJ+6lkpCw4nBUGgYQCMcMx2ji0hN1P9RrzQxXu0U5W1Vn9LueDADUFU9NK6aa8l6jFt+9QLsy5n5Jnl9//XXs3r07Vo/mwQcfxJYtW7B///7MejT33XcfbrvtNlx55ZUAgEceeQQjIyN4/PHHTT2ahx56CI8++qjpSvPYY49h48aNePrpp3HZZZfh8ssvx+WXX26O++EPfxj79+/Ht7/97WVZzJmcmpWFfsxrSRx1jr2VpkFp2x/vOMrn0a/72stZvlMbMzQ01FJNmhtvvBFXX311w21OO+00vPLKKzh48GDdZ4cPH65TlmmMjo4CkOqy9evXm/cPHTpk9hkdHUWlUsHk5GRMfXbo0CFccMEFseO9++67+OQnP4ktW7bgb//2b5ueWztgWD7EJ+EER3Ie7GGTq7qvQv1z305js47qXTbB0WjEgm6+o137nLbvMsFy8WFWCpbX3SWsaBxPzmyzaaPVxXhWSmOn6Y69rs3SzvTYiQPSkyhVFy2+7fe6QSDafwFUj4ZAOBY47pZ9yTo3Wa9liuPJtvcL3T5zndqYVjE8PIwzzzyz4Sufz2PLli2YmprCiy++aPZ94YUXMDU1VUdyaZx++ukYHR3Fnj17zHuVSgXPPPOM2WfTpk3wPC+2zYEDB/Dqq6/GjvvOO+/goosuwr/9t/8W3/3ud8F7XIKhl6Dn/vhHz+9hLxTFXXZtzNwl4/1mNZk7Ic76VUey3fvVk/vb6n1Zxra63/blRENf73A/DAvdzxMXy22h0ikpdLTOo9lvpVNlRqvn3UkjhLbQgpHqljTT6LTYJtWjOTZYbnMFoTdoZc461msEevYInWC5FHQ+66yzcPnll2P79u3Yu3cv9u7di+3bt2Pr1q0xZfOZZ56JXbt2AZDpmjt27MDtt9+OXbt24dVXX8U111yDYrGIbdu2AQDK5TKuvfZa3HzzzfjHf/xH/PznP8ef/Mmf4JxzzjFq53fffRcXXXQRNm7ciG9+85s4fPgwJiYmMm0Q4cTAsZ7T20a/yZO08iWt7tqrIfTwWP0i0Ppii4/D4BawfOzLSgGlbR7n6LUEnRb+yxOdEnX2s9HLZ6Wv6U0tpmrGPgP62mI6DAXCNvJW9bZvvfVWLKUml8ulbj8+Po6vf/3rDY/50ksvAaB6NIQTG41SPWi5d2JgJabedWpj+oHvfe97+PKXv2yUyFdccQV27twZ22b//v2Ympoyf99yyy1YWFjADTfcgMnJSWzevBlPPfUUBgcHzTb33nsvXNfFVVddhYWFBVx88cV4+OGH4TgOAOCpp57Cr371K/zqV7/CySefHPs+Qc5cW6CrdYzRaM3a7XFbRFa5lW7nz2bL/067cLazW6NzWKl+bDf3bDnZl5WAvpFnK/XhJRBstFMzoNvv6BbH/VTYjCSzFxVp5FsXCNuUMevngerRHH2Q7Vn5SFtoH/fzG+GERqc2ph9Ys2YNHnvssYbbJMksxhjGx8cxPj6euU8+n8f999+P+++/P/Xza665Btdcc027wz0mIDtz/KPv97CXgd0+1EDTaGUqORrPeycEGqE1LCf7shJAyjPCcYGjOUl2U2i6lxPOCmoQ2T6aKc2aLUZ6vNDod7HN4eFhDA8PN93OrkfziU98AkB79WjOPfdcAFE9mjvvvBNAvB7NVVddBSCqR3PXXXeZY73zzjv45Cc/iU2bNvWtHs1KVJUQeg96RggrCVTQmUBYoWgU2G1n3w7QSrOvXroaJ7TfsoxB9qW3IPKMcFSx0h3jXpBoZHwUjnGqpo1ACARtGJN2tm0Hdj2aBx54AADwpS99KbUezR133IHPfvazsXo0Z5xxBs444wzcfvvtmfVo1q5dizVr1uCrX/1qaj2aU045xdSj0dDqtmMN+ukQCPVY6XZ3JWC52BgCgdBHpJFheh3bp7pZrRBovfgOwvIF2Zfeoi/kGf2GCCc6yJD0CM1UZt1E9NrAcqoXQPVoCAQCYWVhOdkYAqFTtJt6R0DXpFkrM0G/CLRe+zr0/PQHZF96i+NKeUa3kkA4AdFq4dU+1jwL0F69gKCrb2uMlV6PhuZ5AuH4ATk6vcFysjEEAmHlwSa6uuVGSCBwfIHsS29xXJFnBALhBEW7aZo9TuukegHLH7SWIxCyQambyxtkYwgEwtFCkvxqRKYRUXb8g+xLb3FckWe0+EsHXRfCCYNOSTQCgUAgEAgEAoEQw3IiyCh1k7DccVyRZ4TjH0TyEXqCdmuddZu2ScU2CSsAR2NReqIHc5bzwn8l3ZuVdC4A2RgCgdAZVtpcSOg9yL70FscdeUaTRH+xXBf+R3Nc9IwdZzgKTQPCUCCgYpsEAoFA6APIxhw/WI5r5CSO5Tp2ufoRSRwv4zwRQfemtyD70lv0hTzr90N/PJAb9MOvR7f3TO9/NK7r8fCMEVKQkdbZ7b0M2jQ87WxLINB8Qzha0PazF8/bsV7jrKTfDdkYAuHoY6X4aitpLtTI8vlaPc+VcF97BbIvvcVxpzzTsH8Uy/UWH69qqeVuTI4WidZLJ4NQj74+Zz1uGECGZ/ljuc9bzXC0Fr+Uutl/HA/PYrf2bbmc33J41nox3ZONIfQapD5rDUczMN9PLIe5sB/o9JyS+x3v97cbkH3pLfpGnh1t4ij53csFxzOBpo/Zy+P1EkSiHf/o+++DcUCEXX9HELZnTIL+ZI+eEFipC8BWsJIItBMdx8s17mT9tNzO61jPGb0otk025uigF8/J8fLbBo7t+vV4uk5A/BodT+O2caznwuWME5lMI/vSW7REnglVOG5mZqblAy+nh3I5TCRH83r043y7Gf/Ruv5H6xovh+dppaHf907PXaLDIpgUtekvkjbmeFfDdIuj+fT085rRr2DlPJPHA47l80Y2ZvlC35PpmZlltz4+VjgaT08vr8uxftqPt3t8rK/X8YTj5d7OdmFjyL70Fi2RZ3pRcMYZZ/R1MAQCgdBPzMzMoFwut70fGZ7+QtuY3yEbQyAQjmOQjVl+IB+GQCCsFHRiY8i+9BYtkWcbNmzAW2+9hcHBQTB2vHC0BAKBICGEwMzMDDZs2NDR/tSppr8gG0MgEI5nkI1ZviD7QiAQjnd0Y2PIvvQWLZFnnHOcfPLJ/R4LgUAg9A2dqAE0AtFm1KbD1J0TFWRjCATC8Q6yMcsTZF8IBMJKQKc2huxLb3HcdtskEAiEowWSPBMIBAKhXyAbQyAQCIR+gOxLb8GP9QAIBAKBQCAQCAQCgUAgEAgrF5OTkxgbG0O5XEa5XMbY2BiOHDnScB8hBMbHx7FhwwYUCgVcdNFFeO2112LbLC0t4aabbsLw8DBKpRKuuOIKvP3226nHW1pawsc//nEwxrBv3762xk/kGYFAIDSBjtq08yIQCAQCoRWQjSEQCARCP7Dc7Mu2bduwb98+7N69G7t378a+ffswNjbWcJ+77roL99xzD3bu3ImXXnoJo6OjuOSSS0xDGADYsWMHdu3ahSeeeALPPvssZmdnsXXrVgRBUHe8W265peMapZS2SSAQCE1QCwWcNoxJjRwbAoFAILQIsjEEAoFA6Ac6tS/T09Ox93O5HHK5XFdjef3117F7927s3bsXmzdvBgA8+OCD2LJlC/bv34+PfvSjdfsIIXDffffhtttuw5VXXgkAeOSRRzAyMoLHH38c1113HaampvDQQw/h0Ucfxac+9SkAwGOPPYaNGzfi6aefxmWXXWaO9+Mf/xhPPfUUnnzySfz4xz9u+xxIeUYgEAhNsNyiNgQCgUBYOSAbQyAQCIR+oFP7snHjRpNaWS6Xcccdd3Q9lueffx7lctkQZwBw/vnno1wu47nnnkvd54033sDExAQuvfRS814ul8OFF15o9nn55ZdRrVZj22zYsAFnn3127LgHDx7E9u3b8eijj6JYLHZ0DqQ8IxAIhCagNs8EAoFA6BfIxhAIBAKhH+jUvrz11lsYGhoy73erOgOAiYkJrFu3ru79devWYWJiInMfABgZGYm9PzIygt/+9rdmG9/3sXr16rpt9P5CCFxzzTW4/vrrcd555+E3v/lNR+dAyjMCgUBogkCItl8EAoFAILQCsjEEAoFA6Ac6tS9DQ0OxVyPybHx8HIyxhq+f/vSnAADGWN3+QojU920kP29lH3ub+++/H9PT07j11lsb7tMMpDwjEAiEJqA2zwQCgUDoF8jGEAgEAqEfOBr25cYbb8TVV1/dcJvTTjsNr7zyCg4ePFj32eHDh+uUZRqjo6MApLps/fr15v1Dhw6ZfUZHR1GpVDA5ORlTnx06dAgXXHABAOAnP/kJ9u7dW0cCnnfeefjjP/5jPPLIIy2cKZFnBAKB0BTk2BAIBAKhXyAbQyAQCIR+4GjYl+HhYQwPDzfdbsuWLZiamsKLL76IT3ziEwCAF154AVNTU4bkSuL000/H6Ogo9uzZg3PPPRcAUKlU8Mwzz+DOO+8EAGzatAme52HPnj246qqrAAAHDhzAq6++irvuugsA8K1vfQvf+MY3zHHfffddXHbZZfj+978fq8HWDESeEQgEQhOQY0MgEAiEfoFsDIFAIBD6geVkX8466yxcfvnl2L59Ox544AEAwJe+9CVs3bo11mnzzDPPxB133IHPfvazYIxhx44duP3223HGGWfgjDPOwO23345isYht27YBAMrlMq699lrcfPPNWLt2LdasWYOvfvWrOOecc0z3zVNOOSU2loGBAQDARz7yEZx88sktnwORZwQCgdAEgQgRhGFb2xMIBAKB0ArIxhAIBAKhH1hu9uV73/sevvzlL5vOmFdccQV27twZ22b//v2Ympoyf99yyy1YWFjADTfcgMnJSWzevBlPPfUUBgcHzTb33nsvXNfFVVddhYWFBVx88cV4+OGH4ThOT8dP5BmBQCA0AXVCIxAIBEK/QDaGQCAQCP3AcrMva9aswWOPPdZwG5FoisMYw/j4OMbHxzP3yefzuP/++3H//fe3NI7TTjut7ntaAZFnBAKB0ARBKMCXieSZQCAQCCsLZGMIBAKB0A+Qfekt+LEeAIFAIBAIBAKBQCAQCAQCgbBcQcozAoFAaIJaCLA2IjE1KkdDIBAIhBZBNoZAIBAI/QDZl96CyDMCgUBoApI8EwgEAqFfIBtDIBAIhH6A7EtvQWmbBAKB0AS6zXM7LwKBQCAQWsFysjGTk5MYGxtDuVxGuVzG2NgYjhw50nAfIQTGx8exYcMGFAoFXHTRRXjttddi2ywtLeGmm27C8PAwSqUSrrjiCrz99tupx1taWsLHP/5xMMawb9++Hp0ZgUAgnHhYTvZlJYDIMwKBQGgCMjwEAoFA6BeWk43Ztm0b9u3bh927d2P37t3Yt28fxsbGGu5z11134Z577sHOnTvx0ksvYXR0FJdccglmZmbMNjt27MCuXbvwxBNP4Nlnn8Xs7Cy2bt2KIAjqjnfLLbdgw4YNPT83AoFAONGwnOzLSgClbRIIBEITLLc2zwQCgUBYOVguNub111/H7t27sXfvXmzevBkA8OCDD2LLli3Yv38/PvrRj9btI4TAfffdh9tuuw1XXnklAOCRRx7ByMgIHn/8cVx33XWYmprCQw89hEcffRSf+tSnAACPPfYYNm7ciKeffhqXXXaZOd6Pf/xjPPXUU3jyySfx4x//uC/nSSAQCCcKlot9WSkg5RmBQCA0AUVtCAQCgdAvdGpjpqenY6+lpaWuxvH888+jXC4b4gwAzj//fJTLZTz33HOp+7zxxhuYmJjApZdeat7L5XK48MILzT4vv/wyqtVqbJsNGzbg7LPPjh334MGD2L59Ox599FEUi8WuzoVAIBAI5MP0GkSeEQgEQhMIISDCNl6CDA+BQCAQWkOnNmbjxo2mNlm5XMYdd9zR1TgmJiawbt26uvfXrVuHiYmJzH0AYGRkJPb+yMiI+WxiYgK+72P16tWZ2wghcM011+D666/Heeed19V5EAgEAkGCfJjegtI2CQQCoQnCULQlYybJM4FAIBBaRac25q233sLQ0JB5P5fLpW4/Pj6Or3/96w2P+dJLLwEAGGN1nwkhUt+3kfy8lX3sbe6//35MT0/j1ltvbbgPgUAgEFoH+TC9BZFnBAKB0ARCtBeJoagNgUAgEFpFpzZmaGgoRp5l4cYbb8TVV1/dcJvTTjsNr7zyCg4ePFj32eHDh+uUZRqjo6MApLps/fr15v1Dhw6ZfUZHR1GpVDA5ORlTnx06dAgXXHABAOAnP/kJ9u7dW0cAnnfeefjjP/5jPPLII03Pk0AgEAhxkA/TWxB5RiAQCE2gpcztbE8gEAgEQivot40ZHh7G8PBw0+22bNmCqakpvPjii/jEJz4BAHjhhRcwNTVlSK4kTj/9dIyOjmLPnj0499xzAQCVSgXPPPMM7rzzTgDApk2b4Hke9uzZg6uuugoAcODAAbz66qu46667AADf+ta38I1vfMMc991338Vll12G73//+7EabAQCgUBoHeTD9BZU84xAIBCOI0xOTmJsbMzUuBkbG8ORI0ca7iOEwPj4ODZs2IBCoYCLLroIr732WmybpaUl3HTTTRgeHkapVMIVV1yBt99+O/V4S0tL+PjHPw7GGPbt29ejMyMQCATCscRZZ52Fyy+/HNu3b8fevXuxd+9ebN++HVu3bo112jzzzDOxa9cuADJdc8eOHbj99tuxa9cuvPrqq7jmmmtQLBaxbds2AEC5XMa1116Lm2++Gf/4j/+In//85/iTP/kTnHPOOab75imnnIKzzz7bvH73d38XAPCRj3wEJ5988lG+EgQCgUAg1IPIMwKBQGgCXS+gnVe/sG3bNuzbtw+7d+/G7t27sW/fPoyNjTXc56677sI999yDnTt34qWXXsLo6CguueQSzMzMmG127NiBXbt24YknnsCzzz6L2dlZbN26FUEQ1B3vlltuwYYNG3p+bgQCgXAiYjnZmO9973s455xzcOmll+LSSy/F7//+7+PRRx+NbbN//35MTU2Zv2+55Rbs2LEDN9xwA8477zy88847eOqppzA4OGi2uffee/Ef/+N/xFVXXYU/+IM/QLFYxA9+8AM4jtO3cyEQCIQTHcvJvqwEMEGJrQQCgZCK6elplMtlbB7/Adx8qeX9aotzeGH8/42pqamW6tG0itdffx0f+9jHsHfvXpPGsnfvXmzZsgW//OUvY8oADSEENmzYgB07duDP//zPAUjl2MjICO68805cd911mJqawkknnYRHH30UX/jCFwDIlJmNGzfiRz/6ES677DJzvB//+Mf4sz/7Mzz55JP4vd/7Pfz85z/Hxz/+8Z6dI4FAIJwoWG42hkAgEAgrA2Rf+gNSnhEIBEIT6GKb7bwAabjs19LSUlfjeP7556UhtOq/nH/++SiXy3juuedS93njjTcwMTGBSy+91LyXy+Vw4YUXmn1efvllVKvV2DYbNmzA2WefHTvuwYMHsX37djz66KMoFotdnQuBQCAQJDq1MQQCgUAgNALZl96CyDMCgUBogk4lzxs3bjS1ycrlMu64446uxjExMYF169bVvb9u3TpMTExk7gOgrlPayMiI+WxiYgK+78e6oCW3EULgmmuuwfXXX4/zzjuvq/MgEAgEQgRKqyEQCARCP0D2pbegbpsEAoHQBJ12qnnrrbdikudcLpe6/fj4OL7+9a83POZLL70EQBZnrvs+IVLft5H8vJV97G3uv/9+TE9P49Zbb224D4FAIBDaA3VDIxAIBEI/QPaltyDyjEAgEJqhTcMDte3Q0FBL9QJuvPFGXH311Q23Oe200/DKK6/g4MGDdZ8dPny4TlmmMTo6CkCqy9avX2/eP3TokNlndHQUlUoFk5OTMfXZoUOHcMEFFwAAfvKTn2Dv3r11BOB5552HP/7jP8YjjzzS9DwJBAKBkIIObQyBQCAQCA1B9qWnIPKMQCAQmiAUAqyNGgBhm/UChoeHMTw83HS7LVu2YGpqCi+++CI+8YlPAABeeOEFTE1NGZIridNPPx2jo6PYs2cPzj33XABApVLBM888gzvvvBMAsGnTJniehz179uCqq64CABw4cACvvvoq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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABNgAAAKcCAYAAAA6rwc8AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOydf7wUVf3/X2dmd+9efusFQZSfaoqiqWAEflDUREH7mGHy0SQ1tQhLgY99Ss1EtPxdZIqkoUaa0vejlhml+APSREMl7aNmliiI3BBUfgj37t2Z8/1j5syemZ3Zndnd+/v1fDz2AXf2zMx7zs7M+5zXeZ/3EVJKCUIIIYQQQgghhBBCSEUY7W0AIYQQQgghhBBCCCGdGQpshBBCCCGEEEIIIYRUAQU2QgghhBBCCCGEEEKqgAIbIYQQQgghhBBCCCFVQIGNEEIIIYQQQgghhJAqoMBGCCGEEEIIIYQQQkgVUGAjhBBCCCGEEEIIIaQKKLARQgghhBBCCCGEEFIFFNgIIYQQQgghhBBCCKkCCmyEEEI6LPfccw+EEN4nlUph7733xrnnnosNGzYUlX/jjTdwzjnnYOjQochkMujfvz+mTp2KP/zhD0VlV6xY4Tt2JpPBgAEDcOSRR+Lyyy/Hu+++2xaXWDHLli3DvHnzQr8bPnw4zjnnnIqOW82+bck555yD4cOHt/l5W1pacMABB+C6667ztj333HOYN28ePv7444qPO2/ePAghfNsmTZqESZMmxdr/8ssvx2GHHYbdd98d2WwWI0eOxNe+9rXQ+7ilpQVXXXUVhg8fjrq6OhxwwAH46U9/WlRuxowZ+MIXvlDJ5bQaCxcuxD333FPRvsOHD8fJJ59cW4O6GW3xfuDvRAghpLNCgY0QQkiH5+6778aqVauwfPlyXHDBBbj//vsxceJEfPLJJ16Zhx56CIcddhj+8pe/4IorrsATTzyB22+/HQAwdepU/M///E/osX/4wx9i1apVePrpp7F48WJMmjQJd911F0aNGoX77ruvTa6vEpYtW4arrroq9LuHH34YV1xxRRtb1D1YuHAhPvroI3zrW9/ytj333HO46qqrqhLYzj//fKxatari/T/++GOcccYZ+MUvfoE//vGPuOSSS/Doo49i3Lhx2LJli6/srFmzcO211+LCCy/EY489hlNPPRUXX3wxfvjDH/rKzZs3D7///e/x1FNPVWxXralGYCOEEEIIaU1S7W0AIYQQUo7Ro0dj7NixAIBjjjkGlmXh6quvxm9+8xt8+ctfxr/+9S/MmDEDBx98MFasWIGePXt6+37pS1/CN77xDdx44404/PDD8V//9V++Y++333747Gc/6/39n//5n/jv//5vfO5zn8M555yDQw45BAcffHDbXGiNOOyww9rbhC5JPp/HjTfeiK9+9au+e6wW7L333th7770r3v+2227z/T1p0iSMGDECU6dOxW9/+1t89atfBQC89tprWLx4MX7wgx/g29/+tld2y5YtuOaaazBz5kzsvvvuAIB99tkHJ554Iq677joce+yxFdvWXbAsC/l8HnV1de1tCiGEEELaAUawEUII6XQoQUxNf/vxj3+MnTt34qc//Wmo8HHzzTejX79++MEPfhDr+Lvvvjt+9rOfIZ/P48c//nHJsrZt45prrsH++++P+vp69OvXD4cccgh+8pOfeGXU9L81a9bgi1/8Ivr06YO+ffvirLPOwgcffOA73tKlSzF58mTsueeeqK+vx6hRo/Dd737XF613zjnneIKKPs31nXfeAVA8jaupqQn//d//jUMPPRR9+/bF7rvvjvHjx+O3v/1trPoIQwiBb37zm7j77ru9ax87diyef/55SClx4403YsSIEejVqxeOPfZY/POf/yw6xl133YVPf/rTyGaz2H333XHqqafijTfeKCp3zz33YP/990ddXR1GjRqFJUuWhNqUy+VwzTXX4IADDkBdXR0GDBiAc889t6iOn3rqKUyaNAkNDQ2or6/H0KFDMW3aNOzcubPkNT/yyCPYsGEDZsyY4W2bN2+eJ1SNGDHC+y1WrFgBIN7vqY4TnCJaLQMGDAAApFKF8dTf/OY3kFLi3HPP9ZU999xzsWvXLvzxj3/0bZ8xYwaeeOIJ/Otf/6rIhiT3nm3b+OlPf4pDDz3Ue5Y++9nP4pFHHgHg3NevvfYaVq5c6dVztdOEFy5ciFQqhSuvvBIA8MEHH2DWrFk48MAD0atXL+yxxx449thj8cwzz/j2e+eddyCEwA033IBrrrkGI0aMQF1dHZ5++mnvt3z11VfxpS99ybvuuXPnIp/P480338SJJ56I3r17Y/jw4bjhhhsqrjP1HP7yl7/EqFGj0KNHD3z605/Go48+mrgumpubMX/+fIwaNQrZbBYNDQ045phj8Nxzz5Xcb926dTjrrLOwxx57eM/ozTffDNu2feWuuuoqjBs3Drvvvjv69OmDww8/HIsXL4aUsqxtwd+JEEII6Ygwgo0QQkinQ4k1SkBYvnw5Bg4c6ItE0+nRowcmT56MX//612hsbMSgQYPKnuOII47AnnvuiT/96U8ly91www2YN28evve97+Goo45CS0sL/v73v4dOFzz11FNx+umnY+bMmXjttddwxRVX4PXXX8cLL7yAdDoNAHjrrbcwdepUzJ49Gz179sTf//53XH/99fjLX/7iTdW74oor8Mknn+B///d/fdMK99xzz1Abm5ub8eGHH+KSSy7BXnvthVwuhyeeeAJf/OIXcffdd+MrX/lK2foI49FHH8WaNWtw3XXXQQiB73znOzjppJNw9tln4+2338att96KrVu3Yu7cuZg2bRr++te/eiLStddei8suuwxnnHEGrr32WmzZsgXz5s3D+PHjsXr1auy3334AHHHt3HPPxSmnnIKbb74ZW7duxbx589Dc3AzDKIwT2raNU045Bc888wz+53/+BxMmTMC7776LK6+8EpMmTcKLL76I+vp6vPPOOzjppJMwceJE3HXXXejXrx82bNiAP/7xj8jlcujRo0fk9f7+97/HHnvsgQMPPNDbdv755+PDDz/ET3/6Uzz00EPeb6DKxPk9a0k+n/fuwdmzZ+NTn/oUvvjFL3rf/9///R8GDBhQ9Awccsgh3vc6kyZNgpQSy5Yt802LnTRpElauXFlWHEly751zzjm49957cd5552H+/PnIZDJ4+eWXPeH44YcfxmmnnYa+ffti4cKFAFBxtJiUEt/+9rdxyy234Oc//7knSH/44YcAgCuvvBKDBg3Cjh078PDDD2PSpEl48skni3Li3XLLLfjUpz6Fm266CX369MF+++2H559/HgBw+umn46yzzsLXv/51LF++HDfccANaWlrwxBNPYNasWbjkkkvwq1/9Ct/5znew7777er9T0uf197//PVavXo358+ejV69euOGGG3DqqafizTffxMiRI2PVRz6fx5QpU/DMM89g9uzZOPbYY5HP5/H8889j3bp1mDBhQuh+H3zwASZMmIBcLoerr74aw4cPx6OPPopLLrkE//rXv7zfCXBEya9//esYOnQoAOD555/Ht771LWzYsAHf//73E/1OhBBCSIdEEkIIIR2Uu+++WwKQzz//vGxpaZHbt2+Xjz76qBwwYIDs3bu3bGxslFJKmc1m5Wc/+9mSx/rOd74jAcgXXnhBSinl008/LQHI//f//l/kPuPGjZP19fUlj3vyySfLQw89tGSZK6+8UgKQc+bM8W2/7777JAB57733hu5n27ZsaWmRK1eulADkK6+84n134YUXyig3PmzYMHn22WdH2pPP52VLS4s877zz5GGHHZZoXwUAOWjQILljxw5v229+8xsJQB566KHStm1v+4IFCyQA+eqrr0oppfzoo49kfX29nDp1qu+Y69atk3V1dfLMM8+UUkppWZYcPHiwPPzww33He+edd2Q6nZbDhg3ztt1///0SgHzwwQd9x1y9erUEIBcuXCillPJ///d/JQD517/+tew1Bhk1apQ88cQTi7bfeOONEoBcu3Ztyf1L/Z7qHtE5+uij5dFHHx3bvo0bN0oA3mfcuHFyw4YNvjLHH3+83H///UP3z2Qy8mtf+1rR9r322ktOnz7dt+3YY4+VpmnGtk0Rde/96U9/kgDk5ZdfXnL/gw46KFGd6AwbNkyedNJJcufOnXLatGmyb9++8oknnohl73HHHSdPPfVUb/vatWslALnPPvvIXC7n20f9ljfffLNv+6GHHioByIceesjb1tLSIgcMGCC/+MUvlrUh7HkFIAcOHCi3bdvmbWtsbJSGYchrr7225LXpLFmyRAKQd955Z8lywffDd7/7Xd97VfGNb3xDCiHkm2++GXocy7JkS0uLnD9/vmxoaPA935X8ToQQQkhHgFNECSGEdHg++9nPIp1Oo3fv3jj55JMxaNAg/OEPf8DAgQNjH0O6kTZJpuHJGFOXPvOZz+CVV17BrFmz8Nhjj2Hbtm2RZb/85S/7/j799NORSqXw9NNPe9vefvttnHnmmRg0aBBM00Q6ncbRRx8NAKHTJ+Py//7f/8ORRx6JXr16IZVKIZ1OY/HixVUd85hjjvFNyR01ahQAYMqUKb56VtvVlN5Vq1Zh165dRdEoQ4YMwbHHHosnn3wSAPDmm2/i/fffx5lnnuk73rBhw4oiah599FH069cPn//855HP573PoYceikGDBnlTNg899FBkMhl87Wtfwy9+8Qu8/fbbsa/3/fffxx577BG7PFDb31Pl+FKf4BS8/v37Y/Xq1Xj22Wdx55134sMPP8QxxxyDjRs3+sqVegbCvttjjz2KVu198sknkc/nY9kd595TK/1eeOGFsY5ZKVu2bMGxxx6Lv/zlL3j22Wdx3HHHFZVZtGgRDj/8cGSzWc/eJ598MvT3+s///E8v+jRIcCXMUaNGQQiBKVOmeNtSqRT23XffotVekzyvxxxzDHr37u39PXDgQOyxxx6JVkL+wx/+gGw26+Xqi8tTTz2FAw88EJ/5zGd828855xxIKX1Rmk899RQ+97nPoW/fvt6z8P3vfx9btmzBpk2bfPvH+Z0IIYSQjgYFNkIIIR2eJUuWYPXq1VizZg3ef/99vPrqqzjyyCO974cOHYq1a9eWPIaaZjZkyJDY5123bh0GDx5cssyll16Km266Cc8//zymTJmChoYGHHfccXjxxReLygan5aVSKTQ0NHirPO7YsQMTJ07ECy+8gGuuuQYrVqzA6tWr8dBDDwEAdu3aFdt2nYceeginn3469tprL9x7771YtWoVVq9eja9+9atoamqq6JgAvGT4ikwmU3K7Ope63rAprYMHD/a+V/+GTekNbvv3v/+Njz/+GJlMBul02vdpbGzE5s2bATiJ+5944gnsscceuPDCC7HPPvtgn3328eXMi2LXrl3IZrNlyylq/Xsed9xxvusKiiGpVApjx47FkUceifPPPx9PPfUU3n77bVx33XVeGf1+0/nkk0+Qy+WKfjsAyGazrX7vffDBBzBNM9b07Wr4xz/+gRdeeAFTpkzB6NGji77/0Y9+hG984xsYN24cHnzwQTz//PNYvXo1TjzxxNA6iJqWDYQ/Bz169Ci6hzKZjK8ukj6vDQ0NRdvq6uoS/WYffPABBg8e7Jt2HYctW7ZEPsfqewD4y1/+gsmTJwMA7rzzTvz5z3/G6tWrcfnllwMofhbK/U6EEEJIR4Q52AghhHR4Ro0a5a0iGsbxxx+P2267Dc8//3xoHradO3di+fLlGD16dOwO/F/+8hc0NjbivPPOK1kulUph7ty5mDt3Lj7++GM88cQTuOyyy3DCCSdg/fr1vpxejY2N2Guvvby/8/k8tmzZ4nWQn3rqKbz//vtYsWKFF+UEIDSfWxLuvfdejBgxAkuXLvVFKDU3N1d13EpR1xuMrAKcKLH+/fv7yjU2NhaVC27r378/GhoaipL0K/QIn4kTJ2LixImwLAsvvvgifvrTn2L27NkYOHBg0SqzwXOoHF1xqPXv+bOf/Qzbt2/32VOKvffeG4MHD8Y//vEPb9vBBx+MBx54oCgX4d/+9jcACBUzPvzww4oXE4h77w0YMACWZaGxsbGkaFUt48ePx5e+9CXvub799tt9otK9996LSZMm4fbbb/ftp9e7Tq0XplA2tPXzOmDAADz77LOwbTuRyNbQ0BD5HAOFe/SBBx5AOp3Go48+6hMYf/Ob34Qet9zvRAghhHRE6KkIIYR0eubMmYP6+np861vfKlqdEQAuueQSfPTRR/je974X63gffvghZs6ciXQ6jTlz5sS2o1+/fjjttNNw4YUX4sMPP/Si5hT33Xef7+9f//rXyOfzXuJ01ZkOJm7/2c9+VnQuVSZOlIoQAplMxtdZb2xsrGoV0WoYP3486uvrce+99/q2v/fee3jqqae86WD7778/9txzT9x///2+6brvvvtu0cqGJ598MrZs2QLLsjB27Niiz/77719kh2maGDdunLci68svv1zS7gMOOCB0Nc2o3yLJ7xmH/fff33dN5USvf/7zn3jvvfew7777ettOOeUUCCHwi1/8wlf2nnvuQX19PU488UTf9nw+j/Xr1/sWdkhC3HtPTZsMCltBkkZmhXH22WfjgQce8BYMsCzLZ2/w93r11Vd9i4m0Nu3xvE6ZMgVNTU245557Eu133HHH4fXXXy96dpYsWQIhBI455hgAzjWlUimYpumV2bVrF375y19GHrvU70QIIYR0RBjBRgghpNOzzz774Je//CW+/OUv44gjjsDcuXOx//7749///jfuuusu/OEPf8All1yC6dOnF+371ltv4fnnn4dt29iyZQteeOEFLF68GNu2bcOSJUtw0EEHlTz35z//eYwePRpjx47FgAED8O6772LBggUYNmyYtxKm4qGHHkIqlcLxxx/vrSL66U9/GqeffjoAYMKECdhtt90wc+ZMXHnllUin07jvvvvwyiuvFJ334IMPBgBcf/31mDJlCkzTxCGHHOJNx9Q5+eST8dBDD2HWrFk47bTTsH79elx99dXYc8898dZbb8Wu51rRr18/XHHFFbjsssvwla98BWeccQa2bNmCq666CtlsFldeeSUAwDAMXH311Tj//PNx6qmn4oILLsDHH3+MefPmFUUi/td//Rfuu+8+TJ06FRdffDE+85nPIJ1O47333sPTTz+NU045BaeeeioWLVqEp556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VKq2cTP0Q0SHfaH4dN8r72b06dMHp512Gu666y5v2/333w/DMDB9+vSi8k1NTTjmmGOwZMkSzJ07F7///e9x1lln4YYbbsAXv/hFr5yUEl/4whfwy1/+Ev/93/+Nhx9+GJ/97GcxZcqUomO+/vrrOOKII/B///d/uPnmm/Hoo4/ipJNOwkUXXYSrrrqqdS6ckBqQeGpON3YqhITRVX3QU089BQA46KCDKtqfkDhU4oPohwjx05X8UD6fx65du7BmzRrMnj0bn/rUp3w2EVJr2soHLVy4ECNGjEA2m8WYMWPwzDPPlCy/cuVKjBkzxstJuGjRIt/3d955JyZOnIjddtsNu+22Gz73uc/hL3/5S0W2xYVTRLsRX/3qV3HMMcfgtddew0EHHYS77roLX/rSl0JzDvziF7/Aq6++il//+tf40pe+BAA4/vjj0atXL3znO9/B8uXLcfzxx+Oxxx7D008/jZ/85Ce46KKLvHKZTAaXX36575hz585F79698eyzz3qh2ccffzyam5tx3XXX4aKLLsJuu+3WyrVASHIMw0gUQdmW0ZaEdBa6mg969dVXccMNN+DUU0/FIYccUmm1EFKWpD5I7UMI8dMV/FBjYyP23HNP7+9x48bh6aefRq9evaqqG0JK0RZ9oaVLl2L27NlYuHAhjjzySPzsZz/DlClT8Prrr2Po0KFF5deuXYupU6figgsuwL333os///nPmDVrFgYMGIBp06YBAFasWIEzzjgDEyZMQDabxQ033IDJkyfjtddew1577ZXYxjjQ+3Yjjj76aOyzzz6466678Le//Q2rV6+ODIl+6qmn0LNnT5x22mm+7SpU+MknnwQAPP300wCAL3/5y75yZ555pu/vpqYmPPnkkzj11FPRo0cP5PN57zN16lQ0NTXh+eefr8VlhmJZlu+ctm232rlI14ORA4RUT1fyQe+88w5OPvlkDBkyBD//+c/LlqcPItXACDZCakNX8EP9+/fH6tWr8eyzz+LOO+/Ehx9+iGOOOQYbN24suR/9EKmGtvBBP/rRj3Deeefh/PPPx6hRo7BgwQIMGTIEt99+e2j5RYsWYejQoViwYAFGjRqF888/H1/96ldx0003eWXuu+8+zJo1C4ceeigOOOAA3HnnnbBt23t+WwN6326EEALnnnsu7r33XixatAif+tSnMHHixNCyW7ZswaBB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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABNgAAAKcCAYAAAA6rwc8AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOy9eZwcVbn//zmnqrp79iSTHQiEqJAYEEgQEwwEkSiBexGJ5CpGdm++QSHJRblh+REXjCxqVAgIBpALAl4BQUQlCASRgCwRuCwKAgmExJA9ma27qs7vj1OnuqqXma6Znskyn/fr1ZCpOVV9qrqnnno+51mEUkqBEEIIIYQQQgghhBDSLeTOngAhhBBCCCGEEEIIIbszFNgIIYQQQgghhBBCCOkBFNgIIYQQQgghhBBCCOkBFNgIIYQQQgghhBBCCOkBFNgIIYQQQgghhBBCCOkBFNgIIYQQQgghhBBCCOkBFNgIIYQQQgghhBBCCOkBFNgIIYQQQgghhBBCCOkBFNgIIYQQQgghhBBCCOkBFNgIIYTskdx6660QQoQv27ax995748wzz8SaNWuKxr/22ms444wzMGrUKKRSKQwePBjTp0/H73//+6Kxjz/+eOzYqVQKQ4YMwZFHHolLLrkEq1at6otT7DYPPfQQFi5cWPJ3++23H84444xuHbcn+/YlZ5xxBvbbb78+f99cLocDDzwQ3//+98NtTz31FBYuXIgtW7Z0+7gLFy6EECK2berUqZg6dWpF+19yySU49NBDMWjQIGQyGey///746le/WvJ7nMvl8K1vfQv77bcf0uk0DjzwQPz0pz8tGjdr1ix87nOf687p9BpLlizBrbfe2q19S11PIUTs78jcFx5//PFuz7Ezevv4lfLee+9h7ty5OProozFgwAAIIcpe1/322w8nnnhi306QEEII2UlQYCOEELJHc8stt2DFihVYtmwZzj33XNx5552YMmUKWlpawjH33nsvDj30UPz1r3/FZZddhkceeQTXX389AGD69On45je/WfLY3/ve97BixQo89thjWLp0KaZOnYqbb74ZY8eOxR133NEn59cdHnroIXzrW98q+bv77rsPl112WR/PqH+wZMkSbN68GV//+tfDbU899RS+9a1v9UhgO+ecc7BixYpu779lyxZ88YtfxC9+8Qv84Q9/wIUXXogHH3wQRxxx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vfSSUkqpzZs3q5qaGjV9+vTYMVevXq3S6bT60pe+pJRSyvM8NXLkSHXYYYfFjvfOO+8ox3HUvvvuG2678847FQB1zz33xI757LPPKgBqyZIlSimlfv3rXysA6m9/+1uX51jI2LFj1Wc/+9mi7VdffbUCoN5+++1O9+/s8zTfkShHH320Ovrooyue39q1axWA8HXEEUeoNWvWxMYcd9xx6oADDii5fyqVUl/96leLtu+1115q5syZsW2f+tSnlGVZFc/NUO6798QTTygA6pJLLul0/49+9KOJrkmUUtcTgLr88svDn8194bHHHut0P6WUOv3002Pfwa997WtqwIABnc6h8PjmO37QQQcpz/PCcdu3b1dDhw5VkydPDreZ78hVV10VO+acOXNUJpOJ/Y0kwfyN3HLLLSV/v++++6oTTjihW8cmhBBCdjcY400IIWSP5hOf+AQcx0FDQwNOPPFEDB8+HL///e8xbNiwio+hgkibJGl4qovoHAD4+Mc/jhdffBFz5szBH//4R2zbtq3s2NNOOy3286mnngrbtvHYY4+F29566y186UtfwvDhw2FZFhzHwdFHHw0AJdMnK+V///d/ceSRR6K+vh62bcNxHCxdurRHxzzmmGNiKbljx44FABx//PGx62y2m5TeFStWoK2trahb6T777INPfepT+NOf/gQA+Pvf/473338fX/rSl2LH23fffTF58uTYvg8++CAGDBiAf/u3f4PruuHrkEMOwfDhw8OUvEMOOQSpVApf/epX8Ytf/AJvvfVWxef7/vvvY+jQoRWPB6r7eXqeFzs33/djvx88eDCeffZZPPnkk7jpppuwadMmHHPMMVi7dm1sXGd/A6V+N3To0KKuvX/605/gum5F867ku2c6/Z533nkVHXNX5OMf/3jYbOL+++/Hhg0butzHfMdnzZoVi8isr6/HKaecgqeffroodfnf//3fYz8ffPDBaG9vx/r166tzIoQQQkg/hgIbIYSQPZrbbrsNzz77LFauXIn3338fL730Eo488sjw96NGjcLbb7/d6TFMmtk+++xT8fuuXr0aI0eO7HTMggULcM011+Dpp5/G8ccfj+bmZhx77LF47rnnisYWpuXZto3m5uawy+OOHTswZcoUPPPMM/jud7+Lxx9/HM8++yzuvfdeAEBbW1vFc49y77334tRTT8Vee+2F22+/HStWrMCzzz6Ls846C+3t7d06JoCwGL4hlUp1ut28lznfUimtI0eODH9v/l8qpbdw27/+9S9s2bIFqVQKjuPEXuvWrQvFjjFjxuCRRx7B0KFDcd5552HMmDEYM2ZMrGZeOdra2pDJZLocZ6j253nsscfGzss0LjDYto2JEyfiyCOPxDnnnINHH30Ub731Fr7//e+HY6LftygtLS3IZrNFnx0AZDKZXv/uffDBB7Asq6L07V2VWbNm4eabb8aqVatwyimnYOjQoTjiiCOwbNmysvt09bfg+z42b94c297c3Bz72aTJdvczIoQQQkge1mAjhBCyRzN27Niwi2gpjjvuOFx33XV4+umnS9Zha21txbJlyzB+/PiKHfi//vWvWLduHc4+++xOx9m2jfnz52P+/PnYsmULHnnkEVx88cX4zGc+g3fffTdW02vdunXYa6+9wp9d18XGjRtDh/nRRx/F+++/j8cffzyMcgJQsp5bEm6//XaMHj0ad999dyxCqaOjo0fH7S7mfAsjqwAdJTZ48ODYuHXr1hWNK9w2ePBgNDc3FxXpNzQ0NIT/njJlCqZMmQLP8/Dcc8/hpz/9KebOnYthw4YVdZktfI9NmzZ1cXZ5qv15/uxnP8P27dtj8+mMvffeGyNHjsQ//vGPcNtBBx2Eu+66q6gW4csvvwxAd+wtZNOmTYmaCUSp9Ls3ZMgQeJ6HdevWla0luLPIZDIlmwiUilA788wzceaZZ6KlpQVPPPEELr/8cpx44on4xz/+gX333bdofFd/C1JKDBw4sApnQQghhJBKYAQbIYSQfs28efNQU1ODr3/960XdGQHgwgsvxObNm3HppZdWdLxNmzZh9uzZcBwH8+bNq3geAwYMwIwZM3Deeedh06ZNYdSc4Y477oj9/Ktf/Qqu64YdCo0AUVi4/Wc/+1nReyWJWhFCIJVKxQSOdevW9aiLaE+YNGkSampqcPvtt8e2v/fee3j00Udx7LHHAgAOOOAAjBgxAnfeeWcsXXfVqlV46qmnYvueeOKJ2LhxIzzPw8SJE4teBxxwQNE8LMvCEUccEXZkfeGFFzqd94EHHliym2a5zyLJ51kJBxxwQOycuhK93nzzTbz33nv40Ic+FG476aSTIITAL37xi9jYW2+9FTU1NfjsZz8b2+66Lt59991YY4ckVPrdO/744wHoLpydkU6n+zxSa7/99sM//vGPmCi4cePGou9glLq6Ohx//PG45JJLkM1m8corr5Qcd8ABB2CvvfbCL3/5y9h3vKWlBffcc0/YWZQQQgghfQMj2AghhPRrxowZg//5n//BaaedhsMPPxzz58/HAQccgH/961+4+eab8fvf/x4XXnghZs6cWbTvG2+8gaeffhq+72Pjxo145plnsHTpUmzbtg233XYbPvrRj3b63v/2b/+G8ePHY+LEiRgyZAhWrVqFxYsXY9999w07YRruvfde2LaN4447Luwi+rGPfQynnnoqAGDy5MkYOHAgZs+ejcsvvxyO4+COO+7Aiy++WPS+Bx10EADgyiuvxPHHHw/LsnDwwQeH6ZhRTjzxRNx7772YM2cOZsyYgXfffRff+c53MGLECLzxxhsVX+dqMWDAAFx22WW4+OKL8ZWvfAVf/OIXsXHjRnzrW99CJpPB5ZdfDgCQUuI73/kOzjnnHJx88sk499xzsWXLFixcuLAoEvE//uM/cMcdd2D69Om44IIL8PGPfxyO4+C9997DY489hpNOOgknn3wybrjhBjz66KM44YQTMGrUKLS3t4fdFz/96U93Ou+pU6fi29/+NlpbW2Oih/ksfvzjH+P000+H4zg44IADEn2ePeGll17CvHnzMGPGDOy///6QUuLll1/Gj370IzQ3N+PCCy8Mx370ox/F2WefjcsvvxyWZeHwww/Hww8/jBtvvBHf/e53i1JEX3rpJbS2tuKYY46JbT/22GOxfPnyLuuwVfrdmzJlCmbNmoXvfve7+Ne//oUTTzwR6XQaK1euRG1tbdjB1ETg3X333dh///2RyWTC699bzJo1Cz/72c/w5S9/Geeeey42btyIq666Co2NjbFx5557LmpqanDkkUdixIgRWLduHRYtWoSmpiYcfvjhJY8tpcRVV12F0047DSeeeCL+8z//Ex0dHbj66quxZcuWWHpvtfn1r38NAGEdwueeew719fUAgBkzZsTGrlu3LhwfZb/99us0upgQQgjZ7di5PRYIIYSQ3sF0EX322WcrGv/KK6+o008/Xe29997KcRw1aNAg9dnPflb97ne/KxpruvmZl23bqrm5WU2aNEldfPHF6p133qnoPX/wgx+oyZMnq8GDB6tUKqVGjRqlzj777Nj+pvvf888/r/7t3/5N1dfXq4aGBvXFL35R/etf/4od76mn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bWzbww8/jIkTJ8JxnHDMsmXLMG/evNiYyZMnA9CC+csvvxw7xplnnokDDzwQF110Ua+IawAFNtIJmUwGjz32GC655BJcffXV+OCDD7DXXnvhwgsvxOWXXx6Ok1LigQcewPz583HVVVchm83iyCOPxEMPPYQDDzwwdsxx48bhhRdewHe+8x1ceumlWL9+PQYMGIAPf/jDYa2Cznjsscdw5plnxraZLkCnn346br311p6feMCXvvQlvP/++7jhhhtwyy23YPz48bj++usTtdwmex4U2AipPruavXn11Vfx1ltvAQC+/OUvF/2+qzSIpNDekEIosBFSfXY1WzNs2DCMHDkSP/jBD7B27Vq4rou9994bJ554Ii6++OKiFLyeQltDStEXvs38+fMxa9YsTJw4EZMmTcKNN96I1atXY/bs2QB0B9A1a9aE0aSzZ8/Gtddei/nz5+Pcc8/FihUrsHTp0lh30AsuuABHHXUUrrzySpx00km4//778cgjj+DJJ58EADQ0NGD8+PGxedTV1aG5ubloezURyiSzEkIIKcu2bdvQ1NSEN771VTRkKk8B2N6exYcvvxFbt26taJWHEEJI/6W7tgagvSGEEFI5fe3bLFmyBFdddRXWrl2L8ePH40c/+hGOOuooALqO4jvvvIPHH388HL98+XLMmzcPr7zyCkaOHImLLrooFOQMv/71r3HppZfirbfewpgxY3DFFVfg85//fNk5TJ06FYcccggWL15c8byTwgg2QghJgJAiWZ0CWb54JyGEEFKKpLbG7EMIIYQkoa98mzlz5mDOnDklf1cqC+3oo4/GCy+80OkxZ8yYgRkzZlQ8h6iA11tQYCOEkAQwRZQQQkhvwxRRQgghfQF9m+rCq0MIIYQQQgghhBBCSA9gBBshhCSAqzyEEEJ6G0awEUII6Qvo21QXXh1CCEmAEBJCJniJ7t1mlyxZgtGjRyOTyWDChAn485//3On45cuXY8KECchkMth///1xww03FI255557MG7cOKTTaYwbNw733Xdf7PeLFi3C4YcfjoaGBgwdOhSf+9zn8Pe//71b8yeEENJ9EtuaHtgbQggh/Ze+8m36C7w6hBCSAGFZkAlewrISv8fdd9+NuXPn4pJLLsHKlSsxZcoUHH/88Vi9enXJ8W+//TamT5+OKVOmYOXKlbj44otx/vnn45577gnHrFix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O8Yc87nnnsPOnTsxZ84c2LYN27bx+OOP4+abb4Zt23AcJ/7NIgcs+7u8aWpqQlNTU9H2E088EYD+PdcKyhsSRXxZk5c3lURMM1qajBf2d3lTioMOOggAYNt21ccohPKGRFGtbkOi4d05gGhtbcUFF1yA7373u/62Bx54AFJKXHjhhUXjBwcHcdppp+F73/seli1bhv/8z//Exz/+cVx//fX48Ic/7I9TSuEf/uEfcN999+GLX/wiHnnkEZx00kn4wAc+UHTMP/7xj3jve9+LF198Ed/85jfx05/+FH/3d3+HK664Al/5ylfqc+FV8OlPfxr9/f344Q9/GNq+d+9e/OhHP8LHP/5xJBKJfTQ7si+JnbLjCaHW1tbQK5VKRR4/mUxizpw5WL9+fWj7+vXrsWDBgsh95s+fXzR+3bp1mDt3rv89LTXGHPP000/HH/7wB2zcuNF/zZ07FxdffDE2btwIy7Li3yxywDJe5c0vfvELAMC73vWuqvaPgvKGRFGNrDHyJk7ENKOlyf7OeJI3uVwOAwMDeP7557F06VIcddRRoTmNFMobEkW1soZEUzuTONkv+PSnP43TTjsNL730Et71rnfhu9/9Lj760Y9G1ie499578cILL+CHP/whPvrRjwIAzjzzTDQ3N+Nf/uVfsH79epx55pn4r//6L/zyl7/Ef/zHf+CKK67wxyWTSVx77bWhYy5btgwtLS144okn0Nra6o8dGhrCv//7v+OKK64YE7U43v3ud+M973kP7r77blx22WX+9gceeABDQ0P41Kc+tQ9nR/YlUspYkZTVRF0uW7YMixYtwty5czF//nzccccd2Lx5M5YsWQIAuOaaa7B161Z873vfAwAsWbIEa9aswbJly7B48WJs2LABd911V8ib+4UvfAHvf//7sWrVKnzoQx/Cj370Izz22GN44oknAOgaJscee2xoHk1NTZg8eXLRdkIqYbzJmxdeeAHXX389zj///JoWZae8IVHElTVmH0Abscx3HkCkQ2csREsbHMfBr3/9a6xZswZDQ0N06JDYjAd509nZiWnTpvl/z5s3D7/85S/R3Nw8onsThPKGRDEaus2BBO/OAcYpp5yCI488Et/97nfxhz/8Ac8880zJ8Olf/OIXaGpqwgUXXBDafskllwAAfv7znwMAfvnLXwIALr744tC4iy66KPT34OAgfv7zn+P8889HY2Mjcrmc//rgBz+IwcFBPPXUU7W4zEgcxwmd03XdsuM/9alP4cknn8SmTZv8bXfffTfe+9730uBwADMaXp4LL7wQq1evxle/+lUcf/zx+PWvf41HH30Uhx12GABg+/btoS5vM2fOxKOPPopf/epXOP744/G1r30NN998Mz7ykY/4YxYsWIAHH3wQd999N9797nfjnnvuwUMPPYR58+aN/KYQEsF4kjevv/46zj33XMyYMQPf+c53hh1PeUNGykgi2CqJmGa0NBlPjAd5M2XKFDzzzDN44okncOedd+Ltt9/Gaaedhu3bt5fdj/KGjBRGsNUW3p0DDCEEPvWpT+H+++/HbbfdhqOOOgonn3xy5Njdu3ejo6OjKNR/6tSpsG3b72qze/fukPfS0NHRUXS8XC6HW265BYlEIvT64Ac/CAB+DY96cPrpp4fOWUrwGi6++GKkUincc889AHT49zPPPEPvDhkVLr/8crz++usYGhrCc889h/e///3+e/fcc09RS/pTTjkFv/vd7zA0NITXXnvNj3YLcsEFF+CVV15BJpPByy+/PGzawa9+9auizm+EVMp4kTdvvPEGTjvtNNi2jZ///OeYNGnSsPtQ3pD9gWXLluE73/kOvvvd7+Lll1/GlVdeWRQt/YlPfMIfv2TJErzxxhtYtmwZXn75ZXz3u9/FXXfdhauuusof84UvfAHr1q3DqlWr8Morr2DVqlV47LHHsHTpUgD5aOngi9HSZKSMB3lj2zbmzp2L973vfbjsssvwi1/8Aq+++ir+/d//vex+lDeEjC2YInoAcskll+B//+//jdtuuw3/9m//VnLc5MmT8fTTTxfV49i5cydyuRymTJnij8vlcti9e3dICBWmCEycOBGWZWHRokX453/+58hzzpw5cySXVpbbb78dPT09/t9m/qWYOHEiPvShD+F73/sevv71r+Puu+9GOp32i6mSA5O4nht6eciBzP4ub9544w2ceuqpUErhV7/6FQ455JBh9wEob8jIqSZKIO74Cy+8ELt378ZXv/pVbN++Hccee2xF0dJXXnklbr31VkyfPr1ktPSXv/xl/Ou//iuOPPJIRkuTUWF/lzeFHHLIIZg+fXrJpgoGyhsyUqjb1BYa2A5ADj74YPzP//k/8corr+CTn/xkyXGnn346fvjDH+L//b//h/PPP9/fbuo+nX766QCA0047Dddffz2+//3v+zUKAOAHP/hB6HiNjY047bTT8Pzzz+Pd7343kslkLS9rWN75znfG3udTn/oUfvjDH+LRRx/F/fffj/PPPx8TJkyo/eTIfoMQ8dpTC0EhRA5c9md5s3nzZpx66qlwHAe/+tWvfKNDJVDekJESV9aYfeJy+eWX4/LLL498z0S4BDHR0uW44IILitLvylEYkU1INezP8iaKv/zlL3jzzTdx3nnnlR1HeUNGCnWb2kID2wHKcOHGAPCJT3wCt956Kz75yU/i9ddfx3HHHYcnnngCK1aswAc/+EGcccYZAICzzjoL73//+3H11Vejr68Pc+fOxW9+8xvcd999Rcf8j//4D/zt3/4tTj75ZPzTP/0TDj/8cPT09OAvf/kLfvKTn/gd2krxxhtv4JlnngEA/PWvfwUA/N//+38BAIcffjjmzp0b6z4Mx1lnnYVDDjkEl19+OTo7Oxk+TejlISQm+6O82blzp1/75q677sLOnTuxc+dO//1DDjmk4mi2SqG8IUFGI4KNkPHG/ihvXnjhBVx55ZW44IILcMQRR0BKiT/84Q+46aabMHny5FAKdq2gvCFBqNvUFhrYSEnS6TR++ctf4tprr8U3vvENvPXWWzj44INx1VVX4brrrvPHSSnx4x//GMuWLcP111+PTCaD973vfXj00Udx9NFHh455zDHH4He/+x2+9rWv4ctf/jJ27tyJCRMmYNasWX6dgnL88pe/LBICpgPQJz/5yUhv60iQUuITn/gEVqxYgRkzZvheLXLgQiFESO0Za/Lmj3/8I1599VUAwMc//vGi96+77josX7585BcegPKGBKGBjZD6MNbkTXt7O6ZPn45vfvOb2L59O3K5HA455BCce+65+NKXvoQZM2bU/B5Q3pAg1G1qi1BKqX09CUIIGet0d3ejra0Nf/7KZ9CSrjz8v2cwg1nX3YGuri6/dTshhBASRbWyBqC8IYQQUjnUbeoDI9gIISQGQop4dQqkGH4QIYQQEiCurDH7EEIIIXGgblNbaGAjhJAYMIyaEEJIvWGKKCGEkNGAuk1t4d0hhJAYGCEU50UIIYTEoRpZQ3lDCCEkLqMla771rW9h5syZSKfTmDNnDv77v/+77PjHH38cc+bMQTqdxhFHHIHbbrutaMzDDz+MY445BqlUCscccwweeeSR0PvLly+HECL06ujoqGr+lUJJTAghhBBCCCGEEEJqzkMPPYSlS5fi2muvxfPPP4+TTz4ZH/jAB7B58+bI8a+99ho++MEP4uSTT8bzzz+PL33pS7jiiivw8MMP+2M2bNiACy+8EIsWLcLvf/97LFq0CAsXLsTTTz8dOta73vUubN++3X/94Q9/qOu1MkWUEEJiIISMV6dA0I9BCCEkHnFljdmHEEIIicNo6DY33ngjLr30Ulx22WUAgNWrV+O//uu/8O1vfxsrV64sGn/bbbfh0EMPxerVqwEAs2fPxrPPPosbbrgBH/nIR/xjnHnmmbjmmmsAANdccw0ef/xxrF69Gg888IB/LNu26x61FoSSmBBCYiAsCzLGS1jWvp4yIYSQ/Yy4sobyhhBCSDVUq9t0d3eHXkNDQ5HHz2QyeO6553DWWWeFtp911ll48sknI/fZsGFD0fizzz4bzz77LLLZbNkxhcf885//jOnTp2PmzJn42Mc+hldffbXym1MFFUWwua6Lbdu2oaWlBUKwawQhZP9FKYWenh5Mnz4dMmZ0AMBCoPWG8oYQMl4Yibxhk4P6QllDCBlPjKa8MWNnzJgR2n7ddddh+fLlReN37doFx3HQ3t4e2t7e3o7Ozs7Ic3R2dkaOz+Vy2LVrF6ZNm1ZyTPCY8+bNw/e+9z0cddRR2LFjB77+9a9jwYIFeOmllzB58uSKrzkOFRnYtm3bVnQDCSFkf2bLli045JBDYu9HA1t9obwhhIw3qpE3NLDVF8oaQsh4ZDTkjRm7ZcsWtLa2+ttTqVT5/QqcGUqpsg6OqPGF24c75gc+8AH//4877jjMnz8fRx55JO69914sW7as7HyrpSIDW0tLCwAdXmf+Pw7D+YVU7CPWnsI5joU5jQb7k89uLH0mI7lvY+k6xgqj9j1ULrp7+zBr1qyq1jIAEDJmnYIqouQOZCqRN5V8X0byO6vm+1jufOWOV2q/uNe4P63lI2V/WUNH4zPZX+7FvmAs/CZ6enrwjirlTVxZY/YhlVFv3aaQan+rtTpPJXrOSGRrPXS9ONdeq7WwGnldi/PVcy0vdU37s/wYS7aFsSBrFIDeUZQ3Zmxra2vIwFaKKVOmwLKsomi1nTt3FkWgGTo6OiLH27btR56VGlPqmADQ1NSE4447Dn/+85+HnXe1VGRgM1bAlpaWim5i5DFKbB8rP+4D0cA2FhaEShlLnweNa7VnNA1syivMWW1KCCPY6stw8qbST220lJlKzlkvA9uBxv60ftK4tu8ZS7+hauQNI9jqy0h1m9EysMU913gysFVy3JEcO8756rXeBs83GucIsr/KkNH+TgzHWJA1Cvl5jIa8iStrkskk5syZg/Xr1+P888/3t69fvx4f+tCHIveZP38+fvKTn4S2rVu3DnPnzkUikfDHrF+/HldeeWVozIIFC0rOZWhoCC+//DJOPvnkWNcQh33aRXR//WGPB8bCYlAp/J6Mb/an7yIACCniCSG5v13h/s++WjNExLn56dcGygFyoBFX1ph9yPgjqDyTMLWWDVH3up7yZzQ+29G+JjL6jPQ7NBq6zbJly7Bo0SLMnTsX8+fPxx133IHNmzdjyZIlAHQH0K1bt+J73/seAGDJkiVYs2YNli1bhsWLF2PDhg246667Qt1Bv/CFL+D9738/Vq1ahQ996EP40Y9+hMceewxPPPGEP+aqq67C3//93+PQQw/Fzp078fWvfx3d3d345Cc/GfsaKmXUDGxj/YccXHzG+lzJvoPRa/s5QtZACDFFdCyzr39nQSMbFaIDkwPxc4+65n39W9zfYYro2GVf/MZrLVeiHEJj0ZBXbk71WmNGWyfkWrl/MxZ/N3EZDd3mwgsvxO7du/HVr34V27dvx7HHHotHH30Uhx12GABg+/bt2Lx5sz9+5syZePTRR3HllVfi1ltvxfTp03HzzTfjIx/5/9n781jbtrM+FPyNZjar2Xuf5jbnXvvaXPsFsON6KcqmjK0YowJsgyLICw5OUropBEZYpjMupHqXRjhIJeSIR24RGgcwXZCxVTKIKLIs7JcUAdkgMKZ5BBvi7ja+x/eebnermXOOMeqPb4wxx5xrrnavfc7e54yftM4+e+3ZjDnnWuMb3+/7fd/3bX6b1772tXj/+9+PH/uxH8OP//iP46UvfSk+8IEP4NWvfrXf5umnn8a//Jf/EteuXcP999+Pr/mar8Ef//Ef+/OeBu6ogu2sIU5wEREREfcutum4rLNttD0R5xWLPut38rN93p2diIh5WES03Y6SA/O+16dJMrTrjUbS62Q4z9d2Vuf2e7Um7rp4+9vfjre//e2df/v1X//1mfde//rX48///M8XHvPNb34z3vzmN8/9+/vf//61xrgNRIItIuI2ITrSdxhGA+zk0X3GBRgXa20fsT1sM5odF0EREZsjfn9OF+vaGrdPxOnirHzu27ZwE3JtUxXbnSDZwnNErI976b7d6Ws9j2Rb9G22i0iwRURE3FGc6oLM6MW/bwIu6LXO9hFbxaafmbO60Inke8R5w6rfpfjZPgHWtTVun4gzhdOu37UIq3xPt50q2kUuxDkgYhs4S51DV8W5SR+Nvs1WEQm2iIgVcS4myAjCNoi0eeCcXutsH7F1dC1auhbz5+V7e17GGRERP6u3CevaGrdPxKlhG908bxdOWqpgGTGwCnl+p+9BxL2B+DnbAqJvs1VEgi0iIuLuwKqk2gnJNyYEmFhDRr3GthHrYZ4DEAmAiIiI8451bY3bJ+J0sKpdOa/O/iZq06hQjbhdiI10ThfRt9kuIsEWERFxvnGaarUuRBl1RETEPYpNU7OjI7QBYoromcEqn/uT1AA9je/HJqlp7e/qSeqxRURsC+3P4Hn7vJ2LNNHo22wVaxFsZ/7DEbEyzsWX3eJuMt5307WcCdxucg2wMup1jFCUUUdEnBRx7oy457CurXH7RGwVt6Pu01mqVbYJyRYRcVo47+TauUH0bbaKqGCLiIg4n7gT5BoAxjnYGoZlnW0jIiIiziqik317sa6tcftE3D6cxNmfV0d0mwTCSRoCrUOyxQBIxO1A/IydHqJvs11Egi0iYgVEx+J0sfYi8A6RawAAtqaMmkUZdUTENhCduPOL+Ow2wLq2xu0TcerY1me5qyFPJNkiIgjR97qNiL7NVrE2wRYn0LsHUfYdcS5xJ8k1INYpiIiIiIg4fcQabGcSp+UDueMyLF6bb3L+ba33I8kWcbtwt6WGnnmfO/o2W0VUsEVERJwfrEuudW1/pwm6iK3izC9aIraK6MDdGcTvWMS9irM232xar61tK7vUc13nip1FI243VmmsMQ/xsxdxFhAJtnsc58U5vZsM9qbXols78fPw4LaJVYmxUybQYp2CiIiIiIjTRqzBFrFtzFt7LiLbulJFu7ZbtE/EdtF17++W+72I7F11/7vlXtxORN9mu4h3JyLiDEOb+jXvbycB63idWxh9e9RpTka9zmsD/MIv/AIeffRR5HmOV77ylfjDP/zDhdv/wR/8AV75ylciz3O85CUvwXve856ZbT74wQ/i5S9/ObIsw8tf/nL87u/+buPvP/VTP4Wv/uqvxs7ODh544AH803/6T/HpT396o/FHRJwWzvU8FRGxKjaxNTFt557Bac2Dqy4rl2137teUZxQnJaDOMu6Gazi3iLZmq1ibYIus8N2H+EzPHtYhzzYh2hYtfE5i4EJCcBE52IWFmy0jzm5n2qdrZb3ya/04xgc+8AG84x3vwI/+6I/ik5/8JF73utfhm77pm/Dkk092bv+5z30O3/zN34zXve51+OQnP4kf+ZEfwQ/8wA/ggx/8oN/m4x//ON7ylrfgsccew1/+5V/isccew7d/+7fjT/7kT/w2f/AHf4Dv/d7vxR//8R/jIx/5CKqqwhve8AYcHx+vf58iIk4RcSG+Ok4yL28T8ZmtibVtzWb2JgZzzi9Ok2RbJYV0lWkkfu+3g7udsNzmtd3N9+nUcBt8m3sJ8e5EnBtsMmHOcyzulIOxCjYd16r7rXIf173Xy+7nqd7v21xTjQmx9mtd/MzP/Ay+67u+C29961vxspe9DE888QQeeeQR/OIv/mLn9u95z3vwohe9CE888QRe9rKX4a1vfSu+8zu/Ez/90z/tt3niiSfwjd/4jXj88cfxlV/5lXj88cfx9V//9XjiiSf8Nh/+8IfxHd/xHfiH//Af4h/9o3+EX/u1X8OTTz6JT3ziE2tfQ0TEvYq1pjqnvG2/toBtzcvRWbkz2MTWrGtvYjDnbOEk37WujISTZie0ibZ526wytojNcbvu353KZomfjzuP2+Hb3EuIBFvEXYtVHIe7LZK/zWtZdYzrnHPr93qdumzbcl45X/+1BoqiwCc+8Qm84Q1vaLz/hje8AR/72Mc69/n4xz8+s/0b3/hG/Nmf/RnKsly4zbxjAsD+/j4A4NKlS2tdw90Gg9UcjYjbi3O9KF82F51wrrqj8/IJcd6CYqeGTWzNmvYmBnPODjadz1YlRE5CuEWS7e5HV+29O3HeiDuEU7Y19xrWujv32trmXsLd9mzXXYifaOE+T4GwgXN02g7Etg3ZJuNde59593GVtNHTqMu2YZ2Cg4ODxms6nXYe/tq1a1BK4cEHH2y8/+CDD+Lq1aud+1y9erVz+6qqcO3atYXbzDumMQbvfOc78Y//8T/GK17xiuX35R5BJNrOFs7l4nydOWmDOey2zMungFVItK0TbaeoIDwxTrkGWwzmnH+c5KuwbaXSqiTbuZyz7yBux/26U3Xd4mfhDCHWYNsqYhfRiLsOJ0mx7OrMOdcArLIId9uw5Vz2snGvUg/DHWet61iARd14TuLkzBvjylim/DhFMC7A1jAsbttHHnmk8f5P/MRP4F3vetf8/VjzBhljZt5btn37/XWO+X3f9334q7/6K/zRH/3R3HOeFZx2N+Su78EqndQibg9Ou2uYm+u20rl50/nJ6Bk70nXdt31e7rqeFexd17nX3X7eWJdewpZt92lhXVvj9gEooBMiyzJkWdZ47zSCOQ899FAM5myATaaWbc157tzbON6qtjh2elwNZ2F9EZ/VvYFNfZuIbkSCLcLjtJ3UbWDZRH/SyPbKDsa6TlKHc7TSbiu8f9af2TxsTLJtqmjbFtia0mj73J966ins7u76t9vOjsN9990HIcSMM/Lcc8/NOC0OV65c6dxeSonLly8v3KbrmN///d+P//Sf/hP+23/7b3jhC1+45AIjIu48tu0EzOvc7LAVsu02on05Jx7+KkGOFW3etoNiC3GbbPdWsK6tcftgvYBODOacP5wG4bGtOXTV4NM2ib2I84f25yMGLe8wNvRtIroR705EA+fZ0J2FFJeFWGNhv04a2mnXvujad969NgteXWgfZ2a79j07IbnGjAa7Q+k/u7u7jdc8gi1NU7zyla/ERz7ykcb7H/nIR/Da1762c5/XvOY1M9v//u//Pl71qlchSZKF24THNMbg+77v+/A7v/M7+C//5b/g0UcfXfs671acpItaxO3BNhbmq6YgbpSquErNtUUpikvmrq7xzJt/573fPkbnPV2n9uUpY61ncBL14DnDU089hf39ff96/PHHZ7Y5S8Gc//pf/+s9Hcw5UZWSrY1iuymcq44rpo3eWZxG47N1jhfLbkTcjYgEW8Rdj0WkzyoOxuwBOxbbJ6zjEp5zE0OzUZrQFmvPrNpp6lSM6JIxO1JtW8Sak1Gv81oX73znO/Erv/Ir+NVf/VX87d/+LX7oh34ITz75JN72trcBAB5//HH863/9r/32b3vb2/CFL3wB73znO/G3f/u3+NVf/VW8973vxQ//8A/7bX7wB38Qv//7v493v/vd+NSnPoV3v/vd+OhHP4p3vOMdfpvv/d7vxW/91m/hfe97H3Z2dnD16lVcvXoV4/F48xsWEXFOsM3aZSsdatG8O+9vK85jq863a8/LmyjAArQdtUWk4KqBmk3GcR6wia1x9maVgE4M5pxPbF2N2oFtkV7rfG8j0dZEvBcRtxO3w7e5lxBTRCNmcNZTRdeRsa/qYLjjLt94TWennV6yZrrJMocvTI9Z+bmtMmagMc5F93xtEQea49w4LXfBdZyqSo3z9Yp7btBp5y1veQuuX7+On/zJn8Szzz6LV7ziFfjQhz6EF7/4xQCAZ599Fk8++aTf/tFHH8WHPvQh/NAP/RB+/ud/Hg8//DB+9md/Ft/2bd/mt3nta1+L97///fixH/sx/PiP/zhe+tKX4gMf+ABe/epX+21c57iv+7qva4zn137t1/Ad3/Eda1/H3YZ534OzPmfeS9g0zenUa5dtSJT5bZfYjWVBmvb1tccbfobnXs+q19Ae64p2b1N7vfT+n0NyDcD6tsbtswbe+c534rHHHsOrXvUqvOY1r8Ev/dIvzQRznnnmGfzmb/4mAArm/NzP/Rze+c534ru/+7vx8Y9/HO9973vx27/92/6YP/iDP4iv/dqvxbvf/W5867d+K37v934PH/3oRxspoN/7vd+L973vffi93/s9H8wBgL29PfR6vfWu+S7BWbQj20rjXOfaYuro3Y2ubqXhsz6L34Nt4Mxf023wbe4lRIItohO3ZYJbtOhdM7d7XhR8reGg28GYey/WTZNZoUB1+PuqaUrAYuei8actO3XrKOfWJgPDsa7o2N2W9M9121NvaITe/va34+1vf3vn337913995r3Xv/71+PM///OFx3zzm9+MN7/5zXP/7mrpnEfcrkVZJNnOPu5EUea16oHNU6yFWERSrRGomTcnn7hxwxZqsJ1EuX2igNKqxOCdxrq2xu2zBmIw52xh2ef6TlnobZBe69bYisX17x10kWzu/W0dM2IJbpNvc68gEmwRc3FqDuNt6OC1TurniVVgy66n7RStq0aYQ3qEBYOdc7fWM1vFyegY6zKHbR7aDt1KiokudIx7JWJN6+ZANgQTAkys0WlnjW0jzgcWkWzt7SLuDNZZXK8yJazybLdadD98fw1izY2zM+jUUYw+nJcX2o9VAx7z7McWSEE3Tn94rPkd20C93fjbbSbg1rU1bp91EYM5ZwtnOVjTVZi+XU9rGdZVs8VPyt2Dtc3jBvtEbIbo22wXkWCLuL3YtH7LCuovv0vr93WJn/D9ddN+HNFjNkgLbTtGyxagbWdp5TSZkzgZ4WbB/9chBFd26Nrjaf8faxJr2wIXa8qooxG6G7HKwn8bUdiIzbEN52wVO7NxuuKC+WwTGxKOwe/aMR+H77Xtx9I5eZkdaduPE5KCbcwL1HTe93UCYe39zoKabV1b4/aJ2ArWJY62ia7v4VkkmrrS/YDVbGMk2c4X4nO4yxF9m60iEmwRC7HV6MFJ0vc2XPDOdKlsORtrqcDajoX92UXyzDhKbfXanOvpItcW3TVutw2vY2MVgh98t+JunnFdRAhq1J1U5hGCm6gGFxJr2ybU2ohGKMJi1QVnjMLeOWzqFKyzz7znu+pcvC0bMlNywM6582ZEjtl52WEhYbVk7H68wMpjXiVIE45zY9XzqqUp1gyMncr3OxJsZwZ3glxY5Xxn1a6sQrSt892J5M75x8bVCE6wb8QaiL7NVhEJtoilODOT25aj+OH7m0bxG85FF7HDea1qAzqdjHkkoPa/dw+BsXobbkm20OloOCALUno6Hbn2WFckBLtcl/C9tkM3d7ztcc5z6DYh005Yp41xDrZG7YF1to04f4gL/7OP2/GMVkp5XxScmTeXWRvSSbJ1YNGc7GyJ49K8/bDba7Bu+9dBUM0Qa278oc1bccyNUy2wJfPsx4mDSu2/r6i+M3P+v3Hd0xbWtTVun4iIs4JtEm13UlEYcWdxZvzQE+CsX0P0bbaLSLBFrIQTTwzbSs8I/raoScA8cm2dRbs7TsNR6iJ69CwJRBvwGafDk2xL0CbXdAfLxu0TcUQbD5Rsnc8rcO7mKcBWcYw4607lWUYIAoBmbuxN5d2qn6+Ze74OTkis1YNYM8rDYpTnduEsL2DO8tgimli3zMDShjgdmDuXteuZ2b8z3pqXF4yPyDL3/9lzh+95+xEca1kH0YXzcPDeojF3jT8cd3usjYASavuxblBpWVmBmfHOGfsywmAr3/V1bY3bJ2IriHP29rAKOTaXpN7wePciTjuYtOnx4/foHCD6NltFJNgiVsapLTa6FrxdZFX4txWVbPOi4cbMj+IvJX1Cgkrr1SLkGnXHldBRsddhQM5B2zFyxFp3aqax47bqO0Ykm1MhdKI99jnwvkXjFw3GeGca0iqEYHusQPd456knOse9LdIsIuIEiKmiZx+bOgbr1gVzz3gVompucCYMbgCeaPOEVddhUdsQ/17HfNz+DLbtRztVtD2uubavja4xu7l8RTvS/Hv9/9B+hCTbMhXbQttn7fOMWrDrcPPOE3HXIc7Z28dppI8uO17E6SK0kxt3pj4FnLUMgzif3DuIBFvEWtj65LAqQdJO3bBYNHkuS7Vs/67ZbKrMQjgHoxHZD6VzCiZ0VpzPtFKaDDlFXYo8hzpqb8AXOUX+oC3HbmEdM/rBeHB/lxWqXkIIurEC5NSFyjt3v+c5pQvVgstqyW0bjK137FWeTURExKljlcV2+PdlNTz9cReUGOiymaxr/mrZkVktF2ob0jh3R+o+uufjLiconJNXCtIAC22fac934ZjXsH2N93ACUtCPb0mApiUcdOddNSU3ROMzcBI7tK6tcftEbBVnyUG/m7DqfLyyKniF40WsgEUChxY6s1nmBZdOiLuFnFqYYdTG7Wy2E32brSISbBFrY2uT3CbqozVq0ACrpVo6OAcDqJ0MMY/sCRwMT6p1FaqGU6i5xf1iZyN0jGpFQnhtNZmmrAqPiCkz4yA1xh5G8Dd1jlYYM13rYtWHGy8MwNmCMc+cKHBEW/c6JDb9Ncz7fJ1U9cb4mkYo1imIqHG3LBLPKzZxwuY1cAE2qCvpD9Cch9t2hBn4uaNBti0J1HTZEGA2oOTsBzBrQ1zihzao52SnXlti+/y4wwDTArsXKre3QQp6NRvjgK5aN2c9xXmjrENHkG+enQuJ1hNhXVvj9om4J3A32JGoZjt9LArQz2BeRtGCGszzznnblGxdYw7G22XzT1NxN+++rHXP23+7HfN69G22ikiwRWyEdQze6gddQnzM6W4JzNYFWyfV0sE5GCHH5ByU0MkIx8uMmVUghBOiHSMDb5BsEPXfOCNnxyvuWuRae+zKOXP2TcPcmJsOkr/WLuXaJs6RvZ5QMdF2PJeRgu7ygW6nzu0/N92nyyFtXwOak/4MaXhCGMZXUiGG20fc/ViHuIkk253FKs9q1Y7O4XsrpyzOm4fnpIq6edmEtF6H07PMhgCtz54LcqCejwVjdJx582Y47q4xh+Nu2T3GAbPAYWirttchBTuDNJZk88RgOD60AjPtp9tWca/Y7ADA4hThNbCurXH7REScN0Q12ywWXu+KxMuyIEDnMbuOv0bKvDvs0nP4HebX1e4610qfgdb4w2O378kysm0uL7bKODrOM7cxzyKisMverzDGdRB9m+0iEmwRJ8LGjuKcAsQr7dMi1xaniXanWraLPAPdC/ZOJ0N3OBjznCM3Xsap7oydj+bVeamVdrMqhLaz0VavKToCOBgMgrE7YiwgADudoxZxOUMKsvp6HCnYuFzMOnTzSMG2U0fjp0+TN3ChaqCFTmKzfR1u24Bw2wrZFqM8ERHnHqHdmNe4JURbDT1zvCDlva1ka2w3z06E8/FcW2fnZFfbrJNks+PtsCFtNGye/d0YSr1kYWDJ34SAmGrbvfC6QjvCOMAM2T5nRxifWTc0gzKzxGC9Ybf9aAdpOtXQqwSXYMk2d29D9Z3bNqgh1+W0hQrGEzn5UcEWcQ9hVTXbOiTbsuOdOyzyM5btan8uJMDa55hji7wvNec8cwPk895fQSEXNhPCuuewNqc93jZJFZJgy7CO+jLcfqXu1/4A3fd/3nXcVnsTbc1CRIIt4sQ4sRpjJt1vPtk2090SWPgl70qTmVm02wW7i44bGAi7YHdOxsw1tok1R2C1x27HaRgHuATTsI6G9o7GzDUG41TO0bDTphs6s5bCGNYYsyPZGGNghsbclSo6lxjsIAWdc8RUBSNkQMCBat8YUxNqcxy6NldpYMAMZtR3VHmI1eMOn/M85cSqknbGwYxpKt82AWPz1R3zto+IaOHE82ZENxYssFdFW72m0SaAZueQeXXB5qrZuoI0aNk/+3/jFr6Mg3FJxJojq4wGwKENveXmX2MosKFbNqQxhMB+MAPrVZAtMTAwjG1m+5wT4MYd2D5mdCcxCNTk2CJikII73fbDBWkaNnteoGZBUIzZca9c2mGOImIV4nYh1rU1bp+Ic427QX11EjSWrB1/X0iwLDje7bynp3LORUKEjjl1Hgm2lKBa0YZ2BReAWfJooVKrdcxFga92EMNfx4Jg/Ix/aQUCXcGP8PeFCuSZIP7iz2n79xmFc3v8i+7/nOe8lTTX6NtsFZFgi9gKNnYWGxHk1qTS7vRla6K48y0k1tB0iJbVCDMwYICP5LOA8AlVT8w7FLVjxHRF/w/quzAT7Mc4mJAwuiJHw3AY65Q0JnTQGHWLXHNOEl1HvQdjDIbRuI1hALeOkVUhtMdeX0PTqZtHDDacOjSdo87C2oEx73TqGs9j1rEzzjlqO3ZdxnMZwXaa4LzuCLvq9hEREaeLZfNAR0BmFUc2tCWLSg0oE9gQtqSuZAe55ufhjg6XjFvCh0tKeQRgRH0dbUWxsyHa2g+yfx3DCOwHC1b9zAZpxMwOC2yf/bsdcHPcQE2yMe6JQW7/phq2oWn/gKYdMYZuaJf9CIlBUrHNOi4nUd8tSnHtrtPHTuZgr2tr3D4Rdz3uFdd2Edlm5my37FinTbSxjv8vOudG9cC6SKog+O0OuYwE8wGgti0KT7VA0NDuXA3QvDdzLfPW6x2+xyqE1Uy65TKSKiCm2iRb6F+5wFhYz9P7IvPECB3jWgR3vEWlJGaegf9DM3PLne9EgRyH6NtsFZFgi9gaVo4qdThCjcmk7WC4idG9HxYfdvs30iyt6iCIgHelWrojcDTVa2Ek3ynBZpyk0MFQlf2/alyfJ4eYIVJKyGCSZHQ9fDYaYeykH5JrLsWykdoKckKEJdqsV+d/urGb9gQeGlJdobOjKOO1c9Sa0BnI4HY5Rw0lGzZz7IBQfTeHXAvv8zLlYBjxYS5PaXPEOgUR28LGgYmIJtYh2dskEOg5dNXxDLGo1EBYZsCpq8AZeCvQMk9BHM7DjfR3u59TfFGgw6qIdQXGecMOOtuhNNkGR64pbWZSXJkl5UQQqAHoJnDB/NzNgFmiLbwGPWv7ADU7bgAICUJHYDXucVOB12VDFIx3SubZD2gDGXh3hrnQWce977AvDbvRUJ4D81JcgWYaManMzYkcn1iDLSKixrbIttMk2uadex5ptEj9CsxZI8xbEy9QOHWRYHNLGIQBEzd3ct6YF01w3DAI5eY9ESi4Z2pYz1mvN0vUdNtlf8nBtQFBY5vW8duKcCccYLxJf/i63caAB/dnRh02Jxgzb3wro2PsjT+3fN5GaaHg7dtpb6KtWYxIsEVsHZ0GYZGEN0Q7Eu4PGhJwwSGD43fVBXOEmjMGzuFwfwPmL9idk2HX67WTES7QVUXOhY3kd6XKGJfWg4CkYrWxai8avIoNNbmmNODcu4ZBgXWOGPFqGqC2dpqcDc4BbRgRVeF9DJw6plXT8DEOQAGaUwpOQAw6xwisAhOpVWzApqbW91XDjtnMlI6m09gDCo6GY+eITXqOzBtN41JSW7VzvGMaXpu97/QjUFCEz+4kcMq+dbaPiIg4HWz6fZ6zMA7hHIelpQbc+zZlkTMitcA7UvXDAJKufJCGGePtSBvMEj0zNqQjSOOi8sbaPke20fvNMYf2QzAboAGzh6T9OGOz98nZutB+tDt2huM2GkxY9TNSms91Zef1bjX0IhtCBNYsMRgGxpQJnK45Cuh5qkHmIvPOdnSouMM1R1cXVBhXeuIEHs+6tsbtE3HHsA3yZtm+MSiz+D6331uX9DottM+3VhfONjm1iKRqHafd9M2RYMYAIjyJmwv9/GhgXO1lSDBUFOCx5zDG2ZbAstigTWe6vj3HXHWWu4456CJY3U9l7Sx3gZvQJoXZRYxUWgaA4NL7jCFJqEAChpn704aZtb0z17PkOtz2c88S2KaGz+veaJ37RIi+zVYRCbaIU8HCCcNNfqECCWgqqRoRCJo5vBLAK9rsoToMyzwV2LwFuzJEFIWEj3MyiGSrr8YpDJiuyTUieipvlPw4GKsneyNpQBJ19N4t1tudLw2gdD1mRwy2jTOl8gTOkbYmnAPMsLqbaDgRBvc7dI5Co+dVa9yOmWtA2AgKY7WKjUswxrxyI3Ts3H1W7pEGo2cuOqSNV1BQWqjxIasZctARf6FzFKo+ws+GsdXoXDQsVONFgi3ilLDJgn3hXBlxMiz6rgdzQVi0fh7atcHcew7t2mCCOzs0m6rvbUhIUrk5OHwFcx+VFbBklbUh4KJxjTpIByXbUSvZKEjTugWB/TDMfhK5ATdkP7gj5kIVdEN5FwSXushBN+6QHLS2j8avwTn3tTwBLLUhDFRrlLPm2I0rTMpp3AamaT/C8cOuO9SszaYbabtoO2KtpXac78y2uqDCQJ/E84kE27kCa/3/dpI39ypWITTPw3PoIgWXkWyNNXv7gCH5glnyH0AzAOSOZwK/IBQAGANjhJ8T3XGdbQkDTprbgBKnK/HX0Fq3+6E60qulylrWcCBM8XQKPeePCCds0BUp8MJ7ZziY1jCSSDbvK6J1j7oCZO4Y7h63fc9gbCHcNbAl7/njMD5DRPrmRvbcM0SdMSf7rEffZquIBFvEqWHGcVxEboQRZG88TOv3+jhd3S1Zx4QT1qEJiaqZaD6I9NEwnqwKnQyvXgvVUloHCoQCqCpAK2hd67kYF+QESV3XL3PXwDigBKWgtA0LDEWHdE0Qlkr7aLjWlP4uGKOOanbSpfI0pAkQVkUhGHlHJrg/DUfOXUOobnMTrdEwXDYci3nqO3+L4BR4aNxzd23M/s8pPQwLjLFV3jkniZmW9Dscf5ia2yZmnUPEOFyKLoScraMQERFx92GV73hrkRzWZQnV0I2UxVapgTBpg4I0dW3JVVP1Z4I01o54cAEmRD1fI4VRFc3FWnZea02yAaW2NlAblC2lVmg/fEqSvSRSAwDCYIaoas6/zeCSUYH9C8dtmiQbGAPTpHBrO1C1entWee5cmHn2A06R3laxobWmCFQaXukQfCZC29Eu78BUNZPiGirl3WdFAaiiublnsQ0127xjRjRxGvf6NNBFvLZJmYUdJkMFWKhucmmcQK0wgyVdTLPhDYAgtb6lUnbKL2eT/Ak4IFLyCQAwnvoxl9r4OZoxisdLDiQAwAFhg/3ObjTW7IxT+r0TGTBOquDgfvmfrfEIuy0QKNCMy+ghmyV8SYU6sAUoGC7AKniSTbeCPIBTodPZ5zUq6iLZ2im5XV3FF5Frnb6yF5Z0dxB3waitKNkitoJIsEWcKtqT5OKNO9RIXY6SJ4A6uluijoaH6Tzz1GDKbiRs/RZlgIQ3nQzByMHwBSArMnCN1JiqgqkKoCphtPKGz3BOJJtMwGQKlqReBeYdJF2BS9nQsBnrXChjUCpyjEplfPFnAICisXHGkAiGXApoy0JyBpSaJnaKarAZBYJzLJyD1OUcGcZqYtBI/0wZ44AiuThJsluPqEUOKl13GnXgjEFwBs0MFKPJSDN3181c566Z5qpqJy/8G1CTmC6SpSoi2aKCLSLi7sCq3+V5281RIrURpn4o7QIgs4vZdtOZhan6jqSyczBTJUxZwFQlKagsyca4IGdAJkCa1fM45zC6skGaCpxJIsuMrgM0QXDG2ZGwjEJoPxLOSWDNiSSsYIlG1qrBFgbAvCOmYMqCgkuB02c4B5OpDzC5e8hYQfOzVzI0bUhbed5lQ0L7oRkFx5xym2t033f32FuO3lz1nVeaG1KeA40AU/MzUo/TEW0AOZ8bIyrYzhXmkSLbUrNFcm05GkKjOzaKFtqBX9RjC+eKums1XYUL+ITqqRkFWHhsx/c4H8lCt9bj7tgKlgQDwDiD4BKM2WBJVYCpsia1GAdEASQ5AEBkkhrB2Tm6UMYrjTmjGpjKAKmos4OgNR1TFeRXMAqw+MnfEmYu7Z4xSjF15FqjJAynpj/cpnmGNoOuzSDhDFzwOmCiy9p+WUU30/R378cEdp0zFzgJ1NAt9aAXHvh7XZNdDmFXcXfclYP9nKNdE8//31CjIOU/P8sPtxDRt9kqIsEWcVvgFx6rpui1U2XacMQJ0KiLwjlvOBAGjlCjyb/SBpWN5Ls5SxlSeXGOGSdDsZroapBUdowuTVQXE5hiAlMWpD5oOxlJCqQ5mFZgaRC5sWk+TDiD6LqIBso1rTGtageJxlwb3YRzaMOgNZBJHpBr5Og5FUDDyQjVd5Yg1FXRHDdATh2vaPwmMGyqoBPoCkKkKMPbYmw0CU3njq6n5SA5505waGOQuCAWIyfJO3dWQu6LvTmCsKWgaH9GnBIvTFM6qYLNMLZmIdC4LL6XsIkzNTdaHbE+wu9367u+sPYL4FVs1PSGNVasPhUGzVID4aKWMXImmAsUcDOrgg7Gwozx5JqejilAUxHRBoCINqtgMzIB0wo8M/VC2zoo5FA1j69sYKlUGpPK2pAWwQYACWcQFUMmDXLJAdA9cPdB2yDHTJmBwP45YtC0bAgFpEpAJuBZr96dcYBXgBZkh6waAgidTcwQhGE9M2ezyYbMpvgy1rrvzn6EAaYOBV49RlbbDwCsQmd5h7CGXB24q0nY6gRez7q2xu0TcfZwUpItPtX10Zl6d8rnnLHlXfao4zvdIGVMXWh/JjWxVSPNv89svTRTp7UzcLsWr0k2Ny8B8I1uEg4YAWRCQgoJlCBfpZoAxQSmKsC4AEszGFXCGA3NOGQ6BGN2ntMG04p8FDcnV5rBGA7BgExwmmdVAVYVXihhJMC4hLb+UKVquypceQU3T1eFb6hDQScQQRden1UOc8uWMcaQckkTOOO1r+D8R6sGFyKFwmw9addRO/zchI0gQjWZCZ6jE29wVncVd0bak2uLfBFnd7q20doylsFbwedoU0TfZruIBFvEibEsQNvZDachR24ZDKcKC6XQc6LLQLBwaaWIOoWBSzWptEERRPJL62nUBBtDwmedDG5TUDSnBTdrORp6OiZyzf20CoT6BpD6gJUFWFWS4cpRk0YiJXLQyboDx2JSKUwrcpBKbVAq7ck316Un4QaJZsjst1lwAQZLDnIrCw/vTEhc6goopk3nyI2dCzKsMiWj5tQTzrEL0pM46mibto5d6NyVmmrROJKN27ELxpBohkQZZLK+r6GDpzQgREuB52qvhQoKpTpTqwwXvsYSAwAVmtANEKM8ERFnAysGbOZ1qW6k1bj32nYEgS2BaaihXRAhWDvbtEunROhQQfsD13OwKa36uZjATCdWBR0ooWVCSuiqhK5KsHxglVSMSgyoBEKkfgxuvJU2nlybKPqpTF0XjHMb5OCsEQBhNpovGIPidtHOgnHbAI0bu7N7PsAUIkkBraAB8Cx4bkJ6G8JZswqpsyPGdNsQf595PfaEc2jhniGlZlKzCUC2nJGG/WiXdwi7lXNBwSWgWd6BS1If2oLfbSfW1b/TgF9nbISoYLursCnJFt3Y7WHevTwV4m2e/wL476lTPIUlCUJSxlhyhzl/yAc36jpp/pAu4MK4r2/Zng3a9ouBIeEUnNeGYSjTum50MYEeHcBMJ4BWYEkK1t8Bt6o2KSRSkWFc0XWMSoVRqWywnGMvJ6ckFYJKmhlDyrhqSsfnspHW6tRrypDHwhgjksIpjgNFHbMlB6jEQECyeXtMK37ODITgEKLuYA2Amuy4e2bnchdgM6jVeIbV9nSmFmmYoml/OnLNq8pQdxUXcH14dHAd1savOtd3KO518Nk5EaJvs1VEgi1iY6ya+TBTV2CRUxQaEO+AtKI1CKLLQWeyMGXRb2cn69Iq18JIfhgRDxfrpTboJwYu9u0cjUobKohpi/w7I2eqksi1yQimmJASwRFVXNBCXaZErlUluEv9ccZQFGA6AxOyMW6K3NOYR6Wia2gpwbh1jhJNxsk5ToNUQHBAaXKQtEFjMnTKCUptLWv1XVXax6Bs/TgOJKVXT3hi0Dp2UBUQqO/8+GGVgoru/bRSgYqt/jx4go3T33LDAUnOnXfwLHHXcHydgdIVUBa1+q5FsNUpSrKO6p3YCLHam151+4iIJYgqti2jlcoxt0O10s0iy9aGGLTqsJnaOal0rYhuL2oTUddySwzVZeMMENy6Mx0km/F2xCqhqwKmDAI1XIAlCVgxAcv7pGSDtSFCAsoGaWBrqhkDA+PVz6NSeYLNBWkAOwdzSg8tddOuELlmYAyj6HgrsMSMnrEfpiq9DaEDcZiqJCW01tBag/cGRFJVwtsQJhwVCd/cwN3ntvrOkWyhzU40Ry4AHxgD3XfFAM2d4ps3lQOBetvd83Y3UciEbKEk59IwDlTW9rkGE8x9RoKyDrom2ooTEWxr2hq3T8Qdw7J5fFvpohHbRfuZneQZNT4Dc0i2MFDv5g03L9P8Bl9kv61iazSXcfNZIDrw9S0Zh+Ccyq+YOpunUMYH7gEglxx9DQAcqRDIZFbP91UJMzmmuR0AGx+DFxNw2wW6t/MgRlZFMa009qcVKmVsJo0GZxl6kvwTAauMU0Vt+4RNUmXcElM0d7q+be1rZpVVdwvtFWmh7+RrwQGAFXopQ+mvPvMpaMwTNgsK00QdwhRdbz/CFF0NHx1SmgIrla7JLtfwyD93dz2BCs5efqN+3h1B9G22ikiwRWyEVci1hirA7rOIZPPqNTf5hB2+WimAjdS/Vkc1Z7i0MY1aOdMgkl+qOiIeFp9MBEfCNbQWNIemwqb9GEgOpCJQsBkNoxQtzosJ9PiYDFExofdtCIQJShFlRQ7eG9B8zAURP5ZcM6pySmdf4Lm0DpF/qWaaKBASbDVZyDlDohgE595BChUIYRTMVEWtvrPOkVNPGC6o/k/gIAGoSTZVEEGoK3Ce1gKHILWVnDrlnSRn5B0EY5CCIRdt4pAib10KPJ9aZY2udrXvOpSDrn4B0wmQLiF3V0WM8kQsQXSizhhCci20I6ZOQYHhVMw+sCGNQ6AmfSrdXddMMCBRBolgSAUHwMCYscEOQIUq6FbKvlOC6fFxTfiEZJVMwNIcvCrAtSY7IqUN0qTkaLAU7pNHCmLj5+FxSfNwaENck4NEaChTJ7FyxqiGjnYOC6udxsDZqe3HpEFSuVqeTNg5WKY+AKJt11DGpSW5lL3n0o9dw6rXNNnpLptN46xtdim5vQYBSDTuu+F1imuYmgunvpsTYDIyIQWbLK2iwqm3q4b6DnDEIKyDVX9GxtUJFNNRwXZXItqHsw+/nl1h29Af6ux4aTSYCgr0+46Z3AcT3Fzrd7FMD8VjHIvfqp2sAr+IcTCrBmOMU/aJ1pBCQghS4Yaq2v1JhYnS0JqyR5QhAUEqGNK0D50NIKoJWHoMHB/4OR4AzOgQZjqhsjMyRyZ3PKE2LhUOpwrCBvb7icBOyqF0mLVkm/kkaIw/VIgLMOsPMEtGEbnmlHvGKeCs78QZg4HxczAAQAClAiQ3ZIfac2OYzWPqMgtd/q1TnvmxGAMYBSNqJSJlStUKQYACa8w0U0O7nl1Dw+38lLa/EqjXmNHbn0Oib7NVRIItYm0sI9e6/uzlswGZNTe9py2D1pWf1Pzfg/ozdUEBiuZzKWdSZVwHNVKuuYi4aUTzAYAzhURwTJQl4rQG8gQcGqkQKDXVKXAdOUlpUJB6bXIMdXSIalJAl1WDYBN5CplT+gzVVgCljSYpGcJE0QLejiNsbuBk145gK5RuNGdIBUciNHTgIAnreJTaIDUGqqOGjlffNZwMmyrqH5Hw9eOa0RYJVBJGVGCq8gYc9tqc+m5aKYxKS2xWqqGSoPtNBGElxYyCgkkGqalYaluBF6YnIVTgtev/cFETg1zM/8ytARNGClfcPiIi4pSw4DvN2ovTsAyB36b+jyORDKizmC/5iJBAaabuh41nqGmARm4dlp4UviamYAxKG0iRwnAq8u+JPK3qecwGaXRRohxNvB3hiYTMU3JytAYHYGQKJlJbx60C9+mMdC1loAAblQrHhUJhf3fIJEcquS3SbwBIJFyjkBqSC6Q+Gt8sM9BQb7sgk6sjFzRoABdgrv6oVhRc4oJqYlaJ70zHORpkFdX0oQDNtKU8d+AMEEojE7wZuOESghmUGrX9COHsRxhgapV3cAEmpDmQUWFvR7KZStC4A3ujnaPc+owcF5vbm3Vtjdsn4uwjkmznA+1yAW2055aQ/Peujmtm41RNmtbjXKQ2pRDUJMDUAX9piTXf6IB2rMm1qqhtGeMwVkrFjIZRBZiCJd0kTKqRiBxje66wZAAAJIbm2kIZjCuNhAv0+xfBdAWuCrDxMXB4C/r4ANWkgEhuQUzHYEKAJ330Lu35pgajUuHmqPYhdguJ+zTq+nJBUzgmJQW57L0NFWzgRLLRPoE/aBskwEgK8ouUgibW6XNEFwBAGW93FWc14RGqx7j2YwBqd9L5Iomtx8382GuSLyTCQvVyFQo3dF1HjzMQsRZ2UmUcJvyQeRUiHZt8zdY6ho7mr9d1ij0pom+zXUSCLWKrCL/j84g4A3JEfDSYc4pmhOo1HZBrQSpHo7uaTMFkXQeM0jcEmEh9qgwopuGdIGXI6Zi6ui5OgRAMNhUcmeSkuLLv33cxxzAlCbPhlG7IjPbpMXpM5FpxOEI1KaAmhY/i8yQBnxRQeYqkrCABcM6h0xwizcGSPkyg0HOO3MQVp1Yao5IIqnFBi/aQYMskRy8Vtu5ZbRSySiHh0hf+9E0anDTaEWuTY5jJyNeSC1MtTaN+XFGnJsnU1/4xmmqz+fvtCU1jSTabomQdpCJw7hxBWGqDUnOvokg4twoKMo4zNYCces0Rg9NAPRE6dzIJ0qw4uBDbUbDxGOWJWIxNHKhl6UURc7CIOO+q8Rm+3P6M1x2TJXx6DbNzqlPfFkq35rRmTTNXZqDmgWoVtOA050mZUh0aOzcYrTxBpcfH6L3mTVC7DwEfei9ufeYZcMHBkwSyRzbEngwsI1U0qqJRXwawc7BqkmuHkwrjQqGoFJQ2EJw6cfZS2SjKnFi7klriShlmHQbuFdBGqTo9NFTelQWM0jBag3FOCu6K7Ae3ymjNOURCtT2NJQddowOv4LZkWkgQ1oRm/c1KOEcpjQ3Q2DvOGHgmIEP74eZs99xdamhQ3sGUBXwXcF/awY4dsPVUJZhIvc0O77m264uxDSyNSoWDaaBcWRfr2hq3T8QdxarzeCTZzhcWPa8ZDt+g7jrZJodcugqXthGZrQ+tEdRFM4DkdYojEKi/ApIHqL/zqvLZHe59nfYAnUExU6urbApqJjm+4nIfF3OBpw9LlMpgVGpaw6ccO8P7KfgxOoK+9TyqSYHJ9QMYpZGNJmAyRbp7GXLvCoStg3ZcKNwaFd6+XMwTW1LBeP/Oq7OrCrBN33TgnxkDQDNoXt9XX2/ZppdSiQIJI53vJLyN9um2nEr7KKvABuc1uWbvkVeC2bIQrhxE+B0WjEFA1wo6Vfjn59YOyvo7lfV9jLF+LnVpI7/JKhmZKpvrFcewucys4NzNskmWoMUJfZh5iL7NVhEJtoi1sEi91kWu1W2nCaTeoqnDt1+eOVA4oViDUkxpAWwj/QAofUOmFB0HKILBKaohRA7GjI081GfRto6Zq2s2LhRGNqqvtIbgHIIz9FOBvV5CBUc5Qy6pE44C6kklINhMMUE5mqA8nqA8HluCrVaw8VRC5Zl/T9qovukNyGB4go3blMg6RXRSaRwXlXWOKjvWQMEmOUaFwE4uASQQXCGxY+5ZxyN0kBpjD9OSJsdQkym00k31XSLB0wlYNSDVRJA6Y1RGTpLRcE/TOUekXqtfR9MKo0L5OgXh+PupwCCV9nPBkHBl06wYDFo1gLQ10q5znUtPsqm5HkIQuWZr4TGZkOIjpohGRNybcLbFqaLDucBGb33xZEadxQRnYKom2Rzp44ifqWoGaHzKotIolUCZGOxQFRwwxiGYgbQBCsMYdRezTWbMZITyeIJ050Ho4f3g/T4m1w8AADJPIXoptJubne3r74ClAwp+2BIqGnV65dTakcNJhf1R4W2eQyZ5w6YA8ARbPxGWoGrad+ckOeWXsx/l8dirt43SRK4JDllWEHkJbVUDLsDEZEYlElqpltSIAV5l4WzIuGwq8FyAaSI5BqnwqgPB6H2q7cma7oi2AaaqINsxGdXkYDFplBkwLi1Xa2gubGmHBJA5HSMMiqGuo+TGfDStcFyckGCLKaL3NGLA5WyhK3U0zNBxPo9TVAlPfhXgxZhsD5fQRgMyBWMZpfNr+CC0YBR41k6VBo00ofWxK43iM3oAH1xxTVtc6QMjc4ANYJIck9Kl2pOqa5hKJILhUk8gFwx9yXBjolAWwLQymFQGKhe4MLwfyeQQkE9ClxWmtw6hJgXK0Zhs0uUrYJdfjIRfgNYG40Jhf1Si0gapFBjtKBQqSGfUVR3It4Ehdw8r7epMA4IZCM5rn9No8pWmY1sXk5oxMJXRdTPhFeZVIDYQrFalNYJtWnkyi26sqwFXz+PORyUlGhFjrJrYpgi12ss30dNU3650QTlbf43ZWqfQpW/UUNd/kwBL/P9dYI63/GNfKolRd1ijNcA1XJMdHajwNkb0bbaKSLBFbB0huabRlNxSvRuKnjDOauMTwn3Jfa65gQ7TUYI0DiZTsN6AIsx9ah0NLiFFilQwVJqh4HXXSkqZocX6rVGJo0mJo0lF8uhK+4h+PxVEBpkMnDE8fVjick/Qwj0LSKqSFulqRI5RcXiM4mAEXVbQdmHNBAdPJPSgClRtkpyjfAAx3AucPTq2qzdD6jVSHhxNSu8cNRR3lqCqiasUCecYSYV+IpBJ1pQPWyPj0mNC56g6nkC101sTCZGnSELySiakvpN5Hcm3Bs7Apreqevz743KGIHT3OpUcRdWtoOglnGroBEbWOcjt9FaXnuTg1HfIcmp4IKyizUSjEBFxp7BNxUbD+ZxXt2RmAB01Pv0xmO1uSUoAk2ikIkclGAptPAmkbSpImLofNsxJBEMmOMalxjSluXwvcwEE6qqWcFE7a4Edmd46Qvpffxvy/hfgi/+/T2B8/QgAkPQkZJ9SFbngkPkBeG8AMxmB92cVbFrXdeKOppW3dU7F5ubb1Cqgi5C0EkSuTas63VW352Bb95JIqmMUB8dQkyIokaDAhADjHKqXQvYrsiFWGWayHljer9NlwkdkZsm1/XGJUaEwLipv65z96KWS7EretB+ZZL6jp680HY7fBWYmx9DjY+iyggoIMZFKTwxyzuvgUtaj4JIxPrikg9RQF8A7KhT2T6Jgi7jrEVVs5xPt59auLwyXtsc5pCvCrwrwcmx3qKC5RNrPIWx6vLalYUZaI+EaueKotIExApwZ9NM+jCoBXliirarXw5MR9GQEUxY07/Z3gN0hTDrAUQUcThUmFWXsZJIjlxypYJSaGiiu960/lEmOSmeQQ4m93h54PoBRGsXBCNNbx5D5CDLPkD70PMT0CLJ/AQAwskrpwh5jVAZp/a7u5XRCa3XOwe187ObPQlOKqOCAtOmWGeC7iKrJMc3dMoEQgkg6VQEy83VSJ5WyGaDcd5mmZ0L3zBOUVkloGINhHKXWXoHGQQwXZwySAaycgFUTsJI6oBojfBdUZSjFt1AGU0XKcc6Yzeu1TY4YqEGDKsBKqmXHhGwSfEF6qwEgWctmuReXM2scg+XlmyJuLyLBFnEqCMk1x6w7A0RR9toVMK5OAGx9Ey4BXjVTOjCm9FAXbXYdvzgHz3rUOrqYgO9W0Ibq0+S9PSLYFBVs1i5lRmkcTSrcOJpaks2mzVhSKRUcw1x6hyPhHJ+7OUKpctzfT6CMpcGCRXo1KVAdj1EcjFAdT1Ack4JNKwMuGGRP+sg+EwIiT8F7FDVHMbX1FGhSdkVPS5siOi4UjiYlbo2IYBsHKjDnYIwbKjzr4EmOUUkkmwqjG65+jo3ek2pijPKQSEJVlNBlTQ4SwZZBlxUZOs5r9V1eWINVOS20T5NxKa5H08qrJ2q1YFPBtpOH75GCop8IL2kP67ZRgWpqbtBIT3LNDqxjR/XX6i581AlVn3w1G6M8EREed3JN11BVAfNJNluf02gNprhXDIdpNswRVKUtNyDG0GkPMq3QT4e+Tk5iC6pUNoBwMKn8vOaQSu5T90el9MWkBU+RcFrAJ4LSS1yqpZ4c+yDN0//7n2Jy8w9QHJdQhQIXHOUxR7ZL85kLevDBMfh03CylAN7onj2pNEaFwtGk8vZuUiroSoNxBsYZxoVozMm5FL7z6NDWxDEN57Gl3j4eowoU3LqoGimi1SRFYt/LhICWKZFrxRQstU0O7KG1VQOUtrHBVNU25GhSNmxIrYBWKPLEjr9pPxrqO6Nr+xHYDj2me68mBVQZEGyJhCgqpAA1Z5BUA4/benIm+IyFwSWnXjssKtw4DhpVrIuoYIuIOLNw6iZlWvMjaD4ojMFEGeQiQ57tAJNDr5Zl+RgcgBQJdtJdX9zfzdsusJCXHFVOa2qdcQwHl8G5BIojYHoMNj6E2r8OfXAd+vgQAMB3LkDmA+hsgIno4caowo0RNTXoJxz9hGMvEximlJkzUQaZZMAU2J9WuD4qwBnDqFTI5AA7/YtUigBANZpgdG0MJiaQgxx7+9chqyk4IyJrXFTYH5UolEYqOSaVgjLGK+zM5JiaJFQFOOfALqVbujTZcUl+A2UNMZTSwAiq1aanY5jjQ/KbZGJrRPe8DXeNfUal9t2mM1k3GKD0zjrNtO66yn2DgtKq6AQHBKj+mlevlVNSsBkNiBQmyf3YS6teGxUKpda2zI2oz220PcaEVGywdkmkcPXEXWdyALUIxX2gnI23+zFGTf7aEtcT5edE32ariARbxKnDAI0OOZJTPjqpAbhNleGAqdl8KtRZgckp+ISDZyWUnZT10S2Uh0dQE5qYeSKR7g7Ady9DjI8hLlckrGYMWbqLCTe+6UGpDY4LhetHU1w/KnDjqMBoXKKYVjB2YptIjvFUekXbTi6xl0tMKt0kqqq6SDI5FhNUxxNMDyaoJhVUoaGVBhcc1aRCMghUYXmKZJCD2/bXzNd9oUNrO9GXynjFwdGkajhHDlxy5EnoIDnHTmFSCVv/oI5uMGN8cwA9HZP67nCE4oCcDKdAAADGuY3i1ymvuRAwWQ96fAw52KXokdaAwEwHVKe+2x8VTYIwUHs49UTDYUqE7xynNGu2r7b1fxy56WrH6cmk4RxxwSHyjK4jSQPl4wmNQjRCESvibq7DdtaCpd52tEk2TkEbxSSMAKQoqHAzqwBm6qh2NW3UoWQygRzsQmc7YP0SO72L1gkgRXSpta03U/rgQRj4yCQFavb6Ke4fZtDaWGVbikIZ9KyiKuzEWRyMSCFwMMH0oMD0YApVKDDBIFIBrQyYYJB5CjnoIbP1M1FMvSLPlQHQxpVDINXXobUfo3GJqlRQTrEmOXQQ9HCK6H5CAZpSSQpyAL4rc6je1uNjCiodkHrb1yANCDaRpw2bkqU5zGRAjmajRIINzgVOZniPnQ1p24txKprjb9mPRmTfjd2O39lvRxCqoibEZJ5RKg6AlFtiMM1JLZ0OgnEL3wHPNWQYlRq3RiWuH042/1BHgu3c4rzM4xFroqUcYrbhGXPp/gAybsDKCQ5ZjuvjCtoAl/IM9/Uvgh9egz4+AJuOIbgAlxnSJMcwtQECxpDwWrnrak/2EwrW7GQcu+kuBiKBKMZQ+9dRPfMZjJ5+FuXxBMkgR/9FAHvky6H7F3FtrPD0/hQ3JiUSzrCTUj3pC7KCOLwOGI1+toMqGUIwhoNJhSdvjFFUCqNLfVzqJbgyGGDQ3wEAlOMK45ukwst2D1DcOkBeTcCYrT9ZKIzGJZTSOEoExoWCNjRXs3IKfXwAdXjT+jsCwtbfVjbFcn9SWWKPgzOGfsJdxwe7zj+m+ydTMC4oA8gG0pWpiUlXF9X5PmGap0ujhSOphPR1o0nRR76MtJJ1Zmu/sWpCtVMBuJqtmpHKsFD1uUklCPQtPUZlJqZ+f6aKRr01cI7KgBrYObGfSytlvK4dazvRMoBqgQclCig9+YSf7ejbbBWRYIvYGpwzyZmts2YXyo5c04Yiy4I1ZbCu2YHLY/cd2WSGLBt6OSxLDwCtoUYjjJ+7hemtQ+iyAuMccpCjd/kmelduwUxGkA9Rfn2a5EiFpE4yVhV2aCP5zx9McXw0xXRceYeDcwYuOTLrfPRSgb1RicuD1DcXCBfrpiqhi9KnxhTHBYrjEuVx0SDYRMqh7YXxRCI5HqM8nkDYBb4v4GnDFWoF58hFzLjgqBLhFXhUtFr4FJ9CaWjbDpwG3VYf1M6Ra9JglPHqO5EKn5oEACJPwfJjsP4OydGtCsRNtc5QTQP1HZFsJSalQlWoRp2KolQoKunHntp6OkPbhILqC8CP3ae3Bh1E9WQy072Vu5puXPhxmqqkFLATwMnJ19k+IuJuwlkh1+alRHDr9JgglaLQBilngEihe3tUT9JQ4WKUGigL6MNb0Ee3YMbHAEBNBHYvQ9z/MKAr7PTvx7jUSATVazm0SujnDqY4mlQoSgWjDUWeJccwk7g0LG2wBugnAnt5QmSQDAouVyUqW9emPBxhejD1L1VoS7DRnCNSAZmPkO72oSZTCNv9kmnlF9x1wWgqfeACNONphem4RFVqKDdPMgaZkkOyb23HcFJhN098506leXPxXlW+blw1KXyAqTgcoTwmUlCV1h4lHLLXrEmaDHKw/Bjc2b7AadVwHahJveaUa0Sy1TbEQbbSW3up8PZjUmkMUzFTYiC0f2o0QjUiG1hNiob6TpcVdFna+05lHUxB6U3huF1hbEovck0lKLB047juqLcu1rU1bp+I7aOr9tY2j9113PgkzyjcfGV/CsbBucS4AnqSQT7/GVSXXoSBILXYRz5zAy+/f4hLD+xB2MwKU4FqStuSBcx2Du3Z1M1ByjGpJPYnJW6MS1wf0Ty0m0s8spvjhbs9DEUCfXQLB//jC7j1d09BlRUGVy6h9/ADQNbHkZZ46mCMz9wcYVQqPDjIkApSr8kbTwE3vwhoBXHxIfTv2/WqtadvjLBvgxkPDjK89GKOQdazauQK0wPyEaYHUxSHFODhjFTdhxOyMUppHGeyrpfJGFg5gj64AXXrBozWkJzDTMjWKgOMS439aYWjokLCORLBsZNyoJfQvD2dQB8fQB/eAktSsCQBn4woRdammDr1MEBNh5wijAGeJDOTEfmeVmFG6aGUjuoECQCV1uEAEVs2vVOPjymLhwsfyHPKM9fYhjK1qMEOY/baiykp4MoRiRx43XDNcGnPD1+/LeG267jrf6DrzqPGpYlq7ftl+GyxE0xQ0bfZLiLBFrEWXMvoeQgXChz0BWSM2i1nghbzB3Zx3JNyZgExrii10OXgp5xhmO0hByCqCdj1q9BKY3rrEEfPXMf0YAqtDGQu0b/vBoa3DrE7oYhxkqQwaR9pfsnXzpkqTYveowKjUYHRUYFiXKKY1o6RTLj//34mcTQpa0ejUXSBCuirsoLyJFuF8rhAcVRClQrGKg5EITxhlfQmPh3FdTHjQQtvAL5TWts5KqYVyqmCUtor7oTg1JjAGOwzIqiGkxKjfuKLmlJEBkENM+WLPDsHqTgcBeo7WytOMPBEIA2cI3mYIhkERaGDYuHGuAYH5NwcTipPro2nFcopkZlh1z2ZCJhAjTDMbXc+5Zy7VmTGOqXGKvCcc+pITgdhlQeMc2pwUJ0gVSdEjPJERNxxtO2QCd4n9Qh1AeXjfZgkQyqktwMQKUw6IEVWMa5rgFUl9PEBqhvXUB5PwARHduEqzOgQ8sVAJjP0kwFyqz4bFwrXjwpc359gMiq8HQEAmQhMehJjO5f2U4G9LLEpM8LPC6YqoKe2/uW4QDkmGzI9mPoUUQCQuQQXHEVeIB1MPamV2DqUrvu2M6raqummNkV0XCiU1n4U06pulsAZlCJHY8QZ9lNqmDOpUq/G0qCgj3FlG5z9KCa+4LWzH+UxXYOxxpIJhrRMrS3kkHmK8ngCPiDVnicGjYY23NsQVz9uXKhGgMmNXWtqQFSVCtrWt6N6bCUmfUcOmpnPCdMVlRcIyjt4BbrvAK597VRX1oEnEiI/hhnsWEKzatRO1Xbck4AYvDUqcfMopoieV3S5j6dVMy3WYjtfoIYClSdZTDbEQaGwu/809PB+QKRgAB7sS+xlEs8dF2Ca1wX6s5xSzm1zHRPU30oFQyYYckE+12Gh8MzBFFcPJsgkx8GkQj/ZxVCm0Ie3sP+ZZ3Dt09cAUDMcxgV0bw/Xxwp/f/0Yf/elQwjOcamXIJMM6WQf5kufRfnk3wEAki9TkHsPQ3CJo2mFq7cmOD6aoqg0XvLAEEflDh5I+n4+LI8p0D89mFq1soI2VH/yaEI2pioVqj6pujljSDlgRgdQ+9cxev4WjNIYJpIIK6tgOywUbo5L3JqUSAWHFAz39RNUhsgKU0xgRocoDkdgfEJKaOeDgEgulzkDUMOh0voBrvsqbNdoV16IbGZdR21q69T1pGjWblOl7zbt9wUAS85Nq1rB5s6tDdVySziAagJWUkkeozVYmgPSChe4RFVq3yDBrVNSw5qdT10XWkPN/IzO/OeRfMaTfqijb7NNxLsTsTb4EtKagfLHhVWr9STHhYyjJznGlcbnb01xOO3OFJ8ojaNC42CqcDDVuDVVuDVRKPI9Uh3sXAAAlMcTHH3pGDc/ewvP//dreP6/X8Nzf/0lXP/bp3Hz759C+cXPobr2LNj0CClnXr48qaj+2sG4xOS4xOS4wOiwwPToGNOjQxSjCYpx5R2RSWm7jNrFvg5qLRitYMrSRrkrWpwXCqrQqCYViqMSxXHpf1Zj+95R4RfzxqoPHElFXVat+sCmWTolmBtTOa1QjEtM3WtSYjqmqNF4TFF+V1fOEYOd9XOmde04cjCm5NjtTxsKCnoVKA4pfYnIwSml11Rlo5uRNkFxba0xLmgcx5Zco3FWKOzL/z6tcDytbBosRc5c8XCDoAababf5LqEmU0+uqcnUdnGdohoXvqaOsduiVUx7IzC2/msD/MIv/AIeffRR5HmOV77ylfjDP/zDhdv/wR/8AV75ylciz3O85CUvwXve856ZbT74wQ/i5S9/ObIsw8tf/nL87u/+7onPG3Fv4Sw4gQ0VMeoCv2HzF/c+K0cU6e3oWm1ESoXrGaM6jVrBFBNMbx3h+Op1HD75Jex/5hkUT30G6tkvQBzfwCChFEoAGBcVbhwXOD6Y4uDGGAfXRrj13BH2r42wf31E790a4/mDKa4fTXFYUAFpZdNTqDMYKdiUtSEuQFOOK0zGFY5LjeNSoxiRLSmP6W9eses6X7q6cvbanGq8sA18SqXJ6bGvYkqvcmJtiiXeXCOEw0mFSaXqAE2gAjOupIELbowdSVV4mzG5NcHk1oR+3yey0NX5rEa2uUBVULp/qGDz6juDcUkK7qNJhSNLrpVT5W1I/ZMCOM7ujW0dHK1n7UeoGjSlC8xMrS0kO1gcFyiPp426cs6+mCkFxcLAEmBTW3W4ziBbPBpvrmDbyNZsYG+irVkPq9zhTaw+a70iziBCUkFImLRf/8oYzPOfhxFJY5dhJnFfPwGbHILJFHzvMvjwApCkNGdXBZXOYZSaCACJYOglDIOEIxdUy+zpGyP8H0/v4zPXj3EwVTBcQh8f4Pi5Ixw8fYjRNerizAe7UMP78eT+BH/3pUN89rlj3Dii1MZBwsEPv4Tis/8H9v/2f+DwM5+H+tJTYNNDMACHkwrHR1Mc3Zrg5o0Rnr4xwnGhYZIMXFAmTlUqjJVBNSZxAbS2gRFqqDOd0FxdlUSwJYKBVRPow1uYPH8Do2evY/zcTUyuH3iCTBlSn10fFXj21hjP7k9wc1zalEs7f1clSlvrujgcUT23KQWXtDFWRaZsczUbHNK2I6crAzE5hhnbV1kE9duCxkUu88eQP0Z1O61q2wb1jVIBOWe8rza1dUNLn1Hkzl0Ahat7bWtGm5rcKxXVoHPNEipbQ86VumBa1V1iLbnLbG07V+fcdNQCXO+zHX2bbSISbBEbgbPlL8mA3EzRO3oWrBih0gYHU42n9sf44uGECoPa4xlQCs+4NDiaUueta8cFro9K7E8VDqaKauHkA3DBoSYFxtfHuPbcCE/vT/H5Lx3j+c/ewvVPX8et/3EVR89cg771PHhxDAFLXOk6Gl6MS1IcjCuUowMUo32UkyP7c4zCRWBsvZfC11+bnbyM0pRWogxUoer6a6X2v7dfuqygJlPoorTpJrWT4cbado5UFThIhbZkW+0gOfJtbEnBUaG8CkC18/NtumRNTBUBGVigPC49sVYeEylYHJUNZ8SlabKgE59r0KANvKNzNKnoPk4dcUmEWvh7Vdqx2/s9temhpdazikmvYCMVgioqUhFOplAB2VlNpvS3SUHXq+i1qVHwCLvcrvpaEx/4wAfwjne8Az/6oz+KT37yk3jd616Hb/qmb8KTTz7Zuf3nPvc5fPM3fzNe97rX4ZOf/CR+5Ed+BD/wAz+AD37wg36bj3/843jLW96Cxx57DH/5l3+Jxx57DN/+7d+OP/mTP9n4vBERtxuLFNSCM2R6AgmaN45LDSNS6thmWuSg7b7m5i7qeCnsIn6C6c0jjK7ewNEzz+PomWuonn8GfLKPjBtITucqKk3kztEUo/0Rjm9cxfHzT+H4+adxfOMaRgdTjI8KHB4Xvlt1qWwah1P9loWfi9WESguokuzGWGmMlUZh69OoUkEVCrpUvuMlBXoKnx4awnXQLCpNddYqY22IRlUolNYRKqztqEqFolQBSWWscrt10+38G5ZHqANIpbUZ9f+nB1NrQ9y2hb9u37DI3Q8Y35DIKe/GhbI2QjVsiPtZldrbj5ELLC2wH64Gmy4rCsSMC9+ciMjLigjBcVU/l0lB97sqLUFna4+6WwJSsFVWdXc0qbA/LlGMt6BgO0V7E23NZogE2N0PM+cFLjHlOb44lRhpAaYVkmufxf05A9+7D2x66I/hahJ/2YUcrJzA5EOw4QUg6wGcivezaoqUaWSCIeUMgtEr5QypoIYtALA/KvH84RTPHUxwVFQAozT24qhAOSEfRPZziMtXsF9xfP7WGJ/64iGevznGqFDIBMcw4TDPfQEHf/8kbnyKUkuLZ58Cnx5DcKCoKIBxfDDF8QGVP9ifVjBJHyLPwAWz3S4NjDZUo5LXJFNVkN+kK+MzU3IpwIox9MF1jJ67idHztzC5ceBJMmbIxhxNK1w/KvDcwRTPH0xwa1ziaKoozZOR+k9NChtEp4AHrEDBwGbPKKqNGtotBvjmBmZKddxcBo6rxeZILpd9E3YG951aXefssvDBegPbXCHI3HGBPs4YkeVVAVZNiVgbH9ddVAHA1XCzr0lVK9lUaNJd/bhiakm2OqjmPmeNGuGbIPo2W0Uk2CK2Dt8xpZpA3PoidLZDqZqC4WIucGNc4pmDKQ6mwQRlgMOpndhseshhUVHtgXGJUalhsiHJagGossJkf4prhcIz4xJPjkp8flTi5hePcPTsIUZXr1MhTVtEmdkabFOrYCumFRFTkzHKyTHUdAI1nUBXJXRVQFeUgmmsgzLMJC71ErBwWaVtqqWdBbUy5Pgo6yCVFP0vLfGmCu1/6tKSQmXViOKzFvnjimbrimrmOHKNflaoiso7SKHj5IiqUteEl38+1sGAJidNFxWqCSnsHAFYjitUY4VyXHnCjf5GzoiaFLUirOXYOafMRXQKTwyG47evov6bslEj3yXO1MTgrGOs/P0nspKUam5suqi8spAI0C2p14Ba0r/Ga138zM/8DL7ru74Lb33rW/Gyl70MTzzxBB555BH84i/+Yuf273nPe/CiF70ITzzxBF72spfhrW99K77zO78TP/3TP+23eeKJJ/CN3/iNePzxx/GVX/mVePzxx/H1X//1eOKJJzY+b8S9hbOgXluEpDii7xvjSBwJ1r/sCxP7Ri+2ExgrJ7RIFZJUB5y6eqqiRHE4wvjmBJObI4yfv4nq1g2ow5tgxcgXP55WROxMjkuMb37Jvq5ifPMqpvvXMD3ax9Qqr5wSukFW+bmMyH8qN6AweGCAh195xdYORU2wFcqSbBpG0QuO8HHHs9C+Biot+JUiG6KtPaltXQntHCM7R5P9qO2XcQGacC7TmuyXUjaoUb+K49LWI6XADZVOoJ9qUkBb5befx2dsiPYBplGhfN01Ut45QlB5u+fIwUnZDIjNBJYaJRIoOKPL0ttifw3jKrCHpQ/YaGevq8LbPmadO5fi5WzfyDqaZdGt1l8Fm9iade1NtDVNrKMgm7dNJN/uXmgDFBr4wn6BnuToSQbd24POBki/+NdQFx4BH++DFSMoAzxzVOKFuznu7wmYtA/dv0g+UdKnGqGATUEskEuOXkLH7EmGjBv0bE3iQSqQ2s6agjNwxuqgvOBIcon8YobhC+4Hv/8RXB9X+LsvHeL6jREmo8L7Mbkao/zi53DjU1/AtU9dRzUpIIZD8tds3bByWqEYjTCxtar3JyVM2gNLc/BE+E7aAKgECxd0X6yPolStGhacQXJSkuvDW5jeOsLo2hijayNMbx4S2WRIAReW8LluXyPrR7l6X0Zpa2+mPsDknouzV84GuDFwxoicKguvemtkD8F1AtWY2MwhV6qoYa4dsaZ147zGmCCgU/tbgjNS76mSFHy2g6ypCr+/4YLqj6s6zXRcKasaDzJ3dEWBoWJC3bddcyA3NuP8xc0/29G32S4iwRaxVTCgXsRWBUUMsqH/217GsZNKXB8VmNqUS2WVBmM7IXLGoB0ZVlDByokyUOCAlGCce4XYUaWxX2rslwo3CoVrhcL4JnVj06OR7wgHoKEIc0Weq2IMXRbW0Sjo/27i1LWB+KZ/cB/+L1cGGCTdXxmjtSfaXO0ZoG67rbWhOmn2pypU7SBZoog6wM0eW2m7v40IKaWhKlWP2TpIypJwSml7ncqmtRp7/U332GhHBlaW+KvVd9Wk8iqKmhi0jp2m5g0+ku/q56BuEe2IQae+00o3xuiiXMoq83RF2ygb/fFj12aWXAO8Gk0XpSXRFLRVsrlr0oWtjVdWvhPcVnDKUZ6iKPCJT3wCb3jDGxrvv+ENb8DHPvaxzn0+/vGPz2z/xje+EX/2Z3+G0hbpnreNO+Ym542IOCsQDGDlmNreg+xNLhiOClqcyuKoOZe4FE0uYezLgZRNpAyYHtT1zlzNsLChS1VolJMxqskRyuN9lCN6Fcf7KCfHqEo7t80UjavTFXVR0pxqa3Z+9c//v/GK9/1/8fKv/7K512uUbs5rztlaotA11p4YXdsQVSkbpA/nYO1VAJSCG4zfOQduri0r6FYgaVlwSTvbF47NNBs0dAWYdGX8OIkodDaR3ncdRtv2DkCttg6DM3b8boxErJWBzSPb4oI2jtA0rYANlUeoHbxadVfNjGNlnLKCLdqa7SGmdt5d6AomORX0VGlcH5fYy7h/3np4P8qn/wdYOYLaexgm7WNcaeSC44W7KQkJBpehhvdDDy7DpKRg84XitYZkVNw+Ewx8egQ+OUSfK+xkHFeGGV583wAvuNTDA7s5qdqMBk8k8os5dh4a4uJLLmL3H7wI1d7D+PytCT773DHGRwV0ZdBLBS7lCfjxdYw+/yRufvYWDp4+xFf8v/6fEN/8duhshxrQaVI5U0bPBONxiaNCwcgcLB8gHSYQqUBPcPCEQ6QJWJJ6tbQO7Byz3bSl7SCqjg4xvXXkywiUtlQAbEClVAajQuFoXGJ/XOJoUtqmAQCErJsSWN8ptH+uyL8bh3sBNmlFV161ZooJTGmVaOH+vryN8z067Ehoszh1AXU1SgF4gkswSvkVjAGqIHLPkWvOd7LzdWkDaC49dVpp3/DPALWCzpKCdM/CGqCUWqwx6+ethejbbBWxyUHEbUcmOQaptDJooHLMu48C0MSigwmzrWLiQQTFRaodmeVIIOUi5AHceUjabGYWyYwLcDuJc87AfLTI/t39bLfq5hzMFr5mYv0llgk6wDHGwJcUuvPRD60oeqTDa2pOsDRJt1UTNs0yUN854ozIP+M7oBpFx9QlkYJa1fsFN8D/16W3hs6R9goKQx1QrZEymsFoeAfJBEZxwcXbEyk/ZjcmZ3QZ5zBCN4lP96w3iLpsAwcHB43fsyxDlmUz2127dg1KKTz44ION9x988EFcvXq189hXr17t3L6qKly7dg0PPfTQ3G3cMTc5b0TEmcKC7zbTqi4kzqX/P9OqsZ9L+dfK0NynTBAMqZXGIZFltIIKAh7UvNSmpAcQNv2HMXiCrZ7L6zmVCWuDrC0RdqHOxepz1zKiLQQVqTbejiydg4GZa3OBJdeAwDkcQqMRXHLNFeieqoXKYmW7oHYFmIxWMFxQYyLNPXHo9iP+rmX3gus1ug5ymcDukU0x4LCkYCq8HdF2zO11gx+vb06kvKNalVsM7qyBVexNtDVNbFo3LeLuwyZNJ/TxIZLD52BkjtIQWdZLgKOCaoJpQ+Vz9rIeBjnAzU0wW8fY2Dpk2tDJmSrBpkcQ5QgP7j6EL788wLTSuDbq41IvwU7KwYwGH+xi5wWXkA5SXPjyR5B9+VfhOh/iszeew3M3RiinFeQww+Vhios9CX7reRx8/lnc/OwtPD2yZA3ofCKlT7PWhrJ6ZIpyWtm6Zgb5YAfJIEe2S0GsdJBC5Cl1n7Rrfm7VdYxTo59eKpDaGmzlaOLLBvCEU2mdloqssMETAJTur4hsMlxS904RvFw3T2u/lWkSTE5BJmwNNiprY8sScD6TgeOyfZyazZeVYM63E77RD+PCK7hCTktwesaJYEg4I3JRFb77tClLsEzYDxmHkSmqgjqIOoItM9x/9nxGmFbQrgYcAK5V3WQD3HdRvRPzUfRtuhEVbBHbh2O2ZQqd9MCmRwBoojooNDLJ8cAwRSYZDMh5GKYCKWc2vUL51s6uVbO0DRMA2MmVgSccQ8mxlwgMJf1/IDiSnqSOX4kEXMTDBPn0oMgK/RT+xWUKniTgMoVMBLjkEJJDcIaPfvYG/vq5MUblbBqnI9Zcx00uOBgnkiy1L85Z/b4d/zJw1yjC7s8C0s1FclhABobXNfNIlky7fANSMBiM/68Jic7AUWuXB3IOju5QGoT7zdT+cdBNJ8foVjRL104cbb49J4daWa/3AoBHHnkEe3t7/vVTP/VTC8/T/pwZY2beW7Z9+/1VjrnueSMizgIMAJP0KFpsf58og37CAVVRkWb7MTZcohI5TNqH4cKnYKA1jzDBIFIOkZMjwRJyLLhTGkgOLhm4TCFkau1IQj8TsidCcHDBkUpu7Zkl2Wx6oQsUuPMZZfBn3/sj+O//j3+Bv/vo570NSTmj8SQCIuXgKam5nY0L4a6TMwbByYY5UpB5eyKa9o/N2pm54MKTgOtgHYKwa8rWKxB/DoJjNlBl9AwxCDTthCNWdUCuatUM1tjBNDp/A2FarqmJwRMoCjaxNZvYm2hrTh+m9Yo4+2h/El2jnExwXO4lOCi0f5bi8EtgSQLDGI6zC9AGyATDrpkgEwyfuznBF25N8MXDAjfGFaqEmiOwagJWUNOUShscFQqjysCIBEyV4MfXIW98AY/uCnz1C3bx6hfu4eUPDLFjSRpx8X5cfNmL8eD/9WW4+NVfDf3CV+DpgwKfevYA48MCWhtkPYmHLvRwIReonv0cDp++gWfHFZ4ZV/jI//K/4ub/53+FufoZMOtzOKiqgFLUlbPQBnywi+zCENluhmw3RTpMIPMUsOpvYf0bmXLIVEDa1NaEU5okqYCVD2Y4EQQzZiZ7xwV6vD/AJViaQ+QpZJ5C5hlESqSbYbyRDurHYm2e4IzIKFfz0ynIWuUJfLaPU49ZwpOaIFl/kgtAJtQUifFG+R3OmPdZMykgGKiDqGqVFgBo3FwAXNrGOMp3vi5tDVJvOqzdohRVW0NU1SUKaOy12GRTRN9mu4gKtoitwgZfLDOfQ+09BD45hJEpJkhwa6Lwor0c00qjJ20kADQJXepJjCoq8ggA0kYA9jKJXFIUAAXV0pF5it7FHPcdkjOljMFAcNy3k2LwwADZhSF4vw+W5jBCIlyrCzvpykRApj1SgQkBxjlk2oNIc8iUI80E0kSgnwpMK439aYlLfdk0BFyACXJ2RCogUgGeEMlntAG3sx1JqTlEKqyTRCQgIy+AoiH2XpCsuCbXBGfgkoNxclCE4DBSAEhhtLIOHYOQnAyK4MG+wAx3xuicCFR39DY5glyQGoCDiEAmyOlyKj1uo0fgTWdpHT8iJAY5I+fP8XRu7O5ZbQvunCdVsPl6RGtsDwBPPfUUdnd3/ftdER4AuO+++yCEmImsPPfcczMRGIcrV650bi+lxOXLlxdu4465yXkjto+7073cHjhrNjoIlQZl0ofU1J2rNBxKGwyqAzCtYETq760BqOOYBnbSPlhxTIvYqoBWGswGagAgHSbILg6R7g7A8gG0SICK5q5eKpBmEmk/RzLY8yUHGBdI+3tI+3tIctpmJ5fIJUcmOc3Jqi5QTJF4mltFKnD83DHGNyfUhdsScolgkLmE7EkK5CQS3DoY4LPpGq5QtuCMavdYMpB+cnCd+DlRSGHjYkSyefuxZP6tVQS1rRCpgCw1UJIj4YJLPOHe9jGvRBA1QdihBph59j6QxP1Pxpl/1WMP7BpbXc1H9o/59QJdU32uNkxw3BOWl+7EurbG7QOsZm+irSGcdmpnJNTOL0KbAdRN3F68l+LGpKImBOUh+Ogm+Je/EtXFF8EYwPYlgEn7uG90E6UWeHJ/4hsWXMgFUgDm+BaYTMF6e5hwg/0pqd3S4RACz0HffA6mfBrp+BZecOGFuP/yZRTKoCcZMAHkgy9CP83Bsh7wgq/AVbaLv/7SDXz2uWNUpUKaSezu5njhbo49qVF96SkcPnuEG4XCUaXx2ZsTvOCTn8HlryvAwZBKQYEWG0DRFTVtmVQGfHgB2YUd9O/rgwuGbDdDMsh9eYVUErGWZBJCcOz2EvQTAckZkUJBIAkIAi5Gg9mgVSq5D/QIznwnTyMS8KxH5NqgBwAQeQaW9RrlHQSjY6TW1kpnxzqaALXRthMGdj61JSSYTMDSnAJtUpKfawwRk4z8VW2bUuSCIxWcOogqao7gP1NcgMkURkhUvv6a9sRa0mYbXRkhrWCqgjJ0qmImwGPa2UprIvo220VUsEVsHQawKZtAwVKM80uYIkFh1QRXBilevJcj4bXxYgB6kmE349jLJYapwE4qcKmX4GJPYifl4JND6v6iNJJBjv59fdz3QB+P7mb4n/ZyvOjBAS4+egG7L9zF4KHLEHuXgawPiNRPBKnk6KUCSSYgU44k7yHJh0j7u+QM9XeR9STSXoIkkxjmEr1UIJMuIhFowTg5CNySZTwhki3pSchcIh0k5BDl0r/nXkTIJbXKLpAbA/CRl8waikRwSwjSuLnkkKmETFMIySETblV35ECRgRH+OLzDyWCc1HYiaZKDIuUQif2/VUu4v4uUnClykmwkh/GGo+HgnDRyfih6IASNT0hhnTpm+T7WMKqkurBpunM+ZyxQbjgnL/wdgJeSO0O+ieqiDadSWOcFALu7u43XPCOUpile+cpX4iMf+Ujj/Y985CN47Wtf27nPa17zmpntf//3fx+vetWrkCTJwm3cMTc5b0TEnUB7/enr4FibU4FIrKEEoAqYJGsQUAw0H00qg6m20WmXOg+ApxLpbg/9+wYYXLmEwZXLEJevgA12YZIcShsIxtBLyVbk/QT57n3oXbyCfO9++/M+9HaG6A1T7AxS7PVTWnhLWyTaNKPnREJJiJR7+5H2E/QzibwnkQ5Sb1OSHim0RSLBZDpjPxhzwQt4Z4PshLAvpzCQ9HK2xSqw3T7cknQU+HES8jpAQ7YjAU9rOydSItP8uXIJ2RO1jQltn0xmyEF3GsEBwbl3uLhkPsDEObM2hJNNERxCUiDKEYoJ50iC8g7zHKzaRrhgEv2fXrXqvN7W2pAOwi2ECxKtk6rbxia2Zh17E23N6SOSa3cHWPgyGqkp8FBaoYcSYBzVxRehuvgi3DQ5pQUG+/LRTbxor4fnDqd46tYYzx1TDWqoCvrwFvTBdTBVoFAG10YFnj0qsD9VMEkGfXyA8gufwuQv/wj41MfQ+9J/x3ByDWK8D8Ml9N4VsJf8n6H/p6/Bc9kV/PVzx/irZ/ZxeDQF4wy9YYp/cGWIl17qQ+w/i9EXn8Po2hhHthGMYHUWDrP2gnyJBEKSYntqO1uit4P88i4GDwzRu9xHdmEAkWekLmNALolUy/sJ8kGCvX6CXiLq7CPOwRMBkQgbKKqzjASjbqP9VCDLJFJLzHFWk1ws74MPdpHu9JHu9MHyAZj1QQAi6BLB0EuEJ7ncMZgxtXqZt+ZwS/C5umk8yJjSxsAI6RV0LMvpnFz683JQSmgmOXoJ+Yv9VFBzh6po2B4i5xIwmcBwacsWwZc5csHDudk7QLOpkdbexnhCcENE32a7iAq2iBOjK2vDoJasVrbwr7L1BzLJvHqtjZ7kuJBL5FJAaYNccuxlHEOhwUc3oY+p/bUc5Nh5eAiRcpTjirroDBIMHtzBziMPov/CKxCXH4LOd1BoWENiJ0AbYUl7CbQ24HIAow3Vckk4sl6CLJfIemQgLljHKLGKMF+PzRJMIpEkV87JAVIFTeIUzbcOm1W1pcPEy6pJgUATbUj8hNGQ1E7UvVRgnAjIREFXtK2rU0DjJmcmtYapZzsOkcHhaK/xWUJOmVNByDyB7JVIC1sgXFQAhFceOOWESCRELyXlhB2369rnuwZ1KCfIAeLQipNN4HZbwSBTSsfldpumc0f3Y8ZF8SmyteourM3g/ub+7lOpWo7oJlg3xWMTe/fOd74Tjz32GF71qlfhNa95DX7pl34JTz75JN72trcBAB5//HE888wz+M3f/E0AwNve9jb83M/9HN75znfiu7/7u/Hxj38c733ve/Hbv/3b/pg/+IM/iK/92q/Fu9/9bnzrt34rfu/3fg8f/ehH8Ud/9Ecrnzci4qygS8kWqnWdSswkvZnvvLH755I1UmIAUkdnF3asyjhB74ELyB56IeRDj0IPLqOARKkpGt1LBS4OUox3M2ilIdMUVVGAywRZT2Kwm2Owm+OhCzke2M0wtOkygjGq/eYaE6QJpZPmKZJBimRcWSUd840PRCKs/UiRDDKInk1blVRkul10WDDYVBXuF/5FQsElow04V3UjH8mRZBJJJpDaoFJqicBEsFn7YSPwzJKCMk+R9CTKPEE6sPV0bPfMmjBMbYApsH0uks/bNsSlt9JLpsLbEJmGgRR4+yETgdwqzjMZpOJidvz+GqxtcMExUWqbHmqVco5os3aSC7IjLq3WAJQmpKkMQ5iSJDiz6cGbE2ybpBOuu320NRERK8LUxAazjdy4reGp0x4gUuiJsuRIkL5WjjDocxxOKhxNSjxyoUdkTnGM8uA6WD4AN0R47U8qlNrgvn6K+7IdAMD0xi1Mbx4hv3YdvaNbSF78ldB7V6h76fB+HJYa10YKn7lxhE88s49PffEQ5bRC1pN48IEhXvGCPVwZJmBffAbHz17H9IAygVLOkHOGpCfBktT6Z9YW5ENb8oA6i1baQKcDpJfvQ++BC2CCI7s4BMsHAGjdP0wlLg1THIwzcM5weUi+kw82pOQDZbsZzbd5SiSX0aTWTjj2+in2+tT1tJcKr2IzMiWCbecCUls3jg92wPIBpVoCnlwrlSGSKxHUTdz5bILsDWRpSTIRkGTM2jsOoElaUUZWApZmVMtVpjCiTo1l1rfMJUdigB1r51NBqalMV7b2q/VbZAKkGSBST6yVtnapQ6NWK4Ia0g4dncOVNmAnSRFF9G22iUiwRZwI88i1EC76HUpX3YQXmiFtgFQw7KYCuTA2IsKwk3Dwwy+BjQ+gJ8c0sV/YgVEa2YUhtNIQiUS620d+eQ/5g/cheehR8EtXUGU7mNp2ywBFZ3ZyiV4vQVUqcMaoM5ktzikTgbQnkQ9SXBqkuNBPMMwl+jYi0lCwwarA8hyylyIZ5FCToibYOK8l0ZxD9iQ5T4MccpAjGeQUEZFUJDR0jtxE37NkWS8lx8fVEuOSQwcEmxAcaU961V0/JUcjlzTRc9TKAMOskyATX8+AyMHSj72O2AeO3SCBHOSQeUZOUtazCgTZGDtQ14/LJMmkpzZtyBGZqtJesUbEIBGELvqTSac+CNR3zpAwUhGwJLWOKTnCPK0ggiYHTinRSEfaArTp/twv2n5dvOUtb8H169fxkz/5k3j22Wfxile8Ah/60Ifw4he/GADw7LPP4sknn/TbP/roo/jQhz6EH/qhH8LP//zP4+GHH8bP/uzP4tu+7dv8Nq997Wvx/ve/Hz/2Yz+GH//xH8dLX/pSfOADH8CrX/3qlc8bEXGWMK9cGAPqLltCwsgclbGdRu3fJTR2hAYrRmDFCKgqQCbgg10MX2AXwvkA4uL9kA++CHrvCtTwfhwVCqX9Uu/kEpeHKY4u9MA4Q9oroascXFKJgd4wwwsu93HlQg+Xegn2colU2IW3tt0lLfEvezQXp4MUqlDggqOalFRw39b3TIcpsl2yIUmf0mV8RD0g2DhcPRjm7ccwlxiXqmFDXEMcYevmpJnE0Kazkv0gJZhf7ANWvSZIwZ3l3n7IPEEyqInB0JYkgwTJIPFFsWWeku1Lc5qX/djtPq0AU5cNMdrYej+B/bCBpbDenUvp8um4gsgxF2ASaQLhSjwUCkZxMMX8PXdKC/cC574GT1e5AUpREj5dSiab2511bY3bZx1EW3N6iOq1uwgBuRauRY2dApiqYLjEbiZwVGikVgXFJgfQ/Yt46mAKpTWGeYL7+gkuZBz61nMw0wl4fxeGS3BQeuFE2e7NaR9i5wJ4IlEcHqM4PIbRGoO8DzG4CJPv4uqowjMHBf7++jH+5tkD/P3VIxweTcEFx85ujv/5RRfw8vuHuJwoVM9+HpPrB9DaoCcYekLgvlwi2aFyOgAFjbKeRDYk4YGbv0ptx7N3Gf37L4JxjnR3AN4bAIz7WtoP7OY4mlRIJcflYYZccPI/khRJP0e600d+sQQXjHwgq5JLbGrl5WGK/VGKaUX3KrEqM8MlTNIH7+/ATC3B1t8FbHCJg+qf9RNq+uYINilY3ZSOC7AkAbRVornAFMg/SgRHbn2fRPAgW0nCiNR2UoVVcdcdYClYJ9BP6DzDlOx8whlYWRNjTCYAEkrnFSmMSBqqNXcsVzonhM/YsbY3VFAzVvtcJ6k2HX2b7SISbBEbY9GXS5u6ECFFdA2YqSeMQtvijw5GQzBORilh6Ce2DpkuwI/2IUY3oUfUqYRlObILQ8g8hbH1ckSeItkZQuxdBr9wP8T9D0MNLsPkuygmyisWegk5GpeG1FZaSI7KE2KW7MklLg1SPLCb4fIww15GKauuG403sFbB5oiqZNCDmhT1mFLhiyLzhNJjst0cSZ+cI+GcjCz3JJUBRVKcg5EISkPaySXGRaA4EBrKHduOO8kkhlZ1N8wTDFJZR/ItyWYvlJyjJCX13aCHZDCBLivvzInUOpj2/+kgQbrbI8dukFPtA+sgGTvhO2kyt6o5l97q1HdJZh2cUkGIunaOS29NcyIHh7n0abFk5DoUCN7I1Co8UdJ05u55rZSQXpbdVatoXRhj/Gd71e03wdvf/na8/e1v7/zbr//6r8+89/rXvx5//ud/vvCYb37zm/HmN7954/NG3NsI652dRfhpop0OqCuASShDxBpTBVAV1KmtmoCVtjNXbwBcvkKFl9McfLALtnsZanAZenAZR4pjVCqMrM3IpcClYYZppZFKjuNB5QmgQSZx/26Ghy708IILOS7mCfbyBLlkkNaOMGOsisvZkBzVpECuNUQqUE1kbU8STuq1nT6SnT7S3T54bh0UmfgovgPVbSOiKbQhTvkshK5ttCByrd9L/Bw8zCUy4exHB0klUzCZettXDiZISyINRSo8wSZSShPN9zKku32kOwNIG1xy4w5r6HCr4PYBGskxzCUmpbL3luoCaWOolmpKiossc+OmtCSq4co9qdoA53TPU1vawZGE9l47G0JjD4pqO5vtmjy07ImrlefsngsebYp1bY3bZ11EW7N9nOV5MuKEsGt1JmStJLJKLAmNCymHo1XKbBdPTnP8/fV9PHShhxfu5vjyy32Ig2dhxsdE9PQGgNEYphwv2M1QKINhSnML372E9OJFJIMbKI/H5GMUE4BLHJUazxwU+OSzB/irp27hs88fY3JMdal3dnP8wxfu4n9+eBdfdiGDvPE5TK4+ienBBJwzXEoFcs6w+8Id9B+4SPVFDdk0V/YAANJMQnCGUhmY4QD84v3oPXARAOp5HESQ7aQSD+xmGBcVBGe4aLN/GGMwXEAMd5BdHEJNClKz7Qz8/oLR/hd6CS4NMxSVwoV+gkzWHUKlzMB3LtpmRApssENKMkZkWCaZFUJQg7y+VZJ5u5XW4/X/t/O3YPAKPoDSTZ3foQzAuYSRWf38g9RUwRgkJ2INdhypoA6i0LbWqkxsOQdua4OngJAIe+64GqOuBncjgMhJVACt6xRXADDaCyhOWkcy+jbbRSTYIraK8OvGGAM3BpoBAgwMxha0py+mNi3CymgIAAKgjm5VAVaOwafHYNWUunZxAZ4PkHJBE6wluXhvAN7fBd+7DLZziaTT/YsYVQaFIvmtSxHd66cY28X/USIwthEGqqlDBNyFfoL7d3PcN0yxl1OhztSmy3jnTUo/SSd9p16j1CEqkl1BFcqmmVgSsJ+Tk7HbRzLoEblmc/EbC3VLrmWCjMQwlxgVyrfBrgKyjerTcAwyib1+gh07/twrwShF1HdLYZycozQn9Z117LR3jjhUoQOikCMZ5LbuwQCyn1PtA6e+E5JUcfbhC1bfS//KZK2WkCwgCuuiqMNM2u1lTQ7atNwGfA0g4Z0kkUiY3Bo/WheAJ4l3jpjt7Mf4yVVstyPKE3Fv4qQLpHsVjfsWkGuGcYpeGw0Jmt+8bVElmCrAXPFhKcF3L4MPL9ACNs1hkj5UvkO2RDEclQrjymBUUhpQIhguWEdkJ5coKuoE50oRXB5muG+Y4oFBhvv6KXLJkAkGqILGAfgFtyvenJaVtRcFdFE1FLm0TY50d0DR/7xPaTNZD4rVUW1miZ6EM+RWtX00ITuitMHYBpaccptLjjwRtiQCBWj6gZqYt+6xYYwchrRWsKW7fRilwcUEZRBcYoJTeuggJ3JwkHvbx7LcO0jhNMk5s8SgwDBPcDipMLA2xCmg67IOFJxxtm/HKs5zEQSXWK1ioI5wtYJN5ilUnkL3raOXVF55zhPpFedOYdggNBt1U2EVc8wryHuZxHG++TL7dijYIm4/4jx/jsGISPNzllughin6dj18VGrcmihcH5UYZhIXewkevdjHXiaAESAuPgDWG4DnA5hyioEeI93po9SGukZPDwCRQj74IuxVJYpbB6QaG16AzgaWYJvgs88f4QvXRp5cywdUd+2rHrmA/9MDQ9zHpzBf+hyOr96AVgbZboYXAuhdzHHxJRcxfMF9MPkQANBPOO7byTC5kMNog36PSC5tDLTMIXcvQVx8AD3A2wAAkJxhL5e4f5BS108AO5nwhJURKfhgF73Le963SHf75ANZBVw/FdjLiKQrKo3dXNYEmzYwCQW9uFbU4K2/a1M1ObihMbj0TPKfLMllFAxj1BAuH/g5nKU5tGu4xmp76QIlrsmPATVZYDL1dsoRZIyRj5Jy5hmVTHCkoSCDUSM7ZDnZnd4ASpDfpysDY+qyQACRg05pT4MLGgLZkhDhWBhj4FaIME/Vvwqib7NdRIItYqtwCoewPg4HiGRjtYqqXfcGAEX0bWc1phU5IeUETNmWxNYR4QBMb0AqA0sUscEOTba9Xah8B6a3h4KlmJQKhTJQhhyJXHJc6CdQ2vgos2sD7brOEMGW4mI/xV6WYC+TuH+Q4GJORTe9Y8RIBcZ6A/D+MZKyqtNvEolqUkD6SLj0i/XaORp4ooq61FjVmJ0kE86RS4FhJjHKExRWeZAWHGPBvRFLgyj/Ti6x108xzFpOBguiIYyi95AJWN5HMhihmtjuebYejSPbXI0d5xQROUiycJblQJI2yEHXsc2pB5xyoqg01QdwzpFpEmy9wLlz3fbc2Dma6glnrNw1iDyDKOrxmuCeC6tig90WWyDYgBidjog4C5i7lpxDskHr2r4EhAvjlEbKhLYp9BImyWCSPnS+g6lmGFca08pgXCmUmuqlCMYwtBH+nVyi0gbSpjb2EoFhKnCxl+BSL8FuLtCzKfOsLALlA0WmRb+P1M5j933VyyEffBGe/98/iuJg5GtLehJupw8xHNp5uEe2Iwh0uLR6X3jZElXeZhQcRSIaNqSXClzoJ9jrU2kEashANsiRVHTTeW37kgS83ycFdFHBKG3n3SJQb0uIVFrV3YCcqv5Ow/aBS79eYCBHw9W06aUCO5YYBICx5I36o3lSB8WGOSnwHDGYChdcQj1pB2NnWQ6RTyDyzNtqlVSBapBqjiaDHgWWsvpFzX34jO0LVXd7/QSHvWTTjzd9Pk+0d8SdwKJnFsm1uwABydZ4z5JuylBKZaWJ/NjLEx/s7kv6BOhsB9i7AtYfe4EBP76OVB4itcdnJaVCistXwHoDJGVBKuuLD6Ea3o/xfolRqbxvIBNqEPAPrgzxjx65gFc8sIMX7CSQz30GxdUnUR2PIVKBnYeGGD7Yx+CBAV76v7wO+Vd/A1RvD8YA/UTgoQs5xgUd9/7dDMNMQhkSKyTZLsTlK3TNnJP6DqT46icCl/tEsGkD7GaSRAkAIFKw/g7yS7vQSoMLjuzCEDy3Kaac5v1hJrFXKpSJ8T4MZ3RPjSSbzIYa3BgYkZCSDDY1XzD0knre9yQXAAgJZD3w3gCmKsn2JESSAcSTSs6QCg7OyI477lQb+FIT/plz6Wu7CuvzAPS8XXooAl8RMqHvvkzsuKmGm4HyTYkSn57aEhYwDsNFrb4L61/DpYfSOMwJJ5hob7aHSLBFnBo4A7RdTojW+wwB026Ni3OCmFZUP0dXvg2xL07ZG9BEpZV3TFiag/cG0OkAJhvApANUIqfON5rINefLuBx9ZaXHw1z5hbsryr+TS+xkEjupxMWepKhMXyIXVGwTpa6dDBsF4fkASVV6p0KllPLTqMGWZ1SrLXSO0pzUBzYa7klJVnelyeyYPMEWEINu3K4RgnOOhqlAL3FFPtlsmoydrHnWazh2XHCIXgpdNAk2mbv0HuccUQcfX+iTS18TTgSRIKe+m9q/jQtOZJttOiF4rRrcsek9w6x2kJxzJwLnzjhjJakOnComkHnqx+ucI6dOYBldJ7PpvL5oRkRExLnC0rXjnE6RM9s4Z4hxMEdM2aL1YNwvpk3ag2IShdIorS0BHHnFkQiNPqh8gNK0yHW1v3oJdcIepmRD+glHTzKkHFQo2RYtdjaMpTmSQUFE2td+O/Twflw+uoWbf/6XDYLNlUPg/V3w/q5PN/GBDkNkj+QMmRTIpEI/ET6wBJANcfbE/d63JJwj11wNG5eyyRjqWjaB7XMKbmf7Kpty6Qk2qzBOdwcN28d7A2/7DGN+Ye/Ol3BScJPtSxvkYJfNdgEaVy81l9w7HYIx+EKsrkSCHbvMU+hB7u8Ft8ElwCnY6tIIvm6ctPfbprZyBt9JzjmadC8rHPTT5Z/JiHsCkVw7x3CkWvh76++uuRuVyKG3E87Qlwy5lLZTps3iSXKYfJdsjarIJqiSsnWcP2RhBhfBdu4HZAKdDqF7e7g+URiVConguH839+rpy8MUX/7gDl5+/xAP7yRIj5+Hvv5F6KNbAID8Yp+6Pecpdh55EMn/7f+Ocng/oCvoicEwk3jhpT6UNigqjUvDDH1bh63QBr18B/LiAyR6sKUUDKOyB72E4VJOzeOUMdjLE6/KMlyAD3YhLlxC314X37kA1htQkAtUNmgnFZjmCaaVRi8hP8CXiJEpdQS3aZfe7sE29eEMKWdQjH6XvG5iZGwdNT7YhSkLsCSFlrm3mwxkv1NLbjHUHbSNMXa+F2BI/fWAcU9uJaZWkSWceTEGMxS04zYQxpIUWjhyzd4Hu49rgEQKtjqo5YJ+TLr0HCLcXA04BldWofa5I+48IsEWsTFClVqIMFjcJVRz5BoDmgZLzzpHlM4hAaGJRLMLY38s241FJ32YtAeT9GGSvO5cqut1tbD59aXmACRSwX3KDACkVjHVt8X2d1KBvSxBL6EFvXY58YGT4WTGrDcAq0qkIJJHTQpb08w6GYESTPT74PmAUnt6A68+QBCNYLZgp1usl8pA9Y11KKqGc+ScjGGeeHLQORmZ5D6fP7ynTMrascv7SAYkLRdWeRem9zglWNLPIXYvkER7sEvPo20oUDdoyCTHMCNyTQXKDqcYBGr1nSPX2g5S6Ny1FWxOlcbSHFxrSAAmlT46xjinGkW21o/rnIpWM551EWXUEaeB87IsOmkdtlO7zlXINcA6SiooVMwB3nKcbAFiY7t8heoqV9sskwbKCCTcoNR1aocUDLkgcmqY0VzWs4rcVHBAFT6yTfaN+zIHAJAkE4ij5wEuUU3HyC/vUsDA2hCW5USuDXZIPd0bQDtyLeiIRmQfjSWXHINUNmyIm4ddx0unNh6kTfvhu2e728w4ORtu7s16MP0+El0Tai5A436nWp+5JwadetuIBJB1NzYAttg0qe9yKTBINQprp1PJMS4UKvtAZMv27ebOfpD9kTa4FKq3fYkE++LB2JngM2N3JRQcKVjXHg3veR1cyq3ybieXGPdTXBxsrmCLKaLnD/Nu/3mZ3yMWwJFqoQ9g4cg1g7o2leCANAAL1vapzcqgwvkJYHKAWZugKjCNhi0zgkglkw6h8x2MNcPRWOGo0JhUGsNU4MUXe7h/mEFwYC9L8MhejhfsptiTGvzWTejxMcAFsgs7tskLdQDNr1whlZyuLFFVYScVuLKT+bl2mEns5kQOKg2YbACd7UBcVDBVAZb3yYaqArmgYBIAlFr7Yv+cgdIp8z7EzkV/bWLnIu1vVWTS+l/DVDdUzCIgmoxIAVkBxtj6z3UnT6dA0zC26UGr9rRIYYwGRALt7r8PktD2igPMkL2j2qP2m8slEXxVUf8eKNgEAxinMkheOaeJXGMuQynN6Hna8zZU24IhNx0KNrdOESl1MXVZOM6WGVOTfJxBxxTRM4NIsEWcCrw4reO9TtKtwzkyjJNh4pImRQCsJ5qOlO/uYicuO+FqVZ/ZzY9EWBlkgvvJtKkY435S37FOxjATSDlDoQwEp847PrWIS3IuiglYkYMPdqABJFxAJBMYrWcINpaR2o0Pdn2KjI/CBF1hGgo2RUZG21SkLpLKpQH1E4GdVPoU0USQdJl3Ohm1g4SqRCoElCXTQuUdEWwZtRIf7FhycODl2YaLRsSOM3LqQvWdc+Tc2B2cgq127kTDQWo7d278lOJj1YtVCa01OOdAVdZqyeD6PMkmBKDPR5ODiIizimUk21l3JilizmtVQpDu42yIkSkMq+crIq0MUsHRT6wDxUDBD1MvtGnepjmMUlxItUBpIwArqrrwcVAPE1pROQXOMf2L/0ZjKSZIL+yS4srVG816FJwZ7FrnJKkdBcbhnoxbtGc2XZXSdmZtSNhMYJBK7GSU2kpBDuFragrO6uCEG7ebg/OBfZvsnNF1DU9SbxORxZ0NGezO2L6wpAS3UfxMcmQVBWoAp97WKCpliUHu1Xe7eeLHnkmBnhR+3GEwz4QlErIcrCog+nU6q24p2NzYfUmHLAek9AqGsA6ObyqRCOxkEkVfY7pbBwTX/pzepiYHEaeLsz4fRqyJlnqt6xtHil9LlgR+hp9LAa9EYqB6Wgy13+NrfcmE1NRJhsJwTCuNqVWrucYAFIwmcn8vT7CbcVzKJfj4OjX04Ry8v4PeFSC/XFBq/N5liMsP+TrXk2wPABXqf2CQopcITCtNdaszEiQoY2BkDp3vUF1OVRC5JiSY0cgkQy9hACilNOFk9xisv9Qbgu9c8L4OH+yC9YYUqAfZ2NQGp5w/lgo+QzZBkI1xwgTjlGQMkBwwYF6RxhizdYukTyeF0TR/y9TP44wZqx6rM67CRgcGsCoyEHFmSUEGpxgPzmvH4sdsiUFmVXfuvO6ZC07NJRxS13mV1ec1XIDZ+wQX5HKnQK3U9tk+GyD6NttFJNgiToR5KjaH9lfd2RW/4A1bXndBSDsJpba+TEtpwHidzy5SGEmKg/B778iqRNOrD4FSG5QB08d5ndbonKOepMYGvhMno5x6l7ZKEzujKHhvAA1yDowldkxVQAQdhnxjgV6tXkOaAU4FxjiMMX7Cdl00c8FRJXUhzrZzJGxB6Nym9LjU0Do9tJY61xcsAwejD6Yo5VbIBKIqSf4Nm75knRHXSIINdrx6zThj5x6JXTyEqTLa3q/MO0c22s/rTnG9VGBg06m6nDtvJC3ovtuUT1uXT085qSHcWFwx0LwfpPVsockBsFYr7JO0zY5YD2fNkbmblx9n6l7PsyHWZvg6bJz7L6RxqljmWusgsCmU/hfEacBY3S3MLYYFA5REo+GMsyOpLXScClJSSW4L7euawAHsPJURwQYAXCaAnYN9zRXbsdLXHM1cDc8+OV/Bgt2N1dXxdDZEWxviulyGNiRUbw+9go1DclIV+A5lzp4x3mjywyw5yLgATyfefgC1DWG5VYDZcSPNPTFouGws1iWvg0tkQ2rbR/U8Xd0cCtDUtk8EY3cpNx1BvbC8hFbQAATn4NOJV28DAE+tQj1vqtdgC1yH9sQFsTKrVuwnArqfoDyJgg3r249ob84OztQcGXEqcLOWbtgKavAGzmAMGg3eOAIBglU3wZimmjoIBsB2rzQi9Y3CaJ1tqEumbaxCcyZDT3IMU47UFESuMQ4+2AW0pp+ulvXeZWB4Cbp/EToboNKk+solx339FMOUyiIIRnXZpJ1ECw1kOYkJmCI1l+HUTTVNGAYJB4fxZWC8msuSTHx4gbJPAGrukPTJd0Nd2iCXHIkxSLhrFhDUUgMs0YZmDUyQjRCcbqez1zRsXqvdLEEGIesSN6gVa9IAyrCaDGX18xWuRI2ozwvQdgjPy2shhq+fBlLP+a7ZXtXoamSbel3B66wjZ/uYTKmxnztpKMow2ir4gOoE+oHo22wXkWCLODF4MAEt2wZYcdERFAxl3EZzGKvJLdj0GiEtyZPORJQBO1Fp5qMvADBhGokxUJL7LmqC1bVzqGOoc45YQFJhNo2VS68+YM6p4AKsKmFUkMoqbPeXMKU0H9TjDurnALURzgVHKTlK58AxoNTMk1ZucnYdR13dHNcgwKXIuBRLZgxFzawCwQQplkYImDKhSVxrr5pgCXXc4fmg7njkVBOWZAufvXPscjtubchopIKjUHqGHMyl8B1PHblG6T21c8cZaEzOoHqCsOefCeecIktK1Q6pcwAdUcg4DDsZ7WFaBO4q20dELEJ0xE4fXSSbhy9czGunBmgpc+FTMQyAnhRIOEcZ2ARaKNcBBqfA9SRPO0DEbap+VQKZrm1HUKONTi68go1luU/tN7ImqSCkTfEwQTSfeRvi7J8LyChj7LZ2rIJ7csoFOGRg+9oKaF8iIcvrsboxBvekEejI69prOojkh/VHGWNgjDq0urGTk0HkYCnrcbuad87uubHTs6lTfBrfr5AcLAtSDmrr+MjUE50AasWgvd8uOBbWvKvHXSsY+4nwNnuab06wrWtr3D4Rdw5xLr93sOyrxlE3eHO/Nxq82XqQxoiG6MAEf2+WkDG+xhfAIZihjBmbHpgKIth6goFNJlapJYlYkwmtlTmnOSzpU83qpEdreKV9DTLOKFDv5jZHlAFApQ0SmYOnGlBpQ/0tGHXQZNBQhuxGKjgFlWyKJvIBRJJS3U1b59QkWa0Ud/ugbjyQuFpqOlAhu/sXwPkKGh1BlcCu22hQg6RijIGB/BLurtvZj1aDn8YxrW0yVo7GuuyNFyHYZ9k4b53e6W00q5VzdFOkrwGHVg042M+NYNRQYorNEX2b7SISbBFbQ1fqZxt+kwWqtUb6Dig6wljL8AC1TNblt7uJK0hV5ACE7QiT25QRzoigCtUJIcHmyDXZcpAEa6WyeicjUIMJQZLuqux0kLxzlOY2gp/aVCTWWKwT+WSdDNsi2zl42sBPxAC8c+TSW/uJoMiWG7t1kmDUzLiJHAxUdmkOo6l4acOps2QWy/tA1qNuOoFzZALjyBnZjkxS7Tin7ijt9YRw9zwkB52DFDp34V6GMTBLEEKW1Prajh/alvjknMbvxm1JNqpVdLIibLFOQUTEGUeodg7+3yDZAITJ564jFwLipJ0CIZitccLD1JBaxeRIFrdgdsq1dpoinY/ZRX7dHQycA0IAqjlH1QGa1Hc/86URpK0XF9TCpLGQDanrjloCkDMk3DQW81555QI0NsDh5t9212/XeZXJFEhzH1xiXMBUSdOmyLRO17eBJVJkJHWjgCC11SkHyDZo5JL790ttoGV9lY4cDMfu1BYisB/ecXVlJ1yJhEA5CJnYwJhq3fMFQbGwrINNIc6t3RsaahBRZpsvs2MNtoiIs4lFXzMieSyp5hq12XmWoZv8MRyAYWC23IC3D1ZE4M7n0iAZq+t0cZB6yQXVfY1PLgGZkdpMZp5w83Xdkr7P+uHMEWJE+ChbzIuzwK5Z1qfUQJr2YVQBpqwimxORlnJJSi+41EWAVXY8MgHQhxGVT9k0CQUsnMmjMVD5nsQSftL6YP5cqNVr/j51wJNUYWC+cc+pE6e7t5wxGBjvcLDwGMF+IanIGXU4dSScL8dj6u19IwV7/11aq9veKeNd4E8weqac2Zp+QpLSUaYwqi4v4dcsRvuGPnwVR3wOom+zXUSCLeK2YYZcW1aU2k0ejFMNti5yi9XSXxOoDgCaGJ1cV9O0CQYeEGxBLTBbPyWUBTvlGk2yTqYXSLcZIwWCrigiD1CEwaq+Ggt13kzx8Q6SUx/4ls/GFtU0fkyJMnBrdMGYVR7Q794ZEawRyZc28tNQH4SToT0nOUjOGRKkxAtVepw3utyFRTpNkNqqYbyqrm7QQM4dHcY5dbyhvgvJQd+5LhWN9B7Brcw7SE8iBZ5sNLygCF2L1AzGXkd8TkawxToFEdtEVDzcBrRItrnbBMrpxnTJ6sW2sLIEzg24CdM44FOAQnJH2Pca695wwW80IDUY8jowAFCgA0Ra+WBBYlNBBZVEgC2NEJKC4Zjdwt3NwwDNxVq0CLYgtbVWENfKAaeoDsfu5uBGcKksgKpWbDFn93xDnYG3fWEk32C2rINLE3WBMMFYo5OrIwad/XBjT2ytO2kDTCwMLrnxz1MOVkWQgtMRFHNjl0FZB9SfDQ7YFOKa0FT55svsWIMtIuJsYdVvlwuGh8RL5/EYp2WpRu3rALW6zc637lg05wFcI6jtxrzaS7i1vs/0SG35meC4rkt2o0mAI9Jqss2NmwXnAGiOUYZBOCWvK3tgNKAKiID4YUYDlhgLu346RZtOe6jAoY22CnFGTQZYrZxLXHmF0A8MldStzCU35uYD4TCaAkN039mMIo0xQKDOEHK2z5NmfsOWcm7e8w3PaTQ9Ay5nu38yY+9t/Vlx91rbZ+melQ/UBQQjc/eOhSHD9RF9m+0iEmwRtwUrO5FOtQbdLEIdKhLcdi56wSXVzGk5GEA9SbdVB8Y6Ro3FcUCuSdeqOSR42oRg4CQZwLdh9kSVH4R1MhzxZskpVzuOfm+mWTpj55QFDk550JUi6gg2nxpqVRQ+RcbowKiw2slw57TKu+YNDEhBaY2yVU24+x6mUbkxS97sisM5Q8l1pwPoioK7xgiSwztIiWg5d3ZMniBMTF2XQKumciJwTCEDpcQJIjxArFMQsRri0uMOo8tmOHR0gQsX6l2RWRf4ABjADYwhR6C5TRjtd7aDzU8bMRowsm4AJOo0xeacJ2xh/aAkgkx8Yx8jLNmjTaMOmyMEXW0Xbkkq3SaqAoIttYRVqN4mkipwboI5uCu41Bh3O7BkHTs/9qDEALfpoW7sCefIrUCQurXS+ZWpHcGQYMukVZ8LR24GDqd79IuUgzIgB0PVoExIuS0CtWCYogN451AYstnONlfJCRRsiDXYIiLOClax6eF0015qOiKmEaxAzYfZverzhSRSmIoOBsYNDAKihjMfdAgJNgPM2rkw68eNC7YwP6/rxtE1sIZ/xKw9cEEo14jO2YeQUGuIKVyKKFD7bZIIN6WM98VcrTfnR7hsIuhyLrnm7s9cvscFhBiHQZPA9Hed0f6akXrMBUxmbHfr2c28Hfy/Vkybep/whdrHTOwzcB1QGVqfEy4BYe17QLCF5xU8SGfdANG32S4iwRZxqpj5qi9SrdkUP9qxngh9o2UWGp4g+sBnjRDQrTqAVR10EXFhek/dkQVNgodzEkC582qS/3qblqTkVAA+zRKgxbpzJoxrDx2qD6wh9PV+UKsfjA2Lc04d63RgRRoEm1XgOedopv6Pfyhz1BNa1XUa7LV6UjBUTYggLbcVOUJwvxPOqWGnBIQytmtrK71HcKrbILmt+UNGteEgcdZNrtrxe2JzDsHmyLUup2gTtNUWq2wfEdGFk1G9ER5tMm2V99v/b80LjYAHbPAAzGuhybkI5jPn7Nh5W/BAwdYx/za6ZNtzUIRbzI7ZpZU4WyGTulByh3rNE3YAGOoCyoJpCGWgeL0xEWx1un4jtZWxWs2wyhwc2g+g24ZYVYWzezPqAzBwq+B2tg8AFDdIDAsUbc6GsMbY/UsE6m3dcsy6lIMynS3r0KUaFEGwJrjvoRrBPXDJBXS2eWOddW2N2yciImK7WOd7tbZd71hHu/fbJH5IAvnNrJ/SsDNhbbeWvfMEV+A3+ZRTbWAYwExNdAE1uebOM/cadUCqhSUR3No7qHOqwKE0/JzOHUGIVv016Do9NLDXLrgErJCu2LqXXXbfNafQAbnm1GtOpBDCBeMWnppxGKa9H9tOaXX3kxolGDDjzskax2ir4dpjh9E2Q2vJfViA6NtsF5Fgizg1rPM9nylAHeS4mzZPHk6MgYS6ndYDoM6pR5Nca0tbfX2EIOrNmIveBFEhwNcAcxN8oGuwhsM5SHWnGBM6R649dKA+AOMNv8RHjDxnyCA4OUgAfIcegFJkKHoRpoU201tn1BMtctA5ojy4L84Q+LEHqglXVBtcQmvUijrrHDFbEwKSg2uAsyYx6J6NIwddK27JQ3Iw6F7njDWrjVTonCLNwI1ppOXWSgWJsNbP3BSxFaHN7LUs2z4iIuIOYZXve6AkaC+YnXCNMzJNAlQMOXRCgFlHxJFsfv7tKnEgZNN26KpZ59PCOSTeqQhtiQswoRlRpppgFBEnCbexQZjmfOTqw7nAUjM9PyAIvTQumINd6gswMwcDQWCpbUM8OTgbGHMRfVfWAWCArUPaLuvguueFY++yfY176mwfsx38pFUOKlWXGODC25AZ1WD7vpvaMWTMQAoGKAMmSAmSnMDjWdfWuH0iIiK2h219ozqnAhcE6rJTgW/j32IMAoAJ3g3ru7n1vFdscQCBnZoRJoTjA83r2lD3UGebGh1Q7ZzaIJ3a6mbdup4WsQZGqf/08rO8i6X4oI7gDJKhVsSF9yQQWTRU2wFZ5+Cuo9HgiDd9AeaPQSRbeE8b53b1z7AGupRzwbkpy8r4tYVLx3XXYACASzBOtnamTh9A/hvn2NzaRN9m24gEW8Sp4ETKjHAS7DI87veAXGtv4yZaJ6d2U6xTHRjDZrZ3suBQfTBXbctayjX70xmZcEHvxxjIsj3h0xHBd+SaI/uMnUOVMb67zsy2qJ0MUiDANzhwRr0xJi4BrutxW4LTtJzAxthl0nDsTGvcLoXKFdeuzSZ1O+pyjtz42oXB6/pFrfprCGTXgfKjrm8g6nsePCdf94C3PksREaeAVVNJIraIeWq1VfbD4mfm6nY6ks2r1YK93GLY2RDXZMfXb2kQVKp2glyHMUNBg0a90XAeYwy+m1w7YMB4naYP5xAZr6oCJ5KNM9NwPIBu+xE2x/EpJy2bxoQEjKGAS2g/nILbBWdWsCFh3VRn+3gwdsYYjGFNO4naRi60fXNKSzBnP9w9502lmScFw7GHivOZphI2xQoMEFZpYjDT2CciIuJ8YB36gLPFKqqFPHuX7WqTX6yu9RnWdnN/c6RXcyfua7s1fKnQdwqPbwkewVhDyRSSayvFC1pNhBp+miXXTLBFu9Mq8+t/zMzf3nVjvLM0UGjfGnfQ+2wV2nB7+Gtztrzr2oJ7Nk+91tiv3bW8FeT33UsZfNAuzL5qqxJhWmRdiyQ8YQWciC0iEmwRW8XC7/Y858dO/l7FBnRHQRx4x6SNWePm1Gsa8EUznQKhK63RTWpuYR46SI3xM27ZOxcFt0QTY2DG1BH94PraHYHa6oP22ImWYjDM1Gq6BamtHEFhbV6rEjiCKJMf+6x6Am1yLdjWOXZdHVvbakB3H41ZzTny5JqLVoXKCd4RQXLj4uQUOSdpbhpY6xq6yNh10aWUXLZ9RESIuAY6JaxDsgXzgPuOLnaQmiQbUDsF9TYhwdV0gkI0FLi6smSVhtHaqg1aaYVuzgtSE7uKNFvqLpiHyYYgqBvX5ns67Ufg4HRG8d0YuADToOvoakQE1MGNLhvSMRc72+fSlWDr0ZiW8o5SX8OAWJMY9PXXdPPzEJKaDGiMm9l1SH3P54y9pUDw99vJHUE3zRggPYHHs66tcftERJwH+JjDHR1FNzb5HrmvetiEbCFCezVPwdZ+q+O484gg0zhEYO86yDWfpmnf08FB26OYlzLpj90efxgoag/TKvLaxw67P9Mf+Ow1OHsYjN2N2xF1AD0P13jBlTVojAvzCdL2vV60Vmg8m3mE6dySRrVy0P0+QxC6wJZuZvTUgztZVbTo22wXkWCL2Bq2YSRnSLY529AJg4X+gjFx+48r3Gk6Bhs6RWH0wKeYhONjNqfeOxkSjOmWgxQenPtJtbFID6P7AVHFWe0kCZD3wwxJwlmH0XO1E5xT4lRfLj2poZ4I7luDHLROHbNjcF1uPBEXqiaC2g1thXBdSwArOUdtBULDWQpSq1jLIHr1oKrgugJ1GbQ6TbS+5pMitrI+mziLi/SIOwD3HV9We81i2dfTLdqNaS6CZxwFt32QTtNI22mPIyh87AMdYr6SYUYJ7ea11jW4wFKoXGOsuzxCWBohtB8uPWUmvZVZCi9Y7Hv1nem4t2FgKbQhge1rN75xts8Fu6Cd3atVFW1FRduGzET+2zXYvO0Gjdt1mA3uedfYfcfyjqAeQ1Dr1R2JnTRFdH37Ee1NxFnGvHnzLOGkX6FFX/lOW7CirZp7jHnnnOcbdQSana+k7dF5y064c8wEW1YhCNmsnZpHFM4Lqodzc3gNXglnxy5ANm5usf+ucRry5xY9t3Dsbo5tX094vAYCX7ZNbgL1GiOs4+rsW/t+16Rp65hd41gT0bfZLiLBFrEVnNhIBhP1SiTIEiVSOGF1pfYAsO/Xv8+o2LDAUDoHybaonusgdThHfuwzUXDrxcHJnA0UbM0fm9YqUMvE/ZjR7PLjSbY542/WkKvJQQBEEPLagDVUgq3ovY/kBPfPpeUyez0CtXPUfj5dde/C1NyZ1KrWPfXOj4/mdKs+Oq/lJDCYIRaXbR8R4XAWHYq7Emt+zzsXzYx5QspNz85mdNWUdNuF0fPOtB1FHZ0ZrE1wCuIltXhmCKIF85mbjx3po60aOiyP0GU/6vm7ew5eqL5rX6cdeyPVMlSvtYJL7j66uqkKhgJFHVKX0HaEts/V9uwkNt14nBKgKyjWUhq0m0ksVSBwNAqEi5OYm3Vtjd0nIuKsYWkq3b2MNsk2Z05fprLqJL8W2cGOv3WloIbnWHiced25sfz5zxw/PFZ4HcHP9jG9ghBs5p4YgOzWgnsSbttG+753kWud5GC4T2CvZ8aOWfVd53NFU4iyDdFAfeDo22wTkWCLODE2NpJdRmWZxLU9maw4udTRmRphek84kdWOUju8Yse3roM04xSxuc5RSFJxWyHN5eW7uSxM8QlJQVcY0zlH89R3foyOHAzuO2OtFJlGtL9ZP25etMOlJrmUJDZnm1A14UjNUMHmx96o/WOdO3c9QLfqw40fHYrHE0LDQK9hWdbZNuL8Iz7t84NVnxWHLWVjJ9OQaHNwvzaCB/MO6BfZdXcx//6cVH33/4Z6rWM+czbEpSy2SR8TbAc07QcRVh3pKTPjCdR3dsyMzaZi+u2DBg2h7WunpHjb5YNIs91agWak3403VLDVxGCrnt0y1WCXzZgz9uZ4mgG9MM1HnETBtqatcftERJwVnDdi7Y59e1Zcm641ncyzJ/M2B13/vHOEb/v5k83On8vOtZLCb95xF5BjoZotPFbjfPP8zMB3C4/V5eP4aw8wL212JmW2ZT+c8s4Y08hKmltXz46/MxX3hIi+zXaxFsF2lifFiNuPrXweVozczOwTYN5XvL3oZcDS1B7AET/13+ZJr+ncHQ7SvPE6co3LpmS4PW5W144LFXgIolezBqQmqmZqx3WMx4/b1SNoT9odhOAyxUSXQ7fMOQpVE52Fwdv3sVGoVTZIzZnGEuF+WzRGZs0oT2y0sxnuRntzN17TecXS1NAgkh8ufkOirbG9/bmwELQndnTw+2o2pFFzLeyAFmzvVNBOTVUHatBZHiG0H+EcHAZomhfZHVwCOiLpLVvSTtfvCi6hRQw6u8daZRdC+xfW6lxp3C7HhvHuoFiXDewgNdupSdwYX6zbGDQI102xrq1x+0REnAVEcu0OYRHpFW6D1jxm6uczL/1xrj+0BpE3DzOHXnAdq362NolvLPscdN2PxmkWEY7zCMIOwcfsRnzWXoV/OyGib7NdRAVbxEa4Y0ZyDlEy0ySgldrjnAxHtIUIiTVgVp47c/52VxvnIM2LjDjnwu3f0cHHjwW2Bg1qxV049lC91iAEWyqEufUHgo42Cx07H8kPyLWgKHVbecAZgwocujAtV7TTQwODtNSx66qr1h6323SZ0dlWlCfWKYiIONdY9JVkHX8P7cmiWSScd+cGZ4B6HraL5XlzGoCm7VgUOHCHRl0GLAzM8DlWu8t+hAGaGUVBV3BpXqBsHjFo/97VmEi1iMG23XPbtcfeLusw11lz51+muliR1GyMyx/D/mBsvi1eAbEGW8R5xLyPYCTXTg8Lyamuv617vKU7nGx9Pfd8HcddWXneEUzyx1xX2LHg+DP+yhLCcV5qa2Pz9vuh/TphM4N5iL7NdhEJtoi1sXUj6SaeRZPGnEVw1/c7rFPA0VQcLHIy3PbLFsTUHAAzDhKAGSep4Ry1r2OOg9QmqZyDFLbODofY5RyFf2ucr50KA6pdxoJQROeYQ9UBm+16StfeJAW7HKN6XGx1xw4uHXSOwTrhQmJdxChPRMT5xSr1VRxCW+Lsgml9obtq1TSm3XkD6SDZOtG2HSHRswBOxebGFCqgw7G6MYZz8Pzup4HtQwfJNrP9fHKtXevOTZTeseiwe7RtPfZ5416m3qbzdATFwsCS+7099i5Sk9XFwdvvnyBDNCrYIs4VziOxBpx/cm0u1lSWzavxFv79LKJr3EvHOuc+bELerXz8Ln+vdd6VbvEqCsUNEH2b7SISbBFr4UTz6zLmfdWJYsXtwtSesBB/F3iwDzDrJHXvVKvBHGacpHnO0YJFuks1cSSV+xsdp3V4v2/TyZg3/tpBasqM241P/Zhdl7pF0Rd0N5RoO0b+sJs6SC31HV3/ks9C52flZAYp1imI2ARndG16T2GTb2J78T6PfFrH+fBFijtsSCe67EdrbqsDHE0bAmDWjqD5eXRz8FrX5MkozFVu+59t9dcC5d0qds+9tZLdY0HntWCY3la3m+MsGnsHGFavW7QuYg22iPOAZYrgs4y7/tuyJgGzrJHCaWCdQ88tCbSF8a3yWWifZ2VCbMk52sfZlCA8CaJvs11Egi1iZZwJQ7nipDKvfo4Dqaxah+4gd+aliRogcJAWdOoJHCMAC2uYzeza4SC10VU3bhk8yQYsdu7aY15x7KG0uX34rtpxqxCDzRPUikenNFhKtEVE3AbE5cbZxTrPxpEmDn4+W8HxYHPerzfgvt7lSjYE6Ax0uGMtq0E6r/Npu4O222fhPNwgq4J0lUXR+qDe6Er245TsXmMMq3bXW0IMhnWL5td2XXeAERHnA+vUqjqLuFvs9bbv86pzVlex/7OIk4xxpYYM/o31yUygua5YSOCdYnpoxPaxFsF2t0xGEXcQJ5kgWpPXvCKXXfVzgGZqD+/4O9Cdnjhvgms4SEC3kxQ2M1ghPdSNhxvjU1u7OtbV2zavZ1mBbRqInh17B9btvtnlGLmmErPRmaaDtEqdmnnqu5BoWzrGLWiao4z69qHr+3wecR4WoXczNvkMdX32ljke65IqnTbEn4zPbEsnWT4fN9TbdizLOp+6/ZYWWAZWC9K0ScEl496G3QuDO22sHFiaN3Z/8jl2u+swJ/zixxTRiLOISKzdXixaB531e70M21CvbQPrHnuT+74V9d0pkmzRt9kuooItYiVsdRLfZILYQA67ampPuP1a4wmJKmAuWbWonXKXAzevftw8tNNbgTmRlVYBTjeuud033baBk9FJai5xjLTtatfcpzl2AIsdpHD8qyg+5ny+DGMrk3HzoI2Zq6yYt33E6eIsLzLP8tjuBZzk27cqwTuXk5p5I1BPtQM1QKcNWWQ/utCl3l7W+RTYzH6sFaRZcA1tuwfUxOA8LCIF542/c8y6+97PjL0jJTdMDV2kQtgU69oat09ExJ3CWbd3d9O34yzc6/OgYls2xrP6mZg75nnrgBMSb9G32S4iwRaxFKcyea5Kss2ZSJbVfHB/X5TaE6KrNs1KOfABUQWsQFaFP9uHQ1DosiO1dSbdMtx3WXHn1pjbarbO7Rb97sawhBAMyba2g7eSaiIcM9q1i1pFPpd1BdpS1Edpeq2zfcS9ibO+8LzbsU7B4nk2Yl31wMrkSmteW2n7rp+t8TjSZ1GJhJn9NrEfc2zfKnav0eAATbsHoNP2ta+hPeaFzkjHmOeRmusSmg6nkQq6rq1x+0REnBZivbU7A3dv1yqGf8ZxVtRrtwPnYfzRt9kuIsEWsRCnOom3F6/L6qKcAG2ibaOCxB3qgzZRuJSsWkKyubE55wioU1u79liU3rpKqs9KdWhWQNuRm9dUYhEpuPSzNk+F0CbagG4ybUufrRjliZiHu2HRe7dgm9+6VZ/rSiTLAvsxd/uu/6+IdbqfAusHmBzmEmvh/1ewe25s65R0mPf7ojEvJTVXTAs9zXS5qGCLOEuI5Nqdx1m8z+dBxTYPm5aPOG1sdI4T+jjRt9kuIsEWMRe3fcLcIrk2b+G7asHKtZyLLmJnFVXYArSdjVW2X4quMc9z7jqcC3c//bgwq5YAZtUHnYdfck3LLqczrSqsX7SIbNuCEVLRCN0WnCQ9L+LexTa6hd42bDIfLaplhu7UxUVz7lr2Y9UAzQpj9ZugaUuA5TZiXkfUmXPPG/Mqtm8FrJpGvAnWtTVun4iI24XzYH/jN+J8YxvPr638O8ufiTv1nYq+zXYRCbaIc4d5xFnbOVpn4btxtGBV8mZFFUI7vXUVh2+Zo9GILrXHvE4Uf8kY2o7RqooJBONbqrqbV7sI6Cba2tewhTRRbdYzLHfEaY+IuEdxp75up1qo+ARBgXllErrGu/IlbFLiYUnttfa45s2bS+utLRrLOrZvje3CMWxTzbGurXH7RERsG/Oaip11nLWvw1lSe23z3mxyXavss+3nd9Y+D2cJ0bfZLk4nHy/i3OOsGICTgmH+tbA5f19aD2zR713br1iYOhxX+L57tbdvv7+Wo3GC7ebdnxnHh7HGa962S9NyF7xnGJ9N9eF8fuHtFZ/HIrg6Beu8IiIiTh8nXe9tUkera34GOua1LtuxiQJshXm56zJCm7FsvEs7Ubv/z7uGDebZLjvc9Vp7zO1xrTWoDVJy195jPjaxNadpb27evInHHnsMe3t72Nvbw2OPPYZbt24t3McYg3e96114+OGH0ev18HVf93X4m7/5m8Y20+kU3//934/77rsPg8EA3/It34Knn37a//3zn/88vuu7vguPPvooer0eXvrSl+InfuInUBTFaVzmuYLB6ZMG55Vcizg7mPd5uVc4mpXM04rbnRbOkq25GxAJtohzhU0nY9bx2hpCR6P96tp2lUN2vLfIOVoby2qvtf6+apHvZeM70fgXqCE66+ksItpOAFenYJ3XaeFed3jiIj/CYVvfsnXmp63Nxe05d5ENWfWwp7Rt9wFOz+6deNtNiM0tBGK2gU1szWnam3/1r/4V/uIv/gIf/vCH8eEPfxh/8Rd/gccee2zhPv/23/5b/MzP/Ax+7ud+Dn/6p3+KK1eu4Bu/8RtxeHjot3nHO96B3/3d38X73/9+/NEf/RGOjo7wT/7JP4FSCgDwqU99Clpr/If/8B/wN3/zN/h3/+7f4T3veQ9+5Ed+5NSu9bzhdhEVd5oEWAf3CnlzXrAuyXa3Pb+F2o3bNor5OEu25m7AnV9BRJw5nIUv+ibYhrNzKlHwOfttqzjyKiq8znOtSgpuMM5V1Aftfdd+fquQbEBNtJ0S4XYncS87POd1norYPra9zFs0Z50o0HFSYmeJDelSeM1ziFdVcM/c2y2rwOaNee7hlvwdWHHM6wTGIvC3f/u3+PCHP4xf+ZVfwWte8xq85jWvwS//8i/jP//n/4xPf/rTnfsYY/DEE0/gR3/0R/HP/tk/wyte8Qr8xm/8BkajEd73vvcBAPb39/He974X/9v/9r/hG77hG/BVX/VV+K3f+i389V//NT760Y8CAN70pjfh137t1/CGN7wBL3nJS/At3/It+OEf/mH8zu/8zm27/vOA03B32x1/IyLm4SQ1c03H66ziJGPrEnvE79XdiViDLaKB8/5FP0mh6jtdQ2deHTngzkr0VzWa69z2jcfeVUeno5MdW/RsTsjEqjULga5bpHpVOIfnj//4j/HqV78aAPDLv/zLeM1rXoNPf/rT+Iqv+IqZfdoODwD8xm/8Bh588EG8733vw/d8z/d4h+c//sf/iG/4hm8AAPzWb/0WHnnkEXz0ox/FG9/4RrzpTW/Cm970Jn/cl7zkJfj0pz+NX/zFX8RP//RPb+X6zvICK+Ls4LQ/J1tRqW0DJyR+tq7aXrcGm8Vp1EU9M89oy1jX1rh9AODg4KDxfpZlyLJs47F8/OMfx97enrc1APA1X/M12Nvbw8c+9rFOe/O5z30OV69exRve8IbGOF7/+tfjYx/7GL7ne74Hn/jEJ1CWZWObhx9+GK94xSvwsY99DG984xs7x7O/v49Lly5tfD0Ry3GeybW4frgzWKWu2rr+QsTtwVnxbe4WxFBdxLnBql/lTevorI0tFkleeoiO1zzcKWdj1UjMPMXb5ifucOK6arNtCRquGOiKL7vfwcFB4zWdTk80jmUOTxeWOTwAljo883A7HZ7zttiPOB2cy+XdlruGtnGSuXTtIFOX4msDJdhtGfM27MFtVretbWsCe/PII4/40gF7e3v4qZ/6qRON5erVq3jggQdm3n/ggQdw9erVufsAwIMPPth4/8EHH/R/u3r1KtI0xcWLF+du08ZnPvMZ/Pt//+/xtre9be3riFgN55lci7iziN3fzyc29W0iuhEJtoi7Eqsuuk9c02yRI7FBuslpkmObOKPz9lk0znkS6HkE3NJrXqXI95z77Ii28HVSKG3WfgHR4YmI2CbOMrm2dBpfxzZsMGfdtiCTwxbSK2/LmM9Z+ucmtsbZm6eeegr7+/v+9fjjj3ee413vetdMQ6L268/+7M8AYKZZEUCq6K73Q7T/vso+87b54he/iDe96U345//8n+Otb33rwmPcC2jfoW3Mi5FcizgpzrJ93gbuxuvb1NZEdCOmiEZ43G2GNFx8t+eBrRNZW1y4n2aa61lqEX4idKUorZi2dFKSzaxZ3NOY2uHZ3d31789L13nXu96Ff/Nv/s3CY/7pn/4pgOjwRNybuGuWdW4u6prLToh17MhZSbG8LWOed89X2ec2Y11b4/YBgN3d3Ya9mYfv+77vw7/4F/9i4TZf9mVfhr/6q7/Cl770pZm/Pf/88zMBG4crV64AoKDNQw895N9/7rnn/D5XrlxBURS4efNmI6jz3HPP4bWvfW3jeF/84hf//+3df2xT193H8Y/5kQBd4hGyxESEwqasUEG1lIoQ/hggfk6KItGtUZUtEhMKdIjSVHTZEJUwaxUUykq1ZNLoDxVEqNqnmpC2bouSrhpSB4G0a9bSh0VCox20BJAWHFDThMb3+YPaT5w4jo99r3PtvF+SJXDOub7Xce7X3+899xytWbNG5eXlevHFF8c9NpjLlOIatyHGlor3JmPyjUki0dwG0RkV2PhDwURJ9s/YLQlEvBIpstl9jOO9fDKFwOHbGCmpw0gkeTI0ZN19mLSXSHgAO2TkVzqHCjih8+tY52k3xsV44oqtK2mPFysmcNSbaawJ9TGRn5+v/Pz8cduVl5crEAjo3LlzWr58uSTp7NmzCgQCo+JCyMKFC+Xz+dTe3q7S0lJJ0uDgoE6dOqXGxkZJ0rJlyzR9+nS1t7erqqpKknT16lWdP39eBw8eDG/rs88+05o1a7Rs2TK9+uqrmpJhCxYlw65iUqadWymyTTyKbOkj0dwG0TGCDZI4AaZaPOclp67mxxPwTOa7m7BFJWKNWBueFNlcbDNdntp0BAIJz12ckwB7JFuQsitJcnIe1YS5+LZR01gT6uOExYsXa9OmTaqtrdWRI0ckSdu2bVNFRUXEAgeLFi3SgQMHtHnzZnk8HtXV1amhoUElJSUqKSlRQ0ODZs2aperqakmS1+vV1q1btXv3bs2ZM0d5eXl66qmntHTp0vAiO59//rlWr16t+fPn69ChQ7px40b49UIXjSa7ZP5kJnIRLadRZJt4FNnSg9O5zWRDgQ1wMaducw1tyo75O0yLbLYmT/HcFhrPHG4GTOcecGqeAhIeTDZ8ncNkksg8N07Oi3PixAnt2rUrvABOZWWlmpubI9p0d3crEAiE/19fX6/+/n7t2LFDvb29KisrU1tbm3JycsJtDh8+rGnTpqmqqkr9/f1au3atjh49qqlTp0qS2tradPHiRV28eFHz5s2LeD1uU0pOJhfX4B4U2dzPLblNpqDABtef9PgTvsuJq/p2vbfj3Yo0vI3t4px7zS5uusqT6QkPV58RwucAk42bRrBJUl5enlpaWmK2GXn+93g88vv98vv9Y/aZMWOGmpqa1NTUFPXnW7Zs0ZYtW0x3F+OYLOdUvke4w8giG78Xd3FTbpMJjApsvJUAYnH01p54bglNQaHNTfMUkPBgMkin7x5uv2CF9JGKOdgwOY31MeH8BScxks293JTbZAL3Tj6BlOBEh4zimeLqOXUAIB0l+12a7+KAO0zG4pqbjm2ynwszYaXadN1vpA63iMLV3BCI7L7i4oZjcqtx3+d4bwdN40UOEGnkbQR8sZlc+OvBZOW2W0SR/iZjcQ2R3HCGGJ5XcauoO5Db2IsCG4D0Yjrnms2LHASDloIGk3uatAUAQDKPNaE+gETRYiQKOe46fops7kJuYy8KbJMYV6yQtlK8sMFwQcN5CohByeNcNTnxp+MuiY7m5veYGNNYE+oDmHwMJlN8dUshh7nI7qLIZq9k3j9yG3tRYAOQniaoyMYwagCA07hFNDUsTd5pCCbTsbpNqotsbj0zOF1kG/4eu/U9cANyG3tRYAOQvlK4emjIkGVpyCCwmLQFADczTQo5+yXONNaE+mByi/cTMFmLa24aKcVItrucKLJFe1/d9Lt3G3Ibe1FgA1KI05FDUlhoY56C1OILKIDJiDnY0kvonU+HeJUO+zhZpOI7TjqcFUYW2ULPJcLk/RzZNp7XzMTvpeQ29qLABtdy059uJp5MM1I8hbYkFzkYktk8BUNJvdrk5qZzgNNGHivnG7hVvPFwMv39OsE01oT6IDmJfN+zovx7os7hjNJJP+QYd418H5IttMUy1vsdb8Et0+Y5JLexFwU2uBZfEpAwB0e0MU8BYpno5ApIBRJC5zEH28Sxo4icKX8jpjEt2nuSCe+D0zLl8+IEu/LBRLaRyAi3dERuYy8KbMMwESKQYSZgjjbYI93PwSZflqMdK1+24XaxPqPp/veLySOdPsN2LMaQaFyKJybFGu1DPBufU+9TOg1YGOs9iPcY+JzBDSiwfY0/SIyHLwiQmAgU0fFbtl86JQUhky1ORBvdkm6/M7dikYPUcerza9f5IB0uwvDJGy0dz4uJFlEni3T8XhIPcht7pW2Bzcn7sicLtwXnaDL1RIYU80wJj2JL9vMUDFoaYiLQlLLjXGXXHGdu+m1OhltxiAHpwW2/o3T4fjMe01gT6oPJw6nPOZ+i5KXb+Sfe/Z3IoiHfB8ZGbuMuyc32nQDPiEei/cf6v13S7cSYydz0u0j2dOKmY0Fihr4OQiYPTKyxrv6b/macaD/eHD6mP0vkuNwu3c6bmfg7QOolEmuIN+6T7G8kU36jmXIcTnPqfYoVR52+3Rju57ZY09vbq5qaGnm9Xnm9XtXU1OjmzZsx+1iWJb/fr6KiIs2cOVOrV6/Wxx9/HNFmYGBAjz/+uPLz83XPPfeosrJSV65ciWizYMECeTyeiMcvf/lLo/1PaYFtrHuqk+mfyHbs7pvOJ5h0CXhueo/tKLK56XjcxNHP49fzsSX73rstCCE2u959N/0W4y3cOflItWQuzE0Up9+ndHovJoKb/mYTQYEt9Zx699Lpt5LIvqbT8UnOnzuTzXedMnIf7IipsbaRjnHbbqk69kzLbaqrq9XV1aXW1la1traqq6tLNTU1MfscPHhQzz//vJqbm9XZ2Smfz6f169fr1q1b4TZ1dXU6efKkXn/9db377ru6ffu2KioqNDQUuS7qr371K129ejX8ePrpp432P65bRK2v77MdvoOmkr2nO5nJoscTa9uxthetX7oFGSm9Tnxuen+dmlcDzn4mQ+cxK8H5A4aCMgosQ6yvYCRavEnm8zCRsSWe10hk/9x43pjoOOLG92Q4p94ftx+3G0zkZzOZeGMaa0J9EJ/Q76Tv1q2U/R2ZfhaTmdTdNH+J9zXtWFUxmdePdzvjsfN3brIfdn0nSVS0eTNTKdnjt+tznarPTirf59spjDdOxpoLFy6otbVVHR0dKisrkyS99NJLKi8vV3d3t+67775RfSzL0gsvvKC9e/fq4YcfliQdO3ZMhYWFeu2117R9+3YFAgG98sorOn78uNatWydJamlpUXFxsd5++21t3LgxvL2cnBz5fL6EjyGuAlvoC0JJSUnCLwQAbnLr1i15vV7jfqZXbhhRYIZ4AyDTJBJvEhklQLyJH7EGQCZKRbwJte3r64t4Pjs7W9nZ2UavPdKZM2fk9XrDxTVJWrFihbxer06fPh21wHbp0iX19PRow4YNEfuyatUqnT59Wtu3b9f777+vO3fuRLQpKirSkiVLdPr06YgCW2Njo5555hkVFxfrkUce0c9//nNlZWXFfQxxFdiKiop0+fJl5eTkyOOZ6OvUAJA4y7J069YtFRUVJdSfApuziDcAMkUy8YYCm7OINQAySSrjTahtcXFxxPP79u2T3+83fv3henp6VFBQMOr5goIC9fT0jNlHkgoLCyOeLyws1Keffhpuk5WVpdmzZ49qM3y7TzzxhB588EHNnj1b586d0549e3Tp0iW9/PLLcR9DXAW2KVOmaN68eXFvFADcLJGRayGstOMs4g2ATJJovGEVUWcRawBkmlTFm1CsuXz5snJzc8PPxxq95vf7tX///pjb7ezslKSoFz0syxr3YsjIn8fTZ2SbJ598MvzvBx54QLNnz9aPfvQjNTY2as6cOTG3FRJXgQ0AAAAAAADIzc2NKLDFsnPnTj366KMx2yxYsEAffvihrl27NupnN27cGDVCLSQ0X1pPT4/mzp0bfv769evhPj6fT4ODg+rt7Y0YxXb9+nWtXLlyzH1asWKFJOnixYsU2ADACUOW4TDqBBdTAABMXqaxJtQHAAATqcht8vPzlZ+fP2678vJyBQIBnTt3TsuXL5cknT17VoFAYMxC2MKFC+Xz+dTe3q7S0lJJ0uDgoE6dOqXGxkZJ0rJlyzR9+nS1t7erqqpKknT16lWdP39eBw8eHHN/PvjgA0mKKNyNhwIbABhgDjYAgNOYgw0AkApuym0WL16sTZs2qba2VkeOHJEkbdu2TRUVFRELHCxatEgHDhzQ5s2b5fF4VFdXp4aGBpWUlKikpEQNDQ2aNWuWqqurJd29fXbr1q3avXu35syZo7y8PD311FNaunRpeFXRM2fOqKOjQ2vWrJHX61VnZ6eefPJJVVZWav78+XEfAwU2ADDgpiAEAMhMFNgAAKngttzmxIkT2rVrV3jFz8rKSjU3N0e06e7uViAQCP+/vr5e/f392rFjh3p7e1VWVqa2tjbl5OSE2xw+fFjTpk1TVVWV+vv7tXbtWh09elRTp06VdHcOuTfeeEP79+/XwMCA7r33XtXW1qq+vt5o/ymwAYCBr4KWphoElq9IeAAAhkxjTagPAAAm3Jbb5OXlqaWlJWYba8Rtqh6PR36/P+YqpjNmzFBTU5Oampqi/vzBBx9UR0eH8f6ORIENAAy47SoPACDzMIINAJAK5Db2osAGAAYSXcoaAIB4mcaaUB8AAEyQ29iLAhsAGBiyLKPVc1jVDQBgyjTWhPoAAGCC3MZeFNgAwADDqAEATuMWUQBAKpDb2GvKRO8AAAAAAPfq7e1VTU2NvF6vvF6vampqdPPmzZh9LMuS3+9XUVGRZs6cqdWrV+vjjz+OaDMwMKDHH39c+fn5uueee1RZWakrV65E3d7AwIC+973vyePxqKury6YjAwDAPhTYAMBA6CqPyQMAABOJxBon4011dbW6urrU2tqq1tZWdXV1qaamJmafgwcP6vnnn1dzc7M6Ozvl8/m0fv163bp1K9ymrq5OJ0+e1Ouvv653331Xt2/fVkVFhYaGhkZtr76+XkVFRbYfGwBMZm6KNZmAAhsAGHBTEGJEAQBkJjcV2C5cuKDW1la9/PLLKi8vV3l5uV566SW99dZb6u7ujtrHsiy98MIL2rt3rx5++GEtWbJEx44d0xdffKHXXntNkhQIBPTKK6/o17/+tdatW6fS0lK1tLToo48+0ttvvx2xvb/85S9qa2vToUOHHDlGAJis3BJrMgUFNgAwMGQFNRQ0eFhBx/aFEQUAkJmMY82weNPX1xfxGBgYSGpfzpw5I6/Xq7KysvBzK1askNfr1enTp6P2uXTpknp6erRhw4bwc9nZ2Vq1alW4z/vvv687d+5EtCkqKtKSJUsitnvt2jXV1tbq+PHjmjVrVlLHAgCI5KbcJhNQYAMAA0HDKzxOLWXNiAIAyFymsWZ4vCkuLg6PbPZ6vTpw4EBS+9LT06OCgoJRzxcUFKinp2fMPpJUWFgY8XxhYWH4Zz09PcrKytLs2bPHbGNZlrZs2aLHHntMDz30UFLHAQAYzS25TaagwAYABhIdRs2IAgBAvJK5RfTy5csKBALhx549e6K+ht/vl8fjifl47733JEkej2dUf8uyoj4/3Mifx9NneJumpib19fWNeQwAgORwi6i9pk30DgBAOvkqKHkMAstXX4+iLi4ujnh+37598vv9Ce+H3SMKPv3003Ab0xEFn3zyScLHAQAYzTTWhPpIUm5urnJzc8dtv3PnTj366KMx2yxYsEAffvihrl27NupnN27cGBVPQnw+n6S7MWXu3Lnh569fvx7u4/P5NDg4qN7e3oiYc/36da1cuVKS9M4776ijo0PZ2dkR23/ooYf04x//WMeOHRv3OAEAY0s0t0F0FNgAwMBQ0NIUgyA0fETB8IRnZLIQ4vf7tX///pjb7OzslMSIAgDIVKaxJtTHRH5+vvLz88dtV15erkAgoHPnzmn58uWSpLNnzyoQCIQLYSMtXLhQPp9P7e3tKi0tlSQNDg7q1KlTamxslCQtW7ZM06dPV3t7u6qqqiRJV69e1fnz53Xw4EFJ0m9+8xs9++yz4e1+/vnn2rhxo954442IEdwAgMQkmtsgOgpsAGAg0SDEiAIAQLxSUWCL1+LFi7Vp0ybV1tbqyJEjkqRt27apoqJC9913X7jdokWLdODAAW3evFkej0d1dXVqaGhQSUmJSkpK1NDQoFmzZqm6ulqS5PV6tXXrVu3evVtz5sxRXl6ennrqKS1dulTr1q2TJM2fPz9iX77xjW9Ikr7zne9o3rx5jhwvAEwmFNjsRYENAAw4HYQYUQAAcFOBTZJOnDihXbt2hefnrKysVHNzc0Sb7u5uBQKB8P/r6+vV39+vHTt2qLe3V2VlZWpra1NOTk64zeHDhzVt2jRVVVWpv79fa9eu1dGjRzV16lTHjgUA8P8osNmLAhsApCFGFAAAUiUvL08tLS0x21hWZNLl8Xjk9/tjzjc6Y8YMNTU1qampKa79WLBgwajXAQDALSiwAYCBoOHqOU4uZc2IAgDITKaxJtQHAAATbsptMgEFNgAwMBS0jFbacXIYNSMKACAzmcaaUB8AAEy4KbfJBBTYAMCAZVmyDAILhScAgCnTWBPqAwCACXIbe1FgAwADwaBlNDSaYdQAAFOmsSbUBwAAE+Q29qLABgAGLMsyunLDVR4AgCnTWBPqAwCACXIbe1FgAwADVtBwGDVXeQAAhkxjTagPAAAmyG3sRYENAAwwjBoA4DRuEQUApAK5jb0osAGAAQMG/mEAAAxNSURBVCt492HSHgAAE6axJtQHAAAT5Db2mjLROwAAAAAAAACkM0awAYABJgIFADiNRQ4AAKlAbmMvCmwAYIB5CgAATmMONgBAKpDb2IsCGwAYYKUdAIDTWEUUAJAK5Db2osAGACZMkx6CEADAVAIFNuINAMAYuY2tKLABgIGgZcljMPdAkHkKAACGTGNNqA8AACbIbexFgQ0ADFiW4TBqghAAwJBprAn1AQDABLmNvSiwAYAB5ikAADiNOdgAAKlAbmMvCmwAYCAYlDxGK+04uDMAgIxkGmtCfQAAMEFuY68pE70DAAAAAAAAQDpjBBsAGLAsy2juAeYpAACYMo01oT4AAJggt7EXBTYAMGAF7z5M2gMAYMI01oT6AABggtzGXhTYAMBAMGgZzlPAVR4AgBnTWBPqAwCACXIbe1FgAwADrLQDAHAaq4gCAFKB3MZeFNgAwABBCADgNApsAIBUILexFwU2ADAQtCx5DCb3DDIRKADAkGmsCfUBAMAEuY29KLABgAGu8gAAnMYINgBAKpDb2IsCGwAYsCzDIMRVHgCAIdNYE+oDAIAJcht7UWADAANW0DJaPYerPAAAU6axJtQHAAAT5Db2mjLROwAAAAAAAACkMwpsAGDAsizjBwAAJhKJNU7Gm97eXtXU1Mjr9crr9aqmpkY3b94c9xj8fr+Kioo0c+ZMrV69Wh9//HFEm4GBAT3++OPKz8/XPffco8rKSl25cmXUtv70pz+prKxMM2fOVH5+vh5++GE7Dw8AJi03xZpMQIENAAyEJgI1eQAAYCKRWONkvKmurlZXV5daW1vV2tqqrq4u1dTUxOxz8OBBPf/882publZnZ6d8Pp/Wr1+vW7duhdvU1dXp5MmTev311/Xuu+/q9u3bqqio0NDQULjN73//e9XU1OinP/2p/vnPf+rvf/+7qqurHTtWAJhM3BRrMgFzsAGAgWDQkgwCi+kcOgAAmMaacB8HXLhwQa2trero6FBZWZkk6aWXXlJ5ebm6u7t13333jepjWZZeeOEF7d27Nzza7NixYyosLNRrr72m7du3KxAI6JVXXtHx48e1bt06SVJLS4uKi4v19ttva+PGjfrqq6/0xBNP6LnnntPWrVvD24/2mgAAc+Q29mIEGwAYsIJDxg+ncMsOAGSmRGJNKN709fVFPAYGBpLalzNnzsjr9YaLa5K0YsUKeb1enT59OmqfS5cuqaenRxs2bAg/l52drVWrVoX7vP/++7pz505Em6KiIi1ZsiTc5h//+Ic+++wzTZkyRaWlpZo7d65+8IMfjIpbAIDEuCm3yQQU2ADAgJuCELfsAEBmSqbAVlxcHL7w4vV6deDAgaT2paenRwUFBaOeLygoUE9Pz5h9JKmwsDDi+cLCwvDPenp6lJWVpdmzZ4/Z5t///rckye/36+mnn9Zbb72l2bNna9WqVfrvf/+b1HEBANyV22QCbhEFAANWMGgUWKxg0JH94JYdAMhcprEm1EeSLl++rNzc3PDz2dnZUdv7/X7t378/5jY7OzslSR6PZ/TrWVbU54cb+fN4+gxvE/z6mPbu3asf/vCHkqRXX31V8+bN05tvvqnt27fH3BYAIDa35DaZghFsAGDAGhoyfkjcsgMAiF8isSYUb3JzcyMeYxXYdu7cqQsXLsR8LFmyRD6fT9euXRvV/8aNG6NGqIX4fD5JGjXC7fr16+E+Pp9Pg4OD6u3tHbPN3LlzJUn3339/+OfZ2dn69re/rf/85z/jvo8AgNgSjTWIjgIbABiwLMNh1Ba37AAAzBjHmmHxJl75+flatGhRzMeMGTNUXl6uQCCgc+fOhfuePXtWgUBAK1eujLrthQsXyufzqb29Pfzc4OCgTp06Fe6zbNkyTZ8+PaLN1atXdf78+Yg22dnZ6u7uDre5c+eOPvnkE917771GxwsAGC3R3AbRUWADgBS4fPmyAoFA+LFnz56o7fx+vzweT8zHe++9J8k9t+wsW7ZMr776qjwej958882Y2wEApJfFixdr06ZNqq2tVUdHhzo6OlRbW6uKioqIqQEWLVqkkydPSrobZ+rq6tTQ0KCTJ0/q/Pnz2rJli2bNmhWer9Pr9Wrr1q3avXu3/vrXv+qDDz7QT37yEy1dujQ8RUFubq4ee+wx7du3T21tberu7tbPfvYzSdIjjzyS4ncCAOA0pxZxe/HFF7V69Wrl5ubK4/FE3WYirz0Sc7ABgAHTyT1DbUO36oxn586devTRR2O2WbBggT788MOkbtkJ3XYjjX3LzvBRbNevXw+PKOCWHQBwViITSTs58fSJEye0a9eu8PQBlZWVam5ujmjT3d2tQCAQ/n99fb36+/u1Y8cO9fb2qqysTG1tbcrJyQm3OXz4sKZNm6aqqir19/dr7dq1Onr0qKZOnRpu89xzz2natGmqqalRf3+/ysrK9M4774waaQ0AMJdobuOU6upqXblyRa2trZKkbdu2qaamRn/84x/H7BNaxO3o0aP67ne/q2effVbr169Xd3d3OOZ88cUX2rRpkzZt2jTmQIdEXnskj2VZVtytAWCS6uvrk9frVeHmX2vK9Jlx9wve6de1k7sVCATiKrDF68KFC7r//vt19uxZLV++XNLdW3ZWrFihf/3rX2MuclBUVKQnn3xS9fX1ku7eslNQUKDGxsbwIgff+ta31NLSoqqqKkl3b9mZN2+e/vznP2vjxo3q6+tTQUGBfvvb34YXObhz547mzZunZ555Rtu2bbPtOAFgMkk01kjOxRsAQOZxW24j/X9+M3wRt46ODpWXl4+b39TV1ekXv/iFJGlgYECFhYXh/Ga4v/3tb1qzZo16e3v1zW9+M6nXjoZbRAHAgFuWsuaWHQDIXInEGqdHFQAAMk+iscbuBdwk5xZxc+q1o+EWUQAw4KalrLllBwAyk2msCfUBAMBEorlNcXFxxPP79u2T3+9Pal/sXsTt008/dfS1o6HABgAGgsEhySAIBR0cUZCXl6eWlpaYbUbOAuDxeOT3+2MGwBkzZqipqUlNTU1jtpk+fboOHTqkQ4cOGe0zAGB8prEm3AcAAAOJ5jaXL1+OuEU0Ozt7zD5+v1/79++Pud3Ozk5JqV3EbbxtJLIdCmwAYMBtE4ECADKP2xY5AABkJqcXcJMmfhG3ePh8PuPXjoYCGwAYoMAGAHAaBTYAQCqkIrfJz89Xfn7+uO3Ky8sVCAR07ty5iEXcAoGAVq5cGbXPwoUL5fP51N7ertLSUkl3F3E7deqUGhsb497HRF47GgpsAGBiaEjWFIPAMkTCAwAwZBprvu4DAIARF+U2wxdxO3LkiCRp27ZtURdxO3DggDZv3hyxiFtJSYlKSkrU0NAQsYibdHeEW09Pjy5evChJ+uijj5STk6P58+crLy8v7tceD6uIAgAAAAAAYEKdOHFCS5cu1YYNG7RhwwY98MADOn78eESbaIu41dXVaceOHXrooYf02WefjVrE7Xe/+51KS0tVW1srSfr+97+v0tJS/eEPfzB67fF4rJEzYAMARunr65PX69U31+6RZ9qMuPtZX32pm389oEAgEPc8BQCAySnRWCMRbwAA8SO3cQa3iAKAASsYNFppJ7SUNQAA8TKNNeE+AAAYILexFwU2ADBgGS5lzaTTAABTprEm3AcAAAPkNvaiwAYABu5e5Yn/yg1XeQAApkxjTbgPAAAGyG3sRYENAAxwlQcA4DRGsAEAUoHcxl4U2ADAAEEIAOA0CmwAgFQgt7EXBTYAMBAMDslDEAIAOMg01kjEGwCAOXIbe1FgAwAD1lBQ8hgEoSHmKQAAmDGNNeE+AAAYILex15SJ3gEAAAAAAAAgnTGCDQAMWJbhPAUWw6gBAGZMY024DwAABsht7EWBDQAMWMEhs2HUzFMAADBkGmvCfQAAMEBuYy8KbABggCAEAHAaBTYAQCqQ29iLAhsAGLDufGkWWIbuOLczAICMZBxrJOINAMAYuY29KLABQByysrLk8/nU87//Y9zX5/MpKyvLgb0CAGSSZGKNRLwBAMSH3MYZHsuyrIneCQBIB19++aUGBweN+2VlZWnGjBkO7BEAINMkGmsk4g0AIH7kNvajwAYAAAAAAAAkYcpE7wAAAAAAAACQziiwAQAAAAAAAEmgwAYAAAAAAAAkgQIbAAAAAAAAkAQKbAAAAAAAAEASKLABAAAAAAAASaDABgAAAAAAACTh/wA8g5DOrrnyNAAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# tail-30 的 POD 模态图(每个 case 前3模态,u/v 分量)\n", + "\n", + "def plot_top3_modes_from_result(res: dict):\n", + " U = res[\"pod_U\"]\n", + " mask = res[\"fluid_mask\"]\n", + " n_fluid = int(np.sum(mask))\n", + "\n", + " fig, axes = plt.subplots(2, 3, figsize=(12.5, 6.5))\n", + " for k in range(3):\n", + " mode = U[:, k]\n", + " um = np.zeros(mask.shape, dtype=np.float32)\n", + " vm = np.zeros(mask.shape, dtype=np.float32)\n", + " um[mask] = mode[:n_fluid]\n", + " vm[mask] = mode[n_fluid:]\n", + "\n", + " lim_u = np.max(np.abs(um)) + 1e-12\n", + " lim_v = np.max(np.abs(vm)) + 1e-12\n", + "\n", + " im_u = axes[0, k].imshow(um.T, origin=\"lower\", cmap=\"RdBu_r\", vmin=-lim_u, vmax=lim_u)\n", + " axes[0, k].set_title(f\"Mode {k+1} - u\")\n", + " axes[0, k].set_xticks([])\n", + " axes[0, k].set_yticks([])\n", + " fig.colorbar(im_u, ax=axes[0, k], fraction=0.046, pad=0.04)\n", + "\n", + " im_v = axes[1, k].imshow(vm.T, origin=\"lower\", cmap=\"RdBu_r\", vmin=-lim_v, vmax=lim_v)\n", + " axes[1, k].set_title(f\"Mode {k+1} - v\")\n", + " axes[1, k].set_xticks([])\n", + " axes[1, k].set_yticks([])\n", + " fig.colorbar(im_v, ax=axes[1, k], fraction=0.046, pad=0.04)\n", + "\n", + " fig.suptitle(f\"POD spatial modes (tail-30): {res['prefix']}\", y=1.02)\n", + " plt.tight_layout()\n", + " plt.show()\n", + "\n", + "\n", + "for rr in results_tail:\n", + " plot_top3_modes_from_result(rr)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "pycuda_3_10", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/src/drl_pinball/legacy_train/erase.py b/src/drl_pinball/legacy_train/erase.py new file mode 100644 index 0000000..e99c03a --- /dev/null +++ b/src/drl_pinball/legacy_train/erase.py @@ -0,0 +1,79 @@ +# 训练d1a3o12_250729_250326_erase系列模型 +# 上游扰流圆柱,场景与Karman_cloak_standard一致, +# 但是目标是希望pinball后流场跟入口一致,即抹除扰流圆柱尾迹 +# 模型名中D代表信号延迟 +# erase模型类似re100,其余模型基于erase模型迁移训练 +import os +os.environ['MKL_THREADING_LAYER'] = 'GNU' +os.environ["OMP_NUM_THREADS"] = "8" +os.environ["MKL_NUM_THREADS"] = "8" +import torch +import numpy as np +from torch.nn import Module +import gymnasium as gym +from legacy_env.legacy_env_erase import CustomEnv +from stable_baselines3 import PPO +from stable_baselines3.common.vec_env import SubprocVecEnv +from stable_baselines3.common.vec_env import DummyVecEnv +from sb3_contrib import RecurrentPPO +from torch.utils.tensorboard import SummaryWriter +import pickle + +current_dir = os.path.dirname(os.path.abspath("__file__")) +parent_dir = os.path.abspath(os.path.join(current_dir, os.pardir)) + +class Sin(Module): + def __init__(self): + super().__init__() + + def forward(self, x): + return torch.sin(x) + +if __name__ == '__main__': + + vec_env = CustomEnv(device_id=2) + name = "d1a3o12_250729_250326_erase_250804_20D_retrain2" + + model = PPO.load(os.path.join(parent_dir, "models", "250729", "d1a3o12_250729_250326_erase_250804_20D.zip"), env=vec_env, device=torch.device("cuda:2")) + + # model = PPO( + # "MlpPolicy", + # policy_kwargs=dict(activation_fn=Sin), + # env=vec_env, + # device=torch.device("cuda:1"), + # # n_steps=3000, + # # batch_size=300, + # verbose=0) + + writer = SummaryWriter(log_dir=os.path.join(parent_dir, "tensorboard", name)) + max_reward = 0 + + history_data = [] + + for i in range(500): + model.learn(total_timesteps=400) + test_env = model.get_env() + test_obs = test_env.reset() + list_reward = [] + episolde_data = {'actions': [], 'observations': [], 'rewards': []} + + for step in range(200): + test_action, _states = model.predict(observation=test_obs) + test_obs, test_rewards, test_dones, info = test_env.step(test_action) + list_reward.append(test_rewards) + episolde_data['actions'].append(test_action[0, :]) + episolde_data['observations'].append(np.array(test_obs)) + episolde_data['rewards'].append(test_rewards) + + history_data.append(episolde_data) + + avg_reward = np.mean(list_reward[-100:]) + writer.add_scalar('Reward', np.mean(avg_reward), i) + if avg_reward > max_reward: + max_reward = avg_reward + model.save(os.path.join(parent_dir, "models", "250729", name + ".zip")) + # if i % 10 == 0: + # model.save(os.path.join(parent_dir, "models", "250329", name + f"_{i}.zip")) + + # with open(os.path.join(parent_dir, "output", name + ".pkl"), 'wb') as f: + # pickle.dump(history_data, f) \ No newline at end of file diff --git a/src/drl_pinball/legacy_train/imit.py b/src/drl_pinball/legacy_train/imit.py new file mode 100644 index 0000000..231479e --- /dev/null +++ b/src/drl_pinball/legacy_train/imit.py @@ -0,0 +1,77 @@ +# 训练d1a3o14_250525_imit系列模型 +# 上游干净来流,目标是pinball后流场跟设定尺寸圆柱一致 +# 模型名中L代表目标直径,S代表SAMPLE_INTERVAL +import os +os.environ['MKL_THREADING_LAYER'] = 'GNU' +os.environ["OMP_NUM_THREADS"] = "8" +os.environ["MKL_NUM_THREADS"] = "8" +import torch +import numpy as np +from torch.nn import Module +import gymnasium as gym +from legacy_env.legacy_env_imit import CustomEnv +from stable_baselines3 import PPO +from stable_baselines3.common.vec_env import SubprocVecEnv +from stable_baselines3.common.vec_env import DummyVecEnv +from sb3_contrib import RecurrentPPO +from torch.utils.tensorboard import SummaryWriter +import pickle + +current_dir = os.path.dirname(os.path.abspath("__file__")) +parent_dir = os.path.abspath(os.path.join(current_dir, os.pardir)) + +class Sin(Module): + def __init__(self): + super().__init__() + + def forward(self, x): + return torch.sin(x) + +if __name__ == '__main__': + + vec_env = CustomEnv(device_id=1) + name = "d1a3o14_250525_imit_1L_2U_1000S_08Vis" + + model = PPO.load(os.path.join(parent_dir, "models", "250525", "d1a3o14_250525_imit_1L_2U_600S"), env=vec_env, device=torch.device("cuda:1")) + + # model = PPO( + # "MlpPolicy", + # policy_kwargs=dict(activation_fn=Sin), + # env=vec_env, + # device=torch.device("cuda:2"), + # # n_steps=3000, + # # batch_size=300, + # verbose=0) + + writer = SummaryWriter(log_dir=os.path.join(parent_dir, "tensorboard", name)) + max_reward = 0 + + history_data = [] + + for i in range(500): + model.learn(total_timesteps=400) + test_env = model.get_env() + test_obs = test_env.reset() + list_reward = [] + episolde_data = {'actions': [], 'observations': [], 'rewards': []} + + for step in range(300): + test_action, _states = model.predict(observation=test_obs) + test_obs, test_rewards, test_dones, info = test_env.step(test_action) + list_reward.append(test_rewards) + episolde_data['actions'].append(test_action[0, :]) + episolde_data['observations'].append(np.array(test_obs)) + episolde_data['rewards'].append(test_rewards) + + history_data.append(episolde_data) + + avg_reward = np.mean(list_reward[-100:]) + writer.add_scalar('Reward', np.mean(avg_reward), i) + if avg_reward > max_reward: + max_reward = avg_reward + model.save(os.path.join(parent_dir, "models", "250525", name + ".zip")) + # if i % 10 == 0: + # model.save(os.path.join(parent_dir, "models", "250421", name + f"_{i}.zip")) + + with open(os.path.join(parent_dir, "output", name + ".pkl"), 'wb') as f: + pickle.dump(history_data, f) \ No newline at end of file diff --git a/src/drl_pinball/legacy_train/karman_cloak.py b/src/drl_pinball/legacy_train/karman_cloak.py new file mode 100644 index 0000000..1d8a05d --- /dev/null +++ b/src/drl_pinball/legacy_train/karman_cloak.py @@ -0,0 +1,77 @@ +# 训练d1a3o12_250326模型,用于训练和评估d1a3o12_re系列模型和250326模型 +# re100应等同于250326模型,不同雷诺数使用不同粘性实现 +# re系列都基于re100模型迁移训练,250326模型直接训练 +import os +os.environ['MKL_THREADING_LAYER'] = 'GNU' +os.environ["OMP_NUM_THREADS"] = "8" +os.environ["MKL_NUM_THREADS"] = "8" +import torch +import numpy as np +from torch.nn import Module +import gymnasium as gym +from legacy_env.legacy_env_karman_cloak_standard import CustomEnv +from stable_baselines3 import PPO +from stable_baselines3.common.vec_env import SubprocVecEnv +from stable_baselines3.common.vec_env import DummyVecEnv +from sb3_contrib import RecurrentPPO +from torch.utils.tensorboard import SummaryWriter +import pickle + +current_dir = os.path.dirname(os.path.abspath("__file__")) +parent_dir = os.path.abspath(os.path.join(current_dir, os.pardir)) + +class Sin(Module): + def __init__(self): + super().__init__() + + def forward(self, x): + return torch.sin(x) + +if __name__ == '__main__': + + vec_env = CustomEnv(device_id=3) + name = "d1a3o12_250326" + + # model = PPO.load(os.path.join(parent_dir, "models", "d1a3o12_c0"), env=vec_env, device=torch.device("cuda:1")) + + model = PPO( + "MlpPolicy", + policy_kwargs=dict(activation_fn=Sin), + env=vec_env, + device=torch.device("cuda:3"), + # n_steps=3000, + # batch_size=300, + verbose=0) + + writer = SummaryWriter(log_dir=os.path.join(parent_dir, "tensorboard", name)) + max_reward = 0 + + history_data = [] + + for i in range(500): + model.learn(total_timesteps=360) + test_env = model.get_env() + test_obs = test_env.reset() + list_reward = [] + episolde_data = {'actions': [], 'observations': [], 'rewards': []} + + for step in range(360): + test_action, _states = model.predict(observation=test_obs) + test_obs, test_rewards, test_dones, info = test_env.step(test_action) + list_reward.append(test_rewards) + episolde_data['actions'].append(test_action[0, :]) + episolde_data['observations'].append(np.array(test_obs)) + episolde_data['rewards'].append(test_rewards) + + history_data.append(episolde_data) + + avg_reward = np.mean(list_reward[-180:]) + writer.add_scalar('Reward', np.mean(avg_reward), i) + if avg_reward > max_reward: + max_reward = avg_reward + model.save(os.path.join(parent_dir, "models", "250326", name + ".zip")) + # if i % 10 == 0: + # model.save(os.path.join(parent_dir, "models", "250326", name + f"_{i}.zip")) + + with open(os.path.join(parent_dir, "output", name + ".pkl"), 'wb') as f: + pickle.dump(history_data, f) \ No newline at end of file diff --git a/src/drl_pinball/legacy_train/reduce_obs.py b/src/drl_pinball/legacy_train/reduce_obs.py new file mode 100644 index 0000000..9f3ef5b --- /dev/null +++ b/src/drl_pinball/legacy_train/reduce_obs.py @@ -0,0 +1,77 @@ +# 训练d1a3o12_250421系列模型 +# 上游扰流圆柱,场景与Karman_cloak_standard一致 +# obs从12逐渐减少至2,观察模型是否能够适应,具体观察量在模型名中体现 +import os +os.environ['MKL_THREADING_LAYER'] = 'GNU' +os.environ["OMP_NUM_THREADS"] = "8" +os.environ["MKL_NUM_THREADS"] = "8" +import torch +import numpy as np +from torch.nn import Module +import gymnasium as gym +from legacy_env.legacy_env_reduce_obs import CustomEnv +from stable_baselines3 import PPO +from stable_baselines3.common.vec_env import SubprocVecEnv +from stable_baselines3.common.vec_env import DummyVecEnv +from sb3_contrib import RecurrentPPO +from torch.utils.tensorboard import SummaryWriter +import pickle + +current_dir = os.path.dirname(os.path.abspath("__file__")) +parent_dir = os.path.abspath(os.path.join(current_dir, os.pardir)) + +class Sin(Module): + def __init__(self): + super().__init__() + + def forward(self, x): + return torch.sin(x) + +if __name__ == '__main__': + + vec_env = CustomEnv(device_id=3) + name = "d1a3o12_250421_total_force" + + # model = PPO.load(os.path.join(parent_dir, "models", "d1a3o12_c0"), env=vec_env, device=torch.device("cuda:1")) + + model = PPO( + "MlpPolicy", + policy_kwargs=dict(activation_fn=Sin), + env=vec_env, + device=torch.device("cuda:3"), + # n_steps=3000, + # batch_size=300, + verbose=0) + + writer = SummaryWriter(log_dir=os.path.join(parent_dir, "tensorboard", name)) + max_reward = 0 + + history_data = [] + + for i in range(500): + model.learn(total_timesteps=400) + test_env = model.get_env() + test_obs = test_env.reset() + list_reward = [] + episolde_data = {'actions': [], 'observations': [], 'rewards': []} + + for step in range(300): + test_action, _states = model.predict(observation=test_obs) + test_obs, test_rewards, test_dones, info = test_env.step(test_action) + list_reward.append(test_rewards) + episolde_data['actions'].append(test_action[0, :]) + episolde_data['observations'].append(np.array(test_obs)) + episolde_data['rewards'].append(test_rewards) + + history_data.append(episolde_data) + + avg_reward = np.mean(list_reward[-100:]) + writer.add_scalar('Reward', np.mean(avg_reward), i) + if avg_reward > max_reward: + max_reward = avg_reward + model.save(os.path.join(parent_dir, "models", "250421", name + ".zip")) + # if i % 10 == 0: + # model.save(os.path.join(parent_dir, "models", "250421", name + f"_{i}.zip")) + + with open(os.path.join(parent_dir, "output", name + ".pkl"), 'wb') as f: + pickle.dump(history_data, f) \ No newline at end of file diff --git a/src/drl_pinball/legacy_train/vortex.py b/src/drl_pinball/legacy_train/vortex.py new file mode 100644 index 0000000..19dc2e6 --- /dev/null +++ b/src/drl_pinball/legacy_train/vortex.py @@ -0,0 +1,75 @@ +# 训练vortex模型,基于d1a3o12_re100模型,训练vortex_taylor和vortex_lamb模型 +# 上游干净来流,目标是vortex流过的时序信号于无pinball情况一致 +# vortes系列模型都基于d1a3o12_re100模型迁移训练 +import os +os.environ['MKL_THREADING_LAYER'] = 'GNU' +os.environ["OMP_NUM_THREADS"] = "16" +os.environ["MKL_NUM_THREADS"] = "16" +import torch +import numpy as np +from torch.nn import Module +import gymnasium as gym +from legacy_env.legacy_env_vortex import CustomEnv +from stable_baselines3 import PPO +from stable_baselines3.common.vec_env import SubprocVecEnv +from stable_baselines3.common.vec_env import DummyVecEnv +from sb3_contrib import RecurrentPPO +from torch.utils.tensorboard import SummaryWriter +import pickle + +current_dir = os.path.dirname(os.path.abspath("__file__")) +parent_dir = os.path.abspath(os.path.join(current_dir, os.pardir)) + +class Sin(Module): + def __init__(self): + super().__init__() + + def forward(self, x): + return torch.sin(x) + +if __name__ == '__main__': + + vec_env = CustomEnv(device_id=3) + name = "vortex_taylor" + + model = PPO.load(os.path.join(parent_dir, "models", "d1a3o12_re100"), env=vec_env, device=torch.device("cuda:3")) + + # model = PPO( + # "MlpPolicy", + # policy_kwargs=dict(activation_fn=Sin), + # env=vec_env, + # device=torch.device("cuda:3"), + # n_steps=3600, + # batch_size=360, + # verbose=0) + + writer = SummaryWriter(log_dir=os.path.join(parent_dir, "tensorboard", name)) + max_reward = 0 + + history_data = [] + + for i in range(100): + model.learn(total_timesteps=1500) + test_env = model.get_env() + test_obs = test_env.reset() + list_reward = [] + # episolde_data = {'actions': [], 'observations': [], 'rewards': []} + + for step in range(150): + test_action, _states = model.predict(observation=test_obs) + test_obs, test_rewards, test_dones, info = test_env.step(test_action) + list_reward.append(test_rewards) + # episolde_data['actions'].append(test_action[0, :]) + # episolde_data['observations'].append(np.array(test_obs)) + # episolde_data['rewards'].append(test_rewards) + + # history_data.append(episolde_data) + + avg_reward = np.mean(list_reward[-130:]) + writer.add_scalar('Reward', np.mean(avg_reward), i) + if avg_reward > max_reward: + max_reward = avg_reward + model.save(os.path.join(parent_dir, "models", name + ".zip")) + + # with open(os.path.join(parent_dir, "output", name + ".pkl"), 'wb') as f: + # pickle.dump(history_data, f) \ No newline at end of file diff --git a/src/environments/__init__.py b/src/environments/__init__.py deleted file mode 100644 index dfde36d..0000000 --- a/src/environments/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -""" -Gymnasium environments for CFD control tasks. -""" - -from .cfd_env import CFDFlowControlEnv - -__all__ = ['CFDFlowControlEnv'] diff --git a/src/environments/cfd_env.py b/src/environments/cfd_env.py deleted file mode 100644 index fea191f..0000000 --- a/src/environments/cfd_env.py +++ /dev/null @@ -1,328 +0,0 @@ -""" -CFD Flow Control Environment using CelerisLab. - -A Gymnasium environment for active flow control using lattice Boltzmann simulation. -""" - -import os -from collections import deque -from typing import Optional, Tuple, Dict, Any - -import gymnasium as gym -import numpy as np -from gymnasium import spaces - -# Set threading to avoid conflicts with GPU -os.environ["OMP_NUM_THREADS"] = "1" -os.environ["MKL_NUM_THREADS"] = "1" - - -class CFDFlowControlEnv(gym.Env): - """ - CFD flow control environment with cylinder and sensors. - - The environment simulates flow around a cylinder with multiple control cylinders - and sensors to measure flow properties. The agent controls the cylinder velocities - to optimize flow characteristics. - - Args: - device_id: CUDA device ID to use for simulation - config_cuda: CelerisLab CUDA configuration (optional, will load from config if None) - config_field: CelerisLab flow field configuration (optional, will load from config if None) - n_control_cylinders: Number of controllable cylinders (default: 3) - n_sensors: Number of flow sensors (default: 3) - max_steps: Maximum steps per episode (default: 500) - sample_interval: Simulation steps between observations (default: 800) - fifo_length: Length of state history (default: 120) - convergence_length: Steps to check for convergence (default: 60) - warmup_steps_factor: Multiple of grid size for warmup (default: 4) - """ - - metadata = {"render_modes": ["human"], "render_fps": 30} - - def __init__( - self, - device_id: int = 0, - config_cuda = None, - config_field = None, - n_control_cylinders: int = 3, - n_sensors: int = 3, - max_steps: int = 500, - sample_interval: int = 800, - fifo_length: int = 120, - convergence_length: int = 60, - warmup_steps_factor: int = 4, - ): - super().__init__() - - # Load configurations if not provided - if config_cuda is None or config_field is None: - from ..config import load_celeris_configs - config_cuda, config_field = load_celeris_configs() - - self.config_cuda = config_cuda - self.config_field = config_field - self.device_id = device_id - - # Environment parameters - self.n_control = n_control_cylinders - self.n_sensors = n_sensors - self.max_steps = max_steps - self.sample_interval = sample_interval - self.fifo_length = fifo_length - self.convergence_length = convergence_length - self.warmup_steps_factor = warmup_steps_factor - - # Determine data type - if config_field.data_type == "FP32": - self.dtype = np.float32 - elif config_field.data_type == "FP64": - self.dtype = np.float64 - else: - raise ValueError(f"Unsupported data type: {config_field.data_type}") - - # Action and observation dimensions - # Action: velocity control for n cylinders (x, y, rotation) - self.action_dim = n_control_cylinders - # Observation: sensor readings (u, v) from n sensors - self.obs_dim = n_sensors * 2 * 2 # 2 velocity components × 2 (current + derivative) - - # Gym spaces - self.action_space = spaces.Box( - low=-1.0, - high=1.0, - shape=(self.action_dim,), - dtype=self.dtype - ) - self.observation_space = spaces.Box( - low=-np.inf, - high=np.inf, - shape=(self.obs_dim,), - dtype=self.dtype - ) - - # State tracking - self.fifo_states = deque(maxlen=fifo_length) - self.target_states = np.empty((0, self.n_sensors * 2), dtype=self.dtype) - self.current_step = 0 - - # Normalization factors (will be set during warmup) - self.sens_norm_fact = np.ones(self.n_sensors * 2, dtype=self.dtype) - self.sens_deviation = np.zeros(self.n_sensors * 2, dtype=self.dtype) - - # Reward tracking - self.reward_cd = 0.0 - self.reward_cl = 0.0 - self.reward_sim = 0.0 - - # Initialize flow field - self._init_flow_field() - - def _init_flow_field(self): - """Initialize the CelerisLab flow field simulation.""" - from CelerisLab import FlowField - - self.flow_field = FlowField( - self.config_field, - self.config_cuda, - self.device_id - ) - - # Get grid parameters - L0 = 20 # Characteristic length - U0 = self.config_field.velocity - NX = self.flow_field.FIELD_SHAPE[0] - NY = self.flow_field.FIELD_SHAPE[1] - - # Add main cylinder (obstacle) - center = (10 * L0, (NY - 1) / 2, 0) - self.flow_field.add_cylinder(center, L0) - - # Add sensors - sensor_y_positions = [ - (NY - 1) / 2 + 2 * L0, # Above centerline - (NY - 1) / 2, # At centerline - (NY - 1) / 2 - 2 * L0, # Below centerline - ] - for i in range(min(self.n_sensors, len(sensor_y_positions))): - center = (40 * L0, sensor_y_positions[i], 0) - self.flow_field.add_sensor(center, L0 / 4) - - # Warmup simulation - warmup_steps = int(self.warmup_steps_factor * NX / U0) - self.flow_field.run(warmup_steps, np.zeros(self.n_control + 1, dtype=self.dtype)) - - # Collect baseline states for normalization - for _ in range(self.fifo_length): - self.flow_field.run( - self.sample_interval, - np.zeros(self.n_control + 1, dtype=self.dtype) - ) - new_state = self.flow_field.obs.copy()[2:2 + self.n_sensors * 2] - self.target_states = np.vstack((self.target_states, new_state)) - self.fifo_states.append(new_state) - - # Calculate normalization factors - self._calculate_normalization() - - def _calculate_normalization(self): - """Calculate normalization factors from baseline states.""" - if len(self.target_states) > 0: - self.sens_norm_fact = np.std(self.target_states, axis=0) + 1e-6 - self.sens_deviation = np.mean(self.target_states, axis=0) - - def _normalize_state(self, state: np.ndarray) -> np.ndarray: - """Normalize state using calculated factors.""" - return (state - self.sens_deviation) / self.sens_norm_fact - - def _compute_reward(self, state: np.ndarray, action: np.ndarray) -> float: - """ - Compute reward based on drag reduction and flow similarity. - - Args: - state: Current state observation - action: Applied action - - Returns: - Total reward - """ - # Get force measurements from simulation - obs = self.flow_field.obs - cd = obs[0] # Drag coefficient - cl = obs[1] # Lift coefficient - - # Drag reduction reward (negative drag is good) - self.reward_cd = -cd * 0.1 - - # Lift minimization (want symmetric flow) - self.reward_cl = -abs(cl) * 0.05 - - # Flow similarity to baseline (want smooth control) - if len(self.fifo_states) >= self.convergence_length: - recent_states = np.array(list(self.fifo_states)[-self.convergence_length:]) - target_recent = self.target_states[-self.convergence_length:] - - # Dynamic Time Warping distance (simplified) - diff = np.mean(np.abs(recent_states - target_recent)) - self.reward_sim = -diff * 0.5 - else: - self.reward_sim = 0.0 - - # Total reward - total_reward = self.reward_cd + self.reward_cl + self.reward_sim - - return float(total_reward) - - def reset( - self, - seed: Optional[int] = None, - options: Optional[Dict[str, Any]] = None - ) -> Tuple[np.ndarray, Dict[str, Any]]: - """ - Reset the environment to initial state. - - Args: - seed: Random seed for reproducibility - options: Additional options - - Returns: - Tuple of (observation, info) - """ - super().reset(seed=seed) - - self.current_step = 0 - self.fifo_states.clear() - - # Run a few steps to get initial state - for _ in range(10): - self.flow_field.run( - self.sample_interval, - np.zeros(self.n_control + 1, dtype=self.dtype) - ) - state = self.flow_field.obs.copy()[2:2 + self.n_sensors * 2] - self.fifo_states.append(state) - - # Get current state - current_state = self.fifo_states[-1] - - # Compute state derivative (approximation) - if len(self.fifo_states) >= 2: - state_derivative = self.fifo_states[-1] - self.fifo_states[-2] - else: - state_derivative = np.zeros_like(current_state) - - # Normalize and concatenate - obs = np.concatenate([ - self._normalize_state(current_state), - self._normalize_state(state_derivative) - ]).astype(self.dtype) - - info = { - 'step': self.current_step, - 'cd': self.flow_field.obs[0], - 'cl': self.flow_field.obs[1], - } - - return obs, info - - def step(self, action: np.ndarray) -> Tuple[np.ndarray, float, bool, bool, Dict[str, Any]]: - """ - Take a step in the environment. - - Args: - action: Action to take (cylinder velocities) - - Returns: - Tuple of (observation, reward, terminated, truncated, info) - """ - # Convert action to control input - # Action is in [-1, 1], scale to appropriate velocity range - control = np.zeros(self.n_control + 1, dtype=self.dtype) - control[:self.n_control] = action * 0.1 * self.config_field.velocity - - # Run simulation - self.flow_field.run(self.sample_interval, control) - - # Get new state - new_state = self.flow_field.obs.copy()[2:2 + self.n_sensors * 2] - self.fifo_states.append(new_state) - - # Compute observation - if len(self.fifo_states) >= 2: - state_derivative = self.fifo_states[-1] - self.fifo_states[-2] - else: - state_derivative = np.zeros_like(new_state) - - obs = np.concatenate([ - self._normalize_state(new_state), - self._normalize_state(state_derivative) - ]).astype(self.dtype) - - # Compute reward - reward = self._compute_reward(new_state, action) - - # Check termination - self.current_step += 1 - terminated = False # CFD simulations typically don't have natural termination - truncated = self.current_step >= self.max_steps - - # Info - info = { - 'step': self.current_step, - 'cd': float(self.flow_field.obs[0]), - 'cl': float(self.flow_field.obs[1]), - 'reward_cd': float(self.reward_cd), - 'reward_cl': float(self.reward_cl), - 'reward_sim': float(self.reward_sim), - } - - return obs, reward, terminated, truncated, info - - def render(self): - """Render the environment (not implemented).""" - pass - - def close(self): - """Clean up resources.""" - if hasattr(self, 'flow_field'): - del self.flow_field