diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..b15ef57040eaf9304c52eee2889b75a5e4d724e6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,18 @@ +FROM python:3.10 + +RUN useradd -m -u 1000 user && python -m pip install --upgrade pip +USER user +ENV PATH="/home/user/.local/bin:$PATH" + +WORKDIR /app + +COPY --chown=user ./requirements.txt requirements.txt +RUN pip install --no-cache-dir --upgrade -r requirements.txt + +COPY --chown=user . /app +ENV MCP_TRANSPORT=http +ENV MCP_PORT=7860 + +EXPOSE 7860 + +CMD ["python", "PySDM/mcp_output/start_mcp.py"] diff --git a/PySDM/mcp_output/README_MCP.md b/PySDM/mcp_output/README_MCP.md new file mode 100644 index 0000000000000000000000000000000000000000..7de83fa719a1fc4b87969095206f8179a5703ff8 --- /dev/null +++ b/PySDM/mcp_output/README_MCP.md @@ -0,0 +1,52 @@ +# PySDM: Pythonic Particle-Based Cloud Microphysics Package + +## Project Introduction + +PySDM is a Pythonic particle-based (super-droplet) cloud microphysics package designed for simulating warm-rain and aqueous-chemistry processes. It provides a comprehensive framework for modeling cloud microphysics with support for box, parcel, and 1D/2D prescribed-flow scenarios. The package is implemented in Python, with examples available in Julia and Matlab, making it versatile for various scientific and research applications. + +## Installation Method + +To install PySDM, ensure you have Python installed on your system. The package requires the following dependencies: + +- Required: numpy, scipy, numba +- Optional: matplotlib, netCDF4 + +You can install PySDM and its dependencies using pip: + +``` +pip install numpy scipy numba +pip install matplotlib netCDF4 # Optional +``` + +## Quick Start + +To get started with PySDM, you can use the `Particulator` class, which serves as the main entry point for handling particle-based cloud microphysics. Here is a simple example to demonstrate how to use PySDM: + +``` +from PySDM import Particulator + +# Initialize the Particulator +particulator = Particulator() + +# Run a simulation (example) +particulator.run_simulation() +``` + +For more detailed examples, refer to the examples provided in the repository. + +## Available Tools and Endpoints List + +- **Particulator**: Main class for handling particle-based cloud microphysics. +- **Dynamics Module**: Functions for handling cloud microphysics dynamics such as condensation, freezing, and displacement. +- **Backends**: Provides backend implementations for computational efficiency using Numba and ThrustRTC. +- **Exporters**: Functions to export simulation data to formats like NetCDF and VTK. + +## Common Issues and Notes + +- Ensure all required dependencies are installed before running simulations. +- The package is computationally intensive; using the Numba or ThrustRTC backends can improve performance. +- If you encounter issues with optional dependencies, ensure they are installed if needed for specific functionalities like plotting or data export. + +## Reference Links or Documentation + +For more information, visit the [PySDM GitHub Repository](https://github.com/open-atmos/PySDM). The repository contains detailed documentation, examples, and additional resources to help you get the most out of PySDM. \ No newline at end of file diff --git a/PySDM/mcp_output/analysis.json b/PySDM/mcp_output/analysis.json new file mode 100644 index 0000000000000000000000000000000000000000..ff563e7fb0fe0b9fa1611b855b0419245305fc9a --- /dev/null +++ b/PySDM/mcp_output/analysis.json @@ -0,0 +1,2822 @@ +{ + "summary": { + "repository_url": "https://github.com/open-atmos/PySDM", + "summary": "Imported via zip fallback, file count: 899", + "file_tree": { + ".binder/apt.txt": { + "size": 7 + }, + ".binder/requirements.txt": { + "size": 15 + }, + ".codecov.yml": { + "size": 72 + }, + ".github/actions/env-setup/action.yml": { + "size": 2080 + }, + ".github/actions/set-pip-path-and-cache-key/action.yml": { + "size": 673 + }, + ".github/dependabot.yml": { + "size": 176 + }, + ".github/workflows/cancel.yml": { + "size": 284 + }, + ".github/workflows/pdoc.yml": { + "size": 1046 + }, + ".github/workflows/precommit.yml": { + "size": 1147 + }, + ".github/workflows/pypi.yml": { + "size": 2979 + }, + ".github/workflows/readme_snippets.yml": { + "size": 3187 + }, + ".github/workflows/stale.yml": { + "size": 802 + }, + ".github/workflows/tests.yml": { + "size": 14187 + }, + ".github/workflows/urlcheck.yml": { + "size": 1275 + }, + ".pre-commit-config.yaml": { + "size": 893 + }, + ".zenodo.json": { + "size": 4962 + }, + "PySDM/__init__.py": { + "size": 504 + }, + "PySDM/attributes/__init__.py": { + "size": 171 + }, + "PySDM/attributes/chemistry/__init__.py": { + "size": 223 + }, + "PySDM/attributes/chemistry/acidity.py": { + "size": 1642 + }, + "PySDM/attributes/chemistry/concentration.py": { + "size": 782 + }, + "PySDM/attributes/chemistry/hydrogen_ion_concentration.py": { + "size": 475 + }, + "PySDM/attributes/ice/__init__.py": { + "size": 222 + }, + "PySDM/attributes/ice/cooling_rate.py": { + "size": 1320 + }, + "PySDM/attributes/ice/freezing_temperature.py": { + "size": 1428 + }, + "PySDM/attributes/ice/immersed_surface_area.py": { + "size": 376 + }, + "PySDM/attributes/impl/__init__.py": { + "size": 608 + }, + "PySDM/attributes/impl/attribute.py": { + "size": 1156 + }, + "PySDM/attributes/impl/attribute_registry.py": { + "size": 1897 + }, + "PySDM/attributes/impl/base_attribute.py": { + "size": 470 + }, + "PySDM/attributes/impl/cell_attribute.py": { + "size": 220 + }, + "PySDM/attributes/impl/derived_attribute.py": { + "size": 844 + }, + "PySDM/attributes/impl/dummy_attribute.py": { + "size": 418 + }, + "PySDM/attributes/impl/extensive_attribute.py": { + "size": 224 + }, + "PySDM/attributes/impl/intensive_attribute.py": { + "size": 548 + }, + "PySDM/attributes/impl/maximum_attribute.py": { + "size": 391 + }, + "PySDM/attributes/impl/mole_amount.py": { + "size": 410 + }, + "PySDM/attributes/impl/temperature_variation_option_attribute.py": { + "size": 755 + }, + "PySDM/attributes/isotopes/__init__.py": { + "size": 134 + }, + "PySDM/attributes/isotopes/delta.py": { + "size": 1565 + }, + "PySDM/attributes/isotopes/moles.py": { + "size": 3404 + }, + "PySDM/attributes/numerics/__init__.py": { + "size": 202 + }, + "PySDM/attributes/numerics/cell_id.py": { + "size": 288 + }, + "PySDM/attributes/numerics/cell_origin.py": { + "size": 379 + }, + "PySDM/attributes/numerics/position_in_cell.py": { + "size": 405 + }, + "PySDM/attributes/physics/__init__.py": { + "size": 855 + }, + "PySDM/attributes/physics/area.py": { + "size": 574 + }, + "PySDM/attributes/physics/critical_saturation.py": { + "size": 1931 + }, + "PySDM/attributes/physics/critical_volume.py": { + "size": 2878 + }, + "PySDM/attributes/physics/diffusional_growth_mass_change.py": { + "size": 1152 + }, + "PySDM/attributes/physics/dry_radius.py": { + "size": 541 + }, + "PySDM/attributes/physics/dry_volume.py": { + "size": 2003 + }, + "PySDM/attributes/physics/equilibrium_saturation.py": { + "size": 1396 + }, + "PySDM/attributes/physics/heat.py": { + "size": 314 + }, + "PySDM/attributes/physics/hygroscopicity.py": { + "size": 931 + }, + "PySDM/attributes/physics/multiplicity.py": { + "size": 471 + }, + "PySDM/attributes/physics/radius.py": { + "size": 913 + }, + "PySDM/attributes/physics/relative_fall_velocity.py": { + "size": 1258 + }, + "PySDM/attributes/physics/reynolds_number.py": { + "size": 1081 + }, + "PySDM/attributes/physics/temperature.py": { + "size": 340 + }, + "PySDM/attributes/physics/terminal_velocity.py": { + "size": 1577 + }, + "PySDM/attributes/physics/volume.py": { + "size": 587 + }, + "PySDM/attributes/physics/water_mass.py": { + "size": 1604 + }, + "PySDM/backends/__init__.py": { + "size": 2801 + }, + "PySDM/backends/impl_common/__init__.py": { + "size": 35 + }, + "PySDM/backends/impl_common/backend_methods.py": { + "size": 560 + }, + "PySDM/backends/impl_common/freezing_attributes.py": { + "size": 1253 + }, + "PySDM/backends/impl_common/index.py": { + "size": 1765 + }, + "PySDM/backends/impl_common/indexed_storage.py": { + "size": 1723 + }, + "PySDM/backends/impl_common/pair_indicator.py": { + "size": 567 + }, + "PySDM/backends/impl_common/pairwise_storage.py": { + "size": 1383 + }, + "PySDM/backends/impl_common/random_common.py": { + "size": 282 + }, + "PySDM/backends/impl_common/storage_utils.py": { + "size": 1958 + }, + "PySDM/backends/impl_numba/__init__.py": { + "size": 34 + }, + "PySDM/backends/impl_numba/atomic_operations.py": { + "size": 4941 + }, + "PySDM/backends/impl_numba/conf.py": { + "size": 216 + }, + "PySDM/backends/impl_numba/methods/__init__.py": { + "size": 716 + }, + "PySDM/backends/impl_numba/methods/chemistry_methods.py": { + "size": 15451 + }, + "PySDM/backends/impl_numba/methods/collisions_methods.py": { + "size": 26515 + }, + "PySDM/backends/impl_numba/methods/condensation_methods.py": { + "size": 25995 + }, + "PySDM/backends/impl_numba/methods/deposition_methods.py": { + "size": 14180 + }, + "PySDM/backends/impl_numba/methods/displacement_methods.py": { + "size": 7853 + }, + "PySDM/backends/impl_numba/methods/fragmentation_methods.py": { + "size": 17127 + }, + "PySDM/backends/impl_numba/methods/freezing_methods.py": { + "size": 10343 + }, + "PySDM/backends/impl_numba/methods/index_methods.py": { + "size": 1427 + }, + "PySDM/backends/impl_numba/methods/isotope_methods.py": { + "size": 805 + }, + "PySDM/backends/impl_numba/methods/moments_methods.py": { + "size": 5512 + }, + "PySDM/backends/impl_numba/methods/pair_methods.py": { + "size": 6259 + }, + "PySDM/backends/impl_numba/methods/physics_methods.py": { + "size": 7092 + }, + "PySDM/backends/impl_numba/methods/seeding_methods.py": { + "size": 2569 + }, + "PySDM/backends/impl_numba/methods/terminal_velocity_methods.py": { + "size": 4771 + }, + "PySDM/backends/impl_numba/random.py": { + "size": 514 + }, + "PySDM/backends/impl_numba/storage.py": { + "size": 6241 + }, + "PySDM/backends/impl_numba/storage_impl.py": { + "size": 1957 + }, + "PySDM/backends/impl_numba/test_helpers/__init__.py": { + "size": 92 + }, + "PySDM/backends/impl_numba/test_helpers/scipy_ode_condensation_solver.py": { + "size": 10026 + }, + "PySDM/backends/impl_numba/toms748.py": { + "size": 6393 + }, + "PySDM/backends/impl_numba/warnings.py": { + "size": 649 + }, + "PySDM/backends/impl_thrust_rtc/__init__.py": { + "size": 34 + }, + "PySDM/backends/impl_thrust_rtc/bisection.py": { + "size": 1369 + }, + "PySDM/backends/impl_thrust_rtc/conf.py": { + "size": 1181 + }, + "PySDM/backends/impl_thrust_rtc/methods/__init__.py": { + "size": 40 + }, + "PySDM/backends/impl_thrust_rtc/methods/collisions_methods.py": { + "size": 34305 + }, + "PySDM/backends/impl_thrust_rtc/methods/condensation_methods.py": { + "size": 19341 + }, + "PySDM/backends/impl_thrust_rtc/methods/displacement_methods.py": { + "size": 5053 + }, + "PySDM/backends/impl_thrust_rtc/methods/freezing_methods.py": { + "size": 9369 + }, + "PySDM/backends/impl_thrust_rtc/methods/index_methods.py": { + "size": 2201 + }, + "PySDM/backends/impl_thrust_rtc/methods/isotope_methods.py": { + "size": 1209 + }, + "PySDM/backends/impl_thrust_rtc/methods/moments_methods.py": { + "size": 8006 + }, + "PySDM/backends/impl_thrust_rtc/methods/pair_methods.py": { + "size": 6997 + }, + "PySDM/backends/impl_thrust_rtc/methods/physics_methods.py": { + "size": 7329 + }, + "PySDM/backends/impl_thrust_rtc/methods/terminal_velocity_methods.py": { + "size": 5848 + }, + "PySDM/backends/impl_thrust_rtc/methods/thrust_rtc_backend_methods.py": { + "size": 811 + }, + "PySDM/backends/impl_thrust_rtc/nice_thrust.py": { + "size": 472 + }, + "PySDM/backends/impl_thrust_rtc/random.py": { + "size": 1214 + }, + "PySDM/backends/impl_thrust_rtc/storage.py": { + "size": 17980 + }, + "PySDM/backends/impl_thrust_rtc/test_helpers/__init__.py": { + "size": 145 + }, + "PySDM/backends/impl_thrust_rtc/test_helpers/cpp2python.py": { + "size": 6078 + }, + "PySDM/backends/impl_thrust_rtc/test_helpers/fake_thrust_rtc.py": { + "size": 7962 + }, + "PySDM/backends/impl_thrust_rtc/test_helpers/flag.py": { + "size": 170 + }, + "PySDM/backends/numba.py": { + "size": 4036 + }, + "PySDM/backends/thrust_rtc.py": { + "size": 2646 + }, + "PySDM/builder.py": { + "size": 5339 + }, + "PySDM/dynamics/__init__.py": { + "size": 838 + }, + "PySDM/dynamics/ambient_thermodynamics.py": { + "size": 353 + }, + "PySDM/dynamics/aqueous_chemistry.py": { + "size": 4823 + }, + "PySDM/dynamics/collisions/__init__.py": { + "size": 554 + }, + "PySDM/dynamics/collisions/breakup_efficiencies/__init__.py": { + "size": 59 + }, + "PySDM/dynamics/collisions/breakup_efficiencies/constEb.py": { + "size": 313 + }, + "PySDM/dynamics/collisions/breakup_fragmentations/__init__.py": { + "size": 329 + }, + "PySDM/dynamics/collisions/breakup_fragmentations/always_n.py": { + "size": 493 + }, + "PySDM/dynamics/collisions/breakup_fragmentations/constant_mass.py": { + "size": 498 + }, + "PySDM/dynamics/collisions/breakup_fragmentations/expon_frag.py": { + "size": 306 + }, + "PySDM/dynamics/collisions/breakup_fragmentations/exponential.py": { + "size": 1157 + }, + "PySDM/dynamics/collisions/breakup_fragmentations/feingold1988.py": { + "size": 1351 + }, + "PySDM/dynamics/collisions/breakup_fragmentations/gaussian.py": { + "size": 1170 + }, + "PySDM/dynamics/collisions/breakup_fragmentations/impl/__init__.py": { + "size": 125 + }, + "PySDM/dynamics/collisions/breakup_fragmentations/impl/volume_based.py": { + "size": 783 + }, + "PySDM/dynamics/collisions/breakup_fragmentations/lowlist82.py": { + "size": 4114 + }, + "PySDM/dynamics/collisions/breakup_fragmentations/slams.py": { + "size": 1226 + }, + "PySDM/dynamics/collisions/breakup_fragmentations/straub2010.py": { + "size": 3909 + }, + "PySDM/dynamics/collisions/coalescence_efficiencies/__init__.py": { + "size": 246 + }, + "PySDM/dynamics/collisions/coalescence_efficiencies/_gravitational.py": { + "size": 484 + }, + "PySDM/dynamics/collisions/coalescence_efficiencies/_parameterized.py": { + "size": 648 + }, + "PySDM/dynamics/collisions/coalescence_efficiencies/berry1967.py": { + "size": 353 + }, + "PySDM/dynamics/collisions/coalescence_efficiencies/constEc.py": { + "size": 287 + }, + "PySDM/dynamics/collisions/coalescence_efficiencies/lowlist1982.py": { + "size": 3746 + }, + "PySDM/dynamics/collisions/coalescence_efficiencies/specified_eff.py": { + "size": 578 + }, + "PySDM/dynamics/collisions/coalescence_efficiencies/straub2010.py": { + "size": 1672 + }, + "PySDM/dynamics/collisions/collision.py": { + "size": 13359 + }, + "PySDM/dynamics/collisions/collision_kernels/__init__.py": { + "size": 532 + }, + "PySDM/dynamics/collisions/collision_kernels/constantK.py": { + "size": 279 + }, + "PySDM/dynamics/collisions/collision_kernels/electric.py": { + "size": 458 + }, + "PySDM/dynamics/collisions/collision_kernels/geometric.py": { + "size": 732 + }, + "PySDM/dynamics/collisions/collision_kernels/golovin.py": { + "size": 1304 + }, + "PySDM/dynamics/collisions/collision_kernels/hydrodynamic.py": { + "size": 435 + }, + "PySDM/dynamics/collisions/collision_kernels/impl/__init__.py": { + "size": 53 + }, + "PySDM/dynamics/collisions/collision_kernels/impl/gravitational.py": { + "size": 528 + }, + "PySDM/dynamics/collisions/collision_kernels/impl/parameterized.py": { + "size": 974 + }, + "PySDM/dynamics/collisions/collision_kernels/linear.py": { + "size": 445 + }, + "PySDM/dynamics/collisions/collision_kernels/simple_geometric.py": { + "size": 842 + }, + "PySDM/dynamics/condensation.py": { + "size": 4509 + }, + "PySDM/dynamics/displacement.py": { + "size": 6170 + }, + "PySDM/dynamics/eulerian_advection.py": { + "size": 682 + }, + "PySDM/dynamics/freezing.py": { + "size": 3497 + }, + "PySDM/dynamics/impl/__init__.py": { + "size": 103 + }, + "PySDM/dynamics/impl/chemistry_utils.py": { + "size": 5243 + }, + "PySDM/dynamics/impl/random_generator_optimizer.py": { + "size": 1515 + }, + "PySDM/dynamics/impl/random_generator_optimizer_nopair.py": { + "size": 1059 + }, + "PySDM/dynamics/impl/register_dynamic.py": { + "size": 497 + }, + "PySDM/dynamics/isotopic_fractionation.py": { + "size": 1405 + }, + "PySDM/dynamics/relaxed_velocity.py": { + "size": 3138 + }, + "PySDM/dynamics/seeding.py": { + "size": 3841 + }, + "PySDM/dynamics/terminal_velocity/__init__.py": { + "size": 413 + }, + "PySDM/dynamics/terminal_velocity/columnar_ice_crystal.py": { + "size": 863 + }, + "PySDM/dynamics/terminal_velocity/gunn_and_kinzer.py": { + "size": 6495 + }, + "PySDM/dynamics/terminal_velocity/power_series.py": { + "size": 1621 + }, + "PySDM/dynamics/terminal_velocity/rogers_and_yau.py": { + "size": 437 + }, + "PySDM/dynamics/terminal_velocity/spheres_ice.py": { + "size": 563 + }, + "PySDM/dynamics/vapour_deposition_on_ice.py": { + "size": 721 + }, + "PySDM/environments/__init__.py": { + "size": 246 + }, + "PySDM/environments/box.py": { + "size": 1095 + }, + "PySDM/environments/impl/__init__.py": { + "size": 145 + }, + "PySDM/environments/impl/moist.py": { + "size": 4102 + }, + "PySDM/environments/impl/register_environment.py": { + "size": 634 + }, + "PySDM/environments/kinematic_1d.py": { + "size": 3230 + }, + "PySDM/environments/kinematic_2d.py": { + "size": 3409 + }, + "PySDM/environments/parcel.py": { + "size": 5070 + }, + "PySDM/exporters/__init__.py": { + "size": 328 + }, + "PySDM/exporters/netcdf_exporter.py": { + "size": 4268 + }, + "PySDM/exporters/netcdf_exporter_1d.py": { + "size": 5212 + }, + "PySDM/exporters/vtk_exporter.py": { + "size": 5690 + }, + "PySDM/exporters/vtk_exporter_1d.py": { + "size": 2180 + }, + "PySDM/exporters/vtk_exporter_parcel.py": { + "size": 5543 + }, + "PySDM/formulae.py": { + "size": 15549 + }, + "PySDM/impl/__init__.py": { + "size": 51 + }, + "PySDM/impl/arakawa_c.py": { + "size": 331 + }, + "PySDM/impl/camel_case.py": { + "size": 367 + }, + "PySDM/impl/mesh.py": { + "size": 2839 + }, + "PySDM/impl/null_physics_class.py": { + "size": 128 + }, + "PySDM/impl/particle_attributes.py": { + "size": 3702 + }, + "PySDM/impl/particle_attributes_factory.py": { + "size": 4687 + }, + "PySDM/impl/wall_timer.py": { + "size": 465 + }, + "PySDM/initialisation/__init__.py": { + "size": 294 + }, + "PySDM/initialisation/aerosol_composition/__init__.py": { + "size": 207 + }, + "PySDM/initialisation/aerosol_composition/dry_aerosol.py": { + "size": 4373 + }, + "PySDM/initialisation/discretise_multiplicities.py": { + "size": 1040 + }, + "PySDM/initialisation/hygroscopic_equilibrium.py": { + "size": 6017 + }, + "PySDM/initialisation/impl/__init__.py": { + "size": 122 + }, + "PySDM/initialisation/impl/spectrum.py": { + "size": 1138 + }, + "PySDM/initialisation/init_fall_momenta.py": { + "size": 1429 + }, + "PySDM/initialisation/sampling/__init__.py": { + "size": 40 + }, + "PySDM/initialisation/sampling/spatial_sampling.py": { + "size": 1419 + }, + "PySDM/initialisation/sampling/spectral_sampling.py": { + "size": 4865 + }, + "PySDM/initialisation/sampling/spectro_glacial_sampling.py": { + "size": 1662 + }, + "PySDM/initialisation/spectra/__init__.py": { + "size": 309 + }, + "PySDM/initialisation/spectra/exponential.py": { + "size": 364 + }, + "PySDM/initialisation/spectra/gamma.py": { + "size": 385 + }, + "PySDM/initialisation/spectra/gaussian.py": { + "size": 362 + }, + "PySDM/initialisation/spectra/lognormal.py": { + "size": 1169 + }, + "PySDM/initialisation/spectra/sum.py": { + "size": 1502 + }, + "PySDM/initialisation/spectra/top_hat.py": { + "size": 563 + }, + "PySDM/particulator.py": { + "size": 22151 + }, + "PySDM/physics/__init__.py": { + "size": 2152 + }, + "PySDM/physics/air_dynamic_viscosity/__init__.py": { + "size": 88 + }, + "PySDM/physics/air_dynamic_viscosity/zografos_et_al_1987.py": { + "size": 616 + }, + "PySDM/physics/bulk_phase_partitioning/__init__.py": { + "size": 160 + }, + "PySDM/physics/bulk_phase_partitioning/kaul_et_al_2015.py": { + "size": 763 + }, + "PySDM/physics/constants.py": { + "size": 1475 + }, + "PySDM/physics/constants_defaults.py": { + "size": 25578 + }, + "PySDM/physics/diffusion_coordinate/__init__.py": { + "size": 165 + }, + "PySDM/physics/diffusion_coordinate/water_mass.py": { + "size": 442 + }, + "PySDM/physics/diffusion_coordinate/water_mass_logarithm.py": { + "size": 563 + }, + "PySDM/physics/diffusion_ice_capacity/__init__.py": { + "size": 136 + }, + "PySDM/physics/diffusion_ice_capacity/columnar.py": { + "size": 802 + }, + "PySDM/physics/diffusion_ice_capacity/spherical.py": { + "size": 310 + }, + "PySDM/physics/diffusion_ice_kinetics/__init__.py": { + "size": 162 + }, + "PySDM/physics/diffusion_ice_kinetics/neglect.py": { + "size": 511 + }, + "PySDM/physics/diffusion_ice_kinetics/standard.py": { + "size": 1059 + }, + "PySDM/physics/diffusion_kinetics/__init__.py": { + "size": 261 + }, + "PySDM/physics/diffusion_kinetics/fuchs_sutugin.py": { + "size": 1025 + }, + "PySDM/physics/diffusion_kinetics/grabowski_et_al_2011.py": { + "size": 197 + }, + "PySDM/physics/diffusion_kinetics/lowe_et_al_2019.py": { + "size": 661 + }, + "PySDM/physics/diffusion_kinetics/neglect.py": { + "size": 500 + }, + "PySDM/physics/diffusion_kinetics/pruppacher_and_klett_2005.py": { + "size": 686 + }, + "PySDM/physics/diffusion_thermics/__init__.py": { + "size": 279 + }, + "PySDM/physics/diffusion_thermics/grabowski_et_al_2011.py": { + "size": 732 + }, + "PySDM/physics/diffusion_thermics/lowe_et_al_2019.py": { + "size": 452 + }, + "PySDM/physics/diffusion_thermics/neglect.py": { + "size": 312 + }, + "PySDM/physics/diffusion_thermics/seinfeld_and_pandis_2010.py": { + "size": 316 + }, + "PySDM/physics/diffusion_thermics/tracy_welch_porter.py": { + "size": 424 + }, + "PySDM/physics/dimensional_analysis.py": { + "size": 790 + }, + "PySDM/physics/drop_growth/__init__.py": { + "size": 161 + }, + "PySDM/physics/drop_growth/fick.py": { + "size": 1018 + }, + "PySDM/physics/drop_growth/howell_1949.py": { + "size": 1332 + }, + "PySDM/physics/drop_growth/mason_1971.py": { + "size": 756 + }, + "PySDM/physics/fragmentation_function/__init__.py": { + "size": 366 + }, + "PySDM/physics/fragmentation_function/always_n.py": { + "size": 187 + }, + "PySDM/physics/fragmentation_function/constant_mass.py": { + "size": 197 + }, + "PySDM/physics/fragmentation_function/expon_frag.py": { + "size": 319 + }, + "PySDM/physics/fragmentation_function/exponential.py": { + "size": 194 + }, + "PySDM/physics/fragmentation_function/feingold1988.py": { + "size": 304 + }, + "PySDM/physics/fragmentation_function/gaussian.py": { + "size": 121 + }, + "PySDM/physics/fragmentation_function/lowlist82.py": { + "size": 5827 + }, + "PySDM/physics/fragmentation_function/slams.py": { + "size": 182 + }, + "PySDM/physics/fragmentation_function/straub2010nf.py": { + "size": 1130 + }, + "PySDM/physics/freezing_temperature_spectrum/__init__.py": { + "size": 202 + }, + "PySDM/physics/freezing_temperature_spectrum/bigg_1953.py": { + "size": 742 + }, + "PySDM/physics/freezing_temperature_spectrum/niemand_et_al_2012.py": { + "size": 1343 + }, + "PySDM/physics/freezing_temperature_spectrum/null.py": { + "size": 267 + }, + "PySDM/physics/heterogeneous_ice_nucleation_rate/__init__.py": { + "size": 137 + }, + "PySDM/physics/heterogeneous_ice_nucleation_rate/abifm.py": { + "size": 450 + }, + "PySDM/physics/heterogeneous_ice_nucleation_rate/constant.py": { + "size": 309 + }, + "PySDM/physics/heterogeneous_ice_nucleation_rate/null.py": { + "size": 309 + }, + "PySDM/physics/homogeneous_ice_nucleation_rate/__init__.py": { + "size": 220 + }, + "PySDM/physics/homogeneous_ice_nucleation_rate/constant.py": { + "size": 516 + }, + "PySDM/physics/homogeneous_ice_nucleation_rate/koop.py": { + "size": 931 + }, + "PySDM/physics/homogeneous_ice_nucleation_rate/koop_corr.py": { + "size": 1147 + }, + "PySDM/physics/homogeneous_ice_nucleation_rate/koop_murray.py": { + "size": 1179 + }, + "PySDM/physics/homogeneous_ice_nucleation_rate/null.py": { + "size": 596 + }, + "PySDM/physics/hydrostatics/__init__.py": { + "size": 231 + }, + "PySDM/physics/hydrostatics/constant_g_vapour_mixing_ratio_and_theta_std.py": { + "size": 1398 + }, + "PySDM/physics/hydrostatics/variable_g_isothermal.py": { + "size": 737 + }, + "PySDM/physics/hygroscopicity/__init__.py": { + "size": 210 + }, + "PySDM/physics/hygroscopicity/kappa_koehler.py": { + "size": 606 + }, + "PySDM/physics/hygroscopicity/kappa_koehler_leading_terms.py": { + "size": 711 + }, + "PySDM/physics/impl/__init__.py": { + "size": 154 + }, + "PySDM/physics/impl/fake_unit_registry.py": { + "size": 1844 + }, + "PySDM/physics/impl/flag.py": { + "size": 189 + }, + "PySDM/physics/isotope_diffusivity_ratios/__init__.py": { + "size": 242 + }, + "PySDM/physics/isotope_diffusivity_ratios/grahams_law.py": { + "size": 729 + }, + "PySDM/physics/isotope_diffusivity_ratios/hellmann_and_harvey_2020.py": { + "size": 1400 + }, + "PySDM/physics/isotope_diffusivity_ratios/stewart_1975.py": { + "size": 1598 + }, + "PySDM/physics/isotope_equilibrium_fractionation_factors/__init__.py": { + "size": 568 + }, + "PySDM/physics/isotope_equilibrium_fractionation_factors/barkan_and_luz_2005.py": { + "size": 350 + }, + "PySDM/physics/isotope_equilibrium_fractionation_factors/ellehoj_et_al_2013.py": { + "size": 480 + }, + "PySDM/physics/isotope_equilibrium_fractionation_factors/horita_and_wesolowski_1994.py": { + "size": 1018 + }, + "PySDM/physics/isotope_equilibrium_fractionation_factors/lamb_et_al_2017.py": { + "size": 472 + }, + "PySDM/physics/isotope_equilibrium_fractionation_factors/majoube_1970.py": { + "size": 550 + }, + "PySDM/physics/isotope_equilibrium_fractionation_factors/majoube_1971.py": { + "size": 693 + }, + "PySDM/physics/isotope_equilibrium_fractionation_factors/merlivat_and_nief_1967.py": { + "size": 705 + }, + "PySDM/physics/isotope_equilibrium_fractionation_factors/van_hook_1968.py": { + "size": 3470 + }, + "PySDM/physics/isotope_kinetic_fractionation_factors/__init__.py": { + "size": 208 + }, + "PySDM/physics/isotope_kinetic_fractionation_factors/craig_gordon.py": { + "size": 684 + }, + "PySDM/physics/isotope_kinetic_fractionation_factors/jouzel_and_merlivat_1984.py": { + "size": 1070 + }, + "PySDM/physics/isotope_meteoric_water_line/__init__.py": { + "size": 285 + }, + "PySDM/physics/isotope_meteoric_water_line/barkan_and_luz_2007.py": { + "size": 626 + }, + "PySDM/physics/isotope_meteoric_water_line/dansgaard_1964.py": { + "size": 651 + }, + "PySDM/physics/isotope_meteoric_water_line/picciotto_et_al_1960.py": { + "size": 485 + }, + "PySDM/physics/isotope_ratio_evolution/__init__.py": { + "size": 295 + }, + "PySDM/physics/isotope_ratio_evolution/gedzelman_and_arnold_1994.py": { + "size": 534 + }, + "PySDM/physics/isotope_ratio_evolution/merlivat_and_jouzel_1979.py": { + "size": 531 + }, + "PySDM/physics/isotope_ratio_evolution/rayleigh_distillation.py": { + "size": 342 + }, + "PySDM/physics/isotope_relaxation_timescale/__init__.py": { + "size": 253 + }, + "PySDM/physics/isotope_relaxation_timescale/bolin_1958.py": { + "size": 358 + }, + "PySDM/physics/isotope_relaxation_timescale/jouzel_et_al_1975.py": { + "size": 1078 + }, + "PySDM/physics/isotope_relaxation_timescale/miyake_et_al_1968.py": { + "size": 888 + }, + "PySDM/physics/isotope_relaxation_timescale/zaba_et_al.py": { + "size": 636 + }, + "PySDM/physics/isotope_temperature_inference/__init__.py": { + "size": 251 + }, + "PySDM/physics/isotope_temperature_inference/picciotto_et_al_1960.py": { + "size": 574 + }, + "PySDM/physics/isotope_ventilation_ratio/__init__.py": { + "size": 168 + }, + "PySDM/physics/isotope_ventilation_ratio/brutsaert_1982.py": { + "size": 598 + }, + "PySDM/physics/isotope_ventilation_ratio/neglect.py": { + "size": 302 + }, + "PySDM/physics/latent_heat_sublimation/__init__.py": { + "size": 138 + }, + "PySDM/physics/latent_heat_sublimation/murphy_koop_2005.py": { + "size": 481 + }, + "PySDM/physics/latent_heat_vapourisation/__init__.py": { + "size": 185 + }, + "PySDM/physics/latent_heat_vapourisation/constant.py": { + "size": 263 + }, + "PySDM/physics/latent_heat_vapourisation/kirchhoff.py": { + "size": 384 + }, + "PySDM/physics/latent_heat_vapourisation/lowe2019.py": { + "size": 582 + }, + "PySDM/physics/latent_heat_vapourisation/seinfeld_and_pandis_2010.py": { + "size": 316 + }, + "PySDM/physics/optical_albedo/__init__.py": { + "size": 132 + }, + "PySDM/physics/optical_albedo/bohren1987.py": { + "size": 342 + }, + "PySDM/physics/optical_depth/__init__.py": { + "size": 138 + }, + "PySDM/physics/optical_depth/stephens_1978.py": { + "size": 380 + }, + "PySDM/physics/particle_advection/__init__.py": { + "size": 169 + }, + "PySDM/physics/particle_advection/explicit_in_space.py": { + "size": 293 + }, + "PySDM/physics/particle_advection/implicit_in_space.py": { + "size": 352 + }, + "PySDM/physics/particle_shape_and_density/__init__.py": { + "size": 226 + }, + "PySDM/physics/particle_shape_and_density/columnar_ice.py": { + "size": 1498 + }, + "PySDM/physics/particle_shape_and_density/liquid_spheres.py": { + "size": 1278 + }, + "PySDM/physics/particle_shape_and_density/mixed_phase_spheres.py": { + "size": 1400 + }, + "PySDM/physics/particle_shape_and_density/porous_spheroids.py": { + "size": 861 + }, + "PySDM/physics/saturation_vapour_pressure/__init__.py": { + "size": 352 + }, + "PySDM/physics/saturation_vapour_pressure/august_roche_magnus.py": { + "size": 609 + }, + "PySDM/physics/saturation_vapour_pressure/bolton_1980.py": { + "size": 597 + }, + "PySDM/physics/saturation_vapour_pressure/flatau_walko_cotton.py": { + "size": 1908 + }, + "PySDM/physics/saturation_vapour_pressure/lowe1977.py": { + "size": 1307 + }, + "PySDM/physics/saturation_vapour_pressure/murphy_koop_2005.py": { + "size": 1059 + }, + "PySDM/physics/saturation_vapour_pressure/wexler_1976.py": { + "size": 778 + }, + "PySDM/physics/state_variable_triplet/__init__.py": { + "size": 113 + }, + "PySDM/physics/state_variable_triplet/libcloudphplusplus.py": { + "size": 1895 + }, + "PySDM/physics/surface_tension/__init__.py": { + "size": 250 + }, + "PySDM/physics/surface_tension/compressed_film_ovadnevaite.py": { + "size": 1479 + }, + "PySDM/physics/surface_tension/compressed_film_ruehl.py": { + "size": 3599 + }, + "PySDM/physics/surface_tension/constant.py": { + "size": 447 + }, + "PySDM/physics/surface_tension/szyszkowski_langmuir.py": { + "size": 2529 + }, + "PySDM/physics/terminal_velocity/__init__.py": { + "size": 171 + }, + "PySDM/physics/terminal_velocity/gunn_kinzer_1949.py": { + "size": 202 + }, + "PySDM/physics/terminal_velocity/power_series.py": { + "size": 134 + }, + "PySDM/physics/terminal_velocity/rogers_yau.py": { + "size": 690 + }, + "PySDM/physics/terminal_velocity_ice/__init__.py": { + "size": 140 + }, + "PySDM/physics/terminal_velocity_ice/columnar_ice_crystal.py": { + "size": 1617 + }, + "PySDM/physics/terminal_velocity_ice/spheres_ice.py": { + "size": 582 + }, + "PySDM/physics/trivia.py": { + "size": 8125 + }, + "PySDM/physics/ventilation/__init__.py": { + "size": 181 + }, + "PySDM/physics/ventilation/froessling_1938.py": { + "size": 414 + }, + "PySDM/physics/ventilation/neglect.py": { + "size": 330 + }, + "PySDM/physics/ventilation/pruppacher_rasmussen_1979.py": { + "size": 1474 + }, + "PySDM/products/__init__.py": { + "size": 314 + }, + "PySDM/products/ambient_thermodynamics/__init__.py": { + "size": 477 + }, + "PySDM/products/ambient_thermodynamics/ambient_dry_air_density.py": { + "size": 300 + }, + "PySDM/products/ambient_thermodynamics/ambient_dry_air_potential_temperature.py": { + "size": 362 + }, + "PySDM/products/ambient_thermodynamics/ambient_pressure.py": { + "size": 282 + }, + "PySDM/products/ambient_thermodynamics/ambient_relative_humidity.py": { + "size": 620 + }, + "PySDM/products/ambient_thermodynamics/ambient_temperature.py": { + "size": 287 + }, + "PySDM/products/ambient_thermodynamics/ambient_water_vapour_mixing_ratio.py": { + "size": 367 + }, + "PySDM/products/aqueous_chemistry/__init__.py": { + "size": 354 + }, + "PySDM/products/aqueous_chemistry/acidity.py": { + "size": 1589 + }, + "PySDM/products/aqueous_chemistry/aqueous_mass_spectrum.py": { + "size": 2540 + }, + "PySDM/products/aqueous_chemistry/aqueous_mole_fraction.py": { + "size": 1194 + }, + "PySDM/products/aqueous_chemistry/gaseous_mole_fraction.py": { + "size": 890 + }, + "PySDM/products/aqueous_chemistry/total_dry_mass_mixing_ratio.py": { + "size": 824 + }, + "PySDM/products/collision/__init__.py": { + "size": 442 + }, + "PySDM/products/collision/collision_rates.py": { + "size": 1501 + }, + "PySDM/products/collision/collision_timestep_mean.py": { + "size": 1285 + }, + "PySDM/products/collision/collision_timestep_min.py": { + "size": 822 + }, + "PySDM/products/condensation/__init__.py": { + "size": 336 + }, + "PySDM/products/condensation/activable_fraction.py": { + "size": 1301 + }, + "PySDM/products/condensation/condensation_timestep.py": { + "size": 1907 + }, + "PySDM/products/condensation/event_rates.py": { + "size": 1902 + }, + "PySDM/products/condensation/peak_saturation.py": { + "size": 1289 + }, + "PySDM/products/displacement/__init__.py": { + "size": 315 + }, + "PySDM/products/displacement/averaged_terminal_velocity.py": { + "size": 1195 + }, + "PySDM/products/displacement/flow_velocity_component.py": { + "size": 1315 + }, + "PySDM/products/displacement/max_courant_number.py": { + "size": 985 + }, + "PySDM/products/displacement/surface_precipitation.py": { + "size": 1464 + }, + "PySDM/products/freezing/__init__.py": { + "size": 503 + }, + "PySDM/products/freezing/cooling_rate.py": { + "size": 498 + }, + "PySDM/products/freezing/freezable_specific_concentration.py": { + "size": 1566 + }, + "PySDM/products/freezing/frozen_particle_concentration.py": { + "size": 1497 + }, + "PySDM/products/freezing/ice_nuclei_concentration.py": { + "size": 1361 + }, + "PySDM/products/freezing/total_unfrozen_immersed_surface_area.py": { + "size": 767 + }, + "PySDM/products/housekeeping/__init__.py": { + "size": 261 + }, + "PySDM/products/housekeeping/dynamic_wall_time.py": { + "size": 673 + }, + "PySDM/products/housekeeping/super_droplet_count_per_gridbox.py": { + "size": 1026 + }, + "PySDM/products/housekeeping/time.py": { + "size": 487 + }, + "PySDM/products/housekeeping/timers.py": { + "size": 1135 + }, + "PySDM/products/impl/__init__.py": { + "size": 612 + }, + "PySDM/products/impl/activation_filtered_product.py": { + "size": 828 + }, + "PySDM/products/impl/concentration_product.py": { + "size": 1505 + }, + "PySDM/products/impl/moist_environment_product.py": { + "size": 889 + }, + "PySDM/products/impl/moment_product.py": { + "size": 1488 + }, + "PySDM/products/impl/product.py": { + "size": 3247 + }, + "PySDM/products/impl/rate_product.py": { + "size": 1099 + }, + "PySDM/products/impl/register_product.py": { + "size": 524 + }, + "PySDM/products/impl/spectrum_moment_product.py": { + "size": 1670 + }, + "PySDM/products/optical/__init__.py": { + "size": 121 + }, + "PySDM/products/optical/cloud_albedo.py": { + "size": 345 + }, + "PySDM/products/optical/cloud_optical_depth.py": { + "size": 421 + }, + "PySDM/products/parcel/__init__.py": { + "size": 155 + }, + "PySDM/products/parcel/cloud_water_path.py": { + "size": 1798 + }, + "PySDM/products/parcel/parcel_displacement.py": { + "size": 683 + }, + "PySDM/products/size_spectral/__init__.py": { + "size": 1600 + }, + "PySDM/products/size_spectral/arbitrary_moment.py": { + "size": 1972 + }, + "PySDM/products/size_spectral/cloud_water_content.py": { + "size": 2857 + }, + "PySDM/products/size_spectral/effective_radius.py": { + "size": 1901 + }, + "PySDM/products/size_spectral/effective_radius_activated.py": { + "size": 1326 + }, + "PySDM/products/size_spectral/mean_radius.py": { + "size": 1130 + }, + "PySDM/products/size_spectral/mean_radius_activated.py": { + "size": 945 + }, + "PySDM/products/size_spectral/mean_volume_radius.py": { + "size": 909 + }, + "PySDM/products/size_spectral/number_size_spectrum.py": { + "size": 1436 + }, + "PySDM/products/size_spectral/particle_concentration.py": { + "size": 1413 + }, + "PySDM/products/size_spectral/particle_concentration_activated.py": { + "size": 1554 + }, + "PySDM/products/size_spectral/particle_size_spectrum.py": { + "size": 3216 + }, + "PySDM/products/size_spectral/particle_volume_versus_radius_logarithm_spectrum.py": { + "size": 1725 + }, + "PySDM/products/size_spectral/radius_binned_number_averaged_terminal_velocity.py": { + "size": 1372 + }, + "PySDM/products/size_spectral/size_standard_deviation.py": { + "size": 2173 + }, + "PySDM/products/size_spectral/total_particle_concentration.py": { + "size": 470 + }, + "PySDM/products/size_spectral/total_particle_specific_concentration.py": { + "size": 476 + }, + "PySDM/products/size_spectral/water_mixing_ratio.py": { + "size": 1483 + }, + "README.md": { + "size": 14547 + }, + "docs/bibliography.json": { + "size": 47413 + }, + "docs/generate_html.py": { + "size": 5430 + }, + "docs/markdown/pysdm_landing.md": { + "size": 31335 + }, + "docs/templates/README.md": { + "size": 83 + }, + "examples/PySDM_examples/Abade_and_Albuquerque_2024/__init__.py": { + "size": 279 + }, + "examples/PySDM_examples/Abade_and_Albuquerque_2024/settings.py": { + "size": 2077 + }, + "examples/PySDM_examples/Abade_and_Albuquerque_2024/simulation.py": { + "size": 4032 + }, + "examples/PySDM_examples/Abdul_Razzak_Ghan_2000/__init__.py": { + "size": 311 + }, + "examples/PySDM_examples/Abdul_Razzak_Ghan_2000/aerosol.py": { + "size": 3405 + }, + "examples/PySDM_examples/Abdul_Razzak_Ghan_2000/data_from_ARG2000_paper.py": { + "size": 11585 + }, + "examples/PySDM_examples/Abdul_Razzak_Ghan_2000/data_from_CloudMicrophysics_ARG.py": { + "size": 11547 + }, + "examples/PySDM_examples/Abdul_Razzak_Ghan_2000/run_ARG_parcel.py": { + "size": 5500 + }, + "examples/PySDM_examples/Alpert_and_Knopf_2016/__init__.py": { + "size": 519 + }, + "examples/PySDM_examples/Alpert_and_Knopf_2016/simulation.py": { + "size": 10169 + }, + "examples/PySDM_examples/Alpert_and_Knopf_2016/table.py": { + "size": 228 + }, + "examples/PySDM_examples/Alpert_and_Knopf_2016/table_1.py": { + "size": 3791 + }, + "examples/PySDM_examples/Alpert_and_Knopf_2016/table_2.py": { + "size": 1857 + }, + "examples/PySDM_examples/Arabas_and_Shima_2017/__init__.py": { + "size": 237 + }, + "examples/PySDM_examples/Arabas_and_Shima_2017/example.py": { + "size": 253 + }, + "examples/PySDM_examples/Arabas_and_Shima_2017/settings.py": { + "size": 2370 + }, + "examples/PySDM_examples/Arabas_and_Shima_2017/simulation.py": { + "size": 4162 + }, + "examples/PySDM_examples/Arabas_et_al_2015/__init__.py": { + "size": 291 + }, + "examples/PySDM_examples/Arabas_et_al_2015/example.py": { + "size": 826 + }, + "examples/PySDM_examples/Arabas_et_al_2015/example_benchmark.py": { + "size": 2367 + }, + "examples/PySDM_examples/Arabas_et_al_2015/settings.py": { + "size": 1028 + }, + "examples/PySDM_examples/Arabas_et_al_2015/spin_up.py": { + "size": 736 + }, + "examples/PySDM_examples/Arabas_et_al_2025/__init__.py": { + "size": 770 + }, + "examples/PySDM_examples/Arabas_et_al_2025/commons.py": { + "size": 662 + }, + "examples/PySDM_examples/Arabas_et_al_2025/curved_text.py": { + "size": 5338 + }, + "examples/PySDM_examples/Arabas_et_al_2025/frozen_fraction.py": { + "size": 783 + }, + "examples/PySDM_examples/Arabas_et_al_2025/make_particulator.py": { + "size": 2436 + }, + "examples/PySDM_examples/Arabas_et_al_2025/plots.py": { + "size": 5031 + }, + "examples/PySDM_examples/Arabas_et_al_2025/run_simulation.py": { + "size": 1281 + }, + "examples/PySDM_examples/Bartman_2020_MasterThesis/__init__.py": { + "size": 136 + }, + "examples/PySDM_examples/Bartman_2020_MasterThesis/fig_4_adaptive_sdm.py": { + "size": 2376 + }, + "examples/PySDM_examples/Bartman_2020_MasterThesis/fig_5_SCIPY_VS_ADAPTIVE.py": { + "size": 4237 + }, + "examples/PySDM_examples/Bartman_et_al_2021/__init__.py": { + "size": 255 + }, + "examples/PySDM_examples/Bartman_et_al_2021/label.py": { + "size": 523 + }, + "examples/PySDM_examples/Berry_1967/__init__.py": { + "size": 248 + }, + "examples/PySDM_examples/Berry_1967/example.py": { + "size": 2506 + }, + "examples/PySDM_examples/Berry_1967/example_fig_6.py": { + "size": 3101 + }, + "examples/PySDM_examples/Berry_1967/settings.py": { + "size": 1617 + }, + "examples/PySDM_examples/Berry_1967/spectrum_plotter.py": { + "size": 1105 + }, + "examples/PySDM_examples/Bieli_et_al_2022/__init__.py": { + "size": 205 + }, + "examples/PySDM_examples/Bieli_et_al_2022/settings.py": { + "size": 1679 + }, + "examples/PySDM_examples/Bieli_et_al_2022/simulation.py": { + "size": 1338 + }, + "examples/PySDM_examples/Bolin_1958/__init__.py": { + "size": 175 + }, + "examples/PySDM_examples/Bolot_et_al_2013/__init__.py": { + "size": 187 + }, + "examples/PySDM_examples/Bulenok_2023_MasterThesis/__init__.py": { + "size": 164 + }, + "examples/PySDM_examples/Bulenok_2023_MasterThesis/performance_comparison_Srivastava_Setup.py": { + "size": 7272 + }, + "examples/PySDM_examples/Bulenok_2023_MasterThesis/setups.py": { + "size": 3424 + }, + "examples/PySDM_examples/Bulenok_2023_MasterThesis/utils.py": { + "size": 7825 + }, + "examples/PySDM_examples/Ervens_and_Feingold_2012/__init__.py": { + "size": 159 + }, + "examples/PySDM_examples/Ervens_and_Feingold_2012/settings.py": { + "size": 456 + }, + "examples/PySDM_examples/Fisher_1991/__init__.py": { + "size": 380 + }, + "examples/PySDM_examples/Gedzelman_and_Arnold_1994/__init__.py": { + "size": 303 + }, + "examples/PySDM_examples/Gonfiantini_1986/__init__.py": { + "size": 312 + }, + "examples/PySDM_examples/Grabowski_and_Pawlowska_2023/__init__.py": { + "size": 534 + }, + "examples/PySDM_examples/Grabowski_and_Pawlowska_2023/settings.py": { + "size": 3231 + }, + "examples/PySDM_examples/Grabowski_and_Pawlowska_2023/simulation.py": { + "size": 4608 + }, + "examples/PySDM_examples/Graf_et_al_2019/__init__.py": { + "size": 252 + }, + "examples/PySDM_examples/Jaruga_and_Pawlowska_2018/__init__.py": { + "size": 263 + }, + "examples/PySDM_examples/Jensen_and_Nugent_2017/__init__.py": { + "size": 478 + }, + "examples/PySDM_examples/Jensen_and_Nugent_2017/plotting.py": { + "size": 3605 + }, + "examples/PySDM_examples/Jensen_and_Nugent_2017/settings.py": { + "size": 2245 + }, + "examples/PySDM_examples/Jensen_and_Nugent_2017/simulation.py": { + "size": 4869 + }, + "examples/PySDM_examples/Jensen_and_Nugent_2017/table_3.py": { + "size": 952 + }, + "examples/PySDM_examples/Jouzel_and_Merlivat_1984/__init__.py": { + "size": 335 + }, + "examples/PySDM_examples/Jouzel_and_Merlivat_1984/thermodynamic_profiles.py": { + "size": 928 + }, + "examples/PySDM_examples/Kinzer_And_Gunn_1951/__init__.py": { + "size": 118 + }, + "examples/PySDM_examples/Kinzer_And_Gunn_1951/table_1_and_2.py": { + "size": 3130 + }, + "examples/PySDM_examples/Kreidenweis_et_al_2003/__init__.py": { + "size": 276 + }, + "examples/PySDM_examples/Kreidenweis_et_al_2003/settings.py": { + "size": 2988 + }, + "examples/PySDM_examples/Kreidenweis_et_al_2003/simulation.py": { + "size": 4759 + }, + "examples/PySDM_examples/Lamb_et_al_2017/__init__.py": { + "size": 235 + }, + "examples/PySDM_examples/Lowe_et_al_2019/__init__.py": { + "size": 442 + }, + "examples/PySDM_examples/Lowe_et_al_2019/aerosol.py": { + "size": 6827 + }, + "examples/PySDM_examples/Lowe_et_al_2019/aerosol_code.py": { + "size": 9666 + }, + "examples/PySDM_examples/Lowe_et_al_2019/constants_def.py": { + "size": 480 + }, + "examples/PySDM_examples/Lowe_et_al_2019/plot_helper.py": { + "size": 6427 + }, + "examples/PySDM_examples/Lowe_et_al_2019/settings.py": { + "size": 2651 + }, + "examples/PySDM_examples/Lowe_et_al_2019/simulation.py": { + "size": 5834 + }, + "examples/PySDM_examples/Matsushima_et_al_2023/__init__.py": { + "size": 181 + }, + "examples/PySDM_examples/Merlivat_and_Nief_1967/__init__.py": { + "size": 178 + }, + "examples/PySDM_examples/Miyake_et_al_1968/__init__.py": { + "size": 202 + }, + "examples/PySDM_examples/Morrison_and_Grabowski_2007/__init__.py": { + "size": 203 + }, + "examples/PySDM_examples/Morrison_and_Grabowski_2007/common.py": { + "size": 5227 + }, + "examples/PySDM_examples/Morrison_and_Grabowski_2007/strato_cumulus.py": { + "size": 1096 + }, + "examples/PySDM_examples/Niedermeier_et_al_2014/__init__.py": { + "size": 199 + }, + "examples/PySDM_examples/Niedermeier_et_al_2014/settings.py": { + "size": 1557 + }, + "examples/PySDM_examples/Niedermeier_et_al_2014/simulation.py": { + "size": 3596 + }, + "examples/PySDM_examples/Pierchala_et_al_2022/__init__.py": { + "size": 242 + }, + "examples/PySDM_examples/Pierchala_et_al_2022/commons.py": { + "size": 1021 + }, + "examples/PySDM_examples/Pruppacher_and_Rasmussen_1979/__init__.py": { + "size": 220 + }, + "examples/PySDM_examples/Pyrcel/__init__.py": { + "size": 283 + }, + "examples/PySDM_examples/Pyrcel/profile_plotter.py": { + "size": 2354 + }, + "examples/PySDM_examples/Pyrcel/settings.py": { + "size": 2090 + }, + "examples/PySDM_examples/Pyrcel/simulation.py": { + "size": 4229 + }, + "examples/PySDM_examples/Rogers_1975/__init__.py": { + "size": 171 + }, + "examples/PySDM_examples/Rozanski_and_Sonntag_1982/__init__.py": { + "size": 260 + }, + "examples/PySDM_examples/Rozanski_and_Sonntag_1982/multibox.py": { + "size": 3594 + }, + "examples/PySDM_examples/Shima_et_al_2009/__init__.py": { + "size": 234 + }, + "examples/PySDM_examples/Shima_et_al_2009/error_measure.py": { + "size": 222 + }, + "examples/PySDM_examples/Shima_et_al_2009/example.py": { + "size": 2396 + }, + "examples/PySDM_examples/Shima_et_al_2009/example_timing.py": { + "size": 1844 + }, + "examples/PySDM_examples/Shima_et_al_2009/settings.py": { + "size": 1176 + }, + "examples/PySDM_examples/Shima_et_al_2009/spectrum_plotter.py": { + "size": 5622 + }, + "examples/PySDM_examples/Shima_et_al_2009/tutorial_example.py": { + "size": 1677 + }, + "examples/PySDM_examples/Shima_et_al_2009/tutorial_plotter.py": { + "size": 4190 + }, + "examples/PySDM_examples/Shima_et_al_2009/tutorial_settings.py": { + "size": 1176 + }, + "examples/PySDM_examples/Shipway_and_Hill_2012/__init__.py": { + "size": 298 + }, + "examples/PySDM_examples/Shipway_and_Hill_2012/mpdata_1d.py": { + "size": 2341 + }, + "examples/PySDM_examples/Shipway_and_Hill_2012/plot.py": { + "size": 3606 + }, + "examples/PySDM_examples/Shipway_and_Hill_2012/settings.py": { + "size": 6404 + }, + "examples/PySDM_examples/Shipway_and_Hill_2012/simulation.py": { + "size": 10338 + }, + "examples/PySDM_examples/Singer_Ward/__init__.py": { + "size": 161 + }, + "examples/PySDM_examples/Singer_Ward/aerosol.py": { + "size": 5805 + }, + "examples/PySDM_examples/Spichtinger_et_al_2023/__init__.py": { + "size": 231 + }, + "examples/PySDM_examples/Spichtinger_et_al_2023/data/__init__.py": { + "size": 0 + }, + "examples/PySDM_examples/Spichtinger_et_al_2023/data/reference_bulk.py": { + "size": 1540 + }, + "examples/PySDM_examples/Spichtinger_et_al_2023/data/simulation_data.py": { + "size": 782 + }, + "examples/PySDM_examples/Spichtinger_et_al_2023/settings.py": { + "size": 2001 + }, + "examples/PySDM_examples/Spichtinger_et_al_2023/simulation.py": { + "size": 3737 + }, + "examples/PySDM_examples/Srivastava_1982/__init__.py": { + "size": 564 + }, + "examples/PySDM_examples/Srivastava_1982/equations.py": { + "size": 3251 + }, + "examples/PySDM_examples/Srivastava_1982/example.py": { + "size": 8576 + }, + "examples/PySDM_examples/Srivastava_1982/settings.py": { + "size": 2275 + }, + "examples/PySDM_examples/Srivastava_1982/simulation.py": { + "size": 3039 + }, + "examples/PySDM_examples/Stewart_1975/__init__.py": { + "size": 140 + }, + "examples/PySDM_examples/Strzabala_2025_BEng/__init__.py": { + "size": 240 + }, + "examples/PySDM_examples/Strzabala_2025_BEng/paraview_parcel_model.py": { + "size": 7598 + }, + "examples/PySDM_examples/Toon_et_al_1980/__init__.py": { + "size": 0 + }, + "examples/PySDM_examples/Van_Hook_1968/__init__.py": { + "size": 170 + }, + "examples/PySDM_examples/Ware_et_al_2025/__init__.py": { + "size": 196 + }, + "examples/PySDM_examples/Yang_et_al_2018/__init__.py": { + "size": 278 + }, + "examples/PySDM_examples/Yang_et_al_2018/settings.py": { + "size": 2278 + }, + "examples/PySDM_examples/Yang_et_al_2018/simulation.py": { + "size": 4368 + }, + "examples/PySDM_examples/Zaba_et_al/__init__.py": { + "size": 183 + }, + "examples/PySDM_examples/_HOWTOs/__init__.py": { + "size": 169 + }, + "examples/PySDM_examples/__init__.py": { + "size": 284 + }, + "examples/PySDM_examples/deJong_Azimi/__init__.py": { + "size": 376 + }, + "examples/PySDM_examples/deJong_Azimi/cloudy_data.py": { + "size": 11258 + }, + "examples/PySDM_examples/deJong_Azimi/cloudy_data_0d.py": { + "size": 12260 + }, + "examples/PySDM_examples/deJong_Azimi/settings1D.py": { + "size": 2232 + }, + "examples/PySDM_examples/deJong_Azimi/simulation_0D.py": { + "size": 2023 + }, + "examples/PySDM_examples/deJong_Mackay_et_al_2023/__init__.py": { + "size": 760 + }, + "examples/PySDM_examples/deJong_Mackay_et_al_2023/plot_rates.py": { + "size": 2421 + }, + "examples/PySDM_examples/deJong_Mackay_et_al_2023/settings1D.py": { + "size": 3058 + }, + "examples/PySDM_examples/deJong_Mackay_et_al_2023/settings_0D.py": { + "size": 1918 + }, + "examples/PySDM_examples/deJong_Mackay_et_al_2023/simulation1D.py": { + "size": 2430 + }, + "examples/PySDM_examples/deJong_Mackay_et_al_2023/simulation_0D.py": { + "size": 4649 + }, + "examples/PySDM_examples/deJong_Mackay_et_al_2023/simulation_ss.py": { + "size": 5864 + }, + "examples/PySDM_examples/seeding/__init__.py": { + "size": 220 + }, + "examples/PySDM_examples/seeding/settings.py": { + "size": 2238 + }, + "examples/PySDM_examples/seeding/simulation.py": { + "size": 4124 + }, + "examples/PySDM_examples/utils/__init__.py": { + "size": 217 + }, + "examples/PySDM_examples/utils/basic_simulation.py": { + "size": 698 + }, + "examples/PySDM_examples/utils/dummy_controller.py": { + "size": 741 + }, + "examples/PySDM_examples/utils/kinematic_2d/__init__.py": { + "size": 989 + }, + "examples/PySDM_examples/utils/kinematic_2d/fields.py": { + "size": 1584 + }, + "examples/PySDM_examples/utils/kinematic_2d/gui.py": { + "size": 1314 + }, + "examples/PySDM_examples/utils/kinematic_2d/gui_controller.py": { + "size": 4283 + }, + "examples/PySDM_examples/utils/kinematic_2d/gui_settings.py": { + "size": 14456 + }, + "examples/PySDM_examples/utils/kinematic_2d/gui_viewer.py": { + "size": 13162 + }, + "examples/PySDM_examples/utils/kinematic_2d/make_default_product_collection.py": { + "size": 4700 + }, + "examples/PySDM_examples/utils/kinematic_2d/mpdata_2d.py": { + "size": 4441 + }, + "examples/PySDM_examples/utils/kinematic_2d/plots.py": { + "size": 6998 + }, + "examples/PySDM_examples/utils/kinematic_2d/simulation.py": { + "size": 10403 + }, + "examples/PySDM_examples/utils/kinematic_2d/storage.py": { + "size": 2579 + }, + "examples/PySDM_examples/utils/progbar_controller.py": { + "size": 470 + }, + "examples/PySDM_examples/utils/pvanim.py": { + "size": 10972 + }, + "examples/PySDM_examples/utils/read_vtk_1d.py": { + "size": 966 + }, + "examples/PySDM_examples/utils/widgets/__init__.py": { + "size": 519 + }, + "examples/PySDM_examples/utils/widgets/freezer.py": { + "size": 407 + }, + "examples/PySDM_examples/utils/widgets/progbar_updater.py": { + "size": 267 + }, + "examples/README.md": { + "size": 680 + }, + "examples/docs/pysdm_examples_landing.md": { + "size": 9951 + }, + "examples/pyproject.toml": { + "size": 175 + }, + "examples/setup.py": { + "size": 2105 + }, + "pyproject.toml": { + "size": 1583 + }, + "setup.py": { + "size": 1356 + }, + "tests/__init__.py": { + "size": 0 + }, + "tests/examples_tests/__init__.py": { + "size": 0 + }, + "tests/examples_tests/conftest.py": { + "size": 6752 + }, + "tests/examples_tests/test_run_examples.py": { + "size": 393 + }, + "tests/examples_tests/test_run_notebooks.py": { + "size": 207 + }, + "tests/examples_tests/test_tests_completeness.py": { + "size": 1758 + }, + "tests/smoke_tests/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/box/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/box/alpert_and_knopf_2016/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/box/alpert_and_knopf_2016/test_ak16_fig_1.py": { + "size": 3198 + }, + "tests/smoke_tests/box/alpert_and_knopf_2016/test_frozen_fraction.py": { + "size": 770 + }, + "tests/smoke_tests/box/berry_1967/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/box/berry_1967/test_coalescence.py": { + "size": 2916 + }, + "tests/smoke_tests/box/bieli_et_al_2022/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/box/bieli_et_al_2022/test_moments.py": { + "size": 2188 + }, + "tests/smoke_tests/box/dejong_and_mackay_et_al_2023/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/box/dejong_and_mackay_et_al_2023/test_collision.py": { + "size": 1294 + }, + "tests/smoke_tests/box/dejong_and_mackay_et_al_2023/test_fig_6.py": { + "size": 3352 + }, + "tests/smoke_tests/box/dejong_and_mackay_et_al_2023/test_fig_7.py": { + "size": 6263 + }, + "tests/smoke_tests/box/dejong_and_mackay_et_al_2023/test_fig_8.py": { + "size": 2416 + }, + "tests/smoke_tests/box/dejong_azimi/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/box/dejong_azimi/test_box.py": { + "size": 3039 + }, + "tests/smoke_tests/box/partmc/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/box/partmc/test_dry_wet_equilibration.py": { + "size": 3870 + }, + "tests/smoke_tests/box/shima_et_al_2009/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/box/shima_et_al_2009/test_convergence.py": { + "size": 2899 + }, + "tests/smoke_tests/box/shima_et_al_2009/test_lwc_constant.py": { + "size": 2874 + }, + "tests/smoke_tests/box/srivastava_1982/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/box/srivastava_1982/test_eq_10.py": { + "size": 3207 + }, + "tests/smoke_tests/box/srivastava_1982/test_eq_13.py": { + "size": 2441 + }, + "tests/smoke_tests/box/srivastava_1982/test_equations.py": { + "size": 1754 + }, + "tests/smoke_tests/conftest.py": { + "size": 200 + }, + "tests/smoke_tests/kinematic_1d/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/kinematic_1d/deJong_Azimi/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/kinematic_1d/deJong_Azimi/test_few_steps.py": { + "size": 1749 + }, + "tests/smoke_tests/kinematic_1d/deJong_Azimi/test_initial_condition.py": { + "size": 1563 + }, + "tests/smoke_tests/kinematic_1d/shipway_and_hill_2012/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/kinematic_1d/shipway_and_hill_2012/test_1d_exporters.py": { + "size": 5977 + }, + "tests/smoke_tests/kinematic_1d/shipway_and_hill_2012/test_few_steps.py": { + "size": 3927 + }, + "tests/smoke_tests/kinematic_1d/shipway_and_hill_2012/test_initial_condition.py": { + "size": 3408 + }, + "tests/smoke_tests/kinematic_1d/shipway_and_hill_2012/test_settings.py": { + "size": 1044 + }, + "tests/smoke_tests/kinematic_2d/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/kinematic_2d/arabas_et_al_2015/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/kinematic_2d/arabas_et_al_2015/dummy_storage.py": { + "size": 556 + }, + "tests/smoke_tests/kinematic_2d/arabas_et_al_2015/test_adaptive_displacement.py": { + "size": 2543 + }, + "tests/smoke_tests/kinematic_2d/arabas_et_al_2015/test_environment.py": { + "size": 992 + }, + "tests/smoke_tests/kinematic_2d/arabas_et_al_2015/test_export.py": { + "size": 3143 + }, + "tests/smoke_tests/kinematic_2d/arabas_et_al_2015/test_freezing.py": { + "size": 1779 + }, + "tests/smoke_tests/kinematic_2d/arabas_et_al_2015/test_gui_settings.py": { + "size": 686 + }, + "tests/smoke_tests/kinematic_2d/arabas_et_al_2015/test_initialisation.py": { + "size": 3515 + }, + "tests/smoke_tests/kinematic_2d/arabas_et_al_2015/test_spin_up.py": { + "size": 1683 + }, + "tests/smoke_tests/no_env/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/no_env/bolin_1958/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/no_env/bolin_1958/test_table_1.py": { + "size": 1858 + }, + "tests/smoke_tests/no_env/gedzelman_and_arnold_1994/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/no_env/gedzelman_and_arnold_1994/test_fig_2.py": { + "size": 1280 + }, + "tests/smoke_tests/no_env/gonfiantini_1986/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/no_env/gonfiantini_1986/test_fig_3_1.py": { + "size": 1446 + }, + "tests/smoke_tests/no_env/jouzel_and_merlivat_1984/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/no_env/jouzel_and_merlivat_1984/test_thermodynamic_profiles.py": { + "size": 2099 + }, + "tests/smoke_tests/no_env/kinzer_and_gunn_1951/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/no_env/kinzer_and_gunn_1951/test_table_1_and_2.py": { + "size": 649 + }, + "tests/smoke_tests/no_env/lamb_et_al_2017/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/no_env/lamb_et_al_2017/test_fig_4.py": { + "size": 1617 + }, + "tests/smoke_tests/no_env/matsushima_et_al_2023/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/no_env/matsushima_et_al_2023/test_fig_1.py": { + "size": 2526 + }, + "tests/smoke_tests/no_env/miyake_et_al_1968/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/no_env/miyake_et_al_1968/test_fig_19.py": { + "size": 2125 + }, + "tests/smoke_tests/no_env/pierchala_et_al_2022/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/no_env/pierchala_et_al_2022/test_fig_3.py": { + "size": 4185 + }, + "tests/smoke_tests/no_env/pierchala_et_al_2022/test_fig_4.py": { + "size": 2396 + }, + "tests/smoke_tests/no_env/pierchala_et_al_2022/test_supplement.py": { + "size": 848 + }, + "tests/smoke_tests/no_env/pruppacher_and_rasmussen_1979/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/no_env/pruppacher_and_rasmussen_1979/test_fig_1.py": { + "size": 1556 + }, + "tests/smoke_tests/no_env/stewart_1975/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/no_env/stewart_1975/test_fig_1.py": { + "size": 1604 + }, + "tests/smoke_tests/no_env/toon_et_al_1980/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/no_env/toon_et_al_1980/test_fig_1.py": { + "size": 1934 + }, + "tests/smoke_tests/no_env/zaba_et_al/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/no_env/zaba_et_al/test_global_meteoric_water_line.py": { + "size": 1091 + }, + "tests/smoke_tests/parcel_a/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/parcel_a/lowe_et_al_2019/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/parcel_a/lowe_et_al_2019/conftest.py": { + "size": 583 + }, + "tests/smoke_tests/parcel_a/lowe_et_al_2019/test_dz_sensitivity.py": { + "size": 2238 + }, + "tests/smoke_tests/parcel_a/lowe_et_al_2019/test_fig_1.py": { + "size": 4717 + }, + "tests/smoke_tests/parcel_a/lowe_et_al_2019/test_fig_2.py": { + "size": 3373 + }, + "tests/smoke_tests/parcel_a/lowe_et_al_2019/test_fig_s2.py": { + "size": 2386 + }, + "tests/smoke_tests/parcel_a/lowe_et_al_2019/test_surface_tension_models.py": { + "size": 12461 + }, + "tests/smoke_tests/parcel_a/lowe_et_al_2019/test_zero_forg.py": { + "size": 3803 + }, + "tests/smoke_tests/parcel_a/pyrcel/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/parcel_a/pyrcel/test_parcel_example.py": { + "size": 2526 + }, + "tests/smoke_tests/parcel_b/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/parcel_b/arabas_and_shima_2017/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/parcel_b/arabas_and_shima_2017/test_conservation.py": { + "size": 3297 + }, + "tests/smoke_tests/parcel_b/arabas_and_shima_2017/test_displacement.py": { + "size": 821 + }, + "tests/smoke_tests/parcel_b/arabas_and_shima_2017/test_event_rates.py": { + "size": 1238 + }, + "tests/smoke_tests/parcel_b/arabas_and_shima_2017/test_initialisation.py": { + "size": 2592 + }, + "tests/smoke_tests/parcel_b/arabas_and_shima_2017/test_vs_scipy.py": { + "size": 1296 + }, + "tests/smoke_tests/parcel_c/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/parcel_c/abade_and_albuquerque_2024/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/parcel_c/abade_and_albuquerque_2024/test_fig_2.py": { + "size": 3702 + }, + "tests/smoke_tests/parcel_c/abdul_razzak_ghan_2000/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/parcel_c/abdul_razzak_ghan_2000/test_ARG_example.py": { + "size": 2565 + }, + "tests/smoke_tests/parcel_c/abdul_razzak_ghan_2000/test_just_do_it.py": { + "size": 459 + }, + "tests/smoke_tests/parcel_c/abdul_razzak_ghan_2000/test_single_supersaturation_peak.py": { + "size": 4534 + }, + "tests/smoke_tests/parcel_c/grabowski_and_pawlowska_2023/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/parcel_c/grabowski_and_pawlowska_2023/test_condensation_tolerance.py": { + "size": 1548 + }, + "tests/smoke_tests/parcel_c/grabowski_and_pawlowska_2023/test_figure_1_and_2.py": { + "size": 3966 + }, + "tests/smoke_tests/parcel_c/grabowski_and_pawlowska_2023/test_figure_3.py": { + "size": 2998 + }, + "tests/smoke_tests/parcel_c/grabowski_and_pawlowska_2023/test_figure_4.py": { + "size": 1538 + }, + "tests/smoke_tests/parcel_c/grabowski_and_pawlowska_2023/test_ripening_rate.py": { + "size": 1566 + }, + "tests/smoke_tests/parcel_d/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/parcel_d/graf_et_al_2019/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/parcel_d/graf_et_al_2019/test_fig_4.py": { + "size": 786 + }, + "tests/smoke_tests/parcel_d/graf_et_al_2019/test_table_1.py": { + "size": 2007 + }, + "tests/smoke_tests/parcel_d/jensen_and_nugent_2017/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/parcel_d/jensen_and_nugent_2017/test_fig_1.py": { + "size": 1301 + }, + "tests/smoke_tests/parcel_d/jensen_and_nugent_2017/test_fig_3_and_tab_4_upper_rows.py": { + "size": 4726 + }, + "tests/smoke_tests/parcel_d/jensen_and_nugent_2017/test_fig_4_and_7_and_tab_4_bottom_rows.py": { + "size": 6898 + }, + "tests/smoke_tests/parcel_d/jensen_and_nugent_2017/test_fig_5.py": { + "size": 2684 + }, + "tests/smoke_tests/parcel_d/jensen_and_nugent_2017/test_fig_6.py": { + "size": 2639 + }, + "tests/smoke_tests/parcel_d/jensen_and_nugent_2017/test_table_3.py": { + "size": 798 + }, + "tests/smoke_tests/parcel_d/kreidenweis_et_al_2003/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/parcel_d/kreidenweis_et_al_2003/test_fig_1.py": { + "size": 3081 + }, + "tests/smoke_tests/parcel_d/kreidenweis_et_al_2003/test_ionic_strength.py": { + "size": 3619 + }, + "tests/smoke_tests/parcel_d/kreidenweis_et_al_2003/test_spectrum_at_t_0.py": { + "size": 1722 + }, + "tests/smoke_tests/parcel_d/kreidenweis_et_al_2003/test_table_3.py": { + "size": 4306 + }, + "tests/smoke_tests/parcel_d/niedermeier_et_al_2013/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/parcel_d/niedermeier_et_al_2013/test_temperature_profile.py": { + "size": 1031 + }, + "tests/smoke_tests/parcel_d/rogers_1975/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/parcel_d/rogers_1975/test_fig_1.py": { + "size": 1754 + }, + "tests/smoke_tests/parcel_d/rozanski_and_sonntag_1982/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/parcel_d/rozanski_and_sonntag_1982/test_figs_4_5_6.py": { + "size": 2361 + }, + "tests/smoke_tests/parcel_d/seeding/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/parcel_d/seeding/test_hello_world.py": { + "size": 1591 + }, + "tests/smoke_tests/parcel_d/seeding/test_seeding_no_collisions.py": { + "size": 1047 + }, + "tests/smoke_tests/parcel_d/yang_et_al_2018/__init__.py": { + "size": 0 + }, + "tests/smoke_tests/parcel_d/yang_et_al_2018/test_displacement.py": { + "size": 884 + }, + "tests/smoke_tests/parcel_d/yang_et_al_2018/test_initialisation.py": { + "size": 2575 + }, + "tests/smoke_tests/parcel_d/yang_et_al_2018/test_just_do_it.py": { + "size": 1856 + }, + "tests/tutorials_tests/__init__.py": { + "size": 0 + }, + "tests/tutorials_tests/conftest.py": { + "size": 397 + }, + "tests/tutorials_tests/test_run_notebooks.py": { + "size": 207 + }, + "tests/unit_tests/__init__.py": { + "size": 0 + }, + "tests/unit_tests/attributes/__init__.py": { + "size": 0 + }, + "tests/unit_tests/attributes/test_acidity.py": { + "size": 5671 + }, + "tests/unit_tests/attributes/test_area_radius.py": { + "size": 2246 + }, + "tests/unit_tests/attributes/test_critical_saturation.py": { + "size": 1003 + }, + "tests/unit_tests/attributes/test_diffusional_growth_mass_change.py": { + "size": 2694 + }, + "tests/unit_tests/attributes/test_fall_velocity.py": { + "size": 6133 + }, + "tests/unit_tests/attributes/test_impl_attribute_registry.py": { + "size": 2054 + }, + "tests/unit_tests/attributes/test_isotopes.py": { + "size": 4648 + }, + "tests/unit_tests/attributes/test_multiplicities.py": { + "size": 1344 + }, + "tests/unit_tests/attributes/test_reynolds_number.py": { + "size": 1120 + }, + "tests/unit_tests/backends/__init__.py": { + "size": 0 + }, + "tests/unit_tests/backends/storage/__init__.py": { + "size": 0 + }, + "tests/unit_tests/backends/storage/test_basic_ops.py": { + "size": 1671 + }, + "tests/unit_tests/backends/storage/test_index.py": { + "size": 956 + }, + "tests/unit_tests/backends/storage/test_setitem.py": { + "size": 477 + }, + "tests/unit_tests/backends/test_collisions_methods.py": { + "size": 11166 + }, + "tests/unit_tests/backends/test_ctor_defaults_and_warnings.py": { + "size": 1265 + }, + "tests/unit_tests/backends/test_fake_thrust.py": { + "size": 456 + }, + "tests/unit_tests/backends/test_instance_cache.py": { + "size": 670 + }, + "tests/unit_tests/backends/test_isotope_methods.py": { + "size": 728 + }, + "tests/unit_tests/backends/test_moments_methods.py": { + "size": 1168 + }, + "tests/unit_tests/backends/test_oxidation.py": { + "size": 5882 + }, + "tests/unit_tests/backends/test_pair_methods.py": { + "size": 3642 + }, + "tests/unit_tests/backends/test_physics_methods.py": { + "size": 3635 + }, + "tests/unit_tests/backends/test_seeding_methods.py": { + "size": 5382 + }, + "tests/unit_tests/backends/test_toms748.py": { + "size": 1084 + }, + "tests/unit_tests/conftest.py": { + "size": 434 + }, + "tests/unit_tests/dummy_environment.py": { + "size": 2133 + }, + "tests/unit_tests/dummy_particulator.py": { + "size": 761 + }, + "tests/unit_tests/dynamics/__init__.py": { + "size": 0 + }, + "tests/unit_tests/dynamics/collisions/__init__.py": { + "size": 0 + }, + "tests/unit_tests/dynamics/collisions/conftest.py": { + "size": 2497 + }, + "tests/unit_tests/dynamics/collisions/test_croupiers.py": { + "size": 1744 + }, + "tests/unit_tests/dynamics/collisions/test_defaults.py": { + "size": 701 + }, + "tests/unit_tests/dynamics/collisions/test_efficiencies.py": { + "size": 3824 + }, + "tests/unit_tests/dynamics/collisions/test_fragmentations.py": { + "size": 13842 + }, + "tests/unit_tests/dynamics/collisions/test_kernels.py": { + "size": 3076 + }, + "tests/unit_tests/dynamics/collisions/test_sdm_breakup.py": { + "size": 34120 + }, + "tests/unit_tests/dynamics/collisions/test_sdm_multi_cell.py": { + "size": 1640 + }, + "tests/unit_tests/dynamics/collisions/test_sdm_single_cell.py": { + "size": 10464 + }, + "tests/unit_tests/dynamics/condensation/__init__.py": { + "size": 0 + }, + "tests/unit_tests/dynamics/condensation/test_diagnostics.py": { + "size": 3829 + }, + "tests/unit_tests/dynamics/condensation/test_parcel_sanity_checks.py": { + "size": 7464 + }, + "tests/unit_tests/dynamics/condensation/test_ventilation.py": { + "size": 2828 + }, + "tests/unit_tests/dynamics/displacement/__init__.py": { + "size": 0 + }, + "tests/unit_tests/dynamics/displacement/displacement_settings.py": { + "size": 1876 + }, + "tests/unit_tests/dynamics/displacement/test_advection.py": { + "size": 6897 + }, + "tests/unit_tests/dynamics/displacement/test_courant_product.py": { + "size": 1672 + }, + "tests/unit_tests/dynamics/displacement/test_sedimentation.py": { + "size": 1397 + }, + "tests/unit_tests/dynamics/test_eulerian_advection.py": { + "size": 1296 + }, + "tests/unit_tests/dynamics/test_freezing.py": { + "size": 12880 + }, + "tests/unit_tests/dynamics/test_impl_register_dynamic.py": { + "size": 1178 + }, + "tests/unit_tests/dynamics/test_isotopic_fractionation.py": { + "size": 2922 + }, + "tests/unit_tests/dynamics/test_relaxed_velocity.py": { + "size": 5672 + }, + "tests/unit_tests/dynamics/test_seeding.py": { + "size": 8811 + }, + "tests/unit_tests/dynamics/test_terminal_velocity.py": { + "size": 7749 + }, + "tests/unit_tests/dynamics/test_vapour_deposition_on_ice.py": { + "size": 12477 + }, + "tests/unit_tests/environments/__init__.py": { + "size": 0 + }, + "tests/unit_tests/environments/test_impl.py": { + "size": 1660 + }, + "tests/unit_tests/environments/test_moist.py": { + "size": 2488 + }, + "tests/unit_tests/exporters/__init__.py": { + "size": 0 + }, + "tests/unit_tests/exporters/test_vtk_exporter.py": { + "size": 1534 + }, + "tests/unit_tests/exporters/test_vtk_exporter_parcel.py": { + "size": 7326 + }, + "tests/unit_tests/impl/__init__.py": { + "size": 0 + }, + "tests/unit_tests/impl/test_camel_case.py": { + "size": 488 + }, + "tests/unit_tests/impl/test_mesh.py": { + "size": 3618 + }, + "tests/unit_tests/impl/test_moments.py": { + "size": 4541 + }, + "tests/unit_tests/impl/test_particle_attributes.py": { + "size": 10612 + }, + "tests/unit_tests/initialisation/__init__.py": { + "size": 0 + }, + "tests/unit_tests/initialisation/test_aerosol_init.py": { + "size": 1751 + }, + "tests/unit_tests/initialisation/test_alpha_sampling.py": { + "size": 1827 + }, + "tests/unit_tests/initialisation/test_discretise_multiplicities.py": { + "size": 1984 + }, + "tests/unit_tests/initialisation/test_hygroscopic_equilibrium.py": { + "size": 5588 + }, + "tests/unit_tests/initialisation/test_init_fall_momenta.py": { + "size": 1921 + }, + "tests/unit_tests/initialisation/test_spatial_discretisation.py": { + "size": 2478 + }, + "tests/unit_tests/initialisation/test_spectra_lognormal.py": { + "size": 1244 + }, + "tests/unit_tests/initialisation/test_spectra_sum.py": { + "size": 753 + }, + "tests/unit_tests/initialisation/test_spectral_discretisation.py": { + "size": 3208 + }, + "tests/unit_tests/initialisation/test_spectro_glacial_discretisation.py": { + "size": 1780 + }, + "tests/unit_tests/physics/__init__.py": { + "size": 0 + }, + "tests/unit_tests/physics/test_accommodation_coefficients.py": { + "size": 945 + }, + "tests/unit_tests/physics/test_air_dynamic_viscosity.py": { + "size": 1793 + }, + "tests/unit_tests/physics/test_bulk_phase_partitioning.py": { + "size": 906 + }, + "tests/unit_tests/physics/test_constants.py": { + "size": 5851 + }, + "tests/unit_tests/physics/test_diffusion_ice_capacity.py": { + "size": 4231 + }, + "tests/unit_tests/physics/test_dimensional_analysis.py": { + "size": 1245 + }, + "tests/unit_tests/physics/test_drop_growth.py": { + "size": 3070 + }, + "tests/unit_tests/physics/test_fake_unit_registry.py": { + "size": 376 + }, + "tests/unit_tests/physics/test_formulae.py": { + "size": 4871 + }, + "tests/unit_tests/physics/test_fragmentation_functions.py": { + "size": 4952 + }, + "tests/unit_tests/physics/test_freezing_temperature_spectra.py": { + "size": 1692 + }, + "tests/unit_tests/physics/test_homogeneous_nucleation_rates.py": { + "size": 3691 + }, + "tests/unit_tests/physics/test_hydrostatics_var_g.py": { + "size": 1758 + }, + "tests/unit_tests/physics/test_hygroscopicity_fierce_diagrams.py": { + "size": 2582 + }, + "tests/unit_tests/physics/test_isotope_diffusivity_ratios.py": { + "size": 6114 + }, + "tests/unit_tests/physics/test_isotope_equilibrium_fractionation_factors.py": { + "size": 3863 + }, + "tests/unit_tests/physics/test_isotope_kinetic_fractionation_factors.py": { + "size": 6139 + }, + "tests/unit_tests/physics/test_isotope_meteoric_water_line.py": { + "size": 5566 + }, + "tests/unit_tests/physics/test_isotope_ratio_evolution.py": { + "size": 2272 + }, + "tests/unit_tests/physics/test_isotope_relaxation_timescale.py": { + "size": 2140 + }, + "tests/unit_tests/physics/test_isotope_temperature_inference.py": { + "size": 2177 + }, + "tests/unit_tests/physics/test_isotope_ventilation_ratio.py": { + "size": 1148 + }, + "tests/unit_tests/physics/test_latent_heat.py": { + "size": 2121 + }, + "tests/unit_tests/physics/test_optical.py": { + "size": 1417 + }, + "tests/unit_tests/physics/test_particle_shape_and_density.py": { + "size": 6262 + }, + "tests/unit_tests/physics/test_saturation_vapour_pressure.py": { + "size": 3826 + }, + "tests/unit_tests/physics/test_spectra.py": { + "size": 3169 + }, + "tests/unit_tests/physics/test_spectra_top_hat.py": { + "size": 1914 + }, + "tests/unit_tests/physics/test_surface_tension.py": { + "size": 2680 + }, + "tests/unit_tests/physics/test_terminal_velocity.py": { + "size": 2152 + }, + "tests/unit_tests/physics/test_thermal_conductivity.py": { + "size": 1338 + }, + "tests/unit_tests/physics/test_trivia.py": { + "size": 10079 + }, + "tests/unit_tests/physics/test_ventilation_coefficient.py": { + "size": 5811 + }, + "tests/unit_tests/products/__init__.py": { + "size": 0 + }, + "tests/unit_tests/products/test_activation_criteria.py": { + "size": 4194 + }, + "tests/unit_tests/products/test_ambient_relative_humidity.py": { + "size": 1257 + }, + "tests/unit_tests/products/test_arbitrary_moment.py": { + "size": 3669 + }, + "tests/unit_tests/products/test_averaged_terminal_velocity.py": { + "size": 2416 + }, + "tests/unit_tests/products/test_collision_rates.py": { + "size": 11852 + }, + "tests/unit_tests/products/test_concentration_product.py": { + "size": 2024 + }, + "tests/unit_tests/products/test_cooling_rate.py": { + "size": 5079 + }, + "tests/unit_tests/products/test_effective_radii.py": { + "size": 1856 + }, + "tests/unit_tests/products/test_impl.py": { + "size": 5902 + }, + "tests/unit_tests/products/test_mixed_phase_moments.py": { + "size": 2805 + }, + "tests/unit_tests/products/test_parcel_liquid_water_path.py": { + "size": 2724 + }, + "tests/unit_tests/products/test_particle_size_product.py": { + "size": 4657 + }, + "tests/unit_tests/products/test_particle_size_spectrum.py": { + "size": 4283 + }, + "tests/unit_tests/products/test_surface_precipitation.py": { + "size": 3603 + }, + "tests/unit_tests/test_builder.py": { + "size": 2388 + }, + "tests/unit_tests/test_formulae.py": { + "size": 5285 + }, + "tests/unit_tests/test_imports.py": { + "size": 1453 + }, + "tests/unit_tests/test_particulator.py": { + "size": 5023 + }, + "tutorials/README.md": { + "size": 2883 + } + }, + "processed_by": "zip_fallback", + "success": true + }, + "structure": { + "packages": [ + "source.PySDM", + "source.PySDM.attributes", + "source.PySDM.backends", + "source.PySDM.dynamics", + "source.PySDM.environments", + "source.PySDM.exporters", + "source.PySDM.impl", + "source.PySDM.initialisation", + "source.PySDM.physics", + "source.PySDM.products", + "source.examples.PySDM_examples", + "source.tests", + "source.tests.examples_tests", + "source.tests.smoke_tests", + "source.tests.tutorials_tests", + "source.tests.unit_tests" + ] + }, + "dependencies": { + "has_environment_yml": false, + "has_requirements_txt": false, + "pyproject": true, + "setup_cfg": false, + "setup_py": true + }, + "entry_points": { + "imports": [], + "cli": [], + "modules": [] + }, + "llm_analysis": { + "core_modules": [ + { + "package": "source.PySDM", + "module": "PySDM", + "functions": [], + "classes": [ + "Particulator" + ], + "description": "Main entry point for the PySDM package, handling particle-based cloud microphysics." + }, + { + "package": "source.PySDM.dynamics", + "module": "dynamics", + "functions": [ + "condensation", + "freezing", + "displacement" + ], + "classes": [], + "description": "Handles the dynamics of cloud microphysics including condensation, freezing, and displacement." + }, + { + "package": "source.PySDM.backends", + "module": "backends", + "functions": [], + "classes": [ + "NumbaBackend", + "ThrustRTCBackend" + ], + "description": "Provides backend implementations for computational efficiency using Numba and ThrustRTC." + }, + { + "package": "source.PySDM.exporters", + "module": "exporters", + "functions": [ + "export_to_netcdf", + "export_to_vtk" + ], + "classes": [], + "description": "Handles exporting simulation data to various formats like NetCDF and VTK." + } + ], + "cli_commands": [], + "import_strategy": { + "primary": "import", + "fallback": "blackbox", + "confidence": 0.85 + }, + "dependencies": { + "required": [ + "numpy", + "scipy", + "numba" + ], + "optional": [ + "matplotlib", + "netCDF4" + ] + }, + "risk_assessment": { + "import_feasibility": 0.8, + "intrusiveness_risk": "medium", + "complexity": "complex" + } + }, + "deepwiki_analysis": { + "repo_url": "https://github.com/open-atmos/PySDM", + "repo_name": "PySDM", + "content": "open-atmos/PySDM\nPythonic particle-based (super-droplet) warm-rain/aqueous-chemistry cloud microphysics package with box, parcel & 1D/2D prescribed-flow examples in Python, Julia and Matlab\nRepository Not Indexed\nThis repository hasn't been indexed yet. Indexing allows you to explore code structure, find documentation, and understand dependencies.\nIndexing typically takes 2-10 minutes to complete after it starts indexing\nOnce indexed, you'll have full access to code exploration and search functionality", + "model": "gpt-4o-2024-08-06", + "source": "selenium", + "success": true + }, + "deepwiki_options": { + "enabled": true, + "model": "gpt-4o-2024-08-06" + }, + "risk": { + "import_feasibility": 0.8, + "intrusiveness_risk": "medium", + "complexity": "complex" + } +} \ No newline at end of file diff --git a/PySDM/mcp_output/diff_report.md b/PySDM/mcp_output/diff_report.md new file mode 100644 index 0000000000000000000000000000000000000000..035acf53d3c24eba728ce334f7305922d730d59b --- /dev/null +++ b/PySDM/mcp_output/diff_report.md @@ -0,0 +1,68 @@ +# PySDM Project Difference Report + +**Repository:** PySDM +**Project Type:** Python Library +**Report Date:** January 31, 2026 +**Intrusiveness:** None +**Workflow Status:** Success +**Test Status:** Failed + +## Project Overview + +PySDM is a Python library designed to provide basic functionality for stochastic dynamic modeling. It is a tool used by researchers and developers to simulate and analyze complex systems through stochastic processes. The library aims to offer a robust and flexible framework for modeling, with a focus on ease of use and integration. + +## Difference Analysis + +### New Files Added + +In this update, 8 new files have been introduced to the PySDM repository. These files are likely to contain new features, enhancements, or documentation that expand the library's capabilities. However, no existing files were modified, indicating that the new additions are supplementary rather than replacements or modifications of existing functionalities. + +### Modified Files + +There were no modifications to existing files in this update. This suggests that the current functionality remains unchanged, and the focus was on expanding the library with new features or components. + +## Technical Analysis + +### New Features and Components + +The addition of 8 new files suggests the introduction of new modules or functionalities. Without specific details on the content of these files, it is assumed that they could include: + +- New stochastic models or algorithms. +- Additional utilities or helper functions. +- Enhanced documentation or examples to aid users in understanding and utilizing the library. + +### Test Failures + +Despite the successful workflow status, the test status indicates failures. This discrepancy suggests that while the integration and deployment processes were executed without errors, the new additions may have introduced issues that were not anticipated during development. Possible causes for test failures include: + +- Incompatibility of new features with existing ones. +- Bugs or errors in the newly added code. +- Insufficient test coverage for new functionalities. + +## Recommendations and Improvements + +1. **Conduct Thorough Testing:** Review and expand the test suite to cover the new functionalities introduced by the 8 new files. Ensure that all edge cases are considered and that the new components integrate seamlessly with existing ones. + +2. **Debug and Resolve Issues:** Investigate the specific causes of the test failures. Focus on debugging the new code to identify and resolve any errors or incompatibilities. + +3. **Enhance Documentation:** Update the library's documentation to include detailed information about the new features. Provide examples and use cases to help users understand and implement the new functionalities effectively. + +4. **Community Feedback:** Engage with the user community to gather feedback on the new features. This can provide insights into potential issues and areas for improvement. + +## Deployment Information + +The deployment process was executed successfully, indicating that the new files were integrated into the repository without any issues. However, given the test failures, it is advisable to hold off on a full release until the issues are resolved and the test suite passes successfully. + +## Future Planning + +1. **Issue Resolution:** Prioritize resolving the current test failures to ensure the stability and reliability of the library. + +2. **Feature Expansion:** Continue to expand the library's capabilities by introducing new models and utilities that align with user needs and industry trends. + +3. **Community Engagement:** Foster a strong community around PySDM by encouraging contributions, hosting discussions, and organizing events or workshops. + +4. **Versioning Strategy:** Consider implementing a versioning strategy to manage updates and releases effectively, ensuring users are aware of changes and improvements. + +## Conclusion + +The recent update to the PySDM project introduces new functionalities that have the potential to enhance the library's capabilities. However, the test failures highlight the need for further testing and debugging to ensure the new features integrate smoothly with the existing framework. By addressing these issues and engaging with the community, PySDM can continue to evolve as a valuable tool for stochastic dynamic modeling. \ No newline at end of file diff --git a/PySDM/mcp_output/mcp_plugin/__init__.py b/PySDM/mcp_output/mcp_plugin/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/mcp_output/mcp_plugin/adapter.py b/PySDM/mcp_output/mcp_plugin/adapter.py new file mode 100644 index 0000000000000000000000000000000000000000..ab8a574a8633d5cfe8b71ba128ea69d21c4c7416 --- /dev/null +++ b/PySDM/mcp_output/mcp_plugin/adapter.py @@ -0,0 +1,184 @@ +import os +import sys + +# Path settings +source_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "source") +sys.path.insert(0, source_path) + +# Import statements +try: + from PySDM import particulator + from PySDM.dynamics import condensation + from PySDM.backends import numba + from PySDM.exporters import netcdf_exporter + from PySDM.environments import parcel + from PySDM.initialisation import dry_aerosol + from PySDM.physics import constants +except ImportError as e: + print("Import failed: ", e) + print("Ensure that the source directory is correctly set and all dependencies are installed.") + # Fallback mode + particulator = None + condensation = None + numba = None + netcdf_exporter = None + parcel = None + dry_aerosol = None + constants = None + +class Adapter: + """ + Adapter class for MCP plugin, providing access to PySDM functionalities. + """ + + def __init__(self): + self.mode = "import" if particulator else "fallback" + + # ------------------ Particulator Module ------------------ + + def create_particulator(self, *args, **kwargs): + """ + Create an instance of the Particulator class. + + Parameters: + *args: Positional arguments for Particulator. + **kwargs: Keyword arguments for Particulator. + + Returns: + dict: Contains 'status' and 'instance' of Particulator or error message. + """ + if not particulator: + return {"status": "error", "message": "Particulator module not available in fallback mode."} + + try: + instance = particulator.Particulator(*args, **kwargs) + return {"status": "success", "instance": instance} + except Exception as e: + return {"status": "error", "message": str(e)} + + # ------------------ Dynamics Module ------------------ + + def call_condensation(self, *args, **kwargs): + """ + Call the condensation function. + + Parameters: + *args: Positional arguments for condensation. + **kwargs: Keyword arguments for condensation. + + Returns: + dict: Contains 'status' and result of condensation or error message. + """ + if not condensation: + return {"status": "error", "message": "Condensation module not available in fallback mode."} + + try: + result = condensation.condensation(*args, **kwargs) + return {"status": "success", "result": result} + except Exception as e: + return {"status": "error", "message": str(e)} + + # ------------------ Backends Module ------------------ + + def call_numba_backend(self, *args, **kwargs): + """ + Call the numba backend function. + + Parameters: + *args: Positional arguments for numba backend. + **kwargs: Keyword arguments for numba backend. + + Returns: + dict: Contains 'status' and result of numba backend or error message. + """ + if not numba: + return {"status": "error", "message": "Numba backend not available in fallback mode."} + + try: + result = numba.backend(*args, **kwargs) + return {"status": "success", "result": result} + except Exception as e: + return {"status": "error", "message": str(e)} + + # ------------------ Exporters Module ------------------ + + def create_netcdf_exporter(self, *args, **kwargs): + """ + Create an instance of the NetCDFExporter class. + + Parameters: + *args: Positional arguments for NetCDFExporter. + **kwargs: Keyword arguments for NetCDFExporter. + + Returns: + dict: Contains 'status' and 'instance' of NetCDFExporter or error message. + """ + if not netcdf_exporter: + return {"status": "error", "message": "NetCDFExporter module not available in fallback mode."} + + try: + instance = netcdf_exporter.NetCDFExporter(*args, **kwargs) + return {"status": "success", "instance": instance} + except Exception as e: + return {"status": "error", "message": str(e)} + + # ------------------ Environments Module ------------------ + + def create_parcel_environment(self, *args, **kwargs): + """ + Create an instance of the Parcel environment. + + Parameters: + *args: Positional arguments for Parcel. + **kwargs: Keyword arguments for Parcel. + + Returns: + dict: Contains 'status' and 'instance' of Parcel or error message. + """ + if not parcel: + return {"status": "error", "message": "Parcel environment not available in fallback mode."} + + try: + instance = parcel.Parcel(*args, **kwargs) + return {"status": "success", "instance": instance} + except Exception as e: + return {"status": "error", "message": str(e)} + + # ------------------ Initialisation Module ------------------ + + def create_dry_aerosol(self, *args, **kwargs): + """ + Create an instance of the DryAerosol class. + + Parameters: + *args: Positional arguments for DryAerosol. + **kwargs: Keyword arguments for DryAerosol. + + Returns: + dict: Contains 'status' and 'instance' of DryAerosol or error message. + """ + if not dry_aerosol: + return {"status": "error", "message": "DryAerosol module not available in fallback mode."} + + try: + instance = dry_aerosol.DryAerosol(*args, **kwargs) + return {"status": "success", "instance": instance} + except Exception as e: + return {"status": "error", "message": str(e)} + + # ------------------ Physics Module ------------------ + + def get_constants(self): + """ + Retrieve constants from the physics module. + + Returns: + dict: Contains 'status' and 'constants' or error message. + """ + if not constants: + return {"status": "error", "message": "Constants module not available in fallback mode."} + + try: + return {"status": "success", "constants": constants} + except Exception as e: + return {"status": "error", "message": str(e)} \ No newline at end of file diff --git a/PySDM/mcp_output/mcp_plugin/main.py b/PySDM/mcp_output/mcp_plugin/main.py new file mode 100644 index 0000000000000000000000000000000000000000..fca6ec384e22f703b287550e94cc00baaaa4c4a7 --- /dev/null +++ b/PySDM/mcp_output/mcp_plugin/main.py @@ -0,0 +1,13 @@ +""" +MCP Service Auto-Wrapper - Auto-generated +""" +from mcp_service import create_app + +def main(): + """Main entry point""" + app = create_app() + return app + +if __name__ == "__main__": + app = main() + app.run() \ No newline at end of file diff --git a/PySDM/mcp_output/mcp_plugin/mcp_service.py b/PySDM/mcp_output/mcp_plugin/mcp_service.py new file mode 100644 index 0000000000000000000000000000000000000000..5855948af6431e89905dc47291b3807a32956981 --- /dev/null +++ b/PySDM/mcp_output/mcp_plugin/mcp_service.py @@ -0,0 +1,79 @@ +import os +import sys + +# Add the local source directory to sys.path +source_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "source") +if source_path not in sys.path: + sys.path.insert(0, source_path) + +from fastmcp import FastMCP + +# Import core modules from the source directory +from PySDM import particulator +from PySDM.dynamics import condensation +from PySDM.physics import constants + +# Create the FastMCP service application +mcp = FastMCP("pysdm_service") + +@mcp.tool(name="simulate_particles", description="Simulate particle dynamics using PySDM") +def simulate_particles(particle_count: int, time_step: float) -> dict: + """ + Simulate particle dynamics using PySDM. + + Parameters: + - particle_count (int): Number of particles to simulate. + - time_step (float): Time step for the simulation. + + Returns: + - dict: A dictionary containing success, result, and error fields. + """ + try: + # Example simulation logic + result = particulator.simulate(particle_count, time_step) + return {"success": True, "result": result, "error": None} + except Exception as e: + return {"success": False, "result": None, "error": str(e)} + +@mcp.tool(name="calculate_condensation", description="Calculate condensation rates") +def calculate_condensation(temperature: float, pressure: float) -> dict: + """ + Calculate condensation rates. + + Parameters: + - temperature (float): Temperature in Kelvin. + - pressure (float): Pressure in Pascals. + + Returns: + - dict: A dictionary containing success, result, and error fields. + """ + try: + # Example condensation calculation + rate = condensation.calculate_rate(temperature, pressure) + return {"success": True, "result": rate, "error": None} + except Exception as e: + return {"success": False, "result": None, "error": str(e)} + +@mcp.tool(name="get_physical_constants", description="Retrieve physical constants") +def get_physical_constants() -> dict: + """ + Retrieve physical constants. + + Returns: + - dict: A dictionary containing success, result, and error fields. + """ + try: + # Example retrieval of constants + constants_data = constants.get_constants() + return {"success": True, "result": constants_data, "error": None} + except Exception as e: + return {"success": False, "result": None, "error": str(e)} + +def create_app() -> FastMCP: + """ + Create and return the FastMCP application instance. + + Returns: + - FastMCP: The FastMCP application instance. + """ + return mcp \ No newline at end of file diff --git a/PySDM/mcp_output/requirements.txt b/PySDM/mcp_output/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..b079fbecfb9d213cced9fd2bbb5d77382320ae69 --- /dev/null +++ b/PySDM/mcp_output/requirements.txt @@ -0,0 +1,7 @@ +fastmcp +fastapi +uvicorn[standard] +pydantic>=2.0.0 +numpy +scipy +numba diff --git a/PySDM/mcp_output/start_mcp.py b/PySDM/mcp_output/start_mcp.py new file mode 100644 index 0000000000000000000000000000000000000000..fc7fcbd9646ad53f089fc94af8129043a703325a --- /dev/null +++ b/PySDM/mcp_output/start_mcp.py @@ -0,0 +1,30 @@ + +""" +MCP Service Startup Entry +""" +import sys +import os + +project_root = os.path.dirname(os.path.abspath(__file__)) +mcp_plugin_dir = os.path.join(project_root, "mcp_plugin") +if mcp_plugin_dir not in sys.path: + sys.path.insert(0, mcp_plugin_dir) + +from mcp_service import create_app + +def main(): + """Start FastMCP service""" + app = create_app() + # Use environment variable to configure port, default 8000 + port = int(os.environ.get("MCP_PORT", "8000")) + + # Choose transport mode based on environment variable + transport = os.environ.get("MCP_TRANSPORT", "stdio") + if transport == "http": + app.run(transport="http", host="0.0.0.0", port=port) + else: + # Default to STDIO mode + app.run() + +if __name__ == "__main__": + main() diff --git a/PySDM/mcp_output/workflow_summary.json b/PySDM/mcp_output/workflow_summary.json new file mode 100644 index 0000000000000000000000000000000000000000..d92bdfb7f33aa3e6d86fe154cc56c49554df7044 --- /dev/null +++ b/PySDM/mcp_output/workflow_summary.json @@ -0,0 +1,210 @@ +{ + "repository": { + "name": "PySDM", + "url": "https://github.com/open-atmos/PySDM", + "local_path": "/export/zxcpu1/shiweijie/code/ghh/Code2MCP/workspace/PySDM", + "description": "Python library", + "features": "Basic functionality", + "tech_stack": "Python", + "stars": 0, + "forks": 0, + "language": "Python", + "last_updated": "", + "complexity": "complex", + "intrusiveness_risk": "medium" + }, + "execution": { + "start_time": 1769870957.2162707, + "end_time": 1769871085.716835, + "duration": 128.50056433677673, + "status": "success", + "workflow_status": "success", + "nodes_executed": [ + "download", + "analysis", + "env", + "generate", + "run", + "review", + "finalize" + ], + "total_files_processed": 16, + "environment_type": "unknown", + "llm_calls": 0, + "deepwiki_calls": 0 + }, + "tests": { + "original_project": { + "passed": false, + "details": {}, + "test_coverage": "100%", + "execution_time": 0, + "test_files": [] + }, + "mcp_plugin": { + "passed": true, + "details": {}, + "service_health": "healthy", + "startup_time": 0, + "transport_mode": "stdio", + "fastmcp_version": "unknown", + "mcp_version": "unknown" + } + }, + "analysis": { + "structure": { + "packages": [ + "source.PySDM", + "source.PySDM.attributes", + "source.PySDM.backends", + "source.PySDM.dynamics", + "source.PySDM.environments", + "source.PySDM.exporters", + "source.PySDM.impl", + "source.PySDM.initialisation", + "source.PySDM.physics", + "source.PySDM.products", + "source.examples.PySDM_examples", + "source.tests", + "source.tests.examples_tests", + "source.tests.smoke_tests", + "source.tests.tutorials_tests", + "source.tests.unit_tests" + ] + }, + "dependencies": { + "has_environment_yml": false, + "has_requirements_txt": false, + "pyproject": true, + "setup_cfg": false, + "setup_py": true + }, + "entry_points": { + "imports": [], + "cli": [], + "modules": [] + }, + "risk_assessment": { + "import_feasibility": 0.8, + "intrusiveness_risk": "medium", + "complexity": "complex" + }, + "deepwiki_analysis": { + "repo_url": "https://github.com/open-atmos/PySDM", + "repo_name": "PySDM", + "content": "open-atmos/PySDM\nPythonic particle-based (super-droplet) warm-rain/aqueous-chemistry cloud microphysics package with box, parcel & 1D/2D prescribed-flow examples in Python, Julia and Matlab\nRepository Not Indexed\nThis repository hasn't been indexed yet. Indexing allows you to explore code structure, find documentation, and understand dependencies.\nIndexing typically takes 2-10 minutes to complete after it starts indexing\nOnce indexed, you'll have full access to code exploration and search functionality", + "model": "gpt-4o-2024-08-06", + "source": "selenium", + "success": true + }, + "code_complexity": { + "cyclomatic_complexity": "medium", + "cognitive_complexity": "medium", + "maintainability_index": 75 + }, + "security_analysis": { + "vulnerabilities_found": 0, + "security_score": 85, + "recommendations": [] + } + }, + "plugin_generation": { + "files_created": [ + "mcp_output/start_mcp.py", + "mcp_output/mcp_plugin/__init__.py", + "mcp_output/mcp_plugin/mcp_service.py", + "mcp_output/mcp_plugin/adapter.py", + "mcp_output/mcp_plugin/main.py", + "mcp_output/requirements.txt", + "mcp_output/README_MCP.md" + ], + "main_entry": "start_mcp.py", + "requirements": [ + "fastmcp>=0.1.0", + "pydantic>=2.0.0" + ], + "readme_path": "/export/zxcpu1/shiweijie/code/ghh/Code2MCP/workspace/PySDM/mcp_output/README_MCP.md", + "adapter_mode": "import", + "total_lines_of_code": 0, + "generated_files_size": 0, + "tool_endpoints": 0, + "supported_features": [ + "Basic functionality" + ], + "generated_tools": [ + "Basic tools", + "Health check tools", + "Version info tools" + ] + }, + "code_review": {}, + "errors": [], + "warnings": [], + "recommendations": [ + "Improve test coverage by adding more unit tests for uncovered modules", + "optimize large files by breaking them into smaller", + "more manageable components", + "ensure all dependencies are clearly defined in a requirements.txt or environment.yml file", + "enhance documentation for better understanding of complex modules", + "consider indexing the repository for easier code exploration and search functionality", + "streamline the import strategy to reduce complexity and improve import feasibility", + "evaluate and reduce the intrusiveness risk of the project", + "improve the README to provide a clearer overview of the project and its usage", + "implement continuous integration to automate testing and deployment processes", + "refactor code to reduce complexity and improve maintainability." + ], + "performance_metrics": { + "memory_usage_mb": 0, + "cpu_usage_percent": 0, + "response_time_ms": 0, + "throughput_requests_per_second": 0 + }, + "deployment_info": { + "supported_platforms": [ + "Linux", + "Windows", + "macOS" + ], + "python_versions": [ + "3.8", + "3.9", + "3.10", + "3.11", + "3.12" + ], + "deployment_methods": [ + "Docker", + "pip", + "conda" + ], + "monitoring_support": true, + "logging_configuration": "structured" + }, + "execution_analysis": { + "success_factors": [ + "Comprehensive node execution covering download, analysis, environment setup, generation, run, review, and finalize", + "Successful MCP plugin generation and service health" + ], + "failure_reasons": [], + "overall_assessment": "good", + "node_performance": { + "download_time": "Efficient download process with no reported delays", + "analysis_time": "Completed successfully with medium complexity analysis", + "generation_time": "Efficient generation of MCP plugin files", + "test_time": "Original project tests did not pass, but MCP plugin tests were successful" + }, + "resource_usage": { + "memory_efficiency": "Memory usage data not available", + "cpu_efficiency": "CPU usage data not available", + "disk_usage": "Disk usage data not available" + } + }, + "technical_quality": { + "code_quality_score": 75, + "architecture_score": 70, + "performance_score": 65, + "maintainability_score": 75, + "security_score": 85, + "scalability_score": 70 + } +} \ No newline at end of file diff --git a/PySDM/source/.binder/apt.txt b/PySDM/source/.binder/apt.txt new file mode 100644 index 0000000000000000000000000000000000000000..7eaee3381bab7b1ad6e01a7d457f0d8b649b6ffa --- /dev/null +++ b/PySDM/source/.binder/apt.txt @@ -0,0 +1 @@ +libgl1 diff --git a/PySDM/source/.binder/postBuild b/PySDM/source/.binder/postBuild new file mode 100644 index 0000000000000000000000000000000000000000..080122e96cc69c50b2ea791a1a65b1e31807261e --- /dev/null +++ b/PySDM/source/.binder/postBuild @@ -0,0 +1,7 @@ +#!/bin/bash + +# mimicking what happens on Colab: packages are fetched from PyPI, only notebooks from the repo + +set -e +shopt -s extglob +rm -rfv !("examples"|"tutorials") diff --git a/PySDM/source/.binder/requirements.txt b/PySDM/source/.binder/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..ac83d89b47b164f7b310eabc7c08e5b8e76020fa --- /dev/null +++ b/PySDM/source/.binder/requirements.txt @@ -0,0 +1 @@ +PySDM-examples diff --git a/PySDM/source/.codecov.yml b/PySDM/source/.codecov.yml new file mode 100644 index 0000000000000000000000000000000000000000..b07361e52eef7921e9feedda7013231ec9133476 --- /dev/null +++ b/PySDM/source/.codecov.yml @@ -0,0 +1,5 @@ +coverage: + status: + project: + default: + threshold: 0.1% diff --git a/PySDM/source/.pre-commit-config.yaml b/PySDM/source/.pre-commit-config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..1460baf4cda5d2c8dd0e18651d331dd270dc2498 --- /dev/null +++ b/PySDM/source/.pre-commit-config.yaml @@ -0,0 +1,37 @@ +files: '.py|.json' +exclude: '.git' +default_stages: [pre-commit] + +repos: + - repo: https://github.com/psf/black + rev: 25.1.0 + hooks: + - id: black + +# - repo: https://github.com/timothycrosley/isort +# rev: 5.13.2 +# hooks: +# - id: isort + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: debug-statements + - id: check-json + files: '.json' + + - repo: https://github.com/lk16/detect-missing-init + rev: v0.1.6 + hooks: + - id: detect-missing-init + args: ['--create', "--track", "--python-folders", "tests,PySDM,examples/PySDM_examples"] + + # TODO #1794 + # - repo: https://github.com/open-atmos/devops_tests + # rev: v0.0.2 + # hooks: + # - id: check-badges + # name: check badges + # args: [--fix-header, --repo-name=PySDM] diff --git a/PySDM/source/.zenodo.json b/PySDM/source/.zenodo.json new file mode 100644 index 0000000000000000000000000000000000000000..948d2ddeba0f39f8357472b44f992426cd635a09 --- /dev/null +++ b/PySDM/source/.zenodo.json @@ -0,0 +1,161 @@ +{ + "title": "PySDM", + "description": "Pythonic particle-based (super-droplet) warm-rain/aqueous-chemistry cloud microphysics package with box, parcel & 1D/2D prescribed-flow examples in Python, Julia and Matlab", + "creators": [ + { + "affiliation": "AGH University of Krakow, Kraków, Poland (2023-...); University of Illinois at Urbana-Champaign, IL, USA (2021-2022); Jagiellonian University, Kraków, Poland (2018-2023)", + "name": "Arabas, Sylwester", + "orcid": "0000-0003-2361-0082" + }, + { + "affiliation": "California Institute of Technology, Pasadena, CA, USA", + "name": "Azimi, Sajjad", + "orcid": "0000-0002-6329-7775" + }, + { + "affiliation": "University of Washington, Seattle, WA, USA", + "name": "Barr, Jason" + }, + { + "affiliation": "Jagiellonian University, Kraków, Poland", + "name": "Bartman, Piotr", + "orcid": "0000-0003-0265-6428" + }, + { + "affiliation": "California Institute of Technology, Pasadena, CA, USA", + "name": "Bhalla, Brady" + }, + { + "affiliation": "AGH University of Krakow, Poland", + "name": "Bhiogade, Sanket" + }, + { + "affiliation": "Jagiellonian University, Kraków, Poland", + "name": "Bulenok, Oleksii", + "orcid": "0000-0003-2272-8548" + }, + { + "affiliation": "Columbia University, New York, NY, USA", + "name": "Buch, Jatan", + "orcid": "0000-0001-6672-6750" + }, + { + "affiliation": "California Institute of Technology, Pasadena, CA, USA", + "name": "Croci, Caterina" + }, + { + "affiliation": "California Institute of Technology, Pasadena, CA, USA", + "name": "de Jong, Emily", + "orcid": "0000-0002-5310-4554" + }, + { + "affiliation": "Jagiellonian University, Kraków, Poland", + "name": "Derlatka, Kacper" + }, + { + "affiliation": "California Institute of Technology, Pasadena, CA, USA", + "name": "Dula, Isabella" + }, + { + "affiliation": "Jagiellonian University, Kraków, Poland", + "name": "Górski, Kamil" + }, + { + "affiliation": "California Institute of Technology, Pasadena, CA, USA", + "name": "Jaruga, Anna", + "orcid": "0000-0003-3194-6440" + }, + { + "affiliation": "DTN, Aberdeen, Scotland, UK", + "name": "Konstantinov, Boris" + }, + { + "affiliation": "Columbia University, New York, NY, USA", + "name": "Loftus, Kaitlyn", + "orcid": "0000-0002-1927-9326" + }, + { + "affiliation": "Johannes Gutenberg University Mainz, Germany", + "name": "Lüttmer, Tim" + }, + { + "affiliation": "Jagiellonian University, Kraków, Poland", + "name": "Łazarski, Grzegorz", + "orcid": "0000-0002-5595-371X" + }, + { + "affiliation": "California Institute of Technology, Pasadena, CA, USA", + "name": "Mackay, J. Ben", + "orcid": "0000-0001-8677-3562" + }, + { + "affiliation": "AGH University of Krakow, Poland", + "name": "Magnuszewski, Paweł", + "orcid": "0009-0007-9269-6795" + }, + { + "affiliation": "California Institute of Technology, Pasadena, CA, USA", + "name": "Mints, Mikhail" + }, + { + "affiliation": "University of Warsaw, Poland", + "name": "Makulska, Agnieszka" + }, + { + "affiliation": "Jagiellonian University, Kraków, Poland", + "name": "Olesik, Michael", + "orcid": "0000-0002-6319-9358" + }, + { + "affiliation": "Jagiellonian University, Kraków, Poland", + "name": "Piasecki, Bartosz" + }, + { + "affiliation": "AGH University of Krakow, Kraków, Poland", + "name": "Romaniec, Weronika" + }, + { + "affiliation": "NOAA GFDL, Princeton, NJ, USA (2024-); Columbia University, New York, NY, USA (2023-2024); California Institute of Technology, Pasadena, CA, USA (2018-2023)", + "name": "Singer, Clare E.", + "orcid": "0000-0002-1708-0997" + }, + { + "affiliation": "AGH University of Krakow, Kraków, Poland", + "name": "Strząbała, Alksandra" + }, + { + "affiliation": "Jagiellonian University, Kraków, Poland", + "name": "Talar, Aleksandra" + }, + { + "affiliation": "California Institute of Technology, Pasadena, CA, USA", + "name": "Ward, Ryan X.", + "orcid": "0000-0003-2317-3310" + }, + { + "affiliation": "University of California Davis, Davis, CA, USA", + "name": "Ware, Emma C.", + "orcid": "0009-0000-6556-9730" + }, + { + "affiliation": "AGH University of Krakow, Kraków, Poland", + "name": "Wroński, Michał" + }, + { + "affiliation": "AGH University of Krakow, Kraków, Poland", + "name": "Żaba, Agnieszka", + "orcid": "0009-0002-0922-4599" + } + ], + "keywords": [ + "cloud microphysics", + "aerosol microphysics", + "aerosol-cloud interactions", + "atmospheric physics", + "super-droplet method", + "particle-based simulation", + "Monte-Carlo" + ], + "license": "GPL-3.0-only", + "upload_type": "software" +} diff --git a/PySDM/source/LICENSE b/PySDM/source/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..f288702d2fa16d3cdf0035b15a9fcbc552cd88e7 --- /dev/null +++ b/PySDM/source/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/PySDM/source/PySDM/__init__.py b/PySDM/source/PySDM/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..1eff5916907e8b91f72354cbe99f12a75d024550 --- /dev/null +++ b/PySDM/source/PySDM/__init__.py @@ -0,0 +1,20 @@ +# pylint:disable=invalid-name +""" +.. include:: ../docs/markdown/pysdm_landing.md +""" + +from importlib.metadata import PackageNotFoundError, version + +from PySDM.attributes.impl.attribute_registry import register_attribute + +from . import attributes +from . import environments, exporters, products +from .builder import Builder +from .formulae import Formulae +from .particulator import Particulator + +try: + __version__ = version(__name__) +except PackageNotFoundError: + # package is not installed + pass diff --git a/PySDM/source/PySDM/attributes/__init__.py b/PySDM/source/PySDM/attributes/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..ff28bd0edee40daa6fc6729b54cc0fdb3ae112c5 --- /dev/null +++ b/PySDM/source/PySDM/attributes/__init__.py @@ -0,0 +1,9 @@ +""" +Classes representing super-particle attributes +""" + +from .physics import * +from .numerics import * +from .ice import * +from .chemistry import * +from .isotopes import * diff --git a/PySDM/source/PySDM/attributes/chemistry/__init__.py b/PySDM/source/PySDM/attributes/chemistry/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..291a21654e6cbc81e2b99042560ac40d59786bc7 --- /dev/null +++ b/PySDM/source/PySDM/attributes/chemistry/__init__.py @@ -0,0 +1,7 @@ +""" +attributes used by the `PySDM.dynamics.aqueous_chemistry` dynamic +""" + +from .acidity import Acidity +from .concentration import make_concentration_factory +from .hydrogen_ion_concentration import HydrogenIonConcentration diff --git a/PySDM/source/PySDM/attributes/chemistry/acidity.py b/PySDM/source/PySDM/attributes/chemistry/acidity.py new file mode 100644 index 0000000000000000000000000000000000000000..0dc572e9bd760e673422c61d68d6e18c13a1bea2 --- /dev/null +++ b/PySDM/source/PySDM/attributes/chemistry/acidity.py @@ -0,0 +1,44 @@ +""" +pH calculated by finding equilibrium hydrogen ion concentration +""" + +from PySDM.attributes.impl import DerivedAttribute, register_attribute +from PySDM.backends.impl_numba.methods.chemistry_methods import _conc +from PySDM.dynamics.impl.chemistry_utils import AQUEOUS_COMPOUNDS + + +@register_attribute(name="pH") +class Acidity(DerivedAttribute): + def __init__(self, builder): + self.conc = {} + for key, val in AQUEOUS_COMPOUNDS.items(): + if len(val) > 1: + self.conc[key] = builder.get_attribute("conc_" + key) + super().__init__(builder, name="pH", dependencies=self.conc.values()) + self.environment = builder.particulator.environment + self.cell_id = builder.get_attribute("cell id") + + def allocate(self, idx): + super().allocate(idx) + self.data.fill(self.formulae.constants.pH_w) + + def recalculate(self): + dynamic = self.particulator.dynamics["AqueousChemistry"] + + self.particulator.backend.equilibrate_H( + equilibrium_consts=dynamic.equilibrium_consts, + cell_id=self.cell_id.get(), + conc=_conc( + N_mIII=self.conc["N_mIII"].get(), + N_V=self.conc["N_V"].get(), + C_IV=self.conc["C_IV"].get(), + S_IV=self.conc["S_IV"].get(), + S_VI=self.conc["S_VI"].get(), + ), + do_chemistry_flag=dynamic.do_chemistry_flag, + pH=self.data, + H_min=dynamic.pH_H_min, + H_max=dynamic.pH_H_max, + ionic_strength_threshold=dynamic.ionic_strength_threshold, + rtol=dynamic.pH_rtol, + ) diff --git a/PySDM/source/PySDM/attributes/chemistry/concentration.py b/PySDM/source/PySDM/attributes/chemistry/concentration.py new file mode 100644 index 0000000000000000000000000000000000000000..682b961e168a5ca22995ff0814b44cd8d972928e --- /dev/null +++ b/PySDM/source/PySDM/attributes/chemistry/concentration.py @@ -0,0 +1,25 @@ +""" +concentrations (intensive, derived attributes) +""" + +from PySDM.attributes.impl import IntensiveAttribute, register_attribute +from PySDM.attributes.impl.mole_amount import make_mole_amount_factory +from PySDM.dynamics.impl.chemistry_utils import AQUEOUS_COMPOUNDS + + +class ConcentrationImpl(IntensiveAttribute): + def __init__(self, builder, *, what): + super().__init__(builder, name="conc_" + what, base="moles_" + what) + + +def make_concentration_factory(what): + def _factory(builder): + return ConcentrationImpl(builder, what=what) + + return _factory + + +for compound in AQUEOUS_COMPOUNDS: + register_attribute(name=f"conc_{compound}")(make_concentration_factory(compound)) + + register_attribute(name=f"moles_{compound}")(make_mole_amount_factory(compound)) diff --git a/PySDM/source/PySDM/attributes/chemistry/hydrogen_ion_concentration.py b/PySDM/source/PySDM/attributes/chemistry/hydrogen_ion_concentration.py new file mode 100644 index 0000000000000000000000000000000000000000..137236eab689696150a4447edb0f5bc7bbffcad9 --- /dev/null +++ b/PySDM/source/PySDM/attributes/chemistry/hydrogen_ion_concentration.py @@ -0,0 +1,15 @@ +""" +hydrogen ion concentration derived from pH +""" + +from PySDM.attributes.impl import DerivedAttribute, register_attribute + + +@register_attribute(name="conc_H") +class HydrogenIonConcentration(DerivedAttribute): + def __init__(self, builder): + self.acidity = builder.get_attribute("pH") + super().__init__(builder, name="conc_H", dependencies=(self.acidity,)) + + def recalculate(self): + self.data[:] = self.formulae.trivia.pH2H(self.acidity.get().data) diff --git a/PySDM/source/PySDM/attributes/ice/__init__.py b/PySDM/source/PySDM/attributes/ice/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..962b3e78e6788d3715051fabf22aba565ddc6292 --- /dev/null +++ b/PySDM/source/PySDM/attributes/ice/__init__.py @@ -0,0 +1,7 @@ +""" +attributes used by the `PySDM.dynamics.freezing.Freezing` dynamic +""" + +from .cooling_rate import CoolingRate +from .freezing_temperature import FreezingTemperature +from .immersed_surface_area import ImmersedSurfaceArea diff --git a/PySDM/source/PySDM/attributes/ice/cooling_rate.py b/PySDM/source/PySDM/attributes/ice/cooling_rate.py new file mode 100644 index 0000000000000000000000000000000000000000..2de9ed9f3afac406f761e15f0b6cb42af975e76c --- /dev/null +++ b/PySDM/source/PySDM/attributes/ice/cooling_rate.py @@ -0,0 +1,35 @@ +""" +cooling rate estimated as the difference in current and previous grid-cell + temperatures divided by the timestep (i.e. equals zero if particle has not + moved to a different cell since the last timestep) +""" + +import numpy as np +from PySDM.attributes.impl import DerivedAttribute, register_attribute + + +@register_attribute() +class CoolingRate(DerivedAttribute): + def __init__(self, builder): + self.cell_id = builder.get_attribute("cell id") + super().__init__( + builder=builder, name="cooling rate", dependencies=(self.cell_id,) + ) + self.prev_T = builder.particulator.backend.Storage.from_ndarray( + np.full(builder.particulator.n_sd, np.nan) + ) + builder.particulator.observers.append(self) + + def notify(self): + """triggers update to ensure recalculation is done before + overwriting `self.prev_T` with current temperature""" + self.update() + cell_id = self.particulator.attributes["cell id"] + self.prev_T[:] = self.particulator.environment["T"][cell_id] + + def recalculate(self): + cell_id = self.particulator.attributes["cell id"] + env_T = self.particulator.environment["T"] + self.data[:] = env_T[cell_id] + self.data -= self.prev_T + self.data /= -self.particulator.environment.dt diff --git a/PySDM/source/PySDM/attributes/ice/freezing_temperature.py b/PySDM/source/PySDM/attributes/ice/freezing_temperature.py new file mode 100644 index 0000000000000000000000000000000000000000..12613d32710634ea80f0e518c2c385276a9280a6 --- /dev/null +++ b/PySDM/source/PySDM/attributes/ice/freezing_temperature.py @@ -0,0 +1,44 @@ +""" +particle freezing temperature +""" + +from PySDM.attributes.impl import MaximumAttribute, register_attribute +from ..impl import DerivedAttribute + + +@register_attribute() +class FreezingTemperature(MaximumAttribute): + """singular variant: assigned at initialisation, modified through collisions only""" + + def __init__(self, builder): + super().__init__(builder, name="freezing temperature") + + +@register_attribute() +class TemperatureOfLastFreezing(DerivedAttribute): + """time-dependent variant: assigned upon freezing""" + + def __init__(self, builder): + assert "Freezing" in builder.particulator.dynamics + assert ( + builder.particulator.dynamics["Freezing"].immersion_freezing != "singular" + ) + self.signed_water_mass = builder.get_attribute("signed water mass") + self.cell_id = builder.get_attribute("cell id") + super().__init__( + builder, + name="temperature of last freezing", + dependencies=(self.signed_water_mass, self.cell_id), + ) + builder.particulator.observers.append(self) + + def notify(self): + self.update() + + def recalculate(self): + self.particulator.backend.record_freezing_temperatures( + data=self.data, + cell_id=self.cell_id.data, + temperature=self.particulator.environment["T"], + signed_water_mass=self.signed_water_mass.data, + ) diff --git a/PySDM/source/PySDM/attributes/ice/immersed_surface_area.py b/PySDM/source/PySDM/attributes/ice/immersed_surface_area.py new file mode 100644 index 0000000000000000000000000000000000000000..62d2dfbda5237d7f843aeace3c08701e7c43cb31 --- /dev/null +++ b/PySDM/source/PySDM/attributes/ice/immersed_surface_area.py @@ -0,0 +1,12 @@ +""" +immersed INP surface area (assigned at initialisation, modified through collisions only, + used in time-dependent regime) +""" + +from ..impl import ExtensiveAttribute, register_attribute + + +@register_attribute() +class ImmersedSurfaceArea(ExtensiveAttribute): + def __init__(self, particles_builder): + super().__init__(particles_builder, name="immersed surface area") diff --git a/PySDM/source/PySDM/attributes/impl/__init__.py b/PySDM/source/PySDM/attributes/impl/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..fc322b74b03c928376269dc2e7e0b12a45222eb5 --- /dev/null +++ b/PySDM/source/PySDM/attributes/impl/__init__.py @@ -0,0 +1,14 @@ +""" +common code intended for use from within attribute classes (not in user code) +""" + +from .attribute import Attribute +from .base_attribute import BaseAttribute +from .cell_attribute import CellAttribute +from .derived_attribute import DerivedAttribute +from .dummy_attribute import DummyAttribute +from .extensive_attribute import ExtensiveAttribute +from .maximum_attribute import MaximumAttribute +from .attribute_registry import register_attribute, get_attribute_class +from .intensive_attribute import IntensiveAttribute +from .temperature_variation_option_attribute import TemperatureVariationOptionAttribute diff --git a/PySDM/source/PySDM/attributes/impl/attribute.py b/PySDM/source/PySDM/attributes/impl/attribute.py new file mode 100644 index 0000000000000000000000000000000000000000..24818607639b7a49e669123943f7157a7905fcab --- /dev/null +++ b/PySDM/source/PySDM/attributes/impl/attribute.py @@ -0,0 +1,42 @@ +""" +logic around `PySDM.attributes.impl.attribute.Attribute` - the parent class for all attributes +""" + + +class Attribute: + def __init__(self, builder, name, dtype=float, n_vector_components=0): + self.particulator = builder.particulator + self.timestamp: int = 0 + self.data = None + self.dtype = dtype + self.n_vector_components = n_vector_components + self.name = name + self.formulae = self.particulator.formulae + + def allocate(self, idx): + if self.n_vector_components >= 1: + self.data = self.particulator.IndexedStorage.empty( + idx, + (self.n_vector_components, self.particulator.n_sd), + dtype=self.dtype, + ) + else: + self.data = self.particulator.IndexedStorage.empty( + idx, (self.particulator.n_sd,), dtype=self.dtype + ) + + def set_data(self, data): + self.data = data + + def get(self): + self.update() + return self.data + + def update(self): + pass + + def mark_updated(self): + self.timestamp += 1 + + def __str__(self): + return self.name diff --git a/PySDM/source/PySDM/attributes/impl/attribute_registry.py b/PySDM/source/PySDM/attributes/impl/attribute_registry.py new file mode 100644 index 0000000000000000000000000000000000000000..e3fad95fc5eb1e661c5cc4079032b43c0fb02d0b --- /dev/null +++ b/PySDM/source/PySDM/attributes/impl/attribute_registry.py @@ -0,0 +1,62 @@ +"""definition of decorator used to register PySDM attribute classes""" + +import warnings + +from PySDM.impl.camel_case import camel_case_to_words + +_ATTRIBUTES_REGISTRY = {} + + +def _make_dummy_attribute_factory(name): + # pylint: disable=import-outside-toplevel + from PySDM.attributes.impl.dummy_attribute import DummyAttribute + + def _factory(builder): + return DummyAttribute(builder, name=name) + + return _factory + + +def register_attribute(*, name=None, variant=None, dummy_default=False, warn=False): + if variant is not None: + assert name is not None + if dummy_default: + assert variant is not None + if warn: + assert dummy_default + + def decorator(cls): + key = name or camel_case_to_words(cls.__name__) + + if key not in _ATTRIBUTES_REGISTRY: + _ATTRIBUTES_REGISTRY[key] = {} + elif cls in _ATTRIBUTES_REGISTRY[key]: + raise ValueError(f"attribute {key} already exists!") + _ATTRIBUTES_REGISTRY[key][cls] = variant or (lambda _, __: cls) + if dummy_default: + _ATTRIBUTES_REGISTRY[key][_make_dummy_attribute_factory(key)] = ( + lambda _, __: ( + warnings.warn( + f"dummy implementation used for requested attribute named '{name}'" + ) + if warn + else None + ) + is None + and not variant(_, __) + ) + return cls + + return decorator + + +def get_attribute_class(name, dynamics=None, formulae=None): + if name not in _ATTRIBUTES_REGISTRY: + raise ValueError( + f"Unknown attribute name: {name};" + f" valid names: {', '.join(sorted(_ATTRIBUTES_REGISTRY))}" + ) + for cls, func in _ATTRIBUTES_REGISTRY[name].items(): + if func(dynamics, formulae): + return cls + assert False diff --git a/PySDM/source/PySDM/attributes/impl/base_attribute.py b/PySDM/source/PySDM/attributes/impl/base_attribute.py new file mode 100644 index 0000000000000000000000000000000000000000..5027be06ecf9e9e3a40162a7d50d1253d37995c0 --- /dev/null +++ b/PySDM/source/PySDM/attributes/impl/base_attribute.py @@ -0,0 +1,17 @@ +""" +logic around `PySDM.attributes.impl.base_attribute.BaseAttribute` - the parent class +for non-derived attributes +""" + +from .attribute import Attribute + + +class BaseAttribute(Attribute): + def __init__(self, builder, name, dtype=float, n_vector_components=0): + super().__init__( + builder, name=name, dtype=dtype, n_vector_components=n_vector_components + ) + + def init(self, data): + self.data.upload(data) + self.mark_updated() diff --git a/PySDM/source/PySDM/attributes/impl/cell_attribute.py b/PySDM/source/PySDM/attributes/impl/cell_attribute.py new file mode 100644 index 0000000000000000000000000000000000000000..dd62d7a82b0666b166ac6428891af04aba425600 --- /dev/null +++ b/PySDM/source/PySDM/attributes/impl/cell_attribute.py @@ -0,0 +1,10 @@ +""" +logic around `PySDM.attributes.impl.cell_attribute.CellAttribute` - the parent class +for grid-particle mapping attributes +""" + +from .base_attribute import BaseAttribute + + +class CellAttribute(BaseAttribute): + pass diff --git a/PySDM/source/PySDM/attributes/impl/derived_attribute.py b/PySDM/source/PySDM/attributes/impl/derived_attribute.py new file mode 100644 index 0000000000000000000000000000000000000000..bee08a6e4018cb44c135f6d65e4827eccc222d3e --- /dev/null +++ b/PySDM/source/PySDM/attributes/impl/derived_attribute.py @@ -0,0 +1,29 @@ +""" +logic around `PySDM.attributes.impl.derived_attribute.DerivedAttribute` - the parent class +for all derived attributes +""" + +from .attribute import Attribute + + +class DerivedAttribute(Attribute): + def __init__(self, builder, name, dependencies): + assert len(dependencies) > 0 + super().__init__(builder, name) + self.dependencies = dependencies + + def update(self): + for dependency in self.dependencies: + dependency.update() + dependencies_timestamp = sum( + dependency.timestamp for dependency in self.dependencies + ) + if self.timestamp < dependencies_timestamp: + self.timestamp = dependencies_timestamp + self.recalculate() + + def recalculate(self): + raise NotImplementedError() + + def mark_updated(self): + raise AssertionError() diff --git a/PySDM/source/PySDM/attributes/impl/dummy_attribute.py b/PySDM/source/PySDM/attributes/impl/dummy_attribute.py new file mode 100644 index 0000000000000000000000000000000000000000..9c8fa50eb6798dbfbd48090c9f561991a6cb2fca --- /dev/null +++ b/PySDM/source/PySDM/attributes/impl/dummy_attribute.py @@ -0,0 +1,18 @@ +"""logic around `PySDM.attributes.impl.dummy_attribute.DummyAttribute` - parent class +for do-nothing attributes""" + +import numpy as np + +from .attribute import Attribute + + +class DummyAttribute(Attribute): + def __init__(self, builder, name): + super().__init__(builder, name) + + def allocate(self, idx): + super().allocate(idx) + self.data[:] = np.nan + + def get(self): + return self.data diff --git a/PySDM/source/PySDM/attributes/impl/extensive_attribute.py b/PySDM/source/PySDM/attributes/impl/extensive_attribute.py new file mode 100644 index 0000000000000000000000000000000000000000..e729e33e210a87863974f63ed112bc234f81314c --- /dev/null +++ b/PySDM/source/PySDM/attributes/impl/extensive_attribute.py @@ -0,0 +1,10 @@ +""" +logic around `PySDM.attributes.impl.extensive_attribute.ExtensiveAttribute` - parent class + for all extensive attributes +""" + +from .base_attribute import BaseAttribute + + +class ExtensiveAttribute(BaseAttribute): + pass diff --git a/PySDM/source/PySDM/attributes/impl/intensive_attribute.py b/PySDM/source/PySDM/attributes/impl/intensive_attribute.py new file mode 100644 index 0000000000000000000000000000000000000000..12dccaa762a14a7e08ab2802381f040482f304d8 --- /dev/null +++ b/PySDM/source/PySDM/attributes/impl/intensive_attribute.py @@ -0,0 +1,16 @@ +""" +logic around `PySDM.attributes.impl.intensive_attribute.IntensiveAttribute` - parent class + for all intensive attributes +""" + +from .derived_attribute import DerivedAttribute + + +class IntensiveAttribute(DerivedAttribute): + def __init__(self, builder, name: str, base: str): + self.volume = builder.get_attribute("volume") + self.base = builder.get_attribute(base) + super().__init__(builder, name, dependencies=(self.volume, self.base)) + + def recalculate(self): + self.data.ratio(self.base.get(), self.volume.get()) diff --git a/PySDM/source/PySDM/attributes/impl/maximum_attribute.py b/PySDM/source/PySDM/attributes/impl/maximum_attribute.py new file mode 100644 index 0000000000000000000000000000000000000000..19ef877620eed7ed5b7a6de22deb25f1bdf6d866 --- /dev/null +++ b/PySDM/source/PySDM/attributes/impl/maximum_attribute.py @@ -0,0 +1,12 @@ +""" +logic around `PySDM.attributes.impl.maximum_attribute.MaximumAttribute` - parent class + for attributes for which under coalescence the newly collided particle's attribute + value is set to maximum of values of colliding particle (e.g., freezing temperature + in singular immersion freezing) +""" + +from .base_attribute import BaseAttribute + + +class MaximumAttribute(BaseAttribute): + pass diff --git a/PySDM/source/PySDM/attributes/impl/mole_amount.py b/PySDM/source/PySDM/attributes/impl/mole_amount.py new file mode 100644 index 0000000000000000000000000000000000000000..657a4498fbd2c234c415714230899b681c7e583c --- /dev/null +++ b/PySDM/source/PySDM/attributes/impl/mole_amount.py @@ -0,0 +1,17 @@ +""" +mole amounts (extensive, base attributes) +""" + +from PySDM.attributes.impl.extensive_attribute import ExtensiveAttribute + + +class MoleAmountImpl(ExtensiveAttribute): + def __init__(self, builder, *, name): + super().__init__(builder, name=name) + + +def make_mole_amount_factory(compound): + def _factory(builder): + return MoleAmountImpl(builder, name="moles_" + compound) + + return _factory diff --git a/PySDM/source/PySDM/attributes/impl/temperature_variation_option_attribute.py b/PySDM/source/PySDM/attributes/impl/temperature_variation_option_attribute.py new file mode 100644 index 0000000000000000000000000000000000000000..6a3add92c4e3bd818d4ca159211cb0fab7969839 --- /dev/null +++ b/PySDM/source/PySDM/attributes/impl/temperature_variation_option_attribute.py @@ -0,0 +1,18 @@ +"""common code for attributes offering an option to neglect temperature variation, +intended for use with Parcel environment only""" + + +class TemperatureVariationOptionAttribute: # pylint: disable=too-few-public-methods + """base class""" + + def __init__(self, builder, neglect_temperature_variations: bool): + if neglect_temperature_variations: + assert builder.particulator.environment.mesh.dimension == 0 + self.neglect_temperature_variations = neglect_temperature_variations + self.initial_temperature = ( + builder.particulator.Storage.from_ndarray( + builder.particulator.environment["T"].to_ndarray() + ) + if neglect_temperature_variations + else None + ) diff --git a/PySDM/source/PySDM/attributes/isotopes/__init__.py b/PySDM/source/PySDM/attributes/isotopes/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..26875f6fe3c941f7587e798f187dfaa612ea8499 --- /dev/null +++ b/PySDM/source/PySDM/attributes/isotopes/__init__.py @@ -0,0 +1,6 @@ +""" +isotopic fractionation related attributes +""" + +from .moles import Moles1H, Moles16O, MolesLightWater +from ..isotopes import delta diff --git a/PySDM/source/PySDM/attributes/isotopes/delta.py b/PySDM/source/PySDM/attributes/isotopes/delta.py new file mode 100644 index 0000000000000000000000000000000000000000..df8c19ca79b9085e707089f943478d3121562807 --- /dev/null +++ b/PySDM/source/PySDM/attributes/isotopes/delta.py @@ -0,0 +1,50 @@ +""" +per-droplet isotopic ratio of heavy-to-light isotope number concentrations +expressed vs the VSMOW reference in SI units (i.e., not per mille) +""" + +from PySDM.attributes.impl import DerivedAttribute, register_attribute +from PySDM.dynamics.isotopic_fractionation import HEAVY_ISOTOPES + + +class DeltaImpl(DerivedAttribute): + def __init__(self, builder, *, heavy_isotope): + assert heavy_isotope[:-1].isnumeric() + if heavy_isotope[-1] == "H": + light_isotope = "1H" + elif heavy_isotope[-1] == "O": + light_isotope = "16O" + else: + raise NotImplementedError() + + self.heavy_isotope = builder.get_attribute(f"moles_{heavy_isotope}") + self.light_isotope = builder.get_attribute(f"moles_{light_isotope}") + super().__init__( + builder, + name="delta_" + heavy_isotope, + dependencies=( + self.heavy_isotope, + self.light_isotope, + ), + ) + + self.reference_ratio = getattr( + self.formulae.constants, f"VSMOW_R_{heavy_isotope}" + ) + + def recalculate(self): + self.data.ratio(self.heavy_isotope.get(), self.light_isotope.get()) + self.particulator.backend.isotopic_delta( + self.data, self.data, self.reference_ratio + ) + + +def make_delta_factory(what): + def _factory(builder): + return DeltaImpl(builder, heavy_isotope=what) + + return _factory + + +for isotope in HEAVY_ISOTOPES: + register_attribute(name=f"delta_{isotope}")(make_delta_factory(isotope)) diff --git a/PySDM/source/PySDM/attributes/isotopes/moles.py b/PySDM/source/PySDM/attributes/isotopes/moles.py new file mode 100644 index 0000000000000000000000000000000000000000..d592a0bfb4b1510982271d4187f8ea51dfe992fd --- /dev/null +++ b/PySDM/source/PySDM/attributes/isotopes/moles.py @@ -0,0 +1,92 @@ +""" +derived attributes providing amounts of light isotopes in water (1H and 16O) + + (water mass) = ( + moles_H2O * (2 * molar_mass_1H + molar_mass_16O) + + moles_2H * (molar_mass_1H + molar_mass_2H + molar_mass_16O) + + moles_3H * (molar_mass_1H + molar_mass_3H + molar_mass_16O) + + moles_17O * (2 * molar_mass_1H + molar_mass_17O) + + moles_18O * (2 * molar_mass_1H + molar_mass_18O) + ) + + moles_H2O = ( + water_mass + - moles_2H * (molar_mass_1H + molar_mass_2H + molar_mass_16O) + - moles_3H * (molar_mass_1H + molar_mass_3H + molar_mass_16O) + - moles_17O * (2 * molar_mass_1H + molar_mass_17O) + - moles_18O * (2 * molar_mass_1H + molar_mass_18O) + ) / (2 * molar_mass_1H + molar_mass_16O) + + moles_1H = 2 * (moles_H2O + moles_17O + moles_18O) + moles_2H + moles_3H + moles_16O = moles_2H + moles_3H + moles_H2O +""" + +from PySDM.attributes.impl import DerivedAttribute, register_attribute +from PySDM.attributes.impl.mole_amount import make_mole_amount_factory +from PySDM.dynamics.isotopic_fractionation import HEAVY_ISOTOPES + + +class Helper(DerivedAttribute): + def __init__(self, builder, name, attrs_to_multiplier): + self.attrs_to_multiplier = attrs_to_multiplier + super().__init__( + builder=builder, + name=name, + dependencies=attrs_to_multiplier.keys(), + ) + + def recalculate(self): + self.data.fill(0) + for attr, mult in self.attrs_to_multiplier.items(): + self.data += (mult, "*", attr.data) + + +@register_attribute() +class MolesLightWater(Helper): + def __init__(self, builder): + const = builder.formulae.constants + super().__init__( + builder=builder, + name="moles light water", + attrs_to_multiplier={ + builder.get_attribute("moles_2H"): -const.M_2H_1H_16O / const.M_1H2_16O, + builder.get_attribute("moles_3H"): -const.M_3H_1H_16O / const.M_1H2_16O, + builder.get_attribute("moles_17O"): -const.M_1H2_17O / const.M_1H2_16O, + builder.get_attribute("moles_18O"): -const.M_1H2_18O / const.M_1H2_16O, + builder.get_attribute("signed water mass"): 1 / const.M_1H2_16O, + }, + ) + + +@register_attribute(name="moles_1H") +class Moles1H(Helper): + def __init__(self, builder): + super().__init__( + builder=builder, + name="moles_1H", + attrs_to_multiplier={ + builder.get_attribute("moles_17O"): 2.0, + builder.get_attribute("moles_18O"): 2.0, + builder.get_attribute("moles_2H"): 1.0, + builder.get_attribute("moles_3H"): 1.0, + builder.get_attribute("moles light water"): 2.0, + }, + ) + + +@register_attribute(name="moles_16O") +class Moles16O(Helper): + def __init__(self, builder): + super().__init__( + builder=builder, + name="moles_16O", + attrs_to_multiplier={ + builder.get_attribute("moles_2H"): 1.0, + builder.get_attribute("moles_3H"): 1.0, + builder.get_attribute("moles light water"): 1.0, + }, + ) + + +for isotope in HEAVY_ISOTOPES: + register_attribute(name=f"moles_{isotope}")(make_mole_amount_factory(isotope)) diff --git a/PySDM/source/PySDM/attributes/numerics/__init__.py b/PySDM/source/PySDM/attributes/numerics/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..0f936849a61667264e96da5b28081172c35c04e5 --- /dev/null +++ b/PySDM/source/PySDM/attributes/numerics/__init__.py @@ -0,0 +1,7 @@ +""" +attributes used for tracking cell-particle mapping in multi-dimensional simulations +""" + +from .cell_id import CellId +from .cell_origin import CellOrigin +from .position_in_cell import PositionInCell diff --git a/PySDM/source/PySDM/attributes/numerics/cell_id.py b/PySDM/source/PySDM/attributes/numerics/cell_id.py new file mode 100644 index 0000000000000000000000000000000000000000..1bc02df6cb5d75699667a3ece85884d48a11071e --- /dev/null +++ b/PySDM/source/PySDM/attributes/numerics/cell_id.py @@ -0,0 +1,14 @@ +""" +grid cell id attribute +""" + +from PySDM.attributes.impl import CellAttribute, register_attribute + + +@register_attribute() +class CellId(CellAttribute): + def __init__(self, builder): + super().__init__(builder, name="cell id", dtype=int) + + def recalculate(self): + pass diff --git a/PySDM/source/PySDM/attributes/numerics/cell_origin.py b/PySDM/source/PySDM/attributes/numerics/cell_origin.py new file mode 100644 index 0000000000000000000000000000000000000000..65288a750bffb01a642153b53debe0b3932b19c7 --- /dev/null +++ b/PySDM/source/PySDM/attributes/numerics/cell_origin.py @@ -0,0 +1,16 @@ +""" +grid-cell origin (multi-dimensional) +""" + +from PySDM.attributes.impl import CellAttribute, register_attribute + + +@register_attribute() +class CellOrigin(CellAttribute): + def __init__(self, builder): + super().__init__( + builder, + name="cell origin", + dtype=int, + n_vector_components=builder.particulator.mesh.dim, + ) diff --git a/PySDM/source/PySDM/attributes/numerics/position_in_cell.py b/PySDM/source/PySDM/attributes/numerics/position_in_cell.py new file mode 100644 index 0000000000000000000000000000000000000000..081c60b12beff14d0a1943374a8eda7b2e91b96b --- /dev/null +++ b/PySDM/source/PySDM/attributes/numerics/position_in_cell.py @@ -0,0 +1,15 @@ +""" +position-within-cell attribute (multi-dimensional, values normalised to one) +""" + +from PySDM.attributes.impl import CellAttribute, register_attribute + + +@register_attribute() +class PositionInCell(CellAttribute): + def __init__(self, builder): + super().__init__( + builder, + name="position in cell", + n_vector_components=builder.particulator.mesh.dim, + ) diff --git a/PySDM/source/PySDM/attributes/physics/__init__.py b/PySDM/source/PySDM/attributes/physics/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..699deab9c369e0ef7a18b4334903e4eb9137171f --- /dev/null +++ b/PySDM/source/PySDM/attributes/physics/__init__.py @@ -0,0 +1,21 @@ +""" +attributes carrying information on particle physical properties +""" + +from .area import Area +from .critical_saturation import CriticalSaturation +from .critical_volume import CriticalVolume, WetToCriticalVolumeRatio +from .dry_radius import DryRadius +from .dry_volume import DryVolume +from .equilibrium_saturation import EquilibriumSaturation +from .heat import Heat +from .hygroscopicity import Kappa, KappaTimesDryVolume +from .water_mass import SignedWaterMass +from .multiplicity import Multiplicity +from .radius import Radius, SquareRootOfRadius +from .relative_fall_velocity import RelativeFallMomentum, RelativeFallVelocity +from .temperature import Temperature +from .terminal_velocity import TerminalVelocity +from .volume import Volume +from .reynolds_number import ReynoldsNumber +from .diffusional_growth_mass_change import DiffusionalGrowthMassChange diff --git a/PySDM/source/PySDM/attributes/physics/area.py b/PySDM/source/PySDM/attributes/physics/area.py new file mode 100644 index 0000000000000000000000000000000000000000..aff69601a88472b75a8ad46df8fa1339fe1f7680 --- /dev/null +++ b/PySDM/source/PySDM/attributes/physics/area.py @@ -0,0 +1,18 @@ +""" +particle wet radius (calculated from the volume) +""" + +from PySDM.attributes.impl import DerivedAttribute, register_attribute + + +@register_attribute() +class Area(DerivedAttribute): + def __init__(self, builder): + self.volume = builder.get_attribute("volume") + dependencies = [self.volume] + super().__init__(builder, name="area", dependencies=dependencies) + + def recalculate(self): + self.data.product(self.volume.get(), 1 / self.formulae.constants.PI_4_3) + self.data **= 2 / 3 + self.data *= self.formulae.constants.PI_4_3 * 3 diff --git a/PySDM/source/PySDM/attributes/physics/critical_saturation.py b/PySDM/source/PySDM/attributes/physics/critical_saturation.py new file mode 100644 index 0000000000000000000000000000000000000000..b7fa81bf3cc2d16003a6c978fbaea026d0cbcd3e --- /dev/null +++ b/PySDM/source/PySDM/attributes/physics/critical_saturation.py @@ -0,0 +1,54 @@ +""" +kappa-Koehler critical saturation calculated for either initial or actual environment temperature +""" + +from PySDM.attributes.impl import ( + DerivedAttribute, + register_attribute, + TemperatureVariationOptionAttribute, +) + + +@register_attribute() +class CriticalSaturation(DerivedAttribute, TemperatureVariationOptionAttribute): + def __init__(self, builder, neglect_temperature_variations=False): + assert builder.particulator.mesh.dimension == 0 + + self.v_crit = builder.get_attribute("critical volume") + self.v_dry = builder.get_attribute("dry volume") + self.kappa = builder.get_attribute("kappa") + self.f_org = builder.get_attribute("dry volume organic fraction") + TemperatureVariationOptionAttribute.__init__( + self, builder, neglect_temperature_variations + ) + DerivedAttribute.__init__( + self, + builder=builder, + name="critical saturation", + dependencies=(self.v_crit, self.kappa, self.v_dry, self.f_org), + ) + + def recalculate(self): + temperature = ( + self.initial_temperature + if self.neglect_temperature_variations + else self.particulator.environment["T"] + ) + r_cr = self.formulae.trivia.radius(self.v_crit.data.data) + rd3 = self.v_dry.data.data / self.formulae.constants.PI_4_3 + sgm = self.formulae.surface_tension.sigma( + temperature.data, + self.v_crit.data.data, + self.v_dry.data.data, + self.f_org.data.data, + ) + + self.data.data[:] = self.formulae.hygroscopicity.RH_eq( + r_cr, T=temperature.data, kp=self.kappa.data.data, rd3=rd3, sgm=sgm + ) + + +@register_attribute() +class CriticalSaturationNeglectingTemperatureVariations(CriticalSaturation): + def __init__(self, builder): + super().__init__(builder, neglect_temperature_variations=True) diff --git a/PySDM/source/PySDM/attributes/physics/critical_volume.py b/PySDM/source/PySDM/attributes/physics/critical_volume.py new file mode 100644 index 0000000000000000000000000000000000000000..70ea4cb818caa3e266dea50dbb5c4ba6e3c97558 --- /dev/null +++ b/PySDM/source/PySDM/attributes/physics/critical_volume.py @@ -0,0 +1,88 @@ +""" +critical wet volume (kappa-Koehler, computed using actual or initial temperature) +""" + +from PySDM.attributes.impl import ( + DerivedAttribute, + register_attribute, + TemperatureVariationOptionAttribute, +) + + +@register_attribute() +class CriticalVolume(DerivedAttribute, TemperatureVariationOptionAttribute): + def __init__(self, builder, neglect_temperature_variations=False): + self.cell_id = builder.get_attribute("cell id") + self.v_dry = builder.get_attribute("dry volume") + self.v_wet = builder.get_attribute("volume") + self.kappa = builder.get_attribute("kappa") + self.f_org = builder.get_attribute("dry volume organic fraction") + self.environment = builder.particulator.environment + self.particles = builder.particulator + + dependencies = [self.v_dry, self.v_wet, self.cell_id] + TemperatureVariationOptionAttribute.__init__( + self, builder, neglect_temperature_variations + ) + DerivedAttribute.__init__( + self, builder, name="critical volume", dependencies=dependencies + ) + + def recalculate(self): + temperature = ( + self.initial_temperature + if self.neglect_temperature_variations + else self.environment["T"] + ) + self.particulator.backend.critical_volume( + v_cr=self.data, + kappa=self.kappa.get(), + f_org=self.f_org.get(), + v_dry=self.v_dry.get(), + v_wet=self.v_wet.get(), + T=temperature, + cell=self.cell_id.get(), + ) + + +@register_attribute() +class CriticalVolumeNeglectingTemperatureVariations(CriticalVolume): + def __init__(self, builder): + super().__init__(builder, neglect_temperature_variations=True) + + +@register_attribute() +class WetToCriticalVolumeRatio(DerivedAttribute): + def __init__( + self, + builder, + neglect_temperature_variations=False, + name="wet to critical volume ratio", + ): + self.critical_volume = builder.get_attribute( + "critical volume" + + ( + " neglecting temperature variations" + if neglect_temperature_variations + else "" + ) + ) + self.volume = builder.get_attribute("volume") + super().__init__( + builder, + name=name, + dependencies=(self.critical_volume, self.volume), + ) + + def recalculate(self): + self.data.ratio(self.volume.get(), self.critical_volume.get()) + + +@register_attribute() +class WetToCriticalVolumeRatioNeglectingTemperatureVariations(WetToCriticalVolumeRatio): + def __init__(self, builder): + super().__init__( + builder, + neglect_temperature_variations=True, + name="wet to critical volume ratio neglecting temperature variations", + ) diff --git a/PySDM/source/PySDM/attributes/physics/diffusional_growth_mass_change.py b/PySDM/source/PySDM/attributes/physics/diffusional_growth_mass_change.py new file mode 100644 index 0000000000000000000000000000000000000000..d68093fa582cadcda22c36698754d8c1087d83f6 --- /dev/null +++ b/PySDM/source/PySDM/attributes/physics/diffusional_growth_mass_change.py @@ -0,0 +1,36 @@ +"""diagnosed diffusional mass change within a timestep +(temporarily without support for collisional processes), developed to enable +clean coupling between condensation and other dynamics (e.g., isotopic +fractionation)""" + +from PySDM.attributes.impl import register_attribute, DerivedAttribute + + +@register_attribute() +class DiffusionalGrowthMassChange(DerivedAttribute): + def __init__(self, builder): + self.water_mass = builder.get_attribute("signed water mass") + super().__init__( + builder, + name="diffusional growth mass change", + dependencies=(self.water_mass,), + ) + self.old = None + assert "Collision" not in builder.particulator.dynamics + for triggers in ( + builder.particulator.observers, + builder.particulator.initialisers, + ): + triggers.append(self) + + def setup(self): + self.old = self.water_mass.data.data.copy() + self.data.data[:] = 0 + + def notify(self): + new = self.water_mass.data.data + self.data.data[:] = new - self.old + self.old[:] = new + + def recalculate(self): + pass diff --git a/PySDM/source/PySDM/attributes/physics/dry_radius.py b/PySDM/source/PySDM/attributes/physics/dry_radius.py new file mode 100644 index 0000000000000000000000000000000000000000..ff317eafeaebbf217ec914a2209b5f3bbef50387 --- /dev/null +++ b/PySDM/source/PySDM/attributes/physics/dry_radius.py @@ -0,0 +1,17 @@ +""" +particle dry radius computed from dry volume +""" + +from PySDM.attributes.impl import DerivedAttribute, register_attribute + + +@register_attribute() +class DryRadius(DerivedAttribute): + def __init__(self, builder): + self.volume_dry = builder.get_attribute("dry volume") + dependencies = [self.volume_dry] + super().__init__(builder, name="dry radius", dependencies=dependencies) + + def recalculate(self): + self.data.product(self.volume_dry.get(), 1 / self.formulae.constants.PI_4_3) + self.data **= 1 / 3 diff --git a/PySDM/source/PySDM/attributes/physics/dry_volume.py b/PySDM/source/PySDM/attributes/physics/dry_volume.py new file mode 100644 index 0000000000000000000000000000000000000000..957f1332048559c894f29c658c9ab9cfa379bc8d --- /dev/null +++ b/PySDM/source/PySDM/attributes/physics/dry_volume.py @@ -0,0 +1,63 @@ +""" +particle dry volume (subject to evolution due to collisions or aqueous chemistry) +""" + +from PySDM.attributes.impl import ( + DerivedAttribute, + ExtensiveAttribute, + register_attribute, +) + + +@register_attribute( + name="dry volume", variant=lambda dynamics, _: "AqueousChemistry" in dynamics +) +class DryVolumeDynamic(DerivedAttribute): + def __init__(self, builder): + self.particulator = builder.particulator + self.moles_sulphur_p6 = builder.get_attribute("moles_S_VI") + super().__init__( + builder, name="dry volume", dependencies=(self.moles_sulphur_p6,) + ) + + def recalculate(self): + dynamic = self.particulator.dynamics["AqueousChemistry"] + self.data.fill(self.moles_sulphur_p6.data) + self.data *= dynamic.dry_molar_mass / dynamic.dry_rho + + +@register_attribute( + name="dry volume", variant=lambda dynamics, _: "AqueousChemistry" not in dynamics +) +class DryVolume(ExtensiveAttribute): + def __init__(self, builder): + super().__init__(builder, name="dry volume") + + +@register_attribute( + name="dry volume organic", + variant=lambda _, formulae: formulae.surface_tension.__name__ != "Constant", + dummy_default=True, +) +class DryVolumeOrganic(ExtensiveAttribute): + def __init__(self, builder): + super().__init__(builder, name="dry volume organic") + + +@register_attribute( + name="dry volume organic fraction", + variant=lambda _, formulae: formulae.surface_tension.__name__ != "Constant", + dummy_default=True, +) +class OrganicFraction(DerivedAttribute): + def __init__(self, builder): + self.volume_dry_org = builder.get_attribute("dry volume organic") + self.volume_dry = builder.get_attribute("dry volume") + super().__init__( + builder, + name="dry volume organic fraction", + dependencies=(self.volume_dry_org, self.volume_dry), + ) + + def recalculate(self): + self.data.ratio(self.volume_dry_org.get(), self.volume_dry.get()) diff --git a/PySDM/source/PySDM/attributes/physics/equilibrium_saturation.py b/PySDM/source/PySDM/attributes/physics/equilibrium_saturation.py new file mode 100644 index 0000000000000000000000000000000000000000..c374aca0106a2d1a4e2b521471be2d92e0842dfe --- /dev/null +++ b/PySDM/source/PySDM/attributes/physics/equilibrium_saturation.py @@ -0,0 +1,41 @@ +""" +kappa-Koehler equilibrium saturation calculated for actual environment temperature +""" + +from PySDM.attributes.impl import DerivedAttribute, register_attribute + + +@register_attribute() +class EquilibriumSaturation(DerivedAttribute): + def __init__(self, builder): + self.r_wet = builder.get_attribute("radius") + self.v_wet = builder.get_attribute("volume") + self.v_dry = builder.get_attribute("dry volume") + self.kappa = builder.get_attribute("kappa") + self.f_org = builder.get_attribute("dry volume organic fraction") + + super().__init__( + builder=builder, + name="equilibrium saturation", + dependencies=(self.kappa, self.v_dry, self.f_org, self.r_wet), + ) + + def recalculate(self): + if len(self.particulator.environment["T"]) != 1: + raise NotImplementedError() + temperature = self.particulator.environment["T"][0] + rd3 = self.v_dry.data.data / self.formulae.constants.PI_4_3 + sgm = self.formulae.surface_tension.sigma( + temperature, + self.v_wet.data.data, + self.v_dry.data.data, + self.f_org.data.data, + ) + + self.data.data[:] = self.formulae.hygroscopicity.RH_eq( + self.r_wet.data.data, + T=temperature, + kp=self.kappa.data.data, + rd3=rd3, + sgm=sgm, + ) diff --git a/PySDM/source/PySDM/attributes/physics/heat.py b/PySDM/source/PySDM/attributes/physics/heat.py new file mode 100644 index 0000000000000000000000000000000000000000..e07731a5e7241125fcbe16f09c381f9966f6c209 --- /dev/null +++ b/PySDM/source/PySDM/attributes/physics/heat.py @@ -0,0 +1,11 @@ +""" +particle heat content (test-use only for now, exemplifying intensive/extensive attribute logic) +""" + +from PySDM.attributes.impl import ExtensiveAttribute, register_attribute + + +@register_attribute() +class Heat(ExtensiveAttribute): + def __init__(self, builder): + super().__init__(builder, name="heat") diff --git a/PySDM/source/PySDM/attributes/physics/hygroscopicity.py b/PySDM/source/PySDM/attributes/physics/hygroscopicity.py new file mode 100644 index 0000000000000000000000000000000000000000..eafab22aa0cd3f6c92f9c4f70e192accccfb81a3 --- /dev/null +++ b/PySDM/source/PySDM/attributes/physics/hygroscopicity.py @@ -0,0 +1,25 @@ +""" +kappa-Koehler hygroscopicity representation: + the `PySDM.attributes.physics.hygroscopicity.KappaTimesDryVolume` base attribute + and the derived `PySDM.attributes.physics.hygroscopicity.Kappa` attribute +""" + +from ..impl import DerivedAttribute, ExtensiveAttribute, register_attribute + + +@register_attribute() +class KappaTimesDryVolume(ExtensiveAttribute): + def __init__(self, builder): + super().__init__(builder, name="kappa times dry volume") + + +@register_attribute() +class Kappa(DerivedAttribute): + def __init__(self, builder): + self.kappa_times_dry_volume = builder.get_attribute("kappa times dry volume") + self.dry_volume = builder.get_attribute("dry volume") + deps = (self.kappa_times_dry_volume, self.dry_volume) + super().__init__(builder, name="kappa", dependencies=deps) + + def recalculate(self): + self.data.ratio(self.kappa_times_dry_volume.get(), self.dry_volume.get()) diff --git a/PySDM/source/PySDM/attributes/physics/multiplicity.py b/PySDM/source/PySDM/attributes/physics/multiplicity.py new file mode 100644 index 0000000000000000000000000000000000000000..6425c97ed0a1de48245d49dfb1a389d0db0d7a63 --- /dev/null +++ b/PySDM/source/PySDM/attributes/physics/multiplicity.py @@ -0,0 +1,16 @@ +""" +super-particle multiplicity (aka weighting factor) - the number of real-world particles + represented in the simulation with a given super particle +""" + +import numpy as np +from PySDM.attributes.impl import BaseAttribute, register_attribute + + +@register_attribute() +class Multiplicity(BaseAttribute): + TYPE = np.int64 + MAX_VALUE = np.iinfo(TYPE).max + + def __init__(self, builder): + super().__init__(builder, name="multiplicity", dtype=Multiplicity.TYPE) diff --git a/PySDM/source/PySDM/attributes/physics/radius.py b/PySDM/source/PySDM/attributes/physics/radius.py new file mode 100644 index 0000000000000000000000000000000000000000..3b42e7772f43e1e091af5b0ce8d00149256bec7b --- /dev/null +++ b/PySDM/source/PySDM/attributes/physics/radius.py @@ -0,0 +1,33 @@ +""" +particle wet radius (calculated from the volume) +""" + +from PySDM.attributes.impl import DerivedAttribute, register_attribute + + +@register_attribute() +class Radius(DerivedAttribute): + def __init__(self, builder): + self.volume = builder.get_attribute("volume") + dependencies = [self.volume] + super().__init__(builder, name="radius", dependencies=dependencies) + + def recalculate(self): + self.data.product(self.volume.get(), 1 / self.formulae.constants.PI_4_3) + self.data **= 1 / 3 + + +@register_attribute() +class SquareRootOfRadius(DerivedAttribute): + def __init__(self, builder): + self.radius = builder.get_attribute("radius") + + super().__init__( + builder, + name="square root of radius", + dependencies=(self.radius,), + ) + + def recalculate(self): + self.data.fill(self.radius.data) + self.data **= 0.5 diff --git a/PySDM/source/PySDM/attributes/physics/relative_fall_velocity.py b/PySDM/source/PySDM/attributes/physics/relative_fall_velocity.py new file mode 100644 index 0000000000000000000000000000000000000000..d047cad4b120d17957753ba6b3751837aaa52bd5 --- /dev/null +++ b/PySDM/source/PySDM/attributes/physics/relative_fall_velocity.py @@ -0,0 +1,41 @@ +""" +Attributes for tracking particle velocity +""" + +from PySDM.attributes.impl import ( + DerivedAttribute, + ExtensiveAttribute, + register_attribute, +) + + +@register_attribute( + name="relative fall momentum", + variant=lambda dynamics, _: "RelaxedVelocity" in dynamics, + dummy_default=True, + warn=True, + # note: could eventually make an attribute that calculates momentum + # from terminal velocity instead when no RelaxedVelocity dynamic is present +) +class RelativeFallMomentum(ExtensiveAttribute): + def __init__(self, builder): + super().__init__(builder, name="relative fall momentum", dtype=float) + + +@register_attribute( + name="relative fall velocity", + variant=lambda dynamics, _: "RelaxedVelocity" in dynamics, +) +class RelativeFallVelocity(DerivedAttribute): + def __init__(self, builder): + self.momentum = builder.get_attribute("relative fall momentum") + self.signed_water_mass = builder.get_attribute("signed water mass") + + super().__init__( + builder, + name="relative fall velocity", + dependencies=(self.momentum, self.signed_water_mass), + ) + + def recalculate(self): + self.data.ratio(self.momentum.get(), self.signed_water_mass.get()) diff --git a/PySDM/source/PySDM/attributes/physics/reynolds_number.py b/PySDM/source/PySDM/attributes/physics/reynolds_number.py new file mode 100644 index 0000000000000000000000000000000000000000..0853f1d1b672bbcd8285d4a7c4f56002e28a29cb --- /dev/null +++ b/PySDM/source/PySDM/attributes/physics/reynolds_number.py @@ -0,0 +1,32 @@ +""" +particle Reynolds number +""" + +from ..impl import DerivedAttribute, register_attribute + + +@register_attribute( + name="Reynolds number", + variant=lambda _, formulae: formulae.ventilation.__name__ != "Neglect", + dummy_default=True, +) +class ReynoldsNumber(DerivedAttribute): + def __init__(self, builder): + self.radius = builder.get_attribute("radius") + self.velocity_wrt_air = builder.get_attribute("relative fall velocity") + self.cell_id = builder.get_attribute("cell id") + super().__init__( + builder, + name="Reynolds number", + dependencies=(self.radius, self.velocity_wrt_air, self.cell_id), + ) + + def recalculate(self): + self.particulator.backend.reynolds_number( + output=self.data, + cell_id=self.cell_id.get(), + dynamic_viscosity=self.particulator.environment["air dynamic viscosity"], + density=self.particulator.environment["air density"], + radius=self.radius.data, + velocity_wrt_air=self.velocity_wrt_air.data, + ) diff --git a/PySDM/source/PySDM/attributes/physics/temperature.py b/PySDM/source/PySDM/attributes/physics/temperature.py new file mode 100644 index 0000000000000000000000000000000000000000..491730d5f7f7070afbcb1445860fbb1f7486194b --- /dev/null +++ b/PySDM/source/PySDM/attributes/physics/temperature.py @@ -0,0 +1,11 @@ +""" +particle temperature (test-use only for now, exemplifying intensive/extensive attribute logic) +""" + +from PySDM.attributes.impl import IntensiveAttribute, register_attribute + + +@register_attribute() +class Temperature(IntensiveAttribute): + def __init__(self, builder): + super().__init__(builder, base="heat", name="temperature") diff --git a/PySDM/source/PySDM/attributes/physics/terminal_velocity.py b/PySDM/source/PySDM/attributes/physics/terminal_velocity.py new file mode 100644 index 0000000000000000000000000000000000000000..2a4171f1bf66c55895018357718cd1ccc107f1eb --- /dev/null +++ b/PySDM/source/PySDM/attributes/physics/terminal_velocity.py @@ -0,0 +1,39 @@ +""" +particle terminal velocity (used for collision probability and particle displacement) +""" + +from PySDM.attributes.impl import DerivedAttribute, register_attribute + + +@register_attribute( + name="relative fall velocity", + variant=lambda dynamics, _: "RelaxedVelocity" not in dynamics, +) +@register_attribute() +class TerminalVelocity(DerivedAttribute): + def __init__(self, builder): + self.radius = builder.get_attribute("radius") + self.signed_water_mass = builder.get_attribute("signed water mass") + self.cell_id = builder.get_attribute("cell id") + dependencies = [self.radius, self.signed_water_mass, self.cell_id] + super().__init__(builder, name="terminal velocity", dependencies=dependencies) + + self.approximation_liquid = builder.formulae.terminal_velocity_class( + builder.particulator + ) + self.approximation_ice = builder.formulae.terminal_velocity_ice_class( + builder.particulator + ) + + def recalculate(self): + self.approximation_liquid(self.data, self.radius.get()) + # TODO #1605 order of functions calls changes result. approximation_liquid will override + # approximation_ice when mixed-phase spheres shape active + if self.formulae.particle_shape_and_density.supports_mixed_phase(): + self.approximation_ice( + self.data, + self.signed_water_mass.get(), + self.cell_id.get(), + self.particulator.environment["T"], + self.particulator.environment["p"], + ) diff --git a/PySDM/source/PySDM/attributes/physics/volume.py b/PySDM/source/PySDM/attributes/physics/volume.py new file mode 100644 index 0000000000000000000000000000000000000000..d3ce82558a198cdb44bb78878e82b930f480c9c7 --- /dev/null +++ b/PySDM/source/PySDM/attributes/physics/volume.py @@ -0,0 +1,17 @@ +""" +particle volume (derived from water mass); +in simulation involving mixed-phase clouds, positive values correspond to +liquid water and negative values to ice +""" + +from PySDM.attributes.impl import DerivedAttribute, register_attribute + + +@register_attribute() +class Volume(DerivedAttribute): + def __init__(self, builder): + self.water_mass = builder.get_attribute("water mass") + super().__init__(builder, name="volume", dependencies=(self.water_mass,)) + + def recalculate(self): + self.particulator.backend.volume_of_water_mass(self.data, self.water_mass.get()) diff --git a/PySDM/source/PySDM/attributes/physics/water_mass.py b/PySDM/source/PySDM/attributes/physics/water_mass.py new file mode 100644 index 0000000000000000000000000000000000000000..b46312c1dc776e34d2ccea9cdc83d27e2187b123 --- /dev/null +++ b/PySDM/source/PySDM/attributes/physics/water_mass.py @@ -0,0 +1,63 @@ +""" +particle mass attributes +in simulations involving mixed-phase clouds, positive values correspond to +liquid water and negative values to ice +""" + +from PySDM.attributes.impl import ( + ExtensiveAttribute, + DerivedAttribute, + register_attribute, +) + + +@register_attribute() +class SignedWaterMass(ExtensiveAttribute): + def __init__(self, builder): + super().__init__(builder, name="signed water mass") + + +@register_attribute( + name="water mass", + variant=lambda _, formulae: not formulae.particle_shape_and_density.supports_mixed_phase(), +) +class ViewWaterMass(DerivedAttribute): + def __init__(self, builder): + self.signed_water_mass = builder.get_attribute("signed water mass") + + super().__init__( + builder, + name="water mass", + dependencies=(self.signed_water_mass,), + ) + + def mark_updated(self): + self.signed_water_mass.mark_updated() + + def allocate(self, idx): + pass + + def recalculate(self): + pass + + def get(self): + return self.signed_water_mass.data + + +@register_attribute( + name="water mass", + variant=lambda _, formulae: formulae.particle_shape_and_density.supports_mixed_phase(), +) +class AbsWaterMass(DerivedAttribute): + def __init__(self, builder): + self.signed_water_mass = builder.get_attribute("signed water mass") + + super().__init__( + builder, + name="water mass", + dependencies=(self.signed_water_mass,), + ) + + def recalculate(self): + self.data.fill(self.signed_water_mass.data) + self.data.abs() diff --git a/PySDM/source/PySDM/backends/__init__.py b/PySDM/source/PySDM/backends/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3ec1b6e4929e91dee927c18ef4e899ea1ae4993a --- /dev/null +++ b/PySDM/source/PySDM/backends/__init__.py @@ -0,0 +1,95 @@ +""" +Number-crunching backends +""" + +import ctypes +from functools import partial +import os +import sys +import warnings + +from numba import cuda + +from . import numba as _numba + +# for pdoc +CPU = None +GPU = None +Numba = _numba.Numba +ThrustRTC = None + + +# https://gist.github.com/f0k/63a664160d016a491b2cbea15913d549 +def _cuda_is_available(): + lib_names = ("libcuda.so", "libcuda.dylib", "cuda.dll") + for libname in lib_names: + try: + cuda_lib = ctypes.CDLL(libname) + except OSError: + continue + else: + break + else: + return False + + result = cuda_lib.cuInit(0) + if result != 0: # cuda.h: CUDA_SUCCESS = 0 + error_str = ctypes.c_char_p() + cuda_lib.cuGetErrorString(result, ctypes.byref(error_str)) + # pylint: disable=no-member + warnings.warn( + f"CUDA library found but cuInit() failed (error code: {result};" + f" message: {error_str.value.decode()})" + ) + if "google.colab" in sys.modules: + warnings.warn( + "to use GPU on Colab set hardware accelerator to 'GPU' before session start" + ' in the "Runtime :: Change runtime type :: Hardware accelerator" menu' + ) + return False + + return True + + +if "CI" not in os.environ and (_cuda_is_available() or cuda.is_available()): + from PySDM.backends.thrust_rtc import ThrustRTC +else: + from .impl_thrust_rtc.test_helpers import flag + + flag.fakeThrustRTC = True + + import numpy as np + + from PySDM.backends.impl_common.random_common import ( # pylint: disable=ungrouped-imports + RandomCommon, + ) + from PySDM.backends.thrust_rtc import ThrustRTC # pylint: disable=ungrouped-imports + + ThrustRTC.ENABLE = False + + class Random(RandomCommon): # pylint: disable=too-few-public-methods + def __init__(self, size, seed): + super().__init__(size, seed) + self.generator = np.random.default_rng(seed) + + def __call__(self, storage): + # pylint: disable=unsupported-assignment-operation + storage.data.ndarray[:] = self.generator.uniform(0, 1, storage.shape) + + ThrustRTC.Random = Random + +_BACKEND_CACHE = {} + + +def _cached_backend(formulae=None, backend_class=None, **kwargs): + key = backend_class.__name__ + ":" + str(formulae) + ":" + str(kwargs) + if key not in _BACKEND_CACHE: + _BACKEND_CACHE[key] = backend_class(formulae=formulae, **kwargs) + return _BACKEND_CACHE[key] + + +CPU = partial(_cached_backend, backend_class=Numba) +""" returns a cached instance of the Numba backend (cache key including formulae parameters) """ + +GPU = partial(_cached_backend, backend_class=ThrustRTC) +""" returns a cached instance of the ThrustRTC backend (cache key including formulae parameters) """ diff --git a/PySDM/source/PySDM/backends/impl_common/__init__.py b/PySDM/source/PySDM/backends/impl_common/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69c4440f25e0375ff402cbd98b5e1648ea7632f --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_common/__init__.py @@ -0,0 +1 @@ +"""common code for all backends""" diff --git a/PySDM/source/PySDM/backends/impl_common/backend_methods.py b/PySDM/source/PySDM/backends/impl_common/backend_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..dc8917b1d91b3ec9f76e2d3019c59d54c3cca8c1 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_common/backend_methods.py @@ -0,0 +1,17 @@ +""" +logic around the `PySDM.backends.impl_common.backend_methods.BackendMethods` - the parent + class for all backend methods classes +""" + + +# pylint: disable=too-few-public-methods +class BackendMethods: + def __init__(self): + if not hasattr(self, "formulae"): + self.formulae = None + if not hasattr(self, "formulae_flattened"): + self.formulae_flattened = None + if not hasattr(self, "Storage"): + self.Storage = None + if not hasattr(self, "default_jit_flags"): + self.default_jit_flags = {} diff --git a/PySDM/source/PySDM/backends/impl_common/freezing_attributes.py b/PySDM/source/PySDM/backends/impl_common/freezing_attributes.py new file mode 100644 index 0000000000000000000000000000000000000000..35fe0c18fbd40c805e055e53820eaaf26cab1e0c --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_common/freezing_attributes.py @@ -0,0 +1,50 @@ +""" +groups of attributes used in either singular or time-dependent immersion freezing regimes, +in threshold or time-dependent homogeneous freezing and for thaw routines +""" + +from collections import namedtuple + + +class SingularAttributes( + namedtuple( + typename="SingularAttributes", + field_names=("freezing_temperature", "signed_water_mass"), + ) +): + """groups attributes required in singular regime""" + + __slots__ = () + + +class TimeDependentAttributes( + namedtuple( + typename="TimeDependentAttributes", + field_names=("immersed_surface_area", "signed_water_mass"), + ) +): + """groups attributes required in time-dependent regime""" + + __slots__ = () + + +class TimeDependentHomogeneousAttributes( + namedtuple( + typename="TimeDependentHomogeneousAttributes", + field_names=("volume", "signed_water_mass"), + ) +): + """groups attributes required in time-dependent regime for homogeneous freezing""" + + __slots__ = () + + +class ThresholdHomogeneousAndThawAttributes( + namedtuple( + typename="ThresholdHomogeneousAttributes", + field_names=("signed_water_mass"), + ) +): + """groups attributes required in time-dependent regime for homogeneous freezing""" + + __slots__ = () diff --git a/PySDM/source/PySDM/backends/impl_common/index.py b/PySDM/source/PySDM/backends/impl_common/index.py new file mode 100644 index 0000000000000000000000000000000000000000..16a4bbfbb5e521ffeb44881456e1dbf298bc91f8 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_common/index.py @@ -0,0 +1,56 @@ +""" +permutation-defining Index class (can be shared between multiple IndexedStorage instances) +""" + +import numpy as np + +from .storage_utils import StorageSignature + + +def make_Index(backend): + class Index(backend.Storage): + def __init__(self, data, length): + assert isinstance(length, int) + super().__init__(StorageSignature(data, length, backend.Storage.INT)) + self.length = backend.Storage.INT(length) + + def __len__(self): + return self.length + + @staticmethod + def identity_index(length): + result = Index.from_ndarray(np.arange(length, dtype=backend.Storage.INT)) + return result + + def reset_index(self): + backend.identity_index(self.data) + + @staticmethod + def empty(*args, **kwargs): + raise TypeError("'Index' class cannot be instantiated as empty.") + + @staticmethod + def from_ndarray(array): + data, array.shape, _ = backend.Storage._get_data_from_ndarray(array) + result = Index(data, array.shape[0]) + return result + + def sort_by_key(self, keys): + backend.sort_by_key(self, keys) + + def shuffle(self, temporary, parts=None): + if parts is None: + backend.shuffle_global( + idx=self.data, length=self.length, u01=temporary.data + ) + else: + backend.shuffle_local( + idx=self.data, u01=temporary.data, cell_start=parts.data + ) + + def remove_zero_n_or_flagged(self, indexed_storage): + self.length = backend.remove_zero_n_or_flagged( + indexed_storage.data, self.data, self.length + ) + + return Index diff --git a/PySDM/source/PySDM/backends/impl_common/indexed_storage.py b/PySDM/source/PySDM/backends/impl_common/indexed_storage.py new file mode 100644 index 0000000000000000000000000000000000000000..673bd5f9d6adf7e2fc9ad79727e5fbcc7a756a39 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_common/indexed_storage.py @@ -0,0 +1,55 @@ +""" +attribute storage class featuring particle permutation logic +""" + +from .storage_utils import StorageSignature + + +def make_IndexedStorage(backend): + class IndexedStorage(backend.Storage): + def __init__(self, idx, signature): + super().__init__(signature) + assert idx is not None + self.idx = idx + + def __len__(self): + return len(self.idx) + + def __getitem__(self, item): + result = backend.Storage.__getitem__(self, item) + if isinstance(result, backend.Storage): + return IndexedStorage.indexed(self.idx, result) + return result + + @staticmethod + def indexed(idx, storage): + return IndexedStorage( + idx, StorageSignature(storage.data, storage.shape, storage.dtype) + ) + + @staticmethod + def empty(idx, shape, dtype): + storage = backend.Storage.empty(shape, dtype) + result = IndexedStorage.indexed(idx, storage) + return result + + @staticmethod + def from_ndarray(idx, array): + storage = backend.Storage.from_ndarray(array) + result = IndexedStorage.indexed(idx, storage) + return result + + def to_ndarray(self, *, raw=False): + result = backend.Storage.to_ndarray(self) + dim = len(self.shape) + if raw: + return result + if dim == 1: + idx = self.idx.to_ndarray() + return result[idx[: len(self)]] + if dim == 2: + idx = self.idx.to_ndarray() + return result[:, idx[: len(self)]] + raise NotImplementedError() + + return IndexedStorage diff --git a/PySDM/source/PySDM/backends/impl_common/pair_indicator.py b/PySDM/source/PySDM/backends/impl_common/pair_indicator.py new file mode 100644 index 0000000000000000000000000000000000000000..922f7e17ea0534026175d0e945d322baf4fdf723 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_common/pair_indicator.py @@ -0,0 +1,19 @@ +""" +storage abstraction layer facilitating pairwise operations (for use with PairwiseStorage class) +""" + + +def make_PairIndicator(backend): + class PairIndicator: + def __init__(self, length): + self.indicator = backend.Storage.empty(length, dtype=bool) + self.length = length + + def __len__(self): + return self.length + + def update(self, cell_start, cell_idx, cell_id): + backend.find_pairs(cell_start, self, cell_id, cell_idx, cell_id.idx) + self.length = len(cell_id) + + return PairIndicator diff --git a/PySDM/source/PySDM/backends/impl_common/pairwise_storage.py b/PySDM/source/PySDM/backends/impl_common/pairwise_storage.py new file mode 100644 index 0000000000000000000000000000000000000000..f49d3457b561e727676025b82c26aad5adbcf4a9 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_common/pairwise_storage.py @@ -0,0 +1,39 @@ +""" +specialised storage equipped with particle pair-handling methods +""" + + +def make_PairwiseStorage(backend): + class PairwiseStorage(backend.Storage): + @staticmethod + def empty(shape, dtype): + result = PairwiseStorage(backend.Storage._get_empty_data(shape, dtype)) + return result + + @staticmethod + def from_ndarray(array): + result = PairwiseStorage(backend.Storage._get_data_from_ndarray(array)) + return result + + def distance(self, other, is_first_in_pair): + backend.distance_pair(self, other, is_first_in_pair, other.idx) + + def max(self, other, is_first_in_pair): + backend.max_pair(self, other, is_first_in_pair, other.idx) + + def min(self, other, is_first_in_pair): + backend.min_pair(self, other, is_first_in_pair, other.idx) + + def sort(self, other, is_first_in_pair): + backend.sort_pair(self, other, is_first_in_pair, other.idx) + + def sum(self, other, is_first_in_pair): + """ + Sums values from `other` for each pair (e.g., drop radius for coalescence kernels). + """ + backend.sum_pair(self, other, is_first_in_pair, other.idx) + + def multiply(self, other, is_first_in_pair): + backend.multiply_pair(self, other, is_first_in_pair, other.idx) + + return PairwiseStorage diff --git a/PySDM/source/PySDM/backends/impl_common/random_common.py b/PySDM/source/PySDM/backends/impl_common/random_common.py new file mode 100644 index 0000000000000000000000000000000000000000..3f2613c83bfbc08c91bb7499debe75e83926b30e --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_common/random_common.py @@ -0,0 +1,10 @@ +""" +common base class for random number generation abstraction layer +""" + + +class RandomCommon: # pylint: disable=too-few-public-methods + def __init__(self, size: int, seed: int): + assert isinstance(size, int) + assert isinstance(seed, int) + self.size = size diff --git a/PySDM/source/PySDM/backends/impl_common/storage_utils.py b/PySDM/source/PySDM/backends/impl_common/storage_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..3b24e6be7dcac990bed82b0a4524251aa844c7c4 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_common/storage_utils.py @@ -0,0 +1,79 @@ +""" +common code for storage classes +""" + +from abc import abstractmethod +from collections import namedtuple +from typing import Type + + +class StorageSignature(namedtuple("StorageSignature", ("data", "shape", "dtype"))): + """groups items defining a storage""" + + __slots__ = () + + +class StorageBase: + def __init__(self, signature: StorageSignature): + self.data = signature.data + self.shape = ( + (signature.shape,) if isinstance(signature.shape, int) else signature.shape + ) + self.dtype = signature.dtype + self.backend = None + + def __len__(self): + return self.shape[0] + + def __pow__(self, other): + raise TypeError("Use **=") + + def __mod__(self, other): + raise TypeError("Use %=") + + def __truediv__(self, other): + raise TypeError("Use /=") + + def __mul__(self, other): + raise TypeError("Use *=") + + def __sub__(self, other): + raise TypeError("Use -=") + + def __add__(self, other): + raise TypeError("Use +=") + + @abstractmethod + def to_ndarray(self): + raise NotImplementedError() + + @abstractmethod + def urand(self, generator): + raise NotImplementedError() + + @abstractmethod + def upload(self, data): + raise NotImplementedError() + + @abstractmethod + def fill(self, other): + raise NotImplementedError() + + +def get_data_from_ndarray(array, storage_class: Type[StorageBase], copy_fun): + if str(array.dtype).startswith("int"): + dtype = storage_class.INT + elif str(array.dtype).startswith("float"): + dtype = storage_class.FLOAT + elif str(array.dtype).startswith("bool"): + dtype = storage_class.BOOL + else: + raise NotImplementedError() + + data = copy_fun(array.astype(dtype)) + + return StorageSignature(data, array.shape, dtype) + + +def empty(shape, dtype, storage_class: Type[StorageBase]): + return storage_class(storage_class._get_empty_data(shape, dtype)) diff --git a/PySDM/source/PySDM/backends/impl_numba/__init__.py b/PySDM/source/PySDM/backends/impl_numba/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..95f2ce8be203a18d103c3903ac4b7b881958f17a --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_numba/__init__.py @@ -0,0 +1 @@ +"""the guts of the CPU backend""" diff --git a/PySDM/source/PySDM/backends/impl_numba/atomic_operations.py b/PySDM/source/PySDM/backends/impl_numba/atomic_operations.py new file mode 100644 index 0000000000000000000000000000000000000000..bc5c942416b6db06a6a37bec6dedad490241c9bf --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_numba/atomic_operations.py @@ -0,0 +1,150 @@ +""" +https://github.com/KatanaGraph/katana/blob/master/python/katana/native_interfacing/numpy_atomic.py +""" + +from numba import types +from numba.core import cgutils +from numba.core.typing.arraydecl import get_array_index_type +from numba.extending import lower_builtin, type_callable +from numba.np.arrayobj import basic_indexing, make_array, normalize_indices + + +def _atomic_rmw( + context, builder, operation, arrayty, val, ptr +): # pylint: disable=too-many-arguments + assert arrayty.aligned # We probably have to have aligned arrays. + dataval = context.get_value_as_data(builder, arrayty.dtype, val) + return builder.atomic_rmw(operation, ptr, dataval, "monotonic") + + +def _declare_atomic_array_op(iop, uop, fop): + def decorator(func): + @type_callable(func) + def func_type(context): + def typer(ary, idx, val): + out = get_array_index_type(ary, idx) + if out is not None: + res = out.result + if context.can_convert(val, res): + return res + return None + + return typer + + _ = func_type + + @lower_builtin(func, types.Buffer, types.Any, types.Any) + # pylint: disable=too-many-locals + def func_impl(context, builder, sig, args): + """ + array[a] = scalar_or_array + array[a,..,b] = scalar_or_array + """ + aryty, idxty, valty = sig.args + ary, idx, val = args + + if isinstance(idxty, types.BaseTuple): + index_types = idxty.types + indices = cgutils.unpack_tuple(builder, idx, count=len(idxty)) + else: + index_types = (idxty,) + indices = (idx,) + + ary = make_array(aryty)(context, builder, ary) + + # First try basic indexing to see if a single array location is denoted. + index_types, indices = normalize_indices( + context, builder, index_types, indices + ) + dataptr, shapes, _strides = basic_indexing( + context, + builder, + aryty, + ary, + index_types, + indices, + boundscheck=context.enable_boundscheck, + ) + if shapes: + raise NotImplementedError("Complex shapes are not supported") + + # Store source value the given location + val = context.cast(builder, val, valty, aryty.dtype) + operation = None + if isinstance(aryty.dtype, types.Integer) and aryty.dtype.signed: + operation = iop + elif isinstance(aryty.dtype, types.Integer) and not aryty.dtype.signed: + operation = uop + elif isinstance(aryty.dtype, types.Float): + operation = fop + if operation is None: + raise TypeError("Atomic operation not supported on " + str(aryty)) + return _atomic_rmw(context, builder, operation, aryty, val, dataptr) + + _ = func_impl + + return func + + return decorator + + +@_declare_atomic_array_op("add", "add", "fadd") +def atomic_add(ary, index, value): + """ + Atomically, perform `ary[i] += v` and return the previous value of `ary[i]`. + + i must be a simple index for a single element of ary. + Broadcasting and vector operations are not supported. + + This should be used from numba compiled code. + """ + orig = ary[index] + ary[index] += value + return orig + + +@_declare_atomic_array_op("sub", "sub", "fsub") +def atomic_sub(ary, index, value): + """ + Atomically, perform `ary[i] -= v` and return the previous value of `ary[i]`. + + i must be a simple index for a single element of ary. + Broadcasting and vector operations are not supported. + + This should be used from numba compiled code. + """ + orig = ary[index] + ary[index] -= value + return orig + + +@_declare_atomic_array_op("max", "umax", None) +def atomic_max(ary, index, value): + """ + Atomically, perform `ary[i] = max(ary[i], v)` and return the previous value of `ary[i]`. + This operation does not support floating-point values. + + i must be a simple index for a single element of ary. + Broadcasting and vector operations are not supported. + + This should be used from numba compiled code. + """ + orig = ary[index] + ary[index] = max(ary[index], value) + return orig + + +@_declare_atomic_array_op("min", "umin", None) +def atomic_min(ary, index, value): + """ + Atomically, perform `ary[i] = min(ary[i], v)` and return the previous value of `ary[i]`. + This operation does not support floating-point values. + + i must be a simple index for a single element of ary. + Broadcasting and vector operations are not supported. + + This should be used from numba compiled code. + """ + orig = ary[index] + ary[index] = min(ary[index], value) + return orig diff --git a/PySDM/source/PySDM/backends/impl_numba/conf.py b/PySDM/source/PySDM/backends/impl_numba/conf.py new file mode 100644 index 0000000000000000000000000000000000000000..78fe3e36e9e0fd9da64b99f1c785deefb0686055 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_numba/conf.py @@ -0,0 +1,10 @@ +""" +default settings for Numba just-in-time compilation +""" + +JIT_FLAGS = { + "parallel": False, + "fastmath": True, + "error_model": "numpy", + "cache": False, # https://github.com/numba/numba/issues/2956 +} diff --git a/PySDM/source/PySDM/backends/impl_numba/methods/__init__.py b/PySDM/source/PySDM/backends/impl_numba/methods/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..4c613462a2bc1424c3b949c68bfa13b4375bc23a --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_numba/methods/__init__.py @@ -0,0 +1,16 @@ +"""method classes of the CPU backend""" + +from .chemistry_methods import ChemistryMethods +from .collisions_methods import CollisionsMethods +from .condensation_methods import CondensationMethods +from .displacement_methods import DisplacementMethods +from .fragmentation_methods import FragmentationMethods +from .freezing_methods import FreezingMethods +from .index_methods import IndexMethods +from .isotope_methods import IsotopeMethods +from .moments_methods import MomentsMethods +from .pair_methods import PairMethods +from .physics_methods import PhysicsMethods +from .terminal_velocity_methods import TerminalVelocityMethods +from .seeding_methods import SeedingMethods +from .deposition_methods import DepositionMethods diff --git a/PySDM/source/PySDM/backends/impl_numba/methods/chemistry_methods.py b/PySDM/source/PySDM/backends/impl_numba/methods/chemistry_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..d4b5956802576ea6fb200b8e12631703063edd1e --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_numba/methods/chemistry_methods.py @@ -0,0 +1,476 @@ +""" +CPU implementation of backend methods for aqueous chemistry +""" + +from collections import namedtuple + +import numba +import numpy as np + +from PySDM.backends.impl_common.backend_methods import BackendMethods +from PySDM.backends.impl_numba import conf +from PySDM.backends.impl_numba.toms748 import toms748_solve +from PySDM.dynamics.impl.chemistry_utils import ( + DIFFUSION_CONST, + DISSOCIATION_FACTORS, + GASEOUS_COMPOUNDS, + MASS_ACCOMMODATION_COEFFICIENTS, + EquilibriumConsts, + HenryConsts, + KineticConsts, + SpecificGravities, + k4, +) +from PySDM.physics.constants import K_H2O + +_MAX_ITER_QUITE_CLOSE = 8 +_MAX_ITER_DEFAULT = 32 +_REALY_CLOSE_THRESHOLD = 1e-6 +_QUITE_CLOSE_THRESHOLD = 1 +_QUITE_CLOSE_MULTIPLIER = 2 + +_K = namedtuple("_K", ("NH3", "SO2", "HSO3", "HSO4", "HCO3", "CO2", "HNO3")) +_conc = namedtuple("_conc", ("N_mIII", "N_V", "C_IV", "S_IV", "S_VI")) + + +class ChemistryMethods(BackendMethods): + def __init__(self): + BackendMethods.__init__(self) + self.HENRY_CONST = HenryConsts(self.formulae) + self.KINETIC_CONST = KineticConsts(self.formulae) + self.EQUILIBRIUM_CONST = EquilibriumConsts(self.formulae) + self.specific_gravities = SpecificGravities(self.formulae.constants) + + def dissolution( # pylint:disable=too-many-locals + self, + *, + n_cell, + n_threads, + cell_order, + cell_start_arg, + idx, + do_chemistry_flag, + mole_amounts, + env_mixing_ratio, + env_T, + env_p, + env_rho_d, + dissociation_factors, + timestep, + dv, + system_type, + droplet_volume, + multiplicity, + ): + assert n_cell == 1 + assert n_threads == 1 + for i in range(n_cell): + cell_id = cell_order[i] + + cell_start = cell_start_arg[cell_id] + cell_end = cell_start_arg[cell_id + 1] + n_sd_in_cell = cell_end - cell_start + if n_sd_in_cell == 0: + continue + + super_droplet_ids = numba.typed.List() + for sd_id in idx[cell_start:cell_end]: + if do_chemistry_flag.data[sd_id]: + super_droplet_ids.append(sd_id) + + if len(super_droplet_ids) == 0: + return + + for key, compound in GASEOUS_COMPOUNDS.items(): + ChemistryMethods.dissolution_body( + super_droplet_ids=super_droplet_ids, + mole_amounts=mole_amounts[key].data, + env_mixing_ratio=env_mixing_ratio[compound][cell_id : cell_id + 1], + henrysConstant=self.HENRY_CONST.HENRY_CONST[compound].at( + env_T[cell_id] + ), + env_p=env_p[cell_id], + env_T=env_T[cell_id], + env_rho_d=env_rho_d[cell_id], + timestep=timestep, + dv=dv, + droplet_volume=droplet_volume.data, + multiplicity=multiplicity.data, + system_type=system_type, + specific_gravity=self.specific_gravities[compound], + alpha=MASS_ACCOMMODATION_COEFFICIENTS[compound], + diffusion_const=DIFFUSION_CONST[compound], + dissociation_factor=dissociation_factors[compound].data, + radius=self.formulae.trivia.radius, + const=self.formulae.constants, + ) + + @staticmethod + @numba.njit(**{**conf.JIT_FLAGS, **{"parallel": False}}) + def dissolution_body( # pylint: disable=too-many-locals + *, + super_droplet_ids, + mole_amounts, + env_mixing_ratio, + henrysConstant, + env_p, + env_T, + env_rho_d, + timestep, + dv, + droplet_volume, + multiplicity, + system_type, + specific_gravity, + alpha, + diffusion_const, + dissociation_factor, + radius, + const, + ): + mole_amount_taken = 0 + for i in super_droplet_ids: + Mc = specific_gravity * const.Md + Rc = const.R_str / Mc + cinf = env_p / env_T / (const.Rd / env_mixing_ratio[0] + Rc) / Mc + r_w = radius(volume=droplet_volume[i]) + v_avg = np.sqrt(8 * const.R_str * env_T / (np.pi * Mc)) + dt_over_scale = timestep / ( + 4 * r_w / (3 * v_avg * alpha) + r_w**2 / (3 * diffusion_const) + ) + A_old = mole_amounts[i] / droplet_volume[i] + H_eff = henrysConstant * dissociation_factor[i] + A_new = (A_old + dt_over_scale * cinf) / ( + 1 + dt_over_scale / H_eff / const.R_str / env_T + ) + new_mole_amount_per_real_droplet = A_new * droplet_volume[i] + assert new_mole_amount_per_real_droplet >= 0 + + mole_amount_taken += multiplicity[i] * ( + new_mole_amount_per_real_droplet - mole_amounts[i] + ) + mole_amounts[i] = new_mole_amount_per_real_droplet + delta_mr = mole_amount_taken * specific_gravity * const.Md / (dv * env_rho_d) + assert delta_mr <= env_mixing_ratio + if system_type == "closed": + env_mixing_ratio -= delta_mr + + def oxidation( # pylint: disable=too-many-locals + self, + *, + n_sd, + cell_ids, + do_chemistry_flag, + k0, + k1, + k2, + k3, + K_SO2, + K_HSO3, + timestep, + droplet_volume, + pH, + dissociation_factor_SO2, + # output + moles_O3, + moles_H2O2, + moles_S_IV, + moles_S_VI, + ): + ChemistryMethods.oxidation_body( + n_sd=n_sd, + cell_ids=cell_ids.data, + do_chemistry_flag=do_chemistry_flag.data, + explicit_euler=self.formulae.trivia.explicit_euler, + pH2H=self.formulae.trivia.pH2H, + k0=k0.data, + k1=k1.data, + k2=k2.data, + k3=k3.data, + K_SO2=K_SO2.data, + K_HSO3=K_HSO3.data, + timestep=timestep, + droplet_volume=droplet_volume.data, + pH=pH.data, + dissociation_factor_SO2=dissociation_factor_SO2.data, + # output + moles_O3=moles_O3.data, + moles_H2O2=moles_H2O2.data, + moles_S_IV=moles_S_IV.data, + moles_S_VI=moles_S_VI.data, + ) + + @staticmethod + @numba.njit(**conf.JIT_FLAGS) + def oxidation_body( # pylint: disable=too-many-locals + *, + n_sd, + cell_ids, + do_chemistry_flag, + explicit_euler, + pH2H, + k0, + k1, + k2, + k3, + K_SO2, + K_HSO3, + timestep, + droplet_volume, + pH, + dissociation_factor_SO2, + # output + moles_O3, + moles_H2O2, + moles_S_IV, + moles_S_VI, + ): + for i in numba.prange(n_sd): # pylint: disable=not-an-iterable + if not do_chemistry_flag[i]: + continue + + cid = cell_ids[i] + H = pH2H(pH[i]) + SO2aq = moles_S_IV[i] / droplet_volume[i] / dissociation_factor_SO2[i] + + # NB: This might not be entirely correct + # https://doi.org/10.1029/JD092iD04p04171 + # https://doi.org/10.5194/acp-16-1693-2016 + + ozone = ( + ( + k0[cid] + + (k1[cid] * K_SO2[cid] / H) + + (k2[cid] * K_SO2[cid] * K_HSO3[cid] / H**2) + ) + * (moles_O3[i] / droplet_volume[i]) + * SO2aq + ) + peroxide = ( + k3[cid] + * K_SO2[cid] + / (1 + k4 * H) + * (moles_H2O2[i] / droplet_volume[i]) + * SO2aq + ) + dt_times_volume = timestep * droplet_volume[i] + + dconc_dt_O3 = -ozone + dconc_dt_S_IV = -(ozone + peroxide) + dconc_dt_H2O2 = -peroxide + dconc_dt_S_VI = ozone + peroxide + + if ( + moles_O3[i] + dconc_dt_O3 * dt_times_volume < 0 + or moles_S_IV[i] + dconc_dt_S_IV * dt_times_volume < 0 + or moles_S_VI[i] + dconc_dt_S_VI * dt_times_volume < 0 + or moles_H2O2[i] + dconc_dt_H2O2 * dt_times_volume < 0 + ): + continue + + moles_O3[i] = explicit_euler(moles_O3[i], dt_times_volume, dconc_dt_O3) + moles_S_IV[i] = explicit_euler( + moles_S_IV[i], dt_times_volume, dconc_dt_S_IV + ) + moles_S_VI[i] = explicit_euler( + moles_S_VI[i], dt_times_volume, dconc_dt_S_VI + ) + moles_H2O2[i] = explicit_euler( + moles_H2O2[i], dt_times_volume, dconc_dt_H2O2 + ) + + def chem_recalculate_drop_data( + self, dissociation_factors, equilibrium_consts, cell_id, pH + ): + for i in range(len(pH)): + H = self.formulae.trivia.pH2H(pH.data[i]) + for key in DIFFUSION_CONST: + dissociation_factors[key].data[i] = DISSOCIATION_FACTORS[key]( + H, equilibrium_consts, cell_id.data[i] + ) + + def chem_recalculate_cell_data( + self, equilibrium_consts, kinetic_consts, temperature + ): + for i in range(len(temperature)): + for key in equilibrium_consts: + equilibrium_consts[key].data[i] = ( + self.EQUILIBRIUM_CONST.EQUILIBRIUM_CONST[key].at( + temperature.data[i] + ) + ) + for key in kinetic_consts: + kinetic_consts[key].data[i] = self.KINETIC_CONST.KINETIC_CONST[key].at( + temperature.data[i] + ) + + def equilibrate_H( + self, + *, + equilibrium_consts, + cell_id, + conc, + do_chemistry_flag, + pH, + H_min, + H_max, + ionic_strength_threshold, + rtol, + ): + ChemistryMethods.equilibrate_H_body( + within_tolerance=self.formulae.trivia.within_tolerance, + pH2H=self.formulae.trivia.pH2H, + H2pH=self.formulae.trivia.H2pH, + cell_id=cell_id.data, + conc=_conc( + N_mIII=conc.N_mIII.data, + N_V=conc.N_V.data, + C_IV=conc.C_IV.data, + S_IV=conc.S_IV.data, + S_VI=conc.S_VI.data, + ), + K=_K( + NH3=equilibrium_consts["K_NH3"].data, + SO2=equilibrium_consts["K_SO2"].data, + HSO3=equilibrium_consts["K_HSO3"].data, + HSO4=equilibrium_consts["K_HSO4"].data, + HCO3=equilibrium_consts["K_HCO3"].data, + CO2=equilibrium_consts["K_CO2"].data, + HNO3=equilibrium_consts["K_HNO3"].data, + ), + # output + do_chemistry_flag=do_chemistry_flag.data, + pH=pH.data, + # params + H_min=H_min, + H_max=H_max, + ionic_strength_threshold=ionic_strength_threshold, + rtol=rtol, + ) + + @staticmethod + @numba.njit(**{**conf.JIT_FLAGS, **{"parallel": False, "cache": False}}) + def equilibrate_H_body( # pylint: disable=too-many-arguments,too-many-locals + within_tolerance, + pH2H, + H2pH, + cell_id, + conc, + K, + do_chemistry_flag, + pH, + # params + H_min, + H_max, + ionic_strength_threshold, + rtol, + ): + for i, pH_i in enumerate(pH): + cid = cell_id[i] + args = ( + _conc( + N_mIII=conc.N_mIII[i], + N_V=conc.N_V[i], + C_IV=conc.C_IV[i], + S_IV=conc.S_IV[i], + S_VI=conc.S_VI[i], + ), + _K( + NH3=K.NH3[cid], + SO2=K.SO2[cid], + HSO3=K.HSO3[cid], + HSO4=K.HSO4[cid], + HCO3=K.HCO3[cid], + CO2=K.CO2[cid], + HNO3=K.HNO3[cid], + ), + ) + a = pH2H(pH_i) + fa = acidity_minfun(a, *args) + if abs(fa) < _REALY_CLOSE_THRESHOLD: + continue + b = np.nan + fb = np.nan + use_default_range = False + if abs(fa) < _QUITE_CLOSE_THRESHOLD: + b = a * _QUITE_CLOSE_MULTIPLIER + fb = acidity_minfun(b, *args) + if fa * fb > 0: + b = a + fb = fa + a = b / _QUITE_CLOSE_MULTIPLIER / _QUITE_CLOSE_MULTIPLIER + fa = acidity_minfun(a, *args) + if fa * fb > 0: + use_default_range = True + else: + use_default_range = True + if use_default_range: + a = H_min + b = H_max + fa = acidity_minfun(a, *args) + fb = acidity_minfun(b, *args) + max_iter = _MAX_ITER_DEFAULT + else: + max_iter = _MAX_ITER_QUITE_CLOSE + H, _iters_taken = toms748_solve( + acidity_minfun, + args, + a, + b, + fa, + fb, + rtol=rtol, + max_iter=max_iter, + within_tolerance=within_tolerance, + ) + assert _iters_taken != max_iter + pH[i] = H2pH(H) + ionic_strength = calc_ionic_strength(H, *args) + do_chemistry_flag[i] = ionic_strength <= ionic_strength_threshold + + +@numba.njit(**{**conf.JIT_FLAGS, **{"parallel": False}}) +def calc_ionic_strength(H, conc, K): + # Directly adapted + # https://github.com/igfuw/libcloudphxx/blob/0b4e2455fba4f95c7387623fc21481a85e7b151f/src/impl/particles_impl_chem_strength.ipp#L50 + # https://en.wikipedia.org/wiki/Ionic_strength + + # H+ and OH- + water = H + K_H2O / H + + # HSO4- and SO4 2- + cz_S_VI = H * conc.S_VI / (H + K.HSO4) + 4 * K.HSO4 * conc.S_VI / (H + K.HSO4) + + # HCO3- and CO3 2- + cz_CO2 = K.CO2 * H * conc.C_IV / ( + H * H + K.CO2 * H + K.CO2 * K.HCO3 + ) + 4 * K.CO2 * K.HCO3 * conc.C_IV / (H * H + K.CO2 * H + K.CO2 * K.HCO3) + + # HSO3- and HSO4 2- + cz_SO2 = K.SO2 * H * conc.S_IV / ( + H * H + K.SO2 * H + K.SO2 * K.HSO3 + ) + 4 * K.SO2 * K.HSO3 * conc.S_IV / (H * H + K.SO2 * H + K.SO2 * K.HSO3) + + # NO3- + cz_HNO3 = K.HNO3 * conc.N_V / (H + K.HNO3) + + # NH4+ + cz_NH3 = K.NH3 * H * conc.N_mIII / (K_H2O + K.NH3 * H) + + return 0.5 * (water + cz_S_VI + cz_CO2 + cz_SO2 + cz_HNO3 + cz_NH3) + + +@numba.njit(**{**conf.JIT_FLAGS, **{"parallel": False}}) +def acidity_minfun(H, conc, K): + ammonia = (conc.N_mIII * H * K.NH3) / (K_H2O + K.NH3 * H) + nitric = conc.N_V * K.HNO3 / (H + K.HNO3) + sulfous = ( + conc.S_IV * K.SO2 * (H + 2 * K.HSO3) / (H * H + H * K.SO2 + K.SO2 * K.HSO3) + ) + water = K_H2O / H + sulfuric = conc.S_VI * (H + 2 * K.HSO4) / (H + K.HSO4) + carbonic = ( + conc.C_IV * K.CO2 * (H + 2 * K.HCO3) / (H * H + H * K.CO2 + K.CO2 * K.HCO3) + ) + zero = H + ammonia - (nitric + sulfous + water + sulfuric + carbonic) + return zero diff --git a/PySDM/source/PySDM/backends/impl_numba/methods/collisions_methods.py b/PySDM/source/PySDM/backends/impl_numba/methods/collisions_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..383644dbb154fec7aa956063a0223adcd043f7b1 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_numba/methods/collisions_methods.py @@ -0,0 +1,782 @@ +""" +CPU implementation of backend methods for particle collisions +""" + +from functools import cached_property +import numba +import numpy as np + +from PySDM.backends.impl_common.backend_methods import BackendMethods +from PySDM.backends.impl_numba import conf +from PySDM.backends.impl_numba.atomic_operations import atomic_add +from PySDM.backends.impl_numba.storage import Storage +from PySDM.backends.impl_numba.warnings import warn + + +@numba.njit(**{**conf.JIT_FLAGS, **{"parallel": False}}) +def pair_indices(i, idx, is_first_in_pair, prob_like): + """given permutation array `idx` and `is_first_in_pair` flag array, + returns indices `j` and `k` of droplets within pair `i` and a `skip_pair` flag, + `j` points to the droplet that is first in pair (higher or equal multiplicity) + output is valid only if `2*i` or `2*i+1` points to a valid pair start index (within one cell) + otherwise the `skip_pair` flag is set to True and returned `j` & `k` indices are set to -1. + In addition, the `prob_like` array is checked for zeros at position `i`, in which case + the `skip_pair` is also set to `True` + """ + skip_pair = False + + if prob_like[i] == 0: + skip_pair = True + j, k = -1, -1 + else: + offset = 1 - is_first_in_pair[2 * i] + j = idx[2 * i + offset] + k = idx[2 * i + 1 + offset] + return j, k, skip_pair + + +@numba.njit(**{**conf.JIT_FLAGS, **{"parallel": False}}) +def flag_zero_multiplicity(j, k, multiplicity, healthy): + if multiplicity[k] == 0 or multiplicity[j] == 0: + healthy[0] = 0 + + +@numba.njit(**{**conf.JIT_FLAGS, **{"parallel": False}}) +def coalesce( # pylint: disable=too-many-arguments + i, j, k, cid, multiplicity, gamma, attributes, coalescence_rate +): + atomic_add(coalescence_rate, cid, gamma[i] * multiplicity[k]) + new_n = multiplicity[j] - gamma[i] * multiplicity[k] + if new_n > 0: + multiplicity[j] = new_n + for a in range(len(attributes)): + attributes[a, k] += gamma[i] * attributes[a, j] + else: # new_n == 0 + multiplicity[j] = multiplicity[k] // 2 + multiplicity[k] = multiplicity[k] - multiplicity[j] + for a in range(len(attributes)): + attributes[a, j] = gamma[i] * attributes[a, j] + attributes[a, k] + attributes[a, k] = attributes[a, j] + + +@numba.njit(**{**conf.JIT_FLAGS, **{"parallel": False}}) +def compute_transfer_multiplicities( + gamma, j, k, multiplicity, particle_mass, fragment_mass_i, max_multiplicity +): # pylint: disable=too-many-arguments + overflow_flag = False + gamma_j_k = 0 + take_from_j_test = multiplicity[k] + take_from_j = 0 + new_mult_k_test = ( + (particle_mass[j] + particle_mass[k]) / fragment_mass_i + ) * multiplicity[k] + new_mult_k = multiplicity[k] + for m in range(int(gamma)): + # check for overflow of multiplicity + if new_mult_k_test > max_multiplicity: + overflow_flag = True + break + + # check for new_n >= 0 + if take_from_j_test > multiplicity[j]: + break + + take_from_j = take_from_j_test + new_mult_k = new_mult_k_test + gamma_j_k = m + 1 + + take_from_j_test += new_mult_k_test + new_mult_k_test = ( + new_mult_k_test * (particle_mass[j] / fragment_mass_i) + new_mult_k_test + ) + + return take_from_j, new_mult_k, gamma_j_k, overflow_flag + + +@numba.njit(**{**conf.JIT_FLAGS, **{"parallel": False}}) +def get_new_multiplicities_and_update_attributes( + j, k, attributes, multiplicity, take_from_j, new_mult_k +): # pylint: disable=too-many-arguments + for a in range(len(attributes)): + attributes[a, k] *= multiplicity[k] + attributes[a, k] += take_from_j * attributes[a, j] + attributes[a, k] /= new_mult_k + + if multiplicity[j] > take_from_j: + nj = multiplicity[j] - take_from_j + nk = new_mult_k + + else: # take_from_j == multiplicity[j] + nj = new_mult_k / 2 + nk = nj + for a in range(len(attributes)): + attributes[a, j] = attributes[a, k] + return nj, nk + + +@numba.njit(**{**conf.JIT_FLAGS, **{"parallel": False}}) +def round_multiplicities_to_ints_and_update_attributes( + j, + k, + nj, + nk, + attributes, + multiplicity, +): # pylint: disable=too-many-arguments + multiplicity[j] = max(round(nj), 1) + multiplicity[k] = max(round(nk), 1) + factor_j = nj / multiplicity[j] + factor_k = nk / multiplicity[k] + for a in range(len(attributes)): + attributes[a, k] *= factor_k + attributes[a, j] *= factor_j + + +@numba.njit(**{**conf.JIT_FLAGS, **{"parallel": False}}) +def break_up( # pylint: disable=too-many-arguments,c,too-many-locals + i, + j, + k, + cid, + multiplicity, + gamma, + attributes, + fragment_mass, + max_multiplicity, + breakup_rate, + breakup_rate_deficit, + warn_overflows, + particle_mass, +): # breakup0 guarantees take_from_j <= multiplicity[j] + take_from_j, new_mult_k, gamma_j_k, overflow_flag = compute_transfer_multiplicities( + gamma[i], + j, + k, + multiplicity, + particle_mass, + fragment_mass[i], + max_multiplicity, + ) + gamma_deficit = gamma[i] - gamma_j_k + + # breakup1 also handles new_n[j] == 0 case via splitting + nj, nk = get_new_multiplicities_and_update_attributes( + j, k, attributes, multiplicity, take_from_j, new_mult_k + ) + + atomic_add(breakup_rate, cid, gamma_j_k * multiplicity[k]) + atomic_add(breakup_rate_deficit, cid, gamma_deficit * multiplicity[k]) + + # breakup2 also guarantees that no multiplicities are set to 0 + round_multiplicities_to_ints_and_update_attributes( + j, k, nj, nk, attributes, multiplicity + ) + if overflow_flag and warn_overflows: + warn("overflow", __file__) + + +@numba.njit(**{**conf.JIT_FLAGS, **{"parallel": False}}) +def break_up_while( + i, + j, + k, + cid, + multiplicity, + gamma, + attributes, + fragment_mass, + max_multiplicity, + breakup_rate, + breakup_rate_deficit, + warn_overflows, + particle_mass, +): # pylint: disable=too-many-arguments,unused-argument,too-many-locals + gamma_deficit = gamma[i] + overflow_flag = False + while gamma_deficit > 0: + if multiplicity[k] == multiplicity[j]: + take_from_j = multiplicity[j] + new_mult_k = ( + (particle_mass[j] + particle_mass[k]) + / fragment_mass[i] + * multiplicity[k] + ) + + # check for overflow + if new_mult_k > max_multiplicity: + atomic_add(breakup_rate_deficit, cid, gamma_deficit * multiplicity[k]) + overflow_flag = True + break + gamma_j_k = gamma_deficit + + else: + if multiplicity[k] > multiplicity[j]: + j, k = k, j + ( + take_from_j, + new_mult_k, + gamma_j_k, + overflow_flag, + ) = compute_transfer_multiplicities( + gamma_deficit, + j, + k, + multiplicity, + particle_mass, + fragment_mass[i], + max_multiplicity, + ) + + nj, nk = get_new_multiplicities_and_update_attributes( + j, k, attributes, multiplicity, take_from_j, new_mult_k + ) + + atomic_add(breakup_rate, cid, gamma_j_k * multiplicity[k]) + gamma_deficit -= gamma_j_k + round_multiplicities_to_ints_and_update_attributes( + j, k, nj, nk, attributes, multiplicity + ) + + atomic_add(breakup_rate_deficit, cid, gamma_deficit * multiplicity[k]) + + if overflow_flag and warn_overflows: + warn("overflow", __file__) + + +class CollisionsMethods(BackendMethods): + @cached_property + def _collision_coalescence_breakup_body(self): + _break_up = break_up_while if self.formulae.handle_all_breakups else break_up + + @numba.njit(**self.default_jit_flags) + def body( + *, + multiplicity, + idx, + length, + attributes, + gamma, + rand, + Ec, + Eb, + fragment_mass, + healthy, + cell_id, + coalescence_rate, + breakup_rate, + breakup_rate_deficit, + is_first_in_pair, + max_multiplicity, + warn_overflows, + particle_mass, + ): + # pylint: disable=not-an-iterable,too-many-nested-blocks,too-many-locals + for i in numba.prange(length // 2): + j, k, skip_pair = pair_indices(i, idx, is_first_in_pair, gamma) + if skip_pair: + continue + bouncing = rand[i] - (Ec[i] + (1 - Ec[i]) * (Eb[i])) > 0 + if bouncing: + continue + + if rand[i] - Ec[i] < 0: + coalesce( + i, + j, + k, + cell_id[j], + multiplicity, + gamma, + attributes, + coalescence_rate, + ) + else: + _break_up( + i, + j, + k, + cell_id[j], + multiplicity, + gamma, + attributes, + fragment_mass, + max_multiplicity, + breakup_rate, + breakup_rate_deficit, + warn_overflows, + particle_mass, + ) + flag_zero_multiplicity(j, k, multiplicity, healthy) + + return body + + @cached_property + def _adaptive_sdm_end_body(self): + @numba.njit(**{**self.default_jit_flags, "parallel": False}) + def body(dt_left, n_cell, cell_start): + end = 0 + for i in range(n_cell - 1, -1, -1): + if dt_left[i] == 0: + continue + end = cell_start[i + 1] + break + return end + + return body + + def adaptive_sdm_end(self, dt_left, cell_start): + return self._adaptive_sdm_end_body(dt_left.data, len(dt_left), cell_start.data) + + @cached_property + def _scale_prob_for_adaptive_sdm_gamma_body(self): + @numba.njit(**self.default_jit_flags) + # pylint: disable=too-many-arguments,too-many-locals + def body( + prob, + idx, + length, + multiplicity, + cell_id, + dt_left, + dt, + dt_range, + is_first_in_pair, + stats_n_substep, + stats_dt_min, + ): + """Modified parameters are: `dt_left[cell_id]`, `prob[pair_id]`, + `stats_n_substep[cell_id]`, `stats_dt_min[cell_id]`; + `dt_todo[cell_id]` is a local temporary array of time steps to be taken in each cell, + which will be equal to `dt` if no adaptive substepping is needed. + `stats_n_substep[cell_id]` gets updated by +1 if a given cell will take another substep. + After a full model step is completed, `stats_n_substep[cell_id]` would be 1 if + no adaptive substepping was needed. + """ + dt_todo = np.empty_like(dt_left) + for cid in numba.prange(len(dt_todo)): # pylint: disable=not-an-iterable + dt_todo[cid] = min(dt_left[cid], dt_range[1]) + for i in range(length // 2): # TODO #571 + j, k, skip_pair = pair_indices(i, idx, is_first_in_pair, prob) + if skip_pair: + continue + prop = multiplicity[j] // multiplicity[k] + dt_optimal = dt * prop / prob[i] + cid = cell_id[j] + dt_optimal = max(dt_optimal, dt_range[0]) + dt_todo[cid] = min(dt_todo[cid], dt_optimal) + stats_dt_min[cid] = min(stats_dt_min[cid], dt_optimal) + for i in numba.prange(length // 2): # pylint: disable=not-an-iterable + j, _, skip_pair = pair_indices(i, idx, is_first_in_pair, prob) + if skip_pair: + continue + prob[i] *= dt_todo[cell_id[j]] / dt + for cid in numba.prange(len(dt_todo)): # pylint: disable=not-an-iterable + dt_left[cid] -= dt_todo[cid] + if dt_todo[cid] > 0: + stats_n_substep[cid] += 1 + + return body + + def scale_prob_for_adaptive_sdm_gamma( + self, + *, + prob, + multiplicity, + cell_id, + dt_left, + dt, + dt_range, + is_first_in_pair, + stats_n_substep, + stats_dt_min, + ): + return self._scale_prob_for_adaptive_sdm_gamma_body( + prob.data, + multiplicity.idx.data, + len(multiplicity), + multiplicity.data, + cell_id.data, + dt_left.data, + dt, + dt_range, + is_first_in_pair.indicator.data, + stats_n_substep.data, + stats_dt_min.data, + ) + + @cached_property + def _cell_id_body(self): + # @numba.njit(**conf.JIT_FLAGS) # note: as of Numba 0.51, np.dot() does not support ints + def body(cell_id, cell_origin, strides): + cell_id[:] = np.dot(strides, cell_origin) + + return body + + def cell_id(self, cell_id, cell_origin, strides): + return self._cell_id_body(cell_id.data, cell_origin.data, strides.data) + + @cached_property + def _collision_coalescence_body(self): + @numba.njit(**self.default_jit_flags) + def body( + *, + multiplicity, + idx, + length, + attributes, + gamma, + healthy, + cell_id, + coalescence_rate, + is_first_in_pair, + ): + for ( + i + ) in numba.prange( # pylint: disable=not-an-iterable,too-many-nested-blocks + length // 2 + ): + j, k, skip_pair = pair_indices(i, idx, is_first_in_pair, gamma) + if skip_pair: + continue + coalesce( + i, + j, + k, + cell_id[j], + multiplicity, + gamma, + attributes, + coalescence_rate, + ) + flag_zero_multiplicity(j, k, multiplicity, healthy) + + return body + + def collision_coalescence( + self, + *, + multiplicity, + idx, + attributes, + gamma, + healthy, + cell_id, + coalescence_rate, + is_first_in_pair, + ): + self._collision_coalescence_body( + multiplicity=multiplicity.data, + idx=idx.data, + length=len(idx), + attributes=attributes.data, + gamma=gamma.data, + healthy=healthy.data, + cell_id=cell_id.data, + coalescence_rate=coalescence_rate.data, + is_first_in_pair=is_first_in_pair.indicator.data, + ) + + def collision_coalescence_breakup( + self, + *, + multiplicity, + idx, + attributes, + gamma, + rand, + Ec, + Eb, + fragment_mass, + healthy, + cell_id, + coalescence_rate, + breakup_rate, + breakup_rate_deficit, + is_first_in_pair, + warn_overflows, + particle_mass, + max_multiplicity, + ): + # pylint: disable=too-many-locals + self._collision_coalescence_breakup_body( + multiplicity=multiplicity.data, + idx=idx.data, + length=len(idx), + attributes=attributes.data, + gamma=gamma.data, + rand=rand.data, + Ec=Ec.data, + Eb=Eb.data, + fragment_mass=fragment_mass.data, + healthy=healthy.data, + cell_id=cell_id.data, + coalescence_rate=coalescence_rate.data, + breakup_rate=breakup_rate.data, + breakup_rate_deficit=breakup_rate_deficit.data, + is_first_in_pair=is_first_in_pair.indicator.data, + max_multiplicity=max_multiplicity, + warn_overflows=warn_overflows, + particle_mass=particle_mass.data, + ) + + @cached_property + def _compute_gamma_body(self): + @numba.njit(**self.default_jit_flags) + # pylint: disable=too-many-arguments,too-many-locals + def body( + prob, + rand, + idx, + length, + multiplicity, + cell_id, + collision_rate_deficit, + collision_rate, + is_first_in_pair, + out, + ): + """ + return in "out" array gamma (see: http://doi.org/10.1002/qj.441, section 5) + formula: + gamma = floor(prob) + 1 if rand < prob - floor(prob) + = floor(prob) if rand >= prob - floor(prob) + + out may point to the same array as prob + """ + for i in numba.prange(length // 2): # pylint: disable=not-an-iterable + out[i] = np.ceil(prob[i] - rand[i]) + j, k, skip_pair = pair_indices(i, idx, is_first_in_pair, out) + if skip_pair: + continue + prop = multiplicity[j] // multiplicity[k] + g = min(int(out[i]), prop) + cid = cell_id[j] + atomic_add(collision_rate, cid, g * multiplicity[k]) + atomic_add( + collision_rate_deficit, cid, (int(out[i]) - g) * multiplicity[k] + ) + out[i] = g + + return body + + def compute_gamma( + self, + *, + prob, + rand, + multiplicity, + cell_id, + collision_rate_deficit, + collision_rate, + is_first_in_pair, + out, + ): + return self._compute_gamma_body( + prob.data, + rand.data, + multiplicity.idx.data, + len(multiplicity), + multiplicity.data, + cell_id.data, + collision_rate_deficit.data, + collision_rate.data, + is_first_in_pair.indicator.data, + out.data, + ) + + @staticmethod + def make_cell_caretaker(idx_shape, idx_dtype, cell_start_len, scheme="default"): + class CellCaretaker: # pylint: disable=too-few-public-methods + def __init__(self, idx_shape, idx_dtype, cell_start_len, scheme): + if scheme == "default": + if conf.JIT_FLAGS["parallel"]: + scheme = "counting_sort_parallel" + else: + scheme = "counting_sort" + self.scheme = scheme + if scheme in ("counting_sort", "counting_sort_parallel"): + self.tmp_idx = Storage.empty(idx_shape, idx_dtype) + if scheme == "counting_sort_parallel": + self.cell_starts = Storage.empty( + ( + numba.config.NUMBA_NUM_THREADS, # pylint: disable=no-member + cell_start_len, + ), + dtype=int, + ) + + def __call__(self, cell_id, cell_idx, cell_start, idx): + length = len(idx) + if self.scheme == "counting_sort": + CollisionsMethods._counting_sort_by_cell_id_and_update_cell_start( + self.tmp_idx.data, + idx.data, + cell_id.data, + cell_idx.data, + length, + cell_start.data, + ) + elif self.scheme == "counting_sort_parallel": + CollisionsMethods._parallel_counting_sort_by_cell_id_and_update_cell_start( + self.tmp_idx.data, + idx.data, + cell_id.data, + cell_idx.data, + length, + cell_start.data, + self.cell_starts.data, + ) + idx.data, self.tmp_idx.data = self.tmp_idx.data, idx.data + + return CellCaretaker(idx_shape, idx_dtype, cell_start_len, scheme) + + @cached_property + def _normalize_body(self): + @numba.njit(**{**self.default_jit_flags, **{"parallel": False}}) + # pylint: disable=too-many-arguments + def body(prob, cell_id, cell_idx, cell_start, norm_factor, timestep, dv): + n_cell = cell_start.shape[0] - 1 + for i in range(n_cell): + sd_num = cell_start[i + 1] - cell_start[i] + if sd_num < 2: + norm_factor[i] = 0 + else: + norm_factor[i] = ( + timestep / dv * sd_num * (sd_num - 1) / 2 / (sd_num // 2) + ) + for d in numba.prange(prob.shape[0]): # pylint: disable=not-an-iterable + prob[d] *= norm_factor[cell_idx[cell_id[d]]] + + return body + + # pylint: disable=too-many-arguments + def normalize(self, prob, cell_id, cell_idx, cell_start, norm_factor, timestep, dv): + return self._normalize_body( + prob.data, + cell_id.data, + cell_idx.data, + cell_start.data, + norm_factor.data, + timestep, + dv, + ) + + @cached_property + def remove_zero_n_or_flagged(self): + @numba.njit(**{**self.default_jit_flags, **{"parallel": False}}) + def body(multiplicity, idx, length) -> int: + flag = len(idx) + new_length = length + i = 0 + while i < new_length: + if idx[i] == flag or multiplicity[idx[i]] == 0: + new_length -= 1 + idx[i] = idx[new_length] + idx[new_length] = flag + else: + i += 1 + return new_length + + return body + + @staticmethod + @numba.njit(**conf.JIT_FLAGS) + # pylint: disable=too-many-arguments + def _counting_sort_by_cell_id_and_update_cell_start( + new_idx, idx, cell_id, cell_idx, length, cell_start + ): + cell_end = cell_start + # Warning: Assuming len(cell_end) == n_cell+1 + cell_end[:] = 0 + for i in range(length): + cell_end[cell_idx[cell_id[idx[i]]]] += 1 + for i in range(1, len(cell_end)): + cell_end[i] += cell_end[i - 1] + for i in range(length - 1, -1, -1): + cell_end[cell_idx[cell_id[idx[i]]]] -= 1 + new_idx[cell_end[cell_idx[cell_id[idx[i]]]]] = idx[i] + + @staticmethod + @numba.njit(**conf.JIT_FLAGS) + # pylint: disable=too-many-arguments + def _parallel_counting_sort_by_cell_id_and_update_cell_start( + new_idx, idx, cell_id, cell_idx, length, cell_start, cell_start_p + ): + cell_end_thread = cell_start_p + # Warning: Assuming len(cell_end) == n_cell+1 + thread_num = cell_end_thread.shape[0] + for t in numba.prange(thread_num): # pylint: disable=not-an-iterable + cell_end_thread[t, :] = 0 + for i in range( + t * length // thread_num, + (t + 1) * length // thread_num if t < thread_num - 1 else length, + ): + cell_end_thread[t, cell_idx[cell_id[idx[i]]]] += 1 + + cell_start[:] = np.sum(cell_end_thread, axis=0) + for i in range(1, len(cell_start)): + cell_start[i] += cell_start[i - 1] + + tmp = cell_end_thread[0, :] + tmp[:] = cell_end_thread[thread_num - 1, :] + cell_end_thread[thread_num - 1, :] = cell_start[:] + for t in range(thread_num - 2, -1, -1): + cell_start[:] = cell_end_thread[t + 1, :] - tmp[:] + tmp[:] = cell_end_thread[t, :] + cell_end_thread[t, :] = cell_start[:] + + for t in numba.prange(thread_num): # pylint: disable=not-an-iterable + for i in range( + ( + (t + 1) * length // thread_num - 1 + if t < thread_num - 1 + else length - 1 + ), + t * length // thread_num - 1, + -1, + ): + cell_end_thread[t, cell_idx[cell_id[idx[i]]]] -= 1 + new_idx[cell_end_thread[t, cell_idx[cell_id[idx[i]]]]] = idx[i] + + cell_start[:] = cell_end_thread[0, :] + + @cached_property + def _linear_collection_efficiency_body(self): + @numba.njit(**self.default_jit_flags) + # pylint: disable=too-many-arguments,too-many-locals + def body(params, output, radii, is_first_in_pair, idx, length, unit): + A, B, D1, D2, E1, E2, F1, F2, G1, G2, G3, Mf, Mg = params + output[:] = 0 + for i in numba.prange(length - 1): # pylint: disable=not-an-iterable + if is_first_in_pair[i]: + if radii[idx[i]] > radii[idx[i + 1]]: + r = radii[idx[i]] / unit + r_s = radii[idx[i + 1]] / unit + else: + r = radii[idx[i + 1]] / unit + r_s = radii[idx[i]] / unit + p = r_s / r + if p not in (0, 1): + G = (G1 / r) ** Mg + G2 + G3 * r + Gp = (1 - p) ** G + if Gp != 0: + D = D1 / r**D2 + E = E1 / r**E2 + F = (F1 / r) ** Mf + F2 + output[i // 2] = A + B * p + D / p**F + E / Gp + output[i // 2] = max(0, output[i // 2]) + + return body + + def linear_collection_efficiency( + self, *, params, output, radii, is_first_in_pair, unit + ): + return self._linear_collection_efficiency_body( + params, + output.data, + radii.data, + is_first_in_pair.indicator.data, + radii.idx.data, + len(is_first_in_pair), + unit, + ) diff --git a/PySDM/source/PySDM/backends/impl_numba/methods/condensation_methods.py b/PySDM/source/PySDM/backends/impl_numba/methods/condensation_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..2014cf191297fd2348e5822288ff873745deda77 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_numba/methods/condensation_methods.py @@ -0,0 +1,704 @@ +""" +CPU implementation of backend methods for water condensation/evaporation +with adaptive timestepping as in [Bartman 2020 (MSc thesis, Section 3.3)](https://www.ap.uj.edu.pl/diplomas/attachments/file/download/125485) +""" # pylint: disable=line-too-long + +from collections import namedtuple +import math +from functools import lru_cache + +import numba +import numpy as np + +from PySDM.backends.impl_common.backend_methods import BackendMethods +from PySDM.backends.impl_numba import conf +from PySDM.backends.impl_numba.toms748 import toms748_solve +from PySDM.backends.impl_numba.warnings import warn + +_Counters = namedtuple( + typename="_Counters", + field_names=("n_substeps", "n_activating", "n_deactivating", "n_ripening"), +) +_Attributes = namedtuple( + typename="_Attributes", + field_names=( + "signed_water_mass", + "v_cr", + "multiplicity", + "vdry", + "kappa", + "f_org", + "reynolds_number", + ), +) +_CellData = namedtuple( + typename="_CellData", + field_names=( + "rhod", + "thd", + "water_vapour_mixing_ratio", + "dv_mean", + "prhod", + "pthd", + "predicted_water_vapour_mixing_ratio", + "air_density", + "air_dynamic_viscosity", + ), +) +_RelativeTolerances = namedtuple( + typename="_RelativeTolerances", field_names=("x", "thd") +) + + +class CondensationMethods(BackendMethods): + # pylint: disable=unused-argument + @staticmethod + def condensation(**kwargs): + n_threads = min(numba.get_num_threads(), kwargs["n_cell"]) + CondensationMethods._condensation( + solver=kwargs["solver"], + n_threads=n_threads, + n_cell=kwargs["n_cell"], + cell_start_arg=kwargs["cell_start_arg"].data, + attributes=_Attributes( + signed_water_mass=kwargs["signed_water_mass"].data, + v_cr=kwargs["v_cr"].data, + multiplicity=kwargs["multiplicity"].data, + vdry=kwargs["vdry"].data, + kappa=kwargs["kappa"].data, + f_org=kwargs["f_org"].data, + reynolds_number=kwargs["reynolds_number"].data, + ), + idx=kwargs["idx"].data, + cell_data=_CellData( + rhod=kwargs["rhod"].data, + thd=kwargs["thd"].data, + water_vapour_mixing_ratio=kwargs["water_vapour_mixing_ratio"].data, + dv_mean=kwargs["dv"], + prhod=kwargs["prhod"].data, + pthd=kwargs["pthd"].data, + predicted_water_vapour_mixing_ratio=( + kwargs["predicted_water_vapour_mixing_ratio"].data + ), + air_density=kwargs["air_density"].data, + air_dynamic_viscosity=kwargs["air_dynamic_viscosity"].data, + ), + rtols=_RelativeTolerances( + x=kwargs["rtol_x"], + thd=kwargs["rtol_thd"], + ), + timestep=kwargs["timestep"], + counters=_Counters( + n_substeps=kwargs["counters"]["n_substeps"].data, + n_activating=kwargs["counters"]["n_activating"].data, + n_deactivating=kwargs["counters"]["n_deactivating"].data, + n_ripening=kwargs["counters"]["n_ripening"].data, + ), + cell_order=kwargs["cell_order"], + RH_max=kwargs["RH_max"].data, + success=kwargs["success"].data, + ) + + @staticmethod + @numba.njit(**{**conf.JIT_FLAGS, **{"cache": False}}) + def _condensation( # pylint: disable=too-many-locals + *, + solver, + n_threads, + n_cell, + cell_start_arg, + attributes, + cell_data, + idx, + rtols, + timestep, + counters, + cell_order, + RH_max, + success, + ): + # arrays within namedtuples in prange loops do not work + # https://github.com/numba/numba/issues/5872 + cdt_predicted_water_vapour_mixing_ratio = ( + cell_data.predicted_water_vapour_mixing_ratio + ) + cdt_pthd = cell_data.pthd + cnt_n_substeps = counters.n_substeps + cnt_n_activating = counters.n_activating + cnt_n_deactivating = counters.n_deactivating + cnt_n_ripening = counters.n_ripening + + for thread_id in numba.prange(n_threads): # pylint: disable=not-an-iterable + for i in range(thread_id, n_cell, n_threads): + cell_id = cell_order[i] + cell_start = cell_start_arg[cell_id] + cell_end = cell_start_arg[cell_id + 1] + n_sd_in_cell = cell_end - cell_start + if n_sd_in_cell == 0: + continue + + ( + success[cell_id], + cdt_predicted_water_vapour_mixing_ratio[cell_id], + cdt_pthd[cell_id], + cnt_n_substeps[cell_id], + cnt_n_activating[cell_id], + cnt_n_deactivating[cell_id], + cnt_n_ripening[cell_id], + RH_max[cell_id], + ) = solver( + attributes=attributes, + cell_idx=idx[cell_start:cell_end], + thd=cell_data.thd[cell_id], + water_vapour_mixing_ratio=cell_data.water_vapour_mixing_ratio[ + cell_id + ], + rhod=cell_data.rhod[cell_id], + dthd_dt=(cell_data.pthd[cell_id] - cell_data.thd[cell_id]) + / timestep, + d_water_vapour_mixing_ratio__dt=( + cell_data.predicted_water_vapour_mixing_ratio[cell_id] + - cell_data.water_vapour_mixing_ratio[cell_id] + ) + / timestep, + drhod_dt=(cell_data.prhod[cell_id] - cell_data.rhod[cell_id]) + / timestep, + m_d=( + (cell_data.prhod[cell_id] + cell_data.rhod[cell_id]) + / 2 + * cell_data.dv_mean + ), + air_density=cell_data.air_density[cell_id], + air_dynamic_viscosity=cell_data.air_dynamic_viscosity[cell_id], + rtols=rtols, + timestep=timestep, + n_substeps=counters.n_substeps[cell_id], + ) + + @staticmethod + def make_adapt_substeps( + *, jit_flags, formulae, timestep, step_fake, dt_range, fuse, multiplier + ): + if not isinstance(multiplier, int): + raise ValueError() + if dt_range[1] > timestep: + dt_range = (dt_range[0], timestep) + if dt_range[0] == 0: + raise NotImplementedError() + n_substeps_max = math.floor(timestep / dt_range[0]) + n_substeps_min = math.ceil(timestep / dt_range[1]) + + @numba.njit(**jit_flags) + def adapt_substeps(step_impl_args, n_substeps, thd, rtol_thd): + n_substeps = np.maximum(n_substeps_min, n_substeps // multiplier) + success = False + for burnout in range(fuse + 1): + if burnout == fuse: + return warn( + "burnout (long)", + __file__, + context=( + "thd", + thd, + ), + return_value=(0, False), + ) + thd_new_long, success = step_fake(step_impl_args, timestep, n_substeps) + if success: + break + n_substeps *= multiplier + for burnout in range(fuse + 1): + if burnout == fuse: + return warn("burnout (short)", __file__, return_value=(0, False)) + thd_new_short, success = step_fake( + step_impl_args, timestep, n_substeps * multiplier + ) + if not success: + return warn("short failed", __file__, return_value=(0, False)) + dthd_long = thd_new_long - thd + dthd_short = thd_new_short - thd + error_estimate = np.abs(dthd_long - multiplier * dthd_short) + thd_new_long = thd_new_short + if formulae.trivia__within_tolerance(error_estimate, thd, rtol_thd): + break + n_substeps *= multiplier + if n_substeps > n_substeps_max: + break + return np.minimum(n_substeps_max, n_substeps), success + + return adapt_substeps + + @staticmethod + def make_step_fake(jit_flags, step_impl): + @numba.njit(**jit_flags) + def step_fake(step_impl_args, dt, n_substeps): + dt /= n_substeps + _, thd_new, _, _, _, _, success = step_impl(*step_impl_args, dt, 1, True) + return thd_new, success + + return step_fake + + @staticmethod + def make_step(jit_flags, step_impl): + @numba.njit(**jit_flags) + def step(step_impl_args, dt, n_substeps): + return step_impl(*step_impl_args, dt, n_substeps, False) + + return step + + @staticmethod + def make_step_impl( + *, + jit_flags, + formulae, + calculate_ml_old, + calculate_ml_new, + ): + @numba.njit(**jit_flags) + def step_impl( # pylint: disable=too-many-arguments,too-many-locals + attributes, + cell_idx, + thd, + water_vapour_mixing_ratio, + rhod, + dthd_dt_pred, + d_water_vapour_mixing_ratio__dt_predicted, + drhod_dt, + m_d, + air_density, + air_dynamic_viscosity, + rtol_x, + timestep, + n_substeps, + fake, + ): + timestep /= n_substeps + ml_old = calculate_ml_old( + attributes.signed_water_mass, attributes.multiplicity, cell_idx + ) + count_activating, count_deactivating, count_ripening = 0, 0, 0 + RH_max = 0 + success = True + for _ in range(n_substeps): + # note: no example yet showing that the trapezoidal scheme brings any improvement + thd += timestep * dthd_dt_pred / 2 + water_vapour_mixing_ratio += ( + timestep * d_water_vapour_mixing_ratio__dt_predicted / 2 + ) + rhod += timestep * drhod_dt / 2 + T = formulae.state_variable_triplet__T(rhod, thd) + p = formulae.state_variable_triplet__p( + rhod, T, water_vapour_mixing_ratio + ) + pv = formulae.state_variable_triplet__pv(p, water_vapour_mixing_ratio) + lv = formulae.latent_heat_vapourisation__lv(T) + pvs = formulae.saturation_vapour_pressure__pvs_water(T) + DTp = formulae.diffusion_thermics__D(T, p) + KTp = formulae.diffusion_thermics__K(T, p) + RH = pv / pvs + Sc = formulae.trivia__air_schmidt_number( + dynamic_viscosity=air_dynamic_viscosity, + diffusivity=DTp, + density=air_density, + ) + ( + ml_new, + success_within_substep, + n_activating, + n_deactivating, + n_ripening, + ) = calculate_ml_new( + attributes, + timestep, + fake, + T, + p, + RH, + Sc, + cell_idx, + lv, + pvs, + DTp, + KTp, + rtol_x, + ) + dml_dt = (ml_new - ml_old) / timestep + d_water_vapour_mixing_ratio__dt_corrected = -dml_dt / m_d + dthd_dt_corr = formulae.state_variable_triplet__dthd_dt( + rhod=rhod, + thd=thd, + T=T, + d_water_vapour_mixing_ratio__dt=d_water_vapour_mixing_ratio__dt_corrected, + lv=lv, + ) + + thd += timestep * (dthd_dt_pred / 2 + dthd_dt_corr) + water_vapour_mixing_ratio += timestep * ( + d_water_vapour_mixing_ratio__dt_predicted / 2 + + d_water_vapour_mixing_ratio__dt_corrected + ) + rhod += timestep * drhod_dt / 2 + ml_old = ml_new + count_activating += n_activating + count_deactivating += n_deactivating + count_ripening += n_ripening + RH_max = max(RH_max, RH) + success = success and success_within_substep + return ( + water_vapour_mixing_ratio, + thd, + count_activating, + count_deactivating, + count_ripening, + RH_max, + success, + ) + + return step_impl + + @staticmethod + def make_calculate_ml_old(jit_flags): + @numba.njit(**jit_flags) + def calculate_ml_old(signed_water_mass, multiplicity, cell_idx): + result = 0 + for drop in cell_idx: + if signed_water_mass[drop] > 0: + result += multiplicity[drop] * signed_water_mass[drop] + return result + + return calculate_ml_old + + @staticmethod + def make_calculate_ml_new( # pylint: disable=too-many-statements + *, + formulae, + jit_flags, + max_iters, + RH_rtol, + ): + @numba.njit(**jit_flags) + def minfun( # pylint: disable=too-many-arguments,too-many-locals + x_new, x_old, timestep, kappa, f_org, rd3, temperature, RH, Fk, Fd + ): + """ + root finding problem for the implicit-in-x Euler step + neglecting dependence of `Fk` and `Fd` on particle size + """ + if x_new > formulae.diffusion_coordinate__x_max(): + return x_old - x_new + mass_new = formulae.diffusion_coordinate__mass(x_new) + volume_new = formulae.particle_shape_and_density__mass_to_volume(mass_new) + r_new = formulae.trivia__radius(volume_new) + RH_eq = formulae.hygroscopicity__RH_eq( + r_new, + temperature, + kappa, + rd3, + formulae.surface_tension__sigma( + temperature, volume_new, formulae.constants.PI_4_3 * rd3, f_org + ), + ) + r_dr_dt = formulae.drop_growth__r_dr_dt(RH_eq=RH_eq, RH=RH, Fk=Fk, Fd=Fd) + dm_dt = formulae.particle_shape_and_density__dm_dt(r=r_new, r_dr_dt=r_dr_dt) + return ( + x_old + - x_new + + timestep * formulae.diffusion_coordinate__dx_dt(mass_new, dm_dt) + ) + + @numba.njit(**jit_flags) + def calculate_ml_new( # pylint: disable=too-many-branches,too-many-arguments,too-many-locals + attributes, + timestep, + fake, + T, + p, + RH, + Sc, + cell_idx, + lv, + pvs, + DTp, + KTp, + rtol_x, + ): + result = 0 + n_activating = 0 + n_deactivating = 0 + n_activated_and_growing = 0 + success = True + lambdaK = formulae.diffusion_kinetics__lambdaK(T, p) + lambdaD = formulae.diffusion_kinetics__lambdaD(DTp, T) + for drop in cell_idx: + if attributes.signed_water_mass[drop] <= 0: + continue + v_drop = formulae.particle_shape_and_density__mass_to_volume( + attributes.signed_water_mass[drop] + ) + x_old = formulae.diffusion_coordinate__x( + attributes.signed_water_mass[drop] + ) + r_old = formulae.trivia__radius(v_drop) + x_insane = formulae.diffusion_coordinate__x( + formulae.particle_shape_and_density__volume_to_mass( + attributes.vdry[drop] / 100 + ) + ) + rd3 = attributes.vdry[drop] / formulae.constants.PI_4_3 + sgm = formulae.surface_tension__sigma( + T, v_drop, attributes.vdry[drop], attributes.f_org[drop] + ) + RH_eq = formulae.hygroscopicity__RH_eq( + r_old, T, attributes.kappa[drop], rd3, sgm + ) + if not formulae.trivia__within_tolerance( + np.abs(RH - RH_eq), RH, RH_rtol + ): + Dr = formulae.diffusion_kinetics__D(DTp, r_old, lambdaD) + Kr = formulae.diffusion_kinetics__K(KTp, r_old, lambdaK) + mass_ventilation_factor = formulae.ventilation__ventilation_coefficient( + sqrt_re_times_cbrt_sc=formulae.trivia__sqrt_re_times_cbrt_sc( + Re=attributes.reynolds_number[drop], + Sc=Sc, + ) + ) + heat_ventilation_factor = mass_ventilation_factor # TODO #1588 + Fk = formulae.drop_growth__Fk( + T=T, K=Kr * heat_ventilation_factor, lv=lv + ) + Fd = formulae.drop_growth__Fd( + T=T, D=Dr * mass_ventilation_factor, pvs=pvs + ) + minfun_args = ( + x_old, + timestep, + attributes.kappa[drop], + attributes.f_org[drop], + rd3, + T, + RH, + Fk, + Fd, + ) + r_dr_dt_old = formulae.drop_growth__r_dr_dt( + RH_eq=RH_eq, RH=RH, Fk=Fk, Fd=Fd + ) + mass_old = formulae.diffusion_coordinate__mass(x_old) + dm_dt_old = formulae.particle_shape_and_density__dm_dt( + r=r_old, r_dr_dt=r_dr_dt_old + ) + dx_old = timestep * formulae.diffusion_coordinate__dx_dt( + mass_old, dm_dt_old + ) + else: + dx_old = 0.0 + if dx_old == 0: + x_new = x_old + else: + a = x_old + b = max(x_insane, a + dx_old) + fa = minfun(a, *minfun_args) + fb = minfun(b, *minfun_args) + + counter = 0 + while not fa * fb < 0: + counter += 1 + if counter > max_iters: + if not fake: + warn( + "failed to find interval", + __file__, + context=( + "T", + T, + "p", + p, + "RH", + RH, + "a", + a, + "b", + b, + "fa", + fa, + "fb", + fb, + ), + ) + success = False + break + b = max(x_insane, a + math.ldexp(dx_old, counter)) + fb = minfun(b, *minfun_args) + + if not success: + break + if a != b: + if a > b: + a, b = b, a + fa, fb = fb, fa + + x_new, iters_taken = toms748_solve( + minfun, + minfun_args, + a, + b, + fa, + fb, + rtol_x, + max_iters, + formulae.trivia__within_tolerance, + ) + if iters_taken in (-1, max_iters): + if not fake: + warn("TOMS failed", __file__) + success = False + break + else: + x_new = x_old + + mass_new = formulae.diffusion_coordinate__mass(x_new) + mass_cr = formulae.particle_shape_and_density__volume_to_mass( + attributes.v_cr[drop] + ) + result += attributes.multiplicity[drop] * mass_new + if not fake: + if ( + mass_new > mass_cr + and mass_new > attributes.signed_water_mass[drop] + ): + n_activated_and_growing += attributes.multiplicity[drop] + if mass_new > mass_cr > attributes.signed_water_mass[drop]: + n_activating += attributes.multiplicity[drop] + if mass_new < mass_cr < attributes.signed_water_mass[drop]: + n_deactivating += attributes.multiplicity[drop] + attributes.signed_water_mass[drop] = mass_new + n_ripening = n_activated_and_growing if n_deactivating > 0 else 0 + return result, success, n_activating, n_deactivating, n_ripening + + return calculate_ml_new + + # pylint disable=unused-argument + def make_condensation_solver( + self, + timestep, + n_cell, + *, + dt_range, + adaptive, + fuse, + multiplier, + RH_rtol, + max_iters, + ): + return CondensationMethods.make_condensation_solver_impl( + formulae=self.formulae_flattened, + timestep=timestep, + dt_range=dt_range, + adaptive=adaptive, + fuse=fuse, + multiplier=multiplier, + RH_rtol=RH_rtol, + max_iters=max_iters, + ) + + @staticmethod + @lru_cache() + def make_condensation_solver_impl( + *, + formulae, + timestep, + dt_range, + adaptive, + fuse, + multiplier, + RH_rtol, + max_iters, + ): + jit_flags = { + **conf.JIT_FLAGS, + **{"parallel": False, "cache": False, "fastmath": formulae.fastmath}, + } + + step_impl = CondensationMethods.make_step_impl( + jit_flags=jit_flags, + formulae=formulae, + calculate_ml_old=CondensationMethods.make_calculate_ml_old(jit_flags), + calculate_ml_new=CondensationMethods.make_calculate_ml_new( + jit_flags=jit_flags, + formulae=formulae, + max_iters=max_iters, + RH_rtol=RH_rtol, + ), + ) + step_fake = CondensationMethods.make_step_fake(jit_flags, step_impl) + adapt_substeps = CondensationMethods.make_adapt_substeps( + jit_flags=jit_flags, + formulae=formulae, + timestep=timestep, + step_fake=step_fake, + dt_range=dt_range, + fuse=fuse, + multiplier=multiplier, + ) + step = CondensationMethods.make_step(jit_flags, step_impl) + + @numba.njit(**jit_flags) + def solve( # pylint: disable=too-many-arguments,too-many-locals + attributes, + cell_idx, + thd, + water_vapour_mixing_ratio, + rhod, + dthd_dt, + d_water_vapour_mixing_ratio__dt, + drhod_dt, + m_d, + air_density, + air_dynamic_viscosity, + rtols, + timestep, + n_substeps, + ): + step_impl_args = ( + attributes, + cell_idx, + thd, + water_vapour_mixing_ratio, + rhod, + dthd_dt, + d_water_vapour_mixing_ratio__dt, + drhod_dt, + m_d, + air_density, + air_dynamic_viscosity, + rtols.x, + ) + success = True + if adaptive: + n_substeps, success = adapt_substeps( + step_impl_args, n_substeps, thd, rtols.thd + ) + if success: + ( + water_vapour_mixing_ratio, + thd, + n_activating, + n_deactivating, + n_ripening, + RH_max, + success, + ) = step(step_impl_args, timestep, n_substeps) + else: + n_activating, n_deactivating, n_ripening, RH_max = -1, -1, -1, -1 + return ( + success, + water_vapour_mixing_ratio, + thd, + n_substeps, + n_activating, + n_deactivating, + n_ripening, + RH_max, + ) + + return solve diff --git a/PySDM/source/PySDM/backends/impl_numba/methods/deposition_methods.py b/PySDM/source/PySDM/backends/impl_numba/methods/deposition_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..7c1fa01458c6c95ed80249383ed5e1c1855ff45c --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_numba/methods/deposition_methods.py @@ -0,0 +1,350 @@ +"""basic water vapor deposition on ice for CPU backend, for Howell factor see: +[Howell 1949](https://doi.org/10.1175/1520-0469(1949)006%3C0134:TGOCDI%3E2.0.CO;2) +""" + +from functools import cached_property +import numba +import numpy as np + +from PySDM.backends.impl_common.backend_methods import BackendMethods + + +# TODO #1524 +# pylint: disable=too-many-arguments,too-many-locals,too-many-statements + + +class DepositionMethods(BackendMethods): # pylint:disable=too-few-public-methods + @cached_property + def _deposition(self): + assert self.formulae.particle_shape_and_density.supports_mixed_phase() + + formulae = self.formulae_flattened + multiplier = 2 + midpoint = True + rel_tol_rh = 1e-2 + fuse = 16 + + @numba.njit(**{**self.default_jit_flags, **{"parallel": False}}) + def calc_saturation_ratio_ice_temperature_and_pressure( + vapour_mixing_ratio, dry_air_potential_temperature, dry_air_density + ): + temperature = formulae.state_variable_triplet__T( + rhod=dry_air_density, + thd=dry_air_potential_temperature, + ) + total_pressure = formulae.state_variable_triplet__p( + rhod=dry_air_density, + T=temperature, + water_vapour_mixing_ratio=vapour_mixing_ratio, + ) + vapour_partial_pressure = formulae.state_variable_triplet__pv( + p=total_pressure, + water_vapour_mixing_ratio=vapour_mixing_ratio, + ) + saturation_ratio_ice = ( + vapour_partial_pressure + / formulae.saturation_vapour_pressure__pvs_ice(temperature) + ) + return saturation_ratio_ice, temperature, total_pressure + + @numba.njit(**{**self.default_jit_flags, **{"parallel": False}}) + def mass_deposition_rate_per_droplet( + temperature: float, + rho_d: float, + signed_mass_old: float, + latent_heat_sub: float, + saturation_ratio_ice: float, + pressure: float, + ): + radius = formulae.particle_shape_and_density__mass_to_radius( + signed_mass_old + ) + pvs_ice = formulae.saturation_vapour_pressure__pvs_ice(temperature) + + capacity = formulae.diffusion_ice_capacity__capacity(abs(signed_mass_old)) + + mass_ventilation_factor = 1 # TODO #1655 + heat_ventilation_factor = mass_ventilation_factor # TODO #1588 + + Dv_const = formulae.diffusion_thermics__D(temperature, pressure) + lambdaD = formulae.diffusion_ice_kinetics__lambdaD(temperature, pressure) + diffusion_coefficient = formulae.diffusion_ice_kinetics__D( + Dv_const, radius, lambdaD, temperature + ) + + Ka_const = formulae.diffusion_thermics__K(temperature, pressure) + lambdaK = formulae.diffusion_ice_kinetics__lambdaK(temperature, pressure) + thermal_conductivity = formulae.diffusion_ice_kinetics__K( + Ka_const, radius, lambdaK, temperature, rho_d + ) + + Fk = formulae.drop_growth__Fk( + T=temperature, + K=thermal_conductivity * heat_ventilation_factor, + lv=latent_heat_sub, + ) + Fd = formulae.drop_growth__Fd( + T=temperature, + D=diffusion_coefficient * mass_ventilation_factor, + pvs=pvs_ice, + ) + howell_factor_x_diffcoef_x_rhovsice_x_icess = ( + formulae.drop_growth__r_dr_dt( + RH_eq=1, + RH=saturation_ratio_ice, + Fk=Fk, + Fd=Fd, + ) + * formulae.constants.rho_w + ) + + mass_deposition_rate = ( + 4 * np.pi * capacity * howell_factor_x_diffcoef_x_rhovsice_x_icess + ) + if mass_deposition_rate > 1: + print( + radius, + Fk, + Fd, + mass_deposition_rate, + howell_factor_x_diffcoef_x_rhovsice_x_icess, + pvs_ice, + ) + assert False + return mass_deposition_rate + + @numba.njit(**{**self.default_jit_flags, **{"parallel": False}}) + def _loop( + fake, + temperature, + rhod, + thd, + signed_water_mass, + saturation_ratio_ice, + total_pressure, + multiplicity, + sub_time_step, + mass_of_dry_air, + ): + latent_heat_sub = formulae.latent_heat_sublimation__ls(temperature) + delta_rv = 0 + for i, ksi in enumerate(multiplicity): + if not formulae.trivia__unfrozen(signed_water_mass[i]): + mass_deposition_rate = mass_deposition_rate_per_droplet( + temperature=temperature, + rho_d=rhod, + signed_mass_old=signed_water_mass[i], + latent_heat_sub=latent_heat_sub, + saturation_ratio_ice=saturation_ratio_ice, + pressure=total_pressure, + ) + delta_rv += ( + -mass_deposition_rate * ksi * sub_time_step / mass_of_dry_air + ) + if not fake: + x_old = formulae.diffusion_coordinate__x(-signed_water_mass[i]) + dx_dt_old = formulae.diffusion_coordinate__dx_dt( + -signed_water_mass[i], mass_deposition_rate + ) + x_new = formulae.trivia__explicit_euler( + x_old, sub_time_step, dx_dt_old + ) + signed_water_mass[i] = -formulae.diffusion_coordinate__mass( + x_new + ) + if x_new > 1: + print(x_old, dx_dt_old, x_new, signed_water_mass[i]) + assert False + delta_thd = ( + formulae.state_variable_triplet__dthd_dt( + rhod=rhod, + thd=thd, + T=temperature, + d_water_vapour_mixing_ratio__dt=delta_rv / sub_time_step, + lv=latent_heat_sub, + ) + * sub_time_step + ) + if delta_rv == 0: + assert delta_thd == 0 + else: + assert (delta_rv < 0 < delta_thd) or (delta_rv > 0 > delta_thd) + return delta_rv, delta_thd + + @numba.njit(**{**self.default_jit_flags, **{"parallel": False}}) + def body( # pylint: disable=too-many-arguments + *, + adaptive, + multiplicity, + signed_water_mass, + current_vapour_mixing_ratio, + current_dry_air_density, + current_dry_potential_temperature, + cell_volume, + time_step, + cell_id, + # to be modified + predicted_vapour_mixing_ratio, + predicted_dry_potential_temperature, + predicted_dry_air_density, + ): + """simplest adaptivity: + - global dt - no cell-wise logic (we don't have any test/example for it!) + - no mechanism to retain dt value over timesteps + - explicit Euler mass integration (vs. implicit in condensation) + """ + # pylint: disable=too-many-locals + n_substeps = 1 + cid = cell_id[0] # TODO #1524: add support for multi-cell environments + + rv_tendency = ( + predicted_vapour_mixing_ratio[cid] - current_vapour_mixing_ratio[cid] + ) / time_step + thd_tendency = ( + predicted_dry_potential_temperature[cid] + - current_dry_potential_temperature[cid] + ) / time_step + rhod_tendency = ( + predicted_dry_air_density[cid] - current_dry_air_density[cid] + ) / time_step + dry_air_mass_mean = ( + cell_volume + * (predicted_dry_air_density[cid] + current_dry_air_density[cid]) + / 2 + ) + + if adaptive: + n_substeps = 1 / multiplier + delta_rh_long = np.nan + for burnout in range(fuse + 1): + if burnout == fuse: + assert False + sub_time_step = time_step / n_substeps + rhod = ( + current_dry_air_density[cid] + + rhod_tendency * (0.5 if midpoint else 1) * sub_time_step + ) + rv = ( + current_vapour_mixing_ratio[cid] + + rv_tendency * (0.5 if midpoint else 1) * sub_time_step + ) + thd = ( + current_dry_potential_temperature[cid] + + thd_tendency * (0.5 if midpoint else 1) * sub_time_step + ) + + saturation_ratio_ice, temperature, total_pressure = ( + calc_saturation_ratio_ice_temperature_and_pressure( + vapour_mixing_ratio=rv, + dry_air_potential_temperature=thd, + dry_air_density=rhod, + ) + ) + delta_rv, delta_thd = _loop( + fake=True, + temperature=temperature, + rhod=rhod, + thd=thd, + signed_water_mass=signed_water_mass, + saturation_ratio_ice=saturation_ratio_ice, + total_pressure=total_pressure, + multiplicity=multiplicity, + sub_time_step=sub_time_step, + mass_of_dry_air=dry_air_mass_mean, + ) + delta_rh_short = ( + calc_saturation_ratio_ice_temperature_and_pressure( + vapour_mixing_ratio=rv + delta_rv, + dry_air_potential_temperature=thd + delta_thd, + dry_air_density=rhod, + )[0] + - saturation_ratio_ice + ) + if ( + n_substeps < 1 + or rv < -delta_rv + or not formulae.trivia__within_tolerance( + abs(delta_rh_long - multiplier * delta_rh_short), + saturation_ratio_ice, + rel_tol_rh, + ) + ): + delta_rh_long = delta_rh_short + n_substeps *= multiplier + else: + break + sub_time_step = time_step / n_substeps + + rv = current_vapour_mixing_ratio[cid] + thd = current_dry_potential_temperature[cid] + rhod = current_dry_air_density[cid] + + assert n_substeps == int(n_substeps) + for _ in range(int(n_substeps)): + rv += sub_time_step * rv_tendency * (0.5 if midpoint else 1) + thd += sub_time_step * thd_tendency * (0.5 if midpoint else 1) + rhod += sub_time_step * rhod_tendency * (0.5 if midpoint else 1) + + saturation_ratio_ice, temperature, total_pressure = ( + calc_saturation_ratio_ice_temperature_and_pressure( + vapour_mixing_ratio=rv, + dry_air_potential_temperature=thd, + dry_air_density=rhod, + ) + ) + delta_rv, delta_thd = _loop( + fake=False, + temperature=temperature, + rhod=rhod, + thd=thd, + signed_water_mass=signed_water_mass, + saturation_ratio_ice=saturation_ratio_ice, + total_pressure=total_pressure, + multiplicity=multiplicity, + sub_time_step=sub_time_step, + mass_of_dry_air=dry_air_mass_mean, + ) + thd += delta_thd + rv += delta_rv + assert rv >= 0 + + if midpoint: + thd += sub_time_step * thd_tendency / 2 + rv += sub_time_step * rv_tendency / 2 + rhod += sub_time_step * rhod_tendency / 2 + + predicted_dry_potential_temperature[cid] = thd + predicted_vapour_mixing_ratio[cid] = rv + + return body + + def deposition( # pylint: disable=too-many-locals + self, + *, + adaptive, + multiplicity, + signed_water_mass, + current_vapour_mixing_ratio, + current_dry_air_density, + current_dry_potential_temperature, + cell_volume, + time_step, + cell_id, + predicted_vapour_mixing_ratio, + predicted_dry_potential_temperature, + predicted_dry_air_density, + ): + self._deposition( + adaptive=adaptive, + multiplicity=multiplicity.data, + signed_water_mass=signed_water_mass.data, + current_vapour_mixing_ratio=current_vapour_mixing_ratio.data, + current_dry_air_density=current_dry_air_density.data, + current_dry_potential_temperature=current_dry_potential_temperature.data, + cell_volume=cell_volume, + time_step=time_step, + cell_id=cell_id.data, + predicted_vapour_mixing_ratio=predicted_vapour_mixing_ratio.data, + predicted_dry_potential_temperature=predicted_dry_potential_temperature.data, + predicted_dry_air_density=predicted_dry_air_density.data, + ) diff --git a/PySDM/source/PySDM/backends/impl_numba/methods/displacement_methods.py b/PySDM/source/PySDM/backends/impl_numba/methods/displacement_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..ac7b5fa44c67fd197a2d7e094a3201bfdc8ae97a --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_numba/methods/displacement_methods.py @@ -0,0 +1,249 @@ +""" +CPU implementation of backend methods for particle displacement (advection and sedimentation) +""" + +from functools import cached_property + +import numba + +from PySDM.backends.impl_numba import conf + +from ...impl_common.backend_methods import BackendMethods + + +@numba.njit(**{**conf.JIT_FLAGS, **{"parallel": False}}) +# pylint: disable=too-many-arguments +def calculate_displacement_body_common( + dim, droplet, scheme, _l, _r, displacement, courant, position_in_cell, n_substeps +): + displacement[dim, droplet] = scheme( + position_in_cell[dim, droplet], + courant[_l] / n_substeps, + courant[_r] / n_substeps, + ) + + +class DisplacementMethods(BackendMethods): + @staticmethod + @numba.njit(**{**conf.JIT_FLAGS, **{"parallel": False, "cache": False}}) + # pylint: disable=too-many-arguments + def calculate_displacement_body_1d( + dim, scheme, displacement, courant, cell_origin, position_in_cell, n_substeps + ): + length = displacement.shape[1] + for droplet in numba.prange(length): # pylint: disable=not-an-iterable + # Arakawa-C grid + _l = cell_origin[0, droplet] + _r = cell_origin[0, droplet] + 1 + calculate_displacement_body_common( + dim, + droplet, + scheme, + _l, + _r, + displacement, + courant, + position_in_cell, + n_substeps, + ) + + @staticmethod + @numba.njit(**{**conf.JIT_FLAGS, **{"parallel": False, "cache": False}}) + # pylint: disable=too-many-arguments + def calculate_displacement_body_2d( + dim, scheme, displacement, courant, cell_origin, position_in_cell, n_substeps + ): + length = displacement.shape[1] + for droplet in numba.prange(length): # pylint: disable=not-an-iterable + # Arakawa-C grid + _l = ( + cell_origin[0, droplet], + cell_origin[1, droplet], + ) + _r = ( + cell_origin[0, droplet] + 1 * (dim == 0), + cell_origin[1, droplet] + 1 * (dim == 1), + ) + calculate_displacement_body_common( + dim, + droplet, + scheme, + _l, + _r, + displacement, + courant, + position_in_cell, + n_substeps, + ) + + @staticmethod + @numba.njit(**{**conf.JIT_FLAGS, **{"parallel": False, "cache": False}}) + # pylint: disable=too-many-arguments + def calculate_displacement_body_3d( + dim, scheme, displacement, courant, cell_origin, position_in_cell, n_substeps + ): + n_sd = displacement.shape[1] + for droplet in numba.prange(n_sd): # pylint: disable=not-an-iterable + # Arakawa-C grid + _l = ( + cell_origin[0, droplet], + cell_origin[1, droplet], + cell_origin[2, droplet], + ) + _r = ( + cell_origin[0, droplet] + 1 * (dim == 0), + cell_origin[1, droplet] + 1 * (dim == 1), + cell_origin[2, droplet] + 1 * (dim == 2), + ) + calculate_displacement_body_common( + dim, + droplet, + scheme, + _l, + _r, + displacement, + courant, + position_in_cell, + n_substeps, + ) + + def calculate_displacement( + self, *, dim, displacement, courant, cell_origin, position_in_cell, n_substeps + ): + n_dims = len(courant.shape) + scheme = self.formulae.particle_advection.displacement + if n_dims == 1: + DisplacementMethods.calculate_displacement_body_1d( + dim, + scheme, + displacement.data, + courant.data, + cell_origin.data, + position_in_cell.data, + n_substeps, + ) + elif n_dims == 2: + DisplacementMethods.calculate_displacement_body_2d( + dim, + scheme, + displacement.data, + courant.data, + cell_origin.data, + position_in_cell.data, + n_substeps, + ) + elif n_dims == 3: + DisplacementMethods.calculate_displacement_body_3d( + dim, + scheme, + displacement.data, + courant.data, + cell_origin.data, + position_in_cell.data, + n_substeps, + ) + else: + raise NotImplementedError() + + @cached_property + def _flag_precipitated_body(self): + @numba.njit(**{**self.default_jit_flags, "parallel": False}) + # pylint: disable=too-many-arguments + def body( + cell_origin, + position_in_cell, + water_mass, + multiplicity, + idx, + length, + healthy, + precipitation_counting_level_index, + displacement, + ): + rainfall_mass = 0.0 + flag = len(idx) + for i in range(length): + position_within_column = ( + cell_origin[-1, idx[i]] + position_in_cell[-1, idx[i]] + ) + if ( + # falling + displacement[-1, idx[i]] < 0 + and + # and crossed precip-counting level + position_within_column < precipitation_counting_level_index + ): + rainfall_mass += abs(water_mass[idx[i]]) * multiplicity[idx[i]] + idx[i] = flag + healthy[0] = 0 + return rainfall_mass + + return body + + @cached_property + def _flag_out_of_column_body(self): + @numba.njit(**{**self.default_jit_flags, "parallel": False}) + # pylint: disable=too-many-arguments + def body( + cell_origin, position_in_cell, idx, length, healthy, domain_top_level_index + ): + flag = len(idx) + for i in range(length): + position_within_column = ( + cell_origin[-1, idx[i]] + position_in_cell[-1, idx[i]] + ) + if ( + position_within_column < 0 + or position_within_column > domain_top_level_index + ): + idx[i] = flag + healthy[0] = 0 + + return body + + # pylint: disable=too-many-arguments + def flag_precipitated( + self, + *, + cell_origin, + position_in_cell, + water_mass, + multiplicity, + idx, + length, + healthy, + precipitation_counting_level_index, + displacement, + ) -> float: + """return a scalar value corresponding to the mass of water (all phases) that crossed + the bottom boundary of the entire domain""" + return self._flag_precipitated_body( + cell_origin.data, + position_in_cell.data, + water_mass.data, + multiplicity.data, + idx.data, + length, + healthy.data, + precipitation_counting_level_index, + displacement.data, + ) + + # pylint: disable=too-many-arguments + def flag_out_of_column( + self, + cell_origin, + position_in_cell, + idx, + length, + healthy, + domain_top_level_index, + ): + self._flag_out_of_column_body( + cell_origin.data, + position_in_cell.data, + idx.data, + length, + healthy.data, + domain_top_level_index, + ) diff --git a/PySDM/source/PySDM/backends/impl_numba/methods/fragmentation_methods.py b/PySDM/source/PySDM/backends/impl_numba/methods/fragmentation_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..ef9789df6db75fc22f3cee9c30acebc326c0d80b --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_numba/methods/fragmentation_methods.py @@ -0,0 +1,499 @@ +""" +CPU implementation of backend methods supporting fragmentation functions +""" + +from functools import cached_property +import numba +import numpy as np +from PySDM.backends.impl_numba import conf +from PySDM.backends.impl_common.backend_methods import BackendMethods + + +@numba.njit(**{**conf.JIT_FLAGS, **{"parallel": False}}) +def straub_Nr( # pylint: disable=too-many-arguments,unused-argument + i, + Nr1, + Nr2, + Nr3, + Nr4, + Nrt, + CW, + gam, +): # pylint: disable=too-many-branches` + if gam[i] * CW[i] >= 7.0: + Nr1[i] = 0.088 * (gam[i] * CW[i] - 7.0) + if CW[i] >= 21.0: + Nr2[i] = 0.22 * (CW[i] - 21.0) + if CW[i] <= 46.0: + Nr3[i] = 0.04 * (46.0 - CW[i]) + else: + Nr3[i] = 1.0 + Nr4[i] = 1.0 + Nrt[i] = Nr1[i] + Nr2[i] + Nr3[i] + Nr4[i] + + +@numba.njit(**{**conf.JIT_FLAGS, **{"parallel": False}}) +def straub_mass_remainder( # pylint: disable=too-many-arguments,unused-argument + i, vl, ds, mu1, sigma1, mu2, sigma2, mu3, sigma3, d34, Nr1, Nr2, Nr3, Nr4 +): + # pylint: disable=too-many-arguments, too-many-locals + Nr1[i] = Nr1[i] * np.exp(3 * mu1 + 9 * np.power(sigma1, 2) / 2) + Nr2[i] = Nr2[i] * (mu2**3 + 3 * mu2 * sigma2**2) + Nr3[i] = Nr3[i] * (mu3**3 + 3 * mu3 * sigma3**2) + Nr4[i] = vl[i] * 6 / np.pi + ds[i] ** 3 - Nr1[i] - Nr2[i] - Nr3[i] + if Nr4[i] <= 0.0: + d34[i] = 0 + Nr4[i] = 0 + else: + d34[i] = np.exp(np.log(Nr4[i]) / 3) + + +@numba.njit(**{**conf.JIT_FLAGS, **{"parallel": False}}) +def ll82_Nr( # pylint: disable=too-many-arguments,unused-argument + i, + Rf, + Rs, + Rd, + CKE, + W, + W2, +): # pylint: disable=too-many-branches` + if CKE[i] >= 0.893e-6: + Rf[i] = 1.11e-4 * CKE[i] ** (-0.654) + else: + Rf[i] = 1.0 + if W[i] >= 0.86: + Rs[i] = 0.685 * (1 - np.exp(-1.63 * (W2[i] - 0.86))) + else: + Rs[i] = 0.0 + if (Rs[i] + Rf[i]) > 1.0: + Rd[i] = 0.0 + else: + Rd[i] = 1.0 - Rs[i] - Rf[i] + + +class FragmentationMethods(BackendMethods): + @cached_property + def _fragmentation_limiters_body(self): + @numba.njit(**self.default_jit_flags) + # pylint: disable=too-many-arguments + def body(n_fragment, frag_volume, vmin, nfmax, x_plus_y): + for i in numba.prange(len(frag_volume)): # pylint: disable=not-an-iterable + if x_plus_y[i] == 0.0: + frag_volume[i] = 0.0 + n_fragment[i] = 1.0 + else: + if np.isnan(frag_volume[i]) or frag_volume[i] == 0.0: + frag_volume[i] = x_plus_y[i] + frag_volume[i] = min(frag_volume[i], x_plus_y[i]) + if nfmax is not None and x_plus_y[i] / frag_volume[i] > nfmax: + frag_volume[i] = x_plus_y[i] / nfmax + elif frag_volume[i] < vmin: + frag_volume[i] = x_plus_y[i] + n_fragment[i] = x_plus_y[i] / frag_volume[i] + + return body + + def fragmentation_limiters(self, *, n_fragment, frag_volume, vmin, nfmax, x_plus_y): + self._fragmentation_limiters_body( + n_fragment=n_fragment.data, + frag_volume=frag_volume.data, + vmin=vmin, + nfmax=nfmax, + x_plus_y=x_plus_y.data, + ) + + @cached_property + def _slams_fragmentation_body(self): + @numba.njit(**self.default_jit_flags) + def body(n_fragment, frag_volume, x_plus_y, probs, rand): + for i in numba.prange(len(n_fragment)): # pylint: disable=not-an-iterable + probs[i] = 0.0 + n_fragment[i] = 1 + for n in range(22): + probs[i] += 0.91 * (n + 2) ** (-1.56) + if rand[i] < probs[i]: + n_fragment[i] = n + 2 + break + frag_volume[i] = x_plus_y[i] / n_fragment[i] + + return body + + def slams_fragmentation( + self, n_fragment, frag_volume, x_plus_y, probs, rand, vmin, nfmax + ): # pylint: disable=too-many-arguments + self._slams_fragmentation_body( + n_fragment.data, frag_volume.data, x_plus_y.data, probs.data, rand.data + ) + self._fragmentation_limiters_body( + n_fragment=n_fragment.data, + frag_volume=frag_volume.data, + vmin=vmin, + nfmax=nfmax, + x_plus_y=x_plus_y.data, + ) + + @cached_property + def _exp_fragmentation_body(self): + @numba.njit(**self.default_jit_flags) + # pylint: disable=too-many-arguments + def body(*, scale, frag_volume, rand, tol=1e-5): + for i in numba.prange(len(frag_volume)): # pylint: disable=not-an-iterable + frag_volume[i] = -scale * np.log(max(1 - rand[i], tol)) + + return body + + def exp_fragmentation( + self, + *, + n_fragment, + scale, + frag_volume, + x_plus_y, + rand, + vmin, + nfmax, + tol=1e-5, + ): + self._exp_fragmentation_body( + scale=scale, + frag_volume=frag_volume.data, + rand=rand.data, + tol=tol, + ) + self._fragmentation_limiters_body( + n_fragment=n_fragment.data, + frag_volume=frag_volume.data, + x_plus_y=x_plus_y.data, + vmin=vmin, + nfmax=nfmax, + ) + + def feingold1988_fragmentation( + self, + *, + n_fragment, + scale, + frag_volume, + x_plus_y, + rand, + fragtol, + vmin, + nfmax, + ): + self._feingold1988_fragmentation_body( + scale=scale, + frag_volume=frag_volume.data, + x_plus_y=x_plus_y.data, + rand=rand.data, + fragtol=fragtol, + ) + + self._fragmentation_limiters_body( + n_fragment=n_fragment.data, + frag_volume=frag_volume.data, + x_plus_y=x_plus_y.data, + vmin=vmin, + nfmax=nfmax, + ) + + def gauss_fragmentation( + self, *, n_fragment, mu, sigma, frag_volume, x_plus_y, rand, vmin, nfmax + ): + self._gauss_fragmentation_body( + mu=mu, + sigma=sigma, + frag_volume=frag_volume.data, + rand=rand.data, + ) + self._fragmentation_limiters_body( + n_fragment=n_fragment.data, + frag_volume=frag_volume.data, + x_plus_y=x_plus_y.data, + vmin=vmin, + nfmax=nfmax, + ) + + def straub_fragmentation( + # pylint: disable=too-many-arguments,too-many-locals + self, + *, + n_fragment, + CW, + gam, + ds, + frag_volume, + v_max, + x_plus_y, + rand, + vmin, + nfmax, + Nr1, + Nr2, + Nr3, + Nr4, + Nrt, + d34, + ): + self._straub_fragmentation_body( + CW=CW.data, + gam=gam.data, + ds=ds.data, + frag_volume=frag_volume.data, + v_max=v_max.data, + rand=rand.data, + Nr1=Nr1.data, + Nr2=Nr2.data, + Nr3=Nr3.data, + Nr4=Nr4.data, + Nrt=Nrt.data, + d34=d34.data, + ) + self._fragmentation_limiters_body( + n_fragment=n_fragment.data, + frag_volume=frag_volume.data, + x_plus_y=x_plus_y.data, + vmin=vmin, + nfmax=nfmax, + ) + + def ll82_fragmentation( + # pylint: disable=too-many-arguments,too-many-locals + self, + *, + n_fragment, + CKE, + W, + W2, + St, + ds, + dl, + dcoal, + frag_volume, + x_plus_y, + rand, + vmin, + nfmax, + Rf, + Rs, + Rd, + tol=1e-8, + ): + self._ll82_fragmentation_body( + CKE=CKE.data, + W=W.data, + W2=W2.data, + St=St.data, + ds=ds.data, + dl=dl.data, + dcoal=dcoal.data, + frag_volume=frag_volume.data, + rand=rand.data, + Rf=Rf.data, + Rs=Rs.data, + Rd=Rd.data, + tol=tol, + ) + self._fragmentation_limiters_body( + n_fragment=n_fragment.data, + frag_volume=frag_volume.data, + x_plus_y=x_plus_y.data, + vmin=vmin, + nfmax=nfmax, + ) + + @cached_property + def _ll82_coalescence_check_body(self): + @numba.njit(**self.default_jit_flags) + def body(*, Ec, dl): + for i in numba.prange(len(Ec)): # pylint: disable=not-an-iterable + if dl[i] < 0.4e-3: + Ec[i] = 1.0 + + return body + + def ll82_coalescence_check(self, *, Ec, dl): + self._ll82_coalescence_check_body( + Ec=Ec.data, + dl=dl.data, + ) + + @cached_property + def _straub_fragmentation_body(self): + ff = self.formulae_flattened + + @numba.njit(**self.default_jit_flags) + def body( + *, CW, gam, ds, v_max, frag_volume, rand, Nr1, Nr2, Nr3, Nr4, Nrt, d34 + ): # pylint: disable=too-many-arguments,too-many-locals + for i in numba.prange(len(frag_volume)): # pylint: disable=not-an-iterable + straub_Nr(i, Nr1, Nr2, Nr3, Nr4, Nrt, CW, gam) + sigma1 = ff.fragmentation_function__params_sigma1(CW[i]) + mu1 = ff.fragmentation_function__params_mu1(sigma1) + sigma2 = ff.fragmentation_function__params_sigma2(CW[i]) + mu2 = ff.fragmentation_function__params_mu2(ds[i]) + sigma3 = ff.fragmentation_function__params_sigma3(CW[i]) + mu3 = ff.fragmentation_function__params_mu3(ds[i]) + straub_mass_remainder( + i, + v_max, + ds, + mu1, + sigma1, + mu2, + sigma2, + mu3, + sigma3, + d34, + Nr1, + Nr2, + Nr3, + Nr4, + ) + Nrt[i] = Nr1[i] + Nr2[i] + Nr3[i] + Nr4[i] + + if Nrt[i] == 0.0: + diameter = 0.0 + else: + if rand[i] < Nr1[i] / Nrt[i]: + X = rand[i] * Nrt[i] / Nr1[i] + lnarg = mu1 + np.sqrt(2) * sigma1 * ff.trivia__erfinv_approx(X) + diameter = np.exp(lnarg) + elif rand[i] < (Nr2[i] + Nr1[i]) / Nrt[i]: + X = (rand[i] * Nrt[i] - Nr1[i]) / Nr2[i] + diameter = mu2 + np.sqrt(2) * sigma2 * ff.trivia__erfinv_approx( + X + ) + elif rand[i] < (Nr3[i] + Nr2[i] + Nr1[i]) / Nrt[i]: + X = (rand[i] * Nrt[i] - Nr1[i] - Nr2[i]) / Nr3[i] + diameter = mu3 + np.sqrt(2) * sigma3 * ff.trivia__erfinv_approx( + X + ) + else: + diameter = d34[i] + + frag_volume[i] = diameter**3 * ff.constants.PI / 6 + + return body + + @cached_property + def _ll82_fragmentation_body(self): # pylint: disable=too-many-statements + ff = self.formulae_flattened + + @numba.njit(**self.default_jit_flags) + def body( + *, CKE, W, W2, St, ds, dl, dcoal, frag_volume, rand, Rf, Rs, Rd, tol + ): # pylint: disable=too-many-branches,too-many-locals,too-many-statements + for i in numba.prange(len(frag_volume)): # pylint: disable=not-an-iterable + if dl[i] <= 0.4e-3: + frag_volume[i] = dcoal[i] ** 3 * ff.constants.PI / 6 + elif ds[i] == 0.0 or dl[i] == 0.0: + frag_volume[i] = 1e-18 + else: + ll82_Nr(i, Rf, Rs, Rd, CKE, W, W2) + if rand[i] <= Rf[i]: # filament breakup + (H1, mu1, sigma1) = ff.fragmentation_function__params_f1( + dl[i], dcoal[i] + ) + (H2, mu2, sigma2) = ff.fragmentation_function__params_f2(ds[i]) + (H3, mu3, sigma3) = ff.fragmentation_function__params_f3( + ds[i], dl[i] + ) + H1 = H1 * mu1 + H2 = H2 * mu2 + H3 = H3 * np.exp(mu3) + Hsum = H1 + H2 + H3 + rand[i] = rand[i] / Rf[i] + if rand[i] <= H1 / Hsum: + X = max(rand[i] * Hsum / H1, tol) + frag_volume[i] = mu1 + np.sqrt( + 2 + ) * sigma1 * ff.trivia__erfinv_approx(2 * X - 1) + elif rand[i] <= (H1 + H2) / Hsum: + X = (rand[i] * Hsum - H1) / H2 + frag_volume[i] = mu2 + np.sqrt( + 2 + ) * sigma2 * ff.trivia__erfinv_approx(2 * X - 1) + else: + X = min((rand[i] * Hsum - H1 - H2) / H3, 1.0 - tol) + lnarg = mu3 + np.sqrt( + 2 + ) * sigma3 * ff.trivia__erfinv_approx(2 * X - 1) + frag_volume[i] = np.exp(lnarg) + + elif rand[i] <= Rf[i] + Rs[i]: # sheet breakup + (H1, mu1, sigma1) = ff.fragmentation_function__params_s1( + dl[i], ds[i], dcoal[i] + ) + (H2, mu2, sigma2) = ff.fragmentation_function__params_s2( + dl[i], ds[i], St[i] + ) + H1 = H1 * mu1 + H2 = H2 * np.exp(mu2) + Hsum = H1 + H2 + rand[i] = (rand[i] - Rf[i]) / (Rs[i]) + if rand[i] <= H1 / Hsum: + X = max(rand[i] * Hsum / H1, tol) + frag_volume[i] = mu1 + np.sqrt( + 2 + ) * sigma1 * ff.trivia__erfinv_approx(2 * X - 1) + else: + X = min((rand[i] * Hsum - H1) / H2, 1.0 - tol) + lnarg = mu2 + np.sqrt( + 2 + ) * sigma2 * ff.trivia__erfinv_approx(2 * X - 1) + frag_volume[i] = np.exp(lnarg) + + else: # disk breakup + (H1, mu1, sigma1) = ff.fragmentation_function__params_d1( + W[i], dl[i], dcoal[i], CKE[i] + ) + (H2, mu2, sigma2) = ff.fragmentation_function__params_d2( + ds[i], dl[i], CKE[i] + ) + H1 = H1 * mu1 + Hsum = H1 + H2 + rand[i] = (rand[i] - Rf[i] - Rs[i]) / Rd[i] + if rand[i] <= H1 / Hsum: + X = max(rand[i] * Hsum / H1, tol) + frag_volume[i] = mu1 + np.sqrt( + 2 + ) * sigma1 * ff.trivia__erfinv_approx(2 * X - 1) + else: + X = min((rand[i] * Hsum - H1) / H2, 1 - tol) + lnarg = mu2 + np.sqrt( + 2 + ) * sigma2 * ff.trivia__erfinv_approx(2 * X - 1) + frag_volume[i] = np.exp(lnarg) + + frag_volume[i] = ( + frag_volume[i] * 0.01 + ) # diameter in cm; convert to m + frag_volume[i] = frag_volume[i] ** 3 * ff.constants.PI / 6 + + return body + + @cached_property + def _gauss_fragmentation_body(self): + ff = self.formulae_flattened + + @numba.njit(**self.default_jit_flags) + def body(*, mu, sigma, frag_volume, rand): # pylint: disable=too-many-arguments + for i in numba.prange(len(frag_volume)): # pylint: disable=not-an-iterable + frag_volume[i] = mu + sigma * ff.trivia__erfinv_approx(rand[i]) + + return body + + @cached_property + def _feingold1988_fragmentation_body(self): + ff = self.formulae_flattened + + @numba.njit(**self.default_jit_flags) + # pylint: disable=too-many-arguments + def body(*, scale, frag_volume, x_plus_y, rand, fragtol): + for i in numba.prange(len(frag_volume)): # pylint: disable=not-an-iterable + frag_volume[i] = ff.fragmentation_function__frag_volume( + scale, rand[i], x_plus_y[i], fragtol + ) + + return body diff --git a/PySDM/source/PySDM/backends/impl_numba/methods/freezing_methods.py b/PySDM/source/PySDM/backends/impl_numba/methods/freezing_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..0189838b4d08a812f88add4e5ddaa1e31fb1ee33 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_numba/methods/freezing_methods.py @@ -0,0 +1,301 @@ +""" +CPU implementation of backend methods for homogeneous freezing and +heterogeneous freezing (singular and time-dependent immersion freezing) +""" + +from functools import cached_property + +import numba +import numpy as np + +from PySDM.backends.impl_common.backend_methods import BackendMethods + +from ...impl_common.freezing_attributes import ( + SingularAttributes, + TimeDependentAttributes, + TimeDependentHomogeneousAttributes, + ThresholdHomogeneousAndThawAttributes, +) + + +class FreezingMethods(BackendMethods): + @cached_property + def _freeze(self): + @numba.njit(**{**self.default_jit_flags, **{"parallel": False}}) + def body(signed_water_mass, i): + signed_water_mass[i] = -1 * signed_water_mass[i] + # TODO #599: change thd (latent heat)! + + return body + + @cached_property + def _thaw(self): + @numba.njit(**{**self.default_jit_flags, **{"parallel": False}}) + def body(signed_water_mass, i): + signed_water_mass[i] = -1 * signed_water_mass[i] + # TODO #599: change thd (latent heat)! + + return body + + @cached_property + def _thaw_instantaneous_body(self): + _thaw = self._thaw + frozen_and_above_freezing_point = ( + self.formulae.trivia.frozen_and_above_freezing_point + ) + + @numba.njit(**self.default_jit_flags) + def body(attributes, cell, temperature): + n_sd = len(attributes.signed_water_mass) + for i in numba.prange(n_sd): # pylint: disable=not-an-iterable + if frozen_and_above_freezing_point( + attributes.signed_water_mass[i], temperature[cell[i]] + ): + _thaw(attributes.signed_water_mass, i) + + return body + + @cached_property + def _immersion_freezing_singular_body(self): + _freeze = self._freeze + unfrozen_and_saturated = self.formulae.trivia.unfrozen_and_saturated + + @numba.njit(**self.default_jit_flags) + def body( + attributes, + temperature, + relative_humidity, + cell, + ): + n_sd = len(attributes.freezing_temperature) + for i in numba.prange(n_sd): # pylint: disable=not-an-iterable + if attributes.freezing_temperature[i] == 0: + continue + if ( + unfrozen_and_saturated( + attributes.signed_water_mass[i], relative_humidity[cell[i]] + ) + and temperature[cell[i]] <= attributes.freezing_temperature[i] + ): + _freeze(attributes.signed_water_mass, i) + + return body + + @cached_property + def _immersion_freezing_time_dependent_body(self): + _freeze = self._freeze + unfrozen_and_saturated = self.formulae.trivia.unfrozen_and_saturated + j_het = self.formulae.heterogeneous_ice_nucleation_rate.j_het + prob_zero_events = self.formulae.trivia.poissonian_avoidance_function + + @numba.njit(**self.default_jit_flags) + def body( # pylint: disable=too-many-arguments + rand, + attributes, + timestep, + cell, + a_w_ice, + relative_humidity, + ): + n_sd = len(attributes.signed_water_mass) + for i in numba.prange(n_sd): # pylint: disable=not-an-iterable + if attributes.immersed_surface_area[i] == 0: + continue + cell_id = cell[i] + if unfrozen_and_saturated( + attributes.signed_water_mass[i], relative_humidity[cell_id] + ): + rate_assuming_constant_temperature_within_dt = ( + j_het(a_w_ice[cell_id]) * attributes.immersed_surface_area[i] + ) + prob = 1 - prob_zero_events( + r=rate_assuming_constant_temperature_within_dt, dt=timestep + ) + if rand[i] < prob: + _freeze(attributes.signed_water_mass, i) + + return body + + @cached_property + def _homogeneous_freezing_time_dependent_body(self): + _freeze = self._freeze + unfrozen_and_ice_saturated = self.formulae.trivia.unfrozen_and_ice_saturated + j_hom = self.formulae.homogeneous_ice_nucleation_rate.j_hom + prob_zero_events = self.formulae.trivia.poissonian_avoidance_function + d_a_w_ice_within_range = ( + self.formulae.homogeneous_ice_nucleation_rate.d_a_w_ice_within_range + ) + d_a_w_ice_maximum = ( + self.formulae.homogeneous_ice_nucleation_rate.d_a_w_ice_maximum + ) + + @numba.njit(**self.default_jit_flags) + def body( # pylint: disable=unused-argument,too-many-arguments + rand, + attributes, + timestep, + cell, + a_w_ice, + temperature, + relative_humidity_ice, + ): + + n_sd = len(attributes.signed_water_mass) + for i in numba.prange(n_sd): # pylint: disable=not-an-iterable + cell_id = cell[i] + if unfrozen_and_ice_saturated( + attributes.signed_water_mass[i], relative_humidity_ice[cell_id] + ): + d_a_w_ice = (relative_humidity_ice[cell_id] - 1.0) * a_w_ice[ + cell_id + ] + + if d_a_w_ice_within_range(d_a_w_ice): + d_a_w_ice = d_a_w_ice_maximum(d_a_w_ice) + rate_assuming_constant_temperature_within_dt = ( + j_hom(temperature[cell_id], d_a_w_ice) + * attributes.volume[i] + ) + prob = 1 - prob_zero_events( + r=rate_assuming_constant_temperature_within_dt, dt=timestep + ) + if rand[i] < prob: + _freeze(attributes.signed_water_mass, i) + + return body + + @cached_property + def _homogeneous_freezing_threshold_body(self): + _freeze = self._freeze + unfrozen_and_ice_saturated = self.formulae.trivia.unfrozen_and_ice_saturated + const = self.formulae.constants + + @numba.njit(**self.default_jit_flags) + def body(attributes, cell, temperature, relative_humidity_ice): + n_sd = len(attributes.signed_water_mass) + for i in numba.prange(n_sd): # pylint: disable=not-an-iterable + cell_id = cell[i] + if unfrozen_and_ice_saturated( + attributes.signed_water_mass[i], relative_humidity_ice[cell_id] + ): + if temperature[cell_id] <= const.HOMOGENEOUS_FREEZING_THRESHOLD: + _freeze(attributes.signed_water_mass, i) + + return body + + def thaw_instantaneous( + self, + *, + attributes, + cell, + temperature, + ): + self._thaw_instantaneous_body( + ThresholdHomogeneousAndThawAttributes( + signed_water_mass=attributes.signed_water_mass.data, + ), + cell.data, + temperature.data, + ) + + def immersion_freezing_singular( + self, *, attributes, temperature, relative_humidity, cell + ): + self._immersion_freezing_singular_body( + SingularAttributes( + freezing_temperature=attributes.freezing_temperature.data, + signed_water_mass=attributes.signed_water_mass.data, + ), + temperature.data, + relative_humidity.data, + cell.data, + ) + + def immersion_freezing_time_dependent( + self, + *, + rand, + attributes, + timestep, + cell, + a_w_ice, + relative_humidity, + ): + self._immersion_freezing_time_dependent_body( + rand.data, + TimeDependentAttributes( + immersed_surface_area=attributes.immersed_surface_area.data, + signed_water_mass=attributes.signed_water_mass.data, + ), + timestep, + cell.data, + a_w_ice.data, + relative_humidity.data, + ) + + def homogeneous_freezing_threshold( + self, + *, + attributes, + cell, + temperature, + relative_humidity_ice, + ): + self._homogeneous_freezing_threshold_body( + ThresholdHomogeneousAndThawAttributes( + signed_water_mass=attributes.signed_water_mass.data, + ), + cell.data, + temperature.data, + relative_humidity_ice.data, + ) + + def homogeneous_freezing_time_dependent( + self, + *, + rand, + attributes, + timestep, + cell, + a_w_ice, + temperature, + relative_humidity_ice, + ): + self._homogeneous_freezing_time_dependent_body( + rand.data, + TimeDependentHomogeneousAttributes( + volume=attributes.volume.data, + signed_water_mass=attributes.signed_water_mass.data, + ), + timestep, + cell.data, + a_w_ice.data, + temperature.data, + relative_humidity_ice.data, + ) + + @cached_property + def _record_freezing_temperatures_body(self): + ff = self.formulae_flattened + + @numba.njit(**{**self.default_jit_flags, "fastmath": False}) + def body(data, cell_id, temperature, signed_water_mass): + for drop_id in numba.prange(len(data)): # pylint: disable=not-an-iterable + if ff.trivia__unfrozen(signed_water_mass[drop_id]): + if data[drop_id] > 0: + data[drop_id] = np.nan + else: + if np.isnan(data[drop_id]): + data[drop_id] = temperature[cell_id[drop_id]] + + return body + + def record_freezing_temperatures( + self, *, data, cell_id, temperature, signed_water_mass + ): + self._record_freezing_temperatures_body( + data=data.data, + cell_id=cell_id.data, + temperature=temperature.data, + signed_water_mass=signed_water_mass.data, + ) diff --git a/PySDM/source/PySDM/backends/impl_numba/methods/index_methods.py b/PySDM/source/PySDM/backends/impl_numba/methods/index_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..caa92108a15077c725f0c64400bcb676c488f1ac --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_numba/methods/index_methods.py @@ -0,0 +1,48 @@ +""" +CPU implementation of shuffling and sorting backend methods +""" + +from functools import cached_property + +import numba + +from PySDM.backends.impl_common.backend_methods import BackendMethods + + +class IndexMethods(BackendMethods): + @cached_property + def identity_index(self): + @numba.njit(**self.default_jit_flags) + def body(idx): + for i in numba.prange(len(idx)): # pylint: disable=not-an-iterable + idx[i] = i + + return body + + @cached_property + def shuffle_global(self): + @numba.njit(**{**self.default_jit_flags, "parallel": False}) + def body(idx, length, u01): + for i in range(length - 1, 0, -1): + j = int(u01[i] * (i + 1)) + idx[i], idx[j] = idx[j], idx[i] + + return body + + @cached_property + def shuffle_local(self): + @numba.njit(**self.default_jit_flags) + def body(idx, u01, cell_start): + # pylint: disable=not-an-iterable + for c in numba.prange(len(cell_start) - 1): + for i in range(cell_start[c + 1] - 1, cell_start[c], -1): + j = int( + cell_start[c] + u01[i] * (cell_start[c + 1] - cell_start[c]) + ) + idx[i], idx[j] = idx[j], idx[i] + + return body + + @staticmethod + def sort_by_key(idx, attr): + idx.data[:] = attr.data.argsort(kind="stable")[::-1] diff --git a/PySDM/source/PySDM/backends/impl_numba/methods/isotope_methods.py b/PySDM/source/PySDM/backends/impl_numba/methods/isotope_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..f33e6761dc7d75fe510abbbbf73f38226cf15df7 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_numba/methods/isotope_methods.py @@ -0,0 +1,28 @@ +""" +CPU implementation of isotope-relates backend methods +""" + +from functools import cached_property + +import numba + +from PySDM.backends.impl_common.backend_methods import BackendMethods + + +class IsotopeMethods(BackendMethods): + @cached_property + def _isotopic_delta_body(self): + ff = self.formulae_flattened + + @numba.njit(**self.default_jit_flags) + def body(output, ratio, reference_ratio): + for i in numba.prange(output.shape[0]): # pylint: disable=not-an-iterable + output[i] = ff.trivia__isotopic_ratio_2_delta(ratio[i], reference_ratio) + + return body + + def isotopic_delta(self, output, ratio, reference_ratio): + self._isotopic_delta_body(output.data, ratio.data, reference_ratio) + + def isotopic_fractionation(self): + pass diff --git a/PySDM/source/PySDM/backends/impl_numba/methods/moments_methods.py b/PySDM/source/PySDM/backends/impl_numba/methods/moments_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..29cfba19ac10fb0cbcc58f0bbae433d7584eb865 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_numba/methods/moments_methods.py @@ -0,0 +1,182 @@ +""" +CPU implementation of moment calculation backend methods +""" + +from functools import cached_property + +import numba + +from PySDM.backends.impl_common.backend_methods import BackendMethods +from PySDM.backends.impl_numba.atomic_operations import atomic_add + + +class MomentsMethods(BackendMethods): + @cached_property + def _moments_body(self): + @numba.njit(**self.default_jit_flags) + def body( + *, + moment_0, + moments, + multiplicity, + attr_data, + cell_id, + idx, + length, + ranks, + min_x, + max_x, + x_attr, + weighting_attribute, + weighting_rank, + skip_division_by_m0, + ): + # pylint: disable=too-many-locals + moment_0[:] = 0 + moments[:, :] = 0 + for idx_i in numba.prange(length): # pylint: disable=not-an-iterable + i = idx[idx_i] + if min_x <= x_attr[i] < max_x: + atomic_add( + moment_0, + cell_id[i], + multiplicity[i] * weighting_attribute[i] ** weighting_rank, + ) + for k in range(ranks.shape[0]): + atomic_add( + moments, + (k, cell_id[i]), + ( + multiplicity[i] + * weighting_attribute[i] ** weighting_rank + * attr_data[i] ** ranks[k] + ), + ) + if not skip_division_by_m0: + for c_id in range(moment_0.shape[0]): + for k in range(ranks.shape[0]): + moments[k, c_id] = ( + moments[k, c_id] / moment_0[c_id] + if moment_0[c_id] != 0 + else 0 + ) + + return body + + def moments( + self, + *, + moment_0, + moments, + multiplicity, + attr_data, + cell_id, + idx, + length, + ranks, + min_x, + max_x, + x_attr, + weighting_attribute, + weighting_rank, + skip_division_by_m0, + ): + return self._moments_body( + moment_0=moment_0.data, + moments=moments.data, + multiplicity=multiplicity.data, + attr_data=attr_data.data, + cell_id=cell_id.data, + idx=idx.data, + length=length, + ranks=ranks.data, + min_x=min_x, + max_x=max_x, + x_attr=x_attr.data, + weighting_attribute=weighting_attribute.data, + weighting_rank=weighting_rank, + skip_division_by_m0=skip_division_by_m0, + ) + + @cached_property + def _spectrum_moments_body(self): + @numba.njit(**self.default_jit_flags) + def body( + *, + moment_0, + moments, + multiplicity, + attr_data, + cell_id, + idx, + length, + rank, + x_bins, + x_attr, + weighting_attribute, + weighting_rank, + ): + # pylint: disable=too-many-locals + moment_0[:, :] = 0 + moments[:, :] = 0 + for idx_i in numba.prange(length): # pylint: disable=not-an-iterable + i = idx[idx_i] + for k in range(x_bins.shape[0] - 1): + if x_bins[k] <= x_attr[i] < x_bins[k + 1]: + atomic_add( + moment_0, + (k, cell_id[i]), + multiplicity[i] * weighting_attribute[i] ** weighting_rank, + ) + atomic_add( + moments, + (k, cell_id[i]), + ( + multiplicity[i] + * weighting_attribute[i] ** weighting_rank + * attr_data[i] ** rank + ), + ) + break + for c_id in range(moment_0.shape[1]): + for k in range(x_bins.shape[0] - 1): + moments[k, c_id] = ( + moments[k, c_id] / moment_0[k, c_id] + if moment_0[k, c_id] != 0 + else 0 + ) + + return body + + def spectrum_moments( + self, + *, + moment_0, + moments, + multiplicity, + attr_data, + cell_id, + idx, + length, + rank, + x_bins, + x_attr, + weighting_attribute, + weighting_rank, + ): + assert moments.shape[0] == x_bins.shape[0] - 1 + assert moment_0.shape == moments.shape + return self._spectrum_moments_body( + moment_0=moment_0.data, + moments=moments.data, + multiplicity=multiplicity.data, + attr_data=attr_data.data, + cell_id=cell_id.data, + idx=idx.data, + length=length, + rank=rank, + x_bins=x_bins.data, + x_attr=x_attr.data, + weighting_attribute=weighting_attribute.data, + weighting_rank=weighting_rank, + ) diff --git a/PySDM/source/PySDM/backends/impl_numba/methods/pair_methods.py b/PySDM/source/PySDM/backends/impl_numba/methods/pair_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..48449873df6b5a986ecea58f9ae5c9b9e239bed8 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_numba/methods/pair_methods.py @@ -0,0 +1,180 @@ +""" +CPU implementation of pairwise operations backend methods +""" + +from functools import cached_property + +import numba +import numpy as np + +from PySDM.backends.impl_common.backend_methods import BackendMethods + + +class PairMethods(BackendMethods): + @cached_property + def _distance_pair_body(self): + @numba.njit(**self.default_jit_flags) + def body(data_out, data_in, is_first_in_pair, idx, length): + data_out[:] = 0 + for i in numba.prange(length - 1): # pylint: disable=not-an-iterable + if is_first_in_pair[i]: + data_out[i // 2] = np.abs(data_in[idx[i]] - data_in[idx[i + 1]]) + + return body + + def distance_pair(self, data_out, data_in, is_first_in_pair, idx): + return self._distance_pair_body( + data_out.data, + data_in.data, + is_first_in_pair.indicator.data, + idx.data, + len(idx), + ) + + @cached_property + def _find_pairs_body(self): + @numba.njit(**self.default_jit_flags) + def body(*, cell_start, is_first_in_pair, cell_id, cell_idx, idx, length): + for i in numba.prange(length - 1): # pylint: disable=not-an-iterable + is_in_same_cell = cell_id[idx[i]] == cell_id[idx[i + 1]] + is_even_index = (i - cell_start[cell_idx[cell_id[idx[i]]]]) % 2 == 0 + is_first_in_pair[i] = is_in_same_cell and is_even_index + is_first_in_pair[length - 1] = False + + return body + + # pylint: disable=too-many-arguments + def find_pairs(self, cell_start, is_first_in_pair, cell_id, cell_idx, idx): + return self._find_pairs_body( + cell_start=cell_start.data, + is_first_in_pair=is_first_in_pair.indicator.data, + cell_id=cell_id.data, + cell_idx=cell_idx.data, + idx=idx.data, + length=len(idx), + ) + + @cached_property + def _max_pair_body(self): + @numba.njit(**self.default_jit_flags) + def body(data_out, data_in, is_first_in_pair, idx, length): + data_out[:] = 0 + for i in numba.prange(length - 1): # pylint: disable=not-an-iterable + if is_first_in_pair[i]: + data_out[i // 2] = max(data_in[idx[i]], data_in[idx[i + 1]]) + + return body + + def max_pair(self, data_out, data_in, is_first_in_pair, idx): + return self._max_pair_body( + data_out.data, + data_in.data, + is_first_in_pair.indicator.data, + idx.data, + len(idx), + ) + + @cached_property + def _min_pair_body(self): + @numba.njit(**self.default_jit_flags) + def body(data_out, data_in, is_first_in_pair, idx, length): + data_out[:] = 0 + for i in numba.prange(length): # pylint: disable=not-an-iterable + if is_first_in_pair[i]: + data_out[i // 2] = min(data_in[idx[i]], data_in[idx[i + 1]]) + + return body + + def min_pair(self, data_out, data_in, is_first_in_pair, idx): + return self._min_pair_body( + data_out.data, + data_in.data, + is_first_in_pair.indicator.data, + idx.data, + len(idx), + ) + + @cached_property + def _sort_pair_body(self): + @numba.njit(**self.default_jit_flags) + def body(data_out, data_in, is_first_in_pair, idx, length): + data_out[:] = 0 + for i in numba.prange(length - 1): # pylint: disable=not-an-iterable + if is_first_in_pair[i]: + if data_in[idx[i]] < data_in[idx[i + 1]]: + data_out[i], data_out[i + 1] = ( + data_in[idx[i + 1]], + data_in[idx[i]], + ) + else: + data_out[i], data_out[i + 1] = ( + data_in[idx[i]], + data_in[idx[i + 1]], + ) + + return body + + def sort_pair(self, data_out, data_in, is_first_in_pair, idx): + return self._sort_pair_body( + data_out.data, + data_in.data, + is_first_in_pair.indicator.data, + idx.data, + len(idx), + ) + + @cached_property + def _sort_within_pair_by_attr_body(self): + @numba.njit(**self.default_jit_flags) + def body(idx, length, is_first_in_pair, attr): + for i in numba.prange(length - 1): # pylint: disable=not-an-iterable + if is_first_in_pair[i]: + if attr[idx[i]] < attr[idx[i + 1]]: + idx[i], idx[i + 1] = idx[i + 1], idx[i] + + return body + + def sort_within_pair_by_attr(self, idx, is_first_in_pair, attr): + self._sort_within_pair_by_attr_body( + idx.data, len(idx), is_first_in_pair.indicator.data, attr.data + ) + + @cached_property + def _sum_pair_body(self): + @numba.njit(**self.default_jit_flags) + def body(data_out, data_in, is_first_in_pair, idx, length): + data_out[:] = 0 + for i in numba.prange(length): # pylint: disable=not-an-iterable + if is_first_in_pair[i]: + data_out[i // 2] = data_in[idx[i]] + data_in[idx[i + 1]] + + return body + + def sum_pair(self, data_out, data_in, is_first_in_pair, idx): + return self._sum_pair_body( + data_out.data, + data_in.data, + is_first_in_pair.indicator.data, + idx.data, + len(idx), + ) + + @cached_property + def _multiply_pair_body(self): + @numba.njit(**self.default_jit_flags) + def body(data_out, data_in, is_first_in_pair, idx, length): + data_out[:] = 0 + for i in numba.prange(length - 1): # pylint: disable=not-an-iterable + if is_first_in_pair[i]: + data_out[i // 2] = data_in[idx[i]] * data_in[idx[i + 1]] + + return body + + def multiply_pair(self, data_out, data_in, is_first_in_pair, idx): + return self._multiply_pair_body( + data_out.data, + data_in.data, + is_first_in_pair.indicator.data, + idx.data, + len(idx), + ) diff --git a/PySDM/source/PySDM/backends/impl_numba/methods/physics_methods.py b/PySDM/source/PySDM/backends/impl_numba/methods/physics_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..8a9871d91f69b87c47cd04045a6854c9ec1bc349 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_numba/methods/physics_methods.py @@ -0,0 +1,213 @@ +""" +CPU implementation of backend methods wrapping basic physics formulae +""" + +from functools import cached_property + +import numba +from numba import prange + +from PySDM.backends.impl_common.backend_methods import BackendMethods + + +class PhysicsMethods(BackendMethods): + def __init__(self): + BackendMethods.__init__(self) + + @cached_property + def _critical_volume_body(self): + ff = self.formulae_flattened + + @numba.njit(**self.default_jit_flags) + def body(*, v_cr, kappa, f_org, v_dry, v_wet, T, cell): + for i in prange(len(v_cr)): # pylint: disable=not-an-iterable + sigma = ff.surface_tension__sigma( + T[cell[i]], v_wet[i], v_dry[i], f_org[i] + ) + v_cr[i] = ff.trivia__volume( + ff.hygroscopicity__r_cr( + kp=kappa[i], + rd3=v_dry[i] / ff.constants.PI_4_3, + T=T[cell[i]], + sgm=sigma, + ) + ) + + return body + + def critical_volume(self, *, v_cr, kappa, f_org, v_dry, v_wet, T, cell): + self._critical_volume_body( + v_cr=v_cr.data, + kappa=kappa.data, + f_org=f_org.data, + v_dry=v_dry.data, + v_wet=v_wet.data, + T=T.data, + cell=cell.data, + ) + + @cached_property + def _temperature_pressure_rh_body(self): + ff = self.formulae_flattened + + @numba.njit(**self.default_jit_flags) + def body(*, rhod, thd, water_vapour_mixing_ratio, T, p, RH): + for i in prange(T.shape[0]): # pylint: disable=not-an-iterable + T[i] = ff.state_variable_triplet__T(rhod[i], thd[i]) + p[i] = ff.state_variable_triplet__p( + rhod[i], T[i], water_vapour_mixing_ratio[i] + ) + RH[i] = ff.state_variable_triplet__pv( + p[i], water_vapour_mixing_ratio[i] + ) / ff.saturation_vapour_pressure__pvs_water(T[i]) + + return body + + def temperature_pressure_rh( + self, *, rhod, thd, water_vapour_mixing_ratio, T, p, RH + ): + self._temperature_pressure_rh_body( + rhod=rhod.data, + thd=thd.data, + water_vapour_mixing_ratio=water_vapour_mixing_ratio.data, + T=T.data, + p=p.data, + RH=RH.data, + ) + + @cached_property + def _a_w_ice_body(self): + ff = self.formulae_flattened + + @numba.njit(**self.default_jit_flags) + def body( + *, T_in, p_in, RH_in, water_vapour_mixing_ratio_in, a_w_ice_out, RH_ice_out + ): + for i in prange(T_in.shape[0]): # pylint: disable=not-an-iterable + pvi = ff.saturation_vapour_pressure__pvs_ice(T_in[i]) + pv = ff.state_variable_triplet__pv( + p_in[i], water_vapour_mixing_ratio_in[i] + ) + pvs = pv / RH_in[i] + a_w_ice_out[i] = pvi / pvs + RH_ice_out[i] = pv / pvi + + return body + + def a_w_ice(self, *, T, p, RH, water_vapour_mixing_ratio, a_w_ice, RH_ice): + self._a_w_ice_body( + T_in=T.data, + p_in=p.data, + RH_in=RH.data, + water_vapour_mixing_ratio_in=water_vapour_mixing_ratio.data, + a_w_ice_out=a_w_ice.data, + RH_ice_out=RH_ice.data, + ) + + @cached_property + def _volume_of_mass_body(self): + ff = self.formulae_flattened + + @numba.njit(**self.default_jit_flags) + def body(volume, mass): + for i in prange(volume.shape[0]): # pylint: disable=not-an-iterable + volume[i] = ff.particle_shape_and_density__mass_to_volume(mass[i]) + + return body + + def volume_of_water_mass(self, volume, mass): + self._volume_of_mass_body(volume.data, mass.data) + + @cached_property + def _mass_of_volume_body(self): + ff = self.formulae_flattened + + @numba.njit(**self.default_jit_flags) + def body(mass, volume): + for i in prange(volume.shape[0]): # pylint: disable=not-an-iterable + mass[i] = ff.particle_shape_and_density__volume_to_mass(volume[i]) + + return body + + def mass_of_water_volume(self, mass, volume): + self._mass_of_volume_body(mass.data, volume.data) + + @cached_property + def __air_density_body(self): + formulae = self.formulae.flatten + + @numba.njit(**self.default_jit_flags) + def body(output, rhod, water_vapour_mixing_ratio): + for i in numba.prange(output.shape[0]): # pylint: disable=not-an-iterable + output[i] = ( + formulae.state_variable_triplet__rho_of_rhod_and_water_vapour_mixing_ratio( + rhod[i], water_vapour_mixing_ratio[i] + ) + ) + + return body + + def air_density(self, *, output, rhod, water_vapour_mixing_ratio): + self.__air_density_body(output.data, rhod.data, water_vapour_mixing_ratio.data) + + @cached_property + def __air_dynamic_viscosity_body(self): + formulae = self.formulae.flatten + + @numba.njit(**self.default_jit_flags) + def body(output, temperature): + for i in numba.prange(output.shape[0]): # pylint: disable=not-an-iterable + output[i] = formulae.air_dynamic_viscosity__eta_air(temperature[i]) + + return body + + def air_dynamic_viscosity(self, *, output, temperature): + self.__air_dynamic_viscosity_body(output.data, temperature.data) + + @cached_property + def __reynolds_number_body(self): + formulae = self.formulae.flatten + + @numba.njit(**self.default_jit_flags) + def body( # pylint: disable=too-many-arguments + output, + cell_id, + air_dynamic_viscosity, + air_density, + radius, + velocity_wrt_air, + ): + for i in numba.prange(output.shape[0]): # pylint: disable=not-an-iterable + output[i] = formulae.particle_shape_and_density__reynolds_number( + radius=radius[i], + velocity_wrt_air=velocity_wrt_air[i], + dynamic_viscosity=air_dynamic_viscosity[cell_id[i]], + density=air_density[cell_id[i]], + ) + + return body + + def reynolds_number( + self, *, output, cell_id, dynamic_viscosity, density, radius, velocity_wrt_air + ): + self.__reynolds_number_body( + output.data, + cell_id.data, + dynamic_viscosity.data, + density.data, + radius.data, + velocity_wrt_air.data, + ) + + @cached_property + def _explicit_euler_body(self): + ff = self.formulae_flattened + + @numba.njit(**self.default_jit_flags) + def body(y, dt, dy_dt): + y[:] = ff.trivia__explicit_euler(y, dt, dy_dt) + + return body + + def explicit_euler(self, y, dt, dy_dt): + self._explicit_euler_body(y.data, dt, dy_dt) diff --git a/PySDM/source/PySDM/backends/impl_numba/methods/seeding_methods.py b/PySDM/source/PySDM/backends/impl_numba/methods/seeding_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..83fb64dc5a03ceddd50f2d8eb27aa212ce5341d2 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_numba/methods/seeding_methods.py @@ -0,0 +1,68 @@ +"""CPU implementation of backend methods for particle injections""" + +from functools import cached_property + +import numba + +from PySDM.backends.impl_common.backend_methods import BackendMethods + + +class SeedingMethods(BackendMethods): # pylint: disable=too-few-public-methods + @cached_property + def _seeding(self): + @numba.njit(**{**self.default_jit_flags, "parallel": False}) + def body( # pylint: disable=too-many-arguments + idx, + multiplicity, + extensive_attributes, + seeded_particle_index, + seeded_particle_multiplicity, + seeded_particle_extensive_attributes, + number_of_super_particles_to_inject: int, + ): + number_of_super_particles_already_injected = 0 + # TODO #1387 start enumerating from the end of valid particle set + for i, mult in enumerate(multiplicity): + if ( + number_of_super_particles_to_inject + == number_of_super_particles_already_injected + ): + break + if mult == 0: + idx[i] = -1 + s = seeded_particle_index[ + number_of_super_particles_already_injected + ] + number_of_super_particles_already_injected += 1 + multiplicity[i] = seeded_particle_multiplicity[s] + for a in range(len(extensive_attributes)): + extensive_attributes[a, i] = ( + seeded_particle_extensive_attributes[a, s] + ) + assert ( + number_of_super_particles_to_inject + == number_of_super_particles_already_injected + ) + + return body + + def seeding( + self, + *, + idx, + multiplicity, + extensive_attributes, + seeded_particle_index, + seeded_particle_multiplicity, + seeded_particle_extensive_attributes, + number_of_super_particles_to_inject: int, + ): + self._seeding( + idx=idx.data, + multiplicity=multiplicity.data, + extensive_attributes=extensive_attributes.data, + seeded_particle_index=seeded_particle_index.data, + seeded_particle_multiplicity=seeded_particle_multiplicity.data, + seeded_particle_extensive_attributes=seeded_particle_extensive_attributes.data, + number_of_super_particles_to_inject=number_of_super_particles_to_inject, + ) diff --git a/PySDM/source/PySDM/backends/impl_numba/methods/terminal_velocity_methods.py b/PySDM/source/PySDM/backends/impl_numba/methods/terminal_velocity_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..d74d28f9ca3084ce8948c142fc2888972dc7fe4b --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_numba/methods/terminal_velocity_methods.py @@ -0,0 +1,127 @@ +""" +CPU implementation of backend methods for terminal velocities +""" + +from functools import cached_property + +import numba + +from PySDM.backends.impl_common.backend_methods import BackendMethods + + +class TerminalVelocityMethods(BackendMethods): + + @cached_property + def _gunn_and_kinzer_interpolation_body(self): + @numba.njit(**self.default_jit_flags) + def body(output, radius, factor, b, c): + for i in numba.prange(len(radius)): # pylint: disable=not-an-iterable + if radius[i] > 0: + r_id = int(factor * radius[i]) + r_rest = ((factor * radius[i]) % 1) / factor + output[i] = b[r_id] + r_rest * c[r_id] + elif radius[i] == 0: + output[i] = 0 + + return body + + def gunn_and_kinzer_interpolation(self, *, output, radius, factor, b, c): + return self._gunn_and_kinzer_interpolation_body( + output.data, radius.data, factor, b.data, c.data + ) + + @cached_property + def _rogers_and_yau_terminal_velocity_body(self): + v_term = self.formulae.terminal_velocity.v_term + + @numba.njit(**self.default_jit_flags) + def body(*, values, radius): + for i in numba.prange(len(values)): # pylint: disable=not-an-iterable + if radius[i] >= 0.0: + values[i] = v_term(radius[i]) + + return body + + def rogers_and_yau_terminal_velocity(self, *, values, radius): + self._rogers_and_yau_terminal_velocity_body(values=values, radius=radius) + + @cached_property + def _power_series_body(self): + @numba.njit(**self.default_jit_flags) + def body(*, values, radius, num_terms, prefactors, powers): + for i in numba.prange(len(values)): # pylint: disable=not-an-iterable + values[i] = 0.0 + for j in range(num_terms): + values[i] = values[i] + prefactors[j] * radius[i] ** (powers[j] * 3) + + return body + + def power_series(self, *, values, radius, num_terms, prefactors, powers): + self._power_series_body( + values=values, + radius=radius, + num_terms=num_terms, + prefactors=prefactors, + powers=powers, + ) + + def terminal_velocity_columnar_ice_crystals( + self, *, values, signed_water_mass, cell_id, temperature, pressure + ): + self._terminal_velocity_columnar_ice_crystals_body( + values=values, + signed_water_mass=signed_water_mass, + cell_id=cell_id, + temperature=temperature, + pressure=pressure, + ) + + @cached_property + def _terminal_velocity_columnar_ice_crystals_body(self): + v_base_term = self.formulae.terminal_velocity_ice.v_base_term + atmospheric_correction_factor = ( + self.formulae.terminal_velocity_ice.atmospheric_correction_factor + ) + + @numba.njit(**self.default_jit_flags) + def body(*, values, signed_water_mass, cell_id, temperature, pressure): + for i in numba.prange(len(values)): # pylint: disable=not-an-iterable + if signed_water_mass[i] < 0: + cid = cell_id[i] + correction = atmospheric_correction_factor( + temperature[cid], pressure[cid] + ) + values[i] = v_base_term(-signed_water_mass[i]) * correction + + return body + + def terminal_velocity_ice_spheres( + self, *, values, signed_water_mass, cell_id, temperature, pressure + ): # pylint: disable=unused-argument + self._terminal_velocity_ice_spheres_body( + values=values, + signed_water_mass=signed_water_mass, + cell_id=cell_id, + temperature=temperature, + ) + + @cached_property + def _terminal_velocity_ice_spheres_body(self): + v_base_term = self.formulae.terminal_velocity_ice.v_base_term + stokes_prefactor = self.formulae.terminal_velocity_ice.stokes_regime + formulae = self.formulae_flattened + + def body(*, values, signed_water_mass, cell_id, temperature): + for i in numba.prange(len(values)): # pylint: disable=not-an-iterable + if signed_water_mass[i] < 0: + cid = cell_id[i] + radius = formulae.particle_shape_and_density__mass_to_radius( + signed_water_mass[i] + ) + dynamic_viscosity = formulae.air_dynamic_viscosity__eta_air( + temperature[cid] + ) + prefactor = stokes_prefactor(radius, dynamic_viscosity) + values[i] = v_base_term(prefactor, radius) + + return body diff --git a/PySDM/source/PySDM/backends/impl_numba/random.py b/PySDM/source/PySDM/backends/impl_numba/random.py new file mode 100644 index 0000000000000000000000000000000000000000..b155a6b94dcdf1bb52462bdafa45aaa7ec4e7ed9 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_numba/random.py @@ -0,0 +1,19 @@ +""" +random number generator class for Numba backend +""" + +import numpy as np + +from ..impl_common.random_common import RandomCommon + +# TIP: can be called asynchronously +# TIP: sometimes only half array is needed + + +class Random(RandomCommon): # pylint: disable=too-few-public-methods + def __init__(self, size, seed): + super().__init__(size, seed) + self.generator = np.random.default_rng(seed) + + def __call__(self, storage): + storage.data[:] = self.generator.uniform(0, 1, storage.shape) diff --git a/PySDM/source/PySDM/backends/impl_numba/storage.py b/PySDM/source/PySDM/backends/impl_numba/storage.py new file mode 100644 index 0000000000000000000000000000000000000000..64b2291a14585673a4e6f714f56f5017a6b73973 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_numba/storage.py @@ -0,0 +1,213 @@ +""" +CPU Numpy-based implementation of Storage class +""" + +import numpy as np + +from PySDM.backends.impl_common.storage_utils import ( + StorageBase, + StorageSignature, + empty, + get_data_from_ndarray, +) +from PySDM.backends.impl_numba import storage_impl as impl + + +class Storage(StorageBase): + FLOAT = np.float64 + INT = np.int64 + BOOL = np.bool_ + + def __getitem__(self, item): + dim = len(self.shape) + if isinstance(item, slice): + step = item.step or 1 + if step != 1: + raise NotImplementedError("step != 1") + start = item.start or 0 + if dim == 1: + stop = item.stop or len(self) + result_data = self.data[item] + result_shape = (stop - start,) + elif dim == 2: + stop = item.stop or self.shape[0] + result_data = self.data[item] + result_shape = (stop - start, self.shape[1]) + else: + raise NotImplementedError( + "Only 2 or less dimensions array is supported." + ) + if stop > self.data.shape[0]: + raise IndexError( + f"requested a slice ({start}:{stop}) of Storage" + f" with first dim of length {self.data.shape[0]}" + ) + result = Storage(StorageSignature(result_data, result_shape, self.dtype)) + elif isinstance(item, tuple) and dim == 2 and isinstance(item[1], slice): + result = Storage( + StorageSignature(self.data[item[0]], (*self.shape[1:],), self.dtype) + ) + else: + result = self.data[item] + return result + + def __setitem__(self, key, value): + if hasattr(value, "data"): + self.data[key] = value.data + else: + self.data[key] = value + return self + + def __iadd__(self, other): + if isinstance(other, Storage): + impl.add(self.data, other.data) + elif ( + isinstance(other, tuple) + and len(other) == 3 + and isinstance(other[0], float) + and other[1] == "*" + and isinstance(other[2], Storage) + ): + impl.add_with_multiplier(self.data, other[2].data, other[0]) + else: + impl.add(self.data, other) + return self + + def __isub__(self, other): + impl.subtract(self.data, other.data) + return self + + def __imul__(self, other): + if hasattr(other, "data"): + impl.multiply(self.data, other.data) + else: + impl.multiply(self.data, other) + return self + + def __itruediv__(self, other): + if hasattr(other, "data"): + self.data[:] /= other.data[:] + else: + self.data[:] /= other + return self + + def __imod__(self, other): + impl.row_modulo(self.data, other.data) + return self + + def __ipow__(self, other): + impl.power(self.data, other) + return self + + def __bool__(self): + if len(self) == 1: + result = bool(self.data[0] != 0) + else: + raise NotImplementedError("Logic value of array is ambiguous.") + return result + + def detach(self): + if self.data.base is not None: + self.data = np.array(self.data) + + def download(self, target, reshape=False): + if reshape: + data = self.data.reshape(target.shape) + else: + data = self.data + np.copyto(target, data, casting="safe") + + @staticmethod + def _get_empty_data(shape, dtype): + if dtype in (float, Storage.FLOAT): + data = np.full(shape, np.nan, dtype=Storage.FLOAT) + dtype = Storage.FLOAT + elif dtype in (int, Storage.INT): + data = np.full(shape, -1, dtype=Storage.INT) + dtype = Storage.INT + elif dtype in (bool, Storage.BOOL): + data = np.full(shape, -1, dtype=Storage.BOOL) + dtype = Storage.BOOL + else: + raise NotImplementedError() + + return StorageSignature(data, shape, dtype) + + @staticmethod + def empty(shape, dtype): + return empty(shape, dtype, Storage) + + @staticmethod + def _get_data_from_ndarray(array): + return get_data_from_ndarray( + array=array, + storage_class=Storage, + copy_fun=lambda array_astype: array_astype.copy(), + ) + + def amin(self): + return impl.amin(self.data) + + def amax(self): + return impl.amax(self.data) + + def all(self): + return self.data.all() + + @staticmethod + def from_ndarray(array): + result = Storage(Storage._get_data_from_ndarray(array)) + return result + + def floor(self, other=None): + if other is None: + impl.floor(self.data) + else: + impl.floor_out_of_place(self.data, other.data) + return self + + def product(self, multiplicand, multiplier): + if hasattr(multiplier, "data"): + impl.multiply_out_of_place(self.data, multiplicand.data, multiplier.data) + else: + impl.multiply_out_of_place(self.data, multiplicand.data, multiplier) + return self + + def ratio(self, dividend, divisor): + impl.divide_out_of_place(self.data, dividend.data, divisor.data) + return self + + def divide_if_not_zero(self, divisor): + impl.divide_if_not_zero(self.data, divisor.data) + return self + + def sum(self, arg_a, arg_b): + impl.sum_out_of_place(self.data, arg_a.data, arg_b.data) + return self + + def ravel(self, other): + if isinstance(other, Storage): + self.data[:] = other.data.ravel() + else: + self.data[:] = other.ravel() + + def urand(self, generator): + generator(self) + + def to_ndarray(self): + return self.data.copy() + + def upload(self, data): + np.copyto(self.data, data, casting="safe") + + def fill(self, other): + if isinstance(other, Storage): + self.data[:] = other.data + else: + self.data[:] = other + + def exp(self): + self.data[:] = np.exp(self.data) + + def abs(self): + self.data[:] = np.abs(self.data) diff --git a/PySDM/source/PySDM/backends/impl_numba/storage_impl.py b/PySDM/source/PySDM/backends/impl_numba/storage_impl.py new file mode 100644 index 0000000000000000000000000000000000000000..2d1b2001947ccb253cf039e2ae98ac1d890801c2 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_numba/storage_impl.py @@ -0,0 +1,83 @@ +""" +Numba njit-ted basic arithmetics routines for CPU backend +""" + +import numba +import numpy as np + +from PySDM.backends.impl_numba import conf + + +@numba.njit(**{**conf.JIT_FLAGS, **{"parallel": False}}) +def add(output, addend): + output += addend + + +@numba.njit(**{**conf.JIT_FLAGS, **{"parallel": False}}) +def add_with_multiplier(output, addend, multiplier): + output += multiplier * addend + + +@numba.njit(**conf.JIT_FLAGS) +def amin(data): + return np.amin(data) + + +@numba.njit(**conf.JIT_FLAGS) +def amax(data): + return np.amax(data) + + +@numba.njit(**conf.JIT_FLAGS) +def row_modulo(output, divisor): + for d in range(output.shape[0]): + for i in numba.prange(output.shape[1]): # pylint: disable=not-an-iterable + output[d, i] %= divisor[d] + + +@numba.njit(**conf.JIT_FLAGS) +def divide_if_not_zero(output, divisor): + for i in numba.prange(output.shape[0]): # pylint: disable=not-an-iterable + if divisor[i] != 0.0: + output[i] /= divisor[i] + + +@numba.njit(**conf.JIT_FLAGS) +def floor(output): + output[:] = np.floor(output) + + +@numba.njit(**conf.JIT_FLAGS) +def floor_out_of_place(output, input_data): + output[:] = np.floor(input_data) + + +@numba.njit(**{**conf.JIT_FLAGS, **{"parallel": False}}) +def multiply(output, multiplier): + output *= multiplier + + +@numba.njit(**conf.JIT_FLAGS) +def multiply_out_of_place(output, multiplicand, multiplier): + output[:] = multiplicand * multiplier + + +@numba.njit(**conf.JIT_FLAGS) +def divide_out_of_place(output, dividend, divisor): + output[:] = dividend / divisor + + +@numba.njit(**conf.JIT_FLAGS) +def sum_out_of_place(output, a, b): + output[:] = a + b + + +@numba.njit(**conf.JIT_FLAGS) +def power(output, exponent): + # TODO #599 (was: output[:] = np.power(output, exponent)) + output[:] = np.sign(output) * np.power(np.abs(output), exponent) + + +@numba.njit(**{**conf.JIT_FLAGS, **{"parallel": False}}) +def subtract(output, subtrahend): + output[:] -= subtrahend[:] diff --git a/PySDM/source/PySDM/backends/impl_numba/test_helpers/__init__.py b/PySDM/source/PySDM/backends/impl_numba/test_helpers/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d64031beb554371688c8ace27fdf7ee6652951de --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_numba/test_helpers/__init__.py @@ -0,0 +1 @@ +"""logic intended to be used in tests only including the SciPy-based condensation solver""" diff --git a/PySDM/source/PySDM/backends/impl_numba/test_helpers/scipy_ode_condensation_solver.py b/PySDM/source/PySDM/backends/impl_numba/test_helpers/scipy_ode_condensation_solver.py new file mode 100644 index 0000000000000000000000000000000000000000..1ea2a43d0b19cbfefe5878b20643bfa0941a20f3 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_numba/test_helpers/scipy_ode_condensation_solver.py @@ -0,0 +1,310 @@ +""" +condensation/evaporation solver drop-in replacement implemented using + SciPy adaptive-timestep ODE solver, for use in tests only +""" + +import types +from functools import lru_cache + +import numba +import numpy as np +import scipy.integrate + +from PySDM.backends import Numba +from PySDM.backends.impl_numba.conf import JIT_FLAGS +from PySDM.backends.impl_numba.methods.condensation_methods import ( + _RelativeTolerances, + _Attributes, + _CellData, + _Counters, +) +from PySDM.physics.constants_defaults import PI_4_3 + +idx_thd = 0 +idx_x = 1 +rtol = 1e-6 + + +def patch_particulator(particulator): + particulator.condensation_solver = _make_solve(particulator.formulae) + particulator.condensation = types.MethodType(_condensation, particulator) + + +def _condensation( + particulator, *, rtol_x, rtol_thd, counters, RH_max, success, cell_order +): + func = Numba._condensation + if not numba.config.DISABLE_JIT: # pylint: disable=no-member + func = func.py_func + func( + solver=particulator.condensation_solver, + n_threads=1, + n_cell=particulator.mesh.n_cell, + cell_start_arg=particulator.attributes.cell_start.data, + attributes=_Attributes( + signed_water_mass=particulator.attributes["signed water mass"].data, + v_cr=None, + multiplicity=particulator.attributes["multiplicity"].data, + vdry=particulator.attributes["dry volume"].data, + kappa=particulator.attributes["kappa"].data, + f_org=particulator.attributes["dry volume organic fraction"].data, + reynolds_number=particulator.attributes["Reynolds number"].data, + ), + idx=particulator.attributes._ParticleAttributes__idx.data, + cell_data=_CellData( + rhod=particulator.environment["rhod"].data, + thd=particulator.environment["thd"].data, + water_vapour_mixing_ratio=particulator.environment[ + "water_vapour_mixing_ratio" + ].data, + dv_mean=particulator.environment.dv, + prhod=particulator.environment.get_predicted("rhod").data, + pthd=particulator.environment.get_predicted("thd").data, + predicted_water_vapour_mixing_ratio=particulator.environment.get_predicted( + "water_vapour_mixing_ratio" + ).data, + air_density=particulator.environment["air density"].data, + air_dynamic_viscosity=particulator.environment[ + "air dynamic viscosity" + ].data, + ), + rtols=_RelativeTolerances( + x=rtol_x, + thd=rtol_thd, + ), + timestep=particulator.dt, + counters=_Counters( + n_substeps=counters["n_substeps"], + n_activating=counters["n_activating"], + n_deactivating=counters["n_deactivating"], + n_ripening=counters["n_ripening"], + ), + cell_order=cell_order, + RH_max=RH_max.data, + success=success.data, + ) + particulator.attributes.mark_updated("water mass") + + +@lru_cache() +def _make_solve(formulae): # pylint: disable=too-many-statements,too-many-locals + jit_formulae = formulae.flatten + + @numba.njit(**{**JIT_FLAGS, **{"parallel": False}}) + def _liquid_water_mixing_ratio(n, x, m_d_mean): + return np.sum(n * jit_formulae.diffusion_coordinate__mass(x)) / m_d_mean + + @numba.njit(**{**JIT_FLAGS, **{"parallel": False}}) + def _impl( # pylint: disable=too-many-arguments,too-many-locals + dy_dt, + x, + T, + p, + n, + RH, + reynolds_number, + kappa, + f_org, + dry_volume, + thd, + dot_thd, + dot_water_vapour_mixing_ratio, + m_d_mean, + air_density, + air_dynamic_viscosity, + rhod, + pvs, + lv, + ): + DTp = jit_formulae.diffusion_thermics__D(T, p) + KTp = jit_formulae.diffusion_thermics__K(T, p) + lambdaD = jit_formulae.diffusion_kinetics__lambdaD(DTp, T) + lambdaK = jit_formulae.diffusion_kinetics__lambdaK(T, p) + schmidt_number = jit_formulae.trivia__air_schmidt_number( + dynamic_viscosity=air_dynamic_viscosity, + diffusivity=DTp, + density=air_density, + ) + sum_n_dm_dt = 0 + for i, x_i in enumerate(x): + m = jit_formulae.diffusion_coordinate__mass(x_i) + v = jit_formulae.particle_shape_and_density__mass_to_volume(m) + r = jit_formulae.trivia__radius(v) + Dr = jit_formulae.diffusion_kinetics__D(DTp, r, lambdaD) + Kr = jit_formulae.diffusion_kinetics__K(KTp, r, lambdaK) + mass_ventilation_factor = jit_formulae.ventilation__ventilation_coefficient( + sqrt_re_times_cbrt_sc=jit_formulae.trivia__sqrt_re_times_cbrt_sc( + Re=reynolds_number[i], + Sc=schmidt_number, + ) + ) + heat_ventilation_factor = mass_ventilation_factor # TODO #1588 + sgm = jit_formulae.surface_tension__sigma(T, v, dry_volume[i], f_org[i]) + Fk = jit_formulae.drop_growth__Fk( + T=T, + lv=lv, + K=heat_ventilation_factor * Kr, + ) + Fd = jit_formulae.drop_growth__Fd( + T=T, + pvs=pvs, + D=mass_ventilation_factor * Dr, + ) + r_dr_dt = jit_formulae.drop_growth__r_dr_dt( + RH_eq=jit_formulae.hygroscopicity__RH_eq( + r, T, kappa[i], dry_volume[i] / PI_4_3, sgm + ), + RH=RH, + Fk=Fk, + Fd=Fd, + ) + dm_dt = jit_formulae.particle_shape_and_density__dm_dt(r, r_dr_dt) + dy_dt[idx_x + i] = jit_formulae.diffusion_coordinate__dx_dt(m, dm_dt) + sum_n_dm_dt += n[i] * dm_dt + dy_dt[idx_thd] = dot_thd + jit_formulae.state_variable_triplet__dthd_dt( + rhod, thd, T, dot_water_vapour_mixing_ratio - sum_n_dm_dt / m_d_mean, lv + ) + + @numba.njit(**{**JIT_FLAGS, **{"parallel": False}}) + def _odesys( # pylint: disable=too-many-arguments,too-many-locals + t, + y, + kappa, + f_org, + dry_volume, + n, + reynolds_number, + dthd_dt, + d_water_vapour_mixing_ratio__dt, + drhod_dt, + m_d_mean, + air_density, + air_dynamic_viscosity, + rhod, + qt, + ): + thd = y[idx_thd] + x = y[idx_x:] + + water_vapour_mixing_ratio = ( + qt + + d_water_vapour_mixing_ratio__dt * t + - _liquid_water_mixing_ratio(n, x, m_d_mean) + ) + rhod += drhod_dt * t + T = jit_formulae.state_variable_triplet__T(rhod, thd) + p = jit_formulae.state_variable_triplet__p(rhod, T, water_vapour_mixing_ratio) + pv = jit_formulae.state_variable_triplet__pv(p, water_vapour_mixing_ratio) + pvs = jit_formulae.saturation_vapour_pressure__pvs_water(T) + RH = pv / pvs + + dy_dt = np.empty_like(y) + _impl( + dy_dt, + x, + T, + p, + n, + RH, + reynolds_number, + kappa, + f_org, + dry_volume, + thd, + dthd_dt, + d_water_vapour_mixing_ratio__dt, + m_d_mean, + air_density, + air_dynamic_viscosity, + rhod, + pvs, + jit_formulae.latent_heat_vapourisation__lv(T), + ) + return dy_dt + + def solve( # pylint: disable=too-many-arguments,too-many-locals,unused-argument + attributes, + cell_idx, + thd, + water_vapour_mixing_ratio, + rhod, + dthd_dt, + d_water_vapour_mixing_ratio__dt, + drhod_dt, + m_d, + air_density, + air_dynamic_viscosity, + rtols, + timestep, + n_substeps, + ): + n_sd_in_cell = len(cell_idx) + y0 = np.empty(n_sd_in_cell + idx_x) + y0[idx_thd] = thd + y0[idx_x:] = jit_formulae.diffusion_coordinate__x( + attributes.signed_water_mass[cell_idx] + ) + total_water_mixing_ratio = ( + water_vapour_mixing_ratio + + _liquid_water_mixing_ratio( + attributes.multiplicity[cell_idx], y0[idx_x:], m_d + ) + ) + args = ( + attributes.kappa[cell_idx], + attributes.f_org[cell_idx], + attributes.vdry[cell_idx], + attributes.multiplicity[cell_idx], + attributes.reynolds_number[cell_idx], + dthd_dt, + d_water_vapour_mixing_ratio__dt, + drhod_dt, + m_d, + air_density, + air_dynamic_viscosity, + rhod, + total_water_mixing_ratio, + ) + if ( + dthd_dt == 0 + and d_water_vapour_mixing_ratio__dt == 0 + and drhod_dt == 0 + and (_odesys(0, y0, *args)[idx_x:] == 0).all() + ): + y1 = y0 + else: + integ = scipy.integrate.solve_ivp( + fun=_odesys, + args=args, + t_span=(0, timestep), + t_eval=(timestep,), + y0=y0, + rtol=rtol, + atol=0, + method="LSODA", + ) + assert integ.success, integ.message + y1 = integ.y[:, 0] + + m_new = 0 + for i in range(n_sd_in_cell): + attributes.signed_water_mass[cell_idx[i]] = ( + jit_formulae.diffusion_coordinate__mass(y1[idx_x + i]) + ) + m_new += ( + attributes.multiplicity[cell_idx[i]] + * attributes.signed_water_mass[cell_idx[i]] + ) + + return ( + integ.success, + total_water_mixing_ratio - m_new / m_d, + y1[idx_thd], + 1, + 1, + 1, + 1, + np.nan, + ) + + return solve diff --git a/PySDM/source/PySDM/backends/impl_numba/toms748.py b/PySDM/source/PySDM/backends/impl_numba/toms748.py new file mode 100644 index 0000000000000000000000000000000000000000..6a09916a1af863a66b7d4ed0dcb433cea5a45f87 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_numba/toms748.py @@ -0,0 +1,217 @@ +# pylint: disable=invalid-name,too-many-arguments,too-many-locals,too-many-branches,too-many-statements +""" +Numba-based TOM 748 root-finding algorithm implementation adapted from Maciej Waruszewski's +libcloudph++ version (GPL) which in turn was based on Boost implementation +(Copyright John Maddock 2006 http://www.boost.org/LICENSE_1_0.txt) + +https://github.com/igfuw/libcloudphxx/blob/master/include/libcloudph%2B%2B/common/detail/toms748.hpp +""" + +from sys import float_info + +import numba +from numpy import nan + +from PySDM.backends.impl_numba.conf import JIT_FLAGS +from PySDM.backends.impl_numba.warnings import warn + +float_info_epsilon = float_info.epsilon +float_info_max = float_info.max +float_info_min = float_info.min + + +@numba.njit(**{**JIT_FLAGS, **{"parallel": False, "cache": False}}) +def bracket(f, args, a, b, c, fa, fb): + tol = float_info_epsilon * 2 + if (b - a) < 2 * tol * a: + c = a + (b - a) / 2 + elif c <= a + abs(a) * tol: + c = a + abs(a) * tol + elif c >= b - abs(b) * tol: + c = b - abs(a) * tol + fc = f(c, *args) + if fc == 0: + a = c + fa = 0 + d = 0 + fd = 0 + else: + if fa * fc < 0: + d = b + fd = fb + b = c + fb = fc + else: + d = a + fd = fa + a = c + fa = fc + return a, b, fa, fb, d, fd + + +@numba.njit(**{**JIT_FLAGS, **{"parallel": False}}) +def safe_div(num, denom, r): + if abs(denom) < 1: + if abs(denom * float_info_max) <= abs(num): + return r + return num / denom + + +@numba.njit(**{**JIT_FLAGS, **{"parallel": False}}) +def secant_interpolate(a, b, fa, fb): + tol = float_info_epsilon * 5 + c = a - (fa / (fb - fa)) * (b - a) + if c <= a + abs(a) * tol or c >= b - abs(b) * tol: + return (a + b) / 2 + return c + + +@numba.njit(**{**JIT_FLAGS, **{"parallel": False}}) +def quadratic_interpolate(a, b, d, fa, fb, fd, count): + B = safe_div(fb - fa, b - a, float_info_max) + A = safe_div(fd - fb, d - b, float_info_max) + A = safe_div(A - B, d - a, 0.0) + + if A == 0: + return secant_interpolate(a, b, fa, fb) + if A * fa > 0: + c = a + else: + c = b + for _ in range(1, count + 1): + c -= safe_div( + fa + (B + A * (c - b)) * (c - a), B + A * (2.0 * c - a - b), 1.0 + c - a + ) + if (c <= a) or (c >= b): + c = secant_interpolate(a, b, fa, fb) + return c + + +@numba.njit(**{**JIT_FLAGS, **{"parallel": False}}) +def cubic_interpolate(a, b, d, e, fa, fb, fd, fe): + q11 = (d - e) * fd / (fe - fd) + q21 = (b - d) * fb / (fd - fb) + q31 = (a - b) * fa / (fb - fa) + d21 = (b - d) * fd / (fd - fb) + d31 = (a - b) * fb / (fb - fa) + + q22 = (d21 - q11) * fb / (fe - fb) + q32 = (d31 - q21) * fa / (fd - fa) + d32 = (d31 - q21) * fd / (fd - fa) + q33 = (d32 - q22) * fa / (fe - fa) + c = q31 + q32 + q33 + a + + if (c <= a) or (c >= b): + c = quadratic_interpolate(a, b, d, fa, fb, fd, 3) + return c + + +@numba.njit(**{**JIT_FLAGS, **{"parallel": False}}) +def tol_check(a, b, rtol, within_tolerance): + return within_tolerance(abs(a - b), min(abs(a), abs(b)), rtol) + + +@numba.njit(**{**JIT_FLAGS, **{"parallel": False}}) +def toms748_solve(f, args, ax, bx, fax, fbx, rtol, max_iter, within_tolerance): + count = max_iter + mu = 0.5 + a = ax + b = bx + fa = fax + fb = fbx + if not a < b: + return warn("TOMS748 problem: not a < b", __file__, return_value=(nan, -1)) + + if tol_check(a, b, rtol, within_tolerance) or fa == 0 or fb == 0: + max_iter = 0 + if fa == 0: + b = a + elif fb == 0: + a = b + return (a + b) / 2, max_iter + + if not fa * fb < 0: + return warn( + "TOMS748 problem: not fa * fb < 0", __file__, return_value=(nan, -1) + ) + + fe = e = fd = 1e5 + + if fa != 0: + c = secant_interpolate(a, b, fa, fb) + a, b, fa, fb, d, fd = bracket(f, args, a, b, c, fa, fb) + count -= 1 + + if count > 0 and fa != 0 and not tol_check(a, b, rtol, within_tolerance): + c = quadratic_interpolate(a, b, d, fa, fb, fd, 2) + e = d + fe = fd + a, b, fa, fb, d, fd = bracket(f, args, a, b, c, fa, fb) + count -= 1 + + while count > 0 and fa != 0 and not tol_check(a, b, rtol, within_tolerance): + a0 = a + b0 = b + min_diff = float_info_min * 32 + prof = ( + abs(fa - fb) < min_diff + or abs(fa - fd) < min_diff + or abs(fa - fe) < min_diff + or abs(fb - fd) < min_diff + or abs(fb - fe) < min_diff + or abs(fd - fe) < min_diff + ) + if prof: + c = quadratic_interpolate(a, b, d, fa, fb, fd, 2) + else: + c = cubic_interpolate(a, b, d, e, fa, fb, fd, fe) + e = d + fe = fd + a, b, fa, fb, d, fd = bracket(f, args, a, b, c, fa, fb) + if count == 1 or fa == 0 or tol_check(a, b, rtol, within_tolerance): + count -= 1 + break + prof = ( + abs(fa - fb) < min_diff + or abs(fa - fd) < min_diff + or abs(fa - fe) < min_diff + or abs(fb - fd) < min_diff + or abs(fb - fe) < min_diff + or abs(fd - fe) < min_diff + ) + if prof: + c = quadratic_interpolate(a, b, d, fa, fb, fd, 3) + else: + c = cubic_interpolate(a, b, d, e, fa, fb, fd, fe) + a, b, fa, fb, d, fd = bracket(f, args, a, b, c, fa, fb) + if count == 1 or fa == 0 or tol_check(a, b, rtol, within_tolerance): + count -= 1 + break + if abs(fa) < abs(fb): + u = a + fu = fa + else: + u = b + fu = fb + c = u - 2 * (fu / (fb - fa)) * (b - a) + if abs(c - u) > (b - a) / 2: + c = a + (b - a) / 2 + e = d + fe = fd + a, b, fa, fb, d, fd = bracket(f, args, a, b, c, fa, fb) + if count == 1 or fa == 0 or tol_check(a, b, rtol, within_tolerance): + count -= 1 + break + if (b - a) < mu * (b0 - a0): + continue + e = d + fe = fd + a, b, fa, fb, d, fd = bracket(f, args, a, b, a + (b - a) / 2, fa, fb) + count -= 1 + + max_iter -= count + if fa == 0: + b = a + elif fb == 0: + a = b + return (a + b) / 2, max_iter diff --git a/PySDM/source/PySDM/backends/impl_numba/warnings.py b/PySDM/source/PySDM/backends/impl_numba/warnings.py new file mode 100644 index 0000000000000000000000000000000000000000..3093f8fa6563637b8494037b12dc5ff743acea51 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_numba/warnings.py @@ -0,0 +1,22 @@ +""" +warning reporting logic for use whithin Numba njitted code (printing to standard + error using numba.objmode() allowing to capture the output from Python tests +""" + +import sys + +import numba + +from PySDM.backends.impl_numba import conf + + +@numba.njit(**{**conf.JIT_FLAGS, **{"parallel": False}}) +def warn(msg, file, context=None, return_value=None): + with numba.objmode(): + print(msg, file=sys.stderr) + print("\tfile:", file, file=sys.stderr) + if context is not None: + print("\tcontext:", file=sys.stderr) + for var in context: + print("\t\t", var, file=sys.stderr) + return return_value diff --git a/PySDM/source/PySDM/backends/impl_thrust_rtc/__init__.py b/PySDM/source/PySDM/backends/impl_thrust_rtc/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..b5239a840a5dfc11705525b2bd60f04a62c3e1f3 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_thrust_rtc/__init__.py @@ -0,0 +1 @@ +"""the guts of the GPU backend""" diff --git a/PySDM/source/PySDM/backends/impl_thrust_rtc/bisection.py b/PySDM/source/PySDM/backends/impl_thrust_rtc/bisection.py new file mode 100644 index 0000000000000000000000000000000000000000..5e332cfd892283dda8224de187cfbbd308639d93 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_thrust_rtc/bisection.py @@ -0,0 +1,52 @@ +""" +C code of basic bisection root-finding algorithm for use within ThrustRTC CUDA codes +""" + +BISECTION = """ + struct Bisect { + static __device__ real_type bisect( + real_type (*f)(real_type , void*), + void* args, + real_type min, real_type max, + real_type fmin, real_type fmax, + real_type tol + ) { + assert(min < max); // assumes in order + + if (fmin == 0) { + return min; + } + if (fmax == 0) { + return max; + } + + // if bisection is ill-posed, return mid of the range + if (fmin * fmax >= 0) { + return (min + max) / 2; + } + + real_type mid = (min + max) / 2; + while (abs(max - min) > tol * abs(mid)) { + if (mid == max || mid == min) { + break; + } + + real_type fmid = f(mid, args); + + if (fmid == 0) { + break; + } + else if (fmid * fmin > 0) { + min = mid; + fmin = fmid; + } + else { + max = mid; + fmax = fmid; + } + mid = (min + max) / 2; + } + return mid; + } + }; +""" diff --git a/PySDM/source/PySDM/backends/impl_thrust_rtc/conf.py b/PySDM/source/PySDM/backends/impl_thrust_rtc/conf.py new file mode 100644 index 0000000000000000000000000000000000000000..566eeca5f448216acd4c2f73a0b8f9ea8d569897 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_thrust_rtc/conf.py @@ -0,0 +1,40 @@ +""" +ThrustRTC import logic implementing the switch between the genuine ThrustRTC and FakeThrustRTC; +default nice_thrust flags +""" + +import os +from warnings import warn + +from PySDM.backends.impl_thrust_rtc.test_helpers.flag import fakeThrustRTC + +if not fakeThrustRTC: + try: + # noinspection PyUnresolvedReferences + import ThrustRTC as trtc # pylint: disable=unused-import + + if "NVRTC_PATH" in os.environ: + trtc.set_libnvrtc_path(os.environ["NVRTC_PATH"]) + except OSError as error: + warn(f"importing ThrustRTC failed with {error}") + try: + # noinspection PyUnresolvedReferences + import CURandRTC as rndrtc # pylint: disable=unused-import + except OSError as error: + warn(f"importing CURandRTC failed with {error}") +else: + # noinspection PyUnresolvedReferences + # fmt: off + # isort: off + from .test_helpers.fake_thrust_rtc import ( # pylint: disable=unused-import + FakeThrustRTC as trtc, + ) + # isort: on + # fmt: on + # noinspection PyUnresolvedReferences + rndrtc = None + +NICE_THRUST_FLAGS = { + "wait": False, + "debug_print": False, +} # TODO #1120: move to GPU backend ctor diff --git a/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/__init__.py b/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..0464ba4f743a918863be6582fb933727a66ebe7d --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/__init__.py @@ -0,0 +1 @@ +"""method classes of the GPU backend""" diff --git a/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/collisions_methods.py b/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/collisions_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..9b21d6ef66e7ed9380b39ff92d860cfb1796cbc3 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/collisions_methods.py @@ -0,0 +1,1118 @@ +""" +GPU implementation of backend methods for particle collisions +""" + +from functools import cached_property + +from PySDM.backends.impl_thrust_rtc.conf import NICE_THRUST_FLAGS +from PySDM.backends.impl_thrust_rtc.nice_thrust import nice_thrust + +from ..conf import trtc +from ..methods.thrust_rtc_backend_methods import ThrustRTCBackendMethods + +# pylint: disable=too-many-lines + +COMMONS = """ +struct Commons { + static __device__ void flag_zero_multiplicity( + int64_t j, + int64_t k, + VectorView multiplicity, + VectorView healthy + ) { + if (multiplicity[k] == 0 || multiplicity[j] == 0) { + healthy[0] = 0; + } + } + + static __device__ void coalesce( + int64_t i, + int64_t j, + int64_t k, + VectorView cell_id, + VectorView multiplicity, + VectorView gamma, + VectorView attributes, + VectorView coalescence_rate, + int64_t n_attr, + int64_t n_sd + ) { + auto cid = cell_id[j]; + atomicAdd((unsigned long long int*)&coalescence_rate[cid], (unsigned long long int)(gamma[i] * multiplicity[k])); + auto new_n = multiplicity[j] - gamma[i] * multiplicity[k]; + if (new_n > 0) { + multiplicity[j] = new_n; + for (auto attr = 0; attr < n_attr * n_sd; attr+=n_sd) { + attributes[attr + k] += gamma[i] * attributes[attr + j]; + } + } + else { // new_n == 0 + multiplicity[j] = (int64_t)(multiplicity[k] / 2); + multiplicity[k] = multiplicity[k] - multiplicity[j]; + for (auto attr = 0; attr < n_attr * n_sd; attr+=n_sd) { + attributes[attr + j] = gamma[i] * attributes[attr + j] + attributes[attr + k]; + attributes[attr + k] = attributes[attr + j]; + } + } + } + + static __device__ auto pair_indices( + int64_t i, + int64_t *j, + int64_t *k, + VectorView idx, + VectorView is_first_in_pair, + VectorView prob_like + ) { + if (prob_like[i] == 0) { + return true; + } + auto offset = 1 - is_first_in_pair[2 * i]; + j[0] = idx[2 * i + offset]; + k[0] = idx[2 * i + 1 + offset]; + return false; + } + + static __device__ auto compute_transfer_multiplicities( + real_type gamma, + int64_t j, + int64_t k, + VectorView multiplicity, + VectorView particle_mass, + real_type fragment_mass_i, + int64_t max_multiplicity, + + real_type *take_from_j, + real_type *new_mult_k + ) { + real_type gamma_j_k = 0; + real_type take_from_j_test = multiplicity[k]; + take_from_j[0] = 0; + real_type new_mult_k_test = ((particle_mass[j] + particle_mass[k]) / fragment_mass_i * multiplicity[k]); + new_mult_k[0] = multiplicity[k]; + + for (auto m = 0; m < (int64_t) (gamma); m += 1) { + if (new_mult_k_test > max_multiplicity) { + break; + } + + if (take_from_j_test > multiplicity[j]) { + break; + } + + take_from_j[0] = take_from_j_test; + new_mult_k[0] = new_mult_k_test; + gamma_j_k = m + 1; + + take_from_j_test += new_mult_k_test; + new_mult_k_test = new_mult_k_test * (particle_mass[j] / fragment_mass_i) + new_mult_k_test; + } + return gamma_j_k; + } + + static __device__ void get_new_multiplicities_and_update_attributes( + int64_t j, + int64_t k, + VectorView attributes, + VectorView multiplicity, + real_type take_from_j, + real_type new_mult_k, + int64_t n_attr, + + real_type *nj, + real_type *nk + ) { + for (auto a = 0; a < n_attr; a +=1) { + attributes[a + k] *= multiplicity[k]; + attributes[a + k] += take_from_j * attributes[a + j]; + attributes[a + k] /= new_mult_k; + } + + if (multiplicity[j] > take_from_j) { + nj[0] = multiplicity[j] - take_from_j; + nk[0] = new_mult_k; + } else { + nj[0] = new_mult_k / 2; + nk[0] = nj[0]; + + for (auto a = 0; a < n_attr; a += 1) { + attributes[a + j] = attributes[a + k]; + } + } + } + + static __device__ void round_multiplicities_to_ints_and_update_attributes( + int64_t j, + int64_t k, + real_type nj, + real_type nk, + VectorView attributes, + VectorView multiplicity, + real_type take_from_j, + int64_t n_attr + ) { + multiplicity[j] = max((int64_t)(round(nj)), (int64_t)(1)); + multiplicity[k] = max((int64_t)(round(nk)), (int64_t)(1)); + auto factor_j = nj / multiplicity[j]; + auto factor_k = nk / multiplicity[k]; + for (auto a = 0; a < n_attr; a +=1) { + attributes[a + k] *= factor_k; + attributes[a + j] *= factor_j; + } + } + + static __device__ void break_up( + int64_t i, + int64_t j, + int64_t k, + int64_t cid, + VectorView multiplicity, + VectorView gamma, + VectorView attributes, + VectorView fragment_mass, + int64_t max_multiplicity, + VectorView breakup_rate, + VectorView breakup_rate_deficit, + VectorView particle_mass, + int64_t n_sd, + int64_t n_attr + ) { + real_type take_from_j[1] = {}; // float + real_type new_mult_k[1] = {}; // float + auto gamma_j_k = Commons::compute_transfer_multiplicities( + gamma[i], + j, + k, + multiplicity, + particle_mass, + fragment_mass[i], + max_multiplicity, + take_from_j, + new_mult_k + ); + auto gamma_deficit = gamma[i] - gamma_j_k; + + real_type nj[1] = {}; // float + real_type nk[1] = {}; // float + + Commons::get_new_multiplicities_and_update_attributes( + j, k, attributes, multiplicity, take_from_j[0], new_mult_k[0], n_attr, nj, nk + ); + + atomicAdd( + (unsigned long long int*)&breakup_rate[cid], + (unsigned long long int)(gamma_j_k * multiplicity[k]) + ); + atomicAdd( + (unsigned long long int*)&breakup_rate_deficit[cid], + (unsigned long long int)(gamma_deficit * multiplicity[k]) + ); + Commons::round_multiplicities_to_ints_and_update_attributes( + j, k, nj[0], nk[0], attributes, multiplicity, take_from_j[0], n_attr + ); + } +}; +""" + + +class CollisionsMethods( + ThrustRTCBackendMethods +): # pylint: disable=too-many-instance-attributes + @cached_property + def __scale_prob_for_adaptive_sdm_gamma_body_1(self): + return trtc.For( + param_names=("dt_todo", "dt_left", "dt_max"), + name_iter="cid", + body=""" + dt_todo[cid] = min(dt_left[cid], dt_max); + """, + ) + + @cached_property + def __scale_prob_for_adaptive_sdm_gamma_body_2(self): + return trtc.For( + param_names=( + "prob", + "idx", + "multiplicity", + "cell_id", + "dt", + "is_first_in_pair", + "dt_todo", + ), + name_iter="i", + body=f""" + {COMMONS} + int64_t _j[1] = {{}}; + int64_t _k[1] = {{}}; + auto skip_pair = Commons::pair_indices(i, _j, _k, idx, is_first_in_pair, prob); + auto j = _j[0]; + auto k = _k[0]; + if (skip_pair) {{ + return; + }} + auto prop = (int64_t)(multiplicity[j] / multiplicity[k]); + auto dt_optimal = dt * prop / prob[i]; + auto cid = cell_id[j]; + static_assert(sizeof(dt_todo[0]) == sizeof(unsigned int), ""); + atomicMin((unsigned int*)&dt_todo[cid], __float_as_uint(dt_optimal)); + """.replace( + "real_type", self._get_c_type() + ), + ) + + @cached_property + def __scale_prob_for_adaptive_sdm_gamma_body_3(self): + return trtc.For( + param_names=("prob", "idx", "cell_id", "dt", "is_first_in_pair", "dt_todo"), + name_iter="i", + body=f""" + {COMMONS} + int64_t _j[1] = {{}}; + int64_t _k[1] = {{}}; + auto skip_pair = Commons::pair_indices(i, _j, _k, idx, is_first_in_pair, prob); + auto j = _j[0]; + auto k = _k[0]; + if (skip_pair) {{ + return; + }} + prob[i] *= dt_todo[cell_id[j]] / dt; + """.replace( + "real_type", self._get_c_type() + ), + ) + + @cached_property + def __scale_prob_for_adaptive_sdm_gamma_body_4(self): + return trtc.For( + param_names=("dt_left", "dt_todo", "stats_n_substep"), + name_iter="cid", + body=""" + dt_left[cid] -= dt_todo[cid]; + if (dt_todo[cid] > 0) { + stats_n_substep[cid] += 1; + } + """, + ) + + @cached_property + def __sort_by_cell_id_and_update_cell_start_body(self): + return trtc.For( + param_names=("cell_id", "cell_start", "idx"), + name_iter="i", + body=""" + if (i == 0) { + cell_start[cell_id[idx[0]]] = 0; + } else { + auto cell_id_curr = cell_id[idx[i]]; + auto cell_id_next = cell_id[idx[i + 1]]; + auto diff = (cell_id_next - cell_id_curr); + for (auto j = 1; j < diff + 1; j += 1) { + cell_start[cell_id_curr + j] = idx[i + 1]; + } + } + """, + ) + + @cached_property + def __collision_coalescence_body(self): + return trtc.For( + param_names=( + "multiplicity", + "idx", + "n_sd", + "attributes", + "n_attr", + "gamma", + "healthy", + "cell_id", + "coalescence_rate", + "is_first_in_pair", + ), + name_iter="i", + body=f""" + {COMMONS} + int64_t _j[1] = {{}}; + int64_t _k[1] = {{}}; + auto skip_pair = Commons::pair_indices(i, _j, _k, idx, is_first_in_pair, gamma); + auto j = _j[0]; + auto k = _k[0]; + if (skip_pair) {{ + return; + }} + + Commons::coalesce(i, j, k, cell_id, multiplicity, gamma, attributes, coalescence_rate, n_attr, n_sd); + + Commons::flag_zero_multiplicity(j, k, multiplicity, healthy); + """.replace( + "real_type", self._get_c_type() + ), + ) + + @cached_property + def __collision_coalescence_breakup_body(self): + return trtc.For( + param_names=( + "multiplicity", + "idx", + "attributes", + "gamma", + "rand", + "Ec", + "Eb", + "fragment_mass", + "healthy", + "cell_id", + "coalescence_rate", + "breakup_rate", + "breakup_rate_deficit", + "is_first_in_pair", + "max_multiplicity", + "particle_mass", + "n_sd", + "n_attr", + ), + name_iter="i", + body=f""" + {COMMONS} + int64_t _j[1] = {{}}; + int64_t _k[1] = {{}}; + auto skip_pair = Commons::pair_indices(i, _j, _k, idx, is_first_in_pair, gamma); + auto j = _j[0]; + auto k = _k[0]; + if (skip_pair) {{ + return; + }} + auto bouncing = rand[i] - (Ec[i] + (1 - Ec[i]) * (Eb[i])) > 0; + if (bouncing) {{ + return; + }} + + if (rand[i] - Ec[i] < 0) {{ + Commons::coalesce( + i, + j, + k, + cell_id, + multiplicity, + gamma, + attributes, + coalescence_rate, + n_attr, + n_sd + ); + }} else {{ + Commons::break_up( + i, j, k, + cell_id[j], + multiplicity, + gamma, + attributes, + fragment_mass, + max_multiplicity, + breakup_rate, + breakup_rate_deficit, + particle_mass, + n_sd, + n_attr + ); + }} + + Commons::flag_zero_multiplicity(j, k, multiplicity, healthy); + """.replace( + "real_type", self._get_c_type() + ), + ) + + @cached_property + def __compute_gamma_body(self): + return trtc.For( + param_names=( + "prob", + "rand", + "idx", + "multiplicity", + "cell_id", + "collision_rate_deficit", + "collision_rate", + "is_first_in_pair", + "out", + ), + name_iter="i", + body=f""" + {COMMONS} + out[i] = ceil(prob[i] - rand[i]); + + int64_t _j[1] = {{}}; + int64_t _k[1] = {{}}; + auto skip_pair = Commons::pair_indices(i, _j, _k, idx, is_first_in_pair, out); + auto j = _j[0]; + auto k = _k[0]; + if (skip_pair) {{ + return; + }} + + auto prop = (int64_t)(multiplicity[j] / multiplicity[k]); + auto g = min((int64_t)(out[i]), prop); + + atomicAdd( + (unsigned long long int*)&collision_rate[cell_id[j]], + (unsigned long long int)(g * multiplicity[k]) + ); + atomicAdd( + (unsigned long long int*)&collision_rate_deficit[cell_id[j]], + (unsigned long long int)(((int64_t)(out[i]) - g) * multiplicity[k]) + ); + + out[i] = g; + """.replace( + "real_type", self._get_c_type() + ), + ) + + @cached_property + def __normalize_body_0(self): + return trtc.For( + param_names=("cell_start", "norm_factor", "dt_div_dv"), + name_iter="i", + body=""" + auto sd_num = cell_start[i + 1] - cell_start[i]; + if (sd_num < 2) { + norm_factor[i] = 0; + } + else { + auto half_sd_num = sd_num / 2; + norm_factor[i] = dt_div_dv * sd_num * (sd_num - 1) / 2 / half_sd_num; + } + """, + ) + + @cached_property + def __normalize_body_1(self): + return trtc.For( + param_names=("prob", "cell_id", "norm_factor"), + name_iter="i", + body=""" + prob[i] *= norm_factor[cell_id[i]]; + """, + ) + + @cached_property + def __remove_zero_n_or_flagged_body(self): + return trtc.For( + param_names=("data", "idx", "n_sd"), + name_iter="i", + body=""" + if (idx[i] < n_sd && data[idx[i]] == 0) { + idx[i] = n_sd; + } + """, + ) + + @cached_property + def __cell_id_body(self): + return trtc.For( + param_names=("cell_id", "cell_origin", "strides", "n_dims", "size"), + name_iter="i", + body=""" + cell_id[i] = 0; + for (auto j = 0; j < n_dims; j += 1) { + cell_id[i] += cell_origin[size * j + i] * strides[j]; + } + """, + ) + + @cached_property + def __exp_fragmentation_body(self): + return trtc.For( + param_names=("scale", "frag_volume", "rand", "tol"), + name_iter="i", + body=""" + frag_volume[i] = -scale * log(max(1 - rand[i], tol)); + """, + ) + + @cached_property + def __fragmentation_limiters_body(self): + return trtc.For( + param_names=( + "n_fragment", + "frag_volume", + "x_plus_y", + "vmin", + "nfmax", + "nfmax_is_not_none", + ), + name_iter="i", + body=""" + frag_volume[i] = min(frag_volume[i], x_plus_y[i]); + + if (nfmax_is_not_none) { + if (x_plus_y[i] / frag_volume[i] > nfmax) { + frag_volume[i] = x_plus_y[i] / nfmax; + } + } + else if (frag_volume[i] < vmin) { + frag_volume[i] = x_plus_y[i]; + } + else if (frag_volume[i] == 0.0) { + frag_volume[i] = x_plus_y[i]; + } + n_fragment[i] = x_plus_y[i] / frag_volume[i]; + """, + ) + + @cached_property + def __gauss_fragmentation_body(self): + return trtc.For( + param_names=("mu", "sigma", "frag_volume", "rand"), + name_iter="i", + body=f""" + frag_volume[i] = mu + sigma * {self.formulae.trivia.erfinv_approx.c_inline( + c="rand[i]" + )}; + """.replace( + "real_type", self._get_c_type() + ), + ) + + @cached_property + def __slams_fragmentation_body(self): + return trtc.For( + param_names=("n_fragment", "frag_volume", "x_plus_y", "probs", "rand"), + name_iter="i", + body=""" + probs[i] = 0.0; + n_fragment[i] = 1; + + for (auto n = 0; n < 22; n += 1) { + probs[i] += 0.91 * pow((n + 2), (-1.56)); + if (rand[i] < probs[i]) { + n_fragment[i] = n + 2; + break; + } + } + frag_volume[i] = x_plus_y[i] / n_fragment[i]; + """, + ) + + @cached_property + def __feingold1988_fragmentation_body(self): + return trtc.For( + param_names=("scale", "frag_volume", "x_plus_y", "rand", "fragtol"), + name_iter="i", + body=f""" + frag_volume[i] = {self.formulae.fragmentation_function.frag_volume.c_inline( + scale="scale", + rand="rand[i]", + x_plus_y="x_plus_y[i]", + fragtol="fragtol" + )}; + """.replace( + "real_type", self._get_c_type() + ), + ) + + @cached_property + def __straub_Nr_body(self): + return """ + if (gam[i] * CW[i] >= 7.0) { + Nr1[i] = 0.088 * (gam[i] * CW[i] - 7.0); + } + if (CW[i] >= 21.0) { + Nr2[i] = 0.22 * (CW[i] - 21.0); + if (CW[i] <= 46.0) { + Nr3[i] = 0.04 * (46.0 - CW[i]); + } + } + else { + Nr3[i] = 1.0; + } + Nr4[i] = 1.0; + Nrt[i] = Nr1[i] + Nr2[i] + Nr3[i] + Nr4[i]; + """ + + @cached_property + def __straub_mass_remainder(self): + const = self.formulae.constants + return f""" + Nr1[i] = Nr1[i] * exp(3 * mu1 + 9 * pow(sigma1, 2) / 2); + Nr2[i] = Nr2[i] * (pow(mu2, 3) + 3 * mu2 * pow(sigma2, 2)); + Nr3[i] = Nr3[i] * (pow(mu3, 3) + 3 * mu3 * pow(sigma3, 2)); + Nr4[i] = v_max[i] * 6 / {const.PI} + pow(ds[i], 3) - Nr1[i] - Nr2[i] - Nr3[i]; + if (Nr4[i] <= 0.0) {{ + d34[i] = 0; + Nr4[i] = 0; + }} + else {{ + d34[i] = exp(log(Nr4[i]) / 3); + }} + """ + + @cached_property + def __straub_fragmentation_body(self): + const = self.formulae.constants + return trtc.For( + param_names=( + "CW", + "gam", + "ds", + "frag_volume", + "v_max", + "rand", + "Nr1", + "Nr2", + "Nr3", + "Nr4", + "Nrt", + "d34", + ), + name_iter="i", + body=f""" + {self.__straub_Nr_body} + auto sigma1 = {self.formulae.fragmentation_function.params_sigma1.c_inline(CW="CW[i]")}; + auto mu1 = {self.formulae.fragmentation_function.params_mu1.c_inline(sigma1="sigma1")}; + auto sigma2 = {self.formulae.fragmentation_function.params_sigma2.c_inline(CW="CW[i]")}; + auto mu2 = {self.formulae.fragmentation_function.params_mu2.c_inline(ds="ds[i]")}; + auto sigma3 = {self.formulae.fragmentation_function.params_sigma3.c_inline(CW="CW[i]")}; + auto mu3 = {self.formulae.fragmentation_function.params_mu3.c_inline(ds="ds[i]")}; + {self.__straub_mass_remainder} + Nrt[i] = Nr1[i] + Nr2[i] + Nr3[i] + Nr4[i]; + + if (rand[i] < Nr1[i] / Nrt[i]) {{ + auto X = rand[i] * Nrt[i] / Nr1[i]; + auto lnarg = mu1 + sqrt(2.0) * sigma1 * {self.formulae.trivia.erfinv_approx.c_inline( + c="X" + )}; + frag_volume[i] = exp(lnarg); + }} + else if (rand[i] < (Nr2[i] + Nr1[i]) / Nrt[i]) {{ + auto X = (rand[i] * Nrt[i] - Nr1[i]) / Nr2[i]; + frag_volume[i] = mu2 + sqrt(2.0) * sigma2 * {self.formulae.trivia.erfinv_approx.c_inline( + c="X" + )}; + }} + else if (rand[i] < (Nr3[i] + Nr2[i] + Nr1[i]) / Nrt[i]) {{ + auto X = (rand[i] * Nrt[i] - Nr1[i] - Nr2[i]) / Nr3[i]; + frag_volume[i] = mu3 + sqrt(2.0) * sigma3 * {self.formulae.trivia.erfinv_approx.c_inline( + c="X" + )}; + }} + else {{ + frag_volume[i] = d34[i]; + }} + + frag_volume[i] = pow(frag_volume[i], 3) * {const.PI} / 6; + """.replace( + "real_type", self._get_c_type() + ), + ) + + @nice_thrust(**NICE_THRUST_FLAGS) + def adaptive_sdm_end(self, dt_left, cell_start): + i = trtc.Find(dt_left.data, self._get_floating_point(0)) + if i is None: + i = len(dt_left) + return cell_start[i] + + # pylint: disable=unused-argument + @nice_thrust(**NICE_THRUST_FLAGS) + def scale_prob_for_adaptive_sdm_gamma( + self, + *, + prob, + multiplicity, + cell_id, + dt_left, + dt, + dt_range, + is_first_in_pair, + stats_n_substep, + stats_dt_min, + ): + # TODO #406 implement stats_dt_min + dt_todo = trtc.device_vector("float", len(dt_left)) + d_dt_max = self._get_floating_point(dt_range[1]) + d_dt = self._get_floating_point(dt) + + self.__scale_prob_for_adaptive_sdm_gamma_body_1.launch_n( + n=len(dt_left), args=(dt_todo, dt_left.data, d_dt_max) + ) + self.__scale_prob_for_adaptive_sdm_gamma_body_2.launch_n( + n=len(multiplicity) // 2, + args=( + prob.data, + multiplicity.idx.data, + multiplicity.data, + cell_id.data, + d_dt, + is_first_in_pair.indicator.data, + dt_todo, + ), + ) + self.__scale_prob_for_adaptive_sdm_gamma_body_3.launch_n( + n=len(multiplicity) // 2, + args=( + prob.data, + multiplicity.idx.data, + cell_id.data, + d_dt, + is_first_in_pair.indicator.data, + dt_todo, + ), + ) + self.__scale_prob_for_adaptive_sdm_gamma_body_4.launch_n( + n=len(dt_left), args=(dt_left.data, dt_todo, stats_n_substep.data) + ) + + @nice_thrust(**NICE_THRUST_FLAGS) + def cell_id(self, cell_id, cell_origin, strides): + if len(cell_id) == 0: + return + + assert cell_origin.shape[0] == strides.shape[1] + assert cell_id.shape[0] == cell_origin.shape[1] + assert strides.shape[0] == 1 + n_dims = trtc.DVInt64(cell_origin.shape[0]) + size = trtc.DVInt64(cell_origin.shape[1]) + self.__cell_id_body.launch_n( + n=len(cell_id), + args=(cell_id.data, cell_origin.data, strides.data, n_dims, size), + ) + + # pylint: disable=unused-argument + @nice_thrust(**NICE_THRUST_FLAGS) + def collision_coalescence( + self, + *, + multiplicity, + idx, + attributes, + gamma, + healthy, + cell_id, + coalescence_rate, + is_first_in_pair, + ): + if len(idx) < 2: + return + n_sd = trtc.DVInt64(attributes.shape[1]) + n_attr = trtc.DVInt64(attributes.shape[0]) + self.__collision_coalescence_body.launch_n( + n=len(idx) // 2, + args=( + multiplicity.data, + idx.data, + n_sd, + attributes.data, + n_attr, + gamma.data, + healthy.data, + cell_id.data, + coalescence_rate.data, + is_first_in_pair.indicator.data, + ), + ) + + @nice_thrust(**NICE_THRUST_FLAGS) + def collision_coalescence_breakup( # pylint: disable=unused-argument,too-many-locals + self, + *, + multiplicity, + idx, + attributes, + gamma, + rand, + Ec, + Eb, + fragment_mass, + healthy, + cell_id, + coalescence_rate, + breakup_rate, + breakup_rate_deficit, + is_first_in_pair, + warn_overflows, + particle_mass, + max_multiplicity, + ): + if warn_overflows: + raise NotImplementedError() + if len(idx) < 2: + return + n_sd = trtc.DVInt64(attributes.shape[1]) + n_attr = trtc.DVInt64(attributes.shape[0]) + self.__collision_coalescence_breakup_body.launch_n( + n=len(idx) // 2, + args=( + multiplicity.data, + idx.data, + attributes.data, + gamma.data, + rand.data, + Ec.data, + Eb.data, + fragment_mass.data, + healthy.data, + cell_id.data, + coalescence_rate.data, + breakup_rate.data, + breakup_rate_deficit.data, + is_first_in_pair.indicator.data, + trtc.DVInt64(max_multiplicity), + particle_mass.data, + n_sd, + n_attr, + ), + ) + + @nice_thrust(**NICE_THRUST_FLAGS) + def compute_gamma( + self, + *, + prob, + rand, + multiplicity, + cell_id, + collision_rate_deficit, + collision_rate, + is_first_in_pair, + out, + ): + if len(multiplicity) < 2: + return + self.__compute_gamma_body.launch_n( + n=len(multiplicity) // 2, + args=( + prob.data, + rand.data, + multiplicity.idx.data, + multiplicity.data, + cell_id.data, + collision_rate_deficit.data, + collision_rate.data, + is_first_in_pair.indicator.data, + out.data, + ), + ) + + # pylint: disable=unused-argument + def make_cell_caretaker(self, idx_shape, idx_dtype, cell_start_len, scheme=None): + if not (scheme is None or scheme == "default"): + raise NotImplementedError() + return self._sort_by_cell_id_and_update_cell_start + + # pylint: disable=unused-argument + @nice_thrust(**NICE_THRUST_FLAGS) + def normalize( + self, *, prob, cell_id, cell_idx, cell_start, norm_factor, timestep, dv + ): + n_cell = cell_start.shape[0] - 1 + device_dt_div_dv = self._get_floating_point(timestep / dv) + self.__normalize_body_0.launch_n( + n=n_cell, args=(cell_start.data, norm_factor.data, device_dt_div_dv) + ) + self.__normalize_body_1.launch_n( + prob.shape[0], (prob.data, cell_id.data, norm_factor.data) + ) + + @nice_thrust(**NICE_THRUST_FLAGS) + def remove_zero_n_or_flagged(self, data, idx, length) -> int: + n_sd = trtc.DVInt64(idx.size()) + + # Warning: (potential bug source): reading from outside of array + self.__remove_zero_n_or_flagged_body.launch_n(n=length, args=(data, idx, n_sd)) + + trtc.Sort(idx) + + new_length = idx.size() - trtc.Count(idx, n_sd) + return new_length + + # pylint: disable=unused-argument + @nice_thrust(**NICE_THRUST_FLAGS) + def _sort_by_cell_id_and_update_cell_start( + self, cell_id, cell_idx, cell_start, idx + ): + # TODO #330 + # was here before (but did not work): + # trtc.Sort_By_Key(cell_id.data, idx.data) + # was here before (but cause huge slowdown of otherwise correct code) + # max_cell_id = max(cell_id.to_ndarray()) + # assert max_cell_id == 0 + # no handling of cell_idx in __sort_by_cell_id_and_update_cell_start_body yet + trtc.Fill(cell_start.data, trtc.DVInt64(len(idx))) + if len(idx) > 1: + self.__sort_by_cell_id_and_update_cell_start_body.launch_n( + n=len(idx) - 1, args=(cell_id.data, cell_start.data, idx.data) + ) + + def exp_fragmentation( + self, + *, + n_fragment, + scale, + frag_volume, + x_plus_y, + rand, + vmin, + nfmax, + tol=1e-5, + ): + self.__exp_fragmentation_body.launch_n( + n=len(frag_volume), + args=( + self._get_floating_point(scale), + frag_volume.data, + rand.data, + self._get_floating_point(tol), + ), + ) + + self.__fragmentation_limiters_body.launch_n( + n=len(frag_volume), + args=( + n_fragment.data, + frag_volume.data, + x_plus_y.data, + self._get_floating_point(vmin), + self._get_floating_point(nfmax if nfmax else -1), + trtc.DVBool(nfmax is not None), + ), + ) + + def gauss_fragmentation( + self, *, n_fragment, mu, sigma, frag_volume, x_plus_y, rand, vmin, nfmax + ): + self.__gauss_fragmentation_body.launch_n( + n=len(frag_volume), + args=( + self._get_floating_point(mu), + self._get_floating_point(sigma), + frag_volume.data, + rand.data, + ), + ) + + self.__fragmentation_limiters_body.launch_n( + n=len(frag_volume), + args=( + n_fragment.data, + frag_volume.data, + x_plus_y.data, + self._get_floating_point(vmin), + self._get_floating_point(nfmax if nfmax else -1), + trtc.DVBool(nfmax is not None), + ), + ) + + def slams_fragmentation( + self, n_fragment, frag_volume, x_plus_y, probs, rand, vmin, nfmax + ): # pylint: disable=too-many-arguments + self.__slams_fragmentation_body.launch_n( + n=(len(n_fragment)), + args=( + n_fragment.data, + frag_volume.data, + x_plus_y.data, + probs.data, + rand.data, + ), + ) + + self.__fragmentation_limiters_body.launch_n( + n=len(frag_volume), + args=( + n_fragment.data, + frag_volume.data, + x_plus_y.data, + self._get_floating_point(vmin), + self._get_floating_point(nfmax if nfmax else -1), + trtc.DVBool(nfmax is not None), + ), + ) + + def feingold1988_fragmentation( + self, + *, + n_fragment, + scale, + frag_volume, + x_plus_y, + rand, + fragtol, + vmin, + nfmax, + ): + self.__feingold1988_fragmentation_body.launch_n( + n=len(frag_volume), + args=( + self._get_floating_point(scale), + frag_volume.data, + x_plus_y.data, + rand.data, + self._get_floating_point(fragtol), + ), + ) + + self.__fragmentation_limiters_body.launch_n( + n=len(frag_volume), + args=( + n_fragment.data, + frag_volume.data, + x_plus_y.data, + self._get_floating_point(vmin), + self._get_floating_point(nfmax if nfmax else -1), + trtc.DVBool(nfmax is not None), + ), + ) + + def straub_fragmentation( + # pylint: disable=too-many-arguments,too-many-locals + self, + *, + n_fragment, + CW, + gam, + ds, + frag_volume, + v_max, + x_plus_y, + rand, + vmin, + nfmax, + Nr1, + Nr2, + Nr3, + Nr4, + Nrt, + d34, + ): + self.__straub_fragmentation_body.launch_n( + n=len(frag_volume), + args=( + CW.data, + gam.data, + ds.data, + frag_volume.data, + v_max.data, + rand.data, + Nr1.data, + Nr2.data, + Nr3.data, + Nr4.data, + Nrt.data, + d34.data, + ), + ) + + self.__fragmentation_limiters_body.launch_n( + n=len(frag_volume), + args=( + n_fragment.data, + frag_volume.data, + x_plus_y.data, + self._get_floating_point(vmin), + self._get_floating_point(nfmax if nfmax else -1), + trtc.DVBool(nfmax is not None), + ), + ) diff --git a/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/condensation_methods.py b/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/condensation_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..7b680fad987fa50ea0545984b7cfde3499ebd20b --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/condensation_methods.py @@ -0,0 +1,540 @@ +""" +GPU implementation of backend methods for water condensation/evaporation +""" + +from functools import cached_property +from typing import Dict, Optional + +from PySDM.backends.impl_common.storage_utils import StorageBase +from PySDM.backends.impl_thrust_rtc.bisection import BISECTION +from PySDM.backends.impl_thrust_rtc.conf import NICE_THRUST_FLAGS +from PySDM.backends.impl_thrust_rtc.nice_thrust import nice_thrust + +from ..conf import trtc +from ..methods.thrust_rtc_backend_methods import ThrustRTCBackendMethods + +MINFUN_ARGS_VARS = ("x_old", "dt", "kappa", "f_org", "rd3", "_T", "_RH", "Fk", "Fd") + + +def arg(name): + return f"minfun_args[{MINFUN_ARGS_VARS.index(name)}]" + + +class CondensationMethods( + ThrustRTCBackendMethods +): # pylint: disable=too-many-instance-attributes + keys = ( + "T", + "p", + "pv", + "lv", + "pvs", + "RH", + "DTp", + "KTp", + "lambdaK", + "lambdaD", + "schmidt_number", + ) + + @cached_property + def __calculate_m_l(self): + return trtc.For( + param_names=("ml", "signed_water_mass", "multiplicity", "cell_id"), + name_iter="i", + body=""" + atomicAdd((real_type*) &ml[cell_id[i]], multiplicity[i] * signed_water_mass[i]); + """.replace( + "real_type", self._get_c_type() + ), + ) + + def __init__(self): + ThrustRTCBackendMethods.__init__(self) + self.RH_rtol = None + self.adaptive = None + self.max_iters = None + self.ml_old: Optional[StorageBase] = None + self.ml_new: Optional[StorageBase] = None + self.T: Optional[StorageBase] = None + self.dthd_dt_pred: Optional[StorageBase] = None + self.d_water_vapour_mixing_ratio__dt_predicted: Optional[StorageBase] = None + self.drhod_dt_pred: Optional[StorageBase] = None + self.rhod_copy: Optional[StorageBase] = None + self.m_d: Optional[StorageBase] = None + self.vars: Optional[Dict[str, StorageBase]] = None + self.vars_data: Optional[Dict] = None + + @cached_property + def __update_drop_masses(self): + phys = self.formulae + const = phys.constants + return trtc.For( + param_names=( + "signed_water_mass", + "vdry", + *CondensationMethods.keys, + "_kappa", + "_f_org", + "dt", + "RH_rtol", + "rtol_x", + "max_iters", + "cell_id", + "reynolds_number", + ), + name_iter="i", + body=f""" + struct Minfun {{ + static __device__ real_type value(real_type x_new, void* minfun_args_p) {{ + auto minfun_args = static_cast(minfun_args_p); + if (x_new > {phys.diffusion_coordinate.x_max.c_inline()}) {{ + return {arg("x_old")} - x_new; + }} + auto m_new = {phys.diffusion_coordinate.mass.c_inline(x="x_new")}; + auto v_new = {phys.particle_shape_and_density.mass_to_volume.c_inline(mass="m_new")}; + auto r_new = {phys.trivia.radius.c_inline(volume="v_new")}; + auto sgm = {phys.surface_tension.sigma.c_inline( + T=arg('_T'), + v_wet="v_new", + v_dry=f"const.PI_4_3 * {arg('rd3')}", + f_org=arg("f_org") + )}; + auto RH_eq = {phys.hygroscopicity.RH_eq.c_inline( + r="r_new", + T=arg('_T'), + kp=arg("kappa"), + rd3=arg("rd3"), + sgm="sgm" + )}; + auto r_dr_dt = {phys.drop_growth.r_dr_dt.c_inline( + RH_eq="RH_eq", + RH=arg("_RH"), + Fk=arg("Fk"), + Fd=arg("Fd"), + )}; + auto dm_dt = {phys.particle_shape_and_density.dm_dt.c_inline( + r="r_new", r_dr_dt="r_dr_dt" + )}; + return {arg("x_old")} - x_new + {arg("dt")} * { + phys.diffusion_coordinate.dx_dt.c_inline(m="m_new", dm_dt="dm_dt") + }; + }} + }}; + {BISECTION} + + auto _T = T[cell_id[i]]; + auto _pv = pv[cell_id[i]]; + auto _lv = lv[cell_id[i]]; + auto _pvs = pvs[cell_id[i]]; + auto _RH = RH[cell_id[i]]; + auto _DTp = DTp[cell_id[i]]; + auto _KTp = KTp[cell_id[i]]; + auto _lambdaK = lambdaK[cell_id[i]]; + auto _lambdaD = lambdaD[cell_id[i]]; + auto _schmidt_number = schmidt_number[cell_id[i]]; + + auto v_old = {phys.particle_shape_and_density.mass_to_volume.c_inline( + mass="signed_water_mass[i]" + )}; + auto x_old = {phys.diffusion_coordinate.x.c_inline(mass="signed_water_mass[i]")}; + auto r_old = {phys.trivia.radius.c_inline(volume="v_old")}; + auto m_insane = {phys.particle_shape_and_density.volume_to_mass.c_inline(volume="vdry[i] / 100")}; + auto x_insane = {phys.diffusion_coordinate.x.c_inline(mass="m_insane")}; + auto rd3 = vdry[i] / {const.PI_4_3}; + auto sgm = {phys.surface_tension.sigma.c_inline( + T="_T", v_wet="v", v_dry="vdry[i]", f_org="_f_org[i]" + )}; + auto RH_eq = {phys.hygroscopicity.RH_eq.c_inline( + r="r_old", T="_T", kp="_kappa[i]", rd3="rd3", sgm="sgm" + )}; + + real_type Fk=0; + real_type Fd=0; + real_type r_dr_dt_old=0; + real_type dm_dt_old=0; + real_type dx_old=0; + + real_type x_new = 0; + if ( ! {phys.trivia.within_tolerance.c_inline( + return_type='bool', error_estimate="abs(_RH - RH_eq)", value="_RH", rtol="RH_rtol" + )}) {{ + auto Dr = {phys.diffusion_kinetics.D.c_inline(D="_DTp", r="r_old", lmbd="_lambdaD")}; + auto Kr = {phys.diffusion_kinetics.K.c_inline(K="_KTp", r="r_old", lmbd="_lambdaK")}; + auto qrt_re_times_cbrt_sc={phys.trivia.sqrt_re_times_cbrt_sc.c_inline( + Re="reynolds_number[i]", + Sc="_schmidt_number", + )}; + auto mass_ventilation_factor = {phys.ventilation.ventilation_coefficient.c_inline( + sqrt_re_times_cbrt_sc="qrt_re_times_cbrt_sc" + )}; + auto heat_ventilation_factor = mass_ventilation_factor; // TODO #1588 + Fk = {phys.drop_growth.Fk.c_inline( + T="_T", + K="heat_ventilation_factor*Kr", + lv="_lv", + )}; // TODO #1588 + Fd = {phys.drop_growth.Fd.c_inline( + T="_T", + D="mass_ventilation_factor*Dr", + pvs="_pvs", + )}; + r_dr_dt_old = {phys.drop_growth.r_dr_dt.c_inline( + RH_eq="RH_eq", + RH="_RH", + Fk="Fk", + Fd="Fd", + )}; + dm_dt_old = {phys.particle_shape_and_density.dm_dt.c_inline(r="r_old", r_dr_dt="r_dr_dt_old")}; + dx_old = dt * {phys.diffusion_coordinate.dx_dt.c_inline( + m="signed_water_mass[i]", dm_dt="dm_dt_old" + )}; + }} + else {{ + dx_old = 0; + }} + real_type kappa = _kappa[i]; + real_type f_org = _f_org[i]; + real_type minfun_args[] = {{{','.join(MINFUN_ARGS_VARS)}}}; // array + + if (dx_old == 0) {{ + x_new = x_old; + }} + else {{ + auto a = x_old; + auto b = max(x_insane, a + dx_old); + auto fa = Minfun::value(a, &minfun_args); + auto fb = Minfun::value(b, &minfun_args); + + auto counter = 0; + while ( ! fa * fb < 0) {{ + counter += 1; + if (counter > max_iters) {{ + printf("failed to find interval"); + //success = False + return; + }} + b = max(x_insane, a + ldexp(dx_old, 1.*counter)); + fb = Minfun::value(b, &minfun_args); + }} + + //if not success: + // x_new = np.nan + // break + if (a != b) {{ + if (a > b) {{ + auto tmp = a; + a = b; + b = tmp; + auto ftmp = fa; + fa = fb; + fb = ftmp; + }} + x_new = Bisect::bisect(Minfun::value, &minfun_args, a, b, fa, fb, rtol_x); + //if iters_taken in (-1, max_iters): + // if not fake: + // print("TOMS failed") + // success = False + // break + }} + else {{ + x_new = x_old; + }} + }} + signed_water_mass[i] = {phys.diffusion_coordinate.mass.c_inline(x="x_new")}; + """.replace( + "real_type", self._get_c_type() + ), + ) + + @cached_property + def __pre_for(self): + return trtc.For( + param_names=( + "dthd_dt_pred", + "d_water_vapour_mixing_ratio__dt_predicted", + "drhod_dt_pred", + "m_d", + "pthd", + "thd", + "predicted_water_vapour_mixing_ratio", + "water_vapour_mixing_ratio", + "prhod", + "rhod", + "dt", + "RH_max", + "dv", + ), + name_iter="i", + body=""" + dthd_dt_pred[i] = (pthd[i] - thd[i]) / dt; + d_water_vapour_mixing_ratio__dt_predicted[i] = ( + predicted_water_vapour_mixing_ratio[i] - water_vapour_mixing_ratio[i] + ) / dt; + drhod_dt_pred[i] = (prhod[i] - rhod[i]) / dt; + + m_d[i] = (prhod[i] + rhod[i]) / 2 * dv; + + pthd[i] = thd[i]; + predicted_water_vapour_mixing_ratio[i] = water_vapour_mixing_ratio[i]; + + RH_max[i] = 0; + """, + ) + + @cached_property + def __pre(self): + phys = self.formulae + return trtc.For( + param_names=( + *CondensationMethods.keys, + "dthd_dt_pred", + "d_water_vapour_mixing_ratio__dt_predicted", + "drhod_dt_pred", + "pthd", + "predicted_water_vapour_mixing_ratio", + "rhod_copy", + "dt", + "RH_max", + "air_density", + "air_dynamic_viscosity", + ), + name_iter="i", + body=f""" + pthd[i] += dt * dthd_dt_pred[i] / 2; + predicted_water_vapour_mixing_ratio[i] += ( + dt * d_water_vapour_mixing_ratio__dt_predicted[i] / 2 + ); + rhod_copy[i] += dt * drhod_dt_pred[i] / 2; + + T[i] = {phys.state_variable_triplet.T.c_inline( + rhod='rhod_copy[i]', thd='pthd[i]')}; + p[i] = {phys.state_variable_triplet.p.c_inline( + rhod='rhod_copy[i]', T='T[i]', + water_vapour_mixing_ratio='predicted_water_vapour_mixing_ratio[i]' + )}; + pv[i] = {phys.state_variable_triplet.pv.c_inline( + p='p[i]', water_vapour_mixing_ratio='predicted_water_vapour_mixing_ratio[i]')}; + lv[i] = {phys.latent_heat_vapourisation.lv.c_inline( + T='T[i]')}; + pvs[i] = {phys.saturation_vapour_pressure.pvs_water.c_inline( + T='T[i]')}; + RH[i] = pv[i] / pvs[i]; + RH_max[i] = max(RH_max[i], RH[i]); + DTp[i] = {phys.diffusion_thermics.D.c_inline( + T='T[i]', p='p[i]')}; + KTp[i] = {phys.diffusion_thermics.K.c_inline( + T='T[i]', p='p[i]' + )}; + lambdaK[i] = {phys.diffusion_kinetics.lambdaK.c_inline( + T='T[i]', p='p[i]')}; + lambdaD[i] = {phys.diffusion_kinetics.lambdaD.c_inline( + D='DTp[i]', T='T[i]')}; + schmidt_number[i] = {phys.trivia.air_schmidt_number.c_inline( + dynamic_viscosity="air_dynamic_viscosity[i]", + diffusivity="DTp[i]", + density="air_density[i]", + )}; + """.replace( + "real_type", self._get_c_type() + ), + ) + + @cached_property + def __post(self): + phys = self.formulae + return trtc.For( + param_names=( + "dthd_dt_pred", + "d_water_vapour_mixing_ratio__dt_predicted", + "drhod_dt_pred", + "pthd", + "predicted_water_vapour_mixing_ratio", + "rhod_copy", + "dt", + "ml_new", + "ml_old", + "m_d", + "T", + "lv", + ), + name_iter="i", + body=f""" + auto dml_dt = (ml_new[i] - ml_old[i]) / dt; + auto d_water_vapour_mixing_ratio__dt_corrected = - dml_dt / m_d[i]; + auto dthd_dt_corr = {phys.state_variable_triplet.dthd_dt.c_inline( + rhod='rhod_copy[i]', thd='pthd[i]', T='T[i]', + d_water_vapour_mixing_ratio__dt='d_water_vapour_mixing_ratio__dt_corrected', + lv='lv[i]' + )}; + pthd[i] += dt * (dthd_dt_pred[i] / 2 + dthd_dt_corr); + predicted_water_vapour_mixing_ratio[i] += dt * ( + d_water_vapour_mixing_ratio__dt_predicted[i] / 2 + + d_water_vapour_mixing_ratio__dt_corrected + ); + rhod_copy[i] += dt * drhod_dt_pred[i] / 2; + ml_old[i] = ml_new[i]; + """.replace( + "real_type", self._get_c_type() + ), + ) + + def calculate_m_l(self, ml, signed_water_mass, multiplicity, cell_id): + ml[:] = 0 + self.__calculate_m_l.launch_n( + n=len(multiplicity), + args=(ml.data, signed_water_mass.data, multiplicity.data, cell_id.data), + ) + + # pylint: disable=unused-argument,too-many-locals + @nice_thrust(**NICE_THRUST_FLAGS) + def condensation( + self, + *, + solver, + n_cell, + cell_start_arg, + signed_water_mass, + v_cr, + multiplicity, + vdry, + idx, + rhod, + thd, + water_vapour_mixing_ratio, + dv, + prhod, + pthd, + predicted_water_vapour_mixing_ratio, + kappa, + f_org, + rtol_x, + rtol_thd, + timestep, + counters, + cell_order, + RH_max, + success, + cell_id, + reynolds_number, + air_density, + air_dynamic_viscosity, + ): + assert solver is None + + if self.adaptive: + counters["n_substeps"][:] = 1 # TODO #527 + + n_substeps = counters["n_substeps"][0] + + success[:] = True # TODO #588 + dvfloat = self._get_floating_point + self.rhod_copy.fill(rhod) + + self.__pre_for.launch_n( + n=n_cell, + args=( + self.dthd_dt_pred.data, + self.d_water_vapour_mixing_ratio__dt_predicted.data, + self.drhod_dt_pred.data, + self.m_d.data, + pthd.data, + thd.data, + predicted_water_vapour_mixing_ratio.data, + water_vapour_mixing_ratio.data, + prhod.data, + self.rhod_copy.data, + dvfloat(timestep), + RH_max.data, + dvfloat(dv), + ), + ) + timestep /= n_substeps + self.calculate_m_l(self.ml_old, signed_water_mass, multiplicity, cell_id) + + for _ in range(n_substeps): + self.__pre.launch_n( + n=n_cell, + args=( + *self.vars_data.values(), + self.dthd_dt_pred.data, + self.d_water_vapour_mixing_ratio__dt_predicted.data, + self.drhod_dt_pred.data, + pthd.data, + predicted_water_vapour_mixing_ratio.data, + self.rhod_copy.data, + dvfloat(timestep), + RH_max.data, + air_density.data, + air_dynamic_viscosity.data, + ), + ) + self.__update_drop_masses.launch_n( + n=len(multiplicity), + args=( + signed_water_mass.data, + vdry.data, + *self.vars_data.values(), + kappa.data, + f_org.data, + dvfloat(timestep), + dvfloat(self.RH_rtol), + dvfloat(rtol_x), + dvfloat(self.max_iters), + cell_id.data, + reynolds_number.data, + ), + ) + self.calculate_m_l(self.ml_new, signed_water_mass, multiplicity, cell_id) + self.__post.launch_n( + n=n_cell, + args=( + self.dthd_dt_pred.data, + self.d_water_vapour_mixing_ratio__dt_predicted.data, + self.drhod_dt_pred.data, + pthd.data, + predicted_water_vapour_mixing_ratio.data, + self.rhod_copy.data, + dvfloat(timestep), + self.ml_new.data, + self.ml_old.data, + self.m_d.data, + self.vars["T"].data, + self.vars["lv"].data, + ), + ) + + # pylint disable=unused-argument + def make_condensation_solver( + self, + timestep, + n_cell, + *, + dt_range, + adaptive, + fuse, + multiplier, + RH_rtol, + max_iters, + ): + self.adaptive = adaptive + self.RH_rtol = RH_rtol + self.max_iters = max_iters + for attr in ( + "ml_old", + "ml_new", + "T", + "dthd_dt_pred", + "d_water_vapour_mixing_ratio__dt_predicted", + "drhod_dt_pred", + "m_d", + "rhod_copy", + ): + setattr( + self, attr, self.Storage.empty(shape=n_cell, dtype=self._get_np_dtype()) + ) + self.vars = { + key: self.Storage.empty(shape=n_cell, dtype=self._get_np_dtype()) + for key in CondensationMethods.keys + } + self.vars_data = {key: val.data for key, val in self.vars.items()} diff --git a/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/displacement_methods.py b/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/displacement_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..7c6758abd6d3f0cf96e43482628421d9e3e21a51 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/displacement_methods.py @@ -0,0 +1,158 @@ +""" +GPU implementation of backend methods for particle displacement (advection and sedimentation) +""" + +from functools import cached_property + +from PySDM.backends.impl_thrust_rtc.conf import NICE_THRUST_FLAGS +from PySDM.backends.impl_thrust_rtc.nice_thrust import nice_thrust + +from ..conf import trtc +from ..methods.thrust_rtc_backend_methods import ThrustRTCBackendMethods + + +class DisplacementMethods(ThrustRTCBackendMethods): + @cached_property + def __calculate_displacement_body(self): + return { + n_dims: trtc.For( + param_names=( + "dim", + "n_sd", + "displacement", + "courant", + "courant_shape_0", + "courant_shape_1", + "cell_origin", + "position_in_cell", + "n_substeps", + ), + name_iter="i", + body=f""" + // Arakawa-C grid + auto _l = cell_origin[i]; + auto _r = cell_origin[i] + 1 * (dim == 0); + + if ({n_dims} > 1) {{ + auto _l_1 = cell_origin[i + n_sd]; + auto _r_1 = cell_origin[i + n_sd] + 1 * (dim == 1); + + _l += _l_1 * courant_shape_0; + _r += _r_1 * courant_shape_0; + }} + + if ({n_dims} > 2) {{ + auto _l_2 = cell_origin[i + 2 * n_sd]; + auto _r_2 = cell_origin[i + 2 * n_sd] + 1 * (dim == 2); + + _l += _l_2 * courant_shape_0 * courant_shape_1; + _r += _r_2 * courant_shape_0 * courant_shape_1; + }} + + displacement[i + n_sd * dim] = { + self.formulae.particle_advection.displacement.c_inline( + position_in_cell="position_in_cell[i + n_sd * dim]", + c_l="courant[_l] / n_substeps", + c_r="courant[_r] / n_substeps" + ) + }; + """.replace( + "real_type", self._get_c_type() + ), + ) + for n_dims in (1, 2, 3) + } + + @cached_property + def __flag_precipitated_body(self): + return trtc.For( + ( + "idx", + "n_sd", + "n_dims", + "healthy", + "cell_origin", + "position_in_cell", + "water_mass", + "multiplicity", + "rainfall_mass", + ), + "i", + """ + auto origin = cell_origin[n_sd * (n_dims-1) + idx[i]]; + auto pic = position_in_cell[n_sd * (n_dims-1) + idx[i]]; + if (origin + pic < 0) { + atomicAdd( + (real_type*) &rainfall_mass[0], + multiplicity[idx[i]] * abs(water_mass[idx[i]]) + ); + idx[i] = n_sd; + healthy[0] = 0; + } + """.replace( + "real_type", self._get_c_type() + ), + ) + + @nice_thrust(**NICE_THRUST_FLAGS) + def calculate_displacement( + self, *, dim, displacement, courant, cell_origin, position_in_cell, n_substeps + ): + n_dim = len(courant.shape) + n_sd = position_in_cell.shape[1] + self.__calculate_displacement_body[n_dim].launch_n( + n=n_sd, + args=( + trtc.DVInt64(dim), + trtc.DVInt64(n_sd), + displacement.data, + courant.data, + trtc.DVInt64(courant.shape[0]), + trtc.DVInt64(courant.shape[1] if n_dim > 2 else -1), + cell_origin.data, + position_in_cell.data, + trtc.DVInt64(n_substeps), + ), + ) + + @nice_thrust(**NICE_THRUST_FLAGS) + def flag_precipitated( # pylint: disable=unused-argument + self, + *, + cell_origin, + position_in_cell, + water_mass, + multiplicity, + idx, + length, + healthy, + precipitation_counting_level_index, + displacement, + ): + if precipitation_counting_level_index != 0: + raise NotImplementedError() + n_sd = trtc.DVInt64(cell_origin.shape[1]) + n_dims = trtc.DVInt64(len(cell_origin.shape)) + rainfall_mass = trtc.device_vector(self._get_c_type(), 1) + trtc.Fill(rainfall_mass, self._get_floating_point(0)) + self.__flag_precipitated_body.launch_n( + length, + ( + idx.data, + n_sd, + n_dims, + healthy.data, + cell_origin.data, + position_in_cell.data, + water_mass.data, + multiplicity.data, + rainfall_mass, + ), + ) + return rainfall_mass.to_host()[0] + + @staticmethod + def flag_out_of_column( # pylint: disable=unused-argument + *, cell_origin, position_in_cell, idx, length, healthy, domain_top_level_index + ): + pass diff --git a/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/freezing_methods.py b/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/freezing_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..d06225c1dcd454ae9f7aa45d3be11e9293435124 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/freezing_methods.py @@ -0,0 +1,273 @@ +""" +GPU implementation of backend methods for freezing (singular and time-dependent immersion freezing) +""" + +from functools import cached_property + +from PySDM.backends.impl_thrust_rtc.conf import NICE_THRUST_FLAGS +from PySDM.backends.impl_thrust_rtc.nice_thrust import nice_thrust + +from ..conf import trtc +from ..methods.thrust_rtc_backend_methods import ThrustRTCBackendMethods + + +class FreezingMethods(ThrustRTCBackendMethods): + @cached_property + def immersion_freezing_time_dependent_body(self): + return trtc.For( + param_names=( + "rand", + "immersed_surface_area", + "signed_water_mass", + "timestep", + "cell", + "a_w_ice", + "relative_humidity", + ), + name_iter="i", + body=f""" + if (immersed_surface_area[i] == 0) {{ + return; + }} + if ({self.formulae.trivia.unfrozen_and_saturated.c_inline( + signed_water_mass="signed_water_mass[i]", + relative_humidity="relative_humidity[cell[i]]" + )}) {{ + auto rate_assuming_constant_temperature_within_dt = {self.formulae.heterogeneous_ice_nucleation_rate.j_het.c_inline( + a_w_ice="a_w_ice[cell[i]]" + )} * immersed_surface_area[i]; + auto prob = 1 - {self.formulae.trivia.poissonian_avoidance_function.c_inline( + r="rate_assuming_constant_temperature_within_dt", + dt="timestep" + )}; + if (rand[i] < prob) {{ + signed_water_mass[i] = -1 * signed_water_mass[i]; + }} + }} + """.replace( + "real_type", self._get_c_type() + ), + ) + + @cached_property + def immersion_freezing_singular_body(self): + return trtc.For( + param_names=( + "freezing_temperature", + "signed_water_mass", + "temperature", + "relative_humidity", + "cell", + ), + name_iter="i", + body=f""" + if (freezing_temperature[i] == 0) {{ + return; + }} + if ( + {self.formulae.trivia.unfrozen_and_saturated.c_inline( + signed_water_mass="signed_water_mass[i]", + relative_humidity="relative_humidity[cell[i]]" + )} && temperature[cell[i]] <= freezing_temperature[i] + ) {{ + signed_water_mass[i] = -1 * signed_water_mass[i]; + }} + """.replace( + "real_type", self._get_c_type() + ), + ) + + @cached_property + def thaw_instantaneous_body(self): + return trtc.For( + param_names=( + "signed_water_mass", + "cell", + "temperature", + ), + name_iter="i", + body=f""" + if ({self.formulae.trivia.frozen_and_above_freezing_point.c_inline( + signed_water_mass="signed_water_mass[i]", + temperature="temperature[cell[i]]" + )}) {{ + signed_water_mass[i] = -1 * signed_water_mass[i]; + }} + """.replace( + "real_type", self._get_c_type() + ), + ) + + @cached_property + def homogeneous_freezing_threshold_body(self): + return trtc.For( + param_names=( + "signed_water_mass", + "cell", + "temperature", + "relative_humidity_ice", + ), + name_iter="i", + body=f""" + if ( + {self.formulae.trivia.unfrozen_and_ice_saturated.c_inline( + signed_water_mass="signed_water_mass[i]", + relative_humidity_ice="relative_humidity_ice[cell[i]]" + )} && temperature[cell[i]] <= {self.formulae.constants.HOMOGENEOUS_FREEZING_THRESHOLD} + ) {{ + signed_water_mass[i] = -1 * signed_water_mass[i]; + }} + """.replace( + "real_type", self._get_c_type() + ), + ) + + @cached_property + def homogeneous_freezing_time_dependent_body(self): + return trtc.For( + param_names=( + "rand", + "volume", + "signed_water_mass", + "timestep", + "cell", + "a_w_ice", + "temperature", + "relative_humidity_ice", + ), + name_iter="i", + body=f""" + if ({self.formulae.trivia.unfrozen_and_ice_saturated.c_inline( + signed_water_mass="signed_water_mass[i]", + relative_humidity_ice="relative_humidity_ice[cell[i]]" + )}) {{ + auto da_w_ice = (relative_humidity_ice[cell[i]] - 1.0) * a_w_ice[cell[i]]; + if ({self.formulae.homogeneous_ice_nucleation_rate.d_a_w_ice_within_range.c_inline( + da_w_ice="da_w_ice" + )}) {{ + auto da_w_ice = {self.formulae.homogeneous_ice_nucleation_rate.d_a_w_ice_maximum.c_inline( + da_w_ice="da_w_ice" + )}; + auto rate_assuming_constant_temperature_within_dt = {self.formulae.homogeneous_ice_nucleation_rate.j_hom.c_inline( + T="temperature[cell[i]]", + da_w_ice="da_w_ice" + )} * volume[i]; + auto prob = 1 - {self.formulae.trivia.poissonian_avoidance_function.c_inline( + r="rate_assuming_constant_temperature_within_dt", + dt="timestep" + )}; + if (rand[i] < prob) {{ + signed_water_mass[i] = -1 * signed_water_mass[i]; + }} + }} + }} + """.replace( + "real_type", self._get_c_type() + ), + ) + + @nice_thrust(**NICE_THRUST_FLAGS) + def immersion_freezing_singular( + self, *, attributes, temperature, relative_humidity, cell + ): + n_sd = len(attributes.freezing_temperature) + self.immersion_freezing_singular_body.launch_n( + n=n_sd, + args=( + attributes.freezing_temperature.data, + attributes.signed_water_mass.data, + temperature.data, + relative_humidity.data, + cell.data, + ), + ) + + @nice_thrust(**NICE_THRUST_FLAGS) + def immersion_freezing_time_dependent( # pylint: disable=unused-argument + self, + *, + rand, + attributes, + timestep, + cell, + a_w_ice, + relative_humidity, + ): + n_sd = len(attributes.immersed_surface_area) + self.immersion_freezing_time_dependent_body.launch_n( + n=n_sd, + args=( + rand.data, + attributes.immersed_surface_area.data, + attributes.signed_water_mass.data, + self._get_floating_point(timestep), + cell.data, + a_w_ice.data, + relative_humidity.data, + ), + ) + + @nice_thrust(**NICE_THRUST_FLAGS) + def thaw_instantaneous( + self, + *, + attributes, + cell, + temperature, + ): + n_sd = len(attributes.signed_water_mass) + self.thaw_instantaneous_body.launch_n( + n=n_sd, + args=( + attributes.signed_water_mass.data, + cell.data, + temperature.data, + ), + ) + + @nice_thrust(**NICE_THRUST_FLAGS) + def homogeneous_freezing_threshold( + self, + *, + attributes, + cell, + temperature, + relative_humidity_ice, + ): + n_sd = len(attributes.signed_water_mass) + self.homogeneous_freezing_threshold_body.launch_n( + n=n_sd, + args=( + attributes.signed_water_mass.data, + cell.data, + temperature.data, + relative_humidity_ice.data, + ), + ) + + @nice_thrust(**NICE_THRUST_FLAGS) + def homogeneous_freezing_time_dependent( # pylint: disable=unused-argument + self, + *, + rand, + attributes, + timestep, + cell, + a_w_ice, + temperature, + relative_humidity_ice, + ): + n_sd = len(attributes.signed_water_mass) + self.homogeneous_freezing_time_dependent_body.launch_n( + n=n_sd, + args=( + rand.data, + attributes.volume.data, + attributes.signed_water_mass.data, + self._get_floating_point(timestep), + cell.data, + a_w_ice.data, + temperature.data, + relative_humidity_ice.data, + ), + ) diff --git a/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/index_methods.py b/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/index_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..a6765f13c59559d4a396c6b405bd383fecc4a0fb --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/index_methods.py @@ -0,0 +1,65 @@ +""" +GPU implementation of shuffling and sorting backend methods +""" + +from functools import cached_property + +from PySDM.backends.impl_thrust_rtc.conf import NICE_THRUST_FLAGS +from PySDM.backends.impl_thrust_rtc.nice_thrust import nice_thrust + +from ..conf import trtc +from ..methods.thrust_rtc_backend_methods import ThrustRTCBackendMethods + + +class IndexMethods(ThrustRTCBackendMethods): + @cached_property + def __identity_index_body(self): + return trtc.For( + param_names=("idx",), + name_iter="i", + body="idx[i] = i;", + ) + + @nice_thrust(**NICE_THRUST_FLAGS) + def identity_index(self, idx): + self.__identity_index_body.launch_n(idx.size(), (idx,)) + + @staticmethod + @nice_thrust(**NICE_THRUST_FLAGS) + def shuffle_global(idx, length, u01): + # WARNING: ineffective implementation + + # TODO #328 : Thrust modifies key array, conflicts with rand_reuse logic + # tmpu01 = trtc.device_vector(u01.name_elem_cls(), u01.size()) + # trtc.Copy(u01, tmpu01) + # trtc.Sort_By_Key(tmpu01.range(0, length), idx.range(0, length)) + + trtc.Sort_By_Key(u01.range(0, length), idx.range(0, length)) + + @cached_property + def __shuffle_local_body(self): + return trtc.For( + param_names=("cell_start", "u01", "idx"), + name_iter="i", + body=""" + for (auto k = cell_start[i+1]-1; k > cell_start[i]; k -= 1) { + auto j = cell_start[i] + (int64_t)(u01[k] * (cell_start[i+1] - cell_start[i]) ); + auto tmp = idx[k]; + idx[k] = idx[j]; + idx[j] = tmp; + } + """, + ) + + @nice_thrust(**NICE_THRUST_FLAGS) + def shuffle_local(self, idx, u01, cell_start): + raise AssertionError("Unpredictable behavior") # TODO #358 + self.__shuffle_local_body.launch_n( # pylint: disable=unreachable + cell_start.size() - 1, [cell_start, u01, idx] + ) + + @staticmethod + @nice_thrust(**NICE_THRUST_FLAGS) + def sort_by_key(idx, attr): + d_attr_data_copy, _, _ = attr._get_empty_data(attr.shape, attr.dtype) + trtc.Sort_By_Key(d_attr_data_copy, idx.data) diff --git a/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/isotope_methods.py b/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/isotope_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..b9682f031882f3ef3b75b017a23f6b0c7a7ade33 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/isotope_methods.py @@ -0,0 +1,39 @@ +""" +GPU implementation of isotope-relates backend methods +""" + +from functools import cached_property + +from PySDM.backends.impl_thrust_rtc.conf import NICE_THRUST_FLAGS +from PySDM.backends.impl_thrust_rtc.nice_thrust import nice_thrust + +from ..conf import trtc +from ..methods.thrust_rtc_backend_methods import ThrustRTCBackendMethods + + +class IsotopeMethods(ThrustRTCBackendMethods): + @cached_property + def __isotopic_delta(self): + return trtc.For( + param_names=("output", "ratio", "reference_ratio"), + name_iter="i", + body=f""" + output[i] = {self.formulae.trivia.isotopic_ratio_2_delta.c_inline( + ratio="ratio[i]", + reference_ratio="reference_ratio" + )}; + """.replace( + "real_type", self._get_c_type() + ), + ) + + @nice_thrust(**NICE_THRUST_FLAGS) + def isotopic_delta(self, output, ratio, reference_ratio): + self.__isotopic_delta.launch_n( + n=output.shape[0], + args=(output.data, ratio.data, self._get_floating_point(reference_ratio)), + ) + + @nice_thrust(**NICE_THRUST_FLAGS) + def isotopic_fractionation(self): + pass diff --git a/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/moments_methods.py b/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/moments_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..f84cdaf3fad97b9b42685defef66c3d19e3ebfc3 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/moments_methods.py @@ -0,0 +1,274 @@ +""" +GPU implementation of moment calculation backend methods +""" + +from functools import cached_property + +from PySDM.backends.impl_thrust_rtc.conf import NICE_THRUST_FLAGS +from PySDM.backends.impl_thrust_rtc.nice_thrust import nice_thrust + +from ..conf import trtc +from ..methods.thrust_rtc_backend_methods import ThrustRTCBackendMethods + +# https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#atomic-functions +DOUBLE_ATOMIC_ADD_FOR_COMPUTE_LT_60 = """ +struct Commons { + static __device__ double atomicAdd(double* address, double val) + { + unsigned long long int* address_as_ull = + (unsigned long long int*)address; + unsigned long long int old = *address_as_ull, assumed; + + do { + assumed = old; + old = atomicCAS(address_as_ull, assumed, + __double_as_longlong(val + + __longlong_as_double(assumed))); + + // Note: uses integer comparison to avoid hang in case of NaN (since NaN != NaN) + } while (assumed != old); + + return __longlong_as_double(old); + } +}; +double (*atomicAdd)(double*, double) = &Commons::atomicAdd; +""" + + +class MomentsMethods(ThrustRTCBackendMethods): + def __init__(self, double_precision): + ThrustRTCBackendMethods.__init__(self) + + if trtc.Get_PTX_Arch() < 60 and double_precision: + self.commons = DOUBLE_ATOMIC_ADD_FOR_COMPUTE_LT_60 + else: + self.commons = "" + + @cached_property + def __moments_body_0(self): + return trtc.For( + ( + "idx", + "min_x", + "attr_data", + "x_attr", + "max_x", + "moment_0", + "cell_id", + "multiplicity", + "n_ranks", + "moments", + "ranks", + "n_sd", + "n_cell", + ), + "fake_i", + self.commons + + """ + auto i = idx[fake_i]; + if (min_x <= x_attr[i] && x_attr[i] < max_x) { + atomicAdd((real_type*)&moment_0[cell_id[i]], (real_type)(multiplicity[i])); + for (auto k = 0; k < n_ranks; k+=1) { + auto value = multiplicity[i] * pow( + (real_type)(attr_data[i]), + (real_type)(ranks[k]) + ); + atomicAdd((real_type*) &moments[n_cell * k + cell_id[i]], value); + } + } + """.replace( + "real_type", self._get_c_type() + ), + ) + + @cached_property + def __moments_body_1(self): + return trtc.For( + ("n_ranks", "moments", "moment_0", "n_cell"), + "c_id", + """ + for (auto k = 0; k < n_ranks; k+=1) { + if (moment_0[c_id] == 0) { + moments[n_cell * k + c_id] = 0; + } + else { + moments[n_cell * k + c_id] = moments[n_cell * k + c_id] / moment_0[c_id]; + } + } + """, + ) + + @cached_property + def __spectrum_moments_body_0(self): + return trtc.For( + ( + "idx", + "attr_data", + "x_attr", + "moment_0", + "cell_id", + "multiplicity", + "x_bins", + "n_bins", + "moments", + "rank", + "n_sd", + "n_cell", + ), + "fake_i", + self.commons + + """ + auto i = idx[fake_i]; + for (auto k = 0; k < n_bins; k+=1) { + if (x_bins[k] <= x_attr[i] and x_attr[i] < x_bins[k + 1]) { + atomicAdd( + (real_type*)&moment_0[n_cell * k + cell_id[i]], + (real_type)(multiplicity[i]) + ); + auto val = multiplicity[i] * pow((real_type)(attr_data[i]), (real_type)(rank)); + atomicAdd((real_type*) &moments[n_cell * k + cell_id[i]], val); + break; + } + } + """.replace( + "real_type", self._get_c_type() + ), + ) + + @cached_property + def __spectrum_moments_body_1(self): + return trtc.For( + ("n_bins", "moments", "moment_0", "n_cell"), + "i", + """ + for (auto k = 0; k < n_bins; k+=1) { + if (moment_0[n_cell * k + i] == 0) { + moments[n_cell * k + i] = 0; + } + else { + moments[n_cell * k + i] = moments[n_cell * k + i] / moment_0[n_cell * k + i]; + } + } + """, + ) + + @staticmethod + def ensure_floating_point(data): + data_type = data.data.name_elem_cls() + if data_type not in ("float", "double"): + raise TypeError(f"data type {data_type} not recognised as floating point") + + # TODO #684 + # pylint: disable=unused-argument,too-many-locals + @nice_thrust(**NICE_THRUST_FLAGS) + def moments( + self, + *, + moment_0, + moments, + multiplicity, + attr_data, + cell_id, + idx, + length, + ranks, + min_x, + max_x, + x_attr, + weighting_attribute, + weighting_rank, + skip_division_by_m0, + ): + if weighting_rank != 0: + raise NotImplementedError() + + self.ensure_floating_point(moment_0) + self.ensure_floating_point(moments) + + n_cell = trtc.DVInt64(moments.shape[1]) + n_sd = trtc.DVInt64(moments.shape[0]) + n_ranks = trtc.DVInt64(ranks.shape[0]) + + moments[:] = 0 + moment_0[:] = 0 + + self.__moments_body_0.launch_n( + length, + ( + idx.data, + self._get_floating_point(min_x), + attr_data.data, + x_attr.data, + self._get_floating_point(max_x), + moment_0.data, + cell_id.data, + multiplicity.data, + n_ranks, + moments.data, + ranks.data, + n_sd, + n_cell, + ), + ) + + if not skip_division_by_m0: + self.__moments_body_1.launch_n( + moment_0.shape[0], (n_ranks, moments.data, moment_0.data, n_cell) + ) + + # TODO #684 + # pylint: disable=unused-argument,too-many-locals + @nice_thrust(**NICE_THRUST_FLAGS) + def spectrum_moments( + self, + *, + moment_0, + moments, + multiplicity, + attr_data, + cell_id, + idx, + length, + rank, + x_bins, + x_attr, + weighting_attribute, + weighting_rank, + ): + assert moments.shape[0] == x_bins.shape[0] - 1 + assert moment_0.shape == moments.shape + if weighting_rank != 0: + raise NotImplementedError() + + self.ensure_floating_point(moment_0) + self.ensure_floating_point(moments) + + n_cell = trtc.DVInt64(moments.shape[1]) + n_sd = trtc.DVInt64(moments.shape[0]) + d_rank = trtc.DVInt64(rank) + n_bins = trtc.DVInt64(len(x_bins) - 1) + + moments[:] = 0 + moment_0[:] = 0 + + self.__spectrum_moments_body_0.launch_n( + length, + ( + idx.data, + attr_data.data, + x_attr.data, + moment_0.data, + cell_id.data, + multiplicity.data, + x_bins.data, + n_bins, + moments.data, + d_rank, + n_sd, + n_cell, + ), + ) + + self.__spectrum_moments_body_1.launch_n( + moment_0.shape[1], (n_bins, moments.data, moment_0.data, n_cell) + ) diff --git a/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/pair_methods.py b/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/pair_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..1db5c235329ad732349948cf239983ef2aedfd63 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/pair_methods.py @@ -0,0 +1,217 @@ +""" +GPU implementation of pairwise operations backend methods +""" + +from functools import cached_property + +from PySDM.backends.impl_thrust_rtc.conf import NICE_THRUST_FLAGS +from PySDM.backends.impl_thrust_rtc.nice_thrust import nice_thrust + +from ..conf import trtc +from ..methods.thrust_rtc_backend_methods import ThrustRTCBackendMethods + + +class PairMethods(ThrustRTCBackendMethods): + @cached_property + def __distance_pair_body(self): + return trtc.For( + param_names=("data_out", "data_in", "is_first_in_pair"), + name_iter="i", + body=""" + if (is_first_in_pair[i]) { + data_out[(int64_t)(i/2)] = abs(data_in[i] - data_in[i + 1]); + } + """, + ) + + @nice_thrust(**NICE_THRUST_FLAGS) + def distance_pair(self, data_out, data_in, is_first_in_pair, idx): + perm_in = trtc.DVPermutation(data_in.data, idx.data) + trtc.Fill(data_out.data, trtc.DVDouble(0)) + self.__distance_pair_body.launch_n( + len(idx), [data_out.data, perm_in, is_first_in_pair.indicator.data] + ) + + @cached_property + def __find_pairs_body(self): + return trtc.For( + param_names=("cell_start", "perm_cell_id", "is_first_in_pair", "length"), + name_iter="i", + body=""" + if (i < length -1) { + auto is_in_same_cell = perm_cell_id[i] == perm_cell_id[i + 1]; + auto is_even_index = (i - cell_start[perm_cell_id[i]]) % 2 == 0; + + is_first_in_pair[i] = is_in_same_cell && is_even_index; + } else { + is_first_in_pair[i] = false; + } + """, + ) + + @nice_thrust(**NICE_THRUST_FLAGS) + # TODO #330 handle cell_idx (_ below) + def find_pairs(self, cell_start, is_first_in_pair, cell_id, _, idx): + perm_cell_id = trtc.DVPermutation(cell_id.data, idx.data) + d_length = trtc.DVInt64(len(idx)) + self.__find_pairs_body.launch_n( + n=len(idx), + args=( + cell_start.data, + perm_cell_id, + is_first_in_pair.indicator.data, + d_length, + ), + ) + + @cached_property + def __max_pair_body(self): + return trtc.For( + param_names=("data_out", "perm_in", "is_first_in_pair"), + name_iter="i", + body=""" + if (is_first_in_pair[i]) { + data_out[(int64_t)(i/2)] = max(perm_in[i], perm_in[i + 1]); + } + """, + ) + + @nice_thrust(**NICE_THRUST_FLAGS) + def max_pair(self, data_out, data_in, is_first_in_pair, idx): + perm_in = trtc.DVPermutation(data_in.data, idx.data) + trtc.Fill(data_out.data, trtc.DVDouble(0)) + self.__max_pair_body.launch_n( + len(idx), [data_out.data, perm_in, is_first_in_pair.indicator.data] + ) + + @cached_property + def __sort_pair_body(self): + return trtc.For( + param_names=("data_out", "data_in", "is_first_in_pair"), + name_iter="i", + body=""" + if (is_first_in_pair[i]) { + if (data_in[i] < data_in[i + 1]) { + data_out[i] = data_in[i + 1]; + data_out[i + 1] = data_in[i]; + } + else { + data_out[i] = data_in[i]; + data_out[i + 1] = data_in[i + 1]; + } + } + """, + ) + + @nice_thrust(**NICE_THRUST_FLAGS) + def sort_pair(self, data_out, data_in, is_first_in_pair, idx): + perm_in = trtc.DVPermutation(data_in.data, idx.data) + trtc.Fill(data_out.data, trtc.DVDouble(0)) + if len(idx) > 1: + self.__sort_pair_body.launch_n( + len(idx) - 1, [data_out.data, perm_in, is_first_in_pair.indicator.data] + ) + + @cached_property + def __sort_within_pair_by_attr_body(self): + return trtc.For( + param_names=("idx", "is_first_in_pair", "attr"), + name_iter="i", + body=""" + if (is_first_in_pair[i]) { + if (attr[idx[i]] < attr[idx[i + 1]]) { + auto tmp = idx[i]; + idx[i] = idx[i + 1]; + idx[i + 1] = tmp; + } + } + """, + ) + + def sort_within_pair_by_attr(self, idx, is_first_in_pair, attr): + if len(idx) < 2: + return + self.__sort_within_pair_by_attr_body.launch_n( + len(idx) - 1, [idx.data, is_first_in_pair.indicator.data, attr.data] + ) + + @cached_property + def __sum_pair_body(self): + return trtc.For( + param_names=("data_out", "perm_in", "is_first_in_pair"), + name_iter="i", + body=""" + if (is_first_in_pair[i]) { + data_out[(int64_t)(i/2)] = perm_in[i] + perm_in[i + 1]; + } + """, + ) + + @nice_thrust(**NICE_THRUST_FLAGS) + def sum_pair(self, data_out, data_in, is_first_in_pair, idx): + perm_in = trtc.DVPermutation(data_in.data, idx.data) + trtc.Fill(data_out.data, trtc.DVDouble(0)) + self.__sum_pair_body.launch_n( + n=len(idx), + args=(data_out.data, perm_in, is_first_in_pair.indicator.data), + ) + + @cached_property + def __min_pair_body(self): + return trtc.For( + param_names=( + "data_out", + "data_in", + "is_first_in_pair", + "idx", + ), + name_iter="i", + body=""" + if (is_first_in_pair[i]) { + data_out[(int64_t)(i/2)] = min(data_in[idx[i]], data_in[idx[i + 1]]); + } + """, + ) + + @nice_thrust(**NICE_THRUST_FLAGS) + def min_pair(self, data_out, data_in, is_first_in_pair, idx): + trtc.Fill(data_out.data, trtc.DVDouble(0)) + self.__min_pair_body.launch_n( + n=len(idx), + args=( + data_out.data, + data_in.data, + is_first_in_pair.indicator.data, + idx.data, + ), + ) + + @cached_property + def __multiply_pair_body(self): + return trtc.For( + param_names=( + "data_out", + "data_in", + "is_first_in_pair", + "idx", + ), + name_iter="i", + body=""" + if (is_first_in_pair[i]) { + data_out[(int64_t)(i/2)] = data_in[idx[i]] * data_in[idx[i + 1]]; + } + """, + ) + + @nice_thrust(**NICE_THRUST_FLAGS) + def multiply_pair(self, data_out, data_in, is_first_in_pair, idx): + trtc.Fill(data_out.data, trtc.DVDouble(0)) + self.__multiply_pair_body.launch_n( + n=len(idx), + args=( + data_out.data, + data_in.data, + is_first_in_pair.indicator.data, + idx.data, + ), + ) diff --git a/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/physics_methods.py b/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/physics_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..cd56b986fb55652aab68d494d4fe2535d92638eb --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/physics_methods.py @@ -0,0 +1,224 @@ +""" +GPU implementation of backend methods wrapping basic physics formulae +""" + +from functools import cached_property + +from PySDM.backends.impl_thrust_rtc.conf import NICE_THRUST_FLAGS +from PySDM.backends.impl_thrust_rtc.nice_thrust import nice_thrust + +from ..conf import trtc +from ..methods.thrust_rtc_backend_methods import ThrustRTCBackendMethods + + +class PhysicsMethods(ThrustRTCBackendMethods): + @cached_property + def _temperature_pressure_rh_body(self): + return trtc.For( + ("rhod", "thd", "water_vapour_mixing_ratio", "T", "p", "RH"), + "i", + f""" + T[i] = {self.formulae.state_variable_triplet.T.c_inline( + rhod="rhod[i]", thd="thd[i]")}; + p[i] = {self.formulae.state_variable_triplet.p.c_inline( + rhod="rhod[i]", + T="T[i]", + water_vapour_mixing_ratio="water_vapour_mixing_ratio[i]" + )}; + RH[i] = {self.formulae.state_variable_triplet.pv.c_inline( + p="p[i]", water_vapour_mixing_ratio="water_vapour_mixing_ratio[i]" + )} / {self.formulae.saturation_vapour_pressure.pvs_water.c_inline(T="T[i]")}; + """.replace( + "real_type", self._get_c_type() + ), + ) + + @cached_property + def __explicit_euler_body(self): + return trtc.For( + ("y", "dt", "dy_dt"), + "i", + f""" + y[i] = {self.formulae.trivia.explicit_euler.c_inline(y="y[i]", dt="dt", dy_dt="dy_dt")}; + """.replace( + "real_type", self._get_c_type() + ), + ) + + @cached_property + def __critical_volume_body(self): + return trtc.For( + ("v_cr", "kappa", "f_org", "v_dry", "v_wet", "T", "cell"), + "i", + f""" + auto sigma = {self.formulae.surface_tension.sigma.c_inline( + T="T[cell[i]]", v_wet="v_wet[i]", v_dry="v_dry[i]", f_org="f_org[i]" + )}; + auto r_cr = {self.formulae.hygroscopicity.r_cr.c_inline( + kp="kappa[i]", + rd3="v_dry[i] / const.PI_4_3", + T="T[cell[i]]", + sgm="sigma" + )}; + v_cr[i] = {self.formulae.trivia.volume.c_inline(radius="r_cr")}; + """.replace( + "real_type", self._get_c_type() + ), + ) + + @cached_property + def __volume_of_mass_body(self): + return trtc.For( + param_names=("volume", "mass"), + name_iter="i", + body=f""" + volume[i] = {self.formulae.particle_shape_and_density.mass_to_volume.c_inline(mass="mass[i]")}; + """.replace( + "real_type", self._get_c_type() + ), + ) + + @cached_property + def __mass_of_volume_body(self): + return trtc.For( + param_names=("mass", "volume"), + name_iter="i", + body=f""" + mass[i] = {self.formulae.particle_shape_and_density.volume_to_mass.c_inline(volume="volume[i]")}; + """.replace( + "real_type", self._get_c_type() + ), + ) + + @nice_thrust(**NICE_THRUST_FLAGS) + def critical_volume(self, *, v_cr, kappa, f_org, v_dry, v_wet, T, cell): + self.__critical_volume_body.launch_n( + v_cr.shape[0], + ( + v_cr.data, + kappa.data, + f_org.data, + v_dry.data, + v_wet.data, + T.data, + cell.data, + ), + ) + + @nice_thrust(**NICE_THRUST_FLAGS) + def temperature_pressure_rh( + self, *, rhod, thd, water_vapour_mixing_ratio, T, p, RH + ): + self._temperature_pressure_rh_body.launch_n( + T.shape[0], + ( + rhod.data, + thd.data, + water_vapour_mixing_ratio.data, + T.data, + p.data, + RH.data, + ), + ) + + @nice_thrust(**NICE_THRUST_FLAGS) + def explicit_euler(self, y, dt, dy_dt): + dt = self._get_floating_point(dt) + dy_dt = self._get_floating_point(dy_dt) + self.__explicit_euler_body.launch_n(y.shape[0], (y.data, dt, dy_dt)) + + @nice_thrust(**NICE_THRUST_FLAGS) + def volume_of_water_mass(self, volume, mass): + self.__volume_of_mass_body.launch_n(volume.shape[0], (volume.data, mass.data)) + + @nice_thrust(**NICE_THRUST_FLAGS) + def mass_of_water_volume(self, mass, volume): + self.__mass_of_volume_body.launch_n(mass.shape[0], (mass.data, volume.data)) + + @cached_property + def __air_density_body(self): + return trtc.For( + param_names=("output", "rhod", "water_vapour_mixing_ratio"), + name_iter="i", + body=f""" + output[i] = {self.formulae.state_variable_triplet.rho_of_rhod_and_water_vapour_mixing_ratio.c_inline( + rhod="rhod[i]", + water_vapour_mixing_ratio="water_vapour_mixing_ratio[i]" + )}; + """.replace( + "real_type", self._get_c_type() + ), + ) + + @nice_thrust(**NICE_THRUST_FLAGS) + def air_density(self, output, rhod, water_vapour_mixing_ratio): + self.__air_density_body.launch_n( + n=output.shape[0], + args=(output.data, rhod.data, water_vapour_mixing_ratio.data), + ) + + @cached_property + def __air_dynamic_viscosity_body(self): + return trtc.For( + param_names=("output", "temperature"), + name_iter="i", + body=f""" + output[i] = {self.formulae.air_dynamic_viscosity.eta_air.c_inline( + temperature="temperature[i]" + )}; + """.replace( + "real_type", self._get_c_type() + ), + ) + + @nice_thrust(**NICE_THRUST_FLAGS) + def air_dynamic_viscosity(self, output, temperature): + self.__air_dynamic_viscosity_body.launch_n( + n=output.shape[0], args=(output.data, temperature.data) + ) + + @cached_property + def __reynolds_number_body(self): + return trtc.For( + param_names=( + "output", + "cell_id", + "air_dynamic_viscosity", + "air_density", + "radius", + "velocity_wrt_air", + ), + name_iter="i", + body=f""" + output[i] = {self.formulae.particle_shape_and_density.reynolds_number.c_inline( + radius="radius[i]", + velocity_wrt_air="velocity_wrt_air[i]", + dynamic_viscosity="air_dynamic_viscosity[cell_id[i]]", + density="air_density[cell_id[i]]", + )}; + """.replace( + "real_type", self._get_c_type() + ), + ) + + def reynolds_number( + self, + *, + output, + cell_id, + dynamic_viscosity, + density, + radius, + velocity_wrt_air, + ): + self.__reynolds_number_body.launch_n( + n=output.shape[0], + args=( + output.data, + cell_id.data, + dynamic_viscosity.data, + density.data, + radius.data, + velocity_wrt_air.data, + ), + ) diff --git a/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/terminal_velocity_methods.py b/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/terminal_velocity_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..45534551715aaaddc44df2547f4129a81f0dcfc1 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/terminal_velocity_methods.py @@ -0,0 +1,177 @@ +""" +GPU implementation of backend methods for terminal velocities +""" + +from functools import cached_property + +from PySDM.backends.impl_thrust_rtc.conf import NICE_THRUST_FLAGS +from PySDM.backends.impl_thrust_rtc.nice_thrust import nice_thrust + +from ..conf import trtc +from ..methods.thrust_rtc_backend_methods import ThrustRTCBackendMethods + + +class TerminalVelocityMethods(ThrustRTCBackendMethods): + @cached_property + def __linear_collection_efficiency_body(self): + return trtc.For( + ( + "A", + "B", + "D1", + "D2", + "E1", + "E2", + "F1", + "F2", + "G1", + "G2", + "G3", + "Mf", + "Mg", + "output", + "radii", + "is_first_in_pair", + "idx", + "unit", + ), + "i", + """ + if (is_first_in_pair[i]) { + real_type r = 0; + real_type r_s = 0; + if (radii[idx[i]] > radii[idx[i + 1]]) { + r = radii[idx[i]] / unit; + r_s = radii[idx[i + 1]] / unit; + } + else { + r = radii[idx[i + 1]] / unit; + r_s = radii[idx[i]] / unit; + } + real_type p = r_s / r; + if (p != 0 && p != 1) { + real_type G = pow((G1 / r), Mg) + G2 + G3 * r; + real_type Gp = pow((1 - p), G); + if (Gp != 0) { + real_type D = D1 / pow(r, D2); + real_type E = E1 / pow(r, E2); + real_type F = pow((F1 / r), Mf) + F2; + output[int(i / 2)] = A + B * p + D / pow(p, F) + E / Gp; + if (output[int(i / 2)] < 0) { + output[int(i / 2)] = 0; + } + } + } + } + """.replace( + "real_type", self._get_c_type() + ), + ) + + @cached_property + def __gunn_and_kinzer_interpolation_body(self): + # TODO #599 r<0 + return trtc.For( + ("output", "radius", "factor", "a", "b"), + "i", + """ + auto r_id = (int64_t)(factor * radius[i]); + auto r_rest = (factor * radius[i] - r_id) / factor; + output[i] = a[r_id] + r_rest * b[r_id]; + """, + ) + + def linear_collection_efficiency( + self, *, params, output, radii, is_first_in_pair, unit + ): + # pylint: disable=too-many-locals + A, B, D1, D2, E1, E2, F1, F2, G1, G2, G3, Mf, Mg = params + dA = self._get_floating_point(A) + dB = self._get_floating_point(B) + dD1 = self._get_floating_point(D1) + dD2 = self._get_floating_point(D2) + dE1 = self._get_floating_point(E1) + dE2 = self._get_floating_point(E2) + dF1 = self._get_floating_point(F1) + dF2 = self._get_floating_point(F2) + dG1 = self._get_floating_point(G1) + dG2 = self._get_floating_point(G2) + dG3 = self._get_floating_point(G3) + dMf = self._get_floating_point(Mf) + dMg = self._get_floating_point(Mg) + dunit = self._get_floating_point(unit) + trtc.Fill(output.data, trtc.DVDouble(0)) + self.__linear_collection_efficiency_body.launch_n( + len(is_first_in_pair) - 1, + [ + dA, + dB, + dD1, + dD2, + dE1, + dE2, + dF1, + dF2, + dG1, + dG2, + dG3, + dMf, + dMg, + output.data, + radii.data, + is_first_in_pair.indicator.data, + radii.idx.data, + dunit, + ], + ) + + @nice_thrust(**NICE_THRUST_FLAGS) + def gunn_and_kinzer_interpolation(self, *, output, radius, factor, b, c): + factor_device = trtc.DVInt64(factor) + self.__gunn_and_kinzer_interpolation_body.launch_n( + len(radius), (output.data, radius.data, factor_device, b.data, c.data) + ) + + @cached_property + def __power_series_body(self): + # TODO #599 r<0 + return trtc.For( + ("output", "radius", "num_terms", "prefactors", "powers"), + "i", + """ + output[i] = 0.0 + int kmax = num_terms + for (auto k = 0; k < kmax; k+=1) { + auto sumterm = prefactors[k] * pow(radius[i], 3*powers[k]) + output[i] = output[i] + sumterm + """, + ) + + @nice_thrust(**NICE_THRUST_FLAGS) + def power_series(self, *, values, radius, num_terms, prefactors, powers): + prefactors = self._get_floating_point(prefactors) + powers = self._get_floating_point(powers) + num_terms = self._get_floating_point(num_terms) + self.__power_series_body.launch_n( + values.size(), (values, radius, num_terms, prefactors, powers) + ) + + @cached_property + def __rogers_and_yau_terminal_velocity_body(self): + return trtc.For( + param_names=("values", "radius"), + name_iter="i", + body=f""" + values[i] = {self.formulae.terminal_velocity.v_term.c_inline( + radius="radius[i]" + )}; + """.replace( + "real_type", self._get_c_type() + ), + ) + + @nice_thrust(**NICE_THRUST_FLAGS) + def rogers_and_yau_terminal_velocity(self, *, values, radius): + self.__rogers_and_yau_terminal_velocity_body.launch_n( + n=values.size(), args=[values, radius] + ) diff --git a/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/thrust_rtc_backend_methods.py b/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/thrust_rtc_backend_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..9f9df2fc8714af34bc98c58c25c1633c8c02197c --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_thrust_rtc/methods/thrust_rtc_backend_methods.py @@ -0,0 +1,27 @@ +""" +common parent class for all ThrustRTC backend methods classes +""" + +from typing import Callable, Optional + +from ...impl_common.backend_methods import BackendMethods + + +class ThrustRTCBackendMethods(BackendMethods): # pylint: disable=too-few-public-methods + def __init__(self): + super().__init__() + if not hasattr(self, "_conv_function"): + self._conv_function: Optional[Callable] = None + if not hasattr(self, "_real_type"): + self._real_type = None + if not hasattr(self, "_np_dtype"): + self._np_dtype = None + + def _get_floating_point(self, number): + return self._conv_function(number) # pylint: disable=not-callable + + def _get_c_type(self): + return self._real_type + + def _get_np_dtype(self): + return self._np_dtype diff --git a/PySDM/source/PySDM/backends/impl_thrust_rtc/nice_thrust.py b/PySDM/source/PySDM/backends/impl_thrust_rtc/nice_thrust.py new file mode 100644 index 0000000000000000000000000000000000000000..9d31b9b138045d068dcf908e378fd0bd0c94d8ce --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_thrust_rtc/nice_thrust.py @@ -0,0 +1,20 @@ +""" +a decorator triggering ThrustRTC.Wait() after each function call +""" + +from PySDM.backends.impl_thrust_rtc.conf import trtc + + +def nice_thrust(*, wait=False, debug_print=False): + def decorator(func): + def wrapper(*args, **kwargs): + if debug_print: + print(func.__name__) + result = func(*args, **kwargs) + if wait: + trtc.Wait() + return result + + return wrapper + + return decorator diff --git a/PySDM/source/PySDM/backends/impl_thrust_rtc/random.py b/PySDM/source/PySDM/backends/impl_thrust_rtc/random.py new file mode 100644 index 0000000000000000000000000000000000000000..2a1dc5e1392220f21e4c089775c02e3008ce1d29 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_thrust_rtc/random.py @@ -0,0 +1,41 @@ +""" +random number generator class for ThrustRTC backend (using CURandRTC) +""" + +from PySDM.backends.impl_thrust_rtc.conf import NICE_THRUST_FLAGS +from PySDM.backends.impl_thrust_rtc.nice_thrust import nice_thrust + +from ..impl_common.random_common import RandomCommon +from .conf import rndrtc, trtc + +# TIP: sometimes only half array is needed + + +class Random(RandomCommon): # pylint: disable=too-few-public-methods + __urand_init_rng_state_body = trtc.For( + ["rng", "states", "seed"], + "i", + """ + rng.state_init(seed, i, 0, states[i]); + """, + ) + + __urand_body = trtc.For( + ["states", "vec_rnd"], + "i", + """ + vec_rnd[i] = states[i].rand01(); + """, + ) + + def __init__(self, size, seed): + super().__init__(size, seed) + rng = rndrtc.DVRNG() + self.generator = trtc.device_vector("RNGState", size) + dseed = trtc.DVInt64(seed) + Random.__urand_init_rng_state_body.launch_n(size, [rng, self.generator, dseed]) + + @nice_thrust(**NICE_THRUST_FLAGS) + def __call__(self, storage): + assert len(storage) <= self.size + Random.__urand_body.launch_n(len(storage), [self.generator, storage.data]) diff --git a/PySDM/source/PySDM/backends/impl_thrust_rtc/storage.py b/PySDM/source/PySDM/backends/impl_thrust_rtc/storage.py new file mode 100644 index 0000000000000000000000000000000000000000..ecff39ac18766ae17177b19917261a01e64f008f --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_thrust_rtc/storage.py @@ -0,0 +1,574 @@ +""" +storage internals for the ThrustRTC backend +""" + +import numpy as np + +from PySDM.backends.impl_common.storage_utils import ( + StorageBase, + StorageSignature, + empty, + get_data_from_ndarray, +) +from PySDM.backends.impl_thrust_rtc.conf import NICE_THRUST_FLAGS, trtc +from PySDM.backends.impl_thrust_rtc.nice_thrust import nice_thrust + + +def make_storage_class(BACKEND): # pylint: disable=too-many-statements + class Impl: + @staticmethod + def thrust(obj): + if isinstance(obj, tuple): + result = tuple(Impl.thrust(o) for o in obj) + elif hasattr(obj, "data"): + result = obj.data + elif isinstance(obj, float): + result = BACKEND._get_floating_point(obj) + elif isinstance(obj, int): + result = trtc.DVInt64(obj) + else: + raise ValueError(f"Cannot upload {obj} to device.") + return result + + __add_elementwise_body = trtc.For( + ("output", "addend"), + "i", + """ + output[i] += addend[i]; + """, + ) + + __add_elementwise_with_multiplier_body = trtc.For( + ("output", "addend", "multiplier"), + "i", + """ + output[i] += multiplier * addend[i]; + """, + ) + + __add_body = trtc.For( + ["output", "addend"], + "i", + """ + output[i] += addend; + """, + ) + + @staticmethod + @nice_thrust(**NICE_THRUST_FLAGS) + def add(output, addend): + args = (output, addend) + if hasattr(addend, "data"): + loop = Impl.__add_elementwise_body + elif ( + isinstance(addend, tuple) + and len(addend) == 3 + and isinstance(addend[0], float) + and addend[1] == "*" + and isinstance(addend[2], Storage) + ): + loop = Impl.__add_elementwise_with_multiplier_body + args = (output, addend[2], addend[0]) + else: + loop = Impl.__add_body + loop.launch_n(n=output.shape[0], args=Impl.thrust(args)) + + @staticmethod + @nice_thrust(**NICE_THRUST_FLAGS) + def amin(data): + return trtc.Reduce(data, Impl.thrust(np.inf), trtc.Minimum()) + + @staticmethod + @nice_thrust(**NICE_THRUST_FLAGS) + def amax(data): + return trtc.Reduce(data, Impl.thrust(-np.inf), trtc.Maximum()) + + __row_modulo_body = trtc.For( + ("output", "divisor", "length"), + "i", + """ + auto d = (int64_t)(i / length); + output[i] %= divisor[d]; + """, + ) + + @staticmethod + @nice_thrust(**NICE_THRUST_FLAGS) + def row_modulo(output, divisor): + Impl.__row_modulo_body.launch_n( + output.shape[0], Impl.thrust((output, divisor, output.shape[1])) + ) + + __floor_body = trtc.For( + ("arr",), + "i", + """ + if (arr[i] >= 0) { + arr[i] = (int64_t)(arr[i]); + } + else { + auto old = arr[i]; + arr[i] = (int64_t)(arr[i]); + if (old != arr[i]) { + arr[i] -= 1; + } + } + """, + ) + + @staticmethod + @nice_thrust(**NICE_THRUST_FLAGS) + def floor(output): + Impl.__floor_body.launch_n(output.shape[0], Impl.thrust((output,))) + + __floor_out_of_place_body = trtc.For( + ("output", "input_data"), + "i", + """ + if (input_data[i] >= 0) { + output[i] = (int64_t)(input_data[i]); + } + else { + output[i] = (int64_t)(input_data[i]); + if (input_data[i] != output[i]) { + output[i] -= 1; + } + } + """, + ) + + @staticmethod + @nice_thrust(**NICE_THRUST_FLAGS) + def floor_out_of_place(output, input_data): + Impl.__floor_out_of_place_body.launch_n( + output.shape[0], Impl.thrust((output, input_data)) + ) + + __multiply_elementwise_body = trtc.For( + ("output", "multiplier"), + "i", + """ + output[i] *= multiplier[i]; + """, + ) + + __multiply_body = trtc.For( + ["output", "multiplier"], + "i", + """ + output[i] *= multiplier; + """, + ) + + @staticmethod + @nice_thrust(**NICE_THRUST_FLAGS) + def multiply(output, multiplier): + if hasattr(multiplier, "data"): + loop = Impl.__multiply_elementwise_body + else: + loop = Impl.__multiply_body + loop.launch_n(output.shape[0], Impl.thrust((output, multiplier))) + + __truediv_elementwise_body = trtc.For( + ("output", "divisor"), + "i", + """ + output[i] /= divisor[i]; + """, + ) + + __truediv_body = trtc.For( + ["output", "divisor"], + "i", + """ + output[i] /= divisor; + """, + ) + + @staticmethod + @nice_thrust(**NICE_THRUST_FLAGS) + def truediv(output, divisor): + if hasattr(divisor, "data"): + loop = Impl.__truediv_elementwise_body + else: + loop = Impl.__truediv_body + loop.launch_n(output.shape[0], Impl.thrust((output, divisor))) + + __multiply_out_of_place_elementwise_body = trtc.For( + ("output", "multiplicand", "multiplier"), + "i", + """ + output[i] = multiplicand[i] * multiplier[i]; + """, + ) + + __multiply_out_of_place_body = trtc.For( + ("output", "multiplicand", "multiplier"), + "i", + """ + output[i] = multiplicand[i] * multiplier; + """, + ) + + @staticmethod + @nice_thrust(**NICE_THRUST_FLAGS) + def multiply_out_of_place(output, multiplicand, multiplier): + if hasattr(multiplier, "data"): + loop = Impl.__multiply_out_of_place_elementwise_body + elif isinstance(multiplier, float): + loop = Impl.__multiply_out_of_place_body + else: + raise NotImplementedError() + loop.launch_n( + output.shape[0], Impl.thrust((output, multiplicand, multiplier)) + ) + + __divide_out_of_place_elementwise_body = trtc.For( + ("output", "dividend", "divisor"), + "i", + """ + output[i] = dividend[i] / divisor[i]; + """, + ) + + @staticmethod + @nice_thrust(**NICE_THRUST_FLAGS) + def divide_out_of_place(output, dividend, divisor): + if hasattr(divisor, "data"): + loop = Impl.__divide_out_of_place_elementwise_body + else: + raise NotImplementedError() + loop.launch_n(output.shape[0], Impl.thrust((output, dividend, divisor))) + + __sum_out_of_place_elementwise_body = trtc.For( + ("output", "a", "b"), + "i", + """ + output[i] = a[i] + b[i]; + """, + ) + + @staticmethod + @nice_thrust(**NICE_THRUST_FLAGS) + def sum_out_of_place(output, a, b): + if hasattr(a, "data"): + loop = Impl.__sum_out_of_place_elementwise_body + else: + raise NotImplementedError() + loop.launch_n(output.shape[0], Impl.thrust((output, a, b))) + + __power_body = trtc.For( + ("output", "exponent"), + "i", + """ + output[i] = pow(output[i], exponent); + """, + ) + + @staticmethod + @nice_thrust(**NICE_THRUST_FLAGS) + def power(output, exponent: int): + if exponent == 1: + return + Impl.__power_body.launch_n( + output.shape[0], Impl.thrust((output, float(exponent))) + ) + + __subtract_body = trtc.For( + ("output", "subtrahend"), + "i", + """ + output[i] -= subtrahend[i]; + """, + ) + + @staticmethod + @nice_thrust(**NICE_THRUST_FLAGS) + def subtract(output, subtrahend): + Impl.__subtract_body.launch_n( + output.shape[0], Impl.thrust((output, subtrahend)) + ) + + __divide_if_not_zero_body = trtc.For( + param_names=("output", "divisor"), + name_iter="i", + body=""" + if (divisor[i] != 0.0) { + output[i] /= divisor[i]; + } + """, + ) + + @staticmethod + @nice_thrust(**NICE_THRUST_FLAGS) + def divide_if_not_zero(output, divisor): + Impl.__divide_if_not_zero_body.launch_n( + n=(output.shape[0]), args=(Impl.thrust((output, divisor))) + ) + + __exp_body = trtc.For( + ("output",), + "i", + """ + output[i] = exp(output[i]); + """, + ) + + @staticmethod + @nice_thrust(**NICE_THRUST_FLAGS) + def exp(output): + Impl.__exp_body.launch_n(output.shape[0], Impl.thrust((output,))) + + __abs_body = trtc.For( + ("output",), + "i", + """ + output[i] = abs(output[i]); + """, + ) + + @staticmethod + @nice_thrust(**NICE_THRUST_FLAGS) + def abs(output): + Impl.__abs_body.launch_n(output.shape[0], Impl.thrust((output,))) + + class Storage(StorageBase): + FLOAT = BACKEND._get_np_dtype() + INT = np.int64 + BOOL = np.bool_ + + def __getitem__(self, item): + dim = len(self.shape) + if isinstance(item, slice): + start = item.start or 0 + stop = item.stop or self.shape[0] + if dim == 1: + result_data = self.data.range(start, stop) + result_shape = (stop - start,) + elif dim == 2: + result_data = self.data.range( + self.shape[1] * start, self.shape[1] * stop + ) + result_shape = (stop - start, self.shape[1]) + else: + raise NotImplementedError( + "Only 2 or less dimensions array is supported." + ) + result = Storage( + StorageSignature(result_data, result_shape, self.dtype) + ) + elif ( + dim == 2 + and isinstance(item, tuple) + and isinstance(item[0], int) + and isinstance(item[1], slice) + ): + assert item[1].start is None or item[1].start == 0 + assert item[1].stop is None or item[1].stop == self.shape[1] + assert item[1].step is None or item[1].step == 1 + result_data = self.data.range( + self.shape[1] * item[0], self.shape[1] * (item[0] + 1) + ) + result = Storage( + StorageSignature(result_data, (*self.shape[1:],), self.dtype) + ) + else: + result = self.to_ndarray()[item] + return result + + def __setitem__(self, key, value): + if not ( + isinstance(key, slice) + and key.start is None + and key.stop is None + and key.step is None + ): + raise NotImplementedError() + if ( + hasattr(value, "data") + and hasattr(value, "shape") + and len(value.shape) != 0 + ): + if isinstance(value, np.ndarray): + vector = trtc.device_vector_from_numpy(value) + trtc.Copy(vector, self.data) + else: + trtc.Copy(value.data, self.data) + else: + if isinstance(value, int): + dvalue = trtc.DVInt64(value) + elif isinstance(value, float): + dvalue = BACKEND._get_floating_point(value) + else: + raise TypeError("Only Storage, int and float are supported.") + trtc.Fill(self.data, dvalue) + return self + + def __iadd__(self, other): + Impl.add(self, other) + return self + + def __isub__(self, other): + Impl.subtract(self, other) + return self + + def __imul__(self, other): + Impl.multiply(self, other) + return self + + def __itruediv__(self, other): + Impl.truediv(self, other) + return self + + def __imod__(self, other): + Impl.row_modulo(self, other) + return self + + def __ipow__(self, other): + Impl.power(self, other) + return self + + def __bool__(self): + if len(self) == 1: + result = bool(self.data.to_host()[0] != 0) + else: + raise NotImplementedError("Logic value of array is ambiguous.") + return result + + def _to_host(self): + if isinstance(self.data, trtc.DVVector.DVRange): + if self.dtype is self.FLOAT: + elem_cls = BACKEND._get_c_type() + elif self.dtype is self.INT: + elem_cls = "int64_t" + elif self.dtype is self.BOOL: + elem_cls = "bool" + else: + raise NotImplementedError() + + data = trtc.device_vector(elem_cls, self.data.size()) + + trtc.Copy(self.data, data) + else: + data = self.data + return data.to_host() + + def amin(self): + return Impl.amin(self.data) + + def amax(self): + return Impl.amax(self.data) + + def all(self): + assert self.dtype is self.BOOL + return self.amin() + + def download(self, target, reshape=False): + shape = target.shape if reshape else self.shape + target[:] = np.reshape(self._to_host(), shape) + + @staticmethod + def _get_empty_data(shape, dtype): + if dtype in (float, Storage.FLOAT): + elem_cls = BACKEND._get_c_type() + dtype = Storage.FLOAT + elif dtype in (int, Storage.INT): + elem_cls = "int64_t" + dtype = Storage.INT + elif dtype in (bool, Storage.BOOL): + elem_cls = "bool" + dtype = Storage.BOOL + else: + raise NotImplementedError + + size = int(np.prod(shape)) + if size == 0: + data = None + else: + data = trtc.device_vector(elem_cls, size) + return StorageSignature(data, shape, dtype) + + @staticmethod + def empty(shape, dtype): + return empty(shape, dtype, Storage) + + @staticmethod + def _get_data_from_ndarray(array): + return get_data_from_ndarray( + array=array, + storage_class=Storage, + copy_fun=lambda array_astype: trtc.device_vector_from_numpy( + array_astype.ravel() + ), + ) + + @staticmethod + def from_ndarray(array): + result = Storage(Storage._get_data_from_ndarray(array)) + return result + + def floor(self, other=None): + if other is None: + Impl.floor(self.data) + else: + Impl.floor_out_of_place(self, other) + return self + + def product(self, multiplicand, multiplier): + Impl.multiply_out_of_place(self, multiplicand, multiplier) + return self + + def ratio(self, dividend, divisor): + Impl.divide_out_of_place(self, dividend, divisor) + return self + + def sum(self, arg_a, arg_b): + Impl.sum_out_of_place(self, arg_a, arg_b) + return self + + def ravel(self, other): + if isinstance(other, Storage): + trtc.Copy(other.data, self.data) + else: + self.data = trtc.device_vector_from_numpy(other.ravel()) + + def to_ndarray(self): + result = self._to_host() + result = np.reshape(result, self.shape) + return result + + def urand(self, generator): + generator(self) + + def upload(self, data): + trtc.Copy( + trtc.device_vector_from_numpy(data.astype(self.dtype).ravel()), + self.data, + ) + + def divide_if_not_zero(self, divisor): + Impl.divide_if_not_zero(self, divisor) + return self + + def fill(self, other): + if isinstance(other, Storage): + trtc.Copy(other.data, self.data) + else: + if isinstance(other, int): + dvalue = trtc.DVInt64(other) + elif isinstance(other, float): + dvalue = BACKEND._get_floating_point(other) + else: + raise TypeError("Only Storage, int and float are supported.") + trtc.Fill(self.data, dvalue) + return self + + def exp(self): + Impl.exp(self) + return self + + def abs(self): + Impl.abs(self) + return self + + return Storage diff --git a/PySDM/source/PySDM/backends/impl_thrust_rtc/test_helpers/__init__.py b/PySDM/source/PySDM/backends/impl_thrust_rtc/test_helpers/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..ecc99ce2eb192563c90163735f4a4ace6a59b082 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_thrust_rtc/test_helpers/__init__.py @@ -0,0 +1,4 @@ +""" +logic intended to be used in tests only including the +`PySDM.backends.impl_thrust_rtc.test_helpers.fake_thrust_rtc.FakeThrustRTC` magick +""" diff --git a/PySDM/source/PySDM/backends/impl_thrust_rtc/test_helpers/cpp2python.py b/PySDM/source/PySDM/backends/impl_thrust_rtc/test_helpers/cpp2python.py new file mode 100644 index 0000000000000000000000000000000000000000..8d3e7623bcd53cef41d156b4f6db6a58910d2fb7 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_thrust_rtc/test_helpers/cpp2python.py @@ -0,0 +1,224 @@ +""" +a simplistic C++ to Python translation utils for use in + `PySDM.backends.impl_thrust_rtc.test_helpers.fake_thrust_rtc.FakeThrustRTC` +""" + +import re + +from ...impl_numba.conf import JIT_FLAGS + +JIT_OPTS = "error_model='numpy', fastmath=True" + +CPPYTHON = { + "int ": "", + "void ": "", + "int64_t *": "", + "int64_t ": "", + "double *": "", + "double ": "", + "float *": "", + "float ": "", + "auto ": "", + "bool ": "", + "[1] = {}; // float": "=np.empty(1, dtype=float)", + "[1] = {}": "=np.empty(1, dtype=np.int64)", + "[] = {": " = (", + " {": ":", + "}; // array": ")", + "//": "#", + "||": "or", + "&&": "and", + "! ": "not ", + "&": "", + "(int64_t)": "np.int64", # TODO #324 test depicting failure when set to int16 + "(double)": "np.float64", + "(float)": "np.float32", + "return;": "continue", + "void*": "", + "VectorView ": "", + "VectorView ": "", + "VectorView ": "", + "VectorView ": "", + "::": "_", + "(*": "", + ")(, )": "", + "else if": "elif", + "printf": "print", + "false": "False", + "true": "True", +} + + +def extract_var(cpp, start_position, end_char): + stop = cpp.find(end_char, start_position) + return cpp[start_position + 1 : stop].strip() + + +def for_to_python(cpp: str) -> str: + """ + need ';' in code and be running after removing types + + :param cpp: + :return python code: + """ + start = cpp.find("(") + var = extract_var(cpp, start, "=") + + start = cpp.find("=") + range_start = extract_var(cpp, start, ";") + + start = cpp.find("<") + if start == -1: + start = cpp.find(">") + sign = "-" if cpp[start] == ">" else "" + range_stop = extract_var(cpp, start, ";") + + start = cpp.find("+=") + if start == -1: + start = cpp.find("-=") + start += 1 + + range_step = extract_var(cpp, start, ")") + + return f"for {var} in range({range_start}, {range_stop}, {sign}{range_step}):" + + +def replace_fors(cpp) -> str: + start = cpp.find("for ") + while start > -1: + stop = cpp.find(":", start) + cpp_for = cpp[start : stop + 1] + python_for = for_to_python(cpp_for) + cpp = cpp.replace(cpp_for, python_for.replace("for", "__python_token__")) + start = cpp.find("for ", start + len(python_for)) + return cpp.replace("__python_token__", "for") + + +def atomic_min_to_python(cpp: str) -> str: + cpp = ( + cpp.replace("unsigned int*", "") + .replace("double*", "") + .replace("float*", "") + .replace(" ", "") + .replace("()", "") + .replace("&", "") + ) + cpp = re.sub( + r"atomicMin\((\w+)\[(\w+)],\s*__float_as_uint\(([^)]*)\)\);", + r"np.minimum(\1[\2:\2+1], np.asarray(\3), \1[\2:\2+1]);", + cpp, + ) + return cpp + + +def replace_atomic_mins(cpp: str) -> (str, bool): + start = cpp.find("atomicMin") + parallel = start == -1 + while start > -1: + stop = cpp.find(";", start) + cpp_atomic_min = cpp[start : stop + 1] + python_atomic_min = atomic_min_to_python(cpp_atomic_min) + cpp = cpp.replace(cpp_atomic_min, python_atomic_min) + start = cpp.find("atomicMin", start + len(python_atomic_min)) + return cpp, parallel + + +def atomic_add_to_python(cpp: str) -> str: + cpp = ( + cpp.replace("atomicAdd", "") + .replace("\n", "") + .replace("unsigned long long int*", "") + .replace("unsigned long long int", "") + .replace("double*", "") + .replace("float*", "") + .replace(" ", "") + .replace("()", "") + .replace("&", "") + .replace(",", "+=", 1)[1:-2] + ) # remove '(' and ');' + return cpp + + +def replace_atomic_adds(cpp: str) -> (str, bool): + start = cpp.find("atomicAdd") + parallel = start == -1 + while start > -1: + stop = cpp.find(";", start) + cpp_atomic_add = cpp[start : stop + 1] + python_atomic_add = atomic_add_to_python(cpp_atomic_add) + cpp = cpp.replace(cpp_atomic_add, python_atomic_add) + start = cpp.find("atomicAdd", start + len(python_atomic_add)) + return cpp, parallel + + +def extract_struct_defs(cpp: str) -> (str, str): + stop_string = "};" + structs = [] + names = [] + + start = cpp.find("struct ") + while start > -1: + stop = cpp.find(stop_string, start) + struct = cpp[start : stop + len(stop_string)] + cpp = cpp.replace(struct, "") + start = cpp.find("struct ", start + 1) + structs.append(struct[struct.find(":") + 1 : -2]) + names.append(re.match(r"(struct )(.*)(:)", struct)[2]) + + for i, struct in enumerate(structs): + structs[i] = struct.replace( + "static __device__ ", + f"\n @numba.njit(parallel=False, {JIT_OPTS})\n def {names[i]}_", + ) + + return cpp, "\n".join(structs) + + +def to_numba(name, args, iter_var, body): + body = re.sub(r"static_assert\([^;]*;", "", body) + body = re.sub(r"static_cast<[^;]*>", "", body) + body = body.replace("\n", "\n ") + for cpp, python in CPPYTHON.items(): + body = body.replace(cpp, python) + body = replace_fors(body) + body, parallel_add = replace_atomic_adds(body) + body, parallel_min = replace_atomic_mins(body) + parallel = parallel_add and parallel_min + + body, structs = extract_struct_defs(body) + + result = ( + f""" +def make(self): + import numpy as np + from numpy import ( + floor, ceil, + exp, log, + power, sqrt, + arctanh as atanh, + arcsinh as asinh, + sinh, + maximum, minimum, + where, # TODO #1295 + ) + import numba + + @numba.njit(parallel=False, {JIT_OPTS}) + def ldexp(a, b): + return a * 2**b + + {structs} + @numba.njit(parallel={parallel and JIT_FLAGS['parallel']}, {JIT_OPTS}) + def {name}(__python_n__, {str(args).replace("'", "").replace('"', '')[1:-1]}): + for {iter_var} in numba.prange(__python_n__): + {body} + + return {name} +""".replace( + "};", ")" + ) + .replace("} ", "") + .replace("}", "") + ) + + return result diff --git a/PySDM/source/PySDM/backends/impl_thrust_rtc/test_helpers/fake_thrust_rtc.py b/PySDM/source/PySDM/backends/impl_thrust_rtc/test_helpers/fake_thrust_rtc.py new file mode 100644 index 0000000000000000000000000000000000000000..fadcccf8d31bcf048a752c2ae397370ac05058d0 --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_thrust_rtc/test_helpers/fake_thrust_rtc.py @@ -0,0 +1,237 @@ +""" +Numba-based implementation of ThrustRTC API involving translation of C++ code + to njitted (and multi-threaded) Python code - a hacky workaround enabling + testing ThrustRTC code on machines with no GPU/CUDA +""" + +# pylint: disable=no-member,unsupported-assignment-operation,unsubscriptable-object,no-value-for-parameter +import types +import warnings + +import numpy as np +from numba.core.errors import NumbaError + +from .cpp2python import to_numba + + +class FakeThrustRTC: # pylint: disable=too-many-public-methods + class DVRange: + def __init__(self, ndarray): + self.ndarray = ndarray + self.size = lambda: len(self.ndarray) + self.range = lambda start, stop: FakeThrustRTC.DVRange( + self.ndarray[start:stop] + ) + + def __setitem__(self, key, value): + self.ndarray[key] = value + + def __getitem__(self, item): + return self.ndarray[item] + + class DVVector: + DVRange = None + DVVector = None + + def __init__(self, ndarray): + FakeThrustRTC.DVVector.DVVector = FakeThrustRTC.DVVector + FakeThrustRTC.DVVector.DVRange = FakeThrustRTC.DVRange + self.ndarray: np.ndarray = ndarray + self.size = lambda: len(self.ndarray) + self.range = lambda start, stop: FakeThrustRTC.DVRange( + self.ndarray[start:stop] + ) + self.to_host = lambda: np.copy(self.ndarray) + + def __setitem__(self, key, value): + if isinstance(value, FakeThrustRTC.Number): + value = value.ndarray + self.ndarray[key] = value + + def __getitem__(self, item): + return self.ndarray[item] + + def name_elem_cls(self): + return {"f": "float", "d": "double", "l": "long"}[ + np.ctypeslib.as_ctypes_type(self.ndarray.dtype)._type_ + ] + + class Number: # pylint: disable=too-few-public-methods + def __init__(self, number): + self.ndarray = number + + @staticmethod + def DVInt64(number: int): # pylint: disable=invalid-name + assert isinstance(number, int) + return FakeThrustRTC.Number(number) + + @staticmethod + def DVDouble(number: float): # pylint: disable=invalid-name + return FakeThrustRTC.Number(number) + + @staticmethod + def DVBool(number: bool): # pylint: disable=invalid-name + assert isinstance(number, bool) + return FakeThrustRTC.Number(number) + + @staticmethod + def DVFloat(number: float): # pylint: disable=invalid-name + return FakeThrustRTC.Number(number) + + class For: # pylint: disable=too-few-public-methods + def __init__(self, param_names, name_iter, body): + for name in param_names: + assert "," not in name + d = {} + self.code = to_numba( + "__internal_python_method__", param_names, name_iter, body + ) + exec(self.code, d) # pylint: disable=exec-used + self.make = types.MethodType(d["make"], self) + self.__internal_python_method__ = ( + self.make() # pylint: disable=not-callable + ) + + def launch_n(self, n, args): + if n == 0: + raise SystemError("An internal error happened :) (size==0).") + try: + result = self.__internal_python_method__( + n, *(arg.ndarray for arg in args) + ) + except (NumbaError, IndexError) as error: + warnings.warn( + f"Error occurred while JIT-compiling/executing: {self.code}" + ) + raise error + return result + + @staticmethod + def Sort(dvvector: DVVector): # pylint: disable=invalid-name + dvvector.ndarray[:] = np.sort(dvvector.ndarray) + + @staticmethod + def Copy(vector_in: DVVector, vector_out: DVVector): # pylint: disable=invalid-name + if vector_out.ndarray.dtype != vector_in.ndarray.dtype: + raise ValueError( + f"Incompatible types {vector_out.ndarray.dtype} and {vector_in.ndarray.dtype}" + ) + vector_out.ndarray[:] = vector_in.ndarray + + @staticmethod + def Fill(vector, value): # pylint: disable=invalid-name + vector[:] = value + + @staticmethod + def Find(vector: DVVector, value): # pylint: disable=invalid-name + for i in range(len(vector.ndarray)): + if vector[i] == value.ndarray: + return i + return None + + @staticmethod + def device_vector(elem_cls, size): + if not size > 0: + raise ValueError("size must be >0") + if elem_cls == "double": + dtype = np.float64 + elif elem_cls == "float": + dtype = np.float32 + elif elem_cls == "int64_t": + dtype = np.int64 + elif elem_cls == "uint64_t": + dtype = np.uint64 + elif elem_cls == "bool": + dtype = np.bool_ + else: + raise NotImplementedError(f"Unsupported type {elem_cls}") + result = np.full( + size, np.nan if elem_cls in ("float", "double") else -666, dtype=dtype + ) + return FakeThrustRTC.DVVector(result) + + @staticmethod + def device_vector_from_numpy(ndarray): + result = np.copy(ndarray) + return FakeThrustRTC.DVVector(result) + + @staticmethod + def Max_Element(dvvector): # pylint: disable=invalid-name + np.amax(dvvector.ndarray) + + @staticmethod + def Min_Element(dvvector): # pylint: disable=invalid-name + np.amin(dvvector.ndarray) + + @staticmethod + def DVPermutation(dvvector, idx): # pylint: disable=invalid-name + _length = np.where(idx.ndarray == idx.size())[0] + length = _length[0] if len(_length) != 0 else idx.size() + result = dvvector.ndarray[idx.ndarray[:length]] + return FakeThrustRTC.DVVector(result) + + @staticmethod + def Count(dvvector, value): # pylint: disable=invalid-name + unique, counts = np.unique(dvvector.ndarray, return_counts=True) + results = dict(zip(unique, counts)) + if value.ndarray in results: + return results[value.ndarray] + return 0 + + @staticmethod + def Reduce(dvvector, start, operator): # pylint: disable=invalid-name + if operator == "+": + return start.ndarray + dvvector.ndarray.sum() + if operator == "-": + return start.ndarray - dvvector.ndarray.sum() + if operator == "max": + return max(start.ndarray, np.amax(dvvector.ndarray)) + if operator == "min": + return min(start.ndarray, np.amin(dvvector.ndarray)) + raise NotImplementedError() + + @staticmethod + def Plus(): # pylint: disable=invalid-name + return "+" + + @staticmethod + def Minus(): # pylint: disable=invalid-name + return "-" + + @staticmethod + def Maximum(): # pylint: disable=invalid-name + return "max" + + @staticmethod + def Minimum(): # pylint: disable=invalid-name + return "min" + + @staticmethod + def Transform_Binary(vec_in1, vec_in2, vec_out, op): # pylint: disable=invalid-name + if op == "+": + vec_out.ndarray[:] = vec_in1.ndarray + vec_in2.ndarray + elif op == "-": + vec_out.ndarray[:] = vec_in1.ndarray - vec_in2.ndarray + else: + raise NotImplementedError() + + @staticmethod + def Wait(): # pylint: disable=invalid-name + pass + + @staticmethod + def Sort_By_Key(keys, values): # pylint: disable=invalid-name + values.ndarray[:] = values.ndarray[np.argsort(keys.ndarray)] + # TODO #328 Thrust sorts keys as well + + @staticmethod + def Set_Kernel_Debug(_): # pylint: disable=invalid-name + pass + + @staticmethod + def Set_Verbose(_): # pylint: disable=invalid-name + pass + + @staticmethod + def Get_PTX_Arch(): # pylint: disable=invalid-name + return 666 diff --git a/PySDM/source/PySDM/backends/impl_thrust_rtc/test_helpers/flag.py b/PySDM/source/PySDM/backends/impl_thrust_rtc/test_helpers/flag.py new file mode 100644 index 0000000000000000000000000000000000000000..3701b05c84e5fed405b28cd5effe7e5ede73d08f --- /dev/null +++ b/PySDM/source/PySDM/backends/impl_thrust_rtc/test_helpers/flag.py @@ -0,0 +1,6 @@ +""" +flag enabling `PySDM.backends.impl_thrust_rtc.test_helpers.fake_thrust_rtc.FakeThrustRTC` + (for tests of GPU code on machines with no GPU) +""" + +fakeThrustRTC = False diff --git a/PySDM/source/PySDM/backends/numba.py b/PySDM/source/PySDM/backends/numba.py new file mode 100644 index 0000000000000000000000000000000000000000..b337862bc6113d6ae0ca97076e8a93c2d566aad6 --- /dev/null +++ b/PySDM/source/PySDM/backends/numba.py @@ -0,0 +1,108 @@ +""" +Multi-threaded CPU backend using LLVM-powered just-in-time compilation +""" + +import os +import platform +import warnings + +import numba +from numba import prange +import numpy as np + +from PySDM.backends.impl_numba import methods +from PySDM.backends.impl_numba.random import Random as ImportedRandom +from PySDM.backends.impl_numba.storage import Storage as ImportedStorage +from PySDM.formulae import Formulae +from PySDM.backends.impl_numba.conf import JIT_FLAGS + + +class Numba( # pylint: disable=too-many-ancestors,duplicate-code + methods.CollisionsMethods, + methods.FragmentationMethods, + methods.PairMethods, + methods.IndexMethods, + methods.PhysicsMethods, + methods.CondensationMethods, + methods.ChemistryMethods, + methods.MomentsMethods, + methods.FreezingMethods, + methods.DisplacementMethods, + methods.TerminalVelocityMethods, + methods.IsotopeMethods, + methods.SeedingMethods, + methods.DepositionMethods, +): + Storage = ImportedStorage + Random = ImportedRandom + + default_croupier = "local" + + def __init__( + self, formulae=None, *, double_precision=True, override_jit_flags=None + ): + if not double_precision: + raise NotImplementedError() + self.formulae = formulae or Formulae() + self.formulae_flattened = self.formulae.flatten + + parallel_default = True + + if override_jit_flags is not None and "parallel" in override_jit_flags: + parallel_default = override_jit_flags["parallel"] + + if parallel_default: + if platform.machine() == "arm64": + if "CI" not in os.environ: + warnings.warn( + "Disabling Numba threading due to ARM64 CPU (atomics do not work yet)" + ) + parallel_default = False # TODO #1183 - atomics don't work on ARM64! + + try: + numba.parfors.parfor.ensure_parallel_support() + except numba.core.errors.UnsupportedParforsError: + if "CI" not in os.environ: + warnings.warn( + "Numba version used does not support parallel for (32 bits?)" + ) + parallel_default = False + + if not numba.config.DISABLE_JIT: # pylint: disable=no-member + + @numba.jit(parallel=True, nopython=True) + def fill_array_with_thread_id(arr): + """writes thread id to corresponding array element""" + for i in prange( # pylint: disable=not-an-iterable + numba.get_num_threads() + ): + arr[i] = numba.get_thread_id() + + fill_array_with_thread_id(arr := np.full(numba.get_num_threads(), -1)) + if not max(arr) == arr[-1] == numba.get_num_threads() - 1: + raise ValueError( + "Numba threading enabled but does not work" + " (try other setting of the NUMBA_THREADING_LAYER env var?)" + ) + + assert "fastmath" not in (override_jit_flags or {}) + self.default_jit_flags = { + **JIT_FLAGS, # here parallel=False (for out-of-backend code) + **{"fastmath": self.formulae.fastmath, "parallel": parallel_default}, + **(override_jit_flags or {}), + } + + methods.CollisionsMethods.__init__(self) + methods.FragmentationMethods.__init__(self) + methods.PairMethods.__init__(self) + methods.IndexMethods.__init__(self) + methods.PhysicsMethods.__init__(self) + methods.CondensationMethods.__init__(self) + methods.ChemistryMethods.__init__(self) + methods.MomentsMethods.__init__(self) + methods.FreezingMethods.__init__(self) + methods.DisplacementMethods.__init__(self) + methods.TerminalVelocityMethods.__init__(self) + methods.IsotopeMethods.__init__(self) + methods.SeedingMethods.__init__(self) + methods.DepositionMethods.__init__(self) diff --git a/PySDM/source/PySDM/backends/thrust_rtc.py b/PySDM/source/PySDM/backends/thrust_rtc.py new file mode 100644 index 0000000000000000000000000000000000000000..a85d7a79036e3ece2df70adf51b324f9d086e0ab --- /dev/null +++ b/PySDM/source/PySDM/backends/thrust_rtc.py @@ -0,0 +1,74 @@ +""" +GPU-resident backend using NVRTC runtime compilation library for CUDA +""" + +import os +import warnings + +import numpy as np + +from PySDM.backends.impl_thrust_rtc.conf import trtc +from PySDM.backends.impl_thrust_rtc.methods.collisions_methods import CollisionsMethods +from PySDM.backends.impl_thrust_rtc.methods.condensation_methods import ( + CondensationMethods, +) +from PySDM.backends.impl_thrust_rtc.methods.displacement_methods import ( + DisplacementMethods, +) +from PySDM.backends.impl_thrust_rtc.methods.freezing_methods import FreezingMethods +from PySDM.backends.impl_thrust_rtc.methods.index_methods import IndexMethods +from PySDM.backends.impl_thrust_rtc.methods.isotope_methods import IsotopeMethods +from PySDM.backends.impl_thrust_rtc.methods.moments_methods import MomentsMethods +from PySDM.backends.impl_thrust_rtc.methods.pair_methods import PairMethods +from PySDM.backends.impl_thrust_rtc.methods.physics_methods import PhysicsMethods +from PySDM.backends.impl_thrust_rtc.methods.terminal_velocity_methods import ( + TerminalVelocityMethods, +) +from PySDM.backends.impl_thrust_rtc.random import Random as ImportedRandom +from PySDM.backends.impl_thrust_rtc.storage import make_storage_class +from PySDM.formulae import Formulae + + +class ThrustRTC( # pylint: disable=duplicate-code,too-many-ancestors + CollisionsMethods, + PairMethods, + IndexMethods, + PhysicsMethods, + CondensationMethods, + MomentsMethods, + DisplacementMethods, + TerminalVelocityMethods, + FreezingMethods, + IsotopeMethods, +): + ENABLE = True + Random = ImportedRandom + + default_croupier = "global" + + def __init__( + self, formulae=None, *, double_precision=False, debug=False, verbose=False + ): + self.formulae = formulae or Formulae() + + self._conv_function = trtc.DVDouble if double_precision else trtc.DVFloat + self._real_type = "double" if double_precision else "float" + self._np_dtype = np.float64 if double_precision else np.float32 + + self.Storage = make_storage_class(self) + + CollisionsMethods.__init__(self) + PairMethods.__init__(self) + IndexMethods.__init__(self) + PhysicsMethods.__init__(self) + CondensationMethods.__init__(self) + MomentsMethods.__init__(self, double_precision=double_precision) + DisplacementMethods.__init__(self) + TerminalVelocityMethods.__init__(self) + FreezingMethods.__init__(self) + + trtc.Set_Kernel_Debug(debug) + trtc.Set_Verbose(verbose) + + if not ThrustRTC.ENABLE and "CI" not in os.environ: + warnings.warn("CUDA is not available, using FakeThrustRTC!") diff --git a/PySDM/source/PySDM/builder.py b/PySDM/source/PySDM/builder.py new file mode 100644 index 0000000000000000000000000000000000000000..fc7467b0bcd77b644636b343e1a1f809c51627ad --- /dev/null +++ b/PySDM/source/PySDM/builder.py @@ -0,0 +1,135 @@ +""" +The Builder class handling creation of `PySDM.particulator.Particulator` instances +""" + +import inspect + +import numpy as np + +from PySDM.attributes.impl.attribute_registry import get_attribute_class +from PySDM.impl.particle_attributes_factory import ParticleAttributesFactory +from PySDM.impl.wall_timer import WallTimer +from PySDM.initialisation.discretise_multiplicities import ( # TODO #324 + discretise_multiplicities, +) +from PySDM.particulator import Particulator +from PySDM.physics.particle_shape_and_density import LiquidSpheres, MixedPhaseSpheres + + +class Builder: + def __init__(self, n_sd, backend, environment=None): + assert not inspect.isclass(backend) + self.formulae = backend.formulae + self.particulator = Particulator(n_sd, backend) + self.req_attr_names = ["multiplicity", "water mass", "cell id"] + self.req_attr = None + self.aerosol_radius_threshold = 0 + self.condensation_params = None + self.particulator.environment = environment.instantiate(builder=self) + + def _set_condensation_parameters(self, **kwargs): + self.condensation_params = kwargs + + def add_dynamic(self, dynamic): + assert self.particulator.environment is not None + key = inspect.getmro(type(dynamic))[-2].__name__ + assert key not in self.particulator.dynamics + self.particulator.dynamics[key] = dynamic + + def _register_product(self, product, buffer): + if product.name in self.particulator.products: + raise ValueError(f'product name "{product.name}" already registered') + self.particulator.products[product.name] = product.instantiate( + builder=self, buffer=buffer + ) + + def _resolve_attribute(self, attr_name): + if attr_name not in self.req_attr: + self.req_attr[attr_name] = get_attribute_class( + attr_name, + self.particulator.dynamics.keys(), + self.formulae, + )(self) + assert self.req_attr is not None + + def get_attribute(self, attribute_name): + """intended for obtaining attribute instances during build() logic, + from within register() methods""" + self._resolve_attribute(attribute_name) + return self.req_attr[attribute_name] + + def request_attribute(self, attribute_name): + """can be called either before or during build()""" + if self.req_attr_names is not None: + self.req_attr_names.append(attribute_name) + else: + self._resolve_attribute(attribute_name) + + def build( + self, + attributes: dict, + products: tuple = (), + int_caster=discretise_multiplicities, + ): + assert self.particulator.environment is not None + + if "volume" in attributes and "water mass" not in attributes: + assert self.particulator.formulae.particle_shape_and_density.__name__ in ( + LiquidSpheres.__name__, + MixedPhaseSpheres.__name__, + ), "implied volume-to-mass conversion is only supported for spherical particles" + attributes["water mass"] = ( + self.particulator.formulae.particle_shape_and_density.volume_to_mass( + attributes.pop("volume") + ) + ) + self.request_attribute("volume") + + if ( + "water mass" in attributes + and "signed water mass" not in attributes + and not self.particulator.formulae.particle_shape_and_density.supports_mixed_phase() + ): + attributes["signed water mass"] = attributes.pop("water mass") + self.request_attribute("water mass") + + self.req_attr = {} + for attr_name in self.req_attr_names: + self._resolve_attribute(attr_name) + self.req_attr_names = None + + for key, dynamic in self.particulator.dynamics.items(): + self.particulator.dynamics[key] = dynamic.instantiate(builder=self) + + single_buffer_for_all_products = np.empty(self.particulator.mesh.grid) + for product in products: + self._register_product(product, single_buffer_for_all_products) + + for attribute in attributes: + self.request_attribute(attribute) + if "Condensation" in self.particulator.dynamics: + self.particulator.condensation_solver = ( + self.particulator.backend.make_condensation_solver( + self.particulator.dt, + self.particulator.mesh.n_cell, + **self.condensation_params, + ) + ) + attributes["multiplicity"] = int_caster(attributes["multiplicity"]) + if self.particulator.mesh.dimension == 0: + attributes["cell id"] = np.zeros_like( + attributes["multiplicity"], dtype=np.int64 + ) + self.particulator.attributes = ParticleAttributesFactory.attributes( + self.particulator, self.req_attr, attributes + ) + self.particulator.recalculate_cell_id() + + for key in self.particulator.dynamics: + self.particulator.timers[key] = WallTimer() + + if (attributes["multiplicity"] == 0).any(): + self.particulator.attributes.healthy = False + self.particulator.attributes.sanitize() + + return self.particulator diff --git a/PySDM/source/PySDM/dynamics/__init__.py b/PySDM/source/PySDM/dynamics/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d296bf47208fd562f9cb38b117e17797b0371eaa --- /dev/null +++ b/PySDM/source/PySDM/dynamics/__init__.py @@ -0,0 +1,19 @@ +""" +Classes representing physicochemical processes: +`PySDM.dynamics.collisions.collision.Collision`, +`PySDM.dynamics.condensation.Condensation`, ... +""" + +from PySDM.dynamics.isotopic_fractionation import IsotopicFractionation + +# isort: split +from PySDM.dynamics.ambient_thermodynamics import AmbientThermodynamics +from PySDM.dynamics.aqueous_chemistry import AqueousChemistry +from PySDM.dynamics.collisions import Breakup, Coalescence, Collision +from PySDM.dynamics.condensation import Condensation +from PySDM.dynamics.displacement import Displacement +from PySDM.dynamics.eulerian_advection import EulerianAdvection +from PySDM.dynamics.freezing import Freezing +from PySDM.dynamics.relaxed_velocity import RelaxedVelocity +from PySDM.dynamics.seeding import Seeding +from PySDM.dynamics.vapour_deposition_on_ice import VapourDepositionOnIce diff --git a/PySDM/source/PySDM/dynamics/ambient_thermodynamics.py b/PySDM/source/PySDM/dynamics/ambient_thermodynamics.py new file mode 100644 index 0000000000000000000000000000000000000000..e7ac002ea708b6ba251902a0fdd607de36ecdd7a --- /dev/null +++ b/PySDM/source/PySDM/dynamics/ambient_thermodynamics.py @@ -0,0 +1,17 @@ +""" +environment-sync triggering class +""" + +from PySDM.dynamics.impl import register_dynamic + + +@register_dynamic() +class AmbientThermodynamics: + def __init__(self): + self.particulator = None + + def register(self, builder): + self.particulator = builder.particulator + + def __call__(self): + self.particulator.environment.sync() diff --git a/PySDM/source/PySDM/dynamics/aqueous_chemistry.py b/PySDM/source/PySDM/dynamics/aqueous_chemistry.py new file mode 100644 index 0000000000000000000000000000000000000000..5d850951fcab7558b5fdc04a2137e84ae92df3a3 --- /dev/null +++ b/PySDM/source/PySDM/dynamics/aqueous_chemistry.py @@ -0,0 +1,129 @@ +""" +Hoppel-gap resolving aqueous-phase chemistry (incl. SO2 oxidation) +""" + +from collections import namedtuple + +import numpy as np + +from PySDM.dynamics.impl.chemistry_utils import ( + AQUEOUS_COMPOUNDS, + DIFFUSION_CONST, + GASEOUS_COMPOUNDS, + M, + SpecificGravities, +) +from PySDM.dynamics.impl import register_dynamic + +DEFAULTS = namedtuple("_", ("pH_min", "pH_max", "pH_rtol", "ionic_strength_threshold"))( + pH_min=-1.0, pH_max=14.0, pH_rtol=1e-6, ionic_strength_threshold=0.02 * M +) + + +@register_dynamic() +class AqueousChemistry: # pylint: disable=too-many-instance-attributes + def __init__( + self, + *, + environment_mole_fractions, + system_type, + n_substep, + dry_rho, + dry_molar_mass, + ionic_strength_threshold=DEFAULTS.ionic_strength_threshold, + pH_H_min=None, + pH_H_max=None, + pH_rtol=DEFAULTS.pH_rtol, + ): + self.environment_mole_fractions = environment_mole_fractions + self.environment_mixing_ratios = {} + self.particulator = None + + assert system_type in ("open", "closed") + self.system_type = system_type + assert isinstance(n_substep, int) and n_substep > 0 + self.n_substep = n_substep + self.dry_rho = dry_rho + self.dry_molar_mass = dry_molar_mass + self.ionic_strength_threshold = ionic_strength_threshold + self.pH_H_max = pH_H_max + self.pH_H_min = pH_H_min + self.pH_rtol = pH_rtol + + self.kinetic_consts = {} + self.equilibrium_consts = {} + self.dissociation_factors = {} + self.do_chemistry_flag = None + self.specific_gravities = None + + def register(self, builder): + self.particulator = builder.particulator + self.specific_gravities = SpecificGravities( + self.particulator.formulae.constants + ) + + for key, compound in GASEOUS_COMPOUNDS.items(): + shape = (1,) + self.environment_mixing_ratios[compound] = np.full( + shape, + self.particulator.formulae.trivia.mole_fraction_2_mixing_ratio( + self.environment_mole_fractions[compound], + self.specific_gravities[compound], + ), + ) + self.environment_mole_fractions = None + + if self.pH_H_max is None: + self.pH_H_max = self.particulator.formulae.trivia.pH2H(DEFAULTS.pH_min) + if self.pH_H_min is None: + self.pH_H_min = self.particulator.formulae.trivia.pH2H(DEFAULTS.pH_max) + + for key in AQUEOUS_COMPOUNDS: + builder.request_attribute("conc_" + key) + builder.request_attribute("pH") + + for key in self.particulator.backend.KINETIC_CONST.KINETIC_CONST: + self.kinetic_consts[key] = self.particulator.Storage.empty( + self.particulator.mesh.n_cell, dtype=float + ) + for key in self.particulator.backend.EQUILIBRIUM_CONST.EQUILIBRIUM_CONST: + self.equilibrium_consts[key] = self.particulator.Storage.empty( + self.particulator.mesh.n_cell, dtype=float + ) + for key in DIFFUSION_CONST: + self.dissociation_factors[key] = self.particulator.Storage.empty( + self.particulator.n_sd, dtype=float + ) + self.do_chemistry_flag = self.particulator.Storage.empty( + self.particulator.n_sd, dtype=bool + ) + + def __call__(self): + self.particulator.chem_recalculate_cell_data( + equilibrium_consts=self.equilibrium_consts, + kinetic_consts=self.kinetic_consts, + ) + for _ in range(self.n_substep): + self.particulator.chem_recalculate_drop_data( + equilibrium_consts=self.equilibrium_consts, + dissociation_factors=self.dissociation_factors, + ) + self.particulator.dissolution( + gaseous_compounds=GASEOUS_COMPOUNDS, + system_type=self.system_type, + dissociation_factors=self.dissociation_factors, + environment_mixing_ratios=self.environment_mixing_ratios, + timestep=self.particulator.dt / self.n_substep, + do_chemistry_flag=self.do_chemistry_flag, + ) + self.particulator.chem_recalculate_drop_data( + equilibrium_consts=self.equilibrium_consts, + dissociation_factors=self.dissociation_factors, + ) + self.particulator.oxidation( + kinetic_consts=self.kinetic_consts, + equilibrium_consts=self.equilibrium_consts, + dissociation_factors=self.dissociation_factors, + do_chemistry_flag=self.do_chemistry_flag, + timestep=self.particulator.dt / self.n_substep, + ) diff --git a/PySDM/source/PySDM/dynamics/collisions/__init__.py b/PySDM/source/PySDM/dynamics/collisions/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..76454659c9d9a8b16f00dc07eafda7a367cba649 --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/__init__.py @@ -0,0 +1,12 @@ +""" +collisions-related logic including the `PySDM.dynamics.collisions.collision.Collision` +dynamic and coalescence. +Includes collision kernels, ``PySDM.dynamics.collisions.collision_kernels`, +as well as coalescence efficiencies, `PySDM.dynamics.collisions.coalescence_efficiencies`, +and breakup efficiencies `PySDM.dynamics.collisions.breakup_efficiencies`, and +breakup fragmentations `PySDM.dynamics.collisions.breakup_fragmentations` +""" + +from PySDM.dynamics.collisions.collision import Breakup, Coalescence, Collision + +from . import collision_kernels diff --git a/PySDM/source/PySDM/dynamics/collisions/breakup_efficiencies/__init__.py b/PySDM/source/PySDM/dynamics/collisions/breakup_efficiencies/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d6bbc4a5faa594b3daf91c9b0c6605d32b2aab7a --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/breakup_efficiencies/__init__.py @@ -0,0 +1,5 @@ +""" +Breakup efficiencies +""" + +from .constEb import ConstEb diff --git a/PySDM/source/PySDM/dynamics/collisions/breakup_efficiencies/constEb.py b/PySDM/source/PySDM/dynamics/collisions/breakup_efficiencies/constEb.py new file mode 100644 index 0000000000000000000000000000000000000000..f40f98c9c1bb53123aa991705fa5460d9914e511 --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/breakup_efficiencies/constEb.py @@ -0,0 +1,15 @@ +""" +Specifies constant breakup efficiency. +""" + + +class ConstEb: + def __init__(self, Eb=1.0): + self.Eb = Eb + self.particulator = None + + def register(self, builder): + self.particulator = builder.particulator + + def __call__(self, output, is_first_in_pair): + output.fill(self.Eb) diff --git a/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/__init__.py b/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..020ffe8505928de72c491afcf5377932a7dceb59 --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/__init__.py @@ -0,0 +1,13 @@ +""" +TODO #744 +""" + +from .always_n import AlwaysN +from .constant_mass import ConstantMass +from .expon_frag import ExponFrag +from .exponential import Exponential +from .feingold1988 import Feingold1988 +from .gaussian import Gaussian +from .lowlist82 import LowList1982Nf +from .slams import SLAMS +from .straub2010 import Straub2010Nf diff --git a/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/always_n.py b/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/always_n.py new file mode 100644 index 0000000000000000000000000000000000000000..7c76a4148a6cc7b5b49ff9abc1fbe5d0b688915e --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/always_n.py @@ -0,0 +1,17 @@ +""" +Always produces N fragments in a given collisional breakup +""" + + +class AlwaysN: # pylint: disable=too-many-instance-attributes + def __init__(self, n): + self.particulator = None + self.N = n + + def __call__(self, nf, frag_mass, u01, is_first_in_pair): + nf.fill(self.N) + frag_mass.sum(self.particulator.attributes["water mass"], is_first_in_pair) + frag_mass /= self.N + + def register(self, builder): + self.particulator = builder.particulator diff --git a/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/constant_mass.py b/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/constant_mass.py new file mode 100644 index 0000000000000000000000000000000000000000..d7951d6c645c1a94fa6e852d6f181417da64a4fd --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/constant_mass.py @@ -0,0 +1,17 @@ +""" +Always produces fragments of mass c in a given collisional breakup +""" + + +class ConstantMass: # pylint: disable=too-many-instance-attributes + def __init__(self, c): + self.particulator = None + self.C = c + + def __call__(self, nf, frag_mass, u01, is_first_in_pair): + frag_mass[:] = self.C + nf.sum(self.particulator.attributes["water mass"], is_first_in_pair) + nf /= self.C + + def register(self, builder): + self.particulator = builder.particulator diff --git a/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/expon_frag.py b/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/expon_frag.py new file mode 100644 index 0000000000000000000000000000000000000000..e2111c57cff086b71274abce5fe285d445420591 --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/expon_frag.py @@ -0,0 +1,13 @@ +""" +DEPRECATED +P(x) = exp(-x / lambda); lambda specified in volume units +""" + +import warnings + +from .exponential import Exponential + + +class ExponFrag(Exponential): # pylint: disable=too-few-public-methods + def __init_subclass__(cls): + warnings.warn("Class has been renamed", DeprecationWarning) diff --git a/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/exponential.py b/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/exponential.py new file mode 100644 index 0000000000000000000000000000000000000000..b322f89eb9fc55af298bcf95342b35e6c5953443 --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/exponential.py @@ -0,0 +1,37 @@ +""" +P(x) = exp(-x / lambda); lambda specified in volume units +""" + +# TODO #796: introduce common code with Feingold fragmentation, including possible limiter +from .impl import VolumeBasedFragmentationFunction + + +class Exponential(VolumeBasedFragmentationFunction): + def __init__(self, scale, vmin=0.0, nfmax=None): + super().__init__() + self.scale = scale + self.vmin = vmin + self.nfmax = nfmax + self.sum_of_volumes = None + + def register(self, builder): + super().register(builder) + self.sum_of_volumes = self.particulator.PairwiseStorage.empty( + self.particulator.n_sd // 2, dtype=float + ) + + def compute_fragment_number_and_volumes( + self, nf, frag_volume, u01, is_first_in_pair + ): + self.sum_of_volumes.sum( + self.particulator.attributes["volume"], is_first_in_pair + ) + self.particulator.backend.exp_fragmentation( + n_fragment=nf, + scale=self.scale, + frag_volume=frag_volume, + x_plus_y=self.sum_of_volumes, + rand=u01, + vmin=self.vmin, + nfmax=self.nfmax, + ) diff --git a/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/feingold1988.py b/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/feingold1988.py new file mode 100644 index 0000000000000000000000000000000000000000..30d6a89423c96e4351f058b253e30e218e261ce3 --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/feingold1988.py @@ -0,0 +1,42 @@ +""" +P(m; x, y) = nu^2 * (x+y) exp(-m * nu) +nu = 1/m* where m* is a scaling factor for fragment volume dist. +see [Feingold et al. 1999](https://doi.org/10.1175/1520-0469(1999)056%3C4100:TIOGCC%3E2.0.CO;2) +""" + +from .impl import VolumeBasedFragmentationFunction + + +class Feingold1988( + VolumeBasedFragmentationFunction +): # pylint: disable=too-many-instance-attributes + def __init__(self, scale, fragtol=1e-3, vmin=0.0, nfmax=None): + super().__init__() + self.scale = scale + self.fragtol = fragtol + self.vmin = vmin + self.nfmax = nfmax + self.sum_of_volumes = None + + def register(self, builder): + super().register(builder) + self.sum_of_volumes = self.particulator.PairwiseStorage.empty( + self.particulator.n_sd // 2, dtype=float + ) + + def compute_fragment_number_and_volumes( + self, nf, frag_volume, u01, is_first_in_pair + ): + self.sum_of_volumes.sum( + self.particulator.attributes["volume"], is_first_in_pair + ) + self.particulator.backend.feingold1988_fragmentation( + n_fragment=nf, + scale=self.scale, + frag_volume=frag_volume, + x_plus_y=self.sum_of_volumes, + rand=u01, + fragtol=self.fragtol, + vmin=self.vmin, + nfmax=self.nfmax, + ) diff --git a/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/gaussian.py b/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/gaussian.py new file mode 100644 index 0000000000000000000000000000000000000000..b2b416bcbdc8215ec3f15fedb4aebe921eac92ba --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/gaussian.py @@ -0,0 +1,40 @@ +""" +P(x) = exp(-(x-mu)^2 / 2 sigma^2); mu and sigma are volumes +""" + +from .impl import VolumeBasedFragmentationFunction + + +class Gaussian( + VolumeBasedFragmentationFunction +): # pylint: disable=too-many-instance-attributes + def __init__(self, mu, sigma, vmin=0.0, nfmax=None): + super().__init__() + self.mu = mu + self.sigma = sigma + self.vmin = vmin + self.nfmax = nfmax + self.sum_of_volumes = None + + def register(self, builder): + super().register(builder) + self.sum_of_volumes = self.particulator.PairwiseStorage.empty( + self.particulator.n_sd // 2, dtype=float + ) + + def compute_fragment_number_and_volumes( + self, nf, frag_volume, u01, is_first_in_pair + ): + self.sum_of_volumes.sum( + self.particulator.attributes["volume"], is_first_in_pair + ) + self.particulator.backend.gauss_fragmentation( + n_fragment=nf, + mu=self.mu, + sigma=self.sigma, + frag_volume=frag_volume, + x_plus_y=self.sum_of_volumes, + rand=u01, + vmin=self.vmin, + nfmax=self.nfmax, + ) diff --git a/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/impl/__init__.py b/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/impl/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..8e5c97a1586828bbb1c5dae7e7ff08921ae17c63 --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/impl/__init__.py @@ -0,0 +1,5 @@ +""" +Abstractions and common code for fragmentation functions +""" + +from .volume_based import VolumeBasedFragmentationFunction diff --git a/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/impl/volume_based.py b/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/impl/volume_based.py new file mode 100644 index 0000000000000000000000000000000000000000..62bcb8c0839f7088803d9fb0cbe149e8ce5c5ebd --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/impl/volume_based.py @@ -0,0 +1,26 @@ +""" +Base class for volume-based fragmentation functions +""" + + +class VolumeBasedFragmentationFunction: + def __init__(self): + self.particulator = None + + def __call__(self, nf, frag_mass, u01, is_first_in_pair): + frag_volume_aliased_to_mass = frag_mass + self.compute_fragment_number_and_volumes( + nf, frag_volume_aliased_to_mass, u01, is_first_in_pair + ) + self.particulator.backend.mass_of_water_volume( + frag_mass, frag_volume_aliased_to_mass + ) + + def compute_fragment_number_and_volumes( + self, nf, frag_volume, u01, is_first_in_pair + ): + raise NotImplementedError() + + def register(self, builder): + self.particulator = builder.particulator + builder.request_attribute("volume") diff --git a/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/lowlist82.py b/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/lowlist82.py new file mode 100644 index 0000000000000000000000000000000000000000..26fd76f7a6fdc48ff60ba79e72f143bd833efb25 --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/lowlist82.py @@ -0,0 +1,105 @@ +""" +See [Low & List 1982](https://doi.org/10.1175/1520-0469(1982)039%3C1607:CCABOR%3E2.0.CO;2) +""" + +from .impl import VolumeBasedFragmentationFunction + + +class LowList1982Nf(VolumeBasedFragmentationFunction): + # pylint: disable=too-many-instance-attributes + def __init__(self, vmin=0.0, nfmax=None): + super().__init__() + self.vmin = vmin + self.nfmax = nfmax + self.arrays = {} + self.ll82_tmp = {} + self.sum_of_volumes = None + self.const = None + + def register(self, builder): + super().register(builder) + self.sum_of_volumes = self.particulator.PairwiseStorage.empty( + self.particulator.n_sd // 2, dtype=float + ) + self.const = self.particulator.formulae.constants + builder.request_attribute("radius") + builder.request_attribute("relative fall velocity") + for key in ("Sc", "St", "tmp", "tmp2", "CKE", "We", "W2", "ds", "dl", "dcoal"): + self.arrays[key] = self.particulator.PairwiseStorage.empty( + self.particulator.n_sd // 2, dtype=float + ) + for key in ("Rf", "Rs", "Rd"): + self.ll82_tmp[key] = self.particulator.PairwiseStorage.empty( + self.particulator.n_sd // 2, dtype=float + ) + + def compute_fragment_number_and_volumes( + self, nf, frag_volume, u01, is_first_in_pair + ): + self.sum_of_volumes.sum( + self.particulator.attributes["volume"], is_first_in_pair + ) + self.arrays["ds"].min(self.particulator.attributes["radius"], is_first_in_pair) + self.arrays["ds"] *= 2 + self.arrays["dl"].max(self.particulator.attributes["radius"], is_first_in_pair) + self.arrays["dl"] *= 2 + self.arrays["dcoal"].sum( + self.particulator.attributes["volume"], is_first_in_pair + ) + + self.arrays["dcoal"] /= self.const.PI / 6 + self.arrays["dcoal"] **= 1 / 3 + + # compute the surface energy, CKE, & dimensionless numbers + self.arrays["Sc"].sum(self.particulator.attributes["volume"], is_first_in_pair) + self.arrays["Sc"] **= 2 / 3 + self.arrays["Sc"] *= ( + self.const.PI * self.const.sgm_w * (6 / self.const.PI) ** (2 / 3) + ) + self.arrays["St"].min(self.particulator.attributes["radius"], is_first_in_pair) + self.arrays["St"] *= 2 + self.arrays["St"] **= 2 + self.arrays["tmp"].max(self.particulator.attributes["radius"], is_first_in_pair) + self.arrays["tmp"] *= 2 + self.arrays["tmp"] **= 2 + self.arrays["St"] += self.arrays["tmp"] + self.arrays["St"] *= self.const.PI * self.const.sgm_w + + self.arrays["tmp"].sum(self.particulator.attributes["volume"], is_first_in_pair) + self.arrays["tmp2"].distance( + self.particulator.attributes["relative fall velocity"], is_first_in_pair + ) + self.arrays["tmp2"] **= 2 + self.arrays["CKE"].multiply( + self.particulator.attributes["volume"], is_first_in_pair + ) + self.arrays["CKE"].divide_if_not_zero(self.arrays["tmp"]) + self.arrays["CKE"] *= self.arrays["tmp2"] + self.arrays["CKE"] *= self.const.rho_w / 2 + + self.arrays["We"].fill(self.arrays["CKE"]) + self.arrays["W2"].fill(self.arrays["CKE"]) + self.arrays["We"].divide_if_not_zero(self.arrays["Sc"]) + self.arrays["W2"].divide_if_not_zero(self.arrays["St"]) + + for key in ("Rf", "Rs", "Rd"): + self.ll82_tmp[key] *= 0.0 + + self.particulator.backend.ll82_fragmentation( + n_fragment=nf, + CKE=self.arrays["CKE"], + W=self.arrays["We"], + W2=self.arrays["W2"], + St=self.arrays["St"], + ds=self.arrays["ds"], + dl=self.arrays["dl"], + dcoal=self.arrays["dcoal"], + frag_volume=frag_volume, + x_plus_y=self.sum_of_volumes, + rand=u01, + vmin=self.vmin, + nfmax=self.nfmax, + Rf=self.ll82_tmp["Rf"], + Rs=self.ll82_tmp["Rs"], + Rd=self.ll82_tmp["Rd"], + ) diff --git a/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/slams.py b/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/slams.py new file mode 100644 index 0000000000000000000000000000000000000000..ea4f039d495717011293e3f8e30f0180e022f645 --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/slams.py @@ -0,0 +1,40 @@ +""" +Based on [Jokulsdottir & Archer 2016 (GMD)](https://doi.org/10.5194/gmd-9-1455-2016) +for ocean particles +""" + +from .impl import VolumeBasedFragmentationFunction + + +class SLAMS(VolumeBasedFragmentationFunction): + def __init__(self, vmin=0.0, nfmax=None): + super().__init__() + self.p_vec = None + self.sum_of_volumes = None + self.vmin = vmin + self.nfmax = nfmax + + def compute_fragment_number_and_volumes( + self, nf, frag_volume, u01, is_first_in_pair + ): + self.sum_of_volumes.sum( + self.particulator.attributes["volume"], is_first_in_pair + ) + self.particulator.backend.slams_fragmentation( + n_fragment=nf, + frag_volume=frag_volume, + x_plus_y=self.sum_of_volumes, + probs=self.p_vec, + rand=u01, + vmin=self.vmin, + nfmax=self.nfmax, + ) + + def register(self, builder): + super().register(builder) + self.p_vec = self.particulator.PairwiseStorage.empty( + self.particulator.n_sd // 2, dtype=float + ) + self.sum_of_volumes = self.particulator.PairwiseStorage.empty( + self.particulator.n_sd // 2, dtype=float + ) diff --git a/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/straub2010.py b/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/straub2010.py new file mode 100644 index 0000000000000000000000000000000000000000..76bdc99bfabc5e02095b543e2c7a027e5d7e907f --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/breakup_fragmentations/straub2010.py @@ -0,0 +1,101 @@ +""" +See [Straub et al. 2010](https://doi.org/10.1175/2009JAS3175.1) +""" + +from PySDM.physics.constants import si + +from .impl import VolumeBasedFragmentationFunction + + +class Straub2010Nf(VolumeBasedFragmentationFunction): + # pylint: disable=too-many-instance-attributes + def __init__(self, vmin=0.0, nfmax=None): + super().__init__() + self.vmin = vmin + self.nfmax = nfmax + self.arrays = {} + self.straub_tmp = {} + self.max_size = None + self.sum_of_volumes = None + self.const = None + + def register(self, builder): + super().register(builder) + self.max_size = self.particulator.PairwiseStorage.empty( + self.particulator.n_sd // 2, dtype=float + ) + self.sum_of_volumes = self.particulator.PairwiseStorage.empty( + self.particulator.n_sd // 2, dtype=float + ) + self.const = self.particulator.formulae.constants + builder.request_attribute("radius") + builder.request_attribute("relative fall velocity") + for key in ("Sc", "tmp", "tmp2", "CKE", "We", "gam", "CW", "ds"): + self.arrays[key] = self.particulator.PairwiseStorage.empty( + self.particulator.n_sd // 2, dtype=float + ) + for key in ("Nr1", "Nr2", "Nr3", "Nr4", "Nrt", "d34"): + self.straub_tmp[key] = self.particulator.PairwiseStorage.empty( + self.particulator.n_sd // 2, dtype=float + ) + + def compute_fragment_number_and_volumes( + self, nf, frag_volume, u01, is_first_in_pair + ): + self.max_size.max(self.particulator.attributes["volume"], is_first_in_pair) + self.sum_of_volumes.sum( + self.particulator.attributes["volume"], is_first_in_pair + ) + self.arrays["ds"].min(self.particulator.attributes["radius"], is_first_in_pair) + self.arrays["ds"] *= 2 + + # compute the dimensionless numbers and CW=CKE * We + self.arrays["tmp"].sum(self.particulator.attributes["volume"], is_first_in_pair) + self.arrays["Sc"].fill(self.arrays["tmp"]) + self.arrays["Sc"] **= 2 / 3 + self.arrays["Sc"] *= ( + self.const.PI * self.const.sgm_w * (6 / self.const.PI) ** (2 / 3) + ) + self.arrays["tmp2"].distance( + self.particulator.attributes["relative fall velocity"], is_first_in_pair + ) + self.arrays["tmp2"] **= 2 + self.arrays["CKE"].multiply( + self.particulator.attributes["volume"], is_first_in_pair + ) + self.arrays["CKE"].divide_if_not_zero(self.arrays["tmp"]) + self.arrays["CKE"] *= self.arrays["tmp2"] + self.arrays["CKE"] *= self.const.rho_w / 2 + + self.arrays["We"].fill(self.arrays["CKE"]) + self.arrays["We"].divide_if_not_zero(self.arrays["Sc"]) + + self.arrays["CW"].fill(self.arrays["We"]) + self.arrays["CW"] *= self.arrays["CKE"] + self.arrays["CW"] /= si.uJ + + self.arrays["gam"].max(self.particulator.attributes["radius"], is_first_in_pair) + self.arrays["tmp"].min(self.particulator.attributes["radius"], is_first_in_pair) + self.arrays["gam"].divide_if_not_zero(self.arrays["tmp"]) + + for key in ("Nr1", "Nr2", "Nr3", "Nr4", "Nrt"): + self.straub_tmp[key].fill(0) + + self.particulator.backend.straub_fragmentation( + n_fragment=nf, + CW=self.arrays["CW"], + gam=self.arrays["gam"], + ds=self.arrays["ds"], + frag_volume=frag_volume, + v_max=self.max_size, + x_plus_y=self.sum_of_volumes, + rand=u01, + vmin=self.vmin, + nfmax=self.nfmax, + Nr1=self.straub_tmp["Nr1"], + Nr2=self.straub_tmp["Nr2"], + Nr3=self.straub_tmp["Nr3"], + Nr4=self.straub_tmp["Nr4"], + Nrt=self.straub_tmp["Nrt"], + d34=self.straub_tmp["d34"], + ) diff --git a/PySDM/source/PySDM/dynamics/collisions/coalescence_efficiencies/__init__.py b/PySDM/source/PySDM/dynamics/collisions/coalescence_efficiencies/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..c5cfc593d62953b2d3d4110d7ff8dc752c59430b --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/coalescence_efficiencies/__init__.py @@ -0,0 +1,9 @@ +""" +Coalescence efficiencies for the overall collision dynamic +""" + +from .berry1967 import Berry1967 +from .constEc import ConstEc +from .lowlist1982 import LowList1982Ec +from .specified_eff import SpecifiedEff +from .straub2010 import Straub2010Ec diff --git a/PySDM/source/PySDM/dynamics/collisions/coalescence_efficiencies/_gravitational.py b/PySDM/source/PySDM/dynamics/collisions/coalescence_efficiencies/_gravitational.py new file mode 100644 index 0000000000000000000000000000000000000000..254ef0b4321db1e21e7b5a7492e77f587c61594c --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/coalescence_efficiencies/_gravitational.py @@ -0,0 +1,17 @@ +""" +TODO #744 +""" + + +class Gravitational: # pylint: disable=too-few-public-methods + def __init__(self): + self.particulator = None + self.pair_tmp = None + + def register(self, builder): + self.particulator = builder.particulator + builder.request_attribute("radius") + builder.request_attribute("relative fall velocity") + self.pair_tmp = self.particulator.PairwiseStorage.empty( + self.particulator.n_sd // 2, dtype=float + ) diff --git a/PySDM/source/PySDM/dynamics/collisions/coalescence_efficiencies/_parameterized.py b/PySDM/source/PySDM/dynamics/collisions/coalescence_efficiencies/_parameterized.py new file mode 100644 index 0000000000000000000000000000000000000000..7bd80b2d14fda44aa1621fd5d96cf84748aa7c7b --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/coalescence_efficiencies/_parameterized.py @@ -0,0 +1,25 @@ +""" +TODO #744 +""" + +from PySDM.physics import constants as const + + +class Parameterized: + def __init__(self, params): + self.particulator = None + self.params = params + + def register(self, builder): + self.particulator = builder.particulator + builder.request_attribute("radius") + + def __call__(self, output, is_first_in_pair): + self.particulator.backend.linear_collection_efficiency( + params=self.params, + output=output, + radii=self.particulator.attributes["radius"], + is_first_in_pair=is_first_in_pair, + unit=const.si.um, + ) + output **= 2 diff --git a/PySDM/source/PySDM/dynamics/collisions/coalescence_efficiencies/berry1967.py b/PySDM/source/PySDM/dynamics/collisions/coalescence_efficiencies/berry1967.py new file mode 100644 index 0000000000000000000000000000000000000000..1e4b7c8b1c70a3259b2604eb600dc02869cb671d --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/coalescence_efficiencies/berry1967.py @@ -0,0 +1,11 @@ +""" +[Berry 1967](https://doi.org/10.1175/1520-0469(1967)024%3C0688:CDGBC%3E2.0.CO;2) +Cloud Droplet Growth by Collection +""" + +from ._parameterized import Parameterized + + +class Berry1967(Parameterized): # pylint: disable=too-few-public-methods + def __init__(self): + super().__init__((1, 1, -27, 1.65, -58, 1.9, 15, 1.13, 16.7, 1, 0.004, 4, 8)) diff --git a/PySDM/source/PySDM/dynamics/collisions/coalescence_efficiencies/constEc.py b/PySDM/source/PySDM/dynamics/collisions/coalescence_efficiencies/constEc.py new file mode 100644 index 0000000000000000000000000000000000000000..393e9647d2379da883a6a7bfe06dccef2936bebf --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/coalescence_efficiencies/constEc.py @@ -0,0 +1,13 @@ +"""constant value""" + + +class ConstEc: + def __init__(self, Ec=1.0): + self.Ec = Ec + self.particulator = None + + def register(self, builder): + self.particulator = builder.particulator + + def __call__(self, output, is_first_in_pair): + output.fill(self.Ec) diff --git a/PySDM/source/PySDM/dynamics/collisions/coalescence_efficiencies/lowlist1982.py b/PySDM/source/PySDM/dynamics/collisions/coalescence_efficiencies/lowlist1982.py new file mode 100644 index 0000000000000000000000000000000000000000..b484ae80643994dd9c0cba7923c14173910f368a --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/coalescence_efficiencies/lowlist1982.py @@ -0,0 +1,100 @@ +""" +See [Low & List 1982](https://doi.org/10.1175/1520-0469(1982)039%3C1607:CCABOR%3E2.0.CO;2) +""" + +import numpy as np + +from PySDM.physics.constants import si + + +class LowList1982Ec: + # pylint: disable=too-many-instance-attributes + def __init__(self): + self.particulator = None + self.arrays = {} + self.ll82_tmp = {} + self.max_size = None + self.sum_of_masses = None + self.const = None + + def register(self, builder): + self.particulator = builder.particulator + self.max_size = self.particulator.PairwiseStorage.empty( + self.particulator.n_sd // 2, dtype=float + ) + self.sum_of_masses = self.particulator.PairwiseStorage.empty( + self.particulator.n_sd // 2, dtype=float + ) + self.const = self.particulator.formulae.constants + builder.request_attribute("radius") + builder.request_attribute("water mass") + builder.request_attribute("relative fall velocity") + for key in ("Sc", "St", "dS", "tmp", "tmp2", "CKE", "Et", "ds", "dl"): + self.arrays[key] = self.particulator.PairwiseStorage.empty( + self.particulator.n_sd // 2, dtype=float + ) + + def __call__(self, output, is_first_in_pair): + self.max_size.max(self.particulator.attributes["water mass"], is_first_in_pair) + self.sum_of_masses.sum( + self.particulator.attributes["water mass"], is_first_in_pair + ) + self.arrays["ds"].min(self.particulator.attributes["radius"], is_first_in_pair) + self.arrays["ds"] *= 2 + self.arrays["dl"].max(self.particulator.attributes["radius"], is_first_in_pair) + self.arrays["dl"] *= 2 + + # compute the surface energy, CKE + self.arrays["Sc"].sum( + self.particulator.attributes["water mass"], is_first_in_pair + ) + self.arrays["Sc"] **= 2 / 3 + self.arrays["Sc"] *= ( + self.const.PI * self.const.sgm_w * (6 / self.const.PI) ** (2 / 3) + ) + self.arrays["St"].min(self.particulator.attributes["radius"], is_first_in_pair) + self.arrays["St"] *= 2 + self.arrays["St"] **= 2 + self.arrays["tmp"].max(self.particulator.attributes["radius"], is_first_in_pair) + self.arrays["tmp"] *= 2 + self.arrays["tmp"] **= 2 + self.arrays["St"] += self.arrays["tmp"] + self.arrays["St"] *= self.const.PI * self.const.sgm_w + self.arrays["dS"].fill(self.arrays["St"]) + self.arrays["dS"] -= self.arrays["Sc"] + + self.arrays["tmp"].sum( + self.particulator.attributes["water mass"], is_first_in_pair + ) + self.arrays["tmp2"].distance( + self.particulator.attributes["relative fall velocity"], is_first_in_pair + ) + self.arrays["tmp2"] **= 2 + self.arrays["CKE"].multiply( + self.particulator.attributes["water mass"], is_first_in_pair + ) + self.arrays["CKE"].divide_if_not_zero(self.arrays["tmp"]) + self.arrays["CKE"] *= self.arrays["tmp2"] + self.arrays["CKE"] *= self.const.rho_w / 2 + + self.arrays["Et"].fill(self.arrays["CKE"]) + self.arrays["Et"] += self.arrays["dS"] + + a = 0.778 + b = 2.61e6 / si.J**2 * si.m**2 + + self.arrays["tmp2"].fill(self.arrays["Et"]) + self.arrays["tmp2"] **= 2 + self.arrays["tmp2"] *= -1.0 * b * self.const.sgm_w + self.arrays["tmp2"] /= self.arrays["Sc"] + + output.fill(self.arrays["ds"]) + output /= self.arrays["dl"] + output += 1.0 + output **= -2.0 + output *= a + output *= np.exp(self.arrays["tmp2"]) + + self.particulator.backend.ll82_coalescence_check( + Ec=output, dl=self.arrays["dl"] + ) diff --git a/PySDM/source/PySDM/dynamics/collisions/coalescence_efficiencies/specified_eff.py b/PySDM/source/PySDM/dynamics/collisions/coalescence_efficiencies/specified_eff.py new file mode 100644 index 0000000000000000000000000000000000000000..c9b1ce471e96819421026e48fb77bd236b8909e6 --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/coalescence_efficiencies/specified_eff.py @@ -0,0 +1,28 @@ +""" +Taking the form of Berry 1967 +Cloud Droplet Growth by Collection +but with user-specified collection efficiency constants +""" + +from ._parameterized import Parameterized + + +class SpecifiedEff(Parameterized): # pylint: disable=too-few-public-methods + def __init__( + self, + *, + A=1, + B=1, + D1=-27, + D2=1.65, + E1=-58, + E2=1.9, + F1=15, + F2=1.13, + G1=16.7, + G2=1, + G3=0.004, + Mf=4, + Mg=8, + ): + super().__init__((A, B, D1, D2, E1, E2, F1, F2, G1, G2, G3, Mf, Mg)) diff --git a/PySDM/source/PySDM/dynamics/collisions/coalescence_efficiencies/straub2010.py b/PySDM/source/PySDM/dynamics/collisions/coalescence_efficiencies/straub2010.py new file mode 100644 index 0000000000000000000000000000000000000000..a9eab644e1662660440c1a75ecf5c12cefb2cad3 --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/coalescence_efficiencies/straub2010.py @@ -0,0 +1,51 @@ +""" +[Straub et al. 2010](https://doi.org/10.1175/2009JAS3175.1) +TODO #744 +""" + +import numpy as np + +# TODO #744: TEST + + +class Straub2010Ec: + def __init__(self): + self.particulator = None + self.pair_tmp = None + self.arrays = {} + self.const = None + + def register(self, builder): + self.particulator = builder.particulator + self.const = self.particulator.formulae.constants + builder.request_attribute("volume") + builder.request_attribute("relative fall velocity") + for key in ("Sc", "tmp", "tmp2", "We"): + self.arrays[key] = self.particulator.PairwiseStorage.empty( + self.particulator.n_sd // 2, dtype=float + ) + + def __call__(self, output, is_first_in_pair): + self.arrays["tmp"].sum(self.particulator.attributes["volume"], is_first_in_pair) + self.arrays["Sc"].fill(self.arrays["tmp"]) + self.arrays["Sc"] *= 6 / self.const.PI + self.arrays["tmp"] *= 2 + + self.arrays["tmp2"].distance( + self.particulator.attributes["relative fall velocity"], is_first_in_pair + ) + self.arrays["tmp2"] **= 2 + self.arrays["We"].multiply( + self.particulator.attributes["volume"], is_first_in_pair + ) + self.arrays["We"].divide_if_not_zero(self.arrays["tmp"]) + self.arrays["We"] *= self.arrays["tmp2"] + self.arrays["We"] *= self.const.rho_w + + self.arrays["Sc"] **= 2 / 3 + self.arrays["Sc"] *= self.const.PI * self.const.sgm_w + + self.arrays["We"].divide_if_not_zero(self.arrays["Sc"]) + self.arrays["We"] *= -1.15 + + output.fill(np.exp(self.arrays["We"])) diff --git a/PySDM/source/PySDM/dynamics/collisions/collision.py b/PySDM/source/PySDM/dynamics/collisions/collision.py new file mode 100644 index 0000000000000000000000000000000000000000..18b24d86377737c68bcd7e55487787c586f3d124 --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/collision.py @@ -0,0 +1,349 @@ +""" +General algorithm format: +1. Determine whether collision occurs +2. If collision occurs: + a. Determine whether coalescence, breakup, or bouncing occur + Ec = coalescence efficiency + Eb = collisional-breakup efficiency + 1 - Ec - Eb = bounce back to original fragments (subset of breakup) + b. Perform the relevant dynamic +""" + +import warnings +from collections import namedtuple + +import numpy as np + +from PySDM.attributes.impl import get_attribute_class +from PySDM.dynamics.collisions.breakup_efficiencies import ConstEb +from PySDM.dynamics.collisions.breakup_fragmentations import AlwaysN +from PySDM.dynamics.collisions.coalescence_efficiencies import ConstEc +from PySDM.dynamics.impl.random_generator_optimizer import RandomGeneratorOptimizer +from PySDM.dynamics.impl.random_generator_optimizer_nopair import ( + RandomGeneratorOptimizerNoPair, +) +from PySDM.physics import si +from PySDM.dynamics.impl import register_dynamic + +# pylint: disable=too-many-lines + +DEFAULTS = namedtuple( + "_", ("dt_coal_range", "adaptive", "substeps", "max_multiplicity") +)( + dt_coal_range=(0.1 * si.second, 100.0 * si.second), + adaptive=True, + substeps=1, + max_multiplicity=get_attribute_class("multiplicity").MAX_VALUE // int(2e5), +) + + +@register_dynamic() +class Collision: # pylint: disable=too-many-instance-attributes + def __init__( + self, + *, + collision_kernel, + coalescence_efficiency, + breakup_efficiency, + fragmentation_function, + croupier=None, + optimized_random=False, + substeps: int = DEFAULTS.substeps, + adaptive: bool = DEFAULTS.adaptive, + dt_coal_range=DEFAULTS.dt_coal_range, + enable_breakup: bool = True, + warn_overflows: bool = True, + ): + assert substeps == 1 or adaptive is False + + self.particulator = None + + self.enable = True + self.enable_breakup = enable_breakup + self.warn_overflows = warn_overflows + self.max_multiplicity = DEFAULTS.max_multiplicity + + self.collision_kernel = collision_kernel + self.compute_coalescence_efficiency = coalescence_efficiency + self.compute_breakup_efficiency = breakup_efficiency + self.compute_number_of_fragments = fragmentation_function + + self.rnd_opt_frag = None + self.rnd_opt_coll = None + self.rnd_opt_proc = None + self.optimised_random = None + + assert dt_coal_range[0] > 0 + self.croupier = croupier + self.optimized_random = optimized_random + self.__substeps = substeps + self.adaptive = adaptive + self.stats_n_substep = None + self.stats_dt_min = None + self.dt_coal_range = tuple(dt_coal_range) + + self.kernel_temp = None + self.n_fragment = None + self.fragment_mass = None + self.Ec_temp = None + self.Eb_temp = None + self.norm_factor_temp = None + self.gamma = None + self.is_first_in_pair = None + self.dt_left = None + + self.collision_rate = None + self.collision_rate_deficit = None + self.coalescence_rate = None + self.breakup_rate = None + self.breakup_rate_deficit = None + + def register(self, builder): + self.particulator = builder.particulator + rnd_args = { + "optimized_random": self.optimized_random, + "dt_min": self.dt_coal_range[0], + "seed": builder.formulae.seed, + } + self.rnd_opt_coll = RandomGeneratorOptimizer(**rnd_args) + if self.enable_breakup: + self.rnd_opt_proc = RandomGeneratorOptimizerNoPair(**rnd_args) + self.rnd_opt_frag = RandomGeneratorOptimizerNoPair(**rnd_args) + + if self.particulator.n_sd < 2: + raise ValueError("No one to collide with!") + if self.dt_coal_range[1] > self.particulator.dt: + self.dt_coal_range = (self.dt_coal_range[0], self.particulator.dt) + assert self.dt_coal_range[0] <= self.dt_coal_range[1] + + empty_args_pairwise = {"shape": self.particulator.n_sd // 2, "dtype": float} + empty_args_cellwise = {"shape": self.particulator.mesh.n_cell, "dtype": float} + self.kernel_temp = self.particulator.PairwiseStorage.empty( + **empty_args_pairwise + ) + self.norm_factor_temp = self.particulator.Storage.empty(**empty_args_cellwise) + + self.gamma = self.particulator.PairwiseStorage.empty(**empty_args_pairwise) + + self.is_first_in_pair = self.particulator.PairIndicator(self.particulator.n_sd) + self.dt_left = self.particulator.Storage.empty(**empty_args_cellwise) + + self.stats_n_substep = self.particulator.Storage.empty( + self.particulator.mesh.n_cell, dtype=int + ) + self.stats_n_substep[:] = 0 if self.adaptive else self.__substeps + self.stats_dt_min = self.particulator.Storage.empty(**empty_args_cellwise) + self.stats_dt_min[:] = np.nan + + self.rnd_opt_coll.register(builder) + self.collision_kernel.register(builder) + + if self.croupier is None: + self.croupier = self.particulator.backend.default_croupier + + counter_args = (np.zeros(self.particulator.mesh.n_cell, dtype=int),) + self.collision_rate = self.particulator.Storage.from_ndarray(*counter_args) + self.collision_rate_deficit = self.particulator.Storage.from_ndarray( + *counter_args + ) + self.coalescence_rate = self.particulator.Storage.from_ndarray(*counter_args) + + if self.enable_breakup: + self.n_fragment = self.particulator.PairwiseStorage.empty( + **empty_args_pairwise + ) + self.fragment_mass = self.particulator.PairwiseStorage.empty( + **empty_args_pairwise + ) + self.Ec_temp = self.particulator.PairwiseStorage.empty( + **empty_args_pairwise + ) + self.Eb_temp = self.particulator.PairwiseStorage.empty( + **empty_args_pairwise + ) + self.rnd_opt_proc.register(builder) + self.rnd_opt_frag.register(builder) + self.compute_coalescence_efficiency.register(builder) + self.compute_breakup_efficiency.register(builder) + self.compute_number_of_fragments.register(builder) + self.breakup_rate = self.particulator.Storage.from_ndarray(*counter_args) + self.breakup_rate_deficit = self.particulator.Storage.from_ndarray( + *counter_args + ) + + def __call__(self): + if self.enable: + if not self.adaptive: + for _ in range(self.__substeps): + self.step() + else: + self.dt_left[:] = self.particulator.dt + + while self.particulator.attributes.get_working_length() != 0: + self.particulator.attributes.cell_idx.sort_by_key(self.dt_left) + self.step() + self.particulator.attributes.cut_working_length( + self.particulator.adaptive_sdm_end(self.dt_left) + ) + + self.particulator.attributes.reset_working_length() + self.particulator.attributes.reset_cell_idx() + self.rnd_opt_coll.reset() + if self.enable_breakup: + self.rnd_opt_proc.reset() + self.rnd_opt_frag.reset() + + def step(self): + pairs_rand, rand = self.rnd_opt_coll.get_random_arrays() + + self.toss_candidate_pairs_and_sort_within_pair_by_multiplicity( + self.is_first_in_pair, pairs_rand + ) + + prob = self.gamma + self.compute_probabilities_of_collision(self.is_first_in_pair, out=prob) + + if self.enable_breakup: + proc_rand = self.rnd_opt_proc.get_random_arrays() + rand_frag = self.rnd_opt_frag.get_random_arrays() + self.compute_coalescence_efficiency(self.Ec_temp, self.is_first_in_pair) + self.compute_breakup_efficiency(self.Eb_temp, self.is_first_in_pair) + self.compute_number_of_fragments( + self.n_fragment, self.fragment_mass, rand_frag, self.is_first_in_pair + ) + else: + proc_rand = None + + self.compute_gamma( + prob=prob, rand=rand, is_first_in_pair=self.is_first_in_pair, out=self.gamma + ) + + self.particulator.collision_coalescence_breakup( + enable_breakup=self.enable_breakup, + gamma=self.gamma, + rand=proc_rand, + Ec=self.Ec_temp, + Eb=self.Eb_temp, + fragment_mass=self.fragment_mass, + coalescence_rate=self.coalescence_rate, + breakup_rate=self.breakup_rate, + breakup_rate_deficit=self.breakup_rate_deficit, + is_first_in_pair=self.is_first_in_pair, + warn_overflows=self.warn_overflows, + max_multiplicity=self.max_multiplicity, + ) + + def toss_candidate_pairs_and_sort_within_pair_by_multiplicity( + self, is_first_in_pair, u01 + ): + self.particulator.attributes.permutation(u01, self.croupier == "local") + is_first_in_pair.update( + self.particulator.attributes.cell_start, + self.particulator.attributes.cell_idx, + self.particulator.attributes["cell id"], + ) + self.particulator.sort_within_pair_by_attr( + is_first_in_pair, attr_name="multiplicity" + ) + + def compute_probabilities_of_collision(self, is_first_in_pair, out): + """eq. (20) in [Shima et al. 2009](https://doi.org/10.1002/qj.441)""" + self.collision_kernel(self.kernel_temp, is_first_in_pair) + out.max(self.particulator.attributes["multiplicity"], is_first_in_pair) + out *= self.kernel_temp + self.particulator.normalize(out, self.norm_factor_temp) + + def compute_n_fragment(self, n_fragment, u01, is_first_in_pair): + self.compute_number_of_fragments(n_fragment, u01, is_first_in_pair) + + def compute_gamma(self, prob, rand, is_first_in_pair, out): + """see sec. 5.1.3 point (3) in [Shima et al. 2009](https://doi.org/10.1002/qj.441) + note that in PySDM gamma also serves the purpose of disabling collisions + for droplets without a pair (i.e. odd number of particles within a grid cell) + """ + if self.adaptive: + self.particulator.backend.scale_prob_for_adaptive_sdm_gamma( + prob=prob, + multiplicity=self.particulator.attributes["multiplicity"], + cell_id=self.particulator.attributes["cell id"], + dt_left=self.dt_left, + dt=self.particulator.dt, + dt_range=self.dt_coal_range, + is_first_in_pair=is_first_in_pair, + stats_n_substep=self.stats_n_substep, + stats_dt_min=self.stats_dt_min, + ) + if self.stats_dt_min.amin() == self.dt_coal_range[0]: + warnings.warn("adaptive time-step reached dt_min") + else: + prob /= self.__substeps + + self.particulator.backend.compute_gamma( + prob=prob, + rand=rand, + multiplicity=self.particulator.attributes["multiplicity"], + cell_id=self.particulator.attributes["cell id"], + collision_rate_deficit=self.collision_rate_deficit, + collision_rate=self.collision_rate, + is_first_in_pair=is_first_in_pair, + out=out, + ) + + +@register_dynamic() +class Coalescence(Collision): + def __init__( + self, + *, + collision_kernel, + coalescence_efficiency=ConstEc(Ec=1), + croupier=None, + optimized_random=False, + substeps: int = DEFAULTS.substeps, + adaptive: bool = DEFAULTS.adaptive, + dt_coal_range=DEFAULTS.dt_coal_range, + ): + breakup_efficiency = ConstEb(Eb=0) + fragmentation_function = AlwaysN(n=1) + super().__init__( + collision_kernel=collision_kernel, + coalescence_efficiency=coalescence_efficiency, + breakup_efficiency=breakup_efficiency, + fragmentation_function=fragmentation_function, + croupier=croupier, + optimized_random=optimized_random, + substeps=substeps, + adaptive=adaptive, + dt_coal_range=dt_coal_range, + enable_breakup=False, + ) + + +@register_dynamic() +class Breakup(Collision): + def __init__( + self, + *, + collision_kernel, + fragmentation_function, + croupier=None, + optimized_random=False, + substeps: int = DEFAULTS.substeps, + adaptive: bool = DEFAULTS.adaptive, + dt_coal_range=DEFAULTS.dt_coal_range, + warn_overflows=True, + ): + coalescence_efficiency = ConstEc(Ec=0.0) + breakup_efficiency = ConstEb(Eb=1.0) + super().__init__( + collision_kernel=collision_kernel, + coalescence_efficiency=coalescence_efficiency, + breakup_efficiency=breakup_efficiency, + fragmentation_function=fragmentation_function, + croupier=croupier, + optimized_random=optimized_random, + substeps=substeps, + adaptive=adaptive, + dt_coal_range=dt_coal_range, + warn_overflows=warn_overflows, + ) diff --git a/PySDM/source/PySDM/dynamics/collisions/collision_kernels/__init__.py b/PySDM/source/PySDM/dynamics/collisions/collision_kernels/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..0e9bf2b809f75a24e9eb80a5c12ef6b568c36761 --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/collision_kernels/__init__.py @@ -0,0 +1,14 @@ +""" +Collision kernels including +[Golovin](https://open-atmos.github.io/PySDM/PySDM/dynamics/collisions/collision_kernels/golovin.html), +[Geometric](https://open-atmos.github.io/PySDM/PySDM/dynamics/collisions/collision_kernels/geometric.html) +and other... +""" # pylint: disable=line-too-long + +from .constantK import ConstantK +from .electric import Electric +from .geometric import Geometric +from .golovin import Golovin +from .hydrodynamic import Hydrodynamic +from .linear import Linear +from .simple_geometric import SimpleGeometric diff --git a/PySDM/source/PySDM/dynamics/collisions/collision_kernels/constantK.py b/PySDM/source/PySDM/dynamics/collisions/collision_kernels/constantK.py new file mode 100644 index 0000000000000000000000000000000000000000..d906be151a9f3a6e53f35f58d4ed46ca29c4d027 --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/collision_kernels/constantK.py @@ -0,0 +1,15 @@ +""" +#TODO #744 +""" + + +class ConstantK: + def __init__(self, a): + self.a = a + self.particulator = None + + def __call__(self, output, is_first_in_pair): + output.fill(self.a) + + def register(self, builder): + self.particulator = builder.particulator diff --git a/PySDM/source/PySDM/dynamics/collisions/collision_kernels/electric.py b/PySDM/source/PySDM/dynamics/collisions/collision_kernels/electric.py new file mode 100644 index 0000000000000000000000000000000000000000..731e2fa9b9dbbe830a3cc0815c88513e432c9674 --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/collision_kernels/electric.py @@ -0,0 +1,13 @@ +""" +kernel modelling influence of electric field of 3000V/cm + as in [Berry 1967](https://doi.org/10.1175/1520-0469(1967)024%3C0688:CDGBC%3E2.0.CO;2) +""" + +from PySDM.dynamics.collisions.collision_kernels.impl.parameterized import Parameterized + + +class Electric(Parameterized): # pylint: disable=too-few-public-methods + def __init__(self): + super().__init__( + (1, 1, -7, 1.78, -20.5, 1.73, 0.26, 1.47, 1, 0.82, -0.003, 4.4, 8), + ) diff --git a/PySDM/source/PySDM/dynamics/collisions/collision_kernels/geometric.py b/PySDM/source/PySDM/dynamics/collisions/collision_kernels/geometric.py new file mode 100644 index 0000000000000000000000000000000000000000..365cfa96b5c72d2ff810b2ce895bcdbf7c0a5c7f --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/collision_kernels/geometric.py @@ -0,0 +1,22 @@ +""" +basic geometric kernel +""" + +from PySDM.dynamics.collisions.collision_kernels.impl.gravitational import Gravitational +from PySDM.physics import constants as const + + +class Geometric(Gravitational): + def __init__(self, collection_efficiency=1.0, x="volume"): + super().__init__() + self.collection_efficiency = collection_efficiency + self.x = x + + def __call__(self, output, is_first_in_pair): + output.sum(self.particulator.attributes["radius"], is_first_in_pair) + output **= 2 + output *= const.PI * self.collection_efficiency + self.pair_tmp.distance( + self.particulator.attributes["relative fall velocity"], is_first_in_pair + ) + output *= self.pair_tmp diff --git a/PySDM/source/PySDM/dynamics/collisions/collision_kernels/golovin.py b/PySDM/source/PySDM/dynamics/collisions/collision_kernels/golovin.py new file mode 100644 index 0000000000000000000000000000000000000000..cbb801cf21e0a9d1460e9d55f9d78bf59bbf3b3a --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/collision_kernels/golovin.py @@ -0,0 +1,44 @@ +""" +Golovin kernel with analytic solution (see [Golovin 1963](http://mi.mathnet.ru/dan27630)) +""" + +import numpy as np +from scipy import special + + +class Golovin: + def __init__(self, b): + self.b = b + self.particulator = None + + def __call__(self, output, is_first_in_pair): + output.sum(self.particulator.attributes["volume"], is_first_in_pair) + output *= self.b + + def register(self, builder): + self.particulator = builder.particulator + builder.request_attribute("volume") + + def analytic_solution(self, x, t, x_0, N_0): + tau = 1 - np.exp(-N_0 * self.b * x_0 * t) + + if isinstance(x, np.ndarray): + func = np.vectorize( + lambda i: Golovin.analytic_solution_helper(x[int(i)], tau, x_0) + ) + result = np.fromfunction(func, x.shape, dtype=float) + return result + + return Golovin.analytic_solution_helper(x, tau, x_0) + + @staticmethod + def analytic_solution_helper(x, tau, x_0): + sqrt_tau = np.sqrt(tau) + result = float( + (1 - tau) + * 1 + / (x * np.sqrt(tau)) + * special.ive(1, 2 * x / x_0 * sqrt_tau) # pylint: disable=no-member + * np.exp(-(1 + tau - 2 * sqrt_tau) * x / x_0) + ) + return result diff --git a/PySDM/source/PySDM/dynamics/collisions/collision_kernels/hydrodynamic.py b/PySDM/source/PySDM/dynamics/collisions/collision_kernels/hydrodynamic.py new file mode 100644 index 0000000000000000000000000000000000000000..1e3d42731be5ce786df0b4720a8d59f599d1f6cd --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/collision_kernels/hydrodynamic.py @@ -0,0 +1,13 @@ +""" +hydrodynamic kernel using + [Berry 1967](https://doi.org/10.1175/1520-0469(1967)024%3C0688:CDGBC%3E2.0.CO;2) parameterization +""" + +from PySDM.dynamics.collisions.collision_kernels.impl.parameterized import Parameterized + + +class Hydrodynamic(Parameterized): # pylint: disable=too-few-public-methods + def __init__(self): + super().__init__( + (1, 1, -27, 1.65, -58, 1.9, 15, 1.13, 16.7, 1, 0.004, 4, 8), + ) diff --git a/PySDM/source/PySDM/dynamics/collisions/collision_kernels/impl/__init__.py b/PySDM/source/PySDM/dynamics/collisions/collision_kernels/impl/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..39107c49c132edb1eb03f68692dfad6c3b148376 --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/collision_kernels/impl/__init__.py @@ -0,0 +1 @@ +"""common code for implementing collision kernels""" diff --git a/PySDM/source/PySDM/dynamics/collisions/collision_kernels/impl/gravitational.py b/PySDM/source/PySDM/dynamics/collisions/collision_kernels/impl/gravitational.py new file mode 100644 index 0000000000000000000000000000000000000000..5021fcc95dcce293d4b00ce6df79dd8f049fcafc --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/collision_kernels/impl/gravitational.py @@ -0,0 +1,15 @@ +"""common parent class for gravitational collision kernels""" + + +class Gravitational: # pylint: disable=too-few-public-methods + def __init__(self): + self.particulator = None + self.pair_tmp = None + + def register(self, builder): + self.particulator = builder.particulator + builder.request_attribute("radius") + builder.request_attribute("relative fall velocity") + self.pair_tmp = self.particulator.PairwiseStorage.empty( + self.particulator.n_sd // 2, dtype=float + ) diff --git a/PySDM/source/PySDM/dynamics/collisions/collision_kernels/impl/parameterized.py b/PySDM/source/PySDM/dynamics/collisions/collision_kernels/impl/parameterized.py new file mode 100644 index 0000000000000000000000000000000000000000..eb1b940f9e814b0a881917e15b61f2a63c80afa7 --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/collision_kernels/impl/parameterized.py @@ -0,0 +1,30 @@ +"""common parent class for collision kernels specified using Berry's parameterization""" + +from PySDM.physics import constants as const + +from .gravitational import Gravitational + + +class Parameterized(Gravitational): + def __init__(self, params): + super().__init__() + self.params = params + + def __call__(self, output, is_first_in_pair): + self.particulator.backend.linear_collection_efficiency( + params=self.params, + output=output, + radii=self.particulator.attributes["radius"], + is_first_in_pair=is_first_in_pair, + unit=const.si.um, + ) + output **= 2 + output *= const.PI + self.pair_tmp.max(self.particulator.attributes["radius"], is_first_in_pair) + self.pair_tmp **= 2 + output *= self.pair_tmp + + self.pair_tmp.distance( + self.particulator.attributes["relative fall velocity"], is_first_in_pair + ) + output *= self.pair_tmp diff --git a/PySDM/source/PySDM/dynamics/collisions/collision_kernels/linear.py b/PySDM/source/PySDM/dynamics/collisions/collision_kernels/linear.py new file mode 100644 index 0000000000000000000000000000000000000000..a5e9d219a049ae397b97d251f989fcd5815a2a4b --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/collision_kernels/linear.py @@ -0,0 +1,19 @@ +""" +TODO #744 +""" + + +class Linear: + def __init__(self, a, b): + self.a = a + self.b = b + self.particulator = None + + def __call__(self, output, is_first_in_pair): + output.sum_pair(self.particulator.attributes["volume"], is_first_in_pair) + output *= self.b + output += self.a + + def register(self, builder): + self.particulator = builder.particulator + builder.request_attribute("volume") diff --git a/PySDM/source/PySDM/dynamics/collisions/collision_kernels/simple_geometric.py b/PySDM/source/PySDM/dynamics/collisions/collision_kernels/simple_geometric.py new file mode 100644 index 0000000000000000000000000000000000000000..005c61927b9136d9c4f7f49fec9ff8618b7ea231 --- /dev/null +++ b/PySDM/source/PySDM/dynamics/collisions/collision_kernels/simple_geometric.py @@ -0,0 +1,26 @@ +""" +basic geometric kernel (not taking fall velocity into account) +""" + + +class SimpleGeometric: + def __init__(self, C): + self.particulator = None + self.pair_tmp = None + self.C = C + + def register(self, builder): + self.particulator = builder.particulator + builder.request_attribute("radius") + builder.request_attribute("area") + self.pair_tmp = self.particulator.PairwiseStorage.empty( + self.particulator.n_sd // 2, dtype=float + ) + + def __call__(self, output, is_first_in_pair): + output[:] = self.C + self.pair_tmp.sum(self.particulator.attributes["radius"], is_first_in_pair) + self.pair_tmp **= 2 + output *= self.pair_tmp + self.pair_tmp.distance(self.particulator.attributes["area"], is_first_in_pair) + output *= self.pair_tmp diff --git a/PySDM/source/PySDM/dynamics/condensation.py b/PySDM/source/PySDM/dynamics/condensation.py new file mode 100644 index 0000000000000000000000000000000000000000..d6fed1e2412a99d8c01050963c69ac0b9aaca18d --- /dev/null +++ b/PySDM/source/PySDM/dynamics/condensation.py @@ -0,0 +1,132 @@ +""" +bespoke condensational growth solver +with implicit-in-particle-size integration and adaptive timestepping +as in [Bartman 2020 (MSc thesis, Section 3.3)](https://www.ap.uj.edu.pl/diplomas/attachments/file/download/125485) +""" # pylint: disable=line-too-long + +from collections import namedtuple + +import numpy as np + +from PySDM.physics import si +from PySDM.dynamics.impl import register_dynamic + +DEFAULTS = namedtuple("_", ("rtol_x", "rtol_thd", "cond_range", "schedule"))( + rtol_x=1e-6, + rtol_thd=1e-6, + cond_range=(1e-4 * si.second, 1 * si.second), + schedule="dynamic", +) + + +@register_dynamic() +class Condensation: # pylint: disable=too-many-instance-attributes + def __init__( + self, + *, + rtol_x=DEFAULTS.rtol_x, + rtol_thd=DEFAULTS.rtol_thd, + substeps: int = 1, + adaptive: bool = True, + dt_cond_range: tuple = DEFAULTS.cond_range, + schedule: str = DEFAULTS.schedule, + max_iters: int = 16, + update_thd: bool = True, + ): + if adaptive and substeps != 1: + raise ValueError( + "if specifying substeps count manually, adaptivity must be disabled" + ) + + self.particulator = None + self.enable = True + + self.rtol_x = rtol_x + self.rtol_thd = rtol_thd + + self.rh_max = None + self.success = None + + self.__substeps = substeps + self.adaptive = adaptive + self.counters = {} + self.dt_cond_range = dt_cond_range + self.schedule = schedule + self.max_iters = max_iters + + self.cell_order = None + + self.update_thd = update_thd + + def register(self, builder): + self.particulator = builder.particulator + + builder._set_condensation_parameters( + dt_range=self.dt_cond_range, + adaptive=self.adaptive, + fuse=32, + multiplier=2, + RH_rtol=1e-7, + max_iters=self.max_iters, + ) + builder.request_attribute("critical volume") + builder.request_attribute("kappa") + builder.request_attribute("dry volume organic fraction") + builder.request_attribute("Reynolds number") + + for counter in ("n_substeps", "n_activating", "n_deactivating", "n_ripening"): + self.counters[counter] = self.particulator.Storage.empty( + self.particulator.mesh.n_cell, dtype=int + ) + if counter == "n_substeps": + self.counters[counter][:] = self.__substeps if not self.adaptive else -1 + else: + self.counters[counter][:] = -1 + + self.rh_max = self.particulator.Storage.empty( + self.particulator.mesh.n_cell, dtype=float + ) + self.rh_max[:] = np.nan + self.success = self.particulator.Storage.empty( + self.particulator.mesh.n_cell, dtype=bool + ) + self.success[:] = False + self.cell_order = np.arange(self.particulator.mesh.n_cell) + + def __call__(self): + if self.enable: + if self.schedule == "dynamic": + self.cell_order = np.argsort(self.counters["n_substeps"]) + elif self.schedule == "static": + pass + else: + raise NotImplementedError() + + self.particulator.condensation( + rtol_x=self.rtol_x, + rtol_thd=self.rtol_thd, + counters=self.counters, + RH_max=self.rh_max, + success=self.success, + cell_order=self.cell_order, + ) + if not self.success.all(): + raise RuntimeError("Condensation failed") + if not self.update_thd: + self.particulator.environment.get_predicted("thd").ravel( + self.particulator.environment.get_thd() + ) + # note: this makes order of dynamics matter + # (e.g., condensation after chemistry or before) + self.particulator.update_TpRH() + + if self.adaptive: + self.counters["n_substeps"][:] = np.maximum( + self.counters["n_substeps"][:], + int(self.particulator.dt / self.dt_cond_range[1]), + ) + if self.dt_cond_range[0] != 0: + self.counters["n_substeps"][:] = np.minimum( + self.counters["n_substeps"][:], + int(self.particulator.dt / self.dt_cond_range[0]), + ) diff --git a/PySDM/source/PySDM/dynamics/displacement.py b/PySDM/source/PySDM/dynamics/displacement.py new file mode 100644 index 0000000000000000000000000000000000000000..f3e409f484acc64ad892392d4532ffe869027408 --- /dev/null +++ b/PySDM/source/PySDM/dynamics/displacement.py @@ -0,0 +1,153 @@ +""" +particle displacement due to advection by the flow & sedimentation + +adaptive time-stepping controlled by comparing implicit-Euler (I) +and explicit-Euler (E) maximal displacements with: +rtol > |(I - E) / E| +(see eqs 13-16 in [Arabas et al. 2015](https://doi.org/10.5194/gmd-8-1677-2015)) +""" + +from collections import namedtuple + +import numpy as np + +from PySDM.dynamics.impl import register_dynamic + +DEFAULTS = namedtuple("_", ("rtol", "adaptive"))(rtol=1e-2, adaptive=True) + + +@register_dynamic() +class Displacement: # pylint: disable=too-many-instance-attributes + def __init__( + self, + enable_sedimentation=False, + precipitation_counting_level_index: int = 0, + adaptive=DEFAULTS.adaptive, + rtol=DEFAULTS.rtol, + ): # pylint: disable=too-many-arguments + self.particulator = None + self.enable_sedimentation = enable_sedimentation + self.dimension = None + self.grid = None + self.courant = None + self.displacement = None + self.temp = None + self.precipitation_mass_in_last_step = 0 + self.precipitation_counting_level_index = precipitation_counting_level_index + + self.adaptive = adaptive + self.rtol = rtol + self._n_substeps = 1 + + def register(self, builder): + builder.request_attribute("relative fall velocity") + self.particulator = builder.particulator + self.dimension = len(builder.particulator.environment.mesh.grid) + self.grid = self.particulator.Storage.from_ndarray( + np.array(builder.particulator.environment.mesh.grid, dtype=np.int64) + ) + if self.dimension == 1: + courant_field = (np.full(self.grid[0] + 1, np.nan),) + elif self.dimension == 2: + courant_field = ( + np.full((self.grid[0] + 1, self.grid[1]), np.nan), + np.full((self.grid[0], self.grid[1] + 1), np.nan), + ) + elif self.dimension == 3: + courant_field = ( + np.full((self.grid[0] + 1, self.grid[1], self.grid[2]), np.nan), + np.full((self.grid[0], self.grid[1] + 1, self.grid[2]), np.nan), + np.full((self.grid[0], self.grid[1], self.grid[2] + 1), np.nan), + ) + else: + raise NotImplementedError() + self.courant = tuple( + self.particulator.Storage.from_ndarray(courant_field[i]) + for i in range(self.dimension) + ) + self.displacement = self.particulator.Storage.from_ndarray( + np.zeros((self.dimension, self.particulator.n_sd)) + ) + self.temp = self.particulator.Storage.from_ndarray( + np.zeros((self.dimension, self.particulator.n_sd), dtype=np.int64) + ) + + def upload_courant_field(self, courant_field): + for i, component in enumerate(courant_field): + self.courant[i].upload(component) + + # note: to be improved, should make n_substeps variable in space as in cond/coal + if self.adaptive: + error_estimate = self.rtol + self._n_substeps = 0.5 + while error_estimate >= self.rtol: + self._n_substeps = int(self._n_substeps * 2) + error_estimate = 0 + for i, courant_component in enumerate(courant_field): + max_abs_delta_courant = np.amax( + np.abs(np.diff(courant_component, axis=i)) + ) + max_abs_delta_courant /= self._n_substeps + error_estimate = max( + error_estimate, + ( + 0 + if max_abs_delta_courant == 0 + else 1 / (1 / max_abs_delta_courant - 1) + ), + ) + + def __call__(self): + # TIP: not need all array only [idx[:sd_num]] + cell_origin = self.particulator.attributes["cell origin"] + position_in_cell = self.particulator.attributes["position in cell"] + + self.precipitation_mass_in_last_step = 0.0 + for _ in range(self._n_substeps): + self.calculate_displacement( + self.displacement, self.courant, cell_origin, position_in_cell + ) + self.update_position(position_in_cell, self.displacement) + if self.enable_sedimentation: + self.precipitation_mass_in_last_step += self.particulator.remove_precipitated( + displacement=self.displacement, + precipitation_counting_level_index=self.precipitation_counting_level_index, + ) + self.particulator.flag_out_of_column() + self.update_cell_origin(cell_origin, position_in_cell) + self.boundary_condition(cell_origin) + self.particulator.recalculate_cell_id() + + for key in ("position in cell", "cell origin", "cell id"): + self.particulator.attributes.mark_updated(key) + + def calculate_displacement( + self, displacement, courant, cell_origin, position_in_cell + ): + self.particulator.calculate_displacement( + displacement=displacement, + courant=courant, + cell_origin=cell_origin, + position_in_cell=position_in_cell, + n_substeps=self._n_substeps, + ) + if self.enable_sedimentation: + displacement_z = displacement[self.dimension - 1, :] + dt = self.particulator.dt / self._n_substeps + dt_over_dz = dt / self.particulator.mesh.dz + displacement_z *= 1 / dt_over_dz + displacement_z -= self.particulator.attributes["relative fall velocity"] + displacement_z *= dt_over_dz + + @staticmethod + def update_position(position_in_cell, displacement): + position_in_cell += displacement + + def update_cell_origin(self, cell_origin, position_in_cell): + floor_of_position = self.temp + floor_of_position.floor(position_in_cell) + cell_origin += floor_of_position + position_in_cell -= floor_of_position + + def boundary_condition(self, cell_origin): + cell_origin %= self.grid diff --git a/PySDM/source/PySDM/dynamics/eulerian_advection.py b/PySDM/source/PySDM/dynamics/eulerian_advection.py new file mode 100644 index 0000000000000000000000000000000000000000..c131e6926c9610ac92220ea0d039a3c433a1c7e3 --- /dev/null +++ b/PySDM/source/PySDM/dynamics/eulerian_advection.py @@ -0,0 +1,22 @@ +""" +wrapper class for triggering integration in the Eulerian advection solver +""" + +from PySDM.dynamics.impl import register_dynamic + + +@register_dynamic() +class EulerianAdvection: + def __init__(self, solvers): + self.solvers = solvers + self.particulator = None + + def register(self, builder): + self.particulator = builder.particulator + + def __call__(self): + for field in ("water_vapour_mixing_ratio", "thd"): + self.particulator.environment.get_predicted(field).download( + getattr(self.particulator.environment, f"get_{field}")(), reshape=True + ) + self.solvers(self.particulator.dynamics["Displacement"]) diff --git a/PySDM/source/PySDM/dynamics/freezing.py b/PySDM/source/PySDM/dynamics/freezing.py new file mode 100644 index 0000000000000000000000000000000000000000..f48b87d2d2943357443e594ce5110984ae098f06 --- /dev/null +++ b/PySDM/source/PySDM/dynamics/freezing.py @@ -0,0 +1,104 @@ +""" +droplet freezing using either singular or +time-dependent formulation for immersion freezing +and homogeneous freezing and thaw +""" + +from typing import Optional +from PySDM.dynamics.impl import register_dynamic + + +@register_dynamic() +class Freezing: # pylint: disable=too-many-instance-attributes + def __init__( + self, + *, + homogeneous_freezing: Optional[str] = None, + immersion_freezing: Optional[str] = None, + thaw: Optional[str] = None, + ): + assert ( + homogeneous_freezing or immersion_freezing or thaw + ), "please enable one or more modes of operation" + assert immersion_freezing is None or immersion_freezing in ( + "time-dependent", + "singular", + ) + assert homogeneous_freezing is None or homogeneous_freezing in ( + "time-dependent", + "threshold", + ) + assert thaw is None or thaw == "instantaneous" + + self.homogeneous_freezing = homogeneous_freezing + self.immersion_freezing = immersion_freezing + self.thaw = thaw + self.enable = True + self.rand = None + self.rng = None + self.particulator = None + + def register(self, builder): + self.particulator = builder.particulator + + assert ( + self.particulator.formulae.particle_shape_and_density.supports_mixed_phase() + ) + + builder.request_attribute("signed water mass") + if self.immersion_freezing == "singular": + builder.request_attribute("freezing temperature") + + if self.immersion_freezing == "time-dependent": + assert ( + self.particulator.formulae.heterogeneous_ice_nucleation_rate.__name__ + != "Null" + ) + builder.request_attribute("immersed surface area") + + if self.homogeneous_freezing == "time-dependent": + assert ( + self.particulator.formulae.homogeneous_ice_nucleation_rate.__name__ + != "Null" + ) + builder.request_attribute("volume") + + if ( + self.homogeneous_freezing == "time-dependent" + or self.immersion_freezing == "time-dependent" + ): + self.rand = self.particulator.Storage.empty( + self.particulator.n_sd, dtype=float + ) + self.rng = self.particulator.Random( + self.particulator.n_sd, self.particulator.formulae.seed + ) + + def __call__(self): + if "Coalescence" in self.particulator.dynamics: + # TODO #594 + raise NotImplementedError( + "handling T_fz during collisions not implemented yet" + ) + + if not self.enable: + return + + if self.immersion_freezing == "singular": + self.particulator.immersion_freezing_singular() + elif self.immersion_freezing == "time-dependent": + self.rand.urand(self.rng) + self.particulator.immersion_freezing_time_dependent( + rand=self.rand, + ) + + if self.homogeneous_freezing == "threshold": + self.particulator.homogeneous_freezing_threshold() + elif self.homogeneous_freezing == "time-dependent": + self.rand.urand(self.rng) + self.particulator.homogeneous_freezing_time_dependent( + rand=self.rand, + ) + + if self.thaw == "instantaneous": + self.particulator.thaw_instantaneous() diff --git a/PySDM/source/PySDM/dynamics/impl/__init__.py b/PySDM/source/PySDM/dynamics/impl/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..2843142c69a21fc501d62bcb1febd1b11bc510d3 --- /dev/null +++ b/PySDM/source/PySDM/dynamics/impl/__init__.py @@ -0,0 +1,3 @@ +"""stuff not intended to be imported from user code""" + +from .register_dynamic import register_dynamic diff --git a/PySDM/source/PySDM/dynamics/impl/chemistry_utils.py b/PySDM/source/PySDM/dynamics/impl/chemistry_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..047818cf007dfa7923277c169637220642aefe48 --- /dev/null +++ b/PySDM/source/PySDM/dynamics/impl/chemistry_utils.py @@ -0,0 +1,150 @@ +""" +aqueous chemistry helper utils including specific gravity constants with + values obtained using [chempy](https://pythonhosted.org/chempy/)'s `Substance` +""" + +import numpy as np +from chempy import Substance + +from PySDM.physics.constants import K_H2O, M, si + + +class EqConst: # pylint: disable=too-few-public-methods + def __init__(self, formulae, constant_at_T0, dT, T_0): + self.formulae = formulae + self.K = constant_at_T0 + self.dH = formulae.trivia.tdep2enthalpy(dT) + self.T0 = T_0 + + def at(self, T): + return self.formulae.trivia.vant_hoff(self.K, self.dH, T, T_0=self.T0) + + +class KinConst: # pylint: disable=too-few-public-methods + def __init__(self, formulae, k, dT, T_0): + self.formulae = formulae + self.Ea = formulae.trivia.tdep2enthalpy(dT) + self.A = k * np.exp(self.Ea / (self.formulae.constants.R_str * T_0)) + + def at(self, T): + return self.formulae.trivia.arrhenius(self.A, self.Ea, T) + + +class HenryConsts: # pylint: disable=too-few-public-methods + def __init__(self, formulae): + const = formulae.constants + T0 = const.ROOM_TEMP + self.HENRY_CONST = { + "HNO3": EqConst(formulae, 2.1e5 * const.H_u, 8700 * const.dT_u, T_0=T0), + "H2O2": EqConst(formulae, 7.45e4 * const.H_u, 7300 * const.dT_u, T_0=T0), + "NH3": EqConst(formulae, 62 * const.H_u, 4110 * const.dT_u, T_0=T0), + "SO2": EqConst(formulae, 1.23 * const.H_u, 3150 * const.dT_u, T_0=T0), + "CO2": EqConst(formulae, 3.4e-2 * const.H_u, 2440 * const.dT_u, T_0=T0), + "O3": EqConst(formulae, 1.13e-2 * const.H_u, 2540 * const.dT_u, T_0=T0), + } + + +# Table 4 in Kreidenweis et al. 2003 +class EquilibriumConsts: # pylint: disable=too-few-public-methods + def __init__(self, formulae): + const = formulae.constants + T0 = const.ROOM_TEMP + self.EQUILIBRIUM_CONST = { # Reaction Specific units, K + "K_HNO3": EqConst(formulae, 15.4 * const.M, 8700 * const.dT_u, T_0=T0), + "K_SO2": EqConst(formulae, 1.3e-2 * const.M, 1960 * const.dT_u, T_0=T0), + "K_NH3": EqConst(formulae, 1.7e-5 * const.M, -450 * const.dT_u, T_0=T0), + "K_CO2": EqConst(formulae, 4.3e-7 * const.M, -1000 * const.dT_u, T_0=T0), + "K_HSO3": EqConst(formulae, 6.6e-8 * const.M, 1500 * const.dT_u, T_0=T0), + "K_HCO3": EqConst(formulae, 4.68e-11 * const.M, -1760 * const.dT_u, T_0=T0), + "K_HSO4": EqConst(formulae, 1.2e-2 * const.M, 2720 * const.dT_u, T_0=T0), + } + + +DIFFUSION_CONST = { + "HNO3": 65.25e-6 * si.m**2 / si.s, + "H2O2": 87.00e-6 * si.m**2 / si.s, + "NH3": 19.78e-6 * si.m**2 / si.s, + "SO2": 10.89e-6 * si.m**2 / si.s, + "CO2": 13.81e-6 * si.m**2 / si.s, + "O3": 14.44e-6 * si.m**2 / si.s, +} + +MASS_ACCOMMODATION_COEFFICIENTS = { + "HNO3": 0.05, + "H2O2": 0.018, + "NH3": 0.05, + "SO2": 0.035, + "CO2": 0.05, + "O3": 0.00053, +} + +AQUEOUS_COMPOUNDS = { + "S_IV": ("SO2 H2O", "HSO3", "SO3"), # rename: SO2 H2O -> H2SO3(aq) ? + "O3": ("O3",), + "H2O2": ("H2O2",), + "C_IV": ("CO2 H2O", "HCO3", "CO3"), # ditto + "N_V": ("HNO3", "NO3"), + "N_mIII": ("NH4", "H2O NH3"), + "S_VI": ("SO4", "HSO4"), +} + +GASEOUS_COMPOUNDS = { + "N_V": "HNO3", + "H2O2": "H2O2", + "N_mIII": "NH3", + "S_IV": "SO2", + "C_IV": "CO2", + "O3": "O3", +} + +DISSOCIATION_FACTORS = { + "CO2": lambda H, eqc, cell_id: 1 + + eqc["K_CO2"].data[cell_id] * (1 / H + eqc["K_HCO3"].data[cell_id] / (H**2)), + "SO2": lambda H, eqc, cell_id: 1 + + eqc["K_SO2"].data[cell_id] * (1 / H + eqc["K_HSO3"].data[cell_id] / (H**2)), + "NH3": lambda H, eqc, cell_id: 1 + eqc["K_NH3"].data[cell_id] / K_H2O * H, + "HNO3": lambda H, eqc, cell_id: 1 + eqc["K_HNO3"].data[cell_id] / H, + "O3": lambda _, __, ___: 1, + "H2O2": lambda _, __, ___: 1, +} + + +class KineticConsts: # pylint: disable=too-few-public-methods + def __init__(self, formulae): + const = formulae.constants + T0 = const.ROOM_TEMP + self.KINETIC_CONST = { + "k0": KinConst(formulae, k=2.4e4 / si.s / M, dT=0 * const.dT_u, T_0=T0), + "k1": KinConst(formulae, k=3.5e5 / si.s / M, dT=-5530 * const.dT_u, T_0=T0), + "k2": KinConst(formulae, k=1.5e9 / si.s / M, dT=-5280 * const.dT_u, T_0=T0), + # Different unit due to a different pseudo-order of kinetics + "k3": KinConst( + formulae, k=7.45e7 / si.s / M / M, dT=-4430 * const.dT_u, T_0=T0 + ), + } + + +k4 = 13 / M + + +class SpecificGravities: # pylint: disable=too-few-public-methods + def __init__(self, constants): + self._values = { + compound: Substance.from_formula(compound).mass + * si.gram + / si.mole + / constants.Md + for compound in GASEOUS_COMPOUNDS.values() + } + + for compounds in AQUEOUS_COMPOUNDS.values(): + for compound in compounds: + self._values[compound] = ( + Substance.from_formula(compound).mass + * si.gram + / si.mole + / constants.Md + ) + + def __getitem__(self, item): + return self._values[item] diff --git a/PySDM/source/PySDM/dynamics/impl/random_generator_optimizer.py b/PySDM/source/PySDM/dynamics/impl/random_generator_optimizer.py new file mode 100644 index 0000000000000000000000000000000000000000..4e2bb54094d20fd4f7a96d724d92a335e98facca --- /dev/null +++ b/PySDM/source/PySDM/dynamics/impl/random_generator_optimizer.py @@ -0,0 +1,48 @@ +""" +clever reuser of random numbers for use in adaptive coalescence +""" + +import math + + +class RandomGeneratorOptimizer: # pylint: disable=too-many-instance-attributes + def __init__(self, optimized_random, dt_min, seed): + self.particulator = None + self.optimized_random = optimized_random + self.dt_min = dt_min + self.seed = seed + self.substep = 0 + self.pairs_rand = None + self.rand = None + self.rnd = None + + def register(self, builder): + self.particulator = builder.particulator + shift = ( + math.ceil(self.particulator.dt / self.dt_min) + if self.optimized_random + else 0 + ) + self.pairs_rand = self.particulator.Storage.empty( + self.particulator.n_sd + shift, dtype=float + ) + self.rand = self.particulator.Storage.empty( + self.particulator.n_sd // 2, dtype=float + ) + self.rnd = self.particulator.Random(self.particulator.n_sd + shift, self.seed) + + def reset(self): + self.substep = 0 + + def get_random_arrays(self): + if self.optimized_random: + shift = self.substep + if self.substep == 0: + self.pairs_rand.urand(self.rnd) + self.rand.urand(self.rnd) + else: + shift = 0 + self.pairs_rand.urand(self.rnd) + self.rand.urand(self.rnd) + self.substep += 1 + return self.pairs_rand[shift : self.particulator.n_sd + shift], self.rand diff --git a/PySDM/source/PySDM/dynamics/impl/random_generator_optimizer_nopair.py b/PySDM/source/PySDM/dynamics/impl/random_generator_optimizer_nopair.py new file mode 100644 index 0000000000000000000000000000000000000000..d0fa9289a1b2a37597a9095c8070fd6158f61a67 --- /dev/null +++ b/PySDM/source/PySDM/dynamics/impl/random_generator_optimizer_nopair.py @@ -0,0 +1,40 @@ +""" +TODO #744 +""" + +import math + + +class RandomGeneratorOptimizerNoPair: + def __init__(self, optimized_random, dt_min, seed): + self.particulator = None + self.optimized_random = optimized_random + self.dt_min = dt_min + self.seed = seed + self.substep = 0 + self.rand = None + self.rnd = None + + def register(self, builder): + self.particulator = builder.particulator + shift = ( + math.ceil(self.particulator.dt / self.dt_min) + if self.optimized_random + else 0 + ) + self.rand = self.particulator.Storage.empty( + self.particulator.n_sd // 2, dtype=float + ) + self.rnd = self.particulator.Random(self.particulator.n_sd + shift, self.seed) + + def reset(self): + self.substep = 0 + + def get_random_arrays(self): + if self.optimized_random: + if self.substep == 0: + self.rand.urand(self.rnd) + else: + self.rand.urand(self.rnd) + self.substep += 1 + return self.rand diff --git a/PySDM/source/PySDM/dynamics/impl/register_dynamic.py b/PySDM/source/PySDM/dynamics/impl/register_dynamic.py new file mode 100644 index 0000000000000000000000000000000000000000..3cd5a9d9d5ad8344c592d709c37eedcbe173708a --- /dev/null +++ b/PySDM/source/PySDM/dynamics/impl/register_dynamic.py @@ -0,0 +1,21 @@ +"""decorator for dynamics classes +ensuring that their instances can be re-used with multiple builders""" + +from copy import deepcopy + + +def _instantiate(self, *, builder): + copy = deepcopy(self) + copy.register(builder=builder) + return copy + + +def register_dynamic(): + def decorator(cls): + if hasattr(cls, "instantiate"): + assert cls.instantiate is _instantiate + else: + setattr(cls, "instantiate", _instantiate) + return cls + + return decorator diff --git a/PySDM/source/PySDM/dynamics/isotopic_fractionation.py b/PySDM/source/PySDM/dynamics/isotopic_fractionation.py new file mode 100644 index 0000000000000000000000000000000000000000..5ab953df025a9feb8f5ca38e4f64c018eb8d54fb --- /dev/null +++ b/PySDM/source/PySDM/dynamics/isotopic_fractionation.py @@ -0,0 +1,43 @@ +""" +Resolves fractionation of water molecules across different isotopologues. + +Requires condensation dynamic to be registered (and run beforehand). +Considers only single-substituted molecules (i.e. no D2O, etc.) +""" + +from PySDM.dynamics.condensation import Condensation +from PySDM.dynamics.impl import register_dynamic + +LIGHT_ISOTOPES = ("1H", "16O") +HEAVY_ISOTOPES = ("2H", "3H", "17O", "18O") + + +@register_dynamic() +class IsotopicFractionation: + def __init__(self, isotopes: tuple = HEAVY_ISOTOPES): + self.isotopes = isotopes + self.particulator = None + + def register(self, builder): + self.particulator = builder.particulator + + try: + ix_cond = list(builder.particulator.dynamics.keys()).index( + Condensation.__name__ + ) + except ValueError: + ix_cond = -1 + ix_self = list(builder.particulator.dynamics.keys()).index( + self.__class__.__name__ + ) + if ix_cond == -1 or ix_cond > ix_self: + raise AssertionError( + f"{Condensation.__name__} needs to be registered to run prior to {self.__class__}" + ) + + builder.request_attribute("diffusional growth mass change") + for isotope in self.isotopes: + builder.request_attribute(f"moles_{isotope}") + + def __call__(self): + self.particulator.isotopic_fractionation(self.isotopes) diff --git a/PySDM/source/PySDM/dynamics/relaxed_velocity.py b/PySDM/source/PySDM/dynamics/relaxed_velocity.py new file mode 100644 index 0000000000000000000000000000000000000000..cd6d32a067bfb1fb62dc46dd4708f321b86b22c6 --- /dev/null +++ b/PySDM/source/PySDM/dynamics/relaxed_velocity.py @@ -0,0 +1,90 @@ +""" +A dynamic which relaxes +`PySDM.attributes.physics.relative_fall_velocity.RelativeFallVelocity` +towards the terminal velocity +""" + +from PySDM.attributes.impl.attribute import Attribute +from PySDM.particulator import Particulator +from PySDM.dynamics.impl import register_dynamic + + +@register_dynamic() +class RelaxedVelocity: # pylint: disable=too-many-instance-attributes + """ + A dynamic which updates the fall momentum according to a relaxation timescale + proportional to the sqrt of the droplet radius. + """ + + def __init__(self, c: float = 8, constant: bool = False): + """ + Parameters: + - constant: use a constant relaxation timescale for all droplets + - c: relaxation timescale if `constant`, otherwise the proportionality constant + """ + # the default value of c is a very rough estimate + self.c: float = c + self.constant = constant + + self.particulator = None + self.fall_momentum_attr = None + self.terminal_vel_attr = None + self.water_mass_attr = None + self.sqrt_radius_attr = None + + self.tmp_momentum_diff = None + self.tmp_tau = None + self.tmp_scale = None + + self.tmp_tau_init = False + + def calculate_tau(self, output, sqrt_radius_storage): + """ + Calculates the relaxation timescale. + """ + output.fill(self.c) + if not self.constant: + output *= sqrt_radius_storage + + def calculate_scale_factor(self, output, tau_storage): + output.fill(-self.particulator.dt) + output /= tau_storage + output.exp() + output *= -1 + output += 1 + + def create_storage(self, n): + return self.particulator.Storage.empty((n,), dtype=float) + + def register(self, builder): + self.particulator: Particulator = builder.particulator + + self.fall_momentum_attr: Attribute = builder.get_attribute( + "relative fall momentum" + ) + self.terminal_vel_attr: Attribute = builder.get_attribute("terminal velocity") + self.water_mass_attr: Attribute = builder.get_attribute("signed water mass") + self.sqrt_radius_attr: Attribute = builder.get_attribute( + "square root of radius" + ) + + self.tmp_momentum_diff = self.create_storage(self.particulator.n_sd) + self.tmp_tau = self.create_storage(self.particulator.n_sd) + self.tmp_scale = self.create_storage(self.particulator.n_sd) + + def __call__(self): + # calculate momentum difference + self.tmp_momentum_diff.product( + self.terminal_vel_attr.get(), self.water_mass_attr.get() + ) + self.tmp_momentum_diff -= self.fall_momentum_attr.get() + + if not self.tmp_tau_init or not self.constant: + self.tmp_tau_init = True + self.calculate_tau(self.tmp_tau, self.sqrt_radius_attr.get()) + + self.calculate_scale_factor(self.tmp_scale, self.tmp_tau) + + self.tmp_momentum_diff *= self.tmp_scale + self.fall_momentum_attr.data += self.tmp_momentum_diff + self.particulator.attributes.mark_updated("relative fall momentum") diff --git a/PySDM/source/PySDM/dynamics/seeding.py b/PySDM/source/PySDM/dynamics/seeding.py new file mode 100644 index 0000000000000000000000000000000000000000..0a7075e3ce611daa515f938311c41de3cb1fb456 --- /dev/null +++ b/PySDM/source/PySDM/dynamics/seeding.py @@ -0,0 +1,94 @@ +"""particle injection handling, requires initialising a simulation with +enough particles flagged with NaN multiplicity (translated to zeros +at multiplicity discretisation""" + +from collections.abc import Sized + +import numpy as np + +from PySDM.dynamics.impl import register_dynamic +from PySDM.initialisation import discretise_multiplicities + + +@register_dynamic() +class Seeding: + def __init__( + self, + *, + super_droplet_injection_rate: callable, + seeded_particle_extensive_attributes: dict, + seeded_particle_multiplicity: Sized, + ): + for attr in seeded_particle_extensive_attributes.values(): + assert len(seeded_particle_multiplicity) == len(attr) + self.particulator = None + self.super_droplet_injection_rate = super_droplet_injection_rate + self.seeded_particle_extensive_attributes = seeded_particle_extensive_attributes + self.seeded_particle_multiplicity = seeded_particle_multiplicity + self.rnd = None + self.u01 = None + self.index = None + + def register(self, builder): + self.particulator = builder.particulator + + def post_register_setup_when_attributes_are_known(self): + if tuple(self.particulator.attributes.get_extensive_attribute_keys()) != tuple( + self.seeded_particle_extensive_attributes.keys() + ): + raise ValueError( + f"extensive attributes ({self.seeded_particle_extensive_attributes.keys()})" + " do not match those used in particulator" + f" ({self.particulator.attributes.get_extensive_attribute_keys()})" + ) + + self.index = self.particulator.Index.identity_index( + len(self.seeded_particle_multiplicity) + ) + if len(self.seeded_particle_multiplicity) > 1: + self.rnd = self.particulator.Random( + len(self.seeded_particle_multiplicity), self.particulator.formulae.seed + ) + self.u01 = self.particulator.Storage.empty( + len(self.seeded_particle_multiplicity), dtype=float + ) + self.seeded_particle_multiplicity = ( + self.particulator.IndexedStorage.from_ndarray( + self.index, + discretise_multiplicities( + np.asarray(self.seeded_particle_multiplicity) + ), + ) + ) + self.seeded_particle_extensive_attributes = ( + self.particulator.IndexedStorage.from_ndarray( + self.index, + np.asarray(list(self.seeded_particle_extensive_attributes.values())), + ) + ) + + def __call__(self): + if self.particulator.n_steps == 0: + self.post_register_setup_when_attributes_are_known() + + time = self.particulator.n_steps * self.particulator.dt + number_of_super_particles_to_inject = self.super_droplet_injection_rate(time) + + if number_of_super_particles_to_inject > 0: + assert number_of_super_particles_to_inject <= len( + self.seeded_particle_multiplicity + ) + + if self.rnd is not None: + self.u01.urand(self.rnd) + # TODO #1387 make shuffle smarter + # e.g. don't need to shuffle if only one type of seed particle + # or if the number of super particles to inject + # is equal to the number of possible seeds + self.index.shuffle(self.u01) + self.particulator.seeding( + seeded_particle_index=self.index, + number_of_super_particles_to_inject=number_of_super_particles_to_inject, + seeded_particle_multiplicity=self.seeded_particle_multiplicity, + seeded_particle_extensive_attributes=self.seeded_particle_extensive_attributes, + ) diff --git a/PySDM/source/PySDM/dynamics/terminal_velocity/__init__.py b/PySDM/source/PySDM/dynamics/terminal_velocity/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e973cee80e316f7058a1777ca0974ef1ee53b0ca --- /dev/null +++ b/PySDM/source/PySDM/dynamics/terminal_velocity/__init__.py @@ -0,0 +1,9 @@ +""" +particle terminal velocity formulae +""" + +from PySDM.dynamics.terminal_velocity.gunn_and_kinzer import GunnKinzer1949 +from PySDM.dynamics.terminal_velocity.power_series import PowerSeries +from PySDM.dynamics.terminal_velocity.rogers_and_yau import RogersYau +from PySDM.dynamics.terminal_velocity.columnar_ice_crystal import ColumnarIceCrystal +from PySDM.dynamics.terminal_velocity.spheres_ice import IceSphere diff --git a/PySDM/source/PySDM/dynamics/terminal_velocity/columnar_ice_crystal.py b/PySDM/source/PySDM/dynamics/terminal_velocity/columnar_ice_crystal.py new file mode 100644 index 0000000000000000000000000000000000000000..fde145ff28e37e04f1fe4f950caf4dc79fff1508 --- /dev/null +++ b/PySDM/source/PySDM/dynamics/terminal_velocity/columnar_ice_crystal.py @@ -0,0 +1,20 @@ +""" +[Spichtinger & Gierens 2009](https://doi.org/10.5194/acp-9-685-2009) +Eq. (18) .Assumed shape is columnar based on empirical parameterizations of +[Heymsfield & Iaquinta (2000)](https://doi.org/10.1175/1520-0469(2000)057%3C0916:CCTV%3E2.0.CO;2) +[Barthazy & Schefold (2006)](https://doi.org/10.1016/j.atmosres.2005.12.009) +""" + + +class ColumnarIceCrystal: # pylint: disable=too-few-public-methods,too-many-arguments + def __init__(self, particulator): + self.particulator = particulator + + def __call__(self, output, signed_water_mass, cell_id, temperature, pressure): + self.particulator.backend.terminal_velocity_columnar_ice_crystals( + values=output.data, + signed_water_mass=signed_water_mass.data, + cell_id=cell_id.data, + temperature=temperature.data, + pressure=pressure.data, + ) diff --git a/PySDM/source/PySDM/dynamics/terminal_velocity/gunn_and_kinzer.py b/PySDM/source/PySDM/dynamics/terminal_velocity/gunn_and_kinzer.py new file mode 100644 index 0000000000000000000000000000000000000000..182d46c0d2311af6bb70b8dd30881e104960b6ac --- /dev/null +++ b/PySDM/source/PySDM/dynamics/terminal_velocity/gunn_and_kinzer.py @@ -0,0 +1,214 @@ +""" +[Gunn & Kinzer 1949](https://doi.org/10.1175/1520-0469(1949)006%3C0243:TTVOFF%3E2.0.CO;2) +terminal velocities used for things like coalescence kernel evaluation, particle +displacement, ventilation factor, etc +""" + +import numba +import numpy as np +from scipy.interpolate import Rbf + +from PySDM.backends.impl_numba import conf +from PySDM.physics import constants as const + + +class GunnKinzer1949: # pylint: disable=too-few-public-methods + def __init__(self, particulator, small_r_limit=None): + self.particulator = particulator + + """ + Table 2 in + [Gunn & Kinzer 1949](https://doi.org/10.1175/1520-0469(1949)006%3C0243:TTVOFF%3E2.0.CO;2) + """ + ir = ( + np.array( + [ + 0.078, + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1.0, + 1.2, + 1.4, + 1.6, + 1.8, + 2.0, + 2.2, + 2.4, + 2.6, + 2.8, + 3.0, + 3.2, + 3.4, + 3.6, + 3.8, + 4.0, + 4.2, + 4.4, + 4.6, + 4.8, + 5.0, + 5.2, + 5.4, + 5.6, + 5.8, + ] + ) + * 1e-3 + / 2 + ) + iu = ( + np.array( + [ + 18, + 27, + 72, + 117, + 162, + 206, + 247, + 287, + 327, + 367, + 403, + 464, + 517, + 565, + 609, + 649, + 690, + 727, + 757, + 782, + 806, + 826, + 844, + 860, + 872, + 883, + 892, + 898, + 903, + 907, + 909, + 912, + 914, + 916, + 917, + ] + ) + / 100 + ) + + rbf = Rbf(ir, iu) + self.factor = 100000 + num = 6 * self.factor // 1000 + 1 + + self.minimum_radius = 0 + self.maximum_radius = 0.6 * const.si.cm + space, step = np.linspace( + self.minimum_radius, self.maximum_radius, num, retstep=True + ) + u = np.empty(num) + u[:] = rbf(space) + u[0] = 0 + approximation_small = TpDependent.make(only_small=True) + small_r_limit = small_r_limit or 40 * const.si.um + approximation_small(u[1:], space[1:], small_r_limit) + self.a = particulator.backend.Storage.from_ndarray(u) + b = np.append(np.diff(u), [u[-1] - u[-2]]) / step + self.b = particulator.backend.Storage.from_ndarray(b) + + def __call__(self, output, radius): + r_max = radius.amax() + if r_max > self.maximum_radius: + raise ValueError( + f"Radii can be interpolated up to {self.maximum_radius} m" + + f" (max value of {r_max} m within input data)" + ) + self.particulator.backend.gunn_and_kinzer_interpolation( + output=output, radius=radius, factor=self.factor, b=self.a, c=self.b + ) + + +class TpDependent: + def __init__(self, _, small_r_limit=None): + si = const.si + self.small_r_limit = small_r_limit or 40 * si.um + self.approximation = TpDependent.make() + + def __call__(self, output, radius): + return self.approximation(output, radius, self.small_r_limit) + + @staticmethod + def make(only_small=False): + # pylint: disable=too-many-locals + # TODO #348 T, p dependence + # TODO #348 move constants to physics.constants + + si = const.si + si_cm = si.cm + T = 293.15 + p = 1000 * si.hPa + + p0 = 1013.25 * si.hPa + rho0 = 1.204 * si.kg * si.m ** (-3) + n = 1.832e-5 # * (1 + 0.00266 * (T - 296)) #* si.kg * si.m**(-1) * si.s**(-1) # TODO #348 + rho = 0.348 * p / T # * si.kg * si.m ** (-3) + l0 = 6.62e-6 * si.cm + n0 = 1.818e-5 * si.kg * si.m ** (-1) * si.s ** (-1) + l = l0 * (n / n0) * (p0 * rho0 / p * rho) ** (1 / 2) # TODO #348 + es = (n0 / n) - 1 + ec = ((rho0 / rho) ** (1 / 2)) - 1 + + c4 = np.array([10.5035, 1.08750, -0.133245, -0.00659969]) + + @numba.njit(**{**conf.JIT_FLAGS, "cache": False, "parallel": False}) + def f4(r): + return (n0 / n) * (1 + 1.255 * l / r) / (1 + 1.255 * l0 / r) + + c8 = np.asarray( + ( + 6.5639, + -1.0391, + -1.4001, + -0.82736, + -0.34277, + -0.083072, + -0.010583, + -0.00054208, + ) + ) + + @numba.njit(**{**conf.JIT_FLAGS, "cache": False, "parallel": False}) + def f8(r): + result = 1.058 * ec - 1.104 * es + result *= (6.21 + np.log(r)) / 5.01 + result += 1.104 * es + result += 1 + return result + + @numba.njit(**{**conf.JIT_FLAGS, "cache": False}) + def terminal_velocity(values, radius, threshold): + for i in numba.prange(len(values)): # pylint: disable=not-an-iterable + if radius[i] < 0: + values[i] = 0 + continue # TODO #599 + r = radius[i] / si_cm + sum_r = 0 + if radius[i] < threshold: + for j in range(4): + sum_r += c4[j] * (np.log(2 * r) ** j) + values[i] = f4(r) * np.exp(sum_r) * si_cm + elif not only_small: + for j in range(8): + sum_r += c8[j] * (np.log(2 * r) ** j) + values[i] = f8(r) * np.exp(sum_r) * si_cm + + return terminal_velocity diff --git a/PySDM/source/PySDM/dynamics/terminal_velocity/power_series.py b/PySDM/source/PySDM/dynamics/terminal_velocity/power_series.py new file mode 100644 index 0000000000000000000000000000000000000000..9f97a9bd992ecc661932a6fb84d4154e4966dd8d --- /dev/null +++ b/PySDM/source/PySDM/dynamics/terminal_velocity/power_series.py @@ -0,0 +1,34 @@ +""" +Power series expression - a simple and mutable power-law (where the coefficients are specified +by the user), which is a form used in many more complex terminal velocity parameterizations +such as [Rogers & Yau 1989](https://archive.org/details/shortcourseinclo0000roge_m3k2). +This formulation emerges from balancing drag and gravitational forces on +a spherical object, and the power depends on the drag coefficient (and thus the Reynolds number +and flow regime). Rogers and Yau distinguishes different power law parameters for three regimes, +whereas this much simpler formulation applies across the full range of particle sizes. +It is introduced it as a simple option for comparison against bulk methods, think of it as the +terminal-velocity analogue to the Geometric collision kernel. +""" + +import numpy as np + +from PySDM.physics import si, constants as const + + +class PowerSeries: # pylint: disable=too-few-public-methods + def __init__(self, particulator, *, prefactors=None, powers=None): + self.particulator = particulator + self.prefactors = np.array(prefactors or [2.0e-1 * si.m / si.s / np.sqrt(si.m)]) + self.powers = np.array(powers or [1 / 6]) + assert len(self.prefactors) == len(self.powers) + for i, p in enumerate(self.powers): + self.prefactors[i] *= const.PI_4_3**p / si.um ** (3 * p) + + def __call__(self, output, radius): + self.particulator.backend.power_series( + values=output.data, + radius=radius.data, + num_terms=len(self.powers), + prefactors=self.prefactors, + powers=self.powers, + ) diff --git a/PySDM/source/PySDM/dynamics/terminal_velocity/rogers_and_yau.py b/PySDM/source/PySDM/dynamics/terminal_velocity/rogers_and_yau.py new file mode 100644 index 0000000000000000000000000000000000000000..fc46d2d3a1120b802cf284277d5826496be5f900 --- /dev/null +++ b/PySDM/source/PySDM/dynamics/terminal_velocity/rogers_and_yau.py @@ -0,0 +1,15 @@ +""" +[Rogers & Yau](https://archive.org/details/shortcourseinclo0000roge_m3k2), +equations: (8.5), (8.6), (8.8) +""" + + +class RogersYau: # pylint: disable=too-few-public-methods + def __init__(self, particulator): + self.particulator = particulator + + def __call__(self, output, radius): + self.particulator.backend.rogers_and_yau_terminal_velocity( + values=output.data, + radius=radius.data, + ) diff --git a/PySDM/source/PySDM/dynamics/terminal_velocity/spheres_ice.py b/PySDM/source/PySDM/dynamics/terminal_velocity/spheres_ice.py new file mode 100644 index 0000000000000000000000000000000000000000..82bac7dbb78542a7973fac89840994b8c7b46427 --- /dev/null +++ b/PySDM/source/PySDM/dynamics/terminal_velocity/spheres_ice.py @@ -0,0 +1,17 @@ +""" +terminal velocity of solid ice spheres +""" + + +class IceSphere: # pylint: disable=too-few-public-methods,too-many-arguments + def __init__(self, particulator): + self.particulator = particulator + + def __call__(self, output, signed_water_mass, cell_id, temperature, pressure): + self.particulator.backend.terminal_velocity_ice_spheres( + values=output.data, + signed_water_mass=signed_water_mass.data, + cell_id=cell_id.data, + temperature=temperature.data, + pressure=pressure.data, + ) diff --git a/PySDM/source/PySDM/dynamics/vapour_deposition_on_ice.py b/PySDM/source/PySDM/dynamics/vapour_deposition_on_ice.py new file mode 100644 index 0000000000000000000000000000000000000000..fa73d4624e1f637b567d90e75bbf47c751bf5e92 --- /dev/null +++ b/PySDM/source/PySDM/dynamics/vapour_deposition_on_ice.py @@ -0,0 +1,21 @@ +"""basic water vapor deposition on ice""" + +from PySDM.dynamics.impl import register_dynamic + + +@register_dynamic() +class VapourDepositionOnIce: + def __init__(self, adaptive: bool = True): + """called by the user while building a particulator""" + self.particulator = None + self.adaptive = adaptive + + def register(self, *, builder): + """called by the builder""" + self.particulator = builder.particulator + assert builder.formulae.particle_shape_and_density.supports_mixed_phase() + builder.request_attribute("Reynolds number") + + def __call__(self): + """called by the particulator during simulation""" + self.particulator.deposition(adaptive=self.adaptive) diff --git a/PySDM/source/PySDM/environments/__init__.py b/PySDM/source/PySDM/environments/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f9e9d18f9c1e9d5eccfc715c41d47d92bf1dfdce --- /dev/null +++ b/PySDM/source/PySDM/environments/__init__.py @@ -0,0 +1,10 @@ +""" +Classes representing particle environment: +`PySDM.environments.box.Box`, +`PySDM.environments.parcel.Parcel`, ... +""" + +from .box import Box +from .kinematic_1d import Kinematic1D +from .kinematic_2d import Kinematic2D +from .parcel import Parcel diff --git a/PySDM/source/PySDM/environments/box.py b/PySDM/source/PySDM/environments/box.py new file mode 100644 index 0000000000000000000000000000000000000000..ccb7e18e238776a1c35f923d71481fccf6b99aa0 --- /dev/null +++ b/PySDM/source/PySDM/environments/box.py @@ -0,0 +1,41 @@ +""" +Bare zero-dimensional framework +""" + +import numpy as np + +from PySDM.impl.mesh import Mesh +from PySDM.environments.impl import register_environment + + +@register_environment() +class Box: + def __init__(self, dt, dv): + self.dt = dt + self.mesh = Mesh.mesh_0d(dv) + self.particulator = None + self._ambient_air = {} + + def __getitem__(self, item): + return self._ambient_air[item] + + def __setitem__(self, key, value): + if key not in self._ambient_air: + self._ambient_air[key] = self.particulator.backend.Storage.from_ndarray( + np.array([value]) + ) + else: + self._ambient_air[key][:] = value + + def register(self, builder): + self.particulator = builder.particulator + + def init_attributes(self, *, spectral_discretisation): + attributes = {} + ( + attributes["volume"], + attributes["multiplicity"], + ) = spectral_discretisation.sample( + backend=self.particulator.backend, n_sd=self.particulator.n_sd + ) + return attributes diff --git a/PySDM/source/PySDM/environments/impl/__init__.py b/PySDM/source/PySDM/environments/impl/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..aa175b9042aa2a1bc86975c029a35644a1a98847 --- /dev/null +++ b/PySDM/source/PySDM/environments/impl/__init__.py @@ -0,0 +1,3 @@ +"""common internals not intended to be imported from user code""" + +from PySDM.environments.impl.register_environment import register_environment diff --git a/PySDM/source/PySDM/environments/impl/moist.py b/PySDM/source/PySDM/environments/impl/moist.py new file mode 100644 index 0000000000000000000000000000000000000000..bef7c02dc44e9df3ca2118bedebccddae1ca774c --- /dev/null +++ b/PySDM/source/PySDM/environments/impl/moist.py @@ -0,0 +1,116 @@ +""" +common logic for environments featuring moist-air thermodynamics +""" + +from abc import abstractmethod + +import numpy as np + + +class Moist: + def __init__(self, dt, mesh, variables, mixed_phase=False): + variables += ["water_vapour_mixing_ratio", "thd", "T", "p", "RH"] + if mixed_phase: + variables += ["a_w_ice", "RH_ice"] + all_vars_unique = len(variables) == len(set(variables)) + assert all_vars_unique + + self.particulator = None + self.dt = dt + self.mesh = mesh + self.variables = variables + self._values = None + self._tmp = None + self._nan_field = None + + def register(self, builder): + self.particulator = builder.particulator + self.particulator.observers.append(self) + + if self.particulator.formulae.ventilation.__name__ != "Neglect": + for var in ("air density", "air dynamic viscosity"): + if var not in self.variables: + self.variables += [var] + + self._values = {"predicted": None, "current": self._allocate(self.variables)} + self._tmp = self._allocate(self.variables) + + self._nan_field = self._allocate(("_",))["_"] + self._nan_field.fill(np.nan) + + def _allocate(self, variables): + result = {} + for var in variables: + result[var] = self.particulator.Storage.empty((self.mesh.n_cell,), float) + return result + + def __getitem__(self, key: str): + """returns a Storage representing the variable (field) at a given key or + otherwise a NaN-filled Storage if the key is not found (in order to simplify + generic code which uses optional variables, e.g. air viscosity, etc.)""" + if key in self._values["current"]: + return self._values["current"][key] + return self._nan_field + + def get_predicted(self, key: str): + if self._values["predicted"] is None: + raise AssertionError( + "It seems the AmbientThermodynamics dynamic was not added" + " when building particulator" + ) + return self._values["predicted"][key] + + def _recalculate_temperature_pressure_relative_humidity(self, target): + self.particulator.backend.temperature_pressure_rh( + rhod=target["rhod"], + thd=target["thd"], + water_vapour_mixing_ratio=target["water_vapour_mixing_ratio"], + T=target["T"], + p=target["p"], + RH=target["RH"], + ) + + def sync(self): + target = self._tmp + target["water_vapour_mixing_ratio"].ravel(self.get_water_vapour_mixing_ratio()) + target["thd"].ravel(self.get_thd()) + + self._recalculate_temperature_pressure_relative_humidity(target) + + if "a_w_ice" in self.variables: + self.particulator.backend.a_w_ice( + T=target["T"], + p=target["p"], + RH=target["RH"], + water_vapour_mixing_ratio=target["water_vapour_mixing_ratio"], + a_w_ice=target["a_w_ice"], + RH_ice=target["RH_ice"], + ) + if "air density" in self.variables: + self.particulator.backend.air_density( + water_vapour_mixing_ratio=target["water_vapour_mixing_ratio"], + rhod=target["rhod"], + output=target["air density"], + ) + if "air dynamic viscosity" in self.variables: + self.particulator.backend.air_dynamic_viscosity( + temperature=target["T"], + output=target["air dynamic viscosity"], + ) + self._values["predicted"] = target + + @abstractmethod + def get_water_vapour_mixing_ratio(self) -> np.ndarray: + raise NotImplementedError() + + @abstractmethod + def get_thd(self) -> np.ndarray: + raise NotImplementedError() + + def notify(self): + if self._values["predicted"] is None: + return + + self._tmp = self._values["current"] + self._values["current"] = self._values["predicted"] + self._values["predicted"] = None diff --git a/PySDM/source/PySDM/environments/impl/register_environment.py b/PySDM/source/PySDM/environments/impl/register_environment.py new file mode 100644 index 0000000000000000000000000000000000000000..5db033aa3037333ce1ed259373856fe9c1a4a7d6 --- /dev/null +++ b/PySDM/source/PySDM/environments/impl/register_environment.py @@ -0,0 +1,24 @@ +"""decorator for environment classes +ensuring that their instances can be re-used with multiple builders""" + +from copy import deepcopy + + +def _instantiate(self, *, builder): + copy = deepcopy(self) + copy.register(builder=builder) + return copy + + +def register_environment(): + def decorator(cls): + if hasattr(cls, "instantiate"): + if cls.instantiate is not _instantiate: + raise AttributeError( + "decorated class has a different instantiate method" + ) + else: + setattr(cls, "instantiate", _instantiate) + return cls + + return decorator diff --git a/PySDM/source/PySDM/environments/kinematic_1d.py b/PySDM/source/PySDM/environments/kinematic_1d.py new file mode 100644 index 0000000000000000000000000000000000000000..81590173e7d267bdbea05288742cbfd1feef0d9a --- /dev/null +++ b/PySDM/source/PySDM/environments/kinematic_1d.py @@ -0,0 +1,91 @@ +""" +Single-column time-varying-updraft framework with moisture advection handled by +[PyMPDATA](http://github.com/open-atmos/PyMPDATA/) +""" + +import numpy as np + +from PySDM.environments.impl.moist import Moist + +from PySDM.impl import arakawa_c +from PySDM.initialisation.hygroscopic_equilibrium import equilibrate_wet_radii +from PySDM.environments.impl import register_environment + + +@register_environment() +class Kinematic1D(Moist): + def __init__(self, *, dt, mesh, thd_of_z, rhod_of_z, z0=0): + super().__init__(dt, mesh, []) + self.thd0 = thd_of_z(z0 + mesh.dz * arakawa_c.z_scalar_coord(mesh.grid)) + self.rhod = rhod_of_z(z0 + mesh.dz * arakawa_c.z_scalar_coord(mesh.grid)) + self.formulae = None + + def register(self, builder): + super().register(builder) + self.formulae = builder.particulator.formulae + rhod = builder.particulator.Storage.from_ndarray(self.rhod) + self._values["current"]["rhod"] = rhod + self._tmp["rhod"] = rhod + + def get_water_vapour_mixing_ratio(self) -> np.ndarray: + return self.particulator.dynamics["EulerianAdvection"].solvers.advectee + + def get_thd(self) -> np.ndarray: + return self.thd0 + + def init_attributes( + self, + *, + spatial_discretisation, + spectral_discretisation, + kappa, + z_part=None, + collisions_only=False, + ): + super().sync() + self.notify() + + attributes = {} + with np.errstate(all="raise"): + positions = spatial_discretisation.sample( + backend=self.particulator.backend, + grid=self.mesh.grid, + n_sd=self.particulator.n_sd, + z_part=z_part, + ) + ( + attributes["cell id"], + attributes["cell origin"], + attributes["position in cell"], + ) = self.mesh.cellular_attributes(positions) + + if collisions_only: + v_wet, n_per_kg = spectral_discretisation.sample_deterministic( + backend=self.particulator.backend, n_sd=self.particulator.n_sd + ) + attributes["volume"] = v_wet + else: + r_dry, n_per_kg = spectral_discretisation.sample_deterministic( + backend=self.particulator.backend, n_sd=self.particulator.n_sd + ) + attributes["dry volume"] = self.formulae.trivia.volume(radius=r_dry) + attributes["kappa times dry volume"] = attributes["dry volume"] * kappa + r_wet = equilibrate_wet_radii( + r_dry=r_dry, + environment=self, + cell_id=attributes["cell id"], + kappa_times_dry_volume=attributes["kappa times dry volume"], + ) + attributes["volume"] = self.formulae.trivia.volume(radius=r_wet) + + rhod = self["rhod"].to_ndarray() + cell_id = attributes["cell id"] + domain_volume = np.prod(np.array(self.mesh.size)) + + attributes["multiplicity"] = n_per_kg * rhod[cell_id] * domain_volume + + return attributes + + @property + def dv(self): + return self.mesh.dv diff --git a/PySDM/source/PySDM/environments/kinematic_2d.py b/PySDM/source/PySDM/environments/kinematic_2d.py new file mode 100644 index 0000000000000000000000000000000000000000..5034a7bbc0a01dfcaa1b09f54cf75f2f5ef96ed3 --- /dev/null +++ b/PySDM/source/PySDM/environments/kinematic_2d.py @@ -0,0 +1,100 @@ +""" +Two-dimensional single-eddy prescribed-flow framework with moisture and heat advection +handled by [PyMPDATA](http://github.com/open-atmos/PyMPDATA/) +""" + +import numpy as np + +from PySDM.environments.impl.moist import Moist +from PySDM.impl.mesh import Mesh +from PySDM.initialisation.hygroscopic_equilibrium import ( + default_rtol, + equilibrate_wet_radii, +) +from PySDM.initialisation.sampling.spectral_sampling import ConstantMultiplicity +from PySDM.impl import arakawa_c +from PySDM.environments.impl import register_environment + + +@register_environment() +class Kinematic2D(Moist): + def __init__(self, *, dt, grid, size, rhod_of, mixed_phase=False): + super().__init__(dt, Mesh(grid, size), [], mixed_phase=mixed_phase) + self.rhod_of = rhod_of + self.formulae = None + + def register(self, builder): + super().register(builder) + self.formulae = builder.particulator.formulae + rhod = builder.particulator.Storage.from_ndarray( + arakawa_c.make_rhod(self.mesh.grid, self.rhod_of).ravel() + ) + self._values["current"]["rhod"] = rhod + self._tmp["rhod"] = rhod + + @property + def dv(self): + return self.mesh.dv + + def init_attributes( + self, + *, + spatial_discretisation, + kappa, + dry_radius_spectrum, + rtol=default_rtol, + n_sd=None, + spectral_sampling=ConstantMultiplicity, + ): + super().sync() + self.notify() + n_sd = n_sd or self.particulator.n_sd + attributes = {} + with np.errstate(all="raise"): + positions = spatial_discretisation.sample( + backend=self.particulator.backend, grid=self.mesh.grid, n_sd=n_sd + ) + ( + attributes["cell id"], + attributes["cell origin"], + attributes["position in cell"], + ) = self.mesh.cellular_attributes(positions) + + r_dry, n_per_kg = spectral_sampling( + spectrum=dry_radius_spectrum + ).sample_deterministic(n_sd=n_sd, backend=self.particulator.backend) + + attributes["dry volume"] = self.formulae.trivia.volume(radius=r_dry) + attributes["kappa times dry volume"] = kappa * attributes["dry volume"] + if kappa == 0: + r_wet = r_dry + else: + r_wet = equilibrate_wet_radii( + r_dry=r_dry, + environment=self, + kappa_times_dry_volume=attributes["kappa times dry volume"], + rtol=rtol, + cell_id=attributes["cell id"], + ) + rhod = self["rhod"].to_ndarray() + cell_id = attributes["cell id"] + domain_volume = np.prod(np.array(self.mesh.size)) + + attributes["multiplicity"] = n_per_kg * rhod[cell_id] * domain_volume + attributes["water mass"] = ( + self.formulae.particle_shape_and_density.radius_to_mass(r_wet) + ) + + return attributes + + def get_thd(self): + return self.particulator.dynamics["EulerianAdvection"].solvers["th"] + + def get_water_vapour_mixing_ratio(self): + return self.particulator.dynamics["EulerianAdvection"].solvers[ + "water_vapour_mixing_ratio" + ] + + def sync(self): + self.particulator.dynamics["EulerianAdvection"].solvers.wait() + super().sync() diff --git a/PySDM/source/PySDM/environments/parcel.py b/PySDM/source/PySDM/environments/parcel.py new file mode 100644 index 0000000000000000000000000000000000000000..1a501bcd45ff5d68f33ae3119e385705940930b9 --- /dev/null +++ b/PySDM/source/PySDM/environments/parcel.py @@ -0,0 +1,153 @@ +""" +Zero-dimensional adiabatic parcel framework +""" + +from typing import List, Optional, Union + +import numpy as np + +from PySDM.environments.impl.moist import Moist +from PySDM.impl.mesh import Mesh +from PySDM.initialisation.hygroscopic_equilibrium import ( + default_rtol, + equilibrate_wet_radii, +) +from PySDM.environments.impl import register_environment + + +@register_environment() +class Parcel(Moist): # pylint: disable=too-many-instance-attributes + def __init__( + self, + *, + dt, + mass_of_dry_air: float, + p0: float, + initial_water_vapour_mixing_ratio: float, + T0: float, + w: Union[float, callable], + z0: float = 0, + mixed_phase=False, + variables: Optional[List[str]] = None, + ): + variables = (variables or []) + ["rhod", "z"] + super().__init__(dt, Mesh.mesh_0d(), variables, mixed_phase=mixed_phase) + + self.p0 = p0 + self.initial_water_vapour_mixing_ratio = initial_water_vapour_mixing_ratio + self.T0 = T0 + self.z0 = z0 + self.mass_of_dry_air = mass_of_dry_air + self.w = w if callable(w) else lambda _: w + self.delta_liquid_water_mixing_ratio = np.nan + + @property + def dv(self): + rhod_mean = (self.get_predicted("rhod")[0] + self["rhod"][0]) / 2 + return self.particulator.formulae.trivia.volume_of_density_mass( + rhod_mean, self.mass_of_dry_air + ) + + def register(self, builder): + formulae = builder.particulator.formulae + pd0 = formulae.trivia.p_d(self.p0, self.initial_water_vapour_mixing_ratio) + rhod0 = formulae.state_variable_triplet.rhod_of_pd_T(pd0, self.T0) + self.mesh.dv = formulae.trivia.volume_of_density_mass( + rhod0, self.mass_of_dry_air + ) + + Moist.register(self, builder) + + self["water_vapour_mixing_ratio"][:] = self.initial_water_vapour_mixing_ratio + self["thd"][:] = formulae.trivia.th_std(pd0, self.T0) + self["rhod"][:] = rhod0 + self["z"][:] = self.z0 + + self._tmp["water_vapour_mixing_ratio"][ + : + ] = self.initial_water_vapour_mixing_ratio + self.sync_parcel_vars() + Moist.sync(self) + self.notify() + + def init_attributes( + self, + *, + n_in_dv: [float, np.ndarray], + kappa: float, + r_dry: [float, np.ndarray], + rtol=default_rtol, + include_dry_volume_in_attribute: bool = True, + ): + if not isinstance(n_in_dv, np.ndarray): + r_dry = np.array([r_dry]) + n_in_dv = np.array([n_in_dv]) + + attributes = {} + dry_volume = self.particulator.formulae.trivia.volume(radius=r_dry) + attributes["kappa times dry volume"] = dry_volume * kappa + attributes["multiplicity"] = n_in_dv + r_wet = equilibrate_wet_radii( + r_dry=r_dry, + environment=self, + kappa_times_dry_volume=attributes["kappa times dry volume"], + rtol=rtol, + ) + attributes["volume"] = self.particulator.formulae.trivia.volume(radius=r_wet) + if include_dry_volume_in_attribute: + attributes["dry volume"] = dry_volume + return attributes + + def advance_parcel_vars(self): + """compute new values of displacement, dry-air density and volume, + and write them to self._tmp and self.mesh.dv""" + dt = self.particulator.dt + formulae = self.particulator.formulae + T = self["T"][0] + p = self["p"][0] + + dz_dt = self.w((self.particulator.n_steps + 1 / 2) * dt) # "mid-point" + water_vapour_mixing_ratio = ( + self["water_vapour_mixing_ratio"][0] + - self.delta_liquid_water_mixing_ratio / 2 + ) + + # derivative evaluated at p_old, T_old, mixrat_mid, w_mid + drho_dz = formulae.hydrostatics.drho_dz( + p=p, + T=T, + water_vapour_mixing_ratio=water_vapour_mixing_ratio, + lv=formulae.latent_heat_vapourisation.lv(T), + d_liquid_water_mixing_ratio__dz=( + self.delta_liquid_water_mixing_ratio / dz_dt / dt + ), + ) + drhod_dz = drho_dz # TODO #407 + + self.particulator.backend.explicit_euler(self._tmp["z"], dt, dz_dt) + self.particulator.backend.explicit_euler( + self._tmp["rhod"], dt, dz_dt * drhod_dz + ) + + self.mesh.dv = formulae.trivia.volume_of_density_mass( + (self._tmp["rhod"][0] + self["rhod"][0]) / 2, self.mass_of_dry_air + ) + + def get_thd(self): + return self["thd"] + + def get_water_vapour_mixing_ratio(self): + return self["water_vapour_mixing_ratio"] + + def sync_parcel_vars(self): + self.delta_liquid_water_mixing_ratio = ( + self._tmp["water_vapour_mixing_ratio"][0] + - self["water_vapour_mixing_ratio"][0] + ) + for var in self.variables: + self._tmp[var][:] = self[var][:] + + def sync(self): + self.sync_parcel_vars() + self.advance_parcel_vars() + super().sync() diff --git a/PySDM/source/PySDM/exporters/__init__.py b/PySDM/source/PySDM/exporters/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7099e94d56d89af2ae2b1be12badf5f120fba626 --- /dev/null +++ b/PySDM/source/PySDM/exporters/__init__.py @@ -0,0 +1,9 @@ +""" +Exporters handling output to metadata-rich file formats incl. netCDF and VTK +""" + +from .netcdf_exporter import NetCDFExporter +from .netcdf_exporter_1d import NetCDFExporter_1d, readNetCDF_1d +from .vtk_exporter import VTKExporter +from .vtk_exporter_1d import VTKExporter_1d +from .vtk_exporter_parcel import VTKExporterParcel diff --git a/PySDM/source/PySDM/exporters/netcdf_exporter.py b/PySDM/source/PySDM/exporters/netcdf_exporter.py new file mode 100644 index 0000000000000000000000000000000000000000..60b93a763908c6d2157763d07ad2f85879d0511f --- /dev/null +++ b/PySDM/source/PySDM/exporters/netcdf_exporter.py @@ -0,0 +1,106 @@ +"""netCDF exporter implemented using +[SciPy.io.netcdf_file](https://docs.scipy.org/doc/scipy/reference/generated/scipy.io.netcdf_file.html) +""" # pylint: disable=line-too-long + +import numpy as np +from scipy.io import netcdf_file + +from PySDM.products.impl.spectrum_moment_product import SpectrumMomentProduct + +DIM_SUFFIX = "_bin_left_edges" + + +class NetCDFExporter: # pylint: disable=too-few-public-methods + def __init__(self, storage, settings, simulator, filename): + self.storage = storage + self.settings = settings + self.simulator = simulator + self.vars = None + self.filename = filename + self.XZ = ("X", "Z") + + def _write_settings(self, ncdf): + for setting in dir(self.settings): + setattr(ncdf, setting, getattr(self.settings, setting)) + + def _create_dimensions(self, ncdf): + ncdf.createDimension("T", len(self.settings.output_steps)) + + for index, label in enumerate(self.XZ): + ncdf.createDimension(label, self.settings.grid[index]) + + for name, instance in self.simulator.products.items(): + if isinstance(instance, SpectrumMomentProduct): + ncdf.createDimension( + f"{name}{DIM_SUFFIX}", len(instance.attr_bins_edges) - 1 + ) + + def _create_variables(self, ncdf): + self.vars = {} + self.vars["T"] = ncdf.createVariable("T", "f", ["T"]) + self.vars["T"].units = "seconds" + + for index, label in enumerate(self.XZ): + self.vars[label] = ncdf.createVariable(label, "f", (label,)) + self.vars[label][:] = ( + self.settings.size[index] / self.settings.grid[index] + ) * (1 / 2 + np.arange(self.settings.grid[index])) + self.vars[label].units = "metres" + + for name, instance in self.simulator.products.items(): + if isinstance(instance, SpectrumMomentProduct): + label = f"{name}{DIM_SUFFIX}" + self.vars[label] = ncdf.createVariable(label, "f", (label,)) + self.vars[label][:] = instance.attr_bins_edges.to_ndarray()[:-1] + self.vars[label].units = instance.attr_unit + + for name, instance in self.simulator.products.items(): + if name in self.vars: + raise AssertionError( + f"product ({name}) has same name as one of netCDF dimensions" + ) + + n_dimensions = len(instance.shape) + if n_dimensions == 3: + dimensions = ("T", "X", "Z", f"{name}{DIM_SUFFIX}") + elif n_dimensions == 2: + dimensions = ("T", "X", "Z") + elif n_dimensions == 0: + dimensions = ("T",) + else: + raise NotImplementedError() + self.vars[name] = ncdf.createVariable(name, "f", dimensions) + self.vars[name].units = instance.unit + + def _write_variables(self, i): + self.vars["T"][i] = self.settings.output_steps[i] * self.settings.dt + for var in self.simulator.products.keys(): + n_dimensions = len(self.simulator.products[var].shape) + if n_dimensions == 3: + self.vars[var][i, :, :, :] = self.storage.load( + var, self.settings.output_steps[i] + ) + elif n_dimensions == 2: + self.vars[var][i, :, :] = self.storage.load( + var, self.settings.output_steps[i] + ) + elif n_dimensions == 0: + if i == 0: + self.vars[var][:] = self.storage.load(var) + else: + pass + else: + raise NotImplementedError() + + def run(self, controller): + with controller: + controller.set_percent(0) + with netcdf_file(self.filename, mode="w") as ncdf: + self._write_settings(ncdf) + self._create_dimensions(ncdf) + self._create_variables(ncdf) + for i in range(len(self.settings.output_steps)): + if controller.panic: + break + self._write_variables(i) + controller.set_percent((i + 1) / len(self.settings.output_steps)) diff --git a/PySDM/source/PySDM/exporters/netcdf_exporter_1d.py b/PySDM/source/PySDM/exporters/netcdf_exporter_1d.py new file mode 100644 index 0000000000000000000000000000000000000000..c98b7ea5d8015db46a7cb9cb614d38472c1873f2 --- /dev/null +++ b/PySDM/source/PySDM/exporters/netcdf_exporter_1d.py @@ -0,0 +1,133 @@ +""" +netcdf exporter of scalar products for 1d simulations +""" + +from collections import namedtuple +from copy import deepcopy + +import numpy as np +from scipy.io import netcdf_file + + +class NetCDFExporter_1d: # pylint: disable=too-few-public-methods,too-many-instance-attributes + def __init__( # pylint: disable = too-many-arguments + self, data, settings, simulator, filename, exclude_particle_reservoir=True + ): + self.data = data + self.settings = settings + self.simulator = simulator + self.vars = None + self.filename = filename + self.nz_export = ( + int(self.settings.z_max / self.settings.dz) + if exclude_particle_reservoir + else settings.nz + ) + self.z0 = ( + 0.0 if exclude_particle_reservoir else -settings.particle_reservoir_depth + ) + self.n_save_spec = len(self.settings.save_spec_and_attr_times) + + def _write_settings(self, ncdf): + for setting in dir(self.settings): + setattr(ncdf, setting, getattr(self.settings, setting)) + + def _create_dimensions(self, ncdf): + ncdf.createDimension("time", self.settings.nt + 1) + ncdf.createDimension("height", self.nz_export) + + if self.n_save_spec != 0: + ncdf.createDimension("time_save_spec", self.n_save_spec) + for name, instance in self.simulator.particulator.products.items(): + if len(instance.shape) == 2: + dim_name = name.replace(" ", "_") + "_bin_index" + ncdf.createDimension(dim_name, self.settings.number_of_bins) + + def _create_variables(self, ncdf): + self.vars = {} + self.vars["time"] = ncdf.createVariable("time", "f", ["time"]) + self.vars["time"][:] = self.settings.dt * np.arange(self.settings.nt + 1) + self.vars["time"].units = "seconds" + + self.vars["height"] = ncdf.createVariable("height", "f", ["height"]) + self.vars["height"][:] = self.z0 + self.settings.dz * ( + 1 / 2 + np.arange(self.nz_export) + ) + self.vars["height"].units = "metres" + + if self.n_save_spec != 0: + self.vars["time_save_spec"] = ncdf.createVariable( + "time_save_spec", "f", ["time_save_spec"] + ) + self.vars["time_save_spec"][:] = self.settings.save_spec_and_attr_times + self.vars["time_save_spec"].units = "seconds" + + for name, instance in self.simulator.particulator.products.items(): + if len(instance.shape) == 2: + label = name.replace(" ", "_") + "_bin_index" + self.vars[label] = ncdf.createVariable(label, "f", (label,)) + self.vars[label][:] = np.arange(1, self.settings.number_of_bins + 1) + + for name, instance in self.simulator.particulator.products.items(): + if name in self.vars: + raise AssertionError( + f"product ({name}) has same name as one of netCDF dimensions" + ) + + n_dimensions = len(instance.shape) + if n_dimensions == 0: + dimensions = ("time",) + elif n_dimensions == 1: + dimensions = ("height", "time") + elif n_dimensions == 2: + dim_name = name.replace(" ", "_") + "_bin_index" + if self.n_save_spec == 0: + continue + if self.n_save_spec == 1: + dimensions = ("height", f"{dim_name}") + else: + dimensions = ("height", f"{dim_name}", "time_save_spec") + else: + raise NotImplementedError() + + self.vars[name] = ncdf.createVariable(name, "f", dimensions) + self.vars[name].units = instance.unit + + def _write_variables(self): + for var in self.simulator.particulator.products.keys(): + n_dimensions = len(self.simulator.particulator.products[var].shape) + if n_dimensions == 0: + self.vars[var][:] = self.data[var][:] + elif n_dimensions == 1: + self.vars[var][:, :] = self.data[var][-self.nz_export :, :] + elif n_dimensions == 2: + if self.n_save_spec == 0: + continue + if self.n_save_spec == 1: + self.vars[var][:, :] = self.data[var][-self.nz_export :, :, 0] + else: + self.vars[var][:, :, :] = self.data[var][-self.nz_export :, :, :] + else: + raise NotImplementedError() + + def run(self): + with netcdf_file(self.filename, mode="w") as ncdf: + self._write_settings(ncdf) + self._create_dimensions(ncdf) + self._create_variables(ncdf) + self._write_variables() + + +def readNetCDF_1d(file): + f = netcdf_file(file, "r") + output_settings = deepcopy(f._attributes) + + _products = deepcopy(f.variables) + output_products = {} + for k, v in _products.items(): + output_products[k] = v[:] + + output_dicts = namedtuple("output_dicts", "settings products") + outputs = output_dicts(output_settings, output_products) + f.close() + return outputs diff --git a/PySDM/source/PySDM/exporters/vtk_exporter.py b/PySDM/source/PySDM/exporters/vtk_exporter.py new file mode 100644 index 0000000000000000000000000000000000000000..7b49f0207116e454edf5bae711d6a22f41567868 --- /dev/null +++ b/PySDM/source/PySDM/exporters/vtk_exporter.py @@ -0,0 +1,167 @@ +""" +VTK exporter implemented using [pyevtk](https://pypi.org/project/pyevtk/) +""" + +import numbers +import os +import sys + +import numpy as np +from pyevtk.hl import gridToVTK, pointsToVTK +from pyevtk.vtk import VtkGroup + + +class VTKExporter: + """ + Example of use: + + exporter = VTKExporter() + + for step in range(settings.n_steps): + simulation.particulator.run(1) + + exporter.export_attributes(simulation.particulator) + exporter.export_products(simulation.particulator) + + """ + + def __init__( + self, + *, + path=".", + attributes_filename="sd_attributes", + products_filename="sd_products", + file_num_len=10, + verbose=False, + ): + self.path = os.path.join(path, "output") + + if not os.path.isdir(self.path): + os.mkdir(self.path) + + self.attributes_file_path = os.path.join(self.path, attributes_filename) + self.products_file_path = os.path.join(self.path, products_filename) + self.num_len = file_num_len + self.exported_times = {} + self.exported_times["attributes"] = {} + self.exported_times["products"] = {} + self.verbose = verbose + + def write_pvd(self): + pvd_attributes = VtkGroup(self.attributes_file_path) + for k, v in self.exported_times["attributes"].items(): + pvd_attributes.addFile(k + ".vtu", sim_time=v) + pvd_attributes.save() + + pvd_products = VtkGroup(self.products_file_path) + for k, v in self.exported_times["products"].items(): + pvd_products.addFile(k + ".vts", sim_time=v) + pvd_products.save() + + def export_attributes(self, particulator): + path = ( + self.attributes_file_path + + "_num" + + self.add_leading_zeros(particulator.n_steps) + ) + self.exported_times["attributes"][path] = particulator.n_steps * particulator.dt + if self.verbose: + print("Exporting Attributes to vtk, path: " + path) + payload = {} + + for k in particulator.attributes.keys(): + if len(particulator.attributes[k].shape) != 1: + tmp = particulator.attributes[k].to_ndarray(raw=True) + tmp_dict = { + k + "[" + str(i) + "]": tmp[i] + for i in range(len(particulator.attributes[k].shape)) + } + + payload.update(tmp_dict) + else: + payload[k] = particulator.attributes[k].to_ndarray(raw=True) + + payload.update( + { + k: np.array(v) + for k, v in payload.items() + if not (v.flags["C_CONTIGUOUS"] or v.flags["F_CONTIGUOUS"]) + } + ) + + if particulator.mesh.dimension == 2: + y = ( + particulator.mesh.size[0] + / particulator.mesh.grid[0] + * (payload["cell origin[0]"] + payload["position in cell[0]"]) + ) + x = ( + particulator.mesh.size[1] + / particulator.mesh.grid[1] + * (payload["cell origin[1]"] + payload["position in cell[1]"]) + ) + z = np.full_like(x, 0) + else: + raise NotImplementedError( + "Only 2 dimensions array is supported at the moment." + ) + + pointsToVTK(path, x, y, z, data=payload) + + def export_products(self, particulator): + if len(particulator.products) != 0: + path = ( + self.products_file_path + + "_num" + + self.add_leading_zeros(particulator.n_steps) + ) + self.exported_times["products"][path] = ( + particulator.n_steps * particulator.dt + ) + if self.verbose: + print("Exporting Products to vtk, path: " + path) + payload = {} + + if particulator.mesh.dimension != 2: + raise NotImplementedError( + "Only 2 dimensions data is supported at the moment." + ) + + data_shape = (particulator.mesh.grid[1], particulator.mesh.grid[0], 1) + + for k in particulator.products.keys(): + v = particulator.products[k].get() + + if isinstance(v, np.ndarray): + if v.shape == particulator.mesh.grid: + payload[k] = v[:, :, np.newaxis].copy() + else: + if self.verbose: + print( + f"{k} shape {v.shape} not equals data shape {data_shape}" + f" and will not be exported", + file=sys.stderr, + ) + elif isinstance(v, numbers.Number): + if self.verbose: + print( + f"{k} is a Number and will not be exported", file=sys.stderr + ) + else: + if self.verbose: + print(f"{k} export is not possible", file=sys.stderr) + + y, x, z = np.mgrid[ + : particulator.mesh.grid[0] + 1, : particulator.mesh.grid[1] + 1, :1 + ] + y = y * particulator.mesh.size[0] / particulator.mesh.grid[0] + x = x * particulator.mesh.size[1] / particulator.mesh.grid[1] + z = z * 1.0 + + gridToVTK(path, x, y, z, cellData=payload) + else: + if self.verbose: + print("No products to export") + + def add_leading_zeros(self, a): + return "".join(["0" for i in range(self.num_len - len(str(a)))]) + str(a) diff --git a/PySDM/source/PySDM/exporters/vtk_exporter_1d.py b/PySDM/source/PySDM/exporters/vtk_exporter_1d.py new file mode 100644 index 0000000000000000000000000000000000000000..7d3c3f94c1240ee584937af997d5a7226d479847 --- /dev/null +++ b/PySDM/source/PySDM/exporters/vtk_exporter_1d.py @@ -0,0 +1,66 @@ +""" +vtk exporter of particle attributes for 1d simulations +""" + +import os + +import numpy as np +from pyevtk.hl import pointsToVTK + + +class VTKExporter_1d: # pylint: disable=too-few-public-methods + def __init__( + self, + data, + settings, + path="./sd_attributes/", + exclude_particle_reservoir=True, + ): + self.data = data + self.settings = settings + self.path = path + if not os.path.isdir(self.path) and len(settings.save_spec_and_attr_times) > 0: + os.mkdir(self.path) + + self.exclude_particle_reservoir = exclude_particle_reservoir + + self.num_len = len(str(settings.t_max)) + + def _export_attributes(self, time_index_and_value): + time_index = time_index_and_value[0] + time = time_index_and_value[1] + path = self.path + "time" + self._add_leading_zeros(time) + + payload = {} + for k in self.data.keys(): + if len(self.data[k][time_index].shape) == 1: + payload[k] = self.data[k][time_index] + elif len(self.data[k][time_index].shape) == 2: + assert self.data[k][time_index].shape[0] == 1 + payload[k] = self.data[k][time_index][0] + else: + raise NotImplementedError("Shape of data array is not recognized.") + + z = ( + self.settings.dz * (payload["cell origin"] + payload["position in cell"]) + - self.settings.particle_reservoir_depth + ) + + if self.exclude_particle_reservoir: + reservoir_particles_indexes = np.where(z < 0) + z = np.delete(z, reservoir_particles_indexes) + keys = payload.keys() + for k in keys: + payload[k] = np.delete(payload[k], reservoir_particles_indexes) + + x = np.full_like(z, 0) + y = np.full_like(z, 0) + + pointsToVTK(path, x, y, z, data=payload) + + def _add_leading_zeros(self, a): + return "".join(["0" for i in range(self.num_len - len(str(a)))]) + str(a) + + def run(self): + for time_index_and_value in enumerate(self.settings.save_spec_and_attr_times): + self._export_attributes(time_index_and_value) diff --git a/PySDM/source/PySDM/exporters/vtk_exporter_parcel.py b/PySDM/source/PySDM/exporters/vtk_exporter_parcel.py new file mode 100644 index 0000000000000000000000000000000000000000..b137ba5291b23a69f6bcea308eebd85064091b8d --- /dev/null +++ b/PySDM/source/PySDM/exporters/vtk_exporter_parcel.py @@ -0,0 +1,151 @@ +""" +VTK Exporter for parcel PySDM simulations. + +This module defines `VTKExporterParcel`, a subclass of `PySDM.exporters.VTKExporter`, +that writes simulation outputs to VTK format using `pyevtk`. It exports product +profiles (e.g., relative humidity) as unstructured grids and particle attributes +as point clouds, along with `.pvd` collection files for time-series visualization +in ParaView. +""" + +from pyevtk.hl import unstructuredGridToVTK, pointsToVTK +from pyevtk.vtk import VtkHexahedron, VtkGroup +import numpy as np + +from PySDM.exporters.vtk_exporter import VTKExporter + + +# pylint: disable=too-many-instance-attributes +class VTKExporterParcel(VTKExporter): + """ + Custom VTK exporter for parcel PySDM, exporting products as grids + and attributes as point clouds for ParaView visualization. + """ + + def __init__(self, n_sd, output, mass_of_dry_air): + super().__init__() + self.output = output + self.coords = { + "x": np.random.random(n_sd), + "y": np.random.random(n_sd), + "z": np.random.random(n_sd), + } + self.half_diagonal = [] + self.n_levels = len(self.output["products"]["z"]) + + volume = mass_of_dry_air / output["products"]["rhod"][0] + delta_z = output["products"]["z"][1] - output["products"]["z"][0] + for level in range(self.n_levels): + if level > 0: + prev_to_curr_density_ratio = ( + output["products"]["rhod"][level - 1] + / output["products"]["rhod"][level] + ) + volume *= prev_to_curr_density_ratio + delta_z = ( + output["products"]["z"][level] - output["products"]["z"][level - 1] + ) + area = volume / delta_z + self.half_diagonal.append((2 * area) ** 0.5) + + def write_pvd(self): + pvd_attributes = VtkGroup(self.attributes_file_path) + for key, value in self.exported_times["attributes"].items(): + pvd_attributes.addFile(key + ".vtu", sim_time=value) + pvd_attributes.save() + + pvd_products = VtkGroup(self.products_file_path) + for key, value in self.exported_times["products"].items(): + pvd_products.addFile(key + ".vtu", sim_time=value) + pvd_products.save() + + def export_products( + self, step, simulation + ): # pylint: disable=arguments-differ, too-many-locals + path = self.products_file_path + "_num" + self.add_leading_zeros(step) + self.exported_times["products"][path] = step * simulation.particulator.dt + + n_vertices = 4 * self.n_levels + x_vertices = np.zeros(n_vertices) + y_vertices = np.zeros(n_vertices) + z_vertices = np.zeros(n_vertices) + connectivity = [0, 1, 2, 3] + cell_type = np.zeros(self.n_levels - 1) + cell_type[:] = VtkHexahedron.tid + + for level in range(self.n_levels): + half_diag = self.half_diagonal[level] + current_z = self.output["products"]["z"][level] + idx = level * 4 + x_vertices[idx], y_vertices[idx], z_vertices[idx] = ( + -half_diag, + -half_diag, + current_z, + ) + idx += 1 + x_vertices[idx], y_vertices[idx], z_vertices[idx] = ( + -half_diag, + half_diag, + current_z, + ) + idx += 1 + x_vertices[idx], y_vertices[idx], z_vertices[idx] = ( + half_diag, + half_diag, + current_z, + ) + idx += 1 + x_vertices[idx], y_vertices[idx], z_vertices[idx] = ( + half_diag, + -half_diag, + current_z, + ) + connectivity += [*range(4 * (level + 1), 4 * (level + 2))] * 2 + connectivity = np.asarray(connectivity[:-4]) + offset = np.asarray(range(8, 8 * self.n_levels, 8)) + + _ = {"test_pd": np.array([44] * n_vertices)} + + relative_humidity = self.output["products"]["S_max_percent"] + cell_data = { + "RH": np.full(shape=(len(relative_humidity) - 1,), fill_value=np.nan) + } + cell_data["RH"][:step] = ( + np.array(relative_humidity[:-1] + np.diff(relative_humidity) / 2) + )[:step] + unstructuredGridToVTK( + path, + x_vertices, + y_vertices, + z_vertices, + connectivity=connectivity, + offsets=offset, + cell_types=cell_type, + cellData=cell_data, + ) + + def export_attributes(self, step, simulation): # pylint: disable=arguments-differ + path = self.attributes_file_path + "_num" + self.add_leading_zeros(step) + self.exported_times["attributes"][path] = step * simulation.particulator.dt + + payload = {} + + for key in self.output["attributes"].keys(): + payload[key] = np.asarray(self.output["attributes"][key])[:, step].copy() + if step != 0: + delta_z = ( + self.output["products"]["z"][step] + - self.output["products"]["z"][step - 1] + ) + if step == 1: + self.coords["z"] *= delta_z + else: + self.coords["z"] += delta_z + + pointsToVTK( + path, + 2 * (self.coords["x"] - 0.5) * self.half_diagonal[step], + 2 * (self.coords["y"] - 0.5) * self.half_diagonal[step], + self.coords["z"], + data=payload, + ) diff --git a/PySDM/source/PySDM/formulae.py b/PySDM/source/PySDM/formulae.py new file mode 100644 index 0000000000000000000000000000000000000000..025e8d30699bc194e4bd46d702c8d5ebca011f77 --- /dev/null +++ b/PySDM/source/PySDM/formulae.py @@ -0,0 +1,401 @@ +""" +Logic for enabling common CPU/GPU physics formulae code +""" + +import inspect +import math +import numbers +import re +import warnings +from collections import namedtuple +from functools import lru_cache, partial, cached_property +from types import SimpleNamespace +from typing import Optional + +import numba +import numpy as np +import pint +from numba.core.errors import NumbaExperimentalFeatureWarning + +from PySDM import physics +from PySDM.backends.impl_numba import conf +from PySDM.dynamics.terminal_velocity import ( + GunnKinzer1949, + PowerSeries, + RogersYau, + ColumnarIceCrystal, + IceSphere, +) +from PySDM.dynamics.terminal_velocity.gunn_and_kinzer import TpDependent + + +class Formulae: # pylint: disable=too-few-public-methods,too-many-instance-attributes,too-many-statements + def __init__( # pylint: disable=too-many-locals + self, + *, + constants: Optional[dict] = None, + seed: int = None, + fastmath: bool = True, + diffusion_coordinate: str = "WaterMassLogarithm", + saturation_vapour_pressure: str = "FlatauWalkoCotton", + latent_heat_vapourisation: str = "Kirchhoff", + latent_heat_sublimation: str = "MurphyKoop2005", + hygroscopicity: str = "KappaKoehlerLeadingTerms", + drop_growth: str = "Mason1971", + surface_tension: str = "Constant", + diffusion_kinetics: str = "FuchsSutugin", + diffusion_ice_kinetics: str = "Standard", + diffusion_ice_capacity: str = "Spherical", + diffusion_thermics: str = "Neglect", + ventilation: str = "Neglect", + state_variable_triplet: str = "LibcloudphPlusPlus", + particle_advection: str = "ImplicitInSpace", + hydrostatics: str = "ConstantGVapourMixingRatioAndThetaStd", + freezing_temperature_spectrum: str = "Null", + heterogeneous_ice_nucleation_rate: str = "Null", + homogeneous_ice_nucleation_rate: str = "Null", + fragmentation_function: str = "AlwaysN", + isotope_equilibrium_fractionation_factors: str = "Null", + isotope_kinetic_fractionation_factors: str = "Null", + isotope_meteoric_water_line: str = "Null", + isotope_ratio_evolution: str = "Null", + isotope_diffusivity_ratios: str = "Null", + isotope_relaxation_timescale: str = "Null", + isotope_temperature_inference: str = "Null", + isotope_ventilation_ratio: str = "Null", + optical_albedo: str = "Null", + optical_depth: str = "Null", + particle_shape_and_density: str = "LiquidSpheres", + terminal_velocity: str = "GunnKinzer1949", + terminal_velocity_ice: str = "ColumnarIceCrystal", + air_dynamic_viscosity: str = "ZografosEtAl1987", + bulk_phase_partitioning: str = "Null", + handle_all_breakups: bool = False, + ): + # initialisation of the fields below is just to silence pylint and to enable code hints + # in PyCharm and alike, all these fields are later overwritten within this ctor + self.optical_albedo = optical_albedo + self.optical_depth = optical_depth + self.diffusion_coordinate = diffusion_coordinate + self.diffusion_coordinate = diffusion_coordinate + self.saturation_vapour_pressure = saturation_vapour_pressure + self.hygroscopicity = hygroscopicity + self.drop_growth = drop_growth + self.surface_tension = surface_tension + self.diffusion_kinetics = diffusion_kinetics + self.diffusion_ice_kinetics = diffusion_ice_kinetics + self.diffusion_ice_capacity = diffusion_ice_capacity + self.latent_heat_vapourisation = latent_heat_vapourisation + self.latent_heat_sublimation = latent_heat_sublimation + self.diffusion_thermics = diffusion_thermics + self.ventilation = ventilation + self.state_variable_triplet = state_variable_triplet + self.particle_advection = particle_advection + self.hydrostatics = hydrostatics + self.freezing_temperature_spectrum = freezing_temperature_spectrum + self.heterogeneous_ice_nucleation_rate = heterogeneous_ice_nucleation_rate + self.homogeneous_ice_nucleation_rate = homogeneous_ice_nucleation_rate + self.fragmentation_function = fragmentation_function + self.isotope_equilibrium_fractionation_factors = ( + isotope_equilibrium_fractionation_factors + ) + self.isotope_kinetic_fractionation_factors = ( + isotope_kinetic_fractionation_factors + ) + self.isotope_meteoric_water_line = isotope_meteoric_water_line + self.isotope_ratio_evolution = isotope_ratio_evolution + self.isotope_diffusivity_ratios = isotope_diffusivity_ratios + self.isotope_relaxation_timescale = isotope_relaxation_timescale + self.isotope_temperature_inference = isotope_temperature_inference + self.isotope_ventilation_ratio = isotope_ventilation_ratio + self.particle_shape_and_density = particle_shape_and_density + self.air_dynamic_viscosity = air_dynamic_viscosity + self.terminal_velocity = terminal_velocity + self.terminal_velocity_ice = terminal_velocity_ice + self.bulk_phase_partitioning = bulk_phase_partitioning + + self._components = tuple( + i + for i in dir(self) + if not i.startswith("__") and i not in ("flatten", "get_constant") + ) + + constants_defaults = { + k: getattr(defaults, k) + for defaults in (physics.constants, physics.constants_defaults) + for k in dir(defaults) + if isinstance( + getattr(defaults, k), (numbers.Number, pint.Quantity, pint.Unit) + ) + } + + physics.constants_defaults.compute_derived_values(constants_defaults) + if constants is not None: + for key in constants: + if key not in constants_defaults: + raise ValueError( + f"constant override provided for unknown key: {key}" + ) + + constants_defaults = {**constants_defaults, **(constants or {})} + physics.constants_defaults.compute_derived_values(constants_defaults) + + constants = namedtuple("Constants", tuple(constants_defaults.keys()))( + **constants_defaults + ) + self.constants = constants + self.seed = seed if seed is not None else physics.constants.default_random_seed + self.fastmath = fastmath + self.handle_all_breakups = handle_all_breakups + dimensional_analysis = physics.impl.flag.DIMENSIONAL_ANALYSIS + + self.trivia = _magick( + "Trivia", physics.trivia, fastmath, constants, dimensional_analysis + ) + + # each `component` corresponds to one subdirectory of PySDM/physics + for component in self._components: + setattr( + self, + component, + _magick( + value=getattr(self, component), + module=getattr(physics, component), + fastmath=fastmath, + constants=constants, + dimensional_analysis=dimensional_analysis, + ), + ) + + # TODO #348 + self.terminal_velocity_class = { + "GunnKinzer1949": GunnKinzer1949, + "RogersYau": RogersYau, + "TpDependent": TpDependent, + "PowerSeries": PowerSeries, + }[terminal_velocity] + self.terminal_velocity_ice_class = { + "ColumnarIceCrystal": ColumnarIceCrystal, + "IceSphere": IceSphere, + }[terminal_velocity_ice] + + def __str__(self): + description = [] + for attr in dir(self): + if not attr.startswith("_") and attr != "flatten": + attr_value = getattr(self, attr) + if attr_value.__class__ in (bool, int, float): + value = attr_value + elif attr_value.__class__.__name__ == "Constants": + value = str(attr_value) + elif attr_value.__class__ in (SimpleNamespace,): + value = attr_value.__name__ + else: + value = attr_value.__class__.__name__ + description.append(f"{attr}: {value}") + return ", ".join(description) + + @cached_property + def flatten(self): + """returns a "flattened" representation providing access to all formulae from within + one Numba-JIT-usable named tuple, e.g. with obj.latent_heat_vapourisation__lv(T) + """ + functions = {} + for component in ["trivia"] + list(self._components): + for item in dir(getattr(self, component)): + attr = getattr(getattr(self, component), item) + if not item.startswith("__") and callable(attr): + functions[component + "__" + item] = attr + for attr in ("constants", "fastmath"): + functions[attr] = getattr(self, attr) + return namedtuple("FlattenedFormulae", functions.keys())(**functions) + + def get_constant(self, key: str): + """getter-like method for cases where using the `constants` named tuple is not possible + (e.g., if calling from a language which does not support named tuples)""" + return getattr(self.constants, key) + + +def _formula(func, constants, dimensional_analysis, **kw): + parameters_keys = tuple(inspect.signature(func).parameters.keys()) + special_params = ("_", "const") + + if dimensional_analysis: + first_param = parameters_keys[0] + if first_param in special_params: + return partial(func, constants) + return func + + source = "class _:\n" + "".join(inspect.getsourcelines(func)[0]) + source = re.sub(r"\(\n\s+", "(", source) + source = re.sub(r"\n\s+\):", "):", source) + loc = {} + for arg_name in special_params: + for sep in ",", ")": + source = source.replace( + f"def {func.__name__}({arg_name}{sep}", + f"def {func.__name__}({')' if sep == ')' else ''}", + ) + + extras = func.__extras if hasattr(func, "__extras") else {} + exec( # pylint:disable=exec-used + source, {"const": constants, "np": np, "math": math, **extras}, loc + ) + + n_params = len(parameters_keys) - (1 if parameters_keys[0] in special_params else 0) + function = getattr(loc["_"], func.__name__) + if hasattr(func, "__vectorize"): + vectorizer = ( + np.vectorize + if numba.config.DISABLE_JIT # pylint: disable=no-member + else numba.vectorize( + "float64(" + ",".join(["float64"] * n_params) + ")", + target="cpu", + nopython=True, + **{ + k: v + for k, v in conf.JIT_FLAGS.items() + if k not in ("parallel", "error_model") + }, + ) + ) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", category=NumbaExperimentalFeatureWarning) + return vectorizer(function) + return numba.njit( + getattr(loc["_"], func.__name__), + **{ + **conf.JIT_FLAGS, + **{"parallel": False, "inline": "always", "cache": False, **kw}, + }, + ) + + +def _boost(obj, fastmath, constants, dimensional_analysis): + """returns JIT-compiled, `c_inline`-equipped formulae with the constants catalogue attached""" + formulae = {"__name__": obj.__name__} + for item in dir(obj): + attr = getattr(obj, item) + if item.startswith("__") or not callable(attr): + pass + else: + formula = _formula( + attr, + constants=constants, + fastmath=fastmath, + dimensional_analysis=dimensional_analysis, + ) + setattr( + formula, "c_inline", partial(_c_inline, constants=constants, fun=attr) + ) + formulae[attr.__name__] = formula + return SimpleNamespace(**formulae) + + +def _c_inline(fun, return_type=None, constants=None, **args): + real_t = "real_type" + return_type = return_type or real_t + prae = r"([,+\-*/( ]|^)" + post = r"([ )/*\-+,]|$)" + real_fmt = ".32g" + source = "" + in_docstring = False + for line in inspect.getsourcelines(fun)[0]: + stripped = line.strip() + if in_docstring: + if stripped.endswith('"""'): + in_docstring = False + continue + if stripped.startswith("@"): + continue + if stripped.startswith("//"): + continue + if stripped.startswith('"""'): + if not (stripped.endswith('"""') and len(stripped) >= 6): + in_docstring = True + continue + if stripped.startswith("def "): + continue + if stripped.endswith(","): + stripped += " " + source += stripped + source = source.replace("np.power(", "np.pow(") + source = source.replace("np.arctanh(", "atanh(") + source = source.replace("np.arcsinh(", "asinh(") + source = source.replace("np.minimum(", "min(") + source = source.replace("np.maximum(", "max(") + for pkg in ("np", "math"): + source = source.replace(f"{pkg}.", "") + source = source.replace(", )", ")") + source = re.sub(pattern="^return ", repl="", string=source) + for arg in inspect.signature(fun).parameters: + if arg not in ("_", "const"): + source = re.sub( + pattern=f"{prae}({arg}){post}", + repl=f"\\1({real_t})({args[arg]})\\3", + string=source, + ) + source = re.sub( + pattern=f"{prae}const\\.([^\\d\\W]\\w*]*){post}", + repl="\\1(" + real_t + ")({constants.\\2:" + real_fmt + "})\\3", + string=source, + ) + assert constants + source = eval(f'f"""{source}"""') # pylint: disable=eval-used + return f"({return_type})({source})" + + +def _pick(value: str, choices: dict, constants: namedtuple): + """ + selects a given physics logic and instantiates it passing the constants catalogue; + `value` is expected to be string containing a plus-separated list of class names + """ + + obj = None + if "+" not in value: + for name, cls in choices.items(): + if name == value: + obj = cls(constants) + break + name = value + else: + parent_classes = [] + for name in value.split("+"): + if name not in choices: + parent_classes.clear() + break + parent_classes.append(choices[name]) + + if len(parent_classes) > 0: + + class Cls(*parent_classes): # pylint: disable=too-few-public-methods + def __init__(self, const): + for cls in parent_classes: + cls.__init__(self, const) + + obj = Cls(constants) + + if obj is None: + raise ValueError( + f"Unknown setting: {name}; choices are: {', '.join(choices.keys())}" + ) + + obj.__name__ = value # pylint: disable=attribute-defined-outside-init + return obj + + +def _choices(module): + return {name: cls for name, cls in module.__dict__.items() if isinstance(cls, type)} + + +@lru_cache() +def _magick(value, module, fastmath, constants, dimensional_analysis): + """ + boosts (`PySDM.formulae.Formulae._boost`) the selected physics logic + """ + return _boost( + _pick(value, _choices(module), constants), + fastmath, + constants, + dimensional_analysis, + ) diff --git a/PySDM/source/PySDM/impl/__init__.py b/PySDM/source/PySDM/impl/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3ecaaaf8afc12564ff7e88d4787a336166e6e9aa --- /dev/null +++ b/PySDM/source/PySDM/impl/__init__.py @@ -0,0 +1 @@ +"""stuff not intended to be used from user code""" diff --git a/PySDM/source/PySDM/impl/arakawa_c.py b/PySDM/source/PySDM/impl/arakawa_c.py new file mode 100644 index 0000000000000000000000000000000000000000..c39e3f0717f54c9f781987003a6764d6245507ae --- /dev/null +++ b/PySDM/source/PySDM/impl/arakawa_c.py @@ -0,0 +1,17 @@ +""" +Arakawa-C staggered spatial grid helper utils +""" + +import numpy as np + + +def z_scalar_coord(grid): + return np.linspace(1 / 2, grid[-1] - 1 / 2, grid[-1]) + + +def make_rhod(grid, rhod_of_zZ): + return np.repeat( + rhod_of_zZ(z_scalar_coord(grid) / grid[-1]).reshape((1, grid[1])), + grid[0], + axis=0, + ) diff --git a/PySDM/source/PySDM/impl/camel_case.py b/PySDM/source/PySDM/impl/camel_case.py new file mode 100644 index 0000000000000000000000000000000000000000..8f4b609d352bdd20dc19f3ea1892aedf77ed2067 --- /dev/null +++ b/PySDM/source/PySDM/impl/camel_case.py @@ -0,0 +1,13 @@ +""" +utility routine converting "CamelCase" strings into space-separated ones (i.e., "camel case") +""" + +import re + +CAMEL_CASE_PATTERN = re.compile(r"[A-Z]?[a-z]+|[A-Z]+(?![^A-Z])") + + +def camel_case_to_words(string: str): + words = CAMEL_CASE_PATTERN.findall(string) + words = (word if word.isupper() else word.lower() for word in words) + return " ".join(words) diff --git a/PySDM/source/PySDM/impl/mesh.py b/PySDM/source/PySDM/impl/mesh.py new file mode 100644 index 0000000000000000000000000000000000000000..b918ef209522107ae8d66fe0e12667931bafad8f --- /dev/null +++ b/PySDM/source/PySDM/impl/mesh.py @@ -0,0 +1,87 @@ +""" +spatial mesh representation (incl. memory strides) +""" + +import numpy as np +from PySDM.physics import si + + +class Mesh: + # pylint: disable=too-many-arguments + def __init__(self, grid, size, n_cell=None, dv=None, n_dims=None, strides=None): + """sizes of dimensions not specified in grid/size are assumed to be of 1 m""" + self.grid = grid + self.size = size + self.strides = strides or Mesh.__strides(grid) + self.n_cell = n_cell or int(np.prod(grid)) + self.dv = dv or np.prod((np.array(size) / np.array(grid))) + self.n_dims = len(grid) if n_dims is None else n_dims + + @property + def dz(self): + return self.size[-1] / self.grid[-1] + + @property + def dimension(self): + return self.n_dims + + @property + def dim(self): + return self.n_dims + + @property + def domain_bottom_surface_area(self): + assert self.n_dims > 0 + return { + 1: 1 * si.m**2, + 2: 1 * si.m * self.size[0], + }[self.n_dims] + + @staticmethod + def mesh_0d(dv=None): + return Mesh( + grid=(1,), size=tuple(), n_cell=1, dv=dv or np.nan, n_dims=0, strides=(-1,) + ) + + @staticmethod + def __strides(grid): + """ + returns strides, i.e.: to compute a `cell_id`, use np.dot(strides, cell_origin) + + returns the stride vector for a given grid (for use in `cell_id` arithmetics where + the stride vector indicates the distances of cell ids adjacent in a given dimension) + """ + domain = np.empty(tuple(grid)) + strides = np.array(domain.strides) // domain.itemsize + if len(grid) == 1: + strides = strides.reshape((1, 1)) + else: + strides = strides.reshape(1, -1) + return strides + + def cellular_attributes(self, positions): + """ + computes values of `cell_id`, `cell_origin` and `position_in_cell` attributes + based on `positions` values passed as input + + takes: + droplet `positions` as input (expressed in grid coordinates, i.e. + in a range of 0 ... 10, 0 ... 5 for a 10x5 grid) + + returns: + a tuple of: + - `cell_id`: scalar integer ids of cells + (each value within the range of 0...prod(grid)) + - `cell_origin`: n-component vector of integer cell coordinates + (each within the range of 0...grid[dim]) + - `position_in_cell`: n-component vector of floats + (each within the range of 0...1) + """ + n_sd = positions.shape[1] + cell_origin = positions.astype(dtype=np.int64) + position_in_cell = positions - np.floor(positions) + + cell_id = np.empty(n_sd, dtype=np.int64) + cell_id[:] = np.dot(self.strides, cell_origin) + + return cell_id, cell_origin, position_in_cell diff --git a/PySDM/source/PySDM/impl/null_physics_class.py b/PySDM/source/PySDM/impl/null_physics_class.py new file mode 100644 index 0000000000000000000000000000000000000000..81aeeeef1d1e2f04edbfcc6248cadd1f15189ffc --- /dev/null +++ b/PySDM/source/PySDM/impl/null_physics_class.py @@ -0,0 +1,8 @@ +""" +do-nothing null default +""" + + +class Null: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass diff --git a/PySDM/source/PySDM/impl/particle_attributes.py b/PySDM/source/PySDM/impl/particle_attributes.py new file mode 100644 index 0000000000000000000000000000000000000000..5e486db29fa78ad28aedf1dd57f971fa2b1a1c7e --- /dev/null +++ b/PySDM/source/PySDM/impl/particle_attributes.py @@ -0,0 +1,125 @@ +""" +logic for handling particle attributes within + `PySDM.particulator.Particulator` +""" + +from typing import Dict + +import numpy as np + +from PySDM.attributes.impl.attribute import Attribute + + +class ParticleAttributes: # pylint: disable=too-many-instance-attributes + def __init__( + self, + *, + particulator, + idx, + extensive_attribute_storage, + extensive_keys: dict, + cell_start, + attributes: Dict[str, Attribute], + ): + self.__valid_n_sd = particulator.n_sd + self.__healthy_memory = particulator.Storage.from_ndarray(np.full((1,), 1)) + self.__idx = idx + + self.__extensive_attribute_storage = extensive_attribute_storage + self.__extensive_keys = extensive_keys + + self.cell_idx = particulator.Index.identity_index(len(cell_start) - 1) + self.__cell_start = particulator.Storage.from_ndarray(cell_start) + self.__cell_caretaker = particulator.backend.make_cell_caretaker( + self.__idx.shape, + self.__idx.dtype, + len(self.__cell_start), + scheme=particulator.sorting_scheme, + ) + self.__sorted = False + self.__attributes = attributes + + @property + def healthy(self) -> bool: + return bool(self.__healthy_memory[0]) + + @healthy.setter + def healthy(self, value: bool): + self.__healthy_memory[:] = value + + @property + def cell_start(self): + if not self.__sorted: + self.__sort_by_cell_id() + return self.__cell_start + + @property + def super_droplet_count(self): + """returns the number of super-droplets in the system + (which might differ from the initial one due to precipitation + or removal during collision of multiplicity-of-one particles)""" + assert self.healthy + return len(self.__idx) + + def mark_updated(self, key): + self.__attributes[key].mark_updated() + + def sanitize(self): + if not self.healthy: + self.__idx.length = self.__valid_n_sd + self.__idx.remove_zero_n_or_flagged(self["multiplicity"]) + self.__valid_n_sd = self.__idx.length + self.healthy = True + self.__sorted = False + + def cut_working_length(self, length): + assert length <= len(self.__idx) + self.__idx.length = length + + def get_working_length(self): + return len(self.__idx) + + def reset_working_length(self): + self.__idx.length = self.__valid_n_sd + + def reset_cell_idx(self): + self.cell_idx.reset_index() + self.__sort_by_cell_id() + + def keys(self): + return self.__attributes.keys() + + def __getitem__(self, item): + return self.__attributes[item].get() + + def __contains__(self, key): + return key in self.__attributes + + def permutation(self, u01, local): + """apply Fisher-Yates algorithm to all super-droplets (local=False) or + otherwise on a per-cell basis""" + if local: + self.__idx.shuffle(u01, parts=self.cell_start) + else: + self.__idx.shuffle(u01) + self.__sorted = False + + def __sort_by_cell_id(self): + self.__cell_caretaker( + self["cell id"], self.cell_idx, self.__cell_start, self.__idx + ) + self.__sorted = True + + def get_extensive_attribute_storage(self): + return self.__extensive_attribute_storage + + def get_extensive_attribute_keys(self): + return self.__extensive_keys.keys() + + def has_attribute(self, attr): + return attr in self.__attributes + + def reset_idx(self): + self.__valid_n_sd = self.__idx.shape[0] + self.__idx.reset_index() + self.healthy = False diff --git a/PySDM/source/PySDM/impl/particle_attributes_factory.py b/PySDM/source/PySDM/impl/particle_attributes_factory.py new file mode 100644 index 0000000000000000000000000000000000000000..f8f1b4546bbfce37c937734f43bfda0c2c7f26f1 --- /dev/null +++ b/PySDM/source/PySDM/impl/particle_attributes_factory.py @@ -0,0 +1,133 @@ +""" +factory logic for creating `PySDM.impl.particle_attributes.ParticleAttributes` instances +""" + +import numpy as np + +from PySDM.attributes.impl import ( + CellAttribute, + DerivedAttribute, + DummyAttribute, + ExtensiveAttribute, + MaximumAttribute, +) +from PySDM.impl.particle_attributes import ParticleAttributes +from PySDM.attributes.impl import get_attribute_class + + +class ParticleAttributesFactory: + @staticmethod + def attributes(particulator, req_attr, attributes): + # pylint: disable=too-many-locals + idx = particulator.Index.identity_index(particulator.n_sd) + + extensive_attr = [] + maximum_attr = [] + for attr_name in req_attr: + if isinstance(req_attr[attr_name], ExtensiveAttribute): + extensive_attr.append(attr_name) + elif isinstance(req_attr[attr_name], MaximumAttribute): + maximum_attr.append(attr_name) + elif not isinstance( + req_attr[attr_name], + ( + DerivedAttribute, + get_attribute_class("multiplicity"), + CellAttribute, + DummyAttribute, + ), + ): + raise AssertionError() + + extensive_attribute_storage = particulator.IndexedStorage.empty( + idx, (len(extensive_attr), particulator.n_sd), float + ) + maximum_attributes = particulator.IndexedStorage.empty( + idx, (len(maximum_attr), particulator.n_sd), float + ) + + for attr in req_attr.values(): + if isinstance(attr, (DerivedAttribute, DummyAttribute)): + if attr.name in attributes: + raise ValueError( + f"attribute '{attr.name}' is a dummy/derived one," + f" but values were provided" + ) + attr.allocate(idx) + + extensive_keys = {} + maximum_keys = {} + + def helper(req_attr, all_attr, names, data, keys): + for i, attr in enumerate(names): + keys[attr] = i + req_attr[attr].set_data(data[i, :]) + try: + req_attr[attr].init(all_attr[attr]) + except KeyError as err: + raise ValueError( + f"attribute '{attr}' requested by one of the components" + f" but no initial values given" + ) from err + + helper( + req_attr, + attributes, + extensive_attr, + extensive_attribute_storage, + extensive_keys, + ) + helper(req_attr, attributes, maximum_attr, maximum_attributes, maximum_keys) + + n = req_attr["multiplicity"] + n.allocate(idx) + n.init(attributes["multiplicity"]) + req_attr["multiplicity"].data = particulator.IndexedStorage.indexed(idx, n.data) + cell_id = req_attr["cell id"] + cell_id.allocate(idx) + cell_id.init(attributes["cell id"]) + req_attr["cell id"].data = particulator.IndexedStorage.indexed( + idx, cell_id.data + ) + try: + cell_origin = req_attr["cell origin"] + cell_origin.allocate(idx) + cell_origin.init(attributes["cell origin"]) + req_attr["cell origin"].data = particulator.IndexedStorage.indexed( + idx, cell_origin.data + ) + except KeyError: + cell_origin = None + try: + position_in_cell = req_attr["position in cell"] + position_in_cell.allocate(idx) + position_in_cell.init(attributes["position in cell"]) + req_attr["position in cell"].data = particulator.IndexedStorage.indexed( + idx, position_in_cell.data + ) + except KeyError: + position_in_cell = None + + cell_start = np.empty(particulator.mesh.n_cell + 1, dtype=int) + + return ParticleAttributes( + particulator=particulator, + idx=idx, + extensive_attribute_storage=extensive_attribute_storage, + extensive_keys=extensive_keys, + # maximum_attributes, maximum_keys, # TODO #594 + cell_start=cell_start, + attributes=req_attr, + ) + + @staticmethod + def empty_particles(particles, n_sd) -> ParticleAttributes: + idx = particles.Index.identity_index(n_sd) + return ParticleAttributes( + particulator=particles, + idx=idx, + extensive_attribute_storage=None, + extensive_keys={}, + cell_start=np.zeros(2, dtype=np.int64), + attributes={}, + ) diff --git a/PySDM/source/PySDM/impl/wall_timer.py b/PySDM/source/PySDM/impl/wall_timer.py new file mode 100644 index 0000000000000000000000000000000000000000..176f0c3b56b5f915b38d4b408703d66e8a451ef0 --- /dev/null +++ b/PySDM/source/PySDM/impl/wall_timer.py @@ -0,0 +1,22 @@ +""" +context manager automating wall-time counting using Python's basic + [time.perf_counter()](https://docs.python.org/3/library/time.html#time.perf_counter) +""" + +import time + + +class WallTimer: + @staticmethod + def __clock(): + return time.perf_counter() + + def __init__(self): + self.time = None + + def __enter__(self): + self.time = self.__clock() + + def __exit__(self, *_): + self.time *= -1 + self.time += self.__clock() diff --git a/PySDM/source/PySDM/initialisation/__init__.py b/PySDM/source/PySDM/initialisation/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..486b127f40189578155c161f7f49af719439262b --- /dev/null +++ b/PySDM/source/PySDM/initialisation/__init__.py @@ -0,0 +1,10 @@ +""" +initialisation logic, particle size spectra, sampling methods and +wet radii equilibration +""" + +from . import sampling, spectra +from .discretise_multiplicities import discretise_multiplicities +from .init_fall_momenta import init_fall_momenta + +from . import aerosol_composition # isort:skip diff --git a/PySDM/source/PySDM/initialisation/aerosol_composition/__init__.py b/PySDM/source/PySDM/initialisation/aerosol_composition/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..9e8b9e7124c534b070cbdea10ee955bafe4a9317 --- /dev/null +++ b/PySDM/source/PySDM/initialisation/aerosol_composition/__init__.py @@ -0,0 +1,6 @@ +""" +classes defining structure for specifying internal and external mixtures of aerosols +and functions for computing bulk properties of the aerosol like kappa +""" + +from .dry_aerosol import DryAerosolMixture diff --git a/PySDM/source/PySDM/initialisation/aerosol_composition/dry_aerosol.py b/PySDM/source/PySDM/initialisation/aerosol_composition/dry_aerosol.py new file mode 100644 index 0000000000000000000000000000000000000000..bb6a7e6f2333190bbb87be30939c70e7af1d45ff --- /dev/null +++ b/PySDM/source/PySDM/initialisation/aerosol_composition/dry_aerosol.py @@ -0,0 +1,116 @@ +""" +`PySDM.initialisation.aerosol_composition.dry_aerosol.DryAerosolMixture` class defines structure +of arbitrary composition aerosol specification. +A `PySDM.initialisation.aerosol_composition.dry_aerosol.DryAerosolMixture` must specify a list of +compounds and the following properties for each compound: +- density +- molar mass +- solubility (True/False) +- number of ions per molecule (phi) + +`PySDM.initialisation.aerosol_composition.dry_aerosol.DryAerosolMixture` class also +contains functions for computing properties like `kappa` from a `mass_fractions` dictionary. +""" + +from typing import Dict, Tuple + +from PySDM import formulae +from PySDM.physics import surface_tension + + +class DryAerosolMixture: + def __init__( + self, + *, + compounds: Tuple[str, ...], + densities: Dict[str, float], + molar_masses: Dict[str, float], + is_soluble: Dict[str, bool], + ionic_dissociation_phi: Dict[str, int], + ): + self._modes = None + self.compounds = compounds + self.densities = densities + self.molar_masses = molar_masses + self.is_soluble = is_soluble + self.ionic_dissociation_phi = ionic_dissociation_phi + + @property + def modes(self): + return self._modes + + @modes.setter + def modes(self, value: Tuple[Dict]): + self._modes = value + + def volume_fractions(self, mass_fractions: dict): + """convert mass fractions to volume fractions""" + return { + k: (mass_fractions[k] / self.densities[k]) + / sum(mass_fractions[i] / self.densities[i] for i in self.compounds) + for k in self.compounds + } + + def f_soluble_volume(self, mass_fractions: dict): + """calculate total volume fraction of soluble species""" + volfrac = self.volume_fractions(mass_fractions) + return sum(self.is_soluble[k] * volfrac[k] for k in self.compounds) + + def volfrac_just_soluble(self, volfrac: dict, soluble=True): + """calculate volume fractions of just soluble or just insoluble species""" + if soluble: + _masked = {k: (self.is_soluble[k]) * volfrac[k] for k in self.compounds} + else: + _masked = {k: (not self.is_soluble[k]) * volfrac[k] for k in self.compounds} + + _denom = sum(list(_masked.values())) + if _denom == 0.0: + x = {k: 0.0 for k in self.compounds} + else: + x = {k: _masked[k] / _denom for k in self.compounds} + return x + + def kappa(self, mass_fractions: dict, water_molar_volume: float): + """calculate hygroscopicities with different assumptions about solubility""" + volfrac = self.volume_fractions(mass_fractions) + molar_volumes = { + i: self.molar_masses[i] / self.densities[i] for i in self.compounds + } + volume_fractions_of_just_soluble = self.volfrac_just_soluble( + volfrac, soluble=True + ) + all_soluble_ns = sum( + self.ionic_dissociation_phi[i] * volfrac[i] / molar_volumes[i] + for i in self.compounds + ) + part_soluble_ns = self.f_soluble_volume(mass_fractions) * sum( + self.ionic_dissociation_phi[i] + * volume_fractions_of_just_soluble[i] + / molar_volumes[i] + for i in self.compounds + ) + + result = {} + for st in formulae._choices(surface_tension).keys(): + if st in (surface_tension.Constant.__name__): + result[st] = all_soluble_ns * water_molar_volume + elif st in ( + surface_tension.CompressedFilmOvadnevaite.__name__, + surface_tension.CompressedFilmRuehl.__name__, + surface_tension.SzyszkowskiLangmuir.__name__, + ): + result[st] = part_soluble_ns * water_molar_volume + else: + raise AssertionError() + return result + + def nu_org(self, mass_fractions: dict): + """calculate molar volume of just organic species""" + volfrac = self.volume_fractions(mass_fractions) + molar_volumes = { + i: self.molar_masses[i] / self.densities[i] for i in self.compounds + } + volume_fractions_of_just_org = self.volfrac_just_soluble(volfrac, soluble=False) + return sum( + volume_fractions_of_just_org[i] * molar_volumes[i] for i in self.compounds + ) diff --git a/PySDM/source/PySDM/initialisation/discretise_multiplicities.py b/PySDM/source/PySDM/initialisation/discretise_multiplicities.py new file mode 100644 index 0000000000000000000000000000000000000000..1daf978cf0607c6f02f86fc14b17c60d5e7cfe92 --- /dev/null +++ b/PySDM/source/PySDM/initialisation/discretise_multiplicities.py @@ -0,0 +1,32 @@ +""" +integer-valued discretisation with sanity checks for errors due to type casting +""" + +import numpy as np + + +def discretise_multiplicities(values_arg): + """any NaN values in the input array are ignored and flagged + with zero multiplicities in the output array""" + + values_int = np.where(np.isnan(values_arg), 0, values_arg).round().astype(np.int64) + + if np.issubdtype(values_arg.dtype, np.floating): + if np.isnan(values_arg).all(): + return values_int + + if not np.logical_or(values_int > 0, np.isnan(values_arg)).all(): + raise ValueError( + f"int-casting resulted in multiplicity of zero (min(y_float)={min(values_arg)})" + ) + + percent_diff = 100 * abs( + 1 - np.nansum(values_arg) / np.sum(values_int.astype(float)) + ) + if percent_diff > 1: + raise ValueError( + f"{percent_diff}% error in total real-droplet number" + f" due to casting multiplicities to ints" + ) + + return values_int diff --git a/PySDM/source/PySDM/initialisation/hygroscopic_equilibrium.py b/PySDM/source/PySDM/initialisation/hygroscopic_equilibrium.py new file mode 100644 index 0000000000000000000000000000000000000000..e5c82db8647971877228ce648c512b81da365dc2 --- /dev/null +++ b/PySDM/source/PySDM/initialisation/hygroscopic_equilibrium.py @@ -0,0 +1,198 @@ +""" +Koehler-curve equilibrium in unsaturated conditions +""" + +import numba +import numpy as np + +from ..backends.impl_numba.conf import JIT_FLAGS +from ..backends.impl_numba.toms748 import toms748_solve +from ..backends.impl_numba.warnings import warn + +default_rtol = 1e-5 +default_max_iters = 64 + +# pylint: disable=too-many-locals,too-many-arguments + + +def _solve_equilibrium_radii( + *, + radii_in: np.ndarray, + get_bounds, + get_args, + minfun, + environment, + kappa, + f_org: np.ndarray, + cell_id: np.ndarray, + rtol: float, + max_iters: int, + skip_fa_lt_zero: bool, + RH_range: tuple = (0, 1), +): + T = environment["T"].to_ndarray() + RH = environment["RH"].to_ndarray() + formulae = environment.particulator.formulae + within_tolerance = formulae.trivia.within_tolerance + jit_flags = {**JIT_FLAGS, **{"fastmath": formulae.fastmath}} + + if cell_id is None: + cell_id = np.zeros_like(radii_in, dtype=int) + if f_org is None: + f_org = np.zeros_like(radii_in, dtype=float) + + @numba.njit(**jit_flags) + def impl(radii_in, iters, T, RH, cell_id, kappa, f_org): + radii_out = np.empty_like(radii_in) + for i in numba.prange(len(radii_in)): # pylint: disable=not-an-iterable + cid = cell_id[i] + a, b = get_bounds(radii_in[i], T[cid], kappa[i]) + + if not a < b: + radii_out[i] = radii_in[i] + iters[i] = 0 + continue + + RH_i = np.maximum(RH_range[0], np.minimum(RH_range[1], RH[cid])) + args = get_args(T[cid], RH_i, kappa[i], radii_in[i], f_org[i]) + fa, fb = minfun(a, *args), minfun(b, *args) + + if skip_fa_lt_zero and fa < 0: + radii_out[i] = radii_in[i] + iters[i] = 0 + continue + + radii_out[i], iters[i] = toms748_solve( + minfun, + args, + a, + b, + fa, + fb, + rtol=rtol, + max_iter=max_iters, + within_tolerance=within_tolerance, + ) + if iters[i] == -1: + warn( + msg="failed to find equilibrium particle size", + file=__file__, + context=( + "i", + i, + "r", + radii_in[i], + "T", + T[cid], + "RH", + RH_i, + "f_org", + f_org[i], + "kappa", + kappa[i], + ), + ) + return radii_out + + iters = np.empty_like(radii_in, dtype=int) + radii_out = impl(radii_in, iters, T, RH, cell_id, kappa, f_org) + assert (iters != max_iters).all() and (iters != -1).all() + return radii_out + + +def equilibrate_dry_radii( + *, + r_wet: np.ndarray, + environment, + kappa: np.ndarray, + f_org: np.ndarray = None, + cell_id: np.ndarray = None, + rtol=default_rtol, + max_iters=default_max_iters, +): + formulae = environment.particulator.formulae + sigma = formulae.surface_tension.sigma + phys_volume = formulae.trivia.volume + const = environment.particulator.formulae.constants + RH_eq = formulae.hygroscopicity.RH_eq + jit_flags = {**JIT_FLAGS, **{"fastmath": formulae.fastmath}} + + @numba.njit(**{**jit_flags, "parallel": False}) + def get_args(T_i, RH_i, kappa, r_wet, f_org): + return T_i, RH_i, kappa, r_wet, f_org + + @numba.njit(**{**jit_flags, "parallel": False}) + def get_bounds(r_wet, _, __): + return 0.0, r_wet + + @numba.njit(**{**jit_flags, "parallel": False}) + def minfun_dry(r_dry, temperature, relative_humidity, kappa, r_wet, f_org): + r_dry_3 = r_dry**3 + sgm = sigma( + temperature, phys_volume(radius=r_wet), const.PI_4_3 * r_dry_3, f_org + ) + return relative_humidity - RH_eq(r_wet, temperature, kappa, r_dry_3, sgm) + + return _solve_equilibrium_radii( + radii_in=r_wet, + get_args=get_args, + get_bounds=get_bounds, + minfun=minfun_dry, + environment=environment, + kappa=kappa, + f_org=f_org, + cell_id=cell_id, + rtol=rtol, + max_iters=max_iters, + skip_fa_lt_zero=False, + ) + + +def equilibrate_wet_radii( + *, + r_dry: np.ndarray, + environment, + kappa_times_dry_volume: np.ndarray, + f_org: np.ndarray = None, + cell_id: np.ndarray = None, + rtol=default_rtol, + max_iters=default_max_iters, +): + formulae = environment.particulator.formulae + sigma = formulae.surface_tension.sigma + phys_volume = formulae.trivia.volume + const = environment.particulator.formulae.constants + RH_eq = formulae.hygroscopicity.RH_eq + r_cr = formulae.hygroscopicity.r_cr + jit_flags = {**JIT_FLAGS, **{"fastmath": formulae.fastmath}} + + @numba.njit(**{**jit_flags, "parallel": False}) + def get_args(T_i, RH_i, kappa, r_dry, f_org): + return T_i, RH_i, kappa, r_dry**3, f_org + + @numba.njit(**{**jit_flags, "parallel": False}) + def get_bounds(r_dry, T_i, kappa): + a = r_dry + b = r_cr(kappa, r_dry**3, T_i, const.sgm_w) + return a, b + + @numba.njit(**{**jit_flags, "parallel": False}) + def minfun_wet(r_wet, temperature, relative_humidity, kappa, r_dry_3, f_org): + sgm = sigma( + temperature, phys_volume(radius=r_wet), const.PI_4_3 * r_dry_3, f_org + ) + return relative_humidity - RH_eq(r_wet, temperature, kappa, r_dry_3, sgm) + + return _solve_equilibrium_radii( + radii_in=r_dry, + get_args=get_args, + get_bounds=get_bounds, + minfun=minfun_wet, + environment=environment, + kappa=kappa_times_dry_volume / phys_volume(radius=r_dry), + f_org=f_org, + cell_id=cell_id, + rtol=rtol, + max_iters=max_iters, + skip_fa_lt_zero=True, + ) diff --git a/PySDM/source/PySDM/initialisation/impl/__init__.py b/PySDM/source/PySDM/initialisation/impl/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..131d44e76dd1a498afaeaccb65db391d37523338 --- /dev/null +++ b/PySDM/source/PySDM/initialisation/impl/__init__.py @@ -0,0 +1,2 @@ +"""commons (incl. `PySDM.initialisation.impl.spectrum.Spectrum` base class) +not intended to be imported from user code""" diff --git a/PySDM/source/PySDM/initialisation/impl/spectrum.py b/PySDM/source/PySDM/initialisation/impl/spectrum.py new file mode 100644 index 0000000000000000000000000000000000000000..875a0245eaa8477466951731c2d61c3d761e859c --- /dev/null +++ b/PySDM/source/PySDM/initialisation/impl/spectrum.py @@ -0,0 +1,37 @@ +""" +logic around `PySDM.initialisation.impl.spectrum.Spectrum` - parent class for most + of the spectra +""" + + +class Spectrum: + def __init__(self, distribution, distribution_params, norm_factor): + self.distribution_params = distribution_params # (loc, scale) + self.norm_factor = norm_factor + self.distribution = distribution + + def size_distribution(self, arg): + result = self.norm_factor * self.distribution.pdf( + arg, *self.distribution_params + ) + return result + + def pdf(self, arg): + return self.size_distribution(arg) / self.norm_factor + + def cdf(self, arg): + return self.distribution.cdf(arg, *self.distribution_params) + + def stats(self, moments): + result = self.distribution.stats(*self.distribution_params, moments) + return result + + def cumulative(self, arg): + result = self.norm_factor * self.distribution.cdf( + arg, *self.distribution_params + ) + return result + + def percentiles(self, cdf_values): + result = self.distribution.ppf(cdf_values, *self.distribution_params) + return result diff --git a/PySDM/source/PySDM/initialisation/init_fall_momenta.py b/PySDM/source/PySDM/initialisation/init_fall_momenta.py new file mode 100644 index 0000000000000000000000000000000000000000..257b14cd96f9a5b987377bce4bef9dc13237dacf --- /dev/null +++ b/PySDM/source/PySDM/initialisation/init_fall_momenta.py @@ -0,0 +1,48 @@ +""" +Initialize the `PySDM.attributes.physics.relative_fall_velocity.RelativeFallMomentum` +of droplets +""" + +import numpy as np + +from PySDM.dynamics.terminal_velocity import GunnKinzer1949 +from PySDM.particulator import Particulator + + +def init_fall_momenta( + water_mass: np.ndarray, + zero: bool = False, + terminal_velocity_approx=GunnKinzer1949, # TODO #1155 +): + """ + Calculate default values of the + `PySDM.attributes.physics.relative_fall_velocity.RelativeFallMomentum` attribute + (needed when using + `PySDM.attributes.physics.relative_fall_velocity.RelativeFallVelocity` attribute) + + Parameters: + - water_mass: a numpy array of superdroplet water masses + + Returns: + - a numpy array of initial momentum values + """ + if zero: + return np.zeros_like(water_mass) + + from PySDM.backends import CPU # pylint: disable=import-outside-toplevel + + particulator = Particulator(0, CPU()) # TODO #1155 + + approximation = terminal_velocity_approx(particulator=particulator) + + volume_arr = particulator.formulae.particle_shape_and_density.mass_to_volume( + water_mass + ) + radii_arr = particulator.formulae.trivia.radius(volume=volume_arr) + radii = particulator.Storage.from_ndarray(radii_arr) + + output = particulator.Storage.empty((len(water_mass),), dtype=float) + + approximation(output=output, radius=radii) + + return output.to_ndarray() * water_mass diff --git a/PySDM/source/PySDM/initialisation/sampling/__init__.py b/PySDM/source/PySDM/initialisation/sampling/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..bcd42d431bc3d22fa3b254f0be449141febb539e --- /dev/null +++ b/PySDM/source/PySDM/initialisation/sampling/__init__.py @@ -0,0 +1 @@ +"""particle attribute sampling logic""" diff --git a/PySDM/source/PySDM/initialisation/sampling/spatial_sampling.py b/PySDM/source/PySDM/initialisation/sampling/spatial_sampling.py new file mode 100644 index 0000000000000000000000000000000000000000..4c5f85d1ff86ede8b00d23c4d4f9a5c892796bdb --- /dev/null +++ b/PySDM/source/PySDM/initialisation/sampling/spatial_sampling.py @@ -0,0 +1,43 @@ +""" +spatial sampling logic (i.e., physical x-y-z coordinates) +""" + +# TODO #305 QUASIRANDOM & GRID +# https://slayoo.github.io/workshop_2019/files/talk_Shima.pdf + + +class Pseudorandom: # pylint: disable=too-few-public-methods + @staticmethod + def sample(*, backend, grid, n_sd, z_part=None, x_part=None): + n_dims = len(grid) + scale_factors = [] + affine_factors = [] + + storage = backend.Storage.empty(n_dims * n_sd, dtype=float) + backend.Random(seed=backend.formulae.seed, size=n_dims * n_sd)(storage) + positions = storage.to_ndarray().reshape(n_dims, n_sd) + + if z_part is None: + scale_factors.append(grid[0]) + affine_factors.append(0) + else: + iz_min = int(grid[0] * z_part[0]) + iz_max = int(grid[0] * z_part[1]) + scale_factors.append(iz_max - iz_min) + affine_factors.append(iz_min) + + if x_part is not None: + ix_min = int(grid[1] * x_part[0]) + ix_max = int(grid[1] * x_part[1]) + scale_factors.append(ix_max - ix_min) + affine_factors.append(ix_min) + else: + if n_dims == 2: + scale_factors.append(grid[1]) + affine_factors.append(0) + + for dim in range(n_dims): + positions[dim, :] *= scale_factors[dim] + positions[dim, :] += affine_factors[dim] + + return positions diff --git a/PySDM/source/PySDM/initialisation/sampling/spectral_sampling.py b/PySDM/source/PySDM/initialisation/sampling/spectral_sampling.py new file mode 100644 index 0000000000000000000000000000000000000000..c1198ec3075008f8b7e969f8232df369398045dc --- /dev/null +++ b/PySDM/source/PySDM/initialisation/sampling/spectral_sampling.py @@ -0,0 +1,153 @@ +""" +spectral discretisation logic incl. linear, logarithmic, and constant-multiplicity + layouts with deterministic, pseudorandom and quasirandom sampling +""" + +import abc +from typing import Optional, Tuple + +import numpy as np +from scipy.interpolate import interp1d + +default_cdf_range = (0.00001, 0.99999) + + +class SpectralSampling: + def __init__( + self, + spectrum, + *, + size_range: Optional[Tuple[float, float]] = None, + error_threshold: Optional[float] = None, + ): + self.spectrum = spectrum + self.error_threshold = error_threshold or 0.01 + + if size_range is None: + self.cdf_range = default_cdf_range + self.size_range = spectrum.percentiles(self.cdf_range) + else: + assert len(size_range) == 2 + assert size_range[0] > 0 + assert size_range[1] > size_range[0] + self.size_range = size_range + self.cdf_range = ( + spectrum.cdf(size_range[0]), + spectrum.cdf(size_range[1]), + ) + + @abc.abstractmethod + def _sample(self, frac_values): + pass + + def _sample_with_grid(self, grid): + x = grid[1:-1:2] + cdf = self.spectrum.cumulative(grid[0::2]) + y_float = cdf[1:] - cdf[0:-1] + + diff = abs(1 - np.sum(y_float) / self.spectrum.norm_factor) + if diff > self.error_threshold: + raise ValueError( + f"{diff * 100:.3g}% error in total real-droplet number due to sampling " + f"({len(x)} samples)" + ) + + return x, y_float + + def sample_deterministic( + self, n_sd, *, backend=None + ): # pylint: disable=unused-argument + return self._sample( + frac_values=np.linspace( + self.cdf_range[0], self.cdf_range[1], num=2 * n_sd + 1 + ) + ) + + def sample_quasirandom(self, n_sd, *, backend): + num_elements = n_sd + storage = backend.Storage.empty(num_elements, dtype=float) + backend.Random(seed=backend.formulae.seed, size=num_elements)(storage) + u01 = storage.to_ndarray() + + frac_values = np.linspace( + self.cdf_range[0], self.cdf_range[1], num=2 * n_sd + 1 + ) + + for i in range(1, len(frac_values) - 1, 2): + frac_values[i] = frac_values[i - 1] + u01[i // 2] * ( + frac_values[i + 1] - frac_values[i - 1] + ) + + return self._sample(frac_values=frac_values) + + def sample_pseudorandom(self, n_sd, *, backend): + num_elements = 2 * n_sd + 1 + storage = backend.Storage.empty(num_elements, dtype=float) + backend.Random(seed=backend.formulae.seed, size=num_elements)(storage) + u01 = storage.to_ndarray() + + frac_values = np.sort( + self.cdf_range[0] + u01 * (self.cdf_range[1] - self.cdf_range[0]) + ) + return self._sample(frac_values=frac_values) + + +class Logarithmic(SpectralSampling): + def _sample(self, frac_values): + grid = np.exp( + (np.log(self.size_range[1]) - np.log(self.size_range[0])) * frac_values + + np.log(self.size_range[0]) + ) + return self._sample_with_grid(grid) + + +class Linear(SpectralSampling): + def _sample(self, frac_values): + grid = self.size_range[0] + frac_values * ( + self.size_range[1] - self.size_range[0] + ) + return self._sample_with_grid(grid) + + +class ConstantMultiplicity(SpectralSampling): + def _sample(self, frac_values): + grid = self.spectrum.percentiles(frac_values) + assert np.isfinite(grid).all() + return self._sample_with_grid(grid) + + +class AlphaSampling(SpectralSampling): + """as in [Matsushima et al. 2023](https://doi.org/10.5194/gmd-16-6211-2023)""" + + def __init__( + self, + spectrum, + *, + alpha, + dist_1_inv, + interp_points, + size_range=None, + error_threshold: Optional[float] = None, + ): + super().__init__( + spectrum, size_range=size_range, error_threshold=error_threshold + ) + self.alpha = alpha + self.dist_0_cdf = self.spectrum.cdf + self.dist_1_inv = dist_1_inv + self.x_prime = np.linspace( + self.size_range[0], self.size_range[1], num=interp_points + ) + + def _sample(self, frac_values): + if self.alpha == 0: + frac_values = self.spectrum.percentiles(frac_values) + elif self.alpha == 1: + frac_values = self.dist_1_inv(frac_values, self.size_range) + else: + sd_cdf = self.dist_0_cdf(self.x_prime) + x_sd_cdf = (1 - self.alpha) * self.x_prime + self.alpha * self.dist_1_inv( + sd_cdf, self.size_range + ) + frac_values = interp1d(sd_cdf, x_sd_cdf)(frac_values) + return self._sample_with_grid(frac_values) diff --git a/PySDM/source/PySDM/initialisation/sampling/spectro_glacial_sampling.py b/PySDM/source/PySDM/initialisation/sampling/spectro_glacial_sampling.py new file mode 100644 index 0000000000000000000000000000000000000000..ef0132e98defa7eba1e90351897bc95e2045c73f --- /dev/null +++ b/PySDM/source/PySDM/initialisation/sampling/spectro_glacial_sampling.py @@ -0,0 +1,46 @@ +""" +two-dimensional sampling for singular immersion freezing: constant-multiplicity + sampling in the freezing-temperature vs. immersed-surface-area phase space +""" + +import numpy as np + +from PySDM.initialisation.sampling.spectral_sampling import default_cdf_range + +DIM_TEMP = 0 +DIM_SURF = 1 +N_DIMS = 2 + + +class SpectroGlacialSampling: # pylint: disable=too-few-public-methods + def __init__(self, *, freezing_temperature_spectrum, insoluble_surface_spectrum): + self.insoluble_surface_spectrum = insoluble_surface_spectrum + self.freezing_temperature_spectrum = freezing_temperature_spectrum + + self.insoluble_surface_range = insoluble_surface_spectrum.percentiles( + default_cdf_range + ) + self.temperature_range = freezing_temperature_spectrum.invcdf( + np.asarray(default_cdf_range), insoluble_surface_spectrum.median + ) + + def sample(self, *, backend, n_sd): + simulated = np.empty((n_sd, N_DIMS)) + + n_elements = n_sd * N_DIMS + storage = backend.Storage.empty(n_elements, dtype=float) + backend.Random(seed=backend.formulae.seed, size=n_elements)(storage) + random_numbers = storage.to_ndarray().reshape(n_sd, N_DIMS) + + simulated[:, DIM_SURF] = self.insoluble_surface_spectrum.percentiles( + random_numbers[:, 0] + ) + simulated[:, DIM_TEMP] = self.freezing_temperature_spectrum.invcdf( + random_numbers[:, 1], simulated[:, DIM_SURF] + ) + + return ( + simulated[:, DIM_TEMP], + simulated[:, DIM_SURF], + np.full((n_sd,), self.insoluble_surface_spectrum.norm_factor / n_sd), + ) diff --git a/PySDM/source/PySDM/initialisation/spectra/__init__.py b/PySDM/source/PySDM/initialisation/spectra/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7895073208e12429b99d934fb4f2382512ee7c7a --- /dev/null +++ b/PySDM/source/PySDM/initialisation/spectra/__init__.py @@ -0,0 +1,11 @@ +""" +classes representing logic around size spectra and more generally +probability density functions (based on SciPy.stats logic) +""" + +from .exponential import Exponential +from .gamma import Gamma +from .gaussian import Gaussian +from .lognormal import Lognormal +from .sum import Sum +from .top_hat import TopHat diff --git a/PySDM/source/PySDM/initialisation/spectra/exponential.py b/PySDM/source/PySDM/initialisation/spectra/exponential.py new file mode 100644 index 0000000000000000000000000000000000000000..468deaf1a2c3ed7aa3e0e809ef595ca98e89ffcd --- /dev/null +++ b/PySDM/source/PySDM/initialisation/spectra/exponential.py @@ -0,0 +1,13 @@ +""" +exponential spectrum implemented using + [SciPy.stats](https://docs.scipy.org/doc/scipy/reference/stats.html) +""" + +from scipy.stats import expon + +from PySDM.initialisation.impl.spectrum import Spectrum + + +class Exponential(Spectrum): + def __init__(self, norm_factor, scale): + super().__init__(expon, (0, scale), norm_factor) # loc # scale = 1/lambda diff --git a/PySDM/source/PySDM/initialisation/spectra/gamma.py b/PySDM/source/PySDM/initialisation/spectra/gamma.py new file mode 100644 index 0000000000000000000000000000000000000000..d7f9f5d2e82af1a458b05ed81a8194c3cc1d2df1 --- /dev/null +++ b/PySDM/source/PySDM/initialisation/spectra/gamma.py @@ -0,0 +1,15 @@ +""" +gamma spectrum implemented using + [SciPy.stats](https://docs.scipy.org/doc/scipy/reference/stats.html) +""" + +from scipy.stats import gamma + +from PySDM.initialisation.impl.spectrum import Spectrum + + +class Gamma(Spectrum): + def __init__(self, norm_factor, k, theta): + super().__init__( + gamma, (k, 0, theta), norm_factor # shape factor # loc # scale + ) diff --git a/PySDM/source/PySDM/initialisation/spectra/gaussian.py b/PySDM/source/PySDM/initialisation/spectra/gaussian.py new file mode 100644 index 0000000000000000000000000000000000000000..3a0db161eb452552aabe1e3b218c5a9baf583884 --- /dev/null +++ b/PySDM/source/PySDM/initialisation/spectra/gaussian.py @@ -0,0 +1,13 @@ +""" +Gaussian/normal spectrum implemented using + [SciPy.stats](https://docs.scipy.org/doc/scipy/reference/stats.html) +""" + +from scipy.stats import norm + +from PySDM.initialisation.impl.spectrum import Spectrum + + +class Gaussian(Spectrum): + def __init__(self, norm_factor, loc, scale): + super().__init__(norm, (loc, scale), norm_factor) # mean # std dev diff --git a/PySDM/source/PySDM/initialisation/spectra/lognormal.py b/PySDM/source/PySDM/initialisation/spectra/lognormal.py new file mode 100644 index 0000000000000000000000000000000000000000..1a318dee909afd2d2a33c747ffb18e6ec0bf57ff --- /dev/null +++ b/PySDM/source/PySDM/initialisation/spectra/lognormal.py @@ -0,0 +1,42 @@ +""" +lognormal spectrum implemented using + [SciPy.stats](https://docs.scipy.org/doc/scipy/reference/stats.html) +""" + +import math + +from scipy.stats import lognorm + +from PySDM.initialisation.impl.spectrum import Spectrum + + +class Lognormal(Spectrum): + def __init__(self, norm_factor: float, m_mode: float, s_geom: float): + """`norm_factor=1` corresponds to standard normalised probability density, + other settings allow to express, e.g., size or mass distributions; + `m_mode` is the median value, `s_geom` is the geometric standard deviation""" + super().__init__(lognorm, (math.log(s_geom), 0, m_mode), norm_factor) + + @property + def s_geom(self): + return math.exp(self.distribution_params[0]) + + @property + def m_mode(self): + return self.distribution_params[2] + + @property + def median(self): + return self.m_mode + + @property + def geometric_mean(self): + return self.s_geom + + def __str__(self): + return ( + f"{self.__class__.__name__}:" + f" (N={self.norm_factor:.3g}," + f" m_mode={self.m_mode:.3g}," + f" s_geom={self.s_geom:.3g})" + ) diff --git a/PySDM/source/PySDM/initialisation/spectra/sum.py b/PySDM/source/PySDM/initialisation/spectra/sum.py new file mode 100644 index 0000000000000000000000000000000000000000..25f626a2596f0787d9b9c6ad0c5c86e7f98dfd39 --- /dev/null +++ b/PySDM/source/PySDM/initialisation/spectra/sum.py @@ -0,0 +1,44 @@ +""" +spectrum defined as a sum of an arbitrary set of + `PySDM.initialisation.impl.spectrum.Spectrum` instances +""" + +import numpy as np +from scipy.interpolate import interp1d + +from PySDM.initialisation.sampling.spectral_sampling import default_cdf_range + +default_interpolation_grid = tuple(np.linspace(*default_cdf_range, 999)) + + +class Sum: + def __init__(self, spectra: tuple, interpolation_grid=None): + interpolation_grid = interpolation_grid or default_interpolation_grid + self.spectra = spectra + self.norm_factor = sum((s.norm_factor for s in self.spectra)) + percentiles = [s.percentiles(interpolation_grid) for s in self.spectra] + cdf_arg = np.zeros(len(interpolation_grid) * len(self.spectra) + 1) + cdf_arg[1:] = np.concatenate(percentiles) + cdf = self.cumulative(cdf_arg) / self.norm_factor + self.inverse_cdf = interp1d(cdf, cdf_arg, bounds_error=False) + + def size_distribution(self, arg): + result = 0.0 + for spectrum in self.spectra: + result += spectrum.size_distribution(arg) + return result + + def cumulative(self, arg): + result = 0.0 + for spectrum in self.spectra: + result += spectrum.cumulative(arg) + return result + + def percentiles(self, cdf_values): + return self.inverse_cdf(cdf_values) + + def pdf(self, arg): + return self.size_distribution(arg) / self.norm_factor + + def cdf(self, arg): + return self.cumulative(arg) / self.norm_factor diff --git a/PySDM/source/PySDM/initialisation/spectra/top_hat.py b/PySDM/source/PySDM/initialisation/spectra/top_hat.py new file mode 100644 index 0000000000000000000000000000000000000000..ddf2347f8d084a523d0484ceda5f2e24c2bdd7c9 --- /dev/null +++ b/PySDM/source/PySDM/initialisation/spectra/top_hat.py @@ -0,0 +1,22 @@ +""" +top-hat spectrum +""" + +import numpy as np + + +class TopHat: + def __init__(self, norm_factor, endpoints): + self.norm_factor = norm_factor + self.endpoints = endpoints + self._mn = endpoints[0] + self._mx = endpoints[1] + + def cumulative(self, arg): + cdf = np.minimum(1, np.maximum(0, (arg - self._mn) / (self._mx - self._mn))) + return self.norm_factor * cdf + + def percentiles(self, cdf_values): + return (self._mx - self._mn) * ( + np.asarray(cdf_values) + self._mn / (self._mx - self._mn) + ) diff --git a/PySDM/source/PySDM/particulator.py b/PySDM/source/PySDM/particulator.py new file mode 100644 index 0000000000000000000000000000000000000000..0f583582eb52d27a0808fdad0a038a6870917995 --- /dev/null +++ b/PySDM/source/PySDM/particulator.py @@ -0,0 +1,575 @@ +""" +The very class exposing `PySDM.particulator.Particulator.run()` method for launching simulations +""" + +import numpy as np + +from PySDM.backends.impl_common.backend_methods import BackendMethods +from PySDM.backends.impl_common.freezing_attributes import ( + SingularAttributes, + TimeDependentAttributes, + TimeDependentHomogeneousAttributes, + ThresholdHomogeneousAndThawAttributes, +) +from PySDM.backends.impl_common.index import make_Index +from PySDM.backends.impl_common.indexed_storage import make_IndexedStorage +from PySDM.backends.impl_common.pair_indicator import make_PairIndicator +from PySDM.backends.impl_common.pairwise_storage import make_PairwiseStorage +from PySDM.impl.particle_attributes import ParticleAttributes + + +class Particulator: # pylint: disable=too-many-public-methods,too-many-instance-attributes + def __init__(self, n_sd, backend): + assert isinstance(backend, BackendMethods) + self.__n_sd = n_sd + + self.backend = backend + self.formulae = backend.formulae + self.environment = None + self.attributes: (ParticleAttributes, None) = None + self.dynamics = {} + self.products = {} + self.observers = [] + self.initialisers = [] + + self.n_steps = 0 + + self.sorting_scheme = "default" + self.condensation_solver = None + + self.Index = make_Index(backend) # pylint: disable=invalid-name + self.PairIndicator = make_PairIndicator(backend) # pylint: disable=invalid-name + self.PairwiseStorage = make_PairwiseStorage( # pylint: disable=invalid-name + backend + ) + self.IndexedStorage = make_IndexedStorage( # pylint: disable=invalid-name + backend + ) + + self.timers = {} + self.null = self.Storage.empty(0, dtype=float) + + def run(self, steps): + if len(self.initialisers) > 0: + self._notify_initialisers() + for _ in range(steps): + for key, dynamic in self.dynamics.items(): + with self.timers[key]: + dynamic() + self.n_steps += 1 + self._notify_observers() + + def _notify_observers(self): + reversed_order_so_that_environment_is_last = reversed(self.observers) + for observer in reversed_order_so_that_environment_is_last: + observer.notify() + + def _notify_initialisers(self): + for initialiser in self.initialisers: + initialiser.setup() + self.initialisers.clear() + + @property + def Storage(self): + return self.backend.Storage + + @property + def Random(self): + return self.backend.Random + + @property + def n_sd(self) -> int: + return self.__n_sd + + @property + def dt(self) -> float: + if self.environment is not None: + return self.environment.dt + return None + + @property + def mesh(self): + if self.environment is not None: + return self.environment.mesh + return None + + def normalize(self, prob, norm_factor): + self.backend.normalize( + prob=prob, + cell_id=self.attributes["cell id"], + cell_idx=self.attributes.cell_idx, + cell_start=self.attributes.cell_start, + norm_factor=norm_factor, + timestep=self.dt, + dv=self.mesh.dv, + ) + + def update_TpRH(self): + self.backend.temperature_pressure_rh( + # input + rhod=self.environment.get_predicted("rhod"), + thd=self.environment.get_predicted("thd"), + water_vapour_mixing_ratio=self.environment.get_predicted( + "water_vapour_mixing_ratio" + ), + # output + T=self.environment.get_predicted("T"), + p=self.environment.get_predicted("p"), + RH=self.environment.get_predicted("RH"), + ) + + def condensation(self, *, rtol_x, rtol_thd, counters, RH_max, success, cell_order): + """Updates droplet volumes by simulating condensation driven by prior changes + in environment thermodynamic state, updates the environment state. + In the case of parcel environment, condensation is driven solely by changes in + the dry-air density (theta and water_vapour_mixing_ratio should not be changed + by other dynamics). + In the case of prescribed-flow/kinematic environments, the dry-air density is + constant in time throughout the simulation. + This function should only change environment's predicted `thd` and + `water_vapour_mixing_ratio` (and not `rhod`). + """ + self.backend.condensation( + solver=self.condensation_solver, + n_cell=self.mesh.n_cell, + cell_start_arg=self.attributes.cell_start, + signed_water_mass=self.attributes["signed water mass"], + multiplicity=self.attributes["multiplicity"], + vdry=self.attributes["dry volume"], + idx=self.attributes._ParticleAttributes__idx, + rhod=self.environment["rhod"], + thd=self.environment["thd"], + water_vapour_mixing_ratio=self.environment["water_vapour_mixing_ratio"], + dv=self.environment.dv, + prhod=self.environment.get_predicted("rhod"), + pthd=self.environment.get_predicted("thd"), + predicted_water_vapour_mixing_ratio=self.environment.get_predicted( + "water_vapour_mixing_ratio" + ), + kappa=self.attributes["kappa"], + f_org=self.attributes["dry volume organic fraction"], + rtol_x=rtol_x, + rtol_thd=rtol_thd, + v_cr=self.attributes["critical volume"], + timestep=self.dt, + counters=counters, + cell_order=cell_order, + RH_max=RH_max, + success=success, + cell_id=self.attributes["cell id"], + reynolds_number=self.attributes["Reynolds number"], + air_density=self.environment["air density"], + air_dynamic_viscosity=self.environment["air dynamic viscosity"], + ) + self.attributes.mark_updated("signed water mass") + + def collision_coalescence_breakup( + self, + *, + enable_breakup, + gamma, + rand, + Ec, + Eb, + fragment_mass, + coalescence_rate, + breakup_rate, + breakup_rate_deficit, + is_first_in_pair, + warn_overflows, + max_multiplicity, + ): + # pylint: disable=too-many-locals + idx = self.attributes._ParticleAttributes__idx + healthy = self.attributes._ParticleAttributes__healthy_memory + cell_id = self.attributes["cell id"] + multiplicity = self.attributes["multiplicity"] + attributes = self.attributes.get_extensive_attribute_storage() + if enable_breakup: + self.backend.collision_coalescence_breakup( + multiplicity=multiplicity, + idx=idx, + attributes=attributes, + gamma=gamma, + rand=rand, + Ec=Ec, + Eb=Eb, + fragment_mass=fragment_mass, + healthy=healthy, + cell_id=cell_id, + coalescence_rate=coalescence_rate, + breakup_rate=breakup_rate, + breakup_rate_deficit=breakup_rate_deficit, + is_first_in_pair=is_first_in_pair, + warn_overflows=warn_overflows, + particle_mass=self.attributes["water mass"], + max_multiplicity=max_multiplicity, + ) + else: + self.backend.collision_coalescence( + multiplicity=multiplicity, + idx=idx, + attributes=attributes, + gamma=gamma, + healthy=healthy, + cell_id=cell_id, + coalescence_rate=coalescence_rate, + is_first_in_pair=is_first_in_pair, + ) + self.attributes.sanitize() + self.attributes.mark_updated("multiplicity") + for key in self.attributes.get_extensive_attribute_keys(): + self.attributes.mark_updated(key) + + def oxidation( + self, + *, + kinetic_consts, + timestep, + equilibrium_consts, + dissociation_factors, + do_chemistry_flag, + ): + self.backend.oxidation( + n_sd=self.n_sd, + cell_ids=self.attributes["cell id"], + do_chemistry_flag=do_chemistry_flag, + k0=kinetic_consts["k0"], + k1=kinetic_consts["k1"], + k2=kinetic_consts["k2"], + k3=kinetic_consts["k3"], + K_SO2=equilibrium_consts["K_SO2"], + K_HSO3=equilibrium_consts["K_HSO3"], + dissociation_factor_SO2=dissociation_factors["SO2"], + timestep=timestep, + # input + droplet_volume=self.attributes["volume"], + pH=self.attributes["pH"], + # output + moles_O3=self.attributes["moles_O3"], + moles_H2O2=self.attributes["moles_H2O2"], + moles_S_IV=self.attributes["moles_S_IV"], + moles_S_VI=self.attributes["moles_S_VI"], + ) + for attr in ("moles_S_IV", "moles_S_VI", "moles_H2O2", "moles_O3"): + self.attributes.mark_updated(attr) + + def dissolution( + self, + *, + gaseous_compounds, + system_type, + dissociation_factors, + timestep, + environment_mixing_ratios, + do_chemistry_flag, + ): + self.backend.dissolution( + n_cell=self.mesh.n_cell, + n_threads=1, + cell_order=np.arange(self.mesh.n_cell), + cell_start_arg=self.attributes.cell_start, + idx=self.attributes._ParticleAttributes__idx, + do_chemistry_flag=do_chemistry_flag, + mole_amounts={ + key: self.attributes["moles_" + key] for key in gaseous_compounds.keys() + }, + env_mixing_ratio=environment_mixing_ratios, + # note: assuming condensation was called + env_p=self.environment.get_predicted("p"), + env_T=self.environment.get_predicted("T"), + env_rho_d=self.environment.get_predicted("rhod"), + timestep=timestep, + dv=self.mesh.dv, + droplet_volume=self.attributes["volume"], + multiplicity=self.attributes["multiplicity"], + system_type=system_type, + dissociation_factors=dissociation_factors, + ) + for key in gaseous_compounds.keys(): + self.attributes.mark_updated(f"moles_{key}") + + def chem_recalculate_cell_data(self, *, equilibrium_consts, kinetic_consts): + self.backend.chem_recalculate_cell_data( + equilibrium_consts=equilibrium_consts, + kinetic_consts=kinetic_consts, + temperature=self.environment.get_predicted("T"), + ) + + def chem_recalculate_drop_data(self, *, dissociation_factors, equilibrium_consts): + self.backend.chem_recalculate_drop_data( + dissociation_factors=dissociation_factors, + equilibrium_consts=equilibrium_consts, + pH=self.attributes["pH"], + cell_id=self.attributes["cell id"], + ) + + def recalculate_cell_id(self): + if not self.attributes.has_attribute("cell origin"): + return + self.backend.cell_id( + self.attributes["cell id"], + self.attributes["cell origin"], + self.backend.Storage.from_ndarray(self.environment.mesh.strides), + ) + self.attributes._ParticleAttributes__sorted = False + + def sort_within_pair_by_attr(self, is_first_in_pair, attr_name): + self.backend.sort_within_pair_by_attr( + self.attributes._ParticleAttributes__idx, + is_first_in_pair, + self.attributes[attr_name], + ) + + def moments( + self, + *, + moment_0, + moments, + specs: dict, + attr_name="signed water mass", + attr_range=(-np.inf, np.inf), + weighting_attribute="water mass", + weighting_rank=0, + skip_division_by_m0=False, + ): + """ + Writes to `moment_0` and `moment` the zero-th and the k-th statistical moments + of particle attributes computed filtering by value of the attribute `attr_name` + to fall within `attr_range`. The moment ranks are defined by `specs`. + + Parameters: + specs: e.g., `specs={'volume': (1,2,3), 'kappa': (1)}` computes three moments + of volume and one moment of kappa + skip_division_by_m0: if set to `True`, the values written to `moments` are + multiplied by the 0-th moment (e.g., total volume instead of mean volume) + """ + if len(specs) == 0: + raise ValueError("empty specs passed") + attr_data, ranks = [], [] + for attr in specs: + for rank in specs[attr]: + attr_data.append(self.attributes[attr]) + ranks.append(rank) + assert len(set(attr_data)) <= 1 + if len(attr_data) == 0: + attr_data = self.backend.Storage.empty((0,), dtype=float) + else: + attr_data = attr_data[0] + + ranks = self.backend.Storage.from_ndarray(np.array(ranks, dtype=float)) + + self.backend.moments( + moment_0=moment_0, + moments=moments, + multiplicity=self.attributes["multiplicity"], + attr_data=attr_data, + cell_id=self.attributes["cell id"], + idx=self.attributes._ParticleAttributes__idx, + length=self.attributes.super_droplet_count, + ranks=ranks, + min_x=attr_range[0], + max_x=attr_range[1], + x_attr=self.attributes[attr_name], + weighting_attribute=self.attributes[weighting_attribute], + weighting_rank=weighting_rank, + skip_division_by_m0=skip_division_by_m0, + ) + + def spectrum_moments( + self, + *, + moment_0, + moments, + attr, + rank, + attr_bins, + attr_name="water mass", + weighting_attribute="water mass", + weighting_rank=0, + ): + attr_data = self.attributes[attr] + self.backend.spectrum_moments( + moment_0=moment_0, + moments=moments, + multiplicity=self.attributes["multiplicity"], + attr_data=attr_data, + cell_id=self.attributes["cell id"], + idx=self.attributes._ParticleAttributes__idx, + length=self.attributes.super_droplet_count, + rank=rank, + x_bins=attr_bins, + x_attr=self.attributes[attr_name], + weighting_attribute=self.attributes[weighting_attribute], + weighting_rank=weighting_rank, + ) + + def adaptive_sdm_end(self, dt_left): + return self.backend.adaptive_sdm_end(dt_left, self.attributes.cell_start) + + def remove_precipitated( + self, *, displacement, precipitation_counting_level_index + ) -> float: + rainfall_mass = self.backend.flag_precipitated( + cell_origin=self.attributes["cell origin"], + position_in_cell=self.attributes["position in cell"], + water_mass=self.attributes["water mass"], + multiplicity=self.attributes["multiplicity"], + idx=self.attributes._ParticleAttributes__idx, + length=self.attributes.super_droplet_count, + healthy=self.attributes._ParticleAttributes__healthy_memory, + precipitation_counting_level_index=precipitation_counting_level_index, + displacement=displacement, + ) + self.attributes.sanitize() + return rainfall_mass + + def flag_out_of_column(self): + self.backend.flag_out_of_column( + cell_origin=self.attributes["cell origin"], + position_in_cell=self.attributes["position in cell"], + idx=self.attributes._ParticleAttributes__idx, + length=self.attributes.super_droplet_count, + healthy=self.attributes._ParticleAttributes__healthy_memory, + domain_top_level_index=self.mesh.grid[-1], + ) + self.attributes.sanitize() + + def calculate_displacement( + self, *, displacement, courant, cell_origin, position_in_cell, n_substeps + ): + for dim in range(len(self.environment.mesh.grid)): + self.backend.calculate_displacement( + dim=dim, + displacement=displacement, + courant=courant[dim], + cell_origin=cell_origin, + position_in_cell=position_in_cell, + n_substeps=n_substeps, + ) + + def isotopic_fractionation(self, heavy_isotopes: tuple): + self.backend.isotopic_fractionation() + for isotope in heavy_isotopes: + self.attributes.mark_updated(f"moles_{isotope}") + + def seeding( + self, + *, + seeded_particle_index, + seeded_particle_multiplicity, + seeded_particle_extensive_attributes, + number_of_super_particles_to_inject, + ): + n_null = self.n_sd - self.attributes.super_droplet_count + if n_null == 0: + raise ValueError( + "No available seeds to inject. Please provide particles with nan filled attributes." + ) + + if number_of_super_particles_to_inject > n_null: + raise ValueError( + "Trying to inject more super particles than space available." + ) + + if number_of_super_particles_to_inject > len(seeded_particle_multiplicity): + raise ValueError( + "Trying to inject multiple super particles with the same attributes. \ + Instead increase multiplicity of injected particles." + ) + + self.backend.seeding( + idx=self.attributes._ParticleAttributes__idx, + multiplicity=self.attributes["multiplicity"], + extensive_attributes=self.attributes.get_extensive_attribute_storage(), + seeded_particle_index=seeded_particle_index, + seeded_particle_multiplicity=seeded_particle_multiplicity, + seeded_particle_extensive_attributes=seeded_particle_extensive_attributes, + number_of_super_particles_to_inject=number_of_super_particles_to_inject, + ) + self.attributes.reset_idx() + self.attributes.sanitize() + + self.attributes.mark_updated("multiplicity") + for key in self.attributes.get_extensive_attribute_keys(): + self.attributes.mark_updated(key) + + def deposition(self, adaptive: bool): + self.backend.deposition( + adaptive=adaptive, + multiplicity=self.attributes["multiplicity"], + signed_water_mass=self.attributes["signed water mass"], + current_vapour_mixing_ratio=self.environment["water_vapour_mixing_ratio"], + current_dry_air_density=self.environment["rhod"], + current_dry_potential_temperature=self.environment["thd"], + cell_volume=self.environment.mesh.dv, + time_step=self.dt, + cell_id=self.attributes["cell id"], + predicted_vapour_mixing_ratio=self.environment.get_predicted( + "water_vapour_mixing_ratio" + ), + predicted_dry_potential_temperature=self.environment.get_predicted("thd"), + predicted_dry_air_density=self.environment.get_predicted("rhod"), + ) + self.attributes.mark_updated("signed water mass") + # TODO #1524 - should we update here? + # self.update_TpRH(only_if_not_last='VapourDepositionOnIce') + + def immersion_freezing_time_dependent(self, *, rand: Storage): + self.backend.immersion_freezing_time_dependent( + rand=rand, + attributes=TimeDependentAttributes( + immersed_surface_area=self.attributes["immersed surface area"], + signed_water_mass=self.attributes["signed water mass"], + ), + timestep=self.dt, + cell=self.attributes["cell id"], + a_w_ice=self.environment["a_w_ice"], + relative_humidity=self.environment["RH"], + ) + self.attributes.mark_updated("signed water mass") + + def immersion_freezing_singular(self): + self.backend.immersion_freezing_singular( + attributes=SingularAttributes( + freezing_temperature=self.attributes["freezing temperature"], + signed_water_mass=self.attributes["signed water mass"], + ), + temperature=self.environment["T"], + relative_humidity=self.environment["RH"], + cell=self.attributes["cell id"], + ) + self.attributes.mark_updated("signed water mass") + + def homogeneous_freezing_time_dependent(self, *, rand: Storage): + self.backend.homogeneous_freezing_time_dependent( + rand=rand, + attributes=TimeDependentHomogeneousAttributes( + volume=self.attributes["volume"], + signed_water_mass=self.attributes["signed water mass"], + ), + timestep=self.dt, + cell=self.attributes["cell id"], + a_w_ice=self.environment["a_w_ice"], + temperature=self.environment["T"], + relative_humidity_ice=self.environment["RH_ice"], + ) + + def homogeneous_freezing_threshold(self): + self.backend.homogeneous_freezing_threshold( + attributes=ThresholdHomogeneousAndThawAttributes( + signed_water_mass=self.attributes["signed water mass"], + ), + cell=self.attributes["cell id"], + temperature=self.environment["T"], + relative_humidity_ice=self.environment["RH_ice"], + ) + + def thaw_instantaneous(self): + self.backend.thaw_instantaneous( + attributes=ThresholdHomogeneousAndThawAttributes( + signed_water_mass=self.attributes["signed water mass"], + ), + cell=self.attributes["cell id"], + temperature=self.environment["T"], + ) diff --git a/PySDM/source/PySDM/physics/__init__.py b/PySDM/source/PySDM/physics/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..82cabe3bd5e1eb4e102990d1c19a02580d21c464 --- /dev/null +++ b/PySDM/source/PySDM/physics/__init__.py @@ -0,0 +1,58 @@ +""" +Physical constants and formulae (mostly one-liners) + that can be automatically either njit-ted or translated to C (in contrast to + more complex code that resides in backends). Note that any code here should + only use constants defined in `PySDM.physics.constants_defaults` and accessible + through the first `const` argument of each formula, for the following reasons: + - it makes it possible for a user to override value of any constant by + passing a `constants` dictionary to the `__init__` of `Formulae`; + - it enforces floating-point cast on all constant values making the code behave + in the same way on both CPU and GPU backends (yes, please use `const.ONE/const.TWO` + instead of `1/2`); + - it enables dimensional analysis logic if the code in question is embedded + in `with DimensionalAnalysis:` block - allows checking physical unit consistency + within unit tests (disable by default, no runtime overhead); + - last but not least, it requires all the constants to be named + (thus resulting in more readable, and more reusable code). +""" + +from . import ( + diffusion_coordinate, + constants_defaults, + diffusion_kinetics, + diffusion_ice_kinetics, + diffusion_ice_capacity, + diffusion_thermics, + drop_growth, + fragmentation_function, + freezing_temperature_spectrum, + heterogeneous_ice_nucleation_rate, + homogeneous_ice_nucleation_rate, + hydrostatics, + hygroscopicity, + impl, + isotope_equilibrium_fractionation_factors, + isotope_kinetic_fractionation_factors, + isotope_meteoric_water_line, + isotope_ratio_evolution, + isotope_diffusivity_ratios, + isotope_relaxation_timescale, + isotope_temperature_inference, + isotope_ventilation_ratio, + latent_heat_vapourisation, + latent_heat_sublimation, + optical_albedo, + optical_depth, + particle_advection, + particle_shape_and_density, + saturation_vapour_pressure, + state_variable_triplet, + surface_tension, + trivia, + ventilation, + air_dynamic_viscosity, + terminal_velocity, + terminal_velocity_ice, + bulk_phase_partitioning, +) +from .constants import convert_to, in_unit, si diff --git a/PySDM/source/PySDM/physics/air_dynamic_viscosity/__init__.py b/PySDM/source/PySDM/physics/air_dynamic_viscosity/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..194432c84c6451b5e4fee860250ba387268c7cdb --- /dev/null +++ b/PySDM/source/PySDM/physics/air_dynamic_viscosity/__init__.py @@ -0,0 +1,3 @@ +"""air dynamic viscosity formulae""" + +from .zografos_et_al_1987 import ZografosEtAl1987 diff --git a/PySDM/source/PySDM/physics/air_dynamic_viscosity/zografos_et_al_1987.py b/PySDM/source/PySDM/physics/air_dynamic_viscosity/zografos_et_al_1987.py new file mode 100644 index 0000000000000000000000000000000000000000..be3266106afb33fb34f4434e579095e9d1a66fad --- /dev/null +++ b/PySDM/source/PySDM/physics/air_dynamic_viscosity/zografos_et_al_1987.py @@ -0,0 +1,21 @@ +""" +calculate dynamic viscosity of Earth air +from [Zografos et al. (1987)](doi:10.1016/0045-7825(87)90003-X) Table 1 +(note labeled as μ not η there) +fit for T ∈ [100-3000] K +neglects effects of pressure +""" + + +class ZografosEtAl1987: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def eta_air(const, temperature): + return ( + const.ZOGRAFOS_1987_COEFF_T3 * temperature**3 + + const.ZOGRAFOS_1987_COEFF_T2 * temperature**2 + + const.ZOGRAFOS_1987_COEFF_T1 * temperature + + const.ZOGRAFOS_1987_COEFF_T0 + ) diff --git a/PySDM/source/PySDM/physics/bulk_phase_partitioning/__init__.py b/PySDM/source/PySDM/physics/bulk_phase_partitioning/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3bed092f452aa853e2012c7d3c62b38e8679de61 --- /dev/null +++ b/PySDM/source/PySDM/physics/bulk_phase_partitioning/__init__.py @@ -0,0 +1,4 @@ +"""phase partitioning formulae for bulk description of cloud water""" + +from PySDM.impl.null_physics_class import Null +from .kaul_et_al_2015 import KaulEtAl2015 diff --git a/PySDM/source/PySDM/physics/bulk_phase_partitioning/kaul_et_al_2015.py b/PySDM/source/PySDM/physics/bulk_phase_partitioning/kaul_et_al_2015.py new file mode 100644 index 0000000000000000000000000000000000000000..cf99036dae84998cc03b138b0cd698c326ee9514 --- /dev/null +++ b/PySDM/source/PySDM/physics/bulk_phase_partitioning/kaul_et_al_2015.py @@ -0,0 +1,27 @@ +""" +Eq. 1 in [Kaul et al. 2015](https://doi.org/10.1175/MWR-D-14-00319.1) +""" + +import numpy as np + + +class KaulEtAl2015: # pylint: disable=too-few-public-methods + def __init__(self, const): + assert np.isfinite(const.bulk_phase_partitioning_exponent) + + @staticmethod + def liquid_fraction(const, T): + return np.minimum( + 1, + np.power( + np.maximum( + 0, + (T - const.bulk_phase_partitioning_T_cold) + / ( + const.bulk_phase_partitioning_T_warm + - const.bulk_phase_partitioning_T_cold + ), + ), + const.bulk_phase_partitioning_exponent, + ), + ) diff --git a/PySDM/source/PySDM/physics/constants.py b/PySDM/source/PySDM/physics/constants.py new file mode 100644 index 0000000000000000000000000000000000000000..16ab1a378a31b17c8b3404a79d1320bbccb3cd9a --- /dev/null +++ b/PySDM/source/PySDM/physics/constants.py @@ -0,0 +1,74 @@ +""" +Collection of constants with dimensional analysis handled with +[Pint](https://pypi.org/project/Pint/)'s package `UnitRegistry` for test +purposes and mocked with `PySDM.physics.impl.fake_unit_registry.FakeUnitRegistry` by default. +""" + +import os +import time + +import numpy as np +import pint +from scipy import constants as sci + +from .impl.fake_unit_registry import FakeUnitRegistry +from .impl.flag import DIMENSIONAL_ANALYSIS + +si = pint.UnitRegistry() +if not DIMENSIONAL_ANALYSIS: + si = FakeUnitRegistry(si) + + +def convert_to(value, unit): + value /= unit + + +def in_unit(value, unit): + return value / unit + + +sqrt_two = np.sqrt(2) +sqrt_pi = np.sqrt(sci.pi) +PI = sci.pi +PI_4_3 = PI * 4 / 3 +LN_2 = np.log(2) +ZERO_MASS = 0 * si.kg +ZERO_VOLUME = 0 * si.m**3 +ZERO = 0 +ONE = 1 +TWO = 2 +THREE = 3 +FOUR = 4 +NINE = 9 +TWELVE = 12 +ONE_THIRD = 1 / 3 +ONE_HALF = 1 / 2 +TWO_THIRDS = 2 / 3 +ONE_AND_A_HALF = 3 / 2 +TWO_AND_A_HALF = 5 / 2 +NaN = np.nan + +default_random_seed = ( + 44 + if "CI" in os.environ # https://en.wikipedia.org/wiki/44_(number) + else time.time_ns() +) + +PPT = 1e-12 +PPB = 1e-9 +PPM = 1e-6 + +PER_CENT = 1e-2 +PER_MILLE = 1e-3 +PER_MEG = PPM + +T0 = sci.zero_Celsius * si.kelvin + +# there are so few water ions instead of K we have K [H2O], see [Seinfeld & Pandis, p. 345]( +# https://archive.org/details/0237-pdf-atmospheric-chemistry-and-physics-2nd-ed-j.-seinfeld-s.-pandis-wiley-2006-ww +# ) +M = si.mole / si.litre +K_H2O = 1e-14 * M * M + +CM = 1 * si.cm +UM = 1 * si.um diff --git a/PySDM/source/PySDM/physics/constants_defaults.py b/PySDM/source/PySDM/physics/constants_defaults.py new file mode 100644 index 0000000000000000000000000000000000000000..963b00f1c27dafa1cfd3070a4986978e0267e80a --- /dev/null +++ b/PySDM/source/PySDM/physics/constants_defaults.py @@ -0,0 +1,835 @@ +""" +Default values for constants which can be altered by providing alternative + values in a constants dictionary passed to Formulae __init__ method. +Unless, there is a very specific and sound reason, everything here should + be provided in SI units. +""" + +import numpy as np +from scipy import constants as sci + +from .constants import ( # pylint: disable=unused-import + FOUR, + ONE_THIRD, + PER_CENT, + PER_MEG, + PER_MILLE, + PI, + PI_4_3, + PPM, + T0, + ONE, + ONE_HALF, + TWO_THIRDS, + TWO, + THREE, + NINE, + M, + si, +) +from .trivia import Trivia + +Md = 28.966 * si.g / si.mole +""" +A "twenty-first century" value of dry-air molar mass recommended in +[Gatley et al. 2008](https://doi.org/10.1080/10789669.2008.10391032) +""" + +VSMOW_R_2H = 155.76 * PPM +""" +[IAEA VSMOW-SLAP](https://web.archive.org/web/20200729203147/https://nucleus.iaea.org/rpst/documents/VSMOW_SLAP.pdf) +heavy-to-light isotope abundance ratio for deuterium +""" # pylint: disable=line-too-long +VSMOW_R_3H = 1.85e-11 * PPM +""" +〃 for tritium +""" +VSMOW_R_18O = 2005.20 * PPM +""" +〃 for oxygen-18 +""" +VSMOW_R_17O = 379.9 * PPM +""" +〃 for oxygen-17 +""" + +M_1H = 1.00782503224 * si.g / si.mole +""" +hydrogen atomic weight as in +[NIST database](https://physics.nist.gov/cgi-bin/Compositions/stand_alone.pl?ele=H) +""" +M_2H = 2.01410177812 * si.g / si.mole +""" +deuterium atomic weight as in +[NIST database](https://physics.nist.gov/cgi-bin/Compositions/stand_alone.pl?ele=H) +""" +M_3H = 3.01604927792 * si.g / si.mole +""" +tritium atomic weight as in +[NIST database](https://physics.nist.gov/cgi-bin/Compositions/stand_alone.pl?ele=H) +""" + +M_16O = 15.99491461957 * si.g / si.mole +""" +oxygen-16 atomic weight as in +[NIST database](https://physics.nist.gov/cgi-bin/Compositions/stand_alone.pl?ele=O) +""" +M_17O = 16.99913175651 * si.g / si.mole +""" +oxygen-17 atomic weight as in +[NIST database](https://physics.nist.gov/cgi-bin/Compositions/stand_alone.pl?ele=O) +""" +M_18O = 17.99915961287 * si.g / si.mole +""" +oxygen-18 atomic weight as in +[NIST database](https://physics.nist.gov/cgi-bin/Compositions/stand_alone.pl?ele=O) +""" + +R_str = sci.R * si.joule / si.kelvin / si.mole +""" universal gas constant (value from SciPy) """ + +N_A = sci.N_A / si.mole +""" Avogadro constant (value from SciPy) """ + +MAC = 1.0 +""" mass accommodation coefficient of unity as recommended in +[Laaksonen et al. 2005](https://doi.org/10.5194/acp-5-461-2005) """ +HAC = 1.0 +""" thermal accommodation coefficient of unity as recommended in +[Laaksonen et al. 2005](https://doi.org/10.5194/acp-5-461-2005) """ + +MAC_ice = 0.5 +""" mass accommodation coefficient for vapour deposition as recommended in +[Kaercher & Lohmann 2002](https://doi.org/10.1029/2001JD000470) """ +HAC_ice = 1.0 +""" thermal accommodation coefficient for vapour deposition as recommended in +[Pruppacher & Klett](https://doi.org/10.1007/978-0-306-48100-0) """ + +C_cunn = 0.7 +""" Cunningham correction factor as used in +[Spichtinger & Gierens 2009](https://doi.org/10.5194/acp-9-685-2009) """ + +ARM_C1 = 6.1094 * si.hectopascal +""" [August](https://doi.org/10.1002/andp.18280890511) Roche Magnus formula coefficients +(values from [Alduchov & Eskridge 1996](https://doi.org/10.1175%2F1520-0450%281996%29035%3C0601%3AIMFAOS%3E2.0.CO%3B2)) +""" # pylint: disable=line-too-long +ARM_C2 = 17.625 * si.dimensionless +""" 〃 """ +ARM_C3 = 243.04 * si.kelvin +""" 〃 """ + +FWC_C0 = 6.115836990e000 * si.hPa +""" [Flatau et al. 1992](https://doi.org/10.1175/1520-0450(1992)031%3C1507:PFTSVP%3E2.0.CO;2) +polynomial fit coefficients """ +FWC_C1 = 0.444606896e000 * si.hPa / si.K +""" 〃 """ +FWC_C2 = 0.143177157e-01 * si.hPa / si.K**2 +""" 〃 """ +FWC_C3 = 0.264224321e-03 * si.hPa / si.K**3 +""" 〃 """ +FWC_C4 = 0.299291081e-05 * si.hPa / si.K**4 +""" 〃 """ +FWC_C5 = 0.203154182e-07 * si.hPa / si.K**5 +""" 〃 """ +FWC_C6 = 0.702620698e-10 * si.hPa / si.K**6 +""" 〃 """ +FWC_C7 = 0.379534310e-13 * si.hPa / si.K**7 +""" 〃 """ +FWC_C8 = -0.321582393e-15 * si.hPa / si.K**8 +""" 〃 """ +FWC_I0 = 6.098689930e000 * si.hPa +""" 〃 """ +FWC_I1 = 0.499320233e000 * si.hPa / si.K +""" 〃 """ +FWC_I2 = 0.184672631e-01 * si.hPa / si.K**2 +""" 〃 """ +FWC_I3 = 0.402737184e-03 * si.hPa / si.K**3 +""" 〃 """ +FWC_I4 = 0.565392987e-05 * si.hPa / si.K**4 +""" 〃 """ +FWC_I5 = 0.521693933e-07 * si.hPa / si.K**5 +""" 〃 """ +FWC_I6 = 0.307839583e-09 * si.hPa / si.K**6 +""" 〃 """ +FWC_I7 = 0.105785160e-11 * si.hPa / si.K**7 +""" 〃 """ +FWC_I8 = 0.161444444e-14 * si.hPa / si.K**8 +""" 〃 """ + +L77W_A0 = 6.107799961 * si.hPa +""" polynomial fits from +[Lowe et al. 1977](https://doi.org/10.1175/1520-0450(1977)016%3C0100:AAPFTC%3E2.0.CO;2) """ +L77W_A1 = 4.436518521e-1 * si.hPa / si.K +""" 〃 """ +L77W_A2 = 1.428945805e-2 * si.hPa / si.K**2 +""" 〃 """ +L77W_A3 = 2.650648471e-4 * si.hPa / si.K**3 +""" 〃 """ +L77W_A4 = 3.031240396e-6 * si.hPa / si.K**4 +""" 〃 """ +L77W_A5 = 2.034080948e-8 * si.hPa / si.K**5 +""" 〃 """ +L77W_A6 = 6.136820929e-11 * si.hPa / si.K**6 +""" 〃 """ +L77I_A0 = 6.109177956 * si.hPa +""" 〃 """ +L77I_A1 = 5.03469897e-1 * si.hPa / si.K +""" 〃 """ +L77I_A2 = 1.886013408e-2 * si.hPa / si.K**2 +""" 〃 """ +L77I_A3 = 4.176223716e-4 * si.hPa / si.K**3 +""" 〃 """ +L77I_A4 = 5.824720280e-6 * si.hPa / si.K**4 +""" 〃 """ +L77I_A5 = 4.838803174e-8 * si.hPa / si.K**5 +""" 〃 """ +L77I_A6 = 1.838826904e-10 * si.hPa / si.K**6 +""" 〃 """ + +rho_i = 916.8 * si.kg / si.metres**3 +""" density of ice, for discussion, see [Pounder 1965](https://doi.org/10.1016/C2013-0-08278-3) """ +rho_w = 1 * si.kilograms / si.litres +""" 〃 of water, 〃 """ + +pH_w = 7 +""" pH of pure water """ + +p1000 = 1000 * si.hectopascals +""" 1000 hPa reference pressure as in the definition of potential temperature""" + +p_tri = 611.657 * si.pascal +""" water triple point pressure ([Murphy & Koop 2005](https://doi.org/10.1256/qj.04.94)) """ +T_tri = 273.16 * si.kelvin +""" water triple point temperature ([Murphy & Koop 2005](https://doi.org/10.1256/qj.04.94)) """ +L_tri = 45051.0 * si.joule / si.mol +""" latent heat of vaporization of liquid at triple point +([Murphy & Koop 2005](https://doi.org/10.1256/qj.04.94)) """ + +l_l19_a = 0.167 * si.dimensionless +""" [Seinfeld and Pandis](https://archive.org/details/0237-pdf-atmospheric-chemistry-and-physics-2nd-ed-j.-seinfeld-s.-pandis-wiley-2006-ww) +Appendix 16.1, 16A.2 default constant values according to +[Lowe et al. 2019](https://doi.org/10.1038/s41467-019-12982-0), +from ICPM code """ # pylint: disable=line-too-long +l_l19_b = 3.65e-4 / si.kelvin +""" 〃 """ + +k_l19_a = 4.2e-3 * si.joules / si.metres / si.seconds / si.kelvins +""" Thermal diffusivity constants from +[Lowe et al. 2019](https://doi.org/10.1038/s41467-019-12982-0) """ +k_l19_b = 1.0456 * si.dimensionless +""" 〃 """ +k_l19_c = 0.017 / si.kelvin +""" 〃 """ + +dv_pk05 = 0.0 * si.metres +""" Delta v for diffusivity in [Pruppacher & Klett](https://doi.org/10.1007/978-0-306-48100-0) +eq. 13-14 """ + +lmbd_w_0 = 6.6e-8 * si.metre +""" Mean free path of water molecules as in table 13.1 in +[Pruppacher & Klett](https://doi.org/10.1007/978-0-306-48100-0) """ + +d_l19_a = 0.211e-4 * si.metre**2 / si.second +""" [Seinfeld & Pandis](https://archive.org/details/0237-pdf-atmospheric-chemistry-and-physics-2nd-ed-j.-seinfeld-s.-pandis-wiley-2006-ww) +eq. 15.65 +[Hall & Pruppacher 1976](https://doi.org/10.1175/1520-0469(1976)033%3C1995:TSOIPF%3E2.0.CO;2) +""" # pylint: disable=line-too-long +d_l19_b = 1.94 +""" 〃 """ + +MK05_ICE_C1 = 1 * si.Pa +""" [Murphy and Koop 2005](https://doi.org/10.1256/qj.04.94) """ +MK05_ICE_C2 = 9.550426 * si.dimensionless +""" 〃 """ +MK05_ICE_C3 = 5723.265 * si.K +""" 〃 """ +MK05_ICE_C4 = 3.53068 * si.dimensionless +""" 〃 """ +MK05_ICE_C5 = 1 * si.K +""" 〃 """ +MK05_ICE_C6 = 0.00728332 / si.K +""" 〃 """ +MK05_LIQ_C1 = 1 * si.Pa +""" 〃 """ +MK05_LIQ_C2 = 54.842763 * si.dimensionless +""" 〃 """ +MK05_LIQ_C3 = 6763.22 * si.K +""" 〃 """ +MK05_LIQ_C4 = 4.210 * si.dimensionless +""" 〃 """ +MK05_LIQ_C5 = 1 * si.K +""" 〃 """ +MK05_LIQ_C6 = 0.000367 / si.K +""" 〃 """ +MK05_LIQ_C7 = 0.0415 / si.K +""" 〃 """ +MK05_LIQ_C8 = 218.8 * si.K +""" 〃 """ +MK05_LIQ_C9 = 53.878 * si.dimensionless +""" 〃 """ +MK05_LIQ_C10 = 1331.22 * si.K +""" 〃 """ +MK05_LIQ_C11 = 9.44523 * si.dimensionless +""" 〃 """ +MK05_LIQ_C12 = 1 * si.K +""" 〃 """ +MK05_LIQ_C13 = 0.014025 / si.K +""" 〃 """ +MK05_SUB_C1 = 46782.5 * si.joule / si.mole +""" 〃 """ +MK05_SUB_C2 = 35.8925 * si.joule / si.mole / si.kelvin +""" 〃 """ +MK05_SUB_C3 = 0.07414 * si.joule / si.mole / si.kelvin**2 +""" 〃 """ +MK05_SUB_C4 = 541.5 * si.joule / si.mole +""" 〃 """ +MK05_SUB_C5 = 123.75 * si.kelvin +""" 〃 """ + +T_STP = (sci.zero_Celsius + 15) * si.kelvin +""" standard temperature (ICAO) ... """ +p_STP = 101325 * si.pascal +""" ... and pressure """ + +ROOM_TEMP = T0 + 25 * si.K +""" room temperature """ + +dT_u = si.K + +sgm_org = np.nan +delta_min = np.nan + +RUEHL_nu_org = np.nan +RUEHL_A0 = np.nan +RUEHL_C0 = np.nan +RUEHL_m_sigma = np.nan +RUEHL_sgm_min = np.nan + +BIGG_DT_MEDIAN = np.nan + +NIEMAND_A = np.nan +NIEMAND_B = np.nan + +HOMOGENEOUS_FREEZING_THRESHOLD = T0 - 38 * si.K +""" value from [Shima et al. 2020](https://doi.org/10.5194/gmd-13-4107-2020) """ + +ABIFM_UNIT = 1 / si.cm**2 / si.s +""" ice nucleation rate using ABIFM +([Knopf & Alpert 2013](https://doi.org/10.1039/C3FD00035D)) """ +ABIFM_M = np.inf +""" 〃 """ +ABIFM_C = np.inf +""" 〃 """ + +KOOP_2000_C1 = -906.7 +""" homogeneous ice nucleation rate +([Koop et al. 2000](https://doi.org/10.1038/35020537)) """ +KOOP_2000_C2 = 8502 +""" 〃 """ +KOOP_2000_C3 = -26924 +""" 〃 """ +KOOP_2000_C4 = 29180 +""" 〃 """ +KOOP_UNIT = 1 / si.cm**3 / si.s +""" 〃 """ +KOOP_MIN_DA_W_ICE = 0.26 +""" 〃 """ +KOOP_MAX_DA_W_ICE = 0.34 + +KOOP_CORR = -1.522 +""" homogeneous ice nucleation rate correction factor +([Spichtinger et al. 2023](https://doi.org/10.5194/acp-23-2035-2023)) """ + +KOOP_MURRAY_C0 = -3020.684 +""" homogeneous ice nucleation rate for pure water droplets +([Koop & Murray 20016](https://doi.org/10.1063/1.4962355)) """ +KOOP_MURRAY_C1 = -425.921 / si.K +""" 〃 """ +KOOP_MURRAY_C2 = -25.9779 / si.K**2 +""" 〃 """ +KOOP_MURRAY_C3 = -0.868451 / si.K**3 +""" 〃 """ +KOOP_MURRAY_C4 = -1.66203e-2 / si.K**4 +""" 〃 """ +KOOP_MURRAY_C5 = -1.71736e-4 / si.K**5 +""" 〃 """ +KOOP_MURRAY_C6 = -7.46953e-7 / si.K**6 +""" 〃 """ + +J_HET = np.nan +J_HOM = np.nan +""" constant ice nucleation rates """ + +STRAUB_E_D1 = 0.04 * si.cm +""" [Straub et al. 2010](https://doi.org/10.1175/2009JAS3175.1) """ +STRAUB_MU2 = 0.095 * si.cm +""" 〃 """ + +VEDDER_1987_b = 89 / 880 +""" [Vedder 1987](https://doi.org/10.1119/1.15018) """ +VEDDER_1987_A = 993 / 880 / 3 / VEDDER_1987_b +""" 〃 """ + +MERLIVAT_NIEF_1967_ALPHA_L_2H_T2 = 15013 * si.K**2 +""" [eq. 5 in Merlivat and Nief 1967](https://doi.org/10.3402/tellusa.v19i1.9756) """ +MERLIVAT_NIEF_1967_ALPHA_L_2H_T1 = 0 * si.K +""" 〃 """ +MERLIVAT_NIEF_1967_ALPHA_L_2H_T0 = -0.1 +""" 〃 """ +MERLIVAT_NIEF_1967_ALPHA_I_2H_T2 = 16289 * si.K**2 +""" 〃 """ +MERLIVAT_NIEF_1967_ALPHA_I_2H_T1 = 0 * si.K +""" 〃 """ +MERLIVAT_NIEF_1967_ALPHA_I_2H_T0 = -0.0945 +""" 〃 """ + +LAMB_ET_AL_2017_ALPHA_I_2H_T2 = 13525 * si.K**2 +""" [Lamb et al. 2017](https://doi.org/10.1073/pnas.1618374114) """ +LAMB_ET_AL_2017_ALPHA_I_2H_T1 = 0 * si.K +""" 〃 """ +LAMB_ET_AL_2017_ALPHA_I_2H_T0 = -0.0559 +""" 〃 """ + +ELLEHOJ_ET_AL_2013_ALPHA_I_2H_T2 = 48888 * si.K**2 +""" [Ellehoj et al. 2013](https://doi.org/10.1002/rcm.6668) """ +ELLEHOJ_ET_AL_2013_ALPHA_I_2H_T1 = -203.1 * si.K +""" 〃 """ +ELLEHOJ_ET_AL_2013_ALPHA_I_2H_T0 = 0.2133 +""" 〃 """ + +MAJOUBE_1971_ALPHA_L_18O_T2 = 1137 * si.K**2 +""" [Majoube 1971](https://doi.org/10.1051/jcp/1971681423) +(values taken from [Jouzel 1986](https://doi.org/10.1016/b978-0-444-42225-5.50007-3)) """ +MAJOUBE_1971_ALPHA_L_18O_T1 = -0.4156 * si.K +""" 〃 """ +MAJOUBE_1971_ALPHA_L_18O_T0 = -0.0020667 +""" 〃 """ +MAJOUBE_1971_ALPHA_L_2H_T2 = 24844 * si.K**2 +""" 〃 """ +MAJOUBE_1971_ALPHA_L_2H_T1 = -76.248 * si.K +""" 〃 """ +MAJOUBE_1971_ALPHA_L_2H_T0 = 0.052612 +""" 〃 """ + +MAJOUBE_1970_ALPHA_I_18O_T2 = 0 * si.K**2 +""" [Majoube 1970](https://doi.org/10.1038/2261242a0) """ +MAJOUBE_1970_ALPHA_I_18O_T1 = 11.839 * si.K +""" 〃 """ +MAJOUBE_1970_ALPHA_I_18O_T0 = -0.028224 +""" 〃 """ + +VAN_HOOK_1968_ALPHA_I_2H_A = 11484.5 * si.K**2 +""" [Van Hook 1968](https://doi.org/10.1021/j100850a028) """ +VAN_HOOK_1968_ALPHA_I_2H_B = 35.3315 * si.K +""" 〃 """ +VAN_HOOK_1968_ALPHA_I_2H_C = -0.159290 +""" 〃 """ +VAN_HOOK_1968_ALPHA_L_2H_A = 26398.8 * si.K**2 +""" 〃 """ +VAN_HOOK_1968_ALPHA_L_2H_B = -89.6065 * si.K +""" 〃 """ +VAN_HOOK_1968_ALPHA_L_2H_C = 0.075802 +""" 〃 """ +VAN_HOOK_1968_ALPHA_I_18O_A = 1740.59 * si.K**2 +""" 〃 """ +VAN_HOOK_1968_ALPHA_I_18O_B = 2.2965 * si.K +""" 〃 """ +VAN_HOOK_1968_ALPHA_I_18O_C = -0.005793 +""" 〃 """ +VAN_HOOK_1968_ALPHA_L_18O_A = 1991.1 * si.K**2 +""" 〃 """ +VAN_HOOK_1968_ALPHA_L_18O_B = -4.1887 * si.K +""" 〃 """ +VAN_HOOK_1968_ALPHA_L_18O_C = 0.001197 +""" 〃 """ +VAN_HOOK_1968_ALPHA_I_17O_A = 933.651 * si.K**2 +""" 〃 """ +VAN_HOOK_1968_ALPHA_I_17O_B = 1.0953 * si.K +""" 〃 """ +VAN_HOOK_1968_ALPHA_I_17O_C = -0.002805 +""" 〃 """ +VAN_HOOK_1968_ALPHA_L_17O_A = 1057.8 * si.K**2 +""" 〃 """ +VAN_HOOK_1968_ALPHA_L_17O_B = -2.24 * si.K +""" 〃 """ +VAN_HOOK_1968_ALPHA_L_17O_C = 0.000668 +""" 〃 """ +VAN_HOOK_1968_ALPHA_I_3H_A = 18464.5 * si.K**2 +""" 〃 """ +VAN_HOOK_1968_ALPHA_I_3H_B = 31.0436 * si.K +""" 〃 """ +VAN_HOOK_1968_ALPHA_I_3H_C = -0.20752 +""" 〃 """ +VAN_HOOK_1968_ALPHA_L_3H_A = 37813.2 * si.K**2 +""" 〃 """ +VAN_HOOK_1968_ALPHA_L_3H_B = -136.751 * si.K +""" 〃 """ +VAN_HOOK_1968_ALPHA_L_3H_C = 0.124096 +""" 〃 """ +VAN_HOOK_1968_ALPHA_I_TOT_A = 33453.7 * si.K**2 +""" 〃 """ +VAN_HOOK_1968_ALPHA_I_TOT_B = 62.4058 * si.K +""" 〃 """ +VAN_HOOK_1968_ALPHA_I_TOT_C = -0.395542 +""" 〃 """ +VAN_HOOK_1968_ALPHA_L_TOT_A = 68702.3 * si.K**2 +""" 〃 """ +VAN_HOOK_1968_ALPHA_L_TOT_B = -244.687 * si.K +""" 〃 """ +VAN_HOOK_1968_ALPHA_L_TOT_C = 0.224388 +""" 〃 """ +VAN_HOOK_1968_ALPHA_I_DOT_A = 27722.4 * si.K**2 +""" 〃 """ +VAN_HOOK_1968_ALPHA_I_DOT_B = 66.5930 * si.K +""" 〃 """ +VAN_HOOK_1968_ALPHA_I_DOT_C = -0.351698 +""" 〃 """ +VAN_HOOK_1968_ALPHA_L_DOT_A = 59313.4 * si.K**2 +""" 〃 """ +VAN_HOOK_1968_ALPHA_L_DOT_B = -204.941 * si.K +""" 〃 """ +VAN_HOOK_1968_ALPHA_L_DOT_C = 0.182686 +""" 〃 """ +VAN_HOOK_1968_ALPHA_I_DOD_A = 21577.6 * si.K**2 +""" 〃 """ +VAN_HOOK_1968_ALPHA_I_DOD_B = 69.3358 * si.K +""" 〃 """ +VAN_HOOK_1968_ALPHA_I_DOD_C = -0.305394 +""" 〃 """ +VAN_HOOK_1968_ALPHA_L_DOD_A = 49314.9 * si.K**2 +""" 〃 """ +VAN_HOOK_1968_ALPHA_L_DOD_B = -164.266 * si.K +""" 〃 """ +VAN_HOOK_1968_ALPHA_L_DOD_C = 0.140049 +""" 〃 """ + +HORITA_AND_WESOLOWSKI_1994_ALPHA_L_18O_T3 = 1e-3 * 0.35041e9 * si.K**3 +""" [Horita and Wesolowski 1994](https://doi.org/10.1016/0016-7037(94)90096-5) """ +HORITA_AND_WESOLOWSKI_1994_ALPHA_L_18O_T2 = 1e-3 * -1.6664e6 * si.K**2 +""" 〃 """ +HORITA_AND_WESOLOWSKI_1994_ALPHA_L_18O_T1 = 1e-3 * 6.7123e3 * si.K +""" 〃 """ +HORITA_AND_WESOLOWSKI_1994_ALPHA_L_18O_T0 = 1e-3 * -7.685 +""" 〃 """ +HORITA_AND_WESOLOWSKI_1994_ALPHA_L_2H_T3 = 1e-3 * 2.9992e9 * si.K**3 +""" 〃 """ +HORITA_AND_WESOLOWSKI_1994_ALPHA_L_2H_T_0 = 1e-3 * -161.04 +""" 〃 """ +HORITA_AND_WESOLOWSKI_1994_ALPHA_L_2H_T_1 = 1e-3 * 794.84e-3 / si.K +""" 〃 """ +HORITA_AND_WESOLOWSKI_1994_ALPHA_L_2H_T_2 = 1e-3 * -1620.1e-6 / si.K**2 +""" 〃 """ +HORITA_AND_WESOLOWSKI_1994_ALPHA_L_2H_T_3 = 1e-3 * 1158.8e-9 / si.K**3 +""" 〃 """ + +BARKAN_AND_LUZ_2005_EXPONENT = 0.529 +""" [Barkan and Luz 2005](https://doi.org/10.1002/rcm.2250) """ + +BARKAN_AND_LUZ_2007_EXCESS_18O_COEFF = 0.528 +""" eq. 11 in [Barkan and Luz 2007](https://doi.org/10.1002/rcm.3180) """ + +CRAIG_1961_SLOPE_COEFF = 8 +""" [Craig 1961](https://doi.org/10.1126/science.133.3465.1702) """ +CRAIG_1961_INTERCEPT_COEFF = 10 * PER_MILLE +""" 〃 """ + +capacity_columnar_ice_B1 = 0.3 +""" eq. A11 & A12 in [Spichtinger et al. 2023](https://doi.org/10.5194/acp-23-2035-2023) """ +capacity_columnar_ice_B2 = 0.43 +""" 〃 """ +capacity_columnar_ice_A1 = 0.015755 * si.m / si.kg**capacity_columnar_ice_B1 +""" 〃 """ +capacity_columnar_ice_A2 = 0.33565 * si.m / si.kg**capacity_columnar_ice_B2 +""" 〃 """ + +columnar_ice_mass_transition = 2.146e-13 * si.kg +""" tab. 1 in [Spichtinger & Gierens 2009](https://doi.org/10.5194/acp-9-685-2009) """ +columnar_ice_length_beta_1 = 3.0 +""" 〃 """ +columnar_ice_length_beta_2 = 2.2 +""" 〃 """ +columnar_ice_length_alpha_1 = 526.1 * si.kg / si.m**columnar_ice_length_beta_1 +""" 〃 """ +columnar_ice_length_alpha_2 = 0.04142 * si.kg / si.m**columnar_ice_length_beta_2 +""" 〃 """ +columnar_bulk_ice_density = 0.81e3 * si.kg / si.m**3 +""" 〃 """ + +asymmetry_g = 0.85 # forward scattering from cloud droplets +""" [Bohren 1987](https://doi.org/10.1119/1.15109) """ + +diffusion_thermics_D_G11_A = 1e-5 * si.m**2 / si.s +""" [Grabowski et al. 2011](https://doi.org/10.1016/j.atmosres.2010.10.020) """ +diffusion_thermics_D_G11_B = 0.015 / si.K +""" 〃 """ +diffusion_thermics_D_G11_C = -1.9 +""" 〃 """ +diffusion_thermics_K_G11_A = 1.5e-11 * si.W / si.m / si.K**4 +""" 〃 """ +diffusion_thermics_K_G11_B = -4.8e-8 * si.W / si.m / si.K**3 +""" 〃 """ +diffusion_thermics_K_G11_C = 1e-4 * si.W / si.m / si.K**2 +""" 〃 """ +diffusion_thermics_K_G11_D = -3.9e-4 * si.W / si.m / si.K +""" 〃 """ + +PRUPPACHER_RASMUSSEN_1979_XTHRES = 1.4 * si.dimensionless +""" +[Pruppacher & Rasmussen 1979](https://doi.org/10.1175/1520-0469%281979%29036%3C1255:AWTIOT%3E2.0.CO;2) +also in +[Beard & Pruppacher 1971](https://doi.org/10.1175/1520-0469%281971%29028%3C1455:AWTIOT%3E2.0.CO;2) +""" # pylint: disable=line-too-long +PRUPPACHER_RASMUSSEN_1979_CONSTSMALL = 1.0 * si.dimensionless +""" 〃 """ +PRUPPACHER_RASMUSSEN_1979_COEFFSMALL = 0.108 * si.dimensionless +""" 〃 """ +PRUPPACHER_RASMUSSEN_1979_POWSMALL = 2 * si.dimensionless +""" 〃 """ +PRUPPACHER_RASMUSSEN_1979_CONSTBIG = 0.78 * si.dimensionless +""" 〃 """ +PRUPPACHER_RASMUSSEN_1979_COEFFBIG = 0.308 * si.dimensionless +""" 〃 """ + +ZOGRAFOS_1987_COEFF_T3 = 2.5914e-15 * si.K ** (-3) * si.Pa * si.s +"""[Zografos et al. 1987](https://doi.org/10.1016/0045-7825(87)90003-X) Table 1""" +ZOGRAFOS_1987_COEFF_T2 = -1.4346e-11 * si.K ** (-2) * si.Pa * si.s +""" 〃 """ +ZOGRAFOS_1987_COEFF_T1 = 5.0523e-8 / si.K * si.Pa * si.s +""" 〃 """ +ZOGRAFOS_1987_COEFF_T0 = 4.1130e-6 * si.Pa * si.s +""" 〃 """ + +FROESSLING_1938_A = 1 +""" Froessling 1938 coefficients as given on page 61 in + [Squires 1952](https://doi.org/10.1071/CH9520059) """ +FROESSLING_1938_B = 0.276 +""" 〃 """ + +HELLMANN_HARVEY_T_UNIT = 100 * si.K +""" fit coefficients from [Hellmann & Harvey 2020](https://doi.org/10.1029/2020GL089999) """ +HELLMANN_HARVEY_EQ6_COEFF0 = 0.98258 +""" 〃 """ +HELLMANN_HARVEY_EQ6_COEFF1 = -0.02546 +""" 〃 """ +HELLMANN_HARVEY_EQ6_COEFF2 = 0.02421 +""" 〃 """ +HELLMANN_HARVEY_EQ7_COEFF0 = 0.98284 +""" 〃 """ +HELLMANN_HARVEY_EQ7_COEFF1 = 0.003517 +""" 〃 """ +HELLMANN_HARVEY_EQ7_COEFF2 = -0.001996 +""" 〃 """ +HELLMANN_HARVEY_EQ8_COEFF0 = 0.96671 +""" 〃 """ +HELLMANN_HARVEY_EQ8_COEFF1 = 0.007406 +""" 〃 """ +HELLMANN_HARVEY_EQ8_COEFF2 = -0.004861 +""" 〃 """ + +ROGERS_YAU_TERM_VEL_SMALL_K = 1.19e6 / si.cm / si.s +""" terminal velocity formulation from +[Rogers & Yau 1989](https://archive.org/details/shortcourseinclo0000roge_m3k2) +(equations: 8.5, 8.6, 8.8) """ +ROGERS_YAU_TERM_VEL_MEDIUM_K = 8e3 / si.s +""" 〃 """ +ROGERS_YAU_TERM_VEL_LARGE_K = 2.01e3 * si.cm**0.5 / si.s +""" 〃 """ +ROGERS_YAU_TERM_VEL_SMALL_R_LIMIT = 35 * si.um +""" 〃 """ +ROGERS_YAU_TERM_VEL_MEDIUM_R_LIMIT = 600 * si.um +""" 〃 """ + +SPICHTINGER_GIERENS_TERM_VEL_LIMIT_0 = 2.146e-13 * si.kg +""" empirical terminal velocity formulation +for columnar ice crystals from Table 2. in +[Spichtinger & Gierens 2009](https://doi.org/10.5194/acp-9-685-2009) """ +SPICHTINGER_GIERENS_TERM_VEL_LIMIT_1 = 2.166e-9 * si.kg +""" 〃 """ +SPICHTINGER_GIERENS_TERM_VEL_LIMIT_2 = 4.264e-8 * si.kg +""" 〃 """ +SPICHTINGER_TERM_DELTA_COEFF0 = 0.42 +""" 〃 """ +SPICHTINGER_TERM_DELTA_COEFF1 = 0.57 +""" 〃 """ +SPICHTINGER_TERM_DELTA_COEFF2 = 0.31 +""" 〃 """ +SPICHTINGER_TERM_DELTA_COEFF3 = 0.096 +""" 〃 """ +SPICHTINGER_TERM_GAMMA_COEFF0 = ( + 735.4 * si.m / si.s / si.kg**SPICHTINGER_TERM_DELTA_COEFF0 +) +""" 〃 """ +SPICHTINGER_TERM_GAMMA_COEFF1 = ( + 63292.4 * si.m / si.s / si.kg**SPICHTINGER_TERM_DELTA_COEFF1 +) +""" 〃 """ +SPICHTINGER_TERM_GAMMA_COEFF2 = ( + 329.8 * si.m / si.s / si.kg**SPICHTINGER_TERM_DELTA_COEFF2 +) +""" 〃 """ +SPICHTINGER_TERM_GAMMA_COEFF3 = 8.8 * si.m / si.s / si.kg**SPICHTINGER_TERM_DELTA_COEFF3 +""" 〃 """ +SPICHTINGER_CORRECTION_P0 = 300 * si.hectopascal +""" 〃 """ +SPICHTINGER_CORRECTION_P_EXPO = -0.178 +""" 〃 """ +SPICHTINGER_CORRECTION_T0 = 233 * si.kelvin +""" 〃 """ +SPICHTINGER_CORRECTION_T_EXPO = -0.394 +""" 〃 """ + +W76W_G0 = -2.9912729e3 * si.K**2 +""" [Wexler 1976](https://doi.org/10.6028/jres.080A.071) saturation vapour pressure """ +W76W_G1 = -6.0170128e3 * si.K +""" 〃 """ +W76W_G2 = 1.887643854e1 +""" 〃 """ +W76W_G3 = -2.8354721e-2 * si.K**-1 +""" 〃 """ +W76W_G4 = 1.7838301e-5 * si.K**-2 +""" 〃 """ +W76W_G5 = -8.4150417e-10 * si.K**-3 +""" 〃 """ +W76W_G6 = 4.4412543e-13 * si.K**-4 +""" 〃 """ +W76W_G7 = 2.858487 +""" 〃 """ +W76W_G8 = 1 * si.Pa +""" 〃 """ +one_kelvin = 1 * si.K +""" 〃 """ + +B80W_G0 = 6.112 * si.hPa +""" [Bolton 1980](https://doi.org/10.1175/1520-0493(1980)108%3C1046:TCOEPT%3E2.0.CO;2) +saturation vapour pressure """ +B80W_G1 = 17.67 * si.dimensionless +""" 〃 """ +B80W_G2 = 243.5 * si.K +""" 〃 """ + +sgm_w = 0.072 * si.joule / si.metre**2 +""" surface tension of water/air interface, value compatible with the kappa-Koehler hygroscopicity + parameterisation [Petters & Kreidenweis 2007](https://doi.org/10.5194/acp-7-1961-2007) """ + +D0 = 2.26e-5 * si.metre**2 / si.second +""" diffusivity of water vapor in air (Tracy et al. 2010, "Properties of Air", page 22) """ +D_exp = 1.81 +""" 〃 """ + +K0 = 2.4e-2 * si.joules / si.metres / si.seconds / si.kelvins +""" thermal conductivity of air (see Tracy et al. 2010, "Properties of Air", page 32) """ + +c_pd = 1005 * si.joule / si.kilogram / si.kelvin +""" specific heat at constant pressure of dry air, as in Table 2.1 in + [Cotton et al. 2011](https://doi.org/10.1016/S0074-6142(10)09908-0) """ +c_pv = 1850 * si.joule / si.kilogram / si.kelvin +""" 〃 of water vapour 〃 """ +c_pw = 4218 * si.joule / si.kilogram / si.kelvin +""" 〃 of liquid water 〃 """ + +g_std = sci.g * si.metre / si.second**2 +""" standard gravitational acceleration (value from SciPy) """ + +celestial_body_radius = np.nan +""" radius of the considered celestial body (Earth, Titan, ...) """ + +bulk_phase_partitioning_T_cold = 235 * si.K +""" [Kaul et al. 2015](https://doi.org/10.1175/MWR-D-14-00319.1) """ +bulk_phase_partitioning_T_warm = 273 * si.K +""" 〃 """ +bulk_phase_partitioning_exponent = np.nan +""" 〃 """ + +BOLIN_ISOTOPE_TIMESCALE_COEFF_C1 = np.nan * si.dimensionless +""" +Coefficient c1 used in [Bolin 1958](https://digitallibrary.un.org/record/3892725) +for the falling drop evaporation timescale of equilibration with ambient air void of a given +isotopologue; in the paper timescale is calculated for tritium with assumption of no tritium +in the environment around the drop (Table 1). +""" + +PICCIOTTO_18O_A = -0.9 * PER_MILLE / si.K +""" linear fit coefficients from [Picciotto et al. 1960](https://doi.org/10.1038/187857a0) +for atmospheric temperature inference from water isotopic composition +(note that the sign of A coefficient is opposite to match the paper plot - typo in the paper?) """ +PICCIOTTO_18O_B = 6.4 * PER_MILLE +"""〃""" +PICCIOTTO_2H_A = -0.8 * PER_CENT / si.K +"""〃""" +PICCIOTTO_2H_B = 8 * PER_CENT +"""〃""" + +PICCIOTTO_18O_TO_2H_SLOPE_COEFF = 0.8 * PER_CENT / PER_MILLE +""" [hydro]meteoric water line [Picciotto et al. 1960](https://doi.org/10.1038/187857a0) coeffs +(note that the delta-2H and delta-18O are swapped to match the paper plot - typo in the paper?) +(note that the sign of INTERCEPT is opposite to match the paper plot - typo in the paper?) +""" +PICCIOTTO_18O_TO_2H_INTERCEPT_COEFF = -1.8 * PER_CENT +"""〃""" + + +def compute_derived_values(c: dict): + """ + computes derived quantities such as molar mass ratios, etc. + + water molar mass is computed from molecular masses and VSMOW isotope abundances + (and neglecting molecular binding energies) + for discussion, see: + - [IAPWS Guidelines](http://www.iapws.org/relguide/fundam.pdf) + """ + + c["M_1H2_16O"] = c["M_1H"] * 2 + c["M_16O"] + c["M_2H_1H_16O"] = c["M_2H"] + c["M_1H"] + c["M_16O"] + c["M_3H_1H_16O"] = c["M_3H"] + c["M_1H"] + c["M_16O"] + c["M_1H2_17O"] = c["M_1H"] * 2 + c["M_17O"] + c["M_1H2_18O"] = c["M_1H"] * 2 + c["M_18O"] + + c["Mv"] = ( + ( + 1 + - 2 + * Trivia.isotopic_fraction_assuming_single_heavy_isotope( + isotopic_ratio=c["VSMOW_R_2H"] + ) + - 2 + * Trivia.isotopic_fraction_assuming_single_heavy_isotope( + isotopic_ratio=c["VSMOW_R_3H"] + ) + - Trivia.isotopic_fraction_assuming_single_heavy_isotope( + isotopic_ratio=c["VSMOW_R_17O"] + ) + - Trivia.isotopic_fraction_assuming_single_heavy_isotope( + isotopic_ratio=c["VSMOW_R_18O"] + ) + ) + * c["M_1H2_16O"] + + 2 + * Trivia.isotopic_fraction_assuming_single_heavy_isotope( + isotopic_ratio=c["VSMOW_R_2H"] + ) + * c["M_2H_1H_16O"] + + 2 + * Trivia.isotopic_fraction_assuming_single_heavy_isotope( + isotopic_ratio=c["VSMOW_R_3H"] + ) + * c["M_3H_1H_16O"] + + Trivia.isotopic_fraction_assuming_single_heavy_isotope( + isotopic_ratio=c["VSMOW_R_17O"] + ) + * c["M_1H2_17O"] + + Trivia.isotopic_fraction_assuming_single_heavy_isotope( + isotopic_ratio=c["VSMOW_R_18O"] + ) + * c["M_1H2_18O"] + ) + c["eps"] = c["Mv"] / c["Md"] + c["Rd"] = c["R_str"] / c["Md"] + c["Rv"] = c["R_str"] / c["Mv"] + + c["Rd_over_c_pd"] = c["Rd"] / c["c_pd"] + + c["water_molar_volume"] = c["Mv"] / c["rho_w"] + c["rho_STP"] = c["p_STP"] / c["Rd"] / c["T_STP"] + c["H_u"] = c["M"] / c["p_STP"] + + c["l_tri"] = c["L_tri"] / c["Mv"] diff --git a/PySDM/source/PySDM/physics/diffusion_coordinate/__init__.py b/PySDM/source/PySDM/physics/diffusion_coordinate/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..fec5bff8c35fd8502b424cef0043e80ab2b7966c --- /dev/null +++ b/PySDM/source/PySDM/physics/diffusion_coordinate/__init__.py @@ -0,0 +1,6 @@ +""" +definitions of particle-size coordinates for the condensation solver +""" + +from .water_mass import WaterMass +from .water_mass_logarithm import WaterMassLogarithm diff --git a/PySDM/source/PySDM/physics/diffusion_coordinate/water_mass.py b/PySDM/source/PySDM/physics/diffusion_coordinate/water_mass.py new file mode 100644 index 0000000000000000000000000000000000000000..f85acbb7d5bf680405af1d2d3ef060c6d6ca5178 --- /dev/null +++ b/PySDM/source/PySDM/physics/diffusion_coordinate/water_mass.py @@ -0,0 +1,25 @@ +""" +particle water mass as condensation coordinate (i.e. no transformation) +""" + + +class WaterMass: + def __init__(self, _): + pass + + @staticmethod + def dx_dt(m, dm_dt): # pylint: disable=unused-argument + return dm_dt + + @staticmethod + def mass(x): + return x + + @staticmethod + def x(mass): + return mass + + @staticmethod + def x_max(const): + """1 kg droplet!""" + return const.ONE diff --git a/PySDM/source/PySDM/physics/diffusion_coordinate/water_mass_logarithm.py b/PySDM/source/PySDM/physics/diffusion_coordinate/water_mass_logarithm.py new file mode 100644 index 0000000000000000000000000000000000000000..13ad05560f557dfe73a1e0d049380476b59ff347 --- /dev/null +++ b/PySDM/source/PySDM/physics/diffusion_coordinate/water_mass_logarithm.py @@ -0,0 +1,32 @@ +""" +logarithm of particle mass as coordinate (ensures non-negative values) +""" + +import numpy as np + + +class WaterMassLogarithm: + def __init__(self, _): + pass + + @staticmethod + def dx_dt(m, dm_dt): + """ + x = ln(m/m0) + m0 = 1 kg + dx_dt = 1/m(x) dm_dt + """ + return dm_dt / m + + @staticmethod + def mass(x): + return np.exp(x) + + @staticmethod + def x(mass): + return np.log(mass) + + @staticmethod + def x_max(const): + """corresponds to 1 kg droplet!""" + return const.ZERO diff --git a/PySDM/source/PySDM/physics/diffusion_ice_capacity/__init__.py b/PySDM/source/PySDM/physics/diffusion_ice_capacity/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7957de7f29d0e44f03456beec6a49c37839c74fc --- /dev/null +++ b/PySDM/source/PySDM/physics/diffusion_ice_capacity/__init__.py @@ -0,0 +1,6 @@ +""" +Formulae for capacity in diffusional growth/evaporation of ice +""" + +from .spherical import Spherical +from .columnar import Columnar diff --git a/PySDM/source/PySDM/physics/diffusion_ice_capacity/columnar.py b/PySDM/source/PySDM/physics/diffusion_ice_capacity/columnar.py new file mode 100644 index 0000000000000000000000000000000000000000..554504e0a3f4d8284d3bdf7d67bb0eec5365cf39 --- /dev/null +++ b/PySDM/source/PySDM/physics/diffusion_ice_capacity/columnar.py @@ -0,0 +1,29 @@ +""" +capacity for columnar ice crystals approximated as prolate ellipsoids +eq. A11 & A12 in Spichtinger et al. 2023 (https://doi.org/10.5194/acp-23-2035-2023) +""" + +import numpy as np + + +class Columnar: # pylint: disable=too-few-public-methods + + def __init__(self, _): + pass + + @staticmethod + def capacity(const, mass): + return ( + const.capacity_columnar_ice_A1 * mass**const.capacity_columnar_ice_B1 + + const.capacity_columnar_ice_A2 * mass**const.capacity_columnar_ice_B2 + ) + + @staticmethod + def reference_capacity( + const, polar_diameter, eccentricity + ): # pylint: disable=unused-argument + return ( + polar_diameter + * eccentricity + / np.log((1 + eccentricity) / (1 - eccentricity)) + ) diff --git a/PySDM/source/PySDM/physics/diffusion_ice_capacity/spherical.py b/PySDM/source/PySDM/physics/diffusion_ice_capacity/spherical.py new file mode 100644 index 0000000000000000000000000000000000000000..9c7c1a44503ca1970ee4203255ac3fba03697c22 --- /dev/null +++ b/PySDM/source/PySDM/physics/diffusion_ice_capacity/spherical.py @@ -0,0 +1,15 @@ +""" +capacity for approximation of ice crystals as spheres +""" + +import numpy as np + + +class Spherical: # pylint: disable=too-few-public-methods + + def __init__(self, _): + pass + + @staticmethod + def capacity(const, mass): + return np.power(mass / const.PI_4_3 / const.rho_i, const.ONE_THIRD) diff --git a/PySDM/source/PySDM/physics/diffusion_ice_kinetics/__init__.py b/PySDM/source/PySDM/physics/diffusion_ice_kinetics/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..db101bf85af606fb1e2174477a72baf663fb92b5 --- /dev/null +++ b/PySDM/source/PySDM/physics/diffusion_ice_kinetics/__init__.py @@ -0,0 +1,6 @@ +""" +Formulae for handling transition-régime corrections in diffusional growth/evaporation of ice +""" + +from .neglect import Neglect +from .standard import Standard diff --git a/PySDM/source/PySDM/physics/diffusion_ice_kinetics/neglect.py b/PySDM/source/PySDM/physics/diffusion_ice_kinetics/neglect.py new file mode 100644 index 0000000000000000000000000000000000000000..3147173c8f661759ffd6835fbb60426d6f5bdd37 --- /dev/null +++ b/PySDM/source/PySDM/physics/diffusion_ice_kinetics/neglect.py @@ -0,0 +1,24 @@ +""" +no transition-regime corrections formulation +""" + + +class Neglect: + def __init__(self, _): + pass + + @staticmethod + def lambdaD(_, T, p): # pylint: disable=unused-argument + return -1 + + @staticmethod + def lambdaK(_, T, p): # pylint: disable=unused-argument + return -1 + + @staticmethod + def D(_, D, r, lmbd, T): # pylint: disable=unused-argument + return D + + @staticmethod + def K(_, K, r, lmbd, T, rho): # pylint: disable=unused-argument + return K diff --git a/PySDM/source/PySDM/physics/diffusion_ice_kinetics/standard.py b/PySDM/source/PySDM/physics/diffusion_ice_kinetics/standard.py new file mode 100644 index 0000000000000000000000000000000000000000..63ac40836e3bf25d7a34a4729f17681cdf59b1a0 --- /dev/null +++ b/PySDM/source/PySDM/physics/diffusion_ice_kinetics/standard.py @@ -0,0 +1,40 @@ +""" +transition-regime correction as in 'Physics and Chemistry of Clouds' +by Lamb and Verlinde (2011), Chapter 8.2 +or 13.1 in Pruppbacher and Klett (2010) +with free pathway of air/vapour (lambdaD) after Pruppacher and Klett (2010) +""" + +import numpy as np + + +class Standard: + def __init__(self, _): + pass + + @staticmethod + def lambdaD(const, T, p): + return const.lmbd_w_0 * T / const.T_STP * const.p_STP / p + + @staticmethod + def lambdaK(const, T, p): + return const.lmbd_w_0 * T / const.T_STP * const.p_STP / p + + @staticmethod + def D(const, D, r, lmbd, T): + return D / ( + r / (r + lmbd * const.C_cunn) + + 4.0 * D / const.MAC_ice / np.sqrt(8.0 * const.Rv * T / const.PI) / r + ) + + @staticmethod + def K(const, K, r, lmbd, T, rho): # pylint: disable=too-many-arguments + return K / ( + r / (r + lmbd) + + K + / const.HAC_ice + / np.sqrt(8.0 * const.Rd * T / const.PI) + / const.c_pd + / rho + / r + ) diff --git a/PySDM/source/PySDM/physics/diffusion_kinetics/__init__.py b/PySDM/source/PySDM/physics/diffusion_kinetics/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..5c3594712bb794fae34f9b9ef5fc74f4eb9eada4 --- /dev/null +++ b/PySDM/source/PySDM/physics/diffusion_kinetics/__init__.py @@ -0,0 +1,8 @@ +""" +Formulae for handling transition-régime corrections in condensational growth/evaporation +""" + +from .fuchs_sutugin import FuchsSutugin +from .lowe_et_al_2019 import LoweEtAl2019 +from .neglect import Neglect +from .grabowski_et_al_2011 import GrabowskiEtAl2011 diff --git a/PySDM/source/PySDM/physics/diffusion_kinetics/fuchs_sutugin.py b/PySDM/source/PySDM/physics/diffusion_kinetics/fuchs_sutugin.py new file mode 100644 index 0000000000000000000000000000000000000000..828118ef53db019bf5c0f6bec5ec2ff11cd3229f --- /dev/null +++ b/PySDM/source/PySDM/physics/diffusion_kinetics/fuchs_sutugin.py @@ -0,0 +1,43 @@ +""" +Fuch-Sutugin transition-regime correction as advocated for cloud modelling + in [Laaksonen et al. 2005](https://doi.org/10.5194/acp-5-461-2005) +""" + +import numpy as np + + +class FuchsSutugin: + def __init__(self, _): + pass + + @staticmethod + def lambdaD(const, D, T): + return D / np.sqrt(2 * const.Rv * T) + + @staticmethod + def lambdaK(const, T, p): + return (4.0 / 5) * const.K0 * T / p / np.sqrt(2 * const.Rd * T) + + @staticmethod + def D(const, D, r, lmbd): + return ( + D + * (1 + lmbd / r) + / ( + 1 + + (4.0 / 3 / const.MAC + 0.377) * lmbd / r + + (4.0 / 3 / const.MAC) * lmbd / r * lmbd / r + ) + ) + + @staticmethod + def K(const, K, r, lmbd): + return ( + K + * (1 + lmbd / r) + / ( + 1 + + (4.0 / 3 / const.HAC + 0.377) * lmbd / r + + (4.0 / 3 / const.HAC) * lmbd / r * lmbd / r + ) + ) diff --git a/PySDM/source/PySDM/physics/diffusion_kinetics/grabowski_et_al_2011.py b/PySDM/source/PySDM/physics/diffusion_kinetics/grabowski_et_al_2011.py new file mode 100644 index 0000000000000000000000000000000000000000..0b0bcc5f986d66f328eb814f347aa0de61cafce3 --- /dev/null +++ b/PySDM/source/PySDM/physics/diffusion_kinetics/grabowski_et_al_2011.py @@ -0,0 +1,9 @@ +""" +as in [Grabowski et al. (2011)](https://doi.org/10.1016/j.atmosres.2010.10.020) +""" + +from .pruppacher_and_klett_2005 import PruppacherKlett + + +class GrabowskiEtAl2011(PruppacherKlett): + pass diff --git a/PySDM/source/PySDM/physics/diffusion_kinetics/lowe_et_al_2019.py b/PySDM/source/PySDM/physics/diffusion_kinetics/lowe_et_al_2019.py new file mode 100644 index 0000000000000000000000000000000000000000..0a93b60f2874ba463af21773f20c3c853040a402 --- /dev/null +++ b/PySDM/source/PySDM/physics/diffusion_kinetics/lowe_et_al_2019.py @@ -0,0 +1,21 @@ +""" +as in [Lowe et al. 2019](https://doi.org/10.1038/s41467-019-12982-0) +uses eq. 13-14 in [Pruppacher & Klett](https://doi.org/10.1007/978-0-306-48100-0) with Delta v = 0 +and no corrections for thermal conductivity +""" + +from PySDM.physics.diffusion_kinetics.pruppacher_and_klett_2005 import PruppacherKlett + + +class LoweEtAl2019(PruppacherKlett): + def __init__(self, const): + assert const.dv_pk05 == 0 + PruppacherKlett.__init__(self, const) + + @staticmethod + def lambdaK(const, T, p): # pylint: disable=unused-argument + return -1 + + @staticmethod + def K(const, K, r, lmbd): # pylint: disable=unused-argument + return K diff --git a/PySDM/source/PySDM/physics/diffusion_kinetics/neglect.py b/PySDM/source/PySDM/physics/diffusion_kinetics/neglect.py new file mode 100644 index 0000000000000000000000000000000000000000..a357b594a85655cd21a5bd9a93718ef66d2d8de5 --- /dev/null +++ b/PySDM/source/PySDM/physics/diffusion_kinetics/neglect.py @@ -0,0 +1,24 @@ +""" +no transition-regime corrections formulation +""" + + +class Neglect: + def __init__(self, _): + pass + + @staticmethod + def lambdaD(_, D, T): # pylint: disable=unused-argument + return -1 + + @staticmethod + def lambdaK(_, T, p): # pylint: disable=unused-argument + return -1 + + @staticmethod + def D(_, D, r, lmbd): # pylint: disable=unused-argument + return D + + @staticmethod + def K(_, K, r, lmbd): # pylint: disable=unused-argument + return K diff --git a/PySDM/source/PySDM/physics/diffusion_kinetics/pruppacher_and_klett_2005.py b/PySDM/source/PySDM/physics/diffusion_kinetics/pruppacher_and_klett_2005.py new file mode 100644 index 0000000000000000000000000000000000000000..6edca23befb171b102629eb5d5db94afcee7aec9 --- /dev/null +++ b/PySDM/source/PySDM/physics/diffusion_kinetics/pruppacher_and_klett_2005.py @@ -0,0 +1,29 @@ +""" +as in Pruppacher and Klett 2005 (eq. 13-14) +with reference to [Okuyama and Zung 1967](https://doi.org/10.1063/1.1840906) +""" + +import numpy as np + + +class PruppacherKlett: + def __init__(self, _): + pass + + @staticmethod + def lambdaD(const, D, T): + return D / np.sqrt(2 * const.Rv * T) + + @staticmethod + def D(const, D, r, lmbd): + return D / ( + (r / (r + const.dv_pk05)) + 2 * np.sqrt(const.PI) * lmbd / r / const.MAC + ) + + @staticmethod + def lambdaK(_, T, p): # pylint: disable=unused-argument + return -1 + + @staticmethod + def K(const, K, r, lmbd): # pylint: disable=unused-argument + return K # TODO #1266 diff --git a/PySDM/source/PySDM/physics/diffusion_thermics/__init__.py b/PySDM/source/PySDM/physics/diffusion_thermics/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..a05d2c62ecbb733e9c9b9a6c5594947210b9e1eb --- /dev/null +++ b/PySDM/source/PySDM/physics/diffusion_thermics/__init__.py @@ -0,0 +1,8 @@ +""" +Formulae for representing the temperature and pressure dependence of vapour diffusion coefficient +""" + +from .lowe_et_al_2019 import LoweEtAl2019 +from .neglect import Neglect +from .tracy_welch_porter import TracyWelchPorter +from .grabowski_et_al_2011 import GrabowskiEtAl2011 diff --git a/PySDM/source/PySDM/physics/diffusion_thermics/grabowski_et_al_2011.py b/PySDM/source/PySDM/physics/diffusion_thermics/grabowski_et_al_2011.py new file mode 100644 index 0000000000000000000000000000000000000000..f02b3de826d2573c45c10ec58c1b54b955b5ab1e --- /dev/null +++ b/PySDM/source/PySDM/physics/diffusion_thermics/grabowski_et_al_2011.py @@ -0,0 +1,25 @@ +""" +as in [Grabowski et al. (2011)](https://doi.org/10.1016/j.atmosres.2010.10.020) +""" + + +class GrabowskiEtAl2011: + def __init__(self, _): + pass + + @staticmethod + def D(const, T, p): # pylint: disable=unused-argument + """eq (10)""" + return const.diffusion_thermics_D_G11_A * ( + const.diffusion_thermics_D_G11_B * T + const.diffusion_thermics_D_G11_C + ) + + @staticmethod + def K(const, T, p): # pylint: disable=unused-argument + """eq (12)""" + return ( + const.diffusion_thermics_K_G11_A * T**3 + + const.diffusion_thermics_K_G11_B * T**2 + + const.diffusion_thermics_K_G11_C * T + + const.diffusion_thermics_K_G11_D + ) diff --git a/PySDM/source/PySDM/physics/diffusion_thermics/lowe_et_al_2019.py b/PySDM/source/PySDM/physics/diffusion_thermics/lowe_et_al_2019.py new file mode 100644 index 0000000000000000000000000000000000000000..de48591fecc8226d6af087541da2e36ff7114dd6 --- /dev/null +++ b/PySDM/source/PySDM/physics/diffusion_thermics/lowe_et_al_2019.py @@ -0,0 +1,16 @@ +""" +as in [Lowe et al. 2019](https://doi.org/10.1038/s41467-019-12982-0) +""" + +from PySDM.physics.diffusion_thermics.seinfeld_and_pandis_2010 import ( + SeinfeldAndPandis2010, +) + + +class LoweEtAl2019(SeinfeldAndPandis2010): + def __init__(self, const): + SeinfeldAndPandis2010.__init__(self, const) + + @staticmethod + def K(const, T, p): # pylint: disable=unused-argument + return const.k_l19_a * (const.k_l19_b + const.k_l19_c * T) diff --git a/PySDM/source/PySDM/physics/diffusion_thermics/neglect.py b/PySDM/source/PySDM/physics/diffusion_thermics/neglect.py new file mode 100644 index 0000000000000000000000000000000000000000..a3c61dd42867545944ee90cca50c0c1b1a5eb9db --- /dev/null +++ b/PySDM/source/PySDM/physics/diffusion_thermics/neglect.py @@ -0,0 +1,16 @@ +""" +constant diffusion coefficient formulation +""" + + +class Neglect: + def __init__(self, _): + pass + + @staticmethod + def D(const, T, p): # pylint: disable=unused-argument + return const.D0 + + @staticmethod + def K(const, T, p): # pylint: disable=unused-argument + return const.K0 diff --git a/PySDM/source/PySDM/physics/diffusion_thermics/seinfeld_and_pandis_2010.py b/PySDM/source/PySDM/physics/diffusion_thermics/seinfeld_and_pandis_2010.py new file mode 100644 index 0000000000000000000000000000000000000000..0f737b5733d565392f9821f6b0fd178889d0ee75 --- /dev/null +++ b/PySDM/source/PySDM/physics/diffusion_thermics/seinfeld_and_pandis_2010.py @@ -0,0 +1,14 @@ +""" +as in Seinfeld and Pandis 2010 (eq. 15.65) +""" + +import numpy as np + + +class SeinfeldAndPandis2010: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def D(const, T, p): + return const.d_l19_a * (const.p_STP / p) * np.power(T / const.T0, const.d_l19_b) diff --git a/PySDM/source/PySDM/physics/diffusion_thermics/tracy_welch_porter.py b/PySDM/source/PySDM/physics/diffusion_thermics/tracy_welch_porter.py new file mode 100644 index 0000000000000000000000000000000000000000..ff8ff8df4e17a0ab1cc83139309d82623099fb76 --- /dev/null +++ b/PySDM/source/PySDM/physics/diffusion_thermics/tracy_welch_porter.py @@ -0,0 +1,19 @@ +""" +based on "PROPERTIES OF AIR: A Manual for Use in Biophysical Ecology" +(Fourth Edition - 2010, page 22) +""" + +import numpy as np + + +class TracyWelchPorter: + def __init__(self, _): + pass + + @staticmethod + def D(const, T, p): + return const.D0 * np.power(T / const.T0, const.D_exp) * (const.p1000 / p) + + @staticmethod + def K(const, T, p): # pylint: disable=unused-argument + return const.K0 diff --git a/PySDM/source/PySDM/physics/dimensional_analysis.py b/PySDM/source/PySDM/physics/dimensional_analysis.py new file mode 100644 index 0000000000000000000000000000000000000000..31ca77e753c9b8f80f7a418dc0fabe5d31aa58ab --- /dev/null +++ b/PySDM/source/PySDM/physics/dimensional_analysis.py @@ -0,0 +1,27 @@ +""" +A context manager (for use with the `with` statement) which +enables Pint physical-units checks and disables Numba in `PySDM.formulae.Formulae` +""" + +from importlib import reload + +from PySDM import formulae +from PySDM import physics +from . import constants, constants_defaults +from .impl import flag + + +class DimensionalAnalysis: + def __enter__(*_): # pylint: disable=no-method-argument,no-self-argument + flag.DIMENSIONAL_ANALYSIS = True + reload(constants) + reload(constants_defaults) + reload(formulae) + reload(physics) + + def __exit__(*_): # pylint: disable=no-method-argument,no-self-argument + flag.DIMENSIONAL_ANALYSIS = False + reload(constants) + reload(constants_defaults) + reload(formulae) + reload(physics) diff --git a/PySDM/source/PySDM/physics/drop_growth/__init__.py b/PySDM/source/PySDM/physics/drop_growth/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..a3b257be62b749883c62bc56b0e7b1cc1d9d1a93 --- /dev/null +++ b/PySDM/source/PySDM/physics/drop_growth/__init__.py @@ -0,0 +1,7 @@ +""" +Formulation of the coupled heat-moisture diffusion problem +""" + +from .fick import Fick +from .howell_1949 import Howell1949 +from .mason_1971 import Mason1971 diff --git a/PySDM/source/PySDM/physics/drop_growth/fick.py b/PySDM/source/PySDM/physics/drop_growth/fick.py new file mode 100644 index 0000000000000000000000000000000000000000..bc0f1140fe072ee438c9d2a0ea8b09b0b9a7207f --- /dev/null +++ b/PySDM/source/PySDM/physics/drop_growth/fick.py @@ -0,0 +1,35 @@ +""" +Fickian diffusion only drop growth + +The notation for terms associated with heat conduction and diffusion are from +[Rogers & Yau 1989](https://archive.org/details/shortcourseinclo0000roge_m3k2). +""" + + +class Fick: + def __init__(self, _): + pass + + @staticmethod + def Fk(const, T, K, lv): # pylint: disable=unused-argument + """heat conduction not taken into account""" + return 0 + + @staticmethod + def Fd(const, T, D, pvs): + """the term associated with vapour diffusion""" + return const.rho_w * const.Rv * T / D / pvs + + @staticmethod + def r_dr_dt(RH_eq, RH, Fk, Fd): # pylint: disable=unused-argument + """Drop growth equation with radius r. + + Parameters + ---------- + Fk + Thermodynamic term associated with heat conduction from + [Rogers & Yau 1989](https://archive.org/details/shortcourseinclo0000roge_m3k2 + Fd + Term associated with vapour diffusion. + """ + return (RH - RH_eq) / Fd diff --git a/PySDM/source/PySDM/physics/drop_growth/howell_1949.py b/PySDM/source/PySDM/physics/drop_growth/howell_1949.py new file mode 100644 index 0000000000000000000000000000000000000000..44e9faf4f1825ae99db990979b86e67fd7dc3aca --- /dev/null +++ b/PySDM/source/PySDM/physics/drop_growth/howell_1949.py @@ -0,0 +1,42 @@ +""" +single-equation approximation of the vapour and heat diffusion problem +as proposed in [Howell 1949](https://doi.org/10.1175/1520-0469(1949)006%3C0134:TGOCDI%3E2.0.CO;2) +same as in [Mason 1951](https://doi.org/10.1088/0370-1301/64/9/307) + +The notation for terms associated with heat conduction and diffusion are from eq. 7.17 + in [Rogers & Yau 1971](https://archive.org/details/shortcourseinclo0000roge_m3k2). +""" + +from .fick import Fick + + +class Howell1949(Fick): # pylint: disable=too-few-public-methods + + @staticmethod + def Fk(const, T, K, lv): + """Thermodynamic term associated with heat conduction. + + Parameters + ---------- + T + Temperature. + K + Thermal diffusivity with heat ventilation factor. + lv + Latent heat of evaporation or sublimation. + """ + return const.rho_w * lv / T / K * (lv / T / const.Rv) + + @staticmethod + def r_dr_dt(RH_eq, RH, Fk, Fd): + """Drop growth equation with radius r. + + Parameters + ---------- + Fk + Thermodynamic term associated with heat conduction from + [Rogers & Yau 1989](https://archive.org/details/shortcourseinclo0000roge_m3k2). + Fd + Term associated with vapour diffusion. + """ + return (RH - RH_eq) / (Fk + Fd) diff --git a/PySDM/source/PySDM/physics/drop_growth/mason_1971.py b/PySDM/source/PySDM/physics/drop_growth/mason_1971.py new file mode 100644 index 0000000000000000000000000000000000000000..5279eea8482650741a5269c87d1066c09f6617fb --- /dev/null +++ b/PySDM/source/PySDM/physics/drop_growth/mason_1971.py @@ -0,0 +1,19 @@ +""" +single-equation approximation of the vapour and heat diffusion problem +as given in eq. 3.11 in [Mason 1971](https://archive.org/details/physicsofclouds0000maso) +(see also discussion of the ventilation effect on page 125). +The difference between Howell'49 and Mason'71 is `-1` in the `Fk` definition. + +The notation for terms associated with heat conduction and diffusion are from +[Rogers & Yau 1989](https://archive.org/details/shortcourseinclo0000roge_m3k2). +""" + +from .howell_1949 import Howell1949 + + +class Mason1971(Howell1949): # pylint: disable=too-few-public-methods + + @staticmethod + def Fk(const, T, K, lv): + """thermodynamic term associated with heat conduction""" + return const.rho_w * lv / T / K * (lv / T / const.Rv - 1) diff --git a/PySDM/source/PySDM/physics/fragmentation_function/__init__.py b/PySDM/source/PySDM/physics/fragmentation_function/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..b7a32776e68ac9de0750d777474c2c35be06a3b8 --- /dev/null +++ b/PySDM/source/PySDM/physics/fragmentation_function/__init__.py @@ -0,0 +1,13 @@ +""" +fragmentation functions for use with breakup +""" + +from .always_n import AlwaysN +from .constant_mass import ConstantMass +from .expon_frag import ExponFrag +from .exponential import Exponential +from .feingold1988 import Feingold1988 +from .gaussian import Gaussian +from .lowlist82 import LowList1982Nf +from .slams import SLAMS +from .straub2010nf import Straub2010Nf diff --git a/PySDM/source/PySDM/physics/fragmentation_function/always_n.py b/PySDM/source/PySDM/physics/fragmentation_function/always_n.py new file mode 100644 index 0000000000000000000000000000000000000000..fca8646c390753a224c7617c4728608a2011ecae --- /dev/null +++ b/PySDM/source/PySDM/physics/fragmentation_function/always_n.py @@ -0,0 +1,8 @@ +""" +Formulae supporting `PySDM.dynamics.collisions.breakup_fragmentations.always_n` +""" + + +class AlwaysN: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass diff --git a/PySDM/source/PySDM/physics/fragmentation_function/constant_mass.py b/PySDM/source/PySDM/physics/fragmentation_function/constant_mass.py new file mode 100644 index 0000000000000000000000000000000000000000..b02507d5d01450f634d779bf9f4fb5dc60eeb26c --- /dev/null +++ b/PySDM/source/PySDM/physics/fragmentation_function/constant_mass.py @@ -0,0 +1,8 @@ +""" +Formulae supporting `PySDM.dynamics.collisions.breakup_fragmentations.constant_mass` +""" + + +class ConstantMass: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass diff --git a/PySDM/source/PySDM/physics/fragmentation_function/expon_frag.py b/PySDM/source/PySDM/physics/fragmentation_function/expon_frag.py new file mode 100644 index 0000000000000000000000000000000000000000..554a14b02ee4048a94d1749d00502b33f8f0e118 --- /dev/null +++ b/PySDM/source/PySDM/physics/fragmentation_function/expon_frag.py @@ -0,0 +1,12 @@ +""" +Formulae supporting `PySDM.dynamics.collisions.breakup_fragmentations.expon_frag` +""" + +import warnings + +from .exponential import Exponential + + +class ExponFrag(Exponential): # pylint: disable=too-few-public-methods + def __init_subclass__(cls): + warnings.warn("Class has been renamed", DeprecationWarning) diff --git a/PySDM/source/PySDM/physics/fragmentation_function/exponential.py b/PySDM/source/PySDM/physics/fragmentation_function/exponential.py new file mode 100644 index 0000000000000000000000000000000000000000..f9323771a44f9f54f5f0dff3e65813903d44f41e --- /dev/null +++ b/PySDM/source/PySDM/physics/fragmentation_function/exponential.py @@ -0,0 +1,8 @@ +""" +Formulae supporting `PySDM.dynamics.collisions.breakup_fragmentations.exponential` +""" + + +class Exponential: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass diff --git a/PySDM/source/PySDM/physics/fragmentation_function/feingold1988.py b/PySDM/source/PySDM/physics/fragmentation_function/feingold1988.py new file mode 100644 index 0000000000000000000000000000000000000000..2557010c2595f018092df0653c0511c23883bcd9 --- /dev/null +++ b/PySDM/source/PySDM/physics/fragmentation_function/feingold1988.py @@ -0,0 +1,14 @@ +""" +Scaled exponential PDF +""" + +import numpy as np + + +class Feingold1988: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def frag_volume(_, scale, rand, x_plus_y, fragtol): + return -scale * np.log(max(1 - rand * scale / x_plus_y, fragtol)) diff --git a/PySDM/source/PySDM/physics/fragmentation_function/gaussian.py b/PySDM/source/PySDM/physics/fragmentation_function/gaussian.py new file mode 100644 index 0000000000000000000000000000000000000000..cc698c3ddf94477f5038e42a819521c46880fa6c --- /dev/null +++ b/PySDM/source/PySDM/physics/fragmentation_function/gaussian.py @@ -0,0 +1,8 @@ +""" +Gaussian PDF +""" + + +class Gaussian: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass diff --git a/PySDM/source/PySDM/physics/fragmentation_function/lowlist82.py b/PySDM/source/PySDM/physics/fragmentation_function/lowlist82.py new file mode 100644 index 0000000000000000000000000000000000000000..7e916e1235b05c8a6ca3c7445d2bd0ff7351d44b --- /dev/null +++ b/PySDM/source/PySDM/physics/fragmentation_function/lowlist82.py @@ -0,0 +1,186 @@ +""" +Formulae supporting `PySDM.dynamics.collisions.breakup_fragmentations.lowlist82` +""" + +import math + +import numpy as np + + +class LowList1982Nf: # pylint: disable=too-few-public-methods, too-many-locals + def __init__(self, _): + pass + + @staticmethod + def params_f1(const, dl, dcoal): + dcoalCM = dcoal / const.CM + dlCM = dl / const.CM + Hf1 = 50.8 * (dlCM) ** (-0.718) + mu = dlCM + sigma = 1 / Hf1 + for _ in range(10): + sigma = ( + 1 + / Hf1 + * np.sqrt(2 / np.pi) + / (1 + math.erf((dcoalCM - dlCM) / (np.sqrt(2) * sigma))) + ) + return (Hf1, mu, sigma) # in cm + + @staticmethod + def params_f2(const, ds): + dsCM = ds / const.CM + Hf2 = 4.18 * ((dsCM) ** (-1.17)) + mu = dsCM + sigma = 1 / (np.sqrt(2 * np.pi) * Hf2) + return (Hf2, mu, sigma) + + @staticmethod + def params_f3(const, ds, dl): # pylint: disable=too-many-locals + dsCM = ds / const.CM + dlCM = dl / const.CM + # eq (3.3), (3.4) + Ff1 = max( + 0, + ( + (-2.25e4 * (dlCM - 0.403) ** 2 - 37.9) * (dsCM) ** (2.5) + + 9.67 * (dlCM - 0.170) ** 2 + + 4.95 + ), + ) + Ff2 = 1.02e4 * dsCM ** (2.83) + 2 + # eq (3.5) + ds0 = max(0.04, (Ff1 / 2.83) ** (1 / 1.02e4)) + if dsCM > ds0: + Ff = max(2.0, Ff1) + else: + Ff = max(2.0, Ff2) + Dff3 = 0.241 * (dsCM) + 0.0129 # (4.14) + # eq (4.18) - (4.21) + Pf301 = 1.68e5 * dsCM ** (2.33) + Pf302 = max( + 0, + ( + (43.4 * (dlCM + 1.81) ** 2 - 159.0) / dsCM + - 3870 * (dlCM - 0.285) ** 2 + - 58.1 + ), + ) + alpha = (dsCM - ds0) / (0.2 * ds0) + Pf303 = alpha * Pf301 + (1 - alpha) * Pf302 + if dsCM < ds0: + Pf0 = Pf301 + elif dsCM > 1.2 * ds0: + Pf0 = Pf302 + else: + Pf0 = Pf303 + # eq (4.22), (4.16), (4.17) (4.23) + sigmaf3 = 10 * Dff3 + muf3 = np.log(Dff3) + sigmaf3**2 + Hf3 = Pf0 * Dff3 / np.exp(-0.5 * sigmaf3**2) + for _ in range(10): + if sigmaf3 == 0.0 or Hf3 == 0: + return (0.0, np.log(ds0), np.log(ds0)) + sigmaf3 = ( + np.sqrt(2 / np.pi) + * (Ff - 2) + / Hf3 + / (1 - math.erf((np.log(0.01) - muf3) / np.sqrt(2) / sigmaf3)) + ) + muf3 = np.log(Dff3) + sigmaf3**2 + Hf3 = Pf0 * Dff3 / np.exp(-0.5 * sigmaf3**2) + + return (Hf3, muf3, sigmaf3) + + @staticmethod + def params_s1(const, dl, ds, dcoal): + dsCM = ds / const.CM + dlCM = dl / const.CM + dcoalCM = dcoal / const.CM + Hs1 = 100 * np.exp(-3.25 * dsCM) + mus1 = dlCM + sigmas1 = 1 / Hs1 + for _ in range(10): + sigmas1 = ( + 1 + / Hs1 + * np.sqrt(2 / np.pi) + / (1 + math.erf((dcoalCM - dlCM) / (np.sqrt(2) * sigmas1))) + ) + return (Hs1, mus1, sigmas1) # in cm + + @staticmethod + def params_s2(const, dl, ds, St): + dsCM = ds / const.CM + dlCM = dl / const.CM + Dss2 = ( + 0.254 * (dsCM ** (0.413)) * np.exp(3.53 * dsCM ** (2.51) * (dlCM - dsCM)) + ) # (4.27) + bstar = 14.2 * np.exp(-17.2 * dsCM) + Ps20 = 0.23 * dsCM ** (-3.93) * dlCM ** (bstar) # (4.29) + sigmas2 = 10 * Dss2 # as in (4.22) + mus2 = np.log(Dss2) + sigmas2**2 # (4.32) + Hs2 = Ps20 * Dss2 / np.exp(-0.5 * sigmas2**2) # (4.28) + + Fs = 5 * math.erf((St - 2.52e-6) / (1.85e-6)) + 6 # (3.7) + + for _ in range(10): + sigmas2 = ( + np.sqrt(2 / np.pi) + * (Fs - 1) + / Hs2 + / (1 - math.erf((np.log(0.01) - mus2) / np.sqrt(2) / sigmas2)) + ) + mus2 = np.log(Dss2) + sigmas2**2 # (4.32) + Hs2 = Ps20 * Dss2 / np.exp(-0.5 * sigmas2**2) # (4.28) + + return (Hs2, mus2, sigmas2) + + @staticmethod + def params_d1(const, W1, dl, dcoal, CKE): + dlCM = dl / const.CM + dcoalCM = dcoal / const.CM + mud1 = dlCM * (1 - np.exp(-3.70 * (3.10 - W1))) + Hd1 = 1.58e-5 * CKE ** (-1.22) + sigmad1 = 1 / Hd1 + for _ in range(10): + sigmad1 = ( + 1 + / Hd1 + * np.sqrt(2 / np.pi) + / (1 + math.erf((dcoalCM - mud1) / (np.sqrt(2) * sigmad1))) + ) + + return (Hd1, mud1, sigmad1) # in cm + + @staticmethod + def params_d2(const, ds, dl, CKE): + dsCM = ds / const.CM + dlCM = dl / const.CM + Ddd2 = np.exp(-17.4 * dsCM - 0.671 * (dlCM - dsCM)) * dsCM # (4.37) + bstar = 0.007 * dsCM ** (-2.54) # (4.39) + Pd20 = 0.0884 * dsCM ** (-2.52) * (dlCM - dsCM) ** (bstar) # (4.38) + sigmad2 = 10 * Ddd2 + + mud2 = np.log(Ddd2) + sigmad2**2 + Hd2 = Pd20 * Ddd2 / np.exp(-0.5 * sigmad2**2) + + Fd = max(1.0, 297.5 + 23.7 * np.log(CKE)) # (3.9) + if Fd == 1.0: + return (0.0, np.log(Ddd2), np.log(Ddd2)) + + for _ in range(10): + if sigmad2 == 0.0 or Hd2 <= 0.1: + return (0.0, np.log(Ddd2), np.log(Ddd2)) + if sigmad2 >= 1.0: + return (0.0, np.log(Ddd2), np.log(Ddd2)) + sigmad2 = ( + np.sqrt(2 / np.pi) + * (Fd - 1) + / Hd2 + / (1 - math.erf((np.log(0.01) - mud2) / np.sqrt(2) / sigmad2)) + ) + mud2 = np.log(Ddd2) + sigmad2**2 + Hd2 = Pd20 * Ddd2 / np.exp(-0.5 * sigmad2**2) + + return (Hd2, mud2, sigmad2) diff --git a/PySDM/source/PySDM/physics/fragmentation_function/slams.py b/PySDM/source/PySDM/physics/fragmentation_function/slams.py new file mode 100644 index 0000000000000000000000000000000000000000..10a2a5bdcbf396f813e37a5dc9791daa9fb4af7b --- /dev/null +++ b/PySDM/source/PySDM/physics/fragmentation_function/slams.py @@ -0,0 +1,8 @@ +""" +Formulae supporting `PySDM.dynamics.collisions.breakup_fragmentations.slams` +""" + + +class SLAMS: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass diff --git a/PySDM/source/PySDM/physics/fragmentation_function/straub2010nf.py b/PySDM/source/PySDM/physics/fragmentation_function/straub2010nf.py new file mode 100644 index 0000000000000000000000000000000000000000..5092ae2ab0aa598ac687f503796e708370407d32 --- /dev/null +++ b/PySDM/source/PySDM/physics/fragmentation_function/straub2010nf.py @@ -0,0 +1,45 @@ +""" +Formulae supporting `PySDM.dynamics.collisions.breakup_fragmentations.straub2010` +""" + +import numpy as np + + +class Straub2010Nf: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def params_sigma1(const, CW): + return np.sqrt( + np.log( + CW + / 64 + / 100 + * const.CM + * const.CM + / 12 + / np.power(const.STRAUB_E_D1, const.TWO) + + 1 + ) + ) + + @staticmethod + def params_mu1(const, sigma1): + return np.log(const.STRAUB_E_D1) - np.power(sigma1, const.TWO) / 2 + + @staticmethod + def params_sigma2(const, CW): + return max(0.0, 7 * (CW - 21) * const.CM / 1000) / np.sqrt(const.TWELVE) + + @staticmethod + def params_mu2(const, ds): # pylint: disable=unused-argument + return const.STRAUB_MU2 + + @staticmethod + def params_sigma3(const, CW): + return (1 + 0.76 * np.sqrt(CW)) * const.CM / 100 / np.sqrt(const.TWELVE) + + @staticmethod + def params_mu3(ds): + return 0.9 * ds diff --git a/PySDM/source/PySDM/physics/freezing_temperature_spectrum/__init__.py b/PySDM/source/PySDM/physics/freezing_temperature_spectrum/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..82c9547cabd2f5954e4ee7244d22257670d2283c --- /dev/null +++ b/PySDM/source/PySDM/physics/freezing_temperature_spectrum/__init__.py @@ -0,0 +1,7 @@ +""" +freezing temperature spectra for use with singular immersion freezing representations +""" + +from .bigg_1953 import Bigg_1953 +from .niemand_et_al_2012 import Niemand_et_al_2012 +from .null import Null diff --git a/PySDM/source/PySDM/physics/freezing_temperature_spectrum/bigg_1953.py b/PySDM/source/PySDM/physics/freezing_temperature_spectrum/bigg_1953.py new file mode 100644 index 0000000000000000000000000000000000000000..53d12e765ef84719c39604719394917989972812 --- /dev/null +++ b/PySDM/source/PySDM/physics/freezing_temperature_spectrum/bigg_1953.py @@ -0,0 +1,25 @@ +""" +freezing temperature spectrum based on [Bigg 1953](https://doi.org/10.1088/0370-1301/66/8/309) + formulae (i.e. immersed surface independent) +""" + +import numpy as np + + +class Bigg_1953: + def __init__(self, const): + assert np.isfinite(const.BIGG_DT_MEDIAN) + + @staticmethod + def pdf(const, T, A_insol): # pylint: disable=unused-argument + A = np.log(1 - 0.5) + B = const.BIGG_DT_MEDIAN - const.T0 + return -A * np.exp(A * np.exp(B + T) + B + T) + + @staticmethod + def cdf(const, T, A_insol): # pylint: disable=unused-argument + return np.exp(np.log(1 - 0.5) * np.exp(const.BIGG_DT_MEDIAN - (const.T0 - T))) + + @staticmethod + def median(const): + return const.T0 - const.BIGG_DT_median diff --git a/PySDM/source/PySDM/physics/freezing_temperature_spectrum/niemand_et_al_2012.py b/PySDM/source/PySDM/physics/freezing_temperature_spectrum/niemand_et_al_2012.py new file mode 100644 index 0000000000000000000000000000000000000000..f0b92b095968f32ef7cdb80d5967f81c241c20ee --- /dev/null +++ b/PySDM/source/PySDM/physics/freezing_temperature_spectrum/niemand_et_al_2012.py @@ -0,0 +1,46 @@ +""" +freezing temperature spectrum based on + [Niemand et al. 2012](https://doi.org/10.1175/JAS-D-11-0249.1) INAS density parameterization +""" + +import numpy as np + + +class Niemand_et_al_2012: + def __str__(self): + return "Niemand et al. 2012" + + def __init__(self, const): + assert np.isfinite(const.NIEMAND_A) + assert np.isfinite(const.NIEMAND_B) + + @staticmethod + def ns(const, T): + return np.exp(const.NIEMAND_A * (T - const.T0) + const.NIEMAND_B) + + @staticmethod + def pdf(const, T, A_insol): + ns_T = np.exp(const.NIEMAND_A * (T - const.T0) + const.NIEMAND_B) + return -A_insol * const.NIEMAND_A * ns_T * np.exp(-A_insol * ns_T) + + @staticmethod + def cdf(const, T, A_insol): + ns_T = np.exp(const.NIEMAND_A * (T - const.T0) + const.NIEMAND_B) + return ( + 1 + - np.exp(-A_insol * ns_T) + - np.exp(-A_insol * np.exp(-const.NIEMAND_A * const.T0 + const.NIEMAND_B)) + ) + + @staticmethod + def invcdf(const, cdf, A_insol): + tmp = np.log( + ( + np.log(1 - cdf) + + np.exp( + -A_insol * np.exp(-const.NIEMAND_A * const.T0 + const.NIEMAND_B) + ) + ) + / -A_insol + ) + return const.T0 + (tmp - const.NIEMAND_B) / const.NIEMAND_A diff --git a/PySDM/source/PySDM/physics/freezing_temperature_spectrum/null.py b/PySDM/source/PySDM/physics/freezing_temperature_spectrum/null.py new file mode 100644 index 0000000000000000000000000000000000000000..a4e737f03b5ff0a757d554212decd38056d61a61 --- /dev/null +++ b/PySDM/source/PySDM/physics/freezing_temperature_spectrum/null.py @@ -0,0 +1,13 @@ +""" +null spectrum (needed as other formulations require parameters + to be set before instantiation of Formulae) +""" + + +class Null: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def cdf(const, T): + pass diff --git a/PySDM/source/PySDM/physics/heterogeneous_ice_nucleation_rate/__init__.py b/PySDM/source/PySDM/physics/heterogeneous_ice_nucleation_rate/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..91fe9627d68f1beac0453080d7c1c16f661a6e16 --- /dev/null +++ b/PySDM/source/PySDM/physics/heterogeneous_ice_nucleation_rate/__init__.py @@ -0,0 +1,7 @@ +""" +immersion-freezing rate (aka J_het) formulations +""" + +from .abifm import ABIFM +from .constant import Constant +from .null import Null diff --git a/PySDM/source/PySDM/physics/heterogeneous_ice_nucleation_rate/abifm.py b/PySDM/source/PySDM/physics/heterogeneous_ice_nucleation_rate/abifm.py new file mode 100644 index 0000000000000000000000000000000000000000..d41f2a2f36033d56aa91a86b4779cebb1e1faed2 --- /dev/null +++ b/PySDM/source/PySDM/physics/heterogeneous_ice_nucleation_rate/abifm.py @@ -0,0 +1,16 @@ +""" +ABIFM heterogeneous freezing rate parameterization + ([Knopf & Alpert 2013](https://doi.org/10.1039/C3FD00035D)) +""" + +import numpy as np + + +class ABIFM: # pylint: disable=too-few-public-methods + def __init__(self, const): + assert np.isfinite(const.ABIFM_M) + assert np.isfinite(const.ABIFM_C) + + @staticmethod + def j_het(const, a_w_ice): + return 10 ** (const.ABIFM_M * (1 - a_w_ice) + const.ABIFM_C) * const.ABIFM_UNIT diff --git a/PySDM/source/PySDM/physics/heterogeneous_ice_nucleation_rate/constant.py b/PySDM/source/PySDM/physics/heterogeneous_ice_nucleation_rate/constant.py new file mode 100644 index 0000000000000000000000000000000000000000..4fc862be3f662350db541960c425ce611e85c99d --- /dev/null +++ b/PySDM/source/PySDM/physics/heterogeneous_ice_nucleation_rate/constant.py @@ -0,0 +1,14 @@ +""" +constant rate formulation (for tests) +""" + +import numpy as np + + +class Constant: # pylint: disable=too-few-public-methods + def __init__(self, const): + assert np.isfinite(const.J_HET) + + @staticmethod + def j_het(const, a_w_ice): # pylint: disable=unused-argument + return const.J_HET diff --git a/PySDM/source/PySDM/physics/heterogeneous_ice_nucleation_rate/null.py b/PySDM/source/PySDM/physics/heterogeneous_ice_nucleation_rate/null.py new file mode 100644 index 0000000000000000000000000000000000000000..b7e8703f2e1f7f117e8150acf94b4eb82e0c79f8 --- /dev/null +++ b/PySDM/source/PySDM/physics/heterogeneous_ice_nucleation_rate/null.py @@ -0,0 +1,13 @@ +""" +do-nothing null formulation (needed as other formulations require parameters + to be set before instantiation of Formulae) +""" + + +class Null: # pylint: disable=too-few-public-methods,unused-argument + def __init__(self, _): + pass + + @staticmethod + def j_het(const, a_w_ice): + return 0 diff --git a/PySDM/source/PySDM/physics/homogeneous_ice_nucleation_rate/__init__.py b/PySDM/source/PySDM/physics/homogeneous_ice_nucleation_rate/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..95fe7ed62291a82dc28b49b30e2525a4c667624b --- /dev/null +++ b/PySDM/source/PySDM/physics/homogeneous_ice_nucleation_rate/__init__.py @@ -0,0 +1,9 @@ +""" +homogeneous-freezing rate (aka J_hom) formulations +""" + +from .constant import Constant +from .null import Null +from .koop import Koop2000 +from .koop_corr import Koop_Correction +from .koop_murray import KoopMurray2016 diff --git a/PySDM/source/PySDM/physics/homogeneous_ice_nucleation_rate/constant.py b/PySDM/source/PySDM/physics/homogeneous_ice_nucleation_rate/constant.py new file mode 100644 index 0000000000000000000000000000000000000000..fadb1829ef59d9e10b3ae9e8e4d478beedc2762c --- /dev/null +++ b/PySDM/source/PySDM/physics/homogeneous_ice_nucleation_rate/constant.py @@ -0,0 +1,22 @@ +""" +constant rate formulation (for tests) +""" + +import numpy as np + + +class Constant: + def __init__(self, const): + assert np.isfinite(const.J_HOM) + + @staticmethod + def d_a_w_ice_within_range(const, da_w_ice): # pylint: disable=unused-argument + return True + + @staticmethod + def d_a_w_ice_maximum(const, da_w_ice): # pylint: disable=unused-argument + return da_w_ice + + @staticmethod + def j_hom(const, T, da_w_ice): # pylint: disable=unused-argument + return const.J_HOM diff --git a/PySDM/source/PySDM/physics/homogeneous_ice_nucleation_rate/koop.py b/PySDM/source/PySDM/physics/homogeneous_ice_nucleation_rate/koop.py new file mode 100644 index 0000000000000000000000000000000000000000..c7b1156e4a11fbb9cc333803d633d15d0448a6aa --- /dev/null +++ b/PySDM/source/PySDM/physics/homogeneous_ice_nucleation_rate/koop.py @@ -0,0 +1,35 @@ +""" +Koop homogeneous nucleation rate parameterization for solution droplets +valid for 0.26 < da_w_ice < 0.34 + ([Koop et al. 2000](https://doi.org/10.1038/35020537)) +""" + +import numpy as np + + +class Koop2000: + def __init__(self, const): + pass + + @staticmethod + def d_a_w_ice_within_range(const, da_w_ice): + return da_w_ice >= const.KOOP_MIN_DA_W_ICE + + @staticmethod + def d_a_w_ice_maximum(const, da_w_ice): + return np.where( + da_w_ice > const.KOOP_MAX_DA_W_ICE, const.KOOP_MAX_DA_W_ICE, da_w_ice + ) + + @staticmethod + def j_hom(const, T, da_w_ice): # pylint: disable=unused-argument + return ( + 10 + ** ( + const.KOOP_2000_C1 + + const.KOOP_2000_C2 * da_w_ice + + const.KOOP_2000_C3 * da_w_ice**2.0 + + const.KOOP_2000_C4 * da_w_ice**3.0 + ) + * const.KOOP_UNIT + ) diff --git a/PySDM/source/PySDM/physics/homogeneous_ice_nucleation_rate/koop_corr.py b/PySDM/source/PySDM/physics/homogeneous_ice_nucleation_rate/koop_corr.py new file mode 100644 index 0000000000000000000000000000000000000000..82873e8fe9ce9c79d39509887556f770fa1f8164 --- /dev/null +++ b/PySDM/source/PySDM/physics/homogeneous_ice_nucleation_rate/koop_corr.py @@ -0,0 +1,37 @@ +""" +Koop homogeneous nucleation rate parameterization for solution droplets [Koop et al. 2000] corrected +such that it coincides with homogeneous nucleation rate parameterization for pure water droplets +[Koop and Murray 2016] at water saturation between 235K < T < 240K + ([Spichtinger et al. 2023](https://doi.org/10.5194/acp-23-2035-2023)) +""" + +import numpy as np + + +class Koop_Correction: + def __init__(self, const): + pass + + @staticmethod + def d_a_w_ice_within_range(const, da_w_ice): + return da_w_ice >= const.KOOP_MIN_DA_W_ICE + + @staticmethod + def d_a_w_ice_maximum(const, da_w_ice): + return np.where( + da_w_ice > const.KOOP_MAX_DA_W_ICE, const.KOOP_MAX_DA_W_ICE, da_w_ice + ) + + @staticmethod + def j_hom(const, T, da_w_ice): # pylint: disable=unused-argument + return ( + 10 + ** ( + const.KOOP_2000_C1 + + const.KOOP_2000_C2 * da_w_ice + + const.KOOP_2000_C3 * da_w_ice**2.0 + + const.KOOP_2000_C4 * da_w_ice**3.0 + + const.KOOP_CORR + ) + * const.KOOP_UNIT + ) diff --git a/PySDM/source/PySDM/physics/homogeneous_ice_nucleation_rate/koop_murray.py b/PySDM/source/PySDM/physics/homogeneous_ice_nucleation_rate/koop_murray.py new file mode 100644 index 0000000000000000000000000000000000000000..0028411fcc7d973da69274854df136e699274759 --- /dev/null +++ b/PySDM/source/PySDM/physics/homogeneous_ice_nucleation_rate/koop_murray.py @@ -0,0 +1,38 @@ +""" +Koop and Murray homogeneous nucleation rate parameterization for pure water droplets +at water saturation +([eq. A9, Tab VII in Koop and Murray 2016](https://doi.org/10.1063/1.4962355)) +""" + +import numpy as np + + +class KoopMurray2016: + def __init__(self, const): + pass + + @staticmethod + def d_a_w_ice_within_range(const, da_w_ice): + return da_w_ice >= const.KOOP_MIN_DA_W_ICE + + @staticmethod + def d_a_w_ice_maximum(const, da_w_ice): + return np.where( + da_w_ice > const.KOOP_MAX_DA_W_ICE, const.KOOP_MAX_DA_W_ICE, da_w_ice + ) + + @staticmethod + def j_hom(const, T, da_w_ice): # pylint: disable=unused-argument + return ( + 10 + ** ( + const.KOOP_MURRAY_C0 + + const.KOOP_MURRAY_C1 * (T - const.T0) + + const.KOOP_MURRAY_C2 * (T - const.T0) ** 2.0 + + const.KOOP_MURRAY_C3 * (T - const.T0) ** 3.0 + + const.KOOP_MURRAY_C4 * (T - const.T0) ** 4.0 + + const.KOOP_MURRAY_C5 * (T - const.T0) ** 5.0 + + const.KOOP_MURRAY_C6 * (T - const.T0) ** 6.0 + ) + * const.KOOP_UNIT + ) diff --git a/PySDM/source/PySDM/physics/homogeneous_ice_nucleation_rate/null.py b/PySDM/source/PySDM/physics/homogeneous_ice_nucleation_rate/null.py new file mode 100644 index 0000000000000000000000000000000000000000..230d933106e4134fbf5ec158db11ebedb72e045f --- /dev/null +++ b/PySDM/source/PySDM/physics/homogeneous_ice_nucleation_rate/null.py @@ -0,0 +1,23 @@ +""" +do-nothing null formulation (needed as other formulations require parameters + to be set before instantiation of Formulae) +""" + +import numpy as np + + +class Null: # pylint: disable=unused-argument + def __init__(self, _): + pass + + @staticmethod + def d_a_w_ice_within_range(const, da_w_ice): # pylint: disable=unused-argument + return True + + @staticmethod + def d_a_w_ice_maximum(const, da_w_ice): # pylint: disable=unused-argument + return da_w_ice + + @staticmethod + def j_hom(const, T, d_a_w_ice): # pylint: disable=unused-argument + return np.nan diff --git a/PySDM/source/PySDM/physics/hydrostatics/__init__.py b/PySDM/source/PySDM/physics/hydrostatics/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..65f93b364fb970f8f5aa1598fc3f597e549025d6 --- /dev/null +++ b/PySDM/source/PySDM/physics/hydrostatics/__init__.py @@ -0,0 +1,8 @@ +""" +Helper routines for hydrostatic pressure and density profiles +""" + +from .constant_g_vapour_mixing_ratio_and_theta_std import ( + ConstantGVapourMixingRatioAndThetaStd, +) +from .variable_g_isothermal import VariableGIsothermal diff --git a/PySDM/source/PySDM/physics/hydrostatics/constant_g_vapour_mixing_ratio_and_theta_std.py b/PySDM/source/PySDM/physics/hydrostatics/constant_g_vapour_mixing_ratio_and_theta_std.py new file mode 100644 index 0000000000000000000000000000000000000000..8f2da45a4852d9bbecf5bba2c0eb4b569d96a8b9 --- /dev/null +++ b/PySDM/source/PySDM/physics/hydrostatics/constant_g_vapour_mixing_ratio_and_theta_std.py @@ -0,0 +1,43 @@ +""" +hydrostatic profile assuming constant g, constant water vapour mixing ratio +and constant (standard) potential temperature +""" + +import numpy as np + + +class ConstantGVapourMixingRatioAndThetaStd: + def __init__(self, _): + pass + + # pylint: disable=too-many-arguments + @staticmethod + def drho_dz( + const, p, T, water_vapour_mixing_ratio, lv, d_liquid_water_mixing_ratio__dz=0 + ): + Rq = const.Rv / (1 / water_vapour_mixing_ratio + 1) + const.Rd / ( + 1 + water_vapour_mixing_ratio + ) + cp = const.c_pv / (1 / water_vapour_mixing_ratio + 1) + const.c_pd / ( + 1 + water_vapour_mixing_ratio + ) + rho = p / Rq / T + return ( + const.g_std / T * rho * (Rq / cp - 1) + - p * lv / cp / T**2 * d_liquid_water_mixing_ratio__dz + ) / Rq + + # pylint: disable=too-many-arguments + @staticmethod + def p_of_z_assuming_const_th_and_initial_water_vapour_mixing_ratio( + const, p0, thstd, water_vapour_mixing_ratio, z + ): + z0 = 0 + Rq = const.Rv / (1 / water_vapour_mixing_ratio + 1) + const.Rd / ( + 1 + water_vapour_mixing_ratio + ) + arg = ( + np.power(p0 / const.p1000, const.Rd_over_c_pd) + - (z - z0) * const.Rd_over_c_pd * const.g_std / thstd / Rq + ) + return const.p1000 * np.power(arg, 1 / const.Rd_over_c_pd) diff --git a/PySDM/source/PySDM/physics/hydrostatics/variable_g_isothermal.py b/PySDM/source/PySDM/physics/hydrostatics/variable_g_isothermal.py new file mode 100644 index 0000000000000000000000000000000000000000..786750d65a99fcd4d2239dfa9ef8fccdb1d63c29 --- /dev/null +++ b/PySDM/source/PySDM/physics/hydrostatics/variable_g_isothermal.py @@ -0,0 +1,25 @@ +""" +assuming constant temperature and variable +gravitational acceleration g(z) = g0 * R^2 / (R+z)^2 +as in [Toon et al. 1980](https://doi.org/10.1016/0019-1035(80)90173-6) +""" + +import numpy as np +from PySDM.physics import constants_defaults + + +class VariableGIsothermal: # pylint: disable=too-few-public-methods + def __init__(self, const): + assert np.isfinite(const.celestial_body_radius) + assert const.g_std != constants_defaults.g_std + + @staticmethod + def pressure(const, z, p0, temperature, molar_mass): + return p0 * np.exp( + -const.g_std + / const.R_str + * molar_mass + / temperature + * z + / (1 + z / const.celestial_body_radius) + ) diff --git a/PySDM/source/PySDM/physics/hygroscopicity/__init__.py b/PySDM/source/PySDM/physics/hygroscopicity/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f102d926519af23314a7ff5b9c324b4c6df7e10b --- /dev/null +++ b/PySDM/source/PySDM/physics/hygroscopicity/__init__.py @@ -0,0 +1,6 @@ +""" +Alternative formulations of particle hygroscopicity for condensational drop growth/evaporation +""" + +from .kappa_koehler import KappaKoehler +from .kappa_koehler_leading_terms import KappaKoehlerLeadingTerms diff --git a/PySDM/source/PySDM/physics/hygroscopicity/kappa_koehler.py b/PySDM/source/PySDM/physics/hygroscopicity/kappa_koehler.py new file mode 100644 index 0000000000000000000000000000000000000000..339c0baaabd9b5a710e31840ab51861793f92c59 --- /dev/null +++ b/PySDM/source/PySDM/physics/hygroscopicity/kappa_koehler.py @@ -0,0 +1,25 @@ +""" +kappa-Koehler parameterization + ([Petters & Kreidenweis 2007](https://doi.org/10.5194/acp-7-1961-2007)) +""" + +import numpy as np + + +class KappaKoehler: + def __init__(self, _): + pass + + # pylint: disable=too-many-arguments + @staticmethod + def RH_eq(const, r, T, kp, rd3, sgm): + return ( + np.exp((2 * sgm / const.Rv / T / const.rho_w) / r) + * (r**3 - rd3) + / (r**3 - rd3 * (1 - kp)) + ) + + @staticmethod + def r_cr(const, kp, rd3, T, sgm): + # TODO #493 + return np.sqrt(3 * kp * rd3 / (2 * sgm / const.Rv / T / const.rho_w)) diff --git a/PySDM/source/PySDM/physics/hygroscopicity/kappa_koehler_leading_terms.py b/PySDM/source/PySDM/physics/hygroscopicity/kappa_koehler_leading_terms.py new file mode 100644 index 0000000000000000000000000000000000000000..83aef8410859dc211721d428e1e598eb337fbeae --- /dev/null +++ b/PySDM/source/PySDM/physics/hygroscopicity/kappa_koehler_leading_terms.py @@ -0,0 +1,25 @@ +""" +leading-terms of the kappa-Koehler parameterization resulting in classic + two-term formulation with 1/r and 1/r^3 terms corresponding to surface-tension + (Kelvin) and soluble substance (Raoult/Wüllner) effects, respectively +""" + +import numpy as np + + +class KappaKoehlerLeadingTerms: + def __init__(self, _): + pass + + # pylint: disable=too-many-arguments + @staticmethod + def RH_eq(const, r, T, kp, rd3, sgm): + return ( + 1 + + (2 * sgm / const.Rv / T / const.rho_w) / r + - kp * rd3 / np.power(r, const.THREE) + ) + + @staticmethod + def r_cr(const, kp, rd3, T, sgm): + return np.sqrt(3 * kp * rd3 / (2 * sgm / const.Rv / T / const.rho_w)) diff --git a/PySDM/source/PySDM/physics/impl/__init__.py b/PySDM/source/PySDM/physics/impl/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..0c0a26974805ea59a20241b67f8e006d7c9195d2 --- /dev/null +++ b/PySDM/source/PySDM/physics/impl/__init__.py @@ -0,0 +1,6 @@ +""" +physics stuff not intended to be imported from user code +(incl. the `PySDM.physics.impl.fake_unit_registry.FakeUnitRegistry`) +""" + +from . import flag diff --git a/PySDM/source/PySDM/physics/impl/fake_unit_registry.py b/PySDM/source/PySDM/physics/impl/fake_unit_registry.py new file mode 100644 index 0000000000000000000000000000000000000000..ddac31daef1d71c812209aab5d278570389554f6 --- /dev/null +++ b/PySDM/source/PySDM/physics/impl/fake_unit_registry.py @@ -0,0 +1,58 @@ +"""logic around `PySDM.physics.impl.fake_unit_registry.FakeUnitRegistry` - PySDM mock of Pint's +[UnitRegistry](https://pint.readthedocs.io/en/stable/api/base.html#pint.UnitRegistry), +with the genuine Pint class used only within unit tests through the +`PySDM.physics.dimensional_analysis.DimensionalAnalysis` context manager +""" + + +def _fake(si_unit): + return (1.0 * si_unit).to_base_units().magnitude + + +class FakeUnitRegistry: # pylint: disable=too-few-public-methods + def __init__(self, si): + self.dimensionless = 1.0 + for prefix in ("nano", "micro", "milli", "centi", "deci", "", "hecto", "kilo"): + for unit in ( + "bar", + "metre", + "meter", + "gram", + "hertz", + "mole", + "joule", + "kelvin", + "second", + "minute", + "pascal", + "litre", + "liter", + "hour", + "newton", + "watt", + ): + self.__setattr__(prefix + unit, _fake(si.__getattr__(prefix + unit))) + self.__setattr__( + prefix + unit + "s", _fake(si.__getattr__(prefix + unit + "s")) + ) + + for prefix in ("n", "u", "m", "c", "d", "", "h", "k"): + for unit in ( + "b", + "m", + "g", + "Hz", + "mol", + "J", + "K", + "s", + "min", + "day", + "Pa", + "l", + "h", + "bar", # note: "b" is barn !!! + "N", + "W", + ): + self.__setattr__(prefix + unit, _fake(si.__getattr__(prefix + unit))) diff --git a/PySDM/source/PySDM/physics/impl/flag.py b/PySDM/source/PySDM/physics/impl/flag.py new file mode 100644 index 0000000000000000000000000000000000000000..883e57fef68e3be19ff01f8f0525187edba5caf1 --- /dev/null +++ b/PySDM/source/PySDM/physics/impl/flag.py @@ -0,0 +1,6 @@ +"""flag disabling `PySDM.physics.impl.fake_unit_registry.FakeUnitRegistry` within +`PySDM.physics.dimensional_analysis.DimensionalAnalysis` +context manager +""" + +DIMENSIONAL_ANALYSIS = False diff --git a/PySDM/source/PySDM/physics/isotope_diffusivity_ratios/__init__.py b/PySDM/source/PySDM/physics/isotope_diffusivity_ratios/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..8babf250f90a796c92ab99da080d9f3f949f513d --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_diffusivity_ratios/__init__.py @@ -0,0 +1,6 @@ +"""heavy-to-light diffusivity ratios for water molecules""" + +from PySDM.impl.null_physics_class import Null +from .grahams_law import GrahamsLaw +from .stewart_1975 import Stewart1975 +from .hellmann_and_harvey_2020 import HellmannAndHarvey2020 diff --git a/PySDM/source/PySDM/physics/isotope_diffusivity_ratios/grahams_law.py b/PySDM/source/PySDM/physics/isotope_diffusivity_ratios/grahams_law.py new file mode 100644 index 0000000000000000000000000000000000000000..7079a1848f1bfbfd741cdae80bf0edf2b5355534 --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_diffusivity_ratios/grahams_law.py @@ -0,0 +1,20 @@ +"""[Graham's law](https://en.wikipedia.org/wiki/Graham%27s_law) +see also eq. (21) in [Horita et al. 2008](https://doi.org/10.1080/10256010801887174) +""" + + +class GrahamsLaw: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def ratio_2H_heavy_to_light(const, temperature): # pylint: disable=unused-argument + return ( + (2 * const.M_1H + const.M_16O) / (const.M_2H + const.M_1H + const.M_16O) + ) ** const.ONE_HALF + + @staticmethod + def ratio_3H_heavy_to_light(const, temperature): # pylint: disable=unused-argument + return ( + (2 * const.M_1H + const.M_16O) / (const.M_3H + const.M_1H + const.M_16O) + ) ** const.ONE_HALF diff --git a/PySDM/source/PySDM/physics/isotope_diffusivity_ratios/hellmann_and_harvey_2020.py b/PySDM/source/PySDM/physics/isotope_diffusivity_ratios/hellmann_and_harvey_2020.py new file mode 100644 index 0000000000000000000000000000000000000000..2bd65e5afaa2489abab1ba32f435326ff3a84ecc --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_diffusivity_ratios/hellmann_and_harvey_2020.py @@ -0,0 +1,39 @@ +""" +Ratios of diffusivity in air of heavy vs. light isotope using fits provided in +[Hellmann & Harvey 2020](https://doi.org/10.1029/2020GL089999) +""" + + +class HellmannAndHarvey2020: + def __init__(self, _): + pass + + @staticmethod + def ratio_2H_heavy_to_light(const, temperature): + return ( + const.HELLMANN_HARVEY_EQ6_COEFF0 + + const.HELLMANN_HARVEY_EQ6_COEFF1 + / (temperature / const.HELLMANN_HARVEY_T_UNIT) + + const.HELLMANN_HARVEY_EQ6_COEFF2 + / (temperature / const.HELLMANN_HARVEY_T_UNIT) ** (const.TWO_AND_A_HALF) + ) + + @staticmethod + def ratio_17O_heavy_to_light(const, temperature): + return ( + const.HELLMANN_HARVEY_EQ7_COEFF0 + + const.HELLMANN_HARVEY_EQ7_COEFF1 + / (temperature / const.HELLMANN_HARVEY_T_UNIT) ** (const.ONE_HALF) + + const.HELLMANN_HARVEY_EQ7_COEFF2 + / (temperature / const.HELLMANN_HARVEY_T_UNIT) ** (const.TWO_AND_A_HALF) + ) + + @staticmethod + def ratio_18O_heavy_to_light(const, temperature): + return ( + const.HELLMANN_HARVEY_EQ8_COEFF0 + + const.HELLMANN_HARVEY_EQ8_COEFF1 + / (temperature / const.HELLMANN_HARVEY_T_UNIT) ** (const.ONE_HALF) + + const.HELLMANN_HARVEY_EQ8_COEFF2 + / (temperature / const.HELLMANN_HARVEY_T_UNIT) ** (const.THREE) + ) diff --git a/PySDM/source/PySDM/physics/isotope_diffusivity_ratios/stewart_1975.py b/PySDM/source/PySDM/physics/isotope_diffusivity_ratios/stewart_1975.py new file mode 100644 index 0000000000000000000000000000000000000000..7c04caec030cc050120d25c6bc3830c42fb2dfda --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_diffusivity_ratios/stewart_1975.py @@ -0,0 +1,40 @@ +""" +Temperature-independent ratio of vapour diffusivity in air for heavy vs. light isotopes +assuming same collision diameters for different isotopic water molecules, see: +eq. (8) in [Stewart 1975](https://doi.org/10.1029/JC080i009p01133), +eq. (1) in [Merlivat 1978](https://doi.org/10.1063/1.436884), +eq. (6) in [Cappa et al. 2003](https://doi.org/10.1029/2003JD003597), +eq. (22) in [Horita et al. 2008](https://doi.org/10.1080/10256010801887174), +eq. (3) in [Hellmann and Harvey 2020](https://doi.org/10.1029/2020GL089999). + +All functions return constants, so there is a potential overhead in computing them on each call, +but this variant is provided for historical reference only, hence leaving like that. +""" + + +class Stewart1975: + def __init__(self, _): + pass + + @staticmethod + def ratio_2H_heavy_to_light(const, temperature): # pylint: disable=unused-argument + return ( + ( + (2 * const.M_1H + const.M_16O) + * (const.Md + const.M_2H + const.M_1H + const.M_16O) + ) + / ( + (const.M_2H + const.M_1H + const.M_16O) + * (const.Md + (2 * const.M_1H + const.M_16O)) + ) + ) ** const.ONE_HALF + + @staticmethod + def ratio_18O_heavy_to_light(const, temperature): # pylint: disable=unused-argument + return ( + ((2 * const.M_1H + const.M_16O) * (const.Md + 2 * const.M_1H + const.M_18O)) + / ( + (2 * const.M_1H + const.M_18O) + * (const.Md + (2 * const.M_1H + const.M_16O)) + ) + ) ** const.ONE_HALF diff --git a/PySDM/source/PySDM/physics/isotope_equilibrium_fractionation_factors/__init__.py b/PySDM/source/PySDM/physics/isotope_equilibrium_fractionation_factors/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..abcfaf98f608b9d9c407f4dbb559bca2a2d94ccc --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_equilibrium_fractionation_factors/__init__.py @@ -0,0 +1,12 @@ +"""factors describing partitioning of water isotopologues under thermodynamic +phase equilibrium, defined as ratios alpha of isotopic ratios R""" + +from PySDM.impl.null_physics_class import Null +from .barkan_and_luz_2005 import BarkanAndLuz2005 +from .horita_and_wesolowski_1994 import HoritaAndWesolowski1994 +from .majoube_1970 import Majoube1970 +from .majoube_1971 import Majoube1971 +from .merlivat_and_nief_1967 import MerlivatAndNief1967 +from .van_hook_1968 import VanHook1968 +from .lamb_et_al_2017 import LambEtAl2017 +from .ellehoj_et_al_2013 import EllehojEtAl2013 diff --git a/PySDM/source/PySDM/physics/isotope_equilibrium_fractionation_factors/barkan_and_luz_2005.py b/PySDM/source/PySDM/physics/isotope_equilibrium_fractionation_factors/barkan_and_luz_2005.py new file mode 100644 index 0000000000000000000000000000000000000000..e5904f494b9e87322ed5827dc5cb5ec9c1889754 --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_equilibrium_fractionation_factors/barkan_and_luz_2005.py @@ -0,0 +1,13 @@ +""" +Equilibrium fractionation factor for Oxygen-17 from +[Barkan and Luz 2005](https://doi.org/10.1002/rcm.2250) +""" + + +class BarkanAndLuz2005: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def alpha_l_17O(const, _, alpha_l_18O): + return alpha_l_18O**const.BARKAN_AND_LUZ_2005_EXPONENT diff --git a/PySDM/source/PySDM/physics/isotope_equilibrium_fractionation_factors/ellehoj_et_al_2013.py b/PySDM/source/PySDM/physics/isotope_equilibrium_fractionation_factors/ellehoj_et_al_2013.py new file mode 100644 index 0000000000000000000000000000000000000000..fcdd5658203d72ed4942ce7937e10341549d17b8 --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_equilibrium_fractionation_factors/ellehoj_et_al_2013.py @@ -0,0 +1,19 @@ +""" +Equilibrium fractionation factors from +[Ellehoj et al. 2013](https://doi.org/10.1002/rcm.6668) +""" + +import numpy as np + + +class EllehojEtAl2013: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def alpha_i_2H(const, T): + return np.exp( + const.ELLEHOJ_ET_AL_2013_ALPHA_I_2H_T2 / T**2 + + const.ELLEHOJ_ET_AL_2013_ALPHA_I_2H_T1 / T + + const.ELLEHOJ_ET_AL_2013_ALPHA_I_2H_T0 + ) diff --git a/PySDM/source/PySDM/physics/isotope_equilibrium_fractionation_factors/horita_and_wesolowski_1994.py b/PySDM/source/PySDM/physics/isotope_equilibrium_fractionation_factors/horita_and_wesolowski_1994.py new file mode 100644 index 0000000000000000000000000000000000000000..34e772eb79b7dfea2d871d8dedda086dd2dc4dd6 --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_equilibrium_fractionation_factors/horita_and_wesolowski_1994.py @@ -0,0 +1,30 @@ +""" +Equilibrium fractionation factors from +[Horita and Wesolowski 1994](https://doi.org/10.1016/0016-7037(94)90096-5) +""" + +import numpy as np + + +class HoritaAndWesolowski1994: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def alpha_l_18O(const, T): + return np.exp( + const.HORITA_AND_WESOLOWSKI_1994_ALPHA_L_18O_T3 / T**3 + + const.HORITA_AND_WESOLOWSKI_1994_ALPHA_L_18O_T2 / T**2 + + const.HORITA_AND_WESOLOWSKI_1994_ALPHA_L_18O_T1 / T + + const.HORITA_AND_WESOLOWSKI_1994_ALPHA_L_18O_T0 + ) + + @staticmethod + def alpha_l_2H(const, T): + return np.exp( + const.HORITA_AND_WESOLOWSKI_1994_ALPHA_L_2H_T3 / T**3 + + const.HORITA_AND_WESOLOWSKI_1994_ALPHA_L_2H_T_0 + + const.HORITA_AND_WESOLOWSKI_1994_ALPHA_L_2H_T_1 * T + + const.HORITA_AND_WESOLOWSKI_1994_ALPHA_L_2H_T_2 * T**2 + + const.HORITA_AND_WESOLOWSKI_1994_ALPHA_L_2H_T_3 * T**3 + ) diff --git a/PySDM/source/PySDM/physics/isotope_equilibrium_fractionation_factors/lamb_et_al_2017.py b/PySDM/source/PySDM/physics/isotope_equilibrium_fractionation_factors/lamb_et_al_2017.py new file mode 100644 index 0000000000000000000000000000000000000000..baa58002468f9a070d2917b6da95da9c3c67111b --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_equilibrium_fractionation_factors/lamb_et_al_2017.py @@ -0,0 +1,19 @@ +""" +Equilibrium fractionation factors from +[Lamb et al. 2017](https://doi.org/10.1073/pnas.1618374114) +""" + +import numpy as np + + +class LambEtAl2017: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def alpha_i_2H(const, T): + return np.exp( + const.LAMB_ET_AL_2017_ALPHA_I_2H_T2 / T**2 + + const.LAMB_ET_AL_2017_ALPHA_I_2H_T1 / T + + const.LAMB_ET_AL_2017_ALPHA_I_2H_T0 + ) diff --git a/PySDM/source/PySDM/physics/isotope_equilibrium_fractionation_factors/majoube_1970.py b/PySDM/source/PySDM/physics/isotope_equilibrium_fractionation_factors/majoube_1970.py new file mode 100644 index 0000000000000000000000000000000000000000..ca4d5624ccbf51dc7852babe190ea581f6a4b6d0 --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_equilibrium_fractionation_factors/majoube_1970.py @@ -0,0 +1,19 @@ +""" +Equilibrium fractionation factors from [Majoube 1970](https://doi.org/10.1038/2261242a0) +(also published by the same author in [Majoube 1971](https://doi.org/10.1051/jcp/1971680625)) +""" + +import numpy as np + + +class Majoube1970: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def alpha_i_18O(const, T): + return np.exp( + const.MAJOUBE_1970_ALPHA_I_18O_T2 / T**2 + + const.MAJOUBE_1970_ALPHA_I_18O_T1 / T + + const.MAJOUBE_1970_ALPHA_I_18O_T0 + ) diff --git a/PySDM/source/PySDM/physics/isotope_equilibrium_fractionation_factors/majoube_1971.py b/PySDM/source/PySDM/physics/isotope_equilibrium_fractionation_factors/majoube_1971.py new file mode 100644 index 0000000000000000000000000000000000000000..2aa8aba889389488af8fe5f9653ee59a7e84ce5f --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_equilibrium_fractionation_factors/majoube_1971.py @@ -0,0 +1,26 @@ +""" +Equilibrium fractionation factors from [Majoube 1971](https://doi.org/10.1051/jcp/1971681423) +""" + +import numpy as np + + +class Majoube1971: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def alpha_l_18O(const, T): + return np.exp( + const.MAJOUBE_1971_ALPHA_L_18O_T2 / T**2 + + const.MAJOUBE_1971_ALPHA_L_18O_T1 / T + + const.MAJOUBE_1971_ALPHA_L_18O_T0 + ) + + @staticmethod + def alpha_l_2H(const, T): + return np.exp( + const.MAJOUBE_1971_ALPHA_L_2H_T2 / T**2 + + const.MAJOUBE_1971_ALPHA_L_2H_T1 / T + + const.MAJOUBE_1971_ALPHA_L_2H_T0 + ) diff --git a/PySDM/source/PySDM/physics/isotope_equilibrium_fractionation_factors/merlivat_and_nief_1967.py b/PySDM/source/PySDM/physics/isotope_equilibrium_fractionation_factors/merlivat_and_nief_1967.py new file mode 100644 index 0000000000000000000000000000000000000000..ce20b27916e3eb00028233690cdd9edd0ab8bee2 --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_equilibrium_fractionation_factors/merlivat_and_nief_1967.py @@ -0,0 +1,27 @@ +""" +Equilibrium fractionation factors from +[Merlivat and Nief 1967](https://doi.org/10.3402/tellusa.v19i1.9756) +""" + +import numpy as np + + +class MerlivatAndNief1967: + def __init__(self, _): + pass + + @staticmethod + def alpha_l_2H(const, T): + return np.exp( + const.MERLIVAT_NIEF_1967_ALPHA_L_2H_T2 / T**2 + + const.MERLIVAT_NIEF_1967_ALPHA_L_2H_T1 / T + + const.MERLIVAT_NIEF_1967_ALPHA_L_2H_T0 + ) + + @staticmethod + def alpha_i_2H(const, T): + return np.exp( + const.MERLIVAT_NIEF_1967_ALPHA_I_2H_T2 / T**2 + + const.MERLIVAT_NIEF_1967_ALPHA_I_2H_T1 / T + + const.MERLIVAT_NIEF_1967_ALPHA_I_2H_T0 + ) diff --git a/PySDM/source/PySDM/physics/isotope_equilibrium_fractionation_factors/van_hook_1968.py b/PySDM/source/PySDM/physics/isotope_equilibrium_fractionation_factors/van_hook_1968.py new file mode 100644 index 0000000000000000000000000000000000000000..4eb3d091abc11a17403198c2dc4f1efc2a4b6081 --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_equilibrium_fractionation_factors/van_hook_1968.py @@ -0,0 +1,122 @@ +""" +Vapour pressure factors from Table V in [Van Hook 1968](https://doi.org/10.1021/j100850a028) +""" + +import numpy as np + + +class VanHook1968: + def __init__(self, _): + pass + + @staticmethod + def alpha_l_2H(const, T): + return np.exp( + const.VAN_HOOK_1968_ALPHA_L_2H_A / T**2 + + const.VAN_HOOK_1968_ALPHA_L_2H_B / T + + const.VAN_HOOK_1968_ALPHA_L_2H_C + ) + + @staticmethod + def alpha_i_2H(const, T): + return np.exp( + const.VAN_HOOK_1968_ALPHA_I_2H_A / T**2 + + const.VAN_HOOK_1968_ALPHA_I_2H_B / T + + const.VAN_HOOK_1968_ALPHA_I_2H_C + ) + + @staticmethod + def alpha_l_18O(const, T): + return np.exp( + const.VAN_HOOK_1968_ALPHA_L_18O_A / T**2 + + const.VAN_HOOK_1968_ALPHA_L_18O_B / T + + const.VAN_HOOK_1968_ALPHA_L_18O_C + ) + + @staticmethod + def alpha_i_18O(const, T): + return np.exp( + const.VAN_HOOK_1968_ALPHA_I_18O_A / T**2 + + const.VAN_HOOK_1968_ALPHA_I_18O_B / T + + const.VAN_HOOK_1968_ALPHA_I_18O_C + ) + + @staticmethod + def alpha_l_17O(const, T): + return np.exp( + const.VAN_HOOK_1968_ALPHA_L_17O_A / T**2 + + const.VAN_HOOK_1968_ALPHA_L_17O_B / T + + const.VAN_HOOK_1968_ALPHA_L_17O_C + ) + + @staticmethod + def alpha_i_17O(const, T): + return np.exp( + const.VAN_HOOK_1968_ALPHA_I_17O_A / T**2 + + const.VAN_HOOK_1968_ALPHA_I_17O_B / T + + const.VAN_HOOK_1968_ALPHA_I_17O_C + ) + + @staticmethod + def alpha_l_3H(const, T): + return np.exp( + const.VAN_HOOK_1968_ALPHA_L_3H_A / T**2 + + const.VAN_HOOK_1968_ALPHA_L_3H_B / T + + const.VAN_HOOK_1968_ALPHA_L_3H_C + ) + + @staticmethod + def alpha_i_3H(const, T): + return np.exp( + const.VAN_HOOK_1968_ALPHA_I_3H_A / T**2 + + const.VAN_HOOK_1968_ALPHA_I_3H_B / T + + const.VAN_HOOK_1968_ALPHA_I_3H_C + ) + + @staticmethod + def alpha_l_TOT(const, T): + return np.exp( + const.VAN_HOOK_1968_ALPHA_L_TOT_A / T**2 + + const.VAN_HOOK_1968_ALPHA_L_TOT_B / T + + const.VAN_HOOK_1968_ALPHA_L_TOT_C + ) + + @staticmethod + def alpha_i_TOT(const, T): + return np.exp( + const.VAN_HOOK_1968_ALPHA_I_TOT_A / T**2 + + const.VAN_HOOK_1968_ALPHA_I_TOT_B / T + + const.VAN_HOOK_1968_ALPHA_I_TOT_C + ) + + @staticmethod + def alpha_l_DOT(const, T): + return np.exp( + const.VAN_HOOK_1968_ALPHA_L_DOT_A / T**2 + + const.VAN_HOOK_1968_ALPHA_L_DOT_B / T + + const.VAN_HOOK_1968_ALPHA_L_DOT_C + ) + + @staticmethod + def alpha_i_DOT(const, T): + return np.exp( + const.VAN_HOOK_1968_ALPHA_I_DOT_A / T**2 + + const.VAN_HOOK_1968_ALPHA_I_DOT_B / T + + const.VAN_HOOK_1968_ALPHA_I_DOT_C + ) + + @staticmethod + def alpha_l_DOD(const, T): + return np.exp( + const.VAN_HOOK_1968_ALPHA_L_DOD_A / T**2 + + const.VAN_HOOK_1968_ALPHA_L_DOD_B / T + + const.VAN_HOOK_1968_ALPHA_L_DOD_C + ) + + @staticmethod + def alpha_i_DOD(const, T): + return np.exp( + const.VAN_HOOK_1968_ALPHA_I_DOD_A / T**2 + + const.VAN_HOOK_1968_ALPHA_I_DOD_B / T + + const.VAN_HOOK_1968_ALPHA_I_DOD_C + ) diff --git a/PySDM/source/PySDM/physics/isotope_kinetic_fractionation_factors/__init__.py b/PySDM/source/PySDM/physics/isotope_kinetic_fractionation_factors/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..b3f96b4aa78ed1b5aedab10a66835dcda6127d17 --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_kinetic_fractionation_factors/__init__.py @@ -0,0 +1,7 @@ +""" +kinetic fractionation factors for water isotopologues +""" + +from PySDM.impl.null_physics_class import Null +from .craig_gordon import CraigGordon +from .jouzel_and_merlivat_1984 import JouzelAndMerlivat1984 diff --git a/PySDM/source/PySDM/physics/isotope_kinetic_fractionation_factors/craig_gordon.py b/PySDM/source/PySDM/physics/isotope_kinetic_fractionation_factors/craig_gordon.py new file mode 100644 index 0000000000000000000000000000000000000000..00c0e436a054747c28ca9eb8ae86324d48c3500e --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_kinetic_fractionation_factors/craig_gordon.py @@ -0,0 +1,17 @@ +""" +kinetic fractionation factor in the framework of the Craig-Gordon model +as given in eq. 1.5 in +[Rozanski_et_al_2001 (UNESCO, ed. Mook) +](https://web.archive.org/web/20160322221332/https://hydrology.nl/images/docs/ihp/Mook_III.pdf) +and as used in [Pierchala et al. 2022](https://doi.org/10.1016/j.gca.2022.01.020) +""" + + +class CraigGordon: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def alpha_kinetic(*, relative_humidity, turbulence_parameter_n, delta_diff, theta): + """delta_diff = 1 - diffusivity_ratio_heavy_to_light""" + return 1 + theta * turbulence_parameter_n * delta_diff * (1 - relative_humidity) diff --git a/PySDM/source/PySDM/physics/isotope_kinetic_fractionation_factors/jouzel_and_merlivat_1984.py b/PySDM/source/PySDM/physics/isotope_kinetic_fractionation_factors/jouzel_and_merlivat_1984.py new file mode 100644 index 0000000000000000000000000000000000000000..f17186751d666daee5a23474f3b89af6c0827ee9 --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_kinetic_fractionation_factors/jouzel_and_merlivat_1984.py @@ -0,0 +1,32 @@ +""" +kinetic fractionation factor [Jouzel & Merlivat 1984](https://doi.org/10.1029/JD089iD07p11749), +as eq. 3e for n=1 in [Stewart 1975](https://doi.org/10.1029/JC080i009p01133) +and eq. 1 in [Bolot 2013](https://doi.org/10.5194/acp-13-7903-2013), +where alpha_kinetic is multiplied by alpha equilibrium (eq. 1 defines effective alpha). +""" + + +class JouzelAndMerlivat1984: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def alpha_kinetic(alpha_equilibrium, saturation, D_ratio_heavy_to_light): + """eq. (11) + + Parameters + ---------- + alpha_equilibrium + Equilibrium fractionation factor. + saturation + Over liquid water or ice. + D_ratio_heavy_to_light + Diffusivity ratio for heavy to light isotope. + + Returns + ---------- + alpha_kinetic + Kinetic fractionation factor for liquid water or ice.""" + return saturation / ( + alpha_equilibrium / D_ratio_heavy_to_light * (saturation - 1) + 1 + ) diff --git a/PySDM/source/PySDM/physics/isotope_meteoric_water_line/__init__.py b/PySDM/source/PySDM/physics/isotope_meteoric_water_line/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3b2917f0bec7ed2035bb5d12dc280a1b626397a9 --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_meteoric_water_line/__init__.py @@ -0,0 +1,7 @@ +"""definitions of meteoric water line parameters +and definitions of the excess quantities""" + +from PySDM.impl.null_physics_class import Null +from .barkan_and_luz_2007 import BarkanAndLuz2007 +from .dansgaard_1964 import Dansgaard1964 +from .picciotto_et_al_1960 import PicciottoEtAl1960 diff --git a/PySDM/source/PySDM/physics/isotope_meteoric_water_line/barkan_and_luz_2007.py b/PySDM/source/PySDM/physics/isotope_meteoric_water_line/barkan_and_luz_2007.py new file mode 100644 index 0000000000000000000000000000000000000000..aab4063b377ef2384de5429e659f42a38545212b --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_meteoric_water_line/barkan_and_luz_2007.py @@ -0,0 +1,24 @@ +""" +Water isotopic line excess parameters defined in +[Barkan and Luz 2007](https://doi.org/10.1002/rcm.3180) +""" + +import numpy as np + + +class BarkanAndLuz2007: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def excess_17O(const, delta_17O, delta_18O): + return np.log( + delta_17O + 1 + ) - const.BARKAN_AND_LUZ_2007_EXCESS_18O_COEFF * np.log(delta_18O + 1) + + @staticmethod + def d17O_of_d18O(const, delta_18O): + return ( + np.exp(const.BARKAN_AND_LUZ_2007_EXCESS_18O_COEFF * np.log(delta_18O + 1)) + - 1 + ) diff --git a/PySDM/source/PySDM/physics/isotope_meteoric_water_line/dansgaard_1964.py b/PySDM/source/PySDM/physics/isotope_meteoric_water_line/dansgaard_1964.py new file mode 100644 index 0000000000000000000000000000000000000000..5a6f4d609e0ea4425450105cf99a1320c7288a84 --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_meteoric_water_line/dansgaard_1964.py @@ -0,0 +1,21 @@ +""" +Water isotopic line excess parameters defined in +[Dansgaard 1964](https://doi.org/10.3402/tellusa.v16i4.8993) +for Northern hemisphere continental stations, except African and Near East +(weighted means) - see, e.g., abstract and Fig 10 +""" + + +class Dansgaard1964: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def excess_d(const, delta_2H, delta_18O): + return delta_2H - const.CRAIG_1961_SLOPE_COEFF * delta_18O + + @staticmethod + def d18O_of_d2H(const, delta_2H): + return ( + delta_2H - const.CRAIG_1961_INTERCEPT_COEFF + ) / const.CRAIG_1961_SLOPE_COEFF diff --git a/PySDM/source/PySDM/physics/isotope_meteoric_water_line/picciotto_et_al_1960.py b/PySDM/source/PySDM/physics/isotope_meteoric_water_line/picciotto_et_al_1960.py new file mode 100644 index 0000000000000000000000000000000000000000..43f428e9d15218e0c145354dffb40da73d9e5169 --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_meteoric_water_line/picciotto_et_al_1960.py @@ -0,0 +1,15 @@ +"""based on [Picciotto et al. 1960](https://doi.org/10.1038/187857a0) +where delta(2H)= slope_coeff * delta(18O) + intercept_coeff formulae is given +with swapped 2H and 18O in a paper +""" + + +class PicciottoEtAl1960: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def d18O_of_d2H(const, delta_2H): + return ( + delta_2H - const.PICCIOTTO_18O_TO_2H_INTERCEPT_COEFF + ) / const.PICCIOTTO_18O_TO_2H_SLOPE_COEFF diff --git a/PySDM/source/PySDM/physics/isotope_ratio_evolution/__init__.py b/PySDM/source/PySDM/physics/isotope_ratio_evolution/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..44b0ec42f3f9d06b42f6fdb58983c80b9711c4f2 --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_ratio_evolution/__init__.py @@ -0,0 +1,6 @@ +"""isotope-ratio evolution formulae (incl. Rayleigh distillation)""" + +from PySDM.impl.null_physics_class import Null +from .merlivat_and_jouzel_1979 import MerlivatAndJouzel1979 +from .rayleigh_distillation import RayleighDistillation +from .gedzelman_and_arnold_1994 import GedzelmanAndArnold1994 diff --git a/PySDM/source/PySDM/physics/isotope_ratio_evolution/gedzelman_and_arnold_1994.py b/PySDM/source/PySDM/physics/isotope_ratio_evolution/gedzelman_and_arnold_1994.py new file mode 100644 index 0000000000000000000000000000000000000000..30cfa7ee4f5ab0f1ede01ea16ea80ea2ebe353d1 --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_ratio_evolution/gedzelman_and_arnold_1994.py @@ -0,0 +1,18 @@ +""" +eqs. (22) and (23) in [Gedzelman & Arnold 1994](https://doi.org/10.1029/93JD03518) +""" + + +# pylint: disable=too-few-public-methods +class GedzelmanAndArnold1994: + def __init__(self, _): + pass + + # pylint: disable=too-many-arguments + @staticmethod + def zero_dR_condition( + _, diff_rat, iso_ratio_x, iso_ratio_r, iso_ratio_v, b, alpha_w + ): + return (diff_rat * iso_ratio_x - iso_ratio_r / alpha_w) / ( + diff_rat * iso_ratio_x - (1 + b) * iso_ratio_v + b * iso_ratio_r / alpha_w + ) diff --git a/PySDM/source/PySDM/physics/isotope_ratio_evolution/merlivat_and_jouzel_1979.py b/PySDM/source/PySDM/physics/isotope_ratio_evolution/merlivat_and_jouzel_1979.py new file mode 100644 index 0000000000000000000000000000000000000000..2453887497b073b86f97a60c95b5e947df6fb60f --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_ratio_evolution/merlivat_and_jouzel_1979.py @@ -0,0 +1,16 @@ +""" +see derivation of eq. (12) in [Merlivat and Jouzel 1979](https://doi.org/10.1029/JC084iC08p05029) +(for constant alpha leads to eq. (13) in +[Gedzelman & Arnold 1994](https://doi.org/10.1029/93JD03518)) +""" + + +class MerlivatAndJouzel1979: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def d_Rv_over_Rv(_, alpha, d_alpha, n_vapour, d_n_vapour, n_liquid): + return ((alpha - 1) * d_n_vapour - n_liquid * d_alpha) / ( + n_vapour + alpha * n_liquid + ) diff --git a/PySDM/source/PySDM/physics/isotope_ratio_evolution/rayleigh_distillation.py b/PySDM/source/PySDM/physics/isotope_ratio_evolution/rayleigh_distillation.py new file mode 100644 index 0000000000000000000000000000000000000000..c425621f6ce3c377377efb7694052a6f1c239535 --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_ratio_evolution/rayleigh_distillation.py @@ -0,0 +1,12 @@ +"""Fractional distillation assuming continuous removal under equilibrium""" + + +class RayleighDistillation: # pylint:disable=too-few-public-methods + """https://en.wikipedia.org/wiki/Rayleigh_fractionation""" + + def __init__(self, _): + pass + + @staticmethod + def R_over_R0(_, X_over_X0, a): + return X_over_X0 ** (a - 1) diff --git a/PySDM/source/PySDM/physics/isotope_relaxation_timescale/__init__.py b/PySDM/source/PySDM/physics/isotope_relaxation_timescale/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..c85d33d7bff088a813dc8a8a9fd0bef2b784df73 --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_relaxation_timescale/__init__.py @@ -0,0 +1,9 @@ +""" +isotope relaxation timescale formulae +""" + +from PySDM.impl.null_physics_class import Null +from .miyake_et_al_1968 import MiyakeEtAl1968 +from .bolin_1958 import Bolin1958 +from .jouzel_et_al_1975 import JouzelEtAl1975 +from .zaba_et_al import ZabaEtAl diff --git a/PySDM/source/PySDM/physics/isotope_relaxation_timescale/bolin_1958.py b/PySDM/source/PySDM/physics/isotope_relaxation_timescale/bolin_1958.py new file mode 100644 index 0000000000000000000000000000000000000000..082f1cc8ed04468f09913f485772b150a550ecea --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_relaxation_timescale/bolin_1958.py @@ -0,0 +1,14 @@ +"""Bolin number""" + +import numpy as np + + +class Bolin1958: # pylint: disable=too-few-public-methods + """Implementation of timescale used by Bolin 1958""" + + def __init__(self, const): + assert np.isfinite(const.BOLIN_ISOTOPE_TIMESCALE_COEFF_C1) + + @staticmethod + def bolin_number(const): + return const.BOLIN_ISOTOPE_TIMESCALE_COEFF_C1 diff --git a/PySDM/source/PySDM/physics/isotope_relaxation_timescale/jouzel_et_al_1975.py b/PySDM/source/PySDM/physics/isotope_relaxation_timescale/jouzel_et_al_1975.py new file mode 100644 index 0000000000000000000000000000000000000000..39355209cd050e86f236617e6d83d1db871878ff --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_relaxation_timescale/jouzel_et_al_1975.py @@ -0,0 +1,26 @@ +"""eq. 7 in [Jouzel et al. 1975](https://doi.org/10.1029/JC080i036p05015) +with assumptions that the supersaturation is absent (S=1) +and at constant vapour phase (R_liq = alpha * R_vap). +D_iso (diffusivity coefficient for heavy isotopes) is replaced by D in calculations of timescale +as stated to be very similar +ventilation coefficient from [Kinzer & Gunn 1951 (J. Meteor.)](https://doi.org/10.1175/1520-0469(1951)008%3C0071:TETATR%3E2.0.CO;2) +with empirical calculations of F-coefficient +""" # pylint: disable=line-too-long + + +class JouzelEtAl1975: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def tau( + const, rho_s, radius, D_iso, D, S, R_liq, alpha, R_vap, Fk + ): # pylint: disable=too-many-arguments, unused-argument + """relative growth of heavy isotope as a function of mass + + Parameters + ---------- + D_iso + diffusivity of the heavy isotope multiplied by ventilation coefficient + """ + return (radius**2 * const.rho_w * alpha) / (3 * rho_s * D_iso) diff --git a/PySDM/source/PySDM/physics/isotope_relaxation_timescale/miyake_et_al_1968.py b/PySDM/source/PySDM/physics/isotope_relaxation_timescale/miyake_et_al_1968.py new file mode 100644 index 0000000000000000000000000000000000000000..2d0913897d58d5bfa4998ef9dd73e6de8c62ddc6 --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_relaxation_timescale/miyake_et_al_1968.py @@ -0,0 +1,24 @@ +"""eq. 28 in [Miyake et al. 1968](https://doi.org/10.2467/mripapers1950.19.2_243) +theta is as discussed in [Kinzer & Gunn 1951 (J. Meteor.)](https://doi.org/10.1175/1520-0469(1951)008%3C0071:TETATR%3E2.0.CO;2) +""" # pylint: disable=line-too-long + + +class MiyakeEtAl1968: # pylint:disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def tau( + const, rho_s, radius, D_iso, D, S, R_liq, alpha, R_vap, Fk + ): # pylint: disable=too-many-arguments, unused-argument + """ + relative growth of heavy isotope as a function of mass from eq. (28) + + Parameters + ---------- + rho_s + is equal to (e_s * M / R_str / T) in eq. (28) + D + diffusivity * theta, where theta from eq. (25) is ventilation_coefficient + """ + return (radius**2 * alpha * const.rho_w) / (3 * rho_s * D) diff --git a/PySDM/source/PySDM/physics/isotope_relaxation_timescale/zaba_et_al.py b/PySDM/source/PySDM/physics/isotope_relaxation_timescale/zaba_et_al.py new file mode 100644 index 0000000000000000000000000000000000000000..cd5e46e68324412e7408f4604b825864596a4b87 --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_relaxation_timescale/zaba_et_al.py @@ -0,0 +1,21 @@ +"""isotope e-folding timescale based on Fick's first law and Fourier's law""" + + +class ZabaEtAl: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def tau( + const, rho_s, radius, D_iso, D, S, R_liq, alpha, R_vap, Fk + ): # pylint: disable=too-many-arguments, unused-argument + """relative growth of heavy isotope as a function of mass""" + return 1 / ( + 3 + * rho_s + / radius**2 + / const.rho_w + / alpha + * D_iso + * (S * (alpha * R_vap / R_liq - 1) + (S - 1) / (1 + D * Fk)) + ) diff --git a/PySDM/source/PySDM/physics/isotope_temperature_inference/__init__.py b/PySDM/source/PySDM/physics/isotope_temperature_inference/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..5103bd8446e2c69ef4d006fe2ad9fc343191b45a --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_temperature_inference/__init__.py @@ -0,0 +1,8 @@ +""" +formulae for inferring atmospheric temperatures from (ice core) water isotopic composition +""" + +from PySDM.impl.null_physics_class import Null +from PySDM.physics.isotope_temperature_inference.picciotto_et_al_1960 import ( + PicciottoEtAl1960, +) diff --git a/PySDM/source/PySDM/physics/isotope_temperature_inference/picciotto_et_al_1960.py b/PySDM/source/PySDM/physics/isotope_temperature_inference/picciotto_et_al_1960.py new file mode 100644 index 0000000000000000000000000000000000000000..75a387df4f277fea3a159cb4d168a13b8cac4838 --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_temperature_inference/picciotto_et_al_1960.py @@ -0,0 +1,15 @@ +"""based on [Picciotto et al. 1960](https://doi.org/10.1038/187857a0) +where delta(T)=-(a*T + b) formulae given, here cast as T(delta)=(-delta-b)/a""" + + +class PicciottoEtAl1960: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def temperature_from_delta_18O(const, delta_18O): + return const.T0 + (-delta_18O - const.PICCIOTTO_18O_B) / const.PICCIOTTO_18O_A + + @staticmethod + def temperature_from_delta_2H(const, delta_2H): + return const.T0 + (-delta_2H - const.PICCIOTTO_2H_B) / const.PICCIOTTO_2H_A diff --git a/PySDM/source/PySDM/physics/isotope_ventilation_ratio/__init__.py b/PySDM/source/PySDM/physics/isotope_ventilation_ratio/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..5672090ece8a1fb08d5a2e4011ddd3189cdaf71d --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_ventilation_ratio/__init__.py @@ -0,0 +1,7 @@ +""" +Isotope ventilation coefficient formulae +""" + +from PySDM.impl.null_physics_class import Null +from .neglect import Neglect +from .brutsaert_1982 import Brutsaert1982 diff --git a/PySDM/source/PySDM/physics/isotope_ventilation_ratio/brutsaert_1982.py b/PySDM/source/PySDM/physics/isotope_ventilation_ratio/brutsaert_1982.py new file mode 100644 index 0000000000000000000000000000000000000000..1a3235b6cb9b25d63a7e9599889593a1e25786d8 --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_ventilation_ratio/brutsaert_1982.py @@ -0,0 +1,16 @@ +""" +based on [Brutsaert 1982](https://doi.org/10.1007/978-94-017-1497-6) Springer Netherlands +statement about ventilation coefficient for heavy isotopes on pp. 92-93. +""" + + +class Brutsaert1982: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def ratio_heavy_to_light(ventilation_coefficient, diffusivity_ratio_heavy_to_light): + """heavy to light isotope ventilation ratio""" + return ( + 1 - diffusivity_ratio_heavy_to_light ** (1 / 3) + ) / ventilation_coefficient + diffusivity_ratio_heavy_to_light ** (1 / 3) diff --git a/PySDM/source/PySDM/physics/isotope_ventilation_ratio/neglect.py b/PySDM/source/PySDM/physics/isotope_ventilation_ratio/neglect.py new file mode 100644 index 0000000000000000000000000000000000000000..6be0f2d87d78a4faf3c10fdee6307bbf2c7a5814 --- /dev/null +++ b/PySDM/source/PySDM/physics/isotope_ventilation_ratio/neglect.py @@ -0,0 +1,12 @@ +"""assuming f'/f equals 1""" + + +class Neglect: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def ratio_heavy_to_light( + ventilation_coefficient, diffusivity_ratio_heavy_to_light + ): # pylint: disable=unused-argument + return 1 diff --git a/PySDM/source/PySDM/physics/latent_heat_sublimation/__init__.py b/PySDM/source/PySDM/physics/latent_heat_sublimation/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..c601aa4cfe84d6a4c18596d238f6189234ca193f --- /dev/null +++ b/PySDM/source/PySDM/physics/latent_heat_sublimation/__init__.py @@ -0,0 +1,5 @@ +""" +formulations of latent heat of sublimation temperature dependence (or lack thereof) +""" + +from .murphy_koop_2005 import MurphyKoop2005 diff --git a/PySDM/source/PySDM/physics/latent_heat_sublimation/murphy_koop_2005.py b/PySDM/source/PySDM/physics/latent_heat_sublimation/murphy_koop_2005.py new file mode 100644 index 0000000000000000000000000000000000000000..957526be68c270a9c4720ccdaf15299476eca6de --- /dev/null +++ b/PySDM/source/PySDM/physics/latent_heat_sublimation/murphy_koop_2005.py @@ -0,0 +1,19 @@ +""" +temperature-dependent latent heat of sublimation from Murphy and Koop (2005) Eq. (5) +""" + +import numpy as np + + +class MurphyKoop2005: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def ls(const, T): + return ( + const.MK05_SUB_C1 + + const.MK05_SUB_C2 * T + - const.MK05_SUB_C3 * T**2.0 + + const.MK05_SUB_C4 * np.exp(-((T / const.MK05_SUB_C5) ** 2.0)) + ) / const.Mv diff --git a/PySDM/source/PySDM/physics/latent_heat_vapourisation/__init__.py b/PySDM/source/PySDM/physics/latent_heat_vapourisation/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..bc8cc28d600916887b30375bafd5f0d51c0c7d41 --- /dev/null +++ b/PySDM/source/PySDM/physics/latent_heat_vapourisation/__init__.py @@ -0,0 +1,7 @@ +""" +alternative formulations of latent heat temperature dependence (or lack thereof) +""" + +from .constant import Constant +from .kirchhoff import Kirchhoff +from .lowe2019 import Lowe2019 diff --git a/PySDM/source/PySDM/physics/latent_heat_vapourisation/constant.py b/PySDM/source/PySDM/physics/latent_heat_vapourisation/constant.py new file mode 100644 index 0000000000000000000000000000000000000000..63967fd70315d008a6a129bb50f7f288db2acd1d --- /dev/null +++ b/PySDM/source/PySDM/physics/latent_heat_vapourisation/constant.py @@ -0,0 +1,12 @@ +""" +temperature-independent latent heat of vaporization +""" + + +class Constant: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def lv(const, T): # pylint: disable=unused-argument + return const.l_tri diff --git a/PySDM/source/PySDM/physics/latent_heat_vapourisation/kirchhoff.py b/PySDM/source/PySDM/physics/latent_heat_vapourisation/kirchhoff.py new file mode 100644 index 0000000000000000000000000000000000000000..e94ec0bb74f1bda31c59928e3a1d089a4e25591c --- /dev/null +++ b/PySDM/source/PySDM/physics/latent_heat_vapourisation/kirchhoff.py @@ -0,0 +1,14 @@ +""" +[Kirchhoff's + law](https://en.wikipedia.org/wiki/Gustav_Kirchhoff#Kirchhoff's_law_of_thermochemistry) + based temperature-dependent latent heat of vaporization +""" + + +class Kirchhoff: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def lv(const, T): + return const.l_tri + (const.c_pv - const.c_pw) * (T - const.T_tri) diff --git a/PySDM/source/PySDM/physics/latent_heat_vapourisation/lowe2019.py b/PySDM/source/PySDM/physics/latent_heat_vapourisation/lowe2019.py new file mode 100644 index 0000000000000000000000000000000000000000..c8965d4b93b12cb00dfb6b1d906242891e7552bf --- /dev/null +++ b/PySDM/source/PySDM/physics/latent_heat_vapourisation/lowe2019.py @@ -0,0 +1,16 @@ +""" +temperature-dependent latent heat of vaporization used in Lowe et al. 2019 +using equation form from Seinfeld and Pandis, with constant values from ICPM code +""" + +from PySDM.physics import constants_defaults +from PySDM.physics.latent_heat_vapourisation.seinfeld_and_pandis_2010 import ( + SeinfeldPandis, +) + + +class Lowe2019(SeinfeldPandis): # pylint: disable=too-few-public-methods + def __init__(self, const): + assert const.l_l19_a == constants_defaults.l_l19_a + assert const.l_l19_b == constants_defaults.l_l19_b + SeinfeldPandis.__init__(self, const) diff --git a/PySDM/source/PySDM/physics/latent_heat_vapourisation/seinfeld_and_pandis_2010.py b/PySDM/source/PySDM/physics/latent_heat_vapourisation/seinfeld_and_pandis_2010.py new file mode 100644 index 0000000000000000000000000000000000000000..2433d38b0499beacabea7d7d3590bdd012a0ec50 --- /dev/null +++ b/PySDM/source/PySDM/physics/latent_heat_vapourisation/seinfeld_and_pandis_2010.py @@ -0,0 +1,12 @@ +""" +temperature-dependent latent heat of vaporization from Seinfeld and Pandis +""" + + +class SeinfeldPandis: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def lv(const, T): + return const.l_tri * (const.T_tri / T) ** (const.l_l19_a + const.l_l19_b * T) diff --git a/PySDM/source/PySDM/physics/optical_albedo/__init__.py b/PySDM/source/PySDM/physics/optical_albedo/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..95122284091563a63b81c081e93eff37ef392522 --- /dev/null +++ b/PySDM/source/PySDM/physics/optical_albedo/__init__.py @@ -0,0 +1,6 @@ +""" +alternative formulations of cloud albedo +""" + +from PySDM.impl.null_physics_class import Null +from .bohren1987 import Bohren1987 diff --git a/PySDM/source/PySDM/physics/optical_albedo/bohren1987.py b/PySDM/source/PySDM/physics/optical_albedo/bohren1987.py new file mode 100644 index 0000000000000000000000000000000000000000..6f9a5ceba946e4495209654d1c4edd06839f056a --- /dev/null +++ b/PySDM/source/PySDM/physics/optical_albedo/bohren1987.py @@ -0,0 +1,14 @@ +""" +[Bohren 1987](https://doi.org/10.1119/1.15109) Eq. 14 +""" + + +class Bohren1987: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def albedo(const, tau): + return ((const.ONE - const.asymmetry_g) * tau) / ( + const.TWO + (const.ONE - const.asymmetry_g) * tau + ) diff --git a/PySDM/source/PySDM/physics/optical_depth/__init__.py b/PySDM/source/PySDM/physics/optical_depth/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..86ced9e05b153357d776a7e62ed43cd49e3265cc --- /dev/null +++ b/PySDM/source/PySDM/physics/optical_depth/__init__.py @@ -0,0 +1,6 @@ +""" +alternative formulations of optical depth +""" + +from PySDM.impl.null_physics_class import Null +from .stephens_1978 import Stephens1978 diff --git a/PySDM/source/PySDM/physics/optical_depth/stephens_1978.py b/PySDM/source/PySDM/physics/optical_depth/stephens_1978.py new file mode 100644 index 0000000000000000000000000000000000000000..ee5c7d2603a4ea2f3a53256b3b71c4fa1c8ef9b1 --- /dev/null +++ b/PySDM/source/PySDM/physics/optical_depth/stephens_1978.py @@ -0,0 +1,13 @@ +""" +[Stephens 1978](https://doi.org/10.1175/1520-0469(1978)035%3C2123:RPIEWC%3E2.0.CO;2) +Eq. 7 for optical depth, where LWP is in g/m^2 and reff is in um. +""" + + +class Stephens1978: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def tau(const, LWP, reff): + return (const.ONE_AND_A_HALF * LWP) / (const.rho_w * reff) diff --git a/PySDM/source/PySDM/physics/particle_advection/__init__.py b/PySDM/source/PySDM/physics/particle_advection/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..36341ccaa46283d89f8e21645f027aa06ab5520b --- /dev/null +++ b/PySDM/source/PySDM/physics/particle_advection/__init__.py @@ -0,0 +1,6 @@ +""" +Implicit (default) and explicit particle displacement integrators +""" + +from .explicit_in_space import ExplicitInSpace +from .implicit_in_space import ImplicitInSpace diff --git a/PySDM/source/PySDM/physics/particle_advection/explicit_in_space.py b/PySDM/source/PySDM/physics/particle_advection/explicit_in_space.py new file mode 100644 index 0000000000000000000000000000000000000000..120e64cb06a0b95df0aece699bc620139cc59065 --- /dev/null +++ b/PySDM/source/PySDM/physics/particle_advection/explicit_in_space.py @@ -0,0 +1,12 @@ +""" +basic explicit-in-space Euler scheme +""" + + +class ExplicitInSpace: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def displacement(_, position_in_cell, c_l, c_r): + return c_l * (1 - position_in_cell) + c_r * position_in_cell diff --git a/PySDM/source/PySDM/physics/particle_advection/implicit_in_space.py b/PySDM/source/PySDM/physics/particle_advection/implicit_in_space.py new file mode 100644 index 0000000000000000000000000000000000000000..92ccec0fdb0e2a42d402e4d643c966e42383fc0c --- /dev/null +++ b/PySDM/source/PySDM/physics/particle_advection/implicit_in_space.py @@ -0,0 +1,12 @@ +""" +eqs. 14-16 in [Arabas et al. 2015](https://doi.org/10.5194/gmd-8-1677-2015) +""" + + +class ImplicitInSpace: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def displacement(_, position_in_cell, c_l, c_r): + return (c_l * (1 - position_in_cell) + c_r * position_in_cell) / (1 - c_r + c_l) diff --git a/PySDM/source/PySDM/physics/particle_shape_and_density/__init__.py b/PySDM/source/PySDM/physics/particle_shape_and_density/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..c8ad12112a8cb253cb92a4cc75db3432cb53ac61 --- /dev/null +++ b/PySDM/source/PySDM/physics/particle_shape_and_density/__init__.py @@ -0,0 +1,8 @@ +""" +particle shape and density relationships +""" + +from .liquid_spheres import LiquidSpheres +from .mixed_phase_spheres import MixedPhaseSpheres +from .porous_spheroids import PorousSpheroid +from .columnar_ice import ColumnarIce diff --git a/PySDM/source/PySDM/physics/particle_shape_and_density/columnar_ice.py b/PySDM/source/PySDM/physics/particle_shape_and_density/columnar_ice.py new file mode 100644 index 0000000000000000000000000000000000000000..36d3715184216d5930c3c277a177dad5f4c0c71b --- /dev/null +++ b/PySDM/source/PySDM/physics/particle_shape_and_density/columnar_ice.py @@ -0,0 +1,46 @@ +""" +for columnar ice crystals as in +[Spichtinger & Gierens 2009](https://doi.org/10.5194/acp-9-685-2009) +""" + +import numpy as np +from .porous_spheroids import PorousSpheroid + + +class ColumnarIce(PorousSpheroid): + + @staticmethod + def polar_radius_empirical_parametrisation(const, mass): + """Sec. 3.1.2 in [Spichtinger & Gierens 2009](https://doi.org/10.5194/acp-9-685-2009). + Note that the notation in the paper is incorrect. + """ + return np.where( + mass < const.columnar_ice_mass_transition, + (mass / const.columnar_ice_length_alpha_1) + ** (1 / const.columnar_ice_length_beta_1) + / 2, + (mass / const.columnar_ice_length_alpha_2) + ** (1 / const.columnar_ice_length_beta_2) + / 2, + ) + + @staticmethod + def aspect_ratio_empirical_parametrisation(const, mass): + """Eq. 17 in [Spichtinger & Gierens 2009](https://doi.org/10.5194/acp-9-685-2009)""" + return np.where( + mass < const.columnar_ice_mass_transition, + 1, + np.sqrt( + np.sqrt(27) + * const.columnar_bulk_ice_density + / 8.0 + / const.columnar_ice_length_alpha_2 + ** (3 / const.columnar_ice_length_beta_2) + ) + * mass + ** ( + (3 - const.columnar_ice_length_beta_2) + / 2 + / const.columnar_ice_length_beta_2 + ), + ) diff --git a/PySDM/source/PySDM/physics/particle_shape_and_density/liquid_spheres.py b/PySDM/source/PySDM/physics/particle_shape_and_density/liquid_spheres.py new file mode 100644 index 0000000000000000000000000000000000000000..ff46851bbb4699527ff9f7513a6f3c35bd83bcf9 --- /dev/null +++ b/PySDM/source/PySDM/physics/particle_shape_and_density/liquid_spheres.py @@ -0,0 +1,49 @@ +""" +spherical particles with constant density of water +Reynolds number calculation assumes length scale is particle diameter +""" + +import numpy as np + + +class LiquidSpheres: + def __init__(self, _): + pass + + @staticmethod + def supports_mixed_phase(_=None): + return False + + @staticmethod + def mass_to_volume(const, mass): + return mass / const.rho_w + + @staticmethod + def volume_to_mass(const, volume): + return const.rho_w * volume + + @staticmethod + def radius_to_mass(const, radius): + return const.rho_w * const.PI_4_3 * np.power(radius, const.THREE) + + @staticmethod + def reynolds_number(_, radius, velocity_wrt_air, dynamic_viscosity, density): + return 2 * radius * velocity_wrt_air * density / dynamic_viscosity + + @staticmethod + def dm_dt(const, r, r_dr_dt): + """ + dm_dt = d(4/3 pi r^3 rho_w) / dt + = 4 pi r^2 rho_w dr/dt + = 4 pi rho_w r(mass) * r_dr_dt + = 4 pi rho_w cbrt(mass/rho_w/pi/(4/3)) r_dr_dt + """ + return 4 * const.PI * const.rho_w * r * r_dr_dt + + @staticmethod + def dm_dt_over_m(r, r_dr_dt): + return 3 / r**2 * r_dr_dt + + @staticmethod + def r_dr_dt(r, dm_dt_over_m): + return r**2 / 3 * dm_dt_over_m diff --git a/PySDM/source/PySDM/physics/particle_shape_and_density/mixed_phase_spheres.py b/PySDM/source/PySDM/physics/particle_shape_and_density/mixed_phase_spheres.py new file mode 100644 index 0000000000000000000000000000000000000000..6f014e881f1bbeb73ade750e1513e408bb192973 --- /dev/null +++ b/PySDM/source/PySDM/physics/particle_shape_and_density/mixed_phase_spheres.py @@ -0,0 +1,52 @@ +""" +spherical particles with constant density of water or ice +""" + +import numpy as np + + +class MixedPhaseSpheres: + def __init__(self, _): + pass + + @staticmethod + def supports_mixed_phase(_=None): + return True + + @staticmethod + def mass_to_volume(const, mass): + return ( + max(const.ZERO_MASS, mass) / const.rho_w + + min(const.ZERO_MASS, mass) / const.rho_i + ) + + @staticmethod + def volume_to_mass(const, volume): + return ( + np.maximum(const.ZERO_VOLUME, volume) * const.rho_w + + np.minimum(const.ZERO_VOLUME, volume) * const.rho_i + ) + + @staticmethod + def radius_to_mass(const, radius): + return ( + np.maximum(const.ZERO_VOLUME, const.PI_4_3 * radius**3) * const.rho_w + + np.minimum(const.ZERO_VOLUME, const.PI_4_3 * radius**3) * const.rho_i + ) + + @staticmethod + def mass_to_radius(const, mass): + return np.power( + np.maximum(const.ZERO_MASS, mass) / const.PI_4_3 / const.rho_w, + const.ONE_THIRD, + ) + np.power( + -np.minimum(const.ZERO_MASS, mass) / const.PI_4_3 / const.rho_i, + const.ONE_THIRD, + ) + + @staticmethod + def dm_dt(const, r, r_dr_dt): + """ + note: no ice phase support here yet! TODO #1524 + """ + return 4 * const.PI * const.rho_w * r * r_dr_dt diff --git a/PySDM/source/PySDM/physics/particle_shape_and_density/porous_spheroids.py b/PySDM/source/PySDM/physics/particle_shape_and_density/porous_spheroids.py new file mode 100644 index 0000000000000000000000000000000000000000..1dc3a25805506cab7a4c8c2fcea60be573f63e68 --- /dev/null +++ b/PySDM/source/PySDM/physics/particle_shape_and_density/porous_spheroids.py @@ -0,0 +1,33 @@ +""" +for porous spheriods as in +[Shima et al. 2020](https://doi.org/10.5194/gmd-13-4107-2020) +and prolate spheriods as in +[Spichtinger & Gierens 2009](https://doi.org/10.5194/acp-9-685-2009) +""" + +import numpy as np + + +class PorousSpheroid: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def supports_mixed_phase(_=None): + return True + + @staticmethod + def aspect_ratio(polar_radius, equatorial_radius): + return polar_radius / equatorial_radius + + @staticmethod + def equatorial_radius(polar_radius, aspect_ratio): + return polar_radius / aspect_ratio + + @staticmethod + def polar_radius(equatorial_radius, aspect_ratio): + return equatorial_radius * aspect_ratio + + @staticmethod + def eccentricity(aspect_ratio): + return np.sqrt(1 - aspect_ratio**-2.0) diff --git a/PySDM/source/PySDM/physics/saturation_vapour_pressure/__init__.py b/PySDM/source/PySDM/physics/saturation_vapour_pressure/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..9ac3cb742f8ad64c648dfded9f14bdee5f772b8f --- /dev/null +++ b/PySDM/source/PySDM/physics/saturation_vapour_pressure/__init__.py @@ -0,0 +1,10 @@ +""" +Alternative formulations of saturation vapour pressure temperature dependence approximations +""" + +from .august_roche_magnus import AugustRocheMagnus +from .flatau_walko_cotton import FlatauWalkoCotton +from .lowe1977 import Lowe1977 +from .murphy_koop_2005 import MurphyKoop2005 +from .wexler_1976 import Wexler1976 +from .bolton_1980 import Bolton1980 diff --git a/PySDM/source/PySDM/physics/saturation_vapour_pressure/august_roche_magnus.py b/PySDM/source/PySDM/physics/saturation_vapour_pressure/august_roche_magnus.py new file mode 100644 index 0000000000000000000000000000000000000000..7a41c041f1df37d30960badd35543617c975303c --- /dev/null +++ b/PySDM/source/PySDM/physics/saturation_vapour_pressure/august_roche_magnus.py @@ -0,0 +1,23 @@ +""" +August-Roche-Magnus formula (see, e.g., +[Wikipedia](https://en.wikipedia.org/wiki/Clausius–Clapeyron_relation#August–Roche–Magnus_formula) +and references therein) +""" + +import numpy as np + + +class AugustRocheMagnus: + def __init__(self, _): + pass + + @staticmethod + def pvs_water(const, T): + return const.ARM_C1 * np.exp( + (const.ARM_C2 * (T - const.T0)) / ((T - const.T0) + const.ARM_C3) + ) + + @staticmethod + def pvs_ice(const, T): + """NaN with unit of pressure and correct dimension""" + return np.nan * (T - const.T0) / const.ARM_C3 * const.ARM_C1 diff --git a/PySDM/source/PySDM/physics/saturation_vapour_pressure/bolton_1980.py b/PySDM/source/PySDM/physics/saturation_vapour_pressure/bolton_1980.py new file mode 100644 index 0000000000000000000000000000000000000000..b41a07410598307dcd61d3efa9a71d4064d300a6 --- /dev/null +++ b/PySDM/source/PySDM/physics/saturation_vapour_pressure/bolton_1980.py @@ -0,0 +1,22 @@ +""" +[Bolton 1980](https://doi.org/10.1175/1520-0493(1980)108%3C1046:TCOEPT%3E2.0.CO;2) +""" + +import numpy as np + + +class Bolton1980: + def __init__(self, _): + pass + + @staticmethod + def pvs_water(const, T): + """valid for 243.15(-30) <= T <= 308.15(35) K(C), eq. (10)""" + return const.B80W_G0 * np.exp( + (const.B80W_G1 * (T - const.T0)) / ((T - const.T0) + const.B80W_G2) + ) + + @staticmethod + def pvs_ice(const, T): + """NaN with unit of pressure and correct dimension""" + return np.nan * (T - const.T0) / const.B80W_G2 * const.B80W_G0 diff --git a/PySDM/source/PySDM/physics/saturation_vapour_pressure/flatau_walko_cotton.py b/PySDM/source/PySDM/physics/saturation_vapour_pressure/flatau_walko_cotton.py new file mode 100644 index 0000000000000000000000000000000000000000..900ff356e6a6ceeba5d2e489d9017048d5c80601 --- /dev/null +++ b/PySDM/source/PySDM/physics/saturation_vapour_pressure/flatau_walko_cotton.py @@ -0,0 +1,65 @@ +""" +polynomial fits from +[Flatau et al. 1992](https://doi.org/10.1175/1520-0450(1992)031%3C1507:PFTSVP%3E2.0.CO;2) +""" + + +class FlatauWalkoCotton: + def __init__(self, _): + pass + + @staticmethod + def pvs_water(const, T): + return const.FWC_C0 + (T - const.T0) * ( + const.FWC_C1 + + (T - const.T0) + * ( + const.FWC_C2 + + (T - const.T0) + * ( + const.FWC_C3 + + (T - const.T0) + * ( + const.FWC_C4 + + (T - const.T0) + * ( + const.FWC_C5 + + (T - const.T0) + * ( + const.FWC_C6 + + (T - const.T0) + * (const.FWC_C7 + (T - const.T0) * const.FWC_C8) + ) + ) + ) + ) + ) + ) + + @staticmethod + def pvs_ice(const, T): + return const.FWC_I0 + (T - const.T0) * ( + const.FWC_I1 + + (T - const.T0) + * ( + const.FWC_I2 + + (T - const.T0) + * ( + const.FWC_I3 + + (T - const.T0) + * ( + const.FWC_I4 + + (T - const.T0) + * ( + const.FWC_I5 + + (T - const.T0) + * ( + const.FWC_I6 + + (T - const.T0) + * (const.FWC_I7 + (T - const.T0) * const.FWC_I8) + ) + ) + ) + ) + ) + ) diff --git a/PySDM/source/PySDM/physics/saturation_vapour_pressure/lowe1977.py b/PySDM/source/PySDM/physics/saturation_vapour_pressure/lowe1977.py new file mode 100644 index 0000000000000000000000000000000000000000..98351b77590d7d3ba9b95b141d1d6c0ce3ab9bb9 --- /dev/null +++ b/PySDM/source/PySDM/physics/saturation_vapour_pressure/lowe1977.py @@ -0,0 +1,49 @@ +""" +polynomial fits from +[Lowe et al. 1977](https://doi.org/10.1175/1520-0450(1977)016%3C0100:AAPFTC%3E2.0.CO;2) +""" + + +class Lowe1977: + def __init__(self, _): + pass + + @staticmethod + def pvs_water(const, T): + return const.L77W_A0 + (T - const.T0) * ( + const.L77W_A1 + + (T - const.T0) + * ( + const.L77W_A2 + + (T - const.T0) + * ( + const.L77W_A3 + + (T - const.T0) + * ( + const.L77W_A4 + + (T - const.T0) + * (const.L77W_A5 + (T - const.T0) * (const.L77W_A6)) + ) + ) + ) + ) + + @staticmethod + def pvs_ice(const, T): + return const.L77I_A0 + (T - const.T0) * ( + const.L77I_A1 + + (T - const.T0) + * ( + const.L77I_A2 + + (T - const.T0) + * ( + const.L77I_A3 + + (T - const.T0) + * ( + const.L77I_A4 + + (T - const.T0) + * (const.L77I_A5 + (T - const.T0) * (const.L77I_A6)) + ) + ) + ) + ) diff --git a/PySDM/source/PySDM/physics/saturation_vapour_pressure/murphy_koop_2005.py b/PySDM/source/PySDM/physics/saturation_vapour_pressure/murphy_koop_2005.py new file mode 100644 index 0000000000000000000000000000000000000000..2515a2aeccb64948f335da518de724b6c991fb5d --- /dev/null +++ b/PySDM/source/PySDM/physics/saturation_vapour_pressure/murphy_koop_2005.py @@ -0,0 +1,37 @@ +""" +[Murphy and Koop 2005](https://doi.org/10.1256/qj.04.94) +""" + +import numpy as np + + +class MurphyKoop2005: + def __init__(self, _): + pass + + @staticmethod + def pvs_water(const, T): + """valid for 123 < T < 332 K, eq (10)""" + return const.MK05_LIQ_C1 * np.exp( + const.MK05_LIQ_C2 + - const.MK05_LIQ_C3 / (T) + - const.MK05_LIQ_C4 * np.log(T / const.MK05_LIQ_C5) + + const.MK05_LIQ_C6 * (T) + + np.tanh(const.MK05_LIQ_C7 * (T - const.MK05_LIQ_C8)) + * ( + const.MK05_LIQ_C9 + - const.MK05_LIQ_C10 / T + - const.MK05_LIQ_C11 * np.log(T / const.MK05_LIQ_C12) + + const.MK05_LIQ_C13 * T + ) + ) + + @staticmethod + def pvs_ice(const, T): + """valid for T > 110 K, eq (7)""" + return const.MK05_ICE_C1 * np.exp( + const.MK05_ICE_C2 + - const.MK05_ICE_C3 / T + + const.MK05_ICE_C4 * np.log(T / const.MK05_ICE_C5) + - const.MK05_ICE_C6 * T + ) diff --git a/PySDM/source/PySDM/physics/saturation_vapour_pressure/wexler_1976.py b/PySDM/source/PySDM/physics/saturation_vapour_pressure/wexler_1976.py new file mode 100644 index 0000000000000000000000000000000000000000..514dd35a83477856b3aa37699fed02f32029363e --- /dev/null +++ b/PySDM/source/PySDM/physics/saturation_vapour_pressure/wexler_1976.py @@ -0,0 +1,31 @@ +""" +[Wexler 1976](https://doi.org/10.6028/jres.080A.071) +""" + +import numpy as np + + +class Wexler1976: + def __init__(self, _): + pass + + @staticmethod + def pvs_water(const, T): + return ( + np.exp( + const.W76W_G0 / T**2 + + const.W76W_G1 / T + + const.W76W_G2 + + const.W76W_G3 * T + + const.W76W_G4 * T**2 + + const.W76W_G5 * T**3 + + const.W76W_G6 * T**4 + + const.W76W_G7 * np.log(T / const.one_kelvin) + ) + * const.W76W_G8 + ) + + @staticmethod + def pvs_ice(const, T): + """NaN with unit of pressure and correct dimension""" + return np.nan * (T - const.T0) / const.B80W_G2 * const.B80W_G0 diff --git a/PySDM/source/PySDM/physics/state_variable_triplet/__init__.py b/PySDM/source/PySDM/physics/state_variable_triplet/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..4adfcef56d9de3d4f8c52abe42f3547fb2eda53f --- /dev/null +++ b/PySDM/source/PySDM/physics/state_variable_triplet/__init__.py @@ -0,0 +1,5 @@ +""" +Formulae pertaining to the choice of state variables +""" + +from .libcloudphplusplus import LibcloudphPlusPlus diff --git a/PySDM/source/PySDM/physics/state_variable_triplet/libcloudphplusplus.py b/PySDM/source/PySDM/physics/state_variable_triplet/libcloudphplusplus.py new file mode 100644 index 0000000000000000000000000000000000000000..13cc137b2872ea8fcc112829d8d37bdf30f5cb6b --- /dev/null +++ b/PySDM/source/PySDM/physics/state_variable_triplet/libcloudphplusplus.py @@ -0,0 +1,63 @@ +""" +dry-air density / dry-air potential temperature / water vapour mixing ratio triplet +(as in libcloudph++) +""" + +import numpy as np + + +class LibcloudphPlusPlus: + def __init__(self, _): + pass + + # A14 in libcloudph++ 1.0 paper + @staticmethod + def T(const, rhod, thd): + return thd * np.power( + rhod * thd / const.p1000 * const.Rd, + const.Rd_over_c_pd / (1 - const.Rd_over_c_pd), + ) + + # A15 in libcloudph++ 1.0 paper + @staticmethod + def p(const, rhod, T, water_vapour_mixing_ratio): + return ( + rhod + * (1 + water_vapour_mixing_ratio) + * ( + const.Rv / (1 / water_vapour_mixing_ratio + 1) + + const.Rd / (1 + water_vapour_mixing_ratio) + ) + * T + ) + + @staticmethod + def pv(const, p, water_vapour_mixing_ratio): + return p * water_vapour_mixing_ratio / (water_vapour_mixing_ratio + const.eps) + + # pylint: disable=too-many-arguments + @staticmethod + def dthd_dt(const, rhod, thd, T, d_water_vapour_mixing_ratio__dt, lv): + return -lv * d_water_vapour_mixing_ratio__dt / const.c_pd / T * thd * rhod + + @staticmethod + def th_dry(const, th_std, water_vapour_mixing_ratio): + return th_std * np.power( + 1 + water_vapour_mixing_ratio / const.eps, const.Rd / const.c_pd + ) + + @staticmethod + def rho_d(const, p, water_vapour_mixing_ratio, theta_std): + return ( + p + * (1 - 1 / (1 + const.eps / water_vapour_mixing_ratio)) + / (np.power(p / const.p1000, const.Rd_over_c_pd) * const.Rd * theta_std) + ) + + @staticmethod + def rho_of_rhod_and_water_vapour_mixing_ratio(rhod, water_vapour_mixing_ratio): + return rhod * (1 + water_vapour_mixing_ratio) + + @staticmethod + def rhod_of_pd_T(const, pd, T): + return pd / const.Rd / T diff --git a/PySDM/source/PySDM/physics/surface_tension/__init__.py b/PySDM/source/PySDM/physics/surface_tension/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..1d78cfe6919e1c4f1c57d1b033b94d8b5e13d776 --- /dev/null +++ b/PySDM/source/PySDM/physics/surface_tension/__init__.py @@ -0,0 +1,8 @@ +""" +Particle surface tension formulae +""" + +from .compressed_film_ovadnevaite import CompressedFilmOvadnevaite +from .compressed_film_ruehl import CompressedFilmRuehl +from .constant import Constant +from .szyszkowski_langmuir import SzyszkowskiLangmuir diff --git a/PySDM/source/PySDM/physics/surface_tension/compressed_film_ovadnevaite.py b/PySDM/source/PySDM/physics/surface_tension/compressed_film_ovadnevaite.py new file mode 100644 index 0000000000000000000000000000000000000000..f4e2c9f886cd20c784b651b343bf81aceac62119 --- /dev/null +++ b/PySDM/source/PySDM/physics/surface_tension/compressed_film_ovadnevaite.py @@ -0,0 +1,40 @@ +""" +surface tension coefficient model featuring surface-partitioning + as in [Ovadnevaite et al. (2017)](https://doi.org/10.1038/nature22806) +""" + +import numpy as np + + +class CompressedFilmOvadnevaite: # pylint: disable=too-few-public-methods + """ + Compressed film model of surface-partitioning of organics from Ovadnevaite et al. (2017) + and as described in Lowe et al. (2019). + + Assumes a surface tension of the organic material `sgm_org` and minimum film thickness, + the thickness of a monolayer of the organic molecules, `delta_min`. Assumes all organic + partitions to the surface of the droplet and that the surface tension is a weighted + average of the surface tension of water and the organic material. + """ + + def __init__(self, const): + assert np.isfinite(const.sgm_org) + assert np.isfinite(const.delta_min) + + @staticmethod + def sigma(const, T, v_wet, v_dry, f_org): # pylint: disable=unused-argument + # convert wet volume to wet radius + r_wet = ((3 * v_wet) / (4 * np.pi)) ** (1 / 3) + + # calculate the minimum shell volume, v_delta + v_delta = v_wet - ((4 * np.pi) / 3 * (r_wet - const.delta_min) ** 3) + + # calculate the total volume of organic, v_beta + v_beta = f_org * v_dry + + # calculate the coverage parameter + c_beta = np.minimum(v_beta / v_delta, 1) + + # calculate sigma + sgm = (1 - c_beta) * const.sgm_w + c_beta * const.sgm_org + return sgm diff --git a/PySDM/source/PySDM/physics/surface_tension/compressed_film_ruehl.py b/PySDM/source/PySDM/physics/surface_tension/compressed_film_ruehl.py new file mode 100644 index 0000000000000000000000000000000000000000..2afbf2a56899560a406bf27a07ac84e226c27635 --- /dev/null +++ b/PySDM/source/PySDM/physics/surface_tension/compressed_film_ruehl.py @@ -0,0 +1,101 @@ +""" +surface tension coefficient model featuring surface-partitioning + as in [Ruehl et al. (2016)](https://doi.org/10.1126/science.aad4889) +""" + +import numba +import numpy as np + +from PySDM.backends.impl_numba.conf import JIT_FLAGS as jit_flags +from PySDM.backends.impl_numba.toms748 import toms748_solve +from PySDM.physics.trivia import Trivia + + +# pylint: disable=too-many-arguments +@numba.njit(**{**jit_flags, "parallel": False}) +def minfun(f_surf, Cb_iso, RUEHL_C0, RUEHL_A0, A_iso, c): + lhs = Cb_iso * (1 - f_surf) / RUEHL_C0 + rhs = np.exp(c * (RUEHL_A0**2 - (A_iso / f_surf) ** 2)) + return lhs - rhs + + +within_tolerance = numba.njit( + Trivia.within_tolerance, **{**jit_flags, "parallel": False} +) + + +class CompressedFilmRuehl: # pylint: disable=too-few-public-methods + """ + Compressed film model of surface-partitioning of organics from Ruehl et al. (2016). + Described in supplementary materials equations (13) and (15). + + Allows for more realistic thermodynamic partitioning of some organic to the surface, + while some remains dissolved in the bulk phase. The surface concentration is solved + implicitly from the isotherm equation that relates the bulk organic concentration + `C_bulk` to the surface average molecular area `A`. The equation of state relates + the surface concentration to the surface tension. For the compressed film model it + is linear, with slope `m_sigma`. + """ + + def __init__(self, const): + assert np.isfinite(const.RUEHL_nu_org) + assert np.isfinite(const.RUEHL_A0) + assert np.isfinite(const.RUEHL_C0) + assert np.isfinite(const.RUEHL_m_sigma) + assert np.isfinite(const.RUEHL_sgm_min) + + @staticmethod + def sigma(const, T, v_wet, v_dry, f_org): # pylint: disable=too-many-locals + # wet radius (m) + r_wet = ((3 * v_wet) / (4 * const.PI)) ** (1 / 3) + + if f_org == 0: + sgm = const.sgm_w + elif f_org == 1: + sgm = const.RUEHL_sgm_min + else: + # C_bulk is the concentration of the organic in the bulk phase + # Cb_iso = C_bulk / (1-f_surf) + Cb_iso = (f_org * v_dry / const.RUEHL_nu_org) / ( + v_wet / const.water_molar_volume + ) + + # A is the area one molecule of organic occupies at the droplet surface + # A_iso = A*f_surf (m^2) + A_iso = (4 * const.PI * r_wet**2) / ( + f_org * v_dry * const.N_A / const.RUEHL_nu_org + ) + + # solve implicitly for fraction of organic at surface + c = (const.RUEHL_m_sigma * const.N_A) / (2 * const.R_str * T) + + args = (Cb_iso, const.RUEHL_C0, const.RUEHL_A0, A_iso, c) + rtol = 1e-6 + max_iters = 1e2 + bracket = (1e-16, 1) + f_surf, iters = toms748_solve( + minfun, + args, + *bracket, + minfun(bracket[0], *args), + minfun(bracket[1], *args), + rtol, + max_iters, + within_tolerance, + ) + assert iters != max_iters + + # calculate surface tension + sgm = const.sgm_w - (const.RUEHL_A0 - A_iso / f_surf) * const.RUEHL_m_sigma + + # surface tension bounded between sgm_min and sgm_w + sgm = np.minimum(np.maximum(sgm, const.RUEHL_sgm_min), const.sgm_w) + return sgm + + +CompressedFilmRuehl.sigma.__extras = { + "toms748_solve": toms748_solve, + "minfun": minfun, + "within_tolerance": within_tolerance, +} +CompressedFilmRuehl.sigma.__vectorize = True diff --git a/PySDM/source/PySDM/physics/surface_tension/constant.py b/PySDM/source/PySDM/physics/surface_tension/constant.py new file mode 100644 index 0000000000000000000000000000000000000000..84531a7f6687aca5cc867e89a26747505b90925e --- /dev/null +++ b/PySDM/source/PySDM/physics/surface_tension/constant.py @@ -0,0 +1,18 @@ +""" +constant surface tension coefficient +""" + + +class Constant: # pylint: disable=too-few-public-methods + """ + Assumes aerosol is dissolved in the bulk of the droplet, and the + droplet surface is composed of pure water with constant surface + tension `sgm_w`. + """ + + def __init__(self, _): + pass + + @staticmethod + def sigma(const, T, v_wet, v_dry, f_org): # pylint: disable=unused-argument + return const.sgm_w diff --git a/PySDM/source/PySDM/physics/surface_tension/szyszkowski_langmuir.py b/PySDM/source/PySDM/physics/surface_tension/szyszkowski_langmuir.py new file mode 100644 index 0000000000000000000000000000000000000000..cc228ad800ea2834872da5c6b2e4b5544dc0830c --- /dev/null +++ b/PySDM/source/PySDM/physics/surface_tension/szyszkowski_langmuir.py @@ -0,0 +1,68 @@ +""" +surface tension coefficient model featuring surface-partitioning + as in [Ruehl et al. (2016)](https://doi.org/10.1126/science.aad4889) +""" + +import numpy as np + + +class SzyszkowskiLangmuir: # pylint: disable=too-few-public-methods + """ + Szyszkowski-Langmuir surface-partitioning of organics described in Ruehl et al. (2016). + Described in supplementary materials equations (12) and (14). + + Allows for more realistic thermodynamic partitioning of some organic to the surface, + while some remains dissolved in the bulk phase. The surface concentration is solved + implicitly from the isotherm equation that relates the bulk organic concentration + `C_bulk` to the surface average molecular area `A`. The equation of state relates + the surface concentration to the surface tension. + """ + + def __init__(self, const): + assert np.isfinite(const.RUEHL_nu_org) + assert np.isfinite(const.RUEHL_A0) + assert np.isfinite(const.RUEHL_C0) + assert np.isfinite(const.RUEHL_sgm_min) + + @staticmethod + def sigma(const, T, v_wet, v_dry, f_org): + # wet radius (m) + r_wet = ((3 * v_wet) / (4 * np.pi)) ** (1 / 3) + + if f_org == 0: + sgm = const.sgm_w + else: + # C_bulk is the concentration of the organic in the bulk phase + # Cb_iso = C_bulk / (1-f_surf) + Cb_iso = (f_org * v_dry / const.RUEHL_nu_org) / ( + v_wet / const.water_molar_volume + ) + + # A is the area that one molecule of organic occupies at the droplet surface + # A_iso = A*f_surf (m^2) + A_iso = (4 * np.pi * r_wet**2) / ( + f_org * v_dry * const.N_A / const.RUEHL_nu_org + ) + + # fraction of organic at surface + # quadratic formula, solve equation of state analytically + a = -const.RUEHL_A0 / A_iso + b = ( + const.RUEHL_A0 / A_iso + + (const.RUEHL_A0 / A_iso) * (const.RUEHL_C0 / Cb_iso) + + 1 + ) + c = -1 + f_surf = (-b + np.sqrt(b**2 - 4 * a * c)) / (2 * a) + + # calculate surface tension + sgm = const.sgm_w - ( + (const.R_str * T) / (const.RUEHL_A0 * const.N_A) + ) * np.log(1 + Cb_iso * (1 - f_surf) / const.RUEHL_C0) + + # surface tension bounded between sgm_min and sgm_w + sgm = min(max(sgm, const.RUEHL_sgm_min), const.sgm_w) + return sgm + + +SzyszkowskiLangmuir.sigma.__vectorize = True diff --git a/PySDM/source/PySDM/physics/terminal_velocity/__init__.py b/PySDM/source/PySDM/physics/terminal_velocity/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..74f9ec8c75766b9d4cc489c11caaf8d59da92287 --- /dev/null +++ b/PySDM/source/PySDM/physics/terminal_velocity/__init__.py @@ -0,0 +1,5 @@ +"""terminal velocity formulae for liquid droplets""" + +from .rogers_yau import RogersYau +from .gunn_kinzer_1949 import GunnKinzer1949 +from .power_series import PowerSeries diff --git a/PySDM/source/PySDM/physics/terminal_velocity/gunn_kinzer_1949.py b/PySDM/source/PySDM/physics/terminal_velocity/gunn_kinzer_1949.py new file mode 100644 index 0000000000000000000000000000000000000000..7ff81dacf5f463c616bf19ed0f7a5ee4cd40417d --- /dev/null +++ b/PySDM/source/PySDM/physics/terminal_velocity/gunn_kinzer_1949.py @@ -0,0 +1,6 @@ +"""[Gunn & Kinzer 1949](https://doi.org/10.1175/1520-0469(1949)006%3C0243:TTVOFF%3E2.0.CO;2)""" + + +class GunnKinzer1949: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass diff --git a/PySDM/source/PySDM/physics/terminal_velocity/power_series.py b/PySDM/source/PySDM/physics/terminal_velocity/power_series.py new file mode 100644 index 0000000000000000000000000000000000000000..c9cd110795a221be69a7d64aafb8c2f99937c2fa --- /dev/null +++ b/PySDM/source/PySDM/physics/terminal_velocity/power_series.py @@ -0,0 +1,6 @@ +"""power-series formulation""" + + +class PowerSeries: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass diff --git a/PySDM/source/PySDM/physics/terminal_velocity/rogers_yau.py b/PySDM/source/PySDM/physics/terminal_velocity/rogers_yau.py new file mode 100644 index 0000000000000000000000000000000000000000..df4442b5f2e9702a94c586d16faee31aa5e9b9b7 --- /dev/null +++ b/PySDM/source/PySDM/physics/terminal_velocity/rogers_yau.py @@ -0,0 +1,23 @@ +""" +equations 8.5, 8.6, 8.8 in +[Rogers & Yau 1989](https://archive.org/details/shortcourseinclo0000roge_m3k2) +""" + +import numpy as np + + +class RogersYau: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def v_term(const, radius): + return np.where( + radius < const.ROGERS_YAU_TERM_VEL_SMALL_R_LIMIT, + const.ROGERS_YAU_TERM_VEL_SMALL_K * radius**const.TWO, + np.where( + radius < const.ROGERS_YAU_TERM_VEL_MEDIUM_R_LIMIT, + const.ROGERS_YAU_TERM_VEL_MEDIUM_K * radius, + const.ROGERS_YAU_TERM_VEL_LARGE_K * radius**const.ONE_HALF, + ), + ) diff --git a/PySDM/source/PySDM/physics/terminal_velocity_ice/__init__.py b/PySDM/source/PySDM/physics/terminal_velocity_ice/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..4bf6eb0adcfc76071095d1789735bb3f3442976a --- /dev/null +++ b/PySDM/source/PySDM/physics/terminal_velocity_ice/__init__.py @@ -0,0 +1,4 @@ +"""terminal velocity formulae for ice particles""" + +from .columnar_ice_crystal import ColumnarIceCrystal +from .spheres_ice import IceSphere diff --git a/PySDM/source/PySDM/physics/terminal_velocity_ice/columnar_ice_crystal.py b/PySDM/source/PySDM/physics/terminal_velocity_ice/columnar_ice_crystal.py new file mode 100644 index 0000000000000000000000000000000000000000..67aefb2d982d936aa0c1c4a244e22bf5f7487c52 --- /dev/null +++ b/PySDM/source/PySDM/physics/terminal_velocity_ice/columnar_ice_crystal.py @@ -0,0 +1,41 @@ +""" +[Spichtinger & Gierens 2009](https://doi.org/10.5194/acp-9-685-2009) +Eq. (18) and (19) Assumed shape is columnar based on empirical parameterizations of +[Heymsfield & Iaquinta (2000)](https://doi.org/10.1175/1520-0469(2000)057%3C0916:CCTV%3E2.0.CO;2) +[Barthazy & Schefold (2006)](https://doi.org/10.1016/j.atmosres.2005.12.009) +""" + +import numpy as np + + +class ColumnarIceCrystal: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def v_base_term(const, mass): + return np.where( + mass < const.SPICHTINGER_GIERENS_TERM_VEL_LIMIT_0, + const.SPICHTINGER_TERM_GAMMA_COEFF0 + * mass**const.SPICHTINGER_TERM_DELTA_COEFF0, + np.where( + mass < const.SPICHTINGER_GIERENS_TERM_VEL_LIMIT_1, + const.SPICHTINGER_TERM_GAMMA_COEFF1 + * mass**const.SPICHTINGER_TERM_DELTA_COEFF1, + np.where( + mass < const.SPICHTINGER_GIERENS_TERM_VEL_LIMIT_2, + const.SPICHTINGER_TERM_GAMMA_COEFF2 + * mass**const.SPICHTINGER_TERM_DELTA_COEFF2, + const.SPICHTINGER_TERM_GAMMA_COEFF3 + * mass**const.SPICHTINGER_TERM_DELTA_COEFF3, + ), + ), + ) + + @staticmethod + def atmospheric_correction_factor(const, temperature, pressure): + return ( + pressure / const.SPICHTINGER_CORRECTION_P0 + ) ** const.SPICHTINGER_CORRECTION_P_EXPO * ( + const.SPICHTINGER_CORRECTION_T0 / temperature + ) ** const.SPICHTINGER_CORRECTION_T_EXPO diff --git a/PySDM/source/PySDM/physics/terminal_velocity_ice/spheres_ice.py b/PySDM/source/PySDM/physics/terminal_velocity_ice/spheres_ice.py new file mode 100644 index 0000000000000000000000000000000000000000..f87d1667ace3cc0908ec1e974039b53b8f5b1790 --- /dev/null +++ b/PySDM/source/PySDM/physics/terminal_velocity_ice/spheres_ice.py @@ -0,0 +1,21 @@ +""" +terminal velocity of smooth ice spheres +""" + + +class IceSphere: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def v_base_term(const, radius, prefactor): + return prefactor * const.TWO * radius + + @staticmethod + def stokes_regime(const, radius, dynamic_viscosity): + return const.g_std * const.rho_i * radius / const.NINE / dynamic_viscosity + + # TODO #1602 general functional relationship between reynolds number and drag coefficient + @staticmethod + def general_flow_regime(const): + pass diff --git a/PySDM/source/PySDM/physics/trivia.py b/PySDM/source/PySDM/physics/trivia.py new file mode 100644 index 0000000000000000000000000000000000000000..ff9fcd39a49ec12ceab4dec96ca83227970f6cc0 --- /dev/null +++ b/PySDM/source/PySDM/physics/trivia.py @@ -0,0 +1,262 @@ +""" +Various (hopefully) undebatable formulae + +`erfinv` approximation based on eqs. 11-12 from Vedder 1987, https://doi.org/10.1119/1.15018 +""" + +import numpy as np + + +class Trivia: # pylint: disable=too-many-public-methods + def __init__(self, _): + pass + + @staticmethod + def volume_of_density_mass(rho, m): + return m / rho + + @staticmethod + def radius(const, volume): + return np.power(volume / const.PI_4_3, const.ONE_THIRD) + + @staticmethod + def area(const, radius): + return const.PI * const.FOUR * np.power(radius, const.TWO) + + @staticmethod + def volume(const, radius): + return const.PI_4_3 * np.power(radius, const.THREE) + + @staticmethod + def sphere_surface(const, diameter): + return const.PI * diameter**2 + + @staticmethod + def explicit_euler(y, dt, dy_dt): + return y + dt * dy_dt + + @staticmethod + def within_tolerance(error_estimate, value, rtol): + return error_estimate < rtol * np.abs(value) + + @staticmethod + def H2pH(H): + return -np.log10(H * 1e-3) + + @staticmethod + def pH2H(pH): + return np.power(10, -pH) * 1e3 + + @staticmethod + def vant_hoff(const, K, dH, T, *, T_0): + return K * np.exp(-dH / const.R_str * (1 / T - 1 / T_0)) + + @staticmethod + def tdep2enthalpy(const, tdep): + return -tdep * const.R_str + + @staticmethod + def arrhenius(const, A, Ea, T): + return A * np.exp(-Ea / (const.R_str * T)) + + @staticmethod + def mole_fraction_2_mixing_ratio(mole_fraction, specific_gravity): + return specific_gravity * mole_fraction / (1 - mole_fraction) + + @staticmethod + def mixing_ratio_2_mole_fraction(mixing_ratio, specific_gravity): + return mixing_ratio / (specific_gravity + mixing_ratio) + + @staticmethod + def p_d(const, p, water_vapour_mixing_ratio): + return p * (1 - 1 / (1 + const.eps / water_vapour_mixing_ratio)) + + @staticmethod + def th_std(const, p, T): + return T * np.power(const.p1000 / p, const.Rd_over_c_pd) + + @staticmethod + def unfrozen(signed_water_mass): + return signed_water_mass > 0 + + @staticmethod + def unfrozen_and_saturated(signed_water_mass, relative_humidity): + return signed_water_mass > 0 and relative_humidity > 1 + + @staticmethod + def unfrozen_and_ice_saturated(signed_water_mass, relative_humidity_ice): + return signed_water_mass > 0 and relative_humidity_ice > 1 + + @staticmethod + def frozen_and_above_freezing_point(const, signed_water_mass, temperature): + return signed_water_mass < 0 and temperature > const.T0 + + @staticmethod + def erfinv_approx(const, c): + return ( + 2 + * np.sqrt(const.VEDDER_1987_A) + * np.sinh( + np.arcsinh( + np.arctanh(c) + / 2 + / const.VEDDER_1987_b + / np.power(const.VEDDER_1987_A, const.ONE_AND_A_HALF) + ) + / 3 + ) + ) + + @staticmethod + def isotopic_delta_2_ratio(delta, reference_ratio): + return (delta + 1) * reference_ratio + + @staticmethod + def isotopic_ratio_2_delta(ratio, reference_ratio): + return ratio / reference_ratio - 1 + + @staticmethod + def isotopic_enrichment_to_delta_SMOW(E, delta_0_SMOW): + """(see also eq. 10 in Pierchala et al. 2022) + + conversion from E to delta_R_SMOW with: + δ_R/SMOW = R / R_SMOW - 1 + E = δ_R/R0 = R / R0 - 1 + and the sought formula (the quantity used to define d-excess, etc.) is: + δ_R/SMOW(E) = (E + 1) * R_0 / R_SMOW - 1 + = (E + 1) * (δ_R0/SMOW + 1) - 1 + where: + δ_R0/SMOW is the initial SMOW-delta in the experiment + """ + return (E + 1) * (delta_0_SMOW + 1) - 1 + + @staticmethod + def mixing_ratio_to_specific_content(mixing_ratio): + return mixing_ratio / (1 + mixing_ratio) + + @staticmethod + def isotopic_fraction_assuming_single_heavy_isotope(*, isotopic_ratio): + """ + isotopic ratio = n1/n2 + isotopic fraction = n1/n_total = n1 / (n1 + n2) = isotopic_ratio / (1 + isotopic_ratio) + """ + return isotopic_ratio / (1 + isotopic_ratio) + + @staticmethod + def isotopic_ratio_assuming_single_heavy_isotope(isotopic_fraction): + return isotopic_fraction / (1 - isotopic_fraction) + + @staticmethod + def dn_dlogr(r, dn_dr): + return np.log(10) * r * dn_dr + + @staticmethod + def air_schmidt_number(dynamic_viscosity, diffusivity, density): + return dynamic_viscosity / diffusivity / density + + @staticmethod + def sqrt_re_times_cbrt_sc(const, Re, Sc): + return np.power(Re, const.ONE_HALF) * np.power(Sc, const.ONE_THIRD) + + @staticmethod + def K2C(const, TK): + return TK - const.T0 + + @staticmethod + def C2K(const, TC): + return TC + const.T0 + + @staticmethod + def poissonian_avoidance_function(r, dt): + """cumulative probability of zero events occurring within time `dt` + (or void probability, or avoidance function) in a Poisson counting + process with a constant rate `r` + """ + return np.exp(-r * dt) + + @staticmethod + def tau(Bo, dm_dt_over_m): + """ + see text above Table 1 [Bolin 1958](https://digitallibrary.un.org/record/3892725) + """ + return 1 / Bo / dm_dt_over_m + + @staticmethod + def moles_heavy_atom( + *, + atoms_per_heavy_molecule, + mass_total, + mass_other_heavy_isotopes, + molar_mass_light_molecule, + molar_mass_heavy_molecule, + molecular_isotopic_ratio, + ): + """ + Calculate moles of heavy atoms (e.g. deuterium, oxygen-17, oxygen-18) + from molecular isotope ratios (e.g. moles of HDO to moles of H2O), + using total mass and mass of other heavy isotopes. + """ + return ( + (mass_total - mass_other_heavy_isotopes) + / ( + molar_mass_light_molecule / molecular_isotopic_ratio + + molar_mass_heavy_molecule + ) + / atoms_per_heavy_molecule + ) + + @staticmethod + def molecular_isotopic_ratio( + *, + moles_heavy_molecule, + mass_total, + mass_other_heavy_isotopes, + molar_mass_light_molecule, + molar_mass_heavy_molecule, + ): + """ + Molecular isotope ratio (e.g. moles of HDO to moles of H2O): + n_{heavy molecule}/n_{light molecule}, + in contrast to (atomic) isotopic ratio as in VSMOW standard: + n_{heavy atom}/n_{light atom}; + + calculated with: + m_light = m_total - m_considered_heavy_isotope - m_other_heavy_isotopes + """ + return ( + moles_heavy_molecule + * molar_mass_light_molecule + / ( + mass_total + - moles_heavy_molecule * molar_mass_heavy_molecule + - mass_other_heavy_isotopes + ) + ) + + @staticmethod + def molality_in_dry_air( + isotopic_fraction, density_dry_air, total_vap_concentration + ): + """ + n'/m_d [number of moles of isotopic molecules]/[mass of dry air] - molality in dry air + n/V - total vapour concentration + + molality_in_dry_air = n'/m_d = + = n'/(rho_d * V) = isotopic_concentration / rho_d + isotopic_concentration = n'/V = n/V * n'/n = + = total vapour concentration * isotopic_fraction + + where n'/n is an isotopic fraction (referred to as isotopic concentration (C) + on p. 10 in [Gat 2010](https://doi.org/10.1142/p027) + """ + return total_vap_concentration * isotopic_fraction / density_dry_air + + @staticmethod + def isotopic_fraction( + molality_in_dry_air, density_dry_air, total_vap_concentration + ): + """ + isotopic_fraction: n'/n = n'/m_d / (n/V) * rho_d + = molality in dry air / total vapour concentration * dry air density + """ + return molality_in_dry_air / total_vap_concentration * density_dry_air diff --git a/PySDM/source/PySDM/physics/ventilation/__init__.py b/PySDM/source/PySDM/physics/ventilation/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..6390942d0fd1616510d326141ed4877c139c4a1d --- /dev/null +++ b/PySDM/source/PySDM/physics/ventilation/__init__.py @@ -0,0 +1,7 @@ +""" +Ventilation coefficient formulae +""" + +from .neglect import Neglect +from .pruppacher_rasmussen_1979 import PruppacherAndRasmussen1979 +from .froessling_1938 import Froessling1938 diff --git a/PySDM/source/PySDM/physics/ventilation/froessling_1938.py b/PySDM/source/PySDM/physics/ventilation/froessling_1938.py new file mode 100644 index 0000000000000000000000000000000000000000..4225a44e2991d57a98877ff08e60b44fbc2e6025 --- /dev/null +++ b/PySDM/source/PySDM/physics/ventilation/froessling_1938.py @@ -0,0 +1,13 @@ +""" +based on Froessling ,N. (1938) Beitr. Geophys. 52 pp. 170-216 +as referenced in [Squires 1952](https://doi.org/10.1071/CH9520059) +""" + + +class Froessling1938: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def ventilation_coefficient(const, sqrt_re_times_cbrt_sc): + return const.FROESSLING_1938_A + const.FROESSLING_1938_B * sqrt_re_times_cbrt_sc diff --git a/PySDM/source/PySDM/physics/ventilation/neglect.py b/PySDM/source/PySDM/physics/ventilation/neglect.py new file mode 100644 index 0000000000000000000000000000000000000000..4a812491191f5bbb2afa6306dffb78b7cb6a6bc7 --- /dev/null +++ b/PySDM/source/PySDM/physics/ventilation/neglect.py @@ -0,0 +1,14 @@ +""" +constant ventilation coefficient of unity (i.e., neglect ventilation effects) +""" + +import numpy as np + + +class Neglect: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def ventilation_coefficient(sqrt_re_times_cbrt_sc): + return np.power(sqrt_re_times_cbrt_sc, 0) diff --git a/PySDM/source/PySDM/physics/ventilation/pruppacher_rasmussen_1979.py b/PySDM/source/PySDM/physics/ventilation/pruppacher_rasmussen_1979.py new file mode 100644 index 0000000000000000000000000000000000000000..b669052450e8a9077167ee38d73cf3476e24a6db --- /dev/null +++ b/PySDM/source/PySDM/physics/ventilation/pruppacher_rasmussen_1979.py @@ -0,0 +1,37 @@ +""" +ventilation coefficient as a function of dimensionless Reynolds (Re) and Schmidt (Sc) +numbers for liquid drops following +[Pruppacher & Rasmussen 1979](https://doi.org/10.1175/1520-0469%281979%29036%3C1255:AWTIOT%3E2.0.CO;2) +NB: this parameterization is only experimentally validated for Re < 2600 +but is hypothesized to be valid for spheres with Re < 8 × 10⁴ +based on theory (Pruppacher & Rasmussen, 1979). +the parameterization also does not account for effects of air turbulence. + +For smaller droplets, such as 1.96 <= Re <= 158.76 previously published in +[Beard and Pruppacher](https://doi.org/10.1175/1520-0469%281971%29028%3C1455:AWTIOT%3E2.0.CO;2) + +""" # pylint: disable=line-too-long + +import numpy as np + + +class PruppacherAndRasmussen1979: # pylint: disable=too-few-public-methods + def __init__(self, _): + pass + + @staticmethod + def ventilation_coefficient(const, sqrt_re_times_cbrt_sc): + return np.where( + sqrt_re_times_cbrt_sc < const.PRUPPACHER_RASMUSSEN_1979_XTHRES, + ( + const.PRUPPACHER_RASMUSSEN_1979_CONSTSMALL + + const.PRUPPACHER_RASMUSSEN_1979_COEFFSMALL + * np.power( + sqrt_re_times_cbrt_sc, const.PRUPPACHER_RASMUSSEN_1979_POWSMALL + ) + ), + ( + const.PRUPPACHER_RASMUSSEN_1979_CONSTBIG + + const.PRUPPACHER_RASMUSSEN_1979_COEFFBIG * sqrt_re_times_cbrt_sc + ), + ) diff --git a/PySDM/source/PySDM/products/__init__.py b/PySDM/source/PySDM/products/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..696bd8247b7163637940099f9b8a54870f6b6cfd --- /dev/null +++ b/PySDM/source/PySDM/products/__init__.py @@ -0,0 +1,14 @@ +""" +Simulation output products +""" + +from .ambient_thermodynamics import * +from .aqueous_chemistry import * +from .collision import * +from .condensation import * +from .displacement import * +from .freezing import * +from .housekeeping import * +from .size_spectral import * +from .optical import * +from .parcel import * diff --git a/PySDM/source/PySDM/products/ambient_thermodynamics/__init__.py b/PySDM/source/PySDM/products/ambient_thermodynamics/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..9cdca7d937d7394fb847b21a13a28ae3f5ebd0d7 --- /dev/null +++ b/PySDM/source/PySDM/products/ambient_thermodynamics/__init__.py @@ -0,0 +1,10 @@ +""" +products offering access to environment variables (ambient temperature, pressure, ...) +""" + +from .ambient_dry_air_density import AmbientDryAirDensity +from .ambient_dry_air_potential_temperature import AmbientDryAirPotentialTemperature +from .ambient_pressure import AmbientPressure +from .ambient_relative_humidity import AmbientRelativeHumidity +from .ambient_temperature import AmbientTemperature +from .ambient_water_vapour_mixing_ratio import AmbientWaterVapourMixingRatio diff --git a/PySDM/source/PySDM/products/ambient_thermodynamics/ambient_dry_air_density.py b/PySDM/source/PySDM/products/ambient_thermodynamics/ambient_dry_air_density.py new file mode 100644 index 0000000000000000000000000000000000000000..431d4070010cfc020f749366378bde5a88d7a338 --- /dev/null +++ b/PySDM/source/PySDM/products/ambient_thermodynamics/ambient_dry_air_density.py @@ -0,0 +1,11 @@ +""" +ambient dry-air density +""" + +from PySDM.products.impl import MoistEnvironmentProduct, register_product + + +@register_product() +class AmbientDryAirDensity(MoistEnvironmentProduct): + def __init__(self, name="rhod", unit="kg/m^3", var=None): + super().__init__(name=name, unit=unit, var=var) diff --git a/PySDM/source/PySDM/products/ambient_thermodynamics/ambient_dry_air_potential_temperature.py b/PySDM/source/PySDM/products/ambient_thermodynamics/ambient_dry_air_potential_temperature.py new file mode 100644 index 0000000000000000000000000000000000000000..86b45702a7c29ed1a61a33dd62b8a96791de4976 --- /dev/null +++ b/PySDM/source/PySDM/products/ambient_thermodynamics/ambient_dry_air_potential_temperature.py @@ -0,0 +1,11 @@ +""" +ambient dry-air potential temperature (computed using dry air partial pressure) +""" + +from PySDM.products.impl import MoistEnvironmentProduct, register_product + + +@register_product() +class AmbientDryAirPotentialTemperature(MoistEnvironmentProduct): + def __init__(self, unit="K", name=None, var=None): + super().__init__(unit=unit, name=name, var=var) diff --git a/PySDM/source/PySDM/products/ambient_thermodynamics/ambient_pressure.py b/PySDM/source/PySDM/products/ambient_thermodynamics/ambient_pressure.py new file mode 100644 index 0000000000000000000000000000000000000000..627f7c437f4fef1e98854f22ac04414c84e09884 --- /dev/null +++ b/PySDM/source/PySDM/products/ambient_thermodynamics/ambient_pressure.py @@ -0,0 +1,11 @@ +""" +ambient pressure +""" + +from PySDM.products.impl import MoistEnvironmentProduct, register_product + + +@register_product() +class AmbientPressure(MoistEnvironmentProduct): + def __init__(self, name=None, unit="Pa", var=None): + super().__init__(name=name, unit=unit, var=var) diff --git a/PySDM/source/PySDM/products/ambient_thermodynamics/ambient_relative_humidity.py b/PySDM/source/PySDM/products/ambient_thermodynamics/ambient_relative_humidity.py new file mode 100644 index 0000000000000000000000000000000000000000..e6ce0f423a50815229152a4cedaf7231c2e7cc6a --- /dev/null +++ b/PySDM/source/PySDM/products/ambient_thermodynamics/ambient_relative_humidity.py @@ -0,0 +1,20 @@ +""" +ambient relative humidity (wrt water or ice) +""" + +from PySDM.products.impl import MoistEnvironmentProduct, register_product + + +@register_product() +class AmbientRelativeHumidity(MoistEnvironmentProduct): + def __init__(self, name=None, unit="dimensionless", var=None, ice=False): + super().__init__(name=name, unit=unit, var=var) + self.ice = ice + + def _impl(self, **kwargs): + super()._impl() + if self.ice: + RHw = self.buffer.copy() + self._download_to_buffer(self.environment["a_w_ice"]) + self.buffer[:] = RHw / self.buffer[:] + return self.buffer diff --git a/PySDM/source/PySDM/products/ambient_thermodynamics/ambient_temperature.py b/PySDM/source/PySDM/products/ambient_thermodynamics/ambient_temperature.py new file mode 100644 index 0000000000000000000000000000000000000000..8a6127ec343d3ab5211f23a80f305df70e16fe7f --- /dev/null +++ b/PySDM/source/PySDM/products/ambient_thermodynamics/ambient_temperature.py @@ -0,0 +1,11 @@ +""" +ambient temperature +""" + +from PySDM.products.impl import MoistEnvironmentProduct, register_product + + +@register_product() +class AmbientTemperature(MoistEnvironmentProduct): + def __init__(self, name=None, unit="K", var=None): + super().__init__(name=name, unit=unit, var=var) diff --git a/PySDM/source/PySDM/products/ambient_thermodynamics/ambient_water_vapour_mixing_ratio.py b/PySDM/source/PySDM/products/ambient_thermodynamics/ambient_water_vapour_mixing_ratio.py new file mode 100644 index 0000000000000000000000000000000000000000..91507e66b1fd97b98aff9fa3fe3f879c7822c70f --- /dev/null +++ b/PySDM/source/PySDM/products/ambient_thermodynamics/ambient_water_vapour_mixing_ratio.py @@ -0,0 +1,11 @@ +""" +ambient water vapour mixing ratio (mass of water vapour per mass of dry air) +""" + +from PySDM.products.impl import MoistEnvironmentProduct, register_product + + +@register_product() +class AmbientWaterVapourMixingRatio(MoistEnvironmentProduct): + def __init__(self, name=None, unit="dimensionless", var=None): + super().__init__(unit=unit, name=name, var=var) diff --git a/PySDM/source/PySDM/products/aqueous_chemistry/__init__.py b/PySDM/source/PySDM/products/aqueous_chemistry/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..ab59effcc4bd6ae98b4c26f82a399ada83bab59f --- /dev/null +++ b/PySDM/source/PySDM/products/aqueous_chemistry/__init__.py @@ -0,0 +1,9 @@ +""" +products pertinent to the `PySDM.dynamics.aqueous_chemistry.AqueousChemistry` dynamic +""" + +from .acidity import Acidity +from .aqueous_mass_spectrum import AqueousMassSpectrum +from .aqueous_mole_fraction import AqueousMoleFraction +from .gaseous_mole_fraction import GaseousMoleFraction +from .total_dry_mass_mixing_ratio import TotalDryMassMixingRatio diff --git a/PySDM/source/PySDM/products/aqueous_chemistry/acidity.py b/PySDM/source/PySDM/products/aqueous_chemistry/acidity.py new file mode 100644 index 0000000000000000000000000000000000000000..320d3787757748e87f548099065bc4ea2260fbac --- /dev/null +++ b/PySDM/source/PySDM/products/aqueous_chemistry/acidity.py @@ -0,0 +1,58 @@ +""" +average pH (averaging after or before taking the logarithm in pH definition) +with weighting either by number or volume +""" + +import numpy as np + +from PySDM.products.impl import MomentProduct, register_product + + +@register_product() +class Acidity(MomentProduct): + def __init__( + self, + *, + radius_range=(0, np.inf), + weighting="volume", + attr="conc_H", + unit="dimensionless", + name=None, + ): + assert attr in ("pH", "moles_H", "conc_H") + self.attr = attr + + if weighting == "number": + self.weighting_rank = 0 + elif weighting == "volume": + self.weighting_rank = 1 + else: + raise NotImplementedError() + + self.radius_range = radius_range + super().__init__(name=name, unit=unit) + + def register(self, builder): + builder.request_attribute("conc_H") + super().register(builder) + + def _impl(self, **kwargs): + self._download_moment_to_buffer( + attr=self.attr, + rank=1, + filter_range=( + self.formulae.trivia.volume(self.radius_range[0]), + self.formulae.trivia.volume(self.radius_range[1]), + ), + filter_attr="volume", + weighting_attribute="volume", + weighting_rank=self.weighting_rank, + ) + if self.attr == "conc_H": + self.buffer[:] = self.formulae.trivia.H2pH(self.buffer[:]) + elif self.attr == "pH": + pass + else: + raise NotImplementedError() + + return self.buffer diff --git a/PySDM/source/PySDM/products/aqueous_chemistry/aqueous_mass_spectrum.py b/PySDM/source/PySDM/products/aqueous_chemistry/aqueous_mass_spectrum.py new file mode 100644 index 0000000000000000000000000000000000000000..51a013f6d7a0f74143f085ab6f22a911a19e308e --- /dev/null +++ b/PySDM/source/PySDM/products/aqueous_chemistry/aqueous_mass_spectrum.py @@ -0,0 +1,71 @@ +""" +dry-radius-binned concentration of aqueous-chemistry relevant compounds (optionally + expressed as specific concentration) +""" + +import numpy as np +from chempy import Substance + +from PySDM.dynamics.impl.chemistry_utils import AQUEOUS_COMPOUNDS +from PySDM.physics.constants import si +from PySDM.products.impl import SpectrumMomentProduct, register_product + + +@register_product() +class AqueousMassSpectrum(SpectrumMomentProduct): + def __init__( + self, *, key, dry_radius_bins_edges, specific=False, name=None, unit="kg/m^3" + ): + super().__init__(name=name, unit=unit, attr_unit="m") + self.key = key + self.dry_radius_bins_edges = dry_radius_bins_edges + self.molar_mass = ( + Substance.from_formula(AQUEOUS_COMPOUNDS[key][0]).mass * si.g / si.mole + ) + self.specific = specific + + def register(self, builder): + builder.request_attribute("dry volume") + builder.request_attribute(f"moles_{self.key}") + + dry_volume_bins_edges = builder.particulator.formulae.trivia.volume( + self.dry_radius_bins_edges + ) + self.attr_bins_edges = builder.particulator.backend.Storage.from_ndarray( + dry_volume_bins_edges + ) + + super().register(builder) + + self.shape = (*builder.particulator.mesh.grid, len(self.attr_bins_edges) - 1) + + def _impl(self, **kwargs): + vals = np.empty([self.particulator.mesh.n_cell, len(self.attr_bins_edges) - 1]) + self._recalculate_spectrum_moment( + attr=f"moles_{self.key}", rank=1, filter_attr="dry volume" + ) + + for i in range(vals.shape[1]): + self._download_spectrum_moment_to_buffer(rank=1, bin_number=i) + vals[:, i] = self.buffer.ravel() + self._download_spectrum_moment_to_buffer(rank=0, bin_number=i) + vals[:, i] *= self.buffer.ravel() + d_log10_diameter = np.diff(np.log10(2 * self.dry_radius_bins_edges)) + vals *= self.molar_mass / d_log10_diameter / self.particulator.mesh.dv + + if self.specific: + self._download_to_buffer(self.particulator.environment["rhod"]) + vals[:] /= self.buffer + return vals + + +@register_product() +class SpecificAqueousMassSpectrum(AqueousMassSpectrum): + def __init__(self, key, dry_radius_bins_edges, name=None, unit="dimensionless"): + super().__init__( + key=key, + dry_radius_bins_edges=dry_radius_bins_edges, + name=name, + unit=unit, + specific=True, + ) diff --git a/PySDM/source/PySDM/products/aqueous_chemistry/aqueous_mole_fraction.py b/PySDM/source/PySDM/products/aqueous_chemistry/aqueous_mole_fraction.py new file mode 100644 index 0000000000000000000000000000000000000000..0cb2d8dc6646502566f40e8e3e06337e7841338e --- /dev/null +++ b/PySDM/source/PySDM/products/aqueous_chemistry/aqueous_mole_fraction.py @@ -0,0 +1,38 @@ +""" +mole fractions of aqueous-chemistry relevant compounds +""" + +from PySDM.products.impl import MomentProduct, register_product + +DUMMY_SPECIFIC_GRAVITY = 44 + + +@register_product() +class AqueousMoleFraction(MomentProduct): + def __init__(self, key, unit="dimensionless", name=None): + super().__init__(unit=unit, name=name) + self.aqueous_chemistry = None + self.key = key + + def register(self, builder): + super().register(builder) + self.aqueous_chemistry = self.particulator.dynamics["AqueousChemistry"] + + def _impl(self, **kwargs): + attr = "moles_" + self.key + + self._download_moment_to_buffer(attr=attr, rank=0) + number = self.buffer.copy() + + self._download_moment_to_buffer(attr=attr, rank=1) + tmp = self.buffer.copy() + tmp[:] *= number + tmp[:] *= DUMMY_SPECIFIC_GRAVITY * self.formulae.constants.Md + + self._download_to_buffer(self.particulator.environment["rhod"]) + tmp[:] /= self.particulator.mesh.dv + tmp[:] /= self.buffer + tmp[:] = self.formulae.trivia.mixing_ratio_2_mole_fraction( + tmp[:], specific_gravity=DUMMY_SPECIFIC_GRAVITY + ) + return tmp diff --git a/PySDM/source/PySDM/products/aqueous_chemistry/gaseous_mole_fraction.py b/PySDM/source/PySDM/products/aqueous_chemistry/gaseous_mole_fraction.py new file mode 100644 index 0000000000000000000000000000000000000000..a6fd46bde6ca6281541329973952e60b871cbe60 --- /dev/null +++ b/PySDM/source/PySDM/products/aqueous_chemistry/gaseous_mole_fraction.py @@ -0,0 +1,25 @@ +""" +mole fractions of gaseous compounds relevant for aqueous chemistry +""" + +from PySDM.dynamics.impl.chemistry_utils import GASEOUS_COMPOUNDS +from PySDM.products.impl import Product, register_product + + +@register_product() +class GaseousMoleFraction(Product): + def __init__(self, key, unit="dimensionless", name=None): + super().__init__(name=name, unit=unit) + self.aqueous_chemistry = None + self.compound = GASEOUS_COMPOUNDS[key] + + def register(self, builder): + super().register(builder) + self.aqueous_chemistry = self.particulator.dynamics["AqueousChemistry"] + + def _impl(self, **kwargs): + tmp = self.formulae.trivia.mixing_ratio_2_mole_fraction( + self.aqueous_chemistry.environment_mixing_ratios[self.compound], + specific_gravity=self.aqueous_chemistry.specific_gravities[self.compound], + ) + return tmp diff --git a/PySDM/source/PySDM/products/aqueous_chemistry/total_dry_mass_mixing_ratio.py b/PySDM/source/PySDM/products/aqueous_chemistry/total_dry_mass_mixing_ratio.py new file mode 100644 index 0000000000000000000000000000000000000000..78bde64f53185eb5a7e5a5fb567b61be20a639a8 --- /dev/null +++ b/PySDM/source/PySDM/products/aqueous_chemistry/total_dry_mass_mixing_ratio.py @@ -0,0 +1,25 @@ +""" +dry aerosol mass summed over all particles in a grid box per mass of dry air +""" + +import numpy as np + +from PySDM.products.impl import MomentProduct, register_product + + +@register_product() +class TotalDryMassMixingRatio(MomentProduct): + def __init__(self, density, name=None, unit="kg/kg"): + super().__init__(unit=unit, name=name) + self.density = density + + def _impl(self, **kwargs): + self._download_moment_to_buffer(attr="dry volume", rank=1) + self.buffer[:] *= self.density + result = np.copy(self.buffer) + self._download_moment_to_buffer(attr="dry volume", rank=0) + result[:] *= self.buffer + self._download_to_buffer(self.particulator.environment["rhod"]) + result[:] /= self.particulator.mesh.dv + result[:] /= self.buffer + return result diff --git a/PySDM/source/PySDM/products/collision/__init__.py b/PySDM/source/PySDM/products/collision/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..334d1d1318acc508bcaa636eb23c1aa4ed6d82ef --- /dev/null +++ b/PySDM/source/PySDM/products/collision/__init__.py @@ -0,0 +1,13 @@ +""" +Collision rate products for breakup, coalescence, and collisions +""" + +from .collision_rates import ( # BreakupOnlyRatePerGridbox,; CoalescenceOnlyRatePerGridbox, + BreakupRateDeficitPerGridbox, + BreakupRatePerGridbox, + CoalescenceRatePerGridbox, + CollisionRateDeficitPerGridbox, + CollisionRatePerGridbox, +) +from .collision_timestep_mean import CollisionTimestepMean +from .collision_timestep_min import CollisionTimestepMin diff --git a/PySDM/source/PySDM/products/collision/collision_rates.py b/PySDM/source/PySDM/products/collision/collision_rates.py new file mode 100644 index 0000000000000000000000000000000000000000..023f355c14c7b47cd8dbff8c41b46cf831569dc3 --- /dev/null +++ b/PySDM/source/PySDM/products/collision/collision_rates.py @@ -0,0 +1,47 @@ +""" +Rates of collision events and their deficit wrt expected values in case mutiplicity + values preclude representation of resultant multiplicity in multiple collisions, + or due to unrepresentable breakups from integer overflow +""" + +from PySDM.products.impl import RateProduct, register_product + + +@register_product() +class CollisionRateDeficitPerGridbox(RateProduct): + def __init__(self, name=None, unit="s^-1 kg^-1"): + super().__init__( + name=name, unit=unit, counter="collision_rate_deficit", dynamic="Collision" + ) + + +@register_product() +class CollisionRatePerGridbox(RateProduct): + def __init__(self, name=None, unit="s^-1 kg^-1"): + super().__init__( + name=name, unit=unit, counter="collision_rate", dynamic="Collision" + ) + + +@register_product() +class CoalescenceRatePerGridbox(RateProduct): + def __init__(self, name=None, unit="s^-1 kg^-1"): + super().__init__( + name=name, unit=unit, counter="coalescence_rate", dynamic="Collision" + ) + + +@register_product() +class BreakupRatePerGridbox(RateProduct): + def __init__(self, name=None, unit="s^-1 kg^-1"): + super().__init__( + name=name, unit=unit, counter="breakup_rate", dynamic="Collision" + ) + + +@register_product() +class BreakupRateDeficitPerGridbox(RateProduct): + def __init__(self, name=None, unit="s^-1 kg^-1"): + super().__init__( + name=name, unit=unit, counter="breakup_rate_deficit", dynamic="Collision" + ) diff --git a/PySDM/source/PySDM/products/collision/collision_timestep_mean.py b/PySDM/source/PySDM/products/collision/collision_timestep_mean.py new file mode 100644 index 0000000000000000000000000000000000000000..1b7d090aaab70426b510c627c0e663c20d75bfec --- /dev/null +++ b/PySDM/source/PySDM/products/collision/collision_timestep_mean.py @@ -0,0 +1,40 @@ +""" +Average collision timestep length used when adaptive timestepping is enabled in the + `PySDM.dynamics.collisions.collision.Collision` dynamic (fetching a value reset the counter) +""" + +import numba +import numpy as np + +from PySDM.backends.impl_numba.conf import JIT_FLAGS +from PySDM.products.impl import Product, register_product + + +@register_product() +class CollisionTimestepMean(Product): + def __init__(self, unit="s", name=None): + super().__init__(unit=unit, name=name) + self.count = 0 + self.collision = None + self.range = None + + def register(self, builder): + super().register(builder) + self.particulator.observers.append(self) + self.collision = self.particulator.dynamics["Collision"] + self.range = self.collision.dt_coal_range + + @staticmethod + @numba.njit(**JIT_FLAGS) + def __get_impl(buffer, count, dt): + buffer[:] = np.where(buffer[:] > 0, count * dt / buffer[:], np.nan) + + def _impl(self, **kwargs): + self._download_to_buffer(self.collision.stats_n_substep) + CollisionTimestepMean.__get_impl(self.buffer, self.count, self.particulator.dt) + self.collision.stats_n_substep[:] = 0 + self.count = 0 + return self.buffer + + def notify(self): + self.count += 1 diff --git a/PySDM/source/PySDM/products/collision/collision_timestep_min.py b/PySDM/source/PySDM/products/collision/collision_timestep_min.py new file mode 100644 index 0000000000000000000000000000000000000000..6fe22b3ab1dc36331911302ad1f522f5c38268da --- /dev/null +++ b/PySDM/source/PySDM/products/collision/collision_timestep_min.py @@ -0,0 +1,24 @@ +""" +Minimal collision timestep used when adaptive timestepping is enabled in the + `PySDM.dynamics.collisions.collision.Collision` dynamic (fetching a value resets the counter) +""" + +from PySDM.products.impl import Product, register_product + + +@register_product() +class CollisionTimestepMin(Product): + def __init__(self, unit="s", name=None): + super().__init__(unit=unit, name=name) + self.collision = None + self.range = None + + def register(self, builder): + super().register(builder) + self.collision = self.particulator.dynamics["Collision"] + self.range = self.collision.dt_coal_range + + def _impl(self, **kwargs): + self._download_to_buffer(self.collision.stats_dt_min) + self.collision.stats_dt_min[:] = self.collision.particulator.dt + return self.buffer diff --git a/PySDM/source/PySDM/products/condensation/__init__.py b/PySDM/source/PySDM/products/condensation/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..5574b072842a78603f81eaa6c8916ba477b13f90 --- /dev/null +++ b/PySDM/source/PySDM/products/condensation/__init__.py @@ -0,0 +1,8 @@ +""" +products pertinent to the `PySDM.dynamics.condensation.Condensation` dynamic +""" + +from .activable_fraction import ActivableFraction +from .condensation_timestep import CondensationTimestepMax, CondensationTimestepMin +from .event_rates import ActivatingRate, DeactivatingRate, RipeningRate +from .peak_saturation import PeakSaturation diff --git a/PySDM/source/PySDM/products/condensation/activable_fraction.py b/PySDM/source/PySDM/products/condensation/activable_fraction.py new file mode 100644 index 0000000000000000000000000000000000000000..b8bc8ef72103821d8cd5ee02c2fdbd9cbb261943 --- /dev/null +++ b/PySDM/source/PySDM/products/condensation/activable_fraction.py @@ -0,0 +1,40 @@ +""" +fraction of particles with critical saturation lower than a given saturation + (passed as keyword argument while calling `get()`) +""" + +import numpy as np +from PySDM.products.impl import MomentProduct, register_product + + +@register_product() +class ActivableFraction(MomentProduct): + def __init__( + self, unit="dimensionless", name=None, filter_attr="critical saturation" + ): + super().__init__(name=name, unit=unit) + self.filter_attr = filter_attr + + def register(self, builder): + super().register(builder) + builder.request_attribute(self.filter_attr) + + def _impl(self, **kwargs): + if self.filter_attr.startswith("critical saturation"): + s_max = kwargs["S_max"] + assert not np.isfinite(s_max) or 0 < s_max < 1.1 + filter_range = (0, s_max) + elif self.filter_attr.startswith("wet to critical volume ratio"): + filter_range = (1, np.inf) + else: + assert False + self._download_moment_to_buffer( + attr="volume", + rank=0, + filter_range=filter_range, + filter_attr=self.filter_attr, + ) + frac = self.buffer.copy() + self._download_moment_to_buffer(attr="volume", rank=0) + frac /= self.buffer + return frac diff --git a/PySDM/source/PySDM/products/condensation/condensation_timestep.py b/PySDM/source/PySDM/products/condensation/condensation_timestep.py new file mode 100644 index 0000000000000000000000000000000000000000..1cece9f0d779fb483853185a6ccda1fa1c4e98b5 --- /dev/null +++ b/PySDM/source/PySDM/products/condensation/condensation_timestep.py @@ -0,0 +1,54 @@ +""" +minimum and maximum condensation timestep in between product get() calls +(fetching a value resets the counter) +The time step is variable when adaptive timestepping is enabled +(see Fig. 5 and Fig. 8 in [Bartman 2020 (MSc thesis, Section 3.3)](https://www.ap.uj.edu.pl/diplomas/attachments/file/download/125485) +for sample plots of this product) +""" # pylint: disable=line-too-long + +import numpy as np + +from PySDM.products.impl import Product, register_product + + +@register_product() +class _CondensationTimestep(Product): + def __init__(self, name, unit, extremum, reset_value): + super().__init__( + name=name, + unit=unit, + ) + self.extremum = extremum + self.reset_value = reset_value + self.value = None + self.condensation = None + self.range = None + + def register(self, builder): + super().register(builder) + self.particulator.observers.append(self) + self.condensation = self.particulator.dynamics["Condensation"] + self.range = self.condensation.dt_cond_range + self.value = np.full_like(self.buffer, np.nan) + + def notify(self): + self._download_to_buffer(self.condensation.counters["n_substeps"]) + self.buffer[:] = self.condensation.particulator.dt / self.buffer + self.value = self.extremum(self.buffer, self.value) + + def _impl(self, **kwargs): + self.buffer[:] = self.value[:] + self.value[:] = self.reset_value + return self.buffer + + +@register_product() +class CondensationTimestepMin(_CondensationTimestep): + def __init__(self, name=None, unit="s"): + super().__init__(name=name, unit=unit, extremum=np.minimum, reset_value=np.inf) + + +@register_product() +class CondensationTimestepMax(_CondensationTimestep): + def __init__(self, name=None, unit="s"): + super().__init__(name=name, unit=unit, extremum=np.maximum, reset_value=-np.inf) diff --git a/PySDM/source/PySDM/products/condensation/event_rates.py b/PySDM/source/PySDM/products/condensation/event_rates.py new file mode 100644 index 0000000000000000000000000000000000000000..2bc9273fdafc73d3e8f29f320af91475d73bbdd2 --- /dev/null +++ b/PySDM/source/PySDM/products/condensation/event_rates.py @@ -0,0 +1,61 @@ +""" +rates of activation, deactivation and ripening events (take into account substeps, + fetching a value resets the given counter) +""" + +import numpy as np + +from PySDM.products.impl import Product, register_product + + +@register_product() +class EventRate(Product): + def __init__(self, what, name=None, unit=None): + super().__init__(name=name, unit=unit) + self.condensation = None + self.timestep_count = 0 + self.what = what + self.event_count = None + + def register(self, builder): + super().register(builder) + self.particulator.observers.append(self) + self.condensation = self.particulator.dynamics["Condensation"] + self.event_count = np.zeros_like(self.buffer) + + def notify(self): + self.timestep_count += 1 + self._download_to_buffer(self.condensation.counters["n_" + self.what]) + self.event_count[:] += self.buffer[:] + + def _impl(self, **kwargs): + if self.timestep_count == 0: + return self.event_count + + self.event_count[:] /= ( + self.timestep_count * self.particulator.dt * self.particulator.mesh.dv + ) + self._download_to_buffer(self.particulator.environment["rhod"]) + self.event_count[:] /= self.buffer[:] + self.buffer[:] = self.event_count[:] + self.timestep_count = 0 + self.event_count[:] = 0 + return self.buffer + + +@register_product() +class RipeningRate(EventRate): + def __init__(self, name=None, unit="s^-1 kg^-1"): + super().__init__("ripening", name=name, unit=unit) + + +@register_product() +class ActivatingRate(EventRate): + def __init__(self, name=None, unit="s^-1 kg^-1"): + super().__init__("activating", name=name, unit=unit) + + +@register_product() +class DeactivatingRate(EventRate): + def __init__(self, name=None, unit="s^-1 kg^-1"): + super().__init__("deactivating", name=name, unit=unit) diff --git a/PySDM/source/PySDM/products/condensation/peak_saturation.py b/PySDM/source/PySDM/products/condensation/peak_saturation.py new file mode 100644 index 0000000000000000000000000000000000000000..ae36b22a0b20373e3d941bca19582a4681a65af7 --- /dev/null +++ b/PySDM/source/PySDM/products/condensation/peak_saturation.py @@ -0,0 +1,37 @@ +""" +highest saturation encountered while solving for condensation/evaporation (takes into account + substeps thus values might differ from ambient saturation reported via + `PySDM.products.ambient_thermodynamics.ambient_relative_humidity.AmbientRelativeHumidity`; + fetching a value resets the maximum value) +""" + +import numpy as np + +from PySDM.products.impl import Product, register_product + + +@register_product() +class PeakSaturation(Product): + def __init__(self, unit="dimensionless", name=None): + super().__init__(unit=unit, name=name) + self.condensation = None + self.RH_max = None + + def register(self, builder): + super().register(builder) + self.particulator.observers.append(self) + + assert ( + "Condensation" in self.particulator.dynamics + ), "It seems the Condensation dynamic was not added when building particulator" + self.condensation = self.particulator.dynamics["Condensation"] + self.RH_max = np.full_like(self.buffer, np.nan) + + def _impl(self, **kwargs): + self.buffer[:] = self.RH_max[:] + self.RH_max[:] = 0 + return self.buffer + + def notify(self): + self._download_to_buffer(self.condensation.rh_max) + self.RH_max[:] = np.maximum(self.buffer[:], self.RH_max[:]) diff --git a/PySDM/source/PySDM/products/displacement/__init__.py b/PySDM/source/PySDM/products/displacement/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..696efe67e10afb89b56a67586ce28ef1d1879317 --- /dev/null +++ b/PySDM/source/PySDM/products/displacement/__init__.py @@ -0,0 +1,8 @@ +""" +products pertinent to the `PySDM.dynamics.displacement.Displacement` dynamic +""" + +from .averaged_terminal_velocity import AveragedTerminalVelocity +from .flow_velocity_component import FlowVelocityComponent +from .max_courant_number import MaxCourantNumber +from .surface_precipitation import SurfacePrecipitation diff --git a/PySDM/source/PySDM/products/displacement/averaged_terminal_velocity.py b/PySDM/source/PySDM/products/displacement/averaged_terminal_velocity.py new file mode 100644 index 0000000000000000000000000000000000000000..944decaa34cf4361aad6a7c73b1b0b23881ba8c7 --- /dev/null +++ b/PySDM/source/PySDM/products/displacement/averaged_terminal_velocity.py @@ -0,0 +1,43 @@ +""" +average terminal with weighting either by number or volume +""" + +import numpy as np + +from PySDM.products.impl import MomentProduct, register_product + + +@register_product() +class AveragedTerminalVelocity(MomentProduct): + def __init__( + self, *, radius_range=(0, np.inf), weighting="volume", unit="m/s", name=None + ): + self.attr = "terminal velocity" + + if weighting == "number": + self.weighting_rank = 0 + elif weighting == "volume": + self.weighting_rank = 1 + else: + raise NotImplementedError() + + self.radius_range = radius_range + super().__init__(name=name, unit=unit) + + def register(self, builder): + builder.request_attribute(self.attr) + super().register(builder) + + def _impl(self, **kwargs): + self._download_moment_to_buffer( + attr=self.attr, + rank=1, + filter_range=( + self.formulae.trivia.volume(self.radius_range[0]), + self.formulae.trivia.volume(self.radius_range[1]), + ), + weighting_attribute="volume", + weighting_rank=self.weighting_rank, + ) + + return self.buffer diff --git a/PySDM/source/PySDM/products/displacement/flow_velocity_component.py b/PySDM/source/PySDM/products/displacement/flow_velocity_component.py new file mode 100644 index 0000000000000000000000000000000000000000..a0a86dded00317c7c01218347c48d6c3c30285b8 --- /dev/null +++ b/PySDM/source/PySDM/products/displacement/flow_velocity_component.py @@ -0,0 +1,40 @@ +""" +reports on the flow velocity +""" + +import numpy as np + +from PySDM.products.impl import Product, register_product + + +@register_product() +class FlowVelocityComponent(Product): + def __init__(self, component: int, name=None, unit="m/s"): + super().__init__(unit=unit, name=name) + assert component in (0, 1) + self.component = component + self.displacement = None + self.grid_step = np.nan + self.time_step = np.nan + + def register(self, builder): + super().register(builder) + self.displacement = self.particulator.dynamics["Displacement"] + self.time_step = self.particulator.dt + mesh = self.particulator.mesh + self.grid_step = mesh.size[self.component] / mesh.grid[self.component] + + def _impl(self, **kwargs): + courant_component = self.displacement.courant[self.component].to_ndarray() + if self.component == 0: + self.buffer[:] = 0.5 * ( + courant_component[:-1, :] + courant_component[1:, :] + ) + elif self.component == 1: + self.buffer[:] = 0.5 * ( + courant_component[:, :-1] + courant_component[:, 1:] + ) + else: + raise NotImplementedError() + self.buffer[:] *= self.grid_step / self.time_step + return self.buffer diff --git a/PySDM/source/PySDM/products/displacement/max_courant_number.py b/PySDM/source/PySDM/products/displacement/max_courant_number.py new file mode 100644 index 0000000000000000000000000000000000000000..882b23b254b6775d72e4ca6d61640e97fa205074 --- /dev/null +++ b/PySDM/source/PySDM/products/displacement/max_courant_number.py @@ -0,0 +1,34 @@ +""" +reports on the maximum Courant field value for each cell (maximum of +absolute values of Courant number on all edges of a cell) +""" + +import numpy as np + +from PySDM.products.impl import Product, register_product + + +@register_product() +class MaxCourantNumber(Product): + def __init__(self, name=None, unit="dimensionless"): + super().__init__(unit=unit, name=name) + self.displacement = None + + def register(self, builder): + super().register(builder) + self.displacement = self.particulator.dynamics["Displacement"] + + def _impl(self, **kwargs): + self.buffer[:] = 0 + + field = tuple( + abs(component.to_ndarray()) for component in self.displacement.courant + ) + self.buffer[:] = np.maximum( + self.buffer, np.maximum(field[0][:-1, :], field[0][1:, :]) + ) + self.buffer[:] = np.maximum( + self.buffer, np.maximum(field[1][:, :-1], field[1][:, 1:]) + ) + + return self.buffer diff --git a/PySDM/source/PySDM/products/displacement/surface_precipitation.py b/PySDM/source/PySDM/products/displacement/surface_precipitation.py new file mode 100644 index 0000000000000000000000000000000000000000..0851db3b72bca7d5da4b1b0cad4ad0df7d554f8e --- /dev/null +++ b/PySDM/source/PySDM/products/displacement/surface_precipitation.py @@ -0,0 +1,47 @@ +""" +water (equivalent) volume flux derived from sizes of particles crossing bottom domain boundary +(computed from mass of both liquid and ice water) +""" + +from PySDM.products.impl import Product, register_product + + +@register_product() +class SurfacePrecipitation(Product): + def __init__(self, name=None, unit="m/s"): + super().__init__(unit=unit, name=name) + self.displacement = None + self.domain_bottom_surface_area = None + self._reset_counters() + + def _reset_counters(self): + self.accumulated_rainfall_mass = 0.0 + self.elapsed_time = 0.0 + + def register(self, builder): + super().register(builder) + self.particulator.observers.append(self) + self.shape = () + self.displacement = self.particulator.dynamics["Displacement"] + self.domain_bottom_surface_area = ( + self.particulator.mesh.domain_bottom_surface_area + ) + + def _impl(self, **kwargs) -> float: + if self.elapsed_time == 0.0: + return 0.0 + + result = ( + self.accumulated_rainfall_mass + / self.formulae.constants.rho_w + / self.elapsed_time + / self.domain_bottom_surface_area + ) + self._reset_counters() + return result + + def notify(self): + self.accumulated_rainfall_mass += ( + self.displacement.precipitation_mass_in_last_step + ) + self.elapsed_time += self.displacement.particulator.dt diff --git a/PySDM/source/PySDM/products/freezing/__init__.py b/PySDM/source/PySDM/products/freezing/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..ccd0afdf9574757fe6e8e3b8f2b8029e88e95c1d --- /dev/null +++ b/PySDM/source/PySDM/products/freezing/__init__.py @@ -0,0 +1,15 @@ +""" +products pertinent to the `PySDM.dynamics.freezing.Freezing` dynamic +""" + +from .cooling_rate import CoolingRate +from .freezable_specific_concentration import FreezableSpecificConcentration +from .frozen_particle_concentration import ( + FrozenParticleConcentration, + FrozenParticleSpecificConcentration, +) +from .ice_nuclei_concentration import ( + IceNucleiConcentration, + SpecificIceNucleiConcentration, +) +from .total_unfrozen_immersed_surface_area import TotalUnfrozenImmersedSurfaceArea diff --git a/PySDM/source/PySDM/products/freezing/cooling_rate.py b/PySDM/source/PySDM/products/freezing/cooling_rate.py new file mode 100644 index 0000000000000000000000000000000000000000..5b25daba94a3ea112994b99a85b3894199ee5b54 --- /dev/null +++ b/PySDM/source/PySDM/products/freezing/cooling_rate.py @@ -0,0 +1,19 @@ +""" +number-averaged cooling rate +""" + +from PySDM.products.impl import MomentProduct, register_product + + +@register_product() +class CoolingRate(MomentProduct): + def __init__(self, unit="K/s", name=None): + super().__init__(unit=unit, name=name) + + def register(self, builder): + builder.request_attribute("cooling rate") + super().register(builder) + + def _impl(self, **kwargs): + self._download_moment_to_buffer(attr="cooling rate", rank=1) + return self.buffer diff --git a/PySDM/source/PySDM/products/freezing/freezable_specific_concentration.py b/PySDM/source/PySDM/products/freezing/freezable_specific_concentration.py new file mode 100644 index 0000000000000000000000000000000000000000..a1587fad7c5b32aa33dcc5c99c406a8ba340a8a6 --- /dev/null +++ b/PySDM/source/PySDM/products/freezing/freezable_specific_concentration.py @@ -0,0 +1,41 @@ +""" +freezing-temperature binned specific concentration of particles +""" + +import numpy as np + +from PySDM.products.impl import SpectrumMomentProduct, register_product + + +@register_product() +class FreezableSpecificConcentration(SpectrumMomentProduct): + def __init__(self, temperature_bins_edges, name=None, unit="kg^-1 K^-1"): + super().__init__(name=name, unit=unit, attr_unit="K") + self.attr_bins_edges = temperature_bins_edges + + def register(self, builder): + builder.request_attribute("freezing temperature") + particulator = builder.particulator + self.attr_bins_edges = particulator.backend.Storage.from_ndarray( + self.attr_bins_edges + ) + super().register(builder) + self.shape = (*particulator.mesh.grid, len(self.attr_bins_edges) - 1) + + def _impl(self, **kwargs): + vals = np.empty([self.particulator.mesh.n_cell, len(self.attr_bins_edges) - 1]) + self._recalculate_spectrum_moment( + attr="volume", filter_attr="freezing temperature", rank=0 + ) + + for i in range(vals.shape[1]): + self._download_spectrum_moment_to_buffer(rank=0, bin_number=i) + vals[:, i] = self.buffer.ravel() + + self._download_to_buffer(self.particulator.environment["rhod"]) + rhod = self.buffer.ravel() + for i in range(len(self.attr_bins_edges) - 1): + dT = abs(self.attr_bins_edges[i + 1] - self.attr_bins_edges[i]) + vals[:, i] /= rhod * dT * self.particulator.mesh.dv + + return np.squeeze(vals.reshape(self.shape)) diff --git a/PySDM/source/PySDM/products/freezing/frozen_particle_concentration.py b/PySDM/source/PySDM/products/freezing/frozen_particle_concentration.py new file mode 100644 index 0000000000000000000000000000000000000000..e1240cf0e8630450924eef2ae6343bab5210effc --- /dev/null +++ b/PySDM/source/PySDM/products/freezing/frozen_particle_concentration.py @@ -0,0 +1,52 @@ +""" +concentration of frozen particles (unactivated, activated or both) +""" + +import numpy as np + +from PySDM.products.impl import ConcentrationProduct, register_product + + +@register_product() +class FrozenParticleConcentration(ConcentrationProduct): + def __init__( + self, + *, + count_unactivated: bool, + count_activated: bool, + unit="m^-3", + name=None, + specific=False, + stp=False, + ): + super().__init__(specific=specific, stp=stp, unit=unit, name=name) + self.__filter_range = [-np.inf, 0] + if not count_activated: + self.__filter_range[0] = -1 + if not count_unactivated: + self.__filter_range[1] = -1 + + def register(self, builder): + super().register(builder) + builder.request_attribute("wet to critical volume ratio") + + def _impl(self, **kwargs): + self._download_moment_to_buffer( + attr="volume", + rank=0, + filter_attr="wet to critical volume ratio", + filter_range=self.__filter_range, + ) + return super()._impl(**kwargs) + + +@register_product() +class FrozenParticleSpecificConcentration(FrozenParticleConcentration): + def __init__(self, *, count_unactivated, count_activated, unit="kg^-1", name=None): + super().__init__( + unit=unit, + name=name, + count_activated=count_activated, + count_unactivated=count_unactivated, + specific=True, + ) diff --git a/PySDM/source/PySDM/products/freezing/ice_nuclei_concentration.py b/PySDM/source/PySDM/products/freezing/ice_nuclei_concentration.py new file mode 100644 index 0000000000000000000000000000000000000000..d5683a2c1db1aec6df2f15df2afefbfca93575fe --- /dev/null +++ b/PySDM/source/PySDM/products/freezing/ice_nuclei_concentration.py @@ -0,0 +1,43 @@ +""" +immersed ice nucleus concentration (both within frozen and unfrozen particles) +""" + +import numpy as np + +from PySDM.products.impl import ConcentrationProduct, register_product + + +@register_product() +class IceNucleiConcentration(ConcentrationProduct): + def __init__(self, unit="m^-3", name=None, __specific=False, stp=False): + super().__init__(unit=unit, name=name, specific=__specific, stp=stp) + self.__nonzero_filter_range = ( + np.finfo(float).tiny, # pylint: disable=no-member + np.inf, + ) + self.__filter_attr = None + + def register(self, builder): + super().register(builder) + singular = ( + builder.particulator.dynamics["Freezing"].immersion_freezing == "singular" + ) + self.__filter_attr = { + True: "freezing temperature", + False: "immersed surface area", + }[singular] + + def _impl(self, **kwargs): + self._download_moment_to_buffer( + attr="volume", + rank=0, + filter_attr=self.__filter_attr, + filter_range=self.__nonzero_filter_range, + ) + return super()._impl(**kwargs) + + +@register_product() +class SpecificIceNucleiConcentration(IceNucleiConcentration): + def __init__(self, unit="kg^-1", name=None, __specific=True): + super().__init__(unit=unit, name=name) diff --git a/PySDM/source/PySDM/products/freezing/total_unfrozen_immersed_surface_area.py b/PySDM/source/PySDM/products/freezing/total_unfrozen_immersed_surface_area.py new file mode 100644 index 0000000000000000000000000000000000000000..ab430eb56cd7c5dc6e96b50d2b2ba9e0105100aa --- /dev/null +++ b/PySDM/source/PySDM/products/freezing/total_unfrozen_immersed_surface_area.py @@ -0,0 +1,26 @@ +""" +mean immersed surface area per particle for unfrozen particles +""" + +import numpy as np + +from PySDM.products.impl import MomentProduct, register_product + + +@register_product() +class TotalUnfrozenImmersedSurfaceArea(MomentProduct): + def __init__(self, unit="m^2", name=None): + super().__init__(unit=unit, name=name) + + def _impl(self, **kwargs): + params = { + "attr": "immersed surface area", + "filter_attr": "volume", + "filter_range": (0, np.inf), + } + self._download_moment_to_buffer(**params, rank=1) + result = np.copy(self.buffer) + self._download_moment_to_buffer(**params, rank=0) + result[:] *= self.buffer + # TODO #599 per volume / per gridbox ? + return result diff --git a/PySDM/source/PySDM/products/housekeeping/__init__.py b/PySDM/source/PySDM/products/housekeeping/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e82a508c42a80ba7cec949aeed59dfd113f894df --- /dev/null +++ b/PySDM/source/PySDM/products/housekeeping/__init__.py @@ -0,0 +1,8 @@ +""" +Housekeeping products: time, super-particle count, wall-time timers... +""" + +from .dynamic_wall_time import DynamicWallTime +from .super_droplet_count_per_gridbox import SuperDropletCountPerGridbox +from .time import Time +from .timers import CPUTime, WallTime diff --git a/PySDM/source/PySDM/products/housekeeping/dynamic_wall_time.py b/PySDM/source/PySDM/products/housekeeping/dynamic_wall_time.py new file mode 100644 index 0000000000000000000000000000000000000000..ea9719463e7c5408ee9ed1216766a25c6388fd9e --- /dev/null +++ b/PySDM/source/PySDM/products/housekeeping/dynamic_wall_time.py @@ -0,0 +1,26 @@ +""" +wall-time for a given dynamic (fetching a value resets the counter) +""" + +from PySDM.products.impl import Product, register_product + + +@register_product() +class DynamicWallTime(Product): + def __init__(self, dynamic, name=None, unit="s"): + super().__init__(name=name, unit=unit) + self.value = 0 + self.dynamic = dynamic + + def register(self, builder): + super().register(builder) + self.particulator.observers.append(self) + self.shape = () + + def _impl(self, **kwargs): + tmp = self.value + self.value = 0 + return tmp + + def notify(self): + self.value += self.particulator.timers[self.dynamic].time diff --git a/PySDM/source/PySDM/products/housekeeping/super_droplet_count_per_gridbox.py b/PySDM/source/PySDM/products/housekeeping/super_droplet_count_per_gridbox.py new file mode 100644 index 0000000000000000000000000000000000000000..e9bc82ec647f83f13a78ab605f3dbeeb4d6f36f4 --- /dev/null +++ b/PySDM/source/PySDM/products/housekeeping/super_droplet_count_per_gridbox.py @@ -0,0 +1,33 @@ +""" +super-droplet count per gridbox (dimensionless) +""" + +import numba + +from PySDM.backends.impl_numba.conf import JIT_FLAGS +from PySDM.products.impl import Product, register_product + + +@register_product() +class SuperDropletCountPerGridbox(Product): + def __init__(self, unit="dimensionless", name=None): + super().__init__(unit=unit, name=name) + self._jit_impl = None + + def register(self, builder): + super().register(builder) + + @numba.njit(**{**JIT_FLAGS, "fastmath": builder.formulae.fastmath}) + def jit_impl(cell_start, ravelled_buffer): + n_cell = cell_start.shape[0] - 1 + for i in numba.prange(n_cell): # pylint: disable=not-an-iterable + ravelled_buffer[i] = cell_start[i + 1] - cell_start[i] + + self._jit_impl = jit_impl + + def _impl(self, **kwargs): + self._jit_impl( + cell_start=self.particulator.attributes.cell_start.to_ndarray(), + ravelled_buffer=self.buffer.ravel(), + ) + return self.buffer diff --git a/PySDM/source/PySDM/products/housekeeping/time.py b/PySDM/source/PySDM/products/housekeeping/time.py new file mode 100644 index 0000000000000000000000000000000000000000..d0febc7b307c8c062703d9ec139b818e2f5a74a2 --- /dev/null +++ b/PySDM/source/PySDM/products/housekeeping/time.py @@ -0,0 +1,22 @@ +""" +physical time (in dt increments) +""" + +from PySDM.products.impl import Product, register_product + + +@register_product() +class Time(Product): + def __init__(self, name=None, unit="s"): + super().__init__(name=name, unit=unit) + self.t = 0 + + def register(self, builder): + super().register(builder) + self.particulator.observers.append(self) + + def _impl(self, **kwargs): + return self.t + + def notify(self): + self.t += self.particulator.dt diff --git a/PySDM/source/PySDM/products/housekeeping/timers.py b/PySDM/source/PySDM/products/housekeeping/timers.py new file mode 100644 index 0000000000000000000000000000000000000000..ffe32ebef19d5cfb7c55c873b1908d91ddcb06fc --- /dev/null +++ b/PySDM/source/PySDM/products/housekeeping/timers.py @@ -0,0 +1,53 @@ +""" +CPU- and wall-time counters (fetching a value resets the counter) +""" + +import time +from abc import abstractmethod + +from PySDM.products.impl import Product, register_product + + +class _Timer(Product): + def __init__(self, name, unit): + super().__init__(name=name, unit=unit) + self._time = -1 + self.reset() + + def reset(self): + self._time = self.clock() + + def register(self, builder): + super().register(builder) + self.shape = () + + def _impl(self, **kwargs) -> float: + result = -self._time + self.reset() + result += self._time + return result + + @staticmethod + @abstractmethod + def clock(): + raise NotImplementedError() + + +@register_product() +class CPUTime(_Timer): + def __init__(self, name="CPU Time", unit="s"): + super().__init__(unit=unit, name=name) + + @staticmethod + def clock(): + return time.process_time() + + +@register_product() +class WallTime(_Timer): + def __init__(self, name=None, unit="s"): + super().__init__(unit=unit, name=name) + + @staticmethod + def clock(): + return time.perf_counter() diff --git a/PySDM/source/PySDM/products/impl/__init__.py b/PySDM/source/PySDM/products/impl/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..78668d868920d04222a4ab091800683c58f6e0bb --- /dev/null +++ b/PySDM/source/PySDM/products/impl/__init__.py @@ -0,0 +1,10 @@ +"""commons, not intended to be imported from user code""" + +from PySDM.products.impl.moist_environment_product import MoistEnvironmentProduct +from PySDM.products.impl.moment_product import MomentProduct +from PySDM.products.impl.product import Product +from PySDM.products.impl.rate_product import RateProduct +from PySDM.products.impl.spectrum_moment_product import SpectrumMomentProduct +from PySDM.products.impl.concentration_product import ConcentrationProduct +from PySDM.products.impl.activation_filtered_product import ActivationFilteredProduct +from PySDM.products.impl.register_product import register_product diff --git a/PySDM/source/PySDM/products/impl/activation_filtered_product.py b/PySDM/source/PySDM/products/impl/activation_filtered_product.py new file mode 100644 index 0000000000000000000000000000000000000000..6d9b9dc28f6f37e2bd40779f78e414f5f6510743 --- /dev/null +++ b/PySDM/source/PySDM/products/impl/activation_filtered_product.py @@ -0,0 +1,31 @@ +""" +common base class for products filtering droplets based on their activation state +""" + +import numpy as np + + +class ActivationFilteredProduct: + def __init__( + self, + *, + count_unactivated: bool, + count_activated: bool, + ): + self.__filter_attr = "wet to critical volume ratio" + self.__filter_range = [0, np.inf] + if not count_activated: + self.__filter_range[1] = 1 + if not count_unactivated: + self.__filter_range[0] = 1 + + def impl(self, *, attr, rank): + getattr(self, "_download_moment_to_buffer")( + attr=attr, + rank=rank, + filter_attr=self.__filter_attr, + filter_range=self.__filter_range, + ) + + def register(self, builder): + builder.request_attribute(self.__filter_attr) diff --git a/PySDM/source/PySDM/products/impl/concentration_product.py b/PySDM/source/PySDM/products/impl/concentration_product.py new file mode 100644 index 0000000000000000000000000000000000000000..a75faf0248ea466f5801637bf69438520ee425c9 --- /dev/null +++ b/PySDM/source/PySDM/products/impl/concentration_product.py @@ -0,0 +1,46 @@ +""" +common code for products computing particle concentrations with +the option to return them at standard temperature and pressure (STP) conditions +""" + +from PySDM.products.impl.moment_product import MomentProduct + + +class ConcentrationProduct(MomentProduct): + @staticmethod + def check_ctor_arguments(specific, stp): + if stp and specific: + raise ValueError( + "std-temperature-and-pressure precludes specific conc. option" + ) + + def __init__(self, *, unit: str, name: str, specific: bool, stp: bool): + """ + `stp` toggles expressing the concentration in terms of standard temperature + and pressure conditions (ground level of the ICAO standard atmosphere, zero humidity) + """ + self.check_ctor_arguments(specific, stp) + super().__init__(unit=unit, name=name) + self.specific = specific + self.stp = stp + self.rho_stp = None + + def register(self, builder): + super().register(builder) + self.rho_stp = builder.formulae.constants.rho_STP + + def _impl(self, **kwargs): + assert len(kwargs) == 0 + + self.buffer[:] /= self.particulator.mesh.dv + + if self.specific or self.stp: + result = self.buffer.copy() + self._download_to_buffer(self.particulator.environment["rhod"]) + result[:] /= self.buffer + if self.stp: + result[:] *= self.rho_stp + else: + result = self.buffer + + return result diff --git a/PySDM/source/PySDM/products/impl/moist_environment_product.py b/PySDM/source/PySDM/products/impl/moist_environment_product.py new file mode 100644 index 0000000000000000000000000000000000000000..9c64faf6748430178185961583dc695d275dcccb --- /dev/null +++ b/PySDM/source/PySDM/products/impl/moist_environment_product.py @@ -0,0 +1,28 @@ +""" +common code for products based on moist environment variables (e.g., ambient humidity) +""" + +from PySDM.environments.impl.moist import Moist +from PySDM.products.impl.product import Product + + +class MoistEnvironmentProduct(Product): + def __init__(self, *, name, unit, var=None): + super().__init__(name=name, unit=unit) + self.environment = None + self.source = None + self.var = var or name + + def register(self, builder): + super().register(builder) + self.particulator.observers.append(self) + self.environment = builder.particulator.environment + self.source = self.environment[self.var] + + def notify(self): + if isinstance(self.environment, Moist): + self.source = self.environment.get_predicted(self.var) + + def _impl(self, **kwargs): + self._download_to_buffer(self.source) + return self.buffer diff --git a/PySDM/source/PySDM/products/impl/moment_product.py b/PySDM/source/PySDM/products/impl/moment_product.py new file mode 100644 index 0000000000000000000000000000000000000000..9c5eccae72da543e8ff7e0b87f2a4ec5bcf8471d --- /dev/null +++ b/PySDM/source/PySDM/products/impl/moment_product.py @@ -0,0 +1,49 @@ +"""common code for products computing statistical moments (e.g., effective radius, acidity)""" + +from abc import ABC + +import numpy as np + +from PySDM.products.impl.product import Product + + +class MomentProduct(Product, ABC): + def __init__(self, name, unit): + super().__init__(name=name, unit=unit) + self.moment_0 = None + self.moments = None + + def register(self, builder): + super().register(builder) + self.moment_0 = self.particulator.Storage.empty( + self.particulator.mesh.n_cell, dtype=float + ) + self.moments = self.particulator.Storage.empty( + (1, self.particulator.mesh.n_cell), dtype=float + ) + + def _download_moment_to_buffer( + self, + *, + attr, + rank, + filter_attr="signed water mass", + filter_range=(-np.inf, np.inf), + weighting_attribute="water mass", + weighting_rank=0, + skip_division_by_m0=False, + ): + self.particulator.moments( + moment_0=self.moment_0, + moments=self.moments, + specs={attr: (rank,)}, + attr_name=filter_attr, + attr_range=filter_range, + weighting_attribute=weighting_attribute, + weighting_rank=weighting_rank, + skip_division_by_m0=skip_division_by_m0, + ) + if rank == 0: # TODO #217 + self._download_to_buffer(self.moment_0) + else: + self._download_to_buffer(self.moments[0, :]) diff --git a/PySDM/source/PySDM/products/impl/product.py b/PySDM/source/PySDM/products/impl/product.py new file mode 100644 index 0000000000000000000000000000000000000000..60f15b4383ec83e4f3fc088c14558bed64a65329 --- /dev/null +++ b/PySDM/source/PySDM/products/impl/product.py @@ -0,0 +1,101 @@ +""" +logic around the `PySDM.products.impl.product.Product` - parent class for all products +""" + +import inspect +from abc import abstractmethod +from typing import Optional + +import pint + +from PySDM.physics.constants import PPB, PPM, PPT +from PySDM.impl.camel_case import camel_case_to_words + +_UNIT_REGISTRY = pint.UnitRegistry() + + +class Product: + def __init__(self, *, unit: str, name: Optional[str] = None): + self.name = name or camel_case_to_words(self.__class__.__name__) + + self._unit = self._parse_unit(unit) + self.unit_magnitude_in_base_units = self._unit.to_base_units().magnitude + self.__check_unit() + + self.shape = None + self.buffer = None + self.particulator = None + self.formulae = None + + def register(self, builder): + self.particulator = builder.particulator + self.formulae = self.particulator.formulae + self.shape = self.particulator.mesh.grid + + def set_buffer(self, buffer): + self.buffer = buffer + + def _download_to_buffer(self, storage): + storage.download(self.buffer.ravel()) + + @staticmethod + def _parse_unit(unit: str): + if unit in ("%", "percent"): + return 0.01 * _UNIT_REGISTRY.dimensionless + if unit in ("PPB", "ppb"): + return PPB * _UNIT_REGISTRY.dimensionless + if unit in ("PPM", "ppm"): + return PPM * _UNIT_REGISTRY.dimensionless + if unit in ("PPT", "ppt"): + return PPT * _UNIT_REGISTRY.dimensionless + return _UNIT_REGISTRY.parse_expression(unit) + + def __check_unit(self): + init = inspect.signature(self.__init__) + if "unit" not in init.parameters: + raise AssertionError( + f"method __init__ of class {type(self).__name__}" + f" is expected to have a unit parameter" + ) + + default_unit_arg = init.parameters["unit"].default + + if ( + default_unit_arg is inspect._empty + or default_unit_arg is None + or str(default_unit_arg).strip() == "" + ): + raise AssertionError( + f"unit parameter of {type(self).__name__}.__init__" + f" is expected to have a non-empty default value" + ) + + default_unit = self._parse_unit(default_unit_arg) + + if default_unit.to_base_units().magnitude != 1: + raise AssertionError( + f'default value "{default_unit_arg}"' + f" of {type(self).__name__}.__init__() unit parameter" + f" is not a base SI unit" + ) + + if self._unit.dimensionality != default_unit.dimensionality: + raise AssertionError( + f"provided unit ({self._unit}) has different dimensionality" + f" ({self._unit.dimensionality}) than the default one" + f" ({default_unit.dimensionality})" + f" for product {type(self).__name__}" + ) + + @property + def unit(self): + return str(self._unit) + + @abstractmethod + def _impl(self, **kwargs): + raise NotImplementedError() + + def get(self, **kwargs): + result = self._impl(**kwargs) + result /= self.unit_magnitude_in_base_units + return result diff --git a/PySDM/source/PySDM/products/impl/rate_product.py b/PySDM/source/PySDM/products/impl/rate_product.py new file mode 100644 index 0000000000000000000000000000000000000000..2931aa878b99a8d89a7f3a6d2d07ccf7fde51ec3 --- /dev/null +++ b/PySDM/source/PySDM/products/impl/rate_product.py @@ -0,0 +1,35 @@ +""" +common code for products representing event rates +""" + +from PySDM.products.impl.product import Product + + +class RateProduct(Product): + def __init__(self, name, unit, counter, dynamic): + super().__init__(name=name, unit=unit) + self.timestep_count = 0 + self.counter = counter + self.dynamic = dynamic + + def register(self, builder): + super().register(builder) + self.counter = getattr(self.particulator.dynamics[self.dynamic], self.counter) + self.dynamic = None + self.particulator.observers.append(self) + + def notify(self): + self.timestep_count += 1 + + def _impl(self, **kwargs): + self._download_to_buffer(self.counter) + result = self.buffer.copy() + if self.timestep_count != 0: + result[:] /= ( + self.timestep_count * self.particulator.dt * self.particulator.mesh.dv + ) + self.timestep_count = 0 + self._download_to_buffer(self.particulator.environment["rhod"]) + result[:] /= self.buffer + self.counter[:] = 0 + return result diff --git a/PySDM/source/PySDM/products/impl/register_product.py b/PySDM/source/PySDM/products/impl/register_product.py new file mode 100644 index 0000000000000000000000000000000000000000..3278b4a1533d7d7e8e7c8af599ece14968c98c13 --- /dev/null +++ b/PySDM/source/PySDM/products/impl/register_product.py @@ -0,0 +1,22 @@ +"""decorator for product classes +ensuring that their instances can be re-used with multiple builders""" + +from copy import deepcopy + + +def _instantiate(self, *, builder, buffer): + copy = deepcopy(self) + copy.set_buffer(buffer) + copy.register(builder) + return copy + + +def register_product(): + def decorator(cls): + if hasattr(cls, "instantiate"): + assert cls.instantiate is _instantiate + else: + setattr(cls, "instantiate", _instantiate) + return cls + + return decorator diff --git a/PySDM/source/PySDM/products/impl/spectrum_moment_product.py b/PySDM/source/PySDM/products/impl/spectrum_moment_product.py new file mode 100644 index 0000000000000000000000000000000000000000..b02fe781d5241830595845c2803045112f064b2c --- /dev/null +++ b/PySDM/source/PySDM/products/impl/spectrum_moment_product.py @@ -0,0 +1,53 @@ +""" +common code for products computing **binned** statistical moments + (e.g., dry radius spectrum in each grid cell) +""" + +from abc import ABC + +from PySDM.products.impl.product import Product + + +class SpectrumMomentProduct(ABC, Product): + def __init__(self, name, unit, attr_unit): + super().__init__(name=name, unit=unit) + self.attr_bins_edges = None + self.attr_unit = attr_unit + self.moment_0 = None + self.moments = None + + def register(self, builder): + super().register(builder) + self.moment_0 = self.particulator.Storage.empty( + (len(self.attr_bins_edges) - 1, self.particulator.mesh.n_cell), dtype=float + ) + self.moments = self.particulator.Storage.empty( + (len(self.attr_bins_edges) - 1, self.particulator.mesh.n_cell), dtype=float + ) + _ = self._parse_unit(self.attr_unit) + + def _recalculate_spectrum_moment( + self, + *, + attr, + rank, + filter_attr="volume", + weighting_attribute="volume", + weighting_rank=0, + ): + self.particulator.spectrum_moments( + moment_0=self.moment_0, + moments=self.moments, + attr=attr, + rank=rank, + attr_bins=self.attr_bins_edges, + attr_name=filter_attr, + weighting_attribute=weighting_attribute, + weighting_rank=weighting_rank, + ) + + def _download_spectrum_moment_to_buffer(self, rank, bin_number): + if rank == 0: # TODO #217 + self._download_to_buffer(self.moment_0[bin_number, :]) + else: + self._download_to_buffer(self.moments[bin_number, :]) diff --git a/PySDM/source/PySDM/products/optical/__init__.py b/PySDM/source/PySDM/products/optical/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..051f6d0bb4fe1a50beeec0b71d15d6804cdf85dd --- /dev/null +++ b/PySDM/source/PySDM/products/optical/__init__.py @@ -0,0 +1,4 @@ +"""cloud optical properties""" + +from .cloud_optical_depth import CloudOpticalDepth +from .cloud_albedo import CloudAlbedo diff --git a/PySDM/source/PySDM/products/optical/cloud_albedo.py b/PySDM/source/PySDM/products/optical/cloud_albedo.py new file mode 100644 index 0000000000000000000000000000000000000000..498988d11bf690edf66ec01ec7af3bac2b8401e5 --- /dev/null +++ b/PySDM/source/PySDM/products/optical/cloud_albedo.py @@ -0,0 +1,14 @@ +""" +cloud albedo +""" + +from PySDM.products.impl import Product, register_product + + +@register_product() +class CloudAlbedo(Product): + def __init__(self, *, unit="dimensionless", name=None): + super().__init__(name=name, unit=unit) + + def _impl(self, **kwargs): + return self.formulae.optical_albedo.albedo(kwargs["optical_depth"]) diff --git a/PySDM/source/PySDM/products/optical/cloud_optical_depth.py b/PySDM/source/PySDM/products/optical/cloud_optical_depth.py new file mode 100644 index 0000000000000000000000000000000000000000..9f0773c2b3941c522cb603cdf14232eba8549f54 --- /dev/null +++ b/PySDM/source/PySDM/products/optical/cloud_optical_depth.py @@ -0,0 +1,17 @@ +""" +cloud optical depth +""" + +from PySDM.products.impl import Product, register_product + + +@register_product() +class CloudOpticalDepth(Product): + def __init__(self, *, unit="dimensionless", name=None): + super().__init__(name=name, unit=unit) + + def _impl(self, **kwargs): + return self.formulae.optical_depth.tau( + kwargs["liquid_water_path"], + kwargs["effective_radius"], + ) diff --git a/PySDM/source/PySDM/products/parcel/__init__.py b/PySDM/source/PySDM/products/parcel/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..925670630546b0605118ffad5a52e98861d07f7f --- /dev/null +++ b/PySDM/source/PySDM/products/parcel/__init__.py @@ -0,0 +1,4 @@ +"""products specific to the parcel environment""" + +from .cloud_water_path import ParcelLiquidWaterPath +from .parcel_displacement import ParcelDisplacement diff --git a/PySDM/source/PySDM/products/parcel/cloud_water_path.py b/PySDM/source/PySDM/products/parcel/cloud_water_path.py new file mode 100644 index 0000000000000000000000000000000000000000..3408464c530a7a33441bcfe63ce2d99b24b95727 --- /dev/null +++ b/PySDM/source/PySDM/products/parcel/cloud_water_path.py @@ -0,0 +1,59 @@ +""" +cloud water path integrated over parcel displacement taking into account changes +in parcel volume along the way +""" + +from PySDM.environments.parcel import Parcel + +from PySDM.products.impl import ( + ActivationFilteredProduct, + MomentProduct, + register_product, +) + + +@register_product() +class ParcelLiquidWaterPath(MomentProduct, ActivationFilteredProduct): + def __init__( + self, + count_unactivated: bool, + count_activated: bool, + name=None, + unit="kg/m^2", + ): + MomentProduct.__init__(self, unit=unit, name=name) + ActivationFilteredProduct.__init__( + self, count_activated=count_activated, count_unactivated=count_unactivated + ) + self.previous = {"z": 0.0, "cwc": 0.0} + self.cwp = 0.0 + + def register(self, builder): + if not isinstance(builder.particulator.environment, Parcel): + raise NotImplementedError() + ActivationFilteredProduct.register(self, builder) + MomentProduct.register(self, builder) + self.particulator.observers.append(self) + + def notify(self): + ActivationFilteredProduct.impl(self, attr="water mass", rank=1) + avg_mass = self.buffer.copy() + + ActivationFilteredProduct.impl(self, attr="water mass", rank=0) + tot_numb = self.buffer.copy() + + self._download_to_buffer(self.particulator.environment["z"]) + current_z = self.buffer.copy() + + cwc = avg_mass * tot_numb / self.particulator.mesh.dv + dz = current_z - self.previous["z"] + cwc_mean = (cwc + self.previous["cwc"]) / 2 + + if self.previous["cwc"] > 0: + self.cwp += cwc_mean * dz + + self.previous["z"] = current_z + self.previous["cwc"] = cwc + + def _impl(self, **kwargs): + return self.cwp diff --git a/PySDM/source/PySDM/products/parcel/parcel_displacement.py b/PySDM/source/PySDM/products/parcel/parcel_displacement.py new file mode 100644 index 0000000000000000000000000000000000000000..73cede025cf9f2aacaa58845233930e19651926e --- /dev/null +++ b/PySDM/source/PySDM/products/parcel/parcel_displacement.py @@ -0,0 +1,22 @@ +""" +parcel displacement, for use with `PySDM.environments.parcel.Parcel` environment only +""" + +from PySDM.environments import Parcel +from PySDM.products.impl import Product, register_product + + +@register_product() +class ParcelDisplacement(Product): + def __init__(self, unit="m", name=None): + super().__init__(unit=unit, name=name) + self.environment = None + + def register(self, builder): + super().register(builder) + assert isinstance(builder.particulator.environment, Parcel) + self.environment = builder.particulator.environment + + def _impl(self, **kwargs): + self._download_to_buffer(self.environment["z"]) + return self.buffer diff --git a/PySDM/source/PySDM/products/size_spectral/__init__.py b/PySDM/source/PySDM/products/size_spectral/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d8cbc86b0a0116da4e399a733eb2dd2d9520d799 --- /dev/null +++ b/PySDM/source/PySDM/products/size_spectral/__init__.py @@ -0,0 +1,46 @@ +"""products expressing particle size-spectral quantities""" + +from .arbitrary_moment import ( + RadiusFirstMoment, + RadiusSixthMoment, + VolumeFirstMoment, + VolumeSecondMoment, + ZerothMoment, +) +from .effective_radius import EffectiveRadius +from .effective_radius_activated import ActivatedEffectiveRadius +from .mean_radius import MeanRadius +from .mean_radius_activated import ActivatedMeanRadius +from .mean_volume_radius import MeanVolumeRadius +from .number_size_spectrum import NumberSizeSpectrum +from .particle_concentration import ParticleConcentration, ParticleSpecificConcentration +from .particle_concentration_activated import ( + ActivatedParticleConcentration, + ActivatedParticleSpecificConcentration, +) +from .particle_size_spectrum import ( + ParticleSizeSpectrumPerMassOfDryAir, + ParticleSizeSpectrumPerVolume, +) +from .particle_volume_versus_radius_logarithm_spectrum import ( + ParticleVolumeVersusRadiusLogarithmSpectrum, +) +from .radius_binned_number_averaged_terminal_velocity import ( + RadiusBinnedNumberAveragedTerminalVelocity, +) +from .size_standard_deviation import ( + AreaStandardDeviation, + RadiusStandardDeviation, + VolumeStandardDeviation, +) +from .total_particle_concentration import TotalParticleConcentration +from .total_particle_specific_concentration import TotalParticleSpecificConcentration +from .water_mixing_ratio import WaterMixingRatio +from .cloud_water_content import ( + CloudWaterContent, + SpecificCloudWaterContent, + LiquidWaterContent, + SpecificLiquidWaterContent, + IceWaterContent, + SpecificIceWaterContent, +) diff --git a/PySDM/source/PySDM/products/size_spectral/arbitrary_moment.py b/PySDM/source/PySDM/products/size_spectral/arbitrary_moment.py new file mode 100644 index 0000000000000000000000000000000000000000..aa3761558a45bba69a2ddd9da590524b59358c0d --- /dev/null +++ b/PySDM/source/PySDM/products/size_spectral/arbitrary_moment.py @@ -0,0 +1,79 @@ +""" +factory for arbitrary-moment product classes +""" + +from PySDM.products.impl import MomentProduct, register_product + + +def make_arbitrary_moment_product(**kwargs): + """returns a product class to be instantiated and passed to a builder""" + for arg in kwargs: + assert arg in ( + "rank", + "attr", + "attr_unit", + "skip_division_by_m0", + "skip_division_by_dv", + ) + + class ArbitraryMoment(MomentProduct): + def __init__( + self, + name=None, + unit=f"({kwargs['attr_unit']})**{kwargs['rank']}" + + ("" if kwargs["skip_division_by_dv"] else " / m**3"), + ): + super().__init__(name=name, unit=unit) + + def _impl(self, **_): + self._download_moment_to_buffer( + attr=kwargs["attr"], + rank=kwargs["rank"], + skip_division_by_m0=kwargs["skip_division_by_m0"], + ) + if not kwargs["skip_division_by_dv"]: + self.buffer /= self.particulator.mesh.dv + return self.buffer + + return register_product()(ArbitraryMoment) + + +ZerothMoment = make_arbitrary_moment_product( + rank=0, + attr="volume", + attr_unit="m^3", + skip_division_by_m0=True, + skip_division_by_dv=True, +) + +VolumeFirstMoment = make_arbitrary_moment_product( + rank=1, + attr="volume", + attr_unit="m^3", + skip_division_by_m0=True, + skip_division_by_dv=True, +) + +VolumeSecondMoment = make_arbitrary_moment_product( + rank=2, + attr="volume", + attr_unit="m^3", + skip_division_by_m0=True, + skip_division_by_dv=True, +) + +RadiusSixthMoment = make_arbitrary_moment_product( + rank=6, + attr="radius", + attr_unit="m", + skip_division_by_m0=True, + skip_division_by_dv=True, +) + +RadiusFirstMoment = make_arbitrary_moment_product( + rank=1, + attr="radius", + attr_unit="m", + skip_division_by_m0=True, + skip_division_by_dv=True, +) diff --git a/PySDM/source/PySDM/products/size_spectral/cloud_water_content.py b/PySDM/source/PySDM/products/size_spectral/cloud_water_content.py new file mode 100644 index 0000000000000000000000000000000000000000..2658f6d5f56a636964269e3bc9cc9e581526996e --- /dev/null +++ b/PySDM/source/PySDM/products/size_spectral/cloud_water_content.py @@ -0,0 +1,90 @@ +""" +cloud water content products +if `specific=True`, reports values per mass of dry air, otherwise per volume + +CloudWaterContent is both liquid and ice +LiquidWaterContent is just liquid +IceWaterContent is just ice +""" + +import numpy as np + +from PySDM.products.impl import MomentProduct, register_product + + +@register_product() +class CloudWaterContent(MomentProduct): + def __init__( + self, unit="kg/m^3", name=None, specific=False, liquid=True, ice=True + ): # pylint: disable=too-many-arguments + super().__init__(unit=unit, name=name) + self.specific = specific + self.liquid = liquid + self.ice = ice + + def _impl(self, **kwargs): + cwc = 0.0 + if self.liquid: + self._download_moment_to_buffer( + attr="water mass", rank=1, filter_range=(0, np.inf) + ) + mass = self.buffer.copy() + + self._download_moment_to_buffer( + attr="water mass", rank=0, filter_range=(0, np.inf) + ) + number = self.buffer + cwc += mass * number / self.particulator.mesh.dv + + if self.ice: + self._download_moment_to_buffer( + attr="water mass", + rank=1, + filter_range=(-np.inf, 0), + filter_attr="signed water mass", + ) + mass = self.buffer.copy() + + self._download_moment_to_buffer( + attr="water mass", + rank=0, + filter_range=(-np.inf, 0), + filter_attr="signed water mass", + ) + number = self.buffer + cwc += mass * number / self.particulator.mesh.dv + + if self.specific: + self._download_to_buffer(self.particulator.environment["rhod"]) + cwc /= self.buffer + return cwc + + +@register_product() +class SpecificCloudWaterContent(CloudWaterContent): + def __init__(self, unit="kg/kg", name=None): + super().__init__(unit=unit, name=name, specific=True, liquid=True, ice=True) + + +@register_product() +class LiquidWaterContent(CloudWaterContent): + def __init__(self, unit="kg/m^3", name=None): + super().__init__(unit=unit, name=name, specific=False, liquid=True, ice=False) + + +@register_product() +class SpecificLiquidWaterContent(CloudWaterContent): + def __init__(self, unit="kg/kg", name=None): + super().__init__(unit=unit, name=name, specific=True, liquid=True, ice=False) + + +@register_product() +class IceWaterContent(CloudWaterContent): + def __init__(self, unit="kg/m^3", name=None): + super().__init__(unit=unit, name=name, specific=False, liquid=False, ice=True) + + +@register_product() +class SpecificIceWaterContent(CloudWaterContent): + def __init__(self, unit="kg/kg", name=None): + super().__init__(unit=unit, name=name, specific=True, liquid=False, ice=True) diff --git a/PySDM/source/PySDM/products/size_spectral/effective_radius.py b/PySDM/source/PySDM/products/size_spectral/effective_radius.py new file mode 100644 index 0000000000000000000000000000000000000000..3ed603ce64fa22037f97f2713b5e76a7bcd2fbbc --- /dev/null +++ b/PySDM/source/PySDM/products/size_spectral/effective_radius.py @@ -0,0 +1,59 @@ +""" +effective radius of particles within a grid cell (ratio of third to second moments, + optionally restricted to a given size range) +""" + +import numba +import numpy as np + +from PySDM.backends.impl_numba.conf import JIT_FLAGS +from PySDM.physics import constants as const +from PySDM.products.impl import MomentProduct, register_product + +GEOM_FACTOR = const.PI_4_3 ** (-1 / 3) + + +@register_product() +class EffectiveRadius(MomentProduct): + def __init__(self, *, radius_range=None, unit="m", name=None): + super().__init__(name=name, unit=unit) + self.volume_range = None + self.radius_range = radius_range or (0, np.inf) + + def register(self, builder): + super().register(builder) + self.volume_range = self.formulae.trivia.volume(np.asarray(self.radius_range)) + + @staticmethod + @numba.njit(**JIT_FLAGS) + def nan_aware_reff_impl(input_volume_output_reff, volume_2_3): + """computes the effective radius (/) based on and """ + input_volume_output_reff[:] = np.where( + volume_2_3[:] > 0, + input_volume_output_reff[:] + * GEOM_FACTOR + / ( + volume_2_3[:] + (volume_2_3[:] == 0) + ), # (+ x==0) to avoid div-by-zero warnings + np.nan, + ) + + def _impl(self, **kwargs): + tmp = np.empty_like(self.buffer) + self._download_moment_to_buffer( + attr="volume", + rank=2 / 3, + filter_range=self.volume_range, + filter_attr="volume", + ) + tmp[:] = self.buffer[:] + self._download_moment_to_buffer( + attr="volume", + rank=1, + filter_range=self.volume_range, + filter_attr="volume", + ) + EffectiveRadius.nan_aware_reff_impl( + input_volume_output_reff=self.buffer, volume_2_3=tmp + ) + return self.buffer diff --git a/PySDM/source/PySDM/products/size_spectral/effective_radius_activated.py b/PySDM/source/PySDM/products/size_spectral/effective_radius_activated.py new file mode 100644 index 0000000000000000000000000000000000000000..cb2a26194057df05bf25aa5f64655d617058fc2b --- /dev/null +++ b/PySDM/source/PySDM/products/size_spectral/effective_radius_activated.py @@ -0,0 +1,45 @@ +""" +effective radius of particles within a grid cell, for activated, unactivated or both +""" + +import numpy as np + +from PySDM.products.impl import ( + ActivationFilteredProduct, + MomentProduct, + register_product, +) +from PySDM.products.size_spectral.effective_radius import EffectiveRadius + + +@register_product() +class ActivatedEffectiveRadius(MomentProduct, ActivationFilteredProduct): + def __init__( + self, *, count_unactivated: bool, count_activated: bool, name=None, unit="m" + ): + MomentProduct.__init__(self, name=name, unit=unit) + ActivationFilteredProduct.__init__( + self, count_activated=count_activated, count_unactivated=count_unactivated + ) + + def register(self, builder): + ActivationFilteredProduct.register(self, builder) + MomentProduct.register(self, builder) + + def _impl(self, **kwargs): + ActivationFilteredProduct.impl( + self, + attr="volume", + rank=2 / 3, + ) + tmp = np.empty_like(self.buffer) + tmp[:] = self.buffer[:] + ActivationFilteredProduct.impl( + self, + attr="volume", + rank=1, + ) + EffectiveRadius.nan_aware_reff_impl( + input_volume_output_reff=self.buffer, volume_2_3=tmp + ) + return self.buffer diff --git a/PySDM/source/PySDM/products/size_spectral/mean_radius.py b/PySDM/source/PySDM/products/size_spectral/mean_radius.py new file mode 100644 index 0000000000000000000000000000000000000000..73c3fa690256a1271f9cc31a1536068728d2a1a6 --- /dev/null +++ b/PySDM/source/PySDM/products/size_spectral/mean_radius.py @@ -0,0 +1,40 @@ +""" +mean radius of particles within a grid cell (optionally restricted to a given size range) +""" + +import numpy as np + +from PySDM.products.impl import MomentProduct, register_product + + +@register_product() +class MeanRadius(MomentProduct): + def __init__( + self, + name=None, + unit="m", + radius_range=(0, np.inf), + ): + self.radius_range = radius_range + super().__init__(name=name, unit=unit) + + def register(self, builder): + builder.request_attribute("volume") + super().register(builder) + + def _impl(self, **kwargs): + self._download_moment_to_buffer( + attr="volume", + rank=1 / 3, + filter_range=( + self.formulae.particle_shape_and_density.radius_to_mass( + self.radius_range[0] + ), + self.formulae.particle_shape_and_density.radius_to_mass( + self.radius_range[1] + ), + ), + filter_attr="signed water mass", + ) + self.buffer[:] /= self.formulae.constants.PI_4_3 ** (1 / 3) + return self.buffer diff --git a/PySDM/source/PySDM/products/size_spectral/mean_radius_activated.py b/PySDM/source/PySDM/products/size_spectral/mean_radius_activated.py new file mode 100644 index 0000000000000000000000000000000000000000..ba70c6ed56a9637abf41f0bc0ee6289728c4a708 --- /dev/null +++ b/PySDM/source/PySDM/products/size_spectral/mean_radius_activated.py @@ -0,0 +1,29 @@ +""" +mean radius of particles within a grid cell, for activated, unactivated or both +""" + +from PySDM.products.impl import ( + ActivationFilteredProduct, + MomentProduct, + register_product, +) + + +@register_product() +class ActivatedMeanRadius(MomentProduct, ActivationFilteredProduct): + def __init__( + self, count_unactivated: bool, count_activated: bool, name=None, unit="m" + ): + MomentProduct.__init__(self, name=name, unit=unit) + ActivationFilteredProduct.__init__( + self, count_activated=count_activated, count_unactivated=count_unactivated + ) + + def register(self, builder): + for base_class in (ActivationFilteredProduct, MomentProduct): + base_class.register(self, builder) + + def _impl(self, **kwargs): + ActivationFilteredProduct.impl(self, attr="volume", rank=1 / 3) + self.buffer[:] /= self.formulae.constants.PI_4_3 ** (1 / 3) + return self.buffer diff --git a/PySDM/source/PySDM/products/size_spectral/mean_volume_radius.py b/PySDM/source/PySDM/products/size_spectral/mean_volume_radius.py new file mode 100644 index 0000000000000000000000000000000000000000..6afb7b8eebf7c8b640a25e10c4c055c8af0890a9 --- /dev/null +++ b/PySDM/source/PySDM/products/size_spectral/mean_volume_radius.py @@ -0,0 +1,28 @@ +""" +mean volume radius of particles within a grid cell, for activated, unactivated or both +""" + +from PySDM.products.impl import ( + ActivationFilteredProduct, + MomentProduct, + register_product, +) + + +@register_product() +class MeanVolumeRadius(MomentProduct, ActivationFilteredProduct): + def __init__( + self, count_unactivated: bool, count_activated: bool, name=None, unit="m" + ): + MomentProduct.__init__(self, name=name, unit=unit) + ActivationFilteredProduct.__init__( + self, count_activated=count_activated, count_unactivated=count_unactivated + ) + + def register(self, builder): + for base_class in (ActivationFilteredProduct, MomentProduct): + base_class.register(self, builder) + + def _impl(self, **kwargs): + ActivationFilteredProduct.impl(self, attr="volume", rank=1) + return self.formulae.trivia.radius(self.buffer[:]) diff --git a/PySDM/source/PySDM/products/size_spectral/number_size_spectrum.py b/PySDM/source/PySDM/products/size_spectral/number_size_spectrum.py new file mode 100644 index 0000000000000000000000000000000000000000..a6be2c544b538df6be086940211b6724966fe7b7 --- /dev/null +++ b/PySDM/source/PySDM/products/size_spectral/number_size_spectrum.py @@ -0,0 +1,43 @@ +""" +n(V) particle volume spectrum per volume of air, +i.e. number of particles per volume of air having in the size range bin +""" + +import numpy as np + +from PySDM.products.impl import SpectrumMomentProduct, register_product + + +@register_product() +class NumberSizeSpectrum(SpectrumMomentProduct): + def __init__(self, radius_bins_edges, name=None, unit="m^-3"): + super().__init__(name=name, unit=unit, attr_unit="m") + self.radius_bins_edges = radius_bins_edges + self.moment_0 = None + self.moments = None + self.attr = "volume" + + def register(self, builder): + builder.request_attribute("volume") + + volume_bins_edges = builder.particulator.formulae.trivia.volume( + self.radius_bins_edges + ) + self.attr_bins_edges = builder.particulator.backend.Storage.from_ndarray( + volume_bins_edges + ) + + super().register(builder) + + self.shape = (*builder.particulator.mesh.grid, len(self.attr_bins_edges) - 1) + + def _impl(self, **kwargs): + vals = np.empty([self.particulator.mesh.n_cell, len(self.attr_bins_edges) - 1]) + self._recalculate_spectrum_moment(attr=self.attr, rank=1, filter_attr=self.attr) + + for i in range(vals.shape[1]): + self._download_spectrum_moment_to_buffer(rank=0, bin_number=i) + vals[:, i] = self.buffer.ravel() + + vals *= 1 / self.particulator.mesh.dv + return vals diff --git a/PySDM/source/PySDM/products/size_spectral/particle_concentration.py b/PySDM/source/PySDM/products/size_spectral/particle_concentration.py new file mode 100644 index 0000000000000000000000000000000000000000..d7616506fa67f0c4c2008a0755cf6080f7959a34 --- /dev/null +++ b/PySDM/source/PySDM/products/size_spectral/particle_concentration.py @@ -0,0 +1,44 @@ +""" +concentration of particles within a grid cell (either per-volume of per-mass-of-dry air, + optionally restricted to a given size range) +""" + +import numpy as np + +from PySDM.products.impl import ConcentrationProduct, register_product + + +@register_product() +class ParticleConcentration(ConcentrationProduct): + # pylint: disable=too-many-arguments + def __init__( + self, + radius_range=(0, np.inf), + specific=False, + stp=False, + name=None, + unit="m^-3", + ): + self.radius_range = radius_range + super().__init__(name=name, unit=unit, specific=specific, stp=stp) + + def _impl(self, **kwargs): + self._download_moment_to_buffer( + attr="water mass", + rank=0, + filter_range=( + self.formulae.particle_shape_and_density.volume_to_mass( + self.formulae.trivia.volume(radius=self.radius_range[0]) + ), + self.formulae.particle_shape_and_density.volume_to_mass( + self.formulae.trivia.volume(self.radius_range[1]) + ), + ), + ) + return super()._impl(**kwargs) + + +@register_product() +class ParticleSpecificConcentration(ParticleConcentration): + def __init__(self, radius_range=(0, np.inf), name=None, unit="kg^-1"): + super().__init__(radius_range=radius_range, specific=True, name=name, unit=unit) diff --git a/PySDM/source/PySDM/products/size_spectral/particle_concentration_activated.py b/PySDM/source/PySDM/products/size_spectral/particle_concentration_activated.py new file mode 100644 index 0000000000000000000000000000000000000000..8e607c0c8a33027125b728dee59c58d0ce5b7046 --- /dev/null +++ b/PySDM/source/PySDM/products/size_spectral/particle_concentration_activated.py @@ -0,0 +1,51 @@ +""" +concentration of particles within a grid cell (either per-volume of per-mass-of-dry air), + activated, unactivated or both +""" + +from PySDM.products.impl import ( + ActivationFilteredProduct, + ConcentrationProduct, + register_product, +) + + +@register_product() +class ActivatedParticleConcentration(ConcentrationProduct, ActivationFilteredProduct): + # pylint: disable=too-many-arguments + def __init__( + self, + *, + count_unactivated: bool, + count_activated: bool, + specific=False, + stp=False, + name=None, + unit="m^-3", + ): + ConcentrationProduct.__init__( + self, name=name, unit=unit, specific=specific, stp=stp + ) + ActivationFilteredProduct.__init__( + self, count_activated=count_activated, count_unactivated=count_unactivated + ) + + def register(self, builder): + for base_class in (ActivationFilteredProduct, ConcentrationProduct): + base_class.register(self, builder) + + def _impl(self, **kwargs): + ActivationFilteredProduct.impl(self, attr="volume", rank=0) + return ConcentrationProduct._impl(self, **kwargs) + + +@register_product() +class ActivatedParticleSpecificConcentration(ActivatedParticleConcentration): + def __init__(self, count_unactivated, count_activated, name=None, unit="kg^-1"): + super().__init__( + count_unactivated=count_unactivated, + count_activated=count_activated, + specific=True, + name=name, + unit=unit, + ) diff --git a/PySDM/source/PySDM/products/size_spectral/particle_size_spectrum.py b/PySDM/source/PySDM/products/size_spectral/particle_size_spectrum.py new file mode 100644 index 0000000000000000000000000000000000000000..6ce5ad90951f75991dcfad2df9f949067084004a --- /dev/null +++ b/PySDM/source/PySDM/products/size_spectral/particle_size_spectrum.py @@ -0,0 +1,110 @@ +""" +wet- or dry-radius binned particle size spectra (per mass of dry air or per volume of air) +""" + +from abc import ABC + +import numpy as np + +from PySDM.products.impl import ( + SpectrumMomentProduct, + ConcentrationProduct, + register_product, +) + + +class ParticleSizeSpectrum(SpectrumMomentProduct, ABC): + def __init__( + self, + *, + stp: bool, + radius_bins_edges, + name: str, + unit: str, + dry: bool = False, + specific: bool = False, + ): + ConcentrationProduct.check_ctor_arguments(specific, stp) + self.volume_attr = "dry volume" if dry else "volume" + self.radius_bins_edges = radius_bins_edges + self.specific = specific + self.stp = stp + self.rho_stp = None + super().__init__(name=name, unit=unit, attr_unit="m^3") + + def register(self, builder): + builder.request_attribute(self.volume_attr) + + volume_bins_edges = builder.particulator.formulae.trivia.volume( + np.asarray(self.radius_bins_edges) + ) + self.attr_bins_edges = builder.particulator.backend.Storage.from_ndarray( + volume_bins_edges + ) + + super().register(builder) + + self.shape = (*builder.particulator.mesh.grid, len(self.attr_bins_edges) - 1) + self.rho_stp = builder.formulae.constants.rho_STP + + def _impl(self, **kwargs): + vals = np.empty([self.particulator.mesh.n_cell, len(self.attr_bins_edges) - 1]) + self._recalculate_spectrum_moment( + attr=self.volume_attr, rank=1, filter_attr=self.volume_attr + ) + + for i in range(vals.shape[1]): + self._download_spectrum_moment_to_buffer(rank=0, bin_number=i) + vals[:, i] = self.buffer.ravel() + + vals[:] /= self.particulator.mesh.dv + + if self.specific or self.stp: + self._download_to_buffer(self.particulator.environment["rhod"]) + + for i in range(len(self.attr_bins_edges) - 1): + dr = self.formulae.trivia.radius( + volume=self.attr_bins_edges[i + 1] + ) - self.formulae.trivia.radius(volume=self.attr_bins_edges[i]) + vals[:, i] /= dr + if self.specific or self.stp: + vals[:, i] /= self.buffer.ravel() + if self.stp: + vals[:, i] *= self.rho_stp + + return np.squeeze(vals.reshape(self.shape)) + + +@register_product() +class ParticleSizeSpectrumPerMassOfDryAir(ParticleSizeSpectrum): + def __init__( + self, + *, + radius_bins_edges, + dry=False, + name=None, + unit="kg^-1 m^-1", + ): + super().__init__( + radius_bins_edges=radius_bins_edges, + dry=dry, + specific=True, + name=name, + unit=unit, + stp=False, + ) + + +@register_product() +class ParticleSizeSpectrumPerVolume(ParticleSizeSpectrum): + def __init__( + self, *, radius_bins_edges, dry=False, name=None, unit="m^-3 m^-1", stp=False + ): + super().__init__( + radius_bins_edges=radius_bins_edges, + dry=dry, + specific=False, + name=name, + unit=unit, + stp=stp, + ) diff --git a/PySDM/source/PySDM/products/size_spectral/particle_volume_versus_radius_logarithm_spectrum.py b/PySDM/source/PySDM/products/size_spectral/particle_volume_versus_radius_logarithm_spectrum.py new file mode 100644 index 0000000000000000000000000000000000000000..c140e43c6a120d062b15c5e93bef57e8b4ec5dc8 --- /dev/null +++ b/PySDM/source/PySDM/products/size_spectral/particle_volume_versus_radius_logarithm_spectrum.py @@ -0,0 +1,46 @@ +""" +n_V^e(ln(r)) particle volume spectrum per volume of air (uses natural logarithm), +i.e. volume of particles per volume of air having in the size range ln(r) to +ln(r) + dln(r) +""" + +import numpy as np + +from PySDM.products.impl import SpectrumMomentProduct, register_product + + +@register_product() +class ParticleVolumeVersusRadiusLogarithmSpectrum(SpectrumMomentProduct): + def __init__(self, radius_bins_edges, name=None, unit="dimensionless", dry=False): + super().__init__(name=name, unit=unit, attr_unit="m^3") + self.radius_bins_edges = radius_bins_edges + self.moment_0 = None + self.moments = None + self.attr = ("dry " if dry else "") + "volume" + + def register(self, builder): + builder.request_attribute("volume") + + volume_bins_edges = builder.particulator.formulae.trivia.volume( + self.radius_bins_edges + ) + self.attr_bins_edges = builder.particulator.backend.Storage.from_ndarray( + volume_bins_edges + ) + + super().register(builder) + + self.shape = (*builder.particulator.mesh.grid, len(self.attr_bins_edges) - 1) + + def _impl(self, **kwargs): + vals = np.empty([self.particulator.mesh.n_cell, len(self.attr_bins_edges) - 1]) + self._recalculate_spectrum_moment(attr=self.attr, rank=1, filter_attr=self.attr) + + for i in range(vals.shape[1]): + self._download_spectrum_moment_to_buffer(rank=1, bin_number=i) + vals[:, i] = self.buffer.ravel() + self._download_spectrum_moment_to_buffer(rank=0, bin_number=i) + vals[:, i] *= self.buffer.ravel() + + vals *= 1 / np.diff(np.log(self.radius_bins_edges)) / self.particulator.mesh.dv + return vals diff --git a/PySDM/source/PySDM/products/size_spectral/radius_binned_number_averaged_terminal_velocity.py b/PySDM/source/PySDM/products/size_spectral/radius_binned_number_averaged_terminal_velocity.py new file mode 100644 index 0000000000000000000000000000000000000000..b464f8176c6e507404e579dbe2364808937c0933 --- /dev/null +++ b/PySDM/source/PySDM/products/size_spectral/radius_binned_number_averaged_terminal_velocity.py @@ -0,0 +1,45 @@ +""" +Provides radius bin-resolved average terminal velocity (average is particle-number weighted) +""" + +import numpy as np + +from PySDM.products.impl import SpectrumMomentProduct, register_product + +ATTR = "terminal velocity" +RANK = 1 + + +@register_product() +class RadiusBinnedNumberAveragedTerminalVelocity(SpectrumMomentProduct): + def __init__(self, radius_bin_edges, name=None, unit="m/s"): + super().__init__(name=name, unit=unit, attr_unit="m") + self.radius_bin_edges = radius_bin_edges + + def register(self, builder): + builder.request_attribute(ATTR) + + volume_bin_edges = builder.particulator.formulae.trivia.volume( + self.radius_bin_edges + ) + self.attr_bins_edges = builder.particulator.backend.Storage.from_ndarray( + volume_bin_edges + ) + + super().register(builder) + + self.shape = (*builder.particulator.mesh.grid, len(self.attr_bins_edges) - 1) + + def _impl(self, **kwargs): + vals = np.empty([self.particulator.mesh.n_cell, len(self.attr_bins_edges) - 1]) + + self._recalculate_spectrum_moment( + attr=ATTR, + rank=RANK, + ) + + for i in range(vals.shape[1]): + self._download_spectrum_moment_to_buffer(rank=RANK, bin_number=i) + vals[:, i] = self.buffer.ravel() + + return np.squeeze(vals.reshape(self.shape)) diff --git a/PySDM/source/PySDM/products/size_spectral/size_standard_deviation.py b/PySDM/source/PySDM/products/size_spectral/size_standard_deviation.py new file mode 100644 index 0000000000000000000000000000000000000000..610f332b6f0419abbf62d59c64c0b297396946a6 --- /dev/null +++ b/PySDM/source/PySDM/products/size_spectral/size_standard_deviation.py @@ -0,0 +1,75 @@ +""" +standard deviation of radius/area/volume of particles within a grid cell, +for activated, unactivated or both +""" + +import numpy as np + +from PySDM.products.impl import ( + ActivationFilteredProduct, + MomentProduct, + register_product, +) + + +class _SizeStandardDeviation(MomentProduct, ActivationFilteredProduct): + # pylint: disable=too-many-arguments + def __init__( + self, + count_unactivated: bool, + count_activated: bool, + name=None, + unit="m", + attr="radius", + ): + self.attr = attr + MomentProduct.__init__(self, name=name, unit=unit) + ActivationFilteredProduct.__init__( + self, count_activated=count_activated, count_unactivated=count_unactivated + ) + self.tmp = None + + def register(self, builder): + builder.request_attribute(self.attr) + for base_class in (ActivationFilteredProduct, MomentProduct): + base_class.register(self, builder) + self.tmp = np.empty_like(self.buffer) + + def _impl(self, **kwargs): + ActivationFilteredProduct.impl(self, attr=self.attr, rank=1) + self.tmp[:] = -self.buffer**2 + ActivationFilteredProduct.impl(self, attr=self.attr, rank=2) + self.tmp[:] += self.buffer + self.tmp[:] = np.sqrt(self.tmp) + return self.tmp + + +RadiusStandardDeviation = register_product()(_SizeStandardDeviation) + + +@register_product() +class AreaStandardDeviation(_SizeStandardDeviation): + def __init__( + self, *, name=None, unit="m^2", count_activated: bool, count_unactivated: bool + ): + super().__init__( + name=name, + unit=unit, + count_activated=count_activated, + count_unactivated=count_unactivated, + attr="area", + ) + + +@register_product() +class VolumeStandardDeviation(_SizeStandardDeviation): + def __init__( + self, *, name=None, unit="m^3", count_activated: bool, count_unactivated: bool + ): + super().__init__( + name=name, + unit=unit, + count_activated=count_activated, + count_unactivated=count_unactivated, + attr="volume", + ) diff --git a/PySDM/source/PySDM/products/size_spectral/total_particle_concentration.py b/PySDM/source/PySDM/products/size_spectral/total_particle_concentration.py new file mode 100644 index 0000000000000000000000000000000000000000..6d40816ec6c1cc677a380386202e54f729d50df3 --- /dev/null +++ b/PySDM/source/PySDM/products/size_spectral/total_particle_concentration.py @@ -0,0 +1,15 @@ +""" +particle concentration (per volume of air) +""" + +from PySDM.products.impl import ConcentrationProduct, register_product + + +@register_product() +class TotalParticleConcentration(ConcentrationProduct): + def __init__(self, name=None, unit="m^-3", stp=False): + super().__init__(name=name, unit=unit, specific=False, stp=stp) + + def _impl(self, **kwargs): + self._download_moment_to_buffer(attr="water mass", rank=0) + return super()._impl(**kwargs) diff --git a/PySDM/source/PySDM/products/size_spectral/total_particle_specific_concentration.py b/PySDM/source/PySDM/products/size_spectral/total_particle_specific_concentration.py new file mode 100644 index 0000000000000000000000000000000000000000..d97d94802d733fcd055d0df54029e4a434ea9178 --- /dev/null +++ b/PySDM/source/PySDM/products/size_spectral/total_particle_specific_concentration.py @@ -0,0 +1,15 @@ +""" +particle specific concentration (per mass of dry air) +""" + +from PySDM.products.impl import ConcentrationProduct, register_product + + +@register_product() +class TotalParticleSpecificConcentration(ConcentrationProduct): + def __init__(self, name=None, unit="kg^-1"): + super().__init__(name=name, unit=unit, stp=False, specific=True) + + def _impl(self, **kwargs): + self._download_moment_to_buffer(attr="volume", rank=0) + return super()._impl(**kwargs) diff --git a/PySDM/source/PySDM/products/size_spectral/water_mixing_ratio.py b/PySDM/source/PySDM/products/size_spectral/water_mixing_ratio.py new file mode 100644 index 0000000000000000000000000000000000000000..f095a1dcede601335e6409267346d9060efd46ef --- /dev/null +++ b/PySDM/source/PySDM/products/size_spectral/water_mixing_ratio.py @@ -0,0 +1,48 @@ +""" +liquid water mixing ratio (per mass of dry air) computed from particle sizes + (optionally restricted to a given size range) +""" + +import numpy as np + +from PySDM.products.impl import MomentProduct, register_product + + +@register_product() +class WaterMixingRatio(MomentProduct): + def __init__(self, radius_range=None, name=None, unit="dimensionless"): + self.radius_range = radius_range or (0, np.inf) + self.signed_mass_range = None + super().__init__(unit=unit, name=name) + + def register(self, builder): + super().register(builder) + self.signed_mass_range = ( + self.formulae.particle_shape_and_density.radius_to_mass( + np.asarray(self.radius_range) + ) + ) + self.radius_range = None + + def _impl(self, **kwargs): # TODO #217 + self._download_moment_to_buffer( + attr="water mass", + rank=0, + filter_range=self.signed_mass_range, + filter_attr="signed water mass", + ) + number = self.buffer.copy() + + self._download_moment_to_buffer( + attr="water mass", + rank=1, + filter_range=self.signed_mass_range, + filter_attr="signed water mass", + ) + result = self.buffer.copy() + result[:] *= number + result[:] /= self.particulator.mesh.dv + + self._download_to_buffer(self.particulator.environment["rhod"]) + result[:] /= self.buffer + return result diff --git a/PySDM/source/README.md b/PySDM/source/README.md new file mode 100644 index 0000000000000000000000000000000000000000..620185e9a0ec81dd266cd73b1b88c3201557b4e9 --- /dev/null +++ b/PySDM/source/README.md @@ -0,0 +1,157 @@ +# pysdm logo + +[![Python 3](https://img.shields.io/static/v1?label=Python&logo=Python&color=3776AB&message=3)](https://www.python.org/) +[![LLVM](https://img.shields.io/static/v1?label=LLVM&logo=LLVM&color=gold&message=Numba)](https://numba.pydata.org) +[![CUDA](https://img.shields.io/static/v1?label=CUDA&logo=nVidia&color=87ce3e&message=ThrustRTC)](https://pypi.org/project/ThrustRTC/) +[![Linux OK](https://img.shields.io/static/v1?label=Linux&logo=Linux&color=yellow&message=%E2%9C%93)](https://en.wikipedia.org/wiki/Linux) +[![macOS OK](https://img.shields.io/static/v1?label=macOS&logo=Apple&color=silver&message=%E2%9C%93)](https://en.wikipedia.org/wiki/macOS) +[![Windows OK](https://img.shields.io/static/v1?label=Windows&logo=Windows&color=white&message=%E2%9C%93)](https://en.wikipedia.org/wiki/Windows) +[![Jupyter](https://img.shields.io/static/v1?label=Jupyter&logo=Jupyter&color=f37626&message=%E2%9C%93)](https://jupyter.org/) +[![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://github.com/open-atmos/PySDM/graphs/commit-activity) +[![OpenHub](https://www.openhub.net/p/atmos-cloud-sim-uj-PySDM/widgets/project_thin_badge?format=gif)](https://www.openhub.net/p/atmos-cloud-sim-uj-PySDM) +[![status](https://joss.theoj.org/papers/62cad07440b941f73f57d187df1aa6e9/status.svg)](https://joss.theoj.org/papers/62cad07440b941f73f57d187df1aa6e9) +[![DOI](https://zenodo.org/badge/199064632.svg)](https://zenodo.org/badge/latestdoi/199064632) +[![EU Funding](https://img.shields.io/static/v1?label=EU%20Funding%20by&color=103069&message=FNP&logoWidth=25&logo=image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC4AAAAeCAYAAABTwyyaAAAEzklEQVRYw9WYS2yUVRiGn3P5ZzozpZ3aUsrNgoKlKBINmkhpCCwwxIAhsDCpBBIWhmCMMYTEhSJ4i9EgBnSBEm81MRrFBhNXEuUSMCopiRWLQqEGLNgr085M5//POS46NNYFzHQ6qGc1i5nzP/P973m/9ztCrf7A8T9csiibCocUbvTzfxLcAcaM3cY3imXz25lT3Y34G7gQYAKV3+bFAHcATlBTPogJNADG92iY28FHW97kyPbnuW/W7xgzAhukQ9xe04PJeOT0HkQRwK0TlEeGWb/kOO9v3kdD3a8YK9GhDMfa6mg9fxunOm/lWPtcpDI4K7n/jnN8+uQbrFrUSiwU/DtSEUB/MsKKBT+zYslJqiYNgVE4JwhHkzy86wlWvrKVWDSZ/YFjZlU39yw4y/rGoyQGowWB67zl4QQue+jssMdXrQvZ/00jyeHwqCgDKwnsiJjSvkYAxsG5K9WsenYbJdqAtAjhCIxCSZt/4fK1w5A2WCvxrUAKCHwNVoA2aGmvq11jJQQapEXrgMBKqmJJugejKGWLIxXrBPFoigfv/omd675gRkU/xgqUDlAhH3UDaAAlLSqUQekAYyVTyhLs3tDMsvntlIYzOFcEcOcEGd9jx9oDbGs6QO0t/Tijxi9S4bhzxiWaVh5m94Zm0n7oui4ybo0raUlcncQnxx+g+WgDF/vLoYDmoqSl/dJUnt7XRCoTZjij0Z6Pc2LiNS4EBBkNvoeOJXN+yPWWSZeANOhwJq/98nKVwNdoL8B5AROxBKBL0gjh8DMhdCh3eJnrA0yqhLpplwmyup6IajvAOIGfKGVx3VmCRGnOMpe5QAdG0bT8CAeeep0d6z6nqjSJnQiZWEllLMWrmz6k+fE9rGk8MVqYgsGv5ZH2i1Opr+9kajzB5d74hKQ+KS3d/WVMLhtgdu1lriRiOR/4nDVunaR24x7qp3UV5Cb/fJvC83nv26W81LIK58SYNFmwq4hsGx/5BwKlzYRma2NUthgOJSew4i7ru9nJYCQF5tApb2yvjiDQKJV/IfJKh0o6qssSLKv/jcAoRKHQQzE2Lj2OMV5OkWFc4MZIpsev8uXWXRx6ZicbGk8QZLxxgwe+x/rlR3h3816+f2E7lbEU+ZDn3vKVpePCdFovzCISHqbl5EIoQOteKMPB1rto65zNyfOz+KOrGl06lHPQyi/WOohH0/T0l1MZH6A3GUEKl7Pmr2la6wBrBWWRDP2DUcqjKVKBGom9RZmABAykwnglafpSJSPQvsfiOR0EQ7ExVmazA8cY6N4K1iw6RdAXRwi4mgrheT5Dvs4LeuS81a15Ll/3dQisFVSVpnj7sf1sX/sZvhAc+6UOrQyBVUQ8gx/orFmDsZqtaw/y1qZ9zKjp5vDpenyjcNe+cLNmTiUdf/bEOddVQ0VpgsOn54ET+EYxvWKALSu+5tGG76it7MNaiZKGQ23zCIcMfUMxBnrjN3fmHHvCAlp+vJcXWx6itqoXpAEnUNLx8iMfo5Xh1i17R3PJYCpC2cZ3qK3sQ8WGEDDuXlAQuFKGHzpmopXhTNfk0bmxs7uC1w6uJul79AxFkMIiBJy5UoUWjrZLU5DCFdTARDHuDqVw+OkSwI0MCEW4gtNF2BPrBCo8fKNbtILWX9aUDqFqHnn7AAAAAElFTkSuQmCC)](https://www.fnp.org.pl/en/) +[![PL Funding](https://img.shields.io/static/v1?label=PL%20Funding%20by&color=d21132&message=NCN&logoWidth=25&logo=image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAABmJLR0QA/wD/AP+gvaeTAAAAKUlEQVQ4jWP8////fwYqAiZqGjZqIHUAy4dJS6lqIOMdEZvRZDPcDQQAb3cIaY1Sbi4AAAAASUVORK5CYII=)](https://www.ncn.gov.pl/?language=en) +[![US Funding](https://img.shields.io/static/v1?label=US%20DOE%20Funding%20by&color=267c32&message=ASR&logoWidth=25&logo=image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAQCAMAAAA25D/gAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAASFBMVEVOTXyyIjRDQnNZWINZWITtzdFUU4BVVIFVVYHWiZM9PG/KZnNXVoJaWYT67/FKSXhgX4hgX4lcW4VbWoX03uHQeIN2VXj///9pZChlAAAAAWJLR0QXC9aYjwAAAAd0SU1FB+EICRMGJV+KCCQAAABdSURBVBjThdBJDoAgEETRkkkZBBX0/kd11QTTpH1/STqpAAwWBkobSlkGbt0o5xmEfqxDZJB2Q6XMoBwnVSbTylWp0hi42rmbwTOYPDfR5Kc+07IIUQQvghX9THsBHcES8/SiF0kAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTctMDgtMDlUMTk6MDY6MzcrMDA6MDCX1tBgAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE3LTA4LTA5VDE5OjA2OjM3KzAwOjAw5oto3AAAAABJRU5ErkJggg==)](https://asr.science.energy.gov/) + +[![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0.html) + +[![Github Actions Build Status](https://github.com/open-atmos/PySDM/workflows/tests/badge.svg?branch=main)](https://github.com/open-atmos/PySDM/actions) +[![Coverage Status](https://codecov.io/gh/open-atmos/PySDM/branch/main/graph/badge.svg)](https://app.codecov.io/gh/open-atmos/PySDM) +[![PyPI version](https://badge.fury.io/py/PySDM.svg)](https://pypi.org/project/PySDM) +[![API docs](https://shields.mitmproxy.org/badge/docs-pdoc.dev-brightgreen.svg)](https://open-atmos.github.io/PySDM/) + +PySDM is a package for simulating the dynamics of population of particles. +It is intended to serve as a building block for simulation systems modelling + fluid flows involving a dispersed phase, + with PySDM being responsible for representation of the dispersed phase. +Currently, the development is focused on atmospheric cloud physics + applications, in particular on modelling the dynamics of particles immersed in moist air + using the particle-based (a.k.a. super-droplet) approach + to represent aerosol/cloud/rain microphysics. +The package features a Pythonic high-performance implementation of the + Super-Droplet Method (SDM) Monte-Carlo algorithm for representing collisional growth + ([Shima et al. 2009](https://doi.org/10.1002/qj.441)), hence the name. + +PySDM documentation is maintained at: [https://open-atmos.github.io/PySDM](https://open-atmos.github.io/PySDM) + +There is a growing set of [example Jupyter notebooks](https://open-atmos.github.io/PySDM/PySDM_examples.html) exemplifying how to perform + various types of calculations and simulations using PySDM. +Most of the example notebooks reproduce results and plot from literature, see below for + a list of examples and links to the notebooks (which can be either executed or viewed + "in the cloud"). + +There are also a growing set of [tutorials](https://github.com/open-atmos/PySDM/tree/main/tutorials), also in the form of Jupyter notebooks. +These tutorials are intended for teaching purposes and include short explanations of cloud microphysical + concepts paired with widgets for running interactive simulations using PySDM. +Each tutorial also comes with a set of questions at the end that can be used as homework problems. +Like the examples, these tutorials can be executed or viewed "in the cloud" making it an especially + easy way for students to get started. + +PySDM has two alternative parallel number-crunching backends + available: multi-threaded CPU backend based on [Numba](http://numba.pydata.org/) + and GPU-resident backend built on top of [ThrustRTC](https://pypi.org/project/ThrustRTC/). +The [`Numba`](https://open-atmos.github.io/PySDM/PySDM/backends/numba.html) backend (aliased ``CPU``) features multi-threaded parallelism for + multi-core CPUs, it uses the just-in-time compilation technique based on the LLVM infrastructure. +The [`ThrustRTC`](https://open-atmos.github.io/PySDM/PySDM/backends/thrust_rtc.html) backend (aliased ``GPU``) offers GPU-resident operation of PySDM + leveraging the [SIMT](https://en.wikipedia.org/wiki/Single_instruction,_multiple_threads) + parallelisation model. +Using the ``GPU`` backend requires nVidia hardware and [CUDA driver](https://developer.nvidia.com/cuda-downloads). + +For an overview of PySDM features (and the preferred way to cite PySDM in papers), please refer to our JOSS papers: +- [Bartman et al. 2022](https://doi.org/10.21105/joss.03219) (PySDM v1). +- [de Jong, Singer et al. 2023](https://doi.org/10.21105/joss.04968) (PySDM v2). + +PySDM includes an extension of the SDM scheme to represent collisional breakup described in [de Jong, Mackay et al. 2023](https://doi.org/10.5194/gmd-16-4193-2023). +For a list of talks and other materials on PySDM as well as a list of published papers featuring PySDM simulations, see the [project wiki](https://github.com/open-atmos/PySDM/wiki). + +## Dependencies and Installation + +PySDM dependencies are: [Numpy](https://numpy.org/), [Numba](http://numba.pydata.org/), [SciPy](https://scipy.org/), +[Pint](https://pint.readthedocs.io/), [chempy](https://pypi.org/project/chempy/), +[pyevtk](https://pypi.org/project/pyevtk/), +[ThrustRTC](https://fynv.github.io/ThrustRTC/) and [CURandRTC](https://github.com/fynv/CURandRTC). + +To install PySDM using ``pip``, use: ``pip install PySDM`` +(or ``pip install git+https://github.com/open-atmos/PySDM.git`` to get updates +beyond the latest release). + +Conda users may use ``pip`` as well, see the [Installing non-conda packages](https://docs.conda.io/projects/conda/en/latest/user-guide/tasks/manage-pkgs.html#installing-non-conda-packages) section in the conda docs. + +For development purposes, we suggest cloning the repository and installing it using ``pip -e``. +Test-time dependencies can be installed with ``pip -e .[tests]``. + +PySDM examples constitute the [``PySDM-examples``](https://github.com/open-atmos/PySDM/blob/main/examples) package. +The examples have additional dependencies listed in [``PySDM_examples`` package ``setup.py``](https://github.com/open-atmos/PySDM/blob/main/examples/setup.py) file. +Running the example Jupyter notebooks requires the ``PySDM_examples`` package to be installed. +The suggested install and launch steps are: +``` +git clone https://github.com/open-atmos/PySDM.git +pip install -e PySDM +pip install -e PySDM/examples +jupyter-notebook PySDM/examples/PySDM_examples +``` +Alternatively, one can also install the examples package from pypi.org by +using ``pip install PySDM-examples`` (note that this does not apply to notebooks itself, +only the supporting .py files). + +## Contributing, reporting issues, seeking support + +#### Our technologicial stack: +[![Python 3](https://img.shields.io/static/v1?label=+&logo=Python&color=darkred&message=Python)](https://www.python.org/) +[![Numba](https://img.shields.io/static/v1?label=+&logo=Numba&color=orange&message=Numba)](https://numba.pydata.org) +[![LLVM](https://img.shields.io/static/v1?label=+&logo=LLVM&color=gold&message=LLVM)](https://llvm.org) +[![CUDA](https://img.shields.io/static/v1?label=+&logo=nVidia&color=darkgreen&message=ThrustRTC/CUDA)](https://pypi.org/project/ThrustRTC/) +[![NumPy](https://img.shields.io/static/v1?label=+&logo=numpy&color=blue&message=NumPy)](https://numpy.org/) +[![pytest](https://img.shields.io/static/v1?label=+&logo=pytest&color=purple&message=pytest)](https://pytest.org/) +[![Colab](https://img.shields.io/static/v1?label=+&logo=googlecolab&color=darkred&message=Colab)](https://colab.research.google.com/) +[![Codecov](https://img.shields.io/static/v1?label=+&logo=codecov&color=orange&message=Codecov)](https://codecov.io/) +[![PyPI](https://img.shields.io/static/v1?label=+&logo=pypi&color=gold&message=PyPI)](https://pypi.org/) +[![GithubActions](https://img.shields.io/static/v1?label=+&logo=github&color=darkgreen&message=GitHub Actions)](https://github.com/features/actions) +[![Jupyter](https://img.shields.io/static/v1?label=+&logo=Jupyter&color=blue&message=Jupyter)](https://jupyter.org/) +[![PyCharm](https://img.shields.io/static/v1?label=+&logo=pycharm&color=purple&message=PyCharm)](https://www.jetbrains.com/pycharm/) + +Submitting new code to the project, please preferably use [GitHub pull requests](https://github.com/open-atmos/PySDM/pulls) - it helps to keep record of code authorship, +track and archive the code review workflow and allows to benefit +from the continuous integration setup which automates execution of tests +with the newly added code. + +Code contributions are assumed to imply transfer of copyright. +Should there be a need to make an exception, please indicate it when creating +a pull request or contributing code in any other way. In any case, +the license of the contributed code must be compatible with GPL v3. + +Developing the code, we follow [The Way of Python](https://www.python.org/dev/peps/pep-0020/) and +the [KISS principle](https://en.wikipedia.org/wiki/KISS_principle). +The codebase has greatly benefited from [PyCharm code inspections](https://www.jetbrains.com/help/pycharm/code-inspection.html) +and [Pylint](https://pylint.readthedocs.io/en/stable/), [Black](https://black.readthedocs.io/en/stable/) and [isort](https://pycqa.github.io/isort/) +code analysis (which are all part of the CI workflows). + +We also use [pre-commit hooks](https://pre-commit.com). +In our case, the hooks modify files and re-format them. +The pre-commit hooks can be run locally, and then the resultant changes need to be staged before committing. +To set up the hooks locally, install pre-commit via `pip install pre-commit` and +set up the git hooks via `pre-commit install` (this needs to be done every time you clone the project). +To run all pre-commit hooks, run `pre-commit run --all-files`. +The `.pre-commit-config.yaml` file can be modified in case new hooks are to be added or + existing ones need to be altered. + +Further hints addressed at PySDM developers are maintained in the [open-atmos/python-dev-hints Wiki](https://github.com/open-atmos/python-dev-hints/wiki) + and in [PySDM HOWTOs](https://github.com/open-atmos/PySDM/tree/main/examples/PySDM_examples/_HOWTOs). + +Issues regarding any incorrect, unintuitive or undocumented bahaviour of +PySDM are best to be reported on the [GitHub issue tracker](https://github.com/open-atmos/PySDM/issues/new). +Feature requests are recorded in the "Ideas..." [PySDM wiki page](https://github.com/open-atmos/PySDM/wiki/Ideas-for-new-features-and-examples). + +We encourage to use the [GitHub Discussions](https://github.com/open-atmos/PySDM/discussions) feature +(rather than the issue tracker) for seeking support in understanding, using and extending PySDM code. + +We look forward to your contributions and feedback. + +## Licensing: + +copyright: [Jagiellonian University](https://en.uj.edu.pl/en) (2019-2023) & [AGH University of Krakow](https://agh.edu.pl/en) (2023-...) +licence: [GPL v3](https://www.gnu.org/licenses/gpl-3.0.html) + + diff --git a/PySDM/source/__init__.py b/PySDM/source/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..114dd9022fc78abe002093f4d277b17d331ce091 --- /dev/null +++ b/PySDM/source/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +""" +PySDM Project Package Initialization File +""" diff --git a/PySDM/source/docs/bibliography.json b/PySDM/source/docs/bibliography.json new file mode 100644 index 0000000000000000000000000000000000000000..7b715f414a1613190fcf0e065596efa908cf14bb --- /dev/null +++ b/PySDM/source/docs/bibliography.json @@ -0,0 +1,1015 @@ +{ + "https://doi.org/10.1007/978-1-935704-36-2_1": { + "usages": [ + "examples/docs/pysdm_examples_landing.md", + "examples/PySDM_examples/utils/kinematic_2d/__init__.py" + ], + "label": "Kessler 1969 (Meteorol. Monogr. 10)", + "title": "On the Distribution and Continuity of Water Substance in Atmospheric Circulations" + }, + "https://doi.org/10.1175/1520-0450(1977)016%3C0100:AAPFTC%3E2.0.CO;2": { + "usages": [ + "PySDM/physics/saturation_vapour_pressure/lowe1977.py", + "PySDM/physics/constants_defaults.py" + ], + "label": "Lowe 1977 (J. Appl. Meteorol. Climatol. 16)", + "title": "An Approximating Polynomial for the Computation of Saturation Vapor Pressure" + }, + "https://doi.org/10.1175/1520-0450(1992)031%3C1507:PFTSVP%3E2.0.CO;2": { + "usages": [ + "PySDM/physics/saturation_vapour_pressure/flatau_walko_cotton.py", + "PySDM/physics/constants_defaults.py" + ], + "label": "Flatau et al. 1992 (J. Appl. Meteorol. Climatol. 31)", + "title": "Polynomial Fits to Saturation Vapor Pressure" + }, + "https://doi.org/10.1038/s41467-019-12982-0": { + "usages": [ + "examples/PySDM_examples/Lowe_et_al_2019/fig_1.ipynb", + "examples/PySDM_examples/Lowe_et_al_2019/fig_2.ipynb", + "examples/PySDM_examples/Lowe_et_al_2019/fig_3.ipynb", + "examples/PySDM_examples/Lowe_et_al_2019/fig_s2.ipynb", + "examples/PySDM_examples/Lowe_et_al_2019/__init__.py", + "PySDM/physics/diffusion_kinetics/lowe_et_al_2019.py", + "PySDM/physics/diffusion_thermics/lowe_et_al_2019.py", + "PySDM/physics/constants_defaults.py" + ], + "label": "Lowe et al. 2019 (Nature Comm. 10)", + "title": "Key drivers of cloud response to surface-active organics" + }, + "https://doi.org/10.1039/C3FD00035D": { + "usages": [ + "PySDM/physics/heterogeneous_ice_nucleation_rate/abifm.py", + "PySDM/physics/constants_defaults.py" + ], + "label": "Knopf & Alpert 2013 (Faraday Discuss. 165)", + "title": "A water activity based model of heterogeneous ice nucleation kinetics for freezing of water and aqueous solution droplets" + }, + "https://doi.org/10.1256/qj.04.94": { + "usages": [ + "PySDM/physics/saturation_vapour_pressure/murphy_koop_2005.py", + "PySDM/physics/constants_defaults.py" + ], + "label": "Murphy & Koop 2005 (Q. J. R. Meteorol. Soc. 131)", + "title": "Review of the vapour pressures of ice and supercooled water for atmospheric applications" + }, + "https://doi.org/10.1051/jcp/1971680625": { + "usages": [ + "PySDM/physics/isotope_equilibrium_fractionation_factors/majoube_1970.py" + ], + "label": "Majoube 1971 ( J. Chim. Phys. 68)", + "title": "Fractionnement en 180 entre la glace et la vapeur d’eau" + }, + "https://doi.org/10.5194/acp-5-461-2005": { + "usages": [ + "PySDM/physics/diffusion_kinetics/fuchs_sutugin.py", + "PySDM/physics/constants_defaults.py" + ], + "label": "Laaksonen et al. 2005 (Atmos. Chem. Phys. 5)", + "title": "Commentary on cloud modelling and the mass accommodation coefficient of water" + }, + "https://doi.org/10.1175/1520-0469(1999)056%3C4100:TIOGCC%3E2.0.CO;2": { + "usages": [ + "PySDM/dynamics/collisions/breakup_fragmentations/feingold1988.py" + ], + "label": "Feingold et al. 1999 (J. Atmos. Sci. 56)", + "title": "The Impact of Giant Cloud Condensation Nuclei on Drizzle Formation in Stratocumulus: Implications for Cloud Radiative Properties" + }, + "https://doi.org/10.1029/93JD03518": { + "usages": [ + "examples/PySDM_examples/Gedzelman_and_Arnold_1994/fig_2.ipynb", + "examples/PySDM_examples/Gedzelman_and_Arnold_1994/__init__.py", + "PySDM/physics/isotope_ratio_evolution/gedzelman_and_arnold_1994.py", + "PySDM/physics/isotope_ratio_evolution/merlivat_and_jouzel_1979.py", + "examples/PySDM_examples/Zaba_et_al/timescales_comparison.ipynb" + ], + "label": "Gedzelman & Arnold 1994 (J. Geophys. Res. Atmos. 99)", + "title": "Modeling the isotopic composition of precipitation" + }, + "https://doi.org/10.1175/JAS3980": { + "usages": [ + "examples/docs/pysdm_examples_landing.md", + "examples/PySDM_examples/utils/kinematic_2d/__init__.py", + "examples/PySDM_examples/Morrison_and_Grabowski_2007/__init__.py", + "examples/PySDM_examples/Morrison_and_Grabowski_2007/fig_1.ipynb" + ], + "label": "Morrison & Grabowski 2007 (J. Atmos. Sci. 64)", + "title": "Comparison of Bulk and Bin Warm-Rain Microphysics Models Using a Kinematic Framework" + }, + "https://doi.org/10.1002/qj.4775": { + "usages": [ + "examples/PySDM_examples/Abade_and_Albuquerque_2024/fig_2.ipynb", + "examples/PySDM_examples/Abade_and_Albuquerque_2024/__init__.py" + ], + "label": "Abade & Albuquerque 2024 (Q. J. R. Meteorol. Soc. 150)", + "title": "Persistent mixed-phase states in adiabatic cloud parcels under idealised conditions" + }, + "https://doi.org/10.1073/pnas.1618374114": { + "usages": [ + "examples/PySDM_examples/Lamb_et_al_2017/fig_4.ipynb", + "examples/PySDM_examples/Lamb_et_al_2017/__init__.py", + "PySDM/physics/constants_defaults.py", + "PySDM/physics/isotope_equilibrium_fractionation_factors/lamb_et_al_2017.py" + ], + "label": "Lamb et al. 2017 (PNAS 114)", + "title": "Laboratory measurements of HDO/H2O isotopic fractionation during ice deposition in simulated cirrus clouds" + }, + "https://doi.org/10.1002/qj.441": { + "usages": [ + "README.md", + "docs/markdown/pysdm_landing.md", + "examples/PySDM_examples/Shima_et_al_2009/fig_2.ipynb", + "examples/PySDM_examples/Shima_et_al_2009/__init__.py", + "PySDM/dynamics/collisions/collision.py", + "tutorials/collisions/collisions_playground.ipynb", + "tutorials/wikipedia/sdm.ipynb", + "tests/smoke_tests/box/shima_et_al_2009/test_convergence.py" + ], + "label": "Shima et al. 2009 (Q. J. R. Meteorol. Soc. 135)", + "title": "The super-droplet method for the numerical simulation of clouds and precipitation: a particle-based and probabilistic microphysics model coupled with a non-hydrostatic model" + }, + "https://doi.org/10.5194/acp-11-12945-2011": { + "usages": [ + "examples/PySDM_examples/Arabas_et_al_2025/aida.ipynb" + ], + "label": "Steinke et al. 2011 (Atmos. Chem. Phys. 11)", + "title": "Ice nucleation properties of fine ash particles from the Eyjafjallajökull eruption in April 2010 " + }, + "https://doi.org/10.1016/0045-7825(87)90003-X": { + "usages": [ + "PySDM/physics/constants_defaults.py" + ], + "label": "Zografos et al. 1987 (Comput. Method. Appl. M. 61)", + "title": "Equations of properties as a function of temperature for seven fluids" + }, + "https://doi.org/10.1119/1.15018": { + "usages": [ + "PySDM/physics/constants_defaults.py", + "PySDM/physics/trivia.py" + ], + "label": "Vedder 1987 (Am. J. Phys. 55)", + "title": "Simple approximations for the error function and its inverse" + }, + "https://doi.org/10.21105/joss.03896": { + "usages": [ + "examples/docs/pysdm_examples_landing.md" + ], + "label": "Bartman et al. 2022 (J. Open Source Soft. 7)", + "title": "PyMPDATA v1: Numba-accelerated implementation of MPDATA with examples in Python, Julia and Matlab" + }, + "https://doi.org/10.2467/mripapers1950.19.2_243": { + "usages": [ + "examples/PySDM_examples/Miyake_et_al_1968/fig_19.ipynb", + "PySDM/physics/isotope_relaxation_timescale/miyake_et_al_1968.py", + "examples/PySDM_examples/Zaba_et_al/timescales_comparison.ipynb" + ], + "label": "Miyake et al. 1968 (Pap. Meteorol. Geophys. 19)", + "title": "An Isotopic Study on Meteoric Precipitation" + }, + "https://doi.org/10.5194/acp-7-1961-2007": { + "usages": [ + "PySDM/physics/hygroscopicity/kappa_koehler.py", + "PySDM/physics/constants_defaults.py", + "examples/PySDM_examples/Jensen_and_Nugent_2017/settings.py" + ], + "label": "Petters & Kreidenweis 2007 (Atmos. Chem. Phys. 7)", + "title": "A single parameter representation of hygroscopic growth and cloud condensation nucleus activity" + }, + "https://doi.org/10.1016/0016-7037(94)90096-5": { + "usages": [ + "PySDM/physics/constants_defaults.py", + "PySDM/physics/isotope_equilibrium_fractionation_factors/horita_and_wesolowski_1994.py" + ], + "label": "Horita & Wesolowski 1994 (Geochim. Cosmochim. Acta 58)", + "title": "Liquid-vapor fractionation of oxygen and hydrogen isotopes of water from the freezing to the critical temperature" + }, + "https://doi.org/10.1175/MWR-D-14-00319.1": { + "usages": [ + "PySDM/physics/bulk_phase_partitioning/kaul_et_al_2015.py", + "PySDM/physics/constants_defaults.py" + ], + "label": "Kaul et al. 2015 (Mon. Weather Rev. 143)", + "title": "Sensitivities in Large-Eddy Simulations of Mixed-Phase Arctic Stratocumulus Clouds Using a Simple Microphysics Approach" + }, + "https://doi.org/10.3402/tellusa.v16i4.8993": { + "usages": [ + "PySDM/physics/isotope_meteoric_water_line/dansgaard_1964.py" + ], + "label": "Dansgaard 1964 (Tellus A 16)", + "title": "Stable isotopes in precipitation" + }, + "https://doi.org/10.1016/j.gca.2022.01.020": { + "usages": [ + "examples/PySDM_examples/Pierchala_et_al_2022/fig_3.ipynb", + "examples/PySDM_examples/Pierchala_et_al_2022/fig_4.ipynb", + "examples/PySDM_examples/Pierchala_et_al_2022/__init__.py", + "examples/PySDM_examples/Pierchala_et_al_2022/commons.py", + "PySDM/physics/isotope_kinetic_fractionation_factors/craig_gordon.py" + ], + "label": "Pierchala et al. 2022 (Geochim. Cosmochim. Acta 322)", + "title": "Quantification the diffusion-induced fractionation of 1H217O isotopologue in air accompanying the process of water evaporation" + }, + "https://doi.org/10.1175/1520-0469(1982)039%3C1607:CCABOR%3E2.0.CO;2": { + "usages": [ + "PySDM/dynamics/collisions/breakup_fragmentations/lowlist82.py", + "PySDM/dynamics/collisions/coalescence_efficiencies/lowlist1982.py" + ], + "label": "Low & List 1982 (J. Atmos. Sci. 39)", + "title": "Collision, Coalescence and Breakup of Raindrops. Part II: Parameterization of Fragment Size Distributions" + }, + "https://doi.org/10.5194/npg-24-535-2017": { + "usages": [ + "examples/PySDM_examples/Arabas_and_Shima_2017/fig_5.ipynb", + "examples/PySDM_examples/Arabas_and_Shima_2017/__init__.py" + ], + "label": "Arabas & Shima 2017 (Nonlin. Proc. Geophys. 24)", + "title": "On the CCN (de)activation nonlinearities" + }, + "https://doi.org/10.1038/nature22806": { + "usages": [ + "PySDM/physics/surface_tension/compressed_film_ovadnevaite.py" + ], + "label": "Ovadnevaite et al. 2017 (Nature 546)", + "title": "Surface tension prevails over solute effect in organic-influenced cloud droplet activation" + }, + "https://doi.org/10.3402/tellusa.v34i2.10795": { + "usages": [ + "examples/PySDM_examples/Rozanski_and_Sonntag_1982/figs_4_5_6.ipynb", + "examples/PySDM_examples/Rozanski_and_Sonntag_1982/__init__.py", + "examples/PySDM_examples/Rozanski_and_Sonntag_1982/multibox.py" + ], + "label": "Rozanski & Sonntag 1982 (Tellus A 34)", + "title": "Vertical distribution of deuterium in atmospheric water vapour" + }, + "https://doi.org/10.5194/acp-18-7313-2018": { + "usages": [ + "examples/PySDM_examples/Yang_et_al_2018/fig_2.ipynb", + "examples/PySDM_examples/Yang_et_al_2018/__init__.py" + ], + "label": "Yang et al. 2018 (Atmos. Chem. Phys. 18)", + "title": "Cloud droplet size distribution broadening during diffusional growth: ripening amplified by deactivation and reactivation" + }, + "https://doi.org/10.1063/1.1840906": { + "usages": [ + "PySDM/physics/diffusion_kinetics/pruppacher_and_klett_2005.py" + ], + "label": "Okuyama & Zung 1967 (J. Chem. Phys. 1967)", + "title": "Evaporation—Condensation Coefficient for Small Droplets" + }, + "https://doi.org/10.5194/gmd-8-1677-2015": { + "usages": [ + "examples/docs/pysdm_examples_landing.md", + "examples/PySDM_examples/Arabas_et_al_2015/gui.ipynb", + "PySDM/dynamics/displacement.py", + "PySDM/physics/particle_advection/implicit_in_space.py", + "examples/PySDM_examples/utils/kinematic_2d/__init__.py" + ], + "label": "Arabas et al. 2015 (Geosci. Model Dev. 2015)", + "title": "libcloudph++ 1.0: a single-moment bulk, double-moment bulk, and particle-based warm-rain microphysics library in C++" + }, + "https://doi.org/10.1002/rcm.6668": { + "usages": [ + "PySDM/physics/constants_defaults.py", + "PySDM/physics/isotope_equilibrium_fractionation_factors/ellehoj_et_al_2013.py" + ], + "label": "Ellehoj et al. 2013 (Rapid Commun. Mass Spectrom. 27)", + "title": "Ice-vapor equilibrium fractionation factor of hydrogen and oxygen isotopes: Experimental investigations and implications for stable water isotope studies" + }, + "https://doi.org/10.1088/0370-1301/66/8/309": { + "usages": [ + "PySDM/physics/freezing_temperature_spectrum/bigg_1953.py" + ], + "label": "Bigg 1953 (Proc. Phys. Soc. B 66)", + "title": "The Supercooling of Water" + }, + "https://doi.org/10.1029/2003JD003597": { + "usages": [ + "PySDM/physics/isotope_diffusivity_ratios/stewart_1975.py" + ], + "label": "Cappa et al. 2003 (J. Geophys. Res. Atmos. 108)", + "title": "Isotopic fractionation of water during evaporation" + }, + "https://doi.org/10.1175/2009JAS3175.1": { + "usages": [ + "examples/PySDM_examples/deJong_Mackay_et_al_2023/fig_9.ipynb", + "PySDM/dynamics/collisions/breakup_fragmentations/straub2010.py", + "PySDM/dynamics/collisions/coalescence_efficiencies/straub2010.py", + "PySDM/physics/constants_defaults.py" + ], + "label": "Straub et al. 2010 (J. Atmos. Sci. 67)", + "title": "Numerical Investigation of Collision-Induced Breakup of Raindrops. Part II: Parameterizations of Coalescence Efficiencies and Fragment Size Distributions" + }, + "https://doi.org/10.1029/1999JD901161": { + "usages": [ + "examples/PySDM_examples/Abdul_Razzak_Ghan_2000/__init__.py", + "examples/PySDM_examples/Abdul_Razzak_Ghan_2000/figs1-5.ipynb", + "examples/PySDM_examples/Abdul_Razzak_Ghan_2000/fig_4_kinetic_limitations.ipynb" + + ], + "label": "Abdul-Razzak & Ghan 2000 (J. Geophys. Res. Atmos. 105)", + "title": "A parameterization of aerosol activation: 2. Multiple aerosol types" + }, + "https://doi.org/10.5194/acp-16-2083-2016": { + "usages": [ + "examples/PySDM_examples/Alpert_and_Knopf_2016/fig_1.ipynb", + "examples/PySDM_examples/Alpert_and_Knopf_2016/fig_2.ipynb", + "examples/PySDM_examples/Alpert_and_Knopf_2016/fig_3.ipynb", + "examples/PySDM_examples/Alpert_and_Knopf_2016/fig_4.ipynb", + "examples/PySDM_examples/Alpert_and_Knopf_2016/fig_5.ipynb", + "examples/PySDM_examples/Alpert_and_Knopf_2016/__init__.py", + "examples/PySDM_examples/Arabas_et_al_2025/commons.py" + ], + "label": "Alpert & Knopf 16 (Atmos. Chem. Phys. 16)", + "title": "Analysis of isothermal and cooling-rate-dependent immersion freezing by a unifying stochastic ice nucleation model" + }, + "https://doi.org/10.1080/10256010801887174": { + "usages": [ + "PySDM/physics/isotope_diffusivity_ratios/grahams_law.py", + "PySDM/physics/isotope_diffusivity_ratios/stewart_1975.py", + "tests/unit_tests/physics/test_isotope_diffusivity_ratios.py" + ], + "label": "Horita et al. 2008 (Isot. Environ. Healt. S. 44)", + "title": "Isotope effects in the evaporation of water: a status report of the Craig–Gordon model" + }, + "https://doi.org/10.5194/gmd-9-1455-2016": { + "usages": [ + "PySDM/dynamics/collisions/breakup_fragmentations/slams.py" + ], + "label": "Jokulsdottir & Archer 2016 (Geosci. Model Dev. 9)", + "title": "A stochastic, Lagrangian model of sinking biogenic aggregates in the ocean (SLAMS 1.0): model formulation, validation and sensitivity" + }, + "https://doi.org/10.3402/tellusa.v19i1.9756": { + "usages": [ + "examples/PySDM_examples/Merlivat_and_Nief_1967/fig_2.ipynb", + "examples/PySDM_examples/Merlivat_and_Nief_1967/__init__.py", + "PySDM/physics/constants_defaults.py", + "PySDM/physics/isotope_equilibrium_fractionation_factors/merlivat_and_nief_1967.py" + ], + "label": "Merlivat & Nief 1967 (Tellus A 19)", + "title": "Fractionnement isotopique lors des changements d’état solide-vapeur et liquide-vapeur de l’eau à des températures inférieures à 0°C" + }, + "https://doi.org/10.1175/1520-0469(1978)035%3C2123:RPIEWC%3E2.0.CO;2": { + "usages": [ + "PySDM/physics/optical_depth/stephens_1978.py" + ], + "label": "Stephens 1978 (J. Atmos. Sci. 35)", + "title": "Radiation Profiles in Extended Water Clouds. II: Parameterization Schemes" + }, + "https://doi.org/10.1029/2022MS002994": { + "usages": [ + "examples/PySDM_examples/Bieli_et_al_2022/__init__.py" + ], + "label": "Bieli et al. 2022 (J. Adv. Model. Earth Sys. 14)", + "title": "An Efficient Bayesian Approach to Learning Droplet Collision Kernels: Proof of Concept Using “Cloudy,” a New n-Moment Bulk Microphysics Scheme" + }, + "https://doi.org/10.1016/j.atmosres.2010.10.020": { + "usages": [ + "PySDM/physics/constants_defaults.py", + "PySDM/physics/diffusion_kinetics/grabowski_et_al_2011.py", + "PySDM/physics/diffusion_thermics/grabowski_et_al_2011.py" + ], + "label": "Grabowski et al. 2011 (Atmos. Res. 99)", + "title": "Droplet growth in a bin warm-rain scheme with Twomey CCN activation" + }, + "https://doi.org/10.5194/gmd-16-4193-2023": { + "usages": [ + "README.md", + "examples/docs/pysdm_examples_landing.md", + "examples/PySDM_examples/deJong_Mackay_et_al_2023/__init__.py" + ], + "label": "de Jong et al. 2023 (Geosci. Model Dev. 16)", + "title": "Breakups are complicated: an efficient representation of collisional breakup in the superdroplet method" + }, + "https://doi.org/10.1142/p027": { + "usages": [ + "PySDM/physics/trivia.py" + ], + "label": "Gat 2010", + "title": "Isotope Hydrology: A Study of the Water Cycle" + }, + "https://doi.org/10.1071/CH9520059": { + "usages": [ + "PySDM/physics/constants_defaults.py", + "PySDM/physics/ventilation/froessling_1938.py" + ], + "label": "Squires 1952 (Aust. J. Chem. (Aust. J. Sci. Res. 5)", + "title": "The Growth of Cloud Drops by Condensation. I. General Characteristics" + }, + "https://doi.org/10.1038/2261242a0": { + "usages": [ + "PySDM/physics/constants_defaults.py", + "PySDM/physics/isotope_equilibrium_fractionation_factors/majoube_1970.py" + ], + "label": "Majoube 1970 (Nature 226)", + "title": "Fractionation Factor of 18O between Water Vapour and Ice" + }, + "https://doi.org/10.1021/j100850a028": { + "usages": [ + "examples/PySDM_examples/Van_Hook_1968/fig_1.ipynb", + "examples/PySDM_examples/Van_Hook_1968/__init__.py", + "PySDM/physics/constants_defaults.py", + "PySDM/physics/isotope_equilibrium_fractionation_factors/van_hook_1968.py" + ], + "label": "Van Hook 1968 (J. Phys. Chem. 72)", + "title": "Vapor pressures of the isotopic waters and ices" + }, + "https://doi.org/10.1051/jcp/1971681423": { + "usages": [ + "PySDM/physics/constants_defaults.py", + "PySDM/physics/isotope_equilibrium_fractionation_factors/majoube_1971.py" + ], + "label": "Majoube 1971 (J. Chim. Phys. 68)", + "title": "Fractionnement en oxygène 18 et en deutérium entre l’eau et sa vapeur" + }, + "https://doi.org/10.1175/JAS-D-15-0370.1": { + "usages": [ + "examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_1.ipynb", + "examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_3_and_Tab_4_upper_rows.ipynb", + "examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_4_and_7_and_Tab_4_bottom_rows.ipynb", + "examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_5.ipynb", + "examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_6.ipynb", + "examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_8.ipynb" + ], + "label": "Jensen & Nugent 2017 (J. Atmos. Sci. 74)", + "title": "Condensational Growth of Drops Formed on Giant Sea-Salt Aerosol Particles" + }, + "https://doi.org/10.1029/2022GL101917": { + "usages": [ + "examples/PySDM_examples/Grabowski_and_Pawlowska_2023/figure_1.ipynb", + "examples/PySDM_examples/Grabowski_and_Pawlowska_2023/figure_2.ipynb", + "examples/PySDM_examples/Grabowski_and_Pawlowska_2023/figure_3.ipynb", + "examples/PySDM_examples/Grabowski_and_Pawlowska_2023/figure_4.ipynb", + "examples/PySDM_examples/Grabowski_and_Pawlowska_2023/figure_ripening_rate.ipynb", + "examples/PySDM_examples/Grabowski_and_Pawlowska_2023/__init__.py", + "tests/smoke_tests/parcel_c/grabowski_and_pawlowska_2023/test_figure_1_and_2.py", + "tests/smoke_tests/parcel_c/grabowski_and_pawlowska_2023/test_figure_3.py", + "tests/smoke_tests/parcel_c/grabowski_and_pawlowska_2023/test_figure_4.py" + ], + "label": "Grabowski & Pawlowska 2023 (Geophys. Res. Lett. 50)", + "title": "Adiabatic Evolution of Cloud Droplet Spectral Width: A New Look at an Old Problem" + }, + "https://doi.org/10.1029/2020GL089999": { + "usages": [ + "PySDM/physics/constants_defaults.py", + "PySDM/physics/isotope_diffusivity_ratios/hellmann_and_harvey_2020.py", + "PySDM/physics/isotope_diffusivity_ratios/stewart_1975.py" + ], + "label": "Hellmann & Harvey 2020 (Geophys. Res. Lett. 47)", + "title": "First-Principles Diffusivity Ratios for Kinetic Isotope Fractionation of Water in Air" + }, + "https://doi.org/10.1002/fld.1071": { + "usages": [ + "examples/docs/pysdm_examples_landing.md" + ], + "label": "Smolarkiewicz 2006 (Int. J. Numer. Methods Fluids 50)", + "title": "Multidimensional positive definite advection transport algorithm: an overview" + }, + "https://doi.org/10.5194/acp-19-747-2019": { + "usages": [ + "examples/PySDM_examples/Graf_et_al_2019/figure_4.ipynb", + "examples/PySDM_examples/Graf_et_al_2019/Table_1.ipynb", + "examples/PySDM_examples/Graf_et_al_2019/__init__.py" + ], + "label": "Graf et al. 2019 (Atmos. Chem. Phys. 19)", + "title": "A new interpretative framework for below-cloud effects on stable water isotopes in vapour and rain" + }, + "https://doi.org/10.1029/2002JD002697": { + "usages": [ + "examples/PySDM_examples/Kreidenweis_et_al_2003/fig_1.ipynb", + "examples/PySDM_examples/Kreidenweis_et_al_2003/__init__.py" + ], + "label": "Kreidenweis et al. 2003 (J. Geophys. Res. 108)", + "title": "Modification of aerosol mass and size distribution due to aqueous-phase SO2 oxidation in clouds: Comparisons of several models" + }, + "https://doi.org/10.5194/acp-12-5807-2012": { + "usages": [ + "examples/PySDM_examples/Ervens_and_Feingold_2012/__init__.py" + ], + "label": "Ervens & Feingold 2012 (Atmos. Chem. Phys. 12)", + "title": "On the representation of immersion and condensation freezing in cloud models using different nucleation schemes " + }, + "https://doi.org/10.1126/science.aad4889": { + "usages": [ + "PySDM/physics/surface_tension/compressed_film_ruehl.py", + "PySDM/physics/surface_tension/szyszkowski_langmuir.py" + ], + "label": "Ruehl et al. 2016 (Science 351)", + "title": "An interfacial mechanism for cloud droplet formation on organic aerosols" + }, + "https://doi.org/10.1175/1520-0469(1949)006%3C0243:TTVOFF%3E2.0.CO;2": { + "usages": [ + "PySDM/dynamics/terminal_velocity/gunn_and_kinzer.py", + "PySDM/physics/terminal_velocity/gunn_kinzer_1949.py" + ], + "label": "Gunn & Kinzer 1949 (J. Atmos. Sci. 6)", + "title": "The Terminal Velocity of Fall for Water Droplets in Stagnant Air" + }, + "https://doi.org/10.1002/rcm.3180": { + "usages": [ + "PySDM/physics/constants_defaults.py", + "PySDM/physics/isotope_meteoric_water_line/barkan_and_luz_2007.py" + ], + "label": "Barkan & Luz 2007 (Rapid Commun. Mass Spectrom. 21)", + "title": "Diffusivity fractionations of H216O/H217O and H216O/H218O in air and their implications for isotope hydrology" + }, + "https://doi.org/10.1002/qj.1913": { + "usages": [ + "examples/docs/pysdm_examples_landing.md", + "examples/docs/pysdm_examples_landing.md", + "examples/PySDM_examples/deJong_Mackay_et_al_2023/figs_10_11_12_13.ipynb", + "examples/PySDM_examples/Shipway_and_Hill_2012/fig_1.ipynb", + "examples/PySDM_examples/Shipway_and_Hill_2012/__init__.py" + ], + "label": "Shipway & Hill 2012 (Q. J. R. Meteorol. Soc. 138)", + "title": "Diagnosis of systematic differences between multiple parametrizations of warm rain microphysics using a kinematic framework" + }, + "https://doi.org/10.1175/1520-0469(1967)024%3C0688:CDGBC%3E2.0.CO;2": { + "usages": [ + "examples/PySDM_examples/Berry_1967/figs_5_8_10.ipynb", + "examples/PySDM_examples/Berry_1967/__init__.py", + "PySDM/dynamics/collisions/collision_kernels/electric.py", + "PySDM/dynamics/collisions/collision_kernels/hydrodynamic.py", + "PySDM/dynamics/collisions/coalescence_efficiencies/berry1967.py" + ], + "label": "Berry 1967 (J. Atmos. Sci. 24)", + "title": "Cloud Droplet Growth by Collection" + }, + "https://doi.org/10.5194/acp-13-7903-2013": { + "usages": [ + "examples/PySDM_examples/Bolot_et_al_2013/__init__.py", + "PySDM/physics/isotope_kinetic_fractionation_factors/jouzel_and_merlivat_1984.py", + "tests/unit_tests/physics/test_isotope_equilibrium_fractionation_factors.py" + ], + "label": "Bolot et al. 2013 (Atmos. Chem. Phys. 13)", + "title": "Modelling and interpreting the isotopic composition of water vapour in convective updrafts " + }, + "https://doi.org/10.5194/gmd-11-3623-2018": { + "usages": [ + "examples/PySDM_examples/Jaruga_and_Pawlowska_2018/fig_2.ipynb", + "examples/PySDM_examples/Jaruga_and_Pawlowska_2018/fig_3.ipynb", + "examples/PySDM_examples/Jaruga_and_Pawlowska_2018/__init__.py" + ], + "label": "Jaruga & Pawlowska 2018 (Geosci. Model Dev. 11)", + "title": "libcloudph++ 2.0: aqueous-phase chemistry extension of the particle-based cloud microphysics scheme" + }, + "https://doi.org/10.1175/1520-0493(1980)108%3C1046:TCOEPT%3E2.0.CO;2": { + "usages": [ + "PySDM/physics/saturation_vapour_pressure/bolton_1980.py", + "PySDM/physics/constants_defaults.py" + ], + "label": "Bolton 1980 (Mon. Weather Rev. 108)", + "title": "The Computation of Equivalent Potential Temperature" + }, + "https://doi.org/10.1029/JC084iC08p05029": { + "usages": [ + "PySDM/physics/isotope_ratio_evolution/merlivat_and_jouzel_1979.py", + "tests/unit_tests/physics/test_isotope_ratio_evolution.py" + ], + "label": "Merlivat & Jouzel 1979 (J. Geophys. Res. Oceans 84)", + "title": "Global climatic interpretation of the deuterium-oxygen 18 relationship for precipitation" + }, + "https://doi.org/10.1088/0370-1301/64/9/307": { + "usages": [ + "PySDM/physics/drop_growth/howell_1949.py" + ], + "label": "Mason 1951 (Proc. Phys. Soc. B 64)", + "title": "Spontaneous Condensation of Water Vapour in Expansion Chamber Experiments" + }, + "https://doi.org/10.1002/rcm.2250": { + "usages": [ + "PySDM/physics/constants_defaults.py", + "PySDM/physics/isotope_equilibrium_fractionation_factors/barkan_and_luz_2005.py" + ], + "label": "Barkan & Luz 2005 (Rapid Commun. Mass Spectrom. 19)", + "title": "High precision measurements of 17O/16O and 18O/16O ratios in H2O" + }, + "https://doi.org/10.1175/1520-0469(1982)039%3C1317:ASMOPC%3E2.0.CO;2": { + "usages": [ + "examples/docs/pysdm_examples_landing.md", + "examples/PySDM_examples/Srivastava_1982/equations.py", + "examples/PySDM_examples/Srivastava_1982/__init__.py" + ], + "label": "Srivastava 1982 (J. Atmos. Sci. 39)", + "title": "A Simple Model of Particle Coalescence and Breakup" + }, + "https://doi.org/10.21105/joss.04968": { + "usages": [ + "README.md" + ], + "label": "de Jong et al. 2023 (J. Open Source Soft. 8)", + "title": "New developments in PySDM and PySDM-examples v2: collisional breakup, immersion freezing, dry aerosol initialization, and adaptive time-stepping" + }, + "https://doi.org/10.1175/JAS-D-11-0249.1": { + "usages": [ + "PySDM/physics/freezing_temperature_spectrum/niemand_et_al_2012.py" + ], + "label": "Niemand et al. 2012 (J. Atmos. Sci. 69)", + "title": "A Particle-Surface-Area-Based Parameterization of Immersion Freezing on Desert Dust Particles" + }, + "https://doi.org/10.1002/2013GL058684": { + "usages": [ + "examples/PySDM_examples/Niedermeier_et_al_2014/fig_2.ipynb", + "examples/PySDM_examples/Niedermeier_et_al_2014/__init__.py" + ], + "label": "Niedermeier et al. 2014 (Geophys. Res. Lett. 41)", + "title": "A computationally efficient description of heterogeneous freezing: A simplified version of the Soccer ball model" + }, + "https://doi.org/10.1016/S0169-8095(97)00082-3": { + "usages": [ + "examples/PySDM_examples/utils/kinematic_2d/__init__.py" + ], + "label": "Szumowski et al. 1998 (Atmos. Res. 45)", + "title": "Simple two-dimensional kinematic framework designed to test warm rain microphysical models" + }, + "https://doi.org/10.21105/joss.03219": { + "usages": [ + "README.md" + ], + "label": "Bartman et al. 2022 (J. Open Source Soft. 7)", + "title": "PySDM v1: particle-based cloud modeling package for warm-rain microphysics and aqueous chemistry" + }, + "https://doi.org/10.1175/1520-0469%281979%29036%3C1255:AWTIOT%3E2.0.CO;2": { + "usages": [ + "PySDM/physics/constants_defaults.py", + "PySDM/physics/ventilation/pruppacher_rasmussen_1979.py", + "examples/PySDM_examples/Pruppacher_and_Rasmussen_1979/__init__.py", + "examples/PySDM_examples/Pruppacher_and_Rasmussen_1979/fig_1.ipynb", + "tests/smoke_tests/no_env/pruppacher_and_rasmussen_1979/test_fig_1.py" + ], + "label": "Pruppacher & Rasmussen 1979 (J. Atmos. Sci. 36)", + "title": "A Wind Tunnel Investigation of the Rate of Evaporation of Large Water Drops Falling at Terminal Velocity in Air" + }, + "https://doi.org/10.1126/science.133.3465.1702": { + "usages": [ + "examples/PySDM_examples/Zaba_et_al/global_meteoric_water_line.ipynb", + "tests/unit_tests/physics/test_isotope_meteoric_water_line.py", + "PySDM/physics/constants_defaults.py" + ], + "label": "Craig 1961 (Science 133)", + "title": "Isotopic Variations in Meteoric Waters" + }, + "https://doi.org/10.5194/gmd-13-4107-2020": { + "usages": [ + "PySDM/physics/particle_shape_and_density/porous_spheroids.py", + "PySDM/physics/constants_defaults.py" + ], + "label": "Shima et al. 2020 (Geosci. Model Dev. 13)", + "title": "Predicting the morphology of ice particles in deep convection using the super-droplet method: development and evaluation of SCALE-SDM 0.2.5-2.2.0, -2.2.1, and -2.2.2" + }, + "https://doi.org/10.1119/1.15109": { + "usages": [ + "PySDM/physics/constants_defaults.py", + "PySDM/physics/optical_albedo/bohren1987.py" + ], + "label": "Bohren 1987 (Am. J. Phys. 55)", + "title": "Multiple scattering of light and some of its observable consequences" + }, + "https://doi.org/10.1029/JZ067i007p02761": { + "usages": [ + "examples/PySDM_examples/Miyake_et_al_1968/__init__.py" + ], + "label": "Friedman et al. 1962 (J. Geophys. Res. 67)", + "title": "Water-vapor exchange between a water droplet and its environment" + }, + "https://doi.org/10.1063/1.436884": { + "usages": [ + "PySDM/physics/isotope_diffusivity_ratios/stewart_1975.py" + ], + "label": "Merlivat 1978 (J. Chem. Phys. 69)", + "title": "Molecular diffusivities of H216O, HD16O and H218O in gases" + }, + "https://doi.org/10.1029/JD092iD04p04171": { + "usages": [ + "PySDM/backends/impl_numba/methods/chemistry_methods.py" + ], + "label": "Lind et al. 1987 (J. Geophys. Res. Atmos. 92)", + "title": "Aqueous phase oxidation of sulfur(IV) by hydrogen peroxide, methylhydroperoxide, and peroxyacetic acid" + }, + "https://digitallibrary.un.org/record/3892725": { + "usages": [ + "PySDM/physics/constants_defaults.py", + "PySDM/physics/trivia.py", + "examples/PySDM_examples/Bolin_1958/__init__.py", + "examples/PySDM_examples/Bolin_1958/table_1.ipynb", + "examples/PySDM_examples/Zaba_et_al/timescales_comparison.ipynb" + ], + "label": "Bolin 1958 (Proc. 2nd UN Intl Conf. Peaceful Uses of Atomic Energy)", + "title": "On the Use of Tritium as a Tracer for Water in Nature" + }, + "http://mi.mathnet.ru/dan27630": { + "usages": [ + "PySDM/dynamics/collisions/collision_kernels/golovin.py", + "tutorials/README.md", + "tutorials/wikipedia/sdm.ipynb" + ], + "label": "Golovin 1963 (Dokl. Akad. Nauk SSSR 148)", + "title": "К вопросу о решении уравнения коагуляции дождевых капель с учетом конденсации (On solving the equation of rain drop coagulation with allowance for condensation)" + }, + "https://doi.org/10.5194/acp-16-1693-2016": { + "usages": ["PySDM/backends/impl_numba/methods/chemistry_methods.py"], + "label": "Hoyle et al. 2016 (Atmos. Chem. Phys. 16)", + "title": "Aqueous phase oxidation of sulphur dioxide by ozone in cloud droplets" + }, + "https://doi.org/10.1029/2024MS004770": { + "usages": [ + "examples/PySDM_examples/Arabas_et_al_2025/__init__.py", + "examples/PySDM_examples/Arabas_et_al_2025/fig_2.ipynb", + "examples/PySDM_examples/Arabas_et_al_2025/fig_A2.ipynb", + "examples/PySDM_examples/Arabas_et_al_2025/figs_10_and_11_and_animations.ipynb", + "examples/PySDM_examples/Arabas_et_al_2025/figs_3_and_7_and_8.ipynb", + "examples/PySDM_examples/Arabas_et_al_2025/figs_5_and_6.ipynb" + ], + "label": "Arabas et al. 2025 (JAMES)", + "title": "Immersion freezing in particle-based aerosol-cloud microphysics: a probabilistic perspective on singular and time-dependent models" + }, + "https://archive.org/details/physicsofclouds0000maso": { + "usages": ["PySDM/physics/drop_growth/mason_1971.py"], + "title": "The Physics of Clouds", + "label": "Mason 1971 (Clarendon Press, Oxford)" + }, + "https://doi.org/10.6028/jres.080A.071": { + "usages": [ + "PySDM/physics/saturation_vapour_pressure/wexler_1976.py", + "PySDM/physics/constants_defaults.py" + ], + "title": "Vapor Pressure Formulation for Water in Range 0 to 100 °C. A Revision", + "label": "Wexler 1976 (J. Res. NBS A Phys. Ch. 80A)" + }, + "https://doi.org/10.1080/10789669.2008.10391032": { + "usages": ["PySDM/physics/constants_defaults.py"], + "title": "A Twenty-First Century Molar Mass for Dry Air", + "label": "Gatley et al. 2008 (HVAC&R Res. 14)" + }, + "https://archive.org/details/shortcourseinclo0000roge_m3k2": { + "usages": [ + "PySDM/physics/constants_defaults.py", + "PySDM/physics/terminal_velocity/rogers_yau.py", + "PySDM/physics/drop_growth/fick.py", + "PySDM/physics/drop_growth/howell_1949.py", + "PySDM/physics/drop_growth/mason_1971.py", + "PySDM/dynamics/terminal_velocity/rogers_and_yau.py", + "PySDM/dynamics/terminal_velocity/power_series.py" + ], + "title": "A short course in clouds physics", + "label": "Rogers & Yau 1989 (Pergamon Press)" + }, + "https://doi.org/10.1080/00046973.1975.9648397": { + "usages": [ + "examples/PySDM_examples/Rogers_1975/__init__.py", + "examples/PySDM_examples/Rogers_1975/fig_1.ipynb" + ], + "title": "An Elementary Parcel Model with Explicit Condensation and Supersaturation", + "label": "Rogers 1975" + }, + "https://doi.org/10.1016/b978-0-444-42225-5.50007-3": { + "usages": ["PySDM/physics/constants_defaults.py"], + "title": "Isotopes in cloud physics: Multiphase and multistage condensation process", + "label": "Jouzel 1986 (The Terrestrial Environment, B)" + }, + "https://doi.org/10.1002/andp.18280890511": { + "usages": ["PySDM/physics/constants_defaults.py"], + "title": "Ueber die Berechnung der Expansivkraft des Wasserdunstes", + "label": "August 1828 (Ann. Phys. Chem. 89)" + }, + "https://doi.org/10.1016/C2013-0-08278-3": { + "usages": ["PySDM/physics/constants_defaults.py"], + "title": "The Physics of Ice", + "label": "Pounder 1965 (Pergamon Press)" + }, + "https://doi.org/10.1175/1520-0469(1976)033%3C1995:TSOIPF%3E2.0.CO;2": { + "usages": ["PySDM/physics/constants_defaults.py"], + "title": "The Survival of Ice Particles Falling from Cirrus Clouds in Subsaturated Air", + "label": "Hall & Pruppacher 1976 (J. Atmos. Sci. 33)" + }, + "https://doi.org/10.1016/S0074-6142(10)09908-0": { + "usages": ["PySDM/physics/constants_defaults.py"], + "title": "Fundamental Equations Governing Cloud Processes", + "label": "Cotton et al. 2011 (International Geophysics 99)" + }, + "https://doi.org/10.1007/978-0-306-48100-0": { + "usages": [ + "PySDM/physics/constants_defaults.py", + "PySDM/physics/diffusion_kinetics/lowe_et_al_2019.py" + ], + "title": "Microphysics of Clouds and Precipitation", + "label": "Pruppacher & Klett 2010 (Springer Dordrecht)" + }, + "https://archive.org/details/0237-pdf-atmospheric-chemistry-and-physics-2nd-ed-j.-seinfeld-s.-pandis-wiley-2006-ww": { + "usages": [ + "PySDM/physics/constants_defaults.py", + "PySDM/physics/constants.py" + ], + "title": "Microphysics of Clouds and Precipitation", + "label": "Seinfeld & Pandis 2006 (Wiley)" + }, + "https://doi.org/10.1038/187857a0": { + "usages": [ + "PySDM/physics/constants_defaults.py", + "PySDM/physics/isotope_temperature_inference/picciotto_et_al_1960.py", + "PySDM/physics/isotope_meteoric_water_line/picciotto_et_al_1960.py", + "tests/unit_tests/physics/test_isotope_temperature_inference.py" + ], + "title": "Isotopic Composition and Temperature of Formation of Antarctic Snows", + "label": "Picciotto et al. 1960 (Nature 187)" + }, + "https://doi.org/10.1016/0019-1035(80)90173-6": { + "usages": [ + "examples/PySDM_examples/Toon_et_al_1980/fig_1.ipynb", + "PySDM/physics/hydrostatics/variable_g_isothermal.py" + ], + "title": "A physical model of Titan’s clouds", + "label": "Toon et al. 1980 (Icarus 43)" + }, + "https://doi.org/10.1016/B978-0-444-42225-5.50008-5": { + "usages": [ + "examples/PySDM_examples/Gonfiantini_1986/fig_3_1.ipynb", + "examples/PySDM_examples/Gonfiantini_1986/__init__.py" + ], + "title": "Environmental isotopes in lake studies", + "label": "Gonfiantini 1986 (The Terrestrial Environment, B)" + }, + "https://doi.org/10.1007/978-94-017-1497-6": { + "usages": [ + "PySDM/physics/isotope_ventilation_ratio/brutsaert_1982.py" + ], + "title": "Evaporation into the Atmosphere: Theory, History, and Applications", + "label": "Brutsaert 1982 (Springer Netherlands)" + }, + "https://doi.org/10.1029/JC080i009p01133": { + "usages": [ + "PySDM/physics/isotope_diffusivity_ratios/stewart_1975.py", + "PySDM/physics/isotope_kinetic_fractionation_factors/jouzel_and_merlivat_1984.py", + "examples/PySDM_examples/Stewart_1975/__init__.py", + "examples/PySDM_examples/Stewart_1975/fig_1.ipynb" + ], + "title": "Stable Isotope Fractionation Due to Evaporation and Isotopic Exchange of Falling Waterdrops: Applications to Atmospheric Processes and Evaporation of Lakes", + "label": "Stewart 1975 (J. Geophys. Res.)" + }, + "https://doi.org/10.1175/1520-0469(1951)008%3C0071:TETATR%3E2.0.CO;2": { + "usages": [ + "PySDM/physics/isotope_relaxation_timescale/jouzel_et_al_1975.py", + "PySDM/physics/isotope_relaxation_timescale/miyake_et_al_1968.py", + "examples/PySDM_examples/Kinzer_And_Gunn_1951/table_1_and_2.py", + "examples/PySDM_examples/Kinzer_And_Gunn_1951/__init__.py" + ], + "title": "The Evaporation, Temperature and Thermal Relaxation-Time of Freely Falling Waterdrops", + "label": "Kinzer & Gunn 1951 (J. Meteor.)" + }, + "https://doi.org/10.1029/2001JD000470": { + "usages": [ + "PySDM/physics/constants_defaults.py" + ], + "title": "A parameterization of cirrus cloud formation: Homogeneous freezing of supercooled aerosols", + "label": "Kaercher & Lohmann 2002 (J. Geophys. Res. Atmos 107)" + }, + "https://doi.org/10.5194/acp-9-685-2009": { + "usages": [ + "PySDM/dynamics/terminal_velocity/columnar_ice_crystal.py", + "PySDM/physics/constants_defaults.py", + "PySDM/physics/particle_shape_and_density/columnar_ice.py", + "PySDM/physics/particle_shape_and_density/porous_spheroids.py", + "PySDM/physics/terminal_velocity_ice/columnar_ice_crystal.py", + "tests/unit_tests/dynamics/test_terminal_velocity.py", + "tests/unit_tests/dynamics/test_vapour_deposition_on_ice.py", + "tests/unit_tests/physics/test_particle_shape_and_density.py" + ], + "title": "Modelling of cirrus clouds – Part 1a: Model description and validation", + "label": "Spichtinger & Gierens 2009 (Atmos. Chem. Phys. 9)" + }, + "https://doi.org/10.1175/1520-0469(1949)006%3C0134:TGOCDI%3E2.0.CO;2": { + "usages": [ + "PySDM/backends/impl_numba/methods/deposition_methods.py", + "PySDM/physics/drop_growth/howell_1949.py" + ], + "title": "The Growth of Cloud Drops in Uniformly Cooled Air", + "label": "Howell 1949 (J. Atmos. Sci.)" + }, + "https://doi.org/10.1175/1520-0469%281971%29028%3C1455:AWTIOT%3E2.0.CO;2": { + "usages": [ + "PySDM/physics/constants_defaults.py", + "PySDM/physics/ventilation/pruppacher_rasmussen_1979.py" + ], + "title": "A Wind Tunnel Investigation of the Rate of Evaporation of Small Water Drops Falling at Terminal Velocity in Air", + "label": "Beard & Pruppacher 1971 (J. Atmos. Sci. 28)" + }, + "https://doi.org/10.1029/JD089iD07p11749": { + "usages": [ + "PySDM/physics/isotope_kinetic_fractionation_factors/jouzel_and_merlivat_1984.py", + "examples/PySDM_examples/Jouzel_and_Merlivat_1984/__init__.py", + "examples/PySDM_examples/Jouzel_and_Merlivat_1984/fig_8_9.ipynb", + "examples/PySDM_examples/Jouzel_and_Merlivat_1984/thermodynamic_profiles.py", + "tests/unit_tests/physics/test_isotope_kinetic_fractionation_factors.py", + "examples/PySDM_examples/Fisher_1991/__init__.py" + ], + "title": "Deuterium and oxygen 18 in precipitation: Modeling of the isotopic effects during snow formation", + "label": "Jouzel & Merlivat 1984 (J. Geophys. Res. Atmos. 89)" + }, + "https://web.archive.org/web/20160322221332/https://hydrology.nl/images/docs/ihp/Mook_III.pdf": { + "usages": [ + "PySDM/physics/isotope_kinetic_fractionation_factors/craig_gordon.py" + ], + "title": "Environmental isotopes in the hydrological cycle - Principles and applications: Vol III Surface water", + "label": "Rozanski_et_al_2001 (UNESCO, ed. Mook)" + }, + "https://web.archive.org/web/20200729203147/https://nucleus.iaea.org/rpst/documents/VSMOW_SLAP.pdf": { + "usages": [ + "PySDM/physics/constants_defaults.py" + ], + "title": "IAEA Reference Sheet for International Measurement Standards: VSMOW & SLAP", + "label": "IAEA 2006" + }, + "https://doi.org/10.3402/tellusb.v43i5.15414": { + "usages": [ + "examples/PySDM_examples/Fisher_1991/fig_2.ipynb", + "examples/PySDM_examples/Fisher_1991/__init__.py" + ], + "title": "Remarks on the deuterium excess in precipitation in cold regions", + "label": "Fisher 1991 (Tellus B)" + }, + "https://doi.org/10.1029/JC080i036p05015": { + "usages": [ + "PySDM/physics/isotope_relaxation_timescale/jouzel_et_al_1975.py", + "examples/PySDM_examples/Zaba_et_al/timescales_comparison.ipynb" + ], + "title": "Isotopic study of hail", + "label": "Jouzel et al. 1975 (J. Geophys. Res. 80)" + }, + "https://doi.org/10.5194/acp-23-2035-2023": { + "usages": [ + "PySDM/physics/constants_defaults.py", + "PySDM/physics/diffusion_ice_capacity/columnar.py", + "PySDM/physics/homogeneous_ice_nucleation_rate/koop_corr.py", + "examples/PySDM_examples/Spichtinger_et_al_2023/__init__.py", + "examples/PySDM_examples/Spichtinger_et_al_2023/data/reference_bulk.py", + "examples/PySDM_examples/Spichtinger_et_al_2023/fig_B1.ipynb", + "tests/unit_tests/physics/test_homogeneous_nucleation_rates.py" + ], + "title": "Impact of formulations of the homogeneous nucleation rate on ice nucleation events in cirrus", + "label": "Spichtinger et al. 2023 (Atmos. Chem. Phys. 23)" + }, + "https://doi.org/10.1038/35020537": { + "usages": [ + "PySDM/physics/constants_defaults.py", + "PySDM/physics/homogeneous_ice_nucleation_rate/koop.py" + ], + "title": "Water activity as the determinant for homogeneous ice nucleation in aqueous solutions", + "label": "Koop et al. 2000 (Nature 406)" + }, + "https://doi.org/10.1063/1.4962355": { + "usages": [ + "PySDM/physics/constants_defaults.py", + "PySDM/physics/homogeneous_ice_nucleation_rate/koop_murray.py" + ], + "title": "A physically constrained classical description of the homogeneous nucleation of ice in water", + "label": "Koop and Murray 2016 (J. Chem. Phys. 145)" + }, + "https://doi.org/10.5194/gmd-16-6211-2023": { + "title": "Overcoming computational challenges to realize meter- to submeter-scale resolution in cloud simulations using the super-droplet method", + "label": "Matsushima et al. 2023 (Geosci. Model Dev. 16)", + "usages": [ + "PySDM/initialisation/sampling/spectral_sampling.py", + "examples/PySDM_examples/Matsushima_et_al_2023/figure_1.ipynb", + "examples/PySDM_examples/Matsushima_et_al_2023/__init__.py" + ] + }, + "https://doi.org/10.48550/arXiv.2509.05536": { + "usages": [ + "examples/PySDM_examples/Ware_et_al_2025/__init__.py", + "examples/PySDM_examples/Ware_et_al_2025/fig_10.ipynb" + ], + "title": "Adaptive time-stepping for the Super-Droplet Method Monte Carlo collision-coalescence scheme", + "label": "Ware et al. 2025 (arXiv)" + }, + "https://doi.org/10.1175/1520-0469(2000)057%3C0916:CCTV%3E2.0.CO;2": { + "usages": [ + "PySDM/dynamics/terminal_velocity/columnar_ice_crystal.py", + "PySDM/physics/terminal_velocity_ice/columnar_ice_crystal.py" + ], + "label": "Heymsfield & Iaquinta 2000 (J. Atmos. Sci. 57)", + "title": "Cirrus Crystal Terminal Velocities" + }, + "https://doi.org/10.1016/j.atmosres.2005.12.009": { + "usages": [ + "PySDM/dynamics/terminal_velocity/columnar_ice_crystal.py", + "PySDM/physics/terminal_velocity_ice/columnar_ice_crystal.py" + ], + "label": "Barthazy and Schefold 2006 (Atmos. Res. 82)", + "title": "Fall velocity of snowflakes of different riming degree and crystal types" + }, + "https://www.ap.uj.edu.pl/diplomas/attachments/file/download/125485": { + "usages": [ + "PySDM/backends/impl_numba/methods/condensation_methods.py", + "PySDM/dynamics/condensation.py", + "PySDM/products/condensation/condensation_timestep.py" + ], + "label": "Bartman 2020 (MSc thesis)", + "title": "PySDM v1.0: Pythonic particle-based cloud microphysics package" + }, + "https://web.archive.org/web/20220629123450/https://web.gps.caltech.edu/~als/research-articles/other_stuff/hayes-2004-3.pdf" : { + "usages": [ + "tests/unit_tests/physics/test_constants.py" + ], + "label": "Hayes 2004", + "title": "An Introduction to Isotopic Calculations" + } +} diff --git a/PySDM/source/docs/generate_html.py b/PySDM/source/docs/generate_html.py new file mode 100644 index 0000000000000000000000000000000000000000..8aebb1b88b02940b7bd4f012d01f5ac8997bf9ea --- /dev/null +++ b/PySDM/source/docs/generate_html.py @@ -0,0 +1,158 @@ +""" +Script to generate docs based on given input directory (first argument) +and output directory (second argument), includes bibliography logic +""" + +import json +import os +import re +import subprocess +import sys + +import nbformat + +# +from git.cmd import Git + + +def find_files(path_to_folder_from_project_root=".", file_extension=None): + """ + Returns all files in a current git repo. + The list of returned files may be filtered with `file_extension` param. + """ + all_files = [ + path + for path in Git( + Git(path_to_folder_from_project_root).rev_parse("--show-toplevel") + ) + .ls_files() + .split("\n") + if os.path.isfile(path) + ] + if file_extension is not None: + return list(filter(lambda path: path.endswith(file_extension), all_files)) + + return all_files + + +# + + +def generate_badges_md_files(): + """extracts badge-containing cell from each notebook and writes it to an .md file""" + for notebook_path in find_files("examples/PySDM_examples", ".ipynb"): + with open(notebook_path, encoding="utf8") as fin: + with open(notebook_path + ".badges.md", "w", encoding="utf8") as fout: + fout.write(nbformat.read(fin, nbformat.NO_CONVERT).cells[0].source) + + +def read_urls_from_json(code_path): + """loads bibliography data from .json file""" + bibliography_json_path = f"{code_path}/docs/bibliography.json" + urls_from_json = {} + if os.path.exists(bibliography_json_path): + with open(bibliography_json_path, "r", encoding="utf8") as fin: + urls_from_json = json.load(fin) + return urls_from_json + + +def check_urls(urls_from_json): + """checks if all bib entries are referenced from code and vice versa""" + found_urls = [] + for extension in (".md", ".ipynb", ".py"): + for full_path in find_files(file_extension=extension): + with open(full_path, "r", encoding="utf-8") as fin: + text = fin.read() + for pattern in ( + r"\b(https://doi\.org/10[.][0-9]{4,}(?:[.][0-9]+)*/(?:(?![\"&\'<>^\\])\S)+)\b", + r"\b(https://digitallibrary\.un\.org/record/(?:[0-9])+)\b", + r"\b(http://mi\.mathnet\.ru/dan(?:[0-9])+)\b", + r"\b(https://archive\.org/details/(?:[0-9a-z_\.-])+)\b", + r"\b(https://web\.archive\.org/web/(?:[0-9])+/https://(?:[0-9a-zA-Z_\.\-/~%])+)\b", + r"\b(https://www\.ap\.uj\.edu\.pl/diplomas/attachments/file/download/(?:[0-9])+)\b", + ): + urls = re.findall(pattern, text) + if urls: + found_urls.extend((full_path, url) for url in urls) + + unique_urls_found = {url for _, url in found_urls} + unique_urls_read = set(urls_from_json.keys()) + + for url in unique_urls_found: + assert url in unique_urls_read, f"{url} not found in the json file" + for url in unique_urls_read: + assert ( + url in unique_urls_found + ), f"{url} not referenced in the code (or not recognized url pattern)" + + url_usages_found = { + url: sorted({path for path, d in found_urls if d == url}) + for url in unique_urls_found + } + for url in unique_urls_read: + assert set(url_usages_found[url]) == set( + sorted(urls_from_json[url]["usages"]) + ), ( + f"{url} usages mismatch (please fix docs/bibliography.json):\n" + f"\texpected: {url_usages_found[url]}\n" + f"\tactual: {urls_from_json[url]['usages']}" + ) + + +def create_references_html(urls_from_json, code_path): + """writes HTML file with the reference list""" + with open( + f"{code_path}/docs/templates/bibliography.html", "w", encoding="utf8" + ) as fout: + fout.write("
    \n") + for url, data in sorted( + urls_from_json.items(), key=lambda item: item[1]["label"].lower() + ): + fout.write("
  1. \n") + fout.write( + f'{data["label"]}: "{data["title"]}"\n' + ) + fout.write('\n") + fout.write("
\n") + + +def run_pdoc(code_path, out_path): + """generates docs in HTML format by executing pdoc""" + subprocess.run( + [ + sys.executable, + "-We", + "-m", + "pdoc", + "-o", + f"{out_path}/html", + f"{code_path}/PySDM", + f"{code_path}/examples/PySDM_examples", + "-t", + f"{code_path}/docs/templates", + "--math", + "--mermaid", + ], + env={**os.environ, "PDOC_ALLOW_EXEC": "1"}, + check=True, + ) + + +def _main(): + assert len(sys.argv) == 3, f"usage: {sys.argv[0]} code_path out_path" + code_path, out_path = sys.argv[1], sys.argv[2] + generate_badges_md_files() + urls_from_json = read_urls_from_json(code_path) + check_urls(urls_from_json) + create_references_html(urls_from_json, code_path) + run_pdoc(code_path, out_path) + + +if __name__ == "__main__": + _main() diff --git a/PySDM/source/docs/logos/pysdm_logo.png b/PySDM/source/docs/logos/pysdm_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..a356529d6d265249d45a37b02f3dad2029046338 Binary files /dev/null and b/PySDM/source/docs/logos/pysdm_logo.png differ diff --git a/PySDM/source/docs/logos/pysdm_logo.svg b/PySDM/source/docs/logos/pysdm_logo.svg new file mode 100644 index 0000000000000000000000000000000000000000..65dae40a1ba2c9231b56f13ce23647797b60ccd8 --- /dev/null +++ b/PySDM/source/docs/logos/pysdm_logo.svg @@ -0,0 +1 @@ + diff --git a/PySDM/source/docs/markdown/pysdm_landing.md b/PySDM/source/docs/markdown/pysdm_landing.md new file mode 100644 index 0000000000000000000000000000000000000000..fb742ff9fc35fa716946592d440a2d56b8499cbd --- /dev/null +++ b/PySDM/source/docs/markdown/pysdm_landing.md @@ -0,0 +1,761 @@ +# Introduction + +pysdm logo + +PySDM offers a set of building blocks for development of atmospheric cloud +simulation systems revolving around the particle-based microphysics modelling concept +and the Super-Droplet Method algorithm ([Shima et al. 2009](https://doi.org/10.1002/qj.441)) +for numerically tackling the probabilistic representation of particle coagulation. + +For details on PySDM dependencies and installation procedures, see +[project docs homepage](https://open-atmos.github.io/PySDM). + +Below, a set of basic usage examples in **Python**, **Julia** and **Matlab** is provided +as a tutorial +More elaborate examples reproducing results from literature, engineered in Python and accompanied by Jupyter +notebooks are maintained in the +[PySDM-examples package](https://open-atmos.github.io/PySDM/PySDM_examples). + +## Note on physical units and dimensional analysis + +Throughout the entire PySDM codebase, all values are stored in **SI units**, and all physics formulae expect **SI values as arguments**. +Otherwise, it is a bug - please report. + +### Initialisation of physical constants +Physical constants are initialised using the ``PySDM.physics.si`` object as follows: +
+Julia (click to expand) + +```Julia +using Pkg +Pkg.add("PyCall") +using PyCall +si = pyimport("PySDM.physics").si + +temperature = 300 * si.K +pressure = 1000 * si.hPa +vapour_mixing_ratio = 10 * si.g / si.kg +``` +
+
+Matlab (click to expand) + +```Matlab +si = py.importlib.import_module('PySDM.physics').si; + +temperature = 300 * si.K; +pressure = 1000 * si.hPa; +vapour_mixing_ratio = 10 * si.g / si.kg; +``` +
+
+Python (click to expand) + +```Python +from PySDM.physics import si + +temperature = 300 * si.K +pressure = 1000 * si.hPa +vapour_mixing_ratio = 10 * si.g / si.kg +``` +
+ +_Note: The actual numerical values of the above variables are `300`, `100000` and `.01`, respectively._ + +### Output and plotting +The one exception to the **only-SI** rule is when outputting simulation product data or plotting. +In these cases, non-SI units should be always indicated in variable names, + e.g.: + - `temperature_C` for Celsius, + - `RH_percent` for percent values, + - `pressure_hPa` to use hectopascals. + +However, such conversions are best to be done + on-the-fly avoiding storage of non-SI values in variables (e.g., `plot(pressure / si.hPa)`). + +### Dimensional Analysis +By default, the `si` object contains bare multipliers corresponding to SI prefixes. +For testing purposes, the [``DimensionalAnalysis``](https://open-atmos.github.io/PySDM/PySDM/physics/dimensional_analysis.html#DimensionalAnalysis) +context manager can be used to inject an instance of [Pint](https://pint.readthedocs.io/) +unit registry as `si`, thus enabling dimensional analysis of PySDM codebase. +For an example, see the [dimensional analysis HOWTO](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/_HOWTOs/dimensional_analysis.ipynb). + +# Tutorials + +## Hello-world coalescence example in Python, Julia and Matlab + +In order to depict the PySDM API with a practical example, the following + listings provide sample code roughly reproducing the + Figure 2 from [Shima et al. 2009 paper](http://doi.org/10.1002/qj.441) + using PySDM from Python, Julia and Matlab. +It is a [`Coalescence`](https://open-atmos.github.io/PySDM/PySDM/dynamics/collisions/collision.html#Coalescence)-only set-up in which the initial particle size + spectrum is [`Exponential`](https://open-atmos.github.io/PySDM/PySDM/initialisation/spectra/exponential.html#Exponential) and is deterministically sampled to match + the condition of each super-droplet having equal initial multiplicity: +
+Julia (click to expand) + +```Julia +Pkg.add("Plots") + +ConstantMultiplicity = pyimport("PySDM.initialisation.sampling.spectral_sampling").ConstantMultiplicity +Exponential = pyimport("PySDM.initialisation.spectra").Exponential + +n_sd = 2^15 +initial_spectrum = Exponential(norm_factor=8.39e12, scale=1.19e5 * si.um^3) +attributes = Dict() +attributes["volume"], attributes["multiplicity"] = ConstantMultiplicity(spectrum=initial_spectrum).sample_deterministic(n_sd) +``` +
+
+Matlab (click to expand) + +```Matlab +si = py.importlib.import_module('PySDM.physics').si; +ConstantMultiplicity = py.importlib.import_module('PySDM.initialisation.sampling.spectral_sampling').ConstantMultiplicity; +Exponential = py.importlib.import_module('PySDM.initialisation.spectra').Exponential; + +n_sd = 2^15; +initial_spectrum = Exponential(pyargs(... + 'norm_factor', 8.39e12, ... + 'scale', 1.19e5 * si.um ^ 3 ... +)); +tmp = ConstantMultiplicity(initial_spectrum).sample_deterministic(int32(n_sd)); +attributes = py.dict(pyargs('volume', tmp{1}, 'multiplicity', tmp{2})); +``` +
+
+Python (click to expand) + +```Python +from PySDM.initialisation.sampling.spectral_sampling import ConstantMultiplicity +from PySDM.initialisation.spectra.exponential import Exponential + +n_sd = 2 ** 15 +initial_spectrum = Exponential(norm_factor=8.39e12, scale=1.19e5 * si.um ** 3) +attributes = {} +attributes['volume'], attributes['multiplicity'] = ConstantMultiplicity(initial_spectrum).sample_deterministic(n_sd) +``` +
+ +The key element of the PySDM interface is the [``Particulator``](https://open-atmos.github.io/PySDM/PySDM/particulator.html#Particulator) + class instances of which are used to manage the system state and control the simulation. +Instantiation of the [``Particulator``](https://open-atmos.github.io/PySDM/PySDM/particulator.html#Particulator) class is handled by the [``Builder``](https://open-atmos.github.io/PySDM/PySDM/builder.html#Builder) + as exemplified below: +
+Julia (click to expand) + +```Julia +Builder = pyimport("PySDM").Builder +Box = pyimport("PySDM.environments").Box +Coalescence = pyimport("PySDM.dynamics").Coalescence +Golovin = pyimport("PySDM.dynamics.collisions.collision_kernels").Golovin +CPU = pyimport("PySDM.backends").CPU +ParticleVolumeVersusRadiusLogarithmSpectrum = pyimport("PySDM.products").ParticleVolumeVersusRadiusLogarithmSpectrum + +radius_bins_edges = 10 .^ range(log10(10*si.um), log10(5e3*si.um), length=32) + +env = Box(dt=1 * si.s, dv=1e6 * si.m^3) +builder = Builder(n_sd=n_sd, backend=CPU(), environment=env) +builder.add_dynamic(Coalescence(collision_kernel=Golovin(b=1.5e3 / si.s))) +products = [ParticleVolumeVersusRadiusLogarithmSpectrum(radius_bins_edges=radius_bins_edges, name="dv/dlnr")] +particulator = builder.build(attributes, products) +``` +
+
+Matlab (click to expand) + +```Matlab +Builder = py.importlib.import_module('PySDM').Builder; +Box = py.importlib.import_module('PySDM.environments').Box; +Coalescence = py.importlib.import_module('PySDM.dynamics').Coalescence; +Golovin = py.importlib.import_module('PySDM.dynamics.collisions.collision_kernels').Golovin; +CPU = py.importlib.import_module('PySDM.backends').CPU; +ParticleVolumeVersusRadiusLogarithmSpectrum = py.importlib.import_module('PySDM.products').ParticleVolumeVersusRadiusLogarithmSpectrum; + +radius_bins_edges = logspace(log10(10 * si.um), log10(5e3 * si.um), 32); + +env = Box(pyargs('dt', 1 * si.s, 'dv', 1e6 * si.m ^ 3)); +builder = Builder(pyargs('n_sd', int32(n_sd), 'backend', CPU(), 'environment', env)); +builder.add_dynamic(Coalescence(pyargs('collision_kernel', Golovin(1.5e3 / si.s)))); +products = py.list({ ParticleVolumeVersusRadiusLogarithmSpectrum(pyargs( ... + 'radius_bins_edges', py.numpy.array(radius_bins_edges), ... + 'name', 'dv/dlnr' ... +)) }); +particulator = builder.build(attributes, products); +``` +
+
+Python (click to expand) + +```Python +import numpy as np +from PySDM import Builder +from PySDM.environments import Box +from PySDM.dynamics import Coalescence +from PySDM.dynamics.collisions.collision_kernels import Golovin +from PySDM.backends import CPU +from PySDM.products import ParticleVolumeVersusRadiusLogarithmSpectrum + +radius_bins_edges = np.logspace(np.log10(10 * si.um), np.log10(5e3 * si.um), num=32) + +env = Box(dt=1 * si.s, dv=1e6 * si.m ** 3) +builder = Builder(n_sd=n_sd, backend=CPU(), environment=env) +builder.add_dynamic(Coalescence(collision_kernel=Golovin(b=1.5e3 / si.s))) +products = [ParticleVolumeVersusRadiusLogarithmSpectrum(radius_bins_edges=radius_bins_edges, name='dv/dlnr')] +particulator = builder.build(attributes, products) +``` +
+ +The ``backend`` argument may be set to ``CPU`` or ``GPU`` + what translates to choosing the multi-threaded backend or the + GPU-resident computation mode, respectively. +The employed [`Box`](https://open-atmos.github.io/PySDM/PySDM/environments/box.html#Box) environment corresponds to a zero-dimensional framework + (particle positions are not considered). +The vectors of particle multiplicities ``n`` and particle volumes ``v`` are + used to initialise super-droplet attributes. +The [`Coalescence`](https://open-atmos.github.io/PySDM/PySDM/dynamics/collisions/collision.html#Coalescence) + Monte-Carlo algorithm (Super Droplet Method) is registered as the only + dynamic in the system. +Finally, the [`build()`](https://open-atmos.github.io/PySDM/PySDM/builder.html#Builder.build) method is used to obtain an instance + of [`Particulator`](https://open-atmos.github.io/PySDM/PySDM/particulator.html#Particulator) which can then be used to control time-stepping and + access simulation state. + +The [`run(nt)`](https://open-atmos.github.io/PySDM/PySDM/particulator.html#Particulator.run) method advances the simulation by ``nt`` timesteps. +In the listing below, its usage is interleaved with plotting logic + which displays a histogram of particle mass distribution + at selected timesteps: +
+Julia (click to expand) + +```Julia +using Plots; gr() + +for step = 0:1200:3600 + particulator.run(step - particulator.n_steps) + plot!( + radius_bins_edges[1:end-1] / si.um, + particulator.formulae.particle_shape_and_density.volume_to_mass( + particulator.products["dv/dlnr"].get()[:] + )/ si.g, + linetype=:steppost, + xaxis=:log, + xlabel="particle radius [µm]", + ylabel="dm/dlnr [g/m^3/(unit dr/r)]", + label="t = $step s" + ) +end +savefig("plot.svg") +``` +
+
+Matlab (click to expand) + +```Matlab +for step = 0:1200:3600 + particulator.run(int32(step - particulator.n_steps)); + x = radius_bins_edges / si.um; + y = particulator.formulae.particle_shape_and_density.volume_to_mass( ... + particulator.products{"dv/dlnr"}.get() ... + ) / si.g; + stairs(... + x(1:end-1), ... + double(py.array.array('d',py.numpy.nditer(y))), ... + 'DisplayName', sprintf("t = %d s", step) ... + ); + hold on +end +hold off +set(gca,'XScale','log'); +xlabel('particle radius [µm]') +ylabel("dm/dlnr [g/m^3/(unit dr/r)]") +legend() +``` +
+
+Python (click to expand) + +```Python +from matplotlib import pyplot + +for step in [0, 1200, 2400, 3600]: + particulator.run(step - particulator.n_steps) + pyplot.step( + x=radius_bins_edges[:-1] / si.um, + y=particulator.formulae.particle_shape_and_density.volume_to_mass( + particulator.products['dv/dlnr'].get()[0] + ) / si.g, + where='post', label=f"t = {step}s" + ) + +pyplot.xscale('log') +pyplot.xlabel('particle radius [µm]') +pyplot.ylabel("dm/dlnr [g/m$^3$/(unit dr/r)]") +pyplot.legend() +pyplot.savefig('readme.png') +``` +
+ +The resultant plot (generated with the Python code) looks as follows: + +![plot](https://github.com/open-atmos/PySDM/releases/download/tip/readme.png) + +The component submodules used to create this simulation are visualized below: +```mermaid + graph + COAL[":Coalescence"] --->|passed as arg to| BUILDER_ADD_DYN(["Builder.add_dynamic()"]) + BUILDER_INSTANCE["builder :Builder"] -...-|has a method| BUILDER_BUILD(["Builder.build()"]) + ATTRIBUTES[attributes: dict] -->|passed as arg to| BUILDER_BUILD + N_SD["n_sd :int"] ---->|passed as arg to| BUILDER_INIT + BUILDER_INIT(["Builder.__init__()"]) --->|instantiates| BUILDER_INSTANCE + BUILDER_INSTANCE -..-|has a method| BUILDER_ADD_DYN(["Builder.add_dynamic()"]) + ENV_INIT(["Box.__init__()"]) -->|instantiates| ENV + DT[dt :float] -->|passed as arg to| ENV_INIT + DV[dv :float] -->|passed as arg to| ENV_INIT + ENV[":Box"] -->|passed as arg to| BUILDER_INIT + B["b: float"] --->|passed as arg to| KERNEL_INIT(["Golovin.__init__()"]) + KERNEL_INIT -->|instantiates| KERNEL + KERNEL[collision_kernel: Golovin] -->|passed as arg to| COAL_INIT(["Coalesncence.__init__()"]) + COAL_INIT -->|instantiates| COAL + PRODUCTS[products: list] ----->|passed as arg to| BUILDER_BUILD + NORM_FACTOR[norm_factor: float]-->|passed as arg to| EXP_INIT + SCALE[scale: float]-->|passed as arg to| EXP_INIT + EXP_INIT(["Exponential.__init__()"]) -->|instantiates| IS + IS["initial_spectrum :Exponential"] -->|passed as arg to| CM_INIT + CM_INIT(["ConstantMultiplicity.__init__()"]) -->|instantiates| CM_INSTANCE + CM_INSTANCE[":ConstantMultiplicity"] -.-|has a method| SAMPLE + SAMPLE(["ConstantMultiplicity.sample_deterministic()"]) -->|returns| n + SAMPLE -->|returns| volume + n -->|added as element of| ATTRIBUTES + PARTICULATOR_INSTANCE -.-|has a method| PARTICULATOR_RUN(["Particulator.run()"]) + volume -->|added as element of| ATTRIBUTES + BUILDER_BUILD -->|returns| PARTICULATOR_INSTANCE["particulator :Particulator"] + PARTICULATOR_INSTANCE -.-|has a field| PARTICULATOR_PROD(["Particulator.products:dict"]) + BACKEND_INSTANCE["backend :CPU"] ---->|passed as arg to| BUILDER_INIT + PRODUCTS -.-|accessible via| PARTICULATOR_PROD + NP_LOGSPACE(["np.logspace()"]) -->|returns| EDGES + EDGES[radius_bins_edges: np.ndarray] -->|passed as arg to| SPECTRUM_INIT + SPECTRUM_INIT["ParticleVolumeVersusRadiusLogarithmSpectrum.__init__()"] -->|instantiates| SPECTRUM + SPECTRUM[":ParticleVolumeVersusRadiusLogarithmSpectrum"] -->|added as element of| PRODUCTS + + click COAL "https://open-atmos.github.io/PySDM/PySDM/dynamics/collisions/collision.html#Coalescence" + click BUILDER_INSTANCE "https://open-atmos.github.io/PySDM/PySDM/builder.html" + click BUILDER_INIT "https://open-atmos.github.io/PySDM/PySDM/builder.html" + click BUILDER_ADD_DYN "https://open-atmos.github.io/PySDM/PySDM/builder.html" + click ENV_INIT "https://open-atmos.github.io/PySDM/PySDM/environments.html" + click ENV "https://open-atmos.github.io/PySDM/PySDM/environments.html" + click KERNEL_INIT "https://open-atmos.github.io/PySDM/PySDM/dynamics/collisions/collision_kernels.html" + click KERNEL "https://open-atmos.github.io/PySDM/PySDM/dynamics/collisions/collision_kernels.html" + click EXP_INIT "https://open-atmos.github.io/PySDM/PySDM/initialisation/spectra.html" + click IS "https://open-atmos.github.io/PySDM/PySDM/initialisation/spectra.html" + click CM_INIT "https://open-atmos.github.io/PySDM/PySDM/initialisation/sampling/spectral_sampling.html" + click CM_INSTANCE "https://open-atmos.github.io/PySDM/PySDM/initialisation/sampling/spectral_sampling.html" + click SAMPLE "https://open-atmos.github.io/PySDM/PySDM/initialisation/sampling/spectral_sampling.html" + click PARTICULATOR_INSTANCE "https://open-atmos.github.io/PySDM/PySDM/particulator.html" + click BACKEND_INSTANCE "https://open-atmos.github.io/PySDM/PySDM/backends/numba.html" + click BUILDER_BUILD "https://open-atmos.github.io/PySDM/PySDM/builder.html" + click NP_LOGSPACE "https://numpy.org/doc/stable/reference/generated/numpy.logspace.html" + click SPECTRUM_INIT "https://open-atmos.github.io/PySDM/PySDM/products/size_spectral/particle_volume_versus_radius_logarithm_spectrum.html" + click SPECTRUM "https://open-atmos.github.io/PySDM/PySDM/products/size_spectral/particle_volume_versus_radius_logarithm_spectrum.html" +``` +## Hello-world condensation example in Python, Julia and Matlab + +In the following example, a condensation-only setup is used with the adiabatic +[`Parcel`](https://open-atmos.github.io/PySDM/PySDM/environments/parcel.html) environment. +An initial [`Lognormal`](https://open-atmos.github.io/PySDM/PySDM/initialisation/spectra/lognormal.html#Lognormal) +spectrum of dry aerosol particles is first initialised to equilibrium wet size for the given +initial humidity. +Subsequent particle growth due to [`Condensation`](https://open-atmos.github.io/PySDM/PySDM/dynamics/condensation.html) of water vapour (coupled with the release of latent heat) +causes a subset of particles to activate into cloud droplets. +Results of the simulation are plotted against vertical +[`ParcelDisplacement`](https://open-atmos.github.io/PySDM/PySDM/products/parcel/parcel_displacement.html) +and depict the evolution of +[`PeakSaturation`](https://open-atmos.github.io/PySDM/PySDM/products/condensation/peak_saturation.html), +[`EffectiveRadius`](https://open-atmos.github.io/PySDM/PySDM/products/size_spectral/effective_radius.html), +[`ParticleConcentration`](https://open-atmos.github.io/PySDM/PySDM/products/size_spectral/particle_concentration.html#ParticleConcentration) +and the +[`WaterMixingRatio `](https://open-atmos.github.io/PySDM/PySDM/products/size_spectral/water_mixing_ratio.html). + +
+Julia (click to expand) + +```Julia +using PyCall +using Plots; gr() +si = pyimport("PySDM.physics").si +spectral_sampling = pyimport("PySDM.initialisation.sampling").spectral_sampling +discretise_multiplicities = pyimport("PySDM.initialisation").discretise_multiplicities +Lognormal = pyimport("PySDM.initialisation.spectra").Lognormal +equilibrate_wet_radii = pyimport("PySDM.initialisation.hygroscopic_equilibrium").equilibrate_wet_radii +CPU = pyimport("PySDM.backends").CPU +AmbientThermodynamics = pyimport("PySDM.dynamics").AmbientThermodynamics +Condensation = pyimport("PySDM.dynamics").Condensation +Parcel = pyimport("PySDM.environments").Parcel +Builder = pyimport("PySDM").Builder +Formulae = pyimport("PySDM").Formulae +products = pyimport("PySDM.products") + +env = Parcel( + dt=.25 * si.s, + mass_of_dry_air=1e3 * si.kg, + p0=1122 * si.hPa, + initial_water_vapour_mixing_ratio=20 * si.g / si.kg, + T0=300 * si.K, + w= 2.5 * si.m / si.s +) +spectrum = Lognormal(norm_factor=1e4/si.mg, m_mode=50*si.nm, s_geom=1.4) +kappa = .5 * si.dimensionless +cloud_range = (.5 * si.um, 25 * si.um) +output_interval = 4 +output_points = 40 +n_sd = 256 + +formulae = Formulae() +builder = Builder(backend=CPU(formulae), n_sd=n_sd, environment=env) +builder.add_dynamic(AmbientThermodynamics()) +builder.add_dynamic(Condensation()) + +r_dry, specific_concentration = spectral_sampling.Logarithmic(spectrum).sample_deterministic(n_sd) +v_dry = formulae.trivia.volume(radius=r_dry) +r_wet = equilibrate_wet_radii(r_dry=r_dry, environment=builder.particulator.environment, kappa_times_dry_volume=kappa * v_dry) + +attributes = Dict() +attributes["multiplicity"] = discretise_multiplicities(specific_concentration * env.mass_of_dry_air) +attributes["dry volume"] = v_dry +attributes["kappa times dry volume"] = kappa * v_dry +attributes["volume"] = formulae.trivia.volume(radius=r_wet) + +particulator = builder.build(attributes, products=[ + products.PeakSaturation(name="S_max_percent", unit="%"), + products.EffectiveRadius(name="r_eff", unit="um", radius_range=cloud_range), + products.ParticleConcentration(name="n_c_cm3", unit="cm^-3", radius_range=cloud_range), + products.WaterMixingRatio(name="liquid water mixing ratio", unit="g/kg", radius_range=cloud_range), + products.ParcelDisplacement(name="z") +]) + +cell_id=1 +output = Dict() +for (_, product) in particulator.products + output[product.name] = Array{Float32}(undef, output_points+1) + output[product.name][1] = product.get()[cell_id] +end + +for step = 2:output_points+1 + particulator.run(steps=output_interval) + for (_, product) in particulator.products + output[product.name][step] = product.get()[cell_id] + end +end + +plots = [] +ylbl = particulator.products["z"].unit +for (_, product) in particulator.products + if product.name != "z" + append!(plots, [plot(output[product.name], output["z"], ylabel=ylbl, xlabel=product.unit, title=product.name)]) + end + global ylbl = "" +end +plot(plots..., layout=(1, length(output)-1)) +savefig("parcel.svg") +``` +
+
+Matlab (click to expand) + +```Matlab +si = py.importlib.import_module('PySDM.physics').si; +spectral_sampling = py.importlib.import_module('PySDM.initialisation.sampling').spectral_sampling; +discretise_multiplicities = py.importlib.import_module('PySDM.initialisation').discretise_multiplicities; +Lognormal = py.importlib.import_module('PySDM.initialisation.spectra').Lognormal; +equilibrate_wet_radii = py.importlib.import_module('PySDM.initialisation.hygroscopic_equilibrium').equilibrate_wet_radii; +CPU = py.importlib.import_module('PySDM.backends').CPU; +AmbientThermodynamics = py.importlib.import_module('PySDM.dynamics').AmbientThermodynamics; +Condensation = py.importlib.import_module('PySDM.dynamics').Condensation; +Parcel = py.importlib.import_module('PySDM.environments').Parcel; +Builder = py.importlib.import_module('PySDM').Builder; +Formulae = py.importlib.import_module('PySDM').Formulae; +products = py.importlib.import_module('PySDM.products'); + +env = Parcel(pyargs( ... + 'dt', .25 * si.s, ... + 'mass_of_dry_air', 1e3 * si.kg, ... + 'p0', 1122 * si.hPa, ... + 'initial_water_vapour_mixing_ratio', 20 * si.g / si.kg, ... + 'T0', 300 * si.K, ... + 'w', 2.5 * si.m / si.s ... +)); +spectrum = Lognormal(pyargs('norm_factor', 1e4/si.mg, 'm_mode', 50 * si.nm, 's_geom', 1.4)); +kappa = .5; +cloud_range = py.tuple({.5 * si.um, 25 * si.um}); +output_interval = 4; +output_points = 40; +n_sd = 256; + +formulae = Formulae(); +builder = Builder(pyargs('backend', CPU(formulae), 'n_sd', int32(n_sd), 'environment', env)); +builder.add_dynamic(AmbientThermodynamics()); +builder.add_dynamic(Condensation()); + +tmp = spectral_sampling.Logarithmic(spectrum).sample_deterministic(int32(n_sd)); +r_dry = tmp{1}; +v_dry = formulae.trivia.volume(pyargs('radius', r_dry)); +specific_concentration = tmp{2}; +r_wet = equilibrate_wet_radii(pyargs(... + 'r_dry', r_dry, ... + 'environment', builder.particulator.environment, ... + 'kappa_times_dry_volume', kappa * v_dry... +)); + +attributes = py.dict(pyargs( ... + 'multiplicity', discretise_multiplicities(specific_concentration * env.mass_of_dry_air), ... + 'dry volume', v_dry, ... + 'kappa times dry volume', kappa * v_dry, ... + 'volume', formulae.trivia.volume(pyargs('radius', r_wet)) ... +)); + +particulator = builder.build(attributes, py.list({ ... + products.PeakSaturation(pyargs('name', 'S_max_percent', 'unit', '%')), ... + products.EffectiveRadius(pyargs('name', 'r_eff', 'unit', 'um', 'radius_range', cloud_range)), ... + products.ParticleConcentration(pyargs('name', 'n_c_cm3', 'unit', 'cm^-3', 'radius_range', cloud_range)), ... + products.WaterMixingRatio(pyargs('name', 'liquid water mixing ratio', 'unit', 'g/kg', 'radius_range', cloud_range)) ... + products.ParcelDisplacement(pyargs('name', 'z')) ... +})); + +cell_id = int32(0); +output_size = [output_points+1, length(py.list(particulator.products.keys()))]; +output_types = repelem({'double'}, output_size(2)); +output_names = [cellfun(@string, cell(py.list(particulator.products.keys())))]; +output = table(... + 'Size', output_size, ... + 'VariableTypes', output_types, ... + 'VariableNames', output_names ... +); +for pykey = py.list(keys(particulator.products)) + get = py.getattr(particulator.products{pykey{1}}.get(), '__getitem__'); + key = string(pykey{1}); + output{1, key} = get(cell_id); +end + +for i=2:output_points+1 + particulator.run(pyargs('steps', int32(output_interval))); + for pykey = py.list(keys(particulator.products)) + get = py.getattr(particulator.products{pykey{1}}.get(), '__getitem__'); + key = string(pykey{1}); + output{i, key} = get(cell_id); + end +end + +i=1; +for pykey = py.list(keys(particulator.products)) + product = particulator.products{pykey{1}}; + if string(product.name) ~= "z" + subplot(1, width(output)-1, i); + plot(output{:, string(pykey{1})}, output.z, '-o'); + title(string(product.name), 'Interpreter', 'none'); + xlabel(string(product.unit)); + end + if i == 1 + ylabel(string(particulator.products{"z"}.unit)); + end + i=i+1; +end +saveas(gcf, "parcel.png"); +``` +
+
+Python (click to expand) + +```Python +from matplotlib import pyplot +from PySDM.physics import si +from PySDM.initialisation import discretise_multiplicities +from PySDM.initialisation.hygroscopic_equilibrium import equilibrate_wet_radii +from PySDM.initialisation.spectra import Lognormal +from PySDM.initialisation.sampling import spectral_sampling +from PySDM.backends import CPU +from PySDM.dynamics import AmbientThermodynamics, Condensation +from PySDM.environments import Parcel +from PySDM import Builder, Formulae, products + +env = Parcel( + dt=.25 * si.s, + mass_of_dry_air=1e3 * si.kg, + p0=1122 * si.hPa, + initial_water_vapour_mixing_ratio=20 * si.g / si.kg, + T0=300 * si.K, + w=2.5 * si.m / si.s +) +spectrum = Lognormal(norm_factor=1e4 / si.mg, m_mode=50 * si.nm, s_geom=1.5) +kappa = .5 * si.dimensionless +cloud_range = (.5 * si.um, 25 * si.um) +output_interval = 4 +output_points = 40 +n_sd = 256 + +formulae = Formulae() +builder = Builder(backend=CPU(formulae), n_sd=n_sd, environment=env) +builder.add_dynamic(AmbientThermodynamics()) +builder.add_dynamic(Condensation()) + +r_dry, specific_concentration = spectral_sampling.Logarithmic(spectrum).sample_deterministic(n_sd) +v_dry = formulae.trivia.volume(radius=r_dry) +r_wet = equilibrate_wet_radii(r_dry=r_dry, environment=builder.particulator.environment, kappa_times_dry_volume=kappa * v_dry) + +attributes = { + 'multiplicity': discretise_multiplicities(specific_concentration * env.mass_of_dry_air), + 'dry volume': v_dry, + 'kappa times dry volume': kappa * v_dry, + 'volume': formulae.trivia.volume(radius=r_wet) +} + +particulator = builder.build(attributes, products=[ + products.PeakSaturation(name='S_max_percent', unit='%'), + products.EffectiveRadius(name='r_eff', unit='um', radius_range=cloud_range), + products.ParticleConcentration(name='n_c_cm3', unit='cm^-3', radius_range=cloud_range), + products.WaterMixingRatio(name='liquid water mixing ratio', unit='g/kg', radius_range=cloud_range), + products.ParcelDisplacement(name='z') +]) + +cell_id = 0 +output = {product.name: [product.get()[cell_id]] for product in particulator.products.values()} + +for step in range(output_points): + particulator.run(steps=output_interval) + for product in particulator.products.values(): + output[product.name].append(product.get()[cell_id]) + +fig, axs = pyplot.subplots(1, len(particulator.products) - 1, sharey="all") +for i, (key, product) in enumerate(particulator.products.items()): + if key != 'z': + axs[i].plot(output[key], output['z'], marker='.') + axs[i].set_title(product.name) + axs[i].set_xlabel(product.unit) + axs[i].grid() +axs[0].set_ylabel(particulator.products['z'].unit) +pyplot.savefig('parcel.svg') +``` +
+ +The resultant plot (generated with the Matlab code) looks as follows: + +![plot](https://github.com/open-atmos/PySDM/releases/download/tip/parcel.png) + +## Tutorials from Caltech course +There are currently two tutorial notebooks from the Caltech course on Cloud Microphysics available: +- [Part 1: Condensation](https://github.com/open-atmos/PySDM/blob/main/tutorials/condensation/condensation_playground.ipynb) +- [Part 2: Collisions](https://github.com/open-atmos/PySDM/blob/main/tutorials/collisions/collisions_playground.ipynb) + + +# Submodule structure +```mermaid +mindmap + root((PySDM)) + Builder + Formulae + Particulator + ((attributes)) + (physics) + DryVolume: ExtensiveAttribute + Kappa: DerivedAttribute + ... + (chemistry) + Acidity + ... + (...) + ((backends)) + Numba + ThrustRTC + ((dynamics)) + AqueousChemistry + Collision + Condensation + ... + ((environments)) + Box + Parcel + Kinematic2D + ... + ((initialisation)) + (spectra) + Lognormal + Exponential + ... + (sampling) + (spectral_sampling) + ConstantMultiplicity + UniformRandom + Logarithmic + ... + (...) + (...) + ((physics)) + (hygroscopicity) + KappaKoehler + ... + (condensation_coordinate) + Volume + VolumeLogarithm + (...) + ((products)) + (size_spectral) + EffectiveRadius + WaterMixingRatio + ... + (ambient_thermodynamics) + AmbientRelativeHumidity + ... + (...) +``` + +# Contributing, reporting issues, seeking support + +See [README.md](https://github.com/open-atmos/PySDM/tree/main/README.md) + +# Related resources and open-source projects + +### SDM patents (some expired, some withdrawn): +- https://patents.google.com/patent/US7756693B2 +- https://patents.google.com/patent/EP1847939A3 +- https://patents.google.com/patent/JP4742387B2 +- https://patents.google.com/patent/CN101059821B + +### Other SDM implementations: +- SCALE-SDM (Fortran): + https://github.com/Shima-Lab/SCALE-SDM_BOMEX_Sato2018/blob/master/contrib/SDM/sdm_coalescence.f90 +- Pencil Code (Fortran): + https://github.com/pencil-code/pencil-code/blob/master/src/particles_coagulation.f90 +- PALM LES (Fortran): + https://gitlab.palm-model.org/releases/palm_model_system/-/blob/master/packages/palm/model/src/lagrangian_particle_model_mod.f90 +- libcloudph++ (C++): + https://github.com/igfuw/libcloudphxx/blob/master/src/impl/particles_impl_coal.ipp +- LCM1D (Python) + https://github.com/SimonUnterstrasser/ColumnModel +- superdroplet (Cython/Numba/C++11/Fortran 2008/Julia) + https://github.com/darothen/superdroplet +- NTLP (FORTRAN) + https://github.com/Folca/NTLP/blob/SuperDroplet/les.F +- CLEO (C++) + https://yoctoyotta1024.github.io/CLEO/ +- droplets.jl (Julia) + https://github.com/emmacware/droplets.jl +- LacmoPy (Python/Numba) + https://github.com/JanKBohrer/LacmoPy/blob/master/collision/all_or_nothing.py +- McSnow (FORTRAN) + https://gitlab.dkrz.de/mcsnow/mcsnow/-/blob/master/src/mo_coll.f90 +- Particula (Python) + https://github.com/uncscode/particula/blob/main/particula/dynamics/coagulation/particle_resolved_step/super_droplet_method.py + +### non-SDM probabilistic particle-based coagulation solvers + +- PartMC (Fortran with a Python interface): + https://github.com/compdyn/partmc + +### Python models with discrete-particle (moving-sectional) representation of particle size spectrum + +- pyrcel: https://github.com/darothen/pyrcel +- PyBox: https://github.com/loftytopping/PyBox +- py-cloud-parcel-model: https://github.com/emmasimp/py-cloud-parcel-model + +### Open-source models with other than discrete-particle representations of coagulation + +- DustPy: https://stammler.github.io/dustpy +- Cloudy.jl: https://github.com/CliMA/Cloudy.jl diff --git a/PySDM/source/docs/templates/README.md b/PySDM/source/docs/templates/README.md new file mode 100644 index 0000000000000000000000000000000000000000..f16506b21817b5074d55c9f8a3c9745836723084 --- /dev/null +++ b/PySDM/source/docs/templates/README.md @@ -0,0 +1 @@ +dark mode .css from https://github.com/mitmproxy/pdoc/tree/main/examples/dark-mode diff --git a/PySDM/source/docs/templates/custom.css b/PySDM/source/docs/templates/custom.css new file mode 100644 index 0000000000000000000000000000000000000000..e5a589fa912f65111afe93d17e8b4064f6b41484 --- /dev/null +++ b/PySDM/source/docs/templates/custom.css @@ -0,0 +1,12 @@ +img { + max-width: 100%; +} + +mark { + background-color: #859bed; + color: black; +} + +ul { + margin-left: 2em; +} diff --git a/PySDM/source/docs/templates/index.html.jinja2 b/PySDM/source/docs/templates/index.html.jinja2 new file mode 100644 index 0000000000000000000000000000000000000000..49ba30b5ed7f44e55857b8d4141bf144d09e1e75 --- /dev/null +++ b/PySDM/source/docs/templates/index.html.jinja2 @@ -0,0 +1,196 @@ +{% extends "default/index.html.jinja2" %} + +{% block title %}PySDM documentation{% endblock %} + +{% block nav %} + +{% endblock %} + +{% block content %} + +
+
+ + PySDM logo + +

Documentation

+
+
+

What is PySDM?

+

+

+ PySDM is a package for simulating the dynamics of population of particles undergoing diffusional and collisional growth (and breakage). + The package features a Pythonic high-performance (multi-threaded CPU & CUDA GPU) implementation of the Super-Droplet Method (SDM) Monte-Carlo algorithm + for representing collisional growth (Shima et al. 2009), hence the name. + It is intended to serve as a building block for simulation systems modelling fluid flows involving a dispersed phase, + with PySDM being responsible for representation of the dispersed phase. + Currently, the development is focused on atmospheric cloud physics applications, in particular on modelling the dynamics of + particles immersed in moist air using the particle-based (a.k.a. super-droplet) approach to represent aerosol/cloud/rain microphysics. + The key goal of PySDM is to enable rapid development and independent reproducibility of simulations in cloud microphysics + while being free from the two-language barrier commonly separating prototype and high-performance research code. + PySDM ships with a set of examples reproducing results from literature and serving as tutorials. + The animation shown here depicts a flow-coupled simulation in which the flow is resolved using PySDM's sibling project: PyMPDATA. + The examples include also single-column setups (with PyMPDATA used for advection) as well as adiabatic cloud parcel model setups + (with PySDM alone sufficient to constitute a microphysics-resolving cloud parcel model in Python). +

+
+
+

What is the difference between PySDM and PySDM-examples?

+

+ PySDM is a Python package that provides the implementation of SDM that can be used in your own projects. +

+

+ PySDM-examples is a Python package that provides examples of how to use PySDM. + The package contains common code used in PySDM examples Jupyter notebooks, as well as in PySDM test suite. +

+

The two projects exist separately on PyPI, but their development and issue tracking is hosted at the same GitHub repository.

+
+
+

Important links

+ + + + + + + + + + + + +
PySDMPySDM-examples
+ + + +
+
    +
  • +
  • +
+
+
+
+

Installation

+

+ PySDM is available on PyPI and can be installed using pip: +

+
pip install PySDM
+

Note: the way above will not install test-time dependencies, to install them and run the tests, likely the most convenient way is:

+
git clone https://github.com/open-atmos/PySDM.git
+pip install -e PySDM[tests] -e PySDM/examples[tests]
+pytest PySDM
+

(the above should be a viable way to set up development environment for PySDM, see also our Python dev hints Wiki + and PySDM HOWTOs for further information)

+

+ PySDM-examples is also available on PyPI and can be installed using pip: +

+
pip install PySDM-examples
+

Note: this will also install PySDM if needed, but the examples package wheels do not include the Jupyter notebooks - only common code used from the notebooks. + All PySDM example notebooks can be viewed on GitHub and feature header cells with badges enabling single-click execution on either + Google Colab or mybinder.org platforms. + To try the notebooks out locally, use: +

+
git clone https://github.com/open-atmos/PySDM.git
+pip install -e PySDM -e PySDM/examples
+jupyter-notebook PySDM/examples
+
+
+

Dependencies

+

+ PySDM depends on NumPy, Numba, + ThrustRTC, SciPy, Pint, + chempy, pyevtk and CURandRTC +

+

+ PySDM-examples requires additional packages listed in install_requires in + setup.py. + Amongst them is PySDM. +

+
+
+

Contributing, reporting issues, seeking support

+

+ Submitting new code to both packages is done through the same GitHub repository via + Pull requests. +

+

+ Issues regarding any incorrect, unintuitive or undocumented behaviour of PySDM or PySDM-examples + are best to be reported on the + GitHub issue tracker. +

+

+ We encourage to use the GitHub Discussions + feature (rather than the issue tracker) for seeking support in understanding, + using and extending PySDM code. +

+
+
+

Licensing, credits, acknowledgements

+

+ PySDM and PySDM-examples are free/libre open-source software packages released under the + GNU GPL v3 license. +

+ Development of PySDM was started by Piotr Bartman[-Szwarc], + Sylwester Arabas and collaborators + at the Jagiellonian University in Kraków and at + the CliMA team at Caltech. + For an overview of features of the initial release, see the + 2022 PySDM v1 JOSS paper. +

+

+ The v2 release of PySDM is summarised in the 2023 de Jong, + Singer, et al. JOSS paper. + Current development (towards v3) and maintenance is led by the + Environmental Physics Group at the AGH University of Krakow. + See list of code committers + for a complete list of contributors. +

+

+ Development of PySDM was supported by: +

+ +
+
+

Bibliography with code cross-references

+

+ The list below summarises all literature references included in PySDM codebase and includes links to both the referenced papers, as well as to the referring PySDM source files. +

+ {% include "bibliography.html" %} +

EOF

+
+
+ +{# {% include "search.html.jinja2" %}#} +{% endblock %} + diff --git a/PySDM/source/docs/templates/syntax-highlighting.css b/PySDM/source/docs/templates/syntax-highlighting.css new file mode 100644 index 0000000000000000000000000000000000000000..b0a7fe3746cdf607a1b31a6fb97a691eab9a2286 --- /dev/null +++ b/PySDM/source/docs/templates/syntax-highlighting.css @@ -0,0 +1,80 @@ +/* monokai color scheme, see pdoc/template/README.md */ +pre { line-height: 125%; } +span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 20px; } +.pdoc-code .hll { background-color: #49483e } +.pdoc-code { background: #272822; color: #f8f8f2 } +.pdoc-code .c { color: #75715e } /* Comment */ +.pdoc-code .err { color: #960050; background-color: #1e0010 } /* Error */ +.pdoc-code .esc { color: #f8f8f2 } /* Escape */ +.pdoc-code .g { color: #f8f8f2 } /* Generic */ +.pdoc-code .k { color: #66d9ef } /* Keyword */ +.pdoc-code .l { color: #ae81ff } /* Literal */ +.pdoc-code .n { color: #f8f8f2 } /* Name */ +.pdoc-code .o { color: #f92672 } /* Operator */ +.pdoc-code .x { color: #f8f8f2 } /* Other */ +.pdoc-code .p { color: #f8f8f2 } /* Punctuation */ +.pdoc-code .ch { color: #75715e } /* Comment.Hashbang */ +.pdoc-code .cm { color: #75715e } /* Comment.Multiline */ +.pdoc-code .cp { color: #75715e } /* Comment.Preproc */ +.pdoc-code .cpf { color: #75715e } /* Comment.PreprocFile */ +.pdoc-code .c1 { color: #75715e } /* Comment.Single */ +.pdoc-code .cs { color: #75715e } /* Comment.Special */ +.pdoc-code .gd { color: #f92672 } /* Generic.Deleted */ +.pdoc-code .ge { color: #f8f8f2; font-style: italic } /* Generic.Emph */ +.pdoc-code .gr { color: #f8f8f2 } /* Generic.Error */ +.pdoc-code .gh { color: #f8f8f2 } /* Generic.Heading */ +.pdoc-code .gi { color: #a6e22e } /* Generic.Inserted */ +.pdoc-code .go { color: #66d9ef } /* Generic.Output */ +.pdoc-code .gp { color: #f92672; font-weight: bold } /* Generic.Prompt */ +.pdoc-code .gs { color: #f8f8f2; font-weight: bold } /* Generic.Strong */ +.pdoc-code .gu { color: #75715e } /* Generic.Subheading */ +.pdoc-code .gt { color: #f8f8f2 } /* Generic.Traceback */ +.pdoc-code .kc { color: #66d9ef } /* Keyword.Constant */ +.pdoc-code .kd { color: #66d9ef } /* Keyword.Declaration */ +.pdoc-code .kn { color: #f92672 } /* Keyword.Namespace */ +.pdoc-code .kp { color: #66d9ef } /* Keyword.Pseudo */ +.pdoc-code .kr { color: #66d9ef } /* Keyword.Reserved */ +.pdoc-code .kt { color: #66d9ef } /* Keyword.Type */ +.pdoc-code .ld { color: #e6db74 } /* Literal.Date */ +.pdoc-code .m { color: #ae81ff } /* Literal.Number */ +.pdoc-code .s { color: #e6db74 } /* Literal.String */ +.pdoc-code .na { color: #a6e22e } /* Name.Attribute */ +.pdoc-code .nb { color: #f8f8f2 } /* Name.Builtin */ +.pdoc-code .nc { color: #a6e22e } /* Name.Class */ +.pdoc-code .no { color: #66d9ef } /* Name.Constant */ +.pdoc-code .nd { color: #a6e22e } /* Name.Decorator */ +.pdoc-code .ni { color: #f8f8f2 } /* Name.Entity */ +.pdoc-code .ne { color: #a6e22e } /* Name.Exception */ +.pdoc-code .nf { color: #a6e22e } /* Name.Function */ +.pdoc-code .nl { color: #f8f8f2 } /* Name.Label */ +.pdoc-code .nn { color: #f8f8f2 } /* Name.Namespace */ +.pdoc-code .nx { color: #a6e22e } /* Name.Other */ +.pdoc-code .py { color: #f8f8f2 } /* Name.Property */ +.pdoc-code .nt { color: #f92672 } /* Name.Tag */ +.pdoc-code .nv { color: #f8f8f2 } /* Name.Variable */ +.pdoc-code .ow { color: #f92672 } /* Operator.Word */ +.pdoc-code .w { color: #f8f8f2 } /* Text.Whitespace */ +.pdoc-code .mb { color: #ae81ff } /* Literal.Number.Bin */ +.pdoc-code .mf { color: #ae81ff } /* Literal.Number.Float */ +.pdoc-code .mh { color: #ae81ff } /* Literal.Number.Hex */ +.pdoc-code .mi { color: #ae81ff } /* Literal.Number.Integer */ +.pdoc-code .mo { color: #ae81ff } /* Literal.Number.Oct */ +.pdoc-code .sa { color: #e6db74 } /* Literal.String.Affix */ +.pdoc-code .sb { color: #e6db74 } /* Literal.String.Backtick */ +.pdoc-code .sc { color: #e6db74 } /* Literal.String.Char */ +.pdoc-code .dl { color: #e6db74 } /* Literal.String.Delimiter */ +.pdoc-code .sd { color: #e6db74 } /* Literal.String.Doc */ +.pdoc-code .s2 { color: #e6db74 } /* Literal.String.Double */ +.pdoc-code .se { color: #ae81ff } /* Literal.String.Escape */ +.pdoc-code .sh { color: #e6db74 } /* Literal.String.Heredoc */ +.pdoc-code .si { color: #e6db74 } /* Literal.String.Interpol */ +.pdoc-code .sx { color: #e6db74 } /* Literal.String.Other */ +.pdoc-code .sr { color: #e6db74 } /* Literal.String.Regex */ +.pdoc-code .s1 { color: #e6db74 } /* Literal.String.Single */ +.pdoc-code .ss { color: #e6db74 } /* Literal.String.Symbol */ +.pdoc-code .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */ +.pdoc-code .fm { color: #a6e22e } /* Name.Function.Magic */ +.pdoc-code .vc { color: #f8f8f2 } /* Name.Variable.Class */ +.pdoc-code .vg { color: #f8f8f2 } /* Name.Variable.Global */ +.pdoc-code .vi { color: #f8f8f2 } /* Name.Variable.Instance */ +.pdoc-code .vm { color: #f8f8f2 } /* Name.Variable.Magic */ diff --git a/PySDM/source/docs/templates/theme.css b/PySDM/source/docs/templates/theme.css new file mode 100644 index 0000000000000000000000000000000000000000..1dfe7c4a1c300ecced5a18c5030c7efcd18779e4 --- /dev/null +++ b/PySDM/source/docs/templates/theme.css @@ -0,0 +1,20 @@ +:root { + --pdoc-background: #212529; +} + +.pdoc { + --text: #f7f7f7; + --muted: #9d9d9d; + --link: #58a6ff; + --link-hover: #3989ff; + --code: #333; + --active: #555; + + --accent: #343434; + --accent2: #555; + + --nav-hover: rgba(0, 0, 0, 0.1); + --name: #77C1FF; + --def: #0cdd0c; + --annotation: #00c037; +} diff --git a/PySDM/source/examples/MANIFEST.in b/PySDM/source/examples/MANIFEST.in new file mode 100644 index 0000000000000000000000000000000000000000..b2f953c2d0812ca2e9796a8f3799523c06527337 --- /dev/null +++ b/PySDM/source/examples/MANIFEST.in @@ -0,0 +1,3 @@ +global-exclude *.ipynb +global-exclude *.csv +include docs/*.md \ No newline at end of file diff --git a/PySDM/source/examples/PySDM_examples/Abade_and_Albuquerque_2024/__init__.py b/PySDM/source/examples/PySDM_examples/Abade_and_Albuquerque_2024/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f74f0c2df5f7f7d2f40cadfcdd933a8b630af756 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Abade_and_Albuquerque_2024/__init__.py @@ -0,0 +1,11 @@ +# pylint: disable=invalid-name +""" +mixed-phase example using parcel environment based on +[Abade & Albuquerque 2024 (QJRMS)](https://doi.org/10.1002/qj.4775) + +fig_2.ipynb: +.. include:: ./fig_2.ipynb.badges.md +""" + +from .simulation import Simulation +from .settings import Settings diff --git a/PySDM/source/examples/PySDM_examples/Abade_and_Albuquerque_2024/fig_2.ipynb b/PySDM/source/examples/PySDM_examples/Abade_and_Albuquerque_2024/fig_2.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..97fbb7d6273a0cd111391a8c988d52138b529dec --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Abade_and_Albuquerque_2024/fig_2.ipynb @@ -0,0 +1,19465 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a3af9f8e-a138-47c8-af08-62451db09352", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Abade_and_Albuquerque_2024/fig_2.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Abade_and_Albuquerque_2024/fig_2.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Abade_and_Albuquerque_2024/fig_2.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "52caca20-41df-4878-b883-2dc2609591c6", + "metadata": {}, + "source": [ + "#### based on Fig. 2 from [Abade & Albuquerque 2024 (QJRMS)](https://doi.org/10.1002/qj.4775) \"_Persistent mixed‐phase states in adiabatic cloud parcels under idealised conditions_\"\n", + "\n", + "compared to the paper, the analysis below differs by:\n", + "- including only the \"Homogeneous\" and \"Bulk\" methods (no \"stochastic\" yet)\n", + "- extending the analysis to cover both singular (INAS, as used in the paper) as well as time-dependent (ABIFM) immersion freezing models\n", + "- extending the analysis to depict multiple realisations + mean\n", + "- extending the analysis to illustrate the dependence of realisation spread on the number of super droplets used\n", + "- extending the analysis to depict how the results differ depending on the vertical velocity (cooling rate)\n", + "\n", + "TODO #1656:\n", + "- extend to show how the monodisperse vs. polydisperse INP size spectrum assumption changes the results\n", + "- extend to cover the stochastic model" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "aa876f2db21bb522", + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-06T10:37:52.672366Z", + "start_time": "2024-12-06T10:37:52.668120Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "9176c292-0d71-4608-ac4b-030e33de42e5", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from matplotlib import pyplot\n", + "from scipy.interpolate import interp1d\n", + "from open_atmos_jupyter_utils import show_plot\n", + "from PySDM import Formulae\n", + "from PySDM.physics import si, in_unit\n", + "from PySDM.backends import CPU\n", + "from PySDM_examples.utils.widgets import display, FloatProgress\n", + "from PySDM_examples.Arabas_et_al_2025.commons import FREEZING_CONSTANTS\n", + "from PySDM_examples.Abade_and_Albuquerque_2024 import Simulation, Settings" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "c51620c1-e00a-4acc-ad55-1b6d477e90ac", + "metadata": {}, + "outputs": [], + "source": [ + "backend = CPU(\n", + " formulae = Formulae(\n", + " constants={\n", + " \"bulk_phase_partitioning_exponent\": 0.1,\n", + " **FREEZING_CONSTANTS[\"dust\"],\n", + " },\n", + " bulk_phase_partitioning=\"KaulEtAl2015\",\n", + " particle_shape_and_density=\"MixedPhaseSpheres\",\n", + " diffusion_coordinate=\"WaterMassLogarithm\",\n", + " freezing_temperature_spectrum=\"Niemand_et_al_2012\",\n", + " heterogeneous_ice_nucleation_rate=\"ABIFM\",\n", + " ),\n", + " override_jit_flags={\"parallel\": False}\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "478730ad-0c93-4adf-82a1-c606fde3c0b9", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "711644582d8e41cb9de11b68db665218", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=1.0, max=39.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "CI = 'CI' in os.environ\n", + "n_realisations = 3 if not CI else 1\n", + "n_sds = (64, 512) if not CI else (64,) # PAPER: \"on the order of 1e6\"\n", + "updrafts = (3.6, 1.2, .4) if not CI else (3.6,.4) # PAPER: 0.5 m/s\n", + "\n", + "dz_out = 100 * si.s\n", + "timestep = 1 * si.s\n", + "z_max = 3 * si.km\n", + "display(progbar := FloatProgress(value=1, max=(1 + 2 * n_realisations * len(n_sds)) * len(updrafts)))\n", + "\n", + "\n", + "settings_commons_part = {\n", + " 'enable_immersion_freezing': True,\n", + " 'enable_vapour_deposition_on_ice': True,\n", + "}\n", + "\n", + "datasets = {}\n", + "for updraft in updrafts:\n", + " t_max = z_max / updraft\n", + " settings_commons = {\n", + " 'updraft': updraft,\n", + " 'timestep': timestep,\n", + " 'backend': backend,\n", + " 'inp_frac': .5, # PAPER: .1\n", + " }\n", + " run_args = {\n", + " 'nt': int(t_max / timestep),\n", + " 'steps_per_output_interval': int(dz_out / updraft / timestep),\n", + " }\n", + " progbar.description = f'Bulk-{updraft}'\n", + " datasets[f'Bulk-{updraft}'] = {'realisations': [Simulation(Settings(\n", + " **settings_commons,\n", + " n_sd=1,\n", + " enable_immersion_freezing=False,\n", + " enable_vapour_deposition_on_ice=False,\n", + " )).run(**run_args)]}\n", + " progbar.value += 1\n", + " for singular, label in {True: 'INAS', False: 'ABIFM'}.items():\n", + " for n_sd in n_sds:\n", + " datasets[(key := f'Homogeneous-{label}-{n_sd}-{updraft}')] = {'realisations': []}\n", + " backend.formulae.seed = 0\n", + " for i in range(n_realisations):\n", + " progbar.description = '...' + key[-3:] + f'-{i}-{updraft}'\n", + " datasets[key]['realisations'].append(\n", + " Simulation(Settings(\n", + " **settings_commons,\n", + " **settings_commons_part,\n", + " n_sd=n_sd,\n", + " singular=singular\n", + " )).run(**run_args)\n", + " )\n", + " backend.formulae.seed += 1\n", + " progbar.value += 1" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "b7abadbe-aa26-433d-9a9a-1984e3360db5", + "metadata": {}, + "outputs": [], + "source": [ + "colors = {\n", + " 'ice+water': 'black',\n", + " 'ice': 'cyan',\n", + " 'water': 'blue',\n", + " 'vapour': 'gray',\n", + " 'total': 'orange',\n", + "}\n", + "\n", + "def plot_setup(ax):\n", + " ax.set_xlabel(r\"r (g$\\cdot$kg$^{-1}$)\")\n", + " ax.set_ylabel('Height (km)')\n", + " ax.set_ylim(.75, 3)\n", + " ax.set_xlim(-.05, 1.75)\n", + " ax.grid()\n", + " \n", + "def plot_part(ax, data): \n", + " for realisation in data['realisations']:\n", + " realisation['ice+water'] = np.asarray(realisation['water']) + np.asarray(realisation['ice'])\n", + " realisation['vapour'] = np.asarray(realisation['vapour'])\n", + " realisation['total'] = np.asarray(realisation['ice+water']) + np.asarray(realisation['vapour'])\n", + "\n", + " data['mean'] = {}\n", + " for name in ('ice', 'water', 'ice+water', 'height', 'vapour', 'total'):\n", + " data['mean'][name] = [\n", + " np.mean([realisation[name][level] for realisation in data['realisations']]) \n", + " for level in range(len(data['realisations'][0][name]))\n", + " ]\n", + " \n", + " for name in ('ice', 'water', 'ice+water', 'vapour', 'total'):\n", + " for realisation in data['realisations']:\n", + " ax.plot(\n", + " in_unit(np.asarray(realisation[name]), si.g / si.kg),\n", + " in_unit(np.asarray(realisation['height']), si.km),\n", + " linestyle='--' if name == 'ice+water' else '-',\n", + " color=colors[name],\n", + " linewidth=.75,\n", + " )\n", + " mean = data['mean']\n", + " ax.plot(\n", + " in_unit(np.asarray(mean[name]), si.g / si.kg),\n", + " in_unit(np.asarray(mean['height']), si.km),\n", + " label=name,\n", + " marker='.',\n", + " color=colors[name],\n", + " )\n", + "\n", + "def plot_bulk(ax, data):\n", + " liquid_fraction = backend.formulae.bulk_phase_partitioning.liquid_fraction(np.asarray(data['T']))\n", + " total_condensed_mixing_ratio = np.asarray(data['water'])\n", + " vapour_mixing_ratio = np.asarray(data['vapour'])\n", + " for name in ('ice', 'water', 'ice+water', 'vapour', 'total'):\n", + " values = {\n", + " 'ice+water': total_condensed_mixing_ratio,\n", + " 'ice': (1 - liquid_fraction) * total_condensed_mixing_ratio,\n", + " 'water': liquid_fraction * total_condensed_mixing_ratio,\n", + " 'vapour': vapour_mixing_ratio,\n", + " 'total': vapour_mixing_ratio + total_condensed_mixing_ratio,\n", + " }[name]\n", + " ax.plot(\n", + " in_unit(values, si.g / si.kg),\n", + " in_unit(np.asarray(data['height']), si.km),\n", + " label=name,\n", + " marker='.',\n", + " color=colors[name],\n", + " linestyle='--' if name == 'ice+water' else '-'\n", + " )\n", + " interp_temp = interp1d(\n", + " in_unit(np.asarray(data['height']), si.km),\n", + " backend.formulae.trivia.K2C(np.asarray(data['T'])),\n", + " bounds_error=True\n", + " ) \n", + " ax2 = ax.twinx()\n", + " ax2.set_yticks(ax.get_yticks(), [f\"{t:.1f}\" for t in interp_temp(ax.get_yticks())])\n", + " ax2.set_ylim(ax.get_ylim()) \n", + " ax2.set_ylabel('Temperature [°C]')\n", + " ax.legend(\n", + " loc='lower center',\n", + " bbox_to_anchor=(0.5, -.65),\n", + " fancybox=True,\n", + " shadow=True,\n", + " ncol=1\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "721e5606-9e19-44b9-87f3-cba86b54574e", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-09T00:10:08.228100\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "cc3528a91b9c44888dec44fde2892ab2", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_2-updraft=3.6.pdf
\")…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-09T00:10:15.789614\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "51186c849d72484caba32df520d98118", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_2-updraft=1.2.pdf
\")…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-09T00:10:22.660953\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "9ecad07aa7474446a95ceaab8679924d", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_2-updraft=0.4.pdf
\")…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "for updraft in updrafts:\n", + " fig, axs = pyplot.subplot_mosaic(\n", + " \"\"\"AB.\n", + " ABG\n", + " DEG\n", + " DE.\n", + " \"\"\",\n", + " figsize=(10, 7),\n", + " sharex=True,\n", + " sharey=True,\n", + " tight_layout=True,\n", + " )\n", + " for a in axs.values():\n", + " plot_setup(a)\n", + " \n", + " for label, model, index in (\n", + " (\"A\", \"INAS\", 0),\n", + " (\"B\", \"INAS\", 1),\n", + " (\"D\", \"ABIFM\", 0),\n", + " (\"E\", \"ABIFM\", 1),\n", + " ):\n", + " if index + 1 > len(n_sds):\n", + " continue\n", + " n_sd = n_sds[index]\n", + " axs[label].set_title(f\"{model} {n_sd} SDs\")\n", + " plot_part(axs[label], datasets[f'Homogeneous-{model}-{n_sd}-{updraft}'])\n", + " \n", + " axs[\"G\"].set_title(\"Bulk/Kaul et al. '15\")\n", + " plot_bulk(axs[\"G\"], datasets[f'Bulk-{updraft}']['realisations'][0])\n", + " axs[\"G\"].text(x=0, y=3.45, s=f\"w={updraft} m/s\", color='red', size=16)\n", + " show_plot(f'fig_2-updraft={updraft}.pdf')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "61ab56e5-efeb-4622-8307-ac4cbf02b824", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "baecaea8-8d64-45c5-9161-b17cfcf1a410", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c4edc43-d858-4fbb-bee0-e4ff126805d1", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/PySDM/source/examples/PySDM_examples/Abade_and_Albuquerque_2024/settings.py b/PySDM/source/examples/PySDM_examples/Abade_and_Albuquerque_2024/settings.py new file mode 100644 index 0000000000000000000000000000000000000000..b39684bc4f5968aec23f4c3a0c570f55dabb4381 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Abade_and_Albuquerque_2024/settings.py @@ -0,0 +1,61 @@ +from pystrict import strict + +from PySDM.backends import CPU +from PySDM.physics import si +from PySDM.initialisation.spectra import Lognormal + + +@strict +class Settings: + def __init__( + self, + *, + backend: CPU, + n_sd: int, + timestep: float, + # default values correspond to paper settings + singular: bool = True, + enable_immersion_freezing: bool = True, + enable_vapour_deposition_on_ice: bool = True, + inp_frac: float = 0.1, + kappa: float = 0.6, + updraft: float = 0.5 * si.m / si.s, + ): + self.backend = backend + self.n_sd = n_sd + self.timestep = timestep + self.enable_immersion_freezing = enable_immersion_freezing + self.enable_vapour_deposition_on_ice = enable_vapour_deposition_on_ice + self.singular = singular + self.initial_total_pressure = 1000 * si.hPa # note: not given in the paper + + self.initial_water_vapour_mixing_ratio = 1.5 * si.g / si.kg + self.parcel_linear_extent = 100 * si.m + self.updraft = updraft + self.freezing_inp_frac = inp_frac + self.freezing_inp_dry_radius = 0.5 * si.um + + thd_0 = backend.formulae.state_variable_triplet.th_dry( + th_std=269 * si.K, + water_vapour_mixing_ratio=self.initial_water_vapour_mixing_ratio, + ) + rhod_0 = backend.formulae.state_variable_triplet.rho_d( + p=self.initial_total_pressure, + water_vapour_mixing_ratio=self.initial_water_vapour_mixing_ratio, + theta_std=thd_0, + ) + + self.mass_of_dry_air = rhod_0 * backend.formulae.trivia.volume( + radius=self.parcel_linear_extent + ) + self.soluble_aerosol = Lognormal( + norm_factor=200 + / si.mg + * self.mass_of_dry_air, # note: assuming per mg of dry air + m_mode=75 * si.nm, + s_geom=1.6, + ) + self.kappa = kappa + self.initial_temperature = backend.formulae.state_variable_triplet.T( + rhod=rhod_0, thd=thd_0 + ) diff --git a/PySDM/source/examples/PySDM_examples/Abade_and_Albuquerque_2024/simulation.py b/PySDM/source/examples/PySDM_examples/Abade_and_Albuquerque_2024/simulation.py new file mode 100644 index 0000000000000000000000000000000000000000..9f790a1242c54dec289ae86e01dd1b9b78a5eeb1 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Abade_and_Albuquerque_2024/simulation.py @@ -0,0 +1,109 @@ +import numpy as np + +from PySDM_examples.utils import BasicSimulation + +from PySDM import Builder +from PySDM.dynamics import ( + Condensation, + AmbientThermodynamics, + VapourDepositionOnIce, + Freezing, +) +from PySDM.initialisation.sampling.spectral_sampling import ConstantMultiplicity +from PySDM.products import ( + AmbientTemperature, + AmbientWaterVapourMixingRatio, + ParcelDisplacement, + WaterMixingRatio, + SpecificIceWaterContent, +) +from PySDM.environments import Parcel + + +class Simulation(BasicSimulation): + def __init__(self, settings): + builder = Builder( + backend=settings.backend, + n_sd=settings.n_sd, + environment=Parcel( + dt=settings.timestep, + mass_of_dry_air=settings.mass_of_dry_air, + p0=settings.initial_total_pressure, + initial_water_vapour_mixing_ratio=settings.initial_water_vapour_mixing_ratio, + T0=settings.initial_temperature, + w=settings.updraft, + mixed_phase=True, + ), + ) + builder.add_dynamic(AmbientThermodynamics()) + builder.add_dynamic(Condensation()) + + if settings.enable_immersion_freezing: + builder.add_dynamic( + Freezing( + immersion_freezing=( + "singular" if settings.singular else "time-dependent" + ) + ) + ) + if settings.enable_vapour_deposition_on_ice: + builder.add_dynamic(VapourDepositionOnIce(adaptive=True)) + + r_dry, n_in_dv = ConstantMultiplicity( + settings.soluble_aerosol + ).sample_deterministic(n_sd=settings.n_sd) + attributes = builder.particulator.environment.init_attributes( + n_in_dv=n_in_dv, kappa=settings.kappa, r_dry=r_dry + ) + attributes["signed water mass"] = ( + builder.particulator.formulae.particle_shape_and_density.volume_to_mass( + attributes["volume"] + ) + ) + del attributes["volume"] + + if settings.enable_immersion_freezing: + trivia = builder.particulator.formulae.trivia + n_inp = int(settings.n_sd * settings.freezing_inp_frac) + + rng = np.random.default_rng(seed=builder.particulator.formulae.seed) + insoluble_surface_area = trivia.sphere_surface( + diameter=2 * settings.freezing_inp_dry_radius + ) + attributes[ + "freezing temperature" if settings.singular else "immersed surface area" + ] = rng.permutation( + np.pad( + ( + builder.particulator.formulae.freezing_temperature_spectrum.invcdf( + cdf=rng.uniform(low=0, high=1, size=n_inp), + A_insol=insoluble_surface_area, + ) + if settings.singular + else np.full(n_inp, insoluble_surface_area) + ), + (0, settings.n_sd - n_inp), + mode="constant", + constant_values=( + builder.particulator.formulae.constants.HOMOGENEOUS_FREEZING_THRESHOLD + if settings.singular + else 0 + ), + ) + ) + + self.products = ( + WaterMixingRatio(name="water", radius_range=(0, np.inf)), + SpecificIceWaterContent(name="ice"), + ParcelDisplacement(name="height"), + AmbientTemperature(name="T"), + AmbientWaterVapourMixingRatio( + name="vapour", var="water_vapour_mixing_ratio" + ), + ) + super().__init__( + particulator=builder.build(attributes=attributes, products=self.products) + ) + + def run(self, *, nt, steps_per_output_interval): + return self._run(nt=nt, steps_per_output_interval=steps_per_output_interval) diff --git a/PySDM/source/examples/PySDM_examples/Abdul_Razzak_Ghan_2000/__init__.py b/PySDM/source/examples/PySDM_examples/Abdul_Razzak_Ghan_2000/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d46eba5eb1b7df41c5e6acf5a7d5e541c53b1fab --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Abdul_Razzak_Ghan_2000/__init__.py @@ -0,0 +1,11 @@ +# pylint: disable=invalid-name +""" +condensation example using parcel environment based on +[Abdul-Razzak & Ghan 2000 (JGR)](https://doi.org/10.1029/1999JD901161) + +figs1-5.ipynb: +.. include:: ./figs1-5.ipynb.badges.md + +fig_4_kinetic_limitations.ipynb: +.. include:: ./fig_4_kinetic_limitations.ipynb.badges.md +""" diff --git a/PySDM/source/examples/PySDM_examples/Abdul_Razzak_Ghan_2000/aerosol.py b/PySDM/source/examples/PySDM_examples/Abdul_Razzak_Ghan_2000/aerosol.py new file mode 100644 index 0000000000000000000000000000000000000000..fdf4f7d9985111dfbd001178490a095831240d7e --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Abdul_Razzak_Ghan_2000/aerosol.py @@ -0,0 +1,102 @@ +from chempy import Substance +from pystrict import strict + +from PySDM.initialisation import spectra +from PySDM.initialisation.aerosol_composition import DryAerosolMixture +from PySDM.physics import si + +# not in the paper - guessed and checked to match +CONSTANTS_ARG = { + "Mv": 18.015 * si.g / si.mol, + "Md": 28.97 * si.g / si.mol, +} + + +@strict +class AerosolARG(DryAerosolMixture): + def __init__( + self, + water_molar_volume: float, + M2_sol: float = 0, + M2_N: float = 100 / si.cm**3, + M2_rad: float = 50 * si.nm, + ): + super().__init__( + compounds=("(NH4)2SO4", "insoluble"), + molar_masses={ + "(NH4)2SO4": 132.14 * si.g / si.mole, + "insoluble": 44 * si.g / si.mole, + }, + densities={ + "(NH4)2SO4": 1.77 * si.g / si.cm**3, + "insoluble": 1.77 * si.g / si.cm**3, + }, + is_soluble={"(NH4)2SO4": True, "insoluble": False}, + ionic_dissociation_phi={"(NH4)2SO4": 3, "insoluble": 0}, + ) + self.modes = ( + { + "kappa": self.kappa( + mass_fractions={"(NH4)2SO4": 1.0, "insoluble": 0.0}, + water_molar_volume=water_molar_volume, + ), + "spectrum": spectra.Lognormal( + norm_factor=100.0 / si.cm**3, m_mode=50.0 * si.nm, s_geom=2.0 + ), + }, + { + "kappa": self.kappa( + mass_fractions={"(NH4)2SO4": M2_sol, "insoluble": (1 - M2_sol)}, + water_molar_volume=water_molar_volume, + ), + "spectrum": spectra.Lognormal( + norm_factor=M2_N, m_mode=M2_rad, s_geom=2.0 + ), + }, + ) + + +@strict +class AerosolWhitby(DryAerosolMixture): + def __init__(self, water_molar_volume: float): + nuclei = {"(NH4)2SO4": 1.0} + accum = {"(NH4)2SO4": 1.0} + coarse = {"(NH4)2SO4": 1.0} + + super().__init__( + ionic_dissociation_phi={"(NH4)2SO4": 3}, + molar_masses={ + "(NH4)2SO4": Substance.from_formula("(NH4)2SO4").mass + * si.gram + / si.mole + }, + densities={"(NH4)2SO4": 1.77 * si.g / si.cm**3}, + compounds=("(NH4)2SO4",), + is_soluble={"(NH4)2SO4": True}, + ) + self.modes = ( + { + "kappa": self.kappa( + mass_fractions=nuclei, water_molar_volume=water_molar_volume + ), + "spectrum": spectra.Lognormal( + norm_factor=1000.0 / si.cm**3, m_mode=0.008 * si.um, s_geom=1.6 + ), + }, + { + "kappa": self.kappa( + mass_fractions=accum, water_molar_volume=water_molar_volume + ), + "spectrum": spectra.Lognormal( + norm_factor=800 / si.cm**3, m_mode=0.034 * si.um, s_geom=2.1 + ), + }, + { + "kappa": self.kappa( + mass_fractions=coarse, water_molar_volume=water_molar_volume + ), + "spectrum": spectra.Lognormal( + norm_factor=0.72 / si.cm**3, m_mode=0.46 * si.um, s_geom=2.2 + ), + }, + ) diff --git a/PySDM/source/examples/PySDM_examples/Abdul_Razzak_Ghan_2000/data_from_ARG2000_paper.py b/PySDM/source/examples/PySDM_examples/Abdul_Razzak_Ghan_2000/data_from_ARG2000_paper.py new file mode 100644 index 0000000000000000000000000000000000000000..016bd30273b6369f80b06dc00c443116bf3bb4f0 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Abdul_Razzak_Ghan_2000/data_from_ARG2000_paper.py @@ -0,0 +1,533 @@ +# figure 1 +Fig1_N2_obs = [ + 0, + 90.2394106813997, + 399.6316758747698, + 904.2357274401473, + 1906.0773480662983, + 4926.33517495396, +] +Fig1_AF_obs = [ + 0.7935943060498221, + 0.7124555160142347, + 0.594306049822064, + 0.48185053380782916, + 0.34946619217081853, + 0.17580071174377226, +] +Fig1_N2_param = [ + 0, + 12.891344383057117, + 27.624309392265218, + 68.13996316758755, + 97.60589318600375, + 149.1712707182321, + 200.73664825046046, + 274.40147329650097, + 325.9668508287293, + 388.5819521178637, + 454.88029465930015, + 499.0791896869245, + 591.1602209944751, + 653.7753222836095, + 786.3720073664825, + 712.7071823204419, + 841.6206261510129, + 904.2357274401473, + 963.1675874769797, + 1033.1491712707182, + 1092.0810313075506, + 1165.745856353591, + 1239.4106813996316, + 1298.3425414364642, + 1364.6408839779006, + 1441.988950276243, + 1515.6537753222838, + 1659.3001841620626, + 1581.95211786372, + 1725.598526703499, + 1839.779005524862, + 2226.5193370165744, + 2016.5745856353592, + 2591.160220994475, + 2421.731123388582, + 2826.8876611418045, + 3106.8139963167587, + 3471.4548802946592, + 3736.648250460405, + 4023.941068139963, + 4233.885819521179, + 4458.563535911602, + 4639.042357274401, + 4815.837937384898, + 4915.285451197053, +] +Fig1_AF_param = [ + 0.783629893238434, + 0.7437722419928825, + 0.7209964412811387, + 0.6967971530249111, + 0.6654804270462633, + 0.6412811387900356, + 0.6199288256227757, + 0.5957295373665481, + 0.5772241992882562, + 0.5615658362989324, + 0.5459074733096085, + 0.5373665480427046, + 0.5174377224199288, + 0.5060498220640569, + 0.4832740213523132, + 0.49323843416370106, + 0.47473309608540926, + 0.4676156583629893, + 0.4590747330960854, + 0.4505338078291815, + 0.44199288256227753, + 0.4320284697508897, + 0.4192170818505338, + 0.41209964412811384, + 0.40498220640569393, + 0.39786476868327403, + 0.3893238434163701, + 0.37224199288256227, + 0.38220640569395015, + 0.3693950177935943, + 0.3580071174377224, + 0.3323843416370107, + 0.3466192170818505, + 0.31103202846975087, + 0.3209964412811388, + 0.2939501779359431, + 0.2825622775800712, + 0.2612099644128114, + 0.24555160142348756, + 0.22846975088967972, + 0.21992882562277583, + 0.20854092526690393, + 0.2, + 0.18861209964412812, + 0.18434163701067616, +] + +# figure 2 +Fig2a_N2_obs = [ + 0, + 84.38061041292644, + 188.5098743267505, + 500.89766606822263, + 1000, + 1994.6140035906642, + 4996.4093357271095, +] +Fig2a_AF_obs = [ + 0.7951557093425605, + 0.7619377162629757, + 0.7411764705882353, + 0.6982698961937717, + 0.6581314878892733, + 0.5986159169550174, + 0.4975778546712803, +] +Fig2a_N2_param = [ + 12.567324955116646, + 44.88330341113101, + 73.60861759425495, + 109.51526032315974, + 166.9658886894075, + 238.7791741472172, + 350.0897666068223, + 493.7163375224417, + 669.6588868940754, + 863.5547576301616, + 1078.9946140035906, + 1359.0664272890485, + 1754.0394973070017, + 2105.9245960502694, + 2533.2136445242368, + 2978.456014362657, + 3416.517055655296, + 3912.0287253141832, + 4339.317773788151, + 4705.56552962298, + 4942.549371633752, +] +Fig2a_AF_param = [ + 0.7771626297577854, + 0.7522491349480969, + 0.7314878892733563, + 0.7107266435986159, + 0.6927335640138408, + 0.6816608996539792, + 0.6650519031141868, + 0.6442906574394464, + 0.627681660899654, + 0.6166089965397924, + 0.6, + 0.590311418685121, + 0.5695501730103807, + 0.5612456747404844, + 0.5487889273356401, + 0.5321799307958477, + 0.5211072664359861, + 0.5044982698961937, + 0.4975778546712803, + 0.485121107266436, + 0.4782006920415225, +] +Fig2b_N2_obs = [ + 82.31173380035023, + 190.893169877408, + 499.12434325744306, + 1003.5026269702277, + 1994.7460595446587, + 5000, +] +Fig2b_AF_obs = [ + 0.34177215189873417, + 0.3189873417721519, + 0.27721518987341776, + 0.23670886075949368, + 0.1949367088607595, + 0.1329113924050633, +] +Fig2b_N2_param = [ + 78.80910683012257, + 113.83537653239921, + 201.40105078809108, + 306.4798598949212, + 488.61646234676004, + 803.8528896672505, + 1626.970227670753, + 1227.6707530647986, + 2197.8984238178637, + 2789.8423817863395, + 3413.3099824868655, + 4120.840630472854, + 4754.816112084063, + 4947.460595446585, +] +Fig2b_AF_param = [ + 0.2974683544303798, + 0.2860759493670886, + 0.2670886075949367, + 0.2481012658227848, + 0.23037974683544304, + 0.20506329113924052, + 0.1848101265822785, + 0.19240506329113927, + 0.17215189873417724, + 0.15822784810126583, + 0.1468354430379747, + 0.1316455696202532, + 0.12658227848101267, + 0.12151898734177217, +] + +# figure 3 +Fig3a_sol2_obs = [ + 0.09790209790209793, + 0.2517482517482518, + 0.5013986013986014, + 0.7496503496503495, + 1.0034965034965033, +] +Fig3a_AF_obs = [0.7586666666666667, 0.7453333333333334, 0.732, 0.72, 0.712] +Fig3a_sol2_param = [ + 0.10069930069930072, + 0.14405594405594407, + 0.2062937062937063, + 0.3195804195804196, + 0.4258741258741259, + 0.5706293706293706, + 0.7097902097902098, + 0.8349650349650348, + 0.9853146853146852, +] +Fig3a_AF_param = [ + 0.7186666666666667, + 0.7173333333333334, + 0.7066666666666667, + 0.7013333333333334, + 0.6946666666666667, + 0.6839999999999999, + 0.68, + 0.676, + 0.6733333333333333, +] +Fig3b_sol2_obs = [ + 0.09765765765765769, + 0.2518918918918919, + 0.49981981981981977, + 0.7506306306306305, + 1.0036036036036036, +] +Fig3b_AF_obs = [ + 0.3441696113074205, + 0.49823321554770317, + 0.6084805653710248, + 0.6734982332155477, + 0.7074204946996467, +] +Fig3b_sol2_param = [ + 0.10270270270270272, + 0.12000000000000002, + 0.14522522522522524, + 0.19423423423423425, + 0.23891891891891892, + 0.285045045045045, + 0.36720720720720723, + 0.42414414414414414, + 0.5099099099099098, + 0.6108108108108108, + 0.7109909909909908, + 0.8219819819819818, + 0.9055855855855854, + 0.9927927927927926, +] +Fig3b_AF_param = [ + 0.3017667844522968, + 0.3201413427561838, + 0.34982332155477036, + 0.4007067137809187, + 0.43745583038869257, + 0.4685512367491166, + 0.5109540636042402, + 0.537809187279152, + 0.5646643109540637, + 0.5985865724381625, + 0.6183745583038869, + 0.6409893992932862, + 0.6551236749116608, + 0.665017667844523, +] + +# figure 4 +Fig4a_rad2_obs = [ + 0.009935074902399183, + 0.02489084291235577, + 0.050627295045896775, + 0.0750810625568039, + 0.10032621481488437, + 0.25217226111925467, + 0.5095814127548347, +] +Fig4a_AF_obs = [ + 0.7866666666666666, + 0.7586666666666666, + 0.7146666666666666, + 0.6839999999999999, + 0.6506666666666667, + 0.4013333333333333, + 0.12266666666666667, +] +Fig4a_rad2_param = [ + 0.010398558176237407, + 0.011922822170487846, + 0.01483015485791573, + 0.021013064412958932, + 0.028913672210108283, + 0.03952649091680607, + 0.0519637089296805, + 0.06743017922474633, + 0.08065806660390755, + 0.09188082665813241, + 0.10777842296164428, + 0.1260156048064904, + 0.1598370702863125, + 0.19816625542394176, + 0.25882887807971816, + 0.35614478651156634, + 0.4606449735804084, + 0.5013504888178284, +] +Fig4a_AF_param = [ + 0.7466666666666666, + 0.744, + 0.7373333333333334, + 0.7266666666666666, + 0.708, + 0.6933333333333334, + 0.6719999999999999, + 0.648, + 0.6253333333333333, + 0.6133333333333333, + 0.5693333333333334, + 0.5266666666666666, + 0.4533333333333333, + 0.39066666666666666, + 0.30533333333333335, + 0.20400000000000001, + 0.12400000000000001, + 0.09200000000000001, +] +Fig4b_rad2_obs = [ + 0.009901663304056691, + 0.024822743830633227, + 0.05023442442984435, + 0.07582777734439355, + 0.10066099764048131, + 0.25485590857890217, + 0.5140622784173832, +] +Fig4b_AF_obs = [ + 0.06363636363636363, + 0.3805194805194806, + 0.7142857142857144, + 0.8532467532467534, + 0.9181818181818182, + 0.9805194805194806, + 0.9844155844155846, +] +Fig4b_rad2_param = [ + 0.010822678662544178, + 0.012760436250146357, + 0.014995662770320169, + 0.019135119880936365, + 0.02449781353843868, + 0.02860001345745697, + 0.034621184073166916, + 0.040954656754592984, + 0.04892789247282757, + 0.058261158079809215, + 0.06581302820245384, + 0.07862580283595474, + 0.08650733493819075, + 0.09517892000062586, + 0.11038713326488418, + 0.128871643665347, + 0.17797444766159812, + 0.24741109434009928, + 0.3349930256451866, + 0.4925130468761019, +] +Fig4b_AF_param = [ + 0.07532467532467532, + 0.12597402597402596, + 0.17142857142857143, + 0.24415584415584418, + 0.3337662337662338, + 0.3935064935064936, + 0.48961038961038966, + 0.5675324675324676, + 0.6584415584415586, + 0.7350649350649352, + 0.781818181818182, + 0.8324675324675326, + 0.8636363636363638, + 0.8844155844155845, + 0.903896103896104, + 0.9181818181818182, + 0.9454545454545455, + 0.9675324675324677, + 0.9753246753246754, + 0.9792207792207794, +] + +# figure 5 +Fig5a_w_obs = [ + 0.009807965902972919, + 0.10048593159311135, + 0.49755615755592225, + 1.0146489013900863, + 2.01958975016438, + 5.122402304631117, +] +Fig5a_AF_obs = [ + 0.13752711496746203, + 0.4889370932754881, + 0.7583514099783082, + 0.8533622559652929, + 0.9223427331887204, + 0.975704989154013, +] +Fig5a_w_param = [ + 0.012498045610787637, + 0.0206913808111479, + 0.03701869055846206, + 0.06559067575083277, + 0.010547703053816347, + 0.1019579400961098, + 0.15544578391512495, + 0.2347071477635516, + 0.32320173554892717, + 0.46266128305129245, + 0.6590940156644131, + 1.0547703053816342, + 1.562011440893281, + 2.214436126209504, + 3.216387910475258, + 4.786300923226383, +] +Fig5a_AF_param = [ + 0.1505422993492408, + 0.21301518438177874, + 0.2911062906724512, + 0.3691973969631237, + 0.1336225596529284, + 0.42906724511930594, + 0.4967462039045554, + 0.5761388286334057, + 0.6412147505422994, + 0.7062906724511933, + 0.7635574837310197, + 0.832537960954447, + 0.8806941431670283, + 0.9119305856832973, + 0.9418655097613884, + 0.9600867678958787, +] +Fig5b_w_obs = [ + 0.009715695698228766, + 0.1, + 0.49567650039033445, + 1.0048186394823608, + 2.0369343663782664, + 5.2008599192176295, +] +Fig5b_AF_obs = [ + 0.014237288135593246, + 0.12677966101694915, + 0.3396610169491525, + 0.47389830508474573, + 0.6230508474576271, + 0.7993220338983051, +] +Fig5b_w_param = [ + 0.011331300304886677, + 0.013602279391211175, + 0.020764800655076382, + 0.03325987924068119, + 0.05200859919217632, + 0.08533092860365864, + 0.14340893163476384, + 0.24217739964485852, + 0.3786937174482352, + 0.5921648008652588, + 0.7422730185233171, + 0.9622735280876262, + 1.3733684187482063, + 1.8770957658454737, + 2.6279930788429735, + 3.992570647470934, + 5.101811705369689, +] +Fig5b_AF_param = [ + 0.01830508474576273, + 0.025084745762711885, + 0.035932203389830525, + 0.046779661016949164, + 0.0657627118644068, + 0.08474576271186443, + 0.12542372881355934, + 0.18779661016949153, + 0.2555932203389831, + 0.3193220338983051, + 0.36, + 0.41966101694915253, + 0.4928813559322034, + 0.5647457627118644, + 0.632542372881356, + 0.7030508474576271, + 0.7505084745762711, +] diff --git a/PySDM/source/examples/PySDM_examples/Abdul_Razzak_Ghan_2000/data_from_CloudMicrophysics_ARG.py b/PySDM/source/examples/PySDM_examples/Abdul_Razzak_Ghan_2000/data_from_CloudMicrophysics_ARG.py new file mode 100644 index 0000000000000000000000000000000000000000..296793cfa327e0e42c7ae839495e618b272caf3f --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Abdul_Razzak_Ghan_2000/data_from_CloudMicrophysics_ARG.py @@ -0,0 +1,510 @@ +Fig1_N2_param_jl = [ + 100.0, + 357.89473684210526, + 615.7894736842105, + 873.6842105263157, + 1131.578947368421, + 1389.4736842105262, + 1647.3684210526314, + 1905.2631578947367, + 2163.157894736842, + 2421.052631578947, + 2678.9473684210525, + 2936.842105263158, + 3194.736842105263, + 3452.6315789473683, + 3710.5263157894733, + 3968.4210526315787, + 4226.315789473684, + 4484.210526315789, + 4742.105263157894, + 5000.0, +] +Fig1_AF_param_jl_B = [ + 0.6928152473246801, + 0.5917364671483367, + 0.5342490935050366, + 0.49115764181887744, + 0.45551852208754184, + 0.42470043158699433, + 0.39743524314577805, + 0.37299649982772204, + 0.3509094831528093, + 0.3308325003746774, + 0.3125025212729466, + 0.29570776984418223, + 0.2802726148068312, + 0.2660485058733416, + 0.2529080980340771, + 0.24074117690235697, + 0.22945167300724623, + 0.21895537852175306, + 0.20917814450814382, + 0.20005442383670796, +] +Fig1_AF_param_jl_k = [ + 0.6550058902884359, + 0.5543761524547887, + 0.5002156272369503, + 0.46073597090270774, + 0.4285085418304823, + 0.4007694945564878, + 0.37622742779739077, + 0.3541735500767811, + 0.3341658008436963, + 0.31589895390095923, + 0.2991453139815947, + 0.28372526692815925, + 0.26949151864683807, + 0.25632003795915836, + 0.24410447902761423, + 0.23275251198365215, + 0.22218325883180634, + 0.21232540594381152, + 0.203115754206168, + 0.19449806768139086, +] + +Fig2_N2_param_jl = [ + 100.0, + 357.89473684210526, + 615.7894736842105, + 873.6842105263157, + 1131.578947368421, + 1389.4736842105262, + 1647.3684210526314, + 1905.2631578947367, + 2163.157894736842, + 2421.052631578947, + 2678.9473684210525, + 2936.842105263158, + 3194.736842105263, + 3452.6315789473683, + 3710.5263157894733, + 3968.4210526315787, + 4226.315789473684, + 4484.210526315789, + 4742.105263157894, + 5000.0, +] +Fig2a_AF_param_jl_B = [ + 0.7368911341607032, + 0.6785875439974064, + 0.6491810403765178, + 0.6294671836591273, + 0.6143123146479241, + 0.6016560098935324, + 0.5905062109909635, + 0.5803303172315717, + 0.570820856281632, + 0.5617909756257858, + 0.5531227766720281, + 0.5447397419034441, + 0.5365911254192773, + 0.5286426944670407, + 0.5208710288615531, + 0.5132599034817353, + 0.5057979359069296, + 0.4984770263833678, + 0.4912913069971122, + 0.48423642529767014, +] +Fig2b_AF_param_jl_B = [ + 0.3179214254266195, + 0.259931489901035, + 0.2344717926234431, + 0.21861866122652118, + 0.20704050211732655, + 0.19775330603651697, + 0.18984651621362836, + 0.18284640970722288, + 0.1764847155065301, + 0.17059976532979892, + 0.165089265753266, + 0.1598857995056281, + 0.1549432942107356, + 0.1502291732232381, + 0.14571962185220022, + 0.14139664192848056, + 0.13724617229353103, + 0.13325686464464453, + 0.12941927279932897, + 0.12572530837033363, +] +Fig2a_AF_param_jl_k = [ + 0.7007649817406447, + 0.6394415038158453, + 0.6094951460056178, + 0.5899624539369748, + 0.5753170536557588, + 0.5633512307276619, + 0.553001598163247, + 0.5436949288005273, + 0.5350979288263984, + 0.527006062228129, + 0.5192886988166258, + 0.5118598518595352, + 0.5046615914012517, + 0.49765417713387755, + 0.4908099540518869, + 0.4841094525890756, + 0.4775388300859139, + 0.47108815494941086, + 0.46475023488349004, + 0.45851980469605463, +] +Fig2b_AF_param_jl_k = [ + 0.2807188322100428, + 0.22652407294200838, + 0.20346566323791843, + 0.18946731541950906, + 0.1794713029281252, + 0.17160595907620357, + 0.16501317591032533, + 0.15924577321499728, + 0.15404971676659163, + 0.14927138927842204, + 0.14481334479399016, + 0.14061134245412082, + 0.13662162851082177, + 0.1328135246084753, + 0.1291649277611412, + 0.12565948573147578, + 0.12228477511821012, + 0.11903109962090797, + 0.11589068264598451, + 0.11285711657688195, +] + +Fig3_sol2_param_jl = [ + 0.1, + 0.14736842105263157, + 0.19473684210526315, + 0.24210526315789474, + 0.2894736842105263, + 0.3368421052631579, + 0.38421052631578945, + 0.43157894736842106, + 0.4789473684210526, + 0.5263157894736842, + 0.5736842105263158, + 0.6210526315789474, + 0.6684210526315789, + 0.7157894736842105, + 0.7631578947368421, + 0.8105263157894737, + 0.8578947368421053, + 0.9052631578947369, + 0.9526315789473684, + 1.0, +] +Fig3a_AF_param_jl_B = [ + 0.7368911341607032, + 0.7311578661607843, + 0.726699879648498, + 0.7230006648093313, + 0.7198103599724214, + 0.7169876333940002, + 0.7144441962499347, + 0.7121209442396506, + 0.7099762336058458, + 0.7079795424964272, + 0.7061077875470988, + 0.704343058777653, + 0.7026711615652751, + 0.7010806420723289, + 0.6995621148680518, + 0.6981077863502518, + 0.6967111089899646, + 0.6953665253414051, + 0.6940692750950251, + 0.6928152473246801, +] +Fig3b_AF_param_jl_B = [ + 0.3179214254266195, + 0.38036779638199375, + 0.4270309048398107, + 0.464016857773266, + 0.49446004337365235, + 0.520190087427796, + 0.5423694022750007, + 0.5617825949418023, + 0.5789839922260482, + 0.59437947783115, + 0.6082749283504836, + 0.6209063920026614, + 0.6324596954724775, + 0.6430836333098815, + 0.6528991052505484, + 0.6620056080335593, + 0.6704859496759696, + 0.6784097391666652, + 0.6858360138022525, + 0.6928152473246801, +] +Fig3a_AF_param_jl_k = [ + 0.7007649817406447, + 0.6946812305058575, + 0.6899749461046789, + 0.686086787873277, + 0.6827465324204574, + 0.6798014981338822, + 0.6771563968897031, + 0.6747474931065041, + 0.6725299147887092, + 0.670470797622984, + 0.6685453033251423, + 0.6667341724673372, + 0.665022150050789, + 0.6633969336275505, + 0.6618484479112825, + 0.6603683308429138, + 0.6589495608841395, + 0.6575861811797992, + 0.6562730917282336, + 0.6550058902884359, +] +Fig3b_AF_param_jl_k = [ + 0.2807188322100428, + 0.34029068058615, + 0.3855174707172951, + 0.42179269432110295, + 0.4519356670710153, + 0.47761457802038615, + 0.4999008424036837, + 0.519524224591477, + 0.5370043268271644, + 0.5527242052589946, + 0.566974286523312, + 0.5799799213246881, + 0.5919193968540446, + 0.6029361214243187, + 0.6131471087049709, + 0.6226490337114756, + 0.6315226495764195, + 0.6398360701380945, + 0.6476472506111133, + 0.6550058902884359, +] + +Fig4_rad2_param_jl = [ + 0.01, + 0.035789473684210524, + 0.06157894736842105, + 0.08736842105263158, + 0.1131578947368421, + 0.13894736842105262, + 0.16473684210526315, + 0.19052631578947368, + 0.2163157894736842, + 0.2421052631578947, + 0.26789473684210524, + 0.29368421052631577, + 0.3194736842105263, + 0.3452631578947368, + 0.37105263157894736, + 0.3968421052631579, + 0.4226315789473684, + 0.44842105263157894, + 0.47421052631578947, + 0.5, +] +Fig4a_AF_param_jl_B = [ + 0.764174241194262, + 0.7153540757221017, + 0.6747007484135787, + 0.6328001551045893, + 0.5874167815291277, + 0.5390014369620802, + 0.48918338533132233, + 0.43982852696195884, + 0.3925112176692219, + 0.34831955870597253, + 0.3078646473650549, + 0.27137810957454533, + 0.23882668641688415, + 0.21001145675850857, + 0.18464241220366923, + 0.16238973938551587, + 0.14291669466840745, + 0.1258992325303389, + 0.1110366630896063, + 0.09805651490044676, +] +Fig4b_AF_param_jl_B = [ + 0.054563037787685675, + 0.5345457954742245, + 0.7744072250956857, + 0.8737840909114004, + 0.9191298169676247, + 0.9420776822863594, + 0.9547767092241997, + 0.9623461943917353, + 0.9671422335057698, + 0.970337234679177, + 0.9725555135424742, + 0.9741495189700395, + 0.9753284635504136, + 0.9762220148374265, + 0.9769135907785732, + 0.9774586189005925, + 0.9778949815877971, + 0.9782492185807069, + 0.9785403339298273, + 0.9787822023830104, +] +Fig4a_AF_param_jl_k = [ + 0.7301121820611816, + 0.678101704315436, + 0.6369296003738848, + 0.5965124673786515, + 0.5541551472306862, + 0.5097114960288587, + 0.464207039634687, + 0.41903965228988377, + 0.3755014080016033, + 0.33455860036851004, + 0.29680803015035284, + 0.26252558081555133, + 0.23174667817319558, + 0.20434556426417785, + 0.18009993322449191, + 0.15873829113157417, + 0.13997211222676453, + 0.12351624486754997, + 0.10910090439985476, + 0.09647795876789134, +] +Fig4b_AF_param_jl_k = [ + 0.04374624476213335, + 0.4920234074100025, + 0.7424034256470583, + 0.8530292067908476, + 0.9056616198479894, + 0.9330481394870913, + 0.9484833632135794, + 0.9577938457647266, + 0.9637385550973259, + 0.9677186808022951, + 0.9704912526312118, + 0.9724880316069746, + 0.9739671662322136, + 0.9750894768551425, + 0.9759588046154094, + 0.9766443326531625, + 0.9771934385357295, + 0.9776393635869319, + 0.9780059376388271, + 0.978310574049447, +] + +Fig5_w_param_jl = [ + 0.01, + 0.27263157894736845, + 0.5352631578947369, + 0.7978947368421052, + 1.0605263157894738, + 1.323157894736842, + 1.5857894736842104, + 1.848421052631579, + 2.1110526315789473, + 2.373684210526316, + 2.6363157894736844, + 2.8989473684210525, + 3.161578947368421, + 3.4242105263157896, + 3.6868421052631577, + 3.9494736842105262, + 4.212105263157895, + 4.474736842105263, + 4.737368421052632, + 5.0, +] +Fig5a_AF_param_jl_B = [ + 0.12944020220788954, + 0.6285100903048952, + 0.7482169618790103, + 0.809687409362124, + 0.8477422014917383, + 0.8737519597582984, + 0.8926792844821279, + 0.9070681627158375, + 0.9183671866773281, + 0.9274652768476896, + 0.9349395038620025, + 0.9411813657839654, + 0.9464660633456585, + 0.9509927928280987, + 0.9549093240825645, + 0.9583275976770733, + 0.9613339594406796, + 0.9639960715485484, + 0.9663676969433299, + 0.9684920845183195, +] +Fig5b_AF_param_jl_B = [ + 0.012664404475680291, + 0.21787220728204185, + 0.33054091328927154, + 0.4088266110621969, + 0.46791527403148064, + 0.5147568050963675, + 0.5531382053292883, + 0.5853523855317168, + 0.6128913056048884, + 0.6367783725254434, + 0.6577446281005739, + 0.6763293613896437, + 0.6929409903705599, + 0.7078956551854938, + 0.7214426357087101, + 0.7337816363772185, + 0.7450748652743012, + 0.7554556760353061, + 0.7650348786197523, + 0.7739054316021551, +] +Fig5a_AF_param_jl_k = [ + 0.117403467894988, + 0.5880446208721788, + 0.712784226780866, + 0.7789181752642282, + 0.8207144458055557, + 0.8497244266793125, + 0.8710961310384944, + 0.8875105843389566, + 0.9005139768964971, + 0.9110653190928761, + 0.9197929507733348, + 0.9271266240134752, + 0.9333706253053256, + 0.9387466489977918, + 0.9434201383213663, + 0.947517110689598, + 0.9511352641520638, + 0.9543515185538107, + 0.9572272627902872, + 0.9598120851214849, +] +Fig5b_AF_param_jl_k = [ + 0.010855776616507606, + 0.18813455060462483, + 0.29261119465493646, + 0.36739350821601047, + 0.4249609254828743, + 0.47127820741698506, + 0.5096847747056903, + 0.5422428017411671, + 0.5703150781619188, + 0.5948484237944858, + 0.6165265555504096, + 0.6358586338117043, + 0.653233490546411, + 0.6689543639395013, + 0.683261984989131, + 0.6963504060352029, + 0.7083781440927749, + 0.7194762071014165, + 0.7297539914455804, + 0.7393036921009909, +] diff --git a/PySDM/source/examples/PySDM_examples/Abdul_Razzak_Ghan_2000/fig_4_kinetic_limitations.ipynb b/PySDM/source/examples/PySDM_examples/Abdul_Razzak_Ghan_2000/fig_4_kinetic_limitations.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..cc48d78713af378e6a7b982881cfecc071bb7ad3 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Abdul_Razzak_Ghan_2000/fig_4_kinetic_limitations.ipynb @@ -0,0 +1,200 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "85369076917921c3", + "metadata": { + "collapsed": false + }, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Abdul_Razzak_Ghan_2000/fig_4_kinetic_limitations.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Abdul_Razzak_Ghan_2000/fig_4_kinetic_limitations.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Abdul_Razzak_Ghan_2000/fig_4_kinetic_limitations.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "83f42e3d-021e-4195-9f93-400c56329fc5", + "metadata": {}, + "source": [ + "#### based on Figs. 4 from Abdul-Razzak and Ghan 2000 (JGR: Atmos.) \"_A parameterization of aerosol activation: 2. Multiple aerosol types_\"\n", + "\n", + "An example of kinetic limitations of growth for large aerosols\n", + "\n", + "https://doi.org/10.1029/1999JD901161" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "42a7e28aed734d2f", + "metadata": {}, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c92babb49c403c66", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from matplotlib import pyplot\n", + "from open_atmos_jupyter_utils import show_plot\n", + "from PySDM.physics import si\n", + "\n", + "from PySDM_examples.Abdul_Razzak_Ghan_2000.run_ARG_parcel import run_parcel\n", + "from PySDM_examples.Abdul_Razzak_Ghan_2000 import data_from_ARG2000_paper as ARG_paper\n", + "\n", + "n_sd_per_mode = 20" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ddee02f7b4776328", + "metadata": {}, + "outputs": [], + "source": [ + "rad2 = np.geomspace(10,500,5) * si.nm\n", + "AF_S = np.zeros((2,len(rad2)))\n", + "AF_V = np.zeros((2,len(rad2)))\n", + "AFerror = np.zeros(len(rad2))\n", + "\n", + "w = 0.5 * si.m / si.s\n", + "N2 = 100 / si.cm**3\n", + "sol2 = 1 # 100% ammonium sulfate\n", + "\n", + "for i,rad2i in enumerate(rad2):\n", + " output = run_parcel(w, sol2, N2, rad2i, n_sd_per_mode)\n", + " AF_S[:,i] = output.activated_fraction_S\n", + " AF_V[:,i] = output.activated_fraction_V\n", + " AFerror[i] = output.error[0]\n", + "\n", + "fig, axes = pyplot.subplots(2, 1, sharex=True, figsize=(6,6))\n", + "axes[0].plot(np.asarray(ARG_paper.Fig4a_rad2_obs)*1e3, ARG_paper.Fig4a_AF_obs, \"ko\", label=\"ARG 2000 data\")\n", + "axes[0].plot(np.asarray(ARG_paper.Fig4a_rad2_param)*1e3, ARG_paper.Fig4a_AF_param, \"k-\", label=\"ARG 2000 param\")\n", + "axes[1].plot(np.asarray(ARG_paper.Fig4b_rad2_obs)*1e3, ARG_paper.Fig4b_AF_obs, \"ko\")\n", + "axes[1].plot(np.asarray(ARG_paper.Fig4b_rad2_param)*1e3, ARG_paper.Fig4b_AF_param, \"k-\", label=\"ARG 2000 param\")\n", + "\n", + "for i, ax in enumerate(axes):\n", + " ax.errorbar(rad2 / si.nm, AF_S[i,:], yerr=AFerror, fmt='o', capsize=4, label=\"PySDM, Scrit def\")\n", + " ax.errorbar(rad2 / si.nm, AF_V[i,:], yerr=AFerror, fmt='x', capsize=2, label=\"PySDM, Vcrit def\")\n", + " ax.set_ylabel(f'Mode {i+1} Activated Fraction')\n", + " ax.set_ylim([0,1.1])\n", + " ax.set_xscale('log')\n", + " ax.set_xlim([10,1000])\n", + "\n", + "pyplot.xlabel('Mode 2 Radius (nm)')\n", + "axes[0].legend(loc=\"best\")\n", + "show_plot(\"fig_4.pdf\")" + ] + }, + { + "cell_type": "markdown", + "id": "58b8d6584bfa9da5", + "metadata": {}, + "source": [ + "## Kinetic limitations\n", + "\n", + "Differences in activated fraction as diagnosed from peak supersaturation (compared to critical supersaturations) vs. droplet volumes (compared to critical volumes) is evident in this example for the cases where Mode 2 has a very large mean radius. Here we can see the kinetic limitations of activation where the large droplets take finite time to come into equilibrium with the ambient humidity and grow past their critical sizes. This is explored more in the figures below which show first the profile of ambient humidity compared to the critical supersaturation of each droplet, and then profiles of the ratio of the actual droplet volume to the critical volume. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "16a5e3b65236b5f6", + "metadata": {}, + "outputs": [], + "source": [ + "for drop_id, Scrit in enumerate(output.attributes['critical saturation']):\n", + " if drop_id < n_sd_per_mode:\n", + " pyplot.plot(\n", + " np.asarray(Scrit) - 1,\n", + " output.profile['z'],\n", + " color='magenta',\n", + " label=\"Scrit mode 1\" if drop_id == 0 else \"\"\n", + " )\n", + " else:\n", + " pyplot.plot(\n", + " np.asarray(Scrit) - 1,\n", + " output.profile['z'],\n", + " color='blue',\n", + " label=\"Scrit mode 2\" if drop_id == n_sd_per_mode else \"\"\n", + " )\n", + " pyplot.xlabel('Scrit [%]')\n", + " pyplot.ylabel('z [m]')\n", + "\n", + "pyplot.plot(np.asarray(output.profile['RH'])-1, output.profile['z'], 'k', lw=3, label='ambient RH')\n", + "pyplot.xscale('log')\n", + "pyplot.legend(loc='lower right')\n", + "pyplot.grid()\n", + "show_plot(\"RH_profile.pdf\")\n", + "\n", + "from PySDM import Formulae\n", + "frm = Formulae()\n", + "for drop_id, Vcrit in enumerate(output.attributes['critical volume']):\n", + " volume = np.asarray(output.attributes['volume'][drop_id])\n", + " if drop_id < n_sd_per_mode:\n", + " pyplot.plot(\n", + " frm.trivia.radius(volume=volume) / frm.trivia.radius(volume=np.asarray(Vcrit)),\n", + " output.profile['z'],\n", + " color='magenta',\n", + " label=\"mode 1\" if drop_id == 0 else \"\"\n", + " )\n", + " else:\n", + " pyplot.plot(\n", + " frm.trivia.radius(volume=volume) / frm.trivia.radius(volume=np.asarray(Vcrit)),\n", + " output.profile['z'],\n", + " color='blue',\n", + " label=\"mode 2\" if drop_id == n_sd_per_mode else \"\"\n", + " )\n", + " pyplot.xlabel('Volume / Vcrit')\n", + " pyplot.ylabel('z [m]')\n", + " pyplot.axvline(1, color='k')\n", + "\n", + "pyplot.xscale('log')\n", + "pyplot.legend(loc='lower right')\n", + "pyplot.grid()\n", + "show_plot(\"Droplet_profile.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "272a4bfc3f2a6412", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/PySDM/source/examples/PySDM_examples/Abdul_Razzak_Ghan_2000/figs1-5.ipynb b/PySDM/source/examples/PySDM_examples/Abdul_Razzak_Ghan_2000/figs1-5.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..ec1e60c89aad2a14adb996d0983a8e31aeff8ac4 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Abdul_Razzak_Ghan_2000/figs1-5.ipynb @@ -0,0 +1,10283 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "6a24a08f", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Abdul_Razzak_Ghan_2000/figs1-5.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Abdul_Razzak_Ghan_2000/figs1-5.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Abdul_Razzak_Ghan_2000/figs1-5.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "87ff1568", + "metadata": {}, + "source": [ + "#### based on Figs. 1-5 from Abdul-Razzak and Ghan 2000 (JGR: Atmos.) \"_A parameterization of aerosol activation: 2. Multiple aerosol types_\"\n", + "https://doi.org/10.1029/1999JD901161" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "8f0dfc29-5a88-4d7f-bcc8-10df9185d386", + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:10:26.301401Z", + "start_time": "2024-02-01T07:10:26.295293Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "c000670c", + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:11:41.551370Z", + "start_time": "2024-02-01T07:11:41.548264Z" + } + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "from matplotlib import pyplot\n", + "from open_atmos_jupyter_utils import show_plot\n", + "from PySDM.physics import si\n", + "\n", + "from PySDM_examples.Abdul_Razzak_Ghan_2000.run_ARG_parcel import run_parcel\n", + "from PySDM_examples.Abdul_Razzak_Ghan_2000 import data_from_ARG2000_paper as ARG_paper\n", + "from PySDM_examples.Abdul_Razzak_Ghan_2000 import data_from_CloudMicrophysics_ARG as ARG_CMjl\n", + "\n", + "n_sd_per_mode = 10" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "2a21f4e9", + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:10:50.590811Z", + "start_time": "2024-02-01T07:10:28.013213Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-02-01T08:10:50.563916\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "9a1994b0f0cd4c099b199006d206db60", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./fig_1.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "N2 = np.linspace(100,5000,5) / si.cm**3\n", + "AF_S = np.zeros((2,len(N2)))\n", + "AF_V = np.zeros((2,len(N2)))\n", + "AFerror = np.zeros(len(N2))\n", + "\n", + "w = 0.5 * si.m / si.s\n", + "sol2 = 1.0 # 100% ammonium sulfate\n", + "rad2 = 50.0 * si.nm\n", + "\n", + "for i,N2i in enumerate(N2):\n", + " output = run_parcel(w, sol2, N2i, rad2, n_sd_per_mode)\n", + " AF_S[:,i] = output.activated_fraction_S\n", + " AF_V[:,i] = output.activated_fraction_V\n", + " AFerror[i] = output.error[0]\n", + "\n", + "fig, ax = pyplot.subplots(1, 1, sharex=True, figsize=(6,3))\n", + "ax.plot(ARG_paper.Fig1_N2_obs, ARG_paper.Fig1_AF_obs, \"ko\", label=\"ARG 2000 data\")\n", + "ax.plot(ARG_paper.Fig1_N2_param, ARG_paper.Fig1_AF_param, \"k-\", label=\"ARG 2000 param\")\n", + "\n", + "ax.plot(ARG_CMjl.Fig1_N2_param_jl, ARG_CMjl.Fig1_AF_param_jl_B, \"k--\", label=\"CloudMicrophysics.jl param (B)\")\n", + "\n", + "ax.errorbar(N2 * si.cm**3, AF_S[0,:], yerr=AFerror, fmt='o', capsize=4, label=\"PySDM, Scrit def\")\n", + "ax.errorbar(N2 * si.cm**3, AF_V[0,:], yerr=AFerror, fmt='x', capsize=2, label=\"PySDM, Vcrit def\")\n", + "ax.set_ylabel('Mode 1 Activated Fraction')\n", + "ax.set_ylim([0,1.1])\n", + "\n", + "pyplot.xlabel('Mode 2 Aerosol Number (cm$^{-3}$)')\n", + "pyplot.legend(loc=\"best\")\n", + "show_plot(\"fig_1.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "2aa5322c", + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:11:03.123232Z", + "start_time": "2024-02-01T07:10:50.602973Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-02-01T08:11:03.081914\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "04c8459810644f0180981ff5b6e156dc", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./fig_2.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "N2 = np.linspace(100,5000,5) / si.cm**3\n", + "AF_S = np.zeros((2,len(N2)))\n", + "AF_V = np.zeros((2,len(N2)))\n", + "AFerror = np.zeros(len(N2))\n", + "\n", + "w = 0.5 * si.m / si.s\n", + "sol2 = 0.1 # 10% ammonium sulfate, 90% insoluble\n", + "rad2 = 50.0 * si.nm\n", + "\n", + "for i,N2i in enumerate(N2):\n", + " output = run_parcel(w, sol2, N2i, rad2, n_sd_per_mode)\n", + " AF_S[:,i] = output.activated_fraction_S\n", + " AF_V[:,i] = output.activated_fraction_V\n", + " AFerror[i] = output.error[0]\n", + "\n", + "fig, axes = pyplot.subplots(2, 1, sharex=True, figsize=(6,6))\n", + "axes[0].plot(ARG_paper.Fig2a_N2_obs, ARG_paper.Fig2a_AF_obs, \"ko\", label=\"ARG 2000 data\")\n", + "axes[0].plot(ARG_paper.Fig2a_N2_param, ARG_paper.Fig2a_AF_param, \"k-\", label=\"ARG 2000 param\")\n", + "axes[0].plot(ARG_CMjl.Fig2_N2_param_jl, ARG_CMjl.Fig2a_AF_param_jl_B, \"k--\", label=\"CloudMicrophysics.jl param (B)\")\n", + "axes[1].plot(ARG_paper.Fig2b_N2_obs, ARG_paper.Fig2b_AF_obs, \"ko\")\n", + "axes[1].plot(ARG_paper.Fig2b_N2_param, ARG_paper.Fig2b_AF_param, \"k-\", label=\"ARG 2000 param\")\n", + "axes[1].plot(ARG_CMjl.Fig2_N2_param_jl, ARG_CMjl.Fig2b_AF_param_jl_B, \"k--\", label=\"CloudMicrophysics.jl param (B)\")\n", + "\n", + "for i, ax in enumerate(axes):\n", + " ax.errorbar(N2 * si.cm**3, AF_S[i,:], yerr=AFerror, fmt='o', capsize=4, label=\"PySDM, Scrit def\")\n", + " ax.errorbar(N2 * si.cm**3, AF_V[i,:], yerr=AFerror, fmt='x', capsize=2, label=\"PySDM, Vcrit def\")\n", + " ax.set_ylabel(f'Mode {i+1} Activated Fraction')\n", + " ax.set_ylim([0,1.1])\n", + "\n", + "pyplot.xlabel('Mode 2 Aerosol Number (cm$^{-3}$)')\n", + "axes[0].legend(loc=\"best\")\n", + "show_plot(\"fig_2.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "9ca851f8", + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:11:15.930401Z", + "start_time": "2024-02-01T07:11:03.142130Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-02-01T08:11:15.890000\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "3895e9b0111c429d97ed08b7252ca36c", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./fig_3.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "sol2 = np.linspace(0.1,1,5) # X% ammonium sulfate, (1-X)% insoluble\n", + "AF_S = np.zeros((2,len(sol2)))\n", + "AF_V = np.zeros((2,len(sol2)))\n", + "AFerror = np.zeros(len(sol2))\n", + "\n", + "w = 0.5 * si.m / si.s\n", + "N2 = 100 / si.cm**3\n", + "rad2 = 50.0 * si.nm\n", + "\n", + "for i,sol2i in enumerate(sol2):\n", + " output = run_parcel(w, sol2i, N2, rad2, n_sd_per_mode)\n", + " AF_S[:,i] = output.activated_fraction_S\n", + " AF_V[:,i] = output.activated_fraction_V\n", + " AFerror[i] = output.error[0]\n", + "\n", + "fig, axes = pyplot.subplots(2, 1, sharex=True, figsize=(6,6))\n", + "axes[0].plot(ARG_paper.Fig3a_sol2_obs, ARG_paper.Fig3a_AF_obs, \"ko\", label=\"ARG 2000 data\")\n", + "axes[0].plot(ARG_paper.Fig3a_sol2_param, ARG_paper.Fig3a_AF_param, \"k-\", label=\"ARG 2000 param\")\n", + "axes[0].plot(ARG_CMjl.Fig3_sol2_param_jl, ARG_CMjl.Fig3a_AF_param_jl_B, \"k--\", label=\"CloudMicrophysics.jl param (B)\")\n", + "axes[1].plot(ARG_paper.Fig3b_sol2_obs, ARG_paper.Fig3b_AF_obs, \"ko\")\n", + "axes[1].plot(ARG_paper.Fig3b_sol2_param, ARG_paper.Fig3b_AF_param, \"k-\", label=\"ARG 2000 param\")\n", + "axes[1].plot(ARG_CMjl.Fig3_sol2_param_jl, ARG_CMjl.Fig3b_AF_param_jl_B, \"k--\", label=\"CloudMicrophysics.jl param (B)\")\n", + "\n", + "for i, ax in enumerate(axes):\n", + " ax.errorbar(sol2, AF_S[i,:], yerr=AFerror, fmt='o', capsize=4, label=\"PySDM, Scrit def\")\n", + " ax.errorbar(sol2, AF_V[i,:], yerr=AFerror, fmt='x', capsize=2, label=\"PySDM, Vcrit def\")\n", + " ax.set_ylabel(f'Mode {i+1} Activated Fraction')\n", + " ax.set_ylim([0,1.1])\n", + "\n", + "pyplot.xlabel('Mode 2 Soluble Mass Fraction')\n", + "axes[0].legend(loc=\"best\")\n", + "show_plot(\"fig_3.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "60e22fd9", + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:11:28.980772Z", + "start_time": "2024-02-01T07:11:15.942137Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-02-01T08:11:28.919899\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "39fde89993044dc89b1a3253442fb747", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./fig_4.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "rad2 = np.geomspace(10,500,5) * si.nm\n", + "AF_S = np.zeros((2,len(rad2)))\n", + "AF_V = np.zeros((2,len(rad2)))\n", + "AFerror = np.zeros(len(rad2))\n", + "\n", + "w = 0.5 * si.m / si.s\n", + "N2 = 100 / si.cm**3\n", + "sol2 = 1 # 100% ammonium sulfate\n", + "\n", + "for i,rad2i in enumerate(rad2):\n", + " output = run_parcel(w, sol2, N2, rad2i, n_sd_per_mode)\n", + " AF_S[:,i] = output.activated_fraction_S\n", + " AF_V[:,i] = output.activated_fraction_V\n", + " AFerror[i] = output.error[0]\n", + "\n", + "fig, axes = pyplot.subplots(2, 1, sharex=True, figsize=(6,6))\n", + "axes[0].plot(np.asarray(ARG_paper.Fig4a_rad2_obs)*1e3, ARG_paper.Fig4a_AF_obs, \"ko\", label=\"ARG 2000 data\")\n", + "axes[0].plot(np.asarray(ARG_paper.Fig4a_rad2_param)*1e3, ARG_paper.Fig4a_AF_param, \"k-\", label=\"ARG 2000 param\")\n", + "axes[0].plot(np.asarray(ARG_CMjl.Fig4_rad2_param_jl)*1e3, ARG_CMjl.Fig4a_AF_param_jl_B, \"k--\", label=\"CloudMicrophysics.jl param (B)\")\n", + "axes[1].plot(np.asarray(ARG_paper.Fig4b_rad2_obs)*1e3, ARG_paper.Fig4b_AF_obs, \"ko\")\n", + "axes[1].plot(np.asarray(ARG_paper.Fig4b_rad2_param)*1e3, ARG_paper.Fig4b_AF_param, \"k-\", label=\"ARG 2000 param\")\n", + "axes[1].plot(np.asarray(ARG_CMjl.Fig4_rad2_param_jl)*1e3, ARG_CMjl.Fig4b_AF_param_jl_B, \"k--\", label=\"CloudMicrophysics.jl param (B)\")\n", + "\n", + "for i, ax in enumerate(axes):\n", + " ax.errorbar(rad2 / si.nm, AF_S[i,:], yerr=AFerror, fmt='o', capsize=4, label=\"PySDM, Scrit def\")\n", + " ax.errorbar(rad2 / si.nm, AF_V[i,:], yerr=AFerror, fmt='x', capsize=2, label=\"PySDM, Vcrit def\")\n", + " ax.set_ylabel(f'Mode {i+1} Activated Fraction')\n", + " ax.set_ylim([0,1.1])\n", + " ax.set_xscale('log')\n", + " ax.set_xlim([10,1000])\n", + "\n", + "pyplot.xlabel('Mode 2 Radius (nm)')\n", + "axes[0].legend(loc=\"best\")\n", + "show_plot(\"fig_4.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "2ae85602", + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:11:41.540705Z", + "start_time": "2024-02-01T07:11:28.998583Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-02-01T08:11:41.473118\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "8298779be49b4af8897e6812d64febc2", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./fig_5.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "w = np.geomspace(0.01, 5, 5) * si.m / si.s\n", + "AF_S = np.zeros((2,len(w)))\n", + "AF_V = np.zeros((2,len(w)))\n", + "AFerror = np.zeros(len(w))\n", + "\n", + "N2 = 100 / si.cm**3\n", + "rad2 = 50.0 * si.nm\n", + "sol2 = 0.1 # 10% ammonium sulfate; 90% insoluble\n", + "\n", + "for i,wi in enumerate(w):\n", + " output = run_parcel(wi, sol2, N2, rad2, n_sd_per_mode)\n", + " AF_S[:,i] = output.activated_fraction_S\n", + " AF_V[:,i] = output.activated_fraction_V\n", + " AFerror[i] = output.error[0]\n", + "\n", + "fig, axes = pyplot.subplots(2, 1, sharex=True, figsize=(6,6))\n", + "axes[0].plot(ARG_paper.Fig5a_w_obs, ARG_paper.Fig5a_AF_obs, \"ko\", label=\"ARG 2000 data\")\n", + "axes[0].plot(ARG_paper.Fig5a_w_param, ARG_paper.Fig5a_AF_param, \"k-\", label=\"ARG 2000 param\")\n", + "axes[0].plot(ARG_CMjl.Fig5_w_param_jl, ARG_CMjl.Fig5a_AF_param_jl_B, \"k--\", label=\"CloudMicrophysics.jl param (B)\")\n", + "axes[1].plot(ARG_paper.Fig5b_w_obs, ARG_paper.Fig5b_AF_obs, \"ko\")\n", + "axes[1].plot(ARG_paper.Fig5b_w_param, ARG_paper.Fig5b_AF_param, \"k-\", label=\"ARG 2000 param\")\n", + "axes[1].plot(ARG_CMjl.Fig5_w_param_jl, ARG_CMjl.Fig5b_AF_param_jl_B, \"k--\", label=\"CloudMicrophysics.jl param (B)\")\n", + "\n", + "for i, ax in enumerate(axes):\n", + " ax.errorbar(w, AF_S[i,:], yerr=AFerror, fmt='o', capsize=4, label=\"PySDM, Scrit def\")\n", + " ax.errorbar(w, AF_V[i,:], yerr=AFerror, fmt='x', capsize=2, label=\"PySDM, Vcrit def\")\n", + " ax.set_ylabel(f'Mode {i+1} Activated Fraction')\n", + " ax.set_ylim([0,1.1])\n", + " ax.set_xscale('log')\n", + " ax.set_xlim([0.01, 10])\n", + "\n", + "pyplot.xlabel('Updraft velocity (m/s)')\n", + "axes[0].legend(loc=\"best\")\n", + "show_plot(\"fig_5.pdf\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.10.9 ('pysdm')", + "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.9" + }, + "vscode": { + "interpreter": { + "hash": "b14f34a08619f4a218d80d7380beed3f0c712c89ff93e7183219752d640ed427" + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/PySDM/source/examples/PySDM_examples/Abdul_Razzak_Ghan_2000/run_ARG_parcel.py b/PySDM/source/examples/PySDM_examples/Abdul_Razzak_Ghan_2000/run_ARG_parcel.py new file mode 100644 index 0000000000000000000000000000000000000000..ccd8e8ed1e2f8ce41d993187b106c300c55e24b5 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Abdul_Razzak_Ghan_2000/run_ARG_parcel.py @@ -0,0 +1,145 @@ +from collections import namedtuple + +import numpy as np +from PySDM_examples.Abdul_Razzak_Ghan_2000.aerosol import CONSTANTS_ARG, AerosolARG + +from PySDM import Builder, Formulae +from PySDM import products as PySDM_products +from PySDM.backends import CPU +from PySDM.dynamics import AmbientThermodynamics, Condensation +from PySDM.environments import Parcel +from PySDM.initialisation.hygroscopic_equilibrium import equilibrate_wet_radii +from PySDM.initialisation.sampling.spectral_sampling import ConstantMultiplicity +from PySDM.physics import si + + +def run_parcel( + w, + sol2, + N2, + rad2, + n_sd_per_mode, + RH0=1.0, + T0=294 * si.K, + p0=1e5 * si.Pa, + n_steps=50, + mass_of_dry_air=1e3 * si.kg, + dt=2 * si.s, +): + products = ( + PySDM_products.WaterMixingRatio(unit="g/kg", name="liquid water mixing ratio"), + PySDM_products.PeakSaturation(name="S max"), + PySDM_products.AmbientRelativeHumidity(name="RH"), + PySDM_products.ParcelDisplacement(name="z"), + ) + + formulae = Formulae(constants=CONSTANTS_ARG) + const = formulae.constants + pv0 = RH0 * formulae.saturation_vapour_pressure.pvs_water(T0) + + env = Parcel( + dt=dt, + mass_of_dry_air=mass_of_dry_air, + p0=p0, + initial_water_vapour_mixing_ratio=const.eps * pv0 / (p0 - pv0), + w=w, + T0=T0, + ) + + aerosol = AerosolARG( + M2_sol=sol2, M2_N=N2, M2_rad=rad2, water_molar_volume=const.Mv / const.rho_w + ) + n_sd = n_sd_per_mode * len(aerosol.modes) + + builder = Builder(backend=CPU(formulae), n_sd=n_sd, environment=env) + builder.add_dynamic(AmbientThermodynamics()) + builder.add_dynamic(Condensation()) + builder.request_attribute("critical saturation") + + attributes = { + k: np.empty(0) for k in ("dry volume", "kappa times dry volume", "multiplicity") + } + for i, mode in enumerate(aerosol.modes): + kappa, spectrum = mode["kappa"]["CompressedFilmOvadnevaite"], mode["spectrum"] + r_dry, concentration = ConstantMultiplicity(spectrum).sample_deterministic( + n_sd_per_mode + ) + v_dry = builder.formulae.trivia.volume(radius=r_dry) + specific_concentration = concentration / builder.formulae.constants.rho_STP + attributes["multiplicity"] = np.append( + attributes["multiplicity"], + specific_concentration * builder.particulator.environment.mass_of_dry_air, + ) + attributes["dry volume"] = np.append(attributes["dry volume"], v_dry) + attributes["kappa times dry volume"] = np.append( + attributes["kappa times dry volume"], v_dry * kappa + ) + + r_wet = equilibrate_wet_radii( + r_dry=builder.formulae.trivia.radius(volume=attributes["dry volume"]), + environment=builder.particulator.environment, + kappa_times_dry_volume=attributes["kappa times dry volume"], + ) + attributes["volume"] = builder.formulae.trivia.volume(radius=r_wet) + + particulator = builder.build(attributes, products=products) + + output = {product.name: [] for product in particulator.products.values()} + output_attributes = { + "multiplicity": tuple([] for _ in range(particulator.n_sd)), + "volume": tuple([] for _ in range(particulator.n_sd)), + "critical volume": tuple([] for _ in range(particulator.n_sd)), + "critical saturation": tuple([] for _ in range(particulator.n_sd)), + } + + for _ in range(n_steps): + particulator.run(steps=1) + for product in particulator.products.values(): + value = product.get() + output[product.name].append(value[0]) + for key, attr in output_attributes.items(): + attr_data = particulator.attributes[key].to_ndarray() + for drop_id in range(particulator.n_sd): + attr[drop_id].append(attr_data[drop_id]) + + error = np.zeros(len(aerosol.modes)) + activated_fraction_S = np.zeros(len(aerosol.modes)) + activated_fraction_V = np.zeros(len(aerosol.modes)) + for j, mode in enumerate(aerosol.modes): + activated_drops_j_S = 0 + activated_drops_j_V = 0 + RHmax = np.nanmax(np.asarray(output["RH"])) + for i, volume in enumerate(output_attributes["volume"]): + if j * n_sd_per_mode <= i < (j + 1) * n_sd_per_mode: + if output_attributes["critical saturation"][i][-1] < RHmax: + activated_drops_j_S += output_attributes["multiplicity"][i][-1] + if output_attributes["critical volume"][i][-1] < volume[-1]: + activated_drops_j_V += output_attributes["multiplicity"][i][-1] + Nj = np.asarray(output_attributes["multiplicity"])[ + j * n_sd_per_mode : (j + 1) * n_sd_per_mode, -1 + ] + max_multiplicity_j = np.max(Nj) + sum_multiplicity_j = np.sum(Nj) + error[j] = max_multiplicity_j / sum_multiplicity_j + activated_fraction_S[j] = activated_drops_j_S / sum_multiplicity_j + activated_fraction_V[j] = activated_drops_j_V / sum_multiplicity_j + + Output = namedtuple( + "Output", + [ + "profile", + "attributes", + "aerosol", + "activated_fraction_S", + "activated_fraction_V", + "error", + ], + ) + return Output( + profile=output, + attributes=output_attributes, + aerosol=aerosol, + activated_fraction_S=activated_fraction_S, + activated_fraction_V=activated_fraction_V, + error=error, + ) diff --git a/PySDM/source/examples/PySDM_examples/Alpert_and_Knopf_2016/__init__.py b/PySDM/source/examples/PySDM_examples/Alpert_and_Knopf_2016/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..c7d499822b1d7ebd7531a5fca66ea516ecdb11ee --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Alpert_and_Knopf_2016/__init__.py @@ -0,0 +1,24 @@ +""" +box-environment example based on +[Alpert & Knopf 2016 (Atmos. Chem. Phys. 16)](https://doi.org/10.5194/acp-16-2083-2016) + +fig_1.ipynb: +.. include:: ./fig_1.ipynb.badges.md + +fig_2.ipynb: +.. include:: ./fig_2.ipynb.badges.md + +fig_3.ipynb: +.. include:: ./fig_3.ipynb.badges.md + +fig_4.ipynb: +.. include:: ./fig_4.ipynb.badges.md + +fig_5.ipynb: +.. include:: ./fig_5.ipynb.badges.md +""" + +# pylint: disable=invalid-name +from .simulation import Simulation, simulation +from .table_1 import Table1 +from .table_2 import Table2 diff --git a/PySDM/source/examples/PySDM_examples/Alpert_and_Knopf_2016/fig_1.ipynb b/PySDM/source/examples/PySDM_examples/Alpert_and_Knopf_2016/fig_1.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..d8f4316c17851188223ddb4977b433429c81d87e --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Alpert_and_Knopf_2016/fig_1.ipynb @@ -0,0 +1,13906 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Alpert_and_Knopf_2016/fig_1.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Alpert_and_Knopf_2016/fig_1.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Alpert_and_Knopf_2016/fig_1.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### based on Fig. 1 from Alpert and Knopf 2016 (Atmos. Chem. Phys. 16) \"_Analysis of isothermal and cooling-rate-dependent immersion freezing by a unifying stochastic ice nucleation model_\"\n", + "https://doi.org/10.5194/acp-16-2083-2016" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-03T12:19:25.327245Z", + "start_time": "2025-07-03T12:19:25.313099Z" + }, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-03T12:19:42.014095Z", + "start_time": "2025-07-03T12:19:27.622324Z" + }, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "from open_atmos_jupyter_utils import show_plot\n", + "from PySDM_examples.Alpert_and_Knopf_2016 import Simulation, Table1\n", + "from PySDM.physics import si" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-03T12:19:42.039683Z", + "start_time": "2025-07-03T12:19:42.036237Z" + }, + "pycharm": { + "name": "#%%" + } + }, + "outputs": [], + "source": [ + "cases = Table1()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Fig 1 a" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-03T12:22:08.045481Z", + "start_time": "2025-07-03T12:19:42.092230Z" + } + }, + "outputs": [], + "source": [ + "sim_1a = Simulation(cases=cases, time_step=1*si.s, total_time=6*si.min)\n", + "sim_1a.run(('Iso3', 'Iso4', 'Iso1', 'Iso2'))" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-03T12:22:09.710138Z", + "start_time": "2025-07-03T12:22:08.113492Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-03T14:22:09.626514\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "346cb5497b92477aa3f79bc0e826af93", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_1a.pdf
\"), HTML(value=\"\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-03T14:22:46.857934\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "8d4651491f114640a12a4abda8304e5a", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_1b.pdf
\"), HTML(value=\"\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-03T14:25:50.644356\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "66e331e5a40546e0ad52ebc26c1057c3", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_2a.pdf
\"), HTML(value=\"\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-03T14:26:25.155900\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f9f4d47d48e74f6688465090ca781a9f", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_2b.pdf
\"), HTML(value=\"\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-03T14:27:00.475620\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "d0162b0ff76c4b5b943c9eb276c9342e", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_2c.pdf
\"), HTML(value=\"\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-03T14:30:59.958213\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "1522d2b9fdbf4ed0b53f77454f8f8ef9", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_3.pdf
\"), HTML(value=\"\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-03T15:02:37.284991\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "99025b6ba8a4428eaf2d66447ed5c6dc", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_4a.pdf
\"), HTML(value=\"\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-03T15:02:38.889040\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "860452254cdb4425adb5a06e7d4e97b7", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_4b.pdf
\"), HTML(value=\"\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-03T15:02:39.533356\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f2a39fc3b12d4a6383c7fd0366ed02d8", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_4cd.pdf
\"), HTML(value=\"\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-03T15:17:37.735857\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "941aa9816eb8484495b704a1505856c9", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_5a.pdf
\"), HTML(value=\"\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-03T15:17:50.566960\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "b16bd4843b3b4e26821d8886cdcab570", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_5b.pdf
\"), HTML(value=\"\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-03T15:17:52.536777\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "8483459a4d56411fa493ce225dabb795", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_5c.pdf
\"), HTML(value=\"= version.parse("3.3.0"): + pyplot.gca().set_box_aspect(1) + pyplot.legend() + if grid is not None: + pyplot.grid(which=grid) + pyplot.ylim(ylim) + if self.temperature_range: + pyplot.xlim(*self.temperature_range) + pyplot.xlabel("T / K") + pyplot.ylabel("$f_{frz}$") + else: + pyplot.xlim(0, self.total_time / si.min) + pyplot.xlabel("t / min") + pyplot.ylabel("$f_{ufz}$") + pyplot.yscale("log") + + def plot_j_het(self, variant: str, abifm_params_case: str, ylim=None): + assert variant in ("apparent", "actual") + + formulae = Formulae( + particle_shape_and_density="MixedPhaseSpheres", + heterogeneous_ice_nucleation_rate="ABIFM", + constants={ + "ABIFM_M": self.cases[abifm_params_case]["ABIFM_m"], + "ABIFM_C": self.cases[abifm_params_case]["ABIFM_c"], + }, + ) + + yunit = 1 / si.cm**2 / si.s + svp = formulae.saturation_vapour_pressure + plot_x = np.linspace(*self.temperature_range) * si.K + plot_y = formulae.heterogeneous_ice_nucleation_rate.j_het( + svp.pvs_ice(plot_x) / svp.pvs_water(plot_x) + ) + pyplot.grid() + pyplot.plot(plot_x, plot_y / yunit, color="red", label="ABIFM $J_{het}$") + + for key in self.output: + for run in range(self.n_runs_per_case): + time = self.time_step * np.arange(len(self.output[key][run]["f_ufz"])) + if self.cases[key]["cooling_rate"] == 0: + raise NotImplementedError() + + temperature = ( + self.temperature_range[1] - time * self.cases[key]["cooling_rate"] + ) + spec = self.cases[key]["ISA"] + + particle_number = spec.norm_factor * self.volume + n_ufz = particle_number * np.asarray(self.output[key][run]["f_ufz"]) + n_frz = particle_number - n_ufz + + j_het = np.diff(n_frz) / self.time_step + if variant == "apparent": + j_het /= n_ufz[:-1] * spec.m_mode + else: + a_tot = np.asarray(self.output[key][run]["A_tot"][:-1]) + j_het = np.divide( + j_het, a_tot, out=np.zeros_like(j_het), where=a_tot != 0 + ) + + pyplot.scatter( + temperature[:-1] + np.diff(temperature) / 2, + np.where(j_het != 0, j_het, np.nan) / yunit, + label=self.cases.label(key) if run == 0 else None, + color=self.cases[key]["color"], + ) + key = None + + pyplot.yscale("log") + pyplot.xlabel("K") + pyplot.ylabel( + f"$J_{{het}}$, $J_{{het}}^{{{variant}}}$ / cm$^{{-2}}$ s$^{{-1}}$" + ) + pyplot.xlim(self.temperature_range) + if ylim is not None: + pyplot.ylim(ylim) + pyplot.legend() + if version.parse(matplotlib.__version__) >= version.parse("3.3.0"): + pyplot.gca().set_box_aspect(1) + + +def simulation( + *, + constants, + seed, + n_sd, + time_step, + volume, + spectrum, + droplet_volume, + multiplicity, + total_time, + number_of_real_droplets, + cooling_rate=0, + heterogeneous_ice_nucleation_rate="Constant", + initial_temperature=np.nan, +): + formulae = Formulae( + seed=seed, + heterogeneous_ice_nucleation_rate=heterogeneous_ice_nucleation_rate, + constants=constants, + particle_shape_and_density="MixedPhaseSpheres", + ) + builder = Builder( + n_sd=n_sd, + backend=CPU(formulae=formulae), + environment=Box(dt=time_step, dv=volume), + ) + builder.add_dynamic(Freezing(immersion_freezing="time-dependent")) + builder.request_attribute("volume") + + if hasattr(spectrum, "s_geom") and spectrum.s_geom == 1: + _isa, _conc = np.full(n_sd, spectrum.m_mode), np.full( + n_sd, multiplicity / volume + ) + else: + _isa, _conc = spectral_sampling.ConstantMultiplicity( + spectrum + ).sample_deterministic(n_sd) + attributes = { + "multiplicity": discretise_multiplicities(_conc * volume), + "immersed surface area": _isa, + "signed water mass": np.full(n_sd, droplet_volume * formulae.constants.rho_w), + } + np.testing.assert_almost_equal(attributes["multiplicity"], multiplicity) + products = ( + IceWaterContent(name="qi"), + TotalUnfrozenImmersedSurfaceArea(name="A_tot"), + ) + particulator = builder.build(attributes=attributes, products=products) + env = particulator.environment + + env["T"] = initial_temperature + env["a_w_ice"] = np.nan + env["RH"] = 1 + np.finfo(float).eps + svp = particulator.formulae.saturation_vapour_pressure + + cell_id = 0 + f_ufz = [] + a_tot = [] + for i in range(int(total_time / time_step) + 1): + if cooling_rate != 0: + env["T"] -= np.full((1,), cooling_rate * time_step / 2) + env["a_w_ice"] = svp.pvs_ice(env["T"][0]) / svp.pvs_water(env["T"][0]) + particulator.run(0 if i == 0 else 1) + if cooling_rate != 0: + env["T"] -= np.full((1,), cooling_rate * time_step / 2) + + ice_mass_per_volume = particulator.products["qi"].get()[cell_id] + ice_mass = ice_mass_per_volume * volume + ice_number = ice_mass / (formulae.constants.rho_w * droplet_volume) + unfrozen_fraction = 1 - ice_number / number_of_real_droplets + f_ufz.append(unfrozen_fraction) + a_tot.append(particulator.products["A_tot"].get()[cell_id]) + return f_ufz, a_tot diff --git a/PySDM/source/examples/PySDM_examples/Alpert_and_Knopf_2016/table.py b/PySDM/source/examples/PySDM_examples/Alpert_and_Knopf_2016/table.py new file mode 100644 index 0000000000000000000000000000000000000000..7cb5b4b9c7b0cf2905663d0d7cbe258fcbedb7e5 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Alpert_and_Knopf_2016/table.py @@ -0,0 +1,10 @@ +class Table: + def __getitem__(self, item): + return self._data[item] + + def items(self): + return self._data.items() + + def __init__(self, volume, data): + self._data = data + self.volume = volume diff --git a/PySDM/source/examples/PySDM_examples/Alpert_and_Knopf_2016/table_1.py b/PySDM/source/examples/PySDM_examples/Alpert_and_Knopf_2016/table_1.py new file mode 100644 index 0000000000000000000000000000000000000000..4ae3ed93db03e7e0622cb1b99d4ac7a3b41bf31f --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Alpert_and_Knopf_2016/table_1.py @@ -0,0 +1,101 @@ +from PySDM_examples.Alpert_and_Knopf_2016.table import Table + +from PySDM.initialisation.spectra import Lognormal, TopHat +from PySDM.physics import si + + +class Table1(Table): + def label(self, key): + if isinstance(self[key]["ISA"], Lognormal): + return ( + f"σ=ln({int(self[key]['ISA'].s_geom)})," + f"N={int(self[key]['ISA'].norm_factor * self.volume)}" + ) + return key + + def __init__(self, *, volume=1 * si.cm**3): + super().__init__( + volume=volume, + data={ + "Iso1": { + "ISA": Lognormal( + norm_factor=1000 / volume, m_mode=1e-5 * si.cm**2, s_geom=1 + ), + "color": "#298131", + "J_het": 1e3 / si.cm**2 / si.s, + }, + "Iso2": { + "ISA": Lognormal( + norm_factor=30 / volume, m_mode=1e-5 * si.cm**2, s_geom=1 + ), + "color": "#9ACFA4", + "J_het": 1e3 / si.cm**2 / si.s, + }, + "Iso3": { + "ISA": Lognormal( + norm_factor=1000 / volume, m_mode=1e-5 * si.cm**2, s_geom=10 + ), + "color": "#1A62B4", + "J_het": 1e3 / si.cm**2 / si.s, + }, + "Iso4": { + "ISA": Lognormal( + norm_factor=30 / volume, m_mode=1e-5 * si.cm**2, s_geom=10 + ), + "color": "#95BDE1", + "J_het": 1e3 / si.cm**2 / si.s, + }, + "IsoWR": { + "ISA": Lognormal( + norm_factor=1000 / volume, + m_mode=6.4e-3 * si.cm**2, + s_geom=9.5, + ), + "color": "#FED2B0", + "J_het": 6e-4 / si.cm**2 / si.s, + }, + "IsoBR": { + "ISA": TopHat( + norm_factor=63 / volume, + endpoints=(9.4e-8 * si.cm**2, 7.5e-7 * si.cm**2), + ), + "color": "#FED2B0", + "J_het": 2.8e3 / si.cm**2 / si.s, + }, + "IsoHE1": { + "ISA": Lognormal( + norm_factor=40 / volume, m_mode=1.2 * si.cm**2, s_geom=2.2 + ), + "color": "#FED2B0", + "J_het": 4.1e-3 / si.cm**2 / si.s, + }, + "IsoHE2": { + "ISA": Lognormal( + norm_factor=40 / volume, m_mode=2e-2 * si.cm**2, s_geom=8.5 + ), + "color": "#FED2B0", + "J_het": 2e-2 / si.cm**2 / si.s, + }, + "IsoDI1": { + "ISA": Lognormal( + norm_factor=45 / volume, m_mode=5.1e-1 * si.cm**2, s_geom=3.2 + ), + "J_het": 1.8e-2 / si.cm**2 / si.s, + "color": "#9ACFA4", + }, + "IsoDI2": { + "ISA": Lognormal( + norm_factor=45 / volume, m_mode=5.1e-2 * si.cm**2, s_geom=3.2 + ), + "J_het": 1 / si.cm**2 / si.s, + "color": "#FED2B0", + }, + "IsoDI3": { + "ISA": Lognormal( + norm_factor=45 / volume, m_mode=5.1e-1 * si.cm**2, s_geom=3.2 + ), + "J_het": 1 / si.cm**2 / si.s, + "color": "#95BDE1", + }, + }, + ) diff --git a/PySDM/source/examples/PySDM_examples/Alpert_and_Knopf_2016/table_2.py b/PySDM/source/examples/PySDM_examples/Alpert_and_Knopf_2016/table_2.py new file mode 100644 index 0000000000000000000000000000000000000000..09e6d03b2ccdae93316a0bd180e398ebdff966cc --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Alpert_and_Knopf_2016/table_2.py @@ -0,0 +1,52 @@ +from PySDM_examples.Alpert_and_Knopf_2016.table import Table + +from PySDM.initialisation.spectra import Lognormal +from PySDM.physics import si + + +class Table2(Table): + def label(self, key): + return f"r={self[key]['cooling_rate']/(si.K/si.min)} K/min" + + def __init__(self, *, volume=1 * si.cm**3): + super().__init__( + volume=volume, + data={ + "Cr1": { + "ISA": Lognormal( + norm_factor=1000 / volume, s_geom=10, m_mode=1e-5 * si.cm**2 + ), + "cooling_rate": 0.5 * si.K / si.min, + "color": "orange", + "ABIFM_c": -10.67, + "ABIFM_m": 54.48, + }, + "Cr2": { + "ISA": Lognormal( + norm_factor=1000 / volume, s_geom=10, m_mode=1e-5 * si.cm**2 + ), + "cooling_rate": 5 * si.K / si.min, + "color": "blue", + "ABIFM_c": -10.67, + "ABIFM_m": 54.48, + }, + "CrHE1": { + "ISA": Lognormal( + norm_factor=40 / volume, s_geom=8.5, m_mode=2.1e-2 * si.cm**2 + ), + "cooling_rate": 0.2 * si.K / si.min, + "color": "orange", + "ABIFM_c": -12.98, + "ABIFM_m": 122.83, + }, + "CrHE2": { + "ISA": Lognormal( + norm_factor=40 / volume, s_geom=8.5, m_mode=2.1e-2 * si.cm**2 + ), + "cooling_rate": 2 * si.K / si.min, + "color": "blue", + "ABIFM_c": -12.98, + "ABIFM_m": 122.83, + }, + }, + ) diff --git a/PySDM/source/examples/PySDM_examples/Arabas_and_Shima_2017/__init__.py b/PySDM/source/examples/PySDM_examples/Arabas_and_Shima_2017/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f177e4513dd420035551f70bd03dace0f3bf5cea --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Arabas_and_Shima_2017/__init__.py @@ -0,0 +1,9 @@ +""" +condensation-evaportaion parcel example based on +[Arabas and Shima 2017 (Nonlin. Processes Geophys. 24)](https://doi.org/10.5194/npg-24-535-2017) + +fig_5.ipynb: +.. include:: ./fig_5.ipynb.badges.md +""" + +# pylint: disable=invalid-name diff --git a/PySDM/source/examples/PySDM_examples/Arabas_and_Shima_2017/example.py b/PySDM/source/examples/PySDM_examples/Arabas_and_Shima_2017/example.py new file mode 100644 index 0000000000000000000000000000000000000000..8d2570334792c19bd7fadc12fa59d0dba7498c79 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Arabas_and_Shima_2017/example.py @@ -0,0 +1,11 @@ +from PySDM_examples.Arabas_and_Shima_2017.settings import setups +from PySDM_examples.Arabas_and_Shima_2017.simulation import Simulation + + +def main(): + for settings in setups: + Simulation(settings).run() + + +if __name__ == "__main__": + main() diff --git a/PySDM/source/examples/PySDM_examples/Arabas_and_Shima_2017/fig_5.ipynb b/PySDM/source/examples/PySDM_examples/Arabas_and_Shima_2017/fig_5.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..11e1f550f7be3ca0c4b35e7834db114658976803 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Arabas_and_Shima_2017/fig_5.ipynb @@ -0,0 +1,4521 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Arabas_and_Shima_2017/fig_5.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Arabas_and_Shima_2017/fig_5.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Arabas_and_Shima_2017/fig_5.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### based on Fig. 5 from Arabas and Shima 2017 (Nonlin. Processes Geophys. 24) \"_On the CCN (de)activation nonlinearities_\" \n", + "https://doi.org/10.5194/npg-24-535-2017" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:19:21.141780Z", + "start_time": "2024-02-01T07:19:21.139199Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:21:08.342040Z", + "start_time": "2024-02-01T07:21:08.332830Z" + } + }, + "outputs": [], + "source": [ + "from PySDM_examples.Arabas_and_Shima_2017.example import Simulation, setups\n", + "from open_atmos_jupyter_utils import show_plot\n", + "from PySDM.physics import si, in_unit\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:19:48.529398Z", + "start_time": "2024-02-01T07:19:22.801120Z" + } + }, + "outputs": [], + "source": [ + "output = []\n", + "for settings in setups:\n", + " simulation = Simulation(settings)\n", + " output.append(simulation.run())" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:19:49.936545Z", + "start_time": "2024-02-01T07:19:48.536048Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-05-19T13:24:15.209953\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "771d17a840d348efb10346fea6f5ed50", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_5.pdf
\"), HTML(value=\"= settings.dt_max: # TODO #334 dt_max + self.n_substeps += 1 + + builder = Builder( + backend=backend( + formulae=settings.formulae, + **( + {"override_jit_flags": {"parallel": False}} + if backend is Numba + else {} + ), + ), + n_sd=1, + environment=Parcel( + dt=dt_output / self.n_substeps, + mass_of_dry_air=settings.mass_of_dry_air, + p0=settings.p0, + initial_water_vapour_mixing_ratio=settings.initial_water_vapour_mixing_ratio, + T0=settings.T0, + w=settings.w, + ), + ) + + builder.add_dynamic(AmbientThermodynamics()) + builder.add_dynamic( + Condensation( + rtol_x=settings.rtol_x, + rtol_thd=settings.rtol_thd, + dt_cond_range=settings.dt_cond_range, + ) + ) + attributes = {} + r_dry = np.array([settings.r_dry]) + attributes["dry volume"] = settings.formulae.trivia.volume(radius=r_dry) + attributes["kappa times dry volume"] = attributes["dry volume"] * settings.kappa + attributes["multiplicity"] = np.array([settings.n_in_dv], dtype=np.int64) + environment = builder.particulator.environment + r_wet = equilibrate_wet_radii( + r_dry=r_dry, + environment=environment, + kappa_times_dry_volume=attributes["kappa times dry volume"], + ) + attributes["volume"] = settings.formulae.trivia.volume(radius=r_wet) + products = [ + PySDM_products.MeanRadius(name="radius_m1", unit="um"), + PySDM_products.CondensationTimestepMin(name="dt_cond_min"), + PySDM_products.ParcelDisplacement(name="z"), + PySDM_products.AmbientRelativeHumidity(name="RH"), + PySDM_products.PeakSaturation(name="S_max"), + PySDM_products.Time(name="t"), + PySDM_products.ActivatingRate(unit="s^-1 mg^-1", name="activating_rate"), + PySDM_products.DeactivatingRate( + unit="s^-1 mg^-1", name="deactivating_rate" + ), + PySDM_products.RipeningRate(unit="s^-1 mg^-1", name="ripening_rate"), + ] + + self.particulator = builder.build(attributes, products) + + self.n_output = settings.n_output + + def save(self, output): + cell_id = 0 + output["r"].append( + self.particulator.products["radius_m1"].get(unit=const.si.m)[cell_id] + ) + output["dt_cond_min"].append( + self.particulator.products["dt_cond_min"].get()[cell_id] + ) + output["z"].append(self.particulator.products["z"].get()[cell_id]) + output["RH"].append(self.particulator.products["RH"].get()[cell_id]) + output["t"].append(self.particulator.products["t"].get()) + + for event in ("activating", "deactivating", "ripening"): + output[event + "_rate"].append( + self.particulator.products[event + "_rate"].get()[cell_id] + ) + + def run(self): + output = { + "r": [], + "RH": [], + "z": [], + "t": [], + "dt_cond_min": [], + "activating_rate": [], + "deactivating_rate": [], + "ripening_rate": [], + } + + self.save(output) + for _ in range(self.n_output): + self.particulator.run(self.n_substeps) + self.save(output) + + return output diff --git a/PySDM/source/examples/PySDM_examples/Arabas_et_al_2015/__init__.py b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2015/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..652faa9d836ac3020f149a8e388b8c41846e29d9 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2015/__init__.py @@ -0,0 +1,10 @@ +# pylint: disable=invalid-name +""" +2D prescribed-flow case extended with Paraview visualisation with spin-up logic from +[Arabas et al. 2015](http://doi.org/10.5194/gmd-8-1677-2015) + +gui.ipynb: +.. include:: ./gui.ipynb.badges.md +""" +from .settings import Settings +from .spin_up import SpinUp diff --git a/PySDM/source/examples/PySDM_examples/Arabas_et_al_2015/example.py b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2015/example.py new file mode 100644 index 0000000000000000000000000000000000000000..a1f981bb0a81d30d284a111fb98456883023252d --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2015/example.py @@ -0,0 +1,28 @@ +from open_atmos_jupyter_utils import TemporaryFile +from PySDM_examples.Arabas_et_al_2015 import Settings, SpinUp +from PySDM_examples.utils.kinematic_2d import Simulation, Storage +from PySDM_examples.utils import DummyController + +from PySDM import Formulae +from PySDM.exporters import NetCDFExporter +from PySDM.physics import si + + +def main(): + settings = Settings(Formulae()) + + settings.n_sd_per_gridbox = 25 + settings.grid = (25, 25) + settings.simulation_time = 5400 * si.second + + storage = Storage() + simulation = Simulation(settings, storage, SpinUp) + simulation.reinit() + simulation.run() + temp_file = TemporaryFile(".nc") + exporter = NetCDFExporter(storage, settings, simulation, temp_file.absolute_path) + exporter.run(controller=DummyController()) + + +if __name__ == "__main__": + main() diff --git a/PySDM/source/examples/PySDM_examples/Arabas_et_al_2015/example_benchmark.py b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2015/example_benchmark.py new file mode 100644 index 0000000000000000000000000000000000000000..f7f405a11bf31844b5b2e9681f936d805199b98f --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2015/example_benchmark.py @@ -0,0 +1,70 @@ +import importlib + +from matplotlib import pyplot as plt +from PySDM_examples.Arabas_et_al_2015 import Settings +from PySDM_examples.utils.kinematic_2d import Simulation, Storage + +import PySDM.backends.impl_numba.conf +from PySDM import Formulae +from PySDM.backends import Numba, ThrustRTC +from PySDM.products import WallTime + + +def reload_cpu_backend(): + importlib.reload(PySDM.backends.impl_numba.methods.collisions_methods) + importlib.reload(PySDM.backends.impl_numba.methods.displacement_methods) + importlib.reload(PySDM.backends.impl_numba.methods.moments_methods) + importlib.reload(PySDM.backends.impl_numba.methods.index_methods) + importlib.reload(PySDM.backends.impl_numba.methods.pair_methods) + importlib.reload(PySDM.backends.impl_numba.methods.physics_methods) + importlib.reload(PySDM.backends.impl_numba.methods.chemistry_methods) + importlib.reload(PySDM.backends.impl_numba.storage_impl) + importlib.reload(PySDM.backends.impl_numba.atomic_operations) + importlib.reload(PySDM.backends) + + +def main(): + settings = Settings(Formulae()) + + settings.grid = (25, 25) + settings.simulation_time = settings.dt * 100 + settings.output_interval = settings.dt * 10 + settings.processes = { + "particle advection": True, + "fluid advection": True, + "coalescence": True, + "condensation": False, + "sedimentation": True, + "freezing": False, + "breakup": False, + } + + n_sd = range(14, 16, 1) + + times = {} + backends = [(Numba, "sync"), (Numba, "async")] + if ThrustRTC.ENABLE: + backends.append((ThrustRTC, "async")) + for backend, mode in backends: + if backend is Numba: + PySDM.backends.impl_numba.conf.NUMBA_PARALLEL = mode + reload_cpu_backend() + key = f"{backend} (mode={mode})" + times[key] = [] + for sd in n_sd: + settings.n_sd_per_gridbox = sd + storage = Storage() + simulation = Simulation(settings, storage, None, backend) + simulation.reinit(products=[WallTime()]) + simulation.run() + times[key].append(storage.load("wall time")[-1]) + + for parallelization, t in times.items(): + plt.plot(n_sd, t, label=parallelization) + plt.legend() + plt.loglog() + plt.savefig("benchmark.pdf", format="pdf") + + +if __name__ == "__main__": + main() diff --git a/PySDM/source/examples/PySDM_examples/Arabas_et_al_2015/gui.ipynb b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2015/gui.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..6f7876cf748dac7b8b37d7167d13b069ce200c28 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2015/gui.ipynb @@ -0,0 +1,99 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Arabas_et_al_2015/gui.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Arabas_et_al_2015/gui.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Arabas_et_al_2015/gui.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### based on Figs. 8 & 9 from Arabas et al. 2015 (Geosci. Model Dev. 8) \"_libcloudph++ 1.0: a single-moment bulk, double-moment bulk, and particle-based warm-rain microphysics library in C++_\" \n", + "https://doi.org/10.5194/gmd-8-1677-2015" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-16T17:34:09.974590Z", + "start_time": "2024-12-16T17:34:09.970558Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:20:49.033295Z", + "start_time": "2024-02-01T07:20:48.277874Z" + }, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "b7b03e5eb4bf402d9223b46970bf4632", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Tab(children=(VBox(children=(HBox(children=(FloatProgress(value=0.0, max=1.0), Button(description='start simul…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from PySDM_examples.utils.kinematic_2d import gui, Simulation, GUISettings, Storage\n", + "from PySDM_examples.Arabas_et_al_2015 import Settings, SpinUp\n", + "from PySDM import Formulae\n", + "settings = GUISettings(Settings(Formulae()))\n", + "storage = Storage()\n", + "simulation = Simulation(settings, storage, SpinUp)\n", + "gui.launch(settings, simulation, storage)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.12.8" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/PySDM/source/examples/PySDM_examples/Arabas_et_al_2015/settings.py b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2015/settings.py new file mode 100644 index 0000000000000000000000000000000000000000..04ffbde8b3189c2b84b40effe094fcd4a6a69167 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2015/settings.py @@ -0,0 +1,37 @@ +from typing import Iterable + +from PySDM_examples.Morrison_and_Grabowski_2007.strato_cumulus import StratoCumulus + +from PySDM import Formulae +from PySDM.physics import si + + +class Settings(StratoCumulus): + def __dir__(self) -> Iterable[str]: + return ( + "dt", + "grid", + "size", + "n_spin_up", + "versions", + "steps_per_output_interval", + "formulae", + "initial_dry_potential_temperature_profile", + "initial_vapour_mixing_ratio_profile", + "rhod_w_max", + ) + + def __init__( + self, + formulae=None, + rhod_w_max: float = 0.6 * si.metres / si.seconds * (si.kilogram / si.metre**3), + ): + super().__init__(formulae or Formulae(), rhod_w_max=rhod_w_max) + + self.grid = (25, 25) + self.size = (1500 * si.metres, 1500 * si.metres) + + # output steps + self.simulation_time = 90 * si.minute + self.dt = 5 * si.second + self.spin_up_time = 1 * si.hour diff --git a/PySDM/source/examples/PySDM_examples/Arabas_et_al_2015/spin_up.py b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2015/spin_up.py new file mode 100644 index 0000000000000000000000000000000000000000..d045223f1ae176dcf06f32b085d42648de9465be --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2015/spin_up.py @@ -0,0 +1,20 @@ +from PySDM.dynamics import Collision, Displacement + + +class SpinUp: + def __init__(self, particulator, spin_up_steps): + self.spin_up_steps = spin_up_steps + particulator.observers.append(self) + self.particulator = particulator + self.set(Collision, "enable", False) + self.set(Displacement, "enable_sedimentation", False) + + def notify(self): + if self.particulator.n_steps == self.spin_up_steps: + self.set(Collision, "enable", True) + self.set(Displacement, "enable_sedimentation", True) + + def set(self, dynamic, attr, value): + key = dynamic.__name__ + if key in self.particulator.dynamics: + setattr(self.particulator.dynamics[key], attr, value) diff --git a/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/__init__.py b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d5ecf6eafd23ff40dc345bf8bd8ccf794c499fc5 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/__init__.py @@ -0,0 +1,29 @@ +""" +box-model and 2D prescribed-flow immersion-freezing examples based on +[Arabas et al. 2025](https://doi.org/10.1029/2024MS004770) + +aida.ipynb: +.. include:: ./aida.ipynb.badges.md + +copula_hello.ipynb: +.. include:: ./copula_hello.ipynb.badges.md + +fig_2.ipynb: +.. include:: ./fig_2.ipynb.badges.md + +figs_10_and_11_and_animations.ipynb: +.. include:: ./figs_10_and_11_and_animations.ipynb.badges.md + +fig_A2.ipynb: +.. include:: ./fig_A2.ipynb.badges.md + +figs_3_and_7_and_8.ipynb: +.. include:: ./figs_3_and_7_and_8.ipynb.badges.md + +figs_5_and_6.ipynb: +.. include:: ./figs_5_and_6.ipynb.badges.md +""" + +from .make_particulator import make_particulator +from .plots import make_freezing_spec_plot, make_pdf_plot, make_temperature_plot +from .run_simulation import run_simulation diff --git a/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/aida.ipynb b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/aida.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..5694cc4544c4f7535b4e4012cef260fe83212d87 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/aida.ipynb @@ -0,0 +1,4972 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Arabas_et_al_2025/aida.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Arabas_et_al_2025/aida.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Arabas_et_al_2025/aida.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "inspired by Fig. 2 in [Steinke et al. 2011](https://doi.org/10.5194/acp-11-12945-2011) (\"Ice nucleation properties of fine ash particles from the Eyjafjallajokull eruption in April 2010\")" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:33:34.405400Z", + "start_time": "2025-07-02T15:33:34.401555Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## package imports" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:33:34.443992Z", + "start_time": "2025-07-02T15:33:34.440543Z" + } + }, + "outputs": [], + "source": [ + "import math\n", + "import numpy as np\n", + "\n", + "from matplotlib import pyplot\n", + "from open_atmos_jupyter_utils import show_plot\n", + "\n", + "from PySDM import Builder, Formulae\n", + "from PySDM.backends import CPU\n", + "from PySDM.environments import Parcel\n", + "from PySDM.dynamics import AmbientThermodynamics, Condensation, Freezing\n", + "from PySDM.physics import si\n", + "from PySDM import products as PySDM_products\n", + "from PySDM.initialisation.sampling import spectral_sampling\n", + "from PySDM.initialisation.spectra import Lognormal\n", + "from PySDM.initialisation.hygroscopic_equilibrium import equilibrate_wet_radii\n", + "\n", + "from PySDM_examples.utils import BasicSimulation" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## parameter values" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:33:34.522505Z", + "start_time": "2025-07-02T15:33:34.516038Z" + } + }, + "outputs": [], + "source": [ + "vertical_velocity = 5 * si.m / si.s\n", + "aerosol_cloud_radius_threshold = .25 * si.um\n", + "dNdD = Lognormal(200 / si.cm**3, m_mode=0.4 * si.um, s_geom=1.8)\n", + "common = {\n", + " 'n_sd': int(1e4), # number of super-particles / moving-bins\n", + " 'dt': 1 * si.s, # timestep\n", + " 'p0': 1000 * si.hPa, # initial pressure\n", + " 'T0': 253 * si.K, # initial temperature\n", + " 'initial_water_vapour_mixing_ratio': .58 * si.g/si.kg,\n", + " 't_max': 300 * si.s,\n", + " 'volume': 84.5 * si.m**3,\n", + " 'kappa': .1, # hygroscopicity\n", + " 'formulae': Formulae(\n", + " particle_shape_and_density=\"MixedPhaseSpheres\",\n", + " saturation_vapour_pressure='MurphyKoop2005',\n", + " heterogeneous_ice_nucleation_rate='ABIFM',\n", + " freezing_temperature_spectrum='Niemand_et_al_2012',\n", + " constants = {\n", + " # time-dependent model parameters\n", + " 'ABIFM_M': 28.13797,\n", + " 'ABIFM_C': -2.92414,\n", + " # singular model parameters\n", + " 'NIEMAND_A': -0.517,\n", + " 'NIEMAND_B': 8.934\n", + " }\n", + " )\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## class definitions to adapt PySDM building blocks for AIDA case study" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:33:34.573981Z", + "start_time": "2025-07-02T15:33:34.570494Z" + } + }, + "outputs": [], + "source": [ + "class AIDA(Parcel):\n", + " \"\"\" a kludge to mimick departures from adiabaticy (like wall losses?) \"\"\"\n", + " def get_thd(self):\n", + " self['thd'][:] += .5e-2 * si.K / si.min / si.kg * self.dt * self.mass_of_dry_air\n", + " return self['thd']\n", + "\n", + " def get_water_vapour_mixing_ratio(self):\n", + " self['water_vapour_mixing_ratio'][:] += .1e-3 * si.g / si.min / si.kg * self.dt * self.mass_of_dry_air\n", + " return self['water_vapour_mixing_ratio']" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:33:34.639298Z", + "start_time": "2025-07-02T15:33:34.633742Z" + } + }, + "outputs": [], + "source": [ + "class Settings: # pylint: disable=too-many-instance-attributes,too-few-public-methods\n", + " \"\"\" container class for simulation parameters \"\"\"\n", + " def __init__(self, *, n_sd, dt, t_max, p0, T0, initial_water_vapour_mixing_ratio, attributes, formulae, volume, kappa, singular):\n", + " dry_air_density = formulae.trivia.p_d(p0, initial_water_vapour_mixing_ratio) / formulae.constants.Rd / T0\n", + "\n", + " self.n_sd = n_sd\n", + " self.dt = dt\n", + " self.t_max = t_max\n", + " self.p0 = p0\n", + " self.T0 = T0\n", + " self.initial_water_vapour_mixing_ratio = initial_water_vapour_mixing_ratio\n", + " self.attributes = attributes\n", + " self.formulae = formulae\n", + " self.kappa = kappa\n", + " self.singular = singular\n", + " if self.singular:\n", + " self.immersion_freezing = \"singular\"\n", + " else:\n", + " self.immersion_freezing = \"time-dependent\"\n", + " self.mass_of_dry_air = dry_air_density * volume" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:33:34.706665Z", + "start_time": "2025-07-02T15:33:34.696097Z" + } + }, + "outputs": [], + "source": [ + "class Simulation(BasicSimulation): # pylint: disable=too-few-public-methods\n", + " \"\"\" orchestrates AIDA-like simulation setup and run \"\"\"\n", + " def __init__(self, settings: Settings):\n", + " self.settings = settings\n", + " builder = Builder(\n", + " n_sd=settings.n_sd,\n", + " backend=CPU(settings.formulae, override_jit_flags={\"parallel\": False}),\n", + " environment=AIDA(\n", + " dt = settings.dt,\n", + " mass_of_dry_air = settings.mass_of_dry_air,\n", + " p0 = settings.p0,\n", + " T0 = settings.T0,\n", + " initial_water_vapour_mixing_ratio = settings.initial_water_vapour_mixing_ratio,\n", + " w = vertical_velocity,\n", + " mixed_phase = True\n", + " )\n", + " )\n", + " builder.add_dynamic(AmbientThermodynamics())\n", + " builder.add_dynamic(Condensation())\n", + " builder.add_dynamic(Freezing(immersion_freezing=settings.immersion_freezing))\n", + "\n", + " dry_radius = settings.formulae.trivia.radius(volume=settings.attributes['dry volume'])\n", + " self._set_attribute(\n", + " settings.attributes,\n", + " 'kappa times dry volume',\n", + " settings.kappa * settings.attributes['dry volume']\n", + " )\n", + " self._set_attribute(\n", + " settings.attributes,\n", + " 'signed water mass',\n", + " settings.formulae.particle_shape_and_density.radius_to_mass(\n", + " radius=equilibrate_wet_radii(\n", + " r_dry=dry_radius,\n", + " environment=builder.particulator.environment,\n", + " kappa_times_dry_volume=settings.attributes['kappa times dry volume'] \n", + " )\n", + " )\n", + " )\n", + " if settings.singular:\n", + " self._set_attribute(\n", + " settings.attributes,\n", + " 'freezing temperature',\n", + " settings.formulae.freezing_temperature_spectrum.invcdf(\n", + " np.random.random(settings.n_sd),\n", + " settings.formulae.trivia.sphere_surface(diameter=dry_radius * 2)\n", + " )\n", + " )\n", + " else:\n", + " self._set_attribute(\n", + " settings.attributes,\n", + " 'immersed surface area',\n", + " settings.formulae.trivia.sphere_surface(diameter=dry_radius * 2)\n", + " )\n", + " \n", + " super().__init__(builder.build(\n", + " settings.attributes,\n", + " products=(\n", + " PySDM_products.AmbientPressure(var='p', unit='hPa'),\n", + " PySDM_products.AmbientTemperature(var='T'),\n", + " PySDM_products.AmbientRelativeHumidity(var='RH', unit='%'),\n", + " PySDM_products.AmbientRelativeHumidity(var='RH', name='RHi', ice=True, unit='%'),\n", + " PySDM_products.ParticleConcentration(\n", + " name='n_a', unit='1/cm**3',\n", + " radius_range=(0, np.inf)),\n", + " PySDM_products.ParticleConcentration(\n", + " name='n_c', unit='1/cm**3',\n", + " radius_range=(aerosol_cloud_radius_threshold, np.inf)),\n", + " PySDM_products.ParticleConcentration(\n", + " name='n_i', unit='1/cm**3',\n", + " radius_range=(-np.inf, 0)),\n", + " PySDM_products.EffectiveRadius(\n", + " name='r_eff', unit='um',\n", + " radius_range=(aerosol_cloud_radius_threshold, np.inf)),\n", + " PySDM_products.Time()\n", + " )\n", + " ))\n", + " \n", + " @staticmethod\n", + " def _set_attribute(attributes, attr, value):\n", + " assert attr not in attributes\n", + " attributes[attr] = value\n", + " \n", + " def run(self):\n", + " return super()._run(\n", + " nt = math.ceil(self.settings.t_max / self.settings.dt),\n", + " steps_per_output_interval=1\n", + " )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## execution of simulations" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:34:09.389498Z", + "start_time": "2025-07-02T15:33:34.758646Z" + } + }, + "outputs": [], + "source": [ + "d_dry, conc = spectral_sampling.ConstantMultiplicity(dNdD).sample_deterministic(common['n_sd'])\n", + "\n", + "ATTRIBUTES = {\n", + " 'multiplicity': conc * common['volume'],\n", + " 'dry volume': common['formulae'].trivia.volume(radius=d_dry / 2)\n", + "}\n", + "\n", + "outputs = []\n", + "SETTINGS = [\n", + " Settings(**common, attributes=dict(ATTRIBUTES), singular=False),\n", + " Settings(**common, attributes=dict(ATTRIBUTES), singular=True),\n", + "]\n", + "for setting in SETTINGS:\n", + " simulation = Simulation(setting)\n", + " outputs.append(simulation.run())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## plotting" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:34:12.163748Z", + "start_time": "2025-07-02T15:34:09.411658Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-02T17:34:11.870566\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "60edf1e46f7546fb82bd72d5674d9042", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_2.pdf
\"), HTML(value=\"" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib\n", + "font = {'family' : 'monospace',\n", + " 'weight' : 'light',\n", + " 'size' : 13\n", + " }\n", + "matplotlib.rc('font', **font) \n", + "matplotlib.pyplot.tight_layout()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "a8cd1d9a", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T14:21:07.637558807Z", + "start_time": "2024-12-10T19:06:51.442853Z" + } + }, + "outputs": [], + "source": [ + "freezing_fit_a = -0.5\n", + "freezing_fit_b = 10\n", + "lognormal_median = 5*si.um**2\n", + "lognormal_g_mean = 1.75" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "d49edfcf", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T14:21:07.647317131Z", + "start_time": "2024-12-10T19:06:51.451359Z" + } + }, + "outputs": [], + "source": [ + "formulae = Formulae(\n", + " freezing_temperature_spectrum='Niemand_et_al_2012',\n", + " constants={\n", + " 'NIEMAND_A': freezing_fit_a,\n", + " 'NIEMAND_B': freezing_fit_b \n", + " }\n", + ")\n", + "spectrum = Lognormal(norm_factor=1, m_mode=lognormal_median, s_geom=lognormal_g_mean)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "9f12ad46", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T14:21:07.648470635Z", + "start_time": "2024-12-10T19:06:51.478725Z" + } + }, + "outputs": [], + "source": [ + "T_range = (230 * si.K, 255 * si.K)\n", + "A_range = (.05 * si.um**2, 40 * si.um**2)\n", + "\n", + "label_T=f'normalised freezing T within {T_range} K' \n", + "label_A=f'normalised insoluble A within {tuple(np.asarray(A_range) / si.um**2)} $μm^2$'" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "019715d6", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T14:21:07.655940294Z", + "start_time": "2024-12-10T19:06:51.491083Z" + } + }, + "outputs": [], + "source": [ + "N = 256\n", + "T = np.linspace(*T_range, N)\n", + "A = np.linspace(*A_range, N)\n", + "\n", + "grid = np.meshgrid(T, A)\n", + "\n", + "def pdf(T_arg, A_arg):\n", + " return formulae.freezing_temperature_spectrum.pdf(T_arg, A_arg) * spectrum.pdf(A_arg)\n", + "\n", + "def cdfA(A_arg):\n", + " return spectrum.cdf(A_arg)\n", + "\n", + "def cdfT(T_arg):\n", + " return formulae.freezing_temperature_spectrum.cdf(T_arg, spectrum.median)\n", + "\n", + "def invcdfA(x):\n", + " return spectrum.percentiles(x)\n", + "\n", + "def invcdfT(x):\n", + " return formulae.freezing_temperature_spectrum.invcdf(x, spectrum.median)\n", + "\n", + "def _T_to_01(T_arg):\n", + " return (T_arg - T_range[0]) / np.diff(T_range)\n", + "\n", + "def _01_to_T(x):\n", + " return x * np.diff(T_range) + T_range[0]\n", + "\n", + "def _A_to_01(A_arg):\n", + " return (A_arg - A_range[0]) / np.diff(A_range)\n", + " \n", + "def _01_to_A(x):\n", + " return x * np.diff(A_range) + A_range[0]\n", + "\n", + "\n", + "dT = T[1] - T[0]\n", + "dA = A[1] - A[0]\n", + "sampled_pdf = pdf(*grid)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "944ab8a0", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T14:21:07.657679093Z", + "start_time": "2024-12-10T19:06:52.055741Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-07T09:17:27.577569\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "6dd964a6bbea436e9760d125f83340d3", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./tmp45pw849u.pdf
\"), HTML(value…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig = pyplot.figure(figsize=(7, 6))\n", + "ax = fig.add_subplot(111)\n", + "ax.set_xlabel('freezing temperature [K]')\n", + "ax.set_ylabel('insoluble surface [$μm^2$]')\n", + "cnt = ax.contour(grid[0], grid[1] / si.um**2, sampled_pdf * si.um**2, levels=20)\n", + "cbar = pyplot.colorbar(cnt)\n", + "cbar.set_label('pdf [$K^{-1} μm^{-2}$]')\n", + "pyplot.grid()\n", + "show_plot()\n", + "np.testing.assert_almost_equal(np.sum(sampled_pdf) * dT * dA, 1, decimal=1)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "8a08db5f", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T14:21:07.658389563Z", + "start_time": "2024-12-10T19:06:52.573754Z" + } + }, + "outputs": [], + "source": [ + "n_samples = 2222\n", + "seed = 222\n", + "mult = 22\n", + "\n", + "rng = np.random.default_rng(seed)\n", + "T_rand = rng.uniform(0, 1, n_samples)\n", + "A_rand = rng.uniform(0, 1, n_samples)\n", + "data = pdf(\n", + " _01_to_T(T_rand),\n", + " _01_to_A(A_rand)\n", + ")\n", + "\n", + "data -= min(data)\n", + "data /= max(data)\n", + "data *= mult\n", + "\n", + "points_T = []\n", + "points_A = []\n", + "for i, v in enumerate(np.round(data).astype(dtype=int)):\n", + " for _ in range(v):\n", + " points_T.append(T_rand[i])\n", + " points_A.append(A_rand[i])\n", + "\n", + "data = np.asarray([points_T, points_A]).T" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "e4ccc918", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T14:21:07.659392554Z", + "start_time": "2024-12-10T19:06:52.929936Z" + } + }, + "outputs": [], + "source": [ + "def jointplot(data_arg, title=''):\n", + " h = seaborn.jointplot(x=data_arg[:, 0], y=data_arg[:, 1], kind='hex', label=title)\n", + " h.set_axis_labels(label_T, label_A)\n", + " if title != '':\n", + " pyplot.legend(loc='upper right')" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "fd8fd4c1", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T14:21:07.659824001Z", + "start_time": "2024-12-10T19:06:52.945081Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-07T09:17:34.842983\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "e2c7c0a9f0d64365a1bd9a4160688e81", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./01.pdf
\"), HTML(value=\"\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-07T09:17:37.920936\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "c3106bfd2e654ef2836829d5772dbe40", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./tmpg0wld9tg.pdf
\"), HTML(value…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-07T09:17:38.339199\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "24282ae6f0a4430bb51d3ea8c4bf562f", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./tmpt9auh7sa.pdf
\"), HTML(value…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "def check_cdf(arg, fun, xlabel):\n", + " histo, bins = np.histogram(arg, bins=64, density=True)\n", + " dx = bins[1] - bins[0]\n", + " cdf = np.concatenate([[0], np.cumsum(histo*dx)])\n", + " pyplot.step(bins, cdf, where='mid')\n", + " pyplot.plot(bins, fun(bins)*n/(n+1))\n", + " pyplot.ylabel('cdf')\n", + " pyplot.xlabel(xlabel)\n", + " pyplot.grid()\n", + " show_plot()\n", + "\n", + "check_cdf(data[:,0], lambda x: 1-cdfT(_01_to_T(x)), xlabel=label_T)\n", + "check_cdf(data[:,1], lambda x: cdfA(_01_to_A(x)), xlabel=label_A)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "1f1cade9", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T14:21:07.667962269Z", + "start_time": "2024-12-10T19:06:54.177224Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-07T09:17:39.443802\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f246c555630e4c2a8d18625853864e76", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./02.pdf
\"), HTML(value=\"\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-07T09:17:40.011366\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# transform back to original scale\n", + "testdata=np.empty((data.shape[0],2))\n", + "testdata[:,0]=np.quantile(data[:,0],udata[:,0])\n", + "testdata[:,1]=np.quantile(data[:,1],udata[:,1])\n", + "jointplot(testdata)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "0c9a6bcd", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T14:21:07.669937803Z", + "start_time": "2024-12-10T19:06:54.975017Z" + } + }, + "outputs": [], + "source": [ + "families = (pv.BicopFamily.indep, pv.BicopFamily.tll)\n", + "copulae = {}\n", + "for family in families:\n", + " copulae[family] = pv.Bicop.from_data(udata, controls = pv.FitControlsBicop(family_set=[family]))" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "3bd379b3", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T14:21:07.671036471Z", + "start_time": "2024-12-10T19:06:57.134997Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-07T09:23:47.115683\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "968e7068451e4d41a0414fff64b9265c", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./03_0.pdf
\"), HTML(value=\"./03_1.pdf
\"), HTML(value=\" l_fig[-1]: + t.set_alpha(0.0) + rel_pos += w + continue + + if c != " ": + t.set_alpha(1.0) + + # finding the two data points between which the horizontal + # center point of the character will be situated + # left and right indices: + il = np.where(rel_pos + w / 2 >= l_fig)[0][-1] + ir = np.where(rel_pos + w / 2 <= l_fig)[0][0] + + # if we exactly hit a data point: + if ir == il: + ir += 1 + + # how much of the letter width was needed to find il: + used = l_fig[il] - rel_pos + rel_pos = l_fig[il] + + # relative distance between il and ir where the center + # of the character will be + fraction = (w / 2 - used) / r_fig_dist[il] + + ##setting the character position in data coordinates: + ##interpolate between the two points: + x = self.__x[il] + fraction * (self.__x[ir] - self.__x[il]) + y = self.__y[il] + fraction * (self.__y[ir] - self.__y[il]) + + # getting the offset when setting correct vertical alignment + # in data coordinates + bbox2 = t.get_window_extent(renderer=renderer) + + bbox1d = self.axes.transData.inverted().transform(bbox1) + bbox2d = self.axes.transData.inverted().transform(bbox2) + dr = np.array(bbox2d[0] - bbox1d[0]) + + # the rotation/stretch matrix + rad = rads[il] + rot_mat = np.array( + [ + [math.cos(rad), math.sin(rad) * aspect], + [-math.sin(rad) / aspect, math.cos(rad)], + ] + ) + + ##computing the offset vector of the rotated character + drp = np.dot(dr, rot_mat) + + # setting final position and rotation: + t.set_position(np.array([x, y]) + drp) + t.set_rotation(degs[il]) + + t.set_va("center") + t.set_ha("center") + + # updating rel_pos to right edge of character + rel_pos += w - used diff --git a/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/fig_2.ipynb b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/fig_2.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..6c1ae6b37eb91691d931a34a644700495e89db2e --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/fig_2.ipynb @@ -0,0 +1,2221 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "556981ca9ffb9215", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Arabas_et_al_2025/fig_2.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Arabas_et_al_2025/fig_2.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Arabas_et_al_2025/fig_2.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "f85560654c25e11f", + "metadata": {}, + "source": [ + "Fig. 2 from [Arabas et al. 2025 (JAMES)](https://doi.org/10.1029/2024MS004770)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "71145f7dd95293ea", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:34:35.167478Z", + "start_time": "2025-07-02T15:34:35.163822Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "a6ca777a", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:34:35.391397Z", + "start_time": "2025-07-02T15:34:35.219074Z" + } + }, + "outputs": [], + "source": [ + "from matplotlib import pyplot\n", + "from open_atmos_jupyter_utils import show_plot\n", + "from PySDM import Formulae\n", + "from PySDM_examples.Arabas_et_al_2025.curved_text import CurvedText\n", + "from PySDM_examples.Arabas_et_al_2025.commons import FREEZING_CONSTANTS, COOLING_RATES, TEMP_RANGE\n", + "import numpy as np\n", + "import pint\n", + "si = pint.UnitRegistry()\n", + "si.setup_matplotlib()" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "abc1b652", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:34:35.407530Z", + "start_time": "2025-07-02T15:34:35.404797Z" + } + }, + "outputs": [], + "source": [ + "params = {\n", + "# 'illite': {\n", + "# 'm': FREEZING_CONSTANTS['illite']['ABIFM_M'],\n", + "# 'c': FREEZING_CONSTANTS['illite']['ABIFM_C'],\n", + "# 'color': 'olive'\n", + "# },\n", + " 'dust': {\n", + " 'm': FREEZING_CONSTANTS['dust']['ABIFM_M'],\n", + " 'c': FREEZING_CONSTANTS['dust']['ABIFM_C'],\n", + " 'color': 'teal'\n", + " },\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "671fde73", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:34:35.466744Z", + "start_time": "2025-07-02T15:34:35.459624Z" + } + }, + "outputs": [], + "source": [ + "svp = Formulae(saturation_vapour_pressure='FlatauWalkoCotton').saturation_vapour_pressure\n", + "T = np.linspace(*TEMP_RANGE) * si.K\n", + "\n", + "def _T(TK):\n", + " return (TK/si.K).to_base_units().magnitude\n", + "\n", + "a_w_ice = svp.pvs_ice(_T(T)) / svp.pvs_water(_T(T))" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "b499c875", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:34:36.813970Z", + "start_time": "2025-07-02T15:34:35.518066Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-02T17:34:36.683477\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "b972339d125b4bbf81b7031bf7af6282", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_theory.pdf
\"), HTML(value=\"…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "cooling_rates = tuple((x*si.K/si.s).to_base_units() for x in COOLING_RATES)\n", + "\n", + "for label, mc in params.items():\n", + " formulae_abifm = Formulae(\n", + " constants={\n", + " 'ABIFM_M': mc['m'],\n", + " 'ABIFM_C': mc['c']\n", + " },\n", + " heterogeneous_ice_nucleation_rate='ABIFM'\n", + " )\n", + " abifm_j_het = lambda T, formulae=formulae_abifm: (\n", + " formulae.heterogeneous_ice_nucleation_rate.j_het(_T(T)) / si.m**2 / si.s\n", + " )\n", + " for i, c in enumerate(cooling_rates):\n", + " c_K_min = (c / si.K * si.min).to_base_units().magnitude\n", + " minus_J_over_c = -abifm_j_het(a_w_ice) / c\n", + " pyplot.plot(T, minus_J_over_c,\n", + " color=mc['color'],\n", + " label='' if i!=0 else f'ABIFM $-J_{{het}}/c$ ({label})',\n", + " linewidth=3*abs(c.magnitude)**.15\n", + " )\n", + " _ = CurvedText(T.magnitude+.666, 1.111*minus_J_over_c.magnitude,\n", + " text=f'c={c_K_min} K/min'.replace('-', '−'), axes=pyplot.gca(),\n", + " va='bottom'\n", + " )\n", + " \n", + "formulae = Formulae(\n", + " constants=FREEZING_CONSTANTS[\"dust\"],\n", + " freezing_temperature_spectrum='Niemand_et_al_2012'\n", + ")\n", + "inas_ns = lambda T: formulae.freezing_temperature_spectrum.ns(_T(T)) / si.m**2\n", + "\n", + "a = formulae.constants.NIEMAND_A / si.K\n", + "pyplot.plot(T, -a*inas_ns(T), label=r'INAS: $-dn_{s}(T)/dT=-a \\cdot n_s(T)$ (dust)',\n", + " color='black', linestyle=':', linewidth=4)\n", + "\n", + "pyplot.gca().invert_xaxis()\n", + "pyplot.yscale('log')\n", + "pyplot.grid()\n", + "pyplot.xlabel('temperature [K]')\n", + "pyplot.ylabel('$-J_{het}(T) / c = -dn_s(T)/dT$ [$K^{-1} m^{-2}$]')\n", + "pyplot.legend()\n", + "show_plot('fig_theory.pdf')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eab6441c-43aa-4772-8288-474c8a7bf509", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:34:36.873364Z", + "start_time": "2025-07-02T15:34:36.869280Z" + } + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fa880da5-47fe-48d1-a74f-fd7ec4d7bbbe", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:34:36.927256Z", + "start_time": "2025-07-02T15:34:36.925662Z" + } + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/fig_A2.ipynb b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/fig_A2.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..f406a817b09f3d8554e35b0efe3d45bcf7d4e32b --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/fig_A2.ipynb @@ -0,0 +1,8542 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "1371df7e-3c6e-4bdd-91b7-49c20ebe33c5", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Arabas_et_al_2025/fig_A2.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Arabas_et_al_2025/fig_A2.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Arabas_et_al_2025/fig_A2.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "c6f48049ea08fe2e", + "metadata": {}, + "source": [ + "Fig. A2 in [Arabas et al. 2025 (JAMES)](https://doi.org/10.1029/2024MS004770)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "dba473e974480091", + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-19T22:14:42.459800Z", + "start_time": "2024-12-19T22:14:42.457196Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "7ea1f100", + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-19T22:16:49.150846Z", + "start_time": "2024-12-19T22:16:49.107466Z" + } + }, + "outputs": [], + "source": [ + "import itertools\n", + "import numpy as np\n", + "from matplotlib import pyplot\n", + "import pickle\n", + "\n", + "from PySDM import Builder, Formulae\n", + "from PySDM.dynamics import Freezing\n", + "from PySDM.environments import Box\n", + "from PySDM.physics import si\n", + "from PySDM.products import IceWaterContent\n", + "from PySDM.backends import GPU\n", + "\n", + "from open_atmos_jupyter_utils import show_plot" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "a56c35fe", + "metadata": {}, + "outputs": [], + "source": [ + "CI = 'CI' in os.environ\n", + "\n", + "RATE_MARGIN = .15\n", + "TIME_MARGIN = .25\n", + "\n", + "N_SEEDS = 32 if not CI else 1\n", + "\n", + "DTS = (5, 50, 500, 5000, 50000) if not CI else (50000,)\n", + "\n", + "MLT = (1, 4, 16, 64, 256, 1024) if not CI else (4096,)\n", + "SEEDS = tuple(range(N_SEEDS)) if not CI else (44,)\n", + "\n", + "RATE = 1e-7 / si.s\n", + "\n", + "immersed_surface_area = 1\n", + "\n", + "number_of_real_droplets = 256 * max(MLT)\n", + "total_time = (\n", + " 1e5 # effectively interpreted here as seconds, i.e. cycle = 1 * si.s\n", + ")\n", + "\n", + "def error_norm(actual, expected):\n", + " return np.sqrt(np.mean(np.square(actual - expected)))" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "6c3ba75d", + "metadata": {}, + "outputs": [], + "source": [ + "cases = tuple({\"dt\": dt, \"N\": mlt, \"seed\": seed} for dt, mlt, seed in itertools.product(DTS, MLT, SEEDS))\n", + "\n", + "# dummy (but must-be-set) values\n", + "mass = 44 # for sign flip (ice water has negative mass), value does not matter\n", + "d_v = 666 # products use conc., dividing there, multiplying here, value does not matter\n", + "\n", + "output = {}\n", + "\n", + "products = (IceWaterContent(name=\"qi\"),)\n", + "backend_class = GPU\n", + "\n", + "for double_precision in (True, False):\n", + " backend_key = ('double' if double_precision else 'single') + ' precision'\n", + " output[backend_key] = {}\n", + " for case in cases:\n", + " formulae = Formulae(\n", + " particle_shape_and_density=\"MixedPhaseSpheres\",\n", + " heterogeneous_ice_nucleation_rate=\"Constant\",\n", + " constants={\"J_HET\": RATE / immersed_surface_area},\n", + " seed=case['seed'],\n", + " )\n", + " \n", + " n_sd = int(number_of_real_droplets // case[\"N\"])\n", + " assert n_sd == number_of_real_droplets / case[\"N\"]\n", + " assert total_time // case[\"dt\"] == total_time / case[\"dt\"]\n", + "\n", + " key = f\"{case['dt']}:{n_sd}:{case['seed']}\"\n", + " output[backend_key][key] = {\"unfrozen_fraction\": [], \"dt\": case[\"dt\"], \"N\": case[\"N\"]}\n", + "\n", + " builder = Builder(\n", + " n_sd=n_sd,\n", + " backend=backend_class(formulae=formulae, double_precision=double_precision),\n", + " environment=Box(dt=case[\"dt\"], dv=d_v),\n", + " )\n", + " builder.add_dynamic(Freezing(immersion_freezing='time-dependent'))\n", + " attributes = {\n", + " \"multiplicity\": np.full(n_sd, int(case[\"N\"])),\n", + " \"immersed surface area\": np.full(n_sd, immersed_surface_area),\n", + " \"signed water mass\": np.full(n_sd, mass),\n", + " }\n", + " particulator = builder.build(attributes=attributes, products=products)\n", + " particulator.environment[\"RH\"] = 1.0001\n", + " particulator.environment[\"a_w_ice\"] = np.nan\n", + " particulator.environment[\"T\"] = np.nan\n", + "\n", + " cell_id = 0\n", + " for i in range(int(total_time / case[\"dt\"]) + 1):\n", + " particulator.run(0 if i == 0 else 1)\n", + "\n", + " ice_mass_per_volume = particulator.products[\"qi\"].get()[cell_id]\n", + " ice_mass = ice_mass_per_volume * d_v\n", + " ice_number = ice_mass / mass\n", + " unfrozen_fraction = 1 - ice_number / number_of_real_droplets\n", + " output[backend_key][key][\"unfrozen_fraction\"].append(unfrozen_fraction)\n", + " \n", + "with open('output.pkl','wb') as file:\n", + " pickle.dump(output, file)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "ba4cfcf2", + "metadata": {}, + "outputs": [], + "source": [ + "with open('output.pkl','rb') as file:\n", + " output = pickle.load(file)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "131ffda3", + "metadata": {}, + "outputs": [], + "source": [ + "for backend_key in output.keys():\n", + " for key, out in output[backend_key].items():\n", + " dt_to_shortest_dt = out['dt'] / min(DTS)\n", + " assert int(dt_to_shortest_dt) == dt_to_shortest_dt\n", + " \n", + " sim_y = np.asarray(out[\"unfrozen_fraction\"])\n", + " sim_x = out[\"dt\"] * np.arange(sim_y.size)\n", + " output[backend_key][key]['mse'] = error_norm(\n", + " actual=sim_y[1:],\n", + " expected=np.exp(-RATE * sim_x[1:])\n", + " ) \n", + " \n", + " sim_y = np.repeat(np.asarray(out[\"unfrozen_fraction\"][1:]), dt_to_shortest_dt)\n", + " sim_x = out[\"dt\"] / dt_to_shortest_dt * np.arange(1, sim_y.size + 1)\n", + " output[backend_key][key]['mse_interp'] = error_norm(\n", + " actual=sim_y,\n", + " expected=np.exp(-RATE * sim_x)\n", + " ) \n", + " #assert abs(1 - output[backend_key][key]['mse_interp'] / output[backend_key][key]['mse']) < 1.5" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "48b99e24", + "metadata": {}, + "outputs": [], + "source": [ + "def hgh(t):\n", + " return np.exp(-(1 - RATE_MARGIN) * RATE * (t - total_time * TIME_MARGIN))\n", + "\n", + "def low(t):\n", + " return np.exp(-(1 + RATE_MARGIN) * RATE * (t + total_time * TIME_MARGIN))" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "be87d366", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-12-19T23:43:25.020945\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.0, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "0ab89cf0490549e3a4fc770076e5ab40", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_convergence_…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-12-19T23:43:25.378142\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.0, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "c113c1e4f49245c6aa3d9a9739c09204", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_convergence_…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "for backend_key in output.keys():\n", + " fig, ax = pyplot.subplots(1, 1, figsize=(10, 4.5))\n", + " for key, out in output[backend_key].items():\n", + " dt, n_sd, seed = key.split(':')\n", + " fit_x = np.linspace(0, total_time, num=100)\n", + " fit_y = np.exp(-RATE * fit_x)\n", + "\n", + " sim_x = out[\"dt\"] * np.arange(len(out[\"unfrozen_fraction\"]))\n", + " sim_y = np.asarray(out[\"unfrozen_fraction\"])\n", + " arbitrarily_pick = -12\n", + " if int(dt) in DTS[0:2] and number_of_real_droplets//int(n_sd) in MLT[3:5] and int(seed) in SEEDS[arbitrarily_pick:arbitrarily_pick+2]:\n", + " ax.step(\n", + " sim_x / 60 / 60,\n", + " (1-sim_y) * 100,\n", + " label=f\"dt={out['dt']:g}s $n_\\\\text{{sd}}=2^{{{int(np.log2(int(n_sd)))}}}$={int(2**np.log2(int(n_sd)))}\" if int(seed) != SEEDS[arbitrarily_pick+1] else \"\",\n", + " color={DTS[0]: 'olive', DTS[1]: 'navy'}[int(dt)],\n", + " linewidth=2 + out[\"N\"] // 128,\n", + " )\n", + " ax.plot(\n", + " fit_x / 60 / 60, (1-fit_y) * 100, color=\"red\", linestyle=\"--\", label=\"theory: 1-exp(-rt)\", linewidth=3.5\n", + " )\n", + " ax.plot(\n", + " fit_x / 60 / 60, (1-hgh(fit_x)) * 100, color=\"red\", linestyle=\":\", label=f\"{100*RATE_MARGIN:g}% lower rate, {total_time*TIME_MARGIN/60/60:.0g}h later start\"\n", + " )\n", + " ax.plot(\n", + " fit_x / 60 / 60, (1-low(fit_x)) * 100, color=\"red\", linestyle=\"-.\", label=f\"{100*RATE_MARGIN:g}% higher rate, {total_time*TIME_MARGIN/60/60:.0g}h earlier start\"\n", + " )\n", + " ax.legend(loc='lower right')\n", + " ax.set_ylim(100 * (1-fit_y[0]), 100 * (1-fit_y[-1]))\n", + " ax.set_xlim(0, total_time / 60 / 60)\n", + " ax.set_xlabel(\"time [h]\")\n", + " ax.set_ylabel(\"frozen fraction [%]\")\n", + " ax.grid()\n", + " show_plot(f\"fig_convergence_{backend_key}.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "a19ec2af", + "metadata": {}, + "outputs": [], + "source": [ + "NORM_TYPE = 'mse'" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "53881f93", + "metadata": {}, + "outputs": [], + "source": [ + "mean = {}\n", + "serr = {}\n", + "for backend_key in output.keys():\n", + " mean[backend_key] = {}\n", + " serr[backend_key] = {} \n", + " for n_real in range(1, len(SEEDS)+1):\n", + " mean[backend_key][n_real] = np.zeros(shape=(len(DTS), len(MLT)))\n", + " serr[backend_key][n_real] = np.zeros(shape=(len(DTS), len(MLT)))\n", + " tmp = serr[backend_key][n_real]\n", + " for key in output[backend_key].keys():\n", + " dt, n_sd, seed = key.split(':')\n", + " if int(seed) in SEEDS[0:n_real]:\n", + " i = DTS.index(float(dt))\n", + " j = MLT.index(number_of_real_droplets // int(n_sd))\n", + " mse = output[backend_key][key][NORM_TYPE]\n", + " mean[backend_key][n_real][i, j] += mse\n", + " tmp[i, j] += mse**2\n", + " mean[backend_key][n_real] /= n_real\n", + " tmp /= n_real\n", + " tmp -= mean[backend_key][n_real]**2\n", + " mask = tmp > 0\n", + " serr[backend_key][n_real][mask] = np.sqrt(tmp[mask] / n_real)\n", + " serr[backend_key][n_real][~mask] = 0" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "005673b0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "5\n", + "50\n", + "500\n", + "5000\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-12-19T23:43:25.959442\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.0, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "d6ba6ab6aa9847958829e5251c8495ba", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_lines_double preci…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "5\n", + "50\n", + "500\n", + "5000\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-12-19T23:43:26.300565\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.0, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "825be5d3aac64506b1f54b649d398184", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_lines_single preci…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "FIGSIZE = (4.75, 4.75)\n", + "\n", + "for backend_key in output.keys():\n", + " fig, ax = pyplot.subplots(1, 1, figsize=FIGSIZE)\n", + " pick_n_real = len(SEEDS)\n", + " pick_dt = -2\n", + " \n", + " x = number_of_real_droplets // np.asarray(MLT)\n", + " for i, c in enumerate((.0333, .0999)):\n", + " ax.plot(x, c * x**-.5, color='gray', label=r'$\\sim 1/\\sqrt{n_{sd}}$' if i==0 else '')\n", + "\n", + " for i, dt in enumerate(DTS):\n", + " if dt > 5000:\n", + " continue\n", + " print(dt)\n", + " x = number_of_real_droplets // np.asarray(MLT)\n", + " y = mean[backend_key][pick_n_real][i, :]\n", + " common_kwargs = {'marker': '.', 'linewidth': 6.66/np.log(float(dt))}\n", + " label = f\"dt={dt:d} s\"\n", + " if dt == DTS[pick_dt]:\n", + " for n_real in reversed(range(1, len(SEEDS)+1)):\n", + " if n_real < 4 or np.log2(n_real) != int(np.log2(n_real)):\n", + " continue\n", + " ax.errorbar(x=x, y=y,\n", + " yerr=serr[backend_key][n_real][i, :],\n", + " capsize=2*np.log2(n_real),\n", + " **common_kwargs,\n", + " color='black',\n", + " label=label + f\" ({n_real} runs)\"\n", + " )\n", + " else:\n", + " ax.plot(x, y, **common_kwargs, label=label + f\" ({pick_n_real} runs)\")\n", + " \n", + " pyplot.xscale('log', base=2)\n", + " pyplot.yscale('log', base=2)\n", + " if backend_key == 'double precision':\n", + " pyplot.legend(loc=\"lower left\")\n", + " pyplot.xlabel(\"$n_{sd}$\")\n", + " pyplot.ylabel(r'mean $E_{L2}$ over all runs (bars: std. err.)')\n", + " pyplot.grid()\n", + " pyplot.title(backend_key + (' (interpolated MSE)' if NORM_TYPE == 'mse_interp' else ''))\n", + " pyplot.ylim(2**-15, 2**-7.5)\n", + " show_plot(f'fig_lines_{backend_key}.pdf')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3f369c2d-cacb-42e1-b480-be325720ef83", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/figs_10_and_11_and_animations.ipynb b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/figs_10_and_11_and_animations.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..a9bec3baae3fd3920b5de46b33c7849b4f7fe9a1 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/figs_10_and_11_and_animations.ipynb @@ -0,0 +1,6858 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "e75404edab7940f9", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Arabas_et_al_2025/figs_10_and_11_and_animations.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Arabas_et_al_2025/figs_10_and_11_and_animations.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Arabas_et_al_2025/figs_10_and_11_and_animations.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "7ae7d5cdf161486c", + "metadata": {}, + "source": [ + "Figs 10 and 11 in [Arabas et al. 2025 (JAMES)](https://doi.org/10.1029/2024MS004770)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "6d39ff74", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:27:00.461176Z", + "start_time": "2025-07-02T15:27:00.446613Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "b19713b4", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:27:12.391713Z", + "start_time": "2025-07-02T15:27:01.088978Z" + } + }, + "outputs": [], + "source": [ + "import string\n", + "import subprocess\n", + "import platform\n", + "import pathlib\n", + "\n", + "import numpy as np\n", + "\n", + "from scipy.io import netcdf_file\n", + "from scipy.ndimage import uniform_filter1d\n", + "from matplotlib import pyplot\n", + "\n", + "from PySDM.exporters import NetCDFExporter, VTKExporter\n", + "from PySDM_examples.utils import ProgBarController\n", + "from open_atmos_jupyter_utils import show_plot\n", + "import PySDM.products as PySDM_products\n", + "from PySDM.physics import si\n", + "from PySDM import Formulae\n", + "from PySDM.initialisation import spectra\n", + "from PySDM.dynamics import Freezing\n", + "\n", + "from PySDM_examples.Arabas_et_al_2015 import Settings\n", + "from PySDM_examples.utils.kinematic_2d import Simulation, Storage\n", + "from PySDM_examples.Arabas_et_al_2025.commons import FREEZING_CONSTANTS, LOGNORMAL_MODE_SURF_A, LOGNORMAL_SGM_G" + ] + }, + { + "cell_type": "markdown", + "id": "083e1887-fd91-4655-912e-43c8cf9f13d8", + "metadata": {}, + "source": [ + "## Simulations" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "b3ac5007", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:27:12.419147Z", + "start_time": "2025-07-02T15:27:12.416546Z" + } + }, + "outputs": [], + "source": [ + "lognormal_mode_A = LOGNORMAL_MODE_SURF_A\n", + "lognormal_sgm_g = LOGNORMAL_SGM_G\n", + "inp_frac = 1 / (270 + 45 + 1)\n", + "\n", + "conc_cld_unit = '1/cc'\n", + "conc_ice_unit = '1/l'\n", + "cool_rate_unit = 'K/min'\n", + "wall_time_unit = 'ms'\n", + "\n", + "N_REALISATIONS = 2" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "8ac0108f", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:27:12.457104Z", + "start_time": "2025-07-02T15:27:12.452086Z" + } + }, + "outputs": [], + "source": [ + "runs = (\n", + " {'settings': {'rhod_w_max': 2.0 * si.m/si.s*si.kg/si.m**3, 'freezing_immersion': 'time-dependent'}},\n", + " {'settings': {'rhod_w_max': 2.0 * si.m/si.s*si.kg/si.m**3, 'freezing_immersion': 'singular'}},\n", + " {'settings': {'rhod_w_max': 1.0 * si.m/si.s*si.kg/si.m**3, 'freezing_immersion': 'time-dependent'}},\n", + " {'settings': {'rhod_w_max': 1.0 * si.m/si.s*si.kg/si.m**3, 'freezing_immersion': 'singular'}},\n", + " {'settings': {'rhod_w_max': 0.5 * si.m/si.s*si.kg/si.m**3, 'freezing_immersion': 'time-dependent'}},\n", + " {'settings': {'rhod_w_max': 0.5 * si.m/si.s*si.kg/si.m**3, 'freezing_immersion': 'singular'}},\n", + ")\n", + "runs = tuple({'settings': {**run['settings'], 'seed': seed}} for run in runs for seed in range(N_REALISATIONS))" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6a3c69a9", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:27:12.530923Z", + "start_time": "2025-07-02T15:27:12.513427Z" + } + }, + "outputs": [], + "source": [ + "products = (\n", + " PySDM_products.DynamicWallTime(\n", + " 'Condensation', name='Condensation_wall_time', unit=wall_time_unit\n", + " ),\n", + " PySDM_products.DynamicWallTime(\n", + " 'Displacement', name='Displacement_wall_time', unit=wall_time_unit\n", + " ),\n", + " PySDM_products.DynamicWallTime(\n", + " 'Freezing', name='Freezing_wall_time', unit=wall_time_unit\n", + " ),\n", + " PySDM_products.DynamicWallTime(\n", + " 'EulerianAdvection', name='EulerianAdvection_wall_time', unit=wall_time_unit\n", + " ),\n", + " PySDM_products.ParticleConcentration(\n", + " radius_range=(-np.inf, 0*si.um), name='n_i', unit=conc_ice_unit, stp=True\n", + " ),\n", + " PySDM_products.ParticleConcentration(\n", + " radius_range=(1*si.um, np.inf), name='n_c', unit=conc_cld_unit, stp=True\n", + " ),\n", + " PySDM_products.CoolingRate(\n", + " unit=cool_rate_unit\n", + " ),\n", + " PySDM_products.IceNucleiConcentration(\n", + " name='n_inp', unit=conc_ice_unit, stp=True\n", + " ),\n", + " PySDM_products.FrozenParticleConcentration(\n", + " name='n_frozen_aerosols',\n", + " unit=conc_ice_unit,\n", + " count_activated=False,\n", + " count_unactivated=True,\n", + " stp=True\n", + " ),\n", + " PySDM_products.FrozenParticleConcentration(\n", + " name='n_frozen_droplets',\n", + " unit=conc_ice_unit,\n", + " count_activated=True,\n", + " count_unactivated=False,\n", + " stp=True\n", + " )\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "0b25f1e7-4887-4012-bf3b-13f6dc6d672f", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:27:12.573288Z", + "start_time": "2025-07-02T15:27:12.567096Z" + } + }, + "outputs": [], + "source": [ + "class SpinUp:\n", + " \"\"\" enables freezing dynamic after a given number of steps \"\"\"\n", + " def __init__(self, particulator, spin_up_steps):\n", + " self.spin_up_steps = spin_up_steps\n", + " particulator.observers.append(self)\n", + " self.particulator = particulator\n", + " self.set(Freezing, \"enable\", False)\n", + "\n", + " def notify(self):\n", + " if self.particulator.n_steps == self.spin_up_steps:\n", + " self.set(Freezing, \"enable\", True)\n", + "\n", + " def set(self, dynamic, attr, val):\n", + " name = dynamic.__name__\n", + " if name in self.particulator.dynamics:\n", + " setattr(self.particulator.dynamics[name], attr, val)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "944f6247", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-03T11:47:52.429814Z", + "start_time": "2025-07-03T11:47:50.879183Z" + } + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "2f6e08253497454c973d5798acafb382", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='run 1/12', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "3a52b0952a8c49cc908c6035c8964083", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='netCDF', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "b20a877c36b64803a6722fceae6c0342", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='run 2/12', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "12687bf2f4de46db985e5c9880293241", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='netCDF', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "9288038fadfb4c46aeb56d601089f190", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='run 3/12', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "2ff7195ebca547f798273cbf3e12f1f4", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='netCDF', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "89187f2260a4455081ea8eda3f715b8e", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='run 4/12', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "1862b3daa66549ffa2be8c76173633c1", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='netCDF', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ee31fc006dd74eb88c5b8d72776dd91d", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='run 5/12', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "457e1bb1bfdc4aa98630054e6be89820", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='netCDF', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "5cc0ee9710ca4a51ac49b0ef4f8785ae", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='run 6/12', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "2316a3d3652a4a79bef1244a64cb6330", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='netCDF', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "53a201a50b28407188417714634b02dc", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='run 7/12', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "2b50eabbf9f4406fab2fef418047fe04", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='netCDF', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "3db11403b04e4aea952e43928726d920", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='run 8/12', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ea747836357743bbbf018b8e4835c714", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='netCDF', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f0b892017cc84e40b14badad23835141", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='run 9/12', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "18cb085a8e3242ea8b23a882cc4fcc8b", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='netCDF', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "4794827eee5648d1b43a311d36a64aad", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='run 10/12', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "31c8f505776241b49452fb9ee967ffaa", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='netCDF', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f8a7ac0e25454df5b204103b5fe7672b", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='run 11/12', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "2403f9ac1a4f45a4be14c047847ff3d5", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='netCDF', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "c7ba003290694424aedb3d714c6123fc", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='run 12/12', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "a96fc3ab5e354840aee9c0eeac7d1417", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='netCDF', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "formulae = Formulae(\n", + " particle_shape_and_density=\"MixedPhaseSpheres\",\n", + " freezing_temperature_spectrum='Niemand_et_al_2012',\n", + " heterogeneous_ice_nucleation_rate='ABIFM',\n", + " constants=FREEZING_CONSTANTS[\"dust\"],\n", + ")\n", + "\n", + "for i, run in enumerate(runs):\n", + " folder = f\"output/rhod_w_max={run['settings']['rhod_w_max']}_{run['settings']['freezing_immersion']}_seed={run['settings']['seed']}\"\n", + " os.makedirs(folder, exist_ok=True)\n", + " \n", + " run['ncfile'] = f'{folder}/out.nc'\n", + "\n", + " formulae.seed = run['settings']['seed']\n", + " settings = Settings(formulae)\n", + " settings.dt = 2.5 * si.s\n", + " settings.output_interval = settings.dt * 12\n", + " settings.simulation_time = 6000 * si.second if 'CI' not in os.environ else 2 * settings.output_interval\n", + " settings.spin_up_time = 600 * si.second\n", + " settings.size = (1500, 500)\n", + " settings.n_sd_per_gridbox = 32\n", + " settings.grid = (60, 20)\n", + " settings.th_std0 -= 33.3 * si.kelvins\n", + " settings.initial_water_vapour_mixing_ratio -= 6.66 * si.grams / si.kilogram\n", + " \n", + " settings.processes['coalescence'] = False\n", + " settings.processes['sedimentation'] = False\n", + " settings.processes['freezing'] = True\n", + " settings.freezing_inp_spec = spectra.Lognormal(\n", + " norm_factor=1,\n", + " m_mode=lognormal_mode_A,\n", + " s_geom=lognormal_sgm_g\n", + " )\n", + " settings.freezing_inp_frac = inp_frac\n", + " settings.freezing_thaw = 'instantaneous'\n", + "\n", + " settings.kappa = 0.61\n", + " settings.mode_1 = spectra.Lognormal(\n", + " norm_factor=(270 + 270/315) / si.centimetre**3 / formulae.constants.rho_STP,\n", + " m_mode=0.03 * si.micrometre,\n", + " s_geom=1.28,\n", + " )\n", + " settings.mode_2 = spectra.Lognormal(\n", + " norm_factor=(45 + 45/315) / si.centimetre**3 / formulae.constants.rho_STP,\n", + " m_mode=0.14 * si.micrometre,\n", + " s_geom=1.75,\n", + " )\n", + " settings.spectrum_per_mass_of_dry_air = spectra.Sum((settings.mode_1, settings.mode_2))\n", + " \n", + " for key, value in run['settings'].items(): \n", + " if key != 'seed':\n", + " assert hasattr(settings, key)\n", + " setattr(settings, key, value)\n", + "\n", + " storage = Storage()\n", + " simulation = Simulation(settings, storage, SpinUp=SpinUp)\n", + " simulation.reinit(products)\n", + "\n", + " vtk_exporter = VTKExporter(path=folder) \n", + " simulation.run(ProgBarController(f\"run {i+1}/{len(runs)}\"), vtk_exporter=vtk_exporter)\n", + " vtk_exporter.write_pvd()\n", + "\n", + " ncdf_exporter = NetCDFExporter(storage, settings, simulation, run['ncfile'])\n", + " ncdf_exporter.run(ProgBarController('netCDF'))" + ] + }, + { + "cell_type": "markdown", + "id": "27d1536b-6c7d-4e68-91e9-6a1fe2742026", + "metadata": {}, + "source": [ + "## Fig 11" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "241f48c4", + "metadata": {}, + "outputs": [], + "source": [ + "def label(settings_arg):\n", + " tmp = str({k.replace('condensation_', ''):\n", + " f\"{v:.1e}\" if isinstance(v, float) else\n", + " str(v).zfill(2) if isinstance(v, int) else\n", + " v for k, v in settings_arg.items()})\n", + " return tmp\\\n", + " .replace('{', '')\\\n", + " .replace('}', '')\\\n", + " .replace(\"'\", '')\\\n", + " .replace('rhod_w_max:', '$w_{max}\\\\approx$')\\\n", + " .replace('e+00', r' m/s')\\\n", + " .replace('5.0e-01', '0.5 m/s')\\\n", + " .replace('freezing_immersion: ', '')\\\n", + " .replace('singular', r'singular$\\,\\,\\,$')\\\n", + " .replace(', seed: 00', '')" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "bba6ffb0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "$w_{max}\\approx$ 0.5 m/s, singular$\\,\\,\\,$, seed: 01: time=26.18ms\n", + "$w_{max}\\approx$ 0.5 m/s, singular$\\,\\,\\,$: time=26.88ms\n", + "$w_{max}\\approx$ 0.5 m/s, time-dependent, seed: 01: time=26.69ms\n", + "$w_{max}\\approx$ 0.5 m/s, time-dependent: time=27.07ms\n", + "$w_{max}\\approx$ 1.0 m/s, singular$\\,\\,\\,$, seed: 01: time=29.84ms\n", + "$w_{max}\\approx$ 1.0 m/s, singular$\\,\\,\\,$: time=29.95ms\n", + "$w_{max}\\approx$ 1.0 m/s, time-dependent, seed: 01: time=30.41ms\n", + "$w_{max}\\approx$ 1.0 m/s, time-dependent: time=30.15ms\n", + "$w_{max}\\approx$ 2.0 m/s, singular$\\,\\,\\,$, seed: 01: time=33.16ms\n", + "$w_{max}\\approx$ 2.0 m/s, singular$\\,\\,\\,$: time=32.70ms\n", + "$w_{max}\\approx$ 2.0 m/s, time-dependent, seed: 01: time=32.16ms\n", + "$w_{max}\\approx$ 2.0 m/s, time-dependent: time=34.79ms\n", + "$w_{max}\\approx$ 0.5 m/s, singular$\\,\\,\\,$, seed: 01: time=26.18ms\n", + "$w_{max}\\approx$ 0.5 m/s, singular$\\,\\,\\,$: time=26.88ms\n", + "$w_{max}\\approx$ 0.5 m/s, time-dependent, seed: 01: time=26.69ms\n", + "$w_{max}\\approx$ 0.5 m/s, time-dependent: time=27.07ms\n", + "$w_{max}\\approx$ 1.0 m/s, singular$\\,\\,\\,$, seed: 01: time=29.84ms\n", + "$w_{max}\\approx$ 1.0 m/s, singular$\\,\\,\\,$: time=29.95ms\n", + "$w_{max}\\approx$ 1.0 m/s, time-dependent, seed: 01: time=30.41ms\n", + "$w_{max}\\approx$ 1.0 m/s, time-dependent: time=30.15ms\n", + "$w_{max}\\approx$ 2.0 m/s, singular$\\,\\,\\,$, seed: 01: time=33.16ms\n", + "$w_{max}\\approx$ 2.0 m/s, singular$\\,\\,\\,$: time=32.70ms\n", + "$w_{max}\\approx$ 2.0 m/s, time-dependent, seed: 01: time=32.16ms\n", + "$w_{max}\\approx$ 2.0 m/s, time-dependent: time=34.79ms\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-06T04:03:08.746845\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "dab3c352a08a40d1b37d19e5e9d11317", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./figures.pdf
\"), HTML(value=\"= n_last_output_steps\n", + "\n", + " style = {\n", + " 'color': colors[i % len(runs) // N_REALISATIONS], \n", + " 'lw': 1.5 if run['settings']['freezing_immersion'] == 'singular' else 3,\n", + " 'ls': '--' if run['settings']['freezing_immersion'] != 'singular' else '-'\n", + " }\n", + " \n", + " if var != 'n_i':\n", + " wall_time = np.nanmean(nc.variables['Freezing_wall_time'][timesteps] / nc.steps_per_output_interval)\n", + " wall_time = np.nan if not np.isfinite(wall_time) else int(100 * wall_time) / 100\n", + " lbl = label(run['settings'])\n", + " print(f\"{lbl}: time={wall_time:.2f}{wall_time_unit}\") \n", + "\n", + " y, x, _ = ax.hist(\n", + " data[timesteps, :, :].flatten(), \n", + " bins=bins[var],\n", + " range=bin_range[var],\n", + " histtype='step', \n", + " color=style['color'],\n", + " lw=0\n", + " )\n", + " y /= n_last_output_steps\n", + " filt_x = x[:-1] if window % 2 == 0 else (x[1:] + x[:-1])/2\n", + " ax.plot(\n", + " filt_x,\n", + " uniform_filter1d(y, size=window),\n", + " **style,\n", + " label=f\"{lbl}\" if run['settings']['seed'] == 0 else \"\"\n", + " )\n", + " if i == 0:\n", + " ax.set_yscale('log')\n", + " ax.set_ylabel('occurrence count per step ' + f'({window}-bin moving average)')\n", + " binwidth = (bin_range[var][1]-bin_range[var][0])/bins[var]\n", + " ax.set_xlim(bin_range[var])\n", + " ax.set_ylim(2, 200)\n", + " if var == 'n_inp':\n", + " ax.set_xlabel(f'inclusion-rich (frozen or unfrozen) particle conc. [{conc_ice_unit} STP] ({binwidth} binning)')\n", + " elif var == 'n_frozen_aerosols':\n", + " ax.set_ylim(.01, 100)\n", + " ax.set_xlabel(f'frozen aerosol concentration [{conc_ice_unit} STP] ({binwidth} binning)')\n", + " elif var == 'n_frozen_droplets':\n", + " ax.set_ylim(.01, 100)\n", + " ax.set_xlabel(f'frozen droplet concentration [{conc_ice_unit} STP] ({binwidth} binning)')\n", + " elif var == 'n_c':\n", + " ax.set_xlabel(f'cloud droplet concentration [{conc_cld_unit} STP] ({binwidth} binning)')\n", + " elif var == 'cooling rate':\n", + " ax.set_xlabel(f'cooling rate [{cool_rate_unit}] ({binwidth} binning)')\n", + " else:\n", + " assert False\n", + " else:\n", + " ax.plot(\n", + " nc.variables['T'][:] / si.min,\n", + " np.mean(np.mean(data[:,:,:], axis=1), axis=1) ,\n", + " **style\n", + " )\n", + " if i == 0:\n", + " ax.set_yscale('log')\n", + " ax.set_ylim(.05, 50)\n", + " ax.set_ylabel(f'domain-mean time-accumulated ice conc. [{conc_ice_unit} STP]')\n", + " ax.set_xlabel('time [min]')\n", + " for note, times in {\n", + " \"spinup\": [0, min(n_spinup, len(nc.variables['T'][:])-1)],\n", + " \"occurence counting\": [timesteps.start, -1]\n", + " }.items():\n", + " x = nc.variables['T'][times] / si.min\n", + " y = 20\n", + " ax.plot(\n", + " x,\n", + " [y] * 2,\n", + " color='gray'\n", + " )\n", + " ax.text(x[0], 1.1 * y, note, color='gray', size=8)\n", + " \n", + " ax.grid(which='minor')\n", + " ax.grid(which='major')\n", + " if var == 'cooling rate':\n", + " ax.legend(loc='upper right')\n", + " ax.text(\n", + " 0, 1.03,\n", + " '('+string.ascii_lowercase[plot_i]+')',\n", + " transform=ax.transAxes,\n", + " size=15,\n", + " weight='bold'\n", + " )\n", + "show_plot(\"figures.pdf\")" + ] + }, + { + "cell_type": "markdown", + "id": "f497f98e-dea9-4284-9d57-926f41e135ae", + "metadata": {}, + "source": [ + "## Fig 10 & animations" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "de395e91-cbbc-4763-9872-51d8f90f588a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Writing pvscript.py\n" + ] + } + ], + "source": [ + "%%writefile pvscript.py\n", + "\n", + "import argparse\n", + "import json\n", + "from paraview import simple as pvs\n", + "\n", + "arg_parser = argparse.ArgumentParser()\n", + "arg_parser.add_argument('path')\n", + "\n", + "arg_parser.add_argument('--particles_var')\n", + "arg_parser.add_argument('--particles_unit')\n", + "arg_parser.add_argument('--particles_color_range')\n", + "arg_parser.add_argument('--particles_var_multiplier', default=1)\n", + "arg_parser.add_argument('--particles_logscale', default=False)\n", + "\n", + "arg_parser.add_argument('--surface_var')\n", + "arg_parser.add_argument('--surface_color_range')\n", + "arg_parser.add_argument('--surface_unit')\n", + "\n", + "arg_parser.add_argument('--save_frame_pdfs', action=argparse.BooleanOptionalAction)\n", + "arg_parser.add_argument('--anim_path_suffix', default='')\n", + "\n", + "args = arg_parser.parse_args()\n", + "\n", + "# load data\n", + "reader_prod = pvs.OpenDataFile(f\"{args.path}/output/sd_products.pvd\")\n", + "reader_attr = pvs.OpenDataFile(f\"{args.path}/output/sd_attributes.pvd\")\n", + "\n", + "# prepare view settings\n", + "view = pvs.GetRenderView()\n", + "view.ViewSize = [2000, 800]\n", + "view.Background = [1, 1, 1]\n", + "view.CenterAxesVisibility = False\n", + "view.OrientationAxesVisibility = False\n", + "axesGrid = view.AxesGrid\n", + "axesGrid.Visibility = True\n", + "axesGrid.XTitle = 'Z [$m$]'\n", + "axesGrid.YTitle = 'X [$m$]'\n", + "\n", + "axesGrid.XAxisUseCustomLabels = True\n", + "axesGrid.XAxisLabels = [0, 125, 375, 500]\n", + "axesGrid.YAxisUseCustomLabels = True\n", + "axesGrid.YAxisLabels = [300, 600, 900, 1200]\n", + "\n", + "axesGrid.XTitleFontSize = 30\n", + "axesGrid.XLabelFontSize = 30\n", + "axesGrid.YTitleFontSize = 30\n", + "axesGrid.YLabelFontSize = 30\n", + "\n", + "axesGrid.XTitleColor = [0, 0, 0]\n", + "axesGrid.XLabelColor = [0, 0, 0]\n", + "axesGrid.YTitleColor = [0, 0, 0]\n", + "axesGrid.YLabelColor = [0, 0, 0]\n", + "axesGrid.GridColor = [0.1, 0.1, 0.1]\n", + "\n", + "# render particles\n", + "if args.particles_var is not None:\n", + " palette = 'Cold and Hot'\n", + " palette_invert = True\n", + " title = args.particles_var + r' [$' + args.particles_unit + '$]'\n", + " \n", + " calculator = pvs.Calculator(reader_attr)\n", + " calculator.Function = f'{args.particles_var}*{args.particles_var_multiplier}'\n", + " display_attr = pvs.Show(calculator, view)\n", + " \n", + " display_attr.SetRepresentationType('Point Gaussian')\n", + " display_attr.ShaderPreset = 'Sphere'\n", + " display_attr.GaussianRadius = 3\n", + " display_attr.MapScalars = 1\n", + " \n", + " display_attr.Ambient = .25\n", + " pvs.ColorBy(display_attr, ('POINTS', 'Result'))\n", + " color_scale_attr = pvs.GetColorTransferFunction('Result')\n", + " color_scale_attr.ApplyPreset(palette, True)\n", + " if palette_invert:\n", + " color_scale_attr.InvertTransferFunction()\n", + " if args.particles_color_range is None:\n", + " display_attr.RescaleTransferFunctionToDataRange(True)\n", + " else:\n", + " color_scale_attr.RescaleTransferFunction(json.loads(args.particles_color_range))\n", + " if args.particles_logscale:\n", + " color_scale_attr.MapControlPointsToLogSpace()\n", + " color_scale_attr.UseLogScale = 1\n", + " colorbar_attr = pvs.GetScalarBar(color_scale_attr, view)\n", + " colorbar_attr.TitleColor = [0, 0, 0]\n", + " colorbar_attr.LabelColor = [0, 0, 0]\n", + " colorbar_attr.Title = title\n", + " colorbar_attr.ComponentTitle = ''\n", + " colorbar_attr.TitleFontSize = 30\n", + " colorbar_attr.LabelFontSize = 30\n", + " colorbar_attr.Visibility = True\n", + " colorbar_attr.WindowLocation = 'Any Location'\n", + " colorbar_attr.Position = [.025, .333]\n", + " colorbar_attr.RangeLabelFormat = '%g'\n", + " \n", + "# render product\n", + "if args.surface_var is not None:\n", + " palette = 'X Ray'\n", + " palette_invert = True\n", + " color_range = [0, 300]\n", + " logscale = False\n", + " title = '$' + args.surface_var + '$' + ' [$' + args.surface_unit + '$ STP]'\n", + " \n", + " display_prod = pvs.Show(reader_prod)\n", + " display_prod.SetRepresentationType('Surface')\n", + " display_prod.Ambient = .25\n", + " pvs.ColorBy(display_prod, ('CELLS', args.surface_var))\n", + " color_scale_prod = pvs.GetColorTransferFunction(args.surface_var)\n", + " if args.surface_color_range is None:\n", + " display_prod.RescaleTransferFunctionToDataRange(True)\n", + " else:\n", + " color_scale_prod.RescaleTransferFunction(json.loads(args.surface_color_range))\n", + " color_scale_prod.ApplyPreset(palette, True)\n", + " if palette_invert:\n", + " color_scale_prod.InvertTransferFunction()\n", + " colorbar_prod = pvs.GetScalarBar(color_scale_prod, view)\n", + " colorbar_prod.TitleColor = [0, 0, 0]\n", + " colorbar_prod.LabelColor = [0, 0, 0]\n", + " colorbar_prod.Title = title\n", + " colorbar_prod.ComponentTitle = ''\n", + " colorbar_prod.TitleFontSize = 30\n", + " colorbar_prod.LabelFontSize = 30\n", + " colorbar_prod.Visibility = True\n", + " colorbar_prod.Position = [.925, .333]\n", + " colorbar_prod.WindowLocation = 'Any Location'\n", + " colorbar_prod.RangeLabelFormat = '%g'\n", + "\n", + "# time annotation\n", + "time = pvs.AnnotateTimeFilter(guiName = \"AnnotateTimeFilter1\", Format = 'Time: %gs')\n", + "repr = pvs.Show(time, view)\n", + "repr.Color = [0.0, 0.0, 0.0]\n", + "repr.FontSize = 20\n", + "view.Update()\n", + "\n", + "# compose the scene\n", + "scene = pvs.GetAnimationScene()\n", + "scene.UpdateAnimationUsingDataTimeSteps()\n", + "pvs.Render(view)\n", + "cam = pvs.GetActiveCamera()\n", + "cam.SetViewUp(1, 0, 0)\n", + "pos = list(cam.GetPosition())\n", + "pos[-1] = -pos[-1]\n", + "cam.SetPosition(pos)\n", + "cam.Dolly(1.85)\n", + "\n", + "# save animation to an Ogg Vorbis file\n", + "anim_file = f'{args.path}/anim{args.anim_path_suffix}.ogv'\n", + "print(anim_file)\n", + "pvs.SaveAnimation(anim_file, view, FrameRate=5, Quality=0)\n", + "\n", + "# save animation frame as pdfs\n", + "if args.save_frame_pdfs is not None:\n", + " for t in reader_prod.TimestepValues:\n", + " if t not in (0, 600, 1800, 6000):\n", + " continue\n", + " view.ViewTime = t\n", + " for reader in (reader_prod, reader_attr):\n", + " reader.UpdatePipeline(t)\n", + " pvs.ExportView(\n", + " filename=f'{args.path}/anim_frame_{t}{args.anim_path_suffix}.pdf',\n", + " view=view,\n", + " Rasterize3Dgeometry= False,\n", + " GL2PSdepthsortmethod= 'BSP sorting (slow, best)',\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4df59c0e-5203-4572-9cdf-9c5773f3a4ca", + "metadata": {}, + "outputs": [], + "source": [ + "if not ('CI' in os.environ and platform.system() == 'Windows'):\n", + " for path in pathlib.Path('output').glob(\"*\"):\n", + " print(path)\n", + " for args in (\n", + " [path, '--anim_path_suffix=_cld', '--surface_var=n_c', '--surface_color_range=[0, 300]', '--particles_var=radius', \n", + " '--particles_var_multiplier=1e6', '--particles_color_range=[0.5,10]', '--particles_logscale=True', \n", + " r'--particles_unit=\\mu m', f'--surface_unit={conc_cld_unit}',\n", + " '--save_frame_pdfs',\n", + " ],\n", + " [path, \n", + " '--anim_path_suffix=_ice',\n", + " '--surface_var=n_i',\n", + " '--surface_color_range=[0, 100]',\n", + " f'--surface_unit={conc_ice_unit}',\n", + " '--save_frame_pdfs',\n", + " ] + (\n", + " [\n", + " '--particles_var=freezing temperature',\n", + " '--particles_color_range=[225, 250]',\n", + " '--particles_unit=K',\n", + " ] \n", + " if 'singular' in str(path) else \n", + " [\n", + " '--particles_var=immersed surface area',\n", + " '--particles_color_range=[0, 1]',\n", + " r'--particles_unit=\\mu m^2', \n", + " '--particles_var_multiplier=1e12',\n", + " ]\n", + " ),\n", + " ):\n", + " subprocess.run(\n", + " ['pvpython', '--force-offscreen-rendering', 'pvscript.py'] + args,\n", + " check=True,\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e114c258-89d1-4f25-8140-7c41f491fde1", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "adef4842-0234-4952-8955-ad558b4143dc", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/figs_3_and_7_and_8.ipynb b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/figs_3_and_7_and_8.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..f48e2c17bd177b26911085c4eed0b2c3a52cf103 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/figs_3_and_7_and_8.ipynb @@ -0,0 +1,39267 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c75b5c0c2d10ac25", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Arabas_et_al_2025/figs_3_and_7_and_8.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Arabas_et_al_2025/figs_3_and_7_and_8.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Arabas_et_al_2025/figs_3_and_7_and_8.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "99148d254fcace2f", + "metadata": {}, + "source": [ + "Figs 3, 7 and 8 in [Arabas et al. 2025 (JAMES)](https://doi.org/10.1029/2024MS004770)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "a1ef1c9d72aa9b36", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:21:05.909741Z", + "start_time": "2025-07-02T15:21:05.904417Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "12a0626c", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:21:14.509316Z", + "start_time": "2025-07-02T15:21:06.659893Z" + } + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "from scipy.interpolate import interp1d\n", + "from IPython.display import display\n", + "from ipywidgets import FloatProgress\n", + "from open_atmos_jupyter_utils import show_plot\n", + "from PySDM import Formulae\n", + "from PySDM.physics import si\n", + "from PySDM.initialisation.spectra import Lognormal\n", + "\n", + "from PySDM_examples.Arabas_et_al_2025 import make_particulator, run_simulation, \\\n", + " make_freezing_spec_plot, make_pdf_plot\n", + "from PySDM_examples.Arabas_et_al_2025.commons import (\n", + " FREEZING_CONSTANTS, COOLING_RATES, BEST_FIT_LN_S_GEOM, TEMP_RANGE, LOGNORMAL_MODE_SURF_A\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "59aec929", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:21:14.568158Z", + "start_time": "2025-07-02T15:21:14.530471Z" + } + }, + "outputs": [], + "source": [ + "constants = FREEZING_CONSTANTS[\"dust\"]\n", + "\n", + "params = {\n", + " 'log2_n_sd': 6,\n", + " 'n_steps': 60,\n", + " 'ensemble_n': 5,\n", + " 'drop_v_um3': 3,\n", + " 'ln10_drop_n': 15,\n", + " 'ln10_vol_m3': 6,\n", + "}\n", + "\n", + "formulae = Formulae(\n", + " freezing_temperature_spectrum='Niemand_et_al_2012',\n", + " constants=constants\n", + ")\n", + "\n", + "def A_spec(ln_s_geom_arg):\n", + " return Lognormal(norm_factor=1, m_mode=LOGNORMAL_MODE_SURF_A, s_geom=np.exp(ln_s_geom_arg))" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "d367d5e1", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:21:19.178563Z", + "start_time": "2025-07-02T15:21:14.578021Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-02T17:21:17.541872\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "122d8d23f4db45389ce89c89fe173e53", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_0d_pdf_0.05.pdf
\"), HT…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-02T17:21:18.322020\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "5843f3e27c454ca89ea6b5b3b4fc948e", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_0d_pdf_0.25.pdf
\"), HT…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-02T17:21:19.094609\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "51b96d7bb6704d8bb8a2f704d39a3b6b", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_0d_pdf_1.25.pdf
\"), HT…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "for ln_s_geom in (BEST_FIT_LN_S_GEOM/5, BEST_FIT_LN_S_GEOM, BEST_FIT_LN_S_GEOM*5):\n", + " make_pdf_plot(\n", + " A_spec(ln_s_geom),\n", + " formulae.freezing_temperature_spectrum.pdf,\n", + " A_range = (0 * si.um ** 2, 5 * si.um ** 2),\n", + " T_range = TEMP_RANGE\n", + " )\n", + " show_plot(f'fig_0d_pdf_{ln_s_geom}.pdf')" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "7f46a04c", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:21:19.210546Z", + "start_time": "2025-07-02T15:21:19.204296Z" + } + }, + "outputs": [], + "source": [ + "def run_and_plot(*, \n", + " log2_n_sd, n_steps, ensemble_n,\n", + " drop_v_um3,\n", + " ln10_drop_n,\n", + " ln10_vol_m3,\n", + " ln_s_geom_arg,\n", + " times,\n", + " temps\n", + " ):\n", + " progbar = FloatProgress(value=0, min=0, max=100, description='%')\n", + " display(progbar)\n", + "\n", + " progbar.value = 0\n", + " \n", + " temperature_profile = interp1d(\n", + " x=times, \n", + " y=temps,\n", + " kind='linear',\n", + " fill_value=\"extrapolate\"\n", + " )\n", + " assert temperature_profile.x[0] == 0\n", + " \n", + " common_params = {\n", + " 'droplet_volume': drop_v_um3 * si.um**3,\n", + " 'total_particle_number': 10**ln10_drop_n,\n", + " 'volume': 10**ln10_vol_m3\n", + " }\n", + " surf_spec=A_spec(ln_s_geom_arg)\n", + " \n", + " output = []\n", + " for singular in (True, False):\n", + " for seed in range(ensemble_n):\n", + " particulator = make_particulator(\n", + " constants=constants,\n", + " n_sd=2**log2_n_sd, \n", + " dt=temperature_profile.x[-1]/n_steps,\n", + " initial_temperature = temperature_profile(0),\n", + " singular=singular,\n", + " seed=seed,\n", + " shima_T_fz=formulae.freezing_temperature_spectrum.__name__,\n", + " ABIFM_spec=surf_spec,\n", + " **common_params\n", + " )\n", + " output.append({\n", + " **run_simulation(particulator, temperature_profile, n_steps),\n", + " 'singular': singular\n", + " })\n", + " progbar.value += 100/2/ensemble_n\n", + "\n", + " if len(temperature_profile.x) == 2:\n", + " cooling_rate_K_min = np.diff(temperature_profile.y)[0] / np.diff(temperature_profile.x)[0] / (si.K/si.min)\n", + " else:\n", + " cooling_rate_K_min = 'variable'\n", + "\n", + " make_freezing_spec_plot(\n", + " output, \n", + " formulae, \n", + " surf_spec=surf_spec,\n", + " **common_params,\n", + " cooling_rate_K_min=cooling_rate_K_min\n", + " )\n", + " show_plot(f'fig2-c={cooling_rate_K_min}_K_per_min-ln_s_geom={ln_s_geom_arg}.pdf')" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "8dff9902", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:22:23.512628Z", + "start_time": "2025-07-02T15:21:19.255550Z" + } + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "74aea566a4e2496ba36a57d890cc6a28", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='%')" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "singular\n", + "singular\n", + "singular\n", + "singular\n", + "singular\n", + "time-dependent\n", + "time-dependent\n", + "time-dependent\n", + "time-dependent\n", + "time-dependent\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-02T17:21:50.769688\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "73e0c7b2efb5487583f4e877addc0227", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig2-c=-0.7…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "731c212fcb244e6497931905f4f4fa3f", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='%')" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "singular\n", + "singular\n", + "singular\n", + "singular\n", + "singular\n", + "time-dependent\n", + "time-dependent\n", + "time-dependent\n", + "time-dependent\n", + "time-dependent\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-02T17:22:07.220636\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "b377222fd09f4e15a69ca7a231722ab2", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig2-c=-0.7…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "0e24d78583b24afd95bba18206cee98b", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='%')" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "singular\n", + "singular\n", + "singular\n", + "singular\n", + "singular\n", + "time-dependent\n", + "time-dependent\n", + "time-dependent\n", + "time-dependent\n", + "time-dependent\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-02T17:22:23.340210\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "71d03e801f8c495e9ef8f0c35d2b84dc", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig2-c=-0.75…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "assert len(COOLING_RATES) == 3\n", + "abs_base_cooling_rate = abs(COOLING_RATES[1])\n", + "dT = TEMP_RANGE[0] - TEMP_RANGE[1]\n", + "\n", + "for ln_s_geom in (BEST_FIT_LN_S_GEOM, 5*BEST_FIT_LN_S_GEOM, 10*BEST_FIT_LN_S_GEOM): \n", + " run_and_plot(\n", + " **params,\n", + " ln_s_geom_arg=ln_s_geom,\n", + " times=np.asarray([0, dT / abs_base_cooling_rate]),\n", + " temps=np.asarray(list(TEMP_RANGE))\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "64cf293e", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:23:09.526588Z", + "start_time": "2025-07-02T15:22:23.584408Z" + } + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "bd60a53d00504c779594e86ea1315248", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='%')" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "singular\n", + "singular\n", + "singular\n", + "singular\n", + "singular\n", + "time-dependent\n", + "time-dependent\n", + "time-dependent\n", + "time-dependent\n", + "time-dependent\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-02T17:22:39.265548\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "566fd349e73c46339de5ac06b50eeda5", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig2-c=-3.7…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "4b62684782c042418d273de264c564f9", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='%')" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "singular\n", + "singular\n", + "singular\n", + "singular\n", + "singular\n", + "time-dependent\n", + "time-dependent\n", + "time-dependent\n", + "time-dependent\n", + "time-dependent\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-02T17:22:54.764988\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "8bcb10018f104bd4b3be886d1b3b3734", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig2-c=-0.7…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "003d091f65b34827ab5974b52f676c6d", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='%')" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "singular\n", + "singular\n", + "singular\n", + "singular\n", + "singular\n", + "time-dependent\n", + "time-dependent\n", + "time-dependent\n", + "time-dependent\n", + "time-dependent\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-02T17:23:09.399042\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "539df087d43148fdb1675c01836eb942", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig2-c=-0.1…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "for abs_cooling_rate in [abs(c) for c in COOLING_RATES]:\n", + " run_and_plot(\n", + " **params,\n", + " ln_s_geom_arg=BEST_FIT_LN_S_GEOM,\n", + " times=np.asarray([0, dT / abs_cooling_rate]),\n", + " temps=np.asarray(list(TEMP_RANGE))\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4a548d94-908e-4f3d-b484-80ccc8420c4f", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:23:09.572438Z", + "start_time": "2025-07-02T15:23:09.569484Z" + } + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/figs_5_and_6.ipynb b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/figs_5_and_6.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..d4c2d4873240dbab6136aedca8eb6dc231b0345e --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/figs_5_and_6.ipynb @@ -0,0 +1,25902 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "66ce53c182d3bdec", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Arabas_et_al_2025/figs_5_and_6.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Arabas_et_al_2025/figs_5_and_6.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Arabas_et_al_2025/figs_5_and_6.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "bc1f274e9f347bca", + "metadata": {}, + "source": [ + "Figs 5 and 6 in [Arabas et al. 2025 (JAMES)](https://doi.org/10.1029/2024MS004770)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "b1ab25d00d401238", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:24:49.821785Z", + "start_time": "2025-07-02T15:24:49.816603Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "0f657f55", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:25:00.807053Z", + "start_time": "2025-07-02T15:24:50.574750Z" + } + }, + "outputs": [], + "source": [ + "from scipy.interpolate import interp1d\n", + "import numpy as np\n", + "from matplotlib import pyplot, markers, patches\n", + "\n", + "from PySDM import Formulae\n", + "from PySDM.physics import si\n", + "from PySDM.initialisation.spectra import Lognormal\n", + "from PySDM_examples.Arabas_et_al_2025.frozen_fraction import FrozenFraction\n", + "from PySDM_examples.Arabas_et_al_2025 import make_particulator, run_simulation\n", + "from PySDM_examples.Arabas_et_al_2025.commons import FREEZING_CONSTANTS, LOGNORMAL_MODE_SURF_A, BEST_FIT_LN_S_GEOM\n", + "\n", + "from open_atmos_jupyter_utils import show_plot" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "3a4a9dee", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:25:00.922399Z", + "start_time": "2025-07-02T15:25:00.855150Z" + } + }, + "outputs": [], + "source": [ + "constants = FREEZING_CONSTANTS[\"dust\"]\n", + "\n", + "formulae = Formulae(\n", + " freezing_temperature_spectrum='Niemand_et_al_2012',\n", + " constants=constants\n", + ")\n", + "\n", + "n_sd = 32\n", + "n_steps = 120\n", + "volume = 10**6 * si.m**3\n", + "droplet_volume = 3 * si.um**3 \n", + "total_particle_number = 10**15\n", + "ensemble_n = 3\n", + "T_end_min = 60\n", + "\n", + "interp_kwargs = {'kind': 'linear', 'fill_value': \"extrapolate\"}\n", + "temperature_profiles = {\n", + " 'a': interp1d(\n", + " x=(0, T_end_min * si.min), \n", + " y=(265 * si.K, 225 * si.K),\n", + " **interp_kwargs\n", + " ), \n", + " 'b': interp1d(\n", + " x=(0, T_end_min * si.min), \n", + " y=(250 * si.K, 240 * si.K),\n", + " **interp_kwargs\n", + " ), \n", + " 'c': interp1d(\n", + " x=(0, 15*si.min, T_end_min * si.min), \n", + " y=(280 * si.K, 240 * si.K, 240 * si.K),\n", + " **interp_kwargs\n", + " ), \n", + " 'd': interp1d(\n", + " x=(0, 14 * si.min, 15 * si.min, 16 * si.min, T_end_min * si.min), \n", + " y=(270 * si.K, 270 * si.K, 225 * si.K, 270 * si.K, 270 * si.K),\n", + " **interp_kwargs\n", + " ), \n", + " 'e': interp1d(\n", + " x=(0, 15 * si.min, T_end_min * si.min), \n", + " y=(280 * si.K, 240 * si.K, 250 * si.K),\n", + " **interp_kwargs\n", + " ), \n", + " 'f': interp1d(\n", + " x=(0, 5 * si.min, 15 * si.min, 20 * si.min, 30 * si.min, 35 * si.min, 45 * si.min, 50 * si.min, T_end_min * si.min), \n", + " y=(280 * si.K, 237.5 * si.K, 280 * si.K, 237.5 * si.K, 280 * si.K, 237.5 * si.K, 280 * si.K, 237.5 * si.K, 280 * si.K),\n", + " **interp_kwargs\n", + " ), \n", + "}\n", + "\n", + "ff = FrozenFraction(\n", + " volume=volume,\n", + " droplet_volume=droplet_volume,\n", + " total_particle_number=total_particle_number,\n", + " rho_w=formulae.constants.rho_w,\n", + ")\n", + "\n", + "A_spec = Lognormal(\n", + " norm_factor=1,\n", + " m_mode=LOGNORMAL_MODE_SURF_A,\n", + " s_geom=np.exp(BEST_FIT_LN_S_GEOM)\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "e4c60366", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:26:18.947951Z", + "start_time": "2025-07-02T15:25:00.934038Z" + } + }, + "outputs": [], + "source": [ + "output = {}\n", + "for lbl, temperature_profile in temperature_profiles.items():\n", + " output[lbl] = {}\n", + " for singular_flag in (True, False):\n", + " output[lbl][singular_flag] = []\n", + " for seed in range(ensemble_n):\n", + " particulator = make_particulator(\n", + " constants=constants,\n", + " n_sd=n_sd, \n", + " dt=temperature_profile.x[-1]/n_steps,\n", + " initial_temperature = temperature_profile(0),\n", + " singular=singular_flag,\n", + " seed=seed,\n", + " shima_T_fz=formulae.freezing_temperature_spectrum.__name__,\n", + " ABIFM_spec=A_spec,\n", + " droplet_volume=droplet_volume,\n", + " total_particle_number=total_particle_number,\n", + " volume=volume,\n", + " thaw=True\n", + " )\n", + " output[lbl][singular_flag].append({\n", + " **run_simulation(particulator, temperature_profile, n_steps)\n", + " })" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "e081ff9b", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:26:19.027488Z", + "start_time": "2025-07-02T15:26:19.023064Z" + } + }, + "outputs": [], + "source": [ + "def setup_subplots():\n", + " fig, axes = pyplot.subplots(\n", + " len(temperature_profiles)//2, 2,\n", + " figsize=(12,9),\n", + " sharex=True,\n", + " tight_layout=True\n", + " )\n", + " for axss in axes:\n", + " for ax in axss:\n", + " ax.set_xticks((0, 15, 30, 45, T_end_min))\n", + " ax.grid()\n", + " for ax in (axes[-1][0], axes[-1][1]):\n", + " ax.set_xlabel(\"time [min]\") \n", + " return fig, axes" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "32fa0d42", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:26:19.090489Z", + "start_time": "2025-07-02T15:26:19.083664Z" + } + }, + "outputs": [], + "source": [ + "def plot_ff(axes, data, singular, label):\n", + " axes.grid()\n", + " axes.set_ylabel(\" frozen fraction [1]\", loc='bottom')\n", + " axes.set_ylim(-.05,1.5)\n", + " axes.set_yticks((0, .25, .5, .75, 1))\n", + " axes.plot(\n", + " np.asarray(data[\"products\"][\"t\"]) / si.min,\n", + " [ff.qi2ff(qi) for qi in data[\"products\"][\"qi\"]],\n", + " label=\"\" if not label else \"singular\" if singular else \"time-dependent\",\n", + " color='black' if singular else 'teal',\n", + " marker=\".\",\n", + " linewidth=.333,\n", + " markersize=1.5\n", + " )\n", + "\n", + "def plot_T(axes, data, label=\"\"):\n", + " twin = axes.twinx()\n", + " twin.set_ylabel(\"T [K] \", color='red', loc='top')\n", + " ticks = (240, 260, 280)\n", + " twin.set_yticks(ticks=ticks, labels=[str(t) for t in ticks], color='red')\n", + " twin.set_ylim((70, 290))\n", + " twin.plot(\n", + " np.asarray(data[\"products\"][\"t\"]) / si.min,\n", + " data[\"products\"][\"T\"],\n", + " color='red',\n", + " linewidth=2,\n", + " label=label\n", + " )\n", + " twin.grid()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "ba45cb59", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:26:22.620822Z", + "start_time": "2025-07-02T15:26:19.147415Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-02T17:26:22.369255\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "10e7e781506a4eed9d88e8b457bb90bf", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_thought_experiments.p…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "_, axs = setup_subplots()\n", + "\n", + "for i, lbl in enumerate(temperature_profiles.keys()):\n", + " rr, cc = i // 2, i % 2\n", + " for singular_flag in (True, False):\n", + " for r in range(len(output[lbl][singular_flag])):\n", + " plot_ff(axs[rr, cc], output[lbl][singular_flag][r], singular_flag, label=r==0)\n", + " \n", + " plot_T(axs[rr, cc], output[lbl][singular_flag][0])\n", + " axs[rr, cc].text(\n", + " -.12, .9,\n", + " '('+lbl+')',\n", + " transform=axs[rr, cc].transAxes,\n", + " size=15,\n", + " weight='bold'\n", + " )\n", + " axs[rr, cc].legend(loc='center left')\n", + "\n", + "show_plot('fig_thought_experiments.pdf')" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "3cbb3fd1", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:26:26.423776Z", + "start_time": "2025-07-02T15:26:22.681691Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-02T17:26:26.257175\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "6dc81d61b959453fa6d5ad3d41205f26", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_realisations.pdf
\"), …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "focus_realisation = 0\n", + "_, axs = pyplot.subplots(\n", + " 3, 1,\n", + " figsize=(12, 12),\n", + " sharex=True,\n", + " tight_layout=True,\n", + ")\n", + "\n", + "_SIN = 2\n", + "_CNT = 1\n", + "_TFF = 0\n", + "\n", + "axs[_SIN].set_xlim(0, 70)\n", + "axs[_SIN].set_xticks(np.linspace(0, T_end_min, 13, endpoint=True))\n", + "axs[_SIN].set_xlabel(\"time [min]\")\n", + "any_bool_value = True\n", + "time = np.asarray(output['f'][any_bool_value][0][\"products\"][\"t\"]) / si.min\n", + "dt = time[1] - time[0]\n", + "\n", + "axs[_SIN].set_ylim(234, 247)\n", + "axs[_SIN].plot(\n", + " time+dt/2,\n", + " output['f'][True][0][\"products\"][\"T\"],\n", + " color='red',\n", + "# linestyle='-',\n", + "# where='pre'\n", + ")\n", + "axs[_SIN].set_ylabel(\"attr: freezing temp. [K]\")\n", + "\n", + "axs[_CNT].set_ylabel(\"attr: immersed surf. [μm$^2$]\")\n", + "axs[_CNT].set_ylim(1, 3)\n", + "\n", + "mlt = (\n", + " axs[_SIN].twiny(),\n", + " axs[_CNT].twiny()\n", + ")\n", + "for axis in mlt:\n", + " axis.set_xlim(-21, 3.5)\n", + "mlt[0].set_xticks([])\n", + "mlt[1].set_xticks((1, 2, 3))\n", + "mlt[1].set_xlabel(\" \" * 210 + \"realization\")\n", + "\n", + "for axis in axs:\n", + " axis.axvline(x=T_end_min, color='black', linewidth=.75)\n", + "\n", + "for singular_flag in (True, False):\n", + " col = 0 if singular_flag else 1\n", + " color = 'black' if singular_flag else 'teal'\n", + " for r in range(len(output['f'][singular_flag])):\n", + " datum = output['f'][singular_flag][r]\n", + " frozen = np.asarray(datum[\"frozen\"]).T\n", + " X = (\n", + " datum['spectrum'][\"freezing temperature\"] \n", + " if singular_flag else\n", + " datum['spectrum'][\"immersed surface area\"] / si.um**2\n", + " )\n", + " \n", + " mlt[col].scatter(\n", + " np.full(n_sd, r+1),\n", + " X,\n", + " color=color,\n", + " s=5.5,\n", + " marker=markers.TICKLEFT\n", + " ) \n", + "\n", + " if r != focus_realisation:\n", + " continue\n", + " \n", + " mlt[col].hlines(\n", + " X,\n", + " xmin=0,\n", + " xmax=np.full(n_sd, r+1),\n", + " color=color,\n", + " linewidth=.5,\n", + " )\n", + "\n", + " for i in range(n_sd):\n", + " Xi = np.repeat(X[i], len(time))\n", + " for frz in (True, False):\n", + " axs[_SIN if singular_flag else _CNT].plot(\n", + " time,\n", + " np.where(frozen[i, :], Xi, np.nan) if frz else Xi,\n", + " color=color,\n", + " linewidth=1.5 if frz else .5\n", + " )\n", + " \n", + "def add_marginal(axes, spec, unit, color_arg):\n", + " _y = np.linspace(*axes.get_ylim())\n", + " _x = spec(_y * unit)\n", + " axes.plot(\n", + " T_end_min + _x * .95 * (axes.get_xlim()[1] - T_end_min) / max(_x),\n", + " _y,\n", + " color=color_arg,\n", + " linewidth=1.5,\n", + " linestyle='--'\n", + " )\n", + "\n", + "def T_marginal(_):\n", + " T_space = np.linspace(*axs[_SIN].get_ylim())\n", + " A_space = np.linspace(*axs[_CNT].get_ylim()) * si.um**2\n", + " grid = np.meshgrid(A_space, T_space)\n", + " sampled_pdf = formulae.freezing_temperature_spectrum.pdf(grid[1], grid[0]) * A_spec.pdf(grid[0])\n", + " res = np.sum(sampled_pdf, axis=1)\n", + " return res\n", + " \n", + "add_marginal(axs[_CNT], A_spec.pdf, si.um**2, 'teal')\n", + "add_marginal(axs[_SIN], T_marginal, si.K, 'black')\n", + " \n", + "plot_ff(axs[_TFF], output['f'][True][focus_realisation], singular=True, label=True)\n", + "plot_ff(axs[_TFF], output['f'][False][focus_realisation], singular=False, label=True)\n", + "plot_T(axs[_TFF], datum)\n", + "axs[_TFF].grid(False)\n", + "\n", + "handles, labels = axs[_TFF].get_legend_handles_labels()\n", + "handles.insert(0, patches.Patch(color='red', label='temperature', linewidth=.1)) \n", + "axs[_TFF].legend(handles=handles, loc='lower right')\n", + "axs[_TFF].set_ylim(-0.025, .725)\n", + "\n", + "for lbl, xy in {\n", + " 'each line: one super-particle (thin: liquid, thick: frozen)': (31, 2.15),\n", + " 'dashed: sampled spectrum': (63.5, 2.42),\n", + "}.items():\n", + " axs[_CNT].annotate(\n", + " lbl,\n", + " xy=xy, xycoords='data',\n", + " xytext=(-170, +90), textcoords='offset points',\n", + " color='gray',\n", + " arrowprops={\n", + " \"arrowstyle\": \"->\",\n", + " \"color\": 'gray',\n", + " \"connectionstyle\": \"arc,angleA=0,armA=40,angleB=+90,armB=15,rad=7\"\n", + " },\n", + " )\n", + "\n", + "axs[_CNT].annotate(\n", + " 'random sampling of freezing: each particle freezes (or not) at different temperature in each cycle',\n", + " xy=(0.43, -0.1), xytext=(0.43, -0.3),\n", + " fontsize=10, ha='center', va='bottom', xycoords='axes fraction', color='gray',\n", + " arrowprops={'arrowstyle': '-[, widthB=30, lengthB=.5', 'lw': 2.0, 'color': 'gray'}\n", + ")\n", + "\n", + "axs[_SIN].annotate(\n", + " 'deterministic freezing: each particle freezes at its T$_{f}$ in each cycle',\n", + " xy=(0.43, 1.1), xytext=(0.43, 1.25),\n", + " fontsize=10, ha='center', va='bottom', xycoords='axes fraction', color='gray',\n", + " arrowprops={'arrowstyle': '-[, widthB=30, lengthB=.5', 'lw': 2.0, 'color': 'gray'}\n", + ")\n", + "\n", + "axs[_CNT].annotate(\n", + " 'attr. random sampling',\n", + " xy=(0.93, -0.1), xytext=(0.93, -0.4),\n", + " fontsize=10, ha='center', va='bottom', xycoords='axes fraction', color='gray',\n", + " arrowprops={'arrowstyle': '-[, widthB=5, lengthB=.5', 'lw': 2.0, 'color': 'gray'}\n", + ")\n", + "\n", + "axs[_SIN].annotate(\n", + " '', xy=(0.93, 1.1), xytext=(0.93, 1.35),\n", + " fontsize=10, ha='center', va='bottom', xycoords='axes fraction', color='gray',\n", + " arrowprops={'arrowstyle': '-[, widthB=5, lengthB=.5', 'lw': 2.0, 'color': 'gray'}\n", + ")\n", + "\n", + "axs[_TFF].annotate(\n", + " 'singular: identical frozen-fraction pattern in each cycle; time-dependent: different frozen-fraction patterns in each cycle',\n", + " xy=(0.43, -0.1), xytext=(0.43, -0.35),\n", + " fontsize=10, ha='center', va='bottom', xycoords='axes fraction', color='gray',\n", + " arrowprops={'arrowstyle': '-[, widthB=30, lengthB=.5', 'lw': 2.0, 'color': 'gray'}\n", + ")\n", + "\n", + "pyplot.subplots_adjust(top=.66, bottom=0, left=0, right=1)\n", + "show_plot('fig_realisations.pdf')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ee6b9988-5f3a-4706-808a-c27204dac786", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:26:26.458745Z", + "start_time": "2025-07-02T15:26:26.457033Z" + } + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "58576a73-a1f5-4a93-b92d-38a3f524f6ad", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-02T15:26:26.515813Z", + "start_time": "2025-07-02T15:26:26.511536Z" + } + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/frozen_fraction.py b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/frozen_fraction.py new file mode 100644 index 0000000000000000000000000000000000000000..e1abf32b4cbc550cd254bded14fb55ea4201af27 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/frozen_fraction.py @@ -0,0 +1,18 @@ +class FrozenFraction: + def __init__(self, *, volume, droplet_volume, total_particle_number, rho_w): + self.volume = volume + self.rho_w = rho_w + self.droplet_volume = droplet_volume + self.total_particle_number = total_particle_number + + def qi2ff(self, ice_mass_per_volume): + ice_mass = ice_mass_per_volume * self.volume + ice_number = ice_mass / (self.rho_w * self.droplet_volume) + frozen_fraction = ice_number / self.total_particle_number + return frozen_fraction + + def ff2qi(self, frozen_fraction): + ice_number = frozen_fraction * self.total_particle_number + ice_mass = ice_number * (self.rho_w * self.droplet_volume) + ice_mass_per_volume = ice_mass / self.volume + return ice_mass_per_volume diff --git a/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/make_particulator.py b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/make_particulator.py new file mode 100644 index 0000000000000000000000000000000000000000..9d223a6931b1071420a5b9966027b3d5dcf10ea4 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/make_particulator.py @@ -0,0 +1,87 @@ +import numpy as np + +from PySDM import Builder, Formulae +from PySDM import products as PySDM_products +from PySDM.backends import CPU +from PySDM.dynamics import Freezing +from PySDM.environments import Box +from PySDM.initialisation.sampling.spectro_glacial_sampling import ( + SpectroGlacialSampling, +) + +A_VALUE_LARGER_THAN_ONE = 44 + + +def make_particulator( + *, + constants, + n_sd, + dt, + initial_temperature, + singular, + seed, + shima_T_fz, + ABIFM_spec, + droplet_volume, + total_particle_number, + volume, + thaw=False, +): + formulae_ctor_args = { + "seed": seed, + "constants": constants, + "freezing_temperature_spectrum": shima_T_fz, + "heterogeneous_ice_nucleation_rate": "ABIFM", + "particle_shape_and_density": "MixedPhaseSpheres", + } + formulae = Formulae(**formulae_ctor_args) + backend = CPU(formulae, override_jit_flags={"parallel": False}) + + attributes = { + "signed water mass": np.ones(n_sd) * droplet_volume * formulae.constants.rho_w + } + + sampling = SpectroGlacialSampling( + freezing_temperature_spectrum=formulae.freezing_temperature_spectrum, + insoluble_surface_spectrum=ABIFM_spec, + ) + if singular: + ( + attributes["freezing temperature"], + _, + attributes["multiplicity"], + ) = sampling.sample(backend=backend, n_sd=n_sd) + immersion_freezing_flag = "singular" + else: + ( + _, + attributes["immersed surface area"], + attributes["multiplicity"], + ) = sampling.sample(backend=backend, n_sd=n_sd) + immersion_freezing_flag = "time-dependent" + if thaw: + thaw_flag = "instantaneous" + else: + thaw_flag = None + attributes["multiplicity"] *= total_particle_number + + builder = Builder(n_sd=n_sd, backend=backend, environment=Box(dt, volume)) + + env = builder.particulator.environment + env["T"] = initial_temperature + env["RH"] = A_VALUE_LARGER_THAN_ONE + env["rhod"] = 1.0 + + builder.add_dynamic( + Freezing(immersion_freezing=immersion_freezing_flag, thaw=thaw_flag) + ) + builder.request_attribute("volume") + + return builder.build( + attributes=attributes, + products=( + PySDM_products.Time(name="t"), + PySDM_products.AmbientTemperature(name="T"), + PySDM_products.SpecificIceWaterContent(name="qi"), + ), + ) diff --git a/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/plots.py b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/plots.py new file mode 100644 index 0000000000000000000000000000000000000000..3921c39b2287570d073584b976a3eafec362b0c4 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/plots.py @@ -0,0 +1,160 @@ +import matplotlib +import numpy as np +from matplotlib import pyplot +from PySDM_examples.Arabas_et_al_2025.curved_text import CurvedText +from PySDM_examples.Arabas_et_al_2025.frozen_fraction import FrozenFraction + +from PySDM.physics import si + +labels = {True: "singular/INAS", False: "time-dependent/ABIFM"} +colors = {True: "black", False: "teal"} +qi_unit = si.g / si.m**3 + + +def make_temperature_plot(data): + pyplot.xlabel("time [s]") + + xy1 = pyplot.gca() + + xy1.set_ylabel("temperature [K]") + xy1.set_ylim(200, 300) + datum = data[0]["products"] + xy1.plot(datum["t"], datum["T"], marker=".", label="T", color="black") + + xy2 = xy1.twinx() + plotted = {singular: False for singular in (True, False)} + for v in data: + datum = v["products"] + xy2.plot( + datum["t"], + np.asarray(datum["qi"]) / qi_unit, # marker='.', + label=( + f"Monte-Carlo ({labels[v['singular']]})" + if not plotted[v["singular"]] + else "" + ), + color=colors[v["singular"]], + ) + plotted[v["singular"]] = True + xy2.set_ylabel("ice water content [g/m3]") + + xy1.grid() + xy1.legend() # bbox_to_anchor=(.2, 1.15)) + xy2.legend() # bbox_to_anchor=(.7, 1.15)) + + +def make_freezing_spec_plot( + data, + formulae, + volume, + droplet_volume, + total_particle_number, + surf_spec, + cooling_rate_K_min=None, +): + pyplot.xlabel("temperature [K]") + plotted = {singular: False for singular in (True, False)} + + prim = pyplot.gca() + for v in data: + datum = v["products"] + color = colors[v["singular"]] + prim.plot( + datum["T"], + np.asarray(datum["qi"]) / qi_unit, + marker=".", + linewidth=0.333, + label=f"{labels[v['singular']]}" if not plotted[v["singular"]] else "", + color=color, + ) + plotted[v["singular"]] = True + + ff = FrozenFraction( + volume=volume, + droplet_volume=droplet_volume, + total_particle_number=total_particle_number, + rho_w=formulae.constants.rho_w, + ) + twin = prim.secondary_yaxis( + "right", + functions=(lambda x: ff.qi2ff(x * qi_unit), lambda x: ff.ff2qi(x) / qi_unit), + ) + twin.set_ylabel("frozen fraction") + + T = np.linspace(max(datum["T"]), min(datum["T"])) + for multiplier, color in {0.1: "yellow", 1: "brown", 10: "orange"}.items(): + qi = ( + ff.ff2qi( + formulae.freezing_temperature_spectrum.cdf( + T, multiplier * surf_spec.median + ) + ) + / qi_unit + ) + prim.plot( + T, + qi, + label="" if multiplier != 1 else "singular CDFs for median surface", + linewidth=2.5, + color=color, + linestyle="--", + ) + if multiplier != 1: + _ = CurvedText( + x=T.squeeze(), + y=qi.squeeze(), + text=f" {multiplier}x median S", + va="bottom", + color="black", + axes=prim, + ) + title = f"$σ_g$=exp({np.log(surf_spec.s_geom):.3g})" + if cooling_rate_K_min is not None: + title += f", c={cooling_rate_K_min} K/min" + prim.set_title(title) + # prim.set_ylabel('ice water content [$g/m^3$]') + prim.set_yticks([]) + prim.set_xlim(T[0], T[-1]) + prim.legend(bbox_to_anchor=(1.02, -0.2)) + prim.grid() + + +def make_pdf_plot(A_spec, Shima_T_fz, A_range, T_range): + N = 256 + T_space = np.linspace(*T_range, N) + A_space = np.linspace(*A_range, N) + grid = np.meshgrid(A_space, T_space) + sampled_pdf = Shima_T_fz(grid[1], grid[0]) * A_spec.pdf(grid[0]) + + fig = pyplot.figure( + figsize=(4.5, 6), + ) + ax = fig.add_subplot(111) + ax.set_ylabel("freezing temperature [K]") + ax.set_yticks(np.linspace(*T_range, num=5, endpoint=True)) + ax.set_xlabel("insoluble surface [μm$^2$]") + + data = sampled_pdf * si.um**2 + data[data == 0] = np.nan + cnt = ax.contourf( + grid[0] / si.um**2, + grid[1], + data, + norm=matplotlib.colors.LogNorm(), + cmap="Blues", + levels=np.logspace(-3, 0, 7), + ) + cbar = pyplot.colorbar(cnt, ticks=[0.001, 0.01, 0.1, 1.0], orientation="horizontal") + cbar.set_label("pdf [K$^{-1}$ μm$^{-2}$]") + ax.set_title(f"$σ_g$=exp({np.log(A_spec.s_geom):.3g})") + + ax_histx = ax.inset_axes([0, 1.05, 1, 0.25], sharex=ax) + ax_histy = ax.inset_axes([1.05, 0, 0.25, 1], sharey=ax) + ax_histx.tick_params(axis="x", labelbottom=False, bottom=False) + ax_histx.tick_params(axis="y", labelleft=False, left=False) + ax_histy.tick_params(axis="y", labelleft=False, left=False) + ax_histy.tick_params(axis="x", labelbottom=False, bottom=False) + ax_histx.plot(A_space / si.um**2, np.sum(sampled_pdf, axis=0), color="teal") + ax_histy.plot(np.sum(sampled_pdf, axis=1), T_space, color="black") + + pyplot.grid() diff --git a/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/run_simulation.py b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/run_simulation.py new file mode 100644 index 0000000000000000000000000000000000000000..ce047c3be1f595d870ea48675b47f6a0829104d0 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Arabas_et_al_2025/run_simulation.py @@ -0,0 +1,33 @@ +import numpy as np + + +def update_thermo(particulator, T): + env = particulator.environment + svp = particulator.formulae.saturation_vapour_pressure + + env["T"] = T + env["a_w_ice"] = svp.pvs_ice(T) / svp.pvs_water(T) + + +def run_simulation(particulator, temperature_profile, n_steps): + output = { + "products": {k: [] for k in particulator.products.keys()}, + "attributes": [], + } + for step in range(n_steps + 1): + if step != 0: + update_thermo( + particulator, T=temperature_profile((step - 0.5) * particulator.dt) + ) + particulator.run(step - particulator.n_steps) + update_thermo(particulator, T=temperature_profile(step * particulator.dt)) + output["frozen"].append(particulator.attributes["volume"].to_ndarray() < 0) + else: + output["spectrum"] = {} + output["frozen"] = [np.full(particulator.n_sd, False)] + for k in ("multiplicity", "freezing temperature", "immersed surface area"): + if k in particulator.attributes: + output["spectrum"][k] = particulator.attributes[k].to_ndarray() + for k, v in particulator.products.items(): + output["products"][k].append(v.get() + 0) + return output diff --git a/PySDM/source/examples/PySDM_examples/Bartman_2020_MasterThesis/__init__.py b/PySDM/source/examples/PySDM_examples/Bartman_2020_MasterThesis/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..fc9afa58f751c895c4186ca9de6465d7cbde7878 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Bartman_2020_MasterThesis/__init__.py @@ -0,0 +1,4 @@ +# pylint: disable=invalid-name +""" +Box and parcel examples from [Bartman 2020 MSc thesis](https://www.ap.uj.edu.pl/diplomas/141204) +""" diff --git a/PySDM/source/examples/PySDM_examples/Bartman_2020_MasterThesis/fig_4_adaptive_sdm.py b/PySDM/source/examples/PySDM_examples/Bartman_2020_MasterThesis/fig_4_adaptive_sdm.py new file mode 100644 index 0000000000000000000000000000000000000000..e0673911d103d66bae542f6ede1903b71f8cfb5a --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Bartman_2020_MasterThesis/fig_4_adaptive_sdm.py @@ -0,0 +1,71 @@ +import os + +from matplotlib import pyplot as plt +from PySDM_examples.Shima_et_al_2009.example import run +from PySDM_examples.Shima_et_al_2009.settings import Settings +from PySDM_examples.Shima_et_al_2009.spectrum_plotter import SpectrumPlotter + + +def main(plot: bool = True, save: str = None): + n_sds = [13, 15, 17] if "CI" not in os.environ else [13, 15] + dts = [10, 5, 1, "adaptive"] + iters = 10 + base_time = None + + plt.ioff() + fig, axs = plt.subplots( + len(dts), len(n_sds), sharex=True, sharey=True, figsize=(10, 10) + ) + + for i, dt in enumerate(dts): + for j, n_sd in enumerate(n_sds): + outputs = [] + exec_time = 0 + for _ in range(iters): + settings = Settings() + + settings.n_sd = 2**n_sd + settings.dt = dt if dt != "adaptive" else 10 + settings.adaptive = dt == "adaptive" + + states, exec_time = run(settings) + outputs.append(states) + mean_time = exec_time / iters + if base_time is None: + base_time = mean_time + norm_time = mean_time / base_time + mean_output = {} + for key in outputs[0].keys(): + mean_output[key] = sum((output[key] for output in outputs)) / len( + outputs + ) + + plotter = SpectrumPlotter(settings, legend=False) + plotter.fig = fig + plotter.ax = axs[i, j] + plotter.smooth = True + for step, vals in mean_output.items(): + plotter.plot(vals, step * settings.dt) + + plotter.ylabel = ( + r"$\bf{dt: " + str(dt) + "}$\ndm/dlnr [g/m^3/(unit dr/r)]" + if j == 0 + else None + ) + plotter.xlabel = ( + "particle radius [µm]\n" + r"$\bf{n_{sd}: 2^{" + str(n_sd) + "}}$" + if i == len(dts) - 1 + else None + ) + plotter.title = f"norm. time: {norm_time:.2f}; " + plotter.title + plotter.finished = False + plotter.finish() + if save is not None: + n_sd = settings.n_sd + plotter.save(save + "/" + f"{n_sd}_shima_fig_2" + "." + plotter.format) + if plot: + plotter.show() + + +if __name__ == "__main__": + main(plot="CI" not in os.environ, save=".") diff --git a/PySDM/source/examples/PySDM_examples/Bartman_2020_MasterThesis/fig_5_SCIPY_VS_ADAPTIVE.py b/PySDM/source/examples/PySDM_examples/Bartman_2020_MasterThesis/fig_5_SCIPY_VS_ADAPTIVE.py new file mode 100644 index 0000000000000000000000000000000000000000..1f4a69553053b5cbf3f04e03f75da2029043bec2 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Bartman_2020_MasterThesis/fig_5_SCIPY_VS_ADAPTIVE.py @@ -0,0 +1,117 @@ +import os + +import matplotlib +import matplotlib.pyplot as plt +import numpy as np +from matplotlib.collections import LineCollection +from PySDM_examples.Arabas_and_Shima_2017.settings import setups +from PySDM_examples.Arabas_and_Shima_2017.simulation import Simulation + +from PySDM.backends import CPU, GPU +from PySDM.backends.impl_numba.test_helpers import scipy_ode_condensation_solver + + +def data(n_output, rtols, schemes, setups_num): + resultant_data = {} + for scheme in schemes: + resultant_data[scheme] = {} + if scheme == "SciPy": + for rtol in rtols: + resultant_data[scheme][rtol] = [] + for settings_idx in range(setups_num): + settings = setups[settings_idx] + settings.n_output = n_output + simulation = Simulation(settings) + scipy_ode_condensation_solver.patch_particulator( + simulation.particulator + ) + results = simulation.run() + for rtol in rtols: + resultant_data[scheme][rtol].append(results) + else: + for rtol in rtols: + resultant_data[scheme][rtol] = [] + for settings_idx in range(setups_num): + settings = setups[settings_idx] + settings.rtol_x = rtol + settings.rtol_thd = rtol + settings.n_output = n_output + simulation = Simulation( + settings, backend=CPU if scheme == "CPU" else GPU + ) + results = simulation.run() + resultant_data[scheme][rtol].append(results) + return resultant_data + + +def add_color_line(fig, ax, x, y, z): + points = np.array([x, y]).T.reshape(-1, 1, 2) + segments = np.concatenate([points[:-1], points[1:]], axis=1) + z = np.array(z) + vmin = min(np.nanmin(z), np.nanmax(z) / 2) + lc = LineCollection( + segments, + cmap=plt.get_cmap("plasma"), + norm=matplotlib.colors.LogNorm(vmax=1, vmin=vmin), + ) + lc.set_array(z) + lc.set_linewidth(3) + + ax.add_collection(lc) + fig.colorbar(lc, ax=ax) + + +def plot(plot_data, rtols, schemes, setups_num, show_plot, path=None): + _rtol = "$r_{tol}$" + + plt.ioff() + fig, axs = plt.subplots( + setups_num, len(rtols), sharex=True, sharey=True, figsize=(10, 13) + ) + for settings_idx in range(setups_num): + SCIPY_S = None + PySDM_S = None + for rtol_idx, _rtol in enumerate(rtols): + ax = axs[settings_idx, rtol_idx] + for scheme in schemes: + datum = plot_data[scheme][_rtol][settings_idx] + S = datum["RH"] + z = datum["z"] + dt = datum["dt_cond_min"] + if scheme == "SciPy": + SCIPY_S = np.array(S) + ax.plot(SCIPY_S - 1, z, label=scheme, color="grey") + else: + add_color_line(fig, ax, S, z, dt) + PySDM_S = np.array(S) + if SCIPY_S is not None and PySDM_S is not None: + mae = np.mean(np.abs(SCIPY_S - PySDM_S)) + ax.set_title(f"MAE: {mae:.4E}") + ax.set_xlim(-7.5e-3, 7.5e-3) + ax.set_ylim(0, 180) + ax.get_xaxis().set_minor_locator(matplotlib.ticker.AutoMinorLocator()) + ax.grid() + plt.setp(ax.get_xticklabels(), rotation=30, horizontalalignment="right") + for i, ax in enumerate(axs[:, 0]): + ax.set(ylabel=r"$\bf{settings: " + str(i) + "}$\ndisplacement [m]") + for i, ax in enumerate(axs[-1, :]): + ax.set(xlabel="supersaturation\n" + r"$\bf{r_{tol}: " + str(rtols[i]) + "}$") + + plt.tight_layout() + + if path is not None: + plt.savefig(path + ".pdf", format="pdf") + if show_plot: + plt.show() + + +def main(save=None, show_plot=True): + rtols = [1e-7, 1e-11] + schemes = ["CPU", "SciPy"] + setups_num = len(setups) + input_data = data(80 if "CI" not in os.environ else 20, rtols, schemes, setups_num) + plot(input_data, rtols, schemes, setups_num, show_plot, save) + + +if __name__ == "__main__": + main("SCIPY_VS_ADAPTIVE", show_plot="CI" not in os.environ) diff --git a/PySDM/source/examples/PySDM_examples/Bartman_et_al_2021/__init__.py b/PySDM/source/examples/PySDM_examples/Bartman_et_al_2021/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..02ace29a8928d2dc18962e35c0a74ffb6c8963ba --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Bartman_et_al_2021/__init__.py @@ -0,0 +1,13 @@ +# pylint: disable=invalid-name +""" +condensation and coalescence adaptivity examples + +demo.ipynb: +.. include:: ./demo.ipynb.badges.md + +demo_fig2.ipynb: +.. include:: ./demo_fig2.ipynb.badges.md + +demo_fig3.ipynb: +.. include:: ./demo_fig3.ipynb.badges.md +""" diff --git a/PySDM/source/examples/PySDM_examples/Bartman_et_al_2021/demo.ipynb b/PySDM/source/examples/PySDM_examples/Bartman_et_al_2021/demo.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..3db90852d0fda6a18a5760b18e51ef0d7d1ed1bb --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Bartman_et_al_2021/demo.ipynb @@ -0,0 +1,329 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "collapsed": false + }, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Bartman_et_al_2021/demo.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Bartman_et_al_2021/demo.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Bartman_et_al_2021/demo.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# TODO #1417" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-15T19:13:04.632105Z", + "start_time": "2024-12-15T19:13:04.627358Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-15T19:13:07.790075Z", + "start_time": "2024-12-15T19:13:04.635825Z" + } + }, + "outputs": [], + "source": [ + "from PySDM_examples.Arabas_et_al_2015 import Settings, SpinUp\n", + "from PySDM_examples.utils.kinematic_2d import Simulation, Storage\n", + "from PySDM_examples.utils.kinematic_2d.plots import _TimeseriesPlot, _ImagePlot\n", + "from PySDM_examples.utils import ProgBarController\n", + "from PySDM_examples.utils.widgets import HTML, display\n", + "import PySDM.products as PySDM_products\n", + "from PySDM.exporters import NetCDFExporter\n", + "from open_atmos_jupyter_utils import TemporaryFile\n", + "from PySDM.physics import si\n", + "import numpy as np\n", + "from scipy.io import netcdf_file\n", + "from matplotlib import pyplot, rcParams\n", + "from matplotlib.animation import FuncAnimation\n", + "from matplotlib.gridspec import GridSpec" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-15T19:13:14.392062Z", + "start_time": "2024-12-15T19:13:07.883682Z" + } + }, + "outputs": [], + "source": [ + "settings = Settings()\n", + "\n", + "cloud_range = (settings.aerosol_radius_threshold, settings.drizzle_radius_threshold)\n", + "products = [\n", + " PySDM_products.ParticleSpecificConcentration(name='n_a_mg', unit='mg^-1', radius_range=(0, cloud_range[0])),\n", + " PySDM_products.EffectiveRadius(name='r_eff', unit='um', radius_range=(cloud_range[0], np.inf)),\n", + " PySDM_products.ParticleConcentration(name='n_d_cm3', unit='cm^-3', radius_range=(cloud_range[1], np.inf)),\n", + " PySDM_products.WaterMixingRatio(name='qt', unit='g/kg', radius_range=cloud_range),\n", + " PySDM_products.CondensationTimestepMin(name='dt_cond_min'),\n", + " PySDM_products.CollisionTimestepMin(name='dt_coal_min')\n", + "]\n", + "\n", + "settings.n_sd_per_gridbox = 128 if 'CI' not in os.environ else 32\n", + "settings.grid = (32, 32)\n", + "settings.dt = 32 * si.second\n", + "settings.simulation_time = .175 * settings.spin_up_time\n", + "settings.output_interval = 1 * si.minute\n", + "settings.condensation_rtol_x = 1e-6\n", + "settings.condensation_rtol_thd = 5e-7\n", + "\n", + "settings.condensation_dt_cond_range = (.25*si.s, settings.dt)\n", + "settings.coalescence_dt_coal_range = settings.condensation_dt_cond_range\n", + "\n", + "settings.mode_1.norm_factor *= 3\n", + "settings.mode_2.norm_factor *= 3\n", + "settings.spectrum_per_mass_of_dry_air.norm_factor *= 3\n", + "\n", + "storage = Storage()\n", + "simulation = Simulation(settings, storage, SpinUp=SpinUp)\n", + "simulation.reinit(products)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-15T19:14:06.565287Z", + "start_time": "2024-12-15T19:13:14.401584Z" + } + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "5b82aa86d4ee411cba73d97e161fc36e", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "simulation.run(ProgBarController())" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-15T19:14:06.632334Z", + "start_time": "2024-12-15T19:14:06.604935Z" + } + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "51a4d97c7d5a4dcf887bc35713fead6b", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "temp_file = TemporaryFile('.nc')\n", + "exporter = NetCDFExporter(storage, settings, simulation, temp_file.absolute_path)\n", + "exporter.run(ProgBarController())" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-15T19:14:06.862492Z", + "start_time": "2024-12-15T19:14:06.678764Z" + } + }, + "outputs": [], + "source": [ + "default_figsize = rcParams[\"figure.figsize\"]\n", + "figsize = (1.75 * default_figsize[0], 3.1* default_figsize[1])\n", + "gs = GridSpec(nrows=27, ncols=18)\n", + "\n", + "fig = pyplot.figure(figsize=figsize)\n", + "na = gs.nrows-19\n", + "nb = gs.nrows-11\n", + "nc = gs.nrows-3\n", + "axs = (\n", + " fig.add_subplot(gs[:na, 1:gs.ncols//2-1]),\n", + " fig.add_subplot(gs[:na, gs.ncols//2+1:2*gs.ncols//2-1]),\n", + "\n", + " fig.add_subplot(gs[na:nb, 1:gs.ncols//2-1]),\n", + " fig.add_subplot(gs[na:nb, gs.ncols//2+1:2*gs.ncols//2-1]),\n", + " \n", + " fig.add_subplot(gs[nb:nc, 1:gs.ncols//2-1]),\n", + " fig.add_subplot(gs[nb:nc, gs.ncols//2+1:2*gs.ncols//2-1]),\n", + " \n", + " fig.add_subplot(gs[gs.nrows-3:-1,2:-1])\n", + ")\n", + "gs.tight_layout(fig)\n", + "ncdf = netcdf_file(temp_file.absolute_path, mode='r', mmap=False)\n", + "\n", + "# TODO #419: we should not use products here - all info should be obtained from netCDF\n", + "plots = []\n", + "for var, cmap in {\n", + " 'n_a_mg': 'summer',\n", + " 'n_d_cm3': 'bone_r',\n", + " 'r_eff': 'ocean_r',\n", + " 'qt': 'Blues',\n", + " 'dt_cond_min': 'tab20c',\n", + " 'dt_coal_min': 'tab20c'\n", + "}.items():\n", + " plots.append(\n", + " _ImagePlot(fig, axs[len(plots)], grid=ncdf.grid, size=ncdf.size, product=simulation.products[var], cmap=cmap)\n", + " )\n", + "plots.append(_TimeseriesPlot(fig, axs[-1], ncdf.variables['T'][:], show=False))\n", + "\n", + "plots[-1].ax.axvline(ncdf.n_spin_up * ncdf.dt)\n", + "plots[-1].ax.set_ylim(0, .001)\n", + "\n", + "interval = 100 #ms\n", + "frame_list = np.arange(ncdf.variables['T'].shape[0], dtype=int)\n", + "\n", + "def update(frame_num):\n", + " step = frame_num*ncdf.steps_per_output_interval\n", + " \n", + " for i, product in enumerate(('n_a_mg', 'n_d_cm3', 'r_eff', 'qt', 'dt_cond_min', 'dt_coal_min')):\n", + " plots[i].update(ncdf.variables[product][frame_num], step=step)\n", + " \n", + " precip = np.full_like(ncdf.variables['surf_precip'][:], np.nan)\n", + " precip[0:frame_num+1] = ncdf.variables['surf_precip'][0:frame_num+1]\n", + " plots[-1].update(precip)\n", + " \n", + " return (\n", + " plots[0].im, plots[1].im, plots[2].im, \n", + " plots[3].im, plots[4].im, plots[5].im, \n", + " plots[-1].timeseries\n", + " )\n", + "pyplot.close(fig)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-15T19:14:07.140687Z", + "start_time": "2024-12-15T19:14:06.868979Z" + } + }, + "outputs": [], + "source": [ + "animation = FuncAnimation(fig, update, frames=frame_list, interval=interval, blit=False)\n", + "\n", + "if 'CI' not in os.environ:\n", + " display(HTML(animation.to_html5_video()))\n", + " file = TemporaryFile('.gif')\n", + " animation.save(file.absolute_path)\n", + " display(file.make_link_widget())" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-15T21:52:37.549297Z", + "start_time": "2024-12-15T21:52:36.478260Z" + } + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "458642af11f04bf99ba218260443e075", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./tmp72gs0u17.svg
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# save last frame in vector format\n", + "svg_file = TemporaryFile('.svg')\n", + "fig.savefig(svg_file.absolute_path)\n", + "display(svg_file.make_link_widget())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-15T21:52:39.483293Z", + "start_time": "2024-12-15T21:52:39.480037Z" + } + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.8.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/PySDM/source/examples/PySDM_examples/Bartman_et_al_2021/demo_fig2.ipynb b/PySDM/source/examples/PySDM_examples/Bartman_et_al_2021/demo_fig2.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..f5c18789c78d8fec2c36df94788d33c00ad5c2d8 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Bartman_et_al_2021/demo_fig2.ipynb @@ -0,0 +1,12810 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "collapsed": false + }, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Bartman_et_al_2021/demo_fig2.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Bartman_et_al_2021/demo_fig2.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Bartman_et_al_2021/demo_fig2.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# TODO #1417" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-10T18:58:30.038548Z", + "start_time": "2024-12-10T18:58:30.032993Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-10T18:58:32.805460Z", + "start_time": "2024-12-10T18:58:30.055382Z" + } + }, + "outputs": [], + "source": [ + "from PySDM_examples.Arabas_et_al_2015 import Settings, SpinUp\n", + "from PySDM_examples.utils.kinematic_2d import Simulation, Storage\n", + "from PySDM.exporters import NetCDFExporter\n", + "from PySDM_examples.utils import ProgBarController\n", + "from open_atmos_jupyter_utils import show_plot, TemporaryFile\n", + "from PySDM_examples.Bartman_et_al_2021.label import label\n", + "import PySDM.products as PySDM_products\n", + "from PySDM.physics import si\n", + "import matplotlib\n", + "import numpy as np\n", + "import string\n", + "from scipy.io import netcdf_file\n", + "from matplotlib import pyplot\n", + "from scipy.ndimage import uniform_filter1d" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-10T18:58:32.901248Z", + "start_time": "2024-12-10T18:58:32.897660Z" + } + }, + "outputs": [], + "source": [ + "tol = .5e-6\n", + "m = 2\n", + "runs = (\n", + " {'file': TemporaryFile('.nc'), 'settings': {'condensation_adaptive': True, 'condensation_rtol_thd': tol}},\n", + " {'file': TemporaryFile('.nc'), 'settings': {'condensation_adaptive': True, 'condensation_rtol_thd': tol*m}},\n", + " {'file': TemporaryFile('.nc'), 'settings': {'condensation_adaptive': True, 'condensation_rtol_thd': tol*m*m}},\n", + " {'file': TemporaryFile('.nc'), 'settings': {'condensation_adaptive': True, 'condensation_rtol_thd': tol*m*m*m}},\n", + " {'file': TemporaryFile('.nc'), 'settings': {'condensation_adaptive': False, 'condensation_substeps': 128}},\n", + " {'file': TemporaryFile('.nc'), 'settings': {'condensation_adaptive': False, 'condensation_substeps': 32}},\n", + " {'file': TemporaryFile('.nc'), 'settings': {'condensation_adaptive': False, 'condensation_substeps': 8}},\n", + " {'file': TemporaryFile('.nc'), 'settings': {'condensation_adaptive': False, 'condensation_substeps': 2}},\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-10T19:12:55.657266Z", + "start_time": "2024-12-10T18:58:32.919878Z" + } + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "6c5487f9a34a45a8aec2194059c19eac", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='run 1/8', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "40ef21163fe544c4b1f51c0f0a964d21", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='netCDF', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "e512a23ff04f47c1997f729c69dbfb16", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='run 2/8', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "9e7c28e100654f41b9fbb9d5b7d03f96", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='netCDF', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "220c54dcb8a845a79adda1855dcde20c", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='run 3/8', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "56c25c4eabc741c197ccbb8e6db1b47e", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='netCDF', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "349bb3065da24a2c8aa7866520ebaf3a", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='run 4/8', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "762ef5455e534c7290b1a32746860e87", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='netCDF', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "d237414bc00142f1907c6218ef200803", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='run 5/8', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "323b810ff3314088a6f57c5bd2b739c1", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='netCDF', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "a9521d672b3143bb978cf0d73fe87711", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='run 6/8', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "0159a1d100c94befa8b7cb440b3d9eff", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='netCDF', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f3bfcbd89a914e45937b111eda598ee9", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='run 7/8', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f1bb67f37a6a4bde8510f3c8a0ca0fc3", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='netCDF', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "c0cb4ec5def24676acfc413cf4dac346", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='run 8/8', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "fb92d1108d9d421a9aa9401f69a4d8c0", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='netCDF', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "radius_range = (.5*si.um, 25*si.um)\n", + "dt = 32 * si.s\n", + "\n", + "for i, run in enumerate(runs):\n", + " settings = Settings()\n", + "\n", + " products = (\n", + " PySDM_products.PeakSaturation(name='S_max'),\n", + " PySDM_products.DynamicWallTime('Condensation', name='Condensation_wall_time'),\n", + " PySDM_products.ParticleSpecificConcentration(radius_range=(0, radius_range[0]), name='n_a_mg', unit='mg^-1'),\n", + " PySDM_products.EffectiveRadius(radius_range=radius_range, name='r_eff', unit='um'),\n", + " PySDM_products.CondensationTimestepMin(name='dt_cond_min')\n", + " )\n", + "\n", + " settings.n_sd_per_gridbox = 128\n", + " settings.grid = (32, 32)\n", + " settings.dt = dt\n", + " settings.condensation_dt_cond_range = (.125*si.s, settings.dt)\n", + " \n", + " settings.mode_1.norm_factor *= 3\n", + " settings.mode_2.norm_factor *= 3\n", + " settings.spectrum_per_mass_of_dry_air.norm_factor *= 3\n", + " settings.simulation_time = settings.spin_up_time * (1 if 'CI' not in os.environ else .1)\n", + " settings.output_interval = settings.dt\n", + " settings.condensation_rtol_x = 1e-6\n", + " settings.condensation_rtol_thd = -1\n", + " settings.condensation_schedule = 'dynamic'\n", + " settings.kappa = .8\n", + " \n", + " for key, value in run['settings'].items(): \n", + " assert hasattr(settings, key)\n", + " setattr(settings, key, value)\n", + " \n", + " storage = Storage()\n", + " simulation = Simulation(settings, storage, SpinUp=SpinUp)\n", + " simulation.reinit(products)\n", + "\n", + " simulation.run(ProgBarController(f\"run {i+1}/{len(runs)}\"))\n", + " exporter = NetCDFExporter(storage, settings, simulation, run['file'].absolute_path)\n", + " exporter.run(ProgBarController('netCDF'))" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-10T19:12:57.722450Z", + "start_time": "2024-12-10T19:12:55.677044Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-12-10T20:12:57.567055\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.9.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "3989b18c551e4bf4a491c4d595e4bc56", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./tmp1tab8j77.pdf
\"), HTML(value…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "matplotlib.rcParams[\"figure.figsize\"] = (7.3, 18)\n", + "\n", + "colors = (\n", + " '#5940ff', '#5980ff', '#59c0ff', '#59e0ff',\n", + " '#dd0000', '#dd6666', '#dd9999', '#ddcccc'\n", + ")\n", + "skip_steps = 30\n", + "bins = 64\n", + "bin_range = {\n", + " 'S_max': (-.05, .27),\n", + " 'n_a_mg': (0.05, 128.05),\n", + " 'r_eff': (2, 18),\n", + " 'dt_cond_min': (-3.5, 5.5)\n", + "}\n", + "window = 6\n", + "\n", + "fig, axs = pyplot.subplots(len(bin_range),1)\n", + "for plot_i, var in enumerate(bin_range.keys()):\n", + " ax = axs[plot_i]\n", + " for i, run in enumerate(runs):\n", + " nc = netcdf_file(run['file'].absolute_path, mode='r', mmap=False)\n", + " n_spinup = nc.n_spin_up // nc.steps_per_output_interval\n", + " assert skip_steps >= n_spinup // 4\n", + " data = nc.variables[var]\n", + " if var == 'dt_cond_min':\n", + " data = np.log2(data[:])\n", + " if var == 'S_max':\n", + " data = (data[:] - 1) * 100\n", + " timesteps = slice(skip_steps, -1)\n", + " wall_time = np.nanmean(nc.variables['Condensation_wall_time'][timesteps])\n", + " wall_time = np.nan if not np.isfinite(wall_time) else int(100 * wall_time) / 100\n", + " y, x, _ = ax.hist(data[timesteps, :, :].flatten(), \n", + " bins=bins, range=bin_range[var], histtype='step', \n", + " color=colors[i], lw=0)\n", + " if var != 'dt_cond_min':\n", + " filt_x = x[:-1] if window % 2 == 0 else (x[1:] + x[:-1])/2\n", + " ax.plot(filt_x, uniform_filter1d(y, size=window), color=colors[i],\n", + " lw=8 if run['settings']['condensation_adaptive'] else 3,\n", + " ls='--' if not run['settings']['condensation_adaptive'] else '-',\n", + " label=f\"{label(run['settings'])} (time/step: {wall_time:.2f}s)\"\n", + " )\n", + " else:\n", + " dx = x[1] - x[0]\n", + " ax.bar(x[0:-1] - dx/.66 + dx/1.1*min(4,i), y, color=colors[i], width=dx/1.5)\n", + " ax.set_ylabel('occurrence count ' + (f'({window}-bin moving average)' if var != 'dt_cond_min' else ''))\n", + " binwidth = (bin_range[var][1]-bin_range[var][0])/bins\n", + " if var == 'S_max':\n", + " ax.set_ylim(0, 3000)\n", + " ax.set_xlabel(f'peak supersaturation [%] ({binwidth}% binning)')\n", + " elif var == 'n_a_mg':\n", + " ax.set_xlabel(f'interstitial aerosol concentration [1/mg] ({binwidth} binning)')\n", + " elif var == 'r_eff':\n", + " ax.set_xlabel(f'cloud droplet effective radius [μm] ({binwidth} binning)')\n", + " ax.set_ylim(0, 3000)\n", + " elif var == 'dt_cond_min':\n", + " ax.set_xlabel(f'condensation timestep [s] ({dt:.0f} s / substeps)')\n", + " labels = {pw:2**pw for pw in range(-3, 6)}\n", + " ax.set_xticks(tuple(labels.keys()))\n", + " ax.set_xticklabels(tuple(labels.values()))\n", + " ax.set_yscale('log')\n", + " ax.set_ylim(1e2, 1.3e5)\n", + " else:\n", + " assert False\n", + " ax.grid()\n", + " if plot_i == 0:\n", + " ax.legend()\n", + " ax.set_xlim(bin_range[var])\n", + " ax.text(0, 1.03, '('+string.ascii_lowercase[plot_i]+')', transform=ax.transAxes, size=15, weight='bold')\n", + "show_plot()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-10T19:12:57.802091Z", + "start_time": "2024-12-10T19:12:57.796237Z" + } + }, + "outputs": [], + "source": [ + "# TODO #449: updraft vs. downdraft?\n", + "# TODO #449: different n_sd\n", + "# TODO #449: different aerosol\n", + "# TODO #449: schedule\n", + "# TODO #449: different initialisation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-10T19:12:57.822152Z", + "start_time": "2024-12-10T19:12:57.819242Z" + } + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.8.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/PySDM/source/examples/PySDM_examples/Bartman_et_al_2021/demo_fig3.ipynb b/PySDM/source/examples/PySDM_examples/Bartman_et_al_2021/demo_fig3.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..bf76103568288e3f415ecde46972fb7c961c24f5 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Bartman_et_al_2021/demo_fig3.ipynb @@ -0,0 +1,3863 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "collapsed": false + }, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Bartman_et_al_2021/demo_fig3.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Bartman_et_al_2021/demo_fig3.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Bartman_et_al_2021/demo_fig3.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# TODO #1417" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-10T19:08:19.405167Z", + "start_time": "2024-12-10T19:08:19.400212Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-10T19:08:22.077732Z", + "start_time": "2024-12-10T19:08:19.408538Z" + } + }, + "outputs": [], + "source": [ + "from PySDM_examples.utils import ProgBarController\n", + "from open_atmos_jupyter_utils import TemporaryFile, show_plot\n", + "from PySDM.physics import si, convert_to\n", + "from PySDM_examples.utils.kinematic_2d import Simulation, Storage\n", + "from PySDM_examples.Arabas_et_al_2015 import Settings, SpinUp\n", + "from PySDM_examples.Bartman_et_al_2021.label import label\n", + "from PySDM.exporters import NetCDFExporter\n", + "import PySDM.products as PySDM_products\n", + "from matplotlib import pyplot, rcParams\n", + "from scipy.ndimage import uniform_filter1d\n", + "from scipy.io import netcdf_file\n", + "import numpy as np\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-10T19:08:22.206252Z", + "start_time": "2024-12-10T19:08:22.200287Z" + } + }, + "outputs": [], + "source": [ + "ensemble_size = 3 if 'CI' not in os.environ else 2\n", + "runs = []\n", + "for _ in range(ensemble_size):\n", + " runs.append({'file': TemporaryFile('.nc'), 'settings': {'coalescence_adaptive': True}})\n", + "for _ in range(ensemble_size):\n", + " runs.append({'file': TemporaryFile('.nc'), 'settings': {'coalescence_adaptive': False, 'coalescence_substeps': 32 if 'CI' not in os.environ else 2}})\n", + "for _ in range(ensemble_size):\n", + " runs.append({'file': TemporaryFile('.nc'), 'settings': {'coalescence_adaptive': False, 'coalescence_substeps': 1}})\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-10T19:08:22.228446Z", + "start_time": "2024-12-10T19:08:22.222054Z" + } + }, + "outputs": [], + "source": [ + "radius_range = (.5*si.um, 25*si.um)\n", + "dt = 32 * si.s\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-10T19:52:55.506136Z", + "start_time": "2024-12-10T19:08:22.243866Z" + } + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "8161ccb72ad64718a36b57d70f1af722", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='run 1/9', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "dea33784ef124fb58358dfffebb55690", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='netCDF', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "d41879819c9b4782ac2d8ceb1ff2d8d9", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='run 2/9', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "8fb61491099a49cdb00abd0e6a66e400", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='netCDF', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "5c7d3be5169b4a718cccfc48e93d1286", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='run 3/9', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "0541ef37568b41f9807c16f6219f4647", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='netCDF', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "9cefc0a986324e9a8963b86061722497", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='run 4/9', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "02bca451bdfe4e8f9583d5faf4e0c817", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='netCDF', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "00ceca8e867d42ebab48f7afd2cdf145", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='run 5/9', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "d951dd816b0545e7bd352e7e3c96a856", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='netCDF', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "232e9fee0f544bcb83f57a87a37df71f", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='run 6/9', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "a6eed9df94e8456985a9ebf6b8e13bb0", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='netCDF', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "5f67bc3504bb433d87983378bf5f482f", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='run 7/9', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "db60e6185c9a46b796595b749e946529", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='netCDF', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "66a210bde5754cd58899e6ad8eda8f63", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='run 8/9', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "53bccafdaaa0402683589df6ad4f7cf4", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='netCDF', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "4694642d86c64912b1ac9e14aee63f5f", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='run 9/9', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "536885344dba43c292fb1fcbe0d7526e", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='netCDF', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "\n", + "for i, run in enumerate(runs):\n", + " settings = Settings()\n", + "\n", + " products = (\n", + " PySDM_products.DynamicWallTime('Collision', name='Coalescence_wall_time'),\n", + " PySDM_products.SurfacePrecipitation(name='surf_precip', unit='mm/day'),\n", + " PySDM_products.CollisionTimestepMin(name='dt_coal_min')\n", + " )\n", + "\n", + " settings.n_sd_per_gridbox = 128 if 'CI' not in os.environ else 16\n", + " settings.grid = (32, 32)\n", + " settings.dt = dt\n", + " settings.condensation_dt_cond_range = (.25*si.s, settings.dt)\n", + " \n", + " settings.mode_1.norm_factor *= 3\n", + " settings.mode_2.norm_factor *= 3\n", + " settings.spectrum_per_mass_of_dry_air.norm_factor *= 3\n", + " settings.simulation_time = settings.spin_up_time * (2 if 'CI' not in os.environ else 1.5)\n", + " settings.output_interval = settings.dt\n", + " settings.condensation_adaptive = True\n", + " settings.condensation_rtol_x = 1e-6\n", + " settings.condensation_rtol_thd = 2e-5/7/7\n", + " settings.condensation_schedule = 'dynamic'\n", + " settings.kappa = .8\n", + " \n", + " for key, value in run['settings'].items(): \n", + " assert hasattr(settings, key)\n", + " setattr(settings, key, value)\n", + " \n", + " storage = Storage()\n", + " simulation = Simulation(settings, storage, SpinUp=SpinUp)\n", + " simulation.reinit(products)\n", + "\n", + " simulation.run(ProgBarController(f\"run {i+1}/{len(runs)}\"))\n", + " exporter = NetCDFExporter(storage, settings, simulation, run['file'].absolute_path)\n", + " exporter.run(ProgBarController('netCDF'))" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-10T19:52:56.362936Z", + "start_time": "2024-12-10T19:52:55.534703Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-12-10T20:52:56.142746\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.9.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "907dd22567e240a1b9bc6a24be37c448", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./tmp3r6i2vu7.pdf
\"), HTML(value…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-12-10T20:52:56.347708\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.9.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "de479015ada9496a8376571d30371ad7", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./tmpsps46pkr.pdf
\"), HTML(value…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "rcParams[\"figure.figsize\"] = (15, 5)\n", + "\n", + "timeaxis = lambda t: t / 3600 - 1\n", + "\n", + "dt_day = dt\n", + "convert_to(dt_day, si.day)\n", + "\n", + "window=5\n", + "colors=['red', 'green', 'blue']\n", + "\n", + "for i, run in enumerate(runs):\n", + " nc = netcdf_file(run['file'].absolute_path, mode='r', mmap=False)\n", + " wall_time = np.mean(nc.variables['Coalescence_wall_time'][:-10]) # TODO #449\n", + " wall_time = int(10000 * wall_time) / 10000\n", + " filtered_cumsum = uniform_filter1d(dt_day * np.cumsum(nc.variables['surf_precip'][:]), size=window)\n", + " pyplot.plot(\n", + " timeaxis(nc.variables['T'][:]),\n", + " filtered_cumsum,\n", + " label=f\"{label(run['settings'])} (time/step: {wall_time:.4f}s)\",\n", + " color=colors[i//ensemble_size],\n", + " lw=.75\n", + " )\n", + " if i % ensemble_size == 0:\n", + " mean = np.copy(filtered_cumsum)\n", + " else:\n", + " mean += filtered_cumsum\n", + " if (i+1) % ensemble_size == 0:\n", + " pyplot.plot(\n", + " timeaxis(nc.variables['T'][:]),\n", + " mean / ensemble_size,\n", + " lw=3,\n", + " color=colors[i // ensemble_size]\n", + " )\n", + "pyplot.xlim(timeaxis(1.25 * settings.spin_up_time), timeaxis(nc.variables['T'][-1]))\n", + "pyplot.legend()\n", + "pyplot.ylabel('accumulated rainfall [mm] ' + f'({window}-point moving average)')\n", + "pyplot.xlabel('time after spinup [h]')\n", + "show_plot()\n", + "\n", + "\n", + "for i, run in enumerate(runs):\n", + " if not run['settings']['coalescence_adaptive']:\n", + " continue\n", + " nc = netcdf_file(run['file'].absolute_path, mode='r', mmap=False)\n", + " data = nc.variables['dt_coal_min'][:]\n", + " pyplot.plot(timeaxis(nc.variables['T'][:]), np.amax(np.amax(data, axis=-1), axis=-1))\n", + " pyplot.plot(timeaxis(nc.variables['T'][:]), np.amin(np.amin(data, axis=-1), axis=-1))\n", + "show_plot()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-10T19:52:56.387862Z", + "start_time": "2024-12-10T19:52:56.385309Z" + } + }, + "outputs": [], + "source": [ + "# TODO #449: initialisation\n", + "# TODO #449: rng_reuse\n", + "# TODO #449: seed, spread\n", + "# TODO #449: sd_num ?\n", + "# TODO #449: plot deficit\n", + "# TODO #449: dt histogram" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.8.2" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/PySDM/source/examples/PySDM_examples/Bartman_et_al_2021/label.py b/PySDM/source/examples/PySDM_examples/Bartman_et_al_2021/label.py new file mode 100644 index 0000000000000000000000000000000000000000..a53be17e465b5838e89423d3d4171c2c45a813f7 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Bartman_et_al_2021/label.py @@ -0,0 +1,20 @@ +def label(settings): + lbl = str( + { + k.replace("condensation_", ""): ( + f"{v:.1e}" + if isinstance(v, float) + else str(v).zfill(2) if isinstance(v, int) else v + ) + for k, v in settings.items() + } + ) + return ( + lbl.replace("{", "") + .replace("}", "") + .replace("'", "") + .replace("True", "T") + .replace("False", "F") + .replace("_thd", "$_{th}$") + .replace("e-0", "e-") + ) diff --git a/PySDM/source/examples/PySDM_examples/Berry_1967/__init__.py b/PySDM/source/examples/PySDM_examples/Berry_1967/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f4c1c9d5ee56b79dc5c771e98d9a1f3a513c7088 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Berry_1967/__init__.py @@ -0,0 +1,9 @@ +""" +box-model coalescence-only example based on +[Berry 1967 (J. Atmos. Sci. 24)](https://doi.org/10.1175/1520-0469(1967)024%3C0688:CDGBC%3E2.0.CO;2) + +figs_5_8_10.ipynb: +.. include:: ./figs_5_8_10.ipynb.badges.md +""" + +# pylint: disable=invalid-name diff --git a/PySDM/source/examples/PySDM_examples/Berry_1967/example.py b/PySDM/source/examples/PySDM_examples/Berry_1967/example.py new file mode 100644 index 0000000000000000000000000000000000000000..bb161100261c017f92cb1121dc762f7fde03bd33 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Berry_1967/example.py @@ -0,0 +1,68 @@ +import os + +import numpy as np +from PySDM_examples.Berry_1967.settings import Settings +from PySDM_examples.Berry_1967.spectrum_plotter import SpectrumPlotter +from PySDM_examples.Shima_et_al_2009.example import run + +from PySDM.dynamics.collisions.collision_kernels import ( + Electric, + Geometric, + Hydrodynamic, +) + + +def main(plot: bool, save): + with np.errstate(all="ignore"): + u_term_approxs = ("GunnKinzer1949",) + dts = (1, 10, "adaptive") + setup_prop = { + Geometric: (0, 100, 200, 300, 400, 500, 600, 700, 750, 800, 850), + Electric: (0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000), + Hydrodynamic: (0, 1600, 1800, 2000, 2200), + } + setups = {} + + for u_term_approx in u_term_approxs: + setups[u_term_approx] = {} + for dt in dts: + setups[u_term_approx][dt] = {} + + for kernel_type, steps in setup_prop.items(): + s = Settings(terminal_velocity_variant=u_term_approx) + s.dt = 10 if dt == "adaptive" else dt + s.adaptive = dt == "adaptive" + s.kernel = kernel_type() + s._steps = steps + setups[u_term_approx][dt][kernel_type] = s + + states = {} + for u_term_approx, setup in setups.items(): + states[u_term_approx] = {} + for dt in setup: + states[u_term_approx][dt] = {} + for kernel in setup[dt]: + states[u_term_approx][dt][kernel], _ = run(setup[dt][kernel]) + + if plot or save is not None: + for u_term_approx, setup in setups.items(): + for dt in setup: + for kernel in setup[dt]: + plotter = SpectrumPlotter(setup[dt][kernel], legend=True) + for step, vals in states[u_term_approx][dt][kernel].items(): + plotter.plot(vals, step * setup[dt][kernel].dt) + if save is not None: + n_sd = setup[dt][kernel].n_sd + plotter.save( + save + + "/" + + f"{n_sd}_{u_term_approx}_{dt}_{kernel.__name__}" + + "." + + plotter.format + ) + if plot: + plotter.show() + + +if __name__ == "__main__": + main(plot="CI" not in os.environ, save=".") diff --git a/PySDM/source/examples/PySDM_examples/Berry_1967/example_fig_6.py b/PySDM/source/examples/PySDM_examples/Berry_1967/example_fig_6.py new file mode 100644 index 0000000000000000000000000000000000000000..ad69a5a5021f65c3cf1a1563c79cbda311c2019e --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Berry_1967/example_fig_6.py @@ -0,0 +1,105 @@ +import numpy as np +from matplotlib import pyplot as plt +from scipy.optimize import minimize + +from PySDM.backends import CPU +from PySDM.physics import constants as const + +backend = CPU() +um = const.si.um + + +def full_params(params): + return (1, 1, *params) + + +def print_collection_efficiency_portrait(params): + size = 2 * 5.236 + plt.figure(figsize=(size / 2, size)) + points = 200 + Y_c = np.zeros(points) + x_values = np.linspace(0, 1, points) + pair = np.array([0.0, 0.0]) + is_first_in_pair = np.array([True, False]) + idx = np.arange(len(is_first_in_pair)) + for r in ( + radius * const.si.um for radius in (8, 10, 14, 16, 20, 30, 40, 60, 80, 136) + ): + pair[0] = r + for i, _ in enumerate(x_values): + pair[1] = x_values[i] * r + backend._linear_collection_efficiency_body( + params=full_params(params), + output=Y_c[i : i + 1], + radii=pair, + is_first_in_pair=is_first_in_pair, + idx=idx, + length=2, + unit=1 * um, + ) + plt.plot(x_values, Y_c, label=f"{r / const.si.um}um") + xticks = np.arange(11) / 10 + yticks = np.arange(21) / 10 + plt.xticks(xticks, xticks) + plt.yticks(yticks, yticks) + plt.grid() + plt.savefig("berry_fig_6.pdf", format="pdf") + + +p = np.array((0.2, 0.4, 0.6, 0.8, 0.9, 1.0)) +expected = [ + [0, 0, 8.3, 0, 0, 0], # 8 + [0, 36, 55.6, 24, 0, 0], # 10 + [27, 85, 105.3, 96.2, 49.6, 0], # 14 + [46.6, 94.7, 116.5, 107.5, 84.2, 0], # 16 + [70, 108.2, 130.8, 136, 112.8, 0], # 20 + [117, 138, 158, 179, 189, 0], # 136 +] +expected = np.array(expected) +expected /= 100 +radii = np.array((8 * um, 10 * um, 14 * um, 16 * um, 20 * um, 136 * um)) + + +def Y_c_portrait( + params, +): + Y_c = np.zeros(expected.shape) + is_first_in_pair = np.array([True, False]) + idx = np.arange(len(is_first_in_pair)) + pair = np.array([0.0, 0.0]) + for i, _ in enumerate(radii): + pair[0] = radii[i] + for j, __ in enumerate(p): + pair[1] = p[j] * radii[i] + backend._linear_collection_efficiency_body( + params=full_params(params), + output=Y_c[i : i + 1, j], + radii=pair, + is_first_in_pair=is_first_in_pair, + idx=idx, + length=2, + unit=1 * um, + ) + return Y_c + + +def error(params): + Y_c = Y_c_portrait(params) + return np.sum((Y_c - expected) ** 2) + 0 * np.abs(Y_c[0, 2] - expected[0, 2]) + + +def error2(params): + Y_c = Y_c_portrait(params) + return np.sum((Y_c[:-1] - expected[:-1]) ** 2) + 1000 * np.abs( + Y_c[0, 2] - expected[0, 2] + ) + + +if __name__ == "__main__": + x0 = np.array([-27, 1.65, -58, 1.9, 1, 1.13, 1, 1, 0.004, 4, 8]) + x = np.array([-7, 1.78, -20.5, 1.73, 0.26, 1.47, 1, 0.82, -0.003, 4.4, 8]) + print_collection_efficiency_portrait(x) + res = minimize(error, x) + print_collection_efficiency_portrait(res.x) + res = minimize(error2, res.x) + print_collection_efficiency_portrait(res.x) diff --git a/PySDM/source/examples/PySDM_examples/Berry_1967/figs_5_8_10.ipynb b/PySDM/source/examples/PySDM_examples/Berry_1967/figs_5_8_10.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..0b941b9551763fcdceb53d2f3515b0f2bcc4af42 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Berry_1967/figs_5_8_10.ipynb @@ -0,0 +1,2277 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Berry_1967/figs_5_8_10.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Berry_1967/figs_5_8_10.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Berry_1967/figs_5_8_10.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### based on Figs. 5, 8, & 10 from Berry 1967 (J. Atmos. Sci. 24) \"_Cloud Droplet Growth by Collection_\" \n", + "https://doi.org/10.1175/1520-0469(1967)024%3C0688:CDGBC%3E2.0.CO;2" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-16T17:00:57.558059Z", + "start_time": "2024-12-16T17:00:57.554283Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-16T17:01:00.129236Z", + "start_time": "2024-12-16T17:00:57.561086Z" + }, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "from numpy import errstate\n", + "from PySDM.backends import CPU, GPU, ThrustRTC\n", + "from PySDM.dynamics.collisions.collision_kernels import Geometric, Hydrodynamic, Electric\n", + "from PySDM_examples.Berry_1967.spectrum_plotter import SpectrumPlotter\n", + "from PySDM_examples.Berry_1967.settings import Settings\n", + "from PySDM_examples.Shima_et_al_2009.example import run\n", + "from PySDM_examples.utils import widgets" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-16T17:01:00.225076Z", + "start_time": "2024-12-16T17:01:00.222590Z" + } + }, + "outputs": [], + "source": [ + "progbar = widgets.IntProgress(min=0, max=100, description='%')" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-16T17:01:00.237261Z", + "start_time": "2024-12-16T17:01:00.233702Z" + } + }, + "outputs": [], + "source": [ + "def demo(**kwargs):\n", + " with kwargs['freezer']:\n", + " with errstate(all='raise'):\n", + " settings = Settings(steps=[i * (kwargs['n_step'] // kwargs['n_plot']) for i in range(kwargs['n_plot'] + 1)])\n", + " backend = GPU if kwargs['gpu'] else CPU\n", + " settings.n_sd = 2 ** kwargs['n_SD']\n", + " settings.adaptive = kwargs['adaptive']\n", + " settings.dt = 10 if settings.adaptive else settings.dt\n", + " if kwargs['kernel'] == 'geometric sweep-out':\n", + " settings.kernel = Geometric()\n", + " elif kwargs['kernel'] == 'electric field 3000V/cm':\n", + " settings.kernel = Electric()\n", + " else:\n", + " settings.kernel = Hydrodynamic()\n", + "\n", + " states, _ = run(settings, backend, (widgets.ProgbarUpdater(progbar, settings.output_steps[-1]),))\n", + "\n", + " with errstate(invalid='ignore'):\n", + " plotter = SpectrumPlotter(settings)\n", + " plotter.smooth = kwargs['smooth']\n", + " for step, state in states.items():\n", + " plotter.plot(state, step * settings.dt)\n", + " plotter.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-16T17:01:03.042264Z", + "start_time": "2024-12-16T17:01:00.242899Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-06-29T23:23:44.641521\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "79cdf80721424acd938d48320733736c", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./tmpzkkjp5b2.pdf
\"), HTML(value…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "n_SD = widgets.IntSlider(value=14, min=12, max=18, step=1, description='$log_2(n_{SD})$', continuous_update=False)\n", + "n_step = widgets.IntSlider(value=1000, step=100, min=0, max=2400, description='$n_{step}$', continuous_update=False)\n", + "n_plot = widgets.IntSlider(value=10, step=1, min=1, max=16, description='$n_{plot}$', continuous_update=False)\n", + "sliders = widgets.HBox([n_SD, n_step, n_plot])\n", + "\n", + "adaptive = widgets.Checkbox(value=False, description='adaptive dt')\n", + "smooth = widgets.Checkbox(value=True, description='smooth plot')\n", + "gpu = widgets.Checkbox(value=False, description='GPU')\n", + "options = [adaptive, smooth]\n", + "if ThrustRTC.ENABLE:\n", + " options.append(gpu)\n", + "kernel = widgets.Select(\n", + " options=['geometric sweep-out', 'electric field 3000V/cm', 'hydrodynamic capture'],\n", + " value='geometric sweep-out',\n", + " description='kernel:',\n", + " rows=3\n", + ")\n", + "options.append(kernel)\n", + "boxes = widgets.HBox(options)\n", + " \n", + "freezer = widgets.Freezer([n_SD, n_step, n_plot, kernel, adaptive, gpu])\n", + "inputs = {\n", + " 'freezer': freezer, 'n_SD': n_SD, 'n_step': n_step, 'n_plot': n_plot, \n", + " 'kernel': kernel, 'adaptive': adaptive, 'smooth': smooth, 'gpu': gpu\n", + "}\n", + "\n", + "if 'CI' not in os.environ:\n", + " widgets.display(sliders, boxes, progbar, widgets.interactive_output(demo, inputs))\n", + "else:\n", + " demo(**{k:v.value for k,v in inputs.items()})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/PySDM/source/examples/PySDM_examples/Berry_1967/settings.py b/PySDM/source/examples/PySDM_examples/Berry_1967/settings.py new file mode 100644 index 0000000000000000000000000000000000000000..bd52b857a4d8cf7661fca6a816e4ceefc386c85c --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Berry_1967/settings.py @@ -0,0 +1,47 @@ +from typing import Optional + +import numpy as np +from pystrict import strict + +from PySDM import Formulae +from PySDM.dynamics.collisions import collision_kernels +from PySDM.initialisation import spectra +from PySDM.physics import si + + +@strict +class Settings: + def __init__( + self, + steps: Optional[list] = None, + terminal_velocity_variant: str = "GunnKinzer1949", + ): + steps = steps or [200 * i for i in range(10)] + + self.formulae = Formulae(terminal_velocity=terminal_velocity_variant) + self.init_x_min = self.formulae.trivia.volume(radius=3.94 * si.micrometre) + self.init_x_max = self.formulae.trivia.volume(radius=25 * si.micrometres) + + self.n_sd = 2**13 + self.n_part = 239 / si.cm**3 + self.X0 = self.formulae.trivia.volume(radius=10 * si.micrometres) + self.dv = ( + 1e1 * si.metres**3 + ) # 1e6 -> overflows on ThrustRTC (32-bit int multiplicities) + self.norm_factor = self.n_part * self.dv + self.rho = self.formulae.constants.rho_w + self.dt = 1 * si.seconds + self.adaptive = False + self.seed = 44 + self._steps = steps + self.kernel = collision_kernels.Geometric(collection_efficiency=1) + self.spectrum = spectra.Exponential(norm_factor=self.norm_factor, scale=self.X0) + + # Note 220 instead of 200 for smoothing + self.radius_bins_edges = np.logspace( + np.log10(3.94 * si.um), np.log10(220 * si.um), num=100, endpoint=True + ) + + @property + def output_steps(self): + return [int(step / self.dt) for step in self._steps] diff --git a/PySDM/source/examples/PySDM_examples/Berry_1967/spectrum_plotter.py b/PySDM/source/examples/PySDM_examples/Berry_1967/spectrum_plotter.py new file mode 100644 index 0000000000000000000000000000000000000000..032611f31e6dbc9bd1f932f2e4620aef1b54dc8a --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Berry_1967/spectrum_plotter.py @@ -0,0 +1,33 @@ +from matplotlib import pyplot +from open_atmos_jupyter_utils import show_plot +from PySDM_examples.Shima_et_al_2009.spectrum_plotter import ( + SpectrumPlotter as SuperSpectrumPlotter, +) + + +class SpectrumPlotter(SuperSpectrumPlotter): + def __init__(self, settings, title=None, grid=True, legend=False): + size = 2 * 5.236 + pyplot.figure(figsize=(size, size * 0.54)) + pyplot.xlabel("particle radius [µm]") + pyplot.ylabel("dm/dlnr [g/m^3/(unit dr/r)]") + super().__init__(settings, title=title, grid=grid, legend=legend, log_base=2) + self.color = None + self.smooth = True + + @staticmethod + def ticks(): + xticks = [4, 6.25, 12.5, 25, 50, 100, 200] + pyplot.xticks(xticks, xticks) + pyplot.yticks([0.5 * i for i in range(5)], [0, None, 1, None, 2]) + + def show(self): + self.finish() + self.ticks() + show_plot() + + def plot( + self, spectrum, t, label=None, color=None, title=None, add_error_to_label=False + ): + settings = self.settings + self.plot_data(settings, t, spectrum, label, color) diff --git a/PySDM/source/examples/PySDM_examples/Bieli_et_al_2022/__init__.py b/PySDM/source/examples/PySDM_examples/Bieli_et_al_2022/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..6d3ff07f3cf265f44278aafae5cc83f9155d7fb6 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Bieli_et_al_2022/__init__.py @@ -0,0 +1,8 @@ +""" +collision-only box-model example from [Bieli et al. 2022](https://doi.org/10.1029/2022MS002994) + +make_fig_3.ipynb: +.. include:: ./make_fig_3.ipynb.badges.md +""" + +# pylint: disable=invalid-name diff --git a/PySDM/source/examples/PySDM_examples/Bieli_et_al_2022/make_fig_3.ipynb b/PySDM/source/examples/PySDM_examples/Bieli_et_al_2022/make_fig_3.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..86eac725f1874d017dabb2b8f095e50f36d0aa60 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Bieli_et_al_2022/make_fig_3.ipynb @@ -0,0 +1,1994 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Bieli_et_al_2022/make_fig_3.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Bieli_et_al_2022/make_fig_3.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Bieli_et_al_2022/make_fig_3.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Figure 2: evolution of the first three moments for different values of coalescence efficiency\n", + "https://www.essoar.org/doi/abs/10.1002/essoar.10510248.1" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-06T10:41:08.955982Z", + "start_time": "2024-12-06T10:41:08.951980Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2023-12-29T11:31:59.244557Z", + "start_time": "2023-12-29T11:31:58.869Z" + } + }, + "outputs": [], + "source": [ + "from matplotlib import pyplot\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2023-12-29T14:05:36.927393Z", + "start_time": "2023-12-29T14:05:36.913779Z" + } + }, + "outputs": [], + "source": [ + "from PySDM_examples.Bieli_et_al_2022.settings import Settings\n", + "from PySDM_examples.Bieli_et_al_2022.simulation import make_core\n", + "from PySDM.physics import si\n", + "from open_atmos_jupyter_utils import show_plot" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2023-12-29T11:32:03.954357Z", + "start_time": "2023-12-29T11:32:00.332220Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2023-12-29T12:32:03.916453\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "aa0a160219374aea9085a20d43934138", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./fig3.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "settings = Settings()\n", + "fig, ax = pyplot.subplots(nrows=1, ncols=3, figsize=(8,3))\n", + "for i in range(3):\n", + " coal_eff = settings.coal_effs[i]\n", + " particulator = make_core(settings, coal_eff)\n", + " t = settings.output_steps\n", + " moments = np.zeros((3, len(t)))\n", + " j = 0\n", + " for step in settings.output_steps:\n", + " particulator.run(step - particulator.n_steps)\n", + " moments[:,j] = [particulator.products['M0'].get()[0], particulator.products['M1'].get()[0], \n", + " particulator.products['M2'].get()[0]]\n", + " j += 1\n", + " moments[1,:] *= settings.rho / si.g\n", + " moments[2,:] *= settings.rho**2 / si.g**2\n", + " moments *= 1/settings.dv*si.cm**3\n", + " ax[0].plot(t, moments[0,:])\n", + " ax[1].plot(t, moments[1,:])\n", + " ax[2].plot(t, moments[2,:])\n", + "ax[0].set_xlabel('time (s)')\n", + "ax[1].set_xlabel('time (s)')\n", + "ax[2].set_xlabel('time (s)')\n", + "ax[0].set_ylabel('$M_0$ (1/cm$^3$)')\n", + "ax[1].set_ylabel('$M_1$ (g/cm$^3$)')\n", + "ax[2].set_ylabel('$M_2$ (g$^2$/cm$^3$)')\n", + "\n", + "ax[0].set_ylim([0,2e4])\n", + "ax[1].set_ylim([0,5e-6])\n", + "ax[2].set_ylim([0,2e-14])\n", + "pyplot.legend(['E_c=0.8','E_c=0.9','E_c=1.0'])\n", + "pyplot.tight_layout()\n", + "show_plot('fig3.pdf')\n" + ] + } + ], + "metadata": { + "interpreter": { + "hash": "b43cf254c70d60c2e21a7f71ba113e70c1694742e72407132919c841d907074b" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/PySDM/source/examples/PySDM_examples/Bieli_et_al_2022/settings.py b/PySDM/source/examples/PySDM_examples/Bieli_et_al_2022/settings.py new file mode 100644 index 0000000000000000000000000000000000000000..ec2c8e3016c58aebad8862c5ce4e82ebf5aa8341 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Bieli_et_al_2022/settings.py @@ -0,0 +1,46 @@ +from pystrict import strict + +from PySDM.dynamics.collisions.breakup_efficiencies import ConstEb +from PySDM.dynamics.collisions.breakup_fragmentations import Feingold1988 +from PySDM.dynamics.collisions.coalescence_efficiencies import ConstEc +from PySDM.dynamics.collisions.collision_kernels import Golovin +from PySDM.formulae import Formulae +from PySDM.initialisation.spectra import Gamma +from PySDM.physics import si +from PySDM.physics.constants_defaults import rho_w + + +@strict +class Settings: + def __init__(self, formulae: Formulae = None): + self.n_sd = 2**12 + self.n_part = 1e4 / si.cm**3 + self.theta = 0.33e-9 * si.g / rho_w + self.k = 1 + self.dv = 0.1 * si.m**3 + self.norm_factor = self.n_part * self.dv + self.dt = 1 * si.seconds + self.adaptive = False + self.seed = 44 + self._steps = list(range(60)) + self.kernel = Golovin(b=2000 * si.cm**3 / si.g / si.s * rho_w) + self.coal_effs = [ConstEc(Ec=0.8), ConstEc(Ec=0.9), ConstEc(Ec=1.0)] + self.vmin = 1.0 * si.um**3 + self.nfmax = 10 + self.fragtol = 1e-3 + self.fragmentation = Feingold1988( + scale=self.k * self.theta, + fragtol=self.fragtol, + vmin=self.vmin, + nfmax=self.nfmax, + ) + self.break_eff = ConstEb(1.0) + self.spectrum = Gamma(norm_factor=self.norm_factor, k=self.k, theta=self.theta) + self.rho = rho_w + self.formulae = formulae or Formulae( + fragmentation_function=self.fragmentation.__class__.__name__ + ) + + @property + def output_steps(self): + return [int(step / self.dt) for step in self._steps] diff --git a/PySDM/source/examples/PySDM_examples/Bieli_et_al_2022/simulation.py b/PySDM/source/examples/PySDM_examples/Bieli_et_al_2022/simulation.py new file mode 100644 index 0000000000000000000000000000000000000000..cadac6d489a5f4c0612b0efeff6bfddde652c6a6 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Bieli_et_al_2022/simulation.py @@ -0,0 +1,40 @@ +import PySDM.products.size_spectral.arbitrary_moment as am +from PySDM.backends import CPU +from PySDM.builder import Builder +from PySDM.dynamics import Collision +from PySDM.environments import Box +from PySDM.initialisation.sampling.spectral_sampling import ConstantMultiplicity + + +def make_core(settings, coal_eff): + backend = CPU + + builder = Builder( + n_sd=settings.n_sd, + backend=backend(settings.formulae), + environment=Box(dv=settings.dv, dt=settings.dt), + ) + builder.particulator.environment["rhod"] = 1.0 + attributes = {} + attributes["volume"], attributes["multiplicity"] = ConstantMultiplicity( + settings.spectrum + ).sample_deterministic(settings.n_sd) + collision = Collision( + collision_kernel=settings.kernel, + coalescence_efficiency=coal_eff, + breakup_efficiency=settings.break_eff, + fragmentation_function=settings.fragmentation, + adaptive=settings.adaptive, + ) + builder.add_dynamic(collision) + common_args = { + "attr": "volume", + "attr_unit": "m^3", + "skip_division_by_m0": True, + "skip_division_by_dv": True, + } + products = tuple( + am.make_arbitrary_moment_product(rank=rank, **common_args)(name=f"M{rank}") + for rank in range(3) + ) + return builder.build(attributes, products) diff --git a/PySDM/source/examples/PySDM_examples/Bolin_1958/__init__.py b/PySDM/source/examples/PySDM_examples/Bolin_1958/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..49f18772975ae798ffab4f132981676bd426bf7d --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Bolin_1958/__init__.py @@ -0,0 +1,8 @@ +""" +Table 1 from [Bolin 1958](https://digitallibrary.un.org/record/3892725) + +table_1.ipynb: +.. include:: ./table_1.ipynb.badges.md +""" + +# pylint: disable=invalid-name diff --git a/PySDM/source/examples/PySDM_examples/Bolin_1958/table_1.ipynb b/PySDM/source/examples/PySDM_examples/Bolin_1958/table_1.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..35c754e202edb5863f04b39469dcbcf47382ef13 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Bolin_1958/table_1.ipynb @@ -0,0 +1,307 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "e3f1edc815e974f4", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Bolin_1958/table_1.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Bolin_1958/table_1.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Bolin_1958/table_1.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "bb84c52c57a729ec", + "metadata": {}, + "source": [ + "### based on Table 1 from B.Bolin 1958 \"On the use of tritium as a tracer for water in nature\" (https://digitallibrary.un.org/record/3892725)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "5e4e58050cc01f64", + "metadata": { + "ExecuteTime": { + "end_time": "2025-08-12T13:56:32.855405Z", + "start_time": "2025-08-12T13:56:32.851730Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "95f360dc62f373f9", + "metadata": { + "ExecuteTime": { + "end_time": "2025-08-12T13:56:35.553021Z", + "start_time": "2025-08-12T13:56:32.868658Z" + } + }, + "outputs": [], + "source": [ + "import pandas\n", + "import numpy as np\n", + "from PySDM.physics import in_unit, si\n", + "from PySDM import Formulae\n", + "from IPython.display import display" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "4a8c2bd612c892f2", + "metadata": { + "ExecuteTime": { + "end_time": "2025-08-12T13:56:38.312522Z", + "start_time": "2025-08-12T13:56:35.612041Z" + } + }, + "outputs": [], + "source": [ + "any_non_zero_value = 44.0\n", + "radii = np.asarray([0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.15, 0.20]) * si.cm\n", + "\n", + "settings = {\n", + " \"terminal_velocity\":\"RogersYau\",\n", + " \"drop_growth\":\"Mason1971\",\n", + " \"diffusion_thermics\":\"Neglect\",\n", + " \"saturation_vapour_pressure\":\"AugustRocheMagnus\",\n", + " \"ventilation\":\"Froessling1938\",\n", + " \"particle_shape_and_density\":\"LiquidSpheres\",\n", + " \"air_dynamic_viscosity\":\"ZografosEtAl1987\",\n", + " \"isotope_equilibrium_fractionation_factors\":\"VanHook1968\", #Check\n", + " \"isotope_diffusivity_ratios\":\"GrahamsLaw\",\n", + " 'constants': {\"BOLIN_ISOTOPE_TIMESCALE_COEFF_C1\": 1.63},\n", + " 'isotope_relaxation_timescale': \"Bolin1958\",\n", + "}\n", + "formulae = Formulae(**settings)\n", + "const = formulae.constants\n", + "temperature = formulae.constants.T0 + 10 * si.K\n", + "\n", + "p = const.p_STP\n", + "D = formulae.diffusion_thermics.D(temperature, p)\n", + "K = formulae.diffusion_thermics.K(temperature, p)\n", + "lv = formulae.latent_heat_vapourisation.lv(temperature)\n", + "eta_air = formulae.air_dynamic_viscosity.eta_air(temperature)\n", + "air_density = p/const.Rd/temperature\n", + "assert abs(air_density - 1)/air_density <.3\n", + "v_term = formulae.terminal_velocity.v_term(radii)\n", + "Re = formulae.particle_shape_and_density.reynolds_number(\n", + " radius=radii,\n", + " velocity_wrt_air=v_term,\n", + " dynamic_viscosity=eta_air,\n", + " density=air_density,\n", + ")\n", + "Sc = formulae.trivia.air_schmidt_number(\n", + " dynamic_viscosity=eta_air,\n", + " diffusivity=D,\n", + " density=air_density,\n", + ")\n", + "sqrt_re_times_cbrt_sc = formulae.trivia.sqrt_re_times_cbrt_sc(Re, Sc)\n", + "f = formulae.ventilation.ventilation_coefficient(sqrt_re_times_cbrt_sc)\n", + "pvs = formulae.saturation_vapour_pressure.pvs_water(temperature)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "340812de267c4290", + "metadata": { + "ExecuteTime": { + "end_time": "2025-08-12T13:56:38.762219Z", + "start_time": "2025-08-12T13:56:38.345299Z" + } + }, + "outputs": [ + { + "data": { + "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", + "
radius [cm]adjustment time [s]terminal velocity [m/s]distance [m]
00.0051.70.40.69
10.015.40.84.3
20.02520240
30.054841.9e+02
40.075815.54.4e+02
50.11.2e+026.47.6e+02
60.152e+027.81.6e+03
70.23e+0292.7e+03
\n", + "
" + ], + "text/plain": [ + " radius [cm] adjustment time [s] terminal velocity [m/s] distance [m]\n", + "0 0.005 1.7 0.4 0.69\n", + "1 0.01 5.4 0.8 4.3\n", + "2 0.025 20 2 40\n", + "3 0.05 48 4 1.9e+02\n", + "4 0.075 81 5.5 4.4e+02\n", + "5 0.1 1.2e+02 6.4 7.6e+02\n", + "6 0.15 2e+02 7.8 1.6e+03\n", + "7 0.2 3e+02 9 2.7e+03" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "mass_ventilation = f\n", + "RH = 1\n", + "RH_eq = 0\n", + "Fk = formulae.drop_growth.Fk(\n", + " T=temperature,\n", + " K=any_non_zero_value,\n", + " lv=0,\n", + ")\n", + "Fd = formulae.drop_growth.Fd(\n", + " T=temperature,\n", + " D=D*mass_ventilation,\n", + " pvs=pvs,\n", + ")\n", + "r_dr_dt = formulae.drop_growth.r_dr_dt(\n", + " RH_eq=RH_eq,\n", + " RH=RH,\n", + " Fk=Fk,\n", + " Fd=Fd,\n", + ")\n", + "adjustment_time = formulae.trivia.tau(\n", + " Bo=formulae.isotope_relaxation_timescale.bolin_number(),\n", + " dm_dt_over_m=(\n", + " formulae.particle_shape_and_density.dm_dt_over_m(\n", + " radii,\n", + " r_dr_dt\n", + " )\n", + " )\n", + ")\n", + "\n", + "pandas.options.display.float_format = '{:>,.2g}'.format\n", + "data = pandas.DataFrame({\n", + " 'radius [cm]': in_unit(radii, si.cm),\n", + " 'adjustment time [s]': adjustment_time,\n", + " 'terminal velocity [m/s]': v_term,\n", + " 'distance [m]': v_term * adjustment_time,\n", + "})\n", + "\n", + "display(data)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0bef57bc-7e19-4a3f-8f77-6ec762e8f236", + "metadata": { + "ExecuteTime": { + "end_time": "2025-08-12T13:56:38.799723Z", + "start_time": "2025-08-12T13:56:38.798348Z" + } + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/PySDM/source/examples/PySDM_examples/Bolot_et_al_2013/__init__.py b/PySDM/source/examples/PySDM_examples/Bolot_et_al_2013/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..512e62a619822583695d613bdbfc50d734967fc7 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Bolot_et_al_2013/__init__.py @@ -0,0 +1,9 @@ +""" +figures from Bolot et al. 2013 (Atmos. Chem. Phys. 13) +https://doi.org/10.5194/acp-13-7903-2013 + +fig_1.ipynb: +.. include:: ./fig_1.ipynb.badges.md +""" + +# pylint: disable=invalid-name diff --git a/PySDM/source/examples/PySDM_examples/Bolot_et_al_2013/fig_1.ipynb b/PySDM/source/examples/PySDM_examples/Bolot_et_al_2013/fig_1.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..7be56078c8865381f56701a50e6953c5cb171b99 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Bolot_et_al_2013/fig_1.ipynb @@ -0,0 +1,2364 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "dbe9cc43", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Bolot_et_al_2013/fig_1.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Bolot_et_al_2013/fig_1.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Bolot_et_al_2013/fig_1.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "c24338e3", + "metadata": {}, + "source": [ + "### based on Fig. 1 from Bolot et al. 2013 (Atmos. Chem. Phys.) \"_Modelling and interpreting the isotopic composition of water vapour in convective updrafts_\" (http://doi.org/10.5194/acp-13-7903-2013)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "5c681ecc", + "metadata": { + "ExecuteTime": { + "end_time": "2024-01-19T12:18:00.836015Z", + "start_time": "2024-01-19T12:18:00.772980Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "f6396e78", + "metadata": { + "ExecuteTime": { + "end_time": "2024-01-19T12:18:03.703852Z", + "start_time": "2024-01-19T12:18:03.700929Z" + } + }, + "outputs": [], + "source": [ + "from open_atmos_jupyter_utils import show_plot\n", + "from matplotlib import pyplot\n", + "from PySDM import Formulae\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e08d4f87", + "metadata": { + "ExecuteTime": { + "end_time": "2024-01-19T12:18:02.218737Z", + "start_time": "2024-01-19T12:18:02.200124Z" + } + }, + "outputs": [], + "source": [ + "formulae = Formulae(isotope_equilibrium_fractionation_factors=\"MerlivatAndNief1967+Majoube1970+Majoube1971\")\n", + "alphas = formulae.isotope_equilibrium_fractionation_factors\n", + "const = formulae.constants" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "15b167a4", + "metadata": { + "ExecuteTime": { + "end_time": "2024-01-19T12:18:27.771416Z", + "start_time": "2024-01-19T12:18:27.766118Z" + } + }, + "outputs": [], + "source": [ + "XRANGE = (-120, 20)\n", + "YRANGES = {\n", + " \"2H\": (1, 1.9),\n", + " \"18O\": (1, 1.08)\n", + "}\n", + "TITLES = {\n", + " \"2H\": \"D/H\",\n", + " \"18O\": \"$^{18}O/^{16}O$\"\n", + "}\n", + "COLORS = {\n", + " \"ice\": \"green\",\n", + " \"liquid water\": \"blue\"\n", + "}\n", + "N_POINTS = 64" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "59bf6c49", + "metadata": { + "ExecuteTime": { + "end_time": "2024-01-19T12:18:28.883464Z", + "start_time": "2024-01-19T12:18:28.122153Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-01-20T20:40:51.545209\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "0e75035d88b647a79339392d0d4f52c2", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./fig_1.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, axs = pyplot.subplots(2, 1, figsize=(6,10))\n", + "for i, isotopologue in enumerate((\"2H\", \"18O\")):\n", + " for phase, temp_celsius in {\n", + " 'ice': np.linspace(XRANGE[0], 0, N_POINTS),\n", + " 'liquid water': np.linspace(-40, XRANGE[1], N_POINTS)\n", + " }.items():\n", + " axs[i].plot(\n", + " temp_celsius, \n", + " getattr(alphas, f\"alpha_{phase[0]}_{isotopologue}\")(temp_celsius + const.T0),\n", + " color=COLORS[phase],\n", + " label=phase\n", + " )\n", + " axs[i].set_title(TITLES[isotopologue])\n", + " axs[i].set_ylim(*YRANGES[isotopologue])\n", + " axs[i].set_xlim(XRANGE)\n", + " axs[i].set_xlabel(\"cloud temp. [°C]\")\n", + " axs[i].set_ylabel(\"equilib. fractionation coefs.\")\n", + " axs[i].grid()\n", + " axs[i].legend()\n", + "show_plot(\"fig_1.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "adea4c5b9b78d865", + "metadata": { + "ExecuteTime": { + "start_time": "2024-01-19T12:18:03.157192Z" + }, + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7fc2c2234746d53a", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/PySDM/source/examples/PySDM_examples/Bulenok_2023_MasterThesis/__init__.py b/PySDM/source/examples/PySDM_examples/Bulenok_2023_MasterThesis/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f4082a4682aeb73c5969bef86e137d561525b42f --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Bulenok_2023_MasterThesis/__init__.py @@ -0,0 +1,5 @@ +# pylint: disable=invalid-name +""" +Box-model coalescence-breakup performance benchmark from +[Bulenok 2023 MSc thesis](https://www.ap.uj.edu.pl/diplomas/166879) +""" diff --git a/PySDM/source/examples/PySDM_examples/Bulenok_2023_MasterThesis/performance_comparison_Srivastava_Setup.py b/PySDM/source/examples/PySDM_examples/Bulenok_2023_MasterThesis/performance_comparison_Srivastava_Setup.py new file mode 100644 index 0000000000000000000000000000000000000000..f5adda9d8b518aa48ff24b6ec347854079da18ec --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Bulenok_2023_MasterThesis/performance_comparison_Srivastava_Setup.py @@ -0,0 +1,225 @@ +import subprocess +import os + +from datetime import datetime +import numba # pylint: disable=unused-import + +from PySDM_examples.Bulenok_2023_MasterThesis.utils import ( + go_benchmark, + process_results, + plot_processed_results, + write_to_file, +) +from PySDM_examples.Bulenok_2023_MasterThesis.setups import ( + setup_coalescence_only_sim, + setup_breakup_only_sim, + setup_coalescence_breakup_sim, +) + +from PySDM.backends import GPU, CPU +from PySDM.physics import si + + +def main(plot: bool = True, save: str = None): + + TIMESTAMP = str(datetime.now().strftime("%Y-%d-%m_%Hh-%Mm-%Ss")) + + SIM_RUN_FNAME = "env_name_" + TIMESTAMP + + assert not os.path.isfile(SIM_RUN_FNAME) + + cmd = [ + "bash", + "-c", + # pylint: disable=no-member + f"echo NUMBA_DEFAULT_NUM_THREADS: {numba.config.NUMBA_DEFAULT_NUM_THREADS}" + + f">> {SIM_RUN_FNAME}", + ] + subprocess.run(cmd, check=False) + subprocess.run( + [ # pylint: disable=no-member + "bash", + "-c", + f"echo NUMBA_NUM_THREADS: {numba.config.NUMBA_NUM_THREADS} >> {SIM_RUN_FNAME}", + ], + check=False, + ) + subprocess.run(["bash", "-c", f"lscpu >> {SIM_RUN_FNAME}"], check=False) + subprocess.run(["bash", "-c", f"nvidia-smi >> {SIM_RUN_FNAME}"], check=False) + subprocess.run(["bash", "-c", f"nvidia-smi -L >> {SIM_RUN_FNAME}"], check=False) + subprocess.run(["bash", "-c", f"cat /proc/cpuinfo >> {SIM_RUN_FNAME}"], check=False) + + CI = "CI" in os.environ + + exponents = [3, 5, 8, 10, 12, 14, 16, 18, 20, 22, 24] if not CI else [3, 5] + n_sds = [2**i for i in exponents] + + numba_n_threads = [1, 2, 4, 5, 6, 8, 10] if not CI else [1, 2] + + n_realisations = 3 if not CI else 2 + seeds = list(range(n_realisations)) + + n_steps_short = 100 if not CI else 3 + n_steps_full = 2048 if not CI else 3 + + # # Benchmark regular setup (without scaling) + + # ### Coalescence-only + + res_coalescence_only = go_benchmark( + setup_coalescence_only_sim, + n_sds, + n_steps_short, + seeds, + numba_n_threads=numba_n_threads, + double_precision=True, + sim_run_filename=SIM_RUN_FNAME + "-coalescence", + backends=[CPU, GPU], + ) + coalescence_only_processed = process_results(res_coalescence_only) + filename = f"{SIM_RUN_FNAME}-results-coalescence-double-n_steps{n_steps_short}" + if plot: + plot_processed_results( + coalescence_only_processed, + plot_title=f"coalescence-only (n_steps: {n_steps_short})", + plot_filename=filename + ".svg", + ) + if save is not None: + write_to_file(filename=filename + ".txt", d=coalescence_only_processed) + + # ### Breakup-only + + res_breakup_only = go_benchmark( + setup_breakup_only_sim, + n_sds, + n_steps_short, + seeds, + numba_n_threads=numba_n_threads, + double_precision=True, + sim_run_filename=SIM_RUN_FNAME + "-breakup", + backends=[CPU, GPU], + ) + breakup_only_processed = process_results(res_breakup_only) + filename = f"{SIM_RUN_FNAME}-results-breakup-double-n_steps{n_steps_short}" + if plot: + plot_processed_results( + breakup_only_processed, + plot_title=f"breakup-only (n_steps: {n_steps_short})", + plot_filename=filename + ".svg", + ) + if save is not None: + write_to_file(filename=filename + ".txt", d=breakup_only_processed) + + # ### Coalescence and Breakup + + res_coal_breakup = go_benchmark( + setup_coalescence_breakup_sim, + n_sds, + n_steps_full, + seeds, + numba_n_threads=numba_n_threads, + double_precision=True, + sim_run_filename=SIM_RUN_FNAME + "-coal-break", + backends=[CPU, GPU], + ) + coal_breakup_processed = process_results(res_coal_breakup) + filename = f"{SIM_RUN_FNAME}-results-coal_with_breakup-double-n_steps{n_steps_full}" + if plot: + plot_processed_results( + coal_breakup_processed, + plot_title=f"coalescence+breakup (n_steps: {n_steps_full})", + plot_filename=filename + ".svg", + ) + if save is not None: + write_to_file(filename=filename + ".txt", d=coal_breakup_processed) + + # # Benchmark setup with scaling + + def total_number_from_n_sd(n_sd): + return n_sd * 1e8 + + def dv_from_n_sd(n_sd): + return n_sd * (0.125 * si.m**3) + + # ### Coalescence-only + + res_coalescence_only_scaled = go_benchmark( + setup_coalescence_only_sim, + n_sds, + n_steps_short, + seeds, + numba_n_threads=numba_n_threads, + double_precision=True, + sim_run_filename=SIM_RUN_FNAME + "-coalescence-scaled", + total_number=total_number_from_n_sd, + dv=dv_from_n_sd, + backends=[CPU, GPU], + ) + coalescence_only_processed_scaled = process_results(res_coalescence_only_scaled) + filename = ( + f"{SIM_RUN_FNAME}-results-scaled-coalescence-double-n_steps{n_steps_short}" + ) + if plot: + plot_processed_results( + coalescence_only_processed_scaled, + plot_title=f"coalescence-only with scaling (n_steps: {n_steps_short})", + plot_filename=filename + ".svg", + ) + if save is not None: + write_to_file(filename=filename + ".txt", d=coalescence_only_processed_scaled) + + # ### Breakup-only + + res_breakup_only_scaled = go_benchmark( + setup_breakup_only_sim, + n_sds, + n_steps_short, + seeds, + numba_n_threads=numba_n_threads, + double_precision=True, + sim_run_filename=SIM_RUN_FNAME + "-breakup-scaled", + total_number=total_number_from_n_sd, + dv=dv_from_n_sd, + backends=[CPU, GPU], + ) + breakup_only_processed_scaled = process_results(res_breakup_only_scaled) + filename = f"{SIM_RUN_FNAME}-results-scaled-breakup-double-n_steps{n_steps_short}" + if plot: + plot_processed_results( + breakup_only_processed_scaled, + plot_title=f"breakup-only with scaling (n_steps: {n_steps_short})", + plot_filename=filename + ".svg", + ) + if save is not None: + write_to_file(filename=filename + ".txt", d=breakup_only_processed_scaled) + + # ### Coalescence and Breakup + + res_coal_breakup_scaled = go_benchmark( + setup_coalescence_breakup_sim, + n_sds, + n_steps_full, + seeds, + numba_n_threads=numba_n_threads, + double_precision=True, + sim_run_filename=SIM_RUN_FNAME + "-coal-break-scaled", + total_number=total_number_from_n_sd, + dv=dv_from_n_sd, + backends=[CPU, GPU], + ) + coal_breakup_processed_scaled = process_results(res_coal_breakup_scaled) + filename = ( + f"{SIM_RUN_FNAME}-results-scaled-coal_with_breakup-double-n_steps{n_steps_full}" + ) + if plot: + plot_processed_results( + coal_breakup_processed_scaled, + plot_title=f"coalescence+breakup with scaling (n_steps: {n_steps_full})", + plot_filename=filename + ".svg", + ) + if save is not None: + write_to_file(filename=filename + ".txt", d=coal_breakup_processed_scaled) + + +if __name__ == "__main__": + main(plot="CI" not in os.environ, save=".") diff --git a/PySDM/source/examples/PySDM_examples/Bulenok_2023_MasterThesis/setups.py b/PySDM/source/examples/PySDM_examples/Bulenok_2023_MasterThesis/setups.py new file mode 100644 index 0000000000000000000000000000000000000000..4d7ad3afc199902430bbb23864d08144ce049a65 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Bulenok_2023_MasterThesis/setups.py @@ -0,0 +1,112 @@ +from PySDM_examples.Bulenok_2023_MasterThesis.utils import ProductsNames +from PySDM_examples.Srivastava_1982 import Settings +from PySDM_examples.Srivastava_1982.simulation import Simulation + +from PySDM.dynamics import Collision +from PySDM.dynamics.collisions.breakup_efficiencies import ConstEb +from PySDM.dynamics.collisions.breakup_fragmentations import ConstantMass +from PySDM.dynamics.collisions.coalescence_efficiencies import ConstEc +from PySDM.dynamics.collisions.collision_kernels import ConstantK +from PySDM.physics import si +from PySDM.products import SuperDropletCountPerGridbox, VolumeFirstMoment, ZerothMoment + +dt = 1 * si.s +DV = 1 * si.m**3 +drop_mass_0 = 1 * si.g + +TOTAL_NUMBER = 1e12 + +NO_BOUNCE = ConstEb(1) + + +def make_settings(n_sd, total_number, dv, c, beta, frag_mass, backend_class): + if total_number is None: + total_number = TOTAL_NUMBER + elif callable(total_number): + total_number = total_number(n_sd) + + if dv is None: + dv = DV + elif callable(dv): + dv = dv(n_sd) + + print() + print("== Settings ==") + print("n_sd", n_sd) + print("total_number", total_number) + print("dv", dv) + print() + + return Settings( + srivastava_c=c, + srivastava_beta=beta, + frag_mass=frag_mass, + drop_mass_0=drop_mass_0, + dt=dt, + dv=dv, + n_sds=(), + total_number=total_number, + backend_class=backend_class, + ) + + +def setup_simulation(settings, n_sd, seed, double_precision=True): + products = ( + SuperDropletCountPerGridbox(name=ProductsNames.super_particle_count), + VolumeFirstMoment(name=ProductsNames.total_volume), + ZerothMoment(name=ProductsNames.total_number), + ) + + collision_rate = settings.srivastava_c + settings.srivastava_beta + simulation = Simulation( + n_steps=None, + settings=settings, + collision_dynamic=Collision( + collision_kernel=ConstantK(a=collision_rate), + coalescence_efficiency=ConstEc(settings.srivastava_c / collision_rate), + breakup_efficiency=NO_BOUNCE, + fragmentation_function=ConstantMass(c=settings.frag_mass / settings.rho), + warn_overflows=False, + adaptive=False, + ), + double_precision=double_precision, + ) + particulator = simulation.build(n_sd, seed, products=products) + + return particulator + + +def setup_coalescence_only_sim( + n_sd, backend_class, seed, double_precision=True, total_number=None, dv=None +): + c = 0.5e-6 / si.s + beta = 1e-15 / si.s + frag_mass = -1 * si.g + + settings = make_settings(n_sd, total_number, dv, c, beta, frag_mass, backend_class) + + return setup_simulation(settings, n_sd, seed, double_precision) + + +def setup_breakup_only_sim( + n_sd, backend_class, seed, double_precision=True, total_number=None, dv=None +): + c = 1e-15 / si.s + beta = 1e-9 / si.s + frag_mass = 0.25 * si.g + + settings = make_settings(n_sd, total_number, dv, c, beta, frag_mass, backend_class) + + return setup_simulation(settings, n_sd, seed, double_precision) + + +def setup_coalescence_breakup_sim( + n_sd, backend_class, seed, double_precision=True, total_number=None, dv=None +): + c = 0.5e-6 / si.s + beta = 1e-9 / si.s + frag_mass = 0.25 * si.g + + settings = make_settings(n_sd, total_number, dv, c, beta, frag_mass, backend_class) + + return setup_simulation(settings, n_sd, seed, double_precision) diff --git a/PySDM/source/examples/PySDM_examples/Bulenok_2023_MasterThesis/utils.py b/PySDM/source/examples/PySDM_examples/Bulenok_2023_MasterThesis/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..603e9a8df04e69b25b02f58b50f7cf6d5b66e25e --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Bulenok_2023_MasterThesis/utils.py @@ -0,0 +1,301 @@ +import gc +import json +import os +import time + +import numba +import numpy as np +from matplotlib import pyplot + +from PySDM.backends import CPU, GPU + + +class ProductsNames: + super_particle_count = "super_particle_count" + total_volume = "total_volume" + total_number = "total_number" + + +def print_all_products(particulator): + print( + ProductsNames.total_number, + particulator.products[ProductsNames.total_number].get(), + ) + print( + ProductsNames.total_volume, + particulator.products[ProductsNames.total_volume].get(), + ) + print( + ProductsNames.super_particle_count, + particulator.products[ProductsNames.super_particle_count].get(), + ) + + +def get_prod_dict(particulator): + d = { + ProductsNames.total_number: list( + particulator.products[ProductsNames.total_number].get() + ), + ProductsNames.total_volume: list( + particulator.products[ProductsNames.total_volume].get() + ), + ProductsNames.super_particle_count: list( + particulator.products[ProductsNames.super_particle_count].get() + ), + } + + return d + + +def measure_time_for_each_step(particulator, n_steps): + particulator.run(steps=1) + + res = [] + for _ in range(n_steps): + t0 = time.time() + particulator.run(steps=1) + t1 = time.time() + + res.append(t1 - t0) + + return res + + +def measure_time_per_timestep(particulator, n_steps): + particulator.run(steps=1) + + t0 = time.time() + particulator.run(steps=n_steps) + t1 = time.time() + + return (t1 - t0) / n_steps + + +def go_benchmark( + setup_sim, + n_sds, + n_steps, + seeds, + numba_n_threads=None, + double_precision=True, + sim_run_filename=None, + total_number=None, + dv=None, + time_measurement_fun=measure_time_per_timestep, + backends=(CPU, GPU), +): + products = {} + results = {} + + backend_configs = [] + if CPU in backends: + cpu_backends_configs = [(CPU, i) for i in numba_n_threads] + backend_configs = [*backend_configs, *cpu_backends_configs] + if GPU in backends: + backend_configs.append((GPU, None)) + + for backend_class, n_threads in backend_configs: + backend_name = backend_class().__class__.__name__ + if n_threads: + numba.set_num_threads(n_threads) + backend_name += "_" + str(numba.get_num_threads()) + + results[backend_name] = {} + products[backend_name] = {} + + print() + print("before") + + for n_sd in n_sds: + print("\n") + print(backend_name, n_sd) + + results[backend_name][n_sd] = {} + products[backend_name][n_sd] = {} + + for seed in seeds: + gc.collect() + + particulator = setup_sim( + n_sd, + backend_class, + seed, + double_precision=double_precision, + total_number=total_number, + dv=dv, + ) + + print() + print("products before simulation") + print_all_products(particulator) + + print() + print("start simulation") + + elapsed_time = time_measurement_fun(particulator, n_steps) + + print() + print("products after simulation") + print_all_products(particulator) + + results[backend_name][n_sd][seed] = elapsed_time + products[backend_name][n_sd][seed] = get_prod_dict(particulator) + + gc.collect() + del particulator + gc.collect() + + if sim_run_filename: + write_to_file(filename=f"{sim_run_filename}-products.txt", d=products) + + return results + + +def process_results(res_d, axis=None): + processed_d = {} + for backend in res_d.keys(): + processed_d[backend] = {} + + for n_sd in res_d[backend].keys(): + processed_d[backend][n_sd] = {} + + vals = res_d[backend][n_sd].values() + vals = np.array(list(vals)) + + processed_d[backend][n_sd]["mean"] = np.mean(vals, axis=axis) + processed_d[backend][n_sd]["std"] = np.std(vals, axis=axis) + processed_d[backend][n_sd]["max"] = np.amax(vals, axis=axis) + processed_d[backend][n_sd]["min"] = np.amin(vals, axis=axis) + + return processed_d + + +def write_to_file(filename, d): + assert not os.path.isfile(filename), filename + + with open(filename, "w", encoding="utf-8") as fp: + json.dump(d, fp) + + +class PlottingHelpers: + @staticmethod + def get_backend_markers(backends): + markers = {backend: "o" if "Numba" in backend else "x" for backend in backends} + return markers + + @staticmethod + def get_sorted_backend_list(processed_d): + backends = list(processed_d.keys()) + + backends.sort() + backends.sort(key=lambda x: int(x[6:]) if "Numba_" in x else 100**10) + + return backends + + @staticmethod + def get_n_sd_list(backends, processed_d): + x = [] + + for backend in backends: + for n_sd in processed_d[backend].keys(): + if n_sd not in x: + x.append(n_sd) + + x.sort() + return x + + +def plot_processed_results( + processed_d, + *, + plot_label="", + plot_title=None, + metric="min", + plot_filename, + markers=None, + colors=None, +): + backends = PlottingHelpers.get_sorted_backend_list(processed_d) + + if markers is None: + markers = PlottingHelpers.get_backend_markers(backends) + + x = PlottingHelpers.get_n_sd_list(backends, processed_d) + + for backend in backends: + y = [] + for n_sd in x: + v = processed_d[backend][n_sd][metric] + assert isinstance(v, (float, int)), "must be scalar" + y.append(v) + + if colors: + pyplot.plot( + x, + y, + label=backend + plot_label, + marker=markers[backend], + color=colors[backend], + linewidth=2, + ) + else: + pyplot.plot( + x, y, label=backend + plot_label, marker=markers[backend], linewidth=2 + ) + + pyplot.legend() + pyplot.xscale("log", base=2) + pyplot.yscale("log", base=2) + pyplot.ylim(bottom=2**-15, top=2**3) + + pyplot.grid() + pyplot.xticks(x) + pyplot.xlabel("number of super-droplets") + pyplot.ylabel("wall time per timestep [s]") + + if plot_title: + pyplot.title(plot_title) + + pyplot.savefig(plot_filename) + + +def plot_processed_on_same_plot(coal_d, break_d, coal_break_d): + filename = "same_plot.svg" + plot_processed_results(coal_d, plot_label="-c", plot_filename=filename) + plot_processed_results(break_d, plot_label="-b", plot_filename=filename) + plot_processed_results(coal_break_d, plot_label="-cb", plot_filename=filename) + + +def plot_time_per_step( + processed_d, + n_sd, + *, + plot_label="", + plot_title=None, + metric="mean", + plot_filename, + step_from_to=None, +): + backends = PlottingHelpers.get_sorted_backend_list(processed_d) + markers = PlottingHelpers.get_backend_markers(backends) + + for backend in backends: + y = processed_d[backend][n_sd][metric] + x = np.arange(len(y)) + + if step_from_to is not None: + x = x[step_from_to[0] : step_from_to[1]] + y = y[step_from_to[0] : step_from_to[1]] + + pyplot.plot(x, y, label=backend + plot_label, marker=markers[backend]) + + pyplot.legend() + pyplot.grid() + pyplot.xticks(x) + pyplot.xlabel("number of super-droplets") + pyplot.ylabel("wall time per timestep [s]") + + if plot_title: + pyplot.title(plot_title + f"(n_sd: {n_sd})") + + pyplot.savefig(plot_filename) diff --git a/PySDM/source/examples/PySDM_examples/Ervens_and_Feingold_2012/__init__.py b/PySDM/source/examples/PySDM_examples/Ervens_and_Feingold_2012/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..778a043371355ad9cb6fbe84f513754bd6c15ba1 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Ervens_and_Feingold_2012/__init__.py @@ -0,0 +1,6 @@ +""" +aerosol distribution from +[Ervens and Feingold 2012 (Atmos. Chem. Phys. 12)](https://doi.org/10.5194/acp-12-5807-2012) +""" + +# pylint: disable=invalid-name diff --git a/PySDM/source/examples/PySDM_examples/Ervens_and_Feingold_2012/settings.py b/PySDM/source/examples/PySDM_examples/Ervens_and_Feingold_2012/settings.py new file mode 100644 index 0000000000000000000000000000000000000000..9872afb77750e8abf6ec336cbf83c974dc7cd896 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Ervens_and_Feingold_2012/settings.py @@ -0,0 +1,12 @@ +from PySDM.initialisation.sampling.spectral_sampling import Logarithmic +from PySDM.initialisation.spectra import Lognormal +from PySDM.physics import si + + +def sampled_ccn_diameter_number_concentration_spectrum( + n_sd: int = 11, size_range: tuple = (0.02 * si.um, 2 * si.um) +): + return Logarithmic( + spectrum=Lognormal(s_geom=1.4, m_mode=0.04 * si.um, norm_factor=100 / si.cm**3), + size_range=size_range, + ).sample_deterministic(n_sd) diff --git a/PySDM/source/examples/PySDM_examples/Fisher_1991/__init__.py b/PySDM/source/examples/PySDM_examples/Fisher_1991/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d3c57844280b8ff1c18f5f080c533011ad315b0b --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Fisher_1991/__init__.py @@ -0,0 +1,11 @@ +# pylint: disable=invalid-name +""" +based on the [Jouzel and Merlivat 1984](https://doi.org/10.1029/JD089iD07p11749) calculations +of delta_{18O} and delta_D used in d-excess formula and solved +by integration of the eq. (1) in +[Fisher 1991](https://doi.org/10.3402/tellusb.v43i5.15414) and eq. (3) in Jouzel and Merlivat 1984 + + +fig_2.ipynb: +.. include:: ./fig_2.ipynb.badges.md +""" diff --git a/PySDM/source/examples/PySDM_examples/Fisher_1991/fig_2.ipynb b/PySDM/source/examples/PySDM_examples/Fisher_1991/fig_2.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..ceaab1041ee6b3efea19ef4fb2f5ff7638641be0 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Fisher_1991/fig_2.ipynb @@ -0,0 +1,3161 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "4fecbc8260ee990d", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Fisher_1991/fig_2.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Fisher_1991/fig_2.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Fisher_1991/fig_2.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "a290c85256f4f983", + "metadata": {}, + "source": [ + "# based on Fig 2. in [Fisher 1991](https://doi.org/10.3402/tellusb.v43i5.15414) \"_Remarks on the deuterium excess in precipitation in cold regions_\"\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "31481014375c7d36", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-25T12:41:17.048483Z", + "start_time": "2025-06-25T12:41:17.041726Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "c36c2f5c-17c1-422e-9ab9-f7d612064199", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-25T12:41:21.768860Z", + "start_time": "2025-06-25T12:41:17.056517Z" + } + }, + "outputs": [], + "source": [ + "from matplotlib import pyplot\n", + "import numpy as np\n", + "from open_atmos_jupyter_utils import show_plot\n", + "from scipy.integrate import solve_ivp\n", + "from functools import partial\n", + "\n", + "from PySDM import Formulae\n", + "from PySDM.physics import si\n", + "from PySDM.physics.constants import PER_MILLE, in_unit\n", + "from PySDM_examples.Jouzel_and_Merlivat_1984.thermodynamic_profiles import (\n", + " vapour_mixing_ratio,\n", + " ice_saturation_curve_4\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "91bb290498429f53", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-25T12:41:21.875872Z", + "start_time": "2025-06-25T12:41:21.845639Z" + } + }, + "outputs": [], + "source": [ + "formulae= Formulae(\n", + " isotope_meteoric_water_line=\"Dansgaard1964\",\n", + " isotope_diffusivity_ratios=\"Stewart1975\",\n", + " isotope_kinetic_fractionation_factors=\"JouzelAndMerlivat1984\",\n", + " isotope_equilibrium_fractionation_factors=\"MerlivatAndNief1967+Majoube1970\",\n", + ")\n", + "const = formulae.constants\n", + "K2C = formulae.trivia.K2C\n", + "\n", + "diffusivity_ratio = {}\n", + "alpha_eq = {}\n", + "alpha_kinetic = {}\n", + "isotopes = (\"2H\", \"18O\")\n", + "for isotope in isotopes:\n", + " alpha_eq[isotope] = getattr(formulae.isotope_equilibrium_fractionation_factors, f'alpha_i_{isotope}')\n", + " diffusivity_ratio[isotope] = getattr(formulae.isotope_diffusivity_ratios, f'ratio_{isotope}_heavy_to_light')" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "754af83e4ab216bc", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-25T12:41:21.884154Z", + "start_time": "2025-06-25T12:41:21.881306Z" + } + }, + "outputs": [], + "source": [ + "def alpha_kin(iso, T):\n", + " return formulae.isotope_kinetic_fractionation_factors.alpha_kinetic(\n", + " alpha_equilibrium = alpha_eq[iso](T),\n", + " D_ratio_heavy_to_light=diffusivity_ratio[iso](T),\n", + " saturation = ice_saturation_curve_4(const=const, T=T)\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "9ca4e3256065274b", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-25T12:41:21.894710Z", + "start_time": "2025-06-25T12:41:21.891746Z" + } + }, + "outputs": [], + "source": [ + "def d_delta_dT(T, delta):\n", + " y = yf(T=T)\n", + " dT = 1 * si.K\n", + " dy_dT = (yf(T=T + dT) - y) / dT\n", + " res = [0, 0]\n", + " for i, iso in enumerate(isotopes):\n", + " alpha = alpha_eq[iso](T) * alpha_kin(iso, T)\n", + " alpha_dT = alpha_eq[iso](T+dT) * alpha_kin(iso, T+dT)\n", + " d_alpha_dT = (alpha_dT - alpha) / dT\n", + " res[i] = (\n", + " (1 + delta[i])\n", + " * (alpha * (alpha - 1) * dy_dT + y * d_alpha_dT)\n", + " / (alpha * (y + alpha * y_e))\n", + " )\n", + " return res" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "71979cfe7348344d", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-25T12:41:21.901806Z", + "start_time": "2025-06-25T12:41:21.899568Z" + } + }, + "outputs": [], + "source": [ + "delta_18O_0 = -15 * PER_MILLE\n", + "delta_2H_0 = const.CRAIG_1961_SLOPE_COEFF * delta_18O_0\n", + "temperature = np.linspace(260, 225, 10) * si.K\n", + "\n", + "y_e = 0\n", + "yf = partial(vapour_mixing_ratio, formulae)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "4e112378083e50f5", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-25T12:41:25.203838Z", + "start_time": "2025-06-25T12:41:21.907105Z" + } + }, + "outputs": [], + "source": [ + "result = solve_ivp(\n", + " fun=d_delta_dT,\n", + " t_span=(temperature[0], temperature[-1]),\n", + " y0=[delta_2H_0, delta_18O_0],\n", + " t_eval=temperature\n", + ")\n", + "assert result.success, result.message\n", + "delta_2H, delta_18O = result.y\n", + "d_excess = formulae.isotope_meteoric_water_line.excess_d(\n", + " delta_2H=delta_2H,\n", + " delta_18O=delta_18O\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "153a6e26bc2be38a", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-25T12:41:25.731698Z", + "start_time": "2025-06-25T12:41:25.217926Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-06-25T14:41:25.713352\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.9.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "d85508aab73e4fbc91cf47f982b6d8a7", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_1.pdf
\"), HTML(value=\"./fig_2.pdf
\"), HTML(value=\"\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-05-22T19:20:34.495170\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.0, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "d97571d624ee4aaebef6ee975d561949", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_2.pdf
\"), HTML(value=\"\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-05-22T19:20:37.911031\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.0, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "221ba44bdc9d4b98bf3f1529b41caa08", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./plot_grid.pdf
\"), HTML(value=\"\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-03-05T01:26:57.473976\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "1cf12b98d3a24dc8a9232faaf8b9d3b3", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./top_panels.pdf
\"), HTML(value=\"…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "temperature = 300 * si.K\n", + "delta_0 = {\n", + " '2H': -38 * PER_MILLE,\n", + " '18O': -6 * PER_MILLE,\n", + "}\n", + "delta_a = {\n", + " '2H': -86 * PER_MILLE,\n", + " '18O': -12 * PER_MILLE,\n", + "}\n", + "plot_x = np.linspace(1, .01, 50)\n", + "humidities = (0, .25, .5, .75, .95)\n", + "\n", + "fig, axs = pyplot.subplot_mosaic([delta_0.keys()], tight_layout=True)\n", + "plot_y = {}\n", + "for isotope, delta_0_i in delta_0.items():\n", + " plot_y[isotope] = {}\n", + " for h in humidities:\n", + " plot_y[isotope][h] = delta(\n", + " delta_liq_0=delta_0_i,\n", + " remaining_water_fraction=plot_x,\n", + " humidity=h,\n", + " delta_atmos=delta_a[isotope],\n", + " alpha=getattr(formulae.isotope_equilibrium_fractionation_factors, f'alpha_l_{isotope}')(temperature),\n", + " D_ratio_vs_light=getattr(formulae.isotope_diffusivity_ratios, f'ratio_{isotope}_heavy_to_light')(temperature)\n", + " )\n", + " axs[isotope].plot(\n", + " plot_x,\n", + " in_unit(plot_y[isotope][h], PER_MILLE),\n", + " label=f'RH={h*100:.2g}%',\n", + " color='k',\n", + " linewidth=1 + h,\n", + " )\n", + " axs[isotope].set_xlim(1, 0)\n", + " axs[isotope].set_xlabel(\"fraction of remaining water\")\n", + " axs[isotope].grid()\n", + "axs[\"2H\"].legend()\n", + "axs[\"2H\"].set_ylabel('$\\\\delta^2H$ [‰]')\n", + "axs[\"18O\"].set_ylabel('$\\\\delta^{18}O$ [‰]')\n", + "axs[\"2H\"].set_ylim(-50, 200)\n", + "axs[\"18O\"].set_ylim(-10, 40)\n", + "show_plot('top_panels.pdf')" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "45565e875bbe0629", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-04T18:02:37.550956Z", + "start_time": "2025-03-04T18:02:37.386737Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-03-05T01:26:58.547158\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "1020ebafd9e64488be20ea5ce251af1c", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./bottom_panel.pdf
\"), HTML(val…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = pyplot.subplots(figsize=(7,3.5))\n", + "x = np.linspace(-12, 0, 20) * PER_MILLE\n", + "ax.plot(\n", + " in_unit(x, PER_MILLE),\n", + " in_unit(const.CRAIG_1961_SLOPE_COEFF * x + const.CRAIG_1961_INTERCEPT_COEFF, PER_MILLE),\n", + " label = f'y = {const.CRAIG_1961_SLOPE_COEFF}x + {in_unit(const.CRAIG_1961_INTERCEPT_COEFF, PER_MILLE):.2g}',\n", + " color= 'orange',\n", + " linewidth=2\n", + ")\n", + "for h in reversed(humidities):\n", + " bottom_plot = ax.plot(\n", + " in_unit(plot_y['18O'][h], PER_MILLE), \n", + " in_unit(plot_y['2H'][h], PER_MILLE),\n", + " label=f'RH={h*100:.2g}%',\n", + " color='k',\n", + " linewidth=1 + h\n", + " )\n", + "ax.set_xlim(-10, 30)\n", + "ax.set_ylim(-50, 150)\n", + "ax.set_xlabel('$\\\\delta ^{18}O$ [‰]')\n", + "ax.set_ylabel('$\\\\delta ^2H$ [‰]')\n", + "ax.grid()\n", + "ax.legend()\n", + "show_plot(\"bottom_panel.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4fbe39f7-c935-4ceb-a503-99f6da19450e", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/PySDM/source/examples/PySDM_examples/Grabowski_and_Pawlowska_2023/__init__.py b/PySDM/source/examples/PySDM_examples/Grabowski_and_Pawlowska_2023/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..05c050fdfb091ca36c46efead9e44d7ef5043d0a --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Grabowski_and_Pawlowska_2023/__init__.py @@ -0,0 +1,22 @@ +# pylint: disable=invalid-name +""" +ripening-focused parcel-model example based on +[Grabowski & Pawlowska 2023 (GRL)](https://doi.org/10.1029/2022GL101917) + +figure_1.ipynb: +.. include:: ./figure_1.ipynb.badges.md + +figure_2.ipynb: +.. include:: ./figure_2.ipynb.badges.md + +figure_3.ipynb: +.. include:: ./figure_3.ipynb.badges.md + +figure_4.ipynb: +.. include:: ./figure_4.ipynb.badges.md + +figure_ripening_rate.ipynb: +.. include:: ./figure_ripening_rate.ipynb.badges.md +""" +from .settings import Settings +from .simulation import Simulation diff --git a/PySDM/source/examples/PySDM_examples/Grabowski_and_Pawlowska_2023/figure_1.ipynb b/PySDM/source/examples/PySDM_examples/Grabowski_and_Pawlowska_2023/figure_1.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..52575c6b6145a26261295d9987f038f6151f9a10 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Grabowski_and_Pawlowska_2023/figure_1.ipynb @@ -0,0 +1,11756 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Grabowski_and_Pawlowska_2023/figure_1.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Grabowski_and_Pawlowska_2023/figure_1.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Grabowski_and_Pawlowska_2023/figure_1.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### based on Fig. 1 from Wojciech Grabowski and Hanna Pawlowska 2023 (Geophysical Research Letters 50(3)) 'Adiabatic Evolution of Cloud Droplet Spectral Width: A New Look at an Old Problem'\n", + "\n", + "https://doi.org/10.1029/2022GL101917" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:33:00.814600Z", + "start_time": "2024-02-01T07:33:00.806057Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:34:06.001360Z", + "start_time": "2024-02-01T07:34:05.994758Z" + } + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "from matplotlib import pyplot\n", + "from matplotlib import ticker\n", + "from open_atmos_jupyter_utils import show_plot\n", + "\n", + "from PySDM import Formulae\n", + "from PySDM.physics import si, in_unit\n", + "from PySDM.products import ParcelDisplacement, AmbientRelativeHumidity\n", + "\n", + "TRIVIA = Formulae().trivia\n", + "\n", + "from PySDM_examples.Grabowski_and_Pawlowska_2023 import Settings, Simulation" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:33:30.631530Z", + "start_time": "2024-02-01T07:33:03.622223Z" + } + }, + "outputs": [], + "source": [ + "products = (\n", + " ParcelDisplacement(name='z'),\n", + " AmbientRelativeHumidity(name='S_max_percent', unit='%', var='RH'),\n", + ")\n", + "\n", + "vertical_velocity = 0.25 * si.m / si.s\n", + "output = {\n", + " case: Simulation(Settings(\n", + " vertical_velocity=vertical_velocity,\n", + " dt=1*si.s if 'CI' not in os.environ else 50 * si.s,\n", + " n_sd=200 if 'CI' not in os.environ else 10,\n", + " aerosol=case\n", + " ), products=products).run()\n", + " for case in (\"pristine\", \"polluted\")\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:33:34.961425Z", + "start_time": "2024-02-01T07:33:30.667482Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-07-04T15:54:39.100773\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "a505ec117514460dae4b24fa6ba1bf86", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./fig1.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# pylint: disable=too-many-arguments\n", + "def plot_R(axes, output_vol, output_crit_vol, output_z, k):\n", + " for drop_id, volume in enumerate(output_vol):\n", + " if drop_id % k == 0:\n", + " if TRIVIA.radius(volume=volume)[10] > 0.03 * si.um:\n", + " crit_volume = output_crit_vol[drop_id]\n", + " if np.all(volume0.03*si.um:\n", + " if np.all(volume\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-07-04T15:59:52.642019\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "6fc7aa7e9fea41bfa2528da70426c10e", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./fig2.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# pylint: disable=too-many-arguments\n", + "def plot_R(axes, output_vol, output_crit_vol, output_z, k):\n", + " for drop_id, volume in enumerate(output_vol):\n", + " if drop_id%k==0:\n", + " if TRIVIA.radius(volume=volume)[10] > 0.03*si.um:\n", + " crit_volume=output_crit_vol[drop_id]\n", + " if np.all(volume0.03*si.um:\n", + " if np.all(volume\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-07-04T08:00:22.142648\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "7cf7959fa32048d0aba3deda69994530", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./fig3.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, axs = pyplot.subplots(3, 2, figsize=(13, 10))\n", + "axN1, axN2, axR1, axR2, axA1, axA2 = axs[0,0], axs[0,1], axs[1,0], axs[1,1], axs[2,0], axs[2,1]\n", + "\n", + "for i, aerosol in enumerate((\"polluted\", \"pristine\")):\n", + " for w in vertical_velocity:\n", + " r = np.array(output[w][aerosol]['products']['r_vol'])\n", + " n = np.array(output[w][aerosol]['products']['n_act'])\n", + " a = np.array(output[w][aerosol]['products']['area_std'])/(4 * np.pi)\n", + " z = np.array(output[w][aerosol]['products']['z'])\n", + " axs[1, i].plot(z, np.where(r>2*si.um, in_unit(r, si.um), np.nan))\n", + " axs[0, i].plot(z, np.where(r>2*si.um, in_unit(n, si.cm**-3), np.nan))\n", + " axs[2, i].plot(z, np.where(r>2*si.um, in_unit(a, si.um**2), np.nan))\n", + " z_last = z\n", + " axs[1,i].plot(\n", + " z_last,\n", + " np.where(r>2*si.um, 2 * z_last**(1/3), np.nan),\n", + " color='grey',\n", + " linestyle='--'\n", + " )\n", + " axs[2,i].plot(\n", + " z_last,\n", + " np.where(r>2*si.um, 20, np.nan),\n", + " color='grey',\n", + " linestyle='--'\n", + " )\n", + "\n", + "for ax in (axN1, axN2, axR1, axR2, axA1, axA2):\n", + " ax.set_xlim(10, 1000)\n", + " ax.set_xscale('log')\n", + " ax.legend(legend)\n", + "for ax in [axR1,axR2]:\n", + " ax.set_ylabel('mean volume radius [μm]')\n", + "for ax in [axN1,axN2]:\n", + " ax.set_ylabel('concentration [cm$^{-3}$ STP]')\n", + "for ax in [axA1,axA2]:\n", + " ax.set_xlabel('height [m]')\n", + " ax.set_ylabel(r'area st. dev. / 4$\\pi$ [μm$^2$]')\n", + " \n", + "axN1_, axN2_, axR1_, axR2_, axA1_, axA2_ = axN1.twinx(), axN2.twinx(), axR1.twinx(), axR2.twinx(), axA1.twinx(), axA2.twinx()\n", + "for ax in (axR1, axR2, axR1_, axR2_, axA1, axA2, axA1_, axA2_):\n", + " ax.set_yscale('log')\n", + " ax.set_ylim(1, 100)\n", + "for ax in (axN1, axN1_):\n", + " ax.set_ylim(300, 600)\n", + "for ax in (axN2, axN2_):\n", + " ax.set_ylim(0, 300)\n", + "axN1.set_title('POL')\n", + "axN2.set_title('PRI')\n", + "for ax in (axN1_, axN2_, axR1_, axR2_, axA1_, axA2_):\n", + " ax.yaxis.set_major_formatter(ticker.NullFormatter())\n", + "show_plot(\"fig3.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/PySDM/source/examples/PySDM_examples/Grabowski_and_Pawlowska_2023/figure_4.ipynb b/PySDM/source/examples/PySDM_examples/Grabowski_and_Pawlowska_2023/figure_4.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..204f6f145833288abd89ba0506cefa5382bc1d84 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Grabowski_and_Pawlowska_2023/figure_4.ipynb @@ -0,0 +1,2279 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Grabowski_and_Pawlowska_2023/figure_4.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Grabowski_and_Pawlowska_2023/figure_4.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Grabowski_and_Pawlowska_2023/figure_4.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### based on Fig. 4 from Wojciech Grabowski and Hanna Pawlowska 2023 (Geophysical Research Letters 50(3)) 'Adiabatic Evolution of Cloud Droplet Spectral Width: A New Look at an Old Problem'\n", + "\n", + "https://doi.org/10.1029/2022GL101917" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:32:54.227488Z", + "start_time": "2024-02-01T07:32:54.224383Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:33:47.088494Z", + "start_time": "2024-02-01T07:33:47.085534Z" + } + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "from matplotlib import pyplot, ticker\n", + "from open_atmos_jupyter_utils import show_plot\n", + "\n", + "from PySDM.physics import si\n", + "from PySDM.products import ParcelDisplacement, ActivatedMeanRadius, RadiusStandardDeviation\n", + "from PySDM_examples.Grabowski_and_Pawlowska_2023 import Settings, Simulation" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:35:01.224144Z", + "start_time": "2024-02-01T07:34:32.159387Z" + } + }, + "outputs": [], + "source": [ + "products=(\n", + " ParcelDisplacement(name='z'),\n", + " ActivatedMeanRadius(name='r_act',count_activated=True, count_unactivated=False),\n", + " RadiusStandardDeviation(name=\"radius_std\", count_activated=True, count_unactivated=False),\n", + ")\n", + "vertical_velocity = (\"0.25\", \"1\", \"4\")\n", + "output = { velocity:\n", + " {\n", + " case: Simulation(Settings(\n", + " vertical_velocity=float(velocity),\n", + " dt=1*si.s if 'CI' not in os.environ else 50 * si.s,\n", + " n_sd=200 if 'CI' not in os.environ else 10,\n", + " aerosol=case\n", + " ), products=products).run()\n", + " for case in (\"pristine\", \"polluted\")\n", + " }\n", + " for velocity in vertical_velocity\n", + "}\n", + "legend = [velocity + ' m/s' for velocity in vertical_velocity]" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:33:47.079285Z", + "start_time": "2024-02-01T07:33:45.490499Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-07-04T16:08:57.672821\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ff75786e93ce4fe2b0ec4b6e4b56de15", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./fig4.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, axs = pyplot.subplots(2, 1, figsize=(6, 8))\n", + "np.seterr(invalid=\"ignore\")\n", + "for axis, aerosol in ((axs[0], \"pristine\"), (axs[1], \"polluted\")):\n", + " for w in vertical_velocity:\n", + " r=np.array(output[w][aerosol]['products']['r_act'])\n", + " d=np.array(output[w][aerosol]['products']['radius_std'])\n", + " axis.plot(\n", + " np.array(output[w][\"polluted\"]['products']['z']),\n", + " np.where(r > 2 * si.um, np.divide(d,r), np.nan)\n", + " )\n", + "\n", + "for ax in axs:\n", + " ax.set_xscale('log')\n", + " ax.set_yscale('log')\n", + " ax.set_ylim(0.01, 1)\n", + " ax.set_xlim(10, 1000)\n", + " ax.set_ylabel('rel. dispersion')\n", + " ax.legend(legend)\n", + "axs[1].set_xlabel('height [m]')\n", + "axs[0].set_title('PRI')\n", + "axs[1].set_title('POL')\n", + "\n", + "ax0, ax1 = axs[0].twinx(), axs[1].twinx()\n", + "for ax in (ax0, ax1):\n", + " ax.set_yscale('log')\n", + " ax.set_ylim(0.01, 1)\n", + " ax.yaxis.set_major_formatter(ticker.NullFormatter())\n", + "show_plot(\"fig4.pdf\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/PySDM/source/examples/PySDM_examples/Grabowski_and_Pawlowska_2023/figure_ripening_rate.ipynb b/PySDM/source/examples/PySDM_examples/Grabowski_and_Pawlowska_2023/figure_ripening_rate.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..95080394700ad36c1adca1cc691b9ffb950c16ad --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Grabowski_and_Pawlowska_2023/figure_ripening_rate.ipynb @@ -0,0 +1,13825 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Grabowski_and_Pawlowska_2023/figure_ripening_rate.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Grabowski_and_Pawlowska_2023/figure_ripening_rate.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Grabowski_and_Pawlowska_2023/figure_ripening_rate.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### based on figures from Wojciech Grabowski and Hanna Pawlowska 2023 (Geophysical Research Letters 50(3)) 'Adiabatic Evolution of Cloud Droplet Spectral Width: A New Look at an Old Problem'\n", + "\n", + "https://doi.org/10.1029/2022GL101917" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:32:49.758270Z", + "start_time": "2024-02-01T07:32:49.753751Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:33:41.208496Z", + "start_time": "2024-02-01T07:33:41.204376Z" + } + }, + "outputs": [], + "source": [ + "from matplotlib import pyplot\n", + "from open_atmos_jupyter_utils import show_plot\n", + "from PySDM.physics import si\n", + "from PySDM.products import ParcelDisplacement, RipeningRate\n", + "from PySDM_examples.Grabowski_and_Pawlowska_2023 import Settings, Simulation" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:33:39.121598Z", + "start_time": "2024-02-01T07:32:51.956620Z" + } + }, + "outputs": [], + "source": [ + "products = (\n", + " ParcelDisplacement(name='z'),\n", + " RipeningRate(name='ripening')\n", + ")\n", + "\n", + "vertical_velocity = (\"0.25\",\"1\",\"4\")\n", + "output = {\n", + " velocity: {\n", + " case: Simulation(Settings(\n", + " vertical_velocity=float(velocity),\n", + " dt=1*si.s if 'CI' not in os.environ else 50 * si.s,\n", + " n_sd=200 if 'CI' not in os.environ else 10,\n", + " aerosol=case\n", + " ), products=products).run()\n", + " for case in (\"pristine\", \"polluted\")\n", + " }\n", + " for velocity in vertical_velocity\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:33:40.919953Z", + "start_time": "2024-02-01T07:33:39.123726Z" + }, + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-07-04T16:07:41.828355\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "2738292b897646d4889191e6658f4274", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./fig_ripening.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, axs = pyplot.subplots(1, 3, figsize=(15, 5))\n", + "\n", + "for aerosol in (\"polluted\", \"pristine\"):\n", + " for i, velocity in enumerate(vertical_velocity):\n", + " axs[i].scatter(\n", + " output[velocity][aerosol]['products']['z'],\n", + " output[velocity][aerosol]['products']['ripening']\n", + " )\n", + " axs[i].set_title(\"w = \" + velocity+\" m/s\")\n", + "\n", + "for ax in axs:\n", + " ax.set_xlim(10, 1000)\n", + " ax.set_ylim(0, 5e8)\n", + " ax.set_xscale('log')\n", + " ax.set_xlabel('height [m]')\n", + " ax.set_ylabel('ripening rate [s kg]$^{-1}$')\n", + " ax.legend([\"polluted\", \"pristine\"])\n", + "show_plot(\"fig_ripening.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:33:41.195154Z", + "start_time": "2024-02-01T07:33:40.919286Z" + }, + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-07-04T16:11:00.993049\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "0bf3dfd00c7641c7b2e7f726f9151362", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./fig_ripening_zoom.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "for aerosol in (\"polluted\", \"pristine\"):\n", + " pyplot.scatter(\n", + " output[\"0.25\"][aerosol]['products']['z'],\n", + " output[\"0.25\"][aerosol]['products']['ripening']\n", + " )\n", + "pyplot.title(\"w = 0.25 m/s\")\n", + "pyplot.xscale('log')\n", + "pyplot.yscale('log')\n", + "pyplot.xlim(10, 1000)\n", + "pyplot.xlabel('height [m]')\n", + "pyplot.ylabel('ripening rate [s kg]$^{-1}$')\n", + "pyplot.legend([\"polluted\", \"pristine\"])\n", + "show_plot('fig_ripening_zoom.pdf')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/PySDM/source/examples/PySDM_examples/Grabowski_and_Pawlowska_2023/settings.py b/PySDM/source/examples/PySDM_examples/Grabowski_and_Pawlowska_2023/settings.py new file mode 100644 index 0000000000000000000000000000000000000000..c8bf10a2ee2630ddd135ee17b5051a6fb2ac8271 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Grabowski_and_Pawlowska_2023/settings.py @@ -0,0 +1,98 @@ +import numpy as np +from pystrict import strict + +from PySDM import Formulae +from PySDM.dynamics import condensation +from PySDM.initialisation.spectra import Lognormal, Sum +from PySDM.physics import si + +condensation_tolerance = condensation.DEFAULTS.rtol_thd / 100 + + +@strict +class Settings: + def __init__( + self, + *, + aerosol: str, + vertical_velocity: float, + dt: float, + n_sd: int, + initial_temperature: float = 283 * si.K, + initial_pressure: float = 900 * si.mbar, + initial_relative_humidity: float = 0.97, + displacement: float = 1000 * si.m, + mass_accommodation_coefficient: float = 0.3, + rtol_thd: float = condensation_tolerance, + rtol_x: float = condensation_tolerance, + ): + self.formulae = Formulae(constants={"MAC": mass_accommodation_coefficient}) + self.n_sd = n_sd + self.aerosol_modes_by_kappa = { + "pristine": { + 1.28: Sum( + ( + Lognormal( + norm_factor=125 / si.cm**3, m_mode=11 * si.nm, s_geom=1.2 + ), + Lognormal( + norm_factor=65 / si.cm**3, m_mode=60 * si.nm, s_geom=1.7 + ), + ) + ) + }, + "polluted": { + 1.28: Sum( + ( + Lognormal( + norm_factor=160 / si.cm**3, m_mode=29 * si.nm, s_geom=1.36 + ), + Lognormal( + norm_factor=380 / si.cm**3, m_mode=71 * si.nm, s_geom=1.57 + ), + ) + ) + }, + }[aerosol] + + const = self.formulae.constants + self.vertical_velocity = vertical_velocity + self.initial_pressure = initial_pressure + self.initial_temperature = initial_temperature + pv0 = ( + initial_relative_humidity + * self.formulae.saturation_vapour_pressure.pvs_water(initial_temperature) + ) + self.initial_vapour_mixing_ratio = const.eps * pv0 / (initial_pressure - pv0) + self.t_max = displacement / vertical_velocity + self.timestep = dt + self.output_interval = self.timestep + self.rtol_thd = rtol_thd + self.rtol_x = rtol_x + + @property + def initial_air_density(self): + const = self.formulae.constants + dry_air_density = ( + self.formulae.trivia.p_d( + self.initial_pressure, self.initial_vapour_mixing_ratio + ) + / self.initial_temperature + / const.Rd + ) + return dry_air_density * (1 + self.initial_vapour_mixing_ratio) + + @property + def nt(self) -> int: + nt = self.t_max / self.timestep + nt_int = round(nt) + np.testing.assert_almost_equal(nt, nt_int) + return nt_int + + @property + def steps_per_output_interval(self) -> int: + return int(self.output_interval / self.timestep) + + @property + def output_steps(self) -> np.ndarray: + return np.arange(0, self.nt + 1, self.steps_per_output_interval) diff --git a/PySDM/source/examples/PySDM_examples/Grabowski_and_Pawlowska_2023/simulation.py b/PySDM/source/examples/PySDM_examples/Grabowski_and_Pawlowska_2023/simulation.py new file mode 100644 index 0000000000000000000000000000000000000000..93348f3149cd1afd4591197a9e481222cc66b050 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Grabowski_and_Pawlowska_2023/simulation.py @@ -0,0 +1,116 @@ +import numpy as np +from PySDM_examples.utils import BasicSimulation + +from PySDM import Builder +from PySDM.backends import CPU +from PySDM.backends.impl_numba.test_helpers import scipy_ode_condensation_solver +from PySDM.dynamics import AmbientThermodynamics, Condensation +from PySDM.environments import Parcel +from PySDM.initialisation.hygroscopic_equilibrium import equilibrate_wet_radii +from PySDM.initialisation.sampling.spectral_sampling import ConstantMultiplicity +from PySDM.physics import si + + +class Simulation(BasicSimulation): + def __init__( + self, + settings, + products=None, + scipy_solver=False, + ): + builder = Builder( + n_sd=settings.n_sd, + backend=CPU( + formulae=settings.formulae, override_jit_flags={"parallel": False} + ), + environment=Parcel( + dt=settings.timestep, + p0=settings.initial_pressure, + initial_water_vapour_mixing_ratio=settings.initial_vapour_mixing_ratio, + T0=settings.initial_temperature, + w=settings.vertical_velocity, + mass_of_dry_air=44 * si.kg, + ), + ) + builder.add_dynamic(AmbientThermodynamics()) + builder.add_dynamic( + Condensation(rtol_thd=settings.rtol_thd, rtol_x=settings.rtol_x) + ) + for attribute in ( + "critical saturation", + "equilibrium saturation", + "critical volume", + ): + builder.request_attribute(attribute) + + env = builder.particulator.environment + volume = env.mass_of_dry_air / settings.initial_air_density + attributes = { + k: np.empty(0) + for k in ("dry volume", "kappa times dry volume", "multiplicity") + } + + assert len(settings.aerosol_modes_by_kappa.keys()) == 1 + kappa = tuple(settings.aerosol_modes_by_kappa.keys())[0] + spectrum = settings.aerosol_modes_by_kappa[kappa] + + r_dry, n_per_volume = ConstantMultiplicity(spectrum).sample_deterministic( + settings.n_sd + ) + v_dry = settings.formulae.trivia.volume(radius=r_dry) + attributes["multiplicity"] = np.append( + attributes["multiplicity"], n_per_volume * volume + ) + attributes["dry volume"] = np.append(attributes["dry volume"], v_dry) + attributes["kappa times dry volume"] = np.append( + attributes["kappa times dry volume"], v_dry * kappa + ) + r_wet = equilibrate_wet_radii( + r_dry=settings.formulae.trivia.radius(volume=attributes["dry volume"]), + environment=env, + kappa_times_dry_volume=attributes["kappa times dry volume"], + ) + attributes["volume"] = settings.formulae.trivia.volume(radius=r_wet) + + super().__init__( + particulator=builder.build(attributes=attributes, products=products) + ) + if scipy_solver: + scipy_ode_condensation_solver.patch_particulator(self.particulator) + + self.output_attributes = { + "volume": tuple([] for _ in range(self.particulator.n_sd)), + "dry volume": tuple([] for _ in range(self.particulator.n_sd)), + "critical saturation": tuple([] for _ in range(self.particulator.n_sd)), + "equilibrium saturation": tuple([] for _ in range(self.particulator.n_sd)), + "critical volume": tuple([] for _ in range(self.particulator.n_sd)), + "multiplicity": tuple([] for _ in range(self.particulator.n_sd)), + } + self.settings = settings + + self.__sanity_checks(attributes, volume) + + def __sanity_checks(self, attributes, volume): + for attribute in attributes.values(): + assert attribute.shape[0] == self.particulator.n_sd + np.testing.assert_approx_equal( + sum(attributes["multiplicity"]) / volume, + sum( + mode.norm_factor + for mode in self.settings.aerosol_modes_by_kappa.values() + ), + significant=4, + ) + + def _save(self, output): + for key, attr in self.output_attributes.items(): + attr_data = self.particulator.attributes[key].to_ndarray() + for drop_id in range(self.particulator.n_sd): + attr[drop_id].append(attr_data[drop_id]) + super()._save(output) + + def run(self): + output_products = super()._run( + self.settings.nt, self.settings.steps_per_output_interval + ) + return {"products": output_products, "attributes": self.output_attributes} diff --git a/PySDM/source/examples/PySDM_examples/Graf_et_al_2019/Table_1.ipynb b/PySDM/source/examples/PySDM_examples/Graf_et_al_2019/Table_1.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..65e8cefdf047927fe958ef231f3f1b5b3c928a26 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Graf_et_al_2019/Table_1.ipynb @@ -0,0 +1,329 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "e0cb5788-74b3-4da4-8095-880d3505c755", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Graf_et_al_2019/Table_1.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Graf_et_al_2019/Table_1.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Graf_et_al_2019/Table_1.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "2e2202e7d784a1cb", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "### reproducing Table 1 from Graf et al. 2019: https://doi.org/10.5194/acp-19-747-2019\n", + "\n", + "Calculated difference in isotopic composition for vapour vs. liquid/solid in equilibrium \n", + "Two different isotopic compositions \n", + "Exemplifies computation of: δ18O, δ2H, excess-d " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "551e99a9150d82e9", + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-06T13:00:58.789303Z", + "start_time": "2024-12-06T13:00:58.763298Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "initial_id", + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-06T13:01:01.045913Z", + "start_time": "2024-12-06T13:00:58.838016Z" + } + }, + "outputs": [], + "source": [ + "from PySDM import Formulae\n", + "from PySDM.physics import si, in_unit\n", + "from IPython.display import display, HTML" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "9e79217e-e30a-4ebe-a14f-532dca3a3d8b", + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-06T13:01:01.624239Z", + "start_time": "2024-12-06T13:01:01.193934Z" + } + }, + "outputs": [], + "source": [ + "formulae = Formulae(\n", + " isotope_equilibrium_fractionation_factors='Majoube1970+Majoube1971+MerlivatAndNief1967',\n", + " isotope_meteoric_water_line='Dansgaard1964'\n", + ")\n", + "const = formulae.constants\n", + "\n", + "alphas = {\n", + " '18O_l': formulae.isotope_equilibrium_fractionation_factors.alpha_l_18O,\n", + " '2H_l': formulae.isotope_equilibrium_fractionation_factors.alpha_l_2H,\n", + " '18O_s': formulae.isotope_equilibrium_fractionation_factors.alpha_i_18O,\n", + " '2H_s': formulae.isotope_equilibrium_fractionation_factors.alpha_i_2H\n", + "}\n", + "excess_d = formulae.isotope_meteoric_water_line.excess_d\n", + "\n", + "CASES = {\n", + " 'A': {'18O': -10 * const.PER_MILLE, '2H': -80 * const.PER_MILLE},\n", + " 'B': {'18O': -25 * const.PER_MILLE, '2H': -200 * const.PER_MILLE}\n", + "}\n", + "for case in CASES.values():\n", + " case['excess'] = excess_d(delta_2H=case['2H'], delta_18O=case['18O'])" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "65fb351f-2bf7-4118-a063-3ea961403db0", + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-06T13:01:02.314607Z", + "start_time": "2024-12-06T13:01:01.649028Z" + } + }, + "outputs": [], + "source": [ + "# see text just below eq. (4) in the paper\n", + "alpha_20C_l = alphas['18O_l'](20 * si.K + const.T0)\n", + "alpha_20C_2H = alphas['2H_l'](20 * si.K + const.T0)\n", + "assert f\"{alpha_20C_l:.4f}\" == \"1.0098\"\n", + "assert f\"{alpha_20C_2H:.4f}\" == \"1.0850\"" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "dbbf7dc7ddffe08", + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-06T13:01:03.131925Z", + "start_time": "2024-12-06T13:01:02.329497Z" + }, + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "table_data = {}\n", + "for TC in (20, 0):\n", + " table_data[TC] = {}\n", + " for phase in (('s', 'l') if TC==0 else ('l',)):\n", + " table_data[TC][f'{phase}_v'] = {}\n", + " for case, delta_v in CASES.items():\n", + " case_data = table_data[TC][f'{phase}_v'][case] = {}\n", + " for isotope in ('18O', '2H'):\n", + " reference_ratio = getattr(const, f\"VSMOW_R_{isotope}\")\n", + " Rv = formulae.trivia.isotopic_delta_2_ratio(\n", + " delta=delta_v[isotope],\n", + " reference_ratio=reference_ratio\n", + " )\n", + " R_phase = alphas[f'{isotope}_{phase}'](TC * si.K + const.T0) * Rv\n", + " delta_phase = formulae.trivia.isotopic_ratio_2_delta(\n", + " ratio=R_phase,\n", + " reference_ratio=reference_ratio\n", + " )\n", + " \n", + " case_data[f'delta_{phase}_{isotope}'] = delta_phase\n", + " case_data[f'diff_delta_{isotope}'] = delta_phase - delta_v[isotope]\n", + " excess_vapour = excess_d(delta_2H=delta_v['2H'], delta_18O=delta_v['18O'])\n", + " excess_phase = excess_d(delta_2H=case_data[f'delta_{phase}_2H'], delta_18O=case_data[f'delta_{phase}_18O'])\n", + " case_data[\"diff_d_excess\"] = excess_phase - excess_vapour" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "2f24d9cb-a36a-4ebd-87cf-3867f2bf9281", + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-06T13:01:03.166287Z", + "start_time": "2024-12-06T13:01:03.158099Z" + } + }, + "outputs": [], + "source": [ + "table_html = \"\"\n", + "table_html += \"\"\"\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\"\"\"\n", + "row_labels = {\n", + " 'diff_delta_2H': 'δ2H',\n", + " 'diff_delta_18O': 'δ18O',\n", + " 'diff_d_excess': 'd',\n", + "}\n", + "for case in ('A', 'B'):\n", + " for row, var in enumerate(('diff_delta_2H', 'diff_delta_18O', 'diff_d_excess')):\n", + " table_html += f\"\"\"\n", + " 0 else ' style=\"border-top:1px black solid\"'}>\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \"\"\"\n", + "table_html += \"
composition of vapourΔl-v@20CΔl-v@0CΔs-v@0C
{row_labels[var]}{in_unit(CASES[case][var[var.rindex(\"_\")+1:]], const.PER_MILLE)}‰{in_unit(table_data[20]['l_v'][case][var], const.PER_MILLE):.1f}‰{in_unit(table_data[ 0]['l_v'][case][var], const.PER_MILLE):.1f}‰{in_unit(table_data[ 0]['s_v'][case][var], const.PER_MILLE):.1f}‰
\"" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "6d80a1710aaff2ef", + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-06T13:01:03.238062Z", + "start_time": "2024-12-06T13:01:03.231275Z" + }, + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "data": { + "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", + "
composition of vapourΔl-v@20CΔl-v@0CΔs-v@0C
δ2H-80.0‰78.2‰103.3‰121.3‰
δ18O-10.0‰9.7‰11.6‰15.1‰
d0.0‰0.7‰10.5‰0.6‰
δ2H-200.0‰68.0‰89.9‰105.4‰
δ18O-25.0‰9.5‰11.4‰14.9‰
d0.0‰-8.4‰-1.6‰-13.4‰
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "display(HTML(table_html))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902db5d8-c140-47c2-b53a-2a5bd745a8bc", + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-06T13:01:03.273435Z", + "start_time": "2024-12-06T13:01:03.270969Z" + } + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/PySDM/source/examples/PySDM_examples/Graf_et_al_2019/__init__.py b/PySDM/source/examples/PySDM_examples/Graf_et_al_2019/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3b2ddcc308ee8ab9e95fb3072837021f933eea87 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Graf_et_al_2019/__init__.py @@ -0,0 +1,11 @@ +# pylint: disable=invalid-name +""" +Parcel-model Condensation,Isotope example +[Graf.et.al 2019](https://doi.org/10.5194/acp-19-747-2019) + +figure_4.ipynb: +.. include:: ./figure_4.ipynb.badges.md + +Table_1.ipynb: +.. include:: ./Table_1.ipynb.badges.md +""" diff --git a/PySDM/source/examples/PySDM_examples/Graf_et_al_2019/figure_4.ipynb b/PySDM/source/examples/PySDM_examples/Graf_et_al_2019/figure_4.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..6342f5a51a53b40d281ee9d02322765715721d5d --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Graf_et_al_2019/figure_4.ipynb @@ -0,0 +1,1509 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "327f2a018faf3142", + "metadata": { + "collapsed": false + }, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Graf_et_al_2019/figure_4.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Graf_et_al_2019/figure_4.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Graf_et_al_2019/figure_4.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "aa160d9c48befc58", + "metadata": { + "collapsed": false + }, + "source": [ + "based on [Graf et al. 2019](https://doi.org/10.5194/acp-19-747-2019)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "b2c87898f6737935", + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-06T13:01:19.373289Z", + "start_time": "2024-12-06T13:01:19.360208Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "2432d43d15e97625", + "metadata": { + "ExecuteTime": { + "end_time": "2024-01-11T08:43:41.429069Z", + "start_time": "2024-01-11T08:43:41.423952Z" + }, + "collapsed": false + }, + "outputs": [], + "source": [ + "import math\n", + "import numpy as np\n", + "from open_atmos_jupyter_utils import show_plot\n", + "from PySDM.physics import si\n", + "from PySDM.environments import Parcel\n", + "from PySDM.backends import CPU\n", + "from PySDM import Builder, Formulae\n", + "from PySDM.dynamics import AmbientThermodynamics, Condensation\n", + "from PySDM.initialisation.spectra import Lognormal\n", + "from PySDM.initialisation.sampling import spectral_sampling\n", + "from PySDM.initialisation.hygroscopic_equilibrium import equilibrate_wet_radii\n", + "from PySDM.initialisation import discretise_multiplicities\n", + "from PySDM import products" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "81d815991be0cc9", + "metadata": { + "ExecuteTime": { + "end_time": "2024-01-11T08:42:35.791569Z", + "start_time": "2024-01-11T08:42:35.352112Z" + }, + "collapsed": false + }, + "outputs": [], + "source": [ + "formulae = Formulae()\n", + "const = formulae.constants\n", + "pvs_water = formulae.saturation_vapour_pressure.pvs_water\n", + "\n", + "RH0 = .75\n", + "p0 = 950 * si.hPa\n", + "T0 = const.T0 + 12 * si.K\n", + "alt_initial = 500 * si.m \n", + "alt_final = 3500 * si.m\n", + "\n", + "total_displacement = alt_final - alt_initial\n", + "initial_water_vapour_mixing_ratio = const.eps / (p0 / RH0 / pvs_water(T0) - 1)\n", + "\n", + "n_sd = 1\n", + "dt = 10 * si.s\n", + "vertical_velocity = 2.5 * si.m / si.s" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "dada2ae5b4083f0e", + "metadata": { + "ExecuteTime": { + "end_time": "2024-01-11T08:42:35.796706Z", + "start_time": "2024-01-11T08:42:35.793581Z" + }, + "collapsed": false + }, + "outputs": [], + "source": [ + "env = Parcel(\n", + " # given in the paper\n", + " p0=p0,\n", + " T0=T0,\n", + " initial_water_vapour_mixing_ratio=initial_water_vapour_mixing_ratio,\n", + " # arbitrary choices\n", + " w=vertical_velocity,\n", + " dt=dt,\n", + " mass_of_dry_air=1e3 * si.kg,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "47f3f883e4e67ed", + "metadata": { + "ExecuteTime": { + "end_time": "2024-01-11T08:42:36.874577Z", + "start_time": "2024-01-11T08:42:35.804074Z" + }, + "collapsed": false + }, + "outputs": [], + "source": [ + "builder = Builder(backend=CPU(formulae=formulae), n_sd=n_sd, environment=env)\n", + "builder.add_dynamic(AmbientThermodynamics())\n", + "builder.add_dynamic(Condensation())" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "f98070a3d550d65", + "metadata": { + "ExecuteTime": { + "end_time": "2024-01-11T08:42:36.878312Z", + "start_time": "2024-01-11T08:42:36.876718Z" + }, + "collapsed": false + }, + "outputs": [], + "source": [ + "spectrum = Lognormal(norm_factor=1e4 / si.mg, m_mode=50 * si.nm, s_geom=1.5)\n", + "kappa = .5 * si.dimensionless\n", + "cloud_range = (.5 * si.um, 25 * si.um)\n", + "output_interval = 4\n", + "output_points = 40" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "5ec5eabc3c1d3872", + "metadata": { + "ExecuteTime": { + "end_time": "2024-01-11T08:42:36.883Z", + "start_time": "2024-01-11T08:42:36.880940Z" + }, + "collapsed": false + }, + "outputs": [], + "source": [ + "r_dry, specific_concentration = spectral_sampling.Logarithmic(spectrum).sample_deterministic(n_sd)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "ce639f01f48ff348", + "metadata": { + "ExecuteTime": { + "end_time": "2024-01-11T08:42:37.072808Z", + "start_time": "2024-01-11T08:42:36.886365Z" + }, + "collapsed": false + }, + "outputs": [], + "source": [ + "v_dry = formulae.trivia.volume(radius=r_dry)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "28897f499330f699", + "metadata": { + "ExecuteTime": { + "end_time": "2024-01-11T08:42:38.909344Z", + "start_time": "2024-01-11T08:42:37.088206Z" + }, + "collapsed": false + }, + "outputs": [], + "source": [ + "r_wet = equilibrate_wet_radii(r_dry=r_dry, environment=builder.particulator.environment, kappa_times_dry_volume=kappa * v_dry)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "8cf40dfb18e7e082", + "metadata": { + "ExecuteTime": { + "end_time": "2024-01-11T08:42:38.913147Z", + "start_time": "2024-01-11T08:42:38.909929Z" + }, + "collapsed": false + }, + "outputs": [], + "source": [ + "attributes = {\n", + " 'multiplicity': discretise_multiplicities(specific_concentration * builder.particulator.environment.mass_of_dry_air),\n", + " 'dry volume': v_dry,\n", + " 'kappa times dry volume': kappa * v_dry,\n", + " 'volume': formulae.trivia.volume(radius=r_wet)\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "f36faa7368db791e", + "metadata": { + "ExecuteTime": { + "end_time": "2024-01-11T08:42:39.067069Z", + "start_time": "2024-01-11T08:42:38.920056Z" + }, + "collapsed": false + }, + "outputs": [], + "source": [ + "particulator = builder.build(\n", + " attributes=attributes,\n", + " products=(\n", + " products.PeakSaturation(name='S_max'),\n", + " products.ParcelDisplacement(name=\"z\"),\n", + " products.AmbientTemperature(name=\"T\")\n", + " )\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "843a224f900fb94d", + "metadata": { + "ExecuteTime": { + "end_time": "2024-01-11T08:42:45.925004Z", + "start_time": "2024-01-11T08:42:39.077019Z" + }, + "collapsed": false + }, + "outputs": [], + "source": [ + "n_steps = total_displacement / (dt * vertical_velocity)\n", + "\n", + "output = {name:[] for name in particulator.products}\n", + "levels = {\n", + " 'CB': None,\n", + " '0C': None\n", + "}\n", + "\n", + "ONE_AND_ONLY_GRID_ELEMENT = 0\n", + "\n", + "for step in range(math.ceil(n_steps)):\n", + " particulator.run(steps=1)\n", + " for name, product in particulator.products.items():\n", + " product_buffer_reference = product.get()\n", + " output[name].append(product_buffer_reference[ONE_AND_ONLY_GRID_ELEMENT])\n", + " \n", + " if levels['CB'] is None and output[\"S_max\"][-1] > 1:\n", + " levels['CB'] = .5 * (output[\"z\"][-1] + output[\"z\"][-2])\n", + " \n", + " if levels['0C'] is None and output[\"T\"][-1] < const.T0:\n", + " levels['0C'] = .5 * (output['z'][-1] + output[\"z\"][-2])" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "a363f02a36a714d", + "metadata": { + "ExecuteTime": { + "end_time": "2024-01-11T08:42:46.314435Z", + "start_time": "2024-01-11T08:42:45.927080Z" + }, + "collapsed": false + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-01-11T09:42:46.281407\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "30b01b7b859146a7897eba2e25623cf6", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./fig_4a.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from matplotlib import pyplot\n", + "\n", + "fig, axs = pyplot.subplots(1, 1, squeeze=False)\n", + "xy1 = axs[0,0]\n", + "xy2 = xy1.twiny()\n", + "\n", + "xy1.plot(\n", + " np.asarray(output['S_max']) * 100,\n", + " np.asarray(output['z']) + alt_initial\n", + ")\n", + "xy2.plot(\n", + " np.asarray(output['T']) - const.T0,\n", + " np.asarray(output['z']) + alt_initial\n", + ")\n", + "for level in levels.values():\n", + " xy1.axhline(y=level + alt_initial, linestyle='--', color='black')\n", + "\n", + "xy1.set_xlim(75, 107)\n", + "xy2.set_xlim(-10, 16)\n", + "xy1.set_ylim(alt_initial, alt_final)\n", + "xy1.set_xlabel(\"RH / %\")\n", + "xy2.set_xlabel(\"T / C\")\n", + "xy1.set_ylabel(\"z / m\")\n", + "show_plot('fig_4a.pdf')" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "5acab115cd92fd9b", + "metadata": { + "ExecuteTime": { + "end_time": "2024-01-11T08:42:46.317574Z", + "start_time": "2024-01-11T08:42:46.313381Z" + }, + "collapsed": false + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "d0173c4dbd123f7b", + "metadata": { + "ExecuteTime": { + "end_time": "2024-01-11T08:42:46.323642Z", + "start_time": "2024-01-11T08:42:46.319974Z" + }, + "collapsed": false + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "430e8a3643291372", + "metadata": { + "ExecuteTime": { + "end_time": "2024-01-11T08:42:46.326817Z", + "start_time": "2024-01-11T08:42:46.322560Z" + }, + "collapsed": false + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/PySDM/source/examples/PySDM_examples/Jaruga_and_Pawlowska_2018/__init__.py b/PySDM/source/examples/PySDM_examples/Jaruga_and_Pawlowska_2018/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3fdbefa5af9e004c3ea192f9c9a7d242158d34c6 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Jaruga_and_Pawlowska_2018/__init__.py @@ -0,0 +1,11 @@ +# pylint: disable=invalid-name +""" +aqueous-chemistry parcel-model example based on +[Jaruga & Pawlowska 2018 (GMD)](https://doi.org/10.5194/gmd-11-3623-2018) + +fig_2.ipynb: +.. include:: ./fig_2.ipynb.badges.md + +fig_3.ipynb: +.. include:: ./fig_3.ipynb.badges.md +""" diff --git a/PySDM/source/examples/PySDM_examples/Jaruga_and_Pawlowska_2018/fig_2.ipynb b/PySDM/source/examples/PySDM_examples/Jaruga_and_Pawlowska_2018/fig_2.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..8e514afad7f152b0ef34608cc5b34c643e27165b --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Jaruga_and_Pawlowska_2018/fig_2.ipynb @@ -0,0 +1,2631 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Jaruga_and_Pawlowska_2018/fig_2.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Jaruga_and_Pawlowska_2018/fig_2.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Jaruga_and_Pawlowska_2018/fig_2.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### based on Jaruga and Pawlowska (Geosci. Model Dev. 11) \"_libcloudph++ 2.0: aqueous-phase chemistry extension of the particle-based cloud microphysics scheme_\" \n", + "https://doi.org/10.5194/gmd-11-3623-2018" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-06T10:37:16.995005Z", + "start_time": "2024-12-06T10:37:16.990766Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-06T13:01:43.025041Z", + "start_time": "2024-12-06T13:01:39.759448Z" + }, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "from PySDM_examples.Kreidenweis_et_al_2003 import Settings, Simulation\n", + "from PySDM import products as PySDM_products\n", + "from open_atmos_jupyter_utils import show_plot\n", + "from PySDM_examples.utils.widgets import display, FloatProgress\n", + "from PySDM.physics import si\n", + "from PySDM.physics.constants import PPT, PPB\n", + "import numpy as np\n", + "from matplotlib import pyplot" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2023-12-29T11:46:50.614980Z", + "start_time": "2023-12-29T11:46:50.611464Z" + } + }, + "outputs": [], + "source": [ + "n_points = 6 if 'CI' not in os.environ else 2\n", + "nsd = np.logspace(0, n_points-1, num=n_points, base=2.0, dtype=int)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2023-12-29T11:46:51.202427Z", + "start_time": "2023-12-29T11:46:50.619344Z" + } + }, + "outputs": [], + "source": [ + "default_settings = Settings(1,1,1)\n", + "products = (\n", + " PySDM_products.PeakSaturation(name='S_max'),\n", + " PySDM_products.ParticleConcentration(name='n_c_cm3', unit='cm^-3', radius_range=default_settings.cloud_radius_range),\n", + " PySDM_products.Acidity(name='pH_conc_H_volume_weighted', radius_range=default_settings.cloud_radius_range),\n", + " PySDM_products.AqueousMoleFraction('S_VI', name='aq_S_VI_ppb', unit='ppb')\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "ExecuteTime": { + "end_time": "2023-12-29T11:47:53.602057Z", + "start_time": "2023-12-29T11:46:51.204609Z" + } + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "b097ae97b641411bb9a4521e3fef2466", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "simulations = []\n", + "progress = FloatProgress(value=0.0, min=0.0, max=1.0)\n", + "display(progress)\n", + "for it in range(n_points):\n", + " settings = Settings(\n", + " dt=1*si.s, \n", + " n_sd=nsd[it], \n", + " n_substep=5 if 'CI' not in os.environ else 1\n", + " )\n", + " settings.output_interval = 25 * si.s\n", + " simulation = Simulation(settings, products)\n", + " output = simulation.run()\n", + " simulations.append({'settings': settings, 'output': output})\n", + " progress.value += 1/n_points" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "ExecuteTime": { + "end_time": "2023-12-29T11:47:53.608022Z", + "start_time": "2023-12-29T11:47:53.604097Z" + } + }, + "outputs": [], + "source": [ + "x = np.log2(nsd)\n", + "smax = []\n", + "droplet_number = []\n", + "pH = []\n", + "sulfate_ppt = []\n", + "for simulation in simulations:\n", + " smax.append((np.nanmax(simulation['output'][\"S_max\"]) - 1) * 100)\n", + " droplet_number.append(np.nanmax(simulation['output'][\"n_c_cm3\"]))\n", + " pH.append(simulation['output'][\"pH_conc_H_volume_weighted\"][-1])\n", + " S_VI = simulation['output'][\"aq_S_VI_ppb\"]\n", + " sulfate_ppt.append((S_VI[-1] - S_VI[0]) * PPB / PPT)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "ExecuteTime": { + "end_time": "2023-12-29T11:47:54.143480Z", + "start_time": "2023-12-29T11:47:53.610737Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2023-12-29T12:47:54.081348\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "81193b96e70244d4ab894aa58c177edc", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./fig_2
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, axs = pyplot.subplots(2, 2, figsize=(10,7))\n", + "\n", + "axs[0][0].plot(x, droplet_number, 'o-')\n", + "axs[0][0].set_ylabel('Droplet conc. [cm-3]')\n", + "axs[0][0].set_ylim(0, 1800)\n", + "\n", + "axs[0][1].plot(x, smax, 'o-')\n", + "axs[0][1].set_ylabel('Max. supersat [%]')\n", + "axs[0][1].set_ylim(0.24, 0.61)\n", + "\n", + "axs[1][0].plot(x, pH, 'o-')\n", + "axs[1][0].set_ylabel('Average pH')\n", + "axs[1][0].set_ylim(4.7, 5.2)\n", + "\n", + "axs[1][1].plot(x, sulfate_ppt, 'o-')\n", + "axs[1][1].set_ylabel('Total sulfate production [ppt]')\n", + "axs[1][1].set_ylim(135, 180)\n", + "\n", + "for axss in axs:\n", + " for ax in axss:\n", + " ax.set_xlabel('ln_2(number of super-droplets)')\n", + " ax.grid()\n", + " ax.set_xlim(0,10)\n", + "\n", + "show_plot(\"fig_2\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.8.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/PySDM/source/examples/PySDM_examples/Jaruga_and_Pawlowska_2018/fig_3.ipynb b/PySDM/source/examples/PySDM_examples/Jaruga_and_Pawlowska_2018/fig_3.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..ecd503daaa93b1f0358d76ad928fcc23de7858d7 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Jaruga_and_Pawlowska_2018/fig_3.ipynb @@ -0,0 +1,4540 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Jaruga_and_Pawlowska_2018/fig_3.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Jaruga_and_Pawlowska_2018/fig_3.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Jaruga_and_Pawlowska_2018/fig_3.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "#### based on Jaruga and Pawlowska (Geosci. Model Dev. 11) \"_libcloudph++ 2.0: aqueous-phase chemistry extension of the particle-based cloud microphysics scheme_\" \n", + "https://doi.org/10.5194/gmd-11-3623-2018" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-06T10:37:21.599199Z", + "start_time": "2024-12-06T10:37:21.589064Z" + }, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-06T10:37:32.026002Z", + "start_time": "2024-12-06T10:37:29.060774Z" + }, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "from PySDM_examples.Kreidenweis_et_al_2003 import Settings, Simulation\n", + "from PySDM.physics import si\n", + "from open_atmos_jupyter_utils import show_plot\n", + "from PySDM.products import AqueousMassSpectrum\n", + "from PySDM.products.aqueous_chemistry.aqueous_mass_spectrum import SpecificAqueousMassSpectrum\n", + "from matplotlib import pyplot\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2023-12-29T11:45:52.512360Z", + "start_time": "2023-12-29T11:45:02.979951Z" + }, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "settings = Settings(dt=1*si.s, n_sd=16 if 'CI' in os.environ else 256, n_substep=5)\n", + "products = (\n", + " SpecificAqueousMassSpectrum(\n", + " key=\"S_VI\",\n", + " dry_radius_bins_edges=settings.dry_radius_bins_edges,\n", + " name='dm_S_VI/dlog_10(dry diameter)_spec',\n", + " unit='ug/kg'\n", + " ),\n", + " AqueousMassSpectrum(\n", + " key=\"S_VI\",\n", + " dry_radius_bins_edges=settings.dry_radius_bins_edges,\n", + " name='dm_S_VI/dlog_10(dry diameter)',\n", + " specific=False,\n", + " unit='ug/m^3'\n", + " ),\n", + ")\n", + "simulation = Simulation(settings, products)\n", + "output = simulation.run()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2023-12-29T11:45:53.862839Z", + "start_time": "2023-12-29T11:45:52.515932Z" + }, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2023-12-29T12:45:53.173027\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "5c6b0a6dc62345299f1f469d453b63c7", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./fig_3
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2023-12-29T12:45:53.783983\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "528000b81b5c4357a7d7977805a9eada", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./fig_3
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "for product in products:\n", + " fig, axs = pyplot.subplots(1, 2, figsize=(11,5))\n", + " labels = {0:\"first\", -1:\"last\"}\n", + " for ax in axs:\n", + " for step, label in labels.items():\n", + " ax.step(\n", + " 2e6 * settings.dry_radius_bins_edges[:-1],\n", + " output[product.name][step],\n", + " label=f'step: {label}'\n", + " )\n", + " ax.set_ylabel(f'dS(VI)/dlog_10(D) [{product.unit}]')\n", + " ax.set_xlabel('dry diameter [µm]')\n", + " ax.set_xlim([.01, 1])\n", + " ax.grid()\n", + " ax.set_xscale('log')\n", + "\n", + " axs[0].set_yscale('log')\n", + " axs[0].set_ylim([.01, 15])\n", + " axs[1].set_ylim([0, 15])\n", + " axs[1].legend()\n", + "\n", + " show_plot(\"fig_3\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.8.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_1.ipynb b/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_1.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..8641514bbccfa3e6f9df39dd7255407c17421570 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_1.ipynb @@ -0,0 +1,2000 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a68bae6de370294c", + "metadata": { + "collapsed": false + }, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_1.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_1.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_1.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "67f1a1164c2629c6", + "metadata": { + "collapsed": false + }, + "source": [ + "#### based on Fig. 1 from [Jensen and Nugent (JAS 74) \"_Condensational Growth of Drops Formed on Giant Sea-Salt Aerosol Particles_\"](https://doi.org/10.1175/JAS-D-15-0370.1)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "605d5fe5", + "metadata": { + "ExecuteTime": { + "end_time": "2024-03-04T11:31:45.383883Z", + "start_time": "2024-03-04T11:31:45.379202Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "initial_id", + "metadata": { + "ExecuteTime": { + "end_time": "2024-03-04T11:31:50.944088Z", + "start_time": "2024-03-04T11:31:50.361661Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-03-04T12:31:50.875143\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "c9f88210450743e28203d8c6db9c215a", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./fig_1.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import numpy as np\n", + "from matplotlib import pyplot\n", + "from open_atmos_jupyter_utils import show_plot\n", + "from PySDM.physics import in_unit, si\n", + "from PySDM_examples.Jensen_and_Nugent_2017 import Settings, table_3\n", + "settings = Settings(aerosol=\"modified polluted\", cloud_type=\"Sc\")\n", + "trivia = settings.formulae.trivia\n", + "\n", + "TABLE3_DR = table_3.RD[1] - table_3.RD[0]\n", + "np.testing.assert_almost_equal(np.diff(table_3.RD), TABLE3_DR)\n", + "\n", + "X_UNIT = si.um\n", + "Y_UNIT = si.cm**-3\n", + "\n", + "rd = np.logspace(\n", + " np.log10(.01 * si.um),\n", + " np.log10(.5 * si.um)\n", + ")\n", + "pdf = settings.dry_radii_spectrum.size_distribution(rd)\n", + "dN_dlogr = trivia.dn_dlogr(rd, pdf)\n", + "\n", + "pyplot.loglog(\n", + " in_unit(rd, X_UNIT),\n", + " in_unit(dN_dlogr, Y_UNIT),\n", + " label='Modified polluted'\n", + ")\n", + "pyplot.loglog(\n", + " in_unit(table_3.RD, X_UNIT),\n", + " in_unit(trivia.dn_dlogr(table_3.RD, table_3.NA / TABLE3_DR), Y_UNIT),\n", + " label='VOCALS GCCN'\n", + ")\n", + "pyplot.legend()\n", + "pyplot.xlabel(\"r$_d$ (µm)\")\n", + "pyplot.xlim(\n", + " in_unit(rd[0], si.um),\n", + " in_unit(100 * si.um, si.um)\n", + ")\n", + "pyplot.ylabel(\"dN/dlogr (cm$^{-3}$)\")\n", + "pyplot.ylim(1e-4, 1e3)\n", + "pyplot.grid()\n", + "show_plot('fig_1.pdf')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "7ecdcaaa-0d41-49cf-ae2c-7748e8e8dca3", + "metadata": { + "ExecuteTime": { + "end_time": "2024-03-04T11:31:48.375619Z", + "start_time": "2024-03-04T11:31:48.372442Z" + } + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_3_and_Tab_4_upper_rows.ipynb b/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_3_and_Tab_4_upper_rows.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..348af85d87200f6aa091f84da5daaa91180ee0c1 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_3_and_Tab_4_upper_rows.ipynb @@ -0,0 +1,2377 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70b9d65563d58a62", + "metadata": { + "collapsed": false + }, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_3_and_Tab_4_upper_rows.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_3_and_Tab_4_upper_rows.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_3_and_Tab_4_upper_rows.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "d1dc2b61d9602b70", + "metadata": { + "collapsed": false + }, + "source": [ + "#### based on Fig. 3 and Table 4 (Modified Polluted) from [Jensen and Nugent (JAS 74) \"_Condensational Growth of Drops Formed on Giant Sea-Salt Aerosol Particles_\"](https://doi.org/10.1175/JAS-D-15-0370.1)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "879850e1", + "metadata": { + "ExecuteTime": { + "end_time": "2024-03-24T15:36:09.631730Z", + "start_time": "2024-03-24T15:36:09.627733Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "initial_id", + "metadata": { + "ExecuteTime": { + "end_time": "2024-03-24T15:51:52.064243Z", + "start_time": "2024-03-24T15:51:49.276931Z" + } + }, + "outputs": [], + "source": [ + "from IPython.display import display, HTML\n", + "from PySDM_examples.Jensen_and_Nugent_2017 import Settings, Simulation\n", + "from open_atmos_jupyter_utils import show_plot\n", + "from PySDM_examples.Jensen_and_Nugent_2017.plotting import figure, compute_table_values\n", + "\n", + "settings = Settings(aerosol=\"modified polluted\", cloud_type=\"Sc\")\n", + "simulation = Simulation(settings)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "b184e2c6ac313a7a", + "metadata": { + "ExecuteTime": { + "end_time": "2024-03-24T15:51:53.232151Z", + "start_time": "2024-03-24T15:51:52.064598Z" + }, + "collapsed": false + }, + "outputs": [], + "source": [ + "output = simulation.run(steps_per_output_interval= 5)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "7cd7b3fd3774c9b4", + "metadata": { + "ExecuteTime": { + "end_time": "2024-03-24T15:51:53.707434Z", + "start_time": "2024-03-24T15:51:53.246458Z" + }, + "collapsed": false + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-03-24T17:40:39.922200\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "94d7ea1d02ad49589d7de1154ede53f2", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./Fig_3new.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "masks = figure(\n", + " output=output,\n", + " settings=settings,\n", + " simulation=simulation,\n", + " plot_drops_with_dry_radii_um=(.02, .031, .152, .337, .5),\n", + " xlim_r_um=(0, 15),\n", + " xlim_S_percent=(-1, 1),\n", + " return_masks=True\n", + ")\n", + "\n", + "show_plot(\"Fig_3new.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "eabaafa09612b16e", + "metadata": { + "ExecuteTime": { + "end_time": "2024-03-24T15:51:53.713353Z", + "start_time": "2024-03-24T15:51:53.710236Z" + }, + "collapsed": false + }, + "outputs": [], + "source": [ + "table_values = compute_table_values(\n", + " height_above_cb_m = (50, 100, 150, 200, 250, 300),\n", + " height_cb_m = 900,\n", + " products=output[\"products\"],\n", + " ascent_mask = masks[\"ascent\"],\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "9336dc4ba0d6b0dc", + "metadata": { + "ExecuteTime": { + "end_time": "2024-03-24T15:51:53.726471Z", + "start_time": "2024-03-24T15:51:53.724599Z" + }, + "collapsed": false + }, + "outputs": [], + "source": [ + "table_html = \"\"\n", + "\n", + "table_html += \"\"\"\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\"\"\"\n", + "row_labels = {\n", + " 'ascent': 'Mod.polluted (Updraft)',\n", + " 'descent': 'Mod.polluted (Downdraft)'\n", + "}\n", + "for case, data in table_values.items():\n", + " for var in data:\n", + " table_html += f\"\"\"\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \"\"\" \n", + "table_html += \"
Aerosols z (m) Mean cloud drop radius (μm)Drop spectral width (μm)Drop dispersion
{row_labels[case]}{var[0]}{var[1]}{var[2]}{var[3]}
\"" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "5b615403-60e5-4769-ba7d-644fa5821af8", + "metadata": { + "ExecuteTime": { + "end_time": "2024-03-24T15:51:53.997179Z", + "start_time": "2024-03-24T15:51:53.987186Z" + } + }, + "outputs": [ + { + "data": { + "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", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Aerosols z (m) Mean cloud drop radius (μm)Drop spectral width (μm)Drop dispersion
Mod.polluted (Updraft)505.470.430.079
Mod.polluted (Updraft)1006.780.390.058
Mod.polluted (Updraft)1507.710.370.047
Mod.polluted (Updraft)2008.450.350.041
Mod.polluted (Updraft)2509.080.330.037
Mod.polluted (Updraft)3009.640.320.034
Mod.polluted (Downdraft)3009.640.320.034
Mod.polluted (Downdraft)2509.110.340.038
Mod.polluted (Downdraft)2008.480.370.044
Mod.polluted (Downdraft)1507.740.410.053
Mod.polluted (Downdraft)1006.830.460.068
Mod.polluted (Downdraft)505.570.570.102
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "display(HTML(table_html))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_4_and_7_and_Tab_4_bottom_rows.ipynb b/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_4_and_7_and_Tab_4_bottom_rows.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..04b55554dd3daebd2ae59c3434623f8240708c35 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_4_and_7_and_Tab_4_bottom_rows.ipynb @@ -0,0 +1,4966 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "ba7d7fea4c326de4", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_4_and_7_and_Tab_4_bottom_rows.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_4_and_7_and_Tab_4_bottom_rows.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_4_and_7_and_Tab_4_bottom_rows.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "83463dbe10afd52", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "#### based on Fig. 4, Fig.7 and Table 4 (Modified Polluted + GCCN) from [Jensen and Nugent (JAS 74) \"_Condensational Growth of Drops Formed on Giant Sea-Salt Aerosol Particles_\"](https://doi.org/10.1175/JAS-D-15-0370.1)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "f2673625", + "metadata": { + "ExecuteTime": { + "end_time": "2024-11-08T21:53:02.092999Z", + "start_time": "2024-11-08T21:53:02.087100Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "initial_id", + "metadata": { + "ExecuteTime": { + "end_time": "2024-11-08T21:53:04.307601Z", + "start_time": "2024-11-08T21:53:02.327950Z" + } + }, + "outputs": [], + "source": [ + "from matplotlib import pyplot\n", + "import numpy as np\n", + "\n", + "from PySDM_examples.Jensen_and_Nugent_2017 import Settings, Simulation\n", + "from open_atmos_jupyter_utils import show_plot\n", + "from PySDM_examples.Jensen_and_Nugent_2017.plotting import figure, CLOUD_BASE, find_drop_ids_by_dry_size, compute_table_values\n", + "from PySDM.physics import in_unit, si\n", + "from IPython.display import display, HTML\n", + "from PySDM.physics.constants import PER_CENT\n", + "\n", + "settings = Settings(aerosol=\"modified polluted\", cloud_type=\"Sc\")\n", + "simulation = Simulation(settings, gccn=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "153fe91c37fbdea7", + "metadata": { + "ExecuteTime": { + "end_time": "2024-11-08T21:52:51.116093Z", + "start_time": "2024-11-08T21:52:39.350484Z" + }, + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "output = simulation.run(steps_per_output_interval= 5)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "a99bff26cf55cd96", + "metadata": { + "ExecuteTime": { + "end_time": "2024-11-08T21:52:51.994997Z", + "start_time": "2024-11-08T21:52:51.126533Z" + }, + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-11-08T22:52:51.955301\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.9.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "04df5485904c4e3fbb9d9a3faa08a489", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./Fig_4.pdf
\"), HTML(value=\"./Fig_7.pdf
\"), HTML(value=\"\"\n", + "table_html += \"\"\"\n", + " \n", + " Aerosols \n", + " z (m) \n", + " Mean cloud drop radius (μm)\n", + " Drop spectral width (μm)\n", + " Drop dispersion\n", + " \n", + "\"\"\"\n", + "row_labels = {\n", + " 'ascent': 'Mod.polluted + GCCN (Updraft)',\n", + " 'descent': 'Mod.polluted + GCCN (Downdraft)'\n", + "}\n", + "\n", + "for case, data in table_values.items():\n", + " for var in data: \n", + " table_html += f\"\"\"\n", + " \n", + " {row_labels[case]}\n", + " {var[0]}\n", + " {var[1]}\n", + " {var[2]}\n", + " {var[3]}\n", + " \n", + " \"\"\" \n", + "\n", + "table_html += \"\"" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "e7743277-0296-451f-8d66-e0519ec942f2", + "metadata": { + "ExecuteTime": { + "end_time": "2024-11-08T21:52:52.397504Z", + "start_time": "2024-11-08T21:52:52.394077Z" + } + }, + "outputs": [ + { + "data": { + "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", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Aerosols z (m) Mean cloud drop radius (μm)Drop spectral width (μm)Drop dispersion
Mod.polluted + GCCN (Updraft)505.440.440.082
Mod.polluted + GCCN (Updraft)1006.740.400.059
Mod.polluted + GCCN (Updraft)1507.670.370.049
Mod.polluted + GCCN (Updraft)2008.420.350.042
Mod.polluted + GCCN (Updraft)2509.050.340.038
Mod.polluted + GCCN (Updraft)3009.600.330.034
Mod.polluted + GCCN (Downdraft)3009.600.330.034
Mod.polluted + GCCN (Downdraft)2509.070.350.039
Mod.polluted + GCCN (Downdraft)2008.440.380.045
Mod.polluted + GCCN (Downdraft)1507.690.420.054
Mod.polluted + GCCN (Downdraft)1006.770.470.070
Mod.polluted + GCCN (Downdraft)505.480.590.107
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "display(HTML(table_html))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "36fe694861b15027", + "metadata": { + "ExecuteTime": { + "end_time": "2024-11-08T21:52:52.407327Z", + "start_time": "2024-11-08T21:52:52.406086Z" + }, + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb90a2a0c971b374", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_5.ipynb b/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_5.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..465780f2ebf0a80ba79c3f32a11df110659d7b66 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_5.ipynb @@ -0,0 +1,3225 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "804357ff91efdde4", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_5.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_5.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_5.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "434b4c67fd5f2954", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "#### based on Fig. 5 from [Jensen and Nugent (JAS 74) \"_Condensational Growth of Drops Formed on Giant Sea-Salt Aerosol Particles_\"](https://doi.org/10.1175/JAS-D-15-0370.1)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "f1636433", + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:33:00.814600Z", + "start_time": "2024-02-01T07:33:00.806057Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "initial_id", + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-15T09:43:07.594652Z", + "start_time": "2024-02-15T09:43:05.810743Z" + } + }, + "outputs": [], + "source": [ + "from PySDM_examples.Jensen_and_Nugent_2017 import Settings, Simulation\n", + "from open_atmos_jupyter_utils import show_plot\n", + "from PySDM_examples.Jensen_and_Nugent_2017.plotting import figure\n", + "\n", + "settings = Settings(aerosol=\"pristine\", cloud_type=\"Sc\")\n", + "simulation = Simulation(settings, gccn=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "fecc0b0999661682", + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-15T09:43:05.058585Z", + "start_time": "2024-02-15T09:42:57.041759Z" + }, + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "output = simulation.run()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "d6502667395327ab", + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-15T09:43:05.807516Z", + "start_time": "2024-02-15T09:43:05.060081Z" + }, + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-02-18T00:58:44.149028\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f2d41ad850744f618bd635dcca954212", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./Fig_5.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "figure(\n", + " output=output,\n", + " settings=settings,\n", + " simulation=simulation,\n", + " plot_drops_with_dry_radii_um=(.1, 1, 2, 3, 4, 5, 6, 7, 8, 9),\n", + " xlim_r_um=(0, 60),\n", + " xlim_S_percent= (-1.5,1.5)\n", + ")\n", + "show_plot(\"Fig_5.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eae39c956cc61196", + "metadata": { + "collapsed": false, + "is_executing": true, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_6.ipynb b/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_6.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..6b6a3ead6cbf1476f5477a0909a69018263a87fc --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_6.ipynb @@ -0,0 +1,2353 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "9f37f66f49adc99b", + "metadata": { + "collapsed": false + }, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_6.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_6.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_6.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "af8a61829aba28c", + "metadata": { + "collapsed": false + }, + "source": [ + "#### based on Fig. 6 from [Jensen and Nugent (JAS 74) \"_Condensational Growth of Drops Formed on Giant Sea-Salt Aerosol Particles_\"](https://doi.org/10.1175/JAS-D-15-0370.1)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "284358c5", + "metadata": { + "ExecuteTime": { + "end_time": "2024-03-04T14:32:02.816749Z", + "start_time": "2024-03-04T14:32:02.812278Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "initial_id", + "metadata": { + "ExecuteTime": { + "end_time": "2024-03-04T16:25:16.587545Z", + "start_time": "2024-03-04T16:25:14.461586Z" + } + }, + "outputs": [], + "source": [ + "from PySDM_examples.Jensen_and_Nugent_2017 import Settings, Simulation\n", + "from open_atmos_jupyter_utils import show_plot\n", + "from PySDM_examples.Jensen_and_Nugent_2017.plotting import figure\n", + "from PySDM.physics import si\n", + "\n", + "settings = Settings(aerosol=\"modified polluted\", cloud_type=\"Cu\", dt=.5 * si.s)\n", + "simulation = Simulation(settings, gccn=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "6e80d470aed47e31", + "metadata": { + "ExecuteTime": { + "end_time": "2024-03-04T14:32:17.032250Z", + "start_time": "2024-03-04T14:32:08.997662Z" + }, + "collapsed": false + }, + "outputs": [], + "source": [ + "output = simulation.run(\n", + " n_steps=int(1800 * si.m / settings.vertical_velocity / settings.dt),\n", + " steps_per_output_interval=5\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "6341693a2ee4bd21", + "metadata": { + "ExecuteTime": { + "end_time": "2024-03-04T14:32:17.774679Z", + "start_time": "2024-03-04T14:32:17.033939Z" + }, + "collapsed": false + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-03-04T15:32:17.729911\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ce978fbfbd9b4aea9bc15f871c64da80", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./Fig_6.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "figure(\n", + " output=output,\n", + " settings=settings,\n", + " simulation=simulation,\n", + " plot_drops_with_dry_radii_um=(.1, 1, 2, 3, 4, 5, 6, 7, 8, 9),\n", + " xlim_r_um=(0, 60),\n", + " xlim_S_percent=(-1.5, 1.5)\n", + ")\n", + "show_plot(\"Fig_6.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b900e87feeaef621", + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-14T11:19:44.850768Z", + "start_time": "2024-02-14T11:19:44.847362Z" + }, + "collapsed": false + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_8.ipynb b/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_8.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..6bbfa8b1b927a2c801f2c8256ea22f453e5c38d9 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_8.ipynb @@ -0,0 +1,1956 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "b03a1d7a3d3802d6", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_8.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_8.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Jensen_and_Nugent_2017/Fig_8.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "b4ff3463274b433f", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "#### based on Fig. 8 from [Jensen and Nugent (JAS 74) \"_Condensational Growth of Drops Formed on Giant Sea-Salt Aerosol Particles_\"](https://doi.org/10.1175/JAS-D-15-0370.1)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "initial_id", + "metadata": { + "ExecuteTime": { + "end_time": "2024-03-22T11:39:46.833587Z", + "start_time": "2024-03-22T11:39:46.829389Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "c91b89db396d508c", + "metadata": { + "ExecuteTime": { + "end_time": "2024-03-23T11:52:01.383132Z", + "start_time": "2024-03-23T11:51:59.196688Z" + }, + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "from PySDM_examples.Jensen_and_Nugent_2017 import Settings, Simulation\n", + "from open_atmos_jupyter_utils import show_plot\n", + "from PySDM_examples.Jensen_and_Nugent_2017.plotting import figure\n", + "\n", + "settings = Settings(aerosol=\"modified polluted\", cloud_type=\"Sc\")\n", + "simulation = Simulation(settings, gccn=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "846333e445434f6d", + "metadata": { + "ExecuteTime": { + "end_time": "2024-03-22T11:40:03.001232Z", + "start_time": "2024-03-22T11:39:53.824734Z" + }, + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "output = simulation.run()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "5503b3380487f50e", + "metadata": { + "ExecuteTime": { + "end_time": "2024-03-23T11:52:14.808782Z", + "start_time": "2024-03-23T11:52:14.490511Z" + }, + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-04-17T21:21:41.137244\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "6f8a203c020d4574aaec0e810a9ffbd5", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./Fig_8.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "masks = figure(\n", + " output=output,\n", + " settings=settings,\n", + " simulation=simulation,\n", + " plot_drops_with_dry_radii_um=(.1, 4.2),\n", + " xlim_r_um=(0, 60),\n", + " xlim_S_percent=(-1, 1),\n", + " return_masks=True\n", + ")\n", + "show_plot(\"Fig_8.pdf\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/__init__.py b/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..6c5602d73b1eebc09c0fe1f0a537cda8be14e02b --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/__init__.py @@ -0,0 +1,22 @@ +""" +Fig_1.ipynb: +.. include:: ./Fig_1.ipynb.badges.md + +Fig_3_and_Tab_4_upper_rows.ipynb: +.. include:: ./Fig_3_and_Tab_4_upper_rows.ipynb.badges.md + +Fig_4_and_7_and_Tab_4_bottom_rows.ipynb: +.. include:: ./Fig_4_and_7_and_Tab_4_bottom_rows.ipynb.badges.md + +Fig_5.ipynb: +.. include:: ./Fig_5.ipynb.badges.md + +Fig_6.ipynb: +.. include:: ./Fig_6.ipynb.badges.md + +Fig_8.ipynb: +.. include:: ./Fig_8.ipynb.badges.md +""" + +from .settings import Settings +from .simulation import Simulation diff --git a/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/plotting.py b/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/plotting.py new file mode 100644 index 0000000000000000000000000000000000000000..c798e15f8e9e5834ea2bf30b977a937b38f84c34 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/plotting.py @@ -0,0 +1,110 @@ +from matplotlib import pyplot +import numpy as np +from PySDM.physics import si, in_unit +from PySDM.physics.constants import PER_CENT + +CLOUD_BASE = 300 * si.m + + +def find_drop_ids_by_dry_size(plot_drops_with_dry_radii_um, simulation_r_dry): + drop_ids = [] + for drop_size_um in plot_drops_with_dry_radii_um: + drop_id = (np.abs(simulation_r_dry - drop_size_um * si.um)).argmin() + drop_ids.append(drop_id) + return drop_ids + + +def compute_table_values(height_above_cb_m, height_cb_m, products, ascent_mask): + """constructing data for Table 4""" + data = {"ascent": [], "descent": []} + for level_idx, height_m in enumerate(products["z"]): + rounded_height_above_cb_m = np.round(height_m - height_cb_m) + if rounded_height_above_cb_m in height_above_cb_m: + r_mean = products["r_mean_act"][level_idx] + r_standard_deviation = products["r_std_act"][level_idx] + r_relative_dispersion = r_standard_deviation / r_mean + + data["ascent" if ascent_mask[level_idx] else "descent"].append( + ( + f"{rounded_height_above_cb_m:.0f}", + f"{in_unit(r_mean, si.um):.2f}", + f"{in_unit(r_standard_deviation, si.um):.2f}", + f"{r_relative_dispersion:.3f}", + ) + ) + data["ascent"].append(data["descent"][0]) + return data + + +def figure( + *, + output, + settings, + simulation, + plot_drops_with_dry_radii_um, + xlim_r_um: tuple, + xlim_S_percent: tuple, + return_masks: bool = False, +): + y_axis = np.asarray(output["products"]["z"]) - settings.z0 - CLOUD_BASE + + masks = {} + if settings.t_end_of_ascent is None: + masks["ascent"] = np.full_like(output["products"]["t"], True, dtype=bool) + else: + masks["ascent"] = np.asarray(output["products"]["t"]) < settings.t_end_of_ascent + masks["descent"] = np.logical_not(masks["ascent"]) + + colors = {"ascent": "r", "descent": "b"} + + _, axs = pyplot.subplot_mosaic( + mosaic=[["r", "S"]], width_ratios=[3, 1], sharey=True, tight_layout=True + ) + + for label, mask in masks.items(): + axs["S"].plot( + in_unit(np.asarray(output["products"]["S_max"]), PER_CENT)[mask], + y_axis[mask], + label=label, + color=colors[label], + ) + axs["S"].set_xlim(*xlim_S_percent) + axs["S"].set_xlabel("S (%)", fontsize="15") + axs["S"].legend(fontsize="10") + + drop_ids = find_drop_ids_by_dry_size( + plot_drops_with_dry_radii_um=plot_drops_with_dry_radii_um, + simulation_r_dry=simulation.r_dry, + ) + + for ( + drop_id + ) in ( + drop_ids + ): # TODO #1266: bug! why rightmost drop is not 500 nm if size range is set to end at 500 nm??? + for label, mask in masks.items(): + axs["r"].plot( + in_unit(np.asarray(output["attributes"]["radius"][drop_id]), si.um)[ + mask + ], + y_axis[mask], + label=( + f"{in_unit(simulation.r_dry[drop_id], si.um):.2} µm" + if label == "ascent" + else "" + ), + color=colors[label], + ) + axs["r"].legend() + axs["r"].set_xlim(*xlim_r_um) + axs["r"].set_xlabel("r$_c$ (µm)", fontsize="15") + axs["r"].set_ylabel("Height above cloud base (m)", fontsize="15") + # pyplot.xticks(fontsize=12) + # pyplot.yticks(fontsize=12) + + for ax in axs.values(): + ax.grid() + + if return_masks: + return masks + return None diff --git a/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/settings.py b/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/settings.py new file mode 100644 index 0000000000000000000000000000000000000000..9f7006832d33adc7ff30753d74ba17128367f4c3 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/settings.py @@ -0,0 +1,66 @@ +from typing import Optional + +from pystrict import strict +from PySDM import Formulae +from PySDM.physics import si +from PySDM.initialisation.spectra import Lognormal, Sum + +INITIAL_RELATIVE_HUMIDITY = 0.8561 +INITIAL_TEMPERATURE = 284.3 * si.K +INITIAL_PRESSURE = 938.5 * si.hPa +INITIAL_ALTITUDE = 600 * si.metres + + +@strict +class Settings: + def __init__(self, *, aerosol: str, cloud_type: str, dt: Optional[float] = None): + self.p0 = INITIAL_PRESSURE + self.RH0 = INITIAL_RELATIVE_HUMIDITY + self.T0 = INITIAL_TEMPERATURE + self.z0 = INITIAL_ALTITUDE + self.t_end_of_ascent = 1500 * si.s if cloud_type == "Sc" else None + self.dt = dt or 1 * si.s # TODO #1266: not found in the paper yet + + # Table 1 from [Petters & Kreidenweis 2007](https://doi.org/10.5194/acp-7-1961-2007) + self.kappa = 1.28 + + self.formulae = Formulae( + saturation_vapour_pressure="FlatauWalkoCotton", # TODO #1266: Bolton + diffusion_kinetics="GrabowskiEtAl2011", + diffusion_thermics="GrabowskiEtAl2011", + constants={ + # values from appendix B + "MAC": 0.036, + "HAC": 0.7, + }, + ) + + self.vertical_velocity = { + # Table 2 in the paper + "Sc": lambda t: (1 if t < self.t_end_of_ascent else -1) * 0.4 * si.m / si.s, + "Cu": 2 * si.m / si.s, + }[cloud_type] + + self.dry_radii_spectrum = { + # Table 1 in the paper + "modified polluted": Sum( + ( + Lognormal( + norm_factor=48 / si.cm**3, m_mode=0.029 * si.um, s_geom=1.36 + ), + Lognormal( + norm_factor=114 / si.cm**3, m_mode=0.071 * si.um, s_geom=1.57 + ), + ) + ), + "pristine": Sum( + ( + Lognormal( + norm_factor=125 / si.cm**3, m_mode=0.011 * si.um, s_geom=1.2 + ), + Lognormal( + norm_factor=65 / si.cm**3, m_mode=0.06 * si.um, s_geom=1.7 + ), + ) + ), + }[aerosol] diff --git a/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/simulation.py b/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/simulation.py new file mode 100644 index 0000000000000000000000000000000000000000..827d833fffdbade94570471f2b0fb6187f38c835 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/simulation.py @@ -0,0 +1,128 @@ +import numpy as np +from PySDM_examples.utils import BasicSimulation +from PySDM_examples.Jensen_and_Nugent_2017.settings import Settings +from PySDM_examples.Jensen_and_Nugent_2017 import table_3 +from PySDM import Builder +from PySDM.physics import si +from PySDM.backends import CPU +from PySDM.products import ( + PeakSaturation, + ParcelDisplacement, + Time, + ActivatedMeanRadius, + RadiusStandardDeviation, +) +from PySDM.environments import Parcel +from PySDM.dynamics import Condensation, AmbientThermodynamics, Coalescence +from PySDM.dynamics.collisions.collision_kernels import Geometric +from PySDM.initialisation.sampling.spectral_sampling import Logarithmic + +# note: 100 in caption of Table 1 +N_SD_NON_GCCN = 100 + + +class Simulation(BasicSimulation): + def __init__( + self, + settings: Settings, + gccn: bool = False, + gravitational_coalsecence: bool = False, + ): + const = settings.formulae.constants + pvs_water = settings.formulae.saturation_vapour_pressure.pvs_water + initial_water_vapour_mixing_ratio = const.eps / ( + settings.p0 / settings.RH0 / pvs_water(settings.T0) - 1 + ) + + n_gccn = np.count_nonzero(table_3.NA) if gccn else 0 + + builder = Builder( + n_sd=N_SD_NON_GCCN + n_gccn, + backend=CPU( + formulae=settings.formulae, override_jit_flags={"parallel": False} + ), + environment=Parcel( + dt=settings.dt, + mass_of_dry_air=666 * si.kg, + p0=settings.p0, + initial_water_vapour_mixing_ratio=initial_water_vapour_mixing_ratio, + T0=settings.T0, + w=settings.vertical_velocity, + z0=settings.z0, + ), + ) + + additional_derived_attributes = ("radius", "equilibrium saturation") + for additional_derived_attribute in additional_derived_attributes: + builder.request_attribute(additional_derived_attribute) + + builder.add_dynamic( + AmbientThermodynamics() + ) # TODO #1266: order matters here, but error message is not saying it! + builder.add_dynamic(Condensation()) + if gravitational_coalsecence: + builder.add_dynamic(Coalescence(collision_kernel=Geometric())) + + self.r_dry, n_in_unit_volume = Logarithmic( + spectrum=settings.dry_radii_spectrum, + ).sample_deterministic(builder.particulator.n_sd - n_gccn) + + if gccn: + nonzero_concentration_mask = np.nonzero(table_3.NA) + self.r_dry = np.concatenate( + [self.r_dry, table_3.RD[nonzero_concentration_mask]] + ) + n_in_unit_volume = np.concatenate( + [n_in_unit_volume, table_3.NA[nonzero_concentration_mask]] + ) # TODO #1266: check which temp, pres, RH assumed in the paper for NA??? + + pd0 = settings.formulae.trivia.p_d( + settings.p0, initial_water_vapour_mixing_ratio + ) + rhod0 = settings.formulae.state_variable_triplet.rhod_of_pd_T(pd0, settings.T0) + + attributes = builder.particulator.environment.init_attributes( + n_in_dv=n_in_unit_volume + * builder.particulator.environment.mass_of_dry_air + / rhod0, + kappa=settings.kappa, + r_dry=self.r_dry, + ) + + super().__init__( + builder.build( + attributes=attributes, + products=( + PeakSaturation(name="S_max"), + ParcelDisplacement(name="z"), + Time(name="t"), + ActivatedMeanRadius( + name="r_mean_act", count_activated=True, count_unactivated=False + ), + RadiusStandardDeviation( + name="r_std_act", count_activated=True, count_unactivated=False + ), + ), + ) + ) + + # TODO #1266: copied from G & P 2023 + self.output_attributes = { + attr: tuple([] for _ in range(self.particulator.n_sd)) + for attr in additional_derived_attributes + } + + def run( + self, *, n_steps: int = 2250, steps_per_output_interval: int = 10 + ): # TODO #1266: essentially copied from G & P 2023 + output_products = super()._run( + nt=n_steps, steps_per_output_interval=steps_per_output_interval + ) + return {"products": output_products, "attributes": self.output_attributes} + + def _save(self, output): # TODO #1266: copied from G&P 2023 + for key, attr in self.output_attributes.items(): + attr_data = self.particulator.attributes[key].to_ndarray() + for drop_id in range(self.particulator.n_sd): + attr[drop_id].append(attr_data[drop_id]) + super()._save(output) diff --git a/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/table_3.py b/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/table_3.py new file mode 100644 index 0000000000000000000000000000000000000000..9dce83260cb8f7f9d61e5b9c20f7c9e4f8624912 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Jensen_and_Nugent_2017/table_3.py @@ -0,0 +1,55 @@ +import numpy as np + +from PySDM.physics import si + +NA = ( + np.asarray( + [ + 111800, + 68490, + 38400, + 21820, + 13300, + 8496, + 5486, + 3805, + 2593, + 1919, + 1278, + 998.4, + 777.9, + 519.5, + 400.5, + 376.9, + 265.3, + 212.4, + 137.8, + 121.4, + 100.9, + 122.2, + 50.64, + 38.3, + 55.47, + 21.45, + 12.95, + 43.23, + 26.26, + 30.5, + 4.385, + 4.372, + 4.465, + 4.395, + 4.427, + 4.411, + 0, + 0, + 0, + 4.522, + 0, + 4.542, + ] + ) + / si.m**3 +) + +RD = np.linspace(0.8, 9, num=len(NA), endpoint=True) * si.um diff --git a/PySDM/source/examples/PySDM_examples/Jouzel_and_Merlivat_1984/__init__.py b/PySDM/source/examples/PySDM_examples/Jouzel_and_Merlivat_1984/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..5a2385c73d9d3244928353e29585745bd0601dc7 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Jouzel_and_Merlivat_1984/__init__.py @@ -0,0 +1,11 @@ +# pylint: disable=invalid-name +""" +kinetic fractionation factors used in modelling isotopic effect +in snow formation, replacing saturation fractionation factors with +alpha_{saturation} alpha_{kinetic} +(see [Jouzel and Merlivat 1984](https://doi.org/10.1029/JD089iD07p11749)) + + +fig_8_9.ipynb: +.. include:: ./fig_8_9.ipynb.badges.md +""" diff --git a/PySDM/source/examples/PySDM_examples/Jouzel_and_Merlivat_1984/fig_8_9.ipynb b/PySDM/source/examples/PySDM_examples/Jouzel_and_Merlivat_1984/fig_8_9.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..590b38c25cfeb762bf543a55adad5c8e9f55259d --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Jouzel_and_Merlivat_1984/fig_8_9.ipynb @@ -0,0 +1,3127 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "e251450efd0afa5d", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Jouzel_and_Merlivat_1984/fig_8_9.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Jouzel_and_Merlivat_1984/fig_8_9.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Jouzel_and_Merlivat_1984/fig_8_9.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "841538e30be219a1", + "metadata": {}, + "source": [ + "# figs 8 and 9 from [Jouzel and Merlivat 1984](https://doi.org/10.1029/JD089iD07p11749) \"_Deuterium and oxygen 18 in precipitation: Modeling of the isotopic effects during snow formation_\"\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "466d06cabb3174f3", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-25T23:25:40.862379Z", + "start_time": "2025-06-25T23:25:40.859115Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "initial_id", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-25T23:25:42.736860Z", + "start_time": "2025-06-25T23:25:40.951437Z" + }, + "collapsed": true + }, + "outputs": [], + "source": [ + "from matplotlib import pyplot\n", + "import numpy as np\n", + "\n", + "from PySDM import Formulae\n", + "from PySDM_examples.Jouzel_and_Merlivat_1984.thermodynamic_profiles import ice_saturation_curve_4\n", + "from open_atmos_jupyter_utils import show_plot" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "87e416d611926ad5", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-25T23:25:43.030796Z", + "start_time": "2025-06-25T23:25:42.742196Z" + } + }, + "outputs": [], + "source": [ + "formulae = Formulae(\n", + " isotope_diffusivity_ratios=\"Stewart1975\",\n", + " isotope_equilibrium_fractionation_factors=\"Majoube1970\",\n", + " isotope_kinetic_fractionation_factors=\"JouzelAndMerlivat1984\"\n", + ")\n", + "const = formulae.constants\n", + "svp = formulae.saturation_vapour_pressure\n", + "C2K = formulae.trivia.C2K\n", + "K2C = formulae.trivia.K2C\n", + "\n", + "n_points = 100\n", + "T_0_50 = C2K(np.linspace(0.0, -50, n_points))" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "3f97b336a478a65a", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-25T23:25:44.982409Z", + "start_time": "2025-06-25T23:25:43.041753Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-06-26T01:25:44.970122\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.0, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "8c15bde98c9c4698979a4cb47ad75d48", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_8.pdf
\"), HTML(value=\"\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-06-26T01:25:45.940460\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.0, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "a478d96b93d34ed2936063de54732783", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_9.pdf
\"), HTML(value=\"\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2023-12-27T13:28:33.668245\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "a553869ce5db4af693432bb8e480d2aa", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./fig_1.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "figsize = (10, 6)\n", + "fig, axs = pyplot.subplots(1, 3, figsize=figsize, sharey=True)\n", + "\n", + "Y = np.asarray(output['t']) - 196 * si.s\n", + "\n", + "axs[0].plot(output['liquid water mixing ratio'], Y)\n", + "axs[0].set_ylim(0, 2400 * si.s)\n", + "axs[0].set_xlim(0, 2.5)\n", + "axs[0].set_yticks(np.linspace(0, 2400, 13, endpoint=True))\n", + "axs[0].set_ylabel('Time above cloud base (s)')\n", + "axs[0].set_xlabel('Liquid Water Content (g/kg)')\n", + "\n", + "axs[1].plot(np.asarray(output['aq_S_IV_ppb']) + np.asarray(output['gas_S_IV_ppb']), Y)\n", + "axs[1].set_xticks(np.linspace(0, 0.2, 5, endpoint=True))\n", + "axs[1].set_xlabel('SO2 conc (total ppb)')\n", + "\n", + "for product in (\n", + " 'pH_pH_number_weighted', \n", + " 'pH_pH_volume_weighted', \n", + " 'pH_conc_H_number_weighted', \n", + " 'pH_conc_H_volume_weighted'\n", + "):\n", + " axs[2].plot(output[product], Y, label=product)\n", + "axs[2].set_xlim(3.7, 5)\n", + "axs[2].set_xticks(np.linspace(3.8, 5, 7, endpoint=True))\n", + "axs[2].set_xlabel('average pH')\n", + "axs[2].legend()\n", + "\n", + "for ax in axs:\n", + " ax.grid()\n", + "show_plot('fig_1.pdf')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.8.7" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/PySDM/source/examples/PySDM_examples/Kreidenweis_et_al_2003/settings.py b/PySDM/source/examples/PySDM_examples/Kreidenweis_et_al_2003/settings.py new file mode 100644 index 0000000000000000000000000000000000000000..a80eed0fdf1be85c665d85fe559a1c54d73f94b8 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Kreidenweis_et_al_2003/settings.py @@ -0,0 +1,100 @@ +import numpy as np +from chempy import Substance +from pystrict import strict + +from PySDM import Formulae +from PySDM.dynamics.impl.chemistry_utils import AQUEOUS_COMPOUNDS +from PySDM.initialisation import spectra +from PySDM.initialisation.sampling import spectral_sampling as spec_sampling +from PySDM.physics import si +from PySDM.physics.constants import PPB, PPM + + +@strict +class Settings: + def __init__( + self, + dt: float, + n_sd: int, + n_substep: int, + spectral_sampling_class: spec_sampling.SpectralSampling = spec_sampling.Logarithmic, + spectral_sampling_method: str = "sample_deterministic", + ): + self.formulae = Formulae( + saturation_vapour_pressure="AugustRocheMagnus", + constants={"g_std": 10 * si.m / si.s**2}, + ) + const = self.formulae.constants + self.DRY_RHO = 1800 * si.kg / (si.m**3) + self.dry_molar_mass = Substance.from_formula("NH4HSO4").mass * si.gram / si.mole + + self.system_type = "closed" + + self.t_max = (2400 + 196) * si.s + self.output_interval = 10 * si.s + self.dt = dt + + self.w = 0.5 * si.m / si.s + + self.n_sd = n_sd + self.n_substep = n_substep + + self.p0 = 950 * si.mbar + self.T0 = 285.2 * si.K + pv0 = 0.95 * self.formulae.saturation_vapour_pressure.pvs_water(self.T0) + self.initial_water_vapour_mixing_ratio = const.eps * pv0 / (self.p0 - pv0) + self.kappa = 0.61 + + self.cloud_radius_range = (0.5 * si.micrometre, 25 * si.micrometre) + + self.mass_of_dry_air = 44 + + # note: rho is not specified in the paper + rho0 = 1 + + self.r_dry, self.n_in_dv = getattr( + spectral_sampling_class( + spectrum=spectra.Lognormal( + norm_factor=566 / si.cm**3 / rho0 * self.mass_of_dry_air, + m_mode=0.08 * si.um / 2, + s_geom=2, + ) + ), + spectral_sampling_method, + )(n_sd) + + self.ENVIRONMENT_MOLE_FRACTIONS = { + "SO2": 0.2 * PPB, + "O3": 50 * PPB, + "H2O2": 0.5 * PPB, + "CO2": 360 * PPM, + "HNO3": 0.1 * PPB, + "NH3": 0.1 * PPB, + } + + self.starting_amounts = { + "moles_" + + k: ( + self.formulae.trivia.volume(self.r_dry) + * self.DRY_RHO + / self.dry_molar_mass + if k in ("N_mIII", "S_VI") + else np.zeros(self.n_sd) + ) + for k in AQUEOUS_COMPOUNDS + } + + self.dry_radius_bins_edges = ( + np.logspace(np.log10(0.01 * si.um), np.log10(1 * si.um), 51, endpoint=True) + / 2 + ) + + @property + def nt(self): + nt = self.t_max / self.dt + assert nt == int(nt) + return int(nt) + + @property + def steps_per_output_interval(self) -> int: + return int(self.output_interval / self.dt) diff --git a/PySDM/source/examples/PySDM_examples/Kreidenweis_et_al_2003/simulation.py b/PySDM/source/examples/PySDM_examples/Kreidenweis_et_al_2003/simulation.py new file mode 100644 index 0000000000000000000000000000000000000000..425893e2178ea1e15591f03626b8c09071ad92c6 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Kreidenweis_et_al_2003/simulation.py @@ -0,0 +1,126 @@ +import numpy as np +from PySDM_examples.utils import BasicSimulation + +import PySDM.products as PySDM_products +from PySDM import Builder +from PySDM.backends import CPU +from PySDM.dynamics import AmbientThermodynamics, AqueousChemistry, Condensation +from PySDM.dynamics.impl.chemistry_utils import AQUEOUS_COMPOUNDS, GASEOUS_COMPOUNDS +from PySDM.environments import Parcel +from PySDM.physics import si + + +class Simulation(BasicSimulation): + def __init__(self, settings, products=None): + builder = Builder( + n_sd=settings.n_sd, + backend=CPU( + formulae=settings.formulae, override_jit_flags={"parallel": False} + ), + environment=Parcel( + dt=settings.dt, + mass_of_dry_air=settings.mass_of_dry_air, + p0=settings.p0, + initial_water_vapour_mixing_ratio=settings.initial_water_vapour_mixing_ratio, + T0=settings.T0, + w=settings.w, + ), + ) + + attributes = builder.particulator.environment.init_attributes( + n_in_dv=settings.n_in_dv, + kappa=settings.kappa, + r_dry=settings.r_dry, + include_dry_volume_in_attribute=False, + ) + attributes = { + **attributes, + **settings.starting_amounts, + } + + builder.add_dynamic(AmbientThermodynamics()) + builder.add_dynamic(Condensation()) + builder.add_dynamic( + AqueousChemistry( + environment_mole_fractions=settings.ENVIRONMENT_MOLE_FRACTIONS, + system_type=settings.system_type, + n_substep=settings.n_substep, + dry_rho=settings.DRY_RHO, + dry_molar_mass=settings.dry_molar_mass, + ) + ) + + products = products or ( + PySDM_products.AmbientRelativeHumidity(name="RH", unit="%"), + PySDM_products.WaterMixingRatio( + name="liquid water mixing ratio", + radius_range=[1 * si.um, np.inf], + unit="g/kg", + ), + PySDM_products.ParcelDisplacement(name="z"), + PySDM_products.AmbientPressure(name="p"), + PySDM_products.AmbientTemperature(name="T"), + PySDM_products.AmbientDryAirDensity(name="rhod"), + PySDM_products.AmbientWaterVapourMixingRatio( + name="water vapour mixing ratio", + var="water_vapour_mixing_ratio", + unit="g/kg", + ), + PySDM_products.Time(name="t"), + *( + PySDM_products.AqueousMoleFraction( + comp, unit="ppb", name=f"aq_{comp}_ppb" + ) + for comp in AQUEOUS_COMPOUNDS + ), + *( + PySDM_products.GaseousMoleFraction( + comp, unit="ppb", name=f"gas_{comp}_ppb" + ) + for comp in GASEOUS_COMPOUNDS + ), + PySDM_products.Acidity( + name="pH_pH_number_weighted", + radius_range=settings.cloud_radius_range, + weighting="number", + attr="pH", + ), + PySDM_products.Acidity( + name="pH_pH_volume_weighted", + radius_range=settings.cloud_radius_range, + weighting="volume", + attr="pH", + ), + PySDM_products.Acidity( + name="pH_conc_H_number_weighted", + radius_range=settings.cloud_radius_range, + weighting="number", + attr="conc_H", + ), + PySDM_products.Acidity( + name="pH_conc_H_volume_weighted", + radius_range=settings.cloud_radius_range, + weighting="volume", + attr="conc_H", + ), + PySDM_products.TotalDryMassMixingRatio( + settings.DRY_RHO, name="q_dry", unit="ug/kg" + ), + PySDM_products.PeakSaturation(unit="%", name="S_max_percent"), + PySDM_products.ParticleSpecificConcentration( + radius_range=settings.cloud_radius_range, name="n_c_mg", unit="mg^-1" + ), + PySDM_products.AqueousMassSpectrum( + key="S_VI", + dry_radius_bins_edges=settings.dry_radius_bins_edges, + name="dm_S_VI/dlog_10(dry diameter)", + unit="ug / m^3", + ), + ) + + particulator = builder.build(attributes=attributes, products=products) + self.settings = settings + super().__init__(particulator=particulator) + + def run(self): + return super()._run(self.settings.nt, self.settings.steps_per_output_interval) diff --git a/PySDM/source/examples/PySDM_examples/Lamb_et_al_2017/__init__.py b/PySDM/source/examples/PySDM_examples/Lamb_et_al_2017/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..8c592ccbbc5f5059b91c27164f021e2ad961410f --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Lamb_et_al_2017/__init__.py @@ -0,0 +1,8 @@ +# pylint: disable=invalid-name +""" +Formulae-only example depicting equilibrium isotopic fractionation over ice based on +[Lamb et al. 2017](https://doi.org/10.1073/pnas.1618374114) + +fig_4.ipynb: +.. include:: ./fig_4.ipynb.badges.md +""" diff --git a/PySDM/source/examples/PySDM_examples/Lamb_et_al_2017/fig_4.ipynb b/PySDM/source/examples/PySDM_examples/Lamb_et_al_2017/fig_4.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..962beb02ec12f2c9b8e952eaf921903f97675a46 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Lamb_et_al_2017/fig_4.ipynb @@ -0,0 +1,1801 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "624a95a895b096b2", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Lamb_et_al_2017/fig_4.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Lamb_et_al_2017/fig_4.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Lamb_et_al_2017/fig_4.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "c288f2ecf3a1c2f1", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "#### based on Fig. 4 from Lamb et al. 2017 (PNAS) \"_Laboratory measurements of HDO/H2O isotopic fractionation during ice deposition in simulated cirrus clouds_\"\n", + "https://doi.org/10.1073/pnas.1618374114" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "0294286f-145a-4f58-a97b-a0a42486e2cf", + "metadata": {}, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "initial_id", + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-10T23:26:11.044820926Z", + "start_time": "2024-02-10T23:26:11.037101465Z" + } + }, + "outputs": [], + "source": [ + "from open_atmos_jupyter_utils import show_plot\n", + "from PySDM import Formulae\n", + "from PySDM.physics import constants_defaults\n", + "from matplotlib import pyplot\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "61856a8bd8035c7e", + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-10T23:26:30.381539986Z", + "start_time": "2024-02-10T23:26:30.368003667Z" + }, + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "PAPERS = {\n", + " 'MerlivatAndNief1967': 'red',\n", + " 'EllehojEtAl2013': 'blue',\n", + " 'LambEtAl2017': 'black',\n", + "}\n", + "\n", + "T = np.linspace(180, constants_defaults.T0)\n", + "alphas = {\n", + " paper: Formulae(\n", + " isotope_equilibrium_fractionation_factors=paper\n", + " ).isotope_equilibrium_fractionation_factors.alpha_i_2H(T)\n", + " for paper in PAPERS\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "129c78ed6b12197e", + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-10T23:26:31.079798656Z", + "start_time": "2024-02-10T23:26:30.696551402Z" + }, + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-02-11T00:52:33.984919\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f92b021944f4409bba97909e7e7e267c", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./fig_4.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "for label, alpha in alphas.items():\n", + " pyplot.plot(T, alpha, label=label, color=PAPERS[label])\n", + "pyplot.grid()\n", + "pyplot.legend()\n", + "pyplot.xlabel('Temperature (K)')\n", + "pyplot.xlim(T[0], T[-1])\n", + "pyplot.xticks(np.linspace(180, 270, 10))\n", + "pyplot.ylabel('$α_{eq}$')\n", + "pyplot.ylim(1.13, 1.6)\n", + "pyplot.yticks(np.linspace(1.15, 1.6, 10))\n", + "show_plot('fig_4.pdf')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3b6e881ffac9b428", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/PySDM/source/examples/PySDM_examples/Lowe_et_al_2019/__init__.py b/PySDM/source/examples/PySDM_examples/Lowe_et_al_2019/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..ac98484029a4365561facefd429951f14a0bce4b --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Lowe_et_al_2019/__init__.py @@ -0,0 +1,19 @@ +# pylint: disable=invalid-name +""" +parcel-model example focused on surfactants based on +[Lowe et al. 2019 (Nature Comm.)](https://doi.org/10.1038/s41467-019-12982-0) + +fig_1.ipynb: +.. include:: ./fig_1.ipynb.badges.md + +fig_2.ipynb: +.. include:: ./fig_2.ipynb.badges.md + +fig_3.ipynb: +.. include:: ./fig_3.ipynb.badges.md + +fig_s2.ipynb: +.. include:: ./fig_s2.ipynb.badges.md +""" +from .settings import Settings +from .simulation import Simulation diff --git a/PySDM/source/examples/PySDM_examples/Lowe_et_al_2019/aerosol.py b/PySDM/source/examples/PySDM_examples/Lowe_et_al_2019/aerosol.py new file mode 100644 index 0000000000000000000000000000000000000000..9d47470e47444a726a1b5e24be257fbcf8bdb139 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Lowe_et_al_2019/aerosol.py @@ -0,0 +1,209 @@ +from chempy import Substance +from pystrict import strict + +from PySDM.initialisation import spectra +from PySDM.initialisation.aerosol_composition import DryAerosolMixture +from PySDM.physics import si + + +@strict +class AerosolMarine(DryAerosolMixture): + def __init__( + self, water_molar_volume: float, Forg: float = 0.2, Acc_N2: float = 134 + ): + Aitken = { + "palmitic": Forg, + "(NH4)2SO4": (1 - Forg), + "NaCl": 0, + } + Accumulation = { + "palmitic": Forg, + "(NH4)2SO4": 0, + "NaCl": (1 - Forg), + } + + super().__init__( + ionic_dissociation_phi={ + "palmitic": 1, + "(NH4)2SO4": 3, + "NaCl": 2, + }, + is_soluble={ + "palmitic": False, + "(NH4)2SO4": True, + "NaCl": True, + }, + densities={ + "palmitic": 0.852 * si.g / si.cm**3, + "(NH4)2SO4": 1.77 * si.g / si.cm**3, + "NaCl": 2.16 * si.g / si.cm**3, + }, + compounds=("palmitic", "(NH4)2SO4", "NaCl"), + molar_masses={ + "palmitic": 256.4 * si.g / si.mole, + "(NH4)2SO4": Substance.from_formula("(NH4)2SO4").mass + * si.gram + / si.mole, + "NaCl": Substance.from_formula("NaCl").mass * si.gram / si.mole, + }, + ) + self.modes = ( + { + "f_org": 1 - self.f_soluble_volume(Aitken), + "kappa": self.kappa(Aitken, water_molar_volume=water_molar_volume), + "nu_org": self.nu_org(Aitken), + "spectrum": spectra.Lognormal( + norm_factor=226 / si.cm**3, m_mode=19.6 * si.nm, s_geom=1.71 + ), + }, + { + "f_org": 1 - self.f_soluble_volume(Accumulation), + "kappa": self.kappa( + Accumulation, water_molar_volume=water_molar_volume + ), + "nu_org": self.nu_org(Accumulation), + "spectrum": spectra.Lognormal( + norm_factor=Acc_N2 / si.cm**3, m_mode=69.5 * si.nm, s_geom=1.7 + ), + }, + ) + + color = "dodgerblue" + + +@strict +class AerosolBoreal(DryAerosolMixture): + def __init__( + self, water_molar_volume: float, Forg: float = 0.668, Acc_N2: float = 540 + ): + # note: SOA1 or SOA2 unclear from the paper + Aitken = { + "SOA1": Forg, + "SOA2": 0, + "(NH4)2SO4": (1 - Forg) / 2, + "NH4NO3": (1 - Forg) / 2, + } + Accumulation = { + "SOA1": 0, + "SOA2": Forg, + "(NH4)2SO4": (1 - Forg) / 2, + "NH4NO3": (1 - Forg) / 2, + } + + super().__init__( + ionic_dissociation_phi={ + "SOA1": 1, + "SOA2": 1, + "(NH4)2SO4": 3, + "NH4NO3": 2, + }, + molar_masses={ + "(NH4)2SO4": Substance.from_formula("(NH4)2SO4").mass + * si.gram + / si.mole, + "NH4NO3": Substance.from_formula("NH4NO3").mass * si.gram / si.mole, + "SOA1": 190 * si.g / si.mole, + "SOA2": 368.4 * si.g / si.mole, + }, + densities={ + "SOA1": 1.24 * si.g / si.cm**3, + "SOA2": 1.2 * si.g / si.cm**3, + "(NH4)2SO4": 1.77 * si.g / si.cm**3, + "NH4NO3": 1.72 * si.g / si.cm**3, + }, + compounds=("SOA1", "SOA2", "(NH4)2SO4", "NH4NO3"), + is_soluble={ + "SOA1": False, + "SOA2": False, + "(NH4)2SO4": True, + "NH4NO3": True, + }, + ) + self.modes = ( + { + "f_org": 1 - self.f_soluble_volume(Aitken), + "kappa": self.kappa(Aitken, water_molar_volume=water_molar_volume), + "nu_org": self.nu_org(Aitken), + "spectrum": spectra.Lognormal( + norm_factor=1110 / si.cm**3, m_mode=22.7 * si.nm, s_geom=1.75 + ), + }, + { + "f_org": 1 - self.f_soluble_volume(Accumulation), + "kappa": self.kappa( + Accumulation, water_molar_volume=water_molar_volume + ), + "nu_org": self.nu_org(Accumulation), + "spectrum": spectra.Lognormal( + norm_factor=Acc_N2 / si.cm**3, + m_mode=82.2 * si.nm, + s_geom=1.62, + ), + }, + ) + + color = "yellowgreen" + + +@strict +class AerosolNascent(DryAerosolMixture): + def __init__( + self, water_molar_volume: float, Acc_Forg: float = 0.3, Acc_N2: float = 30 + ): + Ultrafine = { + "SOA1": 0.52, + "SOA2": 0, + "(NH4)2SO4": 0.48, + } + Accumulation = { + "SOA1": 0, + "SOA2": Acc_Forg, + "(NH4)2SO4": (1 - Acc_Forg), + } + super().__init__( + ionic_dissociation_phi={ + "SOA1": 1, + "SOA2": 1, + "(NH4)2SO4": 3, + }, + molar_masses={ + "SOA1": 190 * si.g / si.mole, + "SOA2": 368.4 * si.g / si.mole, + "(NH4)2SO4": Substance.from_formula("(NH4)2SO4").mass + * si.gram + / si.mole, + }, + densities={ + "SOA1": 1.24 * si.g / si.cm**3, + "SOA2": 1.2 * si.g / si.cm**3, + "(NH4)2SO4": 1.77 * si.g / si.cm**3, + }, + compounds=("SOA1", "SOA2", "(NH4)2SO4"), + is_soluble={ + "SOA1": False, + "SOA2": False, + "(NH4)2SO4": True, + }, + ) + self.modes = ( + { + "f_org": 1 - self.f_soluble_volume(Ultrafine), + "kappa": self.kappa(Ultrafine, water_molar_volume=water_molar_volume), + "nu_org": self.nu_org(Ultrafine), + "spectrum": spectra.Lognormal( + norm_factor=2000 / si.cm**3, m_mode=11.5 * si.nm, s_geom=1.71 + ), + }, + { + "f_org": 1 - self.f_soluble_volume(Accumulation), + "kappa": self.kappa( + Accumulation, water_molar_volume=water_molar_volume + ), + "nu_org": self.nu_org(Accumulation), + "spectrum": spectra.Lognormal( + norm_factor=Acc_N2 / si.cm**3, m_mode=100 * si.nm, s_geom=1.70 + ), + }, + ) + + color = "orangered" diff --git a/PySDM/source/examples/PySDM_examples/Lowe_et_al_2019/aerosol_code.py b/PySDM/source/examples/PySDM_examples/Lowe_et_al_2019/aerosol_code.py new file mode 100644 index 0000000000000000000000000000000000000000..7f61a69c753faa0ec354b023dbb2e12a44b84874 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Lowe_et_al_2019/aerosol_code.py @@ -0,0 +1,261 @@ +from chempy import Substance +from pystrict import strict + +from PySDM.initialisation import spectra +from PySDM.initialisation.aerosol_composition import DryAerosolMixture +from PySDM.physics import si + + +@strict +class AerosolMarine(DryAerosolMixture): + # cd MAV + # MODAL_PARS.CONC = [223 137]; %[number/cm3] + # MODAL_PARS.GSD = [1.68 1.68]; + # MODAL_PARS.GEOMEAN_DIAM = [0.0390 0.139]; %[um] + # DENSITY(4:5) = 0.852; %Set organic density. Palmitic acid 256.4 [gmol-1] + # MASS_FRAC = [0 0.8 0 0.2 0 0 0 0.0;... + # 0 0.0 0 0.2 0 0 0 0.8]; + # NAT = 2; + # NMODE = [1 1]; + # DENSITY = [1.841, 1.78, 1.77, 0.852, 1.5, 2., 2.65, 2.165]; %[gcm-3] + + def __init__( + self, water_molar_volume: float, Forg: float = 0.2, Acc_N2: float = 137 + ): + Aitken = { + "palmitic": Forg, + "(NH4)2SO4": (1 - Forg), + "NaCl": 0, + } + Accumulation = { + "palmitic": Forg, + "(NH4)2SO4": 0, + "NaCl": (1 - Forg), + } + + super().__init__( + ionic_dissociation_phi={ + "palmitic": 1, + "(NH4)2SO4": 3, + "NaCl": 2, + }, + is_soluble={ + "palmitic": False, + "(NH4)2SO4": True, + "NaCl": True, + }, + densities={ + "palmitic": 0.852 * si.g / si.cm**3, + "(NH4)2SO4": 1.78 * si.g / si.cm**3, + "NaCl": 2.165 * si.g / si.cm**3, + }, + compounds=("palmitic", "(NH4)2SO4", "NaCl"), + molar_masses={ + "palmitic": 256.4 * si.g / si.mole, + "(NH4)2SO4": Substance.from_formula("(NH4)2SO4").mass + * si.gram + / si.mole, + "NaCl": Substance.from_formula("NaCl").mass * si.gram / si.mole, + }, + ) + self.modes = ( + { + "f_org": 1 - self.f_soluble_volume(Aitken), + "kappa": self.kappa( + mass_fractions=Aitken, water_molar_volume=water_molar_volume + ), + "nu_org": self.nu_org(Aitken), + "spectrum": spectra.Lognormal( + norm_factor=223 / si.cm**3, m_mode=19.6 * si.nm, s_geom=1.68 + ), + }, + { + "f_org": 1 - self.f_soluble_volume(Accumulation), + "kappa": self.kappa( + mass_fractions=Accumulation, water_molar_volume=water_molar_volume + ), + "nu_org": self.nu_org(Accumulation), + "spectrum": spectra.Lognormal( + norm_factor=Acc_N2 / si.cm**3, m_mode=69.5 * si.nm, s_geom=1.68 + ), + }, + ) + + color = "dodgerblue" + + +@strict +class AerosolBoreal(DryAerosolMixture): + # cd HYY + # MODAL_PARS.CONC = [1110 540]; + # MODAL_PARS.GSD = [1.75 1.62]; + # MODAL_PARS.GEOMEAN_DIAM = [0.0453 0.1644]; + # DENSITY(4:5) = [1.2 1.4]; %Set organic density + # DENSITY(1) = 1.72; + # INORG_MASS_RATIO = 0.1515/0.1559; %ammonium sulfate:ammonium nitrate mass + # FORG = 0.60; + # MASS_FRAC = [(1-FORG)/(1+INORG_MASS_RATIO) 0 ... + # INORG_MASS_RATIO*(1-FORG)/(1+INORG_MASS_RATIO) FORG... + # 0 0 0 0;... + # (1-FORG)/(1+INORG_MASS_RATIO) 0 ... + # INORG_MASS_RATIO*(1-FORG)/(1+INORG_MASS_RATIO) 0 ... + # FORG 0 0 0]; + # NAT = 2; + # NMODE = [1 1]; + + # DENSITY = [1.841, 1.78, 1.77, 1.5, 1.5, 2., 2.65, 2.165]; %[gcm-3] + # DENSITY = [1.72, 1.78, 1.77, 1.2, 1.4, 2., 2.65, 2.165]; %[gcm-3] + + def __init__( + self, water_molar_volume: float, Forg: float = 0.668, Acc_N2: float = 540 + ): + # TODO #1247: SOA1 or SOA2 unclear from the paper + # TODO #1247: CAN'T FIND WHERE NH4NO3 PROPERTIES ARE DEFINED IN ICPM + INORG_MASS_RATIO = 0.1515 / 0.1559 + Aitken = { + "SOA1": Forg, + "SOA2": 0, + "(NH4)2SO4": INORG_MASS_RATIO * (1 - Forg) / (1 + INORG_MASS_RATIO), + "NH4NO3": (1 - Forg) / (1 + INORG_MASS_RATIO), + } + Accumulation = { + "SOA1": 0, + "SOA2": Forg, + "(NH4)2SO4": INORG_MASS_RATIO * (1 - Forg) / (1 + INORG_MASS_RATIO), + "NH4NO3": (1 - Forg) / (1 + INORG_MASS_RATIO), + } + + super().__init__( + ionic_dissociation_phi={ + "SOA1": 1, + "SOA2": 1, + "(NH4)2SO4": 3, + "NH4NO3": 2, + }, + molar_masses={ + "(NH4)2SO4": Substance.from_formula("(NH4)2SO4").mass + * si.gram + / si.mole, + "NH4NO3": Substance.from_formula("NH4NO3").mass * si.gram / si.mole, + "SOA1": 190 * si.g / si.mole, # TODO #1247: 190 OR 200? + "SOA2": 368.4 * si.g / si.mole, # TODO #1247: 368.4 OR 200? + }, + densities={ + "SOA1": 1.2 * si.g / si.cm**3, + "SOA2": 1.4 * si.g / si.cm**3, + "(NH4)2SO4": 1.77 * si.g / si.cm**3, + "NH4NO3": 1.72 * si.g / si.cm**3, + }, + compounds=("SOA1", "SOA2", "(NH4)2SO4", "NH4NO3"), + is_soluble={ + "SOA1": False, + "SOA2": False, + "(NH4)2SO4": True, + "NH4NO3": True, + }, + ) + self.modes = ( + { + "f_org": 1 - self.f_soluble_volume(Aitken), + "kappa": self.kappa( + mass_fractions=Aitken, water_molar_volume=water_molar_volume + ), + "nu_org": self.nu_org(Aitken), + "spectrum": spectra.Lognormal( + norm_factor=1110 / si.cm**3, m_mode=22.65 * si.nm, s_geom=1.75 + ), + }, + { + "f_org": 1 - self.f_soluble_volume(Accumulation), + "kappa": self.kappa( + mass_fractions=Accumulation, water_molar_volume=water_molar_volume + ), + "nu_org": self.nu_org(Accumulation), + "spectrum": spectra.Lognormal( + norm_factor=Acc_N2 / si.cm**3, + m_mode=82.2 * si.nm, + s_geom=1.62, + ), + }, + ) + + color = "yellowgreen" + + +@strict +class AerosolNascent(DryAerosolMixture): + # cd NUM + # MODAL_PARS.CONC = [2000 30]; + # MODAL_PARS.GSD = [1.71 1.703]; + # MODAL_PARS.GEOMEAN_DIAM = [0.023 0.200]; + # DENSITY(4:5) = [1.2 1.24]; %Set organic density + # MASS_FRAC = [0 0 0.48 0.52 0 0 0 0;... + # 0 0 0.70 0.0 0.3 0 0 0]; + # NAT = 2; + # NMODE = [1 1]; + + # DENSITY = [1.841, 1.78, 1.77, 1.2, 1.24, 2., 2.65, 2.165]; %[gcm-3] + + def __init__( + self, water_molar_volume: float, Acc_Forg: float = 0.3, Acc_N2: float = 30 + ): + # TODO #1247: CAN'T FIND WHEN PHI IS MULTIPLIED FOR KÖHLER B IN ICPM CODE + Ultrafine = { + "SOA1": 0.52, + "SOA2": 0, + "(NH4)2SO4": 0.48, + } + Accumulation = { + "SOA1": 0, + "SOA2": Acc_Forg, + "(NH4)2SO4": (1 - Acc_Forg), + } + super().__init__( + ionic_dissociation_phi={ + "SOA1": 1, + "SOA2": 1, + "(NH4)2SO4": 3, + }, + molar_masses={ + "SOA1": 190 * si.g / si.mole, # TODO #1247: 190 OR 200? + "SOA2": 368.4 * si.g / si.mole, # TODO #1247: 368.4 OR 200? + "(NH4)2SO4": Substance.from_formula("(NH4)2SO4").mass + * si.gram + / si.mole, + }, + densities={ + "SOA1": 1.2 * si.g / si.cm**3, + "SOA2": 1.24 * si.g / si.cm**3, + "(NH4)2SO4": 1.77 * si.g / si.cm**3, + }, + compounds=("SOA1", "SOA2", "(NH4)2SO4"), + is_soluble={ + "SOA1": False, + "SOA2": False, + "(NH4)2SO4": True, + }, + ) + self.modes = ( + { + "f_org": 1 - self.f_soluble_volume(Ultrafine), + "kappa": self.kappa( + mass_fractions=Ultrafine, water_molar_volume=water_molar_volume + ), + "nu_org": self.nu_org(Ultrafine), + "spectrum": spectra.Lognormal( + norm_factor=2000 / si.cm**3, m_mode=11.5 * si.nm, s_geom=1.71 + ), + }, + { + "f_org": 1 - self.f_soluble_volume(Accumulation), + "kappa": self.kappa( + mass_fractions=Accumulation, water_molar_volume=water_molar_volume + ), + "nu_org": self.nu_org(Accumulation), + "spectrum": spectra.Lognormal( + norm_factor=Acc_N2 / si.cm**3, m_mode=100 * si.nm, s_geom=1.703 + ), + }, + ) + + color = "orangered" diff --git a/PySDM/source/examples/PySDM_examples/Lowe_et_al_2019/constants_def.py b/PySDM/source/examples/PySDM_examples/Lowe_et_al_2019/constants_def.py new file mode 100644 index 0000000000000000000000000000000000000000..aad63332fc037d5ed88b538bc61e193b13d1b8c3 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Lowe_et_al_2019/constants_def.py @@ -0,0 +1,13 @@ +from PySDM.physics import constants_defaults, si + +LOWE_CONSTS = { + "sgm_org": 40 * si.mN / si.m, + "delta_min": 0.1 + * si.nm, # 0.2 in the paper, but 0.1 matches the paper plot fig 1c and 1d + "MAC": 1, + "HAC": 1, + "c_pd": 1006 * si.joule / si.kilogram / si.kelvin, + "g_std": 9.81 * si.metre / si.second**2, + "Md": constants_defaults.R_str / 287.058 * si.joule / si.kelvin / si.kg, + "Mv": constants_defaults.R_str / 461 * si.joule / si.kelvin / si.kg, +} diff --git a/PySDM/source/examples/PySDM_examples/Lowe_et_al_2019/fig_1.ipynb b/PySDM/source/examples/PySDM_examples/Lowe_et_al_2019/fig_1.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..5346acf09f05bf3d36dcaaf3949ee655c1997db8 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Lowe_et_al_2019/fig_1.ipynb @@ -0,0 +1,5523 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Lowe_et_al_2019/fig_1.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Lowe_et_al_2019/fig_1.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Lowe_et_al_2019/fig_1.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### based on Fig. 1 from Lowe et al. 2019 (Nature Comm.) \"_Key drivers of cloud response to surface-active organics_\"\n", + "https://doi.org/10.1038/s41467-019-12982-0" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2023-12-29T11:52:23.879155Z", + "start_time": "2023-12-29T11:52:23.870444Z" + }, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "from matplotlib import pyplot\n", + "import numpy as np\n", + "from PySDM import Formulae\n", + "from PySDM.initialisation import spectra\n", + "from PySDM.physics import si\n", + "from open_atmos_jupyter_utils import show_plot\n", + "\n", + "from PySDM_examples.Lowe_et_al_2019.aerosol_code import AerosolMarine, AerosolBoreal, AerosolNascent\n", + "from PySDM_examples.Lowe_et_al_2019.constants_def import LOWE_CONSTS" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2023-12-29T11:52:30.261687Z", + "start_time": "2023-12-29T11:52:28.588326Z" + } + }, + "outputs": [], + "source": [ + "FORMULAE = Formulae(constants=LOWE_CONSTS)\n", + "WATER_MOLAR_VOLUME = FORMULAE.constants.water_molar_volume\n", + "cases = {\n", + " 'Marine (MA)': AerosolMarine(water_molar_volume=WATER_MOLAR_VOLUME),\n", + " 'Boreal (HYY)': AerosolBoreal(water_molar_volume=WATER_MOLAR_VOLUME),\n", + " 'NUM event (NE)': AerosolNascent(water_molar_volume=WATER_MOLAR_VOLUME)\n", + "}\n", + "\n", + "formulae_bulk = Formulae(\n", + " surface_tension='Constant',\n", + " constants=LOWE_CONSTS, \n", + ")\n", + "formulae_surf = Formulae(\n", + " surface_tension='CompressedFilmOvadnevaite',\n", + " constants=LOWE_CONSTS,\n", + ")\n", + "\n", + "r_wet = np.logspace(np.log(150 * si.nm), np.log(3000 * si.nm), base=np.e, num=100)\n", + "r_dry = 50 * si.nm\n", + "v_wet = formulae_surf.trivia.volume(r_wet)\n", + "v_dry = formulae_surf.trivia.volume(r_dry)\n", + "T = 300 * si.K\n", + "r_wet_ticks_nm = (300, 500, 700, 1000, 3000)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Fig. 1 a" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2023-12-29T11:52:32.024468Z", + "start_time": "2023-12-29T11:52:30.265333Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-01-27T17:49:09.593011\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.5.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "1bf7183749b7407abc174d1c93f5b383", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./fig_1a.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "x = np.logspace(np.log(5 * si.nm), np.log(1000 * si.nm), base=np.e)\n", + "for k, v in cases.items():\n", + " spec = spectra.Sum(\n", + " tuple(v.modes[i]['spectrum'] for i in range(len(v.modes)))\n", + " )\n", + " pyplot.loglog(x / si.nm, spec.size_distribution(x)*x / si.cm**-3, label=k, color=v.color)\n", + "pyplot.ylim(1, 2500)\n", + "pyplot.xlim(5, 1000)\n", + "pyplot.legend()\n", + "pyplot.grid()\n", + "pyplot.xlabel('Dry aerosol radius [nm]')\n", + "pyplot.ylabel('Aerosol number concentration\\n size distribution [cm$^{-3}$]')\n", + "show_plot(\"fig_1a.pdf\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Fig. 1 c" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "ExecuteTime": { + "end_time": "2023-12-29T11:52:34.847271Z", + "start_time": "2023-12-29T11:52:32.038091Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-01-27T17:49:16.181163\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.5.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "0f04be1a13d4445e92d2f500c5ac9849", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./fig_1c.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "rd3 = r_dry**3\n", + " \n", + "for formulae in (formulae_bulk, formulae_surf):\n", + " for k, v in cases.items():\n", + " sigma = formulae.surface_tension.sigma(np.nan, v_wet, v_dry, v.modes[0]['f_org'])\n", + " model = formulae.surface_tension.__name__\n", + " RH_eq = formulae.hygroscopicity.RH_eq(r_wet, T, v.modes[0]['kappa'][model], rd3, sigma)\n", + " pyplot.plot(\n", + " r_wet / si.nm, \n", + " (RH_eq - 1) * 100, \n", + " label=f\"{k}\" if model == 'Constant' else \"\",\n", + " color=v.color, \n", + " linestyle='-' if model == 'Constant' else '--'\n", + " )\n", + " pyplot.plot(r_wet / si.nm, np.ones_like(r_wet), color=\"k\", linestyle='-' if model == 'Constant' else '--', label=f\"{model}\")\n", + "pyplot.grid()\n", + "pyplot.xscale('log')\n", + "pyplot.xticks(r_wet_ticks_nm, r_wet_ticks_nm)\n", + "pyplot.xlabel('Wet radius [nm]')\n", + "pyplot.xlim(r_wet[0] / si.nm, r_wet[-1] / si.nm)\n", + "pyplot.ylabel('Equilibrium supersaturation [%]')\n", + "yticks = (-.1, 0, .1, .2, .3)\n", + "pyplot.yticks(yticks, yticks)\n", + "pyplot.ylim(yticks[0], 0.35)\n", + "pyplot.legend()\n", + "show_plot(\"fig_1c.pdf\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Fig. 1 d" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "ExecuteTime": { + "end_time": "2023-12-29T11:52:35.602881Z", + "start_time": "2023-12-29T11:52:34.847287Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-01-27T17:49:11.823914\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.5.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "828ab372642943abbecdea3b846fa682", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./fig_1d.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "for k, v in cases.items():\n", + " sigma = formulae_surf.surface_tension.sigma(np.nan, v_wet, v_dry, v.modes[0]['f_org'])\n", + " pyplot.plot(r_wet / si.nm, sigma / (si.mN / si.m), label=k, color=v.color, linestyle='--')\n", + "pyplot.plot(r_wet / si.nm, np.full_like(r_wet, formulae_bulk.constants.sgm_w / (si.mN / si.m)), label='pure water', color='gray')\n", + "pyplot.grid()\n", + "pyplot.xscale('log')\n", + "pyplot.xticks(r_wet_ticks_nm, r_wet_ticks_nm)\n", + "yticks = (20, 40, 60, 80)\n", + "pyplot.yticks(yticks, yticks)\n", + "pyplot.xlim(r_wet[0] / si.nm, r_wet[-1] / si.nm)\n", + "pyplot.ylim(11, 85)\n", + "pyplot.xlabel('Wet radius [nm]')\n", + "pyplot.ylabel('Surface tension [mN m$^{-1}$]')\n", + "pyplot.legend()\n", + "show_plot(\"fig_1d.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2023-12-29T11:52:35.607915Z", + "start_time": "2023-12-29T11:52:35.603936Z" + } + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.10.9 ('pysdm')", + "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.9" + }, + "vscode": { + "interpreter": { + "hash": "b14f34a08619f4a218d80d7380beed3f0c712c89ff93e7183219752d640ed427" + } + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/PySDM/source/examples/PySDM_examples/Lowe_et_al_2019/fig_2.ipynb b/PySDM/source/examples/PySDM_examples/Lowe_et_al_2019/fig_2.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..b7b8dd498d80a1c3fef45c7293847a475fba7715 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Lowe_et_al_2019/fig_2.ipynb @@ -0,0 +1,3866 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Lowe_et_al_2019/fig_2.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Lowe_et_al_2019/fig_2.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Lowe_et_al_2019/fig_2.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### based on Fig. 2 from Lowe et al. 2019 (Nature Comm.) \"_Key drivers of cloud response to surface-active organics_\" \n", + "https://doi.org/10.1038/s41467-019-12982-0" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-15T07:32:09.342496Z", + "start_time": "2025-06-15T07:32:09.336429Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-15T07:32:12.834828Z", + "start_time": "2025-06-15T07:32:09.355937Z" + } + }, + "outputs": [], + "source": [ + "from PySDM_examples.Lowe_et_al_2019 import Settings, Simulation\n", + "from PySDM_examples.Lowe_et_al_2019.aerosol_code import AerosolBoreal, AerosolMarine, AerosolNascent\n", + "from PySDM_examples.Lowe_et_al_2019.constants_def import LOWE_CONSTS\n", + "from open_atmos_jupyter_utils import show_plot\n", + "\n", + "from PySDM import Formulae\n", + "from PySDM.initialisation.spectra import Sum\n", + "from PySDM.physics import si\n", + "\n", + "import numpy as np\n", + "import matplotlib\n", + "from matplotlib import pyplot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-15T07:32:51.723624Z", + "start_time": "2025-06-15T07:32:12.926684Z" + } + }, + "outputs": [], + "source": [ + "output = {}\n", + "\n", + "FORMULAE = Formulae(constants=LOWE_CONSTS)\n", + "WATER_MOLAR_VOLUME = FORMULAE.constants.water_molar_volume\n", + "for aerosol in (\n", + " AerosolMarine(water_molar_volume=WATER_MOLAR_VOLUME), \n", + " AerosolBoreal(water_molar_volume=WATER_MOLAR_VOLUME), \n", + " AerosolNascent(water_molar_volume=WATER_MOLAR_VOLUME)\n", + " ):\n", + " for model in ('Constant', 'CompressedFilmOvadnevaite'):\n", + " key = f\"{aerosol.__class__.__name__}-{model}\"\n", + " settings = Settings(\n", + " dz=1*si.m, n_sd_per_mode=100, \n", + " model=model,\n", + " aerosol=aerosol,\n", + " )\n", + " simulation = Simulation(settings)\n", + " output[key] = simulation.run()\n", + " output[key]['color'] = aerosol.color\n", + " output[key]['Na_tot'] = Sum(\n", + " tuple(settings.aerosol.modes[i]['spectrum']\n", + " for i in range(len(settings.aerosol.modes)))).norm_factor" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-15T07:32:52.168894Z", + "start_time": "2025-06-15T07:32:51.735042Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-06-15T09:32:52.136502\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.9.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "b2652dcdf01049caad07ac627062377a", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_2ab.pdf
\"), HTML(value=\"\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-06-15T09:32:52.350925\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.9.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "877680c3932042c1beaf8dbcd3a0f0a4", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_2c.pdf
\"), HTML(value=\"" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "from PySDM_examples.Lowe_et_al_2019.plot_helper import plot_contours\n", + "plot_contours(subplot_list, updraft_list, forg_list, output, actfrac=False, save=False)\n", + "plot_contours(subplot_list, updraft_list, forg_list, output, actfrac=True, save=False)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.10.9 ('pysdm')", + "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.9" + }, + "vscode": { + "interpreter": { + "hash": "b14f34a08619f4a218d80d7380beed3f0c712c89ff93e7183219752d640ed427" + } + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/PySDM/source/examples/PySDM_examples/Lowe_et_al_2019/fig_s2.ipynb b/PySDM/source/examples/PySDM_examples/Lowe_et_al_2019/fig_s2.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..3330489f7abdf3218711d25be0116f39eb903b74 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Lowe_et_al_2019/fig_s2.ipynb @@ -0,0 +1,3687 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Lowe_et_al_2019/fig_s2.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Lowe_et_al_2019/fig_s2.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Lowe_et_al_2019/fig_s2.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### based on Fig. S2 from Lowe et al. 2019 (Nature Comm.) \"_Key drivers of cloud response to surface-active organics_\" \n", + "https://doi.org/10.1038/s41467-019-12982-0" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-13T12:31:36.116404Z", + "start_time": "2024-12-13T12:31:36.111703Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-13T12:31:39.101800Z", + "start_time": "2024-12-13T12:31:36.119217Z" + } + }, + "outputs": [], + "source": [ + "from contextlib import contextmanager\n", + "\n", + "import numba\n", + "\n", + "from PySDM_examples.Lowe_et_al_2019 import Settings, Simulation\n", + "from PySDM_examples.Lowe_et_al_2019.aerosol_code import AerosolBoreal, AerosolMarine, AerosolNascent\n", + "from PySDM_examples.Lowe_et_al_2019.constants_def import LOWE_CONSTS\n", + "from open_atmos_jupyter_utils import show_plot\n", + "\n", + "from PySDM import Formulae\n", + "from PySDM.initialisation.spectra import Sum\n", + "from PySDM.physics import si, in_unit\n", + "\n", + "import numpy as np\n", + "from joblib import Parallel, delayed, parallel_backend\n", + "\n", + "from matplotlib import pyplot" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-13T12:31:39.227328Z", + "start_time": "2024-12-13T12:31:39.223339Z" + } + }, + "outputs": [], + "source": [ + "@contextmanager\n", + "def numba_threading_disabled():\n", + " numba_original_num_threads = numba.get_num_threads()\n", + " numba.set_num_threads(1)\n", + " try:\n", + " yield\n", + " finally:\n", + " numba.set_num_threads(numba_original_num_threads)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-13T12:31:39.282772Z", + "start_time": "2024-12-13T12:31:39.241180Z" + } + }, + "outputs": [], + "source": [ + "CI = 'CI' in os.environ\n", + "nRes = 10\n", + "updraft_list = np.linspace(0.2, 2.4, 2 if CI else nRes)\n", + "models = ('Constant', 'CompressedFilmOvadnevaite')\n", + "\n", + "FORMULAE = Formulae(\n", + " constants=LOWE_CONSTS,\n", + ")\n", + "WATER_MOLAR_VOLUME = FORMULAE.constants.Mv / FORMULAE.constants.rho_w\n", + "aerosols = (\n", + " AerosolMarine(water_molar_volume=WATER_MOLAR_VOLUME), \n", + " AerosolBoreal(water_molar_volume=WATER_MOLAR_VOLUME), \n", + " AerosolNascent(water_molar_volume=WATER_MOLAR_VOLUME)\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-13T12:36:11.987889Z", + "start_time": "2024-12-13T12:31:39.295893Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "tasks scheduled: 60\n", + "updrafts: [0.2 0.44444444 0.68888889 0.93333333 1.17777778 1.42222222\n", + " 1.66666667 1.91111111 2.15555556 2.4 ]\n" + ] + } + ], + "source": [ + "def compute(keyname, settings):\n", + " simulation = Simulation(settings)\n", + " out = simulation.run()\n", + " out['updraft'] = settings.w\n", + " out['org_fraction'] = settings.aerosol.modes[0]['f_org']\n", + " out['color'] = settings.aerosol.color\n", + " out['Na_tot'] = Sum(\n", + " tuple(settings.aerosol.modes[i]['spectrum']\n", + " for i in range(len(settings.aerosol.modes)))).norm_factor\n", + " return keyname, out\n", + "\n", + "print(f'tasks scheduled: {len(models) * len(aerosols) * len(updraft_list)}')\n", + "print('updrafts:', updraft_list)\n", + "\n", + "with numba_threading_disabled():\n", + " with parallel_backend(backend='threading', n_jobs=-2):\n", + " output = dict(Parallel(verbose=0)(\n", + " delayed(compute)(f\"w{w:.2f}_{aerosol.__class__.__name__}_{model}\", Settings(\n", + " dz = 10 * si.m if CI else 1 * si.m,\n", + " n_sd_per_mode = 10 if CI else 100,\n", + " model = model,\n", + " aerosol = aerosol,\n", + " w = w * si.m / si.s,\n", + " ))\n", + " for w in updraft_list\n", + " for model in models\n", + " for aerosol in aerosols\n", + " ))" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-13T12:36:13.253574Z", + "start_time": "2024-12-13T12:36:12.074515Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-12-13T13:36:13.180506\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.9.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f6e827fe0c2c4b1cb2affda4e6ce268b", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./tmpf6g1c1hr.pdf
\"), HTML(value…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, axes = pyplot.subplots(1, 3, figsize=(10,3))\n", + "\n", + "for key, out_item in output.items():\n", + " ll = \"_\".join(key.split(\"_\", maxsplit=1)[1:]).replace(\"Aerosol\",\"\").replace(\"CompressedFilm\",\"\")\n", + " cc, disp = {\n", + " \"Marine\": (\"b\", -0.1),\n", + " \"Boreal\": (\"g\", -0.05),\n", + " \"Nascent\": (\"r\", 0.0),\n", + " }[ll.split(\"_\", maxsplit=1)[0]]\n", + " w, a, h = {\n", + " \"Constant\": (0.05, 0.4, \"\"),\n", + " \"Ovadnevaite\": (0.025, 0.6, \"//\"),\n", + " }[ll.split(\"_\", maxsplit=1)[1]]\n", + "\n", + " ax = axes[0]\n", + " label = ll if key.split(\"_\", maxsplit=1)[0] == \"w0.20\" else ''\n", + " common_kwargs = {\n", + " 'color': cc,\n", + " 'width': w,\n", + " 'alpha': a,\n", + " 'hatch': h\n", + " }\n", + "\n", + " ax.bar(\n", + " out_item[\"updraft\"] + disp,\n", + " in_unit(out_item[\"lwp\"], si.g / si.m**2),\n", + " **common_kwargs, label=label\n", + " )\n", + " if label != '':\n", + " ax.legend(loc = 0) #bbox_to_anchor=(5, 1))\n", + "\n", + " ax.set_xlabel(\"w [m s$^{-1}$]\")\n", + " ax.set_ylabel(\"LWP [g m$^{-2}$]\")\n", + " ax.set_ylim(27,35) # 27,33\n", + "\n", + " ax = axes[1]\n", + " ax.bar(out_item[\"updraft\"] + disp, out_item[\"tau\"], **common_kwargs)\n", + " ax.set_xlabel(\"w [m s$^{-1}$]\")\n", + " ax.set_ylabel(\"$\\\\tau$, optical depth\")\n", + " ax.set_ylim(0,13) # 0,11\n", + "\n", + " ax = axes[2]\n", + " ax.bar(out_item[\"updraft\"] + disp, out_item[\"albedo\"], **common_kwargs)\n", + " ax.set_xlabel(\"w [m s$^{-1}$]\")\n", + " ax.set_ylabel(\"$\\\\alpha$, cloud albedo\")\n", + " ax.set_ylim(0,0.5) # 0,0.45\n", + "\n", + "pyplot.tight_layout()\n", + "show_plot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-13T12:36:13.274167Z", + "start_time": "2024-12-13T12:36:13.271871Z" + } + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9" + }, + "vscode": { + "interpreter": { + "hash": "b14f34a08619f4a218d80d7380beed3f0c712c89ff93e7183219752d640ed427" + } + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/PySDM/source/examples/PySDM_examples/Lowe_et_al_2019/plot_helper.py b/PySDM/source/examples/PySDM_examples/Lowe_et_al_2019/plot_helper.py new file mode 100644 index 0000000000000000000000000000000000000000..bc877270a8ac52e2b473ec5362b86d9a903963e1 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Lowe_et_al_2019/plot_helper.py @@ -0,0 +1,182 @@ +import numpy as np +from matplotlib import pyplot +from open_atmos_jupyter_utils import show_plot + + +def plot_profiles(subplot_list, updraft_list, forg_list, output, save=False): + _, axes = pyplot.subplots( + len(subplot_list), + len(updraft_list), + sharex=False, + sharey=True, + figsize=(3 * len(updraft_list), 4 * len(subplot_list)), + ) + + for k, subplot in enumerate(subplot_list): + for i, w in enumerate(updraft_list): + for _, Forg in enumerate(forg_list): + key = subplot + f"_w{w:.2f}_f{Forg:.2f}_" + var = "CDNC_cm3" + z = np.array(output[key + "CompressedFilmOvadnevaite"]["z"]) + CDNC_film = np.array(output[key + "CompressedFilmOvadnevaite"][var]) + CDNC_bulk = np.array(output[key + "Constant"][var]) + + cmap = pyplot.get_cmap("Spectral") + if len(subplot_list) > 1: + ax = axes[k, i] + else: + ax = axes[i] + + ax.plot(CDNC_film, z, "--", color=cmap(Forg)) + ax.plot(CDNC_bulk, z, "-", color=cmap(Forg), label=f"{Forg:.2f}") + + if i == 0: + ax.set_ylabel("Parcel displacement [m]") + ax.set_title(subplot, loc="left", weight="bold") + if i == len(updraft_list) - 1 and k == 0: + ax.legend(title="Forg", loc=2) + if k == 0: + ax.set_title(f"w = {w:.2f} m/s") + if k == len(subplot_list) - 1: + ax.set_xlabel("CDNC [cm$^{-3}$]") + if save: + show_plot("fig3_parcel_profiles.pdf") + + +def plot_contours( + subplot_list, updraft_list, forg_list, output, actfrac=False, save=False +): + _, axes = pyplot.subplots(2, 2, sharex=True, sharey=True, figsize=(14, 10)) + + for subplot in subplot_list: + dCDNC = np.zeros((len(updraft_list), len(forg_list))) + for i, w in enumerate(updraft_list): + for j, Forg in enumerate(forg_list): + key = subplot + f"_w{w:.2f}_f{Forg:.2f}_" + if actfrac: + var = "Activated Fraction" + Naer = 1.0 + CDNC_film = output[key + "CompressedFilmOvadnevaite"][var][0] * Naer + CDNC_bulk = output[key + "Constant"][var][0] * Naer + else: + var = "CDNC_cm3" + z = np.array(output[key + "CompressedFilmOvadnevaite"]["z"]) + wz = np.where(z == z[-1])[0][0] + CDNC_film = np.array( + output[key + "CompressedFilmOvadnevaite"][var] + )[wz] + CDNC_bulk = np.array(output[key + "Constant"][var])[wz] + dCDNC[i, j] = (CDNC_film - CDNC_bulk) / CDNC_bulk * 100.0 + + if subplot == "a": + ax = axes[0, 0] + ax.set_title( + "MA Accum. mode conc. N$_2 = 30$ cm$^{-3}$", fontsize=13, loc="right" + ) + ax.contour( + forg_list, + updraft_list, + dCDNC, + levels=[10, 25], + colors=["#1fa8f2", "#4287f5"], + linestyles=[":", "--"], + linewidths=4, + ) + p = ax.contourf( + forg_list, + updraft_list, + dCDNC, + cmap="Blues", + levels=np.linspace(0, 90, 11), + extend="both", + ) + if subplot == "b": + ax = axes[0, 1] + ax.set_title( + "MA Accum. mode conc. N$_2 = 135$ cm$^{-3}$", fontsize=13, loc="right" + ) + ax.contour( + forg_list, + updraft_list, + dCDNC, + levels=[10, 25], + colors=["#1fa8f2", "#4287f5"], + linestyles=[":", "--"], + linewidths=4, + ) + p = ax.contourf( + forg_list, + updraft_list, + dCDNC, + cmap="Blues", + levels=np.linspace(0, 90, 11), + extend="both", + ) + if subplot == "c": + ax = axes[1, 0] + ax.set_title( + "HYY Accum. mode conc. N$_2 = 160$ cm$^{-3}$", fontsize=13, loc="right" + ) + ax.contour( + forg_list, + updraft_list, + dCDNC, + levels=[10, 25], + colors=["#04c753", "#157d3f"], + linestyles=[":", "--"], + linewidths=4, + ) + p = ax.contourf( + forg_list, + updraft_list, + dCDNC, + cmap="Greens", + levels=np.linspace(0, 75, 11), + extend="both", + ) + if subplot == "d": + ax = axes[1, 1] + ax.set_title( + "HYY Accum. mode conc. N$_2 = 540$ cm$^{-3}$", fontsize=13, loc="right" + ) + ax.contour( + forg_list, + updraft_list, + dCDNC, + levels=[10, 25], + colors=["#04c753", "#157d3f"], + linestyles=[":", "--"], + linewidths=4, + ) + p = ax.contourf( + forg_list, + updraft_list, + dCDNC, + cmap="Greens", + levels=np.linspace(0, 75, 11), + extend="both", + ) + + ax.set_title(subplot, weight="bold", loc="left") + if subplot in ("c", "d"): + ax.set_xlabel("Organic mass fraction") + ax.set_yscale("log") + ax.set_yticks([0.1, 1, 10]) + ax.set_yticklabels(["0.1", "1", "10"]) + ax.set_xlim([0, 1]) + if subplot in ("a", "c"): + ax.set_ylabel("Updraft [ms$^{-1}$]") + pyplot.colorbar(p, ax=ax, label=r"$\Delta_{CDNC}$ [%]") + + pyplot.rcParams.update({"font.size": 15}) + if save: + if actfrac: + pyplot.savefig( + "fig_3_Scrit.png", dpi=200, facecolor="w", bbox_inches="tight" + ) + show_plot("fig_3_Scrit.pdf") + else: + pyplot.savefig( + "fig_3_rcrit.png", dpi=200, facecolor="w", bbox_inches="tight" + ) + show_plot("fig_3_rcrit.pdf") diff --git a/PySDM/source/examples/PySDM_examples/Lowe_et_al_2019/settings.py b/PySDM/source/examples/PySDM_examples/Lowe_et_al_2019/settings.py new file mode 100644 index 0000000000000000000000000000000000000000..847788e15eeba01b5915fb6799f833af3c0709e3 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Lowe_et_al_2019/settings.py @@ -0,0 +1,90 @@ +from functools import lru_cache + +import numpy as np +from PySDM_examples.Lowe_et_al_2019.constants_def import LOWE_CONSTS +from pystrict import strict + +from PySDM import Formulae +from PySDM.backends import CPU +from PySDM.initialisation.aerosol_composition import DryAerosolMixture +from PySDM.physics import si + + +@lru_cache +def backend(model): + return CPU( + formulae=Formulae( + surface_tension=model, + constants=LOWE_CONSTS, + diffusion_kinetics="LoweEtAl2019", + diffusion_thermics="LoweEtAl2019", + latent_heat_vapourisation="Lowe2019", + saturation_vapour_pressure="Lowe1977", + optical_albedo="Bohren1987", + optical_depth="Stephens1978", + ), + override_jit_flags={"parallel": False}, + ) + + +@strict +class Settings: + def __init__( + self, + dz: float, + n_sd_per_mode: int, + aerosol: DryAerosolMixture, + model: str, + w: float = 0.32 * si.m / si.s, + ): + assert model in ("Constant", "CompressedFilmOvadnevaite") + self.model = model + self.n_sd_per_mode = n_sd_per_mode + self.backend = backend(model) + self.formulae = self.backend.formulae + const = self.formulae.constants + self.aerosol = aerosol + + max_altitude = 200 * si.m + self.w = w + self.t_max = max_altitude / self.w + self.dt = dz / self.w + self.output_interval = 1 * self.dt + + self.p0 = 980 * si.mbar + self.T0 = 280 * si.K + pv0 = 0.999 * self.formulae.saturation_vapour_pressure.pvs_water(self.T0) + self.initial_water_vapour_mixing_ratio = const.eps * pv0 / (self.p0 - pv0) + + self.cloud_radius_range = (0.5 * si.micrometre, np.inf) + + self.mass_of_dry_air = 44 + + self.wet_radius_bins_edges = np.logspace( + np.log10(4 * si.um), np.log10(12 * si.um), 128 + 1, endpoint=True + ) + + @property + def rho0(self): + const = self.formulae.constants + rhod0 = ( + self.formulae.trivia.p_d(self.p0, self.initial_water_vapour_mixing_ratio) + / self.T0 + / const.Rd + ) + return rhod0 * (1 + self.initial_water_vapour_mixing_ratio) + + @property + def nt(self) -> int: + nt = self.t_max / self.dt + nt_int = round(nt) + np.testing.assert_almost_equal(nt, nt_int) + return nt_int + + @property + def steps_per_output_interval(self) -> int: + return int(self.output_interval / self.dt) + + @property + def output_steps(self) -> np.ndarray: + return np.arange(0, self.nt + 1, self.steps_per_output_interval) diff --git a/PySDM/source/examples/PySDM_examples/Lowe_et_al_2019/simulation.py b/PySDM/source/examples/PySDM_examples/Lowe_et_al_2019/simulation.py new file mode 100644 index 0000000000000000000000000000000000000000..0fd85de9525a3e1e8a992904a36eb31a0da4b88c --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Lowe_et_al_2019/simulation.py @@ -0,0 +1,141 @@ +import numpy as np +from PySDM_examples.utils import BasicSimulation + +import PySDM.products as PySDM_products +from PySDM import Builder +from PySDM.dynamics import AmbientThermodynamics, Condensation +from PySDM.environments import Parcel +from PySDM.initialisation.hygroscopic_equilibrium import equilibrate_wet_radii +from PySDM.initialisation.spectra import Sum +from PySDM.initialisation.sampling.spectral_sampling import ConstantMultiplicity + + +class Simulation(BasicSimulation): + def __init__(self, settings, products=None): + n_sd = settings.n_sd_per_mode * len(settings.aerosol.modes) + builder = Builder( + n_sd=n_sd, + backend=settings.backend, + environment=Parcel( + dt=settings.dt, + mass_of_dry_air=settings.mass_of_dry_air, + p0=settings.p0, + initial_water_vapour_mixing_ratio=settings.initial_water_vapour_mixing_ratio, + T0=settings.T0, + w=settings.w, + ), + ) + + attributes = { + "dry volume": np.empty(0), + "dry volume organic": np.empty(0), + "kappa times dry volume": np.empty(0), + "multiplicity": np.ndarray(0), + } + initial_volume = settings.mass_of_dry_air / settings.rho0 + for mode in settings.aerosol.modes: + r_dry, n_in_dv = ConstantMultiplicity( + spectrum=mode["spectrum"] + ).sample_deterministic(settings.n_sd_per_mode) + v_dry = settings.formulae.trivia.volume(radius=r_dry) + attributes["multiplicity"] = np.append( + attributes["multiplicity"], n_in_dv * initial_volume + ) + attributes["dry volume"] = np.append(attributes["dry volume"], v_dry) + attributes["dry volume organic"] = np.append( + attributes["dry volume organic"], mode["f_org"] * v_dry + ) + attributes["kappa times dry volume"] = np.append( + attributes["kappa times dry volume"], + v_dry * mode["kappa"][settings.model], + ) + for attribute in attributes.values(): + assert attribute.shape[0] == n_sd + + np.testing.assert_approx_equal( + np.sum(attributes["multiplicity"]) / initial_volume, + Sum( + tuple( + settings.aerosol.modes[i]["spectrum"] + for i in range(len(settings.aerosol.modes)) + ) + ).norm_factor, + significant=5, + ) + r_wet = equilibrate_wet_radii( + r_dry=settings.formulae.trivia.radius(volume=attributes["dry volume"]), + environment=builder.particulator.environment, + kappa_times_dry_volume=attributes["kappa times dry volume"], + f_org=attributes["dry volume organic"] / attributes["dry volume"], + ) + attributes["volume"] = settings.formulae.trivia.volume(radius=r_wet) + + if settings.model == "Constant": + del attributes["dry volume organic"] + + builder.add_dynamic(AmbientThermodynamics()) + builder.add_dynamic(Condensation()) + + products = products or ( + PySDM_products.ParcelDisplacement(name="z"), + PySDM_products.Time(name="t"), + PySDM_products.PeakSaturation(name="S_max"), + PySDM_products.AmbientRelativeHumidity(name="RH"), + PySDM_products.ActivatedParticleConcentration( + name="CDNC_cm3", + unit="cm^-3", + count_activated=True, + count_unactivated=False, + ), + PySDM_products.ParticleSizeSpectrumPerVolume( + radius_bins_edges=settings.wet_radius_bins_edges + ), + PySDM_products.ActivableFraction(name="Activated Fraction"), + PySDM_products.WaterMixingRatio(), + PySDM_products.AmbientDryAirDensity(name="rhod"), + PySDM_products.ActivatedEffectiveRadius( + name="reff", count_activated=True, count_unactivated=False + ), + PySDM_products.ParcelLiquidWaterPath( + name="lwp", count_activated=True, count_unactivated=False + ), + PySDM_products.CloudOpticalDepth(name="tau"), + PySDM_products.CloudAlbedo(name="albedo"), + ) + + particulator = builder.build(attributes=attributes, products=products) + self.settings = settings + super().__init__(particulator=particulator) + + def _save_scalars(self, output): + for k, v in self.particulator.products.items(): + if len(v.shape) > 1 or k in ("lwp", "Activated Fraction", "tau", "albedo"): + continue + value = v.get() + if isinstance(value, np.ndarray) and value.size == 1: + value = value[0] + output[k].append(value) + + def _save_final_timestep_products(self, output): + output["spectrum"] = self.particulator.products[ + "particle size spectrum per volume" + ].get() + + for name, args_call in { + "Activated Fraction": lambda: {"S_max": np.nanmax(output["S_max"])}, + "lwp": lambda: {}, + "tau": lambda: { + "effective_radius": output["reff"][-1], + "liquid_water_path": output["lwp"][0], + }, + "albedo": lambda: {"optical_depth": output["tau"]}, + }.items(): + output[name] = self.particulator.products[name].get(**args_call()) + + def run(self): + output = {k: [] for k in self.particulator.products} + for step in self.settings.output_steps: + self.particulator.run(step - self.particulator.n_steps) + self._save_scalars(output) + self._save_final_timestep_products(output) + return output diff --git a/PySDM/source/examples/PySDM_examples/Matsushima_et_al_2023/__init__.py b/PySDM/source/examples/PySDM_examples/Matsushima_et_al_2023/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..dd4ffe2934064c0598833becbf558d9359e15a6b --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Matsushima_et_al_2023/__init__.py @@ -0,0 +1,7 @@ +# pylint: disable=invalid-name +""" +Fig. 1 from [Matsushima et al. 2023 (GMD)](https://doi.org/10.5194/gmd-16-6211-2023) + +figure_1.ipynb: +.. include:: ./figure_1.ipynb.badges.md +""" diff --git a/PySDM/source/examples/PySDM_examples/Matsushima_et_al_2023/figure_1.ipynb b/PySDM/source/examples/PySDM_examples/Matsushima_et_al_2023/figure_1.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..63d62f8a165b217587038d9281184acc025152e2 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Matsushima_et_al_2023/figure_1.ipynb @@ -0,0 +1,16028 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "1790321c-2424-4ebd-9e26-14637e1ec29f", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Matsushima_et_al_2023/figure_1.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Matsushima_et_al_2023/figure_1.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Matsushima_et_al_2023/figure_1.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "6bc29eca-b273-4418-bedc-4787635bc19d", + "metadata": {}, + "source": [ + "Fig. 1 from [Matsushima 2023 (GMD)](https://doi.org/10.5194/gmd-16-6211-2023) depicting alpha-sampling in PySDM" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "3642a633-e611-4001-8bb0-cbe95e12f232", + "metadata": {}, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "c87ff404-3a80-4d0c-a955-3bc7d0a4d31e", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "from PySDM.physics import si, in_unit\n", + "from PySDM.initialisation.sampling.spectral_sampling import AlphaSampling\n", + "from PySDM.initialisation import spectra\n", + "\n", + "from matplotlib import pyplot, colors\n", + "from open_atmos_jupyter_utils import show_plot" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "10da5008-8e20-43a1-8d11-0ade29c9a492", + "metadata": {}, + "outputs": [], + "source": [ + "n_sd = 2**16\n", + "spectrum = spectra.Sum((\n", + " spectra.Lognormal(\n", + " norm_factor=90 / si.cm**3,\n", + " m_mode=0.03 * si.um,\n", + " s_geom=1.28,\n", + " ),\n", + " spectra.Lognormal(\n", + " norm_factor=15 / si.cm**3,\n", + " m_mode=0.14 * si.um,\n", + " s_geom=1.75,\n", + " ),\n", + "))\n", + "\n", + "samplings = {\n", + " alpha: AlphaSampling(\n", + " spectrum,\n", + " alpha=alpha,\n", + " interp_points=100000,\n", + " size_range=(10 * si.nm, 5000 * si.nm),\n", + " dist_1_inv=lambda y, size_range: np.exp((np.log(size_range[1]) - np.log(size_range[0])) * y + np.log(size_range[0]))\n", + " ) for alpha in np.linspace(0, 1, 11) \n", + "}\n", + "\n", + "xas, yas = {}, {}\n", + "for alpha, sampling in samplings.items():\n", + " xas[alpha], yas[alpha] = sampling.sample_deterministic(n_sd)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "858d3450-6107-4d33-b88b-daa5402e4ac7", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-11-30T17:32:56.731346\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.9.4, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "daeba983b9ce4c04b04221c099f18313", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_1.pdf
\"), HTML(value=\"\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-01-20T20:41:47.404805\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "3771059a2ed24463929c89934681bf2d", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./fig_2.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "def trans_x(TC):\n", + " x = 1e6 / (TC + const.T0)**2\n", + " return x\n", + "\n", + "def trans_x_inv(x):\n", + " TK = 1 / np.sqrt(x / 1e6)\n", + " return TK - const.T0\n", + "\n", + "def trans_y(alpha):\n", + " y = 1e3 * np.log10(np.where(alpha > 0, alpha, np.nan))\n", + " return y\n", + " \n", + "def trans_y_inv(y):\n", + " alpha = 10**(y / 1e3)\n", + " return alpha\n", + "\n", + "line_1_TC = np.linspace(10*si.K, -20*si.K, N_POINTS)\n", + "pyplot.plot(trans_x(line_1_TC), trans_y(alphas.alpha_l_2H(line_1_TC + const.T0)))\n", + "\n", + "line_2_TC = np.linspace(0*si.K, -40*si.K, N_POINTS)\n", + "pyplot.plot(trans_x(line_2_TC), trans_y(alphas.alpha_i_2H(line_2_TC + const.T0)))\n", + "\n", + "pyplot.grid()\n", + "pyplot.xlabel(\"$10^6/T^2$\")\n", + "pyplot.ylabel(\"$10^3 logα$\")\n", + "pyplot.xlim(12, 19)\n", + "pyplot.ylim(35, 88)\n", + "\n", + "twinx = pyplot.gca().secondary_yaxis('right', functions=(trans_y_inv, trans_y))\n", + "twinx.set_ylabel(\"α\")\n", + "\n", + "twiny = pyplot.gca().secondary_xaxis('top', functions=(trans_x_inv, trans_x))\n", + "twiny.set_xlabel(\"T [C]\")\n", + "\n", + "show_plot(\"fig_2.pdf\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/PySDM/source/examples/PySDM_examples/Miyake_et_al_1968/__init__.py b/PySDM/source/examples/PySDM_examples/Miyake_et_al_1968/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..961f5b25ee3bf99c645f4b816e2b0efb5ff5faa6 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Miyake_et_al_1968/__init__.py @@ -0,0 +1,8 @@ +# pylint: disable=invalid-name +""" +isotopic adjustment time plot following +[Friedman et al. 1962 (JGR)](https://doi.org/10.1029/JZ067i007p02761) + +fig_19.ipynb: +.. include:: ./fig_19.ipynb.badges.md +""" diff --git a/PySDM/source/examples/PySDM_examples/Miyake_et_al_1968/fig_19.ipynb b/PySDM/source/examples/PySDM_examples/Miyake_et_al_1968/fig_19.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..2ee73bba0f391922c77cbf7acab29ce2fabb9447 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Miyake_et_al_1968/fig_19.ipynb @@ -0,0 +1,2958 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "06b74a1b-fc97-497b-ad08-7e253c6a7378", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Miyake_et_al_1968/fig_19.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Miyake_et_al_1968/fig_19.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Miyake_et_al_1968/fig_19.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "634d2789-0aac-40d3-bf6c-0eb2f32d42cd", + "metadata": {}, + "source": [ + "#### based on Fig. 19 from Miyake et al. 1968 \"_An Isotopic Study on Meteoric Precipitation_\"\n", + "https://doi.org/10.2467/mripapers1950.19.2_243\n", + "\n", + "Notes:\n", + "- extended to cover Oxygen-17\n", + "- different ventilation coefficients depicted (neglect, Froessling, Prupparcher & Rasmussen)\n", + "- different diffusivity ratios used for different isotopes (with D_heavy/D approximated with D_heavy/D_light), visible but insignificant differences" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "xyz", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-27T19:05:16.231225Z", + "start_time": "2025-06-27T19:05:16.228134Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "2eb77505-705a-4c94-aec3-d371bde8d375", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-27T19:05:17.905536Z", + "start_time": "2025-06-27T19:05:16.315608Z" + } + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "from PySDM import Formulae\n", + "from PySDM.physics import si, in_unit\n", + "from PySDM.physics.constants_defaults import T0\n", + "from matplotlib import pyplot\n", + "from open_atmos_jupyter_utils import show_plot" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "5411525a-0e07-4ee5-89bc-ff2363f65359", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-27T19:05:17.920695Z", + "start_time": "2025-06-27T19:05:17.918597Z" + } + }, + "outputs": [], + "source": [ + "VENTILATION_VARIANTS = ('Neglect', 'Froessling1938', 'PruppacherAndRasmussen1979')\n", + "AIR_DENSITY = 1 * si.kg / si.m**3\n", + "ANY_NUMBER = 44." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "e7a49798-b6b8-4ab4-8382-f1bc25f13f0c", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-27T19:05:23.130278Z", + "start_time": "2025-06-27T19:05:17.928495Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-06-27T21:05:23.096071\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.0, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "91762d0e9bf74cea9e177739e3c0067f", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_19.pdf
\"), HTML(value=\" int: + return int(self.simulation_time / self.dt) # TODO #413 + + @property + def steps_per_output_interval(self) -> int: + return int(self.output_interval / self.dt) + + @property + def n_spin_up(self) -> int: + return int(self.spin_up_time / self.dt) + + @property + def output_steps(self) -> np.ndarray: + return np.arange(0, self.n_steps + 1, self.steps_per_output_interval) + + @property + def n_sd(self): + return self.grid[0] * self.grid[1] * self.n_sd_per_gridbox + + @property + def initial_vapour_mixing_ratio_profile(self): + return np.full(self.grid[-1], self.initial_water_vapour_mixing_ratio) + + @property + def initial_dry_potential_temperature_profile(self): + return np.full( + self.grid[-1], + self.formulae.state_variable_triplet.th_dry( + self.th_std0, self.initial_water_vapour_mixing_ratio + ), + ) diff --git a/PySDM/source/examples/PySDM_examples/Morrison_and_Grabowski_2007/fig_1.ipynb b/PySDM/source/examples/PySDM_examples/Morrison_and_Grabowski_2007/fig_1.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..543c2687f3c800572120d78eab21fb0edb55a003 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Morrison_and_Grabowski_2007/fig_1.ipynb @@ -0,0 +1,3563 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Morrison_and_Grabowski_2007/fig_1.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Morrison_and_Grabowski_2007/fig_1.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Morrison_and_Grabowski_2007/fig_1.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### based on Fig. 1 from Morrison & Grabowski 2007 (J. Atmos. Sci. 64) \"_Comparison of Bulk and Bin Warm-Rain Microphysics Models Using a Kinematic Framework_\"\n", + "https://doi.org/10.1175/JAS3980" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:38:54.741681Z", + "start_time": "2024-02-01T07:38:54.737942Z" + }, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:39:30.127698Z", + "start_time": "2024-02-01T07:39:30.121597Z" + }, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "from PySDM_examples.Morrison_and_Grabowski_2007.strato_cumulus import StratoCumulus\n", + "from PySDM_examples.utils.kinematic_2d.fields import nondivergent_vector_field_2d, z_vec_coord, x_vec_coord\n", + "from PySDM.physics import si\n", + "from PySDM import Formulae\n", + "from matplotlib import pyplot\n", + "from open_atmos_jupyter_utils import show_plot" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:38:57.757865Z", + "start_time": "2024-02-01T07:38:56.824675Z" + } + }, + "outputs": [], + "source": [ + "settings = StratoCumulus(Formulae(), rhod_w_max=1 * si.m/si.s * si.kg/si.m**3)\n", + "settings.grid = (64, 64)\n", + "settings.size = (2*si.km, 1*si.km)\n", + "settings.dt = 1 * si.s\n", + "\n", + "rho_times_courant = nondivergent_vector_field_2d(\n", + " grid=settings.grid,\n", + " size=settings.size,\n", + " dt=settings.dt,\n", + " stream_function=settings.stream_function,\n", + " t=np.nan\n", + ")\n", + "dz = settings.size[-1] / settings.grid[-1]\n", + "dx = settings.size[0] / settings.grid[0]\n", + "\n", + "levels = np.linspace(-1.5, 1.5, 7) * si.m/si.s" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:38:58.020987Z", + "start_time": "2024-02-01T07:38:57.760239Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-05-25T13:29:50.463153\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ca56ec86c3e84df4871afae6679cf9f8", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./tmp95yc0a4e.pdf
\"), HTML(value…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "z_vec_coords = tuple(coord * settings.size[i]/si.km for i, coord in enumerate(z_vec_coord(settings.grid)))\n", + "pyplot.clabel(pyplot.contour(\n", + " *z_vec_coords,\n", + " rho_times_courant[-1] * dz / settings.dt / settings.rhod_of_zZ(z_vec_coords[-1]/settings.size[-1]),\n", + " levels=levels\n", + "))\n", + "pyplot.grid()\n", + "pyplot.title('vertical velocity [m/s]')\n", + "pyplot.xlabel('X [km]')\n", + "pyplot.ylabel('Z [km]')\n", + "show_plot()" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:38:58.241556Z", + "start_time": "2024-02-01T07:38:58.020663Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-05-25T13:29:56.503557\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "d4d72150fd01461191986743cc2d665d", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./tmpswbu1_2g.pdf
\"), HTML(value…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "x_vec_coords = tuple(coord * settings.size[i]/si.km for i, coord in enumerate(x_vec_coord(settings.grid)))\n", + "pyplot.clabel(pyplot.contour(\n", + " *x_vec_coords,\n", + " rho_times_courant[0] * dx / settings.dt / settings.rhod_of_zZ(x_vec_coords[0]/settings.size[0]),\n", + " levels=levels\n", + "))\n", + "pyplot.title('horizontal velocity [m/s]')\n", + "pyplot.grid()\n", + "pyplot.xlabel('X [km]')\n", + "pyplot.ylabel('Z [km]')\n", + "show_plot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/PySDM/source/examples/PySDM_examples/Morrison_and_Grabowski_2007/strato_cumulus.py b/PySDM/source/examples/PySDM_examples/Morrison_and_Grabowski_2007/strato_cumulus.py new file mode 100644 index 0000000000000000000000000000000000000000..bef5a5c89ba054f9177ec7c3812c3cd28146a969 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Morrison_and_Grabowski_2007/strato_cumulus.py @@ -0,0 +1,34 @@ +import numpy as np +from PySDM_examples.Morrison_and_Grabowski_2007.common import Common + +from PySDM.physics import si + + +class StratoCumulus(Common): + def __init__(self, formulae, rhod_w_max: float): + super().__init__(formulae) + self.th_std0 = 289 * si.kelvins + self.initial_water_vapour_mixing_ratio = 7.5 * si.grams / si.kilogram + self.p0 = 1015 * si.hectopascals + self.rhod_w_max = rhod_w_max + + def stream_function(self, xX, zZ, _): + X = self.size[0] + return ( + -self.rhod_w_max * X / np.pi * np.sin(np.pi * zZ) * np.cos(2 * np.pi * xX) + ) + + def rhod_of_zZ(self, zZ): + p = getattr( + self.formulae.hydrostatics, + "p_of_z_assuming_const_th_and_initial_water_vapour_mixing_ratio", + )( + self.p0, + self.th_std0, + self.initial_water_vapour_mixing_ratio, + z=zZ * self.size[-1], + ) + rhod = self.formulae.state_variable_triplet.rho_d( + p, self.initial_water_vapour_mixing_ratio, self.th_std0 + ) + return rhod diff --git a/PySDM/source/examples/PySDM_examples/Niedermeier_et_al_2014/__init__.py b/PySDM/source/examples/PySDM_examples/Niedermeier_et_al_2014/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..efcbac81f40f200ab519cd4de23d3596922acc73 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Niedermeier_et_al_2014/__init__.py @@ -0,0 +1,9 @@ +""" +based on [Niedermeier et al. 2014](https://doi.org/10.1002/2013GL058684) + +fig_2.ipynb: +.. include:: ./fig_2.ipynb.badges.md +""" + +from .settings import Settings +from .simulation import Simulation diff --git a/PySDM/source/examples/PySDM_examples/Niedermeier_et_al_2014/fig_2.ipynb b/PySDM/source/examples/PySDM_examples/Niedermeier_et_al_2014/fig_2.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..28b6f3a99237ff32a7ea703960ff20b140e40f3f --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Niedermeier_et_al_2014/fig_2.ipynb @@ -0,0 +1,2396 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "9c727d1b", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Niedermeier_et_al_2014/fig_2.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Niedermeier_et_al_2014/fig_2.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Niedermeier_et_al_2014/fig_2.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "2ac71b75", + "metadata": {}, + "source": [ + "#### based on Fig. 2 from Niedermeier et al. 2014 (Geophys. Res. Lett. 41) \"_A computationally efficient description of heterogeneous freezing: A simplified version of the Soccer ball model_\" \n", + "https://doi.org/10.1002/2013GL058684" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "36425cff812f5c82", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-03T12:14:00.329145Z", + "start_time": "2025-07-03T12:14:00.323216Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "7d32494f", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-03T12:14:21.853024Z", + "start_time": "2025-07-03T12:14:01.215834Z" + } + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "from matplotlib import pyplot\n", + "from open_atmos_jupyter_utils import show_plot\n", + "from PySDM import Formulae\n", + "from PySDM.physics import si\n", + "from PySDM_examples.Niedermeier_et_al_2014 import Settings, Simulation" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "c38109d8", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-03T12:15:57.331889Z", + "start_time": "2025-07-03T12:14:21.887996Z" + } + }, + "outputs": [], + "source": [ + "formulae = Formulae(\n", + " heterogeneous_ice_nucleation_rate='ABIFM',\n", + " particle_shape_and_density=\"MixedPhaseSpheres\",\n", + " constants = {\n", + " 'ABIFM_M': 70,\n", + " 'ABIFM_C': -10\n", + " }\n", + ")\n", + "\n", + "output = []\n", + "for T0 in (formulae.trivia.C2K(TC) for TC in (-19, -23)):\n", + " settings = Settings(\n", + " in_sampling_n=1800,\n", + " initial_temperature=T0 * si.K,\n", + " timestep=1 * si.s,\n", + " formulae=formulae\n", + " )\n", + " simulation = Simulation(settings)\n", + " output.append(simulation.run())" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "a3df1ee0", + "metadata": { + "ExecuteTime": { + "end_time": "2025-07-03T12:17:49.612143Z", + "start_time": "2025-07-03T12:17:49.102262Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-07T14:27:12.419900\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "2b873cf4a16b4cbb9aff062f4ea6f808", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_2.pdf
\"), HTML(value=\"\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-05-28T11:50:33.147108\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "2bba3ca1a8074667a2eb25fd66c24afb", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./fig_3.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "_, axs = pyplot.subplots(2, 2, figsize=(9, 7), tight_layout=True)\n", + "\n", + "axs[0, 0].set_title('A')\n", + "axs[0, 1].set_title('B')\n", + "axs[0, 0].sharex(axs[1, 0])\n", + "axs[0, 1].sharex(axs[1, 1])\n", + "\n", + "alpha_axl = axs[0, 0]\n", + "alpha_axr = alpha_axl.twinx()\n", + " \n", + "plots = []\n", + "for color, eps_key, ax in (\n", + " ('blue', '18O', alpha_axl), \n", + " ('red', '2H', alpha_axr), \n", + " ('purple', '17O', alpha_axl)\n", + "):\n", + " plots += ax.plot(\n", + " F,\n", + " in_unit(enrichments[eps_key], const.PER_MILLE),\n", + " label=eps_key,\n", + " color=color\n", + " )\n", + " if ax.get_ylabel() == \"\":\n", + " ax.set_ylabel(\"E [‰]\")\n", + " ax.set_ylabel(ax.get_ylabel() + \" \" + eps_key)\n", + "pyplot.legend(plots, [l.get_label() for l in plots])\n", + "pyplot.xlim(*F[[0, -1]])\n", + "alpha_axl.grid(None)\n", + "alpha_axl.set_ylim(0, 35)\n", + "alpha_axr.set_ylim(0, 100)\n", + "\n", + "excess_axl = axs[1, 0]\n", + "excess_axr = excess_axl.twinx()\n", + "plots = []\n", + "for color, fun, args, mult, label, ax in (\n", + " ('green', 'excess_d', (deltas['2H'], deltas['18O']), const.PER_MILLE, \"d-excess [‰]\", excess_axl),\n", + " ('orange', 'excess_17O', (deltas['17O'], deltas['18O']), const.PER_MEG, \"$^{17}O$-excess [per meg]\", excess_axr),\n", + "):\n", + " plots += ax.plot(\n", + " F,\n", + " in_unit(getattr(excess, fun)(*args), mult),\n", + " color=color,\n", + " label=label\n", + " )\n", + " ax.set_ylabel(label, color=color)\n", + "pyplot.legend(plots, [l.get_label() for l in plots])\n", + "excess_axl.grid(None)\n", + "excess_axl.set_ylim(-68, 15)\n", + "excess_axr.set_ylim(-100, 42)\n", + "\n", + "axs[1, 0].set_xlabel(\"F [1]\")\n", + "axs[1, 0].set_xticks(np.linspace(*F[[0,-1]], 8))\n", + "\n", + "\n", + "axs[0, 1].set_ylabel(\"$δ^2H$ [‰]\")\n", + "axs[0, 1].grid()\n", + "axs[0, 1].set_ylim(-65, 60)\n", + "axs[0, 1].set_xlim(-10, 18)\n", + "axs[0, 1].set_xticks(np.linspace(-10, 15, 6))\n", + "\n", + "x = np.linspace(-10e-3, 10e-3, 100)\n", + "axs[0, 1].plot(\n", + " in_unit(x, const.PER_MILLE),\n", + " in_unit(x * const.CRAIG_1961_SLOPE_COEFF + const.CRAIG_1961_INTERCEPT_COEFF, const.PER_MILLE),\n", + " color='black',\n", + " linestyle='--',\n", + " label=\"GMWL\"\n", + ")\n", + "axs[0, 1].plot(\n", + " in_unit(deltas[\"18O\"], const.PER_MILLE),\n", + " in_unit(deltas[\"2H\"], const.PER_MILLE),\n", + " color='black'\n", + ")\n", + "axs[0, 1].legend()\n", + "\n", + "excess_axl = axs[1, 1]\n", + "excess_axr = excess_axl.twinx()\n", + "plots = []\n", + "for color, fun, args, mult, label, ax in (\n", + " ('green', 'excess_d', (deltas['2H'], deltas['18O']), const.PER_MILLE, \"d-excess [‰]\", excess_axl),\n", + " ('orange', 'excess_17O', (deltas['17O'], deltas['18O']), const.PER_MEG, \"$^{17}O$-excess [per meg]\", excess_axr),\n", + "):\n", + " plots += ax.plot(\n", + " in_unit(deltas[\"18O\"], const.PER_MILLE),\n", + " in_unit(getattr(excess, fun)(*args), mult),\n", + " color=color,\n", + " label=label\n", + " )\n", + " ax.set_ylabel(label, color=color)\n", + "pyplot.legend(plots, [l.get_label() for l in plots])\n", + "excess_axl.grid(None)\n", + "excess_axl.set_ylim(-100, 20)\n", + "excess_axr.set_ylim(-120, 40)\n", + "\n", + "axs[1, 1].set_xlabel(\"$δ^{18}O$ [‰]\")\n", + "\n", + "show_plot(\"fig_3.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dbe8dfbe-6369-42ae-b8d4-26e24c37e7f5", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/PySDM/source/examples/PySDM_examples/Pierchala_et_al_2022/fig_4.ipynb b/PySDM/source/examples/PySDM_examples/Pierchala_et_al_2022/fig_4.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..4af8480ab1e2bd6d34bbcc4787e5bccd362c2853 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Pierchala_et_al_2022/fig_4.ipynb @@ -0,0 +1,2009 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0d310c75", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Pierchala_et_al_2022/fig_4.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Pierchala_et_al_2022/fig_4.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Pierchala_et_al_2022/fig_4.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "014768e7", + "metadata": {}, + "source": [ + "### based on Fig. 4 from Pierchala et al. 2020 (Geochim. Cosmochim. Acta) \"_Quantification the diffusion-induced fractionation of $^{1}H_{2} ^{17}O$ isotopologue in air accompanying the process of water evaporation_\" (https://doi.org/10.1016/j.gca.2022.01.020)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "31dc796b", + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:42:09.762313Z", + "start_time": "2024-02-01T07:42:09.758552Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "81882cc4", + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:42:39.780234Z", + "start_time": "2024-02-01T07:42:39.776716Z" + } + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "from matplotlib import pyplot\n", + "from PySDM import Formulae\n", + "from PySDM.physics import in_unit\n", + "from open_atmos_jupyter_utils import show_plot" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "b4cfc727", + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:42:11.407870Z", + "start_time": "2024-02-01T07:42:11.388196Z" + } + }, + "outputs": [], + "source": [ + "from PySDM_examples.Pierchala_et_al_2022.commons import deltas_0_SMOW, TABLE_1, TABLE_2\n", + "\n", + "formulae = Formulae(\n", + " isotope_equilibrium_fractionation_factors='BarkanAndLuz2005+HoritaAndWesolowski1994',\n", + " isotope_meteoric_water_line='Dansgaard1964+BarkanAndLuz2007',\n", + " isotope_ratio_evolution='RayleighDistillation',\n", + " isotope_kinetic_fractionation_factors='CraigGordon',\n", + ")\n", + "const = formulae.constants\n", + "\n", + "F = np.linspace(1, .3, 8)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "2c443b9d", + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:42:12.909082Z", + "start_time": "2024-02-01T07:42:11.412698Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-04-09T23:59:04.159603\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "3722f4f5658c43989add6745f7f54692", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_4.pdf
\"), HTML(value=\"\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-06-08T03:13:45.507217\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.9.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "048cfc9a488644618dd0949c256c127f", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./ventilation_…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "temperature = 273.15 *si.K\n", + "radii = np.linspace(0, 2.5, 100) * si.mm\n", + "fig, ax = pyplot.subplots(1)\n", + "sqrt_re_times_cbrt_sc, vent_coeff = f(radii, temperature)\n", + "\n", + "isotopes = (\n", + " \"2H\",\n", + " \"18O\",\n", + " \"17O\"\n", + ")\n", + "ax.plot(sqrt_re_times_cbrt_sc, vent_coeff, label=\"H2O\")\n", + "for iso in isotopes:\n", + " ax.plot(\n", + " sqrt_re_times_cbrt_sc,\n", + " f_heavy(vent_coeff, temperature, isotope_name=iso),\n", + " label=f\"{iso} - substituted water\"\n", + " )\n", + "ax.set_title(\"\")\n", + "ax.set_xlabel(\"Sc$^{1/3}$Re$^{1/2}$ [1]\")\n", + "ax.set_ylabel(\"Ventilation coefficient [1]\")\n", + "\n", + "ax.legend()\n", + "ax2 = ax.secondary_xaxis(\"top\", functions=(\n", + " interp1d(sqrt_re_times_cbrt_sc,in_unit(radii, si.mm), bounds_error=False, fill_value=\"extrapolate\"),\n", + " interp1d(in_unit(radii, si.mm), sqrt_re_times_cbrt_sc, bounds_error=False, fill_value=\"extrapolate\")\n", + "))\n", + "ax2.set_ticks(np.linspace(0, 5, 11))\n", + "ax2.set_xlabel(\"equivalent drop radius [mm]\")\n", + "ax.grid()\n", + "show_plot(\"ventilation_coefficient_for_isotopes.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "6e88d3799948f9d7", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-08T01:13:45.876630Z", + "start_time": "2025-06-08T01:13:45.556739Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-06-08T03:13:45.843727\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.9.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "404285692919415aa0521ad313917d47", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./ventila…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "n = 10\n", + "_, ax = pyplot.subplots(1)\n", + "ax.plot(sqrt_re_times_cbrt_sc[-n:], vent_coeff[-n:], label=\"H2O\")\n", + "for isotope in isotopes:\n", + " ax.plot(\n", + " sqrt_re_times_cbrt_sc[-n:],\n", + " f_heavy(vent_coeff[-n:], temperature, isotope_name=isotope),\n", + " label=f\"{isotope} - substituted water\"\n", + " )\n", + "ax.set_title(\"\")\n", + "ax.set_xlabel(\"Sc$^{1/3}$Re$^{1/2}$ [1]\")\n", + "ax.set_ylabel(\"Ventilation coefficient [1]\")\n", + "\n", + "ax.legend()\n", + "ax2 = ax.secondary_xaxis(\"top\", functions=(\n", + " interp1d(sqrt_re_times_cbrt_sc,in_unit(radii, si.mm), bounds_error=False, fill_value=\"extrapolate\"),\n", + " interp1d(in_unit(radii, si.mm), sqrt_re_times_cbrt_sc, bounds_error=False, fill_value=\"extrapolate\")\n", + "))\n", + "ax2.set_ticks(np.linspace(0, 5, 11))\n", + "ax2.set_xlabel(\"equivalent drop radius [mm]\")\n", + "ax.grid()\n", + "show_plot(\"ventilation_coefficient_for_isotopes_zoom.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4a5cd5c8032c9c8e", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-08T01:13:45.892254Z", + "start_time": "2025-06-08T01:13:45.890733Z" + } + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c3c0f563-0fd1-41c0-9fb1-618a49b1f0eb", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-08T01:13:45.901772Z", + "start_time": "2025-06-08T01:13:45.900092Z" + } + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.12.8" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/PySDM/source/examples/PySDM_examples/Pyrcel/__init__.py b/PySDM/source/examples/PySDM_examples/Pyrcel/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..b570ce34c008f7b07fdd0eccbfca3779409e8b4a --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Pyrcel/__init__.py @@ -0,0 +1,10 @@ +# pylint: disable=invalid-name +""" +parcel-model example based on the test case from +[Pyrcel package docs](https://pyrcel.readthedocs.io/) + +example_basic_run.ipynb: +.. include:: ./example_basic_run.ipynb.badges.md +""" +from .settings import Settings +from .simulation import Simulation diff --git a/PySDM/source/examples/PySDM_examples/Pyrcel/example_basic_run.ipynb b/PySDM/source/examples/PySDM_examples/Pyrcel/example_basic_run.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..3a582a1c497efa22d704f7b53c2b8c14b47fd564 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Pyrcel/example_basic_run.ipynb @@ -0,0 +1,4168 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Pyrcel/example_basic_run.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Pyrcel/example_basic_run.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Pyrcel/example_basic_run.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### based on Example Figure from Pyrcel code documentation https://pyrcel.readthedocs.io/en/latest/examples/basic_run.html" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-15T08:09:30.494539Z", + "start_time": "2025-06-15T08:09:30.484507Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-15T08:09:34.080888Z", + "start_time": "2025-06-15T08:09:30.501765Z" + } + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "from matplotlib import pyplot\n", + "from scipy.ndimage import uniform_filter1d\n", + "from open_atmos_jupyter_utils import show_plot\n", + "\n", + "from PySDM import Formulae\n", + "from PySDM.physics import si\n", + "from PySDM.initialisation.spectra import Lognormal\n", + "from PySDM.products import (\n", + " ParcelDisplacement, AmbientTemperature, AmbientRelativeHumidity,\n", + " ParticleSizeSpectrumPerVolume, ParticleVolumeVersusRadiusLogarithmSpectrum\n", + ")\n", + "\n", + "from PySDM_examples.Pyrcel import Settings, Simulation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-15T08:09:39.109034Z", + "start_time": "2025-06-15T08:09:34.191376Z" + } + }, + "outputs": [], + "source": [ + "settings = Settings(\n", + " dz = 1 * si.m,\n", + " n_sd_per_mode = (5, 5),\n", + " aerosol_modes_by_kappa = {\n", + " .54: Lognormal(\n", + " norm_factor=850 / si.cm ** 3,\n", + " m_mode=15 * si.nm,\n", + " s_geom=1.6\n", + " ),\n", + " 1.2: Lognormal(\n", + " norm_factor=10 / si.cm ** 3,\n", + " m_mode=850 * si.nm,\n", + " s_geom=1.2\n", + " )\n", + " },\n", + " vertical_velocity = 1.0 * si.m / si.s,\n", + " initial_pressure = 775 * si.mbar,\n", + " initial_temperature = 274 * si.K,\n", + " initial_relative_humidity = .98,\n", + " displacement = 250 * si.m,\n", + " formulae = Formulae(constants={'MAC': .3})\n", + ")\n", + "\n", + "dry_radius_bin_edges = np.logspace(\n", + " np.log10(1e-3 * si.um),\n", + " np.log10(5e0 * si.um),\n", + " 33, endpoint=False\n", + ")\n", + "\n", + "simulation = Simulation(settings, products=(\n", + " ParcelDisplacement(\n", + " name='z'),\n", + " AmbientRelativeHumidity(\n", + " name='S_max_percent', unit='%', var='RH'),\n", + " AmbientTemperature(\n", + " name='T'),\n", + " ParticleSizeSpectrumPerVolume(\n", + " name='dry:dN/dR', radius_bins_edges=dry_radius_bin_edges, dry=True),\n", + " ParticleVolumeVersusRadiusLogarithmSpectrum(\n", + " name='dry:dV/dlnR', radius_bins_edges=dry_radius_bin_edges, dry=True),\n", + "))" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-15T08:09:50.439522Z", + "start_time": "2025-06-15T08:09:39.121807Z" + } + }, + "outputs": [], + "source": [ + "output = simulation.run()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-15T08:10:33.947302Z", + "start_time": "2025-06-15T08:10:33.222745Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-06-15T10:10:33.900169\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.9.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "2c65384c54db4e3e9bdb53680f672ded", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./supersaturation.pdf
\"), HT…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, axs = pyplot.subplots(1, 2, sharey=True, figsize=(10, 5))\n", + "\n", + "axS = axs[0]\n", + "axS.plot(np.asarray(output['products']['S_max_percent'])-100, output['products']['z'], color='black')\n", + "axS.set_ylabel('Displacement [m]')\n", + "axS.set_xlabel('Supersaturation [%]')\n", + "axS.set_xlim(0, 0.7)\n", + "axS.set_ylim(0, 250)\n", + "axS.text(0.3, 52, f\"max S = {np.nanmax(output['products']['S_max_percent'])-100:.2f}%\")\n", + "axS.grid()\n", + "\n", + "axT = axS.twiny()\n", + "axT.xaxis.label.set_color('red')\n", + "axT.tick_params(axis='x', colors='red')\n", + "axT.plot(output['products']['T'], output['products']['z'], color='red')\n", + "rng = (270, 274)\n", + "axT.set_xlim(*rng)\n", + "axT.set_xticks(np.linspace(*rng, num=5))\n", + "axT.set_xlabel('Temperature [K]')\n", + "\n", + "axR = axs[1]\n", + "axR.set_xscale('log')\n", + "axR.set_xlim(1e-2, 1e1)\n", + "for drop_id, volume in enumerate(output['attributes']['volume']):\n", + " axR.plot(\n", + " settings.formulae.trivia.radius(volume=np.asarray(volume)) / si.um,\n", + " output['products']['z'],\n", + " color='magenta' if drop_id < settings.n_sd_per_mode[0] else 'blue',\n", + " label='sulfate' if drop_id == 0 else 'sea salt' if drop_id == settings.n_sd_per_mode[0] else ''\n", + " )\n", + "axR.legend(loc='upper right')\n", + "axR.set_xlabel('Droplet radius [μm]')\n", + "\n", + "show_plot(\"supersaturation.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-15T08:09:50.808752Z", + "start_time": "2024-02-01T18:50:47.502563Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-06-18T16:26:44.383066\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "86be8c039f3c41409ee42fcc1aaf5210", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./size_dist.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, axs = pyplot.subplots(1, 2, figsize=(10, 5), sharex=True)\n", + "for i, name in enumerate([\"dry:dN/dR\",\"dry:dV/dlnR\",]):\n", + " spec = output['products'][name][0]\n", + " if name.endswith(\"/dR\"):\n", + " spec /= np.diff(np.log(dry_radius_bin_edges))\n", + " name = name.replace('/dR', '/dlnR')\n", + " axs[i].step(\n", + " dry_radius_bin_edges[:-1] / si.um,\n", + " uniform_filter1d(spec, size=3),\n", + " where='pre'\n", + " )\n", + " axs[i].set_ylabel(name)\n", + " axs[i].set_xlabel(\"Aerosol dry radius [μm]\")\n", + " axs[i].grid()\n", + " axs[i].set_xscale('log')\n", + " axs[i].set_yscale('log')\n", + "pyplot.xlim(1e-3, 5e0)\n", + "\n", + "show_plot(\"size_dist.pdf\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/PySDM/source/examples/PySDM_examples/Pyrcel/profile_plotter.py b/PySDM/source/examples/PySDM_examples/Pyrcel/profile_plotter.py new file mode 100644 index 0000000000000000000000000000000000000000..b8c508aa4aed049d7a542a9c8b4246bdbf55aef3 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Pyrcel/profile_plotter.py @@ -0,0 +1,71 @@ +import numpy as np +from matplotlib import pyplot +from open_atmos_jupyter_utils import show_plot + +from PySDM.physics.constants import si + + +class ProfilePlotter: + def __init__(self, settings, legend=True, log_base=10): + self.settings = settings + self.format = "pdf" + self.legend = legend + self.log_base = log_base + self.ax = pyplot + self.fig = pyplot + + def show(self): + pyplot.tight_layout() + show_plot() + + def save(self, file): + # self.finish() + pyplot.savefig(file, format=self.format) + + def plot(self, output): + self.plot_data(self.settings, output) + + def plot_data(self, settings, output): + _, axs = pyplot.subplots(1, 2, sharey=True, figsize=(10, 5)) + axS = axs[0] + if output["products"].get("S_max"): + SS_percent = (np.asarray(output["products"]["S_max"]) - 1) * 100 + else: + SS_percent = np.asarray(output["products"]["S_max_percent"]) - 100 + axS.plot( + SS_percent, + output["products"]["z"], + color="black", + ) + axS.set_ylabel("Displacement [m]") + axS.set_xlabel("Supersaturation [%]") + axS.set_xlim(0, 0.7) + axS.set_ylim(0, 250) + axS.text(0.3, 52, f"max SS = {np.nanmax(SS_percent):.2f}%") + axS.grid() + + axT = axS.twiny() + axT.xaxis.label.set_color("red") + axT.tick_params(axis="x", colors="red") + axT.plot(output["products"]["T"], output["products"]["z"], color="red") + rng = (272, 274) + axT.set_xlim(*rng) + axT.set_xticks(np.linspace(*rng, num=5)) + axT.set_xlabel("Temperature [K]") + + axR = axs[1] + axR.set_xscale("log") + axR.set_xlim(1e-2, 1e2) + for drop_id, volume in enumerate(output["attributes"]["volume"]): + axR.plot( + settings.formulae.trivia.radius(volume=np.asarray(volume)) / si.um, + output["products"]["z"], + color="magenta" if drop_id < settings.n_sd_per_mode[0] else "blue", + label=( + "mode 1" + if drop_id == 0 + else "mode 2" if drop_id == settings.n_sd_per_mode[0] else "" + ), + ) + axR.legend(loc="upper right") + axR.set_xlabel("Droplet radius [μm]") diff --git a/PySDM/source/examples/PySDM_examples/Pyrcel/settings.py b/PySDM/source/examples/PySDM_examples/Pyrcel/settings.py new file mode 100644 index 0000000000000000000000000000000000000000..1a36a2bad6be4892f1c0986324421cabad3df681 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Pyrcel/settings.py @@ -0,0 +1,64 @@ +from typing import Dict + +import numpy as np +from pystrict import strict + +from PySDM import Formulae +from PySDM.initialisation.impl.spectrum import Spectrum + + +@strict +class Settings: + def __init__( + self, + dz: float, + n_sd_per_mode: tuple, + aerosol_modes_by_kappa: Dict[float, Spectrum], + vertical_velocity: float, + initial_temperature: float, + initial_pressure: float, + initial_relative_humidity: float, + displacement: float, + formulae: Formulae, + ): + self.formulae = formulae + self.n_sd_per_mode = n_sd_per_mode + self.aerosol_modes_by_kappa = aerosol_modes_by_kappa + + const = self.formulae.constants + self.vertical_velocity = vertical_velocity + self.initial_pressure = initial_pressure + self.initial_temperature = initial_temperature + pv0 = initial_relative_humidity * formulae.saturation_vapour_pressure.pvs_water( + initial_temperature + ) + self.initial_vapour_mixing_ratio = const.eps * pv0 / (initial_pressure - pv0) + self.t_max = displacement / vertical_velocity + self.timestep = dz / vertical_velocity + self.output_interval = self.timestep + + @property + def initial_air_density(self): + return self.formulae.state_variable_triplet.rho_of_rhod_and_water_vapour_mixing_ratio( + rhod=self.formulae.trivia.p_d( + self.initial_pressure, self.initial_vapour_mixing_ratio + ) + / self.initial_temperature + / self.formulae.constants.Rd, + water_vapour_mixing_ratio=self.initial_vapour_mixing_ratio, + ) + + @property + def nt(self) -> int: + nt = self.t_max / self.timestep + nt_int = round(nt) + np.testing.assert_almost_equal(nt, nt_int) + return nt_int + + @property + def steps_per_output_interval(self) -> int: + return int(self.output_interval / self.timestep) + + @property + def output_steps(self) -> np.ndarray: + return np.arange(0, self.nt + 1, self.steps_per_output_interval) diff --git a/PySDM/source/examples/PySDM_examples/Pyrcel/simulation.py b/PySDM/source/examples/PySDM_examples/Pyrcel/simulation.py new file mode 100644 index 0000000000000000000000000000000000000000..aafdb85aa56a6a650f18273676f7a8bd3893d138 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Pyrcel/simulation.py @@ -0,0 +1,108 @@ +import numpy as np +from PySDM_examples.utils import BasicSimulation + +from PySDM import Builder +from PySDM.backends import CPU +from PySDM.backends.impl_numba.test_helpers import scipy_ode_condensation_solver +from PySDM.dynamics import AmbientThermodynamics, Condensation +from PySDM.environments import Parcel +from PySDM.initialisation.hygroscopic_equilibrium import equilibrate_wet_radii +from PySDM.initialisation.sampling.spectral_sampling import ConstantMultiplicity +from PySDM.physics import si + + +class Simulation(BasicSimulation): + def __init__( + self, + settings, + products=None, + scipy_solver=False, + rtol_thd=1e-10, + rtol_x=1e-10, + mass_of_dry_air=44 * si.kg, + ): + n_sd = sum(settings.n_sd_per_mode) + builder = Builder( + n_sd=n_sd, + backend=CPU( + formulae=settings.formulae, override_jit_flags={"parallel": False} + ), + environment=Parcel( + dt=settings.timestep, + p0=settings.initial_pressure, + initial_water_vapour_mixing_ratio=settings.initial_vapour_mixing_ratio, + T0=settings.initial_temperature, + w=settings.vertical_velocity, + mass_of_dry_air=mass_of_dry_air, + ), + ) + builder.add_dynamic(AmbientThermodynamics()) + builder.add_dynamic(Condensation(rtol_thd=rtol_thd, rtol_x=rtol_x)) + + volume = ( + builder.particulator.environment.mass_of_dry_air + / settings.initial_air_density + ) + attributes = { + k: np.empty(0) + for k in ("dry volume", "kappa times dry volume", "multiplicity") + } + for i, (kappa, spectrum) in enumerate(settings.aerosol_modes_by_kappa.items()): + sampling = ConstantMultiplicity(spectrum) + r_dry, n_per_volume = sampling.sample_deterministic( + settings.n_sd_per_mode[i] + ) + v_dry = settings.formulae.trivia.volume(radius=r_dry) + attributes["multiplicity"] = np.append( + attributes["multiplicity"], n_per_volume * volume + ) + attributes["dry volume"] = np.append(attributes["dry volume"], v_dry) + attributes["kappa times dry volume"] = np.append( + attributes["kappa times dry volume"], v_dry * kappa + ) + r_wet = equilibrate_wet_radii( + r_dry=settings.formulae.trivia.radius(volume=attributes["dry volume"]), + environment=builder.particulator.environment, + kappa_times_dry_volume=attributes["kappa times dry volume"], + ) + attributes["volume"] = settings.formulae.trivia.volume(radius=r_wet) + + super().__init__( + particulator=builder.build(attributes=attributes, products=products) + ) + if scipy_solver: + scipy_ode_condensation_solver.patch_particulator(self.particulator) + + self.output_attributes = { + "volume": tuple([] for _ in range(self.particulator.n_sd)) + } + self.settings = settings + + self.__sanity_checks(attributes, volume) + + def __sanity_checks(self, attributes, volume): + for attribute in attributes.values(): + assert attribute.shape[0] == self.particulator.n_sd + np.testing.assert_approx_equal( + sum(attributes["multiplicity"]) / volume, + sum( + mode.norm_factor + for mode in self.settings.aerosol_modes_by_kappa.values() + ), + significant=4, + ) + + def _save(self, output): + for key, attr in self.output_attributes.items(): + attr_data = self.particulator.attributes[key].to_ndarray() + for drop_id in range(self.particulator.n_sd): + attr[drop_id].append(attr_data[drop_id]) + super()._save(output) + + def run(self, observers=()): + for observer in observers: + self.particulator.observers.append(observer) + output_products = super()._run( + self.settings.nt, self.settings.steps_per_output_interval + ) + return {"products": output_products, "attributes": self.output_attributes} diff --git a/PySDM/source/examples/PySDM_examples/Rogers_1975/__init__.py b/PySDM/source/examples/PySDM_examples/Rogers_1975/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..0bc8eb68a8d18330f41b76303cc3b953edd8e8ea --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Rogers_1975/__init__.py @@ -0,0 +1,9 @@ +""" +based on Rogers 1975 (Atmosphere) +https://doi.org/10.1080/00046973.1975.9648397 + +fig_1.ipynb: +.. include:: ./fig_1.ipynb.badges.md +""" + +# pylint: disable=invalid-name diff --git a/PySDM/source/examples/PySDM_examples/Rogers_1975/fig_1.ipynb b/PySDM/source/examples/PySDM_examples/Rogers_1975/fig_1.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..2b7ac946c7d31e7ff212856a5df6e22c645dde38 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Rogers_1975/fig_1.ipynb @@ -0,0 +1,1593 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "bfd1a1e7-29d9-443d-9eb4-ef75151ffcc7", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Rogers_1975/fig_1.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Rogers_1975/fig_1.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Rogers_1975/fig_1.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "ca764a8c-a315-4dd3-9814-bd59401f6ccd", + "metadata": {}, + "source": [ + "## basic parcel simulation based on [Rogers 1975](https://doi.org/10.1080/00046973.1975.9648397) \"_An Elementary Parcel Model with Explicit Condensation and Supersaturation_\" and implemented using SciPy+Pint but without PySDM - for illustrative purposes." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "99b453fa", + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-31T22:04:02.540441Z", + "start_time": "2025-05-31T22:04:02.496744Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "7dadd922", + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-31T22:04:02.558544Z", + "start_time": "2025-05-31T22:04:02.545638Z" + } + }, + "outputs": [], + "source": [ + "import collections\n", + "import functools\n", + "from open_atmos_jupyter_utils import show_plot\n", + "from matplotlib import pyplot\n", + "import numpy as np\n", + "import scipy\n", + "import scipy.constants as const\n", + "import chempy" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "0ecc07d1-88e4-4a4f-a469-fe4f14b31ac4", + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-31T22:04:02.768131Z", + "start_time": "2025-05-31T22:04:02.621182Z" + } + }, + "outputs": [], + "source": [ + "import pint\n", + "si = pint.UnitRegistry()\n", + "si.setup_matplotlib()\n", + "si.define('fraction = [] = frac')\n", + "si.define('percent = 1e-2 frac = pct')" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "31cdfaaa-31be-46c6-9dcf-257024240cd6", + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-31T22:04:02.781844Z", + "start_time": "2025-05-31T22:04:02.774125Z" + } + }, + "outputs": [], + "source": [ + "class Storage:\n", + " \"\"\" state vector representation with each element having its own Pint-compatible\n", + " physical dimension, thus allowing to seamlessly couple Pint and scipy.odeint\n", + " (all methods return objects that inherit from `numpy.ndarray` but are additionally\n", + " equipped with .VAR unit-aware setters and getters, allowing both unit-unaware\n", + " whole-array expressions (e.g., `state += dt * deriv`) as well as unit-aware \n", + " operations on state vars (e.g., `state.T = 300 * si.K` or `state.m[:] = ...`) \"\"\"\n", + "\n", + " var_units = {\n", + " 'p': si.Pa,\n", + " 'T': si.K,\n", + " 'S': si.dimensionless,\n", + " 'r': si.metre,\n", + " }\n", + " \n", + " der_unit = si.second\n", + "\n", + " @staticmethod\n", + " def __make_storage(shape, deriv=False):\n", + " def getter(self, idx, unit):\n", + " return self[idx] * unit\n", + "\n", + " def setter(self, value, idx, unit):\n", + " self[idx] = (value.to(unit) / unit).magnitude\n", + "\n", + " properties = {}\n", + " for i, key in enumerate(Storage.var_units.keys()):\n", + " kwargs = {\n", + " 'unit': Storage.var_units[key] / (Storage.der_unit if deriv else 1),\n", + " 'idx': i\n", + " }\n", + " properties[key] = property(\n", + " functools.partial(getter, **kwargs),\n", + " functools.partial(setter, **kwargs),\n", + " ) \n", + " \n", + " return type(\"StorageImpl\", (np.ndarray,), {**properties, \"__module__\": __name__})(shape)\n", + "\n", + " @staticmethod\n", + " def make_state():\n", + " \"\"\" returns a newly allocated unit-aware storage \"\"\"\n", + " return Storage.__make_storage((len(Storage.var_units),))\n", + "\n", + " @staticmethod\n", + " def make_deriv(state):\n", + " \"\"\" returns a newly allocated unit-aware storage with size of `state` and derivative dimensions \"\"\"\n", + " return Storage.__make_storage(state.shape, deriv=True)\n", + "\n", + " @staticmethod\n", + " def view_state(array):\n", + " \"\"\" returns a newly allocated unit-aware storage with size and data from unit-unaware `array` \"\"\"\n", + " storage = Storage.__make_storage(array.shape)\n", + " storage[:] = array[:]\n", + " return storage" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "69a2d947-8581-4382-9939-07088584933b", + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-31T22:04:02.808299Z", + "start_time": "2025-05-31T22:04:02.788508Z" + } + }, + "outputs": [], + "source": [ + "# constants (coefficients with modified 10 exponents to match SI units)\n", + "C = {\n", + " # gas constant for dry air\n", + " \"R\": const.R * si.joule / si.kelvin / si.mole / (\n", + " 0.76 * chempy.Substance.from_formula(\"N2\").mass * si.gram / si.mole\n", + " + 0.23 * chempy.Substance.from_formula(\"O2\").mass * si.gram / si.mole\n", + " + 0.01 * chempy.Substance.from_formula(\"Ar\").mass * si.gram / si.mole\n", + " ),\n", + " # standard acceleration of gravity\n", + " \"g\": const.g * si.metre / si.second**2,\n", + " # latent heat of condensation\n", + " \"L\": 2.5e3 * si.J / si.g,\n", + " # specific gravity of vapour vs. dry air\n", + " \"eps\": .622,\n", + " # specific heat at constant pressure of dry air\n", + " \"cp\": 1005 * si.joule / si.kilogram / si.kelvin,\n", + " # density of liquid water\n", + " \"rho_L\": 1 * si.kg / si.litre,\n", + " # eq A.1\n", + " \"K\": lambda T: 2.42e-2 * (393/(T.to(si.K).magnitude + 120)) * (T.to(si.K).magnitude/273)**(3/2) * si.J / si.m / si.s / si.K,\n", + " # eq A.2\n", + " \"D_over_K\": lambda p, T: 8.28 / 2.42 * T.to(si.K).magnitude / p.to(si.dyne / si.cm**2).magnitude * si.m**3 / si.J * si.K,\n", + " # eq A.3\n", + " \"e_s\": lambda T: 2.75e12 * np.exp(-5.44e3 / (T/si.K)) * si.ubar,\n", + "}\n", + "C = collections.namedtuple(\"C\", C.keys())(**C)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "7d9b98c0-971c-41a0-87c2-f7adcfc958a1", + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-31T22:04:02.822694Z", + "start_time": "2025-05-31T22:04:02.814783Z" + } + }, + "outputs": [], + "source": [ + "np.testing.assert_approx_equal(actual=C.e_s(273.15 * si.K).to(si.mbar).magnitude, desired=6.1, significant=2)\n", + "np.testing.assert_approx_equal(actual=C.K(273.15 * si.K).to(si.J / si.m / si.s / si.K).magnitude, desired=2.4e-2, significant=2)\n", + "np.testing.assert_approx_equal(\n", + " actual=(C.K(169.75 * si.K) * C.D_over_K(1000 * si.hPa, 169.75 * si.K)).to(si.m**2 / si.s).magnitude,\n", + " desired=9.34e-6,\n", + " significant=2\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "d2011171-3291-4c0e-ad4e-23ba8bf25f52", + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-31T22:04:02.831038Z", + "start_time": "2025-05-31T22:04:02.827859Z" + } + }, + "outputs": [], + "source": [ + "class System: # pylint: disable=too-few-public-methods\n", + " \"\"\" ODE system \"\"\"\n", + " def __init__(self, *, U, nu_0):\n", + " self.U = U\n", + " self.nu_0 = nu_0\n", + "\n", + " def __call__(self, _, state):\n", + " state = Storage.view_state(state)\n", + " deriv = Storage.make_deriv(state)\n", + "\n", + " # eq (8)\n", + " rho = state.p / C.R / state.T\n", + " \n", + " # eq (5) multiplied by Δt\n", + " deriv.p = -rho * C.g * self.U \n", + "\n", + " # eq (2)\n", + " K = C.K(state.T)\n", + " Fk = C.L**2 * C.eps * C.rho_L / K / C.R / state.T**2\n", + " Fd = C.R * state.T * C.rho_L / C.eps / C.D_over_K(state.p, state.T) / K / C.e_s(state.T)\n", + " sigma = (state.S - 1) / (Fk + Fd)\n", + " \n", + " # eq (1)\n", + " deriv.r = sigma / state.r\n", + " \n", + " # time derivative of eq (4)\n", + " dksi_dt = 4 * np.pi * C.rho_L * self.nu_0 * state.r**2 * deriv.r\n", + " \n", + " # eq (6) divided by dt, multiplied by T\n", + " deriv.T = (\n", + " state.T * C.R / C.cp * deriv.p / state.p + \n", + " C.L / C.cp * dksi_dt\n", + " )\n", + "\n", + " # eq (10)\n", + " Q1 = C.L * C.g * C.eps / C.R / C.cp / state.T**2 - C.g / C.R / state.T\n", + " Q2 = C.R * state.T / C.eps / C.e_s(state.T) + C.eps * C.L**2 / C.cp / state.T / state.p\n", + " deriv.S = Q1 * self.U - rho * Q2 * dksi_dt\n", + " \n", + " return deriv" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "0dc34252-4ac9-4d23-89f5-793239183eb7", + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-31T22:04:02.837783Z", + "start_time": "2025-05-31T22:04:02.835253Z" + } + }, + "outputs": [], + "source": [ + "def solve(system, state, t_max, max_step):\n", + " integ = scipy.integrate.solve_ivp(\n", + " system,\n", + " [0, t_max / Storage.der_unit],\n", + " state,\n", + " max_step=max_step.to(Storage.der_unit).magnitude,\n", + " method='LSODA'\n", + " )\n", + " assert integ.success, integ.message\n", + " return Storage.view_state(integ.y), integ.t * Storage.der_unit" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "ceff8ab7-8b6d-46a1-83e8-83b2fc893380", + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-31T22:04:02.949536Z", + "start_time": "2025-05-31T22:04:02.845319Z" + } + }, + "outputs": [], + "source": [ + "storage_state = Storage.make_state()\n", + "storage_state.p = 800 * si.mbar\n", + "storage_state.T = (273.15 + 7) * si.K\n", + "storage_state.S = 1 * si.dimensionless\n", + "storage_state.r = 8 * si.um\n", + "\n", + "rho0 = storage_state.p / C.R / storage_state.T\n", + "ode_system = System(U=10 * si.m / si.s, nu_0=200 / si.cm**3 / rho0)\n", + "solution, tsteps = solve(ode_system, storage_state, t_max=20 * si.s, max_step=.5 * si.s)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "f00aa5f8-219a-41dd-98fd-c8a411a502b9", + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-31T22:04:03.158032Z", + "start_time": "2025-05-31T22:04:02.953629Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-06-01T00:04:03.139525\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.0, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "1f7eafe07a9b44cb818bc21953e497d2", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_1.pdf
\"), HTML(value=\"" + ], + "image/svg+xml": "\n\n\n \n \n \n \n 2026-01-15T11:44:51.702014\n image/svg+xml\n \n \n Matplotlib v3.10.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n" + }, + "metadata": {}, + "output_type": "display_data", + "jetTransient": { + "display_id": null + } + }, + { + "data": { + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_4.pdf
\"), HTML(value=\"" + ], + "image/svg+xml": "\n\n\n \n \n \n \n 2026-01-15T11:44:52.535283\n image/svg+xml\n \n \n Matplotlib v3.10.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n" + }, + "metadata": {}, + "output_type": "display_data", + "jetTransient": { + "display_id": null + } + }, + { + "data": { + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_5.pdf
\"), HTML(value=\"\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-06-29T23:21:23.231644\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "74f75df34825493fb9dff65c3505f8ff", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./tmpgfeualwr.pdf
\"), HTML(value…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "n_SD = widgets.IntSlider(value=14, min=12, max=18, step=1, description='$log_2(n_{SD})$', continuous_update=False)\n", + "n_step = widgets.IntSlider(value=3600, step=100, min=100, max=3600, description='$n_{step}$', continuous_update=False)\n", + "n_plot = widgets.IntSlider(value=3, step=1, min=1, max=8, description='$n_{plot}$', continuous_update=False)\n", + "sliders = widgets.HBox([n_SD, n_step, n_plot])\n", + "\n", + "adaptive = widgets.Checkbox(value=False, description='adaptive dt')\n", + "smooth = widgets.Checkbox(value=True, description='smooth plot')\n", + "gpu = widgets.Checkbox(value=False, description='GPU')\n", + "options = [adaptive, smooth]\n", + "if ThrustRTC.ENABLE:\n", + " options.append(gpu)\n", + "boxes = widgets.HBox(options)\n", + "freezer = widgets.Freezer([n_SD, n_step, n_plot, adaptive, gpu])\n", + "inputs = {\n", + " 'freezer': freezer, 'n_SD': n_SD, 'n_step': n_step, 'n_plot': n_plot,\n", + " 'adaptive': adaptive, 'smooth': smooth, 'gpu': gpu\n", + "}\n", + "\n", + "if 'CI' not in os.environ:\n", + " widgets.display(sliders, boxes, progbar, widgets.interactive_output(demo, inputs))\n", + "else:\n", + " demo(**{k:v.value for k,v in inputs.items()})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-16T17:14:51.419820Z", + "start_time": "2024-12-16T17:14:51.418628Z" + } + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/PySDM/source/examples/PySDM_examples/Shima_et_al_2009/settings.py b/PySDM/source/examples/PySDM_examples/Shima_et_al_2009/settings.py new file mode 100644 index 0000000000000000000000000000000000000000..5e0d524baebc7bfc75d97d36477b32cb5fa4d25c --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Shima_et_al_2009/settings.py @@ -0,0 +1,35 @@ +from typing import Optional + +import numpy as np +from pystrict import strict + +from PySDM import Formulae +from PySDM.dynamics.collisions.collision_kernels import Golovin +from PySDM.initialisation import spectra +from PySDM.physics import si + + +@strict +class Settings: + def __init__(self, steps: Optional[list] = None): + steps = steps or [0, 1200, 2400, 3600] + self.formulae = Formulae() + self.n_sd = 2**13 + self.n_part = 2**23 / si.metre**3 + self.X0 = self.formulae.trivia.volume(radius=30.531 * si.micrometres) + self.dv = 1e6 * si.metres**3 + self.norm_factor = self.n_part * self.dv + self.rho = 1000 * si.kilogram / si.metre**3 + self.dt = 1 * si.seconds + self.adaptive = False + self.seed = 44 + self.steps = steps + self.kernel = Golovin(b=1.5e3 / si.second) + self.spectrum = spectra.Exponential(norm_factor=self.norm_factor, scale=self.X0) + self.radius_bins_edges = np.logspace( + np.log10(10 * si.um), np.log10(5e3 * si.um), num=128, endpoint=True + ) + + @property + def output_steps(self): + return [int(step / self.dt) for step in self.steps] diff --git a/PySDM/source/examples/PySDM_examples/Shima_et_al_2009/spectrum_plotter.py b/PySDM/source/examples/PySDM_examples/Shima_et_al_2009/spectrum_plotter.py new file mode 100644 index 0000000000000000000000000000000000000000..f989cec287a0ab0d293476317de14ed9a7c0fc99 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Shima_et_al_2009/spectrum_plotter.py @@ -0,0 +1,165 @@ +import matplotlib +import numpy as np +from matplotlib import pyplot +from open_atmos_jupyter_utils import show_plot +from packaging import version +from PySDM_examples.Shima_et_al_2009.error_measure import error_measure + +from PySDM.physics.constants import si + +_matplotlib_version_3_3_3 = version.parse("3.3.0") +_matplotlib_version_actual = version.parse(matplotlib.__version__) + + +class SpectrumColors: + def __init__(self, begining="#2cbdfe", end="#b317b1"): + self.b = begining + self.e = end + + def __call__(self, value: float): + bR, bG, bB = int(self.b[1:3], 16), int(self.b[3:5], 16), int(self.b[5:7], 16) + eR, eG, eB = int(self.e[1:3], 16), int(self.e[3:5], 16), int(self.e[5:7], 16) + R = bR + int((eR - bR) * value) + G = bG + int((eG - bG) * value) + B = bB + int((eB - bB) * value) + result = f"#{hex(R)[2:4]}{hex(G)[2:4]}{hex(B)[2:4]}" + return result + + +class SpectrumPlotter: + def __init__(self, settings, title=None, grid=True, legend=True, log_base=10): + self.settings = settings + self.format = "pdf" + self.colors = SpectrumColors() + self.smooth = False + self.smooth_scope = 2 + self.legend = legend + self.grid = grid + self.title = title + self.xlabel = "particle radius [µm]" + self.ylabel = "dm/dlnr [g/m^3/(unit dr/r)]" + self.log_base = log_base + self.ax = pyplot + self.fig = pyplot + self.finished = False + + def finish(self): + if self.finished: + return + self.finished = True + if self.grid: + self.ax.grid() + + base_arg = { + "base" + + ( + "x" if _matplotlib_version_actual < _matplotlib_version_3_3_3 else "" + ): self.log_base + } + if self.title is not None: + try: + self.ax.title(self.title) + except TypeError: + self.ax.set_title(self.title) + try: + self.ax.xscale("log", **base_arg) + self.ax.xlabel(self.xlabel) + self.ax.ylabel(self.ylabel) + except AttributeError: + self.ax.set_xscale("log", **base_arg) + self.ax.set_xlabel(self.xlabel) + self.ax.set_ylabel(self.ylabel) + if self.legend: + self.ax.legend() + + def show(self): + self.finish() + pyplot.tight_layout() + show_plot() + + def save(self, file): + self.finish() + pyplot.savefig(file, format=self.format) + + def plot( + self, spectrum, t, label=None, color=None, title=None, add_error_to_label=False + ): + error = self.plot_analytic_solution(self.settings, t, spectrum, title) + if label is not None and add_error_to_label: + label += f" error={error:.4g}" + self.plot_data(self.settings, t, spectrum, label, color) + return error + + def plot_analytic_solution(self, settings, t, spectrum, title): + if t == 0: + analytic_solution = settings.spectrum.size_distribution + else: + + def analytic_solution(x): + return settings.norm_factor * settings.kernel.analytic_solution( + x=x, t=t, x_0=settings.X0, N_0=settings.n_part + ) + + volume_bins_edges = self.settings.formulae.trivia.volume( + settings.radius_bins_edges + ) + dm = np.diff(volume_bins_edges) + dr = np.diff(settings.radius_bins_edges) + + pdf_m_x = volume_bins_edges[:-1] + dm / 2 + pdf_m_y = analytic_solution(pdf_m_x) + + pdf_r_x = settings.radius_bins_edges[:-1] + dr / 2 + pdf_r_y = pdf_m_y * dm / dr * pdf_r_x + + x = pdf_r_x * si.metres / si.micrometres + y_true = ( + pdf_r_y + * self.settings.formulae.trivia.volume(radius=pdf_r_x) + * settings.rho + / settings.dv + * si.kilograms + / si.grams + ) + + self.ax.plot(x, y_true, color="black") + + if spectrum is not None: + y = spectrum * si.kilograms / si.grams + error = error_measure(y, y_true, x) + self.title = ( + title or f"error measure: {error:.2f}" + ) # TODO #327 relative error + return error + return None + + def plot_data(self, settings, t, spectrum, label, color): + if self.smooth: + scope = self.smooth_scope + if t != 0: + new = np.copy(spectrum) + for _ in range(2): + for i in range(scope, len(spectrum) - scope): + new[i] = np.mean(spectrum[i - scope : i + scope + 1]) + scope = 1 + for i in range(scope, len(spectrum) - scope): + spectrum[i] = np.mean(new[i - scope : i + scope + 1]) + + x = settings.radius_bins_edges[:-scope] + dx = np.diff(x) + self.ax.plot( + (x[:-1] + dx / 2) * si.metres / si.micrometres, + spectrum[:-scope] * si.kilograms / si.grams, + label=label or f"t = {t}s", + color=color + or self.colors(t / (self.settings.output_steps[-1] * self.settings.dt)), + ) + else: + self.ax.step( + settings.radius_bins_edges[:-1] * si.metres / si.micrometres, + spectrum * si.kilograms / si.grams, + where="post", + label=label or f"t = {t}s", + color=color + or self.colors(t / (self.settings.output_steps[-1] * self.settings.dt)), + ) diff --git a/PySDM/source/examples/PySDM_examples/Shima_et_al_2009/tutorial_example.py b/PySDM/source/examples/PySDM_examples/Shima_et_al_2009/tutorial_example.py new file mode 100644 index 0000000000000000000000000000000000000000..e2c5c6d4879ce5c30b70b192e5a0a3d314d1118b --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Shima_et_al_2009/tutorial_example.py @@ -0,0 +1,47 @@ +from PySDM.backends import CPU +from PySDM.builder import Builder +from PySDM.dynamics import Coalescence +from PySDM.environments import Box +from PySDM.initialisation.sampling.spectral_sampling import ConstantMultiplicity +from PySDM.products import ParticleVolumeVersusRadiusLogarithmSpectrum, WallTime + + +def run(settings, observers=()): + builder = Builder( + n_sd=settings.n_sd, + backend=CPU(formulae=settings.formulae), + environment=Box(dv=settings.dv, dt=settings.dt), + ) + attributes = {} + sampling = ConstantMultiplicity(settings.spectrum) + attributes["volume"], attributes["multiplicity"] = sampling.sample_deterministic( + settings.n_sd + ) + coalescence = Coalescence( + collision_kernel=settings.kernel, adaptive=settings.adaptive + ) + builder.add_dynamic(coalescence) + products = ( + ParticleVolumeVersusRadiusLogarithmSpectrum( + settings.radius_bins_edges, name="dv/dlnr" + ), + WallTime(), + ) + particulator = builder.build(attributes, products) + if hasattr(settings, "u_term") and "terminal velocity" in particulator.attributes: + particulator.attributes["terminal velocity"].approximation = settings.u_term( + particulator + ) + + for observer in observers: + particulator.observers.append(observer) + + vals = {} + particulator.products["wall time"].reset() + for step in settings.output_steps: + particulator.run(step - particulator.n_steps) + vals[step] = particulator.products["dv/dlnr"].get()[0] + vals[step][:] *= settings.rho + + exec_time = particulator.products["wall time"].get() + return vals, exec_time diff --git a/PySDM/source/examples/PySDM_examples/Shima_et_al_2009/tutorial_plotter.py b/PySDM/source/examples/PySDM_examples/Shima_et_al_2009/tutorial_plotter.py new file mode 100644 index 0000000000000000000000000000000000000000..ec4f239ab4cfa79b0114b20b0a37c55b4d001ec7 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Shima_et_al_2009/tutorial_plotter.py @@ -0,0 +1,129 @@ +import numpy as np +from matplotlib import pyplot +from open_atmos_jupyter_utils import show_plot + +from PySDM.physics.constants import si + + +class SpectrumColors: + def __init__(self, begining="#2cbdfe", end="#b317b1"): + self.b = begining + self.e = end + + def __call__(self, value: float): + bR, bG, bB = int(self.b[1:3], 16), int(self.b[3:5], 16), int(self.b[5:7], 16) + eR, eG, eB = int(self.e[1:3], 16), int(self.e[3:5], 16), int(self.e[5:7], 16) + R = bR + int((eR - bR) * value) + G = bG + int((eG - bG) * value) + B = bB + int((eB - bB) * value) + result = f"#{hex(R)[2:4]}{hex(G)[2:4]}{hex(B)[2:4]}" + return result + + +class SpectrumPlotter: + def __init__(self, settings, grid=True, legend=True, log_base=10): + self.settings = settings + self.format = "pdf" + self.colors = SpectrumColors() + self.smooth = False + self.smooth_scope = 2 + self.legend = legend + self.grid = grid + self.xlabel = "particle radius [µm]" + self.ylabel = "dm/dlnr [g/m$^3$]" + self.log_base = log_base + self.ax = pyplot + self.fig = pyplot + self.finished = False + + def finish(self): + if self.finished: + return + self.finished = True + if self.grid: + self.ax.grid() + + self.ax.xscale("log") + self.ax.xlabel(self.xlabel) + self.ax.ylabel(self.ylabel) + if self.legend: + self.ax.legend() + + def show(self): + self.finish() + pyplot.tight_layout() + show_plot() + + def save(self, file): + self.finish() + pyplot.savefig(file, format=self.format) + + def plot(self, spectrum, t): + self.plot_analytic_solution(self.settings, t) + self.plot_data(self.settings, t, spectrum) + + def plot_analytic_solution(self, settings, t): + def analytic_solution(x): + return settings.norm_factor * settings.kernel.analytic_solution( + x=x, t=t, x_0=settings.X0, N_0=settings.n_part + ) + + if t == 0: + analytic_solution = settings.spectrum.size_distribution + + volume_bins_edges = self.settings.formulae.trivia.volume( + settings.radius_bins_edges + ) + dm = np.diff(volume_bins_edges) + dr = np.diff(settings.radius_bins_edges) + + pdf_m_x = volume_bins_edges[:-1] + dm / 2 + pdf_m_y = analytic_solution(pdf_m_x) + + pdf_r_x = settings.radius_bins_edges[:-1] + dr / 2 + pdf_r_y = pdf_m_y * dm / dr * pdf_r_x + + x = pdf_r_x * si.metres / si.micrometres + y_true = ( + pdf_r_y + * self.settings.formulae.trivia.volume(radius=pdf_r_x) + * settings.rho + / settings.dv + * si.kilograms + / si.grams + ) + + self.ax.plot(x, y_true, color="black") + + def plot_data(self, settings, t, spectrum): + if self.smooth: + scope = self.smooth_scope + if t != 0: + new = np.copy(spectrum) + for _ in range(2): + for i in range(scope, len(spectrum) - scope): + new[i] = np.mean(spectrum[i - scope : i + scope + 1]) + scope = 1 + for i in range(scope, len(spectrum) - scope): + spectrum[i] = np.mean(new[i - scope : i + scope + 1]) + + x = settings.radius_bins_edges[:-scope] + dx = np.diff(x) + self.ax.plot( + (x[:-1] + dx / 2) * si.metres / si.micrometres, + spectrum[:-scope] * si.kilograms / si.grams, + label=f"t = {t}s", + color=self.colors( + t / (self.settings.output_steps[-1] * self.settings.dt) + ), + ) + else: + self.ax.step( + settings.radius_bins_edges[:-1] * si.metres / si.micrometres, + spectrum * si.kilograms / si.grams, + where="post", + label=f"t = {t}s", + color=self.colors( + t / (self.settings.output_steps[-1] * self.settings.dt) + ), + ) diff --git a/PySDM/source/examples/PySDM_examples/Shima_et_al_2009/tutorial_settings.py b/PySDM/source/examples/PySDM_examples/Shima_et_al_2009/tutorial_settings.py new file mode 100644 index 0000000000000000000000000000000000000000..6343e518d65112d12d29e7bc51189b9b46115e91 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Shima_et_al_2009/tutorial_settings.py @@ -0,0 +1,35 @@ +from typing import Optional + +import numpy as np +from pystrict import strict + +from PySDM import Formulae +from PySDM.dynamics.collisions.collision_kernels import Golovin +from PySDM.initialisation import spectra +from PySDM.physics import si + + +@strict +class Settings: + def __init__(self, steps: Optional[list] = None): + steps = steps or [0, 1200, 2400, 3600] + self.formulae = Formulae() + self.n_sd = 2**13 + self.n_part = 2**23 / si.metre**3 + self.X0 = self.formulae.trivia.volume(radius=30.531 * si.micrometres) + self.dv = 1e6 * si.metres**3 + self.norm_factor = self.n_part * self.dv + self.rho = 1000 * si.kilogram / si.metre**3 + self.dt = 1 * si.seconds + self.adaptive = False + self.seed = 44 + self.steps = steps + self.kernel = Golovin(b=1.5e3 / si.second) + self.spectrum = spectra.Exponential(norm_factor=self.norm_factor, scale=self.X0) + self.radius_bins_edges = np.logspace( + np.log10(10 * si.um), np.log10(5e4 * si.um), num=256, endpoint=True + ) + + @property + def output_steps(self): + return [int(step / self.dt) for step in self.steps] diff --git a/PySDM/source/examples/PySDM_examples/Shipway_and_Hill_2012/__init__.py b/PySDM/source/examples/PySDM_examples/Shipway_and_Hill_2012/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..420b694e0fad91481d0a844848da7530649358f4 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Shipway_and_Hill_2012/__init__.py @@ -0,0 +1,11 @@ +# pylint: disable=invalid-name +""" +single-column prescribed-flow constant-temperature example from +[Shipway & Hill 2012](https://doi.org/10.1002/qj.1913) + +fig_1.ipynb: +.. include:: ./fig_1.ipynb.badges.md +""" +from .plot import plot +from .settings import Settings +from .simulation import Simulation diff --git a/PySDM/source/examples/PySDM_examples/Shipway_and_Hill_2012/fig_1.ipynb b/PySDM/source/examples/PySDM_examples/Shipway_and_Hill_2012/fig_1.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..864e0d7b3b4b11a0258fb4a09b2c501c1aaa3387 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Shipway_and_Hill_2012/fig_1.ipynb @@ -0,0 +1,346 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Shipway_and_Hill_2012/fig_1.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Shipway_and_Hill_2012/fig_1.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Shipway_and_Hill_2012/fig_1.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "#### based on Fig. 1 from Shipway & Hill 2012 (Q. J. Royal Meteo. Soc. 138) \"_Diagnosis of systematic differences between multiple parametrizations of warm rain microphysics using a kinematic framework_\" \n", + "https://doi.org/10.1002/qj.1913\n", + "\n", + "**NOTES**: \n", + "- constant momentum profile rather than constant velocity profile is used herein\n", + "- enabling precipitation interpretted as turning on sedimentation and collisions\n", + "- pressure at z=0 not given in the paper, assumed (see settings.py)\n", + "- domain extended below z=0 to mimic particle inflow" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2023-12-29T15:12:04.969183Z", + "start_time": "2023-12-29T15:12:03.369050Z" + }, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "from PySDM_examples.Shipway_and_Hill_2012 import Settings, Simulation, plot\n", + "from PySDM.physics import si\n", + "from PySDM.exporters import NetCDFExporter_1d" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2023-12-29T15:13:57.683736Z", + "start_time": "2023-12-29T15:12:04.972230Z" + }, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "common_params = {\n", + " \"n_sd_per_gridbox\": 256,\n", + " \"dt\": 5 * si.s,\n", + " \"dz\": 50 * si.m,\n", + " \"p0\": 990 * si.hPa,\n", + " \"kappa\": .9,\n", + " \"particles_per_volume_STP\": 50 / si.cm**3\n", + "}\n", + "if 'CI' in os.environ:\n", + " common_params[\"t_max\"] = 10 * common_params[\"dt\"]\n", + " common_params[\"n_sd_per_gridbox\"] = 16 \n", + "\n", + "output = {}\n", + "settings = {}\n", + "simulation = {}\n", + "for rho_times_w in (\n", + " 2 * si.kg/si.m**3 * si.m/si.s,\n", + " 3 * si.kg/si.m**3 * si.m/si.s\n", + "):\n", + " for precip in (\n", + " False, \n", + " True,\n", + " ):\n", + " key = f\"rhow={rho_times_w}_p={precip}\"\n", + " settings[key] = Settings(\n", + " **common_params,\n", + " rho_times_w_1=rho_times_w,\n", + " precip=precip\n", + " )\n", + " simulation[key] = Simulation(settings[key])\n", + " output[key] = simulation[key].run().products" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "ExecuteTime": { + "end_time": "2023-12-29T15:14:06.730859Z", + "start_time": "2023-12-29T15:13:57.682941Z" + }, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAosAAAHbCAYAAACjjNB9AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8ekN5oAAAACXBIWXMAAA9hAAAPYQGoP6dpAACDsklEQVR4nO3dd3gU1f4G8Hd2N7spkAKBJEDoEDoICoQWUCAiKqj3gigKClhBEBTBhoiKihSvouhVwEKxUfyBgAiEJqL0Il36TejJpmydOb8/lgzZJBuSkGRmw/t5nsCUMzPfM7PZ+WZmzhxJCCFARERERJQPg9YBEBEREZF+MVkkIiIiIp+YLBIRERGRT0wWiYiIiMgnJotERERE5BOTRSIiIiLyickiEREREfnEZJGIiIiIfGKySEREREQ+MVkkv3XixAlIkoRdu3aV+rYkScKSJUtKfTt6Vpz97Y/7LSkpCZIkITU1tdjreOONNyBJEiRJwowZM24onjfeeAOtWrW6oXXMnTtXjWfUqFE3tC4iuvkwWSTSoa5du+rupB4bG4vk5GQ0a9as0MskJyejV69epRjVjclvP3fo0AHJyckICwu7oXU3bdoUycnJeOKJJ4q8rM1mQ0hICI4ePXpDMWTr378/kpOTER8fXyLrI6Kbi0nrAIio9DidTpjN5hJZl9FoRHR0dJGWKWr5kuJyuRAQEFCsZc1mc4nEbTKZir2e1atXo1atWqhfv/4NxwEAQUFBCAoKKrHPAhHdXHhlkXRNURS8//77qF+/PiwWC2rWrIm3337bZ/n169ejbdu2sFgsiImJwbhx4+B2u9X5tWvXznNbsFWrVnjjjTfU8SNHjqBLly4IDAxEkyZNsHr16gJjXLZsGcLDwyHLMgBg165dkCQJ48aNU8sMHToUAwcOBABcunQJAwYMQPXq1REcHIzmzZtjwYIFatnBgwdj/fr1+PDDD9VbhydOnAAA7Nu3D7169UKFChUQFRWFRx55BBcvXlSX7dq1K4YPH45Ro0YhMjISiYmJ+cY8ePBg9O3bF++88w6ioqIQHh6ON998E263Gy+++CIqVaqEGjVqYM6cOeoyuW9Dv/nmm6hWrRouXbqklunduze6desGRVEAeN+Gzl5+0aJF6NatG4KDg9GyZUts2bLFK7b//ve/iI2NRXBwMO677z5MmzYN4eHhPvd/9nq/++47JCQkIDAwEPPmzSv2fs7vNvRPP/2Epk2bwmKxoHbt2pg6darPeApy8OBBdOrUSf1s/fbbb/neql+6dCnuvffefNdx7Ngx1K1bF8OHD4cQolj7jIioKJgskq6NHz8e7777Ll577TX8/fffmD9/PqKiovIte/bsWdx111247bbbsHv3bnz66af48ssv8dZbbxV6e4qi4P7774fZbMbWrVsxa9YsvPTSSwUu07lzZ6Snp2Pnzp0APAlrZGQkkpKS1DLr169H165dAQB2ux1t2rTB8uXLsW/fPjzxxBN45JFH8OeffwIAPvzwQ8THx2PYsGFITk5GcnIyYmNjkZqaittvvx233HILtm3bhpUrV+LcuXPo16+fVzxfffUVzGYzNm/ejFmzZvmMe+3atfjf//6HDRs2YNq0aZgwYQLuvvtuREREYOvWrXjqqafw5JNP4syZM/ku/8orr6B27doYOnQoAGDmzJn4/fff8dVXX8Fg8P3V8sorr+CFF17Arl270LBhQwwYMEBN6Ddv3oynnnoKI0eOxK5du9CjR48C/zjIady4cRg5ciQOHDiAxMTEYu/n3LZv345+/frhwQcfxN69e/HGG2/gtddew9y5cwsVVzZZltG3b18EBwdj69at+Pzzz/HKK6/kKacoCpYtW4Y+ffrkmbdnzx506tQJDz30ED7++GNIknRD+4yIqFAEkU5ZrVZhsVjEf//733znHz9+XAAQO3fuFEII8fLLL4u4uDihKIpaZubMmaJChQpClmUhhBC1atUS06dP91pPy5YtxYQJE4QQQqxatUqYTCZx9uxZdf6KFSsEALF48WKfsbZu3VpMmTJFCCFE3759xdtvvy3MZrNIT08XZ86cEQDE4cOHfS7fu3dvMWbMGHU8ISFBjBw50qvMpEmTRM+ePb2mnT59WgAQhw4dUpe75ZZbfG4n26BBg0StWrXU/SKEEHFxcaJz587quNvtFiEhIWLBggVCiLz7Wwghjh07JipWrCheeuklERQUJObNm+e1nZz7LXv5L774Qp2/f/9+AUAcOHBACCFE//79Re/evb3W8fDDD4uwsDCfdcle74wZM65b78Ls53Xr1gkA4sqVK0IIIR566CHRo0cPrzIvvviiaNKkic/tTJgwQbRs2dJr2ooVK4TJZBLJycnqtNWrV+f5bG3evFlUrVpVPTbZ69q8ebOI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uTMCAwMRGxuL999/v7Srlq/r1Xfw4MF5jvWdd97pVcZf6jt58mTcdtttqFixIqpWrYq+ffvi0KFDXmVK6rOblJSE1q1bw2KxoH79+pg7d25pV89LYeratWvXPMf2qaee8irjD3X99NNP0aJFC/UlxPHx8VixYoU6v7wc02zXq295Oa4lqTycd6/net/lfk/cRBYuXCjMZrOYPXu22L9/vxg2bJgIDw8X586d0zq0IpkwYYJo2rSpSE5OVn8uXLigzn/qqadEbGysWLNmjdi2bZto37696NChgzrf7XaLZs2aie7du4udO3eKX375RURGRorx48drUR0vv/zyi3jllVfEokWLBACxePFir/nvvvuuCAsLE0uWLBG7d+8W9957r6hTp46w2WxqmTvvvFO0bNlS/PHHH2Ljxo2ifv36YsCAAer8tLQ0ERUVJR5++GGxb98+sWDBAhEUFCQ+++yzsqqm6nr1HTRokLjzzju9jvXly5e9yvhLfRMTE8WcOXPEvn37xK5du8Rdd90latasKTIyMtQyJfHZ/eeff0RwcLAYPXq0+Pvvv8VHH30kjEajWLlypa7qmpCQIIYNG+Z1bNPS0vyurj///LNYvny5OHz4sDh06JB4+eWXRUBAgNi3b58Qovwc08LWt7wc15JSXs6713O973J/d1Mli23bthXPPvusOi7LsqhWrZqYPHmyhlEV3YQJE0TLli3znZeamioCAgLEDz/8oE47cOCAACC2bNkihPB8qA0Gg0hJSVHLfPrppyI0NFQ4HI5Sjb0ocv/CKYoioqOjxZQpU9RpqampwmKxiAULFgghhPj7778FAPHXX3+pZVasWCEkSRJnz54VQgjxySefiIiICK+6vvTSSyIuLq6Ua1QwX8linz59fC7jz/U9f/68ACDWr18vhCi5z+7YsWNF06ZNvbbVv39/kZiYWNpV8il3XYXwJBUjR470uYy/1lUIISIiIsQXX3xRro9pTtn1FaJ8H9fiKC/n3aIoj8niTXMb2ul0Yvv27ejevbs6zWAwoHv37tiyZYuGkRXPkSNHUK1aNdStWxcPP/wwTp06BQDYvn07XC6XVz0bNWqEmjVrqvXcsmULmjdvrr6UFQASExNhtVqxf//+sq1IERw/fhwpKSledQsLC0O7du286hYeHo5bb71VLdO9e3cYDAZs3bpVLdOlSxeYzWa1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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "7bc3584a2ca240a092f709b9c7b25817", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./qc_rhow=2_p=False.pdf
\")…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAosAAAHbCAYAAACjjNB9AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8ekN5oAAAACXBIWXMAAA9hAAAPYQGoP6dpAACL1klEQVR4nOzdd3wU1d4G8Ge2pjcDKRI60juCAWlSInIVxIIoXlDBctULYgMsILwKFhQLihX0KqCowL0gKC2AiChVQLpAABNqerbNzHn/WDJkSRaSsMlMwvP1k4+zs7Mzz5xdMr/MzpkjCSEEiIiIiIhKYNI7ABEREREZF4tFIiIiIvKLxSIRERER+cVikYiIiIj8YrFIRERERH6xWCQiIiIiv1gsEhEREZFfLBaJiIiIyC8Wi0RERETkF4tFqpIOHz4MSZKwbdu2Ct+WJElYuHBhhW/HyMrT3lWx3VJTUyFJErKyssq9jokTJ0KSJEiShOnTp19WnokTJ6JNmzaXtY7Zs2dreUaPHn1Z6yKiKxOLRSKD6dGjh+EO6klJSUhPT0eLFi1K/Zr09HT069evAlNdnpLauXPnzkhPT0dkZORlrbt58+ZIT0/Hgw8+WObXOhwOhIaG4sCBA5eVodDgwYORnp6O5OTkgKyPiK48Fr0DEFHFcLvdsNlsAVmX2WxGfHx8mV5T1uUDxePxwGq1luu1NpstILktFku517N8+XLUqVMHDRs2vOwcABAcHIzg4OCAfRaI6MrDM4tkWKqq4rXXXkPDhg1ht9tRu3ZtvPzyy36XX7NmDTp27Ai73Y6EhASMHTsWsixrz9etW7fY14Jt2rTBxIkTtcf79+9Ht27dEBQUhGbNmmH58uUXzbh48WJERUVBURQAwLZt2yBJEsaOHastM2LECAwdOhQAcObMGQwZMgRXX301QkJC0LJlS8ydO1dbdvjw4VizZg3efvtt7avDw4cPAwB27tyJfv36ISwsDHFxcbj33ntx+vRp7bU9evTAY489htGjRyM2NhYpKSklZh4+fDgGDhyIV155BXFxcYiKisKkSZMgyzKefvppxMTEoFatWpg1a5b2mgu/hp40aRISExNx5swZbZn+/fujZ8+eUFUVgO/X0IWv//7779GzZ0+EhISgdevW2LBhg0+2jz/+GElJSQgJCcGtt96KN998E1FRUX7bv3C9X3/9Nbp3746goCB89dVX5W7nkr6G/u6779C8eXPY7XbUrVsX06ZN85vnYvbs2YPrr79e+2ytWLGixK/qFy1ahFtuuaXEdRw8eBD169fHY489BiFEudqMiKisWCySYY0bNw5Tp07FCy+8gD///BNz5sxBXFxcicseP34cN910E6699lps374dH3zwAT799FP83//9X6m3p6oqBg0aBJvNho0bN2LmzJl49tlnL/qarl27Ijc3F1u3bgXgLVhjY2ORmpqqLbNmzRr06NEDAOB0OtG+fXssWbIEO3fuxIMPPoh7770Xv/32GwDg7bffRnJyMkaOHIn09HSkp6cjKSkJWVlZuOGGG9C2bVts2rQJy5Ytw4kTJ3DnnXf65Pn8889hs9mwfv16zJw502/uVatW4e+//8batWvx5ptvYsKECfjHP/6B6OhobNy4EQ8//DAeeughHDt2rMTXP/fcc6hbty5GjBgBAJgxYwZ++eUXfP755zCZ/P9aee655/DUU09h27ZtuOaaazBkyBCtoF+/fj0efvhhjBo1Ctu2bUOfPn0u+sdBUWPHjsWoUaOwe/dupKSklLudL7R582bceeeduOuuu7Bjxw5MnDgRL7zwAmbPnl2qXIUURcHAgQMREhKCjRs34qOPPsJzzz1XbDlVVbF48WIMGDCg2HN//PEHrr/+etx999147733IEnSZbUZEVGpCSIDysnJEXa7XXz88cclPn/o0CEBQGzdulUIIcT48eNF48aNhaqq2jIzZswQYWFhQlEUIYQQderUEW+99ZbPelq3bi0mTJgghBDixx9/FBaLRRw/flx7funSpQKAWLBggd+s7dq1E6+//roQQoiBAweKl19+WdhsNpGbmyuOHTsmAIh9+/b5fX3//v3Fk08+qT3u3r27GDVqlM8ykydPFn379vWZd/ToUQFA7N27V3td27Zt/W6n0LBhw0SdOnW0dhFCiMaNG4uuXbtqj2VZFqGhoWLu3LlCiOLtLYQQBw8eFOHh4eLZZ58VwcHB4quvvvLZTtF2K3z9J598oj2/a9cuAUDs3r1bCCHE4MGDRf/+/X3Wcc8994jIyEi/+1K43unTp19yv0vTzqtXrxYARGZmphBCiLvvvlv06dPHZ5mnn35aNGvWzO92JkyYIFq3bu0zb+nSpcJisYj09HRt3vLly4t9ttavXy9q1qypvTeF61q/fr2Ijo4Wb7zxhs96y9JmJe0vEVFp8MwiGdLu3bvhcrnQq1evUi+fnJwMSZK0eV26dEFeXp7fs2MlrSMpKQmJiYnavNJ0CujevTtSU1MhhMC6deswaNAgNG3aFD///DPWrFmDxMRENGrUCID3DNPkyZPRsmVLxMTEICwsDD/++CPS0tIuuo3t27dj9erVCAsL036aNGkCwPvVZKH27duXal+bN2/ucwYwLi4OLVu21B6bzWZcddVVOHnypN911K9fH2+88QZeffVV3HLLLbj77rsvud1WrVpp0wkJCQCgbWPv3r3o2LGjz/IXPvanQ4cOPo/L284X2r17N7p06eIzr0uXLti/f7926UFp7N27F0lJST7XMZa0b4sWLcI//vEPn/cmLS0Nffr0wYsvvognn3yy2HrL22ZERKXFDi5kSMHBwQFfp8lk0q7zKuTxeC57vT169MBnn32G7du3w2q1okmTJujRowdSU1ORmZmJ7t27a8u+/vrrePvttzF9+nS0bNkSoaGhGD16NNxu90W3kZeXh5tvvhmvvvpqsecKiy4ACA0NLVXmCzuASJJU4rzC6w/9Wbt2LcxmMw4fPgxZlmGxXPxXStFtFBb2l9pGaVy43+VtZ73997//xdSpU33m1ahRA4mJiZg7dy7uv/9+RERE6JSOiK5UPLNIhtSoUSMEBwdj5cqVpVq+adOm2LBhg08xuH79eoSHh6NWrVoAvAfd9PR07fmcnBwcOnTIZx1Hjx71WebXX3+95LYLr1t86623tMKwsFhMTU3VrlcszDRgwAAMHToUrVu3Rv369bFv3z6f9dlstmJnrdq1a4ddu3ahbt26aNiwoc9PaQvEQPv666/x/fffIzU1FWlpaZg8efJlra9x48b4/ffffeZd+Li0ytvOF2ratCnWr19fbN3XXHMNzGZzqfM0btwYR48exYkTJ7R5F+7b/v37ceTIEfTp08dnfnBwMBYvXoygoCCkpKQgNzfXZ72BajMiIn9YLJIhBQUF4dlnn8UzzzyDL774AgcPHsSvv/6KTz/9tMTl//Wvf+Ho0aN4/PHHsWfPHixatAgTJkzAmDFjtK/0brjhBvznP//BunXrsGPHDgwbNszngN+7d29cc801GDZsGLZv345169aV2AnhQtHR0WjVqhW++uorrTDs1q0btmzZgn379vmcWWzUqBGWL1+OX375Bbt378ZDDz3kU0AA3l7bGzduxOHDh3H69GmoqopHH30UZ8+exZAhQ/D777/j4MGD+PHHH3HfffeV6evQQDl27BgeeeQRvPrqq7j++usxa9YsvPLKK6Uqrv15/PHH8cMPP+DNN9/E/v378eGHH2Lp0qU+lxaUVnnb+UJPPvkkVq5cicmTJ2Pfvn34/PPP8d577+Gpp54qU54+ffqgQYMGGDZsGP744w+sX78ezz//PIDzZ1gXLVqE3r17IyQkpNjrQ0NDsWTJElgsFvTr1w95eXkAAttmRET+sFgkw3rhhRfw5JNP4sUXX0TTpk0xePBgv9fQXX311fjhhx/w22+/oXXr1nj44YfxwAMPaAdkwNu7unv37vjHP/6B/v37Y+DAgWjQoIH2vMlkwoIFC+BwONCxY0eMGDGi1D1Lu3fvDkVRtGIxJiYGzZo1Q3x8PBo3bqwt9/zzz6Ndu3ZISUlBjx49EB8fj4EDB/qs66mnnoLZbEazZs1Qo0YNpKWlITExEevXr4eiKOjbty9atmyJ0aNHIyoq6qK9jyuCEALDhw9Hx44d8dhjjwEAUlJS8Mgjj2Do0KFaIVNWXbp0wcyZM/Hmm2+idevWWLZsGZ544gkEBQWVeV3lbecLtWvXDt988w3mzZuHFi1a4MUXX8SkSZMwfPjwMuUxm81YuHAh8vLycO2112LEiBHaHyKF+3exW+YAQFhYGJYuXQohBPr374/8/PyAthkRkT+SuPAiLiIigxg5ciT27NmDdevW6R2l1CZOnIiFCxdecmjE9evX4/rrr8eBAwcQGRmJhIQEHDt2zO/toUrLX5v16NEDbdq0uewhCInoysMzi0RkGG+88Qa2b9+OAwcO4N1338Xnn3+OYcOG6R2rzHbs2IGwsDC8//772rwFCxZg+fLlOHz4MFasWIEHH3wQXbp0QYMGDXD27Fm8+eab5SoUL9VmX331FcLCwqpUwU1ExsIzi0RkGHfeeSdSU1ORm5uL+vXr4/HHH8fDDz+sd6wyOXv2LM6ePQvA26mqcJzpL774Av/3f/+HtLQ0xMbGonfv3pg2bRquuuqqy9repdosNzdXu14zKioKsbGxl7U9IrrysFgkIiIiIr/4NTQRERER+cVikYiIiIj8YrFIRERERH6xWNTRjBkzULduXQQFBaFTp0747bffLrr8/Pnz0aRJEwQFBaFly5b44YcffJ4XQuDFF19EQkICgoOD0bt3b+zfv7/Ss3788cfo2rUroqOjER0djd69exdbfvjw4ZAkyefnxhtvrPSss2fPLpbjwnvUGaVde/ToUSyrJEno37+/tkxFtOvatWtx8803IzExEZIkYeHChZd8TWpqKtq1awe73Y6GDRti9uzZxZYp6+e/IrJ+//336NOnD2rUqIGIiAgkJyfjxx9/9Flm4sSJxdq0cFzuysyamppa4vufkZHhs5wR2rWkz6EkSWjevLm2TEW1KxEFHotFnXz99dcYM2YMJkyYgC1btqB169ZISUnxe9PpX375BUOGDMEDDzyArVu3YuDAgRg4cCB27typLfPaa6/hnXfewcyZM7Fx40aEhoYiJSUFTqezUrOmpqZiyJAhWL16NTZs2ICkpCT07dsXx48f91nuxhtvRHp6uvYzd+7cy8pZnqwAEBER4ZPjyJEjPs8bpV2///57n5w7d+6E2WzGHXfc4bNcoNs1Pz8frVu3xowZM0q1/KFDh9C/f3/07NkT27Ztw+jRozFixAifIqw871NFZF27di369OmDH374AZs3b0bPnj1x8803Y+vWrT7LNW/e3KdNf/7558vKWZ6shfbu3euTpWbNmtpzRmnXt99+2yfj0aNHERMTU+yzWhHtSkQVQJAuOnbsKB599FHtsaIoIjExUUyZMqXE5e+8807Rv39/n3mdOnUSDz30kBBCCFVVRXx8vHj99de157OysoTdbhdz586t1KwXkmVZhIeHi88//1ybN2zYMDFgwIDLylWSsmadNWuWiIyM9Ls+I7frW2+9JcLDw0VeXp42r6LatRAAsWDBgosu88wzz4jmzZv7zBs8eLBISUnRHl/uvgcqa0maNWsmXnrpJe3xhAkTROvWrQOWqySlybp69WoBQGRmZvpdxqjtumDBAiFJkjh8+LA2rzLalYgCg2cWdeB2u7F582b07t1bm2cymdC7d29s2LChxNds2LDBZ3nAO8Ra4fKHDh1CRkaGzzKRkZHo1KmT33VWVNYLFRQUwOPxICYmxmd+amoqatasicaNG+ORRx7BmTNnyp3zcrLm5eWhTp06SEpKwoABA7Br1y7tOSO366effoq77roLoaGhPvMD3a5ldanPaiD2vaKoqorc3Nxin9X9+/cjMTER9evXxz333FPi0ICVpU2bNkhISECfPn2wfv16bb6R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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "6dd31321b476495188454b7cff03ff11", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./qc_rhow=3_p=False.pdf
\")…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAosAAAHbCAYAAACjjNB9AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8ekN5oAAAACXBIWXMAAA9hAAAPYQGoP6dpAAC8AklEQVR4nOzdd3wU5dbA8d/MbEsChN4DSJGiFLGCBVQQERRs2IWrWNGr144N6xUb6PWqqFfFioo0XyyIShdQERQUUREUkNAhbfs87x+zO9lNsiF9N3C+n090d3bmmTMzG+bJzHPmaEophRBCCCGEECXQkx2AEEIIIYRIXdJZFEIIIYQQCUlnUQghhBBCJCSdRSGEEEIIkZB0FoUQQgghRELSWRRCCCGEEAlJZ1EIIYQQQiQknUUhhBBCCJGQdBaFEEIIIURC0lkUtdbGjRvRNI1Vq1ZV+7o0TWPmzJnVvp5UVpH9XRv32/z589E0jb1791a4jQceeABN09A0jWeeeaZS8TzwwAP06tWrUm1MnjzZjufmm2+uVFtCiIOPdBaFSEH9+/dPuZN6VlYWW7du5fDDDy/zMlu3bmXw4MHVGFXllLSf+/bty9atW8nMzKxU24cddhhbt27l6quvLveyXq+XjIwMfv/990rFEHXBBRewdetW+vTpUyXtCSEOLo5kByCEqD6BQACXy1UlbRmGQfPmzcu1THnnryrBYBCn01mhZV0uV5XE7XA4KtzO3Llzadu2LR07dqx0HABpaWmkpaVV2XdBCHFwkSuLIqWZpskTTzxBx44dcbvdtGnThkcffTTh/AsWLOCYY47B7XbTokUL7rrrLkKhkP15u3btit0W7NWrFw888ID9/rfffuOkk07C4/HQrVs35s6dW2qMs2fPpn79+oTDYQBWrVqFpmncdddd9jyjR4/m0ksvBWDXrl1cdNFFtGrVivT0dLp3786UKVPseUeNGsWCBQt49tln7VuHGzduBGDNmjUMHjyYOnXq0KxZMy677DJ27txpL9u/f39uuOEGbr75Zho3bsygQYNKjHnUqFEMHz6cf//73zRr1oz69evz0EMPEQqFuP3222nYsCGtW7fm9ddft5cpehv6oYceomXLluzatcueZ8iQIZx88smYpgnE34aOLj99+nROPvlk0tPT6dmzJ0uXLo2L7ZVXXiErK4v09HTOPvtsJkyYQP369RPu/2i777//Pv369cPj8fDOO+9UeD+XdBt62rRpHHbYYbjdbtq1a8fTTz+dMJ7S/PLLL5xwwgn2d+uLL74o8Vb9rFmzOOuss0psY/369bRv354bbrgBpVSF9pkQQpSHdBZFShs7dizjx4/nvvvu4+eff+bdd9+lWbNmJc67ZcsWzjjjDI4++mh++OEHXnzxRV599VUeeeSRMq/PNE3OOeccXC4Xy5cvZ9KkSdx5552lLnPiiSeSm5vLypUrAavD2rhxY+bPn2/Ps2DBAvr37w+Az+fjyCOP5OOPP2bNmjVcffXVXHbZZXzzzTcAPPvss/Tp04errrqKrVu3snXrVrKysti7dy+nnHIKRxxxBN999x2fffYZ27ZtY8SIEXHxvPHGG7hcLpYsWcKkSZMSxv3VV1/x999/s3DhQiZMmMC4ceMYOnQoDRo0YPny5Vx77bVcc801bN68ucTl77nnHtq1a8fo0aMBeP755/n6669544030PXE/7Tcc8893HbbbaxatYpDDz2Uiy66yO7QL1myhGuvvZabbrqJVatWMXDgwFL/OIh11113cdNNN7F27VoGDRpU4f1c1IoVKxgxYgQXXnghq1ev5oEHHuC+++5j8uTJZYorKhwOM3z4cNLT01m+fDkvv/wy99xzT7H5TNNk9uzZDBs2rNhnP/74IyeccAIXX3wx//3vf9E0rVL7TAghykQJkaJycnKU2+1Wr7zySomfb9iwQQFq5cqVSiml7r77btW5c2dlmqY9z/PPP6/q1KmjwuGwUkqptm3bqokTJ8a107NnTzVu3DillFJz5sxRDodDbdmyxf78008/VYCaMWNGwlh79+6tnnzySaWUUsOHD1ePPvqocrlcKjc3V23evFkB6tdff024/JAhQ9Stt95qv+/Xr5+66aab4uZ5+OGH1WmnnRY3bdOmTQpQ69ats5c74ogjEq4nauTIkapt27b2flFKqc6dO6sTTzzRfh8KhVRGRoaaMmWKUqr4/lZKqfXr16u6deuqO++8U6Wlpal33nknbj2x+y26/P/+9z/7859++kkBau3atUoppS644AI1ZMiQuDYuueQSlZmZmXBbou0+88wz+93usuznefPmKUDt2bNHKaXUxRdfrAYOHBg3z+233666deuWcD3jxo1TPXv2jJv26aefKofDobZu3WpPmzt3brHv1pIlS1TTpk3tYxNta8mSJapBgwbqqaeeimu3PPuspO0VQoj9kSuLImWtXbsWv9/PqaeeWub5+/Tpg6Zp9rTjjz+evLy8hFfHSmojKyuLli1b2tPKkhTQr18/5s+fj1KKRYsWcc4559C1a1cWL17MggULaNmyJZ06dQKsK0wPP/ww3bt3p2HDhtSpU4c5c+bw119/lbqOH374gXnz5lGnTh37p0uXLoB1azLqyCOPLNO2HnbYYXFXAJs1a0b37t3t94Zh0KhRI7Zv356wjfbt2/PUU0/x+OOPc9ZZZ3HxxRfvd709evSwX7do0QLAXse6des45phj4uYv+j6Ro446Ku59RfdzUWvXruX444+Pm3b88cfz22+/2UMPymLdunVkZWXFjWMsadtmzZrF0KFD447NX3/9xcCBA7n//vu59dZbi7Vb0X0mhBBlIQkuImWlpaVVeZu6rtvjvKKCwWCl2+3fvz+vvfYaP/zwA06nky5dutC/f3/mz5/Pnj176Nevnz3vk08+ybPPPsszzzxD9+7dycjI4OabbyYQCJS6jry8PM4880wef/zxYp9FO10AGRkZZYq5aAKIpmklTouOP0xk4cKFGIbBxo0bCYVCOByl/7MSu45ox35/6yiLottd0f2cbB999BHjx4+Pm9akSRNatmzJlClTuOKKK6hXr16SohNCHIzkyqJIWZ06dSItLY0vv/yyTPN37dqVpUuXxnUGlyxZQt26dWndujVgnXS3bt1qf56Tk8OGDRvi2ti0aVPcPMuWLdvvuqPjFidOnGh3DKOdxfnz59vjFaMxDRs2jEsvvZSePXvSvn17fv3117j2XC5XsatWvXv35qeffqJdu3Z07Ngx7qesHcSq9v777zN9+nTmz5/PX3/9xcMPP1yp9jp37sy3334bN63o+7Kq6H4uqmvXrixZsqRY24ceeiiGYZQ5ns6dO7Np0ya2bdtmTyu6bb/99ht//vknAwcOjJuelpbG7Nmz8Xg8DBo0iNzc3Lh2q2qfCSFESaSzKFKWx+Phzjvv5I477uDNN99k/fr1LFu2jFdffbXE+a+//no2bdrEjTfeyC+//MKsWbMYN24ct9xyi31L75RTTuGtt95i0aJFrF69mpEjR8ad8AcMGMChhx7KyJEj+eGHH1i0aFGJSQhFNWjQgB49evDOO+/YHcOTTjqJ77//nl9//TXuymKnTp2YO3cuX3/9NWvXruWaa66J60CAlbW9fPlyNm7cyM6dOzFNkzFjxrB7924uuugivv32W9avX8+cOXP4xz/+Ua7boVVl8+bNXHfddTz++OOccMIJvP766/z73/8uU+c6kRtvvJFPPvmECRMm8Ntvv/HSSy/x6aefxg0tKKuK7ueibr31Vr788ksefvhhfv31V9544w3++9//ctttt5UrnoEDB9KhQwdGjhzJjz/+yJIlS7j33nuBwiuss2bNYsCAAaSnpxdbPiMjg48//hiHw8HgwYPJy8sDqnafCSFESaSzKFLafffdx6233sr9999P165dueCCCxKOoWvVqhWffPIJ33zzDT179uTaa6/lyiuvtE/IYGVX9+vXj6FDhzJkyBCGDx9Ohw4d7M91XWfGjBl4vV6OOeYYRo8eXebM0n79+hEOh+3OYsOGDenWrRvNmzenc+fO9nz33nsvvXv3ZtCgQfTv35/mzZszfPjwuLZuu+02DMOgW7duNGnShL/++ouWLVuyZMkSwuEwp512Gt27d+fmm2+mfv36pWYfVwelFKNGjeKYY47hhhtuAGDQoEFcd911XHrppXZHpryOP/54Jk2axIQJE+jZsyefffYZ//rXv/B4POVuq6L7uajevXvzwQcf8N5773H44Ydz//3389BDDzFq1KhyxWMYBjNnziQvL4+jjz6a0aNH23+IRLevtEfmANSpU4dPP/0UpRRDhgwhPz+/SveZEEKURFNFB3AJIUQKueqqq/jll19YtGhRskMpswceeICZM2futzTikiVLOOGEE/j999/JzMykRYsWbN68OeHjocoq0T7r378/vXr1qnQJQiHEwUWuLAohUspTTz3FDz/8wO+//85zzz3HG2+8wciRI5MdVrmtXr2aOnXq8MILL9jTZsyYwdy5c9m4cSNffPEFV199NccffzwdOnRg9+7dTJgwoUIdxf3ts3feeYc6derUqg63ECJ1yJVFIURKGTFiBPPnzyc3N5f27dtz4403cu211yY7rHLZvXs3u3fvBqykqmid6TfffJNHHnmEv/76i8aNGzNgwACefvppGjVqVKn17W+f5ebm2uM169evT+PGjSu1PiHEwUU6i0IIIYQQIiG5DS2EEEIIIRKSzqIQQgghhEhIOotCCCGEECIh6Swm2fPPP0+7du3weDwce+yxfPPNN6XOP3XqVLp06YLH46F79+588skncZ8rpbj//vtp0aIFaWlpDBgwgN9++606N6Fc2/DKK69w4okn0qBBAxo0aMCAAQOKzT9q1Cg0TYv7Of3001NmGyZPnlwsvqLPtKvp41Ce+Pv3718sfk3TGDJkiD1PTR+DhQsXcuaZZ9KyZUs0TWPmzJn7XWb+/Pn07t0bt9tNx44dmTx5crF5yvv7VRnl3Ybp06czcOBAmjRpQr169ejTpw9z5syJm+eBBx4odhyi9cCTHf/8+fNL/B5lZ2fHzVeTx0AIUT2ks5hE77//Prfccgvjxo3j+++/p2fPngwaNCjhQ6e//vprLrroIq688kpWrlzJ8OHDGT58OGvWrLHneeKJJ/jPf/7DpEmTWL58ORkZGQwaNAifz5cS2zB//nwuuugi5s2bx9KlS8nKyuK0005jy5YtcfOdfvrpbN261f6ZMmVKtcRfkW0AqFevXlx8f/75Z9znNXkcyhv/9OnT42Jfs2YNhmFw/vnnx81Xk8cgPz+fnj178vzzz5dp/g0bNjBkyBBOPvlkVq1axc0338zo0aPjOlsVOa41uQ0LFy5k4MCBfPLJJ6xYsYKTTz6ZM888k5UrV8bNd9hhh8Udh8WLF1dH+OWOP2rdunVx8TVt2tT+rKaPgRCimiiRNMccc4waM2aM/T4cDquWLVuqxx57rMT5R4wYoYYMGRI37dhjj1XXXHONUkop0zRV8+bN1ZNPPml/vnfvXuV2u9WUKVOqYQvKvw1FhUIhVbduXfXGG2/Y00aOHKmGDRtW1aEmVN5teP3111VmZmbC9mr6OFT2GEycOFHVrVtX5eXl2dNq+hjEAtSMGTNKneeOO+5Qhx12WNy0Cy64QA0aNMh+X9n9Uhll2YaSdOvWTT344IP2+3HjxqmePXtWXWBlVJb4582bpwC1Z8+ehPMk8xgIIaqOXFlMkkAgwIoVKxgwYIA9Tdd1BgwYwNKlS0tcZunSpXHzg1ViLTr/hg0byM7OjpsnMzOTY489NmGbNb0NRRUUFBAMBmnYsGHc9Pnz59O0aVM6d+7Mddddx65du6o09qiKbkNeXh5t27YlKyuLYcOG8dNPP9mf1eRxqIpj8Oqrr3LhhReSkZERN72mjkFF7O93oSr2S00zTZPc3Nxivwu//fYbLVu2pH379lxyySUlliRMpl69etGiRQsGDhzIkiVL7Om18RgIIUomncUk2blzJ+FwuFi1hmbNmhUb8xOVnZ1d6vzR/5enzcqoyDYUdeedd9KyZcu4E8rpp5/Om2++yZdffsnjjz/OggULGDx4MOFwuErjh4ptQ+fOnXnttdeYNWsWb7/9NqZp0rdvXzZv3gzU7HGo7DH45ptvWLNmDaNHj46bXpPHoCIS/S7k5OTg9Xqr5LtZ05566iny8vIYMWKEPe3YY49l8uTJfPbZZ7z44ots2LCBE088kdzc3CRGamnRogWTJk1i2rRpTJs2jaysLPr378/3338PVM2/D0KI1OBIdgDi4DV+/Hjee+895s+fH5cgcuGFF9qvu3fvTo8ePejQoQPz58/n1FNPTUaocfr06UOfPn3s93379qVr16689NJLPPzww0mMrPxeffVVunfvzjHHHBM3PdWPwYHm3Xff5cEHH2TWrFlxY/4GDx5sv+7RowfHHnssbdu25YMPPuDKK69MRqi2zp0707lzZ/t93759Wb9+PRMnTuStt95KYmRCiKomVxaTpHHjxhiGYZfgitq2bRvNmzcvcZnmzZuXOn/0/+VpszIqsg1RTz31FOPHj+fzzz+nR48epc7bvn17GjduzO+//17pmIuqzDZEOZ1OjjjiCDu+mjwOlYk/Pz+f9957r0ydjuo8BhWR6HehXr16pKWlVclxrSnvvfceo0eP5oMPPih2a72o+vXrc+ihh6bMcSjqmGOOsWOrTcdACFE66Swmicvl4sgjj+TLL7+0p5mmyZdffhl31SpWnz594uYHmDt3rj3/IYccQvPmzePmycnJYfny5QnbrOltACtT+OGHH+azzz7jqKOO2u96Nm/ezK5du2jRokWVxB2rotsQKxwOs3r1aju+mjwOlYl/6tSp+P1+Lr300v2upzqPQUXs73ehKo5rTZgyZQr/+Mc/mDJlStyjixLJy8tj/fr1KXMcilq1apUdW205BkKIMkh2hs3B7L333lNut1tNnjxZ/fzzz+rqq69W9evXV9nZ2UoppS677DJ111132fMvWbJEORwO9dRTT6m1a9eqcePGKafTqVavXm3PM378eFW/fn01a9Ys9eOPP6phw4apQw45RHm93pTYhvHjxyuXy6U+/PBDtXXrVvsnNzdXKaVUbm6uuu2229TSpUvVhg0b1BdffKF69+6tOnXqpHw+X0psw4MPPqjmzJmj1q9fr1as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Pp0bbriBBg0acMopp/Dss89y0UUXcfHFF8ddPLvvvvto3749zzzzjD3N+sO19me1l7uzeOmllzJv3jw6dOhA3bp1adiwYdxPeSxcuJAzzzyTli1bomkaM2fOLHX++fPnR/66j//Jzs4u72aIFOdypzFu4izunzALl1ueup/K3J40nvrfhzz1vw9xe+RYCZFKDtbzrK7rnHjiiTz33HNs3ryZ+fPnc91119G0aVP27t3LN998w5o1a2jatCmPPvooF154IZmZmezevZs333yTESNGMGDAAM4++2wuu+wyWrZsid/vZ968eTz55JNMmTKl2GMEg8EgDoeDo48+2p62aNEiOnTowNq1a2t6F1Spct+GnjhxYpVdXs3Pz6dnz55cccUVnHPOOWVebt26dXG9+aZNm1ZJPEIIIcSBRM6zYBgG/fr1o1+/fvznP/9h4cKFvPPOO7z22mts27aNzp07c/fddxMMBvn666957bXXePPNNwmFQhQUFDB9+nSUUvz66698+eWXfPXVV8ybN4+TTz45bj0zZ84kPz8ft7vwEWL9+vUDYPXq1XFjIWubcncWow/CLEl5n704ePBgBg8eXN4QaNq0KfXr1y/3ckIIIcTBRM6z8RwOh539/MADD/DRRx/ZnWin00nPnj354YcfAKuc4CuvvAJYYyMbNWrExo0bmTJlCoZhlHh7OTbZRSnFV199xQcffMDAgQNrYOuqT7lvQ//zn/8scXp+fn6NlQDs1asXLVq0YODAgSxZsqTUef1+Pzk5OXE/IvUFA35efOKfvPjkPwkG/MkOR5TC6y3g/JN7cP7JPfB6C5IdjhCiChyI59nFixczfvx4+31WVhZjxoyx75YWFBQwdOhQfvjhB5o2bcoXX3xBVlYWAN988w29e/fmySef5O6770bX9f2WCtQ0jZNPPpkXX3yRBg0aVN+G1YAKPTqnQYMGPPjgg/a0/Px8Tj/99CoNrCQtWrRg0qRJHHXUUfj9fv73v//Rv39/li9fnrBcz2OPPRYXa20Xffh2KGziDYTZlx+ws5+37/OR7wsSDivCwbBVdisYRtM1Qv4wKIUZDBPIC1jZz6YqUqpMxWWN2q/jq8GhGVrc029LehCupml2FrKma4SCRRqJVSQd1OFxoICfVi22JjutDNfYDGV7KIRRmKVrf66UldkdBofHSTgUjo8/Em84aNqZ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8PCAhYUFAgMDcezYMalDemIff/wxZDKZ6KdLly7C60VFRZg2bRpatWoFa2trvPzyy1UGWU1PT8ewYcOgVCrRunVrzJ49G2VlZU3dlCr++OMPDB8+HG3btoVMJqsyHykRYeHChWjTpg0sLS0REhKCy5cvi+rcv38f48aNg42NDezs7PDGG29Umbz+3Llz6NevHywsLODq6orPPvussZtWrdraGxERUeVYv/DCC6I6htLeqKgoPPPMM2jRogVat26NsLAwpKamiuo01Hs3Pj4efn5+UCgU8PT0RExMTGM3T6QubQ0ODq5ybKdOnSqqYwht/fLLL9GjRw9hoOCgoCDs2bNHeN1YjqlWbe01luPakIzhvCul2s4TTYKaka1bt5JcLqfvv/+eLly4QJMnTyY7OzvKysqSOrQnsmjRIurWrRtlZGQIP3fv3hVenzp1Krm6ulJcXBydOHGCnn32WerTp4/wellZGXXv3p1CQkLo9OnTtHv3bnJwcKDIyEgpmiOye/du+vDDD2n79u0EgGJjY0Wvf/rpp2Rra0s7duygs2fP0t/+9jdq3749FRYWCnVeeOEF8vX1pSNHjlBiYiJ5enrSmDFjhNdzc3PJycmJxo0bR8nJybRlyxaytLSkr7/+uqmaKaitveHh4fTCCy+IjvX9+/dFdQylvaGhoRQdHU3Jycl05swZ+r//+z9yc3Oj/Px8oU5DvHevXr1KSqWSZs6cSX/++SetW7eOTE1Nae/evXrV1gEDBtDkyZNFxzY3N9fg2vrLL7/Qr7/+SpcuXaLU1FSaP38+mZubU3JyMhEZzzGta3uN5bg2FGM570qptvNEU2hWyWJAQABNmzZNeK5Wq6lt27YUFRUlYVRPbtGiReTr61vtazk5OWRubk7//ve/hbKUlBQCQElJSURU/sYzMTGhzMxMoc6XX35JNjY2VFxc3KixP4nKfxQajYacnZ1p5cqVQllOTg4pFArasmULERH9+eefBICOHz8u1NmzZw/JZDK6ffs2ERH985//JHt7e1Fb586dS15eXo3coprpShZHjBihcxlDbu+dO3cIACUkJBBRw71358yZQ926dRNta/To0RQaGtrYTdKpcluJypOK9957T+cyhtpWIiJ7e3vauHGjUR/TirTtJTLu41ofxnLe1RdSJYvN5jJ0SUkJTp48iZCQEKHMxMQEISEhSEpK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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "0be29f2bcaba46829096136aa0a916ba", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./qc_rhow=2_p=True.pdf
\"), …" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAosAAAHbCAYAAACjjNB9AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8ekN5oAAAACXBIWXMAAA9hAAAPYQGoP6dpAADE6ElEQVR4nOzdd3wU5dbA8d/MbEsj9F5UEAQEBBUFVLCBiFyxoSiKV7zoe0XFLvaO2LCLHRt2gSvYEAUVFAUERREbSAsoNXXbzPP+MbuT3WQ3JCFhN3C+n09gd3bmmTOzu5knM8+ZoymlFEIIIYQQQiSgpzoAIYQQQgiRvqSzKIQQQgghkpLOohBCCCGESEo6i0IIIYQQIinpLAohhBBCiKSksyiEEEIIIZKSzqIQQgghhEhKOotCCCGEECIp6SwKIYQQQoikpLMo6qzVq1ejaRpLly6t9XVpmsb06dNrfT3prDr7uy7ut7lz56JpGtu3b692G7fddhuapqFpGg8//PAuxXPbbbdx0EEH7VIbU6ZMceIZN27cLrUlhNj7SGdRiDQ0YMCAtDuot2nThry8PA488MBKL5OXl8fgwYNrMapdk2g/9+3bl7y8PHJzc3ep7a5du5KXl8eYMWOqvGxJSQlZWVn8/vvvuxRD1JlnnkleXh59+vSpkfaEEHsXV6oDEELUnmAwiMfjqZG2DMOgefPmVVqmqvPXlFAohNvtrtayHo+nRuJ2uVzVbmf27Nm0a9eODh067HIcABkZGWRkZNTYZ0EIsXeRM4sirVmWxX333UeHDh3wer20bduWu+++O+n88+bNo3fv3ni9Xlq0aMH1119POBx2Xt9nn33KXRY86KCDuO2225znv/32G0cddRQ+n48uXbowe/bsCmOcOXMm9evXxzRNAJYuXYqmaVx//fXOPBdeeCEjR44EYMuWLYwYMYJWrVqRmZlJt27deP311515zz//fObNm8cjjzziXDpcvXo1AMuXL2fw4MFkZ2fTrFkzzj33XDZv3uwsO2DAAMaOHcu4ceNo3LgxgwYNShjz+eefz7Bhw7jnnnto1qwZ9evX54477iAcDnPNNdfQsGFDWrduzYsvvugsU/Yy9B133EHLli3ZsmWLM8+QIUM4+uijsSwLiL8MHV3+vffe4+ijjyYzM5MePXrw9ddfx8X27LPP0qZNGzIzMznllFN46KGHqF+/ftL9H233zTffpH///vh8Pl577bVq7+dEl6HfffddunbtitfrZZ999uHBBx9MGk9FfvnlF4444gjns/Xpp58mvFQ/Y8YM/vWvfyVs448//mC//fZj7NixKKWqtc+EEKIqpLMo0tr48eO59957ufnmm/n555+ZOnUqzZo1Szjv+vXrOfHEEzn00ENZtmwZTz31FM8//zx33XVXpddnWRannnoqHo+HhQsXMnnyZK677roKlznyyCMpKCjg+++/B+wOa+PGjZk7d64zz7x58xgwYAAAfr+fgw8+mFmzZrF8+XLGjBnDueeey7fffgvAI488Qp8+ffjPf/5DXl4eeXl5tGnThu3bt3PMMcfQs2dPFi1axEcffcSmTZsYPnx4XDwvvfQSHo+H+fPnM3ny5KRxf/bZZ2zYsIEvvviChx56iFtvvZWTTjqJBg0asHDhQi6++GIuuugi1q1bl3D5G2+8kX322YcLL7wQgCeeeIIFCxbw0ksvoevJf7XceOONXH311SxdupSOHTsyYsQIp0M/f/58Lr74Yi6//HKWLl3K8ccfX+EfB7Guv/56Lr/8clasWMGgQYOqvZ/LWrx4McOHD+ess87ixx9/5LbbbuPmm29mypQplYoryjRNhg0bRmZmJgsXLuSZZ57hxhtvLDefZVnMnDmTk08+udxrP/zwA0cccQRnn302jz/+OJqm7dI+E0KISlFCpKn8/Hzl9XrVs88+m/D1VatWKUB9//33SimlbrjhBtWpUydlWZYzzxNPPKGys7OVaZpKKaXatWunJk2aFNdOjx491K233qqUUurjjz9WLpdLrV+/3nn9ww8/VICaNm1a0lh79eql7r//fqWUUsOGDVN333238ng8qqCgQK1bt04B6tdff026/JAhQ9RVV13lPO/fv7+6/PLL4+a588471cCBA+OmrV27VgFq5cqVznI9e/ZMup6oUaNGqXbt2jn7RSmlOnXqpI488kjneTgcVllZWer1119XSpXf30op9ccff6icnBx13XXXqYyMDPXaa6/FrSd2v0WXf+6555zXf/rpJwWoFStWKKWUOvPMM9WQIUPi2jjnnHNUbm5u0m2Jtvvwww/vdLsrs58///xzBaht27YppZQ6++yz1fHHHx83zzXXXKO6dOmSdD233nqr6tGjR9y0Dz/8ULlcLpWXl+dMmz17drnP1vz581XTpk2d9yba1vz581WDBg3UAw88ENduVfZZou0VQoidkTOLIm2tWLGCQCDAscceW+n5+/Tpg6ZpzrR+/fpRWFiY9OxYojbatGlDy5YtnWmVSQro378/c+fORSnFl19+yamnnkrnzp356quvmDdvHi1btmT//fcH7DNMd955J926daNhw4ZkZ2fz8ccfs2bNmgrXsWzZMj7//HOys7OdnwMOOACwL01GHXzwwZXa1q5du8adAWzWrBndunVznhuGQaNGjfj777+TtrHffvvxwAMPMHHiRP71r39x9tln73S93bt3dx63aNECwFnHypUr6d27d9z8ZZ8nc8ghh8Q9r+5+LmvFihX069cvblq/fv347bffnKEHlbFy5UratGkTN44x0bbNmDGDk046Ke69WbNmDccffzy33HILV111Vbl2q7vPhBCiMiTBRaStjIyMGm9T13VnnFdUKBTa5XYHDBjACy+8wLJly3C73RxwwAEMGDCAuXPnsm3bNvr37+/Me//99/PII4/w8MMP061bN7Kyshg3bhzBYLDCdRQWFjJ06FAmTpxY7rVopwsgKyurUjGXTQDRNC3htOj4w2S++OILDMNg9erVhMNhXK6Kf63EriPasd/ZOiqj7HZXdz+n2v/+9z/uvffeuGlNmjShZcuWvP7661xwwQXUq1cvRdEJIfZGcmZRpK3999+fjIwM5syZU6n5O3fuzNdffx3XGZw/fz45OTm0bt0asA+6eXl5zuv5+fmsWrUqro21a9fGzfPNN9/sdN3RcYuTJk1yOobRzuLcuXOd8YrRmE4++WRGjhxJjx492G+//fj111/j2vN4POXOWvXq1YuffvqJffbZhw4dOsT9VLaDWNPefPNN3nvvPebOncuaNWu48847d6m9Tp068d1338VNK/u8sqq7n8vq3Lkz8+fPL9d2x44dMQyj0vF06tSJtWvXsmnTJmda2W377bff+Ouvvzj++OPjpmdkZDBz5kx8Ph+DBg2ioKAgrt2a2mdCCJGIdBZF2vL5fFx33XVce+21vPzyy/zxxx988803PP/88wnn/+9//8vatWu59NJL+eWXX5gxYwa33norV155pXNJ75hjjuGVV17hyy+/5Mcff2TUqFFxB/zjjjuOjh07MmrUKJYtW8aXX36ZMAmhrAYNGtC9e3dee+01p2N41FFHsWTJEn799de4M4v7778/s2fPZsGCBaxYsYKLLroorgMBdtb2woULWb16NZs3b8ayLC655BK2bt3KiBEj+O677/jjjz/4+OOP+fe//12ly6E1Zd26dfzf//0fEydO5IgjjuDFF1/knnvuqVTnOplLL72UDz74gIceeojffvuNp59+mg8//DBuaEFlVXc/l3XVVVcxZ84c7rzzTn799VdeeuklHn/8ca6++uoqxXP88cfTvn17Ro0axQ8//MD8+fO56aabgNIzrDNmzOC4444jMzOz3PJZWVnMmjULl8vF4MGDKSwsBGp2nwkhRCLSWRRp7eabb+aqq67illtuoXPnzpx55plJx9C1atWKDz74gG+//ZYePXpw8cUXM3r0aOeADHZ2df/+/TnppJMYMmQIw4YNo3379s7ruq4zbdo0SkpK6N27NxdeeGGlM0v79++PaZpOZ7Fhw4Z06dKF5s2b06lTJ2e+m266iV69ejFo0CAGDBhA8+bNGTZsWFxbV199NYZh0KVLF5o0acKaNWto2bIl8+fPxzRNBg4cSLdu3Rg3bhz169evMPu4NiilOP/88+nduzdjx44FYNCgQfzf//0fI0eOdDoyVdWvXz8mT57MQw89RI8ePfjoo4+44oor8Pl8VW6ruvu5rF69evHWW2/xxhtvcOCBB3LLLbdwxx13cP7551cpHsMwmD59OoWFhRx66KFceOGFzh8i0e2r6JY5ANnZ2Xz44YcopRgyZAhFRUU1us+EECIRTZUdwCWEEGnkP//5D7/88gtffvllqkOptNtuu43p06fvtDTi/PnzOeKII/j999/Jzc2lRYsWrFu3LuntoSor2T4bMGAABx100C6XIBRC7F3kzKIQIq088MADLFu2jN9//53HHnuMl156iVGjRqU6rCr78ccfyc7O5sknn3SmTZs2jdmzZ7N69Wo+/fRTxowZQ79+/Wjfvj1bt27loYceqlZHcWf77LXXXiM7O7tOdbiFEOlDziwKIdLK8OHDmTt3LgUFBey3335ceumlXHzxxakOq0q2bt3K1q1bATupKlpn+uWXX+auu+5izZo1NG7cmOOOO44HH3yQRo0a7dL6drbPCgoKnPGa9evXp3Hjxru0PiHE3kU6i0IIIYQQIim5DC2EEEIIIZKSzqIQQgghhEhKOotCCCGEECIp6SymmSeeeIJ99tkHn8/HYYcdxrffflvh/G+//TYHHHAAPp+Pbt268cEHH+ymSNNfVfbllClT0DQt7kfuU2f74osvGDp0KC1btkTTNKZPn77TZebOnUuvXr3wer106NCBKVOm1HqcdUFV9+XcuXPLfS41TWPjxo27J+A0NWHCBA499FBycnJo2rQpw4YNY+XKlTtdTn5fClE90llMI2+++SZXXnklt956K0uWLKFHjx4MGjQo6U2oFyxYwIgRIxg9ejTff/89w4YNY9iwYSxfvnw3R55+qrovAerVq0deXp7z89dff+3GiNNXUVERPXr04IknnqjU/KtWrWLIkCEcffTRLF26lHHjxnHhhRfy8ccf13Kk6a+q+zJq5cqVcZ/Npk2b1lKEdcO8efO45JJL+Oabb5g9ezahUIiBAwdSVFSUdBn5fSnELlAibfTu3VtdcsklznPTNFXLli3VhAkTEs4/fPhwNWTIkLhphx12mLroootqNc66oKr78sUXX1S5ubm7Kbq6C1DTpk2rcJ5rr71Wde3aNW7amWeeqQYNGlSLkdU9ldmXn3/+uQLUtm3bdktMddXff/+tADVv3ryk88jvSyGqT84spolgMMjixYs57rjjnGm6rnPcccfx9ddfJ1zm66+/jpsf7JJryebfW1RnXwIUFhbSrl072rRpw8knn8xPP/20O8Ld48jnsuYddNBBtGjRguOPP5758+enOpy0s2PHDsAusZmMfC6FqD7pLKaJzZs3Y5pmueoNzZo1Szo+aePGjVWaf29RnX3ZqVMnXnjhBWbMmMGrr76KZVn07duXdevW7Y6Q9yjJPpf5+fmUlJSkKKq6qUWLFkyePJl3332Xd999lzZt2jBgwACWLFmS6tDShmVZjBs3jn79+nHggQcmnU9+XwpRfa5UByBEOujTpw99+vRxnvft25fOnTvz9NNPc+edd6YwMrE369SpE506dXKe9+3blz/++INJkybxyiuvpDCy9HHJJZewfPlyvvrqq1SHIsQeS84sponGjRtjGIZTkitq06ZNNG/ePOEyzZs3r9L8e4vq7Muy3G43PXv25Pfff6+NEPdoyT6X9erVIyMjI0VR7Tl69+4tn8uIsWPHMnPmTD7//HNat25d4bzy+1KI6pPOYprweDwcfPDBzJkzx5lmWRZz5syJO+MVq0+fPnHzA8yePTvp/HuL6uzLskzT5Mcff6RFixa1FeYeSz6XtWvp0qV7/edSKcXYsWOZNm0an332Gfvuu+9Ol5HPpRC7INUZNqLUG2+8obxer5oyZYr6+eef1ZgxY1T9+vXVxo0blVJKnXvuuer666935p8/f75yuVzqgQceUCtWrFC33nqrcrvd6scff0zVJqSNqu7L22+/XX388cfqjz/+UIsXL1ZnnXWW8vl86qeffkrVJqSNgoIC9f3336vvv/9eAeqhhx5S33//vfrrr7+UUkpdf/316txzz3Xm//PPP1VmZqa65ppr1IoVK9QTTzyhDMNQH330Uao2IW1UdV9OmjRJTZ8+Xf3222/qxx9/VJdffrnSdV19+umnqdqEtPB///d/Kjc3V82dO1fl5eU5P8XFxc488vtSiJojncU089hjj6m2bdsqj8ejevfurb755hvntf79+6tRo0bFzf/WW2+pjh07Ko/Ho7p27apmzZq1myNOX1XZl+PGjXPmbdasmTrxxBPVkiVLUhB1+onevqXsT3T/jRo1SvXv37/cMgcddJDyeDxqv/32Uy+++OJujzsdVXVfTpw4UbVv3175fD7VsGFDNWDAAPXZZ5+lJvg0kmgfAnGfM/l9KUTN0ZRSanefzRRCCCGEEHWDjFkUQgghhBBJSWdRCCGEEEIkJZ1FIYQQQgiRlHQWhRBCCCFEUtJZFEIIIYQQSUlnsQ4JBALcdtttBAKBVIdS58m+rDmyL2uO7MuaI/tSiJojt86pQ/Lz88nNzWXHjh3Uq1cv1eHUabIva47sy5oj+7LmyL4UoubImUUhhBBCCJGUdBaFEEIIIURSrlQHIKouPz8/1SHUedF9KPty18m+rDmyL2tO7L70eDz4fL4URyRE3SVjFuuQHTt20Kp1a4oKC1MdihBC1BnNmzdn1apV0mEUoprkzGIdomkaRYWF/Lb6L3Jy6gGKaFdfQeSxPUGpyKPIDCrJNBQoShspbQen/WTToiuKfT3actw0Z57S2FCxsUfajDy3VHQdKr6NmPVG549tUznbHr8P4p4nWa+K2Sdx8yiwKF1xRXGVrj/JeuNiT7RPyrQRs08UoCy7YRUTnDM9+jxmxys7+AT73Z7uzKPityfyJpRrs/S1+NjKtlkaPDEbEHk92XOrzPyxz8t+yK2yzyn/vOx6qDgOFbue6OvR7bNU3HOlVILXy8em4uZJ0GbkcxX7/qHi9z1W7H5XpeuIe4/jvjz2QyvBMrHzEzPNUqCsiteb4LOHpWI+rxYWCqWsyOYqLGWhsLCc75ZlNxV5w+zvQJnXI8vZz8u3Ya/DXs4OLUEbKtqSPS1IkE83fkYwGJTOohDVJJ3FOqhevXo11lmM7WTVWGex7LQEHaNknTbYWWcxto0ybTrtxO+Dmugsxj5P2lnc2Xqh3PP4eSrYPkjcWSzT2Yh27KKxVauzmKTN+M5i6TJl23SCr6hzWPZ5RZ3Fcp3Dss93Yb2x+yhm+8p3BkufqzLPK+4s7rzNanUWYzt/Wsx6NFU6LfKcmOfx80c/WwqURekG2q+VXnRS0Q0qjRW7nejn1XK6ZzGdxZh/7SVKp0TXa1Xi9dg2StdSup5k64jGH51XCFF9kuAihBBCCCGSks6iEEIIIYRISjqLQgghhBAiKeksCiGEEEKIpKSzKIQQQgghkpLOohBCCCGESEo6i0IIIYQQIinpLAohhBBCiKSksyiEEEIIIZKSzqIQQgghhEhKOotCCCGEECIp6SwKIYQQQoikpLMohBBCCCGScqU6AFF1+fn5KAWgIv+DAmcakccKZ6LzetlpKFCUNlLaTmn7yaZFVxT7erTluGnOPKWxoWJjj7QZeW6p6DpUfBsx643OH9umcrY9fh/EPU+yXhWzT+LmUWBRuuKK4ipdf5L1xsWeaJ+UaSNmnyhAWXbDKiY4Z3r0ecyOV3bwCfa7Pd2ZR8VvT+RNKNdm6WvxsZVtszR4YjYg8nqy51aZ+WOfl/2QW2WfU/552fVQcRwqdj3R16PbZ6m450qpBK+Xj03FzZOgzcjnKvb9Q8Xve6zY/a5K1xH3Hsd9eeyHVoJlYucnZpqlQFkVrzfBZ8/+/EXbtbBQKGVFNldhKQuFheV8tyy7qcgbZn8HyrweWc5+Xr4Nex32cnZoCdpQ0ZbsaWHCCCF2jXQW6xCPx0Pz5s3Zf592qQ5FCCHqjObNm+PxeFIdhhB1lqacPxVFXeD3+wkGg6kOQwgh6gyPx4PP50t1GELUWdJZFEIIIYQQSUmCixBCCCGESEo6i0IIIYQQIinpLAohhBBCiKSksyiEEEIIIZKSzqIQQgghhEhKOotCCCGEECIp6SwKIYQQQoikpLMohBBCCCGSks6iEEIIIYRISjqLQgghhBAiKeksCiGEEEKIpKSzKIQQQgghkpLOohBCCCGESEo6i0IIIYQQIinpLAohhBBCiKSksyiEEEIIIZJKaWfxqaeeonv37tSrV4969erRp08fPvzwwwqXefvttznggAPw+Xx069aNDz74YDdFK4QQQtQdcowVNSWlncXWrVtz7733snjxYhYtWsQxxxzDySefzE8//ZRw/gULFjBixAhGjx7N999/z7Bhwxg2bBjLly/fzZELIYQQ6U2OsaKmaEopleogYjVs2JD777+f0aNHl3vtzDPPpKioiJkzZzrTDj/8cA466CAmT56csL1AIEAgEHCeW5bF1q1badSoEZqm1fwGCCGEEDVAKUVBQQEtW7ZE12vm3E5NH2NBjrN1WaU/YypNhMNh9frrryuPx6N++umnhPO0adNGTZo0KW7aLbfcorp375603VtvvVUB8iM/8iM/8iM/dfJn7dq1aXuMlePsnvGzs8+YixT78ccf6dOnD36/n+zsbKZNm0aXLl0Szrtx40aaNWsWN61Zs2Zs3Lgxafvjx4/nyiuvdJ7v2LGDtm3b8vvqv8ipV69mNkLUuOKiIvZt0xqAVWvXkZmVJfGkaTzpFIsQe5KC/Hw67NOOnJycardR28dYSH6cXbt2LfXq1WP79u3k5+eTnZ1Nw4YNq70toubl5+fTpk2bnX7GUt5Z7NSpE0uXLmXHjh288847jBo1innz5iX9MFeV1+vF6/WWm54TGfAr0pPb7ebIo/oDkFu/PhkZGRJPDMMwnMc59eqRlcIOWrrtGyH2NLtyKbe2j7GQ/DgbTay58cYbefzxxznvvPN46aWXamy9oubs7DOW8s6ix+OhQ4cOABx88MF89913PPLIIzz99NPl5m3evDmbNm2Km7Zp0yaaN2++W2IVu09GRgaffPZZqsNwpFs86UT2jRDpKx2Osa+//joA7733nnQW66i0u8+iZVlxA2Vj9enThzlz5sRNmz17Nn369NkdoQkhhBB1WiqOsdFLz7m5ubvUjkidlJ5ZHD9+PIMHD6Zt27YUFBQwdepU5s6dy8cffwzAeeedR6tWrZgwYQIAl19+Of379+fBBx9kyJAhvPHGGyxatIhnnnkmlZshhBBCpJ10OcZ27tyZ3377jf3333+Xt0mkRkrPLP7999+cd955dOrUiWOPPZbvvvuOjz/+mOOPPx6ANWvWkJeX58zft29fpk6dyjPPPEOPHj145513mD59OgceeGCqNkHUkpKSEg47uBeHHdyLkpKS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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ac7c8b37d0a449dda5e561ad1b2daf5f", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./qc_rhow=3_p=True.pdf
\"), …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "for p in (False, True):\n", + " for rho_times_w in (2, 3):\n", + " plot(var='cloud water mixing ratio', qlabel='cloud water mixing ratio [g/kg]', fname=f'qc_rhow={rho_times_w}_p={p}.pdf',\n", + " output=output[f'rhow={rho_times_w}.0_p={p}'])" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "ExecuteTime": { + "end_time": "2023-12-29T15:14:11.163980Z", + "start_time": "2023-12-29T15:14:06.728728Z" + }, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "data": { + "image/png": "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ePBgCgsLmTlzJhdeeCFz5sxJOptZl5gWLlyIaZocdNBBScNDoVDSfXEej4fu3bvXGmuixPGaNWtGVlYW7du3Txo2b9687c7jxRdf5KCDDkLXdRYvXrzN2bDtLROS1+3SpUtp3bo1hYWFzveHH374TrVlT2376n7++Wdat25N69atnWFdunShQYMG/PzzzzVeKq5JSUkJGzZsSGqPYRj07t0by7KSlvfHH38wZMiQpOnPOOMMWrVqxYwZM5Lusd2ddSaEEHUll6FFxsrOzk76vHLlSk444QS6d+/OO++8w/z583nqqacAOwmiNm63O+mzpmlJB/TqunXrRqNGjZg1a5bTWRw8eDCzZs3im2++IRKJ0L9//92Kqby8HMMwmD9/PgsWLHBeP//8M48//rgznt/v32GHraZ2apq2y+0G+OGHHwgEAgQCAdatW7dLy9zZZeyMPbXtU+1///sfw4YNw+fzJQ0//vjj+fHHH5k7d26KIhNCiCpyZlHsM+bPn49lWTz88MNORvDEiRP3+HI0TWPgwIFMmjSJxYsXM2DAALKysgiFQjz33HP86U9/cjozOxOTx+PBNM2kYT179sQ0TTZu3MjAgQP3eBvqYsuWLZx33nnceuutrFu3jr/+9a989913dc4u79SpE2vWrGHDhg00a9YMsB9jUxd1Xc/Vde7cmTVr1rBmzRrn7OJPP/1EcXHxdu/NrC4/P59mzZrxzTffcOSRRwL2PZrfffdd0rMYJ02aVONjey6//HIOOeQQTjrpJKZMmcKgQYOAPbvOhBBiZ8mZRbHP6NixI5FIhCeffJLly5fz6quvOskPe9rgwYN544036NGjBzk5Oei6zpFHHsl///tf58C+szG1a9eO8vJypk+fTlFRERUVFRx00EH89a9/5ZxzzuHdd99lxYoVzJs3j7Fjx+7RR6zsissuu4zWrVtz22238cgjj2CaZo1JIjtr2LBhdOjQgXPPPZcff/yRL7/80nnW4M6eLY2r63qubujQoXTr1s3pCM+bN49zzjmHQYMG8ac//WmXYrrqqqsYO3YskyZNYunSpVxzzTVs3brVadvGjRv59ttvOeGEE2qd/u677+aEE05g9uzZwJ5dZ0IIsbOksyj2GYceeiiPPPII999/P4cccgj//e9/GTt2bL0sa9CgQZimyeDBg51hgwcP3mbYzsTUv39/Lrvs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OnVyPvfv35/ffvuNRx99lFdffTWFkQkhdpacWUyRgoICDMNgw4YNScM3bNhAYWFhjdMUFhZud/z4v7syz91Vl3bEPfTQQ9x33318/PHHdO/efbvjtm/fnoKCApYtW7bbMddmd9oS53a76dmzpxNnpm2TQCDAhAkTdqqzsTe2ya6q7WckLy8Pv9+/R7bx3jRhwgQuuugiJk6cuM3l9eoaNGjAQQcdlFbbozaHH364E2embRMh9kfSWUwRj8dD7969mT59ujPMsiymT5+edKYqUb9+/ZLGB/jkk0+c8Q844AAKCwuTxiktLeXrr7+udZ67qy7tADtD+K677uKjjz7iT3/60w6Xs3btWjZv3kzz5s33SNw1qWtbEpmmycKFC504M2mbgP1oplAoxFlnnbXD5eyNbbKrdvQzsie28d7yxhtvcP755/PGG28kPcKoNuXl5fz2229ptT1qs2DBAifOTNomQuy3Up1hsz+bMGGC8nq9avz48eqnn35Sl1xyiWrQoIFav369Ukqps88+W/3jH/9wxv/yyy+Vy+VSDz30kPr555/VmDFjlNvtVgsXLnTGue+++1SDBg3UpEmT1I8//qhGjhypDjjgAFVZWZk27bjvvvuUx+NRb7/9tlq3bp3zKisrU0opVVZWpq6//no1d+5ctWLFCvXpp5+qXr16qQMPPFAFg8F6a0dd2nLnnXeqadOmqd9++03Nnz9fnX766crn86nFixcntTfdt0ncgAED1GmnnbbN8FRtk7KyMvX999+r77//XgHqkUceUd9//71atWqVUkqpf/zjH+rss892xl++fLnKyspSN9xwg/r555/VU089pQzDUB999JEzzo7WTTq047///a9yuVzqqaeeSvoZKS4udsb5+9//rmbOnKlWrFihvvzySzV06FBVUFCgNm7cWG/tqEtbHn30UfX++++rX3/9VS1cuFBdc801Std19emnnzrjpGKbCCF2nnQWU+zJJ59Ubdq0UR6PRx1++OHqq6++cr4bNGiQOvfcc5PGnzhxojrooIOUx+NRXbt2VVOmTEn63rIs9c9//lM1a9ZMeb1eNWTIELV06dK0akfbtm0VsM1rzJgxSimlKioq1DHHHKOaNGmi3G63atu2rbr44ov32oFjV9oyevRoZ9xmzZqp448/Xn333XdJ88uEbaKUUkuWLFGA+vjjj7eZV6q2SfyxK9Vf8djPPfdcNWjQoG2m6dGjh/J4PKp9+/bqpZde2ma+21s36dCOQYMGbXd8pexHAjVv3lx5PB7VsmVLddppp6lly5bVazvq0pb7779fdejQQfl8PtWoUSM1ePBgNWPGjG3mu7e3iRBi52lKKbVXTmEKIYQQQoiMI/csCiGEEEKIWklnUQghhBBC1Eo6i0IIIYQQolbSWRRCCCGEELWSzqIQQgghhKiVdBYzSCgU4o477iAUCqU6lN22r7RlX2kH7DttkXakn32pLULsj+TRORmktLSU/Px8SkpKyMvLS3U4u2Vfacu+0g7Yd9oi7Ug/+1JbhNgfyZlFIYQQQghRK+ksCiGEEEKIWrlSHYDYdaWlpakOYbfF25DpbdlX2gH7TlukHekn1W3xeDz4fL6ULFuIfYHcs5hBSkpKaNmqFYHy8lSHIoQQGaOwsJAVK1ZIh1GIOpIzixlE0zQC5eX8unIVubl5gCLe1VcQe28PUCr2LjaCqmUYChRVM6maD878axsWX1Di9/E5Jw1zxqmKDZUYe2yesc+Wii9DJc8jYbnx8RPnqZy2J6+DpM+1LFclrJOkcRRYVC14e3FVLb+W5SbFXtM6qTaPhHWiAGXZM1YJwTnD458TVryyg69hvdvDnXFUcntiG2GbeVZ9lxxb9XlWBU9CA2Lf1/bZqjZ+4ufqO7lV/TPbfq6+HLYfh0pcTvz7ePsslfRZKVXD99vGppLGqWGesf0qcfuhktc9VuJ6V1XLSNrGST889lurhmkSxydhmKVAWdtfbg37HpZK2F8tLBRKWbHmKixlobCwnJ8ty55VbIPZPwPVvo9NZ3/edh72Muzp7NBqmIeKz8keFibMp+tnEA6HpbMoRB1JZzED5eXl7bHOYmIna491FqsPq6FjVFunDXbUWUycR7V5OvNJXgd7orOY+LnWzuKOlgvbfE4eZzvtg5o7i9U6G/GOXTy2OnUWa5lncmexaprq83SC317nsPrn7XUWt+kcVv+8G8tNXEcJ7du2M1j1WVX7vP3O4o7nWafOYmLnT0tYjqaqhsU+k/A5efz4vqVAWVQ10P6u6qKTijeoKlbs+cT3V8vpniV0FhP+b09RNSS+XGsnvk+cR9VSqpZT2zLi8cfHFULUnSS4CCGEEEKIWklnUQghhBBC1Eo6i0IIIYQQolbSWRRCCCGEELWSzqIQQgghhKiVdBaFEEIIIUStpLMohBBCCCFqJZ1FIYQQQghRK+ksCiGEEEKIWklnUQghhBBC1Eo6i0IIIYQQolbSWRRCCCGEELWSzqIQQgghhKiVK9UBiF1XWlqKUgAq9i8ocIYRe69wBjrfVx+GAkXVTKrmUzX/2obFF5T4fXzOScOccapiQyXGHptn7LOl4stQyfNIWG58/MR5Kqftyesg6XMty1UJ6yRpHAUWVQveXlxVy69luUmx17ROqs0jYZ0oQFn2jFVCcM7w+OeEFa/s4GtY7/ZwZxyV3J7YRthmnlXfJcdWfZ5VwZPQgNj3tX22qo2f+Ln6Tm5V/8y2n6svh+3HoRKXE/8+3j5LJX1WStXw/baxqaRxaphnbL9K3H6o5HWPlbjeVdUykrZx0g+P/daqYZrE8UkYZilQ1vaXW8O+Z+9/8flaWCiUsmLNVVjKQmFhOT9blj2r2AazfwaqfR+bzv687TzsZdjT2aHVMA8Vn5M9LEoUIcTukc5iBvF4PBQWFnJgu7apDkUIITJGYWEhHo8n1WEIkbE05fypKDJBMBgkHA6nOgwhhMgYHo8Hn8+X6jCEyFjSWRRCCCGEELWSBBchhBBCCFEr6SwKIYQQQohaSWdRCCGEEELUSjqLQgghhBCiVtJZFEIIIYQQtZLOohBCCCGEqJV0FoUQQgghRK2ksyiEEEIIIWolnUUhhBBCCFEr6SwKIYQQQohaSWdRCCGEEELUSjqLQgghhBCiVtJZFEIIIYQQtZLOohBCCCGEqJV0FoUQQgghRK2ksyiEEEIIIWqV0s7iM888Q/fu3cnLyyMvL49+/frx4Ycfbneat956i4MPPhifz0e3bt2YOnXqXopWCCGEyBxyjBV7Sko7i61ateK+++5j/vz5fPvttxx99NGMHDmSxYsX1zj+nDlzOOOMM7jwwgv5/vvvGTVqFKNGjWLRokV7OXIhhBAivckxVuwpmlJKpTqIRI0aNeLBBx/kwgsv3Oa70047jUAgwOTJk51hffv2pUePHjz77LM1zi8UChEKhZzPlmWxZcsWGjdujKZpe74BQgghxB6glKKsrIwWLVqg63vm3M6ePsaCHGcz2U7vYypNRKNR9cYbbyiPx6MWL15c4zitW7dWjz76aNKw22+/XXXv3r3W+Y4ZM0YB8pKXvOQlL3ll5GvNmjVpe4yV4+y+8drRPuYixRYuXEi/fv0IBoPk5OTw3nvv0aVLlxrHXb9+Pc2aNUsa1qxZM9avX1/r/G+++Wauu+4653NJSQlt2rRh2cpV5Obl7ZlGiD2uIhDggNatAFixZi1Z2dkST5rGk06xCLEvKSstpWO7tuTm5tZ5HvV9jIXaj7Nr1qwhLy+P4uJiSktLycnJoVGjRnVui9jzSktLad269Q73sZR3Fjt16sSCBQsoKSnh7bff5txzz2XWrFm17sy7yuv14vV6txmeG7vhV6Qnt9vNwCMHAZDfoAF+v1/iSWAYhvM+Ny+P7BR20NJt3Qixr9mdS7n1fYyF2o+z8cSaMWPG8Nhjj3HhhRfywgsv7LHlij1nR/tYyjuLHo+Hjh07AtC7d2+++eYbHn/8cZ577rltxi0sLGTDhg1JwzZs2EBhYeFeiVXsPX6/n49nzEh1GI50iyedyLoRIn2lwzH2yy+/TPpXZJ60e86iZVlJN8om6tevH9OnT08a9sknn9CvX7+9EZoQQgiR0VJxjC0uLgbsS54iM6X0zOLNN9/McccdR5s2bSgrK+P1119n5syZTJs2DYBzzjmHli1bMnbsWACuueYaBg0axMMPP8yIESOYMGEC3377LePGjUtlM4QQQoi0ky7HWJfLlfSvyDwpPbO4ceNGzjnnHDp16sSQIUP45ptvmDZtGsOGDQNg9erVrFu3zhm/f//+vP7664wbN45DDz2Ut99+m/fff59DDjkkVU0Q9aSyspI+vXvRp3cvKisrUx0OgUCA1oXNaF3YjEAgkOpw0oqsGyHSU7ocYw888EAAOnTosFvzEamTds9ZrG+lpaXk5+ezYctWSXBJY4FAgIJ8e/sUlZSmNIFD4smcWITYl5SWltKsUUNKSkoy6ngVP87G4x44cCCzZ8+me/fu/PDDD6kOTySovq1qI+eERVry+XxM/vAj571IX36/n/k//Oi8F0KIRKZpJv0rMo90FkVaMgyDIbFLJSK96bpOl65dUx2GECJNrVy5EoA//vgjtYGIOku7bGghhBBC7Dvi9zKnw/3nom7kzKJIS9FolE9iGXvDhg+XLLo0Fg6HeSCWTXnjzTfj8XhSHJEQIp00btzYuTdOZCY5Aou0FAqF+L+RJwF20oR0FtNXJBLhnrv+BcC1118vnUUhRJKWLVuyYsWKbUoJiswhR2CRlnRdp9ef/uS8T7V0i0cIIYTYW6SzKNKS3+/ny6++TnUYjnSLRwghMkVWVhaAPFYrg8kpEiGEEELUm3g29OrVq1MbiKgz6SwKIYQQot7Es6CDwWCKIxF1JZ1FkZYqKys5auBAjho4MC0et1BRUUGnDu3p1KE9FRUVqQ5HCCEyRvxh/VJgIXPJPYsiLVmWxVdz5zjvU00pxepVq5z3Qgghdo6cWcx80lkUacnr9fLmO+8474UQQgiRGtJZFGnJ5XJx0shRqQ5DCCHEborXhE6Hq0SibuSeRSGEEELUm/LyckDK/WUyObMo0pJpmnz5xRcAHDFwIIZhpDgiIYQQdaFpWtK/IvNIZ1GkpWAwyPChQwC73J88zFUIITJTXl4eJSUlzsO5ReaRzqJIS5qm0blLF+d9qqVbPOlE1o0QQuzbpLMo0lJWVhbf/bgw1WE40i2edCLrRgixPU2aNGHNmjU0btw41aGIOpIEFyGEEELUm0AgACAFDTKYdBaFEEIIIUStpLMo0lJlZSUjhh/DiOHHpMXjFioqKujVvRu9uneTv46rkXUjhNieTp06AXDggQemOBJRV3LPokhLlmUxY/p0532qKaX4+aefnPeiiqwbIcT2bNmyBYDNmzenOBJRV9JZFGnJ6/Xy4iuvOO9F+vL5fEz7dLrzXgghxL5FOosiLblcLs4486+pDkPsBMMwOHLw4FSHIYRIU8uXLwdg7dq1KY5E1JXcsyiEEEKIehMv9yf3NGcuObMo0pJpmnz/3XcA9OzVS8r9pbFIJMJ/nn8egAsvvhi3253iiIQQ6SQ/P5/SUqnElcmksyjSUjAYZGC/voCU+0t34XCYa6++CoCzzz1XOotCiCRt27ZlzZo1tGzZMtWhiDqSzqJIS5qm0aZtW+d9qqVbPEIIIcTeIp1FkZaysrJY+tvyVIfhSLd4hBAiU2RlZQGQk5OT4khEXUmCixBCCCHqzerVqwFYs2ZNiiMRdSWdRSGEEELUG6kNnfmksyjSUjAY5JT/+zOn/N+fCQaDqQ6HyspKjujbhyP69kmL8oNCCJEp/H4/IA/tz2Ryz6JIS6ZpMvl//3Pep5plWXz37bfOeyGEEDsn/gd2OvzhL+pGOosiLXk8Hp569lnnvRBCCCFSQzqLIi253W4uuOjiVIchhBBiN8WvxiilUhyJqCu5Z1EIIYQQ9aasrAyQBJdMJmcWRVqyLIslP/8MwMGdO6Pr8neNEEIIkQrSWRRpqbKykt6Hdgek3J8QQmQyqQ2d+aSzKNJWQUFBqkNIkm7xpBNZN0IIse+SzqJIS9nZ2axZvyHVYTjSLZ50IutGCLE9jRs3Zs2aNTRs2DDVoYg6khvBhBBCCFFvQqEQAOFwOMWRiLqSzqIQQggh6k0kEgEgGo2mOBJRV9JZFGkpGAxy3tlncd7ZZ6XFU/8rKys55uijOeboo6XcXzWyboQQ29OlSxcAOnXqlOJIRF3JPYsiLZmmyZtvvAHAU88+l+Jo7Ef5fPH5LOe9qCLrRgixPVu2bAFg8+bNKY5E1FVKzyyOHTuWww47jNzcXJo2bcqoUaNYunTpdqcZP348mqYlvaQ4+b7H4/HwwMOP8MDDj0i5vzTn9Xp5bcIEXpswAa/Xm+pwhBAxcowVe0pKzyzOmjWLK664gsMOO4xoNMott9zCMcccw08//bTd5zHl5eUl7fCapu2NcMVe5Ha7ueqaa1IdhtgJLpeLv5x8SqrDEEJUky7H2NWrVwOwbt263ZqPSJ2UdhY/+uijpM/jx4+nadOmzJ8/nyOPPLLW6TRNo7CwsL7DE0IIITJWuhxji4uLgaqyfyLzpFWCS0lJCQCNGjXa7njl5eW0bduW1q1bM3LkSBYvXlzruKFQiNLS0qSXSH+WZbFq5UpWrVwp98GluWg0yjtvv8U7b78l2Y5CpLH6OMbCjo+zOTk5APj9/t2IXqRS2nQWLcti9OjRHHHEERxyyCG1jtepUydefPFFJk2axGuvvYZlWfTv35+1a9fWOP7YsWPJz893Xq1bt66vJog9qLKykoM7duDgjh0kwzbNhUIhzjr9dM46/XTneWpCiPRSX8dY2PFx9oADDgCQ428G05RSKtVBAFx++eV8+OGHzJ49m1atWu30dJFIhM6dO3PGGWdw1113bfN9KBRKOoCVlpbSunVrNmzZSl5e3h6JXex5gUCANs3tyyCr161PeU3RdIynIN/ef1NdOzudYhFiX1JaWkqzRg0pKSnZ7eNVfR1jofbjbDzugQMHMnv2bLp3784PP/ywW+0Qe1ZpaSn5+fk73MfS4tE5V155JZMnT+bzzz/fpZ0Y7ESInj17smzZshq/93q9kqGZgbKzs9lcmj73t6RbPEIIsbPq8xgLOz7Oxv+AjF+OFpknpZehlVJceeWVvPfee8yYMcM5Vb0rTNNk4cKFNG/evB4iFEIIITJTuhxj45ew//jjjzrPQ6RWSs8sXnHFFbz++utMmjSJ3Nxc1q9fD0B+fr5zI+w555xDy5YtGTt2LAD/+te/6Nu3Lx07dqS4uJgHH3yQVatWcdFFF6WsHUIIIUS6SZdjbDzhRbKhM1dKO4vPPPMMAIMHD04a/tJLL3HeeecB9vOZdL3qBOjWrVu5+OKLWb9+PQ0bNqR3797MmTPHKSck9g2hUIhrr74KgEefeDLltxIEg0HOOOVkAN546215SK0QIu2lyzE2/vtSfm9mrrRJcNlb4jdzSoJLeku3pAmJJzNiEWJfsicTXPam6kkTbdq0Yc2aNTRu3JiioqJUhycSZFSCixDVud1u7vjXXc57IYQQQqSGdBZFWvJ4PNx0yy2pDkMIIcRu2s8uYO6T0uah3EIIIYTY98QTXAKBQIojEXUlZxZFWlJKOfe2FBQU7HYheyGEEELUjZxZFGmpoqKCNs0LadO8kIqKilSHI4QQoo5yc3MBJPktg0lnUQghhBD1JvHRPCIzyWVokZays7OpjJqpDsORbvGkE1k3QojtadCgAWvWrMmox/+IZNLdF0IIIUS9kWzozCedRSGEEELUm/h953L/eeaSzqJIS6FQiOuvu5brr7uWUCiU6nAIBoOcedqpnHnaqQSDwVSHk1Zk3Qghtqdr164AdO7cOcWRiLqSzqJIS9FolKeeeIKnnniCaDSa6nAwTZP33nmH9955B9OU+/MSyboRQmzP1q1bAdiyZUuKIxF1JQkuIi253W5u/MfNznuRvjweD48+8aTzXgghxL5FOosiLXk8Hu68++5UhyF2gtvt5rK//S3VYQgh0tQff/wBwMaNG1MciagruQwthBBCiHoTr8ZVXFyc2kBEnUlnUaQlpRSBQIBAICCPXUhzpmny+cyZfD5zptyzKITYRrxyi8/nS3Ekoq7kMrRISxUVFRTk2w9wLSoplTJRaSwYDDJ86BBAtpUQYlvt2rVj3bp1tGzZMtWhiDqSM4tCCCGEqDeGYST9KzKPnFkUaSkrK4uiklLnfaqlWzxCCJEp4lcbcnJyUhyJqCvpLIq0pGlaWl3OTLd4hBAiU8SzodevX5/iSERdyWVoIYQQQtSbeBZ0SUlJagMRdSadRZGWwuEwY267jTG33UY4HE51OIRCIS6+4HwuvuD8tCg/KIQQmSKeBS3Z0JlLOosiLUUiER64bywP3DeWSCSS6nCIRqO89sorvPbKK2lRflAIITJFvGa81I7PXHLPokhLLpeLK66+2nkvhBBCiNSQo7BIS16vl4ceeTTVYQghhBD7PbkMLYQQQoh6U1pqP3asoqIixZGIupLOohBCCCHqTbxkq5RuzVzSWRRpKRAI4HcZ+F0GgUAg1eEIIYSoo9zcXEAKGmQy6SwKIYQQot7out3V0DQtxZGIupIEF5GWsrKyWL1uvfM+1dItnnQi60YIsT3xM4tS7i9zSWdRpCVN02jSpEmqw3CkWzzpRNaNEGJ73G43II9By2RyGVoIIYQQ9SZe7i+eFS0yj3QWRVoKh8Pcf++93H/vvWlT7m/0VVcy+qorpdxfNbJuhBDb061bNwC6dOmS4khEXWlqP8tlLy0tJT8/nw1btpKXl5fqcEQtAoEABfn29ikqKSU7O1viSdN40ikWIfYlpaWlNGvUkJKSkow6XsWPs/G4Bw4cyOzZs+nevTs//PBDqsMTCapvq9rIDQQiLblcLs6/8ELnvUhfbrebW/95u/NeCCHEvkWOwiIteb1enn5uXKrDEDvB4/Fw25gxqQ5DCJGm1q+3n5ZQVFSU4khEXck9i0IIIYSoN5s2bQJg69atKY5E1JV0FoUQu8WyLH5avJifFi/GsqxUhyOESDM+nw+wr0KIzCSdRZGWAoEAjfNyaZyXK+X+0lxlZSW9D+1O70O7U1lZmepwhBBppm3btgC0aNEixZGIupJ7FkXaqqioSHUIQgghdlP8jKIkwGUu6SyKtOT3+1my7DfnfaqlWzxCCJEp4mVA5bFamUs6iyIt6bpO23btUh2GI93iEUKITLFx40agKtFFZB65Z1EIIYQQ9Wbz5s2AZENnMuksirQUiUR48vHHefLxx4lEIqkOh3A4zM033sjNN96YFuUHhRAiU3i93qR/ReaRy9AiLYXDYW78+3UAXHDRRSm/MToSifDYIw8DcNuYMfIICCGE2EnxmvFSOz5zSWdRpCXDMDjtjDOc90IIIYRIjZRehh47diyHHXYYubm5NG3alFGjRrF06dIdTvfWW29x8MEH4/P56NatG1OnTt0L0Yq9yefzMf7V1xj/6mvOA12FEELsPDnGij0lpZ3FWbNmccUVV/DVV1/xySefEIlEOOaYY7b7EOY5c+ZwxhlncOGFF/L9998zatQoRo0axaJFi/Zi5EIIIUR6S5djbFlZGSDPzs1kmlJKpTqIuE2bNtG0aVNmzZrFkUceWeM4p512GoFAgMmTJzvD+vbtS48ePXj22We3GT8UCiXdJ1FaWkrr1q3ZsGUreXl5e74RYp8UCAQoyLf3l6KS0pQ/Lyyd4kmnWITYl5SWltKsUUNKSkr2yPGqPo6xUPtxNh53fn4+paWl+Hw+qfKUZkpLS8nPz9/hPpZW2dAlJSUANGrUqNZx5s6dy9ChQ5OGDR8+nLlz59Y4/tixY8nPz3derVu33nMBi3oTCARoXdiM1oXNpNyfEELsAfVxjIUdH2fjf0BKQYPMlTadRcuyGD16NEcccQSHHHJIreOtX7+eZs2aJQ1r1qwZ69evr3H8m2++mZKSEue1Zs2aPRq3qD9FRUUUFRWlOgwhhMh49XWMhR0fZ10uO5dW19OmyyF2UdpkQ19xxRUsWrSI2bNn79H5er1eebZTBvL7/cz/4UfnfaqlWzzpRNaNEOmvvo6xsOPjbPzMYrzsn8g8adFZvPLKK5k8eTKff/45rVq12u64hYWFbNiwIWnYhg0bKCwsrM8QxV6m6zpdunZNdRiOdIsnnci6ESK9pfoYG/8jUp5skblSek5YKcWVV17Je++9x4wZMzjggAN2OE2/fv2YPn160rBPPvmEfv361VeYQgghRMZJl2Ns/HaiLVu21HkeIrVSembxiiuu4PXXX2fSpEnk5uY690Tk5+c7f4mcc845tGzZkrFjxwJwzTXXMGjQIB5++GFGjBjBhAkT+Pbbbxk3blzK2iH2vEgkwqsvjwfg7HPPS3kFl3A4zAOxffDGm2+WCi4JZN0IkZ7S5RjbrVs31qxZQ+fOnXe/USIlUvroHE3Tahz+0ksvcd555wEwePBg2rVrx/jx453v33rrLW677TZWrlzJgQceyAMPPMDxxx+/U8uMp4nLo3PSW7o9jkXiyYxYhNiX7O6jc1JxjI3Hnfg4loEDBzJ79my6d+/ODz/8sMvtEPVnZx+dk9IzizvTT505c+Y2w0455RROOeWUeohIpAvDMDjhpJOc9yJ9uVwuLr38cue9ECI9yDFW7Cnym12kJZ/Px1vvvpfqMMRO8Hq9PPbkv1MdhhAiTW3atAmQexYzmTz0SAghhBD1ZuPGjYB0FjOZnFkUQuwWpZST7VhQUFDrfVJCiP1TPEFRblPJXHJmUaSliooKOnVoT6cO7aX4fJqrqKigTfNC2jQvlG0lhNhGvPxf8+bNUxyJqCvp5ou0pJRi9apVznshhBCZKf6YHqmmlrmksyjSks/n44u5XznvUy3d4hFCiEwR7yxKub/MJZ1FkZYMw+BPhx2W6jAc6RaPEEJkinhiiyS4ZC65Z1EIIYQQ9SaeDb158+YURyLqSs4sirQUjUZ5a+KbAJxy6mkpz6ILh8P8+4knALjy6qulpJ0QQuyk+O9L+b2ZuaSzKNJSKBTignPOAeCkkaNS3lmMRCLc+o+bALj08svll54QQuykcDgM2L9HRWaSzqJIS7quc/SQIc57IYQQmU2ebJG5pLMo0pLf72fKtI9THYYQQgix35NTNkIIIYSoN+Xl5QBUVlamOBJRV9JZFEIIIUS9MU0TAMuyUhyJqCvpLIq0VFFRQa/u3ejVvZuUkBNCiAwWfxi3FDTIXHLPokhLSil+/ukn570QQojM5Ha7Abu4gchM0lkUacnn8zHt0+nO+1RLt3jSiawbIcT2xH8vyO+HzCWdRZGWDMPgyMGDUx2GI93iSSeyboQQ25Ofnw9ATk5OiiMRdSX3LAohhBCi3mzYsAGAoqKiFEci6ko6iyItRaNR/jfpff436X2i0WiqwyESifDs00/z7NNPSxWCamTdCCG256CDDgKgQ4cOKY5E1JVchhZpKRQKcdpf/gJAUUlpysv9hcNhrr36KgDOPvdc54ZtIetGCLF98T/45Y/JzCWdRZGWdF2nb7/+znuRvgzD4M+xjr1kOwohqos/ZzH+r8g80lkUacnv9/PZF1+kOgyxE3w+H6+/OTHVYQgh0tTmzZsBKC4uTm0gos7klI0QQggh6k08wSXeaRSZRzqLQgghhKg38dtT5JaizCVbTqSlyspKjujbhyP69pHi82kuEAjgdxn4XQaBQCDV4Qgh0kyrVq0AKCwsTHEkoq7knkWRlizL4rtvv3XeCyGEyEzZ2dmAfS+6yEzSWRRpyev18u6k/znvUy3d4hFCiEwRL/MnncXMtVOdxR9//HGXZ9ylS5eUPxtPZC6Xy8VxI0akOgxHusUjhBCZoqSkBIDS0tIURyLqaqd6cz169EDTNJRSOzVTXdf55ZdfaN++/W4FJ4QQQojMtn79egA2btyY4khEXe30qb+vv/6aJk2a7HA8pRSHHHLIbgUlhGmazJwxA4DBRx+d8oc9RyIRJrz+XwBOP/OvUqVECCF2Uvwqo1xtzFw7teUGDRpEx44dadCgwU7N9Mgjj5R7E8RuCQaDnHDcsYBd7i9+g3SqhMNhLrnwQgD+7+RTpLMohBA7KX5VcmevTor0s1Odxc8++2yXZjp16tQ6BSNEnK7rdD/0UOe9EEKIzCTl/jKfnBMWacnv9/P1/O9SHYYQQgix39vlzqJSirfffpvPPvuMjRs3bvMMvHfffXePBSeEEEKIzBZ/WH8oFEpxJKKudrmzOHr0aJ577jmOOuoomjVrhqZp9RGXEEIIIfYB0Wg06V+ReXa5s/jqq6/y7rvvcvzxx9dHPEIAdrm/kbHnGk6aMkUSpoQQIkP5fD5KS0uloEEG2+XOYn5+vjw/UdQ7y7L44vNZznshhBCZKd5JlEfnZK5d3nJ33HEHd955Jy+++KKc7RH1xuv18tqECc77VEu3eNKJrBshxPZ4PB4AeeRYBtvlzuKpp57KG2+8QdOmTWnXrt02G/+77ySDVew+l8vFX04+JdVhONItnnQi60YIsT0FBQX89ttvNGrUKNWhiDra5c7iueeey/z58znrrLMkwUUIIYQQ2/XHH38AsGHDhhRHIupqlzuLU6ZMYdq0aQwYMKA+4hECsB/e+vVXXwHQp2/flJf7i0ajTHr/PQBGjvqz3HuTQNaNEGJ72rRpw5o1a2jZsmWqQxF1tMu/1Vu3bk1eXl59xCKEIxgMMmTQkUD9lfuzrKrSU7UVoYpXpwpUBDnr9NOdePaFDlG8bbt7cSAUCu1z60YIsefk5OQAck9zJtvlOmoPP/wwN954IytXrqyHcISwaZpGh44d6dCxo9zqkOZ0XWfgkYMYeOQgKc0ohNhGMBgE7Eeiicy0y7/ZzzrrLD777DM6dOhAbm4ujRo1Snrtis8//5wTTzyRFi1aoGka77///nbHnzlzJpqmbfNav379rjZDpLmsrCwWLVnKoiVLycrKSnU4Yjv8fj8fz5jBxzNmyBMShEgz6XCc3bp1KwAlJSV1nodIrV2+XvToo4/usTM9gUCAQw89lAsuuID/+7//2+npli5dmnQpvGnTpnskHiGEEGJfkg7H2S1btgCwdu1a1q1bR/Pmzes8L5Eau9xZPO+882r9bldPMR933HEcd9xxuxoCTZs2pUGDBrs8nRBCCLE/SYfjbKNGjVi7di1KKS6//HLee+89ub0ow+zyZeirr766xuGBQGCvlQDs0aMHzZs3Z9iwYXz55ZfbHTcUClFaWpr0EukvGAzy5xNP4M8nnuDc7yLSUyAQoHVhM1oXNiMQCKQ6HCHEHrAnj7M9e/YE7HvRJ02axMSJE+stblE/drmzOGXKFMaMGZM0LBAIcOyxx9Z7kfDmzZvz7LPP8s477/DOO+/QunVrBg8evN0HgY8dO5b8/Hzn1bp163qNUewZpmny0Ycf8tGHH2KaZr0sQ9c152VZCtNSmKaFaVqEIyahaq+4cMQkHDGJmqqWl+W8ItHkV02qjxdOfFVb1p6kafZLKWpsh1I7/yoqKqKoqAilqPG1pznbK/aqfVvUvF2EEDWrj+Ns/PNhhx0GwPPPP19/DRD1YpcvQ3/88ccMHDiQhg0bMnr0aMrKyhg+fDgul4sPP/ywPmJ0dOrUiU6dOjmf+/fvz2+//cajjz7Kq6++WuM0N998M9ddd53zubS0VDqMGcDj8TDuP/9x3qeax+Ph2Rf+g2WptIhHCCHqQ30eZ9u1a8egQYO4++6793zgol7tcmexQ4cOfPTRRxx11FHous4bb7yB1+tlypQp9fIsvB05/PDDmT17dq3fe71eebZTBnK73Zx97nmpDsPhdrs565xzMeWslBBiP7O7x9lIJALAxIkT6d+/v/zBnYHq9FC07t27M3nyZG655RaysrL48MMPU9JRBFiwYIFkVgkhhBD1ZHePs2VlZc77OXPmMHnyZKLRKKtWrdoT4Ym9YKfOLPbs2bPGzCWv18sff/zBEUcc4Qzb3n0N1ZWXl7Ns2TLn84oVK1iwYAGNGjWiTZs23Hzzzfz++++88sorADz22GMccMABdO3alWAwyAsvvMCMGTP4+OOPd3qZIjOYpsmihQsBOKRbt7Qo9/fRpx9hWYohw46RKiVCiIyQDsfZZs2a4fP5iEajRKNRRo0ahd/vp3Xr1vz000+73UZR/3bqiDdq1Kh6Wfi3337LUUcd5XyO3/Nw7rnnMn78eNatW8fq1aud78PhMH//+9/5/fffycrKonv37nz66adJ8xD7hmAwSN8/9QZqLvcXT1JITJ5IzKMIR01noK5rKAUR08LrTuh0xiY2dM2Z1tDtP4rifxy5XfbJ90AgxMmjRgLw2x9FZGVnE45GULF5RGPzjscTHx4xLRpke535BisjGLGEGkuB29C2iT2RUuDzGE4iRyBoYugagWDEGaesMoKlR1CqKv6qdaKcmXvdRkL7qtqoaeCKxZFYAtG0VNL6NS3lxBlfiqZBOCFxJxQxcUVMJ3GGhPGcdQOYlj2NoWkYuoZhbHuRQym2mQ+ArtnbdEe2TazRnHYkJizVtg8lfhnfD+LzMXRth2USkxOS7O2dOE8F6Akzqb7tqv+Bvq8+aSR5O+1uNlTVPl19Gfvq+tuRdDjO3n777dx+++089thjXHvttZimSXl5OT///DNLly5NukdSpCdNqfrIVUxfpaWl5Ofns2HLVqlxncYqKio45GD7F0hNVVzqu7MYn29VZzFAQb69v1R1Fq1d7ixGTGu3OovhSKyzWBGgXbPGAKxYv5ns7Ow6dxYTJXYWLbVzncVAIEDLgoYArN20lezs7DTtLFa1I7786uNJZ3Hvk85i7UpLS2nWqCElJSUZdbyKH2erx71lyxaaN29OOBx2hnXt2pW5c+eSm5ubilD3e7Vtq+qkkKtIS1lZWSxfvYblq9dIuT8hhMhwwWCQqVOnOs9j7tKlCwCLFy+mT58+Ujc6ze1UZ7FRo0YUFRXt9EzbtGkjN64KIYQQAoAnnniCs88+m19++QWAFi1aOBnUP//8M3/5y1+csoAi/ezUPYvFxcV8+OGH5Ofn79RMN2/eXG8PUhZCCCFEZsnLy6NBgwYUFhby008/0atXL+655x5OOOEENm3axOeffy6P1EljO53See6559ZnHEIkCQaDXHDuOQC8+PIr+Hy+FEckhBCiri677DJOPfVURowYAdjlBAOBAIWFhWzatImjjz6anJwcwL7n+7HHHuOcc86hcePGqQxbxOxUZ9Gy5EHE+7OoaW2T8FEfKkN2uUhL2dnH773zDgAPPP4seXnxxJRYckC1eLSETAhTKfwee9eORC2CscxXpRRlFWFUwvRKKTvhRNnLD0dNLKXwuAxchkYoYuFx6VRUVNU8Lg6EMDU37tg4GmDoBhXBKB63gddt4PMY8QVQHAgTiVoo7LJzkaiF26Vj6BrBsBnLOlbOv5ayE02MWCnCxnk+NM1OhtA0jUjUxJ1ws76KJaLoun0Tf3KihOb8vzIcdTKcNQ0n4aL6ff/xVelx6xi67nyfmCmuafZ0CvAk7BMuQ8cVS1axEjIXTEthWcpJMjD02DiWIhKxMEOmM8/qccR53Aa6BpFYclA8ycVIaEti4ks8vupt1DTNiRHs9VpTnl/ikEhCMpMinrxir8v4PqiUnXwTF0/a0bVq32k1/wxFreQYqv/eTfy2+nqKt0vfySyOquZXH19tMyw+y5oShuqSNGKvx9omrG24ii1v+wuMb6OaDlmmFdsW21lyTftLbexQah4zE5Np9ob8/Hx+/PFHwO4snnXWWSyMPSLN7/c747333ntcd911jBkzhtGjR3PdddfRoEGDVIQsYuRhcSIteTwe7nv4MUzTwp0Glybcbg+33/MQCoXbnfp40onH4+GRx5+wO5hpsK2EEOnp119/paKigqysLA466CAee+wxrrjiChYuXMjq1auxLIvKykoaNmxIjx49WLBgAXfddRdPPvkkf//73xk9erRz9lHsXdJZFGnJ7XZz4SWXJT3DL5Xcbjd/Pf9iFKrWR7Lsr9xuN5f97W9IJUQhxPbEzyJaloVhGAwcOJD//Oc/HH744Xz11Vfk5ubSoUMHjj76aL744gumTZvGmDFjWLx4Mf/85z+ZOnUqX3zxRcqLNOyP5NE5QgghhKh3PXr0oFWrVnzyySfOsEMPPZQhQ4YA9vN1Fy5cyOOPP07jxo15+umnOfPMM7n77rvJy8tj7ty5PPnkk6kKf78mZxZFWrIsi+W//UbEtGh3QPvEm6xSwjRNvpk3F4Wi9+H9MXT50YkzTZMvZ3+BZUH/AQPlr34hRI0OPPBAli1bhtfrJRQKsXTpUqdCzKZNm7jlllsIBALMnj2bNWvWMGPGDGbMmAFAdnY2OTk5/P7776xcuZJ27dqltjH7GTniibRUWVlJ356HAPDr2k143Kl9un8oFOSck08AYP6vv+Nxy30zccFgkGOHDgVg/ZaSbUozCiFEnNfrZdmyZRx33HFs3ryZk046iRdeeIEmTZrw/PPPA3ai0i+//MInn3zCJ598wmeffUZZWRkADz30EA899BAdO3Zk2LBhDBs2jKOOOkoSYOrZLncWjz76aAYNGsSYMWOShm/dupW//OUvzl8BYt+RmDkaqeEeQrt8m3LSJaNJ2b2xbF1iGaG6Ro7PtU1pM6jKZNViJe7iz/XMzfLg9xhOpmIwbBK1LFCK8liN5IpQFL/HRdS0MC3Fus0B3C4dy7JLywVCJlasJnCW197t4+XsKkJR8rLcTmZrfraH0kCYcMQix+9G+VyEE2oJV4ai6K4ogWAlFcEoXreBptkZsqGoSdRU6JqGz63jcRtO9nM8u9fnMagMRcnyufB7jdh6Vfg8Bm6XjitWfjBq2tnDazaWO9mVdha1zpr1m514Fq7Ygt8fxOO2t5Oh6zTMtUsMetwGLl1z3tsZ1vY4hgZoWlLpvsT7MUNRk0jUTMqejk1CYhZooDJKp4M7O9MYkajznaHr6JqdsWwYVWUV4/N0xTOJSfgixlLJ2anhqJl0X6RSCl3XEmKKZWqTMJHzVkvIlFZOFrwGhBOCcvZBkjNv7drZzprCTra3t3NiFnNiucR4ycn4z0DV9KoqG1xVZadDPON92+XriRup2nLiP1dKKSKmVXMqrkou1RhySi+qquU60SVNlrhYJyY9FmO1BG47zlr2qXgWvT1f5XwfV1OGd3VRU23/6Rxa1farGhRbp1pVhr69zhNKXybGX0O7asrSVyo54z8phGqDlVI1ZnLvTJv3RW3atEHTNEpKSnj55Zdp164dd9xxh/O9pml06tSJTp06ceWVVxKJRJg3bx6ffPIJn376KV999RXLli1j2bJlPPPMM+i6zuGHH86wYcMYOnQoffv2lWS7PWyXr+3NnDmTf//734waNYpAoOpxIuFwmFmzZu3R4MT+Kzs7m7UbN7OuaIucqUpzWVlZfD7ve7745nspzSiEqNEbb7zBc889x6RJk/B4PLz//vu88cYbDBo0iGuvvXa707rdbo444gjuuOMOZs6cyZ133klBQQGDBw+mXbt2WJbFV199xV133cWgQYNo3LgxJ510EsuWLdtLrdv31eky9Keffsqll15K3759+eCDD+TeASGEEELUyDRNzj33XAzDoEuXLowcOZIuXbrQpUsXCgoK2LRpE3l5eTt8jibYZ2nffPNNioqKmDlzJmBXh4n3Q1asWEFZWRkffPABffv25ZZbbqnHlu0/6pQ10Lx5c2bNmkW3bt047LDDnA0mhBBCCJHIMAxmzJjBySefzOmnn+4MV0rx17/+lQMPPJAvv/zSucUgEAgQjUZrnJfb7Wb69OmMGTOGIUOGkJWVRWlpKT/++CM//vgjwWAQgG7dunHppZcye/Zstm7dWv+N3Mftcmcx3vP3er28/vrrXHPNNRx77LE8/fTTezw4sf8KhUJcetEFXHLB+YRCoVSHI7ajoqKCIw/vycDDelJRUZHqcIQQaWjAgAG8+uqr3HDDDc6w0tJSOnbsiN/v54orruCiiy4C4Mknn6RBgwbceeedNc6rSZMm3HHHHXz66acUFxfzzTffcOutt+L3+4lEInTt2pXp06eTlZXF0UcfTaNGjejWrRuXX345//3vf1m1alWNVZtE7Xb5MnT1FXzbbbfRuXNnqR29n6hLub/EpJiopdhaHiYSNfG4jViJPQvLUs7N9pGoRUUgwOuvvgLApTfeheH24XbphCImXrfh3FxekO8jErVw6TrBcFUSSrNGWYQjFtk+Fy5Dd24wj0QtLKVi81BO8s7m0iCBYIRwxGLF+jInOceMBgiXh4iYVR3WH34tQrdKaNwsB5ehEzYt8rM8eNwauVluTEsRqIwQNi1CUQuvS6ekIoxp2u2Llx8sr4ig6xpej0FelttO3DEVLkMjy+uyS/yh0Tjfh2lZGLpOJGpiWSYNsqtu3m7a0I/f7ycUMYlELXRN44+iAKZSeF0GUdNC0yBiKlyxO/ijlsLj0nEZGn6vyykTl5vlxmXYSTRetw5ouAy7jJweSwoxlXJu8Af7xvylS3625xu1iEQVRiyJJl4mryqHRCUlScTzogy9KumgKpkDjFhmgqbhlHAEO+FEKTuxyrSUkyhiWlZCskzCPHVFJKqcGHRdIxy1qkoYYo8XiVhOSbj47zo7iUZLSjKJv48olfQ7MTExxW3oCck2dgKKFntvxjJ1dL3q50klrlfNbosr9r0ZK3Fop6Qoe73EVz5VyTQAWCqW8FI1SmJ6jq5raCqeqKE5JSapluCka6DFkoDsRSsnscasITEGTbN/buKl+SApqcZOWFLONo012kmIqa2cYTxppmoxWlJySuI0iQlUVZRT/i9xtTnJKdWzWZypcLZXTXEm5+hVfahK3kmOuWo/SRg3odWWRVJiEJA2RQnqS35+Pl988QWTJ0/mxBNPpLS0FID58+cTCATIy8tzxi0vL2fo0KH079+f+++/H7fbDdhnGhs1asQrr7xCZWUlnTt3Zvr06TRp0oRffvmF9u3bs3TpUhYtWsSiRYt49tlnAWjVqpVTFUbs2C53FlesWEGTJk2Shv3lL3/h4IMP5ttvv91jgYn9m8vl5oobbseyFC6Xu9Z6rnuLYbj489nXgrLfq337d7gQQuw1gwYNwjAMVq5cyerVq3nzzTdZvHhxUl/jq6++4uuvv2b9+vU88sgjzvBHHnmEe++9l82bN3PwwQczY8YMmjVrBsBBBx3EkiVL2LhxI3PmzGH27NnMnj2b+fPns3bt2qQ/9JYvX84VV1zBgAEDOOKII+jXrx9er3fvrYQ0t8udxbZt29Y4vGvXrnTt2nW3AxICwO3xcNZFVxA1FW5P8qNrUsHl9jBs5PmgFC63h0gkktJ4hBBiX5Gbm0uvXr345ptvmDFjBueddx7dunVLGqdnz57897//JRwOO8OUUtxyyy2EQiFatGjBjBkzKCws3Gb+TZs2ZdSoUYwaNQqwb52ZN28eHTp0cMb53//+x0cffcRHH30EQPfu3fnhhx/qobWZSR7KLYQQQoiUGjFiBN988w0vvPAC55133jbfN27cmDPPPBOwO4kzZ86kqKiIgoICfv/9d/Ly8nb60V1ZWVkMHjzY+TxnzpxtHt8Tf86vsEltaJGWLMti44Z1bNqwbvsP4d1b8ZgmK5ctYuWyRVhmas9yCiHEvubiiy/G5XLx5Zdf8v3332933A8++ICjjz6aq666is8++4wWLVqwZMkSTjnllO1e9VFKsWjRIu6++26ee+45Z3jPnj3Jysqia9eu3HrrrcybN0+e8lKNnFkUaSkUrGTkkT0A+GLhKgy3L6XxRCIhHviH/VftU+/NR8ed0niEEGJf0qJFC04++WQmTJjAk08+yYsvvuh8Z5omGzZsoEWLFgAcd9xxHHzwwQwZMoSCggI++OADjjzySD755BP+9re/MW7cOCfByDRN5s6dy/vvv8/777/Pb7/9Bti3zl166aUA+P1+li9f7tzrKLYlnUVR7xIzqN2A3+MH7Ey/eFavSijthlJsKdZxuezd0+sx0F0GG7ZW4nFpZPlcdrajCYtXbsVSiqipKC4J4vG5yPK68HkMLMsuB1iQ68Mw7Kxen8fF1vIQoYiFRlVpQk3T8LkNFIpWBVm4XQZ+r0FZIELUtCgvr6pWdGDrBhhuH39sqaS0PExwayXBjQG7EaUhyPbY700FXgNXvg9PrhdfAx9+j4HL0PF7DRpkeQiE7GeJFZeH8cRKAgKUVth/HesarNlQhstjl0j0ew07AzgcdOLZuKUStw8a5njI8rmIRO0yhYahEaiM4ve6MC0Lv1fH7dIxTTtb1oplElcEo1iWvQ62lIac0mguwx4/nmXscRkYhobbpeM27DKFPo9BZUXVPURWbH1GoopQLAsoPi8NMGLZ5/Hyd7qmMAwd07IzqBUJJdksiCiLeJ5rYnavaSkng1vTNIxq10gM3c6AN1y6k2XsMuLZrTqWpdANDdOqykq2s6RxMp/tUoXxDF+cknmWZRFVCTmtiVmx1UrAJSb+JhbUq6o8WNUou1ShnZlsKYWG5mTDJiRNo6HZpRc1e3pd05ys6fiSrVhGdA1VNTEtlVSqT6MqMz0unhFOLFM6MYvYPghrSSUE4yPEs7CT8n5jmcSGvfLtzPVY1rSGhkrIFtO1+PTKWV/RhPS2+DBnyYlVFGPT2+EkZCJXa3+8DU6StVYV87YZzrH9Tosvd9v1mLSQ2Dqx12+1/cD5lLBCaxEvAVnT9tuXXXXVVUyYMIHXX3+dBx54gIKCAubNm8df//pXCgoKmDNnDpqm4Xa7WbhwoXOMaNiwIRMmTGDkyJG88MILdOzYkZtuuokbb7yR8ePHs2nTJmcZXq+XYcOGMWrUKCzLcp5IIB3F7ZPL0CItZWVns2pTKb+tLyYrS8r9CSHEvq5fv3706tWLUCjEo48+ilKKNm3asGbNGn755RfWrl3rjBvvKMadcMIJ/OMf/wDgH//4B2+88QabNm1i06ZNNGzYkLPPPpt33nmHoqIiPvjgAy688MKkR1eJ7ZM1JYQQQoiUW79+vXOG795776VPnz589913TJ48mVWrVtG6devtTj9v3jyOP/54AM4880x0Xefjjz9mw4YNvPLKK/zf//0fOTk59d6OfZF0FoUQQgiRcmvWrOHDDz9E13X8fj/ffPMNI0eO5OCDD96pTt7//d//8fXXXzsP7H7xxRe55557KCoqqu/Q93nSWRRpKRQKccv1o7ntxmsJS7k/IYTYp1iWxQcffMCECROcYYcffjijR49m0qRJLF++nBtuuIErr7ySVq1aOePMnz+/1lJ9l156KZ06dSISidC7d29ycnKYNWsWPXv2lOzm3aSp/axAYmlpKfn5+WzYsjWplJDYdVGz6sb06nuRqvaFArxuwykXljQOOGXX4gKBAAX59vYpKiklO9u+bzEctVi/pcIp7xaMRNE1jVDEJBQ2Ka2M4DF0tpSHCUVMNA2Kft2MFTYhYkIggv+gxqiohRmxyG6ajcvjwuvWnYSOqGlRGTKJmhaGodll5swQpxx5MADPT/2Rhg1y7cQCQ3OamRMrLRgIRTE0uyxeeWUES0GgMkKoNITu0rAiFpGKCLpLx5PrxYqY6G4DFbXQ3TpGvKydBm63Tq7fTSRqOckObpdOOFjJ2UPth+CP/3gRubk5FJeF8HkNDF3H7zGwlMLt0smOlQ4MhS0ilkVOLAnGLoNoLyrLZ5f8s5Sd9AJQGYpimvY8fB4DTYNwxMK0FD6P4ZRfDFZWMLRXewBm/rCCBrHtZpfu03C77HJ5mmYnuOia5iTYODf+x0qqGbrmlHOL3+BvKXDpmpM4kFjqLZ6oYSmVlMgQtewkkmhCMk88YQOqyuxpmj3viBlPpKnaO62EzA49IUkHJ/klIa8BZ5M5cVYlOiSWobPfWbGMisRx9MSafLFQ48uLv0+cd1x8e+mxdVZTHIk/X3pCcoWeUO4wYTM4caqqVebM20ocSFVijHICTlg4dlJKYonC+Lat3o7EOJxZxBKMklZKwvfxQYll8+L7Wk25ISpx/Gr1BeNxxueRFJdWte8l/aqKr7D4e7Ztl/O52jpNqCIYm1zbpn2lpaW0aVZASUlJRh2v4sfZ7cX99ttvc8opp9C8eXNWrlyJx+OpcbxEP/zwAz169KBv377MnDmzxgorixcvpmfPnkQiEZ544gnGjRvHokWL0HWdu+++m5tuuknuVUywM9sKJBtapCm3282t/7zdeZ9qhsvF6ReNRrHtjdX7O5fLzQVXXG93vFyp31ZCiPSzfv16ioqKOOSQQwA46aST6Nq1K8cffzzBYHCnOosLFy7E7/fTtm3bWkvxde3alZtuuom7776bsWPHMn/+fG6++WZefvllbrnlFmbPns0rr7xC48aN92j79nVy1BNpyePxcNuYMakOw+F2ezjzkmuxFJRVSKm/RG6Ph4uuusE+E7i/PetDCLFDs2fPZuDAgUDVWWuPx8OPP/64S2f5zjrrLIYNG5ZU8u+3337jlltuYcKECc5Vh1tvvZU333yTX3/9leuuu45XXnmFgQMHcuWVVzJ16lR69+7NlClTpETxLpBzsUIIIYSoNyUlJYBdQq+0tBSwq7B0796de+65Z5fm1axZs6Ss6DvuuIOJEyfy/PPPO8N8Ph/jxo1D13V++eUXgsEgF154IV999RUdO3Zk1apVDBgwgC+++GIPtG7/IJ1FkZaUUhQXF1NcXFzrzcx7k2VZrP7tF1Yv/yUtyg+mE8uyWP7rEpb/ukTWjRBiGyNGjGDFihVs2bLFuS/u888/Z/HixaxatSpp3GuuuYZnn32WsrKynZp3r169ALj++utZs2aNM3zw4MFMnjyZadOmkZubC8Chhx7KV199Rb9+/SguLmbYsGG8/fbbe6KJ+zzpLIq0VFFRQfOCxjQvaExFRUWqwyEcCnLlGcO4+oxhhEPBHU+wHwkFKznrxEH89YRBhIKVqQ5HCJGG2rVrl3TJ+aabbuLdd9/lkksucYb9/vvvPPHEE1xxxRVJJwnmz5/P3Llzky4/x1199dX07duXsrIyLrvssqTpjjvuOAoKCpzPb7zxBi6Xi+nTpzNq1ChCoRCnnnoqTz755J5u7j5H7lkUdeaqVmMtEt32rFLYtEBBKGpRVBrEnVBCTgGmaaGAYDhKIBglalpETUV5Wbkzj19/L8bnj5CX7cGyVCxzFbK8LnL8bqKmRZbXhaXAbWj8vjlAozwfLkOjpDxM64JsLEthGBqN8/z8tq4UI5Yh+/uaEtA0ilcXE11XBvl2DWojVi5Q1zXc2W707Kp78QxDo7wyittlZ/uGoxZ5WW6KykJo2BnbobAJgN/noqw4iLIsNA2y8n1OCTmv285YtixFMBglq4G97PJS+1FB3iw3kYjFHxuK0Vw6Lp8LZVooU4HbdOIJBEJoLi8et0FlZRSfz0VZpeVskz+CdklBf7YHr1unqCREbpYb07Rwu3QiUYvIVguv28BtVGUJ+z0Gum6X+iupMLEsO+Pb0DW2locxrdj0oSj5DRqBBhVBE5/fjGUya7gMnXDUznp2GwaeWOZyPMPVZdjrT49lTus6eFyG07Z4+TjTspwMZCuWrRvP8DV0LeFeyarsZU3T8LjsLGqXoSVluFpKxcrl2ZnThnMQU7F90850t8O0h1WGTXtZmkZI2SUS42XxjFicidmyroR1GR+uJdb/o1rWcqy0nvOdRlVGdmJpPeyk2fi60GMZ+fHyctWzwuP7QdViEwrPKRXLildOlnLi8vVYZnps1djrO1YiLzErPX58VrVkK2ua5jwJIZ4JXvVdrD0JGfIJLd02qzk2ghFfnwktqimLOzFTXnMm11A1ZGTr1ZYWz8iOB2vvuwlZ41hJZRzt/yeUoqwWc+IwJwM+NizxSRHxSdLgokq9KSgo4M9//nPSMMMwGDNmDOvXr0/KzL3vvvt4++23uffee7n55psBiEajVFRUkJeXx4svvkiPHj2YOnUqr732GmefffY2y3vxxRe58MIL6dWrFx999BFvv/02V199NU8//TRXX301a9as4b777pNM6VrIWhFpyef38+n3q5j54xp8/qxUhyO2w+/P4q3PfuS9WYvwZ8m2EkLUTWFhIXfccQfPPvts0vCGDRtSUFDAgAEDnGFfffUVDRs25LjjjqNz587ccccdgH0Ze/369dvMu1evXjRp0oTvvvuOI488kpUrV/Lvf/+be++9F4AHH3yQs88+m0hEEhhrIp1FkZY0TcPlduNyu6v+shdCCLHfGTduHBs3buSII45whi1YsADLssiK/YF6/fXX07NnT7Zu3cq4ceO2mUePHj344osvaNWqFUuWLKFTp06cd955/PnPf2ZM7Mkbr7/+Oh9//PHeaVSGkc6iEEIIIdKapmlJl4ivvPJK1q5dy9ixYwH7ebxHHXUUAOXl5TXOo1OnTsyePZtjjjkG0zR55ZVX6Ny5M/fffz8AnTt3pn///vXckswknUWRliLhMM889C+eevBOIjXc1CzSRyhYyfUXncy1F/xFElyEEHtNy5YtOeigg7Asi6lTpzJ58mRg+4Uc2rZty7Rp05g3bx69e/cGIBgMMnDgQGbPnr1TNaj3R5LgInZbJGoRNa2qsl+xhACwbzj3ug1chk6W10UkalIetJMDgmGTHL87lqxgl7UzLTv5wSDMm+Pt+1aOO+MK3F4/LkPHZWh4XAbZPoPSigjBsIkZS0QBCIZNWhdk0zjfRyRq4fPaiSqWgvKKCF8tWk9urhdD1zB0jbbtGlIcCHHgYa1wu3RMS7G5LGSXurPsm/+L1pawdfFGp70bf9uMy3ThzvOR2zIPn8dg44ZyDI+B7jawohZZWW48Lp1w1KJZsxxchoZb11lbFMAbK5VXsrWSaEUEw2PgzvbYiS1KOfMo3xjAne0mqyDLvmHfVHhzPDTK9bJpS4kTT36eD2+spF+Wz0VFMIqu23+JN87zYuV60TWoCMWSbrwG4YiJ120QDJvoukaW10UoYhE1TfKy3ISjFmWVUaxYAkTUtPC4dDaWBO1KLYZOfpYbXdMImjo/zv8KANNUbCyutJNR9OpJBnbCiaZp+DwG2T434ajC73HhdRsYukbUUk4ZSSO23HjySThix69p4HYZ9n6i20koZjSedWA5ySTKjO+DVfuqXpXh4GRX2POwkyB0vSoRJbEUoYp9H43vn/HpYkkudmKGhmlZKM1OqjEtExVLTNFjwxKfWR5PgnAZul0OT09Or4gnfSg7y6KqvGAs88FSsWST+Pyq1wqMzTv+Pp4rYyVlTWhEorFygVQlb8TnFIknzWhVySHKmU/COqWG9UbyZ0PXts3aSPhsqliikBNZcsIKVJV9jKutFKBVfZtTbR3GPse3a3IyTtX0WuJ8NTuVRUPFEnxiY1RvDwkrKbb9lVW1HpPiV1XJSkk0bZ9ObtnTotEoF110EevWrQN2XPVLKcWsWbOYP38+ACeffDKvvvoqPp+P0aNHs2DBAm699VaGDh0qt0HFyJlFkZbcLjfnXHIlp557WVqU1zMMF4OOPI3Bw87EMFIfjxBCCJvH4+H66693Sglur7NomiajR4/mhhtuAOyEmDfffBOfz0dlZSUvv/wys2bN4phjjqFv375MmjRJnh+LdBZFmnJ7PFx3y51c9vfbcbl3XDO0vrlcbk48/jJO/MtVUv9YCCHSzHXXXec8oHvjxo21jnf11VfzxBNPAPDwww/z2GOPOfdC+v1+fvzxR66++mr8fj/z5s1j1KhRHHHEEQQCgfpvRBqTzqIQQgghMl6HDh3QdZ1+/frVOs68efMAaNy4MUcfffQ237du3ZrHH3+clStXMnToUMB+TE+8ZOH+SjqLIi0ppYhEIkQjkbQp97dly3q2FK2TSxJCCJFmIpEIbdq0Ydy4cZx22mm1jjdhwgQOPvhgNm/ezIABA/jggw9qHK9p06Y0bNgQgH/+85+0aNGiXuLOFNJZFGmpsrKCww4s5JjebdMiwzYSCXHvA2dwz23/RyQSSnU4QgghEixatIjzzz+f66+/3hm2du1aXn/99aTxOnTowJw5cxgyZAiBQICRI0fy6KOP1nhSYsKECbzyyivOcxj3Z3Knvtgl8ZJ+8R+reKk1j2442XuuWIpmfNzKsEnEtOzM0KrEQrxuneLykFPyLl6SLGoqysurOmRN8n14fX6ipoWua5imYkNxEMtSNG/kx7JgS3mIYKwc28Lvfsflc2OZFp4cD02b5RA1Fdk+Fwe0zOP3zRX4PQZllVEqw1E0TeOH7/9ANzRCZWE8OR403c5k9Bdk07p9I1q2qapM4s310rBFAcGSIFbEpLg4aE+jaQSLg3hzvZimQhl2luyG9WUopdANnSZNsglFLHQNmjbNJtfvJhC0s47tsnZ2OUPLUphZbixLEQ6E0d0GukunojzMljUlRKJV66esIkJFRQUuvxufx8Dj1qkMmbgMKKuMYOg6llJkeV0opcjxuVAqvu08RKIWZcEoXredDV4cCOOLZSc3zPLEspMNwlFFtk/H0DRMpSgPRglFTIKVVbWyw6ZJflYWSimipsJw2RmnuqbH2qZwG3bWeqDSztq2LAhG7KzsbK8Lv9eIZSXb+5ah29nTdlk6O24zfnbXsvdGTdNQlr1/oBRh086QtzN4NafknmUpJ4NYi2UaxzOZFQrLjJXci2WxOuX4EjJniZcm1HBKV7pd8axWzcmANq142Tcttn2r5q3rmpPJGzWjsYAsJyM7/rMVT7jVY2m0djyqKoNWqyo2l5jonVReLpaJbcTaoqvk7E4jdktwYmZwTRnCicdShcLAXmex/2Jts6qNl1DSroaSd/FtFB9XJXyXWGov3raIqZz5xUv6xT/relUJwsQDW1VpPruNZmLDEv51BltVpQ6rr8vEfYGE7xPLDWqahop9lxhTfFmJi1eq6kkOKnGc+LZIg6sqmaKiooK+ffvSpEkTNE2jpKSEoUOHsnTpUkKhEOeff74zbsOGDfnwww+58sorGTduHNdddx1Lly7lySefxOVyOdtM1/UaSwfuj6SzKNKSz+/ngy9/th+z4vfLYySEEELU6ogjjmDu3LlOBzsvL48TTzyRiooKhgwZss34brebZ599lk6dOnH99dfz3HPPsXz5co466iiWLl3K008/7VSHESm+DP35559z4okn0qJFCzRN4/3339/hNDNnzqRXr154vV46duzI+PHj6z1OsfdpmkZOXj65efnynCshhKij/e0465yN1jQeeOABPv74Y9q0aVPruNdddx3vv/8+2dnZfPLJJ/zzn//k5ZdfZtiwYXJmN0FKO4uBQIBDDz2Up556aqfGX7FiBSNGjOCoo45iwYIFjB49mosuuohp06bVc6RCCCFE5tnfjrPFxcWA/fic008/nSFDhjjDanPsscfSuHFjALp27QrAnDlzOPnkk9myZUt9hpsxUnoZ+rjjjuO4447b6fGfffZZDjjgAB5++GHAruM4e/ZsHn30UYYPH15fYYoUiETCvDbuCTQ0/nrJVRhG6p+1KIQQmWZ/Oc7+/PPPnHrqqQQCAe655x6uvvpqioqKMAyDmTNnMmrUqFqnffHFF1m9ejXNmzdnzpw5PPPMM9xy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+nNKGlKVAJM4bnyR+s4k3OzHLMvinHPOYcmSJdx3333ce++9QHI1FsuyWL9+PUopWrZsCdiZ0scffzwrV65kwYIF5OTkoOs6ixcv5vXXX8fns0uk/u1vf+Pxxx+nRYsWNGjQgKKiIrKzs+2fdcPANE02bNjAM8884yyvc+fOPP30087n999/n8LCQrp27Upubu7eWC31QjqLIi15fT7envIJxeUhPF5fqvNbhBBCpIHKykpcLhdutxtd13nooYd48803ufzyy5kwYQIrV67k6quvJivLvvpz9913M2bMGC6++GLGjRsHgMfjYf78+RQXF7Ny5UoOOeQQAA4//HAnOebQQw/l4YcfxuVysWHDhqQ/shYsWEAwGGTp0qUsWrQo6dWtWzdnPMuyOOusswgEAgC0bduWQw45xDkL2atXL7p27bq3Vt1ukc6iSEuGYfCnPv3YVFxpn8mKWqkOSQghRAqtXLmSwYMHU1FRwbBhw+jUqRO33347I0aMAOx7C0tKSjjppJPo0qULYHfQdF13OmxxL7/8Mg0aNOCAAw5whv3www+sW7eO7OxsJk6c6JxhrOlyts/n49BDD+XQQw9NGp74rNmysjL69+/PokWLWLduHatWrWLVqlVMmTIlaZycnP9v77zjq6jS//+ecnt6AqETerFRVBYUYQVBUVeQVVZdRVdZGyuKutavbVXUVeyrrq6i7mIX8SegAgoWEBFEioD0IgQS0nP7zPn9MfdO7oUECAZSOO/XK6/cO3PmnOeZubnzZGY+55PyG/fM4UcWixKJRCKRSBokfr+fr7/+mt/97nesXr3a9l+eOnUqbdu25fLLL6ddu3YAjB49mkgkgsPhsLf/05/+xMUXX5y0DOAPf/jDPmN9//33AAwaNIiuXbseUryJln/p6el8/vnnlJeX8+mnn/Lyyy8ze/bspPaLFi1iyJAhhzTWkUSqoSUNkmg0yozpHzJn5nSiUengIpFIJEcDe/uEnHLKKZx55pnMnj2bs846i2nTptm3brdt20anTp0YO3YsP//8M//5z39444036NKli729y+Xap1CsibvuugtFUZg5cyZffvnlIccfiUTs91OnTiUjI4MLL7wwqVD0eDwMHDjwkMaoD2SxKGmQhEMhrr/yMu644SrpNyyRSCRNnO3btzNq1Ch69eqVVDCedtpptGvXjkAgAMDIkSNZ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0/nss8+SFN0lJSVcf/31/OEPf+Css876Tceg3otFgPHjxzN+/Phq182bNy/p/ZNPPsmTTz55BKKS1Ce6rvOH80bWdxgSiUTS6GnK51iPx8MLL7zA0KFDufLKK/nuu+/o3bs3b731FmedddZhHTtuGHGki8VEcnJyGDt2LGPHjiUQCCRdWZ05cyZTp05l6tSp9rJFixZx8skn13qcBlEsSiQSiUQiaZp88803LFy4kD59+jBkyJDDMsbo0aPp27cvF198MQsXLuTSSy9lx44dOJ11P9WZEIIHH3yQSCSCruukpqbW+Rj745dffmH9+vVJLjvxn8LCQtauXYuqqvTu3Xufbe+8807mzJlT6zFlsShpkBiGwbdffw3AKQMHNuqZ7yUSieRo5rPPPuPBBx/kb3/722ErFgHy8vKYP38+eXl57Nixg08++YTzzz+/TscQQnDXXXcxaZLlWvXggw/WyS32+NQ61RWAu3fv5rvvvrMfF7jrrrt4//33a+yrqKiInJwcevTowcMPP8zcuXMpKysjNTWV55577pDik8WipEESDAYZPtT6UiksLcPn89VzRBKJRCI5FHr37s3YsWMP6fZnbXE4HFx66aU8+uijTJkypU6LRSEEEydOtKf/eeKJJ5g4cWKN7f1+Pzt27Ki2ANy1axczZ860C8AbbriB9957r8a+CgsLad68OQA9evSgV69etGzZstqfxOL1jjvuSBIwHSqKaOru13tRVlZGeno6u4qKD9sDt5Lfjt/v59TfWQ/qfvPdolrNobW32CWRPeUhdpf42VMaxB8yKPWHiURMurROY0dRACEEbZtZhammKpRUhmmW7qGouJS//PFMohGDGy58AkdhBNw6ojKCKAshKsJoHTMhaqJ0yoQsN2l5maRnetj2U74170BxECXLQ0aHLDLTXJaVna6yZ/0eXBluPFleSjcVozpUWnbOJhA2CPkjpKW7iRomakykkOZ1snN3MX89+wQAHnpiDq6IBmEDb8/muNJcZKW58IeilBb4iQYjuFJduFJc5OX6EALCEZNQ1CBiCNwOjYhhYsbEJkJgCyECYQN/MIq/0I8zzYXTbYliUjwOS/QAlJVXcM815wOCx179GLfHi8+l4XbqKAqkep04YhZ4bqdm27lFTYFhmFQGoximwDBNNNUSocTFJ4mCA8v2DZy6iqYpVAai9nFSFPC4dFRVIRCK4nbqhCMGHpduj6trKuGIga6pMdFElT1c1DAJRwyEgEjMBjAcNe19o2sKpgnhqEE4YmKYlo1fiseBU7dyUxRLwKIqlvAjbmUYDBv2eiGEbQvo0LVY7LH9YZiWBaGm2qKceIwQs5UDSLCc01TFsuWLCSfUBDEHMQu8uO1cXHgRFyhZMVUd671c6Oy4iAlqYi/tgyGwbPTUmDWjFjvpxYUhCWEkdmoJXGLbx88+AiumxNORElO9GEmTIVSJQXTNUtmoe4lXEu3y4qIRM7bzzITPUjyVpPHiQhWqBCiJVofxl6bYd04EzRYOJf/eWyCTOHY85vj+jzeNj1NWVkab5tmUlpY2qvNV/DxbX3GvXr2anj17omkav/766z4ONoeCaZpcf/31vPjiiwDcfffdDBo0KKkAzM/P57///a99J2zMmDG8++67Nfa5a9cuuwC89dZbeeedd+yCr1WrVkkF4Omnn35Y3MwO9ljJK4uSBonX62Xp8hX1HYaN2+Plyf99zs6NRYRX7EZw5KZnaOi43B5efG8upqjyX5ZIJJL6okePHvTr149Fixbxv//9b79X/+IYhkFBQcE+VwDz8/OZPHkyV199NVOmTLHbP/jgg/tY9gE89dRTdnHasmVLfD5fjVcAEy+C/POf/+Sf//znb0/+MCGLRYlEIpFIJE2KK664gkWLFvHSSy9x/fXX73cKtv/7v//jmWeeoaysrNr1s2bNYuPGjWiaxtChQ/nss8/IzMystgBMHOfxxx//TY41DQlZLEokEolEIjlsPPTQQ/zzn//k6quv5tFHHz0iY44ZM4a77rqLVq1aUVFRkVTEbd26lZYtWyZNeVNWVoaiKPbz8ZWVlfZjERs3bsTj8fDGG28wfPhwHA4Hbrf7gDEcycm6DzdNJxNJkyIQCPDHkecB8P5H0w/Lsxq1IRjwc9MlCc8s1ms0DYtQMMA1l59F4jOLEolEEicYDFJaWnpErVszMjL4+uuv6dKlC7quU1lZyYcffsjrr7/OF198wUcffUSfPn2YPXs2y5cvt5/bq6iosPvIy8tj+PDhDBs2jNNPP52MjIwjFn9DQxaLkgaJaZp8EfMzbQh2f0LA9s3r7NeSKoQQbN34i/1aIpFIErnxxhu57LLLSE9PP6Ljdu/ena+//prXX3+dd999N6kQvOqqqygoKEhqn5qayumnn86wYcMYNmwYnTp1qhMXnaaALBYlDRKXy8Wrb7xhv64NiQrouDI6viw71UV2qgvaWsposJSeP/yyG1WBdJ+LYNhgV0mANtk+HJpKeYK6GqD76J6k+Hys+HozuteBJ9tL2B8mvKUUALG5BCW/gpL5WylNc6H4HJDtIXdQB3RNobTQz87iAKpDJSQgpVUalTvLUVSVjA6ZOB0qv64twJvjQ3c7KCsNkpLmIhCI0jzLusLq0KvsuVLbpKGaDjJbpVK4uQR/fjnFO8rROmXiTHXRvH0mphAEwwa/bC5BmALdpZPqc5CZ4qQyZFgKY0XB49JwOjQiURN/MIquqbTM9BDK8eIPGYQiBgIIxVTFEcMkPdXH/c+/hWkIwoaGyxREDEFhYSUuh8bOogCts714XDrhqIHPbdkKOnUVn1vH49JttSpAqT8MhmWr53Sotl1cOGLYSt9I1FIku506mSkughGDUNjAFAKXQyMUNkj3OQmEo7HPgYFpgtOhQsQg3edCCEHUBFNY9oKapuLQVOI3l1JVhUjUxOnQCISiti1gVpoLXbPiqgxGCYQNDNO0VNYo+Ny6vX+cuobHqdsqXCW278JRk6gRxRQCXVUxhcDnduBy6rZCO25hZ+UqbNWvHlNTCyEwTUFUmLaqVlcVojF1sxpTE4ejJKmkBeDQFFupq+sKRkzVbNkUCrudGWsUja23rAJFLBcFTVFAUXCqCoatbLa2MUSVqjeugLdU2yTZy1nKagVVt1TVlmI7HoeCplqqa6tplU45EhVoKkSj1nsrP4EQ2Ip0IQTCrBpfiwekJMRqh2GNjRCIBGV0vD9bOi1ElZK5qqsq+77Y78T8Eza1x7LSU1CEQMSV4wmFiSBZrd2Yyc7OJjs7+4iOWVRUxIknnsimTZuqXV9QUICiKJx00kn21cN+/frVqxtLQ0YWi5IGia7rXHTxJfUdhuQg0DSNY/v0xzBM22daIpFIjjTLly/n+OOPZ/bs2bz++uts27ZtnzbZ2dmMGjWKYcOGMWTIELKysuoh0saHLBYlEolEIpEcNhYtWsQPP/zAcccdx2mnnXZYxigsLGTAgAGsXr2ac889l1DIunPk8/n4/e9/T15eHscddxzjxo2Tt5YPAVksShokhmHw49KlAPTu00fa/TVgotEIsz6cimmaDBwxBvm1IpFIEvnkk09su7/DVSzu3r2byspKzj33XMaMGcMbb7xBSkoKq1evpk2bNodlzKMJ+a0uaZAEg0EG9v8dIO3+GjrRSIRXnrgHgP5nnA/Ur3JdIpE0LI455hj++Mc/0qtXr8M2Rnl5OQClpaU8//zzLF68mNWrV3P55ZfTpk0bLrvsMk4//fTDNn5TRxaLkgaJoii0a9/efn2oxIUtltBFIc1b9fBydqolnCmuCNGnSzOU2FirNu8hJ9XNum0l5Ob4SPM4UFWF3FZtQIAJpHodnDikI+u2llK6sQhnupvWA9sTNQRpQzuRn19ONBQlGoiiqArhPX52fbAKWqaCVyfzmFyiwSiRyjCK10Hzrtns2VpKaXmIaGkQZ7YXIQROp0Z5eYiCPX7S26SzfWsJRtjAnVF1pTU3N5V2LbPYWlCJJ8tDr76tKSgLUphfgaKrFO+uQNVVUjM9aDHxRYrbQUUwwsYtJTh9TrJSXQhNpagijENTyUhx0irHh6YqBMKWfZ7PbVqiB1Owq8SyRnQ5NCJm1fHJzXQjVIU9ZSGcDhW3UyMYNtiYX44Q0DrbS1FZiIxUJ1HDpDwQwaGrpPuclg0ckO51gmLZ1AXDUVRFoTIYxeXQUBUFfyiKMAXNM7wYpqCoPAhYIgaf24GigM+tU1wRwqlb9oLOmHglHLWs+4rKgmiagq6qqCp2u4hhiUVMIYgqCpGogaaqeN06mQ7r8xKKGATDUSJRgUNXcDsdtn2hrioUV4RQVQU3WNZ9VAksTCFwO3XcTmzLO8MUMbFMBF2z7PviNnUO3RIcaapi2/FZz4Yadpu4cCXef0wXgpkgwjBRMA1hi2cihmkJN1TFEt9QJSJRFAWBQLX8Aqts/4jZ3Nl2gSYGVTaBmqpWWeMpCnrCa9MUmCQ0FmIfG724ZSGAZvn4Wcc1wYowLrgRibEkKvDj3QNRIyamURTLnhGlWjvDuDVf3BYxoYFtwRc1BfG1asyi0Y479jueTzycxPxEVZfEt7THitk3ioSN4/0ZZlJEjZY//elP/OlPfzqsY8SLxdTUVFJSUnj//fc56aSTmBubVeP9999n8+bN5OTkHNY4miqyWJQ0SLxeL2s3bKzvMGzcHi9TZy2yVMVSxCGRSCQNirj7StzfuGfPnlx33XU8/vjjAFxzzTWyUPwNqAduIpFIJBKJRNJwKS21pi5LTU0FoLi4mP/85z/2+ilTpuzX0k+yf2SxKJFIJBKJ5LDx2GOP0bp1a/7v//7vsPQvhLALwy5dugCwatUqiouLycnJoXfv3uzZs4cJEybQpk0bJkyYwLp16w5LLE0VWSxKGiTBYJALzh/FBeePIhgM1nc4hIIBrrt4BNdffDahBhCPRCKRNBbKy8vZsWOHffWvrikoKKC0tBSv18vf//53AHJzcwHrXPLNN9/wr3/9i+7du1NeXs4zzzxD165dOfvss/n888+l89RBIItFSYPEMAw++fhjPvn4YwzDqO9wME2Ttat+4peff0II+cyiRCKRHCzXXXcdP/74o13I1TXNmzfnxx9/5KuvvrKnyWnZsiUAFRUVGIbBtddey88//8xnn33G2WefDcDMmTMZPnw4PXv25IUXXkiyA5Qko4ijrKQuKysjPT2dXUXF9oOwkoZHJBLhzdenAHDp2MvrzIKpzB+xXycqo+MUV4QQAjJTLOXr1oJylq7fQ2VFBeNGnADAsg072FlqoKKQ6nXgcmoUlgTZUeQnxeNg96+lZLZIpVubdH4trMQwhWUFp8D65flE/RHYUAwdM8jonI3m0PC6dcrLQ7ZSND3NTXFxgGBxAF9uCi6nRjAQQXVoloJaN/jr2VY8L8/8CUV1kZXupnmGm5WrC3CluUj3OclMcbLh1zLMqIkZtYpub7qbFhkeSv0RMnwOQlGTikCUQCiK06HaKllNVUhxO2jTzIfbYamvTSFiimGFaEyVW7inlHP6W7d+Zny3HtXpwufSMYWgsCyErlnWdE5do7AsaKl8NZXsVBdet47XrRONChy6itel43JqKIpCOGaZF4/FMKwiPRyz+gtFTFsl7NAVTBMihomqKISjBl6XTtQQ9rpAOGq31zU1phy2bPQ8Th0hBB6XTiRqVqlXY7/NmLWeYQprv3gcVkwm+EMRIlEzZs0nSImp5207vpiVoAL2voirva0+hK2qFcIaI267F44atg2frlt9OB0ajlg/igKx3WKpgmNK4bhlnhZTYVfNKFCVmaYmKJ3jiuPYawXFVidbZnxV0l5F2VdJHI+7Srwr7M9RXCltK4bjVnrxzuxNRPK6vSK2jluVyV7cGjC+SaJNYbwroGp/ErcsjNkeJjYiWXUdt09MnIchrlaO95+0PxLeJDoDxmNNHGNfgbO1F6tSj8drna/aNs+mtLS0UZ2v4ufZ+o47LS2N8vJy1q5dS9euXZPWrVu3jueee47XXnvNVlKnp6dz5ZVXMn78eDp06FAfIR9xDvZYySuLkgaJw+HgL1eN4y9XjZNenRKJRCLZhx07dvDoo4/W+KhSq1at7HZ706VLF55++mm2b9/O008/TefOnSktLWXy5Ml06tSJkSNH8t133x3W+BsTsliUSCQSiURy2Fi6dClTpkxh0aJFddrvf//7X26//XaGDRuWtDw/P5+JEyeyadMmwLICrIm0tDRuuOEG1q5dy4wZMxg2bBhCCKZPn87AgQPtq45HO3KeRUmDxDRN1qxeDUD3Hj1QVfl/jUQikTRGpk2bZtv99evXr876jV853LZtm/3YhN/vp0ePHpSUlAAwYMAABg0adMC+VFVlxIgRtGrViu+++46ysjJOP/10UlJS6izexow8A0saJIFAgL4nHE/fE44nEAjUdzgSiUQiOUS6du3KiBEj6NmzZ532O2rUKHw+H5s3b7ZvGXu9Xv785z8zYMAAPvvsM7755huaNWt2UP2tXr2aM844g7KyMk455RQ+/PDD3+Qg1pSQAhdJg6SyspLunToCsGbDxsPiDb0/sUtxRQhiD/lnpriorKykc4c8TAHvfPY9LZtnkuZzUlYZJmqYFJYFqQxGAcvSrqA0yJZ1e3B4HHTtnE0oahA1BM3S3RimYE9ZkD1lIQIlQfzbS9HTXKS1yyA3w0M4arCnOIBpCFLSXLgdmv3we8QwcWgq+buLue683gC89tkKFN1N2B9Gc+qgWHaEUVMQChu0beZD1xSKK8L4Q1HCEZNgaZDcVtbnP9WjE4yY+Fw6wYiB26FRVBGyvyTL/RFUFbJT3aR6dNJTXHhdOo6Y4GJPSSlDTj4egLc+/Z6szDQCoSiGaYk9DNMkGDYIhA10VbFFJKX+MOWBCB6nTotMDy6HRmUwgs/tQNMUUj0OPC7dztupq5gC9JhowjQFEUNQEYggEOiqis+tE46atg2crqkYhiVicTo0ojEBjGGaRKLWa01TCIQM29rQ59Zt+z+HpmKYljVdNCZEiZqCaNREICw7Pl21Y4OYAMcwbau2uJjGNGNx6GqSfZ0WU0ToCXZzplllfWfGrPFMU9jWeVGjSpEftwPc2/Yv/vkVdn9We0VREqzokiUctsBDidsOVlnaJbausq+zPphKQttEDEHMxlEkbRi3P0RR7PwTSYwvvkoI645DIokn8sQY44vjVoaxoaw+4kIYkWDVp1blYdvuJfSd2Me+WSaLa/aOoWpJ1Vr7WOwV+96Ul5XRMier3oUiteVICVyEEPTo0YO1a9cyevRo3n//fQBCoRBOp7NWhd6GDRsYOHAgO3fupG/fvsydO5f09PTDFXqD4WCPlbwNLWmQ+Hw+tuXvqu8wbHw+H//vm1XsLA7g8brrO5wGhdfrY/6ydQgBJeWh+g5HIpEcJSiKQtu2be3nDcPhME6nE5fLVat+tm3bxpAhQ9i5cyfHHnssn3322VFRKNYGeRtaIpFIJBJJgycYDHLzzTezOvY8O8C//vUve92nn35aq/527NjB888/z+DBg9myZQtdunRh9uzZZGdn12ncTQFZLEokEolEIjlsPPnkk3Tp0oV//OMfh9zHzz//zMknn8zkyZN54IEHbAFLly5deOGFF4D9q57jbN68mcmTJ3PKKafQunVrxo8fz8aNG2nfvj1z586lRYsWhxxjU0behpY0SILBINeMuwqAF19+Bbe7fm/9BgIBbrj8fMIRk8kvT4WU2t3maMoEAwGuuuSPADz83Jt4XKn1HJFEImlIFBUVsX79egoKCmq9rRCCf//739x4440Eg0G8Xi+zZs3ivvvu46mnngLg6quvJisriz/+8Y/V9vHLL7/wwQcf8MEHH7BkyZKkdf3792f06NGMHTuWnJycWsd3tCCLRUmDxDAM3nnrLQCef/Gleo7GerB+2eKFsddHlSbsgJjC5IfvvrVey30jkUj24qqrruLMM8+0LfgOlqKiIq644go+/vhje5nf7wdg9uzZ9nQ5iqJw4YUX2m2EEKxatcouEFesWGGvU1WV0047jdGjRzNq1Chat279G7M7OpDFoqRB4nQ6eeyJyfbrw0GiAro8YCmjUz3WssyEK4fFFWH8lWH7fbe2GZSFYE9pkGA4StQQ5GZ6APAHo6zfUU6nlqlk921NVqqLb1fk43LpKAr4Q5baNs3rIDfTw660AN4u2RSVh9i9tYR1W0vwtUglJ8tDmsfBjqIAO34tRnNqpDfz4XPptGnmo2V61RMkkahJ8wwnfodKJGridekYpqB5upudRQE27yjDiJi0bZ1GZooTh66xJ81FaWWEUChKUYmgU5t0DFPgi22bl5tKIBQlFDFI8+hoqoqmKlQEo5QFIvhcOi6HZqmW3W6ee+UNdFWhR4dmhKKCdJ8TwxSUVlqq6jSfk8xUhXDEpLQyRMQQtMj00D43hbLKMGX+CBEjRLrXYal/hUJxRYjC0iCappDucxKNmricGlHDstxDUXA7VVLcOlFTYBgmZf4IigKqouBxOWJWexA1TAIhE7dTR1MVVFXD7dCIxqzgfG7rhONx6RimpWQORw1CKOi6ih5TGyuAQ9cwHVZRHDUsVXUoYqCpKrpmqZ2VBHV0XCkbjzEYtmwMDdPE5dBsi7iIYaLF5hNVFAWHplhqaSzLurhloKmArum2YjjebyRape516CqqErP7UxSEZsWgKkrM5rDKWi6OqiroqlqloBYCYWJLdhUspXBcLW0TUxCLBAV3vD9NsdTriVhK9ar9YqnGq9THyYLjKvtCFAVVrbLri6vJrT6rFNRVroFiHyW0oCp2Ta0aSAiBSNg36l7K7mRXQpHwXrE/b4mYe+3beNu9+zLF3qrpvfXpTYP27dvTvn37Wm3z1Vdfcc455yRNiq0oCiNGjOCqq67i7LPPTlbDC8HSpUvtAvGXX36x1+m6zumnn87o0aMZOXIkzZs3/+1JHWXIYlHSIHE4HPxtwoT6DkNyEOi6zog/jLKKJEUhFI0ceCOJRCLZi5KSEjweDw8//DAPPvigPVVS69atue666xg7duw+VwKXLFnCW2+9xQcffMDmzZvt5S6Xi2HDhjF69GjOPfdcsrKyjmQqTQ5ZLEokEolEIjlsrFy5ktWrV9O5c2d69+69z/poNMpLL73EPffcw+jRo3n55ZcBOOmkk7jvvvs488wzq3Xx+uyzzxgxYoRdVHo8HkaMGMHo0aM5++yzG9XclA0dqYaWNEhM02TL5s1s2bx5n4l4JQ2LaDTKzI+n8clHHxKNRus7HIlE0sB45513uPDCC3nttdf2WTdnzhx69erF+PHjKSoqoqCgwDZhWLx4Me+++26Nk2s//vjjmKbJ4MGD+eCDDygsLOT999/noosukoViHSOvLEoaJIFAgO6dOwFQWFp2WBxcJHVDOBxi/FWXAbBuewFoh+cZU4lE0jjJy8tj0KBBdO7c2V62fv16br75Zlu8kp2dzT/+8Q/GjRtHQUEB99xzD6+++irt2rWrtlhcv349c+bMQVEUpkyZUutnIiW1QxaLkgaL1+ut7xCS8Hi91Rh9SSQSiWR/XHnllVx55ZX2+8cee4y7776bSCSCrutcf/313HvvvWRmZgLQsmVLXn75Zf72t7/RsWNHe7sFCxawdOlSrr76av79738DcNZZZ8lC8Qggi0VJg8Tn87GnrPzADeuIuAp6b1U0QGaKk8wUJ0Vl5ZTEVNFen6Va9LqsP6GdRX4qQ9a2x+ZlsnZ7KR6nRjAUpU+3HHwuB8UVIXbs8WOaAtO0VMytsn2kehykeh20yPQgBGzfU8me4gDb1u2heV4mJ5zQEsM02VpQSf6uCn7dWoJwGHZ8g49vSUGlpZusiPldKwrkFwcAyM3xIYRgd0kQt1OzlmW4SfM6CIQNIlGTjTvKQYF2zX04dY2dRX4yU5yk+5z4Y57XhilwaAouh+XXXFwRJjPFSWF50I6lLBAmI92NYZi4nRpetw9NVQhHTfzBKBkpOqleB4oCxeUhistCpPmcaKpqq0rLKsOEoiY5aS58HgeaqlDuj1g+0WGBU9cIRQzLXzjmqawoCg5dpVmGG8OwfKHLY/tCVRV8boetwA1HTcIRA7dTx+PUYt7LChHDxBSgqhqGYeJ1WYpuwxRETYFDUVBiklsz5hdd5fNsKWkNwyRkGggBDs2KCSxVs1tTELpqq3iF0AhHDfsxC01TMaMGbqeGolj7TNes7V0OSwltmAJNUWw1s6KAU1WSrIzj7QxTEI0pji0VMZgIW1GsqiBQqv4BUhSMmIe0EKBZB8T2TjYT1iX6J8ddj21fZSw1sRVrsqJXiSua7UjBehpKxNTZFnb/sM9Vpahh2vLm+DpFsXyw437WVs6JT1lV+UHHiSuR48dP3cv3OVEtnrR/E9TY8dz3VjWreynG987N9o5WktfF+40v20tI3mRo0aIFkUiE4cOH8+STT9KjR49q2x1//PH2a9M0ufHGG1m8eDHbt2/n1VdfBeCaa645IjEf7chiUSKRSCQSyWFj8+bN/Pzzz4wYMQKAP//5z7Rr145BgwbV+Dzi3gghuOKKK9izZw/t27dnz549+Hw+TjjhhMMZuiSGFLhIJBKJRCI5bFx//fWcd9553HzzzYB11Xfw4MEHXSgCaJrGtddeyy+//MKvv/4KQGVlJe3bt6d3797cfffdLFiwAMMwDtCT5FCQxaKkQRIKhbju6r9y3dV/JRQK1Xc4BINBRp17Dn++YBTBYPDAG0gkEokEgLKyMqLRKJ999tlv7kvTNG6//Xauu+46UlMta9Fly5bx0EMPccopp9C8eXMuueQSpk6dyp49e37zeBILWSxKGiTRaJTX/vMfXvvPfxrEdCyGYfDprFnM/fxTTPmfq0QikRw08WcPq5tj8VBISUnhrrvuory8HEVRePbZZxkzZgwZGRkUFRUxdepULrnkEpo3b84pp5zCww8/zE8//WS7Bklqj3xmUdIgcTgc3PfAP+zXR4q9hS6Jy+Kk+5z4fE5KKsNsL6wEIC831XrwHijzh9E0hUAoys6iAJqmsG13JRk+J3ktUtA1lcKSIPnFATxOnd0lAVI9Dto0S6GkMsRJ2c0oD0RQuyus31HGms3FKKpCVrqbNtk+KoIRKisr7Xjmr9jJKce3x+PSceaq5Bf7KSwJ0izNTWaqi4pAhFDUIMVjCVMMU7B+WyktmvnwODWcmkp66zRMIdhTHiISDdEi00MkahKOmGSkOglHTMtiL0ZpZZhW2V4CoWjS8kAoijMYRdcUwERHJRiOosYs/4KhKG6nTjhi0KZZCpqq2Psr3eeitDIUE4VAMGxQUh4iHDXo0DKNcMQkHDURApwOzRa2xE8Alu2eZQXo1FWyUl22mKAiJtJRFXA7NHwunXDUpDwQQdcs0YlT13CqMes2RSMUsY6nQ7cs8AzTsr8LG5ZdnxqzAIwLPjRFQVctwYymWqKWYKwPZ0zoErehU2PCD49Ti1nwYdvsBcIGCpZVXyhijSUSxB56ghedQkwsoyTb+cW3URQ1ZpiHLYqx4hUYprU/1JiIJdHGTosJYuKnVsMUsTbYQiRVSRbW7C3i0Pay3CMWm0KCKAZQlL2FLLFtlLj1YUK/worXWq3Y1nyxHjDtIBQSvQo1NR5ClThFjceskFREVIleIGlHU7U/qvZj1TbW/k+ONZF4fwmyl+TlCcKWWtydbRTE3VPiaue6oFWrVvTp04elS5eSmprK22+/TTQaZcGCBcycOZMZM2awcuVKFixYwIIFC7jrrrto3bo1I0aMYMSIEQwdOpSUlJQ6i6epI68sShokTqeT2+68k9vuvPOweUNLJBKJpPFyzjnnAPDJJ58AlvXoaaedxiOPPMKKFSvYsmULL7zwAueccw4ej4dff/2Vl19+mVGjRpGdnc0ZZ5zBU089xfr16+szjUZBgygWn3/+efLy8nC73fTr14/vv/9+v+3fe+89unfvjtvt5rjjjmPmzJlHKFKJRCKRSBoX9X2OjT87WBfPEFZWVjJnzhzuvvtupk+fDsDnn39e7S3mdu3acc011zB9+nR++ukn/v73v9tXN8PhMHPmzOGmm26ia9eu/L//9/9+c2xNmXq/Df3OO+8wceJEXnzxRfr168dTTz3F8OHDWbt2Lc2bN9+n/YIFC7jooouYNGkS55xzDlOnTmXkyJEsXbqUY489th4ykBwOhBAUFhYCkJOTUyvVnEQikUgsGsI5dsWKFYDlEV1bKisr+eabb5g/fz7z5s1j8eLF+zzHfvLJJyOEYNu2baxfv55169Yl/d6wYcN+hYkpKSnSHvAA1HuxOHnyZMaNG8cVV1wBwIsvvsiMGTN49dVXuf322/dp//TTT3PmmWdy6623AvCPf/yD2bNn89xzz/Hiiy8e0dglhw+/30+7li0AafcnkUgkh0pDOMe2bdsWr9dL27ZtD9i2rKyMYDBoF7ILFy7kzDPPTGrTrFkzOnbsSHp6OoZh8Ouvv+L1evc7c4au63Ts2JHOnTvTpUsX+3eXLl1o164dul7v5VCDpl73TjgcZsmSJdxxxx32MlVVGTp0KAsXLqx2m4ULFzJx4sSkZcOHD+ejjz6qtn0oFEr6AJWWlgJQXlb2G6OXHE78CQKO8rKyIz53VkWCwEVEHNXGU14ZpqLC+myVeYQtcCn3h6moDBEIGfgrAjiEA39lBIdwopgOdE2hsiKEvzKECOvWw/aGg3KXQUVlGCI6FYEoqgL+ynIC/hCKouDXrT78wQh+v9+OJ+CvoKK8jGhYx6mpVJQHqKyw/ot2EKYyaAlc/KGoLXAJ+MP4K0xMp4YC6JqKKQT+yhCRqEmlw/rPXQjQcRKJmpbwI0alP4xLCRMMG4SCVbFUVpSjKaBrlvhD01QiUQNVUQg7dELhKA6HRiRiYIR1NFWhIhDGHzJQzRAVlWEMw7qdFIoYmKYgHDUoL4dIxCRqmjg0FUdc4IKCSPAhMbEcTpy6agkwiAlcQlUCF11VbVeZUNSoErhoGpotcIFQxLScWlRLRGPExBmGaaKrqp1jktABkgQu4WiVwCW+/+JCkkSxSKLAJRSNCVxiy3VNtYUTiqKgaVXHIS46EVSJREQsThFzONlb4JIoSFEVSygSF5zEUZVkgYspqrxNFKXK8WR/Ahc7V6o2jMeULHBJzCZx6yoxit1vwhjxfZgYZ9WtyMQRqgQue/cZb16dwKU6EgUuidRG4LJvb9by6saNn6cOVcV7JM6xUPN5tiwW/4svvmgXmmX7Off+85//5KGHHuKyyy7j/PPPZ+PGjaxZswav14uu61RWVmIYBgUFBRQUFOyzvaZp5OXl0bFjRzp16kSnTp3s123btq2xIEz8Pj3aKDvIz1i9FouFhYUYhkFubm7S8tzcXNasWVPtNvn5+dW2z8/Pr7b9pEmTuP/++/dZ3jlPekk2Fjq0bVPfISTR0OK54Y+n1HcINsN/Jx8FkUjqmvLyctLT02u93ZE4x0LN59mDuZJYHa+//jqvv/56rbczDIMNGzawYcMGZs+efUhjH60c6DPW5K+73nHHHUn/JZmmyZYtW+jVqxfbtm1r8s8plJWV0bZtW5lrE+Roylfm2nQ5mvKtba5CCMrLy2nVqtURiO7Qqe48W1RURHZ2tv28+dF0nOM0hpwP9jNWr8ViTk4Omqaxa9eupOW7du2iRYsW1W7TokWLWrV3uVy4XK6kZXGD+bS0tAZ7AOsamWvT5WjKV+badDma8q1NrodyRTHOkTjHQvXn2YyMjGrbHk3HOU5Dz/lgPmP1OnWO0+mkb9++zJ07115mmiZz586lf//+1W7Tv3//pPYAs2fPrrG9RCKRSCRHI/IcK6kr6v029MSJExk7diwnnngiJ598Mk899RSVlZW2cuuyyy6jdevWTJo0CYAJEyYwaNAgnnjiCc4++2zefvttfvjhB/7973/XZxoSiUQikTQ45DlWUhfUe7E4ZswYCgoKuOeee8jPz6dXr158+umn9gO2W7dutW8bAwwYMICpU6dy9913c+edd9KlSxc++uijWs3/5HK5uPfee/e5bN4Ukbk2XY6mfGWuTZejKd/6yLU+zrHVcTQd5zhNKWdFSGdtiUQikUgkEkkNNAi7P4lEIpFIJBJJw0QWixKJRCKRSCSSGpHFokQikUgkEomkRmSxKJFIJBKJRCKpkaOuWHz++efJy8vD7XbTr18/vv/++/oOqdbcd999lr9qwk/37t3t9cFgkOuvv57s7GxSUlIYPXr0PpOsbt26lbPPPhuv10vz5s259dZbiUajRzqVffjqq68499xzadWqFYqi7ONHKoTgnnvuoWXLlng8HoYOHcq6deuS2hQVFXHJJZeQlpZGRkYGV155JRUVFUltli9fzsCBA3G73bRt25bHHnvscKdWLQfK9/LLL9/nWJ955plJbRpLvpMmTeKkk04iNTWV5s2bM3LkSNauXZvUpq4+u/PmzaNPnz64XC46d+7MlClTDnd6SRxMroMHD97n2F5zzTVJbRpDri+88ALHH3+8PfFw//79mTVrlr2+qRzTOAfKt6kc17qkKZx3a8OBvtcbJeIo4u233xZOp1O8+uqrYtWqVWLcuHEiIyND7Nq1q75DqxX33nuvOOaYY8TOnTvtn4KCAnv9NddcI9q2bSvmzp0rfvjhB/G73/1ODBgwwF4fjUbFscceK4YOHSp+/PFHMXPmTJGTkyPuuOOO+kgniZkzZ4q77rpLfPjhhwIQ06ZNS1r/yCOPiPT0dPHRRx+Jn376SfzhD38QHTp0EIFAwG5z5plnihNOOEF899134uuvvxadO3cWF110kb2+tLRU5ObmiksuuUSsXLlSvPXWW8Lj8YiXXnrpSKVpc6B8x44dK84888ykY11UVJTUprHkO3z4cPHaa6+JlStXimXLlokRI0aIdu3aiYqKCrtNXXx2N27cKLxer5g4caL4+eefxbPPPis0TROffvppg8p10KBBYty4cUnHtrS0tNHl+vHHH4sZM2aIX375Raxdu1bceeedwuFwiJUrVwohms4xPdh8m8pxrSuaynm3Nhzoe70xclQViyeffLK4/vrr7feGYYhWrVqJSZMm1WNUtefee+8VJ5xwQrXrSkpKhMPhEO+99569bPXq1QIQCxcuFEJYH2RVVUV+fr7d5oUXXhBpaWkiFAod1thrw95/ZKZpihYtWoh//vOf9rKSkhLhcrnEW2+9JYQQ4ueffxaAWLx4sd1m1qxZQlEU8euvvwohhPjXv/4lMjMzk3K97bbbRLdu3Q5zRvunpmLxvPPOq3Gbxpzv7t27BSDmz58vhKi7z+7f//53ccwxxySNNWbMGDF8+PDDnVKN7J2rEFZRMWHChBq3aay5CiFEZmameOWVV5r0MU0knq8QTfu4HgpN5bx7qDSVYvGouQ0dDodZsmQJQ4cOtZepqsrQoUNZuHBhPUZ2aKxbt45WrVrRsWNHLrnkErZu3QrAkiVLiEQiSXl2796ddu3a2XkuXLiQ4447zp6UFWD48OGUlZWxatWqI5tILdi0aRP5+flJuaWnp9OvX7+k3DIyMjjxxBPtNkOHDkVVVRYtWmS3Oe2003A6nXab4cOHs3btWoqLi49QNgfPvHnzaN68Od26dePaa69lz5499rrGnG9paSkAWVlZQN19dhcuXJjUR7xNff6d751rnP/973/k5ORw7LHHcscdd+D3++11jTFXwzB4++23qayspH///k36mMK++cZpasf1UGlq592jmXp3cDlSFBYWYhhG0h8oQG5uLmvWrKmnqA6Nfv36MWXKFLp168bOnTu5//77GThwICtXriQ/Px+n07mPiXtubi75+fkA5OfnV7sf4usaKvHYqos9MbfmzZsnrdd1naysrKQ2HTp02KeP+LrMzMzDEv+hcOaZZ3L++efToUMHNmzYwJ133slZZ53FwoUL0TSt0eZrmiY33ngjp5xyiu0MUVef3ZralJWVEQgE8Hg8hyOlGqkuV4CLL76Y9u3b06pVK5YvX85tt93G2rVr+fDDD/ebR3zd/toc6VxXrFhB//79CQaDpKSkMG3aNHr27MmyZcua5DGtKV9oWsf1t9KUzrtHO0dNsdiUOOuss+zXxx9/PP369aN9+/a8++67jeZLRHJw/OlPf7JfH3fccRx//PF06tSJefPmMWTIkHqM7Ldx/fXXs3LlSr755pv6DuWwU1Ouf/3rX+3Xxx13HC1btmTIkCFs2LCBTp06HekwfxPdunVj2bJllJaW8v777zN27Fjmz59f32EdNmrKt2fPnk3quEokcY6a29A5OTlomraPCm/Xrl20aNGinqKqGzIyMujatSvr16+nRYsWhMNhSkpKktok5tmiRYtq90N8XUMlHtv+jmGLFi3YvXt30vpoNEpRUVGjzx+gY8eO5OTksH79eqBx5jt+/Hg++eQTvvzyS9q0aWMvr6vPbk1t0tLSjvg/UzXlWh39+vUDSDq2jSVXp9NJ586d6du3L5MmTeKEE07g6aefbpLHFGrOtzoa83H9rTTl8+7RxlFTLDqdTvr27cvcuXPtZaZpMnfu3KRnTRojFRUVbNiwgZYtW9K3b18cDkdSnmvXrmXr1q12nv3792fFihVJRcbs2bNJS0uzb6U0RDp06ECLFi2ScisrK2PRokVJuZWUlLBkyRK7zRdffIFpmvaXdv/+/fnqq6+IRCJ2m9mzZ9OtW7cGdQu6OrZv386ePXto2bIl0LjyFUIwfvx4pk2bxhdffLHPrfG6+uz2798/qY94myP5d36gXKtj2bJlAEnHtjHkWh2maRIKhZrUMd0f8Xyroykd19rSlM+7Rx31rbA5krz9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tm3buPDCC3nqqaeYOnUqM2bM4PPPP+fZZ5+tdtvu3bvz+OOPc91117F161a2b9/ONddcw6OPPkrPnj1rHPP111/H5/OxaNEiHnvsMR544AFmz54NWFdCR44cidfrZdGiRfz73//mrrvuqrGvvfutzbHf335OxDRNzjvvPIqKipg/fz6zZ89m48aNjBkzZv8BCYlE0qQZNGiQmDBhQtKyL7/8UgCiuLhYCCHEa6+9JgCxfv16u83VV18tvF6vKC8vt5cNHz5cXH311UIIIbZs2SI0TRO//vprUt9DhgwRd9xxR43xAGLatGlJy1JTU8WUKVP2m8fYsWPFeeedt982EklTYdCgQaJ3794HbPfee++J7Oxs+/1rr70m0tPT7ff33nuv8Hq9oqyszF526623in79+tXY5zPPPCOOOeYYIYQQH330kejXr58477zzxAsvvCCEEGLo0KHizjvvPOSYhDi474/499KyZctqHEuIqu+zOXPm2MsmTZokALFhwwZ72dVXXy2GDx9uv6/uu/Hss88WAwcOFEOGDBHDhg0Tpmna6/b+Dho0aJA49dRTk7Y/6aSTxG233SaEEGLWrFlC13Wxc+dOe/3s2bOr/Q5MpK6OfZz27duLJ598UgghxOeffy40TRNbt261169atUoA4vvvv692nE2bNgn5zKJEIgHA6/XSqVMn+31ubi55eXmkpKQkLdu9ezcAK1aswDAMunbtmtRPKBQiOzu7VmNPnDiRq666ijfffJOhQ4dywQUXJMUikRyN9O3bd59lc+bMYdKkSaxZs4aysjKi0SjBYBC/31+jgCEvLy9JxNGyZUv777g6Bg0axIQJEygoKGD+/PkMHjyYFi1aMG/ePK688koWLFiQdDXzUGI62O8Pp9PJ8ccfX2OsiSS2y83Nxev10rFjx6Rl33///X77ePXVV+natSuqqrJq1aoDCgD3ji1x365du5a2bdsmPR9+8sknH1QudXXs92b16tW0bds26Q5Tz549ycjIYPXq1Zx00knVbidvQ0skEoB9bvsqilLtMtM0AaioqEDTNJYsWcKyZcvsn9WrV/P000/Xauz77ruPVatWcfbZZ/PFF1/Qs2dPpk2b9tsSkkgaOT6fL+n95s2bOeecczj++OP54IMPWLJkCc8//zxgiSBqYn9/x9Vx3HHHkZWVxfz58+1icfDgwcyfP5/FixcTiUQYMGDAb4rpYL8/PB7PQc/YkJjngb6/auKnn36isrKSyspKdu7cWasxD3aMg6Gujn1dIa8sSiRNHKfTmSRKqSt69+6NYRjs3r2bgQMH/ub+unbtSteuXbnpppu46KKLeO211xg1alQdRCqRNA2WLFmCaZo88cQTtiL43XffrfNxFEVh4MCBTJ8+nVWrVnHqqafi9XoJhUK89NJLnHjiiXYxczAxVfcdVNffH3VBUVERl19+OXfddRc7d+7kkksuYenSpYesLu/WrRvbtm1j165d5ObmArB48eJD6utQ9/Pe9OjRg23btrFt2zb76uLPP/9MSUnJfp/NlFcWJZImTl5eHosWLWLz5s0UFhbWyX+9YBV3l1xyCZdddhkffvghmzZt4vvvv2fSpEnMmDHjoPsJBAKMHz+eefPmsWXLFr799lsWL15Mjx496iROiaSp0LlzZyKRCM8++ywbN27kzTfftMUPdc3gwYN566236NWrFykpKaiqymmnncb//vc/Bg0aVKuY8vLyqKioYO7cuRQWFuL3++vs+6Muueaaa2jbti133303kydPxjCMakUiB8sZZ5xBp06dGDt2LMuXL+fbb7/l7rvvBqj1/LaHup/3ZujQoRx33HF2Ifz9999z2WWXMWjQIE488cQax5fFokTSxLnlllvQNI2ePXvSrFmzaqeeOFRee+01LrvsMm6++Wa6devGyJEjWbx4Me3atTvoPjRNY8+ePVx22WV07dqVCy+8kLPOOov777+/zuKUSJoCJ5xwApMnT+bRRx/l2GOP5X//+x+TJk06LGMNGjQIwzAYPHiwvWzw4MH7LDuYmAYMGMA111zDmDFjaNasGY899hhQN98fdcUbb7xhz/qg6zo+n4///ve/vPzyy8yaNeuQ+tQ0jY8++oiKigpOOukkrrrqKlsN7Xa7a9XXb9nPiSiKwvTp08nMzOS0005j6NChdOzYkXfeeWe/40sHF4lEckRRFIVp06bV2jbr8ssvp6SkRDq4SCSSRsu3337Lqaeeyvr16xuNiE86uEgkknrhoosuIjs7m+3btx+w7ddff81ZZ51FKBTi7LPPPgLRSSQSSd0wbdo0UlJS6NKlC+vXr2fChAmccsopjaZQjCOLRYlEckSJT7iradpBtT/xxBNZtmwZQNI0PhKJRNLQKS8v57bbbmPr1q3k5OQwdOhQnnjiifoOq1a0adNG3oaWSCQSiUQikdSMFLhIJBKJRCKRSGpEFosSiUQikUgkkhqRxaJEIpFIJBKJpEZksSiRSCQSiUQiqRFZLEokEolEIpFIakQWixKJRCKRSCSSGpHFokQikUgkEomkRmSxKJFIJBKJRCKpkf8PP0WaQPAX61IAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "e8848be18fe74eb883245998ac96ddb7", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./qr_rhow=2_p=True.pdf
\"), …" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAosAAAHbCAYAAACjjNB9AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8ekN5oAAAACXBIWXMAAA9hAAAPYQGoP6dpAAD69klEQVR4nOzdd5wTdf7H8dfMpG1f6i4IiIAgiBQVFaVZUdGDs6HnCfaGp+hZznKW85QTf7az4llQEXsHCwJSBFSqIgIi0lR62ZLdtJnv74/JTJJll74kC5/n47GaTCYzn5nJku8m856PppRSCCGEEEIIUQ093QUIIYQQQojMJYNFIYQQQghRIxksCiGEEEKIGslgUQghhBBC1EgGi0IIIYQQokYyWBRCCCGEEDWSwaIQQgghhKiRDBaFEEIIIUSNZLAohBBCCCFqJINFsV8ZOXIkhYWF6S6jTujTpw9Dhw7d4fkvvvhiBgwYUGv11JaWLVvy+OOP7/Lzly9fjqZpaJpGly5ddqsWZ1nz5s3breU49chrXQixJ8hgUexXBg4cyM8//5zuMlJk6gD2/fff5/7779/h+Z944glGjhxZewXtppr288yZM7nyyit3e/njx49nwoQJu/TcSy65hLvuumu3a3CsXr16twbAQgiRzJPuAoTYEyKRCD6fb7vzZWVlkZWVtRcq2vtM00TTNHR9z/wNWL9+/Z2av6CgYI+sd2ft6LGvSaNGjfZIHQ0aNKBBgwY7/TzTNBkzZgxjx47dI3UAFBcXp+14CCH2PfLJoqiT+vTpw3XXXcfQoUNp2LAhffv2BeDRRx/lsMMOIycnh+bNm3PttddSXl7uPq/qp0v33nsvXbp04bXXXqNly5YUFBRw/vnnU1ZWVu16lVI0atSId999153WpUsXmjRp4t7/+uuv8fv9VFRUbLemSZMmcckll1BSUuJ+dXjvvfcCEA6HufnmmznggAPIycnh6KOPZtKkSVtty8cff0yHDh3w+/2sXLlyq5onTZqEpml88cUXdO3alaysLE444QTWrVvHZ599Rvv27cnPz+cvf/mLW7Ozj52voRctWkR2djajR492H3/77bfJysrip59+Arb+GrpPnz5cf/313HrrrdSvX5/i4mJ32xyLFi2iR48eBAIBOnTowPjx49E0jQ8//LDa/e8sd2eP/bb2c9WvoVeuXEn//v3Jzc0lPz+f8847j7Vr19ZYT01isRjXX389hYWFNGjQgNtuu43Bgwdv9VX99OnT8Xq9dOvWbatlmKbJpZdeyiGHHOIe213ZZ0IIsTtksCjqrFdeeQWfz8e0adN47rnnANB1nf/+978sWLCAV155hYkTJ3LrrbduczlLly7lww8/ZMyYMYwZM4bJkyfzn//8p9p5NU2jV69e7qBt8+bNLFy4kMrKShYtWgTA5MmT6datG9nZ2dut6dhjj+Xxxx8nPz+f1atXs3r1am6++WYArrvuOmbMmMGbb77JDz/8wLnnnsupp57KkiVL3HoqKip46KGHeOGFF1iwYAGNGzeucTvvvfdennrqKaZPn86qVas477zzePzxxxk9ejRjx45l3LhxPPnkk9U+95BDDuH//u//uPbaa1m5ciW//fYbV199NQ899BAdOnSocZ2vvPIKOTk5fPvttwwfPpx//etffPnll4A9EBowYADZ2dl8++23PP/889x55501Lqvqcnfm2G9rPyezLIv+/fuzadMmJk+ezJdffsmvv/7KwIEDd6iuZA899BCvv/46L7/8MtOmTaO0tLTaAd3HH3/MmWeeiaZpKdPD4TDnnnsu8+bNY+rUqbRo0WK39pkQQuwyJUQd1Lt3b9W1a9ftzvfOO++oBg0auPdffvllVVBQ4N6/5557VHZ2tiotLXWn3XLLLeroo4+ucZn//e9/1aGHHqqUUurDDz9URx99tOrfv7969tlnlVJKnXTSSeqOO+7Y5ZqUUmrFihXKMAz1+++/p0w/8cQT1e233+4+D1Dz5s2rcV1KKfXVV18pQI0fP96dNmzYMAWopUuXutOuuuoq1bdvX/d+79691Q033JCyrH79+qmePXuqE088UZ1yyinKsiz3scGDB6v+/funPL9Hjx4pz+/WrZu67bbblFJKffbZZ8rj8ajVq1e7j3/55ZcKUB988EGN27Onjr3jwAMPVI899phSSqlx48YpwzDUypUr3ccXLFigAPXdd99Vu55ly5YpQM2dOzdlelFRkXr44Yfd+7FYTLVo0SJlHyml1MEHH6zGjBmTsqypU6eqE088UfXo0UNt2bLFnXdn9llN2yuEEDtLzlkUddYRRxyx1bTx48czbNgwFi1aRGlpKbFYjFAoREVFhftJX1UtW7YkLy/Pvd+kSRPWrVtX43p79+7NDTfcwPr165k8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yy+/rAoKCvZSdbUDUB988ME257n11lvVoYcemjJt4MCBqm/fvrVY2Z61I9v51VdfKUBt3rx5r9RUG9atW6cANXny5Brnqau/n8l2ZDv3hd9PIcS2ySeLGSISiTB79mxOOukkd5qu65x00knMmDGj2ufMmDEjZX6Avn371jh/JtiV7QQoLy/nwAMPpHnz5vTv358FCxbsjXL3qrp4PHdHly5daNKkCSeffDLTpk1Ldzk7paSkBID69evXOM++cDx3ZDth//j9FGJ/JoPFDLFhwwZM06SoqChlelFRUY3ncq1Zs2an5s8Eu7Kd7dq146WXXuKjjz5i1KhRWJbFsccey2+//bY3St5rajqepaWlVFZWpqmqPa9JkyY899xzvPfee7z33ns0b96cPn36MGfOnHSXtkMsy2Lo0KEcd9xxdOzYscb56uLvZ7Id3c795fdTiP2ZdHARGa979+50797dvX/sscfSvn17RowYwf3335/GysSuaNeuHe3atXPvH3vssSxdupTHHnuM1157LY2V7ZghQ4bw448/8vXXX6e7lFq1o9spv59C7Pvkk8UM0bBhQwzDYO3atSnT165dS3FxcbXPKS4u3qn5M8GubGdVXq+Xrl278ssvv9RGiWlT0/HMz88nKysrTVXtHUcddVSdOJ7XXXcdY8aM4auvvqJZs2bbnLcu/n46dmY7q9pXfz+F2J/JYDFD+Hw+jjjiCCZMmOBOsyyLCRMmpPzVnqx79+4p8wN8+eWXNc6fCXZlO6syTZP58+fTpEmT2iozLeri8dxT5s2bl9HHUynFddddxwcffMDEiRM56KCDtvucung8d2U7q9pXfz+F2K+lO2EjEt58803l9/vVyJEj1U8//aSuvPJKVVhYqNasWaOUUuqiiy5S//jHP9z5p02bpjwej/q///s/tXDhQnXPPfcor9er5s+fn65N2CE7u5333Xef+uKLL9TSpUvV7Nmz1fnnn68CgYBasGBBujZhh5SVlam5c+equXPnKkA9+uijau7cuWrFihVKKaX+8Y9/qIsuusid/9dff1XZ2dnqlltuUQsXLlRPP/20MgxDff755+nahB2ys9v52GOPqQ8//FAtWbJEzZ8/X91www1K13U1fvz4dG3Cdl1zzTWqoKBATZo0Sa1evdr9qaiocOfZF34/d2U76+rvpxBix8lgMcM8+eSTqkWL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T4tvn550/JOjsUnt6JJe8x5Dc6c7eSqnZvtx3Z1f01Jb4yVCxqlJXit+jJPCrvF6nAkWiQRsahvBrZPGiZRx1fS3UU3+SwM0PZEcTn5O8rJTllU1YuxOdtojqsSya0pOV1N/dYnjxL7Uqp13q3aKWuq/Q8klV51a/WbElxtP8Fet13leIglfNd++9fKrXnZYVZNeNq2qtaTm93WN1JR6lbXvrzZs2MC1117LO++8A9iX0nnllVeIRCLceOONTJ06lc2bNwPQpk0bbrzxRgYPHkwgEOC7775zB4uNGzfmwQcfJDc3l1NPPTVt27Mv2OnB4r333st9993HSy+9JJfHEbXG6/Pzn6deRNe0jGiv5/P5Gf7US6CREe0HM4nf7+eFV19HKYXPL/tGCLHrvvjiCwYNGsS6devweDzceeednHjiiVx//fVMmjTJna958+YMHz6c8847D13XKSsro3PnzixatIgFCxbQrl07AK6//vo0bcm+ZacHi+eddx5vvPEGjRs3pmXLlni93pTH58yZs8eKE/svj8fDyaf3j7fXS39o3+PxcHK//u6nBJLOTvB4PPzpz2e7n5YKIcSuUEpx6aWXsm7dOpo1a8aHH37IEUccQYsWLVi1apU730033cS9995LXl4eAO+++y4+n48DDzyQ33//nUWLFrFs2TIOPfRQmjdvnq7N2afs9Lvw4MGDmT17Nn/9618pKiqq8esIIYQQQogdpWkaF110EQ899BBr1qxh3bp1gB1yeeCBB1i0aBFgtwN85plnOP300zn33HO54IILAJg9ezatWrUiKyuLhg0bUl5ezvfff0+nTp3Stk37ip0eLI4dO5YvvviCHj161EY9QgD2pRB+mDMLXdc4+uhjMIz0froYi8X4cuzHoMEJffuh6dLuzxGLxfj0k49QStG335/weLzbf5IQQlTjwQcf5LfffuP111/nnHPOYcKECfz1r3/lwgsv5Mcff+Ttt9/mrbfeYsmSJbz//vspl+tbsGABhxxyCGvWrKFLly4sX748pf3wM888w9KlS7nkkkukLfFO0lTVfkvbccghh/D222/X2ZF6aWkpBQUFrN20mfz8/HSXs88IRUzKQ1H3JPEtwQhKKaIxi1g81KFpGrGYRXbAQ162l4pwjLWbK8nyGQR8nnjARLFiXTmRUCV/OaEDAJ9++wuGN0DEtMjP8mIp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Hjf7uIOh7H7LAZ/h1uS8JkLxYxg14aJLr4wnee3e0k4iN/EHu50C9nsT3YedHtSWpXAC1ZZS8Vot59C5P07aWNc0u5UxdmrU63VS2xaaoSeuyxlPuDqvO3/SY6al8Oh2n227r7TlJvs1IGYpjHgPZ8uyk/6aR0epeEI5/to2Tctdvkevmnq2k7ROElaZFpDcm7qaXtVuGla56W1I7qW8dcrZ6d+cPK+TuHaSxcmpXhIvATd1bCUmub/bzj5EKbe0pEnx5do9pVX8+Kp40trpK20vK5EmTrwctJQUsRbfd0b8WZaVeL6zv53Ndno9u8+t0rfZmeh8EpKamE7qmJ30gJtwTp7T/kcLLZ6Ud7bbPSpVdkhywj05yZ3cS1qLb0/VBLteZec76W895YG9I93vs5qm8fzzz3PooYcya9Yshg8fzh133FHtvH6/n/fff5/evXszb948+vbty/Tp02ncuLE7TyQS4aSTTuKss87ihhtuqPXzBg3D4JhjjkmZFg6HGTBgAF9++SVr1qzhpZde4qWXXsLr9dKrVy/+8Y9/bDVAzzR16pPFXXH77bdTUlLi/qxatSrdJYkd4PP7ueXeh/j73f/BlwHnfXgML2cdewXnXHQrHq8v3eVkFL/fz78ffowH/u/xfeocHSHEjtnT77NNmzblySefBOzL0MyZM6fGefPz8/nss8846KCDWLp0Kf369UsJr4wePZqpU6dy3333bXUpm72lY8eOfPDBB2zcuJFx48Zxww030KZNG6LRKBMmTKCiosKdd/78+bz22mv8+uuvGdGQwlGnPlksLi7e6mCvXbuW/Pz8av/aAfuNTN7AhBBCiO3LlPfZCy+8kHfffZePPvqIU089lUmTJtV4/mJxcTGff/45xx57LLNmzeKNN97g4osvBuyvtSsqKiguLq7Vi3jvCL/fz8knn8zJJ5/M448/zs8//8zYsWM58cQT3XneeustHnjgAcA+x/K4445zf7p06ZKSzN6b6tQni927d2fChAkp07788suUq6sLURuUUpRXllBeujmj/trLBEopNm5Yz8YN62XfCFHHZcr7rKZpjBw5kiOOOIL169dz0kkn8csvv9Q4f9u2bd2EdHLyW9M0rr322oy8+HXbtm258cYbycnJcac1b96cY445Bq/Xy+rVq3n33Xe58cYbOeqooygsLGT58uXuvHvzou1p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GIbIzMwUTz31lL0sPz9fuN1uMX36dCGEEBs2bBCAWL58uV3miy++EIqiiD179gghhHjhhRdEWlpaTF/vvfde0aZNm6Pco0NTWbI4YsSISrc5nvu7b98+AYhFixYJIarvs3vPPfeIDh06xLR1ySWXiCFDhhztLlVK2b4KYSYVt99+e6XbHK99FUKItLQ08eqrr57Q+zQaq79CnNj79Ug4Uc67xyMVHXeOlJPmNnQgEGDlypUMHjzYXqaqKoMHD2bp0qU1GNmRsWXLFho0aEDz5s25/PLL2bVrFwArV64kGAzG9LNt27Y0adLE7ufSpUvp2LGjPSkrwJAhQygsLGT9+vXHtiOHwfbt28nJyYnpW0pKCj179ozpW2pqKt27d7fLDB48GFVVWbZsmV2mX79+uFwuu8yQIUPYtGkTeXl5x6g3VWfhwoXUr1+fNm3a8Le//Y2DBw/a647n/hYUFABQp04doPo+u0uXLo2pwypTk9/zsn21eOedd6hbty6nnnoq999/P16v1153PPZV13Xee+89SkpK6NWr1wm9T6F8fy1OtP16pJxo593jjcqOO0dCjTu4HCsOHDiArusxX1CAjIwMfvnllxqK6sjo2bMnb7zxBm3atCE7O5tHHnmEvn37sm7dOnJycnC5XOVM3DMyMsjJyQEgJyenwnGw1tVWrNgqij26b/Xr149Z73A4qFOnTkyZU045pVwd1rq0tLSjEv+RMHToUP76179yyimnsG3bNh544AHOOeccli5diqZpx21/DcNg7NixnHnmmbYzRHV9disrU1hYSGlpKR6P52h0qVIq6ivAZZddRtOmTWnQoAFr167l3nvvZdOmTXzyySeH7Ie17lBljnVff/75Z3r16oXP5yMxMZEZM2bQvn17Vq9efULu08r6CyfWfv2znEjn3eONyo47R8pJkyyeSJxzzjn269NOO42ePXvStGlTPvjgg+PmICKpGpdeeqn9umPHjpx22mm0aNGChQsXMmjQoBqM7M9x6623sm7dOhYvXlzToRx1KuvrjTfeaL/u2LEjWVlZDBo0iG3bttGiRYtjHeafok2bNqxevZqCggI++ugjRo8ezaJFi2o6rKNGZf1t3779CbVfJccv1X2MPWluQ9etWxdN08qp8Pbu3UtmZmYNRVU9pKam0rp1a7Zu3UpmZiaBQID8/PyYMtH9zMzMrHAcrHW1FSu2Q+3DzMxM9u3bF7M+FAqRm5t73PcfoHnz5tStW5etW7cCx2d/x4wZw2effcY333xDo0aN7OXV9dmtrExycvIx/zFVWV8romfPngAx+/Z46avL5aJly5Z069aNiRMn0qlTJyZPnnxC7lOovL8VcTzv1z/LiXzerc0cznGnqpw0yaLL5aJbt24sWLDAXmYYBgsWLIh51uR4pLi4mG3btpGVlUW3bt1wOp0x/dy0aRO7du2y+9mrVy9+/vnnmCRj3rx5JCcn27dSaiOnnHIKmZmZMX0rLCxk2bJlMX3Lz89n5cqVdpmvv/4awzDsg3avXr349ttvCQaDdpl58+bRpk2bWnULuiJ+++03Dh48SFZWFnB89VcIwZgxY5gxYwZff/11uVvj1fXZ7dWrV0wdVplj+T3/o75WxOrVqwFi9u3x0NeKMAwDv99/Qu3TQ2H1tyJOpP16uJzI593ayJEcdw6n8pOG9957T7jdbvHGG2+IDRs2iBtvvFGkpqbGqNKOB+68806xcOFCsX37dvH999+LwYMHi7p164p9+/YJIcypKpo0aSK+/vprsWLFCtGrVy/Rq1cve3tr6oazzz5brF69Wnz55ZeiXr16tWLqnKKiIrFq1SqxatUqAYinn35arFq1SuzcuVMIYU6dk5qaKmbNmiXWrl0rRowYUeHUOV26dBHLli0TixcvFq1atYqZSiY/P19kZGSIK6+8Uqxbt0689957Ij4+vkamzjlUf4uKisRdd90lli5dKrZv3y7mz58vunbtKlq1aiV8Pt9x19+//e1vIiUlRSxcuDBmWhGv12uXqY7PrjXtyN133y02btwopk6desynHfmjvm7dulU8+uijYsWKFWL79u1i1qxZonnz5qJfv37HXV/vu+8+sWjRIrF9+3axdu1acd999wlFUcTcuXOFECfOPq1Kf0+k/VpdnCjn3eOBqhxjj5STKlkUQojnnntONGnSRLhcLtGjRw/xww8/1HRIh80ll1wisrKyhMvlEg0bNhSXXHKJ2Lp1q72+tLRU3HLLLSItLU3Ex8eL888/X2RnZ8fUsWPHDnHOOecIj8cj6tatK+68804RDAaPdVfK8c033wig3N/o0aOFEOb0OQ899JDIyMgQbrdbDBo0SGzatCmmjoMHD4pRo0aJxMREkZycLK655hpRVFQUU2bNmjWiT58+wu12i4YNG4onnnjiWHUxhkP11+v1irPPPlvUq1dPOJ1O0bRpU3HDDTeUO8geL/2tqJ+AmDZtml2muj6733zzjejcubNwuVyiefPmMW0cC/6or7t27RL9+vUTderUEW63W7Rs2VLcfffdMfPxCXF89PXaa68VTZs2FS6XS9SrV08MGjTIThSFOHH2qcWh+nsi7dfq5EQ47x4PVOUYe6Qo4QYkEolEIpFIJJJynDTPLEokEolEIpFIDh+ZLEokEolEIpFIKkUmixKJRCKRSCSSSpHJokQikUgkEomkUmSyKJFIJBKJRCKpFJksSiQSiUQikUgqRSaLEolEIpFIJJJKkcmiRCKRSCQSiaRSZLIokZyELFy4EEVRyM/PP+ZtK4qCoiikpqZWqbwVq6IojBw58qjGJpEc77zxxhtV/m6d7AwYMICxY8dWufzVV199XB6DmjVrxrPPPnvE2+/YsQPp4CKRnOAMGDCAzp07xxwsAoEAubm5ZGRkoCjKMY1HURSmTZvGueeeS/369f+wvBXr7bffjt/vZ+bMmUc/SInkOKW0tJSioqIqfbeOFW+88QZjx46tkR+nhyI3Nxen00lSUlKVyhcUFCCEqLXJeGXjvH//fhISEoiPjz+ienfs2IGjGuKTSCTHGS6Xi8zMzBprPzU1tconMytWj8eD3+8/ypFJJLWTQCCAy+X6w3IejwePx3MMIjr26LqOoiioavXcFK1Tp85hlU9JSamWdg+Xqu77yqhXr96fjkHehpZITmCuvvpqFi1axOTJk+1buTt27Ch3G9q6dfXZZ5/Rpk0b4uPjufDCC/F6vbz55ps0a9aMtLQ0brvtNnRdt+v3+/3cddddNGzYkISEBHr27MnChQsPO841a9Zw1llnkZSURHJyMt26dWPFihXVNAoSyfHHgAEDGDNmDGPHjqVu3boMGTIEgKeffpqOHTuSkJBA48aNueWWWyguLra3K3sbesKECXTu3Jm3336bZs2akZKSwqWXXkpRUVGF7QohqFevHh999JG9rHPnzmRlZdnvFy9ejNvtxuv1/mFMCxcu5JprrqGgoMA+Bk2YMAH44+OH1ZfZs2fTvn173G43u3btKhezdTz76quv6NKlCx6Ph4EDB7Jv3z6++OIL2rVrR3JyMpdddpkdszXG1m3oX375hfj4eN599117/QcffIDH42HDhg1A+dvQAwYM4LbbbuOee+6hTp06ZGZm2n2z+OWXX+jTpw9xcXG0b9+e+fPnoyjKIe+QHMm+P9Q4l70NvWvXLkaMGEFiYiLJyclcfPHF7N27t9J4QCaLEskJzeTJk+nVqxc33HAD2dnZZGdn07hx4wrLer1epkyZwnvvvceXX37JwoULOf/88/n888/5/PPPefvtt3n55ZdjTiJjxoxh6dKlvPfee6xdu5aLLrqIoUOHsmXLlsOK8/LLL6dRo0YsX76clStXct999+F0Ov9U3yWS450333wTl8vF999/z0svvQSAqqpMmTKF9evX8+abb/L1119zzz33HLKebdu2MXPmTD777DM+++wzFi1axBNPPFFhWUVR6Nevn5205eXlsXHjRkpLS/nll18AWLRoEaeffrp9W/NQMfXu3Ztnn32W5ORk+xh01113AVU7fni9Xp588kleffVV1q9ff8g7EhMmTOD5559nyZIl7N69m4svvphnn32Wd999lzlz5jB37lyee+65Crdt27Yt//73v7nlllvYtWsXv/32GzfffDNPPvkk7du3r7TNN998k4SEBJYtW8akSZN49NFHmTdvHmBeCR05ciTx8fEsW7aM//73v/zjH/+otK6y9R7Ovj/UOEdjGAYjRowgNzeXRYsWMW/ePH799VcuueSSQwckJBLJCU3//v3F7bffHrPsm2++EYDIy8sTQggxbdo0AYitW7faZW666SYRHx8vioqK7GVDhgwRN910kxBCiJ07dwpN08SePXti6h40aJC4//77K40HEDNmzIhZlpSUJN54441D9mP06NFixIgRhywjkZwo9O/fX3Tp0uUPy3344YciPT3dfj9t2jSRkpJivx8/fryIj48XhYWF9rK7775b9OzZs9I6p0yZIjp06CCEEGLmzJmiZ8+eYsSIEeLFF18UQggxePBg8cADDxxxTEJU7fhhHZdWr15daVtCRI5n8+fPt5dNnDhRAGLbtm32sptuukkMGTLEfl/RsXHYsGGib9++YtCgQeLss88WhmHY68oeg/r37y/69OkTs/3pp58u7r33XiGEEF988YVwOBwiOzvbXj9v3rwKj4HRVNe+t2jatKl45plnhBBCzJ07V2iaJnbt2mWvX79+vQDEjz/+WGE727dvF/KZRYlEAkB8fDwtWrSw32dkZNCsWTMSExNjlu3btw+An3/+GV3Xad26dUw9fr+f9PT0w2p73LhxXH/99bz99tsMHjyYiy66KCYWieRkpFu3buWWzZ8/n4kTJ/LLL79QWFhIKBTC5/Ph9XorFTA0a9YsRsSRlZVlf48ron///tx+++3s37+fRYsWMWDAADIzM1m4cCHXXXcdS5YsibmaeSQxVfX44XK5OO200yqNNZrochkZGcTHx9O8efOYZT/++OMh63j99ddp3bo1qqqyfv36PxQAlo0temw3bdpE48aNY54P79GjR5X6Ul37viwbN26kcePGMXeY2rdvT2pqKhs3buT000+vcDt5G1oikQCUu+2rKEqFywzDAKC4uBhN01i5ciWrV6+2/zZu3MjkyZMPq+0JEyawfv16hg0bxtdff0379u2ZMWPGn+uQRHKck5CQEPN+x44d/OUvf+G0007j448/ZuXKlUydOhUwRRCVcajvcUV07NiROnXqsGjRIjtZHDBgAIsWLWL58uUEg0F69+79p2Kq6vHD4/FUecaG6H7+0fGrMtasWUNJSQklJSVkZ2cfVptVbaMqVNe+ry7klUWJ5ATH5XLFiFKqiy5duqDrOvv27aNv375/ur7WrVvTunVr7rjjDkaNGsW0adM4//zzqyFSieTEYOXKlRiGwX/+8x9bEfzBBx9UezuKotC3b19mzZrF+vXr6dOnD/Hx8fj9fl5++WW6d+9uJzNViamiY1B1Hz+qg9zcXK6++mr+8Y9/kJ2dzeWXX85PP/10xOryNm3asHv3bvbu3UtGRgYAy5cvP6K6jnScy9KuXTt2797N7t277auLGzZsID8//5DPZsorixLJCU6zZs1YtmwZO3bs4MCBA9XyqxfM5O7yyy/nqquu4pNPPmH79u38+OOPTJw4kTlz5lS5ntLSUsaMGcPChQvZuXMn33//PcuXL6ddu3bVEqdEcqLQsmVLgsEgzz33HL/++itvv/22LX6obgYMGMD06dPp3LkziYmJqKpKv379eOedd+jfv/9hxdSsWTOKi4tZsGABBw4cwOv1Vtvxozq5+eabady4MQ8++CBPP/00uq5XKBKpKv/3f/9HixYtGD16NGvXruX777/nwQcfBDjs+W2PdJzLMnjwYDp27Ggnwj/++CNXXXUV/fv3p3v37pW2L5NFieQE56677kLTNNq3b0+9evUqnHriSJk2bRpXXXUVd955J23atGHkyJEsX76cJk2aVLkOTdM4ePAgV111Fa1bt+biiy/mnHPO4ZFHHqm2OCWSE4FOnTrx9NNP8+STT3LqqafyzjvvMHHixKPSVv/+/dF1nQEDBtjLBgwYUG5ZVWLq3bs3N998M5dccgn16tVj0qRJQPUcP6qLt956y571weFwkJCQwP/+9z9eeeUVvvjiiyOqU9M0Zs6cSXFxMaeffjrXX3+9rYaOi4s7rLr+zDhHoygKs2bNIi0tjX79+jF48GCaN2/O+++/f8j2pYOLRCI5piiKwowZMw7bNuvqq68mPz9fOrhIJJLjlu+//54+ffqwdevW40bEJx1cJBJJjTBq1CjS09P57bff/rDsd999xznnnIPf72fYsGHHIDqJRCKpHmbMmEFiYiKtWrVi69at3H777Zx55pnHTaJoIZNFiURyTLEm3NU0rUrlu3fvzurVqwFipvGRSCSS2k5RURH33nsvu3btom7dugwePJj//Oc/NR3WYdGoUSN5G1oikUgkEolEUjlS4CKRSCQSiUQiqRSZLEokEolEIpFIKkUmixKJRCKRSCSSSpHJokQikUgkEomkUmSyKJFIJBKJRCKpFJksSiQSiUQikUgqRSaLEolEIpFIJJJKkcmiRCKRSCQSiaRS/h9GwhknNYZ3LgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ab77d9059b8e4ac595ebe832ee345c1a", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./qr_rhow=3_p=True.pdf
\"), …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "p = True\n", + "for rho_times_w in (2, 3):\n", + " plot(var='rain water mixing ratio', qlabel='rain water mixing ratio [g/kg]', fname=f'qr_rhow={rho_times_w}_p={p}.pdf',\n", + " output=output[f'rhow={rho_times_w}.0_p={p}'])" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "ExecuteTime": { + "end_time": "2023-12-29T15:14:11.348122Z", + "start_time": "2023-12-29T15:14:11.160894Z" + } + }, + "outputs": [], + "source": [ + "for p in (False, True):\n", + " for rho_times_w in (2, 3):\n", + " key = f\"rhow={rho_times_w}.0_p={p}\"\n", + " filename = 'products_' + key + '.nc'\n", + " nc_exporter = NetCDFExporter_1d(output[key], settings[key], simulation[key], filename)\n", + " nc_exporter.run()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + }, + "vscode": { + "interpreter": { + "hash": "b43cf254c70d60c2e21a7f71ba113e70c1694742e72407132919c841d907074b" + } + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/PySDM/source/examples/PySDM_examples/Shipway_and_Hill_2012/mpdata_1d.py b/PySDM/source/examples/PySDM_examples/Shipway_and_Hill_2012/mpdata_1d.py new file mode 100644 index 0000000000000000000000000000000000000000..d891c583286bffc64d1ab953dbcf7ddebedbf994 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Shipway_and_Hill_2012/mpdata_1d.py @@ -0,0 +1,82 @@ +from functools import cached_property + +import numpy as np + +from PyMPDATA import Options, ScalarField, Solver, Stepper, VectorField +from PyMPDATA.boundary_conditions import Extrapolated + +from PySDM.impl import arakawa_c + + +class MPDATA_1D: + def __init__( + self, + nz, + dt, + advector_of_t, + advectee_of_zZ_at_t0, + g_factor_of_zZ, + mpdata_settings, + ): + self.__t = 0 + self.dt = dt + self.advector_of_t = advector_of_t + + self._grid = (nz,) + self._options = Options( + n_iters=mpdata_settings["n_iters"], + infinite_gauge=mpdata_settings["iga"], + nonoscillatory=mpdata_settings["fct"], + third_order_terms=mpdata_settings["tot"], + ) + bcs = (Extrapolated(),) + zZ_scalar = arakawa_c.z_scalar_coord(self._grid) / nz + self._g_factor = ScalarField( + data=g_factor_of_zZ(zZ_scalar), + halo=self._options.n_halo, + boundary_conditions=bcs, + ) + self._advector = VectorField( + data=(np.full(nz + 1, advector_of_t(0)),), + halo=self._options.n_halo, + boundary_conditions=bcs, + ) + self._advectee = ScalarField( + data=advectee_of_zZ_at_t0(zZ_scalar), + halo=self._options.n_halo, + boundary_conditions=bcs, + ) + + @cached_property + def solver(self): + return Solver( + stepper=Stepper( + options=self._options, grid=self._grid, non_unit_g_factor=True + ), + advectee=self._advectee, + advector=self._advector, + g_factor=self._g_factor, + ) + + @property + def solver_cached(self): + return "solver" in self.__dict__ + + @property + def advectee(self): + return (self.solver.advectee if self.solver_cached else self._advectee).get() + + @property + def advector(self): + return ( + self.solver.advector if self.solver_cached else self._advector + ).get_component(0) + + def update_advector_field(self): + self.__t += 0.5 * self.dt + self.advector[:] = self.advector_of_t(self.__t) + np.testing.assert_array_less(np.abs(self.advector), 1) + self.__t += 0.5 * self.dt + + def __call__(self, _): + self.solver.advance(1) diff --git a/PySDM/source/examples/PySDM_examples/Shipway_and_Hill_2012/plot.py b/PySDM/source/examples/PySDM_examples/Shipway_and_Hill_2012/plot.py new file mode 100644 index 0000000000000000000000000000000000000000..4a768ca7f334d53225cc785329510c35bf7df2c4 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Shipway_and_Hill_2012/plot.py @@ -0,0 +1,113 @@ +import numpy as np +from matplotlib import pyplot +from open_atmos_jupyter_utils import show_plot + +from PySDM.physics import convert_to, si + + +def plot( + var, qlabel, fname, output, vmin=None, vmax=None, cmin=None, cmax=None, line=None +): + line = line or {15: ":", 20: "--", 25: "-", 30: "-."} + dt = output["t"][1] - output["t"][0] + dz = output["z"][1] - output["z"][0] + tgrid = np.concatenate(((output["t"][0] - dt / 2,), output["t"] + dt / 2)) + zgrid = np.concatenate(((output["z"][0] - dz / 2,), output["z"] + dz / 2)) + convert_to(zgrid, si.km) + + fig = pyplot.figure(constrained_layout=True) + gs = fig.add_gridspec(25, 5) + ax1 = fig.add_subplot(gs[:-1, 0:4]) + if cmin is not None and cmax is not None: + mesh = ax1.pcolormesh( + tgrid, zgrid, output[var], cmap="BuPu", vmin=cmin, vmax=cmax + ) + else: + mesh = ax1.pcolormesh(tgrid, zgrid, output[var], cmap="BuPu") + + ax1.set_xlabel("time [s]") + ax1.set_ylabel("z [km]") + ax1.set_ylim(0, None) + + cbar = fig.colorbar(mesh, fraction=0.05, location="top") + cbar.set_label(qlabel) + + ax2 = fig.add_subplot(gs[:-1, 4:], sharey=ax1) + ax2.set_xlabel(qlabel) + # ax2.set_yticks([], minor=False) + + last_t = 0 + for i, t in enumerate(output["t"]): + x, z = output[var][:, i], output["z"].copy() + convert_to(z, si.km) + params = {"color": "black"} + for line_t, line_s in line.items(): + if last_t < line_t * si.min <= t: + params["ls"] = line_s + ax2.plot(x, z, **params) + if vmin is not None and vmax is not None: + ax1.axvline(t, ymin=vmin, ymax=vmax, **params) + else: + ax1.axvline(t, **params) + last_t = t + + show_plot(filename=fname, inline_format="png") + + +def plot_plusminus( + plus_vars, + minus_vars, + qlabel, + fname, + output, + vmin=None, + vmax=None, + line=None, + cmin=0.0, + cmax=None, +): + line = line or {15: ":", 20: "--", 25: "-", 30: "-."} + dt = output["t"][1] - output["t"][0] + dz = output["z"][1] - output["z"][0] + tgrid = np.concatenate(((output["t"][0] - dt / 2,), output["t"] + dt / 2)) + zgrid = np.concatenate(((output["z"][0] - dz / 2,), output["z"] + dz / 2)) + convert_to(zgrid, si.km) + + fig = pyplot.figure(constrained_layout=True) + gs = fig.add_gridspec(25, 5) + ax1 = fig.add_subplot(gs[:-1, 0:4]) + plot_var = 0.0 * output[plus_vars[0]] + for plus_var in plus_vars: + plot_var += output[plus_var] + for minus_var in minus_vars: + plot_var -= output[minus_var] + + mesh = ax1.pcolormesh(tgrid, zgrid, plot_var, cmap="BuPu", vmin=cmin, vmax=cmax) + + ax1.set_xlabel("time [s]") + ax1.set_ylabel("z [km]") + ax1.set_ylim(0, None) + + cbar = fig.colorbar(mesh, fraction=0.05, location="top") + cbar.set_label(qlabel) + + ax2 = fig.add_subplot(gs[:-1, 4:], sharey=ax1) + ax2.set_xlabel(qlabel) + # ax2.set_yticks([], minor=False) + + last_t = 0 + for i, t in enumerate(output["t"]): + x, z = plot_var[:, i], output["z"].copy() + convert_to(z, si.km) + params = {"color": "black"} + for line_t, line_s in line.items(): + if last_t < line_t * si.min <= t: + params["ls"] = line_s + ax2.plot(x, z, **params) + if vmin is not None and vmax is not None: + ax1.axvline(t, ymin=vmin, ymax=vmax, **params) + else: + ax1.axvline(t, **params) + last_t = t + + show_plot(filename=fname, inline_format="png") diff --git a/PySDM/source/examples/PySDM_examples/Shipway_and_Hill_2012/settings.py b/PySDM/source/examples/PySDM_examples/Shipway_and_Hill_2012/settings.py new file mode 100644 index 0000000000000000000000000000000000000000..b74374da0f3072c9fa2847f4e72b5f28702e09d4 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Shipway_and_Hill_2012/settings.py @@ -0,0 +1,189 @@ +from typing import Iterable, Optional + +import numpy as np +from numdifftools import Derivative +from scipy.integrate import solve_ivp +from scipy.interpolate import interp1d + +from PySDM import Formulae +from PySDM.dynamics import condensation +from PySDM.initialisation import spectra +from PySDM.physics import si +from PySDM.dynamics.collisions.collision_kernels import Geometric + + +class Settings: + def __dir__(self) -> Iterable[str]: + return ( + "n_sd_per_gridbox", + "p0", + "kappa", + "rho_times_w_1", + "particles_per_volume_STP", + "dt", + "dz", + "precip", + "z_max", + "t_max", + "cloud_water_radius_range", + "rain_water_radius_range", + "r_bins_edges_dry", + "r_bins_edges", + ) + + def __init__( + self, + *, + n_sd_per_gridbox: int, + p0: float = 1007 * si.hPa, # as used in Olesik et al. 2022 (GMD) + kappa: float = 1, + rho_times_w_1: float = 2 * si.m / si.s * si.kg / si.m**3, + particles_per_volume_STP: int = 50 / si.cm**3, + dt: float = 1 * si.s, + dz: float = 25 * si.m, + z_max: float = 3000 * si.m, + z_part: Optional[tuple] = None, + t_max: float = 60 * si.minutes, + precip: bool = True, + enable_condensation: bool = True, + formulae: Formulae = None, + save_spec_and_attr_times=(), + collision_kernel=None, + old_buggy_density_formula=False, + ): + self.formulae = formulae or Formulae() + self.n_sd_per_gridbox = n_sd_per_gridbox + self.p0 = p0 + self.kappa = kappa + self.rho_times_w_1 = rho_times_w_1 + self.particles_per_volume_STP = particles_per_volume_STP + self.dt = dt + self.dz = dz + self.precip = precip + self.enable_condensation = enable_condensation + self.z_part = z_part + self.z_max = z_max + self.t_max = t_max + self.collision_kernel = collision_kernel or Geometric(collection_efficiency=1) + + t_1 = 600 * si.s + self.rho_times_w = lambda t: ( + rho_times_w_1 * np.sin(np.pi * t / t_1) if t < t_1 else 0 + ) + apprx_w1 = rho_times_w_1 / self.formulae.constants.rho_STP + self.particle_reservoir_depth = ( + (2 * apprx_w1 * t_1 / np.pi) // self.dz + 1 + ) * self.dz + + self.wet_radius_spectrum_per_mass_of_dry_air = spectra.Lognormal( + norm_factor=particles_per_volume_STP / self.formulae.constants.rho_STP, + m_mode=0.08 / 2 * si.um, + s_geom=1.4, + ) + + self._th = interp1d( + (0.0 * si.m, 740.0 * si.m, 3260.00 * si.m), + (297.9 * si.K, 297.9 * si.K, 312.66 * si.K), + fill_value="extrapolate", + ) + + self.water_vapour_mixing_ratio = interp1d( + (-max(self.particle_reservoir_depth, 1), 0, 740, 3260), + (0.015, 0.015, 0.0138, 0.0024), + fill_value="extrapolate", + ) + + self.thd = ( + lambda z_above_reservoir: self.formulae.state_variable_triplet.th_dry( + self._th(z_above_reservoir), + self.water_vapour_mixing_ratio(z_above_reservoir), + ) + ) + + self.rhod0 = self.formulae.state_variable_triplet.rho_d( + p=p0, + water_vapour_mixing_ratio=self.water_vapour_mixing_ratio(0 * si.m), + theta_std=self._th(0 * si.m), + ) + + def drhod_dz(z_above_reservoir, rhod): + if z_above_reservoir < 0: + return 0 + water_vapour_mixing_ratio = self.water_vapour_mixing_ratio( + z_above_reservoir + ) + d_water_vapour_mixing_ratio__dz = Derivative( + self.water_vapour_mixing_ratio + )(z_above_reservoir) + T = self.formulae.state_variable_triplet.T( + rhod[0], self.thd(z_above_reservoir) + ) + p = self.formulae.state_variable_triplet.p( + rhod[0], T, water_vapour_mixing_ratio + ) + lv = self.formulae.latent_heat_vapourisation.lv(T) + return self.formulae.hydrostatics.drho_dz( + p, T, water_vapour_mixing_ratio, lv + ) / ( + 1 + water_vapour_mixing_ratio + ) - rhod * d_water_vapour_mixing_ratio__dz / ( + 1 + water_vapour_mixing_ratio + ) ** ( + 2 if not old_buggy_density_formula else 1 + ) + + z_span = (-self.particle_reservoir_depth, self.z_max) + z_points = np.linspace(*z_span, 2 * self.nz + 1) + rhod_solution = solve_ivp( + fun=drhod_dz, + t_span=z_span, + y0=np.asarray((self.rhod0,)), + t_eval=z_points, + max_step=dz / 2, + ) + assert rhod_solution.success + self.rhod = interp1d(z_points, rhod_solution.y[0]) + + self.mpdata_settings = {"n_iters": 3, "iga": True, "fct": True, "tot": True} + self.condensation_rtol_x = condensation.DEFAULTS.rtol_x + self.condensation_rtol_thd = condensation.DEFAULTS.rtol_thd + self.condensation_adaptive = True + self.condensation_update_thd = False + self.coalescence_adaptive = True + + self.number_of_bins = 100 + self.r_bins_edges_dry = np.logspace( + np.log10(0.001 * si.um), + np.log10(1 * si.um), + self.number_of_bins + 1, + endpoint=True, + ) + self.r_bins_edges = np.logspace( + np.log10(0.001 * si.um), + np.log10(10 * si.mm), + self.number_of_bins + 1, + endpoint=True, + ) + self.cloud_water_radius_range = [1 * si.um, 50 * si.um] + self.rain_water_radius_range = [50 * si.um, np.inf * si.um] + self.save_spec_and_attr_times = save_spec_and_attr_times + + @property + def n_sd(self): + return self.nz * self.n_sd_per_gridbox + + @property + def nz(self): + assert ( + self.particle_reservoir_depth / self.dz + == self.particle_reservoir_depth // self.dz + ) + nz = (self.z_max + self.particle_reservoir_depth) / self.dz + assert nz == int(nz) + return int(nz) + + @property + def nt(self): + nt = self.t_max / self.dt + assert nt == int(nt) + return int(nt) diff --git a/PySDM/source/examples/PySDM_examples/Shipway_and_Hill_2012/simulation.py b/PySDM/source/examples/PySDM_examples/Shipway_and_Hill_2012/simulation.py new file mode 100644 index 0000000000000000000000000000000000000000..d20c7527a373eb5ba819a639bfc7aae248adf04e --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Shipway_and_Hill_2012/simulation.py @@ -0,0 +1,271 @@ +from collections import namedtuple + +import numpy as np +from PySDM_examples.Shipway_and_Hill_2012.mpdata_1d import MPDATA_1D + +import PySDM.products as PySDM_products +from PySDM import Builder +from PySDM.backends import CPU +from PySDM.dynamics import ( + AmbientThermodynamics, + Coalescence, + Condensation, + Displacement, + EulerianAdvection, +) +from PySDM.environments.kinematic_1d import Kinematic1D +from PySDM.impl.mesh import Mesh +from PySDM.initialisation.sampling import spatial_sampling, spectral_sampling + + +class Simulation: + def __init__(self, settings, backend=CPU): + self.nt = settings.nt + self.z0 = -settings.particle_reservoir_depth + self.save_spec_and_attr_times = settings.save_spec_and_attr_times + self.number_of_bins = settings.number_of_bins + + self.particulator = None + self.output_attributes = None + self.output_products = None + + self.mesh = Mesh( + grid=(settings.nz,), + size=(settings.z_max + settings.particle_reservoir_depth,), + ) + + env = Kinematic1D( + dt=settings.dt, + mesh=self.mesh, + thd_of_z=settings.thd, + rhod_of_z=settings.rhod, + z0=-settings.particle_reservoir_depth, + ) + + def zZ_to_z_above_reservoir(zZ): + z_above_reservoir = zZ * (settings.nz * settings.dz) + self.z0 + return z_above_reservoir + + mpdata = MPDATA_1D( + nz=settings.nz, + dt=settings.dt, + mpdata_settings=settings.mpdata_settings, + advector_of_t=lambda t: settings.rho_times_w(t) * settings.dt / settings.dz, + advectee_of_zZ_at_t0=lambda zZ: settings.water_vapour_mixing_ratio( + zZ_to_z_above_reservoir(zZ) + ), + g_factor_of_zZ=lambda zZ: settings.rhod(zZ_to_z_above_reservoir(zZ)), + ) + + _extra_nz = settings.particle_reservoir_depth // settings.dz + _z_vec = settings.dz * np.linspace( + -_extra_nz, settings.nz - _extra_nz, settings.nz + 1 + ) + self.g_factor_vec = settings.rhod(_z_vec) + + self.builder = Builder( + n_sd=settings.n_sd, + backend=backend(formulae=settings.formulae), + environment=env, + ) + self.builder.add_dynamic(AmbientThermodynamics()) + + if settings.enable_condensation: + self.builder.add_dynamic( + Condensation( + adaptive=settings.condensation_adaptive, + rtol_thd=settings.condensation_rtol_thd, + rtol_x=settings.condensation_rtol_x, + update_thd=settings.condensation_update_thd, + ) + ) + self.builder.add_dynamic(EulerianAdvection(mpdata)) + + self.products = [] + if settings.precip: + self.add_collision_dynamic(self.builder, settings, self.products) + + displacement = Displacement( + enable_sedimentation=settings.precip, + precipitation_counting_level_index=int( + settings.particle_reservoir_depth / settings.dz + ), + ) + self.builder.add_dynamic(displacement) + self.attributes = self.builder.particulator.environment.init_attributes( + spatial_discretisation=spatial_sampling.Pseudorandom(), + spectral_discretisation=spectral_sampling.ConstantMultiplicity( + spectrum=settings.wet_radius_spectrum_per_mass_of_dry_air + ), + kappa=settings.kappa, + collisions_only=not settings.enable_condensation, + z_part=settings.z_part, + ) + self.products += [ + PySDM_products.WaterMixingRatio( + name="cloud water mixing ratio", + unit="g/kg", + radius_range=settings.cloud_water_radius_range, + ), + PySDM_products.WaterMixingRatio( + name="rain water mixing ratio", + unit="g/kg", + radius_range=settings.rain_water_radius_range, + ), + PySDM_products.AmbientDryAirDensity(name="rhod"), + PySDM_products.AmbientDryAirPotentialTemperature(name="thd"), + PySDM_products.ParticleSizeSpectrumPerVolume( + name="wet spectrum", radius_bins_edges=settings.r_bins_edges + ), + PySDM_products.ParticleConcentration( + name="nc", radius_range=settings.cloud_water_radius_range + ), + PySDM_products.ParticleConcentration( + name="nr", radius_range=settings.rain_water_radius_range + ), + PySDM_products.ParticleConcentration( + name="na", radius_range=(0, settings.cloud_water_radius_range[0]) + ), + PySDM_products.MeanRadius(), + PySDM_products.EffectiveRadius( + radius_range=settings.cloud_water_radius_range + ), + PySDM_products.SuperDropletCountPerGridbox(), + PySDM_products.AveragedTerminalVelocity( + name="rain averaged terminal velocity", + radius_range=settings.rain_water_radius_range, + ), + PySDM_products.AmbientRelativeHumidity(name="RH", unit="%"), + PySDM_products.AmbientPressure(name="p"), + PySDM_products.AmbientTemperature(name="T"), + PySDM_products.AmbientWaterVapourMixingRatio( + name="water_vapour_mixing_ratio" + ), + ] + if settings.enable_condensation: + self.products.extend( + [ + PySDM_products.RipeningRate(name="ripening"), + PySDM_products.ActivatingRate(name="activating"), + PySDM_products.DeactivatingRate(name="deactivating"), + PySDM_products.PeakSaturation(), + PySDM_products.ParticleSizeSpectrumPerVolume( + name="dry spectrum", + radius_bins_edges=settings.r_bins_edges_dry, + dry=True, + ), + ] + ) + if settings.precip: + self.products.extend( + [ + PySDM_products.CollisionRatePerGridbox( + name="collision_rate", + ), + PySDM_products.CollisionRateDeficitPerGridbox( + name="collision_deficit", + ), + PySDM_products.CoalescenceRatePerGridbox( + name="coalescence_rate", + ), + PySDM_products.SurfacePrecipitation(), + ] + ) + self.particulator = self.builder.build( + attributes=self.attributes, products=tuple(self.products) + ) + + self.output_attributes = { + "cell origin": [], + "position in cell": [], + "radius": [], + "multiplicity": [], + } + self.output_products = {} + for k, v in self.particulator.products.items(): + if len(v.shape) == 0: + self.output_products[k] = np.zeros(self.nt + 1) + elif len(v.shape) == 1: + self.output_products[k] = np.zeros((self.mesh.grid[-1], self.nt + 1)) + elif len(v.shape) == 2: + number_of_time_sections = len(self.save_spec_and_attr_times) + self.output_products[k] = np.zeros( + (self.mesh.grid[-1], self.number_of_bins, number_of_time_sections) + ) + + @staticmethod + def add_collision_dynamic(builder, settings, _): + builder.add_dynamic( + Coalescence( + collision_kernel=settings.collision_kernel, + adaptive=settings.coalescence_adaptive, + ) + ) + + def save_scalar(self, step): + for k, v in self.particulator.products.items(): + if len(v.shape) > 1: + continue + if len(v.shape) == 1: + self.output_products[k][:, step] = v.get() + else: + self.output_products[k][step] = v.get() + + def save_spectrum(self, index): + for k, v in self.particulator.products.items(): + if len(v.shape) == 2: + self.output_products[k][:, :, index] = v.get() + + def save_attributes(self): + for k, v in self.output_attributes.items(): + v.append(self.particulator.attributes[k].to_ndarray()) + + def save(self, step): + self.save_scalar(step) + time = step * self.particulator.dt + if len(self.save_spec_and_attr_times) > 0 and ( + np.min( + np.abs( + np.ones_like(self.save_spec_and_attr_times) * time + - np.array(self.save_spec_and_attr_times) + ) + ) + < 0.1 + ): + save_index = np.argmin( + np.abs( + np.ones_like(self.save_spec_and_attr_times) * time + - np.array(self.save_spec_and_attr_times) + ) + ) + self.save_spectrum(save_index) + self.save_attributes() + + def run(self): + mesh = self.particulator.mesh + + assert "t" not in self.output_products and "z" not in self.output_products + self.output_products["t"] = np.linspace( + 0, self.nt * self.particulator.dt, self.nt + 1, endpoint=True + ) + self.output_products["z"] = np.linspace( + self.z0 + mesh.dz / 2, + self.z0 + (mesh.grid[-1] - 1 / 2) * mesh.dz, + mesh.grid[-1], + endpoint=True, + ) + + self.save(0) + for step in range(self.nt): + mpdata = self.particulator.dynamics["EulerianAdvection"].solvers + mpdata.update_advector_field() + if "Displacement" in self.particulator.dynamics: + self.particulator.dynamics["Displacement"].upload_courant_field( + (mpdata.advector / self.g_factor_vec,) + ) + self.particulator.run(steps=1) + self.save(step + 1) + + Outputs = namedtuple("Outputs", "products attributes") + output_results = Outputs(self.output_products, self.output_attributes) + return output_results diff --git a/PySDM/source/examples/PySDM_examples/Singer_Ward/MWE_joss_paper.ipynb b/PySDM/source/examples/PySDM_examples/Singer_Ward/MWE_joss_paper.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..05c66011779935c02a5eea29629642274090ce7f --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Singer_Ward/MWE_joss_paper.ipynb @@ -0,0 +1,2076 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "collapsed": false + }, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Singer_Ward/MWE_joss_paper.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Singer_Ward/MWE_joss_paper.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Singer_Ward/MWE_joss_paper.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### MWE for JOSSv2 paper code snippets" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-10T19:09:56.811832Z", + "start_time": "2024-12-10T19:09:56.803208Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-10T19:10:04.060283Z", + "start_time": "2024-12-10T19:09:56.816551Z" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "from PySDM import Formulae\n", + "from PySDM.physics import si\n", + "from PySDM_examples.Singer_Ward.aerosol import AerosolBetaCaryophylleneDark\n", + "\n", + "FORMULAE = Formulae()\n", + "WATER_MOLAR_VOLUME = FORMULAE.constants.Mv / FORMULAE.constants.rho_w\n", + "aerosol = AerosolBetaCaryophylleneDark(water_molar_volume=WATER_MOLAR_VOLUME)\n", + "formulae_bulk = Formulae(surface_tension='Constant')\n", + "formulae_ovad = Formulae(\n", + " surface_tension='CompressedFilmOvadnevaite',\n", + " constants={\n", + " 'sgm_org': 35 * si.mN / si.m,\n", + " 'delta_min': 1.75 * si.nm\n", + " }\n", + ")\n", + "formulae_ruehl = Formulae(\n", + " surface_tension='CompressedFilmRuehl',\n", + " constants={\n", + " 'RUEHL_nu_org': aerosol.modes[0]['nu_org'],\n", + " 'RUEHL_A0': 115e-20 * si.m * si.m,\n", + " 'RUEHL_C0': 6e-7,\n", + " 'RUEHL_m_sigma': 0.3e17 * si.J / si.m**2,\n", + " 'RUEHL_sgm_min': 35 * si.mN / si.m\n", + " }\n", + ")\n", + "formulae_sl = Formulae(\n", + " surface_tension='SzyszkowskiLangmuir',\n", + " constants={\n", + " 'RUEHL_nu_org': aerosol.modes[0]['nu_org'],\n", + " 'RUEHL_A0': 115e-20 * si.m * si.m,\n", + " 'RUEHL_C0': 6e-7,\n", + " 'RUEHL_sgm_min': 35 * si.mN / si.m\n", + " }\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-10T19:10:07.831134Z", + "start_time": "2024-12-10T19:10:04.397617Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-12-10T20:10:07.801306\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.9.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "c705f18abf4744a5af86ea30b1067979", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./Singer_fig1_kohler.pdf
…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import numpy as np\n", + "from matplotlib import pyplot\n", + "from open_atmos_jupyter_utils import show_plot\n", + "lines = {'Constant': '-', 'CompressedFilmOvadnevaite': '--', 'CompressedFilmRuehl': ':', 'SzyszkowskiLangmuir': '-.'}\n", + "colors = {'Constant': 'k', 'CompressedFilmOvadnevaite': 'C0', 'CompressedFilmRuehl': 'C1', 'SzyszkowskiLangmuir': 'C2'}\n", + "\n", + "for formulae in (formulae_bulk, formulae_ovad, formulae_ruehl, formulae_sl): \n", + " r_wet = np.logspace(np.log(50 * si.nm), np.log(2000 * si.nm), base=np.e, num=100)\n", + " sigma = np.ones(len(r_wet))\n", + " for j,vw in enumerate(formulae_ovad.trivia.volume(r_wet)):\n", + " sigma[j] = formulae.surface_tension.sigma(\n", + " 300 * si.K,\n", + " vw,\n", + " formulae_ovad.trivia.volume(50 * si.nm),\n", + " aerosol.modes[0]['f_org']\n", + " )\n", + " RH_eq = formulae.hygroscopicity.RH_eq(\n", + " r_wet,\n", + " 300 * si.K,\n", + " aerosol.modes[0]['kappa'][formulae.surface_tension.__name__],\n", + " (50 * si.nm)**3,\n", + " sigma\n", + " )\n", + " model = formulae.surface_tension.__name__\n", + " pyplot.plot(\n", + " r_wet / si.nm,\n", + " (RH_eq - 1)*100,\n", + " label=f\"{model}\",\n", + " color=colors[model],\n", + " linestyle=lines[model]\n", + " )\n", + "pyplot.grid()\n", + "pyplot.legend(title=\"Surface tension model\", fontsize=10, loc=4)\n", + "pyplot.xscale('log')\n", + "r_wet_ticks_nm = (100, 200, 300, 500, 1000, 2000)\n", + "pyplot.xticks(r_wet_ticks_nm, r_wet_ticks_nm)\n", + "pyplot.xlim(r_wet[0] / si.nm, r_wet[-1] / si.nm)\n", + "pyplot.ylabel('Equilibrium supersaturation [%]')\n", + "yticks = (-.2, -.1, 0, .1, .2, .3, .4)\n", + "pyplot.yticks(yticks, yticks)\n", + "pyplot.ylim(yticks[0], .45)\n", + "pyplot.xlabel('Wet radius [nm]')\n", + "show_plot(\"Singer_fig1_kohler.pdf\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.10.9 ('pysdm')", + "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.9" + }, + "vscode": { + "interpreter": { + "hash": "b14f34a08619f4a218d80d7380beed3f0c712c89ff93e7183219752d640ed427" + } + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/PySDM/source/examples/PySDM_examples/Singer_Ward/__init__.py b/PySDM/source/examples/PySDM_examples/Singer_Ward/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..738c9374e685ccc5fff09c0e97e8caf630929736 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Singer_Ward/__init__.py @@ -0,0 +1,9 @@ +""" +kohler.ipynb: +.. include:: ./kohler.ipynb.badges.md + +MWE_joss_paper.ipynb: +.. include:: ./MWE_joss_paper.ipynb.badges.md +""" + +# pylint: disable=invalid-name diff --git a/PySDM/source/examples/PySDM_examples/Singer_Ward/aerosol.py b/PySDM/source/examples/PySDM_examples/Singer_Ward/aerosol.py new file mode 100644 index 0000000000000000000000000000000000000000..ffe62ec307feb67257edd7ee1b38b6ce93e41fc7 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Singer_Ward/aerosol.py @@ -0,0 +1,185 @@ +from chempy import Substance +from pystrict import strict + +from PySDM.initialisation import spectra +from PySDM.initialisation.aerosol_composition import DryAerosolMixture +from PySDM.physics import si + + +@strict +class AerosolBetaCaryophylleneDark(DryAerosolMixture): + def __init__(self, water_molar_volume: float, Forg: float = 0.8, N: float = 400): + mode = { + "(NH4)2SO4": (1 - Forg), + "bcary_dark": Forg, + } + + super().__init__( + compounds=("(NH4)2SO4", "bcary_dark"), + molar_masses={ + "(NH4)2SO4": Substance.from_formula("(NH4)2SO4").mass + * si.gram + / si.mole, + "bcary_dark": 299 * si.gram / si.mole, + }, + densities={ + "(NH4)2SO4": 1.77 * si.g / si.cm**3, + "bcary_dark": 1.20 * si.g / si.cm**3, + }, + is_soluble={ + "(NH4)2SO4": False, + "bcary_dark": True, + }, + ionic_dissociation_phi={ + "(NH4)2SO4": 3, + "bcary_dark": 1, + }, + ) + self.modes = ( + { + "f_org": 1 - self.f_soluble_volume(mode), + "kappa": self.kappa( + mass_fractions=mode, water_molar_volume=water_molar_volume + ), + "nu_org": self.nu_org(mode), + "spectrum": spectra.Lognormal( + norm_factor=N / si.cm**3, m_mode=50.0 * si.nm, s_geom=1.75 + ), + }, + ) + + color = "red" + + +class AerosolBetaCaryophylleneLight(DryAerosolMixture): + def __init__(self, water_molar_volume: float, Forg: float = 0.8, N: float = 400): + mode = { + "(NH4)2SO4": (1 - Forg), + "bcary_light": Forg, + } + + super().__init__( + compounds=("(NH4)2SO4", "bcary_light"), + molar_masses={ + "(NH4)2SO4": Substance.from_formula("(NH4)2SO4").mass + * si.gram + / si.mole, + "bcary_light": 360 * si.gram / si.mole, + }, + densities={ + "(NH4)2SO4": 1.77 * si.g / si.cm**3, + "bcary_light": 1.50 * si.g / si.cm**3, + }, + is_soluble={ + "(NH4)2SO4": False, + "bcary_light": True, + }, + ionic_dissociation_phi={ + "(NH4)2SO4": 3, + "bcary_light": 1, + }, + ) + self.modes = ( + { + "f_org": 1 - self.f_soluble_volume(mode), + "kappa": self.kappa( + mass_fractions=mode, water_molar_volume=water_molar_volume + ), + "nu_org": self.nu_org(mode), + "spectrum": spectra.Lognormal( + norm_factor=N / si.cm**3, m_mode=50.0 * si.nm, s_geom=1.75 + ), + }, + ) + + color = "orange" + + +@strict +class AerosolAlphaPineneDark(DryAerosolMixture): + def __init__(self, water_molar_volume: float, Forg: float = 0.8, N: float = 400): + mode = { + "(NH4)2SO4": (1 - Forg), + "apinene_dark": Forg, + } + + super().__init__( + compounds=("(NH4)2SO4", "apinene_dark"), + molar_masses={ + "(NH4)2SO4": Substance.from_formula("(NH4)2SO4").mass + * si.gram + / si.mole, + "apinene_dark": 209 * si.gram / si.mole, + }, + densities={ + "(NH4)2SO4": 1.77 * si.g / si.cm**3, + "apinene_dark": 1.27 * si.g / si.cm**3, + }, + is_soluble={ + "(NH4)2SO4": False, + "apinene_dark": True, + }, + ionic_dissociation_phi={ + "(NH4)2SO4": 3, + "apinene_dark": 1, + }, + ) + self.modes = ( + { + "f_org": 1 - self.f_soluble_volume(mode), + "kappa": self.kappa( + mass_fractions=mode, water_molar_volume=water_molar_volume + ), + "nu_org": self.nu_org(mode), + "spectrum": spectra.Lognormal( + norm_factor=N / si.cm**3, m_mode=50.0 * si.nm, s_geom=1.75 + ), + }, + ) + + color = "green" + + +@strict +class AerosolAlphaPineneLight(DryAerosolMixture): + def __init__(self, water_molar_volume: float, Forg: float = 0.8, N: float = 400): + mode = { + "(NH4)2SO4": (1 - Forg), + "apinene_light": Forg, + } + + super().__init__( + compounds=("(NH4)2SO4", "apinene_light"), + molar_masses={ + "(NH4)2SO4": Substance.from_formula("(NH4)2SO4").mass + * si.gram + / si.mole, + "apinene_light": 265 * si.gram / si.mole, + }, + densities={ + "(NH4)2SO4": 1.77 * si.g / si.cm**3, + "apinene_light": 1.51 * si.g / si.cm**3, + }, + is_soluble={ + "(NH4)2SO4": False, + "apinene_light": True, + }, + ionic_dissociation_phi={ + "(NH4)2SO4": 3, + "apinene_light": 1, + }, + ) + self.modes = ( + { + "f_org": 1 - self.f_soluble_volume(mode), + "kappa": self.kappa( + mass_fractions=mode, water_molar_volume=water_molar_volume + ), + "nu_org": self.nu_org(mode), + "spectrum": spectra.Lognormal( + norm_factor=N / si.cm**3, m_mode=50.0 * si.nm, s_geom=1.75 + ), + }, + ) + + color = "lightgreen" diff --git a/PySDM/source/examples/PySDM_examples/Singer_Ward/kohler.ipynb b/PySDM/source/examples/PySDM_examples/Singer_Ward/kohler.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..9454fd7bc46e185567f3287eeecd1908f4947a84 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Singer_Ward/kohler.ipynb @@ -0,0 +1,2963 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "collapsed": false + }, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Singer_Ward/kohler.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Singer_Ward/kohler.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Singer_Ward/kohler.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Example of using different surface tension models and the resulting Köhler curves\n", + "Parameters for surface tension models (and aerosol physiochemical description) loosely based on laboratory experiments by RXW, described in Ward et al., in prep. (2022)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-10T19:16:37.800284Z", + "start_time": "2024-12-10T19:16:37.794555Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-10T19:16:39.922261Z", + "start_time": "2024-12-10T19:16:37.805127Z" + }, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "from matplotlib import pyplot\n", + "import numpy as np\n", + "\n", + "from PySDM import Formulae\n", + "from PySDM.physics import si\n", + "\n", + "from open_atmos_jupyter_utils import show_plot\n", + "\n", + "from PySDM_examples.Singer_Ward.aerosol import AerosolBetaCaryophylleneDark" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-10T19:16:45.169404Z", + "start_time": "2024-12-10T19:16:40.030188Z" + } + }, + "outputs": [], + "source": [ + "# aerosol creation\n", + "FORMULAE = Formulae()\n", + "WATER_MOLAR_VOLUME = FORMULAE.constants.Mv / FORMULAE.constants.rho_w\n", + "aerosol = AerosolBetaCaryophylleneDark(water_molar_volume=WATER_MOLAR_VOLUME)\n", + "\n", + "# formulae creation\n", + "formulae_bulk = Formulae(surface_tension='Constant')\n", + "formulae_ovad = Formulae(\n", + " surface_tension='CompressedFilmOvadnevaite',\n", + " constants={\n", + " 'sgm_org': 35 * si.mN / si.m,\n", + " 'delta_min': 1.75 * si.nm\n", + " }\n", + ")\n", + "formulae_ruehl = Formulae(\n", + " surface_tension='CompressedFilmRuehl',\n", + " constants={\n", + " 'RUEHL_nu_org': aerosol.modes[0]['nu_org'],\n", + " 'RUEHL_A0': 115e-20 * si.m * si.m,\n", + " 'RUEHL_C0': 6e-7,\n", + " 'RUEHL_m_sigma': 0.3e17 * si.J / si.m**2,\n", + " 'RUEHL_sgm_min': 35 * si.mN / si.m\n", + " }\n", + ")\n", + "formulae_sl = Formulae(\n", + " surface_tension='SzyszkowskiLangmuir',\n", + " constants={\n", + " 'RUEHL_nu_org': aerosol.modes[0]['nu_org'],\n", + " 'RUEHL_A0': 115e-20 * si.m * si.m,\n", + " 'RUEHL_C0': 6e-7,\n", + " 'RUEHL_sgm_min': 35 * si.mN / si.m\n", + " }\n", + ")\n", + "\n", + "# aerosol and thermodynamic conditions\n", + "T = 300 * si.K\n", + "r_dry = 50 * si.nm\n", + "v_dry = formulae_ovad.trivia.volume(r_dry)\n", + "\n", + "# plotting info\n", + "r_wet = np.logspace(np.log(50 * si.nm), np.log(2000 * si.nm), base=np.e, num=100)\n", + "v_wet = formulae_ovad.trivia.volume(r_wet)\n", + "r_wet_ticks_nm = (100, 200, 300, 500, 1000, 2000)\n", + "lines = {'Constant': '-', 'CompressedFilmOvadnevaite': '--', 'CompressedFilmRuehl': ':', 'SzyszkowskiLangmuir': '-.'}\n", + "colors = {'Constant': 'k', 'CompressedFilmOvadnevaite': 'C0', 'CompressedFilmRuehl': 'C1', 'SzyszkowskiLangmuir': 'C2'}" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-10T19:16:47.426715Z", + "start_time": "2024-12-10T19:16:45.178408Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-12-10T20:16:47.372902\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.9.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "71891e2c057b44599b877e63f2180046", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./Singer_fig1.pdf
\"), HTML(value…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig,axes = pyplot.subplots(2, 1, figsize=(6,6), sharex=True, sharey=False)\n", + "\n", + "for formulae in (formulae_bulk, formulae_ovad, formulae_ruehl, formulae_sl):\n", + " model = formulae.surface_tension.__name__\n", + " sigma = np.ones(len(v_wet))\n", + " if model in (\"CompressedFilmRuehl\", \"SzyszkowskiLangmuir\"):\n", + " for j,vw in enumerate(v_wet):\n", + " sigma[j] = formulae.surface_tension.sigma(T, vw, v_dry, aerosol.modes[0]['f_org'])\n", + " else:\n", + " sigma = formulae.surface_tension.sigma(T, v_wet, v_dry, aerosol.modes[0]['f_org'])\n", + " RH_eq = formulae.hygroscopicity.RH_eq(r_wet, T, aerosol.modes[0]['kappa'][model], r_dry**3, sigma)\n", + "\n", + " if not type(sigma) is type(v_wet):\n", + " sigma = np.ones(len(v_wet)) * sigma\n", + " # plot surface tension\n", + " axes[0].plot(\n", + " r_wet / si.nm,\n", + " sigma / (si.mN / si.m),\n", + " label=f\"{model}\", \n", + " color=colors[model],\n", + " linestyle=lines[model]\n", + " )\n", + " \n", + " # plot Köhler curve\n", + " axes[1].plot(\n", + " r_wet / si.nm, \n", + " (RH_eq - 1)*100, \n", + " color=colors[model], \n", + " linestyle=lines[model]\n", + " )\n", + "\n", + "# plot attributes\n", + "for ax in axes:\n", + " ax.grid()\n", + " ax.set_xscale('log')\n", + " ax.set_xticks(r_wet_ticks_nm, r_wet_ticks_nm)\n", + " ax.set_xlim(r_wet[0] / si.nm, r_wet[-1] / si.nm)\n", + "\n", + "ax = axes[0]\n", + "ax.set_ylabel('Surface tension [mN m$^{-1}$]')\n", + "yticks = (30, 40, 50, 60, 70, 80)\n", + "ax.set_yticks(yticks, yticks)\n", + "ax.set_ylim(30, 80)\n", + "ax.legend(title=\"Surface tension model\", fontsize=10, loc=4)\n", + "\n", + "ax = axes[1]\n", + "ax.set_ylabel('Equilibrium supersaturation [%]')\n", + "yticks = (-.2, -.1, 0, .1, .2, .3, .4)\n", + "ax.set_yticks(yticks, yticks)\n", + "ax.set_ylim(yticks[0], .45)\n", + "ax.set_xlabel('Wet radius [nm]')\n", + "\n", + "show_plot(\"Singer_fig1.pdf\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.10.9 ('pysdm')", + "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.9" + }, + "vscode": { + "interpreter": { + "hash": "b14f34a08619f4a218d80d7380beed3f0c712c89ff93e7183219752d640ed427" + } + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/PySDM/source/examples/PySDM_examples/Spichtinger_et_al_2023/__init__.py b/PySDM/source/examples/PySDM_examples/Spichtinger_et_al_2023/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..baa2774fa3eaf2f6d97a84ba64596f8b792a76df --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Spichtinger_et_al_2023/__init__.py @@ -0,0 +1,7 @@ +# pylint: disable=invalid-name +""" +homogeneous nucleation event example based on Fig. B1. in +[Spichtinger et al. 2023](https://doi.org/10.5194/acp-23-2035-2023) +""" +from .simulation import Simulation +from .settings import Settings diff --git a/PySDM/source/examples/PySDM_examples/Spichtinger_et_al_2023/data/__init__.py b/PySDM/source/examples/PySDM_examples/Spichtinger_et_al_2023/data/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/examples/PySDM_examples/Spichtinger_et_al_2023/data/reference_bulk.py b/PySDM/source/examples/PySDM_examples/Spichtinger_et_al_2023/data/reference_bulk.py new file mode 100644 index 0000000000000000000000000000000000000000..6aec8eb50785ba53bfa861b51898efc3fd114b10 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Spichtinger_et_al_2023/data/reference_bulk.py @@ -0,0 +1,47 @@ +""" +reference results for bulk scheme in Fig B1. in +[Spichtinger et al. 2023](https://doi.org/10.5194/acp-23-2035-2023) +""" + +import numpy as np + + +def bulk_model_reference_array(): + + initial_temperatures = np.array([196.0, 216.0, 236.0]) + updrafts = np.array([0.05, 0.1, 0.3, 0.5, 1.0, 3.0, 5.0, 10.0]) + + dim_size = (np.shape(initial_temperatures)[0], np.shape(updrafts)[0]) + ni_bulk_ref = np.zeros(dim_size) + + # T = 196 + ni_bulk_ref[0, 0] = 643686.1316903427 + ni_bulk_ref[0, 1] = 2368481.0609527444 + ni_bulk_ref[0, 2] = 20160966.984670535 + ni_bulk_ref[0, 3] = 49475281.81718969 + ni_bulk_ref[0, 4] = 131080662.23620115 + ni_bulk_ref[0, 5] = 401046528.70428866 + ni_bulk_ref[0, 6] = 627442148.3402529 + ni_bulk_ref[0, 7] = 1151707310.2210448 + + # T = 216 + ni_bulk_ref[1, 0] = 60955.84292640147 + ni_bulk_ref[1, 1] = 189002.0792186534 + ni_bulk_ref[1, 2] = 1200751.6897658105 + ni_bulk_ref[1, 3] = 2942110.815055958 + ni_bulk_ref[1, 4] = 10475282.894692907 + ni_bulk_ref[1, 5] = 90871045.40856971 + ni_bulk_ref[1, 6] = 252175505.460412 + ni_bulk_ref[1, 7] = 860335156.4717773 + + # T = 236 + ni_bulk_ref[2, 0] = 13049.108886452004 + ni_bulk_ref[2, 1] = 40422.244759544985 + ni_bulk_ref[2, 2] = 237862.49854786208 + ni_bulk_ref[2, 3] = 545315.7805748513 + ni_bulk_ref[2, 4] = 1707801.469906006 + ni_bulk_ref[2, 5] = 11128055.66932415 + ni_bulk_ref[2, 6] = 27739585.111447476 + ni_bulk_ref[2, 7] = 101799566.47225031 + + return initial_temperatures, updrafts, ni_bulk_ref diff --git a/PySDM/source/examples/PySDM_examples/Spichtinger_et_al_2023/data/simulation_data.py b/PySDM/source/examples/PySDM_examples/Spichtinger_et_al_2023/data/simulation_data.py new file mode 100644 index 0000000000000000000000000000000000000000..3b7f6cb59708557898140f57ec12d0901487ca00 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Spichtinger_et_al_2023/data/simulation_data.py @@ -0,0 +1,22 @@ +import numpy as np + + +def saved_simulation_ensemble_mean(): + + ni_ens_mean = np.array( + [ + [0.00000000e00, 0.00000000e00, 0.00000000e00], + [0.00000000e00, 0.00000000e00, 0.00000000e00], + [1.62069821e03, 0.00000000e00, 0.00000000e00], + [5.25377025e06, 9.75512904e05, 4.51431097e05], + [3.67137290e07, 5.45240337e06, 1.53884530e06], + [7.96514420e07, 1.13878118e07, 3.26386880e06], + [2.19385480e08, 3.62240242e07, 9.00657591e06], + [1.00631095e09, 2.34443408e08, 4.90577208e07], + [1.73457062e09, 5.20774276e08, 1.16040316e08], + ] + ) + T = np.array([196.0, 216.0, 236.0]) + w = np.array([0.01, 0.03, 0.05, 0.1, 0.3, 0.5, 1.0, 3.0, 5.0]) + + return T, w, ni_ens_mean diff --git a/PySDM/source/examples/PySDM_examples/Spichtinger_et_al_2023/fig_B1.ipynb b/PySDM/source/examples/PySDM_examples/Spichtinger_et_al_2023/fig_B1.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..a5a011c138ca79394033ba4a1c8b742eabbbd213 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Spichtinger_et_al_2023/fig_B1.ipynb @@ -0,0 +1,2023 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "340efac1581b14ae", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Spichtinger_et_al_2023/fig_B1.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Spichtinger_et_al_2023/fig_B1.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Spichtinger_et_al_2023/fig_B1.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "a6b09eaef75333df", + "metadata": {}, + "source": [ + "\n", + "#### based on Fig. B1 from Spichtinger et al. 2023 (ACP) \"_Impact of formulations of the homogeneous nucleation rate on ice nucleation events in cirrus_\"\n", + "\n", + "(work in progress)\n", + "\n", + "https://doi.org/10.5194/acp-23-2035-2023" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "initial_id", + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-15T14:20:31.231964Z", + "start_time": "2025-05-15T14:20:31.221457Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "69ce798ec8b87121", + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-15T14:32:48.496132Z", + "start_time": "2025-05-15T14:32:47.161494Z" + } + }, + "outputs": [], + "source": [ + "import json\n", + "from PySDM_examples.Spichtinger_et_al_2023 import Simulation, Settings\n", + "from PySDM_examples.Spichtinger_et_al_2023.data import simulation_data, reference_bulk\n", + "import numpy as np\n", + "from matplotlib import pyplot\n", + "from open_atmos_jupyter_utils import show_plot" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "fabd7ea8e8a11996", + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-15T14:20:46.119340Z", + "start_time": "2025-05-15T14:20:46.116341Z" + } + }, + "outputs": [], + "source": [ + "calculate_data = False\n", + "save_to_file = False\n", + "read_from_json = False" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "1acd9d93e2af385c", + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-19T10:08:33.165481Z", + "start_time": "2025-05-19T10:08:32.903294Z" + } + }, + "outputs": [], + "source": [ + "if calculate_data:\n", + "\n", + " initial_temperatures = np.array([196.0, 216.0, 236.0])\n", + " updrafts = np.array([0.01, 0.03, 0.05, 0.1, 0.3, 0.5, 1.0, 3.0, 5.0])\n", + " number_of_ensemble_runs = 5\n", + " seeds = [124670285330, 439785398735, 9782539783258, 12874192127481, 12741731272]\n", + "\n", + " dim_updrafts = len(updrafts)\n", + " dim_initial_temperatures = len(initial_temperatures)\n", + "\n", + " number_concentration_ice = np.zeros(\n", + " [dim_updrafts, dim_initial_temperatures, number_of_ensemble_runs]\n", + " )\n", + "\n", + " for i in range(dim_updrafts):\n", + " for j in range(dim_initial_temperatures):\n", + " for k in range(number_of_ensemble_runs):\n", + " setting = Settings(n_sd=50000,\n", + " w_updraft=updrafts[i],\n", + " T0=initial_temperatures[j],\n", + " seed=seeds[k],\n", + " dt=0.1)\n", + " model = Simulation(setting)\n", + " number_concentration_ice[i, j, k] = model.run()\n", + "\n", + " if save_to_file:\n", + " file_name = \"data/ni_w_T_ens_\" + str(number_of_ensemble_runs) + \".json\"\n", + " data_file = {\n", + " \"ni\": number_concentration_ice.tolist(),\n", + " \"T\": initial_temperatures.tolist(),\n", + " \"w\": updrafts.tolist(),\n", + " }\n", + " with open(file_name, \"w\", encoding=\"utf-8\") as file:\n", + " json.dump(data_file, file)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "3821d0f892f4af29", + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-15T14:32:54.180974Z", + "start_time": "2025-05-15T14:32:51.733640Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-08T14:41:54.674707\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "c70e79a37b70462485cffc12b0adc5f7", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_B1.pdf
\"), HTML(value=\" 0.0 and RHi < 130.0: + print("break") + break + RHi_old = RHi + + return output["ni"][-1] diff --git a/PySDM/source/examples/PySDM_examples/Srivastava_1982/__init__.py b/PySDM/source/examples/PySDM_examples/Srivastava_1982/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..a851a3dc89093c63faceae85cfee656a3da749aa --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Srivastava_1982/__init__.py @@ -0,0 +1,19 @@ +""" +box-model examples feat. analytic solution for breakup from +[Srivastava 1982 (JAS)](https://doi.org/10.1175/1520-0469(1982)039%3C1317:ASMOPC%3E2.0.CO;2) + +figures.ipynb: +.. include:: ./figures.ipynb.badges.md +""" + +from .equations import Equations, EquationsHelpers +from .example import ( + add_to_plot_simulation_results, + coalescence_and_breakup_eq13, + compute_log_space, + get_coalescence_analytic_results, + get_processed_results, + get_pysdm_secondary_products, +) +from .settings import Settings, SimProducts +from .simulation import Simulation diff --git a/PySDM/source/examples/PySDM_examples/Srivastava_1982/equations.py b/PySDM/source/examples/PySDM_examples/Srivastava_1982/equations.py new file mode 100644 index 0000000000000000000000000000000000000000..f7d05697761648a293de7dacd8be31ee4e8730ca --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Srivastava_1982/equations.py @@ -0,0 +1,96 @@ +import numpy as np + + +class Equations: + """Equations from Srivastava 1982: "A Simple Model of Particle Coalescence and Breakup" + (https://doi.org/10.1175/1520-0469(1982)039%3C1317:ASMOPC%3E2.0.CO;2) + note: all equations assume constant fragment mass""" + + @property + def alpha_star(self): + """see eq. 6""" + return self._alpha_star or self.alpha / self.c / self.M + + @property + def beta_star(self): + """see eq. 6""" + return self._beta_star or self.beta / self.c + + def tau(self, t): + """see eq. 6""" + return self.c * self.M * t + + def __init__( + self, *, M=None, c=None, alpha=None, beta=None, alpha_star=None, beta_star=None + ): + if alpha_star and (alpha or M or c): + raise ValueError("conflicting parameter") + self.M = M + self.c = c + self.alpha = alpha + self.beta = beta + + self._beta_star = beta_star + self._alpha_star = alpha_star + + if alpha_star and beta_star: + amb = alpha_star - beta_star + self._A = amb / 2 / alpha_star + self._B = 1 / np.sqrt( + (0.5 / alpha_star + beta_star / alpha_star) + + amb**2 / (4 * alpha_star**2) + ) + + def eq12(self): + """equilibrium (τ→∞) mean mass under collisions and spontaneous breakup + (no collisional breakup) + expressed as a ratio to fragment mass (i.e., dimensionless)""" + equilibrium_mean_mass_to_frag_mass_ratio = ( + 0.5 + (0.25 + 0.5 / self.alpha_star) ** 0.5 + ) + return equilibrium_mean_mass_to_frag_mass_ratio + + def eq13(self, m0, tau): + """mean mass expressed as a ratio to fragment mass as a function of + dimensionless scaled time (τ) under coalescence and collisional breakup + (no spontaneous breakup)""" + mean_mass_to_frag_mass_ratio = self._eq13(m0, tau) + return mean_mass_to_frag_mass_ratio + + def _eq13(self, m0, tau): + ebt = np.exp(-self.beta_star * tau) + return m0 * ebt + (1 + 0.5 / self.beta_star) * (1 - ebt) + + def eq14(self): + """equilibrium (τ→∞) mean mass expressed as a ratio to fragment mass for + under collisional merging and breakup (no spontaneous breakup)""" + equilibrium_mean_mass_to_frag_mass_ratio = 1 + 0.5 / self.beta_star + return equilibrium_mean_mass_to_frag_mass_ratio + + def eq15(self, m): + return (m - self._A) * self._B + + def eq15_m_of_y(self, y): + return (y / self._B) + self._A + + def eq16(self, tau): + return tau * self.alpha_star / self._B + + def eq10(self, m0, tau): + """ratio of mean mass to fragment size mass as a function of scaled time + for the case of coalescence only""" + mean_mass_to_frag_mass_ratio = m0 + tau / 2 + return mean_mass_to_frag_mass_ratio + + +class EquationsHelpers: + def __init__(self, total_volume, total_number_0, rho, frag_mass): + self.total_volume = total_volume + self.total_number_0 = total_number_0 + self.rho = rho + self.frag_mass = frag_mass + + def m0(self): + mean_volume_0 = self.total_volume / self.total_number_0 + m0 = self.rho * mean_volume_0 / self.frag_mass + return m0 diff --git a/PySDM/source/examples/PySDM_examples/Srivastava_1982/example.py b/PySDM/source/examples/PySDM_examples/Srivastava_1982/example.py new file mode 100644 index 0000000000000000000000000000000000000000..10d86cce1e70a7193bf31d58590b8f8c92a08c42 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Srivastava_1982/example.py @@ -0,0 +1,266 @@ +from collections import namedtuple + +import numpy as np +from matplotlib import pyplot +from PySDM_examples.Srivastava_1982.equations import Equations, EquationsHelpers +from PySDM_examples.Srivastava_1982.settings import SimProducts +from PySDM_examples.Srivastava_1982.simulation import Simulation + +from PySDM.dynamics import Collision +from PySDM.dynamics.collisions.breakup_efficiencies import ConstEb +from PySDM.dynamics.collisions.breakup_fragmentations import ConstantMass +from PySDM.dynamics.collisions.coalescence_efficiencies import ConstEc +from PySDM.dynamics.collisions.collision_kernels import ConstantK + +NO_BOUNCE = ConstEb(1) + + +def coalescence_and_breakup_eq13( + settings=None, n_steps=256, n_realisations=2, title=None, warn_overflows=True +): + # arrange + seeds = list(range(n_realisations)) + + collision_rate = settings.srivastava_c + settings.srivastava_beta + simulation = Simulation( + n_steps=n_steps, + settings=settings, + collision_dynamic=Collision( + collision_kernel=ConstantK(a=collision_rate), + coalescence_efficiency=ConstEc(settings.srivastava_c / collision_rate), + breakup_efficiency=NO_BOUNCE, + fragmentation_function=ConstantMass(c=settings.frag_mass), + warn_overflows=warn_overflows, + ), + ) + + x = np.arange(n_steps + 1, dtype=float) + sim_products = simulation.run_convergence_analysis(x, seeds=seeds) + + secondary_products = get_pysdm_secondary_products( + products=sim_products, total_volume=settings.total_volume + ) + + pysdm_results = get_processed_results(secondary_products) + + equations = Equations( + M=settings.total_volume * settings.rho / settings.frag_mass, + c=settings.srivastava_c, + beta=settings.srivastava_beta, + ) + equation_helper = EquationsHelpers( + settings.total_volume, + settings.total_number_0, + settings.rho, + frag_mass=settings.frag_mass, + ) + m0 = equation_helper.m0() + + x_log = compute_log_space(x) + analytic_results = get_breakup_coalescence_analytic_results( + equations, settings, m0, x, x_log + ) + + prods = [ + k + for k in list(pysdm_results.values())[0].keys() + if k != SimProducts.PySDM.total_volume.name + ] + + add_to_plot_simulation_results( + prods, + settings.n_sds, + x, + pysdm_results=pysdm_results, + analytic_results=analytic_results, + title=title, + ) + + results = namedtuple("_", "pysdm, analytic")( + pysdm=pysdm_results, analytic=analytic_results + ) + return results + + +def get_processed_results(res): + processed = {} + for n_sd in res.keys(): + processed[n_sd] = {} + for prod in res[n_sd].keys(): + processed[n_sd][prod] = {} + all_runs = np.asarray(list(res[n_sd][prod].values())) + + processed[n_sd][prod]["avg"] = np.mean(all_runs, axis=0) + processed[n_sd][prod]["max"] = np.max(all_runs, axis=0) + processed[n_sd][prod]["min"] = np.min(all_runs, axis=0) + processed[n_sd][prod]["std"] = np.std(all_runs, axis=0) + + return processed + + +# TODO #1045 (not needed) +def get_pysdm_secondary_products(products, total_volume): + pysdm_results = products + for n_sd in products.keys(): + pysdm_results[n_sd][ + SimProducts.Computed.mean_drop_volume_total_volume_ratio.name + ] = {} + + for k in pysdm_results[n_sd][SimProducts.PySDM.total_numer.name].keys(): + pysdm_results[n_sd][ + SimProducts.Computed.mean_drop_volume_total_volume_ratio.name + ][k] = compute_drop_volume_total_volume_ratio( + mean_volume=total_volume + / products[n_sd][SimProducts.PySDM.total_numer.name][k], + total_volume=total_volume, + ) + return pysdm_results + + +def get_coalescence_analytic_results(equations, settings, m0, x, x_log): + mean_mass10 = ( + equations.eq10(m0, equations.tau(x * settings.dt)) * settings.frag_mass + ) + mean_mass_ratio_log = equations.eq10(m0, equations.tau(x_log * settings.dt)) + + return get_analytic_results(equations, settings, mean_mass10, mean_mass_ratio_log) + + +def get_breakup_coalescence_analytic_results(equations, settings, m0, x, x_log): + mean_mass13 = ( + equations.eq13(m0, equations.tau(x * settings.dt)) * settings.frag_mass + ) + mean_mass_ratio_log = equations.eq13(m0, equations.tau(x_log * settings.dt)) + + return get_analytic_results(equations, settings, mean_mass13, mean_mass_ratio_log) + + +def get_analytic_results(equations, settings, mean_mass, mean_mass_ratio): + res = {} + res[SimProducts.Computed.mean_drop_volume_total_volume_ratio.name] = ( + compute_drop_volume_total_volume_ratio( + mean_volume=mean_mass / settings.rho, total_volume=settings.total_volume + ) + ) + res[SimProducts.PySDM.total_numer.name] = equations.M / mean_mass_ratio + return res + + +def compute_log_space(x, shift=0, num_points=1000, eps=1e-1): + assert eps < x[1] + return ( + np.logspace(np.log10(x[0] if x[0] != 0 else eps), np.log10(x[-1]), num_points) + + shift + ) + + +# TODO #1045 (not needed) +def compute_drop_volume_total_volume_ratio(mean_volume, total_volume): + return mean_volume / total_volume * 100 + + +def add_to_plot_simulation_results( + prods, + n_sds, + x, + pysdm_results=None, + analytic_results=None, + title=None, +): + fig = pyplot.figure(layout="constrained", figsize=(10, 4)) + _wide = 14 + _shrt = 8 + _mrgn = 1 + gs = fig.add_gridspec(nrows=20, ncols=2 * _wide + _shrt + 2 * _mrgn) + axs = ( + fig.add_subplot(gs[2:, _shrt + _mrgn : _shrt + _mrgn + _wide]), + fig.add_subplot(gs[2:, 0:_shrt]), + fig.add_subplot(gs[2:, _shrt + 2 * _mrgn + _wide :]), + ) + axs[1].set_ylim([6, 3500]) + + expons = [3, 5, 7, 9, 11] + + axs[1].set_yscale(SimProducts.PySDM.super_particle_count.plot_yscale) + axs[1].set_yticks([2**e for e in expons], [f"$2^{{{e}}}$" for e in expons]) + + if title: + fig.suptitle(title) + + ylims = {} + for prod in prods: + ylims[prod] = (np.inf, -np.inf) + for n_sd in n_sds: + ylims[prod] = ( + min(ylims[prod][0], 0.75 * np.amin(pysdm_results[n_sd][prod]["avg"])), + max(ylims[prod][1], 1.25 * np.amax(pysdm_results[n_sd][prod]["avg"])), + ) + + for i, prod in enumerate(prods): + # plot numeric + if pysdm_results: + for n_sd in n_sds: + y_model = pysdm_results[n_sd][prod] + + axs[i].step( + x, + y_model["avg"], + where="mid", + label=f"initial #SD: {n_sd}", + linewidth=1 + np.log(n_sd) / 3, + ) + axs[i].fill_between( + x, + y_model["avg"] - y_model["std"], + y_model["avg"] + y_model["std"], + alpha=0.2, + ) + + # plot analytic + if analytic_results: + add_analytic_result_to_axs(axs[i], prod, x, analytic_results) + + # cosmetics + axs[i].set_ylabel(SimProducts.get_prod_by_name(prod).plot_title) + + axs[i].grid() + axs[i].set_xlabel("step: t / dt") + + if prod != SimProducts.PySDM.super_particle_count.name: + bottom = ylims[prod][0] + top = ylims[prod][1] + + slope = ( + pysdm_results[n_sds[-1]][prod]["avg"][-1] + - pysdm_results[n_sds[-1]][prod]["avg"][0] + ) + if prod == SimProducts.PySDM.total_numer.name and slope < 0: + axs[i].set_ylim(0.2 * bottom, top) + else: + axs[i].set_ylim(bottom, top) + + axs[0].legend() + return fig, axs + + +def add_analytic_result_to_axs(axs_i, prod, x, res, key=""): + if prod != SimProducts.PySDM.super_particle_count.name: + x_theory = x + y_theory = res[prod] + + if prod == SimProducts.PySDM.total_numer.name: + if y_theory.shape != x_theory.shape: + x_theory = compute_log_space(x) + + axs_i.set_yscale(SimProducts.PySDM.total_numer.plot_yscale) + axs_i.set_xscale(SimProducts.PySDM.total_numer.plot_xscale) + axs_i.set_xlim(x_theory[0], None) + + axs_i.plot( + x_theory, + y_theory, + label=f"analytic {key}", + linestyle=":", + linewidth=2, + color="black", + ) diff --git a/PySDM/source/examples/PySDM_examples/Srivastava_1982/figures.ipynb b/PySDM/source/examples/PySDM_examples/Srivastava_1982/figures.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..36b8f5e84d1fae0672861ca9da6cbc492e7adf2e --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Srivastava_1982/figures.ipynb @@ -0,0 +1,2632 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Srivastava_1982/figures.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Srivastava_1982/figures.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Srivastava_1982/figures.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "TODO #1417" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:46:34.616957Z", + "start_time": "2024-02-01T07:46:34.611862Z" + }, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:46:50.777167Z", + "start_time": "2024-02-01T07:46:50.771110Z" + }, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "from matplotlib import pyplot\n", + "import numpy as np\n", + "from PySDM_examples.Srivastava_1982.equations import Equations\n", + "from open_atmos_jupyter_utils import show_plot" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "## Figure 1" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:46:36.530872Z", + "start_time": "2024-02-01T07:46:36.148902Z" + }, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-02-01T08:46:36.502765\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "051c06cb71564492bd950cc8dad49862", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./fig1.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "alpha_star = 1e-5\n", + "beta_star = 1e-4\n", + "\n", + "pyplot.title(\"fig 1 (note: value from paper: m_E=215)\")\n", + "for m0 in (100, 450):\n", + " eqs = Equations(alpha_star=alpha_star, beta_star=beta_star)\n", + " tau = np.linspace(0, 900)\n", + " y0 = eqs.eq15(m0)\n", + " x = eqs.eq16(tau)\n", + " y = (y0 + np.tanh(x)) / (1 + y0 * np.tanh(x))\n", + " pyplot.plot(tau, eqs.eq15_m_of_y(y), label=f\"$m(τ, m_0={m0})$\")\n", + "\n", + "pyplot.axhline(eqs.eq12(), linestyle=\"--\", label=\"$m_E$\")\n", + "pyplot.xlabel(\"τ\")\n", + "pyplot.ylabel(\"mass\")\n", + "pyplot.grid()\n", + "pyplot.legend()\n", + "show_plot('fig1.pdf')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "## coalescence (equation 13) and breakup (equation 14) analytic" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-01T07:46:36.648339Z", + "start_time": "2024-02-01T07:46:36.544540Z" + }, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-02-01T08:46:36.582603\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "004a03e599ba4996b34013df27237e70", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./eq_13_14.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "alpha_star = 1e-5\n", + "beta_star = 1e-4\n", + "eqs = Equations(alpha_star=alpha_star, beta_star=beta_star)\n", + "\n", + "tau = np.linspace(0, 90000)\n", + "m0 = 100\n", + "pyplot.title(\"equations (13) and (14)\")\n", + "pyplot.plot(tau, eqs.eq13(m0, tau), label=f\"m(τ, m_0={m0})\")\n", + "pyplot.axhline(eqs.eq14(), linestyle=\"--\", label=\"$m_E$\")\n", + "pyplot.xlabel(\"τ\")\n", + "pyplot.ylabel(\"mass\")\n", + "pyplot.grid()\n", + "pyplot.legend()\n", + "pyplot.show()\n", + "show_plot('eq_13_14.pdf')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/PySDM/source/examples/PySDM_examples/Srivastava_1982/settings.py b/PySDM/source/examples/PySDM_examples/Srivastava_1982/settings.py new file mode 100644 index 0000000000000000000000000000000000000000..8765ce6bec0adae5e841b9af72532674e05e6d1f --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Srivastava_1982/settings.py @@ -0,0 +1,79 @@ +from PySDM.backends import CPU +from PySDM.physics import constants_defaults + +DUMMY_FRAG_MASS = -1 + + +class SimProd: + def __init__(self, name, plot_title=None, plot_xscale=None, plot_yscale=None): + self.name = name + self.plot_title = plot_title or name + self.plot_yscale = plot_yscale + self.plot_xscale = plot_xscale + + +class SimProducts: + class PySDM: + total_numer = SimProd( + name="total numer", + plot_title="total droplet numer", + plot_xscale="log", + plot_yscale="log", + ) + total_volume = SimProd(name="total volume") + super_particle_count = SimProd( + name="super-particle count", plot_xscale="log", plot_yscale="log" + ) + + class Computed: + mean_drop_volume_total_volume_ratio = SimProd( + name="mean drop volume / total volume %", + plot_title="mean drop mass / total mass %", + ) + + @staticmethod + def get_prod_by_name(name): + for class_obj in (SimProducts.PySDM, SimProducts.Computed): + for attribute_str in dir(class_obj): + if not attribute_str.startswith("__"): + attribute = getattr(class_obj, attribute_str) + if attribute.name == name: + return attribute + return None + + +class Settings: + """interprets parameters from Srivastava 1982 in PySDM context""" + + def __init__( + self, + *, + n_sds, + dt, + dv, + total_number, + drop_mass_0, + srivastava_c, + srivastava_beta=None, + frag_mass=DUMMY_FRAG_MASS, + rho=constants_defaults.rho_w, + backend_class=CPU, + ): + self.backend_class = backend_class + + self.rho = rho + self.total_number_0 = total_number + self.total_volume = self.total_number_0 * drop_mass_0 / self.rho + self.dt = dt + self.dv = dv + self.frag_mass = frag_mass + + self.prods = ( + SimProducts.PySDM.total_volume.name, + SimProducts.PySDM.total_numer.name, + SimProducts.PySDM.super_particle_count.name, + ) + self.n_sds = n_sds + + self.srivastava_c = srivastava_c + self.srivastava_beta = srivastava_beta diff --git a/PySDM/source/examples/PySDM_examples/Srivastava_1982/simulation.py b/PySDM/source/examples/PySDM_examples/Srivastava_1982/simulation.py new file mode 100644 index 0000000000000000000000000000000000000000..3e8746961e65c188afb137b9ce0395b0b6a3bf85 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Srivastava_1982/simulation.py @@ -0,0 +1,85 @@ +import numpy as np +from PySDM_examples.Srivastava_1982.settings import SimProducts + +from PySDM import Builder, Formulae +from PySDM.environments import Box +from PySDM.products import SuperDropletCountPerGridbox, VolumeFirstMoment, ZerothMoment + + +class Simulation: + def __init__( + self, n_steps, settings, collision_dynamic=None, double_precision=True + ): + self.collision_dynamic = collision_dynamic + self.settings = settings + self.n_steps = n_steps + + self.double_precision = double_precision + + self.simulation_res = { + n_sd: {prod: {} for prod in self.settings.prods} + for n_sd in self.settings.n_sds + } + + def build(self, n_sd, seed, products): + env = Box(dt=self.settings.dt, dv=self.settings.dv) + builder = Builder( + backend=self.settings.backend_class( + formulae=Formulae( + constants={"rho_w": self.settings.rho}, + fragmentation_function="ConstantMass", + seed=seed, + ), + double_precision=self.double_precision, + ), + n_sd=n_sd, + environment=env, + ) + builder.add_dynamic(self.collision_dynamic) + particulator = builder.build( + products=products, + attributes={ + "multiplicity": np.full(n_sd, self.settings.total_number_0 / n_sd), + "volume": np.full( + n_sd, + self.settings.total_volume / self.settings.total_number_0, + ), + }, + ) + return particulator + + def run_convergence_analysis(self, x, seeds): + for n_sd in self.settings.n_sds: + for seed in seeds: + products = ( + SuperDropletCountPerGridbox( + name=SimProducts.PySDM.super_particle_count.name + ), + VolumeFirstMoment(name=SimProducts.PySDM.total_volume.name), + ZerothMoment(name=SimProducts.PySDM.total_numer.name), + ) + + particulator = self.build(n_sd, seed, products) + + for prod in self.settings.prods: + self.simulation_res[n_sd][prod][seed] = np.full( + self.n_steps + 1, -np.inf + ) + + for step in range(len(x)): + if step != 0: + particulator.run(steps=1) + for prod in self.settings.prods: + (self.simulation_res[n_sd][prod][seed][step],) = ( + particulator.products[prod].get() + ) + + np.testing.assert_allclose( + actual=self.simulation_res[n_sd][ + SimProducts.PySDM.total_volume.name + ][seed], + desired=self.settings.total_volume, + rtol=1e-3, + ) + + return self.simulation_res diff --git a/PySDM/source/examples/PySDM_examples/Stewart_1975/__init__.py b/PySDM/source/examples/PySDM_examples/Stewart_1975/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..afdb351fb9650c8c9ff7d707d76761a340007728 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Stewart_1975/__init__.py @@ -0,0 +1,7 @@ +""" +based on Stewart 1975 (J. Geophys. Res.) +https://doi.org/10.1029/JC080i009p01133 + +fig_1.ipynb: +.. include:: ./fig_1.ipynb.badges.md +""" diff --git a/PySDM/source/examples/PySDM_examples/Stewart_1975/fig_1.ipynb b/PySDM/source/examples/PySDM_examples/Stewart_1975/fig_1.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..c2e729e59508d58d7e76e3191347f5cc12e9307e --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Stewart_1975/fig_1.ipynb @@ -0,0 +1,3069 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c79d78cdb747b88f", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Stewart_1975/fig_1.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Stewart_1975/fig_1.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Stewart_1975/fig_1.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "f79591b3517712d6", + "metadata": {}, + "source": [ + "### based on Fig. 1 from Stewart 1975 (J. Geophys. Res.) \"_Stable Isotope FractionationDue to Evaporation and Isotopic Exchange of Falling Waterdrops: Applications to Atmospheric Processes and Evaporation of Lakes_\" (https://doi.org/10.1029/JC080i009p01133)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "b647df9dbc16b65f", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-15T21:17:25.938615Z", + "start_time": "2025-04-15T21:17:25.929461Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "60e08ca8f2b2fa2b", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-15T21:17:25.948710Z", + "start_time": "2025-04-15T21:17:25.945438Z" + } + }, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "from PySDM import Formulae\n", + "from PySDM.physics import si, in_unit\n", + "from open_atmos_jupyter_utils import show_plot\n", + "\n", + "from PySDM_examples.Kinzer_And_Gunn_1951.table_1_and_2 import table1" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "c1cf8f167c8737a7", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-15T21:17:25.964308Z", + "start_time": "2025-04-15T21:17:25.958878Z" + } + }, + "outputs": [], + "source": [ + "formulae = Formulae(\n", + " ventilation='PruppacherAndRasmussen1979',\n", + " diffusion_thermics='Neglect',\n", + " air_dynamic_viscosity='ZografosEtAl1987',\n", + " terminal_velocity='RogersYau',\n", + " particle_shape_and_density='LiquidSpheres'\n", + ")\n", + "const = formulae.constants\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "1a88c53d14de54da", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-15T21:17:25.972569Z", + "start_time": "2025-04-15T21:17:25.970070Z" + } + }, + "outputs": [], + "source": [ + "radii = np.asarray(table1['Diameter [cm]'])[:-2] / 2 * si.cm \n", + "\n", + "temperature = 293.15 * si.K\n", + "eta_air\\\n", + " = formulae.air_dynamic_viscosity.eta_air(temperature)\n", + "air_density = const.p_STP/const.Rd/temperature\n", + "Sc = formulae.trivia.air_schmidt_number(\n", + " dynamic_viscosity=eta_air, \n", + " diffusivity=formulae.diffusion_thermics.D(\n", + " T=temperature, \n", + " p=const.p_STP\n", + " ), \n", + " density=air_density,\n", + ")\n", + "Re = formulae.particle_shape_and_density.reynolds_number(\n", + " radius=radii,\n", + " velocity_wrt_air=formulae.terminal_velocity.v_term(radii),\n", + " dynamic_viscosity=eta_air,\n", + " density=air_density,\n", + ")\n", + "sqrt_re_times_cbrt_sc = formulae.trivia.sqrt_re_times_cbrt_sc(Re=Re,Sc=Sc)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "cbe557905243f8f0", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-15T21:17:25.978487Z", + "start_time": "2025-04-15T21:17:25.976966Z" + } + }, + "outputs": [], + "source": [ + "def F_of_f(ventilation_coefficient):\n", + " return (\n", + " (ventilation_coefficient - 1) \n", + " / np.sqrt(Sc * Re / 4 / np.pi)\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "24bafd5305aae7e1", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-15T21:17:25.985046Z", + "start_time": "2025-04-15T21:17:25.983268Z" + } + }, + "outputs": [], + "source": [ + "ventilation_coefficient_kinzer_gunn\\\n", + " = (np.array(table1[f\"{int(formulae.trivia.K2C(temperature))} [deg C]\"][:-2]) \n", + " / 4 / np.pi / in_unit(radii,si.cm))\n", + "ventilation_factor_kinzer_gunn = F_of_f(ventilation_coefficient_kinzer_gunn)\n", + "\n", + "ventilation_coefficient_beard_pruppacher = formulae.ventilation.ventilation_coefficient(sqrt_re_times_cbrt_sc)\n", + "ventilation_factor_beard_pruppacher = F_of_f(ventilation_coefficient_beard_pruppacher)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "eeddfa81b58f2cc", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-15T21:17:26.121401Z", + "start_time": "2025-04-15T21:17:25.990812Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-04-15T23:17:26.104520\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.0, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "9342197088054cfa9edb878681b73fa9", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_1.pdf
\"), HTML(value=\"\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-04-15T23:17:26.236951\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.0, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ac31f80139f14cf3befd3f3d21a6ef11", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./ventilation_coeff.pdf
\")…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plot_K_G_coeff = plt.plot(\n", + " in_unit(radii, si.mm),\n", + " ventilation_coefficient_kinzer_gunn,\n", + " label='Kinzer & Gunn'\n", + ")\n", + "plot_B_P_coeff = plt.plot(\n", + " in_unit(radii, si.mm),\n", + " ventilation_coefficient_beard_pruppacher,\n", + " label='Beard & Pruppacher'\n", + ")\n", + "plt.xlabel(\"Radius [mm]\")\n", + "plt.ylabel(\"Ventilation coefficient\")\n", + "plt.grid()\n", + "plt.legend()\n", + "show_plot(\"ventilation_coeff.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "de5551a1734044f0", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-15T21:17:26.261628Z", + "start_time": "2025-04-15T21:17:26.259960Z" + } + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.12.8" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/PySDM/source/examples/PySDM_examples/Strzabala_2025_BEng/__init__.py b/PySDM/source/examples/PySDM_examples/Strzabala_2025_BEng/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..168e50fdb1cf8115a29d32baf65e8c6d76699262 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Strzabala_2025_BEng/__init__.py @@ -0,0 +1,8 @@ +# pylint: disable=invalid-name +""" +ParaView visualisation of a parcel-model example based on the test case from +[Pyrcel package docs](https://pyrcel.readthedocs.io/) + +paraview_parcel_model.ipynb: +.. include:: ./paraview.ipynb.badges.md +""" diff --git a/PySDM/source/examples/PySDM_examples/Strzabala_2025_BEng/paraview.ipynb b/PySDM/source/examples/PySDM_examples/Strzabala_2025_BEng/paraview.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..d49dee62e73729ad7e79fabd8299aa4ff718058b --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Strzabala_2025_BEng/paraview.ipynb @@ -0,0 +1,221 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "ea16d42c", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Strzabala_2025_BEng/paraview.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Strzabala_2025_BEng/paraview.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Strzabala_2025_BEng/paraview.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "bdde4d45", + "metadata": {}, + "source": [ + "Runs a parcel simulation based on Pyrcel documentation setup, and demonstrates export to VTK and visualisation using Paraview\n", + "\n", + "(requires `pvpython` command in PATH)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "1829d6ee", + "metadata": {}, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "6dcbcc6d", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import subprocess\n", + "import platform\n", + "import pathlib\n", + "from PySDM import Formulae\n", + "from PySDM.physics import si\n", + "from PySDM.initialisation.spectra import Lognormal\n", + "from PySDM.products import (\n", + " ParcelDisplacement,\n", + " AmbientTemperature,\n", + " AmbientDryAirDensity,\n", + " AmbientRelativeHumidity,\n", + " ParticleSizeSpectrumPerVolume,\n", + " ParticleVolumeVersusRadiusLogarithmSpectrum,\n", + ")\n", + "from PySDM.exporters import VTKExporterParcel\n", + "\n", + "from PySDM_examples.Pyrcel import Settings, Simulation\n", + "import PySDM_examples" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "cd6288b5", + "metadata": {}, + "outputs": [], + "source": [ + "settings = Settings(\n", + " dz=10 * si.m,\n", + " n_sd_per_mode=(25, 25),\n", + " aerosol_modes_by_kappa={\n", + " 0.54: Lognormal(\n", + " norm_factor=850 / si.cm**3,\n", + " m_mode=15 * si.nm,\n", + " s_geom=1.6,\n", + " ),\n", + " 1.2: Lognormal(\n", + " norm_factor=10 / si.cm**3,\n", + " m_mode=850 * si.nm,\n", + " s_geom=1.2,\n", + " ),\n", + " },\n", + " vertical_velocity=1.0 * si.m / si.s,\n", + " initial_pressure=775 * si.mbar,\n", + " initial_temperature=274 * si.K,\n", + " initial_relative_humidity=0.90,\n", + " displacement=1000 * si.m,\n", + " formulae=Formulae(constants={\"MAC\": 0.3}),\n", + ")\n", + "\n", + "dry_radius_bin_edges = np.logspace(\n", + " np.log10(1e-3 * si.um), np.log10(5e0 * si.um), 33, endpoint=False\n", + ")\n", + "\n", + "simulation = Simulation(\n", + " settings,\n", + " products=(\n", + " ParcelDisplacement(name=\"z\"),\n", + " AmbientRelativeHumidity(name=\"S_max_percent\", unit=\"%\", var=\"RH\"),\n", + " AmbientTemperature(name=\"T\"),\n", + " ParticleSizeSpectrumPerVolume(\n", + " name=\"dry:dN/dR\", radius_bins_edges=dry_radius_bin_edges, dry=True\n", + " ),\n", + " ParticleVolumeVersusRadiusLogarithmSpectrum(\n", + " name=\"dry:dV/dlnR\", radius_bins_edges=dry_radius_bin_edges, dry=True\n", + " ),\n", + " AmbientDryAirDensity(),\n", + " ),\n", + " mass_of_dry_air = 66666 * si.kg,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "3ff50a25", + "metadata": {}, + "outputs": [], + "source": [ + "output = simulation.run()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "15d59657", + "metadata": {}, + "outputs": [], + "source": [ + "e = VTKExporterParcel(n_sd=simulation.particulator.n_sd, output=output, mass_of_dry_air=simulation.particulator.environment.mass_of_dry_air)\n", + "for step in settings.output_steps:\n", + " e.export_products(step, simulation)\n", + " e.export_attributes(step, simulation)\n", + "e.write_pvd()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4a6ea0e6", + "metadata": {}, + "outputs": [], + "source": [ + "product = pathlib.Path(\"./output/sd_products.pvd\").absolute()\n", + "attributes = pathlib.Path(\"./output/sd_attributes.pvd\").absolute()\n", + "\n", + "paraview_script = pathlib.Path(PySDM_examples.__file__).parent / 'Strzabala_2025_BEng' / \"paraview_parcel_model.py\"\n", + "\n", + "args = [\n", + " \"pvpython\",\n", + " \"--force-offscreen-rendering\",\n", + " str(paraview_script),\n", + " \"--sd-products-pvd\",\n", + " str(product),\n", + " \"--sd-attributes-pvd\",\n", + " str(attributes),\n", + " \"--output-animation-path\",\n", + " str(pathlib.Path(\"./output/parcel_animation.ogv\").absolute()),\n", + " \"--output-screenshot-path\",\n", + " str(pathlib.Path(\"./output/last_frame.pdf\").absolute()),\n", + "]\n", + "result = subprocess.run(\n", + " args,\n", + " check=platform.system() != \"Windows\",\n", + " capture_output=True,\n", + " text=True,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7e65c734-7012-4eff-8099-daa1119cb97d", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "baea1aff-648f-41c0-b97b-4bf8f0b6de31", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2d64ba1c-5eb9-4141-90a9-5a3da6ad7a71", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/PySDM/source/examples/PySDM_examples/Strzabala_2025_BEng/paraview_parcel_model.py b/PySDM/source/examples/PySDM_examples/Strzabala_2025_BEng/paraview_parcel_model.py new file mode 100644 index 0000000000000000000000000000000000000000..85cc5488eb3f4d7577ab2dc8f0d0ca159400b131 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Strzabala_2025_BEng/paraview_parcel_model.py @@ -0,0 +1,244 @@ +#!/usr/bin/env pvpython +""" +ParaView pvpython script for visualizing sd_products and sd_attributes. +""" + +import argparse +from collections import namedtuple +import pathlib +from paraview import simple as pvs # pylint: disable=import-error + +pvs._DisableFirstRenderCameraReset() # pylint: disable=protected-access + + +def cli_using_argparse(argparse_parser): + """ + Command line interface using argparse. + """ + argparse_parser.add_argument( + "--sd-products-pvd", + dest="sd_products_pvd", + help="Path to sd_products.pvd", + ) + argparse_parser.add_argument( + "--sd-attributes-pvd", + dest="sd_attributes_pvd", + help="Path to sd_attributes.pvd", + ) + argparse_parser.add_argument( + "--output-animation-path", + help="Output path for the animation file.", + ) + argparse_parser.add_argument( + "--output-screenshot-path", + help="Output path for the screenshot file.", + ) + + argparse_parser.add_argument( + "--scalarbar-title-size", + type=int, + default=30, + help="Font size for scalar bar titles.", + ) + argparse_parser.add_argument( + "--scalarbar-label-size", + type=int, + default=30, + help="Font size for scalar bar labels.", + ) + argparse_parser.add_argument( + "--axes-title-size", + type=int, + default=30, + help="Font size for axes titles.", + ) + argparse_parser.add_argument( + "--axes-label-size", + type=int, + default=30, + help="Font size for axes labels.", + ) + + +parser = argparse.ArgumentParser( + description="ParaView pvpython script for visualizing sd_products and sd_attributes PVD files." +) +cli_using_argparse(parser) +args = parser.parse_args() + +sd_productspvd = pvs.PVDReader( + registrationName="sd_products.pvd", FileName=args.sd_products_pvd +) +sd_attributespvd = pvs.PVDReader( + registrationName="sd_attributes.pvd", FileName=args.sd_attributes_pvd +) + +setup = { + "renderView1": pvs.GetActiveViewOrCreate("RenderView"), + "sd_productspvdDisplay": pvs.Show( + sd_productspvd, + pvs.GetActiveViewOrCreate("RenderView"), + "UnstructuredGridRepresentation", + ), + "sd_attributespvdDisplay": pvs.Show( + sd_attributespvd, + pvs.GetActiveViewOrCreate("RenderView"), + "UnstructuredGridRepresentation", + ), + "rHlookup_table": pvs.GetColorTransferFunction("RH"), + "volumelookup_table": pvs.GetColorTransferFunction("volume"), +} + +animation_setup = namedtuple("setup", setup.keys())(**setup) + + +def configure_color_bar_and_display( + display, lookup_table, kind, *, anim_setup=animation_setup +): + """ + Configure color bar and display settings. + """ + display.Representation = "Surface" + display.SetScalarBarVisibility(anim_setup.renderView1, True) + color_bar = pvs.GetScalarBar(lookup_table, anim_setup.renderView1) + color_bar.LabelColor = [0.0, 0.0, 0.0] + color_bar.DrawScalarBarOutline = 1 + color_bar.ScalarBarOutlineColor = [0.0, 0.0, 0.0] + color_bar.TitleColor = [0.0, 0.0, 0.0] + + color_bar.TitleFontSize = args.scalarbar_title_size + color_bar.LabelFontSize = args.scalarbar_label_size + + if kind == "prod": + display.Opacity = 0.4 + display.DisableLighting = 1 + display.Diffuse = 0.76 + lookup_table.RescaleTransferFunction(90.0, 101.0) + lookup_table.ApplyPreset("Black, Blue and White", True) + lookup_table.NanColor = [0.67, 1.0, 1.0] + else: + display.PointSize = 13.0 + display.RenderPointsAsSpheres = 1 + display.Interpolation = "PBR" + lookup_table.RescaleTransferFunction(1e-18, 1e-13) + lookup_table.ApplyPreset("Cold and Hot", True) + lookup_table.MapControlPointsToLogSpace() + lookup_table.UseLogScale = 1 + lookup_table.NumberOfTableValues = 16 + lookup_table.InvertTransferFunction() + + +def configure_data_axes_grid(display, kind): + """ + Configure data axes grid settings. + """ + display.DataAxesGrid.GridAxesVisibility = 1 + display.DataAxesGrid.XTitle = "" + display.DataAxesGrid.YTitle = "" + display.DataAxesGrid.XAxisUseCustomLabels = 1 + display.DataAxesGrid.YAxisUseCustomLabels = 1 + + display.DataAxesGrid.XTitleFontSize = args.axes_title_size + display.DataAxesGrid.XLabelFontSize = args.axes_label_size + display.DataAxesGrid.YTitleFontSize = args.axes_title_size + display.DataAxesGrid.YLabelFontSize = args.axes_label_size + + if kind == "prod": + display.DataAxesGrid.ZTitle = "" + display.DataAxesGrid.GridColor = [0.0, 0.0, 0.0] + display.DataAxesGrid.ShowGrid = 1 + display.DataAxesGrid.LabelUniqueEdgesOnly = 0 + display.DataAxesGrid.ZAxisUseCustomLabels = 1 + display.DataAxesGrid.ZTitleFontSize = args.axes_title_size + display.DataAxesGrid.ZLabelFontSize = args.axes_label_size + else: + display.DataAxesGrid.ZTitle = " Z [m] " + display.DataAxesGrid.ZLabelColor = [0.0, 0.0, 0.0] + display.DataAxesGrid.ZTitleColor = [0.0, 0.0, 0.0] + display.DataAxesGrid.ZTitleFontSize = args.axes_title_size + display.DataAxesGrid.ZLabelFontSize = args.axes_label_size + + +def configure_view_appearance(render_view): + """ + Configure view appearance settings.""" + render_view.OrientationAxesLabelColor = [0.0, 0.0, 0.0] + render_view.OrientationAxesOutlineColor = [0.0, 0.0, 0.0] + render_view.OrientationAxesXVisibility = 0 + render_view.OrientationAxesYVisibility = 0 + render_view.OrientationAxesZColor = [0.0, 0.0, 0.0] + render_view.UseColorPaletteForBackground = 0 + render_view.Background = [1.0, 1.0, 1.0] + + +def set_camera_view(render_view): + """ + Set camera view settings. + """ + layout1 = pvs.GetLayout() + layout1.SetSize(1592, 1128) + render_view.CameraPosition = [ + 1548.95, + -1349.49, + 699.27, + ] + render_view.CameraFocalPoint = [ + -1.37e-13, + 3.18e-13, + 505.00, + ] + render_view.CameraViewUp = [ + -0.07, + 0.06, + 0.99, + ] + render_view.CameraParallelScale = 534.08 + + +def save_animation_and_screenshot(render_view, animation_path, screenshot_path): + """ + Save animation and screenshot. + """ + animation_scene = pvs.GetAnimationScene() + animation_scene.UpdateAnimationUsingDataTimeSteps() + + pvs.SaveAnimation(animation_path, render_view, FrameRate=15) + + if not screenshot_path: + return + + time_steps = sd_productspvd.TimestepValues + if not time_steps: + return + + last_time = time_steps[-1] + render_view.ViewTime = last_time + + for reader in (sd_productspvd, sd_attributespvd): + reader.UpdatePipeline(last_time) + + pvs.ExportView( + filename=str(pathlib.Path(screenshot_path)), + view=render_view, + Rasterize3Dgeometry=False, + GL2PSdepthsortmethod="BSP sorting (slow, best)", + ) + + pvs.RenderAllViews() + + +configure_color_bar_and_display( + animation_setup.sd_productspvdDisplay, animation_setup.rHlookup_table, kind="prod" +) +configure_data_axes_grid(animation_setup.sd_productspvdDisplay, kind="prod") +configure_view_appearance(animation_setup.renderView1) +configure_color_bar_and_display( + animation_setup.sd_attributespvdDisplay, + animation_setup.volumelookup_table, + kind="attr", +) +configure_data_axes_grid(animation_setup.sd_attributespvdDisplay, kind="attr") +set_camera_view(animation_setup.renderView1) +save_animation_and_screenshot( + animation_setup.renderView1, args.output_animation_path, args.output_screenshot_path +) diff --git a/PySDM/source/examples/PySDM_examples/Toon_et_al_1980/__init__.py b/PySDM/source/examples/PySDM_examples/Toon_et_al_1980/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/examples/PySDM_examples/Toon_et_al_1980/fig_1.ipynb b/PySDM/source/examples/PySDM_examples/Toon_et_al_1980/fig_1.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..73872ff675681e49c511bf97200df7a1274bd2e8 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Toon_et_al_1980/fig_1.ipynb @@ -0,0 +1,2309 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + }, + "pycharm": { + "name": "#%%\n" + } + }, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Toon_et_al_1980/fig_1.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Toon_et_al_1980/fig_1.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Toon_et_al_1980/fig_1.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "based on Fig. 1 from [Toon, Turco & Pollack (1980): \"A physical model of Titan’s clouds\", Icarus 43(3)](https://doi.org/10.1016/0019-1035(80)90173-6) " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from open_atmos_jupyter_utils import show_plot\n", + "from matplotlib import pyplot\n", + "\n", + "from PySDM.physics import si, in_unit\n", + "from PySDM import Formulae" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "formulae = Formulae(\n", + " hydrostatics='VariableGIsothermal',\n", + " constants={\n", + " 'g_std': 117 * si.cm / si.s**2,\n", + " 'celestial_body_radius': 2.8e8 * si.cm,\n", + " }\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "p0 = {}\n", + "p0['CH$_4$'] = 25 * si.mbar\n", + "p0['CH$_4$+N$_2$'] = 250 * si.mbar\n", + "\n", + "molar_masses = {\n", + " 'CH$_4$': 16 * si.g / si.mol,\n", + " 'CH$_4$+N$_2$': 28 * si.g / si.mol,\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-02-27T10:46:06.450100\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "8c8a6b4b60174bd4b6a2e0212d7f7401", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_1.pdf
\"), HTML(value=\"\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-01-28T12:27:45.695450\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.0, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "b3d190775f5340bea63c0008a247a8fd", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_1.pdf
\"), HTML(value=\"" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f37ff8092bf145b4805cea5401603446", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./tmp2s5xx3k5.pdf
\"), HTML(value…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "def plot(*, particles, params_phys, params_comp, params_bins, rng, time=0):\n", + " \"\"\" plots the particle state as both a histogram as well as population scatter plot\n", + " (with random coordinates shuffled for the purpose of plotting) \"\"\"\n", + " _, axs = pyplot.subplot_mosaic(\n", + " [['hist'], ['part']],\n", + " figsize=(11, 6),\n", + " sharex=True,\n", + " tight_layout=True,\n", + " )\n", + " for k in particles:\n", + " axs['hist'].hist(\n", + " x=x_of_mass(particles[k]['mass']),\n", + " weights=particles[k]['mult'] / params_phys.norm * particles[k]['mass'],\n", + " bins=params_bins.count,\n", + " range=(params_bins.min_x, params_bins.max_x),\n", + " label=f'{k}',\n", + " alpha=.666,\n", + " density=True,\n", + " )\n", + " axs['part'].scatter(\n", + " x_of_mass(particles[k]['mass']),\n", + " rng.uniform(0, 1, params_comp.n_part),\n", + " s=.25 + 2 * particles[k]['mult'] / (params_phys.norm / params_comp.n_part)\n", + " )\n", + " lin_x, d_x = np.linspace(params_bins.min_x, params_bins.max_x, 256, retstep=True)\n", + " x_mean = lin_x[:-1] + d_x / 2\n", + " m_mean = mass_of_x(x_mean)\n", + " dn_dm = analytic_solution(mass_kg=m_mean, time_s=time, params_phys=params_phys)\n", + " axs['hist'].plot(\n", + " x_mean,\n", + " dn_dm * np.diff(mass_of_x(lin_x)) / np.diff(lin_x) * params_phys.norm * m_mean,\n", + " color='black',\n", + " label='Golovin solution'\n", + " )\n", + " axs['hist'].legend()\n", + " axs['hist'].set_ylabel(r'pdf(x) $\\cdot$ mass(x)')\n", + " axs['hist'].set_title(f'time: {time:.1f} s')\n", + " axs['hist'].set_xlim(params_bins.min_x, params_bins.max_x)\n", + " axs['part'].set_xlabel(r'$x = ln(\\sqrt[3]{m})$')\n", + " axs['part'].set_yticks([])\n", + " axs['part'].set_ylim(0,1)\n", + " for vl in axs.values():\n", + " vl.grid()\n", + " show_plot(inline_format='png')\n", + "\n", + "plot(particles=PARTICLES, params_phys=PARAMS_PHYS, params_comp=PARAMS_COMP, params_bins=PARAMS_BINS, rng=RNG, time=1e-10)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "def coagulate(*, params_phys, params_comp, particles, rng, graphs):\n", + " \"\"\" performs Monte-Carlo coagulation of the particles using a simplified SDM algorithm for an additive kernel \"\"\"\n", + " n_pairs = params_comp.n_part // 2\n", + " p_scale = params_comp.n_part * (params_comp.n_part - 1) / 2 / n_pairs\n", + " for _ in range(params_comp.n_step): \n", + " non_overlapping_pairs = rng.permutation(params_comp.n_part)[: 2 * n_pairs].reshape(-1, 2)\n", + " u01 = rng.uniform(0, 1, n_pairs)\n", + " for samp,part in particles.items():\n", + " for alpha, pair in enumerate(non_overlapping_pairs):\n", + " j, k = pair\n", + " if part['mult'][j] < part['mult'][k]:\n", + " j, k = k, j\n", + " kern = params_phys.b_per_s * (part['mass'][j] + part['mass'][k])\n", + " prob = part['mult'][j] * kern * params_comp.dt_s / params_phys.dv_m3\n", + "\n", + " gamma = np.ceil(prob * p_scale - u01[alpha])\n", + " if gamma != 0:\n", + " gamma_t = min(gamma, part['mult'][j] // part['mult'][k])\n", + " deficit = (gamma - gamma_t) * part['mult'][k]\n", + " graphs[samp].add_edge(\n", + " j, k, mult_transfer = gamma_t*part['mult'][k],\n", + " total_mass_transfer = gamma_t*part['mass'][j] * part['mult'][k],\n", + " mass_transfer = part['mass'][j],\n", + " total_mass_deficit=deficit * part['mass'][j],\n", + " size_deficit=deficit*part['mass'][j] / part['mult'][k],\n", + " mult_deficit=deficit\n", + " )\n", + " if part['mult'][j] > gamma_t * part['mult'][k]: \n", + " part['mult'][j] -= gamma_t * part['mult'][k]\n", + " part['mass'][k] += gamma_t * part['mass'][j] \n", + " else:\n", + " part['mult'][j] = part['mult'][k] // 2\n", + " part['mult'][k] -= part['mult'][k] // 2\n", + " part['mass'][k] += gamma_t * part['mass'][j]\n", + " part['mass'][j] = part['mass'][k]" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "B = {}\n", + "for pkey in PARTICLES.keys():\n", + " B[pkey] = nx.DiGraph()\n", + " for i in range(PARAMS_COMP.n_part):\n", + " B[pkey].add_node(i, mass_i=PARTICLES[pkey]['mass'][i], mult_i=PARTICLES[pkey]['mult'][i])\n", + "\n", + "coagulate(particles=PARTICLES, params_comp=PARAMS_COMP, params_phys=PARAMS_PHYS, rng=RNG, graphs = B)\n", + "\n", + "for pkey in PARTICLES.keys():\n", + " for i in range(PARAMS_COMP.n_part):\n", + " B[pkey].nodes[i]['mass_f'] = PARTICLES[pkey]['mass'][i]\n", + " B[pkey].nodes[i]['mult_f'] = PARTICLES[pkey]['mult'][i]" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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ERERERERkVpDiZAK8Pb25tlnn8Xb29vdoUgNonEjlaWxI5WhcSOVoXEjlaFxI5WlsSOV4Ypxo11jRERERERERKTW0IwQEREREREREak1lAgRERERERERkVpDiRARERERERERqTWUCBERERERERGRWkOJkMt4/vnn6dWrF35+fgQFBZU5npiYyNixY2nSpAm+vr60bduWf/3rX1UfqFQrlxs3ANOmTSMuLg5vb29iYmKqND6pnioyblJSUoiPj8fPz4+IiAgef/xxiouLqzZQqfa2b9/O4MGDCQoKIjQ0lMmTJ5OTk+PusKSaO3DgACNGjCAsLIyAgAB69+7N2rVr3R2WVHPfffcdJpOp3MfWrVvdHZ5Uc8uWLaN79+74+voSHBzMyJEj3R2SVHPNmjUr871mzpw5DrejRMhlFBYWMmrUKB544IFyj2/bto2IiAjee+89du/ezZ/+9CeeeuopXn311SqOVKqTy42bC+655x7GjBlTRVFJdXe5cWO1WomPj6ewsJAffviBhQsXsmDBAp555pkqjlSqsxMnTjBo0CBatmzJ5s2bWbFiBbt37+auu+5yd2hSzQ0fPpzi4mLWrFnDtm3b6Ny5M8OHDyctLc3doUk11qtXL1JTU0s97r33Xpo3b07Xrl3dHZ5UY59++il33nknd999N4mJiWzcuJFx48a5OyypAZ577rlS33Mefvhhh9vQ9rkVtGDBAqZPn865c+cue+7UqVPZu3cva9ascX1gUq1VZNz83//9H1988QUJCQlVFpdUbxcbN19//TXDhw/nxIkT1KtXD4C5c+cyY8YMTp8+jZeXlxuilermzTffZNasWaSmpmI22//esXPnTjp16sTBgwdp2bKlmyOU6ujMmTOEh4ezbt06rrvuOgCys7MJCAhg1apVDBo0yM0RSk1RVFREo0aNePjhh5k1a5a7w5Fqqri4mGbNmvHnP/+ZSZMmuTscqUGaNWvG9OnTmT59+hW1oxkhLpCZmUlISIi7wxCRq8ymTZvo2LFjSRIEYOjQoWRlZbF79243RibVSUFBAV5eXiVJEABfX18ANmzY4K6wpJoLDQ0lOjqad955h/Pnz1NcXMwbb7xBREQEcXFx7g5PapAlS5Zw9uxZ7r77bneHItXY9u3bOX78OGazmdjYWBo0aMCwYcPYtWuXu0OTGmDOnDmEhoYSGxvLiy++WKll4kqEONkPP/zAokWLmDx5srtDEZGrTFpaWqkkCFDyuaauywUDBgwgLS2NF198kcLCQjIyMnjyyScBSE1NdXN0Ul2ZTCa+/fZbduzYQd26dfHx8eHll19mxYoVBAcHuzs8qUHmzZvH0KFDady4sbtDkWrs8OHDgH1m9MyZM1m6dCnBwcH069eP9PR0N0cn1dm0adP46KOPWLt2Lffffz+zZ8/miSeecLidWpkIefLJJy9a1OnCY9++fQ63u2vXLkaMGMGzzz7LkCFDXBC5uJOrxo1c3TRuxFkqOpbat2/PwoULeemll/Dz86N+/fo0b96cevXqlZolIrVDRceNYRhMnTqViIgI1q9fz5YtWxg5ciQ33nijEmi1VGV+fh07doxvvvlGSx1qsYqOG5vNBsCf/vQnbr31VuLi4pg/fz4mk4nFixe7+V1IVXPk+80jjzxCv3796NSpE1OmTOGll17ilVdeoaCgwKE+PVzxRqq7Rx999LJF46Kiohxqc8+ePQwcOJDJkyczc+bMK4hOqitXjBu5+jlz3NSvX58tW7aUeu3kyZMlx+Tq5shYGjduHOPGjePkyZPUqVMHk8nEyy+/rO9RtVBFx82aNWtYunQpGRkZBAQEAPDaa6+xatUqFi5cWDKrSGqPyvz8mj9/PqGhodx0000ujEyqs4qOmwsJ1nbt2pW87u3tTVRUFCkpKa4MUaqhK/l9uXv37hQXF5OcnEx0dHSF+6yViZDw8HDCw8Od1t7u3bsZMGAAEydO5Pnnn3dau1K9OHvcSO3gzHHTs2dPnn/+eU6dOkVERAQAq1atIiAgoNQvEnJ1qsxYurB06u2338bHx4fBgwe7IjSpxio6bnJzcwHKzBoym80lf7mV2sXR7zmGYTB//nwmTJiAp6enCyOT6qyi4yYuLg5vb2/2799P7969AXuh3eTkZCIjI10dplQzV/L7ckJCAmazueR344qqlYkQR6SkpJCenk5KSgpWq7VkZ4+WLVvi7+/Prl27GDBgAEOHDuWRRx4pWadvsVh001yLXW7cAPzyyy/k5OSQlpZGXl5eyTnt2rXT7h+11OXGzZAhQ2jXrh133nknL7zwAmlpacycOZOpU6fi7e3t3uClWnn11Vfp1asX/v7+rFq1iscff5w5c+YQFBTk7tCkmurZsyfBwcFMnDiRZ555Bl9fX9566y2SkpKIj493d3hSA6xZs4akpCTuvfded4ciNUBAQABTpkzh2WefpUmTJkRGRvLiiy8CMGrUKDdHJ9XVpk2b2Lx5M/3796du3bps2rSJP/7xj4wfP97xelaGXNLEiRMNoMxj7dq1hmEYxrPPPlvu8cjISLfGLe51uXFjGIbRt2/fcs9JSkpyW9ziXhUZN8nJycawYcMMX19fIywszHj00UeNoqIi9wUt1dKdd95phISEGF5eXkanTp2Md955x90hSQ2wdetWY8iQIUZISIhRt25do0ePHsby5cvdHZbUEGPHjjV69erl7jCkBiksLDQeffRRIyIiwqhbt64xaNAgY9euXe4OS6qxbdu2Gd27dzcCAwMNHx8fo23btsbs2bON/Px8h9syGYZhXFFaRkRERERERESkhlD5eBERERERERGpNZQIEREREREREZFaQ4kQEREREREREak1lAgRERERERERkVpDiRARERERERERqTWUCBERERERERGRWkOJEBERERERERGpNZQIEREREREREZFaQ4kQEREREREREak1lAgRERGRWmvHjh088MADjBgxgq+++qrUsX//+99MmDABk8nktIeIiIi4nxIhIiIiUmM99thjjBw50qFrzp49S0REBMnJycTGxvL666+zYMECNm7cWHKOYRh88sknLFy4EMMwnPIYM2YML730kpO/AiIiIuIoJUJERESkxkpISCAmJsaha55//nlGjBhBs2bNAFi0aBGjRo0iPj6+5JzvvvuOvn37OnUWx8yZM3n++efJzMx0WpsiIiLiOCVCREREpMZKTEx0KBGSm5vLvHnzmDRpUslrY8aMYcWKFfznP/8pee29995jwoQJzgyVDh060KJFC9577z2ntisiIiKOUSJEREREnO7DDz/E19eX1NTUktfuvvtuOnXq5LQZEceOHePMmTN07twZgEOHDmEymVi6dCkDBw7Ez8+P6OhoNm/eXHLN8uXL8fb2pkePHgCsXLmSadOmcf/99zN69GgA8vLyOH78OK1atSrTZ3JyMiaTiU8//ZQ+ffrg6+tLt27dSElJYf369fTo0QM/Pz8GDhzIuXPnylx/44038tFHHznl/YuIiEjlKBEiIiIiTnf77bfTunVrZs+eDcCzzz7Lt99+y9dff01gYGCpc2fPno2/v/8lHykpKWX6SEhIIDAwkObNmwP22SEmk4mXX36ZWbNmkZiYSNOmTXnyySdLrlm/fj1xcXElnwcGBpKamkrr1q255ZZbAPjyyy8ZMWJEue8rMTERgNdff53Zs2fzww8/cPLkScaPH8+cOXN49dVXWbt2LYmJicyfP7/M9ddccw1btmyhoKDAkS+niIiIOJGHuwMQERGRq4/JZOL555/ntttuo379+rzyyiusX7+eRo0alTl3ypQpJbMxLqZhw4ZlXktISCiZDQL2JEVQUBCLFi0iPDwcgJtuuok33nij5JwjR46UaisqKor4+Hj++te/MmPGDAA+/vhj5s2bV24cCQkJhISEsGjRIkJDQwHo27cvGzZsYPfu3fj5+QHQrVs30tLSyn0fhYWFpKWlERkZecn3LCIiIq6hRIiIiIi4xPDhw2nXrh3PPfccK1eupH379uWeFxISQkhIiMPtl5cIGTFiREkSBCApKYmWLVuWfJ6Xl4ePj0/J5+Hh4dx2221MnTqVrVu3EhkZiZeXF8HBweX2mZiYyM0331ySBAFISUlhzJgxJUmQC6+VN6vE19cXsNcqEREREffQ0hgRERFxiRUrVrBv3z6sViv16tW76HlXsjTm94VSExMT6dmz5yXPCQsLIyMjo9Q5/v7+jBgxgvfff58PP/yQsWPHXjTWhIQEunfvXuq1xMTEkpojAPn5+ezfv79UkuaC9PR0gFLJGhEREalamhEiIiIiTrd9+3ZGjx7NvHnzWLBgAbNmzWLx4sXlnluZpTHZ2dkcPny4JMmRmZlJcnIysbGxpc5LSEhg2rRpJZ/HxsaWu2vLHXfcwaRJk+jUqRPLli0rN4asrKwyfSQlJZGZmVnqtZ07d2IYBh07dizTxq5du2jcuDFhYWGXfL8iIiLiOkqEiIiIiFMlJycTHx/P008/zdixY4mKiqJnz55s376dLl26lDm/MktjEhMTsVgsJcttfv75Zzw8PEolH44cOUJGRkapGSFDhw7lqaeeIiMjo9Tyl6FDh2K1WomOjsbT0/OSfXbo0KHktQs1Q35f7yMhIYEWLVrg7+9fpo3169czZMgQh96riIiIOJeWxoiIiIjTpKenc/311zNixIiS3Vq6d+/OsGHDePrpp53WT0JCAm3atMHb2xuwJymio6NL1f/YsWMHQUFBNGvWrOS1jh070qVLFz7++ONS7Xl4eDB27FgmTJhw0T7L6yMxMbHMLJTExMRyl8Xk5+fzxRdfcN999zn0XkVERMS5TIZhGO4OQkRERKSqLFu2jMcff5xdu3ZhNlfd34Ref/11Pv/8c1auXFllfYqIiEhZWhojIiIitUp8fDwHDx7k+PHjNGnSpMr69fT05JVXXqmy/kRERKR8mhEiIiIiIiIiIrWGaoSIiIiIiIiISK2hRIiIiIiIiIiI1BpKhIiIiIiIiIhIraFEiIiIiIiIiIjUGkqEiIiIiIiIiEitoUSIiIiIiIiIiNQaSoSIiIiIiIiISK2hRIiIiIiIiIiI1BpKhIiIiIiIiIhIraFEiIiIiIiIiIjUGkqEiIiIiIiIiEit8f8BqsMv4MBTdCAAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "8d6c15a444ea47a9b9637de4e43d3def", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./tmp99mzo08h.pdf
\"), HTML(value…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plot(\n", + " particles=PARTICLES,\n", + " params_phys=PARAMS_PHYS,\n", + " params_comp=PARAMS_COMP,\n", + " params_bins=PARAMS_BINS,\n", + " rng=RNG,\n", + " time=PARAMS_COMP.t_max_s,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA3oAAAN6CAYAAADYUzt4AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs/XmUHGd96P+/a+t979n30WiXLEveFxkvGNtgbEwAGwhgCHyTXEgIJ8tJTm5+BLgJSW4CuQRCuJebkLAGDFwgBmMby7a8I2/at9n3nt7X6urafn+0NPZYM7IkG2zLz+scnaOprq6u7pl6uj7P83k+j+S6rosgCIIgCIIgCIJw1pBf6RMQBEEQBEEQBEEQXl4i0BMEQRAEQRAEQTjLiEBPEARBEARBEAThLCMCPUEQBEEQBEEQhLOMCPQEQRAEQRAEQRDOMiLQEwRBEARBEARBOMuIQE8QBEEQBEEQBOEsIwI9QRAEQRAEQRCEs4wI9ARBEARBEARBEM4yItATXrMeeOABJEnigQceeKVPRRCEl9n4+DiSJPHv//7vi9s+9alPIUnSGR1PkiQ+9alPveh+L+U1TpVouwTh7HY67ci///u/I0kS4+Pjp/06y7UlH/zgBxkYGDjtYw0MDPDBD37wtJ8nvLqJQE84LZIkndK/U7mB+exnP8uPfvSjX/k5H29EJUni4YcfPuFx13Xp7e1FkiTe+ta3/srPRxDONsevsSeffPKVPpWzimi7BOFX45W4tn5d9zwvlwMHDvCpT33qjAJQ4dVDfaVPQHht+cY3vrHk569//evce++9J2zfsGHDix7rs5/9LO985zu55ZZbXs5TXJHP5+Pb3/4227dvX7L9wQcfZHp6Gq/X+2s5D0EQzsxf/MVf8Gd/9mdn9Fxd11HV1+ZXnmi7BOFX49d5ba10z/P+97+fd7/73S/ba331q1/FcZzTft7hw4eR5efGfw4cOMCnP/1prrrqqjMaIRReHV6b33rCK+Z973vfkp8ff/xx7r333hO2vxq95S1v4Y477uCf/umfltzwffvb3+b8888nk8m8gmcnCMKLUVX1jIM1n8/3Mp/Nr49ouwThV+PVcG0pioKiKC/b8TRNO6PniQ6js5NI3RRedtVqlT/6oz+it7cXr9fLunXr+Id/+Adc113cR5IkqtUq//Ef/7GYPnE8N3xiYoKPfvSjrFu3Dr/fTzKZ5F3vetdLTh94z3veQzab5d57713c1mg0+P73v8973/veZZ/zD//wD1x22WUkk0n8fj/nn38+3//+90/Y795772X79u3EYjFCoRDr1q3jz//8z5fs88UvfpFNmzYRCASIx+NccMEFfPvb335J70kQXq0++MEPEgqFmJmZ4ZZbbiEUCtHa2sof//EfY9v2kn0LhQIf/OAHiUajxGIxbr/9dgqFwgnHfOG8l82bN3P11VefsJ/jOHR3d/POd75zcdtyc/QefvhhLrzwQnw+H0NDQ/zv//2/TzjWcnMFVzqmaLsE4bXldK6tlebWnqyNOO5k9zzLzdEbGBjgrW99K/fccw9bt27F5/OxceNGfvjDH77oe1pujp7jOHzhC1/gnHPOwefz0drayg033LAk3f75c/T+/d//nXe9610AXH311Uum5dx+++20tLRgmuYJr33dddexbt26Fz1H4ddHBHrCy8p1XW6++Wb+8R//kRtuuIHPf/7zrFu3jj/5kz/hD//wDxf3+8Y3voHX6+WKK67gG9/4Bt/4xjf4nd/5HQB27drFo48+yrvf/W7+6Z/+id/93d/lvvvu46qrrqJWq53xuQ0MDHDppZfyne98Z3HbXXfdRbFY5N3vfveyz/nCF77Atm3b+MxnPsNnP/tZVFXlXe96Fz/96U8X99m/fz9vfetbMQyDz3zmM3zuc5/j5ptv5pFHHlnc56tf/Sof//jH2bhxI//rf/0vPv3pT7N161aeeOKJM34/gvBqZ9s2119/Pclkkn/4h3/gyiuv5HOf+xz/5//8n8V9XNflbW97G9/4xjd43/vex1/91V8xPT3N7bff/qLHv+2229i5cyfz8/NLtj/88MPMzs6ueF0D7N27l+uuu46FhQU+9alP8aEPfYi//Mu/5P/9v/93xu9XtF2C8NpyJtfWmTjZPc9Kjh49ym233cab3/xm/uZv/mbxGn5+UHqqPvzhD/OJT3yC3t5e/u7v/o4/+7M/w+fz8fjjjy+7/xve8AY+/vGPA/Dnf/7ni+e8YcMG3v/+95PNZrn77ruXPGd+fp4dO3a8JjK8XldcQXgJPvaxj7nP/zP60Y9+5ALuX/3VXy3Z753vfKcrSZI7PDy8uC0YDLq33377Cces1WonbHvsscdcwP3617++uO3+++93Aff+++8/6Tl+7WtfcwF3165d7pe+9CU3HA4vvsa73vUu9+qrr3Zd13X7+/vdG2+88aTn0mg03M2bN7vXXHPN4rZ//Md/dAE3nU6veA5ve9vb3E2bNp30PAXhter519hxt99+uwu4n/nMZ5bsu23bNvf8889f/Pl4m/E//+f/XNxmWZZ7xRVXuID7ta99bXH7X/7lXy5pbw4fPuwC7he/+MUlr/HRj37UDYVCS65fwP3Lv/zLxZ9vueUW1+fzuRMTE4vbDhw44CqKsuQ1xsbGTjiPlY4p2i5BeG04k2trpet2uTbihW2V6658z3P8XMbGxha39ff3u4D7gx/8YHFbsVh0Ozs73W3bti1uW+6cbr/9dre/v3/x5x07driA+/GPf/yE13YcZ8lrPv/87rjjjmXfr23bbk9Pj3vbbbct2f75z3/elSTJHR0dPeF1hFeOGNETXlY/+9nPUBRlsSfouD/6oz/CdV3uuuuuFz2G3+9f/L9pmmSzWVavXk0sFuPpp59+Sed36623ous6d955J+VymTvvvHPF1KcXnks+n6dYLHLFFVcsOY9YLAbAj3/84xUnQMdiMaanp9m1a9dLOn9BeK353d/93SU/X3HFFYyOji7+/LOf/QxVVflv/+2/LW5TFIXf//3ff9Fjr127lq1bt/Ld7353cZtt23z/+9/npptuWnL9Pp9t29x9993ccsst9PX1LW7fsGED119//Sm/txcSbZcgvPac7rX169LV1cXb3/72xZ8jkQgf+MAHeOaZZ07IYjiZH/zgB0iSxF/+5V+e8NiZLCUjyzK/+Zu/yU9+8hPK5fLi9m9961tcdtllDA4OnvYxhV8dEegJL6uJiQm6uroIh8NLth+vwjkxMfGix9B1nU9+8pOLc/xaWlpobW2lUChQLBZf0vm1trZy7bXX8u1vf5sf/vCH2La9ZB7PC915551ccskl+Hw+EokEra2t/Mu//MuS87jtttu4/PLL+chHPkJ7ezvvfve7+d73vrfkxulP//RPCYVCXHTRRaxZs4aPfexjS9KjBOFsdHwuyPPF43Hy+fzizxMTE3R2dhIKhZbsd6rzPG677TYeeeQRZmZmgOY8moWFBW677bYVn5NOp9F1nTVr1pzw2EuZXyLaLkF47Tnda+vXZfXq1ScEYmvXrgU4rXm/IyMjdHV1kUgkXrZz+8AHPoCu64up7ocPH+app57i/e9//8v2GsLLQwR6wqvO7//+7/PXf/3X3HrrrXzve9/jnnvu4d577yWZTJ5RyeAXeu9738tdd93FV77yFd785jcv9mq/0EMPPcTNN9+Mz+fjy1/+Mj/72c+49957ee9737uksIzf72fnzp384he/4P3vfz979uzhtttu401vetNi0YkNGzZw+PBh/vM//5Pt27fzgx/8gO3bty/bwyYIZ4uXs5LcSm677TZc1+WOO+4A4Hvf+x7RaJQbbrjhZTn+Sj3eLywoA6LtEoTXqlO5tk6nLTjbbdy4kfPPP59vfvObAHzzm9/E4/Fw6623vsJnJryQCPSEl1V/fz+zs7NLhvMBDh06tPj4cSs1mt///ve5/fbb+dznPsc73/lO3vSmN7F9+/Zlq/Cdibe//e3Isszjjz9+0vSMH/zgB/h8Pu6++25+67d+ize/+c1ce+21y+4ryzJvfOMb+fznP8+BAwf467/+a3bs2MH999+/uE8wGOS2227ja1/7GpOTk9x444389V//NfV6/WV5X4LwWtTf38/c3ByVSmXJ9sOHD5/S8wcHB7nooov47ne/i2VZ/PCHP+SWW245aanw1tZW/H4/R48ePeGxF75uPB4HOKH9WS47QbRdgvDadCrX1um0Bcs53TTJ4eHhJR0zAEeOHAE4rXXthoaGmJ2dJZfLndbrv9j5fuADH2DHjh3Mzc3x7W9/mxtvvHHxMxJePUSgJ7ys3vKWt2DbNl/60peWbP/Hf/xHJEnizW9+8+K2YDC47A2QoignNG5f/OIXX7Zes1AoxL/8y7/wqU99iptuumnF/RRFQZKkJa87Pj7Oj370oyX7Ldd4bt26FQDDMADIZrNLHvd4PGzcuBHXdZctUSwIrxdvectbsCyLf/mXf1ncZts2X/ziF0/5GLfddhuPP/44//Zv/0Ymkzlp2iY0r+3rr7+eH/3oR0xOTi5uP3jw4AmV5CKRCC0tLezcuXPJ9i9/+cvLHle0XYLw2nMq11Z/fz+KopxSW7Ccle55VjI7O7ukCnCpVOLrX/86W7dupaOj45SP8453vAPXdfn0pz99wmMvbK9eeL5wYmB73Hve8x4kSeIP/uAPGB0dFdU2X6XEgunCy+qmm27i6quv5r//9//O+Pg45557Lvfccw8//vGP+cQnPsHQ0NDivueffz6/+MUv+PznP09XVxeDg4NcfPHFvPWtb+Ub3/gG0WiUjRs38thjj/GLX/yCZDL5sp3nqZRuv/HGG/n85z/PDTfcwHvf+14WFhb453/+Z1avXs2ePXsW9/vMZz7Dzp07ufHGG+nv72dhYYEvf/nL9PT0sH37dqC5tkxHRweXX3457e3tHDx4kC996UvceOONJ8xnFITXk5tuuonLL7+cP/uzP2N8fHxxrajTmdN266238sd//Mf88R//MYlEYsXRq+f79Kc/zc9//nOuuOIKPvrRj2JZ1uJ6cc+/vgE+8pGP8Ld/+7d85CMf4YILLmDnzp2LPevPJ9ouQXjterFrKxqN8q53vYsvfvGLSJLE0NAQd955JwsLC6d0/JXueVaydu1aPvzhD7Nr1y7a29v5t3/7N1KpFF/72tdO631dffXVvP/97+ef/umfOHr0KDfccAOO4/DQQw9x9dVX83u/93vLPm/r1q0oisLf/d3fUSwW8Xq9XHPNNbS1tQEsrsV3xx13EIvFuPHGG0/rvIRfDxHoCS8rWZb5yU9+wic/+Um++93v8rWvfY2BgQH+/u//nj/6oz9asu/nP/95fvu3f5u/+Iu/QNd1br/9di6++GK+8IUvoCgK3/rWt6jX61x++eX84he/eEnV8M7ENddcw7/+67/yt3/7t3ziE59gcHCQv/u7v2N8fHzJzdLNN9/M+Pj44mhCS0sLV155JZ/+9KeJRqMA/M7v/A7f+ta3+PznP0+lUqGnp4ePf/zj/MVf/MWv9T0JwqvN8TbjE5/4BN/85jeRJImbb76Zz33uc2zbtu2UjtHT08Nll13GI488wkc+8hE0TXvR52zZsoW7776bP/zDP+STn/wkPT09fPrTn2Zubu6EQO+Tn/wk6XSa73//+3zve9/jzW9+M3fdddfiDc9xou0ShLPbF7/4RUzT5Ctf+Qper5dbb72Vv//7v2fz5s0v+tyV7nlWsmbNGr74xS/yJ3/yJxw+fJjBwUG++93vnlF78rWvfY0tW7bwr//6r/zJn/wJ0WiUCy64gMsuu2zF53R0dPCVr3yFv/mbv+HDH/4wtm1z//33L2n3PvCBD3DnnXdy6623njRdXnjlSO7Jxm0FQRAEQRAEQfi1GRgYYPPmzdx5552v9Kmc1I9//GNuueUWdu7cyRVXXPFKn46wDDFHTxAEQRAEQRCE0/LVr36VVatWLaZ6C68+InVTEARBEARBEIRT8p//+Z/s2bOHn/70p3zhC184o4XXhV8PEegJgiAIgiAIgnBK3vOe9xAKhfjwhz/MRz/60Vf6dISTEHP0BEEQBEEQBEEQzjJijp4gCIIgCIIgCMJZRgR6giAIgiAIgiAIZxkR6AmCIAiCIAiCIJxlRKAnCIIgCIIgCIJwlhGBniAIgiAIgiAIwllGBHqCIAiCIAiCIAhnGRHoCYIgCIIgCIIgnGVEoCcIgiAIgiAIgnCWEYGeIAiCIAiCIAjCWUYEeoIgCIIgCIIgCGcZEegJgiAIgiAIgiCcZUSgJwiCIAiCIAiCcJYRgZ4gCIIgCIIgCMJZRgR6giAIgiAIgiAIZxkR6AmCIAiCIAiCIJxlRKAnCIIgCIIgCIJwlhGBniAIgiAIgiAIwllGBHqCIAiCIAiCIAhnGfWVPgFBEARBOJu5rkuj3MAoG7i2CxKoXhVv1IvqFV/DgiAIwq+G+IYRBEEQXtfMmkktV8Ns2GgehWBLENX30r4eXdelmqqSG8lRmChglk1M3cSxnWag51HRAhqB1gCJ1Qli/TG0gPYyvSNBEARBAMl1XfeVPglBEARB+HUrpyocfnaeA7umWJir4DgOsiLT1R1lw0XdrNvaSSAZOO3jVheqzOyaoTBewNItvFEvWkBDC2jIqozrutiGjVk1McoGdsPGn/DTfm477ee0o2jKr+DdCoIgCK83ItATXvVss3lDJCkSnpAHSZJe6VMSBOE1bmr/And/dy9zMyXsgIY/6UdVZSzToZatodUtevvj3PCec2hfnTylYzq2Q2pvitldszTKDUKdITwhzyk9r5apUS/USa5J0ntpL8G24Et9i4IgCMLrnAj0hFcto9Lg2ccmObhrmlymhqLKDKxJsuXSPgY2tYmATxCEMzJ3OMOPv/Y0c0Wdlv4Y3mVG0GqGRW6sQH9nmFs+fD7J/tgJ+zi2Q3mmTG40Ry1dI3MwQ2mmRHxVnOTaJLLarHdmmDbzJYOaYdGwHRRZRlMkkiEPyYAHSW62ZXbDpjBRIJAMsOqNq4j0RF7W922bNvVCnUa5gW3aACiagjfqxRf1LZ6vIAiCcHYQgZ7wqlQvG/zk689w6Ok5XJ+KL+bDNh3quRoRv8Y1v7GR865e9UqfpiAIrzG2afOjf36CPQdStK1OoikrBzcNyyEznOXCC3t5y0fOXwzIAEozJaYfm2ZuJEeqqFOYLmPMlPCHvMSjPuLtQTz9UbKqxES2Rkk3QQJJAtdtzuHzqAptES/9iSBdUR/asbTO4kQRb8TL6htWE2oPvaT36zou5dky2eEshfECZtXEqltL9tF8GlpQI74qTmJ1glBHaMl7FQRBEF6bRKAnvCrd/8MD7LzzEJHBOMHnFShwXUhNFwlJMu/7g0toG4j/Ws6nYTkcHs2xMFfCsV0CIQ/r1rfSEvH9Wl5fEF6vzJpJabqEqZu4tousyfhiPsJdYeQVgjTbtDGKBmbNBEDxKPhiPlSfyvyRDN/8wmNYCT+J2Itfv+lMlVDd5gN/eDnx3igAxakiR+4a5uBYjhkF9JyOOlrADWg4qoQmgVq1sCQw+yIEO8JEfCryC4Inw7Qp6BaO49AZ9XN+f5yQT8V1XfIjeWL9MdbcuOaMK3OW58rMPDFDYbKAa7n44j60oIbqUxczIlzXxdItGtUG9XwdxaMQH4zTfXE3wVaRPioIL6dy2cBsWCSS4toSfj1E1U3hVUcv1jn45DRyzLckyINmb3hbd4T5A2n2PT3HNb/iQM91XXbtnuOXD46THc7iVE1kwFEkHu4IsfqCbq5902qigRefhyMIwqmrpqvkhnNkDmXQszqSJOHS7JeUVZlId4SWDS0khhKoPrU5cjVXJj+cJzeWa45cGc2RK0VV0IIa4a4ww0czVHWT9kjslM4jFvOTPZLl6L4FLuqN0qg2GLt/nP2jWcZUiCgK/ryBE9BQjwWOCyWDlGkRM10603UCHeETgjwAr6bQrilYtstMoUbDdrh0VZKQTyXaHyU3kiO1O0X3Rd2n9dk5lsPcs3PMPTmHqZtEeiKoPpWqYbFQMykX6ximjQT4PAohr0os4iPZGsSsmWSPZCnPlem6sIuOLR1idE8QXgbP7Jpmxw8PYBsWm7f3c8MtG5BlkS4t/GqJQE941cmkKhSzOuG+6LKPy7KEFNKYH86+6LGsusX0wQUq5QbJ1iAd61pO66bl/kcmePiHB1BKBsnOMFp/DEmSsA2bSqrC7h8fIjtX5rYPbBPBniC8TLJHskzsnEAv6PgTfuKr40tG76y6RSVVIT+eJ7k6Sed5nWSHs6T3p6lUGhQViYWGRV0+liIJJGoqyXSVmadmoWLg9kYh4n3Rc9FUGUeVKOVqABTGCkyO5Jj0SCT8HtSFKkalgdLSrM5ZNixKhknQo1BXXWoFHU9Wx3OSpRNURaIzGmCuqLNrLMvWrih+j0KgJUBqT4qW9S14T+FcoTmaOfnwJHNPzeFv8RPuDpOtGIyNlZkp6NTMZvArSdCMm11AIuBR6U34GUiGSKxJUF2oMr5jHLNi0nNpz4qjp4IgvDjHcdj508NUMzU8IQ+7HxjnnPO76V1m7u+vWqagc3DfPOViA3/Qw/pNbXSK0fuzlgj0hDN2vAe9NFWiUW2geBTCnWGifVEUz5mXB5ePpxSd9MV50WIslmHxwHf38vRjUzQsh1BA4+qb1rHl2qFTKuRyaCzHoz8+hK/hEF3fsuQ5ilch2hclUG4w/csZ7moN8u53bD6VtycIwklkj2QZ/cUokiLRsq5lcbvruDguKIqE6lOJ9kWxTZu5Z+YY3TGKFPWRCWnMWRbVqolXVVCPBSd112VB1/GqCo7rYOTr1Pen8Q7FUU+xuqUkSTi2Q/pAmrRl4crglyX0VA3JqyzOvStUzeZImaZSa1jUZJdQqorWGUI6SbETWYaAYbPv4QmmTZegX6O1J0J31E/3RIH2c9pf9Bxdx2X60Wlmn5wl2hcFr8KB2RKHU2Xqpk3Ep9EZ8PPC5s9xoGqYHJwrMZnV2dAVZqglhOpXmX58GkmR6LmkRxTAEoSXQJYkXJrXKRKccCGegkrFwO/TUM6wcNLju6bZ+aODVObLyK6Eg8sTrUEuvnEtV10xIK7xs5AI9IQzUi/UmXhogsJYAbthI3tkXMvFxSXUEaLvsj5iA7EzOnayPUSsJUg6UyO4TNU5x3GhZtG5OnHS48wezvDM41M0WvzEwl4y82We2DHKmm1dBFpOXBvLNm1cx12cD/PskzPYmSrRjStX+NTCHqIxP+NPzjJ/zSo64qe/5pYgCE21TI2JnRNIikS4KwxAoWYylasxna9hOy5Br0pfMkBPzA+6hVE0mDuSJdcWoNwbIhbw0hULnHgPFfSgN2xmFYmqDN66RfhoHp8soS7THhxnWg6y7RJJ+DGrJuVMjZzrEvJq2CUDu9pAOZayWWvYGJaN71gVT01RqDkOVs3ENSwkdeVR/2KqwsIzc7hVEz2sEXBVJvctkPNrxPqitG1+8UrD2SNZZp+ebVbr9Cg8NZ5nNFMl4lNJxPwrPk+WIezXCPs1ijWTp8bzlGoWW/tiBDuCzO6aJdQeIr7q1zMnWhDONrIs84ab1rPjB/uplKo44Twjo8+QSF5IMHhqnU27Hp/kwf93gNb+OL/52xeinmawd3Qsx/3f3YdbadC1pgVZk3Eth8J0iYd/cIB4ws/WzR1n8vaEVzGRiyGcNqNkMHLPCNnDWYLtQdTeCNWYF7s9SKQ3ip7TGblnhMJ44YyO74t42XBhFxTrVKqNJY8dL8YSjfnYdP7J563Uqg0alkM05MGjyvgiPiqVxuK8neOsusXko1M8/H+fZudXn+LIL0aYmy4y/uwcwZj/RVM9Q60B9HSVg/sXzuj9CoLQlBvJUS/UF4O86VyNnUfS7J0p0LCbY/zZaoNfjuZ49GiGmX0LFLI1MhEPtYUaHYpKyKeu2FHu9yj0rEqgKTKphkWtYdEYK+Lo1vJPAPIFnVjEy+rNbTi2g23ZuJKELEvN5zkgKc0XLBvN4i/HsxJkCZCanVOuc/K6Z7nxAk7dItARwvSoyEEPid4olVKdkT3zNCqNkz6/Xqwz88QMql9FC2g8M5VnJF2hNewl7F85bfSFogGNZMjD4YUSe2cKzZRRGaafmKZRPfk5CIKwsnPP6+INb2/D0zPGG9+yhvvuu48vf/nLjI+Pn9Lzp0bzZEcKpEZzlEr10379Z345RSNbJbk6jqw1b/8lVSY+EMOtNHjm8SlEfcazjxjRE05bam+KwniByFCcvXMlxrM1DNNGUSTawj7O74thzVeZfnyacHcYZZk1ql7MpdeuJjtX5sCTs5R9Ct6wF9t2aOTrBL0yNf8kxcYaWtzIir3cLe0hIiEP6dkSvpgfY75C32D8hLkuYw9N8NBdR5hzHBxJInkkw+rhVuq5GqViCi0+iD+4co+/5FGQXSgV9NN+n4IgNJm6SeZQBl+8OTpWrJk8PVnAtB26nzdSHgZsx2V2OIc+W4GYl7rkElZl7JyOGj35XDZv3E+sPURqtkQmIeMp1VGmS3jXnJghUDct7HSV1Zf2EeuKYOomHq+KCpiWi1puwLHmzbJd9Ia1ZLkG23WRHZA9MtIL5rg5lk0mm6GtvR2zbtHI1VGCHhRJwnVBb1gEQl7UoIf0ZAGjaOANr/zeMocyVBYqJNcmmcjVGMvUaA178ZxBipdXU0gGvQwvVGkL++jsiZA7kiN3NEfHVtHjLwhn6vCR/dRqBUKhEOFwmL6+Pvr6+k7puRduH2D46AgXb19NInFq2UOmabJjxw6uu+46ZkbzeEPeZe+ZAlEf6bECddPB/xKm3givPiLQE05Lo9ogezhLoDXAaLbKofkSsYCHZMiDaTnMFmrIElzWF6c0XaI0XSI+ePrpPt6Qh5s/eB4DG9o48MQ0k+MpgkE/527vZ8vFPfzkvu/xne98h+7ubj70oQ8t23AlB+Nc+45NPHb3UWam0qxb180b37Fpyc2SntM5/PQcU7JLrDWEIkvM52rkHz7EbCqPL65SqVZOGugB4LooonqWICxhmzblmTL1Yh3HcpDV5tIIke7ICYtzl6ZL1LK1xfTAqVyNimEuCfKOU1yIlBtkGxauLpMMepFdEztdw+0JI52sc0mRSK5JYJQMcrk65WQALVND6w4jP69gStWwyI3kaItrXHr9GiRZwhP0EB+I0T5R4EjDxFs3F+fdOa6L7TYLqxxnWg5xB7SoF8n33NdtpVxhYnIC02wQCASxLRsbB8VuPleSwHaa+7ouKIqMqZsrvqXjQbI/4adhOxyYK6Ep0rILwZ8qv0ehXLfYP1ekLdKGN+pl4cACrZtaz6jzThBe7SbH8zyyYxQJuPza1fSuUBDuTOVyOXRdJxaLkclkuO222+jq6uLQoUNs2LDhRVOzu3vCSIERJK0FWLPifocPLPDMY1Os2hzgl7vuI51O09fXR71hPNewvIBjO8iajLiNOfuIQE84LXpWRy/oRAdijB8u4feoBI/NadNUmZaQl4WyQdFywHGpZWonBHrlVIW9T0xTWqiQ6I6y+eJuAsv0Tml+jfOvGmTb9n7+4//8B/GEyo23bUOSJNbPrGf37t1ceumlKzaOkiSxYXs/0S6NL/2vL/HG37yG9t7kkn2sukW91sD1KIu9WIpXJRwJEQfqFR3DOHm6klW1cDWFlpe4sLEgnC2sukX2SLMKZmGmRLlu4tgOiiIT8nuI9YRp3dhKcm1ycU6sVbfABVlpLho+XdAJeJb/inIqDeSqRVmS8JguqiLhehQc3cK1nJMHeoCnJUDntg7MZ+YoZ2pIkoSe9KN1h7FMh3quhsewWbMqyUhuJ/+1I8/atWu56KKLSK5N0vb0HDM1g4puETjW/jiu2ywSdew1DMtGkVwCsozWHnwuBdxxmZubxbYtXBcCfj+yqlDq0qkM53CDnma6Jy5m3QLHobUzvGJ1Ktdxmd01y/Tj02gBjYVinWKuRqIngiPJyMFTT9t8oUTQQ7pSJ1Wq09kaoDRZojxTPuP514LwatVoWPzkG8+yMJwDoJCq8P/9yRVnXPRkOYlEgg9/+MOMjIwwNDS0uH1oaGgx2DuZ3bt302g02L9/P+eff/6Kc/uGD6UZfXaOVMEgHA8QiUTweDyce9EAj9+xD8d0FlM3AVzLoVqqs+WKfryq6MQ524hATzgtju2AC64kYdoOnhekI2mKgmU3mo9JEo65tPeoNF/hx//3ScbGcrgeBeXRScb2p7j5w+fjjy8tFuDYDsXJImNPjjH3wBxpJc1T1lMMXDDA5vWbufDCC6lWqywsLNDW1kbdtFFkaUnqFMDuw7sJtgd5/OnHeVvv25Y85ov5SLQG8Q5nWNAMFEWCmklvZ4K+K4Z4+t4RutpOXvSlPF8i1Bth48bW0/04l2WUDCzDwhvxnvFCyYLwSjHKBuMPjDOzb4GsbTPnOJRsF8d1URyHMDZdRxukR/N0n9POwNUDeIKepXPY3GZ6prLC/FhbN7FMCwOXsHZsH6m5lALLd1ifwNsWpP+yXmbH8oTnqripKrW6TiQWZtVAgo0X97B6Szs7n6jzxBNP0N3d3Vy6wXVoW5NgaPc8B10o6Q2CARVZkpCl5nnXbRvJdUmaLv6uEErieW2bLLFm7VoAqpXK4ubWoThGvk4jU8MGGqpMxavQviZJV0902bnClVSFqUemmNg5QWm2RLQ3SrZcRzJsrLE89kwZtcWPZyCGdAbpWMdHJ2cLdXriARzbQc/rItATzjq1mkktrxNqC+LYLuWcTt2wCJ6kgNKZqJs2lYZNw3IW06q9Xi8DAwMcPnyYdevWUdRNLNshFvAsaQMjkQhbt26lp6cH27aXPb5lWRwef5B1V5zLDTdeSDDooVar4bouoSgcfnqO1OE0sc4wvoiXRsUkN1Mm1BPlwkt7X9b3Krw6iLtI4bSoPhVFU5Ash0TAw1S+Ruh5KUllwyTgUYn4NGq2g/aCtaP2PDbJ2GieyOoEAa9KuWZy9GCag0/Nct61z/Vw2Q2biZ0TpPalyGVydHd1IysymdEMjfkG8VVxYtfEiHZFGRmfZMcDB5gZr+H1qVx1xQCbu2OLx5JlmZ6eHoLBII7jLFmgVAtobL5qkNGRKVKTZbzBEIMxP1uvGMC7uYXRkRwLw3na1yaRtBN79iqzZXQXLr+in4j/pX8hjDwxxeN3D1OtNugZiHPlOzcRFOvbCK8RVt1i/IFxxp+Z46jssqCb+L0q8YCGIkvYjkupbrHXNGjzazSemQNg1ZtWLUnllGSJkE9loWQQWaZYpFu3sJ3mHLjjPdCu4zaDIeXk6U/P5wl78fRFGVzdSm/Cxy8m7+O633gb67etX1wiZnBwkHA4TCGn8dW/eZB6qUEgqLG5O8aqUJG5okGtbmG6LoblgGMTRSKMRKgzjHd1fMVlFYKh57IAfCEvfRd1k5spkZ4pEYv7OWdjO/29UYysjie8tH0pz5YZuXcEPadTdx1qQQ+zpTqT1QaqKqEEPPhccGcqOIaNd20S2Xv6wZ5fU8lVG7iOi6IpVBeqp30MQXi1i8X8rNrWwd4Hx1AUhXOvWUUw+OLf6YWCzv0/O0J7T4TL3jC44n4LRZ0nd81w8NlZKtky97en2bi1k4su7CEW9OD3++ns6uLb9z3N7ISDbdoMbmrlxgv7FrOmhoaG8Pl8dHR0oCgnXsu5XI4f//jHZDLTvOUt1yyefyDQzJgKBuEdH9rGPT85xPyhDPmMjuJV6DivnatvXEdf98ubqiq8OohA73XONm3qhTqyIuOL+140RzzYFiTcFaaSqrCmPUym2mCuoBPwqjQsB9txOac7imJYeMNeIr1Ll0fIz1dw/AqBYw1XOKBRViXy85Ul+009NsXcM3NE+6IkhhL06D1YtkUoFMI2bXKjzfSKNTeuYd7wsf+eA4SzBgVZ4udlk7Z3n0NbuFnU4frrr2d0dJRVq1Yt+56OFI4wLe3jqquvYrB/kHB3mNhgDFmRectvnsud39zN7JEMwYCHUEsAWZao1xqU0zWcgMa5N67h6u0DZ/DpL1WeLXPf/zvIdMVADXlYeGoGj0/ljR/cJta2EV4TMocyTO9NcUR2yRkWXbHAkjkfsiyRDHlwHI35Yh3Xp6LtnW+uSdkSQNEUzJqJFtDoTwSYK+hYtrtk3huAa7votoOqSgSOBS+ubiGHPWc0coUCE8MTuHWHXffvYtWaVfiPjcKtXbuWtrZe/vXvdlLN6sR7IuSmSuxXSmy/ZhWenw9jKjKVeh0jl0MNRmlpC6O1BVA7QqcVXHkCGpH+GJ6uMNesbyPq16ikKtgNGz2nY1ZNFI+C6lcZu3+Mer5OKaJxcKpI3bSQAx4s28F1ZVIlA59HoS3qgUyNhkfGuzZ52m2JV5Wpmza66aD6VWrZ2mk9XxBeC2q1GqnSE6y5op/h4WG2XLT1lJ539GCa3feOEOkMccElvXiWSTefyVS44z+eJXcgTcCrEPaq1A9leHjfAkf3L3Dr+86lNepnvgZHn67gHy2gAvvGCsQjPt60pWvxWIqiYFnWsoGeaZo4jkMgEMA0l5/T29cV5bd++yImZoqUiwbBkIeBvtiK2RMvhWFYPLNrmqF1rbSKDutXjAj0XscWDmV4ZscoC7NlVE1maGMb57zxuRuc5ciKTNvmNkrTJSKuS7dSJufxYEoadq3Ahev66Yv4KYwX6NzWSSC5dO5doiuM/OQMNcNaHNFTbZd4x3M927VMjfSBNKHO0OKIoCQ/lwaqaArxVXEK4wWKk0XmFyp4iiaR7jB2rk56tkyxZi4GeiczPT3N008/jZbUiG2J0b+1f8nj6wcThH/nQp785TRHnpohnamC46L4NTov62XrRT1s29T+oo2kUTKoZWtofo1gW3DZNKxqsU6hUCfSEybi15ipm+SO3eSJFE7h1c5u2CzsXyDrOKR184Qg7/lkWaIj6mO2oNPul0nvT7P+N9YT7Y1SnCkS64/RHfPTHfczma2RDHkX59A6DlQaNo7jEPZ5UWQZ13FxTRt1hWvrpByX6mgeY7pAPBmHEdj93d2su2Hd4vzifE6nVjRI9EbxBDRi3WEq2RqRLW1053QkReLIkSNoeg25tx+5O4YncGYj/EW9wUAyiM90yE5nmXtyDk/Iszj5Tz5WmKUyX0Fbl+TpiSK27RIPejE9Mqoi49MUJJrr+i0AHWEvVkZH62qgnKRy53Jk6VihGcdBliVcW5RfF84umUyGO+64g1wuwwUXnMdsah8/+cmPue222+joOHmV2UQraL0Gl77pvGWDPMdxueunhynuW6BzKIHif26fSMVk/qk57usI8e53bKaom7BQJRT1IQc16vMVFl4wgq6q6oppm+3t7Vx66aV0dHQsyVx6IVmWGOyNwa84U/PIoTR7H5+kWKhz/VvX/2pfTFiRuHt8ncqP57n7W7sZy1Rxwh4c3Wbi3mEqxTpveN+5qL6V/zSSa5PUsjUe/M6DPLjzQX7jfb/B4Jp27vzJLkbSc4Q3bKV1Qyu9l53Yimy5pJexAwuMHMlQ8KkoDYd1G9vYcMFzPVaFiQKNSqO56C/NsuWHU1XK1RrbvEECXqWZPqpKZA5n6FgTZ2/cS2m2QkOCUFeYaODUChD09PRwww03kM/niUROXJwdoLstRPdb11O4ehULmSqO4xIMeuhuCSKfwk1l+mCap+46SjpVwevXOOfibtZcs+qE4C0Q8RKLeplOVdAjPtSKRbwluJhCJgivZsWpIoW5MrNuc1HzF6veJssSfq/KnGvTOVOiMlehZUMLudEcdsNG8yhc0J9AwWUiUyYna82gw3EJBlR6Yn6yPpmGZaPVbeSAByW+cifVcmzHRSoamDMVVl+wmnq3Qnd7K/WZMjOPzxDpiTQ7lhJ+AjEfuZky8a4QhZkysc4wXauTOJMlsiNZFhoLeGIOdaVMzgjR5fesuJ7fSmoNGxmIFw2mjuRplBuYDQvPqlYKsWaApjoO1b0pjJzOwlyJRsRD9Fh7LdGcJ+jSXMsv4FWoGTY1n0rAdLCy+mkHesenTpolA328iCRLjD84jubX8MV9RPuioiNKeFWwbQfbdpYNuE4mkUiwevVqHMehXC5j2zaWZTE1NXXSQO/QoUN85zvfYcPGDVx6xcCy+4zPl5jbu0CsNbgkyANQQxrRiJfxZ+ZIXTtE1K8htQepjBRQinWMqJe2tqUjYcdH9JaTy+VIJBIkEievK/DrMDk5yUOP3EUovorzLup5pU/ndU20zq9TR5+aZTJdIdwXXcz/nsvVOLA3xZbJAi1rW1Z8riRL7Mnt4cnCk/jb/Vhli8J4Abkqs2AuMCaNccmbLlk2WAy1BfH35ggU85y7/nyS3WE2XtCNP/bcDZpRMlCel+40W9DZP1/Bsl0i6Qrn9DTzyD0hD/VcnfMGExTetoHDB1IE/RpXXt5/SqN5ALZt4/F4uPjii1fcxz0298bvVVjTFzut1Kd6sc6uu47y7FgOI6LhVnSK9wwT747QsWXpF0jBLpA29tHhXQN1m96tHVx60zqRtim8JhhFg3LNomzZJEOnFkxEvCr5mklZN6kX6rRsaCG5NknmUIbE6gRmvUrmwBOsG1pLpLUdy3U5cmAfA7Eo3noA17JZyNZQXQltdfy056AVdZOA6eCVJcywh31pnTk9zyV9MSpzFWrZGuGOMLGYn2vfuZkdP9hPKVUl2hni2ndtIhT20rqhldxIjjdd9SbK9TKeUIzHRrKkSnXaI75TDvbqpk2+UmfQcHFzNSy/SqZhkVdAL9Zxq0ZzUK/SwLtQJpr0U8jV8VUb2C5IPhXVryFLEo7joCgKEhKKDOW6RVCWMMcLzfmCbnMJB8mrIvlV5IC2/EioA/VUFTWvk56tUU1VCLYFmX9mHsd2kCSJQGug+Xtbk8QXPbV2VxBebvm8zvf/7SlqxTrXvmszm8459fUeZVmmv7+fa6+9llqtxiWXXML8/DyrV69e8Tn1ep0dO3ZQr9fxeFYevU+naziVBt6+2LKPBxN+5lMVMtkaGwYSXPGWtTz1+DSWabN5UyuXrWtbsr+qqisGevPz82zcuPHF3/BL0GhYPPiLERzL4Q1vWo3ff2Kn+pNPPskDDzxArVbjvR+/jVhMVCR/JYlA73UqO1PG1OTFIA8gHPBQzeoUMhWKSpF0Os0ll1yy7POvuvoqnt39LKXOEt1v6mawb5CDykGSwSRvueUtywZ5lmXx/e9/n0PDh7j4you57s3blj22fCwV6zifpuDXFBqSQyY1y9fv+zHxeBy5JrN201rO9ZzLjRf28satXctW3TyZqakpenqW720qVgz2PjvHvl0zlBeacwhjXRE2X9DNOed2EDpJala9XkeSJGqZGgtzZRpRD93xAJW6RXauwsJYYUmgd/fdd7N7927clgbv/52rkRwJX9R30pFVQXg1sRoWttusrqm+yEi3azvYBQPHsGgU65Q8GrnhHIk1CQauHMAxHSafnWTfxD4Mu0484GGwPYRjOUzrNruHD9GndeG1NdAtpNUJtM7TvJlwoWZYrPZpeH0a0fYgkbRLPjXFHQ/fi1/2k2pLcfNtNwNw7nldDK5OUMjrxBMBwsdGxuKr4rSsayFzKIMUl4j6NS4YiLNrPM9cUacldPJFy10XSnWTmmEx4CrEclUIaYxlquRyNQKbWmlP+BezB0wHCqbLeLFOzXHoD3pw5qpINQs15sOjKdQMC01pHlyt27jlGg0TsBxch6VBnSajRL1o7SGUpP+5x2wXY7xAfThHIuTF1xrGNR3aNrYRH2qmtDqWQy1TY3zHONnDWQavGSTcGT6934MgvAxGj2aZ3reAWTO596dPMjPnoVAo0N/ff9KOXGiOhMXjcSRJWlyy4HhAparLfwf7fD7e/va3c+TIkZMeW1VkXIlj69ed2BFlWw6SDIosI8sSV2/s4LzB5LJVN6E5olev1084TqVSIRT61QRUlUqF3bt3k0wmySxIPPqjg7i2S6ItyIWXnLjY+/nnn8/ExATz8/NEo6LAyytN3EW+TsXbQ2j7U+gNe3HuS6VuotZr/O//+894u7ysXr16xUCvXq+zfft2LrnkEmRZxuPx8KHf/xBP7TuE5Fl+cXFZllmzZg2zs7Pour7iuQXaAri221zAU5Fpi3jZvroF03YISFEOPfEA4+PjhBohtlyzZfF5vtNcxNd1XRzHWbYhHxnL8dNv7yY7mkdTFXzHburm96aY2Z3iqTVxbn7vVlqDHkrTJeyGjTfibaYx+VT27t3Lzp07MdIG1lQCp7efqmFRMSxCjkPwBT3fV1xxBcPDw8TjccKt4kZJeHWoZWsUxgvUsjVs3UbWZLxRL7GBGOHO8JKAQdEUFFlCliRs10VZZijL0S0a2RrlySK1fB3DtGhYLgcthyNTaWI793Dt2y+iY2sHWkij0qiQmkph5AzGqymO7J5n7GgBp6Yw5mSIqB7kdQlKcc/ienanKls1CHtVEhao/REU3eINQ0ns3hiPzZdIkaJvXR+1Wm2xal0k4iMSWXrtyqpMz8U9VFNVMvMZaIWWkJfLhpI8O1VgoWRg2Q4Rv0bAozRH3FywHIeSbmJYNiGvxub2MIHhAg2PwkRRJ5+qkFyTxN8deW5xPsDFxe9VkPwauVqVtGnT6lew0zqu5RDyKFTrJq5toxQMlKyO7IIU8iD5NdTWwJLfm9uwsfN17GwdtT2Apz+K7FUxxgoYk0UIarT3RZE9MsigPW9dPlmVCXWECLYFKYwVGLlnhNU3rCYk1hQVfs36BuN0rE1QKzVYd06CZ5/dQa1Wo7u7m4WFBVpbW1fMjslkMqw9tuTJcZ2dnczNzdHbu/JEtnQ6zZVXXnnS8+rvi+JrD1FOVYkOxk54vLxQwd8fJp8eY9jK4jgObW1tJGMn7gsrz9GbnJw86Tp8umFyYO8CI4fTGHWTRGuQjVs6Geh/8Swln8/HI488gmEY5HIGEe+5xGMx2juWv1cZHx/nbW97G4ZhiIykVwER6L1Orb2gi4PPzjE5U6QQ9eE2HLwlgwsuWU3fFedx7/33EovFOHz4MNBsXDo7OxdveJ599lkuuOACfL7mTc9kpsqDj4wzcSSNaU6x6fwBrrqsb0kKpSzLJBIJPv7xj2MYxornFuuPEUgGqKaqhLuaDUnL4rwSP+9617v48fd+jMfxMF4a55577mHbtm20tp7eOnYzMzN0d3efuH2uxE/+42lKkyU6VieXpJFGOkPYhsXCoTR3/O2DDLaHKRQN6pZDLKAysDpB4twQ4XCYWq1G+2A7suRlfnKBup4g5MLmta20b1iaGjs1NcXv/u7vkslkTus9CMKvQnGqSOZghvxoHqNsoPrV5kLmjotVt5h7ao5ob5SWjc2UPUlujkCHvApBbMp1i9gL5smaCzVyh9KUczqGKuP6VGy/gmTZZFNpqo4XZdak+p976W4Nccl1Q2y4ZQMXczH77h7m6V+MYpgWyc4WlKCKpyNGeTgL0xWIeUkpMm1h/4vODQSaywW4sE5T6d2UpHVTK+n9aUqTJRRNYfvbt5MOpNm6dSsLCwssLCzQ0tKyYo95oCVA/5X9pL+Xbhaq6okQ9WtsadO47/AzrDv3QqZzOumyge26SJKEKkvE/BqDLTE6o37MVIXxhSqNoEp+ukysN4avP7okyAMYm5mAVBELF1+ihaJuEol6URd0rGyNQEsQDxL2XAW1ZmOrMt6gB8l2kH0aSBK20yyyIkkgeRTUhB/XtDHnq83qpREv5nQJ3a8SCmrEAh6MYh1f1Ic/eeI8SEmWiK2KkR/JM/7AOOvftl5kIwi/Vq2tQT78R9tpNGxCIS8XXTLIT37yEy699FJ0XWdycrK5ziYQj8cXR5pKpRLh8IkBi9frPel9SrlcPqURtJaIj/UX9/D0jw6hzZbxtweRFLm5SPlsmboqs/W8Vr78T/8TgHA4zMc//nFiKwR6y83Rq9freL3eFYOq2fkyP/7msywczoLtIKsKww2LZ+8bY/MbBnjL29ajvWChdNd1WVhYQNd1JEnC4/EwPj5ONBrltvdtpbe3j3DkxBT9TCZDLBZDVdUVR0OFXy/xW3idiq+Kc/17zuHpHWOkZkt4vCqD2zvZdsNqgq1BoskoXq+XeLyZomOaJnNzc+i6TqlUYmFhYTHoWyjX+cH39lB6NkVIVbBKZfZP6WQXqrzvXZsJ+5674TueCnGyBkALaPRc0sPofaOUpkuEOkKLa2y5jktACnDx5osZ2D7A5jdv5uDBg9x11120tLSwbds2Ojs7T+kzMAwDr/fEhurRB8bIjxXp2ti6ZG2v4xSvSrI1xOyOcVL9EXzrkjiKzZjeYPLxIldJq7jgvefS0tJCvpCnaBV5zwffg1mQCSX9dG1sW9LjPTs7u7guTnt7+ymduyD8KriuS3p/momHJrB0i2B7kHB38yZocZ06wKyZFGeK5MfzVM+v0nNpD5HeCPGuCF3jeQ7oBhGfthh0NWYrLOxLUTQspKgHn6aCBFXDolVVCa7tZTZUIRQNY/o19mYLTP7ro1z+pkH6rutjslzHaPXR2h9HDqjYHgnXcWhv6WXhsWmk4SLBzgizhRpRv0bIpy07N05v2BT0Bj5VYaNPozPqp/fyXhJDCdo2tVHL1lA8CoGWABul5lyXtrbmHJlMJkMmk1lyk/h8iaEE3du7KTxbZvypWXStypHRg/g1hfP64qxvj1BrWJi2iyxLaHIzzVNRJEaOZNh/51H02TK65OB0hWlbE1+yVIRpmhh1A2/QRyVUJmkHkSJBpvI1SpZDMqxhp3U8rUGiVZNyuUEj2Jx751VlzLpFXZWo5Wo4roskNbMgwt7mSKOkKahJP3a6hjFexI17cVWZ3ngARYJqrUFiTQJlhcwJSZKIDcTIj+YpThZJrk2e8d+hIJwJj0ddLMSSTCb54Ac/uJiOeTwlEyCfzzM+Pg7A2NjYCamdhmXz9L4UI0fTqKF2BpYZuRofH2fTpk2ndF43XLuaumFx9KEJCoeyKLKEhYOWCLD1jau58do1bF33P/jKV75CuVxmdHSUWCy2bCEYRVFwHOeEc1mzZs2yr13TTX709WdIHUjTtjqJdqwgjOu6VBaqPPvzowRCGm+6fm2z/U+nqdVqSJJEa2vr4j3JTTfdxB133EEoFGL9hqFlq3oahkGtVqOv78R0TuGVI7nHuziE1yWrbqHndGRVJtASOKXS5A888ABDQ0OYpolpmjxxqMDwPSk6EwGUmI98LkfQ9JFpNLjxoxdz4drmSNvs7CzRaHRJg3symcMZZn45s7hAryRJuI6LL+ajfUs7ned3Ih+bj2dZFocOHWL37t2Ew2G2bdu2JOXiqQNHMTwxCvqxnjCjwsaeJAMdS6tTZXI1/vVvd6LYLpGulVMo9QNppnfPU4l4GLi0g1gsjIvMbK7KemSu/9B5hAfCfPnLX0ZVVT72sY8t29tWqVTI5XL09fUtjpYgNRemFykPwq9b+mCa0XtH0YLNZUAqdYvZos5ktoZhOSiyRGfUR088QCKo0ag0KM+W6bmoh77tfcw+NcvuHxzksGVSMm3a4oHmCPgz8xQNCy3iQ1UkXFxqho1Plmi1Ibg+idTux3VcVE3FLDWY2jdDS9khmfQwNV7GGw/g8SpIHgXZr9IISIR7Elh1i+zOCfqHEmjrk4wWq1iSgkdVUGUJSZKwHQfdtPFrKu1+jXbDpqMzzMAbBk47IMnn8xSLRSKRyGJ1u0bD4tD+BR657yDFkSrWdBkjVaJGhXCPh7fdeg2BFRZfnp4o8NiPDmKP5pGCHnKahONVSW5po3VVjLrenI+jqSpevw+zYZKdmCeUAnwqk6Uqui3RLct4MjWUqA8rXyfv2ugOxPwarm5iuFDvCKJ5lGZbClh284bR71FoDXvxKDJWoY5+MEOjPUDXmhZWt4eoZWp4gh66L+5G9am4LitWHC6MFYgNxFj71rWnv9SFIPwa1Wo1MpkMXq93cTqJqqrs2l9h951HaZR0AgNRPvTx7XQnn7tvMU2To0ePnlbhE9txGZstcXDfPLWqSTjqY+PmdvraQovX0qOPPko8Hqenp4fdu3dTLBbZsmXLCemjk5OTi8GUZVmMjo6ekHp63BNPTPLzrz5N62Bs2VH23GQRfBI3376WseEiR5/O0b+ujZvedQ7q8zq6JycniUQi7N+/n8svv3zZ1zp69OiKAafwyhEjeq9zqk9dTI88FZVKhXq9vqTh+eWh/Xgac8jR5uiYz+fDUUEq2qQXqnAs0KtWq3R1dS173OW0rGsh1h+jOFmklquBw+I8OO8LUgZUVWXz5s1s2LCBo0eP8sgjj+DxeFi7aQujup8nhws4Sg2vpzm6mMnmOZBzWJeqc/X6NkLHitJMTRSo53Ta15zk5s9plho3wx4000W2VSRZRgK8XpV8vo5RNkgNp/jYxz7G6OjoskGb4zhMT0+zds1aMoczZA5mqGaqSEiEOkO0bmglNnh6VT4F4UxV01UmH5pE9asE24KMpavsnSlSMUx8moKmyJimw/7ZIsMLFVa3BtncHSPcFWbqsSmMkkE5p5OeKiDPlnEkmAA8NYuKZaF2hFHkZm+5ZTv4ZJmk6eLrDDXnjakKTt2icTRPfbZIouFSsR2UhQZ4NXxtQSTXxTUdnHIDa1ZHz5poXWHoCRPqj1IcPUqwoDN43lYWTAtDBtcBj6yyyushAYQ0lcT6Nrov7j6juWTxeJx4PE6pVGJ8fBzX1Xjo7hnGn56lrtdJdMfxJv1o836kEQ/WhMG9X3uaTRf30jkQaxabwsU2bMyayeEnprAqDUK9MayoB8d1kCsmhbEckc4AofDSc9Q8Gu2ruknXpqgensZWbCLtXUgSGF4Vd7qE5FNJxPyYjkMxq+M4Lmp3mOALCkh5FBnHdakaNq5r0B7xUs/XMVWZqOXSE/Rg6Rau7ZJclyRvOuwdT1M3bXrifjZ3RVFesJh9sD1IcapINV0Vc/WEV7Xp6WnWrFmz5Du20Whw+DtH8Oo2HatbGTk6z9RUcUmgNzIywtDQ0Gm9liJLrO6Jsrpn5eIkl1122eL/t2/fTq1WY8+ePTz77LNs3ryZgYGBE+4HxsbGGBgYWPGYwwfSi53Hy4m2B1kYK2A2Aow+M0n6cJ5qWmfrRb0MDjU7slKpFOFwmFgstmKQ9/zgU3h1EYHea5ie08kezVIYLyDJEvFVcZJrkicEQS+n3bt3c+655y7ZFov7GVEkXN1CCmj4/QGyE/M4Hi/ReHOOXj6fX0wDLdQa1Bo2juviUWWSQe+KC46rPpXk2iRJTq3XXVEU1q9fz9q1a9l76Cj/dt8e5nWJvpYIawd78fl8VCsVOnrimLLCkxM5SnWTt23tJuRVMS27WXpcOUlwJYPsUVEdibpj4zyvQqhl2fhVhZm5GTZdswlN01i3bt2yhxkZGWGwf5CJnRPMPztPzXaoac2AMXRAJz+Sp/vibrov6j6lYK9RaTC2N0XP2iTB5PIFcQRhJfmRPPVSnZZ1LUxkazw1mUeVJbpigSVpkPGgh6phcWCuDMDasI+5/Qvse2yKcm+YWtKHa5g4GR3bsKjMV7AVCdmBeosf1avQIkn4bRdfRwjPUDNF0crpNEYLOGUDW4NAawSrblKdLIHlYDkOHk1pLg/gV1H9Eq7hYBzN4dYbDHeWsGLzDA0OMdgeoqdqYhkWuKB4FLSARrg7THJtkmhvdNm07NMRiURQNR/f+srjDP9ymuRAhJAvRijUvCF0VIueda00cjWyB7PsP5zBH/USTwRAAl/MR+uGVp6dL6P4VdSqheO6yPUGkkdGtl00Zfn1QCVZRg+7lEImUTNEQLdY3RGiLsvUZRnXdgh6VGZTBSzLhI4oukfBYzuoioT0vIl/siQR9CgU6yYNyyZp2LS0BAjKMvpCdbENVhJ+njySply38GkKB2ZLBD0qq18QzGkBDatuYVbNl/T5CsKvkmEYeDyeE75bPR4P/UNtHDxSxBzLo8Z8JJ/3feq6Lo1GY9lpHy+3QCDAJZdcQr1eZ9++fezZs4eNGzeiac12wXEcLMs66fIO9WpjycjcC+WKeTILGX78458iN8J4o35iXRGSrc33nM/ncV138f5tOfl8nkAg8LJ+Jul0FZ9XXXYeoHB6RKD3GlWeKzN6zygTo1nKx1bIjR5I07cmwerrVxNY5kbfdVxq2RqO5eAJelYMCF3X5fDueYYPpJAkifVbOhna1Iau6xQKBTo7O3Fdl+pCFT2r0+3AXp9KZl+KaHcEF4li1qD18m7W9Tcbh9RCGjXexaO7ZxheqFA3HVxcVFmmM+rjnO4oq9tCS+bzvRSyLDNthwl1r+FCpcHI4SM8e2iabn8LkiTRtqGXQJdKsDXEkVSZh4+muWFzJ36/B1QZ27CX7QGbm52lVqtRLxVRSi62L0DddqibzQIUHt0mHIKejT2LcxiXk0qlSCQSFEeLzD09R1qTOVw2qDUsQCLkU9nkleGXMwTbgsQHV25kj9vz2BHGfnEU84atbL568KV8fMLrjFkzSR9M40/4MS2HA7NFZKkZ1C0n6G2mFh8dL2AWG8ylq5RMm4AvTntbEKkzjJmqUn5mjjzgyhL+Qh2f6eDvCuNJ+lHagmjtwWaQl9UxjmTBdnHjHrTF19EoBDWUrE41p+N5XlDh9XqpO3VMW8ZTdDg33k8qoeLv9LPlrVuoF+vN0SjXRfWqeKPel31h712PTDKzN0Pvxk4Uj0Qmk0GWZPx+H7IkgSzhaQnSflmA+YNpJjS48n1bkCQJSWmmlfZnK8z/6BCOIqEaDqqsUK/oBFpDK5+vBLFkHM9WD/Wii9/1IFdtpIJBOOyhoTeYHplmwQPeviSJtijlukmlYdEwXHCac/SQJNxjQZ8mS2iyTHvI0+z0qppUZiqsum4VLetbyOsmVcOmJeRFVSTqpk1RXzmYcyxnxccE4ZU2OTm57KicrutUyrtpu6STcDBJe6dCT8tz90oTExP09/f/Ok8Vn8/HBRdcwJYtWzhw4AC7du1i+/btzaD0Rc4l2hJkuj634uOSJVOuV2lxVf7gz97LP/zP/81N7/tNIhEftVqNcrl80pE6y7IoFAoMDr589xz1ep1HHnqSc87ZJAK9l4EI9F6DXMdl+rFpjo5kOSQ5WJaD40BAU7AOZwgkAgxdP7TYU+W6LvMHM+x7dJLpkSyG6RCN+Vh7bicbt/fhjy+torbn8Sl+/q3dVGsmuHDwlzO89fZtlBpTbN60mdxwjszhDMXxImbdxHVdvJk5DDtA7mge2gOEt7Vyw1tX0xLyMp8rcf94jczwJK7j0hL2kgh6kSRoWA6zBZ2RdIX2iI/rNrazuu2lLy+QrRgcmi/THvUT84RRnQ7K8yny5WmMuoE5WqT1vD6CF3TTGvZxcL7MJauSDK1OEOkKU5wrk1wmuPJ4PExMTOAP+Ui0hQk0XDyGTWGmQFRWaQ/KrN/eQ+eqlQvC1Ot1jh49yjmbz2F+/zw14GChhiw1R09cIFMxOFCqc77XQ/ZI9qSB3s6dO9m7dy/33HkP115yM2/ZIgq6CKenNF1Cz+rEV8eZKtQp6ibtkZMvfu1XJLIjeQ6XDKS4l4gu49Ot5nwTj4LWEcQKe2h0hvD6FGp1C6nhEgppeM9pQz5WaMSpNmgM58FxUeI+GjUdf6DZJskSSBEvas3EqloUczqhmA9FlnDdZoEQueoydH43bt3kgtYLWHvdWmbGchzem6JaNJAUSHaE2XhuJ/GXMZWw0bDY98Q0ql/Dc6zCaDAUwuf1kkotNCvyuYAEsiIR7YkwezTH5FSRwVXPzQ0+/9I+RvamyOyaQ8nUkDUJR5FJDMWQV8gsKJdKhEMhIpEoM74aa/oThGfKNHrDqEGVWFeM0oMu1pECSUfG6zhoDoTrDo2qiWM62E6z9KbiVVACGt6Ql4oMZtlEr+lofg1f0kdsMIasyAQ8Kl7FJVMx8GkKjuMSXWax5ONe6oip8PpTrTYoFeu0d4SWLfbxUtiWg3Lsb9I0TRRFOeE1du/ezUMPPcTc3Bx/+Ic3kEwmsW2biYkJVq1aBTSrdJ4sVfJXyePxsHXrViRvkMcn8uzavY+rL9zCTZdvweddvlNu47kdHNw5Ti2vE3jevZ7ruJTLZfR0nY61Lbz9HVtJp1MgF9mzdxdd3TcwOzt70kXjoVkIZmhoCNd1GRsbW/ycnvrlNLlMla0XdNPadmrt7vDwME899RQ7duyg0VB547WfP8VPRjgZEei9BlVSFbITBSYkF0WRaTt2Q5YuG0w4Dt1jeeqF+mIAN/HULPd8dy9zZQOiXhS/wlymwvhPDjI3nueN7zt3cV/XdXlyxxh126FnUxu4MHM4za77hokM5mivt3PkmeYCocG2INVSlf0H9lO0UtzyjneyMFvBKht0tMdw51MU22J88+FDlAgwkPSj6RbmZIlGRse1HZSIl86uMFJLiJmizo93z/LWc7pYt8L6LKdqLFOlpJt0RHyYM2Xs8RLBnhgeKYKmqqRnUxzdsZuEXKNz6yrGsg1G0lXO74+z8aJuHrljP0a1gffYiIZj2xSLRbxeH8nWNiQpQNa26NjUht8ymdx7gK1vvYzuIT+br9q87Dk5joPjOBw+fJjp6Wl23rOT8ESY6Nr11BsuXfHmCKBEcx2u+aJONeijPFPGbtgonuWr3SUSCe6++26CsSBbr1kr0jaF02bq5rGARCZbNZr/Xyad2jVtHN3CNWzsQh13rkxekVilBEBxcerPlf12LRfHBSesIYV8aK5LuWYSqFv46xZ4FFzboTFRxNFNlBY/lm2jvKDMt6rJaBEvqlrFtBVK08XmugAuKIrLqnPa2XZ5P9lMhqmn5nn84SkyDQtdbzSrVroukunyxN3DbDi/k6vfthHfCiOVp2NsOEduqki8O7JkuyzLhEIhAsEA5Uq5Oec2GCQY81GYKnJ4X2pJoNfeEebtH9rG/82NUs6VaLgmredtxA4v35NdrVbx+fzIikK6bBALeGiRQO0Ioukajzz5CB2NDqKbO4h4QnindeoHsrgNG9mr4PUqSF6F5uLNLq7l4mZ07IUasu1gaAqhuJ+W9S3Yhk0unWP+6DwzMzOUTfC0D6F6Y2zsijDYcmJhLbNmovrUJevtCcKLKRR0vvMvT1CYr3L+tUNcd9P6l+3Y//W9vYweWOCtv3kuQ2tamJiYWHYEas2aNTz88MPEYjEikeZ1/fwql8fX43sl6Q2b+0dKTO8rkKz38Oi+AiOTd3H9uX1s2rTphDTOtWtbWHNxL4ceHMPUTcJtIXRDxyjXaRQcAi1h3vyBc9lyTjff//730TSN0dFR9u3bd8I0nReamZmhq6urWfDKtvnhD39Ia2sridgq9u0oUcvqzI7nuf2jy6/H/EIDAwN89rOfJZVKccstt4jRvJeJCPReg+yGTaNuYSoSgeeVuvZrCg1sDN3CNpoLatYLdR676yjTukn7YBzteC9rzE+ubLB3zzztD8W48OZmo+o6LvW6hXo8hVICxacxM51iqKuL6V9OE+4K4z12E+IUHPK5PC3JFoIhL4NrvTi2Q2G0QHo+xZ6Sh+mixbahIPZwnvLBDE7VRPIqSLKENV3COJxD6wvTu62Dad3k7v1zxIPakjX4TlfFsJrpUZKElWmWCrYUF0WS8QcD9K0ZpEKa/GyBJ81dmP4kT5LDTQfwhyy83RLje6bxxzVsrYHjuvgDIeYKOoWcjFUu4wxGcLpgbm6BVC/MTB/hHH8fe372CGuSHrwv6I2XZZm5uTna29tJp9OUSiX0lI7T2orrS5zwHlwX3BeZmlepVAiFQvze7/0ezzzzjCjcIpwR135unqlpOcjScz3drtlcUNtcqOJUTdyGDbaLtVDDTtfweRUs61iRFN1Caw+iRI9duy5A81iKJIEmU62aRDI6SsSLna9jpWsocR+SJGEajcXRvON0XaeYzuJvK/LWd76N+ekydd1C88i0dYaQPBZVvYJra+zfN0kpXydyWS9dq+KLcwst26GYqfHLe0Yo5Oq8/UPnveRgr1I2cBrO4mje4vZalWAwgCTLhMNhXMelUm1WDpYVienJBaanl3ZkWbZFYlUNK5NnU2A1of4g+9I647UaMb+KKkkggVFvBuGm7VDIVfCqEv2RAIWJBRKbElQKFep6nQMHDhCLDOEtBFE8CmpvBMewcGsWruXgGksXXJa0ZnuM5aAcG6Wz6hZ2w8br9TI5Ocn0zDShYIjLtm2iv79jxaqb1VSV2ECMYNtzQaBRNqgX6jhm84ZZ1mR8Md/i94ggpGbLZMaL1EsGYwfT8CKBXqNhkZqrEE/6CYVO/nc0M56jlKqwkKowsKqZHaMoJ3acVioV3v3ud5PL5RbnwUHzu9u2bebn59myZcsZvLvmNQugKS9tpHK2qDMzXiY+nCPqCxEyPbj9fRSKRf7jP/6D9vZ21q1bt7jGMcC2yyLUzBaGn5xnZlcKj6ahejTC3SG2XdlJLGZydGSUzeeeR2tbG67jEAwGmZycXPE8yuUy9Xp9cU0/WZYxDIP5+XmsRhjLVHBd95RTuB3HYXx8nP/xP/4H//zP/3xKaxQKp0YEeq9BnqCHQMSLf8GgZDn4jgV75bpJqyTjj3gWe1PnjmaZmi4S6gk/F+Qdkwh7mQnqHH12jnOuHMAX9SErMn1rk6TvG6HgU5sXatlADleQF2QCXYElX87hSJhrr72W1EJqcZusyMSH4gzvmONA9hDr37wee7SA/kwKOaSh9UWWBCRO3aIxUgAHei7q5GiuxsG50osGeo7jUKvVqFari/8qlQrVapUnZ2rovk4gjCQ3Sw9UKxU6Op5LqfR6PAys6qV/VZDHD04yMT7O5uQQ55+3la3btnHHN3cx8uQ0SsWPpihksiaVuoUW9dFxZS/rL+tDll0eK86BH/raIyTaOhmrmYTUKDds7lgy5zCTydDb27u4Lldja4PQVIiFuSpPlnVKuknEr4HbXNA55FMJmg7BjuCyo3mFQoFKpcLAwACu63LdddedsL6OIJyKQlFn5EiG4YUyM9kaecMi0BXGo9vYqSpOzURSZSSvghzxNoOObI16UEXzq8h+Dcs0cEoG+r40SsSD2tosOsLzVvDRFIW6YmKkq2j9EayFGsgykirj2A7KMjdBkiRjWxaBaJxgLMDa5NJRpHQ6TUNvsOfxNKW6RSTqw285SwrIqIpMsj1ELeThyFMzPNQR4k3vXH7kfSWu6zbn71QqTE7keebxWarZKg09hud5KYyu6yI9LyVMkqVm5UzHJd0oguvQ3t6+eCM5OzuLoij89m//Nvf23MsaeQ3Zo1kSa+LsT5fJV83mqKRjY6MhqRqSK9HVEmRjawhvVqflom4G3zjIgR8cIB6Ns33VdmaPFBi1a8gtwcXKmK7tQMPBMW1c59ha7DLImoIruVQyOqoDxckihYkCwWQQwzbo8nSRqqQIxALIUrNgVC1TozRXRi8ZmA0HzSMTCHmxGhZD1w2B25xLnhvOkT2SxSgZiwtWS5KEN+IluTZJYnWCUEdILMXwOte/KsGqC7oY3jfJ+VeefN5ZvW7x3X97iukDC0TbQrzjI+fT2RVZdt+9e/cyXXiYd/zW7Ww6t2PFOXaWZVEsFlmzZg3J5NLibx0dHYyMjCy7NFSqVKekm4R9Gu2RpYuW102b4YUKe2eKZCrNxdfbIz42d0cZag3iVU/8Xm80Gov3MYZhnNB5O11sUKtXaQ34cS1wZJlIOMgbrjiXq668komJCfbu3UtLSwtbtmwhGAzSaDRQrnLZfuUg+ZyL1XCIxHwMrE4wXaiza6bIXMnEcVxmpxtcvW0tgdZWuqK+ZTuPbdtmfHyczZuXtqEXX3wxXq+XX/5yF4l1Awz2b2LbRT3L/l5e+NmPj48zODjI3Nwcn/70p1/21N3XMxHovQb5k35ahhIMpasclh3mijrgEtNUBpFpWdOyGIzlFio0XIivMKnfE/FRKOg0Kg18x3rhL7ymm2ef3YNU9yNLEh0b/KwKRsFiSZDn2A61ao14SytrorElx5VkCbM1gr5/Fn/BpHYoixRQUWInBm+yT0XrDtGYKOLpixCLedk7XWR90oNr1pcEcMf/WZaFLMsEAoHFxVBDoRCJRIJgMEh0lcmd+1LYjovaFkRSZdyazfFic3bZAI+C2hZA9gfo6e3m+rWbsRZG+da3voXX6+WKay7lhpu38NST0zx+YAGjYtDfGaFjdYIgEubRPI1CnYFChFUd7XQM9aNEfSQCHvbOFAC46dwufJpCo9GgUCgs5rtfeOGFAKT2ppje/ySN9Ax0r2Ym31zLJ+hV2RALELCby0y8UCaTwbIsenp6yGQytLQ09xGNo3A60ukq9//sMKM7J9D3LiCFPTQcF7NQZ+6peXyqTKg7gr89uORG3DVtHNvFBmJeFRQZWZZRW4MoMS92uYFdLKDWTFTTwg55UCQJWQZTknBsF7dqYhfryMFm22QYxgmjebbjUi6b+BIt5CIhdhxOsbo1zEDyuTU/dV1Hz8vkU1UiHSGUmoWVrqF2hU64SQkEPdQSfg4+OcOl1w4RijVf7/mdRo1GY9nPSpIkzIbMI/dOMXUwQ2mmjDFeZLhoEB9K0L42ga7rJOMnjs5Dc3BTU1QGhrpYWFggm83i8XgYHBxcrFZ302/chFE2kBSJzOEMF4b8VFtCTBVrTKUyBLxeOltCdAU9hOo2dq5Oy4ZW+q/sxxP04AQc+v39VMYrJFsCpGSoWTZhpfkZS4oMfhnF/9z3geu42Lk69XQVpWZiRjzUQx4aMyUKhkmw1UsyEmBz21YU2aV6uMrw+ATp2TKZUr35+5RBcly0gkFLVxj/zgnGHxzHMiwc0yGQDBAfii/+zlzHpZ6vM7Nrhvnd88QGYvQtM19ceP3w+VR+8/+7kL/5m/to2J3AygVAJkZzTO6eJ5D0szCS4+Ce+WUDvf/6r/9i//79dPcm2HZhD47jYNv2ktG640ZHR1ecjxYIBBgeHuaGG25Y3NawHO47mGLPgRR6wcAf87J5QzvXbmzDqyrkqg1+umeW0UwVVZGJ+FRMy2L3xAK/PDJDf1TlDYMRQt7ngj3XdfF6vQSDQdrb25etptlrOdy/b4JatJVqtYG3M8wlQ62LnWQDAwMMDAwwPT3N/fffT71eZ/369WzatGlJe5gq1fnBM7NM5KookkTUr2HoNSRF4/GJEnvmamzrjXHVujY8LxgkOB6ULfc57dixA8uy+PAnriEWiy37eT6fYRhMT08vzvNzHGfZ349w5kSg9xokSRK9l/Vi1kx8hzOUbJPdu3ez9vxzGDqvh64Ln1urzuPTkFwXy3bRlpniZTVsPJqyuPD4k08+yYMPPkjPpgTvevvVuI7D3XfeRbSSxN+29Et4YmaeGUMjl0rhUSU2dUXpTTTnh01Nz/LY4WGiDZXa3gWckoHa22yIXcfFskxM08I0G5iWhWWamJkq0zvSNDaFSZsKgbyXte0hQqEQwWCQ1tbWxaBOVU/+p7vOZ/LIaJ58tUGyLUij24t/pE5jotj8DL0Kvo2tqG1B0hWDRNDL5r5W5uUa0WiUcrnMI488Qn9/P2rPAKrbwuWtITRA37dAZSSPo1tUTR2/348+m6O84OJdk8C3oYXBlhD7Zov0JgJcsirJyMgI69cvTUXJ5/P86OEfMTM2Q5vUxpCsUHIdXNslZJgEHZ2ONw6eUIhlfn4eVVXp6OgAmikUxwM9QThV83NlfvhvTzF/NEso6Sc2lICGjeu4kK9j2g6G7WLOl3F9CsHn34RLEqbtoEgSHlUB0wFNRgl7kBQZNebDbdh4inV8GQPdp6FEfM2lS44VKHGM5lw/Oew5Ngp2Ys/xXLGOUTRIrGsl2tdKSbfYNZ7DwWWoNYRpmqRSKSqzfhzAoyk4XhdHN3ENG8mnguMutjG246AEXFKjeR6+bx+bLmpeQ8c7jeLx+Iqlyut1i2995ZdMPjtHpCtM7wWdTJg29ekS+UNZXMdBaXGRl+mlByjndPwxH2s2Jmk0yvT29mIYBnNzc7S1tS1W6fWGvQy9aYhYf4z0gTTObIlEOo+Vy6LXdYai5yOVGwQ7w7RubCW5NonqVTEMg0ajQW22Rmwwhjfspd1yGF4oY2nNxeNdF2qN5jqGLiBZDmpWxy0Z5C0b2aviuCAHVeyAgmJBNa1T0GQ0SSaWtljYO0fdp2B1BlEjHrxacx6kndMxkn4mAgrzP9hHwpFY/4Y+2s9pX/x+WfzzkSX8ST/+pB+zZpI9nMUoG6x64yqx9t7rmGEYgMujjz5Ka2vriotvR2I+/FEvhekSqk9lLjVBNpskFostScl84xvf2KyEe6wDdGpq6oTFx6E59y4cibHn6Tlsx2XjOe34/Rqu6zI3N0drayuSJC3pSN09XeCJR8cJHynSYtrossNDswXsWp5VMY27jxaZKhj0Rr0oDthlF4+qMpgM4soqU4U6Byo+3r6mezEr61To1TJvXB0j2LOGumnTGvKyepliJ36/n/Xr16NpGgcPHiSdTrN161bi8TjpssGPnp1hvlinPxFAztcpH17Arxu0eyRau31UFIlHRrJYjst1G9tRj13D8/PztLW1LdupvHHjRtLpNPPz86cU5FWrVdLp9GL106mpKbq7u0/5sxBOjQj0XgMaDYvDB9NUSgbRuI+169vwRrysuXENbee0UZmv8PT4Dg5be7nmmu1oz5sz0jMUJx72ki7p+FuWNgam7UCxTs+5XQRaAjiOszhitm7dOkJxP4cPH6Yr3oWdsZeM5pWKJcZKLnPVGvGAh4ph89REHg2bh37xc57cvZdCoI818Q4Wnh3H1FwsK4PrNr/kNU1DlRUMw6Bu1FEUhb7+LmRkoltWM5yvkeiNQ9iLrSn0dkVOuTFcSJUp5HU2d4TZOZIl4JFRN8RR2vwEpObNlJoMoLT4qZsO+WqDra0qk6NHGRoaWsxtP//883nymT1896FnWbdhI5oioe9OYexLIyf9NPwuAS2O1+elEZKQJAV9TzOF1X9OG2Gfxu7pAklK9PX1nTC6EI/HSbYmSXWk6PR3Yk4WIa8jI2GFPDg+FT2jkxvOkVidQJIlpqenCYVCi41oKpWivf2lV9k0dRPXdlE8yopFX4Szh66b/OTbz5IaztK5vgVFUzBdidrTc7h1i5BPpeBRsB0XaiaF8SKaV8UT0DBth6LewJYlfJJEw3KQDRsl5kXyPfe3I3kUPH0RvFkdc7aC61UwFRnNdpup5cfma0mShK43O0yez7AccoUaLUEfrf0xFI+C36OQrTQYTlWwCynuv+8XmKZJm3YutqpQr9dxbRenYmBnS8gRD5IEqqbh9fsWb050n4GqBJe96VvJ/t1zTO9L0TqUWJyXF+2P0cjqIENhPE8yllystPl8tmlTmivTtSWG5mnQ1bW0N3xhYYGFhQVaWloIhUKoPpX2Le20bmzlF3c8jqPEmZ4aRvHq+Nf6GTxnkEhPZElly/Ej43jqzWVzjs+LaQ/7KOgNUkUDWQbdcDBsu5lNazt40jpStYGuKSiqTKtfxa8p1Ct1/G1hCKjYqRqyayEF/cymKpiKRLQBoZqNFpWbxXl0E0/MT7g7TGO6TE2SSQVkpN0pNJ9Gcl1yxfnDWkAjsSZBfjTP6H2jrHnzGjGy9zo1Pz+/WLXxhe3B83V2RXjr7ecxfHCBsalD/Ps3/5677+tg7dq13HrrraxduxZoXlfvf//7Mc1mdXDDMJbMXYPnUjafebTMwYcnwXU5cGE3V72liy9/+Z+Zn5/nD/7gD+ju7mbPnj2L6ZsP7Mti7pvDMh3ctgBSzsI/U2HB6KY30ELJNVjT5kW2HTyzDczJEkgg9cfwr0+yqqW5tNNIusKmrpUXUX+hmZkZ+ro76e9bvhK3ruvMzMyQTCYXRyj7+/vJZDI89dRTOI7DrLeX2aLN6ngA45l5asNZ6pU6wXAAvVimMmfjP7eN7s4QT03m6U8G2NQVpVqt4jgO4fCJxfJqtRrz8/O87W1vo16vv+j7KBaLlMvlxQqmltWsq7Dc3EnhpRGB3qvc+FiOn31nD+nxAq7loGgK7WsT3PSec+nsipBck8SJOcgDMrP6LD+/5+fccssti8+P9kTZfF4Xjz44zhwS8agPVZapNSxKCxV8RpU94w/h3puhra2NoaEh1q1bRzqdxnVdDh06xAW9FzB59LlJubZtU6rWqNoSMb+HoFcl6FWZLej8fMdOdt51Fw1XRhvoRPGqaLJKrDWGvy/WrJd+TCqVYm6yOToVCgaxbBuPLIML86U69++eIV6xIOJlYnM7N5/btdirtJL5uTLf+dLjVPI6512/mq29MR4+OEVXSwwlAb725siX5TikygaZYo0uT52LB1fR1dG25Fg+n49Y3zo600FaIj7sXJ3GcL4Z5Ck2iiPj9T0X/CrHKkQZR3N4+iK0hr0cmckxF4uyZtWJuf1zc3Osja4FD7REW4iui6J4FCRJQvWpWHWL4mSR/FiervO6sLtt2jralkxS1nX9jAM9x3YoThTJHs1Smio1Az2vQnJdc95MsPXEcxbODgf2ppg7mKF1dQLleAeKJuNULVzTxtMSIG47lHSTRkDDLBnMzZRwEz50w0aWJcIhD1Qr5HNV/KqC3xdEsp0lxQYkTcHfF6FxOEt9ooQb8RCqmlgBD1a6hpuvg09BesF3u+vCQraCt9wgsiqBFHqu8yrkVZjPFHji0TupFTK0tbbhKuDxeBdv4qwa+AMBlBXn+TardmYyGR57dC/JeC9bL+gj4F+5QMvR/QuAi+ZVsEsGruUQDXrQeyJUJgrYeoNGycLtcpCeV8zGqDaYO5Im2hvglvdcQEfn0ps6x3JIxpO0trSSzWXJZDLE43GCwTD//i8PMn+oTLVQpd6IE4i3QZeP2EBsyTEmJycJ1ANkMhk6L+hkYfcCdW+ddCZNenIaM9pHTreRJQm/R0aVZOSSgVttUFYlXBy8SnPJhHrdQLMklFY/ansQw6PgLlQxC2Uc20HSZOoyKNMlnLqJty+K0p1AbfHTGC/ilhuEOkMYls18uYH3mXmUsJeST6FyrCpryKfSGfHh0ZqfkyRLxFfFyRzKcPSnR+m+uLsZrLogKVJz3deoF0/oxAWuhbNHf38//f39jI6O0tNz8rld6ze1sX5TG5OTEQ6PPMD+/ftRFIWurmZGk67rzQ5lVUVVVaanp5cdLRoZGaGjvY/xvQ/gj/lQNJmjT03wf77x/8Olis/n4+mnn2bz5s0sLCywatUqJEnCcSEUCBKWJNRoAMusYdkulVqdv/ryf2BqITSjxJDdRl8jhhL14bou9WfnAfBvbkWRJfbOFFcM9A4fWODBuw5z4RsG2XZhD7VaDb/fv+w14DgOk5OTqKq6bApqS0sL1157LYcm57l351FW93XROJhFP5ShHnCIbTiW3RBycS2H2lPzhK7oRZJh70yRDR1h5ubmlj12o9FgdHR0cc7eC4PpF8pms5imueR3PDMzc1odb8KpE4Heq1i12uDOb+4mM5anZTCOJ6BhVBtM71vgJ9/czYc+cSkej4plWVx99dXU63Xe9KY3LTmGrMpcfPN6XGDfU7MUsnlsScLjunRFZGa1EVy1waOPPspVV11Fa2srHR0ddHR0MDo6Sm9vLx6fB5fnCiqkF9J0trdxIL9AQbcIeVXqlo0iSVx/7TVsP3cd9z3wIEf1AIFACElVUSQZV1ra0d3e3k4kHGZ0bIy2tjYk3aHmM6ikU0xl6mzNNGjJm9gxLwdDHi4cTNAbP/nSAdlMlUqmRr1Yp5iu8o4b12FWsmRsjeF0gyJlAFzHQTYqnNcV5uZLLjohB/24kXQFRZFQZRl9toxTt5DiGlbdJBw5cU6AEvHSGC/SmC3jXZugpteoSieuqZfL5ZjfN0/jYINVa1cRbA1i2S7pch3dtNHqJm1hL7HBGEbZYM/P97Dx+o0Eh54LvmZnZ+nsXH69PsuwmDqUZm66hOZVGVrXQrznuSI4tmkz+cgk88/OIyHhS/iQVRmrbjH5yCQL+xbov6KflvUiJfRstPeXUyBJeI4VC3ItB2u+ihr3YtdMnEoDLaSRDHmpNSzydRMjq2N7IeBV6EpGUGyXSq6OUzOoxgNUbJt8XifsU0kEvYt9Ot6WAIFUFXe8gJSCStxPqWYgVy282TpyrU4oFsLpUHE8MtWcTmG+jJuro2kK2VSVwiOThHsixDojNGSIRSP84R/8PruffpJcLoe3HiG9UAP8zYIfEieMqh1n2Q6S4/DgQ/fx4FMF0ofjdITzZOZ0br71nBU/s0a1gVQ0qe9NN+f42i7IEnFNxo1olIsG1fEyC5KCx6PhuC61so6tuPSc0847br+A9o4wjuVQmi5Rni1TnitjlI3mUhGaQrA9SKgjhC7p/OQHv2Ti6QKxzihtqxLo5TqpoxmO7Kmx7fznziufz+Pz+Jg/Oo836iXYHkTP6ex7eB95q4DhieH1aAwEvOgNh1rDwtEbKIU6DY+CR5UJehQsx6VUaxB1JeSIF7XFjyRL2DEFKSOj+zWcqAcPUDdsJFnGF9DQBmOoCT9O2cDK6SjR5iiqT1MwAyoTmTKpxyao90UWv0ckmnOCNnZF6Y34moVdZktU5ivMPjVL9kgWb/S5TjRJfi7YS6xJEB+Mi1G/1yjHcUjNVwiGPEReZK3OU9HV1cXq1avx+Xz86Z/+KU8//TSDg4MYhrEkKKlWqycEj6lUqjklJOQlkgwweygDErQMJvnR33yPu+76L37+85/zG7/xG8RiMYaHhxePmdcy/HziMIGxIs5EEVuWqA1G2NqT4IlwErNUQLYgoqsorX6UY3/PFmCOF/CtTxILeFgoGzQsZ9l7kJnJAqnhPFN9MbZd2MPExATr1q1jenr6hPdRqVSaU01eZFpLxlAIROIELJfyeJ6aahJve66TW5Ik1JZAcxmsiSJtW9qYyFZ5cv8w521YdcLxbNvm0KFDJxRmWUkqlUJRlMWpJ9AMFFVVFTUGfkVEoPcqdmDPPNnxAm1rkqjH0um8QQ9tqxKkhnMcOZRh85YOOjs76ezs5Mknn0RyJerFOq7jonpVtICGN+LlinefwzmX9TF5NEOjbhFvDdK5JsE//9tBpqcXFhsHxeunalj4NYX9+/dz3XXXoc/rSIqE3bCp6lXC4TCKIrO5O8qT43lmizqKJLGqJUhb2IcT6Oc3brmZA2mDyYM5pA4PsqThlBqLjd1x/kCADevX4zouTrVKdGsPhYifaDBLsGDj2gay4+LYzvOL9y3LdVw6Qh7WrW2hlNM5d1M7mfl5br5wLa6i8fDuBpFkOwsLKWTb5g1btxEJnvyLpmpYeI81wFZWB02iWq0Si6+8gLnkU3DyddLpNK3JBJWGteTxSqXC3NQc8pyM7JEJtgZJlw2enSqQqzaXcpCAsE9jQ0cYn1WiZ30Puf052je0E2oP4boujUZjsYjD8+mFOnd9azdH9sxTN20kIBbzs/2GNZx37RCSLDHzyxlmd80207/8GumyQaNhE/ZrJNcGqMxXGLt/DC2gEe079bQS4dWvUNBZGCssWW/RnC1jZXXUzhBK3cacq+AUDSxFouaA5FHx6hYe16JayuN4PUgmeJM+ggEVw68iyxKS1KwYazkuLUEvqixhpqvouonlkVFqFgW7gd7QCakKkkfGUhSsbA05Vabuujimg2I7+FuC1CIedBk8ZZPc7hS5IznspJ8LL+3DqFW45NJLUBSFkf0LpKYOY1oOquMiaXJzDb1lFDM1wokg7/vEb3LPgzsojuSwTQfzBdfp8zWqDdRUFXssj9kToeFVsWWQXBev6eCrNpBkiUhAxZou41guDduipT3IpsuH2HrtEPFkgOzRLPPPzFOabY6gq34VyaPgSmDWHGoH08w/O4+lWBTHaqiuQvjY78kb0Agk/UzuX6BcNgiHvZimSalUIibHqKaqRPujyIpM26Y2+gv9VJ+Gou2h3e9BVSTCPrAdL+ZcGUNTsRTwKTKSJKHhYtVsdL9GuCOMpCmYpolqy+h1G1MFNeHHlSRUx6Vi24RNsAt11IQfK6OD6Sz53B0X5i2HZKpC12AcNXxsXVLHJVcx2PXLKdK2RMhykCQJT8iD6TVRAyrJtc9VPnQsB7Nmoud1xu4bYy4yR2JNgs7zOvEtU+BLePW6+78O8eyOMQJRH2//rfPoGzjxu1RVmx3YLxa0HN/3t3/7t/F4PIyOjvKGN7yBhx9+GNM0WbVqFbIsMz8/f0KnqGVZlMvlxcDtxt/cyiO/GMZxHS65ahXJZJT3ve99nHPOOYtTJbxeL4Zh4PV62doXY/qyXg5FPDjlBnLIw+Zz2tnWl+TARRdRzCxw9Nnd+Bz/8RVmgGanhes271cAcN0lHenPd8mVA8STAYbWtSwbDFUqlcU5c6ea2VPQTTRVxikZlFJ5oms7lu0UkyMerFSVkCwxUqygru4+4ffhui4HDhxg48aNpxSkzczMEAwGT5i/Nzs7u2wlVOHlIQK9V7Fivg6uuxjkHecJaLiWTSFbW9xWL9apDFfYe2gvVsXCdZvzreKr4iRWJ4h0R0isipNYtbRR7ejoIB6Ps/6C7UzVZB5/fIqGOUZryEPSk0DVPIS7VMLtYcrzZQyvsdigdMX8XLlOpVgz8agybWEvsiyxkMrQ0dnBamrMj5RhVQwVH419GSRVQn7B+lWSK2HNVtG6wmhdYYoFnQsHEpSCPvJxP1ZQY0N/nPaTLJ6p53WO7Bjj6N4U+WIdcDl07wiRVrjivZfij6q0qgZabY4NG3qJRs8keHEoVyokO7tedM9KpUI0OoBRW7rcgWEYjI2N0eXv4vDCYeJDcUq6yS/HclQMi5aQF1WRcBzI6w2eGElxxbpOAokA2XSW3EgOw7QZG5li7cblG8ZHfnaYfU/N4u+L0BnyYjsu6fkSD/7XITr6YiQ7QqT3pwm2BTE1mV3DGVKlOo7j4FEVVrcGOacnRnGsQGpvikhvRKRLvca4jotRMrAbzfXSFI+CJ+xBVmQahoVtOYtzed2GjTVfRQmoSIqMFJTx9EcwCnVKsxVs3cQny9h1EytrobgWWSlLz5ZVKEk/vqyOPJyjVDWpe2QkRSFXaWCYDpG6hTFVouE4WD4bSTYJehSkah2vFgDLQWpAtWzgLZl4VAW1K4C/PUSgLURIgnS5Qd2ycV0FajbaTJnCwTT61ijJlmbb2LcqwZ64l/JClWjE2wz0/v/s/VeUJPeZ5Qn+TJu5VuGhIyMitUYmEpkgFAkSBHUJFrtY1VVbNdV1uqv7TJ196Dmz+7BnT+/uy/buOfswp2dmu0ftzmyL0iyySBRJgCChNRKpRWgd4Vqam7Z9sEjPDGRkAmSR7CIr7wMOMsLD3N3c/G//+333u1e7m+jZtodVMjn+mSmK4zl+53d+g8nxqwSezomHd/9eB37A8ivLpAOBsGCw2ujhJ9RoFi8IUBsWcskkJcuMJzS0QwmkpE4hn0MLwalb3PjG9b6bZnwgDoUYW12H5ZqJ40XGKJIAuYRGMSViWCHhWg8aPfyxFFL2NpkJCbm1L1xeXmZ6eprS5VIkvd6W4VquRfF4kRVXwJmtQ8MiSKiImoQYBNB1cCUBwhAxCBEcD8EPEdMa3ZSKJUEc8FwP2Q6w/QBREhH8EGQBSRQI3ZCeLGBUegSDCbySiXjHfHgQQtP0CFSJ0PIJWzZsEz16Hom1Ds3lJmsxhZMHCyiq3L922+ttcntzyHr0M1EW0VIaWkqLZq2aNhvvb9BcbjJ6bpTCwcKDeIZfAARBwLW3VnFtn8pCnZnr5V2JXqFQoFwu31Ox8mHcMjIaGhpiZWWF4eFh0uk0L730EmfOnKHVau3oIEEk2bzT7GV8Is1v/ZOHdzymUqnsCEgfHh5mdXWVyclJYqrMV0+PsTSZ60cj7dk2o0sbCl5ugGe+9Cz+hSr2zRqRXDzEb9joxwYQVYl222I8a6DeYyQlZqiceiTqQt68ebM/v+h5Xj/u4V5OoR+FRqOBYcQ+kqC5rovv+6RSd8/lXbt2jf37938sQr60tEQ+n78rH8+yLHR99xiHO2H2HBzbJ5N50Mn/cfGA6P09RjypEgK+GyApt7+MruWBJJLYJj71+TqLLy3Sne0SZkMKo9FNz7M8Ns9vsnVpi+GTw4x9Yuwuo41f/dVfZb0n8t/+9asMjE4wkErhey4za1tspTP88PoWnz40yMDRAeb/wzz7zu5cVNKGQvqODKnAD/pB5bGuy8CeNGu5FJrikmQA+2YNv2ohxBUEUYjc8bwQZTRB7OFhSo5H0pD56sNjVNoOm60eKV3hofHMrpkzEBmJXP7bGd58Y5mqISOlos3EYq3F4EZIOnEN45iMaZo8/vjjP95noMk428YGjcAkqRr3lIT1X0/HgvE0umHgtNsktjcwnudx/fp1jh8/zuxzs4iqiCiLLG+2afYcRjKxfvaXKEI+rrLlBcxXu4xmDLSsxrt/eZVF26XRNpnYW+LLv/sQQ9O37dythsWNC1sIOYP0tnmOLAkMDqfZvFnm2vl1jh0pYjUt8gfzvLfYYK1hMpjSUSSRru1xY6tDPqFRHIzTXGpiVswH83q/APBdn9Zqi8ZCg9ZaC7fr4rsR0RNlETWhkhhKIOYNBKIuOYBXtwhMFyl/+wYqKBINVaJTNIj7BoEb4EkCxnScTEEjPzmEuB3ZIiZUcoZMbK2NWe1hdl1sIcRpOQRNGykENS7RFm1M1SJvqEydnsLtBnjXa7hNC88O8FMqkiaj53XiY1EhRgdGswY9x8cPApSsiBQEbN4ooygCIyNDiJKAYsgcPl3kxntVmstNEscGoiiBbYQhtJsW7dUmk0eKfPJLkQOuIAg88cmj9z2v7fU2lRsVYnuzeJU23o0Kcs1C1ASkqk1Y6+GmNML9aeZLLRw1RJnSEVs1dFViT1pHXu/QvVlDyujMeR6VikTP9YipMoosIgoCvh+wVO5ww7YYzafRD+bhR4u03t8gcWQAp+fQu1IivS/L+f/0Fk7gsPfkXqychVkx+8Yspmni+R5iMklzoEsqoSJWevgNi6BlE1geXt3GCQIUUYiiZzSZIKVCQgXPp2t7KEKIoqoENRNfAikIwfVh+3kUSaTjh6QtD79pEdjejvgc0/GwPR9DEXF6HsF20cGv9rDn6gSmR2IwTsvxado+he11UkkomCUT13T7RO9OCIIQha2nNTobHWa/O0tnq8PE4xO3500f4O8lRFFk4sgA115ZJjUYZ+xDs6a3EIvF2Nzc/LGPn0qluH79OlNTUxQKBR5//HG+973v3TX7dUuy+VEkZ21tjZMnT/b/favT2P+3JLJ34G7HyxNjGb59cZ3hdBL1RCSL9DY6IIB+uIB+uIDnB9huwImxzD1JjuX62F5ATBYIggBZlllbW2N9fZ3HHnvsJ5I6ZgyFZqtLsZDET5sEHafvL3AngraDtj/HZr1BIZcm/qGIrpmZGcbHx+85j7e8VOfl788gSxL7ThgcOza962M3Nzf7hiz3Qrnc5U//7dtYHZtnf+sEJ07du9j+yovzfPDaEsXxNF/+R8eIx+89d/0PBQ+I3t9jHD4+yJvDScpzNQb25ZFkAd+NKmH5iQwHjxRpLDWYf34e3/fJHR3h0uwqV5ejeYtcXGWiGCMrCqy+vUoQBux5cs8Oq2vZSPDXP7rAyOgYMUyyWoJQkSgcnmJhdZOXrq0zWYgTS/sMHxmmsdgguzd7l132LdzKdOuWu4iCwDNfPMSLbZOrC+ucPjaGOprCWW3hbXYgDFFGk6gTaeShBFu9qAvw+WND7MnF2ZOLA/eWSN5CY6HBlfMblBMyw7k40nZlt+5bVDSRt16d5TdPP0UsGeuH9iox5WNtCqYHEpxfbrCxuUXh8BhWdZ2g69zVlbwFv2lj4TJ6aDSyp7cdJvLR3NCVK1c4evQooihiNSzU7WOsN3sYqsxua33KUKh2HEzXx7cDZj7YpL4nxsC+AZYXm7z+3E2++seP9h/v2R4900H70PyKKAKySLdhYTUsJEXC80M2Wj3SutI30IhrMq2ey1bLZmwyRnu9jdNxHhC9v8cIw5D6fJ31d9dpr0czqGoymme6VdjxHR/XdClfKRP4AUbVorHZJZE18ErdKAz9jo6I5fp0HQ9DVRAkAbdjQzHG4KkJvMBGuOO7I4gCylACeSCG0bTJ1C1Cx6dZ6uI6PkIxRrGYoLfk4DQCCtkCiWQC5XCKRlyl/INFhGIMyVAxuzZxK5oZFLYJhShAXJOAW88poWdUSnMNVg81+h2BvUdHwYe5t326pkf7WhnJkAlDCEyXmKFw9MwYn/v6MZK5j18Vrs3UCPyAxbaFNhhnOh+jsdLCma0iugFMpTATGit2wICqodddUvtFkEVMx+fCm6uoW10GJzLUK1065Tbp44PkBuI7ikZhIBI6PYrFLA3ToSaFKNMZrMslejdrCCmN5LDBsQNDCGbU1br67avMvjyL7MiIkoht25hdk8JAgc2mhReExIoJKMYJui6h6eJudnHaNq4sIKkyaDLoErcWIFEExw/xPI9YzMAOQkJRQAhChKDfTEQSRdzQx/d8AsuDgKgtuQ3XD7c/PwEEIZoDrZjYMzXCIEQaMKK37/hY2wWJW9dTGNwuRNwLgiCQHEnidBzW31kn9EL2fHLPA7L39xy/8pvHOXZqlERKY2z8pzsWYFkWIyMjNBoNLEGj5YQMjO9F10Xeeustzpw5QxAEOySb90K32901IF0QItJ1P5J1cCjJhZUY8+UO0wMJ4udGCbouCCDGFLwgYK7UYbqQ2DUaAeDGUp3vffMavVoPfUjiS5/fx+zsLCMjI/i+/xPPsw1oAYYiEqbiKHvSWJfKCKqEuF1UCcMQr9pD0CS6qQBXTqB1qjQ2BAbi+5BlmaWlJXK53K7um7fw/W9cZfn9DYIgJAgmOXPmbpK3ML/F899c5JEn5H7ncjesrzSpLjawOw6ri417Er2tzTav/811bNOlulBndE+GJ56+e67wHxoeEL2/x8hkDJ79reN8/08usXWjvP1TgdxEms//9nFUWWTujVU828MfMHh7sc5C2WKoEJGd+XKH5arJoeEk+0eTbH2wRWZPZkcu28xmk2rX4aG9BXol2Pyby8QVA/1EkemJYS4ulnjx/Rs8e6jAsa8cY/6FeZbf26AThqgpldHxDPHEbdJj92y6G10IYeLJCYZODpFqWdQrZeYrXXRFYuBogdSpIcIwxA9CKl2HZq1LJqbyxePDnBz78Rb/zesVSpZLavA2ybNtm5iuIWoSvbbH/I+WsMQupaBKGECyYPQzqBTj3uGcewfiqEEPR1LRB1OEe3vYV6PP4sNkz287NFfL5M5NImV0tho9EpLH/AdvcOFHdT772c8iyzJhGEaGEdu45R1xT4TRYxzXw7E9YimVZEylE5PptmzC4HYGmZpQyeZjrNZNMnfMQ7p+gGD75EaS24aDt0wR2HU64MFM9C8G3J7L6lurbF3YQpRF0hPpXeMxJFVCTajEi3F812dovUXj1WVMXSZsO/2b/C10bI8gAEnZNu/puqT2ZtFiCoonYlk9jNhOYyRBEpFzBvI2iQoVkYWaSTym4LkOw8PDTExMYFc6OPUeyliKnhuAJCIEAYEfyfycnodu+QiJ3S9Cz/OIpXTaGyZLs7U+0ZNlGcXz+cI/O4M/kmTm0hbtqokkCRTG0xw9PcLodO7HkviFQUhrtUVgKJTKbdIxlZgqoYdgtRx6Iz4tJDzbQ0ZAj2sobQvZDZAMBbnloLQcOgmFCxtNdFVmWJGR1juEeWNH17HVbpFOpkAUKCQ1Wistug2LzEgSzQ5ITWcYO1tgoJinXCmzb9++yMWvbnH1u1exWzapRoqjT9zdoRQEASmx3bEDpLKJJ21n/u1SYfI8DyWh0+10UGQBIYxkZzsPun2OICJ5wq6/JgDkEHB8nLk6hCDf0fkLQ3a8Bs8LadkuG40eiiigSAJxTd6hHLkTakIlPZFm4/wGsi4z/vj4A6n532Ooqsyho8WPfuBPgLW1Nfbu3ctytcv/8N33ERydREHjd5/cT7HY4+WXXyaZTHL69OmPPNbCwgKHDx++6+eDg4OUSqW7pKB3IqUrfOnEMN++uMHMVpukrpA0ZAihWTMxHY/JfJwvHh8mpt69Dbc9n+996xr185vEUxrLN+t8MJrla185ucuzfXyYpokeWhzfU+TyepN9hwuEto+z0MD3o7U4aHQRhlXCQ2nCgTh4Il/6xHGKusf7779PtVrtu6PeD54TgCgicm9/hauXVqivmsxdL+9K9GZnZ1laWuKddz4gd+AohjLAsdP3lvOKkogoCQSujyCAdA+TvX9oeED0/p7j6PEhRsbSXL24Sadtk0rrHDs5TDKlUZ+v015vExtP8sp8lVbPYcAQyW/fzDMxhY7lcW2jRTamYoQh1ZvVHURvdnk9MlcRBeSGR7DVo6f7CDMy6kSagWyScrNKr9dDGBJo5jTev1bCXm8juAHXBmKceGyCwkCc2mYNTdeIT8UZPj1Mbl8kKRxM6fzWuSkurDZYNUWWql3CMJIUSqJIPq5y9vAgB4aSFO9ph35vWKaLLwpod2yaLMsinU7j9Vx6G21mqhb1ooSTdAkFgcxGk+HrFQ48PMLUZ6b63bUPo9usc2Q4yXubLq4fYpwoggD2bB2v0kO81TGodOjFXRIPjRA/PoTt+bQtl88fnyLrJtG0Q1y/fp0PPviA/fv3oyZV2mtR92UopXFlo0V2l9fQsT3y8Whz6WsSWkqiU/NY9+rIPZfxD82mBFKAnusRznfZlCXShRiu49NabzFYTHD04VG8hkXgBsiiwFjW4Mp6C00WUWWJluUiiSJDKR27ZUdOd/eZjXyA/3xwTZeFFxcoXy2THEvuyLm8HyRF4tC5MVaXm7Q+2ERFQC7Goy69CKEk0nU9lG3C6DR6SLpMZixymRVlCb93/24LgOiFBIJAz/FIKVI/F0tPxOjVOijdDO2SCQMxpI5L4PqEkrg9g3Zv5yXXdTEMAz2jU1ps0Kj3yGQNOhtRVp+6N4uS0nhs4gCFQvzvdLMPg5AgCAgE8IMQWYxiGbxSFwToBNB1PeKqjOn4BAIEYRANqAXgrLVBAFcUcfwQyffxkzpCy8Zv2n1S3G61SCYS/fgZr9RFXG6iSCJmRmWymCRoWmxtdllp9sjmcviNHgNJDSNnMHJ4hMW3Fwm3Qubfmyc9mUbS4siSiOt/yNFPEhEIEcJw18y/IABZCHFsm82tLUaTRVRCemHInUKOcPsSEBAQFDE6jh/2u3qKJCII4Hk+KWHbzCoEuXC7m+oHUQcvpkp0bY9qx2GrYWJVe8wtNxDqJmEImiIxktaZyMcoJnVkaeeLVmIKyZEkG+c3SI2l7oqfeIBfPCSTSVqtFqld3K13Q61WI5eL9hzL9R5BFbKlEk07z1LN5BPTefbv38+VK1eYn5+/b0fP8zxEUdw10y2RSHwsWelw2uA3z4xzc6vNBysNWpYLwEBS4+RYkYNDKRLa7ltwyw3oVXvReE5GJN7Q8N2/W6fa8zzW19fZt28fRs6i1nWYb/TYc3oIdTKDV+oSugG2q2McHGGj28JyBM5OZjk0lOxHW+VyOTzP44033iCfzzM9Pb3rjN7Tv3KQP6+WGRoe5sln7z7XrVaLhx/dw1ZpAyNX2/U1r62t8eqrrzIyMsI//aef/8j3mM8biMUKhp5kYv8gp8/eP6LjHwoeEL3/DDCrZiQh9ENERSSWj6Em7q0jzmYNHv/k1F0/r8/XQYBKz6XadSgmdVq+heu6KEpU/UzoMh3bY6nW5eFiksZiA6tpoaf1aBFNxAjrPSCatYkXUrSqDWLp6PU0mm0mB/IcOnSAS5du8qPnbhAUYxSPFXErXerXKty4XmZsfx7DMDj+yeOkRm8H+da6DjOlNhdWOswul8nkcoRhtAnYO5DgwGCCYyNpYvdY8D4OskNx9CCM3ELVyClOkaP3311rw2aX9X1ZqorEUExBEAQ2bY9q4OO9s4aaVJl6+u7z22w0aZQbfOb4FF02uLnVZqqQwHhoEHUiHTkV1nqR1KgQIo6nyU6P4PgBC5Uux0bSpP0GAwMD5PN5pqamcByH2dlZrm9dx73pcjB/kIl8nMVaj3LbJh/Xom5aCC3LJQhCpgcSiKKA3bR55Nf20cJg7to8E8eLPP7FA/3X+yd/8icsLi5itk0+/ZWvceXddZoLDUQR9u3J8slfOURuLIWd1jCyBmbV5NBwilK9RcvycH2HmCpzfDTFUFqnNlNj8Pggsfz9Iy0e4OePwA9YenmJ8tUy2b3Zu7p4lutTatn9+VJFFhlIasRuufeqMoePFnn/RgVruYnYtJHz0fxp4IPo+YhxBQcQDJnC0QESP+51IAko4rZzbe62BEpQRDRHpbpawzdd5LyBK4SobZdAEPA8lyAM2I2ehUGAsN1uNhIa9dUm1XKX6lKT1bka5dDjvf/+DURRQpIlsiMJjp0d59ip4Z9oiF+QBGRVRum46LJIz/GRfRevYWFJPrYnoEoCt5pSMoAoEJou9lYXe6aGT4jT88gArghmzicegLfZQc4ZdLtddN1A3N5U+g0Le66OIAokCjEqHZuFuolbManUm6iHBpFaNQQBMobKZD4GdpvMQAYtreFteqjDKjYmWuhQbrqM5m/Lw0RdihyZHR/X95HE22tvSIjreQxkYji9DgDlbp2UomD5IYEs9D8XL/BRQ5A1CSmrI5VMgp4bdQ6BmCqhSAJmx0NDJHR85OJOKVzbisx56h2bUsfB8fyoe5kxiA0l+hLhnuOzVDNZrHUZSOicnsjcVRjTUhq9ao/VN1eJF+N9s6EH+MVEPp/nxRcu0NiCvYcG7ivtC8OQer3O3r17AUhqMupAgobpEBghKV3GdV0sy+Jzn/scs7OzvP7665w9e5Zrl0tUtro88vgEiURULJubm+sbn/xdkDYUHpnMcWo8Q3d7RjWuSh+ZB5zUZIYO5ph9cRFns0t8PM/0/p886igMQ+bn5/vmM8Wkzq8+NMrfXt5gqWYiiwLp6QyiAM1yyMpWhcFCjk+MZ3j6UBFZEmm1WtRqtR0GNpVKhffffx9BENi3bx/ZO9zIQ2r05Pf4/f/y/9Lfj96JWq3G5OQkgyMiH3zwJnv3jnLo0KH+733fZ3h4mBMnTnwsU55arca3v/1tas0bHDt2jGbzAqr66Ef+3T8EPCB6PyeEQUhjqUH1ZpX6fB3XjKo7twbLb4VUJwZ312vvBrttIxsyHcsjDEASBWKxGD2zh5K+/cWKq5ELHqORxMjreZCGUqnEyekxLpQWaZgO6dEk8ScniDkjlMM2quPStVxOTRUj6Y+QxqzZJEZ05ISKnFTx0zqm6WKcyVFMFskUM0BU/X5rvspbCzXqPYekKpO0PYpuSCDJdBSBmVKb9UaPSsfh04eK6D/hbMXAgQLj+ThXOj2askhod0mnMzTaNv5yE0mTsXIagwmRpHGbAJfbNouOz/D1CsOndtp0b97Y4oW/fB8liJHI1jjz6BgMJrm+2SYbVylkNYxt84owDFm+do3hyWE2WxatnsvR4RQnsgHpWJx8/rZNuKqqHDlyhH179vHG//gG19+7jpgSmU4WWO6IbLaiqndISFyVOTGWZiJn4HQc2r02j//O46TH0nznO1tcvvwWHeskWSNaXA8dOsTc3ByfeOITfOoLJzjzqb2UN9qoqszgZKZvbKAlNYYeGmLxpUVW52a58sYP+d0//CNee/NtDkyNc3BomNZKCy2tUTz+s5HYPMDfDZVrFcpXy2SmMjtIXqvnslg1Wap26dq3TQNCIK7KTOQM9mRjeEstnOUW2aEkW3Urij+oBARxlTAMCSwfrW2jZzSSx4vkf4IOiRhTUIjWgvDOttF210qTFHzXRyRAzBt4soS8ZaJpMk7oETouirpzg2DZdr8zKIgQOD4ffOcmnh/g5wzi+7IIgkcyEcdzAypLTV64UeWt78/y2JcOcPbcOL7j94tssibfV8opCAL5Q3laP1hgPBfj8noTreMROj49LZJEx1SZnuujSSJKzcI1bXpzdYJaD79p48Rk/O2uWugEWBsdNFkiaNk4oYc6mkKJ336f7maH0AmQCwZhCD0n4Eanw6AkEOu5DGViCLKIH4Q0ey6vXl9jfyzOgNBFjal4lkdjqcHYo2McQ+fVmS0azRayJBKPxRF0GTGmkHR8SkFk7S5sfz6266NIAkldpWlBPB4nlUojuj16tkknhBjR+uQHIUkEpLiKlFCRBuP4c/W+PFQQQJUlsH08wScsGAjbnTjPD+nYXr9DuNroEddkkrqGZ3ZRBuM75kANVcJQpX7W6BvzVR6ZzDHwoS52aiJFfbZObbbG4ImPZzf/AH8/Yds+b3xnEavksnhpi6n9+XsWa1ZXV3cYrhwdSXE+3SU8N0Reh4mUtIPo7Nu3j4GBAb73vR9y8QcWvWoUj/Kpz0ZyaNu27xv6/eN2G2VJJG18PGVBr9fjjTfe4PKFH3Lq81/BtQWOnpzg9OHb92JRFLnw/hobqy32Hhpg/4H7k8C5uTmmpqZ2SJqH0jq/fXaCuXKHy2tNNlsWXghmt80zpw5wYqLAaNpAFAUsy2J1dZUjR47sOG6hUKBQKOC6LrOzs1y7do2BgQHS6TQ//OEPUVWVubm5HQQOouzPWxELGxsbaJrGwsJC/3G2bbO0tMS+ffv6EvWPwurqKhsbG2QyGer1OpVKhVdffZUnn3zyI//2lx0PiN7PAb7rs/r6KhvnNwCIDcRIbcugwiCkV+ux+sYq5ctlxp8Yp/gx9euCINwesLoVTqxptFqtHY+LZuQFhHBbZiMKrK+vMzIyQiymc3Isw+tzlcjApRhDFASMlsIrF2b41OmDHByMBm5jMRUjrqOKGo1GnUQyidNzUTQJs9Ng795oEQiCkB/dKPHqbIW0IjFp+rjXagjLNbpiG1lTiKV1slMZegWFtxaq9FyfLx0f/onIXmIkQXzEhueX8KwxGoJHz2qj1XpkLZ/WniTpYhLfNnf8XS6uUvItylsduqVun+h1Sh3+/N++Rq0rEsZDhLUGm2tNPvdfnGY8G2325ktdJElAlUQajTo9Mc5CpctAUuPRqRwTMZ/Ate9ZiVLjKgc+dQD1hypqWqXWq5G3y9gNk70Hj5AwNIpJjaSh4PZcaos1iseKpEai66bdbtNr9/jGf/wG//h3/zFbjS2Gh4f52te+1o+OSAzESdzDRKV4ssg3v/FNVt5aYUAoYJg+1uoG5+fXyFoxUiMppj41RXL43sPWD/CfB1bTYu3tqBN9pyvhVsvmvaUaddMhqSsMpoz+rGUYRt2TKxstFi9sMdRycGIyFVlAyhsYgojfc/DiChbQM3yEtE5GV4m1XYKui3gHGVEUZYdyYDdIOR1ZE1B9tjtHO7/boiKDEOK6PrG4gZmEhO2j6yqiLeL7DqbqEEtsX8Pb6xdA6Po4NQtrqYk7EGPgqXESo9F132l30OIqGhDP6PiuT+1Glef/X6+zvD/Pvv2FaK5VEtDTOgNHBsjsydxTVZGdzLKR2WDUD1lRQir1HpLlEOoGXuBgOj5KEJJuW/iVHuQ15KSKZ3pISR+bKFsTSUJUQ7wgINQU/JZFsNBE7oG3J/ocAtPFr1mIiei8NnouPddDCAEpRPNlwp6HkFSRRAE1cBjLp1hrO4Sej9ay0TM6vUqPXq3HaMZgPJ9kvWEyGNfomtH8tJBT0Gs9FFnA9UNUScD1AxzXYzgTQxQhm8vhdW3ctk3o+GiqSLduYuWie4QiCugByINxEIWoM7vaIrR9BE2i1XNJOgHpXIxKvUVXEuh2HSC6dyU0CUEQaJoO6e2cv8DyEDR5h7zzTsiSwFDaoNSyeG+pzif25nfM7omSiJJQKF8pUzhceGDM8gsMSRJQYjKu7GO6Jn/913/Fnj0jxONxHnnkkT5pcZzomlLV29/f1ZVl1s6/xBe/+EVOnTrFK6+80jdCu4V0Os0zz3yKC6//DWIiZGAoKrKvrKwwMTFx39dWLBaZn5//2ETv4yIMQ/7jf/yPrK6ucvjAFA8dTtxFrgDWVjr84E9m6NUsroyn+S/+q8fJ53e/1y8vLzMyMkIQCHhegHyHjFtXJI6OpDkynML2AuqNJguJOo+dvP3+Pc9jZmbmvoHoiqL05xlLpRJXrlxBEAQmJibodrt3Pb7ZbPadNr/+9a9Tr9f757zdblMulzlw4MBdf3c/HDt2DE3TWFlZwXEcRkZGHmTzbeMB0fsZIwxCVl9fZfXtVZKj0RxNw3RZ2urg+QG6IjKUMsgXtkOqX1xAlEQKhz66TR/Lx6jN1cgNJZBFEce7NYuxs/rRsVwODafwTAclriDqIk7N6WfPPH2wiCaLvLdYZXbLjkqxrsWZ6SLnhpS+re6eqQxjR4vMvb2KkdFZ3ygRuiFnv3yQdPZ2J/L8Sp3X5ioMaDLy1QrmQgNBkYgPZWn1OqQTBkHLpvvWGspIgrGHBrmw0iCuSnz+2MfLzbmFq1ev8vzzz3PtyjWe+fQzxCwD11IQBZHMnhy1TJxLhoAsC/j2zr+VhEhyFXJHeCnwxg/ep90WSEymSRoKluuzMVdn62aVp758kEcmc8yVOyxUumxWG2QHkkiByyP7hphI6ISiQ6XW2CFx2A1DJ4bwbZ/VN1dJiSlaSouLr/8NBS2EdJpEcYiwFnXz5FGZE186gSAKOF2HTDfDcfE444zz4n/zIsMHh5k6N8XBgwc/1nmTFZmn/vFT/Lv1f0eePKZt4jouPb/HgrTAb3/1tx/M5v09RWOhgVk1yR+83SmudhzeWahiuj6jd8R03IIgRA6uiSCkvNbiBoArk1YlBEVCTOsELRF5MI6X0Vit9/DCkJosIrcsxM0O2t7bshxN0+h0O/cleg4+2mCSxHKLTs9DFkVkSSD0I/MgxZBBitwcbc9DdEPihRjG4QKhH+JtdRG7Dp16DVVV8TwXVVHxOiaIAvVKB1eVyH5irE/yIKp2B76PKEkETRt3tYXRsDGrPW4214mnDPbszRL4Ad1yl8ZzDYycQeFQgeHTw3eZMxk5A0bhwp++Q6fbZO+ekyyuNXD8kDCENGDULMS2gzwcxx/QEBSJ0PVBFCOTgw8NwoWEhLJAbChFaHvY1yrIE6mom297SGkNP4g6tIokYnkuSixOaHai4wJm10TTNBRVIY1INWaTbvQYSEdZc+21NkMDcc7syfJOGLLe6JEyNBKagouEvdYm1rSpazKOJyAKAhlDJh1TI4fMrS5+y6FdqhF4Ppbsk3NStHs+rYLBYCiiDsYjyS8gxhXkvEFvpUUvoaBYPkNxidrGKnENpkansdyA5eVlxoaLGLEYV9dbJHQlui6CkKBlI48k7ulqfOtaLqZ01hsm1zdanJvO7/h9fCBOc7lJa7W1Yx79AX6xoKoyn/3aPiobLq+//T1WVixmZ69x8OBBzp4923/cysrKDpmlZVl85zvfASI5n+M4FItFyuVyf4bvFjRN4f/4f/01btyco1abxXFyNBqNjyR6giB8rC7TjwtBEHj22Wd58cUX0TTtrtd7C72uj9WyMXIxrKZNu2XvSvS2trZIJpN0uyF/+v9+GSOl8/t//OgOsnfreRUReu0GQ8XbuYFBEHD16lWOHj36sQ2OisUixWKR0dFRHMeh1WoxNzfH5OQkkiT1ndlvIZlM9sPrHceh1+v9RLLZpaUlpqen+3ughYWFj/wc/6HgAdH7GaOx2OgPiAu6zHuLdZZqXSzXR9xeLBK6wpHhFNODcTobHVZeX+mTwjAIMSsmgR8QK8R2VCize7NsnN8gIQkMp3WWa10G4hphx8P1O4RBSNNy0TWZiy//gCvlHgOnBjj/5+f5gz/4g/5xVFnkqf0F3vv+X/LJJz5DLp/HbtV45Og+FhcX6XQ6JBIJRFHk13/3JC8VY8xd2CSRizF5NIMSKzEyElV7LNfn7cU6hiSiXKtizzVQRhIIt+RlVhiF9g7ECHMB3mobGRg8NcjltRanxjMMpj/+LM3Y2BilUolsPsvpz52GEIZzw/2F+OKfXmFhuU7H9vnwlrTreOghxA2lL52anZ1loFAEan0dvSQKBAI4drTBMlSJY6NpDgwYLC+b7Nu3j8tvXKb8/XmubjRREx5f+mdPf+RrF0SB0bOjGDmD6y9f540/ewO1qZLyUgypQ1TmKnSFLgOHBzjwiQP8r//+f+XR048iL8kkq0nCmMSNyibDAwNY6xYz35lh8ulJBo4MfORzQ1Q9/K//b/81vu+jqRreYY9SpcQXvvCFe5K8MIyuR6fjoMQU4sX4A4e7nyN816d0pYSW0vrnPQxCrqw36dgew+n75zx6NQsDWA8DVMdjIK7jSiKhH4Aq4jcs1IyGKkt4vo/l+fQUGbVsEowkEY3tW8a97FpvIQTXdfCHU0yIMm7NooGNosroThDJTeMici6GvdVF8EKKikJ6KosymAQRlKEEQddFtzx6jTaeJZLMZxE0mU7bprtUR57KkB3b2XWOGQYds4veE3Hm6gSOj5RUSWVz1FdbLC7W2Hs8ytDS03qkqqj3WHlthV61x+SnJndc/77vM9uepRQrMZmdJNE0mY7pdOMqq02LRM0kDEKU8RTKYJzAc/rn4BZufSS39oae66JrShRCnlEILA93sUlge4jbckTT9aLinRRGRjmC0D9Sr9dDkqS+tDWhyTQSKj07iKJbkirdchff8UnoMuem88xstVmumWw2eyCAMJJC6tRQLBdbkUhrAoOZFPVKlVhHImw7oMugqbQTDtpgGoU4+mITw+kgF+LUkgq64yG6Pl4Q0CZArJnI8zbGUJwVs43X7DKyt9ifqVufbVNa7RIf2ksYhpGkNQjxqz2kvIH6MWTCggCZmMp60+qHVd+CpEqEQUi33H1A9H7BcfjwHua1eYK3WohipLg5cOAAN2/eZP/+/TQaDTKZnTl0uq7zh3/4h7z99tukUikWFhbYv38/nU6HlZWVuzL1JFnkyJH9tNtDPPfccx+bZMiy/JGqhh8XrVYL3/f5/d//fc6fP39PZ899B3O8VPRQQol958YYH8/c9ZhGo0EYhmSzWW5cX6debuIHIY7rIct3F1IWFxeZmppicXGx/7OrV69y6NChXU1pPgqyLPe7cqVSiXfffRdZllFVlePHj/PCCy/w+OOPYxgG2WyWd955hz179jA29uMbqHieF8Vm/RQ/i18mPCB6P2NUb1YBUOIK7y81uLHVIhvXyG8P/YYhNE2X95brSKLAnqEE1ZtVGosNikeLvPfdGd56cR7PCzh+ZpSn/tFR5O0OW2IoQWYyQ3WmysnhFM5Wh4331ulutan6jeh5FYmhtMF+bZIbrYu8f/N9nj397F0ZLM899xx2u07CaxJ3RE4c3YcgCExNTXH16lUOHjyIJEmkUjpf+dpxgq9GMoggCLh58yazs7OMj4+z1PIptSzGfLCWmshD8dskD/p/I4piZMc+lsSZraOEIe2aycsXShwcSZOeSEczi0OJ+87QVCoVfud3foe5uTlc12Xv3r07HKCKB/MMLze47ng4nk9q22Wu5/jUuw5TvsDQdJbkSJKVlRXy+Tx5RSCTmmdzvYWeM7BbNgVDYeLgzi7rLe15GIbMvb7B1ZtdSoHDuKyz/P46+z91t8HLhyEIAvn9eW6+cpPlxDL+oE8n32H/s/s5nj5OajzFe+ff4+KVi7zxxhvM/miWJ/c8iThZ5I2bHUJFY7lkMpYxOCjAyhtRkUBP39+91PM8tra2dsgxhkeGee211+4pR3F7LsuvLFO+WcHsuBgxmfzeHHue2vOxHR8f4O8Gq2Fh1S3ig7ert1XTodS2ycXV++d0+CH+VhdLFBGDkCAESwiR5SjjTFSlKOvJDUjpMqW2hywKtAmJmy5+w0I0Pt4McafTQdVjSHgcfmwCZ77O6kKdWtui2XYQinGsqome0pAWWySTGvp4DHUizS23D0EWkdIaUlrDS0VB6Zbvk0zGaK40wA3I7MtGEQF3QhQI6hb2qgXQ7zgBxLIG9Y02Wxsdhkej61wQBWL5GFpKo3K9QhiG7H12b7+zJ0kSZ8+epVAoEJfi2DdtKi9XkGWRrY5LGFPQJ1KIhhxtON3tJ5NFsD3YnmcThBDXD1DEEF1QEFSxHz4u6nJEdlbbCLIEaQ3PCwi2HUhFQUT0QwQRHN+DUEIz7vjOCZEc3FclvFIPRVCiWUsvItUxVeLkeIYDg0m+8+JrHDhynGAoiZ82CJZatDyfVbPLerWNuV4n1YbkUJ5WtYspS9iFHF0fgpjI5L4sia5L6liRbkyhXbXoNC26qy2Elh3ZmqdUem0HH42gFae06TM+4RAKLo7rYPegvFUlm04RWB5By0bKx9D2ZRE/pjFXXJWpd03W670dRA+IZtfXOx/rOA/w84Vte2gf8zOOxWI0Gg2SySSDg4OcOnWKEydO0Ol0uHDhArZtc+7cubv+rlar8dhjj1EulzEMA1EUSaVSdDod6vX6DtOQW0gmk0xOTuI4DpcvX76vVLHWdaiGcWYuzPHosb0k9b87wXBdl7W1NQ4fPkyn09kx2//h9/bNb/0VnrLFH/+f/s8Yu8SOmKZJq9UiDEO++c1v8vrrr/PZX/kNjh8/TMy4m+RtbGwwODjY3xfW6z3+t3/7I06d24t67O8eOH6ry7e8vEypVOL555/n+eefZ2FhgT/4gz/g+ecvcvGtLdJJm6Nnujzx1OSPVUBeXl5+INO8Dx4QvZ8hzIpJfaFObCBGw4ycL7Px2853sF2ZjCtU2gEzpTbj2ciBs3KtgpbWeP35WWpCiKjLvPPKIlPHikw9FMkbBVFg/PFxuuUuC8/P4VdNsF2CpBZtkgSoV0potkS3HqBlJhhAI6/meeWVV3jkkUfQdR3TNJEkCVVVuXnzJl/5yld2fMluVdDuzJS5tSAsLy8zPT2NqqosLy/z2s0mkigRrrTBD+/K54onEnS73ShoMwBvo4O92MBZbqHvz7FSNdmTNui8vcbmhU0GDg8w8cTEjlmkW6jValQqFc6ePcvhw4eZm5nDN31c341c5WIKg8cH2b/cxLy0zoxtsy6bIAgowJ4ADo6lGTkzQrVWRVEUms0m58+fZ7H2HscnP02jZjGS1jnz5B6GD98meouLi+zZsycKT/UDzLZD1TIpTBaxSybdpn3X670fvvyVL9NsNRFFkbGDYwyfij7jVqvFvn37qFarjBZHsd61+P76j8i4T6AnUuTjGo7vM1fpkB7PMFDt0Vxqop+4P9H7qBvZblh9Y5XZt1aZC3zaAsRsh33v2SDA/i/sf9DZ+znAalhRZtsd34e1monj+2jK/cl26AYEro8ngRhE871dNyATUwiadhScHUYGJ7GkimxuG2cEAX4IeDsjFQSBKEbgQ4UY3/MRRIGW7ROXAtIpCeXhQfKTGV5+7jUynkpSj1MsZIhNqlzRFJptB21Pmm7PxGn4OKaLHFPIDMaRFAnP80gmk4RBQGWrSvt6FalgkNllhjS0fPylNiIycvaO70EIoSrSbju8c2GDY2okm49p0XosKRLZfVmqN6sYWYM9T0Ubh83NTQqFAoPJQZYuLNEIGsiGTHe5ie6HWBmFmCLevv6F6LT0JLA7Dj1FxPZ8ZEHADwJycRXP8TAGUzuKWKImgQh+1UTO6fhhiO/5oCsoAighOBL4ckgidrcDqiCAUIiRTehUrlaQNGmHJB1gbWkBr7FOXtzL6Ngo4XiG1kiL+bcXSJJgpV7D6wb0POhu1AgkFYoJEjEF3Qc6LtnpHEOFONOfnSY9nmbhepmX/voaalwlcbBAciIVRUssNWhe3iBsybiVgMvvrrH/eJGTx0+yWumwUXVQ6g6hIaGMJlH2pD82ybt1nmOazGLN5MBQEvGOc6nGVHq1XvRd+Ts4Oj/AR2NlqcGNKyUyeZ3Tj4zdN8j7xe/e5IOXFvnkrx/h4bNj2LaN7/v9MZLdkM1m+Rf/4l9QLpf73bZEIkGhUMBxHC5cuMD+/ft3HMPzPIIgwDTNHXPyIyMjzMzMEI/Hd8z0QUSO4vE4+/fvZ2VlhZdffplz586haTvX1bVGj7/6m2tUL27RtXssfsrja587sGs80o+DO/dYy8vLu2b4QVSUX1paYnJycleSd2eMwrVr1/jggw8YHBzkU0+f3jUGodPpRMqyxO0i3oUPbtJreGwu/2TFkiAIdt0PBEHAmTNn+O53v4skSbz77rtcvLTGkPAwftejIlf50VwNVZU494mPJ7t0HGfXKAxN07Bt+67P7x8iHqyAP0PYLRvXdEmNpVjaamO7fr+T92GkYwq1jkPNdEilNay6hdVysG0fJa+jqRLdjSbf/c73Kc4l6HQ6/N7v/R5KTMGTBJZKHRqWi6HKZOJqNGZnWpi1LmYIsZPTtPUcmVYRtZRj/HCRF198kcnJSQ4fPsy5c+d49NFHKZfLd3V0ZFlmdHSU96/OYmkZFEnk6EiKmBpJF24tmBMTE3SuXsZqNXA3TcTU3QufLMt4buQG6Ky1cBYaSAmVENCzGpYkIOV00iNJnI7DxvkNAi9g6jNTO2Srtm1z6dIlnnzySbr1Hi+/cImNm10atRkCP0QzZPYeKXLs7CjKAYlL/+kF9g4fYSiTjRY1Q2FkMsvYo2NIeYlmrcnY2Bj//t//e+bm5jj75Yf48mcfw27aqAk1mtPZRr1eR9O0/o1FEAVIdsj6LuqWydhggrHDHz1jeSfy+TypVIrPfvazO6QltyyIn376ac4cPsOl/3SJkuhyvS1SSGgIAuiihKFIbLZshlSF3nZcxr3gOA71ep2HHnrort/d2XG9E1bDojZTYz7w2XI9cnGNWs9hVoLsbI1uqftjOcY+wE+GW269d6Jueugf7mrtglszJUEY3YAlScD2AqS0RtCwb5MCN0SVRJK6QrXjRNlxAoT+TtKg6wZmu4uu6wiy2A//7ppdJC1GYHnIvQrf/OtXicfjyLJMV+lS3F/k2f/9JyiMFlDiCvu2Onzjf3qX6nwHs97Gr7qEYbRRaA4mGDpeQN2OSwkRMNccwjAgczgKcP8wvHoPyQY/K/RvcK4fUOk4dB0Pz/PprbfozuvENYUDgwkODiYRRAFJkYgPxKnerDL00BBWaOFbPuUrZTaulrlxcx0rEafdsnAXGpBQ8DY6NBs28WIcuWDgeiHVXg/b8VH8AE2T8AQBLwhQJBHb9PAF8GSBzJ0vXBQQYjJBy8WrW/haiCxLOF5ANq4i9FxcDQrZ2+tzGIYEHYeg7eA1LeShJOlDAzgdh9rNGr1Gr28yY9s2s7OzEEZKiNGxUdyOi2u7qHEFoesybKkYto5lOvQ0gSAro4siaiCgJBRaxRgcyJNOang9D204yZt/eomuJDD0xATiHfl2wmQS3bQJ5Q6qH2BudZhzfA4cLWK3HERVQp3MIucNxJ8wCkGTo9l0zw9R7yB6kiZht2x8239A9H6GaDR6/NX//B7VxQZKXMF1Az7xxOQ9H1+rdGlXu3z3uRd54YcbSJLEM888s+u96BYMwyCZTOK6br8b57ouQRAwPT3dVxVBVJS+de9aWFjY1dBj3759XL9+nfTwHiw3YDRroMmRK+ctcjU+Pk4+n+eNN97g0KFDOySUr763RvXNVQqGitZ1WXlpkfemsjxzavQnOIMRZmZmmJ6eRhRFLMtC07R7Fk5/9Vd/lXQ6TaPRuOt3H45RSCQS/Pqv/zqzs7O7krwgCNjc3NyRKbi+vs7yyjyJUfjCbzz7E72fRqNxV9d0fX29T7pPnz7N0aNH2djYYGtN591vzjK67fC9cbXMwkz1YxO9lZUVpqbuVk+lUimazSbF4r3NDT0v4EffnyWd1XnkYz7fLyIerIA/QwR+0HeJ8/zwLoOEO6FIIn4Q4PkBoioTBAGJQoxDxwe59P46puux99Ag2liFK1euMDw8zMbGBuYNi/PnN2geLTAgiVC3CNpOVFJOKDijCr24TyrmUown6CZifHBti3hK5dmvP8vM3AzPPfccQ0ND6PEkoxOTdGyPmCLtqJB6ks4PZju0F7ZAl1h8eJRPjMgMDt62sA6CEFXTyacFWvUNYok499p++m0bd6WFqMu07S6iK1OtdGkIoEkih0ZSTObiZCYzlK6USI2n+m6kYRjy1ltvcfbsWeYubvH8X1xhZa6Enkugp/Wom2B5vPnDeV76zgVMcZNwwmXfM+OcPHCUMAzRUhpqQqVdaTP/xjxHHj6CLMucOXMGXdcZHx9HT+t3SSA9z7vLEerixYtsiRs46Tq/9/v/jGQxTnpPZA7hdB2ctoMoixg5454y1Eajged5DAzcnq+704JY13U2uht4vsdgocBMt47nByjb0i/PD9GUqIIv3iMg2vcCblwvcenCVU6e3t0oJplM0m63+86dt+D2XHpdmy4hKV3BUCVCVHquj9l1o8iOB/iZ48MdGojyzD5ON1WQBQRRRMKH7cCDIAwR4ypCTCbs3SKR0XPkYhqm7dO2HCzHw2nVEbYsioOD+E0ba7NNY7WBIsnIukxiLE2QkggEmW7P5dhImqMjY7zid2k2m2SzWfySTy/dY8+p2zKbqekcv/IHp/kP//eXcWdaiHkDLRcjDMHaaFNOiowdGqK21sKs91ASCsm9eTIjKVrNFqlk8nZXcdvERTIU3O1z5fkhpZaN6XjoioykSuiKxHAmRqvn8sFqA0I4tO1oq2d1qjerlGfLWIaFd8PjyrtrzDo2zZhKXJMQVIlQkwhC8OMKvZ6Ls9TAMB3KuoAvSMRSGlLbieIchBBRFEgoElLPx86oVCwPQXZJb5McQRAQdYWw4+LUTGJjCVp4hIAhi9i9LvFDt80Zgp6LOVenvdWl23GwvQBltY242iab0hh8aBBREKnerJIYTqAlNT777GfZ3NhECiS2Lm7RXm9T36qTyqVQByMX13a5TTodR1QkLC9EB5Scjro3S8PziaU0Qi9aZy5f2KC62GDwQG4HyQOwLQstoeNmPdS0jlhMYNZNlOks+ogCgYx6D6fAjwtREAjCAC8M2VFWFKLvym7flwf46aFWNWmVu2T3pKkvNahu3r8D9LlfOcKNhbf5zd/6LK+//gorKyvEYjEWFxejPciHIg0s12dpw+WDSxfRdR1RNXn6yRRrd2zuRVHk0KFDmKbJpUuXoogpy6YwOIzp+OiKhHTHfVcQBDpanj/7xgUER+TI6SG+dCKa67+zKxSLxfjkJz/JBx98wNbWFidOnIiKTw0T1Q1RJuPE4yLmaotmwyIMw36n7cfB+vo62Wy2HxuzuLh4XyO3lZUVvvCFL7C1tXXX7+6MUdjc3ERRFE6cOMGJEyd2Pdbi4uKO17u2tsb3v/99FEXh3LlzJH/CkYw7XTUh2rO5rtvvriWTSZrNJo888ggvt+dABM/2EEQR3wswPmbhx7IsFEXZtYscj8cpl8v3/fv1tSYXX1lA1mUePnf/bvQvMh4QvZ8hJEUiFKKbjaaIhNG4xa6Ez3Z9FDnagPiujyiLqEmVz/3vHmLviSH+5E/+lE//5uOM7P0kL7yQZnBwkISS4OUffcCCbZJNxVBSMcTCbfmC2LMYbyh4vk88kSCZSJIUBNbdgLlrZQ6stjl48BBSepD/4c+fo6dmGZtw0FWVwbTOidE0+4oJdEVivdGjs2ExMNfA1SUWBhJMqhJjI7dlEaIooEgiPVkiPZCjU23idtghCQDQDR1zpYXg+DhqwOrqFoKcxBmOI2oSbcfjncUapu1zfCyNrMtUrlUoHCogSiLvv/8+R44cYe1mk2//bx9QbnYYPFIkFtt5kwiHklSrMarvNlHdYWIDMYYeGsLtuay+tUrleoXV+VUGBwe5Mn8Fy7A4+vmjHP36UYJgp0ztFmZnZ3eQvJmZGa5fv87C4gIHjhxg/GxU1fMsj+uvLXH17VVq1R66JrPnYJ5TT0+TGU/fddxSqUQymdxxo7tzsVxcXCReiDN2ZIzmapuhtM5KzSSuyThegCwKTCQ1aDkkR+6Ws/lewF/+hw+49toSjWqDtSsewu9qHDyys9qlxpLMrVcYEzXShoK23SlSEypGSiPTtli2XEIiu/4hRSGe0u9pTf8AP12IuwTtqrJE8DGItqBISHkdfdlGkgQ8H3Q1iluRcwbuajsyZdneFEkixBQJ2YOtchOrZTGQCMmVTKpXSrRaNrYCiCB2HLStLqRl4kcGOTaZ4/BwJE08cfIEju0g2RJXO1f53D//3F2vbd/+AuMPDdEpdSPZX70HAYRth/ZMg5KokhiMc/bLhxgYjPP9/3gRSZAxkjqtdhvDMFBUBb9l47dspIwG27brXdvDdDxkIcSxLUQ/RFQkBCFSUtCDG6UO4/kY8e1cPSWucO2la+w5sIfL76xyXQix8Jgo5BA8n54TEExm8Go9dC+kqYl0LAd7o40Vl5EHE5iej2TIaKZLOi7j+yFhxyXM6ciFOEEQUDVtNEXsx8pIaQ2vYeGbDprLdn4pBNU2iaE0fjraKAWWR+tymep6B1MXcGMyuiLixhTmq11is1Ue+8cn2P/l/ZQulWgsNmivtel5PZRQob3Sxm7ZyDEZNalCAG7XxZIcJp6aJOh55LMG1zdbWJaPX+7R7HnEJjMMpXT8jQ7JsSQ//N4soirdFWNgWzaarhOK23EOkoCW0eg2LWbnqnSVgEY3QKhYxHIGydztjL0fB34YIgoC8odvqmGktLjffPcD/N0xPJJi5GCB1aslkkMJXn37Od6++GcMDQ1x8OBBPvOZz+x4vBGTEKQqr776El//+td5//33OXDgAEEQUCqV6PV6CIJAoVCgYYZ8688uU75Wwjc9BARcOeDa+Tqf/5UDdxW3YrEYkweO8I0X3+TSapPiOKhqlaQmc2Isw4HBBJlYdJ+6udlFvNok7vjc1CRG5C6nDt1twiIIAqdOnWJ9fZ2XXnqJc+fOMb4ny2pqlfZsnRAw4wLQ4pVXXuHdd9/lq1/9KmNjE8zerCCI0dom3SMgvdls4rouIyMjQFRI3k2GeAu1Wo1cLockSfi+v+N3t2IUFEXpu43ez3WyVCqRz+f7nb56vY5t23z605/mxo0b/diKnwRhGO74fNbW1vrvsdlsUq/X+2T2oUfGuH5hk82rZcIgpLA3xyOPfbzu2uzsIkeP7u40/nGKn6mMyEzlLX7tq1/8pSV58IDo/UwRK8TQ0zq9eo/hlEFcU2j2XDK7VCtqXYehpMrijStoPY3sdJZau8bQ0BDpSQmlaPHyOy/xm1O/yTPPPAPA1qUtWi0wCikyKZ2uafa10XEjhmboDBnD1KpVbMvuSzLjMY31Uofli5uUehav3yzRs/OMdBVqlz4glknSnShyc6nB9FiKLx4fJqZKqBmdbkrFVkUSWkAxl6PRtthYa+M4HrIskZYE1iyP7J4UetlEUDUa9TrpdBph+4ukKxqV9VViRox6vUrgyHRUFxyTkUyOoZRBx3KZK3eYKsSJF+O0N9qYZZOt7hapVIqknuIvv/EqDdMmMxq7i+RBRKiTSYXhI6NIjYDqWuRauPTSElsXt9iwG2j7R2mIEn6tjlGC0lslUp9P7Sr3WV1dZWRkpL8gLC0tkU6n+c3f/E0cx+mTtMALePfbN3j1hTnaqoCS1vEtl5VXl9labvHFf3Kqn4d3C+VyeUc3704L4ps3bzI0NEQqlUI6JtFabnE0aZCJqWw2e+QTKpNpA71ukZnO9TMa78TVK1vceH0FV7aZemic+lKLH33nRp/oldoWl9danF/sUmt3SS05ZAyVE+MZjo2kSCY1Bg4NMLnVpVQqYyZiDBsxpkWRwoFcPzz+AX62UJPRRiUMwv4mdiCusrrcwG06BL4PQWRkImoSUkbfYYYkF2Io6x0SQsim5ZDZdpsV0xpi18FdboEcHbfr+OiqyEEjRXcwwQdelZAY8++uYFo+JBV0TYYwkoC2LJtCw2WvE3J0NNW/0WYyGTzb49rr1/jUP/4UqeHICW94eHhHYWNgJMXSQJzi/iztSg+n61KbqTB+downvnyQoyeGSaY0ul2H1/92hnbVJD+eJpVO0euakQOeHUAQIkjRZsnzfFq2hyQK2D0T13HQXRXjjtm9lK6w3jDZbFrsLUZFqY7bQetpLL63zrwQIkgwkNARRQh6HoHlIeUMREOmNV/Gr7WQdQNPlMm5QWQ2ocqICRVcD6XtY6sCjaSMn9IwtsPEu7ZHx/b6RE+MK/hSiOALdJo2UxNpWpUmrqAgjafw5agAZa+2qK636SZkRFEgJooMpnQMVcIJBdquz9XrZQ5+Zpp9n9+HWTFpLje58v0rVK9VCbwgIl6dLmo6SUUIWXVs9MGByEBmo0M+DBnPxqh1HWzdJ256jDRsvPk66Yk0cj5GY6NNLHf32us4NslUCle1InUJYJsuvapJd62FMBDDdn1qokBDkVDyBsUDBZID957V2g22G7mKKh8iiZ7tIWkSsvFgi/OzhGEo/NY/PcPifI1cPoainuHf/Jt/w5UrV3jqqafuevzNmzdRVRXbtpmfn+eJJ54Aoq7cnfLIxdUN/tP/coHm9RqDExliU1En22lYzL2yyMt6jKk9Qztyd2e22nz/6hbnrzWZ1nJYry1gBSG90TzfXmowMJLk2SNDHBxKMpAxuDFg0GjbDAzEEQO731HbDSMjI+RyOd566y32TUzR+coh5i9tIcgCR0clvvLkSf7yz/+UTqfD//Q//s9MDz/D2uUGiAIPfXqar3zt7nl413VZX1/fMYu3sLBw347g5ubmrrl6t2IUYrEYnU6HWq22Q475YViW1Y+dAOh2u2xtbXHo0CHW19f5wz/8w13/rtOxCQNI/hjRS0EQEAQBiqJQLpdxXXfHe0yndH7nj85y7WqJwA/Yd6BAPvfR68APX7jBq9+6zuxjNr/2Wyc/9uu5hUajwV/8xV+wVf2AzdIe4MyPfYxfFDxYBX+GUBMqhQMFVt9aJZ+PcXg4xfmVOtVO0A+ItV2fWtchrsnsySj8xTdfwKt4KMcUTjon+eM//mNmZ2f78QZbW1t9nfPK5RI1LyAVUxFE8XbnLAjp9kx80+/Lszz/dsU/ZchsaRIvv75EtWniXFzgkB9DDB2yyQHalSbtmSukpwaZ6wzxN0HIr58a5dNnxnivEKegiiRadT54u87Ndy9j1nqEXrTx9DWRUlpF25MhZciIpk8mnaHRamIYRtS6D0IIQJQErK4NkoSQ06i3mgzEBAQhRUJX2Gj0aPbcKPTY8Slvlqm5NR555BEuvblCabVJYiQWbTbvgVqtxtjYIJteg5VrLSqzFcrXy5SELouhTnupgW3ZJDSJp/YPU5ut0VhsUPiQw2a73Y7O3TZZ3tzcRJIkisUiq6urPPzww/0FuLHc5P3XlummVUYGEv0ObifrMbtQ58rrK3zia0d3HL9cLrN3794dz5fNZrl69SrT09P9DXFuf46JpyZYf2edkY7LRFwn9EOCpk12OsfUp6d2DQnumS6O5aDkBfSYjpZyMBuRK+FStct3Lm2w2bLIxVX2jhQIQ6ibDs9d2uDGZouvnBhh9OwoV69epfPyBfBFpj79JPsfm2bs3NgDI5afE/S0jhJTcE0XSZXolrrI83XU2TrtIIpKuTP6QIwpSMUo60xKqkgpDTGnoy030cUoMsTSfDRFQsoYeBULr+3QUUQEBCZiKmlZ4vCZEU7Fp1m52eQdvwEpDVUWcV0fQYC4LKCrPqbjYpe6OG2nH1Hguz7VmSrqqMr0uWlEWSSdTrO+vo7neYyPjyMIAicfGeXG22uUZ+voGYPA9ckeyvLrf/gwe/ff/j7G4yqHzo7y5jeuEY5GXUMjHsN1XbrNSv+mpqgKXbOH7weIQrSxEj0BB5fEHYRCEKLqr+lEFfJ6vU4ynaS6UKUshHTjEsnQw4hH3/3A8iIyLQn4hkQ3FeB4Amog4lsBshUQq1pISQ3LshBSKl27SdfqkRgfpifImE503gQx6oxnDAVBEGl1LfyEhtzqkAWKocNgNkk5q1MRwe66uF6PzlKTpgCyIKBKMoWkGsmp/RDRdElMpCl7AfPnN8jvzxMfiOM7Pggw9okxYvkY1UaHpTWVqh/StWwKxUI0j6nJ9EYSLM/XUUSBbD7O9GASHWgtNWkEcPBXDuIJIe1Sl8DxaK22kBQJI6OjpkR0Pdo0C7oMCFgdh9pcHb9lI8RVilMZ3IaFKAiIfoBd6rLedhg6NUT6Y876hiFYPZdDSYNuKeocioqIElNwOg7ZyeyDwPSfA+JxlaPHb5O0Z555hkqlgqZpvPbaaxw9erQ/grB//37+5b/8l6yurt6/21QOsddtJg4M46tB//7rBi4D4yk2rmxxY67KyUMRUVmodPmbC+tUL66yd94kKQsIqkLg+7TeXiSVidE+PMzfeD6yNMYT+wrovyZhOh5ZuhwY3T3G4E7ous5TTz3FpUuXmEq4PP37p5AlkW69jGv3OHToEEePHsVzZZ77/86gJTUCP+D626t85ksH7nK7/LDBXRAEeJ6Hqqo4XsC7F9Ypb3ZIZQ0eOT1Cp1HdNW7hzhiFXs9iZWXlnkYuEHXbVldX+0TQcRwWFhY4duwYnuf1O3wfnul75YfzvPW3NwmDkJOfmuLZLx/a9fi+7+/oSK6urjI6Osra2hqapvU7e3ciEVd55JEfL1bh+oUlepsWq9vO9j8uZmdnmZmZodfrsby8zMWLF+8pcf1FxwOi9zNGbn+OzUubVGerjGYNwlychVaPasfGD0MUSWQka3B4KEUhofLsuWf5wVs/YKGxwBH3CFtbWzz11FOcPXuWTqfT7/qEYUi3aeFJAskPz2SJAvF4NPvQM03CEMxOl2qlQjKVQlVVTELMcofUVQ+14qIciPUdMrPFOClngPL1Nfx6k5sEXBxI8NSBAR6eyPLC92/w+reXkC2BWEanOJ5BlKPA207NpHqjyszNGiODcYo9ETWEdCaNaUah3HHdQJREXNNhLDXA6qCEP2CwT5c5OBxtprwgQNqWgoZBiOd5zMzO8MxXnyEMQ66+vYonCYCHpu8+59EzTXRNRxBFYnmDC29eJk2XLAblVIKO65HXRVxZphsoXKt0OanK1OfqO4heEASsra1x6FC0sNVqNUzT7DuAmaa5ozOxMlel3rXJDmZ3yHSjrCuF5esVHu44fbmj7/vUajUeffRRIKrOZTIZbty40Y+1uAVBEBg5PUJmIkN9sU6v1kNSJdLjadLj6SifbBeMjKVxJQehoVMX2pi1Hvs+M0yz5/LcpQ1qXYcDg0nEO16woRoUUxpzpQ5/e2WT/WKZC/ULNMYa6JLORm6DZz/57AOS93OEntFJDCaozFSw6hZmyURURHIDcTZMByOu9a+5MAgJTRd3sYG30YaBGF0BWpsd3PUOhuXSFRxa2wQwH08gFA0ERSLWdhnMx8iIIoXDBeJDUV7iZrdKPKZSKBiIwu2cSdPsYgYizcDi2vUF9p4eYCg1hGd7NOYbBLmAI08dQZRFyuUu3Y5NYSCPrkssLS2RSCSY2FPgV/7JKd780SKVpQbJA0me+sKRHSTvFo6fHuWDHy7QLHXIDEVSZUVRiCfitOw2eOrtjYoQ/SeXy+FsdUkcyBFLfUjmHYbR++iaiIKIHMq0yx1KQzEMWUIUbku5QyeacQSQJYn84ABeIUu57eJ0AmzLIaVLbMottEEDo5CklmyhlXUyvkw2IdOTFdq9SFLadQOqXQdJiIj6+N4cgiLTXqqjjicZOT2CXZlD9CRI5nHbPn7PRUkoDKUN4qqMKEaft1/rIeVjxCYzbFZNNpaauKaLqIisvrmK1/NIH03TMF1eW67S8QSSmsDwyE4r98S+HF7WoLveplTqYHdspoZSDJ8ZJnAC3ntxnplal/a1CqEfIiQU8AMakogYlykcGiA/nkY0FJAFGot1fNONCg+GjCpLJHSZRtchrimIxThOxaR0uUQsbaDo9yFoIfhNm/ZGG61q4pdsVm/9TgRFU3C6DonBxAPXzf8MeOKJJwiCAEmS+vFMMzMzHD9+/K4ZvHthaaGG4AbISQWZyEExDALqjQZKSqV6vcrlC0scP1AgRODVmTLtuSr65U2Sg3mUgdv5roXRFL3NFvY7C7TcUV5JaPzuuT08dSDaS128eJFs9t7drzshCAInTpygVCpx6d03OHv2LMPDwywuLnLq1CkAej2XVwubVBbrEMDQwTyasvMavNN8pf+el5bYs2cPQRDy7e/d5OJzMwiWhy+JLM5XOXVS5aHjOzuD3/rWt7Asiy9+8Yt8888vcf7lG3zpd85yPywvL/dJtu/7XL9+ve/Avbq6uiuZLG21eeNvruM60VjRu9+bZf+RIlPTdwe6V6vVfjTELSnq8vJy33Dup4F2u80nPjPNnr1jTB+4v/Hdh2Wkt3DmzBni8Tj1ep3BwcFf6niGByvgzxBW06Kz1cHtuKy9vQZSVJHfayiEeQO1YJDMxsjGFMIgpD5fZ9+xfdj7bY44Rzh+/Djf+ta3mJqa4uzZs/R693dTvAtBiOt6pNIpEvE45WqFMAhptVrUWiaptoscesjDybtiECRVYfD4HszZCovXFvm+ZnN6PM3rLy/y8p9dJJdNkt6b2vEFEoDUUJLptM7sfJ3N5RbBWIpRz8dfbqEZMp4QUiuVUVwBu9UhdXIERZewOjZ7ktq2KU1IpW0zkNTJx1Wseo/16jpf+CdfQBRFfNenXjERNRH1PgGZjXqj3/2MxVVsy+OlF15DcW2mvvQVZGRsxyOdThP0XLq2hxTXcLo7temzs7P96len06FUKvVJ3/r6OiMjI5RKpf7jPccnREDaZXERRRHfD3aYBFSrVYIg6JP4UqmEpmkcPnz4niQqVogRK9xf3hCGIV3HRxIEUmmBhz4zSHVRxuo4HP3UFJ/71SNc22yz0bQie/JdnksWRSbzcRYrXc4+fIBTp2rU63XCMCTUQqrVal9ieq/XEHgBoiw+IIQ/BQiiQGI4wdW/uIqkSiRGEiAIpNs2K22b65stDEVCkUTimkzckFESKt2NNq3LJWxVwkooGCNx0laAWmpilS2kDsgpAXWfRqGgkWpIYPnkjhbJ7s3eDmcnJBSIyNC2XO6W867jOOiaTkKRsW2b5WvLOC2HzP4M4+fGGRwb5MXv3uTd5+dwLJfUQILP/dYxDh6apN1us7CwwNjEIL/3z6ONSrTxuT3z2i11CfwANa4yOpbi1NNTvPU3N5AUiWR+2wFXkdA0Dd/38TwPSRKJKQJN0yFsOchZg8L0Tjc41wuQRZG0JtLtdhkoDtBYauB5AbYsIXg2ifSH5163z0cYghDNITfcHo7oYcoBXWzCgka518Ra2WJsbBwGA6QgSWemhlOzUASIaxJqXGEqY6D4Nrl4gtDyaaghqbEUsYEYF2YuUG6VOXnyJIVCAj3QeWOmTk2XSG6v26Ht4zdtpLyBtj/KohMEgSAICMOQ2myNys0KmckMpuPzxswWta7DeD6BEdtdsibnDVI5nZiZpdQwKQ/E2X+oyPXzG1z7q6swkSa+N0N3tY0+EBXbAtfHbdmUz2/iuz7FySx2EOJXLaSsht910bcdjBOqTKvnRtmCkoia1bErFq3NNvl7hKaHto+z0sJd79Dp2owMJkgXY/3OXRiE2G0bu22z+cEmgiAw9ujYrnL2B/jZ4E5TE1mWOXHiBL1ej4sXL2IYxq7Sww8j2MXLoN3ukMtkQRSoqW103WBlZYWVusX8hoU0u4Ua01GLOzvCgiAQG06jijKN5ToXEgGPT+fYP5Tuz7z1nzcIMd1ICRVTpXves4rFIul0mrfeeovJyckdM/2u26MeXmby9MOoqsJTn9uPdEcxfn19nVwud5dUtNfrEYvF2Gr1uP7qEklZJHlkAKticv1Hs+yZPsLKykp/Nm95eZmrV68SBAH/j3/9/8Re34fQkJm/VuaRR3fvltZqNZLJJKqqRgXzq1c5cuTI9r4kMvUSRbHv0HwLluXhOj6xjI6sSjTW2jjW7nPhnU6nLwldWVnBtm0mJyc/Nsn/ONjc3OShh/ZzH7NWFipd/na2AwszHBxO88T+Qt9zAKIC/cDAAHv37qVer+/qSvrLgl/ed/afGbW5GksvL2FWTNSUSv7MMNXZGt22jdJ1EComwYqCsTdL3ZDBh+RokslPTnI0exTHcUgmkzz00EO8+eab/Nmf/VmU5TQ42LfeNeIqYhDi+iG7KVTanTbJZLTo3QoVVmQZWVHwPROpa6OmU8jJ3W/0QghCQuWAlmTeFflv/5fnMC8F6Jq0q6HILSQNhanJLAuSwMZGG3k6w1QxgbvcRnQ91JhGM6Fgz/o0cjoJYNjtIgLrdTMKEU+onJ7IIEkCNz+4yZFnjpAe2H7OcPuG7jgYsWiRvnNmCaDT7hBPJu4wlhARRIF0McczJx+hMZDhvfktpobzuH5Ax3KZLiTwLG9HlMLGxgYDAwPIsoxlWSwsLHD8+PH+79crTTZcndnVDiWqTObjDI2nMSSRpulQuKNz4PoBQtehcHAAJX6boJbLZTKZDIqicPnyZVRV3dUW+sfBza027y3X2WxYSCL4tVW++tmH2VPM4nsBkhwt5hcvrpHQ5T7J86o9ehe2EFSJ2KkhxLiCpkj4Qchsucv+qSn+9b/+1/0bxG4IvIDWWovqzSqdjQ6BHyBKIomhBPkDeVJjqXs6gz7A/WG3berzdURJRFRE2o7PQrlL2/aQJAFDkTBdHxyfluWiySKaHxKst/G9EAmflB6jMJpClUTiYylaa2WkJvRaNuaWhaeVePKPfgPRFqncqNCYbxAfjKMmVLLFBPrVEj3XIylF17DresRiBqlUmiDUyNoOqqOSLCYZeHqAMmVK9RIr611e+9YCiq6QHkxQW27yvT+9zPj/4QmSySTJZJLNzc1+yHE+nyfwA1bf3+DyG8usLzdxvYB0SmP6SJFHH5/Atjw+eGEeq2mTHkkgJzVEXUb0BdBETNMkNH2CqkuQ0Rg6OYhxh5NcEEC5bTOU1sFqMTAayYqsmoWWMwhlIdpw3mnqccf/WpbV37CJorBtuBUSjychIROIIfv27ieVTlFba7N6vUzQcRCDENHxoRnNsK2V2uw9MoySlgjyAkOjQ2SGMuT25Wi/0Ka11sIsmfhJHzEmYsQU/I6F54dgeQiqhDKSQJ3KIKhRJINge6SyBqIiUr5axg5tCtkCM2WTuY0q+0fy6Pr9Z20EITKmKepJNto2l2bKzH+wgSAIpGMqdl7H3Ozi9TxkQ0ZUJLR8DLdlUbtWxUjp9ATADwjdAFGViG/PRxqqRCamUOu4CAjIkoQgQWtjd6IXdF3smRpurUdXFcmMppgYTu6QZwqiQOAG5PbnGDo1RGu1xc1v32TPU3sYODJw1zEf4OcDwzA4e/Ys9XqdN998kyAI7ivdHJtIc1UU8E0PKSbjOg6KIoMo4LVt5IRBKiuxZ88eVtwKnY0rjDkSXvbepmBSTiddCuj5Oq9duI5qD9FsNjlx4gRBEHJlo8n5yyVKK00EUWB0MsPDRwfZP3i3uRlEXcannnqKq1evcvPmTSYmJnj99dd5++23CcIuf/DHj91FFJvNJp7n3SVfvCVvBAj8KMv0VidaVkR81yOVyjIyMoQkSYRhyOLiIidPnsS2bYaHhwndIkszNc4+dXfUAEQSzXa73e9cXbt2jf379/cJzurqKmIiz/XFdUpeg/FCkmIy+q6OjqbZc2KQubfXIQxJTxns2aWbdye63S4rKys89thjP1US1Wg07nIF/zAqHZu/ubhOsxVi+D1e7jhIosCnDt42n6tUKkxMTPQls7/MeED0fgaozdWYf36eMAxxBmPcrPXYcly8go6oiMQ7NjlRxDBdNs9vMvHEBPu+uC8acNdkzJ7Tl17mcjm++MUvsrKywre//W3+5E/+pD8PNnZ0gNx7a2yZDjF1J1nzPA9RkvoGKBAFj9YbDXw1Dk0LLaXTEnwK8u5dsa7Z5crMVaaGJkgdO4jeEak15pk+Pb7r4+9EJq6wfzLLgumwMV+H6SypM0ORxCgEq2vTapkkZ9aQU11GRIezBw/RsV00WaKY1FBkkdn3ZkkPptl77vb8mqiIqLqA1QtYrHTp2JEFuYhAUpfJxBQ6rRYjo7cX057lkMtk+NXffRptw8VqlDk8McBm0yYMQ0bSBgfSBkLLJrcvWsBM0+wvop7ncePGjT7JC8OQ770/x/srNlZ5BkyXq9dMYiNJjg0l2XuowKUrW5SCkGRMxfZ8OuUuQ4bCscfGd7gnlstlisUia2trdLtdzp07d9f5tBoWvuujpbSPlCJdXmvynUvruH5ILq7iByFlMcd3r9f4jViMwnaWo+MHmLZHTL19PGexgbPSQhBAGYqjbZ8LQ5VYK9c5eyJaKGVZvkuLD9BcbrL65irt9XYUY5HWomqh51O6WqJ0tURyOMnYuTEy96jaP8C9UblRob3WZvzJcW6+tsLSQh1HEUnpCpIIhbhG1/HoWJEs0HZ9eqttVMcnWTAwQgHN8VFCQAAprpDZM4jfdtCHY9xcWqGm13l3/V1++7d/m/yBPKUrJVpLLVprLYQwIBdTWWtYmCkQAg9NlvHaDu2WhdZyyO9PMfr4KCMPjbDV3uLh6YdZXl5madbD6/kM7o1kPdmxFO1yl1qlR2w82qANDQ3RarW4ePEi+/ftZ/3dEj/4xnVKvg9pDTmhsNmxWfrRPKW1Fp/8+jHyxQTnX1mkttQkDEIkLyDcNAmTKp7tE4QOwweyWIUYHUlAsCJzFsv16doexZTGRMxjaCiKivEsD0mVyIym8CpNYrmd0nBh+/sX+AGSKBL6IaHjEbN8mjUTzfZB9Uh6cVIJA0OLUV9vU76wSeAGqEOJ7czBEMvyoGfjubBqOhT3xMjkVaSuRGIowb7P7iMYDHjSe5K182tU16ooSYWUKqHWLHpFifT0dhbdreJRCNW2RS4UOHhmBLtp09nsoGU1ghDeu7FCPpX4SJJ3JxRJRBRFrl0uQdchN5rCb1jEJ1IkxtN0FmoIooG0fW6UlL4dv9EikAVCUQDTJT6VQb0j5DkX07bngV38UECQJfxdOgWh5WPP1LBrPboxmXRMYf9g5AhtuT6WG3UuJYCeR/FYEVmTye7N0tnssPDDBURFJL8/f9exH+Dnh2w2y+OPP86FCxd44YUXOH78+I6Ipls4dnSQd/blKF+vMjCdpedYpNIpvK5LeanB0MkhRoei66jZ6YHtIoQCsn5vhY+gSOCHqKGAE4p0Op1IRTA+zvlVi9e+fQNhtY0RhATAzfc2mD2/zmd/9Qjn9t1btXLkyBFSqRTf+MY3mJ6e7rtlf5jkOY7DxsZGXw10J1qtFmNj0YxaPqkxfGKQpZeXiLVs6s0Oo2fG2Dc90L/fzs/PMzExwfj4OMvLy5ELeyLBo0/sTvIg6gDeUibNzMxEEVLbXbZax+Jvr9dpmE26Wx0kqU58OMmRiQyfPjSIoUr8o98/zbWHRwn8kD/7y3/Lv/t3N/j85z/PwYN3u16apsk777zDU0899VN3syyXy/eNnwDYalnUug6jWyZB2yGczjBf6ewgere6lreyg3+Z8YDo/ZRht22WXlkiCAK2VJGL8zX8ICRlKCg5GS+t0zIdmo7PgUKC6ZiKIApoKY3V9Rbf/6srNLe6xNI6n/qVQ/0h5/Hxcb7whS/Q6/V49913uXLlCg8deojx0RRbm03cbdnjLZhdk1T6tlzF90PaTsh81cQ2LYSmxWbGQFm1KItVtKRKLqmTMZR+Nluz2cKQNZZXVyhd9DjSTBLP6Ij3sP79MBK6zIEDA2ysNJmSJZSUhuUGKLLA4eEkxclP8Mp/9w02rqwzdvoQhbhCMaURhiF2y2ZjvoTt2jz9e08TL97ebK3UeyxILs26TSdsYXghYhASSAJlVWRDl4hrGhlnm8QEIZuLFaYPjjJ1qsiCPc+gnWNYUTgwYRAKoPZ8rI02dVnkyl9dIZ7WSQ96fOFL5wiCgMuXL3Ps2LH+onVhtckL59cYXPdI1W2EIJracVc7vL3f4txDgzysSNy4ukV7s4MiCoxlJFbq79EUpxjh9s2tVCqRTkcyktOnT+84h2EYsvT2Ku+9uIBluoxP5zjzKweJ5XeXbdqez2tzFUBgKmfgbXYRNInsWJYbm20urDT4zOHouaVtmYZ3xyInJtVI9qWKiPHb1VE/CLEtsy/TlGUZ13V3EL3abI2FHyzg2i7JseRdhPSWIUR7rc3sd2eZ+vQU+QMPNl4fF57lUblaQc/qiFmD9ZiEu2GTGYixPS6HIESzoInt2I1uw6Lpg28oaIqMoUn4TRu/6yBn9Ejy13JQJpIoe1LkTYuRvQf4tV97kqWlJTLZDAe+dIBuqUtzuUl7vY3b87DfXKW60sQSQ1RdjRQGqsSRZ/fyid8/SVfu0gkiCc+tAf94UkRSJMyWRSyl06n1EBRotEqEKy0gMjt65ZVXKJfLjGfGef4bF9gSVQZH0v11iaRGrWNzZabCwKtLPP714zzy2ATXLm9x9YN16nMNzA+2UPMGxakMqmGztbXIZr3H9NQn2GzauH6ArkgcGkySklxyqXj/Wu5sdMjuzSKqIvnnq1Rcnzvrx6IugyzQa3TQQgWn2SC0fSTbReu6GK6PZyiENz00Xae72KG+2gRAG0n1u4Mh4BKSyxnkUzGqS00uvb/Bl3/zIaqVKomhBLZtkxxIoqopGqtNSp02ib1ZHv3EGOG3bnCj1qWWVsmqInIQYrk+zY6NXulx/JExBg4UaG+0IyMWA67Mr2EFIlO53bsU90OMkIXlBgOGSuj6eNUe4lKLgaxOWNXpbXTwZRExrSPFJaSYTGehAWLkAptMa6QGPyyrg3xCQxZFGqaD5XoogbgdGSMiCOAHAe25GuZ6C3kgzkBcZboQx/VDZrc6VLsOjh9AGCI0bdLFBLoqkgy2swuHErRWW6y+vkq8GL8rG/UBfv44evQoW1tbNBoN5ubmOHbs2I75rXRM5UtfP853/uQiy1e30AQJU7LxZZH8sUG+8lsn0AUzcpislIilkthOk5Ryb4lu6G871voe7799nsvP3eSP//iPuby4xY+eWyG10iM5kkSMK4RhSKLt0Jyp86PnZ5ksJhhM3fu6GRsb48SJE1y4cIHf+I3fuPu5w5CbN2/uqoIplUo7HLdVWeTkqRibJRFNzzMkxPlHXz/dj4ZYXl5meHiYSqXC5uYm2Wz2rgirD2N1dbVPJJeWlsjlciST0RpguT7/4ZXrlGY65NdNEtt5qs5CkzfX2/Rcn2Mjaeo9Fz+toUgCUydOMfNBpDT7oz/6o75U03VdTNNkdXWVQ4cO/dRIXsf2eG22wma1wUOjHy3D1hUJWRJwcjqqLGJKMKndLgLU6/W7At1/mfGA6P2U0VhsYFZM3GKcS/NVNFkkdUcFU5EikwvT8bnZ7pEtxEhWLZYvbPDC6yvUlhvECzGqSw3+9v93gdR/qTO+JwNEIZO3NMUXLlzg1XdfRenpJNs6JU1mOBtDFAV6vR66sb0ohdDouaxXu/SqJuGWRXqzTSxh0JVd4lsmQS/ASaqspFW28jGG8zFycY1er4fiCMj7R8kbcdxFk6FD9x98/TC0hIoahBQD+PInJu8+X792jIeeOsn82/OsXlztdzIFTaAb7/KZP/wM6dHb26z5coe/Pr+KhUCu4+Fv1tBjSrRjCCP3Py+u0s6qzItdJvNxus0Ghi9z+JFRFtcWOPfVc7QX25QulzDLJoSgpHUurzZZXW+hxBXajQ7pgSTj45uEQplDhw715QeOF/Dy1VUSqxZGzUMZSSAoUnQjWe9g3KjxQ01kz94UfkxE7DgYMYVDJ4dZfu4yf/WNv+Lg1YN87Wtfw3Ecrl+/zu/8zu+gKArKh2YO22ttXvnWDWbbPUJdZu3NZTRD5txvHWc3bLVsym2bsWwMZ76B+c46Ylwh/tQecnGV65ttPnWwiCQKyJLIdCHOO4u1vkRD25dDSusIsoC8TSaDMKTn+Bwu3CZ+sizvkDu019ss/HCBIAjITmVx3ICFcpeVuonp+BiqxHjWYDRjkJnK0F5vs/jDRZSY8mB+5mOiudykW+6Snc6yUDNpZ3UKB3K4K20E20dMaXeqClFlkVbHRiJE0iRMxyemyQiyiF+3IsJhByjjSbQ9GZAE8gNJNt8r8dbfLjA4lESyIqvu4eFhRh+JpEV7P7eXQ1fKvPP8ZayWSL3RwA5NvvRbTzB2cgg9o1OfrSMIAoODg6yvr1MsFikU4Ma5UWbeWqWx0kJLajz1a0c5dvR2FbrVanH+/HlkWeb/89/8e+TgEKmp2G2St41cQmMtYbNwtczxUpfEYIKHHh7loYdHCYOQhR8tsPHuBsaIwQs/eoFWq8WJEycoag5HDxYQJAlNErF6Jr5Pf73sbHUQFZGRMyNsrW+xJ5+mEQTRuds2OhI0Ca9jE5a6eKoCioigSdiiT0zS8G0Xr6CRKqQIg5D2Zoew3EOLKbDVJcjpkbOl4yMRkEkkEMIQJSHSq7tsrrVQgHgxzubmJrFYgf/0379Neb4GISxdbXP2ywf4zB+cIvHtG8zP12nWeviCgOKHjOoy6+Ys3bSCKx7CaTt4rke1UiXUc6iatyNA+uPA7zj4Sy2UtQ4kdFzFwu9Es8xSQiUjSmhxFatl4S03CQRAkwgFyJ4ZwcwaZDI6Yc0iLBh3zXZnYgoJTWZ5q0ksm6Sx1cHvefiOh2R6yGtthoeSDA4nScVUql2HuXIX2/WJaRIZVSHouXi6TCuv8/ZKg7rtcXIskv8nR5JUb1bZ+mCLPZ/85TVd+PsEx/F4761VNF3m9IccFW8pQg4ePIjruly5cgXP8zh+/Hg/VHv/niy/9y/O8qOXrmL3FCqVMkeOT/DImT2kDBVI8vrrr3N4cor5ZpkwqeC3HOR7RP34TRtSCmFGo3djk8r6Ov/qX/0rHvvVf468aZEoxvpdcUEQkFIaKcdnc6bKtZUGg0fv78xp2zYPP/wwQRBQr9d3OFjOzMywb9++XYlPpVLpE0Df9/nBD37Aiy++yNNPP83U1ADT09P949wZo1AulxkdHd0xY/hhrCw3qVXrDA7r6LrO+vo6qqr2zVIgGvNYnKkxuewgKSLSWCqSXW11ES+V+EvT5q3RNHFNJprShoqZxs4f5ejB4R0k9fr16yQSCQRB2NXUBaDVsnjvzRUOHikyMnZ/CeYtvLdU45UXZ5E6Lr1zEof3BJHD9D0wmY/z8HiW8ysNnLiC7vd4dOo2sWu1WjvMVz48k/jLhgdE76eIwAuoXKugxlXm6iauH1BI7i6PiakSXVtkodLlkWyc2ddXqS/UGDw8gKRIpAbirF8rszRf6xO9bDbL+vo6Y2NjPPLIIxw+fJg3XnqDrZVrBHMJVvYMUsjE8R0XI21AAButXrRxWO1gmDay6SIlJbLnxih3u+iKiNJ0kU2PWMfFKvdYHrLpjqWYyAxDcpDS4TxTisza1Wtou+TVfRQEUaC3S6CzZVnIcZmnv/w0T37tSZauLNFutBkbH+Pi7EU+/8zndwzwVjo2f3t5k63Lq0w1Qpp5g6rtYqoiWkJFQADXR2nZZCyXZghXuyYDPThweJD8WEgsU0Q3dPTDOvkDeext6eb8UoPNVxbIT2cIpYDUSIz6Ypvvf+M9/tl/9ckdr2OrZbG8XGWiB1IucikEECQRM6WxOVdlQQkJjhbZO5khRpRJ9sJagxV9iuGwxjPPPIPnebz++usUi0VEUexr9O+E03GoNi30gRj5uMaqVaey0e4bnHwYQRAtxKIQmUyEXjQbgxcgyhIhtxa1aKN1ZDjFhZUGzZ5L2lCisOjBnVK1rZaFGjo8cuD2hvzDRK90uYTdtsnvz9OxosD7zVYPRYqMQTq2x1rdZChlcGYyS2okSW2mxtalLZKjyQdGLR8DtdkaoiISCgILlQ66JqHtySIaKu5KE79sIsYVBEPpGxn4bgCSiCKK2F6A5XpogoBXMpESKtrBPMpgnDAIaV6v0lio09ns8MKfXsZIaWSzOodODiOdlUCD0dFRFF0hvi/OE5MPE5fivPLSK7x9/gax/Sp6JvqeeJ7XL9rcueH5R79/miunRjC7DsWhxF2OmoZhMD09zeHDh6m+a/PGhfL2BuNuaAmNRt3G7bo7fi6IAhOPTeD1PMpXyhw7cIwrN6+QSCQYHhmOZgB1g0DXMHvRQH4YhtE8qRsw+fQk2aksNafG1NFBKm+vcm2rhtzzkN0QpW1Dw0RBREhH607L7CEJMgUV6qqPo6lYro+uSDieT6DLSAkVoeMgWi69hEoYFxnJJ1FEkW7PJFNIUVtusnGzypHTI8SGYvQaPT545//P3n8GS5Kf553oL31meXu8b+972oy3mAEBDmgAATQgsaK7q7sI3ZBiGZIQV6Qkrr4oNja00m7EkrraXYraXVJcERABEoTHYLzp6e6ZnvZ9+nhf3qfPvB/qdHWf7tMGIMgFyHkiTvTpOllVWZmV/3zN8z7PMoXrFQb35ZAUicpKk3OvzPPwkxN85NePcXS2ysKlIrbpEE8ZjB/q5+3LPu9ffJ+1P17j8fHHqVarjBwe4cJyFfkejAzHcZAlqcfa8GsW7noLv2LhVU0EPwRVRIipSICU0pE3Z+7U/ghxP8SuW/htl8BycdyAx44N8/7FDSwvwIiq+FUbKa1tuebrtRqNQpOw3mHKSRAr2fi+z/XrM/Qbeej4RGs2lu1jJlTmbZ9AgsymcnFgeYRtF2MqRXIkgeX6XF1vosoiB4eTCKJAtC9KabrEwEMDPeuPD/H948qlAu+9vYSqijz27NRdg/Uz7yzzrf/jfdSIQjyhsWvP9jOSiqJw9OhROp0O7733HvF4nH379iGKIvXyOp/8xEOIoshv//ZvE3p9PP/UPwC68UMQBOwbznBmpcNSn0FkxSGIyF2111sQWB5h06G1K01fNkJmsp/Hf+ET7Nu3j//88jKKW+t6Xt4GMaaiNmwKhTYcuOPPW1Cv1xkdHSWbzVKtVnn11Vc5duwY7XabbDa7rRhJvV7vdTIblss7lxf487PztMQcG6bAlCj11s5bbRRuGKLfLZkCuPDBOl/9g7O0Gi0+/ssnkOUSnufdMRt5+toSiY6A6PrIg93OYBDAmiJSXWjhxxW0iTS7B26yAIaSOsXmANfaHf7klff5uWeOsrKyguu65PP5ewoHXvxgndPfmaHTch440XP9EOwAKQDPDQjuk5hJosDHDgywfyiB4wVkIzIby/OQ3betCuff9Pjjw0Tvhwin5dApdwijKuulFnHj7nxxgKShUG47WIMJgo4LXojvB0iKROCHCCFbgvnbucSxWIyPfuKj7Nuzj6//ry9x/dIsC9EosdEBXMmh2nEoLdeJLDeRLR9FEYkMxdAmomgpiXgg0xiIkqKD3HQJFRHD8lFnqtQ3mjCRQT/SR3YsyVDbYl38AT2JQlC2UYtZWVlh3759PeWrnSd3Yts2f/Znf8bjjz9+x8J4abXBxkqd7HwTwYiQfXgY4ZpBebqEXWgjRBVkQ8VPaVAxUa6WsNI6qWNDHH+hn6HJLK1W6+bxlMSe8Er7ShHf9VF1mVa7TSKZwBer+JZ2By2iUqsjCzKSH24xo26aLosNE8ENSCoSOUMhtmngnExqhEkdyd+Npht889RFDo2kSSaTZLNZZFm+Y94Nul6M2aTO9XKH5bZD1AnIDcbvKmbSl9BIRxRKLYe+qRSiIiKoElLOoLrR4uREBvkWiu9YJsLDkxlev17Cdn2yMa1X7Xf9gNVKi0arjV68wmD2aO95tyZ6nXKH6myVWH+MIAg5u1hltW4ymDS2dA6CIGStbnJ2UeCpnV3J/hsd8Gh+e4uMD3ETZs1EMRTajkfd9LqKi2J3llJKqLhrLfxSB7/UAVFAUEQEy0d0/G7Hz/a6iV+0a6eg7UijDMbAD6lcKlKerWLLAiQ1omMxYrkY5arFmy/N0qwP8fH/6ghra10hDsdxevYiK9UVPMHj1Vdf5Rd/8RfZ2NhgZGSEcvlOfyNZFjly7E4fpRtYW1vjl37plxAEgVMzF5CCIp4fIG2z9rhegKKICNKdN2lZl5l6fopADGi/22b/wH4GUgOEYUg+n6fZaHLt6jX27tlLc7WJ3bCJZCNMPtelE5dKJfqH+6mGHWp/ehmulWiEAWrdRfICiCskVAWv6dDBR5cVIm6AH4HMUJq2qFAzXUTTxbV8gjDEDUMCXUKwfdSKSdaIdQt+ZoeosakaGoQ4TZv8gTyFcoHR0VHee+sioiT0REeMhEq7bGK2HdIjSYYeGmToocEtnz9byTJlTrFr1y78ko+iKvieTzIeh1vWwNuxvLyMqqgMDwzirDRxV5rgh11RpsEYYaGFvxkohdwZIImS0F1TMwadhgUNh3C9xbAscX21iZCLoochfsVCyuiAwEZhg85qg7DgYEQFRMEnTKhs1BpUZQG/2iCVTiIqIoYTsHGtjAMkRxNgbBrYtxyU8STKJjtAV7r2DbPFNlO5GBFNQk/pVK5XaK42P0z0fkCsrTb46n98j1ah3R1PWajza7/5BMY2sY6my6gRBUWT0PT7h5qRSIRHH32UcrnMm2++STqdJpfLIYoilmVh2zZXrlzh3Xff5eTJk8zPz/Poo4+yvLzMvpzCyo4sjtJBWGriC1Y3cRMgaDoQhtjjSezRBC/sGUQZeKbnmaZp6wSSSOgGm7OzNxG6Ab4A+r3sPugqcz/00EMUCgWy2SzpdJqnn36al156CVVVefbZZ7d93srKCvv37+d6ocnXX1+gfLmAOpcgIcY5c9ZE7RMZGvUQfIdGo8HY2FjPEH27wvCtWF9uUF6qoKkqs1fXGRgNtjVRb5kuSiDArcqgNZNCyyKhSkRFAcHyceZrhE6AYMhE+6PE+xKUWxpvzK+C9w4vPnoQx3EolUr3NH0/cHiAl196jcMn7tQiuBuOjaUpPDVOs+Pw5J4+9AfwxxRFgfHszbhCGhpicXERTdN6VNPvF+22gyyLaD9mli0/Xnv7I47AC7rm1UI3YTPUex9eWRTxghBfEEildfpVkbXpCmpUxe445Ccz7LlFKSwMQqobbeJai/Qtsw4jO0f41X/xWc58+wwvfeltrDmLjpak3QhIrzXREYiNJiEpkp7qQzBkNjYKDMUjlDSRqiiit1yMuo0ki4imixTA1YjInqEYv3hoiJn3r6LHDeyWjRZ78Jtk6AX4QUAitTVpW1tbY3BwEF3XuXbtWo9+sLi4yLFjx3Ach0Kh0LsgTcfn/EodcbWCHshI+QiCIBCfjDG3OsNAbBCzZBGUO5vKdwFRAWL7+5AeSpMeipPL5Wg0Gtvu5+BwAi2usTpXYGTnIPVaA6flMfX04B3bWs0q2cE0zlwHrWEj5iOEQchKzcRv2kTjKpLjI51ao2l6EIKU1NH3Ztk1nmNmvcqFWsjeYbEXDN9t0Y4Px3nqZ/ZgfGeWq1dmePS5oxz66I5ttwWIqDInxjN8+9I662FIZjyJG4QsFdukIgpHx1JbthdFgWd259EViTMLFWYKrS4LdjOYk5w2l77+f9Kn2NRqP006naa90aa52MSxHMQhkY2ZDeyGTXw4zkbDZr1h0RfX76CHiaJAX1xno2FRaNoMpnRaqy1qC7UPE737IAxDQi/smnQHIUHYPZ43IEYUtB1pgqE4ft0m6DgEDRtZl3GqIaEi4usaQUJDy0fx6/ammTXY5Q7V+RpuVEENAElEkkN0RWKgL0o9InPp7BqT+/MceW6K+fl5XNelWCySz+d57rnncF2XnTt34jgOrusSi8XwfZ+lpSUixr1tQG79jHAzeRjf10fi1XmqLYeB9FY6lusHULcY3JEjdheDbVmXkXfKPHLgEWbencFes7FKFghQKVfIxDPMnJlhaOcQk89PkpnK9DqSrVaLTCbDF79znmoQEM0pcK2ErhiYCR2/7dJwbHRColGNwPWoqiFSOoFn+miqj4CAFwS4QnftlgUBXRZRDQXRcVHqLm2xTnQw2Q1I/ZCw7ZIYS5HZk6FQLnTpr8NdldraehM9qlJZqNO/M0P2HtfMyZMnmZiYoFarEREjhH6I1bGIKTGEIMTzgy0Fn3bNorHWpDRfQ3ZCEDtoIRj9UeTUDb/PADmhE7QciG+eK+Xu9Cm77jCwO8PEyWGy6y3shs3qWpOqLKA6PsJCndCQYd1Hq4EfDckcGmIpkKDYolqrE4QaZseGqEvT97ujCQJE/BBnqdH15UtqKBMp1LHEFuXluK6wWuuwVjfZ0Rfr/k0As/p92hR9iB42VptdUbF9eay2TXW9SbnUYWQbFe5jJ0eIxVV0Q2Fs/M55qBuF69vpjNlslieffJLXXnuNarWKKIrUajU8z8OyLM6fP9+lkg8PI4oSKzULyw3ZO5rlkiCjJBSSFRul4UAIbl+ERlZHGojx3N4+8mKL2MhNKumBvXlmX13ALXZQR26yS8IgxC11oC/Crom70yPb7W7Sm0gkttgs3VAWVRSFN954g4cffnjLaEan0yESiVBq2Xz15Vmab66QsGyiUY2oHsEpOVz65gy6LHIwc3N9vWGIvri4eM9zlR2EsSODuJZP36i0bZK3vr7OjuEsp2ZMwrU2oR9g+SHFto0hikiyiND20E6t0vZCQkEAQpRcBOPYIJmsjq7KnFmq84knFJrNJqOj9xbrc5wWLnOsrF5ldOzRe257A5moyi89Mn5XT7wHQTKZpF6vs7Gxsa0A0O0olUokk0kURcH3Ar725YtceWcFRZd45mf28dD3afD+/yQ+TPR+iBAkAUESEAMBSRTx/JB7Jf5eECCLAmIYohoqL/70Hs58sE5hpUEyG+Hpn9hJdrMiEQYhX//P53n3pWtEonM88tGdPP3iTQl+JaKQP5jnH3/87/Pud8/wf3/3ColQgZZPUWqR2T+GHosgqjc46CAJMJWNUtEVSm2bekYj8AMQRCINh2E7IKFI5CIS7MgwPdGiPlcjv+vBE71WqUMkF+HA4ZsXlmVZ+L5PJLI5A7bZpSyVSjQaDY4fPw50/eWuX7/Ojh07WKp2KNVM9PUOcqKb5JXLZaanp2kHbR46dozQDLBNB9dy8QKPpKZj6wIbtkeg35siMDaRZvhQnIUzHvPvrxCGASP7Bnj8I1uTqlarxVhfml1hwHuDNfKzddz1Fm1JxCy0ifohjaRKZqWFEdeRchEEUcCrWrRPrdA22yRyGtVompIjc+XKFQ4dOnTXoWVBEJh4ZJS1zjLnynMc/plP3VWI5QYensigSALvzlfZqFuIosBUPsoTO3IMp+6cX5AlkSd25jg8kmSm2KZhuoiiQCaqcvblr7OoOpRLFabfnibpJlm8WqJUamI5Nsn4GlfeOo3shhzyD2Glc3j+3fnzity9uVfaDoOprgqt03S23fZD3IQgCAiyQGh3BSZEodsh5bbCpmjIiIYMbNImRxM03lwmMBRckS61U5UAoRv4htBYbuAEIXpE6RqK70ij3FKBT8Y0Vow2l95ZYc/JISRJYs+ePbRaLebn58lkMtTrXbGR5eVlpqamaBfaLHxQ4rWvn2FocIhUX4y9J4bp25lBUrevxt4o/txAZirN3gP9nH5/lXUgHVe7NEfbo1E16RdEDjw6gnyXbsHa2hpDw0NEIhEeGn2ImQszJKUk68vr9Bv9RBNdy4im0ERN3KSd1mo1UqkUM9crLF8uYsYsrI0q8bhMPJcg0vHwJYlmqUVoKDhhSJjXSOXjaJqMZdtoarebYLs+NdvHKpnIYUjcUHEdBy1m4DQ6yHWBMOMj6DKdtSZKXOOhT+6lUC70ij/HHh6huN7kwmsLNEsdspMJHnouj6bJ2LbHS1+/xvzlItGkzpMf28nUjiwrKyukU2mifpSz3zvLytkV+gp9iGoLY6NFVRNJj6VouR615QZWsYPTstDMELXpYAYVzJhGy3SJ5CLE+6O0bZ/MQAx3oY5dt1FUaQuj4VZ0mjaCLDC+Sc2ND8R45BO7mTm9SqXjUHR8vEqHcKlJTpBJPjTKgtemJUUwFJmIKpGO9uM3bFyvQbndIGIYuKJKw3JAFIlZIaEfou/Joo4ntqFjdYuplbbDjVVc1mTMyoeJ3g+KvoEY0VyE9StFAj/E6FcQBJMwvPP4A+zee/fOiecqnH53juPHJ7f4zEF3Fu2hhx4iEolw9epVCoUC//Af/kPOnj3Liy++yNraGmsdeOf8PLNX6pSWCmRzWfyISpDVqfXFEDe9aiOGwqG+GIeGE+zIxzh//jxjtyQjewYTnH14mNXvzZOaq6OmNAjBrlnUdJEdj4wwdY+iyvT0NEeOHAFA13Usy0LTtJ74iiiK5PN5XnvtNY4cOdKbj1tYWGDPnj28OVOierFIPgypp+jNvIlJncRKg1NvTvP4rz+J7/tcu3aNAwfuwyGlG6cMDib4r//Rk1y+fIWjRw9vu51pmhwe7+fCWBOrbMFSkxoBYdVED6FtyMRLHWIDMeTBOIIoEPoB3nqb1jtLWAdj7B0bZK7Y5MvffYOd+eg9ZwYB3nrrLcIw5NSpUxw7dgxVvbslxu34y1Ish4eHmZmZ2TJOcDf82Z/9GeVymePHjxMzJnjvW7OocRWz0OalP73Ezj154j8mzIAPE70fIrS4hpE2MKsm/UmN+XL7rvMlAHXTJR1R0C0fKaExMJnmUwe3rzQszVY499oCgSZhBQGnvjPDkYeHiaY0isVib8BW0RT6Tx4kV1ZIhBtc9UpUayarxXX2ZG7K4Oq6jmV3JYvzCY1sTKXj+PhBiCCAPiwQFtqUV5u8eWGGjz+8n0MPh3zvahnf8pAegIoR+iHNYpu9H91Bf/5m1X1lZYUdO24mULquU61WuXTpEk899VTv8Ww2Szwe5+rVqzTkFM1qg4ykIhoyYRCwvr5Ou9UmEY8jiCJSXEY0JNymSy6dxy638RodDGMQy+0mk3dbKCqVCi+8uJP1oxZXL60yMTXCgcMDJG5T2lpdXWX37t08EbMpNCyWRUhsmNQ2WjgSmONJhGqbvCSh3SIyovRHqV8voMxCfvc+WpUO65bIzMwMn/zkJ+99HMOQK/NX0LM6b77zJj/5kz+J7/sEQdD799bffd9nWAtIT6hU2iCEAUndw6utcbV87+cFQYDg+/hBQBEYHRrg4P6DXPnWFb74P36D2NAkNU3C3fThEywTqxmSKFms/IfvMvzUYYShu1PzoDtDdUPpUxAFAu9vtrTxDwt6QscsmcTzURKGTNPy0O5DYYmlDbS+CJ3lBmFSQ1EkQi/oUjtlEfwAu24RaBJe00FQJZJDcbqakDehpQ0KG03mrsyx93hXHjwWixGLxahUKmxsbNDpdBgZGWH5gw2++8ULrKw1KTkeG04ZabrM+6eWOfnEGI98ch/KNlQv13W3VLyViMJjn9oHYciVSwXqFZNAEFD9kMm0wSM/uYPxE9t3wm3b3lJMEgQBNamiJBQG8gNbBAQyZKjVaszPzzM+Pk6tVmNiYoIPzszgmR4D8STrZokwr6KOJQkdn3rTpIWPko8ymFQQam6vuCEChAGCIKIrEv35GKtlk3bFxAt8sgmjq1gb0aDt4hU7BIaE1bDZ9+l9jB7Idz2tNos/kiTy4qcOcPLJCSzTI5eP4Loma2trvPdWhVNfvYoaUdiYLlNea/DUzwwxqKVYPrNMY7mBZErUPZNIy0VNy8R0mbVim9JCg3bVxFNFxP4o8WwMcaMDSYXQkAn8AN90aczVsNsOYj7C7tEkdVFg/YMCQUZHJkTwfbrN2JDAD2jXbNyOy+jBLPFEQKlY6q272V0RlAWP0WyE1IFxVl9aoj7bYKlmYTVdUmkLUZOx/K7vaWA5eI6DZzk0TRchlNAlHUcTaWZ10nENv24T2n6vQ30rBEHAv2WeRxAFwuBvtvDCXyWGRpK8+F8d5bVvXQQhJD8W8M//xW9x4MABJicn+dSnPvVAr2NZHl/9w0uszxaxfhkeeriPubk54vE44+PjdDqdXtdl37597NixgwsXLrCwsMCVK1cQcxN8/ZVZ7A82SNQdhEqVTFnCkaCRUjGODvD0oQEGkwYxXe5ZClUqFVKp1JZ9iesKn3x+J1/TZBbPbxCutwgFAXlnmj1H+nnxsfEtRtu3YnZ2lsnJyd73e3BwkMXFxR7D4cY1nEgkeOaZZzhz5gwbGxvs3LkTRVEQRZG55QZqzcbRIBa5mVCKmoRndZDNGIWmRWHxOnv37r1vshMEARsbG0xOTnLhwgUOHz647XNuMDJisQhPHxrgJS+gPVejMF3C0yWKAzGEeodxXyByy3yeIImEWY3WTJG+vXk0VUaSZVYaLlPZ+9/LH3/88Z41RBiGvbX6bvHIdo89yDY3fr/1/6VSiUQiwe///u9voZfeOBa34uLFi1iWxdmzZzly8Cfx3SjJvgymLmO3HDod58NE728jRFkkvz/PzLdmGMtHWK6aNEx3i+rmDZiOjxeEjCRVnJrFyGMjqNG7VzbCMCQMQrSYhtNxaVsWv/u7v4eo+YyMjPDoo4/2kqfZYotgpUlou3R8G9uxadTrXdPRzeqJJMmEvkUYBAii2JWhvu1G6Uoi0mqL64NdD6WHTg5z6b1V1j/YYGB3tueZtO3++iGF6TLxkQSPPT3Re3x9ff2OAeLBwUH+9E//lE9/+tN3LEiqqrJ3716++e4V2q0m0uaCK4giY2Nj6JqGbTuICBCE1Bt10ukMge/TbrdJJhJ0QpHA8WhttOgUOrj9Lkrk5jnxPI9yuczw4CC1tTV+4af247fb+GsLdGZNgk6HwDQxazXEYpE1x8FLJjmZD9H2RLiehplEiKOm2TWYIPfOKrZbhzDbM1eu1+uo6QiqIxHaHoYi8ca7Z8G2+cY3vsHOnTu7cy+30degGwDbdlc0plQq8cYbbyCKIpIkbfvvrb9ndAlRVO65ze2P3fg/gGVaGEWDkeMHuOr5uIrIZETpceTDEC4uVQhCnaGBHaTaKmbFJEwabHc/CsNuJyq6SWv2XX/boP9D3InMrgylqyUEQiayMU7NlwlDtj3ONyCIAoP788w0bcSajZoy8E0XKaEixtVu0BuEhC2HQJVI7c2RyEdpNbfOcQkIeK7b9VK7fBld13szeplMBk3TeP311/GqHm//+SqLNZPkeJIRTyckxIjEKFU7vPG9WbSoysmf2eolVS6XtyjB3UCsP8Yzf/coh2arzF8q4No+6YEYY/v7iA/Ft1D1bsXKykpv/27Add1eEnc7UqkUsViMixcv9mZyb1yPXqHD4MAAJLs2Eu0goOyHoMqocRVtdxph2cQrdhANBUEVCW54ugGCCPnJFBuOh1V3KXshqbiCJAn4YkBnuoSXVBl6LM9PHRMofelLDKZSeAMD+GHYC1YUxUcUfZrNKr7vUyqVOPP6NGpcIzeWxHU85t9bYv5lKLWXCARo6TKXN8q0+zOU50tIZpRQFGhYDkKxjRaEKEiExTaBHxD6EEbkrmuzAIIh4TsBzdUmscBD6tfIZmXMoQgNUaCyVENUJCRJJAhCAjdAT2jsenyAw8eGkRXpjjU9m8tSuV6h826DiBJlfV+E4vQGw6NpZNPrCmeYAQQBoRkSOqAJCpFkBDEaoRAGKHGNZhCg6BKppo273kLbxpfTCwKMW4ohH643f3kcODTAufMvUSwW+eSn/j8sLV9ienqa559//oFfIwgDfC8g8AJee/11vv6dK0QiET7xiU8gCMIdgiGqqnLs2DEuXrzIhel5Zi6YaBebZDsedlIk2TdAKEpEAhFlo03l7DqXUwYnJjJbbKeWl5d7Xri3oi+h87mP7Wbh5AjFhoUgCAymDEZSxhaK/K3odDq4rrvFvFuSJJaXlzlw4MAdGgOSJPHwww+zsLDAn/zJn/SS4i6lWMA02+T7bzIaWs0Wum5giSILCws8f2zPAxmPz8/PMzE+zsX33mNqcBB3dRXbNPHbbQLTJDBNQtOkuLBAes8eSjt3kg89To4KXNY03lOSRKIRxrJRcu+uY7nVLUU427K79PxU10sTQBJCrs0uEq97rK6u9qwb7oZCoUCxWLxvDHK3OEVRlAeKZW5/bHFxsTfnWCwWWVlZ4eTJkxQKhTsop/V6Hdu2efHFF/Fchf/0u6dYu1xAFAR2PDpCLvfjM27yYaL3Q0ZqIoWRNVA6LgeHElxYabDhWF0fPUnEDwIalocfhExlNP7i9/+AgVw/e5N7CUfCLZKvt2JkMs2+R0a48u4Kgu3wxE8dIDu+g5dffplGo0EQBF3fq1SKxY0WatFETuhMaOMkEgn27Nlzh3R/MpWiXq+TuoufiJTSCVbLeE4O2/PRVYH+KZPaKqx97R0SMZHYc49sEQYJw5BO2aSx3iIyFOdjnz3C+GgKoDe/c0ONb/MJXDhzhqlcDqlSwWq1CC0Lv9Mh3FyUgk4HbbmMXlKpXi5j+BJSNKRVLjMQiyH6PubLG9imiSqKdIIAu9OGFjSjCv47GjP/5DrrLZPAdanLARm1TkZroIkujuOQUxRarstALEZVVQlv/GgaoaqCptFwHGK5HPV8Hnf3bjRJ4tERg6ODBi/FNcquwv7hFM2rNSw/oFQukctkqdZqxBNxhJZH16VaJAg9IrrOUx/7GB/96EdZWlra1ngUut54w7uPc+XKHPv2TTE1mLintPAPE9ffu051xmba80GXGbhNmUwQYM+hnVgXyzRUAbHjonoBtVyU9DbVrrrpEtMUBlN6t5MXQrT/x2fB/H8SqfEU0VyUTqnTtamIqBSbNn33qSoKhoy+J01kzaRTtQjLJuqeLOZaE9d0sVsugh+QPjBI/1R3LZBum5+xaiat+hp/+CdnMBIGzz333JZEamNjg/3797N2psnyap3M7jx4NqphYJomsgAD2SgrtsfFd1c48MQYelpnfX2doaEhWq3WnYneqVPwD/8h6v/8PzNw8iQDh+4/VwHdYtLtMxih7+M2GkQNA9bXwbbBsrr/bv4uWRaZ+Xlkz6PoOPR1NKQlG2e9gxwTEAohru9RDRR8UUVueURYQyma4PnQAK8pEDohoRQQyGEvCVeAfCBQDeNYZYXWmofkAwLIos94qsOLlXfg/5DwHQdzYIDO3r1Iut4LVGRZRlXVXiA0MDDAS+k1SjN1HMegulEnZoqEczbCcJzrVZNKoUPD8hndOYTrFaDlIqQNzLkmThjSiqhEZQGjYkMQEgxEoRcYh/gBhLKAHhdRWgGhK4PtM3RshOxghKX5Os1iG9cLiBgK/WNJDu3Lk0nfnV4uiAJiUuPd//AeDT+gqIj4vs962iM+kiC7M43od2mZQccFRSIfz3V9PQUBpWbiej6qJNKyfRKajF/oEA7Ft1BJHS9AEgUGkzfp6oEbEMk/2Mzoh9gelmWxsrJCs9nk6tWrnDx5kocffhjf97tJxj2EOG4gYqj8/N87yYUPZnj6uYO88sr3eP/995EkibW1NXzfxzAM+vv7eyJllmV1FR3jI/jvVIi2HOSxJHa7jRGJ0Kg30JIJ1OE4qbUmK9crLOzrY2dfnEajwZtvvsnY2NjWwoPvw//2v8F/+A/Ib7/NjnyMHfl7e9LdwLVr13qUzRuoFAqEzSZ6u02nUrkjuQpME73TYdfVq5x55x0G02kyQoozSzBQbNNauIbgB/iODXZA21bwVxzGv/UedbtFw3EQbRvBddE7HaqStKXS53seSUGg5PsM6jqWrhMqCsGNOGYzprEFgYiu06lWqWcySJLEUFRieEecigXJVJqBpE7zapWI1/X4zeVy2JYNAsTiMdxqo6c4btsOR44c5L/5qUd54403eOSRR+6IN2/F0tLSfWf5ftiwbbtHE5VlmW9/+9sUCgX6+/u3VUT99Kc/vWWk5uf/3yf5v/7gaxw5epAnn9t9B934RxkfJno/ZGgJjbEnx5j9zixDPsSmMsyXOwwUZGwAAQAASURBVBSbNg3fQRJFclGVyXyMrBfS3HuY9yvvs3pqlZPPnbzr60qyxM987ggnnp6gUFjn2CP7EAQBTdPodDrs2rULgLVimeXlAkK7TVuQe9Qlz/O2VGQ0TUNRFarVyl0TPUGTkFwf1wr4L3/+Dd576xV+/ktf4vFik1fSJ5hJ7mbhz99E9j0kAoIgxPNBkV36pSqPnr/K0JfXqDvdGSzXdUkoCvUbVBpBwHFdxmQZX5KoJhK9hSjUNNhMsEJdJ+e4DA+eoJUQEWaahKGAGo8jxuMIsozp2OiREURVpdlpU65UCYs+sckx0oqG99RHWNRkaq0WY8kkthvi78ig7dfoG+tjdnaWw4cP33VWzrZtrPV1BsfHuVOeBSyjzF98sEYYhqjjKdz1NnpEZX5hnrHxcYQQ3JqNfiiPqEpUN0wiWOTz3RmiRCKxbbdztdLhz75yiY3zG/gtlw9i6/Qf7uOTnzzAwDbzdj8sLFU7TC9UufK9Jcxll2ZaY2wb+WkAJRPBSzSJmy5tTSLjBVSrJiUBUhEVWRLw/JC66eAFIUdH00Q1mdZ6i1h/jOTYg0ks/21EGIR0yp2u4XUIkf4I6++tk8tFODaa5t35Cht1q2s6fbv6ZNg1mq2bDpP9EZZWTpOI9+EqGrHxBIEkkhlMcPj4CJfeXCDI6L0OmaZp2JaFEYnQ7LiEtQ4//YtPI/W1ee2118jlciwtLRGGIdVqlcnJScobZeavlAgSOoYq0bIDJFkiGo3Q6rSJxWIkMhE2luq88/JZFlszJBIJnn/+eRRFwTTNbvdqfp7oJz+JtLoKjoP13/13uMPDiI4Dto3gOAi23f1xHLjxf9clDALivo+iKNw6+Wm7LhOxGDZg3rrO3LLe+JKEEIZEcjkcQSAaBKTVFEVXwTc0NEOm6XjYvorYdBHyGtFDo8iZJMgSsigROAF2sYNb7IALbDKZLMdGlWWSrodsB8RVmYGhBLGhGP3DSQxDJvfZQxRrRXK5HKqq8iDukh/7O0f5o//lFZZOLROpecRMDzui8N5cFdv1UcSAvCggtMpoEY12uYO/1kR2fNRUhI7nY7ddRMdHESCs2wRJjYCQIABRhKgqEdcVWuU2V86towzHsfM6QsNCzkdI9EW7qqJewJoA7eU6U5bHjr7Ylm7KDTQbNm9+Y5raapPAkAmyOmpCxe84VC9sYNVNRo8OIisSpHT8UofA8nrfzYQmU3A8FEXCcj1MQyBquvgtB3lTRZkQyi2b/oRObnPd8iwPURbvO+P8Ie6N9fV1hoeHqdfrdDodnnjiid7fZmZmeP/99zly5Mh9KYaDQwlcL4umKfzET/wEExMTSJLEgYOHmFtt8O65FYrFOWJxmcnJBF67wsGDB/mPb8yiFU2kqLqloy9JIr7nI6lSVyNho8X0aoV3v/tVTp8+zerqKl/4whe4fv06nucR/+IX6f8f/gdE0wRRZP1XfqW7vtj2zTXlxnrjOIiOg+C6CL6P63mMiCK1W5SywzDE8X32RSLUJAk5Gt2yvnBLwXgwnUY0DArz88T0GLG+SephhpTlIagCgQxhXKfapzG2wyPx0PPEhoYwg4B4Po8UibBSKtE3edPyyDRNyuUyjuOQyWTuoKjeirm5OSZvee6tONhZ4upGEwB1LEl7tUVuKMvi4iIDA4Pohk5geSAJyJtF2pZpcTjf9c87duwYZ86c4dFHH0xo5a8LGxsbveSyVqsRj8dZXFxkfmGBgcEJ+hwf7ZZC0R0iQTmdtnMFN4wTMQ7+te77XxYfJnp/Bcju6lamF19bRC92OBLX8PpivPz6a4wNDbMnPYZV7BAmNJ7/9eeZ/+o8o2OjrKysMDk5ua3MPnSTvdGpDKHc6i2imqaxb9++3jaJRJJ4LEFo1PBFF103aDSalMtlUqkUEb07HxKLd6tW0ViMVqt1h4UAdBcOXdVwg4BWuw1A2zDI+Ov83Np3WItucObFf8RaAxxfRI2o9PVH2X8wz+RkDjkaRYxEQFXZKBaJRqNb3qdYLLK4uMjx48ep1Wp0Oh2GtpnvqtVqDPo+T9RCvn5qicTFEnOXphk6NImRiNNqtogoCpquUatW8VQVe7lbbVpdKRN98gDn3ADBczFNj0rYYTCuY11aI2lFaPvt3tD03bC0tHQHFexW7MzHSEYUah2X5HgCcbWOfWWNlGLQuF4gohkow3H0XVk6tofvWvTLTk9VdHBwkCtXrpBMJjGMbrDieAF/9meX2HhtkWxfDHksitt0WH91ia8g8Gufe+ivpLN3arrId796ldalAuGlEjVZRNqVwUka27+fJCD3x/Cny93IEIE9qkzBkCm1bIIwRBQE0obCrv44E7kInu1h1S0Gjw0i/5hJFf91wO241BZqFC8Xe/5uYdilxjVXm3TKHYYeGuKRqSznlqsUWxYCAoYiIQjgBiGm7RHRZA4MJjgwnOLdwhQLlxd44nNP8MLnXui9l+/4RDWJUy/Ps+b4ZLIRVFnCdj1a5Q7eRouxnQmOPjmFntTp6+vrMQ9s2yYIAlqtFitLK9QqddzAh+DWuSixR0mWJZGW43L29PuY0Rp9fX1cuXKF8fFx2u12t3ulqDTjw5gCeIFLmD6EeuwxIvko8dEUYkQHXQdN6/7c+F1RmJ2bu+M6XV1dJZlMokejFDapO9thYWFhS8Vfa7cZ+LffIozZ1NsutYaJi4Lr+uh9UcZPjKLHFBqtNoai0mw2MAwDdSIN/RG0QMI3XdrVJjo6nu8ST2URBQE0iUPHR4hoEr7r01huYNUtHMf5vsQJUqmA558cZ+aVRfxOh3h/gmUxxE6qpHQZ23G6PpphiF8yUZEpz5cRw64hdFyUuiqtooCvSUiWBxEFUZWIGSKaLCFLIrbr45geLdMjc2KQgf442y2XQRBSN13OLlbZaNgcGU2Rimyt7H/w7gq1xTpGxqDt+sgtBzUXQYiI+LaPudSknInQtyPTXVv6YtjXyoTxbvMiYSiYnk/DdAmBjhcQDSHc9Gp1vYBS2yauKxwZSfWod+1Cm8Rwgtjgg3VsPsT2mJiYYHx8nIsXL3Lw4NaAd8eOHT17hJMnT973u3xDvETXdVKpFC4S/+mLF5h7Z5mgbiMAgSRwLVfmwJNDJOt1FpZW6G+ouFGD0HFRNumM0WiMRqNBIplAkEVEN2S9UOTsm29imiaO49BqtXod8TAaJYzFwPPA91FeeAExFkPQdUTDQIxEEA0DKRJBjESQNn8sz2NjYYG9e2/Sz8Mw5OLFixw4cABBELh+/fq2KpdhGFK9fJnRTYP0YeCVV15hb9WkLg2zdrmEs97AiEcJcgrHDmb5zJP7aFTLLJRKvPn22xw5coSTJ08iVKtbXndlZQVN69pB3SvJq9frW+imt+PAcJJLaw1cP0AZTaAs16leWSefTdMoVJCNFKHpou3NIucjrBQrpKI6+4a7DYNIJMLY2BhXr169K0upZbpYroeu/PXd+y3X7/0+MDDAZz/7WS5dusaf/J9nEVpt0gNLvPhLR9gxub2YzKuvvtoTxHn66afvS0/9UcKHEdZfEbK7skRyEapzVVbObbB0vczybIe1pXmEJzLsf3iUnUf6ieajfH7s80xMTHDlyhX+4i/+gueff34rvfEuqFQqpNNpBEHA8wNOvbvMhfPrzF4vE12pMbInh2boZLIZ2q0W0UgUyzRptVqEhIiiSCwaZaNQ2DbRcywbWVORFZnP/NQnif/i3+HcL/wC87rO7nffZbhSYfg3P3vf/XQcB9u2t9CpTNPk8uXLPfGVVCrVVcnbJtErlUrs3LmT/RGXCyMNzq+sMzIyiFh0WC8vE8+n0HSNeq2OEapEXZHU4QNsFJpokk/T8xlMGiiySAOHaNxgtWYShhbD1zvsfmb3PW9IrutumVvbDtmYxoHBBG/OlCEi4O6OMDixH3etRa1SJRhOYkz2Y1dNZpcrjCYkhEKdbOYmXW3Pnj188MEHHD58GEEQmF1rsPFBgUw+hrpJN9IMmSwh6+cLzK032DOSuu/x/35Qatm8/J3rCFfKJASfIK5j1i3c+TqFjMFI//aLmzIQJWjayAs1OqpERpI4uKefQsPk7PsfcOLYUXKbXSfP8qjN1eg71Ef/A9Lx/jahPF1m6Y0l2sU2zTCkKguYYddSQZcE4lmd5uUy9YU6Qw8P8dyuPIWWw0KlQ63jUK/XyWVS7B+IM5QyuoFx2WQgOUDqxRT7nt1HqVQil+sqIkqqxDOfOYCmy5w/vULlegWf7vXfl0uQ3xvnp3/lMfRkl95yK7381lk413IpDQgsTRepNxs4drenpigKuq5jmiamJxI1dH7pcz+PmAu5fv06w8PD9Pf349ke1ZkqG5dcFj7zLymt13AuXAB5AvW6RqYoM+5qDBzMkx5K3zFrdasdyw3UajVUVe2tp6lUqqeqeSt830cUxS1diI3VDXYOT5CL1FgqVlDEAS6utQhDhx27B3q+dolkgka9wfzCPIlEsns8RBDjGq4OWjyJ63mkkt19S4awVjOptG0iWqTrm+oGLE8vM/nw9lX27bBwfYHy6TLyisvuHQPMVedw0xrNcodMVMWyTHTdwMZC1FXEmNoV3JkGwfMQGzahKKL4Ia4moSgShiAgiQJy/CYd2Go7tOoWblQGRUKLKtsmedC1/EhHVeK6wlrdxHR9HpnMkI5250FLK03WLhfRRAHBDfFUEbnjIlgBGCKSJuGrIo3FOtmxFJIiIucMvPWu6Iqc0hAE6IvpSIJAue1QNx0MHzo1Ez8qIwgC+ZjO0dFUz1DdaTv4rk//kX7EbbqMH+L7Q8PyKHU86qZL8rbrMJvNcvz4cU6dOsXBgwfvmXSk0+muZ2V/P/VGk3fOtpn+9izZtI6+J4sgiQSmR2OlwYWX1hj5u0fJJaMomQhBrUPJLxKJRrEdB8PYnAsPQkLHJ4hI7Nkxwa/8T/8TL730ErOzsxw7dqzHcmL/fvhv/1s4fRr+3b8j/0u/BHcpst+Kqxcv9jz4bmB6eppdu3bdtGa4i6H30tLSlkKT4zjkcjlOnpzk1dffJHNYZezTT2C22yQkhxP7u7P7el8fV69eZXFxkXK5zI4de1habDI0FCDL3Rm+G/TD+3nEVSqVu3bzAKZyUYZSBsuVDuPZCO1JhWxmEn+piRgK1Owmgw/vQJ1KEQgCq9UOB/qj7B2/yXUaGhri3LlzPY/kW3H6zArf/E+XSPWt8Av/r+PkMn+1HXbb8/nil88zd7HA6K46P/uz+0lt6mHUKhphQSOW0ylPV3j1G9Ps+Pz2/n431E5HR0d/7AzWP0z0/gphpA2qApyttvhgbpFqNoIAvGJWWbbzmE2LR3KR3kW3b98+0uk03/jGN3jiiSfuoPHdQCQSodPpUK1WewIs33lplne+fAnJ9hFqFs3lBhVFYfBYBE3TqJTKCKLQbbuHAdFYjCAIMDsdXMelXCoTiRgYugGigOu4KJ6AIwvEkzoJQ0EShZ71AQ8g8XsDy8vLWxaWIAg4deoUjz766H0vmHa73QvSkobCR3aluXgpoHN8jNhKDX3Fw16oY4VVNE1HSik440mqYkiu4xMORjG9AOWWTpQkCoSuyYYAO/QoYfveKmyLi4sPNHfwzO4+ljfKXFpvMtafQYppqBMp9Hqe5bPXaV6pYtdcEoLHYN5gecll9burOPscEiMJ9KTO7t27uXbtGnv27KFRswhND+W2hVCJq7DapFG34Yds5bJYatO+XqU/bdByAgRFIkioaG2PRrFDkN++ko8koO5IIToewUKd1nqLwHRpl1Y4/co3eOTgDgJLpFpoE3oh/Uf6GX9q/K5S+39bUbhYYP7leaodl/kwoNR2cAMfZTMA8YIAAYHE7hSZxSaLry7SWm0x/MgwwzuyBH7Af/6T1zkw9SSD/XHMikl5sYESVTj804cZfGgQQRSoVqtb5iQUQ+HJzxzg6NMTzF8p0m45NFt19h4eQcvIxPtuFoJmZ8pcu1AAyebJZ29WrRPpBBOHsly5XKTthAzlcr21xLZs6s0mZi3k8HiG3GQaJaJg2zaDg4PYDZvZ781x4d0VVjsONVUkTBpITz+KAHiBz3zbYubUIkPn1jlwYogdH5nqWSK4rotlWVuCHMuy7vB1SiQSLC4u3hF8rqysMHKLt1aj0SCiRag6VURFZPfeYdqmxWVXZDDe10vybqDZaqKqGuVymegmFTsMAmzLJiQkmbhZQRe62gu4/i1dT0HAbJnbzopsh5lrM7Q/aGMumiQnkxQvFonFElwq15AlDc/38UOwHQdZvhmIC4aMn9Sg5RD4AWLHRXAC5M3vVqhIhE2HQJGAEN8NaPo+blpHzUWwSh18/97rJYAsCQwkDdbrJqevl3goYWCttVm+VsSdq6FKAn7HRZQFpCBErFsERvc7JkVUvJaL1XaIpnQETUIZTxBcLhN0XMRIN9HMxzVkEdyWi27auIUa40MJRsfSDOajSJtUZt/1aSw2GDw2SGbHvaXfP8S9UTdd3rxe4vJClcpGm7Pr0+yZSPP4jizpW8TkdF3n8ccf5+zZs+Tz+btqDyiKguu6LC4uEqgp5k5Nk0lrGAM31xvRkElOpXGvljj95iJDSYniYASpbhPTosSTSQhCTMvE83zWZpaQQhVloJ/JXBRRFOnr6+OFF17Ydh84caI7p/cAuNH1v7Xou7y8TC6XQ9NuFkfuVlBqtVpbEr0b93pZlpkcH6XT6WBuXGM0l2P37t1bnlupVDh58iS1WoP/5b//JnYxpLTs8eRHRwiCAMdx7sk6uvH+2xX1b4WuSLywr58vnV7g/ZkVjkwNIU1IhPvzxNwAzelQtUyyAlyY32D3cI7j/dwhFHP48OFt5/WuXtjAXGxhV1zmZip/5Yne+ctFPvj6NTKqzszSHKeGEvzEs93j5PkBYRCiRhQEScS1vbu+zuDgIO12e9tO7Y86Pkz0/grRtj3+4oNV1s8X2NkQCRLDiJKIVBNx3ivwXdsnFVHYO3BzGmNgYICPf/zjfPe732XXrl1baJk3kE6nOXv2bK+q1LJczr8yhy6KpPdl8Osm63Wb1lwN93BfdzH1XAA6pomxWdESRZFoLIZhGBRLJVRVo9VuExLSbDRIdFSsiQQHh+N3mF8/KAqFAvl8fktCd/r0aQ4dOrRlYYRuEHY7rWBtbW3LhbU+c4nfeP4QF0oe7ysW+tgEVJpYjTZKxEAaSLFeXWWw5ZATPQqBjnxrZiJ0k8cwCJB1HV037im37fvddv/d6LRbPuvaMj+xL8eukT7Or9SZKbZgvQ0XCtCwsbBJDSYY7ctRqFUQB9JUF2q888WLmAJMPD3O8587QjKZZH19nURKR4gouE0bzbh5qToNGwyFZOruQeENmp8gCd9XBTsIgTDEdh30WARHsRDtkICQMLi3dLKgSLijcTKSSGo0RbvY5r1vv4fRMTjzrTM89tRjpCfT5PbmSE2k7giW/7ajNl9j4dUFSrbLRcvBdH3SUQVN3nqdeH5IrePQGI6yYzCGud5m7cwaQkzh/dNzVBdUvnH1PQ48NMX+h0cYeWyEzI4MsVuCp3Q6jWEYXL9+vUcXFwSBeH+MQ5sG5EtLS3iezWj/zS57pdzmK79/lupSHUmXyOUznHiku30ymSQ5WmPfwX5OnV5gQ9JIxVVEScKTVJyOzJAhcuiJYdZK3XnWtbU1EnqCxZdXOH1qiSVFQEmo5AxlS3EGuibpDdPlsu3RfGMR1/bZ94ndaAmNpaWlO4pJt9u43A1hGN5hxFupVBjKDXGleQUtrpFOp2l31iAE3/foyqvcxPDwMMPDwwS+j+8H2I5Ns9llTiTiiZ76bu89geCWqn+r1WIgvX1h71YEQcDs7CxKRaE51yQ1lSIMQsyiiaOJtOoSTsek4AdIskwQuMR0lbgOEXVTAVMSIaYSWC6iCGFGR/BDQtvFD0DwAyQ/QEzrNEIwRTCiSnedFIVeAnU/CAJk3ZDCuVXOyRLJmMaG69MWwRG7wYfv+ohuiLjSBE0iSOub4hJb12Q5GyGY8HDmaoRBiBRTCVoOwkqLhB9ApYZY90n1ZbAbDpWRBJmdGQI3oL5QJ7c3x8hjI3dVaf0Q90fb9vjK2WWuv7NMYr1N3glwlSZvXytRfNTi0ydGiOs3rwtRFDlx4gTT09OcO3eux1S5HZZlEY1Gmb5ex6uaGLvvVN8VRIF4PkrlWpndL0QJElEqpQ6RhQoe3W61JqrIbojvSph7ckwOqbQKy7x+foNEIvF906K32892u70laa1Wq4RheEfXKpfLMTMzsyXRW1tb21K8b7fbqKqKoijMzMywY8cOgiDg7bffZnV1lZGRkZvdx83tp6enCQMRyT6EVWlQLnbVIwVBuCtN8lYUi8V7dvNuIKv6nOwXuaT3M1NsY6gyqYiCJAuEQoSNSofl2VVGkzqfPjFGq7h6x2sIQrcxcPu83r7DA6xNl4mmDaZ23nmuf9jwPB98UDSZ0HLZWC9QqaTIZDIcPDLAxQN5ijMVjFyEh5+7d6L844ofONE7f/78tjK1t+KLX/win/nMZ37Qt/ixx3Shxcp8ldxKCylhII12A/PQ9RFXWnQiEu+PJtnTH9+yAEajUT7xiU/w+uuvUyqVePzxx7ckGr7v43leL1Hq2B6u5RGJdRfZlKGynjNw2w5u3UbJRpAkCd/zCYPwDgqiKEkIdGORWDxG4PuElgduSCcBiaDVU/S8F7f7driui2maW6rsV69eJZ/Pb2uqOTg4yPT0NMlkEtcPuLZa5fSSyanyAlFNpt+AWrPNY4+NINqXeebFhzg7s85cQSSaGCdwLbzqOvHKKu3yGtemPfLR4zTkCOlI18DY8zwsy8JDYSjVFQtQondXh1pYWLhrNfJWTE9P09fXRzKZZAw4MZHh/Lk1Fj8o4ScjNIYT1C2X2cUVOoJCpxMiiXHqFwuYa02UEEp/colYWueZzx7hypUrDI6MMnCoj7XXFsiG3U6e07SplE2Gnh5ncuBOGmXgBVSuVyhdKWFWTARJILMzQ25Pjkju/pWz4YyBPp6i8e4C/XsGIa0TXWlQVEXiMZlGvTsXYBgRNH1rAuIHIWHY9Vo6+NmDrJZXabzfoGk2qeQq9D/bz9j+sR872sNfB8IgZO3sGtWGzSXXxfEDBpLbJ/KyJJCLazRNkTk34MiBPJEAPlipU2g4aANJAkHgeqnJQN7gxBPbz6TdsEiYn59nYGBgS1ABXXr17dd7rWbRqpokBuM01lvUylbvb6IoIkdlnv/sISy7RXHVpVZoAwKKEHJwJM34oQiHn9uLIAqsrq5y9MhRzn/5Au+8usCSJpPUFTIRfdvihCKJZGMatiaz1LAQTq+gRRQyj6TvKCbdS/1P0zRsuytI1Sl1uPb2NdyiTyGsEM9HUPplMlMZWu0WoRDSqDdYr6wzNj5OKtktlt3KNLgVoiQhShK1Wg1ZlkkmE3dNLuRbPqNjO0Ri974+HcfpCiIkBrj63atE8hFkTcZu2Diux0LDotRyUEUBAh9d1AhEkZbt0bK9np+YHJVxKhaKKhM2OpCOEKoSnqvgySKGFyCPJHCjCo2aiSZLCAi4HRdJk9DvIsp0O7xCB/d6BdHxuOYHxGUIBfBlkXCzku4pIr4YEnEChPUWIgKOEBKqIl7oEARq934lgDqS6FbdF+s4C3X8poPneOhZnUbVJTACgmiAIAgULxSpL9ZJTaQYODrA6BOj97Qv+hD3x5X1BjPn1sktNpGjClLGQG25aEtNZvU1Lg8neXib+aZdu3ZRLBZ56623OHny5B1qjBsbGzz11FO8f3EaEbr+nttAViWCtkMqnmA0JvOOn6VsSPhFG63QBgHMiELn8CATBzL8/JN7iWkypmmyZ88e1tfXcTaF4SKRyBZFzwfB1atXt8S8tm1TKBS2TbC2u8dVq1X2b87mAVy/fp1Dhw6xuLjI4OAggiBw7do1nnrqKWzb5uWXXyaZTPaSQ0VRyGazPP/884R+mvdOzTA8qeN53h2zktvBNM3e/P+9UCqVsG2bZx7ay0OWy/VCi3NLNcpthyAIkSSBh3YM4hVmeWTfEH1xnVbxxjHx+OZXLjMwmuThx8YwDIOxsTGuXLnSm2k89tAQe/f1IcsC6l38CX+YGOqXmXpqjPL1OoO7h/mJjx1Bllzm5+cB+PhnJnHsvaTSBv19fzPnd3/gRO/EiRP8zu/8Dl/4whfuSBwqlQqf//zn+eIXv9jriPxNhGu6FK9XaDVsUlmDzGR6i7DEfLmFVLXB9pFuqaYLioQYV4mXLZZKbeqmSyqy9SYkSRLPPPMMFy9e5Gtf+xrPP/98LxBbWlraMsuWiWmkhxMUPthAi+mogOoH2ANxxLpDmNTRdB3btu/6WVLpNNValWwuR7PRwugIlFM6R45P8NjBUWRJpFarsbCw0P0MgkAul7sjOLwVt1fZC4UC7Xb7rpUncVPSvWV7fP38Gu9cXSYuaGheCUcSqIcWOwdHeOu9izxyZF938Lq9zEcn+wAbUZTIHjrMW2+1mXbbPHPsGcpzbc4HHmt1k6ShUChXUaMp0skog4FAbDhGYmR7fTvP81mt28xNl9lodAPagaTB7v4YwykDQRAIgoArV64wOTm5ZRHVBYHo9RqjUZWltMj6WgMh8Ng3PkijUWfXUB9uAEvXl2j5AQMjCdy5Gmtn1+GzR3rzej/7s/v4iiCw/sEGrDcRDIWhZ8b52Z/dd4eiXeAFLLy2wPr764SiQGDICHZI561lytfK7Pjojrt+1hsYSOicfHaCb69V2FhtIloeviwijMRI5xOk4hqEYJodapvD4JqmoesGpaZN0gkY2BkjPhLn4K6DfL7v83ztK1/jM7/4GfqH+z9M8u6C5lqT+nKdsgLNpstw6t5BfxiExHQZy3NYDgL8ksXKQglvUmHfoe71VVqocfnsGh/5qb3IdwmeRFFkamqKlZUVTNPsWRwEQdCjVd2KbF4lOxGhumiSm0qz7/CdM5au4vLx//ph/GrA8kIV3/FJ56IM7MxQaVfwfA9F7L62U3EozpvUkjEGkzoRWcS0THw/6O2fYegIt9xjNEUiHddZxuLa+2vs6gvof+zmfqyurt4ziMvn86yurqK3dC5+e4bzF5ewIlF8AWIhGL7Fw8/uxtin0zfaxxvfeYPl+jLnzp1DGTlAYKTRNJlGvUE8HtuybwCu0y1wDQ0PIW6zD5bro0giMa37t2a9SSQaQbuHTUa73aZYLLJz506W31nGbtpkh7rnyvcDliodiraHKgnoIoiqThB6KPKmrU8Y0jC75zKSNXAqFgQhAuB3HATVAAFCUegKKoXQcjzCkB6bI2g7xKbSaA+QMPk1G3umQsfxqSkibScgBWSyEQpVE3u9jSSLaAh0hAA7DJCCELnQRlBFkicGSSTjmKZJsMkkMAwDZSiGFTqYi2UEM0SRReyKiSMYOIrG7GyDkQyoIth1m9zuHFMvTH3Yyfsh4Mp6E3Wje95uqJtKaYnA9tA3OlxZb2yb6AGbxtwx3nnnHQ4dOtQrIJVKJdLpNEEQkMoYBLKI33GRItt4D9dttKTOyGCeZmmFn9jXT+PgGOdmyjQrJkgCw/0xjoyn0a0yMU3u0Sc1TdtCmex0OpuMhS5VL5FI3FEsuhWLi4sMDw/3Yt0wDJmenu7NbW0HVVV7XcTbZ9VuGLcXi0Xi8Ti6rvPWW2/R19fXS0COHz/O2toa5XKZY8eOoes6YRj2Yr9me4EgcNi//6G77sOtWF9fv283b3V1FUVRGB4e7h4XXeHYWJrDw0latocXhN21SxVZWYFms8n8/DyJRDeuqFZMlq+XqZXaPPxY93gPDQ1RKpW2eKVG9L8+MmHoWvz6rz68ae0kb3oAG7197nQ6FAoFzE6blZU6AwMDW+4dnuc9kH/hjzJ+4L3/lV/5FX7rt36Lr3zlK/zH//gfe8H7l7/8ZT7/+c/TbDb5t//23/6w9vNHDlbd4vX/fIFz763heAG6KnHysVEe+dR+ri9cZ2FhATd/ANEPtnU0FmQB0e1aEvj3oA4eOHCATCbDX3zt6zzz9FOoqkoymaTT6fS2EQXYfSyK2U6xsdANvocODuDtSFK/XCa90kSLKLTarS6NaBuoqorjugSej7/eopVPET82yHN7+3qV51Qq1aMihGFIuVymWOyWciRJIp/P97qMxWJXJjwMYbbU4vxiiXMXr3Ps8F5iG02mctEtFe0bkGSZr51b5vxCjdx8nURT7KqpqSLpjMqZwiyVkSSqcIFqqcATTzxxR2X94x//OIcPH6a11iLTCtlf7rAcl7m+XsAXZCbyKcZEiWxUZejE0LYUQtPx+aNXz7Nuy7h+ichmUHZto8U7c2UODCV4ZkeG5YXZHsf+VtSX6jTXm3RSOlcXKiQjKp7lYkQMVFWlWCzS19dP/0QKu2JSXKgzEFdJqN0KvZbQ2L17NwsLC/z6544yt96kXrdIJnUmB+LbypaXp8usnV3DT+t8UGxRr7aRRZHJXISRus3Cqwvs/8z+e87FCYLA3hxk/t7DzM63aDcs4ustatcqLDs+5ZZDKqJgRCI9CnCj1WF5tUTE8RmM6ZTlDb70lS/x4tMvUnithH1OYCW/hv6sTmo8ddf3/tuMyvUKnY7Lsut16U+3LBmhH+DXbULTw2/ZBC23p2qpyyIFVaRRLNGaa+Ad1gl8H1GSkDUJ13LxvOCuid4NDA8PU6lUWF5eZmRkhKWlJcbHx1lZWelt02q1qNeq/L3f/AirK3XSmQjx+J3JSbPZ7AZVachMbbVuGYgPsLy8TCwWI5PJUDlbYa3cxjckIpvfS+OW4lEQBHRMs0evliQRQzcwVImaLDKzWuZgqxtQVMpt3ju9gGO77Ngts2OXgbTNdSKKIs3VJtfeXOKdmQ3a6RjJqIYiClRtn7WmT/FL7/LkT+zC8i0MySAei3Pg4AH0zBCvXy+CIJFIJGg2G2i6fpMSFoRUqhXi8dhdg4O66TKY1MlsJkyNcoNEJtETu7kd1WqVTqfDxMQEvutTvlpGS9087qWOS6XjEovIdDo+gih0AxU/7AUpkiBgqDJNyyMSU5EiKsFGG1GVESyPYLMg21WoDEEE0w56lh1ex0WQRZJDD6A0F4K71sQ2XcqSACEYioizOduXHEpQ7ni4xQ6S56PoEn4YYlsegRCgGRGyo0kEUSASjfRe0zQ7+GbAxso6vtdGHkwRk3Q6G22k/gSRnEFDlllNKjx8YADVD/EsjzAIP0z0fgAUi0U6nU6P0eJ4PqIX3tFxE2QR0QuwvXtT+w3D4PHHH+fd06cZGhxkZGSERqPB2NgYjUaD/fvyvDmWoLbYILMzs+Wc+aZHs2Gz56kxdo31851r5xkaGuLAWJbjY2natocgQFzvagkUCi6tVovFxcVtmWeRSGRLx7/RaDA3N9cTUclkMqQ3bads2952tu5W8ZXtMDg4yMrKChMTEyyvruEnhnjt9FK38FJZYv9wio2NDfL5PGfOnOHQoUN3+H/m83mKxSKvvPIKJ0+e7M3XVatV1tfXef755+8pEncDt3rI3Q3z8/Ok0+ltWVuyJG5pRtxIfN0AvvoXX+ejz3VF9QYG4/Tv8gjCwpbnHzp06IGVWH+YuEHJVySRXGz7Qtqt3wXHcVhdXe2Jcw0MDPClL32JI0eObOnG/rjhB070/v2///d8+tOf5jd+4zd46KGH+Bf/4l9w/vx5/uiP/ojHH3+cP/iDP/ixHFp8UFx5a4nTp5bxBmPEDYVa0+bt1xZYqswyXT2PpmlMPDaEHZERhC5dU7gloQjqDuZAlHRM28JrvxV+EPLBXJn3zhaplAb5t//7K+zeneBXP/0xlpeX8X2fMAyZm5vjuacOc/yYw8xsBUEQmJpMU7Y8/iw2T/X0GsaqSc2tk8/mt32vMAyJobHx/jziaB7lSD8vnBzZYjZ767ZL02Uq5Q5jk1kyAzF836dYLGLbNp7nUS6XOXzkKN++tM6pt5conJmhX09w9sol3htLcuSRYX7y0NCdcv3RLGdOXyO5UEeZb9OMiwhxmdBycC4W2b+7n4aaZKXl8rMf/ei2C60sy4yNjdHJdbjcuky/GsOfXsVtdIjEYgx4kB6IMPXkWM8K41Y4XsDXzq/xwWqb/WP96DUbb7kFQH/WwNRlTi9UWV1d59c/ehR5G/pB6UoJURJZaliEIQie3et+SrJEOp2mWCqSH8kzoUhcvLxIfEeMRFKjNl+j/3A/hmGQSqUoFTbYPTwIw3enzYZBSPFyEWSRD4otiq2ut5rjBlxaaxAdTROuN6kv1e8rSGCZJsd27uTYVPem47Qd5r43xwdvLrHUcijYLqEoIogCfhCgBjAhGewcjTL16ABffvfLtJtt/t3X/39EkruZQUc/v0Hghxz5hYMfUqhuQxiG1BfrdGSRTttjYPOaC2wfv9zBXW8TNB0IQwRJgE0LBQDR9AjWLULHJ2EJtGYCapky0f4UrWKHA89MoD9g9TSTyWAYBufPnyefz28JIG7Yn9wIdsbG7+K9KQj3ZHHceM1Go8FgdpClCxuUCO+6BoqiuKWQ43s+rU67OyvXMakqIgsXN5htWpx+fYHGRh1d0zitLzC6L8dP/9IRstk7KZb1mTrTCzUqisBwRCTwumyHiACO6OAODVBesBh7KIs+pTMeH6d/qB/PD0kbKnXLJRtViScSmJtKxrFYjGK5RCIex7/LPGsQhARByFgmiiAItFttJEeiEjj8lz85T2m1STShsvfYMCceHaVe767nNyrsdt3GbtpbTL9X2zYoIpFAQAgDQqF7fUmSjOe5+L6HJHWTPQHouAHx4RitjSaBt+k/ZnuEitwNCATwBQEv9JAFCc/08Js28V1p4g9A/w7aLn7FoiOLuJ5PVFNwPB/bCwhD0CIK2Z0Z6qpEsFhHanvgg++HuMMR+jIGouXDradNuFkACCoWNhK23UKNqERGE8QP5/AJkcyQQrnD4nyVkZROfalO38E+8vu3v+99iJtotWxOv7VEti9KJNbhm9/8JplMppf8REMLK6MRn28SpgMEWST0AoKWgzUWZ/w+ohqLpTanzq6wsijQfucaieQH/OrPvUBUVzfVKFPsOpHiXM2kcLlIIhdFViXMpk2zaZM+2Mezz02hqiqdTqdXVFZlEVXeek/p6+vj8uXL6Lr+QCySRCLR6/CEYUilUmFmZoYwDLl27doWr8C5+UXESIqWC4oS9uw7boeiKHieR7PZ5N1Vm8tvX0AumAQC2BmJUNH5zNMPMzMzwyOPPHJXuf58Ps8TTzzBO++8w+joKCMjI7z77rucOHHigRkya2trjI2NMVtsMV1o0bY9+hMaewYSZKMq169fZ2Rk5IGonZ7nIYoi52arvPbyLLWqzkL5XX7j5x8nFY8gKS2uXLzIwsKRXpHghr/e2bNnfyB/veWlOqbpsmt37vt63nbexPeCqqo98S7f95mfn+fq1avMzMwwN1fi/Gs1+ocKfPpXj5FIPJhw1o8C/lL9yI997GNcvHiRj33sY/zTf/pPAfit3/ot/uW//Jd/oylaYRCyfKWEpYkMbVZhtXSE1XIHdaHJoScOsbq6yiN7RllsrdMpWxgrTcS4hiALBHWHICJjDkV5djR1Vy+0l84s8+aXLiGWTVRJRPMkzi6XEcSX+DsvnGR9fR3TNNm5syvBm4rrHD9yk9IZj4Y8emiQl/yAVRns8yXc5TqCriAacrcVGISEtk/QcRFUlYWkwJEnxnn+5AgHh7ZPLC68s8w3/ugDOk2H/Gicn/9vHiY3lOhdULOzsxw9epRXLszz3e8sEp6eZzgZJxnVCGwP51KJsx2HvoTOYzu6F+6NRPHdyytsLG2gXatjZNOk+1IgCpimiaaqKI2A+mIBd/je1TToVmoOP3OUb7x6hjfmamQOTLJR77ASl4j0qTQCj73VTo+GeQPThSZvX1th72AG8XyR1mxXAEAAkAS0qRRjB/tYbQnMFNvsG9zaJfUdn/ZGmzCiUFirkzAUPKu9pVOhqCrxWJxKrUJmMENYgMvVDQ4N52lttOinm2QNDAxw9epVUqnUPRdhz/YwKyaeoVCrtsnHdBS564PVdnwqlksqBLuxPX03DEOmCy1KTRM6AbeWaNSoyo4XdhAbiLF2foOl+Roty8MPAxRRIJs0GNudpf9QP8mpJBPFCRJ6gkKnzrQj4CsSZVWgXGzjtJwPE73bEHgBgRfgCzckKEK8komz0CBoWAiajJTWEO4iqiMqIikjST7eodi0sc+UcfpMhh8Z4SOfuP+APsBbr8/zwZuLeK5PZlTh2CN6L/AplUoEQbCt9cntiMVirK2t3XObRCLB8vIytmJTKXcwVYnUA3opSrJELBYjDAKCMKAmw7l3Fyi+5uNGQ0b3DqJHdMymzezZNb4SwK/8/UeRNtdY13VZW1xj9WqF6VqFeF8cTVWRNrtvzUaDob4sK1WT5bU6x35iD0JFwNtUY5Mlgal8jNMLFSzXR1ckDMPA93yWl5eJbna6W83WnTsfwkazW4C5cd9oNVuszDaYa1h4uowaV6mtNlk+X+Tie/N88pcPk8/fLEbZTRvP8pA3k3fHDSh0XIzBGMzXiBkKbSfodfxluSvEJWx2eRVJouN6ZJMR/GwEq2TitT1CTUaQPFRPQjQUAkPGb1lgOghhQGxHmsG9fQ/UGfPKJp7l0pKFnqCOIAgEYYgfhsiCgBZR6Nubw0rq2EsNAj+gacg0UjpWCHqhjZy9bb0LoeP66LEMfVGBgXyMufkqxlAEd72FVzSRvBDJ9qnWbCIpg06pw5WvXKGx3CCzK0NyLPmhvcJd8O4bi3zvjz5ATcn46csg2Oi63ut2aGmT5SrUGw6J1SYCAmEY0kzpxPZkOXCPQuT19Qb/5Q/PYV4to4kCoR+ypAT8rv0K/+DvPgt0zdY/+uRuxieGefeNRQpXivgtDy2ts//ZCZ5+ZpLBzWTSNM37+pitrKxsSdDuhiAIuHalxNBIgkSimxhms1my2SzLy8scP34cy7IoFossrFd4/b0Kkqkjawp7jw/x8UfGNumAN9Fsdr2Ll5aWOHPpOvOdNIPLNlrJIQxCNlqwsXuc6dkFMqnUfT+Lqqo89dRTXL58md/7vd/jc5/7XNdWy7u7SuQNuK6LKIq8fr3M69eKuMUWSgDndJkzA1EOJhyefmjfA9MTV1ZWkBM5vvWHpwmmq0QkgdqKxB9H3ubXP/0ki4uL6Lre80e9AcMwGB8f3zKv9yDomA5f+l/fxbU8fuHvP8ro98EK+ssI8EiSRBiGHD16lEQiwdULDYpX69hFn7nrFY4cu//98EcFf6lEr91u80/+yT/h1KlTHDlyhOnpaX7/93+fxx57jBdffPGHtY8/ehBANWQEP8TzQ2RJ6EqgI3Di0eM8/qmbvO1n94d82/YwIzLxkoXoBpgDUcyhKAcP9HPkLj5ohabFme/NYVRtkrsyXT8Z20eerTA7bfPnzjcYyGf5yEc+csdzXT/g4mqDD5ZrrNRM2gLUJlIsOFn8aIS+mk207SAEEIrgSCLejiRCRuFgJMLHjqTYe5ckD2D6g3WaHYf+vVkKl4vMXSuTG7oZFGYyGSRZYbkhIs1WyfRliI1mMU2TUA4JPBdptsxrZ+eJWgVUSexRP/MDw+iskU/lUDPRTXl2B891iWcTVC4uk98/hendX+LbdHy+dXGNL7+1RD9ZrJkiaU2HZgtno8Prs3XOjCV47OAAT+zMdW0XwpALK3UC30MvOXSmq0j9EcTNwCqwPOzpKpFcBDGucGGlzt6BrWI6gRcQ+N3A3Q9CHNMksY1wg27oeL5HsVDE9z08BNbW1xhyti4gu3fv3uKvtx0EUej6gHkekiDg+JuWEmF3hkeVBEInuGugdm65zl984xrWcp34rgyDQ20mczf3WdZlhk8M03+wn8mlOnbDJvACJFUi2hclPhjvvfbP/dzPEXgBF1qXqL85Q8kLyDoh2VwU9QGFHP42QRCF7nkNu4qHzmwNd7WJIAhIucgDBdeyKNA3muDQ8SEKhRZe1WJiPI1xbzYVAGfeWeKl//QBYQCiJFCadenv6yMarTA3N8fevXvv6810A6Zp3qGmezvq9TrxeJzAC/BcH0EStmO33xPNVpeG3vI6tIoORAV2HprEcZ1ekmXkVRYvrPP66xeZ2pECut3+TDJDLJ7Cl+vks5meimS73UbXDRAEPNdG0buJRv5AnrnvznWveVlkMhelbrpc22iQiWnoioTnupsKeiq2dWcxJQyh0LCIazLHx9IosohpmjTXLRYWajCeZHDnzU672bRZu1Bj/nqbvs1Ez27aVK5XqC90rz9CcEXwii1UTcYRApKKRsd1e/clAEVWcF0HQRQRBfADCIQQI64hSSJWxcRxA2QT2vUWcj6Ks9ogtD3kbJTMeILMaArxAdU2/aaNI4q4ftCjvAObgl9bX0MfiCGLAs5iA1VTcFQR0wvxN1roI/FeougFIabjYSgyw/k4iZqFYvlImoy53kImBENCTBm4LQctF0GOacRkES2hUbhYoHCpQP/BfsaeHOslyh/iJtK5CPF8hE7Q5JOf+mmmpsZYXl7u/X0wafDiyRG+o0mU5qpgeoS6RHYizfOHBxlObV+IDIKQ115fwLxcpn88iRjpKrhGFurUZ3z+9NuvkxQtXnjhBVRV5aE9UQ7vyrNeM3Fdn1hEIRff2j1JJpPU6/W7agT4vk9fXx8bGxv3tUa6dqXEd//LJYZ3pPnkZ4/0Hncch1qt1hM6icUTfPnVVfzLDqrVxHE83ri6jtmp8+yhfizL6nU/E4kEY2NjRKNRXn3/KlJLQTd9pL4otWKFfBhno1jDHu3rza09CBqNBs8++yzvvfcee/fufSA7lpWVFYjleOPCIsZsjeR8g9DzEeMqRdNlbm+ep4QHK344joMsy9Q7HtZai77+KFJKx75awtCyvP/++3z+85/vdRBvx+DgIMVicYuP6/2gKTLZkQTvvXueZPrBumieH/D+1QJi6HN/Kb27Y+fOnT1m4s6pKn+4+hp9Azkmd96bFfWjhh94tfve977Hb/zGb7C6usq/+lf/in/8j/8xMzMz/Oqv/io//dM/za/92q/xb/7Nv/mxco9/UAiCwL5HRrl2qcD6Yq0r9dy0UZ0mI7u3dndOTmRI6Arvj1RZKrdxfUjHVZ4dSXF0NHVHJegGVgotrKU6/X2RXiVf1CTiSYNSySfx8CjXrrzP4cOHt1wwtufzzYvrvLdYQ5NF+uI6o5kIewZCXmsW8GIRGlMp4nENXZQQJYGBTIShTASaBU4e2MWpU6dgYviunz+ejSD5AYXZCkZEIZXuLvC+79NqtZiYmKBuuizOFFDaDokDIwiyRCze5ZeH0RjCTJnKehPvQIqkoZBOp0kkEsTKJYiquKKN3HYRxG4Alkqnqa9XMJIx6ppyh0nr7XC8gG9cXOerX3qH/W0Nw/chHqHlmiTicRTLx5itYRc7fK/lEBLy9K48bcfn6nKR0XwK52wJdKmX5AGIuoyvijgLddKPDLFSM+k4PtFbOhKiLHZtNPwASRRwHA/xLupSsVgMz3UZGh4lrUtM5bJ3zNAJgsDu3bu5evXqXSthsiaTmkphnl5jPBfhylqDtu3j+j4JQ2FAVVCRtkjs32oSfW21TnC5RL5mU3IDlh/pbEn0eu+jy9vSXW+HKItMPTdBs9nEfXOdRw70s/O5yQ+7edtAlESUiILs+bDUxCpbKClty/fubggBPwhQEJBkiWhSZ9dAjMAPqM/Xuf7N6+z46A7i95itmr5YwLN9Bvd16W3F2SpX3ltj98Ed9PX1sba29sCJnuu6d6jq3QrP85AkiXg8Tn2xjiiJhN73J9hlm1Y3mRTAc33sWofUZNcfUNO0XqIZi8H86grFtRaTU0lEUSSbzSL4ApIUYAgilucTk2Qs00KWZBRZpt5soGgGurv5fd+dpTpTpbHUIDWZQpIEDo92C2HThSaiEGI3KoyNdn34Os0OtXIDXTOQFImW5W5SPTWOj6d7Jt7VjSqNooutS/RPbKXCGnGNmiBw9YN1jhzsZ+P9DYpXiyxcLLB6fgOyXfEUwQ9p10wcGQw3RLAtkhmNmukiCHJPSEVRusIQgiiBAJIg4osCii7jJTRygzESkohj2UR2ZdBTUeY6NrF8lETk+7xmvaCb1YU3Ezs/DFFEkTuaaQLIfVH8jkvo+mTMgHxKRzNkOrKAt1kBiKoiu/ti5GUJs+1RLVkYWYVsW2LDD7A1CdcLaZRaCAis1iw2lhtEUzqKJjE8GEN0A9beW8OzPKZemPow2bsNR48PMzyW4vrMJb74xT9meHiYp556ass2ewcSjKQjzB1o07I9oqrEZC56V+o1dGdSN+ZrxHQJcVNkRRAFjIEY1cUqnpvCERoUi8UeRVkSBYZvo4I2LJeNelcUTY0mqVQqDA4Osh3m5ubYsWMHGxsbd1in3I6hkQRNr8Cug1vvrVeuXOnNZYVhyLnL1/CaEkkgfmCQMAiRpysszNWpDCs9sZRIJEI6nUYURcrlMiP5NHO+iBVX8eaKxCIRyhEBQ4Wd43ePs27HtWvXMAyDQ4cOsV5t8dWX30bRdJ44cYTxbBRFEqmbLjPFFrV2155HEsBsdgi0KuZGi8RiAymlIkZV3NUm2dU2qwMxVqomE9vc72/H6uoq4+PjCHULfSBG7XoVpdzBiSoMj2XYtyfFxYsXt1VVv4Eb83qJROKBum2SLPK5v/cw/9d/+ef87u8W+cIXvrDt+Wy325w/f55oNMpaPeDU2SbDgwmOPbjl8z0xNpHm7/zangeysfhRww+80r3wwgscPXqUP//zP+8pD+3atYvXX3+df/2v/zX//J//c77zne/0FIT+pmH4UB8/+fOHeO/1BVoNi+RUlopb5Q++/B/YdXEXH/3oR3sJ2J6BOLv7YzQsDz8IiWnyXemaNyBJIkgC4W0VecsyCWIqR48cZqovweuvv87+/ft75pqvT5c4u1BlJBMhospdDn3dRtIkJnJRkkmNiqeSTRp85njX90YQBFqtFk0jhSzL6Lp+V/lwgCc/tpNGrc7C9BpP/sQhdh7q0gwXFhZuDrXaJvVWjcF0ktAPEW69D/gBoiKTTKcYGxslF9N6ip715TUSqQiVhM1A0aZVq5EeztNYqyA1PJT9/YQJhT3bWAvcigurdb79rXNMlUOiSRV5c6YlE0ZpNLoG0nomAStNuFrhzajCZC5G0lCwbIe+TJLQ9hGUO8+ToEqEVrdzZm3SkW6FqIhE+6NY02UMXCzx3otZMpVmfanAWDTAszyifXce9xvzemtra3e9ueX35qlMV5iwPKIjKSqWx/tn3mXn5ChSVSXz0MCW137rrbd4//33sSwLO7sLNxOnGYDUd+8b94MiNhBj5yemWFIWOPrZQx8GVvdAZneG2e/NESmbWBEZ7QGPle36GIqE4QVE+6LIm36LoiSSmkpRm60x99Icuz6xCyO9fcVdVkSCW0ywAz+k02mTyWQYGRnh2rVrzM7OMjExccfgv+cFvPv2IpfPrtKuWxhJkX1Hc1tMym/FDX8oURRZd9cxIgpSxcHxgvuuidClzTuuQzyRwPUDAstGVqVtaa1BGKKqCoOD/YyNjREEAYVCAcdx6MhVckFIqd5GSeiEQYAejVCv1xEUA8NyGBxIkBhJoBgKI4+OcO0vrtEutonmu0HVQ2MpUobMmxdmUOIZlott7LUm1loLz3QoflBD7U/QvyvD/qEEO/IxEpsFKrNl4pQc9OE4tCwkeZtZY1WivVDn4p9eZvZqiYIAJQk8VUAKu6IYoQiNiIzVthgIJbSKSdz2CPsj1G0fSQJt0yNRURUabYtUVEcSBWxFwKrZ6JrCjsE4miQycGQXjuGQzWUxp8tU2s59z8kdkO70wfP9kLh2l/MrgBRTUQZjdNoOiumTdAL2aiqS2p1xD7wA2h5qTKTvqXECL6A2V2NyRwalZXNhuYblBhiaRC6qoQldv8lmXOGdhQrpospDY2lyU2mKl4rIuszkRyY/FGm5Dfl8lFTqKKfeeRPf97ctLMY0mUP3oGneDlEUECW2rDEAgeNhOTajYyPsG5liY2ODarV6h1WA6we8NVPm7GKVmukSBAGyH2G2UWD33jvVp6GrpBiNRhkaGmJtbe2etPPl5VnM8CIDQ8/3Hruh3HuDznj16lX27trFm6c+wPcCQi8gdAN8YKg/t0V9s9PpsLi4iOM4LC0tkUgkOD4xwTc2KqjJIRoC+EmBT53YeddC/+0oFAoUCgUefvRxvvX+Cu+fWqE959OurXH25QJjJ6YYnsywXreodZwuG0QUqDca6EaUQnMDu9DBaNqk8t0YQIwo4Ab4XoDr35/6YZpmb+ZxKGXw0U8f4LXvzeJ0HA4d6OeJw0MYqsTo6Cjnzp3rrfO341Z/vccee+yBPn8QBBiGwUsvvYQgCHzhC1+4YxvDMHjzzTexLIt4po89O55g6i4qsD8ovh87jh8l/MCR1z/7Z/+M3/7t376D1ysIAv/oH/0jPvGJT/Crv/qrf9n9+5GFKInseGKMsYcGcTsuakxlZiHHn/7pGp7n3dGOFwThvl2oWzHWFyM6kaJ+sUTakBENmdZGlWbLZezxCQYSOkEzxk/91E/xyiuvUC6X2X3oIT5YrpOL60RUGXeliXm+gN+0EVUZLRnQmWiRFBOc/s4M5gcFTh4d4sjx7mK4a9cuAPbu3cvly5c5fvz4tvumR1Uq9kXSGZlk2+Pi/32RttUmMZbASlhoaY1L597jicf2c2HjGpFCB2GkS+3zvRBnrU1VE5kcTRBTu9+fG4qejuMQpKN8uXYB266Tb8uUpldQYwbaoUHW+yPs608wlbu734nnB3z79FUi6x2SerSX5HVPBCSSXQGFZrNJfCiOsNSguVjj8lqDA+mQbDKO6QbE+6LYV8uQ2Roghy0HeW+OxuaMji7f2YHL7clRulxiICJT90JMx8e4i9plpW0znE8jFBdgR9dMfDvcmNdLJpPbUlZiAzGmnp9i8Y1FchttcsDluVW+9ubb/PI/+GUefvLhXiWsUqmQz+ep1+uMj4/zs3/n47w5W2F1o8musTQHh+5tw/CgEFUROS5/mOTdB6IkYlctcimDBc/HC0Lk+wShYQgt22MopqOLIvHhrcUPQRBITaaoTFdYeWeFqY9ObTufdOj4MNNnV1i5UEAQwQs9nnv+WO87ZhgGw8PDzM3NMTw83KMLBUHAn/3xOc6/Mt9NJAyF0qzNyuUydkelUbOYv1RE0ST2Hh/i0afHCcObPp6JgQQDExap9SZ10yW/jYLn7Wi2miRi3c9ZaVkk3IDMoUGWmzYJP9xCL2wUWhhJnR17up3KGypqlmVx8NmDJMJ1vvvuNNNNjWQ8gldpoEbjKI7LDlFi5/EhItnuMUiOJRl7Yoy5l+cAiOaj3QSksc7PnNxB0xV55dszWLNVQl1CiWqIXoCwXENSYN+RIfTN9d93fJbPL7Pz4Z04Q1EuzFex284W24IwCLHXm4i2zxk/ZFXpBhmpjE6QiRD6QU+CXgh9Vj2FkigQFVQSBYu4H6CMJGi4Ph3H71FjQ0HCcV3KbRFNlYlLEumoihJ0KarJ8W4Av7GxwXA6xmrN7IpJPWA+5Hs+oi4hBSGS2E22RLH7/Ii6/RoQhmG3A5jSEQYi9CF1WSYPD+G0uommFtcw0gbRvihhGLJxbgO7atModag6XY/AkZSMoiiEpkvQdImOJNAmMwSCQLFl8c5smUensqTHkxQuFsjuyZIcffCE5W8LFEXh6aef5sCBA8zMzNxXXfJ+SBoKY7vzXL5cJlqxkNIabsemMl8iuSvP1GSGdEzp0R7ffvttTpw40Yst37he4uVrRVKazIQkgiRS0WK8c3Wd/ddLPLtnK9tgdXW1l9jpuo5lWdwNnU6H733vexiGwYULF3jmmWdwXZdKpdJLOBcXFxkYGOgWEUYl5hcN3OsVfFFA2pHk+PGtSWQkEmFycpLp6WmeeeYZFhYWaC1d5NHRCK+/f5WdOyZ48ckTjG8jErUdGo0GFy5c4Nlnn+W759d5408vEyt06IuqCMkk66t1Tv/xOd48OsgjD4+xsz+OKAiEQUBFsMjmkojrDc6sN1A9j/yVAoMDcWi7uBNJ4lG1pwB8L9xOg7UKVyksfYv/72/9NjFN7n1Hstksw8PDnDt3joce2t76Qdd1JicnuXz5Mvv27bvve3c6HSYnJ8nn8/zmb/7mlr9ZlkWhUCAMQ/bs2UOz2eTnfu7nfmyTsr8K/MDR1+/8zu/c8+/79u3jrbfe+kFf/scGSkRB2bzh7tq1ixMnTnD06NH7VpHuh1RE5Zmf3MPXK23Wl+r4HZNAV0g/NMRzz+9AlkQMw8B1XZ5//nnOnTvHH/75dylpI+wbTuHXLDqnVwlsHymjdztQc21WFyu4zQ3smsVFtUjp7BpX3l/h0edvLpaRSKSnnrndgO63//jbrH1nDT3QWaJAqIjUqzUGii6tay1KYokTnzzBpeUCyv40lQ/K6DNVOq5HrePS1EQaezKE1Q7/+xtz7BtMsGcgjua1yeVyjMYSXL58mcbAFBXTJ2h2CDUF0RCYSAQcy4NIAGxfIX738hwLcxX2CBHku3C6DcNAU1Rq9RpRQyZWsLi0UKUvhBNTfbx0tUB2Mom70sBdaSJlDCDEL5uIMRV1Ikmt4/DC3v5tOxHJsSS2ajPiG9Avcnm9ieVKJA2lp9LlegGVjosiChwaTOCaeWpSDbSuymEikbijIna/eb30VJrYYIz6Qh2n5eCd9Vhpr/B73/w9Hv7sw1jF7hxBOp1mz549PPXUU5w8eRLD0PjYwUE4uH238AfFjcC+XWxTvlqmtlhDFEUyuzJkd2Xv6R32twWu6VK8WCTaF0WoW+Q0iULbJmWovTkr6CZ2putjOR5eAG3HJaZKxB0fYzjaS0puhSAKJMYSFC4VSI4lt1Uf3LO/j0deHKa8FlAqVzjx2B5OPrp1vkIURXbs2MHS0hLRaJRMJsP0lRIX31wi3hcjdksxZPbdRf7if3yTaH8UIxuh7Qa8ev0C0xeX+buff7y33eDQIMVckcGoStn1CYLtFexCPyBoudimhaJIhEL3sUajzUOJCJkDUZqXO6xdLhDvjyEpEu2Kie/6PPyJ3QwMbk2A19fXmTw4STKaZGlpEVnOcGFxlXgySazSJpdQmDiSI7o7uoX21XeoDwRYfH2R6kyVptQkkUiQTMSpXC8jlTqMTqZQdKU3iea5PtXlOu+fmeORJ3ZhVSzqa3Wye7JMPj+JZCi88/oCi++vk51MEUnouLZP6VIBtenSGZKpyNAX13vrjJ2L4M7XYJMCqoqQi+nUTAcrouAPiKQLHYzVFtG+CHZUpen5NG2PpKGg+h0GdY2MYlCabxAZ0xg8NkhqItX7rJl0Bq9aJ64rmz6vD1aknJ+fp71RQ1k1EfrzOLIKAWjyTfuMO+AFIIu0BEhpCmlZZuqFKfoObk8XXjm9gqRJjDwxwuuvztEudUiIgOnjiy6ioaBOJlFHEiAJiEB/QmejbvH+UpXn9vQR+AGV65UPE7274EaAPjk5yfXr13uCbz8onnlinJXFMusXSkirNWzPQx/PcPJjuxlNRxAEAcdxGBsbIx6P8/bbb3P06FE8UeW9xRoZXSYyXaM927WPSkyliYoBb18usPzGIq1Ch+xQnI98YjelUonDhw/33juVSlGtVnt2CbdC0zQ++clPcv78+V5x/sqVK73ko1KpIElSb17vl3/yOKd2FLl+rYSsiBw9PMiBbXQWgiDA8zxUVSWXy1EulxlKJvnqH77KMx8/iVtd43q1u6729fX1bBNuh2manDt3jhMnTlA3Pc6dXiVa6BAfTSJqEv9/9v4z1rL8PO8FfyuvtXM+OdSpnDtVV2eyyW5SzWCSoixZtuy5sgVpYGAuDBiwMTBkyBpg4IHHgr8Y1zJGF1e2LOkq2bJIimSzSTY7V3V35XBOnRx3znuvvNZ82FW761TorqbpK5HUAxS6q3Zaa+/1/683PO/zVDo2paROzg2oL1YpzhhMZgZTac1mc3jOM9kom1mD1n5w1jt4XZvcnjTd6ThPjSfJ3sd64Ba63e4uhtfy8jL/8T/+R0RRJKKId10bBw4c4I033mBra2tIx70To6OjDzyvF4vF+K3f+i3OnDmD67rIskypVMK2bTRNY2pqalDYTKXQdf1vkrw78CMrs7fbbf7JP/kn/LN/9s+G7f4H8ff4SYIoirzwwgvAQJTkw2h2D4IjEwb+z+1l/kYdz1eYnc5y/EiBkZuyrul0mnK5jGEYnDx5kstNiffeX2I2tR+57BC0HZTZmzcyTUZsWthntxD3jhDdmyYIQhJJnatvrnLoobsFQG7cuHFXtaV2o0akGGFici/1iMHbnR490yYei1EXIbJTJNJ3ufLNKzz5C09ipLv8vg87N2r4fRFpJIIxEefYdIpCQsdyfV5dKHN2tUY06PPF04foFrf5B88f58KNda5tNdFmUhyYm2U2F2U6EyHwPUql0lBxStf1oRx8uVxmaX2LjJZA9trDmYB7/l6yRDqTpl1tELQtWqUm6pFZDmcSnNtosuW4jJ+ewLpWxa+bAMhjcfTDWXYEyERUDo3dm0IaiiGjj4/Sv9hn2vHQp1IsVXs3jddDQgQkQSAX0ziQj2LULJT9BcaeHeNrX/sa8/Pz/PIv/zLj4+MEfoDbH5gdKxGFgwcPfui8nmIo5A4NNs74gTiHYof4xV/8RTzPu4tO8dxzz933+/lRIAxD3JrLpT+7xuJyg0rgIwEjF3Y4dHyEAy/dn1L404LGcoPWZovJJycpXSgR7nQgqlHrO4iCQESTsLyAVt/FdHwcf+C/qckCta0K70cUxnM+GWeUmHF3ZVYxFBRDoXihSGZ/5i7fyM3NTR5/cj/FYpG5uYc/dN+empqiWq2yvb3NymILr+8Su8OuQxJFetttRh8aJTkyCGDMjs36xTJLC3WO37bXxKfiTO/x2LlcoihajCYMbn18GIZ45T7edge/42CbJno8ip222JR9crJIZkLhU189zZFKjzdeWWLjagXX9EiPx3no6WmeeGZ217HdXryq+3We/kePU1+uc/0/v8GpQ0+z/8R+MnszxMfj9Pq9oRhFGIakUilGjo9gpA3O/cU5ustd0vvS+K5PcatN6Ieod1CeZUVCVhVamxYLby2QG8sRORzh4ZceRtZkwjDk4WdTiKLAzo0azY02giAQ9UGZSFBLqozE9aEoCYCcM/B2OgQ9F08O0DUVTZcICelYHj1ZJChEyLsBUghey8bwA8aiKhMJg6uXFxHcAn4igR/zyZ/Mk77D71DVVBKGxp40XC6ZRFVp1zHciTAI6fV7GIZOW4P8RA4Zje0wgBDSCeW+XcGg5xLqMpYkcECViWci92U1eLZH9WoVPaVjGgrtmSTpiQR+20QWRWRDRUrpCNrdgV4+rlFqW2y3LEZyUeo36ow9PIae+vGRSf+/GoqiMDs7y+Li4pDx88NgLGXw7JMpykcyrK03GRvNceRogQOjibuShGg0ypNPPsm7776LqWdpWS6zCPQW60g3C7f2Yp30WMjiK0vUahbpbJTta2UqOw0++3O7TcEzmQzLy8v3TPQkSSKXy/HQQw8RTWV5/cIighDB8kH2Bp0iRVF2ea09fXiEpw+P3PVet2NtbY2ZmRn6/T6dzqAg9Ad/8AfMzMywubnJZz7zGWCQEFYqFYrFIjAQixodHUXXdVzX5dKlS+zZs4dEIsHl7Rbd9QY5Q0HUJNqmy2azjywJ6CMx0pUezarNjfkbTMUKuJU2Xk5Gzhp4vs1ExKczl6GcNbjUd9hXiPClowMhuo9CtVodnn+9Xuc//af/RLPZZHx8nEajcVeiJggC09PTtFotNE27byJ37Nixe87reX5wT5/lffv28e1vf5tHHnmEQqFwlxjNvTwAf5QI7xjT+XHBjyzRM02T3/3d3+WXfumXPpZ06k8qcrnccAF/HB+P27G+vk4+ovLw50/cU11JluVd8rqZTJbpGZcbizcYseJod9xYHcsjtALUnIEbhnS7XXraoFK+uljmsSemhkFeLpfj2rVrwMCDJRqNYigGm29t4iFSTqQxkTHEkEIygqTIlNoW7abFIyMJ9KbO0vU651p9Ykmdo09MoUoiqiySjWlDXn3CUCgkdGrNDpvtkN9/e5mnpqI8eWgEs/Y2wfYyiZERnj/0+PA8JFHZVSWyLIutrS0ajQbr6+sYsTFkyQHhwRZlPJmg3qyzuFHmRmMSFJtYZ52KE2U1kiD/+Dgxd8Bh7yoiq12bmCTw2aOjFOL3DhK2trY4+PhBWiMtVr+3SrbWJ6qGePkUjiAiAoYoEDU9vKpJam+a2U/Ocv76ea5cuUK/3+fiexcRKiIL725TLw+UBLMjMQ6emiCeiO+iqNwJ27YpFov88i//Mqqqcvbs2f+hDvP9EIYhjh8gINyzs+m7Pr3rfc4LVbY1kYimEoYh12wf+/wO8UKMuRfmfuTH9eOCwA+oXquiGApqTGXk5MiAxrbVIRnVqPsB222LRt8BBFRZIKHJxEUBwwno52NsqiaNRhdjpc6Te/O7lA5vIVqI0lpr0d5sk97zQcBjmiaO41CpVO7rexqLxYY+cTDYG3q9HpXyIvdaYU5r4LNoWRZSd6D6iBwghiIb81XGdBXfGahtJvQE/f19jrVTXNhsshP2yd/sXrnbHdzlJnYQ0ggcxJiO5fs4C2UiEhx8cobHv/IwalRlOqoy/Y8eo9k0cR2fdCZyT5P4WzOCq6urTE9P02w2+fb6t9GOa5gzJrOfmN113rdX2pvNJuvr65RKJdw9LkePHcXetGmttehttBH6LkHPGfIcQz8YzPl2HLyYyrFPH8NKWPiGP0zylpaWePiRwzzyqMDSYp16pYdfN2lfKHLBdknpyl0JlhRTUSbj2ItNPCNEvSk+U4jrxDSfUqOFKch0g5B4VGbsRIERRSKtKTTrdYSKQCvSomf16I/1aXQajHRG6JS6mH0HRZWJ5yLEM3Gy/T6TKZ2Nhkkhod81D2XbNq7jggDRSJRYNDYQvRHi9K5U0IKQUBXvS0MOwxDX8ujmdGZTBrkgJH84f1913u5Ol361T2pPioXtFj5g5CKYUQFFUZHuI3oFN2fFRIG1eo+pvVl65R6d7c7fJHofAUVRmJmZGXb2fhisra2RT0aYHY/yhU98NFVPkiROnz7NX7x+jnqtw2w6C8FtZu1BiCJomFsVoikJMe6jqSqrl7fR/u4jd72fpmlYljWMo27v1Lfbba5umrz3B+9QvLJJIhbn7T07ZCc9njo+zszMzANbD9yCaZqoqsrq6ir79u3jm9/8Jl/84hdZX1/flXCKosjIyMjQKN3zBkXsXq/H/Pw8iURi+FgQhOAFw++g2rXx/JCUoQx0D0KItUIynZD5y2+Sy2TpGl08NaSfgoOfPAKSSMeOU2yZmK6PKotc2W6jKSKTKWOXKfottNvtodUODBRFX3jhBba2thgbG6Pb7d43kTty5Ajnz59HEATOnTs3bITcwq15vTfeeZfsniNcuFqmVe/jeyGyIpLNRzl2IEc06CKGPpFIhGw2e09Fz/+rYdsevZ5D5iP8I/864Ec6OPPjmu3+KOB0HRrLDZqrTYIgIDmVJLM3Q4cOpVJpuFgfFKurq7iuy/79+z9Uxe52pKIqoqxw7Nhxbpy9jNrvkq2qQ+qmYAcEKRXXcnBVGU2V6bTbuI7H/MJV/vf//QKxWIxUKkUymcQ0Tc6ePcs777xDtVrlpVMv4Zd9tlWo9132jsbo9rpEI1EC30f1+8TjcUqiTrJt8/Z3FukcznJoLIEoCPhdB2+ni3WujOX4IAvIIzHUiTi+Y3JitsBWw+TtbZtO+zLtdptarTakTtxPoUnXdbLZLM1mk/3799NTkrx96RqG2UeqgxEzUO6n7hRCqdxjq+PQnM7wzXMr/IXZZSob5wun9lNs2yyVu5TtQUctFiqcnEzxyEya6TsWuO/6rF2rUNlp44UmEyMTpPek0ZM6b/zFG/zpb/8p+yb28eQTTyIgIMgCsbE4+SN50nvTKIbC2NgYf+/v/T1e+85rvPZf3mMp79MIAzxtQI+QtlpcOr/DY09OEz0woCrcPq9XLBaHN7Tp6enhzazX6xEEwY+sy+54AQvlDuevlqnstEEQmZpJceJAjn352JCC193p0qn6tCcl8gkV7WYg1pZcih2HnYUqE49P/NRSOHulHu3tNvGbnWE1pjL28BhGxqC90SYod6n2XEZlCU0WEUNQvBDJkJCnYmjZHO3yJrlCnu2myfvrDZ6cyw4tA25BUgdqi42lxq5E78aNG6RSqQ+9cSaTSba3t3clPdFolBOP7OXaD7bpNvvEUh9cg77pImgS2bE0siLhei6NlRLBRp+tl+d55+IGoQCCIBKNGASxgIm9ExCEXN1ostYsE4YCmS0bNwgpex6uD6HrETouMc9nfzzB9FyezB0y16n7SLzDoHoOgzWSy+WwLIsgCJicnCSXyw3muz5EoS+VSuG6Lvl8ntnZ2YGI1XgXcUckZaUoVbsEwcCeFAFETUbIGvgC7PvyQeZemGNtbY1kMjnc3/fs2TOkGe0/kIMDOZZeXmLd8ugRMnEff0FlLE6/0kZueITGIPgTBAicPn67xsTkNJIHBwyN/Qfyw2Qm2PY4aBzELtr4KR83cNm4vE1rM6TesXHDEBFIxzQm5tLseWQc16sgCFHW6z2ShkpUFen1+gBomjpUU76F0dExGn0HK2ewp+WgpXTKjkfP9tBVGfXmtekHIWbTAklgz1SSPT6MnRxh7NG7WTDNpkm7aWFtdyAcqPrWujbGre50GPIgxMKoKtPqu/jB4De65Y/4N/hwqKrK9PT0D5XsmabJzs4OBw4c+FA1RkEQ7lp/p08e5t3SZebrRWanEnjrbQCU2SRCFiS1RlqLEY3GKJarOIHDpcvnWF4ZeKBpmkYymSSRSLC0tDQUTfn3//7f89RTT/HII49wYbHCW1/fxF4qMztzkxl0dpP6RpRTj+U+dpK3ubnJ+Pg4y8vL7N+/n6WlJU6dOkWn0yGbzTI3d//CpizLTExMcPnyZQ4ePMjExAQ7Ozs4jkOn6+LFRazVPm5So2W5GMrg2ISOg2d5ZDY7iNkIuQMT2L6D6fUQewEpO4Z5roh4vECtZ1Os9ymuNjFv1MlEVFAkEpNxjuzJcHQiyVT6g/28Xq/vms2TZZl0Os3P/dzP8alPfeojv5+5uTn+9b/+18TjcZ555pldTYu+43Fmscnb1wIqf/Y90o6M4oeIAnQdly18zuUMxo+P89ipKR6byJDNZn+omPpHgVvXZqdj81/+tzO0y10+8ZUjnH76f8TE4X8+/kYh4UeAfrXP8svLtDZbKBEFQRSo36hTvlxm7oU5LMX6WBdmt9tlaWmJ559//mMF5nvzA7XEnhty6ImTrPvX2L60zUg3g2SoJB4dRdQdrO0ufjbCzEgMe9tlen+ev/MrTzI+GafdbtNsNmm1WliWxXe+8x3Onj2L7/tc//p1Hp59HPHEwyR0mV6vSzwWx+z3sR2HdCpNAIOA0/bxSx57HhtDCMG8XsVZqOG3HQRdQlAk8APc7S79KyUoqASpLOMpnXML6yzKBR4/fJJGozEcsI3EU/QdD0kUSOgfzLo5jsPi4iKaprFv3z4sL2T60Dj9kkfEDfDDkE6nAwzkvnVD/4C+1XcobbZpJkWOeiLxGxX6mo+VyXJ+o8nfPT3D0/uytM1BopcwlHt28Tzb45U/vMS5t9ZpdXrEIxFKSxaf/wcPUe6UefnqyyzIC0zOTnLwiwdBGNAwY6OxXQIZgiAwt2eOZsaillqkEpfJJnS0mwGN7fqUmyZvvbbKc+ocC+IC+/bvo1qtIggCIyMj9+z+zs3NMT8//0CDzx8Fy/X52pkNrnxvGbHYQ/NDCEOuvLPJ1ekEp1/cx6ePjyFLIr7j4wXgCwyTPABDkWhIAmbPxXc+nrz+TxLMhkngBrvEamRDJncoR3ImyfLZTWQhJHXTExFZQk5rSBljSEs+EN9P6IWEzRabxZByLsrYPeiwWkKjs9MZeDzaPtcvX0dEpJAsEHgB4n2oeaIo3rOId/TEGIefnuPqq0t0Ej0iCQOzaaFlDdBFzI5NPBPB3engXeqiKxLaVJYlTcTxBoF5KgzQt7pUly6QncpyZG+E3ltrFFfbNHoKVjaJ6/nEVBXFcTA0GT8bJX64QOiHWE3rgTsy29vbqKqKqqpIkkSj0WBqamo4z3M/pdBbaDQauO5gH8jnB7OOuVwO9sLIoRk2W69SWWuTmIigRzUkSaGx2SF+OMejz8/heR6iKJJKpSiVSsAHvlS34Ls+zY0WJc/HiNyf7ihIIuJMHEVz8cp9pJiChUetWkUQRRQCHEmi23Nwus7wO0obaeSIDI/BqZ8/xeLbq/zpu98bWEpMJdBlCS8I2e47NC+WCLyAmVOjaP0eGhoLpTZlH7KJKBFN/qDbGILjB/Qdn57tEtMUHn16hnjdorXcIB5AVxeouz6W6w+tFzKBQGEywaShM3pihOlnp5FvS24rlR6vfWuBxfNFHNNF6rjETJeHkiqeH4Lt4/V6+J0+ruYiGSpSUvug83MHRFHA80KCm9dz6P/0Fqc/LlRVZWpq6mMne2+++SanTp3a1RW6FxKJBO12exf9rhDXeeLABD+Yl1gJquydHENVVfpxBbPRYfRoFLUsUVtqEQYhn/u7T/KZlz64x9m2TavVotVqsbS0RKVSwfM8rly5wsLCAq+88l2E6MPY6w0y+7MoMZ12p0PhcIHqSov3393i6MzdyWkYhhTbFjdKXcodC/GmEuX+Qox2u41t28zNzbG5uUkqlSKbzbK1tfVANjXXr18nkUgQj8eJRqPD2bi5IGSxFHJl7Spc3sKL6MQSOkLDwmmZyEFIMq7hxUViWoxIELC1vY2qq4hJnfZ8jXXXpdG2iRZ7TDRsFEkgk40gIGAvNXn7SpXLh7J85rFJTkzef7ZxfX2d2dnZD21CiKJIEARcvXqVkZERbty4waWLV4gYE5imi5HQOLfRZOGVFSJNi4xtER1LEqoCIaDLEbKBiFMz6b2+xcvXa2x/Zh+feWwvl94/+1eS6N26D1bKXSqrDXrlHmuLtZ+eRE9VVT7xiU/c86L4SUYYhGy8uUF7u40/GmOh2sMPA2bzEeSWzdqraxz+6mFqzRrlcvkjF7plWbzxxhu8+OKLD5Tk3VpMojjwzDs8GuftlTpGPsbMM0eo7cmxcW2ZucOTxMYzzOTg8vc2iPcD7FKb9ESST33lBNM3fZwymcyuqls8Pkj+dF1nfO84US3Hih9A4KEoUVrtFpqqkkqlBscD9B2ftuWxVx/I91pLFcyLJaSEhjKduEvSurVTQ98IMN/boT0lcmzPGCt1i3B2lLm5OR564jnOb7a4/J3LmB0bURIZnU5w8kCB/YUoC9euksvliMfjSJJEVIIT02leKVSJLjVRYiraLT/HEEzLHJi3hyHFjR6eEBIzA+RLmwiGxHQ6A22P606R69MpTs/l7kvRvIWF8zu898Y6YU4lP6YTIHPlvS1S4zoLpbc4d+4cfbePkldI7U196LBwZ6vD1XM7tBMaE5normBPUyTGsjG2vQ7vv7HMo/lJLly4wNNPP/2hx3fLCuVHkeh9/0qRi19fIN200UeiiFEFQoh3HPrLLd7+i3kyCZ1Tc1nkiIyuiTh+SNcaqOPBwFsp6ock0/pPtYm6WTPvK/He8gKaUYXMyZFhon8Lgenibnfwm9ZgxskLMELotqtc3ekhHi4QyUWI5CLDmTxZl2mtt1j4xgI7N3aoV+pMTU5x+f3LaEmN/JE8qdkUevLBEidZFvnqP3iIsakU77+2iNW1mDhS4MQTk1x8b4XitTbta1W89QZSOoJ3IMui62Ego8kiQRiyafqIWoSsJjPiqIzuLzAWbqBKNttvlcGQmMxlEAmI7cmjjcYphQFOxsC1XFzTvSvR872AG/NVlm9UcW2PdC7KsYdGaTQa5PN54vE4Gxsbw8p6Lpdjfn7+QxO9breLaZpYlsWePXvuejyXi/ELv/Yk3/zDS1RWmzRqPVzPJTai89iLYyiqzeZmg3R6hP/jP3yfRDLFi58/TLNZo9vtDu8Ldtum07TpiyLR+4mXAN1el3g6AUlwYwNPLMUKSGpxfDGAMCBEwvFDzIaJrMuYdRMlojDz7AxeziPwAq6eKeLvHyVes5DcAElXUAFDNWhKFovzFbQMhLGQuUySubFZNus9Nhomjb6LFwQDy7wQFFnEt/tI7Qqf/MTjJAyFcDROPB+lvdWmV+qRCRx8YTBP6DYtYvko+1+YY/Tk6MCj8LbrvNEw+dP/37tsX68QK8RIjcYxvTbVaxXe/It5gpiC33awAMdxCRV5YAofU1FGo8gj0V0JX9B3cSp9QtOlbAV0tzuoMRVREYlkI8TGYvdUpf0bfABN05icnGRpaYm9e/d+5PPffPNNjhw58pFJHgwSvc3NzbvmrJ4/VMAPQ65saby3uU0iniAXSfD0gVFseYuTR5+gWurQbJf4zOd33980TaNQKFAoFJidnWVnZ4dIJMIPfvADcrkchdFxLs/bCJ6LpMoDz95kCgQwFJFatX/XvJjjBXxvvsyFzSY920MDEATObzRRQpdp3eKrz56gVqshy/JQ5MU0TQzDwHJ9lipdirUeQQCZlM6+QpykobC0tEQ6nca27btiaVEU+MLTe/E9kTe/uUBQ6WNZPpYqIo/EGO97JCbidHpd5ECh0WgwOzOL67lsFUv0qyHOdpuCoSBoMk4hSl8IYSyOKonIHRt9q0u3Y/MN10d4EpbevsHsngkeenTwm7RaLQzDYHt7+yPn+2+Z2j/xxBOcOnWKN988w1/+2SLUN3Fdj5bloiAwk43BeAQ3UClXKkxNT+8qfulRFc31MdfbXP7aPGEYkiPE9/0fqejK1dUG59/bot1zmJtNcfrU1H2V8qdn0px4bobL5xd59Km/ehrpR+FHluil02m+973v/aje7scG3VKX5loTIWdwZq1Ozx5Ubasdh6dmM/jFDu3NNiN7RygWi1QqlWE1+K736na5du0ajz/++AN38lKpFM1mc5icffJggY7tcXW7TSqqkhvLYaRjXL8+Typw6Sk62Ufj/OJTD9EvF3ni6WNE7iHgcAsnTpygUqnw2c9+lsVvLLK9UGW13afb66NIAqlkEvG2xRYEIW3LJSmApIj4lT721SpSxkC6R0AfBMHgxpyJUL+wQTI+izqpkjB8Lm62ELqw8PVrNC+UiXQcVATCMGD9XJHl8W0y+zV+7pn9eHZ/1w3i+GSK68cLFPsu2Z0eoqEgpXUEeaBWGtgedqWPZ4c0sgqFqok+ojM6O4FX7SOWHHKP5Dl3fZUjeY34zUTRD0IafQdZFHbx2UsbLZwgIK6ExGKDClxPk9icL7NcXx4OJSuKQqvVui+FRZIkduYrFBsmicn4PSv6ogixpE59q4Xfj1EKRf7df3iNSCTKyGicEydHmbmNPgkMTaRvn1P4YdDsO1x+f4dYwyIym/ogkBJASmpEVQl7q827729xYjqFnFZITmgkTFjpO7RNhwAwQpiWZcaOj/xUWy/06/37nn+97+B4PpryAa01sD3czTZeuT+gYysSgiohaAO/sYQYpVJpU5FE5CUJPaUPacG16zWqC1UalQZhMuTAyQOIkkjgBdhtm6VvL6GndPKH8ow/Pr6rs3I/RAyVFz53gOc/s49Gs0Wn02R2doqpWZXalsvFP7hCOSXTHk0iaTLZmLbrmk4YCpbrs1G30SMu8nyVp559ip3xHb61+W3aIzLueJLJ0TSIAmEIfqOPDkiKNKCk3oZu1+a//f4FVs7t4FoeojjwIv3+n1/koefHOHr0KMvLy7uCVMMwPlSC3bIs6vX6UCjhftTO2T0Z/uE/fZob81XaTYtIVMWImRw4sJd+v8/Kygrf/vpV1t/uIoo1UukIn/qZA3Q6naFPYeAGeK6PL4F0X/WScNARE0UQQZ1JImUMvFofbT1EFWXoBzimiR0KdHe6xMfiTJ6eJLMvQ2w0xvr6OvXVBpvbbbTxOEZcx56vEer+4JoCYhGVbtvBrMORk3OUS2WyKYnsdJoj40nalkff8QhCkESIqgorC1f5s6//JbMpmdNPnEYQBaIjUaIjUey2jdWwBtdbx8btuxz+ymHGT43f8zs9++Y62/NVRg/lkW/+zvJIDFIG3fkaUlTBn4ghZiKEloigqUgMxF2shTpKx0bbmyawfbztDl7NxGxaZOMapiBht2zam22Wv7OMKIvEx+OMnBghuz/7N/56wNKNKjeuVXj0iSnyhQ/oubquMzExwfLy8n1piEEQcO3aNVKp1AML0t2ibt4JXZH4wolxHpvJsNMaY3llhZGYxzMnJ/jva++xb38WUWpx/OFj93jXD6AoCq7rkkwm+af/9J+STCYJgpBz/68/RUJACAXiNxPSgV9nQCqmIt12LQRByHfnS7y1VCfvhWS2O7ilHoIgII/HWJVMVgpZ3rxR5mBaZGbmg06PYRhc22rw/vV1KteqhOUeQghBSiOxL8PBAxEen8vSbrfv2zHNxjR+4YV9bAQeG+ttREUkoYcUGg6xpS6WbaHIMvVGnZH8QCVYURSkaAa7tUOqaeM+MkYYURCDgMD28YMQQRGQkjpiTCW+1aV1ocz/WWkjnq9wNVrizHuv4Ps9ms0mP//zP08qlbqvz/ItxGIxNjc3SafTg/GbXha/tEN2Nka55+C/sYFs+XQ+qZNUDOKqQcSIUK1UGBnbrWshKBLGbBLWWlz+1hLHP/sBNfZHgfNLVb7+u+fwtrooksD2GxtsbLT4ez9/4p5eh7Is8tLPHubs5T/G9Y8CHy1o81eJH0mE1e12aTQa91ykfx2GJv9nwuk6+I6PyU1Pq6QBAmw3TNquTzYUhl5Ao6OjFIvFe8rJNptNms0myWTyY3VFY7HBTftW4hDVZL50coLJlMHFrRYr1R6EIcboHnaKW+zNR0gm+hybS9DOsSvJM+sm2zdqBEHI+N4M0UIUVVXJZrO4rktiKkHlWgWnUcIVtHsmK+WOheuH5EURORvBLfUIgvD+w/XdLolYnG6vh5aMwpZJeCggF9O4ut2iuRQysrHKiK4jTSQQNYkwCIl0HeyNDtW+y39Xlvi//63Tu943E1X5wsOT/AWwfalMrNgjstNFGMRIhCJ0YgrFeJJAtEj1TEbSg98ktH2ElI7vh1h2j/Pnz5PL5dDSk3zve8uUlutImsTcsRE+84k5koaCEdcIXI+AAWXOD0JCJ2C7tkWv3yMWi/Hwww9TKBTwff++83LJZJLlreu4IiQ+ROVOk0V2Oi7f+L3zeBEVv9dDN3S2Ebn03WWOvzDHS5/cu0sg5fjx45w7d+6BTUrvhY2GSXelSc5Q7kmPEg2ZmCSxeanEf+m57FzapllymYtaTAoCUlxjY22Nhx86yP5HJxh96IcTKvpJQeiF9w0qvZuzCsO/10yc1SZBxyY0JKRc5C47AlkUCNU4rhEQT8RwOg5rr67hWR5qUsWXfIwRg+x0FvU27yQ1phIbi2E1LTbe3sDqWOx5fg/KzYqmJEn3tVsBkGSRXC5NKhVnaWmJ0dFRGr0NEnpA+/Aoza7DyH188nRFYjQdY7HaYjyvkt608XSPUAkYlUMEQ6fSc9Bkka7lko6qpJyAxL4Exh0el9/8r1dZeHODzEySyE11Yt8L2by+zfXXG6TSF3nmud3WJI7j7KIg7Wy3abctMpkI6YzO9vY2+Xwe0zTv6V95O1RV5ujxD67p9fV1wjCkXq+Ty+U4ejxL5eo1wjDE9npsbGwAA2bGpUuXGEuMIYgCYsiQXngnur3eXUGWFFeR4ipOUsSQdULbo90yMfyQcE/ISnSF1ESK2OggYBcEgW7bxvZDIqqMXFDw2zbe9sBKRpDFgfCKKmF1HcIgJF/IUyqWGB0bRZFFsjGVLLv39YvtJpIk8d3vfpfRsdFdga6W0NASGk7XIfAC9nxqz32TPN8PuH52Cz2hDZO8wYkKhH0XOQwJCLHCkPV6D88PkPoeUVUhrsvoEQV3u4tftwgFwPYJDAWyBuOTKSTHJzmdZPShUSRVwrM9eqUeN75xg9bRgQKu9gC+jj/J+P43Flh+a5MgCPncl4/sekzXdcbGxu6Z7Hmex/LyMqIocvDgwR/JsYThgG7baJr4XoLzCzXePP8yiqpyfaOM0+0+UIexUCjQaDSGHfTV1RWy+YBuPYlbNFHHZQihV+zgJTQeemT39bnVMjm33qTgBYjnSthtBzE5EBjrnt8hk5TxUklemy/y+Bd3+xC3fIWXv7eEcqNPWhRRkjqIAn7bofv2Fmd34ogCfPqhDxcni2oyM9koUU1h/OZM8sYPrhEKYFk2jmNTKIxw6+bhegGNtkWk7RJqMhiDPTwIQ4LAIww+2GcESUQejxHb7lCq+4xPp5gajaOmXC5eXESSJF5//XU0TePq1avDePVe++KdiXtlp4MoiwiaTHuxTkQUcHUZRVCH+guSLJFIJmjU66TviC8FScSYShBZbbK94ZAK6j+SRC8IQs68tkaw2WH0YA5BFrErPTbf2WL+9BQn76NKevXqVXRd59VXX2VycvK+Fhl/HfBDJ3qWZfGv/tW/4nd+53eo1Wr3fZ7v/2TP38iajCAKqIAiiTRNF0kYtNk1aXCh3151Hh0dZWdnZ1eyV61W8TwPx3GYm5ujX+3jWR6iLBLJRe47O3M/GKrEM/vzPDqTYbPRx/ICJFEgHzvA+sIV/tt/+w5LR/dz8uTJ4Wt6lR7f/N3z3JivADA9m+Zzf/8hUtPJoZz/3OQcO40dpmSZWjJDqW2RiaookkgQQNtyaZkucVkgIkhIOQP7eg05fZ8b5s3KtGmZSKKIPprAK/bwyj2U8TjVto2w0kcPApR9H3RBBVFASmgYuox8fYfGVIJS2xraTtzCRMrgb5+a5vxogsvrDaobbQTbHwhBRBV8pU+h0aEfqMyMH8C53iBo2ohxFXF/GkEUeeH0CfKKw7lri/zlfzyHtOkQM2QUSeHCQo1+z+WXvnKUww+P8fbLV2ls9OnHPfyuy0guys/92if41usizWaTX/u1X2Pv3r1D9b4gCIaboSRJJJNJYtEYtmUiWj6u6aGJIkh3B0K1Rp/+Up1oLsL4wTyikaVWq1KIp+gWu7z/59dRVYmXnvvgprFv3z7+5E/+5H8o0XO8AGwP8R7Kjrdg9mzqCxXcxSaZXIRsLk+5aSM6PjMTBmX/MtHjh9jz6T0/9VQpQbx3FRsG9+lbEhNeuYe92IAgRMpG6HQ7NDbKTExMIN82+zhIDgQyqTSddhNDNgadlLqFaZtEU1EQQFB0Nut9/HBQNMjHNCRJwEgbKBGFyuUKkiwx+/wskiKRTqdpNpsf6XckyzL79u1jbW2N9ffX6QYB1b5L5iN82AxVIkCgKoQYSxW8Ax6f+18+R7AR0lAF1m0Xyw2YzUSYCASSMY2REyO7grDiToel93dIjMaGSR6A69qMHyiwcWmHytboXQWWXm/g39npdHjz+5uc++4Kbt9Fi6vMPZrkKz9/mvX19XtSNj8KIyMDJsf6+jonT55kzx6DVDpGGMKRY4XhsYRhSLvd5vql61QbRdy2QKUrMTKSQFE/+O7Cm4Iy9yoOOI6DFtWRVBViKqEExYUldparBJ2AQ8cOUSwWSaUGfnmKKiEKIa4fEFEVtLkU+CFeqYuUNggkcXC9KdLw8zKZDLVqjWwue+/f0TA4deoUBw8d3JXk3YLdsenudBl7bIzJ05P37Y66boDTd1Du6Ha71T6CLCHqMj3bx7R9LEEgpksICLQsl67jko9qaJ6PNd9C3ZNEzkVp9WxyMY2kLtOpmxSOFob3ZlmTSU4ncfsuxfNF7I7N3hf3/tSKRAEceWScxfklqs1l4MhdjxuGwdjYGCsrK8O1Ydv2LmXJH4X413yxzXuXiqxeKWOvNJHdADEM8fyAG40KWws2Bx6dRiu0OTgaH15Tvh/Q6di7BJri8TiVSoVCocDa2horKyt89aXHuXjQ5/L3lmlvtABQ8hEe+fQcDx/Yzb6a32ljOR7Z9TZOx0GZ+uDzLL9HpBXiLBURj0yxWO7y2OwgWQnDkAvLHaxLNUazSeTCB4UaKaGR7Dg4i3UuFXSef/ijw/KYprDV/ICFIGcM+jfK9DyHidmpXftD23Tx6hbxvot/MDtUBe71+zTbXZZ7FaKGimFEMAzjpj+zQ8aKMPGFOX7x9AyCcIqHHz7J/Pw8jUaD559/nnw+T6vVolgs0u/3h58niuI9GxbxpI7veNTbNkGlj8BAVOlOux8jEsE0zSHVFYAAuo6H4wW4YUj5YhExGuPxTodk/N4WVw8Kxw9oty10TR4Wr5W4Rti06LTt4fMGxblgKBjkeR779u275z731w0/dKL3j//xP+Z3f/d3+fKXv8yzzz77Uzebdwux0RjRkShm3eT4RJKFUgfHdQkbO6Qn04hpncTkbn762NgY29vb1Gq1ofmjqqoITYGlby7RXm/j2R6iJBIdiVI4WiB3OPexg2JDldg/snsR5B99lKtXr/Ltb3+bmZmZ4Wzd/Nkt5q9XkKYSSJLA0lqDS2+s8+z0cZLJJGtrayiKwtTjU0TeqzGdjTLftal3HYIwJAwhpsvMJQ3Ki3XUE1nkpIZleQjZeyvhdXpdFEXG94MPlNuCkMDycf2AbrFDvu0gTt+7UmJ5NvG4QWOlyVKle1eiBwOqw6cPj/D4ngxr9T6W4yMIAjFN5sx3v06cLvqR59juOuQzBmoAPUVkR4RjYwn2jyYGZr/bHim7yuiREYjImKaJv1Ln/MuXiMVqjMUVnvzCFNXVkNZOl1guymOf2EN+NoX/qs9zzz3HzMzMwCz8jjlIAKtrsXl5k4sXL1I8s4awYFJdreIkddTxOJFCHPGmqWkYQmWlQcLyyRzJI0YGyziVStNst0nPpPEX61x6Y4MnHp0kfbNzI0kSiUTiQ+nDHwVDlRBiKn7duufm4bs+1YUqqBIjB7PEb3WNJqFd7HLh2haMZzgzf4ZjTx/7qd03bkFLaLQ2BkICdscmcIMB3cZQiOsDMQ672sdbagzosTd9pARRxHVd1re2iSXTICqEDIRysjEVGPi+rb+/juiKiBkRelDvtrCOFDh/rUTP9hCEQeU1E1XYl48zm4sMgt7ZJKXLJZLTSXKHchiGQbVafeDzmhyf5PXN1/GjGdw76Kf3QzoeZX6nRFLXODx+GCWvIEwKGFcrZNyQQAMZkchIhKknp0jNpHa9fnO9gdmyGD20+9p2XQfPF4llY2wu1O/63H6/z9TUFK98+wJnvrGOGlXJzKRolbrceKvO62NXePa5D6eF3Q+apvHuu+/y9NNPD7twt3f8bkEQBJLJJKefOY2yqnDp9y/S6HjYoz0KR9MINxN+0zTJZu9N+3ZsZ7iP9hwPXRQ59cgxMqejnF04y+HDh/E8j2azSafTodPvEFMDVksNlHwUTdPR9qdBBK/YpSsIGCGM3/TYC4OQdsvF86HX7RGN3U3devzxgQ3OpXcv0Vpv4bQd7K498ALtuoiyyPjj44w/Mv6hBUxVFYmmDWobLbipuRDYPn65j5zT6TT7uLbHWMag7QZ0LHdoym67Ps1il0zXQ1BEXDug1bPJRDT25mP0WzauJODEVXq2R/Q2irISUcjsz1BfrLP66ir7fmbfXYHoTwuefHaW9e13+O3f/i361ia/+qu/elfiZhgGIyMjrK6uks/nKZVK7N27lxs3bvxQ3TxVVYcK20EQ8uZ8me9/bZ5gtUUsgFRGR4yqCJJA6IdEDNg+v8X6jsufLLR57gsHeebQCKIo8Gf/5QKrF4s8/aXDPPXs7PAzFEXh3Llz+L7PY489hm3bfOn5PI+cHGV1bWDIPjuTZipzN2NipdbHMz0a6y3QJBTbJ6JKBIGPLCv0wi4pM4sjCpQ7HyQJlY7NznKTpC8Qpu5mN4lRGTX0sTYtlqtdjo5/uB/cvkKMS1tN/CBEEgXSewqc+94lDqTvUVzxA6SaCapEeBsDIhRlUhENjQDbtrFtm2ZzcP5uy0JvJbhwUeTRjMvk2Ch79+4llUrx9a9/nUKhgCAIpNPpu+7hQRAME8ClpSU6nQ5hGJLMOxgjGmvvbaNv93CjMtH9KWKZu+PDTCZLsVhElhU6dkCt59BtmoSmh2h5uOst3tEFms55PvfEMQ6MxIjrD6ZOfyc0WSRfiLF6vkS87yLoMma5R2CIWFaNbjdJ25P4zoVt1lbrnKus8MLJMR577DEOHTpEv9//a93Ng/+BRO/P/uzP+JVf+RV++7d/+0d5PD92kFSJydOTLH9nmXzXZWwiSbPd4d//x/+KtrnD3//1v4+sy4TBborW+Pg47777LtlsltnZWc587QzCukDgBWhZA1/SkcIQs26y+K1F+rU+089Mf+xkz/ECFrfbFLfbBH5AIm2gJzI8d+AA3/3ud3nkkUeYnZ3F6jm4kkDKUBAFga4kYPVswjBkcXGR6elpqtUqT3zuCS4KF/E3fR4SJXq5GC6D7kPUDSjWTKozSSKPjBLcpKzeE+EgMAkU+Y7h63B43H7fRUVAiaj4nod0uzqd5+N6HtFMDKlt07ptU70X4rrCsds2z4WFBfqdFk+cOMLssUneWKyyXu/jeAExTebpsQTP7MsNlSI7TQvRCRCjCoIkEovHMGZkaFocOnyCoLWJYRiEepXEnIgkO3TCKo1rRRzHYWJi4r72ENXrVbbe2aJb6WIHIv5YhkinT6vUoWsFxG40sVebiDkdcSJKsecir7WJj8RQsh9QJiRZQtc1er0e8dE4xfUm15dqPHnigxmJEydOcOHChbv8bB4Us9kIqb1pejsbKKaHJwX0ul2SiSSiLNFabWL3PIS5OFa/hUhkGOAmRmNsLRfRxTEeffTAA9uG/CRDT+t0tjv0yj26lT6O7SGIAkZEIToeJ9n3aa01iXgB8m03RMcLcCQdJB3PDhGFgRqk6fjIksD5zSZ5UUL0JNp2i3wqT8+zqO/Y7CxWSR7IMZYyEARw/YEZ+9nVOrbrc3AsjmIoSKpE9VqV7IGPP7MUBiGqqhIaOmalDakIH6Z/H3g+vV4P+eYaSSVTmIrJ1LNT5I/l6e508V0fNaqSmEp8+PzgbZ9j2zZ+EBDRdWzp3uySWzTqasnEt33S+wbFsdx0ktVzW7im/EN3Jra2tkgkEh9rLnbs6BhK7xxCx8ZWBAxtAlmVCHyfIAwwLWsX3UpRFTR1dyLdtlymQoHCVJKjTx/lxLMDuqqiKOTzeXK5HBuRDU49G6X/3SUa/YBoYCET4hVk2raEsNgml9TQkoN1evbNddaulFEjCvsfTnPwsH6XGEK/1qe11sKcN1ldXEUzNAjBalnIukx8LD4Qm/qTq+QO5xg5PnJPWr8oihw7PcV3F+tYXRs9puE0egR9F5IaXVFAjqtgWiQMBUlQ6DkePTtEcgPcSo+eKiPJInLbYnQyzlgmQrHUpVzu4k4kmN9uoZTajKd0pjNRRm+KEImySGpPitp8jcRkgrGHH2zG7CcRgiAgyzKvvvoqTzzxBA899NBdz4lEIiiKwvnz53n66adZXFz8UAuBD0Mmk6FerzM6OspbNyq88qdXMDY6xMfiSPHd14kgQ3Q8TVyw0BFwLm7zPcdH/KrAM4dHKG80aBW7VHc6w9dUKhV838fzPEZGRnAch3g8jigKzOSizOTuPXdWalvMFzu8vVyjXuszUukSqhL0HQxNQgtsoopANpm8Z5e6bXk4LYexxKBbdXuXHqDd6ZAopCm3LLoPYPmxrxAjG9Oo9WyyEYXltVWyT+5BrUdw19uICRVRlwm9gGCrg+gFBCNRwujgc4MgRBAl9k2Nko6quK6LdbOL1mg0EHQV2+zTKpZ55burqGKIruu0220URWFra4t8Pj+c/b8doigOE0BVVRkfHx+KBkbTWX73P7xPRBCJ7kuTnbpbnG/w40IineXiSgnBkpDbNvGOi+wPWGBeyyRysU6t3uIvmgpjB3J87qHxXbYQDwpBEHju+TmqW2125muIfoitC0w/WeATjx9lc6fIH7y2RutMmZwvsTzf5mu2yy99Yh+xWIxKpfKxP/P/avzQiZ4gCDzyyN3GlD+NyOzNIKkS5ctl2lttFC9ETamcXTiL+/9x+cLPfAFREknNpgbD8GMx1tbWOHToEK1Wi7e/+zbihoikSdQNmesbAzUnVRKZyUbYPxpj5/0dYiMxcofupk7pun5PkY0rq3Ve/c4SlWsVgo6DEIaEqoSbDKke7vD3v/o5Xv/B96nVakztHyf6ygLFtQaIAhlRZHRPnGvXrrF3715s2+bVV19FURVyD+XInBpUPetLdQInAAnic3GUhMq1ahsxohC6AYIuE5oeQvTOja1NGAa7krzQG5hQibo08N0LAmRJIhKJ0u11dz233W6TzqTx6iYIIH+MquvS0hLz8/ND08/RmMIvPDZFpWtjewEJXblLbWlkIkEQkXGqJtpIlDAI6Vb6CHkNs1Nh//Q0yWRyOEQdBAG1Wo2XX36Z5eVlNE3jzJkzFAoF8vn8MPnZOb/D+g/WabkemwSU+g5mKCOORghaJt2WQyMiE0MmsmURtANiURnHUEgfzcMdVXEjEqHZbKLoMqEbsLTZwo8o+H5AVJOZzWap1Wo/tGJVRJV5/PFJvr3apL3ewsXk6voNFFkhoyaQrShuUmVmf4FMVMPs96lWqwM6RyJBfiyLkdPZt2/fripY4AW0Nlo0lhrYXRs1qpKeS5OcTv7EVtQ926N6vUrxSgUrItEQBRwRhBBipkOi1kdrWjSbFsH+D6qmluvT9UXkSAJNlrnF3DQdn1REZSSuYXkBKxt1jJbJnr0FTLNPtdHHjqgkuz4RLxyKoiiSSC6u0TFdru60ySU0cjFtYLK+0aKz0yEx8dGqebej3W0TjUcJZRXDMGg0myQT8V3CTbdgmia+76PqEbTAREJEkIVhwGSkDYx72EXcicnpNEZSp1vrk8gP1len3SYWi6OqKlarwaHTk/d9va7LBH6I74VIsoDVHxS6Eskfrlq7s7NDr9fj9OnTlEqlB6b4ZPZlOPzUNP0zm3hTcUy7D/ZgDOKWIuDtcB13KOkOYHoBuAI5XadwrHCXYM3wdT2X/acmsLs2F87v0Gi79GURwQ/JxmLMfnaMiUKE5naT4mqFlQtlRFmmW+ljdrNUyhVGb4omBG5AY7lBfamO7/gkc0narTZaqA1sX46NkDmQQYtrBH6AWTdZf22dxlKDyScnyey9u0v52JNTrN6osnxmC0mTaGwWEVa7CDGdYDxGaiKBVjURHAFZBUOUsPwQv+vieCGmZDGbShGTZZJRjcXlOt2ugzabJD2XQhRFbM9nudJjvW5ybDwxpP7Jmoye1imeK5I9sHue9acJk5OT/NIv/RJTU1P3TPKAobXPQw89xOXLl0mn0z90Ee9WR2+12uPVv7yBsdkhMZ1EvI9glSzLeK5Hbm6KjtGifmWTVxSB5evnKFtXeOH/9iVOPDpOEASsrq4OPUOvX7/OQw89xM7OzoeyW/wg5I3FKmdW6rRtF0MRUSMysckk4mYbLy1huj7VnksioiP3TfKn03h+SP62GU9ZFBBkkSAICdlN1e91u4N4oG6DJCDeT4TpNsQ0mWPjSb57vUi7tEkmkyE6GSHouRgTcdy1FkHPRZAEpD0p+rqI3nGHr+87HhFFInEzzlEUBUVRiMcTGIZBJFCpt/tk9mY5OunTqFWp1WpcvXqViYkJvvGNbwADTYF8Pj9UNs1ms7viilvKm+l0GlEUmdkzNihkhTKxPfdn89iuz2qtj1T3SdT7CAEQkQk0CW4K8qFJ5Hqgn9umWLf4r47HVx6f/qGSvT0jcX7pVx7jytUyvZ6DZVd45wf/nf/vxqvkZg4SdCcYtQW8BMjbDTYXojROTWOoH31f+uuAHzrR+9KXvsR3vvMdfu3Xfu1HeTw/tkhOJUlMJnD7LkvvLJGMJIlKMY4efYKNuolISGOnTfFikWAk4OTnThKJDi7IzloHsSoi7cnx3lINURBI3lSju7LdQpFExhXxvtX1TCZDuVxmfHx8+G8Xl2t8/fcu4K21yOQiqHNpEAT8nktzu8nl9S1eHt3kCy+8yDf/8hv86Rt/ikyEh2c/SQhkp2Si0wpz+w8ShiFbW1scOHCAcrmMIAjEx+PEx+NMnJ7Ad3xESUQ2ZKItC+Ntk67tEUtqKGMxnNUWgS7jBwGiICCLAs16g+k7Ah+/YSGlDeRClJAQMaUQxjToeQR+MHxeu9UeyjV7bQcmY4ymdLaaJgulDqX2QBAmrsnsK8TYm49h3Ax2VldXmZ+fZ//+/ezZswfDMFhcXOTw4cMfaqFwZDbNhSenWXttDb1h4ochQUrnsRcPoHht6vU6pmkyOjoIfERRJJ/PI0kSe/fu5ctf/jKRSIRKpcLCwgK9Xo/+Vp/u+S6uEWU1lDG9gHREJRvXIAN+2qC92qS53cFu2aCKjPZ8cgdyXI2qhPeZk0ulUqyuFqlXLVrfX2HxlRUIQsKoTGxfBknLcmV+kRNHPqDWeJ6HZVnYto1lWR/6p29aWBGfVT0gWO3jNcARXdqpHhP7R4mWXdL6IDAyIhGMSITA9wcejfUWkQmVRqPB5OQkW1tbZBIZtt7conJ1UBmTdRnf9ilfKpM9lN0lCvKTgjAM2Xhrg82FGkUFOr2B15kuiYRAy/Fpej7RuknMD+jU+0QzEURBoNS2cf3BUL6AQBCGWK6PIgnk4yqyJKL7AV7PwVJkyh0HQ4Se5WPMJsD18Wr9uyrkcUNhq9Fns94nF9NQDAXf8WlvtT92otfpdph5ZIatMyUShooXKPT6fXRNQ7nV2Q5C2t0Ohq6jaQa1eod9hoqiC4hxkaj64apud2J0LM7cw2Nc+u7yYLZL9BFEEVXVKC83iKYNTj5+/0TvyecOcuntTXaullGjCu16l7lHpjh87OP7NRWLRQzDwLZtdF2/pxn0/aDFNZ7/+w+RiGlcMW36oUjGEGm1WnS6HXRN39URUFQF27HJ53JYfoDTtpiwHZJ7JayoxcbGBoqikM1mkSWZxkqD6rUqC+dXiEZK6JrMM49N0ujYWH6AqsnsfWiUkQM5lKhCe6NN+VqZ1cUutaUGsi7idbrE1WlKmyVSqRSVyxWaK00kVUKQBYJ+gGM6JPcP7otGxhiyUURJJJqPYmQM2pttrv3FNUZOjRDbG8N13eF3FIYhT34qTzwrcP3MDr7rIQgOsTGJ2N4UUjyGnongrrWQug6GLKIrIpbpgCHSrm9Tt9sY+SluLFSwVIn8sRHU8dgwWZYlmagm0zFdLm62MFSZmZssiUguQv1GneZqk8LRj/Y/+0nEU089xVNPPcX7779Pq9W6y/qgWCwiiuIw9rAsa5cuQ7ttcfbNdSRJ5NRT00QfMGG+tFjFXaqTzkfvm+QNIQqEQUg8l0Ty4ewr51iuwFef288nX9xHp9NhaWmJPXv2IMsyV69e5bnnnmNra+tDO/VBEPLd6yXeXKqRiaocSido9Bxapkt/JEK0aiKXeihySF6W8It91lMaLVVkNKqy/zal0pGETmw0Qq/URnaFIcPLdRxEUUQSJXptG/VYjrEHtLc5MRHne+9eoa8mKER04okE250tkkcn0A9kCGwfQRaxHA/3vS3CK1VwA8wwIADGUsYuRVEY2LbEYjG8zS7eSJzHjs3y/MmJwWPdLu12m+eff55Go0G5XB56FC4uLg5+ClEkm80OC9r5fJ5OpzOkd0q3Et4POa8gCFmv97E3O6SqNhgy4Z3XQAgYMsn9GZqlGtntLjWKfEOV+cUnZ0jotzqXAY7j3DOOsW37rvn4iACxhEir1cOzLSxLI5tK0EDF02SMfkiYSuAbEtsbazhNHc/76A7sXzV+6ETv13/91/n5n/95fvVXf5Vf+7VfY3p6+p4dgvvJyP84wWpZlJcaOI5HYSpJfDx+zxu2IAj0q3061zt86at/l/ebTd5p9YncLKTENJlMqcW+fpLGlQaRxyNsbGwwpo9R0kos7jTwg4B8clAlUGWRIISVao+Z2TSdYgerZd1V3ZZledfF1rM9XvnaPP56i8L+zMCc/NZzkxrZRIH62zUufusGszNxdnZ26Pa7JHIiT/3SfirlClOzU8NNfWNjg6mpKURR5J133mFy8oNgSdbkXTSq8aTOnlyMG6UOsXyMfj5C5VKRVs/Ci8iIgG/1mMrvHuYPbJ+w76IdKyDIIvWWxXheR/dk7I0uxPzBHJNtoygykizhVft0hJDInhSXNltsNPr0HZ+IKiMKAmuez/mNJoWExhNzWbJhm8uXL/PpT3+anZ2d4Q3q+vXrH+kvF1Fl/vZXjnB2LsPiQgVVlTl2cpSpjEAskicWiw0l1GVZZmJigk6nQ6vVIpFIDGchJyYmmJiYIPADrv/X66zESlzsdOn228RVaJkCljYYjNZjGqnjBRJzacyOQ8d0yY3FmUjobKU02ssNslnjrmux3rVp3ugidGwySQUjrhAQ4G05NJZ2qEZFzp2/xudPXSFwBpteEAToun7PP8lkcmjErmkauq7z0HKb1763ykJrk3LHJ56KceT5/Tz51Azf/y+X6FR6JEY/uNGJkkQsEqevuxw6Oc7Vq1cBeP3111F2FA5FDyGOxbhR79Nq9okbCgeyESqXKyi6wp5PfXwhjL/O6JV6bF8osURAP2OQKPVRNHnYrNFkEbtu0rFdMlGNnAstP6DWtenbAVFNxPUCvCAkDEM0WaCQ0NGVgTqma9noioIvCTT6Nm4QIEZUQi1ElGT8cp9wIr5rbwCIagrbLYuTNwMRURFxe4MNLBqN0ruH4uOduDVEnz6RZuvdLaaMGJfrXfLxKJ5jDdU7TcsiEYsTCgLFtklE9BnV4ow8lKVYL/5Qimov/ewRzJ7NjbMbdFs9UukkxZ0ysUKU53/2KHN77xYRGa4fweS5vzVFbUtmfbnI4WeO8MnP7if+MQU5yuXysDNxS93vlgjX7cW4D0PuUI6DT03hvrrKguexXukykU+jKxKWaWF1LFRZQTMGQWEQhDRMF8vxmA0ETpyc4ODnDhAbGaxB13WpVgYU8ZUzRXY6LlUxRIuCHsCoILB3T4aTL+0nfUelPT2XJrUnRfpInkuvryO5AXExpLxZhgBq52rYFRsjZ6DEFJSEgqALeLZHK9LBsgWC7Q6SKBBTpd37VRT8ts/2W9scyhxi6vAHXoZBEHD+3W0a2y5ioBBJxYmFKscf3Y+d0bm41SKViyClNPyGjVfq4Wy1oe0BLnLfp9ls4qs6HDrI2EwKybh3yBM3FCwv4Eapw2TKQJKEgeiXJtFab/3UJnq3cOLECd5++22eeeaZ4b9tbm4SiUSGMd7a2hpHjhzB933W19eZnp7ma398ies/WEcQBRrVHl/5xZP3+4ghmqbH/MUqETdEfIC1p2naoKBi6Bj5BHtzBa426piBxObmJqIoDveSer1ONBolEokMxdDuh3fX6ry5XGMkoQ88IV2fSLnP9GKL6nYHzwvAdFAUETGlIh5OUEtrbHcsHk2N7LJfkvA5ciTHpS2HRKVPlxaxTBLTtIhHojibbVoRiUNHR5hIfXSXKAgClq5e5JdfeIg/fGuRoimgR4KbHU4XWVGGTJisIhIZi9FdaSGXu7gZg8m0QebOpPumOF5o+3gC+Hkd4+aMWiwWY3t7mwMHDuxi8pmmSaVSoVwuUy6XqVQqwz+uG1Iti9SKJrl8gT37czz02CyyJuD4IaEfINxjFKljeQNWRt3cleSFDI4vcFzcwMcLffq9Ho1ei0D34VqDC34H3dzhYGaQ6ImiOIxZdF0f3JfSaXRdH+hi3Of3b7fb9Ho9fuZnfgZd1/nBQoXXTI9+3UTJGHzq2Vke25/HdV3eeuutwZz7Te2FV155hdOnT/+1mtv7oRO9Wwvn3Llz/M7v/M59n/fjrrrZLXX59u9d4MZClSAIKeQjfObnjjP1yN28/TAIKb5fpN11WAgBPU4qsMgmDTzfZ7vapG9EySkSxQtFzIjJ5Mwky2eXyeQzXFgvE97xdamyiOMGBAyqQLfPZtwP11cbtBbr5EfuDuRgENioOR3qfS5fqvDss8+SzWYJw5A/+pM/4otf/OIwyet0Oui6PqRiSJKE49x/9k4QBI5NJLm202al2uWGZSPkDFIrbWQ3oKv4BIrOetPFE0zGEgZB38Wr9FD3plH3JAnDkJbpcHoqgrAnwZVvL5KoeDSbRYjIxKMxrHKLjgDe4RzEFK6XOkymI0ymJPyWDUGAGNMINIly2+aP3rpB1i7yv/7CZ9na2tpl+5HP5x9IoCShK3z69BSfPv1BMLK6uspoYfC6SCTCnj178DyPjY0Nrl27hud597QY6Wx3aG+16RgKoalzYDo9qDKHg7ki0+zTbDYJg4CQEEVRcQ2R670ONOoYaYUVp0vnvSZSVsELfXzPx3E9GlsexqaNPyrRj9r4coAkSUhRnVhgEC1bLJQMtLH9fPr4zHDTe1C8d2aTl//gInbbZnIszdRkCrfv03yvxIWuz/TxAjde3xjMhI7EECSBfsOiudVm7GiBZ547yOKiTz6fZ2Nxg/BSSG2kTSR8lGrXJqoq1Ps92qbLqUKc2o0aow+N3iWl/+OM+lKdcr1PPQjITSdxOw5B19nlNSm6AbIs0ZMEJgIoxAz6tk8QegjC4AYZ0yUMSaC8vUFoTOAiD5J2TccOLRRZpNOzUH0R0jqxRJxut4duiwR9Dym5e3+QhEHSEIQgMVB39N3BppRKpdjZ2blvore+1uD82U36/Q5f+NlHiUZVErMJ9A2PTkJjudEnZqgEVpfQ98nlcvRcn2bfQQldHkkmSWQiTD88zcXFi/e1cvgwxGIapz+VITcJzWqAoUXJFKIcOTlGPn//BLVSqaCqKnN7Rzh2PIIs7xkWZz4OKpUKkiSRyWRYXV0dJnqqOpiDeVAIosD0M9ODPeH7S0i+SLvvUgtsopqCqinYrkuz1sTyQrwgZESXmfYEDh8tsPfTc8MkD25SszoKtet91gSVTkolpkoEnk3HDSg5AaVL67iew6lfegQ9qe+qhNu2DTIc+OTNRDmEdC/N8jvLlOfLzJyaIZKOoMd0VE3FCUTajRjnVnZQIx5hOKjm5+IiM5kIYyl9YN8AkIP2ZpudszskJ5JDpcuXvzbPmb9cIPBColmD9HSefq3E+9+4wcxz02TiKsW2Oeg+FyLIuQiOCHbPJZ40GJmRCfvgnJjGLyTum+TdQspQqPUcyh2bsdQggVaiCr1Sj8ALPrb69V9nOI6Hqj74+pJlmVgsRqPRIJ1Os7q6SjabHfrLmqZJGIZDmf0wDNnY2KBR7CGpEqEfsL1WpVgsEgQBkUjkvutro2HRuVGjkNAeaDZY1zRs20I3BtdsMhfjkJxivWFxuF7nxIkTwCA5Wl9fH1JQx8fHuXTp0j0p1X3H4+xqg5gmkzAUAtOl/+4OznqLUUkkjMtslduogogWitgxhe5oBN1QGJdFPD8gCMKhmEu/3+epgyMUKx0qFyX89QrWzoCy2ai06McVJp+d4cXHdivRdiyXnu2jK+IwcQyCgHfffZdjx44hCAJfOjHKe2WftVofP9TpbJeZm/7AFsILQjRVZjVvMNlzmREEcvfoGnZ7PQxJwy31aI5E2XdklE8+Ok1xZ5vNzU2uXLlyV/HNMAymp6d3xTitVoul5S2+9odXqS40cG2Z7fU6W+fqnP3BEvKcSrflU7hcRy1EURQVRVEIAh/f99lsOTilHm7XxEup0L+lvyAgigJiz8PWJbKFCJFohJGRApIsY2giSqDiJSZ47NTsLpP7B4HvBbTaFpGISiKR4Mtf/vLwsWf355hMG7Qtj6ShMHuz668oCjMzM0xNTdFsNrl8+TKvv/46q6ur/O2//XdYuFYjElU5cuyv1kbqh070/uW//JcPREP5cceFH6xy9XoZZTKJroisbbV561sLRCc03r3wLvF4fChX3y12aW20qCoirbbLRCpC4Cu0Wk0EQWR6JEula7Nmu8T7DuFayL5j+9AzOo3lBnvHsrwxv41lKej6YIi9ZTqIZotr57ZQJBl5UeaR7IfPRi4vVhF7LvLU/ZWbtKiO7IUsX97m2SdOcOLECaLRKNlslldeeYVGo8HDDz9MuVze5U8zOTnJ0tLSfQ09AeZyUaKazA8WKmSiCulcFGW1TXC9QhYBRuKYKY1i00TRe6QyBvrhPPrxPIEkcmOzhQIkhIBCrEviZ49x7XyR5bfnGSOC2XcRJmNk9mexYwody2V/IU6w06W7UMOr9sEHMSKjziSJjsqsVItEp+dYqVvIQTAMIn3HJy/lefdr7/LQyYeQNInERAI99dH0iVKpxMjI3bQuWZaZmZnh0qVLdDqDQfBut0skEhlSI5fPLLO9U+GqP9jcSlYLLxgka77v4fmD/x8IRQgEQYDreXQ8CTewyDcVRo6N0bjhITZ94hEdWZaodx0Mu01qNs7EM5PYZg9VUTBu87kJEi7jN3Y4f6nE508f3eW191HotG1+8OfX8GyPsSO3JcZpSBaibF+rcGw8zqm/dZArb6xTWqgRhiFKXGXvk1O89LNHiN1MZlzX5cmTT9LzevSyETa6NmPJCKIIyVBhu9mnQZR016FX6f3EJHphGNJYbtBgUBhRYipMJbEXaoT6BxLPYd9F02RMQvqOh1Vt0+t2mBnLI4nicMbO83wgZGtrMKuRyWYJb6p3uraL4YOjCZBS8YKQWCxKd7OG1neRkrsr5pYXkI2qSDctPQI/QNE/qI4Gwb1JN72ew5//H+eoLNZBFNDk63zl754keTzJldUrrLx8iYOffJq1fg9X1AhlgcWdGplEjKmYQtYMyScNZj85SzQfRdvQ7jl3/FEYeLrWOXJ84OH2IPeoRqPB6OgomUyGMAzZ3t7m+PHj933++lqD8+9ssna9giiJ7Ds5yiOnpxAEE0EQyN6cg81mB0lRGIb0q33MZZPrC9eJRqNoSY3UTOq+/qIwMISffW6Wtt9majNkZ71FxXIpdx1sSSQUQrRAJeL0SXoeozGFg8/tY/qJ6btsAWzLZvXsKtd36lRUkZGYAkEAkogqikSUkFIYcuXSJtZ/75E8mERRFCKRCOPj48OE9Xa4pkvn3Q7G4waLxQ3EjoiqeqQnE1ypdGn0HCRZI6oIyIqM54fstCy2GiaT6QiPzKSJ3KTUx8fj1BZq7JzbYfYTs8xfL/PutxfR4xrJ2xLW6KMijXe22HhvhxMv7WdLlqh17YEnWBii+iEjozH2H8xj1/q0XJONVJToh9jB3IIiiwThYK3dgqzLuB134EP5Ib/VjxO+9qdXuH52i6NPTPL8z8xx5swZbNvmxRdf/NDXTc7s4z//3quk00k++7njwyQPGGoO3MKt0Yo9xxKYLQvLsaj0rvPv/t3LxONxXnzxxaFC650QRA2/ZyMWHkw2X9d02t0Ocd8fGIZHVNxql0cfe4ZU0uPdd9/lkUceYWFhYZcSqK7rw7nWO7FU6VHp2MwVBvP4/XNFnNUWykRsUDxfrTMzHUPUY/Q7DvGyyeR4grGjWQRBYL3RZ6tlDufFer0e1WqVv/fiMW487PDn3ziL50foSTKxrMHpE2M8vCczTOZapsvbyzWu7bQxXR9VFtmXj3F6T4b1+UscPnyYaDTKysoKR/fu4cBswGq1x+XtNm9faXGj3B2eiyqJHJtIMpePUrxSQd/o4Ky1kJIaYkwFAQLTxS/28DSfeiFC/vQEnz0+hiJLTE1NEQQBL7/8MlNTU2xubjIxMXGXF+nttMizr+/QWu6Tm04iKCKO49Dv9uls2gT0aWkSrDuEVg1RkhElEUM3UI0opi2SskBNRVEjd6y5MMTvmChjCUayCTRFQpFkKtUKsXSWWN1ia63J+sE+c/kH76itrzX4xv95mdZOBzWi8ORL+3nimdnh44Ig3Pf9bs0hZjIZrly5QiqVolqt8u/+zR8T7iRRYyrSr57i4JG/OlbAD53o/cZv/MaP8DD+eiIMQ+qbbXxdpnBzk++mDBaur3H+//0tooUoc3Nzw5tg80aT0k6JGze7b+22h+d7NOp1RkZHMft9NEIqbYeVXovHnKP0+30SswkqVytM5AzmRjOslBroqoMgSURViWN7R3nvz7+HP+azV7y3KeitIEwURWzHRxSFD62GabpOv9rG7cjslMo8cvTgUEHpZ37mZzhz5gy///u/z5e+9KVdrwuC4CPNk3VFYiShDQaQNzvISy0cOYRDWaSWi1Cz0FURO6NTmUgwcWoCLyqzvtpg5WIRv9gjbyh8p9/kyFOHOXlK4JlTCQrjc4xODCo1Y2kDURD40/c3mclG8Tfb9M9sE/rB0PA36Dk0z65TUXuc+uoTbPY9vndxmX/4ySMEfkDpUonK5QqN7TZri1XslasYqkyuECOzP8PYw2P3TfjCMKTZbKJpGq1W6y7+d7fb5Tvf+Q6e57G6usp77703MIdPJMjn8/Tf7dNpCzRFjVxURpZlNElDkmVkSRr8V5QG4hW3/Yw7TZOD2SiTQUDmuRyXD1eZv9agWbJRZAEzHSUmCUzNpFFVGV1NYpsWrWaTRCI5oONFFHKRCAvLVYpNk+n7KI3dC5cv7tDe6VI4cDclW1IkYoUYq5dK/Mr/8zme/MQeVhZr+H7AyGiCmdvk8DVNo1Ao8PRTT3O9cp12WmdjuTY0CBeEm6cd3vyHj25k/9gg8AICL8AlRL55wspIFL9p4ZV7SFkDBIHQCxBuxqflcoV+18eM6ri2jXfb2vZ9n8D30SMRYrEYjuMihCGBBF7DQo5oOFkDXRvIyScjCrpm0G11SI9Eh8a6QRDieD7dnRUue2UCPyDpJTHuY49yO9oti261T3IqQa9mUtlqA3Dh6gWW3CX0WZURGaZTafoSeIIIYRS7WkfoOux/7CCTT04OLRNuSbV/XI+ib3ztdfbtPYJtew+c5HmeN+zmN5vNu+aQbse1yyW+/p/P0yn3BsmUH/DGjRqX3lrhs79wkOMnBmqDnU6H2dlZ+tU+W2e3aCw3qVa7VNttkvEEUUUiMzoQ1xp/dBz5PnNI7U6bqYeniD8ZZ2KtSWOpQX2rRa/dx7FdkELMSMD08Vmik1EaYYPqlYFH660kWRAEvJZHeb1NJ6IxljSI3KOb44o2gWwwro1z9PGjwODaqtVqNJvN4fM0TSObzdJaa9Er9YhPpaic2aa2vkoAeCMxUkcLTKQjCAJ0O10MXUWRBvYsrh+wVu8REvLEniyKLCKIAtGRKLWFQff+yrtbOH2X/B00UiljkDiSp/rOFtX5Gp/47D4qXWdgFSICgoS708Wq9ZBVmeRsig1X+jDB110QGDDYhn+/bV7wJwGm6XL9nQ2qyw3O9Hq89f4foesqY2NjrK2tfehrv/GNFRa+s4Oq1yFUeOmlvZimObRSME0TSZIGM2c3rXye/uReZvcnicWi/NZv/WvEm7YwyWRyVzHkdmiqjue4D6z0q2kadrVKq9VCkmQESSSiG/hOwPT0NIlEgm9961vMzc194Mt2E8lkctilvIUwDLm42USRRWRRxKv0cTc7yKNRBEWi0+ngOjazszf3pnQEP2FBwyYSDArMWw2ThWJnmOhdv36dxx9/nHg8zlGph/rcJEYqRzKZImHIu9Zj23L5b+e2WNppk2k5pPwQV4L3GxZnLi/yv3zyMPF4HNu2hzGbIonsH4mzfyTO4YxAs+eQTGeQRAFDkRhL6vhhyPdG4rx/qUh7vU20YqJ2HCCkY5sEhST9iQRj+9J86nAG0e6wsVHBsizW19dptVqEYcj8/Pxg5EJRGB0dHdqD3aJIuq5IdblHfjJDZjw5vL8AtDIdAiD37BQLX79O3HexYuFAkMvzaLY79NoieqNHLyYjeRKyLCPLCrIkIXYcTE0gmlHRbrLVRHkQJ/lSiOSHhF3ngZRLb8E0Xb72excoL9ZJjMfpNy2+98dXyOaj7D/40TZUiUSCjY0N0uk0Tz31FM888wyCIPBH/+kMl1bWEUQP03xwNsf/DPzQid5PAwRBIDOZQLpaotF1UBURt2ly7NAMxz9/irPnzzIxMTHseJWtMlbWImq7BGGIJgQIrsD0zAy9bhctFgMvwGq3QRBYWlziknmJSrHCyfhJ6hfXmB2NECnobNeaJDSd9UvneL1uM71/mkq6Qr/f55VXXmFsbGyXtG2326VcLpNMJrGtDh3LRKs1h92B3ec16KbsbJfJTceYHC3Q6/WGG7UkSezbtw/XdfnWt77FCy+8MKzQhWHIwYMHuXHjxn3n2lqmS7Xr8MhUisqVGqVWF2UkQioRw88LCAUbQpBOjVEVQsq+x/pfrtCZr6MBk5NxBN8iKsWovl/km+dL5I6kePELs4xkjeGm/PWL2/hBiCYIdK5WB52j8Q+qgK4KFaHDGBmoWORGIqxut6j2PJyL26y+vcW27bITBlQTSa7Va6RjUZKtFrnLq2TO6CQeThBowV2JXLlcJp1OE4vF7jnX1u12KRQKzBRmeOrwU6iKSiQRIYyGNNtN1ivrdE2JWtNk9AEUBW9BFAS8MESWZKYmJjjy2CHCz4f0bI9Wu80ffuc6xfU6tWYV0ZTRjAEvPZFI0Gq3MHQDTdeQnRBlucer//51jhwYY/rkGNmD2XvadwRBSLVjY9sem1ttQiG8rxJmNKVT32hRq5nsP5Aj89jd4hdOz6G25rJ+9TKqq1DdaCF3LDQCyh2buC7TtT0MVSZ2SwHvATqsPy4QZRFRFlEQ8G51yCQBdU+K0PJwd7pISW2gQisMHsvnsrQ1h5bnDoKV22Igy7SY27sX3/cRpYFibRCCi4foQ5jV8TWJhBqw3bJpeC6q6yCJGjvFHeLxBAFQ6TpkIwopLeTMmTMorsJzzz5HT+6xubmJ23dZOb9C7UoNAhAVET2jY+QNgjAkOW2wdr6EFlUZmTPY3NykUCjQaDRgBI69eIzuZpf2apvQDZFkCTOfIbU3RWZPhtREavf3dFvx6kHwR3/wKtdfM7n87TPMnBhjdnYa7R42DKsrdS69t8XSpSK9bo+xPVlkYZtY0mFqaopisXjP93ccj+/++TV6dZPxI/lhINrvmpQWalw+U+f4iTmazSapVIpuqcvyy8usLdXYEqEWBrQFAd11iRAwutVidKtFY7vByJMjeHh3JRSbm5tMTU3R6/UgBsZJg6kTUaRAQlM1IvEIxXqRyfFJ3P4gmFCjKpVaBdM0SafTJJNJOmIHUVgnkL2hZcydUCURk0H36pZwjCRJd3XzLMuiVCqx/MYyvX6P5rZFr94nNZqgVOvRr3SZ08eHHec7VQYVSWQkobPZ6LORMpjLxwiDkGbfYftimaoicuPizl1m6TCgtKozSeRSl061R3OxTjSukrxpYVTpejTLPXIHcuSO5GgHfbSSh+kGw8DwfgiCEAQwlA+uN9/2ERXxw+08foxgGAqHTk9xXRQ5+sQkp555ljfeeGPIQrkfPM+j150nE09i2jYrKxv8m3/zZ0Nz7BMnTtDpdPBvMlSCIBhey57XY3OzRDQapdVqMTo6iq7rbGxscOnSJZLJ5C4th2arjeXadFodxOCju6iCIFAql5BliXQqDY6P43uEeMN54Vtdlmw2Sy43UC6/JZ52Z6JnewGVjk3qpgCYW+wSOgGiLhPcnD+80yNQTGh4Gx28cg91NkVUk9luDQzNl5eXSafTxONxwjBkZ2eHh04cY3FxkdHk3ZS+y5stloodJja7uEsNfEFADEIUw8I6McVC3WP/JGxvbzM7O3vX6w/NjHP58mUOj83iuu7AH69Rx7Is5jQTeQ8sRCRW0hJONyTwQ5xAYmpaYm/aZTbewWl6dG7GMtlslq2tLT7xiU9w+vTp4ec4jsPq6iphGDI2NoYkSUSjUba22uCJGBl9V5IHA6GpTrXHY4cKWKZP9bV1xlQFeSqK53pUWl36VhNZMvFvXnee50FoIvd8XFkmmI1jOG22tizGRkYRZQnDMOj1ekSQCP0Q13/wwkyl1KW+3Sa7J4UWVYllDLavlNlabz1Qonc706Xf7/Paa6+xtrbG6soOn/y5v4MRVTl28q/WouWBd6/f/M3fRBAE/sW/+BeIoshv/uZvfuRrBEHg13/91/+HDvCvGiefm6W80WJhvooVhszkIjz52QNMnRjj0PFDu+YuFEMZGGLrMjfKLQpRZUhvECUJWZLpOQ4RRWLp/etcfu0c2r5BZ+PL/48v07/ap7nSJKXCkT1pqqUqkdQ072y/Awo8+9yzfOITnxiqYFqWxcTExLBKdWsAOojk2TzXQrVDjOzd9Aff8ylt7CDqER57Zj/JWGToLeM4Dr7vs7S0xMzMDJFIhN/7vd/jxIkTQ6P38fFxLly4gKIo96ya1/oepUqddBiit+qYXgu7B44Piiwgh6A0bDqVBiVJ5J13t4iut0mMRSnko7hmm3QmRbfbJZmO43VcSufK/MBQeeTxJo/c9KpZrfVJGApetY/fsJBHP+hM2ZbN9s42U7MzULNxNtt4iYC+4/HffuebiFfqrAgi1UBEwcNQBDrNJnK+QCWQ2JJF9i9bTMkC0y9ME01Eh0mcpmkUi8UP9Qp6+c9fJl6Jk3STLFc3CELQNQmTHiPHR9B0jZ2dGpbl43kq8n2Cr7t+uyBEEQRESRzOjAiCQExXiOlZ9u6dpJsdCNCIcQ3LMul1uzRuzlW2223kro9w3STQRd5frrNddZm+VOLpzx9k+ukPuPaeH3DhaplzZzYpz1fxnYBesUO/2kcvRMlkonBHDO57PoIkIN+jwFDfbnP5vW2und2kvNXGdn3Mfg9vvU1Y6xCbzWDsHaOXjRCNqxwbS6DULJIHs0RHPp4C419nCIJAei5NerHGRhjieQFiz8Wt9fH7LkHLxt1oE5guHiDFVdRUhLGpMXpda1eS1++baJqKJEkoioLZN9E1Ha/WRUtEkCJRvIiCJAQobp9Y6FDpBsR8DV3WMJJRKu0OmmEQkwM+cWSSVHQOVRSgDqm9qUGwWxHZvlxlbb7DpuISAIookk0bzBzMUTiS52/9wmFKn9yLqokcPzHw3TRNk5/92Z8lFosRhiGxQoz8yTye69HpdpC7MslMkma7ydrG2lDoaXt7m5GREd577z3Gxu5/k7wVTBaLRW5cKWG2AuJjEVYubXHh3A3GJ+O7nnf2zR0ufX8Ts2mBNqBZL72zzer7JaZOZvk7/7AwEIq5GSDejqWFGvX1Ftnp5DDJcxwHhJDsVIbVKyXWVsvUGwMl2dVvrbK+1OKGLOD6kDAkClEZ0+zgonPF8ilqMocvFDFSBvs+vW9XUttoNDhy5MiHdhgbOw16iz2uvHYFu2MjCAJ6Wid/JM/E3gl6To9/+2//LZqvYV+FMDeNFVF3mYTfguUF5AQBJXLvff0WdF0nn85TpEhsNoYSU5kv1CguNzBtH3XcoNGoEYlEicUHhTDLHMxQ3YIiiSiSxEq1h9B2WLxcprHTxq/0EVbqNPou3Cx05GfSCNIHxyOIAmHGILE3w94X91Kdr+KaLqIkkj2YRdIlxk+NIykSVtViOmNwaatFKvLhyr1t0yOhK7vUl52+Q2I8cV+Lih9HfOGrR/nMFw8OZ/S+9KUvfahyoGUNlFu/9Lcf49vqNYIw4OlPTbCzYfDyyy9z7NgxyuUyAOl0etjluR3NZpPr16/z6KOPMjU1RSaTGSaW9XqdWq1GOp0ezO2qbc6OrKI5Enr8o+mblXIFXdMHXXlBwC5bkNKIGgKdTof5+XlGR0dRVZUzZ84Qi8WYmZmhWq0Si8VotVq4rjvsgHdtn3qziSGLBJaEU6rhuyZWa7AvxaIxTNO86zh8s4dTqSPHfXo9j7Jr8t57VVQ9gnfTRHxjY4Pp6Wls28bzvLuUeIMg5PJ2m1jPxVttIY8MVEcrxTKRtkjcEbm8WWda69MsD+xbLMu66/fb2NigVqvdVYjOZjJMjI/zycc0LH8g3Le2vsHczBSZqHaXQfwtlMtlnnrqqV3/pqoqBw4cIAgCvv/97/NHf/RHpNNpfF8ipT2B2XYwErsLtGbLQotpzE0kyRRi/HkQUHl7i8RSEz2jEzOiRGIOkZg3EJELAoKOhWe69DQJe0Qmrtu4dkit32ZjfYNUKkUsFqPX6zIuZggl4WONo6iajKRI2D0XLariOYOZdPWj1F7vgUgkwtraGqVSic9/4QWee+7Ix36P/xl44DP5jd/4DQRB4J//83+OqqoPRN38SUj0YiMxXvpHj/Lobaqbt6TGBUHYJWKRmEwQLUSJLG4jiQKOqHArVIjH4tSabWp9h8en8kx9/kXmxCl+8P4PGBsb473L79GoNkjGksS8GHE1zp7H9sAcEEBP7A1nAQVBYHJyclfCd7ua255CjKlHxlj7zjIFQ0VJf9D5s0yLWqlCLEjSnehx+tQeEondviObm5ucOnUKTdOYnp7m6NGjvPLKK8TjcWZmZpienh7QM2QZI1XA9nwkUSBlqBiqRLrnkNsG1XOw7C4TEZWJfWP0fZFa18Hv2aBriJEIWsMiX3eYOzZGPKNTq9XIZ3Nws5pMECJGRJJ5nZV3NhC0KNlUAtfzabXbgyqZG0AQDruXjuOwtb3F1OQkiqrgKS6h6eG6LlFZI9bRMQvjSMDD6QiaLIMoUK/XMTQdIxqh0rHpiwJRX2U8Nk5mT2bX93MvgZVbaG22uPH1G7TKPpsnNVq2Q4CA7rhsnD1H+MdlLLtHKp6l8OKnqTQ7RFWRaCSyyxT+Tnh+iCBC3ActraHF71Yk2z+V4sJoFGezi57QhvYGt+C7HtW31ii2+zSmMuSVPk1Zod1yiby2Qv5wHiNj4PkB//3r81x6eRGh7xHPGsgxBSGqYl6tsvPaOv3jBSbmsgi37antUo/MRIKp6dSu47rx/g7f+uNL1IpdhIRGcm8GQ5PZ2dpmxdwhJqoofRn/cpnISIyjj05iVEwSU0mmnpr6iZsHzuzNUMhESK03qFyvEXdCPM/HlkXcQoRAExHWPcKeg+aHeIKE2rTQBKjUm6TiURzXJaLrCDeTgzAIEUwfp9khSEikDo3hVnt0L5WJpzVqpeJACTKZQNN0fENCkERmRtLEsGhuFXnr1VWOHjvKwfGDiNMiE3smWHljk3MXN6iJIm5cIww9VE3DDwKURo+lH3SYvlhk5FCE/Z/ez+//4e+D8Bz9fp+XXnqJ7e3tewq4dLqdoVACDNbtrWKVIAjDOZDbVX7vhVKpRDwe59HHj/L2zjxWxaGwJ8fhI3O7FDPPv7fFtR+UUFQFY+/AM6rTaQ/URJsmq++W+XrqIs+9OM2lS5fuUshcX6/g2i7qzYTBdZzBnhKN4gguvUaP7e0KU9N51I5K0BIpJ2PoQcjk7WtVEIjH4+QC2Gn1qWoKnZUOTtsh8AP6lT6+47O1ucXU7BTtbpv4WPwuKltnp8P5PzpPrwG1MKRBCGFIriiSW6gxsjdB7HiMsbExbizcYCQ/gmL7VHsOhiJze6PUdn2CwKegyvf0ar0TdtvG7bnEJ+JIisQnP3eYN9/dZKXeZ2o2PujqhAHFm+I9/V6fMEigGx9cr0lDYW2xRmmri+gGRDIGsqEM5PQnRepndqie28FpWIwfyiNIIoIi4gcBgeux/9QEow+NMvrQKIEXIIgCZsPkyh9dwTM9JEVC0zUKYkhMU6h1bbKxe6s42q5P33V5eDSNerOjF4Yhbs8lMf3xrEV+HHCnEMv9xjBuMYVuiXDs+18Hwf7KygrTp08jCAJPPfUUqVRqOM6wtLSE7/sDD8pEgpGREVKpFF/5ylcwTZPt7W2SyeRQpf2WWmGj0eDGjRvEo3GSc0l65xtoXnBPVhJA4Ps0Gg1kWaZQGBSPdE2n48HosTEe3T+J3e8yMTExLMrOzMywuro6pI3Ozs4ShiFLy8uIiTx928cVPSTNJB5VycQ0+lkXq+TTdh30mzHRvWiljhEQzabRsmlMoUe/3uTMsklru4Ll9ll82OKRU6MDi6J+n06nw40bN3Yxsxw/oNZqEwkYxD66TK1aI5aMI9sDL+S6ZbO53eLRoweJRCLouv6B5oAXcP69LZJGklqpwdNPP3zPoitATAYpcJnOxcl9iLVUu92m2+3es+BmmiYbGxt4njf0Dj59+jSGnOX693cw4irGTeEXs23Rq5s89sWDxGMaceDnv3CYlwsxVi6VaK+2UPouSsvG7zlIPZdQk3BVhXAuQXYsxnghSui6mGafvmmia31a7Tadbge/JtD0e/QLJu+8VqQzXRjaPHxYsWx0LM7Rp6Y59/Ii3UqPIAgZP1Lg+A/RhSsWi7zwwgt897vf5bHHHvvYr/+fhQdO9O4cwr/fUP5PIvSkzvQ9VDbvhKRKhIWQ3JrKwymd+bbFVqOPIkn4gU+nZzGXijLqQuFkgdMvnmbPD/YwNTXFvn37cByHUqnEzs4OxWKR+fI8kUiE2f2zHDhwgNXV1V2mpbcHQVtbW6yurpLL5YhEInzx8wf5465D8d1tIuUO0bRBr9fB7nq4voCxP8PRaZXMHeu71+sN5sVu23x0Xeell17i7bff5sqVK3zpK1+lo6T5r6++h5Hr4frB0Pvv2HiSA6MxRhMK339vEWNUI1PXiIYS8YRKThJxbB/poTzNuQzGG2uMRTWS+SiNep3UTbNV13GIRKJD+XAjEsGsu3Tqg3kCXdfJ7izRtT1EQwZFJLA8fDFgc3OTycnJoV9XYPk4CYdkcpzWZh3NVylFRLKyiHabH1UqmaT0/2fvv4Ilue87X/CTvrK8rzren/YGjYYHCBAk6CVRpEZm5ObyjtG9d+fGPGxMxD7sw+7GRuxMxIZ2n+7sxoxWmtmRNCIlUiApGtEBJAjXaG9Odx9vyp3yLisr3T5Un+o+3acbIK92huZ+I/CA02UyKzP//5/5/r7fUgk94CcR0Cg0DCpun8qtytDQ13Vd3HvEXO5Hv9PnyqtXKFUsyskYrgea59DvdCg02+RUDc/nY8ZVOHPoFF5A5WrPIhTy0+t2sbpdNE3b9/vvoWH0iesqQccjeSR54FzPXCpI5liaUr6DnO8M5gpEgXw+T7PRIK5FcMt9+qMRZqdjhOVBglFHYOXWFj/8tklwMsjKpsvSdwokogGC03dpLamIj27bxFiqUL9cQtUV0mPhAfWq2MZ1PB770PS+zeX2+Txf+/9epN7rkz2aGnr3uM7A5yw7NQpjHqlAml6uRTPX4uq727zyTx9n4cXpB4QlfhEQyAQIpfzEv79Oq22ypUJfEHD7zmA4XpdR4hrhnoUpeLQ8F/JtfHiUvA4trcLkxCRYHk6/j9d3wAUt5KMe6JIcTdLPtbBqPejaSE2LTCxDtVdF91T8fki4DrJo0uy49E2bWtGm1e7i1G7xzDOPkT2ZZfXtbS6uVCjFggNvIqeHYfTIhMIgCpi2Q7VjUW11mXqrxfLKMjW7xne+8x3+6I/+CFEUD5xv2tnZYWxsbN/fVFVlbm6Oa9euDTfmVCpFqVQ6UAwEBt2APbW/lz8xitnv4NPCHD2Z3Zfkua7LhTc2sPsWgYSPcGQQvMuygqKqRNMqni2yda2O/qunyGbtBxLMvqlzLrBNt9lD8UmY/f5QQrtT7xGI6ITCKmNjY9x89SaVvk3TchiJ7O8MqoqK1e+jqCrxgEbBsEgslfH+4ioIgzWk2+kiyzK3b9xGVETC42FSR1JEp6IofgWzZbL2vTVW15sUwwH6rodfkTBNi1zHJCCJHLrY5qRvhifOPMHU1BRHR47y3hev8k6uwophEA8H0FQZo+9g2w7jpsfsySTR6ej73r+u5eI6Lp7g3fn9FVIzMcq6hO7XsfoDylg2k6XVaaOoCoqmYhi9YexgNE2aSxUUXSExPrgebqeP23eIJHW6ARW32KZXMNjNdwgmdBzPo93sEz0U5/D83eLbHrvBn/ATm42xe30XR3LYXN9k5dYKPdGPMnGIfN0g6leHvqq249HoWZh9h/l0iIV7BEDMuokW1ojds/79MqFer9NqtfYxV/YKbul0mosXL/KpT31q37/FYrF9NMhms8nGxsaQ+dTv9wkEAty6deuB0Y+999brdeJJm1XZJbTbRR15UASjZ/To9/uIokQsPnhPzzSRWy5GQOa5x0bQZJGltbUHjN6np6eJRCJ873vfI5HKsFYz+f5SnY5g0ndcXBduFVuoksixsTCJhE673cIWBeLJB32MYXDfin4F+c48c6FYx363gN1yCSR07JrF0vdLBPwxfu1zg7UsmUyytbW1jzLreR5jxQ12GlXCqkRlPY8/HUHzZBzRwpAEEpEgc5OJB9ZE13V59a+ucPUHa7i2S7dn4PR8fO53TyM9RIGyWCweSP+8F5ubm4yPjyNJEo7tsrGRp1zO02zVh6qbL7/8MrlcjuPHj3P27Fl2doqUcnWKt6rIm9LAqkSVWHxmko98cnH42emwj3/8kXl2Hh/j2lqV61eLeJt1irpMotjFGw8RywSIhXX8yh29AlXBH/Cjtdo0anUS8Tidpo2db9D1K6iSgNMocfVqafg9mqbtM3ZPpVL7GBuf/twxUqMhKoU2ekDl8WcmfmJbndXVVXRdZ2Fhgfn5+Z+p4vQvBvH8ZwTr6+vMPj1LTB/4RwV9KrWQRtcDVQDb7BIx+mTPpJh8fuA7+OEPf3j4flVVmZiYYGJiIN9v2za7u7ssLi5y/vx5vv/973P27Fnm5+cZGRkhm80O5YzHxsboG32K60X6Xp/x6XF+93dP83/duYTrZagWGvg0HSercuq5Bc6eHaO0fo1qtTr8DBhUJO5V2dyDKIo8++yz1Dom/6f/+E2U5CRdRyGKTSYWxHE96obFd5eKnN+sInbKdAwDYSJE9tAkznYbu95CUCUCiwmEY0m2K12C9T7hlJ96vU4wGBzaOFiWjaLsvz0DcT+9gsPN1U1OHV3kUCbE92+WyKZDKOkAxmaVvFtnbGx82Gl1OxaCAE5Ko+uIBB0PAQHDccneV90VpcHQvmM7SLKEKAo0RYF2sT2U195TnHoY6ut1lq9usaFIqLZFq7RNRxAIhkLMjmUYTcW5sqJxaGqSoCEgtwc+acVmj0zYjy4MaKfNZhNFlofduJZhYbse0z4V3ScTmz04+PApEp98doovt/vsvptDv72LIHl0qlWquV0KbgGf40dKZIgFdIKawtrmBqo/wchIliefPouU0Xnt268hmH3aQCvfRhQHqli6rjN6PMOOC8atCsV3drAP9fAcD19M56nPLPLUPfTPaq7Jt794hXqvz+hs4h6lSJvy7i7pVBpRyuK6g/kyZTSEz7QprdQ4d73I4kd+sfzz9tDaGXhiKikdoddH7lq4moygDH4gHwLBsA/JBS+q0ZJELMtFqLWRa12EpE5nt0EwEkLUZMSkPpgTsWzUdYPujRKiLNP0XKKHEowJIt1Sh1Q6hW3YoElsX61T2+1g4eEFFbodF6ujUI7Bu6Mltsttig2LclhhJLJnrntHlKrTASAYCJCJ+NjpG2wiI5zf5cjHjpA+nB4+y/ej2+0iSdKBxYw9PyLP84YJ3u7u7oGf02w2MU0T0zSHLIPHnswe2G0vFtoUlqsIfmE4b3w/wukgpZtllpd2GZl4cD5weibGyGKC1fd2CI/7iSYGyajZ6dOpdDn5yjTpdATHdAZ+qhLIrsj9I4aaT6PVaqGoKprjUbtZptCwcNsm8rEUBcGlZJoEZBnNscnICt21OvW1OsFMkKkXpzCqBhu3dlnBQ+4YhAMSousQDPvIxIPstkx2BIHM7SqnTh1l4bkFPM/j2MfnUX6gsJlvslFt0QJCssK4z8f8iSTzH539YOqSwuBa7WzvcPPWTWLxGDUCeN4gKFfUwRhDs9UiHAkPmCS75YFK8Z1AubHZhZ6NmtHpGT08z8MpG4htB7VnEwsoNIMqVq1Hc7uB5bpIHoSiPhbCOlvfWsU4kmTksRGUe2iZqaMpyjfLbF8uce7NFVp1k9HRBKF+DT2h03E9aoKAIHgIgkhMVzgxGmE2GdinNtsutBl/evwXRu33J0G5XMayrGEscj8KhcJDiy/3IhwO73veut0uS0tLbG5uUiqVGB8fJ5vN7uv4R6NRfvXlJ/j3xXNsv1MkbZsEJ+7aejSbTTRVxfVcotHBM+jTfNTWC/SVMMnnJzg2Fef27dsP9eKMxWIsHj3O//K1t6iJYQK6D5/bYyo9+B5BgHMbNS5s1ogINnOZENZqgdDoAbZajotdNtAW4kgRH7VGk9ZWg3jdJnssg6SKiEHw2rD01jbPvzxLIqojy/ID9mOCIHBiLMLabptqwkXfkZHLNo7kIi3EaYQ1jmsW8zMPntf2VoMbb26hJ/yEk35qhQaXX1/h8eemhv6h965p7Xb7fT1RYdC99fv9fOmvvsPF10t4PZFIMswrnz/JY0/cLYb9wR/8wfCzx8Yy/O/+9ce5fHGLKxfWQBA5/fgcx06MPGB7IAjCwBor5udDx0dYKjT50g9WiFws4Vdl1OTd+HSvgOS6Lo5j0zG6GD2DqBrDjYdpxCU+8vgcn3/h0D5vv0qlwvb2NmsbW2yuCrQKHr6AyvGn0px9fIJ0Os0TT4//VJY+AJvbOSKJDImR5PCcfpbwD5boNZtN/tW/+lf863/9r/fJ7P4ywPM8VldXGR8fR9M0gs8FCWQC7N7YJbrdwrEcqrUq2WOTiBmRsafHPtBmKssyIyMjjIyMcObMGS5evMiFCxcIBALcvn2bH/7wh/h8PjKpDPauytrlCo6tEI3qFBbadLQi7eJ5Rien+e//L3/EysoaJ48ukg4PNi67Gadare7rCj4yiWkbbHgJvGiQTmGd6WCa5hvL+HwxBE0iNhYmPRpkp2OSLxuE6OHLTNLMJojPJ5D7Dq4sUpUFmt0+i6kA26pMt9clFAoPO3AAlm2h6/vbjZIsgO3SMQZDzoeyId5eq9IybZy5MKtXlgl5fopCB6QukmGjiiLibIDkTIrtVo9jUR21bODZDgc9i5FolHqjTiKRQBIFHMBzPKqVKoXdAuFweEg5uRfdbpeNjQ0u/uVF3rmxRDMS4cRoilQqjXJP11APwMkjPmoejAugIXJMk7nqeezUu0R8CkGfhubTBol+tU6n76HrOkfjfuKWS+ZkBn9yP912ICtfo1ar0Wg0mIw7tI7L7BZ8iDUb15emqVpMn5zkQyMTFN7Nc6tYJxgJ4iph1N0m4RNpQqMhLt3apV80GJ3PIgfvevf0jB7N5mCeQZsQ8NQwRsUisRjn0PERjp7MMj4R4c033+TJJ59EkiSuvpejXGiTPZoa/t6D4fAamUxmSOMS7/ymgiig6grxuRg7t6usXCtx5IlHU/d+3mD3bDZe36DR7JObDOOoIuNdC6duYls2oiAiBmSU6Qhuq49TNwmk/DS6Fl5cYsKM0BJEpEMJfHEdSVPA8zA36nRXymi6j65uY4kiYV1jIRtClyX0qI9WvkUt36ZZbGPhIUkiigeC4xEaiSI/HaFidNndaJNfz9FM6YydSmB0XBRVQdM0REEkGBoIaLQ7HTzPJaxJdDyF+Pg8I2qMZ55/5qGb3fvNtzqOw8TEBI1Gg83NTYLBIK1Wa5+cu2EY1Ot1kskkrVYLURQPnKvbQ8+waDXbjMymeZgEoyQLIHj0ejbp9DilUols9q5YgiiKvPyr81R2q3TzJt1iCTwQZJG5J8c5ejpMOp3G6lp4noftPaBFMIQgCDgdi/7tKm7FoOlTqPUsapu7SJJAyO/DcjwMyyLf6OHXZEbDGumNOpf/7TrFzTq53Q4dSSKgKfR8MlrMhz/pEs0ESAY1cvUuZZchI0EQBEZOjxBMB5m+XaW8UsG2XCzPRJ/wMXosS2jk0TNRjjMwIa9VazQ7TRzNoVqt0ul2yC6cpNL1cF0QRZBkiWBwIMARCYXp+/002y10nw9RkGhsN1ECCgG/D9kFu9TBK5l4moDlAzGgooUk7LqGU+sTmI4yvpDgyGISvyZj1Ay23tiiudVk4tkJIpODoD+QClCsGlz6+zVsPQghjb5Pp1TpohY7pKeijJ8ZQdIVVEkkFdKQ75kB9DyPxnqDyESEkcf/24oo/LdAoVBAkqSHzsY2m010XSeZTL5vzHA//H4/Z86cYWpqiuvXrw89bPcUP2VZJpvNMhIP88qLE7ypB6m+tcXmaxcYPTKJoA7UPDvdLrqu0zYdavU2nWKbUtMi8lSQp0+PYPc6Q3GQg2A7Lu/mTNq+JLpRJ1jv4ex0aL1XBzxScZ1RB5yQTL0vcC3gcGoqg7XZRIppiAH1jtR/H7fZRxkPoR9PYxgG2+UWKV1HVjpIqjiUcdUjOrVSi3rNIHHHFP0gwaljoxHevrbMylQcaUJBcqEvQCegcGgkxInEwQJVPcPC6Tvod/bscCJIZbvOt7/5XXyBLpVKheeff34oqFIulx/azWu322xubrK9vc3rr7/OZz/7W2xeqaIYOsFMgG61x9//xWWicT8zd9hO9x+TLImceXyKM49PDdXHl2/fYmJi4qHXRVclTo5HWVqM816hTmK5htRpI8bu+FoqMn6/PowXYtEoI+EUqiWRO+TjuafHORJqsbKywrFjx1hcHHQP99SDv/zXlzFXt5AFG6Nq8G51i3IlRzTsDYuMe12/dDpNNBpFEATK5TKXL1/m9OnT+4SDumafL37pLUrrfbq71/EFVpk4keXp5yeZHY8eeI7/LfAPlugZhsGf/dmf8Xu/93u/NIne7du3mZ2dZXV1lZmZmWE1QBAFEosJ4vNxjKpBo9og1ooxf3IeURZZXV0lGP3gHh97OH36NOVymXa7zcc//vGBH1etxo9fvcQbf7dExbZxFBe/4id2VaYrLBOdiJKIhFi59A7PPffcvkAoHo9z/fp1YJCoiKL4SN+qc6tFtlsOR8eS9Os2m9+8jtXrE5vXEDwBa6OBPBYicTzGhd0yyYCf33n+CG/ezHGzYeAPhBBdl6Sq8vRsnMmIj//XX19GlWQ03/7qvue6wyRgD3bfQdYVNGVgCp0N+5iI6Xzjao7d3V0iJyZp1vpoxS6C42EHFcyRAF1dJLjZZiKmc3gywvZqHUXyMG3nAQU6XdepVCoA9G2HUmGLP/3+edTrKmbf5I//+I8Hx+d5lMtlNjc32dzcpNPpMD4yTkSL4M+kSOh+RkfHDgwqgz6VSsfE8ytkTmdo5VqcsF0KMYWVepdCc5DIKpJAQNOZCsuEuh20Uo/wM1MI4wJXrlyh0WgMaVDBYJBYLDaUkxZFkV/3Br5VpZZJ37IxOw3OHppGtl1uhXUi7+ywUygz4vcRnw5w9teOc3v1NtWyBH0X6Z4quSiK+AMDg9I9WIk+W0slIhMe/sguG5tl8gWVr3zlK1y6dInf/PXf5Ma7OwhhbUjXNDpd2p02mUz2oQE3gF9XqIlw7Z1tDj8+9oGltn8e0Nhs0Mw1WRddmqbFyEwMPA/3HgEKQZMRfTJOu0/v6i5uu08kqFL3XOSoykTXpuJB0bSRejZark1vs4oU1mkqIp4rE9FEDmVD6HfmcXwxHztrNeqSgJDUCYd9CIAnAo4HsoikKoxMj1J8awU7oCFUTZq320TOjAyl8judgUfTnolyu90e0Jkdk5xlsLgbo7vbJZAebOaSJOE4DpIkDcWcPggikQi6rrO5uYlwZ64N7igG5/PMzs6ytrbGzMyg61sulx86z1eu5AlFg/R7Nlrg4CLbwBhewB9QUVV1ILRyD3q9Hghd/sf/w8e4dqlAbrOGKIlMzSWYWQjTvfO7SKqEpEjogHVfxX4PflmjeSUPLYemJlPO7eJ0PURXYXZmhkjo7jrtuS67tTbvLhURih1C9T5q08KJyKhRH4Is4hgWrXqP9nqDalgjvhBHDCl0BQGjsl84IjQaIjQaYvzZcTzHQ1REREmk1WqxsbGBqqqk0+nhbM69kCSJSCTC7NFZOhc7tBs9AvoY42NTyI6K6pq0TIvIHcVCUZIIh8I0Wk1URUGWFRzHodXqYJk2oZCG7HpYuTZOw0RQRKRMkL6i0jJsuha4uorTtsgLDrV6i/VLXWaSAeZG4sQX4zQ2G7zz5++QeTrDyZdOcvEHq1xcLuBNhkibHmLYh+STIKLR7loU1upEQhonX5p+oBjh2i6NjQa+mI+pl6ZQH3Kv/KJie3ubQCCwj355P3Z2doa0y36//4CgyAdBIpHg8OHDnD9/npdeemn4d8uyKBaLbG9v0y7keOxEhq8WYLcq4twqkomEqeoGgqpS6vVoVg16nocV1Wguhhg/lOTHqxXevdnm9156uBfmWrnDUqnL4kQaOQ+lH97CqHcQMlF8uo6wUifTs9ga0fFPBCmqCs4To/gqPazNBnaxAwhIIRXt8SzaTBRLdMkXa/iCYWZmYeV6ZaBgK3nIsky73MUX8ZFM3U1yMpkMhUJh35q4sbbCK4dTPCNHubJTp27YRFWJE2MRwm6L2YfoA4yMRoiOhdhdqRJMBeiUu0RGAiham0uXLmGaJocPH2Z5eZlAILCv0+p5HsVikc3NTZp3tA8mJyeHCuLh4Bit/DbphTiSIhFK+MldLbK5Vh0meo+CLMvMz8/jeR5bW1tsbW2RSqUIBAJUq9UHxG2eGNEwn51ize8nvtVEqjmIIRUpoA2shxwXt91nWh/B9UTKk36SiyGO+JtkknGmp6eHnnaTk5ND9WCzLpMZSZKYiWFbNluX86TjabKjFqVSiUqlQqVS4caNG8DAED2VSrG5ucmtW7e4du0an/70p5mdnaVn9vl3/+771K500SWRSMyHbdjc+uZtdpZ2+fX/7gzzUz8btO9/UOrmL4rXzAdBuVzm1VdfZWFhgc985jMHVlgEUcCf9LOxu8GRx+/y0X0+3yOrz4/CRz/6Uf7Tf/pPTExMMDU1RUAJUN0EJZvi8ckYtWoFQRDZzjWRqhEOZ8eIJMO0Wi2uXLnCoUOHhvMvsVhsIH0O5PP5Aymbe+jbLhc2qkSDfoRiB/tmnfHFaXZqRZarWxw5fBg8sLZb7BRzhLIWgdQMyaCPM8EmkcUZfIEwfdulY9qs7LZ57eoG+YCKstKi4lOJ+1UiuoJywPCw53l06gbzZ0eZnRghly9QtH1sVdoUS2VkPYQe8ROaUBA9wANNEnC6BnbXpmFYJIMq655HNOwj0TDY7Vpkwg9254KBAJV6C0VSODk+SXzc5Eb9BpZl8Z//83+m2WwOFU8PHz7MCy+8QCKRwHM8vvXOtxAFAZ/P/9BExvM8RAQEAWIzMaJHkuS+vUzlZhm3beJJArYAjmujew4uFmJSwZsJsMkmlbUKc3NzHDt27JHS84IgMBrVGY3u3Wd3RBZUiSOfXmT89Ai9eo/8bp5dc5f0XJqMkOHG7ffo9U3wPB6VjcmihE/3ceToIZ44PYrneZw7d45er8fFixe58e4WaeEJIrNRAFrNJpZlkfoAlB+AQDrI2lKZ8naT1OTDh6l/nuB5HuWlMm3bpdSziAe1QadTEJAO6PJLQRV1Mox5u4qniAR9CvWeTcqncNynwnSM2+dzNLebeEEVJaCS9qskgxpuv4MiSZRbJj3bpbZeY3ergZD2k5yKPnBl3a6FtV5HkAT8+Ch6DfRUELPQJne5yNTZMZSQMvA1ukOrbnfadNptAsEgjtHGUmU2NoqcKDSHiV40GqVer+P3+weqkD+BCbqqqghE+S//4TUU4RaTC2kOndI5dfoI1Wp1X3X1YQHn6uoqp04vcvNil5W3tgkl7umG3/Pyer5NKBvg0NEHJbVN0ySfzw+TyiefnYRn7wZc6+vrw+q4KIskDyWJrddRRIme5eC7T9rfqfRwygZVv4bZsxhLxahGDTruILls1OuYpomqaWALtG83kfJtLFGgPxIkYLVRHRfTJyHfYQwogGc7WA2T0nt53IyfycPpB5Rx9yApEp7s0Ww2abVag3VJFOl0Opw7d45wOMzCwsKBdCbXddmodLj2tVv0RYWbmzt4HpiygBHz4TuRRtMGxyWIApE7yV7/jodaqzuQ4Fc9l+K1TSJiADGg0G9bNCwHo95DEECTJUQFTFVhNBkinAnSNGyulwxulTaZi2vMJHysXVnjvfPvUS6Xuf5WGyvqZ+JEjP56HTvXxukJiCGVoF+hFnHZXquxcDKL/85Mled69Go9OqUO0ekoky9Mvm938xcF169fH45vJBKJfZ3z+7GxsbFvpmx8fHxoAfKTIpVKMTExwfnz5zlz5gwwCKzHx8fZ2W7w3b/dJppo0975MfpshEovghpI0yu16bRtDECfChGeiTEyE6Ne2ORIxo8oyWxWBV69uMOvnxlnLPpgnHVlpzGIEQyH9sUCwXAIfTTK1vYWs9kZxLhOptzF2u5w1eoSmEiRc11GT2fwHUoMinICiEEVUZOxLIvNnV1cX5hnZuI8PRXjz5YKFG5VkMRBl1v2qTz+4iKRe0ZGAoEA+Xx++P+rq6vIsszMzDQApyaiOK6HJAp4nsfGRvOh9MJQWOMzv3eab33pGq1Sm8x8nJd//QiOV+Gpp0/zzW9+k49//ONUq1XOnTuH3+9nd3fgkxeNRpmZmeHw4cP7EsDXX3+dyclJFEVClATsvoOkSDiWg4eA/AjLEttxaRgWrgeq4GK0GwObmDvXeWVlBcMwGB0dPZBim83afNuvc/3WLs5Om8BuF63QRvTABSxVpDUWhKyf+YUknzwxgtttcPny5aHlR6FQ4MKFCxw7dgxVVdGDGnWjhud4uJaH5vdx+MgCTz8zWMtbrdY+yufu7i65XI4LFy6wtLTEe++9x9tvv83nPvc5UEdo3TZJJP3470neQ5kghaUyP/jOMrP/3dmHKpn+18T/NqP3EyB3s8ytq0U0XaHYukGn02FjY4NarXag8ScMZG7vXwTHxsZYXl5mfn7+od/luh4ru22u5RqUKgahkMqxsQiHsiF+8zd/kz/90z/lH//jf4xbdykV2wTSAQQBVFUjGAoi60EatshTZ06za+VRFIX19XXeeustAoEAi4uLLC4uDhQqd3bet8q+UzfYbfc5Mhmjf7UMAkhhjcnwJLdu3mJtfZ2J8QmsgED7ZoX05DhaMMhyqY1Vr3HssSe5uFXjWq5JvW3i7DaRzT4jkQC7vRqdzTqNhB9NkYgHVdT7fFCMShf8KqceHyMcifKVd5bZ6PnIb63z6VPjNF2N7VqXcruPhzfoVACWaTKfjTOTGMxfvFfrMOo5xC2HqgSVTp+4X91H41T1IMvbRc5mM0QiCtmzZ1l5dYVwOMzo6CinTp1CURTq9TqlUolGozGcmWy6TQJ9G0/XcV3vwIe82bMIyQIhv4ohunxrvcRVXw8xbBGqtAiVzQHnK+KnlwpSPTrN8WeneP5EdhjI7tFdwuHwQ++9R0GURCKTESKTETJkuHLlCu+88w5PPfUUpx+f5/J3diiul0hPp4Y0ifvRqXRRYz7GxgdJmCAIJBIJfuM3foMjR46wcrHBN/78MroqU6/VEESR+E9wrHpIoVls02z0SPGLkeh1ih0aWw2qiki/7T6QABwEORvENSysrRZSSMVxPFoSBKoGiYafKcujO+4jnkkiiiCKAn3bZaclsLS7i+2C1zDp7HbpSQKCLtOvGYR8MiGfMrz3Rb+CY7n01xt4poPi0+jbJnpMp1fs0Kp0CacDaJpG1+gSCARQFIVYIo5j2UQiUdp1A8ezuHr5KtvmNidOnMDv91OpVGg0Go+kbB6E3d0OX/2PF+kXBJpujcpqDaMzy4kTHo1GY5h47XUM78fGxgajo6OoqsoTz0+zc6NMaaVKYio2oGreQaPYpt/t8+Qn5wneCcSi0SiNRgNd19nZ2Xnosff7/X3qywCx2RipZIBstc1GyyQV1u6yBxwPu9jBkCXqPYsRBELpEPpMhnwhhyTJSJJIOByh0+qwc72IVTCQYtqAsmh7+H0ietPG6Nl4qoxwJ2MVZAk14cfqWvS3Wuxa4H95Fs/zaLfbQ9PjPQjCYGZxT+F0D/Pz87iuSy6Xw3EcMpnMvgT9zR9ucPG9HYS+Q3gshKQreC606wb9zSYrrsfkySwBTR7cX6JAJBJheyeHiYIni4yOR2lc2Ubs9uglVWRDoImHIQv4VWl4PHanj+ST0MM+RFEgGlCI+hWaPYvVlo0eVJHiCl7J43v/4YeI0TmiR0YRZBF1NoYU8WFtN3FqJgB+CdpVg9ztCiNOjH6nj2M6+GI+Jp6bYOTMCIr+aCuGXyRcvXqVmzdv8gd/8AePTPJ6vd5Q+GgPiqIMffM+qN/lvZibm+Ptt9/eVygBeO+tTTbPldgNNvn9L/wPROJdDh85ymvnrrLelfjBzV3Smgd9g0zIImu6eG2BVrFGYiLDTDLAcqnND2/v8ltn96s217t9bhfqpKN++rkWTtNEmYogCwLZTJadXI50Ko2W9BPO15iwNMqCwOpum8moTiaqI97Ddqm2DW5tFkmnkjw7E+fFxTSu3efDvzLF1tooV86tkczEOXl2jLMH+MruYc+T7v51Zo8N80HYEDOzcf75//45DMNG12VEUeTatd0hjbHVarG0tIRlWUiSxNmzZ9F1nXK5TLPZ5OrVq0SjUUZGRojFYmxubvKxj32MSCTOxPEUq+/lUf0K/a5FZMzPkeP7i7aWZZEvlbm4WWGpZFA3bDwPgrrCmZkUT8yNk7yzvu41FvaOSVVVpqamhut4UJP5tdNjnJmMcT3fZGmzTq3SHSjsSiL+qI8TExGOj0WYTgQGlgrBNGfPnuXb3/42n//858lmsyQSCa5du0Ymk+HZj8zx9VyT3NIuiJA5kuDk6bsU5VAoRCgUGh6b67rDTme5XEYQBNrtNufPn+fEqd+Ebh79PrEmQRaJZAIUr+2SK7cZT/+3Lxj9gyV6qqry4osvPrLl//OM4nKVv/kP5yiXu4iAqLf4/D//PIeOHXpohcU0TSzLGiqz3Ys9ytNB/+a6Hq/d3uWNNzZw1uqoXYuiLHFrIsSxp8f5zKkxPv3pT/PVr36Vjz/3cXRdoWpYcE9HoNlsEwz46RptXv7Yy8O/93o9CoUC7166xjf/81dZW7lFuVLlqSefYGRkhHQ6feD5GJaD44LiQa/WQ7znu8bGx4acbrNvItse44lRmrJErlwnqkX40ntbFModom2L6HqVfr5JwDeQ8XddMG/WkNJ9ehk/+Z6NKNr4gzYBTcKodKmWuhz52CyH5xO8tVrhvZ0OYr/IifmBJ08KmEwMbBEMy8HzwLZMwqqf0dTde1JPh1ht9vEaJoserAG5ehdVHgixmLaDY/fx1ys0mmVuHw8wbU3z27/925w8eZJeuUenaRKM+ggcC+z7Ta9du8Y7K+8glVWCE+NslVtMJEOIooDrupi9Ho22QaVjMmP22R7XeP1yj1xXYqbmohkaXjyJkJUGZtm2ixzV6QU1Xtuq4Y/4ePwOFSCVSpFKpWg2m6ytraFpGiMjI/s2NNt2WbpeQpFFFg4nH7kRnzhxgu9+97usra0xNTXNxKkxtl5fp9VoomoaemD/TKDZ6VEttjj8qUOMJu9Ws2ZnZ4cb1U27CkC5Ukb3+QgccK8/CiLCoHLXP5j+9vMIs2Xi9BxKfRv/B/TmEkQBdToKkoi13UIxHeqqRNKF3eu7mKZJbDSKfCdx6fUdbpfaVDomGD3CrowbUGm3+2ghFUGR6NkO3ZZDz3JJhrThLJkYVrELHdxWH8kvIcgiFjZYDo1ck3B6oOLq3Zk76ff7+DQfCODTfeg9j7Dq49TpU0QWIly9enWoJnz8+PEDk6JHYXOtRmO7SeZQCsPoYHUcVi9tc/16isVDd4Omg5Q5t7e3SSaTwwTl0NE0Jz6S4erruxSWSoiyiO3aNNwOvrCPs59a5KWP3q0sh8NhVldX8TzvkWyHfD7/gACMP+Vn9OwI3dfWB6IlHQvX66MpEm61h1lqY2gieq2DHvdjJWRc12ZhYWEfZb2x1cHdNfGlgwiKiOs44DrUVEjYLr5OH8OnoCv3JEaOTddz8PlFGjt13ru5hbYw8HQdHR39wAG5KIpDKmyxWKRYLBKNRlFUnXe/u4IU1ggdSmAXOng+GUEUCMV1RFGgXjFo13vUVRFFlhCFgQcoeoR+p8UTi2OYnsC7b20g+hQazRaKFKSf9OP33U1cPTzslkloLoYWuCf5EiCsK0iiwPV8k/n0JGfHx+netri600BR7s78yik/UtyHUzdxmiZCvYdX7tI3bSRVIjWZIpgNEp2K/kIq/D4MlUqHjbU8Fy5cYHp6mtXV1Ueapq+vrz9gFA4wMTHxvpZDj8JTTz3Fa6+9ht/vHz7DqUyIYDowUHXOBHG8Pttbmzx5fJ7NS0VOzo4wHvPjdPvs/mCZ1aV36PdMWpEdvFdsYnNZRqM6a+UOO3WD8djd/cuwHFrdHlOpMM7NXQSfPHx2gqEgjuvQbDZRTZWu3eFEOE1rKsb5tTI/fO8yRxYXBkJxgoDrOhjNOi+dmOH4eJT5VBDwWN3e5uTxeU4eh8NH1UeuH+FwmKtXrw596R6GD7p2iqJI4A7l2HEcBEHgK1/5CqFQiFgsRjAY5IUXXtgXK9w7i1yv18nn87z11lvcvHmTkydPIooin/uDM/xwdIXdXItAROHCjW/w//h/vsmpU6c4duzYQC3dE3kr12ctZ+Gr2GQqBoLrYUY03mrsslaz+MzJUSbjd69HKBTi8OHDmKbJysoKwJA2KokC08kA08kAz84lqHctLMdFlkSCmkzqAIupaDTK0aNHeeONN3j66adRVZXTp0+zvb2NY+/yj/7FWXa2miiaTDBo4NMevg+Lokgmk+G5557j2WefRdd1VFXl2Wef5U//7DySJB7IIlF1lUarjdF9uD/lf038gyV6sViM73//+/9QH/czh9XbZSrlLpkjKVqdPhRl0uGRR6r0rK2tHbgwwuDBelhXb7Xc5sc/3sB3ZRddEBBDGl7Ppn+zyhXTZiwe4JnZcaanp7m0fInphRiVd3PUNAXBc6m3+/R2WkzMhTn13H6eus/nIzs2QXfTQSKMHglRkSWq1So7OzuUy2Xi8fhQBGbPaHTvVhZEYV+gBwP6QbPRIBIJc/3aNcakBLpfp+F5bORL5PQA1Npk1xr0Nmv0XIvoVApRk1GAdMbP5sUczmYHNd9GTfioRFWWm7vERBEtonLkY7P82q8dI9/o8cbyLt1aiSOzE/uoW4ok3kNThGKxQyaV2Xf+iiQyM5dgzfVIVfqcMR1KlsNOu0Wr1cZptUhJCqnRGJEjET71hU+xsTmgq9x4fYPX/u4mrUaPSFTnw589zOLTgwVpenoaRVGYOzOHEBGQ2l2uVqv8aNNBFj18mg9V9xMOBkh02yxMZkl/7DhLlRZHG23cjQpiwrePvue5Hnapg3a9TP9EirdWKxwZCeG/xwNpT9XMNE02NjYQRZGxsTEkSeKrX7zC1R+sI8gCz/3aET78sYMVyPYwNjaGoigsL9/mpVfm+OvNOsZmAzkjU7fqRMJhEAR2bm9S3KiTOJrhxZcf3qFRlMHcz+TsyE9FU3Y9DxHQHrEQ/7zBtV0c18V23GGl9oNAkETUqQhSWKN/u4pdNmjaLqImok/oQ8Ef03K4tdOgWjcII4KkQEKhJ8o4hRaqX0FAQFfkO0q5/YEpdUhDYDCr6wv7ECsGsgiWpoJr42oi3XwHc8EaBtyu4yAg0OsZhMJhXNdDcF10VUXxD4Rb0ql5Lry9TqcnIIoipVIJz/OGNMFEIrHv3ri/MyfLIogiTt/G79cpVapofo3tnU2OHpsevs6yrH0qn/l8nnA4vG/ov1gs8vyHFnjqmcNcvZDn9tUcngdjMwmOnx5l8r7KrG3bbG9v88ILL+z7u+O4rK1WadR6gIus9pma2n8tBUFg9OwoeOB7Z5tUvUcVj2q/j5GrErL6uLZAMOIjfCyLkvDT7nT2JXmtcpfWegMprCHeSVwkScbvk2g5LkICArUePZ9ATZLwGHRzZUlG9zykThtpJM7GSpcX5SjR6KCyXK10uPRejmqpTSIT5NTZcWKxRz+fmcxgHa3Vavzw+1cpb1QZPZxBtF3cZh+3YSJFBwm1P+Kj1+gxo8pEZ2LUOia26+GTJdJhDanvR5NdWpJHIhWhbfSRPWj7ZdSYfjfJcz36lS5yWCPxkFmXgCbT61vc3jU49sQc5WoJ8XoJo9tHV++ekyCJyAkdOaFj9Sxkn8Tirx3i6HNTv1Dzvz8JvvxnF9jN1/jcb/whZ5889Mg5u3w+TzabPfA1e8+rbdvvq1poOS4CPKC8+MQTT3DlyhVEUSSZTPLMC9Oks0GarSqIDYL6wPi7ZYsUGibZO95s/ZUaasVm/LF5uj2D/OU1uufz2AERT/DI1S3O3XBJnpkbFnxEQcB1HCRZwRGFoVjK8HxEiUQyweb6BoInIKsSIxGdpGySCBp86tQYDgKe55Lb3OCFDz3OaOzuOrO8vPITMRf2WFUf//jHH/qafD7/UHGc+9Htdtnc3BxSQkdHRxkfH+eJJ56gXC7j8/keea2j0SjRaBTDMPjwhz9MJBLh0qVLQyubxLRONpslmHqc119/nZs3b/L0008zOjrK317KsbZZJbXSgIoxsL4SBcS1Jv5Cl4ph83eCwO88OTmc492DpmlDA/aNjQ1M02R0dHRIJb1xPkduvcaHPr5IKvlotdDFxUU2NjZ4++23eeKJJ/D5fIyPj5NKpbh27RqHDk+QSqWwbZvNzc1HWkyIojj0g+33+8PXRuN+Nmx34F973xpitHpoQfUntmj4/xf+QRI913UfoITs4d5A/OcZgaCKjECtZtDv9hHMNpevXCBSiRCPxx94sIvF4kBV8BEPlC7qLP9oGbEnIusykcmBGfv1XBN7tU5UEFDG7rR9wxpUDXw7bS7dLvP4ZIwnn3ySb37zm6QOBTnWTbF6q0rX6IPbYiyr8dHfPIUv8OA8TK3bZ2e1RmytgePIuP4Ejz12nHq9TjgcxrZtCoUCV65c4bvf/S7hcBglksHsGjR6UdSRIOZSebixA6iaRn4nRyaYQtN11qvb9Bhho9jmcDZEZrmGmW9iRgTiqbsLlud6WH2DxaenaSwa1Dbr2Kt1ApUunZCGfizBZz9zhMcOpxAFgR9e2mVpeZUTizN3Zsjuwu1YWPkWVrFDv9tDCkrYchcpru+7DqosoicU3rFbHO7sYhaqjCph4pNJUqNHyBzNEJuN8faVt2l3BhLEnWKH175+k7xpERkPs1No8+0vXcaSWnTpYhgGV69eZWNrgyeeeIIxa4yZ7R4NG8qeS6tnIHa7qMVdCoVV/mT57wgJHyWaPkR6y0aMag/MaAmigJINYm01CRc7FAIKK7sdTow9SGPUNI3p6Wkcx2FnZwfLsli+mEfyydg9m5UrxfdN9GZmZlhbW2NkZISdnR1+7Z+c4Vt/c43qchUMm3p/m3a7Q6FRwT/h4/AxDbXYpVjsEsgE7tCHB7+zZVnkCmtEI0Ec96dL1Nr1Hv6QSux9FvWfJwjiIIwVBAH3J5xpFgQBOa4jHU0iNEy4WqbX6RG2w7R2Wnh47DZNqq0e0YiOkvYjJ3Q6Xo/+ShckcRhEw4ASpMsyTcMioMp4lkGlXCaZSiP7JIKmw67jEtBUTLeP1TDpd+8met2ugeM6RMKD+7Fp2ARNl8lDMUJ31q3zb27w1peXOPLyCDMzM+Tzefr9PqOjo8iyTLVapVwu3z0mSdrHclg4kiQxF2T3VhVBEOhbfQ69EOeVV55jc3OTdDr9gIJbqVQamAgrPt59a5PdQpt2u4Pul3j6hUOkUgFe/Og8C0f9ZLPZAztcjuOwvr7OwsIClmUNK+kXz+3w7utrFFeq2IaN2TeJpiPcvNTihVfmGRm9O98iSiKjT45iB2zEpRL6epMpXaMVjdBUdLp+idhYEFOwMTudB86jvt3A7TsoUR+2bQ33V88Dx/Owkz6CkkRA92G6HrZ4J4A1LRRNYstvUlN38d+2uHIhR3bkEMVCiy/+v89RWq0iySKu43L9nR3+0T9/glTq/Z+zWCxGKNRARMQwu4iCgDYdwVyu4TR6iGENQRAG64DtspgJAvs7+dVKj6A/SCFfYGwhzNbtJrt1AycoY3f7CIBrOgNLm4BM6ngKJXBwF9LqW8iuhan4yDd6jB9KEnl3h8Jandhp/UBl5Va5y1gmxOzxzC9tkgcwtZjEweIrf/tfeOPNCJFIhD/8wz984HmwbZtOp/PIRGN8fPx9A+a17QZf+9IVREXic791kpF71nW/38/IyAjV6oAFkkwmmVtIsrnZpVAoDEUx1sodbNdDuZMoOs3+oCMniwT8fuSYj4CkY1sualgnEQ/hD4bZ2dnBNAfUXVdUkHBo9WzCmSD9ldq+YD0YCFCr1xFFkXgwQtFtEOn06HebnJ5N8cx8Cs/zuHr1Kk+9cGpfl21jY4OJiYkP3DXP5/M0m80DO37FZo/VcgfTcqiVKzx7IsZB4833CsPVajX8fj+Tk5N86EMfGibha2trdDod2u02yeRgVr/T6fODb90mPRLi8afGaTab1Ov1oeXDO++8w5EjRwiHw3z4wx8enlO32yWfz1OtVmm324yPj2NZFju1DjdzTeJbbYSqgTwRvvt8JcEud4lvNimEVZZLLR6fOjg3EEVxSMnP5XLkcjlCoQhv/t0tqpsNEpkQL73y8LEnGDAMRVHk6aef5u233+b06dMEg0E0TePMmTOsr69TLBY5evQonufxox/9CMuy9tmd3Yv19XVUVd13fx8/leXaa2s0t5qEJ8N3GRVdm2aly+GPL5CN+w/8vP/a+KkTPcuy+Df/5t/wJ3/yJ2xtbT3UQP1+n5CfVxx5fIytWxWWLuUJ+2SOfeoQP7r0d4hXRD760Y/uS/QcZ9D6f5iHC0Ar36L8RpmbF9fwx2OIHsRDGmNPjHEzX6C2uk16fj8VQgqpqK0+rUYPw3KI6ApPP/0058+f58hHxjnz4jStpsXVa+/xzCtnmF48eEA6oisEEjLl0QCuGiAomiSTSZLJJNVqlUajwdTUFGfOnMF1XcrlMju5HNK1HX54rs6o4xJuW/hudwmMJ1B1Fatv0ak0mU9Mkn52nlzE4ubKGoooktnqYBZadMIeyVRy37E0mg2i4QiIArGkn1jCj3E4SWe1TOhIlspiHDmkIksim5U2r52/xvxokkwms2+I2cq1MM4XsOs9BFWi3WkTVHTaOxtohxKoh+M0W00q1SrVagVJlOkoEdLPPsYn/6c5XGtQmZFUaWi+Oz4+zuXLlzl27Bg3L6+ws1XCSqm0q116gkep4CA6IsdPHUfXdfL5PLIs89FPfZSgHqS+Vqe8VKZT6uC5ESRNIrGYoKU8yf/9//PvuJmv8YmQjdPqo0wd7O0FIMV9uIUO0kiA1d32gYne8LWSNKTQJCa2ufVWDlVTmVh8/9k4TdMwTZNIJIIsy6yurvJP/6enuLlSY3mpRL3e5tKlc2QVlxlFZP07m3zpooCHwEgmyFOvzDP25CiGYXDhwgVe+ZVnqW+eY2O7QXD2Jyv4eB4Y5S4nXpgimv3JFWp/ViEpEoIkEFAkyt3++7/BHZjxevZgfRUUCdP1UFQbyS8yeWKS9Hwax3SwbJfV9Sp+MUIg4Ue4EwwFbJmiUT1Q61+SBDwHyvUWTruCJMl0ux1iYT9KxUCzPUzJRZZF+h60Wy1CKT+CAK1Wm1QqBQJYtkvb7HNIFhk5nkbWBltLPOPx2CfnmTkcwrIsRkcHoj17Cd/IyMgw8PA8jxs3bmCa5rBw2O/3+dXfPc7KUotO00T22UzOancEC2aGgcBeEFoulxFFkds3Wvz4m+/RyLWwLQfbttBUlRs/LnL0mUk+9iuHHjpX5Louq6urw+Arl8sxPj7Oj76/ymt/fQ3bdIiOhdADKs12C88Qufq9NQprdX79C4+TyeqUSiVc10UQBDLzGaZPTGMZFnbP5sbf3ECtGaw0DZSAhtnqI0oinufS65o4jott2jS3GwiagOPYSJJ8TxGlj6aqSD4VOaOhzUbxGRZu2wIRxJCGnNBpFMAwTUJCiHPfu8n8EY03f5CntFJh5EgaSRFxLIf8Upl331jnU5899oHuYd0nIykSfp8fURLoeF3cUR9iYSAwI0V9eHgoB3TiPcdDczU2L21i5k2svklkMkBlMUqg79Kvm3iAEtUIjYcxxS5bpTVG5NHBvXYPTNPEtiwikTD9lslGpcN0ws/YbIx2oUUh1ySZCQ7FvRzXY7fSQetYnPzIHPr7dDF/0fHKZw4Dhzl3LsR3vvMdJicnB1Ts+7KJ1dXVR+oJwCA4l2V5H73Qtm36/f5wpu/C+S0K5/IIssiNUyOMPL+/sDA5OTmkelcqFRKJBIVCAcdxhoG/IgnIooDtukiihBRU6ZsOnjOY2xI6DvK4Rnx8BMdzWF4t0G3DzPEjw2e91+uRvrbNra08U7qEo9lo61X0qdhgzRQFyqVdRtQ4/tEwsTMj/HBpFZ9R4ej88wDcuHGDhYWF4bmWmj1ubeYJ+H2khA9W2CwWi5TLZU6cOMHm5ubwt+vbLt+/WeLSdp2OadMzDCRZYbm9wanxCC8dSiN4g4Lu1tYW/X6fVCrF3Nwc0Wj0wO+ampriRz/6EWfPnqXZbFKr1bhxrcwbX7lNIOkjluoTi8WYmJhAkiQMw0CWZZ555pkH6KJ+v5+5uTnm5ub48Ic/TL/f57vf/S5lKU49LzNeMZDuUPzvhZTQsTabaHWTy9uNhyZ692J0dJTR0VGq1SqZQxq+aJTFA8SyDoKu61iWxTPPPMPbb7/NkSNHhqNl09PTdLtdzp8/z5/8yZ9Qq9X41V/91QM/5+bNmySTyQe0EOYnozzx6UO88+oS3eu7+PwqjuXQs10yp7K8/PFHPzP/NfFTJ3r/4l/8C/7sz/6Mp59+ms9+9rNDJcdfVCh+hU/8wWk+VFkcSGfHdfzv2Fy4cGG4IO3xnFdXVx/Zundtl80fbbKzUWc54KPd7BDQNaYMgdq3r3GzehvblnHaJtI9rV/XsHFkAZ+uoN3ZvJLJJOl0GkER6GsdZhZGubJjMrP4cKNps9PiI4ejNI+Oo0kCy+/+YPhv8XiceHzgr7e8vEw0GiWdThOJRPi8FOG1jS5BycGK7tK+kKdy7iau69JsNBAUAd/xFMrhJP1yB0VrIm/labf69EMSyeT+B7TVbBIKhvYHoAIIgktwLIxY6qKNhbi83eDYSIi//e6P0fwhpscHQd2ebLvX6NN9N4/bt1EmwtiOjRYCQdNo5mt0vr1D+6aI/3CaeDzOxPg4ms9Hrm6QN6SB4qEm0Wq1qJaq1Go1Wq3WcDg5FAoRjvvJjiUomQ56LIBTbDM57WdqYQpd12k0GjQaDcLhMN22wN/++TmaFYPDZ0Z4/reODegqPhlZG2yGc/OL+EKLCBULROGRnV9Bk/HqJqrr0TE/OOf7d//Zc1x9soBhdMiOyQfOMd2PRCIx3GQXFha4uXSdE8ePc/pomlqtxvGjsLu2y4VXNyEzTf1OV7dSaeN84xbPhjy2Gls89dRTyLLMsacm2LxVode38akffLlptUz8ssixxz+4R9PPA4LZIHpUJ1vtknecoefY/fAsB6fawyp2cJrmwP4AQBKwFIFeeZOthsPcp+fw31GR3K52afkk0mHfvk1WkqWB8qJ7cAdRlSQcD0YnpsBzcB0X0ZNRew4R16NquViSCHhouk6r1aLX66FqKpIsYVoOu80eIz2XI4+NklgYbIhbW1ucfWoR/SWdfr9Pbc87URAYHR2l3+lz+83bGA2DRCJBMB7EJ/qGQV2tVqNarSIIFvNHfHieRrlcxu+/G6CPjo5y5coV6vU6lmXhOA6rtw2+95dX8DxIzETpmQbhSBjX8Wjutjn39ZuYRp+zLzxY/NjzRJ2bmxsGhq7rsrle441XlxAlkZHDg2Ch3WoTCoYQwgJaSCZ/o8CX/vRNfvOfnjxwFk7RlcF/IYVerkKv16NJn3azia77kARx4FMoSzR7XUQb1JgPQT4ocBQQ8BAkASmuo/geHPgflcfRfTqlXBnXFNB9Cer5NXwhDUkZHJukSKh+hdxa/cB74yAsHEkRygSo5dskJyMEQ0EIBmn569ibDXq5JlLfIRnx4fQdJFXCNmzapTa15RqtQovqZhW35RKeDFNu2/h6NtmFFMJRDcEnI0oitm2zslKk3+9j9a19x2AYBnjecO435JOpdvu0TJvMfBxfOsjtaofKrQquXx7QWns2YZ/CEx9b4Mz7dAR+mXD27FkqlQqvvPIK+Xx+aJQuSRLlcploNPqBOlRjY2NsbGwMmSV/8zd/QyKRYHFxEcdxiCZkIrMxREVkYuLgeHFxcZHV1VWi0SjVapWrV6/y+7//+8N/T4U0kkGVSrvPaFRHnYthl7vY2208QNZVpEORwWxx3yOdiHN8doTV1VUURWFiYgKfz8dThycwtm00RSL4UozqG2tUb+QAD8uykXsOzMn4nxilrcpEQgGsnTyCIHDz5s3hDFnDsPjOhR2uvbdFt9BCD/mJThV46okJnpxNPFRxsVwuUygUOHXqFAAjIyPkcjmmpqZ47VaJH69UyIQ1xkI+drc7pEailJptXn3nFlcuX+Ls6ICK+OSTT6JpB9MD2+021WoVyxo8O2tra4yOjhKJRO4wKnx85Uvf4lc//9vMz++PV7e2toZjO4+Cqg6saD72sY/xH797AR8SnuUg+h7c6wVBGAgk9Rw6fWeoJvpBEI/H+d0/fJFut8vW1hbLy7tMTU3to+vfj5GREdbW1pidneWZZ57h3XffZXp6ehgD+f1+2u32QFys2+XChYt8/GO/SjwxKEB4nse1a9eYmpo6UKRIEAQ+9uFZxsbCXDi3Q3mniaLLPHUiy5nHx4gdMD/43wo/daL3xS9+kd///d/nT//0T/8BD+dnG5IiEcreveBPPvkkc3NzJJNJ2u02t27dQpKkh5pq76FdaNPcabKKi41MVO2haTJrRp8b5y6SSEusJqJUynVSmoIUUnF7NlbFoD0a4OxCcp9a36lTp/jmN7/Js88+yxe/+EWefvrph353p9OhXq9zfOFuIrhxXnhgNiYSiqD0FGr5GldWriCHZR6bmaTY2uZ714scOzzFyNFpnFKXpYtXsXYdwtNJ3m7ehjfrhIMBvL7B44lxNm9cZ/T0zD4pc6PTRfP5BkHofbBtGz3kw2l2idRNtmpd/vpbr+FoYUZCdzeIWDRKvVZD3x50xdTJMIbRJXen0+c6DsFQiEgmzogeIrw4i3iPAEbQJ7O6U+Sb39sipImEQiHi8Tjz8/MEg0E2NjZIJpPMz8/j9/txDZW/+g8/xFjuMDme5KVfOzIMsjc3N4FBVfLr/+UKW1eKaEGVt169STId5LF7TL9VVeWP/sU/50/eWKdRvEm3Y6Ly8I6eZ7sgCTgCB1pPPAyaJvP4k3e/t9PpsLa2hqIoDxVlGBsb48qVKyQSCXw+H0ePHuXKlSscPXqUWCzG008/TV7Ls/Ij6EZ9JO8sZlVRYHm9QvQdi1f+8JVh4nr0sRGuvLnF+nKVzGLiAx2/0bNobzY4/sQYk4eS7/v6nycofoXkkST119YJawPlwKh//2bldq0BFa7aGyR2Pgl7kGdhmzZuuYW1VMUIqCyvLvPkyJMAtPsDEaKDNs9AOEB1u3PgMckS9E1wEAjcCRq8voMYVolEfQjbTapdE1OSqBk9EsEg1UaLaCxBrm4g2i7jpsvpYxkWX5lDC2sUCoWhDx6wz5fONm3y7+UpL5Vp7nYwLIf1dhEZF19EQXlRIbgYpGf19lGaKpUKqqoOg1Gfz4emaYRCIVRV5caNGxw+dJIff/0SApCajQ3NugFESSCaDaH4FK7/aJNYVmJq6i5rwvM8VlZWmJmZ2fdsCILApfd26NYNxo4PZtU816NrdIevUTWVkcUMjUKHdlNGHBWH5uLd7t3XAfS8HgFZIR6WcRyXWDyG67r7BI+sTn9AJ7tvffTwcD0QBJBdD0EbCOYchL3fPp6Osnk1R6XcJpYJsbtc2/c6y7BA6rO1tUUikdinqrgH13ExmybOHWGkE2dG+fE3lqnuNIlmQ4iSQDAZoekpNIwS2ZEwmuLS3G7S2mlR36xjtS2UoII/5ceX8tHOt8kcyVBcryJXOzSvFfGFdZTRMNJECEVVOHzoMMXdEq1mc3gsnU4HWZLR9LuBlCyKOC5YjofqkxlNB3ji2ZMsXciTX6niuR7JiQhHHh8lNRNDlD74OvrLgL35sLGxMVzXZWtrC0EQ6Ha7H9gX2TQdvv2VNUS26MlL1OtFAoEAL730EpIkMT0NJ09MI4kCseDBQbCqqsRiMQRBYGlpiXA4vK8IqskSpyaifP1ynlRIQwmqBJ6bwC528ByPTlfHjAgEgXzDYDrhZzYdQcxG6ff7rK2tDTzVAjLPTAf589evcnh2iswnj2AXO9hVg9WVFdKzi5SFNvVmhWbZJWIUOH7mKNvb2+i6zuLiIkbf4Suvr3L7W7fxVTtMJMK47Q6dzSbf2mzg/vpRnl18sLBarVbZ2triscceG/5NURQsy6LcNrm03SAd0gh0LLa+eoVuuUk1sIx2KsX4SAp8GY4/Pk3izm/Y7XapVqtDauoegsHgMFnL5XJ85jOfodPpkEql2N7e5i//8i/ZbbyH7X0I2J/obW5uDplBtZrBW6+tceyxESYfMiur6zoTYyNcv3mbpCji2e6B65Jne9iKgM4HT/Luhd/v59ChQ0MDdtu2mZycPHDNEgRhSHcXBIEnn3yS8+fP0+/3hyJTs7OzfO5zn6NQKPDGdwr8h//bD3n5Hx3n1JnsYJbv0KFH2gEJgsDxQymOH0o9VGX9ZwE/daLn9/sfmVD8MkAQhCHtyLIsvvOd75BOp4d84PtnLvZg92ysvkOPgYSsKvpxnD6u6/HEc88ydyjJrakgl17foJ7roBU62BJ0Rv1MPzMxVF689zief/55vvjFL/LYY49RKBRYXFx8YDB6b+j3fmWnPRnxeDyO67jsXt+ldLVEaauB1XcIhjW6QpPKXIXC2mVeOfMRVlsOuz0LTzTJhT3CE/MkxqawCnlOz49hdlq8eX6dXr7K4mNH2e1W6eVyjGRHMPsDAYiHVYvsO1QloipersWGXWNsMYoSSMI9FGFFVek2O/Sul+kYHdq38kiShCIrZDIZNH3wgHqOO/BSqhiII3dpgH5Fwh+KcPzM9D4VKBh0E2KxGNFolKWlJU6fPs3N8iXa/ivokp/f+p8/TShzN+nfS/SyI2P8uLSEP+EnPhpi+0qRRr33wDlGdIV4QMGZG6d96wparY0eu3tsnusNOjCigFPvIcV1uqqI2yqzvGwyMjLy0PvrYQgEAszMzGBZFltbW3ieNxRg2cPefM1e4q8oCidOnODq1avDRc9zB8HmvYua0e3g9U0OzR/etzEHYjqf/t2TfPlPzpO7VSE+E8X/EOlyz4NWq0d7s8ncsTSf/J2TSB/AfuDnDbHZGMHzBSZ6fa7VuuiKiHbnPF3DxrxVxambmEGZhunQ7Zh47p25LMcjqSmYiovflTA3TJyzDpIm4bp3DCQPQGQkRPV2GafXR/LdNwu6Z0Zy31sFQUCdDBMLqljn8iSjPlRRorS5SyAQpFtoMB+PM5IIMH04xfQLk/iTAysFRVH2+THtwTZt1r+/zs7FPCU8tm1qm/GIAAEAAElEQVSHru2Cz4dfFpFLZVpfeofU0QTP//7z+97bbDaZmZkhm80OZ2Gq1SrFYhEYdPf+9q/foJ5rMnokQ6vVIhQIPkBZDUR9NHMtbl3Z5eWP3v37ysoK09PTDxTpUqkUV9++hBqUabcGBuJdo0ssFttfUVahvFbj/NvL+AMmkiQRjUYfoPwEnwyytLPEhAzn1kssjCbodDt4rjsUY3nY/KZjOzgIaIqEZnlI4/6HJnp7EEUJn6qztZXjsWcm2bhaIn9jFz3qw6gbBGI6L378OOPjSSqVynBmUvIk1J5Kc7NJu9Cm3+4PKO54hESBhbCPjfUahdU6hFU8ScAX9nHmN47z6c8fp9tosvStJYx1g/SxNIF0ADWoIvlkCms1Ctt1Aj0bRAE56keToNfo4q3V8CwHbSYKkkAmkxmKwbRbgwr8nvjQHgRhkAR7DLwUBU8gPhnl2cko8HCPxf8ND0IUxYH42I0bCILwgZggAKVii8pql2a9yXOfP0bmqdM0Gg1s2x4+U8nw+3toZjIZzp07R6vV4vTp06yuru6LWY6NRri6VeHC8g6n5kbRfDLq1KAAHKi6NFst2oJOQJV5bj413KdUdaB+2e/3efPNN1leXuGx1Awt1+V2zSQS0ehJBmp4HJIZnFoLTZH4zIkUF7/5Lruuj7GxUVzX5Wtf+xrZo0+x8uMt/LU2iWNjQxaF0rWob7R4++1tDo+E9q0R9XqdtbU1Hn/88QfOWxAEbuVqbBfKRNw2xXcreFWT9MI4QtNCqWoETkRZKjZ54/JtjmUGhRy/379PXfh+eJ43pM0XCgVgQD29fPkygiBw48YNnnnmmeE1cl2X7e1tzp49C8D6coWNpV1kVXpoogewOJbi9WyRVr5IqDzQF7gXbsdCUEW6IZVZxWR5eRkYzP3+pBZR9xuwdzodMpnMA5og6XR63/175syZIT14dnaWycnJYULbr7/L+sUCzXqHa9eucezYsfcVF7oXP6tJHvyvSPR+53d+h6997Wv80R/90T/k8fxcwbVdWvkWTt+hUCtQKpVot9scP36cZrNJsVhkYmLigfayGlLxBVSilsOWYRIPaOw2WgSDITTHI5IJ8pnTY0wmg1xcrtCsGfj9Ck/PJzg9GXtArQgG8wqpVIpqvYGenuZLr11gYW6GqWRoYJ7suqysrBxYnYvFYlSrVWLRGNtvbbP02jrrHZOKIGCL4Cv28VUbiJdWefIfPU48KXI4o1NzNf7Lt95AFAVmpyeZH4kyuhjGZ7d4K6+heRDWQ8hBldHYGNVKhZXVVRKJONFH2XB43oDCqcmUVrbRMmmy49Ms77aRBAHDMKhWq1QqFfJrW0zV/QQSETLpCdqd9kAc4l42qCQO4lhr/7yoKAq43h3Z7/vQaDSGg7eNRoPvfe97XLp0iWKryNzcHEubSzyReQIYJNB783lTk+NMHa1y7bU1tstdgik/k7MPnqssiZwaj/JqucPI6Rlyb98mfWIK2RawKwZOuQvuwABZUCX6U2FiIR8fPTtNxCeRz+fZ2toCBvLE2Wz2kV3ke6EoClNTU0OfLMuySKVSQxGMqakpNjc3hxQ6SZI4efIk165dY3p6mkA6QDbu51azR0MUaDWbGB2bExNpwmMPUhwy0zE+/88e5+t/fpmd21Vqooc/HcQfVAcKaJ5Hq96jV+7il0VOPDXGJ37rJIFf0BmaQDpA8liS3ts79KI6y/UuUb9KQJOxdlo49R5GQGG3Y+K6gyq2K3r0bYeQJqF0XMInj0C+gGzIbF7eZOaJGWRJeKi/fSCuE8iG6Ow08Wf3J3oegxbRvXGwt/cMKhJk/EgLMV745CL9cpncusHU9CjNZpXMQpzDzx4mNBJCEAWazSaWZe2T674XhYsFdi7mWRY8Nls9/KpM9M561jZtcoKCrcvo6z0ufOMCxz52DL/fz87ODmNjd2m8oigO54fT6fTwXlWFEo5bp9mqEwwGEQ+kPYIe97G71sI0bTRNZmVlhampqQc29kqlQq3epFlt4g8EBs+IAJ7nHkgbEmWRYCD8SAPp8HiY4EiQ8FYVXVPo9G0C+kB1MxgKDj9n79rcK6DjuC6uJxASBQRVQE68/zPiWA6SIjI2nsEf7PHpPzzNOz9Yp15oM3Eiy1Mvz7JwaECrTyaTuLbL7o1dts9tU1orgQByQCaWjhFKh0AA13I5ElDJpgPk1+s4qkR0PsapTy4wtTAofrZWHaS6xMjxEUxMWm4Pw4DNXIPKZgOjblLYqtPs2Ygi6CEfejSI0erCVhNBElCnogPasTdI9AOBwMEsENdFEgQUcTB3qATuTwR/doOwnzX84Ac/oNPpcOLECSYnJ2m326yurpJIJB46otNut3nvwuvImTZPPLfI8y8dIhp5OEvlUbBtG5/PR7tjQDjD6+9cZrmtMJmNM58Kogoucu4yY8ExtqpdBAQifgUBaPRFVkttHk+O8LGjAz+9+2GaJpVKhampSWZmJqn1HLpyiis7DW7lSszPz6FKIp88OYHPrJH09XjPtvjwhz/JkSNH8DyPXC7HH//59xDWXUam94v6iH6FgCpRXa+znK8yd4cK2Gw2WV5efiDJq1arbG5usrGxwe2mgCSlmBufoXbVhhkJLaLjiBJuuw+2hyLLJDMZ5uY+GNvl3rUzGAzSarV4+eWXCYfDLC0t8dRTT+1LxguFAj6fbzjvd+xUFlkRmZl/dDI2nfBzeCbL1UIbZb0NOx5SzAeSMFDm7VoYM2GC2RDPn5hiJKIPz38v6ZNlmZGRkYfSUe+HIAjDRK1UKrG0tEQoFBqebygUeqBQcfz4cW7dusXS0tK+ePjXfuckt8+kEeUGJ0+e/IVaM37qRO/f/tt/yxe+8AU+85nP8IUvfGHI6b4fZ86c+V91gD+r6JQ63PruKms3y1iWQ61eQPd0PvsvP8vi4UH1aa/a4HnePiWmQDpAaCKE+fYlfOkEPVUmqOuMmyapqRTx+TiaLPHEdJzHJqIYloMmSwNDyAPQ6/VoNBrExuf4+sVNKOyi2CJv3r7C+HSWxyZjpN0qx44cLKEcj8cpFouk1BSrb25zwzCpCh7xgIoiibQNk3PtOqdDSfSyzsxLM3TNLltvvslE9zbPPf88zzx9bPjZP/zhD7l6bYfRkTHERpu9CDIWiw8GratVNE1DP6DdvgfPGzy4IiLpRJLdSoWd7RLlap2wOjjmyclJpscnMb63iaZoCMJAOvn+YNezHJAEhPvEARzXQxRAvq8SUy6Xh51aGPgE7ak49ft9stks7XZ7+O87Ozu4rsvk5CSSJPGZ3zxOeixEt2MxeyjB7NzBC+RiNsRY3E/Jckl3pyn94CYhwU/f8egJHq7pIuGhxnX6V3Z5bCRMPKAiCMK+QLLVarG6uorjOIiiSDabPbCbcj/u9ckqlUrs7u4ODdjX1tb2vVYQBI4fP87S0hKZdIYnPzKH8+3b3FraQVE1jqciPPXyLJHJgwOB1ESU3/2fn2HlWomrb2+xfrNCs9jBxUNEJBBSOfn8FMfODuiav4idvD0IgsDks5PYho17qYgc9bPeMalXu2i5FpImUe6auM5ALKVn2QO6kyoR6XsoMzFqIRXJFoim4lgVi/xWHllSkQQRy3YPpMjGJqN08i2stolyD3XKsl0UUUC9h87mWS6CIuFJIuVbZbKHk8w+l8AfGGOh3qJv9pnWR0gkEwO1XjGMYRg0Go2HJjm2YVO/XqcswGarRzrk23ecUVGhXetTF/yYIR25JlMr1cjZA9PuexO90dFRNjY2KJVKPP/83c6f63jI2kC4xOz3UR7CGhAlEatvY3R77OyUhgU527YpFotDEbFEIsH8XIJQ/AqeMVhbup0uAf/B3XTP8fD5Hz43AoMRgNTRFOuX1zmaSbBUaSOJ2iDhvgMtoAx8/lomnu3i3Wm4Gp5DNOpHN13kTGCfn+nD0Ov0UQMK4xMJJMlCVbv89//qWRzbRbrvPulWumy/uU15qYwaUpl+bHqYdLbbbaqNgSqiJEpEYhHGkn5Gj6Qwqgbd3S7tC0UaPgUtpLH95jaKXyGYDtLu2Vxdr7BWzCGLAomAihr0YTkWkihQbpvYjkcmrOGPBOjWW7DVRIr5EMMazVaTcCi0z37iXrR7DiFdJqDJNAybQPYXR6n3vzba7TbvvvsuuVyOf/JP/gnBYJBgMEilUmF1dZWRkf12Oevr63z5y1+m2WzyK7/yK5w5c2YwZ/dTJnrLy8v0PJmcPs0737mG1le5ubmJHKswPRrFV77J4tQYv/PEU6yVO1zNNcg3BmbuY4kQwWaf33t6al9BfLfUZnO9TjiicX3px8iyzMsvv4woikOJ/XJziedeOsTM7Cw+RUSTJXZ2rKHH35EjR4DB+j02NsbY2BS5C8t0rR4h9icmoiJiG31W1zeYiszRbDZZWlriiSeewHEccrkcW1tbGIZBPB5namqKEydO8B+//jqFjoigq1g6+BoOjmzi1HuoE2E8RcSDfRZLj8Kemf1eAWtiYmJIiX388ccZHR19YL2+l7YJoKoyJ04/2qgdBsXrjx5J0zFtbnvLjHUklHqPZrdPV4BWNoBvJMDnF5LDJA/u6kLAgBlXKBQwTRPP84hEIqRSqQ+UdKXTadLp9PC33jNgl2X5AeuPxcVF1tfXuXz5MidPngTANNv4gz0OHTr5vt/184afOtEzTRPXdfnGN77BN77xjQf+fY8q8YuiunkvbNNm6e+XeevdbXb1gbSvI4U44n+MoHG3Xb1XbdjjE/t8PkZHR3nnnXd4++bbFI0iL8fnEAQF2adheG1mPjxD8J6WtyyJhB4xT7AnHhDNjPGjq8uoVoTeG0tkImkyskgpt8JXt4J88ql5Tj6k4xOPx7lx4waVVoVCrUsFj9GoH1EEz3Wpl/Kkw3668Qi59RrjW02Ck0GKxSKRSIRoJEI+nyeeymA5Lp2eiWsZ+NJBkDoDRSxZpNFskM1ksWybne1torHovs5e33ZpGBbbNROjsI3d6hL0RPJLt6nmOjiijC1FOLQ4h67ryHcq6rVEDmfdoOMYg83FZVAF3rte1R5yXEdO7E8su32HgCoTuS8wa7Va+xK9mZkZ/v7v/54zZ87w8ssvs76+vk9md2NjA2C4OOq6wosfff9h/7BP4VPHR/iqm6O0XkONRtgp1hEUHRBwkz46AQXDLzOrCMRWG1RuVkge3l/JC4VCw2Fh13UpFArkcjlgQOvYk7J/FNLpNE7fYefaDsuvLVPeLZNSUmQPZffNsxw+fJjl5WUChwJM1BWOPnV8kBxORolORx85+6LoCofPjnHozCiVnSbNeg/LdlBVmVjC/wulrvl+kFSJmQ/PIKkS6uUiCU2l0OizY9pURZm26aCrA6PpuCLhtz0UR0AZD6HOREgDxWWFlu0QdkQCBAiFZbSdOuWmzUj8wd8yOhrCOJyicr0EMEz2LMcjFlAGHcE78PoOQlyhdLtCdDzMhz+7gCQPZnbGp8ZpNNt895uXCehNwjEfZn8VAR4pQuVUHWr5Btse+FR5fzLqQaNRJx4NYwoy27ZDerfLhKlhaRahUIj19XWSyeSw83yQT6k/pNDv9onGMji2TbMxSBDup2+a7T6+oMzSrWvoPh/nz58fXBdJIplMDgpRuj5kDozMB1n+YRnXjeI4NqL8YJGqU++hhlQWjry/KpxvzEfmSIZevo8dD7Jc6aCIElLXwO/XMTsWTr0Hrf6gIg64rocqi4g1CzMTwD8W/EABUKfaIzsfv+OVNxDGKeVKeGWPynIF13KJTkXxRX3kzuVoF9pEpiND5VQY+DO2bBlLDOABEh7d3Sq+O/eM3+8nvhCnudVk+e+WkWMyrd0WmWMZTMvh3fUquUaP6UwMSRTo1tuYootrOCQTYXq2g9G3KTZhJOLDHw3T3q4gFlvYQn8wZ3nnGjq2R6PUxmz0ECSRUCZAz3U5MRZBvJMs/7Irav40sG2X3E6DGzeWmZmZ4ROf+MS+rnUikSCRSJDP5ykWi4yPjyPLMtPT08zNzXHr1q2h4XooFBpQpw8QsXgUNjY2SI+M8pW/v4LR8xG5WiLYE5FEiY7c5NzSOqGJIJ/+2GOossihbIhD2RC24+J6A1XOr1WvElTvri2b6zX+5k/eo77VxHB6+Mbb/B//z18YFt5lWR6Meig3COsKlfzWcC+XJIlr167xiU98Yt9xFgoFZmdT1LNVrHqXCvaQfuh5Hv2uhW8xzkgiguM4fOtb3yIajfKXf/mXQxPu0dFRAoEArVaLy5cv0+/3CeGQjWXI19qEz44hLrdx6ibqVAT9dIZKp09UVw7sVB6EnZ2dYTEXGJ5zv99/aEywubnJs88++wGv2H6Mx/z8+mNj/Dis8fW3b9I1POSOidJ3UTsWmiewnApyODsoWt+PPcGcPTSbzaGJ+l4R+6B5vHtxr7fwysoKtm0/QP+FgfJmLpfjvffeI5vNYlnWQ32vf97xUyd6X/jCF/jyl7/Mb//2b/PUU0/9wqtu3ovmdpPl62VKukQm5keWBOqqzEbVYONCnsyJzL6OhCzLzM7O0u12WVlZIZvN0hf6pJ9Lc/bzj2M2TSRFQopJdK3uI775QeyZrn/5zRt0mx6ZjTZdJUTZapIOpfBvNLAcm2tTbZ6e7xP1P/hw7bXzW70WVc/Dp0oDJUDXY2trMHycDAZp2NDsWvQaPW5fuE2v1+P5558nmJ3mndUiF370Ls12m2g4SM2SUfBIhTTcZp+O1iV8R2FTURWmpqfYyeXo9UwymTTljsVWuUXLMOmUKyi2R9RWacUlSqKObMGYZtLsGjRbLWzHoWP22W3bbLoOwU4Hb6fHZtYkFPSRDvsHlLCGCbaHdijxwCxL07DIhDVC9wQ096qn7kGSJHq93oELzF7XFthXBfugmIj7+ZVDGb71g02uRnQKEY2ALCGJgCzhV0SyPhlBFNlu9Ui8lyM2G0NSD07aRVEcShLDYFB7b2hZEATS6fRQYhgGtFS/34/TdVj77hr5W2VaPYd+X+Lv3vs2R55b4OxvnEUL3K1YTk1N8eqrr3Ly5MlHWog8DIIokJyIkHyI8tovC2SfzPRL00Sno1RuVXC+sYyoyIiWi+oJBD0ByQLRJyFnfcgpP1LMhyCJSIAQ06jXDMKJAP1On+REmJPTWV6/WaBcbRAPP0hdzC4mMfsmxkYHs9PG0WUkXSZ4zzNgmTbdXQNbgPSZET75O8dBbDEyMsvGxgaeJ/Ctv1nmyg92kIQiik8mfUznM59/tDy/LuvUWx06mo+Qvn/raTQb6D4dUZKQRZGWYWMIIs1Kk+B8cEi9KZfLlEolWq0Wk5OT+xIdx3GIZSAQDdBt9vCHfYRDIZqtFoGAH+lOYOPYLq1Ki+wZjXgsRjKZ3FdRLhaLQ4uHQGDgDXnqiTFunSuwdmmD+NSge6kq6pBGaJkOte0G809NMD3z/rLh9XadU589xcrfr2DdKGEgUGhbbNsGXqVGf7OJ5wwM6J2Qb2CE7nn4TAfXsKnWTcz1OqNH00MFzYNg9x3cvsOJp+4Ge9FglHf/8l1KK22agogrgv72DnqnT3g8TObkgIrmeR71rsVOzWCj0qF1R/F3r+/oV2TGYzoTcT+u51CtVSEIzXyTtVfX6Ok9PjT3IbabDrmGwUhEHwow+KNBpAmb5lIey+iiSWA7Aqbt0DAsUiENXzRIfbVIamxhX5K3czlPZ6sJ9uBIKrdlIkeTjEZ9GBUDf9JPePzBblKz2WPpWolu20QUBNIjYRYOJR/oav6y4it/cYmbb28jhWb4jf/hEwQfIpgyMjIynOMSBAFRFHnxxRd59tlnh89RKpVidXX1J0r0KpUKPp+PpZ0a1Z7E1EYLz5BoKiaJZALz5jahbgtlZpybxRbJ0N2ZtHsN2O/VHAC4eG6b6kYD36hE5XYXfyvBvkow8O677/L000+TSCRwHGfoSde54295b/JRq9UQRZEzh0a5sVCGyw6aAYWtHKl4Erti0FBFMlM6O7eucPnHuzz++ONMT0+TzWZxHIetrS3W19fpdrvE43HGxsbw+XyUSiX6TYPXb1cZSUYZOR0nqGqgyOy2TTo9i48eyRw4vnM/bNtGFMUHRNei0Si7u7sHvqfZbNJutz+wOftBGI3qPD4V4/Jbfuxyk4CuoYz4CEoCVqnL8rdW+Jog8Dsvzg39EB+GvaQNBuv7vUXsQCBAJpN5qCLsngG74zi8/vrruK67z4AdGM4tnj9/nk9/+tM/9Tn/rOOnTvS+9a1v8S//5b/kj//4j/8hj+fnAnbPpn9ndmqvCh7QJGqySLfVxzEdBEkk1zBo9QabY9SvkA3rzM3NcfHiRV555RV6vR6R+4LdykrlAw+O53I5EonEwPOsahJou3jNPpH5NMbODvb3voF+6CQBN8Lt9SKbRzIHJnqCIKAoCk7X2ZNlGHx+PocoiWSGVEUPEOm0O1y5fYVYLE5TS/G1dzfpmgPaYCoeZWs7hyEFWKm08YcVIltVfDPRfUGnIIqMjY6ytbXNG+9dxZD8qLaHr9pDr3TxSyq0GliOn1RQgHGFoqCgqCpaIIKkaWxU6lTaoGdTeIpGdKMLFQOj2mTNqROURZIZHf1IDFs3EXZ3UTVtICgiShQbBgtJP8ulNumwj4iu0Ov19iV6lYZBq2kyPjnN0tLScEB5D+VyGcMwSCaT71tpehj0hsmkIrM9EiDoDaikgiCgyiJhn4Iii7QMi7zlMrbdpLHVID73wXzp/H7/0APJdV12d3dZWloCwOfzceXKFcq7Zc5GzrJ5rcJNXFqOA5KEEAgyvmFx6RuXSD2eGm7wFy5c4NOf/jSVSuUBmodhWHzn60t0mn0+/KlFMtmfrKL7ywZREonPxYnNxujVesh+md1aB5/h4A8qiJqMGPEhBpUH1gQp6cdp9XFtF/eOz95k3M/RsRi3Sy1qnS6qCMFAcDhDIkgC0yfHyYd3aZdMrGIb3XLoGg57JSbBdPAndU794WnOvjBFvVFkenqacrlMIpFgdbnM8rkdUtNJvFYZ88fnqAVewLLkB2bp7j9Xz/MGzOp7xmKbzSahYBDDMAhoGqbjDkzlEahWq8ym73YJE4kBVTSbzQ7tX/aKGmtra7zw4km2lkxW3s2hLMooPplwJEyz0cSyLRRFobrWRI4K/MqvP8Pc/Cj9fp/t7W1c132oIu3EBJQ/1+XHr67SzdmQsLB0B8ey6FR69NsW6cUYjz0bY3t7m2AwSCgUOnCcodlsEg6H0cIa1liIC399jc56nXargyULCIqGp4uoEyHsLQ/ddNBUCdd2EIMaXsqP6EF7pUZehLFjGbyejdux8Jy7fotSRKO8ViM1F+PEY3cDt8qtCrVNk6uORV+QER1gp06q0uNwJgAC2I7H1Z06q7sderZDQFPIhPWhFYjnQce0uVVqsVruMBbz8dhEDF2VaFpNqrkqps/k1b/5Gr5DZ9EV5QGVPTmho4cH/opRXaPb62C50DYtgqqAILuE9RCdfI3QTBJJlmmU2nQ2m8hBDUmXcRyHTqmLt91CcT2aFYOpF6dQ7gmE222TH3xrmaV3t+mUuiB4A3VaTSI9E+fsSzP71Il/GeHYLhvXdzFqPTRHpZBvMr/w8M60KIpMTk5SqVS4desWs7OzQ8GcPUSjUer1+kO93e7FXmFldnaWL597m4AlQbmLMhIkgp/a228it9pMH3qScqXDxfUyz80ffHx79lB7iZ4sS5i9Hu1ij2QsjnzfWECpVEKSpGFHznQ8ipbG7arHznaFhqNg9AaCaoZh0G63h4nfyx9f4LueR/NWFakjc31rGSvhJ3IkwkjE5HLBZfrsZ7jWt7l5rYh67hopn8fU1BTPPvvsAwIqkWSGtbevsDiRJtfxqJa6KLTwPI+oT+LUSIARucPmZhdFUQiFQsNi1P3Y2dk5sPj8ft288fHxDzzv/zBcXKkgLdfIJoPIqbtxkRT1Ed9osHahwMbxLPPpDx4fSJK0b29pt9usra0N/VAzmQzBYJA333yTcDjMsWPHhu87duwYuq7TbDbJ5XLE43HS6TS3bt1icnKSubk53nzzzaEt1B421mvcul7i2Mkso+M/v4XpnzrRC4fD72ui+YsKLaTh1xWEnonRd/ApEvWuhVNr0kq6/PjWOrc3DIqrDezqYIFQ0wFmjqVZnPQxPjJCJpOhUCiwsrIynAGDgUn39vb2I4f5YUAvtG2beDzO5uYmgVCYfq42GIfzIHntGr2eiTaaQehBNBBibX2D42ORAysg8XgcURCJCwIF2yGfL2DbDpMTEziOg+mChkg0qHJj4wau65JaPM33bpYJajIBr4ek64QjEZrlAmeOHuZv37nFO5bK86qIv+OCBlbfot1u0Wq1MYwuourH0SMETA91qwJNE9evYOHiRRRMVWZsp0s2ksA4NUFPUdluWrR6XRqGNfAM61n0DAthNIQQ0/GLAo4HVVyk+RgT8ynkO0Fmo93lxs0CW9criLsGb6gib8sS2qifxLjMh55dZPwOn/u9izm++1dXMRoG2cNJxhd6uPeofsKDtM2fBkbVoGkO1Ocmowcni0GfQr5n0Oz06R2g4vlBsLcY7m3IvV6PL37xi3SKHW5fXiX2+DMYiko2ouN5Huu9HjfbLV5ohBlNjrK+sc7Vq1d56aWX8Pl8jI2NUSwW9/lGXr2Y572/W8Y2bfwhlV/9Ryd+uh/llwyCIBAcCRIqhYhk/LTrBtr7KNQ5ARl/OoiRaw/nqCRJYCGhsra6ix3J0LddjHoTvyoRDAZx7wTSXZ9M7EiIx5+eQO5Y9HsOruMgSQJSy+Lsbx5n9vlJcrkc2WwWURTpdDokk0n6/Tau5eITbYTvfRfr8Els20WRdUIhgVwuN0y+7oUW0tBkkZAq0ewNPKzarTZ+vx9RkvA8QBRod21Cqojb75Ea219E+OqX36W8aeFaLpFRmSMnPa5evQoM5i4UReZX/vEp/sZyWbuwgyCDHvMNDN9bLo16g1DWz/iRID/41m2+8p8uEk0GeOrFBY6ffHSwPzKu8JHfOs7qtRqFlRpG2QRBID4S4+hTEzz53CTRqI7runQ6nX2zfnvX2PM8CoUCx44do143+N5Xl+joEp1pAXMHlOU+kmojx1xCYggxotMudHB0ATfhh7AfJGGwcbsqnaUKNcNFc1w888533Yn5DMtBCms88+LMvu5M5XaFiu3QF2TGYn6sXItqrUc7otKtdOm1LS7ttljd7RD1KyQO8IMShIE9TdAnY9oOa6U2Rq3HIU2ldbuCaqsEegF0I8rObp/E2IOfIYU1lNEg5s0S/kiAbDxIsdGj0THxYZNNRHC6PXTZh2H00DQNs9EDx0XSZWzHw7Rdwkkdpe+wc6PM6Hyc9LG7wgutlskX/+Q91i/k8cf9pBfjQ7ZNr21SXKnyzY0a7WbvA9Htf1EhySKPvTjNhR+s408K/Pt//8dMTI6RSCT47d/+bfq2S7HVw3E8gj6ZZFDDtm2q1SrPPPMMnU6H1dVV4vH4MLGLx+Osra29b6LneR5ra2scPnwY0zQRRAnJ9QbK05KIffMWoRs3sD7zK4h9iXAgQKvTeSg1NBYbFFv2MDat0vf3CHkR9JCPkneD733vOywsLDA5Ocl7773HSy+9BMBO3eAr721wc3uXeDzOzbJHMH6Ur1/awfUERNzhrB7AoYRM/bTGhYBIs64xlThGNGgxPZ7h25e3KBd97H7pXSTHQ8pEiS5kGDs0ysJCap9Ko+d5nN+s88byLre3uiQScVTZxUMmGwnywkKKY6OR/cwLy6LValGr1YY2Anvo9/v0ej0sy0JVVVzX47vfXeHGhRz+qMCHXh6n0Wg88L7Nzc1H0u8/CIy+w/pKFb/pIo3s38MESUSN+HB3muzUjJ8o0bsfe7OjMChil0ol8vk8b7/9Nu12m2azRb0YZjfX5IWPL9JqlYZ2PZVKhVdffZW5uTkSiQSCIHD69GneeustnnzyyWE8/o2/usrW+Tz5D9X5g//xqZ/6WP9b46dO9P7ZP/tn/MVf/AV/9Ed/9L86+/95Q2g0xOHTWWqvb1Aod6nLAnrfZSrm4+s3z9O6tkayrTCXHUELqXge9Jaq3LpV5dZ0gF//7TNkgGw2i+d5bG9v4zgOk5OTaJqGZVnDKsVBcByHnZ2doWKQaZpMJUNc3GiiyyLW628i1eqIn/wExds5UsemUCM+Thwa59atWywuLj7w2fF4nL7XZywd5NbtLbZ7PRYmx+g5LrVmB9sT0DY3aC5EKDaKzC7MsW3IgIXi9nAFgXAkQr/fR1EUAsEgC6kAlysey6Eevdsb2LqA6xMGVahgAJ/uY6vcwug5qFstvJYJMR+i6SIjYk5E8KfDzKVC+KomiiWzFpDZbZmUmj1mUkHkXQP7wjYREwSxM1CES+i4x1IE/TK5bp/Rtkk2otPoWtx4K0/veplA12JiLEwqpuM6Hp2cQWWrx9dztyl/tMNUQuXrf3WD3mabYMLH6psbSP4EV65c2Ud9vHF9HbPHcDbhp4Fru4PgzHuEafqgxTFohBysvP4TQ1EUjhw5gm/KR1MxWdI0Yj4ZQbhjHRIJslup0mua5NZz/P/Y+88wudLzvhP+nVw5V3XOjdTIeXImhzmLFElJtLyyLWnt17ak9/Je8q68tNf2Zdnyev1KlmVbq2QFihRFDuNw8gxmMDOIg9QBnXNXzlUnvx8KKKCBxgw4oi2S9v8TLnSFU1XnPOe57/sfys0yH//4x9sW0depE/l8nsnJSXbs2EE46sEX9aDXTcL/UyfzfSHQGWDt7Bo9US+Lufrb5vJYtosgiPTvTlIuNHFMB8u0OH/+PAsLC3SmUmwbTbCYr7NaalLULdZXcyiyQjTo5fBwCq9dZ/vgZr1nYaZA6GCI/iPdFItFVFXF5/PRbDbb3efe/jCxLh9rX38Fb98uKnKS3p4AqQ4/qirjui5ra2u30X9CfSF8cQ/dukjWNimWq/g92qYOqm7a6JbDTk1FDFr07Gp1cG3L4ff/w0usXSqhehVsx2J9yqaybrPzXpXh4W6WlpZaGZxeLw+8r5O+bWFmL+YorldxDBdf2Et0WMSyTCZeS6OhofoVlpfzrE+eIf/xBg89PsJWWFst8+K3ViguN1FkhVBngHuP7aB3IEJHVwCf9wZTQhRbmZw+n5/pySzzs3ls0yEQ0uju9zA4OEij0eDU64ukF7KE+vzUizKNooQVA8EFK+xC1GFwuBd7uUh+oYgsSYjStTrOcVGbFna6Tt1yUUbjNFWR6wkbRr6BrFsM94YRZwoUe8NEBiNAyzHT5tqE1bSxVitIXgVXFnEsl8srRWZLDZJBzx0NwG6GYjjENuqsr5QxFJmkqhAMhlF8MtW1Bm5ex8rr0B1A6QpscihUe4Oo+Rr6RoVAZxDLK6GbEqagUqw0UJoGYtMkEIxQq1axHRvbhVrTQBBFIn4VnwNOSUdWRHrv6UWURfRKSw7xzNevMH9ujdRIDPUWLbYnoNG5XSO/Uua1b0zS2R1ix9g7xwj8uOKx923n/keH0TSZ554TOXnyJMMjI7wxucGZU6vkZvI4uo2W8DKyp4PuhMm9B1pFj9/vZ3h4mHw+z+zsbFtLdet0bSvc3CicnZ1lz2gfT5dWEQMqjUuzaG+cwHrf+2lYAiFZoKZJHBzsolAotBpkgc165FgsxoULF4AW4+bipdf5u//4PZi6h1BY46VXSpw4cYKzZ8/y0EMP0dPTg9frpWHYfOPcElNLaY7s7MfUdTao0dPZRR4Pr15d5/HdvZw4cQLHcTAMg0gkwoHRft5z70EEQaDZbHLhyiT/8s+fpdNM0bveIBmPIyoy7pqFYeR52RWIBzT29tyYEF1aLfP05XVEx2bUlQnmdARFwoh7WGlazGVrHOiLbPqciqJsMjK5GdeDwvP5PIZhML9Y5qU/nwDDxnJcEikfxe7MJjdK0zRZW1trF73vFo7r4tjXmBtb3L8ESUB0WprQHxSu6/disRiu69LV1cWJl89Tn+ugkW2iehSOPRLFdV1s22Z5eZn3vve9OI7D5ORkW2t65MgR3nzzTQ4dOoTP5yPeGaDQ6SfxI+4f8K4LvbGxMb7+9a9z6NAhvvCFL9zRdfMTn/jEX+kAfxghyiLbnhjBE9CYvbSB0bCIpfzIo1HeeKqJdGGV1I4EWl+wvYlROvz4SjqF6Qrf+eY48c8eoCfibTsoWpbFwsICiqLgjab42iuX0S0PpmHjD6iMDkbZ1hHEp8pcvXq1rY26nve2O+zh8lKR+htv4ZmYx33sCeSCixLzMRNwOJbyM5gI4sRGtyz2otEo86V5pD6R8Jkc3V2D5A2bmuEg1012+v2UovC7z/4unqSHj3b2s+Y08IkmlmkRi8dpXMmQubREqC+A4zikwl76LYM5Q6NjRwLPbBG75lB1Khi2ieNC0VCQazpSRUcJe5EtEc3jwe6L0Ixo9EW9hIIeLMPBXaqghmI0DQtZFmkUGkSuZLCbFm53FFcUwHIQMnWkK1mUo10IwGqpQcijMP7aIuZbGwgemfiOGB3x1qZDAiJxL6G6SX6+xJkXMvT9zAECniWkgEskHqBZdunr7Wdi4gIDAwM0mzrPfXuW08+uIMkiXalpHnlCIBQK3TEf8E7QQhoeQUQUbAzL2XJz1TBsFFHAK4so7+Dqd7eQJIkPfehDFOeLvLVwGbnapGk57Uw33XKQHZhc3uDsa3Vi/T3MPjNNLOZl50gHXtVpB7B3dHRw+fJlxsbG+PTfPU69ZrBj1/e3acrnaui6TUdn4I5Njh9nRAYieGNeJMMmHlTZqDTpDHm5lZXjOJCuNFGsOqdfOoVqBJl/vchIfQRBUnBdl2QiQVfES1fES7lhUtNtHNfFMg0Es0EiqqLrLoZuoGqt87W0WEINqPTd14cjOpTL5fakemNjo93MiAQUnjz5O7zUs4fcru3sHEkwdjSEfO28DYfDWxZ7siYT2xlDuVilU3JYbjqYgkOA1iQq3zBRbAlvrcCl01fZ85Ex1jJr9PX18crLl0hPVoj2hfGHPVQrVbweL8uX1jFVhYGBDjweD4qiUK/X6eyKs3NXhPJDVWZnVnFcB8du0t3Tw5d+6xySUKT7pslPbrHEG9+9yu4DncTjm40OqlWdP/ud18nOloj2RBBE2JjIoNcMdv69ezcVedeRSVf5xp9eYGUyi9WwrjEtXOSAzPH37eCx920nHquhqR6i0SixaBRnaR4z0sQ0DIjKCCkvNc0guCNMTTQxVnWs1TJKUEVt2AhFA8enUG6alCsNbMCpm7g1Eymk0bk/RdehHoyqwcz3Zhh5coTIQITwQJjQ5Q0EwSW3UsYtNtEDKl0uWB6JuUor8uduijynZtCczOOUmniDHpYMk3ylgdIwERURLAddFVtawek8rmGjDoRvUIkVCf/OJMXzqzRWSwQSfpJBkZhfwXJcGmWLTFXHWs0hyQq6JmKrIp6aSTjpR7Vd6qsVUv0RUns7OH16mdnfS9NsWmA5ZGby+LoCqG+jaYr1hFi9nOatU8v/Qxd6ANq1idHDDz9Ms9lEiO7kL377NXwFi2jMj6TINKcLXJzIsXgwxdBwk+6bWCjXC4/19fW2Ycvy8vIdC7319XWi0SiqquK6LpZlsas/yqmFEnn/KuFXTtDYew+aGyKAQzbiIif9jHWF6U8F2oyam4s9j8eDruuUy2WefvppHn744TZDynEcDh8+zPLyMiMjIywvL/PJT34SgKn1Epfn1ziysx/RcMicmkPKGPTek2BuLce5hTx9Xov+7laUkSzL9F1jPI2Pj7O0tIQoilxeqzC27ShdE0XqHR60nlZj2LUdWKogLpR4q6/A7q4QoihgWA6vz+YQBVCurCKv2dQdt7VeJHz0H+ticqM1Yd/VdWcnU92yERCwTR2v14vP52vLSar1NKo6h+YRqFUNQqEwrptjdXW1fa9dWlrC0E2y2Swej4dgMLjJXfVu4VUkEl0BlmQBf8VACm2e5lslHbfDd1eZit8vFEXhox/9KOl0mvEr06zlwOMN0zMUpbOzk/n5ecrl8qaMvJ07d2KaZtvLYO/evZw9e5b9+/fz8c/tJ/ueUZKpH20X33dd6H3mM59p//tXfuVXtnzMj6vrJoAaUBl5YpjeYz3Yho0W0vjKiVniDQ1trI94XwflSrllEXvNMUwKa0Rsh43xLJdmsvQcvkHPlGWZVHcf331zjnMnr1CdyROXfIiALQhcTHiJbYszNKTy8L7+dlGdzWbZtm0bcdflgdI8ryydp/6Ln8Cr+rBEATPchVRd496+AKIoIIqtoMmpqSl27LgRtxCLxfje976Hpml8/Jc+SG2+RnaxiGk46H6HHfeMsGp5+daS2poE7T3A5MUMXq9DPJXCLjXRx7PUlgukJA/phTX6B3rJ5S9R8CtclZvEO8G30sBeaCJIgM8LloC/bJMQ/Iiqj6bioneGaAQUOiLelg2v0KL5WLkGbs3EcFwO9IZZOrtOZa2Mdzh6o3Mki7gJL1Kujp1v4g8p5Ksm880CtUtpFI9MrDtIf8y/qbMMrfyb2FCEjZk8r59c5Mhjg5z4y3EyqxU69iQ5cnyQKxezJJNJmnU/i2dyaB4Vn+pj5s0Ch46ZNJtpdF2/7XzRNI1QKNTK9rqliAn3h+no8JPYqJC5Nn28eXPvOJCv6fRIEsmu4B0jDN4Ncrkck4uTTK9M0Fg3ELdvo2HYWLZDrdJEmioyLgr4awLFM5O4gKUIvNETpHd3inuO9tGb9LKysoIoipw9e5aDBw9+34XaqZOLvPiXV7B0m1339vKRT+/9sS/2HMuhvFKmtFBCr+gtx1IR6msVDgxGOLNWZqVYJ+hR8KkSuFAzbKpNk1RIY080wnMn5lkyGgQDCo1nZ1BjKgceP070JofZkFch1N7oeoEwlXIFwzCo1+p0dnRSXiyjBlSGnxgm2B1kZmamTXPZpBl2XfSf+zkGxAp/6+v/B7ptUyqViMfjLC4utt1or9O1bjU3Gjg2wERhgsFJ6AgHWMeldC3fMqyIjPo1Ir4OXspe4XLmMsP5YSRJolEF13Lxh2/aHAguggxhTxeKorT1G9e1G5cvX6ajo4MDB7e3s6GWFhqUN+qEuzd3aCPdITYms8xM5Yjfu/mmfvmtddLTOfr2dCNfM0HyR7ysXUlz6ewqjz652ZCoUtH56h+cY/VymthgBO816qNjuxTXq5z8+gSSJLL/aA+BhJfsfIFwZ6BVpDVtfKkQgX6Nrr4b9FdhuwBDItmZAs3ZPPZGHV0RsdyWQ6p9LZZD8SkEtseIDMVoiAKvzxXY1xumQ7dYeGkB3yd8JHYkGJ7KYV3NcWUug+pKdIkCXSEvlYiG6bQcX9/x/NUt9KsFnLJO1a9QuNZQwLSICa0luSFBo6SzGtHo9quYiyUERUS9ySxF9KnoXRKBkoqd11EVkaFEBL+qsGI4aCMx1N4QlmlgJVSqfiguNMkt5whJHjq3xRk82sP3np2mUjFQohqyKlFYLtOYLWCUm4gIxLfFb/XgaCOQ9DN/YYNcrnZbof8/ImRZZu+9j/JHv/0GqaaIOhJBN3UCAY1Qhw9fsUnmrQwv9C3wuQ/uvE0jdp2tNDm7wOWFPN88mQZHQVUltm1LsGs4RkC02nFF0Co0+vr6iPhVHoi6fO/EN1j+6HFSo7sxXKj6ZHS/y4EOhZFk6zcaGBhgYWEBQRDw+2/8brZt89RTT3H8+PFNMpharUYymeRnfuZnWFlZoVwutyeO58enicXiKJKEPpOnemKRYECininRGQvhiDI79gwy3BVvU1W/9KUvIUkSR44c4ZFHHuHcuXMMxbZTfnUZdAsxcqPBIEgiUljDn2uylm9QaphE/SrLhTrr5SYp06ayUEPqCCP6VVzHxVwsIy+UkXoDTG1Utiz00oU6r7w4x/yVNIIgEumR+Pgnjm56zLbROLsfG2L23BrD+zo4fKwPRezm7Nmz7Wben//hGaobMYQPhgiFPFQqlS1NW0RRbNMmt9L6iaLAwZ0p5s+uUZ8r43NdxKAGtoOVrlN0XRI7E4ymfvBTMkEQmJ2d5fXXXycYDPL3/vdPUqtZdHYFqVarTExM8OSTT962t1AUpR3Avri4SCQS4bXXXuPo0aN0dt2dH8LN0HWLpcUi/QMR1LuMwvhviXd9BC+88MIP8jh+ZKFd61ZkKjrzV7IEHIFQf0sDFQqFcCybQrGAx+PB6/MhRTz4s3WuXNjggT1d+K910Gq6xV++PMfUMzOEywZxj4+SXSWaSiEjYRYaVF9d4tScj2AkzsNjgTb/GsA9c4Z7/8EX6P7SU0wmB1krN9lYWebDx7fT7Rvkwpk3GOx8H4IgIMsyIyMjbbqdILSCjs+dO8cXv/hFotEo7j6X/kID13ZZz67TM9rDK3/2CqOjo/zSL/0SX3v+ddIVP7sPX8uW0WQEn4xr2RSNCk5DZOn0Cpl8EcsU6Y8olJIahYiHhCHQrUvUMnXEkkG0IwFSnfJAkJLgEAxq9AY9rWnG9evxmvbQdVtC+m0dIXyREgsKVHQbxXbxKC3HPhQJHBAMCwGFcsOgNlciott0j8bpCHnuSIkTPTIhv8rypTSPPTZM/2CMakVnoD+CYjl0BjuZPDtJR/duGtUGclAgFAtjmTaK7KP3DoJdXdepVCosLi62mx/XNTuCICB3yvStOeiy1NrcawqqLKJbDtWmSUKVGJRkUmMp1C2ys6obVcyaiRbW8MXv3hRmeXmZZ154Br2pc9+u+9EdiTwOS+tVuLCGT1WI7utC6w0iXHP3c+oWzbUaS/PTLI9nePxju7lnx0Bba/Hd736X+++/n3A4zJtvvsnRo0fbn7VZNZBkcVOH3XEcXnv6Ko2SjhZUufLqEofu7ad/4Pag+R8XlBZLLL++TGGpTKGmY4oCguvisRzcXBNrpcLhw52sR7ws5GoUaiYC4FUl9vdF6NFkZl5fBm+IwLDLwI4BKvMFGpfyLJ0u0PuB5NuaOgVDLRH/9IVpJmYm2HZsG4MPDBLsDrK4uLhpc3TzZE7/P/9PhPPnUV99FWQZTZbRdR1JkgiHw5toWpFIhEKhsKnYE2URZVRh/9g+MpcypDaq6NdM8Ztmk77eCGbM5L6D94HYKhRFUcQ0W3E+tuXiYiNJIrVaHUVS8QZUqtVqe8MXCATYu7elDS2VSpw6dYpYLEZnZycLs/WWk8gt139rnXFx7NvpRBvrBSRRahd5AKIkIMoihdztLskXzqywNp6hY0fitufEeoLklhzOvjDHoXv6ePgTY7z01Sukr+awCk3EiEZqdxJDbLTMV2QJXdfRNBUlqOKPeClbLk2vRtG2saoGsuEQ2RYn2BXEE1YRFRfbtvADVcPmzek6h7r8eK/mCVwMMHR8iNH3jTK1OIWSXscX8NPdMUhsewdXi3VCwt01WOxcA7vQoB5UyVV1BKEVXG0IgGoj2w6hgIZrNUhXdTKqREqVsVaqyEk/oibhOi7lSpmO/m7q1RoNr0qoqLdMtWSdsCLTPxjBn/SD2zJCq/uDXC5dIZfeILK/n3s+uI9nn5qgqUl07Uq2DWP0dA0jomFqMrnxDJIqEhnaek3xhFQKy2VKxeb/LPSu4eL4BuZymXh3CNGv4MFLtVLFxSUYDhIsNlm4uM7aA4N0R26f/Fxdr/D0iRy5CxvUljKE/V5kRWP1lUXeGIrQs0Pjc++7YW5WLBZbcVTlMsOf+wjveewDpH/2g8xmq5RKZXqjPu7d2YOdX8ayrHb0w8DAAPPz8wiCgM/nwzAMJiYmuPfee29zha5UKiSTSQzDIJPJ8OSTT3L16lWeeeYZfD1j2PXW9e94BGqqRTgeRPQoCIqKRzU59cbrTHkkvF4vQ0NDfP7zn0cURRYXF3nqqad4/PHHyS43EVywHQdZvuVefS3m13VbFEdoMWdsx6WRKeCRVMRrsQOCKCD6Fex8A20wTPmasd/NKNcNvvz759i4sEEg0JIJzU8bfLl2jp/+uSN4rxUZiizxyU/uQf/oLlRJbN8XGo0G0NpXrS/mCCsJSqUmfQOR24xiruO6BjmdTmNZtx+TqqoMhP0ceWKU08/PUlsu4cnUcUSBhl8hfKib9z041N77/iCh6zq9vb309vYyNjZGMOQlGGo1tLPZLHv27MGyrDuyrgRBaDNXEokEzz77LDt27GD//v0Abffyd5KrPfPNSd56fobjH9zJEx/Y/raP/e+Bd/VNN5tN3nrrLQ4cOMBDDz30gz6mH0nUdItmWSd2C0VElCWisRjNRpNCPk8oFEJRJJpVg5phtU/2F95aY+qZGRJ1C204giCJOGUZE5uGpRPoDBA1HeSlMie+PUVHzIfWyLZOyvl57A99COG//BdGH7+P65Ly8+fL7Oz04fF4qAwNtX8zaHUwrmffJJNJnn32WY4ePdq+uAVRaBcMdtHm29/+NgsLCzzxxBPkcjmGOmPMWjbzqxmGupNU9RqzwSIrsRK+kICSvTbVUn0I1SwPDfSS6u7H9CWYKVrMr+VYXU9Tk0JM56p0Xjbx+lS6VJu+ZAjtlo6yUzURfQq6IuGxHSzbQVVthmJBanE/+ZpBXbdxXBPBcpENi4ph0ajpmI5LqmIwNBAlvMUN6VZ4Ez6Kq2UWFoo8cKCb4nyRy09fZfZSmkK+SjaXZecuL4LZwFwDR7bZdl8v3d1bC4td18UqW2iWRrQnelsYuOu6lEIl9JpO/dw6quWSqblU5JYovdcV6JG8DN3TT/fRVpe/2Wzyla98hQ9/+MNU5+q88rUJ8oUGXd0hHv7U2JaunK7r0sg3cB0Xb9SLILXcVoeGhnAGHB56z4OsXUzz4rPTNKbSJLpDRA72IAU3L4pSUMUfVPE2LIqzJZ77i0t4Prefg0MxRkZGGBgY4JVXXsHj8fDiiy9SqVQ4cvA+vvvnF1maziPJAnvu6ePRD+xAViVEUUS55ipoNSwkVUL5MQ5ML8wWmHlmhqWNKkuCQ9FyWjd910VTJJIJjdCCQeH0Op3DUfo7guiCgCCAarnYVQPJFqgHVOS+CDvGWu5z2o5O1iyXdElHrxhU16soPgXFpyB7ZARRwLEdrIaFUTNwTIfevl7MiIl/t5+SU6KZad5GPzbNllul+bu/i/gHf4Dy5pvgv30zHI1GWVhYIBQKtTu90WiUfD7PxsZGO1uro6uDrr4ukjuTZCeyVNYrGDWDbMmhEWpgYTHQNUDDaRAKh3Ach3sfHGTqdJ70eBpvWMTBway6mJZFvLvlmOc4zm26QMMwGB4eRlEU5ufnCUc1/HEflXSVcPjG9VpOV9FCKgPDW3RvxZbu2DLsduHm2C625RCJ3b6eXD69iqTKm4q8mxHpDJKeyjJ+aYN7HxhkeFucyctpnv69c6g+maBHoVrRqWZKhDqimIZJIHjNdKCkI5kOnp4gdrWJ6lcRTYfOsSSeLUxTAsB6qUkJjd7OAPnJPFqPxplzZ3gr+xZKWKEUNLmQv0BosULd00Fn+J3XSNd2MNdrcC2gHMBzzVXZkQQsr4JSM7ElF8Ew0WyZQs0kEJXwVQ3sQgOSXirVapvxUmpa6CJ09YdwBBEnXccT0rDqFqX5EtCKJPGGvQR3BugdSJCuLfL0t86gOz66u0NtIxqgLWz2BDQapkNxvki4N4Sw1driQMvn9Z3drv9HwdpqBcUG0X9jTxMIBnBsm1K5hKKBkW5QqBu3FXpzmSpf/4vLNC9skAp7Efb10TBaoeZ+XcCYyjOX9/FSzxqPH+hpO9G6lkX+fe/DHh3l6H/4tyC0qI3lcpn0+io7O0PYyR1MTEy0XRWhlYk2NzdHIpHgxRdfZNu2bVtGBOi6TqFQ4Nlnn2Xnzp3Mzs6yuLjIPffcQ7pqMttwKdYNDI9JbY+X6GCSUr3C5PQSw3EPA8NxDh48iKbduNauZ9e+//3vp1gsYpXzNHwiXtdGs24I6l3HxS7pNHqDdES0dkSCJosIuFheGUWTcGpGe6Ln1EzUoQi6ZRPy3L5dv3BhnfSVNJ3DMWRf6++Bis7axQ0uj2c4sn/zd6DdErcTDocpFArYts22wwqPPHQf23Zs1m3fiusa5DtFZxiGQaVSYSxu43ksxesXDJqmgiiK7BwOc2AoQX/k9kLr5MmTDAwMbGnkdTfIZrNUq1V27txJX19fey+7trZGs9lkx44dOI5zzSl8oJUHeoeGP7Sm0p/+9Kc5ceIEL7zwAoODgywuLlIsFvnoRz/6tsfi8UooXgWP969/mgfvstDzeDz8o3/0j/j3//7f/9gWerZpU5wrsjGdw9BtOoYixEfjW05ToDWuFqTWxbwVPF4PHo+HcrlMs1pDFmJI17oquarOlfOrBMtGu8gDCAZaGVDhUJhqrYrjOPg7AjTmS5y7tM6RAZBKJYwnnoB//I9RPvaxTe+ZSCTIZrP09vayc+dOnnnmGbLZbDsMXFEUkskkv/d7v8enPvUpVlZWSKfTaJpGuVwGWh2OpaUl1tbW2LVrF48++ihTU1M8+dhDVE5O8ZUTl5i6dB6/18PyygrBziDKtQ2HNxBEUMI8vm+Qx46MtoW/88urPL12mU9/9D5OL5W5tFSg11VxV/M0ZBftFm2IazvYpSbq/g48AYXhoEq21tKx+FIBPA2LZCpA03ZoNC2s1QqMxujbnSLftOjwK1SnigTu8NvdCkEREWyXZs1k6pUFXvrmJOlSA9OnIvk9NNwIr11ZxjYhGfXyxMd3c/S9o1vmMbmuy+QrC5z4zhSG7rD3SDf3fnJsUxixIAhEEhGOfvooA3vT/P6v/z4RAuzfdxDNIxPo9OHr8yGlJOYX54GWZmpycpKZiRkixRGKhBHjPvLzecLPzfLocHTTNMd1XC4/O8OZVxawLJuRsTj+UYfd+3ezY8cOCoUCoUSIGV3nylcL9O5KEdqefNuYD9ErExkIk1so8vILs2zvDuHXZGRZ5uGHH+Y3f/M3sW2bF55/gdPPFqitCQQ6Aximw8lvTqJ6ZB5+X6vb9fjHxvjeVy5hNCyOPD5MV/edtQg/ymiWmsy/NM9Cusq4bSMKkAho7ZiWhmGzVNOJDYcZqZgoPgUJMHNFwpEwkiaR2NsHURCcKs65zRMlxefB3x8lcI8fuRqGCtQyNfSS3prwiQKKTyHZn8Sb8jO1UubiuUWEiQqjezsIRips237DkKRYLBIOh7G/+12EX/1V5BMnIHVnHVN/f3/bCOA6YrEYa2trXLlyhSNHjjB9cZqF1QXmT8+jl3Tspk29Xm/ZX0+XSSQSrE+sowQVuo53oQQVrr52he0BlYurFTJnCiiygifkYeeBbvpML3JZpkRp07Fks1kEQWjbpodCIcrlMgMHQpz+do71ySxaSEOvmgiOw8j9KUSpAdw492q1GvsO9LJ8pc76VJZQRxBBhMp6lVh/mL2HN0dJOI5DJVdHDdxZE9bKvROoVwwA4nE/99w/wPQri4x/5yoNj4yhGwiSiNnZRIrL4A+ACFaxCY5L3XGwbRelbqJ1BNC2CB++jrBXYb3UZMeQH7HYJKyEefzxx3FLLsvZZcpKmeGhYQxPhGKV2zShW8Eu6TgVg6ZPpqmbeG8ungQB2y9T3sijV0zQHZL+CKsCbFR0+iWJ5moZ12sTDofRTYul8Ty1+QJ+G9ZUkTVXQAP2/+Qexj64vcXQEAQUv4IW1Ngn7gNg8uwif/z/nCQ8EuDWGu16xILrOCgBFb3YpJapE9iiIVcrNvEGNeLJdxeR8+MIQXBbDBrH3SRzEK9N76uVAnW9jnPLVMdxXF5+fZH6pTSprmBbo9Uwm63fW9dBdtDWypx6YZ49w3Eyi4uM7drF2k/8BIph0PHVr7ZPRFUWiUfDTE+1YoEkSaKrq4vl5eVNYeCDg4P84R/+IV1dXdxzzz1cvXqVUqlEoVBou2Wvr6/T1dVFf38/hw4dYnx8nHvvvbdlHFMsMr4+zVRGYO7qJPn0OrGBfqqmzL5do3ziUB+9Ec8mirrrupw+fZrdu3e36YzBeAeX02+xqJkMZ5uYdRdBFnHqJnZYo5rSeCCutbP/eqM+vK6OGQsTGZHRJ3NYudZ1Lid90B/Ethy2d9x+3qbXK4gO7SIPQAlquAslcunqO/7GHR0drK6uous6O3YM3LWuvt4wttQmQ2uiF4/HicfjrCydYOqFP+Q//+7vIYkCotDK9r1uEnMzzp07xwsvvMCuXXuxq70Mbo9z5PjduZnPz88TDAbbv8t1Gu/8/Hy7mQ2QKem89NoqxadWsC2X7oEIh470sP1t2EMPPPAAV65coVar8fLLL2NZFn5/AJUBKmWdo/f3k7rlt3nsfds5dE8/sdgPx3ryrsvNPXv2MD8//wM8lB8e2KbN1HOzvPH8LBtVA1eEsCiy/1A3hz62i3w9z+nTp/nIRz5Cs9lkdXWVfKWO4BdoNC0022kXa5sgQNDrp6HoWFIDo1qEQAdTa2WqV/N0hrTNzxNbYaSOYxMIBHAdl0q1glSvc+XVGXbKgyx87Ccwjn4Sz+GPoby1jifiIdARQPbIJBIJJiYm2ovhAw88wHPPPcf73vc+ZFmm0Wjwta99jbGxMSYmJlqTpVKJ+++/f5Ou5o033mjz0F9++WVUVeWP//iPKdR0IpKPlZpEsVag2WgwNjZGPB5H84fJ6S6HO4J8eG8nl86fIZVKkU6nOX/6Tb7w8ffh8XjoiPgpNQy8moriCLirWUpaEUGTEFQPlVyDZqaOlfCSNU0CFZdjIynOz20w1h1H8YRpnF3HXqmgCAIqII/E8B3pwvQpmIU6+/qjnJREHNvlbuZErt2idpWnczxzbp20CImhKJ7rm5mIhwuFNYwOFd32kL2ax7nfhC2oCM1ikzeenWGxqiP6FN48Mc/ogS66xm7PAJJUiQvrF3i18CqPHH+Ew5/fh6RKeOPeln7rJpTLZQYGBuhJ9LB60iRniEQ8MgVFoFk3Wzdn6cbNuTBf4OXvXGXdddBtk+Wns/z09ofaYu1EIkGtXucb3zpNzPEQHI7dVZaj6JEJh71kJ7JMrZY5eC0sWhRFfu7nfo7x8XHi4U6+9JvnCfeoBK9NQNabFtNvbbQLvR1jKUZ/9RFsx/mh4LT/t0Jxrkhmtcy00zLdidxirONVJboVHyuFOsGwSkx0uNS8hK/bx+DhQSRVAq1VPN3z8C42Jk+RnSsQTPmpl3XMusW+ewcYO7SNQqFAsVhkZ2ongiXgOi6iJKL4FURJ5Ft/cZnT355C0iQa9QbpyQJHP7QT/0E/8/Pz7eDh/kIB+6d/GvHrX4dbqFDQ2njZto0kSQiCQCqVYmVuhYAUwDZtbMdmfmUej8/Dc7/3HPnJPCEtRLQzSmwohuJTyOfzKIqCrMi4totZNJGaEmd/9yyl9RKCT8CNugwcCRJrhggFIyQSPuJ+jcJMgdxkDjWh4pQdGmqDnp6eliHULUVpKBTiI588hNcnMX0hR71o0TkaY+89vRy7p59Gs87s7CwDAwNIkkQmk2FwaJCP/2yA5745wepEFtOy6RyL8d6P7yWZ3DzZFMUWLVmvbt7E3AzXcXFxUK+tF47tcOW5WdamMtTqBq4mIkW82IZNfaWIb01ERcE3FAXTBknAsFxwXFxcQn2h2/TGt55ThbqBLoBmOti6TaQrwvZt20mlU0RHo4iSyEy6ylw1/47nMLR0geBitvj0iDetFQLgehS8ySDWch7V48HvVen2SORqOmXbxskaqH0hKoU69eUK5kyeqE8j3utHlESMjSpNF06/uUxoW5z7Hx7a8jjqNQdRUrCtBrjqpmIv1OknH1CwqgZKyEPTcTEa5pa/Rz3f4PD7txH6b2AQ8cMK0zRZX1/fZFSSyWQolVoNk1gMFjwSdrGJfMvk2nVcrLpDal8Pol5mcbHW1notFxssXUoTkqVNRhwBn78dQK5pGjWjwOrlVc6OdzEQFFn/5V/G+9ZbBE6fRtQ2T6evU/+vIxaLkcvlaDQabcOQ5557rm2Ssr6+zvj4ODt27KC/v7+tyZJlmcuXL7cb1r29vfh8vrZbaLC6hDdbR68U2L7/MMnuXgbjPo4Nxum7tml3HKd9LGfOnGHnzp2bzGDCXoVP3beDP9YNVlcaeNJV/JIHYzCM0+3n3rEOBgO0Y2hkEfZ3+TmdcSgOBomnfDjFJqIqYyY8LBoWY10hhpO3syiCQQ3bdXEtB+Fao9nRbVxJwH+XjW1JkpidneXQoUN39fjXXpnnje9e5cGP7HjbQmxycpIvfelLeL1emvVqO6jc7/dv0lNehyiK9PX1YehQTpfx+FR4m0SD028scualOYb2+3n4kb2bpqwAU1NTxGKx9mBjcaPCX/zBOQqTOXyKhCAKTF5KM3N6hcc+s5d7D9x5kjg2NsYbb7xBIBBgeHiY02/MoS+UMes2pUKdz/0vmzWRoij+0BR58Fco9P75P//nfO5zn+PRRx/liSee+EEe0187CrMFXn9+ljnHIdQdQJZE1itN9DeXaTgl3lh/jXq9zuDgIH6/n+7uboaHvRTcVV6ZOUcg00Dp3Jrnb2zUsKIePvje/Uiiw5UrV1hYlVDqFmLX7R2bQCDQojWEWzfzgORFrJXJPzfP1Je/SbeyHeXoR+H15bYmxxf3kRhLkNqd2mQM4rou0WiUv/iLv2Dfvn28+OKLjI2N8eCDD2KaJmfOnMGyrPYFCa2LZWJigmAwyB/90R+RTCbbi6ZXgr3BBqloium0iuiP4e0YogaIyBwZCPDojhSibeA4DvPz85w/f573vve9N6zao17uGY7z4lSG6P4UstvAKUF+tUaplsNQRMTeMMXeIA4ummVzZnqVfMPm7EqVY0MxAo8PYm3UcA0HwSejdPgxgPlsjX29YQ72x3irL0RtMk8k/s60JKPYRPDKZKcLZByHzp5Ie+ICgCi09JGyS3A4zsR4hu3n1xl77PbNiOu42HYrD0hUJBx7ax0QwMTkJN/81rcQJIGyXSbYE7xjsbV3794WDdeFt8RJqk9PU10oktJkth/suq0w1OsmlYaBE3CIhDzYTaiWb5wb5XKZF09dQKx48HssRO3uqZNK3IM0V+T8+VX2D0Tb+kefz8fhw4c5/fpbmKaJ6NwoalzH5VaauySLSHdyS/gxgG3apK+kKbouddOi5w6ZiYIAsYDGar3J/InXkAZNIjsjDO8a3nQ+7Nnnp/Sp3bz57CyljSqqV+HwB7dz/6Ot8zAajRKJRFhZWUEQhE1hs8VigysnF/FEPUS7gi2Xuo0q468v8cDjIwwODpJOp8mdO0fXL/wC4n/8j7hHj2+p+4tGoxQKhVazIFNj6dwSl1+6gtEQQRCpVisM93aj2zqNSgNP1EP/nv5N56jrujSaDcJqmJpeQ1EVVi+sUluroXk0Gg0o2TAv1/F3xMnisJCrEajqDCX9dPo1rHSNV//fV1lT1jj8wcO85z3v2fL7bTQaPPLEGB/6WIBSuUwhn8fv9yPJLZMBn8/H4uIiHo+nvU51dYf4qb99jHyuhu1ArZZhcDC+5evvONTFia9cxrFdROn267eSa2UkjmxvPT8zmeWlb09Ri3nwb4vRWCq3KJl+jSoWdVsiM5WjJ+IB91ohZdu4hSZqd5BIx11oym4imlx3N1Q9ait/8drfWhOGu8xucVxwwXZcbh2lOa5LpVIm1h9DdMFNN8F28asylmnR55WQBIVQTwTbdRm/mEGKeAkl/a31MttAjXkJ7oiT36jy5jMzHDjSg/8OU0tBEAj4A5TKpTYNFED1KAT7wxSvZDGVVoF3KzXTdVwyswWCST8Hjv94h6ZfPL/K0nyRwdEY/YMBvvzlLyNJEvfdd187zimZTLZzVpM9BvNTDXJn1kkIAmJYQxAFXNOmsVql6pO573A3u7cN0Gw2mZ6eJh6Ps5azsNareKKbi2ZRlnDqdusUE8DXESFSzvHqq1fozbxK8E/+BOn111GiW09Xbm4oWZZFMBjkxRdfZMeOHVy4cIFischnPvMZvF4vMzMzhEKh9jT/Oubn5+np6Wm7TY6Pj5PNZonFYgwPD3P48GG6XnuN+tI4nzj+ONtHh4j4Np933d3drK2tsba2xujo6Kb90nXs6Azygd0xqvsjTG5UKZZKBCWH+3Z2cmi4A1kSqdfrTE9PI4oijx0YIbFa4dXpLAuKA0kPpmkhNxrs7Uvw3t2dt9EuAfYc6OL8y/NsTOWIdgdwHSisVgiPxhjb03Hb4wFM0+bVl+e4+OYKtXKNoT1Jxqev8pGPfOSO587NmJteQG8YvHX+MgeP9NxRr/baa69RrVbRNI1MJrPl93QzPvvZzxIOtzwO8rkagS2o6DfjwqkFZk4vM7Dj0KYiz3VdLl++TH9/f/s9Hcflme9OUR7P0DUaR7pGg3Udl8JMgRNPjbNtOPa2TqDHjx+nq6sV6zE6fJyv/c4pbN1B/hGIl3vXhd5v/uZvEovFePLJJxkaGmJoaOg2K1ZBEPj617/+Vz7I/95YncySrhmEuwIEr/Oooz7SDYsTz5wnfqylQQmFQu1RMcDB0TgTR7rJvrZMfMVBSfoQrms6mhZmukbOcem9b4hdPWGCHqUl+LzwOs16A8QtLgQBJEnEMkzcnI6xWMLaqOEt5vEodZy/9Xk8cS8+/7Vuk+VQz9WZeXaG2VOzLAlLbXMEn8/H0aNHsSyL06dPc+jQIY4fb7VMNE1jz549/Nf/+l+57777WF5eZnp6mj/+4z9G13X6+voYHBxEFMX2pnFoaKjNW57bKFCyZAqlDAf27ma4I0Qq2LpovvOdZ9uarV/6pV9qT5GgdY48MNqabr0+m+NCdwAr5KKkPHhUGdcrkxVMfAocHIrRE/NTbTS5OJ9mtdzkuYkNxjrDpLqDyJKAbjmsFOtYDuztCfPk7k6CHoUdB7s4M54lVG9p/e4E13aoFJqEBkJUVqp4Ov2bizxaPH9VVTB0g2Q4wErBZPz0Ctvu6b0t+kDwCpjyOr6ahmLYVO1lrq6NEx7yEwgEqOkWV9NVLq+WyFZAO/BBOiLjBDtD6Lp+R0F02+1KgD3vHSWc9FPI1Ej2hOjed/sCLwdEVEVHLErodYPOsIeOvtb5lk6nWVhYoH90jHPfPInnLnSMmz6jIuEVBXIbNRqmfZvI+vDxfZx9ZYOF83nMpoVlOsiuy66jPXd4xR9PWA0Lo2xQwG3dtO8whHFNG61mUiwajO04SOc2L+p+dZMBwXXc/8gwB472ksvUCIY9RKO3r8G9vb3ous7c3ByxWOwa7crA0C381zr1mqaBXMVsWlTKTaJRL43lNXr/4Re5+PjPU1yIEPmD88iqjDfuJbEjQbg/jOVaFAoFlpaWWL+yzvLJVTIZk4IaYF1oYLkufjlM8eQKQculayCKJ6KwvrG+SYuRL+QJBUPML8wTCURozDUQKgLh/gRzhQb5egNzo07Yr+CL2IgeG9XjpapbnJ7P41cVtnUEyJbzNDNN5hPzNB5obGkPXq/X24yFcChEOBSiVqsxPz+Pz+cjlUoxODjIuXPnbtsoxq4ZdTQauTv+zvsO93Dp5CLp6Ryp0fimYq9Z1SmvV9j76DDdvWFcx2X89Cq5hknHcBQSAZZsl+ZqBSmgIYqg+GSaBZ3aWgXNo9CoGJiGCREPPfs6btP83grdtFFkEcVtZedJqkQ6naZ7qJvSqRJm3UQLaQQ0GVmSaJr2DfbCnXCtmJLFzcWhZVnouo7ZrCDKCXz9MXSjgNMwaRg6kk8hGQqjyiKDvWGmr2ahbuJPBbBLOq5hI8W8aKNRRJ9CpCdEZibP1JU0B4/eXoglOgJ4NIl6zSQcDFGqlAkFgwjXmpGp0QRW06Y0nUdqmG3XTcd2qebrVDaq+OM+nvjMnh9r86fFhQLf+cPzVNJ13kp5KUinUVSDQCDA4ODglu6JUb/KBz86xjdsl7XxLN50FVEUMRwXJ6qx+/FhHj7U+k08Hg+jo6Nks1kWF5Zb2bBbsJr8gQDVWpVA4Fq0kSAgLq4y8vu/zdIf/AFBj4db21/XmUa1Wo1z584Ri8WQZZl4PM6DDz7IK6+8gmVZfPazn21v+EdGRnjllVcoFottB2DLsjh79uy1eKQmIyMjDA8Pc+zYsfZ7ra2t0Wg02DYyhAcTydaBzYWex+PhzJkz3H///W8bBp/0K9wz0skjOxwcFxRJYH19nbnZGTo7OwkGgwwPD/Paa68RDoc5PBBnW0eQmXSVqm7hOjaZ+Uk+fujAHc3juuJ+PvD5/Xzry+copnUQBFKHunjPh3cSC269d3jmO1Oc+sYksiIiyiJnnpqiGYogSm9fDszOzvLmm29y5eol/tdf/v/yR//1d/jN32xFdd3sZGnaDoWawQc++VlcUebY4YPtAu7tcPNjYu9giDQ/P0/PqEBV93DswRsZxrZtc+nSJXbs2LFp77ScrbJ2MU046W8XedDyooj0hVhbKDI+keHBY328Hfr7+1FVlcXFRZ78qf1UyjoHj/3wN4jedaF34cIFBEGgv78f27aZnp6+7TF3Q/36YYTRsLBFAc9NC5UoCLiiwOEDR3jvzx1F8t5+I0wEND72gR08JYukz66hLhbRhFbKdQMXO+qh73gfH3lshKCntWETRZHBwW6y3gzFdBZ/MoLm2dzJ8Pn8FCZWUTM2giZhFdYQ7QbBv/t36OqOUi6Vmb46jaIq7Y2gEBMQyyKeooegGaRzrLWxcV0XXddZXl7m05/+dPs9LMsim82yvLzMr//6r5NIJFhbW6NYLBKNRhkcHGRwcJDh4WEGBgbaF9Hc3BxDQ0OcvXyVbYfupVRtcOHiJeK+/ST8GqIocObMGdbW1hAE4TZeNrQEsQ9tS5Ir17m0ICHFVRRZxAY8isQRn0pCFdGrFTJWjXg8zr27+pldSTOda7JRabbMLARwLJNaeonPve8BtncE2xuWQ/u6mDi1Sm4yS3woiriFsNm1HSrzJayEl654gKvLldvMdQCqlSqSJKF5POA6qDEvG+tlGvnGpkLvjTfe4PTp06SdNL/wK7+AJCqcmTjFSydf4tzlc3z40z/FK/NVFvI1NFnCr8p0dPehhlM4fi9vLlZ4YFS74yJ/HbJHZujePrYmN7UoIsV6kc/84kP89r/8Y+458hC7j/eS2pFoi4uPHj3K6ZlsS2P6Du+3FQQRLLMVxnrb3wSBn/qFR/jKH7zK+myFWCrIvvv7OfbgnY74xxOu09K82K5w29roui5O1cTKNbDTNYyagVkzyMgSWj3Kttgw+VCeyGCk7fR7HX6/esdpx3VomsbQ0BC5XI65uTk6OrqIdATJLhTx+Fuvpxcswl0+Uh0BshdX2fjk/47e/16UI08SCSoUCgVq1RrqjMr0G9N4414693ey7f5tlBZKFC5WWSoJrHgVVEnC5woodQdprUZThI2IilGq07/opdFsEPAHcFyHZrNJNpPF5/WxY8cOMlcyNDINpLiXqUyNUsNEkxyiQ3Hqq0Xk1QaCpmBQJxYI4PpUKk2TC8tF5GSK/cND9CV7sYpWK03iFlyfXmz+Dlt0onq9zvz8PJIk0dHRQSKRYGZmhu7u7k1Fo8/no16vb2paXUdnV5APfO4A3/mTt1i+tI4noCEqEmbNQBAFtt3bx/s/OQa0NJurs3mcgIIiiyCL9B3qZsOfobZShZKFKdk4pkt+IoeS9KOYNj07EpTiXthiHbsVxYZFR0hDq5toCR++hI9SuoQ37EULaTRLTbSQRsyv0BHSWCs137HQE30KKCKa6yKJAqZhY1YaWE0L1zSQJYdatYZP8aD0BLESMkbWIIVIfbmM1Bukul6lvFjGLRm4vlbeljwUQY572w1SWZVamsT6Zsql67pYTYtkR4DewSiT0zmCIQ/hUJhyuUzA70eUJATRpXtPimbdQG3aGHWL1fNrCJKIJ+pl7KFBjj40yPDI1tPZHxfUayZ63UQLqTi6zc/9vZ/H49Pb2WFbFXoA27tC/MzPHOLi1SzjlzewTIfupI/9+7vY2R2+LWsxkUgwMipy2p2jlisTCm429hBFsUV7dFwc06GczTA6+zSNP/gDdnz0ozSbTa5cuYJpmm2zD0EQCIfDHDhwgI2NjU3635mZGa5evcqnP/3p26h7u3fv5tKlS/T397O8vMwbb7yB67rs2LGDsbGx2z5rPp9HlmWy2Sx9fX3s27ePq1evout6m/4Hrb3vrl277vq7l2/aR143iFlfX2d9fR3Lsjh27Fg7EmZwcJCD/TcaDi+sjtMegd4BA91ePvs39mDhRxCgtyO46T1vRqWmc/nkEp6ASvRaxEm5UUCqeDl7ZpZjx0a3fB604i8mJibYs3c33T1hDh06yBtvvEEikUAURSzb4a3lEm8tF8hUdEzLpux0stPTyWj4B9NE0XWdhYUF/H4/Fy6eZHh4uE2R1HW9bdJz6/lcrRhYDQvPFsHnokdGsByqpeZdHUNnZyeKonD16lUefuLYj0QM1Lsu9H5c9XkAncNRQicWyNWaaLIPQRDI1w28TZvuwTCqX72jJqIv6uOnPzrG1NEezp5fp1Jo2df2JP0c3N/Fjq5W6PnNGB2Ica4zgC+nY9s2hXyBcDiEeG0kbKVrCKsNnIAHuZTGyZaQntiPadVYXWkgCAIdHR3ouo5lWySTydY4vRsaFxtc+fYVAvEAgY4Ar7/+Oq7r8rM/+7O8+OKLjIyMMDc3x9LSEpZlUa1WicVirK6usrS0hCzL/OIv/iL79u27zZJ2dXUVxR/m66cXOTlhM3HlLYSGiSOJ/P8unmDvPSPs7/Fw7tw5Dh06xJ49e1hYWNgyPHUxX+fiUp6x7hA9iQim7eIUGrjLFezxAo7ZCgyXkj6y4WXo8DLQGSfsr3N1vcDBXR3s64ugyiLPfOMK2+Laps1Kb8zH+z41xre/dImN6QIhn4w36UdQRFzbxSg0qRSbWAkv93xsF4HFMpPC1q5MlWoFgFRHilK5jCh5sa3bjXjGxsZ49dVX6R/sp3NHq9Ae08ZadvZje3lprspyoc5wMnCtMw6g0RMLkK8ZvDiVxquKHL0DReyd4Lou4+PjRCIRdu3axVe/+lUYKHL8M6NEo1GmpqYA2LevZWygXOOtcwdDobd9L6dlMiHf4brwBVR+5n99lOWlZSzb2jQJ/x8FkiYhazJBEdLGjXxR13YwFssYqxUaNZOaCDXHxQSEqkE5WyXzyhyx15foGYoy/NgQqb2pd9VIi8fjxGIxlpeX2XN/glfzFdYnMrguhDuD7H0gTv7iBlNf+D8Q/XH47AewVRsbG1/CR7I/SblcRpZkFEOheK7IirTCxlsbzC0bLEnQEfCgN+p4fEGq02u4pksoFcByXHJ1A3OjSLxqo8U1tECIlYJOxg0gWz5WL6xhjeeIRL3M5RqUGyZewcLv92MYJlpHEKdgIK3W0baF2259Ia+CIokUSVENiOhVnZU3Vtj+4e230Zi3QrNpMT+TxzAsIrEw9XoaVVXJ5/OMjIywurpKqVRqTwITicQ197atNSo7d6cQf2YnM5NFlqdKGLpJJJVi96FuxvZ2tHWoru1ims4mJ0hFk+jd10mpp0w9b1DZKKNXbXx+lUOf2U0w18Qri1ysGyzka3SFvXdsBtUNG1yX/oiXwkQOK6Kx+junEbwGoSdDRIejzL80T7C7RRMfiPlZKTSwHfdtHenEoIoU8eBm64gVg/pGFcVyEV0XHyAKEvnpIm40gJTSkHsiqB0uo2EfLJbpOtSFJ+whVNcR5vKouxKoMS/cwp6wDBtEAe+1BprVtCguFMlOZKln6riOS7xuEcg2WNc3SOxKEgqHqJTL5PMFRElCNn0kNIU9+7vRTRu9aeELqIzc08vI0V68Wzin/rhhdHuCPQ8NMndpg22HuhgZTSLJ4h3P35uRDGo8dqiHxw714DjuOzYeh1MBkvt6sE6vUCwU8Pn9m/YOAX+Aaq2GuFpGmBsn+fEHKe3dS2l2FqAdoF4qlUgmk+2Cz3VdJicn26+zvLzMyZMn+cIXvsDS0hKpVOqap0HLBXN2dpZ8Pk8ul2Pv3r2Ew2GOHz++ZZFXr9ep1+ttqczBgwcB2LZtG4uLi6ysrNDT08OlS5fo6Oigo6OD2dnZOwbBb4Vy0+TqRpWNchMB6IokKS1OIMsLpFIp+vr6mJ2dpaOjo6356+zsZH19/W2dKDc2NtqZp++EWtXAbFj4rptFOS5Nu4Ffi7K8tPG2hd7IyAjhcLit4dy2bRt9fX0tvWW9wYm5Eq/P5vCqEvGAhigIeLV+nr6yzmqpwYf2db8zU+BtkMvlqFQqbN++nW984xs0m01yuRaz4nqhvHfv1vm7voCK7JHRq0Z7LbkOR7dxZRH/O1BFb0Y8HkdRFE6ePMnx48fv2Cj5YcEP99H9NSG5I8G+/Z2cObvKRs3CFQW8TZvt3SGGj/W+rfAdIORRODKc4OBgnKZpIwgt6+k7LZDDCT+J7QmKLy0Qx4sv4qVYLiFJEl4UqtMZbMHFyKzgHZ+jPraH3QcG6O1JkC7rVA2LakMg4guS9CnkslkEUSCRSNC1vYvJ1yZ56j88xYw4Q29/L7v69/KNPzzBxORlQh1v0DV4w3glFArR2dnZKm5X8jz5yJP0iD3kr+RRgyrhvjCSKpHP52na8PJEjgvfuES8oBOPKK1spKZFpC5w6csXWT7axS/9H/+c+w/sBODEiRNb6nwur5apNAz29ieQJRF3vUL99Cp2xUSKqIghDddysJYrqCug4qFAHleA7niEc9NLHOnxkgpGGRpshaju3Nl6z2LdYHa+gGnYHHxkiItRqK1ZFFfLCNeMV4SQRurBPg7f08eRbUkulKeRHRfdcjaFBzt2yyFQFEWSiSQrqytImoLXJ7eMMm5CLpfjF3/xFzc1Rfr6+vipn/oprhZsvndymiPbe5FEAbukY+XqCIqE0h0g5ldpmjan5gvs7Yl83wtkvV5nYmKCnTt34vP5uDRxlbnVDJIkc/XqVTRNIxqNbrrRxwIaUtJHc62GP3r3pgSuadOwHbpTgc3ue1ugt6+XdDrNzMwMc3Nz7Ny5c5Nz2o8zFK9CbCRGdLmELIropo0qChizReqLJXKmjdG0kBoWimHjt128LggembWKwZoAy5NZFl9fYujBAYYfHyYyGEG+i6nOzRAEgb6+vtY1+NFOrKYfSZaJJUT05QIX/s6/wF/JEPgXv4YvcjvlxuP1oOs6pWIJ2Scz/d1p1tZyrHdHiPpUDL2BqqrYy1XkBjR94Oo6OC4aLnWPir9ukClrzGzkyRarhIIB9FwdZyaPs1HD1i3qhkXUI+C7NklzbBtVVXCjHux8AynjIdDjp1RqabO8qoRuyWw0XbqiFsX5IpWVCuH+O9OGHMfh5CsLnHlxjuJKBdd2UHwK0T4vH/rMYSIRD/Pz82iaRiAQYHZ2lv7+/ru6sauazfs/svdtHyN7ZfwBFWe9fFvjXlJFurYlSAxGWJ7JcmB3D+/71B4yVzLMPDPD7o4AdcNitdgg6lfxq3L7+Y7jUmyY6KaNV89jT5hMXsxQ6Q5g+hWUpklu5SwPvG8UNaCil3W0kEZX2EMq6GGj3KQr7L2jA6cgCCgpP/krGwj5JoooomsiiiLiUxVwXOyaTnG+gLenD8l02dUVImK4hO7vZ9cndiHKIp3FBpMbNapNi9gWesbiWoVQR4Dtu5JU1irMvzhPeblMxXaoKSKOAEpQpa8zyNJEluxKBYYjlM0auXQObx0GfFFCMS8zpQY1WcQVwVPVqT0/R3m2yI4nhlth6j/GkGWRj39u/5bT7O8H71TkAUR8KrsOdvHGZJZ4RcBSTJqNJoFgANMw0Q2d6sIG9qkrsCfMPf+fv0kycrtsJR6Pk8lkyGQy9PT0oGla2wQlk8nw4osv8uSTTxIOh1FVlRMnTiAIArVajc7OTu655x4WFhZ46KGH+O3f/m36+/vZsWPHbe9jWRZra2uMjIzw1ltvAWy6LyaTSf7tv/237N69m+PHj7c1jB6Ph2azeUd5xc24vFri+Yk0uaqOdK3xpE9nSAYjdIc60fUG6XSaZDJJpVKhXC7T3d3N4OAg58+fbxd6DcPm6nyeSqmJrEikOrQt6el3QjzmI9zpJzedxxvyUG/WsasCgT4/Hd3BdpTOrSgWi6iq2o7nAjY1a587fYXX12w6wz6CHqX1O7nQFfYS9alcWikxEPdzdPD7Dx+H2101P/zhDzM2NobrumSzWbLZbDs7dSv0JQN07kmy8vIinrAH4ZoHgeu4FBeL+LoC7Bq7O8fR6wiFQhw8eJCTJ09y7Nix2ybKP0z4Kxd6L730Et/61rdYWFgAWuGVH/zgB3n44Yf/ygf31wUtpHHk47tIdAeZu5LGumbDOnq8l+gdAle3giQKdxUK6VEk7r+3n6cWi2QnMighCSHQCiIuzq7hr4EvKGGdXSA/OEri8ADBoJcXJtLk6wa4Li4CsiiSCmkc6I3iVwQ21jeQZInxtXGKJ4tMMsno2E4ulevUqyC4CUprGRIJk537dzI0NIRhGKxMrXDyaycZ1AcxLhnMNedamxARAqkAwZEgYofIRFlm9sUFfMsFkvsGkW8SLcu2w7ZVmbnTG5wIqhzd03IZ7O3tZXl5eVMgc6FmMLleIaCArCjYJZ36mTUc00Ed2HwDkAIqdkVHv5wlFO5F6Q2ykcmxXLe5vJihWaswODjIhQsX6B8a5dkXZ5l8c4XaagXBcXEVETfqcuDxXcRDXgzdRlYkOruCDHcE21SUge0JYkGNTLmJN3GDL16t1sB1CQSDLat6VUPPVhk4PoDvJmvuTCZDOBzG4/G0C05obZBisTgTk/MkYyEKuSxRJUjt1WWsQgNBFNDGEvgOdJIKasxla8xla+zquvu4gdXVVYrFIgcOHMBy4Dsvz3HpzTXq5UHivXuZXinwngcOkUxudv7siXjp3Z1iaX4an2G36VPvBDOvY4VUDh7svquNQCqV4syZM7z66qs0m02SoSS5uQL1qkEg7CE2FEF9Byrijyqio1ESb63TU2kwV9UJ55qYV/MUagZOzcTnulgCIAl4JQkt6kUdCIMAhmWTr+i4dQP72VnKS2WSu5LEd8ZJ7U7dMfrlVti2TSaTYXFxkeHhPvL5PJbVYLh/B+P/4iuEFqdR/8U/bhd5juOSqxk0DBtNkUj4VTRNI9WRolatsZxdJrdYpRLQ8KogyTKi7lJeLCCoEpIoYts2fp8PD61ct/mijjaZo5FwGO6MYFs2YVWi0bRxE34W6zqVhoEgKHg9oN60NxUkAdGnYK1VkJO+tjYrGAgQ8iisl+tkdBefoFCYK7xtoffK87O8/JXLiJJIbCCEosrUS03Wx0t89f89w6f/zlEGBwdpNpusr6/j8XhYXl4mEomgqiqGYWwZvntzcPzbQfWrjB7oZOov81SaZlsTfjMquo1Ht9hxpBtJkUiOJSmvlDn7nbNsbEwzcs8jrFcNivX6NbORViMt7FXY3R1i6nsXODlpUO6NMzzSh09TqDRNZlYqdFxcp38oQuZSBjXYoswfHojy2myO9VKDjpCXO9UFBatJTbdRHMAv4+AiiyLmdbMpx8WRJcqZBvcf76PXAjWg0HdfX8sEBohEvBx4dJDX/nKc3FKJSGcQSRGxTZvCWhXbsDny+AhUTWa+N0NmrcKc6JJpmhhV+9qnBV+nj1RHP9JCmVrDIhwLkJBd6gvruGGBdH8IWZEIaHLL4t2wmdAtsrM5zG/b7P+YRGQg8o6/1486/nvRzB4+0M3aapHJp6cITJtoQYWNjSKaoqFaEubFGex+lf/l//n7WxZ515FMJkkmkywvt0LSXdcln8/z9NNP8+CDD9JoNPjGU8+Sz9Xo7vWxbdu2djHgOA5vvfUW09PTRCKRlnO5eztbZW5ujtHR1iRrYaE1Xbu5eLp8+TL1ep1Tp07R3d3dpl92dXXdFiVzHTcXTLOZKt+5tI5tO/TrLvZ6CQQBqdPPuu7wzYtrfOpwL9u2pchkMlSrVXw+H9PT0wwMDNBoNHAcl9fOrXD6xTlKc0Vcw8YVBCyfy74HR3nyyQSRu7hnKorEQx/awXf/61usjKeplitoPg+H3zPKkUOt4PlbQ+YNw6BQKLQjCrZCRQpTKs0zkghgFZo0zq6B5eDZ34GnM4BPk7mwXORgX+SOtNKtcJ2qOTAwcFt2oaqqyLJMuVzetMfaCpIo8PiT2/nLjSorUzn813JlG3UTKeHjkY/sIvkuHHd9Ph/Hjh3j1KlThMNhJiYm+Imf+Inv+3X+W+NdF3qGYfDZz36Wr33ta7iu2xalFotFfuM3foOPf/zj/Omf/umW3YEfBfjiPsY+sJ3RhwZxHRc1cIOuqZd1bNNGC2nvKIR/O1QqFbLZLJZl4XNddh0Nc05voKVNAjkXn6aiVRT0WpXK5VmaIyPIuzoY2pZgfK2M5bgkA562WYhu2awWG+iGzX2jCVKpFH/6Z3/K6VOn0eoakb4IhXXwuAKebhXN4yeaDxNSPTz22GMIgsDCxQWe+4/PEZACHH7PYUzZxFRMOjs7sU2b6kaVua/NMXD/Dq7kawSydcoxZVORByBIIkpPkP4Fh6WZEt87eY4PPXiYgYEBXn311U2F3my2SrFhEFZb2iVjtYxd0lEGtr4BSEENp2pizBZRekN0diSpCRWuZhukVBPbtllaWePPv3yB2ZcWCagSHV1BREUkt57DWXW58LVJ7v3kGO95YGhLCly4N8zIzgTZ0ytUAxqBa1OT6jXaZuumAaatIjVy7DrW26aI1et1dF2/rZC6Dt1yKDVMogEvPlEj+9YKSr6BMhDCKeuYCyWcnYn2pKbStLZ8nVtxM1XzOj3lxKlFXv+LK/h1m6AqMj8+jX7fIN4P3N6wEEWBgwe6mD+5THOtiqc/9I70QEe3KRfqRI51s707hGM7VFYrNItNJFUi1Bu6rWizbZtGo9EKt/36SxiXQ8wvlDAsB68isn0sxQOfGCO4hQvtjzqC3UG6D3ehn1jEtg1WLm5QzTawHJB8ErokIUkCARu8Xhkl5WtPaVRZIhL2UqgbeBFJOi6WbrHw0gL5q3l67+klOtLKT1xeKlEt64iSQCgCpnlDfyBJEtVqlePHjyNJEn19fTiOw8Q/+S3q334B9x/9rba9eblhcm6xSLqiYzsOoiCQCGjs74sQD6jIikzDaGDqNrWlDDVfHK/HS3OlguKKKJFWk8Q0TUzTxBUkyk0TEfBVy/TvaW0edFenWWg55xqqhHUtINiwWtlrSb+8yd1N8CnYmTp2oYnYHSAcDlMpl/F4vSiSRM4Q6VB1qqvVLRkE0HIePfXsDLImE7+pGPRHvXiCHtbG07zx8jwf+8l9eDweBgcH0XWd9fV1lpeX8fv9pNPpLSfS5XL5runJo4e6GT6zxuRKCaszQNinYNsWoiiSLetY6xVGt8VJDLWuB0mReKv4FjP1GWL4GdNUdiQC5Awbw3IQBPCrEjEXzKJOMZxiObCGOBjGd00bHvQqlAMKmZUqhx4fpbZRo7pWJdgdJOxTuGcoxumFAqvFOn5NJuRV2lROx7apVKtYJRtdkbCjIp6KSUSREGQwHBtLt5BFEanbj10zEGcKePZ2MvTYEKHezev6o+9tbSzPvzhPeiqHi4vjOIS7ghz58A7ufbCfqaemyK5VuOLYFGomcb+Kplzb+LlQMyzm6gaDY3F2mTD0QD+48PI3J5hWIBnybNKTaYpE2KuwVmpwZalA4tQq4b7wO7J1/ie2hmmaZDIZms0b68wjB6OYZop6QaNyNY9gaFRdB2f6TXo6SkT/9ocYuIk2O7VY4MrFDRoNk4GhKAf2duJTZVzX5ezZs+zfv59MJsNTTz3F3r17mZiYIBHvZPqUQ7Mks3f3Lur1MqZpIssyothytnzuuef4whe+wMbGBvPz8wwNDbUn8vPz8wwMDCAIAs1mk42NDY4cOUJNtxhfKiKKAk8/+zyaprFv3z46Ojq4dOkSY2NjiKLYporeWkDXajV8Pl/r2BcL1A2LnnSTxsX0jan9bIHOgx0sRjQuLBXpi/rahW02m6VWq3HlyhVkWeZrT1/k4veWUGsmic4gsl/B1A2KyyUuf2uKYqbOT/70AcK+dy729u7pJP4P/Exd3uDVE6+TK89Ttjs5txLnykyBFSdNIqAxkgzgVaUti7+b4bouK8UGfZ0JMtksgSUbc7kCsghXsiidAcJehULdpGbYhL13V+jdTNW8GZMzOc6dmsUbdDlyoGfLQnsrDHWH+Mm/dZTTby4zcyWNYzkkYyKPPD7G2PC7n+hrmkZvby//5t/8G7q7uzdFfvyw4F0Xel/84hf5y7/8S37lV36FX/7lX26Ps9PpNL/xG7/Bv/7X/5p/+k//Kf/sn/2zH9jB/nXg5i55I9/g7HMzzF5OY+g2qe4ghx4ZontPqp3zcqeNsWEYpNNpGo0GtuPguhAJh+jr62svOiMjNio6hphg9kqGymQOoWliF1bwHughsC3Btt4QC+kihi3QcUsHQpMlOkNeVkt1ZrM1ujWTxcVFmnqTUDiEkTOIdXsIKH76Rwdo6jaNSpaNjQ1+53d+h55YD8Ylg3KmzN4P7cXXGefK/BoN1+XC2iw7+zvBrTJ8cJiJN1aopqsoZg3fFgJXaDkaKQGVYNGmaKm8+eabHD16lEgkQqFQIHrNRrnStBAEAYHWd2gulBEDytsWGVLUg5mp45R1pIgHv0cB1cvY2BBLS0tMLzTQZy4z3N+BJ3Ft0ua4CD6JZF+MynyRU9++yo7tSQa2CCIVZZHj799OPlNnaqFALeknElCpVCq4LqB4WVktEapZxHb6iY5cn344rK6utjuEW34vQsvvxHFcNJ8Hb8BHpZ5Fbvhw6hZSQG1n4Lmuu5V52W24laoJYNkOl86v46mZBEbDZLNZBkY6yM+WmV4ucmAkcdvr7OgOs+ORQca/c5X4UgVPb/COmx+naVFeLGH1h3jwsWE8Lox/Z5pzJxcpVnQ0RWRkNM6RD+/ctLmTJIlPfOITNMtN/uzXn+H8dBZPT6g1aWiYnL2wjiKLPPqzh26jw/6oQxAEeo/3YtQM1v7jGbxrdRqagBxQkUQBxXFRbVD8KnJnAOEWPYEsCvhVmYLpUMrUCY2EGM+Ms8e7h6lvTVH2wFSmRHqxgtWwECSRaFeI/Q8Ocf+jQ/i8KuVyGVVVNxVOziuvYv27P0b6O79EVlHwVyv4gyHenMuTrjRJBlsbZdNySFeanJrPc7w/xJk3X6OwWEASk0hFB01WcS0HpezCTQW+oig06g1KpoVhOQSFFk9R82hUq62MpWoug2WZGI6I7TgEPB5UXGq6Rd6x6I7fuE4FAQSPhJWuonS2wsSDoRC1ahUZkYYlYqg2elXHrJlbTjsnL6epput0bL/9Ji/JAv6Ej+lzq9Q+vLNtdqNpGgMDAxiGwcLCAufOnSMajeL3+7l8cZ3xc2vUG3VGxpLcrQw10BHgic/uRf7KZebmCmxI0DANvLKCW65y4EA/j/7kPopWkRitKeF7PvAeXMHFzbkEugKUl8tEDfvGmumaCGGNzkOdBPcGyXxZJa14MA0TRVXABddykGURb9xL7/FeZr43QyPXwBv3EvWr3D+SYDFfYz5XJ11ubeAd28K2LFSPl+xaDslyCaoSmuoi1E0c3UK2HFTXRfAoiOkGumHRdFz6H+wnMhi5/buWRJ54/w6O3NvP1JUMjbqBokJnj8bwSB+lxRLlpTIrMuQKBj1R32ZKqQB+TUaVRBZLTWJhL+lLaZq6xRouAa92m2kItBpbiaBGztZZupqjb7VyWxH6P3E7HMehUCi0tVrQur4TicSmDe6VK1f4mx89TtMRmc9UqdctSv/6X9JbfIPKv/6/8MViLC4uMjo6yslzKzz/55ewNqoIgsikKjJxTx+f+cxevv2Nr/Haa69x6tSpdqzVgQMHSKVSBINRXn+6gF61+MY3n8IbqJLL5fj7f//vE4vFmJub45FHHsHj8SAIAqOjo1y9epXh4WHS6TTxeLw9kV9aWsJ1XXr6+vnqs1eZfm4WQRRIHbmfv/2pGzos0zS5dOkSO3fupKenh+Xl5du0jvV6nWg0Sr5mMJ+rkxBFjOk8YlBBCrf2bVa+gT6VJ3FfL1PpKg81zbZJXyKRIJFIkMvlmF7K8tyXTzMYSBLeHm9f482aQWp7B1ZFZ+X0KidHY7zv8TvvO25Gd2cQv9dhct5io9DDf3phgthbRXqjKSaWruJLhElFPfR7DB7fP/y2ezFBEJAlgaYFnckkq1NX8bayVxCvBbnbjttyWL3LPsr8/HzbEfZmFKo63/jDc6xfXiPYG+bQobef5N32ueN+PvL+HVjv3YbtuqyvLNPf/+7opDdjaWmJ3bt3c/78eZ76+vfo7thLMKSx72DXD4VZy7su9P7kT/6EL3zhC/z6r//6pv9PpVL8q3/1r9jY2OCP/uiPfuQLveswagbP/9lFzp9bxQyqiIrI0pUNVhaKfPgLBwn0e/jzP/9zPv/5z+PxeMjlchSLxfbzVVXFcrxcvlxi9nIGHJf+nQaHjnsZ7o8AMD4+znuO78Xn85E+2MPsm0ssPv0NIkNhoj/1CEajRs2Rmb46zUjv1hkpotjSCC7kavT0B4jH4sRjcUKBEAEngNnnIupeSlM5ZEli75E+MkKB119/nVHPKNqGhhyXiXQN8vyVdUp1A79PRDcErr45yb6RHjpVGTnhpfnGHAvVFUY7993xexM0CUm30Hwhhoe7ePXVVzl06BAXL15sRzvoVmtSgAvYLlibzQm2fF1FBNvBtVo0IUkQMO1WAd3d00s0uI2N2gK6YqI5LoIoUCqXWnmEgkCwL0x1KseFc6sMvO923j60pnrv/5kDhJ6aYHYyS3qlSLlgocgy5lKFRqOMd08fS/0+/u/vnOeJw9sJmgV2jr69m6RHkeiN+ri8ViYe0AiMJrHSNarLJQLRIN79HQiKRN2w0GSJ5B1skq9jdXWVQqHAgQMHNi0qjguWZeO4NtlsjlRHB27NgorZsr++w7F98JFhbMdl6tkZfFdzBMJelJgHlFbmltswaWYaVHQTYSDMYx/fxeHhOEtvrnDimWlWFAFPzINp2uQvtejD9//MfuRbaMylxTLFgoDWHSJx7TP6VIk122FmMsfhlfL3RZX+UYFe1qln6oCL3BvE2zDxXstIEjUJMeJpFfvK1jcIjypRMCw2ciWufv0KVswilUpRXIO519YgrBE52IU34sE2HcrZOie+fJnV2Tyf+tnDZLPZzV3QyUman/oczZ/9F9hDHezoSVEoFHjj4hQZ00dn2Nue5iiySEfIy1qpzrMnz6DUq7iKi+g4eFDwSx5c16FUqSGHNG5WLUiKSrlYwXUdNBfUoBfTMFq5kgJomgfdKlCvN5CvfR8CAqokUjctLNvdFHUiajJOw8LRLURv69zyBwIYpSoNs4kWD7G6lGPtG+NU6gaW6eAPaoRTIqlUJ41rTo7SHb5n1avQrOjU6+ZtrqaqqrJt2zZkWeatt95iY81i6kSVerGJoTdZuVTAo/k4fJfW24mRGB/6O0dZncwwfW6djfUsXT1J8sYiC7WzVBhqNxNt26ZYLPLpz36aZrOJpmpUVivUs3Vss1XsyV6ZUG8IT9hDPpsn/MIE5TWDFaVCJOij0bQJNCyG96TwhD1oIQ2jZrD4yiKO7eBP+fFpEju7QowkA6yXm8yuZHBEDUUNIhaaCHkdu2riiyuIcS9mVEWsGkhNBymsIcW8CGIr/NysGMy/NI9RM0jtTm3JhIlEvBy778aGeXFxEYD8dJ5qw2TdNIn61TvqBhVZRJFE1mwH/2KJXLlJI6rSod2ZVaTJEoYE+XKTWqb2P3yhl06n23FK11Gttoon224ZSImiSDQaZWhoazYMQKFQwOfzoWmtNWB/f5S1X/s1fE//OZ5Tp5jMZNi9ezdvvvkmhiPw6veWEXMNOncmESQRPddg8fVlfrs8zdmTXyOZTHL16lX+wT/4BwiCwO7du6lUKqyuLvKRn9qF7SgEgsf40pe+hG3bzMzMsLi4iM/naxsoQaso2bZtG6dOnaK/v39T0PnCwgKBQADJE2DhykWCZRPbdWkUJExHaG+WFUVhz549XLlyhcHBQUxzsyMs0NbuFUpNDMshbDoYDRO568b7SQEVK9dAsWzqQmsfdGvLOR6P4/UOIFcXEHoELNNCURUc227d6wWQQxq+dI0rZ1Z58IHBu5ILAUzNzrMsdZDWK3QU64Tm1ujslDEMg/CgSa3Lz+mYSiRe4f5R7W2LvZ0dQZ6fTNMV9tJ5bJgNZZ5oOIo20GqA56oGY92hdiF7JxiG0Z6ybqV5MwyLfKaAT1GRkNB1e4tXeWfIkohMq15Jp9PtQdWtsC2HC+dWsR2HfQe72yZat+LBBx/kwQcfZCOd5Xf/7QmubJxD9ig0PrePex8cfFfH+IPEuy701tbW2hv1rXD8+HH+7M/+7N2+/A8dNq7muXJhHaE7SPc1e3Mz7mN9rsDTX3mDZeE8tmvzwgsvsHPnTmKxGKOjo+2LY2ahwNf+4CylhSK+QOuiuTR7lZnz63zop/fT09nSvVyfxqSCGo1/888xkOj423+DtY0NOjs7qRUaRKJRmvUqpt4gHA63M4Ouw6tIZCt1Tp2d5pOf/CR+v5+N9AaemofdP7EbUZM5/doEsiJy72N7QT1EoVigcKJAwBsgEIlydqFAw3LoiweQZJlazSKQjDC5kifm97CSXiKXzoBt0ag37vi9uaaDLQp4NZlEIsGBAwc4depUmyrh8XjwKFIrHgFAEhAUEad2++K56XUNG0ER25th23XxSS3Dm2LNxM7ZoNoEg0HKlTIezYNl2TfiJ2QRVRJZXi693dsQ7g3z5M8dobBQ5Jmvn0Qet+jq60Tv7GT8fJHipSkG1zrJGnW+mXEZGoszMOgSeIdB1J6eMFfWylR1i4AmE314hEq6gCuCkgriui6rxQbdES+i65KpNIn7N0ctXKdqhsNhdu/efdt7qLJIZ5fGqWadASuOXTQorFfwb4sx0HNn3VLIo/DJJ7bxWmeQi2dXyU5lEeeLSI7b0gXIAkLKS+/efo4f62VPXxTHdpg5v0badUkm/a2cOBeWbYeFmRz716q3dfMbdQPdtvHesnh6NYVasYb9LhfxH2boZZ3ZZ2cpzBTwxLzoioAmCmiSCIKAIItv56QNtP4sSSJFyyCuRHETLuWywcqcgRjx4Jcl5HwTMeZF8ikk+sPoNYPZM6t8ze/wE58/euPFNjYw3/te7F/7Igg7QGggSiLxRBxho0q93MDQwHtTjIAogiSKDG3fzf6eIMtXlylcrjK5VKBWN/GJAqqkInk0GvUGsiKjKAo100a3HWRcKsU8aS2HcjnP0PWurQCu62DbYJoOqK1rVZFEGoZLzbAI36xhUyXcioHbsMB74xxSPR6q6TKnX1qgvl7Hni4iB1QQRcymDsCV13NEU35wHWzT3rLw0GsmqlfBH7jz5uS669zV87Pk1gv07e4AIUx+vsKFN5buutAD8EQ8DB/vY+BIN2ura/T29fLKKw2mXrrCN7/5TX7yJ3+S9fV16vV6u1C/bgQR6g1tWaT86Z/+KQsLCwiCwLEDDzE9naOwkSUe8bP3WB+7Hm5994Ig0HWoC1ERWXl9hdxUjvBAGFmTEVwHuVnivp3diIhkx7MU1mrUgxobNQPRr2AaBtRtZEVB6vQiJ30IooBtOdAwSOyMI4gCc8/OUV4uM/jwINpdutzVMjWaskCjZhN9B2paQJMpNS2aho2l2yBwR43hdUiCgGU7uPb37zb8o47LF9e5eGqF7Xs60Pwlnn/+eVKpFEeP3lgjAoEAvb29dwzGvhWu67KwsLDJuCP9X/4Lod/6LXjlFWqaRiwWQ5Ik7r33Xr7z/CkyVzfoToYQrtFXtLgXcb1Cf+9ufvG/fIivf/3r2LbNsWPHePPNNwEIBoMEg0Gy2SzlcpFAoJsPfehD5PP5G9O5np52BBRAtapTKlXbbpbf+ta3+PznP49t25w7d47BwUHyGyvEugKszVcQRNjWF0a7ZSIsiiJ79uxhamoKj8dDOp0mlbph5nGd3eVRJFRZxBBaumKnbCBdMzqzKwaST8G49pg7Ga6tLJXwyTLeoA/LNDEMHdt2NgWQ+2M+CqtVsuUm/uTWDKtb8eLlZYpmgJ2FKooRIbYjhRhSKeaLOPk6vlwDeWecVz05+uN++mO3R8lcR5fPRTDqLOVleqNeOo4Ok8vl8PoU0pUmsiSw7232HHBnquZ12LbNxvIM7/nsQaYuZhnakWgPSd4tvF4vGxsbd/z7668u8PyfXsCxXSqf0nn0yTtTWAFEPAhNhUDSQ3m9Snq1/Fc6vh8U3nWh19vby4svvsjP//zPb/n3l1566cfKTW91oUjDdkjeRAFSJBEhqFHMljn4yEHKRhmPx3Ob1a3juLzwnSmqC0V6diZbmzkg4rhsTOR4/qkJDj/s4b7jN4I7m//b/4awuIjyhX+Ce21zL4it0bcsK0iCQzgUolQqoSgK/ps6U7ppsrG+wZEDI8QTLVpSs94kqAZRVIVQT4j3/MRxTNNkaWkJ0zR56PBDvHzqZRpqg4Fdh7icqRJWodmUkaUW3z3i92K6ApcXNijPTNL0yfjzMsVcnv6B222aXdfFKuswFGYw1Tq+QCDAPffcw/PPP8/p06fZdeAoTdOmbliE5NbiqAyEMU+vvS0V1i40UbqDiNeK7ppuMXwtZLO9yHo81Gt1wuEwGxsbt+lFBdybs37vCEmVSGyLow0bdIZUUgd3c+bNDJ0ZHcs0WW0u0BlO4rm0wbwLZ7qjPLx9awenTCbDzMwMM3PzYAZZsQdJBFSifpVQZ4xyuUy2UGIpq9NcqeLkdX7/qxPIqkxqV5yDR3vZvzOFrjcZHx/fRNW8FUtLSwz2CdgfP8DCmyvYNZ3AWILHP7qLeODtN1leVWJ3Vwg9VWai2KCiKahxD6nBCKmOIF61xoMHd7UnPa7jYuk2jgjK9Z2V0NITWXV7ywliIOIhoClk60ZbA4kLtWqTwYB61+YiPypwbIelk0sU54uE+kJk5os4goCmSO014W4higKeQJCBrhCDDw9y7vQyjfIy8f4IWDbWWhXRp6D2tTYDml9FC6rMX8iif8pFVYFqFeM978H9m38T5XM/Se133yDYeaOnHItGELI6sqxQLpfx+Xxt+pJzjVKcy+cY3DmImF8kvFwmU2siyXKLniyJeH1eLKvVDCpUdBBEfA2dju39zEkZPJqG3tQxDIOG2cS0bOp6DUPwYnlVZEnGsi1kScKwN59DwjUnDsewuXmLVNioUZso4JaaKBGFnoNdbfpxo9FAsAVK61VycwWMukluqURqeDN9xzYd6rkaRz+8A5/3zuehLMutIHtVw+/3kS8WiEVjiJJIuVQml8sRi8W+ryiMjY0NOjpbHWafz8fhQ8fR61FOvbaB4azz+BPH7/r1du/ezdraGkeePMI9hw5zaK5Ao2ZQrGbZcWwH3tANqp0gCHTu6yTYGWT59WXyM3nq9TpCQKBroAsc2Li4QXG+iC/uo0uTyKXr1NbLqJqCEtSQUj7EgNruVdTydbwxL/2DUTSvghbSyI5nsXWbkfeOtIs9w7Co100CAQ352rWQSCTIZrM4toMDrQ7HO3xsUbi2/stCK6nBcd8xKsJ2XBRJRLzDZPfHGc//5RVWzm8wdX4RN34FsIhGo3eleyqUGnzvGxNkVsoM7Urx3vdvR1EkpqenN0kX8t/5DsFf+RWaX/0q0bExZi9eZM+ePe2/d3Qm8YXS1CsNTMUhFGw1Oh0EvF6ZN998k0QiwRNPPNE+72/eG1ynOa6urranjg8++CBf/vKX+exnP8srr7zCzMwMly8ucfXNcSrFCvd9aDsXx59jcXGRjo4Oenp6CAaD3HvvvfT29hLr6OH8rg4kUeLgzuQdTca2b9/O/Pw8s7Ozmwq964j5VUaSAS4sF+ndHke/sIFRNRBcQBFRxhKsGRb39MQJ3GES5zguXp+XSrlMPJHAMAxKmSyBgL8dwdX6Kty7jkYq1RpcXiuzMxglaPiQx1JtFlU4EaFcqRDEgzZXpBSUGV8rb1novfDCC0xOTjIzM8MjH/scM02RyfUKfk3GFbycmlyipyPBE7s62LaFROY67kTVvI7rGXljY2MoisKjD94dRfWvimqlRf13XJdy8c4DjeuIxX30jSWZO7tGKOVnZNf35+T53wrvutD7whe+wD/5J/+ESCTCP/yH/7A9vbp69Sr/7t/9O7785S/zxS9+8Qd5rH+t8PgVJJeWYcTNdvu6RVdXiic/cN8dnQJXNipsTGaJdAU3begEUSDWF2Lpyir3PHFjOmr8h/+A+JWvEPiz7+G+vEEumyMebxVsMb+KT5PBhmajSSQaRdd18vk8wUAQSZaYnl+hLxlhpO9G9oqjO8hBGcV/o9hRFIWOjg7Gx8eZn51HEAS6e7vRLRcEgXAoiCzJ5IsF/D4/iqIQ8nkoVl0ef/Rhzp1fpTBTI+GN4NpOuxsH14q89RoVRSS5Pc7oTV0mTdPYeeQB/u8/+jqxtBdX1pjNVFlxTe4J6kR6gsjTBaz1GnKn/7ZNjV3SW5SuoUjLvMVywHXbzpRBj0KgK0hjOU+lUiYQCCBLEuFQmGKxSDAQRBQEDMtF1cy2fXoqlbpj17JWq5HNZlE8PhZLDv7VGoYi4euP0N3VhetCemKJwFqNi4tFjg/Ft+zQra+v8/zzzwPw8z//C7y1YXBhpcTVjQqCAK4LG4t53As5Yg2bQNyH6lOwDJvllxZYObfO+EOd7N2u3UbVvBlTU1O4rst9Rw9yz2GXuaO91Ms6HVEvkeg7C4VfPzHPy18bp5att3RytkN9rYJhw5H7hyiVzE18e0mR6ByJ4h9Ps1FuEg9o6KYNVYN4Rwhv/Pb3jAxE2LknxanTK6zZNqpPo1nR8ZcNdj46RLD7x8uMJTeZI3M5Q3ggjGM6SCIId9druB2ui0TLvEgQBNYXSqhepXXDVyREn4y5XG5R6K41Q0Qf6Gmb2cks+w90oH/843D0KNqv/RpG3cR0TWT3xi2hM+RFlaBht6ykG7U6zUYTUdWQBAHZrNHR39tyeOwL07NcBtOkrDsITQuf7SAJIq4gUrNcGqaDt2ESD3mh00uvrx+P14OAQL1Wo2rUKZcLqB4ZU3VpmnYr79x1EQTxtpzKm7+L62hUdHKX0wQsl2gygNzpo1wpEw6GQBSwbZtAIIAn4KGcrpEpNKlu1HBsl0h3AEWVqRV1ymsVUiNx7nno7WnY0CqQRnd3cPX0MlZeIFssYFk2jz6wp+3Sed3tLxwOEw6/fWfbsqx2U+rw4cP86e+eZvzleQC0uMLevRX6B96ezu04DvPz84yOjuLz+eju7kb1qXTuvU5R6mN6eppB3yCG6TB1JUOzYdLRHWJoOMa2D2xj/PVxtFUNt+hSmC1QmC5QWizhjXlp5BtIgMcvUC0JkPChdPrb9wDHdannmzgWbN/fiXZtEitrMrHRGPnpPAsvLdD9cD+vvbzAlTeW0KsGvoiHvff2cd/DQ/h8PrLZLFpAQ7Vbznmm5bSC5e+ApumgKSJ+WcHr11gt1Snm6kSUFsMASWgFJF+7h9cNG4/l0tER+LFbb+4Gyd4wpY0qvdsS/PQvfpRarcrS0tJdPfeZb05y+dlZNJ/C6ekC4aiXg4eSuK7bpkRWzp3D87nPUf2t3yL52GMYhoGibNbfH9g5wItDsxTP6WgaFEpZGkUTtTeCTZpqtcr73//+9nOuZ7ldNwC8jq6uLi5cuEAgEOCZZ57hve99L5lMBtM0SSQSOHqJpcvn0bweTj43ztHHd9Hb20smkyESiaBpWttRMx7QePxwH3eDwcFBKpUKly5d2lTAXsfhgSjz2RobSYGO+3txM62CQUz5WFUF4h6FA28zmersCrImSDRqDUi09n1dXV1Ua1UUWcHj9VAvNBATKpVimkW9QDKZfFtDkDMT89iyl1DewPXIm6QyoiS1qKExDXOxTKhiMblR4eHtydv2NKOjo7zyyivs27ePD95/kGxVZ2q9wly2huO6HOwLERWbHOyLcGkizdJcEVESGByNMzoYxbbMt6Vqwjtn5N0tHMdheamEZTn09oXbNMxkMkkmk9nSPO/IvX1MXJjD7w9w/C7uBZIk8um/cYi5h/KEQh66un84qODvutD71V/9VWZmZvhP/+k/8Z//839u/wCO4+C6Ll/4whf41V/91R/Ygf51Y3BngmTcy/J6hXhnEFkUKFR1PHWLbfs739YOvlE3sXQHJbbFY2S3temTro3zv/ENhH/6T1FOnsQT7EA5ladYLpJItowz/JpMf8zLldUyNjpe/zUevKb9/9n7zyjJ8vO8E/xdG967jPS+vO9qU23QDdNoNNAACQIiDCkSQ2mplZuz0nA1n+bs0dHOzlmdQ52RRprVkWgkkaIEgiAJkrDdaADVtrrLV2VVVnoTmeG9u34/RFVUZWVWdwHUzAKNfb5lRkbkvTfu/f9f87zPQ71WZ2ltE3cwwql9oztENOyujeyVcQXvPkzdbpeVlRVmZmZ495V38Xg8TE1M8dbcMprlw7a9aLrdX/w6rTaVtoYkSYT9PqRWkYETM9QlAXWthuJVEN0yjmFjNnXqqoh6coCPnB7ZsUAs5pv8xZVtoiNHMBe3CCAzZIvctC0urlc5PBwi9kia9jvbGOt1xIDa+1zTxq5pIIu4jyZRhnobc6GpMRDyMHHbBkGVRY48MsT3r+Ro5Iv4fH4CwSCSLBEOh2k2mnRzbcSoh2c/fJjx4RDdbpdMJoNt97oGXq+XeDzev6/vbH7x9DD5uo5Lsxg/NI14r1iGR0JuG3Tqei+A2CPR8/l8HD58GMdxiEcjfCTa2wiWCi3aukm1rvHOGxlUWyRxJL4jefal/GTnNrn+3S77Z5964KJ35coVwuEww4PDVFerlG6VqK5WsXSLVQFkt0xsOkZkOkIgvVtsZeFWkVe/dh3HskkfSvarmXrXYONqjr/6L5f5hV89SKFQ2FHFnDw9zInVGpev5yhXOqi2wFTIzckPTeDZI7mUXTJP/OJBFFViYa5As9BmMOBi33PDnHhx9gOlgNetdcmcy6D4FBSvgtE2UFwyar2LZtq7qEEAhmXTNW3s2wmOLPboPZIIpuXgEgQkl4QgCZi6teNeEX0qZrGDvlpFGQzQzlRROhbd7Rar35iH/+H/Scz0k/wX/xsIArZkE5uO0Vnr9A2ko36V4+NJzi/n6OheXIqCZhs0S3VSbpuDE/vvVtgth5mPTREqlMjPt8jaDt2uiWU7WKaJz3YYQabrFVCnQtghGcE08Xq8VGtVfF4foekwMcNDJ1OjqkLDELBtG8u2QRAf0MVydnR5cisVhIZBfCqM0DRREn7cAXfPfiGwM5APJn1oTZ1up0sg6aGWbeGYNqpP5cAz4zz7iRkSyfenQSWTSRSlwomPD5Ff0bEth30nBxgYtvuehXdQr9f7tDJBEIhEIjvmhO5Hu6OzOV/En/QRiPnIL5S4ObfJ6Njeir7Qq35vbGwwMTGBJEkPFIaamprile9dYO6NKpXNBo5to/gUxo+kOHYmyMzpaVwuF1pdI3clR7vQZujxod5eJ0C1VeXZp05x/VqBzK0Spc06kiL2ujGmgzvg4tCTI+w7tLOqLcoi4Ykwuet5Xju3ztpGHVfQhduv0si3+OEfXaOca/LZX+mZVkenoxRuFol5VCodg8QDKJ+OA03N4GDEi6KD5JMIvLPJtm5SlURckgQSCKqE6Fch6qEsOoxaAiMHEnjjD6amfVDx2V85xuaHxkkPBpEkkWAwuOcYwF4oZ5u4PAqJqSibV3PUyh0WFhY4duwYAN2NDXjxRer/5J8w8OUvA7C8vLyrWyiKAp/81Cw/8G2SvVoAQcW7P4Q7VqNdLvK5z31uRwH2Tqf3/kTvxo0b7Nu3j1qtRjKZxLZtFhcXeeWVV5ifnyez3iE9dRrLsDjxzBgHDwYRRZFUKsW//tf/mkOHDj00PfV+HDlyhAsXLjA/P7/Lq2806uXFIwP87rfPoUfTMNiLUwRgIODm+YMDpEMPTsqOHk9z9dUVutkWRlrvCVGJAoFAAK2rUc1XaHYNHnl8lsP7prBtm3w+36ckyrJMMpncYQOzntkm4PEjlE0Ez+40wOv10W13kEQB2bQxLBvNtHfENPl8Hsdx+NVf/VVarRYAcb+L+LSLM9N3hd7ypSr/9t/8iNLNBnZbx0HgXEBl9FScJ55MPJCqCT2bmnw+/54eeQ+DUqnFX/zRFTLzRWzLITYU5GOfP8zMbByfz/fARC8S8aAr1zh+/FEGHlIFXFVl9u3/6ejk3cFPnOhJksTv//7v84/+0T/im9/85g4fvRdffJGjRx8s0PGziOBQkA+9tJ8f/uU8xeUKFuBVRI48PsKRD713ph+JenEFVTrVDgHvzpslv1EglAgSiXqwz53D+cpXkL75TZiYwGM7iCERcW1nEHgwHaSlWawUbNr5KrFwANuyWStUUXwBZhJe/HSBuxuXYisoAwq67bCYrTK/WGBtLcNjjx5g/kdvYrtsIgMRKlsV6NTIF0q4ZZEDk3cpmS6PB1uHmYSXerWM1NHZf8hLaWKI9RsF9LUaUkvHFgWE6RCpmQTPnRpi3z10sK5h8ep8nlapQ3qxgpGzaTRKeJttJsIhyqqLeVngzFQc/zMj6Ov1nt1A20AQRVzTEZTREHKq1+lraiatrslzs4kdymqPnBxk8XqeK98sU1ouMH54vBeAdC3MvEHbgtGjAUZTvUXX7XbvUM5qtVr9gMxxHK5fv47jOEyODrO4ZGBKAnbX3JHoBVw+qt0WEY+0K3A3TZOlpSXGx8eZmJig07lLAwh7VU6N9Rbhb7+8iFBokZiJ7QjcLdOkXKkQm01RXqxx8d1Njh9M7qCU2LbN+fPnGRsbI+KPsPzyMoWbBaBnF+KOusEBo2WQOZ8heyXLwNEBhs8M75hRuvz2Bt26xtB9AZrqVoiOhdi8WSSf1RHl+o5Ez5f08cQvHyb+to//8rtf51Nf+EXGjqWJTj9Y1cob9/LUrxzj+HYTo9NTR/Sn/B+oJA+gvFSmVWgR29frzCtehUDKT7jQYt208CP385WuYdHomjS1ngBJ3yyMXhHDq0oIQECW+nNZHr9Kt97c+U9F6F4voG/WMRWQPS4ERUJ66yztpW1a/9f/nsLXbhLfH8dIGEw/Os2NzA2MjoFyuwNzaDCEo7fJ1DRsSSbi9eIPCUwNRCgU8sSiMcTb3baxZ8YItAOkRzu0//0Fyis19HoXRHAPhVAGPXTDboKjURzbIV/Ik8lk8Pm8uD29QpeSCmAXuyS8ClqzQ1PTUQUHS+zN1N6LXpNMQLzTnWkb1DYb+EMqUtvsdTPDLgRRIBQMUW/Uceyd9M9Q2kdzvsXTn9xPNO5D1ywiUQ+pgYfv7rhcLmq1Go8/OU7yF3c+M1tbW9Tr9b4gRDAY3DFbUy6X+6Ijd+Ta7w1g3S6ZyICf9ctZOuUOnoiHcNT1QOPrWq1GrVZ7T9Xfu3/b5eqPStS2mySnY8iqRCVb4+oPFgknjnH4cC+hkj0y9UydwGCA8HgY0zApFAo9MQ5R4Iln/SyMh5m7UaCQb+Hg4I54SE5GiI9G2ItvKbtkKh2DjR+uEX5iGF+itw77ox4apTY33t5k5cwYiWSUltDCF/cyUmpRdqDWNgjdp0brOJCvdwnJEtLNMjVZIjAcYHA0jLlepegS0SQBVRBBtzEydVirEfcqzD47weAjgz8WvfaDAlWVmZz6yaTlpw4lyc0X2bySQw2ruHw6o6OjCIKAWa/T/shH6P7CLzD4P/6PQI/lYxjGnr6T+yaGkWyN0skwidQgzco2c1fynDp1mq2tLUZHR/v3+x01zXtx8+ZNRkdHcblcnD9/ng9/+MO43W4ikQj/8l/+S0qlEr/1W79FKjlJt2syNh7uWyPcuHGDdrtNo9H4axnKx2IxbMXLuYtXiPh3Jm5OdYsvnhpEig5RavbmhBOBnoXBg2bz7mAsFeDwhyd462tN1i+uMnZkvPeZlo1dN2nldNQJD0eP9b5HURR3CNCYpkkul+uLxiiKQnYrQyC8D/ItnM5uPQS3x025VMJvyziS0GNw3LMnr66u4vP5dqmN7oUL7xbIXaoQH/DjmggDUFwtMP/DTaZm0sw+YKm6M4/8fh55D4Nv/cl1lt/dIjISQlZECstlvvVHVxj8H57aJbR1L958803a7TbLy8s888wzP7NrxEMneidPnuR//p//Z1544QUA/uN//I8888wzHD169AOX1O0FQRCYfXKU9ESE9VslNM0kPRzsbZDu976MiYiHyRNprn17EdWv9rtqlVwZswkTp1OM6SWsT38a8fd+D/HR3qyeIAqQAG/OuyP4cikSj01ESYfdvHtzjVZHo1atEnXBMyf2MxT20Gq1+vxzq2kRSUWou3W+9uoSi6+t0VotEPT5+Ma5Nyi6ajz15CSN7SUW312CwQB+l0yuodNcypEI+fC4ZAzTZjDs4eBQlNxKk32H9zEw6cdHhgPPjtEUJml2DNyqzFDEw+QdUY57sFpqsVXrkCp1MLItlNEgMTGEvpEhmGlibLvIeWQKDY3BsAfPETfu/TEcwwaxR7u5g6Zmkqm0eXQ8xvGR8I7/E3ArfO6LR9kqblBeaLG9WAYbHEXENxTgiSdH+fBTY6ytrRKPxwmHw9Q6BmulFoWGRrbWRTMtRFEg6lV4d62C2G2iCDZRt87NiIJrq2dwLvoU7JaBYknkgwJnRsM7lK8ajQa5XI7Z2dn+QrHXXJ1tO8xfy+FWpB1UinarTVfrkojFQRQIxDzk5goUml1St2dsTNPknXfe4eDBg/hcPpZeXqI4VyQ8Ed51f8puGU/Mg9bQ2Dy3ie3YjD091vcB3LhZxBPemxrmDbqpbtTZ2qgzvEd9wxv3sti9ibCvQ/CEm9j0+wcRoiR+oNXuLN2iOFfEFdqpXBYYDBBaKuPWDNq6hU+VqHUMSi0Ny3ZQJQmvS7xtgt0LlHTLJlvvEnRAGo3gu12oGJmOUlqvYVs2oiRiFjuYuZ4vnWYYyOkoxa06ot2isr6E8n//vzGQjmE3dNZfW4cUDH9uGFfSx/nvLtJEwO1XGJmMcngijTI/z/79ExTyBcLhNIqqEAoGKBfLVJerjB4ZJTQSopFtslAtk21qWCpIwz5kWaGiWejbTfC66Bq9bncykeD63ByBgJ9up4vb40aKuJEHfBhbTQaCHjJVG8MWcCwDxxQwLRH5TtXdsBBUEcEl09EtMpkabt0iPuAH00EZvSvugNBLsvL5/A6Tc93UURWFlZslHvm19w9aHoRisbirkg8wODhIs9lkeXmZ0dHR/ozjHUSj0b6x+h2DZ8uyqNVqiKJIIpHgpS8d40dxH92WzrHHRzhwOEE2m2VwcHDHZ2WzWURRfKjgC+DG1Ry1rSap2TiSItJsNPGGXDhWmPl3Mjz7/Aw+n0p9o04j0yA0HkLTNKrVKunBHsvDsR3mthvMl9t0Yx6SgwEUScCyHTIdg+2FApMxP8dGQrsolw3dxG4ZqPpOSm4g5qW+3WBtqczE5DTlcpn0qTTd769wOOjmZksjU2njUxUkEXTLpmtYhBBJLlWxNYv4s2OEx8K4R0PUZQFzo0ajqdMReuqcYY9KxC3hdymIZs/70xv3/swGcv+/wHMfmyYQclMttbHEOum0TDQaxdZ1ys8/jzU7y+C/+Tf9v89kMu+p2xCNRGg1N5CNKlcvvstLL71EMBjEMAxWVlYIhULE43EkSerP4gEsLi6STqfx+/0sLCwwMjLSp0wbhsGnPv9lvv/aW5RMlaBoMTER6X/PoihiGAbJZJIzZ87wgx/8gKmpKYaGhnY9q+8Fx3GYL4i8+f2r+MMeQqF1xsfHURQFTdNYXV3l4x//+E90f4miwIsfnUGWRb75n8+S26gjmDYOAnLYxchz47z4qf2IRoNMJsPQ0NCO98uyvON3mUwG2hVcMYe828C30SAQdu+aExd1cGSBmlfmSMyHzyVj2zYLCwsMDw/j8/ne99g102Lu3U28bhl31INtWdQbDaIjMZylKlffzfDkE6O7rsva2hqSJD20R957oVRqkblZJDgQwHc7rklMxygsVVhaKHL0+CDRaJRyudxfi+/A7/czNTVFKBSi1Wq9J/vipxkPfSdfuXKFYrHY//krX/kK/+k//aeHNoX9IEAQBIJDQQ4PBXFsh06lg1bXcBynn4Q9CM9/ch+NSpfNy9uwXsPGodFpMjnk5sUPpTE/9gz8T/8T0ksv9d9jGAaRqQiWZZG/nsc9HGRpsYRtOoxOhplK+JFafrayeQYDDo+dPtWnHvgDfnx+H9ub29TX6pz63Cn+eP4WlQvbyIUqk1MDCLJI8/oiastgbURm4sgkN25qeOwUiTEv+VIFUZJYK9bxumROjMZ5bCKGS4St+S2e+MwT7H96P9VqlUKhQFS2QIGhofgD+daVttGzRMi2+tV2AI/fS3Dci5Wvsh6Xee1Klecf2UfIoyDcn/joJvm6hmk7PDEZ49l9SeQ9zOYifhcf+9AAVwY2OHjgFIZh4fWp7N8XJ+LrHd/09DQ3Vzb5y4sbFC0X1baBIPTESCRRwHFgvdRiyQgRjQ5zoeZm/0SMQsthrbtNqFBHzttIPg+tET8DQ0FGPXc3oTuB5cNU2E3bweyad7trDlQqZRRV3bEAyaqM1dLRb6tSdrtdzp8/z8mTJ/F4PGTOZSjeKBKZiiCpEo7jUG7pVFq9c4sHXIQ8Cq6AC2FYIHsxSyAdIL5vt6/eXrgTlsViMYrFIvH43fcVi0UymQyCIHDhwoWHOu8POmobNZr53aqj3riX8ICfxEqFLd2ko5tU2wbSba+8+yEIAg4OPlnE3bJYx2HUsokDY1Mxlq8XqGUa+H0qVq6JoErogki73KVpFLENC5+VY/nFz7KU1wjW88ykAoRDDs6Wza1Xlnh7vsD2QglRt8Ejs7VQov3UGLJLIpvNEglHej5s9MRlqEJqKoU0JVFr1sisa1x9aw0l7MLtFvBEA7TbHTqNNmbdpn0zixRwMZLyU280iMViPaVGSeoZnrs9KKMh7JYJ9S4xj0S2ZeN2udBt6HR0ZKkXoEka6CLUNAPFlhj0u6mIApJmoYyF+up2O66514tlmnQtG0VREEURxa3QrHd3/e3Dotvtzczc/yzcgd/vx+fzsba2RigU6vuH3g9RFInFYn2qp2VZ/Rmjx5+LI8syiUQCWZZ3Sbqvrq4Si8V20VPfC82G1tM3ER1qtRoBnx9RljB90K1rtFs6Pp9KebGMIApohkan3dkhRT6fa3B1q0rApRC7j1IZ9Ch0DYtb+TqCACdHwzs69QICKHe8EH0gPTgITh1JYWkW4tubeA2bis/FQrmCy+UhoMrMiDLmtQKS4zD+0QmssIcL6xU2yh3afhlhxI9T1hBbBiBA1E14Ns70dJRutcvaD9cQJZHk4Z8uytVPM2RJ5PEnekWFCxcuEF1agvFxcr/yK0i2TerrX7+jEgL0utf3Jnr1pkYh3yQ9GMTrVggGg6ytrXH16lVeeOGFfudbURSmpqaoVqssLi7u+IzLcwucv1SlWS8Ri3sQhDWe/9ATLCwsYHqiXMpqLEvDeA5/jO+ttFFWl0j5RJ47MsGh4d5zuLa2RiqVYnBwkGQyycLCAtvb21iWRTgc3kUR3Qvlls6F19dxzdeoCRWUZwaZn59ncnKSS5cucfLkyb9WEUGVRY7MqNh/Y5xgaJJu20RRJMYmo0wMBm+LDflot9ssLi4yOjq6Z+cUegWhx08epRMb5U0ri6slUF/II8RUBLeMLCmohoCnK1FNOAgRN4cGg3Q6HdbW1piZmXloimvXsNHqGm6felt0SyMcCvfsdDwyrWoXw3JQ5bvXZmFhgVAotKe4zU8MB9irUXs7mAkGg6yuru5K9I4dO9ZP9n6W8dCJ3tjYGC+//DJf/OIXkSTpPRURP8hwHIfyYpn81TzNbBPbslH9KokDCVJHUyjevRO+sN/Fl3/jJDduFFhdLLOZyfCCInHs738B4fe8dH71Vwn93b+74z0bGxuMjY+hR3WaxRY/+Op1KrUOOLCxGOC5T+6jUMhTLRf42Ec/tuvhs3QLtaUyfWaaNbvE8o0SkaxG4ugwkiJTKZfRPDZpd5DWmsat4WGkJyC23EbN6sQEF+1siSOzI9QkWM2WcbXbTLs8uAfdTH14qj9n4jgOnU6HdDrN1tYWmqYRCAR2Pazibbs8ZHEXZcCluBlKCbxb3mRwIExbt8jVu6hyL+mybQfdsnBJEmMxL0eHwxxMBx+oqJbJZJiZmSGfz3N41r/rIXYchyubNc6udNiu2shWifF0AteuBdLDeOwUXcNmId9kpdBiKh1l5FNR1rcadBsahmQxElM4GFe4cfFtJgfj5HK5vjT1w0CRBNxBF7Wlap+qGQ6Hd6mF6m0DxSvj9fTUEK9du8ajjz6KoiiYXZPijSLuiBtJlbAsh8uZKsuFFrrZSww9qsyBgQD7BgKofhVR7kmmx2Z6Eugj++Jc+8EKDO3usnUaGrJbJj0SJBKJsLi42A9uax2DXEfmzAtfIJfd5uSRn+3F8b8V2oU2OOyS8BckgeShJEbXpLtRY66twW0VzvthWg6aZSHhEDccAmNBqnE3VzZqfGg2gder8OhzE7z13QUaVwsIUm9WqtbUkUot1EaFkKdO6pOPIQ7GsW2odwwurFcY9jo8OpnmyreX2NyuknxyGCfTwiq3adU05t/ZYt9jPrqdLq4BF6Zm0i600Vs6oeEQEx+ewJf0Ua1WufjGCi1NIz7gQin17jfLMqk3miiyjM/0YVe7rDk6I8kw3W4XVVawHZtAMIjW6dI1dDyTIcwFAyvbZSTmI53w0dBMCtUm2DKC4GAVmigTAaYSXsbjAbKXs9S6FsqREOpo6IH7k8frRetqFIsFUgMDNOwu0nskGe+HbDbL5ORkn4K5FwRB6Em336ZqjoyM7Do+TdN2BGeSJO2gYBmGQT6fx7Is6vU6y8vLDA0N9faJsbFd68T7IRrzYto2tWqDSCzc/32n0sEf9xEMubF0i/pWHVMyQaOv4gw9peNbuSZeVb6rmnsf3IpE1OdiudhiNOolcc+MeCzlQ/TJdMsdXF0T8bZQWKPURvWpjE72AvFQqEe7HTw9iDfupXCjQHi1yvrcDUZGRxkdGaGeq2NEXAw/PkxNEjm/WKDWMQh7VdIRL0LUC7cbnbppU20bXGx2aG/XOToUxrFbbLy5gS/pw5d8/07F/x93kc1mSSeTpJ5+Gt3txhMM4rtwAfGee7nRaOwoQuQKLb76796hmmmQnInypb/9CNuZNVZXV/nIRz6yZ8HkTtK1sbFBs9nk6rXr/OiNCoVzBTyiyGqtRuhYhA8/7aB5k3zz6ha5UoWpQJTBloO+XcU/nKRoivyn129xZizAR4+Ok8vlePQ2i0qWZWZmZlheXmZmZoZarcbq6iqyLJNOpx+Y4KiyiMunUpcEdARCUT+HDx/m7bffplKp/LWTFtu2sSyL4wen37Nw7PV6mZqaYn19HZ/Pt+d1XF9f5/jx4wQSUTYrHbZsSPjcOIUWVC0Ms01TcdCGvCx5dD4RUwmJGtls8cemUXpVCX/ST/7yJolgnMA9tPVOU2focLI/cuM4DnNzcwwPD7+vYNWPg1jMx+D+OAtvbiArIrIsUVqrEB4KMTX7cMXtn3U8dKL3d/7O3+Gf/JN/wh/+4R/i8XgQBIHf+I3f4Dd/8zcf+B5BEKjV3tun7GcN+Wt5Vl5dQRAEvAkvoiyi1TXWfrRGM9dk6vmpB3b33IrMiaNp0kkBTfMT+af/FI+l4VR1rD2UrizLQpIkPBEPgZNpmv/5Kj4H1LiXWr7Fwq0NtjJbfOxjH6NarZJI9oZJHcehXWjTrXRJHk4y/qFx1lsa9a13SKoeEMA2LfL5PJIsExkcYGuxTHe7xolEAClj01zJIemgtrtUNhbxD0ZQ4x7Wuh2ssQ4HnxvdIUATjUYplUpks9l+YtNoNJhfWEQURcZGhlFVlXTIg1uVsQZ9OFcK2B4D0afgdExsXacQ7JDwengsCZ84EiPbhmJTp2NYqLJAyK0ylfQzHPY8UPL4DgqFAsePH2d8fHxXtca2HV5fKvLDWwVcisSBoTCiEKFcLtPRbHy4cUwbQRQQPDJSxI1flvF7VGodg2tbdQ6mg3zhqQkEAbyqTPT29fh+Jcdbb71FKpXqJ8D3D0PvBUEQOHw8zbffXodcmUQ6Afedo2M7NKsdZh6bxuk2uLm8zOOPP96fK6it12gVWkSme0HSarnF/HaDiE/FE3CBA/WuwbVMnZBHJR1240v6el2nbJPAYICjjw6xcGGL4nqN2HCwX4U3uial1SrjJ9JMz9xdIHP1Du9c2OLGxW3aazUc08bC4eqbVaaODnD6kSHGH9Lb54OI5nbzgQUgd8RN+vgAuUaXYKmJ41Xp6iYOQr8Y7txW2AxYDgHANxhEnY4QV0QKTe22GJGbRMrPqVPDzFcNKl2DQrmFoEh4nQY+q0rEqNKuVvEODiKKEPYp0DBYq5lM6hblSpPaeoHgoRChAzHMsg9no0ZtpUxtRcQzoJJtZ3G5XQQGA4w9PUZ4ItxfB8LhMHpDwHA0pFgQai0c3UKW5N4cntsNDRGfYGJ4g+TqGoZhIUkKrXYTVVFxedy4PG5KtQbbARj2RBixBLy6TcrvYiToodZuIlkSkt/N6OkhjGaL2twGgktCnAzhxD0PNePp8/tpNBqYmkE4/pMF95qmPZC9sBei0SiBQIDl5WXS6fQOCnehUNhFx7wXiqL0Xx8ZGeHSpUvkcjmGhobIZrN4PB5isdhDF2CDUZPYeJDKWgNV7KB4ZBqFNoZmceypUVwumVahRWm7hD/p39WJ3Kp2aWoGg+H3FjHxqBKVls5Gpb0j0RsZDbMyE2P73S2slQruoQDdmobe1Dn84QnGJ+4meuvr671u6GSE8ESYt7//NtqiRneoQ+p4CsuwiExGqFo27yyV0Eyb4Yh3TzsGVRZJBl10dIub2Tqi0PP4qixVyJzLMPPizAduRvj/KFhWL444ahjYloVSLqOaJoqzk467trbGwYMH+z8v3ixQmC8RGw6QnStwY26bN17/Jp/5zGf6CrUPwsjICPl8nnevzFFe9hEJuFCTHurXK8gNlZym8PKNLNVCif1NBeNKBqNcx+N2U7t2k/ThYdozEa6WbKrfe51arbaD7izLMpOTkywsLDAzM0M4HMY0zb59QzQa3TFnC71RkRc/uY83Yh7yxS1eeLpX5KzVakxOTpLNZncUbX5cbG5u9r0Mz549+55MGUEQGBsbo1KpcGNhCTEQR79NMnJMne1CmU8ODaGqKp8+Psi3ZJH1gAKDPty6hSMItD0S3rCHT4YlQs0N1lerJBIJVld7oy4PS2F0LJPkmERh3oVW1HENqL0CZKYOPoXjj/ViRcuyuH79OjMzM++pFgq9pPfqpSz1apf9R1IkEu+/dr/w2UMYXZPthRK2ZRMbi/Cxzx/aMZ93R831v2WS+dOCh070fuu3fotjx47x6quvksvl+A//4T9w+vTp/yYc2p8VdGtdMm9nULwKHa/Cm5s1NNNiOOJlajzE0qUs65ZFK+yi2dZBEIgG3aSHgowk/CQCLizLIpPJsL6+zqf+638FQaDmDZAbPszAWpXAYABHcPiDP/gDPvvZz2IYBrlcDk3qIh8M052romca2I7F5vwy+w/uR5VVqt0q7VIbs2OiN3Q8UQ8TH50geSiJpEisX79JfCCOu2TTarUpFPKYlsXQ4CDtlknbtAlpGuo2OD4F+YkBfI4LT7VDfm0L0zLxp/10jyTI6wWeHUqytLTE8PBwP9CJxWKUSiW2trYw3SEuXsmzvFDGcRyCkSIzE34OjCWYSPi40TEZnIlgbTQwS22croE25mPRNNk/GOE3fvljvPvOOWZmZnhi6sHBz4Nw78I6MTHBK6+8wsmTJ/uvX9yo8IP5AlFfz8POcRzMQhv3hk5rqch2pYnP6+spXKkyyoAPdSyEkvYT8ih4FIm5rTqSIPALJ4ZQblNH2+02siyzublJqVTil37pl/qV+Dt0K1VVSaVSu6qDjuPgctXwTwQw1jpYUQvpHkUsx7SpLFdQUj6GJ1QymUy/EnnvPcptDzPHcVgptFBl8a4liNCjVG3rHTKVNumwG8WrYOkWWl0jMBhgejbB6U/t482/vMn6lRyqRwbbBgSGDiV56YtHkW6fb8NS+frvnKN5s4pfEEjEvAiqCKZNa63O3EKZ5StZPvH5wxyd3Dmv1y62qa5V0eoaqk8lPB7+wFXTza5Jp9J5YKIHoETc1NJ+go6Dp6HTbWjogoN5ezpPdsAtgSvsRk76UdK+nnIgvXtmvdxiIOTGsRyMUpvhqQhux6SxrZBQbNisIJoGzaiPjnOvRBNIWMiKyrWVHOvFNeS2RH27TPhgBCXlo2roWPg4/cVHiEV9bOe2mZjtWV+I9810WJaFZnQI+AP4EkFquRZOro6c9JJIJJEliXylTDzoYXrfAG+cz5C9VKNs15H9Ku6xLuFkmK5p4VFcTA+FmYq58eg2clemmW0itw3ChkxpvUR8Oo5LEHB8PgLDMVLDPpa/fYPcapHhAwNI980H23bP7gEHNF0jGAzSKLcxMTlw7CcLwrLZLGNjY0CP/lOv13cFgbu+79tUtPuFWn4cIYhCoYCmaciyjGVZTExM0G63yWQy/UA5EAjsSTvTdZ319XVGR0f4wm/G+c6fXGNroUyz1MYTcjN5KsxTz05iWzY3Xr9Be6ONS3BRKBWQFAl3xI0n4iHf6LEtHiav9LlktmtdHNvpJ1GSLHLmuUne7VpUFBG9ZeCLenjsk7M8/eHJB14LQRBwx9xMPzaNx+2hWWmi+lQkt8y1WwXahkk66Hlfzz2PKuHgYiHXJB3yEBkKUF2t0sw1CTykwt7PO+6oTDa/8hV8moYpyyzv28fh20XNRqPB5uYmothTzc3n83Q6HQy7gSviorTRwJv2cfnKWxw8eJCJiQmazeYDVRChd+9HIhGmxkfZWq5R3ihCu4xP8aMEXCwXm2TyJfa3FfQbRcSIC3kkgMvnwyrL1K5sERIE6hNBlqsWo7E41Wq1z06CXrI3MTHRT/ZkWe5TqsvlMqurqyiKQjqd7t+n+9JB9v3iYV57rUrMp7K0tEQ6nebAgQNsbW3dfuZ+/DngvURsTNN8zxnCfKPLjYLB5Q2T1e3reDweVFWh3W6jC0O8uVrlQDpIKujmS4+OslpqcTNbp9TUkUWB4aiX2aSfZn6DCl5Onz7dP5ZSqdQf4xIEgWQyuWdyVi6XqdVqfPYXHsXnW+TaD1d7WgkiKGGVk8+NcvLIALquc+PGjb5H3vvh/NubfOcPLqG3DK4fTPKV//4JXA/wILyDRMLHr/+Dx9lcr6EbFiOj4V3viUQirK6uEgqFyFY7XLmSZXurTq1W5XhJ5dDB5Pv6D/+04sdS3Xz++ed5/vnnAfj93/99fvM3f5MvfelL/4cc2E8jaus1OpUO7vEQ524VaWgGqiTy+kKBqzawVsN+fRVhNNxXKFoG7KCCdyrKzOEUUVeN04f28/rrr7M0MoL5a1/hpvsoNy+VCf32Gzzy9AgLzYtcvHiR2dlZxsbGSKfTDEsSwt8O8oNv3qK5XUNobnFy/wHQoJVr4bE8FHIFhqeGGTkzQng8jDvUm1FZWFjg8OQQlw93yX97kZTpodPpYtsWqqSwvV2nFZY4qnqxmlXUsRBio4kU6KnWpYf9bN5cxWvYxH0qF3IdwgOjJAIuNjc3sSyrr4oVi8V489oqr/zlW5irTTw9Sz4yDhQnIyifDDDr7bDt1skM+/EkvLgMm3K9yma7StTl8BsvPIpLVThz5gznz5+n0+n0g6k7sGwHzbSQRXGH2uYdZLNZjh8/DvQqNZZl0Ww28fv9ZGtdfrRQxOeWe0meZdO9UaJ7owC6jRpy4Y77abSauNwuZFvC2Gqgr9dRR4N4T6ZRfQrjcR/Xt+qMxXw8OtHraOZyOc6ePcvGxgYnTvTkwRVF2TEMrWnaDnNXj8eD3+9nfn6eI/v3kRwY4xt/eJntWyXckojilrF0i65uIsZcHHk6SioksG/fsV3nbRt2v6Jv26CZ9p7XRxFFWrq143ftrsGVzSpXNqtsN7tYB5N0txt0NIN0zMepU0M8/thw30C63NL50StbVN7JMDI7gBzY2bEMht34uyblhQrf+uNrBL5ykolkL4Aq3ixy85Ul1jZqNC0Hrwgj6RD7nxtn4CcMun8aYXZNTM3sWxbshWbXpKWIhPfHkQwbV7mLVevi6FZPgEiVkKIepIhrx6wq9ALoQkPHshzMpo5W13CFXGyv1fF4XVjtOmIsQm1kkEzSIn7PAL1j24iCQMijUO/YnPzQMa7/xRLahk5zoEO31aVZaDJ9ZoSDH+oV9MQBEckj7UrybNtmZWWFR57Zx8t/cB4EifD+AQrVFZxqFzUZpN3oIqoCY5NJ7KaOsVgh5AgIfhfdagfTtnHFvOwfjjMQUBDNLpFoBNMwe4PyA1HcspvyYpnUkRTyfpX5xS7rixX0pt6jxgoCdsehWW8hKgKyJOG53TUzDANZUWi2mvh9vl53PNti/MgQjlDBNCM/lgCDYRg7/j4cDrO+vv6+id4d3CvUsheV80FYX1+n2Wxy7tw5GrUGH3nsI4SsEIIoEIvE+lYmjUZjhydaJBLBMAwajUa/IzCQdvFrf/8JMps1uh2D5ECAernIjR/Ncf7b50lLaZobTZzbgimO7SDJEt64l65hIngeLjGVRAHbcbCcnUGHy6Ow/3CK4WfG8E+G8QdcfX+rexEIBHbQ/8bH9+E4HiZTg8z/6Q18KR/Fpk6hqRHzud43ybsDrypRbetslNskxyOYXZPaWu3nLtErFFpcPZ9h/+EUg8MP19GoVCp4vV5arRarus7M3//7XH3pJf7y1R/xD+omfqfB1772Nba2tvj85z/PxsYGiUSCZDLJ2BjEYgky61U2tq5x6sRsvyjh9/vJ5/N7JnqVSoVGo8Hk5CS5XI4Pf/ow366dp7CSJzoS4PRzE5xbzTHo8WDdKCKGXZguAVXoJRDeqJ+K1qW7WiE2HOCNfJ3Tp2aYnJykUqmwvLxMKpXC5+v5Bk9MTLC4uEggMcTlS9vU6hqD6SAnjg6jig6bm5vYtr2jy5VMJtnc3OTWrVt8/OMfB3rPeqlU4tatW+9pJ7AX7hdYGR4eJpPJ7IqJ7mBuu853r2cpt3UiXpVjU2na5Rq2reMfTVBuhXnlZp6L61U+djDFocEQs6kAs/cYmuu6ztLSElNTU8iyTKfT6TP57hjUA7usHCRJIpVKsb29jc/nY2Kip9j2qU/s4/RjI6yvVEAUmJyMUClk+oqWP45HXjHfQqvr+FM+Klt16nWNROL9121RFBkd33s++g4cx+EHb2/w1jfn6Ww1UBBwHIeXL1Q5Nx7iI589xIl9D7a2+WnFT2yvYN8nU/3zAL2lI0gCja5FvWuQ8Kusb5RpLNaxuyYDioykSnjHQnfVIU0bq6rRvpznwqVthEGVakckm82x+i/+BbPpWYr/cR49GaFgO7z8jYuE9lUZGRmh2WzueMAP7k8yPOjmL/7iL3nuuU/2KIHhJJLTC/62CluMz4zvCD5WVlaIRCLEY1E++ozI781lWFnIIrZMhobSNNYbrDo6oaPj+MoGjm93RUWWZSLDCcqrOSLlELLLQ1MzSQbdjIyMYJoma2trKIpCPJXm/DslnMUGA6NBxNut8WDboLRQ4q3XVP72r59iesLmtWvL3My1sCUXVqtBvLPG5z/2JBPp3iIiCAKPPPII169fZ25ujoMHD1Jt69zINri6WaOtm0iiwGwqwMHBICORXkC3VzXwDn3z0KFDnF3ozW/MJP04jkN3rkjnSh4p4kIauFuxCYaCdNptWpbe67QaFvptaqLv8SHcHoWQV+GNpSIes04q5O3TU/79v/v3tLZb5K7mcGwHV9BFcDiIpEi4XC5GRkboGhb1jsHiyirr65eZmRwnl88TCgb51b9zmitXcly/uE23ruFySxw5PEC9tcT+Cf+eCn8AkkvqG0tLkkDYo5CpdgjeSyd2empYkdvfjeM4lJoaFy5nKOoW0nYTX90gYjuERAHNLdFQJS7UO3i26jw2EUMUBS5ey1G6miOc9OxK8u5AdMtEJ8LkFiqceyfDxCf30y61ufnKEu+ulimrEi63jGZYFLZr2N9fxpf0fWACLce+7Yj+HoGnaTvYtoMkCogeGXHIjzJ0DzXGdtB0HXmP2T1JFDAtB8t2sE0bx3RotlogyWBaCIEg4vFjBEWB4IHwDkGtVquN3+fDRqCGyPDEAO5nRDK6Sa3excTkic8cZt+xu8nhHfrOvdQdx3FYXl5mYmKCUKjLxdeW2ZrLEhzyE9qXwlisUd2o0GprTJ0YIT0Y4NrlbfSmTjTtw+1xY8d91LYbjCoi+5JeavUaofDt6roik0wl6Xa6ZG9l8Ua9HPzsQc6+s8n8+RVC6SCxkRBG1yS/VMbuGNTWmgzsSyCpAs1G8/Zx2nepkoJIbrGMN+bl2U/tZ3omxsrKCul0ekd12jRtdMPE7ZJ3BSJ3ZN//Orgj1HLhwoX3pG1Cr2O6urrK0NAQw0PDZK9kOfujs+Q6edrn9Z63VshDZDpC6kiKQDKwYy7q6tWrOI5DJBJhc3OTWCzWP9eh28G9Vtc4/9XzvP6N1xmbHiP6WJROvrPD8sTSrR57JNfA8Ck4h9W+AfmDoFs2ntsekHvB41OJxh7czY9EIqyvr+M4Ct/7xg2WLmXptjUi7mUGLDg6GmJjtYJtO3sWtt4LQbfCZqXD/nQAxafQ2G78WO//IOCd19d442vXyX9kki985dT7/r1t26ytrTE9Pc03vvENnvjn/5zAxARngPllF3/0v52jId9EVGqUy2VGRkZ2dX0OHUyyuXGRgwfSHD9+vG/RBTAwMLCL7thoNCgUCjsSpcePpFl4VODFX32BSMTN8soCzY5DyuXHaBnIQwGMdgu/9+69FUnFKF7fwFcM0DVswone/4hEIkQiEbLZLPl8nuHhYRRFQfEn+J1/9Rr6ehvJcbimiMw/NcqXPn+0//wXi0WKxSKqqiKKIleuXOGRRx7ZsWbEYjFcLhfXr1/nwIEDD53YaJrWo73fxujoKG+//faeid6N7Tp/dWUbB4fZqA9zq0H3Qg5qGo5lkpcyRA4PkxgOsdXW+aur24iCwIH03eJUtVqlWOzN492hgb711lu0Wq1+4noH91s5tNtt3n77bZLJJI7joGkaqVQKWZZJRb2konf5JHrbxzvvvMOzzz77UNfhDqYPJLgxEqRV7eJKmvzv//tvMzs7QzQa7TeiflIsbxv88KvX8GsO6Ykooqu3rlltg8pSle/80RWCf+sRph6yGPLTgodO9O4Mmt+5sd9r8Pxe/HU3wp8myKqMYzm4ZBFJEJhfKlO/mkHpWCiDQdSAmzu+Tn2OvyohJ70E4h7aKxnUjMi738nw7C/+Jh89NUI730KSF3G6OjiQHhrgsY+c4PBjh3dx1dvtNt/85jc5c+YJBgcHuXnzJoHY3Y18MjrJ6upqn067sbGBx+PpV18Oj4SZOiRwWRBR9RR6PA5emIh4cAST5mYTn7V36zwYCNCyimS2twiODiHeU32+Q3Podrv88N0bbF3OMBhx95M8ANGrEIr7Kc6XWdquc3wixouPHuBFevLI//5Hb+KWJE6f2G3VcejQIVZWVvj22XOsWhG25kt4ix3UpoGhSryZcHNlLMJHjqU5ORohk8n0u3l3MD4+zltvvUVkeIrlYpPBcK86pW/W6V4vIEXdSP7dyYrH68UyLarVKgF/AGUogLFZp3tdxfvIIAm/yru3MhTSYxy5PXA9Fh/jxemXePOH1/ir1kUcB4JelfHpKNNPjWElvdzKNriSqbK0kUVSVIKBETY3HYYiEpOmRogaE8MiY0MD+INhouEQly5e4JHjJ95z7tWX8CHKIqZmIrtkJhI+svUuxYZGyKvgOL1OXMCt8O6r32IrHsa0/VS6XlxdnVjbQpQExJALQRHBdlDrOv5bZdrbTb5b66JbNqfHoly7tIXHcnDHfBi6jvKAGUTRLeNzS6xczpJ/ehxztcraeo2KS2Iw5EUUez5YmWqbja0G08uVD0yi15M15K5U6R4QBQFBELCdvYXBEASuXLlMKpkikUz0O1TQu26C0JPgtuglA4LQ69bhOCiqgqX1kgCvz8uo7+567OD0ZkBvFwYEAVIDfk6cGaKstJjdP4Xf79rRFYLdFMXl5WXGx8eRJIlEwsff+NuP8bXfe5vSWg1VUnEEsFsGY5NxJg746HR6tiT3etpZht0TQ4z1klFJlvp2H9BT+Ozmu8QSMaIno2zUc1x/c60nGpLoXQ/Vq5DeH2freh5f3EtxpYLslgmnA6gehUKhQLPeQDTd1Botgmk/z3/hKDO3B/LvCKqEw2E0TeKd11a5dXEbo2MSTPo4+vgIp58YRZZFTNNEkqRdXTiv10u73d7TPuVBEASBRCKBy+V6oFBLu90mm832KuQ2rJ1dQ153EU3OMi8qOHqPFp40TBLZOrW1GpMfmyQ0EsIwDNbW1pidne3T7O+nYImiSMgbYvm7Pe/N5P4kTaeJ5Zao6CaF7QaSS8KjSkS8Kr6kj7gsULlZpOsq494X2yXPfs+NRkc32Z8K7Dgvy7TJZ5vU8k2SHZ33q5Pbts2f/ecrLL61QSDpIxDzU75eoFZoEZuNUWhqeJQfv3btc8lkax0aXZOQV6Fb6WLpFtL7JK8fJEzOxNl+ZIg//avfwxVe58yZMzvUVe/H1772NWq1GnNzcxw9erTfuQEYHh2gld/i1371V5DkFqVSadf7HcfhBz/4AYrSY+7UarUdNGOv19vvEkHv/t/c3OTAgQP93/n9/p6i5YFxDh8eotVq0W4P4C6WKZfL+Bynt0A67Jh1dwTwerxsbmaQvOKu8xwYGMBxet06x3F451ITfaXJwEQUySujFdpsvLHJ9aNpHjnSS3LudLl0Xefll19me3t7z5l8v9/P9PQ0165d4+DBg+/LINja2tpVAFJVFcMwdlG9Ky2dl2/ksB2HQVWm/cYm+ma9R/P3KUiKgrcJ5VcWcY2GST81wbZh8r0bOQaCbiI+ta82eu8M4Fe/+lWuXLnyvsqTd6iaTz31VP8513WdbDaLaZpAz684mUz2KbzT09N0u90diez7YWY2zq/94yf5X/5f/4K/+2u/yssvl7hy5cpfO8kzLJv5qxXsQpPQ8eEdc7qSVyE2EyU7X+Sdc5t7JnrbW3W++dVr+IIuPvPFo3jeR4n//0w89Ko4Pj6OIAh0Oh1UVe3//H641+/kZx2BwQCKV8FtO6i6ibFQwe9INL06m2sL2O4Ig6dm9tzwavUakeEEqigjr1Z585sLxCJeTk7GOPP8FOqrqyDCqWfGcMd7m/+911fXdb71rW9x7NixHYvqvRBFEVmW2SjWuby0Ta2hMTSYoiU1GYt6EQWorc1xaizBcy8+R1fTCLllblUdXlsqEpySKJ1dxh/YvcFZVY3E5CAvFxd44nDP9uB+uN1uItEUorZAW+oi6OqO4F/2ydDo0KjrAJQaGkvLZX70o0s46jBPfugYt27dwufzEYvFdiz8ycER/uxGneq1DMlsB4Fe8mh3TLy3qrSqOt9zHAS9Tew+dU3odSKazSY3MmXausVIVO75kq1UAfZM8u5AkiXC4TDNRhNJFFFjXvSNOtJEkKrRZCgZZaGk8Yxlo1e6XPrGTW7Mt7EnZthwSQgIrOsGK+9s8vb1LJ1DCap+Ga1ZZWooidfrwbndZVsutriVs0kGXDw5HeXocJhCocA3/vzPmJ6eptPpEAwGWV5eZm1tjeeee27HsQaGAgSGArTyLUIjIQbDHh4Zj3Jju065pSMKAnG/yqHBENvWPs6+9Q7tVpyB1DCRpoGcDvSrWP37yqPg2A6+rSbSXImzsohpO9RWqkTCHlye2x2Y9xCb8cW8rCzlePvCPPsMDy3bQZFk7uxTggAeRaLRNehWf3Kp+582SGqP5mgZFgp7L/xeVcIli3R0k8Bem4MA0WiMXCGPYZk7Nty2bpEMuJBEsBSRtY01xvaNoYoObef28m47vaT9HnTa7X6Fva1buGUJ2bCobTW49fo8YzNjXLjwNvlqHjkoIz0rMTAzgCiJRKNRVldX+5LUw8PDOwKWsYkoZz4Rwec+SrnYoVwuMhGNImTatAotuo0uHp+JK6LQyHXQPCZGxyQ5GSGZCuDgYX19vd+V1xoajUyDQDrA6FOjhMfDrK9VaFWv4I65cGwb4faNJCkSkiJx8IlRwlEP19/JUFytYpk27VYL1aWiBG0mnohz8FiC/Qd3qq6Njo5y7eoy3/nqAvWNJu6wG8Utk1+q8N35EttrFT7zhWNsbW3tqaYbj8fZ2Nh430TPMm3OvbXO3DubNKsa3qjIs584wvhEepdQS7lcpt1u9wt425e3WX87w4JpUYrEcMkSXkXCsh3Wugbbsoi+WUP8wSqpZ1JoaLvEG+5QsPrHY1m89Ydvsfr2KrOPzWKLAmevLvHDtTKtehfBsuF24c6rSKTDHpJBN5mUj9p6HdGr9M2Q70e1Y+CYGrXtVVa1IF6vl3IRVuby1LcbOF2TuUaX8YvbPPnRqQfSqypFi9XL20RGQn0/LHk4QHmzztLNItaAl4dsktx3LXpFD8t2ECQBy7CwTfvnKtHbfyjJ/kNJLi39Ef/qX/0r/vzP/5zf/u3f3qVUDdBqtbh+/TpXrlxhdnaWT3/60zte/9iL+3ju+Z7v25Urm7uKrgBvvfUWnU6HF154AUEQqFaru5oCg4ODbG1tEYvFWF5e5vDhwzteD4VCfO973+MrX/lK3xR8/+QYqW2TjsuNdqOKtllGjO5MIuy6jiviY9sqE/EnelTf+yAIQp+ttPi1HyLaJpK3t8apMQ9OvkW53N71PlVVKRaLfPnLX2Z+fp5ms4nb7SaVSvXjOZfLxaFDh5ibm2NobILrmSbXbxYRgEP7EzwyHe+bqLfb7T07/QMDA+RyOdLpdP93t/INik2N6ZiPzpsZ9M06ylBgRzwqBVyEEl7ayyVyr84z8Px+FisdbuUbJJxesn3/d/6xj32MbDZLq9XadRx3sLa2toOqee/1uHed7HQ6vP322ziOw+DgIC5Xr5A4MzPzwM/eC4GAgiQ3+ZM/+WM+/elP4/P5mJmZ4ebNm0xPT+/Yj4pNje1qB4ChiLcvmnc/NostirfK+KKuPcWYBFnE51dZu5al+sIMYa/KK6+8wtmzZ4lGo1QLfvQlP66gi/UzI+zb/9Nj1fLQid7v/u7vIghCf1jyzs8/T/ClfMQPxLn4/RW6N0oMqRJiKkJmrYbjuNAki/XmNgec+A6qlmma2Lbdr6YGxsLoyxV+8N1Fxv5mgIMfnmTieBpBEMjVc7sebMuy+M53vsPExMSOitb96OgWl3IOb7zyJtZmC7+ockvcQBoJMno4yWS0i2maTE6Mkw65qVa7DA+mENxt3lmrIAyFiE4M0Nws03DpeL0eBFHErmk4bQPpcBRffRChliHiO7jnMfh8CorfjVfoVb07nS4+nw9JlrA6JqgStiTwJ9+YY+HCNrWNCrnMFh6flyuizupwmS/88j5MrcHS0hKKojA8PMxivkktZ5DId5GCKtJtzyYJcAwLb6ZJabnCWafGP/zMmT2PbWxsjLeWMnjVXifCKncxsi2k6MNVk/wBP7qu02i3Uco6nVvbpJ+YoaWZVFo6lbZB7eI2l65kKQRk0mFv3/pBMy2utXTs1QrDpk7o8ShDs2P9ABV6anABt4J52xT7L69sU2m0kUrLvPDCC3i9XhzH4e233+brX/86LpeL0cFRYuEYwXgQURL7XlCL317szWsFXYzFvAyG3dQ6Bm+89jrTB6YZCLlJHjvG5WvboKsMBPwog4EHUrAEUUAe9ONer9NeqnIt6cPWTCSv+lDzMIIsUi6W+bf/9t/hzxRJyocJn3m035HqVf0tBgQBd/Dhq3s/7VC8Cq6gi26125+ZvR8eVWIk6uFmtr53okdvjjPtGtihfNhpaLQrbeLRntFz22jTocO1d68xeuIwqxUd2wHHsBHv8zgzLQvP7c2wUe0wpDlkV2uUMiWmw9PUt+oIHYGbr91ERkbcEJk6PsXA8QGiU1HC4TBvvX0BWw+yvbFNNOZlejaOJPcMiCVJ5OiJIba2tggGD+P3++lUOmTOZSjNlwhbXoZHFWphN1rHZORAgiOnBlFdEvVaC5/HR2Wzgt2yUTwKg48MMnR6CPV2QSYYchOI+LGNnrgUAvh9fiyz1yWMpfw8+aEJzjw7ya2beWrVLnPXb3D8xBFSaRf+QM+3K5vNYhgGgUCgH+DcuFijttEkvT/et8QIJny0Kh2uv7bO1MEk0YTwQOrV+6kGAnzzz65z8bvLIIDikamsa/zp+jt88tdPcOjIFNvb29TrdSzLwu1294MlUzMpXC+QNSw2uhrJgHuHEbnfLVNq6ixi41zbwD3mZt8ze9O870VhrUBrvcWRJ4+g+FTOr5VpKlG83Q6+mIKz1cbjVXHoFQaWi01M22ZiwM98x6C8ViOe8u0QHbJtqHZ0DMvm9OQAN869yuaKAWYCs+RGEMAlSyjjfuyQi5uvrZFdqfALv3GKicndCYYguGnX26Tufc1xEFWZVq2DNOjDNN//2t8Px+l5+kmigGM7PeGQv4bdxs8yzpw5w9LSEk8++SRen59bG1U216rYtkMo4mH/TIy5a9eYn5/H7Xbj8XjI5/O7VBhlWezPdd2PS5cukc1m+dSnPtUXJNtLiMjlctFqtSgWixw5cmTX52QymX4CtbKywvT0NMJtBdXvzuUYOzVG661NKmtFlEkZ1aVi1TTspg6zIbo1H7M+gfB7CGXJssz0ZJqrVyrUCmXcAR9C2wa3SDiy+9zuqMP6/X7i8ThjY2N0u93btGOHgYEB3G43kiRx4OAh/u2fnyN3vo673Ctufu/iNvkXZ/mFR0fJ53cmcvdifHycy5cv91/XTZsrGzX8bgUr18LYrKMM+vdsOgiyiHciirHdpDifQUkE+d75Bf7hJ44S9O0uUCUSCb78N/87/j//5i/4r//lCsl0gDNnRnEpErqus7q6yujoKHUDFnINbKdXvEyH3Lv8jTOZDFNTU327iWazSS6X47XXXiOTyfD8888/0Gf0XqysrGDbNuVymbW1Nb7whS8gir2Yc2FhgXA4TCyR4vsXN7n85gad1ToI4BkPcfLJMZ47lt51bN22ga2ZuP2uB4rdqG6FRttEMyyKxSKyLPODH/wAURT50NOfZPjoNN6Qm+GR8Puew/+ZeOhE79d//dff8+efBwiCwODjw/zV62sIlS4en4rQtBgKJtCjNhVvl8RYelfgWylXSNxTPRVUiVDCR+5WkUtLJT56bBBv7LZgQGmnupLjOLzyyitEo9EdqpHteyryAKZl863zm7zzp9fwZuvE01EEj4xj2OjLVZbXK7wVaTCbSJFIJMjlcv3OwFDYw0zSz5XNGuOPDOB+10LKN6nOZxERCQyEcR9LsSC3GRsaYF/CZnl5eU/F1clkgMi+OLW3MsQCITwhL81mE6tjYBRN5INxLr26TOlyjoBHwXYaKEGL2dlhjEqXlVs1/qh7gV/+myeYmor3FeLOzpVwsnXQ7X6S17+eioToV3Bt1SimY9Q6xp4dx/TwKKtXrzA72wsUrFIHp2siDjy80qOqqhi6Tt2oESn3NjePKpGpWuS26mxdzVFWRZJBTz/Js22HzUoHw7bRPQJmsUNKiexI8u6FLIkMR7yUmhrfvLzO5x7d16/u36k0Hj14lLlX5tj6bpaFxgrusEzsSIzIRITERIKhR4fYfHsTs2vijXtRJJGoR2Ht1nUKmyt88Ze/iFYxCMRG8YZF5LDnfedsBFFASngIVDWymzUQBUzdQgYUWcHQjb6Zdu/EHTrdDpqmYdV1JFWmXC6QHPTx3P6TrLRtMtU2bllCMy2CpsNI0k94MvzQ38dPOwRBIDAQoLH13nM/IxEvS4UWHd26q5B6DwYHB0GAbqdLebtKM9Omvt1AsByur9TID+VxBTVacouoJ4pq9+i5tbZOwHYQ7wlm7t3Eyut15PUqHo9Kpdlg4vEJBo736EgBO4B6XWV6epqpmSlauRZXv36VmquGOJTm0msZ7OZt+weXTHomyie/cBRNLzMwMEC5XMblcvWDQE/Ew9THpkgeTlJZrNBxOkwOqWysbZCOuNC2mmg0qVaqRJNR6p06+57cR2w6hi/l21FYDIc9zJ4e4vw3bxH2BPGGXVRKVeqZNoEBH4duC/rIssi+/UnOv3uL8ckUswcSBAKuPkXzTlGt0WiwtrZGs2WwfHmbQNK3y/fQF/FQ325y7rV5/ru/96EHfpeqqqLr+gPtVDbWqlw7u44n7CKUuh0gj8D2zSJvfHeR/QeTDAwMcP36dQRB2DGPW9+sU91ukHEsfKqyI8nrXxuPxNJ2mdFwEGPL2KF0uRdKpRK5mzl8Lh+ugIvNcpvlYpvBeBC3ImGrHlolk26jBYqERC9w2Kx0ODwUYnY8wsp8kWKmBkkfsij2ZkZth6BH5thwhIm4D9+pR3jjjYvoJQW3R8YXcmOVuriHgshxL/6Il+zNAq/+xU3G/sHjuwL/aNyL6lVoVbv4b4sblZs1qoUyzVCDQEHD8iXeM3DfCy3dxK1I+F0yRrGNL+lDfh8Vvw8qHn/8cU6fPs3qdp3f/l+/j7Fl4jSMXjbskvlmXGL0kI+h4WF+5ctf5tChQw+8z1dWVnYJj8zPzzM/P89nPvOZ91VYtCyLaq1GV/Lz6rkNoCfiMRb30Wq1aDQaxGIxVldXdzDMDg6GuLZVJyN1GX5iGOOig7HdoG2YBAbCeE4McM2u42nafO65R1hcXHzPjtJjZ0ZYmy/QuFWima3QsU1iJ+KMDNw970pT4/LFLV55+W2Gh9JcvJZldKhHQ7+T8EFPJK7b7eLxeNCUAJUVg0ili3c0hONAe6PG3FsbPLovgdZqPdCSwev10ul0+j/nGl3yzS6DYQ/GXAlEYZdw170QFAlBEnGVbSruErrso6ZBcI9QyLIdvvtXy1hrPubXl5gTBWrVDs88PUCtVkMMp/nWjRJL+SZN3UQAFElkKOzh6HCIQ4MhFEngxo0bDA4O7mBq+f1+BEHg7NmzBINBBEFgdXW1//peVhbQSz7/4T/8h2QyGR577LH+72VZ5sCBAxSLRf7oW+dYfr2Gv9IlGfWC49C8VuD1rSZuVeLpwzuvrdenIrkVRLvXebx3vhnA0A0qxSpm0sPy4i28cq95cODAAU6dOsVv/MZv/NQ2v34+V7O/BjabOmVZILgvhhp2gwNul4TtlUhIPTfwSrlM5HZ1uNFo4Pf7dvmhyWEX3nKbq5e2eXx/Er9LplgsEovtlKA/e/Ysoihy5szOLlWpVNrxtyvFFhe/v4h3u07y0OCOh1yKuJEur6DnukQePcbCwgK/9Eu/1H9dFAUem4xxK9fk1WwNcUBlZGCApCrjkhyyTgfDL1DNN/hbL5zmxGiUb33rW311qnvhUSU+/Pw0f1nukFss4xFFBBzapo2eUujWinTXLAbHIzTNNlpLJxgJ44n48URACSpsvrnGd1I+vvS5I32asH/LodQp0Nbb6E2xN1x9zzUV3DJapozXPYpu3p396egWq6UWLc2krSkU6h32Ob3XHdP6sf2S6rU6LreL5FCKblejmC8QTyYQcGjVulTLHSy3tEMQoNzSKTY0JEvD7XPjNC3stvEe/6WHmN+FbkV4fbnCzECIsFdlcXGR6elpzoyeoSwavL5cRHR7iNQ0VKfD0OgQxVIRK22hzqrUbtVo3zYgbnabyLpMs9zk7F+c5fDTj2NNJwhuNZEiD9dFE30qSqGDVNFw0n6at8q4Uj68Xi/1eh1waLXbveBSAFV1IYgies1g6OgECU7yxc9+ioSTIPLyMmtbdZpdA58oMJIKcODZcQKDH5D5vNvwJrzYlv2eAXfMpzIZ9zGfrSMKrt2m6bffplVN8hdzmG0LyyuTjHtRRIH1Gzlkl8Sh02eIdESMtsF00sX8ZpWqbhKSBJTbojCdTgfV4yW7XEZYrTES8SK7TDzuMN2QmzeXStQ6Bpbt0I5MIUVHaNsOodEQ21e2OffNa2j2BuGDwyRnIkiKRLepsXE1xx83z/GZXzsImFQqlV1zHYIoEBwKEhwKYqdt4v444ZthJEei3WwzkB7A3/QzcWAC2S9TqBTwD+zt2fTRT+6j3dBYuZTtJb2SSHwiwqHHQ6yuznP06FFWlsp870+vszmfw7Hg8qtFTj43wfiMguM4/Y05EOgJl2ysVWjV27jDrj098iSXRLOqP9A4GXqqew+idgJsrFfRGhrR4Z1TacGUj+J6ja1MFU0v9wUb7ih5RiIRjLZBV7foWA5hz+7tW9M0dF0nFAjQkSX0po6pmQ/0ds1mswiWwNq7a8RjcRzHYa3URoA+fUz0u1ASXoRcGynoRgBUyyJf77CRrzAadjOR9GCJMmbMh2475LYy7B8fZCod7d/LQ8NDpOINNrfK+NJurKqGFFaRwr1rLEoCoaEg2wslVlcqTE7t3AtHxyKMH02y8k6eZrUFMlTzXZpGC6XewN8QEf1JDNPeMwF+EBodg6mEv9cNbRkfuPXnx8HExASZYpMrX99Cm2uheB1i0zFERUava+SXcqw34VN/4x9w4sTeqo/Q69DZtr2jK7K2tsb58+d56aWX3ncmy3Ecrl27RkmP8fY3bmKXNRAE3hzwcebT+3Fqtzh9+jRnz57lyJEjO5LGiFfh9ESEPzmf4Q3dQJoJMeMPElQEslqLpmjSLdV5aszHwbEBMhmrp+q7B00VYDQZ4It/6xEuvpuh2tAYGgwyNqKytryA3mliSSG+8Z8us3FlA9mBjaUs2YtVxh4d5tEnwzso0ncSt3a7zeXra9SzFcJuGUGVEACXV6Fe01nbynN09L2pf7FYrJ9IaoaNYTmokohW6yLssTbcD10wsfI19j1zjJvbNW4tr5Dyz+xK3AuVDhuXs0Tivp7n7nqNC6/Oc+RYgBJBXr2QQTMtEgE36bAbQRDoGhbb9S6rl1usFpuMCGUO7pvZRWlvNBp4vV5GR0eZm5vD6/X2E0HHcfpWFo7T67QnEgm8Xi/RaJRoNEo2m93z3DyBMOWchKfcITgdRbjdvQtF3FhLFS6+s8kjs4kdRdWhmJfkwRj51zfxuFRs26bdamNZvflCyRHpag7jB6JMDg/0Zzv/6T/9p0Sj0Z/aJA/+molepVLhj/7oj1heXqZSqeyirAiCwO/8zu/8tQ7wpw2bmTpOpo57MIAU3NtTwx8IUC6VCIfCaJpGIB7f9TeCJOL1KFQWymRrHaaTASqVCjMzM2xvbxMIBLhx4waNRqPPY7+D119/nWw2y3PPPdcPVt6d26K7XGBwIrGrkqPrOhWpQ7glsrbS5qlDd/nitu1wfr3Ca3M5WisVwlsN6i2dG6rE1ZibYNzLvoEYcUnHa2U5PhJBkiTOnDnDa6+9xvPPP7/rBj88GsH36yd599IW60tlAGbHwkxMRfj+f7yMpdbo0vMHlGSZaOzuAusJe/H4qqxeyLL14SmGbiuxBVwKQtiHt2Yhuz3UG70OicfjQVEVjHoXfC5cbgW3ImLbDu+ulXl3rUIh34K2QddxKJhuzi1mObNvCOXHeDAd26ZWqxEMBBFlCavdxeNx4wmHyOVyGKaKIAi9GRHznvc5sF1uYuoaibCfWsdA07oUSyUkv/6e/7NXGZNYr5u8u6Dw+FSCr33ta6TDaWLFNPrgIIYq41MlNto6rvUq6ZUaE8/2ePITkxM0y02WLyxTWaqwkdnAFmysoEXq6RQDn5jEfnUFaaP5vt28HddCFJAth+S+GNtLFWobRQjI1BsNJClKKBii2+2g6TqWbeGT3LRUnUOnR/iFjz7b3/y9cS/Tq1U6lQ6uoIvIRAR/2v9TvWD+JAgOB/GEPXQrXTyxvW0WBFHgyHAI07ZZyrfwqBJBj9LvCkNP7TB7PYfesRBjHhSjQ7NawPS4CaX9aHWLjVsNRp8eo3qzBG2DcY9KaUSh6pLIVHsBfKdr4G+08efaDKQCuGSLetWhEpNolJsIoohb6fmjuQIRzq9XuJlrMBbzUshl6IgulKJJwKHvVef2u0jNxtm4tsXWepfkoPO+wZzskQkMBojYEYaHh7Ftm/n5edSg2hfj8WpeqtXqnn5wPp/KF3/jEVZXyuSzTdwemZl9CTwehVKpxF/+5fe5+bpOebNGejqJZnbRmgZnvzaH9MuHcLtzuyrmoYiHQNiPQ29dbTQaCILQ89QUBVr1NvseH9p1LACdjkGnreMPuN5Tlfpup58dKpSW6WDZJtVqkSNH73ZCxsbGKJfLrK+v46JnHSAA9n37bbPZRJZkAoEAzVqXVqtJpWKxtLTK2nqTVsPAF1CYmgnjcstsb2/j9/tRbZXl+WVuiDdIF8bJEd9JIRZBHQ1h1zXsho4UUBEliaDPQ8cGXyBA127TabUZdQcRFZHWUo7Nmzn2D310xzFamoCoSDhab3ZfHQ3t2K98YTf1TINysbUj0et0OhSLRZ58foi2UaOdldHaGmOPDbPQXmQwFOWpx04yV3YoNjVSD6BJ7/rOdAtRFBiJejE6BpIqERr92VLU+2+Nsz9apX6zyMBMDMktU6tW8fsDNM0WQ8dGqK9Uefs7ixw5lCL+AJr9/Z5x2WyWs2fP8uKLL+7qlLTb7V0F4+vXr+NLjHLhT8/jrRoExsIA1Ndq/OBPrnDqwwFs2yYUCu3o/FbbOq/czLOwXMbZqOGua5R0k7eiNp6gwrGxOKciEm8tvMrhE08DMDQ0xNzcHKFQ6IEFnKGYj6GP7+xOTgwPcPnqdb7+n8/DloUnKhBLxXG7XJhNg+XX1hAUnf3TE7u6016vlwPTY5xLl6hf20ZVTCRZwW4ZqOMhJKu76zrdj/HxcRYWFojH4z2/X25rfvWGTt8TjXodgV6siiCgyDLjY2Nks1m8Xu+O5FSWRURFwtQsLNOkVqnjHQmzXtX57vwWbkUk5lWwOgaVu01G/IDoWLx8qcgjI0GSsSIulwtVVXG5XLhcLhYWFnj33Xc5efIk7bqf//X/8X0OPzrKJ37hIIIgEIvF+g0N27YpFAp9kR5ZltH1vWOockunkakT8bv6SR7cpq16ZepbDcptnSH17n4siQL7DoVZubRBc76INawTTEVxHJtmoU5pu0Fof4IXXjhG6h4rigf5Pf404SdO9L7zne/wuc99jlar1a823o8PWsAGsJmpIms24nuIdyiKQigUYmV1Zddwqt0yMMttrHIXq6HhtHTmvn4D75lhJHdvkfmTP/kTVlZWOHr0KF/60pd2LT6FQoErV66wvr7OoUOHOHnyJDdubBBTPXseVy6bAxFCXi9oIpoj9RPEd1bLfPftddQbJdI1jbQgYDkSrYpOs9al5jgkZxM8k3LRSZ1gaWkJr9fLyMgIg4ODXL9+fdeQNMBE0s/E87N0bnu1eVSJ197ZQCu0SY8nWd/eoNlsMTW1m/7pTXipZZsszBcZOtPbAPYNBLga8+JstXBaBsFQr53fabVp15o0NgqIT84ymw7id8n86FaBH1zL4l6vE8u3ETQLRxDI19pslzUuen0cdgDb2VHZ3wuGbtDutAmHwv0uoq2ZyEEfiqoSTyTJLG9jiB0SqQCupRJt3cKrSGTyJZq6SSTox3ZAMGyisSCpqSHk+HsLNji2jWlZhK0m17cbjPocms0m85vzqGtZhKPHSfhUZEmgY1i0bYtOsbPjM/xRP0c/ehQ+CmeMM3yq/il+93d/l1PPn2K7VqKraz31xfexAOjDdtC6HepNhxHFjXvGR/NajZTfgxiW0HQdwzTxeDyEw2HspkFhvYrvUJzHHx3ZUeH1D/gf2K35IMEVdBGbiZF5N/PARA96dJdTo1GCboWVUpt8vYsgCP3EoJap061oeJNewgEXHsnD6toqrXYLn89PMOGjmqlTbOkMHU2Ru5zDKLY58vQYrrEQhaZGsVwl5Eth3qqAT8U0daoNm1zQhRX3kAi4ke+fT3JUmrrJje06ipogFjSQ/DZWro0V7yDdPifD0pFlmbkrqzz+1HNsbm7+WNdJFMW+ENPKygrJZJJ4PM7KygrBYPCBM3HjE1HGJ3ZW42OxGEHPGPmVt0jsi+AJuLBaBuFRP/mlMtfezjA8sbsjEQy6mT6R5uJ3FvBHvQQCARzbodVu0Sy2cUSbgyd2JnrVaoezLy9x68IWRsfEE3AxuN9P9NNJ/P7dgfDM/jhn414qG1XiE7190zIsyhtlxk6ldiR5dxCNRgkEAtxcv4kqOQQlkXrXxKVI2LbdY474/EiyhG07OLZD2u1Hjfh55c8zFFcqODYIIixNRjj5XIKjxw73VEKLbSRRQlZlOl0dUzaR7usIiD4FZSyEdquM3TYQvQqiAJbdKxaqLhUJiVg01kv02i3KpTI//OEPOXLkCIFgAJfL1VNSbegYmo2S9vVEES27H4xZptNfh7a3tzGMHvPB4/EwPDyMIAhYloYkqmSXCgzGEyzGoHmjQSwY5qDb4u3lEsWGRjzw3ubGXcOi0tLYlw6SCrioLFeITEY+OIq/PwHytS6rl7IEgm6k2xZRoXCYXC6Hx+1BlESCQ0G216tcu57j2Sf27urV63XGx8eBnpjQyy+/zEc/+tE9u2alUmlHweXGjRtMTk5yfr6ClmsRHQr27aqCQ0HWr6/i+uQU3W6X6elpisUio6Oj1DoGf3Yhw8rbG4S22wzpVs9X07LptFpshQSCswMMuTTGB6J9kZexsTFmZ2e5devWe2og3A9BEJDUJGJtkZpdYDI9habrSF4ZKSzjr2rkFposrG6wb3L3dRoKeTj85AiXahpOsY1pdqkFBCbSFhOpvefUGl2D9XIbx4F0yH2bRXNb0EsR6RoWctKHdqMAe+01tkO1VsXn90O1g5z00TUsVFnEo0gMJkapVqt9JWVRFIkFXRx+dpy3/2yO0uUq/lSYM584wHoHAoEg4/EHj75EgUAoTLFrogTjhNwiuq7TarWoVCpsbGxQLBb59re/jdg5QP1mmzlH5CMvzu7y0xTFnQqppmmytLTEjRs3egV/RSGZTKIovQKp6JaxiruF3WzTRnSJyGKvkJfP57Htnv/w8X0J3H/7CX70jZtk5zap5boIoogS9JA4PcLHf+EgY6mfvfXhJ070/vE//scMDAzw9a9/fc9B2Q8qGk0DSRTel/Kn6zqxeJxKpUosGsXumugbNaxyF71toAngWA7tlsHatRzWSpax/cMsZhe5OXeTXCHXEzDodPq0hFa+RWWlgmvThb6k87G/+zFePf9qj988+ylMob2rktNsNGk2G7jcbjySG0NV+vLotuzh9Zt5XHMlfHUNefCuQpPbcfBu1Yhtt9laKPNGuc7nnjvdf+/169cZHR3lzTffZGho6IEDtPe2xjttA8F0MByTdqtNMBi83QXeeS0D4SCFlU26nbv0xqmEn7HpKGuVDvHNJnatjuCVkXULujb6eBQ5IRK0asxnXLy+UCC4VEXZbCCFXYgRN45pEy816d4s04qEWB0MMulXsJv6rrm/u8fcxrpdObwDx3ZAs1BvV37buslAPMLIUJRMskhy0WGl2kKwuliSmzu5TaOjE2mbhI/EkG7PmJiVLkamjqhKKONhxHuulyCKKKLIcDzEZqWN4Ivy5JNPcurQKRb/Yo23NysUGl08qoxhWgQc8CUfvOgqikIsFmNqaopAIEAwFeIHN5vUjRzKtoEAyJqALEiIiogYciH6e1LO7dt0TBwHRRQJJqOcPDhN6jEXf/gH51g5n8Vr2ngCCoFEBLthU10r0QKCh5O89DcOMRx9eMn5DxqiM1Hy1/J0aw8WZYGe9+H+dJDJuI+NTJ3sZp1O10SwbKSyhmzbJINubKm30SmSTDwWQ5ZEREkAQaBWarP/cIpOudNT/DQtWvMlolE3km5BpUZxsYzoFxHDbkphGSfiIfUAhgIC+F0yLkkka9qYep6gWwHHwcg2kSIeEMA0DHTDIBaLvWfh5PKFLTZXK9QbNT70kd3zF3fU2/L5PMVikZGREdbW1h6oNvwglPNtFEXGrarUG3Vcau/8PGE3jWIbyxD2nKV75uPT5DbrbM/lcflVJJdMt67R7nY5+tw4Xn+HTCZDOp2m1TT44985z8aVHN6YB5dPoVXrcvW7FcrZFl/5+8/sCliiMR9PvrSfH319jszVHKIk0ul0SU7F+OTnT/IgKIrCoScO8ebcm4SWa5RUhUqjjYxFKBgCoccgyDW6RN0yYVHi8nyRYrFNYiaG4pIwuibZW0UuyQKnT/eCWlEWmZqZYnh6GEuV+d5cDtNyuH9MTUn5ezPfK1Ucy8FUe8GSJAqYt71BBEnANE0OHz6MKIpMTkyC0KNo5ZZzGMtFzLUqejqAU5WwrhaQgipK2o8VlKhkG+ABX9AiHk/39z7bttne3u6xU1bq1DISq7eq/Ki0gUt14a3adP/8Jgc+NM6p8QgX1qpsVdqEvCo+Vd6xxRimTa1jYFg2M8kgR4ZCtEttFK/C8GPDPzad/4OEUqlNt9whnLi7jzi2gyz1rqFhGCgeBcmC3AP8BiuVSj8eaDQafPvb3+bpp5/ec97s1Vdf7SdqjzzyCEtLSwwPD+P1epGkWu+etu8GNbVKBZfHRbfbZWhoH91ul7m5OUZHRzm/VmH5/CaJ9QZy0IU40JvrdRwHpa7hZCpsXtqikWgyFAyyf/9+LMtibW2t38W637vv/VArd9GbXSIDUbpaF9O0+veaJ+SiVtOp1vZWkRZFgRdODjMY9zF3q4goCMxOhmlsXMc0TVZXVwkEAv2O1tx2ne+9vkr19vPnG/QTj6nUajVSgZ6P8FqpzfBwAG3hbkHmDizTpNFoEAqFcDQLSxJRhoNsNTRGIh4Gbndnw+EwgUCgX2wLBAIcPugmGDqIYLtIDPhxhT28fm6NdGhnMml3DBzdRgrd3UtCHoXtWoeFfItnZhM7mB6VSoXBwUFOnDjB/I0ib768yOzR9K41cy/IsszBgwfpdDqMjY2h6zq5XA7TNDFtB/+ASm3JwNXQ+vGdUW1TaHSIRkNUs+s4wSATE3c7rt1ul4FAlY9/dpgbhzzIYpB6vcaho2Mc3pfE9zM6u/sTH/Xi4iL//J//85+rJA9uKwS+DxzbodlskUgmsC2L4kYWbwG61S5NWaApg2WBYFsYAiw4NmUEjK0GnXPXKV0ocfxTx/no8x/tC67kruZY+uEaW9kGTVMgaE2y8M0VVhdWmXpmCkNp0XJJWDUN+U4lx3bI5rLYlk3UH8JqioRSfiaHkhSLRUpihNpqlURNQx4O7GxxCwJKwouzUUfKNLhhtvH5e5WMYDDIoUOHWF9fJ5VKcfbsWT71qU/1H5ZiU6PeMTBtB1kUiHhVIj4VVZVwRNjezAAOwyPDuF0u1m7PoNyhgd1pMCn3JD0eVeKTR9J8w3bYDBRRi12Ulo7tU8iIEJ9N8NIj4xwfi/FfXruJnmkQ2m4hDfj7dgGCJOIfiyCV6vjWqxSSfoZTXpSV+p6JXqPRQJUVPP6dCYpd0xBDLpR0bzOstHQGwx6Gk1FSnzjJ1uK3CMzVseIx1roGbc1C7lrEuhbpyQi+YwMIooDdNWm/ncHMtUAET9vEc3y3d5Eq98QNTEQ+8YlPANA93qVaarHc1OgaOuM2zEzHie/fTRO+H/F4nGKxyMGDSfZPJ5m7UcV9KYdm2lRaGrppgmUhK+BNefHvSxIKhRBEAbPcoR208Q8HEJsFml34/C8dZOWxKa5c3GLr8gZavYvp2IQOxHjsxCAnj6cZCD24k/XzgMBggNTxFBtvbKD6VMQHzBDZpk270Ka+WccutIhqZi9pEsCpaXQqGvpKFUO1KWl13EEvoVAI0zTRNA3H6XVzSgslKssVgiO9Tli33aVRbRBIBlheWybfzHPiqRPU/UFa23WG3qf7AaDIImG/i+2gSrvYxjMQwKx0sVsabcFA61q4PC4mZh9MZXnt1WV+8LVrmB0T27LJLbV56qVhhodvB5H3zNokk0ksy+r7Wd2ZR3lY2GhIkozb7cFoNOh02rjcLrSmjiugMDyaJpfLMTIysuN9sZiPL/xfHuH8WxvcfCdDt20wdjRFYlRm9lCEiYkJTNMkk8lw7vUMG1dzpGZjKLe7Dr6Ih07DzdqlPDeu5Tl2crc8+hNPj5MeDjJ3aYvr1xZ4+tGTnHh0lMgeSn73QpRE9j+3H7N2ndzKJrVgCElRMdo6lu2gmxYRt8KMI2L5FCrLXeKTEZTba6DilolPRiis1FhaLDK7P9lLcCaHMVoG/oCLVMjFaqm1O6gRQB0OIigSxloNLdciORpGFAWMjoEn4kGURSRBYmZmhmKh2E+azIJJ5VwF2ZHQBwOUcXBMHdGw8TebBPI15HQQRxA4+cl9HDjQS+qbzSblchlBEEilUqyc2+bSK2U2yw08A2EY8CL4fJQUgdL1IuU/nuP0p/fx5HSMpUKTTLVLtd1GFAUEBCzbRhJFYj6ViYSP8agPvdZFq2mMPzf+c8EweBjcywquVqtEwmFEWaJRr99mfzyYG7i5uUmhUKDT6XDx4kVOnTq1p7k39Ci5CwsLfPe73+Wdd97hi1/8Yp+yOD0e4bXRMJXFCuF0ANu2yK2W8B1J8+SjPYEit9uNpmk0NZOrq2X82TaSV0EK300mBEFACrmRqjK+7TZX2xWOnekdjyRJTE5O9goRuRyNRoNoNPpAgZn74fbKNLsdBgOjqH4X21tbfbVRrW0gexRCIe8DveLcisSjU3Eeneqta+VymZoygaqqDA0N0Wg0WF1dpda1+d6FKtq5LFGrZyfTzjRYng7wWnCBTz79CEeGQizkmjhxL67pCN0bReS4F9GnoHU1NE0jHIn0WGXFNu79cYi50Yptjg73nuM7kCSJqakp1tfXuXjxIo8++ijjY3eP/9xKGcOy+3O8AFZNo/X6BnbXxHtiAPUeuxW/S2a11OKZ+9wy72WDHToywKEjD59kQ4/pcPXqVcbGxnZZOXxUCvDVzTqLc1souoEkSpgeF5ETQ/zip44zlbzbmbsjkuN2uxkbG0MQBJJhP7FYjG63i2VZP7NJHvw1Er2ZmRkajfdWkvsgIuh3UbhNjXlQ5a9SKROJ3u5w6Q6ekkNpu0Ir4MGwHVRZwuUSEB1oqeBXQXF7uN7RiEQi/OKTn+fRZ04ydnoMQRRobDVYeHWFq/kmOcEBRcKZGuNKpcbpkSf5yEefph338gf5S7RvlPF7FUSPTLlaodvt4vN4cbdlSmGVZ44OMJCIcWFjjTVRRal0EWRxR5J3B4IoQkBBzjcwE14aXYOw9+4CODo6yuDgIPl8nr/4q29y6LEPcX2rzq3tOu1qF8fq+RL5ox72p0OEwm501aKVbRIbj+PxeHBsh0gkQrPVxO1y43K76JY6CAGFcHTnMSWDbr7w6CjzoxGuZWrUuwaCY/GY0OEjp/bTyG/iODHKpoKvZfQGeO/zhAt7ZJSED7PQxKprdAcCqLk2ZrF9l0ppO1TrNfx+/y6JXbtrYtU1PCcGED0Klu3QMSyODIUQBFjeWObkLx1j5niDpfNbsGlS02wScR+hExHcs7F+tcvpmlgNDXnAh1XTMN/HP866p7I5/Ngwik9heK6A0TYJDgUYODbwnh29O4jH4xQKBQRB4EDcx7Vyi8JmlYZLxPIrSG4VQZKQTBspb6LKVewpG2ywc22ywz5O+i0OzU72r8/4IDx+fJAL8zH8viDZlSyjyQRTU3HUH1MF74OK9Mk0jUyD2nqNyOTuDrjZMclfz1PfrCOIApZbpibItHULy7HpBFUMl4Rp2ZjFDlJbwzEl9IiOy+NC6+o9c/R8h8WbJay4lw3bQm9buFwibs3CYwmUsiX0kM6t7WX0xD58LuXhaLuA360gJX04LZNavoUb6FY6lLttnJbD7OOjHDjcExFwu907AhzTtHnn5SVEUWDwUBLbcti6nufGJRePnN5PLrfbWkaSJMbGxmg2m1y6dInDhw/vOa93P2zbZnjSx3LCT2GlgismEwwG2V7LY9UdDj0zSC633e8Y3o9w2MNHXpjlIy/MYts2uVyOWCyGYRhsb2+TTqcZGRnhWxtrqF6ln+TdgSfgwrFh4Xp2z0QPIJ5QOXDcx5FHHtnzGB6EwFgA7wEPjytjrG8UaSkyuiAhyxJxWSYiiQzMxmiEXViXt3Y9fy6PiqXVaLd6jAlJlYgfiLPy/RV8KR+jUS8b5TYtzdwz2VMGfLQUAXVDxO9AfaOO1tKITEZwrF5nz3EcjLZBM9ekkWlQuFGgrghsjPsxRzxISzXsmgGqRB0RraXjLRcY+PAwhx8JsbGxgW3b+Hw+1tfXeeqpp8jeLPLKn86Rs20iowHCAR+NchWvZuELuOkeSFC6nOXcn8zx5N84zKMTMeodg2xdo6Ob2I6DS+4ZvicDLgQc6hs1BEFg7JkxBo79eEHmBxHJpA9P0kuz0CYUUNE0DVmWEG8XYQPBIPVCBQ2boT0k5HVd7yuF37p1iy9/+cs7VGPvx8jICG+99RbRaJRarcYbb7zBSy+91DuWoJsPf/Ygr37jJvm1GrV6HXPcyy/86qPEAzuTpny9SzXbJNqxkNJ7J+tCSEXINdCw8Md32hbcEWPa3NzklVde4eMf//gDqeL3wrGLxCajVDcbJGZVfD4/AgK1UpVGWWPkqRSKo7O0tMShQ4fe9/PuCFhtbm7Sbrf7x3VupUTt5iIpB9TxMABivkVnvc31eJdPAtNJPyPRXldv4kgCBAFtqUJjvYTkUfB63ehrNQSXjPtgAtfhOCulNiNRL9PJ3desXC5jWRaPPPIIm5ubjI6O9hNgc48ZZKuuYZbaOIaDWe7sSPQkUcCwHjy3/JNCluVdXt31ep18Po/LcfjIC8OsHklT3O5gGAayV+PMqSRSu8TWVqM/45dMJnd1clOpFJubm4yMjLCxsfFQtg8/rfiJE71/9s/+GX/v7/09vvSlL/W52D8PGB4JMu+WsBsa0h4ULE3TECWpHwAbuSZ6WaMd9GDa4HXJCLejKqdj4vhlFAXiQQ+GaZOrQygeIHspS3Q6SnAoSGWpQibXJCvYpAIeZEnAtmEL0G2B8kKZmZk4jz03yesNHTNTxy0JFLNbOO0OkeE0Ja/M9IfHOT4eRZJ6cx2mYyNaDjzAM0gUBBAFOq0WQV9qR6JxB7Is8+SzH+H//Yff5ntffZOw6SZWNYi3jV7lTxToehXejVcg6WFd7hLoCMTDPa6+aRrIsozP56PV7F2rZqHL5JNjVHNL3FI0UqlUnzrpd8mcGotwaiyCadncmLvOwYNHkSSJiHuUlbU1bMdBvJ1k3g+XIjEQC7CWq2NpOpvtEsmYgTNXhSzYIYl2t4Pf56Pb6SVeuu2gWQJCx0RqmHhnQuhhg2ahQLZhEPGpuLplvv/9C8zOzhIKhUgNpxg6MUTmu9cwdBepyfguU3bRr6IMBjBWa6BKqKO7aWz3QrknGRdlkfTxNKkjKWzT/rHkwOPxOOfOnWNgYIDafAWz1GR70EuyaSEj47iVnnWC7FAXBVwlHdwtHMemnPAQPRznQMrL9vY2qVQKRVH4gz/4IPd/SwABAABJREFUA1566SVOTQzz1X/3GpnFFm93bpEcjfLxzx1i4vDuTuXPGxSPwvATwyx+e5H6Zp3g8N3v2+yYZC9maWQb2CGV7aZOtdhEs2wUSUQALFmgq4ho1S6CR8AbCuJ3XIhVA1tVaBc6uE2H2mqRdjpIO+XGJfTum6Zh0BRFKtUWrZzBzOgUA0f3cXa1Quohunl3IAj8f9n77yBL8vu6E/2kz7ze3/K2q73vmR6PmQEGAEkAgiCAFAWIXHFlKbN6uxGP8aSN0NtYLWNXodBqRYkrBfVIQhQlQSRBggAIEAAHGAA93vS0767u8uZ679O+P27Vna6uajNDYAUScyImqufWzbxZeTN/+TXnew6huA9fQEPKtajOl6hey6LPxRl5JMKnfv7UgHYTi8V2JG+9no3ds1G3ikWiJCCpEr1uPwDYy0trG4FAgCeffJJXXnmFkZERxsbG7hmIra2tcfaRQ8hikO9+6TrFxTItrYPiV0mfNHjqg7PEYmEUReHKlSsEg0FGRkb29E4SRZFer4eqqqiqSqvVol6vEwqF6HXNXTYM29B1ldXlTdbXd6vnlUolLMsinU73lS8FgfW1BuViB1wIRlSmZiKoqoIsy0hbz5Rms0mz2eTMT52hlW0xfrPM6oVVWrUW8VgcX8JH8nCS6EyUTKGJ5ldpljsDOwKAZqWDFtRI3BbcRaejbIY26dV7DId0Dg6FuJKp0bNdwreJAtmOR7Vt4igiJ5+eYkJTKF4rIpUkVEOltlJ7pwhqQc2qYYkWZlhkLaIR8htM+DV6Y3GqmQatbBOrZ9MRFWaHQxwcC2CbXURRRZIkFhYW+O53v0uxWCRQGafQ6JEcDtLNVGjdbGI1OnREZasFJWAEVGrrDS588SojB5NE90WZHg2iGP2/1XVcrJZFc6OO1bEIj4YZfXSUyFTkz6WmwLtFPKCx7+QwF/7wOv6WSaPT2NFF91wPq+Kipg3GRnd3vZaWlpBlmevXr2MYBpcvX+bDH/7wLrEVgEK+ydsvtjBrY0wehOc+9Az79u3b8Z6H9ieZ/cUwVxeyvPjSOT79sQ+ieDsLorqu0+n2+ira7t3jmX4s00aKScTiezMDxsbGMAyDc+fOcfDgwYHf217odruUCxk+8XOP8M3/dJHM1TwuNrqm0+5aaOM6H//kCYaTQc6fP8/i4iI+n++u1NBarTaIc8bGxrh16xYzMzN9fzjXQ3RAuK1wLWgSoulQqjR4++23MQyDAz6bjWydNxcrDKdk6t0uRlhD7UITEyGuIg75aAVcNpc2SfpVTsR0KoUcjdvWmUwmg9/vZ3R0tO8luG8fq6urA4qrJovcoQWFnPSh7Yvhti3UyZ2CRqbt/tA6YpZlsbKyMkjaQqEQk1PTvPzqGreuVtF9Mj/53D6mxsMUMhvouk673e4zttS+iF4+n99h5bDd3VUUZUD//bOM93zmn3/+eZLJJIcOHeLDH/4w4+Pju0RDBEHgX/2rf/WnPsgfJUyMhJEnwvRW6vhuS/SazSbZbJaA38/QUL9a5PYcnHybtgw9B/y3JXm4HrbjIMQChP39h7AiiySCGptdm7QLlVsVQqMhWoUWTTxkSRwIJYgi+FSZatemXWyD5/HskSH8mszrb25w67VbVFyH5P5JhKPDpCMWn3pyenCzeZ5H1K8y75PwijsrItsQBBGnZdJwTaZSEXx78Kbbps1XLmwieCn2Z+t0M1l6qogxHAdZBNdFr/XQSx1y11q0dQHfTJL8Yo34sIureSiagud6SF2RynodRnU+/BMHsFs59u/fTy6XI5frzyzeXvF37D7Va/u60zSNSCiI4mapGzKa5e4ptJKWZTb8KiXR4+zkJOnjOuZ0ndprq/RKTVLRYaSQRtdxydY6tLJNKHbxJOgO+TGjARJdibgggCgx4+uxsjDPzMwMzWaTWq2Gbds4joMRFbl5K0/7VoFAcHfVzIu7/QeT7FFySwhbKqV9EQ6xT7tzoGlBYRPklr4j8Bv87PV/br92573Y7XbZ3Nyk1+tRKpX43d/9Xfbv30+wFyYcD1FJeNRLHQKlDmq1iyAKKKKAa9k0q11ifpXeIyNMnRnm46fHmIz7sW2bfD7P6uoqCwsL/Nqv/RpzI09y7fU8kckEQkAgs1njj3/nMj8/Ecb/58gI/b0iPB5m+tlpFp9fpLZWIzQWwrM98pfzNLINzKDKQqGNafepIncaqBuzMQoLJayWTceQUBRgrY5dbKGFdMKyTD2hY06EGEmHBlRzqdEjmY6Q22ygjo0i9TRaudaWQfS7C3BFUUCPGTzzyDjXX16i5bP46N99Dp8PWq06oa3vWZKkHdVWv18lPRPj1qtrCJKA1evPs6TH/PcVRNrG8ePHKZVKrK6u4jgy3baM67jEE35GxrZmZtttdF1HFEUeemSC2f1JXv7+VcLhGCNjYaZnY2xublIq2YyMjGDbNmNjY2xubuI4DvF4fIcB9J1zO6lUiuXlZXw+HyPTUS4uVXcdp+t4uLbH+Ex8l83C2toak5OTg87k4kKVP/mDVTbmS1jbXTZNIjkV5eQTYxw/PYTjOCwvLw+EvjKZTN+/cFZibHgMs22SyWYITYboBXpkS1lESSA642PxlQyBpA8tqNBtWHTLXWYfHQKhwfp6n5XjeR5u1GXj4gbR6SiTIRkcg+Vyl0y1jYCAIPST3qhf4UA6xETMwO7YGHGDg3/xIImDCar5KuVSGQQorZXYzG1yVD9KWdKh3SPi7xcVNJ9CejYGs/2CX67WYbOQI7lscsI9hj/QTwwWagvous7CxQVa1yrgGNilLlLPpic4iLqM6O+rgAJInkHH87AkEcu02HhjA+2aRmAkgBbSEAQB1acSGA6QPJQkMhW5q/XEjyueenqazHKVlVeWScQDuEEbQRSwmha1bAM35uMjP3MMFZtsrsD1aw2uvpWh17HQIxaOuE48Hufnf/7nOXbs2J5JHsCFNza5/sIqhj/J4w+fZf/+vdVso36V/MKbfOYjjzIzPszS0tKO2dpEIkGhVUMPa9iqhNy2EPx7UC87NjWrQyg5SkC/+3cej/fv2W3z7dHR0V22AABvvPEGDz30EPF4nMgv6rz1+gZXzi8TSkR46GiaE6fSVIubRIOTzMzM4LoumqaxuLiIJEmDJGobxWJxhx3N1NQUy8vLzMzMkApqSEkf1s0KYl1GkESschc7ZfDso/vQNIcDBw5wANg32+WrF9a5vJQhOjNDIuxDU/pFsZ7lUmz2cDyP42mDDx9MkvD1u2K2bdPpdJifnx8kuOvr6ziOM1ARzuVyXLhwAT2Soteqs7DSIHA7a2paBmTadh2yfaEY1/PIVi0OBUxu3uwNYpY945jbft6tkOe6Lvl8fsAobDabRCIR3njjDQRB4KmnnuLlV9f4zn++hNRzcGyHzM0Sz35qnF6rxLFjx3aIukB//atUKgMPv9dff52NjY2+J19e4NUX5jn5iMvjH3h3c+I/KnjPid6/+Tf/ZvDvr371q3u+589jojce8zF2MMH6YhWtbSFIIoIk0Gl3KJVK1Go1fH4/oVAIp9LBapo0Jbaq8rcFMm0LU5PQIjKB22bAdEWi1OpRUWWKN4sMnRpCDahonoDtuDvUEXu2QwIBRVcQRAFREHhsf5KpiMi/XPouhw+n+amfeJaxqA+nXaNWzBHcCjqCwSA+v4g4HMTJdnBq3d0dSsfFrHfoDIc4OhHd08j529fzXLucI3mrimg6hA8O43kutVodDQ2f34do9DnirdeW2Bc2cB+bxqpZVLIt2qtVfLqO54EY0hh+copnPzJNp7rO0NDQIMhKp9M0m01u3ryJJElMTExw69atXbSQeDzOVCjPekQlElKxcy3k28yW3Z6N2LQIzkUpyi5tq++F0Ao4aE+NEG6J9JartAot1nMNrGwLo+sg+lW8oIrPgt6lCrlch43JMM9OqZydG2Vi4rG9r5f9XSrqCjI2WB0SicR9KSEd0yFba7Ne7dCzHGodi8NDftKJKAFNGiSR7XZ78O87f5qmSblcptVqAX0T50Qigaqqg0T01/7drxHPjRA98gT74n6qIY1yTKdV6SK3LETbwzYkLL+CfyTA8Y/s4/HZBENb14ksy4yMjLCysjLoZF79k3U8QyYc1Wi1bJSpEOWlKpnlKvuOv0+NAojtiyGIAssvLFO+WUaQBOrrdZywxkKhhe26RPcKVAA9pJE+kKSeb9EotKi3LUKqRExXCGkyZsygMxZgdPidJK8/89pP6JIhjazjUbcdxM0mhOQHV1zd3t3W7K0gCjSbJZ78ySdJbok3FItFHMe5q0z5x/7yUb7iuqxdz6PrKg//1BxHTgUpFAoPJFPt8/lYXc5x8c0St97K0Ci0UFQZI2gwfiTJEx/aB2Jth3BLNGpw7FRqBz1yZGSEcrnMxsYG0E9gthOyYrFIqVTCMAxSqdSe8zWTk5MsLi5y/Ow4829mKCxWiE1EkGQB23QoLlUIjwSZO/yORYDruiwtLTE6OjrY39tvbPDC79yEHoRGQvin+vSgbqtHfrHCt5arNOo9Zg70jevvZVlxgANkMhkAhof7xcYnP9wikYyw+HaOXtXC8Osc/8QMH/kLh/DfcY0NJ4e5qd2kcLWANq4xm/QzFtHJ1bu0ehae6+JTJOJ+CdnrkF2t0VhvIE/IrFqrrF9b7/tgjfS7Y17eY/HmIpu3NhGmDqDG9/5+Xdshd30Bs9QmYytc/b2rGLG+wI9kSXww/UHMism31y/j+nqICT9CWKOcy1HL1dE1nWAoSCQSQRBACmo4osj0s9N4rkdloYIkS8T2xUgfS+OL+1CD6vsdvLsgFTb4+E8f5A+kLu0Nm8xKDcHzEHSJ6Mk0jz87y5nD/UTgN3/9RZZfKaDJIrZtUeg6DB+b4f/453+ZWPje4lujE2FiUxEUQ2Zo9O5sloWFhb6wz0xfoXvbVmDbjD2RSFAoXmdyKs7NG2XkXAfFUHaM1niuRzfboOKXOHlwaCA8cjfMzMxw6dIljh49yubmJhuZLFOTkyhbFNbt0YdtoZSZiQjT42FOndR3JGvp+AGWlpaIRqPU63UmJ/tJn+M4bGxsYNs2qVQKz/N22SnIskwikSCXyzGVSHL08UkutG3UbAtBgG5MZ/qJCR4/MskbL58bKIYa9Hgs5fD4vuNc2ayzUm6zWWqh6zqqLHEgHeToaJiZpB9NfmedrlT6oz6PPfbYPe+N7aLT8SmDG4UuyXSwz/66CzK1DrMBgQ89PI1flfaMWbbn4Lb/37btHV20RqNBuVwe+OnF4/FBAcEwDM6fP8/8/DzZbJbnn38en/EYUs8hdSiBazps3iphW37Onp0ll8vtOteCIAy8+QBeeuklWq0Wr776Jp31EnbZpbFpMb0vzvDIvZlXP4p4z4nevXyC/jxDEgUeOznC7379FuUXlhF8KpIq0fGaYNmkZkcG1WC3bWG5HqYAPvW24N5ysNoW4mSIWHB3QOdXFbKOy1S1R7fWJTIdIfWGSqZjkql18GsyHdNB9QRSSn++4vYb87vf/hZDusNnP/EMQ0NbvOJAigsXLjA0NIQsyySTSeqNJvvmElzPNYmvNnHbdt+8VhRwmxZuo0fW55E6OsSRkd3eQvlGl2vrNaKrdcSegzLav3kEJKKxKL1Ol3K5TCgUIpfL4cRkhpQIQqmH8PAwz0wnuPDqNUKhOIYuM3sgwex4BEUScd048/PztNvtQTU9EAgwNzc3kNVdWVlhYmJi10371Ik5Lm6+RX4yRGqljrVaR1BEsPu0DnM8SHTKj1FawqdIvDG/xv6ROKFo/8EkTYS4eiVHq90j3nXx4gZOUAVBwPNc7HKH8EINUxaIn96/wy/oTqSCOvvSAS6uV5lNJCiWSvh9Pnx3qXLm6l2uZuq0i2181R6S5SDaLhVR4mvzDZ47PMSR0b19nsrlMoVCAdd1kSSJ48eP71AL3UYmk2FkZISHzjzEIfcEr600+oFu1Ec6qFONW7R6feUq03YJ1LocHTf4S6f3NoB+7LHHeOyxfqLbXn6F+tuZQYJpW33pdOVd+PT9OCA6E0ULaay/ss6137+G2TTJuE5fTOMuSd42VJ9CYipCZDRIudIlqcn4WxZaUKOyL4Jfk3aIRnXabfzG1rWty2iGQsW0CbcsNJ9I23LwvYvvp2c7RH0qC7cWGBkewbhNQGR7nuFu4gvxuJ+f+8VHuHZ1gcmpCYJBjfX1dbrd7j1pUtsol1q88NV1Ni5lCaQDTBwdpt1p06n1mH95jdVrWX7yr56AByi8xmIx2u02y8vLO7p2iUSCRCJBu93mzTff7Bft7kheBUEYJIsf+umjfO8Pr5G7UejnywJExsJ89GeOMT7lp1Qq4fP1qc63K7wVCi2e/+IVrK7NxJGdc3y6X2Nov0Zls8FLX77OxD984r6+hNBP8FqtFgsLCwDs2zfJ0aM65Z9sUa12iUQNYndRv5V1mX0f3oeiKxSuFBBlEX/Sx8Hxd9ZXz/MobZSoZfqKiCNnRzj6E0fRfLvpv2tra0xMTjAZnSQr+9no2bve47YszNUaqapGsdxACmm4mgQRDc8DzZIpXS2x+fomTraJOxYEUaBQKFCr12m3OwhAQruNiuf1vx9BENDCGsOnh2kX25Tny2ghjdBY6P0k7y5oNBp88YtfZGZmhn/4i8+wWe6wvlbFdTwiMR+zE+FBcrC6USNzuU407sOf8lMqlkgFk2TnS1x6O8fTT9/7Jjx0NM3w/+cpZFkkENibPm6aJt/97nf53Oc+N3hNEASi0ejA5NwwDHrdLqenoywdiNPu5vGt1hCDGqIu4XYd3EaPkupijhp88Nj0A7EYZmdn+c4bVynWVNZWKnQ6yxw/OcLTp2d48803efbZZ3e8v9Pp7Nn5m56eJpvNks/nmZiY6LN1torVAPl8nitXrnD48OFd24ZCIVqtFt1Om586NcJY0sfVhTKW43BgKsaJiSghQ0FRFEzTpFQq4TgORw72C+CHhkNU2hZXb9xkZmYKXZHRZZFKx6LesUkERARBYHV1FcMwHkjZeFuopc0a568vcb5R5+Ts6A7P123k6126lsPjB1NUOxbNnk0qqN9X7KbX6+2wV5mYmODMmTN73rczMzNcuXKFZDJJMBjk1KlTnH+rx8KtJVzLxWqaSKqEpve7iLa9ex26E08++SSf/vSnkSSFf/n//TrlRh3ZEDH+jOoN/NmVkflvBNdxkW+UUDo2620Lud6DoIbQ6jE7PEo0mn6nY2O7eFsuloNunu3iVLuYSYPosEYsvNuTQxYFXM/DcT1cyyUyFWH2oVHsV9ZY71nU2zYJYESW2H9qmPjcO1Xj69evs7KywkMPPbSLC37w4EFu3LjBkSNHiMVirK6u8hNHjmPaDgtGFl+2jVHtIbgepibSmArRMJv8zQ8cGHRxbsd8tkFzo06qZiKndy9wmqGj6TqZbIZCPk9qKE0wGceudClkGnAwxRNnU3sOa4uiyMGDB/ne977H+vr6DvrT9gDuc889R6lUIpPJEAqFBn+vX5P5qx84zH958TqFYBKl1EHv2DiSQCuoYAwHeW4uSebtRYJKmcTMMIulDpVei1RQp23aVGtdEi0bdzgAuozrunStfiCuhzSGwwZKB1YzHcotk9g9gvOjI2GuZeoUGl2SiQTNRoNSsUj8DgXBcsvk0noVcanKyEYLoWPRsl3GJZEJV6TVdfma7aErErPJvvXG5ubmgJsejUaZm5u7b8cwmUzyD/7BP6DVajFmjzK/dI1cx8SnGiiySDKokdya29qstBn1DEZnH8w75vDZMZau5smt1FF0GbnXYf/xIUb3EB/5cYcv4SN9oq/EWcw3qa1VMVQJVxIRVOmuYk+e4/YNp1sWhuNR1ySmZqJUV2pYkoB2x8yY7ToYW8GZoErIfgWr1kMBUn6V9a3v/kHQtRw0WcLwOiiSgs/vQ79N4U4URXw+H41Gg2AwiKqquywMJEkkEjUIbl1jkiQNHuj3w3e+Ps/mlTzDh1PIW8lpUAniD/hpBBvkb1Z48WuLHD46ivYAMyE+n48DBw7w0ksvEY1G0TRtx+/i8Tjj4+NkMhkcxyGZTA6COcMw0DSN/Ydl5g59gOuXc7SbJqGIzuHjQ4OO2cLCArFYbNCR2Mbl85vUsw2iE3e/t6IjQTavdLj0xgYHDt0/EYa+PYWmadTr9QGNNRb3E4vvLC51uzavv7TK9fObmG2L8QMJzjw+wfQHp4nNxijeKFJbrlFaKtFutwfbRVIR9j+5n/j+OOGJ8F2v00ceeYSR4RGarzfp3Siy6jq4bn/sAMDOtzCXargdG9uvkdBT1EyL1ypt3G6v/8zMNfFnWkRHg/iaJrVcC8sViKXDOLaDIIq4HkiKimn3i89OxyI04Ue+TSDHl/AhGzKbr28iCiITT038WNso3A2VSoWlpSU2NzeJRqMcO3aMsbv4pOWzTaxmD99IHMvcGqPQJEQEclu0vfshErn7uuN5Ht/5znc4e/bsjvsS+s+wGzduEI1GB8H/gaEgHz07zrdlicJiGX+hg1x3cGSB5lSIQqPK2bkQxybie33cLiwUe3z/T7Kw2sRwQQJevnGDl1+5wcc/ML7rmPYyft/G0NAQkiTx6quv8uijj+74nd/v59ChQ/j9/gGtc2xsbFBYGh4eZmFhgenpac5Oxzk7vfv4JyYmOHfuHCdPntzhVSgIAjG/SsInMRLxsVFu84XfvUrpZgUloHL0yTFmkzZTkxMDhfcHheH1iNYXqYVnmM/W8OsqYUNBANqWQ7VtEtRlRiMGb17P83ymiSiJjExHeeJgirnb/Oi2Ba+azSbQH8MZHR3docJ8NyiKgm3bfPzjHx8Uw4xgldxSjeytEoIiMvPoGAcP9OMtURTvOQ++fT638ZHPzFEueYTCwj2v1x9lvOdETxTFB6qK3amI82cdtdUa57+3QjumI59KYy9U0Fo2VsCgUeniX6thHNqiqEjCliBJnzogdC2spoWZMEgeSqJLZl/Z8g64Xt+vTBT6ohuSIjH1zFRfEe1qkU69h+qTSRxMkDrSl8eGfhXkW9/6FolEgkceeWTXfjVNQ9O0wdCv4/S7B58+M86V4RBvr1RYupkhFApiBDUeHgkQe2OFY+O7Fxbbcbm0WcdX6g9GC3cRJPC8vsm37jPQNY2eayJZLlqpy+WNOo/Gd753s9YlX+9iux6aLHLw+BlWb13DNM1BoGTbNq7roqoqw8PDDA8PU6/XmZ+fR1GUvhpoLMBfOTvFfK7ORidKtW0hCnAqFeDgcIigYFL3GQyFND50fJJrmQaX1qusVzss5Ju01ur4WiZ2UIW2iSD0abVjUR8xv4oqiZirNUrrdVZKrXsmemNRnZDX4uuvLZEO+3jmoSMYjkMulyMWjaJsBcHLpRbORoPkcgPXJ1MzdFRZZCQRQDEdgotV1uwuX+7V+OhccGBc/6BS0NsQRQnPH+flS6scmxjBkUSaxTaLPYfRqIGmSNiOR6nVw+g6TI6FiUxH7rs4Ahx+aBTHdvneVy/hWB5jB1J8+JNHkR/AF+fHEY1MAz2m46UMbBVCXRe33sNtWn2BCYG+Asq236TngSggaBLKSBAtblAUPKyoH3e+jFDtYasS/bAELNNEVe64PmI6QqGNIPSp6JlCk7Z5/66e50Gp2SPpE5HtLhE1ih7UdxlMJ5NJlpaWBh5QhUJhQCXs72fnYPvdKuF3olBosXA+SzDtHyR52xBFEUmSGJqLszGf4+L5dR5+dOq++4R3JNY3NjaIx+ODLvi2ncN24NU/hgKFQgG/3z/o/K2srDAyMsJjT+3+vM3NTTzP2zWnB3D9zU0UQ74rzXUbvriPpUs5Go3eIDm+F7bnmcfGxiiXy9y8fJOoEqVb69IpdnAtF0fwePnVNdZulBDDGnJAJbcwz8LFHJ/4745j+Cy0oxqhoRCRboSAL4AgCIiKSGAogC9+/+9LFEX8AT/ijEjkVoWYoZCrd0iHdNxCm+6tMggCJRXqXYdg20EbCeMPqcii0FdF3mhS9lzKXQvJJ+M2RXptE2XdwZ8K0/YUPElhtdIB+gwCf9NCHQpgeR63ny3VrxIaC5E5n8Gf9j+QFc2PEzodi+Xlfnf9Ax/4wH3Nw4MhDVGTMRsmLadFLBrDczxc1yP8A5jHnp+fp9vt7tnpgj59emVlhampqUFB6cxkjFRI5/qBBC9eXMRvBNEMlYNJHetrr/HsseMP9Nkd0+E7LywiLzeJjQSRAiqe4+Jbr3PjYpnM6ekBo2g7Dm61WvdUBE4mk1SrVW7cuMHs7OxgPi+bzQ7onoFAANu2B7NxQ0ND+Hw+pqenWVpa2kEL3Ya95Y9n2/aehvSD9zkuX/vjeQrn1oiEdcxSh1cLDcb//qPvOslbX1/nhRde4C9++CkmZvdz7uItFioWXUvE8/rF6OcOpql3Ld58cRX1VpmQ6eICa9dL/GG+yYfPpPC7rQEdM51O73hOvFvczniYmYjwV//eWVaWqiiaxKGDCXSlf763BbDuVHi+Gw4f7Xu6Pkgn8EcV7zn6+if/5J/sSvS2ubtf+tKXOHDgAB//+Mf/1Af4o4bizRLrlTZGwsfIUJCsX6W8UIFyh3bLprFYQxkK9BUWJQnZclDbNqbt4egy0kSIkbk4PslBVvZ+WLZMm3FNRQuoKP5+EiepEkMnhkgdTWF3bSRV2qH25nke3/rWt1BVlePHj+8QE7gds7OznD9/ntOnTw8CroAm88h0nJPjEZ73cpw6fYCAJlPYXEOd2FstsWf3O1xqy0Iw7n4ZlUpFLNMknUqRSCbpdDo0uk2kpkqjZw+UPFdKLV6+UWDpWp5uroVoeXi6TGgyRCQq8NHJKNeuXePAgQPcunVrlzpXKBQiFAoNFJgcx2FkZIR9VpezsRiabiAK/QpXNpul7bo8+eSTfPvb3+b06dOcHI9wbDRMtt7lK29vcvVyAVlxCEV0FFlCk0X8qox0m6KXKIpgOvTsvWnMruvx9nqVb33/BldeuYHahU3N44+dVQ7NJphOpahWKkiShKD5KNa6hAsdTNelKYGhSAwFJOxOg7oHntklVZXpygHCI9OMvsvqUtdymM81uLReY7nUIufF6DQ6MBZAON+kUu5QKLcJ+jVCskDMhn3JAMc+OE3sUIxMJsPo6N4D89sQBIHjj01w4FSa9dV1VEMllPjxNUq/H6yWhaRKVHs2ejqA7lfxeg5e18bt2LhdG1wXEEAWEHWlL0BhyAhbyY5X6WAbEoohE3M91rs2oS2BiW6vt4va3PEppPwKggejQ0H2iwJXN+t4nnJXZTTXhVy9Q1AVmYnI+GUdq2Yx8tAoN2+WWF4o0etayLJENOlndn90QIe88wHZarV2VL6bzeYDmRSvL1foVDqkD+6e9ep3dwQCwQB1uc3N65sMj8mMjY3t8ufbC0NDQ2QyGXq93uC4m83mLt++7TnCZrPJysoKsiwzPj6+KwjzPI+lpSXS6TQ+n2+g0nk7OvUesibf15tVMWS6tR6dtnnXRC+zWeft19e5+voaruMwc2SYudk4YdOjer3K9ZXrhEIhjKCBIAqsLVdZ/+4SRkhDFcERXJS0QvZWnu987Sr//d9/qp+A7s3AfWDEYjHWqmuk98foXStw05DJbjYQ5ksgCNQlgXbPIuZCPO7D2BdD0mQ826W72UQFfAk/tuPRCCrYZZF614J2D7vdwx0KoMoKogA900ap9xDjPq51TDLX8xwbCzMWfWf9UQMq3VqX9VfXCQwH0Pdgqvw4Ym21xpd+803K5Sqf+dzf4OGzc/fdZnYmxsiRBKuvbiLoDm061LItAiNBjp567wE7wMbGBtlsljNnzty1uLgd2He73YE37MjICONRH+NRH0Z1mROnD6JIIssLN7ksWvccs7gd65U2lYUKMb86UMsWJJGa0iEtGuRyLkNPDA269bFYDNu291TtvR1+v594PM7y8vKgy3dnkiXL8oD6ns1myWazhEIh0uk0m5ubOxKUZrPJxsYGhw8fvu98dMt0KG82COgKxkgQrefQWqlSKnRgZs9N9kShUOCb3/wmJ0+eHCThP/HwQcqVCpl8ibHxcQxVoWc7/P/++AbGzTIBVYaESqvVRNmskj/X4s24zF975tAD2VjcD6ratwK5vcuajvtJx3d3WFVVfWAGCfQ7hpZl/Zmme7/nRO9/+V/+l7v+LpPJ8Oijjw6GZf88oVXr0hMFdEXs+0ENBQmGdQrFFoXVGnq+g7NWRzBkPNPGsxxcVaY+rDM6GSG51TGpVasY/t0BsOW4CAgkXYhMRfpD6bdBlETUPbpH165dI5/P4/f7OXny5F2PXxCEgZqT3+/fEXQJrkMq2KclAryxVaneC47n9W0MXO+uQg6WaVEoFlFUlfiWnLGoqNiyzupmlerVNdo1P+dry9y6UYRLeYIdh5AqgSTiljp01+tc0zx6rsanH+9zsXu93l3nVRRFYWZmpt8d3DIvvXjxImfPnkUQRBYXF4nFYoPKm23bNJtNAoEAkigwGjHYlw6QjfqQc2VS0XskKZ6HJ4kod5FzfmmhwNf/4BLNFy6R8ETSukIg7Cf3Vp6LhQ6ZwwlmkgFcy2RtM0O5YqEWWniyh190iKngVxQMPdKfmzRs3EaPRt2k2jbfVaJX61j88eUM17INVEkkFdQYF1N4XQd3LIyjq7TzLfJrNbqlNmpQ4/RjExx9fILoTJ8e0+v1HvjzNF3DE70/dx39HzScXj9BcVwHcWuuSNBl0GWkyAPuRABXkgikAwiWS0ESqbYtwrq06+FUaZnoPoX0eARNEXEtl6MjYQRBYD7XoNa2CBkKuiIhCmC5LvWOjWk7JPwqUwGPgCIiNkTKrs2lb96ksF7D7tqIojCQ3A6k/KRmfHziM7tpiY1GY4fwiiiKD1QtdVwXDwFxj/ut1W4NEilBEAgFQqRSKZaWlhBFcVelu1BosbpUAWBiKkIyFcB1XVKpFPV6nQsXLtxzXiUQCAyq7xsbGziOw82bN5mbm8M0TVZXV5mamhoEfqurq7sSPcWQaTe6INy7I+9YLpIkoN4lCb9xNc8f/dZ5yjc2kZIhNF3j0hevca1uMjUb5ejjk+w7u4+VXImq3SXgD7LW7NBWQI8p9Dom8qaF2tSJ+X1U1rt0uw5+/59+rlYURURNZPqD0yCAdr3Ear5DDWgZMnKty4gnEIwZqLNRpFA/ULNLHZxSBym2JfwkCURjPgqWS225imR5KB0TT+5gRV1E00VzPUKjIZJHU+BTKLVMXlsqYzseU7fRD4PDQUrzJQpXC4w/9uD+hX+esXyrRP5WCc/2qBQeTH9BlkQ+9dmT/Odei/pal1bbJn08xdM/uZ/RofcuWFEsFrFtG9M09+yE347JyUnm5+cZGxtjYWFhR7wiixDcUtdcWVkhGAw+kAcngO16eD1nRzG91+3heh6GpmObLrLSF0gql8vcunVrz+TBdlxapoMqiRiqxNDQEOvr6+zbt4/19XU2Nzc5e/bs4P3lZo9Ot78fQ1cGBbBtb7hqtdoXPIpEKBQKtNvtwejL6OgoGxsbd01mDUVCDyrUOxa+eg+z3sPTZQLhB7fXqd24wfNf+SPCx84wvv/oDrXkWDRKOBRiZWWFRCJBpuWRWcgTrdZpjviRey6hcAjPCCAX2lQrLh3Lxa/96RO97UT/foXobQiC8EAMpW1s0z3/rOKHwqcaHh7m7/ydv8M//af/lL/yV/7KD+Mj/pshGPdjuP3qyHb1O2go6MMhZA/2HUoz9OFpugKIgoh7rUB9ocKKIaHJUp8Wd5fKj+tCod4j7VOIqgqJA4n7VhHa7Ta/93u/h2VZWJbFk08+ed/qdSwWY2Njg3A4TLFYHCR6jUZjEIy4rkuz2bxrsKNKIoooYGkyXmPvBCCXy+G5LsNbdhOZaod8o4dXbGGm/Uiyynq1w0uXbxK7XmFIkYnPxVBuC2iUro222eDWN+b5VsDH6WQ/KCqVSgPFK9f1MB0XWRSQt7zmBEEY3PTRaJQ/+ZM/QZZlHnvssR0VtO2k9+jRo4PXxqIGQsqHcw08y9mTluq2LHqygJH2DxLj25FvdHnhhVvYX3+Dg4tXWHv0JMPHj6BqKolih1ypQ63aQ0kFaJgmAi5mq0FYcRgZiePfoxJ1uzziu6kuNboWX357g1uFJpNRAzHXxrxaopFt9v8+BJAE1IjO1KlhhKSPDdPiQtrHTEwntrUorqyscO3aNY4ePXpXsY3bIYriQOxiL/uV99GX0fccD0WUcL33Sg3xkEXwJ/0EFRE6JvO2w1KuSSoSoGc7WLZHo2uhiiL7PYG5R8bQohrFq0VCYyGOjYYYDumsVduslzuUWj08ry8+FferTMR9yN06iWiYylKF1UKLpXIbT5eJjAbQ/e8EC47lUMu3ufVSnt/c/C4f/cx+uC2evn39M00Tn8/3QAWBSMyHbEh0Gj2M2zpbve47lVzX6dPeQ1EDVVWZnp7m6tWrWJaFrut4nsC3vnKDKy+v0i73hTyMiM7BR8d56Ikk7Xa73/kyDPL5PD6f755V+u2OHvTno8+dO0cwGOTEiRP3/XtmjqZ5+UslwsN7z/Vso1VsMXVqeM/5kFbL5Bu/c4n6999ktLGM+PjPYa7UsByPNgKLmzWUQpNaoc5asU6nZ2Gam3hFF8/ykGyBYMCHLIu4LRN7qYlkBkH4wfpGGVGD/T+1Hy2wQu1mieBwmBuFJqGgRng8jBw3ELdGEDzXw863EGQR4TbvUNNxacsivbSfkO3ib9k4bRvCGmrKT3A8hJbwI27JvSeDGuWWyfm1KoYqk95KIgVRwIgbFK8XGToxNBh9+HHG3KEkV46ksW2Hg0cebBYUIB4xePpDKUYn5zBNl1Tct6cox4Oi2Wximia3bt3i4Ycfvu/7BUEgmezft9sCYHdiuxjzoEkAQCKgoY8GaV8pEIzrCJJIvpAn5Y9R7vaYmwojiX0l9KqjUldiXLh2gbqj8PDhfmf/rYUib7y+TjXXQjVkDh4f4olTowMmVTKZpFwus7C0TNcJcOHNTVYvZrHb/eeA7JOZOD7EiTMjzE3FmJnpM5ZefvllHMdh3759O57DExMTvP7663dN9Byrx9whnUu1GPnVOmgyc89OcXz2wWYWOwsLrP7kZ1g48Rmcco8br77E3KNjfPyZ2YEie61Ww3Ecrly5wnrdQlUUAv4gauQdASTP21rrf4ANskQiwbVr1x74O06n0+Tz+QdikkCf8XHhwgUmJiZ+IB3I/6fxQxuc8fv9LC0t/bB2/98MiX0xxhM+Lje6VESBgCZjuR7lRpchF554eoq5Z96h8DTmEsz/0Tz+XJPrlk2x0UO0OsSikR377VoOpVaPuKEw4wokD8QJje+silkdi2a2idPr3yiSJnFt7Rqvv/46S0tLHDx4kE9/+tMP9HccOHCAy5cvAwwWi20BBegrQd3pNXI7dEViMu7n7biGVmy/Y5K7hb6pcI1AIIjfH2C92iZb66IJAn5NppnyMzcURpEEWucXCTsehbCIVe0yFfehyP2bSdRltIkw0WubLF7JEz+m8BNPnGFzc5M3Lt6kkPW4eSmP2ewhqjLDM1FOnB5h/0xskPTpuk4ymSQajQ5mZkZHRwcqU6+++uqORG864Wd4Jsr1tzXim02UkcCOZM/t2lj5FvW0j8OzMYb3oP9cXi2y+SfnOXTrBsVDk6SPHEXTNfDACYjouQ7N1SKTJwI8cXiKSrbH73dXcbI25koJf8S3y/jVqfawIhp6WCf6gMGJ43p882qWm4UmMxED+1KRznwJJAEpZiBuCRZ4loNT7dG9VkRthpg6M8RKx+Trl7P8zEPjLM9f5Y033hjIL98PmUyGX/3VX+XChQscPHjwz53Nyg8KalDFsRziAZWNWvv+G9wBx+1XVH2yiOJXGH98HPGtVaSNNqu4dG2Prm0jCQITskTSFZjYH2fmuRmMuIEW0MiczyBrMvHhAMlQlIPpEB3LxvX6tjAhXSafzxEORlm7uEYXnaW1GkpUJxQzEEwPV7QQNQVEkBSJ2GiQYNxg41qOb/7BDaamR/H79yiIbD1s7xak3Y7pmRjp2Rib14oYB29L9MzeoEBVzTbxxX0cOfnOAzwYDDI2Nsb6+jrnnl9j/ns5jJjO0BYFtF5o8dYf3wTg5KMmoVC/GxgOh1leXiaVSt1VZOF2RKNROp3OwHJEURSGh4cRBAHDMOh0OjuKTMdOj/DW87eo5VukJvauqre2PC2PPbx3Z+PapSzlF94iWZxH+vSnMNfqWCs1xIAKPpH8Uo38K0uE90VJRQIYW3MqGSFHcaNJvdalqdkYikTcp9DrOQzb0LhZwX+ifw47lQ5W28JzPERFRI/o78l7Ttb7nofpY2mKhkhvSWEo7keUdwZObtPEqfWQAjs/o9G16NkOobBOz3KIjoYJVLuokxHU2cie4ioxv0q21uVmrkHqNksFI2ZQWahQW6uROPD+rN7QcJC/+f9+EjyQ5AcPZPP5PKlUivgexc53C8uyKBQK6LqOYRgP3H2LxWLMz8/v6Ljc3mna3NzEtu13leglgxpHHxrl9bUa3mIFT7RRO1DrdJH2hTlzdIj5XIOXFoqsVzrYrofjxFhf6vDS0pvENJ3l72WQcy10VcKyXV65XCCXbfCRJ4ZoNBoUCgVGp+b44hcusPDKCj5PIRA18G2N61htm5vfuMXNcyuMnx7hL37mCBG/SjqdptPpIEkSi4uLhMNh4vE4mqZhmuaenqSVSgXDMPipD5zk1PEO2UILTZeYHQ7tsFi4G8x8ntqHP8rXx34KO3KUqOVi1Vpc+PJ1HKvOmf39ueZoNMq+ffuYm5ujVG9yNfc2DdkjnGshJ33geNj5Ds2wzInp6A/ERN12XNZrJq+uNlgXNwmoMtPJAGMR467qqoIgcP78eWzb5hOf+MR9P0NVVQzD4PLlywwPDz+QFdCPEn4oid7ly5f5lV/5lT+X1M3QWIgzH5rB+uYt1gptyoqI7HqMIXDy4THGHt65mASHg0w9PYX37SWkQotlHDbrDqbURZEEXA9M20GVJcYMjUlXYOxggqmnpwa0gXaxTXmhTPF6kXap3W/s0A9wsrUs1ctVqrUq5ozJlStXeOaZZ+77d6iqSjQa5ebNm4PXGo3GYCZle8i50+ncdR9HRsJcSAfw1pu4tR5SdCvhcT2ymSwIAkPDQ5TbJrl6F58qozVMLL8KSR/JoMrV+WLfRiDuI6hLVFo9dEVk/DYJcEEW8Yd8rLy9QvfMwziux6VrHV79oxs0MzWikSCKJmM7HebnS8yfW2HyzAif/PRR7G6dbrfLqVOn2NzcJBQK4ff72djYoNPpEIlEqNfrO72yHJskJa4eDFLJSYQzzb74kCyA6WILUE0bJB8Z5an9yV2LqmmavPTFr5K+NE9jNIn68FlM26JbriAI/d/H0jHMRhdvyWX12gqdaofwRo3NQo9AoUuxOE/gUBp9JIwnglvtIXgetaTB4VTgvj5A21grt7mRbTIeMbCvluheLyKn/YMEb3COFQk56cOLupjrfcW0ibMjLFbazOcaPHziBKZp8odf+EOkjoRjOkj3EO9IpVLs37+fi69fZCY9g9tzkXzvd/TuRHg8jGqopFQRTZbomM6efpV3Q61jETVUgj0HX9LP8Klh1lpr3Lj8BkkhyVzQh6LoyJJAJOkncShB4mBiYIkw+dQkweEg66+uU7lVQZRFPNfDMW1woeu65Ot9r7L19jqSorJ8KYdb7aL2HLrZFggCgiIiahJS0t/vzvgVFF1meH+S9RtZ3nxtiQ88u1tUYVtUqV6/v0qfJIk89tw+vrpWJ79QJj4RptPrEPD7cR2PWr5Ju9Liyb90hPhWRzyzWWd1uU4sZhEMJli7chktohO5TUAmMtSfZ77+6joTcyqu6zI1NQX0pdG3KeB3zuvdjuXlZWKxGKdOnWJhYYGZmRls22ZtbQ3o3w+FQmGHl18kJjF+MsDiq1WKqzWiw4HBmu86HvV8k3a5w9Fnpjl+am8KfeG3fh8vk0X5+E/RcVTaC3lERaTnWFS6Dq4iEXAlJlORHdulJ1N0yxbN5Spix6bhQcd1GR6LsP/0EGsvreGYDp1yh+pSFbvbF8ASJRE9rJM4mCA6E8WX9N2TXZDZrPPaizm+XV5HV2TU5TrDk2HWqm1Un7oryYO+LRG2u6O4Zrseza6NKolIgoAHtC0bw6/gtnr3PIawoZCrd6m0LGJb81ai1BeT65Tu/nz7cYMkvftORTab5fjxBxM4uRc8z2N5eZnZ2Vm+/vWv89xzz72r7aempgade0VRdgg8raysoKrquw7On3toDEGEy6+vs/L2EunJIRL745w+GWd1M8ermxY9x2UkYqBvx2qmzXIR3np+hZlcl8ShJMLWNe7baLDy0hq5EyO061l6rsQ3/+MFMm9sMjIcpSt08YV8SFuJlx6FwEiAXqXLyvdW+M+NDo98IMKpowexLItSqcTMzAzVapXFxUVUVSWVSpHNZneImqyvr+N53qCgPxwxGH4XYx92o0Hj2Wcp+KO0znwIn+XS0208DcSVNvWaxNzc7pnOeCjATz51iC+WL9BbqBNZs3EFgWZAIXx6hIem7i4c86AoNXv88ZUsi9kGZlYit7GKrYpoST9zwyE+enSIkL67KPXlL3+ZGzduPBAzCeCb3/wmv/Zrv0a9XueXfumX3vX1+d8a7znRm56e3nNxrVarfdNwn48vfelLf5pj+5GEIApMPD5BIB1g42KeUraBqklMnRwisT+BFtpdmY3PxZEUCfWlVaQra4yHAjRVmY7rIQoCQVUm4kAsqBOdjTL51CTaFjWpeL3IyvdX6Fa7GHGD6EwUURKxLbuvBJSzkTdlTiVO8dyp53apbb743SXePreC63oce3Scpz44M1jQp6en+d73vjd47+3zevl8nrNnz7KysnLXczEV9zE+HGQ1XSe+VENQRUS/SrlSptfrD0irskqp3EBAQOvaYNqUxyLEowa6IGA3evgsFwwZSRAwVIlKyyQd6itObkOPBfBulGi0XP7kOwu88vtXMUSR5LERaq0GgUgAURQJex69cpfF7y7zH8o1PvUzh5ncCq5GRka4desWMzMzA+7/SibPWkPi//7t73D4yCF8PpB6RT7xgYfwvNcpHE6SXShBro1ou7hxEWEoyMRsjI8cG95F23Qch+/8xm9w+Iu/y4uhxzAO7COm9GXoRVHse1wtLgIxqAmUjSzeUJB1y6ISVMgM+9EVgXSuQ+OldTqJEr6pGGJYozIVJjQT5ZGZ+ANTN69l6piOi9awaNwqIyd9u5K82yHIIspoEHO9jjIewh/TuLhe5eR4hLg0itE6yq//s3McOJriw587see8KPSVDD/5xCe58oUF7PkAb/znSxz+yCzhib09AH9c4U/5CU+GcRbKDIV0VsstdMV3X4EO6Hfz2m2Lab9I4XyOxMEEC99awGyY2BGbwMEAZ84eRxZlJEXCl/Tt+r4EUSC+P05wLMjq91dZPbdKZbGC1bEQEDAtk06zQ71ZJzYeIzQ9QrttEZgIIW91dTwPsBzcnoOzUMFaryOn/ajjITS/iqZqvPb9m4NEb/va3Z6RkCTpgRXNjh4fwvrscV74w+tk5ktYpoludHFtF39U55G/cICpAzL1epc/+p3LLF/K0Wv3eO2PNxmdi9Op9Ejt2x1ghJJ+sjcKFPM9AsGdifa2X96dNi/Qp4UtLy8zMTExUL+dmJhgdXWVyclJJiYm8DyPXC7H2traYE4ol8shCAKf+EsneXM0y+UXN8jPl/HwBiKrvpjBQx87wEc+cQB5j4TI/Pf/HuUPfg/r1Gdp+IOwUEdTFNywRq3axXFBFQVkZfe2oiQwcXKYzaBEJ9dCcURcn4oyFcaI6Ky/uEbmzQzJI0m0uA8vpPbri56H07VZ+f4KmfMZQnMxRh8ZJRDcXXh687V1vv17l2kWWkiKjNfswVKVhX1RWqNB4neZf3ZbFtxRie+YfUEz3xYtU5VE2mbfz1HoOnimg6DtXSAxVIlyq8dGtTNI9KBPjatvPJgNwPvYjR3F0T8lbt68ieM43Lhxg6mpqV3WBfeDqqokEgnW1taYmZmhXq8P2Emrq6vviXKnKxIfe2QS3crx2FOPMjYyxEjEwHE9/u3zV6l3TA6O76S5+lSZWEBjJdOiIUt44sBYCzXtR1wos7BQYsjIsZYJs/nGJkNTEeSAikFftElTtT77h/5aqccM0qpE5q0st4bCPHyy7wenaRrVapVIJEIkEsE0TWq1Gi+//DIf+9jHUBRlMLe4bVvwbuH0euQ+9CH82SyV3/qv2C+0EXsioXAYz3Tp6DaBeyiOnx6PUH1qjPMjZfKFDtFomAMzMR7fn2L8XvoHD4BG1+IrFzdZul4gsdZErPb6zDJJwIm2uLTVaf3UqdFBIr6NT33qU3zhC1944OfOmTNnBuruR44c+VMd938LvOdE7+mnn94VbG4bWc7OzvKzP/uz95R6/bMMQRSIz8WJ7Yvh2v0q5/08edyQS/iRMCNJi0AvQHOziWu7fTE9TSa+P050NtoXVNjaV/F6kcXnFxEVkcTBBI7jkW/2aHZtisUCU2PDBEfqJA4meOLYEwx3hmmvtzHm+snH+dfX+c7vXOr7+IkC3/u9K2i6vEMGfHZ2litXrnDkyJFBy397kbxfMiFLIh86lOaLLZOq7RLZaOJVOuTLOWRNIZFM0jRtmvUegY4NAhTGA8gjPqYybbpvF9AyNQTrnfkcTZGotU1qHWvg5QbQbDTQDZ1qrcPauXV8kkR4sp80xJQo1VoVv9+PoqrocYOUKpG/WufWQofJ22KzmZkZFhcXmZ6Z5cUrWV77/jKVywrN/Carf5LH1gSmH51DCZcZDUr81Klplg6lWCm36FkuflViKhFgKu4bUEM9z6NYLFIsFnnz61/nI//b/8Grk2fxThwj3pbQRQ1RFPFcl4Wbt9Admfr1HMr0MJt+mZVqC12RCGgKIyGPpZ5FSYakZRBrWFSsDuqREYbGwnzk8BATdzE8vhPVtsn1bINEQMOcL4PlDuZg7gVBFhENGXOxSnJ0nPVql4WNGq9/7SY9DAgoXHhtg6kjaY48sfc8QKfc4eq3lxk78TRNVeSVSxkQ4OHPHd/hb/XjDkEQSBxMUJovsT/mo9oxyde7pEL6PZM9x/XILpWJVnrUmzbZnsWiJiBnalCvETATPPTsQ8QmYvel2XmeR+FKgdJ8Cf9W108URZqVJrnrOdw1l3alTW+tR60r4Hnejn0KAqBK/Q5vUO1Tm1fruLUe6kyUUCpAaaVEsdggkXink1YoFEgmd3fE74dTD4+x72CS57/5Nm5Xw3U9Ykk/R04Okx4KYlkWv/Gr32XjfI3waJBgwkct1+TS8ws4psOAErHjHPS9Tm3b2tOuJBaLoes6i4uLTE1N9c9Ps0mhUGB2dnbH36AoymD+OZHoz1lvz4L0ej3OnTtHKpVibm6OcrnMk89M8/gH9nH57QylfAvP9QjHfBw+kR50JqGfGOfzeUzTRH/+eRK/+ItM/G+/in9tBCoOYtNBDGtUO32Ko18W6Tku/tRO2qnnuDjVHs1ClYQt4uk+zHYXp9Vj7dvXePX7PsKSghhUacY0CrU2za1zJIsCyaDGWExn7c0MuS9cQhr2c+qvHOPDnzg4CKbX12o8/zuXMDs2w4dTiKKAU+7QKHcpr9ZptS3Sj+6tDO12LIQ7aOu20//OtsNmURCwXBdPEnB7Dp7l3jXRA9BkiWrH3PGaoiuYTRPH2im68T4eDIuLiz8Q1tb6+jo+n49//a//NY1Gg3/0j/7RXd+7ulxh6VaJEw+N7ppZPXHiBC+88AIzMzM0Gg1isRjFYpFWq/XAapt3ol6vI5hNPnDiHcGUC2tVmrbESNigXq8TkA16y1UEUUCdiiAAuibT6djUuzaR7Weu5+HRXz9Rw8y/vEwkZiDfVnwIhUJ0Wu1dysRyQCUa97P01iabH5phJO4nmUyyvLyMz+dDVVVESUaLj7NcWOI//cFbdHpVPvrBUw9EO78d7XabbDaLY9sIf/tvM3LtGs53v8uTJ0+RkW5y5Y9uYN0oY3kuwqif5G0TPrfTRqvVKr/1W79FOp3mf/z0Zyg3e2Q215kZjRIO/+mVuK9m6iwuVkgv1qBtIw/5EWQRz3Ig2yLleMzrCvMjIY6PRXZsK0kSjz36UX738+e48NYmJ07f22ohHo/zj/7xP+b/9fd/Gbx3dz5/FPCeI67Pf/7zP8DD+LMJQRAe+AGRz+f5/d//feLxOM899xzHnjmGY/YV92Rd3hX8tottVs6tIMoiweEg2VqXq5t1is0ejWYLwzBYnS+QDMb45M/8HMmQRn2jzur3V/ElfBhRg9WlMlbbZvRov+qUuV5keb44SPRs28bn8/Gtb31rQJ360pe+RC6X4+TJkw+kMjQR8/GJk6N8TRQo+Ep0ryxD1SSZiGCvN+m2TYRGl95QgFrSh28iyHChCgsdpLAPvWbitCykto3nV/oPckGgd1vyhwdmtY2ailBYziOU2sRvl1gXBSLRaN9LxrIx/D7UoIouC1x5dZ0nHp8YVHS2/Vp+/zuXuPp8Fr3QZizu50pplemZOTRbovDSCr93K8fkaZ1HzkocGg5xaHjnvGSz2SSTyWDbdj9YTyQo3Fjk5D/7T1yTTlA7+9OMORr11TL2RhN1MoQ6EmRqaILWmxtUZIVKTGLUdRkO+xDFvkppdvEahiiSmpwj1zCxhiUmRZGhkMvDByOMpfYOju5Eo97jras5al2LmbBOa72OGH5wvz0pqmPnOxgNE8f1KDZNbNsBRUTVZbquh+3c/froNXoUc02aPpmRaN+rrZRvYjbN9xO9OxCZipA6miJ7IcvpkQjnMzU2Km0iPhX/HfL7rutR79o0N+oENluYls2m5cJ0GDXpw3M9umoUvx3m9eeXsNoW+z6y757nvHClwOqLqxgxAyNq0O45LGVrrL22gtoGdJASEvtn9rN2uYbQs3fN5N4OUZcRNBmn0qV3o4Q0EkASZF568TWeebYvsFCr1XbJYb8buG6Xp57ZNxBkuh3Npk1l1UQKQHDL7y05HWXt/Cae41HNNomP7+ws13JNlKDMvgP9pORORbZqtcNL31miWu5wPpDjocdGURXlrmJVkUhkQA/fnssLBsO88J2XefIDj2LoMmtra5RKJebm5ggEfDz65NSu/VSr1cHaLIoiyWQS7a23cP/u38X7J/+Efb/0N5n5jbe4/rtX0B0PLaLS7NnIjkev2kGKaBixfqBpt3p4NZPaYh6vaffFwgI6giKhhnyU8gWEeo+CaxOYTJJZrtArNFFHQ/iGAihJH64us1ntcP1yFvdmhWhIw95o8spvvc3QeGQQMF18c51Wsc3IkdSO60RWJDRdplZo02l08e9lb+B43FnlcLY9Jd8jBFEYGKrf/ppne3juD1Z45s87stksmqbhOM597QTuh2KxiM/nw7IsGo0GhmFw7ty5u4r4/dF/ucDmlSL1aodPfObYjt8Fg0E0TSObzdJoNJicnOTSpUsIgsD4+Dj5fP5dKS0CvP766ztUMQE2qx1EUSAUCtBsNim9soS01gWxP78fOZJEGQ9ivpWj0zaJ+JT+upxp4gRlgiGLZt2gW2gxfGz33KDh92GaJvXaliXL1nXvT/mpzRe5+HaGkQ/17aUmJydZWFggPTbJl79xk4VX1igvlMiIFXzxMF9Yv8yRR7PElToHDhzYs6hm2zbZbHYwpuPz+ZicnCT3t/4WyZdfpvlHf0T09GkAPvHBGVJxg/mbJfw+hTNnRplO6ly9ehVZlrlw4QI//dM/PdhvJpOhUqnw8ovnePrppxmK7CeXy9FoNO6rqLoNx3a5fi3P9L4YPqMfw1iOy8W1Gr5yF69moky+I/YiKBLyaBBrrY5c7HBpo7Yr0QOwei6xSIJWy9z1u71g93Q+8sTP8uLzC3zm50490DY/Kng/4vp/AI7l4NN8NJvNAW1Q1e4ddJcXynQrXRIHE+TqPV5bKtO1HDRMYqlQ39vDdsk3ery8WOKx2TiJkSClGyUqixWMMwaaruC5Lo7lgCDi2A7GbW32xcVFvvh7X6RdbaPZGoePHCa/nufVt17l1q1bxOPxB1rI96UC/MzDE3zX5/GV2hJKaphwfATL8yi2TTZ6FsmJMJMRH+MxA1VQyF68TmgsSDjhJ+s20KpdPJ/cn/cBbn8k1ytVFFnHnY7RnM8SNdQdamzbCAaDdDqd/gIZDhFOByiuVFlarXLoNmWpuiNz6XsZjFyT0GyMcq1CPJXEUwTUeICRmEF1ocytiw1eO3aNkViQZDLZ999r9wUzAoEA09PTg/Pzyre+h/j3/y+ynSQbn/1p4qcOY3Usyn6J8mIN380y0moNZziAGwwSOjCMHtYYDr9D03M9F9MycSybdmGdg1P7sB2P/arMTCiBJvXnGKampmg2m2iadleF1Utvb7KxWccSXAS/hme5iIEHF1AQFAnPcfrVMQkcWeT0U5PUvnKd7nqDfQcT7Dt8d3U2xacQifnwbdTYrLQJmy6RmO99hbs9IMoiE09NYJs2xWtFzg6HWWn2WK922Ki0kSQRkX6wKwBB12O4ZlI3bdq2S3R/HGP6NjEKrz+7N9+xEF9Zwxf3MXGXzqvVsdh8cxPFp2BEDdbKbd5eKVM4v4HRcPEiGp4pMTZzhGgySCbcg6UKVsPE9fWvfVEQUCVxRyAuCCDHdOxKl/KFVfJei9f/5CYp1c/I6Ai//pu/jhAQePzxx3fRzR8E2/Mpe6HXtfEcj0A4MFgLAGRdITgcpFMzKa3WCA8FEASo5Vr06j1ihyUSiSCBQGCHoa5ju/zhb7/NwuubSKqE5zl0O10+9hfv/bAfHR0dzOu9dO4m5752g1apx/zL5zj2xATPfmQ/iqJQKpUolUpEo1FUVaVYLA6KbJFIZGc34upVnJ/8Sdy/9gt0fuEfUn99k5PjYUp+jUqpTXW5Rrdno6kSWtrP8NE0guJRXchRurxOWA1gYVHq1PAFA+jBIOKWGq6MSFBSqckOax0TyacQCGqohoy72cQqtFFHAqRGg3Qsl6rlYPgU4n6VeqZBfrUKW4leZrmK4lN2JHmCJIIooBkSmA7tem/vRE8SuLPrKgnCjpdcr19oFZw+VetO4ao74bnejlEAYKB+eD82zvvYievXr/Ptb3+bqakpUqnUPUXb7oV6o8HSZo2AP0yjXqBer/O3/tbfuqc9VGI0TLPcJTG027YF+jFAo9Gg2+2iqiorKysMDw+jadqAIr5Xx34vrK+vE4lEdvmQOp43UBYNBAJ0ug4910STNLy2jaZIjJ0Y5tJmk9pqHV+lh+26dHwyY4+OQD3DzZv+/pp5l5xTVVVkSaJarRAKh7c0AkQ0SWJxvghbiV5fXXyM3/y9N8i+mMdwbUZmopiuRczwU1tucKXrceQDQZaXl4E+Fb3RaFCp9O1lZFlmaGhoh1BU5h//Y9L/4T9Q+e3fJvGhDw1e96kyzz48zrMP77QlmZ2d5V/8i3+B53msrKwQjw9RKrao1+t88pOf5NFHHx28N51O0+12uXXrFuPj4/ct9i0tlcms1ajXujy2VQxrmw71noXR6CH65N3sQrE/N260LUotE9N2d93/Bw6nGJ0IEwg8WLFxZCzModMjjE5GHuj9P0p4P9H7IcKxHK6eW+H66/1h/mBnlE99/FP3vbDtrk3xehE9quM4Hlc2anQth5DqIQrvBPeKLDIU1gfdvg/MJdAjOsVrRVJHU5x5dJxbF7Lk5ksAJCajPPREf/jUMR0SYoJD8iHeuPQGET3C6uoqiqcgLAg888FnmJ6cZm2jLybQavbYWK0RTfhI7tFVSoc0tPw1Ho62OfLYYziyQc92Kbd6WMtljo9HCGr943bSfqIzaarzOfzREMLYMPXVGsFsCy9h4HoeylYi57YtOqs17Ok0eqiH1LOQ1LsPEhuGgSzLVMoVwv4gnuXSvqNic/l6AbFo4vpdStUyyWSSZrNJfcteQlAkAkkf3maJhfUGst3hxo0bpNNp9u/fv+tBceXCJXq/+M9wyx5Xf+5TdEemWVyr9L1afArmgRiu6RCtW+iCQHI2QDaq7S5cux7NeoOZ2Vmmp6cQRInNapuaT6W6WGXskTHC4TDz8/O88sorTE5O8vTTT+95Hg4eTVN1XJRG951F8F0Wrre7q+AiCnD82RlGpmO02ybp8fA9zYb9ST/HPjQN316iXu2SnIty+LmZ9xO9u0AxFGafm0XRFfJX80zbHpNDISp4tE0Hy+k/qEKajDdf5lamQUuXSRxJoY0FdwarAoR9Cq7nsdo0Gb+UZ+jEEGpgd4BTW6nRLrSJ7otSapq8tVKhvlQgaQkow0EERaRrOWxWexiKjGDIdLsOjfUaZrLfLROFPuU6pMv4VYXtgrnngaCIKHmbqO0QCKssfm0RZ8JBuCZg+SxCD4ewe/05iV7X4s1X1mjVTcanIxw8trf0dSaT2SE2cCeSST+xsRDZGwUSszHqtTqqrIELj3/sAL2OxYXvr1BYqoAH/rjB0Z+a4fRjaVzXZWNjY8f+ypUOm7fKRMZCBGIG2fkSdtPANM2BufrdMDU1xR995RxvfaMItogeUOk0TF78g+tohsrUPgVZlnEch+XlZSzLYmRkZO9q99oaznPP0XvmOTb/wv9I/otXKdd69Lo2IdslNhJkXRVxRUinAgTjPjzLwVqt0byapdfpUNNFNN1AsCT8fv/A8sTzPPyehhsSMT2Hru2R8CkIW5RI2afgdm3MlRpOrYeBQFMUqFY6KLoClovafYeFIatS3+riNgiGjKhKyLaHJIp0rL0ZAaIh49whktIPrL3+DCMCltO3NxIdD1GTEe+j3tdznF2iDHbXxogZewrCvI+7wzCMgejJnd6QD4pyvcV/+k9vUlvoYHcs1JjGZ3/hH3PixKF7bveXPneCRqO3p9UI9Cl54+PjXLhwgXa7TbFYHCQZ2/fZxnqNy29nEEWB46dHSO+RNLquy8WLF/nIRz6y63dRX98MfBuRk2NUXl3FFjxCc/1xpfF0gOyT4yQsD7HrEDAUHjsxRCrQZXbiKK+9+C0kRaTVbhMI7M3SESWJSCRKrV7DZ/hQVAVJEem1LFzXGyhKFloO2UtlaNUJHRpDURXW19aR0xoxn0L+ZplrV12OTidwHYfXXnuNQCDA7Ozsnt9f7ld+hfQ/+2fk/+W/JP7pn+bi25usLFQQRYGpuTgHDyV3CffkcjmOHj3K5uYmn/+NrxCy9tNudnnqkc/uSPK2oes6+/btY3V1FcMwWFpaYmhoaE+K7dR0jHq1y9yB27xXBRARcEURz9k7sPFc8EQBURDuHPkd4EGTPABZFnnimXfhLP8jhPcTvR8irnx/hW/8zmUaEgiSgNEd5fWv3+TJnz56z+2a2SadUofITIRCy6TY7BFQBVzXwdhDojzqVyg0ehRbJvGEj+pylVa+RXI8zGf/3iNcv5LHcz32H0qSTAWor9dZ+d4KjUyDidA010dzMJQms75GWPdxKHWEWD7G9S9dR5wSqYTa/M6vvU5+sYo/qvOxnzvJgaM7q3jz8/Pk83keOnaEJ068Q2fqmA5t06HVcwaJnhTWCDw1gXE8RaFRZjYV53pMo3K5gJFtojsuPlekW+5QMXt0JmMc/tA0Hzk5wr9+9WsUCgXCo+G7UnkURSESCVPOFXFcAeUO+eDFxTJuo400piNJEoIoEggEyGQy/VKxKCCGNPQlF8v0c/p0f/jWcRzW1tawLGsgv764sEDrF38JabXJlb/8l6inZ1ARGIm806lrmw6VVo+Z/UmCtyrYjsem1zcwvR2CJHLs+HGGht8JHEVRoCeA3bNxeg56RCefz7O2tsbGxgZD2gSbt+rEhwIcfnJyQNGLxXwcOjbEm6+uYMsCok/BbVsPNKMH/QRb0CVEQ4auiSZLCKJAcib6QNsDDJ8cJjwexmpbaCHtnonh++jLz09/cLo/s3erRPlmmWitR2ybkmK5OM0OhWKb9lgQ32gQ/R5U3rChkjUtNtaq7FutkTy8W3WufKuMpEmIkshquUWpUCfV8BANFWFLxENXJCzH5Vq2geR49DQBqW5iJH2gSLje1n3ec/CpNsmghiKAlW3hVDo4HZtYMowVFmkOx7hFl5ahcWL4ANXXqsxn51H2K3z5mxe4em4V1/PwhQ3s/+4kR+9Qm7RtG8uydlSf74QkizzzsQN8tdIhf6OEi4ft1Dj0xCxnHhnHMBTOPjnF6nK/oj0+GaFczjIy0r/vQqEQb731FoZhkEql8PkUBNmjVeogKSJ2zyYQ0Qbm6ttd9r2wvr5OtSDQrfaYOj2G57q0Ox3crMurz19HDyQ5fvw4mqYNgpxut8vKygqiKDI8PNxnDZTL2M89hzu3n+zf+WUW39hkwfMoOjZOz0S2bVKejNayCI8HCaX8uB2L3s0KTqmNGvMRQafVamFbJuPj4zsKVl7Xxm1aeLpMr2ljKGLfJNhy+lRKaYuSq0o41R4+wSOc8FOqdWiLIvuPpoiL4mBGZ/ZQiqU3NnfMvwmKhOhX6a5UCMUM8Mu3W4MOIPrUQVHKs11wPAwPFBcsy0GRJTwPArqMVzcRU767dkYAepaDKkmMRndeM1bbInUk9a5nRH+ckc00WJhvMjQ0yWc/+9n35I3qeR5/+OW3KbxZJRbT0dJBapsNzn9jlYMHR5kZvbtglySJd03yoD9LW6/XuXTpEleuXKHT6fDss89ubSuRydT4yuevUFrpF2JvvLHJZ//+I8TumHu/dOkShw4d2pPRtC8V5MWFErWORdhQUEaCJD9xiEazQUe0CQKVlsXMUIi/9vgUuiIhi/1kY3l5GU1TicQiNDJdqpUqfp8P4W50UgHC4TDNZhPbtvA8EBVph23A/M08nWyD1FAYaWs/qqZimSaKquL3KWRWm4SSI6TDvoHBerFYZHFxEcMwBoWz4he+QOJ/+p/I/tIvEf87f5cv/vbbzL+83meF4fGWrnDk6Uk++dPHdiR7ExMTg/Xr1/+vc6xdzKP6FVYv1qnXu4TuohI+MTHBjRs3+OM//mP279+/Z6InyyKn77CXCWgywxGdm1GNWKaJZ7sDdVMAt+cg4NEMKRy4TU/hxxXvJ3o/JDimw9XX1mjIAqNTUQQBMpk619/O8NCH96FH7h70OqaD53mIkkizZ2E5Do7pDChId0KTJWzXpNm1iakyrVyL3MUcdscmNBbi8dvEV6rLVRb+ZIF23SSvS6zWOihzxzmfrYMcoeC4jJ0+Q94QsVerdFfaZNdcMjdKxGejFBYqXHh1fUeiZ5omr732Grqu89BDD+04NkOVODoS5vnrORIBFXlrIRL9CqJfYYQgmUyGowcTbKSCLNwoEOzYtEIGbV3C1Lv8xLPHeGgqjl+VmD0+ybWvXyVfyBONRFHUvRMXQRRRXB3P6KJq1uB1z/PI5wpIkkAsHse2bZqNJoFgAMPno9Vu4Q8EQOhXguzbqkWSJA0Cunw+z7lz5/B++ZcZfu0m5577OaTjp3EbFhH/zmPyqRKWo7Ba7XDEp9BerEJEw5V3VqJUVd2R5PWPtx/DCIIw6NpcuXKFdDqNYQb4wr99EVf14xMErJ7NmY8dGGw7HNZJ+FVKXYvoVJj2Gxm8+G6Pnb3gVLooEyHauoThyoxF35tPki/ugwfzY30f9CknobEQobEQw6eGaWw2cHoOruMiymJf8t7zMKttoveRyBbF/n3QbNtUlivYXbvvh+Z6KD6FwFCAbrULEtRrdZbzNQI9F6HrIiZvW5+2Erlis8dEzIeX9GNt1MHxEFQBSehTejzPo23a5KsuiZYN9R4ty6EbVMCnInQMFoo9EC209AyLPgPPJ+MuV6nfbLGw3MaX8hNO+lm/kufGheyuRG99ff2BhBUOHE4R+R8e4+rFLJsbeWbnRogkbIwtEZlgUOPIVsfwTvVAWZZ5+OGHefvttykWixiGwTOfOsyLX71Fs9hm9HCKpz7cp06FQiF0XefWrVtMTU0NAkPLslhZWWFychJdKSNI/RlcRe3P68i6jOjKpNPpXQwPXdeZnJzEdV0ymQxOo0Hqc59DMgysz/9XSl9fZcVzyfYsUkEdEYGq7ZGpdVHaNo7pYGsy5modt95DSvgIba0dsfjeAmn5TB5ft5/MOe47jApgB+1AEAXkmI5T7xEVPNTDSbSkj9OzCaymiWM6yJrM8TMjXHxplcyNIrGJML5Qn53SEjyclsW+syPk/CqNrkXwDrEgUZfxLAdzs47X7NNw8TwClkPT87CCKlpQxUAAUUSJ31vYodq2SId14r7bElvXw3M8jPif3v/txwXtjsnv/fs3yC9W2ffI6feU5AHcWliknHPwqSK+dL9QFZ2JsHm9yMKN4j0TvXvB8zwUReHll1+mXC7zxhtvEAqF+OpXv8rf+Bt/A1mWWbiRobJeY+RIGs/1yM0XuXwlhxLQuH4lT6vVw8Ol28vzs599bk9PunRI5/hYmFcWSogCBHUFQRYJRcI0Gg3WCxXajswHD6aI3HbNraysDOxV4ukAtYsCsViUcrmCz2dg+O5+HQcCAbrtDvVKk7HUO+tfuVymWCzh13RCsTD1VpNwKEwwGKRebxBPxJEUEcmTEISdyU4ikSCRSNBut1lcXKT38svM/vW/Tu7nf56R//1/55VzK9x4aZXwcBDfVpG2WWpz+YVl9h1Mcvzk3gImul/DdVx6TQt/1EC5j47F7/7u79JqtXj77bcxWwlmZmZ48tl7d84EQeDoaJj5tRp2uYu3XkeK6AiajNuxcOs97PEQcirA4eH31nX+84T3E70fElzHxbX68wPb64QgSdi2h3sPEYvdO/Jot9uMjd3dAyazuUndEulsqqxczFFZqIAAxWtFjLjB6NlRkoeTdEodll9YptU0mcdhdbNBQFNIh3TKVpNoNEqlWqXZ6fFWq8dU3E+8ZMKlAqLn0Si0wPXQ70hk3nzzTTqdDh/4wAf2pKWeGI+wUGiykG8ymwzsqq4MDw+zubmJ3/Bz+swIj84kSAY1auUi4zEfk6PvJJXHHxpl/vsL+ESdRrOBJEmEw7sfDJ7r0ap0OfoT+wgZAmtra6TTaS5dusToWJzNay08y0FWZCRJpNft9RfHRgN/IIDTtbAlgVh874RcFEX49V/n8W9+k88/+XcJnjlBtm0TuovCYVBTyFTbtEQPs9VB7emUuoBPxrsLpdLxPDodC8szqUsGmUIGsSpimiaNRoPOehFZmCU0GaG2Xie7Wtuxva5InBiL8I2rWZLDAaSgilPqICfuHRi57X5irE1GWGv02J8O7mkK/z5+uNDD+q4uaLvURviuyIMoU3ieB+UurY0mSyxhJP3YW5tJHmiGTHWxSs2qsdpbpRmYxFf1EDRpB6W4ZTpUOxaqLKLKItpwgEq1S7fU7tM7t+5nQRDwaTK9jQbNjo0rCrR7DupokHDaj9C2CYkCweEo1VqdjuVwfqPKXCqAL99FyLboCAKaXwHHRTd2Pp6azSY+n++BxRTSQ0HSQ0HW1w3Gxsbo9XosLS3tElDJZrO7OnKu69Jut0mlUvR6PY6dTHPwyDCNukkq7Ue7jSqoqiqzs7MsLy+TTCbxPI9SqcTw8DCZTAYt6CDLKtVineRQDMd2aJe7zD0301fj9fpJR/9Z8c6JF0WR0VQK66//ddxikezv/z6t9QqVSoeC7RCxXNylKlaxjVDogOVguS5OtkFlowY9ByGiI3kOgl8BWdizg4YHnVYbu27jOF2QDSRRwHNdREnYcx5aCmk4tR5yto0YMUAQcR27ryStQSik86lfOM3XfucymZtFqmt1XNcjEDOYfXqK2aEQIUPm4noVWRIH3pGe6WBuNvrBW8eCkIYjC4CALApI9Q5O1iTUMXCbNspMBDF4d/pVvWMhigJzqcAOenOn0kGP6oTH31tS8eMIx/LodSycnk2n2XtP+1hfX2doaAhZyrKLuSv0bT/eK7LZLL/xG79BNpsF+veP3+/nzJkzBINBms0mkiQgiCLdVg/X9mh1bL79B1ehYyObLrIiUiqWUQMBfitzjrnHxvnETxzY5W367IEUrttX4MxUu/hVEcd16TrgWTYnRgwev00XwPM8HMcZjN0cOznMwrlVFFvG0VQEQaRarRIOhe86MypZInLEIJqwB0UgRVFIj0S5rG1i13sEo0EajTrBUIhSqT+y02mY6JMawT385KAvvpKq1ZB+8RdZf+IJ+J//Z5rNJjcuZ/tr+m3PoEDcRyPb5PKFDSan/TiOM/jPdV0cx+HoQ2GarRhmx+b4Y2kKhcw9i8tDQ0PIskyrabJ2pUVlbYlTZ8fw38O2AeBgOsiZfQledxwMQyZQ7CA0TBxFpDUXozca4Kn9Sabif/ZUMn/QeD/R+yFBMRQmDidZ/nqVbK7ZX8CKbSYfmxiYFd8Nkib157tsl26zSjgYoGc7aPLuyohrO2zkCjgVh0bBwfXrNDSJri4S8kkMty0Wn19EUiRa+RatfIsFBVZLbYbCBvLWwurYDpIsEY/FKJVLpCIxlootOprIMVXi4KEk2UaP2KjOxVvfIP5ig+PHj2PbNpcvXyaRSAwoAXcibCh84sQIX7uUYaHQwlAlEgEVVRJxXI9y26QhBvDadT7zxAEOj/UXyLcLS0yOzg7202w2UaUqY8eHWX11jakjoziCTSGfJxaLIW1V0z3Xo3SrjDYU4KFHxhkZibK8vMxv//Zv88QTTzA56bIe0TALbbSRIIbPR6Nex+/zU8jnYWiYXrYFSYMjh3YPmtfrdS798i/z9G/9Fi/+wi9w6KG/gmUJXFovYHUFlFBgV6dRFPoTJmrUwAvpjKkaVcdD0TTUu1S8Ss0eQ1GdMVFm/1OzjMz2K2jpdJpjx45xYOQQX/38RbJLVSKazOTB3YbOc+kgLy+VqEkCweNpWq9vYhfbSHFjz8XXaZg45Q760SRW3MCpdzg2Gn6f3vQjAlmX8Qc01HyDjuWg3GW+yHO9fkdnvoRjQ1GAouvQ2VKzVSWRcVfEq/fIXsvS9rex03k6tSD+xM4HY6NnYTsePkVAkyUswcI/4seWZcxiGyQBya8hKgJe10atdGnaLp5PxjceJDXaV0Sz6xZepz+P5zd0XMfF8KnczDeZjulMjgkst03qmw30lEi5O8/GRpzR0b4yXT6fv6sAy4NA0zSGh4d3JHumae6auc3n83Q6nYFf0ujoaF9u3HEYn9itkgf9JHd6eprz589TrVaZnZ2l3W4zMTFBKjVCreCy8PoGC5urdLtd0jMxjs8lWHthjZJbGpiRB0eCxOfiBEeDiKKA9Qu/gHj+PMrLLzM+M8PqG6vkV/N0sm1kR8QTxT7FOqBAR0CUwamZeJaHEtQQbA8v1wXdRo4byBF9T+ESWZXRdQUpEqTUspBFEa9nI4TUu9IipbCGu9mAjQbeXAJREneoUA+PhPiF/+FRFm6VKeaaVGtlHnn8IGLdZP6r80xpMt10kPlcA9uR8QsCvflyfz4vbtDetOj2bJwtbRbH8/BUGdUQ8NomlumgaXsLWngeVNomtuNxcjzCyG3db8/z6BQ7jD06tufc6vvYG8GQxk989jhrixUOn7z7XOrdsK2wGQz4OXh8iBevFlDW62hBlXqmiRzXUfU2jUaDTCZDt9t9V2bsw8PDPPnkk3z+85/n+PHj5HI5nn76aR577DGg36mfOxhh8+wIS29nadZ71IpVRKfD2MERtLBBp9UmFAySiidpZVtc/upNLNPm0588ssOLTZMlfuLIEEdHw1zL1Lm+kqVQLPDBsyc4MBRE7tWpVsokEv1n8p0enHMzUZQhlcpyA31MRTd0NE2lVq9h6MbAR28bnutR2WiQPJrk6SeP8pWvfIXx8XFisRhHphNcOJyi8kaGhF9F1TQ6nQ6SKNHerNFWBGYPRXYlq9toLyzARz9K4+hRpr7+dURVJZ/PUyqWsWxr9waigNWzEARhIHKz7YcqiiLj4wInTj247UY6nWZ6eponnniCG1fLCAL3TfKgb+/1kSNpYn6F88kqhXIbr+dgCw4TI1HOTEY5OR7dQXP9ccX7id4PEWc/OofZsZm/lMWxPKYfHeepTx68r8pXYCiAP+VnfX6d2Zk0RbtOpt5hOGTsqsZmczkaXZdQtsqV1Szp/cep2g6y5WBv1ChGDI6qCuuvrGP3bDqGzEaxSTKoDZI8YLBfQRQwDAO71yUW0Nis1jkUUTgY8/GpzxxC0RV+7dcW+e53vztQbfI8jyeffPKeyUAioPHp02PcyDW4uF4lW++yvLyK3+9n3/gQj07HOJDeR35tkV4vQLVaHah5ua7L8vIyuq5z/MghUukJfrPeILtUxu9TiSQjVEpVVEVGNGUapTZaOsCHfvYY8TCcO3eObrfLpz71KTY3N3n0yAwrKzZr314kmW2iJH0EgyFqtSqKpFBbLFB1PYaPJZi+I+jtdDq8/n/+nzz9q7/KG5/6FPv+6T+leb5J4UaRoViEWsfCsiza7RaqomL4+92zlmnj1xQiho4zm8BTBCZDEsuVDvGAtmMRdt1+kOJ6sE9TCYZ0otPvzMZ95jOfGXQ1/uJfP8PaYplozGDi+O6HbzKocXYqxrev51GGfPgfHqFzMY+1UkP0q4g+GRBwezZuw0TUZYwTacS5GEuVNifHI8wmH8zS4X388KH6VUYOJUktlljsWn2BiTtuO8/zsDYatG6VcfCo+WSKjoPfcjCUfreua7lcLjbRgjKW4zDuGycaG+JqqUjVcYl4/feZtkNtqysS9ql0Ox1qa0XsmMjE40eo59vUV2uYtS5O20NumEhAK+VDj2ikR27rmIjgbiV6iqrSqNcJ6jpdRWKt0eWZuThTYZ3U42NEkhr/9t/+a/7zf17i1KlTHD9+nFTq7iqvDwpd1xkeHh7M1WUymQEVtN1uk8vlSCaTg89aXV0lEokwNDREt9tlYWGB0dHRHVTPbUP0xcVF4vE4ExMTdDodksnk1mfK/MxfO81bR+J842vfxdsoM+wqFF9ap1Kt0kmIuEK/y9rItchfyhMcDTL0rc8T/fKXEb/3PZiZwe7ZtNZbyEWboClQU1yCPhFNkxBCKl69S1hVaTgedlBC978TLHpdGzvTxG1aKCkfwh3d0pGxUSy3TsfyMFQJ1+vPxom+ewdcpl8m3rIo3ygz/sQ44h3m7KIoMrc/wdz+BOvrMtGoAdE+y2T13CoHozqaInIz2yBzvYhS7uJFdcrNLoIooHQt8Cl4HihSf95a6Nj0JAEnrGIt1wn5NfS0HxBwXJd616JrOQR1hZNjEabuYDC0ci30qE7i0O7C2Pu4N44cGxpQnt8NGo0GlmUNZsE+8OQkjUaPW6+s06p1CcxGeOInD/DIiWFyuRxf+cpXkCRpa0Y1gKZLA2n9e+GZZ57li7/7PEcPPcvk5CE+/emfZHl5mbm5uX4iIsHP/vcPcfFalq/8hwtIdoeG0uD60g1GRkfptDuMjY0iSBKB8RByvsXN76zw5nScJ87sLPKIosBEzMdEzMfxmMeVK0U+eHB7jTLI5/MUi0Xi8fiOgtK2/cNH/uIxvvEfL9LdbKFICobfIBKJ0Gm3qdVq+HwB6j0b03RoLlexQwrREZHvfv8cjz76KJ1Oh1AoRCwW5iMfO8hXaz1yC2V8ioxp97DbNhtqg6M/dYTjc3sneWahgPncczjJJLFvfANx6xhTqRQnz+7j2/NvUylWUDQFv9+P1bPBgwOHR9+zEM+d+PjHPz6IHe/nZ3cnFEnksdkEpyairFc69GyH7MYaT56a2rMx8uOK9xO9HyLUgMqznzvOo6U5PM/rK3zdZyi03W7jOA5iWoQF0DSVI6Mhml2LTK1D1K8OKktdy8HVQxyfNBhVU5garK92SBxPoUeNvqlyvcvkmE73egFJksindGzHRbsHb9rv9/fNfg2DnuVSlgT0fIvGeoPYvhjT09OkUimi0SgXLl5mbGyWROLu1NLBfjWZ0xNRjo+GKbVM/ri1SDTs8ZEnpgd/U/zQIa5du0az2eTs2bPk83nq9fqO+ZehhJ+/+rcf43vfvknmVpPiZgNsj6bdpiM5RA6mUYdVLq2s8+bNFscPjvPMoWlkSRyoVn7oyWG+5bhsvLqOerPc/3zXoVuw2Ih2OfihOR45HtxBMzVNkxf/3b/jqX/+z7n29NOk/vk/Z3h4mFKzRPF6kcmowZvNHoKmEfb7sEyLWrWKh0jTldmfDiK3bcaemcRqWghX8khRg42mSbnVQ5YkXK/v6xQyFPZpMklFYuzRMYzYOxXp26lrydkYydm952628fhsgrbp8MpiiWjCIPLBSZxMC3O5itu2+vOgioR2Io0yGqRtSGxW2hwaDvGRI0O7ZImhT01uZpr0Gj1kXSY0GuobZr+PHzqi+6KMJwMUSk1yjR7poLYj2XMbJu2lKlXXw+u5GEM+xkZCO96jKxIRn0JN7mGNDOGpEla2ybAiUxIFqqU2nuvSs1w80yYV9xHxa9htE9MyaUkCK6tLzO3fT2wsRLfZw+k52NdKuJ5L2XRQ7xT+EUDovcPXCgT6PlRhv5/leou61k8aUmGDQCzA3NwcrVaLM2fOUKvV3nOi593BjdZ1naGhIW7evIlvay5mdXUVVVX39MXbntPRdZ3Z2Vk2NzcH1exms4njOHQ6HR566KFBMNfpdFhYWGB6ehpRFFFVmVMPjVG8luLS5Rwls0J+v8hV18MtNgeS7RFDYcKn0/uPf0j7j77H9G9+gcTJk9hdm+UXlindKBEaC6FeW0X1++m4IrV6B9O08Lsu3XIXQZXoICDbLtrWvSvoMoIq4jZ6WKaDMhpA2Pp+TNulI4AlC9i1HuGYj27bRFUkpHtYsnQtB1VTiBkq1ZUqRz93dBCwlUstcpkm9palz9R0jGAwSCVfQZd1wpNh0vU0+ct5xkSBWMzPglciE9bYrHdxPdBjBlKuhWJ7+AIqmigitC1cXaYZVPGl/CgNi+ZalZrRZ8FIokDYkDk2GmEkouO/Q43Talv06j32fXRff374ffzQYZompVJpBz3aUGU+88nD5J6eptW2SMUMAlvUwm63i8/no9Vq8au/8l8ImAfwRzQ+9/cevauoB/RtUL78Xy8Rah3l9S+tE0wHmD9SIp4MUqvVCIVCW95/Io2WjVPvMXpolKu3rhEKhZBEkVA4NLAbAdBTfpRSmwtvrHP25PDO2dXbIMsylrWz+5VKpcjlcly6dIn9+/fTbrdZXV1laGhooNRr/mWPF37vMrnrRRIjUfxJH8ga9bbLzbfXkLIdxKqJ45OQxv0sv1nAPD6GmnM4OpnG6tbZ3Nzk8MQI/r9+htffWGfpehGp56BIXVIJh8984hBrS7d2n69Wi/qHPoTiOASefx75DhuJ42eGeeP78zTXumh+j/JKHbNrM/vQGMdO3131+N3iB8EW0hWJfVvCZEoz936SdwfeT/R+yBAEAd99ZqJux5UrV3j11Vd56OhDJMYT1NfrJMbDPDob52qmTq7eo9zswZZ31WQiyIyu0KhlKbZ64JdRtx5g214vrgB220YMiuQaPXx3SlHvYRgbiUSo1qposkChbTIiyphbNgVPP/00oijyb371N7nxuk051qFZeouf/tzJ+w7eQr/lng7pDAVkdMXdQYkQBIFkMsnq6iqXL19mbGyMffv27drH+FCMhx+KMvyp0ywslFnKVrl4LUOv1KZwdYP2vMaGaaMHfeQW4MqtLqdPDnNiPMLBgwdZWVnhQ48lKJ4a5vybm9TLbQRgVI3T7Czy8WcnMNvNwec5jsP3f/u3eex//V9ZPXoU5V/9K6a2gsLwRJjAcIDi1TX8dod6N0y101eqtEQDy3YIyTbBahkiUVJHUqh+FUEU0OaLDGkKtYBOBw8JCLoQdiAQ1Bl/bJzUsT9dJ0MSBZ47lMavSry2XOZW2yKUMohPhlBcr0+LkgWqPYdK20TH5aGpKM8dSuNTdy8RZtPk+jcXmL+Ypd40MTSJ2QMJDn90H4H0+92/HzaCI0EOPDVJ+5u3uNHusW47+DQZTRbxPGisVvEaXUQPtLiP+FzsriN94aCGMxEme72I07SxWz3Sw0FaHRvbdjFtB7nn4LfALHVxXZf4/mFSM6GB15soCvhCOm7HoiOLtKQ+7U+6Y5bOdhwU6Z2qvCCKSKKI6zp4QNNzCfecwTrzgQ98gFQqxWuvvUY0Ms7aSpXx+3gY2V2bbrVLr97DsRwEQaBWrBFVo+gRHXlr7dN1Hdf9/7P3n1Fy5ed5L/rbOVROnTMaDTQwAAYzgwmcwEnMQSQlSqZEWbYlXcu6d9m6S7LXXV7nLF+fe499tM5ax+ccWbacruVj2QqUKGaKHJLDyQEzg5w651g57dr5fqhGAQ00MMNhFIXny2C6q3ftXVX7X//nfZ/3eQK2trZwXZeBgYE9HfZ6enrY3NzsbMwajQau69JqtXAch/7+fqrVKpOTk7s2LIZhMDo6yvz8fKcDWF+sk7Ny3P/Ue3luYZVXF/IoQkh3SkESBbwgpGK5nLw0y8CF8xz8u7/KgtWFNFeiMF1g89wmm84mC5UFqvUqI8lD5G2PkhOiehAKIrV6Ez+j4wYhm5UWUV0ioipoigiiiBjX2mRvtY7XbVIjpOF4uH6IH4bE/YCW5eI1XIppiW5J2HOT0HLbLqvDGRNTEGg1XAhhfrbIqVcWmTm9gVWx213BMCCmK/TnIkQ0n1wy1SbfQjuCqLHZoDRfwrR9kgkVSw+I70i3AklA3LIQ6i6CLCLGVNSciaJJNG2ffUMJgqpNpiuGljaRRYGEoSDtIU/1Wh6VxQo9x3vudPN+BLh48SKNRoNUKrXndzhAd9KAG0ylhoeH+Qf/4B8A8Ow3p/nO/3UG1/IoF63bEr1zZ9a58MIiSkyiZyzH1kyB7/zlRX7znz7O9MwUiUSiPQ8bhpw/s44eCihRjYGBAZLJJKtrawwN3mz0FMtFyF8psLRZZ1/f3l0sRVHwPO/m6+vuZm5ujgsXLpDL5Th48OCu37/n3n5SKYNvff0s1YUW6+e3qG81ELbr6E5AqAooAxES/XHiURXZlrBeWuHF05ucPdLFB943zlBU6OR1Dn/oIPUnPTw/QBXhy1/8SxThZk+I0PPIf/CDRNbXEV9+Ge26iJhSqcSLL77ICy+8QHf3MI88+BQz5zbJiCkm7u5heJ/O5sYKsViso1r4SYIsy3ie944yoP+m4M4r8ROCMAjxHZ/19XVKpRKnL5/mE499gpUXVqgsV0gPxHlkPEux4VC323M2UU0mHVFwmy7FpouKgDqWohqGxIKQWstDlUR0NyTQJGRDJgj8m0Y0XM+7KXi7c5OEIY7nE6pSe9CetvnAG2+8QX4jxLRj6IrM7OtrLDwyzP7xd/4FevWG3PU6hCEnT57k8OHDNBqNTsX9Vn9f2Fxlo9HgzNkNwtkGvQEomTTb1SLRhEo6buItN9haqPKVcxusf3CC9x/vZ3h4mO3tbQS/xq/9wlGaro9A2z3wP/3HS7z1+qt4nsfY2BhhGPLiF77Avf/0n1Ls66P+f/6fHD90qHMeF69c5FtT3+L8V89z7+H7uPvRIdZbHlXLxVAk+mIacctDEgQid0VYr66TEBLs+8A+spNZClMFKksVAjdAEASUqEL2YJZIdwSv6bH8UjvLUE/pJEeSqO9Av34jJFHgkf05JnvjTG/VObNSZrXawtsheo1Gjf5cmkfHsxzsjdOX0G9ZaZt7YZGXnptnUxMRTRnP8dh4fQWAe37hrs5m+g5+OBAEgb4TfW2HzleWWV2vsVFzcAWB0PFIrDURVYViVCF7V+72kRp+iClBoeVSzTcRGi5mxiTTFUHYMVOpr1fxLRfKHmJMQw8UNF9DutGxMAh3umcCYRhyYyPY932MG2ZXjYhJtVpFEsD12xlpV9eZnp4eLMuiq6ufv/gPZ1A1jV/5rffcZK8eeAHVlWr7Plqu4DbcNsnbYbelconGmQZqRCUxnCCzP4OUkjqh67Zt33JToKoq9Xqd5eXldtZcJMLwcDuLdHt7m4WFBQYGBva8V0RRZN++fSwvL2PqJqtvrGIHIZdbHlIkSVz0EEWhM2epigLZ4ibeS99h5T1PwMFJDtUdrnzpCoEXkBhOoDoqV2amUI4OsCGatLbqRFWJyECCcNCjdWYTQZfwVIGS5VFtuTQdn6gmEVFEFEVBjGk0C01qtoOVaatMBCDdFSGlq7irNey4Qi2isFRskjJVDFVquxAHIS23TeKHMibdmoxXd0mOJXnjG9Nc2KzRLLaIZE0yPRG8tTruVotqsUiZELPH4L0fyRJNaIR+iGd5+K6PfdbGdnzKxSaxmILqtYuPoaEQpEPCpoeU0pG7259LEfBDj5Lt0S2LaHWXrpFbm7K0yi3qm3W6j3Uz/Njw2ypr7uD7x+rqKq+88gonTpxgbGzsHZsoXY97HxzEtjxiCY2Bodsb59QqNqEboGdVRKldYLcqNs2my9DQEMvL7e9Rxw9oVW0UbUdFlM2ysb5OV65rz4KYaiqEVZvGDXm812OvvQzA+fPnMU2TVCpFJLK3KcjkWJp9v/4e/vQ7Z1j7po1agthYGqU7Qqw7imPX8T0PNdme/Vddn0jBovL6Gl+2XD728UkmBgeZmppibGyMqHZtjT106BCnTp3CMAwsy2rH0oQhGz/3c6ROn8b5zneI7t+/63wWFxd54YUX8DyPj3zkKQ4ePMj7P3LwxtOmVqsxNzeHoij09/e/q/f3h4F0Ok2xWPyeFSC1qo0oCe9oPvCvG+7syH4C4Fou3/iTc6zOFdmsLfPhX/oQx+85jiRJaLrGwnNtyY4W10jmTDI7IY++41Nbq+HUHBLDCRIhJEcTXNyqk6/ZaLLEkZ4Yes1FHU3iNlxkJ6Tl+rue33HbeSs3IplIsjq3TF82CdAZtK9Wq5w5c4ZkKoqSMqnlm0S6I0S+h/BJaFfBms1m5/+LxSJzc3OMj48zPDxMGIZcunSJ8fHxXYYJhUKBSqVCEARMb9Y4c6qJOtUgmTGwFI+W6zLUN0rg+xSKRaJdETJShOZKjZNfuoQiC7zvWD+5XA7TNJm6cpkDBw507KJ93+dzn/scjz76KACvfutbTP7O7+AYBiv/+//OQw88sOs6ZmdneXPqTTLHM9z98DGUZojphAhK20pdqLvEemP03ttLZn/baKZcLjO30F4kx94/hlt38R2/PZdiKpTmSix8Z4FmodkJ9A39tvy3//5+codz70ryYLk+9ZaLqUoMpAwGkgb9KZPVxXkOjKQY7Nm7QnfhwgUuX77M8YPHmT23yZYCuYyJIosEAawLdWYvbbN/tUbqe8jau4N3B1ES6b+/n9RYivH5EluX8tiWi1NuUW74bHeblIIA5TaOhIHl4cyV8LabKCmDIK5jTJfwbR+rZCEIQjuKIQhxTYXoUAIhphLWHVoX8yj9UdTh5LX8IlFsm0j5IQjizfJwL2ybhtwAQzewigVUWSR0vF2GHhsbGwwNDdM/vkHghZg3kNbKUoXV11epLLcdZ42MQaw/tktGHOZDorEobt1l88wmUy9O4egOj/3iYyR7kjSbzU4UQue1CQI2Njba8Sv1OgMDA7vm8paWlkgkEtxzzz2dDc/Q0NCehHFwcJCZkzNszGzQzCbY3G7RlzBpNuq7H7i5CX/1daTjd9Nz5CDrFYvuuIH7xhp99/Yh6zKNUoPx+x7j5OwmuUyU/n3X7jXr/DZyd5Sg7qD4IV2JCJWGRdPxyVddSnj0pOMEoUBFAKHqIKsyYlonriskTRXfDVHSOqohE43r5F0fPwzxgxCPEEkQGEiZpCMqmu3j1V0yBzPUgpAzfzVNOJqk/1AX7mYd90qRwPJQExpal0m1UmdjucRXv/gGP/e334NmKqDAyoUVzB4TQZbxZ4tEdAWlL4qUMhAUEUGT8AsWzlIVv2ghJTQERUKTJcqWQ19Ux6nuvQn3HZ/aag1BEhh8aJD+E/13JOY/Amys1zh16ix9fX0dQ5J3g3hc54M/c/sQ9avIdJlIhkx9u0YrYlNfr9MzmcOMKEiS1jFUEgUBQRYJd5RMLatFCOjGLbqFQUgIt81iu1G6ads2CwsLWJbFiRMngPZaViwWSaevjVq0Wi02NjYoNF0uXSrS54ikjvYhZwwCz6dar5FKJtsxObU6rZZFLB5H6YmSLFqUz23zDVMh94nDTExMMDs7S3d3N7EdGebk5CR/+Id/yMrKCh//+Me55557WPuN3yD39a9T++IXSe+c2/Xo7e3lxIkTTE9P3zIfFCAWixGLxXBdl6WlJYIgoK+vb9c6+eNAPB5ndnb2bYnec889x8bGBg8++CCthsFX/+g0kiLxqb9379sqR/664Q7R+wnA4lSBsy8vU5chq40yMXSwQzpSYymMjEF5vszWxS2qS9VOPIMoiUS7oww+NIiZNVl5dYXSbIkHkya+LCJ5AVLVJbUvRXIkycJ3F+iLaZzfqJK6rmrhOg6GcXPnLBQEJFXDcJoIWhIt3t40vvLKK/i+z8/9rUeZm7LZ3qgzebSbvp7YTce4Ha5WwRzHYXFxkVQqhaIo7N+pMAmCwOTkJBcvXqSvr69j/pLJZBgbG8P1Az733RfwzlfJdplU/SaKoJLaWUhFSSKXy1Gr1WhaFVKDCcKlKie/PcfBoRTDmQiRSISJiQmuXLnCyMgIpmly33338cYbbzB9bpa/+IO/ouf/+F3kZotz/+Zf89jTT990HV1dXTz88MMUCgXu/+z9tAot6ut1vJaHqIiYWZP4QHzXBjaZTJJMJnEch4WFBcIwpL+/H8MwePPrb9K61EKP6aT3pzvV5zAIaWw1mP/OPAjQdfh7q1i9tVTi22+sUJ8totVcXF1ksS/KsbtyTCQ01hbnkMN2ILXrunie15HnTU9Pc+rUKV595lWi+SHCiYlOJ0IUQVJkGg0Xr3VzVfMOfngwsyZm1qTv3j5816c8X2bqq1NsBAFq89YW6IHlYU8V8EstpLSO7IfgB5g5k1h3DD1r4HkBoiDgOx6Llo0Y09sF76ROYHu4SzVCL0Tbl0KQRQRVRNBlgpJFRBV3FSKujsndaAICIEgyYRgQC9v5fmqsvTZdNTKQZZFf/NXdGxLf8Vk9ucrG6Q1CPyQxlLjtBl7WZBzPwY27dHd3s3JpjStfmmLgRD89x3s6cnHTNGk2mwiCQE9PD4qiMDAwwOrqKoODg3ie18nDulp8isViRKNRlpeXiUQiZDI3B0eG+RBJUbiwmsfUDESxXeiyrJ1CV6lE+OUvw/4JhPsfQBZAEkWW5kukd2Som5ubtOQoU2sVerKpXflzoRcQNl3ktE4YV3E36lCzSUU04iZsbBdp+rBRrKLrJoEApqGguwGRmI4s0o5eiSgoh7J4pRZevkm65WN7IaO5KFFNQQwhaLl4xRZCTKXraBeJwQSnv3YFr+6Q64riLJZxlqoImoSUM9ufmRBsx8KWLPz1gKnL6wwMxmi1WkxPTSOsCAg9SYTuLgQvxN1sIEZU5J2usdgXQ4xruEsVvILVloQqIr4iEtAuzl2FZ3u4DReraAEQH4gz8MAAibfpCN3BDwarKxW++fkLJM2j/Nqvve9H9ryH7uph6QP7eP5L56htNekaT/Phn7+rE+w9MjLCc889x/79+8n1xZk/v00YhGxtb9Hft7ebLkCjaKFnDLK3mem8vqO3uLhIEATE4/GOYzC0FQobGxtsbW1h2za+73cyMxcub+IvtogrMnLGwLZtbNsmmUx2/j4ai+J5HqViiVg8hpw2SNg+W+e3uHhvP92HexgfH2dlZWVHCdGF67pcuHABz/MoFAqs/7N/Rvd/+k8U//APyX3wgzddR6VSYWpqip/7uZ/Dtu13RNoURWFkZIQwDDtuqYlEYs918IcF3/fxPK+zbykUCkQikc7Prt/PXP1emp2dZWFhgZdeeonu5AmseQ1BFlleLN0henfw7uE4HoV8E1EUyOYinQUontCJJDTc7Qaxnli70nkd9IROz9095A7naGw18Hekm5ImEemKdAjE+AfHyV/Jk7+cx7M85LhGdjJL9kDb+nr7/DbZqoUuSzQdH3NnY+S6LvHEzZX2WsslHYsg5EtExvuJD8RZXl5mcXGR0dFRRoYGGXn77OJbQpZlNjY2WFtbY3x8nGq1Sjwe79yIjuO0c6g0jXPnzvHQQw/tkpgu5BvUlpqkAo+iXSOZTOzZmYzFYviGR6FYJBLT8JaqnJ8tMLyTryLLMocOHWJ2dpZEIsGJEyc4+dJJ5l/zePMPXiLmHmH67/4Cv/yJT9zURTt79iyu6/Kbv/mbVCoVFFVB6VWI9b4z0ns1gysMQ1ZXV6mVajz7358lqkd5/KOPs1mzWSo0CIKAnphKNipRyNdZ+sIZIsVefBkIfGKqQFwT0eX2BvuqAUXHHKHp8ZXTRaRTecy6g6DLaI6Pu1Lk5XqTyEM9BOUyBw4cIBaLoShKW+a1I8eQZZmFhQVOPHmC2mmBkytlLMfHUCVcPyCwXRIR9Y5d+Y8JgiggazKCLFAsNqm6Hk3HI2WoN2dT+SHOXKlN8jIGgigQej7IIoEms1Vt0TRFrsZ9CgIECBTqNpmohgCImoyQEnDX6giqhDaaRJBE3JSOsFWnPxOlbnudNSZsugiGhLRHh7HcdOhORXHWCyTfsx8zaxKGIbVabU9zFK/lsfDcAptnN4n2RNGTt9+M+L7P/NwqxW2HZjVgc2URERneKqF89QrpIxm6H+picLRN8m6coxFFkSAIqNfr5PN5xsbG2p3LIKBcslBUmVhMY2hoiHK5zPz8PENDQ51iXRiENLebEDUJLBfRbhAaMpquUa6UoV4n/OKXCPv6EB9/vCMfiwpQXa2RS+hsr2wzum+c07OldkjzDXmdgeUROD5STEU0FQRVwttqElTbZD+payRlkbobIkkCuYiGFAr45RbhWg0/riF3R1CH4qBJiAkFKasS5Ju0lqqsrxTpjakIUltxEBmLYGQNAj1genqNzcUSsiFSm9ogqHkIURlREaDV6pxjIh6nVqshhTJrCzUOHe4nmUyiGzrRbJTE0ACzxRZiUiOsO9izJQRJQO5qr9NSVEU8mEWp2Xj5Jq31BlLTo+nVCVo6hakCAgKiIqJGVboOd5GZyBAfiHdUEXfww0cyZZDqijA6MfIjf+77H+kl3e3T0zNItiuyy6lTFEXi8TjFYpFj9/Yx99IShcUtooko0i3MO0I/oF6zOfD4CF23yZKVZZlqtcqVK1cYGhrCMAxmZ2c7LqNXnXlbrRb5fJ7h4eHOfFvT8Th7ZpOULWCnQ7xGAxFhT1dLWZZJpVPUqlUEUSSSNTCXK5w5u8GJ8SxRTWZgYIBCodBxFv71X/91nnnmGS7/wV+ivXCS8F/+L/R99rM3HduyLE6dOtVxUP9eO3OCINDX13bNLJfLzM3NoaoqPT09fO5zn+NTn/pUp/N5IwG7/t+e53VmKa8et/N+3PAzLwhZyreQFYmxriia2t63+L6PqqpEIhEURUGWZWRZ3iUvrdfruK7LZz7zGWLRPr7yx2dRVYkD32MB/a8D7hC9HwEcx+OV5xc4/8oy1a0GiALZoQTHHxnivgeG6BpJ8slfu5eNlQpjB3Jot5BbSYpEvP/WlrZqRKXvnj56jvVQrzvMLVdYanmsTefpH0yQncxS++48IxmTy5s1JEFDU6R2htMN+mrL8am3PCYyJmkBGkYDRHj55ZeRJKmTTfNuUa223aIMw+jIAxYWFrjrrrtYXV3Ftm1UVWVgYABJkhgbG+PSpUtMTEx0yN65qTzSdotCq8jIvv231YhLskyuq4tKqUKwVubi2Q0eOdJL4roN01VHvaWlJQRbQL64ih42WTtwkMnhuzsbt6uYnZ1lZWWFD+5UxqLRKLZt71q07JZNZalCbb2G53loaQ2jx0BSpJvcAAVB4NKrl9he2GZRW2Thz0rIvZPYfkiIwGkgbaoEAbgLNQRZQuiLIcoySkQlHlU51Btnsje+KzcK4OWZPGq1TE7UUQ53dSI+nJUaFH2KgclANLqrgng9RkZG+M3f/E1UVWVRWWRttcLKdoOSISPYHl1OyMT9PcT6v7eu7h384HDl8hbP/dkFNl9YpBoENIIQJ1cmvS9N6rp1w12v4W03kdJ653NguR4EIbbr4QUhmq0ja+37yQ3a0r1Ky6Vuu/QmTDRZRFAkpLiKu1aDmEpdl3BNmf7uGMmIykwIFasdARE0XcScjqDtvodqlosfhOyPqeitEGOgne+4vLy8K3fqKgIvYPH5RTbPbJIcTSLrN3+FuV5Aqeni+gH57SJXTq8iViWchoMoQiCGaIaEK/hYlkv1Wwssny8wfW8vRx7uR9MWGR0d3nVM27bZ2NjomEo0Gg5f+uOzLF7YQlZE7nvfOI+/b5xkMkk8Hu9IO1OpFIEftFUYAoSIpJJJavUahm4gOg7ht75FGIshvv8D7cDNzovjEFguTkwkqkQpWj5ly6ErpkMQtgOKg4AgDHCrTZxmC1EHdprqYVYhNCCoOsiWBK0AWh5e6OE77Xy60A0IAgl1NI2X0PBxER0PUZSQYzpqwiToTeB5IfsP5NA0GUmTds24ra+sIwYCEU3D33Ix+qM3zYWGITQdHzXRRVOwmZ0tY721zmBXhEMPPog428ALQJVdLMfHjKqEFRtnsYIYVTvHE0QBKaEjJXTyCZVDqQjpYoveu3vpPtqNqIhoMa1tvLPHZ+MOfviIRFQ+8beO/Vieu1arMTiUo7d37/GBbDbL9vY2E2P76DnaxYWvnuNgZmTPx4Z+QHGmhNof574TN69FV+F5HnNzc7iu28kS3t7eJpfLUSqVqFTasvLu7m56eno6sS6lUolUKsVcvkFppkhKESlZTbLR7J4F6+sRi8dxHZdyrYouCpRmisznGxzpb3etM5kMhmEwNTXF6OgogzMzbJ0q8+pDn2byV35jz2t47bXXePDBB5FlGd/335aQ3egyuhcxK5VKfPnLX+bcuXMsLCzw6KMfoFL0GD+QJhLRkWUZTdM6hOwqKXunYylf+s4Mp7+2ApKI8XNdPHn/INAmrW8nGb733nt54IEHOvvG3/gnj76j5/zriDsr4Q8Zvhfw5T87x/nvLiBrMpGsQRjA+lSBjZkC1VKLJz84wdDBHEMHv38HI8vxeOHFRS6cXKG6UAHXJxRF9JRO/4EsAzGVgaaLl40yV2wgCiLOVdVLCC3Pp9Js38AHuyLkajaRsSSJsQSvvPIKlUqF++67j2j03bkr+r7PwsICkUiEsbExLly4AMD8/DyFQoHl5WV6e3vRtN1kVxRFJneiFyYmJpBlmcuXV6HWJDWce8eDwIlUArnks3B5mfMzaUxNZ2W5jG25iBLkulQWL51G+IN/xZHGBK8eeJRUOokSt7l48WLnONvb25w9e5ZHH32Uy5cvAyBJUmehUhQFEZHim0VmT21RariIgkAm5jH5QIrRJ0b3lpqtgfyITPehbq5st5gt2nRrMqulBhsVm7DiMxAKhJsW6oUSct5BkEQCTaLVG+H5jRqnlsrcP5rm/tF0x9G02nIRLa8tsbtuMylFFNS6yxtnLnFh6SVef/11fuVXfmXXHAGwq5M68MAAT4Zw+Y1VqmULI2swfqSLsUdH7pgc/Jhw5eIWX/rDt2jmG5g9EZSmh+35WMUWm9UNAj8kM5QgsFzc1Vq767PzXjleQKnhEPFCol0R4lEVv9BCihoIgA7ENJmKIrFVs1mvWEQ1uWM1HjQcmCmQvLuXAwdTxHIW25e2GU3ozFdalLbq6KqMnLlWIW657XVGFASO9MZQVwoMPzSOrbUlS6Io3mQQBbB9cZvNs5skRhI3beQrlstayWK+0KBUb1HdquIuNQiKFlrCIJU28D0LU9OQJZFoqk1+A9vHLbdozJZ4ca3G/JEsDzzhcORoW0K+tLREV1dXZ8MG8NKzc1x6cZF4Xwy34fLSly7RNxhn4mAXoigyMjJCsVhkdnaWnp4eHNehVqlhNywqoYMiiZQLeaLPPY8H2E88Dpa1ywyiuV3Ftls4LXA9jdnFTVqWiyW2Z3lFQUAU2yHFiiQTKirKjXNGpkGYo/09YAdUig2sloeSjqAaMoHfLvIZPUmEWzgm6wY0bR8MBWUP8uR7IUIY4pcskEVEU2lv+nbqWJbjU7Bc7J3ZcEmRwfFxPY/prRqiKJAMfHJVh7gksrBcxpLaVjpC3cMVIX5P767NX9320HWFvrSJHtUZfXKUSNfeZhd38MNBrWrTarmk0ibyD7FjGoYhS+s1NlarhEBXb5SRvsSeQdi1Wu22c2XQdvXcXFtlZJ9H9eFRNi5XiWw3iXdFEHWJ0A+xiha1koXcF+Opnz/M+MDest/V1VUsy2J8fJzXXnuNMAyxLIuLFy8yPDxMMpnc83x6e3s7MS31lo9fatL0W6S60gQ3FIBvBUVVSKVSlKvbtDYrrG0X6VbsXURsdnaWL/9P/xN//4//mC/e+36yn3mQ9Y05NjavqX6CIODUqVNMTk4yNTUF0OmAXU++NE0jGo12fv5OCdn6+jrxeJxyucKzX5wjrMjoSpKnPnRr8vxOEAQhc1cKqAULH4Hpy9sdoheLxajVap1Zxb2gvg2Z/mnCHaL3Q8bF8xtcfGGJeG+MyHUSo0hSp7ha4+Q3Z5g82kPvLWx7vxdYjsef/+lZZl5YwpBFcj1RJEMGP6ReaDL7/AIb3RGO9iQYdQIyPQlWmg5XigFr5fY8gyqJ9CUN+jSZeMPDHE6SujdFbijHn/6vf8qBAwc4evTouzq/jY0NGo0GIyMjSJLEysoKa2trzM3Nsbm5yeOPP/62hG10dJQ333wTq2XTqLdIxGKY0ShWo9kJJ387qIaKEAr8+V+cI1yzMD0VMQBkkZbuYV18id8pVvn3fav8/P/r/QyN9DE4ea0LViqVuHTpEr/6q796W1fQrfNbzL6V55Ib4OgyCLDacnBeXCTaE6Xn2M3Bs4lEgmwmSzKZxKxXqFh1NkpNSnPrZByFuCgjmiqBLCJGVaSU0U5Yb7joFwuYEYVWtsG3S01WyxYf3ulaxnQZ35DbszxB2LmWVqnBsl8nWtRpXWlhmzb5V/N4wx7J4eSeUkxJlRh5bJjeu3s6OXpG2viB5OHcwfeOIAh44a+maBab9Ex24W03sS/liZkKFUkgqLsUpwskeqKExRaB5SHl2p/bMIClYpMwDMlIEsZADDlt0mpuE1RtpPi1gkvCVDBUiWLDIarJRLT2hl4xVAzb42BXlEQuQpA28B2f4myRMU1mU5aodhtURZlG2WqHX8sivUmdoYhGpOoQjsQZeXwET/Q4ffo0D9xgeATQzDdZfX0VLamhGG2jo0bTxfN8lkpN5sotGrYHvo1sBUhLFkLTx89ouAJsVC0kERRdxrzOmU7UJGRdRvFDhJzJ0uktmtUWtXodXQ/JZDLU63U2NjY669PCzBqyIhHPmJCBlbMbzFxZxojYu+4D3xf4wudeYe2lPNZ8nYYiUlMluoaTxC+eQnEdSk8+RfaGwgpAuZYnETfIxuJEe9JYgUdP2sDYo0DkyS7CLZZOAUCREBQJ2ddxJBs7IqMbCoIf4FccwpZHpVFja3OTkdHRmzdBQlvCuxckWSBsuPgVGzFt4K7VCKyrHeKAmu0SigJmREXQZDwvIJAEUlENRZdxvIA1u8b6hS1kPyRoeVTDsG1+4QVYxSblhkPqcBfxjEnT8alaLof7EkhVm8SBLGbuTibejxLffWaGN56ZwbV9ciNJfuaXjpHr+sFH66xt1/nmV66wem4Tt2oj0Jbw9h7u4ukPTzDcv5uANRqNW7pbXoWu65TLZQS3zq/9xnt56eVFLr25zuZKFcEPCAElpTP8vjEeeGiYgyM3dwfr9TorKyv09/fT39+P53mUy2VmZ2dxXZejR4+SSt3alCwIAtLpNIuLi1yeL9GqN+nNpZAMnVqtdlOh+5YQIJaMUS7WmJqa4XB2EsMwOkTM3Njgob/4C17I5Tj4b/4/3HvffTcd4tVXX+VjH/vY92Waczt89KMf7fz7P//eyyxd3MRqNb7v44qiwOhEhtPTBUIhpLv/2v46nU6zsbFxW6L3Nwl3iN4PGRfeWidw/V0k7yqSvVHWL25x8ezGOyJ6vutTnC5SmC7gWR7xwTi5yVwnTPvb355l+rklct0RtOsCtpEg1hsjmo2wOZXnoq7w+JEe1M0GUT9E0wV6euIQhiiBgGZ7qKJI6mg3vff3UmqWeO2114jH47d0lrseddtjbrNGs+4gySJxE8JGka5cDk3TuHDhQsdtyvM8KpUK5XKZqampXXLG62fNoN0xE0WRUqlEGIakM1nccANFbrt3GryzL/tSsUkz3yCbNEglo7Qkh1QmRegGrD5/ktyqzv985LMc/uXj1KVNEgNjHWLUaDT49re/zVNPPXVLknfV2GX70jaLjRZhTKNvR46br9ks1hz2X9ym+2j3TeRIT7XldO0MMKhaLqw3SGw6OIFFI6MSj+YQA5ASGqImARKioRBmQoK6i7pYJdf0uOS2h6x+5u4+xrtivDKaop5vEV2qIpoydrXJ7OoKdUXmvV1dXKpGkQWZ0myJ7QvbRHIRhh8bvqWLphbXOgY9d/Djw8J8ic3ZEon+eHtWL63jJXWSJQtPlagbIUG5RWWzjpm3EDQJgXZXrdR0CcOQPkFCiWnIGRPRVFBHE7Smivh1B+k6sq/KIjFdgRDGcxEIQwrbTWpFi5Xz28RyEURJJHc4RxiEbJzeYKQ7hjCgESZjbblxCEYAuuUhuyHpI91IYxJqVKVRakeq7JWDtHVhi1a5RWQozpWLWyxO5anmLWqWS83xUFIqiR6DRCbO8qVV3JqNnFQQEFBFAd/3CJBYLzWwWzLxneILQKiGBPkmUlQg1h9h5cIWtWaFn/nFSfr6+lAUhUwmQ61Wo6uri30HLdbOnqe8XsO1PIyUwcG7RhgcvGZAUCw0+Mafn2HlXA234aOEIVFNplix2X5hioonk3zvUyiJONVKlXgs1pFuNps2nuXSnYsjazJKzsBfqyLrt2BbggDhtXD3W0GWBMLwWnSqIIngh4RuQDQeZXp6miuXr5BNZ1ECk/pGex0XBYHXNhqMjGcYGE6i7pBNp+6gtTyEzQZew0UWBbwQBFkgIKRme/hBiBYKUGrP64lNF7UvilB3CGWRVsnCXq7iWS66H5LqMql7Ia4XACFi1cGaLVOvuWj7UyR6YhzqjTPogRrX6T3ee6fI9CPE5QtbvPTFS4iSiB7XWDqzwdc1ib/9mw/+QJ9nq9Dgz//zWxQubpPqiWJMtO+tVsli5aVlPr9V5+d/7T76u69t5t/u838Vm5ubdHV1kYpqfPT9Ezz22ChziyWaDRdFFunpizOQMW86VhAEnUiBoaEhFhcXaTQahGFbSl2r1Trunqurq7d8flEUUVWVarVKs15DMTXCnaFooa3xvmX26Y0I/RDN0Ln3nruxrDKqqpLJZGjMzWF+8pPUDxxg7u/8HcKtLVZXV3cZxJw+fZr+/v63JXmnT58mCAIOHz7MKy8sc/mNNZ7+2UOM7//eyOHP/737mJtd57/8X/8aNbLNo48+2o58eJf40HvH2D+eQZIETBoUi0USiQQzMzOcPn2a6elpnt7DQO9vGu4QvR8yytsNlMjeOVaiKCCKArWdbtrtEAYhSy8uMfvqCpuWgydC/MIWozNF9n9oP15U4dJrK0R1eTfJuw6CIpIdTbO5XMH6SIyjjwyzdGaJ+ls1cnr7HCVVIjORITWW6gS9X5y9yMzMDOPj40iSxNbWFkEQ4DgOtm3jeR6CIGC5Pqemq8xfLtFaqIMT0HJthJRGejTC/n2LjPcmGBwcRNd1TNNkdXUVSZJ4//vff9MM3I2o1WpMTU3x1FNPtc1BPv8qNRmCmo0oiIRBgPA2HcFGw2V7uoCqK/Tdn0E1FBIhbOe3KZw/z8TSZbYGhokkRqluJGgNCFQqFZrNJplMhmeeeYaHH374ttW6Z599lnw+j3hewk1PEEldez90RcKTBOymQ+iHCPLu1TwxmCDSE2FjvsyiZTPoBIgNj3JMxg5cWladVl4nlk0i31A8EARhx4xBxlut0xXCJUmkK6bx1GQ3Txzv59t+yPZcEa3qsK65NHUFbXWT8GgvBz/4XlYLFZZkgd6+KE7FZu5bc+z/8H7iA99/x/kOfjioVWzcloexE28iKBLqeIrwSkC2aqNFFEplm/xWg3jZBk0iqNuoioQuCUhOgJHS0MZTnVkouSuCFoQ4c2W8YgspqXWKHaYqUWo6rBca5OdKNApN/KbLxkadkhDw0HvHaJUsFEPh6GePEu2NMvPaDFFBJnCCtqGHoZA51kNqX4pod5TVtfamqFQqMTk52TEycBwHx3FoVVrMvTpHzfKY+osFqptNBBF8VaLseuD7CGsO+bUqeX8TATD64whie10AEAUBRVVxPJ+qFxIRFSLXzQwGKQlqIUKfRGYkSWvLYW0JkqlNBgYGMAyDra0tAB5+fIxKvsHc2U0iaZ0HPrCfsX3XSJ5te3zhv55h8dQaXeMZVC1L48wGpbUCmlPBwUFI9LC92KI3nSCailCt1dpOcUHISr7GcDKCX65jjqSQYxoBIeItNrGiLoEkUC6USGVv7g5eRVSTEQTwvOtDlNusT5ZldFXDK0J1s0FQK4Mo4AAxXWZrusDWTJGLWZPxu7roMlVKMyVaxSaqJOAIICU1xJ3CgGV7uJKAriuEO7tW3/XwWiExSaR1YRtfk9narhP4Idr+FO5mk7DhkTQUHEOi5fp4howEuLaHsFBhYjhFvx2gxFRGnxwl1nenav+jxNZGDc9y6T3UNq1wLZft5Sq+FyD9ACWcL72wSP5Snt6JLNJ196mRjaAlDDYu53nu27P84i/e3fndOyF5CwsLpNNp0uk0MzMzRKNRHMchKjmoukMQBFQ2ClQ2dv9dPp/vdAyvuu/29fUxNDSEqqrMzs6STCYZHh6+afThRoRhyNzcHAcOHCAyAP/l3Iu0tppEsyamYdBsNjHfoULJazjQbRI3Vfb37Wdjc4u3nnuOgc9+FimToef553lqbY21tTVisRgzMzPs27eP6elpDMNgcHDwtufZaDSYm5vj0qVLfOELX0Bzj+FuiqwslL9noheJqNx1ZIi+vl5ee+01JEniqaee+p6OcT1UWeTQ8NW9WJLV1VWef/55nnnmGXp6eu509HZwh+j9kBGJa2zZ/i1/HwSgm2+vFa6uVFl6c52Ljkve95Foz2h40wUiPRG+cOkNpt8sc/yBW2TOhBAEPqEcItg2r744Q9+nxtjSttBP6KjZ9jlIqkRNqFHL1yDf/tOvfOUryLLMfffdRyKR4OLFixw7dox4PI6qqsiyjOX4fP6bUyw8VyDa8klFDOpqg6wcw6t7OBddVsIEx++doK+vfWM2Gm03SUmS3pbkra2tUSqVuOeeezqL+fsfO8IfvPUdEvkm0ZEE9Xqd2B5OVdejMl/Es3wy9/Si7hixBGFAbWGR/VNTbGUzqA8/QE/Do7HqciFe5qMfeIpisch//a//lSeeeILe3l6CIKBarVKpVLCs3UQ9DENkWWbi3gm21w3WWu3gdATauU9eSKo/sacTnKRKDDwwwOv/v7eoLJRI1F3ElI7ohRTyFn3xXhRDQxmKc1Mi9Q4ESUQeiOEuV0mpIidTGvtyUe4dTtEV17gymWOrZnOgYiO/toZ43wintmr4ro0kmpxfLTO7LXPPUIpYqcXmuU1i/bE7VfOfUOi6jKSIuC0PdYeoSVEV/WAGYbZMvGAR+iFduoKs+chpA0UU0G2fuUILLWmgH8wg3eAqp/REETUZZ7GCn7cQdAkx0nZeFAWBxfkizlqNFhaaKqOJGitvbnDOFxg+mCb3QI7Yvhhe4GH4Bql4isANEGUR2ZARdIGyV6ayVmFlcYXluWVS6RQb6xuEYUilUiGbzZJKpSgUCwSWxNxMiWbBITuUQpRE5rcqoAhEom3pcFDzCGZKBFkdEQFBEAnEtoOvpraJsCpLeI5H2XIwNaNTOBdMheZaGaNukupPkm+UufDKOnef6GNlZYWBgYGOjbquy3zqs8exLBdFkW6aUbp4doPlC1ttkmcqlEsl1uxNoitr9FarbB0+TEs3Cbab1DYtUEV0I8LSdoWW7XBktIdofoPF0hbbc5sIxSh+dAhfV/ac7a3ZTTa311FVDW6z99IUCVUWae0QvXa+fVuXGfghUS+FVaoTqgFq1sQD1CAglzJRZRHPDaitVTl9pUBX1mTkQA4zY5LIRsg7AVbDJRLVCGmbr7THOHdInudhFRuIcYXocBKBkPKlPGK+iT4YIxQkvKyB1fTQ3ADd9tFFAd9QwAmQ0gbNjQabZzc4/OkjDDwwcKcA9WNALK4hSiKNkoUe1bBKLXoOZN8VyXMcj/m5IoPDSXRN5s///M8JgoAPfuyTTL+xSiyu7SJ5QLsuIYaYCYXpk8ucO54gborYts3c3NxtpZvLy8vMz8/z+OOP4/s+6+vrHDx4kGQyiaqqqKp6szGdZXHp0qUOictkMns6YkajUUql0p5Owbuv2WF+fp6xsbF2jq7hM3jXAPmvX0apWWgxA9+69Z7xegS2T8MNSI+nSaky/+2PTrN8fpXg2W9wj7mfI5/7P0jH2t/d6XQaz/MYGhri+eefxzAM7r///s45XS1oXw9BEIhEIoyMjJDP5zlw4ADf/MbLfPCXf4F77n93M3aCINDb20t3d/eeEv3vB5cvX+bChQv09PRgWdbbvhd/U3CH6P2QceDuXubeWMNpup1N2FXUixZqRGHiHdi5NrYaFCoWxTCgL2kiirBdszm9usqXf/u/sJKV6A0mqVo1sHaUPNfN9AoC7cF9SUTWJZy6TyaXo1qtMjIysufCBXDx4kU0TePJJ5/knnvu2TmWgCAIu6SLz725wpVnZonaDnXdRtcNMt3dHUOFwHIpXC7ytb+4gPb4PqKiiOM7bG1udYKK1ysWC4UmrufTFdcZy0ZRZZHLly9jmiaHDx/edW4jmQgHHxjnyufPMlLS8eWA28GzPCrzZYSYQvLqPEEQMnf2DMOnT9NIp1GfeJIQaAQtNE+nWtJ44623mLp8mWg0ysWLF7EsC1mWicfjndD1qwiCkPnVKrJokhBNYifX2b60SMFvV/u7nICDQ0lyh25tvGNnDfLjSaLLFfxSCxI6kVDGjPciJzXUwQRS5vZyB0Fs25ILBYvqtsX5tQoj2QiDKZPBVPt8F59fZC2iUUrG8KsS3XEdeceKP1+zubhe5bH+BOWFMlbBwpEFXn1ugXKhSe9QgoceG0FV7ywhP26MjqdJ98cprdfo2netkixGVPTDWZqXC5iayP7uGOWSTUSSEGQBMW1CUkXPRZDMvVUHUkpHjyh4Ww28zWb78xiCFPhsLxTx63VC0UOUdUJDxgkDyprHQ0/2kexPomkaitKe5buxchz4AbW1GsXpIvU36gR+gJbV8BM+uUM5SpUSxmD7c97YarC0UKayVic1GKNlNalYDrYnEjMNpJ3NWavVIlRFhIaHW3VQUwYCwq78JABNlrAcn9ZOREgYgtVqoaoq0o6ZXLIvxvZMgYWZGpNHUqyurtLT08P6+nrHEdQw9n7dzr2xBoSdNT+eSFC3inRbq9gT99BlxqgKHlVDorpSw42LmF6IEbYYjysYi6uEIgiqgLfp0eNH8Ner1IQaZkxDzpnIaQNBbbsmb+W3aEk+Zvg2YeAhxHUFSRTajqii2A4lVyXyCyVaSw3kqIpsqrh+gOMFZKMq6s4mXrA9TNunZftsbTdIDCSIySKGJpMZiFN0PGrbdURNxvMDVEUi8AP8potrOQQGEGnPE3p2iO34CIaMVLAJXFDTGs2YQjqmI1oeQdlGcD38lo8sCJgTaSqaTOy+3jsk78eEI8f7mHlkmMuvLlPdqJPoj/HUJ99ZqPmNePavpnn969MEyRpvnP8TqtUq+/fvJ5SSbC4VSCV1ioUbslkFAVEUEXSw8y2azYCRgSxhGHLw4EHuuuuuWz7f4uIiH/3oRxkaaudC9ff3s7q62ok6uB62bXPy5EkADhw4sOdjbnz828kQK5UKhUKBiYmJznqkKxJ3H+vhmbfWaa1XEbX2iErg+4hvUwD3t5u0YgonJnN84y8uMPPCPNrlUwh1idce/ftI2yK1K1cQBIGhoSHOnTtHOp2mUCiQzWa5cOEC0WgURVFIJpO7rnG7bHHu7AYry0u4jke1kePZF98iGoNHnxi77Xm9HT7zmc8AcOnSJQ4ePEi4YwSoSuJtg+nfDidOnEDXdV5++TXsai+X3rLp72uQzvzNNmq6s0v7IePI3b2cP9rNwlvrxHuiRDMmYRBS2WrQKlvc9cQoI6O3lgF2IOwQLCDcEXAHIXRls6SOHaXZWkcqSCRTybftvLiKi6prRAwTy7Ju6aDZarU4efIkhmF0SB7A2NgYb731VudnW+Uarz57BWc9jzeWYCA7eJO+XDQUkimdzb+a42tXimQSBqrsImy05/Beny/w3W/PUZ8uIDg+Ym+U/Q/0MWJUODQxvqftvyyJvO+REUpbDZafnyGd0HFNF0W9efMVNBzKS1UamkRuKEFUkyEImblwgcFTp7EjJvYjj5LLZjrXbtsNAivCd194iUcfvJ+HH34Y3/e5cuUKg4ODN83ozSyU+O43p9m4uI3XbNuYV0t5NLvJE+MDiIJA10Cc4bcJ753erGNpIt29UYKUjrCz2IuG1O663JiLdguIhoy35ZOo2FzZrPFwwyEVudY9tooWsiGTr9nIotAhedA23ig1XCxZQLBc6iWLr3z1Cgun1pFUiSsvLlEtW3zs5468o3O5gx8eVFXmvifHeOa/nyG/UCLVH0dSJAI/pLLZwBHhwV+5m6ODSaa+PEVyNIkoi7iyiHhpE+FtKvGCKiGlDZBEyDcJKjZhxcMQZATdxBYdhLiKkU3iJFSGH93P0OS1gM31tSqvvbjKGaWC6/iouowui8SrLu52g0LN5kq+SCydYXWjSnwdNmaKxNIaF4sXmXxkkoVzq6yvlEFtR7OYpokcKCih2yF5YRAQ1GzQZPAD/EqLMKXj+R7yDRsmSWyvOw3HQ5HAsR1Mw8B3W53sOVltd+EXpvPc/5729WxsbBAEty8oBUHA9lIZPd7ukPqehz0zw8DlK9QeOIZ58DDOSpV0xUb0m7g1n7EgQlLw6M71oKgKlmGRr+eJ+THSg2miiSh+2WK52EAPwC40cU0VqctE6Y3S199PwzcIZ2u7zJZuRMPxSJoKw5kIi/kGxbKFrinIApTnywiKQKgpNJ325jodUUjuKE78uoO7WgM/RO+O0Cy2WFsqs384iWd7dI0k6ckYbK5U2Vit4jdcPMEDSUA2FeLDCfS4SrVewbJaeI2AwPZREipBICBWbeQgwE3rIIltaXpShwC87Qb6oRxSxmDt4hYzV/IMDr+D7807+IFDlkU+9YvHmHtwkJbl0j8Qf9cbaVmVkFWJroFeQu1eLly40O70PPQAMy+9RCKqoaX2Jk9uzaGpQyqdIhaLsb29TSJx6+/Ver1OqVTqkDxoOy9KkoRlWRiGQRAErK+vs7W1Ra1W48SJEyysNPirry3hhYscPd7Hvcf69jx+o9G47fOvr68D7f3TjZjsS/DW3T1UXlxCWKug9cZpNi2isVsb3HjbTUqeR/r+QbK6yvOnVojOnSVdXcP/xM9QyItMXdhmoLebkydPcvfdd7Ox0VZLfOITn0AURVZWVlAUhe7u7s5xwzDkpZMrvPzlK9TXa6gAgkCz5VB0TaIjJn4QIt1ijfleMDwywtdeOMf6akhpu4ERUzl+bz8nDnV3ikvfC+LxOA8//DCaPMhX/t0bnH1mnlQuxvs+fOCmx3pewJVLW3T1xMjlfrqJ4B2i90OGYSh86leO80zKYO70OhtX8ggCRNIm93/0AE995MA7igaI9kTJpQ1y5SbrFQsB0CSJQVXj+K/9LO/rMfjv/+tzOJW2FOtWCIOQVsNlZDSJKAp7ZuhdxcmTJ7Ftm7vvvvsmF7bBwUFefPFF+vr6OL1QorFQYXCsBz1364UpsD28zTrnEwrdaR1no0y8Fmdmaos//9YU0ekG3akogqHSnCnw6tIWPb/x8C2z3QCGMxE++TOTfEkWWH1hlujFDXLdqXaHYicnqlVq0RChNRzD0AVymkoYBsxNTdF/6hSBKFJ/5BF6eq+5YGqahpTQKTbLVGbn+Me/9Q+BtiHM9eHqV4eYZxfL/OV/fpPmUpVkbwS9K8Lli1fwWh6BEqXaHeFDH5wgmjM7Afe3wlKxiVGxCSwPZSj+fUkmpYSGsNGg0hNhu27vInqiIhL4Abos4t9g6ez57YVcRiAURTY3aqxeyZMdS6JHNYqrNa6cXOP9H5tE0+4sIz9uPPDwMJ4X8No3ptmaKhIKbXt7M23w4McP8tSHD9BYr6FGVdSYiiiJCF6AIgo4foDB3p/JoGbjbjTw8k1Ce4fgyAK+JhHpjuJvNtAVAwSReqmFeTDN5LEefD/g4rkNzr+xxsKFLSqb5Xa1W4DQ9mClhuAGOP0x3LRKqCs0fR8hBNfziaoyw5s++vImG0sbfOOLrxDWEqT2pYmY7fXNqjd3bTYCxwcvBE0CWSRseu0gcdHHtttumIpy7fMviQKNlkNUAXPnmIIiEdo+oR8gSCJqRGNjoUwQBMRisbbV+9JSe4bmNo677YO1JVH+2hrmc88THj9O7KG2YYWR1AlqNs2ZEKngIMoeiS6d3oleqqtVfM+nZ7yHVqVFGDfIhyGeLtFSJeqaREzToOnizpfxyy2CXo3cvj6s2mY7nH4Pt1wA2/Xpz0XJxTRMTWKt5VFJaCwtlWmWLMSUjhz4RHWZmKYQ2SG7QcvDW2+TvKszeFpMoVm0qMQ1CMDMmBhpg1TaJNEX48JimagmIckimqkh7nzEcka7a1Cu1HZeJxEkCCIKYs1BBsLMda+tCIjtWBhBbKtJrObuDK87+NFCkkX2T3z/Lo2Pv2+cA4e76emNIctPUqlU2hmaPQmSg0mqUwVytyB6te0G8b4YAztGdm9np//6669z7NjN+X5DQ0O88sor9Pb24routm0zMDBALpdjcbXCl//wFNZaDUEQWDu7hfrrMkcm2yosy3I5/eYqPf0Gpmly+vRpXNftqJSqVtuheGFhgUwmc0simIlqfPiJfXzB8rDeXCdcLONFhT2JXtDy8PMWJc9DO97Dh5/cR2F5DW9qmsjGGu5HPoDW3Y1Q2CYajzE6OophGIRhyOXLl3n66ac7e76BgQGKxWInXB3gzXMbPPsn51Asj/59aYQdqXjoBaRWo5TyFt99cYGnHvv+ZZFvTZc5+bVV5HwLXZGougHfPLvJ9scP8vH3ju0ZofFOkM5GiGZNXMcndQv102svLfKdPz1HdjDB/+2fPIr0UxwNdWeH9iNAMmnw6b99nK0PjLO+WkMUYXAkRfI2hOxGxHpjDJ3ox39lhUzFIhAFosDooS56jvUwkNAYPj5E8fQGubh+y4puq2hBTOXovf17/v4q8vk8ly5doru7u6NzDsOQzc1NqtUquq5jGAatVgtVMonKGmr09g6MQd3FEwXwoSumMl2RKZ/b4t/8uz+hVUpxV7YLpSdKo9HAjUNvU+fSVIH3Huq9bfVovCvGL37qCCcPdfGNr74OjoBQsdraVVlCnkgxcriLg/szPPf5i1hXCmxZW3SdOo3kueQfe4y+waFdxwzDkHrVwhzSOTL5AN/61rd48sknO1LUffv2sb6+ztLSEgMDgzz7zDTNpQo9k7lOPtnksUNsbW9R36wzdWadY4+PMKncfjjYcnwKDRu14bUlVd/nXJwYVQk2G9B0KDacXb9LDCXYvrhNb0+U2XyDQt0mYbQlW4W6zVguitxwkVMGZtZEFAV8byd7xw8QNemWlut38KPHw+8d5ejxPi5d2KRRs9ENhYlDOTI71XY9oaNEFNymixbTUGSRwbTJhbUqiRsliH6Is1bDXakS2gFiVEGMa21FQQg0RfZNZGhuNdlcKOMVLLSoyPGJHElT4cufO8+55+YJnIBI1iS3P0UsHmtndV7O01AlNgVwFstIqwLJiRSprISmawS+z2apzinXZ78qc/HPX4OWgOu5HZJ3tS5x/f0ReAFCEBJeXSuCkGqpgiN5hEGALCuIoogoSgiCgO/5iLKIfqOVedvsEQDFUGjVbCzLIxJRO87DFy5c4MSJE3u+D6IokumLMfPmMgYN9G9/m3B8H8KD17kSiiAmNLyEjtoT5UO/8xBzF6+wNbuFUBdIj6dZLFrkNYPipU2EpIEkiViOT7HhEBMEpHILyQkRl8qkW1nCeAypy8SdLyNElJvWjrrtoSsy6R2ipiPQmzI5dqKfl15fJm8qxNMRFElAlUXCMCT0fIKWh7tSw685iBGFwHIRZBFZkbA8m8p2k6gh73Lgjcc0IkkNVZZuXZm/cfEQBTxdRqk7iHUXUjsbTD8EkV2d5zvzwj/ZsG2PUtEikdRvKW8GkCSRgcFr5CeRSHTI0N0PDvLt6QLN7QbmDR2XVtHCcnzufXAAc6fQWK1W6evbu9u2urpKLBbbNaJSq9XI5/MIgsDAwABbW1ukUikOHTrUIUKLc0Wa6zV6D+VAgLXzW1y5sMbhA1lEUeTSuU1e/fo0+dYC27WTxGIxotEoqVSKN97a4vVvzNB7OMJnfv6Bt81tG++K8smPHOCrhkzhzAbuSgGpskE0G2+78QYhXsOhEYS0Eirpo4N85Ml9iPVt0p//I4aXZ1g88BgZLU1jrkSoy0xMdhEEAWEYsri4yC/+4i/y4osv8uSTT3ZIcTqd7oSrD42M8eqz8whVh9TBzK77TJBFEsMJgtmAt56d4777+km8A3+JW6HWcnntuXn0gk1qIoMgiYRBSH2hzPnnFrjnWA9D6XfXaRvbl+EX/+FDOK7PyOjexjiaLqFqMpqh/NTvY+4QvR8hurpjdHXfeqN/+cIWtVqLQ0d6iER230CCKDD40CDRniiD82U82yPaGyWzP4O2Y93/4BOjfG2xTGm+RGok2SEcV9EqWBS26kw8Ncb+t5G9vPTSSwA8/PDDrK6uMjc3hyAIuK7LSy+9xNNPP008HqfRaJDO9BAKtDdIt4EYUZBDcFyX83OrJCQZNaFTMRT8NQdBlijkC6iaSro7R2WuhGv770gm0JPQ+dgDw0z2Krxw+gpj40chFNB0mYG0SV/CQBQFFidTvPDKDH1LZzDqNTbe8zD9Y/tukppahRrFlsOnf+Zp+pI26+vrnDt3jt7eXnp7e4F26GmlUuG5V86ycX6bRHd092suCnR1d5Pr6mLjwjbnT60zOZbhdnD8ANcPUdwAfhAVJqn9BRH6YHu736DkSBIza+LVHe4ZSnFxvUqhbiNLAqO5KHflYrRWq4w8MULP/ixj9/Qx9coSlZUqki5z/CMTd2b0fsIQi2vc/9DQnr9ToypNP+Dcdxcwe6NMHu5iIGUyvVXH2plVA8ALsOdKuGsNxIh8LUtvZ46i1vKQJQFJEBkcTTMwnKS8WCE3kUFpePzZP/sOqxWb2EiCWMbEdZxOCHBQd/CKFhVVwpcFjKhM0PSpTZVwLIfcRApJkuhNx2m4PiuNFgomGVOhGLQVCdg+vu0hFq12h0kWQRERbR/8cNd8sqIpiLJMo9FElNodbN8P8DwXUdaQ5T06mQLX1oOdWzAIrnW84/E4hmGwtrZ2y41l3z6D2ZddxG88T9jdg/jEEzetMYEf0sg3mPzYBD3dGea+Dva2DRmRM6tlprYbSHGVRFkFWUCQRWJpk+Vig+Z8BaXlIekKsu9TmS6ixDTUkST+tkVQdZAS14hX0/Hx/ZDxrgiRnXvWKlhEchGSXRGUIMTU2y6kYRDi12z8sk3QdAkqNn7FRtBEfNtHIGyvTYqIaHu4ukxkOLkrCsdUJZKmSr7u3JLoyTsGVfjX1jpfAFOTCfJNwoiCoEqEjt8uKhkygR9CGBK5RcfyDn78WFos8eU/OkN1q0EsY/LhXzq6y5H2neLBBwdZX6ty6dkFqvkm0Z3OXrPcwpcEJp4Y4dHHRjqPb7Va6PrNMVZBEHD27Fne9773MTc3x+LiImEYsry83MkEXl9fR5blTldrc3MT27ap1EoEooBTsZEUkVAUEOW2OVwQBOhRl+yoyr7cfs6e22RzcxNFUdpZu+cXKKyUGDjU/Y7Dufflonz2o5NcvqeP02c3uPzaNHHfQfZEBFmC/vae77EjPezviVLZXEF/5hl6fvdfYP+/fxel+17y8yWUWIT73zvCA/cN0GjUOXXqFE8++SSJRILl5WXK5TKFQqFzvYZhsG/fPp557izbV7bJ9d7afC3eF2NzqcKli9s8eN/tGwa3w2bVpr5QIZOJdPZNgigQ6Y5Q3ayzulp710QPoO8WIfdXcd8DQ/T1J0hmjHekqvvrjDu7tJ8QnHlrja/9l1M4NZuZR4b5zN+796bHCKJAel+a9L69KxT33NWD9QtHeOELF1m7nMcwFFRTIfACmpUWoSEz8eQon/jkYWSp7VK1VzDn9PQ0y8vL5HI55s4sc+aFZbpy3Rx5cIDn3voWq6urXLhwgcnJSdbX1ym5FmFCwS5aGLeIkgCQkzpKXGNUVYkEsK8vx7K5xV2/9H4+/yfnKc8ViA6n8EWR0swGhRDGDJuFuRkAFEUhEokQiUQwjL1vzvGhPpx6mTAsMjk5uesx+Xwew6hhlOZxCwHL999Hz/j+myJrnLrF5kKJfU8d4e67+jCkgLfeeovHH3+cpaUlzpw5w1133YUkSSQSCaKRDJX8BTIHbw5Ah3b1WdEkClv1W742N/8Ru9103i3Ca/+9cdnWYhpDjwwx/+154hWbxwZTtGQBCVDqLq3VKrlDObqPdCPJIp/67DHemshQr9l09cQ4cvfe13sHP5lwXJ9Tl7cpvLkGCQ2r4fDgYyMMpQ2mN+t0J3QUUcReqOCu1duRCopEEELD9qi2XOq2h+MGJA2FSxs1IqpEVpeJGjKp0SSXpvOsvbmBORAjutPhd1wX02hL8byChd30aCptQxTPtZEMCTcQsRbrONk42ZF21T2iyRRqAdn9ozgnV9AdqE/lUUIB/BDNcrH9tosnAcieDw0XXxLwRYFQFtCiBoqmomkamtbuFtqOg6IoNF0PXQpptVqd+ySoO4iGguy5KIKK7/jIqoR2g/PfyMgI29vbrK+vdwo/V7GwsMDxAynmr7zEqtpLz5OP3rRWBX7I2pVttIzKY09PUlurQQn0Hp0NV+b0wgaZmEZyJIXV9PFLLcIE+K5DRpXZ9gJcU8YTBQRRwvFDnO0man8MdThO61KB0PEJZIm63ZY5juSidO2QdqfuICoi6Yk0giR0wtaDpou33SSoOYRCu4ofeAFiVL0m4SIEPwQ3gGILYjqSKuHWXaSdLpwgCORiGvm6g+sHKHsUrYyERkWX8VoeckTFD0JEEbSIStjw8EotlO4Ige0hxXUEVaK6WcdMm0wevbP2/KTi21+8xNZMgeRggvxCiWc+f5G//48f/Z6Po0gin/rUYU6NZzhzcoXCfJkwDMkd7+HoiQHuubsX7W3GIADOnj1LPB5nZWWFUqnEiRMnsG2br33ta5w5c4ZPfvKTpFIp6vU6L730EqOjo2SzWXRdJ9fdR3HdZ+HNDQgC+o718PiTB0nHrxFKTWsrfPoHYjQaDe6++25WVlY4dm+KwZE0IyNRlpaWbjovXdeJRCKY5u68vnRE5T37c9wzkublUZWmG2BGosSiEWK6zEgmghB4LC8vkzx7ltQ/+kds/MN/yJH/8Xe4KwxpOj6KJHYKLG+88Qb79u3rjMDouk422zavuXjxIiMjI5imiSRJpJNd2LUrBN23lqVLhgyeT73aeqdv5d7HEQVQREJ/t8No6IUgCEjKD598vR0Z/GnBHaL3E4LidgOrZCFpMtvLlXd9nIdPDDA8nODsqXUuv7WOU7eRTJnh4z0cu2+AQxPZzgJwo6bd8zwWFhb48pe/jCRJPPngU3zhP5xiZrHJ4uoqr798EbVvm+FDw9i2TX9/P8PDw5x84022jvSy8Z0FNNtDvMXMllN1EI93c/hYlLtHBkj2J/nzb02TlBwGjqapKRGEsgvNkJZp0HtvHx9978GOS6TrujQaDcrlMuvr6x1ThOuD1UVRpFgsMjExwdmzZzl69CiiKLK2tka5XEb49/+OkXKNZ499gLhlUrlSQE5opBM6SUUiKDaZn1sjerCLj/+t452ZtqvD3kNDQ3R1dXHq1CmGh4fbrpuGRjQWpVysEMsl9qzeBV6Iqt+aBF+FoUhosogji8j+D4Do+QGI7eq7uYcte2Z/BlmT2Ti7QXWximh7hALIaZORx0foPtqNrLffT02TeejRke//nO7gx4JG3cbyA/Ssgdv0aJTbc2vHBpI4XshioUHc8hHXakjxNslz/YDtmkPddnc24gK9SZ1MVCMMoel4zM2XiXSZGE2HxUt55J4IsuPjLFfR9qV2mYP4ZQtLCHCdAAS/PQsrycgJhZbboDhTRElKnXmJiCrTQCbqi9jrBWxdIzOSAUlEM2SshkMgi20ZoO9D1UGp2kghuF0Gita+58RQwC40CSwXMRBwQwdRFFBzERRZ7VjDe80QORdtmw9YTWr5Kt0HUmxuru96LQVBoFqtYhhGh+xdDVMe6upC+PCH+Zjt8OW/9T+zPl9B1mXMlIEgClhVG6vURM+oPPqJ/fT2xZl/dh5JlKi6DjPbLXKpGLoUUm3WMYfjBA0HoRUimSqtVh1NkxH8AEcIsVsuQlQj8ANaWw2UkSReVqe2VIGUQcxUGEiZZHeUH57jYVdscpO5TlaqGVUplCycsg1egBBR2h3QutMmdca1tUNAAEkgcHx8XcLMGjgVh+ZWk957exFlkZbjt2dEVZHtmk02qt3U2ZNkETNrUF+q4msBXhgS02UUSSLUQvxKCymlgxuidJkEfkhtu8GRJ8Z+6s0T/jqjWrDQ4zqRhI5dd6kXLXw/eFczULIkcuJ4H/ce66WxYxAUUeV3NLu1tbVFsVjk/Pnz/MIv/AKKorC0UOGvvnSZ0xe+ydr6DPF4nPn5ee677z66u7vRNI1kMtnpDBqazC/87ePMPlIi8EPGxlJErvsev76LeNWJe319nVwux/79+297fq1Wi0ajQalUuqXB03BC3vFRcOjOpohGo9RqNQqFArn1dYxf/mW2Pv1p+v63/w3YiUO4bv91/vx5MpkMXV3XnN2Hh4dZXFzk4MGDHDp0iIWFBSRJYnBwEFkR0SMGgePRqNeJ7GHUFwZtO8DvNzOxL6mTnchQfGmZXFxDNBUC26eyUUMcNOnL3n4U6A7eOe4QvZ8QHDrWw5VT6zQqLU489f1Z1w50xRj4QIynnxqn5fnIooChSDe14mu1GtFolLW1NWzbRpZlNjY2iEajnDhxgtlLyywv5hFSIrGkgbMKn/z4k9z16PCu4xyY2E/BWWR7JEFxvkyqP9auAO88X+j62BsNCp7PxAf3MTrose/ufQA0v9JkbWWJ/8en389bCyXOXtjEtX3GR1OcOJDrkDygYwF8O3OWIAhIJpMsLy8TiUT4+te/jqZpCKLI6f/yBfy1gLMPPULqwCj+UgtnvYm9UqW2WGFJEUFzOfz+CUb3KxwauSZvHRkZYX5+nlQqhaqq3HfffczNzXF2ahnbS+BoInIzxLEdPNfbFXbqtzxcQiYOvv3wuiqL9MR1puMq2lLQMYV4t/ArNkJcQYhppCN7y0cSQwnig3Ga+SZu00UQBcysiXKb2Yo7+OuHZMpg/MEBLhUs5FaNgdF2NVNTJO4fSWOIAvMvLmG3XLSIDK2A7ZrdrhDL7UJBwlBJmEpb3SiA6YcYMY1GT4SXT68TFC26h5MIXoC1VETOGm2jjSCg2Wjg1C2CMESURNQd2aTrOu2cTw3cqo3fEEj0tzcYQtGiOV2gul1F1ANcFRzbR9Dax/RDaLg+qiQgiyKhIeNXbQRJQHcCgo06DuCWLCQvbGftie38Uglo1V02NhuYGYNo1kQIQqSohqKqKLJCiToHjw3fFA0RhiGWZSEIAoVCgcXFRVzXZai/n9bP/iza3BzhF7/I08lu5mZqTJ3eorZlEYYhRkxh5HgPT33wblyvwqXTl1h7fY1Mb4aZzSYNx6cvaSIIoMoKtXoNaSBCsFSHhksyGcdOO5C3kZ0AR5GRsxEcVcJeq7cNVfpjdEkSkZpDV3cMdWfz59keza0mqbEUqfFU51pSisJK2SaIa8g7Xb8wDAkaDuwYoOy6/pbXltRnDPRumUQswdpbayyf3cTpiVBseTiejx+GtFyfuXyDmC6Tu4HwaWmTar6JV2kRy5hEdzbRgiYRVBy8rQZK2oC4xuZUnvRggvc8+f19P97BDxdjR7p482vTbEzl8Z2AiadGv2+jC1EUiN2mUBqGIaIoUqlUKJVKAHR1dXH58mU++MEPUiqVaLVavPCNacrTLSafPsJv/t//DqZp7irMDgwMcPnyZQ4ePNj5ma7IHD6wd7TC2trarqy2ubk5PvrRj74jqaau63tKTa+H53lcvnyZTCbD+fPngbYhXLJUQvr0p1m7917sf/7/5fUryyiaTjYRoT/Znqefm5tDFMVOEeoqent7mZqa6lzjyMgItVqt7cnQ04eZi+BWbcweg0qlQiKe2CUHsgpNlKTOyNjtQ+HfDpos8dT7x/lS0WJjuogShHiA1BflqU8cRnQazM8XGR4e/qmXVv6wcYfo/YSguyfGr//OIziuf9vh5e8Fqizecj6iWCxy6dIlRkdHGR0dpWV7zM/N8Pzzz5NIJOjq6kJN6ORyGyxXajTwSBoKyfTNBjLxeJzBhEjykwd5/mszbMyX0NdqKIpE6IdYQUCY0jn49CiHRgUmRscBmJqaIgxDRkdHiWgKjx7o4j37c/hB+D1Z6wZBSLHSIgxD0kmDbDZLsVhkfHwc3/cpFouc/svv0FxpUjk0yXvuewhh3cLyoWZINHQBN6tTS6m4Rsi+u9P4m9PMzMxgmiaRSISBgQG++c1vYpomr732Gu973/uQojlOfneerXMX8GyBihMwYmaRkhLVSpV4Io5neWzPlkhPprnrHcqNRrIRziV1kkkNv2Ij7/GavxOEYUhQd7FHEqQSGt07cpMwCPEdH1EREa9q4wWByJ0q+U81RFHkk585xrET/RROruFvNPBdH0mRUBWRCUNFNjVauQj5pstmrYXtBaRMlbghY6gy8vUul7ZP0HBRR5Nku2NcuDSPGAQgCFRbdcrrW4QLGk6PjCiKRCNRamqFsOHieD66pO52whQgsF2qqxXS/TGCloc9XYK6w8ixEer1GldWS5TLLTxCQl3GDwMcL8QRBeTQQwoCFEKClA4iBDMFBEVGjKkQVUEQCEMIwwBNlVAEEa/lUl+q0NpqkhqMYe7MttVKFkZS5/Ae960gCIyOjrK11c4BnZuboyuXI/M//A+Ib72F9Mor9E9M4LoumVycg3clqJQtfD+kUFhjfP8wrldhfX2dqdenWHhhgZF7R9iSuhDD4Jo5gCgQi8exVRvXdZA3XbyCRddwmkqiAT6EikwkpnGgJ0ptrU5ff5JEbwz1WMjmuU2qS1XChEbohbhNl9S+FLnDubbkFWhsNpBKLfSUTsvyO5uC0PHbTqtau7PbftNDRMdHVEU8UcRXbPL5NXp7Myj9UZbObuLVW5iDCZIRFQFIGAqFukOl5WI5PglTQdpRYciSSO9ElsZyFb/W7jhrUQ1RFBCEELdg4fZGsabzZIZTfOSXj9H/N0Ru9dcV7//YJIapsLlSI9sb5dGn9/1Qn6/VajE7O0upVML3fYaGhsjn81y+fJlCoUCr1SKbzdLV1cXdD22z3e/w2Psmblkw7uvr21OSfSOcHQn41e7+9vY2fX1973ge751AluVd+XYbGxsMmybKxz9OdeIgS3/wp5xdbrBVreE4LkIY0BtTGNJbyM0Cx44dY2lpCVEUcV23M/oiSRKu66IoCs8++yyqqvLggw9y9tIsao9KcbWKmTKIxWKUyyXi8TiiJOFZHqXNOsOPjjDc9/3nWB4cSBL71Xs5e36TzY06iZTOXXd1M97dnhH0fZ/FxUU0TbvlPPQdvD3uEL2fIEiyiPF9tMPtqo3TcFAjascBbXV1lf7+9sBso9FgfXkd3/HRVZ1mqYk4IvJnf/Qqi2dKNNwaubEMP/uzP0NPTw9hGPL0zx7i2S+ewTQjHH3PEP2Te1e2Dh48yKlTp+gbrZB6cJKZK2XshoMoiYwNJjh+vI993VGmLrcD2K9KH3t7e/G8a4Gokih8T/ksp89u8MaLC2zPFAFIDSW45+FhUkmBixcvMjo6yvk/+0saJ6epjY0xsO8E4ekioSJiJqOYyQh+xQYb9GyW0kCUJVvkaM8AY2NjNJtNms0mjUajk62ztrbG7//+75Pqe5DmHEzcNUh+aZuV9Trb9RZG0UKSBQrTRZRIhMxEiiffO0blzQ22ajahFyIZEvH+OMmRJOoNnbb9XVFSaYNWdwT1SpEwpb8rl7mg5iBGFSoxhff2xlianSYn5ti6uIVdtpE0iezBLF2HuzryzDv46YYki+w/2MXwYJK5b81RuFIgMZxAMRVq6zV0TSKbjZB1fWzPJ22q10xarkPQdAkaLspgDGUgRqvpIFsejiqxtrZNc2sbp2ahXVxDE7vw0KgFPkJMRSs00TQVUW5v+Hedn6lgF1q4LY9gqYJdbJLpihBJGNRiKlFHwgpD5GKLsOUhByFSEOL5AaEkYkcUPEVC9UJcy0EIQAgDBLntYBuEIUEQoinSztyYgGyqoIf4m3WKpozu+yieRHW1yqH3jnbmOIqFButrNQxTYXgkhSzLFAoFAI4ePUr9t38b8Y//GOm734WJCTY3arz12jKbS1XiKY3D9/VjmDUO3/UIsnztfltUFpEkiWqtQV1rIQo3fwdouoY60kVVKyJu2/hFm5iq4htgBQF+EGIoMoEmE1UlzJ2Zwu67ugm9kO2L28i6zMCDAySHkwg7mZleyyN/JY8oCwwd6mLm7CZWtYUR1/EcH8f1cAjxgxBcH8EPwZAJgxA1qiAbdep1mzfPnEdMDhIkDfRCCyUX6cz0KZJIT0InG1XZqtmYisRAqh1yH1ElorqM1Z9gbalMcb2OVWoS+kDNRogoRDImd71ngBMPj9DTe3vX4jv48UPTZJ7+8MG3f+D3gDAMd30Hep7H+vo6vu+jqiq+7xOLxajX6zSbTVKpFFtbW3zkIx/ZRbyO3J1j4KMDt+0QxeNxtra28H0f6TaB5SsrK4yOjnayNcfGxlhbW/u+r7XleNQbDomYhiJL9Pb28sYbbzA5OcldIyOUjh/HjcY4+Xv/jTenCsRLNn15C1oefkqn4MlsSPArjz/GYE8cVVVRVZVYLNbZx0iSxGuvvcbQ0BDz8/NsbGzw4suvQHg39WILvwvWFkvEdIVIWqe0ViSwwHZDuu7q5sM/M/muow9uRH/KpP/RvaMaJElidHSUZrPJzMwM2Wz2toquO9gbd3Z3PyXIX87zxtenKZeapNIm935oP8+df44vfOEL/PN//s+prlXJz+QJtgICp52d5xU91vJFTn/5IjXRwfMgmuqlp6ddwRYEgcmHh1GzHkODQyjmrTuNMzMzfOMb38C2bX7l05Pc88Awju9jJHWyAwlcz+WrX/kSDz/8MKdOneLIkSNomoaiKLuI3veC106u8K3/dga/5hDLRRBEyF/Y5uvTBcYfznDP8TSv/Of/TPaPv8h3H/wIifHDyKsOUsZAvO5axIiKX3ewL+bJZAwWRI8V1293IaLRTqD8e97zHk6fPs2xY8c4fPgws6vw4vmz+JZHRDfIjCjEDvkYXpRmIyQlmkiVAgO+xNwX3qLlBHiyQAjICERlmVguStehLvrv6SeWaVexkqbKsYEk392o0x1X8TYbKD23zifcC6Hj4xdbNEbjSJrPlVee4aVX53lo4CESmQRaTMNpOMx/Z576ep2x940h38nD+xsDNaIy9vQYiqGwdWELSZWwChay0f4MFBsuLdcnfUNkSugF7cKIJKKOJlEGYgiSiOf4iJaH4gcEDRu17qMGCmzbqPMWUl1GSukImoIkgimKtFwf80bXVlEEz8PNN3HWahDXiAsC27LAqiZjmioJQ4b+OI2SjWc5+F6IFfhYoohkSNhlC3muiuD5EFGQ3ICw4eBIIoIIuiKhKW2S14HrIyV0Wq7H+tlt9LROaiDBUx87iO8HfOtrVzjz3AJW2UbSRHrGMzz0dB+iKJJOpwn+9b8m8nu/R/2P/5jm8DD2YonP/6c3KS5VUA0Ft+Vx/uUFnvj5I+zbt/uaDx48SHe5m8xEjq+fX6fRbO75ngmiQKI/g5Vo4mzWUWsBYdVBtBzqbp285GNtNnFed5A0icBtG9WkRlNM/NwEftPHrbqUl8pEuiKoEZXKUgUrbxHrjxERBVw3YPHSNuXNOm7LRXB8xDBECQFJwDNk3DAkVCWU7ijDg/30JlRmN6tsNQNS+5I4U0Xc+TLSXbldsnNZEsnGNCzHJ66rxIxrr4NpKowfzOGMpclv1mls1JFjGSI9UR77jRP0/QAy2+7gryeazSb/4l/8C37rt34Lz/NoNpttSWEigSy3Z9gEQeDgwYOk02m+9GfnmJ85xxMfHbqpuyZJEr7vv60U8Oqoxvj4+J6/d10XWZaZn58nnU6TTCZZWlp613sZAM8P+O535jj74gJ2zSHeE+WBJ8dIpWySySRRVaXwnveguy6v/fs/4s31Ol3rLYSpIr4stgsr00VycY3SwTTPT+cZyUY6WcmyLHfiK7q6unj55ZcZGhoiEolw9913s3/iAM+9WEamxqMfGuGNly+zPtukVqhCAIEJg/fEefiRHpxGnuXmNW8EWZbRdb1jenV9IesHAdM0GR8fJ5/PMzs7S39/P1/5ylf42Mc+tqeh4B3sxp2d3U8BmoUmr335CmeWS7RMGX2uwAv/9CVObj+D7/t89z98l4HoAIZiEMlGCKSAr3/96zTqDbLL2whrLYJmlVBXaZZjN4WoS5p0W5IHbd33aP8ob33xLc4F54iaGUJCYlGN0miSBW+BZ555BkVR+OhHP9qpzsmy/K4Wx2bL5eWvTUHLo+fQtS6jmTEpLpRZOVvHmnuZT/+r/4Xff+8vEX3yMQY3BMq1dTL9N0sOpKiKW2rhLVcx96eYK98cyDsyMsLS0hIf//jHAYjnWkyd32b74jaCJnP86XF+5n37mZ66giRKGAWD1ddXWdrOU46aFAOJlucThu3Ug4Qmkqu7VL87x/rUOpn7MqiJ9hdTyg0wTYfZVEhupoowVUXI6kiSjCSJyJKMKEk79vDt1/Kq4UVge3gbDYKhGK2RJO87kuM//o//ltxWjtfKr/Gej36E6UqTpKEyPJIkfyXflnPdolt7Bz+duEr2EkMJFr67QG29hhppyynLlRZSKIAfEHjhjozPBwGkpI46FEdKtqXAge3jz5WRt5soqkSgy2S6+rGaDexCg1poEXgq+qaHY7uoNiQCD8eQabkeigi1Wp3A95EFGUNSsTbqWC2f7qSOgMCqFJLqjiEFAvZcGVmViGUNYCdXj3YGZbXpUFVcfFlECkAIBHxFQnRCdEBWlR0n/+tInh8gugF+VwRRE2jMldBiXTzxqUPkchFefXGB1758BTWq0TWewml5LJ1ep2XZ/MY/fi+l//jvyf7WbxH+4R8S+9mfxS0W+dKfnqW4VKXvUBfiTvdsa67EG99a4J4TQ7vicwaGB6i9UUMgJKLJ1JoiruOiqHuvuUbURDN16pUqmhuhWXZIVW3qa1W8ooeqqOgpEyNuoJgKmq5h5AzMQZN6vk55tsz6zDpuw6VwsYAgC7gbLoIgYCqQ6NJYWbChYiN5IWEo42oigSwiahJm0iDWHWG7WuTyqkdAlponYqgioiqh7kvjzBZxl6sovTGE6xxLVUmkHnjkG/YuoteB7RMVBHrv7aP7aDe1lRraT3vI1R3cBN8LePP1FSJxmT/4d/+SK1eu8Hu/93v80i/9EoZhcPDgQQzj2kjD5uZmx1jOcT3K5TITE0/fdFxZlvF9v5OHeyvIsoxpmkxNTVEsFnlwJwPzapdvfn4e3/cZGxvrEI13u5e5iheem+elz51H1WT0mEpppshXl4r87D94gK6MwMYHP0hyYYHwxReptuJYc7OEsw2kpI4U21lPMgbOcpXUZpO1hMpCvklkj87kVYIcBAE///M/39mPDY34OF5ATFe4/8gIqxvbzCysMTQ4RDYTxW3WqFarDA3tjvDxPA/bttuRFJVK53W43ijvRrRl+8ougvh2BDybzZLJZPi3//bfsr6+TiQS4UMf+tC7ebn/RuEO0fspgF21yW83sOMqAymTlWKTqJnhE+/9BPnLedx1l94P9KJelz2UyqWoWlV6D6aZ35jBWfaJ+gFp26ZZbBLNfm8dJEMxOB4/TqtH4GLdRVRdwjAkWfHpf7PBcy9/k8hEhNnZWTzP6yy0VxfHIAhYmC/RbLj09sc6Ic+3wvR0gepaja6hm+c1Ev0xpl6b5tiFr7GV66b80Y8z3NsL08vEe9KUSkVSqfRNeQNiVMUvNNEnM5Tc8Kb8vnQ6jW3bNBoNIpEIubjOL//de1lYraBpMvv64iiSyOTkJJe+dYlXv/oqYn8P83qEcqmBqYrkUnEE2nl5JcujEIQMZuJEGwLykszYh8Y6uYhdQxZfPL1KPlEgM18lrDkICZlQaVclnXILZ7NOUNixOY4piGo7m8rpj1BIBBw1W8jlFXRXx2261PB4eWoTFxFJFFHGMiRUidJs6Q7R+xsIQRTIHsyiRNvSzcANcJsuTt1BsBx812/LHlUJOWsgZ0zEhHatqNDysKeLhAWLUJcINAlkEVEUiUdjbBcaOLaDJgas1UpEDJM4GkrVIRdC0ZRpuQGBB57to8gKtuPhlFrkEhq9kkw+pRFEJQxVIuyP4dcd/O0mUlq/lr9EO7tN8AAfmqZCYILWdBECAVkAwedamPpVBCFC0yWIa3i6iFdqIQC5fRnuvrcteT9/cgUEgXR/eyNpKBK5fWlKy3Vm//Qb7P9H/4jgd38X6bOfbf/eiLE1V0GJSx2SB5Dqi1FYrLCyWObAoWsueGbWRE/qWEWL4YzJeqWFZbduSfSgPW8ZTyVpNJrUii6DuoKaiFE0WuTvzuKpbcKeNhV6AgH/VIFMNWTf0/vQHtFobDXYeGuDqdoUsiHjuz4EbcIcxkLMowbKWhW74RKYbXIsSgJaQkXRZCDAsy1ajSYnqzWyuRzZWJv4i1EFuScCkoTfcGEnaF0wZATaUs5669qGOAxCnIaDU3WQdZmuu7pIjiaRFImQkMB7m4DWO/ipw8J8kee/eImN/AotscXAwACCIDAxMbHn46/fU/SNWjzy1BPoe5i3SJL0jshYGIacPXuWl156iSNHjnSI3uc//3lGRkZoNps89thju+Sk3486yfV8zry0hKJIZEaSAEQyJuvnt7hwaoP0l/4l6ddfx37mGWIHJ9l4bpZeNUKzvEm8a3cUgpTUoWARtmKUmg6GHOxJoPr6+lhdXd1lNKXJEtp12aL9PTl6cmlmZ2exNZ9sNtsJV9+3b1+HQMqyjCzLRCLf25y/4zjYtk29XqdQKHS6s1fJ4V7/FkWRixcvUiwWWV3d4PIbIV09Of7W3733+3YC/WnFHaL3UwBZl4nGVMRSk02xhdTyGBscZKQnTpAJSI4mOxuzq+jp7WFtbY3Dhw/z7HeeReuXeO+TT9Jn9LLy4gr73r/vbbt416M0W2LtSp5qXw+O7ZJSJAQBtpsu255NQuvl4x94mqMfObpr0ZFlmVbL5ot/co6LLy3h2T6xbpMPfeYYk3d13/L57JZH6AVtYnMDClsbmDOzuIrBa//w/0nf0BBh2F4oREQSscSeZC8MQgRB7OTq7VVHHhkZYXFxkUOHDgFto4Fj47tlRfWNOo2pBsbwAC8sbWCoCmO9KRzHoVYpk0ym0BUJXZFwvYDFkgUpA2m+SOzMJuW4wspCGUWTeXg8xUlJZEmRSJdstK0m/noLSQjxFyqIdQdXEMDzUUs+am+E5v19yAcyfGw8w+MHurAadbpz3Ri+QWS0l6IoE1MlqpaL67XlXb7j73G1d/A3BaqpEu2OYmQMJFVi+YJGs9zCiKgIioigyTetIaEXYM+W8AsWal8UqdrCrbsIO1+2gSAgSBKxeAxZEnE9j6ZtEc3FkRCg0iLX9LAtl6bj0bRtdN/HNBVyvkB3LkJkPMUVzyO+c58Lsoi2L4UdhPiFJlJC78yCeb5P4IFfs9GiKp4ooERUqDkENRuqLUJVQlKEHYloAA0HT5exCRGqDkZfHCOt06y2qFZbxOM69VIL9QaDLMVQ8Atlmv/kX9H6jd/A+dVfJbnzO0kSMEyderWJ1WximO2NWOAHCJKAfEM+lBpRyRzIsPLKCr2jSWK6TLXeIv4OxtFajYDEloOj+Gw2AprdUTzbxQhDNE1jsdRkGYHRlEk4W0QQBfZ/eD/R7igV16WpCPRNZkmnTcIgpNCwqU3nyZgKgShi2h5SbG9plKwo5OIGFhq2G+5aL4WIiiCJqGNJ/HwTr2AR1F0gJPQCXEWiLrXD2RGuvQaxvhh68gYnwh9Ayswd/PVC30CCAyf6ebT3IA88/KsEQUCtVrvpcbZtc/LkSdbW1gjDkLGxMTzP64yf3Ih32nW7arY0MzPTmbvb3NxkenqakydP8tu//ds3zc1/Px092/VxqjbqDTnEsiax/vm/ousrX2D693+fyYcf3plXhACIRiLUGw2ikQjSVblkEIAogCAgCsJNCq2rGB4e5o033rjJUfhGSJLExMQEW1tbTE1NMT4+zvj4ODMzMwwMDGCae2fuBUHIqy8vcfb1ZRzHZ99kjsfft5/IdfvK6+cH3ymCIODEiRMUi0USiW4K01Bcr9GyPSLyD84I56cJd4jeTwGiPVHueWIU75lZylWbZFyjL97u4GQmMp0NmuX4rFUsVkoWjSBF99HH2GqG3P/QexgZGmB4eBjf9SlMFYj2RBl4cOAdPX8Ytof9S15AseXSnzI7rnE9CYnZ9Qbjx44Qbof4to9o7CZ6C3N1Vk5WiWQMzLjO1myRb33+AvsmMqg3zvDsIJ0xkUyZVrWFkbwm4ShsbaM89zJ2qFL49c/ywHvv4fWiwnqlRbYnin2lgJLUScRvJntB3UEb7aJge6R0cc9h45GREd54440O0dsLpdnS/5+9/wyy7DwPe9//ymvtnDuHme7JwCCDyAQIgASTRIuiRMqyxWNJx0H29Ydzj33uF12f8r3XKp2yq1guW5LrOJCyjo5tkbRJSswESIAEkWaAweTUOffOaeV1P+yenmlM9wQIIIHh+2OxgOlZe++1N7pXr+d9n0Cn5XIu8MhmMsRkn7XVNXK5HKlUikq1QiadRlYUNFWmlDSZq9kUc3Fe/MZpzta6BG5AGEYUJnN84vN3cTZncWy+zkpOwyx3iB2r4Lc8KppEN4zwUjpkDUqRRJ+h8tT9I+zb6FwVmSa//Ou/TPdol8RwimPLTRZqHYYyMQbSJvZMneSQaHLwi0yLa6imit/10WIa+UKCOdtDSe1c/+CvdwjW2ihZC0mRe2nTtSpyGFHver0GKbJBRzEJ2g6xWIyBvj4kScbTPPymiydLOBuplIasgxcg6wp216NVdWidWsPpusQsnW4Y9Vr9G0pv5lJXxy93kWMackrHc1xkegO+NVPtNV5RFXKZFK3VFmE3wPMCvG6A5IRIUUSY1AkzBrFSjMxQmlQphucENFba1Ku9QG9gV5aTP5omGkpuXkub06sYx49QePxeYl/4Autzc5tNAnRdZfLOAV79q7N46QDooGsGlZk6fZM5xnZd3ZY8N5Fj+fVlFDfg0GCKH5/tUm3ZZBM7t1+vdz285RaDmkJT16jVGmQLOulSBsdx8FyHgXQC2ws4X26hFBOoF6vUpmucW2vxg//zCN5am1Pn1zn8yBj7D/ax3HLxN5rVdOWNu8kdjAz3fj9UOy5rTQfHDzA3BljLukJo+8iWiro3j9b1iDo+Ydejud4hkTDIjmbQ4hp6rNc8TN0ulROuWmAQbn2WpfHJX71t88+yLJNOX529o2kaL730EtPT05w/f56RkRF+7/d+b8fnVRQF13Vv6Bz27dvH+Pg4//L/9xW+8Pvfp6PO0z/cz9jY2LYBnaqqeF6v5GNpscGLz06x9/a+G+q2HTc1cuNpFl5b3ug3IBG4Ia3z09x1+ltU/u2/ZeSzn2V9fZ1CocBEMcGLK03iKYO4G9KmQzweR5YkgqqNtDePHtPoSxkE9e0DPcMwcBznqkY3OymVSmSzWc6ePUs2X6DjpvjGXx6n2JfigQcmsd5yr/bjF6Z59v9+EzmIkDWZl86sU612+Y2/fdeW85mammJwcBDDMOh2PdbXOgwNJ3dM45RlmQceeICJiQlUVWVuto6qSlvS4YWtRKB3C5AkifGHR8mNZnDbLr7jM/fjOcysuflLstJyeXWmQrnloKkKmizjhCovXlhnoLSXQn9vppKiKVgFi7VTa/Qd7ruhXb3QD3FbLh2pV3D/1muGFPrYusryWpvg9BqhpaJpMvGYTiSrtBseoQfJfG9lKFmK06natNvejoHe7vEsA/sLzL+6hG7pKIZCrVpF+uHztFsq3oOT/Or//GkIPebai0x5IcpwEmmmhl/uouYtUlcEe/56ByWhIw0m8MOQPZntX7dUKlGv13EcZ9siYLflsn5mnYYu0Sh79KdNZFnHNE0qlQqGYZLL5qjVqlixWK8hzUaq25zjUX9tETkfo+++QQIvZOn0GuffXOGZj+7jzuEsX/hP/5UfHJ9iTzWHOVqkomokLQ1VkXD9iOF8jLtiBnty8c2Lt67r3P7I7ZytnaV6ocrh4SQHh1KoQUR7sYmVs8hN/vVm4gjvb6qhEivEqM/VsfIWQ1mLMytNum6wbdfNKIzwV9tImrK5g2dmTTxFQu54EJdQQ5ANFVuCQDbRTI0ggmilSWe1Q1sGXwJvMIEuSfiVLposQVynNt+kNVtD80IwZILxTG/nzoeo64PX6Y1JCEL8tQ5RuQ0xlUBRIIxAkpAiiCKQFRkjoaLGDaSciW/7RJaK0p9A64tjpUzUK+rIJL9Xu+L5vV3u+x4bZ+70Oksn17ByFl6jg//TVzk84DPypT/i0gXvyhumxz48ydpig5ljSwR+CET07S7y4V+7DXWb9KJ4X5z83jzLR5cZncjgT5R4+fwSth+RtDQSugpS7/20HZ+m7aH5ISORjJJUaMzWKO0uoBcSNOoNEvE4qqX2xrwkk6Qsndm6TX/MYP7oEj9+fZHQ9kkNpphfWuc7X/0ptjNOOz6AupEOK1tar/HOdcQ0pVd75/ibgR6KRORH4Eebz4WlQWhCQmPPZIFSbvudgEvCoJfGdTOZJcIvFlmWGR0d5cKFC4yPj2OaJs8//zyHDh3atiGIqqp0u90bfv5G3UOqp1heXKO0d5AnnpjsDRbf5rmvTN08eWyZI988S7PauaFAT5IkHnlykq/NNVg4voqmq3TmFxmceY3R3/kopd/5HQDK5TKFQoGDAynemK/Rnchgnq1g1iIai6tYhok2nGKlaLEnH2MkG+NidftAD6Cvr4/V1VX6+nbOnnrrexwZn+A//NELLLy+gqFo+IQc+ckcf+f3HiW7kb4dhhGvPz+DEkFxbx6AdrnDzJFlFp5qMrLRyTgMQ774xS8yOzvLvffei+ocojzT4vFPH+LBR8d3PI99+/Zt/vvINuU7wlYi0LtFSJJEeuMbfv6lebyOt/ln2wt4daZCpe3Sn45x+Wdeww8iFmod5Fl4aHehl3aUs6ier1KfraP0KSwsLOB5HkNDQ9tu08uKjKIr6EAQbl0CrtabdGyZ+VNlKjWHaKrcuzGUAEOlm3JQIh9Z1qivtIilTZorTQq7ciR3SBmC3gDVj376EF9puiydKRP4Lv6ZMxgNGfvOMf6n/9enSMQMwKBoBJRSBksdj4G7+um+sYI7U0eOacR8jfXjc2SHCph39rNAxGDGYkhr7/g5j4yMMDs7y549e676+856B6fusBZFKIq0uSsoyTL5QoF2u836em93r9Pp4vs+8XictKWy3naJ3ADF9ui2O/iBj93tcuS1Y3jheQDc2TeITr9KdW2QgQN3Mbz/MINpE02VWanbFJMmmiz3UqKuoGgKuz60C0VTqE3XCJwAFEgOJBl5aIRY/to3XcKtLzuRZf3MOlEYkYlp9KdNZtbbmJp11eJN2HAIas6WHb9y18NLaMQbDloIqhcQJnTMmEYYQdP2UMtd9LpLQ44ITRXTDtD8iMD10RsOiXyMWBDhWxpByqBpu4Qtl0TLRRtMbgaVl0ROgF+zsatt9BCCyEe2A5BcJCnaSF2KoOuDLqEXYyQGk70OoNr2rdPDoHdjpGm9X4+7J/L8jd+5h+9+/Q3aizbxIz/kQGKNR7/yJ7Bxw9ff38/KyspmylgmY/GhT41QeXiEWtkmCB0m92fZvTu/7WtKksToI6P43d64g927MoTDCVqRyXyty1K3u5lSbmkKk8UEeSegu9Bher0NhRjWRAZJU0ilNVqtFoosk0omaTSbxGIW9a5PPRtDmq3h1R0UUyGMQkI8WrUW/+Nr32Dg8EPkRnvXNTmh9YLm65AkidhGx17XD685A3W96ZCL65szPa/F63hoce3qVE5B2LC21iZujTM8eBvHT/6Yz372szz66KM7BjY3WqN3STpjMjCRhQhG9xd6sx936Ch5Zerm4bsHsTsekwdvvO59//4i8u/cycs/vkj75BRj3/xzRj95Dwf/2f+2eYwsy4RhyEguxgf3FPlBGNHUZDIdn7gfsua28fsTjA+lePpgH7IsXXNMxNjYGCdOnLjhQA/glZfmWT9RY2A4iyO5aKHG+qka/+Orr/Bbf+uR3vy7KMLpemhm7/MOPB8vdGnUm/z0pdeYmzU330+lUmFubo5ms8nB4SyqE8d13n5TG+FqItC7xQReQPlMGTN7+ZfjYq1LueXSn7Z46/VPVSRKSZOlms1ay+ntQCkyiqlQPltmfmGe559/nlwux2//9m9v+5qSLJHflyczVSWmqqw1HfJxnXK9y8U3ltEbAem2T3wsQ7wvgaRIvaHdHZ/54w0qoUc8lcFeapKoOWSG0nz4M9uvfF9pqD/F3/q9D/DVv3iBpS8/y0DlNK0n7+GXf/9zjA5drpvLxE0+lMvyzROrLKQ0Bh8dJVpq4a+2USTI789R0Rz8mELaVPnIoX5WLpR3fN1du3Zx6tQpxsfHURRlyy+VwAsggq4fbMzp2ioej2Nt7O7FYnFkWaZer5NMprAdByMh0VpvEa3FiFyIZeI89ZF7ufe+Xh79pTbGn7r9U9QudjlBSKXdS0XJxXWSbkhqOLXtXDwzbbLnY3tor7Zxmg6qoZIYSKDscMMr/GLJjGWI5WO019ok+hIcHEhR67isNGz6UuaWYM+v2r1GJhv1Zi3Hp2H75IeTKBUdZ6lFINEbUo6ELIHpR9jrHWxNIVIkDEUBJyCariNFIVYhRmp3lmhjRp+qSfihSlkLaK93SaWMzU6fl0hGr0mMllSQuyFqxyOKa3hxHV+TSOgqasogaHTQCynM20vI2/xcXqlbdzESOpkrrqF9Ayaf+ewB4p/9HGp7BfW7P4H05e69uq5vSQmLoghZirjnvrHNr62vr2+mXl065kqapbHryV3ImszayTXiTsCuXXH2D6R6qbBhhCpLpC0NE1g6uoRdtfFzMczR1JbANZFI4LkejWaTRDJBp9Mh8EPWGg002yORV1g+2cULQAk1kn0GT370b+AmBzi13KuFki2tN2A+jK6ZPukFESlDJRPTWax1MXQFC3qz+jaa0Xh+SLntEtMV7hzJot1A0wSv7aEn9S1NxAThkjOnV/mrP32DxnILWR/i6Yf/Lg8//Mg1OzfebB2drqv8xt+9n/J6B9utMHSNod1XBpHFUoKP/8qhG38zGwzN5pN7Isx/8LuUP/5xBv/tv9ry9/39/SwvLzM4OMgHdufBbXMmLrPugBtCSS+Qk9o8cVuR0sbu2rVSMxOJBO329ovaO7l4fh0NCSsXxyJOq9VClkKWZto8++yzjIyM9OruSjJTL9aRTAnD1OmuuQzs7uNjH32EeOzyz/Tc3ByPP/44n/rUp6jXHZYXGuzZLxrDvZNEoHeL8W2/V2eTvJzuslDrbqQHbv8YTZUJooi1Zi/Qg95Nh123efSZR3n55ZfZs2fPNTsq5ffkGTi1jj1d5YIUcnq2RvPMGvGaQz5ukBzNYB0qbNkBUJIGCSlBtFwjr6rUTImRh0f45Wf2Xrfr5iWVtUVGXvhzfuub/4nn//7f547/zz+4aqDmpR24X7pzkG+/Oc9U3UbvixEb7dWldRwfz7eIOQ0+ds9+5k+/zunTp1FVlTvuuGPLL44wDKlUKnz5y1/mhz/8Ib/7u7/L2NjlmzlJlojodevcaS1cVhQKxSKtZotut9Or26tUMA2LOx7YxdJKm4apoqQV7v7Qbu6593Kt5JNPPsmHP/xh2ittznXOcXC5ST3T241LeyGF/iR9h3denZNkiUR/gsRNzuUTbn16Qqf/7n6mvjeFmTHJxDTuH8/x6kyVhVqHpKmRNDRkGSLHR1JlXD+k7fp4fkhcVymmTEhYVFsuXdvDbzsoEigxFc32sb2AQFNIyBJh20VaayOHYBzIk96dRZYkIkMBRQa/N26gYXu0bJ9Y1UZOGVcFHbbjYMUsiIFseRiLLeyVFtFAAt1SeynZXRu1GLtukAfQqXS556N7tmQUlFdXGfxf/hekC+dRX3oJclenOmuahud5aJrGwsICg2+5KSwUCluCvTAMr1pp1+M6u5/cTWYsw+qJVeZPzZOMJ0mYam+BLIhw17t4ikQsH6Owv8BaWu3VL76FKsnEMWjNVVBkhbDjIskmmVyGT39yLz+MnWDllUVGJ4bYf7hILpdipeFwdrWF6weolopsqUQdD+kawZbt+Yzn44zkYhiazGLNplrvIqsKbc8nqPlIkkQ+rnPHSIbiNTI1ruTUHYoHi6JGT9jWs//9NI2VJqW9ReyWw/wbFY4dXeTu+7bvLbC0tMSXv/xlqtUqDzzwAE8//fQNvY6uq/QPJJmaWr9qLt+VrjVK4EY5586h/dqvUX3gAQb+/M95ayrFpWvMJVnZ5m8/to961yMII2J6r8nbhQsXcGL6Dc2Yy2azVCoVcttc06DXGbNaraKqKoqiYJgKQXA5cyuRSNCU2iga3H///aysrJDNZvn033yAL4evs3JmnU7gkhxO8fRnbtsS5AF85jOfueJcLLJZC+GdJQK9W0wURL0WtVf8cnS8cMuYgO3IkoR7RRtrSe7dVMweX8eoj9O4kGDuzDoj+7YfXGtmTHY/vRv52WnsN5apnFjHLLfJDaTRMyb6rgxK+uoUHEVWCC2J9O4sykyd2aPLvL6/wIdysesWCLdaLV7/wz/kl7/4RX76uc9x4Pd//6ogDy6v4u0tJni4GKAf2MWZ5RaVTq/+ZE8pwYH+FH1xmbmZaV555RXm5ubwPI+77rpry3P5vs93v/tdms0mhmFc1S1KNVVkVSYVRiy5PlyjQDiRTGBZFpVKBVQDt9vB1NN85FcPUfzAEJqqYGhXz7+BXgOeyWcmSbyxTGO+AUBqOEX/Hf0iiBPetr7b+mjMN1g/tU5uMkchafDQRJ7pcoeZcpvlxkZ9S8Mm6rrolkop0Usdnq+2UWQJv2GT2ZUlVorRrHSwVzu4iy20cgffD5FD8NsSiuOjGQp6PkZyV/byz7uuIsdUwraHnNBJmSplN8RuOGhvCTqiMERSZKII3EqXzloH1wuQqw6K7dOq2rhmC1WOSBy4fsDQrtnocY3b7rkcpIVBQPr3fx/5uedQfvITGN7+RrKvr4+FhQWGh4d3nNVVKBRYW1tjfX2dVCq17e6DoisUDxbJ78sjvSIRd+M4TYfACVAMBTNlovfp1Mt1nOccsppCueaQjfdqJ8OG02uUU7YJXR85iHB9H6njEC5bXNQqFA4UeOjRXcwbGrnJHLIiE3gBVsMhOdekXOmSMdVet8y2iz6aRo5pV7Uitr0AXZHJJwxkWWIkF+tliAQRfs5Ez8WxNJm+lEUpaaAoNxa0uW0X1VLJTYjaYeFqruvTqnaxshaKKhHPmDQWG7QaO9eUDgwM0N/fT7Va5f7777+p11tYWGBoaOi6x91IU5OdzB09St/f/tu0x8cp/eVfIu2QbmkYBrZt02g0KBZ7O1/pt3QFnpiY4Pz589RqNc6cOUO1WuW2227btrvlrl27uHjx4o6B3tzcHH/8x39Ms9lkcXGRj3/qt1EyJutny8TzFp2qjZzQeeLDhymXy2jaRuq4ovD5f/ABpi5W8fyA0ZEM6W3uAYV3nwj0bjGyJiMrMlFweWUpYaib6X078cNwS9OFMAiplzs8+58XqTVMamtrrM51+PW/fz+l8ey2z5EcSDL2yT185/wamgbpO0vES2mUrIVsbH/RkmVp82YtMZ4huFDlpW+d58BEgcHMzis7juPwky98gU/8x//IGx/9KKN/8AeUSqUdj08mkywvL5NOxhkfSHNgYPsC3l27dlEsFjl16hTPPPPMVX+v6zq//du/zerqKosLixgYdCtdVsorHD1+FEVSmD09i6Hm0HJ9OH6wZS7NWymqQrFU5NxCmRFLwnPaROmIhHX9dKXkYJLEQAKv3Vvh0+LaX+sXjSDIqsz4B8cJ3ZDK+Qrp0TSpmMbh4TR7+xKsNHrdFWsND1/uUBhOETdU6l2PhWoHd62DaqkYe3Ikcha53Vk6DZvmhSq2H1INQ3JJg1jbR4upqLGN8Q1XLERJG0PZw6YLQUjc0GjoPu2Oj2UH6AkggkazgapqWKZFZ6FBc7GJR4QXU5FyBnrLQw5C7I4LUYR/tkx/0kDboeYr8AKq83Um7htmfFfvGvfd736Xvn/37zj09a+jPPccHDiw82cny0RRdN2bwmKxyNraGmtra9fcIZAVmdRwinQ6jeM4mymf0EsD/e9f/u90XumgaWmMA3fQrNpoKy381Q6EEXJcQ0kZSKpMu+uRLcbRmx1SQ2nOvHCGbCpLZ62DaqpEYUR9uo5dtyl4Ee0ImraPZakEKy2c8xWUlNHbFd0ItN0gpO34jOZixK+4vmsS5OM6w3cMkhh8e4tO7ZU22Yks8b6bm8sl/GLQdZXiWJoLLy+gmRp2w0G1NEr91+4efduhe7lwtkoicXNdpndqvvZWl9I3d6rj24lfr6N/+tOEsRiZ730P2dw5IOrr62Nubg7btnecKwgwOTnJl770Jc6ePUu9Xufuu+/e9rgwDHn22Wd58cUX+Vt/62+RSl1OSW+1WiwvLzM7O4tpmvzSL/0S/9NvfYKXXlvk5e9fpLXWxhpK8sBj49xz5wCyLNFqtZibmyOdTrO0OM/+/eM39VkI7zwR6N1iVFPFSBp0612MjTTJ4azFdKWNF4Tb1o113ABTVRi4YrXFa3lUmg61ms3ggRIQsXxilamz5R0DPYALqy2aNRt13CKz9wZWwDaKi3v/LpEaTLI03+D46VUGHxjb9jG+7/P8v//3PPIv/gXn77+f1L/6VwzvsMp+SX9/P9/85jf56Ec/es3jmr7M5L0f5MXT83hWfttALW7E+fzHP89//T//km//8auEQUS70+C5l79FNVxBciQ+efCTlBI6C40uA6nYjmmz0GuTnksnmdBlAhp0tS4zMzNbUkJ3IkmSqGER3lFG0mD307vREzqrJ1ZRTZV4KY6pK4xtNO0p111WGy5xoxck6E5AvOXStTQK+woomcs3RbGUiT6SZnm5jaJKFFSFyO4gpw2Cttdr+vEWclJHTuqETRc5ZRDXVRQ/otFxsBwdS5Mpl3t1tBkpgbPiYWsSkqqiyZDoS+DIHaKqg542sEcT1Bs20rEVBu8bQn5LK3+36bB2bIXCQJJ7xjIsbNxA5v/bX3Hwy1/hu//oH/LkPfdwvf6PsViMlZWV616PisUi09PTdDqdzVX5t1peXubZZ59lbm6Op59+ejPQq9frtNtt+gb68O722KvsZd4OOH9sCdUJiRdimwtrrh9SbzrIEuQtjXQ+Q2FfntAIcTsunQsdzvzHMxTHi6T6UyT6EiQVCalmc3GtRT0IMEsxqDoEHZdwPkAqxrAtlRAYzsYYzce3LDDZNRszY2IV314KltNwkCSJ0qGSWLgSdvTRTx/ia07A2kwNzVC478P72H9o58VegJ98Z5FgqZ83Xlvk7vtvbHzUdmnYURSxUO0yN19DlmXGR9P0pazN7KGbCfRCx6H8oQ9htNuYr7yCtsPO2iWSJLG2tsbExMR1n/vXfu3X+Mf/+B/zxBNPbBuoRlHEn/3ZnzE9PU2pVNpstre+vs6xY8dYXl7m4MGD/KN/9I84cuQIv/mbv4miKDx0/wh33TlAo+2SiutbRiskEgn279/P1NQUURRdNVxd+NkTgd4tRlZkCgcLXPjOhc0i3P60xVDGYrbSoZgwtqQDdtyAWtthb1+KzEYb68ALiMKI1FgGeaGO4/kEQYQiy2jbtFq/JAwjXj+yRFhpY42kdjxuy/lKcq8r3qU/xzWsMOLN1xZ56K4hEsbWb9EwDHn+v/wXPvD7v8/c2BjBv/7XTG7T/TIMIxbqXVp2r+V3RgswTXPHG4fp9TZHZqtcWGvTdn3Sd32Yf/Pt19k/NsAdwxnuHMlgagp2zeb1vzzD0VcW0JL7OLLaoNFu0qm3CL1xMm6Wpz9xJ4dG91NeaePEDZYaHQoJ46qAMQwjql2PIIi4PRcjF8DkMw9S02qsrKzQ6XTYt2/fNYvLBeHdYCQNdj+1m/RomuWjy9Rn60RhhGqpqBv1Yr7tU52uomoqekJn192DnPQDWrpM+lKLSMB3A1bm6nTWOxiqTKfjIRsaRhghRWw7q0+SJdRiDNf2cRoOvgIxRUazdDpBSMXzaXtgqRrtiocdhWi6hqEqxHUVQ5XRZOh0Pfy2S2T7yGmTdrlLa6lJandvscqudGicKeMutbGSOklV4dhP5pAA7cIF+r78Mv/9yb/Lw3/3f0ORr3+jYts2lnX9AOfs2bMcO3aMWq22YypZX18fKSOFv+jjn/U5MnMEOS0zsH+AsbExnn76aXRFZ/5b8yhfP8uArlJOKlQ9H8/xsD2frhuiyjJJYL7msL47SyCrDMUtwqjK1NoUKxdXCLyA9GCaqu2x3nKotj38MMT2IxxLRas7aEEIQQQLDZLjGQaHUhQTW2smfccn8AL6JvveVpOnMAhpLjQZ/sAwmfHMTT9e+MVR6kvy+X/0INVKB9PUSF5j5ucl+b44zXKHTP7GFyHe+jPt+iHffO4iJ344hbPeAUnCGkhw79OTKKp2U81eoiBg9ROfIHnuHOELL2COjm573ErDZq3pIEkwmL6xc19p2EyX2+x++OOsKzkurtQZL6a2zAeWJInf/u3f5gtf+AKVSoXFxUWOHTuGbdscPnyYD37wgyiKsjmk/Mpg0dLVq2bnXfm8u3fvpl6vs7CwwKlTp9i9e/eOw9WFd5cI9G5B2V1ZzIyJXbOxshaqInHvWA5Fllis2rgtB1mWCcIQS1PY25fi8Eh6Mwhqr7ZJDCTY99gYi2tt5i5WkIGJ20ocuGtgx9ddbtgsnFxFwSOZuXagFwUhYdeHmotc6w3+lTQF2VJJZGKsn69wcanB4fErVrf+6T/ljKJw+Etfop5Os/Qv/yWP33nnVc+9UOvy3JlVZssdHD/sjTmwazxzx35WVlauaiV8fKHOt04s03F9SkmTAVMlCgw6KYPphRWWal3mqx0+PFHkxNfP8JOfzLAekwiDFk6zSSqRYHx4jPquAVbn12mXU2iHTbJeyMFKlwspmdWWix+4GLqCDLhBRBCGpEyNPSmDvBcx9MAwhf0FinKRZrPJm2++ySuvvML8/Dzdbpff+Oxv0Fxs4jQcZE0mNZQSu3nCu0aSJQr7C2QnsjQXmrRWWrSWWjgNBzNjkh5N43d8CgcKxIoxFEPBWG1zYrHeG9nihbQWm3QWWyhVG6tuIzkBth8RGS7dSgcjbaJoMm8NCcII2opEK6HhL7bwvAAUGd31keMqhiKTtHT6rQyVtRqJlEHS0jYzFiI3QPEiknsL2F2PyA2wGw5Ox2ft6BJtxyeq2UirHVAklPE07nCCmSiAo69C/wCcOcWZj/4KIwdv58TXL2Cv2ow+NIqyw2JXGIYEQXDd1fwwCEmRYvqlaQBit8col8vk81tHL1QvVBlsDPPy+ZO8ziLxZIKcoRPOhkgPSBTHi3htj9APSfTFsdyAjBcy7QWUHR8piigqMokQFENFHk/glWIcW6hzbrXNoCIxd3oB17JZX1tHfj1FvS9OpEpYmkLS1DC1iLbjIeUs4g2XZNpE9kKSARTjW4O8KIjorHXI7sqSHLy51Djo7S7UZ+qkRlIM3DsgdvOE61JVmWLpxtODf/mzd+C6/o7zed9qaWlpc2TKJS+9ucTRb5wm6UNuKAUBNBYavPiVk+QPyTcW6P3lX8Kzz7I0P0/hxz9m6otfZN/hw1cd1rQ9vn96lbMrTTpub66nEjjcPpwlValu25OgaXv84PQqZ1datBwPKTPByUbEke+f5I6Jfj5yaJB84nLAZhgGDz/xFH/yn/8Hf/at19m/fzcPPzrBwBUBpSzLN5S6+lbpdJpkMsnFixc5ceIEu3fvJp/PE4Y7z/YT3nki0LsFGSmD/N48Cy8voCf03hB0XeHBXXnWSy5rTQfPDzE2iuSz8csJSV7Hw2t5jD40Sm4oxWf+/v1Mn1lHVmR27S9gbtMx7dLOYccNsMttUqntV5yiMCJsur1GAZUuoe3TbbSQai1qjXlMw0A2e+ciSRGVmTrRWBbP86icP0/f//F/sD+KaKfTPPe//+98epsauvWWw9deX2C16dAfguEEdAKPmUjiuakmd2e8LYHeXLXDt08uEwG7dA3nzVWaSy0iP0JJ6YyPxKl4LY4tQDBVZfaHZ7gQ2ZiOTTaXZWRwcrPOJpGIU8ilWFpqcfbkGg9/ch/q0WXMxSZjSZOyAnW/dzOYdUJSDQ9juYOa0El9YIS+O/o2b5ySySQPPvgg3/3ud/nCF77ASHGEg9JB2is2nY6PIknkBxIMPzBM8SZm9QjCzVI0hcx4ZnOHJYoiiKA6VeXsN85iZIzNcR57+hKUUgYnzq1z6vU5vEqXREInszeHPNukOVvDS2ioMkQtj07TxpmqkduVQd9oKOAFIWtNl7bjI2kycl8Mfa1LIggxLY1AV+i6IalcEbfcRdNkUhs3LpEfEnY9JAnUYgy1GMP0I2INh05Bo9UJUWo2I6UEthdQHrbwxnIYhkba0tDPnoZTb8DpNwj27qN1/92ctz3WWzbd56aJ/Ijxx8eRN8YD1GpdXn9lHrvro1sujz1xCM/zWF1d3bZmuFvpMv3DaeozdeKrSVRFpftql6npKYJHAkpDvcdUZiq89F9e4exqF/2+u1nqBEjlDlOdJlNHlhh4dpqBw320Vlq0V9oU9hcIwojTczU66x45WUHTJWRVQS3FUAsWcsIACTL0hq6fPbVCfnAPuQLMnVunvNjElCXiE9nNniu6CnFDoaHJ2CV39esAAQAASURBVBGkbZ9E2sRtub2Af2MEReiHtJZbJPuTFPYXbrpT5qUgz0gajD02hn6NJlaC8Ndxo0EeQKfTYWDg8uK2F4S8/so8essnsT+/uRiR3p1l9WyZhfkAz/NYWVnhxz/+Mc8888z2u1h/+Ifwox/RD0z/0R8x9slPXnWIF4R88/gyxxfq9GsKpW5AKMGs2+X1RZ1mM2Ry19bRCV034BvHlji11OjNA76ig2XLtnjpzAJtJ+Az945iyiEnTpzgxWNnmF1JMKp9APeNiNdPTHP2jQZPfHQv909u33zvZsiyzOTkJJVKhdOnT5PJZPjjP/5j/vk//+ek02LY+c+CCPRuUUP3D+HUHdbPrJPdnUXRFSRZopg0dmxv7XU86jN1Bu4d2Awe4lmLQw+MXHWsb/vUZmqUz5axqzZI8LWXf0xtVmXw0PhVx4cdD3e2jr/eBT9EtlRaOCy1V2k4LexOyFB2iLihErRdWG5x/qtv8ubXv8HZ1lkeWprls1GEBCTqdR7doYnB8YU6Sw2b8W6AfWSFlu3TbDfZ8+AEC7bHyXWHRzZaoAMcm6vTtH0mTJ32T+YJyh3knIlsyQQNh+i1DpkDGbxsg+e+eYGg1mVgT4H+whjKNk1WDFVBSRvMzddxux77P7Wf2nSNtZNrpBebeH5IbbpJZ71DiERUjCHFdepzdU7+xUmG7huieKiIJElIksSRI0cYGhiicaTBl9vfou/hB2gGEQowulDD/YGHntBJj4oLpvCzIUkSSL3MgYE7B1h4ZQF5REbbSP32Wx7rb64Sd0IG9xS5lPHoqDJaCF4UIbkRZEzkhIZXs6lM1ShM5ECVWW04dFwfS1eRJGhHkMpZGEkdWVehapOQZEJFplzu4LddYrqKHEW93bmEjpKzkBMbzYkUkKOImCJj3Vage2KJ1sUzXOz4OP1FDqaty+ns5871/hmBMj1F+skPkTI1VpsOpwMf5cVZ9KTO8AeGqdW6/N//7lWWTq0CEEgBmpLk0Q/tZm1t7arPzbd9pn4wxcr5Mou6RHV0DE1TmXY8+qZDZpnFftAmiiJWXlyh3lGoqDrp5S5SzSHqengSlMMIo2kTX2xgl20UQ6E6VWWx2mU5CEgPp7BKiV6TG01G2mYHMiZJZB1oxuPo+TQ5o0B3oYFUsfGXWqh98S3BWsrSqWQibDvE6HoETkCn3MHMmjhNB6fukBpKUTpcQrVu7rYiDELq03WMVC9dODV0Y2n/gvBuWllZuWqxxvYCOlUbM7a18ZmkyGiqzGq1zf/7n/0z1I35uvv27SOdTqPrvVEHuq5jBAHSCy8gATJQ/MEPMP/e37vq9afW25xZbjKiKQSvLtNa7+A4DrmhJOr9Bea7PscvzHH75OV0z+OLdU4vN9hdTKCrMn65Q9h0kSyNeCnGPXuGefnMLM2lKUreCrsn96AkDhB/dY68paH2G4S2T+P4Gj8IYei34gxdoynezcjlcjzwwAP8zu/8DnNzc3z5y1/m7/ydv/OOPLdwbSLQu0Vplsb4E+MgwfrpdcyMSawQ21yJvlLgBrRX23gdj4F7Bxh9eHTb4y5pr7aZenaKxlwDRVfQEhqRH9E93yCck5hveQx/6OBGcCnjr7Zxp+uEHQ8lbWzeeCR8BWVdI4wiZEUhHosjKTJKpEPOouk2OPbNI1S9KrsXf4oENAyDV++/H9eyiP3oR733qmkYhoGqafzkgk3aTOIeWQEipH4Ldc3FPVeh0DdMC4tTF2Y4vH+SatvlzEqDYsLAOVvBX++gjaY2b3BkSyOoO0QzLfpSORY6YA71MdSf3+GT6UlbGo2oy8LZMsP3DlG6rURhf4HabI2p70/RKXfolGKsKhIdesOIi1LEcM3G+d5FAj9g4M7eKuI//af/lKVTS7zyZ0d5rd2h5oXk4zq2F3DeDTDX2xROr4tAT/iZk2SJ4Yd6owSWjyxj5kysrMXrP52ltd4hN5xGunQZCSHyfBRNQbcDnLiGmtSQVAUtbeLVbGpLTfycScf1iekqSL0VajOISCR0rNtLyHGdsG4TtD3ChkMsplNpuTQ1iXwhgWypSJZ6VdqfHNPwy12CvgDDhXaoUU6o9CshntPF2+jKnlhY6J1usYB36BBepwMRxKWIta7Hm80Oxo/OovQrnDhZZfnUGn17C9hul+6ay6vfv8AHHhndtvtebbpGdbrKlAbT5Q4pyyCM4HilRTcuUzrTQuqXGJkYQarJLK610FY7REGvg6ZcjKFKEl7XoxFE1GZqGEmD5GCSrutTaTtYdgiLLVw7wBhPI8W3bx8TdX1kJ8BKGcxWOgBkRjOEcoMojPDXu8i6jBzXNgexx0yVphrRn7doTdeoXqgiKRJW2qLvcB+ZsQyydnPpWHbdprXUIjOWYfSR0beV8ikI74ZWq3VVmYepKZgpnfY5j/gVg8ijIMT1QwrFJHfu/zDHjx+nXq8Tj8dxHIdms4njOLiui/yf/hP3hiGhLLP8+OOcfOghtB/+EOh17bwUEB5Z8fEDHWmhi7/WuzdpV2sYaw5WpctKQmW23Ob2yd65eUHIsfkacUNFV2Xc6RqdI8uE3V5mhHl7CetAgd2DvUZQf/vXP0XDhe9+/xUyioI+0EuBlWMaaUli+UKZM3PVdyzQg16Tl1KphOM4/OmX/oKouRdZkvnIrxxicFjcw7xbRKB3CzOSBhNPT5AYSLB+ap3aVA1JldAs7fLw3Y2xC8mBJKOPjFI8ULxmkNetdrnw3Qub7a8lRdoo+IeW3sDoGybZ1bj4/HHMPTkG9BzOhWovgCtYW27AVFUhl8tTrVZJpzNIG/U1gRMQqTK7D4zRqI5Tfa7K19N3c2K3hP73fp2Z+XkGm01+61d+BVmW8TwP13VpdbooswtoQORFSKZKo9UkU8gSVLqoYYSkKnSd3nuudT1aTkDB0uksNFEyVw9jllM63lwToxkS0w1qXfu6n7siy4SyhLuRU//qq69SLBYxWybdapf14QTnyx10WSGhq4QRzDe6rKsKh5Mm2suLZEYzWLne5yV7MrqZQFdNsvFeHZKmyjQdH1uTaS23NtNnBeFnSdEUxh8bx0gYrB5f5fxL86yfWSeWj0EUEoUSBBFB3SZoeqgZHZyQQO+lAmphhCKDbKp0yx0cXcY0NcIQbD/ACENySFjjaZRsr5mSXIpf/sVVjFF9aYFqUiOd0jG3aQAShRGSBGHLxTlXoQ8Fb+8Yu5WQpN7rEgdgd238w4dR9+9HLhQwgCtzH4xYyHrTodGI8NY8HNsjjCJUVQYH9JiO3bHxvJD+/n6Wlpa2dN+sz9XpBBHLXZdC0thyrqteyLiZYCgzRLvRZv7NRdoXKsiWhlrc2tVSkqDt+SzMlAnTAQdKJjU7wglDCjmLKIgIajb2KQ9jMoeyTeOJKIggBMvUKFd6sxELCYkgZ4Isow0nCdY6BC2XS1GwFEb4XoBfShLLx+hKXYyEQXo8TXo0fVNBntNwaK+2UXSFkYdGGLh7AM26Xk9TQXj3ff/732d1dZUHH3xwy9c7nQ7z8/OM7LY4dkalOVXrzawNIxpLTchZ7J7Uuf32cZ555hmOHDnC+Pj4ludYXV3lB+PjDH/+8/z08cephiFPPvkkoxtNWIIg2AwIT7WWUZyAyA2QNBnb7hJPxqHZJfJDZAmQFVzXRdd1ah2P9ZZLPq4TBSH2qXWIIvTRFEHVxj1bxhhLU0zFaA2Mcvz8DLnSAFHT3czGuESyVORySK3Sfkc/21KpxB/8wR8A8JdfPcZLXzlLGEYcHU6LQO9dJAK9W5xqqgzdO0Tf7X3UZ+tUzlWwazahH6JYG7U3u7I0pQg9t/2O35XWTq7RXGiS35en1vU5sVhnveWgyBLD++7AJM38c+fxl1vUXJtMElRD3Zy99FbZbIZCobglV7vbsomNZ+jWV5iZm0EtqMx4SczCAaKjy4zcOcTnPve5zWJeTdPQNI14PM5Ef5dTSw0GBhO0jy+jKCF+s4U+nKKuyiihQ+i4tFotwmijvjCKiMJe2tdbSZKERO/vNFNDiXxqtdq2RdCXeEFIq1rj7IUG0gttvvOd7+B2Xe4y7yKTHma6apOJ6VvmFlq6xUrD4YLjkXBDqlNVrFzvBk1WZRS5d3qu3xuREYa9rp1KGKIYigjyhJ8bWZUZun+I4sEiZ//kFQJJRgOC2sY2mSL1UiGzJtpoCqPjoy61ULoedhjhyRKSKuPVbZy6TaTIKGFI0o9I6QrxXVn0kdTm93ij3iCVTkEY4ZshRlyh0/JoJf0twVPoBIQtF79qE9gu3XIbSW0SFJPU0jqFkTTGRhq74zhEUYj6yCM7vk9NkXsZEn5Ic6rJntsHOP6jBWZPLJLIJehUbCbuH6RcXgagVqttCfSiICKSeo1mlCsXvGSJIJSJIokwCJHrMtWpdRrNCpgZklKCKIKOF9C2fcptB6Pp0K47tHSZqZdOEc8USMR69XKSIqHmLYK6g322jLmvgJJ7y1wumV5X1Ah0VaLW8YgikHSFyAlQcxZaf4Kw4xF1fSLHx3UDFC+gb3+J2MZnMfLgCNWLVepz9d5zJXS0mIYW15CvGOXjOz5e28PreLgtFyNpUDpUonCgQHpE3OAJ7y0XL17kzTff5FOf+hSHDh1ifn4ey7LYs2cPuyYiVC3B8eemWVpsgATWcIpHPjyJGczj+z6GYVwVKNZqNb75zW8y+fDD9P+Tf4Ly9a+z8MYbvPzyywwPDyPLMoqiEIvFiMViTA6FnCovovTFkaZrdBfqJGMJpIRGmDYIfI+4qjI9Pc3evXuJooiIjQXfCAiizd14SZcJO/7molcEjIyOsjQ/h5q3sKdqxPPW5kJ3UHcIYhqyZHP69OnN9xCLxSgUCsRiMTzP40//9E/5lV/5FTKZDLPTVTptj8l9hd7i13UMDOWwchahH5ItiG6c7yYR6P2CUA2V/J48+T29tMNLO0CBH/LfvnSUC0cXMZMGH/+bd+44i8Ztu5TPlIkVY7hByCvTZcoth0zcwA9CWnqWcmWVVbdBrgv6gktnrEMmf3VBr+uHtF2flh2gpPtYbnkonYCkJOFEMDmRZ+H8yxD1cruHsvuw53TktsH4I/fu2AHq9qEMZ1eadCezdJs1MlIMNW3ij6doez4fu22EOwbjvPzyy1i5AXynQzvQUQoW3mzjqlbvoe2DpqAOJFAKMdKLTVD0yzeb26i3XXYVc/zSb9yHPqjz1a9+Fbts89ybzzH+6KO4iQKWfvX552IalY5LN6VSvVhl8J7e7J7UUIp8McHoSoPzXY+G7RMEIQVLIy8pFPb/9QumBeGvS0/orLQc9NuLxIpxomBjPqYi4Ze7cLGGYqhgqJiajFq1iTUcXNvHlSICL8BquKTjBpahYg1YqP0J1GJs8wak1WwxPT1FsVQimUiSyCRhT4hzdJn1coeMpaNI4K91CKo2kRcQKRLrzTpS2yeVM5FjGuFcA6/uQCGGNBzHj3ziiet370saGlXfpb7a4Z7BNE9+9ja+95VjaLLGwcf6eeZXDpLZSHUKw5AzZ84Q+REpM9VrYlPpkpQi1m2PQi5GSESt6zKRtdBlGbfpsvzGMg2tge471Dtdqh2XlhNguwFeEKIAWUnBT+qYpTjoFistH7vl4AcR+YSOIksoaQO/auNO1zDjRaQrhppLmoKkyuAFxHSFWtej4wbENIWw5RE5AbKpoiR02FikqzUdsjGN/t05Oist9KTOwN0DDNw1QGO+QXW62hta3/Ror7Z7i2cbFF1Bj+skB5KkRlJkxjO9XV9BeIfVal0c26fvOsPTt9NuuywuVLGsXjbN97//fQYGBti3b9/mMZoi8cknJrjnzkFm5+sossT4aIZjL7/A1773PSRJ4oknnuDDH/4wQRBQr9cxDIO/+qu/YmhoiIceegjfD1lcqHP48GE+8YlPbNuBck9fkmLSZFl1SR5IEV/S0GIm6q4M8zLsLmZ48LYRjh97nZmZGRw/wpCh6XhYyd6iWvfYam+xxgsx9uaQYxoN2yeuq6RMnfHD+3ntzIusz8tIUzWMtIHf9anZHvkHh/ngPfvIXtEYqdPpUC6XmZubY2lpiXPnzvGFL3yBw7c9wbmftHHaLnc9PcknPn3oup/13fcPk0wbBEHEXnEP864Sgd4vqEur4wvzdS4cWUSPadQXmxx9aW7HQM+u2th1m8yuDPM1m0rbpT9tbcxlUZAAp9RP6v44/osLaBHEBrYO/4wiKLcdGraHH4Aq91IdAVw3YLXcxS9YuIbMXXffTRRFTO6e5OIRm0bRgZbHa18/w0Mf3bttZ7a9fQke21Pk2VOL1EYyRLk0rh+gBCH3jeW5azSDpsj09fWxZ88EZxrTvDmzSn8Gggsu0nILpRhDUmTCtkew1kGbyOAlDRITWRJtj3IrIJ8xtg32ml0PueYwvidPZiwDGjz11FMMpYZovNhgWpGYqna3/Xw1VSYIIwJJIvCCza+bGZPB+wdxnvMwa106moQWQB6J4dtK5Pdeu2ZQEH4WgiDEs300U0F+S22YVNma8iwndLS4hmrH0FsOftujawdocZ3C3hxqPoac2ppK7bkuZ8+dIQp79TOXuuElxjLkOj7ls+tUFhroro/a9lFMFT+m4ncDjMggMlwmDw5Sk2VkWaLZbBCcLBOv58jcMXRD71GRwZUlPM/n29/8Nun+NP/P/+8zgLxlFdv3fKaOTXHke0fIRBn2jO+htlSjerpKLADX0liXaiilGIODSUY8iA3FqM/WkWSJD/36hzj5vZMcO7HIeqWDLUkYqkxcksgEEYm8RRjXUfNx/DCi4fbq7GpdFz+MKCUNVEVCyZgEa23cuQbGRHZztqEc01AyBn6li5EyMFWFtuNjqZeuqdGW9+35IV4QMl6II8u91MvB+wY3d+0udWWNogi32evIGXgBRBszES0VM21udmcVhHfLf//T13Fdn8/+7n2kUub1H7Ch2XT403/9ImuLCm5SIpPTiMViOI5z1bGSJDGUizGUu7xYYVkWq6urSJK02cTlxIkTfOUrXyGdTrNnzx6eeOIJJEnih987h7NWYu+jd+44ZiBtaXzkYB/fPL7EOS1i6L5hWlGE7QUMpy0+cqgPQ1PYtWsXmqaRSCQYXz/Lj86VUVyT1J4M8ZhGULOREzr67iySLLHasDk8nNlsyvdbn7yXL4av0pj3aax3IGtQ2j/EM09ObgnygM3dRujV242Pj1MqlViZb9Fc7aBoMgsXyjf8me/ZJzqG/yyIq+4vuHhSx0waNJZbEEEyvfOFMQqj3k6gLOGHIWHEluGbmirjBhGTkwVOvbyEbwdIYcSlIVlRBKtNm3rXw1AVDENCunTnEUUoLZduSicxkeHiWhtjIMXHP/5xAJbOnKJa7kAYorTcXo3JNiRJ4uHJAm5lgWjPLqodl6SpMVlKMJ6Po2ycb6FQoFwuc+dYjulKF3PAQDUsaq/NE55voUgyZjqGvjeHcbjExXqXiYNF7hzM8Py3z7FStVFjKkG1RjqdwQ0Cam0XqeqwpxDjno9Mbua9f/SjH6VT7nDijRNYTm8QMVcMlL7E9gJ0RUYLoquC2P47+jFTJoVTa7TX2qi6Sn5/nuL+oqhtEd4TJAlkCfxwm7+M4K3f8JIk9WpBLJUoG+J6PkohjjGR2+YJIIwiJif2XP26skRqMkugSxTmWtQXm3iqjB9GSEhYMZXMRJ56WaKjqXi2D4ZCN3BoRx3CuQgrGcPc0+v6ufVFIWw6eOsdwrqD4/j4XkhjzGOtu8YbZ96gWq3yqU99avMhds1m7qdzzHxzhvZ0m/7b+skN5yjtKdHe12b25Vm05QZdL0S9YJNetzHuGyI5lGT+5XmK+4vIiszwQ3uY9VTUlXavVjAAw1DR8xZKzsI5V+kFUhv/kyUZXYe247EuQ1/SRJJASZv4K220vjhycuO6IoFaivd2PoOQjKVhaDLVlk08jHr/Melds9uOT63jsruYYDQXw6k7mCmT7K7sNt8DEkbKwLiB4dWC8G4YmsjRqtrEYjc3oqNZt2mudwiaIR/51Cd57KnJm3r8/fffz1/8xV/gOA4HDx4E4MyZM7z00kuYpsnDDz+MovRuhvqHUqwt5skXr72rvacvideusb8wQtnp3W9NFBLs7U+S2hhFlcvlmJmZIZlM8uihcZa7CmtNB9nuEmYj5JxJMpUCRWa23CZladw7dvlnNxM3+L1fe4Afv3GadHGAuKExkottW+98pQ984AM8/PDDQG/D4Ku1I9gthzsfHrupz01494lA7xdcPh/nmd84zBsvzZHKxnj8Iztf3FRLRTM0vI5HNtZrfNDoeqQsjSiCaselL2kyJCus5WOsOx6NlTaJjIUc16h2XBpdD0tTNwMuiMD2CZouTlwjfaDAyGCKrh9yerlJytTYVYxz16NjGK9pRH7IQDZGa7m1481Eq9ViciDH6Gjftn8PvYvj2bNnOTC5h6VdXX58oUyqmKD4yUNQtfEcj07kUDYkKksVJgayfOTwADldRdMU3vjJLAsrLWoEVFeWSOomCc9l/4EB7ntmD8X9W1eqrJxFajhF+sw6cV2l1vHIXLHrEYZQbjmMZGJYEZsptpdIkkR2d5bs7iyhHyIpkqjLE95TZFkmUYixdLZy9V9e51tVliQkP0SJ7fwryTCMHVO2Q0nCTJr090sM9SfxI/CDkHLHoQ60JZCdDHMNB7fj0o6pBK02+XyWhBnHX+ngF3pz9zafs+ngTNUJajaOG+CpMl0/QHUCyjM1VqZWGTo4xCN3Xa7rq05VmfnRDJ21Dvc+fi/KCY2aC8fXW7ScgDCMUHfliJVSFP0AOQpwPIfyapna12ss1BdoGS0mJieY7ri0R1P0j2eI7N4QZsXSkJMGodvrpBe5AYrZ67LXdQM0RcXUVZq2T9zwSRoqkqEQNRz8chc9efnmV8maKFmT5nKL7FCK3aU4U3N1aq5Lu+sihyFRFBEzNA4OprhtMI0cRNSXWwzdP4SVfee68QnCO+Xpj+27/kHbGBxO89Sv306jZnPPg6PXf8BbaJrG4x/8GKsrZYJAwvM8vvGNb+D7Pvv27aNer28ee+j2fg7d3n+NZ7tM9bt8+I69N3RsIWHwiTsG+dbxZRaqXXRVw5AllhYrdGyPfFzjI4cnGMltDTBVVeHRuw5w/vx5dg9NbAak1zyvKzoKDw2n+d1/8iieF5BIiEWe9xoR6Ak3fNGJFWKkRlJUp6tkd2XZ35/k9FKDhWoHCUjFdG4bStN5fYVC2mLw8BjnlhvUFxpIlS5N30fXVRQCpAgiL8Dv+niaBMUY+ckc/cUEkgwxXaHj+FxYazGai5Epxnnomd5qfuVChcZi46qUxTAM+dKXvkShUOATn/jEdd9PL2Ui4on9fcQNlddmqkzVbSSll1rmByoxXeFwn86hHHTKyxCLsfepXQwf7mP5zDrz59Zp1lucPneC2LDCk7/zic0GD1eSJInCgQLVqSr7dIuTtS6LtS4xTSWIQmwvoJAw2IVEoj9BemznBgXXa5gjCD8vh+4dYuHNVQIvQLlyRVhTeGs64JVCz0fVFMi8vZuEpu2Rs32UKCI11KuHm1pvs2aDpankdIWgG6AOJGnM1Sk7Hqn8IPlcHEkC3+3ir7ZRCzGQeo1knHNlug2HuibTUSEKA+wgJBOEzKV05PwkA8kS88/NYzxtELgBF797kcALSE9kmal2aMRHWAtt1pebqIqMLEEQgktAPK4ylEn2Zl41HI5/5ThL3SWWO8vU2jYLYYZ0XEc1rv41LRkKsqURdjxkUyVlqnScgIgIRerlSTTtXqAHvS56wVobRlKbTackVUbdnYG6Q6obYKkK4ykTv5RA21siiCJ0RaIvZRI3VHzbpzpdo7C/wPAHhq86J0F4v7vnA1fPDL4RS4sNvve108yf8QjdOH/yL37IvntLFIslPv/5z3PPPfeQSt38fMjl5WX6+69/b1YqlVhdXaVUKjGSjfEb949yfrXFqeUGLcenP1Nkf3+SiUKcTr3MzEwDRVHo7+/fDNguDTY/f/48k5OTnD17lv3799/wuRqGirHNtUr4+RP/VYQbJkkSxYNFatM1utUuBwaS9KUMKm0PVe7dEGhhyPJsnVgpxu6DJcZv72N+tcWRY0s0z1cw/RDfC0CCSFNQdmXI9SfI5izMtwz2TVsalbbLWsuh/4qUUj2u015qbztSYGVlhampKSRJ2kz73MnQ0BALCwuMjIzw4ESBw8MZLqy1WGs6+GFEwlAZL8QZTJubr9PpdJibm+ud38E4kx8c54/+6I9YSUzRdGPMvjmL35KIJQ36byttCfpyEzkG7x0kfGmeexIGqxJUHB9VUdiXjpFxAjJZi/EPjm9bfygI73W33TnAT/rOUl9ukxu5fGOjWCrIvZEu0jbdbTtVm1x/Ej9jEobRlpTw6wnDiND2SbX9zZ+3lh2wXLeJGxqGKvdSvWUJrRQj2fXILzdZt31yrk/C6DUd8Ws2YcsBRcY9X6HddFhXejuDpqoQyqDgUdAN9MEkDV3mzTDAn6sTfOMsURARhiGxwSSvz9U5v9ZEU2SKSRPtrYszEbRcn/NrLZbqNvsVBTWpkgtyZDIZyt2QNj7Dse1TuyRJQi3GcM6WiSIdS1fRVRfHCzA1FUNV6Lg+thdgagqyoRC0fULb31I/2ZAkkrcVKNnQWGzg1BxGHh6h0He5MY3TcKjON4miiL7DfYw9OiZq7QRhQ6Nh85X/cISVC2XSA0m0rEFrrcsr37jIJ3/t7/LBJ24uBRR69W+WZdFoNG4o0LtUH3hJ3FC5YyTDHSOZq47NxHtN3oIgYHl5Gd/3twR9u3fv5g//8A+JxWJMTEygaaI05P1OXK2Fm5KdyDL8wDBzL87h1B1SpTj5vgRRFOHUHNbOV5AUiYF7BlAtFRXYO5xmpevipA2ShkboB71WwppM0tKuSOPcSlNl/DCk3HK3BHpaTMNpOXgdD1mRKZ+vsDRVQTdV1s6v8fgvP85HPvKR674Xy7LodnuNUcIwJG6oHB7OXPMxsViMsbFeDnq73WZmZgbXddE0jVQnw3/9Ny+gpguYwP0PjnLvrx7crKGTZImRB0cw0yarb66SXG4yoetERGi6SnpPnoG7B0gOiKHBwvtTKmVy+JExXvzKSdo1nXhmo+W/pSLrCpEXIClbf+10ajZEErfdNcAMEeW2u9ko4EaU2y4pSSJBr/MnQKXt4AUhqY2fvcgNkI1ekxi1L05ytU01DFhvOiQMFUlXoO4QdnxCJ8Cp2VS03iiEuK4SRhGO55NxI8x+CzNvEVdkVhsOZwlwXpwlljIY++AuXp+rc2alQTFpYOxU5yJBwlBJ6CprLYfjUzVGDIs9hwt4bY96aGAayjVTXpWciWRpRB0fNa6RjemsNnvvW1NkIg8cL+zV2mgKeG4vBXQj0Gt0PYIw4tBkgZGkyfrpdRqLvYHplbMVIiKIQItrFA4UyO/Nkx5Ji4wCQbjCG68tsnaxzMC+wmYWgxk3WJ+ucfRH09z38Cgx6+YWbs+dO8dzzz3HgQMHGB4e3myAci2KouD7/paUyusdPzTUa0Ll+z7Ly8sEQcCPfvQjXnnlFQYGBjhx4gSHDx/esWGM8P4gAj3hpkiSxOB9g5hZk7Xja9Tn6wQbg8GNpEHpthKyJhMvxrc8zgsikjGd/E3uVCmyjBMEW752adi7Xbc5/uwUR16ap+EGyFFEKbiTg9nDN5Rj3mg0eO211/j+97/PY489xqFD128JfKV4PL65GjY5PsnSCx38fAGzlKDccjj5xhL7Hhohd0VzCUmWKN1WIr8vT3Oxid/1e3N4shaxYkzU3Qnvex96Zi+1coeTP5zBd3xSxTiSpiAndfxyF3ljNyiMItqVLr4dMHnPAPsPFknVbF6aqlBtu1d1fNtOte0iA3sLcfxVe7NLZ9cLUa+Y4xZ2PNS8haQpqAWL+FCSwlSVqhfQtD0SRi/4Cd2AYKVNV5Vwgt7ijx9E2L5Pyo/IbrQtlzaeu5g0WJyvUV7vYsV0ZtZanF9vUrhWkHclqfccS47PYtOlOJombDjYC03UyaubnVxJtjS0oQTO+RqSqZCyNIIootxyCcKQkIgwii69DABREOIHEbWOSxjB7UMpxgtxAjdAtVTu+jt3kRpM4bZ7Da9kTcZMm8TEnCtB2NbKfB1JlremqgPJYoz6Sou1lTZj4zefoWPbNt1ulzDcrrvV1QYGBlhaWtoyu/NGqaq6+bh0Os3hw4eZm5vnP/+7l7nzDpfP/fa9KGKB531LBHrCTZMkifyePLmJHO3VNl7HQ5IlzIyJ1/FoLjaveowiSb0WbjfpUs3JW74IEiyeWOWlH89Sz2hkkwmCMKK2qvDT75ynNJEj0X/tuVjlcplKpcLy8jKFwtub45JOp/mH//Af4ts+32m/zCszFWS51ygliqIdy5IUTemNXxCEW4yqynzqc4ex4hrHX5hl4cQqVtrEiGl4C038hoPT8QjcACOlc9vDoxy8ow9ZlhnOxfCCiNfnaizXbbIxbduAyfECqh0PXZG5azRLCZijvJnOLcu93TjoDSonBLUvDlJvhpwxkcXqeGjrHRQvouK7qI5P2HAImzb1jXuajhMgE5FxIzJJA2tPFjV3uQmJJIHe9Kj5PsWOz9J0FdVQrtux7q0ypka13JuZl8tYBGfXoe3CdYJdbSBJUHMIyl2UvEU2pqPKErWOT9t1adg+uqr0agM9n2bDRjIUsjGNff0pxvIxwiCkNlUjtydH/x39V92wCoKwM91SCcOrf9F7Tq9O+Wbq1sIw5Oiri7zy4iJ33nkfn/rUx2948VdRFBYWFjh27BhPPPEElvX2miXt27ePu+66i1w+z1f+05uEfniN6mrh/UAEesLbJsnSVcFUFEYoukLgBMixyytAKUtlsbb9/LidRFGv/iaub/02DTYuoDOn1mnI0Je9PDbBL8RZXGhSm69fN9DbtWsXn/zoJ/m3//xPuPCjFep9XUbv6Me8xoiJnaimyt57Bpifr7O+0CQTwb57h0gMXH8IsyDcanRd5ZO/ejv3PDDK8deXOPnyPJ1ylyCIkCpdYgNJxvcVGN2VI/WWBiy7inFMTeHcWpPVhoPbcjA1ZXPxxPFCNFVmIGOyt5SkP21iV21kTe7tTBkqGUtjuW73riFtFyWpo2Yu/1zLpoo7mmRXKU66G1CpdSmHEXbHw+/6dDUJA0hFEjFVxizG0HeltwR5AIQRWsPF01XqXZdyQyI3mrnpz0s2ZOQwYrXpUBwwUIIQp+XC9iNNN0mqjDGRxQkigvUOSs4iaWokDA1ZhpSposi966gsSQzlE0zsyVNKmqiKhO/41KfrpEfTjD8+LoI8QbhJ+w728eaz09RXWqQ3alsDL6C+2GT3fUP030Qpxsnjq3zrS0dxWrBrdPymMnyef/55XnjhBXRd58Mf/vBNv49LrhwO/7n/+T7CMNwyI1R4/xGBnvCOMlIGWlzDbbubc+QAhjMxzq+2cf0Q/QYvGm3HJ65rW+rzANy2S2o4hdz1gKvLWK6xkbZF6IesvdEmo9zJD34wjRFE3HV6jUc/d3jbzpnXs+uhUT6RNFiZrpHIWgzf2S+aqgi/0AaH0wwOp3n0yQnqNZvlYyus/GSO/v0FzMTOPxsDGZP+lEG547JQ7VLreJu1Z5mYxlDWIh/TN1M1jbSBlbfolruoJZVsTCdhqNSaDkknQB3PbJmRV2m7xBMm++8uYHkhydNr5JZayCmT1aqDRIhhasQKMdRSDCVjIm1z3QptH9wANJmW7RGG+g1f364kb4yrqXc92m5AKq6z0HC2nbd59WM1jH05nIs1grUOkqkSmAoJXeXgQJqUpeLbAW7CZOxAESNlEAYhreU2Tt0hvz/P2KNjb2uBSxB+0e3ZX+Cup3Zz9HsXWDi+iqzKRH5IcVeWJz95410rAQI/JAxCCKNtdwmv5ZFHHqHdbvPaa6/dcJ3e9fQCPBHkvd+JQE94R8mqTHIgydrJtS11eoWETjGhs9J0rgrcthNFUOu67O9LkXhLhzev45EcSrI7Z3Hs+DIrdZt80sAPI1qVDnuKcTKD129l3FpucfTFWTrZBKVCnErL5Y2jSxy4f5ihOwdu+r0rmsLg3YMM3j14048VhFuZZWlYlkapGOecF1A+U8Z8y3iUt5JkiULCoHADc5kkWSI9kqa13CIKIzRVZqIQ5/SpNepJjXhKw3ADgjCiaXsYmsKdo5nNOkArYzH54UlUU+X0185Q930CRcLIWddeVfdCPNdHi1uEXoiivb1fqbKloioyXS/EC0JyKYO1lkPHC4jp199lky0Nc38eL2PgL7ToLLdJxjfesyzhth0kWcLreHTLXQIvwMpbTHx4guLBomiwIghvkyzLPPPLB9i9r8jZEyvYHY/+oRSH7x0ik7m59Mnb7uin/bnDtFsu9z10c7P8JEni8cef4uiP2/y3Lx3lV/7mHSiK+LkWRKAnvAuyE1lWj2+dpSXJEocG0zQulllrOhQTxo4r1WEIK80u+bjBnr6tqY9uy0WP62RGM6imyr3nyhx9dYFapYscwmhS5wNP7bpu2ib0dvQcJ0DP9FbhE6aKG0a4TnDdxwqCcPNkVWb4A8O0l9s0F5skB9+5DrOxYgwra9Fea5PoS6A7AXtGM3gTWRZdj64XoEgSk8UkY4XYZmfP1nILM2NSPFhEj+usnVxjZLnJybZN9jqpU1EU4foRfbJEN64hpd7eDr6c0JETOlGtQwRYhko2DFltO8S02HV39QAkRUYfSiHlYoTzdQZMHRkJt+XSXmmT251D0RRSQykyuzJkxjJbsi4EQXh7ZFlm/8ES+w9eJ9f6OhRF5qHHdr3txzebDhYpli5WcVz/prt9CrcmEegJ77j0SJp4f5z2SpvU8OWdtWLK4N7xLK/NVFmodUiZGglT49K9VBhGNGyfluNRTBjcO5bbbI9+SWupRX5ffrND5YO/dhsTd/Qzf76MbmqMHyqR2ZXZTOm6FjNrMjiconyhzJLjE7U9RrMW2T5RVycI75Z4Mc7YY2Nc/O5FWsutG1qUuRGqoVK6rcTSa0tUzlcwUgbjdw2QHEpyux/iBiGqLG1p7tJaaeF3fXY/vXszA6Gwv8DqQoO4rlJuu9fsFFy1fQxNIh4AAwmqNzH/70qSKqP0xZBWW8gShEHIcDFO19RYazk3PG4iDCNWbZeRvXlu25VHATrlDt1yl/2f2k9uIid27wThFlUsxvmlz9+NpioiyBM2iUBPeMcpusLgPYOc+6tzuG13S53aYMbC0lSmyy3mKl2W6h2kK5ark6bGHcMZxvPxq1I2u+UuqqXSf2f/ZjqVntAZvmeQ4XtuPl3STJt88NOH0L9+mvJqh9hgjHuf2k16LP0237kgCDcivzdP6IdMPzdNfa5Oaih1Q4sz12PlLOJ9cZyGg5E0kHWZMOg1b7k0tPzSzM/2Whs9rrPryV0U9l/uupvfm6d0ap29Sw1O+wErDYdcTNsy9NzzQyptF9VS6TN04gmd+HiG+dUmUQRvZ0pKN6GhZ0ykpkvoRxT7EqSKMV6ZrrJStykkjR1njkKvE+lay2EwbXHPaA5NlYnCCLtqM3jPIPm9eTG+RRBucbsnrp0SL/zikaLobfS8F4TriMKIqR9MsXRkidye7VeRO07vxsT1Q2QJdFWmtMP8Kd/2qU3XGP/gOEP3D72j5+p1PdyWi2qoGKmbb8IiCMLbU7lQYe6FOVqrLdKj6b9WKqHbdmnMNUiUEhQOFQicXi1gp9JBkiVkRSYKIsIgxEyb5PbkyO/Nkxq6up63OlXl4vcvsrTcYkYKqTgBfhgiSxJhFKEqMgVDZTiUUJbbxEox0odKfO/kCpLEVZkIN2Kh2mGPppGcaeC7PuOPjZMYSLDWcDg6X6XScpEliZSlYagy0sa5dByfpuOhyQqDWZO7RrJYG3V99bk6Rtxg36f2iWYrgiAIv4BEoCe8a9yWy/nvnKd6oUp2Ivu2W3dfCvL6Dvex64ldKDfQnEAQhPcHu26z+Moiq8dXQYJ4KY5+jY6cb+W2XNqrbYigdFuJofuHNhds3LZLfaaO3bA3x7LoCZ30aBozc+3ApzHfYPbHs9TnG1Q7Lm1NJoxAAeJ+RDaukx5OEe+Ls/jKItnJLG8u1Dm+1GAoE7upXb2uG9B2fB7fU6T1+jKNuQaTH5vEyvaaOXh+yErDZrrcYbVp4wURUdQbmWBqCiM5i5FsjHz8cifS5lKTKIiY+PAEuYncjZ+MIAiCcMsQgZ7wrnIaDhe/d5HK+QqJgcR1b67eqr3Wxq7YlG4vMf7BcVRTZBsLwq0mCiOqU1XWTq3RmGngdT20uIYW19Dj+pbFncANcNsuXtvDa3tolkZqLEXxQJHsruw7kgK6+VpeQGO+QflsmdZSr6OnJEskB5O93cDhFIEXcPIvTuK2XKK8xY/Pr9OyffpS5g01UXH9kLWmzf7+FHcMpiifKWPlLNyWi5k1t3QvjqJeHbPrhQRRhCpLxAx1S2fOMAipz9ZRdZXxx8e3pKUKgiAIv1hEoCe867yux9KRJZZfXyZwA+Kl+DVTJKMowq7ZdFY7GCmDwfsGKd1WEsN8BeEWF0UR7dU2tekazcUm3UoXr+0ReJc74SqaghbXsHIWycEkmfEM8VL8Xa8/C4OQ0A9RNOWqYHL9zDrnvnmORH+CWhDx0lSZjutTTJrXrKvruAHVtsPuQoJ7xrK05xok+hNMfmySyvkKi68s4jZd4n3XvmZeOr9upUu33CUzlmH4gWHSo6LeWBAE4ReZCPSEn5n6bJ21U2tUL1bx2h6SKqHH9d6A0Sgi9EK8jrelhqZ0sES8FL/+kwuCcMvxbR+7ZhO4weZumqIrmBnzPbW7H4UR0z+cZvHVRdJjaRpByJHZKuW2i8zlujro1dW1HJ+242FqCuOFOLcPpekut5AlmcmPTZIe6QVo7bU2S0eWqF6s4rZdNKu3y6maaq9GLwjxuz5u2yVwA2K5GMWDRfru6EN7G3WCgiAIwq1FBHrCz1x7rU1ruUWn3KG12NpcrdcsjUR/glghRnIoKZoHCILwvuE7PjM/nGH5jWXixThqxmClbjNT7rDadPCCkAiQJYmYrjCWjzGUiZE2FOqzdTRTY/xD4+T3XN01r7PeoTZTo7nQpFPuEDgBUdQLfLVY77qZGkqRHktv6XIsCIIg/GITgZ7wcxeFvW/Bd7K2RhAE4Wct8AKWjy6z+NoiXtfrNZZJ6rScANcPe81cZImEoaDSC+CcukNmPMPIgyNb5o7u+BpugNf1iMIIWZHRE7q4dgqCIAjbEoGeIAiCILyDmotNVo+vUp2q4jQdZFlGMXq1faEXEngBkiwRL8YpHCxQOlh6T6WiCoIgCLcGEegJgiAIwrugW+nSXm33mqRUu0R+hGIpxItxzIxJcjCJaogATxAEQXh3iEBPEARBEARBEAThFiP/vE9AEARBEARBEARBeGeJQE8QBEEQBEEQBOEWIwI9QRAEQRAEQRCEW4wI9ARBEARBEARBEG4xItATBEEQBEEQBEG4xYhATxAEQRAEQRAE4RYjAj1BEARBEARBEIRbjAj0BEEQBEEQBEEQbjEi0BMEQRAEQRAEQbjFiEBPEARBEARBEAThFiMCPUEQBEEQBEEQhFuMCPQEQRAEQRAEQRBuMSLQEwRBEARBEARBuMWIQE8QBEEQBEEQBOEWIwI9QRAEQRAEQRCEW4wI9ARBEARBEARBEG4xItATBEEQBEEQBEG4xYhATxAEQRAEQRAE4RYjAj1BEARBEARBEIRbjAj0BEEQBEEQBEEQbjEi0BMEQRAEQRAEQbjFiEBPEARBEARBEAThFiMCPUEQBEEQBEEQhFuMCPQEQRAEQRAEQRBuMSLQEwRBEARBEARBuMWIQE8QBEEQBEEQBOEWIwI9QRAEQRAEQRCEW4wI9ARBEARBEARBEG4xItATBEEQBEEQBEG4xYhATxAEQRAEQRAE4RYjAj1BEARBEARBEIRbjAj0BEEQBEEQBEEQbjEi0BMEQRAEQRAEQbjFiEBPEARBEARBEAThFiMCPUEQBEEQBEEQhFuMCPQEQRAEQRAEQRBuMerP+wQE4e3wOh7ttTZ2zaZb7RLYAchgJA2srIWZNYkX40iy9PM+VUEQBEEQBEH4mROBnvC+0l5rUzlfYf3UOnbNJooiZEVGUiWIIPACiEA1VFJDKQoHCmR3Z1F05ed96oIgCIIgCILwMyNFURT9vE9CEK4n8AJWjq2w9OoSTsvBzJpYOQtZ2T77+NKOX+AEZHdlGX5wmORA8md81oIgCIIgCILw8yECPeE9z2k6TD83zfqZdWL5GLFC7IYfG3gB9Zk6Wkxj5KERSreVkCSRzikIws2JwojQD5FVWaSEC4IgCO8LItAT3tOcpsPF716kcqGyYwqm54d4QYQsgybLKMrVN2HttTZu02Xsg2MM3Dnwszh1QRDe59y2S32mzvrpdZymQxRESIqElbco7C2QHk2jmqICQhAEQXhvEoGe8J4VBiEXvnOB1eOr5CZzyOrlNE0/iFhp2MxWOqy3HIIwQpIkDFVmLBdjKGuRsrQtz9dZ7+B1PPZ8bA/ZXdmf9dsRBOF9wut4LL++zPrpdTqVDpqlocU0JFkiDELclkvohcSLcYqHivQd7kPRRB2wIAiC8N4iAj3hPWv1+CoXvnOB1Ehqy6r5Uq3Lmwt1qh0XkEgYKoosEQGOF9BxfSxdZSRjcftwGuOKG7DadI14Ic7eX9qLHtd/9m9KEIT3NLtuM/3sNOWzZayCtWMtcOiHdNY72DWbvsN9jD02hvaWxSVBEARB+HkSOSfCe5LTdFh4eQE9qW8J8mbKHY7MVvGCiELCRH1LmmZMV8jGdNquz5mVJh0v4P5dOcyNYC81kqJytsLq8VWGPzD8M31PgiC8t7ltl6nvT1G9WCU7mUXRFLwgZHEjc8ALInRVppQ0KSUNEv0JzIzJyhsrAOz60C6xsycIgiC8Z4hAT3hPqk3X6JQ75PfmN7+22nA4OlslAvpSxs4PliBuqBiqwny1g6bI3D+eQ1EkZEXGylusn16n73CfWIEXBGHT4quLVM5XNoO8hWqX4wt1ql0XIpBlmTAMOL3cpJAwuGM4TSFpkNmVYfXNVWLFGIN3D/6834YgCIIgALB9b3pB+DkKg5D1U+voCX2zu10URZxZaWD7AfkbTLlUFYli0mS20mGlaW9+3cpbdNY61Gfr78r5C4Lw/mPXbcpny8T74yhab5Ho5ekKTcenlDQZyFj0pQwGMjHycZ31lsOLF8ustxxUU0VP6ayfXMd3/J/3WxEE4X0oDEPm5+rMTldxXXEdEd4ZYkdPeM9x6g6dcgcza25+rdL2WGk4ZGM3V1enqzIREbOVDoMZC6A3YF2RaK+0KewrvKPnLgjC+1NtuoZdtcnvz+N4AW/M1wnCiGLy6uwBTZXpT5ksN7ocm6vzwb1FYoUYtekajbkGucncz+EdCILwfjV1scIPvnaKlQtVojAiM5jk4Wf2cOc9Qz/vUxPe50SgJ7zn2HUbr+uRHLw84Hyh1sX1A0ztGimbO0hbOkt1m3rXI72RqqnFNJpLTaIoEnP1BOEXXBiErJ9cR0/pSJLEUt2m0fHoT1s7P0iCfNxgveWw1nLoT5vIikz5bPkdD/S8rkd9pk57tU0URZgZk8xYBjNjXv/BgiC8p1XKbb72xaNU5+qkh1IoikR5ts63/+wN4kmDPXvFgrTw9olAT3jP8doeRGwZSlxtuxjq22tyENMUah2XtuNfDvQsDbflEjiBmIMlCL/gAifAaTvoiV7GwFLd7tX0Xqe4QVNlgjBirdkL9PSETrfSveHXddsuzYUmoR8SL8WJFWJXHVM+W2b+xXnaa20kRUKSJAI3wMyY9N3Rx+C9g9t2BRUE4f3hzaNLVGdr9O0voai9+57+pMHC8VVe/+mcCPSEvxZxhyu854RBiMTWXTYvCJHlt7nztvGwILxikojcq/uLQjFdRBDe75ymQ2etg6zKJAYS1+182e16tJoO2VwMVZUJ/ZAoiDYDJtsLUa8X5W2QZQnXDwGQFInADwiD8LrBV32uzvSz07TX2hCBntQZvG+QgbsGNrMMKucrXPzeRZAgO5ndfM4oirArNrMvzBKFEcMPDIvMBEF4n6pXuyBJm0HeJUZCo7zU/DmdlXCrEIGe8J5i2z7zc3WaTZsrk580ReavO/JRuTJQDEGSpC27hm/VXGrSWmqRGk4RL8V3PC6KImrTNdymi5kxSY2kxE2XIPyMrJ5YZeGlBbrVLpIikRpMMf7EOPHi9j+zLzx7kVefvUi37pAuxXn043vZv7eIJEubCz+GKuPf4CJQGIbo6kYAFkQounLN6wpA4AbMPj9Lt9olN5lDkiXaa20WfrpAajBFoj9B4AUsvrJIFEWkR9JbHi9JElbeQpIlll9fJjeZ2/H9CoLw82HbPmEUErOu3VsgmTaJIgiDCPmKkVFexyfzDvxc+37IiWPLZHMWo+PZv/bzCe8vItAT3nVRGBEG4XVX2ZsNh//y719l8dVFlIUmdkxl34ESABlLZbHeeVuv33UDDFUmpl/+dvdsDzNpohjbn5Pv+Jz+9nnWL1Qp7Stw12dvQ1a3X6FfOrLE7AuzhH6IaqiMPzFO6VDpbZ2rIAg3rrXcYuZHM8iqTG4yRxiE1KZrzD4/y75f3nfVrtrxY8v88C9OIElgZS3Wpmt8+/86Rv7/8SBaTMNtuxgpg760wXS5TRhyzfTNXqaBTGEj5dNre8T74tdd6GmvtmmvtkmNpri43qbl+Nw+lKZytkxzqUmiP0FjvkFzuUl6LL3j85hZk9ZKi9pUTQR6gvAe8saRRZ796gl8N+QDH9nDox/aveOxt905wNHnplg5u05uNI2syNQWm+gxjTvuv/68X8fxOXFsmcWZGpIsMTaZZ//BEurGPctLP57hB39+jETO4vP/66NkszvXHne6Lm+8ukguH2PfQXEfcysQif3CuyqKIs59+zxv/pfj2DX7mse++foi82+ukBpO4gcRZ16aJwx6q+qD2RiarOD4wU2fQ73r0Z8ySVtXBHodj8RgYscbsjAMOXXqJCurK70bvR3u2wI3YOXYCpKpYo2lCSVYfmP5r737KAjC9TUWGrgtl0R/grYb4EaQHknTmG/QWbt6YejciRW8rkdhV5Z4xqRvb57WWpsLZ8sU9hdw6g5RFDGYtkiZam9+3jVUWi65uE4paRIGIYEbkN+Tv+ZjgM3ried4nDp5EvmK69Cla5JTd4jCaMsC2VrTYa689X1pMa2X/ikIwrvqzTeW+KN/8UPefGPpmseFYcgPv36a+nIbu+Xy4l+doVrduXa3WErw0d84TH4sTW2pRXmmTixr8fiv38aB2/qu+VqNhs2f/fHLfP3fvcorf3WWl79+mq/+m5f4b188irMx6iUiJAh9JFXmeslGL3z/It/6D6/xtf/wGkuLjWsfLLwviB094d0V9YKqV156lcThOJOZyes+RDJU0BUi+3JQV4jrFJMGKw2bvvSNN2XxgpCIiNF8bPMGKgojIj8iXorz5uuLzJyvkO9LcP+DoyiqzNmzZ3n++eeZcqf4zK9+hr337Nm53kbq/T8KI44cOcK+0sRV9YWCILw7ojDaDJpcz+eNo0d48N67N7MIbkZ2V5bF1CJOw8FKm9w2lObVmSrllks2pm/Z2QvCiPVWr0HU7UNpFEWitdImXoyTGctc9dy27dNsOhQ3dt3ipThLzSXO/vAselHntqH7aK+0MZLG5W7DElddSyTg/LkzjOTvuqn3tpMwCAm9EEmRkFVZpJwLwjV0Wi7ddpepC3PcfsfANY+Vdvj37dRqNf7zn/9r8vki5nicj33844zvKhC/gZnBz33rHNNHlihN5tBjvWZznYbNmR/P8OJoimSuwbM/+ib3/9JT3HX3QTKZa3QSBmRZ7o2gkiVxPbhFiEBPuCGhHxJ4AaqhXrf+5EqSLLH3Y3s41T3Jf/3Gf+XgxYM888wzmObVbcFvv3OQU4eXWDyzjjGUZDRlIsmXn2dPX4Jyy6HW8chsXNCuec5hxGrDZiQXoz91+eLWrXSxchYrHZev/PErdMsdFEOlUenwkV8+yNDQEI7jkB5Ms+/Rfdue6yWKptB/Zz+zz8+ycHQKd6DFr/6vvyoukILwNt3MyJNEXwJFV7DrNrm0SWVtie/8xXeYvH2S+4r3XXX8nkN9nHxhlvWpKlbGpLHSJlGMs+dAEStnkZvIsXR0CT2hM17oBWWvXlxlueGjyDKKJOGFEUQRmZjOHcMZ+tMmgRtgV23GHx+/qotvs+HwZ3/8Mo3lJg98bC+PPTWJoikU7i1w7NgxCkGByrkKRtpg9JHRzXpgM22C0ssaUPTe4pYeOSxdOMX8UJHhkV5Kl9fx0OIa7dU2sipjZsxrXqOjMKK10qJ6oUrlQoXQD3s1f1mLwoEC6bE0mnX966sg/KK578ERBoZj/NEf/ytkfZWHH36YbPbqmjdZlnnsk/t7qZtexAc+MrljuuTZs2f50pe+xPPPP88jjzzCa6+9xqc/8+QNBXmtlsPZ1xaIF2KbQR5ALGXStrr88K/eQC+dR5Lg0Q8eIplMXuPZeh59cjchbY6fPEL/wPWPF977RKAnXJPbdlk7tcb6qXUCJ8BIGRQOFCjsL1y35u4SzdIY3D3IhYUL5HK5HQOnZMrgN//+/SzM19HCiLUfztJZ72zWngxmLO4YyXB0rka55Wyssm9/Q2N7Aesth4GUxT2jOVTl8m5ebaFBN2fy+hePUnlpHjllENYcftA5ytpKi137M/yNv/FpyuW1awZ5l/Tf0Q8WqBdVVuQV3lh4g6cOPHVDn40gCGDXbNbPrFO5UCEKIrLjWfL789etO0sNpxi8Z5Dlo8u0l9tk/AyVqMLuD+7eDI6udNvhfuqfOcQrP7hIt+FQ2p3lkY/u2byhGbp/iG6lS/VClczuDJ2VGaL5czzw6FO8dvI8pcFBDFWmP2UxkDZ74xXcgOrFKoUDBfpuvzrN6uypVWZfXcCpdnlZlTlwxwDFYpzRg6P87r/8XeZOzjE5MUm8FO8Fd1e8t+RAkuZik8x4BoDVlVV0Q+fo60dJpVN4Sx71uTqSLLH65iqyKpMaSlE4UCA3kbsq4HNbLrMvzFI5X8F3fMyMiaIpRFFEfaFO5UKFeCnO8APD5PfeQAqqIPwCkWWZ0bEig4ODvPnmmwwMDHDPPfdse+wddw+y/1CJILx2M5Y33niDM2fOYFkWx48fR1VVTp06xe7dO9f0XdJuuXh2gJm+er6wHtfQ0bjz3vuZnZ0ikUjc0Hs0DJVk1qbZnuenP/0pDzzwwA09TnjvEoGesCOv43Hxuxcpny1jZk1US6VT7nDhOxdor7YZ/+D4jg1K3mp8fJyJiQlardY1V+wNQ2X3RO8GQ215TH1/CiNloBq9b9WJUgJVkTm+WGe5YaPKMklTRZEloijC8UNato+myuwqxLlzJEvsihu++ZOrvHl8hYYq40ugFCyCjo9SsIgVLc6/NM+J5y9y+PE9/NJnb7+h9yZJEkpW4e6P3A3AY489dkOPEwQBOusdzn/7PM3FJkbaQJIk5l6ao3yuzO6nd1/VcfJKkiwx8tAI2V1Z2mttoomIybsnceWda+sefnw39zwwSrPpkM1amw0LAIyUwa4ndzH1/Sl+9NUfsd5dJz+Ux/KbeAsnuevuCRLJ3g1TFEZ0yh06ax0K+wuMf3Drbt7SYoOXfjTN6ZcXsNc7BG2P9XMV/uMf/IiB/WkO3zfAHXcOMjg6uO15yqrM0P1DXPj2BeqzdZKDSQzDYO+evQz0D9C+0Gb59WWyY1lIG3QUGT2MqE5XqU5VGbpvqDd2YSPYc1suF75zgcqFCunRNNpbsiKsnEUYhLSWWlz47gVCP6R4sHj9/4CC8D5y+sQqSwt1DEvj9jsHSCavDpKuZ/fu3YyNjTE6OnrN4wzj+rfYTz31FOfOnWN2dpalpSUMw+DkyZN87GMfu25mQyptYqUNOnWHeHrronS37hDv13jiicdQlCduKstoaWmJMAw5c+YMd911F4Zx85+R8N4hRaJrhLCDxSOLTH1viuxkdsvundfxaC212PdL+8juvrlWvZ7nsbCwwPj4+HWPDbyA8986z/qZdXKTuc06uZWVFd48eZrJ2+9lptyh2nEJwghJ6o1hGM1aDOVi5GP6lhXttZk6P/3WWRqGTN89Q2iGQqdh06nY6HGdVDFGo95AQac2V2ffg6N8+rfuuqGL9SWnT59m//79m3+Owgi7biPJEkbKECmdgnCFKIq48O0LrB5fJbdn6w5U9WKV5ECSA79y4IYXlC5ZX19H0zTS6Z2DxGtxGg5TP5niuS8/h9fyCJSAltti3/597J3ci9Ny8NoeVs6isL/A4D2DW4K8ixfKfO2LR6nM1okXYqSKCRRVIgwiGmstygtViqN5Pvo37+DQ7f3XPJfqxSpzL87RWm4BEBGxOrNKWAlJjmVY74sxV+ti+yG6IjGYttifsogaDpMfnaSwr9D7nL9zgZU3VsjtyV3382wtt4jCiH2/tI+kSN8SbhEvPHuRH37lBF7bA2DwUJHP/e79JFNbAxnX9Tl1fJWl+TpRGJHJxzh0Rz+p1NZg6uLFi4yPjyPf4MzN7Xzve99D13Uee+wxnnvuOer1OnfffTcjIyM39vi/OssLf3GcRClBMm8RRVBfbdGutPngrx3gyWcO3fQ5rays0Ol0GB4eRtNEGvf7ndjRE7YVhRHrp9cx0gaKprDacJgtd9jbnyC1sRJcvVi96UBP0zSSySSVSoVcLnfNYxVNYezRMfyuT/V8L5Vqdm6Wr3/967iuyxOPPsJAUuPsxWlGx3ahyBK6IqNrV190u5Uux348QyOK6L93EHVjly+WMoltXLztro1hGBimgSJLnPvpPK/syfPIE9dPobhElmXCsNdy3bd9zv9givPHVlBViQMPDDP60OhN1TgKwq3MrtnUZmokBhJIssTZ5SZuEHKgP0VyKElzvklrpUVqKHVTz1soFJiamiKZTL6tmzAjZZC4LcFv3vub1KZqnHnhDHPTc+joBF5AvBin+MEimbEMemJrWla53OYbf/oG9YUmAwdKW4Ygy4qEHIsYv3OItfNVvvV/HSP9DyyGr7Frmd2dJTmUpD5bp1vpEoUR3aiL0W+wlNE5vVAnE9NJWzqOH3BhvUUIHJBl1k+uk9+Tp73Wpnq+SmokdUNBc6I/QflsmfLZsgj0hFtCs+nw02+dQ5JlBg+V8GyfxeOrvPbSLI8/vWfzuHNn1/nufzvO2kytV7+68fWf9CW4/+lJHn78cmA3NjbG7OzsDS1cb6dcLjMzM8Ov//qvA+C6Lrt27brhIA/gsad206i0OfPKIksnmyD1FpX3PFzkiQ8feFvn1dfXh+u6rKysMDx8ebxD4Ic43v+fvf8Mk+vO7jvxz42Vc1XnnJAzQYIEM4dxImekSQojaSRZYR9bluPKXlmy9Pc+lp6/41q7XtuKY00UJ5GcGeYEggRJ5NiNzqlyjjfuiwIaaHQAQHI8nJn+vOFDdN3bt27X/dU55/c932Ncdy7gBh8sNhK9DVbFMi3MhrlUpV4s1JjKVoh4VfwuBckhoVf1FcdpmkEmXUV1SEQiq/fXRCIRJicn8fv9yPL6H0Fn0MnAgwNMvjDJWz94ixPjJ5ifn2d4eBgEeO65Zzl39hyPPfYYW7dtXfV9FGYKVCoaecHGtz2GrK78nbZloWka/kAzoHT6HEhOidNHZjlwV98yedd6RKNR0uk0LS0tzB1b5AdfP8UCNqJtMzuZ5aNhF203MJvG1ExykzlsyybQE0C9gcbsDTb4ccPUzOY6E5UxTZupTIWGZtAX8eB1yk0TqMbNj1SBZhA2PT1Nf3//TR9rWRaWZeGNevFGvcR2xEjMJJAKEsW5IrJTRpTFFcYrAMePzJOZztO2ObYsyYMrxSRJkmgZirB4NsnRwzN0da8vE5cd8tLYBr2mc+HVC8gxN9PxIn6XgvfSdbhUiajgYDFfY7gnRGmh1Ow5nMih1TT83htPmN1RN5mxDO172nH4N6RbG/x4Uylr6DUDd6j5WVacMggC1cqVOGZ6Ksd3//IopWSFSG9wyeDE1E1yi2Ve+vopBIGl4q8gCAi2g4sX5xgY6LjpotLbb7/Nli1blvrnisUiIyMjN3UOVZX55M/v4ZU2P++8NIFe04kNu/jkZw+8p51GVVXRtCsS+EpF4yv/7W3yiTL3Pr6VfTcw32+DDwYbid4GqyLKIq6Qi8JsAVfYxXCLD5dk8eTX/poP3XcvYcK07lxuPDA3W+CpL58kO1tAVEQ23drFY5/cirpKYtXX18fExASDg4PXvRZXyMXwY8Okqimm3niVgOHHr/pZWFhgcmKSfD5PMplk67atS/1/pmZSSVXQShqBngCZuk79QorwGslnsVQi4FseBAXavKQm84yeT7J1+/ryqsuEQiHeOHyCU0fzHP/bk8ydSmE4JSwbRhsmX7Vh1+Nb2LG3fc1E2LZspl6aInEygWVZRIYjjHx4ZFVziQ02+KCRz9d49blxsokyoZiHgw8MrPlZV70qqlelUWzgjrrZ2xvi2PGTHH7lHIM9g0TcEVTfuytyiKJIJBIhlUoRi91Er5lhEH/+eTo/dMVQKZ/NUzxTpDBeQHbIS4qHtj1t9N3dt7RLX68bnHlzDodXXZHk2ZaFYRhLPX6iJOAKuxk9ukDxkeEVsrC1sHQLSZComzqaaRG8xiHTqUhkyw0aAsi62QxSJ3LLjF6uxjRtJGmlysAZdJK9mKWSrGwkehv82BMKu/DF3GSm8sh9EvWyhiCLRK4yfHr9+XEKi82dePGqZ0JSJKI9AdJTeY48d5Fdt3QiCPDU108zdSpBpVplaGcnH/v87uuOL7hM+sQJMmfPcvdv/MbSv5VKJfz+m1MvQHO+3+tPXaBeqGPYJsXDFb4jn+Izv7L3PSV7V7eazM3mWTifopqucf7E4k0leplMhYnRDNt2t23sBv4I2Ej0fsqYGM/w5kuTON0K9z06suaiJAgC0S1RcpO5ZhDmdzDSEeatgJ9XvvcKAV+AX/rILy29XtMMnvrySebPpgj3+tFrBieeGScQdnHfQ8PLzj03W+Cdw9P4Qgpeb4LW1vUHggIoMtzy/F+w++QTPPf5v0/W38bpl05TmCwgGAK5yRwX6hcwSga+dh++Dh/eVi89d/QQHgpz+isnkRRp2eJ9mVqlitvlgmsklQ6PiqVbZFcZvLwWbxya5qm/voBWNLByFRB1BI8TxQavR6FUbPDS357gnRcmuO+TW9m7f+ViqZU1cpM5xKCC2+NYkmx5227MNWuDDX5UVGsa3/jzd5g5kUBxKYzXdBam8vzcb6zsgwFQPSqRTRFmD82ieBSiXgc7h3t46YUJytNlDj5+EHfUvewYy7J49skLXDwZZ9O+Dj706KZVr+XMqThzU3liHQJ+v//GDAVmZ7Hvuov2xUWERmPpn1OjKYoTRYJ9QSRVQhAEtLJG4mSCyFAEf1czOEsmSpRSFXwt7hWnLpXKK4I4f8xNejLPwlwR/9YbS/QkVcIf8pPK5nDIElXdJHCV4qCmmTgUCYfdfK0gCpi6ibiKpN22bDKlGk6Hgv+ahPFy8moZNzePcIMN/ldQrWm8+uw4lbLG3tu76etfvxXE4ZB59LM7ePrLJ8ktlJrtG9ES3mAF27ZJJspMn07ia/WtGicAhDp9JEeznD0ZZ2E6z9lXpvC1evE7fJx+ZQLFofC5L96y4rhKRVs+LuF73yP8kY/w2P79uP7hPwSgVquh6zo+n4/pqRyn3p6nZyjMzt2rmzVdzTuvTNEoNWjbHKFSqSBaKhPHFpmazC2Z270bRFHk6NGjJJNJTp06i6dzBH8swvZ9nTd1npd/cJGzh2aoVXXufuD6xf0N3l82Er2fIjTN4Kn/eYLUeLb5DzY8/vlda74+Mhyhur/K4tFFKokKoioSE2KUrTLdB7vp3da79NpMukpmtkC419/sefNDvagxM5aBaxK95751lrHXZ/G2ePD6wO/343ItTzgbDQPLsnG5FEgk4OGHcZ06hYDF4//wE6Ri3Xz9r77OLzx/nKzoZdLeS3I+S6g1jOJVGPnICIHewJKJjK4ZCKsMPbcME8M0cHlWBmYAtg3GDQY6r744wcvfOE0pV6ImljC9Bv3RDqSygCAKeLv9KN1+bBvSUzl+8KUTGIbFrbcvd+4SVZH59DzPf/sFdu/Zw57bdiG7Nh7VDT74jJ1LM382TctIGNWpYGgmifMpLpxLcMttqzvUdezrQCtrpM+lsQwL0Rbx6l4G7hhg3yf2rTAwmpsp8M6z42hVjWK6yrZd7bR3LE+gisU63//bk2Qncwzf2UMgKK+qHpgcz+L1qcRavPDMM/DJT0KlAlebuFQquB7/BUq/8E+RR/YykSqjSiJdYTel+RKVVGUp0TMMC8uyV/TB1arV5hp3TfwoSiK2ZaHrNy5PlZ0ynm4Pp986jemVqbljCIBblakbJvmKxlCLF6nUwN8fxh11I4oiprH8d+iazsmTJ3n58BF+/Yu/DKxuurDRU7zBB5HXX5zi0DfPgmmzMJ7l1//ZXauqh65mcDjKL//uQZKJMk6HzJG3X+Kv/uovGRoaYmToAI2KRqB17ZEuzXjCJp+pMnUmiSvkwn9pR9DQDC4en6VW29WMWy7x4jOjHH1xirs+uplb7+iB3/1d7P/0nxAsC+/+K7M+G1/6Ep/7F/8C5dd+jWeeOMPE4TkuDIXpH4xc1xk0n6qielVK5TIBfwDLsikslCjm6zdwJ9cmFotx8uRJ3jh8FKfcyXB/O1rdYGosjcMhM7wpinSdtpZGo8HU/EnwO+kbXD8Z3+CHw0b0+FOErls0KhqqR0Wr6lQra1uQwxXr8kBvgMJUAa2icUvbLdzfcz+JcoJ4PE5bW1PSKCsSkixi1E24FHOZmoG6Sg+L06MiOyVUl0JvXxfz8/MMDQ0te41l2VjWJUPYuTms0VEEywJRRFRVWntb+cKeXrzZU1QVhd8PHiA3XaQ/OMTmzYME+4LLgi2XR8XWjRXXUiyXCAaCq75/27IBG9Vxfcnk+TNJXvvWOSRFomUgQiKuIysysc092JrZ1PJfkl4KQMtgmPRkjpe+cYZoi2dZ1e27T3+XM8UzFOUCRSFP3319a8quNtjgg4RhWNiWhaw0n3tJFrFprj1rITtlBj40QGxLjHLiktvjxzcR7A0yPTvNUGj52uDxqTh9KrV8HZffgcu9MkGRFRGHW0H1qri8Djo7O5mfn6ez80ol2rIsyuU68mVH4bffxq42d++Fq2dO/c7voObTLD79PZ5Jj1K2JD764Q9z2bD66nXG6ZCRFQm9bqI6m9dlGSamZeFSV16n0TAQFRHnTQwoz2QyPHn4SdLjaYZGRggOBZlMV8hUGqiSyOY2P0NuFbuiE9saQ5RE3DE3mQuZpbmEtVqNF198kcWFRVRMZqYm2Lp1eY+zXtWRVOldS2c32OCHSa3awDZsFI9KvaJjmBY38kn1eh14vc3EaUtlM8eOHaVer9PSEkMUJrFMG2mdx9G2QZIlJEXCKl6VSFkCgiRSb1Rxua4Uioq5OtV8rZl0GQa8+CJYzfVQDgabLyqX8fzu76KUyzz/7/898VQ7zoATj9+BegMtG13DYd7+3nk6QlEQIDdfxB100rmOydONYJomi7NQm+tGayhcWJxFEAUs3eTkS5O0j0T58Gd2rCi0XebYsWO8/vrrjE+dpaOjg87uT76n69ng3bGR6P0U4fGo3PrQMEd+MIa/1cut91zfpEAQBQLdgaVZVkM0g65NxiaeeeYZ7r77brxeL7GYh5FbOznx7AT1YgNDM3CHXOy+baV71Ec+vZ2BLTFa2ry0tvkINlRmZ2eXOU1dXRFj3z7mbrkF//Q0gUYDolEolXB/8YsIgMsw2OzRmNnswdkt0v9A/4qKevdAmDMvTWJo5pLjZqVcxuPxrKiyX6acq+HwOejuvb6z6NuHpmiUG7jaZLzupsmM6lCbCd4a4xmi/SHmzyQ5dnhmWaL38MMPc+zYMapdVao9VXJ2jhA35266wQY/Crp7A/jbvSTGMnhjbirpKt6oh56+9T+/oiQS6AkQ6FkemHR3dzMzM7NsXlUk4uETX9zH9HiWgZHoqvJzt0vlM79xKwuzBYY2RXE6VWRZplwuLxkfiKLIjl1XyaL+/t/H+tM/RRwehsuJ3vPPw//4HwTwEZ2dxLF3C8gajXyaUiWII+hY5gja1uEj1h9k4VwKT7BZnClXKvjcHoxkhUa6ilEzQATV66BU1QkNhui9NBD9RnA6nWzat4lKpsJw3yBiSQPNolAxcKoibbIGiHTd3kVosHnfo5uipM+lMXUTSZFwuVxs2rSJ8YvjKKrCzMwMW7ZsWbZ7WklWCHQH8LZuSMY3+OCx70AP8xN5KrkaBx4Zfle9X729vdx///10dXUhiibeqJtiqkJkjQSpXm4gOSW6eoPIisCrXz9DcjyLIAgYdYNbP7aJbCZNMOBfepY+9JHNjGxrZWA4ArJM8h/8A4K/+ZsonZ1wOeb5nd9BqlaxAPe3v82D//b/z4vPvcNnfuORGxrvtP+uLibOLpCbKWHZJZx+B7c+MtxUKrxL4vE477y5wMzbNcL+GOEu31IB7/K9mD2Z4ImKzqf/3n5isZU7ocFgkHw+T/VSAe3kyZPs2bPnXV/TBu+OjUTvp4y7Hxhk721dKIp0U/PhrkWWZe666y5eeeUVHnroIWRZ5sOf2oY/6GT2Yg7VLbHnth62bF/Zf+f1OpbJFR0OBw6Hg0KhsOrcq/nnn6fj8GESzz1H8J57ALDOnUMHREC0bfonxpndv59qsIoztHL3a/vudl5v81JIlIl0BzAMAxvWnRFTTlYY2N9Jd29w3XsxN1tg6uQittMkEIwiiiK1WrWZRF4HT8TN+PE4qYcrSwtlIpHgj/7ojzhz5gwDAwPXdSbdYIMPCi2tPh77/G6ee+IMlVyNUKefez++mc6ud1dZdjgcBAIBkskkLS1X3GoHBiPX7T2JxTzLgo/W1lYmJibweDyrzrM0/v2/x+rvR3rzTbj088bx4yhOJ75akWHtIkfyRTTRxfypeaIHonQf7MYVvpJoiqLIjlu7mD+dpFHR0C0NJW+SO7NANV+nalhYkgC2jaTbiFWd3g4/9VQV9QZGSKRSKXRd56Mf/SjDw8OU5g0Of32c3Jk0GCa2KDLf7efeX9pDxy0dS+/T3+3H2+alNFci2B9E13WCgSC//du/TSKRIBqLLrsnelXH0i2iW6Ib0s0NPpB0dAX49X98EMOwrivZXAun08k9l2KKkydPMrAnxtGnxtEi7iXHzcuYhk12ukDHthjDm6JN2aIkcfbtOWwbhna2cu+Dw9i2yezs7FJxyuNRr8RBto3yR39E5jd/k/Z/9++Wzt24cAHVthGAbbOz/M07b6I4awSDN6bk0fQ8v/UvPsS5UwnqNYPegdB1i2trYds2ExMTqIqP82+kQYSWvtCKgrjT66BtU4TFcykOPT/OJz67c8W5nE4nv/iLv8ihQ4dwOByo6oY64EfBxsD0DdbEMKzrjhVYWFjgwoUL3Hfffe/59602fNS2beYOHsT0eul75hkAkskk1WqV3Nmz7Pnwhxn95jcZeeQRzk5MYNs2IyMjqyZwzzx5ntf/7izBHj+aVSd4WTaxCrnFEkbd4OO/vp/tO9d33Py7Lx/hyLfHGNh3ZUZePpcjGLr+QmuZNvFzSR74hT3cdf8A8Xgcn8+Hx+Ph4sWLKyStG2zwQUGraJiaiSiJKB4F8aoeWMOwKJUa+HyOddeQSkXDNK3rOk7G43E8Hg8+33ub6WYYBnNzcyvnXuXzmL29SN/4Bjz4IJZlMTs7i9PpJPaVr9D49rdx/Y//QdEV5shLb+Hz+9h95+5V3SjrdYOv/fk7jB6exomFnqhTNCwMp4SqyIhCU/rUyFQh4KCrN8TuLTFu/9S2FTuaVzM9PU0wGFxWDPuzP3mW+Nkisd4AiixhWjbpuSKBdi+/8o/vXHZf89N5xr8/jmma1JTakuw+k84QiV5JmvWqTmG6QPvedvru7bvpYfUbbPBBQtMMDr8yRVdvkMHh6Jqvs22bQ4eOMHrUZObYIqrfiT/qRhAFKvk65UsjFx7/lb3XTaIymQyiKBK6JgZI/Nmf4f+930OZmUH2+6lUKiwuLjbn1t12G41PfILE3Xfz6ugomqbx+c9/no6O9c1Y5ubmiEQiK3wO3g31ep2ZmRn6+/t587VZnvub44T7/CCC07X6Gp1bLCGKAr/6v99DKHTlGhqNBslkku7ubgzDIJlMXve9XMa0bNLlBqZl43PK+JwbQ9vfCxtbBRusimFYiGJzQOZ6zbYdHR3k83mOHz/O7t2739Pv7O3tZWpqioGBKwPKZ598ko633yb92mtUq1Xi8TgtLS20tLSQ++53qQQCOPfuBacTt9u97uDSex8aIpOocOL5UUIdASy/jXjZWa5houfraJpBvaxhygJ3fmLrukmebduMjY1RKem4ve5llW9FUdA1HWWVvpyrESUBQRAoFes0Gg0Mw7ihncBrmUtXOHkiztxsDlGSGBqOsGN7KxHvhi36Bu8flmFRmC2QGc1QmC1g6RaCKOAKu4htjRHsC6J6VGRZXPalvxblUgPrBhK9trY2pqamcDqd6+7CXw9ZllFVN/PzCTo7r6gN9H/7b7G3bkX60IdIpVJUKhW6u7uRJInG+fOImzdDfz92ocCtj926rgW60ynziZ/fxZ+PLbD4aoKKbSGH3TgRsRsGZkXHBrxdfjp2tFKybU6MZ1G/e4E7fm4nzmuq+JqmMTMzQ29v77L3vrhQJH6xQLAzgOPS/ROBlsEQydEs504luO3gFcOsYG+QgQcHOP6t45CDiljBFXEt9RpqZY1KsoJt2rTvbafnzp6NJG+DH3tmp/OcOTLH4kxh3URPEAT279+NKJ6ke2g7Z96YJbdQAtvG4VXZ/eAAB+4doOMG1AlXzwqWpGariG0YOP/NvyH/279Nq9fL5OQkTqdzqZhbXVyEAwfY88gjhLdsWdZPvBblchlJkt6XJC+dTlOpVJbm+E2PZRAkEafHSbFYXDPR88e8JEfTzEzmlq35c3NzSwZYjUbjhlyPLcvm6PkkR4/MkR7LYOoWzoiLzfs6uOO2HiLXMaXZYHU2Er0NVkWWxRva0QPYunUrr7766opemptFkiTC4fDS3CvLspD+4A+YefhhpNZWisXisiTQHh+nGIvh9/vRdf26EkdVlXnw4wMYdp2FMwUWzyRxeFXskkZxKodWaIBho4Sc7Hh4iHvuX9sGuNFoMDY2xtDQEEfl0pLU6zIut5tSqURAvf6Xgk0zoZ6fn1/2/m6UI6fivPCN09TmSzgEEcu2mXl1mmPDET72uZ0MvkvZ3AYbXI1e1Zl+ZZrk2RSFmk5eEajbNrIN/kyV9ESOULef/vv68bXf2M6bP+DENG/M1bavr4+LFy8yODi4qvTyehSLdZ77zjkmTiaoVGtsvbWHhz+xDX+jhPSf/zPat77F5NQUsVhs2dw96+JFHB/96KVzFJf1Eq9FPpdk99ZO0nEdh25AXsOsGwgiOFq9BLr9BFo9SIqEE1g0Tc6dTzEylqFr/5UAL5fLUSqVVt3Zr1Q0LM1CdS03a5AUCRubWlVfcYyny0P3g904K05S51PkJ/IUcgWEnIDiVAgNhohuihLqD20keRv8RNDXH+aW+24sQXM4HGzdOkw8HufgfXcTXyhhmjaRiGvNGbxrcW3hOvEf/gO+eh1+7deYmpqit7d3KQk0ikXc+Txs3750/PXiGdu2icfj74vqZ3p6Go/HQ2/vlcJQo2EgXR7Jso7uT5IFuMad/GqjPmgWq64n27Qsm2dfn+aNb55Fymt4Qw5EWaQ+XeCtsRwzoxl+9ud207JhTHfTbCR6G6zJjSR5lzl48CDPPPMMfr9/XUnkepw/kySxWCQYtQgEAiw88QSdp05x+o//mJ1dXUuL4tL1TU9TikZp8flIpVJEIuv37Ni2TS6X5hd+9SCLC0VOH1/kzPMTzB+L0zAs5G4/3i4/yCLzM3mOvzjBvoeHV5wnk8mQTqfZtm0bgiDg8ijY1wSroti0Tb9R6o0Kra3XN8e5lplkiRe+fhp7sULHUBjhkoOgWdVJnUvx5DdO88XfvA3ve+jH3GADo2Ew+dIkcyfiTIqwqOnoNRNFkrBsG9u2CbkUhqfy2M9MMPTY0JLD43q4bsJtEprJ3vT09Lo792vxna+cZPTQLN6YG6fDyVtPn8fQbD518q+p79pFdft2+ttW7uDL09OIlyrTlm4tzRYVBAFnyImvw7dMtloqldAzOotTJaQeH5tiPrS6jmVaTZmrQ17R9xb2OMgVdcbeWaBtZyuyQ2Z+fh6Hw7Fm8ayl1Ysv6iW7WKBt4Epi2qhoiLJEdBUTlbm5OYZ2NAPD1l2tVJIV5qbn6OzqRPWquGPudZPoyzK41GKJQMTN7Xf3LTkYbrDBBxFJFleMMFoPr9dPYmEGy5pl69Z3N/OtWtMYO5fGF3CTSCSIBYN4/uRPmPzCF+gJh2m/RhFQOX0at6ri7r/xGGC9wrppWOQLNYIB17qKLMMwmJiYoKenB6dzeQLlCzgx6yudyq9Fq+sIsoj7Uk9jo9FA1/VlyqRGo3FdpdL4YpG3nryAp27i2xxZWiOdMQ/ekkbinQWeb/PyuU9uX/c8G6xkI/rb4H2h0TDZs+cAr7zyKo888vBNN91OTWb5zp+/QzlVpWtXC6ZVJvKv/hUTH/4wex59dMXrDcPAtbBA9ZK8ql6vL6sgrfo7pqaWAsT2Dj/tHX7Usk4hV6NvJIKiXEkkF2bynH5jjh139qJeNeh0amoKRVHYtOnKkOaWDj8gLDnaQVNjXqxb1At1bNtGEARUWSTgUpCuCvL0ugEC+EPqu5Jsnji+SG2htCzJA5DcCtHuIInRNGdH09y6Y/17s8EG65Edy7J4Ksm4aDNbbBDzOXAoVwJ807JJlxqcVUTE2TyuI/MMPza8Imm4/Cy8W8ZHs9TqEHfGr/u8X83iQpHZMymCXX68l8xTBMnmzMtnuffbX6fl2W/hX+18loU8MwODg2QvZpn83iQOw4GmmSA0hzD7O/10HejC3+XHtm1SqRQu20U8X8XZ2nT1Va+T0DoUCd0lkZgvUk6WyWgZOjo61pVk+f1Odt7ZxytfP0l2vog35KJe0Skulujd087I5uUytUQiQWvrFbmq4lII9gYpiSVC3Tdm3PDU353h5LMTIDXXu8WZAp//1Vtuqii4wQYfZF57cZxXn5jEERHp/CfRFQZxmUyFes2gtc235uf+ya+d5tyrM4S6/Tz4s70k//iP6RYEtv6f/yfiKrFR4+xZaG0lIAhUq1Xc7tXn+l6mUCjgcrlWjbM0zeBrf3mUuXNpOjdH+cwv713VrKZQKJBOpxkeXrlOA4xsb+Xsa9NoVR2Hw0Gj0UCSVQQBJEFYMmcpLpYJdfmarqIsl2xeRtf160ruTx5fxExV8Y1EVhTCZJ9KwOtg6kSC+P2DtK3itLzB2mwkej+BFBdKjJ9OIAgwsL0V/w3KqN4rbpeL2267lZdeeokHH3zwpgK6SlmjUdGwBUgtZGn84HXapqcpP/nkqq8vFov40mlKd94JwPU8hfL5PB6PZ8Vik5kvYruVZUkegCvgIpupopWacwcNw+DChQv09PSsMITYvqud11tHKSQr+Fq9ZCsa6XKDfFlHrpYvmcvYIAi4FImIVyXicaDKIoVEGSUocfd9765KNTNdwCmKy5K8y0g+BXHOYn46DxuJ3gbvEsuwSJ1NkbcsFqoarQEnirQ8wJFEgVa/k4V8jbhXJjxVoJKsLFnzT09leeulKWbH0qhOhS23dHLgnj7c7psrCIkSeDxuJEkgn8/fsHpA10wsw0K+JEUyDQPd1JGnZijdfhftt922+oGLi6BpZHQ/489fJJmoUg3KpDQdQYA2SaB1PEu9UGfo4SFyZo6enh7mFxYwbFBuwrFSUCRqlQbT49N0dXex8NoCtUwNy7JQXSqhwRDBvuAyA5gHHhshm8+yeL5CbrGE6pDZencfD31i87LgTtO0FVV2aM4SvNr8aj1KpQZj7yzgirgItnmp5OvMnk6wMF+gZ5URNKZuUpwtUpgtYGomDr+DUH/ouruGG2zwo0QURURJpKU1wvj4ONu3b0dVVfL5Gt9/4izTpxMYukm4w8/dH9nEtlW+WyuFOo1SnUIKSukcm//mb6j9wR+smuQBGKOj0NUFQDabpb29fc3rsyyLdDq9Ipm6zPxskakTcUBg6kScudnCCofihYUFRFFc8xwAW7a38Hp/iNlzKaROH8liBUVpXr8qi0S8DlTdRK8a7Li9B4dDJh6PLysmXebaAp9hWuimjUMWl3wS5mcLOBQRYY3k2R1xUUxUSGaqG4neTbKR6P2EUU6UefLPjzIxlQVg4K15Pvar+/C+h3kqN4LLpYALfP5WBgYGOHLkCLddFTwVi3UURVpTqrVpSwubDrYzP5nhroc20/OL/4TRj32M/jUWvGKxSEsqxeJVC1U5UWbueIJKsU5bX5C23W3NoaaXFsbVtOzugBMaJpdmsS/RqGoEPAqSQ6JYLDI3N8fmzZtXyEcBfH4HI/s6OPyt8ySxKWoGiiQS8rowdA3Xpfk+lmVTM0xmM1UKVZ1On4NypszBn9l6XQmbZdlkKhqCABGPurRoiuJ1klwbhEuXbBjGku7fqBvkp/NU83VUl0ywJ7jCBGKDDaD5XJUWSyRFG1EUVyR5SwjgdyvENYOOfI3CbAFvq5fpiSx/99/eJh8v4w66qBYavPK10yzM5PnMr+xbUWRZj+FNV0YsXHbFvFZytBptHT7CXX4SFzO42x2ICBjxKq3xC1j/+udYeGeBwnQBva43B4xH3YSHwvjHx9BaO5k7lmI+W+KiKKMXavicCpZtM5qtkPY62JaqYD5/gb5H+5BlGdkhIQLmTXha16t1Grk6jbMNRo+NAqB6m2tHPVcnO57FEXTQsrWFjv0dSIqEqsrc/UAvgcdjZLNVPB511dlZc3Nzq/b/3kjvzGVEUUAQBUyj+aYs08IwDTStsfSaxcVF2tvbqWVrjD43wYVTcRLFOjrgk0Q6W31sv7OHrtu6NnoAN7gh4osl5mcLbN3RetNS73fDwXv66eoL0tLqxemUOH78OPv27ePJr51i9NAs/nYfDp9KciLH9750gvA/cK8YGH7w4X4suc7w5k66n/4LLL8f7fHH1/6lExMYl2SYpmmuGmdcZnp6elkv3bVEYx6CrT7SUzmi/SFisSvrgWVZTExM0Nrael0HYxMB7742MhNZtDNJ8EmoLQogUq5o5GYKyLrF5rt6OHBX71Ix6fKc0tUoNwyOnFrk9IkEWl0nHHOz75YudvSFEQRh3VjGvtQJs1Eiunk2Er2fMGbHMkxN5XD1BbFsm6nJHAvjOUauk+hpmoGmme9Lv8XQ0BCZTGZpPMCFs0me/JvjuP0qP/fbB1Y47NVqNRYXF3nk45vx+/1M/Jf/gn9hAeXZZ0kkEqvOvarE47hLJeRNm9A0Daticfjps5w4E0cTBWKHZe4r6wze28vk5CT9a2jft9zSwZl3Fkgky8RiHiRRIF9uQK7GyANDZKtZdF1n69at677nbbd28fzLExTGcwQHQ8hyc6HWtSt9eqIo4FFlXLJEodygMpGjd1OAO+5d2Qd4NfF8le8/PcrC+RSCINC5Jcajj20i5nfSPxAmfngOq2EiOpZ/ORj5BoZDQJQrlMtl/u7v/o7bb7+dzlAnb3/nPOdPJynUdByySF9vkDs+uonY5tgaV7HBTytG3cDQTIq6hVtZPzh3KzKJeh1NAr3SNAN546VJ8oslOra1LFVv66UGk+8scP7WLnbsuTHL7Wvp7u5mfHyc/v5+RFHknSNzvPb0BW790CC339m37LWqKrP/gQ6eyeapxTUQBPzzo2xp8ZLPtjL+xDFauloQFRHbsiktlEieSuK7cBo5uJNyosyErqGj0H5VQcTnVJjPVUkEXegTKTZpTUm3M+Ak7FGZrmoE3dcJTm3IFQqo8SqOetP0JtQfQlKXP8+2ZVPP1Zk5NEOj1KD/vn4kVSIUCqFpVfr6w6uePpFILJtBeDWapi1zwzMMi2ymgtfvWDGA2uNR2X6wh7eeHmX+TA1RFNhyZw++gMjMzAyGYfDd736Xu26/C2FC4fDhGRIuGTXU3AFe0A0S6TLFJ0e5S5Xo3H99V8ENfroxTYsn/uIdUhM5sp/cyoOPbbr+QTTjmXc7W0+SxWU7YJs3b+bll95eIf12eh0snEly9kR8KdGzLIuZmRm8PpUv/tb9GIUC+uP/N8U//VMcLhfFYnFVx155ehrz4YeX/dvsdJ7zZxJ094bYvK35/GazWfx+/7pGLaoDFuuv8eDnP8mOPX34LikAqtXqkuHbeokkQF03efLkAmN1nZFHB8meTZM+l8CaKzfnD0sCrpgb50CYUn+QN2dydIrFdY1hqprB1797jsmXpnBqJoosMq+ZzJ9IkPuZbfT0hzj21jy2bq6qUKqkKwghCZEKEGFqaoparcaWLVvWfS8bbCR6P3EoioQoQEM3se3mH3i9ZtzLPPl3Zyjn6nzqC3vweN77UMtbb72VZ599lkAgQGKxRH6uSNXv4M2zCfytXizLxqlISLUcUZ9zqdqsNRoE/vRPmfu5n2OotxefYaxqvqBduIDmcODq6SGTySBXZM6dTVCJOPG7VBZSFc6/PYe7XyUUCq25sHVva+GuR4d56alzpDJVbFHAKYns2NNOdKg58HM1m+N6oY5e1VFcCs6gk4lyHWV3K37TpjFTxI55UHwr76NtWxhlHSVdpRyQ6fnQpnVt6DXD4ttPnGXh0AwBvxNsGH92nG/VDX75F/awa3c7pw7NkJ7IEukNIrkVbNvGKGqk54u03drBh+7exekTRxkdHWV6aprd/oOcv1ChFnHiiTgp6SYnpnPY377AQzEP7sj6/QEb/HRxucgiCALWdceuNof+Cnbz9YZuMjeawR1xLyV5AE6fg4xRYGG28K4TPWias1x2tpubyJKZyDE7nl2W6NXrdRYXFxnZ3MqWP+hlYiyNfn4S/vLb1H73D3AFXMQCMSRJwuO9Im00GgbFpxbJ2YMY80UKToNIYPmzIQjgdymMZ/P0BEPUcjX8XX4CPQF6+0PMnU/RCJg41ti1NA2TSqWCXgH3fJW2vR1EN61uAS+IAq6IC8WjkDiZQHWr9NzVg9frZXZ2lnB4ZaKnaRqapq0qp7r888sV+Hy+xhN/dYzERA53wMkjn9/Bps3LE8SHPrKZWJuXdKKCP+Tiltu6lgLqV199lVqtxnf/5rv4K4PkW6O0hFxLO8BBFAqqzlS2RvuReWJbY8v6nzfY4FoEAZweFdEh3vBu3oWzSSbG0mze3kb/4OrFj5vB6/XidPgp5SfwRa88D5d3uOu1ZkErlUpRKpXo6elZSsSS/8f/gbOlhdZf+zUQBCYmJvB6vSvk0s75eRpX9f0bhsV3vnScxbMpgp0+Iv/4TsIRF/l8fl1n7lKpxFe+8hU0vUjPgIvIJafQZDJJo9FgeHj9ovJlXhlLcWq+QH/Ug0ORaO8KEt/ip5E3kEQZ1SkR6gqgOGRyVY1nTszw+J6udc95ajzD1KvTxJwKal8QQRDwGxaF8RxvPjfOQz+7nTPtPgqTeQKDIYSrlCNark6xbrDz9mE6I34uXLjAD37wAwzDwOl0Ui27ePPFCURB4PYHBtm0dfXC1k8rG4neTxjdW2Js393OsbemcTqdbN3TQdfmtWfHXCba6mFsdBRF2fe+XIcgCNx7770888wzDG67lciBTlKJCoe+dwGhZNBoaBgqRLe2s3lnB3tcVbpDbmb/43+kPZ9H+cM/BJoWw36/n2w2uyyQsS9epBSL4Q8EqFarOBQHlg2CLaDl6lQXyowlKyTMCm297QRjWVrb/fQOhlGcVz72oiTStz9E7ntv8/CHfxZZVIi2u2jIRTbt2LRi9otW0Zg/Mk92LIte1ZFdMo4uH6dMg+7+MI42P5Ovz1KeKaClypgKNOrN+2FqJlZFR/SouIYCdO9tI2FZZMqNNefdzSRLxE8mica8OGLNIFNOSiyeTDCTrjDQ6uOxz+/ie18/RXwij2RaYIPllInd0sEnfnY7bofMyZMnqVQqtHnaOH10mkZ7O50hd1Nu51JICwKT01ky49mNRG+DZSgeBdWtEDYNpqs6/nUCrnLDwKWIOCVwBByIooCsSmi1a6z+bcCykW9CtrkakiTR1tbG3Nwcdz04SKTVy6btzaTGtm1mZ2dRFGXZjv6WLS2c+43fI7fzILEDw4iyiBMnyUQSl8u15KApO2S85XmKsX4yk3noUBGCK69B0xqoimOZpkh2yozc0snF0TRz2SqtUc8KyWuj3kA3dESHG/P0Ai0uhfadqydkVyM7ZTytHlLnU7TsaEH2qsxMF6mWkrR2+AgGl8+yWi8wbDQaS+vqkUMzTB1dJNjlJzdf4MVvnmPT/748YJIkkVtuW93p78yZM6iqypa+LVwYt3G51BXvOeBSmHNqzM8VKS2UiAyv75S8wU83oigysk+kLhW5456+GzomGHYxMTlK37ALeO+JHsCOXf08EzxDejZHxyX5eKOiIYoCoaiDixcvEovFlj1rWjJJ5M//nOJ//a9L45d6e3uZnp5eth7ZhoEnlULYupVKpYLH48GyLAzdxLZtKqUKU1MzpNLN3cX1yGQy6LqO2+0ml8sBMDk5SSAQWHNX/1ryVY0z80Va/M5lBarWzihZd26Fu7lPFTFNkwuZBnsH7GUFvas5cyqBUjVQRwJXioeyiK/NS3ymgN4wuPPxLbzy9TPEz2dQBKikq9TiZQxVJHpHF3fd3kMw6MbpdPLMM88QDoeZm8vRwm00ihq2BdnFErF/dPCmx2H8JLOR6P2E4fA7uO/z23nz7Pd5/DOfYWTfwA1VTe++f4i3j3+HJ574Bp/85Cdv2jVzNVRVpXfbPv7iG2/iikv4KwZOVaSBTsjjQLREakcTHD2TZnR7C/fd28XAf/yPLPzSLzF0lQNeMBDkwtELCBWBQHsAURYRJycpRqO0+/1Uq1WcERceYP6ladJVHVkzOCNWUIpBpDdnCQdDOBSZSKefbbd1sv+uPpwelbm5Ob7zne8ghWDw1jAOh4NCocDOoZ0r5KKWaTH14hSpsym8bV7cMTdaRePsoVkKmAw+NozkdbDz45vJL5ZIXsyQOZPEzNWQPU5kn4PQnjYifUEEp0kwFOJCvMRYsrwi0bus069pFrZpIqlXfi6pMnq5zuTUDELVjVuABz7azviEh0S8iiQKdPR4Gerxo5VSTJZS+P1+YrEYqYUUih1AdavLAlOXKtGwoVrS3vPffYOfLDwtHgI9AaIna8wKAnXdxLlKgmZZNsW6zmavk0DYQ7A3iCiJbNrXzuFvnkOrulEvyRgz80VcQSfDW967VNjtdlOv17HtOnfe1wy0MpkMpVKJrq6uFTKnwg/epPj2BNH/8sfL+sRisRjJVHLZ7peYSBC65zbSaRtXtk4hpuMMXHnvtmVRqDTY2hFGkaRlRiltu1o5sDjAoefHiafKOH1OAk4FWRIol8qYiNRMCRYKxGomI3d044pcSdISxQajiSKaYdERcDHS6kOSLlmOB51kLmRYPJ/i0MlFzr8xhSo7CLR5l3biksnkdYM727aXdhe0mg62jSfopF5qkM+WmJ6eXrH7sJqTKoDL5SKbzXL81GkIbMK3hpJEkkT0hoWpmete2wYbFAoFjhx5HUVRbtjAp7XNR9+wwlNP/x0u98+8q1Es1+LxqDz4s3v5zp+/zuzJeLNAZdtEhr20d6n0D6wsfqR/7/dw9PUR+/znl/5NkppS63Q6TTTaLMBXx8ZwmyaebduYT6Xo7OxEFEUe+ewOzh1fxBsS+Z9/+/8SCATYunUrn/nMZ9a8zr6+Pu688056enqo1+uMjo7S29t7Q8PKLzOWLJOraoy0XdPDJwiY+TqmQ0PyXokPc7kcQ50xZrJV5vM1usOrF4p1zUAUWOGoKSgitm4xMznLrkE/tz7Wyol3kkw+PYOdbWAHVVSfQnE6yzf+/DUe/FgfhmGwZcuWZpxqBWmkdKIDEWzTIjdfJJOpbSR6V7GR6P0E8sobr+Bol0jo82z3rF8BuhqHo1mZevXVV3nggQfe83UsFmq8+Hoc7zSEVAEtJmMpEkFPcOk1imXjydbIv7PIE2++xaedYYZ+//eXfl5JVph+dZrifJGJ9AR92/voPtiNOj9Ppa0Np9NJaqHCq28tsJCp4vIo+KMuHGEFSSyTL+bxhkN0DbVSr+skExUSXznFhWOLPPD4ViIdfkKhEKZpcvLkSfbt27emvKE4VyR7MUuwP8h8RWNmMkNn0EU94kQ8l8JMVJG6m/r7YLsPn1elvWqSvhgn1BPAd1cvUtBJOpUiGm4GuG6HzFSmwoGB5VWyy70zoaAHb4eP/ESesEMCC/KLJYIjYW7dNYLPeWV3Zc+lNsJ33pzl1JE5Kp4IWy/ND0qn00QiEXb07+CFvzrFuUoDy+NYMqAp1Q2Ckoh/Yzdvg2sQBIHo5iiZsSz9ssDFch2vQ8HnVC4XqqlpJtlKgxa3SqsF4aHwkrnPwfsGWZzOM3MyiW1Z2JaN0+/gwEc20dN3Y7b+1yMcDjM/P08mk6FYLBKJROjt6V0RVNiWzeIf/F9wYD9Sx/LdM0EU8Pl8FAtF/IHmcyymUniG23FgEE3XSVV1EoKA/5IqYD6dJxrwMfHKYRp9QXKnctwbuxe3243skNny0CCKKnLyjTlGx5Okgh7KuobL4UQ1DSIItPod+La00HrVbl6uovPmRIaabqJIIslSHhvYeqkXSBAEVK/KoSfOcjFVxt/qRXUq5OeK/OCrp2n9+7fSaDRuuIoPsGlnG+ePzLFwNonslNn/8KZ1TR+Ovz1PLlvlwF19uFwKvb29bNu2Dd9uP9//wUUqmrlifqdtg2WYOFUF2bkRfmywPhMTE81ddlFkampqzV77a7Esi2q1yptvvklPT88NO8uux+139hEOu3n2e2+hKF4aLnDEwvzgeJHg1EV2bI4xGPPiVCTqs7PEvvQlil/5ytJu3mWCwSAzMzNLRki106chHMbjdC5zwd20uYVNm1vQdZ2FxN6m/HxkZN0RNZfHGNi2TbVaXXN0wtWMj6V56elR3F6VRz+1jYvJMm6HjHjNcfUzabS3FyiHK7hvaUfp8DXdz30+nA6Zeq7KQmHtRK+nL8zCa7NYZQ3xqkSxnq6ixNzs27uJ/hYvw8MglkbJHy0QuzuC0yEjigKVXI38ZB1JCDO4KcT27ds5cOAALS09/PV/eJ3E+RQIEBuM0HZtkvpTzsZK+2NKKV5m9FScQrqKO+BgZHsroe4ApmliGMZS1ehGsW2bzZs3E4lE2L1793u+Ptu2eeX4Atkj8wREi4bPXlWbLogCctSNzzJoPDXO04/+Kv/IHwTA1EwmX5ykOF8k0B1ADavMn5+nUqwQi2fI33WAi6cXefEbFynnTSLbY7jcKpZhUqlW6HPGuHhxjI6ODvL5HIFAkLa+II2GwexElif+29s8+nM72blzJ5Zl0dPTQyAQIF1usJCv0TAsbPuylbCKmK5iGRa6KHByLk+hppMpa7gdEqIgYBbq0H2l0dpIVdGTVeQOD1augZGsUBW1ZW5XsihQXaWy3Wg0UFWVoFvl3k9s5fmvn2ZxpgCApy/A/Z/YsizJu4xlWbz25HkWz6TJJcrs3d+FJIvs37+/+XexbEZ2trPw/DgLyRIur4rWMFDyDYY3xQgPvD+B9wY/GViGRX6xhGbZxLZEMU8lkb1OZhoGi/kaCM3g3SmL9Pic9JrQPhKl89Yrfa0+v4PP/fp+zp6ME58rYto6x0+/zB33PvS+Xadt21iWxejoKK3+AV5+6SLxmTxOt8LI3g523daN06tSfeY1Kqdm8P/n31r1PG63m0y6KX9SdB2xXELs7cJnZNCzOn7NJhdTyNcNGo06/WEvA6pKacsAo+YoSkpZNgNLcSmMPDjImfhJHJk5ekJbcTljiKKIP+hkaF87ekVn4a2FZWtjttKg3DDoDLpAgGxFYzZbXUr0ACSHRG6+hORXCMb8lEtlwj0BiokKZ89OcsfBHTd1D4dHonz6t25jZjKHP+hk+661R7KUig2e+/ppKpkqXp+D/bf3cO+99zavfTxL++FZztZ1Gk55mfwrW2ngqVt0bw3i71xpSrHBBlezZ88eIpHImoPBr6aum5iXev97enoYHBxcMmp6v+gd8LP9rk5ePJKEKQHl+CSSCUlJ4ELLNIN39PCxu/oo//N/jrJtG7FPfGLFOXK5GuWiynhlii1bRtAvXIDOTtbaf5qenuZnfuZnGBsbY8uWLYyNjdHZ2bnq3N3JyUlkWUbX9XUl21fz6vfHmHpnAUEQaO30U/PKKNI1aqa6gTaeQ1JljEINbaqA0OLCMIwlcxlBaHoKrMXu7a2cHgmTPJsmGHShuGSq2RoFbEb2d9ITvfJ+FqazONwq7qvaBDwhF4WFEsl4mcHhKF/4wheWfvb4L+/l6BuzCMC+g71LBjQbNNlI9H4MOf/6DC985xyZVBVLFsCwOfLcBAcfGWbvhwb56Ec/ysTExA0/6NCsED/wwANks9k1naFuhkSxwcWTi0j5MvKmGJ7rSAfq42fw1eoUW7cxnakw3OqjHC9TWigR7AtiAJLbiafLQ2IsQWuyRqF1kGf/4i3KRZP2zVGkS/0gxXKJYCAIAmzZshUEsFwu8vkcbrcHh9NBx6YY8YkM3/zzI4zc7uDjn36I2Xydl0/MM5YsU6rplyphNjYCDlmkLdPAn6/RZwVwKCJ2DZyKhCqKYNnLmocBRKeMoEqQqoDPi62KGIaB76p7a9usalevadqSbfwtW1ro+t8OMDmdAwH6e0O0hVavmomiSMdwlGpJo30gvMKIRxAFtj04iGVZnD8eJ5ep41Il+ne2c8uHR5ZJzzb46SY5kePl755j+mIGQ7MIhV30xrwMOl3ErBpnFuZxBvzEQmFcpkXQqRIeDNN7d+/SWIDLOJ0Ke2/thlubM5yOnSnw1a9+lccff3xdO+4b4fKa1dXVRXbG4sv/+RCaAYrPgZksM3kuxfiZBI//8j6qf/DH6HfchdKxUjJqWTaJYh2Px08mnaGtVsN2uRF8XjyddaSqhGVYdAoSFdGkYtm0OV14Wjzs+/hmqm9U+dCHPsTExMQyQ4Zvf+fbvDP6Dr3be/mZX3qoKVkUmkmgKIvET8QRrjENFwUBQQDTtpEEAd20UV3XrBN2cxC7UdOxraZ8slbSMDFoa393ktievtAN7bK6PQo9W2NkExW6eoPLfhboCbD9li6KL0+ykK1hOyVkUaShG3hqJiMhN0N3dG/s6G2wRCZTYXYqjyQJ9A9Fbtj927ZtxlMVTk1mmLyYwTZtfCEXu7d3MdDuJ5lYWHdX+kaxbZuZmRl0S2B0QUEcbRAWJZQOP4IqYdd0Gskqoz8Y4zvFLJ/+xt9RefK7K87TaBh89f85QnIyz6aD7QQCC3DxInp396q/d3Fxkba2NtxuN7t27QJgZGSE+fl5crkcXV1XDFBM0+TChQvcf//9qyaBa+HyORBEAcUh4/KoSBJY13huCYqI6FNRclXqjTpun0Iul1uSnwIICEjrzA1tD7r4xOd28dyzYyTPprGLDdR2L9tu6eTRe/uXHesLuLA0Y9nxet1AkETcq7Qi3ei69dPKxkr7Y0Z6Ks9zT5wlpenERiKosohuWqQTZV7+7nli7V66trXcUBWrqhkkig0kUaDN70SVRcLhMNPT0+8p0bNtm9dOXKR4IUVvexj5OkmeXqvjPnOG8vYdSMkaJ8fSDLf6sIymzEuUREzd4tixY1w8dwZtoc7ebIGTxTYyqTJ9u7uWkrxKuYzH7bnSf3bpv6IoEgqHqZTL1At1Av4AvhaVmTMJojP9fOedec5nKliWTdTnoM3vXCZ5qDQM5vN1pjMVFo4v4HZU6RB19g1sZmImz4LU3Jm8Grndi3t/B/a8E8snU5TqhKwAerzcnG8ni1SrDSKrDLTXNI1AILD0/21hN21rSCKu5ZOf30X6oSGi0dUXe4ffwb5PbWPk9h4apQayQ8bX6UN2bCwHGzQpp6t856+PMTNbwNPpQ1Ek4pkqufMp7n5oiO5tAQ6ffIWwt4/Brn783X7CQ2F87b4VcslryefzeL1eVFWlUqm860Sv0WgwPz9PJBKhr6+PSr7GK09ewFBUOoaCS4qpalVn7Ficd8zvs/X0GcR/+o9XlTOZtk1dNzn8+iEUs0bL2EV2h0I4BAFBEPB1+Qj0BIgMR5g4O8HWziEaDYsSJnOJMnfsfJCWcAzJIS2tocFgkC1btqBpGorSlCtem+AobgVbsLFMa8kIpiPopM3vZLFQA8Clygy3Lr9PWkVj862dlCYyzJ9OohsaDqeDgQMxBgbW3o17P5AkkU9/Ye/qP1Mkhj7Uj6SIXDwRJ5GpoJs6HlWmezDKpjt7iG3dGOOyQZMLZ5M8/T9PUIyXQRBoHQ7z+Bf20Hod+Z1t27w6muLVZy5iTORwNyxEICXCU28vcPr2bvb1yVyb5tm2TbxYJ1FsYFoWLlWmL+LGvcY4hkwmQ6FQoLu7m3OJCgvHxogg4rhKvSN4VJy9CsHJHOf/5lnO3PkAex58cMW5dN2kUmxQL9QZOzdNQzjP3rffxti2DV+5vGwtrNfrGIax6vrY2dlJtVpd6sHTdZ3z58+zc+fOm0ryAO55pI8L48d58OH72X+gh8L5BJPpyrLXCJKIe387SrsXo1hA73Dg81yJkQyrqX7yr6Iyuprhdj+9n9/DbLZCQzcJe520+h0r1uOte9s5f2SO5ESOUIcPQzfJTudpGYow8j70df+0sRHZ/Zhx9p15MvkarSNR5Evb64ok0trmJz6a4vSbcwR6XHi93nWH4R69mOaVFyYoTOQQFJHWzVEefmiYvpi3aaN+lVb8Zshms+TzeXRDxYe8TIu9FtW33sIpK/gP7MWeKZGIl4CmCYQr5KIcL+Pr8HFg/x4yEwvEy2eIe1pYSIp4Oj1LSZ5hGM0dMnXtxcbj9WIYBlNTk0RjMUK9QU4dW0AWbbYf7MGtytiWjZmqYlV0bBtEp4Qr6qZ/IEylYjD5ygSLM5Ns6/NDazdBzUQZCVP3KVy9JAuCgNzhwSwXufjaKaSqRUp00tHaHAJvYCNYFo6CQdwWCfWHlnbULgeG7wZJFq/7JSmIAv6um0/mtbJGLVfD0i0QQFIlPDHPiplfG/x4c/adBRZm8kRHwjiV5teEzx1gcSbPuVMJgptzWFssUt4U2z+//abWit7eXn7rt36LZDK5pu3/eti2zfz8PKIoLlMtjJ9Pk09WiAyFl7XFuN0KRY/C+b/+AVu++MuIAR+mZq74zCqSSH/MS+iOW3jl5Vdw5HJokQiq1eyJsTQLd9iNETTo2NnL0RdnuXg2RaXcaK47skhbp5/dd/ex+65eFhYXOH36NFu3buWRRx4hGAyu+n4C3QE8EQ/VdBXvpWTOoUjc0hPgey+fY9eevYQ9KjHflYKZZVhYusX2R/rp+dAAp96ZJ5nIglzm8790x03dT8u0qOfrWLqFqIi4Qq7rJuvXQ/WojDw6ROe+dsqLZSzTQnErTQn+DXwnbPDjj3nJAXq98U7VmsYPvn6aYrJCy0gU27JYPJfiB988xy/+5q1AM9FaTbp5drHIS98bxTmaIxR0IrU7ECQRT1VHS1WYfmEC4cF+YoE4He3NwsdiocYrxxeYPJOkPltCMC3wqYQGw+za087twzHUS9dbr9eZm5sjGo0urTMXJ3MI6SpK68rkSxAFZLuOOJ4h/Yf/eNX36/U6eOTzO5mdzNEz4OY7T36ZA3NzVB99lGw2u2yHbm5ujqGhIRbTZU4fX2RxoYQkifT0Bdm+q52Q183IyAhvvfUWsizjcDjWlLhqhoVuWiiSuPT+oNnT9/TT30J2xTlwaSTN5jY/78zkqGkmrqvWSMnnQNriQEiYGIKF33llnmimrBHzORiMXb9op8oigy3rxyebNrdw/89u5/XvjZGdKSDIAl3bW3n4Z7bd8JiNDa6wkeh9wMjna3i9DuQ1FsfMfBHbIS8leZcRRRB8DhamM/zg6QlyxRybN2/mnnvuWXGOyWSZ73/tFEwUiIScmJpB/MVpvlXW+OVf3Et7ezvxeJyOjuXzrUzLJlNpICAQ8ajLbHQvV9fD4TADAwMcHh9FsIHrxH9apYLn/Dmqt+5HVWQE28YwbCzLRvWqdN3excwrM6TPp0GA22+7nTfn3+F0dTMGAm7PlYe+XC6vGUxdRtd1SsUiPb29pFIp0g0o2TYt8yUcgDZToDGew0hUsU2ruSEogBRyofYHkPt9LB5LUg6ozFYF5BYP++4fIFuoMJGt4r1U0bItG322yPwr50mMzZHNZxB9DuTuduRuH4IgkMvXCAsC3obJ+DPjOENO2na20bqr9V0n2j8MbMumnCiTG8+RvpCmUWgsOe2Jkog75qZlWwvBvuCSAccGH1zqdYPZmTyDQ+E1P2PJmTyWKi4leZdxh12kFor074vg8/nw+/3X/ayeOLrA7ESWWLuP/bd3L1WcJUnCMIx1h/9eSzabpVAo0NXVtaIQUq/pmLaNuoojqFTMUi/V8f3zf4DnpUWq6Sq+jtWDjWAwyN69e/GcPoXh8TA5OYkqqbhwIYdkcvNlXntylrnZPO5WL9EOH6IgUGvozMbLJL98ksR8ip33dHLw4EFef/31JdnVashOmeiWKFMvTeGOuhElEcu0OPbWm1BMMNLiXZF4lRPlJTfUiFOmfyBMMpnk9OnTNzyywqgb5EZzFN8oUklUmk6/soSnzUNsc4zQYOg97fILgoC31ct8rsrptxeJT+dRHQpDO1vZdUsn4RtUKGzw40cyUeKJvzyGaVh89Od30dO7uqwum65RSlUIdfqQZAGQcEfdzI+n+Na3vkM8vsD8/Dx79uxZdpxl2Rw9n0KYyOOLuJCu+t4R3QqO7gDByTxzZ9NMtoh0tLcxm6vyze+Nkj0yh69h4fM1E0MzXaMyP8PzF9LkHhvmsX1dxBeahaRrB4BX6zoyTSnjahgn3kEPhFA3rT3Ee9uONrbtaCaeiuNTtP+rf0V+YIDJyUlcLhexWIy5uTk6Ozt55fVpXv/ueWqJCoooYNkwKsCRnkke+fQ2XGqZ4eFhLMvi8OHDbNmyZWk9tW2bmWyVc4tFxpJlTMtGlkQ2t3rZ1O6nO+Tm0KFDzM7O4nA4llp2usNuekJupjIVBi4V/q+mWCwuK7DppkWuovHAlpZlieHSPbnJNf4ytx3sZfvuduZm86iKTE9v8IZmQm+wko1E7wOEaVjoukEuZxGLrSG7cyvYRnMY+rXqI7NhUDDynHr9TSKxCC0tLVy8eHHp56Io4nK5eOt4Fn26SGtfENHV/AjITpnkmSSjs3n2j8TQ9eVzry4uFHjpxQmSY1mQBDo3Rbn/gUG6wm7m5uZWVNfdHgVTAAwL1lFu1t88gup04d2xEwwLUxBwOqWlJDK2JYYn5iEzmqG4UEQQBAYcrZzEicM0EfRmwlEulfF5168S1SpVdMMgfGkOjC7I1G3wtig00lVyh+aQEhWQBKSoC9HRXLRsw8LM16m+vYiaDbL3oQMcPXWSuOSnsbeNlqEotyWdzBfrxIt1WlSZ+skEjfE8DkGh5rdpWCIOh4jD40IQBIo1HUEU2dQfJhhxY9s2tUyNqZenKM4VMTtNGFrv3fyvQa/qzLw2Q2Y0g1E3cEVdBAeCSxIzUzeppqqMPzuOM+CkfV877Xva3/OOwAY/PEbPJxERSMTLtHesvqvr8qlg2lgWXJ3D6XUDQTA4c/Y0i9lFOjo6WFxcXHasIAi43W48Hg9TkwWe/utj1HJ1FLeMJAtLc9ja29tZWFhYVsVeC03TmJ+fJxQKrem8F4l5UFWZckXDe/WukQ3102MM7t+C0hYhttVg/JlxvO0rg5jLdHV3oZfKSHv2ktA0wp4wNaXGWHyMhZMyc7MFWkYiqPKVwMbjUvD0h5iZTnHslXm27e1H6BDo6+uj0WgwOztL9zW9OJZlUSw2cPX4CfYFyY3nCA2GmJmZIZfPIUkSuVyOcOTKPLBKqoKlWXTe27kkA9V1nXq9jsvl4kZolBpMvTjF7JFZou3RZoKpiFi6RXmxTH4yT2QkQv99/e9pB+65p8/z5vfG0Cs6qs+BpZvMnUlw+o05Pv6F3WsmABv8eDM+2pQT26bF2LnUmn9nl1tGViXqFR3npR1rvarjiqjMzIwzNzcHwIkTJ5aeVdu2KWowcTqPz7ARAysDDEEUcAQc5GeKJCtRSpUa3z80Re7wLDGHgtzlWTqfDCgNE8dskaPfH0Mwijy6f9OqaqhYxM1FScCq6CsUAXoyiTqbRPzYo8vXn3Xo8flwaBo9995LUtdRVZWTJ0+i6zqpnMTLXzuNolt0bIoiXEpyLM0kMZbmq//1EL/yj+4hGAwyOTnJnXfeyczMDIFAgGAozCtjKd6YyNAwLEKCiGxZ6LLAaxczHJ3Nc+dglLvvvoeWlhYsy1oq3kqiwD2bWsgcnWM6U6U34l66V6VikbbWVkqlEqqqYlo2C2WToRYve3tW/xuvpyy7Hh6PyqbNG8PP3ysbid4HCEkWURQZt3vtremR3R2cPDJPrlAnfFUVq1TRUesG933uNpwdu7lw4QJ79uxZNkPGsixqtRqZzCyyZSM4r9qW9ygIuSrlYgMAj8ezNLwzXqjxrf95gsqFDH6fA9uymZoa5ytzOR56tIMtg70rHuS+riDHw070XAPHGnP8GsUinrFRKgcP4pQkjGyVhkdmeChy1WsapM6kSI+lqefqSKpEflZH1wI4Mwb1ahKxQ4eYiuRbu5JdKBRQFWXJNt0yTUoNGwQRp0tBv5ijcjxBcCSMdI0hiSCLyFE3ls9EG89jGg727tmNrno5MV9gb2+IoRYfH9rSyjNH55g+Pos3VcXR7kNBZsAzyMWLY6TSaQzLJFNuYFqwsytAz6VxBoIg4I66cQQc5KZylCfK9Pf342n50c2C0SoaE89OkBnN4O/xrzqPUVIkfB0+vO3epUTVqBp0H+zeSPY+oAxvijEznaO1bW2ZzaZd7Rx7bYZkqkRLzNu0t64baIkqt9zRx2O/8km+973v0dXVtWryUqvVKBQKXDg7QzVbo3N7KwtnkmSS1aXXiaKIZa10acvna0xP5Ghp89LW7mN+fh5BEFZN8Go1nUyq2U/S0RekezjC2OkE4kAIt1vBtGzSR8fwVMrs/t9+GUEQCPWH8LZ4KcwUCF5jJHI1YjKJ2NmBYiuUsiVaDrTga43y6pnD+DoDy5I8uLSmlMt0dIZJjGY5fWQOOaDT3t6OLMtomsbExMSSucJbb8xw7LUZ8gtFRFmkuz9Ip0chO5olFo1x+223o6jK0tqqlTUqiQqSItF7Ty+RTVfWydnZWfr7+ymXy2Sz2aUh6KthaiZTL02RvpDG3+Mn0B5ANywqelOqFegJYGrmkopi6OEhpHcx2P7s6ThvPjWG4pKJXWWUYOoWiQtpvve10/zqPzy4UaX/CWRgOELH1himYTG8ee2eqkjEw9Y7ejj6/THqxQa2aSG7ZO58ZAu3HXyE119/nbm5uRU74rOZMuIbxxElYc1ijaBKiDUdp9vH0bFZFs6kCNoCSvvKdU90SLg6vVQXyyQzEqK0emi8uTfEW31BqmNZvF4F4arnQnvldYptPYT3DTB0HWkiQKnSYP7QcdzhLtqHRrj45mHcbjdut5v2jg7+6385hF3WCG2OLnuPuqnj7nBSnm9w6niSvq4wpVKJ/v5+AoEAmUyGbx46xZm8RFQWcSxU0GeKWJqJ0ynh7wlQ6fTy/PkkDkUioKordi57wm4+srOD759ZZDRRIuRWCbpkGppGpVzmzPlRiho4vQE+du9+HtvRsaoLODTVXjczx2+D95+NRO8DRjC4fkW2b3sLO2/r4tjrMyzk66g+Bb1qIBYbbN7ZxtZbO1nMLPL444+vOFYURTweD8PDbbzy+iJWobEkedCzNQSPSiTWTDyi0SjT09N4PB5OnEpQGsvS3htEvJSEOgsN4hcLVPSRVas1w60+giMRKofmUHVz2YJ4mcabb2J5vfi2bsW2bGqFOq6dMbZ0N4OCaqbK5POT5KfyeFo9REYiGKaBbFTRvSqeqBPZgOKFOMFGDMvpWrq+y1imST6fJxAIIF0lH8jki9QtCZciItogFRsYPseKJG/Z/XNISGEn2QsJurZ3InkdTKUrjKfKbOsIsKcrSP71WY4lKuSDDmhoiKaO1xcg0tZFsqSRrVm0KRJb2/30rjKzTlIkwoNhMkcyTDw3waaPbUJ2yQji2l9o75WjR+aoVDRuvaMHxyWplqmbTL8yTWYsQ2godN1A73KiKjkk5t6aQ3bLdOzrWPeYDX40uFzKdauknZuiHHxoiNefvUjifBpbAtmCoU0R7vzIJkRRZOfOnSuSPLiyzng8Hm651cH40TwL51L4Ym76hpcnIIFAgHw+vyS5LpUafOX/PkLiYhZnSOXOj/ew/7aV1fVyucEbr0xx5o05ypkqCOBv8dA/HKWvYbA4nSevmwiWjef0SW5/pJ9NdzQVBw6/g957exl/Zpz8VB5/t39ph3oJw0TMZqi7I5BtIA/JhDaFmDtbwTQFfL7lEuVGo7HMQEkJOZkZTbP5QAC5o/lMqarKwMAAi4uLPPv0OU4+P49t2XgiLkzd5Mwb88Q7vdx7Rx9WqkppukQwFKROnbpdR3ErhIfDtGxvWZagplIpotFmMBiJRK6b6OWn82THsoQGQmTyWS7ES4ynytQ1E6cqMRTzMtziJdgfJDOaIbY5Rnho7fOtxckj8+g1ndg1I1skRSTSHyQ5nmX0fIot22++T3ODDzatbT5+/Z/chQ1rtqFc5tFPbCUYdjN5IYmsSGzd3cHuW5rjWXp6ejhw4MCKY3wuFdXtwDCtZovFKs7VVlXHcsr4vSrjF1MwX0INr91aILoUPEBiIku8WKdrFWfr7pCb3Xf18la2ijmVx+11IDok6jOL1JN1zE/fxYGDPQTW6SMbu5jm5FvzTByPUz4/jtj1WXz/vxcY3h3j+RcOcf99d5ApaNQWdQIx97Lv/XKpjCzLBEIBzHyesRNx8nd3LjNskd1+5hpp/JKJfDJFfa6EFHIihRzYNYPaySTOkkZjU4hDo3EeG1q9mDzU4uXTrm7OLRQ5NV/g5FQcRVY4f36Gi2PTyFqJzz54O4/v6cKzjsRb07RlI2d+mJiGxeJiiY5O3021vnyQWmV+GGwkej9myE6ZBz+3k5buAOeOzFMs1HAHXYzcN8Dee/pw+p2QWf3Yet3ANC12bm3h9PZWksfjuNPVZg+WLNB7bx8jHVecHgVBwLZtkokyimUvS6Ikn4o0XySTLK/6u7wOmV2723n5fBrnbBFXT2BJegBQz+Xwjo9Tue8+QECbLVLwyOzc2U5bwIlW1ph8YZLCbIHwSHgpECsXqwi1BkTV5uw6BZRWL0a6CoKAc1OkOdIAaNQb1GpVwqEwXLO7VNEsdAs8koxQ1hBrBrbn+k2+ok/FKmsI6QZKuCn/mMlU2dYRIH0+jWuhwv139ZHRTU5OLGI53BimTSQaozWbocXvRhYFxlNl4oU6HUEX7QEnylX3RhAFvF1eCnMFTnzpBJYs4PQ46Luzh0B3YJ2ru3lmpnL84G9PoJU0ZFnk9rv6AMhP5kmfSxPsD95UNd/hc2A2TBbfWSQ8GN7o2fsxRRAFDnxkE32bo4yeTqLVdNr7ggztaMXpd1Kv15fGf1zL7HSeSIsbt0uloyvAZ3/rVuam87S2+1ZYYAcCAaanp5cSvfGxOAsX00R7gqQn8hgN16pJ3jf+4ihTRxdQ/U58LW6woZis8PZUnsF9HXzsi3sp5Oqc/rP/zKfS3yP2J6eXad2DvUGGHh5i6uUpcuM5ZKeMK+xCVERsy0abmKdqR1FlDy37AgS2BtANHV03scXlctZKpYIkSsvmY0qKSLWm4XSsvEcuV5ALR44jqxKRnivPsy/qZfFckumqxkM/swX1mErYG8a2bCRVwtvmxX1N4GcYBrVajVisuWsS9AZ56/tvcfa7Z2kNteIP+vHEPIQGQk2jFwHS59MIsoCkSszmG4wV6jhlEa9TpqqZHJ3NIUkCgzEvoiSSvpAmNBi66UJTYirf/D5aBYdHxdRMUvHSikTP1E0KMwVyEzkahQaqTyU0ECLYG9wwffox4kZ3amVZ5K77B7jr/pXjoFYLwE3T4oUnzpB8Yw6h0MDtcyBfo3yxNZN6Wce1u4XBmI+pKQdmvYGgrm8WIisSZt2gscY8OFEUeHBPBw5V4sTb86THcwg1nfr8Rby3xPjwF+9g/0B01WMBXnr+Ioe/O0q9WMcTcuHSiuAWyc3meGu6iKvNQUvLDKFYFEs3kS4VlGzLolAs4vN6lwrWskPCqOuMT0yye8e2pd9xMVkiV9XprRjU5ksoXb4rsZdDRvQoaNMFwt0+zuTLlDetXWhp8Tlp2eSkz2uR7vUiqy5O+Wp8fexlMvFR+n0H1k3y4L1JN2+WQy9NcOL1GQ48OMT+29efvTg1mW0m3KcTmLpFpNPH9v2d7NzTgbqGA+uPKz9Z7+anBMWtcMtDQ+y+uw+9qiM7ZZSrkrDLWutrKeRrWKZFe2eAT31mB6/2BpgfzzX7Zra2cPet3TgvBfWFQoFqtcqbb76JZYEuCFh1A/FST4hZ0TFVkeAqu1KX2driY2x3G/NvLRCcLuAMOZECzQZo7Y03MQJB3C291Cbz5NwSffcN8ODeZiUvPZomP5lfSvJ0w2IyU+HkRIJaoIOy6IBqg4jPhdvtomJXEdI1jGgNpd1LqVRCEASCoZW68UatjqwoCI1Ls/I0Cywb6QbmOlmmieiQMQp1HIAqCZQ1A62sMf/WPIpHwe1zYBSL3Le1HdXhIl1ucGaxQKS1A9XhpDw+heB2kwmEmMpUCLpVtnU0m6AvI4gCkiJx4rsXyHZ4cblVGjWdPZ/bgfI+uk55fCrugBNJFvEFLn2p2PZSMPhuzBhcEReZ8xnyU3nadv9wbd43+OEhiALtI1HaR1YGLuVyeVlic5lKRWNuJkcqUWbvrc3eu/YO/6q9gJZlkc1mSSQS2LaNJEmEwg5ivSFSk3l8MTedq0grX3t+nMl3FogNhlGvWvecPgf1coOJdxbo2RRl01Ynm175G2Y/9zliq0iHAj0Btn5qK4WZAqmzKSrJCpZhNSXH83GiMRPXA+1UxSr9g/00Gg0mjl9AsAUM00YSoFQq4fa4V5gN6FUDt2rS2rEyiJoYy1DL1GkZjiz7d0kWcIdcjJ+IkzkYYfjA8HUDpJmZGfr7+7Ftm8yFDK999TXeeuktwiLEWiOUd+0id9FF/FicQF+A9j3tlONlXGEXhmEyU2igSg5Cl2TZDkUiXWowkSrTH/HgCrsoLZYwagaSQ6I0XyIxlqGUreF0K7SNRAj0BFZdJ2SlaSqzGrZlg22vKCLpNb0pKz2fbroWu2SKc0WSp5NENl3qGVyjFWCDnw7SqQoX3l5AyNSpSpCr6gTmisghJ0giVlmjXmhQbHFxyy0deESdeq1MwzKxdRNca3+nmYaF6JCWOVNei0OWeHBXB/uHo0xnqiRfPUT0q1+h68+eIja0tkz1zcMzvPrEOVRFonNbU1FRPVrADrpRun24HR6SY2me+foFPvrLPhSvSr3cQHBCrVYn6A8sK1g3yhrBLh+y2DS2ukyy2ECWRIyFMjikZQV2AEGREESwElUsv02xbq57vw3DAKPBvsHmoIpd3XejlBb5279ZJHzmDHz5y/DZz640jbiEbTddi03D4okvn6CQrvLRz+28rjP4uyHS6sXtdxKOrr+D+NYbM7z49TNUsjWcAQeiLDJ9IsHMyQRjt6X45M/vWlI3/STwk/NOfkKxLZvSYonCTAG9oiMpEt4Ob/PLdZWZTKZpLnvor8bjVbEuTcLsjnj43GObKTcMBGyqxTyZxdmlzcBAIMDIyAhzc3N0jURJXGyQGsviD7uwbZtCrk5oVwvbRlZf2E4dW+CZr52mkq9j+hVK3X6K8TLubA2rXEQdTVLZdwulYg16/Qzf0sFHbu8l4FKa/SFn0ziCDkRJxDBt3p7OMZUu0yhU8BlVMl6ZXNVCUcGhgsvtoF6rIC6WKSkNfH4fyhpBUq1RR1FdYDcHctqGiSiwZi/h1VQqVVwu99JEUUEQMC2b/HSe9FSa1m2tmKZJo9HA7/eTq2gcm8lRyJXoeOdNXHOzYFkQjcBnP4tp2WQrGkcms5iWTd9Vs+9ESaReNcgV6tTDTnLpKnpVf18TvUjEwy/+zh3Ua/pSMF5JVCjMFJas3q+lVNNJlzVEUaDjmt3Iy/fEEXCQPJMktjW2UYX/McFoGFRTVSzTQnbKeGKeNfssa7XasmG5l/F4VEIRD62rzIbUdZ10Ot0MHGh+TsLhMHv37iUejy+Zsnz2N31MjmVoaffS179cMlipNDh3ZB5n0LksybuM0+tAciucOjxL+KmnaAdO7d1LZyKx6igHxa0Q3RwlMhKhUWxQy9VYPLpI6eU0WX8vpWfOs/nBzciyjCzL9AwFOOGXSccLuH0ifp9vhVrAMG2MfJXorQFqWg3FVpb1qJhL80FX3ltRFmg0dGr1xnWTvKslm4lTCaZenKIzmaPz5Mv4s6nmi6Sfh09/Gr2qkz6fppqqohU1VK9KXTcwLBG3Y/nz6VQk6rqFadkIkgAmNMoNpp6f58QbsyzmamiigGTZxF5W2bqjjZ2PDeO+pug3sLOVw988h2WufK+lTA1HwEn/4PK/79ybcyRPJwn2B5clj6Zmkj6XRlZlBh4c+KHJ2Df44BOJeujcHEMUBaLbWym5JJLn06ipCoIFFdFCa5fp3eamTciQSuns3d7L2JE09XQFzxrtGVbdoGLZtPSHaL1Gml2taVw4naSrN0ispfm9GHSrBN0qC//mn/G8XcaqFFkrzdN0kyMvjCOYFqGrpMxiuYx1aV2SVJGWkSiLF1IcPzxG3642TnznLDFPcMlb4DJ6VUczLWxHlmLBbjoDqyrt7e2Yto0ggG3aa/fJSyKYFmBjrbIxkC/Weea7F0gvFvC3inz6s1fJZy2LT584wUOvHmbmtYscll14fYOMPLh33eSoUKgxeSJONVNj5kDuh5LoXe1ouhYz0zle/MYZ9LpB5/arWhjafVSLdS4cmuLlVg8PfWTz+359Pyo2Er0PMPV8nelXp8lP5imXG+iigGiBR5XwtXvpvqObUP/yHav1BhB7vc0FrtFokEwm0TQNaPbUxGIxWlpW9u04nU58LoGPfH4nLz53kexYFkESab+vjwcfHCS8RnJ06PtjlNNVAh1ecjMFdt3Vh/rQAOdOJSl86xjC1giOB7ewfVcXu4aj9EY8KJfkmcW5IpVkhUBfU9Y0n68xnakQ9TkpLhTw21UUp4xiCRTrOh6HjMchYblEivEs0d4+5DWCJNuyEQWRZtzRXOA0w8ShSKju6z8OlWqFiOxCvPS+c4UirlqGs3Gdt0+9DZNe2to6OHDHVgzT5uhMnkJNp61RQpiZvnKiS5p1SRSI+RxkKxonZguXvjyaAazD78B2NPDM1WiPeujcH16as/d+Egq5IHSlN7QcL2PUjWW7xJfJVTTemMiQr2oIgkBH0MXtA5EVyZ475qY4U6SSquDvvPl5fRv8r8PUTBKnEqTPpamkKpiGhepW8Hf6ad3ZuiwwuRE2b22uI5VKhVwut6QwUBSFWCy2qtX21aYssZhnTdfh5GKZcrZKaA23UABP2EUpXiL43adx/smf8PGf+Znrjl257NI3c2iG+IU0C4Uyet8wxmiatnCetp42HH4HIzsH8becIXumgsffsiLJsyybxGQWr1/m3MTrjP+7N4jFYvz6r//6UgGuozuAI+CglKnhjy1PjmqZGj17W8ikkzguyYccDgfRaHRJwlavG7zy3BjugMmdd22ntFhi9tAsikfBM/oOuVqd420jJH1hFKWVnmSZ7pCb8FCY9Lk0pYUSzrAT0QUBt0KubuC9qmBYahh0Bp3IkkBNMxFkgcnXZ3nlhQkSDhFvuxev0lRZTFUa5N+YwbJs9n9627Ii1J5buzl/ZJ7EaJpofwjF2ZxRWs7VKCVK7HhggI6uK9LVWrZG9kIWb5sX2SFTrhukSg0iXhW/S8HX6SM7nqVtTxueNT4fG/zkI8siP/ert1Ao1PB4ZcZnFzkR05hLqJiGzbbuMLdt66E/5ke69Hw2DJO+vV3MPHUeR6qKfM1zZ+sm9YUSjaiLPTvbV+zo/eBb5zj53AQtQyF+7Z/cvdR3mHniCUJjY7z4yCOc/trX6O7uXjWOOnc6QXa6SLRneeuFVKlAKMTlPTVJEXF6HcTHa3TvqhMcDFOaLSObMo6gE2ybarpGPlWmY2876dLr/Jt/8zy9vb3EYjF+7/d+j5BbQTcs5BY3+mxxaUdt6b1aNnbDxPCKaIbG8bcOw2KAtrY22traCAaDPPvUBU4/exGHSyY1anO4Z5J77hlunsCyeOdLL3Ao+jHyYgAbAeGJGcJHK9zz0U3s3LN6b342t4juW+T2e+5g684fndLn1DsLlFPV5UneJdx+JzVfgzNvzHHwvgE8PyHqgY1E7wNKo9Rg4rkJ4mMZFmSBBcOkYZhIokBEVuidK6I/M8Hgw4ME+4JLxxWLxRULTbFYJJPJLAVTDoeD1tbWG9JNt7S0MDMzw9beHoa+sI9kqY4gCLT6HMirNEBfZmnOmtA0EYl5HRzc181IdRbPi/8Xi3/xF/zl609x98/+I2Kx5ZUdraI1B61ekvUs5msIgoAqi9j5AqbbiavFSWOhgemSqWoGEgaiJCGLEhirS1cByuUSXq+Hqm4jiiK6aaFpJoFOH/L6Cobm+6rpCG4fSrsHy7bJ5gso+Vlee2OBwul3EKwQ8eg8rW1OHOEO0uU6MZ8TQYnCzp1w8mTzRK7lXzRhj8p8rspctkrQHaBUKnHx4kUW7Wm29W9j970DDD00uG6/nF7XGTudRJZEhna0Ir5LNzujYcBV8evVXxQTqQq5ikZHyN20Vs7XWCjUVxjLSIqEZVqY2g3c1A1+ZJiayeSLk8wdjZPGYsEy0W3w6wYtuSqFuQL99/XTsm198xbbtslms1SrV1w1PR4PnZ2dN7T74vP5luY4vVdEUcCamQafF+8Xv8js/Px1Ez2AaqpKfirPrARjlowQacERCpC/pKiIbYsxPT3Nh39+Pz/48mnmxwqUFAl3xI0oCdQrGkauRizqYe/9rbSP7Oab3/wmvb29FAqFJYOUzq4AQ3s7OP3iBLZl4Yt6MHSD3GwJR8DJLXf1E2uVaGtrBkP1ep3FxcWl9Tv3F99kNhWgdf9muAuOfO8IZsKkd28vi/c8yJGKi0q1gVtrYHiCLE5mWCjUuK0vQmggRGm+RG4qh2/Ex+a2AO/MFVks1HApMjXNwKlIDMWasz7r2TqBvgBnT8ZJqgJtMe/SDFenIuF1KiwIFc6cXGTkti6im6/s8ra1+/jIF/bwg6+eIj2VB9PCssHpd7D93gEe+9S2Zfe/kqqQWczQuasTw7R5YyLTHFfjc3DfphYcPgflhTKVZGUj0fsppVwuk0gkqFSaTruKotDa0sJnB3vXXWccssQjdw3wV/MZUhdK+It1HAFnc45eVada1ShHXex4aJBdPcGVJ7DBsm3sq5XItg3/8l/y2v79WKFmD+vCwsKqiV58oYhlmChXy0YNE6Vex/D7UZUrsZgroJKazXD3R/eyZauHwy/MkjiXxow3/RDUkJPh+we47+F+ipkQPpfKkSNHaGtrY3FxkcFYBx5HhkZERA45MRbKyC1uBEXC1k2MeAU55iahGgy1tfOFO+5A0KvE43FOnDhBPp/n5OtFmEng29pGvCDztS9/i5aWj7BlyxZOHF/k2Vt/BePkGaJGGkUR0QYjZGcKfP9vjuNwycvMvgzD4Fvf+hanT59mZMsID3/0R7tTNnU2idPvWHO309/iITtTYG42/xMz2mEj0fuAkjqTIjmWYUy0mS/UCLpU/C4HummRKGsUFYkdORvHm3PM5GawBIvdu3dTr9cpFAoUi8Wlc/n9fnp7e9+zq5Aqi6s6Ua3G7Q8P89xXT5GdL9C1o5Ude5tVHvkPfx99xyZCe7bhfOMVvv3tb/NLv/RLy6r8trU8UWsYFvKlh1IsFdFcTnwdfqxsHr1mUBUsAg43TrcDvWJRLpUJtq4eCJiWhShJeETwOWRy2SpOWSTpKmBdrCOV3YRjEVyele9TqzeQSzbKgBcp7CJb0dg62MuHY5t58fz3iAc9dCTjdExM4f2jl5nYeRe09yN3RpsJ3vw8emsLSiIJq/QMeRwK09kKw61eGlqDTCaDu8VN60gr4aH1d/Ns2+aZr53h+KtTCKLAnR8e4e6PrT20dT1s015K9Gzb5pvf/CZutxsBASPUiyR5EITmbqRl20ty4NVP9q4uYYP/RaTOpZg7FmcUg/mKhlORUSSBeF0nIUCjWEd+fRbJL3F+5jwHDx4EmhLxxcXFZTLMUChEJBJZ79etSSjUnB13vUQv1ubFE3JRylSJuFc3JionSvgvnqTlD//ZcteU62BqJqZmUrVtlEoZT18XJRs0y6JWrjExMUFvby+yLPPozwmMn8oweTpLfLaIZVmEvA5GHhpmy95WZJ9FW1sb999/P7t27aJYLDI1NUUsFsPj8fDhn92GLIuMvT1P4kIaQRIJdfk4+OgIoYi9LFh0Op10dnYu/X/Ho7dj/s3TdP7VVyj+hxz1Yg+5aBiFeznz9RdotHTQkUkgaDq0hjFUB7PZKm1+F8OtXpxBZ3NUTU5CUWwa0yfp23Er+ZpOm99Df9RLi99Bo9hAlEVkh8xcvIyz1b2U5F1GECDodZAplJg5lViW6AEMj0Tp+ad3ce50gkyqgiyL9A9HVp+rZsPo2CjvTL2DJUpYbdswbZmGYaFbNgo016WNNeXHmneOzHHxTAJBgOFtrezZ35Rsp1IpqtUqvb3NXjBN0ygWi5w9e3ZpnfF4PLS2tq6pWlqPvqiHT398Gy+/PUd6pkphtgh1A7wKvp0t3LWnnbu2tuKQVxZTH/r4Fkr1OE5PeWk3L/WlL+GbmWHvU08RKxSwrGYMthqWaXNtWqFns8iCgOFw4rsUD9RqNTS9gcvpIRKJgp3nl399P5OzBVLxMqIk0N0TpKvNx7lz5xgcHKSlpQWHw8GnPvUpjh8/TvX0adqDw4zVLXr2tSGcTmEkqs12E0lAbvUgbo+RyWW5vTNwqT9XJRgMsnlzMwlTxVMcmn6NhXdmcNZzPFZ4kanDXyL96GOcr21DO3WOluEI0tkkKE5Up0LbSJSFM0nefGlqWYIkyzI9PT2cP39+yZX4Rnjp+YvMjWd54COb15z3eiNks1my2SzRaJRAIIBpXpKkr4EoNeMa+yeoRr2R6H0A0Ws6qfMp8hIsFOu0B1xLEgSHKNEWkFjM11hwSCRefIeZIzO0jLTg9/uXKkqDg4Pv2/W0tLSQWKPHZS127+ukqzfIN//uSTp7Kvj9TuZeeIGul18m8eyznDxzBpfLRaPRYGZmZtmwdUmRlhnKRDwq8/mmO6hUqWC4PbhCDsxeH8WTCVyBIKqqNHeeEFCdDhr1Bg7n8sRI1/SlypkggN8hk01VidzSSVysY3/3bYJvasgxD7bfhXDbfrjkZmfVDUpjSbxdIVw7WxEEgUylwYGBCCFZoa2tDbXtQ4RCIfx+P/5IhMzJeVyJJIyPw8REs4LXaGCNDCMevGPFPfOoEvmaTrluEAlHCO8Pk8vncKkuGqXGuvfbMixmL2Yw3DJGw2TmQvqG/1bXIjtlsKBSrpDOpKmUK2SzWVxOF3u27uFsSiNeqGOaFiGPSnSV4bCWaSEIAqLyk2tZ/OOOqZukzqbIWibzVZ0Wv3NJPu13KRRqOlOGiTIR59CfHaIWrNHd3Y0gCKTTaXbt2oWivH/9ooIgXNfm2ut1sGlfB69++RSNQr1ZlRVFHB6VQJsHXTNoXJxmi6eA42c/eVO/3xV24Qw56YgbFEt5SooDIZPi/NwUlY4s92+9f6kg1dbVRt2os/++YbSijmFauLwqDp+DmZkZWlubYycuB36BQIBAIEAymUQ+cACXrvN4dzep1n4W/8G/RHU76R8K43DIzMzMrHsPpP372bN/PwD56SyTX/x3tNTj5L/6BDmllcjMRZAluP8+cDmRae6+TWeaRSRflw8xIXLitROUKROJ+TkwuDxBrxfqVBIVum7rQjdMNNtGXUPBoUoiFVmkUqyv+nOHQ2b3vs5VfwbN4DadTlMpVVCdKpIusWfvThyhNt44OcrOjkHcqoTRMBAl8YciX9/gfw2HXp7kxa+dwqw3E7extxeo1w3CLQ2efvppgsEgt9xyS3MoerFILBZjYGDgfVtntnTHcFlVxHs2ES/UMSwLjyrTF/WsOxLB41Hp6JE4fPgIp071smP7duQ//EOyv/qrdPT3o8fjnD59es3j3R4F277UN3cpyTCyGXC5mvJvoTmMXFVUFNGB5TTw+RyEQ73MzMww0t/PyDX9yvqlAeuRSIS/9/f+HgAf+tCHSCQSHHni24z89dNc6N+BT5EIt3Yh3nIrpiySdYpUDYstLS7uHl69q/Chx7YRaQuST1eZWTjL22MS/ek0nJkmOWPgN/JI55OY4TDSY48uHedt9bI4miabqRCOXCm2+/1+fud3fod0+sbiEtOwOPrCBNmpAp19ofeU6MXjcf7Tf/pPAFSrVfZv+zxace14qpyt4fI5iPwIZxi/32wkeh9AatkajXyDlGCjSNJSknc1AbfKbK6MO50jHAljWRYDA80m9fdD/nQ1TqeTZDJ508dFox6CYZFjx96mrS1G5Pd/n7l776Xv3nuJNhrcfvvtVKvVFbO4PC0eFI9Co9jA4XfQFXYzka4Qz1fw1epokSgNTafiE/B3h7CzNRpuFcWyERwSzoiHilZDVRWEqwKmSrWyJOHSGgb6bIFwXwhGQtze2c3b6RR9T72NnqijxxWEwQYYZexLcwC1sEjnvUNIAQfz+RoBp8qOziDEK0SjUTaNbFr6XaZpYzuciD09MNQHp06B1wsuF5rPh3MVw5zmOAvQdJ1sLsu2rdvo7OokN5Fbsct5LZIi0bclyuIPzuN1uxi43mwqXYf//t9h2zbYswd8PhqNBvPz84xfHCcxmSAshmlpbWHfLfuYnprmgQceQFEVfN4aC/kaiiTSH/XgW+ULsp6v4wg4cIXWnwu5wY+OWrZGNV0lZTd365VrAvmAU2E+X+Ps3Cwu1UAMi7hcriUr//czyQNoa2sjHo/T0bH2/MUzp+LMjGXQMzVqZ1PYqozokECEuCiieBW2TB3m4L/7pTVd4NZCcSt03dqF8eRpXOdHyd//ILYqcqoyTi1RZcvilmVra09PD1NTU8uKVJdZS0bW0tKC/ZGPwJ/8CVy4QLS7m9hf/z83tfN4NTYCW++7n47tHSy+eAjz1dPIHg+my4V0lVmOJIpolxwwRUkkNBBia/dWjn3/GFJeohwvIykSpm5SL9RR3SpdB7rouq2L+Ik4DlGgYlisZp/QMCxkw8K72rN+/jwcPgy33AJbtoAsY5omyWQSXdcBcLvdTSOeTsjfnSdxPkFfbx+CKCBmJskvQE90J8XZIoGeAL7O99/EYYMfPpZl8fYLEwgItF/q402OZ3nuiWOkrZdwOBwIgsC2bdsQRZG5uTk6Ojre9/lmiizT6lfpCd/cbLeFhQUURWF8fJzYa68RSKfx/et/DUA4HCabza557MjWFl4POSlnqvguJRB2Po/h9WJZFvl8Hr/XhyhLZCeTDB7oJnbJmC0cDpNOp5eZXyWTyVUlogCtra189Ne/SOFP/i1D8xc52b6JxdZWLJeEIom0+h30eGx293QuuaxfiySJ3Lq/GZeVy50oymM4HA4mJ7K8/bn/iKIr4HJh+bxIgeDScbIqoldsNO2KxjWbzdLb24vT6Vwy3LoeNhaaa5FdD+9k264b6Of7zneaUtp9+6Czk0q1ytzcHHNzc8zOzlIul/F6vXz605+mt3sX35p6i1Kmiu+alhNTNyklK+y4f2DNPvEfRzYSvQ8gtmljWRaaZSOtscUsiaA4HOzeu5ctDwwQ2dmsyK41WuG9oqoqjUZjmXvcjVAoFGhtbSX/yivsePNNUq++CjT7BBVFYXFxcUWi5466CQ2ESJ9L4/A7CLoV9vWGeP3CAlnZTUp2EdShoz3KyCaVb/73p8ie8OBzuPENh3A7JHxOf7Pn57Jb1SU1om1DPl2hFi/Tv62FOx7fygvzeWYLDUYeegC1JYLjq9+ioYTQT76McPc9qDuHkNs8pLJzyEEn/x97/x3nxn3f+ePPGWDQe9ldANsrl72IIkVKVKOKbVnuvSWOY8dxieMUJ5dzLv4mv4svcew4di7tzpfEjh13q1hdVBclFrG37RVlF70Dg5n5/QES5HJ3yaVMJbbD1+PBP7gABoPB4PN5l9f79ZpNl1BVlTvWBQm5zCTjpUWfW6cTMEgi+YoCOh186FfAXFfyKqdSLOUuJSsqOlGgmM/idDgbHHJN0S5rczA/P8/I/HOsubWbrddtoXPwMtxyRUH7rd9C0+sRKhXy3d2c+OY3aW1tZdcbdzGkDFHJ1tVLBwYGGBgYaIhJtHksmJQCZ86cwtpy/ZKHLyVKtG5rxbBEt+8afj6gqRqaqlHhPDV6AQQQ0BhYvZr+Dhd9b+lDFEXK5TJm89VP4CVJalC0lsKBl6d48t+PU85VCG5vJT9fID+bQynKaKqGUquhpfLoPU50d96x4Lgr9XLyr/ZjGhVImqMc6dGz9bYb4KiGy+NaRBc7J2J1YdCVSCQuaVYOIPzP/wl79qAdOoQsiigf+ADmL30JAoFGQLJSGMwGbE4btUoNx/VbMDvayBsNmPVQkmXOfUulqkzw7Cx0rVLD1mLD6rPysdd9jKnDU6gJFaWmIJklmtc14+pyYW2q+4Q625yEAg6OJwvIZmmB8JKqQjpfpsMk0bbUHOeRI/Cxj6Hq9GTKRibf80kyO3bj9rtpHmzG3eM+b5cgwLrXr8Ois5AYSlDU6irDEycnsJastPa30rajbbG5/TX8QqBWU5EvEvmSLBJ6vYm/+MKXmJuLMj4+3kjsXisT60AgQDgcXnHScQ733HMP2WyW9lCIXE8PqY9/nODZwo/BYEBRFGq12pJiU4GAg871zZx6egKLy4TOoEPLZFHMFoqFAoFAsN7VmysgGvWsv/58B9zpdDI1NbVgDYtGo6xfv375k9XpcD7zFNs2bGB9eprJ+Gky+jjBT3+KgN/BxNgofu/KRLYuXI98PivmnVvJawKeVielTBbpgq2jmK5gdZlwnS36qKpKLpfDvYTN1XKIRCI89NBD1Ijwjg9+aEWvUf/qrxD27kWj7jn45Le+RbC3lzVr1nDrrbdSq9XYtGkTW7ZsQVVVxm7p5PDjo5QyFRzNFkSdSDFdJj9fpLnPyy139634fH8RcC3R+zmE3qxHMkq4VYV5uQIsDlCKFQWTXkDUVFRRfc3lppubm5menqa9/dImlFOTKV7ZO83EyXoHsGNwKy3tOto+86tM33UXXdvPy/SKorhAbe9C+AZ8JM4kKKVKmN1mQm4z6x01DCMHObqhm41r22lxGDFKOtpXmzi5dwo57yNlUqi6LHgCHiSDgUKhgMlsIRlPo1YgNzOH1WZg++v72fW6fix2Iw6/lYePR5hKatSu24k/k8V65x3IkQjKd7+LOr2X4r33okoWzkSzuMwG7lgXZG2ozjc3OozozlKLLkzI2t0WDk6mwGJoJHmXQrYk0+I0UcnNE+jqbPxd07RLGo8nEgm+853vkMvl2HaDn641y3TzCgUKDz9M+f77Mb7wAmadDrFUQmtpwf7YY2zv6mo81b/az+hjo9habEvadTz55JNMT01TrVS57fbbFjxWLVTRG/W4e65MrfEa/mMhWSQkk4SrUCEpV7n425IVFVVTqWaylFQjiUSCYDBIPB6/olmLK4HFYqFQKGC1Lqymjo0m2PO9E9SqCoHBekfR7rOg9Hio5KtUZZnxkRN0nRhibN1O9jwywt1vqs+oejweUqnUiqnn+tgI7naRjW/aSKA9wBs63oDNZqNYLDI2NkZnZyeiKFJMFClFSoSnwhRdRcw2M6lqioEtA5d+A0GAH/4Q4Z/+CcMf/zGVf/xH5B07qL33vaQ/9CFCfSsPMsxeM7YWG/lYHme7k45mB6ciWQSLAa1WQ1U1EoUqRqlOT1MVlVq5hqHZgMVowRFwsDawFk3TUGsqol5ctJdY/BbWbWsl/uAZIvN5TA4TZklHtaaSz1fwFGTW7ejAdc7zcHaW6k9/ivzQQxgOHkSnasxqzYTX3o665W5sRhOVZIXRx0ZxtjvpubOnsb5Z/Vb6Xt/H/Kl5hl4core1F8kk0burl7bNbVgu4491DT+/MBj0NHe5Gd47jckqoVEX4NG1yPzd3/0tTU1NS3pzXm1cKu64FDweDwaDgfEvfpGWYpHmz39+weNut5t0Or2k7QzAnfcOkooViJ6axxm0o6XSyE1+PF4viqyQjuSolmusujnI93/w98yGb6Cnp4dgMEh7ezsjIyP09vYiy/KSyeQitLcj/MM/YLHZ6Fq3jtRnPoPl+vWk/viPYdeuK/78AHaHkYEdHex/YAibx4LBYECuVpEMBiqFKpVshevv6sN0VsV3ampqwXzxSjA/P08ikbikgJamKKSfeYbSffdhePpprJOTmGo1MJlQv/517n33uxc8/9d+7dca65ooitzztrV4/DaO7p0iFc6iqRomq4FNd/Vy4+09DQuNXxZcS/R+DmHxWXC0O/CcKGPS60kX5YbkPkBFVihWa7iycX607wU8KQ/GR4389m//9soWgFeBOq3w0t3CM6fneOCfD5GN5jGf3bgPPzaCWpznHaM5Wv7P/1nx+zk7nLTe0MrUc1N12qIJsmdOsjk7R67Dv0Dl8bq1W0iNJxmvDhPoWYvLZqIyX6RakqlUK0j6HIpWwxfw0H9DB2uva6W129344QddZt6+pY3js2mOzGQYv/EOhJKAwdeK8JufpXj8OJXvP4gt4Oe6D76ddYNthFznOxpGpxGD1UA1X12Q6AVdZk5Hc+TK8gJ6o6jTodQUdBcMfcs1FVXTsAtl3G43kr7+fKWqIOpFjM7lO6ler5dbbrmF06dPLwzSVJXq/v3kf/QjxCefxDA/T2nrVgxveAPWv/5rhG98A771LYSnngK3m3K6XF/w3Cbc3W7sITvp8TSubteC4xbyBfL5PPFEnInJCRLxBF5fvaOsyArZqSwtG1uW9eG7htcO1XwVuSSjN+ovO8tkcppw97jxzecJnzXK9liNiGL9fpzLlfEbJUZe2s9IRaNNbuNXfuVXXlVnf6Xw+XxMTU0tSvQO7Z2ikCo1jIbPQWfQYfGYKc7nCUQjqFY9xpCP43un2HFrFw6HCYPB0LCSuRyi0SimU6cwDAw01tJzFW2LxUJ7ezvHnj+GIWegMF2gmq+CAFPxKdxuN6VqCSEi4Fvlw93tXr771NoKX/gCAMaPfxw+8AFqX/gCnt27kT//eQy/9msronOKOhH/aj+piRSKrLA26ERVYSpVJJWXsahlnGYDawIO/HYj+Wgei8dC1VLF6zo/lycIwpKKvqWSTDpVwrPGx3XZHA//8CWMpRBlBPSaSr/TzKrr/Qzqxqn81t+iPvccmslE7dZbsfzO7yBs305qwy3Mhm7F/IG3Y7qA3qkqKsmRJFMvTtH3ur7GGmP2mGnf2U5wS5Bqqcp8Yp6O3o4VfX/X8PONu9+2hkpJJjpSd+zt2BTkde8Y5Jvf+kcOHz7Mjh07FtkBvBZQagYeefAYTqed9ZuDK5bRt0gS3r/5G7Kf+QxWy0XK2Wfpm8slel6PhXd+5Doe/uFxzuyfQCtZkKtOSsNpBFHE3mJj2xsH2L6zlcc//Y/8r//1v+ju7mbHjh185CMfIRgMEg6HKRQKdF1QlL0k3vlOAExA4Ic/JLtvH9qnPoX1S18i9b//N+7bbrv065fALXf2Mz+bY/poDEESUTQZHXq0mkbP9lZ23NoJ1O11DAbDJVkaS2H16tU4HI5F83yl0VEyP/gBPP441uPHkbu6EG6/HeP/+3+YvV7YtAnhgQcQzwqGXYiL7yedXuSm27rZdmM74dks3//+D9mxcxM7d264sovxC4JXlRVMTExw33338cILL3Dy5Eni8TiCIODz+RgcHGTnzp3ce++9K78Zr2EBBEHAv9pPejzNKkngTKHKbKqIQa+jpqiIgkC300ynrQ3nXVs4lj2GKItMTEwQCARes/Py+/2Mj83Q1b2Y8lCrqez58SkK8QLBNU2IZ6lgStDG9Hde4b51b+Fzg2sWve5cde1iioYgCAS3BBF0AicePUElX8GarJB3uDEaTdQqNcqpMuV0GZ/Hx6rXrWJtaC0Gg4FSPsqGtVuxWv3UqgpTU5OEWj2s29SHZFp6rshpltjZ62dLh4fR+TwTiQLFqoKialju3Ems08Fdx/fhe8dtVD/yEfhv/w3O0ijOda/CB8ILTIMdZom+JjvHZtPodCKWs8bhVouFYrGA/SztQ1ZUYtkyrU4DzTYBQaARZBbjRaxNVqwXDQbPZcpMTKZQVI32dmeDf67OzJD+m79BeeghLMePU+ruRrjzTmz/+q/oBwexXLjg/c7vkPzVX8VkNFI4Nc/U8/WkOrg1SHBzkO7buhl+ZJj0WBpXl6tBJY1EIlQrVURRJBaLcfzEcXbt2oVSVUiPp/EOeGm/sX15s9ZruOqoZCuEXwmTHElSK9Ua92RgUwCzZ3maZdPaJtITaarRHKOCSixbpyGLgkDQYqRLBfs7d7M/vZ9QKMT09PSrqoZfCaoVhfm5fKOqOj9fYPRQFNslujlKLkdTLEbqlpux+S0kxtIcOxRh580r24NUVWVycrLu8zc7S6W1dREFU1VUogejVI5UCM+Hcbe68Q7UkyVHp4OJyQnaW9tJjadIDtf93tp2tF2Wdg2AzYb5L/+S2be/Hc+Xv0zla19D/+Uvo9u9+7IvdXe78fX7iJ+O4+51s6XTTV+zjcmIgM/jwWczIOlFSskS1UIV+zo7Hb0dlxRGyOcrvLBnjJP7ZillyhTLRbLKHKFOibe+bh25w6cRX3kZ777ncPz7FPlN29HvvhPhd/8Yc08Q4wW//fhffoP8oWlcZ4t/qqohigKiTsQRcpCZyFCcLy5a4/QmPXqTHl1heUuZa/jFgt9v5QO/uY1IuK4KHgw5MBj0vO997+NHP/oRO3fu5NChQzidztesaJ1Ol3jo308TPRNHp9Nx5kiE9350KwbD0u935uQc8fkCvQM+1H/6a8yCQPPv/d6i53k8Hubn5y/53maTxvZb3ey6q5dTN36dqbd8jMCmVfhb7KzZ0IzdWi+gffjDH+av//qvCYfDjZloi8XSsMrqu4Ku/4VwXH892R/+EOHpp+GjHyXa24vja1/DcgXHszuMvOsj13H4wCwnD84SnZ4n2Oln9ZYQm65rbXTzIpEIvb29TE1NXdE5zszM0N7eTqvHw/w3v0ntoYcw792LajCg3nQTxk9+EuvrXtdQKj2H/MQEtrMx1ZmTc0yMJtlwXYiWwPJdYoNBT6jVzsjoAZKpMbZuXb8iiv8vGq7ol/Tggw/ypS99ieeffx5N0+jp6aG7u5t169ahaRqpVIrDhw/zwx/+kM9+9rPceOON/N7v/R733HPPa3X+v7Rwd7tpu7EN4cUZLFWNtMVIQVMxiAIuWcOpibRsbOb6nddR+VaFd7/73Rw6dIipqSkGBwfp6Li0r8yrQTRS4sU90yTmVa7btpDCOToUJz6ZxtPuaiR5AIXhYZrzYcJ9NzN8Js6adQsHa10uF+l0esm5Fg2NrCXL6retRktpHHzmEeJmP7q8jnwkj8lpovPWTtxdbjbYN6DX66lWqzzxxBMcPvkyGzduZOv1W5HsOSwWy7JJ3oUwSTrWBJ2sCZ6nptVqNV7KWWl/x+dRP/Vxyr/7uxT6+9H+9E+xvf/9IAh4ej3EjsSQi/KCGYRVLXZqqsqZWI5sScZpljBLdUECuaaSLsnIikKH10qLvkgw0E4kHEEQBVRFpZKp0Lq9tVFtV1WNJ/dOcfDxEUrRPCgKainOKmUc94EfopP01G65BetnP4v51lsxX0owQxAYGh5m79696E/psRvd9PT3Ejsao3ltM7YWG3139zG2Z4zEUAKD3YDVbyWbzWIym3DYHVgtVmrFGsnRJIIm0LS2iY5dHUuarV/Da4NqvsroY6NEzsSJ60WyqDiKVTwvFyjOFel9fS8m59LUX1uLje7d3eiencQ5myMriKiigEHRcJsMePs9dN7ciS/qo7e3F03T2L9/P6IoEgwGX5Ng7NjBDHMzU9z73k0Egg7C0xlK6TLNA0tXygH8U1Nora2YOjowmg0IOpHZiRSsINErlUpEIhE6OjrQ6XRUxsaobduG03T+mmmaRvhAmOkXprH4LHR3dpNJZ0ilUvUOvEGqF630Ku4uN9VClfD+MJqi0XlL54o8LZPJJM41azB/97uoBw9S/fSnqf7lX2L66lcRVi3vPaU36em8pRNN0+q/U5sBi99Cb8BNrSajFCA3V0Rn0BHcHkQMiZhMy1PBi6UqP/jnVxg/GMbkMGHxmChMzKMOpSkcy5P54Qfoazch33wnhXv/jJGCmVKyjFbV0D0Wxh7M4Rv04e6pdzQL8RIj0yOciJzAbLbiae8l4Pfgsxkx2AzkZnNUcpVFid41/HLCaNTTeZGCZGtrK7/xG7+BwWDA7/eTTqd58cUXqVar9PT0XNVZvdHhBPMjKYKDfiolmZlT84Rnc3R2LR41OH40ygPfOEgpVeZgp4N3/P0/EvnYx3Assa96vV7OnDmz7PvOzc1RrVbp6+ujODZGx8xzjL7zb+lZt7gAvn79erZt20ZrayuJRII9e/awa9cuTCYTsiz/TF3PUqlE3/vfj/ae9xD70pdQdu0i/IY34P/Lv0Ra4Syd1Wpg581d7Ly5i7GxsUWCVJFIhEAgsOzM4lLQFIX4nj1Uv/tdkgcOYJibo7Z5M+Ldd2P+8pcxXqaJ8fCjj1IoFPB4fJx8XiM7WSQdL/CuX91yydeNjo4iCAJzc3M89dRT3HXXXSs6318krHiX3r59O0eOHOFNb3oT3/ve99i9e/ey6o7ZbJbHH3+cH/zgB7zzne9kw4YN7N2796qd9C8iNE2jMFdAb9Rfct7qHARBILApgMVjIX4mTnoijVJREHUilnYL/tV+vH1edAYdH//4xwHo7Oyks7OT4eFhnnjiCXp6eujq6rpqCZ/bY6Ep4KapZXGFpFCoolQUjBdQIDRVRTpwELmnA1EyUSwspk95PB7C4fCiRO/cPEx/f3+jwpI0DJFvqzH4kR0YTAaMDmPdCuACmEwmXv/617N3714OHz5MMpmktbUVm81GOp1ekXHyxYhGow3zYtHnw/PP/4x86hT5T3yC7Je/jOFrX8O+YyfeAS9zx+bw9Hsa11ynE1gfcuKzGZlMFolmSqSKVfL5Gi6djM9moMNrwUoFm3mhxHluNoc9ZMfTc/7aHD4V48X/9yK68Vk8mRl0pSJpcxOnOjpp/Ycfc/NtizcNqJsuZ7NZ8vn8or9nMhk8Rg8+uxclL2MKORuBqa3FxsAbB0iNpYifjJOZzNCsb2awaRBL0YLFYmHjwEa8XV58q3y4ulyXNHW/hquP+FCcueEEZ0SNaLaIUdIxLSv4rAaEsSSuU3Faty8vPODqdGHxW0hPpOvrjKxgctTpu45WB6JepM9er/gKgkAwGCQUChEOh1EUhUAgcFWroO1dXmqyhv0s9VSWa6DV/Y2WRC6HePo0vOMdDXVaQRSolOTLvlcikaBcLi8IVPRTU+Rb2jjxxAgTQ3HMViNdLXaUoQQWnwWTu75+O11OisUic7E5bHYbLc0tZDIZTEYTBqsBZ4eT6OEo1iYrzesvPx+Yz+cbM9Dili2Ynn8e+cc/Rn7zm1FuvBHzF78Iy9DCjA4jvXf14unxMH9ynlw4hyqrpDNpmkJNNK1rqs891xJ0tF2aBnlw7wyTB2bxGUuIY6fRhcOEBIGmlmYiQgfPv/FOOn5tCzPPTZMZyWB0ytiCtrqgQa7I9Klpxl4Zw93vpmV7C+ls/Z7Kl/M0NzXT5HFhkerrtqqodXXNSwisvFYCY9fw84UL1xCLxcLmzZuxWCwcPXoUs9lMb2/vkvPiVwqzWUJn1JGLF6mVa0hGfaMLdTEiMxlKqRLeLjdzLxwhZg/Q9/u/TzweX6R66XQ6yWQyi46haRrj4+N4PJ7Ga0rHj4PTiX6ZGNpms/Gxj30Mq9VKIpHgscce4/777ycUCrFt2zampqYafoOvFoJOR8vnPkft4x8n97nPUVq9mvgnPkHLH/wBwhUU8CwWC8ViEctZKmutVqNSqWC1WikWi5fcG4qjo4x/58c898wM+XgZr7nIzbvaMPzTP+G47jocS8Su5wRecrncAnaJLMvMzMwgSRItHR0Isg7/Jbp555BIJGhtbUVV1VcVH/4iYMXf5q233sp99923ooF2h8PB2972Nt72trcRjUb56le/+jOd5C8Dxg9FePg7R7HYjbzto1txtFx+fkkQBFydLlydLsrpct1HSC9icpmW3BgFQUAURQYGBujr62NsbIwnnniCzs7Oq1IVc7vN3HnP0pVlp8uE3qKnmC1jcdQDodzJk1gLeeR1r8eQqqKXFm/Y5xaJCxGLxcjlcqxdu7bxt2KxiDMeJTMwgN5tYe9zk8iywrrNQVrbFgpDiKLIzp078Xq9PPTQQ0SjUd7ylreQTCax2+1XtFmkc2VOnJrkuuvWLfi7NDiIe88eio8+ivzRj1Ls6qL5z75E3m8hN5vD0Xp+ARdEgdBZQZlMUaZYVZibhxa/B7/DjKapxKJZrP7ziV45U0atqbRub0WfmSfzzz9Gfugh9s01o6ptuHudGLbdStFgxGcxkxxO88rxFN0DYeTKYj8ro9GIw+HA7/cvSPwDgQCqqnLD5huYPzaPqqo0r2teQLs02o20bGjBv9pPdiZLOVWmZb6F3FM5dAYd6965DluL7TWfq7iGxdA0jcTpBBkB5gpVgi4zoiigqhDJFMnZzcTPxAleF7xkV8lgNdC0pommpZQTl4AoirS2tqJpWp3KW60SCASuyuzelm1tbNl2XonXYNSDCIqsolvCl1F96WXo6ET0eiFXL2RoioL5gqKT1WpdpGg5NTWF3W5fKBZQKkFsjofOSMQeP4oo6dBkhTOJMqt6PKy7dWGHUDKYqOhVTp0YYcvaQZr8TczPz9PU3IRkkTDYDcydmMM36LvyAoggIL31rfDGN1L5m79Bvu46lI98BNPv/R4scZ31Jj1Na5vwrfJRmC+gVBQi0QidA52Y3WaSySQecRlFUE2Do0cp3XcfRx/JIOT1iC1G9F2dCNu2UZCr2B0OWosy8dEEj//9i9gEMLfZmMplCIfnKcsaXpuR3qCHdouRzGQGbVKjb1sfyYkk6wfX09S88P4qzhexeC3YVrAfXsPPD376oxNEpzLc+74Nr4kM/bnfqs1mY+PGjeTzeY4dO4bJZKK3t/dnYhIMDPrZcFsXp16aQbJIbLu7b1l6X2evlyPNNlLjKYJTR3D91oex2u3El7BSEEURo9FIqVRqqBJXKhUmJyfp7OxckPBUT59GaW7G6XQyM50hPJ2hvcu94DzOrVU+n4+3vOUtPProo+zZs4fOzk7sdnv993wZhd+LUS6XF3Xz9Q4Hwb/7O4q/+7vwW79Fur+f2p/9Gf73vnfR66vVGgdfniGTLGG2Smy+vg2/38/MzEwj8ZycnGyMbV2sdlzL5Ujdfz/yT3+KZe9eajo9j695P3Om1Vi3+jhTlvHdtIHXbeolmUotKkyfu852u32R9ca6desa/2o1lVSyuCJRlZ07dzI4OMiBAwfYtm3byi7kLxhW/Gv58z//81f1Bi0tLa/6tb9MiM5kScYKJOYyjJ2ZYq2v/4oWq5V0AS+sfIqiSG9vLz09PYyPj/Pkk0/S1tZGf3//ayJZ3NXjoaXXy/TRKMYBA4KgYXjlENnePkoJmdCaJtzeyx9nZGQEu91Ob2/vgr9ns1kc8TiV22/nwe8d49Rzk6BqjByJ8quf3YHNtjjw6WnvYVP/JqZiU/z4xz/m1ltvZXJycknfq4sxNBznwIuTzByfJxab4/jTRQa2hrh+RztNFywelrvuguPHyf3TPyG+6S4cN95L4rp3kQsL2IOLNw+nRcJpkXAZ/RSLRUTRQng2RnPL+QKKnCyQemkY3/xBpK89SUHSI+/ahfSJT1I8acU6nsXU4wZVQ8vnEHU6TDaJ/FwevclKW2ihD5laUxF0wpKJmNls5razA9ntN11aUVUn6XB3uaELfLKP5yefB0B0LFbqu4b/GGiqhlJVqIkAWoM2LYp1OxFZFFBrakNR8WrjXIcP6nSdSqVCS0vLJamBV4r2TjdWr4XMfAHPxb+pdBpxZBje8x6g7hdXKVVBE+joPb/guFwuIpEINpsNWZaZmpqira1tcbV5bIwhbz/hqQreNg8WhwklX2VueIxpi8RaVWsUQeayFV6ZSpEuViiWjEQOjbO500/IYSWXzWF32LH4LWQmM2Sns7i7l6ZFVas1ZqfTxOYKNDfXMF480ydJGH/nd+DXfo3a5z9Pde1ahD/5E6T3vndJr0BRL2I/GzBmxSxmt7lhQN3Z2Xn+eXNzVB97DPmnP8Vw8CC1nh7U3XdS3nQDJosV6Sxzo1KuNL5Pg0VCSVWQLSbabu/m4HSa8ZSKpDNgMInMlRTi42nWhRx0tzqYPzVPzx099G7uJR/LIztkJLOEqqgU40Wq+SrBW4OLmBnX8PMLRVEZPRYlFc7y/LOH2H3nBmy2q1voKxQKCyT5zyV8xWKREydOIEkSfX19r8rLU6cTufcd67hxdw96vYjDvvxa1dfv472fvoEz/+OL9KgHaP/kP1/y2OcEWUKhEMlkkmw2S39//6LnaSMjFJqbkXPw3b97mUw4h7fdxft/azte7+LE2Ww2s27dOkwmEw888AA33XRTo3h7JXFkNBpdVgXT0tOD5cEHyTz3HPzWbzH313+N8atfxXnDDUBdh+GH3zzC0ItTqKqGIMCZIxHe8+vXoygKAKlUCqfT2YgxK+Uy4okThB96COmppzBFInU65l13YfqLv0D2NVP5kz34FA3VokJEx/CpGbZscuBwOGhra1vxfbVu3flivF4vXpFy5rlC4C8rXvXquhKOcDabverm3b+oWLutlVyySKma43s//RZPHvDS39/PG9/4xtf0fQVBoLu7m+7ubiYnJ9mzZw+BQIBVq1ZdFRrEOYiiyJ1vXc19uQrRM/PIsTDGihXZ1UdTu4u73rYWQcwt+/pqtcrQ0BDd3d0NCsCFyKbTdMfjaF1dRMdSmJ0mzC4jmViOZLy4KNHLzmYZemSI9ESRNb1rGGOMxx57jDVr1mCz2ZY1GwU4cGCWx79zhGqyhMltwmwxIBdk9v3kFGPHY7z1w5tpvWCGD1HE/rGPwQc/iPt//A8M/+s3mLjxvcg7bsHZ5UYn6eoD3dEc665rxeEwYjabSSQSFPIFTCYjTEyQe+FFCvvPICUz+DZ4aX3vzdi+8f8hNDVhp/6bs0QPED+TrFOBCwWsZ6t+laKMvcuHx3l+cVNrKuPPTzF6LIrbZ2X17m6sV6n6KklSQ80wn88vUkm8hv8YiDoRi9+COZxFEnXkyzVsJj2FSg1VqUE2j7HLjc5wdX7rF1J0LsY5IahoNEq5XKapqWnZ514JXC4z/VuCHHhoCFezbQGFU937EvT2IZ6l3JjMZmaHY7gDLlavPz8PfE70KZPJkE6n6e7uXnr/Ghsj3tyJoIoNZoKarSDpRMqVGrKsYjDqqMgKr0ylyJZkrDoVv99JpqzwymQSS7cbo6hgqVkaXbz0VHrJRG//3in2PTlGbCKOXi+x//EIW27tZvvO9sUFOZcL89e+BuPjlD7zGdSvfAXpq19tqMzVaipPPnQGk0Xi5t31Qpndbiefz9cp7F4v6qOPUrrvPqRnnsGhach33IH5N38T8cYbkc52CU1/9jTpyPm1ulqtNISjqoUqulwVq9vEbLbMRKJQn7U7W0RwIpEtyZyK5gg4zSgVhcJcgZ47eph6YYrMdAZVrtOtzG4znbd00rJ+4dz2xdA0jXw0T2GugCAK2Fps12wW/hOh04nc8/6NzMdyPPXs9zhx6mmam5v58Ic/vCCmqFRqlEoyLteVe24uF1+aTGYC3QMUS2X2HzmBw6RfMN5xJfC4V3YP+YxVvN/9Gtm///tGYWU5EblziZ6qqphMpgWFlQshjo8jt7WRCGfJRHJ4OpykZ3NEZnNLJnoAuVyOu+++m5MnT/Lcc88xODiILMsrFmaJRLIcOzpPuWSht8+HfpnCn/Omm+DgQea/+U2097yHyMaNuL76VYaTEsP7ZnC22rE4TMjlGrPH59j/4iRdA1KdXZJIENLrif7Lv8Cjj2I+dIhKZyfCbbdh+N//G/u2bdgv+F71ioojaCeyP4xkEajVBFb3BhvFw/8oGI3GFSsz/yLiVSd6N910E//yL/9CT0/Pko8//PDDfPSjH2V6evpVn9wvExzNNu76lc0A6B/MceLEiSV/oMVSFZ0oLq7qrgCXS7w7Ojro6OhgZmaGp556iqamJlavXn3VBBXaO9y87xPbOPDiBKN/+AClVX1s+OAWNm4N4fVamZpaOtErFoucPn2atWvXLtttLI2NoavVMA0O0ulr4uiTY1SyZVoGm/AvIeM/+/IsL78ySd5tRz6TwtXp4mT4JJOTk6xbt4577rlnyeQkkSzy1A9PoBZlWtY0kcvm8Dhc2Ow2nC02YqfjPPKjk3z4N7cvEJ0BwGzG/Rd/gfOzn0X96O8Q/8snmLvznVi3b2H4UIRsvIC32Y5jTRNaKoX4zLMUjx/HHo6ScAcpd63G++sfgq0B1uzevEi1UhAENm5t5dEjMfITaTSngKBCMZKlJMLWzSGMF1g2JIYSPPXAaWZrCpaTc2iqxtb3rrtqlVebzUYymSSfz6/Yo+warj58q3wkhhL06AUmSjLhswq9XVYjx555hsmqnePKcd71rncBkMtVSMQLuNzmRUGYIitU81U0VUNn0GG0X6Rsls9fdo7h3Dzr3Nwcc3Nz+Hy+KzIBXwpbdrQzciTC3EiCpl5vPdlLJBAnJ+B972s8L5coUisrbL6lc5FkejQaxWQyXXK2RR0Zwe4zYrQZySWK2L0W1KqCXJLxerwYjPXf13y+QrpYpdlholioodfr8Np0zMo1EsUaLqHEyZMnWb16NUaTkWpucRBx4OUpHvu3I2iKhsljxG6zko0VePLbR9BUbXnF0K4uzPfdh/rCC8if/jRqKIT5q18lIro5/Mw4eknPlu1t2KwGXNPTzHzjGzhffhktGqW0bRvGe+5B/B//gzlZXtI0evX1IZ75zlHkioKqyphM5++R1EQas1lP+6Cfg3M5dKLYSPLOwWGSmE0XieUqBJ1GMjMZOnZ1sOpNq8hFclRzVURJxNZiO2+Wvtz3oagkjySZGZuBsyOXBpuB4NYggc2Ba0yC/yR093jp7vFidbyehx56aBFLRlVVvvOP+0iE87zlI1vo7lkBnecSUFWNE5EsR6ZThNNlaoqK2Wih22MifvgkXrOOvr6+JZkEkXCWmqzSds7n8QoR/6M/wtjRgf+Cdcbv9zM3N9dY687B4XDw/PPP87a3ve2SRS7D9DS1rVvp6Pbi7/KQnMnQ3OOmvXPpcxwdifLwv08xPaqnb40evV7PAw88wJYtW5AkadmEEupJ86M/PcOhPWOko2kOO8O0DPh4ywc24fMuc46CgP+DH0R997sp/c//SW37dqbu+DCKsBGLo/5dSiY9giCQS+SpPrSXocceo+noUco6HerOnRh+/ddJrltH16WEpHQi97x9Lf9ezEFZT7DPy+139C753EKhyve/cYBaTeOdv7YFh+PqsUagnrwrinJVGyA/L3jVEf7o6CgbNmzgi1/8Ip/85Ccbf8/lcvz2b/823/jGN9i6detVOclfNuzcuZPu7m7a2toYGRmhvb19QUVqudlzTdVQ5Lqv2sUzerVa7ZI3aKYkIwj1Tbi1tZXW1lYikQjPPvssHo+H1atXLziHdLqE3WZEd4V0L4/XSs/hH7Ej8gA8M4TNu5A+lclkFpgtT05OYjQaaWpqWpDkyYrKZKLA6HyBbFlm/HCU0xvvwO4IcfP2boIdLqoVhTUbWzCbF9M3auUaFUGjBAyNj6PMRjAMGAgGg4yPj/OVr3yF9773vfj9fiwWS+PaHT0UIR/LERioz7IVioVGEqOTRNytdqJn4oxNJOntXnrzEltaaL//3/Dvf4XZX/9DUi8+Qvu2t5DWl5B+9CPmvjwEAqR9Tdi27Eb84FoczQ66B334VvmYnpte1prgujXNxO4dYP8DxxFjGrlYAr3LxJq7+7jxgrkmgGqxSqZYRfOZKWarpBJFNEVD0F/dRC8znyFvy2P1W69ZKvwnwN3lpu2GNsT9s7hrGlWjHoMKTosBz71bOJk7SVvb+XtDUVSUmoqqnB9kL6fLJMeSxE/FqWQroIJoEHG2O/H2e3G21QV6KpXKiqvn57rm8XiceDyOx+NZwPCoVmtEwjk6Oi+v9BZqdfL6927kp986TPjkHPYmK5Z9e9GtWoVot1PKVchE8gjA4K4gN912PvBUVZWJiYnGjOqlIJ8+TajdyeD2Vo4/O0kumkeN5DFZDAxuOq/6pqh1VWBFqS3wwwQYHhmhEhsnl89htVgJ2oMYSoYFXYpaTWX/nnGUmkZLn4d8Lo/RasDfbWB+LMXBp8bZsq1tWZEIAHHnTowHDiB/+9tU77gD992vY+eOd6MfO4Pwq+9H3r+fWnc3hf5+2v7v/4XBQaSz71+pVDAsIRwBsGV7G0NHooRPzCFawBfyUsyWyYTzSDqRrlV+DCY9FVlBWkogR6jThmuKiiDp0Gpa/bOLAo7QlTF8kiNJMicySC4J79n1tpgoMv3iNFa/FWe78zJHuIbXEmvWrKFarbJx40YmJydxuVwNymW1oqDICrKs/EzvoaoaTw/N8/zpOXSRPLZ4CbOsINuNvOIx4utyc3ebn9HRURRFoa+vrzEjt/f5CZ790UlUReP6u3u5/XUDV/Te5dlZfN/8Jrlvf3sBTdpkMlEuL5yFPycQYjabL8tkMEejCD09+P1W3v+p7cTCOUJtzoYA1cUYGZrEKJrY/8Jhjp6eRKfTsWHDBhKJBP/2b//GXXfdRU9PD1arddH6fOJEjAMPDyOKGi39XvSCxPThKE96zvCuD2665HmKBgOBP/kTqp/8JI5PfgHh+D7mc+04u5opDE8gz+QxPv/3WNpVIuvW0fH1r2Nqa+Pcil5cgbWC16Xn/R9aj8fftKBQfTES8QKR0RRqTSUWyV31RM9ms9VZUhbbFce9P+941YneyZMn+c3f/E0+/elP8+Mf/5hvfOMbDA8P85GPfIRoNMqf/dmf8Qd/8AdX81x/aeB2uxuLYU9PD1NTU1gslnrSYV4cRBUTRZKjScaPRCmXakiSjo41Tfj6PA0RjHw+j92+eCYsVaiy51iE8aG612HPKh+3rg3gNEsEAgECgQCxWIwXXngBh8PB2rVrUVWRidEEZrOBgdUrE2donGsmg+/rXyf6679Or3dhIuRwOJiamsLpdKIoCmfOnCEUChEKhRgaGqKlpYWaonJkJsORmXrlTlE1jJKOWKJAsmsdwnSZkcI0a4NOrut041jGMsHUYWJwzEWhqPBKeoot79hEhgz33nsvhUKBRx99lJ/85Cfcc889WCyWhnrTqaNT6HS6xjyTdhE9w+QykQrniM7mlk306i/U0Esinjdej/e736fnu58l7QpQeNN7kD7xeapmC8X5CMGBVoLdQexB+/k5lbnlD6vXibz+5i78HpVKxYSmarR3uukKONBdlGTZA3Z6O1wcPj5NsMlDz7rmqzqnZbPZUCsqY4+MUXJX6dzRRmjr0vz/a3jtIIgCoW0hHK0OUmMpypkyRocRd5ebTU2b8L/iZ8uWLYyOjhIKhXC5FnbyEsMJpp6bopgoYnQY6757AihVhfipOPMn5/EP+i87w7kcfD4fPp+PZDLJxMQEbrcbp9NZH+pPlVAUdUUV/1VrmrD8xlZefnqC8WdOMR+pQWsfwok5RJOeQL+XjTvaaeuqU4pNJhOFQoFYLEZnZyeRSGRJutWF0EZG0N18M/e+ewOdA35mJ9PIM1mM8yVaL0gqvFYDFkliPlMg6HMBUKoqSDodN16/mWzUx6lTp+r05lIVRVCYnZ1tzFFHI3nmJpM4m+2oirLgnJwtNlLhHLPTaXr6lreUAKBSQdfUROmOOzB+/3vs+Md/oHz99Zj++39H/Na3SKRSiNksXDQndCnTe4fDxDt+bQsPfv8VZk+nSU5nEPUigQEfm7aGEE4nUCoKbouReH4xS0NVNUDAatSjlGqYHKZX3XlLDCcwWoyoRpVsSUYUBGxeC6VkidR46lqi958MQRDYvLnOVOrq6iKRSDA5OUl7ezvv/uhWspnKIrG0K8XwXJ4XhuZwDKWQZnJg0iFIIuJsjqb5AklZ5WlJxwe2r0IvaAwNDSHLMr29vZw8MEs5V0UniRzfO3PFiV7yc59DWr0a/1vecsnnRaNRVFVl/fr1HDt2bEnqqaJqZEoypWQKc1XDc/31QF3kzu1ent6qaRqBVjOrPzbAv37rH2lvb0fTNNasWcPg4CCP73mKHz36NLtu1eH3enBIYLpAtOrgy5PIhSq2dhNWqwVEAbvPwuTxGPlCFdsKzOLVYpHe6zqZmDnD+PFR5k5MUPO46bhrA7s+/0eEw9P4RBFTW9tlj3UxYrHYijy3Q60ONE+MXTfd8jN3iJeC1Wpl30vjHHt2jjXXt3Hnvct3In/R8KoTPbfbzXe+8x3e9ra38YlPfILVq1dTLpfZtGkTDzzwwILByGtYHoIg0NHRQTqdZmxsjM7OzgWbfvx0nAMPDTE2mSINyDoBnaZx9GSM9hY71+3uIbQ1RC6XW0QjUFSNnx6cZuiRERzpKirwyuk45arCO2/obFAPm5ubaW5uJh6Ps3fvXqxWKxZzkOYlxEQuh/AXv0gTEPzDP1z2Ofl8nsnJSQYGBhq00VqtRqWm8OSpOfZPJDFJOkJuc6PCI8ZnkCjhCbpJlWSePjPHdLLI69cF8NsXByyqT2XHh7ZSzVWZMBzjVPgUa9asaQSd73jHO/je977HY489xu2778AX6qSqqIjmMHIthaJoqIqMtET3YrmQRZubI/ujH1F76CEsr7xCua0Ndu/G9r1/R1q7Ft3f/A0tX/4yZUeCuQ98gDvefgdDQ0PLijQse43Ds2zbcHmpaUfIQckTxtue4k3vuwNv/9VbHLPZLC+//DLjx8bRDxvZuWUn/gtsIK7hPxaCIOBodSxQez2HHTt2APWi0uzsLHq9vtGlTo2lGHtiDABbt4vZTJnwbJqaouGySLT6LbgkkdjRGKqiYhhYPig4dTzG6aNRfAE7O3Z1otMtnl/xeDykUikmJiZoChhQFZVgaOWBYHuHm/YPuZn55//OxI4tKO/bgk4n4g/Y6O3zodPVZ2fC4TBGoxFZlhu0skt5dp6DODEBH/4wer3I5q2tbN7aSmY6w6kfnaJWrjWKMTaTnsGgnZeH88ykiojUO4cdLolmu5EWZz+tba0YDUZeeeIVenb3LKBJCmTQSyNA3dfKYlnhjKumwcmTlO+/H+3RR9FPTFDduhXDPfcgfeELdRWeP/xDap/5DNqf/inV66/H7XYvUturVquXnKv1eCzsuqsVxzs2k0wUMRh0hFqdiKLASLFG4kyC9iYLk8kiyUIVt8WAINT3nLlcGa/NQIvNSD5ewrvz1a87IydGiM/GSStpyoKJnTfcgM2kR2fQIRcub59xDf+x8Hq9OBwOxsbGaGlp+ZmTPE3TODabhmgB/WwOXYsV8Sx9Go+Z2nwRz2yesM/CyHyetUEnq1evRlEUhoeHqQp5FLmGpog0L0OLXA7F0VGavv99svfdt+Tjer2+Iezk8/kaTCWHw0Emk2lQ3AuVGsNzeY7OpJnLVihMTWHa9hZCeTvrptP0NduwLGPWDhAOhwmFQni9Ltas6efIkSOsWrWKtu5+Dk5nmTZ1k23R87ePHqWro52BrjbWhZysDjrw2Yy4XVkEYa6+dlxQDL5U6UUpFkk9+CDVn/4U8/PPI2ga7NzJ3Z94A+Gu64nsPYzxX79B3/NPML/XRdPWrSQSiSu6vsAVzfc//fTTlORxele97qp23GKxGNPT00xOTjJ2apzalJXJSyTev4j4mYezAoEANpuN+fl5ADZu3LgiVcNrWAiXy4Xdbmd8fLyxaKQn0rz441OciGaRnUacZgmjXkdNVcmUZI4mChTvO8WtRh01Z22RAlU4U2Ly+BzudBVLhxM0Dd1UlvHjMWJrWwg4F97MPp+PW265hWQyyfHjx8kXZli3bt0laQiKqjGVLDIRLxCPpxAeP4TwG5/jJtHIUq+KxWLIssyaNWsWHeepM3O8PJ4g5LZgu2BGUVEUDNksVY8HSa+jya7DYzEwOp/nwaNh3rq5FecF9M1zPGvn2U3G3+XHaDRy5513Nt5ruiAgrrqNF/a+whM/OESwaYpgKES8IJNLFUnOpECVcVqMWKu1uu+TAKV0GZ1VIhCyQ6VC6cknKf7kJxiefRZUFXnXLswf+QjmO+/EfNG8gPOzn4WPfpTkZz5D6I1vJP8HfwC33778TbEEarUagiCsiEe+Z88ehmPDBLoDK5bNXymsViuZTIbR6Ch2zY6pQyKw+dKGptfw6qFpGsV4kXw0jyrXFTTNXjP2gP2K6LKhUIh8Ps/Y2BjBpiBTL0zVvef8Fp4bSTCfqwuP6ESBaLbM6HyBVc12BjqdzJ+cxybZYIkRivn5Aj/95mEykRwGi4TVamDz9Ut7951jNGQyGXT6KoVCBpPpCpKB554jeOBZWn/wr+Bd/DpBEBgbH2fD+vULqJpWq/XSkuSqin56GtuGDQv+bA/acYQc5GP5BR2kZqPK7tVBUmUVRdVwGAWeefg+7JX1rBpchcVioTBfYNXGVaRINUyEAVoCdjxBB/MTaWwB44LvMB3N42yx0druqv9hfp7aI49QeeABDPv3U+vogDvuwPz1r8OaNQ065jlY/umf0E6fJvXRj9JUq6H/6leJBgILEs1qtbpA1fBizM3NNcR0PJ6FK7m330tiKIFTENjU5uJ4OEMkXQRBQAA8NgNb2j1Uk0XMHjOuKwywL4Q9ZOfAngOkxBRWmw2v9WY0VaNWqmELXLNk+HmEJEn09PQQiUTIZDLLCmvUFLW+l120fl0oyV+sKsykStiyVQSdcD7JOwudx4Q8m4NUiXCqxNqzImk6nY5Vq1bR0dHNQ/cfoFQsccvrOq/oc6Q/9zn0W7bQdPfdix6TFZWXXklyeP8BrtsxwOs7zhfFvV4vyWQSl8tFLFvmoWMRJpMFjHodHqsBKRVDM+iYTpcYmS/Q4bXwhnUBmpahIiYSCdavXw/U186bbroJb2s3PzocZjJRxG7Ss6klSKBSY3x0nNPVMpFMiOPhDK9fG6B7wMvRPRK5uRJ2u5NqsUouXmTt7d3nu3maRuallyj+5Cfo9+zBND1NbcMGxDvvxPSFL2Du7MR19nyagI3bu+EzbyHyd3+H4f3vJ3/zzai/8zvU2tquSPNhfn5+Rd28cDjMqVOnkCSJ6enpxjp6NSCKIk888QThcBinown/xtXc8dbVK3ptpVJjfq7wMxc0Xmu86kSvUqnwR3/0R3z1q19lzZo17N+/n0ceeYQ//dM/5emnn+Yb3/gGu3btuprn+ksPnU5HT08Pc3NzTE5MktyX48xsBs1npuWCrpVBFPHbjeT0IhPzRU69ME3bbYs37WpNpVZV0esEhLMVEJ0gUJNVZGV5E1qPx8OuXbtIp9O88soriKLI+vXrFwkqDMVyvDSWYCpeoJYoUXj2BYzO1dB1I+MvTtLbbOPGXh8+mxFN0xgeHmZ+XiQ8k6OjU0V/QcV/JitzNJci6DIvSPLUkkx+Jok+VqYUOv8Z9TqRniYbI7E8L40luGvN+W7mzMzMgpmk97znPRgMBk6ePInB184/vzjOyUiWcq6CTvOSUYpkIxlKlSobBzsYGopTixURmszMFRVS5RwOs4TXqCc/FKFFmsf9gS9SmpykuHEj+te/HusXvoAYCHC5HqhqsZD/3d8l8Md/TOZTn8L/la9Q/Id/wHLHHfXHL0Mtm5mZaZgqXw4DAwNEo1EURVnkZ/Nqoapqg4J24403smfPHmwhG+5t7iXtJJZCtVoDwHCJKuY1nEc+lifySoT0RJpqodqgBOmkejGjeWNz3fZihbDZbFitVo4+dZT4RJzmwQDPjyeI5ysEnGYuvP3ylRonI1nsZgm33UBqKIVyq7LIE65UrFIpVjG7zZSzFQqFyyuYOZ1OnE4nuVyOiYkJrFbrZWfo0DSqv/d7iJ/+dN037yJUq1VOnhzl4FNF4lMTvPV9G5Y4yDKYnUWo1TBcJJIl6kR8gz7Sk+kFXb2qXCXg93NhyOFxuxkeHsZgMDDQP0ApXqJtZxvWASulUonx8XE6OzvR60Wuv72HR755iMREBl2nhCBANlZAU1S2+PLUfu+zCE8/XS8g3Xwz5o98BPFf/xVpBdYVmZYW+MlPkF55hdpHPoKpvR3+7/+Fs3OTlxMdKBaLyyoTuzpdNG9oJrw/TKDFhugTOXhiijXrN2Ix6Gi2GanEiyhVhY4bOzAuM3d0KZRKpTrldksnhgcMSLMSRpuBXDxLLV3DHrLjuQSDoFKpEYvmsNoMy6oYXsNri0AgQLFYXKRDUKoqPH08wtDpOIIAGzYG2Nnvb8QDuVyuMYaiahqqpiHKKiwxDyroRAQNBK1ewL0YZrOBt71rB6qqMjY2xiuv1D3eLlXkACicPEnTffdReOqpJR9/Yd8IB+4/g0PTc+SnI7S3edi8ts6SOKe86WwKcf/hMOFMiW6/Df3ZhVWZjyHaTDR5rdQUlbF4gfuP1AvWnotolOVyeQHF+uabbyZdrPJ/95wgLYt06w3Ix+NUYgVcVYXOnJl0NEy5I0ts3QAPHgvztk2trL45yMnnZ4iejqMz6ejYHOCmzQ5iX/0q6qOPYjt0iGowiHbLLRi+8hXsO3cuUMdcEoJA6e678X3oQ0T+5Atk3vEbPHrLbjb8f39Aa/vS1zeerzAcyxHNlskXS5hEDcWao8NrRVrCG/ocgsEgb33rW6lUKldNPBDq8+O5XI7+/n4KhQLR6CQ337Kdzq6VsZMef/AM00NxbnvLIAOrrm4x/WriVV+xjRs3MjIywu///u/zJ3/yJ0iSxJYtW3jjG9/Ir/zKr3DbbbfxqU99iq985StX83z/S6CpqYnERILH975Cxmig1br0Rmk3S2SteqYnUrhnJBhc+HiLw4Sn1UF2Io1jKgMaZCUBb5uTpiXojhfD5XJx4403ksvlOHLkCFD3KnE4HByeTvPo8QjydBbXXAklnML95AnKq1ZhO5VCnqtwqDlHLFvmdYM+srFpent7mZ+LEo/l63McF8QZM0Ud5UoVu7+eTGqaRmU4SeVMklw0iWlWj9piprg/jGldE6JJj/5swnsqkmV7t7fR1bt4/sRgMKCqGnM4+fsfHCCbU2lLlHGkKwhlBRmFhBES4SQnRlO0BfzEkxVqk1kMPjOyphGeSRFN5mjS4mzfAvbf+RrSpk2Yr3D2ZHR0lJ6eHnSShP6b32XfNx8i/+2n8BrtbNu+GVVVl5yd0TSNfKmCgrBiH8RQKMS2bduWVca9EmiaRjgcplarEQqF0Ov1uFwu7rrrLsLh8AIPx8tB9xr4OP6yIjuTZfSxUUrpErYW2wJqplyUSU+nyUVydN7aiX/wMknSBRAEAXPRjKgXOTMTZS6n4LcbufirsRn1FCsK4/N5gh0eEicT5CP5RbNRoVYn627q5NgL43RsaGHdppVXXO12e8MGYGJiAovFsrz9yeOPozt9Gt2jjy56KJVKkcvlaGtrx2SYo7SCZPMcZqYzTD94BKFrJ2uqcPHy6O3zkp3OEjsaw9XtApElAw6f34fX66U12EpqNIW7203LhhYMNgMTExMEg0FGR0fp6Ohgy/WtVMplXnpylPx8kVqthsmtZ/vhB9mYSCO94Q0Y/tt/g5YWrqREo2kaqVSqXinfvRvdoUOk/uqvCD/7LObbb79skBuNRheNAVwIUSfSvrMdnV7H6N5RXnnuZWxuKyEElLxMbr6E2WumY1cHvlVLzxiqikolU6FaqIIGgk7AaDeiSRrRWHSBNP3H/uJj/OBrP0DNqBQzRdrWtxHYHLhkAilJIpKkw7TMDPc1/MfAYrHQ29vL1NQUZrMZn8/HE4fD7P/JKSzJEpog8PRwAvGd67jpbKBcLBYbXXezpMNhkojbDejDebQLfCwBlHwVzHpqZj0e2/K/knO+wpqmMT4+zvj4OB0dHXiXKBYBZH//9xFuuomWG29c9Njk5CSqomGSjOiBiqJSq50XnPF4PExNTREbSzKdLtLXZEcnCqgFGTmcQz6eQLB6UQsyeqtEj9/GUCzHvokEd69ZuG5OTEwsYsgdncmQUSScmRTl4QpKoYreZ0Yw6vGVzegm58ifKZKuHKOwtp9Xpqys2+Rgx823cuLYJNlcEkPsGMY3f4jytm0YPvABLN/7HvYrVEbOZDLYbDYmZoo86XkDsduuI396hMc++30Gd/bw/l8/f+1KVYWnz8xxIpIlW5IxSTqy2Qxmq50zyWlCLjM39/vp9i9/DtVqdUXdv5UgmUySTqfx+Xx0d3fjdrvJ5XJMTk5SqVRWfJxgu5N0vIDn57yY9DOlxi+88ALXnx0oPYf169ezb98+/vRP/5QvfvGL/6USPbkoI4jCVTF/1Soaos6CIlSplIqYrUvTJy0miWSmsOS8gtWoZ/fODh6Ra8SG4uj1etx9Hu7Y3o5Jujz17xzsdjs7d+6kUChw9OhRZjIVTldcGKaLuMcyoBOojh/H6BJw3rYFKgrM5mjKlIkqKt9Np/j4nRswmSR23ti56PjRTJl4VcQinv8M8mSG0itRBJMexQ4mMU++2UX5dAJN1bBcH0QQBNxWA8OxHCNzObZ0eMjn84s6j9Wayp7TMe4/GiVdkBkIlzBPF9BUDTFXQcpVMdRqOJrMFASZpJjCrDNTKSqURwtoDhPOFjvWnR0QsHG8y0tHfwDvFSZ52WwWs9mMJEmcOT3Hw98+SnoWSpU1+B9JEYs8TVunc4FUck1ROR3NcXQ2w9BUFJfLRVd6lrUhJz2XWBTPvd9SAj0rgVKtK6YJosB8ap5qtUooFFpAD7ZarYiiyKZNm9i4ceOKj/3Lpmj1WqGSqzD+1Dg4TBAOAAEAAElEQVSVfAVPr2fRcL9kkXB3uclH80w+O4nJZWoYZV8OmqZRSpVwNjlJVWVy+Shesx6WqKraTXpSJZmcXEXSScjFxWuNTidy7zvXMbDBSm/v4vm8lcBms2Gz2SgWi0xMTGAymRoJR6kk88rL0xz7b/eTv+UL2P/hKKu3hth8fStWq4GZmRlMJlOj2/3+T2zHaFzZGvfEw2c48Ngo5TPTqK5b2fflF7jn/RsWDPzrDDo6dnWgqirzJ+YpakVCfYuFh7Zdv41SqsTU4Sl6tvRgGfTx9DPjSHqRTdtCzM7O0tvby8TEBHa7Hcmc4s0fGmA+VsJqs9I/EMRg+Nm8VWdnZxfQNCcm0xztvo1Qu58uTWNiYgJZXn6+7eJ5vqWgk3S039jOUHIIbUqjnC8jWSWsTVY8vR5cna6GdYKqqmgqiKJAPponOZIkPZmmmq9SK9XQNA0NjXwlj9llpndbL86u84UET4eHdW9bx+a1dcuZy1kyQD2wDwSvefj+vKC9vZ1MJsOJMyOcOZ7Gnirj6HKj1VRSM1mOHY9y41ml6wuFTPQ6kQ1tLh6cyWD3mqjN5tD5LQgGHWquipIqU+ly4myx4Zf0PHL/KRwuE9tv7FiyIHrOV1jTNCYnJ5mcnKS1tbVRWKopKiee2090KEHhi39B51SKviYbdpOELMuMj4/T3t5OU0BPNFJl9tQ8fQMe1g6eL0y53W6m51JkhCzNdhM6UaCWKlN8eZbafJHaVAmtJUD+uSms20PoXCaaHSZOR3Js7/Lispy/vy9m4pSqCsfDGZxmCcvJGvl4Bmf/eWsjwSJh7/FjjGQxpMuMT4zwcDbJqBChyWGhubmZtWtbCd21FctvfOxn+k7n5uZwOlp48F9fIhvL4+n0YQlZUEtw6plJ/ioa5d53r8bfEuKBo2GOhzO0OEwEnHXFUr/ZidVqpVJTmE2VuO9wmHvWB+hrfnUxy5mTc/iarZfs4GcyGRKJBB6PZ0EC7Xa7ed3rXocsy6xevTLaJtCY4/55x6vOSA4dOrTsZqDX6/nCF77Am9/85ld7+F8oVLIVZvfNkp5II4gC3n4vweuCVyHh0zCbzeglHZlMBofdjrBUN0QQEJYZrR0MOGi+dw0vHD5NR0c77R7LgoUkl61QLFaxWA3YL9Pls1qt3HDDDXznpTFiT5/CdyKBKeRHNGnYRocp7NiBSa8DvQ7BIlGL5PFP5YjZvIzMF9jY5lryuOFMiZqmw6QV65+6plIZToFeRO81o52aQNXrMPjd6CUz1aksxl4Peq8ZURAwSTqG5/Js6fAQDocX+BPKisrjJ2PsHY9TlhXaqiK6YzHQdIiaAGY9SsiIkKmg14ESdJMpxjFSJNQZwIYFVVYwdbuxr2tGtddnA+8/EuZNG0OLqBaXwszMDKtXr6ZWU3nqx6fIhLM093lJpTPkoyXSviZ6V5nYt28fGzZsIBBq5bETMQ5OpUBTMeh16ESBIzNpTkay3DrQxPZLKH/G4/FLVsBqNRVVVdGJIjq9SDFeJD2ZJjudpZQqkU6mkWsy/oAfV7uLvJrH0eZo0PZEUWysAcupvl7Dq0d6PE0+msfb772kaqGtxUZiKEFiKLHiRK8BAQRRxGqxUJNlqtUK1mUqu+VyCYPx0ve71WpYMsk7ejhMIlbg5jt6LtuRtlgsdHZ2Ui6XmZiYoFYTePGxKGOPHkUsGjB2djM/keLJ0/MMH4uw9VYvvb0dDUl1YNm1zGg0Luj4j40m2P/wCIJeoNmUpeYVSU6mefyHJ/nIZ3cuKEpIFonu27ux+qycef4M6bE0oiSiP0s1V2SFWrmG2W1mzV1ryJgqPPmD48THkiAIjJ2Z46a7mzl06BA+n4/Z2VlEUaS7u5OrNdZeLpcRRbFRkBkbTfCjf9hPNlZgn26U3e/ZxC139PHKK68wMTFBa2vrgs5kOBy+ovmX3ffuZmDTAAaDAb/fv+R3G57NUYwXEecKJIeT1Mo1TG4TJrcJXbOOZCoJGrSYW5DzMuN7xgk7wjSvbyawKUA6n8br9S7ydLyGXyw4nU6QTGQfnsQsy3U1EEEAtGUtpQBWBxyc7nQzLCt4Z/KQKoOsgllPqdtJsdPBbZ0eTrw0xcs/Po3ZZcLbZL0klU4QBDo7O+ns7GR6epqDBw/iaw5wMFLjwF89ghp6Pebncxw5dZiW9c3cssGLsVagr6+vsRa/+21rKdcUTHrdAk9dSZKIVwTyhQoDASeaplE+NY+SKCG1OxCELHL3GpT5IuVTcaw3tOKySIzEcozHC2xqr6+x8/Pzi6jss+kS8XyVVqCUqCD5LYuSQYPBQMVpwJ3WYfHYeC4yTbNfxyff+c4Vs4Euh3PjMc/vmSI9myWwuglRJ1DNlnE229EbDOSTOZ7ac4BnhueJGwL0NNka4nqFQgGfr97tN+p1dPttTCYKPHYyRpPDtEBzAbisrc/YaILHfnAcV5OND/zG9Ysez+fz9cTU6VxWQ6RYLGKz2a5a1/DnCa/6W79cxQ9g06ZLe3T8MkBTNSaemSByKIJoEEGA6RenmXl55mc6rsFmwGEzopRl9JKE0+Ekn89TKS30bimWZRxGEdMlPEU8VgMDfhPrW10LkjwAvVT35JNW2OGLZcvMZGT6RAcet4e8ViL11FNULGbsa85XQgRBQN9iQ0uWMabKHJ1JL8mhB6jUVARRaND/1IKMkimjc9Y3di2ToWo2IxkkRIuEVlFQMuevg0EvUqzWGpXAC4PivaMJ9k8mCTrNiIUatuNxTKkaVZ2K5jGhmfX1zcakxygLiAh09/bT1NxMPJ8kKeYwt9rRZnLkn52CaIHeJjuTiSIPH49QXqFH0NTUVKPbMD+XJxXJ4wjUkya9XsARsDM/nUUUrOzevZtyucw/3/8UL43OEXCacOmqdAR8eG1GepvsWI16nhmaYyJeWPY9l5J4vhCiWE/WaiWZ8afGOfH9E4zvGScyFCGRSGBz22gONqNWVKKHopz+yWlO/egUqbEUmqYRiUQIhepdjUJh+fO4hiuHWqt3jgx2w4rEViw+C4nhRN0DbwUQBAGT04RckHGaJUQRJJMZk8lENpNFvYCKlK/UcJklRLmGXtKjN195ActiNeDymK8o0DhH3xs9VeDkcyM4J47QvKUbd5ubph4Pvi43owdmCY9rC5K8S8HtdpNKpRr/Hx9OUM5V8IQckMlg8LhwtjmIT6cJz2YXvV5v1GPuNbP5g5vpv6cfb78Xi9eC2WPG3emm7+4+1rxzDT239lCURebG4gQG/bhCDmbPzGMyufH7/fj9ftavX4/BYGBqBV5TK0U4HF4gfjFyOk42ViC0tgkEOHUgDNTHAzo6OohEIszMzNS7appGoVgmmahQKq1M0fLcTHFzc/Oy360yXyT2whRzx+Yw+8x4B7xYfBYyxQypdAqfz4e/2Y/JYcIetOMd8KI365l6fooz958hOV0X0CmVSj/7BbqGFWFsNMGBl6dWNGt7JXCYDbT2uEjb9cRORMhMZSg3WVmzumnZvcpq1HPvxhAb1jZT2thMZLWH2Dov8xuaMK5v4s71QXb0+LDYjBisBoxWCYtl5UWBtrY2tmzZwsHpDE/9/ZN4D4cJDgzQZDHgixSZe3qCR/ZNE2zrWHCOoihgMegXJHnnYLQ6qFYqiIKAVqqhzBUR3SYENKRSCcnrRfSaqM0VUMs1REEABEoXxBMXijedg6yo9ZnFqoomK1jcdsqlMlwUW9ldDkrFEj6Hh7b2dmoqPPjgg1flN1SpVFBVFZPJRGQqhc6oRzw7P2kymSlVytjcZrSqQHfPRnI6Nx6L1EjyioUCZlPdGupCtHkszOXKjMwttmyZm5trKEUvhaZmG8FuN6s2LKScF4tFxsbGKJVKdHd3L0vVPbeO6fV6arXaFV2PXwSseMf+8z//cz71qU8tosVdDtlslr/927/lDy8ht/+LjMJcgfREGmeHE1msJ3pWnUBiOEFwSxDDJXjjl4I9YKdzwMepPWdIWg14bUbsDgeVcoVsJovDbqcgK0jFGk0BCy0Dy89UXApms7Sk4fhyCKdL5JMlmpMlRJ8FhyIihcNENm8mGQ7j9Xgwn1XpFEQBjDpsyQqRTJlEoUKTfXFCumiZFM7+O5v4CdksVZMZm1Q3HUZgwYJb/5OwKMiZTBR4eTyJz2bEWJExHIkgZMpoDgOiWaJWk9Hr659dkFU0o+7s+6qEQiFMRhOxWIxKtUJbaxtqWqawbxbrthCdTVbOxHIcnEyys/fSs1HVapVqtdr47dhsRiSTjkpRxuoyIRkMFOaKWBxmdKKC0Wikq6eXZ6IitmINo6hRVaA6kkKQRKR2Jz6bkeFYlRPhLJ2+xVSFyyV5UE/ycrMZJp+ZJBfJITpEVLeKx+HBZF78PSmyQi6cY+jBIQJbAuTcZYoGNzPlMi+PJchI9TnJDq/lkoPV13B5yEWZcraMybnwe5BrKrFsGZfFgO0CxoDRaSQzkaGSqzRmlzRNIx/NkxpLkZvNoSkaJo8JT68HZ5uzoZ7otxqw6lTmc2WaHWYcTgf5fL4u1qEzUFM1Or1WqrEE7nbXikV3LkTv5fzglkG1WuPMwSgOuYhZKZHv7ETI57FZrBgsEja3lVMHZrn5zr5LmoufgyRJC2iL57qPmqoh5HII/f1oioYoiuiWMgOnXh1ub28HH/gGfIseiyVjkALJoIIR5kaTqIqGt8OFz2/HYvYyOjqK1+uls7MTSZIYGRmhq6trRWq6y2F+fn7RbKPNYUSnF0nOZKkVFJz+8yMAgiDQ1taGLMtMTk4yPTXPiZeKpKZP4mi2ce8HN9F2GZ+6pboOFyIxnGDu5RmMooCjvz53lYgnUFUVr9eLuMQ6IQgCZrcZo91IYiSBElPwvslLgcKKE/prePWYmc7ww3/YTyFeZPK2Lt72/qtTsJdlmaGhId60s5e9TX6GT8aYT8xz081d7Bi4tJCF0yzx1k2tRDJl9h49g8VqJ9TspctnxX52DvPm3b34W2w4XebL3rcXo1RVmA3X8B8ZQe1wk3NJmPU1rB0O3JNpUpMlxuMFBgMrowM77HbG42eL0aLQiGfkVApJ05DcHpSCXC8yn1tmhPOJT61WW3IGWH82qdR0AoIootVU7HY72VwOh/P8uWmqhtlkIlfIIRkl7rrzDqInXuLHP/4xd91117IJz0owPT3dmPu32AxoFySnkkGilC2hE/SIeh1pWUMxWfA7zv9u83MZLFMyuUIEQ58XU5/n7GWqJ85HZzJsanMvSKAvJx5lsxkX3KflcplIJILZbF6RC8C52NFms5HP5xvWGL8sWHE09u1vf5u2tjZ+8zd/k6effhpFWb6TIcsyTzzxBB/96Edpb2/nO9/5zlU52Z9HqDW1HhjoRWRF5bnnnmdoeIh8Jo9aUy/72sx0hsRwgsx0ZsHzNTSKlhQtVlBTZWLZCpWagtFkxGyxMhlLkoxl6TBKBFc7cDdfmRfbq0VVURFkBU1WEQw65uJx5vsHCF63lWAwSLFYZGZmptHhEQw6xHKNmqpSXeZ6mCQdnE1MVFVFtBnQeS0oqXqlSlcoIFvMiHodar6KaNaj85xfOCo1FYdJolAoNDxZCuUq9+8fYS6RQs1nyB6YxlvSyIfsYDUgFeozaJqmQaUGskLFZcJqMSPUKqCB1+elvaMduSozPj6GbBPQaiqlQzF05Ro+m5G9Y0lm05euko2NjS1YbOwOI6u3t1HJVIgNJ8nOFMlniqzb2Y7RpEMURVKFKsmijNdmIJ3JYJpTKOydobB3Fnmm3mlwWiQmEoUlO6XxeLxBjVgO54Q+krNJFKeCyWmiqblpySQP6nM5rg4XNYuep39ygvt+MsnhtJGTBRMHYjUeOBrmO/um+Ne9E+yfSJJZYVfgGhZD0zRYogFeVVRyxQrP732Z2ZlZlLOdN0EQ6hXSs6+pVWpMPD3BqR+eYualGUqpEpV8hfipOGfuO8OZB84gWSVMXhMzJya5rsuHx2IgnC4Sz1WoCUbmCzViiTQDTVZaHUbkvEzTmqZFipuvJfK5CsVMGRxGhDvvxO5yYbVYyBcK5HN5THYDpUyFXLZ8+YMtgf5BP1avmfnxFLVsgbLeQmo6S6DPQ8sSCW2tVlvQuSqVSkxPTzM1NcXU1BTVapX29nba29vZtn01b/rwdiwtRjo3tvCG967HYq4X/lpbWzlz5gx2ux2TyUR3dzeTk5MUi8VX9TkURWlQjy7Eddta2XRXL2aHgbZNfm57Q/+i10qSREdHB2On88wci2FymYgNJ9j71Ohl3/dSar6FuQKTz0yCAM42J6lkivh8HJfLhb/Jv2SSdyFEvQhukKoS0Zei5FP5y57PNfzsKBQqVApVauUaqfjVuebJZJLR0VEGBwdp9rp48/YOPvn+LQSM45x59sfE56KXPYYgCARdZvpcIjv7/KxvdTWSPAC9XmT9xiAdnVceC+XKMvHZJBWTGevuW/D7/ej0OuKJOBW1ipYqkS2vfD9r8bkolcsoqoZo0iOF7CjJErX5BLLJjKaBkqogheyIRv3ZPVxofJ6JiYnGrL6macTjccbGxsgnIhioUTDr0blMKKkygihgNBnrnT2gXFVIxvLkJD0RvYhclWlrcnHvvffS0tLCfffdx9jY2BVfIzgfV5wrIvevbUY06sglzq9bmqqSnMzgarXhaLEB5y00CoUC0kyF6mQGtVSjfGxuATvLbtKTKcmUaytjSl2MarXKxMQEyWSSrq6uS4pKXYhzibXVaiWf/+VbZ1bc0Tt69Cjf/va3+dKXvsTf//3fYzQaWbt2bUOq9pzS1/j4OMePH0eWZdatW8fXv/513ve+972Wn+E/FRa/BUuThexMFme7E7/dzoGnD2BptWDcaWT79u1Lvi41nmL25Vny0TyaoiHoBCSPRM+uHvQ+PQcOHGDtzrWs6lnF/oeGGJ9Ok84K1AQQNfDJIl6LwMA2N65B11WVnL0U9KJQLw+IAqVckVypRHDTRgSxPifo9fnQVJVUKkUqmcKhmDD6nYiC0JAXvhjtHgt2s0S1ZGwEK6Z+D4VEidJMBn2+TCnYQi1ZQs1VMa9ratA6FVVDVlTanHqy6SwTExMATKRl4hWBNZ0taFNZinM1PN1ukukSmRYNR6yIqVhFzmSRbGaqzVbyDgNBqwGv01gXdbHXxSG6e7qZmppiYnKCluZmbCmF8ol5vFuDDM3lOD6bIeRautIcj8fxeDyLaE133juI02Pm9OEIgiBgb9K47a5+xifqC7B29l9NlpEkCU2pgVZfRDlrjSEi1J+n1TeJC5HNZi+ptlktVDn96Gnik3GaB5tX3KmfSRY5FM0SKxTxZPWs72hn802tjWCvUlOYz1V48GiYoMvM69cGaPMs78N4DUtDb9KjM+qQS/ICVoDVqGdVyMXs6RIvvvgiTpeTO3bfgVyU0Zv06E16VEVl6vkpIq9EsIfsi2abFFkhM5Ehn84j+kU8BQ/6CtzY52cmXWI2VURWVKrpDDes70MoZYmdLOLoduAdWL4SvFwV+meB0SQh6gUqJhtCqF71F0QRm92GpmpEp+KgauilKxNGOodgq5Pb3rGG5354jKRiRytA6/omXvf2tUtSEScnJ9Hr9Q26pdlsprW1ddnu+dbt7QysdlGpVPD7zxdejEYjBoOBbDaLw+E4O6vXzczMDOVy+ZKm7kthenp6SdsVg0HPve9cB+9cR6VSIZ1OA4u7HbOzs/h8fkbFBJJBhyaAXHn1FCZFVph5eYZKtoLgE5iLzeH2uBeIOaWLMslCBVUFSS/itxmxXCygI4K7201yOEnZUKazr/NVn9M1rAy9fT5ufPNqErEcQ5Mv8Ld/e5Suri62bdt2RZ2gffv2EQwGqVarGI1GVq1ateBxs0GH2aDnzPgsL730EuvXr6elpWVJ1ekLsRKxoCuFUdIRT8/jam9HZ67vV2azGbPZTD4zT7qSJx6LonUuFsVaCus7m/npwTFShSo+uxHjKh9KrkrlydPUNAfySIyUvojLZqUDSOQr+KwGus+yc6LRaGP9EQQBr9fbKBZHlAj7JpJ0DHgovhymlixhcJmYS2UIZ6vkEmWE+SLldgdz4RKSWeWVySSbOzzcdtttHDlyhCeffJJEIsF11123os8DdXpjJpNZEFesXtfC2C1dHH1qnHw0j86kJ5fM4+/0svN1PURFBVLnC+GlUgmzJFERBdCLaNW6UNPFuHBmcymBvXypyrNPj5PPVlizoYWBXg8zMzPo9Xo6OjpW/JmABQ0Cm8225BhKeCbD048MUauqbL+ti/6fYyuFpbDiXVkQBN73vvfxvve9j0OHDvGTn/yEvXv38tJLL5FIJIC6UeSqVav43Oc+x5ve9CY2b978mp34zwv0Rj0dN3Yw8cwE6bE0rY5WxtrGoKvuY7YUMlMZxh4bo1at1cUtDDoqhQr3f/t+uo9007SziV337KoHzz642W9h7XCCsaMxyiWZVDrByfGDfOh3fgtzs5kX975IqDV01Rc/OGsuniwSyZapyAqxbJl5TcNu0ZMcmcRgN+Byuha8RhBFPF4vbrdG4sQMM3YZf9m2aMD2HDxWA6ua7bw8VqFQKGCz2ZCCdqzbQyQOTKLPymgtVvLFAilHBblaZWPVS75QIJopoNPryEzF2bl9KxaLBU3T2B+fQdLLSJpGbiiBYNZjtRvp0IlMAQmjDmOphlZTSOpA7zDTZDMScJkRRQG9Xt8QbTAYDHR3dTEzM0s0GsVtdeIbFzF2ufDZFts7nIOqqszPzzM4OLjoM+v1Ijtv6WbnLfXF+/Tp0wuEH5xmCZfZwGx0nsGuIGq/pe4ZJIlIbXWKRrpUZX3ItcCP8HIYOj0PQo3kkUmy01m6NnctqKzLiko0U2Y6WSJXltHrBIJOMyG3mXRR5sBkfT6v2WfCIkvIpxMYW2yc03836nW0ui2omsbYXJ7/86Nj3LO9g61rXx21+L8q9EY93n4vM3tnsPoXUnMFQaCvr4+jR4/S3dUNAhTmC7i73Zi9ZjJTGeaOzeFsdyJZFt6T0UiU+fg8gZYAuckcq1atwtxnZur5KYrjKYJuE91d9WDm4dMHGHomxfo16zH0GKi0VC4pMHWx59PPilKpRDw+h+SRyc+pqIrWmAWBejCg5FTW3NJJPp8km403rD8uhYuV/a7b1s6qWpSJf3sa4+f+CIOpTHNLvZsnyzLz8/ONuY10Os2mTZuuaNbQ4XAwNTXFieOzDB9PkIjmEHUiDr+IpsywcfOqxvFaW1uJx+OLaOiXQjabxWazXfacjEYj1erimStVrYsybbq+jbHj86RmsnhCdjoHrczMzBAKhZYMnKrV6oLE7Rzy+QrP/fAklVMRXO0WvBYvBvf5YkW6KHM6kiWcqe8pglD/Lq1GPW0eM4MBByZJR7FQxGK2IOpFrC1W5k/PU0wUsXgXFo6mJlOcOBxh+64u3O5r1M6fFTqdyC139AJw6pSJn/zkJ0xOTnLXXXet+BiyLLNv3z4SiQSf+tSnlixcaJpGMplszIe2t7dz4sSJyyaTV8sT9tw5hMNhhoeHqWpxxFAb1XAOqcmKoBfrqp56ifZNIdZ2NHH48GFsNhs9PZcWlQo1efEIReL5Ck6LhGQzYLupncqRPWTKFY4Lk+SEMtuEVqo1lVg6z8YmPbHZqUbxZznK4bpWJ6eiWeYNOnxbWqicqgtxzWfL1ObzWDURnVWPPlHCmCrja3fw3OOnGVnfyt3rAmzYsAG3282ePXtIJpPceuutK7qek5OTdHR0LPibTidyz9vX0tXv4/SRKMVcFZsnwNrNAVoCNoqRPFBC1TSKZ2M7fZ8VNVtBLdUwrvKgc53fM4pVBadZWqAKn0gkFhWxHn7wDMceHkZUNIb2T3Pne3q5blPfqxKbmZ+fb3RPrVYrudziGcGHvn+cqcNRBFEgFcvT+gc3NdgZvwh4VeXXTZs2/ZcQWlkpnO1OVr99NfloHlEnYt5lZsPmDZw4cYJSqbRgw9Y0jejRKNVSdYHh69DoEFkhy/FDx3n3De9esIHamm3Ymm20bmtFqSocOHiA7/7VYR596VHe9a530dXVxfz8PAaDYdHAajqd5uWXXyaRSKAoypJJx1KoKSonI1mOTKeZTpWo1BRy2SzJVJpYRU+8WsORKNPaEqpz0JeAmqvgavFSHvTQ5RR5/pk99PT00NXVtShwWB10ciycIZ7McO4jSCE7as2J9/4ox9q7ycmjlE5laD7pIDIMeqMJTVZZvzVEk8HVmN9IFKpMJor47EbkWAElWUIfqFeEXBYJk2QnXaySLsqoGkhKlWaXGbf1vFm0yWwim81ikOpiGKJOR3t7O3NzMeLxONVcnrYJJ+6toYZa1oZWJ9FsmaFojnCmxExkjmafh/CpGP0tdkJO85KD21BXGryQtmWSdAz4jIxGRMqygskiYV53voqUKclUSiWETJ5arXlBcHupzsrEeJJiMkNbQaKpu2lBklesKByYTBJOlxAFAaOkQ6lqxLIpjs6mqdQ0bEYdVr2KXmdB59BTm8xSnclhHlwY4IuCQLvTxKGT8zx2YIb+nsWJ8DVcGp5eD7GjsSWD2/a2dmxWGy63i5nRGcyKGd9AnVKTGEqgadqiJA/gzNAZIuEIDocDf6efxJkEa961hsG3DJIcTRI/HacQLaAqKmpRZU6aI+VPcffb72Y6PM34+DgtLS1YLIu7tOVyuVEZ/VkQi8Uol8uYzWZCoRC77ijzXGmayKk5nEEHJpuBcqFKdjaHO+Tghlu7aWtzo6oq4XAYRVEIBALLBi82m41cLofDcX6mxRaepN+roev1cujQIc4tT5Ik0dTUhF6vb9iVXGkwUS7X2Pd0gkNPDaNHj2SR0BQNuSwz5rcSnilwz5u3Np7v8/koFAqMjY0tuVZejHg8vqI5lOUwOztLKBRCp9Pxoc/cQDScxee34vFaG1Qop9O5KFifn59fUqFzbDjBxMtTWBSNwZ0LWQWpQpWXxhKkizJuqwHv2W61pkG+LHMqkiVbqrGt20OhWMDv99fVTEs1EqcSnPjRKTp3tuPscDZsFmanM+TSJeLzhWuJ3lXGqlWr6OjoYNeuXczMzCwK9C/EM0+McOT5Kcx2AzpnjOnpaTo7OxkeHmbbtm2Lni8IAh/96EeZnJxkcHAQg8GA1+vFZrM1fO6ulkrkOVRrKlPJAvOZIpFIBL2gsqmvjWw2yztv38RIyk5k7wyWSA6dBiWjDv1aPzfv7KAj4KEj4CebzXLkyBGsVmvdE3eJ2TFBEBj0SZTcBkbn8nR4rZgNOgxyAn2fh4pToJqrkc4VSY2GWR108obr+7GZJI4fP35Jif82t4W7VrfwyIkos24juk1NjJ8E42gNm9lA2SxS9drQdCIhuwGvDNXxPNnqDA9rGhaDnq72dt785jfz6KOPct9993HXXXctWA8vhKZppDI59AbjknGFTieyflOQ9ZuCjedPTExQrVbpb3FycLZIqlBFK5fr4yQmsN3WhSYriBfEBKqmkSlWuKnP16B6njvexWvg3HQGo6TH0+MgMpHGYHC8qnvl3LE1TSOcKXM8oXB6Ik1YH6bDa6G3yYZJL5JPFjFYJfQmiXKuQrkoL5nonTgWxWjSv+qZ9NcK/zF8v/8CkMx1TyuA69qvA+qm8kNDQwwPDzck/8upMtnpLLbmeuJRkRVmUkWefXEvrYFmnK1OosNR+tJ9mC/atHSSDp2kYyY6g9vtZu/evfT19eH1emlrqy9WIyMjdHZ2Nn6QNpuNsbExotEora2tK0r0KjWFJ07OcXAqiabUUPMpapUirT4fm3sGmUmX2Xdmjnybj+RMCYO+gLfpfICnqRpKuoxWlMn3unAHHOy+rh2fzcDY2BhPPPEEnZ2dC6piXT4rN/X6+O5zcyQLVTxWA+VSifTEKE06BTknYo4VMRUFHK0WjBYb8UKFkN2EdDpGatrIUGqI0PUhEppGvlqjxWWiHCugCQLCBd0yk6SjxWmmxXn++qaSSURh4fV22C4achagqbkZo8lEZGiK0b3H6etzIAgCpyMZRuZyjM0XKFRrSIJKpaqh5WXGEnH2TSTp9FnZ3lUfIL8Yzc3NzM7OLvhbi1RmW1+A47MZrCYJl1lC1TQS+SqKpnH7una2BOt2DM3NzQ1KRSwWW1ahauOWELlhE9EXZ/C2nK+cKorGwakk06kiLQ4z+guFKDQDJyNZ4rkKG9tdyNUK5nPXzqqnfGwOnVVCtBvRuYyNRdlgkti8s42xZImhWI6tnYurutVqjZpSt3gwGq8tRxfC1myjdXsrk89Mosoq1iZrQ4FTEAU8Xg/ldBmzYkYICZgCJpSqQnY6i8m9sLsfSZcZGp8kGonR3t5OqVjC3GYmOZqklCjhbHdi8Vlo2dBCJVehXCxT6C3ganGxZcsWCoVCI9iPRCJkMplFQX6lUrliyuE5lMtl5ubmgLoi5Ln7d3Jyks1b+vH7m3j6oSFmT8fJzxeRzHq6tgS4+Q0DtHfU111RFGltbW0owlarVQKBwKIuo9PpZHZ2FofDgaqqxONxdPv3I7S0UIpGaW9vx2g01uXgL0A6nV6SHnk5PPyjExx7ahx3swOdRWgkw6qikZhMc+jxMF7fGW648TwDxGq10t7ezujoKO3t7csmreFwuKF8uxKcC2rOQVHqc8rnglWHw4TjAgVng8FAV1cX6XSa8fFxAoFAgzmiKMqSwVVXq5Nxt41Qz8JZKUXRODSVJlOSCbjMXBi7CQLYzRIWo57ZdJGTYR3tFoFivMjEKxGisxnisRzjs8fwnozR1eVh0529+Fb52Lajnf5B/yX9s6YmU6SSRdasC6C/5uO5YgiCwHvf+16gTnEbHh6mt7d3UeB95uQcz//kFGiQmEpjaTLwR3/0eRyOS48EnJsjn5qaalA7PR4PTqeTiYkJfD5fIwGRZZnvfve7jI6Okk6n2b1794pZTNWaypGZNM+emGIqnkXU6XDYHQiixPGXZ8hP5/n0TbexSZI43O3h1Ol5lJpCe6uTTb0++i/wdnM4HGzatIl8Ps+xY8cwmUz09vYuSoJCTV78QQN2i5lT4TS5fAFPoUp5oI2Oji5ePHiYjCxw92AruwebsRr1qGqdx3i5pGVtyInZoOPlsQSPnohRyFdpKsqkg1ZqenCY9bS4LA37J13RiDwRJ+8wsd9no9NrweVy8Za3vIUnn3ySH//4x+zevXvBWjKXKzMcy3EinGU2Oo/P58UxOcG6VmfDW3ApnFtjKpUKIZ+PwYCDJ45N0d98PpEU9OKCmAzqfsrVXIrwqRh5/w3YbLZlReV61jTz8nCCyEgSd6+H1lbXJa/XcojFYrh9fh4/GePQdIpiVUG0+Dg8k+bgVIqA08Tda1rYdHMXLz54BqVaY+1NHUsapE+MJ3nw/72C3qjnw79/089V0elVR1aXk4QWBAGTybRgcPO/Ivr7+5mZmeHIkSOsX78etaai1lR0hvrGmirK1FSN5tZOAn4XgiagyuolhVzcbje33norwWCQTZs2Nb4Lh8OBzWZjYmICl8uFx+NBJ+oINgdJJ9Ps3LnzsuerqBpPnorx5NFJdOU0douRUKBlAUc65LYw0OFmQtKRG0mjhrMIyTJ2h+HsYJkKdgO5Pjdap5PbBppoOhs89Pb20tPTw/j4OE8++SRtbW309/cjiiLrW8zsMxaYicWZUDWCbhvWdJFMtZnVpg6SrTnOhKeQkbHroKvLzaY2N6n5GD63j+RwkmK8SKqvHmAIgJIoIa5ADt7hcNYpExcoVyEKmM0mSsViQ0kU6oGiob+b2ZPjHN13CNq7GI8X6PJZaXGaaPVYiITDdHec7+QWKjWGY3nC6RJ3rWlhbXBhEHmOVnVus8jn83hdDu7p8tLusXJkOk2qWK13yjxmNrS5WRNwIIoCO3bsYGZmhueff57Vq1dfcoahqclG7kgMnVG34HcZy5UJp8s0200LkzzqAiA1RUWnE+rKjOb6Aq0WZeTpHLVoATVbQecyIYXsmDc2Nyp1kkHCapQ5OpNmY5trkRqnXi8iyyo66VrwtRRaNrYg6kVmXpohfCCMoBfQ6XUggoiI2WOmfUc7oetDhKNhzJIZTdUWCaZEsyUKio7Wjk5cjjrNTxAF0EBVzq815+b8rFi5veP2xtqSy+UalKpAIECxWGR0dJS2tjYMBgNPPPEEhw8fxufzcc8991xWCOgc5ubmKJVKC8zOz+EcNVAQBNo73Hzw49sIz2TI5SvYbEZCrUsr6wmC0GBRRCIRKpUKLS0tmEymBl1sdna2EUT4fD6IxdCtX4/nbKAzOTm5INFTVfVV7WPhmQyn981gb7bi8FspFYvIVbluFaMT8He7iZya48Bzk2y5vmtBQqfX6+nt7WVychK3272o4l6tVlFV9Yrosh6PpzFqAYvN1ZeDy+XC5XI1OqaXmkus5av4vWZsF3Wh53IV5vMVfHYjy11KnSjgNBsYDqdo6/JzZv80Y7MZKmYJY9ABFZWIKBAfiZPLVrj1vetxd7svmeQB2GwGajX1WpL3M8BqtdLZ2cmZM2cWJTbZbJlqQaZllZ98ooBW0xBYGYPD6/Vy+vTpBX/T6XR0d3czNzdHJpOhra0NSZIol8vkcjlmZ2dXfN+nc0W+/dwJTsZKNLntbOprx3D2PlBUjflMkZS/jweOz3HP+gC717Rwy6omaqq2gEJ4MWw2Gxs3bqRYLHLixAkkSaKvrw9JkhpK2+NnTrBpYBXtZiuxqovxfB6xoxN/Tze6aoGg3UXkSIx/ORhBL4k4vbB1Y9uKVLN7/DYkncDxyRR6WUPXbEP0WjBLImqlsMBKS7RI2DxOquNxxro8RDJlgi4zBoOBu+++m3379vHQQw+xfft2BgbX8OzQHIdnMuTKMshlXK762hPJlhk7XMBrM3Bjr49Nba5lz1PT6urFO3q8nByfZSZXo01fw3JRQVdRNSKZOr3zfbduoMVQ5emnnyYYDBIKhZak8u7e3YPXb6GQq9I/6MN3iaRq7/MTHNwzjqpqrN/Zzq7buxuJdLlc5kRK4MWxOM0OM22e8/esompMJgo8cDTMO7e10d3vR1EU2tpdS76P02XG2WTDYNJjWYJN85+JV53odXZ2rmjjM5lM3HTTTXz+859fUaLxy4jW1lYsFgv79+9n3ap1GKwGKrkKFq+FZocRm1FPyufk+PHj6Kt67rr7rkvaMtxxxx2Uy2WefPJJVFVdQPM8N9QfmYrw1LP7SU3KRCN6jOVeXnpwiNXXhXCHlm7R5/N5Hnn5GI+cStHR7KKje2DpVr0oMBhwoBNEps0S09E8+WKNNQ4zOklHyaKn4jLi81u5daCJtaGFAZkgCHR3dxMIBDh06BDf/va3aW5uZv369bxuczeK1c9QvMzITIbY8QJZfQC9z4ZBcmLIZTEKNTa1ugi6zegFre4DaJFw97rJTGSYfnoCscuB5jChFmVE0+VVAnV6HTq9jkq5gtF0fgORDAYq1SpKTakH2GdhclhobQ4yolQ4Mh7D42+iw+vHoNeRTqVwulx12WFdPaC2GvX0NtmYTZV45HgUk15Hb9Py1c5IJEpfX31O4vouDxvbXOQrNQQ463u28LfX2tpKMBjkxIkTjI6M0GY2Y1hixkdT65L7F9P6wqkSGhrSEoFQpiRTqak4zRLxbJGg04OmqFRGUqiZMoJBh95nQTDrqYwkAbBsPz/X02Q3MZ0sMpmoexBeCFEUsV6B6fx/NchFGUEQ0Bl05ApVstE8qqwiGXUEB3z4Bn341/gR9fVuVjQSpVApYNEWBtmrAw66fVb2PjtCeHqSjs4OapXaAsPvpeByuchkMshnRYHOwWKx0N3dzfT0NBaLhXXr1vHSSy/hcrkum+RVKhVisRiaptHU1LTIEuAcwuHwIqpYcJnkbjkEAgHS6TTHjx+nWq3i8/kIhUIEg0Ha2toazyuPjqK/8cbG/0VRRFVVZFnGaDReskt+KZw+HqOcKeM5KyRjtljIZrL1a3n2J+xosZOPVDl4YIgbdqxddIyOjo4GnfXCazUzM3PFlE2LxUI4HMbhcFCtVikWaldEeQoGg9RqNU6ePIlRlsHng4ssD6q5Kmgs8n+MZkt1SvFlZoqtRj2xpMz0UJLpmQyy3YDXaqjPZFZKuCwSWcnIcCRL60vTbO10XdZr0uO1LlmFv4YrgyRJDAwMMDw83IhrADp7vHg7XEROzaETRUxtKn/2//tjOjo6sNvtfOADH1j2mIIgNH5vF6OpqYlKpcLIyAitra1cf/31PPfcc3z2s5+9bPwZi8WIzc3xcrhKuGpkU58Ps2FhLKATBVrcVvxOC2PzeR46FuHtW9pwmiX0KxQXtlgsrFmzhsnJSR5//HEEQaC/v5/e3l4mJibo6+2hD6iEw+ie/3eS3/pLXhgtkU06yR9LYlTr51FWNeZQmT6aZf2tJe7c3t7wnlsO4XQZKVuhCRF9m73RJVMNdnL5HHb7+f1W5zZiny0zOznP7NoWgmcF5ARBYNu2bXg8Hp565lkeOjFH0dJCk8NIk00ik67iddVjFS/1BGguV+ahYxGqNZXt3YsTMZvNRiKRIJUq8eN/24fHZKRplZupbIVKuojNKCEKdd9Auabitxu5ud/fiBXf8IY3MDQ0xH333cfrtm7FtnEjF1aH9DqRrZsvz2Q4fXKOp7/7/2fvv6Mku8/zTvxzY9WtHLu6q3P39OSAwSAnEiBAgBRJiRQlmhIlW5bkQNo+a+1a8v50pFVaypL2rKlEy/J6tVaOpkibOSATGZgcOufuyrlu3fz7o7prpqdnBgMQlBj0nDMHmOmuqls3fL9veN7nOYvjuIiiwNN/c45QROW2O0e6658jcmq1SirUNWm3yzp2voWgyqgjEcZSQWZyDc6u1XnodQRY4nGNn/zf7kMQX78j+3eNN53o/df/+l/5rd/6LVZWVvjhH/5h9uzpBqUzMzP86Z/+KaOjo/zYj/0Ys7Oz/PEf/zEPPfQQX/jCF3jwwQffsoP/dkIikeDw4cOcPHWS9Hia3Es5fBEfkiIR8svceccd1Cs1vJKHHtRZy631BkS3oZsOa9U2pZZJuWnyYs6l+twMmVgQXWmQjWkEfTLtis5zn1niwskNaraBFgkg4Ocrn77IqedWeOwHDzNx62U+9crKCjMzM8iyTE1MMTYeZSK9MwlxdQunbiBIIlLMjyyLHMxGGIxrrCcDnFuvU+6P0Bf2kQ0oHB2KsacvROSK9r5pmuTz+Z6wgaZp3HXXXb2O1JkzZ1BVlbGxAO8/PsIl2+MrC8tIPpPURApFEtmbupVCtcF0vsmlXAPB7nBwJI3jeEiSQHQsivFEGcFz8QYiXX+smzCchm7FslqpoKo7TapDoRC1Wm1HhV8QBERZQon2E5Fs4sEtFdCOSXumhFSXqOsWol9GHY2hjEQQ/TKDcY35QpNnZguMJQM7hFRUVaXT6ZDLlbAtmXKp1QtOVFkkIe9MiDzXw2gY2HrXLF72yUyMTRCQJNrf933Ub7uN5G/8BkJwJ63WddwdSStAy3RQrzgWvd1GVX3d5NdyQABFFHBcD8cTcKoGTkVHSmo4ZaPbRQooyH1BzNUGvkoHecsCQ5VFHM/7B7uFN4jGRoO5r86zMF0ib9iUNBFrsnsPSh5UWwbFL80wOlNkz0MTxMfj9A/0UztYY+4rc4T6Lz/DPkXCp0jcc889PPPMM4yPjbNyYYX+yX6CmesHwNtCItcKqgRBYGRkhOJGkYuvreLWokwNHsG13a40/lUoFou0Wi18Ph/Dw8O73tMwbC6czVEptQlHFfqyypvqojWbzR2m6NFolNtu69Lp8/k8hUKBTmenHYO8tISwtYdtz6V96lOfYv/e/Tz8yMNYlvWmBCDaTbOrSHzFehIOh3YEYf6QSiPfJuCPXFeEJZPJUKvVeoIIpVLpTXthbX+Xp584j08O0xzudkhvFrIsE4lESMzM0LnnHoSf+Rl8H/pQLxC7HhtFt9zXFY5ybId8IY9oKxRzNXRFJLI1CyMIdBkjrkcsoLIeNFicLnEg1yQ88Ma9Hf8Bbw7bicz27GY8HiedDvID/+w2Lp7N4dMUbjmR5a/+qsmFCxe45ZZbXleRt7+/n83Na1ss+Hw+9uzZw/PPX2BtqcPBvY/Saqk9k+srsf3smqZJJpMhPjRJcXWJ4YS6I8nbtq/Zfi4lUWAiHWIm1+iJq90IruuSz+d7c/WyLDM4OMjk5CSGYTAzM4Ou6zu+U/vsWZRQiGfn2pz89EUipktyMIJ4RdE13rZobTR5+W/O4zoe33Pf2A2fGdN2ESwPXG8HFVKUJBRZ6QnKAQiKhCzKeKZDo7XbEmpqaoq5hsgLz89wV9ZH0K+S28gRJ0DnUqlbcExpSHE/A1GNYsPgyekCfWHfrngxlUoxMzNDblXhwpMrBEIaj+3v54G7RrmwWWe9qmO7HkNxP/v7I0ymQwSvKDgKgsC+ffu6llv/8T+Sf+01tN/9XcInTtzwulyNzbUaRsske6ibpK2fy3Pp3CrRhIGqqrTEEDW9zlTGj1MzaH99FbuigyDg1vvQbskQD6qcW69xz2Tyhh1eYIeg3rcS3nSit76+jmmazM7O7jIX/IVf+AXuu+8+dF3nE5/4BD/3cz/HiRMn+MVf/MXvuETPqBtUFiqsnC9g6BbhuJ/BA31ER6MoV4lPBAIBbr/9dl545gV8WR+VuQq+iA9Zk7HaFgf7D5J8W5KD7zlIuVHmhRdeYGRkhEAsxamVKmfXaxQaBq7nIUsiVTHMaytVwjWXZ5eaJIMqhwYilJ5Y5NxrGwRHIuwJdi0LPM9FHYyyuVTl839xhu8Li+Qa6+RyOYaHh7n//vsp6Q4vPLdEOrzzuO18i/YrmzgVHUQRZSRM4MQAok8mqilEtSiqLDEU0/jQbcP4FQlRFLBtu3efQDeR6e/vv+aCPzQ0xNDQEKurq3zpS1/i+C3HcS+5TFRWECbTjA/FWCy2ON9wMfETFgUkUaTp+nh5qUq17XB8JIYkCfjSAbzVOm7d2DIqvfnrGY1GqdVru+7pUDC0S+a3YzpUOzaJ/mA3ILVcNr52Ea0MTkBB8Ms4DZPWC2uom00Cd2QR/TLZmMZqRWe53GYsGcRyXRRRpL+/n5dffhlVVUmlBwheI/jyPI92oU1lvkJhtkS52MJod60XZJ+M4zTZe2IS4a++hPiZP6Zxxx14//bfEv3xHwehG3AKgrCDrgfdZOzK+KxUKlOulAgGQ9i+GCIilm0jSxKiCJ7R/UxhexPaimPFgIJdbOPpu6XZTecNXIjvcjQ3m1z83DRnpkusqAKiTySypUYm0J2jLes2BcehPFPC6jgcePcU8fE4I8dGqFyosHB6gbEjO5kXkUiEd7zjHQiWgBSUULIKyyvLDA4OXlNB8fWwMV/mC39+jvWFCq1mgr/9L6e4dGuJxz58lEDUj2mabG5u4nke6XT6ut2+Vsvkr//gFRZP5fBsF1dw2XP7MB/8J+nXVTdrt9uUSqUe3SkUCu3o1l2J7Y7Y2bNnmZ2dpa+vj4jfj7S+DlvdMUEQWLu0Rv1knXquzqtrr+Ib8mH32TdUHb0WZFXq+hteAUEUURW1xx6wTQdBFghHg4iitUPu+0pEo1E0TWN6ehpRFEkmB/n604tYhk1/NsK+gzcn+W2aJpIkkeoLIqK9aZpR8KGH4IknaP3Mz+D8/u/j/53fQTx48LqFte1C0S64Hq7r4rgupmkwPz+PSxip6CCnUmwvMR501xlBQBBA9MvobQu78+ZtIP4Bbx5jY2NsbGywublJf38/A9kIA9nLbKEPfvCD/Pmf/znve9/7OH/+fG+W7VrdjqGhIU6ePHlN3zPHcfncZy5w5olF2pU2luXxF+vPceC+Ed73wcMoikShUCCXy20Visd6RZmvnN+kYzk75snMlTrGhSKe7aCMxPDvTyLIIpIoENEUTq1UOT4S29FN8zyPUqlEvd71sBVFkXQ6fc3j3bYeM02Txx9/nJMnT7Jv3z7MCxe4OHULp784S8yBwER8VyFLDCiExmMIyzVOf3GWvZNJDtyAxSBLAp64Zcd0VVHbr/mp1y4Lym2vQ8FQiEq5BOzsiHUsh4UGHJwaJ+hXaZRqSOdqtIpFPMcFF0RNxjeVwH84TSrsYzbX4Ox6bVeiJ4pid/ZXNUmNJlBUmYHBCMOJwE3bLTmOg6ZpDP+n/0Tt6afp/ORP0jhyhPQnPoESv75Xoud56LpOu92mWN7EFaBeaCGIYDsOjVaJv/zLx9F1nczRBxD8WURBwKx1sCsdlNEoTrmDtd7Af6yPgCpT162uKN7foYfsW4k3nej93u/9Hj/1Uz91TQf5RCLBT/zET/Cbv/mb/Lt/9+9IJpP803/6T/mN3/iNb+RYv+VQminxyudnmJ8rU3ZdXFFAcTzSzy6z71CGY++e6omubEOWZe552z288vwrRDIRhKKApVtoMY3hu4dJ7k2iaAr9wX4ymQxfe22ar37lIrYaoj8ZZTwd7PnRZbRuwj2VGcJxPUpNg698fYn604sEMiEGAj4QIBAMYNs2zWadQEJk9sI6n/ubNu/9J3fvsMAo5Ku0LZsh/+UH0TUd9Nc2cao68mAYz3Iw5ypIET/a4XTv91IhH6WmwfpmDtmzet+1r6/vDVXCh4aGuO222wg6Qb76+Fc5WN+gkjpAy7A5s1YDAQauEFHRVImO5TBbbJKO+BhNBgjFNbzpElapjahKeIYDNxnMCKKI5ts9lyfJEpIoYpkmiqri2S51y8GUBRRRQJFEWjN5pA0DdTyFcGX1MO5iLteQkhraoTSqJFBumfzh84ukQ34cz0MSBCb7giyvFjl46BCpa9CMrLbF+svrLL22wcZmg03HoSEIOPKWQEfdwWu22ShcJPmUQv/U7SR+/17k3/xV9N/5HWo///MIhw9T1su0N9oEzcufIRkmtXob1ZERBAFdb2PoBp4LihTERaWhm8TDATRZwtla8FzLAVFA3PK/cg0HQZJ2fH/oxmfKTXZWv9thGzYLjy9ydrrEsuyRCPp2bTA+RaJPkbBsl9V6B5arqI8vciQZQEto7HnHHua+MsfsS7MMHxzGH+rObLq2i1WxMGoGA7cNMHr/KIIosLa2huu6PfXFK6EoCrq+uwJsd2y++BdnWJgrkxiPkfQlKRQbvPzUHEg2J97ZTR6v1b27Gq++sMrCqxskx2L4giqdlsHCS+ucPNTHPQ+M7/hdwzAoFAo9qlcgELjhzNi1MDk5SavVwrZtVp99lqwoImzNqplVk73SXnKdEiXdxlnKM1iKsOAuMPnI5DW7ldfD+J4kL20ptfmv8DP0+X006nVUVaGWa5EYjDA0HEVV5RuqbaqqiqqqbG40+OyfPENlqSsFrgQV7vveA7zt4T2ve0x+v5+5uTmOHTv2pjqmrutimiatVgvL87B++ZdxzpzB+PEfx5yaovzhf0WtWoPiVcfuGrTabeqivUOoWaAbGIqihCwrICpEAhr9oo+S5eJ5W908ywFZ7K0trumgqsquedR/wN8dBgYGKJfLLC8v75qxDQaD/PiP/zgAR48epdlscvr0aaLR6K7RH1VVaTQau1hMAKde2+C1L80SCPtJHu1HQCC3XODVL1zCk9vsPxAmnU5z+PBO2rPtuFzKNYlfMatml9q0X1rHc1wEVUI/uYmgCPj3dQtQqZCP1UqbzVqHiGj2mAFXe9ndDFRV5dChQ2SzWRYWFrCee47X+o4glDoEJncnedsQRIHgYJjGfIWTp9ZvmOglgz7EuB8vIOM2zJ6/8DYi4cuCcm7dwA3IiHE/B8aHdl2zuUKTfL3DWCqI53nUX1tD27CRBsK9/d1pmHTOFRDDPnwTMVJhH9O5Jps1Hcv1MCwHURB653xoROOf/vQDSJLwhqjT20yzaDRKtVrF2r8f63/8D6r/+T8j3XorhR/6Ifz/5J/soHNuo9Vq8Sd/8ifU63UURWV07CGqizUEQWD4aIq9h2W+9rUO4+PjDE5OUFrrsjvEoIIYVLDXGniWi7IviSAImLaLvBXjfbviTSd6pVJphxz81Wi1WhQKhd7f+/v7d6h9fbujtlzj2f9+nrNrNayoSjTgQxEFDNtlsWlQfnkV23K48wcP44/tFMYQBIHb7r6NS5cuIY6K7B/dj6RIO6oxruvx7FyRFzYdIul+VKtJp7SBrfQjb81EhEIhWs0m0KUe9EW6qnsF3aJj2fhqbQajAVzXplqt0m51r5cvFcZvJ0gn0juOy7RdhKuMtz3dwq6bSOlAtxvkk7tdm1Ibz3VptlqYhoFuudQNl0AoSzZ57RnAN4KQFGIg3U+83eACHutnZ6mZPoYSu+fa/IqEKFgslVuMJgOE/QquX6K51sQLyDhrDbSwes35s2vBp/mp1Wr4HAdRksCDlmnTtESKhRqerCC0bapNg4rnITYMxpMajYubROKh3UmOLCKGVaylGuZwmAuFJqu5Bv5yB82ngGljyfB4SGG52cF6cYk7h/JErjAPNsoG689tMHOxRl4RaSkiPkkkqIr46K53niLQifi4ZLkE2gapF3McrqY48Eu/Q9TYwP+xj+FMTOD/0X/P6eV5TNPsDravr7HvwGGG+xLUdYtMxA+eRywWJ51Ok2sYVPMNXM8jFfIhiAJSzIcYVnHybcSID8Ev49kuTq6JMhhBSl5Oxu3tgPwfVDVvCvWVOsszRVYVgfg1krwrocgimYifjWaH5YUyw4tVMkczpPalkH0ykdciLJxcIOgPoqjdubBQX4jsbVkyRzI9e42hoSFc12V1dRVRFHd4p/X19fHqq6/uCnJWZkqsL1aJjUUJaAqu6xLURPSYwrmXVnjwPQeID1y/8nol1hYrCJKIb2te0x/0geCxvlTd5WXn8/nIZrPf0ByEpmkUi0WGh4dJGAbG4CAbW4FF7XyNRr6NduvRbtDneiiahHSpSPpAmthY7KY/Z8/eFAN7k6yc3qR/X2pHUhIOR8itFHANgaN3j6Cq3edjZGSE5eXla0rZN5tNAoEAixdKlOZrDBxIIykSpZUaL3xumr0H+3Z0VbZh2zamaWLbNvV6nWKxeNOCOVejUqkQDAYxDANFUfD7/ShvexvS179O50/+BOWn/zWVIx8meuRhZE2hWq1Sr9eRDB2f6GIJCsnrUEX1VpvU0CiHRvoZrhi88so6TcMm7JfxTBdJFRH9Ms2Oja9lM3RL+obU43/ANx+JRAKfz8fs7CyTk5PXTWC2xUsqlQonT56kr69vh8rj9Z7n86+t41keka3rbOgdlJBILW+wcLbE93/wrmv7PDouluP2hFcAnJqBp1soI93kyTId7Hwb9kFH12m2WpQrHeYWBY5P9H9DtiXQPTf1ep2DBw9ytmWyImZQ9Ba2F0Xh+uu6oEgEFYmlC0XKbzd76plXYywVYKA/TCnVILxYRwwpl1k20BOUazeayFWb5niUwf4QUwMxahWXarXaa9bkah08QJFECksbaDWQklovyQOQwipuy8RcqKCORQmoEmfXavzB1xexazpGy8TFQ4v6aOZWeUQOMp52uyJYFW4agiCwubmJz+dDURRUVSUYDNL38z8PP/VTOP/236J86EOov/mbRO+/f8dri8UiBw4cIJfLcezYMf7wD/+In/3/fZxIJMrgYJRGs44gOLz97W9no6ZzurBEvWMRSQYI3JHFWqkjBhR8e7sK0qWmwW2jiR3U0m83vOkjv/322/nN3/xN3ve+93HkyJEdPzt9+jS//du/zR133NH7twsXLtyUute3AzzPY+HlNS4uVRAyQQau2LQUWSTok9mUBE6f2WTiSD/Dd1/7e+/bt4/V1VXOXzrP0aNHd/zsxcUyT0wXiAfUrYc8iOe6bOZymPl8zycqGNpJKXRtD1mWkFWZ5WKDSqlEQhN3iCSsLpcplspYptVT/4Rusnh1Ki7IEqIqdql4qkyn06FTqiNFwapWCQaDhMNhGh0LsWMT0L5x03ZBEHBMB6tWRbYtxu68i3UxQm1uE9VukUwmd9E/A4pEvW3RMR1qukWhbZGbK2H3BwgsVfEEl0TYTyLo2zWQfS1EI1EqlQpKIMxmvUNDt7BdF0GQcHWDYMOhFVKo4mHXOzjtDoEm+BPiNR8qUZNplztMz5Xo5Fpkl+pQa0PSj6TKKA5ojovoOqyndE5HYnzg+BCxgIpe0bnwwiVWV12KySiRgMKgX7much105zlzjQ7ech2+NM/R9+4j/vTT1P/ojxD++YfZDN3BmYSffL2IoihIosStdz3ASwslVqttIv4Q0bCEbjkYtoNpWQzEgiSDl/n+vsk47ZIOHlgbLQRA7g+hHc/sKFoUmybpsI/x61T0PNejsdGgU+l057sUkWA6SCAd+K5T7PU8j8LFAvmWiaMKN3WvKrKIKIrkTIf8uTypA92EIjYWIzoSZfCOQRYvLBIMBklkEoQHw9cUYBFFkZGRESzLYmlpCZ/Px8DAQJfqew2hhFbLxLE98BwajQaiKBIKBnFSMlQ6FPIlDMG4Jr3paoRjflzLwXU9BKDdaqG3DSynTbFY7HnZfTPgzs3hjY8zNjZGrVZj7tU5GjZstjwyEY2GYbPcMMioCnpFf0OJniSLvOcfHeVTHZuNi0X8ER9a1I/ruLSKbQzDZO89g9z9wFjvNbIsEw6HqVQqxK+iKOXzeSYmJihtnMMf03qJY6w/xMZ0gelLSzhu9zVXFlZlWe4FTK1Wi3379r3p/djzvOtSY/0f+Qjq+74P/4d/mdr//svEPvYjLLXbzM7M4uFx4q4HOZfTKTUN4gEfV8b2tuOxWe8wmE5yMBtBjVkMLFZYquhUXQ9/00AajFBsm1j1DpMhlb13Dv1DR+8thmnabKw3CIfVm+7CBINBRkdHmZ6eZnJy8obPajweJx6Pk8/nefXVVxkaGqKvr49UKkWhUNglztSsGsg+kVqthmPb+P0aiWQSWgqy0LVsymQyO8YqoBvPiAJcyRYWFKk7f6Xb2IJDu1xHSgp0CgX8mkY8kaDitBgbGSYev7E1xM0gmUxSLncFytjMI04kSGbitNot7JpFNBpFuQ7jSfHLdBoGhu1c9/19ssStI3E+u9HAblmw2kBOaYjBbX9KD9EEc6WBMxzHHI5wfCSOIomkUikWFhaIRLoedLrtIIsCpmkiWh6C5SEmdzOhxKCC27Iw2hbnik2WZ4sItQ5Dho3mdMc5rICKEQjytekWP5Ad4/jIzRX8Gi0T03Tw+2UkSbqmTyexGNk/+AOar71G+6MfZX1wkOTv/A5CIsH09DSJRIIf+ZEf4dd+7df48pe/TCIRZ3gk3tNYCIfDPSuq/oifvf1hXluu4EuK+IYiqEOXC2XrVZ2gT+bIGxQB+1bDm945f/u3f5sHH3yQ48ePc/fdd/fEWGZnZ3nuueeIRCL81m/9FtCVMH3iiSf44Ac/+NYc9d8z2oU2c+fytAIy2eDuyqQgQDLsp9KwmHl1nYHj/ded7dhWrnrxxRc5ceIEkiSxUmnzzGyRiF/ZUckRRJGBgQEcx2FjYwPP84hEIlTK5d4iJ/tFbMOgVakgqSqmHCCciBC8Yl5QsDyyE2lWN1ZJpVO9ik7YLyNttaq3q2CGaKP3SdjnSpADVVSIZBMEjg8iX8G1bho2QZ9MQP3Gg7FIJEJruYWTy2NoGv54nJDtI5FIEguIFPJ5BEEkmUx2uxSA43Z9219aKrO8VEVebyIYDmHHRSrqWE2LfCZAMRlgOB28boVsG67n0XQVNterIIoEVZmQJyHUDZyWhVjW8Y/FiFQNrJiK5hcoWS6t9Tr9qkgq5OPK5qij26y1DIzVBn2rDQzXwxuKER+K9X7P1DvEV12ECwWWZJkv+xQ+cCzL0rMrnDqdY9kvkAr78N1EYKOpEgPxAJs1nbPLVQJPLHL0+w8S+dEfpXrnnUiP/RvuuWjwZL9K7LbbuPvuuwmFfYwHTHK4NFG6YjKCwEg8gOIaeJKCcGXRVRbxHUjhP5BCCqoIARklE+xupleg1ja5/UBmV9LiuR6l6RL583nqq3VcayuZ8EAJKERHo/Qd7iM2Gnvd7/udAqNuUFqqURDZIWT0eohqChXbILdcY6zY7olTCKJAZDDC0cGj5HI5bNG+ocomdKmaY2NjdDodFhcX8Txvl0G6bds4bgNPMGk1FTKZyxthq9xmpC/MxL4xOnaH+fl5RkZGrhv8ua7L0ITGy2GBhVeX8Ud8uB3ITCR5+zuPMDDwjTMEbgT74sWeEEs0GiU7mmXx4ia20WTDNBElmWRQA5cdhbGbRf9AmA/989t59flVzr2wQrvWQRQFhg71sedIknBcx7ZNZPlykSyRSLC4uEg4HO6dt+1ZKIBIMkhhroLreIiSQKvSQQtrjE8MMTR0/aDKMAzC4fCbmse8GVhti9J8k86D72NDnKH9yb+mPyaQP7yX0cNH2DuURNPavDKfY7PuIglCd57bdfEcj3hQ5c6JBPGgCkGVA3cPI7+8zsZ6Hb1lIfY5BIs6I5kQtz+8h76bnEvchtk06dS6VC01qO5i23y3Q9ct/vIPXmH1XB5/xM87/9ERjhy7RrB9DWzbC2wrZF69ZlyNvr4+PH+E5y8ssvHiLIosIVo6w5NWb+2rVCrga1Mr1hkdvjxH7HkenUaH8Vv7mZycZHNzk2q1uqN44ZMlMhE/M/kmiaCKZVk0/CZmWsJZKiKJIpGJNMHbhpAi3Tiu3DJ3xV3fCBKJBPPz8wBo+RzS8TB4Qjfmcj1qjTpWrUY4EtllF+FtCaxIgoBuOhSbBh4QDyg7Zg5vGY5RbBo870FArRGodKCob9F8PNBkvKk0SzGX9x7KcOwKz7nR0VEWFxeZmJjAJ0s4nke1WiWZTtJQ6ngdByG4s9PqdRyEgMyFYpPCTInsTI3BoI9YNt61snI9nJqBtFajoZf4UmSNoE/e4UV4JUzL4dyZHKdfWiM3W+oqZEoC8dEgd7/Nz/4DaZRrqI+Gjh8n9Nxz5P/gD6jecw+5976Xg7/xG8hbifOjjz7KJz/5Sfr6+nYUjDc2NnoJpCAIPHwgg2W7nN+oI4kCIb+M43rUdYuYpvLwwQwjNzlX+K2KNx2VHz16lDNnzvAf/sN/4Itf/CIvvfQS0L1xPvrRj/LTP/3TvYfO7/fz2muvvTVH/C0Ao2HQqBuIAfW6XRVVFnH9EtWKjtW2bjjEv63I+eKLL3Lk2DGemi7QNu1dUvTbkCSJoaEhLMtibXWVhcVFQuEwhXwexzIJJgIIiIQjQRodi/WqTsgnIwCbNZ1OzcAbiVCWEzSLTaLVKuNjY4wkgiT8ArOrOTKh7kKiaRqZOyexBtPYZR1BkVCyYeQrDJk9r/tQ3DWe2EGTeLNIJBLMzczhlUq0wxECgQApV0YSBQRJYSCbxTJNSqUiHpBMJGgYDookUJ+pE8m1kZsW+ZCMlwzgGS6+xRqq7aG3LJZdF6k/QvQ6c3uu67Fa0ck1TPyqjN/2EHMthHIHUbeRGya26hHKt7FXG0gxmczRARiOYJ8vspRv4rhelwIpbHWs8i1qIZlEvg2yhO4XifvlnckgHoHRBM2FIomVJrMDdS44AnMvr7EiQSLkv6kkbxuSKNAf1dhw21w8nyeSDbDh2+DSuUvkP3QfjS8s8I6VFVKzM2imyfnz5/nUf/8U7/6ed3PP4VE6djcQUwSXRc3lTMGioVuENaVL0yy20Y5m0I5dX3Z+tdImHlDZ1x/GNO0ePc113K433EvriIpIeCC84xkxmyblmTK1xRqjbxul7/AbC+i+XeGYDrpuY4ugyTffzVQlCUsUMA37csJ8FTKZDJVKhbW1tZsy2fb7/YyNjTE7O4vruszNzZFMJqnVasiyzOE79rE60+HVJxfZtGr4gip6uY3fFbj1bWPIfpkQIYLBIMvLy4TDYRKJRE/YYJv6L4oi+/Zneej7G6zO2RTXa/QNRbnz7ePXpCG+1XBmZ/E9/HDv76n9KSoLFe4a0phrtHFNg/6WTmAoTWRo9/GYpk27bREO+a6rupZIBHj43Xt54OEJGg0DURSJRn2Iosjy8jKLi4s9w+htjI6OsrCwwMTEBJZlYVlWL3i+95FJCstV1s/nEUUBURY5/s7Jnnn8ldhYr3PpXB7HcZF9OpNTGTRNu67oyw3PlePsmuHchtW2mPvSHKXpEqIqYgQU1vY+SEpucvtXPocmSbBnD0ZpFXvpLHe97VHyjQ6W46EpEj5XZ99wBuWKNS46HOVw1Ef8hTU8TSF5qI/MeIz0VJJgJrir499sGliWu8usuF1qUzhXoDRTwmyYeJ6HGlCJ74mTPpAmnP0H1U6A6QsFFl/dIDIQprZe58Un5m860YPus7ytyLntvXgtlJoGT80Umc03aRo2ohClVqyxul5kg/OMhmB/QiSTSvDu772dv1x/keJMlWg2jCBAbaOJltA4fk93xqy/vx9d15mdnWV4eLjnS5uWO7xQrRJw2/h9KvFEHOGhBE65g+e4SDE/4hX7TqlpcPtYYocH3TeCWCxGpVLBabfJbqwQneqnPdPG74UQRKHbZfKgXq93fXxDYXxa1++z3TQJHkpxdq3K+YtFKusNPM8jnA5w5HA/d+1JEfLJyJLIwwcyRPwKr/YFKa43EGsGkudhCwJezE8iE2AsN8vekIV0BdtGFMVeJzUd8tFqthjsjyJqKvJAEHO+iuIP9+igbsfGa1vo41FyG3Vi8zVcQSAwFrtcBJNATgUQJZvgegPjUpEX+0PsSYd22UJVajqf+fMzLL6yDq5HMK6h+BUcy2Hj1RL//dwLTN05xPt+4AihayTf1WqVzRMnGH72WVK/8ivUTpxA+PVfJ/GudxGPx/noRz+KoihoV1jAXK0AG/LJvO+WLAezEc6u1Sg2TTRF4LaROPv6I/RHdxaDOh2bZ742T/9QhMNHX5+t8q2Ab6j9ks1me1277yYIW6pfXIPOdCU8z70hve5KbCtyfubxF7jYCDCeid3UcaiqSq1W4/z584wMDzM0NUWBTZaeXqJRauOLqjQ6NgvFFvVCC2+hSsAvc+HZZS6eziH2Bwj3SaTOzHPbngH2pnzkWkFiifCOBUEdiaKOXLt9XW1bhP0KezNvTVAWCoVwQy6+ZpFqMMVIQCMgiAzFNRZLLZIhH35VpX9ggE7HYHa9hO24hESNVK6NCPiTAaS0nxYeoaEwQsdGaFgEqia22GRdU4j45WsqxOXqHXL1DkGfjFo3kFYaCB0bzy/j+iUEyYe3J4aJjV43GfBElNNF3LCClAgglXTW2zZy1iUmiziVDtWQgquJyAUdKxMA0+nRILfhOk5XISvhQ24YdFYanJupUTMsUKWbovFdjW6FSmG2Vqbw376IFPCQPIn77r6Pxr84RvlkmQHZovLv/h0b0SjawADRSLQnxw9dP6K9w314SoNTq1U828Vf7qAMR/Htu74M9XpVx/PgkYMZYj4ZWRZ7yd7myU3WXlgjmAkiB1U26jrrGzV00yXslxmMB+gbj9Eutll8chFZk0lMJt7w9/92Q9dTausvb2ik2dt+A7jBmhOPx1FVlYWFhRt6obquy9xsmcXZEvnNPIl0ENubZnRskLGxsZ559yM/cJhQVOPCK2tUSg3GhxPcct8oR++9PF8mCN2AZmVlhQsXLjA8PEwqldoxI1YsFjl26zj3v/2bn9htQ9M0dF1HWlxE3HNZxCS5N0m72EY5kyfm84PPjxSSkCYlqnqV/mh3czcMm6e/Os/Z51cwmwbhdJBb7hvlzntHrjtvpKoyyeTObXd4eJiLFy+yurq6oyMhCAJ9fX3kcjna7fYOoYqJySQ/8M/v4NypDUzDpn8wyok7d1Mx52aKfOb/e5XqeqNLrQ7ItN7p8K73HWdtbe2mEr1OrcPS+TwbSzXanRZH7xjH7dttoVGaKVGaKSGlJWzXZu+De1l/eR29HUT/sX+LNvM0lY99jJXDh4lNTTGeDjKevvz5hby9I8nbhtkwGbhlgP3ftx8tcX1jZIBatYPj7Ez0GusN5r88TzPfJJAO4B8MAQJCx2bz1CaV2Qrj7xgnsec7f315PciKiKiIdBoGru3hewOsgisxNjbG+vo6hmHs8p8sNg3+9rU1littslGNwa1rpUdkfGaVmelLLIWTyLdMcCjTj0+WeM8/Ps6Tn5umuFTFtmz6phIcvH+UsghfObmGLEskoiph1c9LL72Eqqpks1nu2DfCQktmrarTFw0hbq13cnp3hybf6KCp0i7f328EstylIFZfe42o53D87ft5cvEkTs1A3u4mCxCJRsCDRrNBvdAgYMnoiogU9fHUpy7g22gR8bqsJf1iiafOF8m9Y4L33zVCQO0me/fsSXFsOMZsvslqtY1huWiKxGBcY09fiOKmxuzsDMlEfAfNNRKJsLKywkBIJShD0xEICALakT483cZebYLEliiSgDqVYDmqIi1XcWsGwYkYgWs8t4IsIiU0wgWdpZUaK1NtRq+gArc7Fn/7J6dYfHGN1FgMX/gqIZn+EHpV5+KTS3iuxw/+yPHe+mDbNq+9eo7VRYOJPUPEBwaI/+7v0rpwgeZHP8rGb/0W57//+3nsH//jHeyFq5XTt+GTJQ5loxzKRl/XrH5pvszJx+eJ9Ae/OxK9bTSbTVZWVoDupnWtE/mdBH/cTyIVZHGzhhP1diRE29BNB1V3Se8P44vcnEeRLMuomTEqK9NkwzL+6LUXnNpGify5ZeymSXKwjxP7j+JLhAiFQiwtLREYCjJy3wibpzZpr7Zotg3sqkHMcAmHFULZAIJr4pUM3I0ajZCf9v4UsT7Y16ewkgoyX2gw2RfuLYzXQ9u0yTc63L8nRTp8815MN4IgCAg+gaSxSi4w3Ksg3zraVapaq+qUmyYI3bh2ciiDbljos9Uut9wnokZ89A1FWKzqWLKAMBhGmq/iGTbBhkW12qEe1XZ19Zodm816B78idpO8pa6cspvQwHAQ2ibuUBgv7MNrevgUcCMBXL+CkG/jqSLSaBSx2Ca3VieQjeA/kKKES3CmDKqEbrkEVJnIVV1ex3GRRBFBFvF0D/9Gg4ViBzfuI6q9uQ3X0DuUcxvkah3GVk36U1GmHjqMVHK441134N7hsvrcKvV//XMof/hb/MDJk9gHDsDk5SH07YVvXyaMVTO4eLFAqz9I6nAKYWtQ2/M8bNfDcT0aHZtKyySqKTx2pJ9D2e59vJ3kGQ2DzZOb+GI+xIDCS0tllkotQECRBDZqOvOFFvv6wxzORqit1Nh4dYPYWKwnHvKdCtkvowUU1HzXd+xmO7i65eBzPLSg8rrUzGAwyNDQEHNzc4yPj+/q0LRaJp/5s9NMv7SC3ugAHn6/H19KZu/efizLYnFxkb6+PgKBAG/7vgPc/cgkK/MrjO0b65q7Nxo7vOzi8ThHjhzBtm2Wl5dxnMtzJ7Zt026337Q4yJtFLBYjn8vRv7wMWzMbAKIsMnL/CMl9SfSSjiB16a9qSO3RWVVV5bmv5TjztQXUsA9fUKG0UuMrf3IK07BvSv1yG4IgMDTUVcGLRCK9JBq6BcClpSUSicSu4GN4NMbw69Can/jsNPWNJgMH+hAlgcJ8hVcfX+aBdxy6KXG04mKVL/7ZKZbmK5iih2s5XHqlyIl7R7nn/Qd23GsbZzaod+pkg1nqlkBFskntS7H52ia1tRap9/8AoYce4sD/9Ru4C/Nw++2Q6HYgW83WNal+jY0GODBy38jrJnkA8aSGaVy+t8yWycLjC+hVHWUkwvlck/WVKh6QDvvYOxDCrRosPLGAL+ojmP7uFnbZf7CPWx/ew6ln55HTHqMH3jzFN5vN7lLkdFyPL53fZKXaZqovjIhHsVjENAw0TePggQMEl5bIZId4+vwKZr3Ih952C/sP9DG1N8XGRoOZhVUKnsZzJ9epns/h6SauKCCmNAYOD/C2ew8xERMplUpoPoXHDg/wt6+tMZtvMJII7hK3sl2XXM3Aclze+U2g6SUSCcovv4zS18exAxlOH+qj+OoGaUlECl/RpRK682MaPtbW8jRGNazZIpm1Nlom2Ju7Uzo2vvUG008ucmY4yp3jl4utQZ/MseEYx4Zju45jW+RpZmaGW265BUEQaBk2LcMmEO/jtRee4YHDh3n8UoGYpqKGfQTvHcbeaGKX2iCJKJkgXlKjNFfEl2/hajLJkP+6xcVgKkJzroiZb7FR6+xI9E69ss7yqxukxuP4QtfuoGoxjaQgMPvCGuduzXLLLVlWV1ep1WrMnDa5+PQKF2MFfH6Z/Qf7CB44QPDxxznzm7/JiY9/nOLsLJlf/EXELVpssVi8prLrlXg9XYDxPQluf3QP/ddhmzi2y8ULedZXamiawsGjmTekOPrNwDeU6L300kv89E//NM8880xvWF8URe6//35+/dd/vWdS+50GLa4xdayf2aUK+bpOfzSwo3PnuB6lqs6QLDJ12+BNy3HbjstyWWdyZBC9XcUwjN5gsuM45HM5ygs5tHmDmO1D9gfxLjURAiKl/gZ99xxjbHycRqNBIdlk8OEs60s6+WeX8IkeqaMJtOEIwVCw18nyXA+n0qF5rshpDwbeGedgqEnHVLmwVmE0HSF0jcDR9TzKTZNy2+DEaJwH9r611LpWq0W2tUJh8giN9QbhbBi/InHXeIJy26TUsnA9j7BPxieLPHF2k0DLQlAEPMNBGYmQimrULYdS0yAaUmEsirRUQyrqSDEfjYy1M9HzYLPewXY9Qi5IK13pci+oIDRNsFzcgTBuf4iGYSPjsCcTptR2aVkOwb4AQq4FhoN87xD5ukFsKMJoNoozW0S0PXTPw/UgE/HvojF4dEt2ml/DrDYQG1BvGkhxH6k32M1zXZdCLk+pXOZyoN5PKhpiNbfKkK8PXBg4PkAgEeDk/zyJ/OAHceNhgl/4W+ovv0LwYx+lqaoElSCtfAu9ojMa8jH4wUNsJHxcLLY5P1ekbbq0TRvTdnE9j4AqMZEKMpaKEgsovURxm7ZZW6qhV3SSU0kubjaYLzRJh/07aL8tw+bCRp1YQGGgP0RjtUFjrUH0Ol3l7xSoIZW+qST9S1VmDIvYTdqC1HWLIddjYCJBIPX6gYqiKExOTjI/P8/g4CB+/5b9guvyZ//v15n9+jqJkSgDe7vKvLbpsH5hk//5317jR/6XexgbGyOXy5HP58lms9iCTUkvoWx2jzcSieySW4duMWtiYoJcLketVmNoaIiVlZXX3Xy/GZBlGS+XQ2y3ex562xAEgVAmtMseZ5vOOjeb5+yzi4QyISJb5zucDFBcrPLqE4vcdvcIwTcw5xMOh4lEIqyuroKb4Owr66xNl3BdD18MJg62Sb8rg/wGqfH1QgtfTEOUumtNMKVRXq9Sr3Ze55Vd+4wn/uYsMwtlwqMxUn4Fx/MolHW+/rU5EgMhDr9tvCeF3mq2SPel8Wt+inqbxeU1Hjg6hdW2KJwrUFupISZV0r/6H+DcOaxf+WXcO+7E98EP0tbbpNOXVaAd06G2XEMNqoy9Y4zk3pszhw9oKoEr8sHqQpXGegNtNMaz80UKDaNLyxNgudSi3DS4d08Ka7VBebb8XZ/oSZLI93z/IV44+VcMjwzxmf/xpxRLizz66KOvO3N3LSQSCVRV7SlyrpTbLBRbZDSR3OYGAhCPxpGFAILYFZVyXZegX2ViqJ+S53Ly7HnCWtc03fTaPH5mBf1si0jLJpsOoyQj4LpYNZPmM2v8j7UWj3zfAe6cHGNxcZFEIsGj++L82dNnWRdFHNcj6JMRhG6BzLY9+iI+7p3McPSbILqRSCTQz56lMzREX8jHe7//EJ9xXDbP5AnJIqFEABQRz+oKNDU9j9RdowyMRyl/eRFfzN9L8qDrH6n2BfEVdU6ey3PrlrjKzeCOO+7g8ccf56Wz09SkKOdWq+gtE8uxiQUSTNYr7O8PcynXTYo1v4w6HkMdj/Xew7AcWh2bgOEwmNhdLIet+UIBJEXG81xEx8O6wrvXcVzOvLCKJInXTfK2oUX9VNcavPz1BUSx2PNcfuXJ5/EcD0u3Ma7w0nRdl8WJCfadOUPpl36JyvHjeL/4iyQ/+MG3RNxNVeXrFvIc2+Vv//wU559ZxjEdPA9eHo7wvn98nInJm1vDvhl404neCy+8wNvf/nZUVeUnfuInOHDgANBV1/yzP/szHnjgAZ544okdypvfSRi5LcvxxQqvnN5kzXAIhHzIW/YKRqNDynA58cAEiambp4NUdYuW4RANKIQi/VTK5Z45rmVZpFMpRqw4ttREHgl3TTA9D2G9iXm+hHtH19PMc12i0SilSpX5ZoO4KsNUkuBUmsDV0v+igJzUCAlQvlTk1EScH314L+arp1mZXaEcmGKtohMNKKhS1wSzbTq0TZt4QOXte/u4d0/qLfcY6XQ69FVWSdw5gGu7vWRPEAWSId8Oee5Cw8A2HXwdG9cDdSyKMtDl8g/FNSzb7crnRnwwGUcyHHylNlZRw4v4EbaCp5Zp09AtgqrcncnTLdyAiljp4PllnPEoXlKjadjguQzFNAZSUdgoUTE96h2bYNKPUugg1U20hMbaVgDuuR4Nx0EzHIaGNZI3WNx8fj8tvYTjSjiCgP+NBne1OpubG9i2jSCK9KX7UAIRPLWF6grMXZimNVDkzpHbEASB2FiMYqrIff/0PuwNm/bUOK1Xz9L4uU/QGhmh/4c+iBQLMnLvCInJBFLcj7NYZrrcxnY9RAHimoKmSmiKTCyo4LoeJ1eqXMo1GEsGuWcyxVDMT7FYpDRbQtEUbNdjodQiqCq7ZjuDvq6E+mKpxXAigOu41Nfq3/GJHkByKknmpXVWmjo13Xrdbm7bdBA9SGsqqQOp6xpWXw1BEJicnGR5eRlJkrBtm0KhQ2G2SWo0ReQKepOsSgwe6mfp1BqnXlnn/odGsSwLURQ5deoUsiyTTqd7yV2p1OLZJxdwLIfxqdSuzlMmk6HT6fDSSy8xOjr696auKi8t4aTTSG8wiC0XOngdj/BVXaZIX5DKRoP8ZpPxN0g1zmYH+cP/50mWXrkINvhjfoyOTqMgUJptsTbf5Ef/xf343oDMd6QvyOqpTdz+EKIk0CzqqCGFaFyj1TIwDGOXCMQ2cnMVFmbKaNko4a17UBIE+pMBVhsdzj2/SmZviJbRYmhoCPEWkaWnlgj1hxiI+njq/Mssx32EY2HGHhxDDaqsXFxBHpEJHDyM/Bu/gfG3n8b83/43xPe+B++hh7DaFu1CG8dySEwkGLpriFD/m2MIOY5D8WIRJaiwVtcpNA2ysUCPGh1SZdaqbRZKLfYl/BQvFhk4PnDDefrvdDiux6nZFdZ0yM/n8PvjXLh4iSNHjvQE994oQqEQIyMjXLx4kefXOmzmG0T7Iwz094Pj0XppHX2lgSCL+A6mCEQ02u02yZCf8yslylGNgOrwpS99icV2lM55nRHZj3o4smOtk6J+1JZJfa7Kk1+eZTgVZGJigmKxyCtPf5Wfeu/7yLUcLm02yDU6uJ7HmD/I3v4we9KhNzUacTNIJBLU5udxttbGyUyYD/3ILbzw0hrTpzfJrdQQHA9PEgjsjXP82AB33Jrlbx6fx2+5O7t+WxCDCr5ci1q5Tdt0iGo3FyOoqkp0cA9/+cI8AT1IONcm0LGpt1vUR1I8nWxz9GCGw9kol3INHNfrjsrIIh5d0b1i08ABUjE/ff5r61S4bteayvM8PA9caacP3UauQWGxSugaFNrdb+aBz2b5zAYf+OFbSWz5KD/03gNoYR8LSxc5dAWFcmZmhvHxcdRgkIFf+zX0f/kvqX70o6x84hOEP/lJuIZtzTeCUqnUY1ycP7vJ+aeXCaa7fs6O7ZG7VODJz15i7F/d9Q1ZAn0jeNMr2s/+7M8yODjIM888s0s++xd+4Re49957+dmf/Vm+/OUvf8MH+a2IQDLA7e8/SDDsY+ZcjvnlGhW9Q0TzMxJXWK2dJHRgL8oboNx1LAfTcVAEmc3NTUrFIqIo4kkS+/btw2tYNEpFpKTWW+AED6wACGsWy+fmiA6nukptikJb0Ai12wQFkZpfwnZcuI53ixT3E6p2WJ8psXQ8S7uc42PvupXlUou2Emc638R23O7cSNjH0aE+9vSF3zSl8PWg2DahWo34/QcYnJhk6akliheL+GN+AqlAr0vqeR5mvYOQa+HqNv49cdTxWO/8+GSJsVSQpWKLmm4S8iv4siF0v4jml7E3mlsLiUTDsPGqHVRJQpyvgushaB7OQAg3FcCQXOr1NkHNR0J2u0keMJKJo5Rq1G2JpmEjOw7CYg1iPopti/MbdRzPw00FmDQ9Uq9DcfW2Kl+mKnUtF24yCLZMk431DZqtrrdiOBKhP9OPoio0OzZWUCE9lmC5sUSumePzX/s8H/zgB1lcXCSajDJ19xSe52G1LDrvnsL8Xx5j4xP/kb7/9K/x/x8/Q/TuH2StqvPVl1eYKzSJBVTu25O+JnV5G03DZjrXYLWssy8h8tL//FO0aZXB/kEG1MmtTera91BAlai2LWzHQ1RErLZ1U+fh2x3hwTCjh/soPbvEtG6B11XVvBY9ptmxqbdNJmyYOPrGFEpd12Vzc7MrplSvk06n2VjxMBomiasq267j0OnoODiceXGeY7fFe152IyMjuK7Lyy+/jCzLtBoqn/+T09TWG3hAIO7ngfcf5O77x3a8pyzLZDIZTNMkn8/vklX/u4C8tIQ9OnoDR6trQ/XJCLKIbbkoV/hMmR0bDwfL3m0w/3p44evLLLxYRg3L9A0ncWwbw5AJBAO0ah0WX87z2b85xQd+6MRNv+eD79nHpwtt1s/lECURX9TPLQ8NEgyqaFqKjY2N6wrzVMttDMchpl0VJgggBWXWVou4ptvrxib3JSnPlinPlLFUC8mReO1rr3Hk+BHu/pG7qdt1EgcT1GZqNDeauKaLd+RtNFJ7Ub/yVTa/+AqBH/oBokfGSB1IER+PvymV0218/Zmvc/6r5+nr66MUTuKTpR12DggQ9Cnk6waHk0E6lQ52x/6uTPQ8z+Pcep1XZ4s89cWXqZcyuK6LEtGIT4xRk+PYjov8Jgq6zWaT5eVlXNdlvdwkm06QSHRpb+Z6HXOhitwXxG1btM5s4twSYr48R//AAKIgEEllODSRZGBsitN//Crhpol6MHnNgpYYVAnF/eRnKpxfqjAUD1AsFrn99tvZWF9jYGCAiSM3Ly7zViCRSGCsrnapylvIxgK8/5EpSnePsFZqYRoOqk9iKBUiEVTxPA9ZFXFE8Cx3l5I1jocngCgJN9x/r0bTsHmt4GFdqBFvtfBFNHTRJh4Lw1qTUF3lZWOVH3jsMCdG41zYqDOTb1I2bQQEAqrEOw9k2JsJsdC2ERYa15xpsx0HWZJwGya2X0bpC5KNXS6KGR0b13ZQXqdopbfa6B2dcDyK3rDo6DZsbU1Dw1EefNcwpz7xNzz//Ne57777cF2X6elp3v3ud/feQxsbQ/vc57jwB39A4CMfYf2BB8j8+q8jvUEhquvhv/yX/4Isy+zbt49OPYNtOoS25k4lWSDUF6KwVKNeN4jFXp9+/s3AN9TR+/mf//lreiRlMhn+2T/7Z/zyL//yN3Rw3+oIpAP47xum2jGwazq5+U2kiWFK2RQraoS//vz/5MFGiXvuueem3q9arbK2tk5z02Ig08fBgwcRRBHDMFhcWCAby+C5HoZhYDtd6o2AgM+nkkgksDyPeOJyFblYbuMr6HhR9XV1HQRBQA37cJaqPPPaee7bs4ehoSEGBhwWFxe5/Xgaf7A7zPxWd++uBW1zE0tR0CYmSO5NoiU1KvMViheKVBYqCJ7QpToCfr9C4HAKJ64h+aWdhqF0DdXH0yHWqzrFmo7tuDRGY8Qmw/h9AfSKQWWtzmKxSct2MdoWAUXAzUZQEwGUgEzHciiUakiuSTYQJZu63IYXRZFkSCMBGJ5GWRbplDpYDROUbmL8zoMZngvmiNgFnLKOfAN6nZ1vYUZUlHSAcE6n/joXz3O7cw7FYgHP81AUlYGBAULhy5Vwz/MQPQ9Zk0in0gz6BnnXu96F53m88sorPPjgg8DWfRBSUUMqS0sVDv7O/4lQ/l+pfOxjnP7//pTHf+xnqAYiTKRDu+4Dz/OwLQvDNDEMA9MwMEwT1zBYbJm8dFJnbbrAyPkqZ187y+HW7UhDR3Hca39BxwNV6noh4XLTFOhvF2x3qvWKjl7SsXQLQRTQEhqhbIj9+9NwscBix2bNsAiocm++xLRdmoaFp3eYFFWOHh1g4qHxmwpSm80mxWIRSZLo7+/vDatXKpWu55MH4NFu6T1KviSKBAJB/IE6wWBoV1ImiiLZbJZ0uo/f/a9fo7HZpv9AGkHoznl9/XPTHDrWTyRyWcFseXmZ8fFxBEGg0WgwPz/P6OjodVUdvylYWMB9E6bIUwdSxIciFBcq9O1JIskCVsemtl5n4vYhMv0hlpeXgS7dM51O37Bradsup55ZQvUpBGIqruPQarW7Ig1AMOrHSEc489wSD737ALHYzXUgu6Itt/LKC/NEIlGGx+P4te7esU2Tux6CIQVZEDFMZyetutlEr+rsGUqQ6Lu832hxjT2P7WHjtQ1mXp5hdGCU8ECYuz50F7GxGLWlGsO3DZM9lkUv6xg1A7Npsr7uY/AjP480cw7p138JtXk3oUf/D7jJJM9xHNrtNq1WC9u+TOFqtVsUS0WK+SLK1D5sdTcbwHJcIn65SzUThZvuhn8nwXU9nprJ8/TjCwizFQ61AyjhEAgCtu3QONniT1dfIPe+W3jn4f6bSva26bz1ep1QKMT+/fvRdZ3hRohLKznaQZlAIIBpmLSbLYSgh6fbqEgM9Pdj5G1SqRTlXL03S7pYaqEv1QgF5F17/JWQ4n788xXOn81zYixMuVzuxWDr6+vUajWy2exbc/JuAuFwmFaxiLRv366fXc1O2oYgCByYSvL486vYRR1lWN6xftjFNq2AzNiAj9LmGiW6CWU4fGP12Jlcg835MhO2Rsmr0J+IIegeajiAF/cQVhqkSiJPnlngp95zgol0iJZho1sOotBN9PyKxFChyfJyDbvQQSi0kft2Jk2u4yAJMk6lg5mNkomKDF2R5CiKjCAJOLbLtcq8ju1QrVUJaF3PxFapjSgLqFetCWtra2iaxosvvsjQ0BC6rjM5OblrDzEMg+ijj5L80R9l8+Mfp3brrdj//t/T92M/dsPzBWCaJq1Wa9efdruN53m9Isb58+e588T3b31/r0eXtzs2akB+Q0yMtxpv+pNFUdyxqF4Nx3H+3tqUf1d4ZanMFz59EWbLOMUysbCMUykhTPuwq37KtyU4cvzGc4q2bTM/P8/CwgKuHKRfizIwFKdU3KQ8M8PefftwHIdQKMSFxWmSImhVkdBYvPfgW+sNQgNxFp3Gjvc2OzaSB54kIsLrVn4EVcRumORKFfY8difQtXKYnJzszdRca+7mmwFpcZFyLEZkS5AmkAwQSAbIHMnQWG9gGzZ4ICoigVQAfbPOU1+ZIzRTwbOcXRUwVRYZTQYJNUxWoz7aMT8dWeX5RpOaZWEmJar+GKIL8Xwbp2PT9InQ7iB1BCJ+hZFUCL28Sa24yUh2Z6Dr1/zU63WioTCxoRimW0MbjLLuFzk8GOWh/RlydYPFsk5yroZdaCMl/Ds2LM9xsTaaeKpEM5tkKqjiq5qUnesbpjabTTY2NrBMEwSBVDpNOpVCuOrZMx0XzYVIJswPfs8PsrCwgCRJLCwsEIvFSCR208x6UurpNPb/89/44l88QfW//y19MT+5Bx/E9DwM0+wmdE0dq9EBx0PyK6gxDdXvx6eq+Px+JqJRhgdEyuWjRKUq78hMcPDegzx7Ic96qY0v6kf0ybC1OHoetAyLycEYggCO5aDF/36qYW81HMuhdKlE/mye5mYT1+kqGG4nsoXzBQAkRWIw7CdqWFRMh03dpt2xMU2DoOJjjydgGW02Guc5Opm54Wye53ldr6nVKvXzdTRFo+9IH185+xX27NnD5OQkpmmihTxadovihkRyIL5jDfdcD0yBRPb6tONW08KquYQyod5G102Iqqyv1okc7CZ6xWKRVCrVW8PC4XDPhuFGsuxvNdTlZZw3QUkLaCqP/sBhPv/np8ldKmwJQwkM7E3x6PsPEo2Gewa9nU6H1dVVPM9DkiQymcwuT8HlpQrFlRqx/hC+oEo+lyMe3/lMRvuC5Gd0vv7UOd79vtu5aQht3vP+473gZ3V19aZeNrAnSXY4wtxmA/9IDM+16egdLBTCjsjUrdldjJVAKsDkI5MM3zOM53psljZJjCVYX1/vBdeSIvXmH9vtNvJwl/bLLf3w/Q+i/97v0bn3XoR//+8Rvvd7abXbtNvtXlJ6dcIsiiLBYJBUKrXjvNq2TeloiSF5CHcwyjOzRVpbfq/QnTWyHJeRRAC9rBPJdgV3vttwdr3G048voJ0vEgioSGPB3r6kuB5qtQPzZZ794kViQZV79lxfNKnVarG0tESjblIrySTTMQ4cyHLu3Dkef/xxhu5+L4btsrmxgSiKJKIxovv7sdeaIPnx708ihX1IJQnD7DI4tjUCOqaDqDsIrxMwC5KIIolYHYsXXn6Vhx+4XGjPZrO0221mZ2cZGRlBvY5Z+VsJAUjWapTfIGXwYDbKyWP9VL6+Smy5jhzzgSDgVA0aePhuyfDA0TFGU90kq1wus7S0BHRFnK5cX7cxk2+glDvInkB8IMXq8gpjE+Pd4xQEpJRGuG6QrzpcWNrg2OQgQZ/ce2a2MZoIcGRvmlcrHVILNbzlOmLch6gp4HpYhTaiJ9NKBwgc6WN/UtqhSdDfHyI6EKa+2sB/pVihB7V6Dc/zSMYTXZlRoFnUyRxMkbiqIxYOh3nXu95FLBZjeHiYz372s7znPe/ZdS43NzcZGRnprtE/93MYP/mTlD72MTZ+7/ewf/VXcSYmaLfblGtdzYBcw8B0QREhEVDZkw6SinX3qHg8TjAYRNM0RFGkUCggiiLf+73fS7vl8cdrz5G7VCDcH8bULTr1Dsce3I/2TWK/3QzedKJ3zz338Lu/+7v80A/9EKNX3cDLy8t88pOf5N577/2GD/BbFcWmwRNPLCJPl4gmA8zoNgR86K5LIBvguNBPoaDy5Vdm+MB9h3a9vlqtcuHCBRqNBhMTE7zjHe9g+kvzvPL1S5xRZylFKwS0AMlUCk3T6MtkSPf1sSzO4EzrWMt1BJ+EZzhIYZXA0QxSuYNpmr3FS5RFbMBxPCRFwHcN08kr4Tke1XqNuw8d3/Wz7ZmamZkZhoeHe+IN3yxIi4tUYjFGIzuVjWS/THxit1fUQRFOTsRoVDqE1prI6QBi8PKD5dkudrFNWFOIHUqTHQmzVGqTb7v4dY9YSUeeqyKYNoG6SaDcQfTAiap4IbVLIQmHMWsFEAVym5sMXFUVjITC1BsNItEIAgKSB7IoYjldA/rHDvfzKcthE0iuNXFXu3MJyCJYDm69jT2YojQS4sCBNHuaZRprPpYMa4eJPYBlWeQ2c9TrNQACwSADAwPXnLfxvK4K7IRf6RlpZ7NZ1tbWePXVV3nggQeoVqs0m03a7TbNZpN8Pk+j0eDkyZPUmy1eLsCmqZK95wTKzDSZ3/99qnfeQXDPUaI1ASEnIFsBJFFCNGRkXwC1L4oyENqRdN975ABCyMCcqbPw1QWCbRO50CTvQTCgoIR9uBEfNVUgHg8wkgzQqXbwx/xER7/95/PaxTYrz61QulRCCSpEhiN4kkjTsLFcFxEBTZHQVJFOtUsliyg+MkmNPZbL3Mw6p09f5L5H7mHslgEaSprPPTPPEy88QWwgxvj4+M7Pa7cpFApdynWqj9KTJcorHTq0efxzj9McalKv19E0jXQ6zYMPZVi5ZHH2yTnC0RD+rWqzY3sUFipE+yOM773+dQgEfahBFb1hEN5qeusNA2SP4FYQbds2rVZrl8qmKIqMjY1RKpVYWlrqbczfTKgrK7S2utlvFPsO9pH5qXs5f3qTVtMkntQ4eLSfgLYzePT7/QwPDwPd4kkul+sVSVOpFIFAAEPv+h/KqoTndrvytm2hqJfXMEkR8Tzw+YK9RPlm4LpuL8m7WmlTluVdvlLQ3cPn5uZYrLxIVNzL6pk1hLAfRRDxWQZHTgxy7Coq7pVQt8QjxKqI4zhYlrUjqPY8D13XmZ6eJpVK9VS7AXjvexHf9jbkj38c32//NsonPsHA8eNvuHA8NjZG/B/HufCpCwREkf39EaZzDapts/vdRZHJdJChkI9mxSB9MP1d19GzHZeXpwuI02UCAXWX7YAgCsgJjZSQZOVijlfP5zg+Et8xz+Z5Hmtra1SrVQKBAPv27eOPPvkC869soPhlnnryWWqtC3iex3B9g3A4wmCiH9vQMU2T6N3DOFvqtlIq0J0bj8VY3iyRiMcY30pkFFnsFgLNG1NcPM/DdbuxzMT4nl3JXCAQ6M0mbxcIvpnQ5+fRHId25vp+s9dCX9jPe942weclkdKFPFKxg+B5WHEf0X0p3nHfWO/cQLejt12wrTebnJlewHZcNJ/KnpEsiiKjWy6S6YDaFb4RZQmjY+Dzd9d5QZUQHQ9JVKjWm1iWtcOeYBuyJPLIwQyO53JGk1HWG4QqJnLdxEOgLQk0x+KE9sR557EsYauy4/WqInH4ziGemD2FYzpIqoShd2i2WkSikR2faXdsHMfl6B3DuwTsJrbYGIuLi1y8eJG9e/ciiuKOLn+r1WJ6epr19fWdXf+f/En8ly4x8S//JY2Dh5j9V/87C2aAqqshRiRkUUB3PVY8j7bp44gSZXIwuWuW80Mf+lDvHguF4Ht+9Bae+tw0pZU6il/m6Hv28dBje9/QtX+r8aYTvY9//OM88MAD7N+/n/e///3s3dv9IpcuXeLTn/40sizzq7/6q2/ZgX6rYXqjTmO6SJ9fRYr62R/daXYrD8mEVutcuFCmdotONKThOA4LCwvMz88TCoU4cOAA8Xg3aXFtl/JKHa1pYHoWg/uzNJpN4rEY4tYmLQgCo8f3sh5YolMwiIoaYtiHMhhGTmgkSFApl8ls0WljMT/LIQVfScffryFLN97Eyit5fMMJBpPXDuT8fj9TU1Osrq4iy/I1abtvBTzPQ1lZoZpMsl+7uS7OQFTjkWNZPm+5VM8XiJYNhEIbQRbwPMDzMEIq1ZEwqYk4HctlTzzArTWb1loDo2my7JcxQyohWcbXtAlUdYRGBy/mxxyMsFCo05fIYFYLVCoVwpHITisRUeh6c7XbSHjdGR7X6yXYmYifDxwf5Es+mYVMHWO1QlT3EG0XR5UoDCok93YHoR891M/qjE54TGHh3CZV3eraV7gepUqZQj7fDeBkmf5Mhui1OiAeOI5NpdVBaOkImsBcfo5LT1yi1Wrx/PPPYxgG5XIZSZIIBoO9P+12mwMHDhAMBllrCczNVLklFSbgU+Duu/Dabbz//BdYX/wswqFbUUYziCkFBAHPdLDWG5jLddTRKIFb+xFUCXOhSv+0SW6lznqpjdRxSI5EkEMqK6U2zaaJ12ggrtSJxf3siwTQPKjlWmRvz+KPfnOLC99sNDa6fl7tYpvoWJSOB3OVNouFFnXDxnW31EklgYGYxkgiQGoqQafUplPtUBbgpfk8LdfPk9NrjLxvH6N9CcY3xtm7d28vyfM8j1wuh2EYBAKBXiHObJkYDQNTkymbLSJalIHxAWRF3uHf9r4PH8O2HGZeWkYWFWRZwnU9IgMhHv7gYeKpazM5PM/D75c5/rZRnvrr82xOlxAlAbtjs/eeIVyvxtpaC8MwdiWkVyKZTBKJRJi5MEM6mSaSiCDdpM3EtdCpdqiv1nEsh0AqQGTwsoiDvLyM9Q2wFGIxjXseuP53uRqSJPU6W9vG8cVikXqzCaJHp2XhiibxWIxmu4VrO4hb64dlOAiSSCodo91u7yjqXQ9XG5xfnXBtmyUPDOycWzp//jwvvvgi8fE4b79tH9U1j9xiBdUnMXlLlqnjA9ftfl0ZZBmGwZe+9CX27dvXS+a2k3dN09A0bce9twN/8ic4p05h/pt/g370KMGPfxxeh5p2NSKDETJHM6y/tM7egRDZqJ9i08D1IBFUiYkijaUa6YNpYlcoC363YLncZm26RNhwkAauf27FmI9k3sfi6RXmbhvicDaKrussLi7iui6Dg4O962jbLqX1JmpAwaibjA7tYc+hAxQKBfoyUer1IEulFhN9IWRZplgtke7fSW3WgmHWFgvcf3iM8JaXXybiR8qGMc82rnmM23CbJg08glHYNzF2zd8RBIHR0VGq1Srz8/OMjY19Uxhonucx//IZlg/dx+kLVUbby2SjGvv6w2Qir7+f7ekL8aPv2sfsiSwLm11xlJG+EFOZMKlrUD4bHYvpXJNTq1VKTQvHcxEwCc2eY2/SR7vewVAEvI6NpYkMDQ2xvrbG0OAQiAJu28JWRNSAwsToMMvLy0xeYT1zJYI+me85kmUiFeKLL1+kZkXxjK5yuKdY3HV4hEPZKMPxABcuFGi3TQJXGNHfcmKQ8y+usXGxgK9PQgtpJK8YiQGwDZvNmRJDx/o5ery7Rm1TKav1BrPLZcrlJusbKxQ3Zrj3zttYXFzsdfmDwSCWZTExMUF/fz/BYHBnUeuxxzA/+q946jf+X87+3l+j3XKYyQfvQ7miKeK4HqWWweOX8uTqHb7nyEDvngR2rcH79vexZypFtarj9ytvSH35m4U3negdP36cF154gZ/92Z/lM5/5DO12G+hWSx577DF+5Vd+hYMHD75lB/qthoXVGkrDQuq79kCn6JMIiCL1tsjzJ88SU1yq1Srj4+M89NBDuyqooixy8B0TdIIyrY7JyMG+XUqE28juG6WcLlPVdQYHL1MIE/E4CwsLvUQvE9VYyoZxcy2irxMn6bUWdcMksSdBNnjjhHBoaIhGo8Hs7CxjY2O7vssbgaVbGHUDX9iHsiXT2263CefzFPfto91u37Ss89GhGD5Z4umkxvpyHa/QQjIdPFHECitEhyMcTAVZrehIjkt8too5XyGUDhIZjtAutig0DTRFRi4buJFuYkWtA2aZekoh7wUYiCVplfNsrK8zOTnZS8QBFFWhU20iKhJiUMHpWDsW5L6In390+zBnFjZ4wi2jJgdxPQ9ZFMg4DR46PsFgTEMUBS60bQamEvRdLFIwbeoNm0J+E6PTwfM8ItEosVgMz/UoFUvYtoVl2Vv/tbBsu6v26SoM6RbWwRCOFie0NWP18ssv82M/9mNks9kdHVrXdVleXmZsbAzP83j+5RV8PrWb5G1ft6KFN3QXUrSKd/p57GIM3/33wbmzCCOjiNkUnuVgLlRxdQtBFrHWG4hBleRUglLCj9ewMaoGib4AiYifRsfG8TxkQUA1HdqXSsxcKjP69lEGb7u2YMS3CzrVDgtfXUAv68QmYswUWlzcbNAyLPyqTFRTkEQB1/MwbJf5QpPFYotM1M+JkTg+SeT0H53ExSC2N0ot3+K5r87zox+9kw9/+MPU63VWVlZw3a5gUiaT2dXdVQIK8Yk47Vc2iHga6Xfcxt737N0VsMdiGh/5F3dy8f5xnnv6LJKgMnVgiCPHB0gkgxQKBXRdR7uqCLMdqN3/0CR+TeH8q+tYHYe9t/Rz1/1jaJrCxsYGuq6ztrbWE3O5GvW1OqWZEq35FqulVQRFYM+de0hOJW/KOuJKVOYrLD6xSLvcRkBAVEQyRzOM3j+KaOhIhQL23xEd/WoIgtDrJgwNubz6VI6Fk+vER4Pouk4oEOwxBABq63ViA2H2HkijaQpzc3PXDcK2kcvldhhWG4axIzBptWymL+xM9DzPY2BggP7+fvbt28eRuw/2/t2yLFqtFoVaAbdy7fk+SZIIBAIkk0n6+vrI5/O9yvuVyOfzrzsKIB07hvbEE3T+6I/oPPAAjZ/8GKV7HkP4xP/N6G//n/jDNy4Emi2zR/svnitiWzbxpIY/6sfOtegoEpmjGUbuH3ld/8nvRFR1C6faQVGlG3YzBUHAHw3gFlpcnF+GcreoOTU1tWv/l2WR428b4+WvzBHJhDhx9wSjY5dZOKmazqdeW2Mu32AoHiSRSJDP5Uj39SEIAjXdYqPWYTgscvfE5cB/KKYxcjDNxfMbxMo68jV8FT3HpZNvU1Q7fM/+UfL5/C7D9isRi8UIh8MsLi6STCZ7dOu3Arbj8tRMkScuVOgcuo+IP8JSqc35jTovLpa5czzBvZOpXV2qqxHVFE6MJjgxemMV3+Vym8+f3WBjtYavYhAo6Yi2i+uTqCQ0vl4HLeyjFhRxGxVCUhhbs4knEhRKRVKJJE6lQ2MwxMhghGxMo+PrZ2NjY1chaBt+ReL4SJzSTJsDJ26hYzlIgkCrkufQVPc1S4sV/scfziIIM/zwR+8lFBFYWFjgc5/7HMHQAOpADGPTRmhbSI6Oh4uuGzRyTTq6RWjET2yowlNPfhXoWgOtlUUWL7VpL+uILrQ6Fv7+AyjhAzxw98gO9tri4uIN7Xuemikwffhuxk/cjf2pv6H1i8/CvY+hmALi5gry976DdDpATFM5v15HkQS+99jgDWdVJUkk+ffsnXclvqGV7eDBg3zqU5/qenYVunMl6XT6O2I2z/M82oU2lfkKrUILXNASGrGJGOGBMK7tInhej0O8C65Hu91kY71NfjjIxJEp7rzzzht+ZmJPggfGYqy+uMxqpc1E+vqy0olEgmazyeLCQleeXBTxaxodw8BzXQRRJKIp0KfRSWn4ywZen9uzEthxqIZD7vwKysER7rl7D5beZHo6R39//w7z3iuxPVOztLRENBq95ozX60Ev68x9aY5WvoWW1Jh8ZJJgX5BGo0GkUKD88Ad44quXGJ8cZGpv6qY8pPb1h5lIB1nc22K+2KJl2MiSSCbiY6ovzKtLZS5uNhleb9GZr6IMhnvUwoBPxmt0cMMqbkhB0C28kApxDaHQIKP5qEYlmraALErU6w1WV9cYGd0ZrPhNmXbMwwkqdHJNzr+wwvmvzuHYLqGIj32H++l01vnwffvIZDLYbjfRm5ubYzgRwLIs8vkq7XaTWauJEGhQf2GJs5KHYreQRZFwJESjXkdvt5EVBUVRUGQZv+ZHkUPIioIsy5TbDmOmx63DMW770GGCW4WJi5cucccddxAIBHbRcK9c2HN1g+VSi77wFYmgYdM5W0AQQZkahD3fj3nxAtZf/iVKuw0XLsKHP4ygSEgJP62nV5CSGtrhNMI27SGgYCaCJGoGrc0WgiygBVUEScCzPUzdRpRFBEHAtVzMpvltOz/juR5rL67R2GiQmEpwbqPBuY0aAUUmG9vpwSnRFTsK+WQs22W10sawHY5no+CTSFsh4uksRa+NZXY7a7lcjk6nQ6FQ4NZbb73u+isIAqMPjBIZjODaLpHh688kqarM0eNZhsc0yuVy9xnf2rhSqRSrq6s9OuLVkCSRu+4b4677xnb8u23bmKbJoUOHsG2b1dVVBEEgm832uk6bpzZZeXYFu2PjT/hJZVO0621e+/RrDIwPsOeRPdekbl8Ldsdm+dllLN0iuTeJIAiYTZON1zaIDEZIGus0tCS5NQ+rsUgwHSQ+Ef97UV0URZF737mPtekCXlNCjijdjp7rkM/lkW0fluFw69vHe7Me2Wx2x+zbtXA1LdM0zR0J+spiBb1tUavpqCrU63WeeuopRkdHefTRR3FddwetUlVVgls08ZvZ51dXV6/L/NB1/eaUVgUB5Yc+wuORW3j1P38B/ZO/jWi6RH/4d7jzZ36QO+/dOTrieR7NjSbl2TKl6RKdWrcwJvkkjJZBdb6KlujOqA7fNUx8Iv5dR9nchucBLlxTI/8KOLZDo9HAUwSKxRKH73v7DX//ocf2cvzOYfx+edds0kBU4wPHh/jKxRzLpe76prccVmdX8QfCxEN+7hpPMIBAu1FFS3aTPVEUuO/EIHPn1yjPNol1HORkd7bbcz3chkGnqLMqmBx7aJJH7jxKp91kZmaGUCh03WRFkiQmJibI5/M7zN3fKC5dzPPsl2bRgirv/L4DnCu1eXqmgFbKM6BYRPsuJ5GlZrdDpEoid04kb/CuN4f1qs5nTq1RuVQivdzAa5qImgyyiFg1iOfaEPezMRyiGVQR9/SRrrpYKw1sxaOZLyJuGiijSbzxKLeOxpFEgWAwSK1Wu2HBvdFoEAmHGbxifm65fTnRWp6vUF9p0jE6fPZ/PsXpc1/i5MmTlMtlPvzhD7P/jjQr8y4bszWKM2UERBRVIToU4a47hrjrvnH6UpfP3VMvrbL4hTOIFZPBdBQ5ILE830ApODz7p6cpl3U+8J79KJK4i9FwNQoNg5OrNTJhP6GAQueu99C0L2H/xdM4rRoCIoKXwnfbONqxDKPJIOc3GhwbarOn783Zvvx94C3Z0URRvGHV5NsN2wFC6WKJcrlNWwAE8LmQeNVHYk+CVNLHnF/GrRmIVwQGlmlRqVboNHUkW2R8/zjve9dxiutdZZ7X2xxVWeSBqRR/9coKlZZJ/AZt31AohKIoLCwsMDIygqwoRCMRavU6sViMum4xnAkTeCRE6Zk5/Es1fEEFKebvBtSWi1PtkMuVMcZS3PHoFHfvSfWU/TY2Ntjc3KSvr68njtCoNGivtmmsNVACCsnJJJZnsbCwwNjY2BuaqSleLJKfLyP1h2gt1QifzTP+0DjVcplZ6QAvrA0i/9UMrwRXGDma4YM/evym2uCKJDKVCTOV2UlFaXQszm3UiTse5lIVKenfMT8W9snIsojpeUhJDXGhhhfycD0XL6Ii10yCqQC6JDE0Ms7mygKVSmUr+AkgyzJ+udtF0Sb6ePpvzyLnOswhdhWXBDANmzNfnaXuVXnH+22y/Ss9Hvnc3BzhcBjLspAkCUFQmZu7yOb0Jo1LHVJjtyCMjjGeTRL0+VBk+bqFBsf1yNc7hGyP/VGNfW8bJdgX5OTZTb7+lTkunZvm+F0HWe0v7QrErvTWKjYNdMth+Ipqt51rYVc6KINbC50A6oEDcPoM1OpQreJ+7XGEtz+ItVTHNRwkvJ7QCoCmyFREOHHHIHpJp7ZSw6gZeKaHIAlERiJEBiP4E37qS3XmvzbPvvfuw/c61hTfiqguVSmcLxAdibJYbnNho07Er+wacL8aiiySjQbYqLc5u9lg4FAfC08tcPFz50jfMUZmTGV5eZm+vj4ymQxDQ0O9Tuz1ICkSqf03P5NiWRZDQ0O0Wq1eUiEIwq5ZL6A3G3G9jXVbZRO6s2Hbtgxra2t4nkfACLD89DKSXyLSH0FWuufHF/ERH4qzeGqR058+zW0fue2mjK2buSbtQpvYRIy1qk5dt9mXCSOKIrXlGtL8Baajd1J6KU8roiMJIsmpJBOPTLwhW5w3gkpF59TLq6wtVNECMvuODXDoSPf5S6Y9HvvICZ7/3Az56TKS2lXaa9U6qBGX/oM+MkPd+cZtMYBarUaz2dxJId/CdnfXdd3eGrOyskI6ne5do0DEQfR1qNUKvZ+/733vu6633huB67q4rktfXx/lcnlHMfB6sz/XwxNfmeHZz86jju4hNXsOz3apXnT5yu89hao+yPHbu7RB13FZf3mdjZc3sHQLLaWR2JPYkchZeterr5VrsfHqRs+y57sRIZ+MEFJw1pvI15DJ35a3l2SZgOInMhhhYiR7U3Yo8RuIZ/VH/fzQ7SOsVNpMb9b5zBde4/bbThCUHI5PJJjIpmk2I8zMzJBMXk6EJtIhHnpomNf6GuTPFvCt1lE8wANdFXCGAqQHNT7y2C2osogaiRCJRGg2uwlfIBBgcHCQs2fPMjw8vKOD19fXh2EYzM7OMjg4uIuxcCO4rstX/uY8uZkSuB6iX2Y9EyAeUFGLm1hamtYLayAK+EajJPuCWI7HS4tlDg9GX3cvuPFnezw1U6A0WyY1V0WQReSRyI5r6bke9maTgYU6pbSAsr+fYs1EzbfwNU1ikTFmzQqJMR+xxhxhK45td2mO2Wy2xx4wbYeFhW5xyO+TGZ9IMD09TSKRYG1trbfOzM/Pc/Hixe7a07RRsg5YLqk+ielPTdNsNnnkkUf4N//m3/SaCbphsb5Wx+w4qJrM8FAU9SrKfq6m8/XPXkJtmMT3dT1jS8UiieFuN1huC1z86jxn96Y4fqDvht1IgOlcnUbHYiDqx1pt0DmdRx3MoN0xBU89Da6L88yX6WjfhxhU0Pan8FyPc+u178xE75d+6Zfe8JsLgsDP/dzPveHX/X3CsRwWn1xk6dUNljyXnGtj2A5sVdrTLY+x1zYIjUSRBwKc/8opRsVxRJ9ErVZHkkSisRgxSyOveRw6niWqKQRHR183CNvGRDrEXRNJnrhUQBKFbmfuOvD5fIyOjbG0tER2YIBEIkG5XEbWQmzUdO7bk+L2sQR/5jMpr1rUFqr4NxuIHriCQCMg0Nwb4JHvuY2Hjwz0kjyAgYEBBgYGyOVyTE9Po4gKf/Prf8Mtg7cQToRxTIfC+QITD08wPDbM3Nwc/f391ww6rgmhW0icnplmIjzY8wmb+folTgUPo0YjDO4boNM0mH9xjefHE7zjsambe+9rYCbfpNwyGa4bdJoWSnLnQu5XJeKaQqFhoMZ8CGEFsWLQ0Tz8mobX0VHrJk2fRMN0GBjIsrq6Qr1eI51OY+gd8peWMeIKq0+WsFYNYikFJwo128GyLRA9HN2hXTB4/K+mOfaODEcOpUin00Sj0d5cnK7r/P7v/z5PPPEEnU6HA1MH+PCj93JxoUbeFGk7NhG/QNAn7yjG6qZDXbdwHZdY2+bgQJRD7xgncyTDxdkSn//Dk9RXy/hEiflnVinmAhw+vA//VlB9NSWv1DK42sDNXK0jyMJuieujR6BcwatWsSsVaudX0YouSjaEWe1uKNLWnJ0sdatpL6/WEASQMwHiI1EGgio+v7zDPys2HqM0XWLtpTXG3z7+bVd9L02Xuv/jk5iebyJLwk1v7KII/RGNzVqH4xNR5mfa+Msmg/s8vud7b9uxmW/PWTYajdeV2b5ZbHeEegyCLRrMtjT/lYUrWZavm+gVi0WSyeQ1FROHh4dxHIcX//hFqoUqnUCH+kKdu+66a8fvjh0bY/XkKmefPMvtH7j9DTFHPNdjY2OTfZkwHh6u47Ly9DytWIb1aIhSo82hgRjedJHYeIzMkbe+cFnIN/mr//IyudkSkiLh2i4Xnlth/d17eeDhbgJ839sn2Xeon7OvrbM6X8ZzPTJDU0RSDnffc5jZ2Vna7TalUveeUhSF2dlZUqnUruQ7n88Ti8XY3NzsUSlt2941E5dMxLh48SJ+v5+HH374LWHkVCo6n/3vLxOLxXjbOzM0mzsTvc3Nzev6912NZtPg1NNLqAGFZNoHt98KrRbpQoGVtTzPfWWaYyeyCAisPrfK6gurBNNBIiPXZqMomkJ0JIpru1QXqsx+YZY9j+35rkz2RpMBUhNx6vNVlLqJFPXh2g71Rh3XdQkEgiSSSdy2RUNoExmPccf+UZZmLmCZPor5FgcO96GqbzxREUWB0WSQ/pDMQljnew5nSCaTrK+v9xLJVqu163UTST/HPjDK9D0NTp7epNU0ECWRPaMxjMoC3/vQ3QSuWl9DoRBTU1O0222mp6f5/Oc/T39/Pz/8wz9MPtfCMh2GR2P4fD727NnD2tpaz35mG67rsrRYpd0yGRyO3sATTaDYNKiGZaYyYZrrNaz4GMJcV5DE3mgSvH+EdNTHXKHJQrHF4cE3Txldq+nM5xrEN9rgcU3rJkEUkAdC1C/mGIukEMM+7jqWZb7QotaxkASBh5VhQk6Nuw6/nYsXLyJJUs9GQBBFPvnJz5CfN6mvtrBNB2QBf0rFDZR4/wduI7LF8toeBdm3b19vLfn+D8KTTz7JmTNnePTRR2k0Gpw4cWIHY0zzKUy+Tnfz7JlN9LU6A2PdLrzrujQaDcbGxjEtE1dxERY6nHxljVv2p68pNLWNbe/I7Xk7c6Ha9QiN+CCZghO3QqOJt7lJPrdM/IKEOhEnGfYxX2hR71hE/H9/SppvBDf9dP7CL/zCG37zb8dErzJXYe3UJrOey1rbIBm87HNiWA5rTQNDUziwXKVRnGYt5KC/cIHJyREy/QkEV8DcNCgpAn33jHDbZLd6LssyPp+P9bUSgWDgdY0T751MYdouz82XaZk2/RH/dbtlkiQxPjbG8soK0WiU1WINO6Bz53iSt+3tzvq960gK6d40F1erzC/XMA0HTZNQSvP8s0fezljm+nSoTCZDJpPhj3/rj2kttzjlnuLBg48SVmQaqzU2Xt0gNh5jz549XQn3avX6A/ZXILUvxcaFDRa+eprxd6foO9StEObPrtKR/CSGYgiCgBb2o/jbLFwqwDeQ6K1VdGRRwFmuIwaVa57PTESjrtu08AiNRrFnivjaLvg8PE1BrHZQEiqr+TJZzaXVbNFoNSnmCiSdIEJao2QJmCsGckoiEpNQVZlEMrxFq5Qpl8uoe310Ni0K0yLD795PNCxTLBaJRqO88sornDlzBkVRsCyLPVN7+bGPfYzxwb2EXtlg/UKRYsskb5lstE0EUdiyPvNQHI+0K5BRJUaO9zFx3yix8e55nDmXQ8+3cMIdRsfGcdsO+aU6Fy6scvzoGNANwK4sRuiWs8uWw2tZ15a4PnAA6KaFcsvC98oaDaeNXW/T3qiidTJIUai1LZbLLaptC9dz0VQF1/PA84hoCgf6I4xtqa9Bd5OKDEXIn82TnEoSHf72Ud806ga1xRpaSiNX71DVzR002JuBJApIksjZ5U0OHB+gs9ph4DrrQTqd5uzZS1h6kMxAmOzQN3aurkzcQqEQqqoyOztLNpulUCiQyWSw2ha15RqFxQIVqUIkGSEyHCGwdQ23VTavVme+EmbNRG7LDEwN8MWvfZGO0SEYCpMdmyJxxbMaH4wjNATmZ+ZJ96dvOFMTyoQIpAPUV+sYZpHmygXsqdEuDT+ukZ/boDWWZbmiI0siFwst7gr5qa/WvymJ3rNfm2dzpsTA/lRPXKa83uDVr8wTSdjcefdhANLpIG9/ZA+dTodWq4Wu6+i6ziuvvEIqleLixYuMjY3hOi71Uh1Xd5m7OEf/0DAd3Y9PlZmYSuB53g1paJ7n9WwfLMvizjvvfMtUTr/22Yuc/tIKfl8OWZE5eMtVPls3wW7ZxtpqjVZRJzESBb8MJ7qG8QKQKOsUV+ucOjVN2g2x9uIatmbjj19+xgzLodwycTyPgCIT37qfRFkkPhmnPFdm4WsL7H3P3t6c+HcLFudmmBz081w2RG26iFcHOewjEo32ro+r2xibLRr9Ae452EcsoOIMjfHJX/kqbksk/30HeOTduz3ibhaW1bVR2A7Is9ksxWKRlZUVfD4fnU5n13hBLKByx0SS28cTWI6HJArMTF8iMDRJQLv++hoIBKjXu958ly5d4v/6tT+EUgbHcrnjsT28413d7zE4OEiz2WRubo7R0VGaTYtP/+lpVs/nsQ2HUCrAPe/ey91byrOiKPLODx7iM3/+Iql0jNTxLIVcHVEQEMs6ZEKow92kxlqq4ZR1fFv3aMO4vk3ZzWA+38TMt5GqHaQbWey4LsRUQlWTfFlHHrd457iPetOh3WpidHRefPVFmpuLmKbJ6dOnOXjwIJqm8fxzeeafq+CXJAbGkkQSEayOQ2WtSmXFobAR5u67DvfmDQ3D6N0/rVaL8+fPc+bMGXw+Hx/5yEeA3SImN4PVlRoKQo9FVy6VSCSSXVaRqlKv1wmEVIpLNQqV+g2bDobt0rEc/EpXvd6p6IjbjKGB/u4fuklSX7NDfmaN+inIHNqD6Th0LIewT37L1sxvJm460buRsep3CjzXo3C+0PWsMkz6IzuVKn2KxEA0wHq1TaewQevSGaJ3jNBMh2lFIlgNE1cSECejDB3s4113jewI6gYGBnj1lfNMTL6+SpssiTy0P0MiqPL0TJFLuQbpkI94UEWgO5fjOE5vVsITBCKpAS4sraN3DN42GeH+/ZnewKgoCAzHAwzHA3CkO9Nx+vRpAlP7bpjkXYmwHGZ0ZBQ77jK3vM5gtp9QQkOv6FgtC1/ER39/P7quMzMzw8jICD6fj/m5EqoqM3RVgK4lNPKJPNptGuux9d78mFraxFF8yMLl29MxHQLXUJl6I2iZNgoCrukg+K5NL9NUiWxMY6nUQlcEhKEQwYKJWDXA8/AEED0AgWAgyOTgGJszK+BA9sQk2i3DFP/6HJF+H1NTKcJbvjK6rmN0Ohh0rTUmJyYJjXusXypw5rUN7rgrw+bmJk899RTNZhPddNgsSTz67v+V3HKVr3+6zAu8iCiL+GSRmCQyIYtIgoDrej1Bl0jcT/9QhPTBNPGJeE/mHLo+2B29TTAZQlEVDN1BkRWq1drl37mKviNwjUVMELaGO64Pu6wjOgLRTIK5uXmwLKanp6Eao4EfRfGhqRKZiNaTK3Zdj4pu8dJSGdv1mMpcXqTVkEpjo0F5tvxtleh1qh3MtkkgE2B5oYKI8Lp+ltdCTJOpCxHG9k0ipXTiW8+sYzkYNQPbsHumzxElyoXFddL9b01X70qoqsrExAQLCwvYlo2z4pA7k2PpwhKra6t4eGT7s4zsGSE2FmP47mHWy+s3VNmErrqaYzrgdiWzJUlisdjAnzYASG7NEsp+GatpMTI4Qr1T783UuK7L+lqDvkyw112Q/TIj942w8PgCs0/PYtZNLr12idvecxuJqQQrpTz+8TE0RaZt2iDY4Pi+KcG+Y7vMn9okkNB2KIjG+0MsnlxlYa5MdujyLJwgCPj9foLBYK8Tura2RjKZJBFMMPPiDGpdxWgYeK7HyvkaT88s0haBiEp8PMKdjwxdN9GrVCrUal0mwubmZnfO+y0MWKq1Oj5V6SqLFkv8/9n77yDJ0vO8F/wdn95VZlZWlvftfY93GAMMMPCACBpcUgxxSV1yJXEVu9SN0KWCUsTV3VhF6N6INaJCoiSQFCURIAg7xFiMd+19d3mXVem9z2P2j6zK7uqq6q6eGZoB+UT0xHSnO3nynO97zfM+j8MR6lBMM5nMPfkkypLYVvIzt645pmFis2n0R6Jc+h9nWF5aJl1P82TwSWRVYypeZiFdptTQsaw2Hbrbo7E34iHo1hBEAf+In+x0ltx8rlNs/FlFKlkmvlpkaKSLQjHFT37yE2xOF4GJfazWXAQzOva8hdmqYwpgVlvUTYtSxMHEE0M8OhECQNNsWJZAqVLn9OU4c7UmDofC/r1h9kQ9OO6hw7chd39r5yUYDFIqlYjFYqysrDC2g9elIAioskC5XCabzbJnz55tn3crDh06xL59+xAEgT/7w4tM3VhBsSlcPRXrJHrQLmxt6BC89fIqs++v4R/wYnMq5FaKvPHdq/T2exlYF5oZnwzh7onh69bxBiax4gWMSgWlWUf2eLBaBpbZjiEEZb3IYYH0Ee+7fK2F0jTAsBB3iGsASuUynrAffbVEM1fm0tUCtkEvTqeTcCiI0+nk8OHDvPnmmzz11FPMzs626fiWj9Jskr7eMJ6om1azSbna7rQazhZdmodLr84zOhni4IHNHdCZmRkajQbz8/MEAgEOHjz4kTxSbw07TMOgXKkwFAxteZ5pWaTTafZN7CxWJQrCzdlUgTvGNbIs093TjRUMcP36DRoovFKbwyWbfOELX/jQ3+evCn/7ZKbugHq+TjlRJi22KzTb2RGIYlu0Qxsc4Bf3TLDnq5NUHAKxkkG50kRRZXoDDoa6HNuq8uzZO0Q+n7trRw/alfzjgwH6Aw7OLuW4vlZiJlkinc6Qjq8iCrCYKhHq6cUCPDaZZ4+NU11uUpg+g7x/e7EEaCcbqVSKp556atfn56EnHmK6Oo1/zM+rb72H0KwyFOhDdaqbBAw21LiWlpZYi9V4/TtzSKrEN//Jg3TfFnz6gj5GDo7Q19fXkQsfyd9gwTlKeraAM9ikUWwi2kQO3rez6AC0k5RGo0Gz2aTRaNBoNDoFCsuyyOXygMLtVMTbEXRpGKbFbDyHzWVD9zkRi02ERAUpXUVJ1PDaZNx2GdFmR75vgoV6nHlvidaVOPVkgdAeXzvJg7btgrNdaWu1WkiyTKnclogWJYtzby8QT58hk0pgs9mpGQGmLxZxt7pQBIVRexRFkBAlAVO3aORrLDVaoMmE+j3c/9AQgz0eJFXC5rOheTXEba694fEALwoNnAUHRbNIMVeja5+fei1JuVzm1KlTOByOTQp5bpuMYWxe/CSvhp7cSqnpwLQwUlUEm4RhmHgcLiyrxVQ2janakGQDrxtUuwP5lqRHFAW6nCqFWovLqwW6nCqBW8RCHEEH2dksvSd7PzHCLM1ys5OA5WvNTdToe4GmSLQqTapNA7cFa2fXsAyLarZKq9LCbJmdz5EUCaVRomjESOTq+IZ8aJ6Pb7ZRFEVGhkd4/vefx56z44v4GD05ymx+lpbeYvKhScyGSepKivRKmpFPj9w1idgwjLdrdg4dPgTAWMvghy+9wec+9WDneWbzprl8MBik2WwyOzvL0pzOez+eYc/9fXzlFw53nu8f9mOqJmvCGsV8kcGDgww9NoQgCgSLCxi2T3M87KIhwNXX3+F9y+Tw0GF6Gtt7UkI7gNlYX5rNJs1mc9PjGxTKW7+zrhs0mw1g8+9vWiaWBf39/TuK22wgEopw6nunUIoKpUSJltCie7CbTKbK4o0CZk1vFwIrFrkzSd5p6kR7PaiahN/vx+1202w2mZ+fx+/3E4m0FfVGR0dZXl7eUuT5sDAMg5OPRPF727YYjz49QrmSZXp6mp6eHn70ox/xW7/1Wxgtg2K8DAJ4I27EHcS2+gd9BKJusrES3eM36Z+WaVFOlOk5EGDtxiKtfIt3z7+Lw+3gnXffwz18iJlkGZem0O2xI4pQbxnE8jXy1RYPjnQRdLfXSsWhkL6WJrgnuO3a+bOAtdUi/+P3P6CwWsI34GIh9yI2m0xXl87//M3jvDuR4+LlBKm5PGK+DhaY3Q5cwz7u29fN43vCHcq5alMY/NQE55+/RmM2T3Y2T1KEmTcWOf9QP195cuyO+gK3YiPRu31m0+12c+TIEf78z/+c4eHhO4pqnDlzZgvVeyeoqtrpJg2MdjF/PoGpm/SNbaUNCoJAT08fS1cvITgsnB4NRIHgsJ/Y5QQz11OdRG91dZVMJkO5XKZ333EUSaS0tILDZWDb20NrpQiCgDbiR+lxUW3qaLJIZAfboFartSme2eh83o5strDtv9+KRq2OTbN1Qh/NpnHg4AhHR7fOax86dIizZ89y8uRJzp8/z5n312iVmoQOtIsgiqqirJ+/XDaHJ+AhNZ3jzVeusWeyC0VRSKfTFAoFIpEIr7zySsdQPJFI3PVY74Ror4c5y8JsGGQKWboCgU3hnCxJFPJFrGCL82dO3THRUyQBtyaTLDUIOB3I3U6as7k2dfM2GPk6csCOayBMn8fJ83/xPO//pz/jkQfu48iRI/T09HLx3CpLMxlEWWRiXzd7/gYVjT62RK9YLPLbv/3b/M7v/M6uqip/E2HqJqZuUjcN1Dt4zqlSW7BD1w1ERPrDPvp3+Zs6HA7iiQRXZ1PUagaDAz6Cd/FTCbttPLu/hwdHgixnq/zFa4s4iwlM0wKvjcfGQ3S5VPr8DgJOlcqohx/96EecOnWKw4cP86d/+qekUin6+/v5+te/3pbMf+89HnvssXs5PXgHvXj6PWSnsrRSBS5eWcDzkJsDn9+3aaZqAwMDA2RT81SqFdyKa1thr0ceeYRIJNKp2NXrdUKr8xzvtRE7HiQ+l8fVb6OlrvEf/sP/zn33tW+sDW73rXMpgiCgaRqapuF0tmWbb90c+vIK6eUcoipiVLZfNNtvBF4VhrqcZBsW+aaOzaVgU9zgs1HZE0C1SbhHg4hOBadLZf6iQDyTx5ZIYRealLNJsm4bga7Nm0epVKIrEMDt8YBpkRML3Dh/ndVqgnDYj8kg028vE9TcRMaCyDskNJZp0cjXSc/meKHQ4L7nJnjy0eE7SjULZpr7vjBEPqbSKDcZPRDms1/eh97Ik0wmefHFFwmHw/T39LN0vUIhXUWJukFodwzF9R9Q6fPQmM1htYxNYjYbMOs6Zl1HtLdn7bocPvQg+GUHiRooZovYWgKP0468zWyC164Qy1VZydc2JXqaW6OwWKCer39iEj3TMBEQMM12x/KjjBcKpSbZy0nKyRrNWhPNq6F5NBwhB5IqdURSjIaBVJGYPzdPbiqHzW8jMBYgvC/c6ZrvFo7vfhd+6ZfgNlPhzHQGW86G6TAxbAZOm5PegUGyuTyyLCOoAtKwxMLZBQKXAoQHwndMIhxdDlwRF+V4Ge9A+5qQsDBLKS6cO8eDD7WTvVq2RuRopFNYUlWV0dFRrl8+S7PR3HaN6ert4rlffY7l5eVOMqU3GgykL1A9HqFeLCHXW4S6vUyVp7i8cpmhlSFUVd0saLC+1oii2FlnfD4firI9Dfx27DlZ4OxfTOENOztdvfh8Cm+3h5HxO8+m6HWd+Z/OU5+pU3fUiRxoMydqrRrJTJVmS6drNIBlgVVrYS9WaVwpUCuqjB3vJZfLcerUKeLxOA888ACKopBIJDqdVr/fTy6X+1DqybcjFotx4OAohw7fTJgCXS5M0+SFF15gcXGRl378Es14gOvn1kAUOHiyj6d+7sC2iqeqKvPAZ8Z58b9eYO16GlfIgWVYlJJl3N0unv7SQfSrGSr1Cg63A8MwSJaaxBNFQm472i1rlE2R6PHYWSvUuLJa5LHxtqCDM+ykGCtSWi19ohgD94LYcp7sUgFXyEEpUecf/l//bwTDGsViEU2ReHpfhJPDXcwkyxSqzXbx2K4wEnR2xlc2cGYuzdypGP2ihqPHieBQsJoGzUSFxZfneMGh8o0nR3d1XxjFIpNvvYX8a7+25TGbzUZfXx8zMzMMDg7yzjvvcO3aNex2O1//+tfxeDxcv36d4eHhDyUg9MiTI/hDTv70f3yHZ7/y2R2fp6kaIiLFUgm73d7pPt76/crlMnv37kVVVQKyQa/PxrWzScZ8bqyDPsyIgmmZmH6NejHPYq7BoF/DLKVYKm89T4qioGkaqqridrt3FC8aqSVZWFgEScRsGFu7ehbUmw28Xi9GsQF2Gewqrh3mxCORCIuLiySTyXb8+Ad/it2+db81dQNFVXC5XRjdJuW1JnOLSyzOzdJsNjl27BgvvfQSmqbx3HPPbaHffhgcPBThbM8c+aUcVblCKLy5myeULSqSQTZ5FaOpc/XqNRIrMrNXknRFXDz93B7c64mcIAgc6vPxwwsxTMtCG/bRWi3RSlSQg3YEScQyLYx8HQwLbdSPIIlMLcZIXD9DLZ3ktddeo1gs8dnHf4sb765gGSamCZdeW+CBL0zy1F+zUfoGPrZEr1ar8a1vfYtvfvObn9hET1TWq8qiRK65cyLQ0A2csoisSIjKvVf/ZpYs3vqzd6AB/hEff+9Xj9EXvjvNymtX8PZ6mfeYjL/5HaRWi8v/y//Cp/ZszjKdTmfH3PzMmTN0dXWxuLjY6dRcunSJsbGxXfvTbUB1qgw9PcKZV2dY+KlOqeHk+9PzXP3ATc9qgcNHehjpdiNys+Id6bXzmW/uIZWKE0/MU2/4tlS8Y7FYZxErFosEUyl8jzzCk79+P7LcNmwulUr8m38zz40bN+jt7eXAgQP3dOwAfX47pxdziP0eWmfjO1ewrfb1HA366dINMqUGmUqTWrpGrd9N3C0zEHCwpAgY9RZSQ2dyZIDm0kXi83n8DT+CaCMej6MoSjupW0e5XKZ/XcVwbW2NWr6GrCjsP3CERiXI+R9fpSfqw9PjAtvO15YgCtgCdrp9NgqLed7+9hV03eTTnxrdNtnTdZ3Lly/z9a98EZvNTss0O14zpmnj1VdfJRAIoKoq/+H//D5SIULTtHD5bNhORii6W/jWzU7lkAM55KCVqKL0uracQ6tuYDVN8EiYjTYdr6aZFOtgmQb5UhFTsVEvZYnFYvT1b53ndGkKS5kKeyNulPVKvyiLmIZJvVDH07e90MLfNIiSiIWFJLUpm03jzpTX7WDpJq3VMkxlqXltaC4VT8CDb9C3RZhGQEB0tDsUIaWbG5eTaOkKlVSV7FSW6Mko4QPhXRuQ+//X/xUGB+HLX755PKZF+moal9eF7tABgfeur5AwAzTsLt6cSXNswE+lkGHg4ACFhQKVRAVXZOd5CUEUCO0LUVgs0Ci1fTU3ZP2z2Sxrq2t4VS+yJtO1nhRtVLwbjQaHj4fxdUk0m3nm5+dRFGVLEWhtba3zdy0eJ2Q2GPn5+xnSZURTpE6d199+nSNHjtDf3/+hZkjuhIefGmVtPkf8Rrvqqzd0RJvAfZ8Zpye68/Vs6iZLby2RvJQkNBGiWC7SarVwOB3oRZ1KucbGNxUEEBwKol/DWK6S+GCV/hEn+XyegwcP0tPTg2VZXLp0qWPP0N3djcvlYnl5+SMnerquI4ritvN3oti2S3nkkUc4++Y8zUQJpd+DaViceXOB3hE/Bx7Zfo7z+H19KKrIqdcXSC/mEUSB8Qf7eeipUfqjHi69toTdb+fhhx5G1VRu5CwqgrwpyetAaJulp8oNMtUmQZeGbJPbRd5c/Wc20Rsc6SI04ie/WqJ3T4ihkTCaJm9StWx7tt15lKOhG5w9s4qWruEc8nWKfYImo/V78M7lmbsYZ/V47ybJ/Z0gvvMOj/7JnyD+4R9u+3h3dzc+n4+1tTW6u7t555136O7u7qhp7payue1niyIeXxNRSfP66z/l2Wef3fS4aZpYlk50j4+LL87hijioVrKUEzUcbjsOb4ulpaW2loDdzshIm70gSwL7fRZL6TWmekYZECUCo90dn8BEsY5HqfPckUGGej7aPTcadvFOxIkeK0Omihi9TW28VMTtdmNZFmauTn3Qiz/sYDi4c9Hv+PHj/PSnP2Xf/v3Ioky5WCbQ8m1KNovlEm5X+7MEUaBSrlCvN3j66ae5fPky3/ve95BlmZMnT1KpVDpU9HyxjCirSJKIXZHu6iN4K3r8Du773Dg//o/v4KzINHN1ZIeC1TIpJcuUdRPfPicRxU93sItv/7fXEZIRBEFk6Xwc0zD52jePdt5vvNuF36mSLDaIhJ04TkapX0zSipUAAcGyEN0atmMRlEEvhVoL0TJ4YE8/rYjaTu7HH2LqvRjubhdOXzuZzcaKnHlploNHewh3f/wjFPeKj5W6uZ3k9icJNl9bPjiYrrBiWbR0sxNkbsAwLeotg3FFwRV23nOFvNLQufDGIo6mgLfXzdr1NBfPxen7zO4vhlarhdxs0tK0HRWFenp6KJVKHDt2jBf+8wuUz5j4Hw+wsrzC9773PX7zN39zx/ffGM6/lZ7UarVIlZu88c4a8SsZcHbRE1BRBInCO/MkTTj3wjUCk34ee6yPsZ62gpXL5aK7uxtBOEA6naZcLm+ZB2k2mx2hhvn5efzZLGv79m2qALndbqLRKMFgkMnJDzf4PRZ20eVUqJigOhXMchNpG7n+YrHY6RhqskTU7yCoKZQbFsU9QSSbyMmhABGvjZDLRsCpsjZ9iZVWmqliBrWq0eMMIUoiKysrDA0NYXc4MHQDTIvVtTUK+TwAiqQR6Q9TLEtMvzzF+J4BfFHferJZpVatAqCqGppt67EKooBv2I+4VODUj27QHXFzdP9WIYmrV68yPDzcSe418WYAJIoisizzT//pP0WSJP78909x7myMwKif0lyOXllmpdToJHqCLGI/0o35Tgx9tYwccW5S4LQ26LINHbPUQOnzYGk1aDax2yRkwUWtYeCUBWD7NUOVRWotg5ZhodxyiQuCgKl/cuaFVbfaVgczTPxOlYVMBR+7nwEzKy0asznqqQqiJuHr8yKWGmhe7a7qo8vzZaZPxZFkCe3JEVyywPwr8xRXigw+NohtB8oQtJMLQQChVoPbCkKVZIVirIgz5CRfyZNqCMxmW4hWC7/Xw3KuSq1W48HhAIuri6xdXiN6Irol0bMsq0NLajabNFwN5AGZ2MUYoiIiOAXuu+8+6sU69bU6DbFB9/3dFKwCxZViR+DKbrfj8/no6+vDsiyWl5ex2+2bAtgNdESipqfRe3tx3ZLYuHDxta99DaAjwrDT+vphEAo5+YX/+T4unI6xupin2ijx8OP770rxyS/mSVxK4B1sJ7oBLUB8LY5m0zB0A49fRpCgUW6iuVRarRatqona46I4v0ZmNsDoiTaFqVQq4fF4OjQ3XdeJx+MYhsHa2hqhUOgjVd5jsdiOFNS+vj4OHTrEAw88gKt2kTdemSPis2NZsBavkM/V7/jeh45EOXAoQrHYQBLFTmW+UWxgGia9Azcp3dOX1rDfoaiiKRJ6pUm1YcAtl+UnaW25V4RCTv6nf/wgqUSZvgFf2/LnQyBXaZGLl3FJ0hZGR9tYXaMYK5EqNXaV6JnlMrqqslNZpb+/nytXrnDixIm2FYtpY3x8H6Zp8od/+IccPXp0h1e2YRjGJgpks9ncVGx+6623yOfznDlzhp515fJbu/eqqvLIU0PUSy1Wp7LQkAn1djF+wsfhIyOb7hePx4NhGAQCAQYALXaOM4EhsobIzPq4g8smc3zQj7mW5Pf/j/8nJ0+e5NixY4yO7kwzvBN6fXaGQy5uREoESw30VBUpaEcQBFqtFqIkISCgr1XAq1II2Xisx9NRm9wOoihSLBb51//b/4ZNOI5H9pFKpohEuhHXWVLVSoVoT5RGvUFqNUvfkV4O7d9LIpHg1VdfZWxsjM985jMoikKxVOLNd64wdTXD/KUkTsWJJIv4B7wcPNHLgYPduLbpGm6HB46EufyAA6saIjOTgXQFSxRw9rq57/4+RgcEHNohBgcHeeFH13n7O1foOxgkOZslvVra9F4em8IjY0H+4nKcTLlBV58HOeRAT1Sw6gaCIiKHnYhOhVK9RaJY47n793Hiy0dwu904HA5een6Kd2audJI8AF/ETfx6iuXFws9eovdJhyAIBPcE6ZrK0KcILBbr+BxqW8IeqLYMspUGPQ6VLkEgvH/3lfENiILQ9rAz2zRRBAHhHpuCuq6jNBrU3O4dA5GBgQFmZ2cJaAH0lAfLPsgf/39+wrzxAX17+7h69eqOKniCIHRoAzabDa/XS7LU4u0fX6R0LkuvS0M7Nox4y0ZhtQwa6Rr5cznerEt0/WKY8eDmCzwYDOLxeJidnaWnpwenc2uSXMzn8Xk8yPv2bXnsV37lV7Db7Vy/fh1VVQkGt/LL7wS3TWF/1Msb0yn6B73Ur6QRNfmmiTfr8wICSPLNf7MME1JVAmMBWhEXT/e4+bkT/ZuSVbc1wuHDh1lee4/KlEFqNc3Jh08yNz/XttUYHia+FiebzaJpKpIkEQ6HqcVaOKIOrrx2neFITzvJAxDAfkuA3Ww2KRWLAEiSjMNu3+Sh5+73UL+R4fUf32Bo0If/FqpNq9XiypUrfOUrX9nx3EiS1FFYHNoXYvpSnMJ0lp4hHycP95CYS1OotfCuzx3KXQ6cD/RSO7tGK1ZGkAVElwpCm8+uZ6soqgtl0Ic64CFieYk1Uhimheny0KuKDPm1O6piCZ3/3AKLXdGB/qbA5rOhulSaxSYDAQeLmSq6YW07/3s7zHKLxlQGo9ig7lIIuDVcNolSxsC+m0q5KCApbVUwURJwhpzYvDYyNzLoNZ2RZ0aw3+J1ZVkWC1dTXHp/hfhSDgUY8tzHobrMrSlTq9ZCb+goDgWxJhLP1lBVhaDTTqlcJmB3EMvkeH76HJLZRKtrrMyuYPVunV1TVbVTEAoEAvR+tZf0/jSpKynKiTJ6U6dSaeGZCDN4opfogTtTQAVBYGBggHw+z8LCAgMDA9t2lyzDoHHffTtugCMjI8zOzjIyMvKx2A1swOez8/jTY6ytreHz+e7q1WVZFukbaQRR6IjEWKZFIplgemqaffv3sX//HtaWS2TnK5QzVVotHckmMrK/i0g4gJW2sCyrY3y8d10dF9oiA7faHBSLRZLJJABer/eOqqa3Y8P/c6fzVa/XO/S6ngEfdlFgbbmAZZk4bBKR6N0DIlEUt8y3C6LQpi3fItYiiwLNOyRtGzXpLYf6yVlaPhR8Pvuu9AHuhA0Nix3TaAsQrF2fSqtcRte0HRM9u91Ovd4uAqiKF6Uywff+6BL/VfwvWJZFIBDoFHC2m4+VJKmzzvj9/i1U6+eee45HH32UQCBwx/3oV34ryMpSgWq1RW+fF7enzTpQVbXjIy2K4qZZOm+jyKf3hVAfGiRXaSGsd5ODLo3KmI+rZ9/j8uXL3H///bs8W1shCAKPT4TIlJtkTOhaKtJaLCDYFcq1Ci7NgZ4uYQU0UoMeRsa6ODl8Z6p4uVxmeXkZSZKQbUVoBvA6fCTiCXp6ejBpj3Nk8zlEXcDucHDsoWESiQTf//73iUQiPPvss8iyTL2p89ILC1x7cxG92MDl1jAadVpVk8V3cyyfXeXUaBef+8VDjN6lmwxw6eJFvvrZ4wS7e5hbLlAqNdA0iaEBHz6HytzcXOe5fYM+7D4bK5cSSIrI0N6twi3HBvw0dZPXplLkay1CLg3PwM11r9rQSaQrmJbFAyNdPDnZjXpLA6hdMGmL4m10J42WgSCJqHcQx/mrxMeW6KmqyuOPP47fvzsFx7+p8I/4iR6LoH+wiubSiDUN1upVsARsqsSQQ2XAEug91H1PxsMbsKsS9z01wlvfvUYqUyV0qJsjx+8sMnI7dF1HajbRVXVH3nY4HObdd99F3CficKqE3V4alSoHDhzA2+PF5/PdUe78VlQaOj/4wVXyZ9cI9XqQtxlWFRQJW4+LsE8jdS3Dj757hV/+lWNbuP2qqjI2Nsbq6iqFQoFodPN3L5bLfPv3fo+nt/FY2giK9uzZw8WLF1EU5Z4CEYADvV4uxQqkowKBuk5zNo8UtCOtV4JLxSJ+/80qv1nX0RMVlB4XtXE/iiJwbMC/Jdjs7u7mG9/4BqNjY/zbf/0iZkFAFESGBge5cf0Gp0+dwjBNAv4Afp+fcHc31XQNXWuytLSCX3ARHNn5erp1gNzQ22pTlmUiCCIOhx1RkggM+ohPZbh0Mc5jD938ba9cucLY2NiOlfpCocDExASrq6sMDQ1x5JFBvH47hXyNoYkgvrCTNd3grZk0TlXqiAzJIQfOJwbRExWaCwWMQh1MEO0KyoAHbSyA5NUQhLZ2Z8jnYiVbw2NXGO92d9Q2t0O5odPlVLHd1lG3sJDtn5z6lObW8A35SF5O0j3sJ+BUyFWbhO5i/G7WWjSmMxjFJgRsUNfpdtvQ6zqyTcYevHuwNrE31BYukQTG1oUGJFXCP+YnN5Nj/pV5xp4dQ3WpWJbF+y9M88aPpijXWqheDbPeYLHrfq78OMXnx5IM7Wt3njaufcuy8Hl9NFZjGJaAKEl43R5ml2N4/F08cugJkiuLJG4kCHeH7yo2soHQ3hBdE128/8osl99dZi1R51oxhn0mzZ6TvTz0xMhdg1Wfz4fb7WZhYaFTYLoV1YceonH8ODvxMQRBYHh4mLm5uR0V/z4sWq0Wuq7vypC5mqpSWChsYo7oht4RsCgVSwiSwNPP7uf8+XkaVcjlMoxMROjv70K2ZHLzOVanV5G9cicg3Q6a5iC+WsHucNLb76HZqLK0tAS0Z6VCodAdk+zV1dU72jmkUinC4TCGYTB5spfH01WunI4hCnDo4UFGjkR2fO2dIGkSotqmwmq076uo187FWH5HQZByvYVDlQk42s+3LKvtQfYRjKv/tsDvUAlEPWSvZ7DfNhNmmRbVQh1tbxfdd9Ee6LymUsG4y3yd3W6nWq1isykEuwMIqk6mGmRgYACPx7PrtWU7+Hw+Kk2Rt96fIxLpZnjYj9ex9boRRbEjvLKB/v5+isVihwEgSdImhfr3fumXePTRRwm7bVtsdZxOJyMj7QLxR1W17/Ha+eKRKD+RRWIeDTVXQ4zlUBUHTaeNcsCGGbQz3u/jcwd6OgXbneDxePjH//gf8/777/Of/+u38YweZelsHF+3nbV4vF0QMU00UyOXKNN/PEo4YvH888/T3d3N448/jizLtHSDH3z3KldfmsUbdODo925eQ0yLUr7E2uVV/uv/t8DX/8EJ9k3uHA/XajUymUwnMd43spX2uuHxCrD/YAT+wXGW53J4A3ZOPrh1fRIEgQdHg4TcGhdXCsymKsQLtc5xqrLIUJeDw30+9vZ4tqhm7znQzemQg+RMlsCAB9MwyS4UCI0GGJ/cmlj+deBjW9X8fj8//elPP663+2uDKIsMPjqIrMq4r6aIJCtUJBEB0BAI+B10jXfR/1D/toPju8Fj9w8wNBygUm4Q7XHj3WXLGqBUqFPMNpHrDfQ7UDdFUcTj8dAQGjzw1X3031hlPilx4NEDFIvFTVXdu+HGUp7E+TjBoGPbJG/T59oVuqJuEldTXJpK8cSx7f30otEolUqFmZmZTap1pVK7tX57YHY7NpSh9uzZs+OsoWmYlGIlqrkakiLi7fPS7bHx7IEIP7qwRnbER5cq0Vos0szUaEg6NruGWW1htQyMQhNBFVGHfVTH/FREeGoizPgOrXhZlnng/vv5F/+6j2//v08x9eMbGD6RpkOnVqthGAbDx47hcDopx8skk0Wk/haBZhemv7prI3BJlnC525VHy7So1qqYhtE+Bsvk4qkYD97fjyKJNJtNrl27xle/+tUd3y+TyTAyMsL09DTQrpCP3OYj9tBYkHixzmyqzGjIddO2Q5NRB7wo/R7Q28qPVsug/MoCSOKmRV2RRAwLQh7tjkmeaVo0dYPBLscm/r5e15E1+Y6Uw7+JCE4GSV1LYdRaTHR7+GA+S6nWuqnKejtMaz1xbiAG7GRr7cTQ51Spxct4+jy7UtGUVWlbWqAorfuHTWdZ+WCF4SeGWZnJ8OaPp2jaJPqGfO2NvFzGMJPEKyYvffsyv/J/fwTVqaJ5NFSnSrPURPNodLtkCrpFolDHNFrIdg8BxcBjV+g+dJAetQd71711Ed54dZY3v3cNSzfxRL1Iikg11+C9P7/O8nSWn/u1E/j9d35PSZIYGRkhmUxSXO+Gb6BUKm1L7bz99YODg8zPz9/VHuJesEHn3g0qqQqtagtP/831UFEUjh8/zuDgMK+enyFzOU6P10ZPn4tkPM7BY/twuVzE43G6u7uJT8fxJD1MTkyysrKy7ed88M4S7788Q2YpjyRLuMNOjj02xCNPtjua9Xq947knSRLd3d2b9p5ms9kW4blLt727u5tEIkE0GuXBL+7hxNOjILRNzD8sJEWia6yrY5QO0BdwMJeutOlYtxUbW7pJodbiQNSDYz1JaeQbaB4NV8/OHZ2/QxuqLHLieJTnr6QoL7WLEKKzLcbSSFQo2iQOH43uqCZ5O6xKBeMudOGBgQFWVlaYmJjg1/7pI7z++uv8vaP/kLW1tTsWF+6GWlPnL34yzdS7y1RSZSR5HmePixNPjfL4Q4O7mh/zeDy4XC4WFxc3FVNN06RcLuN279yp/vmf/3mgXSRZWVnZlf/wTuj3O/iF+waYHixxbinLXFDA7fEhSwLjPjsHe72Mhly7Vn4WRZFDx07yoBlhKlagnK9SXCqilSu0GlV6IhGqVovuw37ue6KL1197mbGxMfbu3dspYl28nODa6/P4up04uraJ00QBd8CD0+MicT3Fq38xjV1rIgoCDoeDYDC4aU05f/48R44c2fGYM5kMXV1dZLPZDkNp/8FIO+G7C8bCbsbCbhLFOolinZZhIosiAadKr8++47UQ6XHzzM8f4vUfXCe3VARJILInxOd+7gC2D5kjfNz4WI6iXC6Ty+W2ndH7KDfhXxckRWLgkQHCB8LkF/NU01WwQPNp+If8OO5gSnk7LNNqt3HFtkHrxkU7EHZB+N42lakrSf7iTy4wfa2OXztCvyLfcYZkYGCApaUlDh48yHvX3+PJrz2Jw+EglUpx4cIFjh07dldakmFanDsTQyrrKHcQDLgVkkdFi5e4eGaVBw727LiwOJ1OevoH+fYLb3IxKyLZnZxerKLXbcRrIh7d3NQivx1Hjhzh7NmzHDp0aItwQj1f5/SPbnD9whrZchNFEOjv93Ly6REmT/RiHYIXrsRZ6XPRFXVhS1epXInhQMKq6wiyiO1AkHrQzposYFNEnpkMc2IX1IL9+6L80KWRr+k016rQrRLp6aFaqxG7voZD81CjgWdM4PFPP8Sr//kSdt+HE34QRGETBdbwGsSvr3Lu8jQjvV0sLS0xMTFxR6n4jWtAFMVNBtm3wqXJfP5QlB9eWGUmWabXb9/E8RcEARSpTdfRZKSAndZqCcmtYpqQqTSQRZF9UQ+NlrEjfdE0IVGsE3bbtsx31LI1bH7bJjPkTwI8/R66D3azemqV/okAtV4PF2MFWqaFz65uoY+1khX0RBXTrZCvNQk4NMZCbvRiA8WuEBgPfGT6qiiLePo9JC8m8fZ7uXIqRrnWJDoYvklf03Uk0yQw6CGxXGD6SpL99/VhD9jxj/hJXkmieTTCLgW/38VMokSu2ORgf4iJbhflYoFqporda8c35Nv1sS0vFXj/+WkUm0yg9+aaY3NpuMNOYpeTvPXKDF/4+sFdvV84HKbRaHDq1CmCwSA2m41Wq7UjG+JWKIpCJBLp+PXdDsuySE1nuPp+jGKmQqjfy96TffgGtmcaZLNZ/P6tjICdYDSNHemETdmO5e2hoZtcXckRmuymO9Ld8asLdgWZn5tHFmW8Lm/neG/HmQ9WeOlPLmCZFsH19a2YrPDaty9jAY8/3WYDdBRLdZ1kMtnpKoZCIZLJ5B33+mQySTgcRpblzuuAj82z0D/qJ34+TqvaQnEo+BwKR/p9nFvOE8tVcWoKogC1lolpmoyEXOztufkbVdIVIocjm6jMf4edcXQoQPZLk5x6ZY7yUgElZaKLIgQdTDzSz6dP3j1hWVstcvViHH9WZ+guiV4wGGRqaoqJiYmOyuZGgjU9Pc3g4OCHmi198eUZzv/wBh6XSu9IF5ZhUVgr8ua3r+B0qtx3dHdsK1EUGR4eZm1tjcXFRSKRCOVyGeCOdNANRKNRFhYWOvfJdsjna0xdS2HoJiPjXVusqqC9Tx8d8ONsZPjykSMYloAqi3hsH87YeylbZTFTZaRpMjIYYGo8QNgUqCxcZ3g0xKNPHiIeu8Ybr73AsWPHeOihh4jFYqhqmyVy6XQMoWlun+TdAlEW8fW4yc+XMAUvw4N+qtVqx/ZFURTcbve2LLBbUSqVGBoaQtM04vH4Jlr6btHtse26G72Bw8eiTOwNsbSYQ5ZEBocDyHeIXf+q8aETvXq9zr/8l/+SP/iDPyCTyez4PGO90/BJhM1nI+K7dzqJZVmU42Vyczlys7n1Wbw2jSu4N4hv0Peh5OHPv7NIOlZEFyxmhH5sZo7aygrvv//+thzvvr6+Tpd1dHS00/kKhULY7Xbef/99jh8/fkd1uUy5QWImg8upbBLcuBMEQcDjs5OZzrGarzES2rrQ5atNLiznubRaYKag4WxVMZo5qqqfpujhzy+sEZnPc7jfx8Fe77YdIFEUOXLkCOfOnePo0aOdpNfUTU7/8AZvv7VAzafi6nVT000urRYofPsKX/TY2DsZxGtXuBjLc32txJVCnvCnxijT7t7qokDNNHFrCoe7XRzq9W77PXb6/v6wk/JogFrRjmvQQ6VWJuT1UagUsPWWGBmy8eRjR5lfMqBlIHxMCn/ugJvqiokq+wB46623ePbZZ4nFYgTD3aiytGnBX11d7SycPT09rK2t7VhVDDhVvnKslzen0pxbzpEqNej22DreSrdCHfTSWCyQKzWotAy6XBr7ox66nBqnFrLE8lVsqozXpiBLbX57sa5TrrcIezRODAU2KeZZlkWj2KD3vt57nov964YgCERPRKkkKuTmcowP+9BkiSurBeLFKook4dJkJFHAqLWozuSomwayJdPtsTMSciLUdVo1nfCh8MfW0VRdKrVcjdj7MeJzGUSHutmeYH3WxO6wkTMbZG7xTgzuDZKdzVKOl3G6nYDFpM8iPHHTM8/tcLM6v4rtoO2eAujL52JUczWi23QjFU3C0eXgxulVnvj0REeQ427QNI2hoSEymcw9C6zY7XYCgcCme2UDi2dX+cl/u0Sy2MDURKQrSW5ciPP5Xz5C8DZfro05uY+rOyis/zHMdiAkSRKSKeHz+kilUsiSjLpB7xfYVmXY0E1O/XQOo2USmbh5vMEhH+nFPGdfW+DEgwM4b6FAyrLcOQ+WZbG6ukoymWyv+x7PtobItVqtE8Bu2IB8nLO2rogL74CX7EwW/1g7kR7ocuC2KSznqsRyVUwLIl6NwYCTqM+Gsr6fNYoNRFnsqLn+He4OWRJ55kgvkwN+rs5nyaYrOJwKe8aCjIZdHUXnO+Gl713jxpuLhHIuwoLAlTffRFXVbWMZQWjPYRaLxU0qm6IoMjk5ydzc3LYU7TshXaxz/d1l3HZlk4pzYDRA8lqa028vcuxQZFs/5J0QCoWoVqvMzc1hGAYul2vXM75DQ0NMT0+jKMqWEaip60me/5OLFFZKWFg4A3ae+Nr+bamIuVyOgN9HwPXR94nOPKYoIkoiTr+NiM/iwSceotFoMDd9hrm5OaLRKA8//DBws3i8migRu5LCHdqdYKEtYCe7Vub6pQSjg34cDkeneNRqtfjRj35EX18fc/OLeAJdOO12NFm8ORd3S5FaVdUt/qZ/2bDbFSb33FlY668LHzrR+83f/E2+9a1v8eUvf5lHH330Ez+b93Ghnq+z9NYS+YU8el1H82ltjzkLyskyufkcji4H3Ye66TnWs2u6HoDbZ0OwLMyqhc2ocn32OotvuNB1nfvuu2/Lxmm328nn80iSxGc+85lNj7lcLo4fP86ZM2c4cODAjvSChm6iV/V7nl2QbBJWoUGtoW95bDVf4/lLayzlqvgdKv1elZ5IENM0yWQyuFwuJEUjVW7wo4urzKXLfHYHXrksyxw6dIjz5893OpTFWJGrF+LU/CrRoLtTEa/bZNYWClw/FSM40UXUZyfqs7M3IHPVByXsVJs6pmXhUGX6/XbGu91buPW7wee+cYj3Im4cHo2Hnxjk9Tff4PEnnuQH3/sOH7z3Dl883BaWyRdynU3s44Coigi6Sa3aZH4+zuSxB1k2vFy7nif3wSqSAKNddu6f7Gcw5OmY1EPb4zEWi93x/T02hc8djDAadnJuOc9ipkIsX0MU2l1PQRDQDZOGaCLYJByZGof3hRkJuTo0qQdGAixmbSyk2tQqY31jcNtkjvT7GQo6tiSP5bUyzpAT/8gnc53R3BojT48w+9Is+dk8ff0eevZ1s5avs5CpkK+12r6YiSrUWvQP+gi5NZyaRD1Xx2yZnSLRxwl31E1uJodQacHt4hXrnRdzfe5kfmEW/dUY8Xicr3/96ww9PsTCawu04i3StTS9w70IgoDRMqimqjRLTYYfGCZ4MsjCwsKu6Yqp1SKyTdlxbXT5beTWSqTTlV0nehvo7e2lXC53zLt3m/S5XO11NpFIdObcWrUWp1+ZZ7XapHvUjyKL1JoGi/M5zr62wDMjgU3f4VYfv91CUqUd1ay7PTYO9HpZK9SIeu34NNBFG4qq0Gw0adKkt6+XmfdmkBRp2y5mMlkmFyvh3kY92tPtJLdcJLacZ2KHAGZD2e/kyZNAW8xlY65P0zRCoVBbPOyWzw0Gg6TTaUKhj29+RRAEeu/vpZatUVgo4B1qzwL5nQp+p5eDvR5ME6TbWATNSpPSWon+B/o/MZYtf1MgigJDQSdDd5DpvxNCUTdrERf2eJpUucxbb73F/v370XV92/uyu7ubl19+meeee27LYyMjI8RiMRqNxq6vq0S8RC1do3sbESBnl43CYoFCrbWF+nsnSJJEsWnhDHTzwanTpMo6zbswk27F+Pg4V69eRVGUTifQ0E1e/cENCrEi4Ym272N6IccbP7jO+N7Qpnlly7LIZDIf21zxYMDJWNjFNGUsl0yXS8PZWmNgYA/Xr1/n1Vdf5amnnuLBBx/cUggrFhvtGPgu3bwNtG0pRAqF2pbHGo0GTclOzTvEXyzkyV6YwjB0uhwKx4dDHBmJkE3G6enp2fS6j7ug9EnFh070vvvd7/Jrv/Zr/Pt//+8/zuP5RKOWrTH74izFlSLuPjfqbYPg9oC9PVOVqbLw+gKtaouBRwZ2new99tkJdNPkrdfTPPf+a1z/wkNUQyH6+/s7nlPQ3kgdDkfnpt9Jfnijenb27FkGBga2pQx0Kjr36pxhAQJbqmHJUp0fXlglXqwzHnYjiQKpRglrPdgPhUKUikVqtRq9gQANl8q1tSKmBV8+EsWhbr1kNU1j3759nWSvlK6SrzRw97o30Z5sikTGLrW7onW9MxdSyaX47ImtKp8fBQODPgZ+uX3eFxYWGB8ZYG7qMsGAjy996Uu8e+YCB084uLGUotzQaVRFLKWFTRHRZGlb4+fdwgKqtQavXl3A2b+PZjaNx6Hg8/poGRZXsk0uvzHNiAcmveaWSujdFkdBENgT8TDZ7SaWr5EoNkgW6+SqTQzTwqZK9Hjt2Pq7qL+9gtupYbtlYF9TJCa63YwEXeSqTXTDRBJFfHYFdRtfyla1RavSYvDRO1sC/E2HI+hg/LPjrLy/QupqClEW6Qs7GexyUG+ZNJo6yXwMa9CPK+igUWpQztTQvBrdB7tx97o/9k1LlEQkm4TPrkKyTLNpoG50z3UdRIFCqYnLpWFJBd555yxut5t0Ok14IoziVIhfiLPwxgJ1tU6dOojgDDvpu7+P0L4QkirhcDuYmZnZlYqlrEqYxs7iBIZhthVFd6Fcuh1cLhcDAwPEYjHcbveuveN8Ph/pdLozB1LL1oivFtG67B0bHrsqkfdoJJbbfoAb12u1WkVV1XvuJroiLjSXRqPY2DKXKYoC+6Ie9q1T6lOpFKFQiHQqTaArgKEbpGNp/N1+qmIVe9O+hcItisLOSpMmIIBwh9/rViVNaM8rbawljUaD1dVVlpeX6e3t7SSadrudVCq15b30uk5+MU+z3ERxKHgHvFv20DvB1e1i+Mlh5l+dJzeTw93r7lBDBUHgVka6aZjUsjXq2TrR41F67++9p6Lr3+Gj4zNf3MOJhwZo/Or/D3NgAK/Xy8mTJ0kkEh16r9PppKurC0EQqFarHTXw21GttZifa3Hu3RtUi3WG9kQ5crKXyTuIYciKhCC37Xpu7z8aLRPRLt9TN28hXeHCSp7TUxkkW4NiyUZT7OXf/vA0jx0Y5NhQcFfzcfv27ePixYtMTExgs9naxZjlAp6oB2l9f+wa8JGcybK8kMN35Gai92GKSXeCXZX4ytFephJlWoZJn8/G9XMrXLp0iTNnzvALv/ALFItFdL0tEHVrF+3D3E0WFsJtrzRNiz95+QPiVpir1xN4HSpBv7fNkKi1+OGlBG9PJRi3V3hIVTuiUaFQqCMA9bcdHzrREwSBY8eOfZzH8omGXteZ/+k8xVgR/5gfcYcFQhDbMueyTSZ2OobqUek50rPtc2+H06XxxOdGWCu8z8S/n2PO9zk+97nPsX///k71ZyO5S6fTXLt2rcMb30l8pVhqIolR3n17hrGJAvv3j2963KXJaAE7jZkcth016raiXmwg+224bunCGabFi1cTrBXrjIVdiOtBqygImA0dq2aAIODyuDBMg2Qigd/vZzjo4vpakTedKp/Zvz2V1uFwMDo6yqVLlwgqYSSgdVuwaFkgGFbb6H7997mXTsOHxcLCApZlUW00GTz8EBcXskwlS5z5ozPY8iriVAGp4CDvqCHaJTSPja6AA69D6dCLdguzYWLJIq9dmyOvhuh32fDc1gmNeG3kqzYurqSI9g7RaDZZWFgA2vf17YvjTvNMgiDQ53fQ59+5YrdiwvLbywiSsCVQlSXhruqTrWqLwlKBnmM9dE1+8qlVNp+NkadH8A/7SV1NUVwpojd0BEGgUWxQXSigeTTK8TKqSyW0L4Sn33PXgNcwLHTTRJHEXQkI3ApnyEmg0iISdhGbyeDt9+FyqVitFnl7gMZamaOPDPKFv3+Md997l0qlQjgcbs9pCTrpQJqxL4zRH+pfl+OWcUfdmyi2qqoyMjLC3NwcAwMDd6SLD02EmHp7BaNlbEvTLSUrdPV7ifZ+eGPrDRuGbDbLwsLCFm/PnRAMBonH4xQKhbY/oSajNzePJxgtA1WTNh17PB5nZGTkno/T0eVoz0NeTe5KgCeVSuFxezqem5mZDJFDEbBDpVLZwtwIhZ2EBr3ErqU3+UAB5NdKeCNuBu7QRY7H4zuun5qm0dfXh2EY9Pb2kkwmO9Lztdrmqn0xUWTxp4uUYjd9rlyRduLm7tmdD1Wj2EBxKAw9OUT8bJzCcgGjaWDvsiNrctvPUjep5+u0ai0cAQdDTwwRORrZcb/+O/zlQRRFQmEX8UYdfD5+5Vd+ZYvlUrlcZmlpiUqlwvT09LbMo3KlyXe+dZaFs2vtxEyyOPfjq8ycjvHY1/bx6KPbU6UH+7z4B33krmcIudROom+1TMrZGhOfHb+rOuUGLizneelqgmpTx6aIDHW7ECJumrpJslTnOx/McW0lwzcemsC1C3bUgQMHuHjxIvv378dmV5A0Cf0WdlSr0UJURLRbxD7q9TqiKO44j/9h4VBljvT7gPaoRyKRYHp6mscff5zJyUkMw+Ctt97ivvvuY3l5uRMreP12VIdCvdTAtQulbMu0MAwTX2Azzf+NazE+WK1zcNK9JV7wOVR0v52p1Ryzuo9jgtqZ65NlmWq12ollTNOkVCrds1L7zwI+dKL3pS99iZdffpnf+I3f+DiP5xOL/GKe/EIe37BvV5uG5tZoVVokLyQJ7QntWsFT13VEXUc0TVqahsfpoZVqsXp9FUM3UGxKO8hyS0iSxJNPPsm3vvWtTivf4XB0KmSrq0X+7D+fJTOXQxDgxtkcuWdLPPLwzQTe51AZO9TNpRsZ3A19k3feTrAMk3K1Rd8jA/TcMtS6lK2ykK4wEHB0kjxLN9HnihSTWai2Pexkvx1tPEBoIEw+n29vCG4b19aKPDDStePi6/V6iUQirM6u0hd1cyVdwWmT0WQJy4JUqY6jYTB6OIKkStTrdQzD2FG18+OAYRicOnWKgX1HqdmGOf2nlxFWSgzWZLKJBpVqGZomzkIToaRjmtBQysS8GqmIi54eNz67suvyWKPcoCHBXL7EU/cf6ig7GuVmW2TGJiO5VHwOlbBT4tRCjtHQAGNDbWuHSqXCqVOnOtVTv9/Pf/kv/4Wvfe1ruCU3kirtKuDcQPRElEqywuIbi2hejfD+MOIuaSyVVIV6tk7P0R4GHx38mQnGREmka6KLwFiASrJCPV+nlquRupqinCwTnAyiOBVsPttdKdMtw2Q+XWEhVaGum9hVkZGgi6Eu5647XopDQZJEnnh2glNnYizPZFlr6gjJKg5d5PhjQzzz9QOYusnRPUfb0tVGW0AgnU5z8OBBBEHgxo0bRCLtQozaUnEoN++r2EqB2RtpQt1uVldXCQaDO4oUHDzcw5mheZJTGcITwU4VG9oiIaZucvjhgQ817B5bKZBYK2BadXp7TQKBAB6Ph/n5ecLh8K6EEyKRCCsrK/i8Pkb3hUi8tUhSlXDYFUqlBo6qzviRnk436Va654dB10QXqaspmuXmjrPdrWaLfD7P4MAgqtZ+TqPUwOvzokQUgsEgly9f5vjx45teJ4oiDzw9xo+Xi6xdT+OJuECAUrKKJMHJp0Z2NNau1+tbBDAqlSatloHHoyGKIrlcDr/fjyRJm2hV8/PzXLp0Ca/Xi9Pp5O0/epvE5QQPf+FhpPWObmGhwNKbS+z96t47rhmWZbF2do21c2sYDQNHl4PBxwaJnoySm8mRnc3SKDSwLAtREnFFXIT2hvAO3lvH8O/wlwOxWsXw+7f11XW5XLhcLl5//XU+97nP8e6773L16lWcTiey3LYLee+tReZPrxIc9KGt3x9B3WDlRpy3f3CD8YkgkW2Usm2KxMOfGeeFZIXEtRQOrw3LsKiUGnjGu3j4saFdHf9MsswLV+NIosBYt5t0uoHVNLB0C8Uu0+d3EHJr3Ihl+ePXLvIPnjqMcpcZRlEUOXDgAJcvX+bQoUOMH4ty4aUZDN1ElATKyQqDR6OMjt20ZFpZWfnYrWBuhWVZfP/738eyLD7zmc90DN4lSeKBBx7gvffeY//+/aysrDAwMEAk6KT/UITZ1+dxbUMNvx3VTBXVZ2f/4ZvF/Gylyffeu8ZwX7ST5Jl1HbPYAElE8mnIkkhYM8m0FM7FKnzpSHuuT9d1Tp06haqqnfnOt99+m69+9at0dXWh6ybqNgyxO6FYrHPp7CqCKHDoWBTXPdB6/zrxoRO93/3d3+Xnfu7n+PVf/3V+4zd+g4GBgW3V+nZLi/kkwzIt0tfSSKq0pQJtmhalho4iip0ZpQ04Qg5ysznyi3mCk7vz5NN1HXm9PV4pq6y+skrezGMaJoIoYBkWikNhNjPLp3/10yiKwtjYWIcCUalUOjTP5787S3o6S2QyiGVZxKcyzF2rYdNOb1LkPHyoh8uvLVBdK+Mc9N618l2PlzE9KseORzd1F66uFtvUvvVzZFkWtYtJ9AtpFL8TJejAMi30XA3jvRXsrR78YwEa9Tr5Qp6soTGdKHFiaPtryjQt3L4Anmid6J4q+fcaJBYKmA4JWiZSocqh+4cYPtYOOObn5ztD3X8ZME2Tb3/7O1QcERZTPmqXZ/HVDBSvhhRx4Rz0MD0zR7lSRzB1HEEvgmVhq+vYMjXqhQaL+TrlIS9Rv3Orue82KGdqpFwGPd1B3OseUfXpLI2rKcyajmhXsB8K0+gSiXT5WC60uLpWYGxdAdbpdNLb28vQ0BCtVotXX32VbDbLH/ybP+Cw9xiRvm72fWkP9sDuxDUqyQrleJl6y6RwNUU1XaX7UDf2gH3bxM0yLeq5OtV0Fc2jMfzUMOED4U+cAMtuIIgCrogLV6R97vWaTrPU3PUcomFYnF3MMZsqY1NlbLJIqa7zwUKWQq3F0X7f7uloIjjtCr/wTx5kaSpDKl5CePll+i++iu8r/y/S11Okr6VpFttrj+pW8U/6aTgajB9sswC6u7vp7+/fxCoAKJd0XvhvM+SWS9h9Np771aOoapFGo7GtxYHbo/H5XzrMD//4PImpNKIiIckizWoT1aly/Nlx7n94dx6gG6jWmvzku9OkZqvUinVUh8zV0zm+8AuH8PvtjIyMkEgkKBQKu1Jq6+vrY2FhgaNPD9Go6UxdSVBPVrHTwh1qsv/R9vHpuk6j0fhIiZ53wEvP0R5WPljBO+DdVqlyemaa8bFxFLX9WKvaorxapvdkLwP3DbCwuICqqpTL5S1iKQcORbD+/lHef2WW1GIBTAt3ROPRz+7lxP07K2ne2s0zTZOXn5/i4hsLmIZFdLKLL/78IYrF4rZ+rcPDwx0l09RiipnTM1SsCi+/9irH7n8Yr8OGp99DcblIOVHG07vz/Fx+Ic/yO8sINpmmU8VYK7H4xiJ7vrIHT6+H3vt7MZoGlmkhKiKKfef5z7+taBdASvgCdkZG/2qZE0K9jnWHYuuNGzcYHh7GZrNx+PBh4vE4g4ODtFotYmtxTr8+jeZQOkkegChL9E5GmD+3zMWzMSKf3X6fP34wgu3/coJT7yySnM0hyCIHHh/k/kcG6d/BRulWWJbFmcUsDd1kNOTCbBq0rmYpFbJYutkuWu8NonU7GYv4iRfrvHdpmkMjPXftLMmyzL59+7h06RKf/vIeVE3mxpkYpm6i9RkcfcyPtF4AuZNa58cB0zR57bXXmJ2d5R/9o3+05Z7WNI1jx45x4cIFLMvqCLIcPhFl/vQK5UQZV/fORTSjaZJPlBl5ZID+npv3+vn5BMW6zonu9p6op6tUT69h5GogiaiDXuxHuhFEkW6njelkmVSpQcjdth6bnJxEkiQ8Hg/f/va3yWaz/Lt/9/sE7PdjVCS+/PePMTi0u/220dD50z84w8qlOCAwfTHOL/3D+/9GqWvuhA+d6I2Ptzf3c+fO8Qd/8Ac7Pu+TrLp5J9RqLd54aYZCrsbkSIBGrLjJ1Bbafj1nFtvKk4oscqjPx+Atg6miJCIpEtnp7L0leo0GSUIUFxUCPS2sIQfZRgvdsLArEtW1VRxVB7HXY9g+Y2NgYIDl5WW6urpwOp2dyplRm0dzr4vFAKpdoVVvq1jdqsg5FHJx8KkRzv35NcTlEvY+97YbpWVZNOIVMvUWE8+OM7ne7gco1ltMJUubBpuNTI3GTBapy47gsyGsy/OLERd6pkb9Whql141mtxHWNNILa7w7tcrxwc3y5KlSg6lEkYuzWWrlJqIIgiIz9FAXQxmBUrqCokhMy4sstQq0pL2UkqW7GgB/FBiGwSuvvMKFtSoKI1jX4wRtMvKQt3PuFElkdHiIi6lpGksN1GYTWVXBroBNxl5pocRKZOo65iT0Be6c7OmVJmVTp+wyOT7YTmb1VJX6hQQoInK3EyNfp3ouTvOgC/d4H12GyGyqQqne6tgleDweCoUCXq+3MzdBHIrnKtScdZqV5l0TvXq9zquvvgqrED+dw3d4GNmr4tIUBEEgN5cDC0SlreZlGRatWqtNe3OqBPcEiRyN3DHA+1lDJVFBdmxekhfmslw/t4akSBw62Uf3LT5fiVKdxUyVoFvrqNw5NZla02AuXaE/4LgrNXYDil2hkqggKRLD+8MM7w/DpR+S02SmfjRFKda2yqhp67NolSZLf36BwQODlMObN3FBEAgGb65nF8/HyK2WCE8ESE5lSKyWOHRkkkwmw9ra2pYBeoDh0QC//E8e5PK5NWavJGk1DYJRN/uPRRkdC+xayW4Drz4/zex7cbqGuvD3eqiVGsx+sMKPZYFv/vp9QDtRrdfrzM3N0dvbe1cK1NDQEHNzczz5y4c4uVqhUqqjU+c/f/s/8ec/FnnqqaeoVCrbJjr3AkEU6HuwD0M3iJ+Lo/k0HEFH+76xLBLxBAF/AEVVMA2TaqpKo9AgcixC30N9CGLb/P3NN98kk8ng9W4t1h083MP+g90kE5V2Aa6eZWRk5ySvWq1uYkKceX+F9354A9WhIGsSU+8s84Im88CTdxfGsKk2PC4PAwMDCDaNZkunaZg41zt7Zmvnec35+XmuvXqN8myZYsiF6vIxGnBQy9RolpvImoxskz+05+3fBpw/E+PF/3aRaraG5lR58IuTPPHM+JbnGbrJB+8t0ai1OH7/wD0LIe0EsVaDHRK9SqVCJpPhoYceAtqNg6tXrwJt65NQKIKoX0Zxbi0ESrKEw+FgLZakWIzuqMi5fzzIvrEuKk0DSRDu6PF6O1YLdeYzlY4cf/1yCv1qFnoDiHaF1loZo9TA9dgADq8N3bQoy16azeaOli23QlVVxsfHmZub5rmvHeCZL0xiGBYvvvg8f/Inf8Da2qd46qmnKJfLf2mJ3kYsc/XqVT772c/uuJ653W727NnDm2++ydLSEkNDQ+zfG2b+mTHO/egGRtPA0+NGuC0xapQaZBYLdE0E+cwX925am14+fZ3B3h5EQcBqGdTOxTFydeQeF1bTpDGVpSo08R/tQ5JlEsU6S9lKZ98LBALMz8/j9Xrp7+9neHgYny/Aj/9ojlK6QjpZuWOiZ1lWZ62Lr5WIz2Tw9XuxDIvYjQypZJmeXdqO/XXiQ69+/+Jf/Iu/1Wo277w+zzvfu4plwNKpVY5G3JtMbWF9EUhX8DtVyg2dy7ECUa+tM7gPIDtk6sX6rj+31WpBtsESAxiaRMKmcH4uQ62lIwjQaum0KiU+d3Iv5USZpTeXGP7sMFevXt1iNBns85C4kaZRbmJZFs1ai1CPG7fb3VHkPHjwIC6Xi88+Ooyum1z5yTTVqQwunw3Vb0NYD9L1Yp1StkZNkxh7ZpQvfnp80+BxtWFQbxmbaJetRAWraSB5lbaCyC2Q/DZay0X0VBV1oB2Y9ISDVKoVpmbnGB8ZBgQ+WMjy5ukVyrNZbKkaimFhCAJ1VeINj0nv3jBf/dwR+oJOXnm9wttvv83v//7v88QTT3Dfffft+rzfC3Rd58UXX6RhC1DIlRjKFnH77Ejb+L9pNo2hvX3MZxYpr+XxDqwnn4KA5VKRFAlXuk5WzmLX5B0Dd8u0SC8WMALQPRLGrq1TNktNrIaO0t2uHspBB42FPGKtHTxpiki5oVNvmWyIi4bDYWZnZ/F6vTzwwANAW2EwuS+JrMm7Sr5kWeby5ctUl6o4Uk4ChT7cqkzfA330PdDX7vIV6tQyNWq5GtV0FbNkgknby2i5QL1QJ7QvhG/I9zPvb2VZbb/NWylq1XKTc28t0SjUMA2LM3WdZ7++r5PkpEoNDMvcImVuVyWylQaZcmPXiZ4gCejNzQq5zXyFhVY/tUSZfEBjKlmmVG8/R8VgvNdPOVlm4dUFJr44QSAQIJvNbmFxDAwF6B7tIjmTwR1yMrDesezq6qJUKrG4uLht8ODz2XnkUyM88ql7n227FZVKk+unVrB5NTzrhTanz4ZpmCxfSbG6UiDa174/bDYbIyMjrKysdFQj74Th4WFmZ2cZGRkhKIpUKhU8Pg+ZTIZUKkUkEvlY9klJlRh6fAib10bqSorcbA5RFinUCviDfqrlKvmFPEbTwBl00vNkD5FDkU3X0+DgIJVKhZWVlW0FG0RRJLI+D7e0lL+jKFMymex08yzLYnkhg9E0CKxbNOh1g5lLK3zllw7t+J3sdjvVahV7wM59T9xHs9LEHXV3PreSqmDz2rB37XzvC4LA6fOnYRqGfPvxOsIoTR1JkzoFzL/DzjBNk7een6JeatCzN0Q2VuSDF2Y4fKIP/21r7qn3lnjpjy6g13XSiTJf/+b2Im/3Cqle35To1WotspkqDqfKpUtnttgtSJLUkdLXVAlHwE5hsbBFOdbUTSxgaLSPYrFIs9ncVIC6FYIg7Gp27nakSg3qLROXJmOUmzQX84h+DWl93lVwyLQWizRXS9i9Nrx2hflMhWcPjNJqtZiZmSEajW4ZHyk3dBLFOpZl4bWr9Pf3c+PGDSYnJ4H2mIqiKFy+fBmv18uDDz54z8e+G2zEMslkksnJyW2tL25FMBikp6eH+fl5urq6cLvdfPZzE8iKyIVX51m9kUazySiajGla1EsN0GR6jkT4/M8fJHKLhVU2l6PWMoi427+rWW2h5xtIITuCJCLYRQSbhJ6qIK/PBYqCQON29eh13HqOvvoPAqQTFQ4du7NPomVZ/If/8B+o1WqUS00c2lEKsSJY4O314Nrl/vrXjQ+d6P3e7/3ex3gYnzxUSg2sloWjy0GjUKe1TdXRME1My0KTJVqGiWFaJJIprl+9hKqoiJJII9fgxH0ndi0Dq+s6ZqJFE5Wcx8lquk6Xx0nA5QALFhcXcXqDnFnK8cign8JSgUaqAbRV0G6tUj/1uUkKqQqJ6QwIAkMnenn8mTbHe0OR88yZMwwNDREKhfjSk2P0Rj2cPx0jdTWJtVhAsKy2wKZLxXeihwdP9HL/gZ4tVTF9/VyIt3xHq2kgSAKCIGJam8+fIAoIYnuGbwOiAJpmpyfay/z8PItVhXfeXUO9niGkW0g+G6JdxjItnKUm3rjBUmKBP0Pkm8/sRVEUotEooVBoS9L7YWDqJnqjbT2xEVA1m01eeOEFQtF+3p5p4ow1cYXc2yZ5GwiFfJT2VMmcX6O4lsET7bqpPKVJCJaCPVUjvlzANdZ189xa0NJbtBpNcvM5aioYrhRezxDm+mkTZAGLdjVMUCSspkGtUScUaAe2hmmxsDDPB3KcwUiQQCBAIBDYIumu2BV6T96d0pZIJMjn8x3bi1QkxePjj1OYL+AIOei9rxfFoeAf8WNZFslLSXILOZrldpewaZcx2l8Nq9Rg7uU5bF4bPcd76Dl6b3YknzTcfs4bTaMtT+3S0JsGjVoLU7cQ1xlK5h3WDAEw19/O0Num29tR6zd9vrn587MrFYq4qftsnFvMIksiIbeGaZoksjWupiWcA36slQL5+TzhA2GWlpa2JHo+n51f+IcnmZ/NEep2MjB4s4LqdrtRVZXZ2VmGh4fvuVO3G1QrTVo1fUu31OZUqWZqlMuNLa/p62sHh3NzcwwODu547gRB6IjMjI6OYrPZeOaZZwgEAjSbzY88uqDXdSrJCpZpoXk1oieihPaHyM3nuPLGFULuELlMDn+XH2fQSddkF75B37YdLFEUGR0d5cKFC3R1dd1xNjkUCm2yQKjVapRKJRqNBtVqlVqt1hkBEAQBUbKwTBO9aSDJIo1yA7nLYm1tbdO5crvdeDyeTtd3eXm5Pc9zNML8q/PkF/JoHo1muYnRMBh4dADttmDKNE3W1tY689X9h/oJDYUIEIBiE0uR6Hm0d8vr/g5bYZnQqLZQnRqCKGBza9TzdRr1rbZIjbqO3tCxTIuzH5xnOfUaXq+XQCDAl7/85Q99DFK9jrA+G3vjapIXv32ZYrpCo1Vn7wM9KI9spirf6vcqiQIH7+/jpzNZavka9nWrAcu0yMzncEVcHDjSQ5e/rfQai8U+lIn2TjDMm/GJ1TCwGibYb64VgiC09+Bam9kmiQKm1d53VVVlbGyM1dVVCoUCPT091FsG71xPcuH8GsXlIpZh4Ai7GD0QZn+fm7m5OUZGRohEIjzxxBOIosj4+Phd1/YPg41YJpfL8fnPf56LFy/u2vy9VqvxwQcf8NRTT6HIEp/77CRHT/Rx6VyMGxcTNMpNFFHENebgsSf3MTkZxDJ00uk0pVKJer3Ou+++h8s5irG+LwqyhKiIWHUdNLldHK01kW/x6ctks5w7s4KWvxnL2Gw2arUadvvNwsXAoH/TPrQdyuUya2trOJ1OdF3nF3/pi2B28e7LMwiiwCPPjOP+hKwxf8dnuAtquRqmbm6ZKTpyso/Fqymq+ToT9/fhrRsYTWOTeELEY6fbYyNeqCJJIlIxzrdffYtYLIZu6DjsDkbDoxw8cXDXVd9GtYGeNFBpkLTsBCQR1/qmnslk8Pm8eL1OYvkq8VqLkGmSm8/R19fHyspKZ4AWINjl4H/6zftZXi4gCu2LX72lCyeKIidPnuTy5ctUKhWGhoZ46ECEE3vCzMZLrK0WadZ1VE0m1O1kvNe7o3ywKoltQ+hbgknJqWIZ63+/raNntQwQRMRbAhbDtJBEAbum4g738u6fX0C9nsWlSMi9N292ARBtMpLfxuBKkdnXZnkx5OQbjzzCyZMnef/99+9Z5vxWGE2DtYtxrp9epVxo4PJoTJ6IEpj08dKrLzExMUHB1s38O68yLNmQgnfvRg1N9lCvNildT1ONF3BGvICAaZmYsoWgt2gtZJmzmUR97cVFEEAURGprdUSfi9HDCg+ePMR7WY1ctYldtaP0uFAHPDQXCiAJYFqIvU7USLtyn6+2mBiI0O23SCQSXLt2jUKhQKVSYWBggJ6ens6C6fP5tj1v5XKZ1dVVoN0N3Kg69vT0tKuumga3FRwty2L19CpLby0hOVVKXTaWsjUyySKG2U7qA06VwYADxYKF1xcwmgZ9D/T9zDEJjKZBNV2lVW1t6sB4fRrRMT+xG5k2/W5fCPmWAkrAqWGZpc59sYGW3p6PuHTmPaberwLw5FNP3tFM2DKsTR0Qy7JIrTaR7AozqTKCIBBYF66olCv0hfykyg1m0xWOO22krqUI7d+5+xXochLo2n4gX9M0hoeHd6XI+WHg9dlwBR0kFlJwSwG3lKlh82qEI9vP4ng8HlwuF0tLS/h8Pnw+H3OzGS68v0ImUSbY4+bI/X0MDQcYGhpifn6ekZERjh49ytLS0o7+d7uBZVkkLiW59OYiq0t5DMOkK+hk8niUwQeiFNUij//642DCwtwCQyND2wqWZLNVLp9bY3EqTT5XpG8ow/Cebk6fPsNjjz3aeV69Xu8EWBuIxWLU63UEQcBms+H1etE0jYWFhc49vgHH0wHisyXi0xkswB7QePyLY5s6h5ZlUSqViMVinXOzurqKLMt4h7yMPTtG6nKKWq6Gq9tFaH9o00hDOp2mWq22O4+RSGct+vrPfx27Yic3n8NoGNj8NrwDf/uU9e6Eaq3J+28uks9UCYSc3P/IEDabjCSLjB7u5vyLs8SrLfRai5ETPQS3Mbk+/kA/2WSFaqVJsnSRH/zgBwiCwOOPP84jjzyyY7fsbhAbDQSnk2ymwvN/cpFivIQr4qAYKzDzboYze1Y4+cBNimNvby9nz56lr68PgAceHmJ1Kc/UOyvkYyUkRaLZ0HGFXTz5tf10rXcmQ6FQp3jzYRRwt4Mqtz2STctCdClIbhUrW4X1+o5lmGBYSOt7dlM38dqVTbYN0WiUSqXCtalpLqwKXH1pDkemRkBTECWBWizBxWspEo8M8NgRH8vLy+zZs4c9e/bw+uuvd6wozPXY6l5Vl7dDo9Hg+eefp1Kp8IUvfAHLsnYsWumGyVy8xKULayQSZWqVCnv2RNFNldOnT3PixAkAurwKx493MTGhUqpUEQWoVauUi3PMTmfRNA23201PTw/VapXh4SG6u8Y4vZij22NDdCqo4wHqFxOYpSZWy6KpWoQOtBf1RsvA63EzEnZRqZRYXl4mm81imia6rrNnz55OLBMIBLYtdOm6zvLyMq1WC6fT2dG3WF1dZf/+/QDsP7i98vsGDMNk6nqaKxfWyCVKCLJE35CPA8ei9H0EpeiPgl1Hu//qX/0rBEHgn//zf44oivyrf/Wv7voaQRD43d/93Y90gH+dWDu/xuoHq7QaOqE9QYaeGOqIQgwM+fm133mUarWJz2Pj2neuUU1XN9HaHJrEw2NB0uUGiiTS5ejh+dIqxWIRC4t6tY5RNbi4cpHM99omlyMjI3estDYrTYS6ji5DHamT5DUaDWq1Gn397cVPlSUylSZRu0oj32Dg4ADnzp/H191H0zARaBtc+xwqE+N3XqAPHDjA3NwcFz64wGB4kHqhjqdp4BUllKANm8+GI+S4o2CGx67gsSkU6q1OR0rucSK5VfRcHfGWjpdlWejJKnLQjnwLHaNYazHe7UaRRKbiJRorVXy6idy7faAmyCJq1M1AzOLcqVke3ROmml7l0UcfZXp6ui0NfFuQfDeYhsnVl2Z444UZ0qYJmgTLBlcvrWHzZ3n8mw8yND7Gv//BFdREGee+O8u2G+veM62WTveQh1q9SmGxhL6oo/lsSHYFUZIQ/U7cxQa1Gjj6PaiiQDVdpZCq4hjwcvyZQVIrZzmw7xlqizmev7SGadkQFQnHfVGUbhdGrUVLNPD0uRBkEcO0qDV1nj48xPFbqluGYZDJZLh06RKCIDA7O8vp06fbtDSPp6NUWK/XcblcRCIRxsfHMQ2rMxwO3PE6zs5kWX5nGcWrcaVUZyFdQRJFPHYZWRQxTJNMpUm8WGeoy8l+v52V91ew++0E93y4gOJvIlrVFnMvz5GZzZKfyyHb5I44iyiKPPjYMPHJUFtZLLK5mtrjsRHx2lgr1PA7NWyySK1lkKs2cZg1auUcsWwaTdVoNBoUi8Udkz29ruO4ZX7YbJk0inUMl41iXce9vs7Ua+tKiwK4bTLFegvd76BZamI0DVRVpdls3nOyJooiY2NjLCws3FGR88NAVWVOPDHMj/9zisR0FrvPRrPcoFU3uP8Lk5sMh7c7rqGhITKZDC+/cJ6zL65RydVQ7ArLF+NMn47x3N8/yr4DEaLRKIuLi3R3dyPLMj6fj0qlsq2a4N2QuJzk5f9+kflSA8OtIKgSS/Eii3+eY8/cMk/9g0cRRZFao4bNZds2yTv9/hKvf+86xXgZURIwMFm9nOHMyyKefo1C4QWOHNmHIAidIOtWqqplWVsonuVyedvfJhBw8Iu/dT9XLsTRdRPFVuOBBzcng4IgbPLZg3aX2e/3U6lUqNlqOI47sLVs7cq9zaJYKlIoFIA21VfT3DjsyqZ1ZkNyP7T34zNg/1mCrpv82R+eZ+a9FUSxbTO0tlzg7/3yUSRJ5Nkv78fp1lhbyuMPOXn06bFtBSZcLo2v/OJhAFqtI1gUePnll/nGN76BZVlcv34daCcudyoq3Q650UBwuViNlSjGy4THukgk4wzv6Sc+lSG2kN+U6CmK0rHpANBUia//4hGuHO7hxuUEtWqLcI+HQ8d76ItuDqw9Hg+qqjI1NcXY2NhHZhD0++147Qr5aouAU0Xb0wVvZGmtlEARsWo6Sp8bZZ2SXKq3eHBka8LkdDpp2Lq4/Mopgvkm9iEfwno8JZsWtrUy8beWmYp6ONEnEo/H0XWd++67jxffPceZtMVSpoYowJ4eLwd6PYTdu/ecXVrMMT+dQVElxvb4eeONl2k2m3z+85/H5/Nx5syZbRU9q02dH/5kmhtvLmJmqogY6LrBa+/EkII2Gt40pXKFSHcYRVHweDyEw2GGbokNtqPuv/3225w4cYJMU+Lccp5a08CuStj2BpFcKnq21o5hHC2UYHt9TZYaDIc9PPPAUCeR3iguXbhwAafTSTKZ5Pr16xQKBTRNIxAI4Pf7MQwDRVEIBAKszFvMXUlz5KEBensFotHoJiP4OyFfqPP9/3aBpQsJjLqO4pCxTFg+s8q5V+c59MQQn3luz1+5gMuuE73f+73fQxAE/tk/+2eoqror6uYnOdFrlpusnV7jymqBuiwweX6NwGhgkyKe3a5gX585C+0PMfPCDJZpbaKX2VWJ/sDNi/rQoUOMDI8Qi8VILCYYHhjGP+knmUySTCZ599136e3tZWxsjKGhoS0Bk27oiIaBJcsIgIUIFiTiCaK9Ny9G07SQRQHLtGiYJssVkZ/MVDjbWkA3TBBAlUWGupwciHoZDjpRd7j4yokywpJA9kyWGys36I1uNpcVZRFn2EloX7sCux1tyKZIHIh6eeV6gsj64LLk1rAf6qb5/hKt5SJy0GpXx8pNZJ8N++HuzuBuyzDRTYsDvV50w+TiTAZ7urYpQdwOgiqhyRLOqsZ3X3yLX33uYTRNY62s88FrU+TyTQJ+O8cnQuztufvmVFwucvqNRRKqSHfIjSKL1GoNLt9Y4gAeAlKAP/7uX/D+GykmRBk0iXqtTrPVbFPo2GxAL8sSqqLicjkRJYmuB/1c8cySXy4h6SDkWiDqoEgI1RbmXIEVHTRJQOtyMPzkEM88O8HMtbMcPXoEURTZE/FwZjHHmbkMarFBrdwCy0J1KKheGHc5MUyLuVSZ/oCDidvUsCRJIhwOMzAw0BFdgnYx4dq1a6yurrK4uIgsy8zPz3PhwgUk0YPD4WZyT8+OFbNms8nKygrDQ8OkLqcQRIGpSpPZVJmw27bp+pMliZAi0dJN5lJlJBEmFZnExQSB8cDPjNVCbj7HyuUE05aFrBt4FspET0Q7yoqiLBLdYSZSVURODgW4HCuwVqhTrLXQZJHxsJsDvX2caaVo1msd6nWj0egYVTsdThzOm7+PZVqbZ6EEoNFAUmyIQrubbpkmLb2F294OrE2rPRMhWKwbawuEw2FWV1c71fZ7xdDQEKurqx8L7fFW3P/wALlclqVrJdKxPN6wm0MPDfDwE7ur7Hu9fm6cvkitWKd3f1v0wDIt4jfSvP3CDJN7w9hsNrq6ujhz5gwPP/ww0BYLGR7e3sdrJxhNgytvLbFQauDscXVmhxoOmcXVHP6FFvGpOB/c+ADLsvjSl75Es9mkVCp1/Ommrmd54ztT6C0TX78bRZFQVRVJlqkW62QXC1wXmhw7rtAb3b5C7Xa7txQH0un0GlPyDwABAABJREFUjr55Ho+NBx9tP7a0tLSr79rd3d2h4W1A13Xi8Tj5fJ5Wq9X5/PhaivOn4oQiPh55fOJDBemWaVFcKVJYLKA3dVwRF/4RP8ou/dI+iVhezLN4MY5/wIPDY6OcrTF/do21p8fo6/dis8k889y9qU8risIzzzzDZz/7WXy+dpdp37597b11bY1YLIYkSfT19d3VvkhuNBBdLhRZRJQEMqkMbo8bQRQxdQt1m3jC6/V2BMPaxyNx5EiUI0fuHpDbbDZGR0eZnp5meHj4IzEIfA6VPRE3785n8DkU1BEfSi2CWpEwazpKxIk66EW0yaTLDdw2hfFt1Dwty+LClSS2bBNbxNVJ8qC9rio9LtyzOWYuJ3js0BGSS7M0m01ygpvLGTvN0zM4cw0sUeT1kJ2r+0J86UT/pthzJ9y4nuSH/+kspVQF0zJpaGUOPGzjxIkj/PEf/zG/8iu/QqFQwOl0ks/nKZVKVCoVDNPk1XeTLL2+hlsVcPQ40TQXsqogmyKlWJFyMcrrp2L8P37zxK4LXplMBkmS8Hq9uEyLiW4355fzOEWBer6O3jJQbBKyS6Cnq71H5CpNWobJ8YHApm7pRnFpaGiISCTS8fgzDIPl5WWmpqZYXV1FFEXq9TqnTl1k5h0RswCL0zE8gQbhcBCv17tlvcnn23PMfn87J6jVW3z3j86zeDpGYNCL/RZrMcu0KCbKnP7hFIIg8Lkvbu9r/ZeFXSd6pmne8e8/a7BMqy0yIkKxXuPdM2fJdaVxzbn41Kc+tYUT7Rvy4ep2UVgq4Bvy7fi+G5v++Mg415XrRE9GOf6V453B3JmZGVZWVlhZWUGSJAYHBxkba1NgJEnCki00sYlpqbgkg1JDR6+V8Xo9HSqLblgYpkXEa2dpOsOKZJE/b+ILD+HM15ENEwuBliJytdbiymqBXp+dZ/ZFGLhlYTBaBomLCVZPr7Zl38N+vFEvyyvLjA2PISvtz9ugns2+OEvmRoa+B/vw9m9tUe+JuHl/vi39viHKog77cKgW1dkMYhkQBbRRP+qAF8l7k/+cKjXo9tgYCTppGib1ShNFNxF3YcQp2GTEShNvMMzMzAzBoT2cnmpS+mABp24xL4ssHchgfnaS/dE7t9aTizmShRrePg+KLNJsNonFlunvD1NM1Pk3//L/pDDspbIigS9MuVJBVVWcDifSXbxzoP399+0f4QpXabYs3JofvW6hlxqItIsI6v4gewY1Hn7iAAPdLkqlEvF4nEceeQSAlVSZ8nSWwqkVzHQduwSKKFITIKNYrA0WUYf8HJ8I8tzBaEdt83bIsoyu61QqFRKJBACjo6Nb5hvbprYJMum2pP709DS5XA5VVTdRJXK5HN/5znd48MCD9OR6ELqcLC1kCTi1HYsMiizS5dJYztYYGu7CXC1RipV+ZqhZlmlhmmDIkCqkKK7kMc4bhPpC9A9sFcy4HS6bzAOjXRRrLRq6iV252eU/eOggiqqwb98+UskUwVCw07GplCs3kz67E1ESsd1imC0pEk7KNAQPEY+N+XQZQ9Q7QbdlQb7SYCjoQqrpuId8nY7+R90bNvz54vF4x5fvo0IURQ4dC/OZ544wPbXA2PjQPVVVU8kyxXgFzy3m3YIo4I64SC8XyGZrhEJOarUag4ODnWNXFTf//VvvgaGw90gPh+8y/A9tS5K15Twtl9xJ8nRdp9moEw77mZuOM/d//EdsYzY0TWN1dRVVVXG73Z352p/86QKCITOwb2v32+GxIY2IZJeLvPHTK3zhS65tu3R+v5+lpaXOb14sFrc1rL4diURi1wqAkiR1rpdkMkm9Xu947m03d1Qpq9jsbcrnrdRYURTx+XzbBpOGYVAsFvH5fMROxTjz0iwrqQo6Jn5N4fDJPvY8O/YzO9NnmiaWaSGtB8CiLGCa1ke+Tw8ePNj5/2AwyNWrVztBdTQaxTAMVlZWqNVqKIpCf3//lqTKbDSQDAPR7WZoNED3hJ9rb80R6pUpLaWQPSKh6NbrYGBggNn5RQbGJjFNC02Rdm1uDu3rbnJykrm5OUKh0K6u651wcjjAUq7KXLLMcMiFFHHgvI3Gmq00yVWaPLUnvG2nraGbpBMVNNNC3MY+RRAF7HaFTKpKodZqW6U0DX56eh7xSpZQpobo1sCwcMzmyVRbvOpS+eYDQ3dlLJ16fYFKpkpwzMv87CLNNYPFOYNr1/4IgFdeeYVWq8X8/Dwulwu/309fXx/z8RL5qWWiAQ/ObeI997APcyZHvRzge8+/xN/70ue2TaqdTucm5sPZs2c7oi8tw8TeNMlfjLM0ncXWMFBFEUsUqNkgNhJE7XUTDjt5fCLMob7t44KNuc7u7m5WVlbQ9fZe9ulPf3rT80qlCv+p8DZrUxmcQZkbN67x7rtZdF3H7/dvimfeffddLl26xDe+8Q0OHDjAhXNrLJ9fIzTi3+LPKYgC3h43lgUXX5/n2P39RO5gN/Fx4+9m9HaA6lYJHQixp9ai1dS4MmDn/Nx5hqXhbTcgzaMx9MQQMy/OkJ/P4xnw7Nh1aFVblJfLHHnmCKNPj5LMJjFNk+PHj3P8+PFOsDw7O8vc3Bxzc3OoqsrIyEh7s3VUqEt2uuQWSLCarTAx1ItpQrWlk682CaiQiGVYyNdQ9wToTdcxFgsYuTot2iNxgiQQ7nIgDHpYNSy+e3aFzx+KMhZ2YbQMFt9YJH4+jr3LTlf0prfO6OgoM7MzDA0OYbPbkFQJd9SNM+ykuFxk5vkZhp4comt8sx9P2GNjf9TD+/MZbLKIth4YqiEnLQe4fb5taY7FWotKU+fxiSA2RaLeMtptMUHAMnfhJW5ZlOtVhgaOcGDYy7/7wSnKZyqEEZB6XBj5OrlLKd4f8LIn4rnjwthqGlgISJJIo95geXmJaLQXFI1azuBrn/t7/LSWpHj6EuHeCJL33tUiBVFkcs8k165cpSkVGD44giQrGNUmhXSV8KEITx8P4HJJCILA2bNnOXr0KKIocmYmzQt/ehljLs9ep0ZpzE62aVAzTFq1OlrdQrqeRSo26ZoIEd4huNmgAr/11lvs3buXiYmJHY/X5XKxb58LuDn/aZomhUKBbDZLNptlcXGR9957j8uXLzP/5jwj8gjHfu4L1Fo6gR1MoDewoSIZrzbpMy3yC/mfmUTPN+ijZzyAPJ9Hnxzkg/gaU1eniI7sjiqyAc82QY7P5+uophpOg2Qyicvpwulydv4ArE2tgQq5Rg5b09bZjANClrw2zIjHxlq2RLYBZq2FAJQaLRSjQXM1TcXbzeTETZqeIAi7FpfaCcFgkGKxuKMi54eFJIk4Xco9U2dkRUKURYzbRLeMdfERRRExTbMzy7zhI/jyD2aYeW8FSRZZuJTA4VQYn7wzxdAyLQzDQlg3im81WzQaDdxuN6VaC5vTyWc+/1WK7hyxWGwLvXJ2JkN8NotvB0o7gOZUwbSoZlXm5uaYnJy8q51ENpvdsZt3K+r1+q59A0ulEmtra5imSXd3910TxEM7dGxM0ySfz5PNZjf9u6qqVKtVfvzjH3P/vvtJvF/jSqGG4dNQJIFMtUnznWUCvR76H7p7YeWTiN5+Hz0TQZYvxFEcCs1qC1tU4I/++PcZGxvB5XLx3HPPfaTP6IhvpVKcOXOGvXv34nA4Ovdus9nszD7Z7fa2kIokoReLqIDo8aBpMoP7dYI9h0nFyjg9Gr3jKv/xD/4tp88cYWxsjGeeeYZspcmNjM5fXCvgjM9jYaFKIqMhF/ujHoaDrl2PY2wo7DYajQ89Yxh0aXzxcJTnL60xmyq1i9DOVnsGrWWQq7RwqhKfmgzz4Oj2nyEKApIsYFjWFlbYBizDBEWimM8zFOyipMLqT95jJN1C6fd12E+iQ8GXqbO8mCe2p7apeL8dmg0dA5PFxUVUm4wr4OWB+/eiOSeYnZ2lUqnw7LPPbvE8vXw5gZGp4RjbXtREEATcPS7K8RKi0s0777zDY489tqUzFgwGWVpawul0kkql0DQNj8dDrWnw/VenufrCLNFSk6DXTl4SqOkmgm4g52tYZ+PUl4sEPz/BgyOBbfccy7KIx+NMTU1hGAYDAwM7ajS43U5+7Z8+wWqsSP+AD9t60bRcLndimUwmw/T0NN///vfJ5/OcP3+ep59+BodwAkEStyR5t8LT7WLtWpLL51eJfGbnmOrjxseW6BWLRX77t3+b3/md3/lLNaH+q4IgCPQ/2I9vyIepmxyw7+EP/+sfcv/993Pt2jVGRka2bIzeAS9jnx5j4fUFsjNZFLvS9jtS2vTKZqVJLV1DkAQiRyIMPDqAbJOJRqPU63VmZmaIRCIEg0GCwSAPPPAAa2trzMzMMDc3x/Xr15mZmeFQ4jpDooWYqODtSuMdjJKv6Rhmm7o1GXazcPUq751bxTkeZPBsnXrThuS1Ife6EDb4yy0DI1fH+qBCZNxPcsDNTy6v8dVjvbQuplg7u7atQa+iKExOTDIzO0N3d3en4ivKIr5hH8VYkYVXF1DsCp6+zbSzJ/eEqTR0Lq0WGPA7cGhym6KxQ2CYqzRJlRo8PNbF8YF2m16TRUJhB7MCNGdzNFWBerWFZYIkC9hcKjaXhidgRxQFqvkKyoAbv0vDbrcjyG5a6RWksSiiJiEE7dgWC+QzNeotA+cdZJZDvR68NpmVbIlmMUVfXy92u4O1XJWgKjO8N8rVVZVodxI+gsm3oigMDQ+ztLzE4uIiI8PDKJqMYgnU63pHsc7r9ZJKpXj88cdZSJV56btXYT5PaNiHaJPxAt26SaVpUC4LuF0u7JJAfbnE5RdmGIi4OTnRDjxN02R1dZVqtYqqqkxMTKAoyocyexZFEb/fj9/v7wgAVSoVSqUSwWIQv+6n0lofZt8FbIpErtJk0K7SKG1VSfykQvNoTDw3Qc9aCUmVEAYs5t+YR5Zkspksga6Ph74oSRLd3d2US2WSiWTbQ1JsJ2SKqTD62Cjdw90kEgmazWZ7dsrMEerVWEtW2OeWqHZ7Wc23xTr29XjoUSXe+O550r0LVC5U+OLwF4H2LFUmk/nQgROA3tChBLa6jYtvXWTvib0oO3Se/yoQCjnp3Rtk6p1lbA4FdT1gLiXKRI/6sNmETb5YXV1dLC3GiE2ncHc78UXcxC4liMdKd030bH4bgZCDhZUCVVsD02jhcrvAglK1SdSu0DfaQ8sb3FZWPZ+totcM7HfpUKlulUq2hdvtZnl5meHhrUVMTWvPd9br9V3NXe1mPrPVahGPx7EsC5fLxdGjR8lms5tU8e4Voih2Ku23H8/FixcplUr8+L//GLk8hDHWR8+6B5wmi6TSVZYvJ+m9r3fbWcdPOmw2ma/+8hFef3GGbKJMKOrhkadH+B//I8H58+c/VpuhUChEIBDg2rVr2O12RkdHieVrnF/KsVYUCLvdTLptzM7OYpomtlyOIUByubhx4wZ79o5uKuxYlsX1G0eYnp7moYceYiZZ5oUrcRKrRZx5GcdcHEwLwyFz3lfg4nKO40NdPLU3vMV2Zif09fWRTCZZXV3d9SzW7ejx2vn5kwPMJMu8emGWetOgUqvhdTl4bDzIZMRNr8++Y/FLlUWGx7o4/8Eq7kJji0q31TKoNA28PS5Us4bf38fVTBq/zU2ptIgmeDsFb9GpIGWqGOUmlcZW9dTbER12cvqVNIos43C78EXd7D/YR7RvP4899hivvPLKliQPIJWsoIhsopneDsmlIrUsXM4u/P4a7733XscTEdrejPWG3vHbPnfuHA899FCblfD2Alf/YoYuQcQ2GUQQBSKGRbmuU6yUcUS9aKIIiRKJN5Z4s8/Hk8duKqrmcjlSqRSC0J6zGx0dZWBg4K6Ub6dTZXxi897lcrVZDxvru2maXL9+vaNMHOnpZ/FUEYfvzqNEoiQgKzJrS4U7Pu/jxseW6NVqNb71rW/xzW9+82ci0YN2u/VWcZXf+I3fwOFwYJomc3NziKLI8PDwppvXO+Bl71f3UlgskLqaopKsYOgGgiAg22UiRyMExgK4o+5NHT+bzcbY2BjxeJxsNkt/f3/nAo1Gozz88MMsLy9TKpXQjBo+Jc2NlRtMmjB+2M6egSiq3Y4KWPk6q1YAPZKltLpGJt0g/MQRRGXzzy0oEnLYidnQaUxlCYsCK70W772zTO9MHnfUvSXJu/XcjI+Ps7i4SKPR2DTE7+n1kJvLsfzuMpNfmNw0s2dTJD5/KIoqi1yKFTAti4BDwVy/0aGtYJWtNMmUGzhUmU/tCfHwaLCjJpXK1SjM5IjNZfEsFjHtCoIsIgAtAaqmBYpIMmDHE7DTNJv4R/yMhdut8v6+AGm3k9xCgsBYBCPfoKFIRHz2HVVDN+Af9tM37GD61etow1GKukQmUcRdanHgvn78w36yl65jdzjYIiV6j/B4PYRqQTLZLEtLSwxG+7GEDWWttlny2bNnOXbsWNtP6twqjZkc3eszARtQZBEX5v+fvf8Kkiw9zzPA5/j0PrO8N22rq830eI/BYBwsqQVAioSEFSVKWoVCEYpV6IaxN5JC2ojVhkIbWklcigyKIiGK8MAAA4z309PeVHWXt1mV3rvj9iKrs6u6TFfPDEgCmDeiA4OszMqTWef85/++7zVobg3nxt9T6fdTn85w5v0leoMCuUwaURTp6uraopcRBAHLsj4R2/snn3ySp59+mrV31khfT3PVMIA734huwtrQgv2qQfWoren3Ay88gKfiwSE7kN0y6+vrrSncJwGP19PqnLpcLoSagCPoINDfnKbfpEqapkmtmMff2eCGp0jMDGFVLQaDzeMwKjqSJTD00BApV4p77r2n9R4ul4tUKvWRjq9RapCcTJKaSFFNV7E29MSvvv8qBx4+QOdY565r0i8an/vSYSrFOmvX09iGhSBL9B7v4Cu/c5zFxcWWU+1N9PZ14YtdI341SzVXx+F3bDPU2QmaV+Pw6W5mZ5KsrusEoz4KVZ1SuYErX+fAqS78fX7iyXhLc7IZoiACNpZl7+nAZ28YUfX09LCyssLc3BxDQ0Nb7mfRaJSVlRVM09xzmmcaFplslVRyjYOHtusebdtuNREURaGrq2vLmtJoNO74vXwUqKpKV1cXoVAIf8nPz79zg+SmzycKzfgZy7I+lkvq33aEwm6+/PXxLY998Ytf5Lvf/S6nTp3i4sWLjI6O7llsVxsm06t5MukKpmnjcikM9gW2URElSeLo0aNkMhlefON9rlW8FJdKuBomy4rAdH+Qr5zspj/iJv7mmwBcX1oikUptmywKgsChQ4e47777WMpUmIwvUl8o0raQxy7pTemGKCCmq0SWS5idNT6oGYgCPH24fd8OlLFYjHw+f1ea2lyuyvtvLiBKAg882o/HozHeE8CjB2nr6mVxaZnBvp477ilu4thwhKvDIfLXkvjsZlyUIApY5QbVtRKViAPZWsGpNN0fnaqErUn4fH7SqTSRjeadVdExJRHRpeC8w3uvra2xmjzH4SeiuKV2HC6Nex/tb+WK7ma+BDSNfe7A/rUtu9lMlJt07Hw+z7lz5zh+/DjvvbXAudfmqRRqOMMyJx4q43K58Hg8rOaqTLyziF+3cAz6W2uSLAkE3AqSKbWiDez+IMZUhnNvLXB00EchtY5lWQSDwS1MJFEUWV9fp6OjY19/j70giiK/+7u/Szgcxu12UyzX+S8fvMp+SCyCeMsh9a8Lnyh181d5oYRbLoI3HeKq1SoTExNEIpEtlBPVrRI9HCV8IEwtV6NWNagZJopLIRxx77n4tLe302g0mJ6Z4eqiQSVv8tnPDBH1Oejv72d8fJzAz3+OM5ul0lPBc8jD0soiCzfm8bg8RDuiOHo7qB3vIHw9hXjZRu32Ye9xAoqajBxz0ZjLEQ47mPpwkWDQTcB3Z83CTT3K8tIynV1dFGsGxZpOURWZ+nCFeU0kdDRGyKUSdCv4FInlhRwPdPk50unn6mqeqfUiK9k6KSOHKEoIgN+p8MhwhAMdPro3OmG2bXPm/Cpv/uA62dkMmkvFiLlxNSzsgAatwtnGrpuYq0VyMxlKh8OEpRrXLpxlfHyck4NhZu/rIffuAo0ba0g+D8LhCKePtt2R8rGeXqfkjfPFr5xiZbZIqVjH43UwfE8vY58dQnEq1GsFFLcDu/HxdazRaBTLtslmMizPL+F0hnC5FGo1g1KpRDKZ5IknniBZrDN9aR23LCLuQOOrVCpbOvKCLOL1OYhfjDN7MsQDRw5sew00z8f19XXC0TZy1Qa6aSMK4HUodx0wGwgEgOb1YTZMvC6FhXR5X69t2lLLmHXzb2yj/9cBd9RN18kuFt9aJDQcak3h1tfXCYfCLV3sx4EgCsTaYhQyBVYm4tSiASrnVnlkUzC5JEmohoEdCXDP37mH7FKW/EIeq2Di9/txR90Eh4J0W934/D6q1SrpdJpwOMzCfJaXfzxHozhPIOrmxP09HDh8Z81WNVtl7uU5srNZHEEHvl4fkiJh6iZqSuXydy+Tnklz5IUjaHdYm6auJ1lZyHPfI30tw6yPi2jMw9/7Zw8weTVBPlcjEHRw4HAMVZXJ5WXGxsZYXFzE5XK17gdf/4cP8r1vfYhgyYyd3t/3AOAbdXPPw10sXi8Sj5cwgR63yuipbk68MIrskHedDnT2+HH4HZTSVXzR3Wlb9WKDnof7kGUZh8NBIBBoRUQArQZPLpfbMWD9JtbiRX745xdJLuZp6HUeeFbnqeeaZin5fL7lmtnW1nZHeugvAocPHwYgt5AjGlgiUa2Tk0RkSSBXrtNh2nSMhPd0jf5VRGdnJ//gH/wDVFWlu7ubGzeaRhEjIyNbivCabvLe5TgXz6yQu57Gruhg2ViKiKvHz+B4Ow/f30NncOu5FgqFMPzd5CYWaZ/OY9dMPJpE0oazETf9ETfOjT1jGTh+/HjLubOjo6NltPL44483J3sfLDL//lVGEzaCpiD3+rZcA1bdhMUiIRvOOlUOtvvoj+zdIGs0DJaX8piGRXuHj46Ojn07cv7k29e49to8CFCrNPj8bzY1i+JG+LpXk/Zd5AEMRNx85rkRXhYgOZXBma6CAHVJwOr0cPjhLh4Y8uBxOTh//jyhti4CfQEq8RLetE2qFCfoC2CWG+Tb3HT3BegO7l64r6ys8NOf/pSenh6efPLJHSVJ8/PzuzZ4+vqDzEsLWBV9R10hgJGrIbgV2nv8xGKRllb2e99+jysvJxFEEYdHJT1d5H9fnudf/JsmK+TyRJL6coFQh3fbOqc3GqjqrXVEEAW8UTerN5K8d2me5x86vONncTqdrK2t7fp93C02N/VcDgVP2El2MY93h2iSm7BtG71mENkl0ucXhU81eh8DTqezaXSQTHLt2jX6+/u3OEwV6gYXZ9JcOrdKOVFGEAWi/QHGT3UyNhLd1YBCVVWC7T1c+V9vUsnW6On1Ez3VnLIYhoHaaFAVBJ7/yvP85m/+JlMTU9y4eINUIsWavMZkuUheDjEo+PEeacMKKczPL9Db24ui7nxBii4FI1lFmcmRWitR6PSxXxJDIBzl+lKSd96+huTyo5tW01WyWGPmzXmwDARJxKlKRBSZsGlzVJUYHggxHPOQLNb58KoBokx7exuaItEddOLbRNWybZtX35zj7W9fQ6pZdB+KETIt5pdzlBYKuPL1ZgEtN2myQsPEdKnUok5U3caYqOM9Ptyy2f3yZ4f5oNfP9FQct0/lsw8e4Ejn3tSk+fl5zpw5wwt/5wU8Hg/lRBmjaiA7ZdxRN4IoUCwW6Qi6WfCDna3t+fv2A1GS8Hq9yJJEeiZOvi5SfXmWP3x/hfZD1dbkdylRorqYoz20f/qTGnYgL+TJZHZu0NR0k4WCyRuX4hhqiUrDxLRsBAGcikTQpXCg3cdom7eVsbYf+Lp8CKJAm0vhhixR182WXnMn6IaFKAi0ezTsTHVHo59fJXSc6Gg5AwaHgni8HjxeD+l0GmwIhXfWItwNbNtGT+tonRHOXVxlKVVh/FQnvk1OYUKlQk2SiLXFiLXF4J5mw+DmtM50mXQFmlQZr9dLKpXizAdTvP7tWQrrJRwelfXrKeYvr/PM3x3n+Kndg4rNhsn8q/Pk5nNNV9VNa6OkSHg7vLgiLhbOL2DZFie/enJPml0uWyWfrVKvG59YoQfNqIZjJ7aujDcpX7Is09/fT7lcZm5ujkgkQjjs5Zv/5DFmZ2fp7t6fsUwqlcKWbB78+mnG4kWKq0UMw8ITduHv9SM7ZBKJxBYWxWa0d3jpP9rGtTfmcQedSPL2cyW/XkLzqBw91fwsbW1tzM7O0tnZyeLiInrdxY/+10WGxzsItZd3NaswDYsf/vlFFi+v4446kEyJd78/gUmFI8eabnWbN0S7IRQKtRoFvyj4un2MP9RL49U5Zlez2E4HnTaMj7XTMf7JmP78suEmzVYURQ4ePEilUuHy5ctEo9Fm8HXD5Ls/v8HkT2dwVE3CIQdyxNVketQMyksFrs1kWJlK8cWvHmPwNkfJRLGO1wC7ZqL2+2ks5HHVTdbyNWzbxioWAeg9dKgVZG7bNvF4nHg8jiiKdHd3k6nDfLrMiOmkkIkTPNCxbQ0UNQna3bBWQY+WmYgX9iz0zry7yAevzJJZKYBl4wo6OXx/N499dn+OnLVyo8kwsW0qxU9mIn3fcJS2r7u4NJ1mbiaNZdl0tns4frgNqbROX0/zOwqHw8zPz9PtKHFjLEJ2No+WlFgt5JGG2ggdjvLkwbYtDpSbsbCwwM9+9jOGhoZ47LHHdi1qE4lEKz/udhw5EuNMt5fCcgH/cGibrtA2LLJrJQIn2jm4Ed/U3d1NKpVi+lKaarlG/3jz84hOm9S5FAvTBfr6YtyYTOAwbcQd9hW1Wg3vbTRyKaDhSJTIpsVfSHj8nSBJIkfv7+HlqXRTt63ufAzlTBXVp3H0+MefKt4NPrFCT1VVHnvssZbV6K8TotEokUiE+fl5TNNkcHCQVLHBX/3FJeLn4zhMG4enKX5fmcuxdGaF6aeG+OIzo7t2fIIulfFHB0iulRgdunXzMwyDRi6Hc4Mz7HK5GD81zvipcQqFAjemprj49gJ2KktuoUDWI+EV/XjcbhYW5unr60PZZfGS/Cr6TBbJtsk1zB2fsxm2bbOcrXJ1tUCmXEdUXDQKWTrbo02HULeKkari1RTEkJNKw2StWGfRsEit5LDdCsMxL1GvxkikaQLR27vz+fPhxThvf3sCzYTASFOH4VFEBnoCrHo0SskyZGpIDQtbtDH9DqSQk0jERcyjkZnO8LO/nOS3/8l9CGaZa9cu8HBfH7/54AMszM3ik6oIwu4FxPT0NBcvXuT5559vFfOeHVyT5ufnOTrcSzyeorFWQt5FWH038Hg8mIaBX/NxQ6ghxbOkPU7UWINT9zS71dVqA9G0EbTt51O1UtmRkiMoEqJlU6noWx63bZvJtSJvTadYzVWRRAc+IOxRkTf0lDXdZL1YZzYV592ZNKf6gtw7ENpXB9PX7cPb7qWSrdDhc7CQKdPud+44TbUsm0SxTnfQiadmosU8vzJGLLtBdsj0PNjD1I+nKCwXWjrXcDiMaZgkE0mcTide30fvCubn87hCLoYf7aXW5sYbdGwp8gDscpnQbTEJLpertXHP5/MtG/1wOEwkEuF7f3aNzFKWvuO3IljWrqd47+fTjI13bMk/24zcQo7sXJbAYGDXAk5SJHrHe4lfjzNzfoaR0yM7Pg/g9P29NE4aqOqtW9xNVsAniUaj0dQaOW59d263m4GBAVKpFKlUiu7ubgYHB5ment5RB7cZiUQCURRbE0Ffl2+LfMCyLCzLolar7Wlc8tizIyRX8sQnEwS7fLg3OvtGwyS3VkKv6px+boTBTfeW9vZ2stkswWCQt16fIb9eZuHGOvc+PEYikdiR8pTLV0ktFQh0+rAVg2BbmJUrCYyauq8C7yY8Hg+ZTOYXWuiJksjA4/14Ozy0vzWJzxUgMhggdjiG8y4aZL/KcLlcjI+Ps77e1O9djytMvDhLWJJwjPq3FFeSKuHzaXhKDZIXE3xfvsrvfuPklqZf1KuxrAh4NInGQh5BEqkqEn1eDUEQqKRSBIDBjakrsEWyUq3WSSbXeXcmTXIuhzdTJTLUSSafxevzbZsQiw4Z07TwFRpMJYo83oi1cns344N3F/nZ/7yIbdkEOjyIkkgxVeH9701SKdb5ym8fZ3Z2lra2tl2piw89PUy1VEeUJB548pMJYE+mK8Sn0nirOse6/LR1+jgwFGJ9fQ1/99ZGWX9/P93d3bxy5grTHg+61I5s6ASlOs/d30/HLkZw09PTvPrqqxw6dIiHHnpo16ZhrdbMTN3t520+Jw88N8qr37qCeSONr82L7FPABj1fI7deRuzx8sSzt/a5DocD27ZxyG4MoUC1WsXpdJLJZHA73VQrOg3TolE1di5SbdiJHymIApIoUC7tXXD/IhtK46c6ufzeEus30rSNhrcVe9V8jXy8xOEnBuj9a97DfGKFXjAY5NVXX/2kft0vHQRBYGBggEajwdWJ67z8RorsmSRt3X6UTTEBbsumtlbi6otTeH0azz228wIhiQKff3xo2+P5fJ72SgU5FNrmHOTz+Th67DiHcl5qKxnq81MU1AaFQgFoFokXLlxgaHiYUDC0Xe8kixiFOlLYScPcnXY4lyxzfb1AqtTApjnd6fC7EEWwDCeJRJJwJNycHhomVklHDrvwaE27cMOyWMpU+cuzy5zuC/Lo6N50pmSuyps/vI5YMQgc2HqBujWZ4TYv5YCTYt1A39BDOjbslm9OTWMjYeI30rz04nW+/rVxHn30UWZmZjjz3rscPXq0ZVG+U3bX5OQkExMTPP/881s2dJtRLBaZnJxkcXGRL3zhi0wmbOYn0jh2EFZ/FDhQqbidtB8M4CuuoclVPvu5J1taPVkSN1xIt29kdcPY0AzeBtvGFpq895toGBavXk/w4UIGURAYiLh3XHAdikTApbb0lC9PrrOQLvPsWAcRz970LFEWiY3FmP7pNEcjHnTTYiVXxeNQ8DlkRFHAsmhSgGs6HX4nx6Ie9GSF7vu6du2W/SrB1+Vj4MkB5n4+R34xj6+nSVOS5OaErVKpsL6+TjAQRNX2P021LZv8Yh7FrTDw5AD+Xj9f3iHAN51OE6zVEPewHff7/S16VSqVIpFIsjKdxhF0UKlWWnbZnpibbLxEJtuMINgJqespREXcQp9r6Ba5qk7QpaBsXMeyJuN2u6mt1raYn+yEzUUeNKcWpnnnBtbdYHl5uUV1vB3NiV6YlZUVoBmts5MO7ibW1tZ2XIOMukEtV8MyLKq1Kt/6zrfwR/088sgju753R6eP3/jmKX72/QlWJlLkV4tYdtNi39fu5cEXDvDIbZtTl8tFOp3G4XBw6t5uPG4Nj19oueLtDAPD0rFLFlpIxtSbWjftb2kunaRISO0S9/3u6Tvmu/06o62tDdvh47vffh2faePo206huwnRoxLu9LF+NcHFiXWeuOcWzfd4T5DpRIkk4KqZVBUJ50CAkxsN3fmrV2nTNMTbfnexWOe7f3aR9bks/UdjhI634VXrVHNJqk4TTdMoFgrg8aI5b7u/qiJS1aBh2tQNc1uh12gYfPDzGWzbJjZ861oLdfsoOmWuv7/C4sP9DA0NsbS0RL1e37EwGDkQZeRfP34X3+ruWF0r8tars8ydX6OaqSBYNjYguxWiIyH6Drh45nPbOVayLPP0A8d5sFRi4voUbbEYHreTTHIF/NsDzicnJ3njjTcYHx9vRRjshvn5+Ts6Hj98ogtFkXj31VmS0xlYM7AAyasSPt3JY58dZmxo63fX09ODr30Gx4xGaj2N1+/GrILL78bls5vZrJLATj25SqWC27nzdWsD8g7shc3w+/3Mzc39Qgo9v8/BF37nON//0wusT6WRVQnNo2LbUM1VEWSJA4/08sJvHvnYjJy7xSdS6N20Ut+pW/pJht7+MkBVVRRPO/nrM0Sini1FHmzkoXR68UxluPr+Eg/f17OForgXbNvm2rVr3K+q1FR1R4vY61fXSd9IE4668USitEWdVPQahVyefKGAbdtcuniRSDRKNBzBF/DfmvaYNtRNbE3aVauWr+qcW8yylKuQLTXoDDi32PeKctPdL5lM4PP5EWlSPDZDFkUGIm7yVZ03p1PUDYtBzWa37ep7b86Tm8nQObqzi58gNPPEPDsEq7aOS5UItbuZeXeJydPdHB6JMDTUdPi6fPkyhmEwOzuLoihbaEqXL19mbm6O559/fk8ah67r/OVf/iXpdJpsNst9z/wmsz1x6jM5nF61ZX38UWBbNna2QTGo8ezTJ1m/+Bpvv/02mvYswWCQRCJBIOAEj4peaCBtiivYy0jFKuqYqkRkw6TGMC1+dm2d9+fTdPqdyIbFykQK07TwR92EO7dv+kVBIOLR8DsVppMlvndhhS8d7yJ8h2IvcihCOVlm9cNVTsa8RLwaM+t51vI6CELzb6rKHOsO0OtS0dfLdJzooG3s7h1Af1kRGmpSNOdfnydzI4O/z98yNnK5XLhcLrLZLPlCHp8vwNXza2QSZQIRF2OnOtFu01DqFZ38Yh5Pu4eBJwa2OeLehGEYFLNZwroOO2yGS6U6P/3uBJ39AR54uB+g5RQcbp9nfTZFvV6nXC4TjURoVHRUp4JrFx2HqZtUEhUc/k0Bs7bNB/MZVnJV+kIu7t+0WdB8GnrGoJCX+P633ycYiKCoEm6vxsiBaMsS+3aIovixs8PK5caGW6RGOp2+4/1NEAS6u7tb9vJer5fZ2dmWE+1NrK6u4na7W4UzQDVTJTuXbRrTZKrNJo4I5Q/K6J06tUM17IHdYyw6u/1845/cz8J8lsW5LIn1JP2DXRw8EsO9C9W6u7ub+fl5BgYGSKeTeHYxAUokEq1u/IPPHuKV/32Bal4iR5nYYIgTp3en6d6ORsNgfa1MtdKkvn+cTLP9oFKpfCxH2F8XXJlMYifreHbQSd0Oya/iWCtx6ewqY0MRZieTZFMVEOCgU6Yw1k66ZtDn1TjZ2zRGu3HjBiGHA1NVuX1luHBmhen3l3EGHVx9fYFBv4YoS7h9HmSvB4vmZDu+Fsfr9RIOR241ri0bW5YQBXbcxyzM58iuFgjuINXwhl0U4kVmb6To6w/S09PzsR0574S5hRzf+5NzZGaz+MIu2kfCiHKzgVvL11i/mCA5paAqPp58cnvzH5oT8dOnTrC2tsbi4iJer3dbI+zy5cu8++673HPPPZw8efKOxxWPx/eMVQIQRYEHjnVw7ECU6/NZEqs5CoUCh48NMNLj39X59DPPHqGQukhmtsj1yTl6h3o48eQQPf0uVFkkFHWzZFobple39i+maSDK2+9JVs1AB9p20b5V6wZXLq2RTZWp6EUqShB745x2qhI9QdeuUqq7QU+3n9/9Z/dz6dwq7716HbsqIIgiQw/0cux0J4cOt6H8DWiBP3Khp+s6//7f/3v+6I/+iKWlpV1voJ90B/WXAZPXElBsoO6h+fK0e0gsFphZyHHiDnbbN3HlyhUikQiqYVDcpdDTFBmXU6GqiPjdClZZxx1043a7ae/ooFQqkkylWFpcRNd10pk0iqoS8PtxlSVEl4KJjWeXqUnDsFjNVanWTTyavKMJYtPsoY10Oo1c0XHtQpfyOxVkUeCD+QxZj8Gjw9uLg0KlweTZFTweDfFjTnIcYRe5tRIXz61weKR5o5dlmRMnTlAqlbh48SIvvfQSzz77LC6Xi3PnzhGPx3nuued2zV1plBs0Sg1EQ0Sv6vj9fh5++GGGO3wcfqiHK8kKcryI0u37yF0cI1khr4qMPj5Am1Sm6vMxNjbGSy+9xBe+8AXK5TKCXcbR46B0MYtmuVu0uXK5jNe9M/WklC7jHApycKC5WT2/lOPDhQzdQRd6sc7ET6bR42XAZl2RiB2M0D0YBFFA8mlIEVfrfRRJZDjmZTpR5OXJBF8+0YWyiz4AmjSq3od7EWWRtfNrdBoWLqeNoz+KYYMkgNu0MXM1rIZF5+lOeh/s/ZW0P98LwcEgjoCD5feXSU2kkF0y7pi7NfkKBoNYpsWbr06wfLWALIskZjNYls29Dze7sUbdaLr/1k3ax9vpuq9rS1F1O5aWlui7uRHeIYBaEAQkSWgFMG/GsQd7eWU+C1UFt9fBwtQKdgnGnu7D6dz5GmpNoTddHpYF5bpBaePfTVQrOjNTaVZnsxQ+WMSomdTqMzi05kYw2O3jyL3dHD3Rse2mvzmc+25x/VqCD96cZ20qDUDbUIjuEY0nnzq+r9erqkp/fz+FQoFsNsvExASHDh0CmlNBn8+3KYzeJnktydK7S9SyNWy3QskhYWIjAoaocNQ1SulMiSV9ia57u/Y0EunrD9LXH2RpSaGnp3vX50HzbxsMBltxB4IgtAqj1dVVDMNoTkJisRZ19PGnIzTMIlbDiaJJHL+ni2hsf2HAyUSJ7/3PC6zPZhFlkfYDHn7n9x6565zDT/HJY2Y6jWayo07qdgiCgMuvsvL2Ev/f6SxmpoJtC0BTuhDs9nP/54a5d2PaVy6XSSaTDKoqxk4smWZMLrbZbG74XQqCoWH7NMx8HTnsxB8I4A8ESCYSxFdXcbqcqIqGrFtUfAq9Xg23un3NsQwL22TXe4kgCBjGrXXiozhy7hf5Up0f/tkF8vM5Og9FtxyTIAg4A06cASe5xTzvfHeCUMTN8WO760nb29tpa2tjenqaGzduAE3TkHPnzvHhhx/ywAMPbAm63w2NRgNZlvfttu3WZE4eiGIMBTeiWvZugHX3RHn6N/pYWijTPafhC0g886UDxONxUqkUgWCDWbeMnqmixpr3INMwkaWd7yH1ZAUh5mbs6PZGcKWm860/Ps/shyuUyzpFXcd+fZ32B7uRVRlZFGjzOhjvCXC403fXJnO3w+vRGD8Z5cBhL15/EEkUdtVK/nXhI3+if/SP/hF/8id/wv3338+XvvSlLZ3IX3eUKw1EUWzl1e0EUZUQDIta5RanuFxukEqW8fo0QreFXObzedbX14lEIsiNBsYuhd7BIzE+61H4wcUVwv0+6hcTLeqgIAp4fT68Ph9dHZ1cn7qOJMvoDZ3kegIppSNqGkLBJOTceTEp1XQsmrbcDkUi5tt9wxgOh8mur5LOZ+li5w6qW5Np8zm4vJqkL1zjdibWzGyGylqZaNfHP78EQcAdcLJ0OUHpizqeTZNUj8fDQw89xMrKCn/6p39KR0cHmqbxzDPP7KipKSfLpCfTrF5LkM9VAQH7qsQXv/kC/e39yJLIY0faWVspkn17idBKEbnTe9d6PSNRplDX0U508Jl7emiklsjn8/zO7/wOf/EXf8Ef/uEfcvjwYZ588kkeftzFS9NnqcdLODbCkptTgO3vaWRrFC2bY2NtRL0a64Uab02n8DmbbpoXfz6LvlrEHfMgpqtYszkKM1myxzvQHDLIInKbG8dICGXjvSRRoC/s5tpqgcGIm3v6917sJUWi96FeggNB0tNpiu8WcaSbIn0EkD0a0fF2wiNhfN2+j611/GWFM+Rk6Okh/L1+1i+tk1/IN+nJAQeKR0HWZPSajCSLBDq95NZKpJYLVNIVatlaM7i200vbeBvhkfCe32M6nSYYDCLWNoyEdpjoud0qX7rNqv0mHnykn3KhzpW3F8ivlnC7PXQc9/L8l8dZWVnBtm2cTucWIxFJkVBcCtVcFeeGlkySBE71BfGKdSqpOWw7Rny1yNk35ijP5cGrEjgUbmXFFQoFZEGllKrwxv++wrmXZ3jw8wd56LFbG7OPOtG7eG6Vn/zPC1RzNdyR5vcxfWaZlRsOQsHoniYzt+NmQTc3N8d7773XCgl3byqoE1cSzL82jyGJLDtFFtNlKg0DhOb1LI8cIxPxEFAllt9bxjZseh7u2RLXsxP222gKBAKcPXuW4eFh/H4/58+fx+fzkclkuOeee7b9nmq1yj339X2kKdlPvn2NxUvrhPr8GDWTG+/EOTO22JoUfxIoJ8qkb6TJzmUxLRM5KhP1RD/V5d0BjY2Ygv0iHy9SuZFBcch0DYeRNxqzes0gu1LgZ/+rqYm776E+zp49y3333Uf6hz/E3MGJ9eS93SzPZVmbTnP4oV6efKiflQ8WKURLuG9ksTxq03wFiMZi5HNNx247WaUom6xR5TG/tKPDeVunF2dAo5iqELyNpdKo6AiySFvH1sf9fj+apu3bkXO/uHQhTmomS/tweM8mZqDXz9q1JOfeWWB8rG3Pa/mmc2pfXx8vv/wyr7zyCo1Gg0cffXRf0Wc/+MEPWFpa4sSJE3cdrSTL8r6HO8PDg7z11h/z29/4barVKt/5zncYHBzE7/fzuYfGWZy1yL67TNSnITpkKtUKXs/2iZ1ZbJCrNOh5sJu+8PbG5PkPV5l+b4mKS6aoaciGCosF3Ed1+o4EaRgWyVKdH15aZXKtwHP7kJ/shffff59SqcSjjz76NzK92wkfudD7y7/8S37nd36HP/7jP/4ED+dXAy6XimXZ2Ka1a7FnN0xsScSxoWV4+/U5PnhpmnKuiupUOHhvN5/74iE0Tca2bd59910effRRvvWtb6HU6xiatmOGEsBwzEPApVIO2mheFSNVQY5s3bBpTgeHDh5mcXGRWDSKGa9Q7Kixbhj40xmunn2XtXCIjo4O2tvakRWZSsNkcq1IV8CJW5ORBGFPt0TbtJpBvyEPi4uLLYfI2xFwqczZNu8vFrnn0FZOfWq9hNUwkV2fjJzU4dXIZiskEmU8vYFtP+/s7GR0dJRXX32Vz3zmM1Sr1W1i7PxinomfTDM1nWJdsKnIIgICncefYPVyg2vWJKNPDxNr9/DCY4N8z7RIfbBCcCGP2u5pZv/cAbZu0oiXKQggjbfxuc8MMRT18KMPZnE6nSwsLHD69GnOnj1LOp2mVCpxciTK7BMDTP10mvByASGobBOr25aNnq6RTpeJ3t/FYw80pz6Xl3PkKg1G27yYlk0lVUaVJaTFPGJRR/BrVCt1Kh4Rd7sHu2FhrJcoJSu4TnWgDTa/S4ci4XHInFnIcrTLf0dzFkEUWK+u8/r06zhiDvqO9RELxxBlEc2n4bhDAOmvC0RJJHYkRng0TGGpQHoqTXGlSHmtjFE3cDZM7EyVXKGO0TARPRK5bI6eoz1ERiP4unx3nIaapkmxWGzaac/MNB/cYaK3FyRZ5JkvHuL+x/q5dmWWkQP9eL0S8Xi81Q2vVCotzZfP5yMQCBA5FGHmpaZu5uYaEfFqNAIKb15a4Kc/NqisO2mUGrgCDlxHYyibAsF9Ph/lUglvu5OQ4iezUuCVv7hMo2bwxOeapi0fRaPXaBi89eMb1MsNOo/c0hL72zys3Ujz1o9ucHisbZse8E4YGBigUCiwvLyMw+FoFXrFeJGlt5cwFJErpTor2Qo+p0K739n0ILChopvMJctkXA1ORj2snlvFFXURPbw/Zsh+4Ha7mZiYoKOjg+HhYdbX14lGozuu36lU6o5anp1gWRbJhRyusAuXzwE+SK9kmbm+xLET0WbO48fUsmTnssy9PEctV2N6ZZrEWoKYL0Z1ucrhFw7j3YGO/imaUB0yxX3mfdVKdQpzOdAkwn2BVpEHoDhkYkMhkrNZ3v7RdVRXib6+vua9qVzG3GGi53arfP2bp7YUGqd6A7yYruComghLBSyXgujTECQBj+SkuJTG9Cg0jvUwPhqmwy0wOTnZyoe9eY0FAk4O3tvNhz+6geqUW0ZFjZpOYjZD56EYB49s9w1wOBwMDg7uy5FzPzAtm8vvL6HIEtIOJmq3w9fuJj6RZG4xx2DfnU0Pb8pQ3nzzTY4ePbpvXVq9XmdycpKlpSXa29s/8SnmTaysrBAKhbh06RLBYJCHHnqI1dVVNE3DqUp87pkRvpOukL6RIdjuwRatLawP27TQM3Uy6TK+8Tae/uzwjlTdxaUsmVIdwyPjdyiIokh1rUT2cpyoS0aLuOkKODG8GjPJEj++HOfLJ7rw7lNSdTuSySQXLlxgdnaWb37zm38jLqC34yPvnl0uF/fff/8neSy/Mhg9GOWST6WRqaHtkmNUTJSQ2jTaoypXL6/x2l9eaebHtXmoFuuc/8kUbq/GZ54d5cqVKwwODuJyuTAMY8+JHjQpkWNdfl6vJOk+GsW4sI6+VmraIm/a7CmqQm9nN8sXZwn2ROl65CjycobOq6sorhKZdIZMOsP5KxNovghF0c1CwSboVqnrFpZeZ3lhFo+mcOjQoW3RDVaxgehR8fZEcdg6c3Nz9PX17Xjit3kV4gWdmWSJo5umd9l0FekT6p4BKC4Zc80km63AbYWeZVm88cYbSJLEv/7X/5oLFy5w7do1VFXl6NGjyLJMNVvl+kszXJhKsaqCx6ER0WRsbEqaxPW6QeXyGqIkcuTLhxiIuPmNp0Z4KeBg4d0VPGtFnJKIHHAgupQtfw/btLBrBmauTq1hUAhoRO/p5DP39dIfUHjrrbeYn5/na1/7WmtDHg6H+clPfsIPf/hD/t7f+3t84bMjfM+ymf3xFPpMkpDPDT4NyadhNExKpTo1TaLjkV6+8PmDRL0axZrO1XiBsLvphCZio7pV7PPrCKqMFXZglHQkRUFTJSrVDb2QV8DKVqm+PYNqtiOGnSiyjF9TWS1UmUmWONK5+yQ2k8kQCoWI+qOsXF8h3BPG2+Ul2Pnr59y7X0iKRHAwSHAwiFE3qOfr1PI1eh7t47035kmsFoh2+3jiS4dwRxwkM0mKYhGv0NzQmqbF++8skktXOHq8g97+W9/14uLirQ17eSPf8CMaVgQCTnr7/S3zla6uLmZmZlrr2E3tSKFQYHFxkYbcQPJIFFeKW7SDuq5jGgpLkxUcIgR9TmSnghzaYXPo8VCr1qjXq0R6/WRXi7zz/Um8AY177utFkiRM07yr4mFuOkNmOU+oe/t5HOr2kV0uMDuV2XFjuBdmZ2c5ePAg5XK5FccQDAbJT+VplBvMyLCSq9ARuM2NVgCXKuFUnKwXqlzKlDnlcpC4miB8IHzHqd5uuBlsXCgWmJubw+1209/fj2fD3dntdnPu3DkikciWxtfmwvxuIYoivqiLlatJrDYPtVIdI9fg2usrLE/m8cccHDgeY/RQqPVesMHMcLvxeDy7NjsBjJrB9MvT1It1ogeiZMUsS+kldL+OWTZZfGuRQ1859GtHB98vBodCrL61iFXWEd17b3pzq0WMfB35WHTX5l6ox8fqZJLzZ0y++Q+fbT5YLmPvYnAGbJkmneoLkavovCuA5pHxrFexM1WwbNAklMMdLDstwgGL58Y6Wq6TlmWxvLzM0tISsizT09PDU88foFKsM/3hKvmVAggCoizSeSjGF//u+K6NG1mWGR0dZWZmhvb29l0dOfeDfKneZD3ssJbtBGfQSS5eYn21eMdC7+ZeZnp6mn/4D/8hpVKJbDbL6uoqIyMjexoRtbW1tVhDv4giz7IsFhcXefHFF3nmmWcwTZPh4eHWzyYnJ+nr6+NAl58v/tY4L37vGsuX13DWLaR8k3Jr6SaVioHulgk/0MUXvnCY7uDOnylnWVQbJiFZQrRBmM+jLhdxiSL58gy2JiH2elAOhAjKAudn43jtMvf1B1vr315TzUajQS6Xa1HZ/X4/kiTxpS996W9FkQcfo9D7+te/zg9/+EN+//d//5M8nl8JjPQEaB9rY+2tJaKajOy71fmxbZv6epmSbXPfI0NIZoN3XpugUqjSN94U+2puFaNmcvX9ZU7cH2V9fZ2nnnoK0zSbDouNBrqmoe1S6AE8NBwhU25weSVP18kO7OtpjHgJRAFho9tmVw2QBHpODDEhZZBKOZ59aICoxwu2Tcauc2EmzmwiT6VQImdU0RxOQMCybZKpBLWKiFSpUbhwhXvGDuLYMHaxbRszX8c5FkN0yDhpLrDz8/P09PRs64bJooAiCVxayXO4w9eiXJimtaMO8CNDEBBssG9jcFmWxcsvv4zb7ebBBx8EYGxsjOnpafr7+3n33Xfp6urCkXUwN5MmrkKb37lJhyYQdKu4NImVQp3QZIqu+RzRw1F6Qi6+/vgQ7/UEOH9+ldRMFjVdQ01VUBAQxKbtu2Hb1GWBut+BZyDKPePtjIbAzC6zVnOSTqf5yle+QjgcplarUS6X6e7u5tFHH+X//J//w6svv8pY5xgH8zUcUQ8z2RKlZBl7sQBOGaHTQ+i+Lh57pI+jg2H8G9Pk9UKdXEVnMOrZ+IoE2rt9rJs2tZqOsG5hSeAZDuGP3NYB93rQFwtoeRHXSAhd16nX6xSLJS5NL+E18lueLopia/H81re+RVewi45SJ1wTOTF+go72v958mV9myJqMHGtq9sJAzw4GGH3ePkzTZHV1tWlesGTy8p9dplHSmbm0xu/93x/F4ZBvUTZv3tAqleb/3uVEbzc0o1N6mZmZ2eI6uVmbJlQEZn4+QyqRomO0A6fHSTAYpD1yiKWldYIRB5IqoQ4GW2vY7XA4HeiNBqViiWCnl/WpDO+/NMOxE52Iooiu63cVsVCvG1i6hbyDbkNxyJiGRb1u7PDKnWHbNjMzM/T29qKqKpqmYRhG00k0X+P6O9eRPV5W01VCbm1XUyxBgKjXQaJQoxBwIa4UKa4U9x09Yls2xdUixXjzX3w2jmmYuH1ubKdN53gnvoiPeCLe2ugcO3aMM2fO8NBDD7V+TyKR2DPm4U54/PMH+UGmxupEktJCHtsw8XR4wZRITBbJLtZwOnw88Eh/6zWWZVGpVEin0+i6vuPvVVUVfU1n8foiK+UVDquHcfkCICvNBrUFxeXmZ/9Vz+X8qDh6KMaZLh/leAnv4N6FRTVewlQlXEGFWrWMx+XeJhmQFIlSqYRb3TT9rVYxd4j+2QmSKPCZQ22EPRrnFrPEOyqY+WrTbtEh4wu7eLLNS6+jTnF9iQ5/00hEFMVWY0nXdZaWlmg0Gpx+LMjxB7pZms1jWhaZ3BLTc28SjuztRikIAsPDw3s6cu4Hhm5hW/aO9NLd3hcEjD0c0eHWXmZpaYlnnnmG7u7upgnfpUscOHCA+fl5AEZHR7cUMLphcuHiGouL8MRTX+b06dMf6XPthlKpxPLycjOD0bK47777GBgYoF6vs7S0RE9PD729vSwvL3PlyhXq9Tqjo6P8/b93ilffnSC+YpBfKWA2LGRNItof4MSpLg72BnadvlUbJpWAA63XT22pgJatISYrSJ1egifbcTgVrGIDc7GGw2PgOtmOpdSIN0R8wTBGvdq6d+4El8tFJpPhJz/5CadOnaItNsr5t0ocOfjkrjmnfxP4yIXef/gP/4FvfvObvPDCC3zzm9+kp6dnx+p1P+4+v2pQZZEvfPkwf1UzWL+4jmMNnJ4mnbNSaGC4ZQ49PcxnHhnAoUi4HAtIUppioYAsyTjdLiRVolHVeeftd3niiceAphsecMeJHjQpdM+NdaBIIhdXckjjUSJVEylZwSo1QBQQ+zTqEScpVSQmxVDSc7QrfkJHo7z1nQkWZYGK5GF4IEi1VuX6WpGAx4m2MbkrYFKzdGoWJGoCZ68vcnSoC7/Xg5mtIXlV1P5bN1FFURgYGGBxYYFoNIr7tm5Y0CWzkq2SqTRaHGmHQ8b6BLOvLMPCloQtjoSGYfDzn/+cSCTCPffc03rc4/HQ19fHysoKjzzyCPOz87zx7TdZrDhxhDw7mo1ostQ0GKk2SE2mWnQqlyrz5KE2TvYFmUqUmJzPkF4vUSnUqZSqCCIEo346oy76u7w4Gjn8aoGYv4Ngfzerq6tIktTKsurq6uLGjRuMjo4yOjrKqfFTnP/Oec7GzyIoAmMPjPHCyeMUGhbVqk4tU8MuNujxuxht9+HaZH+eKTd1ops3lX4L7KMxiraNaVh4gg6Cu+gkxYCGvlzAPBhG8agoikJbWKIub8/SMk2TcrlMPB4nmUySuJxgYuU6Dz7+EI6KA72io3o+HiXmU2yFJEn09DRNEC6dPUepUMHhUqkVG+gNE0URKJVKW+l3Nyd6+9yE7Qc3r/+bk73bu6RDp4cIR8OsnFlhdXKVRq1BXbfIXEmjySKSX0PrDyIF9tZPKKqKKEoU8gUCnR7SS3kmryYYORi8a41eJOpG9aqUc1W84a0d41K2iuZRCe8SGXE7LMtidnaW/v7+LWt3LBYjHo+jZ3U8iofZWoV0ocRIx94bSEkUEAWBeEXHZ0JpvXTHQs+2bXLzOdbOr5GYSVDKlZAdMuH2MJIikc/kkQoS0z+dRvNpeHo8zNXmGDjcpKrdjIvo6uqiXqgTvxCn7qk36XMxD74e357GMLdjeCTC3/3nD/DGT6b48PsTREfDeG7q07u8JGezvPeTKcZOdODZuCdsbhbthkajwdz1ObKZLIVGgbffeZuekaPc+/DjyErzu7dNG72yc6H4KaDd5+DA/d1c/M4k8koRR6dnx+mtVdapZ2rYESddvRFUEYqlZhC6e9M0JJfL4XBqYG8yHCmXse9ijZHEpnZ3rMvPQrrMzEoCgFgkRH/Y3crwW1tb49q1axzelM8HzTXoZiRJrVZjaWmJ7kELj8dDMhnhO9+/wH/9r/+Vb3zjG/h8PizLbt6v1wqUagbdQSeHO/1EvVrLkTMej++YMXknuFwKsiah1wz28w2YdRNEcO4RXWIYBj/72c9YW1vjueeeo7296bUgCAJjY2NcunSJo0ePous6ly9fJhgMtu7RP/zBJJd+Oo1VN/B0+phbzDGwg7zlbmDbNisrKxSLRdxuNwcOHADgRz/6EU8//TQAmtZkEt3M7bv//vv5d//u31EoFPjSl77E6dOnufdgmNjjneSqDQzTRpFEgi7ljiYn8+kyJctm/POjJM6v0XhrCeVEG+HhMA5X81yRfBqIAo25HNpggIhXZS5VZilX51BHgEBg5+/Atm2q1SoXL16kVCrxyiuvIOlLFK/Z0NDgyx/rq/tE8ZELvXq9jmVZvPjii7z44ovbfn6T0vHr6LoJ0BF08dvfOMG5C3Eun1ulkqogCgJt422Mn+pi/FC0ZT3bPxJm6t1lFEFD0WSymRzZ1SKdx32Mjo60uOWtQq9ex1DVPWkr0DQ6eW6sg+GYh0sreRbSZepuGdMwkKQm3dDrUBiPeTja6aM3cICf/uwl3mkUSAg2WqJC53AIQRSomzaaw0DbNIlTVRWBInajjm42yKgaVxdTnOiRkUo67ns7kW5z9xNFkf6BAZaXl2k0GgQ32ZM7ZIHFQpWfXF7D65QRBIGUZVE3m52vT8KMo1FqoLgUIu3N71TXdX7605/S09PD+Ph2g4lgMEij0eDGjRv0d/bT1d7HpWtLiPkGAUcUaQf7YJciUa4aVHI1LNPaQqcKuFRO94c43R+ippvkqzpzC4tYhkF3W5hSLo2qVugbGdny9/3www+59957t7xPZ2cnq6urdLR30C/389qV15jLzBGIBujKdzGgDhBRAY8GUQ+WaZGdaWpWRp4fQd24KVYaBptLaduysbI1vJ1eAvvQyIluBSNewq7osFGkaYpIqW5iWvaWAlKSJHw+H6qqcu+99zLSPkL2wxzlTBVvlxdlFwv+T/HJ4JEnD5FZrRNfSDN0T5B6o0gqXdyusapUsJzObflWHxeSJDE4ONgq9m5vDgb6A/h6fHSvdFNOlPngrXnq3iyxsXZqioHLvz+RvCRLeL1eCsUClmFy6cwyB49EWoVepdpgcS5HvWagqhLdvQG8vu2/u7PbT/9YGxNvLqA6ZLSNa6ZeblBYLXLw4T669zERMk2z5dq3U0O0o6ODyxOX0Rs6qt+D22lSLBaQZXnnDMwNqLJIqaEjaCpGde/Jolk3WXhzgak3pzAMg0B3gOGDW7O2SkaJWFusyTwp1Mlcy1CdqKLpGh3HOggEAggIXP35VcpTZZJLSarRamt99nZ66Xu0D2/H/rVv0agbSRbQvNqtIm8DoR4fieksc9Npxo7v395eVVXCsTChYIiOzg76+/upmbTOZ1M3m4ZPH9Nh71cZgiDw7KMDVCs6N342g+tGGm/Y3WQoCU2fgXKyQqlmIB0I4a4ZLdqm1+cDy6ZUKWNZFoqiUC2XUSWV4CZHVqFavatC7yZUWWSkzYvbLOJ0Nif/m9He3o4kSVy+fHlXl0mHw8HISFO/WywWeeeddxAEgStXrnD16lXuv/9+3phK8tZMCttq3tOurxe5slrgi8c76Q66iMVi5HI55ufnm9rmu4DHqdA31sbkz2fx7UMrWkyUcLe5GR7duQF0cy+TTqd54YUXtk2URFHk6NGjXLlyhWPHjjE+Pk4qleL8+fO0d/cydWYFpybjHwqxcjXB1LXEXRV65XK5tVet1Zo5p5Zl0dXVRXf3Lbff2dlZuru7t7C6uru7mZ6eZnh4mHg8Tj6fp1arcfnyZWIdnehagFS8gEeT6A259x2FUGk018RA0IWzP0R5tYzat329ljwqjXQVI1NDCziwN712NwiCgMvlYmBggEgkwpEjR5iaTPPGizc4cPJvFzPpI69y3/zmN/nOd77D1772Ne67775PXTd3QNCt8ZmH+nno3h7KdQNREPA5lW10nJP39TBzPcX82VUsw0JAIDYSwhFIoqoqjUajSUXRdSzTYibQxXtaN5enSlwsLzAYcTPS5t3RKUiVRY52+Tnc4WMlX2UtX+PSlWscOnQQTZboDbmIbjI18AyeYuKDKQ492I14IYGxXETucFM3DMAG02pSJSQBQRBRZAVLadreCo0KVsXJ6myWvsMxGisF9PUSzqMxpNsKhu7ubhKJBGtra7S3t5OvGSzlDdZKFpV4iW6Hgi0IZGsNsnUdYy5Dd2/gjuYed0I5W8M7FCDid1Kv13nxxRcZHR3d1vnbjLa2NhqNBotLi0iyRMDnQ3DLpFJJFEVphs9v+puaNoh2M2PmZgfUbJgUVprB9d5OL++deY8HHniAsEvmRm6dfD7PcFeE3h2OY3l5GVmWW925m2h2IZPklnPMfzCP4BcQyyKrq6usrq5y6tSpLc8XJZHgUJDMjQyZ6Qzt47d+37bt/N0MUQUBbLYEnAps0JTrOqoib7NLdzgcPPPMMwBUuis0Sg08HZ5fW2fNvy4EAk6+8X97ANO0kCSRhYUFisUi6+vrtLe33+rYl8vYv6BAaVEUGR4eZmZmhr6+vm0NK1ES8ff68ff6KXywiNzlR424aBRLTT3OfqlOooDf56fiqrE0maBWNchmqlz4cJX4jesU1srYpgWSgCfs4sA9XdzzYC/ttxUpz/7GEbLpAsmpPJbRPMklRaLvRAfP/MaROx6HYRjMz8/vOMXcjI6uDhZKC9heBYSmQ7Le0CkUCjgdzm0aaGhepiICWM0GzcqZFUprJVS3SvBAkKXMEmNjY5TyJS794BJiQqR9tB1PePs0LJfLtbrXgiDg8DvQfBqleInzf3UewRaIjcW4/LPLJD9IcnXmKpHeSEtfY+omhcUCsy/NMvLCCK7w/s8fy2KHRQgQmnliprn7gmQYFmvxIsGQc0s+oK/Hx8CRAcyGiazIeDZ9fcXVIu5296dmLHeAS5X5zWcP8Fabh8sfrpCcykCm3FzvZQGty8fosTa6Yx5e++MLVAq1prEOgCi0pq5zs7NouJDcEoc3xQMI1Srmx6C4GYaxqw4qGo0iSRIXL17csYm7GV6vl2PHjhEMBlldXSUYDPLWuau8E7cJup0EN84ry7aZSZR4ZybN3znVjB8JBAJomsbU1NS2fMw7YexUJzfeXaKcrODexc8Bmk2aUq7GPY8PEPBub77e3MuUSiU+//nP75rvKcsyBw8ebBV7N/NPp6ZnqOhlKJjISQkEKFXypNPpHamp83MZ8tkanT1NHfbly5d58803+fKXv8zU1BSSJDE0NLTtb2PbNlevXm3d+zcjFouRSCTo6urin/7Tf8rq6irfe/ltMi8vUl+ZhkIDAhodR9t4/vFBugJ3bhBsJYPZe8qAmre+DR3wttfCqy/dYOZqki/+9viW+JibE2KAQ0fbOLRDxMPfND5yoffTn/6Uf/bP/hn/8T/+x0/yeH4l4VCkPQsUp1Pha3//JBOnu0mulfD6NNbSV3nqqS/jcrmIx+NNwWfV4EpOI3nsKeaVIF11Gztb5cZ6kfdmMzw8EuGevuCO9ApRFOgJuugJurATKvcObbfCnk+VObOQY7S3A79bxbxPoXphjcZ8DiNbxZGvIW8MaG1FwqPa6LKGaZkoloi3KhLwyCS6PbhyVcJVEyxAEPA81LPt/W7m01y6McdcwUJcrxFbq9FuCwScKtg2QU1iyoDMbJaGU2Eg4t7iynk3sHWLum5w36kujEZzYTx69OgdQ0EBenp6mJ6eRnAZBC1YNSw62tupVqvE43G8fl/rplau6wRyOYqii3qjjizIzP5slvSNNDY2KSvF5fJl1tfXGRsbo7u7m0AgsKvW5cMPP2zpBm9HX18f733rPbxOLw8+8SBHckf42c9+xsrKCg3dwrBsXJscvURJRPNrJK8liR6OIikSmiJtqesEUUDwqJiJMlJgH99rzUDQZMRNQdUN02o6XAkCpmntmYvlirhwRX4xRcWn2BmSJLY0v8eOHWsFeguCQGdnJ1Kl8pE67fuFIAgMDQ0xOzu7o2b3JirFBrLaPHfcbhelSvnuDBAE8AX95FN5rl5Z4p2fzJCcyeGL+gn3+ZFVCdOwKCTLfPD9SaYvxnn+7x5nZPTW+ujzaTz15T7qZSfLC3mwbbr6Ahw8Eruj22aj0WBxcXGLLnE3KC6FSDRCrpjDtkQsy0ZRFRRVoVqtUis0HYCFTcVipa7T7gTLkEjfSFPL11DcCnpZ5+XvvEy+LY/b7SZ/KY+UlOg70Ye8S6B8o97YRlO6Gc3RMBtM/GSiybK4nGIpuUSilEDIbJrWKxKBwQDpG2mSV5P0Pbp/J86u/gCXaVrxK5uOL79ewhV00Nsf2PW11UqzAVqp6FsKPdWt0vNAD3OvzJGdyeIIOpqupZkKmlej54EepI+ZzfrrAIci8dR9vTxwvJMbSzky6QqWaeN0Kwz3h2jzaViWzfyFNabeXUIcEHBsajrncjncmg89azH4UBu6kWFtrUp7ezsFQ2Ay0Mcr7y1g2TY9IRcH270tI5U7wTTNPSUsoVAISZI4d+4cJ06cQBAE8lWdqfUiU4kSVd3ApcqMxLyM9A9x8OBBPvjgAwKBAAVNobq0Qr9bpbGYx1gv4zgaJeZzsJipkK/qBDbof06nk4GBAaamphAEYd8mRQdHoxx6tI/LP50GbFyRrU6zlmWTzVaJT6Whx8eiQ+Tb55c50OZlKOrBoUhUq1V+/OMfU6/X+fznP3/HoctN99DN1NaR4SF+4xtuvv3H75Ipljjw+CDPPn8MU6+xsLCAbdstI6Y3fj7N2z+4TqPUwN/l5fjjYV59/bsYhsHi4iLDw8O7mrjs1tyDpl47nU5jmibd3d2UdZtIx8Pk3lsh5nIgu1X0RJX4z2b4Xt3gd754+I7OmI6NvY1l202Kpiph1YwtexVoBq6jSEg+DdOym7LP2/bsqwt5SpkqmUxl3zmhf1vwkQs9n8/X6uR9io8Oy7Aw6gaCKDByMIAgpxCEOodiw60xeGdnJ+W6wbd+dIblgsHJ7Dr5Wp1otkFQ1pGiLlK6yUvX1lAkkeM9gbs/Dsvm7ZkUddOie+NmKfk1HMfaaCwXEJcKaMUGgkvFVgTEcoNAw8QtQF5T0T0yjYhMalDDNRQmcWYNLwKqKDa78LtAdrhJNMqIy0naFsrULQF6/SgbF5JV0YmoEkKuQWUxz6IIIzHvvgTM1YaJYVrNEbsqkV3I4en2MXLQz49+9CNOnjy5pRtzJwwPD5OdzuKfNEkYArmKTsDlxNnlJJ/PE19dBYcPlyVyZLibww8fZHFxkfSNNJn3MtRiQdYSCZbeeA/HMQf5fJ7R0VESiQT5fH7H91xcXMTpdO5aBNqmjZW2sFWbwZ5Bcrkcv/Vbv0VD0njleoK6YXGow0tfyEW2olOqGZRqDWrzWda7PbQNhpribtvGsu0WtUnr82MsF/dFmTWzNZReH+Im06Fy3eRwuw/HR7Qo/hS/eGx22bxplmJZFqurq7iWlnBqDhZn0hSyNaLtHrp2cJ78ONhc7HV2duLYcN+LrxZIrJWQFRG9YXJz1COIIvZHDDxXRJU3vjdFdqVIeCiAP3DL2VNWJUJdPgLtXtZvpPjR/7zA1//Jfa3A9aWlJYYG+5Ek6a7og/V6nZWVlX3fJ70dXjwdHgbWYbaQJFuWCG+wLZxOJziclEolBKGpfao2TByKTKfmIJfMQgFcnUGu13XWF65Rv76CJmkkridgEVxtrl2LvGw2u43+thnhnjBz5+eYenGKarrKgeMH8LR5mLgxw4fTcaJBP33h5ibVHXGTvpGm41RHix5+J4yd7ODSezGWLq/jCjlRLJvqehmzWOfA8Q7W31wkpUi4Ii4cAUezObQxMfT6tB1ptwCRgxEUl0JyIklxpakbax9vJ3oo+uk07y7h1mRODEdgh9NZkgS+9PVxvmPD3LlVMAuUjTIOh0YxXSYUCXLksT4+/9VjOJ0KlUqF189P8k7kKGnvANFcFQGYSZY4t5jlMwfb9rWPMQxjz0IPmi6Io6OjnD17Fik6yBvTKTLlBk5FQpFF1gt1JuMFwh6NJw7EOHr0KO+++y7ewRO3HL8tu+k0bYOw8dDtE5+bjpyvvfYanZ37WydEUeDzXzqCIMDFl2fIr5VxBR0oDplyVWd1pUDNtBG6PfQ91E1dEri2WuDycp52v4P7e71c/+A1LMviC1/4wr6bYB6Ph87OzpbOH2DsaDvD//bzJFM5UutL5LOpLc6iqVSK5eU1XvveZURLIjYaYW0ywXuv5XjwoQexLAtZlnctcC3LYmJigmeffXbX4+rr62uxH6bWSiTOr9Drd6N2NI9BDjsRl4usn1/nxn09nLpDVm9v2EXAqZApNQiHnChdXhqzOeQOTyuH0aqbGGtl1MEAUshJutwg6FLovY2R8IWvHiOVLDMwtPd7/m3ERy70fu/3fo8///M/5/d///f/1liI/m1APF3m6uU1kslyk4LZ7uHoWDux28bM9UKd9HSa1EQKvaIjCAJVucq3X/82QlDgD/4ff7Dl+ZNrBRI1gV7ZwCpoGJdLlNJLKMEScthF+HQHhizx7kyKg+3eu6Y4LueqLKQrdGzS1FlVner5NbBBvK+T0loJvwVCWcc2msWBpFt4VFjqFah0OEDU8YkW+T4vpZxOe8CJ48ju1Iy1fI1avkF0qYwtCxhe55Zui+hSiIy3UclXsefzFP0qBZ+TwB5arlLNIFGska/qmFZzXK9WDBTL4ulHRnjvrVe4//77txmF7AcnP3OS7OzL9M0aLNuwUtcRgHJdpGo6sVaz9FkmXY+eJtwfJiJGSJPm4sWLzKXSmILII488wpEvH8HT31y8JElq6S9vx4cffsijjz666/GYDROn5iRTyCCKIplsht7eXq7EK6RLBQQB3ppKcd2rUW40JzgCQKLE3GQCshUsbBbTFUzbZiTmRZZE5A4PUsiBsVZC2WFDZBsWVs3ALDagoqPEbhlSmJaNaVl07INa8SnuHpZlMTeTYepakkqpgapJdA8EOXS0bYvJ0G6vLRaLGIax1WVzA6IoNrupkoNv+R5l+t++DDq4Q26OPNjL8185gvQJ29EPDg4yPz+PJLp562cLzF9ep1FsGkaV10oIokCox48oCThdLqqVyp66tdthNkyquRpiAcIDfqRdBPyiJBAbibA2keDse4s896UjFAoF3G73Xd/jKpUKiUTirhpJoiwSPRwlv5TnRF8bb03FUWQR303zBQE8Xg+WaZLK5CgZIke7g7jrFqETg5TiJXJ2g1y+QHffAN3tw/Q81kN+Lo8hGnsWXXpDRwnu3ZTpPdrL5R9cxu1yk81nGR8fx9k+yLszKXxFi6BLwedUUDwKpXgJvazvu9BzOVW+9Nvj/PxPLzD92ix6yURzyHQNhhho91GKl7Atm9RkCmhOP4ODQcKjYQJ9gT2bUTdpwEbdQBCET6d4vyB4fRpf++ZJJu7t5srZFd575SzL64scON7Pl3/rNIcOt7fWDl1QuJKTqUgOYokc4qtTuJwuwn0Bsu0SP59YJ+JRd7XMvwnTNPd1bXo8HqxAN99+f5pw0M9wzIOdb2CXdUSXAkEna/k6P7oc54VjHU0tuWzgVCXyVR1/fwBlQ9sVXyvQ7rR23IMIgkB/fz+pVIpisYg/0sb15Tz1ukF71M1QbPu9VFMlTp72cejYQ1y7uMbcxTVypQZrhRpmr4+h8Ta6BkNb2AO6abGaq/L/+eF7jPkl/q+/8cKekQk7IRAI0Gg0tugLnapMb2eE3s4I8Xic8+fPMzIygsfjIRKJ4HB4cWhTVKs1KpUiDV1npHuQz33uln/A9PT0ju83PT3NwMDAnoW5KIr4fD5yuRyJZBWpYiJ3bKWqKiEH4lqJ9fUS3KHQ8zkUjnb6eHM6Rcij4jrRDjboy4WWe6kgiagDAVwn2rEFSJXqPDoSwXdbk3qvhtLfdnzkQu/w4cN873vf4+TJk3zjG9/Y1XXzK1/5ysc6wF8W1HSTn7w0xeRbi1QTZeSmbIlrwAdtbsYeG+CzTw6hyiLFeJG5l+coxotoPg3Vo2KbNquXVmlcaeAd9vLh+x/y5GefBMAwLS4u57FqFaTpdepVDzhEHP1BajbYy2lQRWIP9zCfvXN+2U5YSJepGyauzYvJUhF9pYDS40Or6pg1Hcu1caLb9k1SM/JaEWelQmXjXlvI5VGjQda7JYaGIrt2eHTTYjlXxZurIdYMjHY32LRMam5ClETaTnWx8sYClbk8Ka+DgGvnz5ev6BufxcKlysiqgJmrUczVsI+EeXXqGv/khQfp7ene8fV3gqRIPPH3n+Dl//EyrqTEteUCa1UDu9zAqVs43CrF4QB/srLAidfLfOXhcbxdXtpG2lDnss28nqBA1sxSXi+jKEor3+t2zM/P45JcKFWFcqLcpHXctqERNnSA4VCYZDKJruuoqopTrVOoGaRLdUTbxCVZtAWbk1DbtjGKOp42L0q7h0rDYCVb4cP5LKW6yWibB79TxXmyncr7K+grReQN/YCRrWGkKljFOma+AXUTudND9XKi2Snr9JL3KYQCGkP7dCP8FPtHMlHiR//7MisTKerlBpIiYZsW52SRt3r8PPGlQxwZ26rlNOoGheUCRs2galT5q5/9FcOjw3zhC1/Y+U1yOd6aqDBrdxFu8yM5RbLrRc68OElHj5/TD9y5QWKa5p56tNsRDnXwh/+vV8nOl/F1egl2+bAMi1qmSvl6mvWok45DsWaW5c3oh32inCpjNQwcETeSIu6pP5VkAUfAweSZFR59aphUKnVXxRo0bcQzmcxdmzMAhIZCBAeCMJflvoEo5xZTFGtOPA4ZRRIxTYtSw0CUHIwEZbypLHJnmO77u1k9t0rjWoqHu2KY5QY1qUY+lyc1l6LrUBeFcmHH98xmsgRDd86ulBQJd8hNYa6Au7t5bXs1hZBbQ8GimE2j4Ee2ZURJRJD2r7ctrBRIvr9MnyjhORUl0tuGL+RE3qVhWS/USU2kSE2miB2N0XW6C+0Om7BPjVf+enDgcIzxk51MLHwPj2ly+KTCgUPRLQ2iqfUiiXiKruUEZV8UZ7dCJVuktJTGe7qHtS4v11YLn1ihV64bnFkqEQz4adMUqu+voq8UsesmgkNC7fHRMd7GSrnBG1MpvnDwCHPT1znVO8y7s2nS5ToOWaJQ04l4NE60iXzwwQccPXq0xby6iZsB7e+du8Qf/fmHZC5lEBomSreXz37lCPce2MrOKZVKuJxOBvpjHD4QI//CAf70nXnsUoPRdu+O66giifSF3dRrPZgeJw1kPor4IRaLsbKy0nLS1U2LhXSZRLGOZSu4w/1MzS+hChYHDhzA49E4dH8P51+aprBQxRf10jGgMT8/3yoGd8LNfLznn3/+jscUiUSYnp5GVQVsScRumLCp+W83TCwRHK79Xc/jPUFuJErMp8r0R9y4H+jCSAYxczUApIADOerCFgXmU2U6/A7Ge361snw/8sr31a9+tfXf//Jf/ssdn/Pr4rppmBbf/8EEV34yjc+l0DkSQti4QdkNk8JqkQ++c41Gw+CZhweYe3mOSqpCYChEolwnW6wjSQKL9RThvjAPjDzAsOMWP6LcMLmxsILy8k8ZPTNHHReHr15gXtDpevBBxB4XxaU09SUVw+kmX717y+iVXBXnpiLPNizqs1lEt4ogic3YAEHEtKwmnWFT8WYHnfjX6xRKJrpXplgs0OYPUG1Y1A1r1+lipWFQaRhE03VMh4RpgSwLaDtMDJw+jdhomFquSnYhR0EQ8HZ4txQ+pmmznK2imzYBl4qtm9SXixiSQOzBbnpOtrOSrZC23exfPbIdilPh+G89yh/+nzM0qk56loootojoVhB9KoIhUM06ODtbYXH9NX770cMMPztMfiGPbdsE+gJoPq0V5BqPx5mdnWV8fLxFX9MbOq/+6asM+4a5NnMNSZMI9AXofaQXx6apq6zJOENOivEiaOB0OMlmc5TrFqIA7QEHvUEXsmBTKhWRJAmHpSA45WYnk6bg/nhPkLqRZiFVpljVOdrlJ9buwf1AN5VLCWoTKfTVUnPRlQQEWUT2a8hdXpQ2D7ZhYpYb1M/GyZsmx+/vQW1YsL8s2E+xD2TSZf7q/3eW1espQn1+opuyrRo1ncxCnh/9yXmkv3+qFeCdX8yz8NYCpbUSgi2QTCUpzZSYr88zc2RmZ/OABx7gRuNeHIIb91/9GfyD36NjwMny1TU+fPsGXX0yHR0de2pQ7qSduR3nP1git1Ch/UAEeWPiIqoSnePtzKwWyV5LEeoNoLlVVFVDbzRQdtH1bUajpmM0TERVxh1xYXPnzDtf1E1mMc+Fs9Pc+8DIHZ9freoUCzVCYTeVSolCofCR2ALQnFQNPDGAbdqwkOPRnghzxSJlW6ChW4giDITddDpUnKUGzl4f3mNeMnqG3kd6kTWZ4moRNeTn0D2HKK4UqbqqFMoFMtnm1H9zUWeaJrqu39HB+SY6DneweGYRf73ZaOsIOHjK1YYiiciSQLFQZPnGKoLbhTqXQVjK4XKr9A+EdtTp2rZN4nKCxbcXMesm/n4/dt4mFN2bgqb5NDSfRqPcIH4uTileov+Jfnxdvj1f9yl+Mchmq7z5s2lmLsQxDYvOkTD1uoN/8S++uSONcfnqNOp//2+IaQNHfBJZa+B76jMYmSq1mTRWUODqks3TR9p3eLe7x3SiRLJUZyjqoXY2Tn06gxxzI7YpWGWd2o0MKBLtx2LMp8rkrAjlcplHTgVo9zu5Fs9TqhuMd/sZ6wrQ7ndgGP1cuXIFVVU5ePBgqyATxab+ea0gkz63QkxTkKIOcnMF3nh5hoN9wS3TovX19S3r8Gq+TrpmMNS2c5G3GcOdoZbe8PQdplu7oauri/n5ec5MLnA5abIyn8XIVBFtsJ0KgT4f471BGleuEvD7eO5Lh+nuC1DM1+geCDI41DRrKZVKzM/Ps7y8vIXyWa/XmZiYYHh4eN/MiN7eXs7PrCK0u6gkyrgVCdEpY1V0yskKQq8bu5KkVgu19ky7IerVeOFYJz+8tMrUepGo10GgzY3SviEPsm2yFZ1EoUa738Hzxzq3GBT+KuAjF3qvvvrqJ3kcv9S4Np1i4rV5QgEHrratNyhBlfD3B5BWi1x5eY4OUcKMFwkMhbiwnGcmVcK0LGwbXOFhHjt9P0EEkhNJYmMxHBtulcVCkUo4xChz2IApCpQ9XgqFIkGPH5fLhTcYYjmbY21tjWJMw+vdnwbBsmzSpSZn/SbMfB2zUEcON+l3LlXCqUpUdROPdtvio8k4JBW1qqN7mxdOpVhAdPqo6uauhZ5lgW1ZCGYz265uWgRd8q7Pd3o1uvt83GhUKedsitcSuDwaTp+G6lLIVXSqhSouBGqpCoZpI4cc9D7QQ/eBCJIoUKxbXFrOcazLf8cMlt2gmxY/m0igmxptdh3aPEjhJuXUtm2sUgPXUpHeqpPUcIg3Jlbpd84xPj6+pft3M8g1FotRKpVYWlqiVqvh8Xj48EcfcuPnNzj6d4/ijXnRqzqpyRSWaTH6wmgrskEQBSKHImTnstiKjawoXFrOE68J9IW3Gtf4fD4sw6QwnUAZCuL33toohz0ag1EPN9aKVHSTyyt5josCQY+K6JAQZBGl04vgkBAUEcmjIgUcreBqQZMQXSopRSAiiITWKkx+d5LeR3qbE4pP8bHx9quzrF5PbSmGbkJ1KLQfiBCfTPHq9ycYPhBBL9SZfXmWUrbKuiZSaJhk9TJhR4x720/R07bdIAmAF16AbyeazZzOrpbLpSRKBIIBIpFIy7ilo6Njx4JuLze8nTB5fg3FKW/7XJpbwTsUJH9+jfx6idhgCM2hUSwW91Xo5VaKaEEFWxeRZGEfZR6IsoShG1gWu5rE3MTcbIYf/c+LFNMVfO1OnvxiP4cO390E8HY4Q06Gnxlm5YMVMjMZZEuhQQNPKIBtmtTzdVy2QHgkTOfpTrwd3lbTyD3mZvAzg60G2OqZVTSvhi/iQ5ZlJFkimUwC4Ha7OXv2LKFgiFjb/kLP3RE3nqiH4mKR/GAef9DfWmMK+TozVzIsXkhR8ki8eTGOLMtoLo1Ir5+j9/Vw9EQHwWDznmLbNmsX1lh4YwHVq+Lr9lEulXG59z+bUN0q4dEw+YU8Mz+dYehzQ58We58gKjWdi+fj1Co6bZ1eDh/afp6Uyw3+zx+dZelSU18pygLX31mko+NeDH0X+n5HO/h8wMZ0vvfWWuR0Ogn4AwgYzM3N4XQ6W47TlUoFp9O5L6OTzVjMVJBFAcoNGksFpIir1egU3QqS6UBfzOM40CxaVrJVDh06xI3rkxw/fpyjO2TIyrLM8ePHyeVyfPDBBwwNDbWcPi3LoljQkesWSpcTQZFwOmqUi3WqDbNV6N2cpG3GtdUCkiBsy+k10hUwbaToLcMWURBwazKXlnOc6Al85D2N5Y7wk/evYV8v4c/VkIymxMMCqot53hgp8/B9PfT5FS5eukDvQC+RyFZW1M1sS8MwqNVqpFIpRFFkdnaWH/zgB/zbf/tv9388lsVAxEP+tIOV80UqKwUUy0aXBKxuDw89d4DHjrazsDBPrVZjYGBgT+pqb8jFb57s5sOFLJNrBd44P0d7RzuSKGLZNgGnyv2DYe7pCxLz/ep1pz9SoVer1bh48SLHjx/fUz/06wDbtrl4dhWhpOPcI6zW3e6heCXB5Z9Oc+JwjES5zmyqRMCltAqb9UKdq/ECT4zGKK9nmD47zYFHD+DRZDrDPt71Bqm4baQqzLpDWKqCU3NgJivIHR4sr4pH93BwsId6vU46ncbj8VCtVrl27Rqf+9zndjw2q2XEseXB5r8N+o0oCkQ8GvOpErZmI9xmVCuKIh63mzLN8O1CoYBP82wTLG+GJAqIoogu2hRSOQo+jQ6PzK5ruG5RRefQ/X18/lAnExfjTF9cp5itoqcrZEoNGg0DJeBA6vfTGXLhTVVxZWrcXP5CbpVEsU6uqu8YR7EfzKfKzK0UiCwVwbZRum4V1IIgIHk1RJeCvlzE45DJdnTy7FgnV682u39jY2NbNsGSJCEIQivTJ5/L88q3XsHv8PPGmTf47NOfQ3Uq+Pv8rE+uEzgcoH3kVqcz0BfA0+ZheWoZusNMri3Q39m2ozupXTFwB7w4R9vIpNOIotjMxhJFBiMeijWDeL5KuWFwfT7LofUq9moJdTjUEi/vBNuGRKGGW5U5ORQm7FIpLBeYeWmGwacGCd2FgNkw9nbp/HVEoVDj+oeruEKubcXQZoR7/aTmc0xcXSekW5STZWZUgdl4AU2W0B1hOk7308g0SE+nsaLWto0G//yfc+BPv8Zb3nspHzuN07QpJMtNw6ijsS3GLfF4HMMwaGtr45VXXuHQ8CFcNRfFQhFvjxf2zvxuoVKs70qtC/cHKUymKC4WiQ02zyNJlLAME3GHHMubyK+XsG048fggH/zkOrWyjOwWd7bx3wSjrqNbOl3dd7bJfv3H10nOZvB3+1ifyLBwuINDuye17BuaT2PwqUE6TnWQncuydn2N65fXiM+XqRoWnv4A993TwYGNKIibTaNqtcr8wjyhUAiP00O9UEfZ0PhJkoQiK3g2pmXFQpG52TnijjiWINLdP0zQrWDbNpVyBbdnO/W6Xq8TOxSjXq4TvxJHGpHQfBrz0xkuvzZLLV9H7vYSHWtHcSk06nVKuQprMynWbqQ599ocz3xtjAOHY+Tmciy9vYTm01quu9VqlUh0Z/rXbhBEAX+/n/x8nvlX5znwhQN3pHEahkWxWG8VnZ9iOyo1nb/4o7MsnlsDy0LxqqR+4wiPPrrVUfHy+VVWryZoGw23HFN9MQ/xawk+fHOe3r4glmWxtLREb28vgiDQE/Zw/vS9yNf/kroURPd2IqQqWGUd7Xg7hYbBw8MRBgbaqVQqzM3NIcsyZ86cIRwO89hjj93VZ6kZJrIoYDdM7IaFFNy61giajFmuYtVNZEmkblj09PRw+fJljh07tudkLRAIcP/99zM1NcXi4iLRaBTDMIhEncx5FCpLRVSPSrHSwBO+pf2q1+vYtr1lImXbNmuFGp7bTJOMRJnyO8tYuon7vi7UTXtNn0MhV9Wp6Ca+j1Do6abFa5MJ7OslQskqcpsb0dl8f9uwENfLyNczvO9SGGkb4eTJkywuLnLx4kVGR0ebRlG34WZsg2VZ/Lf/9t8wTZP/9t//iLDjHhDgi18bx+1WsaymWd7thfv6+jqnTp4AznNoZJSZ+TK5TJVgxMXxo20c7vAjiU0zL8uymJ+fp1qt0tfXt6shTczn4LmxDg5HZP7HxDs8PnoURVFwqRIDYXcrQuOTxIWzKzgcSoth8zeFj1ToORwO/tW/+lf8p//0n37tC71Kw2RtKoPTp+7ZZRJEAYdDJrWURzzZSa7SNArZPL0KuhTylQbvn79I5uoy5aky//zEP0fWZe7zRXh/scZ1fwej2TxZIUR7SkR0NpC7PDiPtbFeahDzagxG3DgUH5FIhGKxyHe+8x2SySQBZ4DB2CBmfSudVhIFZFGgZtxytBMUESQRW7cQNhaPgEvBpcmU6wYebRPVx266UXkDPtaNNNg2hmlSq1b3jLzyaBI+p0LcaaFUDUQXFNJrLNRLdHR2bOmmW3UTS7ApeBTu7Qox2hdktC9I47mDpLIVMqkKb0+nmMqUGe0L4nIq6HN5Kisl7LLeyt8SN6yPrb0q0DvgWryAnSwjFnTknp2npoIkIoUcuNJVkvEiywWde++9l2w2yzvvvEN3d3fLgliW5S1mLKtLq4Q9YQYPDGLIFql0mvfefx+jXsNO23hPebcUeopLITAeIJnIcuFSEhkBwdSBW+eWbdmYmRpmqoLS68MuNvA0FKSIg0w228oDGuvyIwiwkq6QO79KomLSeSiKsEfhVddNUqU6AZfKPX2hVgHt7/VTWC4w/9o8mk/DvQ/NnmlaCMKvX7Fn1A30io5t2oiyiOpRETd9/sX5HOVUldjw3tNR1aVgNExWFnKogoilyiQKVYIuFbcmU6jqzCXylOLLnFk+w8CTA9sKvRVB4ESnTtJrMm8pFK6n0HwaJz83wolTt7q4N7Uo0BTaf/jBh0x8Z4LxnuNYlkWoP0jw7wT3ZcgRbHczHy/u+DPNo+DqDSC5FNaup4gOhXG5XBRLxR1ZC7Zlk40XaZR07nthlM994RCp1TLTZxbR2mTcrr3Pw+RSllh/mJ6+wB2Pu17RkRwKbr+DnJCn9hFo83vBGXTiDDopaDLTby0h+DVcISfFbJVXvnUFr9+xRZN50+Y9k8kwPzeP3tDR3M3rUZRETOvW2p/JZhgcGsTj9pDKl3Bl0rz+8w8RsXB73HzmM5/ZdjzFUpFgLIi/389MYgZZk5m6EOfaBysYikj0sX6U2C0tscPpwOF0oId1KuUKycU03/8f53j2q2PYUxnsDUv5nWCYNuuFGsWajmWDLAmE3Cphl7pdqywI+Pv8pG+kWTmzwsATA3satKRT5Y18Pm1Xc55fd1w8H2fx3BrhHh+aRyU9l+X9F29w4p4uvK5b1/T6agHLZksshig2cxjnJhJ85zvfYWZmhlKpxB/8wR8gCAIxh029PcRSd5TI+CnkoBNsG+1ghEzMiVcSObzhM3AzlDqdTnPlyhUAbNtJJeeh1shy5M5RlnhUmYZpIXodiC4Zq9hACm4ynis1kNwqoktGz+m41WbzdXBwkJmZmVYTdi+MjIxQr9d5/fXXicVijPVHqT0/ysQb81g1E/89HZw84aeUz+CMRlleXt5Gnb+5LRFv60ZZDROr0jTBs2/bvwnCxhbsI25pFtJlVuaz+HO1LUUe0JRodHqwFwoUlvJMxAv0hlz09vbS1dXFjRs3EASB0dHRHYvhmxnQX/3qV8mkLc7+cAXbhv+avUwkJpPP5/nH//gfb2OF3IynODA6wuTkJL/77OldIytEUWRwcBDbtllYWGB+fp6enp5dYyYaxSxH2t08ceijhZovLmR577U5Vm6kcfkdHL2vm/sf6ttmUlYq1Xn3pWlEWWT0UOSuNOufND4ydfPo0aPMz89/gofyywnDsjF1E3Ufm1JBlbBNu7mJlURsmgXSzWtaNy0kUaCcLZBP5THqBq//l9epp+vMzi0QSdVZb8RIR7yopokiyJTLJXRLJrGcwgi5eGK0e0vxqGlaqwP/+rfeIddWggELHtl0XIJAu9/BtU0bLdGnIUddGIky4oa1rSqLdPgdzKXK6KbVohYIFQPbKSFGPHhLdYrFIqYtUC0XUSWRmt5cmBRJuGVXzIbDX9DJtF/D7ZIJF2tIbgflconp6WmikWhT3GvbGGtl8i6TYH+MQx23qDmqLNIZ9dAZ9VBxyKxeXsXlbOa3qb0+BLUXyau2CpVCTcejKdsclfYL27ZZzVVxlXWQhT03E6JHxczUoFgnV2lOOoPBII888ggLCwu88cYbHD16dEu4qWVZXL52mS/99pcwcyaBvgCmafPk44/y2k9fo2SXqFPnypUrxGIxIpHmAmJ6TJwPDVBcvUqv7iI1uUJbXyeCIDRvErkaZq4ODgkSZcxUpemm6tVwDQdRR4Lkcjlsy+JYZ4BAokI8WWXer0C+hFkt0RYJozmamUmZcoNUqU6xbiCLAgNhN6f6QsRu66T7un1kpjIsv7/MyDMjW4qXnSBJ4q9NkWfbNoX1EpMX1pg8u0IuW8M0LBRFpK3Dy+H7uhk+2obD78A0LGzLalF294IogGlYyF4V0bSQJYFqo2m0VNUt3JqCbZiU62XK5XKLhlkr1Vi4vkDQE6T0B/9PDnu99NShDsS6fPT0BKk3ajhEx7abVqFQYLR7lORMkoncKj0DfWjxMuVEGXXgzoXekVOdzF9Y3xq4vIHUQo7YoQiPfekQb/xgkrXJZJPm6RPAfStA3WiY5NdL1HI1XEEnj/1fjvDIk00a5di9PSxcWke0REqlEoosozm3U3QalQaNcp3jDw3cMSMPoHPERXIxx+q1JL4OL0dO7j9+4W5w9fwKZtWkc6Mz7Ak5iV9NcO1cfJv5DjTzw/xeP4s/WqRYLtLl70ISJXS9WYjalo3L6eLhhx9u/f+qbiGOHeHixYuoikoymUQURHx+H4rSnPRBc41yBpwcOXaE5FqJ6bMNrH4/7Qcju66HiqrgV/04XU5WJhJ87//9JveNtNM1vn1qapo2s6kyc8kSmWoDa8Mp2LZBlSViXo3hmIfO21x9BVHA1+MjcSVBcDC4J2U8GHJSKjU+LfL2QLWig2WheTay4vwOqhWdatXYUuhp2q1zYzPMhokUkpmYmCCVSiEIAu+99x6Li4skk0mO9h7gW6eOEerqIOC1UGQZ3a4SKJs8MRrBK+rU6yKq2myiF4tFHnvsMSRJ4sxbyyTP60gBgSefrhK4g8PzUMzDhwtZDEVCHQ5SPb+ObVqIrqZGz64ZqPdEaGw0vfsjzWbQyMgIL7300r4KveZ3oXHy5ElSqRRXLp7nyYce5t7jHTQMi4hHI+hWSaVSnD17dsffKYoCXodMPF/b8rjS7sF1uhPLsFB6t1KTq7qJJovohsWV1Tx13UQSRaJejU6/444010SxjpGpIhn2liLvJgRBQPQoONI1ZpOl1uOSJHHo0CEqlQqXLl0iEonQ3d295VyYmpria1/7GqOjoxiGhSZOY1kWgWiJV179OYFAgNXVVVRVJRaLIYoiyWSSaLTp1O71eqnX6/uK0bjpeArNSJylpSW6urq2xcZs/v13i9XlPH/138+SXc7jCjkpZ6u8PJ2hlK/xuS8c2vJcj0fjnicG0Bzy32iRBx+j0Ps3/+bf8Fu/9Vs88cQTPPXUU5/kMf1SwaGIaD6NeqZ2x+caDRO104ORr9Pe7cXrUFgr1Ai6FHTTJldpMBr1MOQfY3bRTcbK4BSdJEny8Ocf4BHFyR//4HUydQfhtg6U9jYapTrF2SLaQomRoyHkDoFLxfiW9z1x4gQAve4CqckstUpTNOtwOHA4HGiaRofPwYWlXKtrIogCWr8ffaW4JWAy7NIoenQShTo+p4KEgJCrYQ4GsD0qATlAsVikaoqIls7rE6sbWhoBWRLo8Dto8znwOxUEQcCnKXgjASoHS0hTq0hJHcstYTtEkvF18kspoq4gQpubXH+YXgp070K3GY55CDhVMqUGEa+GIIuo3bcWRdOyyZbrfOZQW6sYtjeEuLpp4VKlOwZw2huMVsHijhSwJiUBsLdGCRqmhekKo0dU/tfb19EbOvVyg6FkiUZ6hbb2NgY6B5j+yTT5pTzOoBPN1Dh94DTzg+tIwR6yOZ2llXVqtRk6OjVKhSxzUjfaY714dZviWYOGqeNwaEiyjF2WkMIOlG5f629pWzZWoU71/Bq2bhI81gbYZBJpXLNJhrr9LGAxt7CMJgvk6xYub4BksU7DsJCkJm3E65Ap1Q3em00zEvNwoM2LtMlxz9fbLPZ+kjzPSrbK/U8O7plH9utQ5Bl1g/d+dJ3z7yyRzVSw3QqqT0N0ytQMi9RUihvXksTaPTz0zAjOiAtJk6lXGzjcu9PSbKs5rXZ5VAL9AdYvr3Mo4ubyeom1fBW3JnMs6sHjGSf0UIiG1sBrepk/O09qNoVH81Br1CgXykg+CYdTwetW8DoFdLeO5bfI5XKYprllAxGLxfA/6Gc+O0++UKewlMDh9VAoF5CLMg6HY0/Dj2MnOpm+P8n1txYpOau4Q05M3aScrKB6VYZP+xk73sbAcJgr5+NceX+JxFyGueVlNFVrHosAvjYPx58YYOxkF909tzq6Y8fbuXqqk2uvTePv8SJIIoV8AY/HjbhBo27UdJauxOk4Emb0SGDPv59lWczOzvL0c2McPNJPNlWhs8e/5T0/SdSqRotZ0YIsUipWd32NpEh0DHaQW8qRTCSRJKm1WcpkMoTCtxpMgijg0iQGBgfQHBqKohCNRrEtm3whj67r5HN5/AE/lm7hCDpo62/jx9/9IcVcnc4jsTvmbQKomkrPaDtrL04x55bx9DtQVIWrV65y7733YgHnF3NMJYpoqkTU40DetJbUdZO1fI1ksc7xngBDtwUXK06F6ckkS4U6X/xXD+1arKuqTCj0qQvnXmjv9CJ7VDJzORx+jdxKgbaxNgL+revP6NEY51+ZJbNUINjVNEgr52qYNZNTD4/wyJOfY2Jigvfffx9d1xkbG+PIkSNUq1VSq4scefAw8bJNQ9eJuUR6fTIeyWRxcZFarUaj0Wi91801JNrhITmdwlR1lpdmyeXcaJrW2s84HFubUf1hN11BJ0vZCgOjIQRZpDGTw9qIV9COxZAHAkyuZBhu99Mbak6ZZVkmFouxurq672w8SZIIh8P4fD7WVlcwTZMjR4605BrBYJBkMsna2tqO0S1HOn3MJEtbcm0FWUQb2Vn+sJ6vEfU6+LMPFshW9Na2xKGI9Ifd3DsQZiCyO4vBskGw2bsgFAVE02yGid82WXO5XBw/fpxkMsn58+dbhZ5hNDWWL7zwAtC8rz/13GjrdcGQn1wuR29vL7VajZWVFWzbZnFxkbGxsdbzhoeHuXHjRivc/SbyVZ3ra0WSxSbVdbTNS4e/uTfs6emhp6en5Sja3t7ecgRNJBL7Ltxvx7n3l8mu5Ok8HEPcWJcyKwUuvbXAfY/2b2s4nL7/o5lyfdL4yCvdf/7P/5lQKMTnPvc5BgYGGBgY2MbVFQSB733vex/7IP82Q5MlDh7v4L3JFLZh7Upxs+smNdNi/IlBxLUqDtPmdH+Iq6t5chUdWRQZCbloS9dYuJzAHXHjHHSSrCa598F70bTm4vovfus5EsUay9kqpbqBHHDQdriNmKZgrJewJ226HukiPLKDOOZYkxd+8eJFgsFgU8eXzZEpVknmKxSyJa5XcnjU5mewBQsrYGJdX0EKOZC8DmRZIuIUaegyuUINf8mAqAtjMACApGiUcJI3LdyiiWGauDZ0NLppcWO9yHyqQptfozvgIl2u8/njnSy3ySx1+KnNJVHXCki5JpWx7rSY0PJUHRbPHx7h4T4377/3LqdPn95mlBB0KXTKZSYKMqZtE/FoSBubj0rdYDlXoS/s5viGde7UepHzSzkWMxV008KpSBxs93Kyd3dBrigK+BwyOYeEo7F3eLNVN0AS0WUbWbCwLJvLK3nOL2VZzlYxdBPZdGPoBudnFlj/8TmoZPjNJ04TGAwy9PQQaxfWqKSrJHNV1i2BRDXIj//LGbA2FltZZCKs0ne8nZlGjv7eCKpDIeQdpFgqEWlvp3YxiZGpofYHt2zGBFFoGqooEvUbGdROL1ZAwVkWEHSZekDAiGfRFq7xoJ6n9PQXWLdEwh6VNp8Dtyq3CjrbhmJN59JyDsOyGev0td5L1mQM0+LSD26Qs20kWbyr4OlfNRh1g5e/dYUP35jHDDoIH4ig3L5uRFxUagara0Ve/PNLPP75g0T6fKQXCjiGdy/0itkqDr+DA4djBNo8xI7EsC+tc9rnwFBEVN1CKeq0neog3BVm9cwq75x9B4fDQaQrguJWmJ6bZiG9gE/3MTI0gmzJrJ1bQ7oiETkYoeveri3Or5sRUAIsv7dMOpnmwGMHCA2FWnrhm9Mk2LqpuLkxOP14GNVtMHM5TSlTRlYkBu/t5PTDAxw4HGV+fp5YLMYjTw7ywKP93JhMMDW5iM8bwK6aOFSJ4UNRwt0+pNsMnVRV5stfH6dUKLJ8JUUjY+COuMkk81i6jVWxadQb9Bxv56v/4H5Ms0gikSAW266tqNVqLC8vMzg4iCiKjIxqMLrtaZ8o+kcjTL+7TDlXwx1wUM7XwLCJdTtZXFwEmlO82/Upnk4P6ek0sf4Y5VKZlZUVFFXBsq1dzXI2b2gFsUnpvgmvy0t8MU6mkiF/o0xytg6KhbiPIu8m7GIdGSiVweVy8/bbb5FOp+m4cJ700Qe4oQSIeLT/P3v/GWTXfZ75or+V1855d86NRo4EAeYkkhJFWbY0EmXJ9sieZM+5c8+9k+v4i8vjqlNnxtdz5pw7UzPlueUkW7ZlWbYCKVsSk0iIJHJsoHMOO+e40v2w0Q00uhtogrQtS3qqWMXG3r167f9e67/e8LzPg7aFKJemSLQFJPJVg4sLeVyqtKGzl8vWmJ3JYV1LsvvZAQ7twArkJ9ga+/bGSX9mP+99e5xq1SAwHOCJF/pQ75iLHRyK8PAnRnj3lQmWryVbc+q6zJ7HeznxaGv9I5EI7e3tHDx4cJ3BYpombsnmocHojoXj1vDII5D7ZIWx8Wt0dnVQr9ep1+vk83nq9TqNRgPb3vh8DtcsbqQM3lkV6AhoeI94kWwByaNTFSWSySJ+XUBYvkqtGl/vJu3fv59Tp069DxN0cd1eZv/+/ZTLZc6ePUt3dzddXV3Mz8+ze/duoEV57+rq2iDSNhz3EfaoJIr19cRlO6wUaizmajRMm15RoqvQgHITQZVohnXG6hYLuRovHGjf1nLLp8ngUrBxto1hnYpBPaqjVfJUq9UN59swLWbSFbIVAcPTxejlS2QbF/DLJvv27du2m3V7sqXrOj09PZimiSiK6wnfWlJZKpXI5XLr3blEsc7XLy6zmKuiCAImDufmcnx0/8bP2dXVRVdXF6urq1y9epWw18ue/+v/IvKHf3jXdd0OiYU8qktZT/IAfBE32YUCqWTlnp3lvyvcd6J3+fJlBEGgt7cXy7K2NEl8v8pIP4wopCtcv5SgkKkgaxKDI1H6RqIbKFSHjnZy5e05MlNZIsPhTZVXx7JJTefw9/o5/vww1ctJls8u44+6eXI4RsW0EAybwpUkydEkalglraXp9nSz78A+BEGgWDMo1g0su9VI6gm7iXjVDcpMTkCjtFxi+rvTOJZDdM/moXbLsnC73ZiSzlTJ4PKiRaHS4rzXZYd8zeZYPETY06JLOJ1d1K6maExnMdMNbNnEti3CNQOzZpL0yNQ7ZNyNEkYNkhWTsiWhCSYRF7hksJt1JEnEJUl4PSp1y2Z0uchSvsZnjnXz8YMdXL3RZCzqIXlwgPGJBbJLSRxsHE3EscoMeStUx96mEnyQo0eP8t5773Hw4MH1IMQwDMbGxvjkQ3vpW6lweibDVLJFM3BozULuaffz7N42Ai6Fc3M5vjO6grlcwZ9voNdNDJ/KD5ZLTKcrfPJw57YePvu7AkxNZRDcMlapieTbmppmZWo0QzruuIdqYp7/MTrOeB7Cqpt4A5gvYt+c6TmUFQjGHKYMma9fXKRqSzy9O46nN8Arf3mN0Ssr2HUTb8RFeDiCoLQ2Q6tuUUyUufSNSVKyQOBZFf9IBJ/PR7PZJLOURF0oIoX0bSvuokfBylTJja0yJiyjXK8QabiJd/dQr5QI10z8MzdYfOqTqJpIb8i9qZspCOB3KciSyPhqiTa/TtttNM5gl59IQEcOqOw+fH/c+B8VvPvKOOe+P4vU7iF2FzEIty7j6guxulLk+y+PMXikg/R0nlKmii+y+dps1g2KyyX2PdFPZ3frYTfw9ADedi/p0TTNahMtphHdG8WxHa5+7SqZ5Qx9h/vQ3Le+q1gsxuVLl1E1lUg8giAIeGIempUmqxdXKS+X6Xuyj2B/cNM5RHZFCA2EWFxYpGugNb+31bD+dhga7Kf6U3VSySKm2UTTwXEaLC4uIsstGpimtVQ3dVWn2yeRuDaJ0lCoOxJjowl87T7aD7QT3hXeMB/o82t87DPDTB6MsDrbZHU8jWCIiKKDu1Oie1eIT3z6YdwuFfBQLBY3mAkDFAoFCoUCw8PDm0/+bxAPnOxhfjLL5LklCotFJF1m5JEenvnoAVw3xVZyudx60ufxeIhEIgR6Aii6glE1cHvcRCIREokEuqbv2IMMWvurLMs08026d3czcmyE0dEkjZxBqM+/Y7sLALvUQA9olDI10qkqkiwRj8fRv/kKM/H9+IdiWyZ5tyPoVkgU6kwky3T4b+1t/oBGz4E45fkiofuk5/8Et/DEEwMcPd5FrWYSDGgsLcxvacfx1HO7GNodZ2oshWnYdPcH2bU7iigKXLhwgVqtxrPPPruBfrc2l/5+bFhuRyjkwef1bBh9uBceyFZ57foy08kyS7Umtm1hlPLIjk1Ud9gXlVBkjf/23/4bmtbyh3vppZeYmprC5XIRi8U2dA01TUPTtA2x7prq5tq95fV6OXnyJHNzc7z66qsb5tlGRkZYWFigUqmsF5UCLoVetcqljIAkCNsWnXOVBhfmc+iSyO6qiXUjSaNqIGgSjukg4NDeEyA3FOC7owmiXo22LY41GPMQ7PNTmy+0hFc6vRs+j1VsYIkCTqePJw8OsLKyQqVSAUEkhY+pnMVqoYZdbCDYkK+r5Fwqc9cu89LTxxnaZrbudti2gygKrKys0NXVtS4S13rNJpFIcPnyZfr6+nC5XLy92GQ5U6UnXcVaKCGGdbL9Ad4YT9Ef8eC5Q9irvb2d9vZ2pv/kTxh56y2WazUuXLhAJpN5X4zEUNTN/OXEhn+rFRuoLoVg8IdXrfO+E70f9fk8x3E48/oMP/ircQqpKkgCtu1wWpMZPBDnhS8cwn8zEeiKeXj2pYN8548vsXo9jS/swh1yAQ6VXJ1ytobY5uKjP3uYtrAH69GW31HqWorCTA7HdsiMZSgnyxCBrJ7lyENH8Pr8rBbqzGWrrBTq1JsWguDg0BIVCboUBmIeOgMtQ11BEPB3+Smvlpl9YxbFoxC4g0pkmiYLRYtX350jO5fHnamh5xvgQI8icBmb14sNDg2EGI77EBUJ99F2tMEQzeUSdr6OYzlIPpWuNg+risBUukKhZpCrNSk1mkiCQ8QjoUo2blXHqTSxLZOKIlAyTJqmhUcREZoGM7NzjAoZVhYXiCgKA5E2hg7HKO8OMD01RTaxTEBtEHeFwBQ5deoU4+PjPProo0xMTBCLteb45ufn16tHDw3q7O/0M52qUGmaiIJAV9BFV9CFKAokS3XeHE8izRQITBVwbAdBk1CSFdq8Kinb5lVV5udO9m4pVzwc9xLp9FNMVPHPteYab0/2HNvBSldxJJF8u5sjPWFUt0oym6KXCo1TM6RyNUSfijcSQNM1WLHQxssc8Otk1Qp/9tYV6vVh6pMlrr42SyCo4xkKbdgwBUFAdsmE+4M4ERfZKwkWXptCEKB3VytAFxvQyFdx3cNjR/CoFGeTmJ0m1mqRtFJn4WwSSdHwI5LZc5imqNHuUaFYbEljb7F3u1WJQrXJQqa6IdFT3Ar7D7Qx9OIIbVsUIH5cUEpWuPCDecygRvsOFP8EAdo7/CxNZahlqxx6aoBLb8xQydTwt3vQXAqWYVNMVqgVavQcbuPjn7mlTiCpEu2H22k72NaamVElkleTXPnmFURNZOThja0ox3bw+oPILg+Dw7s3vLYmZb+upvrc4JZzUKIsIir3T791u3X6+rd+aPb09DA2Nsbbb75NH3306r0YeQcr5qPmmNhNE9d0kqXRJXy9Proe60ILtq5Dx3FIpxIEw7B3fwelJyLgSHg8LhrNAn6/n2RimZ6eHiRJwu/3o+s6k5OT9PX1kcvlcBznvjzymk2T6YkstmMztCuK9j7Nu3Vd5qVfPMrkoz1kU1XCMTfDw9ENAgChUGi94l2pVFoVcdvB9tsUl4pEdkWwHXvd2DiTbqnu3k7h3A75fJ5QIEQ+kafn4R4kRaJWMVAVFUkRqdXqO0r0HMfBKjaR3SpOsUEikWFwYJAev49x2UMtFCOsKy0+WaUMd+n0BN0KqVKDTLW5LgAlKxIPPTlAZjyD0PjR9/D924DPra7P5PX39zM5Obkl9a2nN0DPbWqQlUqFH/zgB+zatWt9fOR2rHX47zfRux/0hN38wsNDLOZrLOaqGJaDKre0AroCrvXOdH9/P7/xG79BNBqlo6OD4eFh3jl7ESfYTblYQ6VERHMwmw2azeaGubR6vU6hUGiNxHR0bEgIfT4fi4uLFItF9u1rFfF7enrIZDLMzs7S29vLlStXePpgPz15mzfGU4ytFgm51VaMh0C1aZIpN2laFj5d4YAoYp5NILpl1L5b6+8YFs2ZHGFFZLHPx/WV4paJnk9XeHA4xvdWK0hjGbS5IqJXAVHAqRhYokCmy8Pwvhh7ukLoSpSmafNXV5d588o80lIeX7qBtyGgKxpuo4arJNIV38c7KybpV0/z5IG+dYsMaI3RTEykuXxhmaWJLLZl44+6iXcrBMNt+G4r0ImiSGdn5zrlVdDcXF9aIdKA5mgawaVgTuaIhHSWZYHlfI1dbVvvG66rVykODGBIEl/5yleImiY9kQiFWoRzb85g2w5HHuvj0ScHtvz9wyd7mDy/wsqNNL64h2bNpJarcugjg8Tid/f+/LvET0jq2+Dqe4u8/udXaSgSsb3RVsDvQKnUYPTMEpbj8LlfPrFOETp2oA3PPznO6XfmWbi0SnGpCALoAZ1dHxumb1Clr7118UqKRM/DPcT3xyksFEheTZJfyGP2mrjb3Ty2/zEaNrw7nWExX8PBIeBSCbvV9eDatByKdYOzs1m8msKh7sD6ALG33Ut+Js/iu4t4Yh7k29SwplNl3p4t414uEF8ugw2iTwVBQK4aHCw2mE7VuGy0ZnDWbhgpoOEKbKaM9QBRn871lSLL+RpN08KslVhJ5Ggvi1QrGTTVCwjoAZXArjDtezto9+vYTot6EOzuw+f1rIvGNJtN6vU6tQO9vP766+um4mub6eLiIhcvXmRoaIhr165RKpV4+umnmZ6eXp871HWdfe2eLeeCxldLFFIV2hZKCG4Z+bZKjLFYJLJcYT7iZj5bZXAL416/rvD07jjfqjYpAv5EBStTQ9CkVoBi2tg+lUyvj+7dMXbFfbxyZYVw1US+kUdz+5D723Asm1K5RKlUIe9UCUUk9LJJeEHEeyjKX74yiudqlv7eKN62u28igiggtHuQcw0Wvj+LL9yaUXB7PCTqdVz3aq4LYJkWds2kni+RcOpILoW6CSO1GvP+IIpDq4P8V98GTYOnnoLbaF1r8OkKy4UaDcO/Xplfq7g7jZ04mf3o4salFXLZGuGRHfoO0Er2fG1eFqZz/OzHR2jvC3L53QWS0zkKzTKiLOCPeznxwgjxbgvfFmavgigg6zLZmSzn//I8wWiQcM+tAL9hWCzn68xmKhTrJs34Hi5lYLGeoS/ips2vo8hiS8q+N0BhvsDsG7PoAR1X+O4J69JigcvnllidL6BoEkP74hw62onnPuWsx8bGsBdsxm9cxf2xKGOzi5TrIGoqjuPgUmUGOnz4izbN8SZ9H+9b3wPXCiVrogFrtNJSuYlhGAiCwJkzZ9B1nUik9R2pqsq3vvUtOjs76ezsZHV1dUPwdq9Be9O0+fMvXWTyzBKODf3HOvjcLx1738meJIns3hOHPfd+r8fjWadX+QQf5758jup4lZX8CkcOH0EQBKKxKIZhkEwkcbvdeH1332NKCyVCg6F1pohzUyQFh5a1TrmMZxtp8zU4TRunaSGoEpZtk1hdZXJ6iWyxSDbahSaLCJYF1Qp89aswPAyPPApb2GhoikSz3CBbaW6yypF1mWqmeu+F+gneFwRBoLu7m8XFRbq7u7d93+zsLBMTEzzyyCMbaH5rmJ6e5o033uDq1au89dZbnDhxYltZ/J2iYVrMZ6pUmhZeTaI37NlSJE8UhZZyZHh737WVlRUOHjzI9evX+bP/+//msX/yr7n4/UmwW59ZFgW6gm6e3TtIzx3Hqdfr63uE3++n0WhQr9eZmpoiEAhgWRaLi4ucOnWK7u7u9c5VsVjky1/+Mo899hiLi4sEdZ2ne2QWSyJT2SbJgoEkiWiyxENDYfIVgxsrBZTpIobjIN3RURIUCTnmwVgsEejycnWpwMmByJa2SycHwjRMm3fdCqWFAnqmjmhZ1GMunA4vu/bFePFg57q2wampNOems/RnHIRVEGQXht+m0KxTLZWxr5tEgn6qe8Is9ASZzVRZXb3I8PAwoqzx9T+7yuR7iy07DZ+GKAkklkssXLCZu1bm4184xPDAredTW1sbiUQCRVGwLLvlpVitg8MtqqnlUKvWuDo6Smlpo0CQqqq4XC7kt97CHBzk2uuvM3HjBi9MT5P901d57cFfxo51IIjwxleu4gtoHNpivGRoV5SP/cMjvPOdKbLLRRRd5viLu/nIi3/D3P0PiA+c6L355pu8/PLLzM3NAdDX18eLL774vn1OfphgmzZn35yhbjt03N4RE1r0H2EgyMyVFNPXU+w6dKtKsXsgzEh/iJWP7iKbrSEAkaib9pudv9HRUXbv3r3e0tf8GoGeAKPfHWWpvsTIAyN0dHZQbVq8N51huVCjzadvnt/hltS041bJVZucncth2c76cLq/x092MkvicoKuEy0KlWnZnJrKYE4WCaRtpNgt01BoJXPRmBtnPAMzBS5oIlGvSugu4g/QGvrN15r4NJkBn4u56zc4dHEeX1YBWUT7+DOosohaMVEXK/i6gqiR1uZv2Q6jy0V237ZXqqqKqqrkcjk+9alPIQgCZ8+e5dq1axtu3uXlZTweDw8//DDpdJrDhw9j2zaNRoNisUi9Xt9gW7CG0zNV9IqIUzWQejYqWEkRN3a+gVWsky43GdxGnGnNQPU1l0JquYSSraPXTWxBoOZVIO5moDPACwfaubpcpFIzCMwWMBvWuu+eIEsEbm7y9Xqrq5oVqzirVVQamAUTUVAo2WXMvEUwENxWAEYSBBAElE4v1myBlfEsu090Uqhm8IT9lBJ5/O3bq9A5FQPDI2AbJrqigS7TtE3cDQPNNBAVFWlhAWarUKlAKg1/+qfw/PMwsLH6JUsCTdPGsBy0O/Jsx7p/W4u/77CaFjfOLOG45B2p9N4On0djxShy48Iqz//8YY4/1MPiQoFquYmsSnT3BHC5FAzDYH5+nr6+vk3HSK+kufLyFYKBjUneQrbK5aUCxVoTRZLwaDKa14PlOCzlayzkqoS9Ksd6Q+sBtb/HT3a8paY69PzQtmqgF84s8r2vXKWcqqC4W93HqXcXuXZgkU9/8SjhyL0tN+7Ec489R9tCG1PidS5/9WXEep3OkSLiww+DA+WGyfVkGbndjzSTIz+XJ7r7Vhd5bQ8RBAFd12k0Ghw4cGD99b6+PiqVCslkklgsRiKR4IUXXqBQKACtWbh6vU6lUiGbzW7Yk27//7WkcnaqwPi78/jb/UiSwMy5ZcYf7uHg3wKFudk0mVmtkFdc1MYySCGdSrVCodj6LH6/n3hba34vkUgQDoVRVIVqucn8bJ5qpQGCRVhXiMZd9Dzcs+536HK3qv6WZa8nu/fyNlzTgbdNG1mRcHtkBMlPZHaWhXAEJZWEpRnIF6Beh8tXYHISfvGX2MqrRxBaRc9N/y4KWMZPOnp3otk0uXh2GcuyOXy88yZN+f3B5XKhqir5fH7D/Ca0GEOnT5/G5XLx7LPPbkvZq1QqvPnmm8zPz+P3+zly5Mh9fJpbWMhWeeXULKvXU1BsQkCja3+Mjz/Sv0mddSd4+umnefrpp8nn87z5H/87r/5v/x+C5RzRR44jdnTQMC0WczW+eXmZl473bCg0iKK4Pmvmdrtxu91UKhWGh4c3dLUcx2FiYoJyuUxXVxeJRIKPfOQjjI+PEwwGURQFtV7HJ9XpVEzy5TqGYaDJAq5CmosLTVQ1gpWuIvq3/h7XxjLcdYtC06TUMLZM9GRJ5OndMYbjXq6vFJlJVzAtmz6fxoGuAEMx73qSl682ubiQJ5CoIk7nkdo8iLqMAriBsteFJIgUEwXs03nS1RgzgQFeevAgN8bG+d6358lcKRPu8uG+gxFiNSySU1m+8fvneemfPkj3zThrzYKqr6+PiYkJekJurleadI6EMZdKqL0BCmGdTr/Gsw/3bxLVazab1Go17IkJss89R+j11/lnmQyDi4tMysMYV8bp7F2AFz7G8rUkK4vFLRM9gENHOtl/oJ1srobbrdx3wfJvE/ed6DWbTT7/+c/zl3/5lziOs37D5/N5fuu3fotPfepT/PEf//FdldZ+WLE6X2B1Lo+vfesKk9erUTALTI5uTPSg9XDvjHjo3CKAGRkZYXx8nL17WzKstm1z6lunmLw4yfGPHcfj8WBZDhfmciwXanQEXOtiIttBEFom4PmqwaXFPG5VpiOoI8oirrCL1PUU8YNxFJfCfLbK0mqZYLqO5PdtSPLWjyeLxHaFUeYLXCoaXFosMBjzEvGouFRpXQUKWl2AXM1gtVCjWDV5oC+M1y6iJQIMrpTQmiaiZlLPLePePQJhMFNV6jfSyF0+RFUi6tUYT5bo6nQI33a/VKtVJElaF6F55JFHGBkZ4a233iKZTJJMJvF6vWiaxvnz52lra+P06dOcOHHinkPT1+pzzCYTd32PZbVMOL211W3fIwKPxmDV42I6L1OzWvz83SEPh3rDDMV8NC2bq0t5gnULM1lB2sYzShAFvD4vXp8XJ2Cxem0FKVMlH9OIKW4EQWBleZlgKIjLvfkYuiIiSwKmDbJHpTCWpnG4HUcXcfWHqF1YwIqaSFvQZOyaCaKA1hdEzzbRgwECARXd6yY7NoubJubxByjLfgjoMDHZSu4eeKBF4bzzeE7ruhTvfMjfZiXy4wijZlDI11C26LjdC6IIgiZRTFWAVnenr39z4q4oCn6/n3Q6va4yBjAzPkNprISQFvCO3NrX5jJVzs1mcYB2v5s7m1NeTcayHVKlBu9MZXhoMELMp637lqXH0kT3RLekcKZSFV796lUaleYGVUajbjJ/KcF3vn6Dn/1HD7zvtcjN5DArJnV/EMuwiJVyMFqDkydBFPHqrXOeyVWJuXXS19NERiJbBp3bqel5PB46Ozs5f/487e3taJpGW1sbpVKJpaUl+vr6diwgUS2riPIUOC3avePYpFMpFhe3TkTulKpfO29ZlteLYKqqoijKXWdfTNPmz//gIuPvLeIYFvZqGWW8SVukg+490dbsd7FIsVhc/8zlSpnEZImJiznKqSrYDna5gTvm5fn/Vw++zlufeWA4gq/NQ73YpKbU8Hi9FAtF/IHNe8KtD3NzTfI1PHEPBw/1EgwexXr7bfTd+ykODIBHhdk5SCVh9wjsP7BlkgetLWW7R+ROrEh+3PDqK+Oc/tYY2LA4k+Oz/3AzpXIniMfjzMzM4PHcYs1ks1nOnDnDkSNHaGvbbJtxO3bv3k1XVxdzc3M8+eST72vO7k4UagZff32KzJvzBB2QXTLmbJGlpRLfMGx+4WO7N81s7RSa28ciAbzFUaK5DPX//nWsZ15A6wsw0B9gIlHi2lKBJ3ffEm1am9G7Haurq5s889b856ampjh16hRHjx5FURT279/P4uLiPWnii2cXmFoq7PizFIslrly5QkjfuhCjKAqqqjLiVjkw3FJJXrPnup2JP5Esk8/XaFspg09dV/Fe//yCiKwoRAY7MOYLuJoKl2ZWaXPy1HIGqRslFLeNJW8uwkuaRNtIhOXRFO+8Nctnf/bwhvVyHIeOjg5KToZUwMUC4Or00qRFb39sOLqlcrqqqlAooKyuIv7yL5MSRYRSibbf/m1WSj4Ez36Kw12I2RoCAj7/3Z/RkiwS24Ev8A8L7jvR+/Vf/3X+4i/+gn/zb/4N//pf/+v1GzuZTPJbv/Vb/OZv/ib/4T/8B37jN37jQzvZvy3U6wa2YSNr21cmBVmk+T7NcWVZpqOjg4WFBUKhEK9+91WcaYejJ46u0xuSpQaL+Rpxn37PJO92BN0Kq4U6k6kS7YFWIOaKuMhN5ijMFYjuiTKXqWImKyh1C7Fn+wtZUCS8msJ+SaIR0Gn3aySKDep5i/UQxGl52AXdCicHIiiiyK42L7LkJ9TQKO2pUMvM4M6mcZ0/33pgA1JYx1wuY2VriO1eXKpEsWhQv2MpFxcXGRnZ2A6PRqN84hOf4JVXXllXZlpDIpFAEATS6TQnTpy4q3xuT8jNdbdMwK1gFxrrlAfHcbCyNZyYCzXk5viBvi2pm7fDNE32Nps82mxSqzcwjRYFrFlYYTK3xGrJYGapSHy1Qj2bR3QZULljvUWRaqXlaSaJEpIk0sg38TYc6kEfkqZjNmqIksjK6iqKotDR0bFhtkGTJHRFom5YeMI6leUSmcUC7oiAOhTCl6xQmEgSGm5DuE3owCo1sTI19L0RBg7H8a2GsAsJ3F4vDdmkfm0S1aPREw8xmjaxbRC/8Pm7rkmpbtIe0HDdOaclsCPz7B9VOJaDZTmIyv1lu4IkYjatbY1j1xAKhVhYWCCbyXL5B5dRyypiTiRxJkGz1qSaruKJe7CDGhdzFQRRIHKX70USBdr9OolinXNzOZ7aHUNXpBYd0oHMeGbLRO/axWVKyQodezdK7yu6jL/Dx9yVBInVEm3t709xr7xaxlRE0qKOf98IzIiQSML166y5J7esa2rUQiLVdBWzbqLcFC1ZW7tGo4HjOOj65rmVYrFILpfj4YcfxjRNZmdnCQQChMNhXC4XU1NT9Pb2blL+3QqDw1F2He9i4swiODD0YDePPL7vfVM3DcOg2WzSbDapVCoYhrGhO3knxm9kufLWFN6YC92rY3W7SL63zNjVFSTDwNfuwxv14r9ZrKnValTKVa6fT5JdKBAKedAEBaHDS9F2eOf0EvufHFg/b59PY8/xLt77+g3UoIiLVrenVqttK8AjyCKOKGLWDPp2RwlHgq0u3/QUsWd/hmTTAg/Q39f67y4wrZb8/FaBnVk3t1WG/XHG6kIBHAdBlVmdzX+gY/X39zM1NcXw8DDXrl0jnU7z9NNP7+ieUFWVo0ePMj8/z8MPP/yBzmMiUSR1aZWYIKD2ta5l2XEIzxdZvbzK5LEuDvcE7+vYS/ka6V17GWhWaLw1Rv1KAmF/GStTw+vX8LsUrq8WeWIktn4PrsUmaz8vLy/T0bF19358fByPx8OnP/1pVldXeffdd9m3bx/d3d1ks1mmp6cZHBzc8nejXo1RQIq6MRaKSFsUEO1KE0FXqGoSHaEQjxwf3LKj5zjOhv1lbXymWCyu/7yWvL4+XaU0XUKZzUK7C+E2bz1RFDENA1lW0F06ggZysozc3Uago4/ViTnMShNPn5tyuUyhUCAajaHf5mkqyiL+qJvp86uknttF7KbwWDweJ5VKEY/HCatp/sHRDsaSFVYKdQK6wp4O/11tJIqvv05AVfGfOMFDa7HTc89x1LSZ/5NLjJ9ZBtth92O9PHCyZ9vj/H3EfSd6X/7yl/niF7/If/pP/2nDv8fjcf7jf/yPJBIJvvSlL/29TPQCQReqW6FeaqJv4bVj24Bh47s5m2JbNqXlEsXFIkbFQBAF9JBOsC+4Pr9i2za5XI5IJMLVq1d57bXXeOzoY2SWMrhjtzo089kKjuO8b2oX3FQiK7ZmFiJeDVESEWSB0mqJ6J5oS5ikaSEI4j09jwStZR/g82p87ngvpYZBttKk0rAAB02WCHlUIh6V1UKd0ZXi+samKir68DDKR25W628vUIutqsyasZwoCDgOIIjrG8l2VfZKpcLs7Cyf+MQnaDQavPvuuxvUXh3HoVKp8O1vf7vlcfX881sGQCPtPk7HPZR7m3gm81jlEqIqYtdMRL9GusNDf8RzVw7/GmRZRpZl3G43wS1e11Jlgsk5olWVZshGCW+udjt2y5tMFiVsx6bRNGnUGtimRa1Wp1y28Wkt41hRkiiVSly6cBFRkmhri6O7XIiiiEuwydYNVJcCpkW93CDWG6FmGHge6sJ4z6Iyn0NTVBDBMW1Ej4q+P4brYAxBkWjr6aTUbmCmq6TtKsFiDi2gM9QRYbGSJV9rEr5LUmBYNrZt0xf2bLjGzLqJrMnod8wRGDWDZqmJ7JLR7qPT9fcJoiyiyBJl8/4oZbZlo9wUXboXzIbJ//m//Z/46j4ee/gxbNHGdmz8XX4cxyE/n2flcp2K5NB5aAcUQgFiPp2VQpXlfG29AOKOuSnMFWgUG2h3VEGzySqiJG6Qol6DJ6iTnMqSTlXed6Ln2A4OYCsq4sEDcOzwpm6xKHLTZLvl++TYm+l9S0tLWwZRqVQKy7LW6a+yLDMwMEA+n2d6epru7m6Gh4eZn58nEAgQCNzdO0+WRT7zxaNMPdyD48DgrvCOzNjvhKIoKIqy5czTVhi/WkWVFKIdt7olhf4KzaCHyKEImfEMydkk0BLtEQSBbLpKdaaI1y3TtBqUfA3UsITm8bA0keDdH1xnz774eldx7+E4V0/NU1gq4/f7UVSFeqneErjaimopiZTLTXS3St/gzTnVRAKxUqF7dz8TK3XqhrVOE7sbCnWDsEvdUmDCsR1ckR9OqfO/Sxx4sIvUXB7LtMkZ03z/+xJ79uwhFovtaF+5HYIgEIlE+NM//VOOHj36vsZ1ssU6HT0P8g9eGqBcgw8ynpfM1hALzQ0zaoIgIAc0yNTJFu/tcbwdbMfBicaRXnqJ5vAqjKaRgnqrIGxayKqCZbdqFWvLJwgCtm0jCALNZrNlI3EHC8e2ba5evUpvb+86G669vZ14PM7o6CiO47Bv3z7cbjc3btxgeHh4k2jNSJuX07MZzE4vLJWw8vUNa+A0LcxUFW0kQkIReKYrsGWSt3bOa/f0vXC5PgP5FO6Qg9p2WzzjgG1bVKs1LMtERMCUHBqFGuVsgdHxCcbOLSBprXWVZRnDNJmYmMA0DQLBIF6vF1mSEBTILdW4cmWaQwfa18+tWm3N3fb19TE9Pc2TI0PbnOVmNE+dorhrF5E71lGWRT71hcMknhnCsR3aOnxI75MN0GyaXDy3zPT1FLIiMnKgjQOH2//OjdLXcN+J3srKCidPntz29ZMnT/Inf/In93v4v1OEO7z0741x5b0FfAF905eeS5dxezX2HW4nP5tn+ewyufkCmWId86ZJtksQiMY9hIfDdJ3oYnx2nD/5kz/hoYceQtM0Dh06hF/1s1pfXRcKKNYMlgt1/K77o7vqikSm3GQxVyNykzOuelRKyyUcx0GRREwRxB3w5xzLxpFa6p6qLBJXdeK+rSukqiwiiwKGZSOJEqJPQ3DJ2NWWGentf84uNxHdSksABmhaNrIkoCktuoNhGDQajU2D2el0mnw+z/6bFXu3280zzzzDyMgIb7/99jr9CFqeLKOjo4yNjfELv/AL60p0a4j7dJ7aHec7pkVGlQjkGzgNi2a7REGTiMoSxxDJXk8jqRJaQMMVciFts0neDYokIooCjiLibGO7J4giqqKgqMq6ebMsKlguGZdLJxT04bvtmojFYjA4SK1aZWl5mUajgcvlxqOouFWJatNCsG3KpTL5vEQumyUYDOIc9JKdquB3BAQLBJeKGNGp+W0KqQSiJCFLEo7PoTlRJm2VeWp5Gp55EK9bY0+7j3NzOYo1Y8tr1DBtEqU6fWEPnXdU06uZKu6IG1fYRaPRQFVVigtFZt+cpZ6vo7gVeh7uIbZvm6HIHwEoHoW2Lh+J0QTEvO+LxmpaDmLNom1w+znLNRgNg3f+5B08RQ81tYba42PmeoblXA1ZFvBqMl6/Rq7cQMs3qI9n0PdEEO5xfYsiqLLEXKZKf8SDKAqoXpVqsko9X9+U6GkuGXubmUyzaSLKIpr+/h9BWkBDNh08ukS5fjMpuGMta00LVRJRrZaJtnxH9yyVSrXuozuwuLiIx+PZ8rVgMEgwGGRxcRFRFOnt7SWZTLK6urph9mYryLLI7n2bPfn+JhEIuRBEgWbdQNWVlhpw1aT9oTiHfuoQjWKD8mqZWq5GLVPDtmzklRJj81WCw2G0iBtBFqnVahTyeaymRaNuous6kiS19h23xf7H45z6eoGZKwtE+4IgtBgWXu/Ga9yxHXILJWQPdIW8CGKTRh3k6zcgGiXcEaGjkmE2W6HzNgXErdAwLBpNi4OdgQ2G6tAqKomKiHpzbqleqLN6aZVmqbkuJvOjYP10P3jwoV56+0PYtsPVa+/xne98h1OnTvFzP/dzO1KTXSuciKLI8vIyV65c4YEHHthAE78XpubyfP0PL1KcyePgMHE6x0deOsADOyk4bQG3LmPLAs4dKqtO08JRRdS7MLPuhTW1y2LdxNMXwExUsIoN1P4gcsRNMVvhQFdw22t1YWFhUzGp0Whw7do19u3bt4lNIIoiBw4coFQqcfbsWXp6eti9ezdTU1PE4/H17jtAV9DFrriXS/U83QdjmNczNOcKCKrU8sMD1IEQ2V4fIbfKno67UKrfB1RJxBKFFhX99oKOAKIkoWkqpinicruxbBmCGrVohN27ukn4yzhSk0DstnPpbe0NiUSCaqWC2+1GEMRWE8AWqFar5PN5ms0ms7OzZLNZoGV1s1ZsuxO1Glz4QQqzZnP8qV727GuD06epHjhAyLY3JWCiKNLRufP1qVarGIZBIBDAtm2++ZUrXH1z7uYMMlx/Z4HMT+/l6Y/enzH7h437TvS6u7t54403+JVf+ZUtX3/zzTfvqsr0wwxBEHj0o7tYncuzciODv9OL269jGib5ZBWhYmAEklw6dRp5SSFfE1gWHXK23doIAVUSiKdKdCUqVNIVvnbxa8wtzCGKIr/2a78GwHd+/zvcePcGzw8+j6IolBsm9abVUtfcIfLLJbJzeeymhSvsQun2kS431l9X3ApGxcCoGsR9GqZLxtEk7JqBuE1C6dgOTt2iEtAYDt17TjDm04j7NNLlBt0hN1JQQ+n20xjPIHd4EW8GWXbNxMrW0ffF1mkGmXKDnpCLkFvANAzm5uY2cdkXFhYQRXFL76ru7m4++9nPcuHCBS5evLjeFfR4PBiGwW/+5m/yUz/1Uzz00EMbHu7HekP4dZkLsTyzCwWaS0WUpTJDokhHrkkpUaeMgIODpEroQZ3o3ijhwfA9VQZvh1+X8aoydV1GutlF28qQVBAEbMdB5GanTRExHBtFErft7rrcboaHhykVi5RLZRRJwCuZLFcMdNvCG/TR3t6OIstEbj6I2zs7yGazRLcIZG3LYmkihaSrFERwLyXYlZnnTPA5FuYXUHDoUA2mcw1W0g4uWUCRRGzHoWq0AoAOv0av26RcLLS6nYqMJEg0ig16H+lFlEXeeuMtFqYX6C33YhsaKVUglLOwvz+HO+rGE//7w31/PxAlkf0PdTN2JUGlYeJ5H0lOPlclGNTYvb+N/Fweq2Eh6zLeDi+SIrGwsEA0GsWyLG68eYNetZcDnz/Am5dH+e7VFeqzRaS6iVBpkizWsR2oNk16Yh6sTJXmvII2vHUS6VgO6bk8lVQFC6jG3JT6QgRcCqLUoik1So1Nvze0J8b5706tm3zfjtxikdhAiP6B9z+bE+oPsXp+lT7FxflynoYpod0mAGLbDplKg/6IG61hEd0bRbztHrIsi2q1uiGZcxyHmZkZOjo67un7193dTb1eZ3p6mmg0iiiKzMzM0N/f/0OVQOw71M6F/XHmL62i6Apm00QP6Rx/vB9oiYHdmZy35Wucm0xTNW30m2vmcrloFCycUJV4u3tdRRAgEAjw4k8/QCQS4a+/fJ7iXBVX0IUv6EPVFBRVxWxa5FfL1At1gu0B9n6kjZXXZ8gsVtACTZZPz7Eaf5zGH5zFFkUMl8h4RiUW1dHWvrfbZgYqTYdy02Qo6qHNBY16A1mWkW5eA5VkBX+Xn0uTl8ieztJd7iYxU6YKdI1nkBSJ8PD9z4T9fcdaB90feIQLFy7Q29u74xm5t99+m0QisZ4UPvfcc+vXv8fjIZvN3nU+z7Idvvf1UUqTWdp3RRBEgdRUljf+4jqd7V4uvbNAJlHBH3bx6NMDOxJrGmz384OhEJUrKbyKiOhWsMpNyqUG7sPt9xy/uBtiPo2RNi9nZ3MMxb14n+7DaViIPpVc3USWRA5skSA4jkMmk6G9vX3DnlAoFJidneXIkSN37fb4fD5OnjzJ7OwsZ8+e5cCBA2SzWarV6npRSRAEnt3bTrVhMSmWCTzYji/fhIqBoEo0QhormojXrfD8/vYtO9/3g96wmxtumYBXwS41kO4o6griLXaWXWjgdPvQvCrtgZbtWC61WQ1XEAXaO9oxDINCoYBdc7Alh3qjSLncsqDw+XzrTK+1LujU1BSDg4Mb1thxHL725YssXyyA4PCD6gwdXW5iN24w9cwzpC9d2jQHvRVun4e+87+5uTm+9rWv8eCDD9Lfe5gb7y7ijbnX/W0zCwXOvTrFkRPdhHZgo/Q3jftO9L74xS/ya7/2awSDQf7lv/yXDA8PIwgCExMT/Jf/8l/4sz/7M37913/9wzzXv1V0DoT41D95gLdfGWduLE1mtYIoQqzdy+EXR0inRN75k3epSD60PXvwuDSiXm29ulhrWixWGxQUmem/OsvMjRn6nuqjs7OTYrFIJpPh8sXLNEoNMqkM0XAUw7zphr7DWCG/UmL1wiqOYSEoIoV0FancwHPkVmVMkAQcq6VyNhz34o1rNLIWWrKK0OPfkl5jZWo4fgUh7mH/DqpAiiRyuDvINy8vt5IVQcB1KI5j2RjzJcybVDVBldB2hdH3t5IOy3ZoGDaHuoMoQoXVlRVGRkY23LQTExNEIpG7PogkSeL48ePs2rWLt99+m6WlpdZ5KQp9fX28/PLLXLx4kZdeemldMh1gKOrFn67RmaxTzhl4u4ME4x4kTdpwDmbDpJ6rM/v6LImLLRXT2L7YhuBxOwTdKrvbfZwpN2gLu7GyNeQtEhlRENfpZVa2hqvbx2qiQocq3tM82Of34/X6yGYz+FUBVBermkPJrjAzM4NpmkiSRDAUQpQkVFWlXquh3xnQCgKiIFOSQRuO0n7mFEuRXhS9i1pZJxL38Hivm33lZkuNMVvFtFoUlT6PSl/ETcyjgGNjmAamadIoNyjMFxDdIolmgsxohqmpKZbHl7lx9gaBY0fIqSH6w24ClVb1/Uc10QMY3N9GW5ef+eUirv7wJvGTrWCYNs1khZH+MAtvz7E0m6dhWOiqRPdgiJ4T3Zw6d4pKpcILz7+AmlchAFnToazHQYCIS8V0NZFvFnfS5QbFuknJbeHzqZipCkqXd8viz+pkhvz1NAitpK+8Wma2O8DhA7e6WLaxuV29a3eU3Se7uPb9WerFBt6IC9OwKa6W0bwqj3x0F/J9UNR9XT4CvQGMqSyDEQ8zmSqiKKArEqZlUzcs2nw6Q5KMy69umh9cXl7ewEZpNpvMz88zMDCwYwNxXdcZHBwknU5TLpfp7OxkamqKnp6edQGp7WAZFsWFIo1SA9u0W9YXmoyv07eJ2vxB4HIpvPSPH+A737pEMWMRinho71fYNbJ99yUYdHH4iX7e+cYYyekcroBGo9jAqBkce2EX+w8MsrS0tO7Ft1ZV7+wVefin2zGqfsbOLlNcrFCr1tF1DUEUCXR6Of7cEAePdVKpNElfSzJzIUmublBbkZGDfqSGgG1bSKkGolQjUzRx9wZxuWQkQcC0W9+tV1fY0+Nmd8yDY1sYhkG1VsW2bGzTprhSRN+js5JYYfb6LJfPXUYYPoDj8yNna/SmKz/Wid4aPB4Pn/vc5+jr62N8fJxIJEIkEsGyHW5MpJkaS2MYFl09AQ4eakcWbN58801mZmb4whe+wEc+8pH1Y/X09PD7v//7NJtN/vk//+fb/s18pUl2vog/7kG82WkLd/tJrpb4yv84S2Y6i6zJmHWTpekcP/crJ7YVxrAsC1EU6Qm5ePipAV4rVaku11GzNZqKgDQS4cmn+ukIfLBA+8mROKW6yUSyvF54raQq6KrI48NRhrfwT7Ntm3q9vqHbtLy8TLlc5vDhw5vevx36+/vp6uri6tWruN1uYrHYhuQm4FL4maNdnJnNcXUpz7Iq4dDycHYpMvuiHh7sD69bb30YGGn38W7IjdHhRRrPIXrUDcXrtXEcq9REEAXyYZ3+iJvuoIsDxzt59WoC27C39FpVFIVoNMri1RU87RoPndyDz+thcXGR6elpBEHAsqx1X8be3t5NKtOCICA4IIsikiqiqToRw8CfzzPycz+HZ9++e37GNXbZ7TOLlUqFXC5Hs9nk4sWL5HI5vv71rxP2TyJV2ond9pwJtHlJz+RJrpT+fid6v/qrv8rU1BS//du/zf/8n//zlrzyza7WF7/4RX71V3/1QzvRvwt0D0X43P/jIVbn8+RzNRRZomcwhOZRufZKkzP2DRpd7cTdGoE7LAhcqoRLcbNarCP0dPHzw/+Q4z9/jGBfEGjJWh8+cphz185x+a9vEArEcVwyuGW2HPbaAoXFInbDRL/psWbVTeqJKkblNmWTm7MrgtgaWh8OKUwOhXBPFnHmi0gRHdGrtjpKdbOV5CkCqZ4Au/uCO94gdrX5iHo1lnI1esJuRJeC56FuzKEqdqEOCEghHSniWk8uF7JV2gM6QzEv+XSFQqGwntCZpsnY2BiDg4P3rLCvIRAI8OKLLzI5Ock777xDrVZDEAR6e3tJp9P85//8n3nxxRd58MEHERGZPzXP6oVVNLdC5HDbttV4WZPxtnvxtHmoJCtMfWeK8mqZvif6NngUboe9HX4uLuSxBwI45xObuPTQ+n5s28bK13EMG8+JTji1gFbd2TyXIApEoi1frPzMHIH+AO62OF6fTkATmJqcJJfPI0kSoVCIYrG4KdGzHGiEdfyqRLvLZKEoclo+AmczLMhFFLdCvC/I0Yd6ONQdYF+HH8OyEYUWfff29dNo3Q/1Qh05IrPr47sID7W+24WFBdoCbQS7glRRmWwYdKoyoiSuC2b8qEL1qDz24giv/MElEksF2roCd032DNMmMZVFKhWorti8la9S0GUcUYCaxdyFFcKXpxmr3cDd7Wbq/BRyQsbf5+fSXB7DsmkP6DQkNszKuhQZURDIV028IR2nXMPK1BC7N66/ZVgU5wqImoTi17EdB3O1zOJEdkOit1XBSJJEfvrzh7GEGqsTdYqJCqIs0rUvxslnBu/bXkCURPqe6MOoGQwslYi0+VlpGhRrJm5dYX/UR6Bu4lYk+h7v29CBLxQKeL3e9edVqVQik8lsyRbYCaLRKOFwmIWFBfx+P4lEAr/fv0l2Hlr3Qn4mT/JakkqyAjY4a1+K0+qwhXeFCQ+H8XdtXYR7v/D7dU481rHegVlcXLzn73zkYyO4PCrvfu8GVt3CF/dw8OFeHnt6EFkW6e/vp1QqMTMzQzweXz+2IAjUajWG9gyxtFhFlXVKxRI9fe0M7Y5ukPK/sTfOubfmkTSZcDWBeKAPblbDHQeqhTrllQout4p/OITtgCYrdARddAZ0vOv77sbrNTeVo/9YPyOPjZB9J0s8HMfT42F+rkxJEvCrMpr3R3sW+P1gLUAeGRlhbm6OUqXKe+/kGHtrriXkIYhckQXO744i+mcZGxtj3759LC8vbzjO6dOnKRQKlMtlqtXqppm0Nbg0Cdkl08zXWYssGhUDs26SmsrSNtSKr8ymRWIsxdj1BMdPbk0ptSwLWW7NLB/rdmN/pINC00MmWyEW83BgIMzQB+jmrSHgUvj00W4mkiXGEiXqTYt4V4A97T56w+4t44alpaUNJvGTk5Pour5JYO5uaJo2+VoTx4HhvQdoVoqMj4/T19e3HhupqopPV3hmT5wTA2HmMhUapo0kCMR8Gh0B/UNnGcR9Ons6fLxXqNFRMzEWSkgBDTGgtfYsw8FMVLB1B2M4hB13caSnRW89eKSDc73TpCYzxEeiCFvMb1dSFSRN5ZlPHCGVTLEwP8++fXtRVRXbtjlz5gyXLl0CIBKJIMsylUplw+zyIx8ZppirUy02eOzjIzTffoWa34/npuL9vSCK4rpP6lawLAtd13niiSdIrlr8+f/3PRqVJtpN/YJqoYHilvF/iIW7D4L7TvQkSeL3fu/3+Ff/6l/xyiuvbPDR+/jHP86hQ4c+tJP8u4QgCnT0h+i4Tcq8UWrQWG7QcWg/sqwimA2aTWHzIKvQUkZKletUDIHsZHY90ZNlmUOHDpF8ucBssULaqiLXTGy/ihFzo9zNi+gmbMtGuH1+UGrRljRxYzdKUqX1GZW9UZlgW5wzOHiXRLz5JkKu0RIuUETqIZ1ih5uBvXFeONDRMsjeAQIuhef2tfGtSyssZKt035wPUdo80LYxWXQch/lsFZci8fy+NjyazLWlpXVlqmq1yszMDHv27Nlxhf12DA8P09vby+nTpxkdHQVaAZnL5eIv/uIvuHH9Bntce1g6s4QSVdjVt2tHm6EgCHjbvGh+jdVLqziOw8AzA0j36Lj1ht0c6g5wumnScSiKcz2DMV9EDGq3LC6qFkapiORzIx6KUQhq9B9px7qwffVrK9gVC38oxPGPDLBQTbKaD5BTXGj+MH1dbdiWRT6Xo1AosJpI0NPTg9vjI101KNcNBqNedrkUXvnaRQRRAp9MOOBGDLloVJosXE9SLTV5/KPDeLwq8l2+n3q+TjVVpeeRHkK3zZZ99KMfBWD10ioLpxYIOQKiJNJ+pH2DfPuPKkaOd9GoGrz2tVGWJ9J42r0EfNqGOQ/DsilkazRSFbrbfKhKhR9MzVCN+Nnb30kkGMIwbZKlOpnFCg/uO8HD/+ghqtNV5ifmqVgOyVKdwM3EWdTkdQEkAEkETRapmyZ1w0ZTRKxCA+UOtr3jALazvs+YtoMoi4g3j+U4DrZh49gOzXITURE3zMPlcml+5qUHcJBIJyvIkkh7p+8DD6m7o252vbCLxfcW0WfzBC0BQWvtv6Jh4+sN0Hm8c0M3z3EcCoXCemKSTqcxDIP+/v4PdC6iKNLX10e5XKZcLlMqlahWqxsEpXIzOWbfnKWaqrb8U/sCG/YNx3Fo5BusXFghcSVBx+EOuh/uvq+54A8KSRZ5+PFe+oZUgsEobre6qfPq8/nw+Xwkk0lSqRTd3d309PQwPz9Pb28vw7tMEokEy8t5dE8FSbzFpJidyXLl7BJqfxBtKYtgOXBb50MQWmI9kixSXygyvL+N/qFbHTiradEoNRAEAUmT1texFSBKdJ/sRtZknnrqKQCKS0U8p+YxKgbhoTCR3bfO5Se4hb6+Pr758kXOv3yDjs4Qrv4ggiBgVg0SVxJEjnbyX//rf8Xv9296Xq7ZH/35V7/Fl/5/b3P0wT2ceHhzguZWZQ480cd7XxvFHMsgSgI1wyK2J0p+Iot883oXZRHHAWMLpsAa1pgqADPT0zzzwOG/Meq0S5U41B3kUHfwnu8tFAq43W5kWca2ba5du0ZXV9eO6bHFusHocpGLE2nyq2Vs20b36+zdHeHAyEEKyQWq1SpTU1N0dnaudw29msz+zrsLQ31YeHp3nHLDZBSBkEdBXK1gLrW0IARJxPYpVEYi1OMentoVZ99NZljIr/PRnz3Iy1+6xMpoEn+bF0/YBaJAs9ykmKhg4jB0ooulqQzvfjONIMHZziQnHu/m4ME9DAwM4Pf70XWddDrNysoK58+fZ9++ffT09KCqKu0dPn7x/3lLzXX5v75Dac8eXB/S9XHo0KH1HCcQMOnaF2PuwgqukI5jOzRKTQ48PfC+5v7+JrHjRO/YsWP87//7/87HPvYxAP7gD/6AJ554YsMH/nFBLVujlK1S1l34ZBGv7qJcKmNZ1qbukywJ4EBRhuJCEduy1/19BEmgWm5i+VxEgi4ymSpy3aJQbBDdgeKjO+qhtlLBLDcQFAkjV0PwqQz1BtffY1QMgn3B9YBBdGxeONDBwthVpIeGSCUqUGrg2AK4ZALtXh7rCnByMLIeJO4Ue9r9OA58Z3SV8USJgFsl6lWRbwZ1hmWTLjcoVA3a/Dof3d/izxeLRTweD7Ztk81myWQy66Ir9wPbdlAUhccee2xdrCWdTuPxeBgYGGDs1BjXblwDP3T0dlCoFjh58uSOHxKKSyHYHyR5NYkn7qHj6N07E5Io8OzeNpqmzWUxj/9YG95MHWuxhJmoAg6C5SD2+6n3RSi4ZQ53Bzh2oo9vVM6TGE/TNhK9Z7LXKDVYHksTGArR2xXn4wcOcurCKGemk2QsnbGVAqIoospu1Iibci7PZKpCczFDWBM41h/hmUNtfOOPrlBaLLC3Oc/yoYcRFRU7U0MPaKidfrILeSbH0hx+YGu/QtuyKS4WcWyHnkd66Hywc8u1bT/cjifmoVFqoLgU/N0fThfjhx2CIHDwiX58QZ2zr88wN5FldakEuoggtii8Yt0iENA4/Egf+/bFufJdmXquTEgTCd30KVNkkZhPJ9cZR7R05IaMVbcQ5NsM628G6MLNe3lN9ltTJNyaTLbcks0WRAFni6BKViX0iIvKfAEEaNQMXAJ09AaopqrkZnPkpnKIssjSe0uIikigL8BCZYFopNVhXtsTe/s+XGsNV9jF8MeGqaarlJZLWE0LURJxR934unybfNQWFhbWi0lLS0u4XK5tZc/vB16vF6/Xy8rKCsVikampqZZa53Se6e9NY1s2kd1b+/kJQkupWQ/pNMtNFk8vYjZM+p/uv2ch6W7I5XKbxKh2gkQiQVdXx7aFtlKpQbnUoK29RQNdWlra8F5Zlunq6qKrq4vJyUny+TyNRmuO8+wPUtQLdToPxmmmVmgqPmRn88SC7lWp5erMTWboHwpTL9Qpr5QpLhWxbgpvyLqMv9uPpEk4lsPA0wP4uzYGV/4uP/s/sx/bsj/QWv6ow7YdFiYquGWZptzELbQKtLJbIdjmpTxXp9yQCGzznIxGo+wbeYLv/O55yis3OHysc0sLkWefGcLtUnjv+5Poms7hfTGOHO3gq//jLMnxDJ64h2q2hjfm2dIrdA2maSLLMnNzc/T29t53kre6UiKZKDE4HMH7Abu9juOQTqcJhULrFL+9e/fumJWULNX5xpkF5s8s40pU0A0bwQFDFnj3coIre6N89NF+9vZpXLlyhRs3btDT03NP7+APGx5N5pOHOwm6FEZXiixHVPS6g+RAUxTIU2OkL8xHBiIc7Qlu+G727Ynj+mcP8oM3ppk6u0AxUwUHRE0iPBRi95E2xk8vk5jI4Im4ses2C2fqiFYawzhPV1cXyWSS3t5eotEo0WiUwcEWpXxycrJlKq9p9PT0rHf5lIsXqTxwgnfenqVeNRjeHaPnZtPlg0JVZT79xaO81elj6vIqkiLx4Md28djTW9ti/F1gx4ne5cuXSafT6z//0i/9El/60pc+cDX07yNs08aywRZYFyrx+rzUqlUq5TKeOxQjRVHEcFoiJ7Z5K9Hzdfhwh3SUTJWMALppE233suQ4G+R6t0OsP4BRM6gsFrGrBoR0OvfH6buNrmBUDTztrYu92WxSq9WYmhjnheO76OkfYDZdpVAzsB0HtyrRF/HsKMHLZCrYNptMI/d2+Il4VW6slLi6XGAmVVlnjIk3JdofHoiwp8O/LtO/urpKR0cH586dIxQK3dUD725Yytc4e3mV2ZkssiJx8FAbx3bF+Jmf+RlGR0c5c+YMds3GX/IzUZyg0WiQKWbo7emlo6NjA8/7XpB1GT2ks3xmGX+3H889zDN1ReLFQx1EvCqXFgss6hJa3I1qtYLrbEEn63XRF3PzdHeQhwYj6IrEJ37+MN/4w0us3EgRiHvwRN0bu7iA1bAoJcrkEmWaNZPSfJFXfuc8yj85zmPH9nPy4AhvvXeeiaUF9hw5Tr5mYtkOru4g9UKKw7sP0R1yU8xnOfODi1z9wTTUkqBISL3t6OEozfkiVrKCY4MsCsxdT7H/UBvyzcDJbJqUFkvkZnMYZYNAT4Dhjw3Tcbzj7kPnnT58/Oh38e6EIAgMHO6gb38byZkco+eXyScrmE0LRZdp6w+w92gnwQ4fuakcpukQicfY3R2nVCqi6y4UVUGVRSxJoF43sRrWesAriwKSKGDYNpooIXoUBF3GqZkIbgUB8KoSGZxWo88BtpmX6zgQZxmHarKGKAr0D4QJVC0WfrBAPV/HFXHhjrsRZRG7aZO4lOD8e+d558/f4cV/+iI9PffnSWRZNpfOLdM/FNpWlEEQBDwxzz3vv0qlgqZpNJtN5ubmOHr06Lb0sg+Kjo4OotEoc3NzvPu9dxGnRCQkAr07q7arXpVAX4DVS6soboXexzZ2RqybXoprlgh3Q6lU2pGa4p2wLGvbJG9lucif/c+zVHI1jj83xHMv7qGnp4dGo8HFixfRdZ14/JbCaHt7O9Vqld7eXhoNk8kLl8EFlWoV1Uohd3ix8g1El4zo3VgM0AIa6YUiKzfSVOcKFHI1ygJUcBAccBdBncjgDejsf2k/8YNbK5sKooAk/iTJuxsM224JJwXcuH1uMpkM4VC4ZRfl0yguFSkW6nRtMZO2huE9UaZOdtHZG9rWJ1KRRJ58rJ++7lYXce0a/sQvHOY7f3aNYrpCuCfA0z+zl87u7e8Zy2rdB2vX+Ph4munxNLbl0D0QYu++2D2ZUYsLBb7y39+jlKzSfSDOL/wvJ+7L+mT9eIuL9PT0cP78ebLZLI8++uiOWUnVpsm3ziyy+PossVwDOaQj+lt0SLtq4ErXKJ1d4a9sB+9zuzhx4gTLy8tcu3aNVCrFoUOH/lbFoNyqzMcOdHBiIML3L40juEM0LRu3KiFVszx1bGBbm5SBviA9P3eIy0d8OJYH27Lx+TWGhsKcPjVPaipLx57orW69T2N5LMcTL+zFshpcvXqVSCSynsh5PB40TWNgYABFUWg0GiwsLNz0JhYZvnGDl5/4X5j73fNYpsPFLh+f+ZWT9OxwT74XQiEXn3zpIPZnWg2KHxZbhTXs+Iru6+vje9/7Hp///OeRJOmepr0/ypAUCUkUkGh1qVy0LkaXu6VIViwU8QduVRVt20JDRJCEDRVFX4ePw58YwfrqdRxVJtDhp/NQB6VUkWylsW6RsB1EWaL7YBuNwTDNpknestjTF0G92flpFFoVVMWlUC/UmVud43d/93c5evQo/+Jf/AtEUWT3+/SwWkMmVcFxNid60OJwx306D/aHWcxVaZitZEZXJLpDrg03/xrVZ3R0FEVR7lupdS5T4atfuUzpSgrdagWv3zuzxMwzA3z2oyMcOHCAgYEBvvMH32F8YRwtopFdzZLNZlFkhb/69l/x8z//83i8Ox9a9sQ8ZMYyZMYz9ww0ATRZ4smROA/0hXnz/A1KcoBywwRAcTXY3e7n8cODeG97QA71hfjcP32AV789wcKVBPnRFJpLQVZa96BZNzEc8HR46O7pZOXSKt2H2lm+mmBxLs+e/XEUReGZx04Su3KFQmqC/e3t6zNJ+byLZrOKKrfEFTo6TDRpCbdRoez2UKlWyGoq3h4vWsyNla6iLlrUV8ssXVrB63dhVk3SE2nyuRp1UUCIuigX6zin5rEMi+6T3TsSrvlxhCiLtO+K0L5rezqZrMvomkTA7aFp2fj8firlFqUHSUa2HFxepfW+oA4O+FSJiEclU2kS80mIuowcdWEsldbpwqIoEHCpNCwbvW7i6tqaZqK6FOKH2kjnGgzJIrFCk2axjh7RsUwLpS9AynagaeFWJSJDIWrnazg5h0t/fonOeCeRkfdPl0slK+QyVeoNk0ce/2BiAombNOV3332X7u7uv7Ekbw2KojA8PEz5cpnR66MMP/j+ZgAVl4K3zUvyapLo3ijuiJvCQoHMWIbCQgEc8LR5iO6JEhoIfaid8DWRi+0wO5UlPZVDkARunFvmuRf3AKBpGsePH2dsbIxKpUJHRwe6ruP1eslkMjfVTg0EWyQQ9uP1ubCyaezhEcygBis15KrRCm41CWwHSRZoZuqsXlylIjisOg5Nw0IRRagZZMpNtJCLgbib8mqZ4mKRQM/fDn3tRw2qJOKPeUgtFvF3+wmHQmSzWULBILViHdWjEgzdfeaoo9PPL/6LnRmgC4KwIYYcHIrwz/7dY9RqJi6XfM9g2TRNZmZmeOihh/nWX45y6dXpFn1cEDijSVw90cWnf+4I+jYJJ8DKYoH8cglXQCc5m6NQaBCL3V+iV6vVkCSJbDZLMpnk8ccff1+jJxOJMvOXVwnnGqjdPoTb4iTRraD0yPiXyqSvpDg/HGYw6qGzs5O2tjYuXrzIN7/5TZ5//nkajQaWZe2YKvpBoQsmDw1GN1jMTE5W7+mFubq6yqH9A5v8AQu5GsCGWNkT1CksFsllqgzv6sWyLGZnZxEEgd27dyNJEr29vUxPTzM0NISmaesxTuHcOYwGjGdkRJdBuDdAbrrI1FjqQ0v01vDDluCtYcdX9K/8yq/w7//9v+eP/uiPcLlcCILAP/7H/5hf/uVf3vZ3BEGgUCh8KCf6wwRXxIU/6qZtucl03dzgKaZpGpIkkc/lCAaCGLaDIIj47JYs+J0P5IGnBiivlNGDOu6wG0mTOKSJnJnNkqs0Cd3FnHoNoi5RaBr0x3zsintbHZ6VEstnllE9KtOvTiNrMqu1VWorNWaDs5w6dYrHH3+cZLLM1QsrBMI6Rx/o2vGF2tkd4F4KtS5VYlfb9olkvV7HsizOXZtAcUfIFwzms1Xa/fr7Mox3HIfvvztP+VKStqgH6SZPur5YYvr784zui3N8KIqu6vTqvYQ7w9TyNdxuN4qirHeqr127xomTJ3b8d6F1LWTGMnQc7UBx74zq6tVk9sRdDA3dqrRPTra8XbxbPJQ64z5+4YvHmFvK8fYb1ymkoV5uIooC3qDOnsPtHDjUTjFT5SvJCsvXkrjDbjp7NwbugUCA/fv3s7y8zPe//3327t27ruIViUSQJAlZETFNE1e1SjMYpKenF0WWKZVLFIwGYlDAEt3Y5Sbnyxf5B09/kuTZJFldYnk4hO1uyZyvmBbLqRLlV6eRVImuB7ve17r+BLfgbffSMxxh9swiq8U6MZ+Gx+slX6qQzdfos2V6BsN4273oIR13zE0jV2cw5iVVzlCqGfhcCnLMg5GoYlcNTE2iYVjsafdSrxikyk2amkjQsjfM5TYMi3zNxHEcdvs0AisVBLHVRVtaLJKpG5SLNexaAwdQRJGYT6MoqPz0px9FaSjMvD6D4Tg0NImu7sCOlTbbO3xUq03aP+Dc5srKCj6fj8XFxQ+FgdJsmrz81WvM3UjRtyfGi5/Zv2UXoJatYaZM9j24j0QuQSaToa2tjbm5Ofbtv7fymx7SKSfK5GZylFfLzL05h1k30cM6gtSa+c5N5uh8sJPuh7o3PVsMw0BR3r+4USKRuKsvYP9QmOhQiEq2xu6jGyljkiTh9Xrp7e1lZWUFwzA2zO9FIh2tDoXlgGki5fNIA33osRhGvEZ5NoOTq7Z8TW2DcqWKt6ySc6skVRHVhgCAbSO6FeTeINWgxowk4EmU8J5f+dCEbH7cIAgCh050852rCUorJbxtXiLhMInFJPWszb6P7aIj2urmmZZNzbBQZXGDtclOYNs2qWSFYmGzJYsoinh2EPMA6xTJiYkcF787jcurEL85k1vL15j4wQJnhiI8/uTAtsfoH47QtitCfrXE4JEOwu/DOulOLC8vt4zaZZmBgYEtGyHZSpPJZJl0uaWLEPdrDMd9+HWZy7MZ5KUysk/dkOStQRAEpJgb30qJqfEMyT1ttPlbnpYPPPAAw8PDvPzyy0xPTxOLxfjiF7+IIAikUhVKxTrxNu8HpqZuhTUa5e3YiX3BGvX2TgRuCphYhnWro1eoI7tlgjdHmjo7O0kkEkQiEa5evUogEKC/v59QKEQmk9mgrl57803USIC2wW7yC0UqmTq1Zp1EcpHZWZnu7u7187h69SoHDhy4v4X4IcaOE71/+2//LYcPH+b1118nkUjw+7//+zz44IObzCB/HKB6VKJ7okQWCizKAoWasYHuKMsy/kCATD5HHZ02XSHqcxHawqfK3+Wn40gHq5dX8d5Uz+yNuLFsh4uLeVYKNYIuFdcWQ/m27VCsmVSaBn1hD8f6QgiGxcrlBNmpLJbtIO+LktMlVNth9twcvmUfL7z0Ao8+8iiO4/CXf3iRxYsJNL+KLEscProzrvcH3TBs2+HVs6NMpuvUCiq1/DylYpkrY5eJ9wU5sifGnnb/jmikhZrB8kwer9BS9oQWVUfv8CBP5xkfS3N8KEo9V8eqWPSN9OEteEmn06TTaWzbplgsoqitv7WUqzGfreLQMiXtDbu27V7rIZ38TJ5KugIONMtNXGEXvo7tg9OtNjhZlmk2m3f9nKJd5rOfPY6qqps66o7jUKo0OfLcEMmlEoN7Yuw7sDFYa29vZ3V1le7ubjo7O7l+/TpTU1Ps37+fubk5BgcH6e0LIXnBLIPR4SeqqiAKBIJBArTox7NLS5SUFG4pwZe/+mVG3A+yHHPh92q4b16njgPJYp3JaoP4hVVi+2KoO3yA/wQbIcoi3Se6OJAoI8znyTgNTAmEhklnw6anTaT7ZCvQV1wK0T1R5t6co2soyIHOANdXipTyVdyqghVx0ZzJQUinK+SmN+ShXC8S3h0lG3WRKTcxLQtJFFuJmyTS5tPoC7rgRoam5aCHXcyullhcKSIOhwmFXOuFmYZpkSw1ENt3s1yX2N/pJT+T58//j7fIuSSOfGSIT/yDnc/fDg59MOGMZrNJOp0mEokwMDCwI9XJe+HKxVUuvzaD4pa5/No0vUNhHjh5i57abJqMjSZJXExgLhbpf7CT/mA/pWKJb33rW+i6TmdXJ7LuJVdtosoSca+6ZXKiB3WW3ltCQEBURcI9t6r0rpCLeqHO0pklvB3eTTYSyWTynkbuW2G7AGwNHZ1+fulfPkqpWN8yCRcEoeWp2dGBZVksLCyg6zp+vx/DqBDq9LEylsFvFkEU4WZQpgRdhI50Y1cNrGyN2etTGIUm1VKVXLcb0VRw+1t0NsmvIgV1BFVCBRLFOiuSQNtcgXKifNf99yfYHice6CK1UuLKGzMUrycBAUGVCOz18NAjMWpNi7PXE1w5t0wlX0fSJfYeaOf40Q5ivnsrDM5MZ3n169dJzeZpGHVuPFThYz+zb1sLha0wNzfH9evXsSyLRx99lNf+ag67buAfunX9u4IuSskqNy6u3DXRi8U8/OL/+xGy6SodXX6kHYrP3YmVlRUymQxDQ0NEIhEuX7684XXTsvnBVIZzczlytSayDQhgCS1T9qM9QZKJCnrDQryLHL+oSaiCQDFbo1AzNnjjBQIBRkZGePPNN5mYmGCgf4j8qp+x00s0aiaesM7J53fx6F3W437gOM777mTdjTVw8FgnF9+eZ+V6Gk/MjW3a1PJ1hh/qYeimLYosy5imia7rHD58mGw2y/nz5+nv7yefzxMKhW45AZw+jbFvN899Zj/f/eo1GuUmD7/Yxyc+e5BarcLY2BimaVKtVvnOd77DysoKTz75NK99e4LZ6ymCcQ/P//SeHXk6/rDiffWon3/+eZ5//nkAfu/3fo9f/uVf5gtf+MLfyIn9sCMyEqHzRprqQoFxwyJhNAi6ZTRZaslDN21q6MjNKr2SFzPi4rtnF6l836CnP8ixIx0EXK2He9eJLkorJYpLt2gnAzEPbk1mMlEiUayTKTeg3sTtaykUGaaN7Tj4XQpH20IMx71IwMrVJIWFAkXTJhN1UavUoVJDFEXk7m4eag/hyXtIXEkQO9BGKVNt0WMqBtXy3RONDwuGZfOl753nytkEwYKIp2oRFQU8tRpa0iIzluXlCytceqCTn3qol46Ai3S5Qa7SxKVKdAZcGxQK14tHd8ZJggAC2DenBCuZKtcvLlHEh9vj47HH91OrltcDIl3XOXdjhrmqgmG1EqmlXBXbDjOwDTVTlFriGctnlikttQQhNJ/G4PODmwKvNayurm4KwCRJwjTNu6+bYawru96e5F25keTsOwssX01glA0QYX4sxex8joce7aO/q3VNqaq6nkyKosj+/fup1+tcvnyZSqVCMBgkHA4TC1XImRINX7Q1WHkbcitlvAEPgU4XbZ17qVyoMJPOQXfbepK3tvRRn0ayUGN5qcjQconIXeiJP8HdERoIceiTe4icXWZxKku9ZtCQmxx4apjw/jBZK0ug1eug7VAblUSF9FiaXf0B4j6NhVyVRLGB3e0j6Dh4ygZtAZ1asown6mbXsU4kt0KiWGNhJU0gHEIRWz5NEY9KJVlhMV/DE/OwmqmyOJfH1RsgMBDckKBoskTcL5E064wuF/FqMl0dPgrfuk5Nl8mnK3+r63bu3DlGRkY2VHg/MJzWUOPaPejc5lvRaJj82e+fZ/rsMvZEDk2VUNo9dPcGsWyLUDhEYjXBy997k+DuExSrBrIksqfdx4GuzWqGroiL+bfmUT0qXSc3d8X1gE4tXSMzntm039xtzm472La9o5EMn1/bNjiPx+Mkk0na2tqQJIm+vj6q1SqJRIJSqcSBB7tYvprEWF5FCoU3BXyiW0F0K7jsKDSLxHe7qOyOEfLoaG5lS5/ZgEshVzcpFFtKvz9J9O4PkijwUz+1h4NHO5gaz2CZNo5U5uknDzE7v8DvfPk9Eucy6HULTZNpNk3euZxk8lqSz/zsITrukqSUSg2++aWLZOfyeNvcWBWJK69O49gOL/3isR2fYyKR4J133sG2bYaHh2k27U3PKQBREWnU7h3TeDzqjruIW6FarXL+/HmefvppSqbIe1MZRucqJKwc+wY0Ovwa359I89ZEimDDonu1gpVomYbLHR5KbW5erSap5KrsVBpKAMaurPLmH15Cdck88bERBgbDBINBPvvZz7K8vMy3/uIcnlIf7oiLUKePYqLM9796jWibh917tp5lXcPqSonTp2aJxLw8/HjftknZdvvFvfaQrWKgNfj9Op/+R8d467uTzI+mUDWZQ0/08+TzwxvOY62gJAgC4XCYcDjM7Ows+XyeiYkJdu/eDYB26RKNF19k34F2hkeiNJvWeqNCVQPrqqXf/va3MQyDb3zjG5x5ZxljPoCsSSxfT2EaFj//y++P7fXDhPueOrXt7WVvfxzgjroZeGYA59VplIUCqyJkaia1ahWzbqIpMBTw0eHWSDpVzl9YwMk3EUWRSRHGjyf5/C8cxu9S0YM6fY/3MfnXkxSXiuuqYW1+jbhPJV81ePutWSbOzRLZ1UbXgXY8fpnOm55Cys1qejlRpjBfoFAxWNZF5Libtpuy7U3TJmPbzDUNdmsyyctJXN0u9j4cYfqSSrjdx8EddvM+CGzb4StvX+Pca/MMVlRcHhGp291SDS1J6B4ParmJsVxhpTjL10yL7q4AE1MZyukqqltheE+MFw524NNbHbigW6GzP8j01STuQgMpoLUkblfKWD6F3TcNgi+fWWL2SgpCOo5pEQgFOPLAvg00qrELMyTTGfb0tihGqVKD6XR520QPWhWt1GgKOaSj9/qpzhe3DLzW0Gg0NvmzyLKMZW3vmWfb9pab7dvvzvPGn13DztXwxz3ofW6woZytcuOVCRauJfnEPzzK3pudkTs3YF3XOXHiBNlslr/6q79qyWTnLpKz5zlnHWHlego90KLC1gp1dI/Koz+zh6ee+ykArrqv8tq5Japb0PEkUcARBAzLxmruzA/wJ9gewf4ggd4A5VenuH5uiWJTJKwL9PeGicgCk5OTxGKd3LiawvbIBIbClGbzSLLA/riHIz1BAOoDYRZOLZAeTREaDNFxpAPVp+LYDuPvLZJeyTP8iSiR2JqvmUNpqYSAQKPcZHmhgNTpJbA7si1Fzq1I1BCYSpXxCU2GD0Qh4uOJT97dx8gybaYm01RKTaJtvvueoXAch/Pnz9PX1/fhJnnAgSMdzDzZz/xoiviwh4NHbu2bl88vM3V6mVCXD7PQoLRc4sp7i3T3BgkGg+uF0tcuz7JQqNAXC1BpmEylygxEPbf5w7UgKRL1Qh09fKtyv5htzT0P3RTF0AIa5ZXyBkXn+0UikaCtre0DHUPTtHWFzTW43W4GBgZYXV3lemaS6FCI1W9dpy0WZ6sQ27YcpIpO+4CLnqib65qIuk2SB63Os2k7WE5LLO0nuH8IgsBAT5CBm/vFzMwMmiKxkpNYPZMh5lLQ+m4pKfoLDVLnV/hum4df+NT+bYP88dEkufkCbSMRmmYTdzBI1dVg9vIq2UxlRx2TRsMknU5jmiYjIyMtBca+FNdtB7Nhrlu72KZNvdTk4Ejsw1mUbVAul3njjTd49KmP8NcXVxk7v0ptJodZbTAppzg9vER0OELCtgnVLZRLSYxSEzGgggONGxnciSrCwQhzDnglcJcaiNrWM8RO06LpODiiwMVvjmNXDWzDopSp8U//3eP09PSsi1+lJ19nsZCgLRpGkiWiAyGWriaYup6+Z6L3+itjXP3eNO6oi45u/7bMimQyuUF4aae4vWh9J7KFOqPXU6RzNaSASrwnSP/++KZkPBqNkk6nicVufcf9/f10d3dz6tQpKpUKh/buJTA1ReHxx4GWQuZ2Yju2bbN792727dvHxFWT6zOLxAajZBYKpBf/fo+g7TjRm5+fB1jn4q79fC/cj+LX3xcE+4Ps+vguvOdX6JjNc+NagtnRJNQsbNEi/Pwehp8e4MbpecRCmtieKIIkYhQaLJ1d5tKRdh4/0bopQ4MhBp8dZPb1WXJTOfy9fiSlpaoW8qj4RQG9adHrdfH0vq0fxOkbaarZGsW4C9o8RAK3ggNVFmnzu7mey5OTBBpzaZTrCh/7+FHET9w7OCjUDM7N5ZhKlQm4FI72BO86f7cdTl26wZkfzDNQUXBHXUi+jQmPIApIfg3RoxBZKDLx2gwX2jzsSdaJNi0MUeBqooJPV3jhYKv+JQgCj53sITGXJ3k1iZ6oYONQ1SWGnhxkX08r4SoV6liWTbTTR3a+QLlQ23R+Hq+PQANWV1eIxmKIN6tGd4MgCEiqRDFd4MrMdfbHh5G091dJv1dHb2VlZVMFbGwmy5tfvYZSNwnui214wPq7/PjavCTGM3z7jy/T9r8+TNivE4lESKfTRKPRDccKh8N8+tOf5rXXXkM7f4YHXFUCP7eXzJJDai6PKAvseqCDg8e7GNkT57333mNmZob6lTrFFbD6N3cbTKuljKcr0o6M5X+Ce+PdH8zx2l9ex2wYCJLIq390kdXFEp//x8fp7u7lt3/rDXJTFRBg14NdPPfxYbLjWYoLRUrLrWRNlFuG47ZlU01XqaarLaEol0IlX6dZMalUGuuJnlkzKcwXMOoGTdum2ukhMhBCuMesXdClsJgqMByOs/toL2bTJHwXilYqWeYbX77M8lgaq2Gi+TR2He/kp146uK2C31YwTZPp6Wn8fv8G2fGF+QIXzq4iSwJu12YlT9O0efv1KbKJCvuPd24bDGmazGd+4Si2bbO8vIwo3tofKuUmjm2j+zRqooDsVqhXjZba8m3r5fX5EYo5KpUyourCNsHeZp8RBAHHuvXa3PQk7Z1dQCvRc2wHURE33P/ZbHZHtgqO7VBJVtZ96Url0l0tJ6anMpw7NU9qqUgo7uHQiW72H9w5PbS9vR3btlGVFOf+eIFk6AjupSKBuAdJkbBMh1K6TDlVxdKbdO8OYC/WEDUXTdNC20bgoWm2ZktlUdix5+hPsD1s2+at16a58s4CJk2e/ZSLS+eWcTUttIGNcvlyQMNXqLN4JcHKM0N0Brfu6jWbVutalUSsuoVLdyGpJkbNpNG4eyHQNG2++ZUrTF1coWKX+dl/+I9QNRNVVRka8XO6z8XqeAa3vzW/WsnWCA8GOf7I1vGn4zhkKi0z8ohH3cAQ2ikSiQTT09McO/EQr7y7yPh3JvEVDdpCOqYmgmFjjmW5ei1FZijAw46EVTFQb5udFwMa5kIJz1IZT1QnHVSJpBotWvIW+6uZqlLyKERibrKXkkT7g9RKTSrZGpVyY+M+aYPP51lXHNZ0DQEB07x30dUb0FBdCppHw+3evttZq9XeNz38bqyBpdUSf/5758mMZ1AVCUmVmJzIMXN2mcVPjPD8s8Prv+t2u0mlUpuOIcsyTz75JJcvX+biH/0Rx0yTwE1Pzbuhq6uLM2fOcPXqVaKB/ch6kJUbaSzDYvjY3659xYeNHT89+/v7EQSBWq2GqqrrP98Ld+tS/CjA1+HD+3Evqdk8b19choBOZI+X5FKO8VqDg7tClL45ii9ySxZfCWjIS0UW5vJw4tZsR2RXBNWjsvDOArmpXEugJdqSLd93OE6msMwDW2xcjVKD8mqZaqqKf0+UOV0gsAUVQRRBEQVm0nnaMzlW3llhNDHKZz7zmbt+xrph8fXzi0y+s4A7VWPFJTN7MM6nH+9nOL6zZM+2bW7cuMFM1sCddXDpyqYk73YIkojc4UO8nsRZKSFFvKj9QaR8Hf9yhRvTWR4fia2LlwzEvHzuC4c5fXGZhZk8sirRFWzy088M4bn5np7hCKNelcxcHsWt0rGFdHNX0MVyvoboDTOzlEJ1udjfeW9CRXhfmFN/fArVVgmdCNJ2cOtkvFAorFMFbse9OnqNRgNd3zgDceHMIma6RnR/bGv6hCwSHwqzPJ3l6uVVnnisH6/Xu2WiB63unsvlYiiXY7yjA7evwrP/6BFkeaMCWrVaRVEUxsfH8Qk+dnfvZcxpFQMCeqvqbtsOqVKNsOHQORTY5G31E7x/mKbN2ddmWvNPe1tJSGo5y8zFVaYm0rhcKqWVBv4uH5ZhsTCWRnrpACO7o1TTVcyboiqyJuOOuRFEgcJ8geS1JIXZAsZSiZE+P7UuD0FForhYxKyZNMoNjHrLbLoU1iFbQb1b4uUAAtSqZWRFwRYVUtkUk1cn6Xmuh7hncwJl2zavfOUKcxdXiA4E0dwqpWyNq6/PEIp6+MgLIztao0qlQjKZRFGUdbsU27b57rfGOP/aNLVcHcNscv3dDM98eh9HH7yl8nvuvQW+/2fXMGom8+Npuv7d43edRRZFka6uLubm5tZFXto7/SgehfRcHqlu0ig06BoOb1Kd7Q27WSnUyVUbNAp5DvbF8G2xprZl44q4cAwHx3ZIpVIsTd+gI+QFWlX2er6+PqO5hnK5fM8ia2G+wPL5FebH0pTKrQ6cJDUREyIdxzo2KQnfGE3yzd85RzlTQ/OppCazzF5OUP3Zgzx4hzl2MBgkn88TDAY3/d3Ozk6aq6uMTPwpf/1zz7GQskhOZsF2cAQBT0jn6EeHSeSvML1wCWvCxnfgKPmqSFtg60QvX2vSpamEwi687dvL//8EO8O1Kwm+/7VRBEGgWW3wtd89jejz4HYrWz5rXAGNUq5OodzcNtHrGwjhCulkF4vIAbBMi8JSkY69MeJtd//Orl5a4fIbM2heDSsjcv1CDkMe44033sDr9fKL/+uneOfNWSbOL2PbDodOdPHwEwN0bKEqbtsOr15b4cKZZWzbZv/RLj52pGODCNW9MDMzA0BbWxvLFYGJ12aIVC20oZbgntVoIAoCWlxBvbyKejFFxqfSfrNLugZBEBBDGlaiSlunl0x/kJyRI7RQRAnriL6b9go1AzNdoywLaAdiPHG8h1OTeVYnMoiiwNDJboJ30GZ3He3g1J9fwxUMYJom2akCki4zsIMRioPHg1y6muSlX3yJ9g+ZBr0da8C2Hb79l6Nkb2RoH4lsKJYXF4qceXmc3r4ge3fYpR0eHmbxD/6AXFcXc1NTDA8P4/dvjEMMw2BxcZFms4nb7cbn8/H444/z9NNP896peWbH0wSjbp549v2pJv+wYceJ3u/8zu8gCMK6itfazz9B62a1dZmmSyFwpA23XycSUilkyph1E9Wv01wp46L14HRMGxuHQGDz8LKv08fIJ0ZIXkuSHk2Tn8kD0DCqdLXp6LJIo9TAalg0K03MmonqVYnujiKIAk1VgmRpWw++ZrNJOpNh5eIl9KpOzBvjxo0bG94jiiKqqqJpGqqqMl8wmRlNEFkoo7hl7EydzJUkl/pDO0r06vU6k5OTdPQOMPnWWUJ1kNrvPbgtahIyIGXrlMNePICgSgjlJpZpYdkbK+A9ITc9Tw9jPGEjCgJGs8HS0iKBoSEA9hztIPl4PzVJIBjz0L0FLaw/4sZ2HKbTZfyuNjSjhN7IwTZeb7bVqk6dnztPobNAR7SDkZ8aQd2mCpbJZLYUMLpbR2+rjmK+0mTu0iqesH5XhTlRk9BEkcsXlnn80b573rOKJBGfn2fsk5/kwQcf5OzZs4RCIXp7e8lkMjiOg9vt5tixY0xMTPDMk8+QPZPFPDXPXNVgqW4g0GJYBUyH3UE3PQ92/aSj9yGgXG5Qy9dwBW/dOy6fSjFbpZCr097pxx10UVgqYtsO8aEwHo+67jW3FYJ9LTpoNVWlkqqwMLaAZrUqv6Ii4o17QQDVpeLv81MvNyB39zk727aolMrE2uKUiw3eO3MaMgmotqg+DbGVVKwp1GmaRjJRY3EsTagngH4zufJH3TTLTa69t8hTzw0j3aODmM1mqdVqRCIRms3menHixmiSM389geJS6DoQp1QqU0vVee1ro/QNhQnfVHJr1AzMpoXqUWlWDJoNE+4hOiUIAqqqrtOx9+yP88Sn93P6O5M0NJlou48HHtnsz9ke0HlsOEK2YqCIoBplmkZzE6W7lq0RGgghazLpiTTnJs+BA8VicZ1Sq/m192Vf4TgOuakcV14eZ3QuR0YTsWSReqOBxxHI/PUkhxaK7PvEyHrSZNs2P/juJJVcjc59sfU9JzmV5d3vTnLoDnNsv9/P/Pz8lokeQPvCArJq8vP//lMsrWS4emket9sPWATDIoGgTiKxm4tSjWP7j7E8WeIqFplyk7BHXX++2TZkKw00SaTDdAj2B3FH/2atM34ckEtVMKsGXQfbqBTqJBezuAPubSn4Zt0CRUJVt79HO7sDPPTxEd59eZzMZIGq3iTUHeDZn9l7TxEUy3LAdlBdMlUE5uYWGF94A7/fz8mTJ8lnV9h7UGP3gX4cpzU2UK+nmJpKIcvyhnhmoWDwzhszuK5nUYDzuTqDHV72d96bJu44DqOjo8Tj8ZaFSGcX33tlDCVXQ+0JbHoWC6KAHXXhmspTrJtEewKbqMqCJLYsuESR/f1hxK4AC2eWcKVquLJ1BAGaokjJI+I50Mbzj/VzuDdE5z97kGsXV1AUkSMPdm8a7Xj06UGSy0XmLiUwagaKR6b7kJ+9B+5OtSyVSnz7299C91bovYthfbVa3bER/O3YqmgNMDOfZ/VailC3dxMjytfto3ItxZXzyxsSPb/fT7FY3JTAAaRqDuemc3ge+hhP7T1Acmme+fl5BgcHSSQSNJvNdUuvNRrpZz7zmfUC4cOP9/Pw4/3v+/P9MGLH0dcv/uIv3vXnH3eEwi78bR5SU1msdptyooYWkejp8LPnRBdn/+I69nQO1SVTztbQe/wcOLx1y1vWZDqPdRLfH6cwX6C8Umbs7BiaqNEoNtapgqGBEL4OH/4eP+6oG+dlh8xEFr9LplQ30bwbb5ZisYgtiBwdGSQYbSPhTxAcDLJnz54N77Msi2azuf5fKlfAqBhItoMUcYEDasOiUL/3oHM+nyeRSLB//37OXJ1AQUUBxB0G/t6gTipfp1Ft0lwo4lg2pajOnu4A/m2OsVaZk3QdwzDWRQn0kE7XcATLtPDEtzFgFgWG4t71+RdoJ5PJMHZjjOFdw5vEDRrFBqpf5TMvfYbzV8+3krW7NTu2oWdt1dGr5WrMXUuytJhkYFccx3YQRIFqtUq+YGBUDNx36YquQXMr1LM1GqaNrrTkz8vlMl7vHVVUx6Fw4RLzrhiZ4V2Iokh3dzeJRIJXX32V48ePb6DCfe5znwMg8HQAURaJX0uSytawHAddFukaCNB3sovY/r/ZOYkfF/h8GoF2H6sTGXyRVjDbLJkIukC8w4ffr/PxLxzi3TemkSSRx54f3hHlURAEPHEPnriHhr+xyeC8UWqQvJzENmxcN6lzlu0gbVFgsG2bfKGAP+DHdgREQeDxRx6iODvHxOgEQyNDuAK3ggPDMGg2m5SKmVbRqn3j+SouuXXvGxauuyR6KysrKIpCV1cX09PTG4opk9dTGFWT+GBLsU2UBCL9IRJjaSaupzj5aOvBfuREN7PjGZamk5z86PCOVdY6OzuZnZ1d7+o9/vQgxx/qIbdYZOGvJ9G2KcREvNptXqlespksjUZjQ9BSz9bpebSH0ECI6demOW4fZ2FlAbWmkrmRwR110/dE34b9bC2A2QqyLFPNVxl/bYaL8zmybomoV0NTJEolE0lzM1+qY11P4PKp7P3UHk69c4pjxx4ivVDAG/NsCGZ9cQ/FZIVkorLzeUrHYeatUZIHnuehponLJbH3QIx0Ot1KlvfsQpIkurq62LVrF1JVQqhPYC6XmHQsVvJVxJuqsLZt49cUhhHo6g3ScazjJwXoDwEdvUHcIRfL15LYlk2420Pv0S6uz9/AVzEQPbeuL8e0KWarRB7upjt89yT7yWeHGdod4+x7Y3R1tbN7fxy//95F370H41w71snSjTTRwRBPfXKAw8nWNd/Z2cnQzWLuVjBNcz2WqVQqLCXyGLkaIVFAkASKxSZ1497MM9M0uXLlCiMjI+tm7RXDIbdSwSWIiJv22tZ16NIVGrKIYVnUMvVNRWCr0ECOuanLMBTz8OBAhGv9YS5MpCmslsAGyavQFxXZFVY42ttKvmIxD089t32nyeNR+cI/Oc7sTI5ivk5bh49Y3M3U1BS9vb3bJmk3btygXq+39vJtuvKwta3CvXBn/HO7eng6VcGqGehb7COCIKC5VVYXixv+PRQKMTc3tynRW8hW+fPT8ySLESqDvRQuLvFQXMRxHN588036+/vZs2fPpr1iLcn7UcNPyuwfEjRN5uOfP8Rf/+kViukq7bsiDD/oRtNknvv/s/efQZbe930v+HnCeU7OuXPuST15MBjkRAAEGCEGkZRkXZmydaVdv7he794tV7l8S7fKVXZdu+R13fVdWbRMK1ASRYEURRIgCYIkwgCYHHq6ZzrnPjnHJ+yL093T4XQagDJAzacKrGH3Cc85/Tz/5/9L3++TvYiiyK335igXagTOtvHoM7107jLjJptl/P1+/P1+li3L+HU/Rw4faQR6ZmmDoSRAYCBAcjRJl9fGxblMwz9rRbAkk81SNky4LCb8BjijHh7+4sPI1q2ngCRJWK3WtYVgSHFwZbJKabmEbTqHJgmUO530BnZut1hcXKRWqzE4OEihUMCkKIiCuN08fVM8NgUl6iDe40FGoC5D69EWHh0M7emG3tPTw8TEBP39/Y3v84CfmZ/PbBvoNcPv92O32Rm5NUJvby8W690bUzlRJnIygtlpJhAIMDCwfYvZTrLlmyt6pWSJH/y3K4wMxyhWykT8MZ7K6rj6Zb7zne9w6txTaLre8KPaBV3TkRUJeWWDFggEmJ6e3hDoaZpG+ZOfxDyc5i/7nyPy4xtkXkzS3t5OR0cHhmFw+/Zt3nrrLY4ePbrhuYpDoe/5PsLHwsSH4xSXi4iSiLPV2agkrbTy3ef9IUkiDz3by/djBeauLzfmWWWJjpM+Olcyr4OHQgwe2v9w/E4odgVH1EFmKkOww4XHqpAt1/Ftag9flah2Opzouk62UsdrNRFyWrC6w/T/aj+WTZs6k8mEyWRi8EA7rwfGycWK+Fdu9IZuUEqW6ToVxbqDzcrU1BSBQACHw8Hc3BxtbW0bfq/VdTbrGIkiYBho2l3hDpfLwm/87lnK5fK+/V+tViulUmnNiN1qNWHp81G47SV1J4Xi2F3Vz+f3USgUSMQTBIIBaoUaJqsJb48XR9jBwc8cZPziOIFCAFmScYQdeLo8WDZ1hsTj8W3nZiRJIjWeYnoqRcoiEl2nYCwgYFMkZLeVpUyZO8OLXFh8j9ncLB63Dx0NrbJR6KReUZEVCZt967pmtVopl8sbN5SVCoVwO98Lv0TJ1kfmf/s/efpffpWOjg46OjrQdZ25uTlkWaalpaWxyfRA78d6kX46hXsuSwqRbK6KUapjMQw8Fp2WIyF6nulZsyi6z/ujfyDA879+nJuX57FaFQ4c9yA6rMzeCRAbTuJ2m1GcZrSKSi5ZQo86ePDRrj156rW1u9H0MJ2dew8SbFaFL/+TM8RjBSrVHA6HGcnUyuHDu1u1yLKMLMtr16Zk93DrUI1EbhZBB/cB/7btpqsUi0Vu377N0NAQsiwzNjZGX18fiUK1kYDdfH9bd8/z2hUyskDVa0VQddR4qWEDZRho6QoAWocLs0lmcMVS6qG+ACc7vVydTXPjVpzYTJZ0RuDHpVlsriBDnd49fdeiKG4RUhkcHGRqagqn07kmVJXJlFmYy2I2y5w8cYpgMEgwGNzRZmUnW4VmCe1iscbli2P09DbaNt955x0ymQzPPfccuVyOdDqBquvoNR3JsvWzqXUN8x7stgBmv/MK6a+9TttYlkpZ4cbFER79wkP09/YwNDRENpvlypUrtLa23pOYzEeN9xXopdNp/vzP/5yJiQnS6fSWP64gCPzRH/3R+zrAjxLdPT5++188SrFUw25TmF+YW6ucvPhMH48/2kW1ruG2mpD3qY5WKBTo6urCvEMFx93pbtgzzGQ5HHExligyly5RLpewWm34HWZcgohUrBM82bJnX7Oo28qjJ6K8ASSW84gWEwOHg5zo3L6sPzExgcPhWMuQLCws0NnWhcmWog5Y6lpTU9DNSHWdUNTJwad6aPfaiC/O8/iJdvzOHWScK3Wu304wMZ7EYjZhc5Rp76hhMSv4+nwsX1umGC9u287WDIvVwoGDBxi7M0YwGMTn91FJV5AtciPA3mTS2YylpaVthQ42V/RGLswzcjOG1OHEUZOJFwz+9s/fpOAaweqzMj56DXdbmOJ4Dqt/++/CMAzK+RqDvb4N59zma/XChQtIuRztuQVmbWHOvnGTxPI/IrqyaRYEgcHBQWq1GteuXUOSJLq7u/nWt77Fb/7mb1LNVlm8tMTUcIzlRImqqqFIAn6Pla7BIF2PdNyfnfkAGDregttjZfjaEvWaRnu3F7ev/gt9T0EUCBwIkLyTRDKgJ2jn4lSaqnJXHKNeq1Gt1nC5XI3suWZQrekMtbgRDQNDNQgfCm+bnHG6zJx+uoef//UwiyMJzHaFSraCzW/lwaeae7WqqsrU1BQdHR0oikK5XEaSpC1qbh29Pq69PkkpV8G2EmimlwpYPBa6enxbXtdqtTYd8t+JcDi8oaoHjWsmdKTRlVGMFfeUXHI4HJgVM/Mz85jyJlpPt649z2QzYe+yb6m4bmYnWwVZlklNpohXNRSnaS3IK5fLawksRRZBFhi+M4OzrYC71Y2BxtFHunnn2yMU7AoOn5VSrkJuqcChx7rwN6l+BgIB5ubmNh6v2YzVJBCsLpPTyhyfTm1IcImiSEdHB5VKhcnJSbxeL06nk7H4GEOfHWLmjRnK37tDbjpDqVqnrEjUww6sqTLp8TQ2v+1+m/gHxLGTLRxbEaEwDIPp6Wle+vJxfvSjMZZuxtCTRQSThOd0lIce7+ZU/9a572bsxUy7GbIsouk5vF4HHo+HWCy2p+flClUWF3LoOvj8VsJBB599uperPT50w2Co00vUvf09NB6PE4/HOX78OIIgbEgmOcwydp+FomZgU/WmAioOk4xVkpgN21H6g4gzOdTlRvu75LFAr5cFm8zJFhet6wLOy+NJXvv2LdSpDDZDAFHAKFf4xsTPmPjMUT71WDeWPeyhmtHV1UUsFmN6eprJ23Uu/GicYqqMaBIJdXs5+rB3w1q2mb3asKyiqTp/8V8uMHZhhkh/EGt0lsXFGdxuN4ODgzidTh48d4DrP4+TXczj6/ZsfH5Fo65pHGiifWA2m6lUKhvaQc2hACI16hYRNRPDVsrjsN7dO7vdbk6cOMHc3BxXrlxhYGBgLRHwy8g9r4ivvPIKn/vc5ygWi7hcrqYKX/8QWygkWVxrRWhpaWFycpL+/n6gsSg4tmmlWpjLUihUUcwyLa2uLRKw+Xy+aR/yemSzTNeTXUz8aILobA6/18p0IYu7oxWLIOCuaMws1og+HiV6aq+OLQ3O9QboCTpYzlWwKRIdPvuaSfJ6VkVX2tvbcTobFcvVhdFmU+gZCHDzWgxbptGusBOGqlOpqdg6gzwxEKLFY6XYYiMWW8LvbG76mSnV+MZfXmfhvXlMVR0dwKuwsFTitz53DpvfRsvpFiZ/PInZad7XpkCSJAYPDDI7M0s2ncVeteM6HMIWsrM4sbj2d96OarW6raTw5opeIV2hLoNN0DA57OiiikPw8eLnvkiikmj0lre38dqdK9QLNUzbVAzK8SKCU+HoyY2qmKueeqvHo+s6V/1+fjv9Jv3pGb75z/4Z0VJpS+uGoiicPn2abDbLH/zBH1Aul3nte6/hzwa5cHGBZUWkbhaQrQq6rmN4hGoAAQAASURBVDGVrzDzxhS5ZJmhTw3c97f6AOjo8m6YnVhYWNjV5Pr94u5wYw/aKSwX6I04yZbq3InlsSsmFEFFMAwcTge6bpAu1KjqcDDqoSdgp7CYxx6y496lte+xp3txuMxcPT9HPlmmayjMqUc76R/YuoEslUosLS3R29u7dp9ZXFxsOv86dCLK7bPt3D4/S3omR61axeF1cObj/bQ0EWSClbnrbSxN1lOtqqSSJXx+Gw6Hg3w+v7buQWMGsuORDiZ/MomxZOwp2SHoAkpRQfNr+I77PtD7qCRJ1Mt16hgbxCc0VdtQeRNFiUhrG89+6iCRByLUajUGBhVyqQqTlxfJLeaRrTK9p1t47rOHmr3Vms/Vph8iPfYIv/Y3f4PR2Y30/7vGZDyO2+3e8DktFgvd3d2k02m+9a1vsbCwgKPmYH4kxZhokBlwgywhyCIJXWc5lmfxuyOcyVfpfaZnTWb/Ph8Mq3/LnoiT3/rSMWaSJfL5KmaLTGfQgVXZe8BRKpWw2/dvPr2+cr8XUtkyb70+yei78xRSpcbIic1Ex9EwDz7ezYsntypFN3tPwzA4dKhxjlcqjQrcalBhMUkcOx7lx5cWsS8VMbc1EX9JlrD4rQwejZB2KIheM85qw903JWgoNoUTrW6ePRReS7zMpEq8/t0R5IkM/lYn4kpS3lnXmL82zY3v3yYUsvPE0P72cesJhUK8/cYYr/7JBRxuB6FeL/WqytzNZfLZAsdPDG7bSbGbrcLmNatYqpGYzaKpOpmFAh1DnbS1hVlYWKC9vX0tMXXiiR7e/OZNUpMZ3FEnokmgnK6QXswTGgpx7MTWzxsKhZibm9vQRnr4yQeY+l//N+60tmI+e4ynPvcUheQSAWfXhue2tbXR0tLC7du31+wVJiYmCAQCe1It/qhwz6vhP//n/5xIJMK3vvUthoaGPshj+qVBlmXq9Z2z7VcuznPl7RkW76RQKyqCLOBtcXHkbBtnH+nCshKIbN5AbIc9aKf/4/1MX5xm+sI0A2YXFOqIsogj4iAUCtH1RNcWFbi9EHZZCO/QT1+r1bh9+zYDAwNrAUS1WkXTtLVsyVCHl+FON7XhBKJT2XZWzzAM1OUiBZuJAwcDRFfak+z2hlzwdrz13iwLb88R9lmR/VbQDQrTWcbeWGLkbJJDnQFCR0IUl4ssX1/G0+3Z96agJdrC1KUpfjY2DrNpxrIljpyxva8N2ebnRtrd2ASRhXgBX8BNPV4k1O1ncGiQIUfjeiuU69y+vszcu/P4W5yYvZa7Js6aTjFWIpOpcOT5XgY3VS68Di8TVyfwO/yN2au8lQl7BwXs/LCvndqZM5w+fZq5ubktmzBo/H0kSUKWZf76P32LQ93PkPDa8bssG7KMdVVnOVfm8kgMh9fCkV85+L69vu6zkVWD6vXzkx80skWm5UwL4z8cp5apcLLDi9Mic3MuQaZqoCgm0pkyAqBIcLjNw4Goh2qmjF7XaTndsut1Jooip892cPrszi1d6XSaYrG4IajbyYBXUWR+5dePcWMozM1rM2hajQcfPcSBHVpcw+EwsVhsR+nwpcU8L//xJdKLBbxRB5/9n06SLCS3rNOhIyFESWT659MkR5NYA1asXusW4YZ6qU5xuYhW14gcjdD5WCdLyRjf+k/X0esin/ry0V0Dz1Qqhc+3tUq5iizLyHYZmyCwXNeA5hs5VdOwSgImmwlFUdbW81/9rVPMTKdJxoq4PBa6e7canm9+vy1JiBMnEF5+GfGHr4DDQZvZvLXyt4LFYmFpaYlsIss3/sO3cLUeJedUCDotyNLK92dArlJnvFhFfmMad5uLyDbz7/e5d0RRRNd1ZEmkJ+SA0NaAS1V1Rm/FiC3mAYi2uukfDGwQUsrn8/vaRBuGwfj4OO3t7VvEirYjmS7zl1+7yOL1ZexuC8FWF4IgUsqWufP6FPOjSV789eMc3mENGB4eJhAIbAho5ubm6OvbOBd3tMvH8IPtLP90Cu90FpPfimHo6LpGdblMuq4RerCFX3lukHxF5cZ8luV8FQFwxGZ5ejDCsZ7IhsTLtdEY1cks4YhjLcgDEEwSgQMtLI8uc+3SAmcHQ/sKsjdz51oSs8mM7AZDMLA4zDhbLBSXKowOxzh+qnkwXCqV9mWr4HJZ6D7hR0On+1CEF1481tTP7sknexAEuPzTKWLTGQxdx+RQ6HqonY9/9hAe59b9pyiKWxJKFquZZxPDPJiZwvG9/4zbaWV5WWu6jxZFkQMHDlCpVLhy5QqvvPIKHR0dfOUrXyGfrzIzlUESBbr7/Gv78Y8a93zUY2Nj/Lt/9+/uB3m7sLo4NrsZ/uh7I7z93dvodR1X2I4SdqDVVdILeV7/s2vMjKf43G+cwGQSUFV1z4tchQr2QTtPPPgE5WS5MaNlllG8CrVbtV2DvFSmzJs/HmdyOI7FoTD0QBsPPNi+ozJWLpdjYWGBw4c3GqbOzMxsWBh7gnYOn27lWrKEdyGPOWBDdCobnqNXNdR4iaxg4DgZ5cGBjfYBJpOJer2+RXBA1XTujCSwGQamVeU1ScDR5iI7nuLd86Mc6gwgmSQ6H+/EMAxiN2LYgjZs/r2V7SuZCoWlAq1DHVzN1FkYi2Gymzh8unmFcZWdhpqb0XO6hUcW8vzsh8PIsRKd7V4e/+yhDbM+DquJl758lG8rIrNXlmAhj8ksYehQUzUUr4VjL/bzwouDSKKArupkZ7MkbyfJzmRZnl0m5U4BKwI8kyb+34MvYe+I8mXPEFpWo729ndnZ2S1D1x6Ph3/5L/8l2aUsb/zXS1xOVfA5LVtaSUyySMhtJaGXmRpN0Dmf37Wyc5/9sbqZfr/s1PIHEDgQoF6qM/PGDGpVJSDXeGowRL4uUlnxZrKaJBStjN/tpLRcQK2qdDzSgX/wgzEtX1paQpKkDXN49Xqder2+Y+uNosicfKCNti6FfD5Pb+/OcxmrFe+dePeNKRZG4ng7PCyMxHnnjSkefbqNTCaDw+FYC24EQSB4KIjVZyU1liIxmiB5O4koiQiyAHqjomaymPB0efAP+vH2eJFMEpaCm4XRFKVMmRtH3Dz82M5dA7vZKkiShL3dTuSGmeVqnUpdQ9A23lvy5TqWukEk3BD62kxHp5eOHdr21xMKhVheXqa19e6Gsfov/gW5l14iuNIBsTqnuX7GcRWr1crv/d7vsXB1gR/998tMmGVaXNaNM5cCuKwmNN1gLlNh/uoSwUPBLTPs93l/7Jb8mBhP8upf3SQ+mUZTtYaIiEUi3Ofj+c8dWTtnVhVq94KqqkxOTtLd3b2vjoXvf3uYxevLRAf8SOsSTE6LA0fAzvKdJK/+5Q3a/x8P49qkrKuqKjdu3KCvr29D9XA7WwCPTeGzz/bzt4rE/NVFWCpCtYouSwghJ+GjYV58qpc2b+PcPhh1oekGArC0ZGsISUl3k3SGYTA5nsKi6ojOrZ06FrsFk2SQns6QKFZpV+695TCfLmGyK3g8bvL5PCbZhGQSQdcpFrZf/+4lqX3m4RCf/vzJbQ3LoaGW+vRTvZx5sJ3J8RS1ukYw5KCz1bXje0qStOX+9erv/z7HHniA1pUxn3A4zNjY2LYFE4vFQiaToVqt8t5772GSvSTG7CSmMgiiQNuhIJ/7rZN7Eg/6sHHPgV5/fz/5fP6DPJZfSrZbHK9dWeCdv7uN2WbCs66dzWSWsDjMVApVxs7P8lrAzsNPt+6pmgeNfnJd19c2Quvn8CqVyq6LZaWq8s2vXWT+2jJmp5ncfI4fjSaoVuo88XRzhaelpSUqlcoW9c5YLEYwuClIk0SeO96Cquvc/OkU1qUStmQZ2SyhlcvU0hoVXadgV3CeiPDCY910bpr/WA0+NrdpGYBhwJbJ6EZ7O9ValW984xt0dXXx4IMP0vN0D7agjcWLi2uZdovHsqXiZOgGlWyFcqKMbJbpeKSD6IkoGb+F4QtzYM/sWrlNpVJN28q2QzbL9D4apOPIQ9itdiweCxbP1gXG77byG795ijtTaW5cXiAZLyKKIi3tboZOROmIOBEEgcJSgbnzc6Qn0wiCgNVvxdHmwBf2rVUW2ifbuT1c5sSJExRHigxPDxM8FMSIGFtFFVaoJqvkcip1s7RtZtEkiWASiWXK5BfvB3r/o0iny9y4vEixUCXa5ubw0QjyuqRPUyXWdQiCQNaWpeeZHq5+7yom1YTJZSLiV5AUMxig1TUW7xTIpsAasNL5WCeBA4Ftb9KLCznmpjN0dHsJN/G8Ws/09DRer3dLC/vc3NyO8ySbP8NeZ4R2StIBK0JIArJJBBqm5k6nk5dffplEIsHv/M7vbHi8I+LAEXEQOR4hM52hmq1SL9eRTBImmwlnixNHxLGh0hcMOXj6C8fIZkoYYgxJ2rjGrieVKjExlkEU3LS1N7/GZFnGGrbSeyhE/Pwsc/kqmlol5HVRrmkUqipaVaUXga6h8Pu2KmimJpxMpQhvuldEIpEtaqmriKJIaaFE1pCwWuQtwjqruK0mlit1Fmay9MeK9707P2AsFgvlcrnp7+Zms3z7v14mt5jH1+nGvLLvqBSrLNyM83L+Ml/8nTO7XuPrqVQqaxW0/QQW84s5Zq8t44k4NwR5qwiSQLDby/JEihtXl3jo4btKi6VSidHRUY4cObIhkayqKuVyuWmgB41up19/YZCJM22MjCcZHh6jq6uNo4ei9IWcW+6Nq4rF0WiUS5cubXk9Y0XzaLvP7XK5mM9lucdxxzWinR6Wx1IYugun00m5XCa9nMVssRMIN2+vbZaQ2Ss7BXnrcdkUjg3tvWIYDodZXl5e62qpVCokNY3QJhXN1tZW5ufnNySe1nP27FkOHTpEPp/nO396i/xMivCAH13Vmbq8yJs/meDjn27eqv5h5p4Dvf/9f//f+b3f+z2+/OUv7/km+w8Rr9fLrVu3tgR6V87PoVY1gk3EAAAsDjNWr41b783RfdCyp0BvcXERs9lMMNhczn4vczyjwzEWRxIEe71rMsCJqQyXfz7Ng490Ydm0cE5NTWG1WrecA5qmrWTOt8oeO8wynz7dTmfYyeWRGLHxFGSq5IQqjoAdR7uLEweDnOz20+7duqDYbDZKpdKWn5skke4+HxcvLeLKVpHd5oZy32KepFpg8fIVoi6B1tZWDN1A13TCQ2Hc7W6So0mSd5JkJjJNRYUUp0LkRAT/gH9tA/H4M308/kwft2/fRtM0rl27xtGjR5t+r/lclVyusq9sUKFUoOfg7sGhLIkc7PVzsHdr1cTQDRavLDL/7jz1Uh1Xu2uthU43C0wvJXC4XAjAI09+jI997DlMK5nwar7KwoUFnFEno8ujHH/i+JbX11Wdum4gyrv484kiNUNFrbz/ytN9tuJwOIjPxrGZG2IUZtfGLPXiQo5v/peLJCbTCAKIssTtRzp46SvH1ir1qWSWTAJuXbpFaqmAIAqE2twcPt3KwMEgCAY/+clPsFqtfOG3v0BpsUT8Vnyt3RBAMknYIjYGHh3A3eHGZGveGljJVpi8tMh3/vO7FJYKOKMOPvXV07QfjTTM3IVGF0O9XkdRFCYnJ9dEV9aTSqXwer173gjuZ8O4unnYTkDp5Ll2ZkYSZBbzOFttBNsE/s2/+TdYrVYMw6BerlOKl9BVHdEkYg/ZG50VDoXQ4b0rva16Oc3MNFRFN88p6brOa6/c4eIPx8klC7ztmaXraJhP/epR7JsEtyRJAgl6n+5B03Sc15YZz9fJU0ZAwK3qtDjMHDnTRsfDHR/IfOBqcL36WttVjiORyLYtuLVSnTrsaGotigJIItWqiq7q2z7uPvfOdufDez+fIj2bpeVwCFG6+xiL3Ux4MMjicIz33pzhE7+yu0omNJJOiURiS5vkXpi4k6SSqeI7uP2eSTJLiILA+Eh8LdBLJBIsLS2tia6sZ2ZmZtd9rlmWOBh1cTDqIiItcexY564BkSAI+Hy+DS3XgiDQ3unh2vk59GJtQ+smNO7ptbqBqUXBLr+/SO/UI11M3IixeCuOPWCjVq5Rz+qEjyp09zSv2t+LrcLqOv2LYrXLC+D8+fO8/fbbayI66xODVqt1zT6smV6Cw+HA4XAQjUYxGbNY3VVkRQJFQjZLpOM7e8h+WLnnQO/HP/4xwWCQgwcP8rGPfWzDQOUqgiDwB3/wB+/7ID/qrAYOq9nheKzAwmgCxy4qbO6QndjtJLduLNLSvnOgt2pOuznbrak6CA1pdlVVd2zNAshlK+h1fYPXi9WpUMlVKZbqa4GeYRiMjo7S0tLSVCRmswLdZiwmiQd7/Jzo8DCVKJGr1Hnzrbd5/JFjtPtsBHYxKt4sJrLKuQfamRlLsXR5CctSAU3XSWllCs4EjxzpIjazxNylJX5w/V2KuSqSIBLu9jB4soXw0TDVXJVKpoJaVcFo3BAsbgtWn3XL5nkVwzA4ePAgy8vLvPXWW5w5c2ZDNvD737nJuz8YxeaY5uyzfTz2zP5vXveCoRvMvzfP7FuzmN1mfH2NG0muXGchXWYyWSSeyWGxVBqWHUJDCrrLbyfqsWBxmlEGFLIzWerJOrettxk4u9E+QpRFTJKApu+8sarrOoog3FfE+wWQX8iTvJpk8vIkbocb2Srj7fESORZZq8icf32S+ESK6IEgkkmkkC4z8vYsd063cuBQiPmZDN/62g0KyzXAQHGYMQyD+HSGkfNzdBwJ0X5IJxaL4fV6mVueY/DgIIHBAJVMhVSiiNOpYDKbSBQSBDqaK/DVS3Vm35lj7MoiN68ssXQ7gaFIFG6n+Ls/ucrA4AJ9QyE6H2zn+vh1RkdHOXfuHL29vVsqa7quk8vl9pVo3E9Fb/3moRkdnV5+8395iPhyAZOi8q2/+QZms5lzJ8/xgz/7Ed/7/77L4lyOuqpjNkm0dnk49EArbUcjmPYoE77KanDU0dHB0tISpVJpbXbo+pUl3v7OKBoq0YEgtZLKrZ9P4/RYt91YW31WDn5yEFurhfaRJLUyiIKAP+okdDiIt8f7gQmaBINB4vH42vFu9/3bbDYSiUTThKRiM2ECijsEcJpugKZjtin32zZ/AdRqNSYmJrh06RJ9fX1rSc18rsrYlUUcIduGIG8VSRaw+azcvjhP+YWBXa+/dDpNqVTacF1rms6rfzvCwnSahz/Wv+N8bb2mIWJsmYFtdlyVcuP6npmZQVVVjhw5suVxqVQKj8ez63zsZvaaJOnp6WF8fHzDbO2xQyFutjkpzORwtLvXtAwMTacyX6Bkk3n4iQOMXL9CPp/n6aef3tY7cyc6u7x85h+f4q0fT7A8kQKTweNfPM5jT/cyNTVJa2vrBuGccrnO669MI9SXOPZgO8dPN6+MiaK4IaGz33X6Xlj/fb/55puUSiW+/e1v83u/93sbWoU7OjoYHx/f0XsRwBt1kpzN4AzZ0VUNtabh24dS+4eJe17J/9N/+k9r//7ud7/b9DH3Az2YnJzkzTff5JVXXuHZZ5/l8OHDlEp11JqKw7ezd4tkEkEwyKYLDB5q2/L7y5cvE4lEKJfLRKPRpq11S0t5RFEg2uJC07RdK3rhFhcmm4lisoTdb8MwDHKxIqHBADZL40JqJrqynkwmg8vl2jWohEYWbHClnSN2kzUz0N1Ybd/cfLGGXBa+9JVjXB4KMz6RxGyS8VamUXNJxi+NcbL9OW5emAfXMpgldM3ANJHkytsznH2im2PP9e+rtXC9zHA4HMbpdPL2229z9OhRPB4PszNZzn/3FmaLmXqpzvnv3+HgsSjBXRaMdDq9r5m+ZsRvxZl9axab34bFa6Fa17g+l2U2U6ZcU7EpMg5FwuM0Yxig6jrJYo2lXBmH2UR/0MFAxImny0N2Nsvka5OEWkN42u4elzPqJBxyMDmfpVTTsDVp36yrOtR0wl4rzpb7qpsfJNmZLGOvjjE9Os18dh5DMnj83OMsXV4iv5Cn7/k+7EE7C5MpLC4zkknEqGtYJZFcrMjEW7MYSwVe+eZNkrMpIkNRzG7LBpnwSrHG+KUFpqdVPvXllxg6enBtUyGIAgVNZy5WwFZSGDreglBqvsGp5qvc+v4d3n1rhmVJoB6xImQ9aOkyYtRMvsXBpVSBqVdynFjMcyXxHjkjx+joaFNF29nZ2V2tBjaz3wrV5g3LZlwuCy6XhVgsxpEjR7j2zjUufOcOduMIlydTiB4rJlkkVVNZHI1z+1aMMw938sCnD+7JW2+VRCKx1qkRiUTI5XJMT0/T2dnJ6PUl9JqGq92GYjGhWEzUinXuXJyn9snBbdulFLuCudPMg+ceoF6uIwgN8ZXdNsj7xWKx7FkKv729nampKbq7N848e3u8RKwmhmsqum5u2r6ZLddxVDRaD3qw7aLofJ/9s7y8zPDwMKlUimPHjq39vFCoUivXceww5252KFQKNUqlWtNrcDVpu3qebG6tm5/NcunH4xQWiwiCsGOgZ3MoGCJoNR1J2T44U6saTq+VkZERPB5P0yqVYRhkMpl9jV2sPm+vhMNhLly4sOFn3QE7D704yJvfvkV5OotdFhAEgYqqU7abcA05iN15m6+9/NecOHGCRx999J4CPYC+/gB9/QHK5Trz87P09TU+64BzgJmZGUql0tra884bU4z+dAGzxUxsJkNrp6fpXma1ZXsve8D9EI8XmRpLEmlx0d7p2fA7v99PIpGgra0NwzAIBoN86lOfajoPGggEiMfj23a/ATzzyUEKqRLxqTSiKNBzKsrDT+0cHH5YuedAT98lg3+fBibJRO5WjvREmnpLnYK/gKJISLJEvaahbNPaBI0ZEMOAWr3StGp29epVvv/97/Pbv/3bTYM8ALtDWZPs3UtFr7fXx+DD7dz66TTZ+Ry6AbaQnUee7+eVV36A3W6nra2NQ4cONc1wGYZBPB7f1Wrg/bJqxtuMgNPCxx7u4mMPd6385BiFWIH//m9f5dpYAmtfCK/t7vdSrWvEUmV++oM7iKLI8RcH9rzZSSaTBAJ3qxc2m41HHnmEixcvEgqF0DUnal3DHbQ02hwrKvXqxvbFuqpRqaobqmLpdHrfN5f1lJIl5s7PoTgUdLPE9GyWqVKNuXQJr13BZ7OBAHUTqLUaZosZWZKwmCR0vaFid2UuQ6mucazNg7vdjVpWufy3l3nsq4+tZcytPisdh4NMzmSZyVcQnJYN8wg1VSeWKxOtanSdCtwP9D5AdFVn7vwc9VIdc9RMZilDNBrF4rVgdptJ3UmxcGGBvuf78PhsxC8uUs7V0Et16oUaQrJM6WacN34ySWokgd1rQh1JopskRJeCHLQjecxY7Aqhfj+J8RSFlG3LhsLrtWKxKbR2eHY81rHXJjn/5gwLVpGAx9o413w2qmWVqlrF47WhagaxXImfvj2OTRZ54ktP0H9k61qSz+ex2Wz3tJHYzyYsEolsmP3YjlAoxJOPP4l5PsBPx8cwWt1EXJZ1AYmC7rGynC5z/ufTmC0ypz97aM/rzObuBZfLhcViYXx8nFql3hhCXodoEtBUHU3b/bOKsrijP+teuXFtCatVpncbP7VUKoVhGLvOgXo8HtLp9IZWL0+nh84eP4vDyyxly4RcW1U3q6U6PRaFlqPh+xW9XwDt7e08++yzfP3rX98QiJktMiaTRL2iYd1mea9X6phMImZForRp36BpGl/72tc4e/YsnZ2dTVVjfQEb/hYXAgItuwgBHTgU4mdBB/lYAU9b8znNaqEGJhHFmqelpWdb66qZmZl9tymusp928tUgZXUvIQgCTwxFCfqsXLy8yPJUBl3TcQRsPHKyhaClzH/4t3+GrussLS19IPtxq9W0YWYbGtWvRCKx1qGVSqaRJBmTzYRa07bsZVZZtYtSFIVsNrurNdheKBSq/OX/9R7LY0ncUSdf/N2zG2aR7XY7iURirQr5yU9+ctvWX7fbzcTEBH6/f9tKbUubm1//Z+eYm84gygJd3T7MH1Hblo/mUX+EkFISA5YB3iqfpzhXZOJHE/R/sp9gp5uF0QT2JgIbq+QTRWweCzZnEafTSaFQ5ec/GieXKeP0a9y5c4f+/n6mpqa2zUx41hlw7qWiJ0kiTz/WjbpUZPbqEqIiEm01EfXK/O3wMLVajS984QvbXhyrWea/DxRF2bOC1/ilRTI5EUeHC49tYybdbJKIhhzMazmuvDVD3+mWLX5vpXINs0neIBMNjYBsc1AriiJnzpzh9u3b5PNJWg/7SI6VQBA4/HA7kZVgJ5ev8tZPJ7h1YYF6oU66lMJmGuPBR3dW8NwNwzBYeG+BSrqCb8DHzEyWqzNp8rJA1GtbGwQHMCkK+VwOs+XudyiK4LGZMMsio0t5ZElkqMWFp8vD3PU5Jt6ZoP+Ru5+57YE2TsbLcHGeRa1MyiQiiyKaYSDWNaJVg+OHw3Q/2nnfWuEDJL+QJ7+Qx9nmxGv2UigUiGdyQKPS5mhxkLydRJAEArka06kKyYU8ktWEjkD4WJiOE2HG/iaNpdONpBhIJgWjpqHGSqjLJUSnginqwBSyIdtM3Dg/y7nHuzGtC+ZlWVwzVt6O7GyWmxfmWbKIhL02TCvXkShLWJ0Sck2gWqlitpiJuO0s6BAuu+hydTVd2+Lx+D0lQvbTugn7UzTNzma5eXURLewk2mRdF0WBiM/GfE3l5sUFDjzUsWGdyeUqZFLlDR6JO6EoCj09PdwIJCmXy9grZnBCvaJSiJc48mT3mg9WtaoyNZ5C1XRkpaGml8/n9+xJthcmR+OYbaamgZ7b7ebtt9/m3XffpaWlhU9+8pPbClt4vV4mJyfxeDxrm2WTzcTgU92UizVuTKWIqzqGSUAUGhVXW9WgX5Y4/kgnwYPbZ+nv8/5wu6I49DP84b99i2e/cITBAyF8Phvth0LcfnsWV5NKqqEbFBMljj7dg27UtpxzN27cIJFI8Oabb26p5K7icJj5tf/7g6SSpW2FhlbxuCwcfridd//mFkrKhG1T59SdW3cQi2acPVY+9txpnPbmSfJCoYDZbL4nf9L9msL39vau+betIooCQ+1eDrd6yJTr6IaB0yJjliXeeecdHnjgAS5evIimWnnlO6OYZIXegyGOHN1exKRQqPKt/34VxSzx0q9ttDjYTnAtEAhgt9sZHR2lvcdK5lQr6eUCQw93bPEhNQyDdLZCrW6siTCl0+kPpG0zES+SXszhCNrILeVZXsyvnQvpfJUbVxcZvjGNy5Xh2Wd/l+OnB9F1Yy2hv5nOzk6mp6e3PecAnE4zB480X6c+StxzoCeK4p4yFpsVt/6hkZ/PE2oJ8Wj7c5i8LgoLeWrZGkNn25kfSVLMVJoGe2pNI79cZOipHkpcw+l08jd/eo3rP5lEEEAwG3zpd/8pR47uPdukququ5f3cUoG/+2+XmRxPo7sbczq5iwnGh1/GO+ji0NmD21YPi8UiJpNpW1PwD5qOjg5mZ2d3Hdiu5qvcvrxAxSrTam1+bIIAHp+NpZksM8MxDq/bgNVqKgICtbqGdVOgt15kYDMDAwOMjY3RPrjAo8+eQVFk+voCiKJIoVjjL/7oArNXl7A6zSg2E9VklZ/82XWmxmM8+6l7bxEoxoqkx9M4WxuKm1kBMoZOq8e5IcjbDasiYaAwupQj4FBo8VjxRX1MvTdF9wPdyCs3CYvbwsEX+3H4LEzdjLMcK1BVVUyiQMBvp/tAkPZzbTjCH9ym8j6NeTdDM9ZmqSI9BzBn82RKdTw2E/VSnfkL8xSXiwR7vTz8pSGmxlKUC1UCUSf9B4IszuWo5qu4W93UahUEk4hgEhHtJgxNRy/UqY4mUVNlHAEr6YU883MZunr2Z5ewfCvBQqaCOWBZC/LWsz7hIIrgcZiJl4pMXVkkdCS0YV5sJ9W03dhvoAfNpbubMX5lmWSxhifo2eH9weNtrDOzw3EOrVtnkvES5XKNWk3dsAHb6b0FQeD5T5xkfGSJ9ESRwnIJQRJpGQzw+HONZMzoSIwffXOY1GwW3TCQ7QLPflEm3MKeqhWaqnPl0gKFXIXufv+21goPPNq1IQEAoFZUMtMZUmMpqleqGCMGvrAPn3N7rz+gqa2Lt8fLic8cwH9+nuk7CdK5KpqhYTVJtPa46TwWpuV0C9L78BW7z84MX1uiOFujaCS5/NYsgwcaLZQnH+lg5mZDWM3f6UVaEefS6jrxyTR2v42TD3WSz+fXAvyJ8SRv/HCc2dhVDg8dprOzc8ekrcNhxrHL7P4qzzw7QCFbY/SNKbJLBexeK4II+WSBxHQOfCr/5B9/ZdsgDxpq4vciBnMvhEIh3n333aa/E0UB3yZBlrNnzzb+O/MUf/1Hl7n26hSCKDL81izqrx/fdnZueTHP/HAMySyRzVQIrvNCjMfjG2xr1mO1Wunv7+f111/nC199AEWxbGkJv30nwc/+bpTETBZd1Bk4HeETnz2+j29hZ1paXbQfDjN3M0Z0IEBPvx9V0/nRj8e59rMpiksF9LqKJMYxBIGp95JEDwd58aXDRAJb20slScJut5PL5T6QiuOHmXsO9P7Vv/pXWza4mqYxNTXFyy+/zODgIJ/4xCfe9wF+1FGcCmpZxR9xQ66KyWpCtsqcOtvO7ESKmz+ZpJyt4I44MZklNNUgnyhQjJdoORzised6ePWHN5BlmeWZLBangq/Dw+JwjFJhf8eiquq2Qdoql382yeREGlefF9vK5ioXtJMZNXj42BOc3MZiARobsIGBgW1//0FjsVioVCq7Pq6Wr5FKljG5zLBDnGM3y+QkgeWZHOslDBRFRlP1LdW89fN52yGKIs888zQXLlzgyJEja69x8Z1Z5q8tE+71Y7I2vmdHxYrb4WD0zRlOPNhDdA/qwrWayq0bMTq6vXi9jb9tZjJDrVzD5XBRrWvMZsq4Xda7rU6bkLfxJQSwKRLZco2ZVIkWjxVb0EZlosLIOyOEBkNrLWQWt4W+5/poOdVCfiGPVtOQTBL2sB1H2PGBz/3cp9FyZ9BQjxUlEa/NhKrbscsiiZEEiZEEaknF2+fFHrRjByItW8WaDAEEcWvmU5BEJLcZQ9XREiX0TAVNEqnuUzm1kq0wNRwjZxbx7yBCYrFY1qp6DkUma5WYncpwaLGAp8vTeK1KQzhorz5cm7mXQC8cDrO0tLRrcJmYz1JXxF0NjG2KTF4SiM/nNvy8s9tDuaxu2UDF4/ENmf7NmM0yn/3yUVIJnanxGF09rRwcCmOzKmQyZb7/Z9fILuTwd3oRZZGFsWVe+8sbPPa5TvbSlfaD79zi4g/uoNU0XFEnv/JPTtPVvTVQ2yydX0qUmHxtksRUmmxNJVUQMSouvKkgIy+P0P1UN45I8+SPLMuYzWbyuTzTt6bpaO3AFrDh6fTganPRuZCnnCxj6AayVcbV5vpA2k/vszOhqBNnyEG9qtKybpZ98ECIj335KD/5m2GWRuINr17DwNAN3FEnz3z+MN09Pqanp9cqZLdvxBh9fZL+R47w2c+e2/ex7HQdmxWJl754hJuHQ1x5d5axN2ZQS3Wy5hy0xnn4sUEq5STQ/Cb7fpJJq+x3HnhVtGinubHNLC/U0bMGLYfDiJLA4kiCa+/ObhvodfX4eOKLRzAr8oYgD9jRSgYaFb/Dhw+TyWRQFGWDkfzycoG//ePLFBbzOCMOSrkyl783gSiIPPb03ooRmqrz7vkZZFnk1ANtW45FUWS+9NVTLC3m8QfsWK0m/va7t7j83TvYzBLRXh+iIlHIF7Db7VQyZWbemuWbmQpf+MenCPm3BnuhUIi3336b27dv85nPfAa3+5fT+umeA71//a//9ba/W1xc5MEHH/x73fR/WIkej1JOlMnN55DMMi0PtmJfGV79zJeO4Q85uPbmDInJNOgGYGD1Wjn+bB+PP9+PrpfWrBW6Dga5OJVh4cYynibDqLuxW1ZarajMjCYwnMpakAfgsiskrCbGryxy4pnepgvYwsLC+14Y74XVYM9i2cW2YF97u60P3hzkARt66rd9JcPAarXy8MMPc+nSJfx+P93d3QxfXkA2y2tBHjQyd1angl7XuT28zNEdWjBWufDOHK//5XV6T7bwxf/pVGN4fDKzphC6mK2Qq9SJ7GDrYLVYyRfy21Z73VaFxUyFbLmO22oCAy789ALx83E+//nPr7VlCILQCCg+ospUHzUcUQdWr5Vysow9ZMdskmhxWUjcSpC6k0LTNHx9Pmy+7UUSTIqEIAhUK7W1Cu1mBFlEDtgox4sI8SLlyQzGwdCeNzJqRaVcqmOYhB3l8Te0Ea9YQFSrakMBd4WFhYX3Nbt6L3YBzbzgNrPf4LEZoihusUMA1iwmtqNWq2G1Wjh1JsLR4+2NzfTKMn/rxjLZuRyhgUBD3AvwdbhIjeWZnyzCQzsfk6bqjLw3j8lmItzvZ/7GMnduxZsGehuOqVhj4scTzNxJMibqpKsqVV1APniQq7pG/0QKQzcY/NTgtmrGlUyFr//Lr6OUFc6eOYvVa6X1wVYCgwHc7W7cu7Tw3eeD5/BQBOc/e5BqVaO3b+M5cPJMG919fm5cXmRpNosgQrTDw9CJaFNboeMPtKFpOgP7sBrZDyZZ4vjxKCZJYPrSIoaqMzDYxe/8i98gkUhsK+RUrVbRdX3XpPhO7NTpsx29vb2MjY3tK9Bb9dtbn8A2dhjXkySRc4907eu4VonH43R2diIIAqlUas3IHuDmtSVyCzmiBxv2GmanCV1Pc/2NaZ79xFYl02bcHonz2p9fQ5QlAkEH3b1b1xhFkdc6Cq4Px7j6w3FcbvOWhJEgClh9NiJ2M4vDcX74yh2+8uXjW15vbGyMt956i0qlQiqVuh/o7YdoNMrv/M7v8Pu///t86Utf+kW8xUcGs8vMwCcGqGQqSIq0wfRalkWefK6fBx/r4s5onGKhhqLIdPd68a1kHyYnl9cCvWc/dQB/yE4hX6X/UIjoPg1hd5vR2zFDZlbI53NNf1er1ajVahtkeP++6OjoYGpqasekguJQ8HqtLCTy4LZuW9Ur1VTMmk6obW8XezqdZnBwcNvfr6+SiaLI6dOnGRsb49KlS1SLtS3y5YIoUq3XkE0y1eLOBuyrRFqc+FrdtPc1WulqhRrVfBVlZcM4nSwhi+K2feqNN4adnFdtJol0scpitoLbaqJUK5FfyFNylZi9NEvIGcK2g+LafX4xKHaF8LEw0z+dRhAFLD5Lww9ytDGXJ5tlvN1ehG0quQDhFidWl5lCpkwg6tnx/SqqgTvipHwnReJWguChD34WyryuqgcbUy6xWGxDFvleuJeKHtyd1dtu/RQEAX+LE9ONGJW6hmUHMZBSTUXRDAIfkDBRLBZbE4sxmUz09fUxPT2Nx+OhWlbRYS3IW0UzVCRh9wqYJIvY3RbyywWypgKiJGHbQUBslcxkhsREmjuCQbaiEnJZkAQruXyefFVlxCRhmckQGEvS0mS+09ANrn/3OkbCoGgv4u50U1gqMP36NBaP5X4b+P9AtmvdhYYw06NP7S0RE4k6efGlvfnqbWa/gZQoCoiiiM1qw2q1Uq/Xt32Nubm5XaX393J8+yUQCHD+/Pl9fbbBI2GuvTHFwo0YoiSg2BWOnNl/wn0vJujrO5h8Ph82m43R0VF6e3uplGogCGv2GqIoIJklqsU65VINR5ME1mZ8ARuusAPJJOLZRZEe4OqleYySimPTTLMkiuiahihJSGYJT9DOzOUlFp8tEA1sXDf6+vr4+Mc/zt/93d9x49oMDntwS6Xzl4FfmDKC3W5ncnLyF/XyHykkRcIesm8I8tZjtZo4eryFc490ceqBtrUgDxoD86uBnqLInHusm8ee6SOdKPGnf/ge/+e/+Sn/5f94gx997zZLi/kdj2M3w3ST1UTHYAChUKO0LoueL9UxVTQ6DoWo1Wpbnvf3KcCyGUVRWFxc5Hvf+x7FYnMzS7PLTP/JKOaSSr7SPIAyDMikSoR9Njo/oAxjM/Pfvr4+Ojs7KRs5SpmNpu8iAoV8HkmQCO5xE9jT6+d3/tfHeGjFVLlWqFEv15GtMppmUKiqWEy7X+aiKG6v3CWALIoUVr67UGuIs0fP0lHrYvg7dxj9/h3q5b0Fpvf5YIkcj9DxSAe6pjP39hxzb8+h1TQUh0LkWARHy843LYvVRNtAALWoota3r1rVKnVQdbpPRlHsCjNvzlDco3msbJGx2kwIdYO6trM6XMMfswoG6KqGxSwjmxtBVqVSed/iIfca6K22b+5E77EIPpuJdG6HdnIDspkyEZ+V9j2IhuxlNrBZy1VnZyfVahWTuY5slihm7x5TvaaBIRDeRpFwM89+/jDBXh+CAYce6+DUg7u3YiVGEmR0nXSlTshpbcwGCw2vvpDLQq5aJ7PyOL3JOVFKlAhZQjzx6SeQXU6W8mVcbS4quQrZ2eyejvs+v7zstpdZz8HDIZ76whHOfGaQF74wBEBbWxuTk5NbbA322zq5HZqmMz2ZJp1urgy+HaFQaM9WJADtHW5e+u3TnPx4P0ee7OaF3zzBqbP7s5yB3buTmq0xFotlTQzQ5TMhSgLllbXPMCC3XMTb5sK7Q0fJesIRJ7/9/3yMf/y/PLI2hrIdqXyFuesxnE0CQqvVSql093u3B2xUUiXujCSavtahQ4d47KHPc+kHSf70//M2seWd99EfRX4hFb0bN27wH//jf7zfuvkBkM/n8fvvCh/E40W+/SdXmB+Og2FgspnQVZ2Z68tcfm2cRz59kHMrm/7N7MVe4fijXczcSTI5niZtlzF0UEp1Bo6Eefi5I1y/fp3l5WVefPHFleNpzI+sz0BV6hrj8QLxfJVKXUORRDx2hd6go9H6tw26bpDLVZq2eWzHd77zHc6fP4/X6+Xpp5/e9nH9p1q4fWWRkZkMaosTt1VZkz6vqTrJVBFnQeXY89vPjRiGQbJYo6rqSBgYOw38sVUSfRW/38/znzrNn/7711kaixPuDiBIQmNxnC0R6AwydGJnFcPtMDQDQzMQJZG6ZqDpBtIOFZ1VbDY7xWIRh7P5ZxdFgdqKVLsgCgiawGDPAcr+CtV8Da2m7dsE+j7vH1ESaX2gFWerk2t/co3AwQDuDje2gG3P5vQHhyLMT8bIzOdxhOxY1lVsdF0nHy9RSZeJDAbpHQhgUiRSd1LMnZ+j7/m+XaXsLW4LXYdCjExnyJXr+HcRVFAUM2NTM5DTaXvsII6og9nZ2Q/McDefq5FKFjck1HZDkqRdJcw9nR4OHouQeXuGmCwRdFlYn5jXdYhlStjzNQ4+2btF2bcZu83n7UQoFMJstnDt3TmWh3MUE6WGOFMyS+vhMEd3UUpdpafXz+/8vx6jUlWbtpZuRld1qoUqVUkEjA2ed8JK94AoipRFA7WkotU0RKu45TX0uo7L7eLcI49TrNYx9IYJtl6/b+30y4iq6ui6vq3v43r249EmiiIPP7Gxynj16lVeffVVAoEAp0+fXnvNQqHwvgM9VdV5+8cJ1OR5nAE7n/7Nk3T37NzqvEpbWxuvvvoqNpuNl156aU+Vva5u366t1Lux23xeLBZrqpIriiJ9fX3I8iyRw16WbmZIzWQbDUJWg4+9dBR5H0rblj3es8plFa2qYmp2LxEFjHW9IIIsIhhQKW2fjC4V62hFnbJcJZ+rEgr/ctlA3XOg193d3fQkzGQyZLNZbDYbL7/88vs5tvvQCPRWNzilco2Xv36ZuevLBHt9Gzz4dM0gNZfltb+8jtVqajqMuxd7BXfUyaf/8WmuvTXD/O0EgiDSdSTIkXPtvHP1Hd544401I29d18lms2vKVKWayuWZNNfmssSyFYx8DdFoXHK6zYTXbeFQ1MXJTi+BJhdofFllejJFa5t7z5uw559/nlgsxuzs7M6KXWEHH/vVo8h/dZPxiSRLJhHBLKHrBnJJxW8xceZjvRx9rm+LcIiuG9yO5bk+l2U6WaKm6eiait8uU7KmORR17diqpao6mqZv8GA5caKd3G89xCt/9h4Tl2ewWK3kcwUcUQfP/eoQ0cg9LjRCIxAzjIa4hig02qB2fZooYOzQ3K8bIK98LwYNAYSO0y1kpjJ4e71Y3HsPzu/zwZMaSyGIAq1nW/dtYWFzmDj+UAsTw3kSMxmKiSKiLKFWVSrLRajriDaZbKzIOz+f4tS5DlwdLpKjSbzdXkJHdq+Ahw8GaHlrhuFSnbrF1FR5E8DQdcbGx4kV6hwULXSfiJIv5TdI7b8fRFHk+oU4i1MCn/3S8X0917SDaBGAZJI4+6kD1CoqVy8vsJgpI3utmCSRal1HT5dxqganz3Vw4uP9OwoU1Woq9bpOpVLZcT4vmUyu+Y4tLuQwm6UNa6fb7eLX/ukjfO87l5i/ncNqsRE8KPP5rzy8ZwVDWGnhlPempixIApIsIRkNH1gM1trlbTYbpXIJXRcwGQKCLDSEOzZh9VuxBWwUFguEOtwYhkK9VG/Yhtxv2/xIUq/Xm+4/4vEib702zvjVJQwdOg4GOfdUz442Cvup6DWjpaWFYDBIOp1e+9nMzMwH0pm0tJgnOVEiGPGTms4wemN5T4FeLpfjD//wDxkfH+fIkSMfyHr3QVEqlYhEIozcjPHGK7c580TPBkudrq52Pv0lC++dnyIVrxBtDZEvTXPkyB4U5e4Bk0lEkMRtZ6cFQWhsWsS7HRybFYHX88DDnei6gd2hNJ0N/Khzz1fK448/vuVEFAQBr9dLb28vv/qrv9rU+PI++yOXy621bt64vMTCcIxQvx/TpsyHKAkEOj0sjSZ457UJho5H1wRE6qU6+YU8mTsZMq4MzqgTxbH9TdsVcfDIS4fQ6hqCcPdG/NBDD5HNZrly/grldJnF9OJaEJot1/ne9UVuzaRxZqsEY2WMVAU0HUQBwalQCdt4I1FkMlHkhaNR2r0bS/puj4RiknG79z4ErSgKn/n8l/i//uzbfP27tygUaiiKSE+Pj4PdPiIuy92+8h4vL/yTU8zfijPy3jylfA1BEoh0uDlwuhVfj3dLdULXDV6/HeetiQQYEHCascgSqq6TLNT4ztV5xmMFXjwaxbYuE5nJZNYMf+t1DV032ByHPv5kLwMHQ7z6vffIpPL4JSfPvnCSA4P3Vs2Dht+UZJZQKyqKXUExiRSqe7M4EQRxbXHcjKbpWFe+G7WsYvFYCA2FCB/96HvMfNSpZCokR5PYw/Z79im0OxQe/3g/8cUCs5Mp0rEiiyMJRLOEq8+H3WuhUqwzfX2Zek3j8Wf7MTlMxK7H8A/6kUwS5XSZcrJMdiGLS3DhiDjWjsfV5uLQmVZSr42zkC4R9FgxN0mOaIaAPdBCdOwOdkUlO5ulZC/Rd+CDkzkPt9npaN9/1j4cDjM8PIymaRw7dqzpY6w+K49/+ShtA35GLyywMJ+jrum4TCKtPX4OnWmlY6X9tRkz02kuvT3L+NUl9LqGaIUHntQ5da4NWxNrmGKxuNbxkUmVsFiVLUkyp9PKF7/yMOfPn+fAgQOMjo7i9f7iZmoFQcDX78M1lcGumBp2H/ZGcCyIIrlyHYvFhksz8PX4mlaEZbNM24NtTP5kksRIAkFoBIWR4xHcHXubob7Ph4v1YyirZDJlvvlfLrA4EsfmtyEIAtdfn2BhPMUXfucM0Zat7cUzMzNcv34dVVURRfGeDM1bW1v59V//df7Dv/0LvvG1iwwe99Pe4dyxqrVXPF4LFrdEIV5CsSv49ihO5nK5+MQnPsEf/uEffiDiTntlL/N5q0xPpJi7GSPY4trinRoJB3n2eRdvvfUWp0+3cO1a81bJDwK/24q/20P82jK2JtYJNquVUrmMzW6jmqkg2RVaOzzbvp7drvD087+8HYj3HOj98R//8Qd4GPdphqEbFAqFtbmU6+/NIUjiliBvPZ5WJ7HJNONjCXp7/CxeXiR+M04pVSI+G2d0fhSr10rgQIDoyeiO7V2bb8CKovDCsy+QOV/hrT+/xIEXu5BlmXJN4/s3Frk1mSIyk8eYy2MoIpLHgmASQTPQclWU4STRgI1l1eC7wK+cbCXkvFsJslhFBg9trA7E8hVSxRqqZiCJAm6riajbsjZrc2kyxc9+PE59wsvMldvIgAbMKlO80+Pl6EPtPH2yba3iZvVa6Xuog+4zrWi1RiArW+Vts2eXZzP8fCxOyGnZ0HaqIGLzyVTqGtfns1hMEp88dnfhS6VSa+qA1h1aGqMRJx97/gCSJPGnf/qnyFLzDeReMbvMKI5G9luxK3T47FycSoFN2dFaonGc1oZpqn3jol9TdSRJILzSUlsv1QkeCH6oMo7/kMlMZahkK/gj+/O2AygWiuTzeVStMZMbanESanEyfHWJ2EyWQL9vLVizucxIskhsKsPiYo5IyEF2Jkt8OE4pUSI9lqaar5LNZslfzuNqdRE5HsHX19jM9z/VjVpVefetGZbjJTSrhMNiQpZEdL0xT2qU60TyGt62dnLWOpffuM0zg9tLr2uazsJcjrqq4fXZdp3tEASBrl4nR4/uT7DAMAz+6q/+itHRUQ4fPrxtoAeNa/DQUz30nW2jGCuiq/qa1chO7c03ry/x/f9+lXysgNVnQ1ZEyskqr/3JFSZG4nzuH53YsXWydyCAvE2lFBqb22KxSDb7i59x8/X68F9bJnBnjiWbg/lMCUWSqGs6qm5w0CQR9Nnx9W+fDPb1+bB4LGRns+iqji1gw93hvudkxn3+x1IoFLb4tF15b57l2wmiB4Nr+w132MHCrRgX3prmk58b2vI6giBw9epVVFV9Xz538VgFIRXk+q0xZicX+Re/v70dmKrpZMp1BMBjU3b0o3U4zJz+WAibqRV/0MGJfYijnDp1inw+z88v3uD6fBYBCDjMhF3mD/x+u5ApM5cuE1te4vSBrm0fVywW1/ag5x7vwuWxMLiNEJfZbKa3t5fLly+/73nqnZBEgaHTrbx6bYlavobi3LguipKEpmsYukF6IU/L6RZ6N4m2/EPiFzKjd597R62oZKYyjL45yu1rt5lfmOdd57sceOgA2aXCtlLUq1gcZlLVLOl4iamZPEtXl7D6rZy/fZ6lpSXGUmM8cfYJSm+UqGQrdD/VvUX9cSckk8Txc8dYXFwg2hoF4OZClpHZDJHZAvpMDlOrA2F9kCiBHLBh+Azq83kChsGSCG+7LXz6+NZFUNV0JhNFbi7kGIsVKFTrjeEOAyyKSJffzlCrm1yxxo+/eRNpKkvQbUHu9iCYJAzdQM9WKY4meXepQL2u8+K5zg3S7pJJ2nW2qKbqXJpOYzPJ284WWkwSLR4rt5ZyPNDtWwuG9oOqqvj9fo4ePcrly5fxeDz3PJcjSiLOFifL15axB+20ui2MmGWKNRX7Ln9nSZZQS1s90rLlOkGHmYBDwdAb3ki20H2VzQ8DuqoTG45hvseNgKZpnD9/nlqtxvHjxxkYbGQ1k/Fio/V306babDNRTBRJJ0q0trlRyyo3/+ImFq8FxacwlhmjUqlQLVYRlgQKPyjQ9WQXocMhzE4zh14cwBmwM35lkbm5HIlskYokIhoGIQFaw046H+ygHC8xPZHA6pQwb9NiePXSAu/+ZILYVBpd1THbFfqOR3ji4wPbtn7f62ZJEAQeffTRRou4yYxaVZFWrCm2Q7ErKN17a3csFmv86Js3KWXKtBwOrbV1uoJ2qsUak+/N8WaHm2c/cWDtOZtngPcy26TrOh0dHUxOTtLV1fWBbB5rhRq5uRxqVcXsNONqc2H1WxG6BI6oUSLJMhmzTMEAmwBOScQpaXQ82rHrnKItYMMWuL/W/DLQbA5sdjyJqMgb7sWiJGBxmJnZRjyjvb0dt9uNYRgcPnxvqp0ADoeC1W0ml8zT2tG8/VzTDa7NZbg4Gicxn0MQGh1ApwaDHI66tr1+fH6Fhx8e2PMc4SqxfIVluRPZZeOv/+tFEAUsITu9h0M8NRTddb55r7wzkeRndxIUqnU0TeN2fpZnDoY52ubZ8thVWwUAl8uyrQYE3P0bd3R0MD8/z+LiItFo9AM55s3vAzEiQwEWLiwT7PRg3jQ+otc14neSmEN2Hn6md8fg/Jed+4Heh4hKtsLkjydJT6TJFrNcvXKVfDbPD7/2Q1gGI1XG2EXwQl8RzCjP51mez+PudGOymujv72dpaYlgMEiwM4haVYnfjONscRI5tvc+alEW8R63MfjsQ4iySF3TuTqXwZKtYsw2CfLWIYgCplYn9ZkcvqCNO7GGYEtwndFtpa7xo1vLXJnNoFVUfEUVV6aCUdUQFYma28ydQo2rsxmyN2NEpwt4uzyI6/zoBFFA8lpwuhSkqSxXXxmjt9PLULtnz58TYDpZZClf2dJiuhmX1cRitsxYLE/YZdl2FmEnFhcX6ejooKuri7m5OdLpNP39/ft6jVV8vT5i12OoVRWHWSZqVRhZzrBYTNDX09u0NXM76ppOXdPp8tsRRYFSooTVa73fPvUhoZKtUM1UsfrvzfPJ5XZhNptxe9z0D9w930wmselsZ2OMU1irHJWSJbJzWQ5//jCyWaZ+u05sOUZbWxueTg+FpQKzb8/ijDqx+qwodoXep7ppPRklM50hNZ2lUqxhMkt4W1x4e7zYgjbKyTLiWzfpPdyLq4k65HvnZ/jRn1+nVqrjbnEgKxKlTIUrP5wgvlDgi//kNB7P1u/kXlU3AcLBMEe7HuPSD8dYuPBTwu0uTj7eTfvAvSVl1nPz6hKZuSzBPv+W2T2zXcHssnDz/ByPPN2z1sK53lZhrxiGgcPhIBqNMjExQWtr6+4epDuQvJNk5o0ZSskSoiAyvzCPv9tP8IEgR588SvlImcRIgvR4Gq2uIZpEPJ0eqs4qoV+Qf9p9Pny8++67JBIJzGYzkUhkLUBSzDKGunU2XKvrGzQI1jM6EkOhF6vdSjxWuGc5fJ/fzqd/c4jJ8WXOPnRgy+8Nw+D10Rhv/HAMeSKLta6DYDB7dZmZ0QSFZ/t5sKd5F8W9rDGpYo2/+dkkCz+dwl1Uca3MlFXmC9wYS5PJVPn84914bHtLHm1HLF/hjfEEsiQwGGmsrfPpMq+Pxuj027cktdfbKuxGuVxGlhsdUuFwGKvVyvj4OD09PR9oRXJiYoKf/uSHtHcO0PVwB7OXF2Euh81lRhBFaqU6pWoNa9jKc792jEMDH7wV0EeJ+4HehwStrjH1+hTpiTSeHg9e0YvpkgnREDn3iXMoVgVnWWU+VYb27WWxC6kSFqcZU76GbJHXWoX6+vt47fXXOXnqJNCoaKVLaRavLRI8FNy1urVKuVxGEIS1zcF0ssh8uow/WQFJ2DbIW0UQBSS3GWmxQCpo4c5yfi3QU3X4wc0lLk+nCGfriOMZ1HSZuiwimERUzcAY1wg4FZZdJrIjSWw2Ez6T2NQnRJBErK1OCnNZrlxf4nCre2c/uU3kqyqabqCsa4cyVB0tX0O0SIjrWzllidSK910zW4XdaKiNKVSrVY4fP87U1BTvvfcep06d2vfcgKvNhbPFSWY8Q7VQxbZUgMkF0naNSXGqYXK6zdew3sdM1w1iuQrtPhttXiuGblBKlGh/qH3bGaP7/P1SzVapl+s4rfsX76nWNRYyFer2EN3dHWg6a0bbkXY3U9djVIp1LPa753khXcbsUGhpc1PNValkKpisJrSqhmyW6ezoJBZPrLUtyx6ZwmSB9GQa6zopbIvHQsQT2TbJlKllGHp2qGkQUirXeOt7d9DqGtEDd4MsJWLC4bOxMBzjwluzPPPC1pmLew30DMPgp9++xYVXFqgrDmLFGnPnZ5m+neSzXz1N++D7C/YSsQKGAfI2ggEOv5XccoFErEhHZ+Pa200pb5V8Js/y4jK5Qo5gpLHhkSSJ3t5eZmdncTgca/PE+6EYLzL1+hS6quPvbwSo12evc/O7N3mMxzh44iDOqBNn1EnrA41WeckkoTgUZmZm9v1+9/nosrCwwM2bN4nH4/zGb/zG2s8HhyLcPj9HPlnCueLFWs5X0WsaB45vTWJceneOV//8GuVMBSiSmnmXz//2aaItLjRNo1gs4nLt3V9YNwo89bGjTX83nynz7tsz2O+kG23rK/ZYlmSZ/K0kb7os9Icc21bZ9hvYXJxIMP/GDMGyjtLpRljpppA1HWU2z+wbU1zp8vDEwfc3F7+QqZAr1xmMuNArKggQ9VgYjxVYzJY3BHp7XWNWKZVK5HI5ent7mZycxOPxrPnt9fT07CgstRu6rjM+lkLXdS5evkClUiGfjfErn3+ChYc6uHZxnuWpDLqq42p1cu50C16/xvFfkCDMR4n7gd6HhNxcrhHkdXvWgq6WlhY8bg/tHQ1flEibm+Uri2RjRdyhra1JWl0nt1TgwKkWFFXHsi7LP5epEj75LMtlWFwY5fr16+RSOZ7kSdILaRxhR2Pgvcl/wNrFPjs7u8E2I1Wso5frCMkygntvbQWi24w6n8dcVFlc5zk1noPsTJpIqop+PY5uljC1OdcWPGjMLeq5KsU35wgW62T6PCxmK3RsY9gtWmTsosjcSILEI12E9tFaKQrC1nhIFBoZ900BY0PlsvHver2+rwVt9fGCIFAqNbz1urq68Hg8vPnmm5w+fRqrde8VG1EWaTnTwsRPJqhmqihBO0Y6hjVnI+OwUa3VMJubH5+iKORzOUTZxHK+QovbyqkOHyZZJDeXwxF2EB66L8DyYaGSqYCx/01FqaZxfjzJUq6MYAlwZalMpp7idJcPWRJo7/QyeyDA/HCccraKbJZQy3UEWWTwwTZcHgu5uRyG3pDKrxVrKA6FRdUG0cMsp/IszUwwMzPDo8ceJTudpeXU3qpP1WoVXde3rTTdHo6TWcgRbKKOJisSZqeZ4XfnePLZvjVBqvVsDvSqVZXha0tMjMapVVUCURdHjkc3CEGkF/NceXMGw2shuqL6qEecLIzEee8nE7QN+N9XxloUBHYKP7UVUau9brp0TSc/nyd5J0nsToyfvf4zEskEp586zdATQ+gRHVEWaW9vJ5FIsLCwsO/qYHo8TSVbITAYQNUMFpZTTM/N0D7QRmw8Rm4uh7e7EUAqdgXW3bJcLhe5XG5fm/L7fHTp7u7m2rVrvPDCCxuuk6HjUSYf7+LGz6fJLuQRaPgODzzUzpmHtoqsvP3qGLWySuuREJpqsDi8zMW3Z/C35Ll69SodHR288MILezqm3doKx+MFKhMZXIqEvC5JJQdt2GeyxMdTTCSKTQO9ZsmkxWyZ0aU80/ECkijSF3EyGHbitSsUqyo3bsZw5GqYWl0b9jyCJGKK2LEt5rl+M8aDfYEdVb53QxYFGqKURiPhKwhouoEg3FXWXmV5ebmprcJ2rKoEr29ZVRSFwcFBJiYmGsJRooWl+VxDJbrNvSczdYDXfnCbd/7uDrphIIcMvvrVr9LW1oYgCIQCcPxIhJqqoxsGiiQiigLT09N7PvZfZu4Heh8SMpMZoLHIAWRKdRw9J2m3mTAMoyEicDhEPFZkbj5HvaLiiTRalgzdoJipkF3IEerxce7JXuI/ndowX5MuVilqIpMLcUZ//iqqqiIYAlOTU9TfqmPyNrI4hmFgGMaaZ9Tq/zcMg1QqhdVq5datW2uvezsrUKoHQDcQdhACWM+a/L/RqCpAo2VzOg9tkooxkkS0m9YyaJufK7gs1G0y5rk81HRSpSphpxnzNtlwk1kmX6pRadIishM+u4JJEinV1DVFzdWK5Hp0o2EEHVkJIvdbMYjFYpw5c4Z4PI6q3p2R83g8nD17lvfee4/+/n5Cob23OjlbnFjcForxMrqu03OkD5uqkIg6SJY15GoVt03GLK/7zgwo1lRixTp2o0aXz86JTi82RaKSrqDVNNqebtt1TvQ+f3/US3UEef8BxkyiyGK2TIvH2vBIVHWmUkXavFbafDYkSeDc413ciTiYvZOknKvittuJRBz4VZg7P0cpXqK4XES2yJQTZcxBG4liDVWy8NaFy6iZZXRDJ5vPUl2q4pxrVB1Xr4/tAqPp6Wk6OzuZm5trfuzTS2iavm31S7GbqBaqDd+3TZYAoihuuD7T6TJ/898uM3sz1ljDJIGR+gyXX5vg0c8c5NwjXQDEFvIU8zW868RDRBHMPgtL0xnUsoppm1azvdDa6UaSJWqletOWtUK8hL/dTWRlpi2ZTG7wV11PvVRn+ufTJG4l0HUdq8+KJ+ihVCnRqrQy/co0UlKi8/FOzE4zgUCAUqnExMQEXV1d2waTqqpvEHspJUtrHSOzqRLxbJknnn+BjrCf5O0ktXxt28/r8XiYmZm5H+j9A0BTdRamZWzSIeyOjS3/kizyqS8OceBohMmxJIZu0Nbl5eCR0JaZU03VqRRrmFc6DCRZQDJJ3LgxQuqddzGbG3PKN2/e3PWY6vU6iURix0BveLKAVKwhNgnkRKsJoVinXLuraK1pOtL6AG3d+nZ1NsMP35khfzuJJVVFF+B20Mp7h0N88mwHVkWinK1gEwVE89Z1TbTImBEoZsoUqur7CvQ6/DaCDgtTySLtXhuGATPJIlG3lbZNYyrlcnlfM3aqqja1nxEEgUi0nW994wKTV2MYlUaw6QjYOPRgO098rA+TLPL6D+8QXyjw4JPdW7wBb11cwDAMJFnEXPXT2tq65R6ibNqDOhyODYKG/1C5H+h9SKgVaxsUMOfTZe4kygTsGr1hHZtZwmQ3cfBgkJawjZFbceLjaQxdBwzMTgu9Z9v52KcO4HdbSL8jUy/W115zIOzCZTURdll4+ujvMj4+zrV3rnHk2BFOv3B6Vx+0er3O3Nxco+1vHe9Opvj+ezMgFpr22jfD0BuBq2roaJVGYDMeL5CtwcFUBbWsoTSpWK4iCIDVhAGYMxUKNplMuU54m42fvmIgvt9Z3DaPlU6/jclEkZ7g9gtFolDFa1PoCzk32CrsRrlcZmlpaU0FT5blLb4wiqLw0EMPce3aNdLpNIODg1teZ2Y6jckkbahASIpE+GiYSqpCLlkm5HUiqtB3IEhWFplKFInnq9T1GsKK0ZVhGFgUmW6/g56Qk4BDwaY0ZPNLiRIdD3fsqJJ3n79/dG3v8xPrSZVqmE3SWiuzIt9VvlxFNkn0dnoJiCL5+Ty1Qg0yVQorfma1Yo1ysoymaQiSQL1c54jfih510X6yjVKxwK1bt3BanIT7wxsU91KpEoVcFZfHsmGWLpFIMDQ0tGMAkFgSuCzNota0psFerVjH4bdhWREfiscKqKq+dn2sD/T+7q+uM31lcYMvqa4bpGayvP7XwwTDDvr6A1htJmRZoFZTUdYFj2pVxeq2IprenxLk4KEQkUEf8zdihAcCGz5XbkW589jDHWuB1npbhfVoNY3J1yeJ34jj7nSjyiJL2Qp1bwi/w42t04ue0InfiqNrOr3P9mKymrDZbHR2djI5OUlLS8uWDoLYcp5b15cJt7g4sKKMrDgU1JXzJeyyIAgeWr02dE0Hgx0VnQvLBZbeWSL2WgzZItMy1IKvz7cvYbD7fDSYn8ty6YcTqBWZO7cSW2T5JUnk4JEwB4/sXDmSZJHWfj/DP51CMkmoNQ1BFHj8qdOcfOCT/PznP8dqte5JoGViYoJjx47tuHYm5Bivnc+hl+pbks56pY7ut2JduU6vXlrg9ZeHCXV6+JVfP75hjZnPlHn17Wm0C4sEKxqSy4yhG2gzeTKZKt+TRF4404ooN3x9Dd3YMqdraDq6biAp0paq235xWUw8fyTC312Z4c5CGovFTJvXxscOhd9XAAkN4ZaTJ09u+XmlqvLNr19m/PwiZqcJ0QMup5N8oszb3xomkyhx/Gwb5//2NqVUiWqlTtfvPrjhNSKdXpKTGQwDOo6E9tTd4PF4+NnPfoaiKDz88MPv67N9lLm/qn5IkC0y2jq/s5DTTJvXit+uYF3ZRGi1xhzMY8/289hnDzFyY5lCvookiXT0eOnovBtg+Pv9zL87vybSYDNLG4KVgYEB/PiJHInsyex6enp6be5mPT67CcFqAq8VfamAtIM/3yp6toroVCgKEPVaKBZrTCaKiHUNI1ZA8uz8GoIANreFkk3GmqygBGykSzXCTT6HoRuUK3VcUSd++93M3GqVdCdEUeBst4/FTIX5dJkWj2XLc9KlGtlSnacPhHBbTUwsppp+T80/h8DLL7/M9PQ0hmFw9uzZDRW9VWp1jYUpheFLE3zPMsqvffVJgsG7M1lWm8JmxXFBEOh6vAu1pKK/M0tlsYT/gB9X0IZHFOnwWkkUaxQqKnXdQARkScAuG0yM3OS9kVm6OrvoCzQM5Dsf6aTldMt9S4UPGfc6c+aymJhMFjGMxvWkrSRfrCtZdEM3yM3lSI4mqearmF1mHFHHhg2I1W2lXqhTTBRRHAqFpQIsFfB0edDtCk6Xk5PHT5KbyeFbabPM5Sr86Du3GLuyRK1cx2xTGDzTytMvDmKxSBSLxV0VZ/sPBnG3OEnNZgltat9UaxqlbJmjT7avtW3OTmWo1zWiLa4N39fMVJqZ6zHcba4NVTRRFAh0eZi/GePqO7P09Qdo7fXR2u1l7HYSoduDWZHJZsqIRZUDp1v2POO8HYoi86kvH+flr19maTTRMB03SdTLdRSHwqnn+zn78O5mzsnbSeLDcTw9HhaLDdGqfEWlUpVQFA8/HlmmzQ5He8MkR5O42lxrLbWrc3tTUzO4XI4NPrgOlxmH04x3XQubt9vL0tUlKpkKNo+FLnMjOZedyWEL2JqK6ACkJ9K89SdvMXJ1hKpR5cypMxTnigQPBOl5pmfHAPE+Hz3CUSd9p1qpllU6e96fxP3zLx2iXtdYHE0gyyLHXhzggXMdSLLIM888s6fXSCQS+Hy+Xe9lvSEHb3W7KV+JIaYriCudPFqqTEkHOWqmc+V6uHV5kcUbcUrpCrFni8Ddit6thSyF0SShsobceVepU/KY8czkWB5NkDgYpLXHy9T1GJZMZUOrKICWrlA0Swz2+NZm6OqqRjxVolbRMJklQn4bJnn7dahWU7l1I0apVCMcdfKgr4rvaA8mWSbqtm6phu23Evanf/qn3L59m7m5OT7zmc9suC9dvbzA1IVFgt1ezA4FXdfJZbN4oi4sThO3z8/S1u3G6jajVjVcTWxyXvz8YSJtbnRd5+TZ9l2PxzAMvv71rzM/P8/Q0Fabjn9I3F9RPyR4Oj0sX1tGVxuzE0GXmWcPhTcsRqV4CUfEgdVvRZRETu1wsgcOBEjeSZKdyeJq3ygDbBgG+fk8ZqeZ4CY/lGpVZWoyhd1upq290WaRTCbxer1NMyidfjutXivxoAXnfB6jru0oyGLoDU897YAfb9RJl8+G3a5QqKooqo5eVZF28cIC8AVs5H0WmCkgqw1vJnTYrMqiJsuULBInj4YYv5Pg2sUFYrNZdM3A4TVz8EQLQ8cieJzNg92egJ0jDoV3J1NczeTxOiw4bRbqmk6mVMOmSDwxEORc7/7FGCwWC3aznXAwzFNPPUW1Wt1S0QP46Q/HeOfbI5gsEqVchf/8f/yAf/6vXlwzOQ1uY8jqanVx+IuH6X6qu6FwOJYidTuF1W/F6rMSdJo3KJ5C49yYQkRNqIgOEfshO20PtuHudN8P8j6EmGwmDHX/gV6H38Zspsx8uoRJlqirGq1eG1GXBUMzSNxKkBxLIltknK3OtQApX65TrmvoOiCA7lbQFnOggyPioF6ukxpLUc1VCR4MUowXCRwI4Gp3oak6f/P1y4y/t4AjZMcTcVLMVrjwvdsU81XOPumjq6tr12O32xUe+vgAP/qzayyNJnBFHZgUmWKmQiFWoO1IhANDPqampujo6KCz14tab3QbrD+H52ez1Io1Al2epu9jdVmYGYkDDQPv5744hP7n11iYTJOtqThcFk482c3pJ7qbPn8/VCoq2WyZT3z5GFcu3SEb06mWVQIRJwePR+nt862tv5ttFVbRVZ34cBzFrpCt61ycTqPpBhG3lVJRw+6wkynVGF4uEQnUcHotJG4lCB0JIZtlyuU63/xvl0nMZjn2eAuDh8u0tjbsb2xWZcv9xtXuou2BNubfnacYa7Tw1kt1rF4rnY91Nm1lVasqs2/P4na6sbXbKMaLtB5uxVAN4iNxXO2ufalA3+fDj9ks88Xf3FrluRc8Hitf+e3T5HJVTCZpR1/JZui6Ti6X21MyttVj5cy5Dt7M16hO5bClyhiiQEkRMQ76efrxHorJJZZKVg6fihKfzxLp9BKJOrkzdvd1JpbyWDIVRO9GCxxBEhFtJsR4iaVMmZODQSZ7lymOprHVtEYV0QAtW6FYqiMdCXK8L0C2UOXalSWuvztLZjaPrmqIsoSn3cnQA20cPR7dsp+pVFT+6r9dYvLiArpmYHYqtB0z8+unTmz7+ROJBJ2dnWiqzp3RBOlUCavVRP/BYNPvvbW1lWvXrhEOh7fsFW9eXEAQwLxSCBBFEY/XSy6bw2wxY9Q0FmZyfOF/PksyXtzipwyNNejRp/aWRIfGWv/Zz36WP/qjP9qzIfwvK/cDvQ8J7k43rjYXmakM3h5vYxZt3aJQzVXRahrBw8E9GcbaQ3Z6nu5h8vVJkqNJzC4zkllCq2pUc1Vsfhudj3XibLlbGapWVb7xRxeZubaEYjXx+K8c4oGHOkin09sak5okkaNtHr4bK+Bqa1gnbGexYOgG9YUCos9K3K3QQonuqItqtUo+l28MB+8Rh9mErcNNZaGImCxTtQjki3lW5VMMw0DPVEhn6ogHPbz3nRsUZ/JQ1VDsJgQgPaYycX6a1yI2Tj3dwdBhH5Ikrf0nyzLvvDHPe9+fQDMM1EAJ6YFBdMOMIok82hdgMOqizWNFEIQ1WwVN1bl8cY7hi4uU8lWiXR5OPNhOR6eXQqHA66+/zlNPPUVmLE/uupmgb5BaqobslptW9OYn00gmkWCvj+xSHl01eOedixw82L+ruqfZacbsNOPt8RI+GiZ1J0X8Vpz0RBqMhvqqaGrMLWk1DUMz6HJ3sVhdxGKx0vZgG55tNsL3+R+P2W3eVkF1J1xWEw/3+plJlchXVPwOhQ6fDZMsNIK8O0ksPgsmqwlVM0iVqsTzFTKlOrquY6xeaXUNxSxh5CtUp3Scbgsmm4nESIL8Yp6BFwboeqILySQxfGOJmRsxAt0eLCsJBsVmQrHI3L4wy4HjXsSevbVAPnCuA8Uk8c5PJkhMN5TWzA6FYx/r5YmPD+D329F1ndnZWWw2G+HI3YRWqVRiYWGBZDLZ+CxNWqUADIy1lnUAZHji851MjdrIZUt09YTxtthZTizv+/sXRRFZlpEkCUEU+dZ/v8n8jTiusIPHP9POJz69Ve59le1sFd585U1m35xl6KEhxhNFynWVFo8N9IbogiCA166QLZSYShQ42+UnM5khv5DH2+1lZjrN1JVFyuky4z4r5x7tYXx8nO7u7qZJPkEQaD3birPVSWYyQ61Qwxaw4e31YttGHCu/kKe4XMTf7edc6zm+/6PX0XQBxSyh2BUSIwnCR8P3k0r32RZRFJtap2ymVK7x6rdvUSrUePKFQaItLmZmZtY84XZDEASePBDG5zBzaWSZ+FwBQYS2DjenB0McbnEhCCEKhQIFxxK/9c/PYrc3kq7lcpn5+XkymQzLy2lkGj7AW1gRREln0hz1ahw56eGiViUxmcMyWQAByjYZ86Cb08ddZObmefnbE6Qnc8iSiN1nRrYK6GqdpWuLzF6e481uN098spv2FufaXubKhWVuvz2Nt82FzWlhaSLJxIUKqU+X8PmaX6u6rjM7k+H7f3GD2EQKbaVV1t3i5OEXBjjzYAc3btygXq9z/PhxnE4fFsXPuXPn1r6/VfLJclOlbpfbRblYQkUjly7T1u5eKzB8EHg8Hh584AXefu0OA/1pOv+BmqbfD/Q+JMhmma4nupj40QTJ20ksHguKQ0FXdcrJMgjQ+kArgSYy3lpda6h2jqcpp8tAQ77c1+tj4BMD5GZzJEYSa4a2Ladb8HZ7N8idA0xNppi5toQjaCcfK3LljWlCLfquWfYjLW7G4wWG6zoRw0CdK4BZRPJYEEwi6AZapko9U0J1Scx6ZZRaCkuuwL//96/hdDpJ2TupCDqiRcYo16HJQPJ6RBHa/Hbm+n0so+NI1ZAVCyaLiK4aFAs1SoqM+0wYLV6iOp0n2uXHvKmCpdV00jMZrr66QDAY4MypFlRVRVVVNE1j+k6CaqFCvVanXkvzuSNedAQkUUAUqui5ODO5xmstLCwQCAT4k6+9weibi2A0euonr8xx7a0JnnipF4dL5eLFi1y9dBXTXJRE1U5NFRl+e5bjL/Y2DfQCUSeTFxZIzecoJcu0HQvzxBMPc+PGDdLpNAcPHtzxu1rF5rdh89sIHw1TWC5QyVQoJUqoFRVBFFAcCja/rRHwSQJaVUOtbD2e+3x4sHgsyGYZtaLuu+XNZTVxpHXjTbWwWCA1lsLibQR51brOWDxPqlBDEARsZglFulupUYt1yn4r8y12sjWNDk3DYzURPhpGr+mNdWzlBr+8kEeramtB3ip2j5Wl8Tjl0v7aH4+fbmXoeJT5uSzVmobfb10zStc0jWw2iyRJJBIJRkdH13ydarUa4XCY46fsXP/JEoV0eU3afT2VTIUTz/dvmC0E8AQ8SJKE07l/S4tVNE1D0zRUVSUZz7N0J4mm6yyPxRm/oxAKb98yNT8/v/bvTLrC+O0MggCJiWnm70wymZ5EjRzCuiJ8USqXsa2bubObZeL5KobQ2HyuXuOtbW7C/T5iUyn6j0awWq10d3czNTVFJBJpmhUXBAF3uxv3Hjdnel1vCHHJIlbZymNPPEVF1VBMIrKlcR6vjAzf5z7vixtXlrjy6jhqVcNikXnmU73Y7fZ9mZhLosDJDi9DrW4ypXojWWJTkEQBXdfJZrNkMhlKpRJvvvkmqqoSiURIJpMoikJ/fz8PSxl+OnkTbbGE6DKvJZUMVUevaBjtLo72tNHR7qG93eDMwW6G5zJcuDRGKBSkq93NwTYPUl3nL/7wApW5Cp2Hokib9kj+NtCqGrE7Sd55ZYmOr7YSDNpQVZVqeRHDAJNVplqrUhVKiCWF26NTtLZtXcd0XWdsbI5LP75Jdq6Au9WB3WZGUzUScyn+9mvvUihmSKZvMzo6yuuvv052LoLLfIzZmeyWgMrmMpNfyDX9jq12G4KepVov7NvOYS9kEwa1pMjUWPJ+oHef//E4wg4GPjFA8naSxK0E1VwVQRTwD/rxD/jxdnu3ZJ4z0xnm3p4jv5gHQF4xDs/P54ndiOEIO2h7sI0jv3oEQ2uoym2XLbXbzSg2E/nlAmpFxWQVsVqtu5p/WxWJjx+JoukGIyKYbQKmuQz1yTS1cpV6vU7NDLWIjXLYQzRo5uOHwxzsCOFyfZJcLsdbw9OMJKYRWp2ow4mmipubEfM1Ok5HyfgUvFWdTKJMOVPA6rDhPOjj3LEIM1eWmZvOEh0MIilbFxBJEQn0+UiMp/jZy7foHwjic1vWWqOOPdBNdq5MNpfl2JmuHVs+VFVFEr0sXB/FF/HgWhGUMXSDpZE4d67kefozrXg8Hg4OHmTZqFObqWIShBWxh61iLABPPjdAOV9jdjRBYNDNi18cQhAEhoaGmJub45133uH06dN7voGZbKY12fNmGCvqg1pVu1/N+5BjcVswu8xUshUclvenLKZVNRIr82Emm4maqnN7OU+qWMVtU5qLAFRVHH4bwS4v8UKVKVHgTKeXVp+NWrFGfCSOt9eLv9+PySQ12j01A1G6+1rZbA6zxYLpHgRNVK2O2VpDkGoUS2WKpRTQmDdzu90b5swWFxep1+u4XC4kSaKt3U33sQjDP5tCViSsKwGophokp9PY/TaOn23b8p7VavWefOfWs5ppVxQFS7uFvhNtjF9ewBEy89Qzp/AHtv9b6rpOW1sbyWSRb//xe8THk4BAXdWIuAMceegAw1mJ3EoAp+s64rq1wTAa/yMKjarl6v3A4TDzj/5v55gYn6Wvv/G5RVGkp6eH+fl5yuVyUwEYwzCoZqtrGX+z27ztzKLZZUY2N1o8TTZTY9O8ci5Us1X8g1sN4+9zn3vB7bVg81ioFut4Anbi8Ti9vb1bHnfxnVmmx1JEO9ycfbhjS6BRq9XIZDJrAd2qFrAoirhcLjweDx0dHRw9ehRVVZmZmcHpdBIIBBAEgUNRF1cOBMhl53FOZ5GcSiP5XayT8phpORSgb8X0XRAE2n022n02oiQ4evRuEvcvv3GV1J0kLQeC24o/SWaJ8GCAhZE4P39tgl/9ynFkWSYU8WAymVArBlanFS0PgU4PBw52N62OLi4uImpByrEEHUOtSOvez+11szgcZ/ZOhdZ+L62trRw4cICbF8vU8xK2JpW7gyejzF9bQq2qWwSXqoUakknm7KMHuHPnDp2dndta69wLDz3ZTbjVxcGhvauW/7JxP9D7kGFxW2g900rkWKRRaVnZdDULztITaSZ+OEG9UqfsMjNfrBBPNgaB/Q6FNpcZIVFk7JUxep7pwd/fXI57lbZ2N09+7giX35jC7rbQe9SyrbTu9FSc2ek4gbC4tgiqyQxyXiAm26l0mrFELdhMCrLFjMNtxeu2ciDq5FSHb8NsmNfr5ZmzLl5+e4QZsUKrVUZNlpH927dnaPlGhaEecXAgbOfXznaQyBS4MzHJ4UMHCTnN5LIVLn/jJp6wo2mQtx5fh4fF2wmuX17g8SfuBnPnHu2is9vLK6++wpNP7TzQKwgC0+MpKoUq/nUBkiAKOCNOlifTmKQD/NN/+k+xWCzc9N5kYL4R5B14pBNJkpoGeg6Hwhd+8ySapjM/P0fruipMW1sbbrebN998k5MnT34gMsKCKNyfk/mIICkS/kE/0z+bxrFDFWgvFJYLVNIVHFEHhmEwES+SLFbx2800yw0ZugGagSloAwGCTjOJfJXLsxmcVhNuu4IoicRvxBvdBYeCvOWzkp7P4e9onMOqqpJfLOGOuOg/ENzyHm/+dBKn20x3r5NcLrdm+7KK2WzG6/XuybcyGo2iqiqXLl0ilUrh8/l48XNHqNU0pq8ukpzKIkqADs6Ig6deOrRF4ht298lUVZ03fjLB7SuLmO0mTj/axeGh7a8nURT5/G+cYOGZPoqlxI5B3npbhVvXlomPJwkPBjAMWLpWJxwNE3AHaBMrXJxOo+sb5+QMA8qqQaffhFbTEE0iivPuZzGbZQYGO1haWlqbz4PG/E0qlWJ2dpb29sasnq7pZKYyJEYS5OZya4GexWMhdDiEt8e7xYrFHrbj6fEQv9lQBTVZTQ17oFixMZt+cOs5cJ/73AuDB0J87vfOUizUcLprRKNbr8E7o3Fe/bOGAft1s0ipnCXcIm3orFEUBY/HQ1tb266zXrIs09PTw/DwMHfu3KGjo4OI28Lz5zp5RRKIjyZRkhUMUUDt89JyKMgLp9qw76I2G0+VmLi0iMtv21XhVzSJuIJ2pq4usfxckXDAzqmz7cyMpRh5Z5bkdAqbx8qTnz20bQtspVJhcTqHpEgbgrxVbH4ry2NJnvvsWZ588kmWl5c5ccKHyWRqWpE7frKV4QvzzF1bxtPixO6zgg75ZInccoGes60cOdaCxSwzNTWFy+XakKR7P3g8Vk49sDVh9w+J+4HehxRJkdY89ZpRTpeZen0Kta4xI8PoTApdN7CZZQRgKllkJlWiL+SgR4fpn05j9VqxBXZeqB54qIMHHupgdnYWv99PPp8nnU6vBXOZTIZcLkcyLiALVk6ebcHv99HT04PH40FRFMo1jfFEgXiuQqmuYZYlvDYTfSHnmmLUZsyyxJEWJwmrh1Qxhn2ihFnVEdwWarU6ABa7GYFGG6hRriMeCpC0yzzS4iLotCDVSxBy0LuiLnrhzRkq6RK+A7tnckSTiKJI3Li4wGOPd28IrO1OQCiubXCakU6n8Xg8zJIChC1zP6sqnyazGYvFgq7r2EI2Dp/du5CDJImIorilvcHpdPLQQw/x3nvv0dnZuW/j4/t8tPH2eFm8tNhQx3Tem8ehoRlkZ7KIZhFBFMiX6ySLVVxWU9MgD0Av1hEdCpLn7nsGHGbmMyVmUyXcrW7sITvZ2Sz5xTyhVhfnXhjkjZdvMX8zhskik08V8EU9PPqpAzgcJlKpFMViI1lVq2vcujKJzWWmo+sw0Wj0fbf1yLJMa2sroigyMTFBW1sbX/nqaW6PxJkcS1KvqfgCDg4fC6+1ge6Xn/94nJ998waSSUKrqSxPpLH+zw/Q07t9ok2SRYIhM+bczu2g620VGp2XAobRCLoliwlbi5NirEhHq4uZVImpeJaQq+GVVVM1UsUaXptCm9tMMVbE1erCGd34ntslnHw+HzabjfHxcdpb2pl7e47lq8sIooAtYEO2yOiaTiVdYfyH4zhbnHQ/1b3h9QVBoPPRTgzdID2RRl8RybF4LHQ+3om784Obz7nPfXp6/ZTLZVKpFGazGU3TyOVypNNp8vk8IzczJBdTOMJWivESlbLB4OBgUy+4/eB0Ount7WVxcRFd1znY3k746X5uD0WZS5eQBIGugJ3+kAOnZff3GhmOUU6WiA7sTfTNGbCxMJpgdDhG+LFuZFnkpa8cY/xsKzeuj3D23BCtbTt09ayqku+kmyAIOBwORFGkWq1iNm9/73E4FF76Ryf5wd8MM3sjRm5pReTKYeLI0z18/LOH1uxwurq6iMViG5JK93l/3A/0PqKkJ9KUkiXSHjO3ZrJ4bMqapws05m/KNY3RxTy2Dg/BTIXUeIqFzAKZTIbTp0+vPVZV1bVe80wmQywWY2pqCofDgdPpxOPxbGhPWPW4qlY1rE0CN6sicaTFDS37u2l3u6CvI8Q7kkTNlibx+gzq+RJGUcOQQLYpuKIOfAMBjAEfi26Fw61uHu0LrhzPxsWmWKgiCgKCtLdWILNdoZQuU9cMlHUm1BMTE3R2du7YGplKpejt7aW7X8DiMpONFfFEGgGnrhnklwp0nYyuKWQuLy8TDu/sG9SMUChELBbbIsIiyzLnzp3j5s2bpNPpDV5C2oop/fuVgL/PhxNbwIav18fytWWUAeWehCxqxRrV3N1AMVGoomkGyjbCT4ZuYJTrmPp8G4WXBLCbTUwni/SHHKh6jWuXr9H5eCeuVhePPtWDJ6Bw6e1JFmeT9B0Kc/hEhGibQCwWw+PxbMjk+n4jjNkiYbPuT13v/8/efwZZep0Hnuf/Ndd7m9678r5Q8IYEHegpkqIow6aa0oymt3u7Y3Y3YiM2Yr507HyYmJ3Zno3dHkWr1dOt0UgiKXqRAAiA8Ka8r0rvM6/39nX74VZlVVaayiqABoXz+wRkvtdW3nPPc85znudubo5pi4uLNBoG1aqCza4QCLnYvS9+30EewNUzy6h2ldhgCMu0WL6SZPp6ZttAD1pFVu5lYrPnYAeneqZZvpBAViR6Dndw8Nkhln41i1zTODEY4e1mjapuUSrUUBWZzqCLXXEPUqWC0VCI7Yltmip5s8rqnX9LTqeT/r5+3v2Hd2nMNIgNx9ZV11RQsLlseNu95GfyzLw0w8hzI+vOgzt8DkafG6W0XKKeryOrMr5O34bdP0G4X41GY20+Mz4+Tk9PD6lUClmWCQQCRKNR+vv7GRhokl+USc7m6NsX5aFHht93kAe3Pj/d3d00m02mp6cJh8M8MhgBth8HNlMrN5EskNWdLXRJqowMVCvNtZ8pqszorjiF0ty2Qd7Ntgp9ozD57gKGZmyYO1TTVYYe7sbjse+oVRVANOLmj75zjIWFAitLBSRZQlYqHD26sfBUPN4qcjMxMcHQ0BD1eh2bzfaB/Nt8FIlA70NIb+ikr6ZRfHam0xXsNmVdkHeTy65Q1xSm0xViIRcv/O0LnGmcwR/2o2na2kBoWRaBQIBgMEgoFMLr9fKtb33rrmkKLtcHe2hWkeATe9qwKRL/dDlJsW6huBRcPgWbbKNW1SkVGySdEtGYiyOdAT6xp23ttd8Z6EmyvO2C1J0sq7ULd+e8Z3p6mqNHj+7oPto7fLQNhbj8k+ssVzRkVUZ2qkR2x3jyM6Nr19VqtS3TYrdjt9tpNptb/n7v3r0sLy/z9ttvY5fsTLw+wa7YLiwswoNh2g+1iwnVA0aSJDqPdVJcLFJJVPC233sKp1bVMJoGil1B001S5SbObTIKjHwdJezC1r4xIPI7VRLFOu9dHCcxdRk9rTN5fhIt3NqZb+9w8tU/OkI2m71rYBPaQauVe3V7fydVCfCjvzvFwtVVVEVBVVTebffyyW8cYN+B+0tfttlbzZyhtchjWaDadhZ8bzdhutlWwTRNLpxd4cLJJcqZWqtBuaxgs8vkTIPO450svbOEzWfjSIcHuz9EXTNwqDIBh0otUyO5VGb3s7s3Le4FEIvFSKVSxOMbsyGqySpKRkEJKtT0GomFBC63a935PUmWCA4E1/r69T7eu/51yhL+bv+WvfYE4drlJKlEmeFdUTo6/SQSCX75y1/yh3/4h0Drc1wul8nn8xQKhXXplg6HYy3D6JlnntnySIPf7+SP/+UJEitlYnEPXu8H8914++fYbrczNDTEyy9e5swbbyHrNvp2x3js48O0d+ysoJMkS/dcoMgC5DvGk2w2u+k529vdbKvgPOrnwlvzrI5nCPX4cfudNOsa2fkizoCT40/2A60Fqs3Gia36u/b0BOi5UbzpZg/hzcY9r9fLwMAA169f57333mN4ePgj3fT8/RCB3odQo9ignq9Td6vkaxrRbQangMtGolTnpVMXmXnnLNYei7peJxwOMzw8TDAYXLdKkkgk6Orq+q31HbGrMgdCbt5arqDEPDTcQSqaTrPZxKZ6kQtNHHMFPvf1/ezrD6PetuPQaDTWDejBsAtLkjCa5l3P6AHUik26RiPr7rNQKFAsFredkGqahs1mI5+v8aO/OcfCpSR2vwNdVTDqOmChN3Tmp7MMDkXuq8H1vejs7KRerfPv/tW/I2JGGPjiADaHjcV3Fqmmq4x8dmTDgWjhw80VdtF1oovJX0zi8Ds27WG2nZvVDiVJomka6IaJ277534hZ1UCWsfcFNm2jIssSFqBZrTScFX0Fs2yu+wzNzMzsqGfer9vzP7hMcjJLz+4OTEunXm9QSlR48e8v0tsfxO+/96IAhx7tZmU6zeKlBFgQHwyz99D26dT1ev2uBQiSySRt8XZ++r3LnH9lBlMz8MQ9eGNutLrB3LkEC1fTHP3EEIee7mPyjUnMiolVK+NSZSzdItvQW5V3H26j5/GeLQufOJ1Oksnkpr/LTrZKrbeNtrG8vMw777xDb2/vhgmkJEu4Y27S19O0H2rH7v1gd2WFB9fli6v8+K9OU8vVOTsYomtflbNn36HRaDA6OorN1qpb4PV6CQaDdHR0bCgap+s6S0tLdz237nbZGRj8YM6D3e7288Qn357nrR9OYWomulwj+U8ZVmcL/Mm/egTfDhZeA6FWCyejrqPsoLqy0TBAkfGH1o8p8/Pz7Nu3b9vb3gy8/H4nX/7WYX7+vcusTqQpLJSQVIlQt58nPjvG2I0jMbVabdPspB3t8kWjZDIZotHNF5xUVWVubo75+XlyuRwHDx6lmG/Q1u5D2eHupiACvQ8ly7AwDRNDAtOyULdJTZRbpdV4+LHHONE3BntgqbTEwMDAhoICuq5TqVTuK6XwgzR5PQ0VjZFdMXTLoq4ZmBY06jVMr4SVMdHTVdQ7mpQ3Go11r2nvgTbebPdQSpQI3qX8t1HXsSTYf7Rr3c93kra5srJCNBrnu399jqmTy8SGQjhuWym3TIvcSonX//EKDpeNwRHXpitgOxUKhcjlcttW/rvy7hU6HB2sVFc4NX6OJ554glDQSX42T2G+cNfCPMKHT3RXlNJyidWzqwQHg/cUzN+++GCZN45mbDKsmHUds6JhHwjetTJuR0cXuzv9lLvLKM5bn59MJkM4HP6t9Uq7+bhLiwWWr2cIdvmxOVVAxeF0glQkNZvl6sUEJx7bvOeW3tDRa7faktwMmHRdJxTT+f1/8SgzkxlsNoUDx7rWUra3spO0TdM0efu1Wc6+MIkn7sF/W0sIlw/8MTeFRJlTv5gg9EeH6P5UNz7TR3GxiF7X13pmOgNO0rk0mfEMrpALT5tnx/8WpmGSm8nhvDGBTCRau3nXx68T7R6gOx7BflvxBlfIRXYySyVZEYGesGPJlRK1XI1gd4DCSgk1WqGzs5N0Ok1vb++O5ijz8/MMDNw6Az8/l2NpvsC+gx07Cq7ejzs/T2femMPULdpvzAvqtQbzl5c4+c40H/vkxvZIzWZz3QL8nn1xXu/2U0hUCO/gHGtxtYyv3cvefeuzEm72+t3Knef/O7sDfPtfPczMVJZ8tobTbWN4NIpjB98tsixjGMa2cyePx0M6nd4y0AM4cOAAwWCQSxev8+/+7S+QKirDx7v42p9s3exdWE8Eeh9CsiqjqAqqZWGTZZq6iX2L1Q1NN5ElCRvgCgXY+8TeLavzzc7ObmgfkMvVsCxry6aa9yKXr5FKlIm1eQlt0/C00dCRAFmRsCPdem3uVoW26cV5lpeSQO8dt2usWxUP+pzsPtHDyR9exVVsbJmyaOomyaks4dEIe/et/wLZSdqmpmlMT+aZO58gOhjEcUd5YUmWCHf5SU5lOfXyNOFYz/sKpn0+H/Pz89sGeg8deojwahh3j5v5lTS1pknQ03r/muWtUz+FDy9ZkVuFLgyLxMUEgd7Ajnf2bv9yl2VapffviPbMmoZZ1rD1BbD1bJ9yJ2Gh3NgZtywL242zvKZpUiqV1u3mVWtNLp5doa3Dt2mVy3vRaOhcOLtMs2EwPBalrf1WalQmk2F6epqrV68SjUbRNR96Q8cevy0IkyAQ9FOSq8zPLbP3YGTdjkAtVyN5Jkk6l0arakiyhLfdS3RXlOBAkNn5WYaGhpAkibE997aYs1Wwtby8zMLCArJs59wbOWxOdV2Qd7tAm5dGucmZ12b51Df6iA3FCA+HyU3lSF9NUxgvYGitHoPlQBnVoRLoDRDdFSU0GFp3Bsjv91MsFtfOZENrrLQMa+26w4cPc+jQIVLZPCv5GqvFOr23PbebAbCpr6+WKgjbGRqNcq4nSDlVoXN3jG/+6XO4XDZKpdKOKuxms1lCodDaZ6pW0/jBX58hM5Nn5RODfOWP3l+QMDGRZvJaGptd5vDxbiJ3zI9untGD1phXya5vGO50OXC5XBRy5U0Xk5vN5rpjKF6Xnd3Hu3jn+1fwlJs4tlk0aVY0KuUGJz4xiO+2x7xZaXg7q6urG+Ym508v894rMzTrGqOHOxi5bRE7lUptuxun6/o99S3cTGdnJ52dnfi9/Xz35DtYRpOZC6uUy40PLNX2QScCvQ8hZ8iJO+7GTFSIeO2kyk3atghi8jWNsMeOq2bgiXo2NEm/KZvNEggENlS1k6SdbcHfzdxcnh/89enWSlPcyxe/fZjBLSZ1oYgbZAmtrt9Yab+lWdPwBrx0doe5cuUKPT09a42L71wFA/j4J4eZm1wmeTGPL+jC1+ZdK09sWRb1XI38cglvb4DPfGM/ntuKy+wkbRNa78/lM8tYholzm4En2OUjM1dgdqrI2Ni2d/m+2b32VmsO2cZATxcWFoZmrO1ACA8m1anS/3Q/siqzemEVu9eOt91718+w6lJBau0+21UFh02hoZvYFBnLtDDzdSxJwj4QxNbj37bXmW5YSJKM90ZDX62mEY23JgPz8/MbPk+KLGO3KSjvs6Kmrpt89387w9R7S5iGycmeIF/9s2N039jNd7lcnD59munpaY4fP05HRzsuv5NSpka461ZAWC83sDlt7Nk/QLVaJZPJ0NPTQ2W1wvQvp1m9vkr7YDuusAtTNynMFchOZTFjJie+fuKex8ut0jYbpQaVZIVysszLP3qZom7HWG6jb+/2pcKDHT4SU2mK2X60To251+ZIXkqiOBQ87Z5W0J2GSDSCVtXIz+bJTeWIH4zT+1jv2k5wMBhkfn5+XaAnqzKSIq0L3CRJIh4JIdndBO6oIGiZ1trtBGGnevtD/NH/+RGyqQo9A8G1om83v+u3Y5om+Xx+3aK1okiotlYlc3WLlPSdunolyU/++gzlVKs68Pj5Vf7oL07gv63i8e2BnizLdI5EuPLaLIG4B8UmU87WUBwKo3t66OuLMTc3h9vtXiuyVq/X1wV6hmEwPOZk7mCM5TMJIl1+XGHXurHGsizq2RrZlRJ9x7t46uPrewbOz8+vK9K2mTsXy8evJXnhby/QqDRRHSrv/Ogaiirz7GdaE5hKpUIstnlLlK36At/J6/WuFYDZTk9viLbhCNmFAgP720WQdw9EoPchJCsysT0xCnMFhiNuMpUmuUqToNu+VgrdsqBQ07AsGI550TM1Yk/0bqiedHPFJZPJMDIysuGxtuqzcq9Ovz1HdiZP23CY5ESG02/Obxno7d7Xxlu9QdIzOdpGo2vNlQ3NJDObp2NXjENH+lFVmYWFBVZXVxkaGqJarZJKpYhFY+i6zo9/8mMeeughvvD1vZzpSzN+aomV8XSrEidgmBY2n53uh7p59nNj9PUE1z2Pu6VtXrt2jUqlQm9vL7nEIjb39h8nu9NGs9ZAkTZO6kzT5O233+bUe6do1ps89sRj2+7YuVwuarUaLtfm/z7+Lj/hoTDpq2lc0dY1pUyN0GCIQK8oYf4gU50q/c/04+3wsvTuEtnxLN4O77ZFeOweO6pTRW/o2Fw24j4Hk8kSbsPCLGsoQQe23gBK2HnXQKZQ0wi6bLgrOivTK2SuZ1q/8IK7y73h8+RwqBw+/v77HC3M55k9v4q/w4fqlJg6Nc/l81109wSoZWuUFku0G+0k8glCjRBKTWfvo9289+NxMqaJN+yiXm5SSlYYONLB2O44qtpKP5q6NkXi1QRqQ0UP6bhjbiRJYnV1lWwti9/px75iJ3khSdfxrrs/2dvcmbapVTVWTq+Qvp6mnq9jYdG80CQe66RYk7HdJW3K7rahNTSaVYu51+ZInE8QHAiiOlUa9Qa/+MUvKJfL9Pb08tCJhwgOBNceEwv6n+rfMjCTFZnQQIilk0t47khHjW3S2qOWq+EMOvHE77+KqfDRFIt57pryfLtms8mlS5cIh8P09q7P9rHbVb7+58dZXigw+j57NV46v0wlVaFrXxyzaZC8nmH8Wopjd4xht6fDP/6JYVKLBRLj6VZxJofCnsd72bU7jqLIDA4OUiqVmJycpLOzkzfeeGNdUDY1NcVPfvITRkb34g8MMHNmhdxKCZfXgWKTMXSLeqmO6nMw/EQfX/j6frzu9Qu6N2sJbOZmiuWd9QMWZvLU8nU697WyE5LTOaYuJdcCve0oyvp+hFuJRCLMzc3dNdDz+R38yb88QTZdo2OHRWyEFhHofUiFBloTdmmxyJHuIJdWiizlqthUBQnQDAOPw8bh7gD+QgNvt5/Q4MbA4e///u9RFIUvfvGLv9bnq9hUwEJrts7CKerWk0WXy8anfn8fP/0v51m+ksTutmGZYDQ0IgMhPvP1fag3JiI9PT1omsb4+Dgvv/wy82fnOdZ5jNWlVa7OXSWxmuCff+ef86Xfayf77DCXzi2TTlbQTROfz8no3jgDPcHWWcY73C1tM5lM8vLLLxMKhVDMvZjG9kVWLNNCNww8vvVfXpbV6in17n95l+TVJKfPnGbMO4bzkHPLHdhoNMrCwgKhUGjTVU5ZlRn42ACusIvsRBYLi+6Humk/3C4KsXwEyIpMfG8cX4eP5VPL5KZzlJZLOINOHAEHqlNdF7CpbhVXxEVpuYQkSbgbBvZCk7rHjqc/gK3Th+y4ewqOaUKtqTPQNElML6PXdAzL4PWXXyd6OcrT33iaaDR6Xzs8hm6yuFjANCx6+oJrY8BNiiwhyRK1So3k4gr1mk4zVWHi+UlmLiVZSZQp1RWMYjc//5tzxCNuuociHDrRzeRslmKijM1pY98zA3zyi7vX7l9RFAJWgMXsIvVAnbMnz6LICsMjwyRWE1y9dpWRkRHGusdIXUnRtr8NdQcFE253899Cq2lMvzRN8nKKvEMmgUHDMLGNjhDQ/RSuLtNcLGLv9m9Zhc+8sYtWXSqSXKmsBXkADqeDtngbxWKRoaFbK/42t41Ab4DEhQT+bv9aNU6Hw0GpVKLRaKylaIWHw6yeX6VZbm6bHWCZFtVUle4T3SKLQPi1y+VyvPDCC0SjUb7yla9sSFO818BxK0qrgSWYraq6SGySCSWtC5q6ewL88b96hMvnV6lWmnR0B9i9J76uoIjP58Pn83HhwgVeffVVisXi2uL7+Pg45XKZmenrfOufPUL66QEunV5m5lqaWrVJ3bKIHehleH8bh3fHCNyx23W3tM2LFy/y5ptvEgqFcDgcdHW1FqvsDrVVWKuuo9gV9JqG60Y66Hb3efLkSV555RUMw+DZZ5/l+PHjWz52s9nkypUrnDlzhs9+9rPbpua6XXbcPWIsuVdixvchZXPb6H+mn6kXpzCXSzzW4SdtWeSqTbAg5LERlRXI1fG0exl4ZmDDl201UyWfyFOoF/inf/onfu/3fu/X9nwferSHpYkMuaUigX4few5v3/hzZCzGH/2rhzl/apn5yTSSJDGwK8aBo50bzgvabDZUVSU7n2X23Cz2MSelahlrySJ+IE6pVMLhcBAOOHnyqVY6h2VZFOs6pmmhmSYOef0k9m5pm6Zu4rf5MeoGjz/+ONmEl9evX8I0rU2DRoByrobdo9J9286hZVksn1xm4e0F9nbsZfzcOJ3tnSRPJ6kuVRn65NCmZypfe+01zp49i8fj4c/+7M82fTy7x07vY710PdQatEUfvY8eV9jF0CeHWj03Z1rntGqpGlqt1epAkiWQaU1amibVZBVZlvGHXfTG3UxpOp6oG1m9+9+OZUGiWCNqgS1Zwe5zYFkWjpCD+nKdVD3F+efPrwskdmp+Lsfz37tMcjqHhUW0O8AnvrqXoZFb99PVE2D3iW5OPn8NLW8hGVUyF+Z54byDnF1Gc8jY3W5MZ5x5h8JCvsrkWyX6wx4+97FhHANB/CHnpj30suNZPEEPk6lJtKbGuXPn8EXijE9OEY1G8bg9OENOinNFSsulTRfVNnNn2mbycpLklRRzdompXLV1vlqRaHpjLNQ1dLdK+VqGYNCBvMkOGkAuUcAddGMvNrG5bRuCzo7+YcYXk9g8689Z2tw2ZEUmfS1NZCRCoVjg5ZdfZnp6mqeeeorHH38cAG+7l/i+OMunlrc8B2qZFvmZPL4OH/F99194ShB2KpvNUqvV8Pv96ypeftAOnuhh5mKS5atJJFkiNOxj1571u4R3BnrQyo567KkB7mZlZYVAIMDVq1f5//xPP8YseilZCT77uc9y8OBBbDYbwQAEox5swyEuX0khrZTIpiu8+8YsF8fTjOyJcWwwQk+oNVe6W9pmb28vP/jBD7Db7et21g4e6+Tq2SWWL6ewLAt/u4+HP9Z6DcViccvKyYcPH+aNN95AlmUOH97+POSrr77K2bNnCYVCOzp/Kdw7Eeh9iHliHkY+M8LKmRVykzmipSbRGwtEVr6J3e8gdLCNjiMduO84vJ++lmby5RlWXknw8T97hqc+99R9Pw/TNHn79TmSS0VG97exd//G/lMdHX6+/W8eIZuuEQq7yKRXqVar27ZxiMW9PPvcKDC65TU3xeNxThw6gXvRTcNv0dM/wIHhvfQd6lt3WNiyLMYTZc5eS7IwlcXSTdxBJwcOtHOwP0zgxlmA7dI2m+Ump358jcnLSXzZfoaiQ3R2yJx9aZrcUpHIJhU+Dd0iM59j8Fg7PX3BtZ+XV8ssn1zGGXIS6A/SNj3EyLH9RCJBclM5Ft9ZZOzzYxvORMXjcarV6oYUlc2IAE9wR9y4I27aD7bTKDSoF+o0Cg30uo6pm8g2GVmVCQ4EqaarxPbG6EKCuSxTqQpBtw3PNjvBumGRLNUJuu2MNE2aZg0A2SaTt2t4fD4eOXECpaSQmcjcU6DXbOr8099eYGU8TbgviCRBYjLLz/72Av/8//I4nhsrzIoi88VvHGTXoQ7SV9PMvT3DRLWOEvcSdttx3PgclCUNr9eFZpjkqxoXy3Uav5rhEe8YoaHNV6iblSaKQ2GgfwCX04WsqCwmMuw+/BD7hm5L2bLAaN79XMpNt6dt6nWd9JU0JZvETL5GyG3DefOz64FUSaYcdSHPFnAnKzg3CfRMwyK/XGTvwW7UuoG7e/1u//XVEldWiphtu3jlWpJ93QGGYrcmdp42D4X5AuVEmWBHkEgkwtSVKdzS+gIrPY/2YJkWiQuJVhuFqBvVqWKZFrVsjUaxga/Tt5ZVIAi/Ti/87Bpn37nGY49/kk9+6v31WisVG1y5uEqs3cvg0Mbq1EMDYX7/v3mIqesZbHaZ/mEvhXwat+tWX9zNAr2d+tSnPkVHRweDQ6P8f/+7V8hOJ+g+NMixY8fWrlkp1PjhW7OsvL2EL98gZllIqoylm9Rni5y/kmLmSAeff3KAkTYfuq5v22zc0O1omsoXv/hFAoFb8xe/38kf/NlDXLqwgqGZ9A9H1s48b0dVVYaHDrCyktyw23mnZ599llqtxsLCwg7eHeF+iEDvQ84VcjH48UFqR2oUF4o0q00wweaxEegJbPklW1wsUs9Weezg4+zu3X3XD+N2Lp5b5eW/u0iz3GTq/Crt/zcfkU1WxV0OG11drcHG3dPDzMwMgUCA69evc/78ec6fP086neZb3/rWPT8Hv99P73AvfrcfU5FQbAparo7jtsmQZVm8fi3Jaz8fx5zJ4261mqFsWPzy9ArXn+jly88MEfU6tk3bXLiwytuvzZLzqDiJcvLnEzz758d45LNjvPr9SyQmsoS6fDdSTi3KuRqFlRLeDief/fr61a38TB6truHv9XN2Pod94Ainlms87HAT7PJRXChSSW5sgr17925yiTzXT09haIYI5oQdUWwK7qgbd3SLqo09Aa7/6DrVdBVvm5ejfWEcqsx0ukK+2sTjsOF1qMiShIVFQzcp1pqARJvPydG+ENXraZpAPV/HNxIm0utlSBki5LVT1ss0io17es7TE1lSs3lig2HsN3aPYsNhUlNZxq8k153vU1SZod4Q6dfmSaBiawvT5nNsmuZoU2RiPgd5RWai2MD9ygyBnsCmTbxVl0qj1MDtd9Pb14vX6yVbbuK5LZ3VNEwsrHtOS12rDJirUcvVyMsSpmXdCvJuiHjsVLq8KIUGuStpIl0+7LcVP2lWNTKzefztHg4e6KByJbUuTTtTbnB5qYCqynQE3eSrTS4uFoh6HWsLXDa3DaNpUM/V8XX4eHjfw5z/m6sk3yyS68wRGmjtVKoOlf6n+gn2BUlfT1NcLFLL1JAUCWfQSfeJbkKDoW3PhQrCB6HZ1Ln05jyVBQP3xzrufoO7+Ml3L3Dt9Xn8cQ/f/FeP0Nm9MbDp7grQ3XXr55lMplUfIBb7wPrkej1ORo91Mqkq7Hno1rnfSkPnp+/Nk3x1nlhFw9bmRXbd+pzbmgauRIXcu0v8TJF47lhs2/P+szNZvv+/nsKnHaZW3RgM+vwOHnm8f93PCoXCuoDwTpcurHL1NY1mzcMLP7nOp7+4sYXETZIksX/fY5x/9Vf89B8v87mvbF8wRrh3ItB7QLhCLlyhna+chkfCFBaLhO3KphObe1GvaZgNHYffgVbXqdfufgAXWjnpf/mXf0kkEuHQoUOcP3/+rq0MtmJZFs6Yk97BXlbOrGAaJl0Pda3bOZhKlXntFxPYJ3L4Yh5kv7218qYZeFcqLL4yw/NeO88djm6btlmrajQNE7fPQaNpUK82MQ2TJz42iNOl8t7L02TnW2XMARx+B6OP9DB60E1H5/r3ulFsoDpVNMNkMVdDlSUKNY1UuUGs04/e0NfS7O400DZM/KF2ypkqgXZxOFl4/7ztXnoe72HmpRnKiTLeNi+HekP0Rbws5avMZSpkK01My0ICbKpMX9hDb8RN3OdEVSQaqkI5UabreBfxXVEaJrjsreBHq2gEb9vR3glN0zENE+W2/myyImMZFtomZfvzs3nmZ/MUXTJdmwV5d8zDgm4bq5rBQqpMejy96XgYGYmQm84h+STsztYOYviOVPhatoYr5MLbcWtRxjRNMukqiipvSDm/M23TMq3WOV4J5E0K3siShN3nZPB4F8nxLJnZApZhItsUTM1AVhVCfT6e/sIwUd1OhdS621carfN+kRuLXyG3neVCjWpDXwv0bro5djUKDfYM7KeR06nn6nBb5pmsyoSHw4SGQjQKDYxmq6qvw+9AsYuFJ+E3w25XeeqLu1lZLLDv8NaBXjJRYnY6h8OhMro7tlbJ807VQgOjadKoaFSqm3/33ikSibC6usqZM2f44Q9/SK1W45VXXuHP//zP79rSYDtf+sZBGl/W1/Wtm0iWWbqQIFxqYu8LICl3nA+0K6hdPoILRVIXE7zhKvPHzx67867X5LM1qrkaSK3/7uu/e9p5LpfbMm0TYPp6itJqCYffwfXTy9sGegBLcwWMnMT0xQSIQO8DJwK9j6hgX5D9f7APSZbe927QvkMdTD7SQ2I6x95Heujo3FnQsby8TEdHB+PXxzkw/AhyrY2Q6/5W5G72nWk/2E50LIplWhvOjpyfSGPM5PBF3SiB20oh2xRs3T6CM3lmLyZ4z5bbttpm92iEwb4Qcwt5wm47u451r72Hxx/p5eDRTiaupynm68iyRFdvEH9Aotnc2L/O7rFjNAxUWSLuczCTruB1qATdNvSGjmyTtyye0j4Wpd7hxfUBVUYVBIDYnhhYMP/GPLnpHP4ePyGPjZAnwEjcS7lhYJgWsgQOVcZ3Y8JkWRblRBmjaRAeCuOOuVs7iDfut56rt3pKjtzbxKdvMIw37iEzXyA6EEICsktF3BEX/XechTN1k8Xzq6xqOl6fi512Ogi4bGSrGrOXknQe6cR5RzP4YH8QT8xDcjZJz76NC0B6Q6eWqdH7eO9avyxDN/nxP1zk2sklFEXi2LNDfOzTt9LQ76y2affYW0VRak0M08Q0Wz0Nbyo3dJx2hU6nncN/uB+9N8D09TT1WhO3287g7hhuX4PhoX5Wz61uCGhdNgWbLFNp6HgcKqW6hkOVN+wcSkhru5LBviCjj/dhaAahoc0ngJIkbXi/BOE36eiJHjixdRukd96Y5fUfXqWSqSGpMm2DIb787SPr+mze9PEv7uFkxE2808/Q8M7HqnQ6zS9+8Qvcbjf1ep2BgYH3FeTddHuQZ5oW56cyqMsV1IBzQ5B3kyRLKBEXrlyNiRkLzWr1Ut7MvoMdlL6xH4C9BzYeu7kfXb1BLkfcaDWd3t13T9M//FA3hmHS2Rv8QB5fWE8Eeh9hO6m+mEqWuXY5yZ4DbZumYwJ4PHb+8M+OYxjmWpPknThw4AAHDhzg+e+/y8//y2WclQG++/87yZe/c5S+XfdWArnRaKz1ndms4l2taTA3lcWlWcibpBPdXIkuzBe44K/ztaePbPlYwd4gz/3zI+SXS7h8dsJ3nOux29UN5xSnp6c3NKMHCPQHWD67TLPc5EhfiJ6wG4eqEPHaW5PsLj+ets3fd0mWRJAnfOAkSSK+L44r7GLxnUVy0zkUp4In5sHuUoncERgYmkEtW6Oer+MKutj9ldbq7eI7i2SuZ1DsCkbTQHWpdJ3oItgf3PCYszNZCrk6+w+1b0gj9/udPPmFXbzyvcusXkliIeEOOnnks2PE29ZP1OqFOonlEhW7TNs9VL502RWyDoV0okwtW9sQuDj8Dvqf7ifz3Qy5yRzuuBu7x45pmNQyNZqVJm37W+ehb7pyaZWLv5rFFXSgNQzee36S3Qfa1+3q31751Bl0EhoMUTq9QtTnYKVQJexxYFNkqk2dUl1jLOrBZUh07GsjMhLh0NH1rRzm5+fXnq+kSOvSuqM+OyNtXsYTZQo1DZsis6fDT+i2psp6XUdSpbWUS5vbxsgn1vfjEoQPk2y2yhs/vkazptO+O4ahGSxfT/Pq8xN8/Vsbv+cHhsIMbHFWdzvRaJTjx4/zxutvUit76e/f90E8/XVqmkE6WcbZ1FHu0rJE9thQlvJYdQeFmoZ7i/6BqirzxDMb5yZbKZVKd22FcPRED6Gom1pVW9dgfSsej52nnh3e8XMQ7o0I9IRtvfTT61x+ZYbUZ0b4yjcPrv3c0E3yhRp2u4rvRirQvQR5t6tmZUzFTmjQTjZRZep6+n0FepsxrVZalARbN3tWJPSqTqVSvWuTdF+HD98H0MvF3+Wn41AHSyeXsLltRINOTM0kM5HB6XfS9VAX8n2+r4Lwfvg6fYx8doT8TJ701TSl5RJafWMqk6zKuIIu+p/sJzwcXjsX7O3wkp/JUy/UsXvtBPuCeDs2Nm9vNnV+9J/OUC81cXuOMzK28bN//OFeunuDTFxtVX8bGInQ27dxh8nUTLSmAbK0ZfXbrUgy6MattMU7BfuD9D7bi1pQyU5mKeVLSEqrEEnv471Ex6LrUhYbDR1TN/CGXTTrOsVkhdqNNOytmqS37W+jMF9gd66GK6iQrDTRTQ2XTWFv1Etb3SCyN75p+mulUlkrbuXvaS0QVVNVfDcyLCRJ4kBXgI6Ai5pm4LYrRO9IP60kK/i7/Gu3EYQPu4XZPJVMjfiNnryyouIOu1m4lsYwTCQJUskKpmkRi3s3tG3Zqfb2dtrb2+lo28s//Ps3OfNqghMnPtg0xLV5jMWW7VVukiSJWq2G29WB+cEcGwRa5xG3S9u8abNCNsJvhwj0hG31DodZncnSfSNv2zRN3ntrnrOvz5FPVlBVmYEDbTzxieFN0yB2wh/xoGgm+dUGAZsLn//e04DuFug5bQqBsJuELGHW9HWHl2/SS00qdoND3e1bpm3ej3K5jMez+epboVhnVbYohRxIuQZB00JxKLTtayO+P/6BBJOCcL9Uh0p0V5TIaGRt165RbGAaJpIkYXPbcAZb/R7v3En3xDwbmmpvxm5X6R2LkUtViMa3Xinu6PRvOON6J0mRUJT7q3gnISFLbLuw4oq56D7cTefRTrSq1tpVD7k2LcASjdnwdblYvZ5GQqL/SPtaxbo70zZv8rZ7GfrEELOvzuJMlOl32TFkCbtp4UQmvC9K/9P9KHalVegpUUaraExMTuCNedl7tDWxVGwK8T1xpl6cwh1zo5tW66ygLBGJuFE3yWrQGzp6XSe2JyYWl4QHhttrR3YoNGtNXDcWpfWaRiDmZvJ6mjdfnCQxkwPDItjp49jTAxx/5O7VrLfS3RuiZyxG3+i9tZDZCYeq4PTaKasKZk1H2a6PZdNAs0x8ficuUaztI00EesK2Hn1ygIcf71tLp3r95Wle+95lQMITdaE3DC68NE1irsAf/MXxTXtQ3fUxPjFEvdLk8qlJjj41xuHHtt9N20yj0di2VYMiSxw60M6P3lugsVrG2etfl99uFOqUdAM9rvHw3p2nMexEMplkYGBj/5zXX57m3ecnqKSqWFgoqkK8P8Bnf38/3WI1TPgdcrOE/lbVOt+vL9+WLXBTKlXhjRcnyWWqtHX6efKTw2vZA1uxuW34Ag4cmQq1poFrh0VBdMMCzcDjdW3aF+5ODp9jXUXfOy0sLOALuPln//pJrpxPoNhkDh7pxH5b+tSdO5s3BXoD7Pm9PeTn8hTmCxhNA4ffQWgghK/Lh6zI5GZyJM4nKCy0fn/mzBky5Qznjp/jK//iK7hCLiJjEXKzOU7/fILlbJV6SUOSwR1ysetwB8O3pVTpDZ38dJ7YvhjheziXJAi/6wYHw/TuiTF1ahmnz4He1JFkmY6BID/+6zNUslX87T4kRSI9V+D5vzmHbpgbKk3uVDDo4gvf2L1tO4P7ZVdl9o9GefnMCr5sfdtAr7ZapBH2cHgsStjzwfSnu1tLLEM3KZUbuN22dWOd8Nsl/iWEu7oZ5JVKDU6/PI3iUIn23iqt64u4WLmW5sy7Szf63t0bl8/BZ//4EB17TY4c2XPPt19aWmJubo7u7m4ikcimTTcty0IqLNF+JEzinQy+6Txurx1JldErGkXTwBj20+7MMzzQd8/PYTP1ep0f/vCHOJ1OOjs716VqnTm5yKvfv4SsKsTHoiiqRL3SYHkiw0+/f4Vv/cuHt6wKJggPukqlyff/+gzLlxPYPXZmTy+TSVX45neObZtaZffY6d/fzrWpLIlaE5d9Z2dYi3UNf92k72h0QysTy7Qor5Z58Z9eJJvJ0t7Tzqe/+mnUTSYyuq4zNzdHV1fX2uf9iY+tXzjaKm3zdja3jdjuGLHdG9NY09fTzLw8g6EZeNu82Nw23Ek35fkykUaEyZ9PMvzpYVxhF0m7zNRsHqncwBVxI7lVKpkqZ381g6LI9PYGqKQqNEtNYvti9D/VLypmCg8URZX5yp8c4fVuPzOXkji9Ng6c6OXahRUqmSodu2Nrxzk8ASfJqSynXp7myPHudYVQ7sVWizg7YZrmtrff1e7nV90uytkGvmQFdZOzekahTjZfwvvIIPt7ghi6CdL9H68BeOmll6hUKjz77LOb/n56KsML/3CJYqqCw2vnsc+OcuzE/e+MCh8cEegJOzY/m6OUqhK7o9KdYlOwe+xMXU7cV6D3fr32ymu88fob+AI+vv3Pv83Y2Ni639frdS5dusTo6CijYy5e7lzh+oVVUnN50C2kbi9tIxFiwQohutfSNkulBi6X7b5z9h0OB6urrcbwXq93bYA0TZOzb85haibxwVur506Pg/hwhMR4misXExx9qHuruxaEB9r0ZIbEeIa20Sg2p0olX2fhUoKVpSI9d2nPEB4O0xV1kyzUqDZbZ9G2o+km9VqTfreNjn3xtUmfqbfOyqaupigtlihfLbM6t4qtz8Y15RqxPTEio5G1olaFQoFcLsfg4OC2E7Xb0zZzuRrpVIWRHaZ51fN15t+YR5Ilgv1BlnI1spkKKd3BoScfpb8zTnYyy8JbC/Q+O8CFU0sofa2CK3qyilFq4rWplJJlrv1qGv+TA/g7/fQ+2ktkLLKjAl2C8GHj8zt47kt74Uut1OZKpcmvvn8ZT9S94cx+oN1LfqXM0mKB3r4Q49eS5DI1HE6F0d1x/Ds4WiJJEqa5sfXLTtysIL4Z0zRZnLjMvmEXVw2FwpUMnpk8qt+BZFdAN9GLDWoy5Po8fOGJPoKyxF/+D6+j2GW+8WfHd/T8N1OtVrl48SLJZJI//dM/XVc0K5er8dP/cp7cQgFvm4dSusov/+4SgZB7x2Ob8OsjRnXhg3Gjr9ZvUmm5RHYyS1e2C/t1O+2D7XhLXmq5Giu5Fbq7u8nn8ywvL3P48OG1AO6LD/eS3tfGcr6Gblr4HCp9EQ8/+dEPGLzRx6/Z1MFqFVRQ1XtPezA0g/S1NPXpOgeeOLBuFaxYbJCZL+DZJA3O5lSxDIvEcvE+3xVB+PDbNFCSYCf9ErztXnYd7ybz/CQz+RpmwInXoYJpoek6uq6haRo2m42GZpAq1Omsm+x+tGetIbje0Jl/fZ7VC6uoDhVPu4fR8B6minMceuIQtWyNqeenuPzmZUKHQ7T1tKGq6o6KFNz++tKpCpVSg2qtidt193EmP5unlq0RGYswnihzfiGPYVmYzhiXMwb+YBNfp4/CXIG5S0mKiQr+Lh82rwNbuxc9X8dqGBgRJzXDpOvjg3Tvjb3vFjuC8ECRILla4sXvXyE5ncVstgo0vdrm5eFPj/DYUxuPYqy7+X3u6E1MTHD+/HmazSaqqq6r1F2tVjl16hR79+7lSDjMuf48r0WXyExlUVcrKHUNE2h2uHF0uHl80MUzu9oYv5okPV9AViXSqcp9B3oOhwOPx8Pv//7vb6iMvDifJ7dYJD4SRrEp+CJuli8lmJ/OikDvd4AI9IQd6xsI4Yu7ya+UiN62qm5oBlpVY2h/29rPdN2kXG7g9zs2DAqb0XUdTdNoNpvYbLZtB0rLslg9t8riO4toVQ2bz43N62PvrgPMvjJL4kKCN5fexPSbfP7zn+fQoUMb7iPqdRD13lo1KxQK65qk2+0qqirv6LlvZvn0Ci9+9xLNdDfRWve6MueqKiPJEuYmzZ4BLKx7rhgoCA+SgaEw7WMRli4lsbls6HWN4Ud66NxBNUhJluh/og+tpiO9PstCtkbBIeO2KcxfuU6z1sAbDhHs7kLSTLo1i2PHuhn9+CCqU8UyLRbeWmDlzAqBvgAZzeDqSpFUqYHce5jTq2X6ox7aYm5Of+802Tez/Ml/9yd0Dnfe9bndmbY5MhqlVtN2nKadm8lh99pp6ibjiRIOm0LQbQMLlgtVplJlHhqIUFoq0czVkGUJQ79RmEaVUW8uLqkSqmkRGQ6JIE/4yPF47HSMRph4ewF/zLNuV6+wWsYbdvLei9Nk5vJEB4I4PHYMzSC3VOJX/3AJj9e+obXJne6nIJTH4+HKlSsAHDx4kImJCVRVxeFwMD09zcMPP7x2NOVwb4iRNh+TyTITK0UqVQ2HQ2Go3U89OcOjR/egKjKju+I8/bV9yIpE/8Ddm6Fv5tzpJU69VOHJ557D59s4BqtKa05jaCaKTcHQLUyL+86GEj5YItATdszrdXD848P86rsXWZ3I4o240Bo6lVSV9tEIh2+kGq6ulPjhfzlHMVGmb1+cL//hwbsezP2rv/orrl+/zi9/+Uv+4i/+YttGo9nJLHOvzyG5VBacMvOJEoFjT3JJlxmI2KhdGmfx0iKO/Q7S6fSm/eugdfZGb+gATE1ObWiSfr9BHkAxXSVT07DaQqRWyug1fW1C5fU66N4d59obsxu+ZCqFOqrTxsCwKMYifHR5PHa++qdHePOlaXKpCrEuH08+O4xy28TBMEzOnVrCNC2OHO9e9zvVqTLyqSGcfjsT7y0xdyVFKlHGl4dKSSdYNIjk03SORNj9zAADT/XhDLQCsNJKieTFJP4ePwuVBufm8zRNE5/Dhs/rZSlfY7lQI0SFlJ4mrsSYOjPFwPD2q/ywebXNezmLazQNJEWiqZtohoXHcWO8kloV+WpNc+3/w2EXsYEgS1eSuP2OWymphkUpWWXPk314vdsXtxGEB9UjzwyyOpll5WoKX7sXWZEpJSsoqkykM8D0qSXaxyJr39uKTSHaH2T1eprTr89x4HDHlnOE+03d7OzsxOPx4PV62bNnD3/5l3/JxMQEX//613n88cc3XO91qBzqCXKoJwi0mrbPz8+iGg1UtTXnUlV5wznhezU7nqa6pJNYqGz6+/6hMF17YsydW8HhsdOoaER6g+za17bp9cJvlgj0hHvy2NP9OF0qZ16fI79SQrHJHPrEIE98coRwuLVafPLNWZYvJ/DGvVx/e4HrhzvYf2j71e5Hjz/K+Jlxesd6CQW3XnUyDZPEhQQAVyoNZlIVAi4bfpeNasPg/GIBGlV29+9m7MQYBw9urOan13Vy0zlS11LkFnOkM2mWc8s88oVH0Ov6pg3X71Xnnhj7x9OUSw12HevE7luflnXi6X6WxtOsXkvj7/RhsyuUc3XquSpjj/UxLNIdhI+4SMTDF76+f8vfXzq/ys//81kss1Vk4MgdZ1pVh0rH4Q6K80WMpRJew6IUcLC4otHbGyIeduPxObA5lXWf+dxkDkMzqCsSF5eKyLJEh+9WURevU6Vc18nWXTz7pS9jz9QJe8Ktgk+bZCLoDR29piPbWpPC91OowRFwUF4p44ur+J0q6UqDNp8TzTSpaQZhjx3rRtMsh8fB458a5SfLJVaupHBHXVgmVLJVQt0BHv34B1tdWBA+TIZGonzhT4/w1i+nWJ3KYZkWbYNhjj3dz9S1FJIib7rb7Ym6Sc/nKRYbBIMbiz1dvnyZU6dOoes6R44c4fDhwzt+TtcuJ7EzSlu4nVMnz3Hq1ClisRhXr17loYceuuvtm80m3/ve94BWqudWhVPu1WMfHyIY9bDnwOaBm8tl4/f+2WF+9QsP6aUS3pCLRz8+eN8tt4QPlgj0hHsiyzLHH+nlyEPdFIsN7HYFzx2le202FSzQb6w+K9ts3+sNnZXTKzSvNFGn3ER72rj+k+t0Hu3E372xZ1YlUaG4VKTmsbM4nyXud2K/cf8OVaFY07DaBxht8+EsK8jm+sduFBtM/3Ka7HQWm8tGzajxqzd+RX41T22lBhnY/dzubUun70RoMMRT3z6M0TBwx9wbJneDQxG+8O3DvP6LSZJTWcq6iSvg4NjndvGx50a2fc8EQQCb/cZEzALVvvHzYjQNZn81SzlRprk7wmKqTKWmIQ8EmFckih4HeyJuEucTqA6V/qf60Rs62eksrrCLhUKdcl2jO7TxLK3XqVKsN1kp1NkddVNcLNIoNtZ2BQGa5SbJy0ky1zIUs0VsDhv2uJ2QLXTf/TEjwxFSV1JIpsnh3iCnZ3MkSw1kSWIg6mG0zUstW8MZdOLv9hP12lH/q+OcfHWWpfE0kk3m4McHeeiJgbsWtRGEB93orjiju+KkUhVMwyQa9aCoMjPjabC2OFphthZ0tjpe0dPTw49//GN0XecLX/jCjp/LlUur/OSvzlDJNclcnudSu84f/MEfsHv3bqLRnS38hkIharUaQ0NDPProo9tem0pV+OVPrpGczeMOOnjk2WH2HWjf9NpY3MvTnxje9v6CQRdf+sbGhXXht08EesJ9URSZUGjz0uUnnuwnuVQkvVhg11P9jO2Ob3qdaZjM/mqW628vMK8b+B56jCsNmex7i5QSFcY+O4K/a32wp9U0TM2khoVhmGtB3k1eh0qy1KAhS9jqrZV0243UKENrTfyyU1lCQ62zKUpJoSk1USMqrnYXpZkSs6/OMvKZkS2bBhuGSaXSvOuhZtcW789No7viDI9GWV4q0WzqRGOe+z4oLQgfNXv2taP8+XEsC3bt3TjGFOYLZKeyFPx2LiwVcNgU2kMeZBkM0yJbaXJ2tcSxuI/UlRSxPTFsLhtm08Tms1Es1bGpMltVmXKoKrmqjtKmYGQNjBsFG6C1oDT14hS5qRzOsJPZxCwLMwu0+9ppJpvsem4Xwf7gtq+v2Wylld+e9h7oDRDqD5GbzhEaCvHkWIxiTUeWIeSyY9Q0qpkqfU/0Yb/RY2t4JMrwSJR6vXWd6G8lCOvFYutbFPQOR7j8q1m0uo7tjgyfSqrKwPGuLb+r/X4/g4ODFItFYrGN7VG2MjuRpZKt0n2gnWKqglk3OHLkkU13De9ULje4ejFBpdxEleN865/9s2373dVqGj/8z2dZuJTAHXCRXynxT8slHH9+XBRPeQCJEV/4wIVCLv7kvzmBaZrbnnMrLhZZvJDgmmVSliyCPhcN3WSiqSPP5QicX8XX6Vu3GyYrrYnXjSQoLGt9IT7NNFFkCdlqFWW4/fxbcbFIdipLcCC4lpLhcruo1WuMjY7x5DNPgtlK3SouFgluseL90+9eZPJcgk9+Y99dU1LvRpZlunsCd79QEIQNxvZsvohkWRbp62ksSWIqV0VVpFbRkhsUWSLmc7CSr7HY0BhqWuSmc7TtbwO5tWqvKhKmuXVBBcO0sCsSlmkhy/K6sWb17Cq5qRzhkTCyKhPMB5ldmsXV7QId5t+cx9vh3bKdQa2m8b/9L+8A8Cf/8sRaRU7FrtD/TD+WaZGbyqG6VfxeB5ZmUUzkAeg81knHkY4N9+n8AFLSBeGjYN+hds7vibFwMUGwy48n6ESr62QXizj9Dh56sn/L285MZ8nOxalVA1y6sLrlLtmdXB4bkixRTFWpZGoEO7w7Or+7ulLiH//jaVLTWUwL/I69vPDDCb70jYNbZgbNz+RYuZ4hPhjGfmNcXLyQYOJyUgR6DyCRHyb82tytmElpqUSqUKdoGLQHXLjsrQpyLpvCqmSRnyvQLDXX3cYddeMMOPEb4HOqZMoNuDEXM03IlhvEvA4cNQ1vmxeH/1YKZnYyC7A2ubIsi5VCg8OPP8tnnvsMqk1Fdahrk6it1Ko6etOgcaOQiyAIv1tMzaSSqNBwKORrGv4tJkw+p41EsQEOhcpqBdWp4gq5aBQatPmdSJKEtkl1XNO00E2DzmDrWrvPvraD1ig1SE+k8bR5kFUZw7DIazLhWDuHjxwm0BOgkqhQmC9s+fwNw8Ro6OgN/VbVzBtcIRcjz40w/JlhPDEPhmZgmRbR3VHGvjhG3xN9opKmILwPbpedL/3JYUYe6aFRbrJyLU1uqUikJ8Cn//gQu7ZYYDJNk1/8/QVWr2QpLlR58e8vUi43dvSYDz3Wx8gjPRgNnWCHl49/dd+OGra/9sIEq+MZYsMRuvbG8YRdXH5tnksXVre8jWlareretwWCkgy6YWx5G+HDSyzxCb81hmZgSsAdu3KqLKHTatFwZwsCu9dOdCzKwlsLHOjwc265yGKuiiy3dvei3ta5GzNXJ7o7um6VvZ6rY/PcmvBlyk3em80iudopaxI3e5TaPDbq+fqWz/uLf3CAVKJMd6/YiROE30WWaWFZFhZgWaBImy86KbKEZpiYkoRpmUiyRGxPjNxsjni7l86gk/lslajXgfNG8NTUTVLlOm0+J51BJ6WpHP3P9K8tIDVLTbSyhruvlTo1n60yW7PT07sXSZKQVAnLsjYsYt3O63Xwzf/TwwD4NjkvbHPbiO+NE9sda1XilCWUuzSGFwRh52IxD3/8Xz3EwlyebKaK06kyMBzeNvXZNKFabOLw2lGcKo2qRq2m76i6rcdj55vfOUY+V8Pltu9oN0/XTRaupfFE3ag3Pv/esIvCcpGl+TwHj2yecdTdGyTaGyQ5nsHb4W0tVnntotr3A0oEesJvjTvqxmtTsBkW5bqO16limBaFukYfEr6oe0O1SoD2Q+2UE2WYzvFIh5+sZdLULdw2mZABZrpG++EOwsPrWzRIioRl3Fodd9kVfDfSmVy3rYCbhrkuQLyTy2Wjt//++tEIgvDrp9gVbC4b9qqGy6ZQaWj4Npk4lRs6IbcNpWnivHHmJtAXwNvmpbxU4mhfGFWWWcrXyJSbSFioikJ30M3hniDNVBVX2EV48NZYczNl3DRMZFXG61QJuO2E3bfGMsuykJTtq29udQb6dpIsfSBVggVB2FxPX3DHhYtUVebIMwO887Nx9LrOnsd6N5z/244sy4Qj93I92F02GqVbu4aWaYG1/Vlcn9/B5//4IL/43iXyK2U8YScPfXLkfR9FEX43iW8I4bcm2B+kZzBM/mqSOUunUNOQJYhJMr0uB/F98U1TkOxeO0OfHGLp3SWyU1miZW3td86Qi+jjvXQe61yXlgCtQga56VspmR6HytOjcZDAdqPwimVZaBUNf8/Gip+CIHw43NyZK744RXfIybXVEi67inpbcFXXDHTDpDfgRKoaBAeCANg9dvqf7mfqhSkai0WOdwcYbfORr7Z24PxOG2GnSnm1jCRL9D3Rhyt8KyhzR914Yh6qqSr+bj8xn4NP7m5bWzxqlpvYXDa8bd7f3BsiCMJvxDOfHGF4LIZhmvT0Bn+tjyXLMvsf6eHVv79EZqGAw2OnlCjji3u3bIVwU29fiO/8m8eolDVcbptobv4AkyzL2vq0uSD8mpVWSsy8PMPyTI5SXUeVJNpiHrqPdtD9cPeWlS9vqmVrlBNlTN1Edaj4unzYPRt3AQGq6SpXvn8FxaHgjmxekaqSqmDpFnu+umfd5E0QhA+Xer7O1X+8SqXS5JqmsZirocgydkWioZtIwGDUQ79mEewNsPvLu9ctDhWXisy/MU9puQS0UrolSUKrapiGibfNS/fD3YSHwhseO3UlxeTzk7hCLlyRW+OIXtfJz+RpO9jG0CeH3ldPPUEQBF03eeX5cS69NU+jqhPq9PHEZ0bYs29nRWCEB58I9ITfOq2mUZgv0Cg2kFUZf5d/095zH4Tl08vMvTaH3WvHHXOvBZKmYVJNVWlWmvQ/1U/H4Y1V6wRB+HDJTmaZfmmaekWj6FZYrjZpaAZeh40Ohw13uYmv3cvwp4bxxDemTBma0arWO5mllq0Brabl4aEwgd7AllUzLdNi+dQyK2dWaJQaKA4FUzORFZnwaJiBpwewue9+BkcQBGEnajWNWrVJIOhCucsCufDRIgI94SPFMi1WL6yyemaVarZ66yyeBa6wi85jncT3xcVKuyA8IPKzeZZPLVNcKmJoBpLUaolgd9sJ9AfoeqgLzz2co7kX1XSV3GyORqGB6lQJ9ATwdfnumqkgCIIgCB8EEegJH0laVSM/l6deaFXXdAVdBHoDYpVdEB5ApmFSWi5RTVUxDRPFpuDt8OKJe8SijiAIgvDAEoGeIAiCIAiCIAjCA0bkjwiCIAiCIAiCIDxgRKAnCIIgCIIgCILwgBGBniAIgiAIgiAIwgNGBHqCIAiCIAiCIAgPGBHoCYIgCIIgCIIgPGBEoCcIgiAIgiAIgvCAEYGeIAiCIAiCIAjCA0YEeoIgCIIgCIIgCA8YEegJgiAIgiAIgiA8YESgJwiCIAiCIAiC8IARgZ4gCIIgCIIgCMIDRgR6giAIgiAIgiAIDxgR6AmCIAiCIAiCIDxgRKAnCIIgCIIgCILwgBGBniAIgiAIgiAIwgNGBHqCIAiCIAiCIAgPGBHoCYIgCIIgCIIgPGBEoCcIgiAIgiAIgvCAEYGeIAiCIAiCIAjCA0YEeoIgCIIgCIIgCA8YEegJgiAIgiAIgiA8YESgJwiCIAiCIAiC8IARgZ4gCIIgCIIgCMIDRgR6giAIgiAIgiAIDxgR6AmCIAiCIAiCIDxgRKAnCIIgCIIgCILwgBGBniAIgiAIgiAIwgNGBHqCIAiCIAiCIAgPGBHoCYIgCIIgCIIgPGBEoCcIgiAIgiAIgvCAEYGeIAiCIAiCIAjCA0YEeoIgCIIgCIIgCA8YEegJgiAIgiAIgiA8YESgJwiCIAiCIAiC8IARgZ4gCIIgCIIgCMIDRgR6giAIgiAIgiAIDxgR6AmCIAiCIAiCIDxgRKAnCIIgCIIgCILwgBGBniAIgiAIgiAIwgNGBHqCIAiCIAiCIAgPGBHoCYIgCIIgCIIgPGBEoCcIgiAIgiAIgvCAEYGeIAiCIAiCIAjCA0YEeoIgCIIgCIIgCA8YEegJgiAIgiAIgiA8YESgJwiCIAiCIAiC8IARgZ4gCIIgCIIgCMIDRgR6grAJ07R+209BEARBEARBEO6b+tt+AoLwu0A3TOazVa6uFJnPVjEtcKgyo20+xtp9tPmdv+2nKAjC7whDMygtlWiUGlimhWJT8LZ7cYVdO7p9PV+nWWkCYHPbcIV2djtBEARBuBeSZVli60J4YCwvF7l0ZpnJi6vUKxo2h0LPaIx9RzsZGgojSdKG26RKDZ6/ssrMShEzWcFV1lB0C82uUAvY8bT72N8d4JldcZw25bfwqgRB+F1gNA1SV1OkrqQor5axbu78W2D32QkNhojvjePr9G16+8JCgcSVFNOXkpQKdSzA73cwuC9O2544gd7Ab+7FCIIgCA88EegJDwRNM3j+p9e4+Noc9VwNh9eOYlcwdJNGsYHda2fwcCef//39eL32tdulSg1+cHaJ5WspIktlpHwDJJAUCTQTVIVmxEGuP8CRPW08t78DuyoyngXho0av68z+apbExQR2j526VyXfNDBNC4cqE5Zk9FwNh89B/9P9REYia7e1LIvV86uc+fkkUytF8jYJ09lKqJHrBgHNZLDDy9FPjdBxuGPTBSlBED6c6prBbKZCuabjcij0Rzx4HCKhTvjNEH9pwu8kzTC5PJ/j8qUE9bpOX3+Ig7vixHyODdcahsnPfnCF889P4g456dwbR5LXT5Qq2RpXX5+l2dT5+reP4HLaMEyLF6+usnQtSXyqCKaJ2uVFUm4FcmZDx7ZaJWbAWUWmze/gkaHor/31C4Lwu8M0TOZenyNxIYHc7uF8ukwiWUTTTZAsQMLrUBlr89Gumcy+PIvqVAn0tHboshNZ3v3xda7ma+ghJxG3DduNBSNNN8lVNS6mKug/HecJt53oLjHGCMKD4PpqkV++PEX6ahqr1ASXjdCuCE8/PcDBvvCv9bEN06JU13DaFJGN9BEmAj3hfdMbOrWKhqJKuLyODUHWPd+fYfKT16a5+ItJ5Fwd2YI5m8zlQ2383tf20x1yr7v+yuUEF1+ZwdfmwRtxb3qfnrAL1aEwc2qZd0cjPP3xYRayVWZWSkQWK2Ca2Nq9G24nO1Rs3V60xRJun8q5xQKHe0Ni0BSEB0Sp1GB6IoNqkxkZi2K3b/xaLC2VSF1OobZ7OLlSJFWuE/U4cNwYB0wTinWNsws5DnYHiVebrJ5bxd/txzIspt9bZCJTwQo5abtjscqmysT9DjKKxESmQte7i4SGQihijBGED7WFXJUff/8y9QtJwm4bqs+BUdUpvLXIz1IVnH90iLF2/7b3sZyvsZSvIUvQF/EQ9W5c7N7MRKLE65dXSScqOF0qh/a08fBgRGQkfQSJQE+4b6sLBS6dXuLqqSVqFQ1Zloh3+9l3ood9RzqxO+/vz2titcTFX07jL2u4h8KgSBj5BslTK7za6eebn91Fs9nE4WgNeBdOLmE29C2DvJscHjs2h8KldxZ57MkBrq4U0RIVlGIDtWNjkHeTpMgoQQeeZI1kosx0usKejq0H52alidEwkBQJu9eOrIiBVRB+F2WzVf7hP5wmcT2NpMiMnOjia986gnrbZMg0TTITGUzTZKXSJFms0xl0I9/2sZZlCLptFGtwfbVMW1+IwnyBSqKC3tCZnchQdip0eLaepIXddparTeamMowsFgkNhDa9rp6vk5/LU06UMTUT1aHi7/ET7Aui3ueYKwjCB+/05QTVK2nicQ9KsFXQTQmCLy2z9PYi/2hafPLpQXbvb8Ptsm+4/dtTaV57e4HqQh5kmcBQiE892sfezu3P8i7la/z41Rmqp5bx1HSqiszLczmk53bxxEjs1/BKhd9l4ltBuC8X31vkxe9eopCuYg84cHrtmIbFzLUUs5eTXDm1xBf+5BD+4L1Xk5uYSEOqhqs/gHRjwqWGnPiyVRbG0+Se0fi7/+2vGBkZYe/eYyxeTuKJe3Z034EOH9n5AhPX0ywVariqGhasPc5WZJ8Dc7GEVWiQrzY3/N4yLfLzBa6fW2H2UoJaTUdVZWKdPvae6KF9NILNbbvn90IQPuqq5SaXzixz/dwK1UIdX8TNriOd7Dvcgf19nnM5f2qJlasp2kYjNOsakyeXmHy4l11742vXfO9vv4d50aR/eJi5ZAmv07YuyLudz2ljOV8lpelEajrFpSKSLJEtNlD99i1vByBJYHfaSOcb1LK1DYGeVtNYem+JzPUM9UId1akiKRJm02T1/CqeqIf4wTjtB9rfd1aFIAjvT10zmJnI4DJM5MCtBZ7UdJbsRA5rpcR8rs4Pr2Z4sy/IM1/aRThm8fzzz/OpT30K1Rfh9XcWkM6sEDMA06SQqvKyXaH/U9uf8bu+WqJ4OUG8YaJ2+3EWGzBT4uyVBMf7wyIj6SNGBHrCPZu+muT5v7tAtWG0zsPdNqcIRN3Uq03GTy/zE0XmG39+7J5TkAyjVR/ozsmKVjdIXknwvb95k9mZLKurr/Pii+/hqewlEt5ZtTq724apmZTLDTTdRDKsHU2KJFnCsiwky0I31tcvMnWTy69M88YvJskU6zQ9KqpDxWpYTJ8rcensCnsPdfDUV/fijm6/6ygIwi3ZVIUf/KczLF5NgV3B5lBZXSgweWaZq4c7+OKfHMLru//WJ7puYpkWsiJjs6toTZ2l5RU8/sbaNSuLK5SulbgyP4syeIiof+vPsCSBKssUaxpRVUKraNg8NnTTRNlB7CVLoBvWrWqeN2hVjelfTpO+lsbT7sEVCpKuNNFNE5fPTsRtQ8vWmXlpBq2s0fNoz6bjmmVZmLqJJEnIIoVLENap1ppMXU9j6BZ9Q2FC76PtiWWBaVhISGvFlTILBTKXUqBKqAEnUsSBGpZYnlzlu/8+zcijTqanp/l3/+7f4Rs4gFbsJqZb2PsCWJaFf7ZIdqHASqHGcHzzyr4ApbqGWjOQPDYkVUbxO7AVG9SrOnXNEIHeR4wI9IR7YlkWJ1+dpVho0LU7xmbF4ZxuO+HBELMXVpi4mmLXgfZ7eoz+/hDnfXYaiSqOLi+SJFFNVVi9kkQP2FnUoFaP8cQ397Bn/27+j//xLbiHBucSFooi4XGo5O0ylm7e9TaWZiCpMqYqrctxtyyLq6/O8MsfXiXvlIkOhdbO7UCr8Xq21OD06SVM0+TZPz6EY5OCMrVsjXqhDhaoThVPm0ekfAofaZZl8YvvXmTuSpL4cGRdKni92mT85BIv+Z188Y8P3fdj7NrXzqU351m5kkSSoHtfnM6uds68nSAUdvPY04PE43HaR9ppHxvgzaUSJtuPNSYgyxLorUUkxa7gsimkjbuPM03DxGWTUey3xhDLsph/Y570tTT+gSCT2SqTczkqTR1oBZdBl529nX7i7V6W3lvCGXISv21Xsp6vk5vJkb6WRq/rIIE77CYyFhEpn4IATE6k+fn/cYHsQhFMC0/UzeOfH+Phx/s3vT5fbdLUTbxOFfcm53qdNpmOngCTp1fwVTVwqORnclgWODwOmlodV8hNW3sMqd1i+WqK/KqN3t5e9u/fj719mB//4zUs88bCj2lhYrWOkmyXGgB0Bl2cibjQF0pgWVhVnYpLpbfDi88pMos+asToLtyTxGKB2Ssp/G2eTYO8m9xeOznD5PKppXsO9Hb1BDn/eA/TL8/gHs+gyDLJxQJNRaL7RA8RnwPpikQt7yEW8+LwOqgUGtg9G3Pc71Qt1FHdNkJhN2MyTAUc+B0KZk1Ddm09ABq5OrrfgbPNQ/dtq3zVdJX3Xp4lp0p0tgc2pGbJskQ04CQrS1w8u8LwgXbGHu9b+31xqUjqcoqlayly2RqWBU6XSvdgmPjeGNGxqFh5Fz6S5qezzF1JEewObDjv63Tbcce9TJxdIfupYcLxrc/YbqenN8DX/uuHuH45garK7DnQxt//r6dIjGewuVRcXjtf++bXuPh3FzE0E59LpVzXcXg3XxE3zVYad9Bpw6zWsfvs+Dp9tLd5WUiU0LzmWrXNO2mGCTWdjjbfuj581VSV7GQWX7eP6+kKl5YKeJ0qHQEXktSqrJetNDk5m+XEQASfSyV5IUlkNIKsyKycXWH+vSVWlkukTIOqabWCQyRi51fp6g/S/1S/6OEnfGQ1Gjq/+LuLZOcKRIfCyIpMdj7Paz+4SndfiO6eW5+NxVyVk7M5ppIltKaOz+Ngf1eAY/3hdemUkiRx5EA702eWKcwUsDkVtGwdm03ByNVohhx0tPtuzBkk3CEXjYzEf/1v/jk2m0KhphEeDpNPVAjMFbCAvN9O12iErrscidnT4efaYz1MvLGAmq1hBGz498d5fE8byg7Tuuuawdn5HJdms5gm7O0PcaQvtGlQK/xuE/9iwj1JJyvUK03aeravFAXg8DlIzOVbKY/30BfKZVf4vc+M8W53gMsXVtEbBr42N/J4hmiglaYlqwq6buD3ORk52snZn48T2qJJ8e2Kq2W69sToHwgRrOn4273Ul8o4VirYun3rWivcZFY1zLpBoc/PaNy3bpCdu5xiNVki2B/c9vxNyOtg2VHhynuLDB3rQnWqpK+nufrCFBMLeVIy1BQJZAm1pDF1cpHeq0n2PtZH31N9ogKf8JGzNF+gUdWI9G9elMQfdZG8nmFxrnDfgR5Ad09gbSJXr+todR3FLmNoJs2GgepUie2KMff6HAMRLydns2j65gFbptLA77YRlSVkv5NAbwBXyMXw/jZmFookizXag+4Nky3TtEgWakQbJoN7Y+tSvHPTOZqVJkRdTKbK+F02vLcFvoosEfM5SBQbjCdKPN4fprRYpLBQoJqqcvWlGcYrddJyK0XV4VSxLIsZTWdB00hcSVIrN9n92VER7AkfSTOTWbILRSKDQWyO1ndttD/I8uUkk9dSdPe0UienEnl+dilF7nqaQLKGrWlQ89v5ZXuelWKdLx3qWpcWOdbu42Nf2cPrL0ySOp9Ar2hoXpDaPESGgkT9t9LOJVkCqzUWAARcNj77SB8vOhRSs3nAons0ynNHu+9aOdMmmWTOPc/BRx9G9Q/jc9r4xd/9FbOh4wx+7GPId9kRNE2LX1xc4ezL0ziWy0gWvNDmYeVj/Xz5aA+qyDb6UBGBnnBPrFbLqB2SsCxoNBpMT0+Ta1gk8620xROHO+kMbn3Wxee08ezxHp483IVumsxPZfnxfzjNypUkyDKqXWF0XxsAh453c/2dRdJzeaJ9wS3vs5iqICkSBx/tRZZlQi4bhzoCvD5QRTUtWCgh++0oAQeSImM1DfRcHZoGlT4fjv4Ax/rC64LWmUsJGnaF8F2KQkgS2EMuluYLVNNVkODqC1OcW8iTskkE3XaCDhVJgoZmkKlqFBsa+muz2Nw2eh7t2embLggPBMu0WmPNFuONJMlYlkkyleLtt+dZWVnhy1/+MpIkkc/X8HkdKPe4G+50qjz1pd2cfHmaUJuXQ8e6AAgNhlg5u0KbJDEQ9TCTLuNx2PA7VWRZoq4Z5KsaDlXmYGeAZrpK+4F2GoUGhbkCNofKaMyDsZRn1bBwumxrK+PVpk6tqhGpGhw52E7fwz3rxpjCfAGH38FqsUGtqRPeYtwMu22ky00KuoFkWKSvpFkZT3Ot2iAtWbT5XKi3HRQMYafWNJg1GzCfx/mrWfZ+bQ+2bTIbBOFBZJgmlmWtC4AkWQJJ4szps1yb+iWNRpMVVx+uRpToVAHZoSA5VJREFWdJ46pNZqzNx+HeWwtTkiTx6Fic4Q4/l6YyPP/vT2I2dDpHo3js6rqxrZarMXCsC8dtc4nhuJfuj4+wWqjzwgvPoyxO0vGxP972taysrPA3f/M3vPPOO/y/Pv8cfX2tMWxpqJuf//znjI+P84d/+IcEAlsv6iwXalw5t0JwvoQz4AAJnIslrp1ZYX4gwmDs/hfWhN88EegJWzJNk1pNx+VS1wbAYNiF3aFSLTXwBrYvgtAsN8nJOf7tv/232Jxu6spe7HOt360slfj21w/gsm+/U2VXZezI7NodR//WYc69PY9pmOw+3MmxE70A9A+EePIre3j5Hy6SGM8Q6vZjv63Cpd40yK+U0Oo6Rz49zK6BUKt63XiGQF1jMFFmBrDFXfgaBtZyubULqShoQTvFaABnf4BP7e9g+LadA8u00Jo6qNK2aaw32ewqzWYdQzPITmaZXMiTskt0BlytMz03OGwK7QGFdKnBVE2j7fwq8X1xHP6d9c8RhAdBW4cPm12hWm7i9m5Myy5nq9jcKqfOvIopV/D7/SSTSa5cKHLy+Una+oP8wZ8f37Qv3naOHO/myPHudT/zxD10Hu9k7rU59gWdBFw2ZjMVkqUGpmmCadAd9TES82FLVbAsKMwVSF5OrhVWUU0T52oa+0QN50gP1RsBldOwiCs6K/nrxI+NbijYpDd0ZFWmqRnIkrRl4GtTZXTTpKmbOGWJ7GSW5UyVFCadwfVjzE0uu0IYB4tVjfhsju65gmjWLjxQDN3kyqVVluYLOJwqu/a10dG5PiOpfzBMoMNLejZPfCiMrEhkl0s4vXYOHG3nV6+eJ11uwvAgbckasktFvfE5lf12tPki9lSNK8vFdYHeTXG/k48d7sL6coXX/+Ey3DizB615RG6ljOJQOPhQ94bbOm0K/VEPQVXn3dPneOWVbp555pktX+/rr7/O1atX6e3txWa7NQ/y+XzYbDa6urrwercP1Ip1nWaxiV+S1tpC2ApNzKpGsa5ve1uA8WtJMqkq4YibsT3xu14v/HqJQE/Y1LUrSV7+4RUq2Rq+uJdP/t4eBocidPeH6B6JMH0lsW2gV69rKBZ84fefomIM8sLr7yCXVQIYAOSXSpQa2l0DvdvtO9DOvi3O+z3yeB8Oh8rbL0yQnitgaTqSqmAZJpIs4e/w8egXB9gV93L9h9epF+o4A05Uu8LumJfUmxexAhGy3X6sfj9YEpZNwhXzMBp1M/nui4w+80frHlOSJWx2FXQLy+KuwZ7W1HHbFbSKxvK1NCkZAk7bphMwgLDHwYqms7RYYGg2T9uBth2/V4LwYVAuNzh3cgldNxjbu34CNjgWpXM4wuzlJI6x6LrduWZTp7hSYt/jvXz1Tz/PCy+8AEAwGOTiexfIzuQxNJNMurphUne/Oo90gglLJ5dobxh0t/mp2GQsLM6fPImt6EDVh9E1A8uyMHSDQG8Axa7QbDS5fuo6K9Iive299ES8OLt8qC4bwTYvgX4vf/n35/jZr37GY9pjHD9+fO1xVYeKVtWwqzKmZcEWWRWabqLKMnZVplluUqs0WVEtPA51yzEGWsFertIk1dBIX00TGYvcU6q9IPyu0nWTf/zfz3H9rQX0pgGWxem4h0998wD7D3WuXefx2HnmS3t48e8vsno1hQU4/Q6OfmqYZz+xi+MPjfC//Me/pRlux7aQQb6tHoAkSUh2BVtdp1jXtn0+T3xskFyqwtW3F8gvl5AVGVMzcIZcPPaF3Rw80rnlbdPpNLquMz4+zokTJ3C7W4GmbpjMZqqkyw0UWeLwYx+jVCrTaNTXeg0DHDlyhKeffprLly/fNXXT71Sx++00TRMlVwe5dYZYdtsI3GXH/7WXpnjjh1dpVjVsLpVHP7+LZz41su1thF8vEegJG6yulPjZfzlHOVXBE3WTuJbiJ//5HH/yrx8lFHJx5Ik+FicypJeKRLs2TqKaTZ30ZI7+PTF2H2jH5uwmFInx9pU6M6/MAjCwK0JwkwahO2WZFpmlIpVyE6dTJdYT4MjxLvYfaufq5SRzkxlqlSYOl41CdZVPfPoQjorM5C8mUewK0bHWqrWmaZy9dpaV7GW+9NCXyWU13L1hXEMh7IpMZ9BFm8/Ov/jr0/w//4ckex/5GNGuQZCgze+kcyyK4/wKlYa+7tzMhudrQTNXo2swgmyTyWUqVFWJjm0qYMkyKIpCsW5QzVTv+70ShN9FjYbOP/zVaebPr2IaFuf7gvz+XzxEe0frrK2syHz6a/v4QekMq1dT2ANObC6VZkVDLzXp3RPn2S/uQZZlPv3pT9NsNrHb7Tz56T28aU3QPRwi3vbBpRhJskTn8U58nT4ykxmyE1mc2TqmaeKsmMxX5wn2B4nao3jaPLhuK9qkqio+nw9HyEFwOIjL7iTWG2TkuZG11i3haJiOjg6OHj267nEDfQHyc3na+gK47CrlLcaabFUj5rUTUBVSuknDMKk6FUI76DXotqvkahq1XA1TN8WZYOGBcOn8CtfenMfX5sUTdLbOwo5n+NWPrzO6O74uTfLgkU46ewKMX0miawYDwxF6b5wPDgQC/MWf/Sl//cY0TVcBqdhA9rS+uy3TwmoaNNwq/d7ts27sdpUvf/Mg+493cf1igmqpSSjmYfeBNnr7Nj+LfNOzzz7Lww8/zMjIyFqQlyo1eP7yKjOZCsaNqr7lQo6DI4/wzYdH1y2k9/a2MqB27drF1atX2bNnT2tByrTWztyZpkkymWT86lU0KjT6/NSXy0hArdvHgaMd9GzTcqJSafLei1MgS3TuiZFZKHDqpSmOPtKD33//bXCE90cEesIGC7M5Sqtl4qMRAJx+O5npAotzOUIhF3uPdpHP1njjx9dYuprEE/Pg9LQappczVfRSg96xGJ/7w4PYbkxIdo0O09mrcWm0FWDtG4zc9UDxVmYuJzn58jSz42n0hoGiynQOhDj6VD+7j3dx4FAHBw51AK3S5N/+9v/I7NzbfHHsi1imhbe9NflbLdS5dG2C2ZllAsEg/rgPp9rEnqyx56kB1Nu+BMKDBziVaHL9zBJ7ql4kWcayIGaBy+8gmyrj7tq6IEu21MDVMNnzUDeKTaE1JN895VOWJCwJLGPn7SME4cNgYT7P0rUU4d4AdredlStJxi8n1gI9gI7eIN/4Fyc4+848104tU6tqROMe9nxujEMnuvHfVhjJbm8tHB062sWho10fyHM0dZOV2TyNhk603Ys/4sbf7cff7afzSCeNUgPLtLgiX6HT04mr7kKxlHVBHrSC1ra2Nnbt2kW1WsVtd5OfzVNOlPF1+JAkie985zsYhkE6nSYev5XuFOgPUH25SvJaEi1VpB7oxLAs/E7buqqbqiwx1u6jlq7iiXmo5SQsXd/Z7pwE1o3L7uzhJwgfVoszOQzdxHMj/VCWJUI9fvKrJZYWCwwORdZdH4t5iD01sOl9RbwORjv8nOooEJ8qoC0UkRwKZlXHiLiwOnzs3UH2gKLIjO2KM7br3lIaR0dHKZfLzM3NsXfvXuqawc8urjCTLtOjKChFHRSJejzORF7j5WsJPntg4w5hJBLh0vg0L11aYCrbpKGbxHytyqGr107z1ptvUi6X+cYf/hENbycXZzJkMzk+dWSQw72hbQuxNJsGRlPHfmPXz+Gx06g0aTSMe3qtwgdLBHofQaZpMj+Xp1RsEAy56bmt0pphmKwuFijNFygtFECSsAwTJImF6Txje+LY7SqPfWKYeIefC+8tMH0pQTlfQpIlwjE3ez49wpFHevHfMdnxO208uufeWi3cafzMMj/7m/PkinW8HV68HjvNmsbkVIaV2SzVUoNjHx9au75YLAJw/o3zRKYiPPGZJwCYSpY5u5BHw4/StQ+aWRRFwR1rTcCKC0XCw2EAJpMllm3t7D8cZ29PBxSbWJbJSjXH2VSean6R3VaUFblANO7dtI+euVLm8OFO+g+20yw3cbnsqIUqDc1Yd/2dNMPAKcnYNzmjJAgfZna7gmpTqZWamDcKIdg22X0Kxzx8/PO7eea5MQzNQLWrmzYD/6BNX07y6s+usTqbx9RMPAEne0508/TnxrC7bDj8jrVzs9/5199h/Mw4ubdyuGO3ztiZpsVMpkK+VKXTZ8NmtxGwt8Zbva6Tm87huxHYulyt8XJlOcncdI1MoorX7yDWKXN27iy16Rp7n9yHEgsymSyzUqgBrZTxkMvOnk4/EVmmUtPpONpB7fQy9kyrQbJX2f6rvqEbBADFrojdPOFD4+qlBBdOLpKcL2BzqowcbOfw8S7CEQ8Aql3Z0GNX10wURcZmu/fp72NDUVKlJvOqjDtTQ60b1No9GF1eju+OM9Z298rfdzJNk6mJDLNTWVRVZnhXfN2c7HZer5dKpQLAZKrMXKZCn6LQfHeZeq4OEtg6fbQfbuPqSolj/XXa7thJS5cbXG+GmLqaJBbwYpNa9zWRLDPsb8Pt9tDR0cGh/fuQJIlue43//r//f3P8S//TXc87BwIOevfEufbGHPVCnWZNY/jhHsLh+288L7x/ItD7iFldKfHz715keTyDXtexu+307Ivx2a/tR1EkfvR/XGD61DJGXcesNpFsCmgWss/OyZ9dZ2kmyxf+8CBt7T5G9sUZ2RenkK5QLDZQVZlo3Lu2i7cdy7IwdRNZkXc8aWtWmvzqx9co1DQ6x2Jru2dOm4Lf52R1scCbP59gaG+cUHtrwM1kMthsNvSKTmo1Rblexua2M54oIcsSnSE3BacNLD8GUivoMqFRbACtidq7M1mGR3fR57BRfWsRPVHBtExWcvPYR71Ux+LE/V1YkznS0zk0t4rsUFvnA8saHklm37EunvzqXhw+Bza3je7BENMnyySrGh2BzSdWlYaOw7DoiHgI9Imy58KDpbMrQNtQkPFX5zB1k8hwmJ6Brf/OZUVG/g2V9V6eyfGT/3yGTLZGsDuA6pApp+u8/fNxDE3nM988tOE2fpef2cwswdsq/y4XapyezVHXdKqGl47YrettHhu1dG3dfTSbOu+8kmLinQUkbhTA6vPS99gulqxJ2mxxfC4bfbvjpMtNzpw9x/7dY/TEAmjZOuVMje4T3XQ91EU1VSWerjJf17ZNKzdNC9O0iMoKkV2R30gQLQjv1y9/fp13fzaOVtVw+B2YmsnK1RTXTi7x5W8fobM7wOjeOGdfniY1nSPU7Udr6OQWCgwe7aCr+96DsojXwVePdnOxzcvllSIN3aTNbeNAd5A9Hf77ajvwix9d5exL02gVDQuLk2E3T//eXh56tHfT61VVRdd1FnNVJEnCWi5j5OrY+vxgWuiLZTzDIZIOidXCxkDvjYk0S4U6uxwummcSWFUNT4cXbSjEtUyNr339WzgbubVMgHfeeQe3280LL7zA5z73uW1fiyzLHHk8zPj0ZWS8HP/4GE9/cgRFtGP4rRKB3kdIrabxw/90hpXxNMHuAC6fg2qhzsRbC3y31ECWJOYvJAj3BYj0BcjM5ltFALwOogNBTN1k8VKC7//Vab7258eJxVqrZoGoh0DUc9fHtyyLaqrKzOUk0+dXaTYMFFUi3htg99Eugr2BbZuDT15OklosEhrcJEVSgminn9S1FFfPr/LojUDP4/Hw5S9/mXOec4wpY0SiEQzDwrTg5l3IgEmrxsHN+7pptVhnPlul3e+kdnoFbaWCrcsLlkV/vZ3rk0vs++Juik4fX3ligPREhpnLSWpVDVPW6T7Syd4TPbSNRLDdqAQqKzLxvXF6rqYo1JukSg0iHse611Rp6BSqDfp16N4VxfsBnjUShN+E6akM7/5qBq2hs/doF0dP3GoRMjGe5qXvXyY1m0e2K8iqTCVT42//53fY+0gvn/j82D1Xy/wgnXt7nkyySuee+K0FpQ4vWUXi8nvLPPTUIJE7zifb7XY0bX0xBssCEwtJljHNuz/upfOrTJ9aIdoXwulzYGgGK1dTDO0Z4/f/70fR5jQy4xnqiQpem4Q+u8RSSSO0Zx+emIfOj3XSfqAdSZaI7YnRdiXJqqaTrTQJezZmBZgmJIp1wiZ0dvoJDWx/TkgQfheMX0vy3j9NtHpc3vY3a2gGq+MZfvH9y/yzf/kwg0MRnv7qXt7+p3HSMzkUm0zfoQ6e+/r+uxYk2UrAZePxkRiPDkXRTQubIt138aKpiTRnX57G5rIRGwhhmRbp2Tyv/+Qau/bFNz3X1tnZydLSEpbZ+jxbloUlt55DK/36RsEmS+LOJOx0ucFkqkzcptJ4dxmj0DprWL+SwqlIqJ0eJtN1vnR4GABd15mbm0NVVVZWVtA0bV0lzzu9++67/PznP2dgbIBLl95hdP8+fKJa+G+dCPQ+Qq5cWCUxlSU+HFnbdfOGXSg2mclfzSHbJboPda61JujYHVt/B3aF9rEoK1fTvPSTa3zjT4/e+RBbMpoGF16a4swbcySTFep2ubVbaFoo11OcfXOesX1tPPbl3bgjm/eJSqcqaJaJa4viAqoiYdkV0kvFtZ+1tbXx3HPPIRUlMm9k0Bs6qkNlKObhwlKB5XwVSZLZ3e7DaVMw9dZszO5rDaLVpkFDN3EqEuVsHSVgR7oRjDojPnZLI4TaYyxkq9g7vJzYG+fIJ4d58ecv8sMf/5A/+H/8z7g9G19PZCzC3if60F+Z4XqpyqpmtHY3JamVrmlAvw4HD7TT+1jvjlbZLatV/XO7CnuC8JuQSpb50X88Q2G5iKQqLF1NY3eq7D/YwfRUhh//9RnKyQrhvgBtN84Cm4ZFIVnm5M/GadSafPEPDt73SrBlWZRWyyzP5dE0A6/PQddwZEcp0JZpsTiewR50bFhQCkTdpK6lWV4sbgz0PHYUu4JW1dYWdToDLg51B6lqBoPR9Ys1WlXDGVo/kUuulDANE6evNTlSbApOn4PlmRxf+Pp+6If2Q+3kZ/Ocee0Mhs8gZ8/hOuRi7+N7UW/buQsNhhg63EH1nUUmNJOVXAWPIeEwLEzLomZYVG0SEbvCqNtBz7GOLcdeQfhdcvnMCo1Kk67+9efcFJtCsMvPykSG2Zkcg0MRHnm8n77+MOfeW8TuUnjkyQE8myx63CtZlrC/z+/a+ZkczbJG7EbRF0mWCPcESE5kmJnMblqFs62tjVOnTtHVMcK7M1nkdg/KXJHmbAEsC1uHl5rPhhtouyPIKtV1ak2DiAnVQh21w9vqGWyYGKkqnoEgqXJj7XpVVflv/9v/llOnTnHs2LFtX0uj0WBqaop6vc7s7Cwej4e33nqL0dFRUcX3t0wEeh8h6WQFy7Q2pFbaHCrN1TLuweC6/nObUWwK/g4vcxcTrK6U1hVO2IqhGZz+2XVee36SklshOBAkcluz0KZukis2ePfdBcqlBp/+1mFcm+R0y4rUWqzappWBZZit6+4wfHiYmddnKK+WCfYFGWvz4XWqlOs6Lru6Vkmqmq7iiXoI9LRSyOyqjCpL6EhIbhtGsoIcaA2eVlXD3RdolTVXJGw3JqU2t42LkxeZmJ/gvZPv8fTTT2/yWmR6H+9lNb3Kqf/4Grt6j1LIVMnn8nRHQ3T0BBg80c3gxwc3fS/WXq9lMZ+tcnW1yPSN/l3tASd7O/0Mxbxrz0kQfpPmZ/MUVkq0744jKxJLl5IszebZu7+NV392nVKiTPvu2LpFCVmRCHX4sDlULr+5wJ7DXezae+89mCrpKm/903XGLybI5+tYN6rKtXf6OfhYDweeHtj+HJoENpuMqW3cgjN0E0mSNh1jPHEPkf4I+cU8sdHWIpmiSOzq2FigQatpyDaZ0ND6HTR/0IlEq/enalewTItmRSMYu5Ux4Qw4aT/YztMjT3P4q4dxu92bNj9WnSr9T/djmRbFfzzH8nKBhuykYrYGUbsk0eG00zUYZPjJQTqPbV3aXRB+lySXilvOVTxBJ4WlIrl0FYYinH5vkdd+eIVisoIkS1w7tcxnvrGfoZHffr9IVVWQsDANa21MMQwDSZGx2Tf/7pakVpXe4biX7rCLhVyN7ke6kDI1kCWMuJvlhsbh3hDtd+wIOlQZVZHQZJCcKkaujuyzY1Y0bB1e6ppBx136I2/F4XDw6KOPcuzYMV555RUkSaK9vZ1arbZWJXSnyo1WQOpxKLh/i5kdDwrxDn6EOJxqK5XItNZNsPLLJayajnsH6ZcA3oiblctJrpxf2VGgt3AhwZu/nKISsNMZ824I0uyqTFvYRd6hcPlyktBPr/PUHx3csIvV0xvEaVcpl5v4fBtX5OpNHVk3CbY5yOfzQKsssiRJ9A30IXVIlAol7Gk77qib7tD6waeer9MsNen6eNfayni730mb30myVKdtb5RquYk2V0QClLAL564IS+UG3UEXsRur8MvLy8zNzaEoCs8//zz79u0jGt34pfLWO2/x9ntvY7fJxDx26oUCycQMw8NteFw2mqUmpZUSzpBz0xUxy7J4czLNG9cS1BfKuEsNZAOu+GxcbnNzYDTGp/e24xTFFYTfMK/XjuJUKaUrqA4VCXB5bMzO5FgezxDo8m+58+wNuygsl7hwavGeA71qtsbP/vNZrl1OIkXdBIZCKLJEvWEwm6qQ+O5lauUmD39h15bn/SRJYvBgB/MTWZpNA/ttJcqzqyUsuc7Fq2+ylA0SCAQ4ceJE63ayxODDg7z3v7+Hv+hfK9RyJ9MwKc4XCY+E1wqx3LT/UCfnR+ZZuZ7G4bHTrDbxRtwcfbxvw/243e5W+5ViccPvblJdKhOzEyxOj9PubycQ8pFIpdC0JkODfTg8NmwuG/V8nUahse2i0trz11u9ScVZPuG3xeWxo29RyVFvGkiyjN2hsLpS4uV/uEijqhEfDmMaFunpHD//u4t85//6BM4d1BO4G103WZjPA605inoP1cTH9sZ4N+YhOZUl2t86HpOeyeHpcPCX/+F/YGCgF0mS+Na3vrVuMSccDlMvF/ns/g5+fmmVhWwVK2zHssBuGOzrDPDs7rYN84Z2v5PekJvpTIXug23ULyZb5/s6vMijYZqawZ5NFqagNd/YbmfOsizK5TL79u1jdHSUa9eusWvXrh2/F9BKLT05k+XaapFmw8DlUtnT4ef4QOSu/fuErYlA7yNkbG+c98IusotFojeqOlmmRWG+gORSCXXu7HCyLEvIqkwhW7vrtaZucuntBQqWSXt0Y5B3u6DHzkrEyfVLqxxZGcR/R2pUz2iU3rEI1y4lcAxH1k3ADNMiM5Mn2u7mpy/+DS+8ZaOtrY2/+Iu/QJIk7HY7XQe6kEoSelEncz2DM+xEdagYmkE9W0exK/Q81kPb/luNye2qzJHeED+9uEzF58D7RC9GugoSqDEPeQWMemv1TLkx8dE0jYMHD2IYBo899tiWOe29zl7OrZyjapOZbXNxKlOnureH16wSB3wRerNVmi/NYBnWps3SLy8XeeXiKp6rGbzJCpIigyzhXCljpmqcaRh47AqfeJ+VToWPJkMzqGVrrSwAlw1ncOcrvSO7ohz++BAXX5+lWdUYPNHNsUd6uXBmGa2m4xnY/r5cASfLU7l7fs6nXp7m+uUE3oEQ3tsmBja3jK83yGq6wjsvTdE3FqVrz9ZB5OFHe5i6lGDuWgpb2IXNplLNVXHbZJ746mHOTvySidnrPPPMM+tuFx4OEz0YpTxTRqtquKPudeeOG8UG5ZUy/m4/fU/2bQiWfH4HX/3OMd59bZal6SyhmIfDj/YyMrr57oPdbqfRaGz6O4Dp16dZOLUA7dB2rI2uWA+TLy9Rq9cJHY/T3hnC1E1yUzlmXplh5DMja2mnd8qsFHnnpWkWrqdxum3sfbiHo0/0b3uuWhB+HUYOtDFzaplmXcN+Ry/a/FIRf7uXwZEop99ZoJKp0r4njixLKDaIDobIzOWZmkizd//7+26cnsrwwvevkJ5rjVXRvhCf/L09G9o2bCXe5uPZ39/Pr350jfRMDkmWiA+Hee4PDnDhosFbb72F3W7fcJ6wp6eHa9eucfDgQb75UC/T6QrpUgNZlugKuugNu9fmI7eTZYlHh6Okyg3mTYv4Ez04dIuSCrmGzt5OP2PtG+eBDoeDZrO5rgH7nRYWFtZ69d2PTLnBD84usXAlSSBRxVnTaXrsvNaRZylf58tHuvBv03dY2JoI9D5kLNOitFwiP5OnXqxjc9sI9gXx9/jvWha7o9PPo58b4/UfXWXpYgLFoWI0dBSngrPbt6NqmYZh0WxoaA2Dpnb33ij5hQIzExnUsHvLHnO3CwZdpKdyTF1McPiOQE9WZT75tf2Uiw2mTy79/9n7zyDL7vO8F/2tuHMOvTuHCT05R+REgAAhiVESQVKyJFq68tE5llw6Lpdll2/5uu4511XXuvfUsX2tkmVbwhGjaAaAJAASiQAGg8FgQk/onLv37p1zWHuF+2HP9EzPdPf0YCBZIPupmg/TvfYKu9f6rzc87/NgigKSQ0ayy8iGRTTm5tmvHESTt/Ld736Xzs5O5ufn6e7ubnX1enuZmJjg2GPHyE3kyIxnaFabiJJI7FCM0NYQnk7PbVWrg91+ivUm705mSOom3qgTC4tirYFLlXlse3SFf05vby9f/epX+c//+T/z4IMPrkqrquVqLJxeoG3bdsqWxEKhioJOxKVgmg2WynVyksReEeRT83g6PSvmZwzT4oPZHMJMATXREogRrv39LdNCny/hnyky1ObmaF8Qv3PTnmETG4OhGaSupkhdSVFNV7EMC9khE+gPENkdua0AsxpEUeTZz+3m8Mlumk2Djk4fsixiAYK1viqJ1TQwC3WMqsbS0BLOsBN3zH3HOY9arsbwuTim374iyVuGANGQk0SmyqUzC3TsjKy5T2/Qyee+epj3Xp9i9FwcTTPo2hXl4AO97DzcwcA+Hz/+8Y/ZtWsX09PTBINBvF4vgijQfrQdoVcgfTlNbjLXEkYQwdItVI9KdG+UruNdaybOkYiLZz+3e91rXb6kazSu1VDP1ymOFRk8MMhe316WyhovjS8wITrA5eSHV+c5aorsavcS2BIgO54lO5FdUei6jkqhznf+81nmJrKoAQdGtsrC/5WjWtF4+Nm7q9pvYhM3QzdMCrUmgiDgdygbmjHfd7CDy3sXmLuQwBNz4wo4MTSdfKKMZVoc+8QWXC4V0zRbj98tFHHLtDD0DagjrYNyucELz18gN5MncK1wvjSc4sXnL/Dbf/zAhucADxzuZNuOCDNTWSRZpH8giKrKdPd8ing8zhNPPMHU1BSNRoOdO3fidrux2WzLBR67IrW6cO1Q1XRMa4We3G3oD7v4zMFOTk9lmc1UaUoWLlXmkb4Qx/uDqzKA7HY79Xp93USvWCzS09ODppsMJ4q8O11mxkiwLepmIHJnMbkzM1nmriaJjuURLAvRpSBnqthLDSYEgaGoi/u3Ru64n03cjs1E72MEva4z8+YMqeEUlm4h2SQMzSBxLoG/30//o/13rLo/8OgAHT0+Lp+LUyrUCIRdmKbF+z8YwdAtJHn1JcLQTTKLJQpzBfRiAyNdZUgWCLS5Of5ALz736gtAMVejUm/ijm2MFupQJXKqSHqxtOrvc1WNetNEbBg0U5XWfJxDxn+4g2d/4xC9O8JAhMcee4yDBw/SaDQYGxvDbrfT3d3N6dOnkXwSPQ/00HmsE6NpIMriCnP0WyGKAo9sj9AfcnE1XmQ+3+pk7uv0saPdS3fgdv65JEkoikK9Xl810ctN5ViaTZEI+jBqBl0hN12hPSu2SZUajDd03FqN3GRuRaKXLNWZT5XxpGuIXnU5yYMWhUyKOlFzddKJMjOZ6mait4kNQW/oTL82TfJSEtWj4m53I0oiWkUjeSlJfipP/+P9yx6Td0L7LQbCwaATUZVpVDRstwRClmnRjJfQF8toUzns7V4uvzCK22PD1+uj60QXrsja60g2XiKbruDuXjsRFUUBwWMjMZVHr+so69CBLFnE3eZmy+FO3B6VvUc78V0zaI/FYnz5y19GlmWCwSC5XG454WuLtZGSUuzevZvCXIF6vo5pmCgOBW+X9+9M8CQ/naeWr6GGVBSXn6l4GlNxcmTPIJIoUK43ubRQQBIFdnV4Ud0q6atpIjsjt3XprpyLE5/MEd0eXmZSJBeKXHx7lmMP9+PwbCrrbeLuYJoWFxcKnJvNkSo3EBFo99s51BNg5xr0wetwuVQ+9w8O8fJ3rzA9tERyLIMoC/g7PBx5dICTD/YB0DsQwu6xk1ssEejwYJoWmdkC3qiL/g123dbC6JUUubkCkW2hll8fENkWIjWRZeRKkkNHuza8L1kW6e0PrkgOJUniq1/9KpLU2nez2eTq1avLCZ/NZqNer2O325nNVvlgNsd0ujWnH/XaONDtZ2dsdYp8b8hFT9BJpqLRNEy8dgXXOjHQ9URvtVgGYHZ2lq6uLnTD5MWLi1z82TQslolLGT7YEeLJhwc43Lv2O6PS0Lm6WMS3VEOwLJRr7DLJZ6c5V8SVqnJxocjx/tCHsrD4RcdmovcxgWVZzL49S+J8Al+vbwW9xtAMchOtyvH2Z7cjqet39ga2hFZQC3K5GldOzVFMlQmsMnNn6Cbzl5NUpvKIgoAoAU4FxYS3vnmJ6dE0X/gHhwisIgVsGFarin83FyuwrH55M+Zm8vzgv35AKVmh40QXqk3G0HQqxQbFZIU3Xp/kua1BZFnk8OGWIqjD4WD79u3UajXm5uZoNBrMzMywc+dOJFVCVESqmoGpm6jrUJAEQaAv7KJvg3OMwPJCfCssy2Lh/AJNh0q+qhNeI0kOulSSpQZlr0RmJEPH4Y5lqpemm+gNA0k3EVdREhTtMkLTRGgaaMbt32UqVWF8JIWumXj9dnbsjmJbZ6HfxC8GEucSLA0t4e/3ryh+2H127D47xfki029M4wg5cATu3gR362CYSL+f5ESO2OANOqJlWWizRbTpPCXNICcJNDucvN/UiVYs2i+0Zsi2fnIrzvDqiZJhmFhYd1TqFCQR3TSxzNU7YQAjw0l+9NdD5OcL1z/F2Z/N8MyX9y/TKGX5JoXLQIBAIEA2m2VhYYFKpUJHRwehbfcWTN4LMqMZqnqV7lA3QwsFyo0mnTcVpTwOBcOCqXSZLREXzoiT8mKZarqKO7ayAl/M1zBhBV3e4bNRTVUpF+qbid4m7gqWZfGzsRRvjKVQJRE/ApZgMZ2pMputUmsaHOpZ3+ojEHDwa791mKVEiXSqgqyI9PUHV7zH+rcEOfzkFs6+PMHCpSUEwBV28dCnd+Hx2siUG5QbOn6netczYI2GjmVYSDfFDZIsYpkWjYa+oX1UKhqvvzTGyPsLGE2T9m1BHn5qO93XvDivJ3kAiqKwb98+NE1jeHiYVCoFgL1tgB+cn6cykcOdriMaFpMhO5PzBR7Z186D21bvggmCsGbscSvsdjvVanXN3xcKBXp6ehhbKjH07jze8Tw2l4pVNyieT/KWx8ZgzIt7jRij3mwxxOy1JuIt1HHBIaNUmjSuxTKbid7dYzOy+5igmq6SGc3g6fQg2mSuLhZJFBv4na1hVf+An9x0jqXRJSq2Clu3bt2QpG2xWCefrdG1I8LVN6bwhp23UUAzC0UqU3lUt4rgkGkkKji7vLTva6NZazJ/Ls4r0RF+9df337Z/p1NBFgTqmoHjDgkogG5YCE0Lp/f25OX9d2bIL5To2B1dVqiSHApeh4LqVJg5F2fkanJV3v31hG92dpZTp07hdrsJRmO89P48k8NpbE6FRx7oY2+3/47nuFFoDYVXfzRBOTXZ8t3bFeXwiW7a2txkk1lsXi9mvoq8ioIf0OLYWxZNUcTQDAzNWBaJsSsSqkNBt0mI1dsXR7PaxFJFLJuM46a/Z6Oh8/IPrnL11DzVXK01MymKBLu9PPipQQ4c7vzIrn8THy80q01SV1I4Qo7lJM8wLDTDXH52PZ0eMiMZcpM5HIfvPtGTZZETT2zlR4vnSU3mCPb4kWQBs6TRXCiSbTbJ5erYOjxEOrxYosBUqU7WriDM5HG+v8iWp7asura5fXYcdoVqWcO5jqiIUWnga/es2cWvVDRe+volCotF2raHkRQRo2mQHM/w469fpPN/fQCnY/UOeTAYJBgMcvHixRWUzmpNI71UIdbh+VvxB9R1k1yuhtOp4HKprcS5qiEqLZP5dEXDvspxXTaJXKVJpWEQcCkYemuduRXhNjeSIFCrN3Fcm5Mpp2tEgw68ayT8pmkyPZVjdrI1v9S3NUhf/8Y6wZv4+UaiWOf0dJaATcExlkWbLSKIArEtAXJdbt4aT7Mt6sazgZmstpiHtpiHVLLM0PlFFEVm287w8jP65LM72LYzyvREBkkS2b4rSiDi5EdDiwxdXKJe1nAH7Bw92MH9W8Ibtifq6g1g89nIL90okOeXyth8drp7N+ZH+YNvDHH1Z9M4fA4kVWT0nXlyiTJf/p9PEljjuVJVdTnh+7++/k3mnVWE6SahuRKCTUYQBZTxPI2Sxim7wtaom3bf3a/VN8Nut5PNZmk0dGq1Jh6PbbmgNj8/T1dXq3uZKjcw0hVURUKOtIpKruk8+WSVTLmxZqLnVGVcDoWaW0VK1ZCuMdMsy8KsNGl0e4nYZGzyprDch8FmovcxQWmhhFbR8HZ5mUiWuTCfR5FEFvOtKsu2gMLo6CivXHgFq9/iH/2jf0QoFEIUxVWDItM0eeOVCc6+Okmt2ECSW9vFh1PEBiPLVATDMMnPFpBEEGwyWqqK7FWJXKtUKw4Fb9DJ5Adxkp/YRvQWWlKwx0dXl5fhVJnABjys8pUGXlli6772FT+vVDQmzsdxRZyrSpvbPTayMwXGLq+e6F3H9u3bmZubw+/3851XzzP2WhpPsUkR+FGxTvSLB2hbpTN5t5ieynL1rRpGcZ5gexAEgTMvjjB+Ls4nfm0HNruNVjxlrW0XYbUWOtFqubsLN1131GOjr83NSNhBeDyP6dIRHa3H2dJN9FSVWsxFIOai96a/yYvfGuLiq1M4Qw5iOyKIkoBWb5KdLfLS8xdQVOmeB9Q38fFEOVGmnq/jH/ADoDVNTk1mKNSa7O304Zc1JsYnsDVteEe9dBzemBx/KlXh/Jl5mppBz0CQA4c70TSDN797laXhFKIqwVKF5nyRokPG2eOl/2DH8sywU5VZyFVZ8kgEpnItC5RVKJz+Ti8DOyK8f34Rw29fVYygVG1i00x2HutaU0Rk+HKS3HyByJYgktLaRlIkwgMh0pNZRi+nOHBk/YJIX18fpmmi6zpXLo/x2vfnyc+X6doZ5ou/e/QjTfauXsrwwvOT5JfKqA6FnUc7efxTg+RLeXzeFtXKochkytptn9UNC1kUWh6kptV6X6yyvu7YF+Pinghj5+JIbhtm08CpShx5dOA2Ci6Apun84JtDDL87j1ZpHdfmtrHr/h6e/fyeu1Im3MTPH6bSFcp1nUhFpzqcQQw5wLCoX04R9NuZVQ2mM1WClHn19TdwB6M89OCDBJzqqonYmz+d4N0fjVLN1hBEkWCPl2e+uG/ZQqF/S5D+LTeKDO+Mp3n3lQk80wX8QF0UeS1XJ+BU2dO5Oj3xVnT3+Dj02ADvvzTO/KUlAGxOFd2X4mdv/ZDDhw/T19eHqq4e9yzMF5i6kMDb7sFz7R3tDjpJjKS5fD7OA48OrHt8VVXxdmzBTNkIpopIfjvSNaVfy28gLFVILhSYSJbvOdGTZZW3X5/lRwtzNMoa/nYPJz+xhX0HOsjlcsuJnkOREOwyZrOG1TSwNAMdUB0rC863wqFK7Onw8Vosh72k0ZwtIjjkVsHaq1KPOdnX5V91TZ+cyHD6tSniE1ncIScH7u/hyPGu2wRsfpGxmeh9TGA0jeWErVhvYgFhT4t6kClrnP/pd5m7NEdNqVFLaVieb2MpThTBIqQa2BURUWz9kySJZNxi4p0aoiJg80g08xZGWUdyKRTeKyAqIjafQqNm0JgpYrMpNJJlFH/Lw8l1U8XcHXWRGMswN5O/LdGzeWwMHu5g6m+uUKho+NYZUG4aJo1UhcEtYSIDKytijXoTXTOxudau8ImqSLWytgIdQEdHB5ZlUSwWEQQvtlIcT7cXI1sjtVAmX23ec6Jnmiavfn+YRs4gOOAm3O5v/dzwsjSc4sd/c5HPPLiDmffjuFSFcr2JZxXaSLnR8vhz6RbejpViO4IgcLgnwPRSmXJdx52oYugm1rV1sBlxUun3c7I3uFwVnZ7KMnx6AU/MvfxiAVDtCrHtIeJXU7z7k3F27o5uLpK/gDCaBlgs2w4U602WSnXKDZ13Lgyjz19ClmW6Ql2kE2nmZufWldhvPWcaP/zrYTKTRQQBVI9K/NPbOHCkndDv7mbscpaFqQKlcoPGVj9ayEl/j39FEiYI4HUoLOkGvaUGtWxt1URPlEQOPtTHzFiaxGyecKcX23WBIguKVY3KbAFVLWELNDFNc9X7vFJqtLz3bmEgKDYJTItKef01BsDr9TI7O0tPTw+ppE566hKiKLEwkiGdrNDRtbFg8k4YHU7yzvcnEE0JT9RNo6Lx3gsj6KZJW9SJvtCikHUFHMzlqtRuYlaYJmQrDfrCbrwOhWqmiupRsa/io5XKJcma5xk4vgWl4cXhUdl1uIPBg6sn+++8Mc2lV6dxx1xE+ltreTFV5cJPJgm3ue8YxG7i5xtVzUCWxFYgLwhI14rAZqGB0NARVIXv/uCHXLp4nqavE9lp8LP424RcMttDCn0+GUEQEASBZLzOz74zi2AJuGN2LENgbjjBt/6iwmd/exd2m0KjYTJ6JUtqsYLNqTDRtLDPlXD57Eh+O3KyQmMsy+X53IYTPYBPPDtI75YQU2NplpaWMKQ8J04e58/+7M8YGhriwQcf5Omnn171s6VCA63WxN9+gyZ9vbBULt55jQFwegNUR5YINwzEmyjtgiK1RLQaJoV6c8PXsxbeenWKK6/GCcZC2FwKidEMP46XKFfy7Nh5o+g1EHET3dtGKt/AvVDCEKDc7uLgnrZl+6m1cLg3wHy+xqgo4EzWUKpNtLCdepuLPTuj7F3l7zI7k+O///lZiokSjoCTxFiGlyYyNDWD+x/uv+fr/nnBZqL3MYFsk5c7PEGXDUksEy/UMU0TCgmaWpNIIMKMuYRz2/3Uqn0IBZ26U0Ya8PHg/iAxt4xhGK1EZHEGh5oh0OfCsiysoEVuukKw30lbj5O5kSKZ4SLGfA0128D0mLgPxojtjmK7lUMtCghWq/u3GnYe72bqSpJLw2msLi8+l3pbB6vRNEgtFJFKeQSfQLlexm/3L//e5bZhc6nUyw1ca1AaDM3Et4owyorvUZbp6OhgdnaWtlgPox6F8lyRBuDY6v9IvFrm5wrExzO42xyYN80BiZKAu91FYaFE3SHjdMh02wSuZipIkojzpsCyphkUahqDQSdOUSK0/fZZn21tHp7aG+P7tTLVkAO13ES0LBoOBVuHiwe2hDkxcONzI5eTNMoNwn3+Vc/b1+khMZFjfrZAT9/GqCeb+PmBpErL87GiLOJ3qvQEneSrGhIw02ySyWaIOqJEO6N093RTzdVo1HQ8N9E9b8Y7P5umvNigd38HkiIRH0kzfSXPQ48N4nIZtLX5mZvN8cblFNVCHbBW7bRJooB+bd6XtUfr6NoV4elf38dPv3uV1HiWpk1CkATMuoETgV27ojS9Db72na8RCoX4yle+gsezci45EHIgyhJatbnClLlR0RAVCf9diql0dPhoGwiRmskT2xokHN34nO+dcPHMAo2CRv/hbqBlFp0TBM6/Mc5v/u5x4olZ9LpOp9/B1rCL4XgeSZYRBQHDNIl4bOy5JphTS9foOtGFegvzYmRkhBdffJFKtcIXvrifjo71O7mGbjJ0ag7FKa8oKHkjTurFOkPvznHfw32bxaRfYHjsMrrRmi8XBDBydSzDBEXEcrbWkd79J0maEYoXZuhMQrRhUA6qjNo8DGzr4HBvEMuyeOWFYWQzTvvOCBYWlmlhs9kpxMsUciZqVOKlb4+ycCkNQitOyekW7apCINiaXxNUCaliUNfuTolTFEV27I6yY3eUN95I8a1v/ZT9B7rp7++nUqmwc+fONT8biblweG0UU9Vlayut3kQUBUIbXCOCXj+pqStII2WsiSyODg/+djc2eysR1lXxni0JarUml07NIzukZVsuT8jJwuUk507Nct/9u5a39TkUOpUU1YMuTCOKLImcHAzz0GD0zqNEzTrTr3+Lgw98kuRAkJpmELXL7Ovys7fTt6oa6PnT8xTi18Z5rhUdU5M5PnhjiqMnu/9WaPIfR2x+Cx8TeLu82Dw26vk6PQEHlhUkU9Zw22V6Ax18Lz5BqVAivO9+XFqUjriO4lIx8jr583ku+nwcembbsrLSxXdrZNw1wpEbg7pGLk3fQCdf+odHGTq/yN/8f9/FbLMoTGWh1KSeq68q5V3P15FcypqLkyPo4Ilf34fwjUuMXk2yaBOxBZ2oqoRhWNQKNeRSk+6wk92f2M3XX/lLLscvsHPnTj7/+c8DYLPJ7Djawan/fhUj5r5tjrCYqqK6FXbuvzPtsKenh6GhIZ799FHyzw4yNZzC7VR5+ME+YqtUs+8WtVoTo2GiehW05srKXNPUkAQZ3CrhHWG08wl0n4PZikau0kASRQzTRJUltvpddGom4f1hvF2tYMwyrWVvs8XsIq+++H0GugfY+uhDzGQqGIZFzGdnMOah0+9YsbjWyo115w9sDoVCo0xlFYrXJn7+4enw4Aw7l8U4ZEngxEAIy7QQxHbelZqkU2l6Aj04uh2ce2OKt344Sq3apKMvwKe+vJ9A20oRD8u0WkUdoRXUt4J7cVmmO5up8Mo3x0hPFNHzdSqpGnmvA/8tgivluk6bU0FVJZR1uvqCILD1WBdtvX5GLyaYuZRE103iqTnat7n5zJeOkcj0EP/GPH19fbjdt8t+D+6K0rYtyOKVJMFePw6PjVqpQWY6T/feNgZ3bszEXVVVNE3D47Xx5T84zlK8REeX7yMNPgqZKpJNvPY3aj3bdrdKaaGM5HXg7/OTHctiBSzil0/T39mPI9RO07AIuVQ6/A4cqkQ5Ucbmsa2qpjo4OMjc3BwjIyP4/f47nlO11qRWrKOuYhhvc6tUcjU0zcRu30z0flGxJeIm4FTJ2U18+9rQpvKIqoRtb5SEKmLVavy3r/2ELTk7gVQNtSuMaoJ/Kk+lYfAzt8LWqKdVmL3W2ZNumt+ydFBkGa/Xx8JMhcxYkc4dbcuFm8tnF0hlaoQWi8jOlvl6vcvD9o47W8eshWw2i91u58UXX+TJJ5/k8OHDTE1NrWkcHgq52PtgL++/OEp8uIGkSjQrGt37Y+w+cOc4Zn6uwLvfvIo5nCdfNXEWm2jzRUpulWC3F9vOCI4Oz4asDdZDtapRr2gojpUxl4EBxo2iUC6X46WXXuL06dP8/u//PtGuPmRJXHMuL1NuMJWuUNdNJoYvc+mdnzI/Ncb/9Lu/g9fnp2mYqJK4bsySTpRQnSstORwBO5VcnXKpQTC0meLAZqL3sYEj6CCyJ8L8u/MIrFSA1CoaJ7af4HXpDO1bduEbr+Do8yFIIrJlEZgpkrqSYupY9zItYdvuKKOn58knSnhCTsrZWitIumYinIyXQYCufW2gihQvpTCKDerFBvab6JeWYZGPl+g83EH/OkImvk4vz/z2IXZcSHD1zDzzM3k03UQWBOR6hgefPMC+k30Eev1cSZ5namqKI0eOrNjH8Qd6mb6SIj6cxhtz4/I7MA2TQrJCs9zgwJNb6eu/cyeqs7OLb/zVz8hO/wxv0M3TD/azdUtoQ2IxG0Ew5MLmlKmkq5hmHcNbR3AoWJJFs2pic6uEom4CW4NcvnSZd55/iad+5TNoQTcaFjZBwKMZeBAI72uj96FeRFkkP51n8YNFKvEKmXSGs1fOood17r+vnRMDIU4OrK/w53TbVnQYb0Wj1kS2Sbg2MEu5iZ8/yDaZyJ4I069Oo7rV5c7O9QTi0KFDzJyboXuwm7pg8ONvXEJTVJwBO2OXEvzsRTu//NuHV+xz+84IZ3v8xK8sIckikl1mz7EbsuMzkzmys3kih9vRrqapJisk4kXsPht2RcI0IV/TEEWBdlPAFXbRKDZYfH8Rva4jyAKKXcEdc+Nqcy0XNjxtbg5/YiuHP7EVy7R47fXXeOedd7h4tYPDhw/z9NNPMzg4yMTEBP39/SvU7VRV5le+fIAXvn6R+FiG3GwB2SHTd6CdZ7+4d8PzZdFolMXFRbq6unC7bbi3fXTKlKZhUlos4SppmMM5KpVWR1YKOkilC4QGwoTbPIQesrM4vch733+Ppr3J4UMB2jturJFG06AwVwAL+h/tX1bbrGaq1DI1REWkaW9y9OhRHnnkkRVKo2vB6VBweu0UkmW4pfvZKGuEun2o6maS94uMsNvGg9vCvHJ1icU2B/5uDyYWS3UdjyKxJRplIdyHOjJBzSPQ0xVG9jgwnTLuTGvMYjpdYX+3n/5tIc64VXLxEv42N6YJmdkCwW4fvX1BLpyeB0lc0Z3v7A8yZWaIt7mwSwKC14YaatDl/PDeen6/n+PHj3P48GEGBlrU5MHBQWZnZzl37hwHDx687TNPfmoHoYibq+cXaVSa9O+OcvyB3jXFnq7D0E1++M2LZKdyxLb4yWki1XwDR75OM1tnrlAn2OXiE9sjdNxj8drnc+CPushezNw4ftOkXqozsONGQppKpZiamsLv9xMIBNa0dLIsi3cnM7wzlqK4WEJoGIzFE8SLLnZs2Y/T2TJ7l8Q7x2PhmIeZcwlM01pO9qq5Or42F+5NJeBlbCZ6HyN0HW8FSMlLScpLZUSpVcmV7TID9w3Q9txevvviOKosIlybsxEEAcmlQEmjot2Q/D1wuJPEQpFLb82QnMhhd6scfHKAI8dbFCCbXcayQNcMwr0+yvNFjPkSWknDams5cjYKDXILJZwxNw8+sYXCdJ7sbIFKoQ6WhcNrI9jlw9fjQ7bL2P12dj3cx/bjXVSSFXRNR5REvvm9bzJR/YATsRbN4eTJk3zuc58jlUotz7lAK4H6/FeP8OoLw0xfXCI5nkUQBbwxF/ue3sZDT2zdEB3og9NLZK+K1D1x7LKN1FyB2B+cwBG9t8oXtKwu6gtfBTQSAAEAAElEQVQl5LJG+WwaTTaZm5vD5pYxHWCJKtue7MLuMPivz/9X3jjzBvZuO1v2tlNL1rAMC0ES8PQFCe8MExgIIKkShdkCEy9N0KhqVF0KUkeY7mwfmaUkYnpjQdPgnjbef2mcYqaKdxX6WWGhRNeeKF09H8380CY+fojti9EoNkicSyBIAs6QE0ESaFab1DI1urZ10f9oP6lcDbMpEOxxY1Mkqvk6uUQJy7JWdJEjUTef++phzr47i1bT6dseWuEv5XSpSHaZqm6AQ8Jrmrg9Nkp1nVylAQi4VAnt0lXSsgur2s2FSwny1Sa6ZSFY4FBEYmEXA7ujdOyO4u/zr5xnFQXS6TSKorTmaAyD3btbhuQDAwNMTU3R1dW1wgw41u7ht/6Xk0xOZCnm6/gCdvoHgne0brgZlgk/eXGCwuIIO4928sQz2z8SumItV2PmzRny03mCJrj9DvJzRRSbhDmaQZAtth3rRwbkgIP7fvM+Co0CMxdm0OIaBaOAIAgYDQPLtHDFXHQe7SS0LYRlWSy+v0j8gzhaWWut87YGx3/t+IaSPGhJzO852c0bXxuilKku0zeLqQqGZrD3RPcmbXMTHOkL4nMoXFwoMJ+rIgoiR3oD7OvyM5Eq0xmMULbNI3tl3J7Wu1l0KOipKoKmU9db6rDbBiMceXIrH/xkkviVJIIo4o25eeKzu7DbZRwuBVNfqSQr6ibd7V4e+vxuRIeC36ly5dSr/Nv/5/+D48eP89BDDy3HHRvFo48+SrPZZGhoaMXPe3p6cDgcvPvuuxw7dmzFvS/JIsfu6+HYfXd3rLGRNEtjWcL9fkRFQMkW0Hw+ym0uTMOAxQrbHTYe3BbZkPr6emgpJW9hZnSBxctJZLtMOV+hbXuYIydvnPfAwABPP/008Xj8Njr8zbi8WOQn5xdxDGeJZqpgWIQVH3HfLqacZRZLOv3rGLPfjIMnehg/Hyd+JYkj4EArawiSyOFHBzZpmzdh85v4GEGURXru7yGyM0JhrkCz0kSySXg7vbjaXKTLGg6/HU3PozaN1kCuadEsaQg9nhVcbUkW+dRnd3P0/h5SyQqhsIvYTR56ew62c+ndORaH00iKiCPoQGz3IDgk4sMpBAtEp0LbrjDzo6f57//7VYK+PopNAV0QAQvRtIg4bfT2B9h2pIO23VFUt4psl/HdlEwIToGFhQVee+01PvnJTy4HYYFAgNnZWUZHR9m+fTsAkYiLX/utwySXSqSSFWRJpLvff8cK2M0YvZDA4bIj+y3au6MkhtPMTGWJ3GOip9d13n9hhHdfmyRvgd7txcpWKVYbWLkqsm4S3BLi2KEODMMgmUxSqpTo29vHrk/vQitpywbudp99uZNiWRaJiwnq5QaTCkzN5zFME38owrHt21AyCnpdX7ZeWAu9fQF2HO/k4qtTmJqBN+peobpp99o48fiWzSDsFxiiLNL7YC/eTi/pkTTlxTKmYSI7ZHof7CW0PYQj6MBURLxBJ+npHDavHUoN2o53rxpUdHb56Pz83lWPt21HmIOPb2HorRmsoIOedi+D3T7Kmo4mqgiGhZisMJoRuFBLUdXqRLf24grZkQQRE4uibpLIVxn/6QTtp+fZd6KbbY/1Y7upovv444+jaRqhUGhF904URbZs2cL09DThcHgFlVOSxGXPvA+DifE046eXUCSFsz+ps/dQx20G8neLWq7GxI8nKC4U8fZ4MaMu2mWTxak8elHH6RAY3BohoJlMvTbFwOMDNIQGD/72gzzefJzCdIFaptbyDfWo+Pv8+Lp9y96rxfki8+/OY6gyKY9CrZCns2Jn7p05tj+7fV3xnZtx38N9ZJdKXD09TzFeAgTsHhsHPrGFE9fMrDexiW1tHra1eag3DQSBZfn8RLGO5ZTp7u9B8N14txslDcsmYTlWGnw/9Us72bkvxuxkDkWVWhYK12b5dx/s4MrbcyyNZfHFXGg1neJSmb2PDfDAnhvq3r7jRzl39gyXLl3i5MmTH+p6FEVB12/30ItEIthsNk6dOsXRo0fXVOLcKJKJEkZDX1a8tQkG3RE3Td3EAjJ1E7Wqb9gq4k44eLSL6V/qRC95KWSrWIqdT//qfStiptHRUXbv3s2BAwfW3I9hWnwwm0OYKWJPVJDaXYi2lrpmR8pE7ejlwnye/g36FXf3+PjMV4+0VDcns4S7vBy4v+euzOp/EbCZ6H0M4Qg6cKziExXx2BjcF+PsVB5mCthUmWbToOCU6d7XturDE23zEG27vfri9zv41d89wntvz1Iu1Am1uTn+QC/pbI2FuQKmaeF3q2jjWdIfhDm3uAiBabo6onR3diDQUtGcqTZYGElxdSTFkaNd7PvU9hXnbhgGe/bs4eTJk+zde3sw2NPTQzKZ5OLFi+zdu3c5kFzrvDcCu0tBFlQq5Tz1SgNRFu65+mOZFhdeGuOtVyaoBFQ6+vx0bg9STFWp5epUazXsQTdWw+TMj0Z55rcPMTg4yPbt29H1VmfT7l+dYqGVNcqLZTS3wsxiEb9TQZFEEoUaRVVEzNWppCr4uld24soNnbFEiWy2Chb4/Dbu/9QgqkPlyqlZloZTrfkGSSDY5ePBZwc3rRU2gSiJhLaFCG4NopW1ZdbAzWIr/jY3T39xH2++MEyl1KDn/l4eenbw7o8lijz7uVbBydBNIlEX1USF7HiWer5OfibPREMn2x0kbyp0RQL0tN2eLJkeG4Vak+Fyg8KrkzSqGnt/accy/TQUCmGa5jKV8lb09fWxsLBAs9kkEPhohIg8Xjsuv4viYhFfuweH895EESzTYvatWYoLRTx9Pi4sFpnJVqnrBkKbB6sNykYDq82Nz+8geSmJM+SkEW4sU8m87esnmqXFEkbDYNw0GE2WsUnQ1uWguFCknq+v+t6pZqoU54vo9dY65gg58HZ5+eyXDzJ9fy8zE1kA+reGNkWeNrEqbhXa2BJx4+/yUV+q4Y6XadbKLTG6pkmx202ky8vALfFMT2+AnlX86wa2hPjEF/fx9o9HKS5VUGwSex8b4OnP7lqxXVtbG11dXRw4cAC7/cPTHT0eD8ViEa935bPm9Xo5fPgw7733HgcPHsTl+vCiTLIigiAsUxYFQcQyTZTrtHITlI+YHh3pcLJv/25qxRyCIKxI8vL5PC6XC0VZf43LVzUSuRruTA3RqyJee6eITgXD1sCdaTCbqdDQjQ175vUPBOkf2PTnXA+bid7PGT6xrx3Lshi+kKCUqaG4VQZ2Rvjk0a5VVYvWQzDk4pO/vFI1qsep0tPlQ6/rDL04wluvT1GKBlDraQSzykBXDOVaddiBhNeh0PAZJPM13jk1h2GYHP7sLmzXBvUlSeK+++5jYWEBXddXpQdFo1HsdjsffPABBw4cWFGR/zA48dgAqdkCuaEsS3qKXQ9sYcfujQksrIXyUplzp+YoeWQ6wu5rRuQS/nYP3oiTaq2G2+OmWNWYmMrx2ndP4+x08uyzz97Tca/j5k6Kbpi8M5zk7Ok58mNZhJKGYIHpkvFsC7H3UDtfvv9+5iZyNDUDX8DO4K7oJtVhEysgCMKKrtit2HogRv/uCIZmoDiUDXd7VkNb7EbRxtfTontnxjKMXkky45Rwx6IcVlvKbbVqFYdzJfVYFAUCLhW3TSaeq/Leu/PYnCo7n9m2TOMURbGlUrwGOjs7SSaTJJNJotF7Ww8A2ju8fPZ3j3L+/QmO3z+I339vXlblRJn8dB5vt5eL8RIjyRIhl43wtb9RuVymiZOL8wVUWaQt6GDsnTEO/+bhO+z5dggCYLHunIxW1pg7NUd2PEsy1RJVkATwO1XCPX7aj7TTty20aZK+ibtG0KXy8I4oLzeapD0K7mwDS4KK345ri5/HBqM47+J9dehYF7v3x1hKlHC5VUKh25MsWZb5/d//fQDef/993G43Pt/djzH09vYyMTGxauHabrdz4sQJ3nvvPXbs2EEw+OGejYFtYew+O6VUBV+bG5fLRaVSxe1xo1WbIAoMDEbuvKMNoFBr8rORJGevNDg1dQnVUedLT6xcU2ZmZti/f//d7Xg1uYCPpgG5iVuwGdn9nMGpynz6aA9Lg1EKtSYORaLD71jVaPJeMHdmgXffmCblkukJeHDKvfi8PpaSS3R1dq14YG2yRHvQTUKocubMAr6wk12f2r4iOWlrayOZTK4q3W2aFrLdSd+2Qc68/z6HDh68J+rD4I4onj84wQ++18DuUPjVLx265yRncmiJZLaKv89/m3VEuVJZru55HSqzqsjZN8f4R//bZze0b9Wt4unw0BhN0xtyMpWuYFkWHX4nvqaJM+TEeU2l0DAtXjm3wOnvj2BfqhBx25CjLkDAKGtUzyZ4ZyJH6Znt/PJ9vaibxsWboNUtMjQD02hZK6xmlbAaJEW6TQH3o4Cpm4y/M8tIsoQYchB030g4a7qOpmmrrgGKLNIWcJKwKgy9O0fH7ugKJcnryd5a9ORoNEo+n2dhYYHOzvVN0TeCwR1RHM46PT33nuxkx7MYmkEFmMlWCLpsNwSkLLBMk4C35a06vlQmNhCgNl2jsdTAE9gY+8HT4UGySWxRJTx9ARyiiC3fwDsYwh640eXQyhoTL08wMZRgzjTJALplIljgrDboHE5STJXZpptEdn40AecmfrFwqCfQmuHrKjCXrSAIAjvDLvZ1+ekO3p3FCbSUu1fr+K167EOHOHXqFEeOHFkxu7sROJ1OarXamr+XZZmTJ09y9uxZ6vX6He1KVkOs3cPOk92c+/EYlmXhCTvJZjPQlMjPF+k92M6ufffOzjFMix+eX+DqK5N40jW0+hKZoI3ve+d47sF+7IrE9PQ0/f0b86zzO1ViAQezIQf+qQKmW1mmbqKZlAM2DoRcG+7mbWJj+NDR7dNPP81XvvIVPvOZz+Bw3FulchOro940GF8oMDOVo1ZroigS4YiLndvCBNcxHgdo89rv2fh7LWgVjZGziyRFaAs4UWSR9vYW172pN0mmbq+IiyLEAg4SDZ3RCwn6jnetMD2WZfk2bnup3mQsWebifJ58tWX66VJDjL36Po8d3kln5O5pQI1Sg3q+TsCt8tiTOzl9+jTqR6C2uTCaQVMkwrckjKZhtILK68mfAGWzTEDwYLM29gIRBIG2/W2UFkpsqWp0dPuxBHDWDETNpO1A2/J83kiiyPs/HsOdrOLp9yPcFISLLgVv2IEyU+DSS2N0d3g4vvXDzyBt4uMPraKRn86Tvpqmnm/Zp4iSiLvdTXgwjLfb+7eSyN0Jxfki0+NZKk6ZDtfK58ThdFIulZFEaYWk+nUosojTZWMxVSV+JUlgS2C5qNTW1sbS0tLyerUa/H4/siwzPT1NX1/fPV+L0+mkWq3idN59cHozyokyqlslUWpQbxqEbkp+K9XKMhXM51BJlxtMJ9JEfV7qhfqGj+Ht8tJ9Xzfx9+NES1pL7KrHR/fJlfOXi2cXmRxKcBWTmmURdKmosohpQqnRZKTepJmtov5sFnebe1XK5yY2cSdsibjZEnHT0A0EhL+zwqQoihw9epQzZ85w8uTJu55bt9ls1Ov1NSmggiBw5MgRLl++TK1WY8uWLXe1f0M36ez1MRZxkZ7IkpsuolEnHFPZfn83z3x+D7YNFuvWw0K+xsSlJQKpGo6ok2y+QXsZ5i4tMbOrjf6gjVqthiTZeeu1SeYmszhcKjsPxBjccTsrQhIFDvUEmIkXqdWbONI1BN0EVaLY6cbRH2B/l3/Nc7myWCBdatAZcLK300fgDnHwJlr40HfC5OQkX/7yl3G73XzmM5/hK1/5Co8//vg9K/xsojXbdvpSgnOn58kOp7GKDSQETCwMm8TbPT62Hujgwfu6iXj+dpK59ZCfzjM3mwe3coMTfg0+n49UMkkhn8d3i+eSKAooHhvziRLZ8dyKRO86rqv2zeWq/GgowUK+hsO0cDVaqllZVaQiehl5ZYjPHtvCoa0br7rPnF3gnR+NkU1XcfvsHLi/g0q5QiaTIRz+8AmPZVnoTQNBEm6jHpTLFbw3KVBVymU0rYHbG8Qy1nF+vgW+bh9bPrmF+Nk4SqKMZVo4wk5i+2OEd4aXz+P85SWsxTLuTs+KJO86BEnE0e2lMp3n/PlFDvUHUdZREmyUGiQmc9SqGsGwi9Atioab+HjCMi2WhpaIn41Ty9aQ7TKGQ8IUQLZMMiMZ0lfTeDo99Nzfs+zj+HeFxNUUi/ka9rDztg450KJBF4qtZ2sVtoLXoZC0S0xcTtJ7vAv3NX8/RVFoNpt3PL7b7UZV1WX7hbsJ9Gq5GkOn5qgVG/TvitC1J8rCwsI9J3qG3lpjDNO87TsxDQPxGqVdlgQaDQ3V7kc0BczmxiXjBUGg43AHgf4A1XQVSZVaXb6bimGNYoPMaIYFy6JqmLTfREkVxZZpsiqJzNWahOMlOqZym4neJu4J/yM6PKqq4vV0862vv85Djxy5KyGlnp4eZmdnl0Xkbkah1kTTTdw2md27dzM5OcmlS5fYs2fPhvZtGCbf/+YQl96YRtd0JEVCsKB7d5QnPr2LPXt7N3yed0JV09GrTRQBGoKOy+dGyuhYDZ2qpjM6OkNP7xa+9RfvM30ugSCJmIbJ1VNzPParezh+/+3nsrvDS2l/B6dcNlKLJYSGDk6FYKeHR3e0LduG3YypdIX//sYkhXNx1LrBVY/C1ZPdfOH+/js2PW6Frpsbtsj5ecGHTvRGRkY4c+YMzz//PN/85jd5/vnnicViPPfcc3zpS19aV3lnE2ujoRu88OoEQz8aRy1rhEJOlO1uBEnEsiyMcpPqTJHzEzkWp7J85lf30vkhaAz3gpmLS6Q1HW949Zd3JBJlYXEBVVFxuFaem8+hkBPrjJ1fpPNoB+JND1w0GiWVSoHDyw8uLJIu1OlK19Gn85illom316UQ7PGRbAvyN2emsXSNwztatAFNNynUmqiy2DJSvQnlRJlXv3OVqUIVW9DJUr5K5cdTBLe3lD3vJdETBAGHxwZNY4Wfi2kYrbml6+qZpkU8kcDvDuAUFZAEXviby0wOLbHnRBePffL2l8LN8Pe2FPJquZZhut1vX5F0ZSsac6NZnNcUUdc8X0XCrcosjWdZzNfoXWVeAWDxSorXvnOZudkChmXhtMvs3N/OI1/Yjf0jMJbfxP8YWJbFwpkF5t6ZQ3TKlIJ2ZnJVstlrwiuSSIfPTofPhhAvM/bjMbZ8Ygv+Xv9dHaOarra87gQB1a2uKTZ0K7SKxuzVFCVVJOxY+z72ejwUSsVV52gkUUB0KCSSZcqJ8nKitxZM02RqIksyUcZmlxncFcXlUunr62NycpLe3t47Cg1AS3n3lb++wMUPFtEFkQvvzvMrv30Qwb/xos5aUBwKjUIDu10CBEyzlVhVK1VczhvPcE0zECydkM+DWSqBLPLyC8PUq00e+eQ2vBtgeqwl+AUtwZZUokzKMtesqDtUiVxFIycapEfStB9qX7MIbBomUxeXmLi0BILA1j1R+vfF7mnmcxObuFf89EcjvPfDMUrZMpff/DGf/70H2HdwYzTLQCDA2NjYip8tFeu8N5VlbKmEphm4nQp7On0c6euhlE3x/vvvc/jwYaqawehSielMBd206Ak62R71LD9r4yNpLr01gzPkWLYuSc8WqOcsCsUU8NElemG3DWfERW2qiDaTIRAMUMNCDTmR9Sp+v5/LF5aYObdEuD+w7FWYnMxx6qUx9h5qv00RXRAETm4JMxjzMpUuU2+auGwyAxHXCmX467Asi3dHk5TOLBKt6oheG81UnYVT8wz1Bnh4cGPz1GOjaU69OklqOo/LZ2P/A70cv7/nF0Jl/J56u0ePHuXo0aP86Z/+Ka+88grPP/88/+k//Sf+3b/7d+zcuZPf+I3f4LnnnltV5WwTt8OyLF55e4aLL47iR8C5PbTiZScIArJHxetRcRYbpM4s8F1J4EtfOrCmOeVHfo6mRSlfoymL+Nfq7AjQ0d7O3Nw87Uo7inrj4VVlEUMSqFU09IaOKt84b7vdTjKZ5NKSQSJXoyteQRvOIHptyJ2trphZatC4lCRSCxDv83J6KodXNiirIc6MZ8hmWqpaW3sCPLA1vExvyi2WSKYr+Lq9eBwKdadCdiJHh+BndnaWQ4cO3dP3sv1gO+ffn6dQbRK4pvR3azcvk0mjyAqKJtKzK0jJMBl6fYpaoc775QZH7uu5YxAmiC1vs+vIZqtUK00iURd13aRZ1TYkuiPbZcxyk4a+erW/lq3x8jeHmIyX8HZ5cKkSxbLG+6dmUe0yj35p32b3/mOKzEiG+VPzSD4bl4s1ZueriKKI1y4jCtA0LMZTZaazVXa1e2mvNpl+bZrtv7R9xb23GkzDJDeZIz2cpjjXUmJEANWpEtgSILQ9hLfbu+69YzQMGg0dSxKRpXXuMVHA43JTKpVW9W2SZZGa3po9vBnhcJh0Or1c3CmXG3z3ry8wcyFBs6YjiAI/6/DyxBd2s3tvbNl+IRaL3XFMoZquMn41hdjhIea1kxhNMz60xODDoXVnAzeCwECA7HiWWNSPxyaTq2mEXCqGoSPK1/4uFixmCmyJ+XGLAmVFJF3ROP3iCFqpiS/o4OEntn7ocwDQGzpN00I3rXU7LYosUhMEjLqBqZtrMgE++OkEr39vmLKmgwVD787y2Gd2c/DxgXs6z01s4sMilSxz9pUJBEWk50An0xfm+OHXz7Jnf2zDz7AkScsCc4lCnb/5YI7k1TTeZA1HQ6fmUXk1lmchX+MzBzux2+386LW3WZRjzE3nUQoagmUx5FY40xfgmb3tDETczM/mMWpNPFtuzP0GOrwkR9PMzxTg/o/uewi7bRw/0snL6SLmZMu7lO1BjhzpxCil6Nq/n/ffuoCFtcKQ3tfmIp8os7RYpn/L6vPJQZdK0LXyd7VaE1WVVviV1psm8XgZV1VHirXsGBDBXmwwl6rABsSeZ2dyfO8vzlJOVnAGHaTnCvzk+Qs0NYOHHr872uzHER+JGIsoijz11FM89dRT5PN5fu/3fo9vfetb/LN/9s/45//8n/PII4/wR3/0R3zqU5/6KA73cwFDNymVG5iGicOp4nAoxAs1Lr4+hadp4RzwrRsMyV4b4XYfSx/E+WBfjMeOb8xw0zQt5nJV5tMV9KaJz2Nja8yDe4N8bsuysAwLi/UFkgRRpKOzg8WFRbq6OmkakK83qTR08qUG06kKS7ka3bdUhPM1nZFEnXDTojmRR4o4V3SnJJ8dwa7QnCkQjrko2Z2cTWgMjVzFNVXBWdMxBDjbliFTavCrx3pw22RUu4QsCtQ0A49DoaYZyAJ09bTzwdwparXaPc2axraF6O72c2WxgNvuRxKvKdddS9SbWpN0OoM72IGnqLPrWBfBqAtP1IVWbRJo9+C8i2S9VGzw0veuMHk+QbOh44m4GLyvG0mVMNdI3m6G2TQRPMqaIj2Tl5MszhcJbgngvEbbigQcxDWD0YsJjj+1dVXq7Sb+fsPUTZYuLiHKIldLdaZSFdq89hUUbJsCbrtMua4ztFBA7fHjT1XIjmXXTfQMzWDmrRmWzi8hyAKuiAvFqWBZFlpJY2loifRImq4TXet2eCzTwjQt2EA8JcoSdpuNWqW6zB4wazo0Dag3MQ3zNuqi0+kknU4v//+nL44w+s4cgW4fLr8dXTNIT+V4+WsXae/wEAy56O/vZ25uDpvNyZULWfKZKm2dPo6d7EG66bsTFRG7Q6VQbJATRSQTnG6VaDRKMpkkFvvwAgn+Pj92vx2r1GR3h4+zszlmk3nCXhem2WKDZEo1vDaZPZ0BKskK3k4vkb0RAu94aVSaxD6Ej9/8XIGpsQyCANt2RhBlEUkASRTRDfM2+v51GKaFLLbsW8Q16OH1Qp33Xpuiooq097dEWxJzeU6/OsGuY53rqr5u4uOHhm4Qz9exgHaf/a6VwP+uUK020Ro63jYXoigQbA9QyBVIJjPEYhsTF+rs7GRxcZGenh7encqQupoiMl5oPQ8OGSlZxV7SGJUFrrR5ONDtJy5HmT4fp32hhlVrAgIoIrm8xo8E+I2Tfddm7wRMw0K8VgjTG01ERcTptK0pVPVh8eDWCKWDcwiPbuPS0GU+9cgu1EqKjvZWguRw2zANC8u0lhsTjaqOYpNxuTdmKTN8Ock7Pxkns1DE6bNz4IFeTj7YiyiKqLKIx6uSkgXMQgMhIGAUNZoOCb9nY9d57vQ8pUSZjt3R5XNMT+U49+Y0xx/o/UjmGf8+4yO7urfeeovnn3+eb3/722SzWfbs2cNv/MZvoCgKf/EXf8Ev//Iv8yd/8if863/9rz+qQ34skcvVuHQuzqXTcxRTFSwLZFVi68F2yljU54sEewMb6pYoARuOZJmLZxc5ebDzhgLbGshWNH74zjTT5+I0F0oIhgVeFf/2EA893M/BvuAdjytKIooqIVmtF/l6hpyyLBOJRrg8tYCFCyNXQyk3oawxG6zx129OcuhAOw9vjy4PWdcFG8lCnoGigdY0VqUgijYJAwE1Xadkl7hcMnFPVvCWm0hRJ1ZDR50vM3sxwUhPgMO9AYJ9AXbvi/H+mVkWMzZkzWDn9jC7j29lJHuRubm5Vfn0d4JumMzlapTqTbqOd5H7cZXEXB6c0B72L283sxDHkr248hpHHuqne18M2S7zxX90jPmZAv1bg3fFG//+Ny4y8vYsnqgLd9hJcanMme8OI20NUDVMnJqBsMb9YBkmlaqGb39kTcGeUr5OUwD7LRV7h1ulFC+jVbTNRO9jiOJCkdJiiYbfxtxUiYjHtmag7rbLaIbJWKrCfSEXqasp2va1oazyTFqWxfypeeJn4y0BF7vCUqlOIVFHEAXCLpXQQIB6rs7Mz2aQbTLRPatTbkRFRJYlLN3ijhUlQFFVdMOgli0jZjWMdA1TM9CbBpbVEna5OQhZ8X0U64yeXcQdduK6Ri2VVYnIlhCJ4RSXLy7x4KOtzlJ3dzf/5f98k6mzKURBQJQlKqU6TzyzY3l/rqiLww938eNvf4CsWQzui7H/gV5ESyQ9mqY+UqdZaWJZLW9Cd8yNv8+PuoE5E7vPTnRPlLl35ujs9KAMBDk7HqfSNMlXq9hUiZBqcmywG3tdR7MgdiBGMOrht/7J/TSbxl1bPAxfSfLCX56jtFRBwOJMp4+nfmkHgYADX6ZEodZctne4GbphYWERsCDQF1iThlkrNqgWGzijN2YxHX4H5WyVRqmxmej9HGE2W+XHZ+dITOQAaOsP8IlDnQxE1qdV/49AtM1NsMtLYjiD3WujUWqw96FtTE6O4fd7qBoiU+mW35vbJjMQcd9WLG9ra+PMmTN4QjHGl0r4kjUEWURpa703JZ8dbaaIPVXj4kKBoEtlMVUnmqghCKD0tijpRrFBIF4mGXYwtqXMzn1tnGl3szSaJtDjxWha5OYLdO6McvTEdubm5u5a3GU95PM5Dg7EcDjs/PS//AhhfweWLON2t/5uu/a1c/HNaRKjGbztbvS6TilZYddDfRvyOp6cyPD9/3KWWr6GM+gkt1Dkp399AdOwePCxASRR4PBglBem8uSHM6iLJap2GeeBNvauIdxyKzKJEopzpQWQw2+nWmxQLjc2E731cOXKFZ5//nm+9rWvMTs7SzQa5Td/8zf5yle+smJG7x//43/M7/7u7/Lv//2//4VO9K5cSvDyNy+RmyuiOGQcATuiKKJVm5z98Ri5xRJuAay7UEL0hhykRzNMJors7llbhbLS0PnuT8eZ/ukkAR3sQTtIIkZJo3hqgR8mK8hf3M++bv8dj9m+NYjzQoJiQyckrx+gFDWBekXCu5RHbRg0BQF7pUnIqSJ+kOCtUgPLgk/sakMQBOxOF5qmQQNQ1k58BFXEvDbUXM9WCdeaSG0uRJsE12iJYkEjXqgBARSngnOgjn5hipO7H8bX4WHPyW4cQQfd3d1rDk6vh6VinZcvJ5iezWMWNVBFvDtCRCeypBfyLJXBUkTq1Rr1ZImdW7rZf7ybI58aXFbJDIVcq3r6rIe5mTyzF5fwd3pxX5ujcXhsxK8mUWo6jYCderyMvdu7aoClJapUXTJHD3as2ckNhB0oAlQaTTw3zUnVSg3a/XZs7s0A7O87EvESly/EyS5VkCSBWI8Pf1lvzYpWGuiGie0OFXWfQyFZrFOIeXBlaxTmCwQHgsvqnNfvr8pShaVLS3g6POiKxOnJNIv5OsY13zpVlugPuTjQ7UfXdBbPLhLYEkBZZQZPcSr4ww5s8QIVTce1gZewTRcpXEkgaWA6FTS7RFk38Gsmi+cW8XR46L6ve/l8PR4PpVKJYsFEqzWXZ12uQ5IFBAEqxcbyz8rlBqnpCp6IC1+bm/RUjuGz8RWJXjwe5/TkTxl8IMpTTzyI6lSWVU3nhucIhoLLwiambpI4l8ARdBAaDBHeEV6zYzo/V+DqUIJaqYGsijSn8whSkyd2tNMQZM5dvIhUMzm0YwdmqoomQM+DPcvWEq4PqU73+gsjVNJV2ndFwILEcIr33p/jvoEQHakylzEpVJutmehrS01TN0mW6sQUhYjPQWDr2u8lp9+O229nKVPD42ytKdVMlY6gc3MO+OcINc3gxfdmSb4+Q6DcBAGWpgr8UDf5B09sW/M91GjoFIsNfD7bqhZIyVKdofkCi4U6EbfKnk4fXYF70yyIx+O8++67TKbPc+ihZ8knq9RMjU99YQ+KAn/543couToo1nQEoTV7G3HbeWxHlJ3tNzrm14vmtaaOVm3iaRiIzpXXINgllLpOVTNIlxsYhQZSVUfqvJEgSV4bZr6OWNLIlBsc6gnwyef2872/epfiUgVBFOjd384zv7qHWMzLe+9Nf+hEL16oMZ+rIQrQE3QR8dhYWFhg7969vPLKK/h8Pr75zW/yJ3/yJ8uf6e7x8fSXD/CzF0fIL5aQbBL7Hh/gqU/vWudIN3D+1CyVTI3O64W/NkhN5Tj3s2mOP9CDqsoc6PJTvK+dl608oUAXfVEnJ3a2rakvcCsiHV6mz8VXdEEr2Rq+mAfPL0Ax6UMnegcOHGBoaAibzcav/Mqv8B/+w3/gqaeeWpO//Oijj/Lnf/7nH/pEP+4YHU7yw788TzVfJ7YjgiTfCMBdfjvOqJNcvIiWqBG/ukTH7rZ1u2XXIblUrEKdUqGx7nZXFwrMvjNHRBBQ+2/MyEgelWDVRmaywNvvzLDjc947Shi3DYbpDDu5Um5gOdVVVfGgRdNYylTwLtWxNU3MoJ1GTScg2XHtjSIA4nies744uzq8dAWcKJLYihfsMtY6anFW3UC8NgunKBIGYDUNsF0LXJsmlgjqNcrQ9773PS5evMjAfb089RvHVuyrp6eH119//a5maCoNne9fWGDxzAKBpRpiTcdSRAohO7kBB598dAvFiRyVYp0f/fgtPv/lZznxxB68Xd41aUwbRbFYR6vp+LtWilCobhtqQ6fjkT7GXhonOJ3HFnEiulUEQcCsNGmkquQw6Xykj8Pb1i4o9O6MEg6rzM/k0Tt92GwSxUIdJVdn5ye24ghtquj9fYWum7z8g6sMvTlDJVdrzUaZFhctC0epyZZeP5mIHfsGbEUkUcCq62Rn8mjJGiPfG8EZcYLV6u47I07cbW7ys3ma1Sa+Hh/vTWWYzVZp89yghNY0g9FkCY9DYWvURW48R2GmQHjH7fegpEj0H2znyqUlFqvanRM906IxVUDRJZJyk3q9NXsqVJuUQk6UHh+LZxfxdHoI9LeSjkCgJcIUCsVweG1U8nXsN73wjaYBCPhuus9VVUJ1yJQzNfSAQbOm47iJOmSaJq+99hr5fJ6tW7di86hMvzZNbiqH3W+nbbANSZVWqG9apkUtW2P+1DzZ8Sx9j/TdJnpz4YNFXv7rC5TTVRAERCzaom76QiKNpTqWYaFNpkin0gwGuokNxojujRJcYzZmo9B1k0q2dq0g2VrkbV4bxXSNjs91UF4q05zOMa2bzOeqSJKIdW0NjSky22SZrsPteNrXruon80l0dQG17iExnEQA/G4b939iy4a6nJv4eGAuVyU5niVQbmLr8YIg4J8pkB7LMnOwwu6Ole8y0zT52auTnHtzmlqxgSvg4PAj/dz/8A2/tmSxzrc/mGdpsYijbjChSgwnSnz6QOeqyo0bQaVS4a//+q/J5XLcd/8hPvvZo9Trdb761a/y1lsOOvacJC6G8VnQWzegYYBLIVlv8sNLcdx2me6bEs1oNEq1kMPhUtEcCmKhjnRtnbFMC6umU293EXPIqLLUoquLAlyzHLi+HZaAJYAsimiaxrtnfkjNNss/+L3/GUkS6Or2LccugiAsq5ffDd6byvD6O7NU5gogCHgHApzcE6An0FozZVnG6/UyMDCArusr6KF79sXYsStKKlnG7lAIBDYeH2SXKrexROxeG7VCnWq1iarK5HJZ/vLf/d9JptJ8/dvfwalIG4qPr2Nwb4ALb8ssXk3i8NlpVltU1yOP9t+zh/LHAR/6Cv1+P3/2Z3/GF77whWUz6PXwK7/yK0xNTX3Yw32sYegmr31/hEq2RmwwvDqNxQLZriK6mpSmCpSibnx3UIoDQADBAqwbqm6maWKZrJgduTCUQMnXUXr8ty0AolPBrUokx1oB2tbo+sf1tHsY2BFh+q1pclVtTXnbYlXHyNZQ6zpmyEHTsFDqOs6QCznoQJBF1JkChdkSI4kSXQEnXQEHIY+dvGjicsgYxQaSd2XFxaw2QRKph+xEPDb8bW4WEhWURLWlztk0abhkxC7vMi1k3759TE9PYxjGbecZa2tnKd7kb772Hi6nF8UmsWUwTF9/YM3Eb3SpxPxohsh8FcklI0acWDUd32KFGUulcMLN009u4zvf/g5Z/yihfba7Ui1cD6GwC5tHpZKt4o3eeKE1ig3698Z4+uEBXpRFxk7Pw2IZW7wMgCaLWFEn3Qfb+aVHB1YV8DEMg9OnTzM6OspYdoQjW59hdiZPTTcIulV2PdzP8U8Nbgqx/D3Gyz+4ypkXRnEE7XTsji6/EI2mQeqNWa6eXUTYH0W5g+S9WWnSTJRgpkDermA2LRwhR4uyK7bk/rPjWZKXkixdWMLmsdFUJObzNYKulZRQhypRbUpMp8psCbsQFZHC3OqJHkCgP0BXp4/FeAHNY65bfDKKDYx8nZpDolo1UCQRWQKPIKL5bIxWNfYgkRnNLCd61+Fyqew+0c2p/z5MPlHCE3LSrOtkZwqE+gPs2XfDb09VZR54dpBXv3WZ9FQOb7uHBz+5bfn3oijyy7/8ywwPD6MVNCZfnqQULxHcFlwu7qRSqRWJniAKOMNOHCEHhdkCEy9PsPWTW/F1twLfel3nzReGqZW05fkSrdpkdiRJ78ld7NjXxeLkIqXxEoGeAOyCwU8NfiSKlbIs4o+5mb0QxxNyYllQz9fp3RPFHXOz9amtCD+dYOQbL2HZHPTv34skSXgsiIacdBxop/N455rn8p3vfIfx8XFkj8yX/ugZpq6kEESBgV0ROrdv+nt+XFCrNbkytEQ2XcHnd7B7f+y2DrJhtmb7r8+tC4LQGl83LcxVBGlPvz3LG9+6jKyKOPwOSskKr35jCLtD4fCxlrjfhfk8icUisdE8ZqaGz2cjaZic8TvuKtGraQa1poHXLuNyuThx4gTnz59f9tCsVCq0t7fz6uuvY84ZRLq34J4sUL3amvMVZJG2wzFmPApD84UViV5XVxcXL15kR3s7p9rz2Coazbkigl3GrGiYAQdGzMWeTj9dAQfumJv6bOudLUedIAoYqRqGV0Vsc9HmlvmP//E/srCwwODgIP0DtxdzIpEIqVTqNi/j9ZAs1Xnj9BzW2TgRAzBNiqkqL+Yz/LOvPADAww8/TLVa5cknn1xVgViWxbuyoLiOaLeP2aGlW7ptdSK9PjxuG6lUin/7b/8to6OjxGIxRENDtK3ftV2cL3Dl4hLjY5PkK3HGJ9/lU898AaMSYmE8i29LkP0nujl49BdDKPJDJ3p/+Zd/SSQSWVPAolarkUql6OlpiYQ4nU56ez862dePE0aGkyQnswR7fGu+9CRRAJuEIIugmxQXSxtK9My6gaWIOK4F7bVak2/8+RnKuTq//JsH6ekNoBsmpUIdBaFFbVwFikuBYoNa8/ZE6FaMJsvMXxPySC+WEDs8+FdJ9qqajlzVEWQJzbBoVjVCqoSn39e6Tlom3vZ8ncV8DQCPXeHY1nZeODvJzu1BmpdSmDUdyWcDAcyihllrYtsRZk4ROdzm4WhfkO9qJomLCdRSE0MWEHt8HD/YwcC1BV9VVX7nd34HSVp5/SNXkrz+4gjpITuFy9OtKpVl8Z5LpWN7iMd/ZSc9vbdTjxYLNaSSBrqB6HVRquvUdINKuYKcFxlaKHCiy85rb7xG/0A/r776Krt27Vo2Nb4XxNo9DB7t5MJPJtDqOqpDppKu4Aw4OPxADz6Hwmcf7OX/NfRTfPcNosohLCzCYRf7d0fpD7vWVMsTRZFEIsH4+DgPPnOSZ548SXomR62mEwg58XWuTgfdxN8PxBeLDL05gyNoxx9b2UmRFIlAj4/s0BL1mTx2twyrPLeWaaEvVdBmC5iVJpYo4m734KzqeDo8OEIOisUG75+axR90sv9IB9nJLI1yg9yZBTRM/DvCyxXp63AqEtWmQUM3kVQJvaqveR2OgIOu7SGuXk6yaJh0d/rWFA4yqzqYFjWrJRJlmCZKvYnHZ8fR5SVTaqDHvJQWSxhNY1n90Waz0Wg0ePSpbWh1nfNvTlIbbyDZRDr3RHnq87vx3FJkOnK8h66eAPlcjWibi+At1KFMJsPhg4cZfWGU7EKW4Lbgiuel3jSJ52tYgNsm471GXRUEAX+vn/xUnunXptnx6R3YvDYW5vLk42WCXTeeO9WpIEgQny4S+HQAW8zGP9zzD2k2m3R3d6/5nX4YfOIzO/lesUFyLAtA+2CYxz7VoqpKfolz9XOMNC8y4BrgwLYwkiLh6/YRGAjgjDjXLQgdP36cRCLBwMAAPTuj9OzceGC6ib8fyOdrfPu/fMDC5eRyIvfBthCf+53DRG4qGHf4Hfi7vZTmSnhnCggIlFQRKSSRmR0jIXXhcDjweltsowtvzSBKAuG+1rvX5bcTH05z/tTscqI3l6vhrhqYmRpKj5fmTAFv3SBRrKPp6xeHoOVXfGoiw7nLCeoVnVDMSaerwf1793L06NHle7dareJyuaihYA+2EbRAm8ojBe1IbhU9XaUxmsV3fyeT6Qq6YSJfK+zIsoyu65wYDJEo1hkHvNk6ck2nEfGhtbs5sDPKrvYWk+pgb5Cf5epYAjgKDbCg4beR7/Gwvz/A9nY/wpNP8u1vf3vVojW0RGCGhobuKtFbzNepLBQIN03UPj+WZWEfS5PLyCzm6wzGFMbHx9m1axeVSgX/LR7J94Ij9/UwcXGJxctJ7H47zYqG6lA49tgAkiwSCATo7+/nwoULy/HJwMDairyXLib48fPnKSYrVCoVkqk0rp4dPPb4SXy+X0xdgQ+d6PX39/NXf/VXPPfcc6v+/vvf/z7PPffcmjfjLxJGLyXRNQPbOlQUWRJxR1yU42Vku0w1UUarNldI1q6GcrqMa1uQvs5WJSWTrhIfz1HL11mYLdDTG0ASBWwOhYppYunmcpJ1M4yGAW5lmeq4Fi7O53nhxWG0qxkqixlK2QoY7dSiLjwuFbcqrxBP0E2LqtaEkklQkghuDyO33fKwXdNcuI4jfUEuTCywYCh0HmnHmi6i5+uAheS1o+wOs+hR6Aw4uW9LmIjHxhdP9DLcF2A+X8OpSGyNetgadS93M8rlMp2dK83VLw8lePG/naOWr9E92IH9prmzSr7O9Lk4f5Os8pnfOURf/8rKmSyKWECx2iS9VKJS1aDSRFiqUjMEZmdy/B9Lk+w59iBhp8R9992HzfbRccGf+dxuPH4Hl9+do1HV6N4b48TjA2wbjFCv1/nGN77BqZe+S09PD3/2Z3+2oX1mMxV++uIwmZybQ4eOs3XrFmS7TGxwYypjm/gfjysXElRzNdp3r/6SlwJ27B4bjbxGLdtA9zpXWBhYhok2lW+JNdlkNL8NhyDgEQRkp4LjGiUnk6yQnilQztfZd6gd2SYjKzICFuLVFBUjhXtHCPEmOmRDN1EkEUUS0HQTUV17rVmcL/Dm+wuU5os0hqqM76nTvzO6fvBmWWi6iVM3CSgy3u1BTJeKVddXLjDXEIlEWFhYoLu7m8P3hzj6QBeZdAOHU6G3N7CCEXEzYu0eYqvQEa8r9xYXiuSn8/j6bhT2tKbJ1USR8USdptXAAuyKRIfPzu4OH+5rM7u+Xh+ZkQy5qRyx/THkaxSlpmYgaQZaqko5XYJkjbo/T2GuQKqeYuu2rczNza393XxI9PQG+Mr/coKZyRyCAP1bQ8szLbVajaXMEkW1SK27xtZPb92QKXwqVeH0W+O0dzn5nd/5HQqFwkd+3pv4u8GpN6aYPZ8gujWI6lTQNYPF4TQ/e3mMz3754PJ2PofC48d7eNmwSE3lwbKwdTh49GgnX/8//zdsNhvBYJA/+qM/wqbaqRQbt9H5VJdCOVtb/n/YrTKnirjdLRVuwS5TU0S6HArKerYs13B6MsOrPxrBMVVEMUwSdpmlvUH2DMp4b6IldnR08C/+xb/gh6+9xSXNuRzfrCxiWAirqEbVmwZLTRvjF2YQBRnTVkY60IUsCwTtCvu7/ezp8C2vaw9tjyCJAufCTtLJCpZp4Qo7Od7p45HBKLIk0mg0+OM//uM1r0tRFHR97SLaapBFASQJy7pOFbWo1Gp4vO1IokC1WkUQBCKRCPl8/iNN9Dq6fHz+d49y5s1J4jN5fOEI+090s3d/i00hyzJHjhzh0KFD7N+/H1leO23RdZM3fzBMJVenc3eUVCqJLdxBfr7M8KU0x+/fTPTuCpa1ypvzJjSbzV8II8KNoJitIm+AB+xvc1F0KwjlJqbUermvl+iZ1SYVw+LIgXaEfJ35qRz1WpPd+2OYwJ79LSlvQRDYu7uN//yNtxFoEB5cmfBYTYNKRcO/P0r3OnQu07R4+4MFmufj5ONzIAg4nTZckkisZpAulCg4ZERVwgLKZQ1dMwiWmzi7Xfi3BpA73CsWSLPSpN7tpuOmwXufQ+GpnSHOLhmcKy7i3xPCbbTut6okYEkCPQEnT+9pJ3It6Ai4VE5uWZ3uk0wmiURWJivlcoOffOsy9VKD2GDkti6Vy2/H4bGxNJLmx9+6zFf/yf0rVDF7gg6+axOxahq2uTw+3YJCDdkSKEoSnrE85XYb9f1bCPqbBAKBdReou4XNJvPEM9t55MmtNBr6CqrMhQsXeP3118lmswwMDNBoNDaUZL775hSnv3cZp9fN/kOH2LWr7yM730383SCdKCGuM78gBezYgw6qM3kUUWCpWKPd50QUWy94bSqPNldC9tvQJYF6rYlcz3NpooDQ6WPoYhOn14+um7DFjz/sotg0W9TD6QLemJuKUKS+1EQSwLEjguhW0A2Lcl1jV5sbWRTQ6zrezrVpPq+/NMrsaAIzZtDM13BczZB025D8NrxOFddNBSVTFak0DZqmgVrW8LttBHaGESNO0qUG7T4Hcl3H3edf4eUmiuLye6zZbNLb20GsfbWz2RiWlpbo6+tj4pWJlqrmtdlC3bA4O5tjMlXG61AI2RUEoNo0mEiVKdV17tsSxmmTEEQBm89G6kqKyK4IXd0+wp1e5n86iQDUNJ2apiNoJvJikb/5/7zJnqNdaB3ahz/xO8Dvd+A/dPt7IRwO89xzz/H5z3+eRmP9GfGb8cNvn+fKq9P0Hujk8NFtd9V52MTfL0wOLWH3qstxiqxKOENOpi6n0HVzxTtzb6eP2CcHmc1WAYuffv9bjL5zieeee44f/ehHBINBKpUKTqeTtn4/46fm8LW1irWmYVEr1Nl2uGPF/s5NLpHZHsTTMKipIlabi/jQ21x0Fujp6cHvv31UBVoJ2LmrSezTRTwuBclnQ10skx4pcOVwke7gjYLFdRbQ/sGtXHp/nqzdR7jXR2Mkg5Gugiyi7o6wVG9yuDew3M0r1Jq8cHGR0SkdMbOEUW9Qly2ImDywtY2D3QFEUcAwbmgRKJLII4NRDvYEiBdqWBZEPDbC14rQuq7TbDbXtYKqNw3mqhKz703hdzsYjHlXXM9q6Ak5CW0JkouX8c8UaDablINO2mIKXQEHo1cvs3fvXizLYnFxcd19fRh09/jovqkwcDM0rbW2qap6Rwus+EKRXLyEv9NDOp3C6/URbWtjsZFkbjLL8fvvzCpcnC8wM50DCzq7ffT0rS0m9XHBXUWexWKRfD6//P9MJsPs7Oxt2+Xzeb7+9a/T3n4Pb82fIwiisFpB+TZ4HCqObh+1oSSKZiGskyebDZ3UZBZbyMn5773Dt/7VFA6nH7vNjtvjpq0twJuGxe7jXXRtDxN16ZQCdUamF7AMi9BADEEWMcsalVSVepuLI/vDTI4OMzAwsGplVjNMGtUmTlFG2tZJYjFO0O3GeayDpw93s3BxifnpHFrdACwMn5Mz0RyZRp22bs9tSZ6erqKpErYuL4PtK4O+HX1d6NWrGK4ywd5+looNLCx6nQo+zcQvS5ilBmxAmS2fz9+mqDl0Pk5+vkh0e3BNKqIoCQR7faQms4xcTbJ77w0PrIZuUpEFJBG8pSaiAHWPQinqxvKIeMs1vAuQUdO81+ekN1ZgoOujV5GTZRH5FuXTo0eP8uabb5JOp5EkiXQ6fVs381bouk6hnMEX9SHbFTybincfS6zVhboOQRaRYy6E8Sz9Xgd5j8pivorbruDI1WkulMCjUjJM9KZFxGNDkoKMJCpkG016DAlBM0EAOepiXtNJjSQJG+BuGFjFEmqzSN5qQhzspoW1NYApCnT6HVx591UW33fhcXnY37l/zfMsZWpU9RqCQ8exO4grpzIYcFIsN0iXmhRUsSVaYFoITRMadbpEGa3DRa3NQ/oaFT3kVtnhcyBVdcKDqwi/SBJLS0uEQqF7+t6bzSaKolDL1chN5lqCNdewkK8xk7ndr9CpSthlJ4v5KpOpMnuuiSs5I04KMwWK80U8HR62hZ1MNQ2Kdb0lu25XcA56MaIqk5ZI+a0FFMVGYP+9qQ3eLQzDQJIkbDYb27Ztu/MHaMUQDo+Iv8OP/1ZmxyY+dlBsCqaxMroxDRNZWV2kLeKxLRdn53tivPnmm+Tzefbs2cPnP/95xsbGmJmZ4diDPSxN5Vm8nER1KC2v2W4fxx7qA1qNhnpqjs8d6eHCYoVUqUG7U+Fwb5DT8VP8+Z//OX6/ny9+8Yvs2nW7+qNmmK0ilm4heVQESUR0KkgNnVK9ueq19nR3Ejk7TNz0UN3qxxO0YzUMBJdCyq3gEgX23ST3/9pIktGLCSJTBShpZLM5tkRClIwUbwgCVr7B2JkFFieyuLw29t3fw/H7e5EkEZ9DaSnZ3oKRkREGB9d2CK83DX5wYZGhjIw0ncaSJc5HCnxyb4w9twje3AyvXeGZEz38RJVYmsqRK+Tp39fL4S6VM+++w44dOxCE1mylad7Zp/ejxPj4OG63e0NdRLtDRlIkUok0kc4AdkcrjjEtC9W+frpTqWj88G8uM/7BIo1iAwQBm1Ohe2+UZ391711b0/x9wl0len/6p3+6bI8gCAJ/+Id/yB/+4R+uuq1lWfybf/Nv7vkEfx4QirkZb8zfcTtBhJ4tQcZSZZqLFeqJCnZRQrzJdNJqGtTTNfLJMpqmo6Sr1A2Fsl1hqZbAZijs6t9NySbx3pk5Lr2/wM6DHeSlEeqlC9h2HGOhomMuFBEtC00WMcN23F6VH/8frzE5MY3DI7HtQBvH79/K008/tnxsuyIR6/Jx1RUnWAZRiVCPeGjfFqJrf4yOPVH2ZGsYWouuKykSqZff4KU384ipBQYtC+XaIm/WdDRVojjg49ieGJ2+lQ+RJEmcO3eZhx66jy19rSTFMEy+/40hTr89g17TcQYdPPaF3RxZxyw+n8/j892+wI1eSCDIwooK/2qwuVSMhsHY5RuJnmlaXFoosKViYEgS2f1RqvU6Nqcdn8dGu89Bo1qiXKgSmheZswtcms8y0NW27rE+KtTrdT796U8zODjIiRMnVr3+m2EYBsPDw3zu1+5j7EAG1Saz7S7EELLZKj/5wTCJ6TwOj8qxRwbYf6jjzh/cxEeO9m4flwwTo2kirWFPojlk5A4PXsNia9TLvNfO1Fye7FgGy7QQLQu3TaaQXKDWDFEqN3HtbCcYVfG6VEyz0fJQEgCXSk0ziFcaKJYB5+fwuFR6IiFQPZSSNZxVg969bXQFHIw39jL09hBtu9pwh9aeQR7YHSUxkiZfLmCYIm0PdPPJ3zhILV5m8uwC+UyNZtNAlERsdpm0u8T4+6Ps7d2Hf2uMqiggWeDVDCg0iB3txN/nv+04bW1tvPvuGR544OSKn188v8jpn05imXD0kb47Du1fN0YuzhXRyhru9hvXNpOtIIriqn6FotiaS57JVtje5kFVRCRFanVXSxq5yRyTIxmU4530mFAtlQm0BVqefxbkCwWSosj778xw3N8JW9c9zY8U8Xgcn8+34W5eqVSiVCrx+eeOs/RYmVD47zYx3cRHjx2H2olfTVFMVXAHnVQLdRrFOoce60e6wxiIoig4nU46Ozv57Gc/iyRJ7Nixg1qtxsWLFzn5bDuZeDvZeIVwp4dDJ7pp7/BiWRbDw8P09/djt9vZ1RlCM0xUSUQUBbo+91ni8UVEUVyzW+xWZWLtHiadMvbFMqJLQS9r6J1ueoKrFyAEQWBvh4f67CJVsYOMU0ZwSWBBUJV4bDBKz7XOWbJYZ3SxiH++jNAwsNodqF4T2eHAG68wJy3xNy+OoZY1nEEn6bkCP3n+AlrD4JFPrP4Qm6ZJuVxeV/xwPFnmylyetukizJcQ7DKFbSZvO2S2Rd1rzuYDbIm46XxsK68MzXL6kk45VeVMtYlNMnHMLRAMBm/TOPi7QLFYRFGUdRPc64hE3ahBA+OqhRkW0MQmhcUyDq+dnftia37ONE2+//Uhrv5sGm/MQ/CaFkE5W2P07Tm+pxl86f92/K68jv8+4a4SvSeffBK3241lWfzTf/pP+eIXv8ihQ4dWbCMIAi6Xi8OHD3PkyJGP9GQ/rthzoIPzP52ikqvhuoPsrCKLuL0OHPtjoMrE5wrYDAtRELAs0EwL3AoVvYEsqajdPmJeO46oxKVLl5BkGa1SRPW5CW2LUCg3OHdmHrvT4n//X/+Y7p3bmEhWGJ/I0KjrLI1lKQynqcyX8Lr8OGQ7pUSZoZcS5KZh65Z9K4L+TxzooF5vMjOUBN1H7/YQT1xTphMl8TYT7Q61gl4bIz94lEWnA29NQLAsGmE3jh4fJ3a38eiO6G1Us1S2yKVzdey2LFv6WgIDly4kuPTmNM6gA3efg9RUjje+O8z2nVG8axh/Ly0trbpAlLK1VT28VoOoSpQK9eX/z+drzCyVCWQbyO0eoj4buUIRv8+77EvmUP1UlSq58RSesJ0L8zk+uYEB8Y8Cs7OzOBwOOjo67kiLMk2Tq1evsmPHDmRZXtG13AhqtSbf+W/nmLuQwO63k5sv8sO5IrIi3vW+NnHv2HOwnXdfHic7VyAycDvlxDQsCvEyPY/0sOdgJ8mhJO2agUOzSDtV7DEXEgJizeCdZJ2MkqJ9XzuR7R03qJKmSbFUxKbasNltOFQJh+okY4GuG3QiEfG7sHltaB4Hlm7RLkskx9Lkp0FzxqjInXz7a6d49BN7CYVvT/iOH+2iMp1nYUjGbpfY3+2jPFcg0Bfg+O4Iek3HaBoIooCkSvz0zTzlbJnZxgwdejt2zUSQBFwRF9E9USK7bqdnA4yNJJid0BkJJhnc1XpW4otFXvrri1RzrWf+lUSJUNS1qijT9e/jerXbuCZmdZ25YJkWpZqOYx1PULsqUq7p1HUD9abt9IbO/KUMqUaTYIcH2dTxhMMo6rV1SwC/34eeLRJP1Vi4kmX3wwbSOsHcRwnDMMjn83R13Vm5rlwuUygUlrf9MOp89brOT14cITGbZ8eBdh54dG1Bhk383eDEg71klsoMvzfP0kga1amw84E+HnryzhWHo0eP8sgjj3D+/PkVYw0Oh4Pjx4+zsLAAwgwPP7lnObm5nuQNDAwsjyOIooBdvHHPBwIBnnjiCQ4fPszw8DCZTOa2GEAUBR7YFSOdrpIaWkKqGRjdHrbe182ude5NRZYZcGocfXAL0+nqCsP0mztw2apGNVMjUm0ihRxkCllC4TCiKKKbNZpjeWrpKvuOdS0rTaZnC5x7Y5rjD/TiWCU2mZiYuKM/3lKxjlXSYLGCFHJiZKp4snWyFY18tUmbd/21IVVq8NY7k7inm9jreTRZJN/robg9ytDQ0IaSrY8SqVSKSCRCJpPZUJJ58eJFnv78Hs68kWLhSopyuoo75OTEJ7exbR2tgempHJPnFgl0eVfE6O6gA9kmMjuUvI3V9XHCXSV6J0+e5OTJVuWzUqnw2c9+lr179/6tnNjPE3r6AnTtijBxeh67W123i5SZK+AJOvi13zyEM+zk8tUkY6MZ6vUmiiIRDrswZ4u8cmkUR2+AbncUUWxVpqvVKpFIlFq1ysL8Ai5XnvaOduR+P5UpmL+gseuAwqG+IIf6grz8wjCTl5L4wk681xI009bP5cuXsQydWrbJN/9/b/MP/skjdHb5WvMs+To9ZZ1iukqtqlNO13jpaoadx7rYeaAd1y0zfk6Hnf/3H3+VbFVHjfQwn69jWRZtXjvb2zx0+OyrcujHRq6wc+8AHZ03ulHFfB1TM5bNjX0xN8WlCvlcbdVEr1KprKlyqdgkTH1jFATLtFBvUhBMlxs083WksoYUdVKrVYgEfbfNpDpdTsS2AKnxRSrhGMV6c5lr/7cFy7IwTZN4PL4sD70WTNPkypUry0ketBb60aUiS8UGTpvE1oiH/rBrTcXD6Yksi1fTRLYEl+c0Fi8nGTqz8LFdFD/O8PkcPPjsDn76tYskxrIEOz3Lf5dKvk5hvoiv08vjn9lNT4+f8PYwifMJli4u4VIklEoreUo0dUodHixbHSGkrhBYEkURr9dLo96gsJTFYalQN3BVNPJNi8VKDVnTWwmjKqKVNXKFOnOFGlVAaYuSGCswf0nn6nsJPv1bR9m7r5UA1PN14ufiZMeyRKoG7bvbEGURI11jem6aRfcigYEA7Qfbcd1kLVIsFnnk2Ud44IEHUAyFZqWJILWsC9Zbbw2rxsCWboI3FahSyTLVTI3o9jCiCItXUqSTlTUTvYWFhWVq9K3JpCAKKJJARVt7rTHMVuB56zNmNAwSs3kadomQKlMqVfE4bg9CwwEv02WdhakshWSBYMe9+edtBNeZEsVi8Y5WK5VKhVwud5siqGmamCYbrpK/9/YM778wgqiIJCdztHV674p5sImPHqoq85nn9jNxLMaf/6e/4rf+4HfWfE5uxfW5+a1btzI2NnbbeEVnZyexWIyhoSFmZ2d55plnGBsbW5HkrQbTNImGt3H2vUU83iiqTeP06dMcPnx4RULZH3bx3BPbGN7TRqmu0+6zsyPmXdO3M5vNIkkSdrudqKf1by3IooAgC1iigFFvtuwkrscHpoVW1ZBs4nKSB+Dw2agXGhQL9dsSPcuySKfTd6RIO20ypiq2OpRLFcCi6ZCwyxL2O7CXAM5PpzAnywRMBaXXh1lsYM6UuDSW5R8+vYfJsRGKxeId9/NRYXp6ml27dlGtVu+47aVLlwgGg3R1dbFzZz/zcwVqVY2OTi/OO3hyTo9l0CpNIv2337t2l42MVmBmIvuxjWk+tDrEv/pX/+qjPI+fezz9ud18M1sjMZwm2OfH4Vm5UBlNg+x8EcuyePBze5ZVHqPHenj02A1qYilR5i9ffwsp6qVeyjI80mD3zl2IksSWra0qmtfnxef3sxhfZGJ8nHA4gtjm4ur5OEce7SfcHyCbqXDh9SlsXvtykgcQjkTo7e3F7/dTyBeYvTLPd7/5Nr/3Pz3JWy+M8MFbM5TydQSPiuxUqJiQn8szNZrh9Mvj3P/MNvY/0IcgCkxMTNDe3s7+/fs5deoUJ7duTL2x2WySiC/y27/1mRVBhD9oR7RJFJMVXAEH+cUy7pCDQGB1CtDCwsKaC2P39hCLV1JYprWuXYDRNLEMk66bFgDLshBMC0wLrnVa1xIeUhwqYTnIdCpNJpsn7P7bpW9eDzrHx8c5duzYmttZlsWVK1cYHBxcfvmNLZV48dw82YkcaqWJoYic6fBwbE+Mx3dEl4fMb0ZTN7AME+kmsSFRkWhqd6f6tYmPDsfu60GWRU69Mk5mpoBlGGBZyE6VngMxHv/lHfReGzD3dHio5WqEtofwdnuxTIumCSOzWUJBiPpdt4sIWWDk6pCqIGVqlEoZBBFUpwM3AkVRoKGIuE0Bq2mhWxbzI2mMXWE6dkaWgxtDt0iOZ/jBX36A6/cF2rx+pn4yRWG+gKvNhTccIFNp0jRMZEkg1OGBmk7yUpJyokz/Y/34un3Mzs7ymc98BlmWmZ+fp6enZ1kddD3U63XCYQ979qx8ebfFPLgiTpZGUwgCuEMO2tYw/b5eWLlebZbtMqIiojf0ZTGW7oCTc3M5LOfqc0vFWpPuoHM5wLSuGYuJsoihWwiiSKNex25fI7AUwGZXcSg2rl65yn3t9/2t+1wWi8UWVfUOQV+1WiWTySzbLEErED/99izn35qhUmgQ7fNz8tH+dSvu0OromaaFO+ikkqlSr/7tCdBs4u4wdOkUpdo0dsfq823rIRqNMjk5ia7rt601kiSxb98+fvKTn/DGG2/wB3/wB+smeZqm892vXWTsvYVrptgS7YMhPvmFHbz77rvs3bt3xShDm9dO2xpsoJX71bh69Sr33XcfV65cIZFIEIutHfR3+B2EYx7K/gLNqwsE+qKYDQMjW8NyK1huN/aZ4grvuFq2hivoxLfKLNjc3NyKZ2gtbIu6eT/qJtUw8RUa6IpIIerg/g7vqjN/t2J6IY5HsiHaW8JQoktByddp1HQ0w2L37t385Cc/YXZ2dkPncy/QNA1JkojH43dkDVy5cgWv17tiu1Cbmw9ms/zk7WkAdvcHOdwbWDWRN0yL9VyjBKHlh/1xxYYTvX/9r/81giDwJ3/yJ4iiuDyrtx4EQeBf/st/eU8n+POCSNTN53/nMC9+Y4jF0TS52QKqSwFRRK81sXQLT8zNyWe2cfKBvjX3M3IxQTZbw+UWEfC1ujar3KF2h52B/gFyuSxLS0kEMU+15uTqB4s82B/g8oUlKpkabTtWvlxVVWX7tfZ8OBxmvD7N5bdn+NP416lnnAhhJ9FdkVuqzy60pkE6XuLlrw9hNE0GDgWZnJzkiSeeAFodx2QyuSGFteHhYQYHbzfl3r0vxvTjWxh6Y5pKpoYr5ODRz+66zesKWouEqqprBjx7D3Zy4bVpiqnKun6F+XgJd9TF7gM3hIVssoRlk8EmUcoWcIfXnoEzazqGx0ZHRxvppUUcNP5WFsiGbpAo1Lkyk6Kv146o2NZMPq8nedu3b182Pi03dF66FKd8NkF0qYogCVi6RTNd57Ru0RlwrDrM3d7pwxtzkRzPEOrxUa9oWLpBz9Z7E7fYxL3h0LEu9hyIMTqc5jvf+gGPPvY4nT0++voDt90XlVQFxXXDOmEyVaakG3THIogilIpFlGty41bDQJsr0oyXwbSQPAounx/TMKg3Ws+cZBMp2RV6O1qJ4/B7czQTZXy9XqxCA4KtwEqSBaJbQySGU1x9P8FsagxVU/EPBJjMVZkcKVCoNVtMAgE8NpnekIvt/X4q80WmXp0ifCKMJ+pp+V9yZzXom5FIJFbterfFPDz7lYOcfmMSy7A48nA/nV2rP+PxeHyF6Jgr6sLT7qGSrCybnncFHExlKiSLdSIeO9e/fsuCfFVDEQX6b/Ljq+fr2Hw2/P1+nC4F0iVqDRO/f3VKmWFaoBkE2gJEOyJMTEzQ09Oz/J2sBcuy7joh1Os6lUyFeqrOor5IMLp29/C6l+6t/rlvvTbF69+8hCgJqC6FyffmSU7m+PzvH73NxuZm7DvUwfiFOIWlCgNHOti2Y1Ot8+8a5YZOrqIhigJRjw1FEkmn05w/fx5Jknj11Vf59V//9bve765du7hy5cr/n73/DJIsP8870d+xmXnSu6os77uqvZvpmenxGAADDxIERAI0S/BS0uWVtIK4uqHl3QgGdBWK0CeFxF3GSlcUtRRXoAFoYMgBBhiO756Z9r67vDfpfebx90N2V3d1me7pGYAw83yrysyTJ4/5n9c87/Nw4MABAGq6xUqpgSSKNLOLFAoF2tvbOXv2LJlMhgMHDmxZ+Dh1coFrr88RbA+QHIxi1E0WLq7yVsLPL/zqcc6fP7/uy/ZucObMGY4ePYogCAwNDXH69OkdEz1NlXm4P8oL2Sr1fI6I7mDXG5iaTKE7wN7uMHVxgeVraXxhL2bDRBAFjjwzgHcL0ZClpaV1Nt1OSAQ8fPpAJ991DEpJHz6PzNOdYR4fvnfnu1qt0hcPkE3qmPOVlpl93aSqKQx2BgneTJAGBgYQRXF95OOHVVS6RVW9ceMGw8PbU4GvX7+Opml3FZNcXri0wvkfTOFbrQHw/bYVVj80wOce6tlUtE62BxBkEaNpono3JsS2aSMgkLgPX+sfV9x3ovfVr34VQRD4V//qX6GqKl/96lfv+ZkPEr2NaE8F+fV/9iiT41munF3h2oUZgn6NUCLOrgMp9h/qILADtc82bMbPLGFpMmN9rU5VvpDfvsIlQDQWIxgKsbqyynKhyGsvnOXAU93k0hUQW8HWdhAlif7RXmbemGHmbJH2Q2E6trnYVUWioyfC6lKZl795jYk5k5/70sfXF4He3l7Onj17XzNjExMTfOYzn9m8P6LIpz+/j/1HO6lVDdraAxtMWe/E/Pz8jqaaPX0R9j7Ry7kXJhFlcZ0OeicKKxXMpsXxT49uUFzqiWmEkxqNsIo9U0Zs25qq4toO6DbVQS9dIXj84cNMT09z5coV9u7du6kqaJs2jVwDx3IQZRFf3HdPsZiGYXN+Ls+58yukp7LUClUUfwZfUiV8fY1D/TGCdyxct5K84eHhDYHgTLblixbPNpHb/YheuRU0z5URFstcWSptmeglk36e/+IBvv/nVyiuVpAUiYMfGebxZz6Yn/n7hqrKSEoBQV0lFKswOLT5nLiuS22lhnoHtWWx0ECVpfWExOv10Ww0US0RfTKPXWwihb0InjusCiQJTfNh6Dqi41ButILCoE+hCYiaglPUaVzNoPaFUbtC6+uPKIusXcwzFFBphJtMLRSYytbxqlJL9VMUcBwoN00uLBQp1U0e7o9SGM+in9F55EuPrO9HKBSiXC7vKFgAbNk9uBNje9sY28aL8E7cKijdPg4iyT1Jin9bXGcLBH0KD/fFODNfYLVcRxREBEHAsm0CHoWDvRE6IreD1nq2TtdDXQTaAwwf6uD81RX0wPYKlcW6SUB3GD6YwhtU6OnpYXZ2lng8TjB4uxNpmzblhTK5iRz1TB3XdVH9KvFdccJ9YTzB7Z89zVKT3I0c2WtZVuZWCAVCzOqzDB4ZJDGaINwX3hDwNZvNdbuJO6HrFudenUVWxXUz7HBbgOWrac6dXNgx0Ut1BPmN336CSrlJLKbdU2H2A7x/aJo2J6dzXFwoUC7qSLJIKqlxrD/OcDzAl7/8ZS5cuMDevXsfaPuhUAjDMGg2mzRskb/4wQRLF9bAIyFEKjz73Id59NjDBAIBdF3n4sWLhMPhTXTPiYurCJJI4OYIiaopBNr8zF1Zo9GwOHLkCDMzM5w5c4bDhw+vF70s28Fy3C3pjVeuXGFwcHA9sfR6vViWdc815KG+GLOzs8w/u4tc2cI1HXxRHwc7Q3xorI3K3g7efm2GpYk8gb4wBx7t5egjmztXq6urtLffPxOoIyiz/Nqf8+nP/yJ7xwbR7sPaC2Bubo4PHRmlYM4yLawg5hs4cQ+RA+08sSe1rqMQCARQVZVoNMqFCxfYt2/f+2oddQulUondu3fv+J7x8XFUVd20ziwVG1w/u0x0sYI36gVBwLtU5ca5FRaGEgwkNq6nu/e1c3IgwtpEnvZd8fW4q8U6yRPpCbHv4E+ui8B9n527JVV/1BKrPy0QRZFdY22MjCaZz7zC0Ycf5vjxR+/rs1bTolrRUbTbMzMejwe9qePxbvOQdkE0oTPSBk2F0kqGr3/t69jCvf1EALBdpKpDqLedjs57cO8FaOsMcf2tCQb27dpQcbu1ENyS5N4O09PT9Pb27rhw7BQM3PoO2J5OeQsf++xuXMfl8mtzlFeq+KJeJFnEMmyapSbesJfjn93Nkx/aGCCHfQp7O8N8179MfziAlakjJXwbAh3XdrAWK9gJDaE9wGC0RTMaHBwkm83yzW9+kwsXLvCbv/mbJMIJcuM5Js4ss7pQwjIdZEWiozfE8NFO4iPxDYH4LVSaJt96c5bxl2bwFJrIjTp9sTBO1qS+3OT7c+eZeqqPn3tmkIjW+vy1a9cYGhraVByo6RY0TETHQbxZURSEFnXDUzPJ11r7n640KdRMXFyCXoWOkJc9+1L09kfJZmp4vcqWZtIf4EcP13V58803cRyHc+fOcfDgZjsDx3QwGybSHTOodcNGuaPiqagKjZUy7pKBXTORkv4tKYgAqseDorqsFqpUaypBn8KtN0shT6tqOl0EB9TeUGsds1z05SrBJ/pICzC+kCYZ9m0IukQRIpqCX5WYzdeI+hUCio5W0WjkG/huBnbRaJS5ubn1RM9smNh6a+5Q0RTEm8nB8vLyfYmI7ITtGAqRvghaQqO6WiXY2boXkiEPz4wm+f6bp0l09SNKMlG/SmfYt26WDq2ESvbIRIdba237WIKOuMpcxaLoMQn7lPVj77otry4zX2esO0zH7iRFuwhAf38/q6urNJtNkskkk2cmaU40qaxUEAQBJaAgCAKVcoX8dB5f1EfqcIqOQx2bqOy1dI2pH0xRWWoFTb52H9G2KPqCTvZaltx4jp7jPXQc6UAQBAzDYHl5ectCW7msUy838d1ROGudG5XcWnXDex3bQRCFDeuq1yvj9f7kVtZ/EmHaDi9cXuXcxRVCi1UiFQNXgJWEj29m6nzyaDejbW0MDQ3dUyxkJ+zbt4/Lly9TUruYf2WWhOViWw7llJ+uzxxtKf3Sinkefvhh1tbWePPNN9mzZw/RaOt+2bqfv9HeamBggFgsxokTJ9h34CDXMjrnbmTQmxZ9PREe25Wg46YK+OrqKoIgbEq0+vv7mZub2/H3CgL0+kw+9fhBlgoNLMclqqm0hzwIgkCkT6XnV7f2jrsTs7OzPPLII/d8H7Rin2984xsIApQzK2gHNltLbIV6vY7X62Xi2mVOfv3/4te/8v+h0HAIeGVG24O03UFv9fv96zO3t87Z8PDw+vl5rzAsh7OTiyyaGi+en8GnBHAcd5Ng3+TkJKIobrnOlJomRs0gJIpIN62i1KKOXTe3tM/weGQ++cWDfPO/n2dtPIcgiQgiOIZNpCfMx7+4f0vm2E8K3v80/ANsgOO4nBrPMD9boG8gykMjSURR4Pz589RqNa5fv86jjz56X+byruPiOGzw1/P7/eRzuQ2JXqNWx+vxYBd1rEwdu9jEtV3UhoGme4gXIlxcnSS3JhHpC6L5t59nKc6WMHWLtp77U0krFfN4En6Wp6rU8o0N4iy9vb3Mz8/vSJu4cuUKzz///H1913aYm5vbRBfaCqoq8+kv7GPPoQ7OvTXH4vU8ju3gC6rsf7KfA0c7tzXLPNoX5Wyvn4LHR2KhjDlfRvTJIIu4TQvXcrATGvmhMA8Pxxn2VGnenLPxeDxcuXKFarXK97/1fXZpBzl/Zom842JoEqIi4TRNJs/XuHJxlcMPdXPgk7vWg1loBfHfO7PEje9NEq/beHrClGqgxlqBpce00ZaqzLw0zd94JX7x6SEmxm/Q39+/JeUl4JERfAqOKOA0LESf3LreaiZ6LEhYgL86u8jkRI5argaOgBpW6RtJcLQ/ylgqtGM3+gP88NFomOi6td59FgSB3/iN32B6enpbmtE61VG4/bftuBsSOdewkVZ0GoU6vo4wggDZbI5QMIC6BZtAEAQ8qgdBFCgUCgQiKpU5B9d2kUIKCGDMFRF9MkLMh1NqEkz68US9zE7l8HqUbYUDFFkkoCpcnkvzqcN91GfLFGYLG+4NvaCzlFmiOFNEL+nr6pyyR8bf5icyEMEUzftac3dCvV7flOhZuoWlWyTGEsy/OY+Yua1EvLa0SGNtju7BTnp6N1fojapBbbVG7xO96wmi5bV4/DO74VvjTE2nqSaCKNpN8+S6ga9uMZYM8NindhHsDFJcKK5vL5VKUSwW+cYffIPFVxd55OgjhPvC6A7k6zqOA8G4j5g3SCPfYPaVWWzdpvvR7vXkyqgZzPzdDLXVGrHhGNPpIqtll4laDh8ue/pCCBWD+Tfm8QQ9BPuDLC4ubsumCIc9+KM+KulayyKC1jNSrzSJpfxYTYvibJGly2uUMnUkWaRrLEF8OEYgFfih0cQ+wPaYyda4NJElMVVCKumIMS9YDuHZEhXL4UTES4cWXx8DeFB4vV5UVSWbLSIZDkrch1SzcGoG+hbzUe3t7SSTSa5evcrs7Cz79+9nZF878+dWqRWb+CNejLpJNV1j37P9+O8olobDYR599FH+6wsnWZxw0BZqyA5cDKyw8kQvv/z0IB7BZmZmZkvKZE9PD6+99tqOid7MzAz9/f1oqsxI+4MVPwuFAuFw+L6v+2azSSwWQ9M0MpnMfX/P1NQUb731FnNzc3QkYzw6vH0HUdO0liIqrQL+oUOHuH79OtFoFMuysG37gcdT1spNXri8yqmr0ySSSaxMCc2jkBOW+eie9vXu5PT0NI7jbOro3kLYp6CGPJiOg5hvIIgChuPgqBDaxk+vtz/K//SVx7h4Zpm5yRyO49I7FGP/4Q5i8Z9sz88HTvRmZma4fPkyn/70p7d8/dvf/jb79++/p/LfTzsuzxf43h+fx12rcb3dj+8fPcT+vhher5fdu3cTjUap1WobKDbbQVREPKqEXds4hO7X/NRrdTS/ht5ocuL7r9Fhh0n6o/g0Ddcn43gEDNvGI4okvAmOxny8fnGCiRM3SO1P0dbWhnBX4OPYLtXVClLIQyxx7wu90WjQaDTo6GunMlsit1bZkOilUilOnjy5ZaL3xhtvAK2K/LaiA/cB13WxLGvDQ8d1XKprVaqrVRzLQVIkAh0B/G1+RFEkEoNv/+A/8r/+zu8wODCCxyPdMwis5df40hNjfPv8ApmQBzlbx1/QEWwXJ65RiamI7QGODSV4bncbhaxAtVpdT/Q+9alPMXF1gnf+/BKZtjCVmI9Y0Ivvjs5Kw7BZKTdovLWAIAgc/txulJsKikvFBjfOLhGpWHgHwtQbdbQ7FEYFRcLTGyI6U2TqzAqv+Bs8tm8ITdtauKbHr5KI+sgFFKKZGsLN+N8Iq2QjEgvnpkimHWINl4TjIgAmMH0jz9xYnOeO9/HI4AdzeX9fKBYb/Mn/+Ta1os7zX9zP/kMtH0NRFPF4PNtWW0VZRBAF3Jumx4Ig4JEFKvrtwMpYLOOWTYio6+Kbruswv7BIIBggEY9v6MC7bqtL4/V6iWgKzWaeNRmq+TrhiAfRp+AaDs3ZApW1KsGIl47uMDXTIV8ztn0Q34KCScWVKOsOXo9MM9+yQTBqBitnVsiezzKTnyGWiqFqKkpAAbfV3bv65lWUEwqpoRRZssRH4juKMW2HQqGw3kUAqOfqFKYKZK9lMaoGLi0fvPJCGS2pEegPcPXqVRzXoVQu0cNtBUrHdqhn6+glna6Hu+h8qHM9sMvlcizoC8w2T/HwweM0ijLFcuv3RkIeBo52M3ysm+gWVhoAq3OrTP5gEr2ik6fE4prIbLZO3bBwAVUSSYW87O8O41cllt5eQktoxEda93Jxtkh5sUxkKMLl1QrnpjJ4NB+SYGNYNkUzy2NDccSawdK5JXz4GNm1WQDLsR0qyxUqyxUGoxrnLqVZKemoSY1msUG0O0QqKfDyf32DlTmdtaaJLouIDoQvr9GbCnLkI4N0Hu38INn7EWMqXcXN1BALTeTe0O37RRYJ5ptkVivMplTavTvPhN4P9uzZw+Xvn0TqC5GdLeGIApGHOuiMbB0TiKLIvn37qFarnDp1io6eDkaf7GXy9BKlpRKiLNF9oI1nPzm26bMNy6XWDBGcWyQQUBE1Be9KlbWzK9wYTWIuX9u2kyZJEh6Ph3q9vu0zdW1t7b7m6u5EvmYwlalSaZj4PDLF+Rt89Mn76+YB69ZmDz/8MLHY/anv1ut1fD4fH/7wh/nP//k/7zh7CK1jfvcs9NjYGNevX+frX/86o6Oj64meaTtIgrCpG7flfhgW37m4wnyuQpvj0FO2EEQB3S9zdq6AJAh8+mAns7OzGIbB2Njmc3oLXWEfew93cLbQQF1qMQX0niC7j3RQXJpmsRnbktERCnl54tnBnzrrlgdO9P7lv/yXlMvlbRO93//93ycSifCnf/qnD7xzPw0oFBpY+QZJTSVTaFIs6tAHu3fvJhAIbJKc3gmKppDqjzL31hyOE1yfofH4vOTzeTRNQ6w7xMoeiuUs1aSBjziKG8ByXKxiEzOmMe+T6elMsTurM3F2idy1NcrlMqmOjvWE85Yini/mQ7fcbalat2BbNpl0hu6ebkzHxXVdTHNzFc7r9a53tu7E2bNnOXXqFGNjY+zateuei812WFhY2HBMq2tVlt5eIjdTIFdoYLsusiiQiGnEBqNE9kb4g6/9AQsLCzTq9S39a+6G67pUq1Uuv/UWl157k//X//qvubhYZK2i4zogSwJH2wLs7gjRH29ZE/j9fvL5PIlEAlVVOXDgAO1CO7leH0uaREfUz925pU+V6IgFWKbK+dNL9B9K0bav1UG4vlDCnC4RjXoRJBHdMPDfFcwLooAn7qN6Y5XCsfYtrSaapSbpy2ky17N0rpSpFnSylo0Y8eCk/AipAKJh0TMDymqJRlhC60wiyTKSbiNn6tQvpHlZEmkLeTdx3z/AjwaZtSqZ2SLNos7yYnk90bsXRElES2gU54rr/0sGPKQrZfCrOGUdc6WGGFTxeeTWvevz4vV4qAgCAiBLGx8jDdNGlcT1okVbIkxjl8FSrU52toDiVXBdcLJ1/LsTPPr8KOJKBctxsF0XaYcii2mYiIAkS63OoyRgGza1TI2Zv5uhNF8i0BHACTiEkhtZCGpAZeHcAqViiVFrFL2kU1mp0Pt47z1nYe9GqVRaL2LmJ/PMvjpLI99oURuTLRq3GlTJXsuSn8hTz9Q53H+Yil3Bq7XEFxzLoZFvYOs2vpiPgQ8NkDqYWg+ki8UipVKJCxcuoLarHP+VI6imilE11n9PoD2wY6IackIcGD5AM6BzdjaHFZAIemVSYR+C0ComzRVq1E2bx4cSCFKTzJUMsaFWkJi5mkHWZApNi/HVMn6PTPImrc1xXJaLDSbTVcbiPuYuzfHU8ac27UM9V2futTlK8yVs0yYqwkgqyNpyGWOlytjT/TzybD/F82ucuV5lXrQIBVViodZzK980KWSrNL99g2c0leSezeqcjt3qGN8qXHyA9w9100a2WzHAncdW9Mo4JQNHt8lXmyS891a6vRdkWeboYBsdHTJLayk8HplzJ77FWy8v85GPfASfb+vvCAQCPPbYY8zOztI7qrPnyBGqJZNAyMPo7jY8Wygt1g2bZtVAs1ykaMvmSfIrSHWTi9fH+fxje3fsUg4PDzM5ObkuIHMn7hZpuh+cnS/w6vU0xcUSYsPCUSXcoIw2meOJkbsF8LZHLpd7VyIpMzMz7N69m9OnT/O7v/u7pNPpd7Xft3D+/HkEQeD06dMcfOQJJoswNV9EVSQOjsQ52hfb0Ud4Ml1loVDHnyvgmTSoN5ZAEFA6g6QOt3NjtUKXOoXX1dmzZ2dKqigKPL+vg46IxpW5PK4De/qjHOqJ4FUkcrkcr79zjpocwpZ9tIW8jKaChLzvrSv944oHTvROnjzJV77ylW1ff+655/gP/+E/POjmf2qQSih4BoJklutE9iYZ7N1eofFeEASBfY90c+nUIuW6TuQOqlwwEKS8mkNe1In4QxRdCx0PkiCjAJLgogCB7iAT2SpzhTpjhzoYcFyWxrOUbZupzAxawE/QG8QxIdoTZnhXjDMvz27JkV6H21rY2tvbW4aghoUoCqhbVOaHhoa4ceUG3aHuVsDiguSRaBQbyLJMR0fHBvnj+0G2qnN2IkO1ZiJR5lPdrUSvulpl8nuTTE0XWBIcio6L47pIrkAkV6UnUyPzvVe5ONsa7F5YWOChhx665/ddvnyZv/zLvySbzXJgz24OdEfY1xmmaljYtosqi5skfP1+P4uLi+t/O5bD1JllsrZDNLg5ybsFUYRYyEe2WmHy9DKJsURLvGKtgmLYiEkN27I2Bdu3ULHrBAWZYnGz3UEj32Dye5OMX02z6jhUBLADCsGGQMBw6eyM4OyKcualaSIVE3V3J64AhXwBBIhFoyhdAfwLFbLjOS7vaduQ6NmGTXW1iqVbiFJLXMYbfvBu7QfYHj6/yZ6nu7GaAkeObaxU3uuB72/3k72eXf+7K6YxkamhmzZk62A6iJGba40AruPg0/wk2wQUWcFybHQLarqJbrs0DYuemLZe9bWaFp19EXYfGGVifJXsUhmf30dsXzsdnSF6ukIsLVVQJBFFFG/aKWxOvFzHpdFs4PMHkCwDVRJxLAdLt5h6cYraWo3YSAxREqllazdndUUM20EUWt0rwzDQAhrtw+0E/AFWTq+AC/1P9993glCtVtc7pMW5ItMvTeM6LomxBA3DZq2qY7utmcKe4z3Us3Xyk/kW26Gzo5XgZRoIskCoM0RyT5Jwb3i9W38LxWKRI0eO4PP5GB8fJxQO3ZMeJwgCjuMgiiK2aZO7nqOtpw09pHKtmSHmkTewBnyqRIessVysM5+vMdTmp7RQorpaxRv1opd0PCEPq1WDcq3BYPvttVkUBQJehYV8nYRYJx6NY9Y2zr80S02mX5ymvFwm1BOi5raSy5GeCHsth/JCmUR3GL/pcvL8Chm/zGA8DI5NvV5FVT20h7wUJYnpXJ2edxaJjcSQlJYPanmpTHY8S2a2hG05eL0yqd1JooNRtKT2QffvfUAioGJ4JZAEHN1GvCnCZOUbZB2LyZpOY6rMpTWdfRWZw72R9Rm3B8GuXbvInTzJrzx/HAAp38fLL7/M5cuX+eIXv7jj6Ed/fz9dXV1cvHiRSMLH7t39214DYZ9CKKFR8YgoK1VETcGqmVSTEh3RwIaO/VZoa2vj4sWLW742Nzd333N10FI6fvHCMsLlDMlMA8F2QBDQ4z5eEVcI+hSO9N6fP+G7UdNtNBp4PB5WVlaIx+NomvbALLxf+qVfolAocOr8Jf701RvYUzaBkkFNgO9eS1P96DAf2bt98rtUaCAKAs25ApquovZHbmodVPEPR5k2asx5FX7+6aM77kfdsJjP17Edl9FUkGNbaDroksaFjJf5E9dxiw3UjggDx/v4/KN963oGP0144ESvUCjsSDcMBALkcrkH3fxPNHRdx+PxUCwW8ToN/uFvHSdT1mkLeUnuoGx2P+gYjtE7EOXqVI7gsLpe5VFUhdJaDaHq4MYCeCQJn+Yj7G8FXI2ijj/kJd4dQvTIVBomV1bLHHyki2TCT8GyWXEN1tYyFMwsR58c5uOfOYZTtbj+zhKlYpNobOvFO5PNEAwF1+cEK+kaqfYAbZ0brw/btKlOVBn/6wkWlQJVwwK3FYBVztR5/PDjfPFzX9y2arcVSg2Tv/j+BEuvzyEbDk6Hn7b2LI8MxJl7fY7J6QLXXBtJEEgEPMiSgGk7FOsmVwWHPm8nhxOHafY010VcdoLruni9Xo4cOcIrr7yynpSKorBjNUiSpA3bbxabrCyUMDSZhLpzN0FTJApekdXFEs1SE+0uhdBKpUI4HNn0uXKpjM+nYSt17h5Td12XhbcWuHZ5jRuii6iIBDwypu1QlgR8okR4scI110Fba7SENCQRAYgn4liWRTabQ1EVQjENX6HOuQvzPLUrSUASyVzNMHNmmcW5IoZpIwKJ9gBDB1KkDrSjJbamu3yAd4+lpSWCwQCf/9K9ixRbIdAeQJRve7/FNZW2kIflTI1wpoHov/2Y8Hq9NOoNfJqPYDCIZbssFyoYTqtS4QKm7VKsm1xaLDHUFkCpGET6IyTaAyTah3GdlvmvIivYGfu2ubnUkmxfKjY2JCO3UKlUCAWD5OoGUb9C1KeQb5hUlitYDYvYSGw9WRO9Ad4ZX6ZoyRhWK2iK+BQacoAPHz9KJNRK1ILdQVbPrRJIBUju3tnHbXYmz6svjFNv1Pj8rz2K67gsn1nGalpEB6NMZ6pcWS5T0y1cF7yKRH9CY39XhNhIjMxqhpFPjKBoCq7rIikSakDdMsEslUrrxaddu3bds4J9C36/n3q9TiAQwKgYNAtNfHEf84U6huXgC24+rqLYMlqey9UZbQ9iGzaNQqs76d6xbrius4nej+tQrdVJ7RuhNFXYtO305TSlxRKhwQgXlsrr++FVJEbagowORMjeyJKdKbBSMwgkWwwIRJngTSXGSrmM3+tlzStx6ewMY88O4I14mX55hvGLa6wUG5QFcAQByXVIXlmjqz3I2GM9dD3S9a67tR/gNizLImBX0LqC1LINtNUqgtwqsKw1DK4HZKILFTqW6zgembfnqkztivPZI930xrZf4w3DQpbFLUckBEGgt7eX2dlZ+vv7GRkZ4cyZMySTyftKQhRF4ejRo2SzWU6cOMHo6CiJxGZ7Aa8icfxgJ9/NNchezSHXDGptKuEhhQ8d3Vnt8Rbi8Ti5XI54vEV1XllZQVVVIpHIuyoyXFwqoc+XSKzUkG6pXps27lIV1SdxrrPA/q4wiiRS1S0uLZWYXimj+RT2dIbZ1d6aX72XEujdmJmZYXh4mNOnT3P8+PH7/tx2iEajtA0fwD19kWSuhtLux2layDNlLlxa4+GB+JaJVNO0MSyHer1OXPNBcWMsVi6XaQoWQ0Pb0zUBZrM1/vbUApmJHK4L4Z4Qzx3r4UB3ZINC8tuTWbJvLtBTdpGCIUpTOa4Uq/S3+/nw3vtjw/wk4YETvd7eXt58801+67d+a8vXX3/99fesavaTim9/+9sEAgH27du3LgrSFnx/OhmyR+aJT46S+7/OsjJXINUbRZZaAhrehkBDsLBEL6Gwiqa2JPKb5SY+hFY36GanKehTsB0YX6vy1O4knTWTL3x+D2rEw5Wrlzl79iyvvlrgicefYGA0ycXzy0Sivk0Uzlq1im3ZhJOthMeyXdyyztiHh/Dc0XG0TZvZl2e5dmKByRw0AjVEVWkFOY5FaM+jBG2Zye9OMfzRoQ3iCjthsVBn5fwqSQvkhEZhscq5i6uMKTL5+SJzgoMiicTuGMRWJJFk0EOmopP1eVGbXn7pC79E3757C7gsLS3R3t7O3Nwc//pf/2uKxeJ97efdcCwHy3I2COtsCwFEWcKyHJybQ+ltCT8Troudb+J6Npu+l0tlPF4PcsPFkEXa7xoGr2fqZCbzLAgOqtJSAAQQGibl1TmqgRhhw6La0FEsGzG4cXGWZZlkWxK9qZMp5inOZFhSs+Sf28Xy+SzvvDLDimHT0EREWcRxXOSFIjem8xycyHHo06PrghMf4N1jamoKSWrNksZisR0Vz+7lLRfsChLsuu39JogCI8kgmcUytbJOoGPjtiVJbKnnihLZmo5uC3hVCYFWNTUR8JAMeig3TcaXygx4FHq6bp9rQRRItiVpNposlhcJ6SECqQCNbIOBpJ+VUpNywyR0B426Vquh+TUaloNhOQwmAxjlJo7poJd0YrtuJ3lzuToXFopkijUS4RAeRcJxXdIVHSs6wPnlOg+pXgJeGdWvovgVlt5ZItwTRg1sX819+dvXmTm9jAucfHmaZ473UV4sE+wMslpqcm6+iCgKtId8iALUDItrK2W8ssRYRxBrwqKerZM6dG9aeqFQIJFI4PV631XgdksRLxAI4FhOS7lSEtBtZ0fqlyq1Op+W6yIg4FgOgiBg6zal+RJySEJTVWq6tc5WcGyHtUKFY8MpXNNGkIUNx8+oGeRu5NASGrOFBuPpCjG/h5hfoto0ubJcYmHyOu2uAgUVPaQSUjcuiKqqoqoqjUaDSr3K+NQ8l9++TMCNcOrtBZY9AmJAIehTEIWWYt9s02QlU6Hwwg0etV16nuhBlN6b8M7PKq5fv863vv51tL592Pt2k4tU8FUMiqbN9YZIR8FgoGHgiflwSzqefIOcZfNaQOVLx/o2sYBuXE/z1t9Nk5krEYh5Ofh4H48c792U8HV3d3PixAl6e3sZGhriN37jNxAEgTcvjpMnQL5m0B3xcbAnum3xPJFIEI/HuX79OrOzsxw4cGCTt+ThngiBj49y9WCJSk3nxPe/xT//+G/ctyXByMgIly9fXk/0vvnNbzI1NcUnP/lJRkZGdlQYvwXDcljI1wkUdPBIt1WvFQkp7EHLN8kUmxTqBiGvwl+fXmD8tVk82Sa2JHB1NM5HPjTIsYE4a2tr923F0Gw2URSFy5cvb0k/fVBUdQtqBqJXRtQURE3BU9EplXUqurUh0ZvL1Ti/UGQ2V2Ot3OTKUp4nepKI5RzGXAlcsCIiOdGkr6NjR3P7pmnzvTOLZF6ZI3aTWVCZL/N92yX5nMwf/f/+D/r6+hgZ3c3svIS/bqKkAog+mYgq4VSanLkyS59aY2ho6D2Ldf044YF/yRe/+EX+5E/+hN/7vd/bYLVg2zb/8T/+R/7sz/6ML33pS+/LTv44o1pocOqVGV7/23Gun1umUqwwOTnJ2bNnWV1d3fZzlZrO1Wt5dOPeXSTbdjj99jwvfXecxYUS3Xvb+PgvHaDD7yE9kSWTq6Pn69B00BUB3bLxyBLNpkEzV8fnCrTtS+Lr3BiwhXwK1aZFzrYxagaFmQKK2lJR+sIXvoDf7+eb3/omQrCA3yOxtlze0BgyTZNcLr++sDiOy9pUjnibxt4jG1v0y6eWuXJinquWhR4NITo6nVGNjoiPtrAPJ+jhugQXzy8z88oMtnnv4wKt6p8gCji2i2s6uIAkCVQWK2TLOhXLIbLN3F1UU1lrNmgaEnLj/hb2ZrPJlStXOHLkCIFA4IGLGaIioioSruVwL49n1wXXclBVEUmRmL+aZvH70xRmC8y/Mo0+38C943hVymU8Hg+qoqLnGgg9IfYObRRKaRQa5ItNqoJA+I7jI4gCzWadzOoK3z31NvpKEdu96Qm4BTxeD9FghGqjRq1a4a//97/g5MvTzEgOStJHZzxAR0SjK+Yn0qaRCcqcvrLG5RcmMO4SFfoAO8N13fWk7cqVK3zta19D1/Udk7x8rkY63dhxu6Ik0ravDathsbpQJJ+t0xHxsivsw3Ecqrq5oR+sejwYukHTsqk2LbyKDE4ryQt6FeIBD4LQokYZuToVj7hl4cbr89LR10FuIYcVttDLOm0+hX1dIXTLYbXUpG7Y1Bo6hu2SrZqUVqv0NWx88xUWTixQWb7pB3qzA7hcbHBmLo/tunRFfAQ9Aj5Vwu+RaQt5aA97WS41ODWbb1FTgUAqQC1Tozhb3PE4SbLETe92JFlsiTsZDrJXZj5fw3QcYn61RcMWwO+R8XsUZnI1KtU6gViA3MS9WS63fAC3s2/YCaqqYhit+0qUbxZZLAdNkbB3sETSLRuvIiELAi4uelHn+jevU5orsXpulcKpRXqLFpVSk5VSg3SpyeRKjsFUlOG2VpEg1BXaUBTQyzp6RccT9jCfq+NVJHyqhCC0Co0OLovFGpOLk1y7eAWr2pKg3wo+n49arYprW7zyF6/wzokZFj0CsbCP9rAXTZXwKhIhn0JXVEMJepiwHM6/On3P8/oBWnAch6mJLCden+XC2WWaTYu3336bWq2GvniNT+yL8fCT/YQf60bfFaMz4GUYkeBgBDnuQ+kIIPoUomsN5lerLJU2rjvTUzm+9QdnmD2zjOM4ZGZK/OD/vsDJ1+e23J/R0VHGx8fRNI2hoSEsf5IXJ0qcu7DK2rUsr59f5htnF8lW9W1/kyAI7N69mz179nD27Fmmp6c3vb6rPcjPHe5mzJMnrK/xx3/4XyiXy/d1zAKBAPV6fT0GliQJQRBYWlq6Z4Ht9j609sMVhE3+EK7r4goCggiiIDCRrjL19iLxxRoxn0LcAfFKhrcurFA3rHUmwP1genqaUCiE1+t919YId64zdyPiUyDsxWqY2MUm5lqNhijij/o2xGLXVsp84+wi5xeKiA4kPDKaP8DruSqZ0RjaQx2wP0JhyIMQTTCaCuyY6C0XG6xNF4jWTdTeEGp/mJBuk5/M8PKpi+TzeU6fPs03vv5nCKKJIQo4VeOmuriBpUgMD/XQ3d3N1atXmZ+fZ3Z2lu9973vv6tj8OOKBO3q/8zu/wxtvvMFXvvIV/u2//beMjo4CcOPGDTKZDM888wz/2//2v71vO/rjiPnxLN/54/Nkl8o4CCiSiByuE+2Psf/IPoaHh7f97IVzK2RLLuPjGfbv27nCe+LVWV7580uYDYvLfQv86j9/lMGjnfx80MPpl6eZupahOJ7DLetUPSLNuoEsSXgUCa09SLQ/jKfdv4lKIIqt6vxqSSfuV6ila+uvBYNBPvaxjzEzM8Mbb7yBGDFwF/0sN028cQ+xkH994Ni1XUpzJarjOXwC9BwLMvuDGZp7kkQHokgeieVLa8zoJroq0RnSWFu9mTQKIIkCMb9KVbKYbZjErmXoONSxrZLcneiPa/Qd62L61TnEShN5V4xjR7owruewBAFBYNvZQlkSaNSbJIMBrMbmGba7sbS0hCzLKIpy34pW28EX9dE9GOXGiRbVK7CD0mBNt/A2bLqPxihma3zrv50lXWzidAQolnWcCxkEU6DtaAfVWhVVUVElGX2hTMEnMXKkg74daDTr2vqAqijYlo3H42PvkcPEIkFmbYtAQUfapnJq5htovR3sGQnQOLNApc0iGg5uot95ZImOiJ9lt8bE9SxDs0Xa7sOU+mcdju1w4qUpbpxqSVoPH27j1Nun6OzuZH5+npGRzSqHtzA1kaNavnfRJL4rzonv3ODSn1xDjHk58vQAKa9CNe5nTYR8VUeVRXyKjCwJyIpMvq5j2s5NlU2I+FTiAQ+3midOzcDrVylGvRiOi3eL4rbskwlYAQYOD3Bp5RLLV5YZ3ttJ2Kcwl6uzXGxQrdXwN0SYX6JbCxBWVVYnCsheGbNqYhs2Zt0k1BPmRrGO5UB7SAVUqpUqgeDtIEYSBVJhH6ulBouFBkNtgXWPvez17LrY0VZ4/nN7CEa8yKrEUx8epnBxDUFq3TuFmolvi86bTxWpNCxypQpt0SBW897rTD6fR1VVurq67vneneAJe9ASGrVMjVRcw6tUqDY3rzWW7dI0bQ50RTDrJqIkkr6axmxYmH0h9HwdI12krWYR9Mo0OjTml5Z5cqyf3oQfoWpgWg5t+9u27Zzd1HTd8D8BgVAwSH8sQLI/yVTZINM0N803Q6vr0aZF2H0whuBYjOs2kWAQzza0zLCmsGY5LBQaZK5liQ5GP5jX2wGGYfHtP7/E9ZOLmHUDQZZIDISZzMzw4Q8/y6FDh+js7ODgzWP4f781x8JSFUWVEO4452JARSo0MKoGjbuK2OdPzlPNN+jcnWx131OQni5w7tVZHnm8D/kuoY54PM7U1BSmaSJJMu/M5FCqIvEbOZyqSTDhY0UUuLxU4pnRnZ8jmqbx6KOPsri4yIkTJ9i/f/+G0aPJyUlSqRQdHR3MzMzwR3/0R/zTf/pP7+ua6e7u5vLELKF4OzNLazz99NN84hOfuO/rTZFEBhN+3ol58GXq2JXWs9ZpWDhlnepghN6oRlRTubJcxi3oyJqMFPUiAdpMkUquQaG+2R9uO+i6jiiKTE5OPhBlMxAIUK1Wt4yDhtsCDB/rYrxh4sk2cESB5qCfh7okgjfHWxqGzcs30hiWQ0/JwDi1iuy6PN4R4IxP5VylQTYoYQpN+to6GesI8eHd9+5UrufWt4690GIfje3eTWOx5SH81FNPcW6hyHdWdMoTBdS5Ek1VRNmbZH93BJ/Px759+ygUCvz+7/8+oigyPDyMIsW4fH4FgN37UwwMvbcY8EeJB070PB4PL774In/0R3/EX/7lXzI1NQXAsWPH+IVf+AV+7dd+7aeq9Xk3HMvh5b++yupyhbbROKosUa0aVGZt9n74KMePb+3vcQsHDnWgemRGdm3mjt+N5bkCtmHTtitGfq5EerVGLO4ntSvOJ4djFBZLvP6HZ8leybBi26BANBkg1hNFjnp3FBmQpVYHUNRUbH1zQDgwMEB3dzfvvPMOb373ErUVnYmTeRxRpK27g0whhzNXwq1buH4FcyjK1abJjQvLtF9eoyMVpC2msThXpChD201qj98foFqrbqgkBbwypYZBuqKTvZG9r0RPU2V+4dkhruxK0GxYdCb9jLQFmJspITqtCRPXZRPlFMBxoKk3CPn9SJ57Uyyq1Srz8/N86EMfuud7t4IgCOuD0oIo0H+4g7YzK0xna9QtFz1fb832RLyEUwE8fhXLdsjmKwz7FPoPdXDmrUUyhQYdownaHZc5TaV0JY0znkPEwpfwg+hQMOrUwx4Gnu7jU4/1Id8VgHkjXqJhL4GMSalh3KZuiiKDQ8M0BC+diPTsbWOhVEcvphHyDeS7OjNOzUSyRcIH+nl6V5LZJQ9zHnHLGStoFReCmspqus7ihVWStx76H2BbnHhpile+fgXn5jU6N57m8Ief5+d/5fF7BhP9Q3Ga9Xt7YIqSSElpeQ25i2XSqxX8UY1YQKU9rlGoGayWG1R1C+emz16pZoAgtszLPTIeRVoP5Z2mhVO3UAdC6Jq8Ppd1NwSxJR7i1bw88kuPcOnbl5g5O0OyK8nDPRFmxAaiG6SyVmClXiWdzxDoHSS5J4kW18hczeCL+qguV1mdL5H2isQHdl43JFFAlSVmsrWWKq4k4Al6aBQbGDUD1b81fbM9FeTnv3TbdL7skdZtKfweiVLTBDayBwyzRR2/JRyj3mPQv1KprAuueLbwKHw3ECWR5J4kpRdKRDsldrUHuLxcQrcdQl4ZSRCo6RblpklEdeiJ+ajNl5G9MnpVpxzxcmahgOGX8CoqvrqBZti0JQT6htqIBjzUF8oIokDvE73Ed21kDXhCHjxBD3pJpzemcXYuT8OQ8CkSlaaJADxyYIxIw0bxKji5OqWVEllRJOb3rAtU6aZNptigS1QYG+3gxkQO1+Pgvwe9LqopFBoWc9cy9DzWvWm2+QPcxtl3Frn8yizB9gDJwShG02TtRp6jj3+GT37y2Kb3h30KU6oItovr3B4dcGoGtiKh+JVNz4DcWg3Vp2xY731hD/Vig2pVX/f/vBN79uzhypUr7Nqzj2zVINS0caomSm8Ic6GMrx5iubgzY+FOdHd3k0qluHz5MpIksXfvXorFItVqleHhYcLhMEeOHOELX/jC/QuaeOP89z96g5AyT6jzQ4wc3feuiwoHusNcH4hSqJlEMnXsQhNBkah1BmEgzJG+KJIoEPBIuH4Fp9DEaVq4DQtDEvEGVFTB2URN3Q5TU1Poeku98kEKIH6/n1wut2Wi51UkPnu0m4vtASaWSvi8Mnu7o7iFhXWP4+lslUxFp8d2aZxfQ1AlBElAvJHniWMdnJJBrK7xC48fZLSnje6I754WDZ0RH22DETJzJSJzJQSg4pUI9UXojWkc/PVfX6fSHuqJYnx0hNOda9QqOtG4j8f2dbDrjhGXWq1GNBqlXq/zn/7P/0FKeYTScsuq4epbC/zCP3qYgcGfjGTvPRmmi6LIl7/8Zb785S+/X/vzE4PMUpnVuRLh7hCq3Lp4AgGVil9h+vwqT3xs50QvFPBw7OH7o/31jsSZPL1MeiJP21CM1F3zLrHeCAeeHmAtqqFJYKerKHYd5T7m3BzHRZYEXMfddmhdECWOHz/O6Ogo3/3OdzkzeYqA2I6Qc4jLHeQDKpNajVhvgMH+GMLNWYnppsnSWpnkqUVswB2MrM+JBPx+1tJrmygDfo9C1jKorlSxTfu+BumDXoVHhzYmzMGOIFGfgk93qTQ3zvvcQqmuQ6NBd28XgfadqQvLy8tks1n2799/X7z7rXDLWuKW2Ex0MEpXf4gbf3qZvGEhKRKyJFKbK1GYzOMfjuFoMub4PKmP7iLcH6bwzWsImoIkCkiiQH9PmGW/Su7cMnKbRsELwUSQWG+I4wc7ODwQW6+i3Ql/m5+24Rjd6So3LJtMRSfgkbFsh4boJSmIpKIau490Uio3eKui48yU8M4ZiH4FRAG3amIKLvmuAGMPd9Gtu1zULbyhnQMqvyqTU0QK2Tq2YSPfwzftZxmu63Lj1BKORyJ1M4FZmYbCvH1fD+hk8v7tLvYe72PlWhZ3sUKQ1prgui4eRSQV8dIW8lDRLeyb6rVBj0S63CAR2JiQODUDp26h9IUx2/xIDsjbFf1ueu4JgoDqVzn48wfp2tPFH//7PybhSZCQEpQWKnhCHkREKrbJZSNDZyCJs1REqTTpSmoEOgLk5os4s0WEiA+SrWvQ4/GgN/V1oahbCHplyg2TmmER8ikomkKz2MSsmdsmendDi2stewfTpifmZ6nYoG7YaDcDXNN2KDVM9nSG8Mg2el6nff/OVelcLofjONsajr9bRAeihLpCFGeKdEY8LJUNFhdKrCHgSfiIdQTZ0xFiIOYhPbVMSAsR7ApijBtk6waOCz29EZYUCVVVqFxaIFwL0zAb+PGTGEuQGEsQ7tts6qz6VeKjcRbeXGBgKEqpYbKQr1Oo6fhUmd0dIbqCXkq5Ev1P99PlgvHX1xhPV1ltGAiqjOO4KA2LTtvl4Ud7iHeFaFxNI3vkuxuEm6DKIrYqUKsZmHUTPrD53BaTV9IgCARuxgyqVyHUEWD5Ro5CoUE0ujGWGEsFOZ8KYGeaMF9CDHpa4iGmQ6E/xGAqSOddyptt3SEWLq3h2C7izU54Pd8k0RcmGNi6qBEIBHBdF9vQCXpl0opI2CNhzpeQwl6aqkB8m89uh1sm36+//jrf//730XV93Sbs13/911velTdFkO6Fpmnz6uvzCFeKhBMilTmXH0RnGfjcPpR3MRfaHdX45KEu/s6nkF4sQd3AVRUiXQGeG06yt6NVrBtuC5I61M5qVSewVsOSBJr9QZ7Y10atkKGz895CIoZhUK/XURTlnsqi20HTtA0q4nfDrJt0ihIHjvQQvMUE6gqvm9vXaf0eoW6B6SDfpHzbZQMjX0X2GTw81sfRnhCxHdlIt+FVJJ4/2s0Ltkt2Ko/ruIS6Qzz3SO+mZ5QkCjw2lOBwb5SqbhHyKpusH7q7u/kn/+SfAPD9v73B61+/TOe+NgRg6fIaNy6t/Wwkej/L2PYZc3+07HeFx57oJxT2Uio2GRlLbln58oQ92IZNLOHDdVxU1UOz0fK82nZX3Va1tD3kxSwZG+ZoKk2Ta/NFLlxYoVYxULwS+/e30z22G0f7GsnuOElTpq55IBZHmy6SXZ4h6GkpZt2al6g0TJYdB3mpihBU4FZVVRQQRXFdCvwWJAEcWnOJru3eXSC/b4R7w8S7Q3RP5pjULURBIHAzOHDd1u8rVuukDJvuXe2EunfueqysrCAIwgP7+0GrClar1dYTvUJZ5+pCCY+moKoSDcfF8EiAi1nUcd5ZZuhQin0/f5gp/RoT0xMEE37ciSyO0+qOyZKIaDTp6gvy6d84xts3zvCR546SCnm37apBq7vY81gPellHupxmxXao2QaSC4OI9Aa9DD3eS6Q3zHN2CEkUOHdplcxcCTXfRLAdjIQPpSfI/r1tPL+/g9LZ1fu6/G8p+X1Apbp/3D3ucV8iPu8SR4910z8UpZGpU7qUZuHEAmbVXO9Ci+LGeU5REMhWGli2gyyJuJaDXdQRVBF1OIrSGSRfadIf96Nt0zGvZWo0cg3GvzOOYztoCQ21Q8UYMFjJrGDnbHr39xJIBpkdb9IgSSAWJW86OFUDs1AntyTRn/AjBVSEjIg5V0KKeBAUCUVVqFaqmxI9URBwgPWRMOHmPMw2M2JbIdQTwt/mp5au0dMRpNgeZCJTI1/TEQHHdeiNB+gOiKi2BD52ZClUq1Xq9fq6gNf7AUVT6H+2n5N/fJ4z/+MidcPGrykoho1Q1ImoCnv2tNPMNfAIHuQBmVh/jNyNHBGvDK7LUqGBJ6Ai+myGnxhm+KPDlO0yvUO9+KI7FxPb9rZRnCtSnStxuCfISFuQpmkR8Mh4XSjOFEmMJogNxZC9Mk96ZbpPLTE3nqXeaK3byd4ow4c66DzaQe7GzRnHd/mc/WCtuQd2ODxbHbqBhJ8jI0lOGTaeJRlvoYkT9FCJe4juivPUrs2+b0ce7WXqwirLV9P4Il6MqoHsk3nomQGkHfzV9u7dy4ULF3iob4RvFRsUcNFMl5xPIpoKsq/rwSyrLl26xMTEBJ/4xCc4deoUBw4cwOv1Eo/HmZiYAFrFGvlmIWorNE2bZsUg6vWhB128BaiXdXTLeVeJHsBoKkhvTGM6W6Wm23hkkf6Ef8OaG/YpfO54P6+EFMYn08TiEXzlGZ4ebWdy/Pq25u13YmpqilqtxhNPPPGu9u9O3GIn3Q3DsPjet65x7a1F9LqJFvZy+Ol+nvnoMKIosmfPHq5evUqh1mixrTwySAJ2qQmiiGkYVJtlEr19aJ57C4ndjcFkgF//8AjzR+rYrkt3xLejXYJXkbZkmmz6vbTmJ12Hm/eKcM9C048T7jvRe/bZZxFFke9973vIsnxf9DVBEHjppZfe0w7+uCLRFSLVH2XqRgaPN4aqSlQqOkLDZOjwuzPKvB/s3b9zghHpi+AJelAFgZBPwbQdjHplx0Sv2jQJeBTaVBkUm+hQKwhJV5r81d+Os/zOImrVRBGg7sBLby3iGQ7y//33/4lE0eHP/o+XWbCb7AslSYsilmmRzWVob29fD6yCPgU96KHmVBBWa7jDcYSbi3ooFKZcLhOJRNb3SbccAi4oHhlRefBoVvbK9DzWQ6OsQ7rKgmGz1DDWFyhNkmgrV2nrCtP1SBfiDg+a5eVllpeXef755x94f+C25cgtqedLZ5YoZmp0fKgft2TQWK7QLDRbyW9KpVkzCA/HePJXHmEsP8gLL7zAYNtuYgEPy1M5/Ak/lVIVCk0OPzvCgQMdzC9w36blvpiPkY+PEO4Jk7mepVpuosgSke4gbXvbiI/EW50WWeCje1Mc7IlwfbXMSqmJ47gkgh5G24P0RDVEUaAZ9hD1KkzrFuzQFanpNl7TId4euC/K7M8yBEFg98NdrH7jKivTLfl62XQYe+iHIwEdj/sh7ifVH0WURS597RLlxXJLnVJTkDzSetAT8Sm0aV7S6TJxWUUUBaSYF7UvjBTyUKybKKJIf3zr67G6WmXuxAIly+FCvYnlCoRFAap5hKbAvsP7iNQieLu8TKYrVJNtdKkygqUTCHowgwa6V6XhuExlqiT9Ko4mY1cN7KKOnLwj6HFcuCPwNGwHRRRQbwZjjum0VEzfxZojKRIdhzuYenEKvdjkQHeEqAdOX5lgdHSMpdkJWFvB9PQj1FQ6H+7E37b9vbmyskIgEMDvv/8u7P3Al9SYapg0gXDMh2C7oCmYDYvV00vckCV2PdzJwIcG0Ho0lmaWWvuZb7C/zcf00grdoTAdro/ErgTtB9qxV+x7JnnQoogPf3R43TDdsRy8sohhOThembZ9bfQ92bfe1Y8Nx4gMRNi1VsOsmwiigJbQ8IRazxPFr+D3yFi2vS0l/xaapo1sOARjnk0ehR9gI0b2tjN1aolKrk4wrmHUTcqrVXY/0bdlYVmWRD66t51U2MP5hSL5moEiiRxN+jnSF6Nri8/09EX4/D96mLdfnWVltkDnrjiHHu3l4JGd1zJVVamjsrCcxbBdJo0m4WDLtuST+zu2/K7tULtZ9JWwmZmZIRQKsbq6ypNPPtny3otE2LVrF3UpwB++dJVKzSUW9fHQcILdHZuLwUGvQudQlOvX0+iTWXy9bewZiqE9oKWHT5XY27lz4toW9FC6/BK7Ygl+7iMP8c//5//IN9wie/fuvef2DcNgdnaWI0eOPDAzaSe89oNpzvztJFrcS7QjSCVb582/vkYw4uXhR3uBFh23cfEKolEj5w8R29eGMZXHqDWoxB0iB4bQEeiNiO860YOWCNZW5+pBUSqVKNYnUaKwci2NAMT6wuw7/JNjw3DfiZ7ruhvUNR3HuWeV7EFO0k8KRFnkuZ/fTf2PddITOVxcFEVm7OEujj7x/lVk7xdaXCM6GCV9Jc1Ie4Cz8wW8io90vgyyB0loqWwqNxOahmFTaVqtalhRJ9wbJpAKYFgO3/7+JCuvzpL0qSiDUQRZxHVc7EKT3OUir3tWOCrJSG3t9EZ9LVPveIxoNEooHNpUPQ+3BahOF3BLBrVMjUBHi3rq8XooFgoQab3PdVvqb0OSSHxX/D1LYseGY4y4Q/hOLhKbzfHO1XFG9+1Fclzmr11mrbrMU//LF4j0RdY/U6gZzOXr68ql/XGNCxcubCnN/G7h9/uZn59f/3vqchrFpyCpMiRlggkNf9NaD0rLxSZr+Tq6bZNKpfj4xz/OCy+8wNjxYbIzLhPXl4iEfBz6xG4e/+ToA826eSNeep/oJXU4tR5Y+aK+LbfVHvLuqHoV7g3T3RNmcTZPVW9V7e+G47hU6wYjPoXO/W0fVNrvA48+NwTA9TPLAOx+uItHnn1/qH3bQfbKDHxogOJssaWeWNYxqgZ2/uYc782lvUtVqMkipYCCp92PN6ZhCFAr1vHKMod6IqTCm6+ZZqnJwtll5lcqVIYj+G56XC4bFrYdYEw7gm9BIjAWwBPUyCzWUcRW4ahSaQIgeFsd+oBHolg3aZoOHo+MruvI6RpyQgMBNM1HvdFA899O/MpNc0On0aybyH55PaG4XyR2JzBqBosnF6nn64zfOIcHnb1dxxDyPi6dnCRzdZVP/z8+Tc9jPdte77VajWKxyEMPPZgX4p24mymxtFAil64SPt6NTxSwck2cuokccWkK0Ej52fuFves0+e7BbqaKU/gFH2svnqCZy9H1zNPEBmP0PdX3rtdlLaEx+tlRKksVyktlbMNG0RTCPWH8W4mESeK21iu31pi5ySzVpklwG0VlgGLdJGG69O1J3rddz88qjj7Sw9JMnmtvL1JZqSIoIp172vjQp0a3/YwiiRzti3GwO0LNsJFFYUshnTvR2x+lt//d0QVnsjXOFrxkx1cIFk04ew7t6F7KIS/Zqk7/fRQ2s1WdN66nmZ4tIIoCySho4Rj/6Mu/ti56dOzYMVZXV/mrF1/jWtpL9o1pOrwhZmWBxYMF+PDIpgRCEgUO9cm4H+9n/IbC3gMDfPzR3nvOk70XvPLKK8zPzzOmaQi46xoKa2trHDx4cMfPXrlyhWg0et8WDO8GtuVw5e0F1IBCJNW6f+O9YVZvZLlyamk90QM4emAvi6XznFwsoreHCCbaWVtZJdDdT8lyeHokSdJT/3vPIb72ta8xPT1NtVrlH//P/4LZ8TKu67L7QIqOzvcvmfxh474TvVdeeWXHv38W0T0c51e/cpwbl9ao1w3aU0GG9rbtOFdmOy7LpQYNw0YUuOkr9N4SiFtI7klSmC7QYcO+jjCvT2QoLpTw1e0WnaozSGcqgG61Zm9GU0EGPAqWadC2vw1BFJhaLbN4boWoIqPeYccgiAJy3EfMcVk7t8qNoIeaItJ2sxLb09ODY9ssLi5t6NAByFEPnqgXe6lCMVdHifvw3Bykl1UF0zCRFYW1cpOII9CZCt6XEMv9ID4SJ9AZ4J3f++/UnHkOHnwO1a8yZ5zlO3/1HfYu72VPYw+CrPL6RIZLCyXKK2UwHVAlXJ+NZjh8qOu9e0LeCsBuwdBNxDuvFQFE3+1bUqqL2LqNZTrgg1QqxSc+8QleeOEFrIjFTOV1/tO//0/3VVm/F1S/et+zSdtB8kmoSRPn9QUqci9W0EvIK68/9OqGTb7SJFmzGDvWTaQ/8p73+2cBoiRy/KMjHP/o9uqaPwx4w17aD7SzdmGN9oPtWA0Lo2rgWA6u6yJKIrJPZlAWOX1tkbmlCq5HQgt5GUuF6Ilpm2YjbqG6UmVloUxFk2nvjaDcFGkK+xSKssxCugYrJZJ7k+RqBqWGtb4tr8eL3tRRNAXBI+E2bXyqRM2wCXsV1so6nqoBtgOyiChJ2M5toamabiEJ4gYl2mapSds91u6tIAgCnQ91osU1XvnGK+SWcigorFxeob5SJ9AWoOtQF6MfH23NlW2D8fFxRkZG3pfCxy2K+C1VQV23cEwH1SMjemXUO9RzRVXC0ZQNv9vn8zF8dJgXV19kzjNHeCRM93Pd9O7vXbexeLcQJZFwb5hw74PR7G5B9av0HmjnyuUlFkousrS18FOuZqA0LXqTfpJjyff0nT8LkGWRn/vSQQ4/1sfqcplAUGXX7jY890jcoNXdC/veXfKfy9U49/YSN84tYxk27b0R9h/r2sRcMiyHl66vUbmcpmOhwuLSIqGqje/cApLo5RVVojeu7ehT3DRtvvXOArOvzhIs6FgCjLdr7Pvol+jo2NiVSaVSiMsmzZcuE6tbBDq9aEWd/IU0p/sijKWCG+7Rer2O7Bj86vP7KRzrblkWbDET/35CURSSySTlchnbtgmHw8iyzD/8h/9ww/tKDZPJdJV0uYkii3SGVK5cu84vfuHzP5T9Mkwbs2FuWudkj0S9utmK4bNPHsJz8gJX10pMpMv09vWSCvs41BNhX2eYxcWFv/dE79ChQ0xMTPDQQw8xMNDOwMD7nyD/KPDBjN57RCDq4+hT/fd8n2U7XF4uc3GxyGKhgWE5IEDIKzPaHuJQb+RdURC2Qqg7RN9Tfcy8PEPcsgnUTZSsjuPY2KZDrWKwIAns74vSF9OIWy5mWaf3qV5iN6ViJ6fzkK3j6dq6WiHHvHiWy6xVddyu4AYevihJKHKron6nYpwgicg9YXwFnfJymstSnd7uzpZSnxZgMVNA8QWICAKjXoX+h7vQku+fQtrrJ19nqjSFulslfCxMZ2cn8hUZj9/DCy+8QDAcQeg9wpmLq0TmyiSKOlgOKCJZSae2r4vvX13jY3tTm9Qr3wsSnSEyU4VtX28Um8S6Qvjv4Ji3t7dz4MABfud3fodgMEi+nqcreluG/U5lz3cD23GZy9XIVg0c1yXkUxhM+O+Lvw6wurrK1772NYq5IiP7d6HoCguZGqteGUEVcWwXb9Om24VDR7vY8/wwyg7V+A/w44H4SJz0lTRW00INqFsaii8tlkifz1KaT6N01zn2uQMk27YXN3Jsh9JCiUJVx9cXXk/ybiGsKSyLLuVyk2apiRXx4rgO8k0BB0VVqFQqeIJB5LiGuVxBinkxLJtU2IteMyhWdCTdwie3ti2JEqZlUzMcGobF3s4wqZvdaUu31n/rg0AQBKKDUX7u//1z7HpmF0bFIJFIsDewF1uzKZVLKJ6N13o5W2fiahrTsIm3e7Asa1OB7EFxSxHvVqKX6gjhi3qpZOrEekK4toNrOjimg1lu0tG/Ofnyer0M7BqgqlfZs2cPga7AAyd5d8KxHCrLFYyasU7LvF81TNd1efHFF5mbnGMpu8xA1zGWi02KqojfIyOJAoblUNdNNN1lRFM5+OwA4b73llz+rEAURQaGYj902fjFhRJ/9d/OkpnO4wmoSLLEjRPzTJ1dZvkTI3zkk2Pr753L1VheLhNda4BPwo2rOKJCRTbpSNdJL5SZXKvumOhNZ2ssXFojnmvi6QiA7aCs1Ji4tMbySJLu6MbrL1fS8ZkuWmecilEjFA/izdTIF5qYtosq3362jo+Pr3fRotEohcL2z/P3C0888QTxeJzdu3cD8M/+2T/j9ddfZ23ttrjddKbK315eITNXRKqauKJA1q0yNjhK0wblh/Do9fkUUsMxJk4uEEr6EW8KVTUrBn2jW6vLP3VwhOU/+3PyJ97kd3/xPxAPausx5XZzgD8qzM/PEwgE+M3f/M1tPQN/UnDfid6dlLN3g97e3nu/6acclu3w/atrnJrLo9RNImUTpWbiygLNkIdTZZ2JdIVP7u9gpH1rysotGIbF5HgWRd3amiG5J4koi7z9nRvIi1XCwKrcJBj0EzRFPKrEkYAXPdtAiHgZeG5gg3dUs2kiOiBsp8ApiYiigGULiAhYtrsegAFEYlGK+QLtHRsrc3bYgxQTCdVrdEpR7KZF9abBuVDVGfUE6Yz7GXqsm66Hu95XSt/jjz9OvV5HEAQajZYUc6lUYmBggD179jBw4BG+eWqVxHQRqaAjtWuIHpl6oUKkAOJUmXOaylgqxPAOAey7xb6Huhh/5/ZcxJ3QawZW02LfI90bBtWz2SwTExP09vYyMTHB17/+db7yla+sv/4gid5stsZrExnm83UspzVtLNCaBXhkMM7hnsiW29MtG8t20VSJtrY22tvbMU2T5/+njyIUBJYurjE3kaPZsJAUgbbeGINHOkiMJfBuQef7AD9+CHWHaNvTxsq5FeK74ltSetMrFYprJaSwSy1bI5uu7ZjouY5LrdAEj4yne/N6JwggyBKO0AoUVFlEEkVMy1mnnt/qjsspP1aujlk1kLyt+a3esAfH0KlZNvlCHUkUcRxolEu0R4Ic7o0yctM/D6A8XyY2ErunINO9IAgCoY4QPcd61v+3sLBAOBym2Wzi9bau+cmLq7zwJxfJr9YAF8Oq85HPHcY96r4vViOKomCatz21giEP+x/v5a0/v0I2U8dj25gVg2a+gT/mw79UZfXCKtGB6Dp1dWFhgaGhIXw+H5IkkUy+t66Y67rkJ/Isn11mcbpAvWkh4JJM+OkcS9J1rAtvZOc1QRCElpF8Ic3xX36UIWWIiQurrGRq5GsmhiAgOy4jqkx3b5iRx3roONLxAT38hwTXdVkr6ywV6zguxP0qvTFtx2Ko4zi8+JdXyM4U6BhLIt0xE1tcrfDOdyfoHYoxOtaKSSq6hVs1EXULuSvIUHSISCFPPB7HmCuhNG2KjZ394xqGhdu0kCQR8SYDSck1KNWtTV5/AB1JjTlNJlgyaNpV/KpEXZXpTmoblBknJiYYGhracH2lUilWV1ffk2jbvdBoNNYF3W4hHo+TTqcZGhqiWDf420srFM6v0bZSBd3GcVwkocmaN8zfXU/z2UPvzaMTbioa31XUf+Ijw2QXy6xcTSNKAo4DHaMJjj3Zv+XvOHv2LJrPi+zovPXqS3zmM59Zf/1HleitlBqcny+wkq3TFvdxsCeKkV9B0zQ6Ot5/vY2/D9x3otff3/9AC6Zt39us96cdb8/keXs2T7KoI17L4VSMlrqi7SA6kEr5yQ9FeOHyCmGfQts2c1C27fAXf3yeibeXkFSR458Z49kt6FzxXXF2f34P5wo16q/OUJtdwxuMoEYjxE0X1a/S+VAn0YHoptmFaFTDkgWcuom0xbyKo9uYikSsPYBp2JSb5gbqqc/nI7OWxnUchJszIo7j0qw1cCky9nwPjz/2OOlrWSqFBo7rUvfZdOyOM3RsiFBX6H33VfP5fHR2dm6Yf+nr6+MLX/gCZ8+e5fTkKqRriLkmcu/t7zdFh1B/DGuhjLNa5epKactEz3VdCisVMqtVbMtB0xS6h+P3tA0Y29PG/qf7ufCDKRrFJsE2P6IoUMk1MMpNBh7u5qHHbxdKcrkc1WqVL3zhCwwPD5PJZBgfH+fChQvrVcV3uzjOZmv89fklyg2TlOUiFZqtGcGwh5xk8reXVzAsh0cHN3Y7zs0VeP3NOcymRf+uKP3BOr/4i7/ItWvXaEu1QQoSowmGMzVs3UaQBHwx3wddvB8Rxi+vcfn0MsVslVh7gAPHuhkcvf9gffx6mounlhjd386uY11UliuU5ktb0m0DIQ8evwe74iD5RUL3CNitpoXouvhSfuqKxN13lOO4CIqIR1OwGhZJTSWmqRQbJsmbtEO/T6NaqxEIBVC6ghSvpOkIelFlkWahwtED3fjGkqyVGzRMu6Wy2ZTY09++wWS7vFjGE/LQ/Wj3joJM7wWpVIrZ2Vn6+/sxmyYvf/Ma2VydtrE4uDZrKxLvvDTN0N52eu7DV/XdwnVc9qZCpCMaC5fXqFo2giYTG4mz93AnKjD14hTeqJeOwx3YSZtoLIqmaXi9XpLJJNPT0+/J8iFzNcPlFya4ka6QUyUswQUEtOUyXUtl9uca7Pr48KZkb262wJXzKyiqxOjeCL29vXzoQx+iv7+fjo4OOg53kJ/Kk58rYRk2Xr9CYlecSH/kg2LSDxFN0+ala2tcni9SX6siOCDFvAx0h/nY3tS2lO3ZmQIr4zmivaENSR5AJBVk6Uqaq2dX1hM9jyziqhKuIrY842QHj+rBtR0QBCxFwH+PTnPM70GOeTEXK4iZOq7t0BAE/AnflmMzh3tiTDzSRebtJeyMxJJkE32oi8uvf5t/+3KF3t5e2traGBkZ2WC6DjA8PMzp06d/qIleOp3eUHgpl8uEQiFCoRDz8/Os2H7SswXalqqIQQUpFaCQzxOuerEWKox3lEkPxreNM+8Xfr+farW6IdHrH4jxy//0US6eXaacbxBvD3DgaOcmQZ9ms8mZM2cYGBjgtddeIxaLcenSJfbs2cPw8DDw3hO9bFWn0rSI+JR1n+C7sVRs8I1Xp8mfWsJbt5j3ypwdDfKZx3sY+ilJ8uBdJHp/+Id/+EFl7AFQ0y3OzRcIVk2EyxlcSUTuC60fS9dyMJeqxEWBZUXg6kp52xswl60xe3ENLeajWdG5/NbClokeQF93mLFPDPGnc+PIjg8jqLD7+VGeem6Y0V2JbedFRgdjvNUToj5XJhBQNyVd+loNJ+7j8HNDzP/dDNeaJmGfsoHCGQyFKJfLhG9SkXJVndL0HD1Bk2d+8RnaO9pJHUxh1IyWqIMMl8cvE+750VBsarUapmkyNDSEYRj8wck5+spBBI+0/nsb9Tqa76YoiSLiq1mslJqbtrUwnuX867NMXE5Tq+iAgCwJtHWF2HOsiyNP9OO5g5pWM13S5SZRv4oiiXz68/tItAe4cGKe4koFp2Hi9Srsebyfpz8ziuZrfTafz1MulxkYGFjf1pEjR3jooYf4m7/5G1zX5dChQ+9qcbRsh1fHM5QbJl2ZJs2rGQzDBlFAcCHSH6Y2GuPEVJbhtsD6w3uxUOd737mOcymD4rqcvbZG8JcPIssy+/fvX9++IAr39Cf8AO8/Tr4yzSt/cQW9biFrCnNXM9w4s8xHfnE/Rx67P4bF6y+MM/HGAktTefb+7rP0P9PP5PcmKc2XCPWENjwLhkcS8Ik9LM9n6BtK0dW9/X1sVA0qSxUGnhnAmspx3bSoG/K695zjtFR/wy707m9HEAUkSWCkPcipmTyFmkFEa61LLi6O45IPefD2hFi+eJX0DRu/6qf/8X6ifoWo/3ZhoVqRcSwDFB+O7VCeLyN5JPqf6cf/LvwGt0O1Wt3gC2pZ1rqy3a0uW3qxQna5TKQnjCKLVMpVunrirFxJMzudv2ei51gOelVHEFom7/cqirmOy+Jbiyy+tcjewyn2He+hWGqiKCKJhH/9867rUs/WufjNi/Q/2U/3c7dnkj0eD11dXQ+c7DVLTaZfn+NqtkZGFUkEVDyKhOu2RHEmGybC+WWCKT8Dz95e3zKZGn/5B2coLBTBhYl9Mf7x//LMungGQCAVIJAK0Pv4u96tD/Ae8PL1NO+cXyYyXSJQ0sEB2y8zsVbDtl3+wUM9W85OFrJ1zIaJFtp6Bl/VFDJL5fW/++N+4p0BqosVAktVGqKB1+fFzFew4j48HUEGkzs/Y3pjGgce6uJszURcruIKAtKeOE8e7drSgy8V9vIPnhriXH+UxbUq1eIqn3t2GJ/dwe/93u/x9ttv89GPfnTLe8Hr9WJZFpZlIcs/nMmoer2+QZl3dXWVwcFBZFnm5MmTrHh68FQtMG2kYAD3JktHaQ8i5huUsnUyVf09J3qBQIBsNks8vrEInGwL8NwOPtK6rnP69GmOHTuGqqr89m//NhcuXGB0dJRMJsPExMT6vPKDJHqG5fDyjTSXlkrUDZuAR+Jwb5QnhxObus3nZvMU3l4mWTGRYl6cQpPstTKTux0Ovetv/vHFfV+Jv/7rv/5D3I2fXkxnW3NPHdkGlm6j9GwMKARZROn0Y63VCPcEubxU4thADE3dfGoCIQ/+mI/sTZn16MHtB0NFUeC5fW3kPjvCC6+dor3Tx2e/MMaurp0Dia6wj31P9HE2ew2mCmhtfgSvjGvaNNN1CqLL0OP97H+4C3W5Su78MqtCnbawb903JhwJs7iwSCgcoVA3yC9n8FYKTLsL/Lc/+m8MDw/z+c9/flPHy7bt903yt2naTKSrTKQrlGsNhAZ0lRp0hH0sLS2RSCTweDzs3buX5ks3qOsK2h0eWoZp4rvlSeOAK7a8t+7EtbcXefHPL5ErNvG1aURTcURBQDcsFtdqLP/5ZRam8nz6Vw/TkATemMxw/kYTZeI68bYAx4YTHOwO88Szgwz1hnnjb8aZu5bBNGzGzyxRXC4zdrSTgQMRamZtw4PlVlCpaRqf/OQn15O9d7M4zufrLBTqpCyX5tUMYkBBCrYemq5hY0wXCSd8zIZUJtYq64leoW6iL5ZIBFWkqBdjoczycpWfqpXx7wGu61JL1yjOFKmsVrCbNpIqoSU0okNRgh3Bewb2pXyDky9MYIsinXtuV31Xpwu88Tc32H0ghe8+RHe6B+PkFit0D8cQxZaQxtBHhpj5uxnyE3nCfeH1YpEgCvQPRWhLqYQjWyd5rutSXali1Aw6H+4kdSiF8uI0jVMLLFZ08iJIgoDjOERMGI1ojDzRS+5ajlqmRl/Sj+U4XFkqs1xs0TEtUyDXKNMeCXDg6UHSIYsr37+CFbIIhDcHf4FggPRaGqfq0Mg1CHWH6Dnes0F190FhWw75fJ6entu0zUwms1557+zsZH5+HlUKtWinloNt2ciS3LpvcXekvBl1kwtvL3Ll7QWya1UEAVK9EfY/0sO+hzfaw2TSVS6cSbO65BJzXdJvL6O1aesdLm2LOUtBENBFnWhvlMKlAmtta6QO3u5KeDweuru7HyjZK84WWVwok5WhI+xbF2cShJb4juvCcs1g5WqWjsMd61295YUS5ZUKHXvaqRXqVFebVCr6lnL/W8F1XMyGieu0KLGKT3nf2SI/i8hUdC4vFgnPlFELOnJnoFUczNZJzFeYjXiZ6otu6W+neiQEUcQybOQtEkFLt/HesT75PTJPDCd5oWJQVCXsqRU8gkS120+zJ8jx4cQ9tQ0kUeBj+zvpTQSYSleQJYFd7SFGdxiTSYW9fHx/J+yHM2cqdIZUFMXPo48+yuzsLIlEgvHx8S1N1fv6+pibm2NoaGjH/XpQ3P18vzOpTKVSTMyVcQTWlZFL5TLhUAhMt+X9JgqbYpl3i7phcSOrc3E8wx4nwK724Aa/P2glXGuVlhVT1K8S8ioYhsGpU6d4+OGH11XM6/U6yWSSSCRCJBJhcnKS8fHxDUWzd4NzCwVO3EgTWa6SLBk0Ih5eq5tENZVDPZEN711K1/A2TKS4DymoIsgivlyDlXQNx3F/qOqpP0q8LyUH13XJZDIAJJPJn+rOn+O4GHZLOdEji/f8raWGCZaNk64jhrYOsARFwrUctIZNQbcoN6wtEz3Np/KZXzvEmTfnUFSZJz6880ISCQb4/LMPMRy9P99DaCWIH3ukFwG48tYCpbkSkuXgiAJyR4DRo5186pkhQj6FsY8MYegW56+mydoNBFVCUyVEQaDhysyli8hVAy2d4eAn9+J0jnL9xnUGBwc3zZH19vYyPz+/oWP1oKg0Tb5zcYUb41nEbB3JcTE0mf9hzfKRfR0UlpfXq8KKonB4uIvzZ9aI275WQmsZ6xx417TBcWkEPQzcMUe3NJHjxT+/RFE36RxLcofnO7JPwd8foVY3uXZ6CUmRKI7GmHtnkdBaHdmBrCbz7dUKPDFAqmnznT8+x9JiGSmuoYU9OI7LXLbG3J9fovOMxq9+ZeP5u5Onn0gk+NSnPsV3vvMdKpXKfZ/rTFXHchykfBPTsNeTPABBlRD9CuZ8Ge1oirl8ncduXm4Bj4yU8FMbz6NUDXRNJvk+dER+llFdrbJ8ZpncdIFMrk5NABuQAI/t0nbGR7wvQupwiujA9oq00xNZKpkabXcNv8e6ghRmisxO5th98N6UlOc/u5tjT/UTDt+ueEf6I+z69C6W3l4ieyOL7JXxt/uRPTLVapVQcPOMm+u4NPIN6tk6Wlxj5GMjxEdb1ikjHx1CViVS1zNki00c18WnyHQPhul/tIvUgRSekIfZl2cRJZGhZIBUyMtKsUmpYbbuOb3G3v42FEmgmQjR/XA3+x/dz+rCKvK8jD/gR1RafkyO6VApVAgMBeh7so+2/W3vWWl2daXCiZcmmb2SwbB0Dhyv8MRzQ4RCXkzTRLmpetAynBdJdAToHo5x4+IatYREOBpmbaZAOO5n1562Lb+jWTX41h+d5cbZFRyvhC/sw3VdJq5lmL2aZmEyx8e/eABRFjn99jwv/8VVKmtVJGEWX8lg36EOYsM7C2wU8gW8Xi+aX6PqVlm7sEbirmtIVdX1ZO/dWM2UF8sULBvJK24ZOIW8CmuGSSZTpZFvrCd6iTY/WtTL2ngGx3Lp3NtGYBtK4PqxKjUpzZeorlaprlaxmhY4gNgyjw+0tzqA4d7wu7bS+AAtrJab1DJ1kiUdOeVf98WV2/w4cyXIN1nI17dM9AZHEoQ7AxSXyiTuWseMpgm2w64DGwvYh3ujeBSJ020BLsYs1HiCeNjH4d4oh3si9xWMq7LIoZ7IpkD/fjA0NMTU1BRjY2McOnSIgwcP0tPTw9raGidOnGDfvn2EQrfXvt7eXl577bX3NdErN02urZRJl3VK2QbJHp1k0LMpjurv7+fE9Texoxr4FczVKo5sgw1WukYzqRFu89N5j+S4XG6SWasSivg2PdvLTZO/fnueqXcWodBk6lSZjsMdfO54P+0hL67rcmmpxKnZPGtlHdt1CXpkxtr9yLlpnnj04Q10z2q1uqFDOTw8zNTUFDMzM/T1vTurMtd1ubRYQsvrKBNFXK+EJ9ug6ZO5slTadP6TMR+rHhG72ERQROxCk6Yi0h33/dQkefAeE72rV6/yu7/7u3zve9+jXq8DoGkazz//PF/96lfZt2/f+7KTPw4o1g0m0lXOzxeoVg0EAeIRLwe6owy3Bbb1j7ldfWnNJGwHYf09YDZN0isVMisVbNvFH/SQGoyiJTT6B2L0D9y/KlaxWCQafXdWBV5F4jOP9/PwvhST80VK5SaapjDUG6UndlsVKdgZ5OBnxwjFNGavZ1jO1ym7BiYQbIrUVzIURRNnMMrC7jGGk14e7WoJoFy9epWOjg5isdZvSaVSnDx58n1J9N6cynLt4irJqSJC1WxVGwWoFnR+IIC2usynP/zU+vs/emwvb15ZpCIECC5VaYoG4bYodqGJXdLRO/z4uoOM3eGhc/7EPPlik467krw74dcUjK4Q599exFkrk8o2kcMeBI+Mkq5TupLlrbAP37lVFlYqtI8m1sUmADRFIKvarM42OfndSZ79B7fvp7sX+Hg8zqc+9Sn+3b/7d5w5c4ZYLIYsyzs+bFoNTKGlwrdVwUIUcC0H4SZF7hb64xpPfnyEt0PzNJomw6MJntj3w5tJ+GlHcbbI9EvTzMwWWBJdii7YN33QXNdBQCBUqtNxvkFppcLgM/207d06KVhfb+46n4Io4rq3zvn9IbqFbYc/6Wf4Y8Prnp2VxUpLSbFeQe1Wb+6zi6VbWHUL13XxRX30PNZD8i4/M2/Ey9hnRul6qJPqWhXXdlH8SisIvzmLlzqQwrVdFk4s0Cw00do0hu+gA+dzLnqhxtz0Kh39HTzyxUeIDkWpZ+usTq2SWcgQC8aQFKmVQHjB9bt09b93MYJcrsY3/stp0tN5tJgP1xZ566+vszJX4pf/n8c2vb+zs5Pl5WU++g/2Y5oWc9czFLNZkt0Rnv70GPFtlI5ff2Gca6eWCA1G8d+RmEYTGqVCg3OvztDWHWb0aAevfvM6etWgc287ZqZG/lqWiZCHgYPt6/53uarBVKZCvmYS1RSSHof2sLbuM6glNArTBUrzJVzfxgvmVrL3zjvv0N19f5YzruPiCJsZEbcgCuvNhw3diq7uMB//tcOcPzmPx6fwxIeHkLeZo6zn6qxdXCM/kadZaiJ5JBRNaRml3+xumHWT9JU0K+dW8Ea9JEYStB9sv6cIzAfYCNd1Wxxr2BzSuCC4Ls42rBK/X+XYh4d46U8vkZ7KE+kIIiki1XyDSrpG36EODhzdbES9p6PVgesTC4ztHiaiKXjk99/weytEIhGuX7+OYRgUi8X1uLa9vZ1EIsGlS5eQZZk9e/YgiiKSJOHxeKjX62jae1cPL9YNvvHWHPNvLSKXdExNplCf4uef6MfnNNZjKGgVlPb1JijKFulKEOXaMqGmimU0MNo0qoMRnu2Pbuq+3YLjOPzd9yY4/8osjXITj09h5KEuPv65vXhvMrAuLhSZfGOe+EoNxa9gL9dYrc3zVpufzx7p4fxCkRcuryKJAh1hL5IkUKjofOPNKzx3aBhR3lgkqtVqG34DtJLrTCbD1NTUfa8zcMuL2UG6eY2Kfh92o4HkgL6FXsihviiTh1Lkzq6iZWo0VQl1fxtHBx5MgfnHFQ+c6L3++ut8/OMfx3EcPvvZz663sG/cuMG3vvUtXnjhBb773e/y5JNPvm87+/eFaytlvn9hmexkHu9aDbXZumDmAgpTqQBdu+J84nDXlhSCgEdueTnFfFgLZaTw5iqiazkgCjRUCWmuxFuXs6zMl6jqFg6tk9TRFmDXgRSHPjJ0T9803bKZydZYLTUZn1ihtz1GTbfuaWZ6JwRBoDPiu2flJ9AeYN/ndtO33E1+Kk9xucLlxRIzFZVC2MKxXEZG+nBzOqfrFqFgiES6wZG9e1laWuLGjRsMDw8jSRJer3eDOt2DoNQwubpUJrxURdRtlJvS4Y5uEUjXWZzIIDjWhmHp4VSEp/amODdbI+X1EMyBXWpiKxKVgTBOf5gPj7WtyzCX0lUmLq7iSfi2TfJuIRz2sTyRQ5nII8b9SLcEAto1tGyd1ctrCFN5wgPRDUmeaZjUalU6OuKsumVunFvmkQ8PosW2f3DE43EeffRRXnzxRSYmJnjyySc3JXq3AilBEAh5ZQRAiHoBF9e019VWXcfFqRp4BtooVRsc6Lij2ycIPL0nxf6+GKbtENXUDWpkH+D+UVmpMP3SNNfmCkzhIAsicb+yToOGlvVFuWFy3bJppiu4fzeD7JG37NL0DsTwRXyU1qrE7jCdLq5UCMR99NwlptLIN6jl6wiiSCCprSdYO0GURZJ7ksRH45QXy5QXy8xfm0eSJRzHQRAEtLhGcH8QLa4R6g5tacsALdpnqDu0reKlIAp0HOlAi2tkrmUozhapLFduzzg7LouVRYaPD9P/UP/6TKg/6WcoOcTAsQEWFxfx+XzrNMq5ubl7/sb7wfl3lkhP5zeoBxoJjcXLa1w4s0Df0Ma1U5JaxyfRHeSpz/UgO3v4r//lD/mFf/xl2ru3TtxrhQbXTi8hx7UNSd4thKM+VgoNLr81jy/soZqp0zYURZQE3EITT9jbCqIrOuGIj3LD5K3pHOWGgeZRuLaUZ9Gr8Fw0sr5NURaRFInVK6skH98s4KOqKp2dnUxNTW1YX1zXpbZWo5ap4ZgOoizijXrxRryEBIEl096y3lkzLMSmQTDl33Sd7DuQYt+B7YtIju2QvZZl8e3F9UJAPBXfkmlzq4PnOi7NYpPFdxYpzBToeqSLxGjiA1rnfSIZ9OBL+DGDZUjXkTtaCrZ2oQk+GTus7hg3HH9qAFEUOPXyDIXlMo7l4guq7P/QIB/5zNj6XPrdEHCJaTLt73G27EEQiMT5Hy+8zuDYPk5O5+iNaXSGvUiSxKFDh8jn85w8eZLdu3cTi8UYHh5mcnKSAwcObNiOZTuslls2DWGfcl8eyhcWi8y/MU8y3UAKqjjZJtmTC7zVFmBfuL6JPrp/9y7Wcm8TOZzirGpgCRrIIsF2P0/2Rjk+tP34ztlTi5z85nVkr0y0O0SjpHPhB1NoAZXnP9Oyc5hareDNNVCjXqSIF7Fh4s/WmV0oUxozOTmdQ5VFUj4FfSKP1TApuUUe2j3ARLbBTLbGaOr2s2m7hHhwcJDx8XGuX7/O2NjYpte3guPYpDSX80EFrTOIU9QRugNUgjJtpVVWVnwblDQHkwE+96Eh3ukJk87W6Yj5ODqcYFd7gFLDxHVdgt6NGhQ/iXjgRO9f/It/QVtbG6+++uqGuQRoSTM/9dRT/PZv/zanTp16zzv594mJtQrfeXse8+xqSzXTIyH6VXBdfGUTJ59jOVPjm5bN5x/p2zTgOpgMENFU6m0ansUKdlnfoGbpOi7WShUpqZFdKCJNFbnslVFiPkL+YGvmy7SZLtRZ/sEk+bUqH/rlg5vUMm9htdTkhcsrLBTqOK5Ls+llcclkujbLh3e3s+se9g0PgjuDtTcns0wFRJhZJXAiT0wNErxRBbFGZ1KjMhThxaur+FWZ0a4uTNPkxo0bJBIJhoaGmJyc3NAJrjRNrq9WmMpUEQWBoaSfsVRo26S13DCplZpE6yZS9Pa5ED0ylgN2voLaHd0wKC0IAl/60FEy//0vYXAQfXeKasNA8sr0tAc52hdjX+ftQDS3VqNW1gkN3btTKoqAX8Eu6rhh5/YcnW5jCyJ6roYEBO6osFmWRa1WJXKzExuKa+Rni6zMlhiKaRiGgaIoW9KIdV0nnU5TKBR48803+cIXvkBIC7E8keXq6WXK2ZbNRKwzwNDBDhI+hbwkEu6PYEwXEf0KiAJO1UBOaJidAQqL81zLXyLaGGX//v3rNIu7H1Ku42LUDCRV2tEc+gO04Louy2eWmZ4rMuU6BHxKqzB0FyRRIOpX0VSbmaqOkq3if2eRcF94k8F3oj3A0WcGOPE3N1gZz+IJeNDLOrIicuxTuwjdDMDquTqnX5pi/MIa+XwDAZf2jiC7j3Zy8JmBe1IaXcdl4XKaSyfmya1V0fU6+x+Js/dYN4F2/3r36EFhmzaNfAO9pLfod0B0KEq4L4xt2Lh26z6yBZv5cxXmTZHZ12fpGYyx90BqvfMjiiK9vb2Uy2Wmp6fp7u5GkqT3ZR54ea6A7JE3qAeqmoLruExPrvDwY49u+kxnZyczMzP4/D4qlQJK2OT1t17j85/f2sR4ca5IKV8nMrg9iyOQ8JNeLKNXDSRVRG+YaIqEWzdbrEVFRFVa19VioUGxbtAd1Wg06nSEvOSaDov5OhHtNtVOCSgUV4r0aVtTp1RVpb29nampKYLBIDPnZoiYEUpzJayGtZ7MNZoNamaNwkwaIRklo0gkAp71hrNpOa39cSSUsEPBKKA5GuK9Kmi0rpH5N+ZZPbeKGlSJ7Yrd19iIILYUgL1RL9WVKpPfnaSRa/xQlVd/mpAKeRntDnN2rUpipgyLlVYB0SeT6/LT1R+7p03Uo0/0c+RYN3MzBUzTJtke3JH+b9kO6VyBQPD9j1/uhelMlb++mie3LDC3PI0ri/hSfg72x3ludzuqLBKLxTh+/DhXr15lfn6e/fv3c/HixQ3bGV+rcGIqy3K6hqnb+MMqox1hnhxJENG2X2/n0jW8RR055kUKe3ECKv50jcXlEsOasOleEUWRrniIuF7g4Y/vp6w7/O3f/A1f/oUvrj+z64bF+FqVyXQV3bTpjPoYSwW5cmYZ13GJ3xTGU70Klm5z9Z0lnv3YCKoq4/cqmLKI07CQIuDULWxRwOuVWSk1yFR1BhJ+mhfTNC6skS8UiA+lCOxTSGMxma5sSPRc1932fu/t7cW2ba5du7buG7gVTp8+zcmTJ1lcXETWQox+9JeZVmVoWgiazN7OMJ8+2Ek5l+bMmTPE43H6+voQBIHhtiBDyQCm7aJIAuNrVf709AJLhTquC+0hL4d7o+ztCP3E0jkfOBq7cuUK/+bf/JtNSR5AT08Pv/Vbv8VXv/rV97Jvf+8wbYdXb2RonlsjWtRRuoLrfHQAKeTBaVrEVqusnV3lrbYAnzm4kRIU9ins7wrzWk0ntSeOcz2HU2wiaAqu7eI2LOSkRjmoUju1hBr20tcb3lDRV2WRoC9MrqJz4eIqwbCXJ3/l4KYKZKlh8u2LyywXG3Q5AkK2gWs7iDGV1brB31xcwfeQRE/0/TMjvxOVpsmp2RyeXJXsa+N4FIXknl5ERcI1bay1OgHDJrs3zqnZHCNtARRFYc+ePaTTaRYXFzcYjhZqBn91dpG5K2souSYIcDXu48r+dj57uHtL+oEiicgeCVsUEJsWotZ6j+u44LpUjCZ7E5srxKFgkAPdEUS5zKHjR7l89QaTNy7zK5/5pQ0iCYblkCk31xOsRr3O8vIyjuNQq9c4eODQpvOieWQaIRc9oMJcmWKliCTK1IYSRB2ol8z1wMe2LCrlCtFYFByXialJ2to7cFwwrVbAu5AuMNdQ+YM3pqnXDRAEAprKwZ4Ishbm53/+5zl9+jRvvvkm3/3GD5AqncyO56g7LnglyqUy5ht19P9WItwbJfThI8hj/3/2/jPIsvw87wR/x1/vTd70vryvdtUO3Q2gATYBkABJiRQFSSRlV6MdxUaIs7EzHEq7HxSxMRtiULOh4FIzHElBIxIgCRAAYbvRaFdd1eVNenvz3rze2+P2w63KrqzMrMqqBikAnCciI6rymjz3nnP+/9c87/OE8Eec6GtVbN1CPRrFGPCSMk1ee+YYJ0JHuHb1Kn/wB3/A5OQkJ06c2EYJLq+Wuf79FfIbVVSHzKEnBxk+279rwtepdmgVW5i6iSAKyFpv1uv+pOUnHY1sg8JSiQ3RQkbcNcm7F5oi4XUobOgW/etVqutVguM7iw0v//QBglEX199bZ2UhxdixAU4+M8Kxsz1KVLPQ5Gv/x2VmbmaxAhruAQ+WabNUaJL809uUMnVe+sXje1ph2LbNte8s8r2vzFDWTUSvitW1yX51hpWZPD/990/h7Xu8QXq9pVOYK5C/laeRb2De53MlO2TcMXevozgV5r/8p7dY+6CD3rqNIMAlRWb22SE+/3dObqP53ZUfTyaTCIJAOp3eQQmqVttk0jW8Pgd9iYcHlG6PiqVvPz7LtLFtUDVx1+BFURRSqRTPPfcct27dIhQKIUkS5XJ5V8N007TABukBibOkiHQtm74BL4MHIyxdTNMOdTHyTYymjm9I4fLVCwT8ATY6GpKk0mq3EAURzelA7rZp3vc5BFGg0+ygF3TSc2mMroEkSziCji11ZEVRcDgc/M7/63cI18OcOn4KZ9RFN9hjlciiyMqVOdJzSYSCwJQjTNqtk7rzmGXbYEOia3NkPMKRV6fx9HvY2NjAsiz6+/u3Zhw//H4tqskq3XqX3K1eh9c35HusWUtBEPD2e+nUOiTfSwIwdG7ob1Rnz7JsTNveFm88DIIg8MrBGLphMRNwYGQbCDbg0xgcCfCpo33b1rJ22+DWtU0K+QYen8bR4wm8Pg1VlZl6iOWLbdtcTVb4YLVIOl+hL+jmecf2ROGvEvl6hz+9uEL1ap6JlozQaYEg0N1o8G61i1uTeWG69xkEQeDIkSNUq1XOnz+PbdsUCgXC4TBzmRp/fjFJe7aAL3dXN0DhQn+VUrPLF04P7lm89rkVdEXEquuIHrVnzyUJ+FwqimTt+pqpqSm+/OUv87dPn+b69eukbrxLeuUsoSNHKDe7fPVqisXFAnKxg2RazLlkPhjx09ms79iHZU3C6BjouoWqwpFBP7MHQlRv5nEul6mZOsmAwWGvTtcwsG2QRZFWuU2xViE00YfQtrE7Book7lhr9oJwR5xrbGyM5eVlbty4we3bt3n55Zd3KH2Ojo7yX/7LfyEQCPBP/8mv4Q9FWMjVt+wVxqNuNFnC099Pf38/hUKBy5cv4/F4mJiYQJIkVFno0U4vJ9GXyngLbbBt1kJOVlNVKscTPDf10fxE/1vhsRO9kZEROp3Ono93u91dk8AfJ6zkG6SWS/jvGzq+F6JDRg468OVbzC6XyE9EdnjIPDsZodLSuUoJ7+k+vKU2lDsgi5gxFxmXhH49hyYKDA769lx0w16NdFhn9maGk+kavvtmOmY3a6wXm4xa0LmQwqx1e/Npkkjf6Tjrfpur6+W/skTv7WsLfP+9y8Tminh0GDg1jXhn0RAUCbnfg7FWJVTpsuZukSy3GL5DRYzFYoTDYb75zW9y8+ZNjhw5wntLBZYvbhBbq/XmO2wbK99moWtyIeDk44d3Jmwxr8ZQzMNi1ElkpYZh94RFzHIb06tS1Wqcntx9PmdoaIgrV64Q1qCbX6O4Pk86tbF1HefrHb5+fo3ld5PkN2pU3TKTg37KlQqWaTI6OrZrkKAYNvHpCM3xANXVCou3NiiabdqrM5yVgriNIUyrxymvVqsE7/DVM7ks2UyGaqPLuLsPl1vl+3NZXr+wRGmpTqK7hNzpLZpZp8xfxF2UEPn8xw7yr/7VK9x8/zZv/fkKm/kcnkEfCZeGKEDh+hp5vYitafR1PXhnChgnY6z6VZxn+hCwaekmmgBPjAT5+KE4DkViaHCQfD7P1atX+dKXvsTQ0BAnTpzAbbt5/Q+vc2u5iO6SEQyblaUSrzS7HPxEzxPHtmxq6RqlhRL5uTyVfBPDskGwcWoKwX4f0SNRAqOBvzEiCaXFnvBKxYbIHtTG++F1KKTbTfLNLvnZ/K6JniAKnH5mmGCfydp//h5/6x9/dtvw+6U3lpm9mcU1GsDr+jCQDnhU8pU2l99dZ3A6wuHndu/mVFM13vn2AhVVJDES2KIv19s68wt5Pnh9iY/94vFdX/sgVNYqJN9LUl4to3pVPAnPtkKBbdsYbYNGrkHlLyu899VrrM6UccY8xCd7G3+j3Gbm7TWuH45x6omdsx2Dg4M0m00uXbpEOBzeEjRaXizy1f98hcpmDc2t8uxnD/Lsiw+eFz58aoDb55Pk1yoE+zwYpRalhRJOp0ywa5K9mcU/tF30o9vt0t/fz+bmJq+88gqHDh0iHo/v6C52uwa5bAO3W0HVZJq19lY39n40y23cPpVAyMXnfvkUb0TcLF3LgEshOugifsDB1avzpOQU8cnjvQROUvB5nOiGRUfXCTh76p+1ag2f30ctVSO/mOfal26TL7W4Y1uG363QNxyAGPQn+rn9zm1cORcb1Q3G3Ke4kqqQr3cxLQvbshClIEasxslTA2gFlYAhUhdFmrqFKEDQoTA0FWTshZGtBHJoaKjX7U6l0HWdeDyO09mzxFh9c5W595LkV0pY6Qaj5wY/sqCO5tUgAamLKVwRF5GDP3wvwx9FzG1W+d47a7QbXSYPRPn4qX4c+yy2eR0KP3tqgNWRIFfn1+l0uxybGmUi6tlmq1Crdvjj/+0D1m9kel0/W+DSxAqf/wenSfTvTte+F9c3Knz12gbyZhNv12SzbPLVto0iDTzUVuGHgdvpCsvXkhysiUheGSnuxjYs7I0a3hWBy3E3p0eC2xJbn8/HuXPnuHz5Ml/5ylf4xV/6O7y3VKA9VyC0UkH0aQguCaHcJtoyWNQkZvp9nBnZvWt/dMDPzNEo5atZnOtVurKIMR1kPCaT6NudVXRX2K7RaHD16lV8Ph9f//rXmZyc5HszORaubxJZriI0dHRdR+h2KKw42Gi16a+IBO+oolqmTT3XYPLpoS3q+ME+L598ZYL3Im5qxSYWHdpzb/PWn3/Au54wvjOv0Wh3yUo1AtEgYt1AHQ0gelTauTqxfYwHANu68yMjI/zO7/wOS0tL+Hw+Xn311a3HlpaWKBQK/MN/+A+5ffs28XhPzOdo/942P+FwmHA4TK1W4/r16yiKQmJojO/PZRFuFQil6j12kyCgLFfoNHXedSlMx70f2ZbivwUeO9H7jd/4Df7lv/yXvPbaa5w8eXLbY5cvX+a3f/u3+Xf/7t99xMP7b4vVYhMrW0e2bcQHVNtFr4ZWbpPbqJIstXYkeg5F4qeOJYj7NK4mK2x6FOwhLwK9bt2EIlNub5KPOreZ+e4Gv99JbrnMyu0cx+9L9G6mKngcMvrlLFbHQL0zn2YUW3RmCoSeHWQhW6feMR7aPdgPbMvGMnuzGIIgsLqZZ3N1A2E2jzLUv8NHRhAFBJfcU3mMu3qKpPdAkiQ+/vGP89Zbb9HomtxIivizLSSnghzuBTlGrokn3eDWRoXnpqI7NiZRFHhmIkKm3CIvCPjzLaS2TjvsJB9TiLYszkzvDACLxSKjo6MUCgUuXbrE4uIioihy8eJFhoaGsCybb36QZOEbCwTrOu2GTuFKhmXRYHRklFKpSDS2s9pTb3ah02F0yKKqpUn3W/gRsTYyPDE9yM9//Gf5r//fD8gX6qiSTjDYW+z1rs7KygqiJFHaKGMf9nCra/De+RTWpRT9TQNP2L/VsXQ2dKyZEo1Ome86kwiiSu1ag0yuTXgyhOMOdatWq+FyunA6GiT6EgQTMepLJZ44FMd3to+1UhPLsol5Nab7fAwGtqtPRSIRXnnlFarVKteuXeNrX/sawoaD9SUZx3iUqFvFMG02U1Wund9g5OwAqlsl+W6S5OU0uXKLLDYFbHruPqB2DPpqHULzeRJDfkZeGCE0sX/BoR9X1FI16oKAZVrI0v66CIIAsiRRFnpKnaZu7tkJPX/+PIqq8MEHH3Du3DkA2uU2c1fSmH51W5J3FxGfg2S+ye33kxx4anDX916ZzVEotwhPhLbNqHocCrWAxuLNLE+X248kcpGfzbPy+gqmbhKaDO1KoROEnkS+f8hPs94k/Y0k5mIV9z3qfu6Ag8pGleX5wq6JHvQEw6anp0mlUlu2Aa//xQzFtTLRiRCVzTrvfHWGQ0djhMJ708kOHolx7qemOf9HN8hezUDLQPOqjA55sVZbLGQX0AIakakIkUMR3DE3qVSKsbGxrTlBt9tNo9HYptpXr3f43X/3BhszWRxBCdkyMdMePL6dM8G6btHJNzn96hS2YlIvlTjzbJBjT/ooXQ3RmmsSOxAjnUpy5swZLAS66xXybaiXWogijEW9eGhz6dI8pWKJM5NnSJ7fICebrBSbNAUbQQTLtnE0baK3MgSvd/DKXvrFftxPu1k3G7yzkENxuAi5FIxuG92w0VUPXcZQBuPEBwSsrsWxU3HMlokgC3j7vPhH/Du6x4IgbCkjZzIZMpkMYllk5p01ruUbNJIV3JaNZ7m8I5l+HGg+jW69S/J8Ek+f5ydeoCVf7/AX35ijcSGFZtq8fyuH0yHxyvGdQih7Qb6jhPul//2rdLtdfvFj/+OO57z31gqrV9JEJ0KoLgVTN0nfzvPmN+f5W//gzAPf37JsLq2VkDMt/LcKWF2TkEchJwncSFUeKdGr1zvcvpGhUevi9qgcPBLHu49r5vytFeJoCGZra+RGkEXkiAtXVSdfaJOttvHsciynTp0inU7zrR+8z+20SCLXQvRpWyMlokuhu1ZBLbS5larumehNxry89vIk78Q8lPNNPD6VFw/H8bazuwrtWZZFp9Ph5MmT3Lhxg76+PlqtFs8//zy5epf5dJVAso6oWygjfvIbG2zm0igpC/nkMNqwj+x8AejFeMFhP89/slewNQyDXC6Hp1XmuXFoDDrpCyf4WvUDBgef4DOf+Sx/ciXFm1fmOX1qHMdRCbtrIoedZBtdKvlNHG0FiLGysvLAZtDdjh5ApVJBURRUVeWNN95g+sxzXF8vcHNukWOjCV46eQq3JnPkyJGHntN74fV6OXnyJO12m2+cv8nCjSKTmS5y1PUhG8yrImSbZJJVFvONv1mJ3nvvvUc8HufMmTOcO3duy81+fn6ed999l6NHj/Luu+/y7rvvbr1GEAR+67d+66Mf9V8TWl0TqWvBQ5IvQRRAEBENm46xe1vaoUg8OxnlsNfJ7I0s3/rGd3n2xWcZmogQdSj8wdfmUfZR6XDIEhVJoJJr7Dxe3UQRBOyWvnWRQm9BsaodFNumY9p0DQsec1+0jB51pjBfoJaqYVs2siYTPhAms5yi3ux5PNkWtDtt3PLugdJeGqSqquJyuRifnOZPL1/AU6nh6btHVcohITd0uh2DrmntWoGciHr42TNDnA+5WcvVMTsmXr/GQb1I2AyiKTsv+2KxyOTkJKdOneLP//zP+bVf+zXy+fyWZ1S9a5BaKeFvGrgmggQMk9aFFO26TXg4TCjUW3D1rk6z1aTVbNJqd6hnDQb63Jx6ZoKB0QFkWSaVSnHlyhVeffVVREHk0Ok+vv+Xt5EmY1h2T4VOlmWmpg+QzzQIW1XshMr7NzL454o0mh20kTCK58PvVvJp2KaF52oOx+0i32/ZSNezKFE3DkXGsiwy6U1EUWRiYoJYPI7L5UKWZWp+jY3bWV56bZpzk/urZvt8Pp577jnOnDnDl/7f32Szsok71UKIRhAFkVRxk2E5QbvcJnUhxeKFJAuGSc40EQUBn1NBFgVsetftfFtHk0RKKyW6TZ3JT04QnvrJUr66H0bXwGTv+YS7sE2rN+PZNcG2EdsGbU3B0i0sw9o1GbvbOZJleRv9rV1pU8i3cCb2SGAEUP0ahVyDbr27q/BTo9bFREDZZcZNcSi0mjpGx3jIp/8QlfUKK2+sgNCzcHgQctkcgUCAar1KaDxKarZEd76EcFhC8n7Y2XkY+y4Wi9Fut4lGoywtLVHK1dC8GopDxhN2Uss1qde7D0z0OrUO/SacGQtT6/PhCDkZGA1Qr1cIR8Jg9xLrjQsbFBeL9J/rR/EqCIJANBoll8sRDAbJZrPbEr352zlys2Xqeo3ynMVzrx2mvNElNZcjMOjH41axbIt8vkYtWSXW72FwWqPb7TI4OLhVCa+7o9xK30Jv6rz08ks0Gg1sw+BjhwfJ1No0OiZuTSLmddBuNriY3KCZb/LB3BW6uNhMuIg6JBIOZYta3uyarDc6ZAwL7S/nUU2In0mQXdNRHAIxj0KtVsfpdOK8swdJosjMZp3+6ShGsoo74t5h3fAg3K3Qz701x0amSGWzRQhoeFSajS5Gx0C7b0OzLZtcvUvhjoWMIolEPBpht7onNdPT56EwWyBzLcPIC48m6/7jhlKjS3WtTFRTkKIu9PUya+sVeIRED+DK7DI3U1UEAeZWNpi+T8126UYG1a2i3r0WFAlPzMX67Rytlo5zD3o4gGHZVNsGjq6J1TFQRvzoa1UcXYtio7vvY7zw7hpv/cUs1c0ati0gAG/1uTn32gGeenbv81wqlZBlCVlVgNZ9j95VN37w356cnCTfETBWNillc0QGYty7agqSiKJbtPXdKZh3cWzAz8E+L7W2gUuVcCgSt25ld51JXV5eZmxsDE3TEASB5557jnA43BOIyTdpFdt4mgbSHZ2HRKKfYqGI0jHpi8UZPN3PJCKFbB1VE4j1i7TaGW7fziDLMtFolL6+vm1/+/Of//xWJz7cTnF4JEGybuLRBCSHSC3fwKVJ/NLHTmJlF/jSl77EO++8w8/8zM/g9+/eebv3/YPBIL/6q79Ks9nkP3/t+/zuH1+kfjVNyOnhwnKGsq7yhSdHtnWTHwUOh4OB4TFCMzrN8jINrYtP9qOqKoIqgWUhdU1aj7Cv/SjhsRO9f//v//3Wv99++23efvvtbY9fv36d69evb/vdj1uip8kiltyTmH8obBtbFpD3CNps2yZ9KU36YprSehbj2jqbzcv41qcx+zw9Beh9GV335hp2GwoNulWWmg3cQUdPWMOrgihgFltIfgdNAZx3vO72i3q9Q6djEA67aZfbrLyxQmmlhGlCRxWwBAGl1qG6UaX43TliTo2xM2MkLN82bxS40wFsGRBxoski/jvCBWbXxLZtJFVClEQGBgaoFbO4PBINp4iwtIlvMtYTICm0acWc9AeduHfxGryL8aiH0bCbfKNDLdPATtc5/8YiWiRC8r0kgbEA7pgbQRAolUpblbFIJEIikWBhYWHbzIwqiSgOhbYIZrmNx6uhJDx0cw0WF1PIioltmaiKisvlRHN6MWsKk8MOPvv3TjEy+aE3UP8dnjiAruskDku8aB/i+oUNNjMNBGfvexFbBsMBP+d+4QzXHALp11dR2iamX0LRdjE9lkSIOXHUulSvZKiVWxwYDdBqNMlkM0SjUdx3TEjvDSy9YRe5tSqppRJjJx7NJsHpdHL63DGSSSi7bLKZLMlUmnZTIGOb5OfzrFzYZNY0KBgmMa9zR/dKlUX8ToV6x2C+qWNlakivr6B5NTyPOev14wBJkZBgTylyq21gFlromw2sepe7977RMdBlkYxuUVosET4Q3pbs2bZNa7PFqDCK2lUZdgzTbXRR7wS5kggPiissC0RZ2FPYIhzzoAjQ6Op4HdsDtU6tTSDi2VNl834YbYPku0nMjklgLPDQ56+trfHW22/x9FNP0zfoYzbqpp6pI3gUHAfDNCodJEVi7CGzP3ehaRrj4+PERtNc//Yypmmh17skDseIxva+9oy2wcobK+Rn8gwcjSHds6bW63eCFIEt0Y9qssrVP73Kk7/Us11wu91bhurmfbLfHr8DT8hDbaWOMyBx8rmDOCWbb/3JNbLLebK6gYCA1+fi5FOjvPKzh4kN7QyW3HE3/hE/hbkCWp+Gbuhb69z9qoiKqnDq9CkyH2RYupJDHw8Qjrrw3ReIu1QJp+JiabPNUqFJX6WLfChMqdHFLdvU73Yn77l0/E6FdLlJptEhJIlU16uPlOhBL+jOVXJUMmm0vEpBlfC1LTwRN7JDZnNzc0tJuVDvcH2jSq7WQb9TWLJsG1WWiHk1jg8GCOzSzRZEAVfMRWG+QOJ0Yt/X8I8j3JqMI+ymlsqirel0XDKp1Dy/+7tv4/V6cTqdfPazn93z9bW2zndnsnz9+9fJm73k7t997TJfeFnm+anolgqzrEmY98VPlm6juGWkh7AYVFkk4XNwyyUTCzrQ1ypIUTcNp8zgQ9TA7+Lm9U2+84fXMQ2L2FRvnTR1k+J6le/90Q1cHpVj9/mKzszMcPnyZcbHx3niwDjf2lzGKwmY5TaiXwPDwsi3aPa5CYWdD1QBHRsbY/6Nt+kb6sNKtimuZHAMB/D7/T0KqGHRcssc3EWN/X4okrglpnKvSfq9sCyLVqu1pWJ58OBBZmZmtpgDsighqCK2JPQ8gjUJQRQYHBpE0rqUDJ1GvUho3EMoJuN2u4nH49uo/7vhbmfu4sWLnD0yzdMuHzObVWbSVbqmzeE+H0cG/AyHXFijIf7tv/23VKtVfv/3f59/9s/+GTdTFa6ulqjWOoz1+zgzGkK6p6N3F7Yokyyr2FezTDv9CC4ZI1lj/r0kM8NBTg0/mpXYvXBpEoJHxRsJImkK9VaLSqWCW3agiAKmJuN2/HiKzD32Ud9/An4S0R90QsiJnWr0/MT2UOSyGl10VUKLuujz9wwjTdPcdiPmZ/Ks/mAVh9/BWnMNgpA20hx2HyZ3O4ezbaJbFvidD6wS1TsGmmUR20WO/Fi/n7nNGuKBMErDQN+oIdgCYkBDOx4jpZu8Mh7eNw//0oUkr//pLYyWwfjhKAc8DqrrVcpeheVyi3JVx7JBk0T6fBrHThzCu9HB3ZfA0dDRN+vIEReCLGJ1TMxsAzniIudVGNUUzMUyf3nxJrVSz4PR4VQ58EQ/A9NhbizeQK6sc4s6p4IRGitFbGzEeABrIsiZkdBDJW8FoHsrT/FSmk6tQ345z6B/kNW3VklfShM/EWfwqUEKhcJWRxrg5MmTfOc73+Hppz9UzXMoEiePhfnGXJrcYo6WaSA+4WZckumkDToNEdHlAkugUTPQsBgZCfDyzxxi9Gh8l6PrLdazs7McP3ucY6dsjj01zK2LG5TSdURZYGAqxKHT/RRVkY1vzeMrd5AjTvRsGUXevRIqCAJS0IE2WybT1tnM5JAwGR4aRtzDd0hVJJq2Tbut7/r4Xrhx4wZzc3Nc+sElnJ1BAmKCkujHamZxlUpcKF4i/CcWUv8UecMk4Xc90I7Co8lIgsBKS8ezUSU8k/uJTvRcURdOG7B7NKV7izdmrUN3odQzcnXISEGtl8gDZr2DxwSjabDwzQWqG1VGnh9BcSnYts3G+xtsnN/A1E1q9RoLGwv4h/1MfHICV9hF34CP2XSVoHdnIGtZYJZb9J8Z3JMON3wwwkC/n8VkFXk4gFOVsCybQqWN1jQ5dHb/c1PFhSKFxSI1WaC5UaX/Pjp6V7d6FG8BAk6FUqnUs1TYSHL27FkmTyeYv7BB8XoWKi3kmIejL44+UJL/LoLBIJcvX+b48eOceiqM3+9l9toag4f7+NTPHn9gtyFzPUP+dp7gRHBbkgdgsz1xFwQBT8JDqVBi7e01vAkvkioRCoW2iU/dxeRUmHOfHWfmmkZs0Is/1MXlcvGP/h+vkFwskcs0EAToH/QzMBHas0MlCAKDTw5SXC9SWCwwfGJ4z8+jaRoRT4R8uYCciKCOB9jLpkwQIOiSKRfbuGsdlHSNeqNBIOxB03aKZAhC76ejW4iqiN56tHXGtm2+9rWvsba6htvlZtTlB4+K26OgJRRmFmdIbiR59tlnMWUn7y0VqHUMwm4NVf7wGu7oJqlyi0bX5Jnx8K7JnjPopDBfoLJWIXr4x1N4YT/oDzj52KuTvO2UaTe7eHwGX3j5AP/bf/htWq0WZ86c2eHVehe6afGNG5vcuJLmUNHNCWUMW7RpF1XevLSBbdtb8/OHTg+QvJ6lkqnjjbhpVtu0yi2OPj+C+oBC7V08ORZio9xiUxZwdCxaDon+fh8nhvYX0F/8wQrdRpfEoQ/PpaRIRMeDpG9n+eAHKzsSvUwmw8zMDKlUis/+/BDRsSDFWpdguoGwVkWQRboRJ81RP+eGgw+0rVIUBa8mMuV1c3HQR7Rj00lVSS8X8Xt8WIM+5AEfhxN7z5PthnsLG/diZWVlmxexy+VC13U0TaPRaDAQiRONe6iGKrhWKzTqAoZtYpc76JqEEXbw7PFJDu3y/RaLTa5fSrG+WEQQBIYnwxw7nSBwJ+m+ePEiY2NjW0IpT42FeWoXPzpd1zl27BixWIz33nuPL795BcnRwr5dQDZtUj6VlWeH+NSx7VTWfD7Pe1duoQhuQlITub9n64FhIRbb5Gp7a4bsB2NhN5GEl+pGHV+yhkuTcIsuGptVMgEJzddjixmGwfr6+g/F8/mvCz+e6elfEyYiHiJjQWqrVcR0HXnAu21TtW27Z6g9W6DqUfCsZvj3//f/SmDYTyAU4B//438M9OiO2etZJFXCFXExcOAEdVcfQ7EAmt+BbdoE8w02Ks3e/NweVQPbhmqhyVSfl4FdKtZTcQ+HEz6ub1SInorjaYUQbGg6JVZMi5GQixNDgX199k7H4M2v3KZRaOH0a9z681mYDGON+5lJVVAViZBbRRZF2rrJSqGBzxtm/CCsJas4jsVwFNoYm3WwAElA7vNQGfMhFlo0rmb543yLBjY4JUCArsmtWxkSfV488RbZ+fPEfUOsON2MHB7HMA0sp8FLR4IPHLS9i/SVNGtvr+EMO9m0WuQ8LjIuBzGvA7dlk3w3SbPVJHJye3W5v78fr9fLlStXME2TTCZDvV5Hczh45oUo9WeHCAb8HBkOMhJ2UUrXuHU5TWaljKmbuPwa0ycSjB+OIe9xLg3DYGZmZstkFREGD0cZPBzdscHOLRewii1kw0K8G3w+IMe13TKi3kWvNrBEP8OJBwe9pmUjAsojGtBevnyZ9957D03T+Cf/w0+Tullh5XaKYccQ/QcP0ageo7kAWdsm5NYe6jkIvY5ztaWTFSyKc0USpxI4/D9+nPj9IDQRIvpBGl+tRbVtbAWeVqNLZ7aI1dSRIq5ta05bN5GA7MYqdjBAai3FEf0Ilmkx/so4rUKL1MUUjoADyavSyJqE4iGKC0XSH6QZ//g4R54cYv2PrpEtNokGXVu0PMuCTLZGQJE58tTgngmEM+jklS8cJv2/vk5tyaYkCwimjUsSOfXcCMf2SXuzTIvcrRxrG1UW5/IoLoXnfuoA8YQH27KZz/Xkv2ttAwHwORWquswnXn5pqzN16olBEgM+lj9IocU8nPhbRzhwOLZNpbLb6JJeKFCtdJAkkb5BH8GhHi3nz/7sz5ifn+fnfu7nOHpMhJ8/TrFYpFotYFnuXWm1RtsgfyuPI+jYkeQ1G01kzbkjcS8WiwweHqSyWqGyViE0GcLn87G6uoplWWxubqLrHyZAp58c5MVXdsqJjx+JM/4IYyimwyT8RBjXTRfFuSKumAtHwLFtfbFtm065Q+ZahpZp0R0PEI+4aTZ3jgfchSqJtASBeltH2MjjCPlQlL2r/pYNsiRg67tTjR/4GUyTY8eOUalUmH5yGrWg4ol7UFwKmk/j9e+9TqPR4Nvf/g7+Q+eodQwSvp0FU02R6PM7SVdaXN8o89xkZEciI4gCoixSz9Z/ohM9gKenohwaDNAxTP5///63+PM//oDPfOYzfPe73+Xpp5/m/PnzBAIBpqamtokFreQbzCwVia5UEJsGcsyFbdo4Mw0Eu8LlkJOTw0EiHo0nnxkmn65x+911MrO9e/zgcyO89KmpfR2jUd5k7Y0/4tO/9A8pNLokfA6ODPh36CDshnK5RWahiDuyO/3aE/OQWSqRzdWp2lBqdnGpEjPzi3g8Hl566SUmhxKo7ibfUiRSyQpUu1iyiCfh4bnRIE+NP3i8oF6vMzo6ys35JTxhlbwWQcm70LoGy906cp/I5w7FmHiAtcT9qLR03lvIEuvXCNUrTEY9OBQJ27ZpNps7mFQHDhzg5s2bKIpCvV6nT2ywEAZ3XSLcAKek0B30UhrwcOxIHwf6djYRFubzfO0/X6W4XunZydgw/946l3+wwme/eJJiaYXh4WEikYd36jVN4zOf+QwATz7zLN+b69L8wQZ+UUT0a7jzTTYupFhIOBl22Ni2zfXr1xFFkReefZq50kzP3qHSQXQrWE0dK+bC+4DC3F6wbZu5ubmtkTP/6BGUo6couGU8+Z7qZvvEAN4hH2dHnKzN3eR7CwusrKzw9//+3ycUjnA1WebGRpWuaTEd83BqJIjP8ejH8leJ/zPRewCcqsQz01G+lqnTupHHccfwXHQr2F2T9kwBfaNOSwHcPsbqCgnlKItXFvG87NmqurSKLRrZBu64m1Kjy41Mk47gYragEw126As58QecDAqwslFFGNzpE2dZNpliA1/L5OgrQ7giO5UzNVni5ekgczevYfRPsWqIIIBLFjme8PHidPSBfi33wrQsjI6J4pDRFIlmqU1TE1kvtAi41G1caOcdOs9mtYXLozHuc7LW6lI+GsbXMpBN6MgCdaeMK9tAvp4jZdu4Ex76XMpWQGTbPeW+xVwDaalE2N/P//R//XuITg/pShtBgITfSTW/ycLCPJOTk3vON3XrXTJXMjj8DlbaBm/PZmnj5laqwqxU42Cfj4mwg+X3lhl9YhRd18lms2QymS0fuuvXr3PkyBHOnj2Lx7N3ZynU7+O5ByiI1WodNtYqBEJO+hJeTNNkZmaGQ4cO7Xr89wcfhmn37CH2o9chQLlUomO0cGsalvDwBadWbhP0O4j171+yul6vEwqFGBgYYHx8nMFDgwwcHOB05wDiHV+xW398i6vNLBuG8Ujceb9LIdfSyWzWGFyr4Dj2k5noefu9hEcCJK61mDF0nKqIKop0lspYjS5S2LntWjCtXke/TxRxDsdINpOEXCE8Qx5yN3O4Y26wexYFngEfP1jIk6+2kJwdwjFPb/6x3uXIc8MUNmtcfGuVVLGF5HVgWxZ2tYNPlnjq+VGCEReNbAPZKe8wUS+Xy7w78yZGcJHXXvpbZNcrKJrM1PE4iQORfXsotkvtnrm21qNTibqIrvdmIBZzDS6vldFkkahXw7ah0uoixiapWRr31pv7+n0E3CpGy2ByLLSV5Jm6ya23Vrn81iqp9SrGHRaK16MxcTiKb7BDrVYjmUyyvLy8Zf4dCoUIBAKsra3h9Xp3SHlX1io0cg2C9/lo1tsGP5jLoAsaqlzncMLHcNiFaZqIooisyoiSSPJKkrpaRxAEqtUq+XyeZ599FlX94VIFa7UatVqN6bPTNEebZK5neh3U2QKS1qPJW6aF2TFx+B2Ep8NUANspgm0hPaQy0xEMLCz6fX7KTplqW9+1S9bqmmiyRMStYpRquOP7D2qr1SrFYpHjx4/TF+sj9c0Udr+N+05gbFs2Bw4c4NDhQ5RaFlcyHaI+155rpSBA2K2SqXYoNrqEd0kYVLdKPV3fs6P1k4SeRZHC9OQ477//Pt/+9rf54he/yMjICBMTExSLRT744AM0TePAgQM4HA7Wi02sQgOx2kUe8vVE1hQQEh4cuSa5TH1LlE6WRT77C8d48vkR/j//y3/gn/zTf8D4xMMTaMMw+MpXvsLFixeJ+/18YheF7YfBMq2tAsNuEEUBw7D51o1NluodrIaO4JBQAgf5tV84RczX61SNhN383adHWMo3KDe7KJLISMi1L1GOr3zlKzSbTXRd55//6j9kZrPGzGaNVtfgiNdBwmnSzs5Riau72qvcj1S5xZ9fTXFzrkJ43Ub0qhwc8PPZEwNsbqwxOjoKfCiaUi6XgZ4yZSgU4uTJk0xNSYyOljm/VCCXbWB1TdxhJ2cSPj52ILaD8dVodPn671+jnKrSdzCKJPe+T1O3yM7l+c///nW++N89RSwWe+jx3498pU67LuLQLeThXiPF7hjI1Q7Fpk5Qr/H2229z+PBhQnfUyE8ei/P9ZAVrrYZcadNwK4RPxpl6ANV+L9xlC9y6dQtJkvjv//tP0BBcXOn3sZLvFboOBJycGAwwHfdg2zavv/46pVKJ3/qt3+Lcz/8jLizX0IotZNvme+kq6+UWP3d6cN/Mub8O7DvRE8WeL1Cz2URVVURRfOgiKAgChvHjObx4F6eGAnSeHub7qkRhqYw730KudjDWqrRLLQphjXrURSTkZFEWSIyOEmu1OagcxG7YzM7OYlUtTMNEkAS6XRvdsHBrMrW2TtewECUR1aNy7plBpHfWWVkpU/aouAIOREGg3TEw8w28dYNjJxPE+zwU5gp4+73b5giuXr3KN77xDbwuF3/35z5BptbGsnsb24N45LvB5VQ5/sIoF745T2m2gC/iwoq4MKvt3YN2AUJujVyjy8SQj4QN3eEgy7UOumnhVSROqTIL76dJCRAfDqDcR4UVBPA6FdyDftKyADnoZEwSh9VtCap/aIhut8vMzAyxWGzXKlJlvUKr1ELq9zI7X8bQWwxHAgSCLuodg9l0GcPVob64yVf+01fwT/uJxWL09fVx/PhxJEni937v92g0Gg9M8h6GYqHBH/3ORXLLJVwBB5/8peOIcpGDBw/u27BZloQ76hIC1p2gcTfoXZ3NTIa+eB99Y/00Cg26+RZWzLun0aduWBjFJpPPje7b+yyVSnHr1i1efvllnnvuua3fC4Kw1cHsNrp0ah1KtoVjHxSde+FQJIr1Li0sOpWPRsf4UYYoifSd6qOcqtIqNlitdfF0DKRSG8m/vevS1k2KjS5eQcCvSkw8eQglrzAxPoFu6TTMBqsXVkkcTPSMxC2bRsega/VeK7pU7GZPJVf1qLzw80cYno5w8/11Zm+u4tZc9PX7iXhUpHyTW39yq3c+nTKhyRChyRDefi+iJHLhwgVmZmYYOTTCqU9OPuATPhjtchujbXD0bD+qW8XpUugf9KEbFvPZGqosEryHAhr2aORqHRZzdQaD29VgVbdKK9+iXW6jelQs0+LiN+Z58+uz1BQR36AXryZj2TbVaocPLiThfIWf+sxPM3lyfCtA2jo3osjo6CiVSoXl5WUGBwe3RG1q6RqCJGxTBjXNnkJgqtKlL+Sg3jH4YK2EUxGp5VJ4vV7yuTxdunSTXQ68egDNqzE0NMQ777zzQ0nyrlzcYHOjyqmnBnF7BCqVypZXoCviYuylMRKnEpRXyzTzTcyOiaRJuMIuAqMBqhtVNlI1BNOg09WR96CHG4ZBvVZDCXtx5QzsjsHEeIAryTKyKPQUne+cmrZuUqh3mI57ceoWps9BcGx/tLtMJgOwdW4C7gDrnfVtlGJBFOgf6M075zcqIBg79pX7oSkS3XqHXH33RE92yOgNHaNt7Okl+ZMGVVUZGhri6OmnKIt+SutlhoJOwqEQTz75JM1mk1u3bmFZFlXCCFbvBG/r+ktCr3Vr9Vgi96JQXKfZXmM9eYvxiRcfejyiKOLz+Wg0GtvGKh4Ffr+TYL+XzGIJT2gXUalCCzviZC5fJ7ZeR8w1waeyOern/ZUSP338w9c4FInDiYfbQbRaOvVaB3/AgSDYLC4uUi6XCYVCGO0mp4eDnL5vjsyaHODmzZusra1x9OjRPfd327Z7huvLBYbmqniEDsTc3DItEh6JxsoM7XYbYIdoSjwe53vf+97WONHp4SCHEz42yi0sy8bvUoh5d48Rb1xJU1orE50MbyV50PPvFILQzhoUcjAy+tCvZwccEniCTmqKiJZrInlUzHoXI+aiUcyw0trkc5/73Fas1Gg0WHr3G4T7EwhTo3SbBtODXp47GN9Xl/d+NBoN+vv7SaVSHD16lGg0ShQYjbhpdU1sbJyKtLUX67rOM888gyRJlJo6F1dL+FNt1PkitmXj7veyqEos5uoc2Qfr7K8L+47AfuM3fqO38d+5UO7+/ycdgtCT60/4ndw8UGV2tURjoYhVbFEbdNPGplNIE+4fRxQF5gp1+iYnqWabBJdbHPj4AWrFGqvfWaUyW6F/epDxiJtUpc1wyE3C76BT7aC4FAZO9/OZw1FuvZdk4fomuc069UoDvy0TlCVCETcuw2bl+ytAj0IVng4TPRxFcAlcvHiRYrHI9PQ0Qbe6LUh6HHzypw8ycSBC+oM0xkqJq4aF8wFBuyqLmJZNRxKII3J0PMIn/RqG1TNkvf3GMhcKTQKjO5O8eyGKArG4l9XNMlffWqLvYGQHjUxVVQ4fPkw6neb27dtMTU1tm4nUGz0aVLmt0+zo0G3hdveGxj2aTLZUY7NT58TkBJOnJxl+duf8ypNPPsmbb77JE0888djX+q1rGTbn8kQnQ+QXS3znLy7zL/6HV3cdpN4LYbeGFHJirFQx6m2UXVRDK+Uy5XKFcDhMQPVgCALBs/04liukk2XiA4Edlc2OYZJfKTMQ9XDqhdF9fcZr167RarV4+eWXEUVxy4fsfliGhW3a6DZIj/HdCQLYgvBI6o0/jghNhBh/cRReX0EpNljPNqk3usguGdGysC2bjmFRa3aw6h1URWUl6ECTBc6eObt1X3i9XlLXU6TWUzTqDQJ2gKfGQ9TbBgNBJ421Kt5B71Z3TtZkJp8cYPx0grXfvICr5CHuCqK6FFxhV68ra/cS9s3Lm2SuZYgcjDDy/Ajnzp0jEolQLBY/0mc3Oj1REbdH49QTH6r1lZod6h2ToHtnkO11yJSaOo2OsY2qI8q97tTd62VztsA7316g5dMYjHm2dXiiYRcdn0ZhERpJNzzA8s/v9+Pz+VhfX0fTNOLxOEbL2EHZbOkm+XqHoFPG4+j9JIsNllNZBn2OLesV3a3TKraw7hGocDqdNJvNLQGFx0Gh0ODbf3iN0nqVbLrMS68N7ipd7gg46Avs3h0x2gbhgBNPvka50SER2hnYNhoNBARUlwfBlgn1e3ozT6KJVE3TVQbZKDcRRRHTstBkiam4h6NxL421Cv1P9D/UusC2bVZXVwmFQttEo2yrx2zYi1LcNaw9C1r3QxAETHN3ESRBFLDtOyyKvyH49Kc/zXy+xR+9dRsxswbYBPxOXj4Y4/hgAJfLxenTp9F1ndV3rrPZKuGxdcRqZ8t2wCy0MN0KStBB+J7Yw7Ztvvvd7yJJEufPn+fcuXPblIB3gyiKhMNhfv3Xf53Nzc3H+kySLHL8mSG+PV+gkqnjj39YyKzmGlimhWs8gFTXe7N3AQ1zo04w6GQpV6etm/vuypiGxRvfmefaW+u0ax08YSeBIZNsNssrr7zCxMQE8/PznDmz01JCFEWOHTvWY0q8+y7T09NEozu7ns2uSbLUQt0s4WiANKBhJmsoEQdvX53j1145u+vroMdSqNVq27rUDkViYh8WFflMvWeCft+aVywU8PhcWKUmuc36fr6mHVAlgaeOxvlmuk5pJo9SbFHzylhxk+PDEZxiENMWECybH/zgTd59911WV1f59V//dRKDQ+imjVuVHis+SyaTXLp0iU996lM8++yzOBzb16XdGhqqqm5ZFa3kG1x4bxVHuYytSUhuFavUxm4aVNs/WnHLvqPN3/zN33zg/3/SMRpxMxpx89xUhNnuHOmayU3LICYJzOdXWJybIZHoJxGPkam0GQh7KC2WaJ9t4w15OfrSUZbfWCa/mSWhwPRkEK/LiW3ZlNJ1YsdiW3TMZ37Oz4mXxnjvL9/jjf98kSOnXsA/5MeT8GxVkW3LplVqsf7uOqWlEmOvjPHcc88xPT29Q70zl2tw7YMNlm9lsS0YnApx8onBfRmWTkxFcFa7rGzUEHX94cqgNgi2ACIg9Lx2ZAn0ps7M+0lamkR4Hx0eRRJxJPxcu7DMs585jDexO60wkUgQjUaZn58nGAxuDSgLogA2yKJAp9NGkmSUO1VzywJZkhkeGMDRlXf17AI4dOgQ3/72t3cMOD8KNEePrlXPNalXGxwaGH6kJA9gNOxiYDTA5mIJNVVGDn+4SVqGSWozjaaqjAwPk95M08w2qA0FOPTsEN5gm2tv58nNGeBT0dwaYNOudJAaOkP9Pl79xWPE7qmyt3UTy7Zx3XOedF3n7bffZmhoiOPHH26GLcoigiSgCNDel5rsTgi2vW8a4I8z+k70oTgVnG+tId7M03CrtOh1iSRAahmYDZNmtwoDYbRhH7czNcJebUs9UZRE3F434WiYprPJ0sUlnGEn8WiY+koFSZNInEzsCJIvf+8ylesV2lqbE8+ewO3dTqtzqk6cQSd6UydzLYPZNREnRU6dOrXn5zENC+khXRXoBdv3C5cACAi9RH+Xy8a+Yz+ya0Bv33lP2+bmxSSVlkHfsH9XGp+mSKh9XpZmc0ye8dMd7O7ZVRMEgeHhYer1OktLS+imviMJEEUBU/+wC2ZZNqIgIEsiib4PxR7uBlr3nod4PE4qlXrszgWApsk4/Q6a1TaCbDzQn2ovuONuopNB4tk6m10Tw/4wQLBMk3q9gcvtQpJkNjeLjGgKIyf6mEvNcuFPLxAeDHF6Okqm1qHdNVFlkahPw2PY1FcrRA5GGHxqd2/Du+h0OqyvrzM6Orq7D6so7JmAqbK4b5E427b3VH20rZ3n6CcdbUvgezNZnHUZ/0oOwbapjvn5HjAUdG0VjRVF4ZNPHWPT8rDWXMS1lMIpab0ihVulMOThwICPodD2osXnPvc5rly5wrFjx/YVlJfLZWRZ3qZQ/Th48plhitkGV19fZuNGtlcQMixUj8LpV6dojXqZuZ4FUcRumyAIWAIoosij5A7vvLnCW1+eQXHKOH0alc065bTNr/zKr2/5eX73u9994HsEAgHOnTvH7Ows6+vrHD9+fNs9IIkCiiTSsS0kWcYstRE0CVMUCHtdeyZ5d9Hf38/y8vKWZdR+IUnijrW4VCyiqhoej4eq3dg2E/2oODMcQvu0xJXpMGsbWaJal888e4JUucm3bq3hSi/iUiXGgqPkSn9JIpEgFouhyRIPCg8K9Q4rhSYdw8SjyUxEPbg1Gdu2uXLlCsVikU9/+tMoirIv2uz98DpkXJpEJ6ChZJsYzSbigBfBJeP7EVPnfOyj+Tf/5t/w+c9/nqNHj+76+M2bN/nSl77Eb/zGbzz2wf0owqfJaA0D2SPTLXYJe5y9WQfLolarEo/HkCSJIjbRRodOtYMj4CB+Ik4z3yQ/m0fSJCqpAplaG4fooP9wP4NPb98AJU3ixls3MDoGVsLCN7Q9KRNEAVfYhTPopLxSZvYbs8iHZJ5//vltz1taLPCV/+MKpbUyiqvn4bR+I8PNd9Z59ZeO71Cc2hUOmbZu4lVlNisd/E51V2GN3iyGiMuyUD1OlHvmNTrVDvl8E9Xv2N+sGeALuCkmCzRLrT0TPejRFA4dOkQ+n+fmzZtMTU3hirgQZZGQKqNYOi3ZhX6nil5sdFExGI0G6abru847Qq/SdujQIS5fvvzYid6JM/0kV4pcfmueA+dGefm1g4/8HrIkcnI4yFdHi5iZElpBxw7aNJoN8rkc8Xgcp8tFs22QW27RkRSytTqL3z2PnL3Bv/g7P0u34mDuyibVagcRSETcHPr0IAdPJ/DdqXRmqm3eu5JicTaPbdsMjYd4+lQ/Pknn/fff5+zZs1s8+Yces6M32xUQJHJdHR6hu9zWTSRRwCmJH9kI+ccF4enwlgx/u9bG6lhbc5nXxQbeMT/1Youy3EaplrCcfnK1zjaZfEmTMDsmh187THgoTOZWhtxmDmfEycQLEwTHt1OGmvkmpSslhkeGCY+GMe3dPUABFJdCcCLIyoUVhqQh2KkTwvxcnvNvLJFZLuNwqxx5aoBnXhhD22M3lp09it/9XRq/U+kpbDZ1Yved/3Kzy1DQtcMixuyaiKqI7OzR7pILRQS/9sAOj9+rUkjVaNdlMpnMQ5Mjj8eDx+Ph8s3LlHKlbZ5/LlWizw0rVZ222Uv0+nwacY+47bPpTR3JIW0TafJ4PNTrdTqdzkNlzPc+No3P/8pJZmfWePb5RzMNvgtBEIgdiTG6UCSz1mCz0pvHlqwupmnh8/todU2y9RYeQWDM0WOh6Ee6rOZWCXgCGOvV3ty1LGFbNt1kDd2rkTiTYOiZoQdSIcvlMtVqdc+EV3EpyJqM3tJ3tT6IeDQkUUQ3rAcyRjq6iSpLRPawTzDaBopb2VNI6ycRG6UWxVqH2HoVq9HFFgS8q1WyQY2NcmsbO8jrUHjt2ABfQyA7FKGSrZBsNBDiMieOxvjk4b5titiCIDAyMkIul9t3knHr1q1tqtf3Ip2qcu1SilqpRTTh5cTZgS3lx/shSSKvff4Ih070MXNtk2qpjTfg4OCxOBNTEW6mKtxO1ehMBXDmWlh9LsohBy8lfGj7FCczTYtrb68iKyKR4R5dzx1wkL6d4/qFja1ELx6P76mWeReCIHDw4EHq9Trvv/8+IyMjDAz02A4OReJov4+ZqJOwV0Nt6NTDDtoOk2cOPVwAKxwOk8lkHjnRG50Mc8Eh0ap1cHo1yqUSsizj8XpolNsoLoXRqf3FBffDtnuiVYfibtqpOZ5/po/E4DBfu57i0vU0xmIORW6Rc0ksBhWmP/FLTKnVB7IfLMvmvaUC7y7kqKYbCF0d3BqRAS8vTITIzF4iFovxyiuvPNYx30XYo3FiwM9bjS4Oh4xo2dR8Cgf6vPvqlP514rFXst/8zd9kcnJyz0Tvxo0b/Ot//a9/4hK9e5OUu1Vnn89HPBbH471zcu2eiuG9z1ecjk790wABAABJREFUCuOfGCc4HiR3O4daU1EGFcSYiB2wKTaK9Ps/rFzNn5/HyBo4+hxkchn6BgdZLTZZLzYxLZuE38lYxI3HIRMYC3DjjRucGt1eZe90DL7xh9epbFRJHI4h3qlg2pZNZqHId/7rDYZGArsukrZtk6q0md2scXO9SD1fp9UxSJkW+XqHwZCLgLMn1AK9m6vY6DIacaG2TSJPbRdlsEwL07AQxf1fcqIgoGoamWyB+OGHD/pGIhFCoRALCwtoqoZv0EdptYS+cYv4+AlKTR0bG7ciMJ0IoGcbePo8+If35lKPjY1x/vx5ksnk1rzLo0BVZQ6cUHn1sz+Ny70/75/dcGIwQOb0AF/L5Ojb6LJ5eQk0icHhBIIp0Nqosb5RoVnqILkFpjdblNcrFIfHEUcGeH6yjyc/MYne1BEEAdWjbkvEM9U2f/SlGxQupnCbPTv725c3mbm8xtRBiy+8+rFHmiMSJZHo4SihuTyqJD4SDabS0okJAvE+L/6RHx2e+181ZIeMO+4mMBFAdfbmzBAE5ueztHULt+Wlr68Pp8tFqtRC3KXkLAgCikth8OlBEmcSPUN1TSKdTrOyskIsFtvaJIsLRZyik+kz0/j8vq05CNO0qXcMFEnEpX14zkRJRA7KNNebtIotnPfMvczNZPnz3/2Axp3fl9M1vv8H18ml6vz839u9++cIOFCcCnpze+AuSQKHEj7eXy6yWWnjc/ZsI6otHZcqM93n3dEV6NZ7PoGOgAOza9LVzZ463IO+b1HEFgV03dynh2kPk2cmac43SS4niSaiyIrM/Pw8c+ff4fQLn0B2+lBlEZfVpD++vcreLrYZOje0bW10uVx4PD3xrpGRxzPq7nQ6dPUKL71y4rFefxfBsSATHxul9Gdl8h1YLlSwNBVFlqmUWqgC9FtgZBusdEQKl9YZPuTh137z16jla3hNL+XVMkbbQFZlPAMegmNBXOEPAzO9pXPjzRWSC0WcXo1TL4zQUVuoqsrw8N4WEKIs4oq6KC3vtKQAiHo0Ih6NXL1N3y6qm9Dbr4uNDv0BF+E9hMm6jS6hqdDfiNGUu7i7ltiCgG3YCKK95Yux29cwGnHzy0+PMj9WY6PcW4vCmoVZ3CC5UMNz8OAOKty9MC2b9WJPZTzgUuj3fzhzu7y8zPDw8K6zavNzeb76e5eopGqIioRlWNy+sMEv/KOzhMJ7i/yMT4QZn9ipkHmoz8eLB6J8r9OkOhjDqYic6fPx9Pj+Exddt2jVOqj3Uc0Vl7JlHQU98/SLFy8+MNG7C4/Hw7lz51hcXOT999/nxIkTPfPu/CLO5gbWobOkGl00WWRUrHN05OHxkSRJhMNh1tfXH6njP3UgwsixPhbeTyJ5wRXQ8Hi8VAtNaqkaU+eGGJ949ESv2+2iaRqbm5ssLS1x6tQpnE4n7y8XuX4zS+R2kdZmHWdcRcubeAoyG6LMoWenH/i+1zYqfOdqCtdMkVihBYaNrYlUNuv87wsr/PJzk5w49MOxRnjxQIyAS+X6RoW2buCvZfjMiaM/UkIs8FeoulksFn/oKmI/ChAEAd+gD/dmDbfWUxq7q9YGPYVE07YJ2QIOn2PbPIKsyUQPR4kcimCbNoL0oSlxtVpldnYWTdMYSAxAFp7/xPOU9BKJgSHeWy6wXmpuDYZeS5ZJlVs8MxGGbovwcJjyUpl2ub31N2duZimslgmPB7aSPOh1A6NjQTJzeW5cTvPcS9srPBvlFm8v5FnO12l2TYIuFfdkCO1qFtursl7rcGuj3Esy3RoBp0JHN4n7HExIMg6PvEORTlIkNIeM3t27a3A/dMPC7XKSy6XZtYWwC0RRZHp6mnw+z9uFt7n8l5dZvrXMf/fiS8hBP9hQ38zga8uoXpXh54YfKPctyzLHjh3jypUrj5Xo3b59m/Hx8QduevuBJAp8/HCcyx/oLNo6wUSCSA1azR4XPCvYbHhkfDWJyLiP4f4Q3Q0Ps8UStzabnBm30bzaDgXFu3j/Wpr8xRRxnxMl0gvgvZUOm/NVzCOHHjpTsRsCowHiCS+xZIVkvfNAHz3L7NFFdatHuY3aAqHp0E+stcJukDUZSe115USfuKVeOhJyc3m9RGxgGFWRKDW6qLK4Q2DJ7Jio93jjSYq0dW3frQpns1lyuRwuxdXz7ww56dLdek2u1uHqeplKS0cWRYbDTo4O+FEkkXw+T2I0QWmhRGm5tC3RO//GCo1ii8Sh6FYHq1ZoMncxyfLzI4ztEjg5/A58Az5KyyUsw6KWbdC+47GmORROh9zcLpVp6yICAsNhN9MxD5FdruFWoUX8eBzF2WMteNwq6eyD50Y6holkgdfrwOPxUKvV8Hofrjzr6fMQHg9TWavQbDWZvzbP2uoaqqJwfKwfSZawTItCsb2tm9etd5Gd8o7OqizLmKaJJEl7GiE/CN1ul1Qq9UPzdUqcTBBJruBd6zBSc1GodjBtELHxO1QsUeDdmo4kd0m9lyS34efYiWniR3p+ofHju/uGQq/IeP4vZnn72ws0FRGxazJzbY2f/2dPE+l/uDS7b8BH7lZuV0VMSRI4PuTnvUWDzWqLsFvb1tnrGCbFegefU+VI3IdlWohs77jalo1lWFuqnn9TMBRyEg84yY34CIu9CnZxyEvU72Q4tHv3xO9UODsa4uy23w7SbDa5ffs2hmEwPT2N37+9WNfoGHztygZzVzcxmjqqT+PYmX5ePZJAxCKTyezZzXv7W/NUN+tbhWuja5KazXP+rVU+/bnDj/y5RVHgxekYemaJ0QMjuDWJPp/jkZJ8h0MmOuxn+VIaX9SNIAqYhk272uHwueF7nufAMIxHuscnJiYYGBjg61//OjMzM9y8eZPXfu4XMbwq5WqbUqaEFXTz+++v8exk5IGdJLfbTSgU4tatW4+U6EmyyM/88gn+U6PC5myZRr1LQ8ih+TSOfGyMn/q5I3sKyDwI1WqVlZWVbXNvhmlxNVlG22wi1roICSfyHXuM7loV92aT66kKT46Fdk2mdNPi0loRebmCY7OOlPAgajJmvUsgWUd3BckaP7yYQpHE3j0w2tvfLlwo41Efn8b6V4VH2lHefPNN3njjja3/f/nLX2ZhYWHH88rlMn/0R3/EsWPHPvIB/igiNBkicz3DpCZxo9ggU7VwqxK6adHoGAwFnfi6FuHp8K6BtSAICPL2hcTn8+Hz+Wi329x47wbJG0lGj49CFtbLTZKlFn0+55agRtClslFuspir49dLjI2PUZgtUE1WtxK9Yq7R46Pv4ukhKT3rhVKhue33C9k637iRptjokgg4GQr1LhHraIxmy0BdKuHzO5nL5TBMF+lyi3bX5JnhIIOCiFMSGXluZFsFF8AZcjIwEmTjWgor7NrX0Hyz2OTggB9HWNkKhPaLSqXCuzfeZa45RyAewGU7yC+WWM1UaRsGwyf8PP/xMfxDD+8YHT58mCtXrpDNZh9JQnhmZoaRkZGPnOQBWJbFtSuXEdI3+Y1f+SesVQ1uJCtbgTGNDpFrWUKdFuFoT0xDjboZ12SKpTZtw9w2c3cvuobF4lwBt2Ejhz88Vsmv4c1JpJZLVNvGHSnu/UPzaT0qWKZOS7TZrLSI+x3baD2tZpNSqUSxVEK3RKKDI0zJMsP9fqIHf7J9rO6HpEqEJ8Mkzye3BZqTMQ/NrslaqUmtbeBSJY4N+LfRGk3dxLbsh3ZA716/q1dWSS2mCE+Fe0EzAh3d5OJqiWpLJ+hS0E2b2+kqTlliLKyhqAqSLKH5NAqzBRKnewqf3a5BZrmIK+zcFjR7wy5qm3U2N6q7JnqCKKC4FZYvpqi0dIr1NtadNowIhHwawYDE6LEhvAkPrj0ooN16F0EWCE/3KvayQ2byVIKlP71FRze3WAf3o1xoEos4GTkQxhPybNkpPAyCKBA7FqOyVsFhOzh+7DilYqk3v3aH7lUoFgiHPuwgmLpJdb1K/ER8T3uB/v5+ksnkA7taOz57t0symXxkOtZesG2btbU1PCMenvr0U9Q2ajSyDYy2gaiIOENOLt/O0r2RZPxwFL1tUFyrkNms7UmfuxftSpuZS2naPpWBRI8KWl4okl1vEt/HR/AP+9F8Wm8kYpciUMSj8fREmOvJCrl6B8O0EEWxp1Zc7xIyBQZKOpVMi7JtI4oiml/DO+DFFXHRrXZxBpwPZHn8JMKlyrx6pI9vCbAZ0sCGmN/JJw7F8D6iJ5jL5eLUqVMYhsHc3By3b9/eVoR4b6nA9e8uEUo1UCWRjmXxQbVNf8CJVFrbkyVWq3XIrpTxxNxbhWtZldDcKmtzhcf+7OVymaFYkMnHkOe/i2c+PkFuvcrGzSyKQ0Fv60RGgzz53PYO/cjICKurq9uaAw+Dw+HgmWee4Y033qDU6LJkhOhe2iS82cCxWcAbCbGSapGvtPn82SFG9/IN9HhoNBrE43EymQzx+N4FmfuRz6f45OdGcWhRNtYqIMDQSJDBfcRPu6FYLPL973+f48ePMzX1oadix7Codwwc1Q6SWwXhQ9Vtyaei1To0WwbN7u4MoVKjS7bUwl1sIQUciHf2DMmjYlU7uCsdlvINTMveFoc8KkzLpmOYKJKIcs984t3z+8Naj39YeKRE7/XXX+df/+t/DfSSlS9/+ct8+ctf3vW5hw8f5rd/+7c/+hH+CMI36CNxMoF5IcnJgIsN3aTaMdAUiQmfg0jHIjwSeGBlcy84HA6G+4dpBBoUq0UymQxNn4oiidtUEwWhx5WfXc/y0yeGsC275//2donqtSrdbpeg9yhg7zDvvQvbslHuuVnWi03+4lqKZtdkMubZVtUSVQnX2QSiU0FYLhIq1OgfC2EC5UqbwkqJyVP9TDw3QmhyZ1AnyiJHnh7kxtU0xXqXyENmr+odA7VpMH0qwcBhP2tra49UsR4fH+fo0aMkk0lGJkfIjxlcbdoUC1W8bjdXszXI1fjskB/5IYPEqqpy5MgRLl++zKuvvrqvvz87O8vQ0NBHUtK7i0qlwltvvcXU1BQnT55kJB5kJA7PjIfpGBaCANeSFb6erCMvga33ZhHNWpeuJBJ2q6gP+Yw9UQxhRyVTAGx4JGrbveh/op9uvYv9fpIFTLLVNqIg4HMqiFjMLSyRLZaptnT6omEmRYmpqIfRj43g2afdw08SgpNBNq9uojf1LWqtIoucGQ0yFffQMSy8DnnHJtfMNXHH3fsqXAC4VBfBYBC3283K6gqmadKSXFSaXeI+J6IImgJd02Kl2CQgNnE6nGwkNwi5QhhtA0u3kFQJSRJRnSr1+4pGRtdEEIVdZ/Rs22b1wgbf/9YCyVwdo6XjGPThuNOBMUybVKMDSw0qhSWOnR3CeXin+q5lWlSTVRJnEvgGP5xjPnS6n+vvrrO2WiY2GkS9d2bLhkKljVRqc+TVqcfq3gTHgww9O8TqD1apbFQ4ffo0mqaRzWQRBBdXL2bw+ZocOt6HQxaprFYIT4cZeX5kz27BXcsiy7L2VSHXdZ319fVHChofhFqtRi6X26LNiZKIf9i/I+kJllpIikRhvYLeMnD6NILB/dPSbWwQep/v7ldh2/sTUXGGnATHg2SuZvbs9kc8Gi9OR8nVuxTqHRqZOmahjZGrodoWukvBGfQhiiK2aVNL1agmq2h+DduymX5tek/mw08yxiJuvvj0KBvlFmDTH3DuWRzcD2RZ5vDhw9i2zfLyMteuXSMQDHFrsYMn28IRcSJ5NYRCi9ZajUvzmzwVEzFNkytXrnDy5Mlt76dpEopDoV3bbrljdAzcH2GWe2VlhYMHH312/l5MH4zxC//sSa6cT1LKNYgN+jj91BDxvu2Fo+HhYd58881HvmdVVeWLX/wi/+lb71Pe7DKy2qLVbOHpCyCbEFquUhDh/YibkbBr1zXG7XaTyWSYmpri/Pnz+070VlZW6Ha7W9/R8Mj+7FF2g23b3Lp1C9M0OXToEInEdo0IRRJRZZGGW0GutOGepdlq6phBB6oqou0xg3s3VhH2EoL4iEK6bd3k+kaFq4t5qqU2mlvl2ESYE0MB/E6FWCzG0tLSj3ei96/+1b/in//zf45t28RiMf7Df/gPfOELX9j2HEEQcLlcP5Quxo8qBFFg8NwgkibhvJElWGphqCqSAKos45/wM/zs8ENlpB8ESZSI9kVpt9rMlNvUam38KmiODxc02zQxLQun08kf/OHvU5wr0rjaoBHoVW0OHxIRNA+VTI3gfR4wjXIb2akwdqBXde4YJt+6tUm9YzAWcW8tFGa9i5Gq092oYndMRI9Cq0/G5YzjCgawDQvXRJCULJA7HuXJXZK8u+ibDnP4eJxLl9OURYHAHsPwra5Jda3C9EiA6TP9ePu8vPvuu4+U6F25coXx8XE+85nPcPz4cbJSgtq3LhHzOXDHvbQ3m9y+kOKJicgOhbB7oaoq3W6Xo0eP8gd/8AcUi8WHCpLMzc0xMDCA2/3R6T+3bt1idXWVF154Aa/Xy+Li4tZjPUXT3oI3Hffy/RE3GzMS4moRwdvFcCh0JoKcHAs9MJlVZZHxqQiXPkjjLbSQw72gzax1qNs246OBR+7m3YWkSIx+bBRRFXFd3iRXbpEVbIpdA8sG1RNALlRxbqZJ2DZHP/E0oy+M7los+JsAT9xDYDRA7naO0FQI8Z7z5tvjHHQbXbq1LoNPD+6Q/X8YVE0lEo7g9XnZXMvSbLWwPCqi+OH7ZDY3eePGLKIgMD4+Tui+eQxJEjn85CA/+OMbNMpt3AEHpm6SWyoRGPAxfXhnZ3bj8ibf+q832dB1Amf7ERdLmHUdKeREEECVQLRERK+PXLnNlXfXOC0LxA6Et9Ymy7QoL5XxD/kZeGJgWxLoS3h59W8f4xt/cI3N+QK2T0V1a1imRafYwmXB2edGeeqnprfeLxqN7rtrLwgCidMJcoUc4oJIfjFPfCSOz+vnz/7wImYZMkaJ0kKRJ18cJX48zvDzw9vmYnfDXU+nwcFBbLtHIxQEYYcysGEYj9wZeBCSySSqqjI+Pk63u7cCKcDJM/3MfmyA9GwDV8DBC68dIBZ/eCcUenTdA8cTFL63yEari9ixGBzwMTz1cNrmXfSd7KO8WqaRbeCO7b7GiqJAzKMiZ+sUknWazSbJappGp8HkxOS2RE7z966LyloFS7do5pt0ap2/kcmeU5U+UmdrNwh31o1isYjD6SS5sYBSq+L1Re59EleuXuX2xgUcDseWufe9UFWZw08O8O6f3aacruH0adRyTSRN4ujZAR4XnU7nhxKvDo8EH5oESZKEpmmPZKdiGAa3b9/m0OHDDBwVsG/XEHQL0y/i9Peuf8Oy8efbrGbrFBrdXT3l3G53zx5FEAgEAvuKZdbW1mg2mxw+/Oi02PtRr9e5evUqBw4cIBKJcOnSpR3fgSqLHO338d1IAWe+hZWpYaoerIYOgkAl6uTZPh/uPdgdIbdKLOgiG9LwrtYQXQqCKmE1dWzTphnQOBx2P1Y3r62b/MWVJDfeXEVNN1B1i5oo8N1YmoWnhvj808MEXOqWuNZH8V/+YeOREj2n07nlm7W8vEw0Gv2hdCx+HCEpEoNPDxI9EqW6XsVoGwiSgCfuwR13PxLH+37IDhlBEjC7JoIgMDUYodgtopsmrVIJRVVxudykCxWemEogSQLVchVd1ynWikgeiVKpRCa7RMeRIHXLRF1T8EVdaC4HdgP0usWRF8eZvLPBLuUaJMstRsMfHrueqtG6tIlRaiO6ZJBEzEqb6sYmgak4jiPRLQ8do9FlrtDkXKO7p3+f6lZ58QtHMLomN29m2fAqeIJOXJrco44ZJtVyC6HUYWLQz8d/4SjeOxUxh8NBu93e14K8ubnJ7Owsn/jEJ1hZWeH48eP86YUN7EYLd38YyachF1oYbZ2O8eBK8t3FMRgMcuDAAa5evcpLL7205/Pn5+fp6+v7yDd5s9nkrbfeIh6P86lPfQpBENB1fc9ZuZBbRczf5La0wbzoYCLqYehggmeO9nFqOPDQv/fk8T6WZrJkL23iLrYQgAY2rsNRzj0x+JGuZ0mVGH1hlPBkmMJ8geJ8kXKhiW5ZiP39pDH5fnYGok1iR2PoLZ3c7RyuiOtvzKyMZVhYpoWkSIy8MILe1CkuFAmOBx84Q9qpdaht1EicSRA7un9asaT1Zn0ts3f9S6LE1HAfyWaWZL6KVxWRVI1m12KqP0h87Cyzs7PIskwulUP2yGxsbvRmln0+zn1slFy6xtKlDSobVQQBAgN+PvW3juG5L+holVq88405NjpdooP+niz+dIjOXBEz30QKaAiKBDaoskQg6KJYbDLzQQp/3IMj6KBb71JNVvEP+Rn/+PiuQfnQ0Tg//0+f5Ob7SeavbFKrdZAkkcShGEeeGmTiZGKbsqLT6SSXy+37O6xUKgyeGUQ7rbF8eRk7a1NJ16BkoIgytiqiexQO/eyhLbP5h54XSaJZbLKR2aA4W8RoGyD2DM8jByL4h/0IssDKygoTExMfWTCk3W6zsbHB4ODgluJno9F4YJFKkkRe+PgIfb/cjyDwSNLqgijwzOcO4vQqbMwXcXhVTn9snMDA9kKkbdvoDR3LsHo09Hv2FHfUzcATAyx9e2mHqNS9ry8uFsndyqF6VKLxKE2xSX21jqzIWFaPwyAKdzz1uiayQyb+RJxqssryd5eZ+OTEQxPz/xOPhsH+BJ98QeQ7m1fIzKVRZAmHx00pofFTz59ErUR45513EEWRixcvbs1ier1ewuEwz398nG7XYOa9JNVMHVfQxdmXxzl55vESvWq1us2r8a8Dk5OTLC4u7nu06dKlS5w6dYrVtXU0pxujU6Gpt3G7P+y0C6qE2DXRdRNjD3/Iu7PAAFNTU1y8eJGnnnpqz7+bTCapVqt7UmkfBQsLC1QqFZ566qmt+cS9mAsnh4IsHWqwZFpYMw28XRPdq1KJORk8FOPMA5JpRRI5NRzkq+kanbYJ2SZYNoIiUuv34BoLcHzw8eimN1NVrr+1RnC5iurXkMIqVtvAtdFg5a013ou5+dTRBJOTk8zMzOwoVPy3xGP35R9XHezHCd2mzuJsjkati6pKDI8HCdxX7dK8GtFdKtYfBZ4+D564h0auAcBQwMVGsMV6sYlTddHVDZaXU/hUgYlYLxH61Euf4p0330GKSywkF3C5XLjdbuJxkGSJ0qpJfqUCVJCcIv5BgbpZ52tf2yAcDnOl4kAUHFt8Y6PYonkhjdU1UEZ8WwGFbVm0qhaxikXzYhr3s4OImkzApTC3WWM+W+fJsb2rRJ64h09+8RR9bywzd22T1HqNu7bLMjYDYRcTz49w6mPjBO6hDE1MTLC4uMiRIw+WDu90Orz++uu8/PLL5PN5otEoqqoS8cuYcQ/tXBO50KIqCfgGvIQeIvvvdrvJZrMEg0GOHz/O7/7+n9AOrpJvg2lZRDwaB/t8jIZdrK4sE4vF9tw4LMumpfcWWqci7TmnuLS0xK1btzh37ty2itvDArBf/KmPMXf53yLGovyDLz7NYNi3Q7BjLyT8Tn7h54/zzkSYtfkCtmUzNRrk6bMDjN8z4N1tdKmsVXrqnXcsPrwDDw9iDdvmG+fXyK6Xef6FcU4HnVi6hSAKtCuTeIYcdPImF742j2HZSALEIi7iU2Hix+P4Bv56N+O/DtiWzdpcnhsXN1ifK2AYJh6PxtSZfiZPxRFlkdJSCUmVcMfdW0Gnbdt0yh0a+QaiLDL45CCD5wb3lUjchTfhxRF00Cq2QOwFuw5F5OxIiKuSSLml0+100PPrHDhwinDQTygUQpZkOqkOY+fG6B/ux7ZtarUaxWKWcx+PMDylkc+2cHsdnH1qnEBg5/W6OZdnbb2CM+HeolSKPg3tcAR9rYqebYBlYysWtmohSyIuv4N8pk7qZpZA3IOoiPSf7af/bP8DOy/BQT/PDfp54hOTGC2jRyX1a3smz5Ik0Wl06Fa7PcVSRcIZdu54vmEYlMtlRkdHSaVSHHrpEIIlsHR7iaMemdvnN5A0mbNfOLxvOq1t2aQvpym/X2ZxY5HoUBTZ0fN9Ki2VKM4Vcfe5YQSOPnP0Iyd5m5ubmKa5oyvYbDZ3UKp2HKttI4o8lgiD4lJ44qcPcnYXQRXLsCivlinM9ebN7yZ6geEA4elwL9EVezYQrWKL1MUU/mH/1r1hmRazt3JYtQ5SvonqVreuj6GhYUpNg2TdZrlWwgZUSSSiSjg6Fv2HogRGAz0hnbkCslNm4hMTf6P89P468NR4mPyrB5mL+2nX22y2KkjaJk+MP0kscojx8XHq9TonTvQUZO+uMYVCgbW1NfpGTDwRP2ZXYmQsztDQw1Us76La1rm5UWG12MTvUBCqKV48/dG7VY+CWCzGtWvX9pXoLSwskEgkcLlc2JZJn9/DrFdBnm/jU3pFetu2McsdOnEXXo+Kz/nwsF6SJNxu956JbiqVolgs7ssz90HodDpcvnyZoaGhffuE+p0KP3NygHd9Kt93daj7IzgcCk/1eXlyLLxrt/JenBwMUGvrvO9RyaVqCF0TXAqhQR8vH4o/kMG1F2zb5upSAXWjjurTkO8IkUmqhCCJeItNbs3meXYygtfhoNPpUCqVsCyLcHin2utfN/ad6I2NjT3yxiIIwjaq2Y8LLNPive8ucvWtNfLpGqZp92bigk4OnOzjxZ8+iOcj0DIfBkmRiB6JsvDNBSzVQlVEnhoLEfc57tgryDTTyxzqi9Is52mUbFy4+MV/9oss2oukUikEQeDll1+mUqmQn8iTSuVJJ2vYCHi8Ng6ngI3O5uYm88ksF6tuJgb7INpLHLsrFcxaF3Vk+yJQr9dxe70ofT70jRpGpoE67EcUBLxOhavJMqeHAw+kCjpDTp76/GGOf2yM1HyBcqGJbdl4Aw4GJsO7dkR9Ph/VavWh3933v/99Dh06RDwe5+bNm1tqgwm5xVOfO8rtCymMto5vwMunXhh/aKJ3l7ppmBYXNlqsK6Okf7BC0JAQbJtVh8yluJuQU+ezp0d2KIxBb95wdrPGtWSZ8h3xlLBb5diAn+m4d4uG0O12eeedd3A6nXz605/eIT5Tr9cfmOgNDg7SHwtz6NABzoztv7tzFwMBJz//8iT1Z0exbRuPJn9IkzMs0pfTZK9naZVavRfYvdlL35CPwacHH5iMpXIN5t9ao75R5UbUjT7dM3V2tB2sX0zRLPvYVGyaVk9F1LbAvVklvlFlarnMgU9O/ETROfW2wXe+fJOrb63R6ugofgeiJFJMVVmdL/BB3MPHPnOA6SNR8jP5HmugYyDQMxnXvBqJUwlCkyF8A75HDkYVl0L0UJTVt1YRIx/eq1GfxksHYtQ6Bt1Wk6/cfJ2lBTf60BCapqHYCvh6MvzAVkfvbrBwt/7XbrcplUrUasWt9xZFEZ/bx+33N2hIArH75O1Fl4I2HUaOuamt51HaEmalC6aNYttUOxap5RKTL40SPxrHO7DTZmEvPEhx9i7a5TZW0uLdP34Xh+DoKSPLvWJG7EiM0GRoywbi3oH7uyp6lmWhBTV+9h8+wZlPTiGKwr7nWURRZO29NVLvpXAEHYj9TrKyQKXWRhIFokEHYU1m9doy/a1+GuONB3qLPgi6rrO2tkY8Ht+VefCgGcFmvsnv/+7vk0qmCIVD/PKv/jK+Ad8jFRnu4v5zZ3QM1n6wRuZ6BkEQcEZ6CbbZMcneypK7nSNxKsHQuSFEWWT42Z5oTfpSGs2v4Y65KRVb3Hp/HTNVpb9fRfJJDPuGe4JT2To12Q+6jdazcKRRbFJpG7hHA0QTPcVESZTwj/gpzBaIH4vj7X+87/nHHYZhYVkW6keY09sNHk3mC08MsT4Zod4xeOu732T55m1mbo2zpKqMjo5uKz7cu8bcO77R6XQoFAosLCzQavX2JFEUCQaDhEIh/H7/tmus3jH400sbLCbLOOs6XUWgbFQZHtc57Hx866PHQTgcplAoPDAJqFQqVKtVTp8+vdXZPDrg53JQJDAURE/WEKSeHYbtV6kNeHhyMLDvucoDBw5w5coVnnjiiW2/39zcJJvNfuSOVDKZJJlMcurUqUf2Bw26VT51JIGvmWbq0CQORdr3+MhdJdVDCR8r+XsM02MefI8oLHQXumlTrbRRDRsxuP09RLeCkrVp1Ts0uyb5dJJ33nmHP/uzP+Mzn/nMlqLof0vs+w5+8cUX/0b4ytiWzXe+fIvz35zHdqkERv04NAXdtKgWmrz/nUUKmTpf+LWzuP8Kpd9DkyGCC0Eyb2ewhi00ReJAn5cDfV7SqTQJM8ytWzewuuOMBcZoSS26oS4vHn2RarWK3+/fsYl3u12KxSKFQmHrp1gs0ukK6JawZUJstQ309SpS4J6b07K5duManXaHwaGhnr+OLNJdq6De6bx5NZlaW6elm3j3sfE7Q04mntq/ZUEwGKRYLOJz9wKL++dWbt68ia7rW5XAVCrFSy+9RKvVwu9189mpBE9MROgYFiG3+tAk7178YCHPm1dSDKZByze3zKy9lk11PkNyKsJbkQY/GwrivGdOKlNt8xfX0qwXGzhVecsTLFVps5hrMBZx8dqxfvRagQ8++ICzZ8/uWU1vNBq7BmbdrsHtG1luX0kTkp6luKzwx//HZQ6eiHPwSHyb4M5+4LmP/25bNuvvrLPx/gaOkIPQZAjd7KlW2UZPTXChvMDkpyb3TPZiISdDJ/soRFyEYgLf+c53iLgiHHOc5NpSiQ0FPA6FuKYgimBZUO/oLLR1WqslhO8sctSr4on/6PDeHxe2ZfPdP73Fhe8soiW89AeD27yqDNMit1bm239yk8/9g9Mc+MwBGtkGelPvScLLIs6g8yPNAENvjcneyFLYLBAMfpiQSJJAwKWQLNYIBUOsra4RDAQJeAOsX1sndiKGFnjwxu1wOHZcx6Zpkl5Is7q4ie3Rdp+TEEEKOZCVAG6HE7ttbokL1ZttTEEkfuyH3+GtblRZeX2FWrqGbunEpmKIkohl9Ga2Fr+9SH42z9jLY1T0ColEYsd+mEql6O/vRxRFRh/AatgVFVh5e4VAPEDetriaN6l18iiShG3b3E6Diy4vHRtFTzdY+8Eahz5/aMca+DDk83mazSbj4+P73s9t26ayWiE/m6e0VCJaipJaTBGRIsz86Qzefi/Rw1HCU+FHnhG992+sv7NO+nJ6W4cOekUJR9BBp9Zh4/0NJFXamkcdeb6n8Lzx/gbFuSKOsJOhkSCNtkm6vIZdtXF7vWTaEtlam4BTRZIE7KaO1dDRXCrKwQglp8zF9TLPqBJRr4biUjB1k+JC8W9cotdodPn+t+eZvZjGNCyGD0V48dWpHcIij4Nstc1Crk61pePWZKIOm7hHISnLXLhwgddee41Wq8W7775LPB5/YINB0zT6+/vp7//Qf9iyLMrlMtlsloWFhS0RMbfbzXpbZXGjQWKxjJluYKkCtYTMu4t5pmKebeqJf9WYmprixo0bhMPhXa1CTNPk+vXrnDt3ridclcqgeQKMxL1MxiQ2vQMU0nW0lomuiFgxF8emIjw5tv/ukaIoyLLMSqaEjtxLpNpVUqkUp0+ffuBrc7UOa8UGXdPG55CZiHq2BMIMw+DKlSsEg8E9bTIeBsuy6HQ6CEaHgCagaY+eoMW8DmLeB++Rtm2Tr3cxLZuIR92zQSGLAm6vSk4UsFsG3EP5t1oGhiigOlVymynCXieyLG+N/PwoYN+J3u/93u/9FR7Gjw4WbmT44HtLqFE3wciHLV5FFgnHPbQDGkvXs7z73UU+/vkPaYSWZfGD7y1x68IGbr/GS68dZGgk8NjHobgUxl4aY3VljdnLaYrYmF6FsEdF6XTQNA2hI9BcayIPyTz12lN4+71sbGzQaDRwOBw7kgJVVenr69tm2GlZFu/Pp6hc3WRk+A4VoGNi6yai655gThTodrqUSiW63S5ejxeHJmPV9a2nSKKAafWqHz9s2JaN3/Dzvd99B48aRlEkxo7F6DscwxVxUSgUuHr1Kj/zMz+DIAiUSiU6nQ7xeJyVlZUt2sDjtO2LTYOL6wV8i2XUTBO539ObIQKqlSqOooAn2WIuUGA24efkUKD3WFvnL66lWC82mYh5EU0Lo9hGEMAfcmKKsJit8x++fp4nIxavvvrqA0UQGo3GVofyLm5e3+SNP58hv1oGy0Zxyth0yC2Uuf32GtGxAC//zCEOHHr0Dt9dVJNV0lfSePo9tGWR88tFsrUOiiwyFnYxNRqgulIm+V6Sgz9zcNfqvlOV+bt/9xRd0+LqpYsEg0EyVzJkChfRp8eJep3blBFFsSc+4lQlUtU23tUSiZnCT0Sil1ouce3tVbQ+D8HQzkqyLIn0jYbYmM3x3rcXGT8S26ZAOjuTZfNigZGJ0KMnFPfAFXEx8uIIhT/p0eR8gx92BqvVKn6fn8HBQSKRCEP9Q1RWKkw9PcXwS8Mkk0ls2yaRSOzbL1WSJPxeP4rq3PII3A221VOSFRRp6z4DUBQBo9TBeshc7aOimW+y/J1lWuUW4ekwSlXBtMyeD6Eq4e334o67KS2VuPkXN+l/oR9Xf28d6dxZi23bxjTNR/bAu4t2qk2r3kIdDPHBfBHDgsGgu1cAsKFQKlFD48pGhaeHglQ3qlSTVQKjgW3vU693uH4lTWGzjsujcvhEgr6EF8uyWF1dJRgMPpJ9g23ZpC6mSL6XxLZs1JCT4PEx7OIa0VPTKKpMI9ugslahul5l5IWRx5pra2Qa5G7l8A54UVy9Ylim2qHV7VXjo75eR9aKWmSuZYgcjOAIOBBlkfjxOJ6Eh8y1DKWFEoG2iYFJwB3AFm1m5pNYrig+WYRKG9OwEZ0yyqAPJeFBdCvEgHS5xexmjYhH7YnKhV0U5gr0ner7iRBmaTS6LMzmEQSYPBjB5dz9vv3KH15j5q1VHP7e93vj9WXyG1W++C+e2TFvu1/Yts3NXIfXc8vUk1WkjompiHQ9Fgeig4Qjqwz0J5iamkKSJMbGxshkMpw/fx6fz8f09PS+7i1RFAmFQjtERhqNBuffW0Sqd7AyTZQBL/nbG/QnBig1dHLlGqXNJDdv3uTVV1/dlZnzw0TLVji/VuVKZwHTgtGwiyP9/i1rhMuXL3P8+HEWcg0ur5W4trBOKBLBN1vg6GgfHwuHuJ2ukat38DpkDvX5mIp70OQHF1pkWd5iIHQMk1U7zJe+P4/kcCFaOmGhwT96be8OlGnZvDWf48JqiVpb71mFAX1+B584FMdLi/n5eU6cOPFA9lG3232gN+/m5ib/8T/+R/L5PLOzs/zKr/zKg7/Qx0Cp0eXb19MszeexDIv4cIBXTvTvak8higLHx8N8PZFGX62BJCB6VOy2gZ5tUA1qRJQK3/zqtxkYGOBXfuVX+B//59+kaiqkKy3iXse+LMX+qvBXZpj+44obFzZo6yYDkd0TAoemoIQc3L6wwbmPT+C6MwN1/comb37pFoIA6RmdRqXNr/7fnvtItAdHwEFm0s/a5Qb25QxCrcuqQyQ0GGAspPCxFz6GklA4/srxLd+6u6bed28Qj8ezIzm4F6IoEgr4cToqCHfpOpLQ+zG3B1S9AF5genoaj9eDkW0g3GMOad6Z25BEgUajQTqd3jcv+0GwLZvlt1Z5+xvzzG82EAMCogXXb2U4cijGmc9M8d3z3+WFF17YEge6sbCG4YmzmK3ifETawP1YKbWpp9rE8k2kvg+TvHqtjqIqOEa96GtV5FyTa8kyxwf8iKLA7GaNtWKTyZgXu9CifjmNUWiBICCHnThP9zER8zCzYRCdGnlowHw/dfPqpRTf/C9XaNe7hIf9qPcFWN2mTnaxyFf+4we89vdPcfjo/mcZ7kVhvoBt2AgOhffnc+RqbfxOlY5ucnmtDMDkgJdqskpto7anB5UoCjhEiaeeeorTx05zVb7G24t5mqq8Xf7+HiiSiEuVybQM0rezvaDrI0hp/yjg5qUUzaZOYnTvap8ggL/fR3KhwPpCkZEDvSLM7EyWP/udizQKLXwJD3/7//L0Y3sZAYSnwgy+MEhrtkXq2ibFagc14MAdsEj0xTk+fZxCskBmIcPI6ZGtQH7YO4xt26TTabrdLtFodF8qs4IkIEtCryO+C3LZHJZpEo3vLExYdo9CPzc/R2Wu11X7YQgFZG5kaOQahKZDCIKAP+Anl8sRjX44ey1KIv4RP0sXlhg4OgB3mGW5XG5LKfPezsKjoFPtUF4uowU11ooNGl2dgeCd/ceGSrVCKOBHtyBdaVOMmyj07st7E71crsGX/vdLZGby2NjYls2lN5Z5/rMTxBISIyMjjzxTl76SZu2tNRwhB2nLYjFdptTQsfsO8tZiAbcmMxp2M57wkLmeAWDslbEHCgjthuJiEaNloA337u1bqRq30lVMy0KRRE4MBpi8I8RTmC1QWi6ROPVhx9gddTP+yji1wzWq6SqqR8W2bYyOyXymRq1toDgVxJAT0af1PLbum2UKulUy1TbFhk7Yo+IIOiguFKlv1n/sE71yucV//Y8XSd/OAwIDR6L8rV89i/e+tXRtpcTylU38/T48d4pQ3rCL3HyRm1c3eerZx9NmmM/WubTWZrCYIZZtYZs9NdlO2MmK5OC1n/ll3K3sttfE43Hi8TiVSoXLly8jyzIHDhzYIQCYq3UoN7t4HPKeZudut5vxoT6WsxuIXhV9vYZlGZgeDcHq8v/8n/8nVElAVVWefvpp3G73YxdtHoa1YpOvXk2RleOEuiaiIHBxtcTNdJWPH4oTtquEQiHmSybfvLqBsVxGWcog+Fqkwg5yTXjV5eUzJx59vbkrLuf3+7m8VubySpnYpoGdXicrd8ifmeBasrxlAH4/Lq4UeWMuR8ihEG6ZoFvYXpVUvcN/efMWL486ePaZZx7KFtiLnXQX/f39DA0NUSgUHih+97iwLJtvXE0x+60F/IU2gg3J2SJfbep88eNTu9JEj/b7mT83zJyxgjvbQim0MGWBWthB4ulBJj0V3rhYJJlM4vAGOfaxv8dffn0DxZHh1LMjfPLUwEfy7vsoeOwr+c0339zX81544YXH/RN/7TDaBusLeRwP8QTyRlxUV8qk1ytMHPnQnNxoGwwcjVErNKlmm9TrXUKhx18scrUOt5MtIjUD51AAq2tgVbsUHTLaCxM8/fwY+Woe0b1z845EIkQiEWq1GrOzs6iqurXR52odri4XaLYMhvt9BJwKqizS1nsmlKJbQYq6qC6V0CMfqpPJmovpA2GisVhP+rtl4Dz2YZDZ7ppUS0X+19/6c2xTZ3p6+oeS6FXWKrz3rUUWuzr+iRgeh4JhWRRqHa7cypJKr3LgteGtJPdmqsIff3MJsQLJtSs8+fIU/f3WQ/3y9kK2YaA2dGwTRO1OklevI0nilgqt6FJwlzrk6x0aXQOnInEtWcatyQiGReODNGa5jXynM6On63BpE89Lo/jcDq4ly5wYDDxwIbhXkjmXrfOdP75Bp2nQd2B3aXLVpdB3IEJmvsA3/+gGiQH/I/ldQW82r7JewRF0kKm1KdQ7JPzOe6pTXZYLTaZi3h7NrdDcl9lwq9SikGtQk0WCD+HNex0Ked2kmG3SLrd/7BO91HwB0aPyMOacx6uxuV4hnaxuJXqZZJVGoUXiUITN2TyZdO0jJXoAvlEfkdF+3v5f3qZ4eROzbRAb8uN6xoHqURl7Zoym1mT8qfFt3VpBELaSm3w+Ty6Xw+fzbVXTu12D5HoFWRIZHPYjiiKqR8UfcGKlKtghF4LQK+TcLSo16w3yhTzFUpGDBw99OHtog17tUKlm+ebrHyA4BV566aWPrJrXrrQpzhdxxT70nuroJjPZFreKOSIejem4B0UWyRfz9I/3k7+VJ34sjuyQ2UhWyaRtVGeLgYHHm//oNrroTR3JJZPKt3HfQ1Oq/v/J+88YyfL0vBf8HR/e2/Q+s7zpam/HdI/n0A4pUqQoUnu5l3sXCwHS6tNKgBaSLkBd4QK6Eihhl8KC90q8NOIMOcPx0z097avLu8xK7yMyvI84dj9EZVRlVWa5meFMDx+gCmkiI06cOOf/f83zPk+1SsDvRxBFVLHrL1hqdBjwqrQKrT3P89Z3Fti6liU1E0dWJWzbIXuzwA++tsD/9P/65CMnec1Ck62zW2ghjaWWwdXtSle8xKchSwK2DbWOcWv22MPp/gA713YIDgeJH3o0gbLaVg3V3y101VoG8zs13IqI361RbhjcyFQZjLjRFAnZLVPfrsOpe59H9ar40360aQ3FrVCr61y9kcGvSLi9GtznFLgUiUK9Q7mlE/WpvWvP0q1Hei8/jTj37jobV3ZITkZxcNi4nOHc++u88ure/blcbmE0DSJ3rCmy2qUPVyvtx379yxtl2G7gLkhIaR+iJuGYNs5GDdUjc2WwypdOjbC4uMjU1NSevw0Ggzz55JO0221mZ2fRdf1WsTnA92/mOHd+i2axiebXOHQ8xaeOpveMT+ziUCrApUSZHcPCW27RbIlYCS+fnEjye//mX/Gnf/qnVCoVHMfh8uXLPYVKv99PPB4nHA4/lvjQndBNm+/cyFJs6Mz0BXHyLRzLIRH3km3ofPvKJqf8dZ46c5qvvLWMdK2Af6tGw7JxGTbqYoVGy+JNl8J43EfI8/DjJ7A30bu6WcFT7GDd2KFmNkl7gtQKba5uVfZN9NqGxfm1El5FxDVboDFfBEFAdEn0n0mzonmwfImHooQ/SFgO4IWXP8bVuUWGhkce6T0+DLarbVZmc4Tybdx9PgRZRF6rUriRY+lEmlND9xZhvZrMz58Z5FzMy+WbeRqVNqpb4ZnJKE+ORmkWM4R+/uexLIv3l6oUzq0SadmYls2HTYOJgSCTD2lD86PGY2chr7zyykN9oLs3y0cBju1gms4DB8slScR2HMw7KETpgSCugMb29Z0ur/1kmuBDKh4ehM1yk06+jbvjoI50F16r3MbdMljTRHDJxFwpNtZWDjRo9Pv9TE9P0+l0WFxcpNK2ePdGm9z7W0i6zYUBHy9/YYZ00MVOrUN/2M12uc2WBEamhr1dw3HL2AKIgQSOT6PaMnBXOsghF3Lf7apMqanz8vFx/KMyb7/9NoIgsLy8jCAIOI6D3+8nEok88mK5dSPHVrGJJ+3Df6vSokgiqaCbm6Uq+kqVn+ufBqDWNvj2G0uY57KMJxPQaHD+u0uMpQMc6Xu8gNixQbglgwHQqNeRBBH3nZVFUQCrmxQ7QEO3KDUNgm4Fq9DCKra6lM9b15aS9mFm6piFFsGASqnRnW28ez5uz3E4Tu/cXT63RXW7Rvrw/SmZgigQH4+Smc1x5dwmL33y0RJvx3bABkEWusP5jrOHgqBIIoZpY+12aB6StetYDpbpYMMDKQ2S2D3zlmP37AA+qnBurRui9OC1UxDAQcC64z2PTEQJ9vvJzOWJDocZuou697jHtLxWplBuE//EKO1Ci3K5TfiFQUZn4mh+jWq1SqlcOlA8YLewVK1WWV1dBSTe/MYWK5cySJLIsZeH+dwvHkH1qkydTjO3VKRWbuFumZjZJlbHBAeURhPVsRgaHdgjMNM0LLSmxatffIbwMY1vfOMbPPXUUzSbTdbX1+84Z0LPCP5hUM/U6VQ7RCZvBzYX1sus1RxcSofNchPTshjyQygYQlVVyktl6tk62ZbON/54Fqtuk5qJMT4+ciAd7v4fAL37xnFum4hXK1V8Pu9tpkXvwbe+uqMrqusmS5ezeKJe5FtBrigKREeClNaqrC4VmX7AWnE3SsslOtUORsrHjdUSAZeyx79KFLsKeV5VZq3YJOxRGdAkcjdyxKbvNbe/7ykwnd7jTbvrC7b7Wpoi0jIsTNtBo7umHbQO2JaNY3efS5CEbkKhysiadN8kr/eeBIG7vdudH8Mowt82Oh0TQQDl1myRAxRyJebm5sjn86TTacbGxognfKh+lVq+QfAWTV5vGgiSQGQfStvDoG1YbJZb+EodRE+gVywVZBEpqOEttshV2rRtCdM0e9TCu+FyuTh58iSmabKwsMC5d69y5VqH2FqbGKA7Dud3GoS9Ki9P33utJwIuvniynz+r5sk4JoMzwzw1nuDJka7P7G//9m+zvr7O8PDwHlX5Wq1GLpdjZWUF2+52IoPBIPF4/B7Blwdhrdhks9RiMOLBWCjRupgF00YdDxM/neTNxRWe//gJlvINyptV4vkWpl/E640guzRsn4k31yS3WWUp3+D00KOtNz6fj0wm092HbBuj06ZTrRKf6McutRGd7r23H3K1DoWGTtoRaK9WkBNeRLeMsV1HXyjhfyLFQq6+77m/G/V6/UCmmW7avL9c4OL1OpFDP8cfv7nIC4dTP1J/R920MTsWktC1phAEAVERcUwb/T4xhk+TeXk6wdNjURodE02Rbsds/tvXzbaxTOXtq2hJL0pdp1Fu9xTXfxJ47ETv9ddfv+dnlmWxsrLCf/7P/xnbtvmf/+f/+Yc6uL9tSJpEMORic7sG8YNnuVq1DqIsUKvl+fDDTRzH4cknn+RTf/8Ec5czeHwqL3xyHOkRB+XvOR5BQBC7gdju5uWYNrrtsJRr8IffuQmOQ8IHWqBCf+zgREbTNCYnJ3l/Mc/q2z8gWu3gTYZorlS5eCnDM08PsJSrc21LZ3OljLZRI9SxUTN1cARsVcR0y9RcEnWXTHIwyODzg0i3VOjqHRO32hWM6Z95kmQyiSRJe27mWq3G+vp6b0AaIBQKPXCxLG7XaCsi4buqdB29Q61ZZSSSwGpZveMobJdxmRb+8ThmoUUt16TeMR/rMwCIB1xsK22CokC9UkOUJdzevdeHXTdoD/uJuRQ8irT39Rxn/wTI6f33SOh0TK6+t47m1x4qYZBkAcUtc/X9DZ57ZQz5Ea5LURFR/SqNXINw1INblSk3DUIeBdt2KNbbjMd9KLeOQ34IaWfoyhK73DJKW0c37X0rsLvQTQvZAU1VHlvs4acFgiDgj7pZ36g88LG63t2IXG6JfD5PqVRicnKSX/+fnmF7o8rgaJj4j8hnUFZEBElEb5sYAkhBjUDaT103mT1fwB/UQKg8UCp6Vx3vvbeWuP6DRfwpLzISV76/yoknB7qmwkeSRP/8Omvvb9ESoQ10BAABu+UQFgM481UMXUBO+3AcgVKpSX9AY/xkmtBwiH/0j/4RiqIQDAb3zNPYtk2pVKJYvK32KUkSkUhkXw9O+5bQS8871LLJ1ToE3Qp+t0KpobOWq5MwZIy2QafcobJeQZRFFgtVOqt14sdSFNar7GTqjzUzKbtkZJdMp9Mh7ldZyjUQ9BY+nxfxDuVd85YAkt+lYJTb9wjS3Jkk7kJA6C4/D7nM7Hp1WrpF/noeV8jFQrGB5dh4D6DAy5KAT1NYLTQZHYl0KdzbtUcSzNGCGvWdOgA+l0TMq7JVaeFWZZodk6RHxLMr9tA2D+zqS4rUFdG5FawpkoAiCehmV9Dsfugu0w7KXevjowre/DRicCTEJZ9GZi6P43SN6xdWL/L+xXk0TeM3f/M3AUj3BTj83BAXv7VIu6ojygJ6XWfoZJojxx+P+i8KAqIgYIviPUmzY9m0bIdMpcMPbuYYCMW4fnOB44dnDnw+WZaZmZnhQnkZcfE8Tb0N/WG8toa+1eDqQoHnJ2L7MniGo17cWxdpzs2jHT3M6Zd/q/c4SZIYGRm552/8fj9+/+1OjOM4VCqVPYIvu7OB8Xj8vpTEWtvAchxUWaS2WkGQBASfC2OzRiGkk4zHqHQsBNFC6FgIhkXHZRN2da930S0jWDZCx6L1GJ1mWXWxVaiSHDQZCshcsJscOzaMU+ngDAWoB1VeOEB0x7n7m93Te6tAIwhwQI54D5rN5oHFuPeWCnzv67O4l2tols3a1QJ/lW/y916dJB380aijJvwa4T4/tYUS9kqFmmFRaBkUoyrLuQZDEc99X8ulSD3xmf0wNBjkctpLaaOGKQp4T6cfKAzz48RjJ3ovv/zygb/77d/+bV588UXeeOMNPv7xjz/uS/ytQ5REDj01wMZ/vYRuWKj7fZAOVDN1kgMqf/XNP0NzaRw6dIgnn3yS008OcPrJh1eRfBCGIh68fV5qjTb+1TKCLNG2bOb9EqGrO7RbFiBwLepiZafK//jZ0w+WoBUFvKqLQMRFRzBp1RvYtQZjMS/VlsHGUpGh1TpK04DBAEbKi1BsI1Y7yG2LQN2g2e9lMelGc8v00134tsstZlJ++m4pke438L+7YO7U2sxna9zM1inPbiDbS4xFXQyHVPwuhUgksmdh9fhVZNOmY9m9TdixHTY2NggGwvhUrSfu4NNkLNkArwd9s0ZHtxBGAvftlD0IRwbCXM+0qa3ayDs63vG9tCSz0AK3RCvq5sRA11rCq8kEXQqVtoE36kYKuzCzDeRkd3Ezsw3kqBs54qZW7xDzabjvs3AYhtGrchZyDRrFFt7owy963qiHSq5Budwi9giVWUEQiB+KU14pE+mTOJwOcH27yla5iYBA3K+hNXOUt2TcYfdD0TYBPHEPyaEQ0UKTTMu4b6JXbhnELIfkUABv4qNvoD5zuo+bF7Zp6yau+8zwlnMNVK/AV/7m/4f8usjU1BSTk5Ok+wKk+360qpOHj6WYe3qApQvbiIrIyY+NsrxY5J2/nqVZaiO7ZPqPRNG+6KG//8EBnyTJaIpGIOynUqzRarRpNztAly4ZBKxKizWf2k34JREEAVP2ULJt9HqHyFwBtwNFTSRYNTjz6UkCA933fdA8qyiKRKPRPQmpZVkUCoU9ZuiKohCNRhHuKpTIgoBXldm5ZWvQ3K7hK9WpBQJdsRVNxtItOvUOTrGOu9Kh8tYa0dNpIo+pguoKuwgOB8m/n2doIsTseh73XUme40Cu1ibm04h7VGq55h67EVWVGT0a5+K3FgjEPUiKhGM7FNerBNM+hsZCD3Us9Xodn8+H3tDRGzqSX2MnU8X/ANW7gEsmW+tQcxxUw0Gv6490DiKTEfKzXVEERRY5MxLh+naFastgKOxhJCSTL+QJB7r0uV17j7shu2QUr4Je17uWGopEX9DNzZ1ajw1yEGptA6+qEL81j2cZFqIk/kyYph872Uf7N0wuv7eOAJx8fphTZz7LV7/61V6ycu3aNYLBIJ/6uWliCS83zm9hdCzGjyV55qURXK7H20NVWWQs5mMuokHBxqp2EP0qTtOgUWpzzQ3Kh5tcurLDpagH/7DEyEibgOf+95NpgtftJhwJYCgOuWwevQWaYd63fOrzefGoEieOHX1k2X/o7omhUIhQKNT7mW3bFItF1tfXqde7BQtJkojFYsTj8d6IhyqLCHRFTaSwCyNTh4aBHhRRvQFkWcOjyLhVCUeTcBQJp337XrLbJo4k4mjSfffMu6GbNhfWS1xaL7O6ZfJO4QZWNc/RI6MUajpOx8ISLY4Ohzk5uP+9FfdpRLwq5Y6Jf9CPvlRGAFAltLEwWy2DUwf87d04yMKlbVhcuJbFtVzD71WQAhquzRrZK1lmj6V+ZImeV5N5+Yl+/nu5xdy5Lax6G1GTkAtVPvjBTbaKTT51PP3YLLCTQ2Hav3KUK1eyqC6ZF58cIPVjVOl/EH4s06aiKPJrv/Zr/Jt/82/4l//yX/44XuLHhqNP9HHlvXU2FwokxqOod9xMtu2ws1bB71F47ReeQA0d5ctf/jKDg4MsLi4iiiJ9fX2PtXjsh4Bb4alJP4vRKPmFIhg2RUVAKbSYKHbQbgX6rq0GG5bOpcM5Xjp0/wHdwbCHwNEE+Q+3UVsOzkic8ekws/OLdMo1wgtlHMPBSft65WEn5MLWrW65RgCt2EbLNFnIVon7VbYrbSJelRcm4/ftzNm2w/vLBd5ZKlBtGfg0BUlWqRsSH2RtVlrwyZkQnU6HfD7f+zs5YhOWBTYrbVxRL7IkkM1mQXETsGX6h0I9CWy/S2EoZTB3PEoeN7JP5YkXhpn+IbjRk31RQvISG8N+hlQTY62K4OoaZdpNA8GtUBgKMDgeYfpWNUyRRI4NBPn61W1SQRfu0yla5zOYm7c2gagb18kUtiTS7JicmEncdz7vzuFl07RxbPuRVJwEsUtrMo1Hpz6GRkMEh4KUlkpMjoVJBlwUGh2y5TaNUoum4+f65WWe+vljqA9pWSFKIvHDcVLXd8iZZq9LeDeqLQMsh5QskTyaeGSRh59GzBxL8f5QiM2lMqnJCNI+ledGQ8cqtXn2CzOMnz7J1772NXyaj5sXb6LICqmBFK7g/sIDjwOXS+FLv32ajU9WkGURURT44//lbUzdJDkdo1XrsPJhhvciIr/0aw9O9KaPJDh3KM7mtQyyojDz9Ahun8X81Xmy389RDGowEiKSqdNumdgeBVURUaWuQXhNN+m0DHwXtgmNhHju05Mc+9TEY/m1SZJEIrGXTqTrOoVCgUqzQq1Zw1g1cAfd+P1+TgyGOLdSoLpURlzMMjzUtZOQNRm9ruNL+IiMRrCiFoHBAGbZwa/JbL+1jvbyyCPPkAqCQHQqyuL7izilEk+MxZnLNtkqN3EpMpbt0DEsYj6NJ4bDtHbqeBPePUUV0zTpn1DYXIqSmSsgSl3Kry/q4aXPTz80pXTXLL1dbOPYDrbQLeRJD6Dbi2JXYMd2nK4QzCPSHYNDQXxpH5XVCqGxED6XvEcqvrt2aSydX2Ls9BiSJlFZr4DTZeJ4Yp6e5U5sJsby95bxpXwIgsBQxMNKoUG9beI7IFmxbIdqy+BoX6BnM9TMNfEmvfj7fzbsFZ58Zognn9lbgP385z/P6upqr5NVqVRYWlogFHf4+d+aIRbbf/77buimTbVtIAlda5a716UTg0HeGPRScksEcy3ESgdbFln0ilBoMqmqBGUFY6lCXvfy3XNz/MKLJ+77mtP9QRbSXjqrNZSGSNDxke1XUJ0yVy5dZGZmZo9wS77eIVfrkG06fPLV13j22Wcf6r09CI7jkKvrVCwNV3yIsUkXmtyloebzeRYWFmi3u/ONpiAjWzbbZYG+YwlEv4bR1ikJVRKxOO26zljcS9CtEOoPUNuuI8yXMZUmAHbDoNHnI9QfYOwhC7amZfOt6xnOXsng3qoTzjUo1JbRDg/SDlu8MBUn6FbIb63xidMDB9pMuFWJYZ/DW4UW7sNxvAkvjm5BQGPbJRJWZQ6nf7gCZNuw6LQMVMtCCnZHXQSvgtQ0yRQqwOOrh9+NI31BfjAUJJ+pETUc3Db4bIX6jSLZfINviQKpgIvoYyjNSqLA89MJnn8IGuvfBn5sqpvFYpFyufzjevofGwIRDz/3Wyf56v9+ie2FIo4iIru7njp2QycQ9fLxXzjE5IlusPMrv/Ir+AU/rWILUze5uXQT0S/iS/oYGBi4x/T6UTEe0Xj25AQrJxrops3F9TKF7y6j+gSkWxUCp2MR6AhcXc08MNHrC7n54mem+Jpbx+0KMDYa5oWZJG/N54g281itNpmAhls3u0IiCN2E746OmB0T8ZdabG3XuaTJDEe9fOZoiv7Q/astZ1eKfGd2h4BLYSrpx9EtHN1CDLqxRYGNUpO/uZrlF072Mzp6e4Npx9vkFquU391ktVKnKRg0qnUG3CEm+txMn0pTuFnoGh5bNtUPrvHrf+/nCAz041JEBsOex5a2zdc7/Nk3rrK1UEHrT5I7JKEUWvgqHbBsahEFK+ljeDzKZ4+l93RUxyIaYqvMUk5gLOnD9/ERrGJXQEGKunEUiaVcncGIh6kHJKJ3Di+7PQqiImF0LLSHbHDlMwWKhRJf/8ZfMzbWTzqdpq+v7740k12oXpXRj4+y/N1livNFFK+C0TJYe3sVIdOgg0P0tXEqAZ1Lb1wi7o9jGRayKuNNePH3+fed14lMRJh+oo/WO+ssdiy2dBO/JiNLIpbtUGsbyA5MWDDzZB/RyYf3CPpphuZT+eyvH+cr/+U827N5vEkfgYgbURDoGCaVbAO72uHQU/288NoEnUqbU5FTWJsiK/M7GIbJJWOO+IifvmN9DB0b+pFQWiVZZPiWEuj7b6/SLLVIHYojigLekIvaToNSxiCfzz8wAAwEXHzqV8bYeDKJz+fh8PEUmiaTLWX5cPUmy5ZB4nAEKe6ltl6lUWnTwcEWu8q+it31zwu6FZ4+M8CZLx5CfoyOQqHQYGE2j+04jE1Ee15gu1YzyWQStqG0WMLj8VAql7Atm+Fqg+3tIu60G1+sm+QBdCodwuNhFJ9Cea3M4cOHga5gR342j2M5jL82/shdoNBIiIbfQN+EyRNRkkEv66UmxYbe7TKKOoeGYtjFFo4gMPjsILLrVlJcq1EoFHj66SPMzIxz+cNNdrZr+IIuDp9IPbRpO9yutIuKiCiJSHaXZtYxHkSvtsGxqJVLOC3nvvYZ+0HWZEZeGWHxW4sU57vedapPxTZt3n97le25ArGgxsyJJFtrW1T+pIJgCTg4yKqML+UjfjhOaCREeDTMVmCLTrWDK+giHtA4kg5webOCbtmE3Mrt/cCBhm5SaugMhD1M3wpUHcehU+10/fp+BopLB0EQhD10xTup0Ds7O1y/fh1BEBgYGNjDstmFYXVjkotrZUpNHVHsJtZPDIcZj9/eWwbCHp4bcpMbS7C9VcFpmuiSQH2nxljVIDIW7qpZSy3UQpuNcuvAWb1dHB8IsvbKCBffWUFugeCRmTmZ4ufODOISbebm5uh0OoxPTHC9YPHehxs0dpqI6jQ5b5JSQyf8CF66+6HY0Hl9bofFXJ1mx0KSBJJ+jWfHYxzrD95jZ6XrOhVW+MbVbTYdm5AqkG/sEB8cZbPY4OmxeC9eeWk6wZ9m8qhKErVqIQgCtbQHeTTES9PxhxZiWco3OD+XJ3KzBPk6ZaPBQDCCs1ClaMJawMVvPjPMulPB6LRRPPuPLS0uLnIoqqK401xYL5P1yYiCguNA3K3y6uHkD9218rsUYikfm24FbbOO4FUwazrmgA+X3eLs2bMAuN1u+vr6CIfDj13s3Cg1KZfbTFVM5KAb+da4VqjpRt4uc/P8AnNjEZ6beDRhqZ9GPHait7a2tu/Py+Uyb775Jn/wB3/Aiy+++NgH9pNEejjMb/zfn2H2UoYbH25Rr7TQNJnRYymOnE4Tu0WbKi2XKF+ucvHGIsVyC9N2UCSRaNjNwCTc2LiBK+nC6/WSSqX2XJCVlsH1rQo7tQ6JgIvD6cA9tMvdWba4X0OxWmSzOeRWGVsRsetG7/d2x8LyygQ9rj3KjAdBbZf4H75wskcnaOkW1zYrBIsGvuEEPg1Wc1V2mm3cLhWPpiDfqug6OBiigNUyMLdqWMNBfvH0AEMP8KertAzeXSrg02RiHoX2bAFjsYTdsZACKtpMjOHBAIs7dd5dLjAUuZ2cuXwuXvyNpxgYW+fa+2tcvnCD08ODCEqbkN/g6uvXKeTbGJKEaZjUFwX4sIa7VaL/6f4fyr/kz795leVvLBETVWrb2wx8ZpzYdJTNUgvbAX1zlSfHVT5xZhD/HeqRxWKRhdlZ/sHHjvHN2Tw3MzUCboXArS5spWVQKzYZinQN0/0PUJ68M9GLxjykxsKsX93pSWA/CFbDxh8UaWcrXN+pMOubRVAEfD4f6XS69+8g/yBv3MvUF6Yor5TJXc+R/XAdyh3klEaxkMNZK9K86KeynWG2Nk8ylUJCQHFLBAYCpI6n9lDNoEuzGnllpGs8fyFDtt4hbxo0RZBsGLQFEl6FieMpRj428jNBodrFwESUX/ofzvDudxZZvLpDbrY7OyNJEEv5OfzJcZ56YYStDzeZf3+TrWKDrO3QUYSuaIkjkL1aY3t+nrUP10k/m6Jv7OES9/uhXC5TrVapN0o4Qnceate2w9ItIjEf9Xr9oSr9omjy4sduK+g5tkPuRo6arOJ1abg0iXbcwheOEGlDI9PAaHVnW2WXRMevonlUvHcoID4K3v3BCm/99SzNfBMH8ITdPPmpCT722mTvMYIgkDicoLJSwagaRGNRats17GyeaDqAL+JjZ2eHgD9Ap9zBwSEeiZPJZEjEb1drJVUiPB6mMF9AC2mMvjL6aMf6zg3m1h3s9RLNls3wcJiZmBs57gMHtle2aa2UcUfcpE+lMdsmN/7yBvnNPLZtE++Ps1PbITQSemTBpf2g+TV8SR+VjQpDES+XNkoEPcqBSrGVlkEq5GUw4Kdh1yl1SjTXmz0RrocxDvan/Ux+ZpLNs5tUVitUN6uUii3W3lnv+vll63i8MH5kgJbYIhgP4vF5MNsm9Wyd0kqJ0FCIkY+NEBmPkLmYQfNrCKLAdMqPoojMZWpkq226slldkTC3KjOZ9HOsP9ibu2nmmrjCrnt8Cv8uIZFIkEgksG2bjY0N1tbWUBSFoaEhXC4Xlu3w7etZPlgu4lElQjaYjtOzFfr88TQzqdsdnsGAzGdODDO/XWY9s0On3UKxFTybGnbDQAyKOC0T2y0xNjTA4uIi09PTBx6fS5H47NEkIadGKNGHz9W1+tj9DE+cOIFlWXzn7DW+9r1V4ms6UdWFbTssZBu8rin84pODj31+am2Dr1zcZLnQIK3KJG0wBcg1dL56eQtB6Ery3wlVVfnUE5P0p5N8uFLk6tIG/vQwomPhq64QbrU5d24Dn8/HcDzOkLhD9JXTbJe7tPepkItTg+Eec+hhMJep4WTrkG9Q1jqkRgYQRBGr1Ca002QzW2Oz3GJgYIDl5eV7VE8ty+LatWsMDg4SDocZdRyODQRZKTQxTJuAW2E87n1gDPMwkESBF4+k+Eq+yc6VLHLHwhz0M/X8IK89OdQTaGo2m2xvb7O4uAh0hXrS6fQjif2VmwZmpYPUMpDSt/dN0aOgSQoJ1cfb569wqv/ZXqz8UcVjJ3ojIyMHZtKO4/DMM8/wn/7Tf3rsA/tJwxNwcfrFEU6/ONITQrkTmUsZLn5zgZtbVcqqiK1KCKKIbdtI+TrzWxVmBsOc/GwST8DD0tISjuMQiUSQPX7++/kNlpaKqC0Tw6MwOxrml58YwO9SsG2bfKHAtaUNllfWaBkWyWiY0dFRpLDO/1m8SfvyDqxWAWi7JaTREE/PDJLJZA5U4NxFq9Xac+E2dZN2Q0fTLUS/RsKrEPK6qDR1Ngs1StUGoiwjid0FVBYFgkEXQZdCfyrwwCQPYGGnRqmpM5n0oy+WaJ3PIPoURL+KWW5jnd1CdMmkgi5W8g02Ky0Gw7efV/WqTH58lOu5y3zxhWcZHRklezXLB9+6SiUQpOD3YpoW+VoFoS/Fd+c2Gbq5yaFsgaNfOIrygBmT/ZDN5cluVoiJKsHxMOZiCaFh8EunB+mYFo4DG2sSS4uL+F23aSZLS0vUajWeveUn86s+N7OZGpc2yl0qIhByK7w8GWc65X/wXCV7VapEUeTYUwOsXc6iN417/PPuRitTR8npDE31I+zYtFotUHXEcJNavEa9Xmd+fh4Aj8ezJ/G7M0BTvSqR6QiB8QCrAz6WK03ySyvIHRFzOU95LI4RCbGKxPntbZKxGEMuF+GlErXNGkMvDpE+md5zbJpfY+K1CWLTMfJzeYqrVXTDRFEkooNBYjMxgsPBXkflZwmp4TC/8LtnKGxW2VgvYxk2bq/K2FQMxSWz8sYK599YZsE0aSkCfpdKTJURhG4HJdsyyJo2gwstQsEOtUCtJ0KSSqVQVZVmS+fs22u02ybHT/ftme3bnSvZ2trqFY2CwSBDQ0Mkk30sXamzfjmLy6+iN7uzpieeHiQWcz2wq7efIa6lW3RqHaqOg0eVUVQFRVWwTYuG3ESe8BH0erv7igCNjkmrbtCsdTBaxiN1LddWSrz5369jmRbJmW5Ftrxd452/miXdH2TmyO0kLTwWZvCFQdbeXKNT71DbqFFv1Omb6rIjwr4w7XIbl9sDI0HWDYvVmyucnB6FXB5B6BZMNFc3OSreLJI+mcb1kDN7GxsbVEs2rUIHR5Po9PlInkhSWi5h1A0QQPWo9J/ux6yZbF/YplVqUWvW8Ef8eH1eGjsNSkslXCEXsekY/U/1P1YHdBeCKBA7FKO4WKQ/5maloLJTa5Pwu+5J9motA9t2GIv7aBWaxCfipGIpFI+CFtCo1+t7lFE9Hg+RSGR/v7OEl8nPTtLMNbvvKd9gNlujfGOHdrtK26sRHg0TJkypVEKvGIRDXWq5bdmUlkosfmuRgacHqGfqVFYrBEeCCKLAeNzHUNjDdqVNrW1iOw6aIpEKaATuWIPblTadaoeRj43g+gnO1fy0QBTF3sy9ruusra11xyt0ifNbFmmPgnA1h7ndQJAF+meiZKIufjCfZyjsYXazwNn5LWaXNhnL2EwnvDw3M0Qo4CeS2uH14iziWg252KbplnAdiXGkP0xzp/rArt7mxjovnZw6cGZXkiQEf4JIPYMm6JTVFpqk4i+0WFwsUDmaeqj9dz/MZWqsFBqMSBL6e1vUS21QReIzUXb6fLy/VGA66b+HCikIAsf6g8TkDqOKn8mpaSIelaX5WWZmZpAkiVqtxuzsLFI9y5iTJe11CAaDDPf5CYUerZhXaxtILYNypUzyxEhPxVfwKEjFFmbToG3YKIqCae4VrWs0GszPz3P48OHeORYEgXTQ/djzcg8yS59M+vl7r00xeyxFrWPQH/JwKB3Yo/jrSCq2L0HAmyDqU/HJXU/XlZUVHMfpMTbi8fiBiZ8sCTiy2P2nWwju7uOKhQK55TUCM2c4MjPIhQsXmJ6efqAQ2U8zHnsn+KM/+qN7Fupdaevx8fEepeVnAXcnecXFIhe+Ps+VfJ12QCXqVfcodemmTaHR4dJWGedrN3nqV44wPj6O4ziUSiW+++ENFpc6pOerOOU2TlDlWruJTy9wKO5ip2FyoyRQLkGzHqOZ1Xgy4KFfVhiPK7z09CDvqTK5rW6i5037eelUH2MxHzt2475dvVqtti8FA0DoFjmB7tBwPOAi5tNo6Ba1RhNDN3C5NPxeN0q5Q00TUR6yerJRaqHIIoID+lIZ0S0j3+pGiW4fxloFfbOKN55irdDgvaUC56USLd0i6lOZTPjJLl0jmAxy6uVT5K7nWL2UYUVRMCyTZNADtkXEHUfV3OQbHTKCQOBKATF0ncB4N8DdHaR+kNJnqVSiUasyc2yYa8uzWIsl2gG1R4PS5G7QOTw0xNtvvYVpmoiiyMWLF4lEIkyPTNMqtpBdMiGvyjNjUU4PhWnq3YXUq8kHcuH3w92+M0dOpLl0NMnqxW2Sk9GebPbdaG3XyP5gHcsjsyaB6FFxNBW5aRIvhTk+nsB/1MVOcYft7W2azSaLi4v3VMp2/33/+9+nWCxy5vlXGP/MNKvfFOjMZWnHHRYsi3ahjk9TiUcjZHZyFBt+Ij4XRxQR4a11XAEX4bG91X1JlYhORolMRNDrOrZhI8pdz7XH6eR81BDtDxC9S6EweyXL5R+sMmeZyB6F/rsoRm61O4zf0i1W623ED7d4PuRm/FPj2LZNLpdD13Ve//oqy+/nsC2bm+e3+Nzfn2R3jxUEgWg0Sl9fX8+DcheaJvNL//A077y+zMZ8gUDczRPPDzM+2U3uHpToZTIZBgfvrZbvp/4oyhJ+vx/Hdmg0Gjg4eN2eXsL3OLhxNUuz3KL/aLL3s8hAgM2rO8xeyfQSvU61Q6vYwh1yM/zSMFvnt9i8uokv4KO2WesenyLScQtsRzRKzQ7NQgvFleBSzuJov5fRSNebqlardRX5litYf2MxeHIQVVGRFAl3xL3v7N7m5ibBYJCjJ3wsXMyhN01OfHKcsSf60Rs6VscCocukuPy1y1jbFq6wC92rMzg2iKzcEfzYDu1ym433NmhX24x9fOyH6oIHh4P4+/w0sg2eGA5zbrXEVrmJR5VR5S69ut42UWWR4wNBApUOxaUytm5TWimhuBXCY2H6zvTtuRYajQYbGxs4joMgCLhcLqLRaC8gEwQBb8KLN+ElAdTKbV7fyaIMWmznNijWO6wXm2xW2nR0naBS5thomrhfIzIeoThfJD+bZ/iVYZa/s0xpqURoJNQVVpFFhqIHFyab+a5X58AzA6QeU2XyZxmqqva8cf/q3Aq6UUXOtmktlpETHuy2SfXcJtLxIFdzAv/rTpZS2cJXd0jpYUpLBu82TVpqlU8f9fDMWBTjM1Ncvpal3TSIJLy8eLyPkZgXPTDxwK6erusHJnm7sHEQbAevz0so5aNZrpHfzGPmPei6AY+Z6M1marhVGfNGAbPcRhnwYzd0OnNF4kkv2WqHTLW9p2B953EvLy7w8h3m4uPj4ywuLiKH+7i6WeXKcpvxQ88xeWSGkEehUqmQy+VYXFzsKX2Gw2ESicR9mRx+VWS7XmY6GO7GeLvnpa5jaRKqT8Nzy/JCVdXeOc1msxSLRU6cOPEjmwWHh/PQ6w+59x0FsmyH95cKnFstUtxpgO3gibg53B/k5elBRke7TIpOp0Mmk+H8+fPYto2qqiSTSRKJRG+cajDiIZTy0lxz4dluIEVdlCplMrNr6B4Rd1BiKhVkPP4sFy9epF6v77Hd+CjhsRO93/7t3/4RHsZHB47tsHF+m7lslY5f25eTrMoi6YCbbWBuvcLAxQy23+ZP/uRP+J3f+R00fwS9eJPyWhYx6YaNGnKqj1Cin/RwhDfeWaHw4RbBYgu/DfWVBt/cqSNLIk8Mh3lxMs5U0s9GqTug2x/y9I4jlUqxtLR0YFdve3ubycnJPT9zqxKaR8FQRZSmgXhHcCCIAj6XjM/VDUQ77Q6tRhW91KEzGiPouf8l5Djd4K1QKiPS1d91dAvh7hkOWcTpWJQaOlcXciy8OUus2Ea0bXSPTCci0rY3+cWnp/j6175O+b0yKxsGWZeKVs1TNAI0Gg0CgQDtVhtNgM2mjWrZ+OZlQhMhFFVBkiQKhQJra2uIoogkSciyTCKR6NEWy+UyxWKR8fFxomkDlyqR2aoyNBrm5eN7ZyAVRSGVSrGwsEChUKDf3U/naoerm1exTRtZkwlPhInPxPGlfKjy480ENBqNPcm7263wxb9/gr+0bDauZNGCLkIpX89Dy2iblDeqNC9n0T0y/qf7SEa9PRprx7DYKjbhwx0+N3mCY584BnQNmre2tshkMmxtbVGv11leXmZ5eRld13nvvfcolUosLCzwyiuvMXqkn/JglJsdA1EQ6A/dPkb/YJqd3A7FmsM1BE6pCjtXdwiNhvbdOARBQPN/tM3QfxSwTZvMlSyrjQ6OV7nvHIlblbC8Gltmh7XrO/imfHz9+1/nl3/5lwmHw+ysXsYUTDwxF9tLO7Sbk4wdvR10n/9ggw/eukksnuOpl0b2zHNFIh4+/0tH9n3dWCxGLpcjHt9/fmE3iL8TkiqheVV8COR0a08XBW6tNf4uVbHZbJKvtYk6Em6P+lDdKcMwqNVqNJtNdjL5fZX3RFlkJ1NgZX6F9Q+zLF8tUL41NxuJuVHNNmpCxTfiw7EcBFlA8ilczjkUmjWSAReSZOJ1a9RaLT5Y7CA7Jgm/C6/mpZVvYeZN1r++Tmm2hOV0JdBVn0pkKsLIqRHCQ2EEUWBra6unROz3wz/6py92BVRuDf+rXhW83T1n+fXlrlH7TJKm3iTdn7630CoKuCNuVJ9K/kYeSZEY++TYYwnYAChuhZFXRlj4xgLkW7wwGmGr1mG12EQ3bUQBptN+BkJulHyL7XPbSCGNbRnapoW7alH7YIt2qc3EZyZ6Qk1er3dPoNdqtdja2up5lGmaRiwWQxRF9IbOztVlXvzsSdxRN+9fusF3rq5jCgpeTUGSFbabOpsXlnnx0AAjcR/+fj/l1TJ9Z/qY+MwEK6+vUFosIbtkPHEPyl3XnW3ZtAot2qU2ql9l5JURUidSfyeKTAehXu+gqhLqHarAdydVOhIuVaaxk6PZaSC0u9e61NSJ+4KsGiaZksnERgux2EKQRbymjZVvc8GwSQfdPDUa4ZOHUzw9FqWlWwQ9Sq+Iqqoqtm0f2NW73/pzJ0aiXj4cCdK6UYDVCoLtII0mmTo2wMrNa6xLEtPT0w/lvWnbNrVajVqtxlZ2B8UTwGmZiC4ZQRQQPUqXDmhDtVnn4sXLZP33HvuVK1eYmpri3Llze37+7o01lq0tqltlQpLKuyWLtcplPncoStirEQgEiEQi3U6lIFCpVFhcXKTVaiFJ0j1Kn7qu084ukTw2jHW9iLFeRfSqOEaXkVQaCTCe9tN/qzs3MDDA+vo6tm3jdrs5dOgQ1bZBtWWgSiIxn/ZDjcLAXmG5R8U7i3m+d34T91KZaLmDYDt0fCofDNVp6hY/f6ofRRLRNG2PH6Ku62SzWS5dutS7npLJJGeGQnyv3MYWBdyFJq1CHScdZCtg465uMp8d4NsXtvD7QkQ6NcqVy5w4fvyHev8/Cfzs8aF+zKht1ViZy1PRJOL3U+MRIObVKLQtzv7gBktv/jc6Sodvf/vb2OEhpKiP4HgKodjGGQvSivlol3N8M7vN4jsZBjMdXP0hBEVEzDSozRb5sD/Asf4gqiySDLhIHmDI7vF49q2a7G6kdwcIHlVmpj/Iu3E3ruUqTvTeIG0XmktDFWWaWoWSy8Rv1Wi1WjSbTarVKrVaDcPo0hN3gz2v14tPFdFrFoIsIie9tOcKiIHu/ISjW2DYGEGNS/N5PNeLHBJlIskEgiphNwz0mkUhfRplZIrnkn4uzF5h1WowoAoIhhfDNIjF4siyjO10VSn9LoudjkF+rYx3ZQcl1KUmWJbV+2eaJoZhcOHCBRqNBq1Wi2q1Sjqd5vLly2iahgIMesHZ2eD737uGLMu9RVWSJDY2NvjOt7/Dq4df5fzseSRJwhV1IasyTtkh850MS2eXGH5lmPBYuJdc7v69LMsP5JXfaZa+i1jMy6/9X57k3TeXuf7BBrmlEnpHp1atEYqGCLlUpD4/4nCAdHzvwqopEvGoh9xWnZvntkifTCG75J4P2sxM18eoXq/3Er8LFy5Qr9cplUq89dZbbF3Z4tNjn8YaG0Zvtkn791YuBVEgmUxSKBTYLpQZHUliX92g78k+/Om9vkRtw8a0bRRJvK8/zd8FVDeqrC2WKKsisYfoyPg0mYqic+PmJh/8uzdx+h3eeecdTp48yZEnR/nw6/PoJZORY/0Ew0pvvnrxZoV3vrKC2bbYulxia7HEb/4/niUcfjAlx+fzkc/n9w20Dur2CaJA/HCcxPUdsraFaTnI+/lACuByu5F0iLV0rLBJpV4hokZoNBpUq1Us614PKUVRevNgM0dslt7L7aE2m7qFY9lMzfSTv1Dn/Fs7lBQBMRbAAYpVHW22ytRghOFjwz1Bka1yC6vskAzoSCKE4wkcx8atOWyWW2xVOrjbBvlreVqlFo7VFSNxpV0oLgXHdtCrOltnt1h6dwnfhA8zZeLxeYhGowSDwV7gBlAqdVNUQejOkDUzTVbeXqFslxELXb+uaqWKJEld4ZRb/3a/l1Spa9dwI09sJkZoOPTAz/Mg+Pv83WTpjRVqWzVSishwfwhHFBAAs9ph8+IyfsWPGnGzlvawXWqiSCKGZTMUciMsFIksFEmd2L9D5na793SUO50O29vbXU/EuRJzl+aQkhJnzjyFFezHqXfoD7h63d6gWyFfk3n35iYRzzABn4vaZo3iYpGh54eY/uI0O1d3WH93na0Pt7B1u/v5BF1d30+na/cy9OIQ4bEw3h+RN+VHEY2Gzl//yRXWbuygaDLPfGaSZ18Y4fz585w9e5bf/d3fJZfLUS6XqRcaZHItJkMaPtWNJLqxOxZC1IXhksnslEmtFWnnDfxTCSRFxrEdWK+ibde5tFHm1FAIRRLxu5R9Z7zGx8dZWFjo7Ud3YmdnhyNH9i9E3YnJhJ+nXxzmQ7dCPd9EUEWGjyT59OkBEn4XnU6Hubk52u02ExMTuN3uXixzN5VRFEV8Ph+hUIijYxJnV4qE+v0YOw2MbAOnbaIkfdRVkaQ7yItPj2A7cHmjzFquQcin4jOKfOpTn6Kvb2/R2LRszjeCOO/NM7rSxisJCLEOGy6NlbJJ2Kuh6zrNZnNPDLNbHLEsC13XuXHjBu+88w6NRoOlpSXGJybRlDgLYQlVN/FUmliqSD3qJhDSibQznD+fQ5IkHMfhrbfe4rnnnqNp2Lzx+mUWix3aFmiySH/YzVPDYcYT/j0x0KOIDt7PLP1urK2tUS6XOXbsGNW2yYfLRbwLZVzZBnLMA5KAVmgRW7S44VU5MRjaV9hOVVUGBwd7zALTNMlms7hqGwx7WtwchEXFJGMbTB4a5rdOTDK7mOHdP7+Op6pTEGFlOor4RIT2++9z5syZH1po8W8TP1Si99Zbb/FHf/RHLC0tUSqV9hhhQ3ejunTp0g91gD9taOQa5Mst8Ej7Byl3QJG7s3s7a1XGjo1hhA1UVeWlp47SVDeZ00pQ64Bf48homFdP9vOVS1tEKYGsU+3UoQOCBmrDplRoUWkZPZ+fg3BQV29jY+MeitYuDqeDnB8IYOy0ELINlNQdiYHtYJgGuq7TaeuYmzWqYQ13xI9Lr/Dd734Xj8fD9PQ0MzMz+1IpXPkGCx+s0dIttKkIVrGFsV7tBjM4qKMhtn0ytXMbpBom4ZPxXndKCrkQ6zqRbIsblzMcOqNi6A6WIhFwK2heN2vr6/dIqKumRa1hEPK4mJ6YJjAQQG/o1LZqWLqFKIv4Ur7eHEatVmN7e5upqameNPKuLDJ0F4vdatpukjg/P8/IyAj1tTrOhkO4L4zsk7FtG9u2saSu91Zlq8KVr10h8VICwSXsSTTvXLDvvIfu/P7DDz9EEIR7EkRJkoikJJ79XJitdReb6zusvj+HIbno849g2DGCB6iharKE6ZHIbtVo5ps9j7I74fP5mJqaYmpqCp/PR6PRoFwudyt+O248/iDzdR2/6+CuUzQapbVT4vtXZolubDD12hT+tJ+WbnJtNsflDzcprJSxTBvZJTMwHeP4E/1MjYb3Nb39WUer2KJS72Ar3GPefBDcqkxFlDiUnkY+KuPxeEgmk3zqC3HSA0HabZNDx5KE7rgWLp+9Sr1QJz4VxrJssks51lZKD5XowcFdvUajcSCtMzQSom8gQGatTLbaIhV077EVsU2Ltq6zXWkRsRzCYY3gcJBcLsf8/DyhUIjx8fH7zngAHD2Z5sotarPmU0EUaFc69B1JMBR2881vLFD0K6Tv6HJbAReZtTKry0WGsvXe/dDSLWzHQVUkDF2nK0QsIojgcSk0CjrVqo5oiiTGE1i6hV7TiYQiqL5b90UCmOhSRbdmtxhKDXH0paPopk6xWMSyLBzHwXGcXiC5W9hZWVrB1E2mj01jmRaBYOD2+mJZmJaJbdz+3rnlWlwtVZl9a5Z+8eGCKsMwKJVK+/7Od8aHuC1SXapSzhURbAFJkZDcEtc71wmVQgRSw2RrHdKh7mdqmDZbtQ59PheFm4UDE727oWlaLxA05g10Q8esmnz7rfcgdZiY33UPpTfi1ciYNotbeab7Iqh+ldJqiehUlPz1PPn5PFbHQnEpdMwOZsekXW0TioRIHE3Q/2T/PZ2+v4t44xvz3HhrBV/cS7Pc5o0/v8bZc98nk53Dtm2uXbvG8PAw6XQaX7JJ5oNVBEnCJagYG1Ukn4p2KMaSYxPwuBlXvAjBNqVKGduyEEQRtyThKrQoN3UaHfO+6pGqquI4DoZh7Lnn7/7+fpBEgU8eTnG4P0Su1kEVHSKqTbu0w+xms/c6kiTx/vvv02q1mJqaYnp6+r6vcTgtcGWzQi3tJSCmMDINJK+CMBZms2Pw7Fh3puvP3l5m8911XBWdZVXCGveQHhjmbn30hm5R023EXAWv7ME9EsbcrBNyFDqS+57E8H4wTZP33nuPX//1X0eSJLI7OT6Y32Iu2aLcttEUmZeH4zx/aIC+cLewUalUuHnzJs8//zzpwWH+5nqeuR2diFsm5BHQDYu5rQqrO1VeGfXS55P2xC8Pi9nZWarV6p7C9d0xze7XFy5c4Ny5c3znO99h/MwrVLIO8VIbKelFvMXyENI+jPUqTq7JSr7xQAVzAFmW6e/vp7+/nzNAo63zH/7Lf0WWNyi8f45l+VWMTJhYvo0S92A3Dao3CswnffzaSzO8++67PPHEEx8ZkZbHTvT+3b/7d/zTf/pPcblcTE9PE4lEHvxHPwOwTRvDcnoqlA+CKApEEyle++Rx0qfSPfnqXzjVz3x/kErLIORWmEj4cCkSXlXCUkU0UcEbCiAIAkahQdFo0G5UWFtepB32k06n7zukvF9Xr91uH3hhxtwCo2mNs3GR4GwRaacAIRXBJYPQvTHUjoCr7mAOJ3GOxvjcs8OcHupSvWzbZm1tjeXlZdLpNIHA3qRhKOJhNOblRqbKRMKH98UhjEwdp20i+lTElJe1a1m0XItg2o9yl/CC5FPRSm0qa1XWJpooioTcFDAsh06jRjQWpd1q43Lf7nIaloPkgKKIXcrt+xvkruVolVoIdBNMLaARnYoSnAmyU97pzQPIsrxHFhm6leZCoUCn0+ltev39/bz00ktsf38bHOgb70M3bG5kqlTbBn3BW343w1CYLTAeHX/ogGcXpmmiKAqf+MQn9k0Qd7/u77cIhDtcul7C4w/QaNZxhOh9qRaCJGKbt4PD++Hw4cNomkYoFCIej7Px5gbLF7Yxm21cyv2XEkkUKFaquFotzn14Dj3h52/+9CrFxQKCbuMJaN25n3qLueUF5t5apf9Yki9+6RjxB9h2/KzBMiwMx0EUH75iKIsCeDyMDaU58YXbwkCSLHLqyf2LO16/hqapCJaM3TRw+V00W2XW1rpV7Dul1vfDfl29Bw3aqwGV+IkI/Zt5GrUOK+02sijgudU969gCtiDSr7mZ8WkceXWc/tO3E5VOp8PW1lbPs/SgqqrbrfArv/ME7721ws1zW9iOw8mPj/LMS6PM/s08Rd0kmgruuTckUcAfcFHPt8kslXqJniJ1TY4N0+q+3h2zzHpTh60mplvBl7xVHLO73cv9qH8tu0VoJET9Zp1MX4a+M333BHCNRqMnkNMoNbj4NxfpH+wnEAiQy+X2VNB1w+6uY/t0wX2qD6NuEPPFcD9E8l4ul4nH4wfOcDsTDuaz3QTJaBtYjkXLbGFum9R2arQKeYj29RJ3RRKxbQdHpDtr+BgwOgbHTh1j9MQom5U2by3k9y1+3HLlwO0Pohs6W5tbrMytYLWsLiUz4qYd99AIaTj4ccsiEQSMUpv89TzemJf44Y++jPoPi8x6GcWtELx1LW9d22E4PEA8oXL16lVyuRwnTnTXl4Gwm2fHorw1n6c84CM4GsTEYbttEvEoKJKIs9XE1bbwxbrXlGPbVJfzlO0Kemabm7MdJkYG7xs/7s6u3dnVW1lZ2WMLcSd2x0V2KZa2fds71kV3bxdkP7FY7B4tgyNHjuA4Duvr63z44YfEYjHGx8f3ZdyMxLy8MhXn+/M5VkMq3oQH3bKxDJND6QAvTMa4vFFm6/1NYrk2SsSFVTMo3Wzw7o0sU0n/niKXW5EQTB1PX4LmtRzyqoQUcaGr4oH0/aZuUqjrCAIk/K7uHmpZvP/++5w+fboX7w0O9DM40I9tO3RMG11vUykWyK7cZHOhW9S2bZunn34aVVV5e3aT9arFkcEYYrGFsdVA9Cj0jUZZKjXJ2B4+PjP82DTOM2fO7Pn+oLhmF51Oh1y+gGCFwbL3jP7srrOi7aBbj+4TDOB1qTx3bIKXTs0wOTnJhxmd9/6PK8h+FcmvIvoU3MsVGuUWjqzx9NNPc/bsWWZmZj4Suc9jJ3p/8Ad/wPPPP89f//Vf3zcY+FmDKInIooD5EIExgO3YCI7FTm6H+TfniUQiHD16FJcicaz/3vM2lfRzYShIp2rgrFQRFAHbdjDGYjxzapwnjvfRaDRYXV3t3QjhcJh4fK9Z+d1dvUqlgmVZrK+vU61WabVaex7vcrk40+fFfHKID11urBtF1GwDyTBR3DIBr4gT0KgN+5AmInziZB+nBkO3z4so9hbe7e1ttre3e4PC3d8LfOpIirZhsZCtEfFpREdCSKKAbtpslJuUSy3GFIlkYn/qjOCWERsGHbdMIu0jVm6yWm8T0kS8Xi+lYnFPoldrGQw6EEp4yV7Lkruawx11Ex4PI0pilzJYbrPy1grmFZOX/uFL9/0sNU2jr6+PVqvF+fPneeWVV5BlmbWba9gVmzVnDXfWzXLFZrGoo8oS25U2LkWiP+xG9asU70NhOgj1eh2v19ujaN0vkBZFkd/8zd/kqaeeIn+5wF/+8QWquoVvH9VK2wbaBt6IH8X74Oqoy+XaQ5WRVAnJAUkUMSyH+wmbSorKyGA/SUXmvfOzLGz7aK5XiQ+HkH17N7GA46CX26y/u86fNQ1+9R+eJvqYCl8fBZhtE7NjIogCikdBlEVkUcRxHn7TsmwHle5M2dLSErIs95TyDsKTzw2xNl9g40YOWZF48rUpnn3uUO/31Wq1p5YoCAKxWAyXay9dPB6P7+nqZTIZYrEY2WwWXdfveU1RFIkdjnFGceH76jwbC3kKHYu2JCD6FWIBFymXTH/Kz8izA/Sd3psE7c5eWJbVm+tKpVJo2r0sB39A49XPTvPqZ2+LOTi2Q7XYwlD2pwhrXpUG0Kh2etTzZEBDcQzayMS9Go1GHa/PR6NjIpR1vIaNd/j2mmV2zG63667nr5QryJKML+Gj7tTJXcuROJq4Z/5wd4atUqnQyDYY6R9BDskU8gWKha6qqiiIWJLG+c06juPw1GiU6F33kepTaeaaGA3joRK9RqNBOp0+8PeCIKAoCkr09o3e6XT4/M99Hu+TXlavZLmgm+RrHfwumeotg3LVcB7bdFx1q0SCEURJRJMlJKHbKbw72XMcwAHtluDKzQs3aWfabCxvoI0PcDNbpdTU2d22BSDgVphM+EhZsPL6CqIi/sx4dT4uIkkfqxczNMptOg0dxaNw6fJZqo1VvF4voVCo91hBEHhxIk7c7+LyRplstY0iSjzfH+LEYIiL62Xe3qjiKrYwc01En4rd0HFrLuqTIZ47NcqxyRCbm5ssLCwAEAqF6O/v31Og3mUI3dnF203kqtUq7Xb7nlETr9eL3+/fI77xsBAEgaGhIYaGhsjn85w9e7bHWLqbrfT0WJS+sJu57SrZage3KjGV9PcK95vlFkqlgxzUkIIuRI+Ce7NOIdegoZsE7qCrqrJISm5Sm0hSF0BRNJoBN9Gk7x4jct20eX+5wKWNMuVmdz4+5lM5PRSitXGDUydP7ivIJ4rCLSEvL0Gfl6GhIW7evElfXx/BYJCdnZ2uYOC5NaIjM0gtg/r7W1jlNogCjmWTHgmyXmqRqbbp+xEVYXeLV3ef3xMnTnD48GGmpqZYL7X44+8vYnkUhEIL+VacaDcMkEVMr0zC//gquS+88ELv60itiOVXMDcbCG4Zp2nQ1kQ8IRd+TUFRJJ59tivSUqvVmJ+f58iRI/ddP3+SeOxEr9ls8hu/8Rs/s0mebdnUNmu0K21wQHbLBAeDuKNuokEXG802tu3ct6JhWg6CbrOxOs+lP/kWnpSH11577b6vOxH38ezpPt6XBKqbNQTLwQloDB2OMxhxs7BTJ+7XGB8f7/1NqVRifn6eZrNJs9nsBWP5fJ7NzU08Hg8bGxu9jsz4+Pg9ARvASr5B/doqTq6FXmmhNw0E3cJs66wqIMUDPP/0IC9Px5lO+g+c49tVaCwWi8zNzeHxeBgYGCDiVfnF0wOcWy1xdavCUq5+yztMIO7TmOwPEsy0EEwb9mGnOh0Lx6/i8agkjiRI3iyw3KzRVgN4HfB4vLQaTVweD4V6B00QSMoyikchfz3fk+nfrrZpdCw0WSTmkdB9Ot6ml+ylLIPP3d9XZ2dnh6WlJZ5++uleR7Uv3cfY8BhXlq6QSCS4XsigyBIJv8Zaoc72ToGEN4koi5gd877Pvx8eZXi5r+92hyA8GmIgHeByvkbHJd9T9S80OvhaFmOH47jCj75A+lI+VFEg5VNYLrbwHSSY4YBhw8mhPhID/ZilBpX1KsnpGMI+1XlBENDCbpKaQvZSlr/56ix//9dP/kiVv37ScByH+nad4kKRwkIBvWUhiuC5JabhFUCwnYPn2O5CUzextrb5i8J7uFZcfOITn3hgoufzafzG7z3F9mYVVZN6ZuK72J3X3D3efD5PLpfDNM0eO0CSJNbX13sU5931JhQK7Zt8ARTmCxTmCtiOg+xR8DVNPE0DrW4SMmHkxRQTn5nYM8d5NyRJ6s1bZDIZOp0OkUjkwG7ULgRRwO1VkCz2TRisoIqAgyLfnmWWcDiU8LBYdcjUOhgdnYrRREIgbdhEw+4916Ze10kcTuwxDa9Wq7fFZgBPzENpsUR5tUxs+l6aayaTQRRF+vv6KdtlvD4vfqkr3NJut/H7/cxu5slVu36eK1s7yAk/gUDgdoVb6jIZ7Iescu+yTR4FmqZx+vRpSsslKksVjqgmN1s69Y6JaOnMeH0EvRqeuIfKegXFo+COuB/6XvbGvVhGl3EQ9apEvCrFht6dT7/jKYpNHb9LJhl0gdEhYkfQkzqXShnEVS8CEPO5eveSbTtUWgbn1koc6wsxIDhsvLdBcDD4Q9lSfNTxyqenKOcbbN8sImsSZz47ySuvfYY///M/5/r168iyzAcffIAoiqRSKdLpNIfTAQ6l/HRMG1EQUG/dU6cGQyxMx8hbDsFME6XaxlQksoM+jJibfKPNxa0Gh/qGekqe5XKZlZUVarVab43ZTVi+/e1vMzY2RqlUQpIkbNsmnU7vG8v8qBCLxYjFYtTrda5cuQLA9PT0nv14MOzZV1kTIOrV0D0ydlnH9ijY1Q66IhAOaLjkvfvx8vIyn31qhqWKzfdlk3y5zsmpBE+PRffoMdi7/oUrRUIehcGIB9txyNd0/uy9BT51dOih4gXTNLl69SpjY2O9dX73776xrKNKAnbTwK51UAb8GFs17LrRHfmwHHTz8bpnj4I7x48GQm5G+wPMbdeJr1bRVyvdJUAWqfR5iQyFmEz8cD6yu5hK+hl4oo+N9jrechtDBHsmyivH0rhvsc0EQeDUqVN85Stf4a233qJYLPKlL30J6NL9FUn4qRk9eewV7WMf+1jvwv9ZQ2G+QOZihvWFIrWmjuM4uGSZ/uEgycNxBkfCLF3YpNDQ7zsvV2h0COoWn/i1T7Lt3+T9D94nmUxy+fJlHMchFArR19e3p0MjigIfn0kyFvexWmjSNiyCboW51SJf+YurtNs6vqTGk9Meolq3PLlbed71J+p0OpimyfDwMNvb2xw6dIhAIHCP2uadWMk3+O/fukn97BYTuoM0EKExDB3Txq7rUNdpr9fpjDYInOh7qI06EokQiURoNpvMz8/3ugwfn0nw1GiETKWNadu4ZIm+kJs34zv8YLGElW8heJQ9r2F3TAzDQh7wMRz1EPVqxMbcHLnWZtN22Co3kSWRWrWBWwe/KDKOyOjRBGbLRHbLyC6Zq5tVbmSq2LdKuyFJ59WT47SLTfKzeZInkj11uLtx8+ZNDMPgmWee2fNz2SXjCXpQBZVKpcJQ3E9+tcxGqUnIqzGSjlCv1yluFPEP+1lZWSESidxDbz0I9XodUXXT0q3eIvMw8Ma9nHhhiMJXbrC500AOaHhUCct2qNTbaNkWA26V5kqZa39yjeh0lMSRezsMByE4FMQb95IqNtmURcpNg9Dd4iEOZGsdgm6FsO5Q0QTK2Qbx4dC+Sd6dkD0ywZiH9QsZNj5ZZ/AhuPcfBdiWzdbZLVbe2yCTb5B1bJoCiA6ENypEBQGx0MKPQ0WVid5P9Ilbm4rlcObkFNLUMBcXLxIIBHqiK+Fw+MAESJZFBm+JdViWzdtvLHPz4jaSKnDkqRjpvnsDGJfLRTgcptFoYNs20WiUWq1GPB7n+PHj91Wvy93IsfidJVYKTdYFm2bSAzEXtmF11fZkmfJOg+J8EV/S91DKh7sU62KxyOpqt/NwP+uHydN9XDq/Tb7WJhXy9HzhHAcqAnh9GsE71JQLxQKHRlIMtE0uza9RE0W8bhh2+2hvN/f45Zkts7seJL1sllq0DQuvaOJRRALB2/e7KHVFUwpzhT2JnuM4rKysEI/HuzOxOw0EScA2bURJRNVUKtUKATHAWCpCw5SwcTjUH0LBplgq3qZh293nu7uz+ONAaCTEwPMDiGe38Os2hiQhqyqNehUxkGTtB2u9dTg8Gmbg2YGH8qgLDYdwR9y0Si08UQ/HB0J8sFxgs9zEqymIt/wWVVni2EAIjypRzugE3UGSLw/xQbmF40DoLsaCKAqEvSr1tsmNTJXYWBRzp0FlrUJ06u9uVy8cdvOb/+Mz5HbqaJpEJNq9l7/0pS9x7do1jh3rqjPbtk0mk+HixYtYltUr5t7Z8UsEXPzCqQHe9GmsZGpU6jqGIlIstfBfybPywTbX3SLfOxzgEzMBQu7uviNJEuFwGI/Hg67rVCqVXnHJ6/ViGEbvOP624PP5eOKJJ3rCLfV6g8nJiQeqfh7pC3L5VIrC+1t4dpoYEpgzUZ46nOwlxNDtVrZaLUaDQY66df7Lv/0jvvRrv87xyQDB4N41eKPc4vJGmb6QC0/HRr+WA1kkPRpCiYW5nGnzxLhxX3/AWq3G4uIiR48e3XcEaDQd5dJGmWRfDDntw9ysI/lUlJSXWtvAaNdZmL1K3tVV+Uwmkz/2eTVRFHj1UIqOYbPiU1HLHQTbpu2RUWIKz4xFWcjVMTI2qYCLkTvmrx8VQbfCLz43wtm0n6WNCj6Pysmx6D0sPNM0e8XN73znOxx75mWubrdZ36ji9iqcmopzZvgnrzXw2Inev//3/57XXnuNf/tv/y2/8zu/85HgqT4MsleyXP/mIjd3auQVAUMABAHRNFie3WF0tczAUJCxgJsbjQ45IOpV91xQlu1QqOuoDZPJmI++EymOTR5hbHysV7mCbvVqfn4e0zSRJIlkMonL5aJarWLUagSbDQKOw9s3Gyy+niVa6hBUFeobGjfD4/yjzx1+oEKh1+vlnXfeIR6PMzc3h9frJZVKoes6f/qnf8qv//qvYwsS33xvjfoHW0RlCXm0a1oc2n2SmAfHdjC2ahTe3eDbYRe/8bGJPRdvp9bB0i00v3aPsbHL5cI2Qmxv11hauMTQiJ+hoSEm7qq+HO4LculIgsoHmwTWqkhhF4IiYjcMzJpOKe5i+kiS4agXUYDgiSBPRCOkbuTZKTdpWKCrLtRGi9G+OP2HYsSPxpn/2jyemIdq22Rhp45XlfG5ZDqGRbkpsFPrkIy4KS2WaOaa9yR6lmVx/vx5+vr69lWLUjwK0YkowWtBspksU9NTuJWux1nEqxL2qliGghN0mHpxishwhFKpxPLycu85fD7fHi8p6FbubmSqfO3KDrrsxbexyHDUy7H+4D3n7iAMPzvIxx249NYq69s1Wt0npt+yMXIFYsdHkb0qrUqb5e8tU8/WGX91/KGCQ9klkzieoPG9ZY6GPVwrt9gsNfG7FGRJRDdtGh2DgEvhiN+NG7jZ0hFM+x665kHwxj1UZvNcvbDN4Kc/+ome4zhsnd3ixveWudnW2cFCVSTcioTtOKx3TDYFSJkWvmyTmiZSk0T8B2zaumlTqLUZNhyGZmJMf36agfmBPd5TpVKpl/TtWonsyoQ3m83e4y5+mOG9Ly91EwvDoZxt8A/+8fO9YO9u3BnUzc7Osri4SF9fH4VCgVAotKeQcePGDYYTw6y9tcZCscmiZeJWZZJerTtf5UBDN1lp6uiGgfTBJt6kd08S1Kl1uomPIOBNeu+5T3cLS/V6nZWVFVRVJZ2+14YgNhnl+Kk0Z89tsqlbeG8l0s16B1/N4MjzQ2iSiNE00G0dr6e7HrplKC5dZWRkhP5kGKkp0MJBvBWwObZDq9giMBRgx7Y4u1KiY1hEXQKvHruXKaB4Fdrldq9Q1+l0WF9fZ2RkpBd8uUIu3GE3rWLrng6npkicGb1z35X2mPrWMjXqcp1cPUdprYQgCEQikYeSkX9UCIJA3+k+QkMhKusVjIaBpEksf7BMdadKbCyGv8+P3tDJXsliNA0mPzeJvA+l/E6oPpXYTIz1t9fRAhqJgMbzE3FWC3U2y90u8ljcy0jURyKg4dgOuas5tJBGWZOo3zJcPgg+l0y1ZbBZazOmSORn80Qm9zdz/7sCWRZJ9+0tQoqiuCe52p2R3WWPNJtNNjY2uHnzJoIgEI/HSSaTeOjwbMJhQHRomxLnN5s0r24RqXSQAxq+KpQckerxUZ6Z6dLe2oZFU+8ybu40yT569ChvvfUWtVpX6dvj8dDf3084vNeXtdPp7GETdEyrVzSP+zVSAdeBn2++3uHyRoW1nRrhgMbxgTCjsdv3S7HQYe6CxdqNKq8bbxMfU/nkZ48wNrq/v1oq6OKXXh7nvXSAza0qIb/KmekEJwZCex43Pz/P1NQUAF/5ylfQVJVqqWsDdXdSu5yv0zYtfLJE/e0NjI0aCGDXdGJP9bGQrbNaaHD8rtfYxa5t0smTJ/f9PcCTE2k+XMpSsx38zw5gFVsIbgWCKtmdOmcm+/nkiX5s26ZQKLCwsNBjdQQCAVKp1D1sv45pMbdd4b1tg+Vz6yT9LqZT/gPV4/dD3K/xpTOD3BwMsZhrYNo2g2E3czdu8PaFTSrzBTAc1JSPJ58b5JOHUo+d7MV8Gp851gfHDhbBkWWZ3//936fRaPDO+Sv8xZuLmNdreOsmZUngGzcLGJ+Z4sXJn+z872MneoODg/ze7/0e/+Sf/BP+2T/7Z7hcrnu40Ls+Hx8V1DN1br6+zJVCnbwqEPNpvUTKsGyKDZ0buomwXmFsMoqwVGSh1CTbMREUCUkUsGwHRzcJtG0mYl5OfXqSyER3M56YmKDT6fS45XdaEdi2zdmzZzEMA4/HQzwe75k/vrGwSEpwEz4UR3BJuJbK1HJNqm0DlyJRbRusFrreRjGfymDY07u4BwYGmJub6w1Q7wZBly9fZmlpif/4H/8jz3/uS2Ru5AiZDvKAd3+PM1FA6fMTXCmzem2HtRN9jN2S7N++uM32h9tYHQtP3MPox0fxxDy99/XVP7/GpTeWsdrdeb/Ox8fQtCytVot0Ot1bEPpCbj77wghfFwVyszlchTaSDboiYk4EmXiij8+e7A77r6ysMH54HPlYN9n43/7f/xtSS+Lnvvhz5Bt5Tnz8MIH+AM18E9vqymkbbRPDsgne6jppioTl6OiW3fOaupvm1Gw2uXDhAsePH78vLSx2KEbqUoor57v+OHdy1y3DorRUIjIWITgU7AVcdxZH6vV6z78GQFYUZssi7y8UyF4tMih1sLU6VwIqs4NlXj2S5szIg4srkiIx+tIwycNxSstlqvkGkiLR2qqxcN6knFa5vFPDpUicTPkpzHY7DJHxhyvcJI8n0Ws6wodbPOFzkZNgq9ZBN2xkWeBo3E/EdPAhkHx2gB989QaefYyjD4IgiaiSyObGR2cduR8aOw3WPtjkZktnx7FIB917NqKAW6GlW2wm3PTXddKbDbI+lYbe7ezvdnQN06bcMjANi0Hd4cTRFEPPDSGIAtPT09i23Ztj2VV0hW4QdOHChZ5MeCKRwOPx0Gq12NlYQlU10jMxLMNmZ6HA5nrlwETvTuwWj3bpopVKpZdcNhoNvvrVrzLpnUTJBVl1bIIeFc+dBSEBvJqMJktsVVtELEjcyBOdjCKIAvVsnaXvLFHfroMAgf4AY6+O4bnD/Pr61Qxv/s08etPg2HODPPtSqueXeadwi+pTefoXD+PyKsxf3SGXaQAwFfEw+cogRz4+SvZSlu3z27TdbQZGumI2H579kI7eoVgscvz4cVY2VhCc7nl1bId6to4r4iI2HWOx2qZlmAQ0lY4DHcvGc/f+KArYto1jOVTqFarV6p5CIHTnYGOHYyx/dxlfyocgCPh8Pur1+gPpWZ1yh4kXJxiY6h6/4ziUSiUKhULvevB6vYTDYQRBoN022dqo4A+48D/CPXonPDFPb+0vLhQROyJiTCRnWWyuFOkLuekbD1NeKXe7Zw8xE5c6maKRbVBcKhIeDRP2KoS9YY4POD3qP3TX7vJSGUESiE/GuVRudffvB8R5XpfMdrnNVDrY3S9M+2+lC/pRh+M4PTuiXdGTQCCA4zhsbm5y8eJFNE0jHA4zMjCA2+Ph/NoSScFFcDyM6FGwah30aoeV7Rq18VhvrKPZMlFViUPpAGeGw0R9Gh6PB9u2+dSnPoXP56PZbLK1tcXCwkKPITUwMMCXv/xlAoEAn/vc5yg0dP767AZr17PQsdCiHp58aoBXphN7hFAAig2dP3trme33NnBVdVZVibkTSb74sXGmU35KpRZ/8f85x85iEU/Ug6BLbJ3r8JayRvbYFtFolImJiXvoz0MRD0PPDNM2LFRJvCfxKJfLeL1eZFmmVqv1Yubl5WVOnTpFoVDYU8DpGDaSKOLoFla1gxRz47RNrFIbURAQhG68uh/m5uYIBAK9pPIgjMd9HEu42Kp3yDvg9yvopk09W2c05uWFiW7iIooi8Xh8T2ezWq2SyWS4efMm0KV3+yNx3tvSuTqbQd+oUF2yueqW+GAoyCePpDl5h97Dg+DVZE4NhTl1SwiwY1qcvx5k+1s3GFZ9CLJAJ9/mAxwmEv5enPrjhNfrxZueQH/jMvGagZLyYTcMaksVzl/N8MRwGI/6k6OEP/Yr//N//s/5V//qX3XlSc+c+ZmY1SvOF1nbqpKXoO+uAEyRut51mUqb9abBkOBw7LUhLv6v/5WZ8Sep1QwMQAECHpHYpBfviIstZ4vt89sAvaROURRkWUZRlF67WxCEPZWpZrPJpUuXbm3AFk3bIFBsIbhldAFUV9dY9OpWhe9d2qK4UgbbQQm4OHQkwaePpvCoMvV6vccx9/l8+Hw+JiYmejLJqqry7XevIWzqqOGDK13QDUzUgIazVmV2o8JY3Ed1s8r62+tsN3SqgkNyvoUoi8z8/AyCKHDzRp5LbyzjDroIjHuoZOtcfGOZmePPMjPTpZZmMhlCoRDJZJJD6QDRVyeZPZbi+koRo2ORDrs4PhxhKunHrUq0221EUewN7i7ll3hv+z0GBweZ/OIko+YoOzs7hKQQsktG1mSMpoHHJSNaOvlal8pTb5u4FYmAS8HsmF0RjDtoi5lMhtXV1T3zeAfBm/By9ItHuT53nY1LGwRiASRFwmga2KZNZCzC6MdHD6xg7342u7i+UeR7l27iuVHAdWUD0klkzUV0S6RRN3hdkhgIe0g9BP1JEAS8cW/PH8qxHS798SWGDo/wF9eX8IeilJs6a16FIcehkWs8dKInSiJDLwyhSwKlH6wSbOgM+DUsByRAdQT8QwFSJ1OogwHsv7z+0JYBveOXRPTHVO37aUNpqcR2rk7OsUndtcbswq1KdLwalbEQE0WdQMvgw+s3kWcmKSldBUjRAn/bICLY9I/4CJ7wUmgVKKwXsCwLwzAQRbEnV70LRVFIp9M9245sNtub61VcYHYM2vUOrZqO7Jbx3kVN13UTSRSR7voMq9Xqnuv3TsXOs2fPYrQNLr13CTk0gtGXxqPun0jIkoBLkcgINgNrZRo7DbxJL+tvr1PP1KmENEQBnI0KG+9tMPW5bsBSLDT4xn+9TG2njuJSeOsvrxOOeDh5Zhjbttna2sKyrB5rwh1x8/SvHuPwizVatwzT3VE3/rQfQRQYemGIfC6PuWLS2Gngjro5ffo0gUAASZZ6lEjTMtEbOu1yG0/EQ/JEEkETkDo1Im4Z3XIYi3lwK/de87Zpo7gUsrnsfcVzwqNhtkPbNHNNvAkvbrebXC5330SvWWii+lTCY7f3lIMKTBsbG8zPlnj9L28gmTLekJdnPzPFCx8b2++pHxqtYgsc8IZCfOvaNg1LZKfWIX442fUHzDcfKtFTvWrX+P37IoWbBSRFwpPwoHgUBKErZtTINTAaBsGhIJImIWkSes5AeoiZQ1kUMC0HWwDBcn4mE71CocF3/nqO/GaV5FCQT3x+hnDYjWEYXL16lVOnTu37d4Zh7BE92YVlWbTb7T0+arsJjiAIRKPRXnKi63rP1LtaAbvdxFMGVZOw6waGJKJpEl+5tMX89R182w20uo7pVng7WWSt2OQXTvUT82mkUik2NjaYmZnB4/Hcw5BaWFjg/PnzACwvrxB/4rMsf2+ZWLmNhEBrtcq7bZOBsIfpu2aSr2yW2f5gk3ihjRx2Y9V0ShcyvJv2MZnwceX8FrnlEulDsd71Uc7UyczW+MwvvIwgtDh79ixut3tfm6mD2Fdra2scv2XC7ff7+a3f+i2uX7/OzMwMLpeLK1eu7En0wl4Vy7ZxXBLqUJDOfBEkAXUshH5rTjJwlyehYRhcu3aNiYmJh5rfE0WBz54aZinfYrMlkq128HsVXp6Kczgd2NfzcBd3znZDV+39/3xnjvc/3MJ/LYurZaMEA/jdLpqlDt+2u526/scUdsnVOlR3OviaJgy5UVwqbNaobNXZrrQfOdHLbNdYvJkHYGI6ds/s+kGotAykpoHkVRFdMoIqoVbatGo6jY710Uz0/vAP/5DPfe5zfPnLX37kAe6fRphtk+xcjixOl/t/QLs37FUo2jpvfOsDfrD1Tbbr2ww8mSaUDNJpdhAVEU/Kj2vYjzvg3jMoLMtdfyuXy4Xb7T5QrOBupKer/LfmRZYubCNWHJxBP588FqVtWHzrw03aZ7eIVjqIAhiyyMVKG59L5rXDKba2tjh+/DjLy8t7bvAvfOELvc/tD//qOiVzEfEhzJlFr4Jc1ykUu5SvTqVDu6GzoBvs1Dq44j6ixSZm20TxKFQrbay2SWC8W+UNJn1sXduheot2syvcUiqVmJubw+12Mzg4SCLg4qWp+L6CN+vr6715w3w+z9e+9rWekuj6+jrj4+O0221s28YVdBEeDbN2bg0rYPHsZJIr23VqLRNNFjk2ECLsVaisV/Cn/T2J9NnZ2Z7c8MMiNBzi6C8fpbXewi25sSyLwECAyES3k/cgmtKdmM+3cNUhXBPo9AUJjiVpt1s0a21YyLKtGpxPanz21OhDP2cPQrfTR9Mg7PeSq9bwejyokoRjW0jyowU5P3h9ife/OU8z30I2LfqGQrzwiXG8AQ1v3Iu/348oibQNC8klYdXvVWO8H2zTwu376Htc2aZN4WaBHcdGksR7Ksp3IuBSyOgmi80d5ufPUiwVOTIax6h2lXwVRSTe76PveJroRBTljntXVVWCwWBvjXlYGloiUef/yLzDznIBSRU5+uIQo2PdpMAybb71tVmuv7uBpIo8/doEz74w0vtbx3FIpVLs7Ozc42eZTqf5tV/+NfLRPO/v1Gg8gErj1WQaHYtmw8BoGlgdi3a5jeVTubRZQRIFno35aOabOLaDIAoUCi0axRbR4TCqR2HrapZCrtupE0Wx5x+ayWRot9u9GdnAQGBf/0hkSDyVQJlQyF3PUVooIbkkQq4QsUQMvabjkT2sb68jyzLhkTCRyQhNs4lVsZga7WfYsOiYNn5N3vczaJfbdPo6jAfG70undIfdDDwzwPJ3lxEkAcWlUF+vY2xZ2Da43AqeuBt32I0gCrRLbdrFNsMvD+M9QMF4Fz6fj2bD4cL3bmDXIDIcoJSr8q3/eg7TqTM5HSUWiz30XnUnBEm4JQZjUK+UkAIxXMqt696hR3l9GGgBjfATKa6ulijdzONZFBjqDyJKXT8/X9pH/OU4oZEQ838zT6vYwq1K1B9C/MowHVRFRHS6qrWPclwfBZimzZf/+BKrl7Zx+TWy8wUaNZ0Tz2n8h//wHzAMg3/9r/81xWKRZrNJu92m0+lgGAaSJOHz+fB6vXtiGUVRCAQCuN1u3G73voyu/dC3UeZP21fYvriJnC3QcSnIp9JoLpnr13LEb5YQWiaSX0Uut9EqHdYdOBty82Ra6cUKuq7fk0iFQiFs26a/v59UKoUhKCwt5AmUO6hpP6ImQaZOY7nMUq5+T6K3VWqhVXXkoAspqCF6ZNxbNQq5Ji3DolpqIezunbfgDbkorlUoFRpMzSQeKNxyN9bW1nqiUrsol8uEw+He+U4mk2Qymd4s8mBQwW7VyFQU+k4mUQYCCJKAEHGxkm/SF3IzfAcLo1KpsLq6euA83kFIJhIU8td59onDPXr546BpieQMjfGWhqF48ExGsG2barmGfa1M1qzzuqvDLzw9+Vi0ckUSERUJXyBAcSdHcqAPx7BBER9KyOxOvPvWCj/48g2ahW7x7+2om5d/4TBPP78/NfdOJAIadsiFuV7rjRy13DLhuIeA+ycr8PTYr67rOp/73Od+JpI86CZ6rYZBRxTw3kfwQpMlLAEWl9awJRtFU4iMRXjttddwu90/lvMxlQrwD37lCW48X0bXbVIhlZBd5evvXKR4s02q1EYZDCDIIkK+iX+1yrXFIk+PdCk5+9F9fujjvHX/KB4FVZVJOSaW5uDXbTSfhqR1z2Ew4kLxKJQzdYIJL6VMHc2vEYrunZsIh8OEw+GecIskSQwPD9+zMOXz+T3VLdu2e8pYuwbO4+PjjI6Osry8zPj4OE1PE93SiakxfFEfiaCHltGdAdAUiXapjdWxSBxL4AgOH3zwAYODg48klevYDpX1CvaWwrnvX2d8XEFVRIYibuKK9MjBw2a5hadpds+zInbVAj0e3B4PertCVHKTremsrKz0ujMej4dYLLZnwzUsm+1KG9208WkyCb/K4uIiFzcucu7PzzFweppkMIZXbkK2jRpLP5IU+sJ8nrf/ahYHSExFaNV0VjYrDLR0Xv3Y3iTUpUgMzMSY+/oCgYfcOGzdQgdGH7LD+NMMS7ewdIumA64HXA+7t2euUgYXNPoajH+mj6NTR7sVdFXCE/P8SIPSWMzH//X/+XG2t6pomkwwJLOxsYHjOFy7nOeDr66heVVaVZPv/8V1+gaCDI+Ee/ekx+NhZ2fnnucdGBigU+1QFIsPdRzOLZ+63atDUiUUj4Kc7aB1SvT1pRBbJlrqtlhLPOHFn/CSXy4hqTKKRyWevjfAulO4ZWVlBZ/Pt69wy8bGBsNjwwjjAomjCSqrFfI38tQX6hgNo6ugmfYx8MIAMjLx412biVAw1BNn0RRpX387gFalRbla5rlfee6hgpvE0QSdWodL/+0qO6tlivU2HVXq2v0AEb+LWMxLKOHBHXYz+Pwg6VMPt35trVepZGrExoN4/W68YTdb13Yw2y7S6TT5fJ5Op9N7fCgUeqC6KUCmnuHcpXMU3ilw9NQxZL/MxEAMu2kgqRL+vodfZxoNnb/83y+zeW2n203uWHiOJXjl1QlkTcYT9/To98HBIOWVMoNRN1ulFrZ9+37a97l1g4lEGL3SIXE08bPXzcs32FkuER4I4g25qBaazF9Y4T/98R8iSt1Rka2tLcLhcM9C5VEK0Y+Co31B9M8f4f3RGI1ym0RQZSwhcnZpHU/ORmyaKEPdwosUcmFs1QhmW8xmqsRMnSdPHSMajTI7d5NgegTLdkgFXb1uWSQS4R//438MdDss//kr1wDn1qLS/RJRQNjneoj5NWbdEnalg+CWscptdFkkFtDQZJHwLUqyqVvIt2LEeqmF5leJxW/fw7vCLbquMzc3R7PZZHx8/J51xjRNarXaPZ38crm8x8M3kUhw+fLlXjdzfX2dn39qnDcWStzM1wm4FBwHajt1kgEXr94h9LKxsUGr1ep1DB8XP8zMarGh0yi2iDV0Wj6xW3wUBTxeD7pVRdZ81GyV1dVVGo0GjuPg9/tJpVKEQqEHvnbcpzEwGmBhuYSy1qQyl0UP+whORffMVz4IuZ06b335BmbbJHWoS0UtrJZ58ys3GJuKEY8f/FyO4+DulBDTFjnTi6vYxlQFtCNxnj+W7gqN/QTx2Ine5z//eX7wgx/we7/3ez/K4/mJQRAFRElAdLpiKgfBtgHH4ROf+DhPf+n/xpXNK8zMzPxYBtzvxEjMy8ieizbJfNvL2vk5BFXqKRiKfg0136TR6LC4usHMcFc8JJFIsLS0tG91KZHwkpUl7IZxj5DK3bDrBqYmEYvc6tANBUmfTnPu//tV7HKN1C9+msHnB3ub7vRMgideneD8dxbZvpFD82s8+emJXqfgbng8HqampjBNk9XVVSzLYnBwELfb3ZsxuVM9NJFI8NnPfhZBEPj5n//53mKqaRr1ep1Lly4xeXqSZLir/Fa4WcAT9eBydd9vsVBBlEQGnxvE1e/ivffe4+TJk4/0eVqGxfwby3zwvWW2yi2WG1DNVZARuLZUJPXWKk99bIzpj40+8PzuQpFErFu+NcIdVandpM4RBUJ+LyMjt82wm80mm5ubWJaF4zgslU0WqwK5YguzZeIKaAwnvFz/3ldZu3oRf9jPVLif2dlZ1gtFtEMTnPnkSWzTprRcQpREXGEX2n2UZZduFmhX2/QfTQLgi7hpV9vMndva41+2i+On+5n7wSp6qY0WeTBVo5ap40v6OHbsp9Of5lGwa6ItA23n4DVmF47jcPr4SZ753K9Q8pZIxVOIuohe13HsrgekK+TCm9x/rvZxoGkyI3cIfOxKm89eaeGYNpHBAI7tsH0jRyHXYHikq765e98lEgmy2SzJZHLP88puuUslFAQKugX3ub0aHRO/IODxKiheBUEUGHhmgBtfu0Hj4g2cuoH71DT9T90WRgqF3Hz+N0/y1jfm6bQMjj49yPGTBw/R71IYdz1JdymtgiD0aKi751RxK8RmYniGPISfChMOhXuqmaWlEt/9j99F2pZI9icf6nOo1+vkFnKMnhwl9JCzKUbTILdeYanaoiwBmgwdHVnT0B2H9UyN7GaV4ckoL7w8Qv9T/Q99TXSDVgfs7pptGTaO7aCoXd/Ouzu05XK5568It8S22gprSyUAhkbDJAaDHH32KN/78+/hbXgZDfSho9PZrFBtdhh7fuy+9hl3Y3mxSOZmgeRUFMUlU1yvsrhY5PO/HUC+q9gRHguzfX6bmCwR8qrk6m2Sfte+s3qFuo5PU0j7VIS29TOpuOnxqCguiVa5jSeg0S63iSYifPNbf8M777zJG2+8cY+B9Y8LoihwZiTCsYEgjU7X39V2HG5WF7FurCPc1fkQvSpyy6DRMrDF7t6Zb5h8a65O652rWLZDfDjIq6f7mUjsvZ6CboXx8SgXZ/PI2zVkQaAtiUjTYcZi98ZBR/qCXDmVJv/BFp5cE10SsA/HePJQAlkSOX66n8vvrrM9m8cVcmHrFpZp8eRnpvadY1ZVlWPHjmHbNouLi8zPz9Pf399L7Obn5/dVQq9Wq/f8PJ1O89WvfpWZmRmeffZZAJIhH9e3qqwUmkgiPDUS4XBfgJhPw3EcZmdnCYfD91VbfxBCoRDlcnmP8NajQhYFBEXClkQwrLuUlB0sWSQU8HH48O2Et16vk8lkev6KqqqSTCaJx+O9QnYul+ODDz5gbm6OrXyFJ77wD1mZC7KxkWH61DAfP93/SL56izfzNIotUjNxxFsxV3Q4ROZGjuX5woGJ3nvvvcfFixeZm5vji7/8ayjPjbGyU8fnUTjSF3po4bwfJx470fsX/+Jf8Ku/+qv8/u//Pr/7u7/L0NDQvq37j4oap+pTifYFCK9XyLTNPWpPd6LWMfCYDrG0l2A6yKuHX/1bPtLbiHhULLeMrVs4hoWgSFjlNh1ZwBt0IVr6HtNMS3Lxje/fJN0f4fh4tBcIHB4Jc2XAh75cxRU6mOrl2A6dagfpcJRDtxSdBFFAHBHZDG6iRTTGPzeOP7Z3wf30Fw8xdTRBtdQmFHXvCSQPgizLjI+PY9t2z6er0+nsURPcRalUwuPx7KmCra2t9eYfPR4PniPdSndhvkBpsUS71EZURBKHE0SnojTVJjdmb/DMM888ksGq4zgsvrnK61+9ybYMwX4/fd4OqmITiUaptw2WSi0aX51DFGH6E+MPJRk/nfTznUABl+wg151ugueAudNA8GuYYY3RuzYrj8fT20Q+XCnyztIaxrUMnnwT1bQxNJlrgyHK/nHUeIbppwY488kzFP6yQO5CDtNnkpvLM/fmKoZpI4sC4biXxEyMyGRk38BMEOhWSe88JzYHvsep0TADx5OsvbNB0qUgew5egjqlFvWGzulXx4g+xCziTzskTcIb9xJdKjKvm4QPsPGArvKcIor45a7xdtyKk/1elkau0aUrIuDgILtkgkNBYtMxQiOhhy4kPCoSKR+SSya3VMLULTwRN4mUH13X99jDeDwe5m+uk9m0GJ+K4rulaCkpErFDMcJLRRTJpt4x8e2zxhqWjW7apG/RIXeph6GREJu+TcRJEb1fZ/oL0z3Rj11MTMaYmDzYVmE/7JqT67reS146nc6+wVGlUiGejPdYBrZtU7JLJP//7P1nlGX3ed4L/nY6Oac6lXNXdXXOyJEgQYAiKSaJih5Loin7WmN77Luux2vp3jXWmln2mjUz/jAzluVlSxrpWhJNkWIQSBAZIIAGGh2qQ3XlfOrkHHecD6e7uqurOqJBgqSfD0B3n3P22Xufvf/7Dc/7PHs70BM6Zsy87fnP5XKoOZVIZ4Tuo913tBZYlsXsa0u8/dYK+S43sYkoYlOnulHAYXMAFoIkUZJgpaExO5mkY3/HHZmkA4zsihAZ8ZGdKVNzNVHrGtGhIBMHdi6uBAKBzcDP0Ax+8I1zfPD6Is2qCrR9Cg88OsDYEQ9f/79+nXe//y4BW4Bissip86ewddl4/JHH7+jYr0IQQBDZtI4wrTalf6ctuKIufP0+Lr9xmVYlgS02QqJYx++y4bbJILQtSYoNFbsssb/HD5k63h4fvu47s7z5WYLXZ+fB58Z449tTbFzO4vLbeeQzY3i9Tj71qU/xqU996ie+T3ZZ2uxymKaF1yGT9ijY880tNEGz0kILu2g1KowO7kczTF44vU75VJFAWUUEsvMFvt/U+e1P7iLg2rqmPjHRQa2psXgxjdnUcUXdPHq0m5Ed5ra6Ak6++PgQ73Z5mZtPE48HOboruimp7/XZ+dLvHuWdVxdYmsrgcNvYc6ybB25D6xNFkdHRUUZHR1lbW+Odd97BZrPh8/m20U+BbTTJdDrN7Ows0Wh0i39yT9BFzw7+faqqcunSJUZH740KeT3i8TjT09MfKtGL+x3Eom4KIQfWiopZUxFsEnq+CQ4ZPehgV8fW3+OqlsRVqKrK7PI6Pzxzko2yhsch0e+TePutt9B1nV/5ylc4cqSf1EQH6WwnQrPMcNiBrut3TFdtNycErq/BWla7m7lTB/gqdu/ezfe+9z36+/t5+PhhFEXh4ZGfrsrmjbjnRO9qwH327Fn++I//+KbvM4yfDREFQRTayonnk6Q1nXJDw3eDpLmqm1QbKiOWQOd45I4fpB8VdsW9vD8eoVhU8SeqSIJAyyZSGwmwr9NFNLj15/3xu1nmXprHEXFh//3jjF9RLRqMuOmaiJJIVImsV5G7PduSPcu00NYrlF0ygxMxekPXFpiLly4i+SUsyWI5uczeyN5t+zo0fG+VUlEU6e/vp9VqMTk5ydLSEn6/f0tSd33FqdVqMTc3R29vLz6fj/n5+U0+v7fLi7fLS/fxboyWgaiI2Nw2Ll26hNgUOX78+F3vXy1d4/Sby2xIFh0d3jZf3PSRSacJR8J4nQpOm0wyVeb0m8t07+m4I8rSRJePyYEAiVwZ11wdfbXSzqe8NnL9Hnp7/Dc1By01NN6cyWCfLRLKGkghP4K9TUlxzOVp9Ljoe+R5DnYIODodfO7XPkdQD3Hu3BInHTmKkoBxhTrnW24RXy7Qe97H8OMDRPdEt1wbuyZinA45Sc/n8cc91MstKpUWro4O/tMbC5iWRchtY6LLx3DEg9as4Qml8O3ykprJ44+4cEddCNdZdZiqQSVZpVpTGXmsn09+8t4rkh8nCIJAZDxC9HyS1Za24xoD7YdLoabSYYHPpZA8m6RZbGL327Gibqq6gWm2Tb39gkhhsUB+Nk9oV4iBxwdu2YG9V+w/1EnpixNMvr2KYhd58JlRenr9rKysbM6/QVus5c3vJ8jMFxg53sNvfv3aPRUcDBLv8jK4Xma2oaHpJn5nex7astqdvGJdpceuEPfYieyObLnWjj95HF+nrz0vFLq/ib/NZqOvr49EIkGr1WJ5eXlTuOUqrg8aisUixWKRwZFBert7efe/vUthoYC7w40jsF3UyjItEmsJlKaCx+1h4IkB/H13JmBWz9SZOrNBziYQD7nbM24eG7ZuL7LNvjlTGwU2ig2mZ7LsnsvTc2y7FcxOcDhkPvmFYeanmmQTFQyxxac/f+iWVKWreO+NJU6/soIt5CY20u7iFjJV3v77KXRhCKfHyfgj44RCId59+11atRaGZpA8myS/VEDXTDwhJ+GRML5e301pk0MjYXomYqyc2wBBQLJJHHxu1zZRoKt4e+VtEisJXIaL4w/GWCjUWC82KTXaszd2WaQ34GI46sGeb+AIOul/tP/nbj7vKh5+fJDegQD5bJ1IzENP78dHPE8UBfb3BPh+RwFPsYW2UkZ0K1gNHcsuUY67GPALhHxulnM11mayBMotnL1+EAUCSyVyczmWjtQ5eEOiF3Lb+NWHBlgdj/HNb3+XJ/Yf5thY7CZ7Av1hN8sXP+Dywpv8H776f96mDxCNuvnsV+7dw6+np4eenh7eeOMNDMNgcnKSsbGxHWmypXqLH75zDhWJ4cFxQnaTxcXFLSbiN6JYLG5aMtxNwfpmaLZ0VpcruJwFevv89zTy41Akjg+G+H6uRiWVJVjTEEoqmkum0ONmYCTMWPzWBZaWKfDemsni6Rb2fIOsTWK1W8YVHyYzexZN05ifnyMejxMf6ubP//xlvvd33+YrX/nKNiXjm2FkLII37iazWCA6EAAsMotFvHE3I2M7J26GYbC4uMg/+2f/jHPnzm0pen6c8KFUN3/evGaCQ0HGDnVRe3uZhaZOotXu7IlAXTUwTZMeDcZ2RejYd42a1FANppfyXDqfolxpYbNJDI2E2LM7RvQuWsd3i4jHzvNHennJLpNaLGDpJq6QixPjUXqlEl1d17pflmXRqKuILQO9odG8TsXQLks8+2A/f1tRyZ5cw79YxOZ3ILqVNqOnpqKWW5ScMpGHe/nU0Z4tQhLPPvssoVCII0eOfGTXxMrKCkePHkUQBIrF4hbhlkKhQCQSIZFIUKvVmJiY2NyPwcFBZmZmGB8f39yW4lRQnAq6rnPy5EkGBga2Uc3uFMnpLGupCu5OD8qVZMXtdLHWamHoOpIsI0sCnoCL9USFjcvZO0r0Ih47z+/r4i9yObI+GQM7umEiRVz0d/l5bl/nTbvO85kqpfUykUwDOe5GvKIkKsZknKJAT0NAtYfZdWCYxnqOyb89z3LOoD7YT0JvEXW7sSkyhmlRbmhc1E3yGyXUF+cQZZHI+LWuSf9AkKe+vJcfvzBDPlEhrxnog34cfjvGRgXBssjYRE4vJEkuXEZJXaS0vsD//f/5/+aN19ZYOZOkdDmLTRIRJBFTN1Cttnrs0CN9fOq5XTh/impV9xv+Pj89oxHyZxPMcsXuw2nbHBpvqAaFmopfEums6jSNJopTQYu7mcnVSW6UaGnmpsm32y7TH3YzEHGQncpiqibDnxre5jH3YSGKIo9/YoTHP7H1oWlZ1paHv26YaA0draZRr7S2vNcZcjLw2ADGSwvI6SprlkWq0mxXUC1wyhKjdoU+h0LfiZ5tyq99fX0IgrBNvOB+4apa6dVuXiqVIpVKEQgENhVELctiZWUFj8fDwMAA0FaF3P38blIfpGglWuSmc9h9dhSnAiK0ai1SKynC4TD+Xj/dx7sJDATueL+ycznWNsrYoq4t667T6WrPXXuvFXxCHhuFksrMBwni+zvuWADK7bHx/Bfa3YLV1dVtPmo7wdAMJn+8guWQCF1XkQ93eNCbBsmZOvEvdiLbJfL5PCOjI6hrKi//1Tt8L3mKkihjCeACunwORvZ1MPr00I4FVKdT4cu/c4RT76zQqGl0dHnp6vLyyvenKWaqSJJE72iI3fvjOF02HnrqIX5Y/iE9Wg+kqow5FUYGQjSvnD47AkpVRU1WcXV5GXxyEE/8p0+x+ijR1x+krz94+zf+FLCv28/S7hgXAEeyjr3SQve7qcZcxHucPDrW3m/dvKKKinBl1k5oi/4YYJg7WwookshQ1IOeXeLl7y9zYOxf7NhJg7aR+BtvvEGrUafRqH8kIzlra2vs2bOHcDhMrVbj4sWLmKbJ2NgYTqcTSZJ4f2aNv3n7MrI7hN1hZ/bCBm6bTNgq09Pbh03Zfl+vrKygquqHnse7ilyuxjf/9DTLFzZ417HB3of7+NyvHrhpceVWONQbwLIsvlnLk1P8WKqBaYO9Ix08NR7bkd1xPaY2Kiy+u0Jko4bst2PWNApLFl3PPsY//51fxet2UyqVWF9f5/33399MpNfW1hgeHr6juDQUcvH0F/fwyjcvkp7NtW18Ojw89cU9BHdYk64m6nv27MFms/H000/f9Xn5SeGeI6j/7X/73+7jbnw8INtlBp4caP/AZ5Okqi3yqooJRIAOm8zAnjAjTw9tUormEmVe+PZFclM5xIaGLAqYJiy/uczJHh8PfGqURw9337Np4+0wEvPQ8+gQq3vqqIZJ1GMn4rExP1/dcnELgsAnPznKWy4b4Zib3YNbF/zeoIsvfHKUF/0O1i9nsFbKKFcCNc0mIo6HGZyI8qljvTsaXFqWdV8qSDshn89vej3BNepQo9FgdnaWixcvMjExwZ49e5A9Ib43uUG5odETcnJiMIzD4aBer2+hsVYqFSYnJzl06NCWf79brF7OUJcEotd3ZkQBr9dHpVIhcMUyw+eQSckiy1MZRp8cuKOFZzDi5uk+hdZoFEPxkMtnibhkHtrbe8vkp9bSoamBYW4meZu75rFhKzYpV1QqdRX1UolMVSLv99DjsSML0Gg0qLXalW+HIOJx20lUNaRcDe/bq/j7/FtUHo892MfQRJS/eW2BRqVJvw7CVB6j2ATLwuWyEe5yMtuAoh7iuSdH6euO8uu/FmH9EzXOn0mQWC/RrOvU8w0oNjBaOmvvJ/izC2mGD8Y59uggvf2Bu/hlPp6QHe01xlANlMsZ1k2TjN68wn61sAkC3UCXbmGTRdydbvJOmTMLOVTDJOC0EfZcpTy1qeTn14qkynaO9gbJz+dR3lIY/uSdPdw+DG4URgJwOW0892sHWJzNMXEwvu0z4V1hRFnE9d468bUS+YqKLsOFs5McPnKISJeX+ME48QPxu6L23Q+srq5uEUa4WvwpFAosLy+ztraGpmn09fVtowNFO6PU9tYYeWSE4lKRzKUMelOnUq6gmzoTT0wQ3hXG1+27665RYr5ATRTw3XAv78RbtMsSpksim6zSKreQox9dkaRWblHON3DtQKt2BexUCk2qpRaheFvwJrmepLXiILznBMuKgE0wEAWBkm6SqFRJv9XENCwmPrNrx0KF12vnyU+OYpkWr74wzct/dZ56oYFgk7AMk7OvL3Kyz8/RT3Sya08Xv/3138ZoGRhZg8ylDJVEBbvW9pQUJAFbxIVnPIyvP4A7/tHO2P8P3BoOReIz+7voD7s5t1Zsz+kqEg91+bBVk4xe0RqI+xyE+vyUF4v4l0sgCdRlAWe3l07/zRlW6XSaer1OrVbj7//+7/n85z+/4/vOnj2LaZqIosi5c+d46KGH7utxGoZBsVjcZEG43W4OHz6MqqrMzMywtrbGcrrEolQjGO0k3rQg10SwSdQjCktVO9955yJfeuzA5jYty2JqaopIJHJTi5Z7wVsvzbM2maJjOEIhnefimysMT3Rw4PDNZ59vBkEQ2N/lRdvtw981xFK+zuTUDAGnzHqhgVORblq4BtgoNVAKLWSfHTnkxPLbca+WyWXqmKINQRA248Lx8XGOHTvGH//xHzN1cYHkmokgQt+Aj/6BLmKx2E3pnAePdDM0GmZ+um2vMDwWwbdDvKvrOhcuXGDv3r0f2y7e9bhvTwFN03jnnXc4cODAz7Snnt1rZ+TZEaITUbKXs1RSNSzTxOlzEJ1oyzfbPO2H0HKqwrf+/AzN2TzRuAe5z78ZnJg1jfJKmdf+6jyWBU8c69nyPZZlsVpoMJ0sk6m0sMkSI7G2X8utLvid4FAkRjuudYlWV1fp7t5O2xnu9jP8Kzev9vQGXfzWM6MsH+7i0mqRfK5BvVgl6JA4MNrJ8EAQ+w5Us48a2Wx2R4NPp9OJoihomkY4HGZxPcW7KYH0xSyOps50zE25ofH8vj6mp6fZvXs3AOvr62xsbPDggw9+KPVRy7LQVBNLFJBuCKq9Pi+FfGEz0RNFAUsEw2gLHQh3KPtrqg2O7t5F2ZA51VJ5aXKOhYrM3t4AB3sCmwba18Mmi1iyhCAKWLq5KdQDYLV0jCt+gVqmTmqhwIYk4HcqmwqB13cILNOk0WjiQGOxoeO+uELwYoDBY4O8+uqrOBwOHnzwQebzDZKCxUDdxLiYQXDKyDEXiAJmVcWcLvJUdy8vIyB1jWzOIfR0eOh5dhemafLCt6c4fTGNZVj44h5kRaRRaXHu5fZMxC/91iFGb0Kh+FmCO+pm9LlR3B1uembzpFMVWkZ75shjl+nsD2AJFuXVMs2ggzMLOSRBpNO/ld4jim3BAa9dIVlu8MFqkRNdPvJzeeIH45tWIR8VrhdhuR5jEzHGJm5OjwoOBfH3+SmvlcnP51k8s4hQLZJKr9C79yiSTUJraPe9K3krVKtVXC7XjutBMNgWnBEEAVEUSaVSGJqLl78zBcDTn91N30CwLe6i1YgfjNOxv4PVpVW67d2EY+EPpeSoawaWKCDukLiLorgZmF77x2tG7PcCRVF2lK+/EXaHjM0pU2towNYgW23o2J0yjiszuFpdY+6dVaaLdUyfnf4b6MWNlsZCpoz1+gz4dWL721L1V/fh/PnzdHZ2EolE+PEr8/z429MofhvxPbHNIqqq6mzM5nnzuy0GhnqIXpXPj0F4LEwtXUNv6mDBxUtp3r2YIv/eKggQ7fNz7Imhewpi/wfuD5y2NsXvSH+wbS4ui2AaLC4WNt/jtss8fbSHH7R0MnMFBNPE0ePjkRO9dN3Ch800TT75yU9SLBZ56qmnbvq+Rx99lHA4TF9f3xba9v3CzQRYbDYb/f39nD9/nqoSQrJ76awY1N9PgG5iWRa2oQCB8TAXknmebbTwOO20Wi2mpqbYtWvXhypY74RyoYHsUHC4bTj8NhobGrVq6/YfvAlqtRoOl5tz6yVmFgukL1dophexfDY6+4M8t7dzy0jQ9Qg4FXSnjFVoYXltGBUVVRZxu5Ud/QnHx8f55V/6fV771kVS9SKCIJDo1NGfcZBKpdB1vT1KEYkQj8e3/NbVap6xPaHN87mYrXFmKU8qV6cz4uJAn5/i2vwdJ3mVSot0soLP7yD6UxJmuW+JXj6f58knn+RHP/rRLW+knwVIikRoOERoOIShGW3PH0XcUiE3TYtXXl2gPpOjYzi0vXPiVvCPhGAuz8kfzrBnPLJJ4zRMi9em05xcyNFKVXGqJrogcD5gp7fbx/P7Om9ZnbodGo3GPd/0iiQyEvMStwRyVZPv/vBHnF9N0f3pZ9HCbiITESK7Ith9dzYHVG5qtDQTuyJuM/G8E6yuru5I1TJNk9nZWRqNBt3d3Rw9epSFgsbGu5eJL1dB1bGXNS53eHh0NIrH46FSqbC8vIzNZrsvKmOCIOD02pAMi5ZhbJHQ9bg9JNbXMQ0DUZLQdBPJAKdb2VQkvRPUajXyLYFvn1ykcCaFtbRB9pLKD/q8rJ3o4XMHezallK+iL+TCFfegrlVho4rc6UGQRcyGjlFoUen30dnhwZ5rki42qAsmXTcpLgiiiMvtwuUGtdBAsyA7lYUIXLhwgXw+z+z8Is3e47gbBuZ8ATFgR7ru+pD8DkSXgp6o8eShcep2N8lyc8s1Pnlmg9M/msMZcOC7bjbI7rbhi3pIzWT54V9foOdfPYLzp1BsuN9whpwMPzNM15EuyutljFZbjczmteHucDPznRmcISeTmRqaYRH23/yYRbFd6U6Wm2Q1E19DpzBfwNPhwdBNzn6wzsUPElQKdcKdPg6e6GV8z80TsTuBpmkfqpIpSAJqTaW8VmZlZgWkdtU9OZ0kP5vHGXLScbCDzoOdm8WzZrP5kQRf0P7unWZf6vU6yWSSWCyGoih0dHSgaRr/6f/xGqmLJRAEXhEF/sH/9CA+n4+lpSWCwSCLi4t0dXVtCkJ9GLg8NkTDRDMtbjzjLqeTar12TU3Zas+4Ki4H4g4m7beDaZpsrOm888Y5du3uZc++7V3Zq7C7bYwf7uLt703TDDlxXLkvWy2dZqbG/meGcV2phheXi6ytFKk6JLo9258dTrtCJOSjlm+gpyEcDJPP51HVtufm22+/TavV4pmnP8WZN1YRnBKhG8ShbDaZzrEoyctpzr23xic+u3vzNVESN8Wk3nlziddfmMHQDbwxD5ZlsT6VJbNYRNN0jp64f12Rn1UYpkWm0kIQ2mMEt/L8vArLsshUW1jWnX9mJ0iisFnsnl2Yp79/q9jJRKeP2LNjLOdqGCZ0B5103UasKx6PE4/HOXXq1G3pmI1GA7f7/ikZX0WtVkOW5R3n8WZmZqhWq4yMjFCuB0E1UN9LIsgCcpcPs6mjrVUIjISoOb2cujjD/qFu1tfX2b9//0di69XV72f25Bq51RKVfB13yENn9703carVKqeTGkuLJaILJaTVMt6Uhei2kcw1+Z5p8RsP9O9oxj4W93H6QAfZ9xK4Nypokog6EuCxiY4dE73EWol3vz+PZMpEdwWwTJPMfJ4zr6b53f/5EVxOG6ZpksvlmJ2dpdls+zr7/X7eeecdyuUyX/7yl6lJHv721XnqpzewqyYpu8TZMTf/4PkDd/T8e+OlOU69skCt0MDmUBg+3MlzX9qDy/mTK2LCfUz04Jr0+88TblaJXc3XWJ9M4Q84tiV5VyGIAr5uHxtrZS5eSvPElQfI2dUCb02l8C+U8CdqYJhtBQa/g/WqyveBXz/ev2O35naoVqsfmleem80x/8oiK6slJrNNcqIDfWaF0UiU2FyOwbEcw88Mb9JXdzLTXMrWmFwvMp+uoRomNllkOOpmf3fgBpuIm0PTNHRd3xYsFQoFkskkIyMjXL58GUVR2gtzqYSsyCC2T6clCjQaNfL5PH2dnXzjG9/g6aef3iYX/mEwcqCTs6fWKdY0OvzXfi9BFHC7PVSqVfx+P4W6ig8YOXR3FWPDMHh/OU/xgw0i6QZ5RcRvWrjmi1yyS+ztDrC7c+s8TdznYN9AiJOFBoH5EiSrWCagiNS63DAc4PhACO2DJA0BJFHkTp5pdptEuamjWAr9ff04nU4OHz5MQ3QyX6jSVdfR6hq22PYig6BIoIjY0w2yISfrhcbWRO+9NUzN3JLkXYUoCUSGQmQX8lyaTHLkxEczo/XTgDPkxHmDzURhoUA1XYWYm2SihP8OEltRFFAkkeVcjWNBN9npLB0HO3jhe9Oce3key2x7XmbnCyyc2eCxL0zwyJM3H+q/HTY2NraIsNwtkmeTLL+xjOyQ2f/EfgZrw7h9AZw2BVkUqOfqLL26hNEy6HmgB0EQtviA3ohyuYmuGfj9zrueIdnJ5B3axwgwNDREKpUieKU7rygK/oCfFCWwLOr1Gqurq0QiETweD6dOneLo0aP3LfjqnYgRenuFVF3FZbshcbxiPn4VNVXH0TDoPRy8J7GwN15e4M1vXaJWrHHpjSSlX9nHQ48O3PT9Dz0zTGqtxNKFNMaVw5UMi8E9HTzy7DUWRrPQpFDXsAfsN11rvA6ZtCKSS9WwWtam4JZlWbRaLUzT5NvfeJXWWpTIyM7iXpIsYPM5mDq1zlPPj20rqtVqKu/+cA4EiO+61o32BNuCUu/8cI59B7uw3yWr5ucJLd3g+ydXuHw6AcDuo908f6xvW0HxemSrLV46v8HSXA7LtOjsD/L0gU56d1CFvBuoqrpjYhTx2InsUDC4H/gw5uC3wvz8PPv2bRVyaTQanDlzhsHBQXbt2sX7779/Zd0w2nHMVauvq7ZKgoUkSqSS66Q9yrbtfVhYlsXk5CRzc3O88caPeeDxL5Gcq+MOOZlPv4uk7AfuTUl/MV1kraARWi4jNnXcw2F0y8JpSITXqySDRWaHwhzu2z5HGvc7+OXHhvhxzENyo4LPrXBkPMbRm8yczl3OUCs06NodvVIolIgOh8gsFFiYzbN3fxxRFIlGo0Sj11hC5XKZpaUlyuUyf/iHf8ie536H+qkiYdVEDjkwsg2yMzXOHSgxEL211sK50wne+NYlRFki2OWjWVM5/8oCdqfML33p/v5ut8N9Xc1+3sRZboW1lRJ6voHjNupVolvBZljMzeZ44kQfK2vr/P9eOE3ciCAvl5GuiGVYpoWeqhFZqbDmszOXqW7K+t4NEonEh/JNqWxUmHtpgQvrRebVFiVJxOtzk86lcIQCJCWZ+qU0oiiy67PtWYpGo7ElGTuzUuBHUyka2Tr+qoa7qaM7ZM6kqkwnq3xyooMDd+Aftby8vKXKblkW8/PzuFyuTRpmoVDYDMD6Qy56hkOs1FRsDQMt6uTBsW4US+Nb3/oWXV1d5HI5otHofbtWQ0NBBgaDnFnMU7XLeK5L+r0+L5VyBdnhxsg3GegPEB66u2H4lgGriSrusooUceE0BTSbhJQxsbIN1ouNbYmeIAg8Nd4OXM+HnBQ3qgi6CS6FYJeXR3bF2NPlY+ZU8q72ZfOMXZEc/t3f/V0AFjJVpt5dRtRUBPnm51V0yJh1DQET1bg2OF8pt0jO53FHbh4UKHYJy7RYXcj/XCV6O0Gra2BBRTNQDYOI7c4CGo9dptDQ0Dsk1FyF7//tj7n4TgV32IU3fO3cZldKvPPCDLv3dxDewf/pTnCjCMvdoJKosPbuGnafHVvQyVy6wlxao7maxGm30R92sSvmRbJJJN5P4OnwEBwK0mw2t80Ezs5kOfnaAomZHKZhEYi7OfBQP8cf6kO6g865aZrU6/UtiV6r1WJ9fZ3Ozs7Nde1GKuPTvzSOKLXVQp/89C46u33MzMxQKpXQNI1arXZHpuJ3An+/n76+AOnlAg2XbXsBUGgHhBYCxXKTXrvM0OGuO55xvKomapom595cRhIFYruC1FMq595avmWi5w04+crXjnHp7AbLl9tzLX27wkwc6sR5QyBu3WbNFa4eC9aWorFlWfz6r/864XCY999c5vt/+gG2WyRiNqdMq66jtQzsrq3XwMJslnKySnR4+zoc6PRSWCuzNJ+/JfX45x0X1ktM/mAWX6aJhcW5TJ2Bbj99bpOTJ0/S0dHBkSNHNt+vGyYvnFln9sUFAsUmgmWxfDnHdxsav/XUyG2FNm6GbDa7IzX8XlFXdZaKGtJKgbjfcU+sqbqqI4nCXRtgb2xsEI/Ht8Qdi4uLZDIZjh49umVtGY15eHU6TWgkiPF+E3WlDFjYR0LkBYtico1gv43FxUX6+/vvC2vgKgRBYHl5mffff58DB/by1X/w0CY1/H/9X1/nv/yX/8L4+Dhf+MIXbkvtvhEb+SpGWUSpasgxF7Ikks1m8cSiGMslpLJKotjYMdGDtpd030MDNDQDRRJvWXgwLcCytqyBV62gzJuI9kB7ZnLPnj309fUxNDzCX/54A6eaQw47EV0Klgm2cpNUrn7b4710JoHRMohd8Yy2uRR01WTmgw3qz4/9RLt6/6Ojd4/QNAPBtLbMP90MoiSQzeb51//6X1NQRdacQ4zILkSPstkNFEQBucONtlZGLDRZzN59omeaZnvQ/EMkMZlLGZbXS6wKEPE68e0exm6zYbPbURSFSkNjQTXwzWSJLcaI7Y1t6SKuFeq8fDmNuF4lNlfAqKgINhFZNYl5bVRHDV4SIeK1030LTn2xWMTn820Gk5VKhZWVFYaHh7fQt663VvA6FL5wuIfTYRf5aoPesJcOqUY6neWLX/wioihy+vRplpeXMU0Tm81GZ2fnPYvI6LrOwvoCydYUQt6kbkEl4MDvtqGIApLNyUY+iVXPMxbz8cCnd+EI3Dn1rB2ASQjmlaK9aeJyuihXyngtuS0zfpPf2qFIPLevkyP9QZZzdTTDxG2XGYq6Nym0Nr8NBwKG2aYn72hKdR1aukGH0KYXXr+AKrKILAmYinjLuSCzpSP73FgI2K4LwnWjPU8k2m99LwmSiK7ffJH+eYFpmAgIGHdZWRaFdpLy+ltv0two0OyNItejmw+aqwh2+UhNZ5iZyvDgI3ef6OVyuW0J190gP5dHq2t4u72cWSlyOVnGZZPbMye6ybnVIpWmzvGBEI1sg+x0lsBgANhaTJy6kOJ7f3qaWr6BJ+ZGtotkl0q8NH+WQrbGc7+857b7sra2toUank6nUVWVoaEh9KZONVXF1E0auQZmp7kppNLZ5ePXfvfY5udWV1eJRqOMjY3RarVYWFjYXBO9Xu9mMepeYPfa6d3n48ypyxREAS3sxOtQrimvulwUKlUamkiwonHosYG7UvWs1Wq43W5EUURSxPZMoCljaAay/fZro8Nt4/DD/Ry+hZ+Y3W8nYJdJaTpY9h3XmmpLx6mZBCOuLfOZoihuXm8Ol4woCKiqju0mglRaU8frtaPssO+aZmKZ5o5dX8kmYxomqvazYQn1UaFe06Co4oy4sCyLSqnJCz94mcLsSdxuN2NjY7jd7k0aYqZhsXAhSbjQxNHtBUlAWi6Tns6ytL+TvfdI90ulUuzZc/t7+E6QKDb47nsrrE42OX/2PM4ONw+e6OXRkegdCeWlyk3emkqzvFpEsUns2xXhgeHIjrTBG2GaJtlsdrP7pqoqZ86cIR6Pb7F0uppQTXT6mFwrkpANOh/qQaiqoIgUvDIryQy/+vB+HhnrZGpqiqWlpU0hvK6urg9dXDIMg97eXo4cObLZUb8ag3V1dbG8vExPT89dJ3mqqtOoa1hWO/6xTBAVYUtRx7pFPHMV4nW03lthaDTMO34HudUSoW4flgWZxSL+Lg9DN2EDAEiSxJe+9KXNv3dEy0zbRFz5JjJgFpqoLomO8O071Y2auo0RqDgk9KZOq2ng+gm6s923RC8ajbK4uLjF3+znGU6XDVMWsJoGgufmAaplWui6RW88zLF9n+CHb59G1GQEfXuSeDV4lq22Z9/dYn19fUcRljtFs9QkN5sjhYVdFvG47ODaWpX1OhUqTZ2MpZO5lCE6Ed1CqZraKFPJ14nPF7FMC9vAtUVeT9fwzJdIORWmEqVbJnrpdHpTgGVxcRFJktizZw+abnDxUrrdmq+pnD+fY88+L4Vyk6DP0fZsC5j81Q++xdBjj2EGAluqj319fZimSSwWQ1VV1tbWMAwDWZaJx+N3tYC9+uqrvPXWWzQaDX7ra7/F+mSVpYUC+VyDqiggmhadLQufs8Bac5akESVm3Xk3sVarEfZ7ET1BTkYyONariA0JbaOM2hVF7vbQd5sFp8Pn2FElFSA4ECTsseOoNamp+i0XUE03ESyIKDKRsa1V1g6vg6jHTrGq4XHKGJUW0g1iC5ZmQMtEj7tw2eQtg/Nut4Ir6KCSqeEJ7XxNmKaFqRsE7rED9bMEURbbZuii2KYgW9wRtVYzLZwOB6ODgyzVdTTJtmNXR7rSddVVk1alRXm1jNbQ2rLlioQj4Lilp1m1Wr3nRE+tqeRmc7jCLgo1jcVsjaDbjutKl8ppk3DZJFbydQbCbkIxF8WlIs1Cc8t2DN3kjb+foV5s0rlJzwFPyEkpVeXca0vsPdx1Szn5er2OzWZDkiR0XWdlZYVYLIZH9LB2co3s5SzNUhNMKFVKqNMq0YkowaEgslNGFEUMw2BpaYnu7u7NApTdbsflctHb24sgCJtFKgCHw3FXjIJqtcrf/M3fsL62TvfuXhwtG6uJKgmXhORqq83pLR0jV8VebxAZdbL7k8PINxkn2Am1Wm1TYfShZ0d56a/PU1qtIbkE9p24N9uZGxHoD9Dd62dlMUehrhK8QWhHNyzKdZVhUaJ3f/ymxvMj41ECHR5KyRrRHXwIDcOiVWpy7InBHWehY3Evdq+daqGxpcsNUM3VcPrsxDvvTyf2ZxWxkAu5z0dhvtheh4aDlHNnaLVarK6u0tPTw/j4OJqm0Wq1SDWK6C0DUQDhyu8mSgKoBq17iGNM08QwjPumZmiYFi9NJki+tkysrCKJAo21Km+pJr1BF0PXmafv1KwoNzW+9c4yG28u4y1r1ESBV2dy1D9p8Nz+249izM3NbXq5ra+vs7KywqFDh7bNG5fLZXw+HzGfg8/s7+JHUylWS03w26jVG4iFCl96aDcPjra7zWNjY1y6dIm9e/diGAaJRILV1VUEQaCjo4NQ6O4olrquc/78efbu3cuBAwe2nYuHH36Y48ePIwjCHdNbM5ka7725xMwHCRLrGSp2J6ZqEm3oOPp9YIKerGG5FQg56L2DBOpO0DcQ5MHnxzj59zNsTKUBAUfIzpOfn8BzF5Tfo8NhVg7GyZ1N4cg1aDpFPEc6OdR/+3PbvyvC8pkNtKaO4pAxDYtKskrfgTh+/0dDO74Z7jnRe+qpp/g3/+bfbHpHXDW2vopXX32Vf/tv/y2vvPLKh9/LjyGGBwK4un1UN6r4PDcPJvSSiuGUCEZMRkZGcIdi/Nk7K6h1O7bVCqLfvnnDmDUNwSbTckrE7sJ/r1KpUCwWqdfrH8pjSq2qlEstKqJ4S7qFyy5TaOq0yi30lk6tViMej6MZJtOpKr6qhlFuofRvpRRKERfaahlPVWM6VeWJMRN5h4fx+vo6PT091Ot1FhcXGRgYwO12c+lymle/P01uvoDZ0BAlCaMiczmTI3HpDfY+0scnPjnKK6+8QiKR4L333uPrX//6lm1HIhEuXbpELBbbVLqC9iKXTCZRVRVRFOno6LgtJWL//v1MT08jCAJ7H9/L+Amd4nKR9ZkczbqK3WmjKcbYqG8wObXGD37wAyzL4uDBg3f0e1yttO8aCpE40c3quQ1shRbZqANrPEhfBwx+iMTH1+OjayhI/EyCxZqKTRJRdqh0G6ZFutIkbgp09vu2dQtsssiB3gDfz9fxDwfRLmaxVBPJb99U3TTyDZR+P0mPwu6Im87rhudtNpmJ4z288dfn0VUDeYcgr5yu4Qw42bODZP/PGxz+tpBGUJZw22SqTQ3vHczpVZoaAyEXnYqLnqc7WLQ1ufhSGkMztiRt9XIT0bQgUebiXxdpFBvXHtrWlfnSDjexPTFCIyEUt4KpmQiigGF9uABMq2voTR13h5tEqYlqmERu+L1tsohhmhTqKvG4l1qy1qazXoelxTzppQLBXt+2ZNYXdZO4lGH6YvqWiV4qlWJwcJBcLketVmv/eSbH8pvLtIotJI+NulvBFKBmyNSydUo/mMPf4+di4yJ7ju/B5XIxODi4jcba3d29uY55vd7NSnuj0WB1dRVgU9zlVhRYj8dDZ2cnuVyOh3/pQaJylOVzSeYvpSmV2h6EDruEZzRIx2gPL556kb/+dpXHHnuM4eHh2/8gtKv4V1kNh4/10NHpJZepYVAhFnPdcjbyTmHz2Bg90UM2WWW60mJDM/A6FCRRoK4ZNJsaHXWDPYe7CI/evIjgdNs49MQAr/3NRQqpKoGYZ7MIoqkG2YU8oS4fB07sPD/a0+tnYH8HU28uI9sknFcKUrVik0q6xoFPDP3UlPE+LtjV4eHZL07w3gftGb3jR3votA3zH//jf0TTNCYmJrAsC0VRUBSF0V47wZ481dUKwmoZQRJoigL2Dg/x24ik7IRvfvObJJNJnnrqqfsyL5ertkgsFvGVVOx9vnaBfb1CdaXEaqG+JdG7cQwFYC5dZeNsknBRxdbpwWoZyCtlLl5I8cBwhNAt1IEbjbZNkaIonDp1ikAgwIMPPrjje68fQxmKevjtgJP5TJWZpTVsURcP7N1H+LokRRRF3G43lUoFr9e7GftZlkUqleLSpUsAhMNhYrHYLc+jpmlcuHCBffv23dR6YO/evVy6dIn+/n5mZ2d3VEK/Hqlkhf/+nz8gPZfDGXBid4joTZNkskLTLtMrC9iaBjW/Rqk7wEifj5Ho/bv3nnhmhJHxKEtzOSRZ5Nvf/VOSGZl9ZvyOxw5GYl6+8IkRPhgKks7VGY66OToUpu8m6qDX49hDfSxeSrN2IQNYmKZFoMvLo5/e9ZGI59wK95zovfbaa5szOjshnU7z+uuv3+vmP/aIeB2MHu7i7LemcBaaKMEdvOVUg/xGmeDBDqYn3+Qv/vgtOjo6cI8+SCHaQUehhb5SRvDasDQTq6mjDvnxxDzs6rh1VdGyLGrpGtVklXQqzbdf+Dbebi+maW7Or90tri4EArem4FqWtYV5c3VxbGomumEiGxaCuH1mUxDbtFLFsNAME920uJHqrus6rVaLXC6Hpmmb1I3JC0le+PNztHJ1gj2+61Q/I5iaSTlZ4b1vTbGxluW9M29z9Mgh4vE4rVZr28Ld1dVFIpGgq+taNU6W5U1xCdM0SaVSm2IM0Wh0GyUimUxSKpX4J//kn5BItB+IsqPd7YqMRSgUGmQzNQJKkOk3pgmHw4yOjt6xmenk2QQ/+MYHmIbFY8/5+MoD/VweaNMww6kEx8b6iDstFhcX7jiguxGiLNJ1tIuxZBUtVWa90sKhiPicCookYpgWlYZOtaXRIYqMBRz0Hu/ZtBe5Hnu7/Mymq8xY0HkohrBcRk9W24q1bgXbRJRUzEk44OThkci2a+PIA73MntlgYzpLsN+/qdZnGhalVJVmqcWx50fvyMj5Zx3eLi++nnYRqSfkZGqjjOc6qt5OaGkGIgKrl8+ytJDFsdfBp37rl0gv6SQvZwn0+nG4FWrFFuXLWaIuG0KihhhzEx4Nb0mWDM2gnqkz+4NZFIeCM+zE1EzOXzgPfnjmV5+55wDs+oQS4eZ0/6v/bFnWjjS/RkPHVE0Ux/Zrsb3OQOuG5PB6ZLNZQqEQS0tLBAIB+vr6yM/lWXxlEcOClFdhMVul0tSu0KYNNnxNRqIearMJ5i7Osby6zNf+j1/b8aF9debtRusDp9O56XWlqiqJRALTNJEkiY6Ojm1B1urqKo8++ihdXV2M7hpFFEWCw0F2PdpPq9LCMi1kh4wckEllUrjmXMiyfFd+WtWquoUK2d3jp7vHv2nTs7KygiRJH3oWKH4oznHNwPXGMmupCvmahioIOAwTY2OF3Y/vYfenR2+r6PzwU8M0axqnX1skcTGFaJOxTBPBhHCvn+e+uo9Y583Xiee/vBe1ZbByPkVhpS2oI7sUdj3Uy6c+P/GhjvHnAYIgcHQowpHB8ObfAT73uc8xMzPD0NAQJ0+eJBAIMDo6it+p8PixXl5q6qTnCwiGia3Pz4Mnem+rhrkTnE4n2WyW119/fduoxr0dEG1hE5HrFxZM2GZZUqvVthU16qqB0NCQHHJ71MYhYys0KFVVaqp+y0RvdnaWWCzGyZMnOXDgwC0LJsVicTNZe/PNN3E6nTgcDh4b67wpg2JgYGAzQds83CsdPcEVoNrSqbaq5KbaVjA+n4+urq4ta5Kqqly8eJH9+/ffdoxFFEWcTid2u/22FP43fzRHai5H53gESZEw0k16hqMshxxk5guoXhnfvh5qQpPDe7qINNdRxIFbfv/doqfXT88VHY3zU128/PLLrK2t8Ru/8Rs7ivzshJGYh5Er6rx388zz+Rx89R8dZ/J0gvRGGY/Pwd6DnXTEf/KMgQ9F3bzVQc/Nzd23YfSPK55+Yohsqsraj1fxFRt4Yh4Eh4SlW7TyDYr5Os7REM9+fjdD8WP8f2WBS5cuMew1ybtVkqN+gvkWtmIL0y5THfRjdHt4eiRC3O+glqlRXiujN3RERcQddePr8WFoBitvrZCfyaM2VFRVRZ1UURoKASlwz8dj89rwh5z4qk2KTf2m/POGatAlCDhCDmS7vHkD2GQRp02ipIhIgGWYCNd17CzDxAKaNpGQXd4yp3UV09PTmKZJX1/fph9jrtjgR399Aa3YoGP39iRBVEQCvX7s+QarpzL86q/9n3jyyZsnP4FAYFuiB22Kxny6imnBQDhEZ2d7IchkMmQymc3PNptNarUaY2NjAFvosqqq89L3prn4zirNSgvZIdNSZL70W59ibHyAhYWFTRrHzbC+VuKHf3GOzFoRh9vJq39zAX/QyYl9cU4MhslmnRQKBXq6R1lZWWFlZeWejVKDQ0F2fXIY8ZVF/OslUppBzjAxzbbYhtuwsM0vsfvwOONPDxHbt7NIgdsu80v7u3hBFJizVTH8NjwtE8GyaCgSml2iy+/kU3s6dqTsBgJOvvAPD/O9vzpPYiZLcaW8GeC7Qk4e+OwYTz83dk/H+LMGQRSI7o5SXCwy1OkhXW6RLDfo8DrZqRDY0g0ylRajHR52dR/nvea7WHGLYNDN87+2h1e+M8vGXI5ysoqiGnS6bew73kP0JsJAkiLhjrrboilvr2EP2Ok50YOhG6y9u8Z/n/vvfOGffYGuw113newpbgXFqdCqtAi77TgVmWpT3yJi1PbQkoh47KhVFcWloLgVqF3bTjDkxOZWaJSb2+i+hm61RYxvUnW1LItEIoHP56Ovrw9RFNFbOqvvrGLoFnOWwWyigtuu0OFzIIoCmm5SbGi8v1yA3ApePPQ4e6jVapszwjeip6eHtbW1bfemaZoUi02cTmWzuGQYxhZ/p1gsRiqVIhwO43a7OXDgmkGyIAi4Y24Mh8SFc0nqFZXuPj9Oj8hnP/tZwuHwLffrerz+0hyvffs8HX0JfuX3jhK8TqnzahLe19fHwsLCPc3mXA9REul5oIfAQIDCfIHkbA5DN/GFnJxayvJB6SS+ZRdHo7e2vhElkWc+P8Hew11cPLtBLllFsUn0jYaZONCJa4dC1PXw+Rz8xteOMTuTZW2piCBC70CAkV2Rn3il/eOMG+/to0ePcvDgwc0xh3w+z6lTp3C5XOwbGyP2/DjLuTqmadEddDIYvjuLgoZqUG5q5Mttf85/+A//4X2xU4m47fSPhLk8k0NarSCKAnVFwDUcpP8GRky1Wt0Wt4bcNoSgAz1bRMg1sFo6dVnEG3URdG291rLVFmuFBqZlYdbL5JJJZFnmoYceuum5aKgGa4U6C7kmowY4BYOzZ8+yvr7O7/3e790ymbpqFH59N7ChGrx4cYOpyRStqoon7OLo4U4eHYlSrVaYmZnBNE1cLhfRaJSZmZmbJnmVSot33lhi9uwGkiTQtzuILC0zMjrI5OQkfr9/xw5gPl9n4VwSb8yNpEht/QhRRBQFBrv92Ooaoinw3DO7SC7N8PwDQ9RqMd577z0eeOCBj0TYUZIkRFFkfHz8nlgp97JPbreNB28hZvWTwl0len/2Z3/Gn/3Zn23+/Y/+6I/4kz/5k23vKxaLTE5O8txzz334PfwYw+9U+MpX9vFSzM38mQ021soIuoUlgBxy0vPUII8/NczIlS7Eb//2b/P666/zmc98hg8uTPPD0/OkYz5cIzFsNplun5PD/UHGAk7mfzRPfi5PtdhENS0k2rNM3k4vlmFRXisjd7gpOURauoLQ6efg6EFS76Twh/247oHrbPfaie6K0LFcJKsbNFRjm8Jbsa5hlwQiskL0utkYaPvf7O/280KqijfqQk9UkeNuBEXC0gy0jSpS1E3No/B4t3/bEPRV1boHHnhgywP3wpkNyutlOse2J3nXwxlyUs3Vufj+Gg8/OoDtFspYfX19WxKkUkPjGz+aZfWdVTBMooe7+PJzY3T4HFskeC9cuEAmk6G/v59UKrWNEvHKC7O8970ZnEEHoV4/zZpKbs7k7/73Sf6XPxomEAjcVk0stVFpC0x02YhEw2RmC6Q3KpueVqFQiPn5+c3jmJubI5lM3vN8bGQ8gt1vp2M6S2Y6RzZdQzdNJEEgGHPxRirPB6WTDIZ6b3n+/U6FLx3uYSlX42KiTKLYwLQg7raxt8vHSMyD6yYCCgCxDi//4J8+wPxsjqX5PIZm4vbamThw7+qQP6sIDgYJDgYpLBQ42hvg9FqRjVIduyzhcbQFKTTDpNLUARjt8DARdNNK1Xjq157CNtimJW9sbPDkZztRmwPUsy0K7ybw+ex4u25dhCssFCitlAiNXxFEWSmhKgbR8ShHJo6w9vYa7rD7rkQ/ABSnQngszNo7a4SjboZjHi4lSjS09lrT0gxausFI1EvEY6O4UCC2N9YWMbou0evu8dMzEWXunVUcHtsm3VdVVQorVXwdHvYc2D5fZlkW77zzDsPDw5tzaQCl5RK1dI2K387CSpmIx479ukKXIotEvXaKdQ1iwxw56MNhWIScN5/VEEWxnUReUbWEtsfct/7yHMvnUzh8dp79lX2MTcQ2xRSu7uOpU6fw+XzkcjmAbZY5mXSVv/mTU2Tm82CB5JTpPxzkt373UQCWlpbwer23rdBfeGeVeqZOspZhcS5H8Ng1yqPP59ukhQ0NDTE3N8fg4OA9i1dBO1jydnrxdnrpfbh3kyo893ezrF9cp9ls3nHlvLMvQGdf4J72Q5JFxidijP8Cq2veC64P6kOhECdOnKBSqTA5OYkkSRwZH7/rzq9pWry3lOe98xtU8w00unnimcdR7lBt+HYQRYGn98ZpNg1WpzOYLR1vp5dHjvZso+DVajU6Ozu3/Ntw1M34iV4uqQZKsoYhS8hjIR462LU54qLqJq/NpJlcK1FtajRbLVYWF3j6xD6O9g3e9HpeyFT5wfurZBYKNBtNEvVpXGq7eLt3714SicRtGTu9vb1MTk5uJnonF7J88KN5/KsVPIJAXSzweqlJ2GNnb5cfn68dj+bzeV566SWGh4dZXFykt7d3S5dLVXX+9s9OM38qgcNjw7QgOZNj/rKLf/Kv+tm9ezeXL19m79692/apWm6hNjQCV7pXmqahXHfteP1OLNNiT9wLWRlRFPB6vYyNjXHq1CmOHTu2bZsfFp/+9Kf5lV/5FWZmZhAEgVqrraB6J4I6P+u4q0SvXq9vdjagPRt2YwVMEATcbjdf//rX+cM//MP7s5cfYwRcNr703DiZRweYW8jTrGtIskhPb4CBmGdLMuP3+/nsZz8LwLH9uzm6b5wLMwucvXiZqD/EIwf6cUg25l+aZ+N8mqQssGbotHSzLXmtQvRcEnW5RPRYF2eTZVLlJoIArtEj1EIuhEyN3EwO14P3NtQa2R1hcDZHbTHHcl2l3BBw2SRMoNbSsQkCI4ZA/74oZsTJ5FqRhaLOWF0l4LIxHvdxNlYioxpEBYHCXJJcJouq69g7/biPd9Id8zB+ZQEwTYuzK3lOXppHbzX5wpPHtlxTpmlx/v11bDZpU/HuVvB1esgvlpidybHnFg9xj8fD2traJrVqJllm5ccrROo6oiyRfmeV8+MROg5e69YtLy/jdrs3F7ZqtbqpeuVwOPB4glx8dxWHz07wSiCtOGQsK8rS9AoLczlGx6LMzc3h9/t3rCqpuklFaHc9C8s1tHoZRZEIXpe4i6K4hfI2MjLCpUuXUBTlnkUyrgZfnYc6qWfrmFpbYdARdDDvn2ZycpIfvPw6z38+is9lI+DauWouSyIjMS8jMS+W1e6s3Imq2fXHNjoWZXQsevs3/xxDdsgMPjWIqZsUl4s82BMk3dJYzFYpN4220poo0Bdy0Rdy4tcsWqkanUc76X+0f1OI4mrQkslkqEwnoNXC27W1yGCaFrmaiqqbeBwybkGgtFLC5rUhKzJpwSJzPona7+ehY0P4oh4K84W2GuZdJnoA4dEwmQsZqskqe7t8eOwSi9katZaBxy6zrzvAYMRNq9BEUiQiYxEajQYu19Y17ZOf20013yA1k6NUK9FUG+gNg5G9wzz5xQlCO1Trr4pJXJ/kAeRmcgiywGq5gQBbkrzrEXAqrBfrZA2DaFmjtFra5oN4Pa5SH6/OAk+eTjD99grusIvCWolX/25qm5T/0tIS+/bt2+xm5HK5zYTP5/MRCAR4941FUjM5One3aVGlVJWVM3kmJxfZv3+Qvr4+lpeXGRwcvOVvMbw/Tmo1R6QnSG9/YMtrfr+ftbW1zQ7H0NAQCwttqvj9qLgLgrDZtZ+YmOCxxx5D07RfKJumnwd4vV6OHj1Ko9Hg8uXLGIbB2NjYts7Y1XUGIOy2bT4XLm2UeemlOeSpHC7VpKWIvNZcxOuy31Rq/24R8zr49UcHWdvbgWqYdHgdWwSBLMsiWW6ymKnSPyJxfR/RLkt89nAPw3Ev8+kqxVyKJw+PM3rdLOebsxl+PJclYpMxVpO0snkePDDGSknne5MbfOVo77aieUM1+MH7q2RfXyFYa9PMS+kV6kdj/C9/+H8h7L3zhDkWi5FOpwmGI5yfy+FO1HCFnEg+O2K6RnM2z+X1Mnu72iyper3O6uoqn/3sZxEEgVarxdraGq1WC1mW6e7uZmGuzPJkmuhQEPuVc1UtNNi4nGNxIc/wSIRYLMba2to2T1Wn24Zil2nWtLatgKahKNfOt1rX8IRd2G0yPp9vU4gmFAqhqirnzp3bwmK4H+jq6sI0LZaKOud/PE+haSIKMBz1cKA3QM89ej6WmxoX1kosZ6t4nAp7uvxb5j4/DrirRO/3f//3+f3f/30ABgcH+Q//4T9sJi6/6Ih6HUQP3JkZtmlarBbqzKWrlBp2AsOHkM06r7z+JraMhbQss+q0sVRq4HUoBJwKummxUmmSK7WI5BvUZrKkAja6gi5EUSBTaTGdrPBw2EN+Nk/38e4dVcduB3fMzfAzQ/CygG+pQNY0KTV0RAEGEIgqMoP7YxRHArzw8jyVtSKqbrBRucwnHuhjf0+Az+zv5O8F2HDKmGGZxPkiqmyjY18/I70BntsX30wUfnhmnhdPrWHPqUhOO998f4UvH+/b9LhpqDr1bA3HbeY2rsLusWO1ShTzjdu+d2BggKWlJYaGhjBMC0EzkOwygiIiVnU07Zpi2OLiIqIobgmcPB7PJue+2Wxy9sw8hXQRV9jZnhm5krD6Ih64KLCylGJ0LMrQ0BCzs7Ob1E+4YlS6VuK9pTzJUpPmrhDNeouNhkrXwThCzLWl0m2z2bZ4e01MTHDu3DkURdms2N0LHH4HjhvmKgLhCN3HPkXZCvHnf38Zl9vOnvEoj41Gt3t6XYe21cfOr6m6ybnFHLlsnd5ePxPd/v8R4N0AR8DByLMjLL2xRHGhSEAzeCDqQ5MFLFFAtkCoa7RSdfA76Hu0j66jXTve9wFPgPXSOopPIZPO4HA48Pq8VJs6H6wUSJdb6KaBQ5HpRsBTVdtFk7rGRk3DZoFcFzmfKBPztU3eS6sl1Jq6RQr/TuCOuel7tI/FVxeprJXpj3sYDLvRzXbyimlRS1XRGzrB/TFeeW2e1fkMwYiXR5/dxcBoO1HtiHv56u8fZ/LUOufemmfmvUvEOz08eaKPqGGRmkzh6/XhDDpZWVnh/bcTJBZLHH5wlBuZzs1iE9GuUMg0by3fLYBNlsjXNTpkEbWi3vJYBUHAZrPRarWw2+1oelve3+mz06y0UFtbpfwXFhbo7u7eUlkPh8ObBZxSqcTKygpzF9cQbGyK7PiibjayddZXCuzfP7hpSXAzM/ir+OQvjRHpEhkbH8B7g1LuNor8lfXvarJ3P3HV+3Vpaem+btc0TRbm8xiGychIZEdbhV80qLrJcq5GptpCFAS6A066A867KsjtBKfTyaFDh1BVlenpaer1OqOjo4RCIdKVJq9cTrOSb/uP9QZdPDUeo8PnYHK5gDWTx6tIKD0+bJk6+myBs3O5+5boQVvkaacAvNLU+OH5BLMX0pQKDebzUzxypIcj/cHNe8Bpkzg6EOLoQIjLl+tbNBTyNZXJtRI+TPIvncdVEvBpJoqVo/9YJ0u5GvOZ6jabibVCncxigWBNw97ffs2/XCKXqJMoq3eV6MXjcSYnJ/EFwxi6iWhdp+oui0iagaq315pqtcrCwgL79+/fPD673b55T+u6zvr6OufPLlGtVAnK186ZJ+gkvyKyMJvYTPSmpqa2idhEo276JmJMvbWCO2inqWp4PO3Cm64atCotjn96F5Ik4vf7effddwmHw5u2DqqqMjU1dc96EzvBsizemstyIW/DWk3gLaoYssT7YQczw2V+6VAPI3cpxFRt6Xzr5Arzb61gLzTRZZFLe6M8/+TwPduKfBS45xm9xcXF+7kfvzAoNTR+eDHJbKKMlqqh6CaGAGbQTiQ8SGdmnY3EGqtuH32x4KYKogI4FRfr6zXsIjhyDURFQAxfuVFlEc0wsWQR0zCxTAvusSMd6A8w8flx4vN5slNZGlUVSQBX2EV0Ikoj5OC7r8xjnd4gorXn7irZdX5kWHQ+56Q36OLXjvcxm65ycSEHlQpTr77LJ3btoT9ZR1WTZPt9bNRTTCY0OJ/AkWkR6ewgZc8zNxDaTPQsi9tIw+yEO3tgORwONE3DMAwGIh58e2OkTm8gtQSc4xFGr1S45+fnsdlst1Q0dTgcTOwZ5q3wKrpuUKvVr/gagmQpuLxOytV2VV4URTo7O7fYYby7kOPl6TSKKNIbcjF4uBvjQCeaTSRX0/je+Q0amskDQ+2A7+oMz/X7tH//fs6cOcPu3bvvq4mqY+AQhdcW8Myn8GoWmizw47UShmnx3L7O229gB7x5foM3//oCVqnJqV4f1m8eZG/v/Xuo/7zA7rOz67ldVDYqFOYK5GZziBWt7Vcoi9j87fm5wEBgm0ejWlWZm0wyezZJZaWIulhi6IFuOoJBWnqLdCrNZFolXTeIeh3YJDtVVWd+vUJvtYVf9KIbbUERh9+OVlFp1TU008SpiFgNq73O3AOiE1FERWTj1Aal5VLbgF0RMfW2aaSnw0PHsS5+9OoCS5Mp7H47ubUk2USVr3z9ON1X7k2prtOvKLi6Irj6OynWGiydT7N8PoVdFAmF7QhBCzka5rW/OYepC5QTKrFO75au8fUd8jupN5h34Dt5FZ2dnSwtLTEwMMDuvR28P+Aju1jA5rFx5MnBze9fWFigt7f3lnNwfr8fv9/P4FiRU/PTlMplJEGkWW7PMvoC15K1q5Xyq0nm9bBMi/JGhVyqglO3cN2hqbUkSfT29rK4uHjbbuG9QNec/Kf/12uIpszEsZ5bmrXfCV5/aZ63v3MZ07A48OQgn/3Kvtt/6OcY6UqTF84nWV4rYRQaIArYY24m+oJ8ck/HLan1dwqbzca+ffswDIPZ2VkuTk1zruYlm9EIZWoICExHatRVg18/0Ue1riKbFqK7/d2iS0Fu6tQa2pVZ8Y+2APj65TSTP5wnkGrQjUVtrcGLFZXQ8+N31JlJlBosb6SJFmuEVSeVoEYoEkNfrWCuVRB7PCznatsCf4u2n9zm0QnXXrgXT+ru7m5y6SQj/QHeCyaxJ2tINhFNM9H6vYx2tqnYS0tLtxSFk2WZ/v5+CntlZt8qUK82qdeu8OZ1CYfXgSVcs7sZGxtjcnJym5r4vkf6mDyf5MzJNTSngM9v4kZAqmn07I1x9KFeMpkM//k//2ey2SwPPvjgphXW1ZGUhYUFhoaG7vpc7IT1YoO3p9N4pgvYknVEt4JV13FkG+SbBq+5bPSFXLc0Yr8R08ky8++sEUlUUYJOzLpG8WyKH0fc7Orw3tW2Pkp86Lu6UqmwvLxMoVDY8eJ87LHHPuxX/Nyg1tL57mSCmak00ZUqYqHZjhgsC9wKJW+R0moFT7wLl2lul7oXwOuzU0lU8JgWQl0lf0UWv9zQGO3woFdbeAeCd0RzvBWcISfdoW7iB+JojTadRnEriJLI2/NZ6vN5oibYBgJYloVvuURmIc9yrkbUa8duWERWK4zPlvBVHThjwyTXqqTWqkiqis1oMbZ/AMUwKKWymBUTu8OBaPjRrzPddtplnEEnlbUydyLto9Y0sIlbAp5bYWhoiMXFRUZGRjh20E1x934sCyaGw4zEvMzOzuJyue7In9DnczB2vJtT359BtnnxhNw0qy1ScxkCfW7W1s9Tqx3F7Xbj8/koFovUajVyLYE3ZrP4HQoB3aL5foJGqr2wyh1uuiYiFBSJN2YzdAeddPsdqKrKCy+8QHd3N5///OeBdgX+4MGDnD59mv3795NOp7dRKu4WpYbG+eks3vkSLpuEFHeiFJuwXObiVJrjgyEid+FLcxWXL6VoLaXxhh0Up1Osrpb/R6J3EwiigK/bh6/bR+fRTvSGjmm0Pe9sXtuOfnelZIUX/uIcC5eztGQBodyC5SKpUoOB8SgHHunH7g1SWksRcts3H0geu0zDKVNo6fS2dPxOBbsIBVVHqGv0OBU8Npl6qorNY7srv7YbER4NExgIUF4rU92oojU0ZLuMu8ONv8/P7EyW9ctZoqMh7E4F07RIXEhz+XySrj4/6fNpFt5cJpGqkjJNctEIRthkCgML0Bp1AqtVBjIurHNLaIkqQoeD5HoSVd3aSXMEHDQKDfxOhXSldcuunqobhN02zLLaFom5A7hcLur1OoGAk2e+OIBpePH57PQNBLEsi/n5efr7++9YJODYI/0sT2XILxdBFBBE6DkYwuPVuXz5MqOjo0iSRE9PD/Pz81s6cGpN5a1vT3HxgwTlchNZFrn8bp4nvjBB+Ab6pqIoW5gD0A7kOzo6WF1d/VB2Pjei3lD54V9PkZrNITsVkvN53G4bBw7fGVNmJ1x6f72tTGqTmD29gf6FPcgfk+DrJ42GavD9yQ2WzyUJr1YQ6u3nurZa5XS+gSjCZw/cmw+vZpgkS00soMNnxy5LSJLE+Pg4UqrCi2/NEl+pYCUqgECs7mbNZ2clX2e428+a344708Bq6ug1jVrYwf7e7XP89xuVpsbMbBZvuoEr5kJ0KYjrFbKzOeYz1R0TPbfbvWk50mg0OHf2HIpip7uri/ylOexBB4IkYkkCGCaiIKDvUBDrDjgJDwQorZbxLxcBgYpdwtfnvycaYTgcZnJykgdHx1g6liN9uYjYMhH9DvYe76LPAysrK1sUOm+F3Xs7OLU7wspkCleozVKqpCtEJ7zUmhskEgni8bZVwdUZ3qtCc5lKi3cyVfRDHTi9CtJSiVq+QcUuMXqoky/8xiECASfg5OGHH+ab3/zmFns2aI+kXLx4cUdq6L1gPl2lkajiSddRuj0IV56bRqlJIF0nsVFhJV9jJHbnIpLJchOp2ET22pGCDkS/HedyiUKmRrmp3VNs9FHgnp/S2WyWf/pP/ynf/OY3MQxj2+tXaWY7vfazCNO0ePWDNS5fSDMwGuKZB/rvOlu/tFFmdj5HbKEMFRW5w9UWKrEszFILz2KZUqHBik3C79+5qquEHLRcClSbdMZlqldk8EdiHsaDLvRMg8j4rUVL7gaSTdpmXqsbFoJhblIDBEFo/9mwMCyLZrHJwisLXDq9wbqhU5RF6OskJ4pUq1VMQcTr9VGdyaFnSlQaOkqHj3TMRnwgQO91A9KSKLDneDdvzOYwdfO2CWwpVSXYF2DXrlvPeF3tespyWzW0UCgwFPNtWVCmp6fx+XzbhrNvhaefH6NZ15k7nSA1XUN2SIwe7+f5X93HK69+j/X19c2B9lAoxOrqKku6n4Zq0G1XqL67gpGpI4bbHbnWfAGzohJ6tJfZeovpjTJBxeQv//IvKZVKm8qkVyGKIgcPHuTP//zPSSQS/MEf/AE+nw+1pmKoBrJDRrkDT7arKDc0aqUmfs1EinsQRAE55MS+rJIvq1Sa+j0tZvFuP2f0BuWFMmbcu2UG8RcdG4ky+Vyd8d2xbVQzm9t2W6qkoRq89I2LXJ7K4B0MEHEqaBtVmhWNpktm7mIap0smuq8Dw7RQbqB6Kj47ps9GI9sAH4zHvVRaJmapxb6eAIJl0Sq36D7efVNT9TuFpEibwjPbjkM3MS1r8zvEKwmNYZgkzyWZeWmB2UqTdctEkkSCLgWbLGIaBtVqDcXvpqpZXNBNlHoZj2XglFzs/+X9jO3eSmcMj4bJTefoDzrZKDVQdXPH9b3S0HDaZKKyhM1jw9+7Mz2nVW61FZObepvu6bGxUdjA7rYzumtgM6EzTZOFhQUGBgZu6l+1E7p7/PzaPznB5OkEjapKZ1+AA4e7kGWRhYUFkskkhmEgiiLRaHQzWLIsi3e/O807ry5gRFz4hkO0GjqTl1K0mjpf/J9OYPdupY1eunQJl8u1Sa+EduJ61aRZ07RNBb8Pg1ymQTFZITwYwOm1s34xTTpZ+VDbjPf7ycznUesaXWPhX9gkD2A+U2VlqUBkpYIISD2+dpF5o0ZwqcxUzM3xgfBde98lig1+cGadjbk8pmXR0R/gmSM9DEbaVD3VMJEkBbGhY7ltIIDQMNrq5LrJ/m4vPx6SyDm8iJUWZo+H7sOdHB28t3nzu4FhWui6hWRZCMqVeEYRwWjv904Ih8Nks1my2SzpdJpHjx9k/VSCumXRdJn48gZqsYToVJA6PWi6QU9wO8PGbZf55LFefmCY5JYKWKaAv8/HJx7ou6Vlw60gSRLf++ZfcXh8D76vHqTW0gm5bPgllWw6uWlXdSew22W+8NuHePOleRbOJRFkkQOPD/LoJ0ao19tF6unpaSzLwuNp2w8Ui0UCgQA/nsuyVqgzEXKjOmu04l7MgAN5f4yCKJBWda6uFk888QRvvD+J7uviYqJEd8C5OdqzZ88ezpw5gyAInD59ms985jP3HNsWGxqKagDCZpIHIHpsiBtVrLpGXb27fCXgVDBcMmamgemzYVY1NFnE6bHhusVYy08a95zofe1rX+O73/0uf/AHf8Cjjz66qfjz84rFTJW3vz2FuF7l1MU0Pd0+DtzFQqQZJufWijhyDSg0UfquGf0KgoAUcGA2dByJMka1RUkRifvYRg+qWxaOAS/6+QSDzi6CHT4sUcCsq+jZBvHDcYLDH+1v0RVwIHV5US9mEdI1LNOiaVrYOz1EFZnFVxY59/46s5KFbJeJumxYhk6lWqEvGkCS2+p6mbpKSWxxKNKF+2Av3iOdHB8Ob+NJ7z/YxemXF8gtFYkMB296ozdLLdSmzr4TPdh3uMlMw6S8ViY3k6OyXsGyLGSXTGQ8wo9f/TGf+cJnNt87NTVFKBTaJtpwO7icNr78W4dIPDVENlvD47UzMBjcnG+p1+ubFId8Pk9TM/jhe+eJxWJoCQ09U99ybYhuBW21jJaoEoq5mEpWeHRXlM9/4vP8yb/7E2wBG0tvLBEaDuHrbs/mTU9Ps7a2Rq1W44M3PqBD6WX+fJJWU8fpsrHrUCfd+zvuSJnVY5dxem2osoBSaiEFHRilJqok4PLYcNvvbTH7xIk+lldGOH1yii/96gkOD3/0D/WfBVQqLf7mP75POVvjqV/Zx8OP3z09LjmXZ34qjbPHt2m0LkgCAuB12yg0DVZm8/SOR/E4ZEpNjfCVwMKy2nMHI7si6Ms5zJxJoMuFR7FQEUAzyM/nCQ4FCe/6aH+znv4AwS4vmbkc3riHeqmFw2UjqMgsvL7MdLnJumkQ8zo22Q+Neh3DNPH52/eC0wEtVWet6WTo00eZcNvZe6x/W8Dv7/fjjroRayoDYTfz2So+h4L3in+hYVqUGhqqbrKvy4dYbBLcG8MV2XoPtcotkmeSZGezNIvNa96kgoAqq8hdMp2faxeOriZ596pkGY15ePrZ7abFV8UUrn5HOp0mnU5TKpWI++JcPJNADzrojLYDcZdNQlYCLC/kWZnKMnq8/dlGo8Gf/umfkklmOHHsBCMjI1vWXq/Xy+XLl3n11Vd56KGHiEQitMotLMPC7rff9Yx4MOTAG3GTXylSdSrINolw9MOp7faPCXxwNoFpwciBe7Og+XnBWrEOxRbUNeT+qwUKAbnDBek6xXSNZLl5V4leSzf4/qlVEq8sESirYMHGfJHvaQb/4BOjeB1tixKv20a9y4Nzse1b2Ohy4/HYiLoVZi+c5eufOcxq2aBQVwk4FUY7vPjvoiB5r/A7FXr7/UyfT6GsVZAUEdWwYDRIf2j7tddqtXjvvfd44YUX2LVrF7/zO79DIpGg1y/z48vr7H58FEdew9IMxKibdZtAh9t20y7RWNxL/FNjrObrmJZFb9C1RSTmbqFpGu+++y6pVIqvfCXC3sEB8vk86XSGiYm794gMhd187lf2Y3xxLwggXbmnPZ4Yly9f3pyhq1arrK+v88orr9A7OsF00iSmyLROJjDKLRSvDSNVw7FWQej3cTFRZk+Xn1JD4+WpFPaJZ3jp/TQIGXwdHg72B3h0NIoiiezevZs/+qM/QhRFjh07ds/q4l6Hgq4IgIWlX2tUmDUN0yaBQ7prBc5dHT4+OBQn++M1PMkamiSgDvt5aO/9oUHfL9zznrz44ov883/+z/n3//7f38/9+VjjKp/aMk2WFpfuKtErNTTyVRV3roXoVrbYElyFHHMhLyq4Syp6xEWm2iLisW/OjNRVg4aqE6DB3k/vJToRpbpRxTItvN1+IrsjhEZD9yTCcjcYCLs59EAfHxgmrFZAEhB3h3nggV5siSpnTieYk8DtUPA6FSrlMggCodA1KXK7IuGXLcxYBDsSE02Toz1Bgl3bK+TRsIsnv7iHF//yHJmZHME+/5aulGVYVNI1yrk6ux7r33GuQ6trLL+5THYqi2VdCUREkQ/e+IDq31WJD8dJ7kli+Szy+TyxWOxDVai7evx09Ww9lsHBQd55553NRC8UCiE5vQTmWgiGRn6tgN6sY28ouJzOK5QsAUQBo9LC3uWhruqkLmdpnmsx4NlHpeTgzCuLdJ1PM/j4ALG9Mfbs2UM4HOaVb73C6385iTtQoW6XwCZCpsrl2Sz7LqZ56Et78MRvPYMQdNvYsyvKO2tlhOUK9nILVRIo93g4PB4h5r17n6OGarCar3Pkgb1o1OnsiVCsq8R8H94z6WceVvs/hqbfMxtiZTFPo6XTcV2nVXAqoIiYLR2nz0YlXadearE77uODlQIbpQY2SaShGYTcNnrjTkRfBC2pUc/WaeQaKG4FUzPp2NdB74O9dy3CcrcIhFw8/+sHefEbFyhn67jdNh741Ai+qsbpTI11riV5lmlSqVRxOp04bVsDxFazzkBHgGSpgadsEp1MERoJbWEHKE6F7hPdzL84z7jThrPLz3KuzkaprcKJAH6njX2dPkI1HWfEReehrZ3+ZrHJ/IvzFJeKuDuuGdFrmoaIyOr0KuoHKku+JXof62VlbYWhoaH77t12lSbqcrkQRZF4PE48Hmd+fp5MIkc6VUTo8qCpGsqVc+V2KFQti3z2moeFXbGzL7yPV156hZbQYk6ao/tY92Zyq+s6S0tLmKbJpZNTsOpjZSaHYZjEe/3se6Sfjrtgl3g8dp7/9f289v1p6pUWe0/0cvDIvdM2AQ4f2c+7J99CEAQeeODwh9rWzy0EoT1PfqdDp9dhrdBgYyZPsKJi7/WBKBBcLpGby7N8qM7ebj8Rj50HBsO8rhmUPTICAkrQwaMDIVZmLjAxMUEg4CP2U+gVCILA4xNxiuUWqQtpaOlIYReHTvQwtoOxdSqV4sUXX8Rms20WU1555RVOTV5E6NxPPhrFjEhtNptpEffYeW5v/JZJq9+p4L9H4Q7LskhXWtRaOk6bRLFUwu12E4/H6enpIZvNks/nGR8fv6ftX8WNzBJBELaMank8HsbGxhgYGODlk+fI5HRotrDWsvgGI0gOG5ZmomcbuHeFKNTbKs9/f36DqXMbhFYreKs6iNAMlHkjX8Oy4OndHczMzBAOh5mdneXkyfeY2P0Q68tF7A6Z8b0dW7w/b4WRmId3O71o6QasVRB9NjAszLpGuc9HvMu3zW7jdoh67fzyw4O87IoVM5IAAQAASURBVBaYnU0RjQV5Yl83x34C3ei7wT0nei6Xi4GBgfu4Kx9vDEQ9PPj5cS6dT1JMXuLS+5f4xInxbf5GN4NpWVhYYJpwM965KCAHHdhaOj2iSF0WSRTriKKIYZrYJJFgq8JENE7fw31tE2PNwDIsJLv0E1MtlCWRZ/fFGerwsJytIYgwGPYw4HNw8RsXSRoGgk3CpQjk83m8Hu9mQLEJ00LTNbqDftaLdTaKTfKz+R0pXABHj3QjyyKvffcy2ZUS6BayTcQyQdN1HCEXBz49yqc/O76tm2doBstvLJOaTOEf2JokigGRXD5H8mSSi5MXUSYU/tG//Ef3bFNwK8RiMarVKrVabfO6USQBv9dDqZAn0hGitaaBTaFcrWx6TNl0E8llo2GYSJpB+v11ZpMVasODVEyTdVOnkq0iv7uKv8+P3WcnEozQKe1iKehFi7nocNsRxTYFOV1pceZiCq/fwYlf23fTwoCqm1hYPDEWQzctLl/OkCs3cXjtHBqP8vTuu+t26obJe4t5zqwWyVRaoBvg7uPvzyXwOm0Mxzw8Ohr52PDafxrw+ux86WvHyKar2F01vv/97/Poo4/elZKqaZhYgrBlmRE9CnLQiZ5rIPpsWEJbkGMw6sahSKwWatRaJiMeG3G3hGS2CA/Fsfot6rk62ctZ+h/tp/eh3m1drI8Sw2MRfu9/fpRyqYnLraCXVc799QWSooVLllFkkVazhaqp7XN0wxLYarWw2+xIooDXoZBUDdJLRbrXywRumEcLj4UxdZOVt1boaRj0dvioyAImoAjgbhoYxRbuTi8DTw7gjl1b+03dZOm1JYorRUK7thbblpaWWFxYZHhkmHhnnPm350kVUzzwpQfue5IHEIlEtniEXsXAwADTuWnCYR9FS8Q0TaqVKgCqJaAg4L1O0Cd1PgXLAjZ/F3W7l+UzG7TKLcY/P45sl5FlmS9/+ctszG/wp/+3H/Da5QU0r4IgiSx8sM7yfJ7nf/MgnXvu3KtueDTC8D+7ucfo3cDQDFZmcjiNbuwOO7m1MtG+wC+sum93wIXlt4NLRs/WkcJOMCzWJxfJ0aQvOkjHHapbX4VuWJiG2b7tJAHhyrpj6caWubSHhsN0BZws59qFhL6Qi/L6HJ39/QQCgft3kPeAroCT33himIU9cRqaQdRrpz/kQr7uHtZ1nXPnzuHz+fgX/+Jf8O/+3b9jbGwM0zSZnZ0ls77CiMvJLx94hmTVwDAtYl47IzEPXsdH05lMlpq8OZthMVujqZvYZQGjZPHF3/o9fJJOJpOhVquxa9f2rv/9wPW2CFdht9vp7YrjymTwhYKYUY1qooDpELFyLXx74jQ1gw6fg4VslZnFPNGlEmLTQI652myAVB0BOBtycagvyP79+9m7dy8XLlzkW391lgsvvoNaVxEEgZM9Pj79a/sZG7/1GqMbJmGXjaMjYd5p6SjuCs5cA+wS5W43rpEQj41G78lTrzfkgpX3aS1O0ygGOPTpP9hy7XwccM+J3m/8xm/wrW99i3/8j//x/dyfjy0kUeCZE330B5u8+mqLRKLK4vwiMhEyqSqKIjIyFiXSuXOL3mtXcNlkmgE70nIZwturEGZVRYy4sGJOOjUBn2lR8ik0RAvZgtrqBsM9nfQe76HzcLuaLClSW5LzJwxZEpno9DHRee0mz83kSCwXyUlgt1RqNYtQMLSjOl25WsHn9YIAHodCqqaxMZOl80jnTSmFBw90MjYe5dLFNDNTaeqlJpJNoqcvwN6DncRvIo1bWimRuZQhMBhAdsgYRtvPRzdN1lIZurq7EPtELr15ie5iN6FQiFpLp9rS0U0LRRIIuWwf+uYVBIH+/n6WlpY2ufIum8xgxM3ZhkpZL+MKOjBSDTxRF1igpWu0HCamrclKsskJj5dKus4qFk5Fwud0UGvprKk68VSNSqKC3WentFJifiaLFlDovG7mRhQF4j4H602Nuak0E4nKtjmjlXydc6tFFnM1sCx6Q2729/h5YChMuanhtct33X3TDZMXL6V4bymPp6bRmWtgJutgmoheO1qnm7PVFtlqi88d7LqnTuHPC7p7/Hi88N//+w/IZrP09fXd8QA9QLTTi10UqDY1PFepm4KAHHOhZ2rUyy1cdhmPv31ddAYcdF4J8DVNo5AvEO5oPzgFqV25je6OMvjUIDbPR9vF2wmyTSJ0hcK3ejZFKlujjEXULlEpl7HZ7ds8u66i1WptBiJeh8JGUyNTaZCfzW9L9ARBILY3hjPsJD+bJzeTg2KrrQgqibgiLiLHuwmNhLZZkJRWSxQWCwQHg9sKJ4ZuUCqVmJ2dpePhDpLlJOZbJqMPjRLt+cn5RUqSROdIJ92DOfJns1QVLz63i6ZqUFkp0BGQUbwt0uk04VCYzKUMectC6xngQlWjxyfhWitTWa8QHLpWkEvOVEAMYu/1EbsyW9OKGKwvlTj7xhIdY5EPLQ52t5g5neDtH86ysVykpVoINPmziz9maCLK45/ZTaTn3i1oflYxEvXQOxBktdgkvF7FWqmwvLpMRdZJ9/v4RJeX+F2u650BB4EeH5WlEv7lMpYoUFdE3N1+uq67RwRBYDDi3pzbm5mZIRAI3PVoxEcFr0PhQG9gx9cymQwzMzMcOHAAj8fDwnyOqPNhXvzGGr74OouLyzz77LN89rOfxe/3s90+/P4jU2nxrbPrpEoNOhSJmAUtE1LuKKeyIidiNpKXLvH0009/ZPvQ1dXFzMzMtiLk3uFeXrqYYEPVGDzciXRewmwZiOMh6r0ONlJZdnuaXFxWIVtHqGjIfb621oMCctwFuSbZZI31YoOQ29YuiJlhxLwPe9BOdCiIoVukZzO88q0phv9VZMf521JD49xqkQuJEqpu4nXIjA4Eqca9lGoaggR7wm4O9wU/lPfdY489yvT0ZY4dObzpf/pxwj0nel/60pd4/fXXefbZZ/na175Gb2/vjnMGhw//fNEldu3axfDwMD/47mt867+cR6w70XQTwYK3gg6OPDnEE8+PbaNmOm0Se7v9vLpcxJWsoecbyNcZ7VqqgZlrUu/3ETrQwYOjMVqr7Xkyo2WwvrHO+GPj9BzsITAYuGkXpqUbVJo6sijgdyo/0eplLV2jUGtRatUYiPm3SXpfha7riIK46TPntSukWjrZTJsidqvZMadd5sjhLo7coRKbZVlkL2cRJGEzyTu9UmAxW8MwTZSOcfbu7aWUThD4VIBzZ+b5wY+XmGm1B3Mtq53kx7x29vcEGO3w4PsQFbrBwUHOnTu3ZSh6T5efC4kSQtCHPi6hLMvouUZbxKHTi39vlJrXRrDaJIhKNpejAoS87YXJJonULAHDaldXAWq5BoWqirtnhwBYAKfHTi5dp1lsbkn0pjbKfP/cOrX5Au58C8GymAw5mO7386kDXRy6R1+js6tF3l/KEy20EC5m0JsGot8GioSeqyNsVOkc8LMuwA8vJPnV433bREJ+keBwOBgbG6NQKHDx3AKpBYlKvkEw7uXAsW6CkZszCQYmYnT1+JlfL+MYDCFL7TVACjowgk6sSxniR7tx3WD0bRomuWyOeOe1GYhmsYlaUX9qSd6NqCarVAXQNJ1GXcPr8d6UIVGr1fC4rp0nQWgnOyULKonKFl/K6+Ht9OLt9BI/FKdZbGIZbesHV8SFfBM1zvxsHmCLcJWmmyRKTeaTeXp6ejl46ADTl6dJFBLoKZ3MXIZwZxi9qbfntO3SfaPdX+/ddz2CoSB7HusAJOYv5ciuFLHbFMZ6gzz9pT10T8RoNpusLq+S2chQrGlohogiCTQNE0u02hYYV2AaJqtTWZp2iW7ntevDLkvgt5NcLdEoNHDfZNauXG6SSlZxOhV6biJsc7e49N4aL/zlJOWWir/LR9BpAyxKxSbn3l8nl6zxxd87Srj7FyvZc9okPrO/kxdEgeVoESvfRBxx02jkaK6cJ1it8MPvXMYwTHoHg+zZF7+t76DPofDUsR5e1AzSCwUE00LpdtPpLRP17vz8X1lZwTTNjz0jzDRNzp8/j6IoPPTQQwiCQCZd5e/+6xnqCRPVUSA5o/PkI7/Ob/7mMz/RfTu3WmCj2GDQhOb769QqKqJbofdgBxcrBVymjYe6u9A07Y6VfO8WothmBdwIQRD44sN7+eMXz7DsCRJ5uBubCRURSk2dR0e6eHZ/J998d4ZyvohSLuOuKzhdrrZQjywimCaCZWFc1xVemcujt3R8VxglkiwQ7PGTXyuxtlpkYDC0ZT/yNZW/O7vO4lQGT7aB3NRJeWyoXW72jUX5/MFu7IqEzyF/6DjZE+lCCHSx6+Dxmz5Xfpq450TvkUce2fzzj370o22v/7ypbl4PU7dIzdjQijbCwz6cLhuGYVFIV3n7u9P4gg6OPDKw7XN7unxcGghSaOgEVyqoSyUEh4SlWwiWhdrlYtWr85mInZqeZ/ChQYKDQabPT3PgoQP07e27KdWnqRmcXi5wbr1EpaEhigJ9IReH+wJ3JRf7YZBaT1GoVPCFfTdN8qA9uHs9XePqIRmWhaHd3+vFaBlUk1UcwXaVJVVpspCtEXApbKyuUBQdvHl+nqf2DVCQWygRkZOTSUJjETp8jrY0smGSrrT47rl1ol4Hn9jdsSN//07Q1dXFK6+8QrPZ3Kz8DEXcnBgI8eP5HJYNeh/sxNm02pGpRyHX0CiXmzw6GuVExMOlFZ1iqsxcqU6+UqOlm/R6nPh97s0kWRAFENriGjvBurL56xekuqrz8uUU6oUM4bXqpgqZI9ugWlF5zS4zEHbf9bC4qpucWS3irOkIl7Igiyh91wI/yWvDUg20hSIdHoVlu8xy7u5kjn/eYLfbeeSRR4gFR/hv/583mapNIdgkLNXg0sk1vvh7R+m4SaBqc9t44gsT1P/iHKmZLGLQic0u0aqpYJl4+13Ew05axRaiTaSertMsNcnn8nT0dNBytpA9MvVMHa2u0ftQLx37tlbeU8kK5VKTQMhF9EMKZtwNtIZGsVpFtCt4b0FnNU2z3YmTtxYfZVFA1dsFEVM3b6kaavfatyhQ3vS7DJNKorLNx/BCosTlZBnR3c14f5BiociBgwdwOBycXj7N4lurLJ4vUyu3gLYZ8Z7jPXSMbu8Y3i1isRjr6+s72h/s2r8LwSbwwCdGmZ5aoa+/k+7R8OaxOhwOBoYHsI5amG8uk6zn0W12Ouoqtt4Y7o7rk+e2Empb4uAG8oZpIso3D6JmZ7K88L9PUkpWkO0y+x4b4Llf3v2h6KzNcovXvnOZqq7RNRK5zhNRIBhy4vbZWLuc482/n+Zzv3v0YxeQfdTo8Dn46vFeFodCZKsqotCW+T/3eh9v/NUMRt0AAU7bZOYfH+Czv7JvU4DjZtjfEyD+rIPlfB3Lgu6gE4dR54MPPuDo0aObVgTQ7o7l8/ltnmsfNxSLRS5cuMDevXu3xCorS0VKiTLx8SiiJLB8fp16/ierrqjqJpdTVYJOGfXkBmZDR+72YmTr5E4uETjeQQUXHT29zM3N0d/fjyRJt4zJ7hXXzwNfj76Ih998ZBdvTSeoYGOjVKA7FubYYJhjAyEcNonx/jgzvTX8JRG9plNoFbAsE6lkYAt4UIIOwtcVF0VZ3OapbJgWiMKONhzvLuRYvJAitlCCloHokLHly5hllQuCwHDMy8GbdHHvFKpu8vJUkskzSWyuI/zl92bYe6iTZybi90QD/ahwz4nef/2v//V+7sfPFGYvpVmfyxEeDuK8QlWRJIFIp5eNWp5zb69w6ME+REmkXleZOp8im6pi6CZhzaDkt5Mcl3GXNew1DVMRqfptiFEXrtVJvvOfv01cifKFx7/AyvkVREuEKGjrGtHdUQIDgS3709QMvnsuwYWZDK5UDUehhSVLTEedLKYqfPpAF/t7AtsP5D5B13Xm5uZQ7ApBv5/CLd7baDRwOnYenhVgR5GaD4OrNgpXK+Wq3jaA1psNyuUy+XqWnn0TTGUazKereG0yoZBrS4Bnk0VcdhnTslgrNPjuZALT6mR3591XhEVRpKenh5WVlU3uvCgKPDEWw6FInF5RmFxOEwwG0DQNuaUQdtt4ZncHJ4bCSKJAx/4ORt5sYdTqaE4nPkEiqKlYQYNULUUr18ITdRH1OlistdqD4NedVsuCZrXJYMiF8zoK8UK2Ri5RIZyqIwUdSFcWWbOp4840yKyXWchWOeLeWjm7HZZzNZKlBtFiE72hYevbXrkXbBKS3w4rFYyQg0sb5V/oRA/axbJ3X17AbErEJ6JIkoChm2xMZXjnlXk+/5uHbvrZ/r0dfPFrRzn71gqLU2madY2Yz8Gux4eYONzJxdfPk7mYITubRUCgZbRwe92sp9axDAun30nX8S6GPzFMdE90MyAuFBr84G8vsnQhjV7XsLsVRg538qlf3oP7IxZnqVarZHNZPC4XJenW60StWsXr3X5/WpaFJFhXEpT7s9ZYZts4Xrhhn4pXfMrqLY2zF6YYCTuId8aJOjrw0cOp19exdqlglxAEsNZLXDyXpH8gwGOf3018973TOkVRvKXpcndfN8VikZFjHfT07MyO6DrcRavYwjjbxOf1Yffbce5yki6lEcoC0WgUh8PByIFOLl5Mk6s0iXgdILQ9Y4VSi54THThD29d7VdV58RsXKKwWCQ8FaZRanPnRHD0DAQ4euTcvN4DpCylyGxWCI8Edje9tsoQr7mb+YoZ8ovIL19WDdrd1PH7tuAuFBjPvZpFlmfi+9nxkJVfnwlvLTBzsZGzi9jOWMZ/jBjq/E8Mw+G//7b+xvLzMv/yX/5J6vc7CwgInTpy47fYsy2I2XWUmVUHVDYaiXnZ3etud4o8QlmUxNTWFpmk89NBD24oOdoeMpEg0Ki1sLhm1oREI/2SfU5phohsmNgTMpr4p7tdAA9Uk4vNTNSxU3eDkyZN8+9vf5qtf/epH0kHt6elhYWFhxznA8b4OZLWC0x/m8nSRh08Mbkl+RmJeov0B8vkWoUQVd8MCRDSnzGLAop8aaiGJ5ulFURSGxyOcedlGbrVEsMuH3tIprJTpO9CxjQ1Qamhc3ijjTzUQdBOlt329W6aFtlpGStY4v1b80Ine+fUiJ19ewDNfJCqKNC2TU9k6EY+dB4fvz6zx/cA9J3q//du/fT/342cKmWQVQ7c2k7zr4Q46yG5UySarTJ7b4MI7q5Q2KlhXW9AWYBOxdXgw+/0IExFsosCRiJvBgMK3LnyH+dMJBM3koniRUG+Izp5O9KZObjpHYb5A7yO9dB68pvh2MVHi4myOyHQBodBE9NiwGi2CuQbVps7rToXBiPsjGQq+6iczOjpKsprE9l4W0zI3O0ZbYIHaauG/Yfi6pRuIFjjt0ocyYN4Jkk1CtstoDQ27z07AZcNtV1hJpqlpFnanh5W1BFWjm5BLQay0fa/UlRLaRhVLNZACDpQuD3LYRV/IxWq+zo8uJYl67XctHHLVc+Y73/kOkUiEr371q9jtdmRJ5JHRKAd6A1xa8zM1v0xNr/HwoUOMxDx4rqOM9ZzoQXEpaK+cJ+QJojgVIhMR4vvjSDaJYrFIVs0SjAmsTVXZUEQinrY6YUs3yJeb+Ks6u453bFHdrLcMqKugGptJHoDokMEwERo61dbdd1zLTQ3DAtJ1xFskA6LPhp6o4m6ZpK50OX6RUSu1SC2V8MTcSFfpl7KII+Rk9XK2nVxcl6yoLZ1CoQFAIOgkNhTimcEgrXILUzORnTI2tw3Lsugf7ufkyZOsa+tEPBGC/iCSKCGIApKt/X9n2EloJLSZ5Bm6yd/9xf+fvf8OkvPOz3vRzxs659yTc0QOBEAw5+Vm7a4ka9dayTqlfOzrexzKsquu61TZ5eOqa9+yr6ySdVeybIWj3dVKmwOXcbkkAYJExmBmMHmme3o65/Sm+0cDDQxnBhiA5Iqr5VPFKmLmne5fh/f3+4bn+zznWHh7HXenC3fIQTVf48LzCzSbOv/g1468b+/FDf/J7qFuEulV1NswRZRmE7PJvO1ssKLp2AUR2Sa/ZzRJURaRLBLNSnPTz0ciTkq1JpKiUM/EmMsZeAQvs+eylBpgmfDh6/e1GQ36dQuHqZU8lb+4yMd++QDhsXsPFmRZRlXVbf357HY7xWKRWq224zUWt4XRj49idBhEQ1HsoZsFMMMw2rYNclBlfDLA9FSGmLUGooCpqjLU4+XQYwPbJtSlUoNSuoq7w4XZasJsNVFJVcllqvf8egESy3k0Eay3kTZ3+WxkU1kS8eLPZKL3Tqwu56hkaoRHbgqQuQJ2Susl1lbyu0r0tsPa2hqXLl2i2WwyNTVFqVTi4JFjKJp+R1r+qYUML768gHoti6DDxU4niw/384kDne+b0EW5XObChQuMjo7uqLg9Phlm5Hg3195co5Qv0TkS5oEnh96X9ewEm0lqMZIKdYJhB425LOVsAaGm4Z7ooGAYrC7O8krxMrlcjlKphKqq78tabuwxO2F4eJjz58/jMrOlw+Wxmfjo3g6+a8C6R8Za1dEAAjaODPj42P5OrILK4uIiqqpiNpu579khzr+4zMZ0CkGW6BwL8JHP7d2SkFebKvVKE1dNQbylaC+IAoJVxlpVKNRVVE1/V9+ny8s5TCtF7C4LcsCGmK/TjJe5OJ/hxGDgA8MY+OAYPfwUQTaJCIaxbTKjqQaCofPdr15i6cJGSwFx0I/pFr+xeqlBPl5GS1UZDzt5/NkxzLKIoijcN3IU7Q2V6YVpZpOzPHH0k2xUmzjMMr4hH5VUhbXX13CEHLi73Oi6wcW1AuZUBSFbw9TraR+sWrGBK1ElFS4zn6q86+rFrdB1nbm5Odxud1u619PjIRp2sJwsU64rbQ+vGyiVituKJhSqKgHVoKPXi6vzva2OSSaJwFiA5R8t44w48dpNHOn1Ui3m6I5O0OVzEMvX0HQDuaKCXaa5mEdZLyNIIsgizZUCjdks1n0hLCN+un02ZjdKzCSKBIfvruq+tLTEV7/6VWZmZpicnNwSYLmsJo4PRwgKZa5eTW77mYmySMehDkrWEn1dfchWedNskNfrxev10uHvwPJXFzh3bpU1QcAwyThkE35B4PCJHiafGtq0EVlNIobFhGCS0GsK4vXPz2hqIIoYFgnbPdARDON6zH2bDgNwy81k3PHSnwWYzBIms0TtHSauakPDErC3E5lctsrZ02tcOb1GJVcDw8Dus7HneDeHj3fjD2ymVpYTZVZOrRCvxsmYMnSOdjK0d6g1iyaLiCYRXdHJLeTIzGaI7G/RNudm06xOpQn0twytATwRJ6IssnghwepKgZ5turXvBrVajfX1dbq6urBYLEhDEoFz65ibBrWmhm0bv8xavb6tSqmittQBvYJI8F0kUO+EIAgExgIsvbSE6xYxrpBN4r5OM+heEtYBitkip1+cRjN7cPrtOCJObo1PRFHA5zBj6/Owspzn9e/M8vE+b7v41Sg1aBQaGLqBZJawh+y3TVYjkQiJRILOjk50VUeQhE3XR6NRzpw5Q2dn545m7eVqmY6xji3KiIIgbBLSsNvteCIyq9N5ZNnE4MMdTBzvxtO9/ffB6bRgc1soJMrYPFZqxQY6Os67VH18J3T9zvMxwnWi6Yd7TAsWq4xoFlGbKpLpOotDawU35h1mUneD0dFRnnrqKX784x/z3/7kL3jyF3+L09+ZxmKRODAW4r4B/7YdukJN4Y2zMeTLabySiGASacznuWRdY1+3d4vP7nuBa9euUSwWOX78+I73AoAsi3zui4c4N+klk8lz34lxAoGfHHUdWvvE/m4vy5k4+mSQRr2MXBRxTfgxhv0srKyx9vYr+Lqc/Pqv/zp/+r/+jMurGVKiD4dZYijkxPEuPtd3wmw202w2MZu3L+KOjY3xne98h2PHjm353WDIyS/f3883Gkk80X5kSaAv6GA45LyeGJrb3cJms4kgrOL0RklvNOjqirDvYA/mbYo6VlnCbDehWiTEqoLkurk2o67SjNgJmKV3XTRQr58pgnx9z5EEBL0lPvdBwrv6tOv1Ol/72tc4e/YshUJhy2CmIAj88R//8bta4AcRQ2NBXvNYyCcr+G6ZV9A0g8pGCatVYvF8gsCgF6tj68FldVmIjlnIrZc4891Z/AEHx072IssyQ54hrA9Z6dvfR9Pm5+WZFHVVw2aSONDtZSDkYP7NeV79j6/yT/7zP0HRBQo1BWtZQbDKm6qnosuMli8iVBXKjfeuopPP54nH4wwPD2+6uR1hB9FhP6FEicWagsMit7nTmqohCCLiOwR7mqqOpmmEZYnwntCOYgfvBr5BHxsXNignythCNmqZOJ+9fxRZllnN1pjdKBG2mdBSNQSLhBIrYepyIdyS1Gj5OrXzG0huC6aoE4/NzIW1Aoe6vDQ3KmRX8tRKTSw2E74eN55ez7avpb+/n6eeeorFxUVMJtOORsl9fX386Ec/Qtf1bWdWKpUKTo8Ty20CI0fIwWO/coQ9x3uZO5+gXKihGnW6J334h91Ijs3P3R904Ot0Uo6VcMQriE5Ty2Op1KQedWA4dfq2UYu9E+zX3wchZMeYyW6rOAvXVWcdJmomkYH3mQb40wCL3cTYkQ5Of2eWokXC5rFSztSgrrLnWDeCIBBbLfD1/3WO5LUMFpcF2/VZ1Eq+zo++cpnpt+J86lcO0XOLwmRuLoegCwxODmJbt3FtdZ2KuwdF1RkKu+gL2GlqTRbWFvBc8RCaDCHKIulUBa2htpO8G3D6bKwnyqQ3Su9popdIJNB1ncHBwfbPPH0ewp1uQotZEjVlS6JXrVRx2LcPvPI1hQAiHZ2uLfT3dwvfgI/1t9epZWrYAjbqtTrlcpmO6+qlHR1R0vM5sjPXUAUDs9faoipvA6tJQg45WFrIklnI4vDbycxkSF9L0yw2W/RTs4Srw0VwIkhgJLCp0AMtilLiWoIXv/Yi9fU6Rw8fRRAF3D1uAiMBXF0uREmkv7+fxcXFHc2U8/n8FpuG7dA11EXXUBeaolHIFyjXyhT0AkpaIRjcmlRbLDKP/dwEz/3lJRLTaSSzyMSDfXgDCoVCAY/nzt+jZrlJs9JsJb4mCavPSqDDhaDqNFUN8w40v3KpgcUq/0RnSz/IGBoOEh7wE7uaxN/rQZJEsqtFXFEn43vvXRXT6XTyyCOPoIomzqdsXP7ONZxFhZIk8MNrWapPD/H0ng6uXLnC+fPn+cIXvgBAslClsFEmqBmYultJnVFX0bM1MpUGw7x3iV69XufcuXP09fUxMjKyq7+RZRGzrcxHPvb+MRjuhH1dHtbzdV68uIh5wEfQ5ybR1NAaCpbyOuNBM1evXuXP/+8vM/70Fzi7kkPcWAWzRHfYwScPvHfK1j09PayuruL1ere1pbLZbNhsNpLJJOHw1u6wx2ZiPGDi2KHbU7bNZjNDQ0MMDbXGhWKxGHNzswiCQEfH5mKUz2FmOOLifNhOcL5VuBdtMlqpieEwkXcLPBy8t6LS1772NQqFAul0GjUwRD0cpBErIxebqKpGrdPB8QH/B6abB+8i0VteXuaxxx5jaWkJr9dLoVDA7/eTz+fRNI1gMNgewP37hki3h0MP9XHq+3MkKk0cfhtqU6OSquBwWaiXm3i63dsmebfC1+EiOZ/lzIvzHDzaidHQyCxmyCt59hw6yovTSURRoNNjI1ttcilWYObCm8SvLqJpGs9//3lcAS/5jIbdImFpbk60jaaGIEvosoDpDjMtu4FhGCwsLGCxWLYNDARBILwnzMBMhlK2wnqhRofHhigKlMslPB7vpusbqkaq1KBHhf4xP/6hu5v92i0cIQe9D/Yy98M5Fl5bYOzwWFuJKpGvQamJlm8gR51ouTpS0L4pyQOQvFb0UpHmcgFT1EnQaWZxNsMLU2cprhVZz9fRBBANCDnNDAwH2PfE4JbXJAgCTz31FKurq6xvpMiUGwhCSyFVuiVJlySJcDjM/Pz8tgfQxsbGrjj3FreFnmPddB/tQld1RFlEEAVUVWV9fb1l5nzdVNlttfLQaJjvVxWydhlXuga6QWXAg9Hn5uSgi7lLZzFNTOD3b/9ZGYZBNVWlkqowfWUaRVMw7DKSYqXktWAziWil5qYKG4Ch6WjZOvJEAN0qM76DTcnPGh55doxKscnchQTZZAWL08KRJ4c4/sgAtZrCt/7yPMm5LNHx4CZhEYfHiqY4Scyk+dafn+eL/4/7cTotGLpBbjGHYTaI+CIMDA3zwvQGa7kqJkkiv5wjvb7K8uwVtKZG6FoIyyUL1qCVciWPDqhNDfmWxKJZU1odpvcoOW82m6ytrRGNRrcM+ZtsJkKTIaLLOTKGQa7SbIsDGXpr/lbaJsAv1hQM3aADgeBoYItwyruFPdAyUF95bYV6s45u0gmGNic4K7MpGpUGrpATU6+7xRjYAV6nmcxGhXPfvUbIZaVRamAP2fEMeEAQMBSd8kaZ/HKe/HKegUcHMNlbe5paV1l+dZkf/c2PWFtaI9AVQJAFdFVn48IGyUtJfEM++h/pbwdhuVwOn+/dO1ZLJgl/yI+f1v5QrVZZWVkBWoFaOBxuF672H+wkHHGRiBWx2k2MjAaRZJF0Os3y8jI9PT1bilyGblBaL5GezTB/cYNyoYamw8zMFKK5TtNUxiRPkk2UiW7TTdR1KMdL7D3UQfgd9ho/q5BlkU98fj/f++srJOYz6JpBsM/Lo58a35IMl8sNLrwdZ2OtiMNtYc/Bjtuqpb711luYAr2U31ggWGhiijox6irSUpHzF+JcfukbZNZXkWWZ6elpAHJ1HdkiohgGcrmJYBLRmjpYZey3oeTeLZaXl0kkEhw5cmTHbtR2WF1d3Vbk6CcJkyQyaC7iO9xFoiGRqzTp9duZ7HDT98QQavNjVKtVXpuJc3m5QGSphJCqIrjMrNYUXnda+PTBe5+FvQFd13n++ec5c+YMkUiE3/7t3972ukgkwsbGBj6fb4sK6Du9+HYDWZbp6+trryGRSBCPxwEIhUKEQiHuHwwQz9fYEMCdqiPXFBoRB6Wonf3jAYRcjNnZ8l35DN4Q+bp06RJ+v5/f/LkneWk2z8zZdfRSA9FpYuxQJ0f7359Y9l5xz3fNv/gX/4JCocCpU6cYHBwkHA7z5S9/mQceeID/+l//K7//+7/PD37wg/dyrR8YCILA45+awBWwc+G1FfLJCpIscvChPuo1lZk313D6dtf58Ha4yKwUmLmSZKDHzeryKiP7RyipOg1VJ+yygAAui0y+2sTn9tLZ20k+maevp4+JAxMo/iQvVpZxJKuoyQqSz4qh6KipKmrUiT3qpP8u6AWZcoPpRJGFdBVN1+kPOOhxSdRzCYaGhm7rE+Ib8DH6WD/KcwvMFmusF+oImoLLam4pJhkt8ZhiTUE3dHoVg739foYfH3jPg69b4Rn20JhuMBYeo7pRpRxvGQUXV3LIgoBtfxjRbaHy+iqiY/tZRtFlRktXW/OWuTqVU2ucaeoQtOLscGKWBFTdYLHSYP3iOrlMlYd/fi/+4c03fV3Rmbj/Y1TeXOWP/vubCKJAqNvN4SNd7OvzIUtiS25dkggEAmxsbGzxG9qp07cTbsxe3YAsy+3D6sZGWa/X8QkCH9kbZDrqJpatggG9XiuHen1MhJ1UUwGuvnkVQzQ48sgRXnrlJZ588klEUSS/lGflfIL5yxsk0hVWY+uUCjlsJp2xh46w7LJhH/ahz2TRK00kjxVEAb3SRC82kDtdpEI2Or02ht6Fp83fJ9idZj77a4fZWCuSz9cIBO2ErosAXTgdZ+NalvBIYFv1SMkkERkNkrqW4cqFBMcf6ENXdaqVKoquEPKGKNdVqk0dn92CzSQRy9fI1quEgiHSqTR2q51oJIqr04XfH2HmzRyphRzhIT+SSURTNLJLeSLjIYZG740Oeet3OZVKUa/XN3Xx3onw3jDDa0Wa59eZUXVSpQZ+h5lqudyyW9j02Aa5moKiaoxqAsN7QkQPRnd45HeHjiMdlMtllt9Yxml1UhWqmK7Tn+u5OsXlIoZFxjrm32Stsx1MkoheU1h/K0bwsUFs/V6WMhWW4wU03cDnMNMfsBMK20ldSSFKIoNPDqKrOosvLZK8lOTIQ0eQ/BKFcgXVKmOWRRwhB2pdJT2TRmtqdD/Sjcvlolhs0epvpa3d7R6zHex2e7sj2Gg0iMVirY6kJBGJRIh2uIjeUtSpF+qoMZXqUpVXv/cqge4AWbIMHRwiEoqw/OMVLr62zFq6Sl4W0S0iTVUhb3GjpFRGgoN0eJ2sJius6xCIOjFf3/eqNYXcSoFQ0M7JZ0bec+Gvn2Z0dnv4R//kBGurBVRVp7vHs4USVyo2+PIfv8XapQ0EoTWmcunVJZ795YPs2bf1nrp48SJ9fX1MF0SEiopolVvz3lYZc65Oqapx+PB9lJNR5ufn6e3txW63YxgG61qMc6k6arKKqBuUfGa69kUYvI2tzG7RbDY5f/484XB4V8Iwt0LTWp6Yf9eJ3szMDOFQkIltOmgA2K2tDldcw7S0AatFBJ8Vbb2M129lKV2h2lTfdeIsiiIejwdVVXcs/t7AxMQEU1NT7N+/f9PP19bWdsUaSCXL1KoKbq8Vr/fm/imKIp2dnXR2tkSlUqkUU1NTADzY5WDF18u1ZJm6ouG0mTne5eZonx+HZZBEIsHrr7/O/v37b9uYUhSFmZkZKpUKDz30ECsrK3zuc58j7PfwmftcLA/6KdQUXFYT/QEH5p+wb+idcM+f8osvvsjv/M7vcOzYMbLZloeQYRhYLBb+xb/4F1y9epV/+k//Kd/5znfes8V+kCDJEiceHeS+B/oo5euYzBJWh5n/+v96HusukzwAs92EruoszKSoVdfoHejFaBo4nCacFplUuYnHJpOvKnhtJo6PHaDZV0VHp3ewdXPs7fJwdchPsqnhX69gpGogCzQ7nRT6Pdzf6yWySyPUuWSZ716Kk1ktYCsrYAhc1Os4o3Z+/sHxXZlBdhzqQJIlHK8usxYvMlesUpO9lAs1EARMuk5YgYhFpnfSz9BjA3je49meW9FoNLh48SIPfvJBREGkFCtRy9UwdIOFBRdFEWxdHpR4qTW/sUUnvAVDMxBkEUPXyb+9Ti5VwT/oYyjk2nS9x2Yia2syFS/ifm6Oh7oOtQO+SkPlb56/xtwLC1jKKjaziG7A+lSab55bZ+1jYzx7ohdZaqnm+f1+5ufn8Xq9bXnk26np3QtubJQ3HtuRSuE2FMpOCa/PS08kSGExx+w3ZijFS2iKRkNp8N++8t9Iy2ncbjf99n5e/+YMc+tFimYBTdYQ+4KU1xWs/jCJ1QpSo8j6gJfQwTDm9TJ6rt6iXdlMCPvDJL1WPH4rT09GPlDSxH/XEASBaI+H6Dsq51Nvx1r3k2Xn90o2SwiyyNW34xx/oA9FU6g1a7jtrWTRZpIIOs2sZlsiGF67meMje3HbTJSzZZSi0i4QOBxmnvmFvXzvry6yMZturQ0Bf7+PZ39h77aGtbfDzFSSMz9eYmMxj9kmEx608NjT43cMosxOMwOPD7S+O5c2WNJ14tlyS5zGrCGKArpuUGlqaJqOSxQYMURGJoIMPj6I7S7257tBsVTENe7i/vH7yc5lyc5lqV33wzQ5TLhGfaxXm8jBnX1Cb8DQDUjXwGHG8Fl5bT5FqtTAZpaRRYHVbJVYrsa+TjdDfR7SV9MEx4PUsjWSl5N4B70Ykoize5yFpXV+eHUDkyTS67MxGnXjH/KTvZbF6rNin2wlY4uLi5sS7HQ6vaMoxb3AYrG0P1tVVdnY2EDTtPa8X2G+wOrrq9SyNUx2E3bRzuLri5y/cJ6ZPTOcfOBBTr+yzKoMst+KTdAQDA2zy0ZP2M38skzZ42e9qBB1Wyg2NbJzGQxBwNANzJJE34CPJz49QffIDgHyzzBEUaS3b+eu7tunV1m9uEFo2IfZamrRg2fS/Pj71xifCCPJYttWa3Z2Fo/HQyQSIakWwGtBXagjZGsYdZWqWcQdsnPy6Dh28xE0TWsXFQRB4Jm9UZxWiamZNJqms6/Xx8MT4XuaLWuqOsuZCuWGilop0MjGOHL4MDbb3e8Ds7Ozd9UBej9w9epVotHorjrwFpOIJgogi+gVBQBdErBIIuI21MJyQ+Xsco7plRyyJLJ/wM/+bu9tE5cHH3yQQqnEtViGaxslwm5rS+n7FoiiiCiKdHV1sbKysimxu9V+YzsszGd47YfzrM2k0ZoqFruZ4cMdPPL08Jb5c7jZ0YMW9dy0vk40pCFbbQz3deO03WTaRaNRgsEgFy5cwOFwtPUmbqBerzM9PY2iKIyNjbU7j7/7u7/bjsdMkviBVwi/50SvWq22qWNud8vVvlAotH9///3388//+T9/1wv8oEMySXiv0xvK5QZqU8N0l5uRKEssLcR44uMPM5OYIXkpSd+RPo70+Ti/mqfa0PA5TBzq8WGSBUr5Br0P9eJwtZ436LTwyQOdvGiWWO4ooxUaGFJrI324x8tDI7s7rAs1hR9cSVC8kiK8WkKtNqlUKvS5ndR0Ez+4vE7QaabDc/sNUhBaFgCuTheW16/St+Eml6rTVHQEDGwOK9F+L+E9Ybz93vfViLler3P58mUOHz7cnofz9HraiWXEIjA/nwFA8tsQ3Ra0fH1Lxd0wDPRyE8tQBC1TI72cp+qyMOG2bpsU+h1m1hsqC/NZ9i8XCI63uh2vXVzn2nPzBEURy4i/XVV2qjqVlQJnvzNLT6eLgwM3A5HBwUFmZmbam9B7HYDdCkEQCIfDbS59Npvl4gvnWX99HYvZQnQ4imyVqZaqpF5MoSkaX/13X+W+Qx/haraK7hQJOMw4HK1gNuJ1YLFaKdcV8pkq5sU8VZeZzLgfS1NDMASasoBolenx2nhiIkKv/86B8IeAfLqKeYfu860wO80U0lWazSYvvfwSjqiDxnIDV4cLSRI42u8n5LSg6gadXhvu64e0WlTx9no3yeOPTYaJ/B8PMHVpg3KhjsdnY8+BKM67VJ+9eC7O9/7sPPV8HckhkUs2SC/JKFUTv/CrW7sJ74TNb2P4I8OYXWYi1zLMLiRouFwU6yqKICBiEDQgLIhE/A5CQz56H+jdVur/vUAul6PRaLQLJq5OF533dbbN0E12EyVJYO5HSyiqjukOSXEtV0Ms1PFPhrm6XiRZatDpsbfFW9w2E6WawlSiSMhtRTAMkleS1HN1LG4Lgizx1nKWhVQZh9ONzSTSUHUux4vkayr3DwZwRBzk5nPQAUK0dd8nEgmi0VZ3pl6vbztT815AlmW6ulrUMV3XufbmNRZ/uIhoEokMRdoFxTevvom328vay2t88+2XaRzuw2YVkY0GVosNk/nmXjEy0INhQFKqkyk0ONTnwzUZpFxTEWWRzm43g5Ph92UO/KcVqVSFxWsZVE0nFHEyNOzfsYu7Op9BMkuYr6t3C6KAO+okGyty9eoSS8tTzM7O8tRTT2EymdrUupGwk9H7e5hWdCypKqokIo/7OXmgs91ReuesusMi8/SeDh4aCaPq+hbF8GSpTq6iIIkCnV7rjp2pfLXJt86usXB+nUw8hcXv4MEnJhBNdx9zlEolzGbzXdE832tcuXKF7u7uXc2wAoxGXFwI5qkNe3Gk6zS7XBR8Vh7tcG8ppjZVnW+dXePqS4vYUjUMAVb63KQeH+TZfdtbsEDLmDznn6SgFPiL08t47WYeHA5uoi/eUOcMBAKk02lyxRKpGiQKNebSCv5kmf6AfYs4ysJ8hr/90tuUEiVcHS7MvpZw0/kfzJFcLfKLv3F0U3fvnbghTAethDK2soSmaZjMZgxHgFRFQxIFBkb2INSLvPbaa+zbtw9VVfnRj35Eb28v4+PjW0YI3g9PwvcT97zj9fb2sra21nqQ65v2qVOn+MxnPgPA1NTUrro/P21YXSlw7tQKyzMpzBaZ0YMdHD3Ri8ttQb4+/2Tod6e4U6mU0fM6X/rSl2hkGowJYxTXikS73TzhCNNQdawmCUmEwnIBm9+2hQ7Y5bXx+eN9rGar5GsKsijQ5bXdlbn1tY0SybUC4ViZmtaAgIngQA96tYm8WibpNjOTKN0x0bsB2S0T2B+gO9pNJVVBV3QQQLbIOMIOxPe5vV2tVpmamuLw4cM7Hl5DISenF7PUFQ2rVcY87KN2NoEmiYhuM4IgYKg66kYFyWvF3OumuVYiVajh6nDdtsLospvJ5Eusz2UIjgcp1RUuvR3DUdOwjHo2DesKsoijx01lIce5s3H29938fAVBoKenh5WVFbq6e1hNZtkzNnzH118qNXjrjRV0zeDQ8W7870igbogn3Y6e5bK6IAahSAiT30SmkMHIG2iqhjViZTA0SPpKgfMzcRyjESK+zZU5q621B7hsJuSwk4JRZriiEurzkzYMVN3AY5UZDru23eg/xM6QTRLGLtS9dM0gn8/yL//lvyQYDDLeN06no5NSvISr04XdLDHRuXlGolFqoKs6oYnQFoqb12vj5EP997xuTdV5/blr1EsNnD1WzGYLYWuAWqnBwlvrTN+XZP+hnQOLG7C4LQx/ZBi5Rya8L0hpqUSt1EBVdWRZxOowExgJ4B/244w63zeqXiaTQVXVdoJ0Ayabqd3JBxg/2sW5U2vkyg3CtwlOAAqJMg5JwjfgYzpTxmc3887b1GUzEctVWS/UGPLbSF5KIggCvmEfqXKDlUyVoMvSVja0mCQcFpl4vkYsX6M/YCd7LUs5VoaJlnjGDcuFu+12aKqOdK/7uQHamobP68PT56FULFEqlWg2mhSLRXq7eons7+HSVAJTtY47GNh2DhNawr1ht5WYqrEcL/KxJ4faqrEf4iaaTZUffnuGK6+ttNgtGMg2E50jQT76i/s20WlvwO40oyubFYCVuooh6Pzxn/x3BEGhUqkQDAY5ePAgS0tLuN1u3G43nzrSzVCHi4WNEjaLzGSnh5FdqGdaZAELNz/rYl3h5ZkkM/Ei1WzLyiMYdXFfv4+jff4txtmvX0tz+fszWOaTDPm86OUm515Zoifg4HDv3c2kLi4usm/fvrv6m/cSly5doq+v767m2UbDLp4Yi/CmxUSqW8VqEjkadXH/4NaO9lKmwrWzcQLxCma/DUPVqc7nueRPcKTfv614i2EYvHB1g9mFLOG1MlJVodzp4gVVJ+y2tou2NxI9s9mMv7OPP37xAtWGlUosi25orNUWGOnx8tF9HZu6ga/+4BqljTLRyXD7s7U4zDgDduJXk7x9apUnPrK7DqvT6WRsbAzDMPjBpRgvfuttlFgJwSQR3tvJxx8bYWxsjG984xucP3+eUCjEs88++1OX1G2He070Hn/8cb7xjW/wb//tvwXgV3/1V/kP/+E/kMvl0HWdP/uzP+OLX/zie7bQDwKuzab51p+epRAvYfFY0RWN+NUU81eS/OL/dhSX24In4mT9WgZPZHczRoV8AdEQOf7gfhr6PFdrVwntDVGcLpI6lcLkNiGaRLSGBnXwdfsYfHIQe2Br10MSBfrfBYc9lm9VkQsbWVyj4fYXXHSY0VI1bBWFxXSFR8d293jLy8sMDw9jGCCHHYiCsK0c+r3CMAySpQaluoIoCEQ9Nyt7lUqF6enp2yZ5AH0BBz0+eyvwCTqwjvpB1WnMZVFWiiAICALIARuMexFdZpo1BUWHiNN82+DRJAnURYF6ueWvlSo1KK+UCHis2yoyCSYJuyyyEStSrCubfudwOLi0mOB7373MykKSN2YV7j/SxaEe747qTt/8q4vMvr6CocPSTJpf/ccnkCSRa7Npzr2xwup0GgHonQxx6EQPQyNbZ6wKKwVqmRq+IR+iJKIgs16ok0ksUKvVeOvtt/CXerFEw0S8t//O28wSeZeJ7EaFh2SZyL4PA7DbYW21QDpZJhB0bFLNvIG+8SCJ2fSOVOMbaBQb9B8L8XMP/BqnT5+mb7wPn+Zj4YUFMhsZbCEb0nX6p6EZ6BUdsSnSc6JnS0HpvcD6eomNpSySS8DpdLXvIZvLQlbJs7aU31WiB609QHSJDOwdQK2rKFUFTdEQZbGVaNnfe+/QW5FOpzEMY8sM7Xbw9XsZngjx1vk4RbOEexsfVoBcuUk9lqUrYEP0WGgmi+0u6zshSy37DdElolSVltWKSSJTrqDq+hb5ekkUkCSBjWKdgZADRFCqN/eazs5O5ufn6ezsRGB3e/Wl83Fe/JspooN+PvfLB5FuKdaoqk4uV8NuN+HYoehYTpQpxoq4Ol0IgoDb46ZQU5iPL6BqGhdPX8RSjiA4vPg0ecck7wYEAbwOC6lKhaWzcYITwW1nWH+W8fy3Z3jr27NY/VYi4yFESaBarLN8YZ2/rSn8w//9BK53qOvuOdTF7OkYqYUc7oiDRkWhnK5y5NkRPv65j/Ltb3+bl19+mS9+8Ys0Gg2KxSL5fJ6VlRVUVUUCRmQQDREKVTaMVhL4zm4JwMx0kgunVlmbySCIAv17w+w52snbuRpXLyXwrZYIlhUMSaDsLfD9dAXdgBO3JDC1hsJrp6cxxQt0TvQiOkyomRryapHZRPGuEr1YLNa6J/4OlBRviH8MDg7etcChKAqcHA6yp8tDttLEYZY2GdtPT0+ztLTEwMAAGdGLXlSQTWJbEdhaaZLO1yjWlG0TvUylyXKmSiBVg+UCuk3GMZ9jwy6xmC5vSfSaqs73LycoxXWCKxuIsVSrO5nZ4Gq2hijAzx/pQRQF1uNF4jMZ3B2uLQm8bJawOMxMvRnbdaJ3A6u5GmffWie83MQmWtFLGonXl/mjRIxHR2QmJib40z/9Uzo7O/n617/OQM/9XHk7hiSJHHmob9t51A867jnR+1f/6l9x5swZGo0GFouFf/2v/zXxeJy//uu/RpIkPv/5z/Of/tN/ei/X+ncKXdf50XdnKSbKdOy5WV1o1hXWLiZ469QKjz09wt5j3cSvtChtdzpcapUq1XwDb9TD4WP9RKL7GBwcZHJykubJJrn5lo+V1tSQrBKufhdySKYoFCmsFDY9liRJ14fprRTydVxuy7Z0qtuZlRaLBcrlMr0u1xZ6giEAgrjVBH0H5HI5rA4Xby/nOH95g0K6iiAJ9Az4ODASZDjk3HLz3g3i+RpvLKSZT1WoNjVEQSDgMLO/28Nk0Mzy4jyHDx++48YsiQL3DfiJn4uRLjUIuizY9oUx93lQ01VQDUSHCTls5+zFc+TmzlNfNvBoInbT7W8fRTOQdAPLrfQ6Xb9tcigIAi13cTatvVBTODNTJf2jZWzFCoW5Gt9PVnD/3J5tfYV0XWdjIYfVbUUyi2TWCtRqCtNXNnjhry5Tzdfbs0qXXlpk4dw6T/7Sfg7f173pcRqlBosLi8QbcQKBAHNFiYVMFbGuc/zYcUwFM+deiWERhNsmGzfgsVvI5JusXNr4MNG7Dd5+c40XvnKJaraGzWvlsZ/fy7H7Nw+s7z3UyYVXlshvlPFGtw8AChtlDEnj5COjjE12MjQ0hK7ruFwuAuEA62fXKcaK6NVWZ1BAwOwxY+2zQi+sxdY2PZ7ZbMbtdt/TfAu0gpZEYh0Ah2ObLts23qS3w60KeLJVbnvO/SSQSqUQBGHXNGrZIvPgp8eplhtMz2Wo+G14PdY2haqmaBTyNeRcHaupSiwfI/bDNcTuAzRUE/ZtimSq1rLe0VUdURLb76d+mzleURDQbv39Oy41m7z8v//t97BiZ/JEnmc/PXHbYtn8dIrEVIpGRaFQqLeZA2/8eImzryxRTFUw20yMHe3isY+MbEn4muUmhmpwZeYKJtmEy+3mSl4kWYDxyUN0j9h583QMRRLQq8p2S9gCh0Vm3SoRXylQy9Zw7rLw+rOAbKbC5ddXsPqs+G7p3NndVsyjJjauZbl0fn1L1358T5hHf3Evp5+bo7BRxmyR2f/kIE9+fBxFadlo/Pt//++BFrXt1lmpW6HrOqVSq2u7tLREtVrd9PuF2SqXX06i13WcAQcgcP65Oc6+tkK1z01/volYU5ECNtB0HPEKIHDab2NvlwenRSafz3P+4iU8Hg+GuzWP33pyA0MQkITdd591XSebzbYofZpOqtxoCSLZze+ZH11D1VhMV1jNVlE1g6jHynDYidMic/HiRUZGRrZNiHcLj820ZW5OUVpWJi+//DIvvfQS9o5hBNcelDUNsdjAUHXqBtg8NtzW7QtNumGgGQYWrTWbKZgljKaOoBvcSmyTZRlFUVhPV1hYzRNcKSHpBkbEgjnsQd2oEFwuMRe0szIQoD/ooFppojZVXDu8btlmolFpcvFcnCtn45SyNbqH/dz3QB+hd8RE1WoVSZKwWCwkCnWaiQpuwNTlahXqlgtY7EEOnNjDxdeep7+/n0qlwks/vMycxY6maBi6TmIhi/23jzPwPinEv194V9TNWwcqrVYrX/rSl/jSl770nizsg4ZEvERyMYeny70pQTFbTch2MzPn1nns6RH2HezgVNccqaUC0ZGdvwyNeoNKtY5WMuh7KEIk2tpwbygSyQEZe8BO59GW2e16osTaSh4pbTA4Gthi0qlpGpcurPLcV69QTtcw2SQOP9HDvkNhBEHAZrMxPV/hzEtLPPD0MMcO35TWVVWVubk5enx25npDiIU0WqaGFLAhCAJaoY5glqg5ZYZ3qYa4FEtwaUNm/kcrmNNVLLqBBky9FWe6z8PJp0d4bDJyT8neWq7KN87HSZXrdHhsdDktqAJkqwo/nEpwQSzzG88e23X1bbLDTbGm8OJ0krqqEXFbMbktiC4z1UqFWq0G+To1FTZqAqO9bva4HKzWmnBdFXU7lKpNuq0mOoZbVUaf3Yy900V1Ko0nvHXzMjSdWlMjEGlt8LeKrqxmq2SupXGUa7jGIhi5BqmZDPMbpW0TPVEUGT7Uwbnn56ECY/f3oCg6r/ztNEpTo2vvLbM3XS2bj1f+dorhsSDuWyp+iqKQz+fJFrMoikJg+BAuq5uezl7GBwKsnIkBwq6FOMySSEkUqBQb7cH9D7EZuq7z+vdmqZcVOiZCpBZzvP69WQ7f173pfe7t93HosQHe/PYsGUXD2+FGkm/4VhoUNsqU0kUOPzvM2GSrQ+Zw3Nw3fIMtqlx5vUw93xLGMTlMuLvdO84xNZstOt3K8jqlkkIwbEW6ngSIoojT6eTqxRypRIlnPjWxadauXC6TTCbZt3+A8yNpYleS2N2WdnJSydWQbTJ9u+wiVioVbDbbu1aG3A0S6yVymSrBiJNQyEEymUSW5Tsqzb0T7k43H/niIfzfneHaVIr0Qo6cJKAoTSRVJ+Ay07vXS3UiQOpNnSMnjrDQlJhPlrF67Zvom+W6ilWW6PDaqCXKeHo8VLNVdE3HZzchigKqZiDfYq2j69BUNcIuK4ZhYGgGsm3zZ33uVJzych05auH8CwtMHIgyOLSzeMnB4z0Us3W6BrztJO/N11d4/i8vIgCOgJ1GpcmZb09TLtT5B7+22X9MEAXq9Tq5bI50Jo0oiDiHDhH2uxnuD1C+kkQXrwcru/yoBQEEk0izqbVGBj5EGwtzGWrZGuGxrUmYbJYQJIGFqeS29Oz7H+znwJFOUhsV7A4zoZADVVU5depNjh8/vkU6fzvcUGrcbs4slSzzw798BREBV78DpdnyjZSDBqvzWcS1IlqHC/OQr312yIKAI1cns1FhLVtFz63RbDZ5+MEHUK5u8Op8EfN6BVO2Rl0AfdTH2F3Y91y7do2RkREur+U59eYqiYUchqbjCjvZf6STkxPvTjysUFP49sU4c6kyGLTv26jbzIBc4JEje+65sHYrisUiyWQSRWkVS0wmE8PDwwQCAY4fP86Jkw/y1TeXmSk2sKVq6AI0B9wcOxjd1AW8FUGHhS6fjfmglXDZAXWFZp8L2Wel55Z5aJPJ1BJgKuoY6RpiRUHodCAXagiigBy2Q6xMM10lWWrQH3Tg9tqw2EzUi832XOitaJSaSCaR7/zJWZrX2QyxK0kWLiX5xd8+tskaZGpqipdeeglJkqjYwiD3YKh6qwCg6mgGyCYRs0mis7OTz372szidTqbO1lm/UG7HS7HLSZbmMz87id6v/dqv8Zu/+Zs7ytO++eab/OEf/iF/8id/cs+L+yBB1XR0VW8HUrdClEWUZsuQ3Omy8MRnJvne/zrPxlyWYL9vy98oikIuW0DLGkRHAjz5qfEtj9m+VtX4zteuMH16jUaxAQjY/VaOPDnEY08Pt4OcRlPjx99epJZs4u/2UknXuPxKkn0Hh+jp9VGtVlm8Nk9uo0wiVoTriV46nSadTjM8PEy3BjM5jY1Ck8BKEWWl2MphbDL5Hhe+Hjdj0Tvzw1dXV5nOSMw9N0+gomKJOhHtLZUuV7FB5VqOHyszhLxW9nd7d/X+34CuG7wymyJTaTDkslK/kKSYrCBIIt7xAM4uJ/G8yGyyzN7O3St5nhgMYBJ0nr+4wsV0FlEAu1nCYbMhmZwU6wqBjl76IhV+7ZljLHxrltzrKyRtjbYFxq3IVxRMhSb94yG812l3PoeZ0QNR3p5JY9uoYo7cTPYM3aC+VkLxWDh0pBNZEpEkCU3TWoPqAoDQUieTJFTdAPH23Y+PfmYPfcMBdE1ncn+Ut95YoZQsE9nmgA/0ekjOZrjw9hp9Q+b2YaBICr39vdQTdU4+cRKP10epoeK8NREwDETX7njshmEgAJIsfpjk7QBdh2ZdxWyXEcSWkIdaV1EUbUtC/dTHxjGZJM69vMjGTKr1QwHQQXZJ3PfxYT7xmUM7Ppcoibi73bi73ZSKDc6dWSN9ahW3z8aBo51bKqNms5l6zcQPvrxEJV1l5FgXn/nCAURRRNd1yuUy05dXMBQT1arSTvRisRiyLLdVHR/66CjfSlaIX0licVlQ6yoGBnse7mdsfHcCIMlkkoGBAQBmp5OcfnmJ9GqBYI+HY4/u/nFuB03V+e7Xr3DltVUa5SY2j4XBw34ee2b4rpO8Npww9HiQ6KSN2FyBaq6Bw+Uk3Omlf08YV4eLtbU1MtEM9VSdyQ4nxZpCPF/FYWmpblabGgiwt9ODWxIoCdB9fzdrp9eoZWpEAjY63DbWclX8TgtWWaKpaWTLTYLOVnDWKDawuCw4Ot5B9xcAwWjNf4qtLu/t0D/gp/93bsYBmqrz9iuLoEP4erHT4bVSssosnltnZTlHIGgmm81iGAaNWgPJJtEd7EYQBE7cfwKzyYxqGJgkkZokggF6U0O6CwsewwBR2h3T4GcJmmpgwLaxDIBkElGa2ra/A7DbzPT1t7qyuq7z5ptvcvjw4V0leQBXLiW48nacaqlJz7CfI/f3tAU1rlxYp56t0zEZbn12t6BkFCm+vU7VKmMXbplhN4kIqkGz1uDtc+d46uhEW0To/uEQuSeaXHlrBUEVsfltnDzSyWTH7ubcbnSCphNVvv2VSxjzeZyyhCiL1BYKvDKdJvfxUT51sv+e5ssNw+C5KwlmNkr0mWSIlzE0HSnsYKXUQLO7eEC4+zBdVVVSqRT5fL79M7fbTV9f35bP6Vd/9VfbnddPHu7mbb+d188t4PO5OTIW5VDPzhRXURR4bCxMpaGy7jRjKDq6pPJAr4fB4M2z4wZ1UxDM7VimXKngcGw+X27da0IhB337Ilx5ZQmHz7qJIVcvN9DqKoYuIsoiHZOt9WuKzvrVFOfPrPH4M8NsbGy0RSKz2SyRSITPf/xJvn02Qy5dw71cwBAgH7AyMh6k02Oj55acppK7SvzcFTRFQ1N0BAHMP4ViTve84j/90z/lySef3DHRW1xc5H/+z//59ybRi0RduMIOSskK1lsokbpuUC/U2XPypiT4/kOd6Dq8+LXLJKZTLalonxVD19A1g42VNC6Hk+49YT75hf0EbzNX9/Jzc1x4bh5n2IF/wo1hQD5R4vWvX8XttXLfiVZXNZuuUUiW8fW4sbos2N1W4ldSbKyX6O3zYbfb+eTPHeTa3gwjIwF0XWdubg63291Wc3TK8NF9HXwXiLvNiPk6siDQcJgIdrt5dm8HoTsE9IqisF6osTyVxVNsYh3wto2BBVFA8lpxSCL11SJvX0qwp9OzySj8TogVaqxkq3S4rNTOJWjO55F8VvS6SvWtdZyWHiSLwKVYgT0d7h2TCcMwyOfz5HK59s8iZjO/8+Qkq0WFy7EC6XID3Wh1oQ71+hiLuui8Pl9nfXyQXLrKhWtpYg0Fh8OCRRZRNJ1ypYm5rDDR4eLgM8Ob5oQePtrNeqxE/LUVnLN17G4LGAblYoOyTWDyyWEOXJ8zMJlMKIqCJEn0+e2EJkIsL6cxLeZpmETM+8IMR3auTMqyyIHDN2edSoUGCJsPeF3XqVZbXYBavU5sNcnxB462qbt6v44e03FedeL1ehElAe/112PoBlq5idVloWyT2M3RWWloWFWdYPfdGaT+LEGWRUaPdHL2e9eIF5IIhsCBp4ewbTOnJckiT3x0jCP393LlwjqZVAUMsNhhYNTD6Oid/YkA8vkaX/6jt4hfTSGKoGkGV06t8pn/7ciW+cDZKxukF7LYfDbmz66T/+go/oADURRxu938/BdPUK0qeL026vU68Xiczs7OTeJcY+NhbL99jFeem6KYbOJwWxk/1MHhY927EvVIpVLt4GRpMcs3/uQslXQVq9fK/JtrJOYyfPa3j922E7UbnD2zxvnn5rF6rYSHfBSSVa6+mmBiXx87WVjdCk3TyGQy1Ot1oEXHdjgc9Pb1IvQLTJ7Y/u96entw6S6uffcaYqHByaEgy5kqq7kqlWqNTp+L/qCdiM1EYbFAZH8E/7CfeqHO8ivL2Pw27uv3YzWJxAt1CjUFkyjgM2vc1+fDJotkExUi+yOYvZuplHsOB4gvd1HONOjb66FvwHtX71mp3KCYqmD33/y8NVVFsOgUl0tMXV7kvhN9dHe3Ejt6QdwQiZ+N03+yH/l6ccB0PeizOi2Y6ioVq4y8S0VeVTMQGhqOiP19n9P8aUOk04XJYaKSr+N4R+Js6AZKVaFzYHfza2+99RaTk5O7phW+8eoSL37lEmpFQTTLLL4dY+7yBp//zWO43BYK2RqiJGxJ8gCcdhNZk4ii6Og1BfGGOnCmRlavUdFNPHTsGOHgzTPRaZH53H29hMUC0Z5+/Hbzjt2p7TA3N8fI+CRf+vJFhPk8wW434vXvk1XTsSwVuPziEvtHQ4zc5izeCbF8jblUmS6zjPLmOmqqAqKAuJCn90QXSxWVuWSZQ3eYJyyVSiSTSZrNlh6ALMuEQiGi0egdC6q30mtdVhOPjkeIGFl6e3s3MUB2QpfXxheO97GUrtBQNUJOM/nYAqJ4cwxElmWq1SpRtxsxYEd3lajH07jHejA0HTVRQXWbMIfsRD03P59Hnx0lHS+xfjWNzWvFZJOpFxtoDY3ufSESC3kcNzqHukGzWaehN7j89jX6hltKwjeUkNfW1nj66aex2+18wurkebtMarmAKAuMj4V4el/Hllj06P09LFzeYONapiWKtz/KwaPv3mj+J433LTWNx+PvSbv5gwKLRebIowO8+H9fJLWQwxWxoykGxXgJT4eLwyc2B1QHj3TS2+/h0rl1Lp9aJR3LsbayhsvrZt/JUQ4e72V8X2Tb4O0GKpUmV95YxeKx4A63bjgB8He5ScykufD6CkeOdbcoUy4zFruJcq7eCrxzNSSLiOMW6wKPw8LRg53k83mml+cZHh7eMovX47Pz+WO9vGabZ71kxuP10u2zMxpx4t1BPOBWLC8vgyNAbXkRl8vSTvJuheQy40hVWF/MkS43du3xB5CrNGmqGjYdihsV5JAN8frMR3OliJKq4B3xkyrWaWo3xQiazeamjRDA5/MxMDCwZSOcdNiY7HC3LBUMtk1EXZ0uTv78Hlw/nGdxJk1ivcRCIkFXNEqfw0LfZJgDTw4ReIdfk89h5hc/NcGr3W5mLibIrJdBBM94Bw5bno+c7GrTQG5UwaC1AT9+KMTLYpNqCZwWODzuZ/AujMUtVhldNSiVSu2ZBVEUsdtsIIiULDU6OsObvhOiJDLw6ACGapC9lsXsMmOytTpMjWKD4GiQfrvM5WoTTTdum7QbBpQrDca9VjrG3x97iL8veOaT4/gCdtIbJfwhJyce6rvt9V6fjeMP9qJpWnv24oZ8/W5w7vQasakk0bEgsllC1wziU0lOvbxIz69s7giGok5sXhu1fJ2uyTDOdxR/HA4zDoeZRCKBrus7mp/39vl49CO9d20+rOs6lUqlHaCcP71GOVWlc7KlEGp0uIhPJblweu1dJ3oL0yl0XW+La/m7XKxdSrI8n912KL9arZLNZtuKtpIkEQgE7smmwNvvpf+xfpZ/tIyyWmQwaGMiEiadzeL3eKmmqhSTNUKTIXof6kWURcJ7wxTXiuTmc3gHvRwfDFCsKdQUHYss4rZIJDeS5BJmnFEnnUc7yVQzm57XZFL5rX/+KLqus7KyctfrttlMyDaJQqqIYG69D7IsI2PC7rYzMNRN4B1ZcufRTmqZGvnFPK4OFxZ36zulNTV0TcdjkakErGDbXchSqDXxNDSG9kbeN9/En1b0D/jpmQgz9+YaZquM6fpcq64bpBdzOEMO9h3suOPjXLhwgb6+vrZ8/Z1Qqymc/uEc6NCxp3U/qE2N+FSK82/FeOjxQSw2E5q2/Xyp2yIj2E1UI3a0TB1Vr6GrKrlGidJklAcOjdIT2G6MQSDiNDG+CzbSrUgkEkQiEdZyNbJzWfx2UzvJAxAkEVvUQXGjzPRc5raJXrXZStgShTqiINDlszEYcpAuN2mqOpaaRjldxdTrQRAFmisFtI0KcreTRKG+6bFUVW1ZFNxSpHa5XPT09Lxn1g93O1bhtMjs7brJoDIrm61absQy/UEHQ31eZvM1hEwWJVZEQED1mMj0ejjQ66H7FkXiSNTF07+wh/Ovr7A6k0FTdAI9HvYe72F8b4gv/V8vkVnP0tRtCAJYLFassoWhsb4tfoef/vSn2/8/GHLyq4+NkCo3kEWBkNOy7RhRKOzkC797nIXZDKIkMDoZwm77u7PXuFfcVaL3jW98g2984xvtf//RH/0Rzz///Jbr8vk8zz//PPfdd9+7X+EHCPdfD7befnmR0kYZURTp3hfh4Y+Otqve2UqTmfkM5UIDk0WkZ2+E4w/184PvvUjpRxlc7irPfPbpdpXhdshla1QLdVyhrdUym9dKPlGm0dCw2US8Xhsnnh3l1a9fJX4liWSW2PNgL2MTNwMMwzBYWFjAYrEwOTm54/OuzM9iKSf5woMP3NXGUSgUcLlcpCogNjWE2ySxkiiiKzrNO8jD56tNMpUmLotM2G1FFAQMQ8AQjFZQp95yKOgGgiSiGwb1RoPlpWVuNAfMZjOhUOiupHIFQWCbwmIb7i43J76wn8m1IvGLG3zv60s88WA/0fEQocnQjsIQPoeZTz48QP5oF/mqgiBAyGXBUBqcO3eOBx98ENic6AGYlRK//ux+6pqBVRZZmJ+742vQNI1kMkm1WsXqrIPVQCnq+Ls201pz6yXsPhtje7YKpNj8NkY+NkJ2Lkt6Oo1SVTC7zHQe7cQ/7Mfxxipr35ohkavS4XNskYEHwICNYg13RWHoeBT3hx2928Jslnnwse0TJLWhUlguUIwV0RoaZqcZT5+H5cwyb771JseOHWvP+u4WsaUcskVCvi74IUoCNq+V2Hx2y7X7DnZi+g2ZbLbK+GRoi+dds9lkbW2NSCSyY0W42WzyxhtvYLFY6Ozs3OKjdTusra1tSg4LmSqSRW7P+gmigGwxUcjUdv2YO8FkEjHeuUUJBpLcoqre6NbdmKd1OBx0dna+Z3ODoYkQVo+V9Eya7FyWXDpHOVfEFJJwRV2EHgrhH/G3ZyrNDjODTw6y9NISuYUcgixgD9ixmUQMzaAUK9FMN7H12Bh6eqjlK1jd/rlFUaS7u5tYLHbHZLzZbJJKpdoJbv++AJeeq6DXBJwBO41qk+xynp59EYaHt6r72nw2hp4ZIvZmjPxinnKiDFz3aut1s+eZEQozSVK7KAw2VZ1Gtcmwz05k4sOC0nZ49uf38re1JutTaRBAkCW0hoIz5OCJz+2ls3v7sYd8Ps/U1BTBYLBtiL5bpJJlKpkarltGFmSzhCgJbMRa9LqRiRBvO8yUMlVc71AWb+TqRDpd2B7tI1looCXzlBsqrqFR9o2EeGIism1yUi6Xd9WZuhW6rpNMJtm/fz+pWAGjpiBtc54LZglRN6jeRiQonq/x3UvrrC3lEYqN1g/9Vgb7fQwEHa1Z2RtiZpqOgQg6CJKArhsozQYLCws0Gq2/lSSJUCjE+Pj4+zb+oOv6u9rDotEoly5dIhQKIUlSW4zFJIl8dG8H5UqVK0YUzbAiSQJ4ZIY7XDw9GW0nXNNXkpx+ZZHEtQy6buDwWujf72N4wobJ3CCTjTF6rIPLP1xFK4uYbSbyiRLOgJ399925yGmWRbruYHMDLTuhw8e673jdBxl3lehNTU3x1a9+FWgFwadPn+btt9/edM0NasrDDz/Mf/7P//m9W+kHAKIo8sAjAxw90cPGeglZFol2uhBFkYaq8cLry1x6fZXqagGhoWGIArLfRuf+CAvzF9h/cIgTJ07Q0bG1WlZXNMoNFUkQ8Nhag/R2hwmTRaZZVTfRRQGUmoozYMd8C2/5wccG6ez1kFwv4XRbmZgMt6Wuy+Uyy8vLDA4O3rHT+sILLzA7O8vZs2f5nd/5nV13Zjc2NhgdHSWxmsdwWdBLzW2vM3SDpqpjdphw7GB0ahgGpxezvH56lVKqgtVlYd/hTg70+nBaZcqCgLnfS+1SEq3SpFaqgE1CtTRZjac4OhBkZGhrt+69RjVdJTWbYeVammIerl1JkU5WGMrXie6PYA/uTGnx2s2bu6RmGa/Xy9raGt3d3ZsSPcMwyFQUrOUmQacZWRLp6uoiFott6tzouk5sbZ2lxTSySSQSdRCNRuno6GBoCHJJkTPfu9bqSofsGEApVUVXNI5/fGxb/yRoBZDRA1Ei+yNbFP4GT/ZyX6LCm2fWiGtlXE4LLqsJUWx18SoNlWyuirhURLTIXI0XSXztMuP7ogyPBTfJsX+I26OwUmD5x8ukVwrk6wqaIGACgi4LK4UlEnqC19XXGR8fv6sijdtnQ2toGLrR/lyVqkJkcHvK0Pie7TtUqVSKer2+YxfvBkwmE9PT06RSKXK5HM8+++yu1lmtVlGLKrlaDkM3kK0yHd0eFs7EUJsasllCbWoodYWuod3Lp5eKDcrlBuGwcxN1dPJQF1NvrJCcy2JxmcglilhcJrwBg/X1dfx+/65VN+8Vrk4Xrk4XHYc7qGxUWFtYo2e4B2+/d1svUqvHyshHR8gv50nPpCnHyuiajiAJOKNOBh4bIE8eR3hr8NueCb4OWZYRBKEVpN0y26MoCqlUCk3TMAwDs9lMNBpt/2345zoQDRMzb8bYmEkjWSV69kf5+D/YvyM11+azMfzMMNV0lWq62hYHcnW6WhYMX1a5sFZgA4GQ07xtBb7W1MgUanQ3DPY+3P1hQWkHhEIO/uHvHOfK+QTzV1MoTZVor5e9Bzvo6Nz5Pbt06RLf/e53GRgY4Nd//dfv6jmdLkuLeldSsDpa8YyhG2iKjvM6XW9gyM/E/d1cfHGJZkXBdf38LG6UUZoaD35slH0P9fLd185R7wpxMBJmOOxkNOLc0TR9Y2OD7u67C9Tn5uYYHm551bptJmS/ncZqEfkd57leVlBlkdA2xXhofR+/e3md2Ll1QrEKRq2VEBoOE9eKDeqTYRwWExULmHs9NJcLNOp1FKdISa6RzdZxdct0dfVvKVJrusFSusxCqkylqeKzmxmNuHbtc3w7vBdCaaOjo1y7do3x8fG2GIthGKxkqyRWktgFC5rTzMign4dHQ+TX5nBdF105/3aMH/z5BUrZMiaXCJJAeaXExkIGpT7M5/7hISRZZHhYx+v1cOn1VZqVJl17Ipx8aoj+gZ8usZT3G3eV6P3e7/0ev/d7vwe0kp4//uM/5vOf//z7srAPMiwWmd7+m0GEpht896VFLnzzKg7VIBpxItplDNWgkamy9sICwf3388wnjxB5RwWhUFO4sJrncrxAuaG22vpeG/u7PUxE3fTtDXPllSXsXmu72t6oNGmWG0w8O7Ll0BwcCmyhKy0tLQGwZ8+e274uta6iNTXC3jAL0gInT57cdZK3dG0JS8HC9N9OU89XsWaqpFayRCzSls1Ry9SoWET27gnj38FbaTlT5cWX5hEvpPBj0NThTKZK9Bf20eOSODW3QXdIRhixIeSauHqj2IeDFO0ywbrCsdHtPW8Mw2A5U2U5W8EwoNtnZzDouCf1z9xijrPfnObKfJacSWDNZUdERVrNM7OQZf9smkOfHMfVuXvu/p49e3jppZeIRqPtRM8wDF6+vM4L31vBKqXo3hPi5x4dwuNwEIvFSCQSlMtlBEGgWFQ49VyCzEoRSZYYP95N3y/0tx//mU9M4PLYuPDaMoVEGQTwdbg4+FBfu2N9OwiCsMU2xOK2cOhTY5gtElcvrpNIlNmwiBiSCJqBniijLRdAFCh1e6gs5lmcSXPx5SV694T45BcO4vuQXnVHlOIl5p6bY26lwLJoUNX09ui6r2yQOxtnz33jPPapx+6awnPwWDczb8VITKew+W3Ui00ks8Sh+3c346dpGsvLywSDwR0Tn0axQX45TzVdRVM0WIWIO8ITjz2xq+coxUuc/8F5rHUriWqiNbgvgMsh47GLJKZTiNeLBp2TYY7ucu0z00m+95cXqeUb9B+I8NlfPogsi2SzWQQ5x8AxB7GrderlJj2TER54ZnTXPn/vFRqlBrGLG0yfibGRyLB0ocTgvjADRzq3tQ6QzFLbLL5ZaqKrrUTP4mqpnHqannZB6VZ131demmL+Qg6bM8ajHx2ju8dDV1cX8/PzOByOTYp9NquHRLxMR7e7LaZxAxaLzM/90gESjw6STJRwOM30D/p3VdSxB+1bCmSeHg9HPzEG35rhynKORF1BtrYsJ0RRQFF1KnUFuabSa4gcf3SA4ccGtk2EP0QLdpuZ++7v5b7b3Ce1psZaropuQLfPxqVLl2g0GlSrVZLJ5LYdvXS5QTxfQzcg4DTT5bEhigJ+v52RI52cf24eXTMw22RKGyXcESd7r99Poijy8V/Yh8tn4/Iba+TiRUDAE3Vy8ME+BsetLE9f5AtPHNn1XOANC7DdolZrMQFuPH6X10bnZJA33phi2Gji7Q+BKKCXmxSSFWx7goz3b59YzKfKrC3lCcbKCLKIqc+DYRgtO4GVEusBG26ryuX1PJGIGZfdjt0awBHxsK5q3BdxcXJf9xY/zKaq89xUgnPX0ugbZWQVmlaRN7s9PDwe5vg2huh3g/ci0bNYLFgsFgqFAg5HS511ZqPEd16Yo/xqjA67m7pJZLmmUuzx0hGNMjs7S7Op8a2/mKaSrtExEmzFoDeUmfN1Zt9YY+pgB/sOdiLLIk98ZJSHHh+kUddwOE0/ERXmbLbK1IUEpUIds1VmZCJEb9/uC4s/adzzjJ6u355y97OE+fUCl16Yxy2IOIZuCoAIEtg6XZjdFhKX07z6+gqf++hNt/Fspck3zsdYylRw20x4DdBEgYV0hYVUmdRwg0efHSWXKJO4lmnNuxk6IDBwpJOTjwzcdl31ep35+Xn6+vp2NNo0dINirEh6NsPSVJJGXSW3otKrDDAaGG15+N3B5Dy3kmPuW3PYdTuyrcX57wvaef30HNXlFJEDvThHQhiagZqrU9A0HEc7ODa289zKWr5GfT5HxCxh6nAi5+pUY2XeuLDAMwc6uLpQoyBGcI95cNtMqJrOSqWJWFN4eCTI4A4CN6/NpfnR68s0lvIIuoHc6+H+k308NhbeMdnLV5vUFA231dT2zakX6lz83jXOzmepes34bSYKBYNOv4OGqpEuNnh7KonZInPkH+zFdBsa660QBIEDBw5w4cIFLBYLqqqylq/x4reuYJ8vYbc0WIiX+KZQ5f5hN7quk0ql2LdvHwB//WfnWJ9KExzw0axrXHx5ie5BX1u0R5JFHnp8kOMP9pJYLwEQ7XBtod/dLWw+G4c+O8ngsS7WLiVZm8+gNDVy6SorhQZyt5vA3hCmWzqYtVKDhbfi/I1q8A9+/eiOhsofonXwrp9bZ3m1wKygY5EkOpwWBKFVaEqXGjiOHsIlyChJBbYZsTF0g3y8SKlQxxtw4Io42ntVb7+PT/7aYd54fp70apHIsJ+jD/dz6L47V8Kz2SylUon+/v5tD1mlphA/E2fh/DqxtSLZpoImQCllY6w3wsWvTNF/rJvAWGDH4CK3mOPSNy6hVTUs/ZZ2p2ZhboHSQok+u4Wh/hBinwd/xMH+w1243LsL7t58eYncahFX1MnMGyu8MWBmaNSP67qf6Bd+5UlUVadcbuByWnYlFgOQy9VYmsvg9loZGtlKV9wtGqUGb/3NFGdOr5GXoCZo2NYLzCxm2DOT5uFf3LdjMUkQhPa8260wm82IotgWiYGWhcSrf3sNGgKaopPeKPDpL44giiLlcplQKNSWxC+XG/z5//cUG4s5gn1evvC7x7cke9DaW3ZiCdwtgmNB7reZiJ5aY3E6SSxTpWK0PF4l3aDfKtMz4Mff56XoMPHW2RijE6EtVkQfYndYSJX5/plVUos50MHf7yVdF/mt3/othoeHt9zrDVXjldkUl2IFSvUWE8UiiwyHnDw1GcFrN/ORT09iMsvMvBVrib5MhnnomVG6e25SRWVZou9oF1m/lZXVAg6LxKHxMBRXqZSdnDx5clfrv1f64dzc3KaiuCQKnNjv5/lunan1DaLZCj6Ph4YsII96efwjI3TuQAFMFutQaEBNRe5rvUZBEJBDdoiXUTI1IiMm/ENBkqqVsluhLAhYBNjT6eHpyciWJA/gzFKGMxcT+OfyyNkaBgKCBLVMnRdUjaDLwtBdzO/fiovn4zz/rWXe+H6entEA9z3Qv8mq4G4wMDDAhQsX2L9/P6qqcmEph3I5hUcHR48bS6pK7mqa5/wGH5twE4vFCPknMMoCPeOd7fnRG3B4rRTjJaYvbLDv4M1im9ksv+sYZrd46/QKr3x9mmKijCC0zuY33Vb2PtTLRz+9Z9fnw08S9/zOlEol8vn8Ju5+PB7nD//wD2k0Gnz2s5/l2LFj78kiP+g4f24dI13FMbJ9oCI5zbjMEvMX1sk83E/AacEwDF6a2WApU6VfllAuplDTVQRZJNrvpd7n5vX5NB2He/jC757g4tkYy/MZJFFkcCLEvoOdWLfhjOu6gaLrpBLr1Ot1JicndwyedFVn5Y1Vzr20yGq6QlEWUEUQTD4cePjB/zjL3qPdjD01uG2wAFDL1jjz5TO4ZBe+EV+b9rXPZ+W1S29STllQZhP4RQlcFvSgFf9EiGcfGaTnNgpqkiiAScSoKhi6gaFoGAJ0RsMk15b44sOTbChmLq7mKdYURAHGIi72d3sZjTi3fc3JYp3X34ohX0jhMVrOzLXLaU5LIqMR15b1ZCtNXr2W4lqsQLOi4PBZGQlZeXxPN/nFPHPXspRdMp1uG8b1BBzAIktEvXbieoVr0ymGlwsEx3cf6AUCAebn56lUKjidTppNjXq2SsRlQ/ZbMSUq2KzuNrVkcXGxTa3KrJewuizX/4NKskwhW9/yHGaz/J5XoCSzhH/Ij2/Qx3hVQWmo/Pl/P4MRdbblj2+FzWUhNOJn7eIGF8/GuX8b36YP0UI1VSW7kCMuGojGTeVTaN0rEbeVWL5K2iSSmkoRmgxt6rwqNYUffW2Ky2/FqNUUHC4LB0/0cPKT4+1Czth4mLHxMM2muqtD84ZYh8fjoa9v+26wUlWY+cEcb722TBxoWASsDhuiAIK7i6sNlbVzcdZXChx5doSOwx1b7t16oc7Sy0tUChUGD2+mhJYqJa6uXSUaiDKh+xjs99F9/O5oWiazhKHrNMsNTFYTXT2ddHb6WVhYaN9jsixum8jshFSyzJf/8AzpxRwWp5kHPzPJQzvMXEIrKZybTqHrBoOjQYJBO5qmIcsya+cTvHl6jaLXTMhjRWk0sNntpEt1Ls2m8b+4wInP799qPn8HdHZ2srCw0O7+FnI1GuUmncMRyvk6SlkjGu3GapXp7e1laWmpneitx0ukVvJY7GYyy3kSsdJdvT/3Ck+vhwPdbgbXe8gt5Cimq6hNDZvTjL/fy+xagR/+cI5qttWVeS1o57HP7eXIT/mMzU8a1abK98+skn5lBV9FAQMKq0X8Jx8k0jPQTqBu7fy8Mpvitbk0IYsJf11HMAwaLonL8QJNTedzR7qx2Ux8/LN7eOwjIzTqCl7fZh9MTTd4aXqDU0tZDN3A4bOxXipz/uULnJzs43P7b08JvxXz8/N8+9vfxmQyoes6e/fuvePfJJNJAoFWHLearbKWryGLAoXYGmFnnJGffxQIousw2uPm4ETktoJooiiA0LIUupUWjwEI0FAbyIKJX3rsQLsTqukGQZel3Ql9J+qKxoW1AvbVElK2htzjRhAF9IaGbb1MxW3mcm9hS6JnGAbrhTqpUgOrSaIvYMcii+04A66ron75Evl0Ho/fIHYlxdKVFL/0O8fvmXUzMDDA4uIiAIVyA6NawxZ0IUgiktuCKVPDYnYzNjZOZ2cnr7w4g67qW5K8G5AsEqXCu5+/vhdcm03zwl9dRmlqRMdDSLLQapakKpz93hwuj41Hnxr+O1nb7XDPid5v/MZvsLi4yKlTp4CWGeOJEydYW1tDFEX+y3/5L3z/+9/n0Ucffa/W+oGErhusx4pYJBHhNpm8w29jY6NCOl+jnEnwvZffYNHUw0AoSPNUHDXdMig3FI36hQ2sskg2aOVKvMDY4W4eeHSQBx69/SZ3baPEC1fXWYsnmewL84kjO8+oGYbB6qlVXv32DIuahuS34rWZMMsimm5QqqlMVxrkX1nAMHQmPza6bVdq5dwKelHHd58PVTeYj5fI1hREtYFbqvPAL56gsKbS8FvwPtjLQLeX4bBzsw/bNhgMOvBMhMiejuFYytOURaTJAGJ1g47eDgZ6OhgEjvT6qDZVRFHAZZFvSzdYL9SprpcIqjqm69U1Y6VAIV7i8mIMm+5rqcPJMnUN/vb8OqtnE3g2qjhVg7pF4k8qq5zeF+BxNULa0PHY7a2qjm5s8rQTRXDazSRTFTZm03eV6AEcOnSIL3/5y/j9fsJhC65+F5npCvJ6Cb3TRf8tcst9fX3toDTc7SE2laKSr6PUVQSThP82c4LvB2p1BVXTia+XSa0V8PbsPPNhtpoQTCKXTq9x/IHenwjt4qcR9UKdYrFO0QD3dgegAHazTFrTycayvPC9F7B4LDzyyCMAnH1pgVMvLWCE7TgjDnK5Kq89N4c34mDfOxLs3SR56/HWbN3Y+NCOQiqGYbD06jJnfrzMshk8LivBd9z3um4hU2kwla8ifH+Ok14r/neY0eYWcqzPrdN39GYymas0aag664kkfp+fkYkRnBYnqakUkX2Ru5LUP/JQB+mNHErZYO/JHgaHWkneDY++e8HViwlSi1miY0EyKwVO/XCGiX3u9v5yQ6BAlmUuXVjn+a9cppSoYGDgCNo59swAl6ef59jhYyTPKBRlgbDPjoCBSss/M+S2EqspLEyn2btRxnUPnbNwOMzc3Bzd3d0EIxbCQ37SizkESWT/YwObiokej4d8Po/X66Wzy010OEBiLkN4OEBXz/biHe8HBFHA3eXG3bV5X1mPF3n9f7yN2lCJXlf1TS/leOlvphgY9rfN3D/EnbGWq5Gaz+GrKFiun5We5QKZpTyLySLNjXkuX77M/fffz8TEBOlyg0uxAiGLCfPlFJWVAhggB+303tfBXKrMQqrCxHX/uhvKvO/EzEaJU4tZAiYZ02KO5PQaDpeJY/ePspBtcnohwxMTuxOA6enpoVgs4nQ6d+V5aRgGiUSCffv28eO5NK+9vkxttQiSgBA28YVf/8fcNxxtX7sbamOX14YYdKC7KqiJMnLEAbqBmqhQsQkoZpUHD7eE8YJOC0HnnVkIuWqTfL6Oq9hA8lnbyaNokdDMEo58k5lYmvWohNlkQpZlEEReX8pz7u0EjWwd0SLRMxnmyX1B/vz/9wfs37+fBx58hDd/OA8GePudhAI+NE0nMZPm/JtrPPbMyB3Xth3cbjfxeJxGo0F/h4vLJh1/tWWLoRUb1LwWxjudiKKAx+NBkFQEWaRZVTBvs4+rDRWP/+9m1OP8G6tU8/W2gTq09iNPxIlSU7nw6jLHH+y7rZr+3wXuOdH78Y9/zG/+5m+2//3nf/7nxONxXn/9dfbs2cMTTzzBv/t3/+7vfaJ3A3e85Vs9Xv7Hn/4p1eQCRZMf554OrCWFcrqGqct53YrAhKEZKIt5vF3drGSr1JoatjvQJ5OlOl9+dYbSW3GCgoXpfBq7w8GnD26vPlRJVrj46jKLmobLb2tTEqHVIfA6TDisEolslXOnVukYDxHeu5lqqdQUFt9cJDwQRhAELsXyTK8XMcsSDUVj6MTTDHQF0PwK9VydsQE/rohrVxtkxG1l34DEsneQXKqG222hJ2zQ79A3BWBmWcQs747yJ0sCgiyia9fNgIWWaqdgErFZzOi6Tq1WQ1VVzq8VmToTJzSdpWmRUGwyRrKBO57lrF6ntHgFz9B+nPKNyiZb3MstskhVFKjktnbU7gSTycTAwACzs7P09vbykcf6mBvXqVaaTE6EOXTLTIAoilitVqrVKo8+O0ohU2F9NotoEjn89DAHjvxk5onmrqU59/oqy1c20FWdWrlJea2I7w7G9XafjXyiRLWq4NzFQfczCQPau8wOt4+AQL6Q5+W3L2EpWRjcM8jq6ipaU+Pcq/OoDpmO65Rme8TFWjnD7Ftx9j7Yt+t5DMMw+OH3zvPW82vYbFb4JTeTe7faDEBrj7l2PsGqBD63bds9TBRbirMbhsFcqszApQ18AzeZAbqms35xHYvb0gpYaFHKLqwWaGg6ZlcPHz06gtUsoWs6ufkchdUCwbHdFVZUVcVkavK//96TbarX8vIyPT09d6UE+k6YrDKiKFDO1lDrKiGfpz2noqoq9XodVVUp5Gt8688uUk3X8fW5QID8WpEffuUCmi9DZu17CCth6OpDEgV0Td/0+ZvtZkr5BsptVP9uB6fTSb1eR9M0qtUiv/KPH2T6cotyvu/g5s/V5/OxtLSE1+vF4TDz+d86RiJeJNrp/onTrnVdp1BoIAi0O4kLs2mq2RrRiVC7ExLo85GcSTM/m8Z/Ynczmx/i+nl2y+xm+zunwze++U1qsRkEQWBwcBBBEJhN18mWYFiTqKwUMHU4QRJRVgqYYiWI2FnNVtuJ3k64HCsgAPrVDWKnF/BEA8h5Ae38BoGDES7HCxwfDNyxUAxQrxs0m/DMM8/sSuV8YWGBwcFB1gt1Xju1gnQ+ScgAQTMoZEROBTNM9gRx3KGgfCv6gw5G+n1cLdQJrJYgXgZRoOqSmXcpfOzYGNG7sJcCkAQBURbRBTDUd4xQaQa6LCIKAs1Gg/r1eGYuVeWHP1rHMZ3FomloOpyfiRFb9ZDa2CDx3HP84PuvEdFP4Ao7KddaM/WSKCHKEom1/F2t8Z0YHR3lK1/5Ck/sP8qr+31k4gJyqYkSthE+0smxoZv79SOP7efSj18gu1ogMhLYxFQoZ2tIZpnxA3e2/3ivoes6q9OpHe1a3GEHuXiJ9XjxXdv6vNe450QvnU5vUvv75je/yYMPPsiJEy0H2C9+8Yv8n//n//nuV/gBhygKhKIurmlxDFXfsatXzdawRh385u/+OpfOvMa3Xr+E3WlvWQIYRnvYFFqyuoZmIAIaoN+64W4DTdM4c2mGcqJKT00GQUNYLbOcrrTULbdZU24+x2qihOCxbEryboVJEvE4LWwkqyyfWyc4Htw03L62sIbNZMPisqCoOrFcDbfVhMtmoqFobBTrZMs1chtrJK8meWntJe576j4ee+yxO76vsViM4+O9PO50UW6oFLNp4msr7N+/g8PwLtAXcBAc9pOOl/GtFEGAokPGPxJg/0DHJmGYU8lVAkoKj8uNqbtVKTfCOoFkBneojyMde1hJVlB1g+v24Vvib0XTkXSw3qNhb19fH9euXWNxcZFDo0Mc27Pz43R3dzM7O8vo6Cj/8LdPkEqWkU3SPXPrb8Vuqpdvv7nG8391kWq+jiNgQ7LKNNaKNJYLrNridB+KYv6AVbl+mmBxW3C6zDhzCuW6isW5NQmpNBVGvF4GP/4UcV+czu5Oenp6UOsqFss81BubrhdEAbWpt2lEd0KlUmFjY4Plq2WaeYV6psn5U6s7Jnq5hRzxZAnDLt+xUOV3mEnXVZaupug91t2eOdMaGql4imjPzUr6XLKMbhiEXRY2CgZr6SJGJc3GxgYDrgHUmnq7p9qEG0rE0CqY3FDSvBvxhu1w+L5uVq5lWL6cJDjg48lP79kibKVUFRbOLKAu14j0uTGpJkSHGeeYi7VLccLDo3z6k8e5+I0Y5+IFdJ3rCf8tj9FQsVjkLQJJd4OOjg5WV1cRRRGfz35bCnUoFGqb1Tsc5nc1e3ivuHp5g9MvzbOxmEcQBKIjAR54YveUvg9xZ3T5bPj6vZRWigjLLeuDklXG3evmHz37BVKxJX7wgx9w8OBBvF4v9dU8Z3MxBFVrfUdvqDKLIoZqIIoCmn77OEbXDVKlBrVCDuYShPs7kQI29IaGmqnh0A2SDY1iTbltotdsqnz3b6aYPRPD0tjP0ozAxLh+29mpRqOBoig4nU5mlrPU4mVCOph7W366rqUi2bUC6/kaDr3M+fPnGRoa2uLV9k6YJJHHhj2kUxuUIlGaqSo6kNcKfPS+/Tw9uZWqficEnBY6A3ZWgna8iwU0s4Rok9EKDQwRKj4zjw9F6eu7WZifb67jKW4QDPkxRZ0YTQ1HvETDcNM1MMLjDxxneGiSP/6/fkyt2OR6UNNSRVU1PIF31w1v7S0+SqkYHzkQQHikm2S5TsBhYaLDvamT6XY5OfhQlIsvZVifSuEI2RFlkVqujqZo7H9ikPHJu/clvVvous7cbJrURgWny8LoxE9+r3uvcM+JntfrJZFIAC2VoldffZV/82/+zc0HlmWq1R0Mev6e4eChTq69ukwtUca+jZyzXlUo1RTG90fp8DnoePppIsP7+OaVDKrZhOiyoKWqSEE7hqKj5RtY94RIKRpdXhu22xzimUyGVCrFUH8vp2ZnUB0yckWl5jThs8rI23C8DcNgZSpJQRBw3yH4dlpNrNskYst5JrO1tiS3rutUa1XsdjuGbiAKAqIg0LheYVK01uZ+/uzbvPGjlyADhe4Ctg4bXq8Xv9+P3+/H6dw6T1cqldA0rT0PIqp1FuZmefjhh2//QdwBTovMR4/18pwskpjPgWEQ6PPy1JHuLeqfAgKGIGxSpEMU6OvrI+X3EYi4KSYqpGoKNrPUipXf+TpqTfpMIuHRm9Wdmakk2UwVh8vM5N4o8m0OH1mWGR0d5fz584yPj9/x9fl8PnK5HD6f77YS2bvBeqHGlViB+XQFXTfoCziY7HDTv43ITSpV4aWvXUapq3TdIrtvANX1MrWVAkmHie4D2ycE1VyN6EgA+z0mxD8LcIQd+Pu8dGSqTBkapZrSkqIWQNchU2lgN8kEBJH++/o5eeRk25pDtsr0jAVZ+eE8Jb8Nh9VEqdpArmr07g3tarYrFoshSRJdXV3I9lWgNZMZjO5MF0xey5I1jF1V302SiGEWSWdrVDPVdqKXy+VaHljXb0NBEDBLEk2t0VIpFgXOnz+LXsqAANHeKNlcloAS2GQHsNNr6uy8qc6bTqcxm824XO9ePMRikfnFf3SEYrGO1bpZKKCSrJBbyJG6mmL98gasFmlUFVRJRLTJCD4bsgInThxnYGyA6j6VmYUMmXIdv92EIFy3zKmryKUmvXujODvuTXgBWkGYzWZrn+e3g8PhIJVKEQwG33frmu1w5VKC7/yPs9SKdZxhJ4YOC2+ukVzK8fCnxrH7bWSX8/h7vQBklnM4ww6GRn96g7S/CzgtMk8f6+EHukFmMQcIuHvcPHV/Hz6HGd/oKMPDw+3vQMBpxiKLNFwtlW1lpdUREm0mxIgDVTOIeG7fuarVqsRXlzE7vQS7IigbFURNRy83ES0SigiyKGC6g3LrmTdWufDDeWx+GxarlbM/mCPS6bqtuui1a9eYmJgAQBZFBFnA0Fp2M2g6umAgyhJ/8Ae/TyO9htls5jOf+Qzd3d23Vf/UdZ3EyiK/85EjxIt1UsU6V65c4aHDR+iP3psFgCQK3NfvZy1VpqTquDI19EIDzSKR63MRHgkw+Q4WjXijknc92b4R15itVn7jN38Tj81Es9nE3SsQP1unrtewCFUqqTLuiJN9hzfPuBaLdV57aYGFixtIJpHxI53c//AAltvs9YFAgKmpKU6cOEEwePv78elnj2AynyMTE1mdSqLWVYL9Hvbf38vxk313Zcmk6y1Lh7lkiUpTw2c3MxJx3taGotFQ+fpfXuDaWzG0moIgS0RGAgS6XCy8vQ5dW8+IYrKCO+R417HX+4F7TvROnjzJH/zBHzA+Ps73v/996vU6n/rUp9q/n52d3dTx+/uMkS43E48NcPm7s2iLeRxRJ6JNxtB0lGydbLKCe2+IBx64udHs6YtwPtFgNVel+1CE2oUk6loJJBFzvweGvDTqCvu7vdsO5Oq6ztzcHG63G6/XSzIV4+j+Di5arWiVJp5OF4+Mhrb9W0MzaDY0NLFFZ7wdBAGQBJSmhn4LTWBpaYmRvSPMXpullqvh7nIz2eHm3EqOeL6GJApMRN2M7z2BpWlw5dIVVJ+KLMtcvny5LWEsyzJ+vx+fz4ff78fj8ZDJZDh06BDQUg49c+YMDz/88HsyvzUQdPArjw0TO1TDMFoc+u26DQMBO2d9ZuyxEvp6GbPbgpar0XSYsEadDO2NoFzLkIznycoiLhPcaIsYRkti2l7V6Bvw4+33AvC9b0xx9rl5lJqCaJKYOtbNZ375wI4zUbIsYzKZ8HhuSqHfDqFQiNnZWXy+dyeycnW9yPcurlNYLeAoNUE3OOM0c7HbxRN7otz3Dinpy+filJMVohObq2xOnw1r2EFtKU9lvUxjWMHi2Bx8N+sKhqKz73j3h/N5t4EgCnQc6qAUL9FMlFhRdWL5GgIGBuA2yQwZAj3jQQLXCws3qI4Ax58cJh0rsTCTpqzpoKv0jng49ODtLTXq9Xo7IcpkMmQyGX7pHz3AuX1ryJK4Iy3Y0A2aDRVdELDs8lCWBBHVUDftM+VGmWBvkMJqAau3FSju6/agrujUFI3xqIt9Rx5naXGRpYUl/CE/gc4AmUymbQVwAw6HA6/XiyiKFAoFzGZzu8tWLBZRFGVbj9N3A/cttCxDN4ifjRM/E6dZaWLz2xg+2sVirEglW8Plt6HVNcoXNzDZZWy5GhfOXmAhs0C9tIZZ6CNml5DMAkJFQy41GfTb2f9o/6aO3vy1NC99a5pKvsHwgShPfWLstnOXgiC0A/bddO87OzuJx+Pv2fmu6zrFYgOr1bStwNit1516YYFasU7HLXuNK2AjPpVkfirFQ5+a4NVvXCUxnQLAGbLz6GcmP5zPuweMR910PDPGSraKYRj0+Oz4bimI3rpfd3lsDIecXI4X6L2vA1Os1GIlhR3EzQIdTgsj4Z2LEfPz8+RyOU5O9PDSXJ7evSGMhoq6Xkawylj2holpBiNBB0Hn7WnCuVQFwzDwRlvPF88myaQqO16fyWTwer1tqnZ/0IF/NEhuo4pnuYAOFHwW+kaD/B9f/H/y3W9/i0uXLhGNRllcXNzU0HA4HO14xmKxMD09zdjYGJIk0uOzk1qc5qPHJwkE3p3P20SHG+VwNz92W0mtl6ChIros9He4eXwiTMi1mZEwEHRwZtBL9XIa63IB3TDIeUwcHvBRzib52g9+wOLiIqqi89hnfpkff/cCuqrRsz/KA88M09N7M3FsNFT++k/OsnR+HavLjK7Dy7MZkvESv/Arh2+7bq/X2y4U3Q6yLDM0EuD+B8LAXlRVvyvF4xtQNJ3npzY4O5dGSVSQVR3FInK6y8OD4yFODG4voHjm9RWmX13B3eXC4bXSrCrELifpnAhh91pJLuQI9Ho3ibEoNYUDnxz/wM3nwbtI9P7jf/yPPP3003z2s58F4J/9s3/WlqTVNI2vfvWrfOQjH3lvVvkBh0kS+cRTI4iywOzrqxRXC0iajg4IHguhB7t59mPj9Nwi8yxLIo+Mhvj6+RgrhkHkoW7sVRVdEilaRDLVJgd6PExsM2BfKBTawdfa2hqRSIT9+/ayRzfY1xOgpmiEXdYtN/sNCJKA2Swh6aBqBrdj/RgGoOmY7FKbtlmpVLBYLJitZkJ7Qsw9N4fW1BgIOVpm5nUVq0ki6m6pi072TjJ5cpIFbYFPfOITeL1earUauVyObDZLNpsll8uxsLDA6uoqoVCIS5cu4Xa7WVlZ4YEHHiCTyeD3++/aH2w7WE3SjtLDtabGtWSJc3MpNvI1lhQFd7KEy2HGGnKgDroQkjO8cfoCFy6eZyx4glymRtwMRVVgo1hHa6g4qhoTHS72PzWE1WPl2myas8/PY7KbCA36qBbrzLyxyrnxIMcf2D7YNplM5HI5jh492j5Ybg3et0M0GiWRSBCNbt89uxPy1SbPTSWoX00TWS1hqDqGKODAoFpq8JIgEPVY6fHdDJxiS3lEs4z4jqKBKAoEhnys52soGxXK2QoWh7f9+2qxTm65QO/BDvYf/sn6kv00wtPrYejpIeRXlwmtFSkIOpoAZt3AZzMTGfLT/2g/lm3ue1fIwWd++xiLU0kK2Rq6UeH7r36dty45OHny5LZJdiKRQNM0fD4fiUSC7u7udpfs/gf7b7tWQRQwWyQE3bgjZesGNENHvj5/ArSLG0WlSHY+i6ZoSCaJgNPM4+NhNN3AdP3awaFBfJIPZ9hJaCi0rX9apVIhkUjQaDRYX1+np6eHtbVWdb5Wq+2oHPpewNAN1k6vsfrGKna/fZOR96GH+jj36hKlZAUMA2u3m85eK+tnlpl5cYaSv8Tj//AxpIyL2SvrFIoN3C6Znr1R9j/av2kesVRq8J2/uEh2JY/FZeGt78zicFvuqARXLpcZHR0lFovdsaBkNpvRNG2Lufq9YD1e5DtfvkR2tYBkkTn6xCCPPLn9WjOZGsnlPK7I5jNREAUcQQexmTSf+vx+hkYDzM9mEAQY+dBe4V3BYzOxr+vOQjuiKPDUZISmpjOXKkPEjigKrU6eTeaj+zraZti3ol6vc/78eUKhEM1mE7ueZbwryGK+TvR4F/amhiILxK4zA04M7GzBcgPegB0BgWKyxUYRJAHfbaiHa2trHDhwYNNr/tiJXl4wSyRX8oiSyNBwgGcOduKwWfn5n/959u3bt6mjeQOVSoVcLsfMzExbmLBareJ2u8lkMkSjUQKB92Z+a3+3l5Gwi5VslYaq4bKa6PHZkLcprA2FnDz08ACn7CYyqSqiRWJo1EeINIHAHuLxOKqq8umf+zTHj4/jDhTZs+fAtr50Vy9vsHolSXjI3xZLKWWqzL8VZ+WRgU0e07fCMAwsFgtWq5VCodBmbO2EgYEBLl++3LaOuhe8vZzj9OV1vDM5TLlGi3klQi1V5yVFI+i0MBLZGmPPXd5AMIk4rhcXzXYTzrCDUqbKyU+O8eZz82zMpNqvy+q2cvgjw7dVVv67xD0nesPDw8zMzDA1NYXH46G/v7/9u2q1yu///u9vunn+vsNmlvjs06OsH+9hejpFId/AbJboH/Ix3OXZ1gulP+jgM4e7eW0uzVKmQkMCAR2vJGEvLvPU2BOb/s4wDBYXF9udnkwmw549e9qbjSQKdLqs5HM1xKYK7JDoCQLdE0E8lxNkrlMPd0K5rmCr63Tu9WC7rnQUi8Xa3PTAaID8cp701TSuLhcht6WdYCpVheJqEU+vh+Fnh3nA88DN98tmw2azbRqSjsViQKtQkMlk+NGPfoTH4+H8+fO8/fbbwOaK2Q36p9frvWMCtBukyw2+c3qFxbfiSOtlRuoqhapOUYdUXUUVDQ50ehgVS3z9K9/l2MPH+PTDjzB/ao1rMxsI+TpRr4LXYaF/j5+h+3vxD7cqd4VcFaWiELq+CdrdVvJrRQr5nYVaZFkmnU4TDAY5cOBAey5AluUd6WVut5uNjQ0ikcg9UauuJctk1opE4iUEpwn5uq2GXlOxxyokPRZm1oubEr3rvYBtH88TcaLtj5J4bZXiaolmRUWQRLSmimyVGTzaySe/cPBDD71dwj/kxxlxkl/Ok1/KozU0zC4zvkEfnh7PbT0vzQ4zY9d98fL5PC+eNXP69Gn8fj+Tk5Pt6xRFYXV1Fa/X2za7vVUAKbZW4NrVFI2Git1uZnxvmNA21frQkB//+XXidQXnbTo10Co4CU2dgM+GzW+j2Wy21mw24+334hvwkVvI4RvyIcoioihsYitUUhUwIHowuqNJtsPhwOFwMD8/z/33348gCKiqyuXLlwkGg6yurravFUXxuujIe5MkpK6mWDu1hiPswHqdwlaqKcQKdTJNFefBDpzlJn6HiZEBP3aHmaXZJYLZIEJF4PiTx5ElmYGFCPVSHV/QhzPq3DKbl0lXKKbKBPq9WBxm1qfTJFYLt12byWSi0Wi0vfVKpRJra2uMj41T3ijTKDbQFR1BFJCtMs4OJ93d3aytrdHbe+8CJ42Gyjf/4gKxqSSeDheNisKP/mYKt8e6o3+jAJvp9G3cHDQNR1yEtwnePsT7C6/dzOeOdLOQqrCaraLpLbrmn/x//j0j2jN0P/HEpqRhZWWFWCyG0+lkaWmJw4cP4/P5OPD/Z++/gyS7z/te+HNS5xymJ+eZnZ3NAXGRSBAgAIIAkyhSFGlJtl9JtizfUtlVKtX7vlW+rJLLvnUt2XVv+aUs2VcWLZqySBEkBYAESOS0WGDz7E7YydM90zl3nz7p/aN3Bjs7cRcriQT3WwUUsJ1On+3zO8/z/L6h0uDVyRRTyTIZERQR+oNu7ukPbyoduB53nugmuVRg8v0EgiBw+JFBjt3VtelzZ2ZmNnXXHYh66Hx4kGRRRRQFWn2OZuTTVWwlpVhdY1aH0iMjI1iWxaVLl6hWq+TzeU6dOgV8sMYEg0H8fv9NMVqcNok929DnVyGKAg8MR9nb5iNVVnHIIl0hF5ah893vfpfPfvazvPbaa2tMKlkRt8whzaWrWKa5zhHTG3YRXy6TSVW2bPQKhQJ9fX1r2Xo79QeCIBAOh9dqoBuFqhucWcjjmC8hZ+rIXc1IB6th4EiUqfptnF8qbNrobX1QcPTOLvYfbmfsbIJiQcVulxgaja3b9fxZw4eqjhVF2fQvy+v1rqNx/qJAEATaAy7a7979ZLg75KLzeBeJYp1iTUMSmzsm//H/+DZ/9icTPP300/T391OpVJidncXlclEqlRgYGMDh+IAWVKk0ePOVGcbeXqSaryNKAq0DQY490Mf+gxt3d8KDYTpbvaQzFapOBdcmBaJuWBTKKr2yRM/hNkRZJJlM0tLyAW1Gdsj0fbwP2SGTncpSTpThaoik4lAI7wnTfV/3WnGzFVZ1eauFQzKZ5POf/zydnZ1XqT3FdTuA8/PznD9/fu2m7/f71zV/N7p4FusaP3hrjtmfzBAuaSghB2KHj5Ao0KjraLkaZl4jd3KJuUMuPveFL7CSSBAdjRIeCtM+tojwQppHP3kUu9+Or8O3ruD0+h3ITplSpoo37EKtNBAsYdtQZ1mWMQwDQRAIBAK8++67/PVf/zVf+MIXOH78+Jav6+rqYmFhYcsirFDTuJIqk680sCkSPSFX0wZaFFgu1FGqGlbdQIp9ULyLThnDtHDWdBZy6zNsOvqCTL41j2lYG3b1AERZoOXuDu54bIjsSpmGquMPudhzMMbgUOSG+Pa3ATaPjZZ9LbTs21yQbuomhcUixVwNrWGg2CS8AQeBLv/ab9LhcDA6OsrAwMC6Ji+dTlOpVFAUhVqttq4IqtYaPPudi0yeilMvqM1BgmXyVtDJ6IkeHr2OIhgaCNEe9ZDIVahrBo7ttMaVBv6GQc+eKN42L7Nzs2vDQ9neXGMAslNZFLeCM+hsmsnUdKqZKopLoeeBnjXa6la4VpdnWRazs7McPHhwwzphGAaFQoFsNrvuz2VZJhgMrlt7d4KhGSyfWUZ2ymvr4JVkmQvxIuW6hv3qeVExyWsGSqFOp6Hij/ixCTY66MBqWAhuAVeLC2fUueU0PBB04g66yC4UcfjtmLq5rY7yepRKJf78z/6cDncHwoRAcaGIrn5gbiMIAs6wk8hIBN2to6rqTRvXrCyXSM/lCfcEcHrteMMQv5hkfia7aaMXDjtp6Q2wcG4Z7zU7NKZpUUlV2ftALy7n7YHRPyTsssTeNt86d80ftYT59re/zeTkJF/5yldwuVycOXMGQRCoVCq0tLTw8MMPrz0/5LbxmcMdpMtq03hKEYl5HZvKUDaDzSbzuV89QvbxYQRR3DL/rdFoUKvV8Pk211TZZWnbrN+tYJomExMTa7VxJpOhXq+v+47wwRqTyWSYnp5eq2VW15itPAw+DKLeDwbxpmly6swZHnjgAZLJJL/+67++K8aUL+hEEAQadQ3b1V3aSr6O4lIIbHO+UqkU9913H9DcrVt1Od0O7e3tnDt3bstGT9UNZtIVEvk6ltUcLAxEPTgUiUJVI1es4cqrSEH7VVd7EGwSgl3CnVdZytfQDHOD7nPoYCtzZ5cpZ2t4Qk4aVY1yssz+j/dfjQZhx7iznyV86G2QsbExpqenyeVym07avva1r33Yj/hIoaGbJAo1VN1EALwOhZjPTkfAScc1gbO9vb2cPXuWF198kYcffphGo4FlWXg8ng0TqGqtwV//+Wmm313C7rXhCjowNJPZMwnik1mqXzrAndcJkd0xNwfv7Sb/t+PMZKtU3Tb8TgVFFjFNi2Jdp1xRiVUNjpzoITQQWmu4VkOEV2Fz2xh4ZIC2I20Ul4oYqoGoiHhaPXhinh3NHkzTZGlpaW1KdvnyZTwezxqFaHXyFQgE1n13wzDI5/Nr1M9sNsvExATlcnnd665t/rZaPM8t5Jh5fZ5IWcPe41tbFABsDhlbmxe1VEOZSpIL9fD4lx7gfrvBe++9x9GjR/H1+HD2OYkd3DzjZ2g4woEHejn30gyl5QqCKNB3R/uWk2toFlWmaa6dk0QiQblcJpfLNb+/aVHXDKAZ5bBK2XA4HGiatim1any5xI/HlsnM5xEqGoYs4mj1cKgvzCOjMWRRwETYGPAKWKbJcnKFFqOl2UzMF8jP5vGkqjg1i9SFFVoOxNa9plHXKKeqHHl0kEee2LPld72ND49GpUH8cpqL7ywwM5mhUtUwraZRktup0DccZt9dXbSPRHC4HXzmM5+hUCiwsrJCJBJhbm4OURSxLIv29vZ1ZiaGbvLMX55n/PVZPDEvoY6rIb2GRTFV5r2/ncA0TJ764gc0G3fMzdDhVlZevMJcoU7Q59gwUFo1kpGrGoMRD+0HYxSKTVrPtdeoI+Bg8PHBponJWIpqugpWMzy343gHoaHQmoHLVrhelzc7O0tPT8+mwyBJktbWjWuhaRq5XI5UKrXuz+12O6FQaFNmQXGhSGWlgr+32ZzNZ6qcns+jyCKdQdc6x9NSTePUdBKt3cOh/lYCgQBz788xd3aOoXuHdtTQBQJOHvvSAX7yvTHqpQYHH+7jxMe2zwPM5XJrw7v0fBrpisRyfpmslKfskMiLEqpuIgkCHptIoKxSfW0em8dGdjbLsSeO3XBYO4CiSIiSiN5ormGWaWEaFsrVYYGu6+vOpyiK3PuJQb6/UFhz47NMi3Kqgr/dy907fM/b+IdBOBxmYmKCRqNBMplkaWkJy7JwuVw8+OCDW5omrebKNWNPbvz3FdqBsjs5OcmePbf+nrSqy1ttZCcnJ9fc6K/FTmvM0tIS5XJ57ZpfXWOCweC2BjDXo1jXuLBYYD5XxWOXGW3z0R10cPLkSUZHRwkEArS2tnL+/HmGhoZ2fO/RAzFO7YmwdCGJK+zENCzqhTrD93bR0xfY8nWNRmNtQLaarVev13ccmq2yB66nlGcrDf72XJzp+TxWujl8FoIOunoCfOpAW9MgUBKxxGaM1joYFg0s8isriMJGqvgd93QRn80z8e4ixXgJUZHoPtTKw0/+fNYwN93oXblyhV/91V/l5MmTW1ApmoXq7UaviUJNY3y5xNnFPCvFOrphgQBORaIv4mZfu4/hmBdFEtF1nXPnznHffffhdrspFov4fL61zJrr8d5bi8ycWiLS16TrrMIbdpGazvHaDy4zsq9lnTGAIAh03dvF/Ra4X55hMVMlY1OxJAEMC1fDZMht4+ADXex5ZADFpawVRlvBFXHhuolw7qmpqbXmcXFxkUqlwrFjx3Z8nSRJhMPhDZz3RqNBLpdbtwO4uLhIvd6kSa4awKwumm5fgPfOZXBm69hi7nVN3irUukpNrxPtipJaKHJ+Lsfn7uxmZGSEU6dO0dfXt+3uoSiKPPmF/fQORchlKrg9dg4ebd/WpSqdTq9NG/ft28fv/M7v8F//63/lzNgEwaFjnL60QilXx7LA5bVxaLSF4damVXFvby+zs7MMDAysvd9Ksc6zZ5eonk8SXa6CZmAhoCcqnCypeB0yXWEXJ0MOLJ8NPVlBvuqyamRr1NGZLmdpHTvDlQosnVshXWlgSAJBp8LKVJZ4qoZzJIxsl6mXmvlePYfbfm4XyJ8XlJNlXvnrMcbOLVMRQA47m7bUgoBpWeSrGu+eX2Hs3AqjB1t58AujuCIu/vRP/5T29naOHDmCoihrA5XrMTmRZurUEv5O/5puAUCUBAKtXkRR5OIb8xy9p5vOq+HZgiDQ+0APalVDeHOeeKZKzi7itCuIgoBmmDRUDW/dZCjo4sijg4QGQ8zOzm5Kp1KcCi37WoiMRGiUG1iGheyQdxWOrus6uVxubZdwVdu8kzPnhmNQlHWMhlXU63VSqdSa0+kq3G43qUspBFFAUiQMoxkPgQCBTY5bNBs4ZIl4xWJUN1FkkWBLkMzlDL5+H3aHfUdd3N79MfaMRtF1c1sTlutRSVXwrng5PnwHr89N8F5VJZtpgHXVeMOy0AwTt12mM+CkB4v6mQKT7kmGHhracG+ancny6nMTFDJV2nqDPPzkyLrdlVirh4GjbYy9MkMtV0erabijDvwRnfPnzzM1NcWnH/80mckMuakcpmES6AvwxJcP8P47SyxPN3dbR070cPfH++nt+3AGF7fxd4NDhw5x7NgxXnvtNaamplAUhWPHju1oGnbyrXlOvnAFXTcYOdbOJz41sq1L9Y0gn8/j8XhuieTjWsTjcUKhEE5nk35++vRp7r777hvalVtdY65fZ+r1Orlcbp0BjCAIuN3utSH29TtyZVXnb95fYmoxj6us05AFzi1k6SLD5x46vkZNF0WRgwcPcuHChXUyrM3gdCp84TeO8toLV5g+u4yoiBz5eB8PfGJwyxpI1/UN69bw8DAXLlzg4MGD235eKBRiaWmJjo6OtfNomBbPX0wweW6Z6GwRodI03rJcCgu5Os8CXzreRWfYzXTURXA634yhcMmYxQaWBZcrSRyTacqlA/j9fuKLBZYWCjicMsN7W/j8Vw8xe183K4kSXp+D4ZHIDa2nP0u46aP+zd/8Tc6fP88f//Efc//9939op7+PMuL5Gs+eTzCfreIBYhUdSTOxsFAdMhOqzuXlEoc6/Tw62so7b77O+Pg4oihy5MgRTpw4seWUxTRNLryzgOyQ1zV5qwh1+UhOZrl4dnlDPpKkSPQ+0EOoP0jycorZS2nUuo4sCbT1BujYHyPQG0B2yNRqNSRJuuHCaMdzE48Ti8WQZZlsNsvMzMza9v52aOgm89kK1YZBwGWj8yr1EJq6nlgsRiy2fnftegOYbDbLlStXWK5aTE7ZOWz5ETcpvmqVKpquEwgGsXQT93yBmckMpYNteD0eDhw4wKuvvophGNsesySJHLoB05Fqtboue6ulpYWnvvbbfP/Fy/zgv5/Blqli15pDlrwk8KP3Erw1HOah+3o52h1AUZR1E7PLiSL5uTwtSxWkoB3RbcMyTITFEp75EmfbcvzKXT30dAWYKapE5kpoi6WmI49H4X1yiE6VqWde4c0ByEXD5EwDwQSh1YXbI+NMVKgUVMyQQKjdy767Ojl2d9ftIPS/Q9SyNX78zXNcHEsidXhp9di5/n7rtsuYfifZssp778dR6zrBEZW333672ZD19nLPPfdQz9cpr5SRFAlHwLG2U3PpbAJd1dc1edfCG3ERv5jk0vnltUYPmrv9I48P4Qk5mTm3zOJSkVylhoGASxBo9djoPhii765OInsjJBKJHcONRUnckQp+Pa7Ny0smk3g8nhuaiu8Eh8OxqWNnuVQmPhHHNEyMlEG2prNSqBH1bfzscqmM3W4n6pRZKdZJllQ6gk4cAQdaVUMwBOLx+K5MY0RRxGbbpdupJFEr1Ei8m6C0XCbldWDF+ik3DKIex3pnZgvKDZ3xZImsx86hlgBTL00RjAWJjkbXnpZKVfjefztNfqGAzWcndSVHOV/jV3/77rViXRRFnvrlgwQjLpam8zg9Cnc80Et3j58//uM/pl6u418JkJ43SKgaFhA9ucC+o+388q8epkHTWOH22vKzjcHBQd5++20Mw8Bms22gMG6GictJfvLt8xiqgWSTeOcHE7i9Du7/+K2hy83Pz+/YYNwoqtUqxWKRkZERTNPk3Xff5fjx47tuJjWjuWu+1e7l6hpz/TpTqVTIZrNcunRpTdssCELTzK6mMB0v0n4lj7FcwVQEpkIGwXv2oNjXU1oFQeDAgQOMjY1RLBa3PdZw2M1nvnQQ4wv7EUR2lMgkEokN9EtRFInFYiQSCVRVpaenZ8uGuL+/n+np6bXB9VymwpWFAtH5EqJuInc3B+JGpkZoscR8yMFsf5jjPSEWkiVKDeNqDEUd0yETb5HIFzLEsvP81//63xjpf5QzL82gFlQERSTWF+Sprx6mfyD8Mxd+fjO46UbvjTfe4A/+4A/4F//iX9zK4/nIIVVS+cG5OMvZGl1ZFX06R6OoNnMLLAskkWjEiTUc4r25HPV6nW//X/835XKZqakp/vk//+dMXZyixdZCeaWMKIp42jwEegPY3DZU1aCcq+HYwmFTUiSwLIqFzU0/BFHA3+3H3+2n974ejIbRdOV029bRcRYWFnYMB71RXJuXV61WOXPmDA8++OCO0694vsZz5+IsTWSxGhqyx87Q3iiPb+HstYrNDGAA3p9epvw/LyCuNDa8plxq0kB9/uZCIsgiEgKqqqPqJt6r77t//36++c1vfijNyk4YXy7xwx9NYpxJEwHkoAvRczWuvaZjZGsU317iuUId4ckRjnR1MTU1xdDQEAAzmQqOkoZlWYhXhwKCJCKGnTgLKtlsjbKq86kDbTwLzAUdmNkaWCAEHNwT6uQOv0zS3s3FnEpDN2jzOxHF5oQtYVrE9oS5dyDMwFN7cHvtt/V3f8cwdZM3vneJsUtJHN0+/NuY2ogiRHx2CpLA2FiSxRdPInkkQpEQc+fnCKQDVOKVNeq1t91LdDRKaDBEIV1D2cZQRRCbbpnlTcyFFJdC38f6aDvSRn4uTzVVxWgYKG4FX4cPf3fTREbXdQzDuOXXz7W6vHw+D7DpruVmOH8mzuVzKxi6ydC+Fg4f77ih37TD5iDgDyDIAs6gEzVXQxAba26hqygWirjdLqRrikJVbw6OBFHAMix8Hh9VrUo8Hl+7pj8syuUywWCQxOkExbkiKY/M5XiRgMu2uUmX0MxYcykyK8Ua5wWBEZ+byVcnCQ2G1syAZqcy5BcKxEaiSLJAJW9naSJDcrlEe+cHgwCHQ+aRT603tlhZWeHw4cOkx9K88eIY1vAQsteGKApMqjrGe3HCPQG67tncYOM2/mFRr9ex2WwIgsDp06cZGxtjcHCQ3/7t3+aFF15AbWhkqjq6aeF3Kvg3saNfXirRKKm0XQ3GTlxOszSbuyXHNzc396FMhDbD9bq8U6dOsX///l1peROFGu9MpZlbKuJ22zg6FOFQZ2Cd+ct2WDWA6epqXg+1hsFysUapVObKTByxbGAmq5ghG7nJZQbbeik1TIp1bV1Q+SpGR0e5dOnSrkxQdht3sLKyQktLywY6tsfj4Rvf+Ab1ep3f+73f2/J8ud1uVFVde3263MDI1hDLGnKn9wNDwrATa76Ila+RKqk8MBzlU0e7eM27GkNhIHps9EUcfCrYg8uo8Pqr47z73AQOr5220ShaQycxnuZH373IP/qduz8SsU833ehFIpEd7VFvA968kmYpU6UrVUO9mELy2pG7fGtNlKUZ6KkqwvvLtB9t5eJymYP3P8YDB/qa2//xBrPPz5Kz53B4HFiWxfKZZdwxN70P9uJp96I4ZGpFddPPt0wL07S2pQiuQnEpm9Kgbtb1aDtcq8vTNI233nqLEydO7EhLqmsGf3tmifhr8wTSNSQEdAkupCvYFYmnD994tpPP40Zx2NH0GrJprU3UioUiiizjdH8wfbdMC9Oy0LQG1w66rw03P3DgwJpO7maL1lqttm43r9YwePGdebSzKwQd8hqlchWCS0F0KfizNQqXMrwcmKX36X14vd416q8oCCCAcD3T2rSwBAFBbOZpRb12vnxnd1PkXKhhWRZRb1PkXJsvUFSWMQMNgk5lbedIEgW8ToWVhsFAro7UMG83eX8PyC8UuHx+BTPq2rbJuxZ+t43lFhcD8jG++M9+CxsKi28sMn1ymq6RLuSIjNEwKMwVyM/k6bynE5tTwmhsv2NtGhb2bTKEHAEHrYGtYz8WFxdvecTBtbq8arVKuVzeMT5gFa+/NM0rf30BvW6AYDH5ziLJ5RKPPz2684uvQpRFEJvrBjSvE0EQMEyrWciZFoVSEa/Xu1ZQWBbouoF5lQa6qpUVZRG3240gCMzPz9+SYjWXy9He2s6F8QsoTidTmSpOm7ytEzM0hwYxn4NEoUZPV5D6fJLCfGHNZVgUheYs0zQBCdMwEUVxV1q+VTbGRH2C9Nw0BZtE5OogsyAIrNRrJMfTdN7VeVPawNv4u8XXv/51BEFgdHQUh8PBE088saZDc7UN8n8+8w6CJ4xuWnjtMnvbfJwYjOC5pkZxe+0IkkQ5W0OxN9ce7xZsghuBruuUy+Vbvs5cq8s7f/483d3du6qPs5UG3319luSbC7grOmURfjCUQX10kHsGbqzm0g2Td2ezvD+fJ5OuYFlQtKCgNnBLJrWJZcKREDW3glarMD89Scnrpq2tbUOD1dPTQ7FYRNf1m45quhamaeJwODY0ehcvXkQQBAqFAtVqddvGeGhoaC3YXhBoapubNrysCZ0tQBCwaK4/AAc6/AxGPWsxFG67THfItWbAMjcukT13hcBV0yqbQyHQ6WP5So6V5fLPZAD6jeKmK7Hf+q3f4pvf/OaOdLVfZKRKKpPJMlHdojGRRQ47kYKOdTcnQZFQ2r1YmolwOYMiSuy59xE+9rGPMdoxSu50jlg0xoxZZEa0mJcF9Jibeq7O9IvTqIU6w0faqOfrGNcLTmnmmzgCDob2Rjc8thtYlkUul9sgGP6wWNXlmabJG2+8wR133LGr6ddspkJ8PEMwXcPe4sbW7cPutuFfqjAxlSFd3rzh3Q4NwySh60wtFrg0mWKlUCebzWK329c1eQBmUaWqCKhWnr/93nd4/vnnOX/+PNlsFkmS1jLvvvWtb/HCCy/c8LGsYnl5mVgstqZ/vZIqk57I4DWsDU3etZBDTryySGEyw2SytJarBzAU81Lz2bEUET1bazatdR0jW6cStBONuGm5WlDZZJE9rV4e2tPCx0Zi7O/w47RJWJZ1dS0VuL7EEq7+awvJ7m38HWD8dIJcWSXov7GBQtBvp25IzL6XJv5OHEESaN3biiE1qVI2j41AXwBn2MnSO0t0BFxgsWaccT3USgPZLtE7dHPrRLHYbHZupcPcqi4vGo2iaRorKyu7bvJqNY13fzKNIAi0jUZp29uCzWPj/GtzZDNbhy9fD1ESsfvsaFc1JGG3DZ9ToVjTsEyTQqmI3+tbNzUu1zV8LjtexSKVSrGyuEJJLWEKJpZl4fV6iUajzMzMAKztUgLodR21qKLX12sFt4JlWRQXi9RSNQqKSKmu4duGFbHuu4kCdlliLl/DE/Qx+dbk2no1vLeF2FCY5csplsfTFOIlYoMeYq1bh2ZvODbTAqx160yzwBO2SnO5jb9naIbJVLLMpUSRQk3j3Xff5eWXX+Z73/seTqeTz33uc2u1w6VEkdM5Cc3uI2xChwmibvLmlTQ/PBdfMxYDOHS0nb33dVMr1MknSnQfbuXeW5BRNjExccuZSdfq8qanp3G5XLtuji4niiTfTxApNvBFnARtMvaJLKcurKw7H7vBa1NpfnxqkfI7S4ROLRN5b4XApSzZvMrJgIj3xBDV/VEqMQ8P7Ovm6MH9tLa2Eo/HGRsbY2xsjMXFxTWdcX9/P41Gg8XFxRs+J9eiXC6v6SGv1zDfeeed/MZv/AbhWBvPnZrih2/N8f5sFt0wN7yPoijIclNG1OZ3YGtxo/vs6IlKM+/XMNGXy5geBSnqps3/waB8NYbiYGeAgahnncvmpvWK0BzCWbvMgP1Zx03v6A0PD2MYBocOHeI3fuM36Orq2nQ35nOf+9yHOsCfZ1xJlSnVNfzpGg3NWKPLbQY56kJfLhOpGcxlKiQKdapjKfS6Ts5nY7oiI9VLmJbFjK3CHb1B9JUq2cksx+7pZvLMMivjKYLdfpxeO4bedMOrZWoc/uQgHZ03t/s6Pz9/y6df1+ry3nrrLfbu3bulxfH1qKgGlqojWiBepZKJPju2YoNcpUF1h12H65GtNPjJu4vIMwXEVBVbvMxib43YUJiQY33xbBkmWraG0e3ji08e4kCHn3Q6zdLSEmfOnOHcuXO43W4mJia4dOkS3d3dPPHEEySSZS6eSbCSKCPbJPoGQuw/2IrHtfXvwTCM9ZOvuSxCvISyi6mmHHRgT9c4eznJHT0hotEoqVSKkZif04MhUtUGoUQVa7GEpYjUW91kW2Tub3Vta4EP4Aw5CYacBFY00lWNmE9qrommRbGm0S/JeCMuHLdg+nob20MtqUxfSKJ7lE1zOreDXZbQPQoTbyywpzdI7FBTzzo2s4Rl05vGPKGmPqyWqRGSRCL9AVYmMrTuCa/Lb9PqOunpHN2H29gzsnnkw07IZDKbGrB8GKzq8kzTZG5ubp0x0U4oFurUSyrOa8xD3AEHuXiRXLa2o6PftYiORMlN56hWGpw5uUhhsUBOEWl0O4mFg+tcNxu6SaGusb/dTywSwLIsMpkM3Xd1ky/mWVpaolQq4Xa7aW1t5ZVXXuHs2bN86dNforZQIzv1QbB8aDBEZCSCe4vB0KqbXy3THPrEKyo2Wdqg79wOfqdCrqphtvooxHM0qg3sbjten51f+n8d582XpikV6sQ6/Bw4GiaRSNDRsTvWRaA3QOQtmYxmkKs0EASBar1BLwLRofDt3bx/YGiGyfffnuPCK3NYdZ3YgRjf+tP/HcMw2L9/P9PT0+ue+9Z0Gt0w6czpqJeW0RoG9oiL7qOtjC+XmFgpcbAzAIAsi3zuK4dYfKAXTTPo7ArguHq/L9U16pqJ2y7hugFzjGKxiNPpvKU+A9fq8paXl6lUKjcU8J2rNRArGpLbhuhUEOwytrkClZJKWdV3vB+vIl1WeW8mg3cyhy1ZRQo3IxBC6SrHPAoTvUEK7V5iXgcPdAU43tP01HA6nesiDorFItPT00xPT6/lATYaDcbHx3nllVf42te+dkPRMsCaY2YymdzQ6AG4PV76jj3F+ecmkUorvNfiovj5fTy0f2OzPDAwwNjYGPv27WO0O8j7mRqhmSIkmhIb3auw0uGmxy/QG96dBrtvKMyFV2Yppqr4oi4MzaCwVKRzfwutOzg5/7zgphu9X/7lX17773/1r/7Vps8RBOEXescvV2kgCQLGShnBsz2tSpBFLBPsNR3VJZNNV1HnC9iCDi4vFXA77ASvNoorRZWJlTJ3BFxkJjMcvLODz/76UX70nYssX8mSmy8gIOAMOTj+5B4e/fTNOR6qanN3bDfZKrvFtbq8c+fObWqash18DhnRZcMQBIxyA8ljw8jWUBURj8+BdxcU1WsxlSyTOrtCryWwMBLGuJzFN1uiEHTRHnSv0TjNqkZjpULGa6PveDtDLc3dh2g0uvZPIBBgZGSEZDKJw+Hgpy+9RLEYgHQAqwI2Rca0LMZfmuVkf4BPf+UwfT2BdceTSqWoVCprsQqrOxz5bB1FtxCcO38/walgMypUiioNwyQYDDYnmdEoTx/u4MeKxMJSEbOkgiLhb/XwRJcPIzlFpdOHJEmYprmpWYUr7CI8GKInWaEmmizlqkiigGlB1CbTIStERiPbBnffxq2BXtepVNRduU5uBsWlUJnMI1/d7S9UNSZyFuVGDociIYlhOoJOnBEnjVSVxz+7n+e+e5GViQyiIqE4ZLSahmWYdB6I8dRXDu1as3EtlpeXbwk96Fpcq8ubnp6mr6/vhnYLgyEnnrCTfLy8ZkBTTFdx+h1EY7vflQLw9/hxhVycfW2OmcspZJuMVFEpe2xYSn2NslZpGJiWyUDEw+jVHDK1qGL32YkMRnCGnE0adTSKaZqMj4/z0ksvUVms8OzUs0QjveQkqFoWLkEgtFigdSJD38f7CPZvNEtbpeSn5lKIokhNNTbkSe0ERRbRTRNdgIA3wOLcIgOjzYY6HHbz6S+sL3rL5fKuaaehwRAHjndgnlwkUVexgD5BYP/BVqL7bo6hchu3DrPpChdemcM3X0S2ySRem+Nf/n/+A0/f3bfBgCRRqJMo1ImZoI6lEOwSUsCOFq8gTmaRBwKMX9PoQdO8rOea8O10WeWtc8tMXFxBq2rYvXb2HWrlrtHYpjq/6zEzM7NjSPeN4FpdXqFQYH5+njvvvPOG3iPqcaD7bDQWy2CTsCoN6rZm9p93G0309ZjPViklKrRka8htHoSr91/B4SWwVKbHhPsHI9w/FN32Gvf5fPh8vrXmNZvNkkql+B//439QLBZ5+eWXeeyxxyjWNXTDIuBUdoy+uPa9Nmv0spUGU+eXCeRVnK0eCoky584muG9vy1pk1CpEUcTn81EoFHhkb7NuvBxxkUtWEAQBW9jJkQ4/HWRYWlzY1SbFwSPtzH0sy8XX51hKlhEFCPcGeeRz+z4S+jz4EI3eSy+9dCuP4yMJw7Ka3FgThE2CpDeDZTS3inXNwDRMkGQ0w1w32XHIItWGgSiLmJqJaZh09wb5jf/tXqYm02RTVWRZpH84TPgGJs/XY35+fkNm3ofBtbq81WnfToGZ16Mn7KZ3b4Qr6SqBlQpypkbDJlLs83NiOLLWDO8Wdc1AVHVcXjvdfh/zgHY5g7lSoabIKLKEaRhUBKiFnfTe18WTd3Vv0LBYloUsy3R0dPDlL3+ZL3/5y7x9co7v/f/ehkYN063TkEQ8bjcOxUl2MsMzf3Gar/2Lewhd4yBYKBT4y7/8SxwOB5/85CfX3luQAKzdUZZW6ZWwxlO/NovmV+/uYS5ToVDTUCSR7pCLgMuGrkd54403OH36NCMjIzz22GPU83UK8wXUooogNsOS2460odd07BeTZCwBTbCwm9DittN9rJ3Ygd037rdx81jV3yLc3M3Iomnmsro2VRo6Nd2k1edgpVincnV3XJRFTMOkvd3L1373Hs6djjN+JkG1qOINO9l7uJ19h1px3+C1B82d60ajsU6P+mFxrS5vfn6ejo6OHbW/18Nmk7n3sWFe/NY5Fs8tI4gCdo+dOz8xsC6mZlfv5bYRHY1Sfm4cybLwxzzo8zpdIRdy0Emm0rT7bvXZ6Qm76Qg4kSUBQzMox8u0HWvDGWqeH8uyEEURWZY5cOAAXsXL2//tbS7PpJh2llBXw38FCbfTTi1RRHhlFmfYuc6pVFVVarUa0Wi0afZiWU0t4Y3yri2ucp8sRElElEQ0Tdty18Tj8SBJEjMzM/T19ZFMJjeNq4DmIGLosUGCvQHSl9OYpkX46i6lc4sQ7Nv4+0PDMLEqKopDRvLZkDM1TF3c1GWyoZtohoVsgqoaSBFXU3fqlDCrGrarNc1WSJdV/uoHl1l+cx5vw8QuizR0i9fPL7P0sT6+8OjwtkZsm+WwfVis6vJUVeXChQvcc889N/T6bKVBuqKyFHEwEy/iX8jh9NjxH2nl7gOtN8TS0AwTQTea9aPywf1AkEQsLEQTREG4oUGOIAiEw+FmfuW99zI5OcnzP3mZOSPE/HwZyxQYGGrl0eM9WwbLX5v7uRl1E5oUcFESMSyw6joGFrJNanoKbILu7m7OnTvHwYMHefpQO3f2hkgU6oBFi9dBR8CJKHZx+fJlJicnGRoaotFobLlhIckiT31xPwfv6CA+X8DhkhnZF/tIufnedKP34IMP3srj+EjCbZPRLQvRY0NLVpC2YU9altXkBNslBEHA5bFRccoYqo7PoZAs1bHLTkzLoqxqDEQ9aFUNT8zTFPzTnIDtGWmBkY3vX2vo6IaFdxeTL4BsNkswGLylmpmpqSkGBga4Mr/E3NIyH7//3ht+D5ssctdAmOVSnanxFfx2O8GIm7v3RHlw+ManvC0+O0KLm8ZkDifQbVeYOdSC0e+nICugm4h2mXC3j/v3tXCwK7iphmW1AFuFaVqcfyeOS7TTeqRJVdI0jUqlQqVSpiJWSJ/K8cx3TR5/fB8tLS2IokhXVxeNRoOhoSEOHDiwls0Vi3pI2CXMcmONsroVzHIDVRGJRd3Yri7sLpdrLaA0k8kwuAl9ShRFLl++zNLSEmpNZcS9l/F3l1haLFLSmgt0xG2juz/E0F2dhIZD5KfzNCoNHAEHocEQ/m4/4m0Tlr8XSIqE3SZjaLvTY10PUzexeRTMRnP3OOy20eZzspQrE/Y6ifmaNzqtqiE7ZGSHjMMhc+LBPk48eGtolgsLC7fUAe/avLzl5WUCgcANU41WcezOTvxBG++/M43P66dvT/imqaltR9sYPNFD/jtjZGdzeFs97B+OEoq40PTm+Zcl4YOcqIZBbjpHeCi8zl3SNM31QfJ1ByFfG859LQiiQOfV3V2t0SBZqHLesuBSicBYgL57mn9nlmXxJ3/yJwAcP36cXkcvlmnhsyvkr2oJdwtVN1BkCcUEySHR2d3J22+/zfLyMk8+/CTFxSJ6XcfUTSR7M7LD3+Wno6ODH/3oR4yPj/NP/+k/RREUCgsF9LqObJfxdfpQXAo2t432Y+20H2vfMSz+Nv5+0e53EtkfI/3mAlK6htjrY88WVvQ+h4zbJlM1LaSAA22phOiUsWo6csxNVdVpadtavvHWuWWW35wn5rAh97iaNZJp4YqXmHl5jrMDEe7bt/mA0TAM8vn8LW30pmYXWKrKnPrJDFOTMxy/ay9L+fqWDc/1SJbqPHM6zsLlFP3JOkldoxJ2Uu7ysWcozP4bNADxOxUErw3LqWAWVKSrLASj1ABFwvLYCGwjFdkOwWCQT3/601iWxf/z0kVm3k4RWK4hGCYTk+MsLCV5/HCIloBnnbGLrutks1mi0WZdpigKmrZxfQm7bRy5u5N38nWKKyVc+6KcuKtr253CVe+B1tZW2gNO2gMbBz+rGwqvvfYab775Jr/7u7+75UBRFMWPTJTCZrgl6X9jY2PMzc0BTbee0dHdu5J9lNEVcjWdBzu9WIvFZmbZFkWwWWwgeW3kPQpRj43uVi/pkShzr85xqMPPe/MmVxJpvB4P7X4nIxEPWqJEdDS6481vOVXmr/70PRpVjcd+5SD79+5crGQymVtm4Q1NXZ4qe/nzvznP2PvTtHW0kVeneODurk0tfrfCdKrMc6/OUBpLQrGM/Ug3j97dzaFrKB83goGoh0MnujkLmMkqBDzsPdTKw8c6muH1poVNEmkPOLfly19fgC2nKiSv5PDFPlj41wVSd0B8bIWV+RpvnDrLeCKP4vLSEo3QcLfw5FOfWZvc67rOvu4AZ7t8aBM5pJBzS32KZVnoORW93c2hoQ9+G6ZpsrKywre//W2OHj26qU5GEAQ+/vGPEw1HefNbb/K9K29R9vkxXBIOmwvTgvG6xuJYksxKmXs/N8rQE7fuN3IbNwa7305rt5+503HMiHtHCs21ME0Lq6DSPtosjkzdxK5IHO/20uMTiUUCuK6a79QyNbpPdCPvMGAoFuv89NkJ2rsD3Hnvzs1buVzG5XLdUnrMqi4vk8mgKMqutb+bIZWqMDW+xOE7+hnec3MN3iokm8TDv3kcj9/B0ukE4VYPXnezKbs2akFXdSrJCnpVJzISofeh3g3U3GvXmeyVLCULqpreNMy5CsVmoyNiYylfxbLbSV5OInU01y9JkggEAiwtLRGNRnE73Yg2kTanwizWB46gu0C+ptPqt2Or6gRGorzxzhu8+eyb2Eo2LmYvopbUa9xEm42aO+ZGjIlcfP8iFb3C6997HSPtZX4mR0U1cCoS3T1+jj48QOxAy9r3vd3k/Wwh6Lbx+Sf2cLI3QLWqMToU4cAWXgAtPgcDUTdnFvP0Hm9FvJzFrGoogyGqHV5susHeLRq9sqozcXEFb8Nca/KgGTtia/PgvJLj7LkEd49EN1D9gLVdnVuFeLrAd16YpjJZpTafwud28/6Vi1zsX+LhT49w59DOA+fXJ9MsXlghNl0Aw0ISTLwqGDmNRKLIVKqy5fnYDH0RNx2dfpaWK4QXypjFIpbQPEf5djfRLh8D0ZtndwGkyirJpQb+RAV31I1gl7AvFEmvgBzqoLXFuTZMBvjRj35EJpPh6NGjhMNhZFleC3q/FoIg8IlDHfS2+5maW2Sgo4U9Hdvncre0tKxJf7ZbF0KhEP/9v/93arUa77//PidOnCCTqVCv6cRavWuZnh91fKhG75lnnuH3fu/3mJ2dXffnfX19/If/8B946qmnPszb/9yjN+yiM+BkWTcJtXnQF0vNzI/rFiOzpmHm69gORCmJcE+nH4ciER2Nkp/LU1gosM8r4DdFursjeDQTdalEZG9kU+3F9ZibzZOazGDUdGYnMxsavVrD4EqqzEy6Ql030CsFjg62Y14TNfBhUCqVWMpV+emP4iTeukJHOISVT3PmYppkosiv/NKBXTm96YbJi6cWKb65hKdUJmSzUzyT5P1WHwc7/DdVCCiSyKcOdzDS4SdZVHHaJAajnhumgF4/bdY0HUs3kJStm1jFrpBXFWbtvahBlUKhzNx4kbKrn3/1X/6Wu3oD2Csr5HI5emKtdI7GWFgoEV4qoXR4NzR7lmWhJ8rkZYGW0SiDLR9oiUzTJJPJYBgGqqpimhbZaoOGbiJLAiGXDVkSGRoaIiJFMM4HGavW8YdcuK6lqXpsZMoqlzMVfD+d4US3H/sWGY638XcLURLZf1cXF04nKFQ1gjvogK9FoarhRuDQ4wOoS2VyV3IE+gOo1Qq9raEmnc+0yM/mcbe4Ce/ZedI5PZnh3EvTLHb5d9XopVKpW2LAskrLWdXlFQpF3nljnkZVJBjJcff9vbuKl7kW6XSZb/5fb7MykeJsW4JP/dpR9h/8cDpCxalwz68eJHdPF6mxVHO3q6Gv7YBbhoUoi3g7vET3NvMLd2quzYaJIay6UV734NU/N0VwO9x47SEmxpJUyxWmL5Q4cHQUm81GRajganUh1QwCThvZSoPoNde0ZZgYeRUjX8NSDSzrKp3XpWDaBDrbfYg1g0B/ADkhs1hfZGJqgtIhi5RLJl/VMCwLhywRcytY+TpWXOOeyD1kyfLmD6YRQ+2YAQfOgINSQyd5JUMmXeWT8iFaRm/r8X5W0eZ38vQ9vbt67oPDUQo1jZl0Bc+BCIokkqxrKJrB/UMRerYwz6hrRlOTJ4sb7vGCJCKLAvWK1qSGXr1VWZaFqpvUa1UURbll2ZymafI/nztN9XwJpVAkPBzD4XVhllQKl7P8VBin/dfcdAa33tlLlVSuLJcIJCogCigxN1JBR/H7YKGIsFJlLF68oUbPLkt8crSV72sGSa8NR0EFC+pehUBvkE+Otm5Lbd0NVM2kUdNwWCC4ZARBQLRLoOrUNXODscuFCxcYGxvj7NmzDAwM4HK51rwHrocoCgzHvLS7u8hms8DO95ve3l7m5ubo7e3d8jmapnH48GHGx8f57nf/loVxB3MXVtA1g1C7jwee3MO+A7dWH/6ziJtu9J599lk+//nP09PTwx/+4R+yd+9eAC5dusSf/Mmf8LnPfY4f/vCHPPbYY7fsYH/eIEsid/SGeKZQo743jAPQF0tglxBdCpgWZqkBooBtJMxyi4s2v5PRtuZUzO6zM/jJQc7+7VkS5xNEFTe2ZBXBZ6f9znY67+zcsRAAGBqOEN7rR0Jh76H1P+p4vsazFxIspSsIeRVRN8hrdaZLIvs7ajw62rpjptJWOH/+PJqm4XK5WIhLxN+eZnBPO/Zgc7LkSlZJnIpz6XgHd+3dWddVaRgUUhXyM/OYIYG+1n5cK3VyqXJzobnuOA3NID6VIb5QxDRMPD4H/SMR3NfdUBRJZKTVx8iHuN6vp276/Q5sXjv1grppI2SYFksrJUp2g5YrOVqXq5gVramXCvkotMSYalg48gnMn/wEm81GONhKfJ+H5PkiodkCNq8N0aNgmib1bIV6voYWdOO9o4PH7u3BfU1xK8syTz31FF09fTz31llKL04Sv5JFUw0kWSTS5ePIwVZG2nwsjyVZzFWxhR3rm7yrCHvsLDV05udy7JvN39bk/QMiNhiiq8fP2GIBjzOwKw2GZphUVsqMdvvpOtCKscdg5qUZCrMFisUiNs2GqZnoNR13zE3fx/pw7cLBbGgkwuiDrew90Lvjc7fTZt0o3nzzTeLxOA888ACiKPLic5e49NIyutbcZc9nazz9xd074QHMzWQZe+cS9qhEY67B/HT2Qzd60KTbRkYihIfDlBIlystl9FqTeis7ZNwtbrwd3l3Tn51hJ46p5pBJNyzka7TgumFhGiaNbIHLdY3n352nkquBIGIaMWbftRF2V7nr4V6MoEFpPkPMZyOrauRFAb9NQk9V0ZYrzfsUIMjN92/oJqVyg2jUjVYxUQ63UoqXWHp7ib79Iyz7/LyZKNAwLBxKs0BPGw3mMhV8TpnBqIdwQaM2ZSLZWnC2ej6QFbgVKi6FuaUSF9+cJzIcXpMn3MbPL8IeO1841smFxTwvnZ7AH2tlIOpmtM3PQNS95aDWbZOxe+2ouonraqbkKizNoGFaRPx27LKIZphcjBc5t5gnV22QSSX5xJFhwtXGTVMXr8Wb71+gmhKQ8iVcfSGcvmYtI/kd+C1Ymc4xdiVL5/Gt18uKqlOrNAg3TCSvjUq1gtPV1CwKioi9ppOpNG742LpCLr58Zw+XugJcSVUwLYu+sJu97T5avB/eBTvssRFo9VAazyItlRFtIo2Gge6TSMyMs2Cv0dnZiSAImKZFX/8AHs9b/Ot//a9xu90sLCxw/vx5DMNAFEVisRjB4PqNCo/Hw8LCwq6Ox+fzsbCwgGEYW2qwo9Eon/nMZwD4L3/8MmOvzOJr82L32kjN5HjuL84Q/N17aL9JV/qfF9x0o/f1r3+dgwcP8tprr+F2f7Al/NRTT/E7v/M73Hffffybf/NvfqEbPYB97T6KNY2XJpJUR8OEe3xYCyXMstZ0CRoIosVcLNlEWvwOnjjQtm43yRFw8FriNSYWJxjqGuIz93+Grj1d64T1O8HtEPjCr4zQ1ta+jpKTrzb4wbk4y3N5ovMlrHSVUrFEX9iPWcvyXlVDEODTB9t33C2zTAut2uRfy04ZURJZWlrizTff5NixY7x9ziLi8aw1eQBK2IE0WWV+vrCrRs8hiTh9NmqygLlYYC47TrC3F4eooYjwwx/+kCeeeAJRFJm7sMKbz08wP5Wj3tCxrGZYcSjqZv8dHdz7xDDKLvWKu8H11M2A18HQ8TZO/2ACT4sHyba+UEksFqgg0FJXSf7oDK6Ij0B7BJusYCxX8GfqKHvDJH3d3PXAIL1tUeLxOIK4wE9zVRJxA1fGQp8oUKvWKJkaYq+fw8c6+Oyjw/RFNtI0KqrOVNGNttLG7PlxXKaFQxYxDYvkxRQ/OLnE2ePtRKfylBWR0DYTQI/DRqpYITd3u9H7h4TNY+O+J/aQ/4szJBYLtHT411EBr4emm6wsFWh32TjxxB5sV3cBh58cpjBfYOKtCZwOJ7JDJjgQJNAbwLbL3e1cLsVnvnh8UzOGVYxdWGZqLEkul+P4vXvYd2D3dKLcconJsRSVsoosS7R1+ujbGyWbzTIzM0OtVuOBBx4gH9cRZJGOPRGyC0WmTicwPr+vSaPfAnpdpzBfoJqtYqgGclHFYVmoiwaBPieWUF3LgwLWifstyyKXKJFaLiMIAu3dfjw7NMaCKODr8OHr+HBhvKHBEKFzK7SIsFysEfHYsSsSqmaQLqu0uR00FitcLsQROj1EBvw4nQ50wyS/Uua1Zy4higL9x/pR3SqpCyns7QKnp5PkZoo4iw2cXge2oBNBEjFMqDY0NF2gJRKgRbOoLZfJTGQozBWw9fg4nalQwk7YqWygu5smFGsaZxbzDLsdZGfyaA6BqG39BN9tl8l7ZJZmclRSFbxtHw2b8190eB0KPS6Nf3Rv765jNpw2if2H23jt/DKupRL2dk/TZEQzqC+W0IIODh9uw7QsfnxxhVNjK0jxMqzkkVwKL5ZnmRyJ8PThjhuSiVyPeDyO6PRSXJwh5FLw+tb/JkWvDSVZIREvbvs+DkXC5pDRFBGhqtEQG7g9nmZGbcNEtUv4duGuvRlCbhsnBqOcGLz1u+Aum8z9Rzr4UUklfTmLoBkwFOSO+3r41ME2UskV3njrHWZKkFVdaKrAsad+g4ZgwyMIxGIxCoUCo6OjmKZJMpnk0qVLWJaFoii0tbWtra+7RX9/P9/5zndQVZWvfvWrWz4vvlggPVMh0OnDc9XYyumxs3Qxydi5lduN3lY4d+4cf/iHf7iuyVuF2+3m137t1/iDP/iDD3VwHwUIgsA9A2F8ToV3ZjIk5DqGR0FGwBJAsyzcdpkDUTcnBqK0btLAdXd3c/nyZcJDYfqP9d+wg1wikaCnp2dDs3YpUSS+XCI2XcDM1TGDCs5QEAkFa6ZARBK45LVxrCdExyZiV4BGTWPsdIKL7yyyslTEsizCLW723dXJxNgUkUiEbDZLS2yA+nx2nU7RahiYkoDHvXVDoZZUCnMF0hNp1GKDnnyVS4Aq2nH29eI81sbDd3bwN3/zXS5duoTX66XLN8wPv3mGdFnF2+Yl5rYjilDXdDKpGq8+O0EhW+OJrx7e1Y7oTnjjjTd49tlnKRaLJJNJvvSlLwFw4sF+FiezJC6n8EfduENOTL1ZYK2slPB1eukVROSjLZQbVZYzKwAEgkHcNXDNFai0wWSywnBPB729vfT29vLEx0zGlzK8dXGOn/zkTWbOzGDmCoy6otxnP0CwpmEaJt975ns89NBDhEIhdMPk+fcWOf/DSULlBo6YB/Ga8+6s62grFeZfnCalmWhuBWkb7ZQkCuiigHaDuYW3cevRcTDGI1/Yx0++O0ZiJocQchL0NQv+VaiaQa6oYmVrdPrsfPyzo3Qe+qBBn1ucI74cx3vQy+ih0ZuiQeu6vm2T9/ILU7zxvUuUciVsNjuL54qkPrOXhx7Z3tm3nK/x8vfHGT+ToJyvgdA0erTJEm19Qeay0zz0qRNEo1EGBgY4FSjQqGhU8nXqxTqB9uiWTV6j0iA1liJ5MUV8Pk9J1TFpsiCDoklbXztD9/Rzz4m91Oo10uk05XKZ06dP85WvfIVyts7L37/E5PkVqiUVQRDwhZzsO97B/Z/ag+0moy92C1+Xj5bRCAPvJ5CcNlJ1Ha3cQJFFOpwKbRWdsbKGsz9MtNOPWlcplUqIgkCk1UvShPdfnSXWM0jvA71NKuiFJPtrEsumTLHFTlE3MIrVJj0di6DHSXfE09RMeUSsfoXGSoPcXJ60oZOWoM3v2jSPTxQh4FaoqAITKyVcioBQ1jDyKnJ4/T1GtMloqoGpb073uo2fT6ysrNxQ1hzAXaMtLH6sn+mXZ3BeyaFIAg3DQgs62POJfg71hhhfKfHe5STBy1nEdIWiViWgeqGcZc4wecNj5+nDu2sur8X58+fp6+ujWCzisHnQ0XHZN9a9lmZiiODYYYAc89npafUy0ZInPFPArNYxxDpmQcXy26j6RORigkajlddee40DBw7cMvbDh8XhrgChx0eYPFqioZt0hVwMx7woskh7eztnMzD+5gTmpVlMXWc55kO1HHz5vj4cirLmuimKIq2trWuxOpqmkUgkePPNNzl58iQ+n49f+qVfoq2tbdvj+au/+ismJyd3HBpUaxp6Q8fr+GAAJ4gCoiBQr9747unPG266ynU4HFe5tJsjm83etNvZRw2CILC/w8+eVi9zmQoz6Qqluo4siYRcCoMtXmI++4biyjQtGoZJrLXpZPQrv/IrN9zk6bqOJEmbvvf5pSLuQgMrU0Pp8lEoFppGIYAUdiIuV6hGnUynyps2erWSyg/+4jTj7ycwZRHHVcvrubk88+Np5GCURz4/jDfkpCiEeXbyFOXZPO5WD5ZhUVwuo3R6GNmzcfpkGibLp5eZOxUnvlhgWdWpXc0WCDqdDLXEiLX62TcY5UBfjHd+nMFms/HMX3+fduk+Sg2R9sHwOo2hQ5FpbfdS8Ng4/84CnQMhjn78xuIdNkMkEmFlZYVqtYrX+8GUryXi5pd+4xgv/XiSubPLrMzkQRKQgw4cPT4GJQlhoYTksuF32fAHAjQaDXK5HOlCAfeyjGwPMZWqrPs8WRLZ1x2l1+HiSE7gh2M5LqWSxNwSUz8e59wPTqNEFE7GT3Lq1Cn+2T/7Z6j2ABdfnydYUnH2BjboREWHjK3bR3iuwMpSAaPDQ03T19E/r0VNM/BarE3HbuMfDoIgMHB3Fy6/g7OvznJlPE0ulcOwiyCJYJhIqknYY2PgSDuHHuilbW90w3u8/vrrOJ1OfD7fDceeXJ+FZxompmaC0KQrFop1Tr1wBUswadsbwelykZkvcOqFKxy+o4PAFoOkaqHO3/zZ+1w5v4wj5qFlb7TJSrCgUm0wN5vDZduDlncxfGIYURR56PFhEgtp6jmVcE+ARz67d9P3ruVqzPx0hokzy8QNg6wkoAlmU+omCEjH9tIwBbIzeSaenWTg4X56e3v5z//5P5PJZPjx375AatzN1FgSZ8xNqDWMYZkU0zXeeG6SSknlya8d+dC0w3q+Tn4uTzVdJbGQoN5ex+614+/2423z0vdQH6Ik4pvIkMvX0EQJRYCgz0nWplH3yESv7ojZHXbsDjuWaVKuVlA8Fpn5IvNX8gzvG6D/kX7Sl9KYyxW6Qy5Mp0RdFDAAtVYnn0rjytaQNB3dZ8OKSoyPjxOpR7A7A6QvpogebdsxdN1tlynbJeqSiKWZaMnyukbPNJvuwb6od23X+TZ+vtFoNPjmN79JqVQiGAxuq626Hj6HwhceHeIZuUih6KBe1oj47Rw+3Mah3hAOReJivAiJEmKmRsUPQV8rkiyhLZcJJCpMLhfJVqKEbkB/b1kWL774IuVymX/8j/8xk5eu4BhsY/6tOGGXTDDkRLyqZ1aTVcygk5E9kW3fUxAE7huIkCzUSZgGzrhOwzCpt3modrg5vj/Gw4NB3nrrLZ577jkuX77Mb/7mb3JlKsvJl2dJzuUJxDwcPdHNkTtubWTEbtAdctG9ibtopqxy/twKgfky7rYIgk2iNJ/hzE8v4rOyPHp8ZMtcbUVR6O7uZnp6Gl3XUVWVSqXC2NgY0KRptra2rhskLiws8MUvfpEf/vCHnHrvPdJlFVEQCLqUDfVuW7sPT9hNYblM9KqvhVppIEgCsc4Px6r4ecBNN3of//jH+Y//8T/y2GOPbcgPeeedd/hP/+k/8eijj37oA/woQZFEBlu8DLZsT0Mp1DQuJ4qcubBMpaBiWA7u+/xvk6yadNlvzGI6Ho9vaivcMEyqDR2bZpLOZPAEhXXb5qJbQc/XkTSTcn2jfbtlWbz4nTHGTsYJ9AdwXbt4RlzU6xrxi8u89sMZ/rf//TOopsXiE8OceXGc4koZBAFnf4AHnhim9zqaoWVaLLyxwPmXZpisqhRkAcUtY79aMNmd7RQMEzFTYeXVeaIOhd/6rd/CsizOvT7LD/6fMwT7/Vsayfh9duIZmQvvLHDwRDfyDRo1XI+hoSGi0SiLi4vcd9996x5rbfHw5V89wsony6SSVSRZAI/CX5+JI19MY11H6bTZbM0Q+WgLufFliqk0p8/lGeu1Mzg4uEYXWxpfIv1OGjWtcvzB4yyUFug81Mm9j95Lo9rgvZfeQ5lVmC5N83u/93t86tf/31gLBRwR15bOr4IgYGv14EiUqaSr5CIuXDaZ639uumHRqDVoCzgJ9Yc+1Lm7jVuHtr1RYsNhCgtFJs4myMRLaHUdxSETbvcyfKgNf5dvTf/VqDQozBXIzeRo1Bqol1Xu/ey99Hb33vBn1+t17DY7xaUiuakcudlcM6NPEJAdMlWHxPS5SQIDQVpcTZqer8VNdr5AaqW8ZaP32vMTTF9YJjQUwnEtlVgAt9uGayjM3Pgy519Pc+eJBv6oQqzVw1Nf3YPXHcHnd+DcZMLeKDeY+ekM507FmZItLEUk4JLXZVdphkmxpnFJM6hcSmIaJj0f72ZoaIjh4WFOvzpPZc5HaCiE8+qxKYg42rwUXApjJxfZf0cn/TdJbS4lSqQvp8lOZqkX6qTzadKZNHFvnHAgjNvrxt/lJ7I3Qt/DfbQeaqW4WESraShOBV+nj9Nn41iXUhvcCAVRXFvvS3NVEvEki4uLeCUvdr+dgUcHaFQaFBeKyHUNXdVZmppC0zXCezoZPjGMK+pifHKcRr7BUnIJsRuoypCvg3fnYjrgd5AN2JELdXKLRcQOH3afHd2wSBVr+OoGw4dbb2fmfURgs9mo1WosLS3xwgsv8E/+yT+5oVrGqFd4YH87HZ1daIaJTRLX3eMzZRVHVceUmzo+6eq1LHnt2CoNcmWNiqrvutHTdZNqtUwikSASifBn334Gb/99VBbjlHNVyksF8u1+2lvcaNUGRafMwH1d7NmFiUpXyIUvM8aUUcQ41IccCOFz2znR4edYTwinTSKXy+Hz+Th//jz/5//xZwiZbqrZGo6gk6WxJMtXMphWMwbmZwGluk69pBKwQAo2N3lcfjdhu41YZw+JRIILFy4QCATo7e1da9pUVV0zy3nwwQeZmZlBVdV1Gc7FYnGtCRSEZgzNd7/7XR5++GE+/ZnPsaS38Kd//j6iJHL4rg4ePtSxTqbkdtu4+5ODvPzXF1m6mGz+bkzou6OdA4fb/x7P0j8MbrrC/ff//t9zzz33cN9993HnnXeyZ88eAMbHxzl58iQtLS38u3/3727Zgf6iYCFX5fs/vcLKewkcBRXFBBOLy6dlpi8VeeATA5wY2jlSAZq6setNQlYhiwI2WaQiCeSyOeITWfr7+wmGmoW7VdcR7BKGJGxqxpJaLHL5dBxnm2d9k3cVAiYEBOpZiTffnufcRBrd0Dn+eDchbxhJEenr8G9KVU1dSnHxlVnG6g0aDplWj33dhNjnVDBMi2SxxoVcFemlGZxBJ4HeAHNjaTRZwLUDJdMddbE8XyC7VKTlQzYrpgUPPfI409PTuD2bN/GxqIdYtFlYJQo1FEnAcMoI9c0nXJZh4vF5iPVFCMQUarUa3/ve92hpaaE11spf/n//kqP9Rxk5MUJYCPO080u4fAGKNQ2fy0b7/nbuFu+mY08HB750mG+9MI9TMxF3cMgUHTJuvx2t0sCeV1kWBUJXdT+W1dT5Fcp1YnWTPXe24u24rZ35WYIoiQR7A9zVG9j2eYWFApMvTjM9lSGl6miWhWz1UzhnMS5M0P/xvl27qabTaVyWi/EfjFOYK2BoBo6gA66G9aollexYDjlepVBRWbHbiHW0UcnXsbsVAltkT5WyVS6/H0cJu9Y3edegXq8R7fZTnCnyk+9fQrXAFYQnP3NkW/bDyvkVLp9OMCVZOOwyvk2aQUUSCXvs1BoGc2UV20QGT8TNw08+DBbET73OtDu31uRdC7/PwVK8xPjZ5Ztq9NKX08y9ModaVnFH3YRbwxSuFFheWKaqVNm3bx9mw6SwVCA3m6P1cCtd93bhaV2vcfFcsSMI0NAMbJvEw6gNA7tNobOntUm9evYsyaUkrftaiXRF8HX7WJheoKu1C/8hP6+/8TqDDwzijTWv+3A4zOG9h6ELLtcbGFYDI1nFavMg7LCTaVckrJibVkUiN54hO5eHkANBswgYFkfu7GToxK3LWLyNf3jIsozX6+WrX/3qDdPDFxcX2bdvH4IgIIkbf8s+h0LGIaNlCkTarsmerGpoNgmbU8au7Ly7Pn0lwwvfGaOUrSG4GvR072FwuIslsYeFF2cZLGsUuwIsz6RZVhs0JDetB1s4dqiNjx3r2DaG6Vq0eBTCpWn8jiq//NhXCPlc6177+OOP8/DDD2MYBn/zl+eZm0jRvn+VwulleSLDqZdnOHK8/ZZG1Nwsgm4b7pCTuiQiJysINgldNRB8diI+FyNtrZTLZYLBIGfOnAGaGrv/9b/+F3fccQdHjx6lphkM3vsYr52d4DvvLTLS5mVvqw+fz7cWlWNZFs8//zyqqvLtb3+bly8sUr3iwZcpYAJvZWt0tfo2uJbec38voaiL8XMr1GsaXf0hDh/vwHEL5Ds/67jpb9jX18e5c+f4t//23/Lcc8/x7W9/G2jm6P3Lf/kv+f3f//2fGV7xzwvy1QbP/OQKmVfnaREF5HYvokPGMky8uTrlcyl+WtPxflHhUNfOsQrxeJz29vXTikSmQjxexDQhKAosuEQqYoOA5kGWmwWLpRnoqSpCtw8l6KA3vJGPPjGWpFJUad1ExKrrOql0it6+dhKX07z6zGXUdIVqrU57+1Huu29r3rWhGSTOrjBdrFF3yrT6Ni82JVGg1e9iKVdlerlE16UU/h4/arWBsIvF3HY1vLW+yW4lQKOhUyio+Hz2La3Zc5UGY7NZzr4Xp5SXgCG+8c3THDrazmh/eMvJYYvXQbvfyaK/il8WMSsNxGuea1kWerKKGHbS8CkMtTg5duwoR48eZWFhgW9941skp5K8lHsJX6+Phj3A5YJELZ3HIYvsa/cz2NNDZ1snhYUCVkHA0Mymq9cu4jIkl4Ktw8s+r4OlxQK5qo5uE8ECR92gW5Q4fHc3ww/33w5H/zlEJVVh7NlJ3h9PkXJIKB4FRRQx3W2cqavkXpsDC4afGELaheNuaiZF41KDSqqCEnOTUXXm0hWqjeb0Ney2EWx3IPl1PCUn5qUaiUIKBIGjjw0Q3SLfaWY8QyFdIzS8+SBGVVWwLNwuF0Vnnfe+ewnTLaOEFO66dw+tWxh4NCoNkheTLJkGgk3atMm7Fk6bRMOpsFTT6bySpWOlgjPkbJrCbPVaAQSbSK1Y3/a9N0NmIsP0T6YRFZHw8AcmJf39/Zw9d5be3l4kSUJySgR6AjQqDeKn4limRe+DveuoosP7YwSiLnKJMrHujWt1Ll4i3OameyCAqZmIGZHWviblLZVKMT8/Tzwexx1x4+uM4RwcZaokUpOr9IRdxGIxtFmNcrWKaTWZIEapgZGvI0d2dmoV7BItR9ro8NixWtwYiojTZWP4eDuto9Hb0S0fAZimtRbfVAsP8fETj2Kz3djfa7VaxeVybdscjrb7OOuFaMSLtlRG8tsw6waWZlDs8LM35iXqsWOaFnPZKpenMxQLKsGgk739ITqDTiqVBj/85llyc3mcYSflK3UMI8Kezx/h7EuLeCsNbF1+3GqdPnuEpCAx9PQePnO4g/ANGr14vV4EQeAff/VX8Pk2rlV2u31tp6ua07FdVwu5Ag6K6QqqauB0bn0frtU0FuZyKDaZ7p7AtqZU1WqVb3zjGywvL3Pq1Cl+8zd/c9cNud+pcMfhdl7KVFGv5BErBmqnh5E7Otdl+IXDYcLhMLquc/LkSS5cuMD09DSibGOi4efiXBZH2c3YuWUuxQvkRlq475psQkEQaGtr45FHHqG/v58zizrvnj6Lq9WN1TCopqqU6xuD2QH2jLSwZ+QXry+56UZP13UcDgd/9Ed/xB/90R9teLxYLO4ozr+N9bgUL5J8P06LJGC7hjcsSCJyxIXXLqFN5zl5Ns5ou39LK/XV867rOorSLESWkmVeeWma+dMJ6rk6gmWhKRIpj0htsIPDngjkNBr5YrMhaPOw0uFhX4uXrk0m7rWSCpKwQYthmibLiWXa2toQRRFBlpAEC8Mlg6QQ2KRpvBbFxSIL01myNpHwDhQLQWhOkdJFlcXLadqPt2N32bC0ncX7DVVHEoVNpznvvjXPOy9coZSt4g44Of7xPu59YH3e10S8wLPPXCY7lsShGmvGF4WGwQun4pwcifDEZ0YZ6dhYXEmiwKHOADOpMuZAACazGMUGoscGpomZVxF9dvLdPiI+aHeLV7+vQHd3N0pVIRqKUraXeeGVNwiMnsACWrx2inWd80sFQm4bIY8NTKjGSyh2marJOjOcrWAYFnKbm6OPjXBkscjUmQSlgoogQnTYS++RViJ7Irt2Y7yNny2kx9Jcmkqz4hBpCzqvobgoVBsGcyUV/5kEsf0thAZDmIZJaalEbiaHmlcxDRPZIZNSU4Q6QhTOFKAKatTF2/M5itUGNlnCLouYlsVctsIV3WDg8buI1GHunXm8DoWDXxjl7vt7tzzOWk3DtCxs8sZmU9d1NE1box/KDgldkbBsEIj6cG+j6yrMFYgvFsmJAuFdOu/6HAqJusZKqkL2Spauli7cHjsr2Y0BwABYYKkmTt/WOvVyWeX86QQNVadnIERvX4hqusrcq3PNPL2rjaplWqTKDeL5KmZ0iKwUYi5Tpc3nwKaI2Nw2/N1+lk8v44q4aL0mQsftd3DXJwb46V+PsTyXJ9jqwW6TqTc0cvEydtPi7keGsDkltIpGo9LAGXaiOBWcTidjF8eoVqq8+JOX8Q7fQVnxo+ZqJAp1bJJIW8CBqIgIpoUogHG1KLTU3RmomJaFzSYR7A2y9/N7cUVcSIp0O07hI4TXplK8/uos+nQegJ9mk6iym4/t2R0zCWBmZmYtwmszzM/Pc/aV1/C72ygeaMNarmAvNjCcEsVuL+GRCPcMhDFMix+9v8jpF6cxlkqIhokui7zb4+eeRwfpddsprJQJ9wWxuRRKxSJqXsHv8SLbJAxRxKzp1NQqXsGOwy3TE3LdUJN3eSzJKz8cJ7OSp2f448jKzl4WkXYfyakslulbG9ZW8zVig2Hs9q2HcWffj/PK9y9TWC4hSiKxwRCf+uUDtLVvTi91uVy0tbWxtLTE0NDQDe+6nhiMEHTb+OmpcfyBEMMdAQ51BdZR4lchyzKGYXD48GFM0+QHr5zE7L6Tlpki1kIRQZKoDQc45VQ40BnAf81affjw4bX/Tmo5aHFRiJcxEZBHQjfcdH/UcdNd2O/+7u/y6quvcuHChU0fP3HixJqO7zZ2hmFanDm/jLPQQG7ffBItee2401USE1kW765taqEP8JOf/ITFxUVOnDgBwNxyie/+t/cpTKTxhVwEegIIIqjFBuZCgRVR4WKnje5eP4oJNUVEDzjY0+Hjsf1t67jO2ZJKQzeQ7DIYJlh8ENRrwXIiQUusZY0fbxkmQ3tbMFpMZJvM/ce3d0eqJCvkinVMm7itTfwq3DaZvKyRTleoJCsMHIxx/t0lqnV9W/pmJVVlsMdP+LodyctjSV78n+cxGgbusJNSqsJL376A22Pn0NHm7uhsqswz3z5P/WKa1lYPkt++tvh6TAujoJI9vcL36zr2rx6hr2WjZfC+dh9L+TAnTfA4ZdzJChSbmYrWUJBUyIG7xc29HXZstcy61z752JNkujOEBkPE8zVemUjRflXjFHApxHM1Kg2dEDZEm4hZ1xnZG+GNk4v4NnG3uxZmRaMiQE9fiM6eAGJfkLajbWjVZhyI3Wff1S7PbfxsolFpsDiWJAUE3PZ11zaAyyZRkkUSpTqZyQymYbJyboXSUgnTNJuRJAKYmsnF9y8SvxIn6oky8pk7eW8uh6qbtAVc63SdPqeCppskS3Ukr427nx7EodvYOxxB3uYaVxQRgWYm07VaHMs0qVara1QeAEO36DzQQt+JEC0xN95tdoLKyTIlzcAQrF2tMdAcKimyRK5hUJwvIpwQ2HNHBzPjaWp1bQN9M1+s41QkRo5szl6oVBr8z/9yisXzK1iWhTvs4omvHSFQ06nn62sB9Zpucnohx1ymhm6aKK4gc9kaM+kqYa+NO3pCBN02FJeCzWsjeSFJdG903TV618cHEESBUy9Nk5pIgygiSSItbV7uemSAI/f1sLi4iKmbmIbZzPK8iv0H9nPHnXdQM0RemkgTdTd1jEu5KoW6RhsO7D47Bb1A2G1jPlfFjtV0U9kBtYaBU5FxGxaKR8ERcNzSyJvb+IdHslTnnXeXsF1IE7h6rdXGMpx0yOxt89Lm31l/2Wg0UBRlW3piLpdj9sokom2Bvfd8kkmvA9PRgqKIHI55uaO36Rz+7lSKd58Zx5up4Yq5ER0yZlWnPJXn9eplbJ/di8Njo7BSRnRbNMoGHX1Bgl4H+/ZGeWMqR2Mmg2xaFPzgHI0y3Lp7M49Ussyz3zxDKVnG7nEw83aKn4Qn+PQX9m/7uuP39TB/MUniUgpHwIFaaqA4ZO54sG/L85KIF/nx/zxHraAS6vRhGCbz55b5oWnxG//yXqQt1r6PfexjvPrqq9x///27/l6rkESBobAdz8EofX19Oz7//vvvX/ucN6+k+dHJeYRUFSnswig3cGXqZLp0cpXGukbvWhzoCpD/3D7OnkkgyQL33N1Nf/TGYho+6rjpRu/555/na1/72paPf+ELX+Cb3/zm7UZvl1B1g2pRRTEtxG0aFMWhYJZUKo31lEPLtDA0A1EWURSF5eVl3nrrLTq7u3n+e2MUJzK0DkcQrykAHGEnPSEHrsks6ekc+Tuga7CTdo+N/e1+hlo86zjj751L8JP/dRG9rtM5HMLmkCmV1bWiamVlmWAguGYYUq9ryAL07wnjjRm0t7eTS6fWufNdD72uo8GGAnRLCM0iTDctTM1kYF8LsU4fC4sFHP2hTQ1Z8sU6imowelfXhqZl7P0l1KK6xoV3B50kLqW4eHqJQ0fbsSyLV9+YozqWJtbjbwbfX3s4YtNVM2KXWLmc5ZXXZ+n5zL4NxyFLIo+MxvA7Fc4s5BkX6gQHYlgi2BwK/SEn9wxECEoNJidX1r3W4XZw1YAUuyyiSCIVtemQWWsYyJK4NkEzdRPJLrG3N8S7/UEqYxm8HhviJlNAyzCpr5QxW1wc2R9bO2ab23Z79+4jAr2mUy6qqLKAb4uG3WmTqFQaLL6zRGY8g6iIeDu9SIpETTMxLQunLCLOishjMnannQuvz1FtcdG2BV1SkUViPiezyRzd4XaUeoPcVG7THLlisc7J1+eRTROX10YhVyO4mktnQbFUwu/7YEBjmmCUVKKHgzzw0CipVArTNLcsgPSajiZww7oWSRRoAHpDxzItDtzRwcTpBFMXlqnGPHiDTkzTpJipYWZrHL6/h54tHPgunEmwdD5JZCCIYpNZnsjw9nPjHG/1N/WNV3FuscBUskzYY1+3FpumxUqxzrszWe4biuKyS7iiLgqzBQoLBUIDH9BdBVHgro8PcPDOTt557RJeTxCXR2FgT3Qt/kEQBARJQBRFLNNae2043Gw4Jc3AbZPIVjScsokkCjivHo+nxUPWkcUvgiyK6KaOfRfrd77WoDfkRq5ohI+336ZpfgSRKqlUUxUihonSdfVanyuQSlVJldRdNXrT09PrjDmuRa1hUKprJFIZdF3n049/jDvvPMzrb51kz4F+7LK01iBohsn778dRUlU8fQGEq79fyS/htUnUlwpMzxe479N7ePGvzlGMV2gfjPHw50YBeGCoBf0Ji9de0fC5/AgOnSce7NvUgXIrxBeLlFYqxPZEESWBtGEyP57e8XUDQxGe/ifHOPnqLKm5ArHeAEfv610bPm+G8YsrlJMV2va1rN3LI70Blq9kmZnJMji0fm2yLItLyyXOLqh0PvLrvHA5w7Ge4KY+CtshkUgQbWmhXFZxOpQtG8rr4bHLCHYZfHb0RBlBFGh0eXEqEp5tamJJFHhofyv37W25qt+88WigjzpuutGLx+PbZle0t7eztLR0s2//CwdJFBAVEU3Ynl5n6SaCJCFf/THX83WmziaYPJ2gXtFQbBLJWpy+9n6++NVfYnK+yPKlFME277ombxWCIBAdCFI8vcB+dye/+rGtM63eemUGdbGIzaWwNJkh2hNkcTKNYyhCsZjD6XThdDcXPcMwSc8V6OwOYPPW6O0dRpIklpeXtz8PioRI04Bm17BAvFqoOHwOHv7sXn74F2eJT2TwtHvxXTVzqTd08ukqQk7l6IluDt6zUehfV/UNJgKiLFGvNhvrpXyNhQsr+BzKhiZv3WtcCj6nzOKFFRYf7KN7E8qqIomcGIywt8XB93JXOHB0FFEQiHrtdAaats2rFOhr4WlrTqsMzSDktrEn5uXycoliTUMUBYZaPEQ9NkzdxDIsfB0+QgEndz7Yxxu5OswXcIecSAF7M3jWtDBLKvV0jZzXxoGP9THcettk5aMIQRSQFRHRAt00NzU10E0LOVkjl64TengAxWdnMV9jLlMhW2lczbATqWUFunr78bQFiI8lcRsWVtiFsEUDKUsCNklgNlOlNeolM5mh7VjbBvv8t16Z5bVvn8fT4qatw8uVy2m8AQeyJFIsFvF5fR+wCIBsuozbY6N7yIvD4aCtrY1EIrHl/UlSJCSrWdjcCCzLQrIERElEEAWcXjtP//oRXvnBZSbOLpOfzCAIAv6Qi/1PjnDf40NbUhC1hoGJhWKTEUQBxS5RXS5TlRWCA039daGqMZ+tEnDZNhg8iKJAzOcknq+ylK8xFPMgKRKCKJCZzKxr9FZRa1S444GhTbNvRVFEtIlIDgm9qm/YWbMrEkd7QpxfzKPqJvtbAnRddcO0++x4Wj0YcwXCLhvLqSrOHYqtXKWBXZLo8tgRqxqhwdvuvR9FOBQJyS5jWGCqTc2uYVkIdmltULAVxsfHGRsb22Crv4rZdIW/fXGKQqqMJlh88R//cw72taHrOm6HQot3fYNSquvkk1WckrTW5K1CdMo4EEjFi3zy7iHufjLEwOB9RKMevFe1cU6bxIluF72PDxKMxCgml+gJ3dgA1OO1ITtliqkK7oADtdzAvwstK8DwSAvDN6AvazRMEIR1Q2bZJmPqJg11ozfB+aUCPzgbx0pVcWgWJ6sJ5rIVvni864aC5k+/Gyc5M08xVcUbdnH0gV7uOtGz4+sGoh66WrzM1XSCYSe6KFAKObi7zberz7/eWfg2PsBNN3rhcJjx8fEtH7906dI6as1tbA+7LNE3EOLsyTiegoq8ST6ZpZtU6zqeLh9tfiep6Rw/+tY55mdyqHYRwSZhagZS2YFND5K4lGEmXsAqqRBxsLiwSGdHB1x3ExYkEcHSSc5V0Q1zywvG7bWTNC3Mioa3zcMjX9zPj/7iLNNnFxG8Iu39flTNoJpTqSTLRFs9fOLzoxhKcc0Bz+12U6lUNi02AOx+O167jGUYGyhbm6Ghm0gWeF0K9qsLct+hNp5WJN58bpL5KxlWFopYgoUsCESibvY/2cO9T+zZNCy9dzjK5FuLVPJ13AEHtZKKrur0jzTFwPNLRbTlCo4tDCSuhTPiJL9SZn6xuGmjt4p8Osmdwx3s791Y7KxqLa+Fv8uPu9VNOVHG3+1nf7uPqNdOtaHjVCRafQ4EUaAUL+FucePv9iMIzakXwMlXZqhM53Fka0hicze4pojQ6+PQiR4eO965Kaf+Nn7+4Qg4aO0JEFhqmuzEfOv/nk2zqf1oS1UJ3NWB7LPz/nyOK6kyoijidciIAtQ1E1UMsFSsEPQ3MHw2xIJKPV7E2bu1UZTbJlGoaTTsEkKmSqPc2NDo+QIOHH4bLp+de58YplxQWZnMYIsohALeNZq0aVpkVsqQVxl5sI2Rw02q0Kr2Y8tzEHSwWgKaJjtmvq2irhl0CQLOyAdrszfk4lNfO8KJ5RLJ5TKiKNDW5cezw5S/ZyCMN+pm+XIK2aGgqzpdQ62gW2sGRysllZqmE/Js/l6i2Cw+5zIVhmLN4Y/iUahla5s+v1KpEApt3lDJsoxgEwgPhlk6tYRzE3p3zGenZW8LlsWGddnf7ae8UiZaaqBFXKQk8NU0PHZl3flVdYN8RUOWBA61+bHn6oT3t6zpEW/jo4WuoIue0ShTy2V8iQpgUQo66N0Xozu8/TVSr9c5c+YMNpuNUCi05uwOzfv+8y9Pk35tHp8FmgBvtqYZ6YqRTiY3NQKURAFBFjA3GfBYloVpWgiCxdiFCzz8yIlNd/wXFxfZv79Js4x5+5icnGRkZGTX52NgKMLRTwxw5qfTZObytPQHefCx4V2//kbQ2eNHdsiUMs2GCyC7WMQXddHVs36N1g2Td2eziMsVfJczWHUDV9jJsihwMV7gweHdNZhn3lvi1HMLOGxOXEEnmYUCL/zlWSRZ4Phd2zvouu0yTx9u522fnel0BYckcne7jzs2qYtu48Zw043eY489xje+8Q2+8pWvcOTIkXWPvf/++/zJn/wJv/RLv/ShD/AXCQf6w1zoDVAdz+Cxy4juD6aqlmGiLhYpB+zcfSiGUtV45ltnuTKbx9/nJ3KNRqTWMJifz/P8t87hHQiiGwaXxi9Rq1YJh0Jru27XQpDAbBjNaf4WNf7jT47wgiKRSed59Il99PaH+Njne9G/X0DLOShdyYMAdo+N4w/0cvyhPmpmdl0uV0tLCzMzM1vytwM9ATo6/cxcyVCoaQR3oAzmqhrBhkHngdA6Glj3aAudwxESV7LEFwqYhonba6d/JIprm5DvY3d2MjeRYerUEoWlApJNZuCuTu64t7lINVQdDHNH63AAZBHRsNC0rYtOaO6Oj46OcuqdeZYXSzz8xPBa9tdmjZ5kk+i6u4srP75CYaGAt927jl6xap6BCZ13d641tIok8omDbeztDjA2m+XypTSaqiMpIkMDIQ4ORugJuW5Pxj7CEESB2L4Wui8mKVVVkkUIuhQUSaSmGWQrKv5MHZ9LIbInwuXlIpPJMhGPbc1wCJqTepfHQUYvsZitIQgCDUEnc2aS/rajTQ3vdTANo/k5FpiAaFiY+kYt110nugmEFBxOgb7+Vly/buM7/+1tcvEay2kN0aE09cE1DU/QyfGnR+jaa1uXAxoIBMjlcgSDG5tOf7eftpiHuZUixbpGYJud+VXUGgY2BKIeO+Gh8LrHBEEg2OYjeNXOWy2qlFfKWIaFqIjYPLYNO2Rd3X4+/Y+O8PZPp6lVGgzsb2E45GLpjcW15+iGibiDGYIiiai6uTYUEyURUzOxTGtTh13DMHnzlVlCURf7DnxAoV9dZ4KDQZbPLqNVNZRNzkszw2rjcbgiLlr2tTD94jRDA0HaegLMZ6skCtW13CvTNFEkiZjPQX/QiTNbJzQQoueBnl25Ad/Gzx9sssinjnXyslNhejoLwP6+IA+Ntm47TDRMC1lRqFQqfOpTn2LPnj0kEgna2pqa15pmkEsWcRsWrsEgxlSOYrZKTTNYWVlZa8auhc8h0z0QYvzsCu6iinSNi6WRrVOVBfxKgRP3PLRpk1cqldatMZIkYZrmtjTxzfD4Z0ZJFyYwTIHPf/7etR3DW409e1vYf38PF16dpbhcBtPCFXJy4lMjGzTMVc0gX9Nw1w0s1UDp9qEvlHCoBqmSuuvPfPe1aUzdonX/1azUqIvliTSnX5/fsdEDiHjsPHmwnbpmIIvC7VrkFuGmG72vf/3rPP/889x555089dRT7Nu3D4ALFy7wgx/8gJaWFr7+9a/fsgP9RUBf2M29jwzwWl2jsVDCIwpITgVLM6nWNcpeG70P9XJiT4zJtxZYmMkT6A/gvq6octokWnqDJCazrBQzXJmeQ/RpSLJEsVjc0OhpDQ3BFLF57di2ubBaI26++pXDzM/P090dpVQqEU/P8du//xTlXJ30ShnLsghF3ARjHkzTZGoqteb8uQpRFLdcHG0eG+37W+icyTKualRkccP3W0W+qiFoBp1OG7F9LRtoUqIs0rEnQscWOpnNYLfLfPHXjjB1oovpuTymQ2JwOIJ8VdNms8vNfDDNhJ2o67qJKQkoO1BUstkswWCQUy9foJKtsudAjKHh5jHLsoymbbQKDvYH6X+kn4U3F8hN5Zq0K5uE2TDRGzquiIvue7s3LUo7gy46gy4+frCdhmEiiyK22y53vzAIDYU49GAf1kvTzJbqZBtNYxKbYdGmWgRMgdbDrZh2mZl0BY9DXtfkrUKQBLwOmYRuojZUqvUiUt0iP58kPLRRO1Kr15FtdiTNRLKspmvvJlEooiiyd18HCwsLADhD8Eu/dYTcisn46TjlvIoiS7QPh9h/tB3s2gZql8/nY35+ftNGzxVx0TIUJpYoMVHXcNukbU1ZTNMiU1HpNAXaugP4uzY66a4OVzKTGXLTOfRaU8cnSAI2t43I3gjB/iDuFveak92e0Rb2jH4wKU+cTqyjpMqSuOnuw7XQDLNJI1/d5TRMFLuyoXFapanm8zUun47j8tg2bfR8bT78PX6yU1lCg6EbasBkp0zroVZsPhv+ik5HyE1BFqkazUbUJov4BQF7RUMoNgiPRul9sPe2/vcjjrDHzufv7CZ/lVHidypbujnqhskb40nOnlmmWi1z4tO/wh13HOH5559nYmKCz372s5RKJQzTwh9zsmIXMSezVBwS/V0BPPb1buPXQhAE7jzcztylFOmxNL68iuJW0CoN8qpOvUPkq48cW/MYuB5zc3Nrde4qurq6WFhYoKdnZ2riKorFItOzZ+no6Pg7a/IAJEnkqS8eYGh/jMWZHBom/XtaGN2kHpItA6NWpOGQ8NkltLkCYsRF3S4RvQHtbHIugy+43gjF7rVTTFcxd2HQtIrdZhHexu5w041ee3s7p06d4vd///d55pln+Ju/+RugeYP9yle+wh/+4R9uyHC7je0higIP7Y3hcymcPB0nNZWFkoblUfB0RbnrQIz79rXiVSQm34+j2kWiWzRBNlnE8imI5Qb+qB+PQ6auqKiNxobnlgslLNnG6KHWHamS0HS4isVinDx5kvvvvx9RFPGFXfiuo2I0G8KNU5yOjg6Wlpbo6ura8BhA6+FWDiTKaCcXmK00KNV1/E5lbQJY1XRKNQ3FgGFE9t/bTeQGmrmdYAGLlsWFQo3KZJX3xtO0DYV47HAHbodGyabiSzf59tuhlq6hxNx0d25NYa7VajidTgRB4NEv7CO9UqH/mgB3URQ31RKZpoUZcxN6pA9vooKxXEav6chOmUBPAH+Pf8cCSpbE2xOzX0CIkkjXfV04I076LiRJzOZpaAYej41ou5fsZLPIj5dUyqpOq2/zHXDBIYMg4JagYkLA70fQq+jVzTOMLNOiqJq0+uwoNR3J71ijW2+FcrmMqqq0dbXR1gWjm7j2zs7O0tvbu/b/RsOglq2hZTXKnjKe0PrCQxAEYgdjDM7mqS7kWSzWCXnsuDbRFqqaQbqsEkVgKOii7VjbBgOnSqrC/GvzFOYLWKaFK+LCGXIiiAKmYaIWVRbeWGD5/WWCg0G67+teuzZTqQrvvDpDMV/DpYO/rjedLwUBRa8iY64ZLV0P02zSSQ90BNb+TCtr+DZZb3K5HKFQCLfbzf2PDePzrz/viqJQrVYRRIHuE900Sg3yM3kCfYFdNXu1TA21qDLy2RH83X5y0znSl9KIqQqBNXMXA8WlEBiJENkTwdflQ7pd0P3CIODauaE/t5Dn5b++iD1ewRAsLtdE/u3Z/0Jh6QqSJKGqKqOjTXOU1h6VH/k8pBIlhtq8fPLuHgSsbSMB+qMenv7CAV56bYbk5QxmqY4QsiFGFL74yF4G28Obvq5Wq+FwODa8t9frZW5u7gbOArz00kuoqsrKygrLy8vbmtN9WEiySPdwmFnTYHIyzeUzS1zMV3lwNEbM56BcLjM3N4eiKHziYB/PXUySkwQcmknJJdPa5mVf+8bB1lbwtjrIXK6sMQos06KWrdF/Z+fPRKj7Lyo+VMhdW1sbf/7nf45lWaRSKQCi0d1no9zGRoiiwPG+MAe6Aixkq1QbBpIo0O53rtEYG5UGlXIDaQcrasWp4BO83Hm4m7lTcfp7AsjX6WEswyR9JUd0XwcHD69vzLWqRi1ba06JnQquiItcPsef/dmfsXfvXr761a9uOf2yLIt6vY7D0dz2MnWTwnyB/GyeRqlBppzBe8KLr8u3IXTb5rYx+OgAkiLiO51gudogq5nkJQEsC4dh0WlAm9/Bnjs76b6/e9eW/3pdR6/rCKKAzWPbtIg5v1Tg9TdmcV/KErnqMjg5scKF8xcZcWQIdrdRuqjiqmpbGrKYVY1iTadnf4zO4NZahGvpKP0DYfoHNr/RrMKyLC4vlzizmGcxW0M3TGyKyFC7hyNdwU0zD2/jNq6HKIm07GshMhJhIFPD1JvZeHpDp7JcQZRFdMNEYGsNmxSwI3pt2EoqbocdFBF/wIbi2VxvpRomJiY9YTdqukb3/d2b2ulbloWqqqiqSiaT2XZaXiwW17TgRsNg5cIK6bE01WwVTJhWpxm+Z5iWAy04gx80rL4OH0Of6Mf68TTKQp7likauouKwychCU8dT1XQUBNoMgT1BF8MP9RIZWT9QKiVKTL8wTSVdwd/tR7bLaLpJtqZhmBaKJBIIuXBH3TTKDVbOrqBVNPof6afS0Pmrb7zLymQa2S6jVzVCukWdGr42D92tYbKawvhKsZn5uYnrZsRtp+NqtIqhGVimtWEXH9br80b2bdTbXMsccEVc9H+in+mfTJO+nMFwiMgeO8GQc4ODnl7XKa+UwYSe+3uIHYwhCALOY05a9rdQTpTR683dTVERcQQcuCLbB1/fxi8u4okSrFTxt3qw6jrJuQIP/aNHCZoFUqnUut9N1GvnVx8ZpqGba4yUZDJJJLL90Hek3Uf/5w8wlylTqWucP/Me9x4cZqBv88EzNHP8ttLiBQIB8vk8gUBgV9/x6aefZmhoiJ6eni3rp1sF07R4/kycCy9M4U0185PHLiRZSqS5r1chGvQxOjqKIAhYloXTbuPMYp58pcFo2M2xnuANGbGMHo1wJiewNJbE4bahVjQ8YSd3PdQLsPY5t6//v1/ckjRzQRA2Fb/exs3DLksMtmyRp6dI2O0SVnZ77ZfeMHA4ZB7/9AjfB5beX8Yli7giTiRRpFqsUy2oWGEbj3/5EC1XCwa1qJK8mCR5MUk6VcHQLdxeG639IZYaS1QrVS5fvsylS5e44447Nv3sqckZSnkb772zQEvEjTqZJXWpOQyQHTJqTuX9uffpOd5Dz4M9lGsaqWQFh12mo8uH3Wdn8LFBonujpC+nmTg3zztvvce9996D22OnYyRCeDiMv8u/q3DdYrzE5dMJpk4nKFdUZEmitcfP/rs66Rj5IHfKNE2effsi4ngJBxZlj05mJY0138Dyd3Pv008RcLv4q784TepimnDMgxSwY1msTeKtcoPschnn3jAP3te77S7pqj5vN7Asizem0rw8kcRK1fAXVFyqgeaSOb1cYipZ5okDbYzcQK7PbfxiQ5RE3C0fGAVVM1UEqTmJFa/elNdlZV4DQRJRWt2Y6QrBoBObTaKYK+EXYF1pYEG5rpGvGezvDhCxwHArBPubtEpTN2mUG019r9dOPBHnhz/8IR0dHTz55JPbHn82m6W3txejYTDz0xlWzq9g99vxdTSDhSvTFRbfWaSwWGDwk4O4rmEdhIfC7LVJeN9eJDGdI1mqM5cpYCoKDsVGqyAQdSq0DQbovKODyN7IugKllqsx85MZatkaocEQdc1kMl5kNlOhVNcxrf9/e/cZJPd5H3j++4+d8/SknpwwyAABAgwgRVGiqGRJ1q6DvPZ5d7233rVvy1v3Yreuaqvu9dVdXd292fOt09onr71OspUzRYoJJEHkOIPJoad7Oud/vBcNDDGcgAEFyRL0fKr4Bt3Tadj/eX7P8wsuqiwTC2gMdQQYjAeIjcXI384j6zJLrsvadI6e/Ukc16FSqpK9uE7PWpCRI+3F6pG+CI7rMperk68aaKqM7bi4rksiqHNyMI7/Tlp5PVsn0BXYNrX0ft5fCxzsDtLoDHD+5TlKUzlcw8bX4WNwf5KxfUlcx6VVaiGpEuHeMF1Hu9qpnvd8PqpHJToUfeDXIvz8iiX82EGdRrqK7booQxF6kgn2dQ/t+DP3lh2k02kmJu7f3ERXZV756l/T29tLXHUYHd758Q3DwHUgl2sQDnvw3Dldr7Us0uUmuj/GwuIs0WiUWs3gyoVVlmbzuC70DsU4fLRnS4qmLMs7NqR7mFbLTaavZYitNfB1Bqi2aqjzWdaCMr5TjzOUeu9aIUkS+3vC7O/5YOuHZrPJyGicsbER3n1tnsxyhURPiBNPD2xsYN+9zrw/tdawHAp1A6+m7DgvT/jgHkqgJ/xkKbrC4IFObt3Ob9rNupfjuLilFgNPddPdF+EL//wx3jqwyOW3lyktV3FdB0/Yw76nUnhDZQ7va3eVbOQb3P7ObW5eXmPFsSmpMg4u3qxBZ6bKzKW36O/p57GPPsbRo0e3fX23bmT46z+4hFVunxhqZYNOn8aRF0aZrzQpN1qEo35CrQqLby/z1ltLLJUbNMstFE2heyzO85+ZZGg4TmIiQWwsxg9u/4Bmap0TvzJJOBHetDt/P7PvrvCdv73C2loNy6+iBXSclsHCO0tcfXeFx54a4JnPH0AP6Pz+7/+//OBqmf25GEXZRY14SfZ04g9IrIcjOJLKYEeAz/7qYb7+9zdYPLuI/XYVu9QC28WVgO4A8dN9fPZXj2w7LP1ed+vz9mJmvcYrU1kCC1X02wWwHNAV1KZFZ1CnaMJ3FJnusHdPaTKC8H76neHbrUqLRFDHq6vUjO1TBwHUpB8z7KHHdOjtDTOVb1BxHMrFOpqi4AKGZSPZFkcGEuyP+mlm6gw8M4A36iVzJcPSxTSZlQqyItE7FGPVXCKTyZDJZDhz5syOO+WVSoXpGxXefe0ixnIZb7bB+KkUiqc9781xXOL9SVqNOtXVKotvLDLxqYlNwUh0MEo4FaZvqcz6zXXSX/wH+vv6Gd+Xwh/30zHZ0T6p26ZDb+ZShspqhcREglrL5u25PCvFBgGPSiLYHkZv2g6FuklmNk+20uLEQIzYSIwv/cGXiB49gGVb1Jt1dFUjnozRijWQPCqVlQqh3hCaKnNyMMZQIsBKsU6lZaOr7dEKPWEv+p0aR7NuYlQM+p7o25LdsJcxEu/fYT/72jzf+/J1LFyCj/fglprUZktce3MJs2Vz8LFeuh/rJjGWINgT3JKVIQgfxGNDcZY/OcbtS2lkVeH0E32M3edv6L1ardZGFtFuDMNgdXWVy5cvc+zYsV3v++W/e5uVGw1qxVl8YQ/Hnx0iPB7nlfMrFJcrqLqCJ2bjyKuHskqtAABHHUlEQVS8/g83yd7O39kYk7jy0hznhmb5hd84ytDwT757ZMOwaVYNpHqNVsshFAzhTchYaDTu0yTuQaXTaXp6evD5fDu+V03TtgR611fLvHJplXy6gu7R2H8gyYcnu/DtMUtLuD8R6P2Mmnysl4uvL7C6VKKrP4qqvPeH2nFc0qsVYj6Ng4/3IUkSYb/OR58b5czTQ2QKDWzb4d23X2Ph9ksMDQ1RqVQI+APMvTzHpQurTKvtgDLiU1FlmXrLYqZhEt5/jJNdUTqikW3TDirlFn/3h2dp5ky6x5NIlkPhtUUWMzXW3lrEiHjwaAoLuTpR3cE7V2LtZgHfiW7i/RGMpsXCxTR/n6vzhd85TVd3iCtXrrCwvoATdLgyf4WPjH1kz59T+tY63/yLS6w2DJJj8U1NJZzOELlinbMvz6GqMqtc4/d///+hkTxAMPw4TyYGCPckcBoWTcVCj3g30hgmeiJM9UdYfWuJuuvixtoz6SRFRtMV3EyN9akc+1I7767vlPe/k6srJcz1BtG5IpJfQ7mnzsZMV4kslFgLa0xlqhstiV3XxWpayIq85/RW4eeX6lXpmOxg4bUFOpIBUlEfU2sVfJqy7cl0yXDwjcfpabm0lsoMdwSJHE+RMSxqhoUsSazOTiPVC7iNDuqpIfqf6Kf7eDcLry7w9vduM181qOsyEi6h+QLV5Wn2ndnHwRcO7rpo++rfXWT69Ry2YeHcLuLVFHypECVdZjpTxXZcuiNeRoI2nak42Vs5ZsqXMBSJrlSYx58cQFFkZFUmOhTldu42zX1NGj0Njv760V0zBVrlFrmpHIFkANuFc/MFVkoNeu/Mv7xLV2WSIQ+G5TCTreJRZeoL17h+6zq+XIHk2AkwFLSAh/xyBTXhY/j5IdyZ0kawJ8kSHsfFzdSpzhexbQc7GUAb76C3L4xRMygvluk50UPnNmmZd+vz9qrRMDn77Wkcx6V735000K4goYkOcgsllnB48ROjJDraC3DHcigvlTEbJqpXJdQT2lOWhSC8n09X+PyZYfLHU8iyRCKg7/nv424bGrlcjfNnl8ksl4h2+PGEqszOzvLhD3+YZDK548+989YC576xQCAQIJDwUy80+c5/u4Q1EiFcMIibDrYL2ZDKH3znVRItme59Hah3/tbapk3mVo6vfvEi/+J/fprAj7nxUL1e59y5czzzzDNUKhUunn2VquXgCwcINRUky8BwXLSEj+QDpGTuxd1+A7u5myJ+937LxQZf++EszbdXCdQtbEXizbkisizx8UM9D/X1/TwTgd7PqFh/hI98/iDf+esrZKfWkaJeNJ+G2bJxCg1iPo3nPr2P3v2b89W9msLAnR2ypViQH87Ps76+zqFDh+j0dLJ8M8eCAn6PSvieI/SQTyPgUVkpwkKuhjYN7pmtudbXr6yRXy4zfLQPRVOwsnV8HpWa41CYLXDguWEUWcKyXZZWSjjzecI+jXDIg+pV0bwqvpCHlWsZ3j27yCc+e4DR/lF+6dO/RLVeZWT/yJ4/I9dxufDDeTKlJt1jiU3BMLRrj5JxP2nb5a1XbvP3r/9nADqkKvJ4kJIcQC62MGVoDEd47FjPRj3MO2cXePfrt4jF/Awf6MK+01FKkWVkGXILJX7wt1cJhr0cfWz7pkSrq6t7bljUNG3m1muE6xZO3UTv3Jz2oSb9WMtVvGWD29l2oFdaKLF2eY3qahVJkYiPxuk62oU3cv8dT+HnV2wkRvp8mla5xaHeCLWWxUqxQcinEbozF61l2hTrJooscXwsQX/Qw1zDRA/qeMstBlxwXXAdB6UG88tlONrBxCcmSB5IUpgpcP7lOW6YFp64l06fhuO6FOsmmjZB1IrQE+6hXC5vG+ytrRWYfmcdLaDT0RuiUTSolJtcPreMORrDqyl4VJmlQp2Y5iUu2bz72iI520KK+5BVmVrV4CMfn8A2bEoLJepTdfqlfgKtAKVsiVjPziftxfkijUKDxESC1XKTdLlJV9i7Y5q2rspEfDo3l/NMvfoG4c4Acr3O5PEe5qaKVNdq6CGdJz45weOfmGD9xjrzr8yTn8pTNm0uvLNMPV9H9apIskx5tcrS9XWGR+OMjMbpPdnLwNMD2wZYu83P287s7RzF1QqJweiW26K9YdZuZpm+mSPREaSyWmHxtUXKS2Uc20FWZEK9Ifqf7t807kYQ9kpVZDrDD/43aqdxKmvpCn/1X95hfSaP4lGwDBs1JPNvf/t/4eSpMW7cuLHjY/7w21fwqB46R9vfn0DUy9TlNWpvLpMajqMPRsB08F5ao5EtE3pqaCPIg3aZTXI8QXY6z7VLaR5/8v4jBvaqWDe4la7gOA4jXSE6Ajr/6T/9J+bvrOkCgQAff/5Zeg80+P7Ls6xPF8B2YSLGiVN929bz27bDzesZlhdK6LrC5KEuurof3ozL96eIT69VKF/N0mU4qEMRnJqJu1rj2vUMR7o89HTENtaYDcNmPl/DtF26w94H6gb6804Eej/Dxk/3EYp5uXJ2idnrGeplA49HYfDxPg4/2U/qQOemZiOu61Jfr1Nfr+OYDnE7TmGuwC/+3i8yPj7O3MtzZCtNGpJLyrs1T1qWJcI+jbWGSWyxQiPf2FTzApBeXUfXtPe6qd3ZZVN0FadhbsyFkiUJo2IgtRw8UXXjfgCyIuEJ6My+s8Itn4fbl9OspdcJRwIkrToVqbJpwK5lO5Tv1MQEdHXjyL+8WuH29Sxy3LclyLtXIuYjt17nf/tf/zPHXxymVqtRtFQur9RYSpcJeDSeHY5xYjCOLEvYlsM7P5jFdSCeCt35bDYvsBIDEVZvrPPOK7McPta9bcepB6nPsx0XxwXJcbYMvId2vRSui+xAOpPlD/+Pb3PAcwDZlvHFfTi2w9KbS1TTVcY+MYZHXCSFHQQ6A3Qc6GDl7RWiQ1FOjySYzlSZz9VYKzdwac9v6454GesM0hPxUpovkXo8RWIiwfr1dWbeXqZeauICLVtG9/l59nPPbpxQZW+us1hsoEQ9G02mFCSSIQ+rtsPCcokDK3Xc3u136RfmV1FR8Yb0dg0hoHg1WnUL1wVNac9gcl3w+gMsLa5TXCsTOthJeCLB+lyRq28u8di+JItvLFJNV1FchWQrSbgaZuarM3Qd7qL3ZO+2wVNpvoTmb48xWMjV4c5nspugR6XUUPmVf/HbTHSFWL+5ztDTQzQ/6aFaMYglfAzcGWTcMdmBJ+Jh6eIqr/7nt2mmq0S6Q0h3GlK5Po161WB2tsDEJ8cYfHbwoaVPGi0H13K3zQBQ1Pa1xzJtGoUG5/7qHLqtbzSisVoWlaUKM9+dYd8v7MO3y8xSQXiY0un0tl2+z74yR/Z2jp79nSia3M54up7l6jvrnDw1hs/n2/Y0ynEcqoUW+vtOvhS/ilu3QL7TXESRMOsmiithuSbvn7uk6gqu67K2XH5o77VYN/jv37zJ8utL4Lh0nOjh0ptf5NLZV/H5fPh8Pj72sY8B8MSIn2TIy+1sFdN2GOoIsK8rhPK+dYRtOfzDX17k6muL2C0L13V5uzPAi184wuFju29IN5tNPJ77ryneH+jVDRvFcJC8CpIsIftVlHWHWqXJX/3tlxjsivPZz36W1XKLr761QPrGOq7tEugJcubxPp4YSYjGLnsgAr2fcd0THXSNJ2gWmtiGjazJ+GK+LQFeca7I2rUMM1ez5HJ1DNtFxmXC+wTBlRiZKxkahQZN2g0Etmu+AODTFAqGTbPRwmpaW27XPBYen4dmtYU36EHyqaDJuIUm/t4Aa+UGAV2l2rIIeTVMZAzJJvC+Alyn2KS4XOFr+QZlj4IpKWi5JvNfuc7klQxP/OJ+fP1hrq6UubRUZL1qtLtG6QqHUhEO9oYxCg0q1RbBgd2bE2iqjONRKKTr7RSNQIBOYKI3vu3gzulb62TnisR6d9/pivQGSU/lmJstbNtNc6/1ebIso9+ZWZb1qARp1z5K97wmp24ieVVyVoPVV97C99ot0vE0n/pnn0L1eFAk8Ma85G/lyd3K0XtCjD4RtidJEgNPDWC3bNYurRHoDHA4FWa8M0ipYeK4Ll5VIebXsE2b4kxxIyi4+NIsq4U6uYhGK661NyWsIN5mjJe/dIO+1AoTT/eTWy7TkNvBz/v5dJWyY9AsNPH0bl08mKZJIhEg2OEnv1SmcyiKI0kYxQap4z14Yj5mMmWQZDqCHrrDHjJ5kGQZ+851zTJsqJrc/vZtHMtB7g6SrZvkHR1vVwCv5bLw6gKO5dD/dP/GYqLeMFAVGaNmbMwALDbMvc19kton/pVG+7opyzKO4TA6vn2XwFBPiNpCgXrEQ2woimTYuKbTDvY8ComYl/RiicV8ncd3CPL2Up/3fl3dQfSwTjXfINyxeSOvWW0hqWC5NW68doPLb1wmNBLi9MhpXNdtN2AZiZK7mSN3K0ffE30P/PyC8EHUarVNA83vWrqdxxv2oNz5vsqyRKDDT2auSL1hEIlEKBaLWwK92dlZxg+nuPbSIk4qjHynSZVbMVCGI7QkGWmhjGvb1AMaGi6utXV8FbT3sWVVptI0WS7USddsRm3nvptDO5nOVFl5c4mk4SKpEpm3ljl46iN89mPPUSwWN60rJElirDN431rHq1fSXP3hAoEOP8G4D8d2yUzl+MGXbzKxv3OjCc127u0evpv3B3qpmA+3049xu4i7VsNtWlT9Kv4wzL52lYVbMH17hvixXyD9g3liZQNZkqgvlnnZsOmL+UWn8T0Qgd4jQJKkHXdOXcdl5d0V3v3mNDPZGkVdwvUoKB4FBxd3cJjvv7VIz6U0vQEdyXSwd+kSaTkuigOqomwJBvP5PEeO95OZs7n99gr+hA9FU6jXWvgUicce72WmUsV2ZLrDPgZ7I5y7kqHhOCQC7wV6RqVFc7aI1BvA7vCTDOioSjvdM1czuDRfQPrqTfJHk9wqNvDWTEING8l1aWgyLxUaXF0u8fg2Q1N3+RS3XRRtt4Ar5OvYLRvPffLt/WEvxcUyxVwDRjff9iD1eZqm4To2R/qifDVdIdwZwFyqoCb9SB4Fp2Zi5xswEiXUF6N+o4Zkebi1fIvKX7cYe/JFAh6VE4Mx9LBOYaYgAj1hV4quMPzhYfSATvZ6ltzNHJ6wh6ivHbw5LYvCahVJlvAn/LQaBhevrrGoSTgBlYhPJ6G1rxGO41JuWkw3DVaXipS/2iAIyLaDaTv42PwdM20HvwSKt/3vjUYDj8ezcSq+srLC8MgAH/slH1//84uszRSQLJtIxMexkykCES8+t0koEqEr5CXoVWmpPhKTUdYdl9WrGTwhD0MxL3bLphDUuDS9Ts1onwYuVNYZ6vBzsDNA+kKa8ECEqeUSl84ukV8pI8sykYpBX2+IUE/ogYKpB917zqZruJqMd3D7zSpvqcXybGHHn3/Q+jyAnlSY4cPdXPvhHB6funGdM5om67NFBo91c+aZg1z67xcpNooU5gssZooMHDrFaGeQZMiDJ+yhOFsUgZ7wY5fP51leXqZWq217ezDiJTuz+Tti1k2CyQAeTcXrCZPJZDYFKq7r0mg0OPORfaRnyqxczaAHNMy6SbInxNGPDDO7XKawXELzaUSPdOJ9ewWn6WBZFqr63tK6VTNQNAUjqPFH37hJcamII8FseZZPnUh9oDRVaHdDlmTam/o2HDt+nA//CHVtS3NFLMMieGctKSsS8YEIhZUKiwtFxnbYkIL2id796vOuXLnCK6+8QqvV4vHHH+fMmTNMdIU4dKafqzK4mTpOyEfsYJLTE36ulw7S2dlJS/VzfTZPuGzg6Q8jqTIslsnOF1ks1EWgtwci0HvEpS+lOfvVW9yoNnGiHjr8Gto9qUiOA+WmyXS1RWU6T8B0kIZDmJaz6X53lRomoVqdqlrimy9/k8mDk4yNjXHlyhWCwSAHDhzg8/9DnJc6g0xdWMVu2aSeG6ZfUwg6EoprkurvBsOhVWox8tQA1xbzrNzIEOwIYjUs6rMFZI8CwzG67mlLrCoSXWEPy5bFm+dXcByLXk3FnivitmyQJLxAoDvA+ojDWUlC0xVqVWPHroEApuUgt2w6UnvLRd9ripRzZ1Dw++t2lpaWyGaze6rP+4P/8gdcOHuB73/9+/zib3yBkd4IM4ZNp1fByTRw8w1kn4YzEWe9O8CJvgihjz1LNVwhnorjj0V4d7H0Xrbng2/wCz+nFF1h4MwAnYc6Kc4XWb+2jlE32qMXVLndUn80TvryGu9eWmVOk4iFvVu6pcmyRNSvEfVpZKstrjdaDFcsYg2TxYCBX1c3Uqtbpo3ZsugOezl/+zy33ryFz+fjd3/3d5mammJmZob+/vYJ274DncT//VMsLxRxmibMFGmu1XE0hVREJ5kMYjUtCjMFvCEvz/zmPhxfnGqlRQionU8jJXxcnsnhAKk78y5bps1MtkYy6CVYN/jaH51jZrWMC/hjPizLZvFGlrUb6+DXifp1FvJ14D4bS247iA3e6eDpOi6KZ/eTQFnefgNq4yFtZ9dBxA9an3fXx//JAZr1FguXM9imjYSEpMik9if59K8cRlFlTMtk/4H99E704uh+ZnJ1Nu1biYwq4SfAsiz+8i//klarRT6f5/Of//ym24+c6mPh6hrZmQKBDh/NkoHZsDjyVP/GXEjnTo39XfPz8wwODhIKhfiVf3OK82cXySxViHcFOHa6j/6BKG9dvU2hFaO7s4OhuI8vOxLTby6StXP0DHYBUC83yc8VSR7s5OZaBfv8GommjY3L/FqDb6kSv/b08EYaZalhciNdJl81iAd1JrvD244bGE2G6D7Vx+obi2DZxB/rYbI/+iN9jooqb1kfWJaNrEjoe8lYuI/e3l7W19dpNpv097dnFno1hV843sdkb5S1chOfrjDWGaQz5OWpQ+3d8dVSg1t/e+VHfv6fZyLQe4Q1i02uvTzPrXIDOeqlY5sTKFmGqF/Dp8msmTZdN/MkchprkkRX2LsR7LkuFOoGiuvSq6ukpTzl6TJnPnSGZrPJN7/5TbxeL7Ztc/jwYT7zy4dp/MIkpmkTDOqYNZPcVI7it4ooroQa8tB3uo8To1FGLq3xyjev4DRsPH6VvqNdLKxU0d/XNKRWq1EulljIFnHKOofnPdi2hJL0bQwudy0Ha7lChwyrY1G6e0NUZgpY0Z3r9PLFJpGgzuSxve2GdfeG8AR0qoUGwV3GPFRzDbwRL13vS/H8yle+wrlz5+jt7UXTNPbt27flZx3bYf3GOvF0HM+UBzWsot2wOTPhQR2JMxvxYHW1kG0X2yPjj/o41RvmI5NdeI+kuNG8QWmxRMCn89RoB7LcfkyjYpA6ndrT+xQEAG/US3e0m67DXdiGvTH8WtEUystlZi6vsahAdJsgbxOpPeR4zYWVmsmgR6VZNcnYNSTPnTpdwyZluEye6iV2cpKbX7zJyMgIqqpiGAYXL17k5s2bxONxent7SSYDJJPtxkTNQ02W3lwicyNDo9ggX8i357ylwqROpZBjMqZpEo/3kr6YZsZ2ybcsai2Tnuh7u8IeTUGRZZaLdRKlFtNvLuB5rJN493unakFHYv21RS69ucCh54eZx8Wy3V1rge+OqeiJeNudcNXNMwy3MzAc4y2PupEKfy/HdmlVDcaOdm/5Ocdx9ly74jgOf/5f/5zMUobO3k5+41/9BtGoj1/77VPcvJphca6I6zh090U4cKQb352FZ+pgCnPNJB6PI8kSyXD7Wui6Lq1yi+5jW1+XIDxsdwekd3R08OKLL265/djJFLWawTvfm6GWb+AJ6hz/yDBnPjy65b53VatVhoaGAOjpDdPziwe33EdtlfjY8eMbGy2f+7UjfFmGa2/MsHwlg4SL5tcYfTxF6ul+fviNKTrvNB3BcnCXKixO5Vg73ENPxMvV2wv88FaN9IU15IqBHdS58Hgvn39yaEvjkVhA55c/McGNo924jsNoT5ieyI9WDztxoJN3YzNkZwvE+8KYhkV+ocTg0R76dih/uXbtGrdv38bv9zM2NrZlPt694vE4k5OT3Lx5k8HBwY1/96jtcptDO3Qo7wx5SY3EmZ3OIy+WkSWoazL+4RgD4jRvT0Sg9wgrzhVZWCzS8Cuk7pNm6NEUfFEflZiPwYaN4rhkKy1sXGRZwnFcgkiMujKHnhhmNNHJt7/+XTxNL6ZpUqlUGBsbY3h4eOMxfT5tY1HgCXmI7Y8xHhhnZGgERVM26ghPPz1IZ8olFunE59eZe2mOla9e37Qh7DoOFy9cpFar0tSjRN04SraB1B/eCPIAJFVG7Q1irdUJ9oVo9YVIZOqsLRbp7ItsOqV0XcgVm8jZOoefGyZ6n0HDmUqTxXwD23HxDYTJX8sSiHg31UPe5dgu5XSV/c8M0tPb7j7XLDWprdVI6SnO5s4SnYxuuuC9915dFl9bZOWdFVKxFHghNZqiud6kvjrPM6f7ePLUIIuFOk3TIeRVGe4I0B1+LxW050QP9fU6+an8RjOWRq5BbCRGx8TOKRiCsBNJlrbMkytMF1jJ1XH8Cv49ju9IBHSypoVryZwcCJPONciXm8iSREfcx/iRboaeGaRRapFknNV3LL5XuoitlXBbLp/+p5/e9jTcG/Ey9uIY9EBEa3+XPSHPpjlvCwsL7RMulzvZTy4uEu+PiSQJVlbSTL+9RL1kE7cDwHvXByXuJZgKUl2tYuQbdIW8rJYa9ER823beNC2HQs1gsjtM2KdRWigRSoUI3afOd2IySWp/krl3V+gYjeENtBd8lmGTnSkQSYU4eqK9ceNYDqXFEvmpPKtTq7z9ztt4kh6e+OQTHH/2+PbXKcth9d1VvNNe6pfq+A77mPnuDKnTKTwhD4eP93L4+PaZB4mxBLkbOQozBcJ9YVSvitW0KC+VCXYFSUxsrUsWhIep3jCYmcpjW1G+8Ktf2HEI+dMfGubE6X4K+TrhiHfTmIOLFy/yzjvvcOHCBSKRCB0dHYyNjd33uW3b3nSaHg57+fV/fYqrT/Zy4+oSHR0d9A5EGR2L8+ZsHmwHV5baDVxkCRmwTIs//KM/xmNVmap76bIm6SqZqEEda7XG8muLvNMT4hOHt34HE0EPT0/sPBriQQ2PxHnu8wd4/Ru3yNwuIKsyfYc6+eQvH9oxa0DXdd5++228Xi+dnZ0cPnx4x8efupnFqQ8RcH289K0pjp/uIxq9f3CqyBIfO97LVy2blZvruJZLqCfIs6f66YuJQG8vRKD3iHIsh/kLq2Qsh7B3bzs9YZ9GpjcATZcjHSHylRZl08a2wYtEV8JP10ickutw8+UKxvIgf/S//5BgWKPDmeCzH/8sgeDOO9Srq6v0pnpRt0mj7O/rY3V1lWisn0hPkIiqkm6ZhO4EipIs8+STTzK/MM+VlQoRQ2rPwApuDWAlTQHbxWs4WP1hnvt8gte+epPMVA43qKP6NWzbxSk28CNx7MwgZz63f8eUTNN2ePlmhvNzBaqrFWTHoeFXqSky9rUMqYmOTS2VzabF+myBWCrI0x8ZwWpZLJ9dZv3mOs1SEztjE1gMcCZxhma6iXdo88llabFE+kKaQHeAlioz/tRzhGJR1K4ASt1i7d1VJocijExsnZl1V3QwyvinxslcyVBZriCrMgPPDNB5sBN9m89MEB6UUTNYvJYhJ7mEtunSuxNVkZBUhVzL4ti+DvZPtJtJIUEgGcAT8fDW129x/vUFihUVW5VYWZ1FMgxS/cfxG7tvyHhiHpL92y+AdF3HMAz0kA4yxDwaPl2h0njvWmPZLoZlc3ysnzdfXsYJyPj8m6+hsk9F7wriLpaoVw1OHO3hrVmXlWKDoFcj7FWR7wxMLzdMmqbDSDLI4VQE27QxGybJA8kt15xGw+TyhVWWZguomszoZJLP/NphvgwsX8+Sb5VAagei0b4IH/+Vw3T3hLCaFvOvzJO5lgEXfGEfrVoLo2CwHl5nUV2k78m+Lc+3/PYyi68v0j84zOWFWXpHh1k9v4pZNxn/5Piu8/C8US8jL4xsjFewzXYzsOhglP6n+vHtku0gCD+qcrnJX/3ROZavZfExznf/foFf/q0koR06Snu96sam670KhQJzc3P09fVh2zY/+MEPOHLkCF/4whd2DHBM09x2jjDAwcN9IJc4ePC9YLEv5sc7EKGRz8BCGdd2KAU1ekbinO75MN/6xteomj7GmjZa0o8S1JF8Kv5ik5mlEmwT6P0oHMdlpdSg2DAJezX67sz/fPKZIQ4e7WZxvojuURkZiW+kt25nYGCAer3O888/v2uQd+6tJb7zF5dolJoomsIP/vISty6m+cK/eZzwHmoUeyI+fuNDoywe7sGwHXoiPuI/5pmEjxIR6D2izIZJKdegqUl07VKfdi9FlnA9KrYCYy+MImsy1XQVx3I2dsbfeXmOt1+Zo+lXCIx1IcmQLTSQix1884uX+NRvHt9xTlu1WmVkZPs5ePd2Y4oORRkajLA+V6CgGUT9envHXZKIJHvpL2YJhBSUuBfXcrY8luu0h3g5soQstcdQpAYi3Hh3helLa9SqBqoi0X2sl4On+xg40LnlpOJeb9zO8eqFVUJTBZKFZru9uV9jKRWgnG2gTOVQZAlFV7ANC5BIDEb5+K8epq8vwuz3Z1m9sErRo7CMQz0c4chnPk1lvd31b/TFUaL3zKzKT+dxbAfHo/LmVJas6WU106TQynNmrAMzW6MwU9g0YmI74VSYcCqMbdjt1sViiLHwEFkNi2qlRVOViezxNO8ur6ZQcQ2MitH+f//OwbZjO7z+N1d59bu3acW9JMbj6Gp7TEKlbrCwUuFrX7zA5/7VSZIj9+9Y+35dXV0sLi7S29dLsDNIq9JiX1eIqytlqq06siRju067m1vIy3RXAkc1CYa2fte0niCuV8PKNQl5VZ4a7WBmvcp8rk6m0sJxXRRZIubXOJwKMpTwowCF20U6JjqIj26unavVDP7mT84xdyHdvn45LhdfmuWxj47y6//2FFM311mcKeA4LonOAIeO9RAI6Liuy+Ibi6QvpIkMRsCjUG5YOFEfp48fJhnuYOnNJVSvSu/J9xaMRtUgezWLFvFwodRAGTrK29kqj/dFyc/kKS+ViQ5Fd/08g11BJj83STVdxWyYaD6NQFfgoY16EISdvPP6AouX0iTH4uDA4sVVzr25yHMv3P807l7PPvssFy5coKOjg2w2Szwe5/Tp07vWvqbTabq6una8PRaLbZrr1x/zceapQV6TJbKrVSRNJjGe4IXjfQx1BBjsT/F//t2bmFcdnKqB7NdwaiaWDOGHvDHbsmy+eXmVq+dXMUottKDO5LFuPnG0F7+uEg57OXh497TrYt3g6kqJ5WKD5NEPM3n8iR3vaxgWb35rCqNhkTrY3py2DJvV6xnePbu059+XV1MY73p4M/1+nohA7xHlOu7GEO8HmjPi0l6g6Aodkx0k97+3Mz57fpV331jA6fKTuic3OuLXKUcMrl9M0//aPKc+ubXmbC8SiQS5XI5EIsHxF8ao/d01prNVVn0t0BUwbHx1myPdEdIDYRTbxZ4pIge0Te/RLjaRQzpFn8LReACvpuAdiPLUQJSTL4y162MUGT2k33dBUmmanF/IE5gt4sk10HqDoMjYhSb9DYe14z0c6A5CtkGzZuILaowd7OLA4S58Po3CbIHM1Qxpj8K1XA1ZktAUiUJLoalZHCo2WDm3QqQ/spFaVV+vowU0inWDXLVFb9SP7TqsV1uUmyaaT22fgOzRdvOwBOFH5ToujuXcOWF6sM4bsgQuEra5eaOmuFji3TcWacW99NzTDlySIBzQ8Q3HWJnKcf6Hc7wwHN3yvPeb53T3/oqu0Huyl+lvTzPg04iNJUiXm5i2SzLooVNTaKxUGXpygMqtLJK79TpRb1noYzF6RuMUbheIDLTrTMY7gxQbFrbTbp8e9+sod2Zt5RdKxIZjDH5ocMvm0rk3Fph9d5XEUHSj02VprcqFl2bYd7iLg4e7t12A1dfrrN9YJ5QK0ZIl3pnOtdPuk/tYbHlJ+lS8MS9rl9dIHkii3Ul1b5aaGFWDZtzLaqlJPOglW2mx3jTpsF2apb1dYyRZum8KqiA8bK074530O9kEriTRrJsP/DiFQoOYfoLZixVqpo/f/r1fort797TjbDbL0aNHd7y9t7eXq1evbgR6kiTxzHiSsc4gq6UmmiIzmPATvvPaI5EIv/SxJ/h76ybl6+t454o0PQpMJjg28nBLLS4tlTj/0izh2RJhWca0HS7mGiQjPp7dQypovmbwN6/PsfzmElqphen38Bffn+Lzz44wmNia0ZVbr1PO1Ah3vbdmVHUFWVVYWdi5Y7Dw8IhA7xGlaAq6R0W50+lN3+tpjuugKvKW4MB1Xa6eXaTmOPRukxcdDupUgxo3zq1w/LmRjcUEtLtMtlqtXQt1AcLhMLOzsyQSCToPdfKcV2Xg7WXmptYxGhZ60MvQsQQ9R3v4h2yJQrpKrNjCXCijRD2gyDjlFhIS1oEESkDn4PtSNfSAjv4AR/5r5RbFbJ1EyWiPM7jTfUpN+DDmS+hNE19/hM/+wvaDz/PTeZqmw2zLwK8rhO+kh0X9OsvFOrmOIMGlCtV0dWOxpHpVnHUHb1jBq6kU6i0ct72j5dUUTMO5b7c+QfhxkzUZzaMil8Gyt+/SuxPTcfHhovk3/wm6dTFNsWrQ0bN9p0hNlZESPm5fWePpbH1LM5P19fX7drPt6upibW2Nrn1dOJbD4puLaOkaI34NSZMwC01MXaH7WDfjBzpY/aNzpG+ukxiK4A16cByXaq5Oda3K/meHePKT+5h/ZZ7SQglJkvAn/STvDFN3bAej2KS+XkfxKCQPJhl6dmjb9OnpK2sourppbEukK8jylTVmp3I7tjcvL5UxqgbhvjDnbudYKdbpDHlxXJWZbJWAR+FQT5jCdIHyUnmjdk7RFRRNweO4+HWFbKWFrsr47/wet0uxF4SfFqmhKHpQJz2VB9fFG9DpG36wU37DsPjSn11g4cIanqAHpx7kB1+f41f/5e6Bnm3bKMruf4M1TWunid+T4tkT8e3YNOVAT5jGC6Oc7Q5QLTQJxbycOtTNwZ6t6aY/imsLBbSlCt6wBzXuQym38K3VuDqT48xYx7Y1xve6vFRk6fVFOrIN1IgHp9gid3aFN3vCDMT9WzbfAkEd1afSqpn470nTtC2bYOiDjJYQHpS4kj+iNL9G/3iCq7dzlBrmlq5N22kYNnrTpmsoumUBZbdsCpkackDf0rjgLk/YSyHXwKgamwK9xcVFvvzlLxMKhXAch4MHt3aw2ngMj4dWq4XH4yE+Fic2EmMyU8M2bBRdwZ/0Iysyz6c9fNWwyU/Gia03Ya0Kho3aFaTeHaAU1XhiMMZIcvchoXvisuN7llwJd5eZBc1ik6YCddOm656LmiSBV1UoWg59tFNt74qPxclP5Yl5VY72R7iZriBJEgd6wvgUiZbt3DelShB+3DxhD119YcIrRcpNk0Tw/tcYAFxotkwGNJVw3+ZFTGGthqvLuwaN/pCHynKVRqW15Trluu6uKVfw3jVGkiQ6D3US7g9TnCtSXirjOi6+uI/YcIxgdxBJlvjcbz7GN//mCunpHIVWCddt16cd/sgIn/gnB/H7dCY/N0l5qUzuVo7iXJF6to7rtIcZ6wGd1KkU8bH4xmPu8LF8IEa1Pbzdtl0KNYOQ970ROj5doVA125kLEljN94YV+zv8hAfD5G7kODUQI9cw8esKoaqJnvBt+d0Iwk+TI8d6afyqycXXF5FkOPbUAIeOPFin18xajbWZPPGhKP6wl9JalcVrGWo1Y1PDlnu9f07eToaGhpibm2NiYmJPr0WWJR4fSnA4FaXasgh61G3n+P6othvXIrnvjYO6n/lsDW+5hRb3okS8OH4Nf6bOykqZluVsec3hsJfJUyne+doUrgsev0Y5XSUQ93NIzPP9iRCB3iMsOdlB7xuLXG4aWH5919bfuFCot+i2YeRY95YdZ0mRUDUZ17J3fAjbslFVectCZnx8HMMwGB4eZv/+/bu+5p6eHubm5rAsi7GxMSRZIti9NVib7A4jIfHDqSyrIR27xw+uA5pCNODhuf4oT491bMyn+aA6wx7CST+NoI5vvY7UGwJFwi40waNghTX6Yzs3oFE9KpojockSLcve1H7etB38ioTkbq6fiw3HiAxGKEwX6O8PM3QwgEQ7GCzcLpAYT2yq6ROEfwyyItN9sJOuC2muGxau69lxQ+ReNcPCa7r0piLE3rcDr+gyruPg7rK5YtsuiuRu2nlutVr86Z/+KR6Ph5MnT+66mQQQDAapVqsEg0G8ES/dR7vp3mZMAUD/YJR/+XtPMXVrnexaFUWVGR6Nb2rsoGgKseEYseEYjUIDq2nhWE47syKo76kB0tihLhYurmHUTfQ7G2XlbB3drzM0uv0JJ7RP5lzLRVEkPKpMvm4Q9mm4bnsmoM9zd0SOu+k6I0kSA08NYNUtSoslulyAdpC306mjIPw0Of30IKef3tq5eq98fhVNV2lWDPxhL62qQbDDj2eX0+z71efddbfx04O6m7nz43JgIM5cb5DWfAW3YmA4LrXeAE+MxO97mgcQDerMagp2xWzXEpZbmApEQzraDqUwH/3UJCBx860lmuUWnSMxnnpxnJFR0Zn3J0EEeo+wcF+Y8UNdrL02R7pYpyvq2/aL6Lrt0QH+msX4UGzbttiKpjAw2cGt2/lth6k7Dpj5Bv3HevHGvDRNm/lcnWrLREJGjnTxuV/8/H1328+fP8/3v/99NE3j937v93a9777uEMMdAeZyNbJ3mh8EPCqjyeC2Q0Y/iLBX4/hAjJfWaii2C+karu3g+jVyqQD+uIy3lQe2TxmJjcaYfWeW7kiQ2+tVkmEfmixRbproikyHK+Hv8BPsei+Y1fwaIx8dYeHVBYqzRWzTxnVdNJ9G8mCSwWcGRd2d8FMhOhRlYCjKylSWTLlJZ9i7a7BnWg6FaotBy2XwUCfe6ObUncF9Sc69tkC1+V4XzPer5+pM9IQI3nOa5/F4UBSFdDq9Yze8eyUSCebn51FVdeO/3SiqzOSBTiYP7Nzp9q4P2m3yxBP9zFzPsHB+DRQJ13aQVImJp3pYXr3K+L7ncGyH2lptIwNA9ar4O/zIqsztm7cZ6+jg7dk6y4U6rgvxgM54Z4hWuYUn6CHQtXlTyhf3se8z+ygtlGhVWqgelchABE94j6ezgvAzLJEIcPz5Yd76xhQrV7P4Ih6e/sQE6g4ZBX/7t3/L8vIyH//4x/eUvtnb28vKysp908kftqZpk68ZSBIkAp5NpTtH+iIsPz/CtXMrlCotFL9GvAuO32e81F2H+6JcnIxSuFrAv1ylpUlY++Kc2Ne548a6z6fxC//0EM+9OE6jbhBPBHb8jIWHTwR6jzBFU5j46AjNmsE7F1dZd+rIHo2IT0WVZRzXpdqyqDdMwg2Lg70Rjn58fFPQca8DJ/u4fHaZlYUiyf4Inju7TqbtsJ6uENVV9p9K8dZ8gfOLRTL5OjQsXAkij32Kvz6/yqnhOJPdO6cEdXd34/f7aTQae3qPuioz0RVi4sfYjemp0Q6als3FqJdiuoJku0gRD/Vamtzr32HKeYzxVD/FuWK7S6npoPk0wv1hQqkQs+uzrL9bIvXECcqmTc12CXpVxgIewqZD55GtXT99MR8Tn5qgslrZaLzi7/AT6Ao8cOMLQfhx0YM6488OUs43uJKvknZdEkHPlppg14Vay6JYbdHTcjh2rIeex3q2PN7g/iS9fRFur1TwDkW3bEzlSk08DYv9p/rQAzq19Rrzt3I06ibFOZsnn35qTzOw0uk0r7zyCtlslt/6rd8iEtnbIufHKRj08Cv/6iQX31lhYTqHpsl0DwZ48+2vkV6HidgExaki+YUS9YaBi4TPpxK703136uwUNf8FTj/zYZroSEB32ENAU8hP5+k+1o0/sbW+WvWqYuad8HPrhU9NMjSWoFRs0NkVYmBo5zo/0zRZWVnhS1/6Ev/u3/07/P7d57jF43GuXr36Ewv0LNvhnfkCFxaL5AoNJBk6435ODMQ42hdFliW8msJnj6U4MRSnWG+neveENG5cv8axY8fu+xwx1eTxST8rA3Fy2RqxsIfH93dxrC96358NhTw7jr8Qfnwkd7uEXeGR0sg3mHpphttXMyzn6pQVCVuWkXDxmw49AQ+DIzEmnxsmPrZzihDA7IVVvvNXV8isVTG9CpIkITUtYmEvZz4xzmqnn7enc/izNQLpGlLdAknCjuiUkn6UviAvHuzhsYGdL6br+QK//+d/x6EzHyddbgc5XWEvB3rCDHcE9t5Y5iFyXZd0uclSoYHluPgkm+9/+S9ZW1ombsT5yOGPsrJUpmTYOJKLjkQ8qNM9EOHvX/57yENCT3Dg+HEcCXQbfEGd7uPdpE6nRDty4WeW67qkL6a5/J0ZplbL5HUZW5Xx6goyEpbj0DIt/C2HTiSOHu1h4mOjO24oLVxM8/X/dpF0oYHa4ccf0LEsh1q+hqduc/x0Hx/6/AEuvzrP+TcWyWVrWC64pklXT4RDJ/t44tMTaLuc6i8tLfH1r3+ddDrNf/gP/wGv98GbArQqLarp6sb4Ej2gE+wJojzEtKuZmRnefOVNbnzzBgdTx7GCYdJA885ej8d16UYmUGtx/tU3CHeGGTk+wuTRSXChUWjQzDeJj8UZeWEEj1hkCcIH9t3vfpevfOUr/Mf/+B/3HLxduHCBmZkZ+vr6OHXq1I/ttbmuy/eur/HqxTS+xRL+QgsXqCW8mMNRXjjWy5OjO3fwrFarLC0tMTk5uaUO0XVdXNfFMAympqY4fPgwtuO20/BV5R9lTSbsnQj0fk44tkNlpcL6zRwr0zkMw0ZRZBLdAXoOdREZiOy6MLpXJV3l1sU0CzcyuA50DUWYPN7LnOTy1XeXid0uIS+UkANae6C542KXWuC41MaiyGNxvnBqgFR0a4pTrtriG1fTzGZruC2LQKtdE1j3KLgeheFEgE8c7qFjh8YPrutSy9Qozhap59oNETxhD9HBKOG+8I6z5Czb4fVra9y8kaWnN8yHT/YRuE/XOcd2ePUvX+Wb//VlUidPs65KuK6LJMvYtoNXkvAVypSXbvDCv/gwY/vGqCxVsA17o+GDOKETHhWF2QLpi2vM38yyul6nYjs4uGhIxL0qqVSEwUNddB3t2pKy+X7pm+ucf3mO2zcy1BsWEhLdvUEOPN7HoacHePvb07z6rSlaQZ1oMoBHlTFsh0KujlZo8cSHh/nQLx/aNejK5/P83//HH/LkiU9RWq+he1SG9yc5cLQHfZe5mtV0ldzNHLmpHM1i873vrwKhrhDJg0ni4/Edr6f1hkG52KL7PnMwAWzTZua7M7z97avcwsEJBAl6NAJ3Urfrpk2laaK0GnSulTi4fwh/3N9uuiKBL+KjY38HyYPJB+o2LAjCe1aKDW5nq8wvryLVC/zSx57ZU3CzvLzMn/3Zn9FqtfjoRz/KmTNnfqyv8c9+OIN+fg19vYES84LrYheaNHoCKCd7+Zdnhon6d74OrK2tcfbsWQqFAr/5m7+58e83b97k9ddfZ//+/Zw6deq+JTjCTxeRuvlzQlZkIv0RIv0Rhj40iGM5yIqMrMkPHGiEuoOc6B7jxIvvpUhZtsPFN+fRM3WUxTJKdxD5nhEAsl/DLrUIzldYC+rcWC1vCfTKTZOvXFphdrVCd74JC2WcSruY2R/UkfpDTLdsvuK4fP6xvi11eI1Cg8XXF9td7yoGrTtPr7ngO79KuDdM6lRq246VVxYKvPQXl1EzNRYDGrIi8ckndi/yLs4WMZdlkk88yTouHUHPptrFhmGTc0P0R06hrXsIfzhM5/771/gIws+i2HCM6GCUvrUUxbkizUKz3ZDEqxDqDhEdiu659qt7XwcfH09QSVdoVAwUVSbcFUQP6uTmi7zzw3mM2OZZe15Zoac7REZTOP/GAgdOpujet/0OttmyePUbC+ilSV7/6k0Uj4pr2lx+dZ63xxN85teP0ZXammK+fnOd+ZfnaZab1L0q6x6JmmmjSBJxj4qRqVJaKhGfjjP8/PCWgLZabfHF//wWxdUKpz8xxoc/tntHvuJskcWLadaiYWTboed9NZARVSbi1VirKNSHgzQdGH2sh8idehtv1LslLVwQhL27tlrmaz+cpXo9i9xyoDvA17pX+PTR3h2bj9yVSqX4zGc+wxe/+EV0j4emaf/YGq3M5WrU01VC+SZqKoR0Zy0ieVR8uSbrK2XmcnWO7RLovfvuu5w7dw6/37+pBnFubo7bt29TrVY5dOgQweBD6GYu/MSIvwA/hxRNeajpRQDLxQbL+TrxbAN0eVOQt/G8EQ/GfIlQ2eDaapmnxzo2XfQuLRaZWauSWq1h3sqjRDyoqfaut1NuYV1Zp3cixqwEFxYLfGjivaCpnqsz/a1p8oslsprCkmNRbVjggkdV6FEkuueKNPINhj8yTGI8selCls/XsQoNkh0B8us1squVXd+v67pkb2RZLtQpyJCK+bc0ofDpCl1hLyvVJh3zRfrnijt29hOER4EkS4R6QoT2cFq1l8cK94Z5f7h14/wq5WqLRO/2dWUdMR9r2RpXzy3vGOi99PUbnP/eDP7uIMlhP9z57raaFos31vn7Pz3Pr/+7Jwjck+qYv51n9vuzNCyHm5JLOlvBdlx0RcYF5nI2AV1lNBHAnc4DMPriKCYmgUC7Ccri3Dpr01maeYOF6Tx8bOf37zou6zfWydRNCra17TWm/UFBV8jDcrFOtgWl+RI9j/WIdHBB+BG1LJuXLyxjvLVCp+mCR8GYKnBJkZjsCbN/DzPu9u8/wJOf/gI/mGvw+tI1envCPDGRZCC+e33fA79W00YxHFyHjSAPQPIoYNlIpkNrl67pAE8//TSyLPOtb32br/3D2ziGD3/Iy6tvvMXg4CCnTp26b12i8NNHBHrCQ1E3bGzDRm6YsEuKkORR0GsmLdOhYby3u9UwbC4tlwg3bayZEmpnANn33v+eStSL5FWxZktEEj4uL5d4fCiOX1exTZv5l+fJLZS4JcNyrkrQq5EIeJCl9mubLtTI+jQO1UB+eZ4r01cwNZMTJ06Qz+fRFYvAaIz0bBGtN8TE/uTu73e9TuZ2gbQkEfapO3Ya1FQZWZLJmA6Zqxk6D3WKBZgg/AiKmRqOruy4my7LEq5HpbRW2/b2Ur7O5deX8MR9RN7XnMTjVemciJOeynP9YpqTZ9qn+lbTYvH1RRpNm6umSbrcJBna3HTGdaHSNLmSLuN2hWA6x2xullq0xpkzZzAMg2jCy9HnR1lbKHPq2eFd32c9Vyc/XyQtufg9O19jAJAg4NFIWw75xRK1TO2hBNuC8PMsVzUorFQJNG3UwUh7dNRqFTdTI1NpsX9rP6ktzi8WuHC1gXIjh+TkuO5VWDld5lefH6c78sEHhluWhaIoGxlZsm3QUB3QZJx6e/QB0M6K8qoQ0Al5dy/PCYfDvPjii6jOIK/87VUkV8KxXTpGT/Hrv/48vofUzVz4yRKBnvBQKLIEsoQrS0i2s/MdbRdXkZFkNs1sWSs3ydUMOsstLNveFOTdJXtVbNslVDJIh00u3Jrn8GAn1rpFcb7Isi6znKvRE/VtavMb9KoEPSpr5SY3FYvcpWlunb2OOqRy7NgxRkdHGaWdZrGUrhCLejmQiu76fs26SaXcoqlC8j61fD5doWKZtCoGdstG9otATxA+KM2jgO20J4zvEPy4toO6TVYBwPT1LNVcnc4dTvt0XQVN4taFVU6eGaRardJaaVHL1Mh4ZVbXG/RGfVtmTkkShH0asiQxtV6jWs2zcPY6HITnnnuOnp72qvBzv3r/GVzQDi6bdZOGBP49ZGD4dYWy7VCvm5sGowuC8MF4VBnVq2JJEm7TAo+C27JxYzpe7f5/xw3L4a1ra6g3ckQ1BTnswbdWI3t+jauTnXsO9LLZGvlcjWQyQDwRwLIs/uRP/oRTp04RDAbJZrP4vEE6h+JUijahlSqS0gTXxQVyqSAdXX5GOnae+XuvG+dW0T06yZEYRt0kv1hmfrawp/Eywk8fEegJD0Uy6CES9NBM+NBnSijbNFtwbQfXdqhGdIZCXkL3BEiW42DbDrLhwG5FzpqMZNi0Wi2++73X+V55ldMdp5GbPpZNk6hf336WiwQdIQ+ZcpNaJU9/qB/fgI9Wq7Vxl+FkkOHk3nLPJUna2GHfUzsjt52KttPCVBCEvRne38m51+apNExC/q07zPWWhW46DB3cflHSbLVHvii7XGcUj0q5WMN1Xb70pS+h3lbpCfazUGwR9Gq7DhYOelVKDYOCJjPRN0FkPEK9Xn/g93l3p36vlwwXF4mdB80LgvBgEkEP+w4kOTdfhHQN2XapBjRiB5KM7mGt0LRsquUWHtNF6fIhqTJySEerm1yfWWB/xCYUChEIBDY1OPna177G/v37GRkZ4dxbS7z0N1dolFr44z5e+JVDnDv/Laanp1leXuZ3fud3GB0dBcC/XOIbDqyHNPylFkbLIlNsYt5ax12q8Gfnsxw+3ceJ033tDa0dyIqMY7cXNrbtICkgif3pn1ki0BMeilhAZ193iLOrZTozKma6itoZaAc3gGvamCtVpK4AZtzL4b7IpsWSripoqozlVXDNnU8EXdPB9qoYRovV21PI1RwzfzfD6JHHsQbGd+0opcgSSBJ9hw8zpigceuEQwe4PVlSsB3UiUS+B1RbVlkV0mwXnXTXDoleS8EW8qPc5/RMEYXeDB5L0D8e4NZtHG47hvWfBYlgO+YUSQ30Rxg5vf3IWCOjISBiGteNix2qY+CMRlpeXWVlaoXWpxXV1CXniKN2R+w9E9+sqitbBQIfG0P4hegcffI6W5tfwB3UC9SZVw8Kn736qV2/Z+Nz2+9N2uR4JgrB3Hz3QjSJJ3LiRxTBsUqkwzx3ZufP3vQK6SiIZYNmj4E3XkEMaVqmF2eXn6PgAwaCPUqnEysoK9zbAP3/+PGfPnmVoaJz8VCfNqkF8IEJuociX/+wtiKcZHx+n2WwSCr2Xon0oFSHi17jSF+XqdI70d2dQsnW6kn4CukJ6KsfqjXUWpnP84j87uuP178Rzw3zvLy+zciWDJEsMn0oxPLL76C3hp5dYdQoPzYnBGHO5GjnTJbFQxlqs4MpsnGZJ3QHWBsLs749uGXDeHfbSFfKSCxmEfCp2uYXyvg59dsVA0hXKYZ3xVIj+fYN0xR4jsT9BTfZyodi47262KkuYtBsdOLulmN6HL+6jczxB13KZW02TsFfddpe/adpILnRpCskDyY3AVxCED0YP6rzwS4cwvniRpek8blBH8WnYTQsqBn3dQT72y4fw79DsYN+hLqLdQYqrVToHo1tub9QNFCSOPTFCb28P+8b34XE8SOEob2aqu57m3aXIEpbTnj1l36cBwk68MS/xkRhda1WyhoXjuDs+t+O0B9IPygrxwSiB5N5StARB2F3Ao/LpYyme2ddJy7KJ+3XUPdbZK7LEk/u7+HK6xvqlNbSqQTPpJXU6xeG+GGG/Tji8uaGL67rouk5fXx9eT4SVcpNAIoDmVQkk2qNT/vXv/ntise03nPpjfvpjfopvLlGuGvQ81rPRfC/SFaRWanLj9UUuTCY59eTAto9x6skBwmEvywtFfEGd4ydTu54ACj/dxG9OeGi6wl4+faSXb8gSK2EdT6GFr9UeIF7zargdXg6kInziUM+WFsO6KnO0P8pX83XCozGc6zmcuokS8QASdrkFho18oIOaX+FD/VFOP/s7uI7LpT+/RCvT7pK522IIwLQcfNL9O4+urpT5yl9cBOAXvnCUnt6t3bU6Jjvov5ahmKuzWmoQD3g2dt3vNmaoNAyGbIn+/YltxzoIgvDgusYS/NN/e4pr7ywzdX6Ves3AF/cz+twIBx9PEd1mNMJdvoDOyedHeOmvLpNdLBHrCaGqMq4L1WKD0mKZiZO97D/ShSzLfOYXP8Ol/+8SuUoTVZYxLWfTGJXtGJZDzK8jIaHuskCq1QzeeWOB4bEEA0OxTbdJkkTHvg46r2RI1iFdbtId9m65vjkOrFUadGgKnV6djv0dYkNJEB6y9jinBz8p398TxvPCGJcPdFCsGAx2BjnSF901++i3f/u3iUQiGIbFHy++wcr1DEbdpJarM3ish/B9xtSspSvMX8sS7g1vWecEIl4q6SpX3lraMdADmDzYyeQO6e/CzxYR6AkP1UDcz6+dGmAqU+XyUolS00QFhqJeDvZGGOkI7jho9HAqwtx6jcuOS9Lbhb5cwS62ABc15sVMhVgNaRxKRTnaFwXaJ4XxsTjFlQphn0apYRLboetny7JRFZmYBYFUAF985xSsa5fSLLy7CsDV/V3bBnqRgQjjzw7hfH+W6XydbM0gX7tTI+O6+F2JUUti/1iCkeeH0INiYLEgPCzhriBPfGofJz8y2u74q8k7Dil/vyefH8FxHN767gzZG+u4d76znqDOwaf6+cSvHEa7k2YtKzKRoQjVt2vEAxrFuklHaOeFluu2rzWpgAfZdPB37NyO/NK7K7zy11eZO9rFb/7OE1tujwxGGDzZS+vVBW6pkC410FWl3YUTqBsWTdMmoSmMSwqDj/USG4ltfSJBEP7RjCSDjDxA/X8k0p6Dqesqn/31o3zr766SX64wdrqfj39+P8p9ThSL+QZGzSTcuf3JvifkoZTZviux8OgRgZ7w0IW8Go8NxDjeH8W0XWSJPaU6eDWFTx3pwacrXF+tkA1peIx2eqWhywT8Gie7Q3xksmvTiWBsJIb/3TSDhszlfI26YeN/Xz2LaTtkyy2G4j6CNiQPJJF32ZXvG4gS64/iAv07nMRJkkTPYz3oAZ3EuyuszhUpVg0cCVQkOhN+uscS9D7eK1qdC8KPiepVH3gouCRJnHlhnCMn+7hxOU2l3ELTFEbGO0gNRzcaodyVGEuwdmmNAU0lW2ntOvh4vdoi4tOJmg6hVIhQaufv/sh4gvFTfUwc2b6eUFZkBp5u77p7z62QKUPacajf6aoZwGVMUenw6Qwc62Hw2cGHPiNVEIR/PD29Yf75//QkjuNsatiyG59fQ9FljKaJuk1tr9m0CO+yASU8WiTX3VPPQEH4iVorN5laq7Bea3fFTAQ8THSF6Apv083TdZl7eY6lt1ZY0SSmSw1M28Gvq8hSe0af47r0hj2M2RJdYwnGPzl+34YF+Vx7xyueuH+9i23alBfLNPINbMtG1VWCPUGC3cEti0ZBEH62uI7Lza/cJHszx4JHZipbxacrhH3aRpfflmlTqJt4VYVjnQFCDZuxF8dIHth9JudeOLZDaaFE7laOwkyBetXApb2gi4/ESEwkiA5Gd928EgTh54PjOPzx//U6y9cydO/b3BvAbNlkpnJ8+NeO8NwLY/+Ir1L4SRGBnvBIsFoW8y/Pk76UpuLCugKZhglAyKPQI8mETIeOkTjDHxnGnxC7WYIg7F2j0GDqG1OUlytk/QrzpSalhglSO11TU2Q6wx5GvB58dZPU4ykGnhl46PVyjUIDq9E+0VO9Kt6YV2wmCYKwyY1rGb78x+doFBqEe8NoukK93KSWrZM63MkX/sdThO5T6yc8GkSgJzwybNMmdzNH5mqG6moV27JxkZAl8Hf46ZjsILk/iUdc3ARB+ADquTpzP5ijNF/Ccl1qHgVTBtmV8DoO3qaNHtDpOtpF3+k+ccImCMI/mpvXMrz+3WnStwtYho03oDH2WC8f+vg48R26EguPHhHoCY8cx3KorFQwqgau66J6VEKp0J4bNQiCIOzENmxKi+00yvJiGcd0QAbVp5KcTLZrhpN+ccomCMI/OsdxSK9UaLYsYnH/jmMZhEeXCPQEQRAE4QG5rotRNbANG0mW0HzaAzeFEQRBEIQfJxHoCYIgCIIgCIIgPGJEAYEgCIIgCIIgCMIjRgR6giAIgiAIgiAIjxgR6AmCIAiCIAiCIDxiRKAnCIIgCIIgCILwiBGBniAIgiAIgiAIwiNGBHqCIAiCIAiCIAiPGBHoCYIgCIIgCIIgPGJEoCcIgiAIgiAIgvCIEYGeIAiCIAiCIAjCI0YEeoIgCIIgCIIgCI8YEegJgiAIgiAIgiA8YkSgJwiCIAiCIAiC8IgRgZ4gCIIgCIIgCMIjRgR6giAIgiAIgiAIjxgR6AmCIAiCIAiCIDxiRKAnCIIgCIIgCILwiBGBniAIgiAIgiAIwiNGBHqCIAiCIAiCIAiPGBHoCYIgCIIgCIIgPGJEoCcIgiAIgiAIgvCIEYGeIAiCIAiCIAjCI0YEeoIgCIIgCIIgCI8YEegJgiAIgiAIgiA8YkSgJwiCIAiCIAiC8Ij5/wHc9EN09dPpdwAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f0e15813ac234520baec62fd7b3ae7b6", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./sdm_network.pdf
\"), HTML(value…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "graph_types = (\"total_mass\", \"mass\", \"mult\")\n", + "\n", + "fig, axes = pyplot.subplot_mosaic(\n", + " (\n", + " ['sampling: uniform random in x' + t for t in graph_types],\n", + " ['sampling: uniform random in ln(x)' + t for t in graph_types],\n", + " ['sampling: constant multiplicity' + t for t in graph_types],\n", + " ),\n", + " figsize=(9, 9),\n", + " tight_layout=True,\n", + ")\n", + "titles = {\n", + " 'sampling: uniform random in x': 'uniform-in-x',\n", + " 'sampling: uniform random in ln(x)': 'uniform-in-log(x)',\n", + " 'sampling: constant multiplicity': 'constant-multiplicity',\n", + " 'total_mass': 'Total Mass',\n", + " 'mult': 'Multiplicity',\n", + " 'mass': 'Individual Mass',\n", + "}\n", + "for sampling_key, v in B.items():\n", + " pos = nx.forceatlas2_layout(v, scaling_ratio=0.2, strong_gravity=True)\n", + " for graph_type in graph_types:\n", + " ax = axes[sampling_key + graph_type]\n", + " if graph_type == \"total_mass\":\n", + " attr_i = np.array([v.nodes[i]['mass_i'] * v.nodes[i]['mult_i'] for i in v.nodes])\n", + " attr_f = np.array([v.nodes[i]['mass_f'] * v.nodes[i]['mult_f'] for i in v.nodes])\n", + " scale = 1e4\n", + " scale_edge = 1e2\n", + " else:\n", + " attr_i = np.array([v.nodes[i][graph_type + \"_i\"] for i in v.nodes])\n", + " attr_f = np.array([v.nodes[i][graph_type + \"_f\"] for i in v.nodes])\n", + " scale = 1e14 if graph_type == \"mass\" else 1e-9\n", + " scale_edge = 1e4 if graph_type == \"mass\" else 1e-11\n", + " nx.draw(v, pos, ax=ax, \n", + " node_size= scale * attr_i, \n", + " alpha= 0.4,\n", + " width=[0.2 + scale_edge * v.edges[e][graph_type +'_transfer'] for e in v.edges],\n", + " arrowsize=5,\n", + " with_labels=False\n", + " )\n", + " nx.draw_networkx_edges(\n", + " v,\n", + " pos=pos,\n", + " ax=ax,\n", + " edgelist=[e for e in v.edges if v.edges[e]['mult_deficit']>0],\n", + " edge_color='red',\n", + " width=[0.2 + 1e-11 * v.edges[e]['mult_transfer'] for e in v.edges if v.edges[e]['mult_deficit']>0],\n", + " label='deficit',\n", + " arrowsize=5,\n", + " )\n", + " nx.draw_networkx_nodes(\n", + " v,\n", + " pos,\n", + " ax=ax, \n", + " node_size= scale * attr_f,\n", + " node_color='purple',\n", + " alpha=0.3,\n", + " label='final size'\n", + " )\n", + " if sampling_key == 'sampling: uniform random in x':\n", + " ax.set_title(titles[graph_type], fontsize=12)\n", + " if graph_type == \"total_mass\":\n", + " ax.annotate(\n", + " titles[sampling_key],\n", + " xy=(-0.15, 0.5),\n", + " xycoords='axes fraction',\n", + " va='center', \n", + " fontsize=12,\n", + " rotation=90\n", + " )\n", + "show_plot('sdm_network', inline_format='png')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.12.11" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/PySDM/source/examples/PySDM_examples/Yang_et_al_2018/__init__.py b/PySDM/source/examples/PySDM_examples/Yang_et_al_2018/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..29e89d250b60217d7c4c6b0d8c63d15c4229ac51 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Yang_et_al_2018/__init__.py @@ -0,0 +1,10 @@ +# pylint: disable=invalid-name +""" +parcel-model condensation-evaporation example based on +[Yang et al. 2018 (ACP)](https://doi.org/10.5194/acp-18-7313-2018) + +fig_2.ipynb: +.. include:: ./fig_2.ipynb.badges.md +""" +from .settings import Settings +from .simulation import Simulation diff --git a/PySDM/source/examples/PySDM_examples/Yang_et_al_2018/fig_2.ipynb b/PySDM/source/examples/PySDM_examples/Yang_et_al_2018/fig_2.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..a2e1ca33369a54ced8ac81cf5751750a0d766e96 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Yang_et_al_2018/fig_2.ipynb @@ -0,0 +1,349 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Yang_et_al_2018/fig_2.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Yang_et_al_2018/fig_2.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Yang_et_al_2018/fig_2.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### based on Fig. 2 from Yang et al. 2018 (Atmos. Chem. Phys. 18) \"_Cloud droplet size distribution broadening during diffusional growth: ripening amplified by deactivation and reactivation_\" \n", + "https://doi.org/10.5194/acp-18-7313-2018" + ] + }, + { + "cell_type": "code", + "metadata": { + "pycharm": { + "name": "#%%\n" + }, + "ExecuteTime": { + "end_time": "2025-12-08T21:21:28.343055Z", + "start_time": "2025-12-08T21:21:28.338005Z" + } + }, + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ], + "outputs": [], + "execution_count": 1 + }, + { + "cell_type": "code", + "metadata": { + "ExecuteTime": { + "end_time": "2025-12-08T21:21:33.550909Z", + "start_time": "2025-12-08T21:21:28.346833Z" + } + }, + "source": [ + "import matplotlib as mpl\n", + "import matplotlib.pyplot as plt\n", + "from open_atmos_jupyter_utils import show_plot\n", + "from PySDM_examples.Yang_et_al_2018 import Simulation, Settings\n", + "from PySDM.physics.constants import PER_CENT, si, in_unit" + ], + "outputs": [], + "execution_count": 2 + }, + { + "cell_type": "code", + "metadata": { + "ExecuteTime": { + "end_time": "2025-12-08T21:21:33.563483Z", + "start_time": "2025-12-08T21:21:33.561152Z" + } + }, + "source": [ + "ix51 = 40\n", + "ix503 = 99" + ], + "outputs": [], + "execution_count": 3 + }, + { + "cell_type": "code", + "metadata": { + "ExecuteTime": { + "end_time": "2025-12-08T21:21:33.569763Z", + "start_time": "2025-12-08T21:21:33.566532Z" + } + }, + "source": [ + "def runner():\n", + " settings = Settings(dt_output=10 * si.minutes)\n", + "\n", + " assert int(round(in_unit(settings.r_dry[ix51], si.nm))) == 51\n", + " assert int(round(in_unit(settings.r_dry[ix503], si.nm))) == 503\n", + "\n", + " output = Simulation(settings).run()\n", + " mass = settings.mass_of_dry_air\n", + " result = {\n", + " 'r_bins_edges': settings.r_bins_edges,\n", + " 'n': settings.n / mass,\n", + " 'dt_max': settings.dt_max,\n", + " 'rtol_thd': settings.rtol_thd,\n", + " 'rtol_x': settings.rtol_x,\n", + " }\n", + " array_keys = (\n", + " 'r_mean_gt_1_um', 'z', 'S', 't', 'T',\n", + " 'r_act', 'r_bins_values', 'r',\n", + " )\n", + "\n", + " result.update({k: output[k] for k in array_keys})\n", + " result['liquid_water_mixing_ratio'] = (\n", + " output[\"water_vapour_mixing_ratio\"][0] - output[\"water_vapour_mixing_ratio\"]\n", + " )\n", + " result['r_bins_values'] = result['r_bins_values'].T / mass\n", + " result['r'] = result['r'].T\n", + " return result\n" + ], + "outputs": [], + "execution_count": 4 + }, + { + "cell_type": "code", + "metadata": { + "ExecuteTime": { + "end_time": "2025-12-08T21:22:03.540018Z", + "start_time": "2025-12-08T21:21:33.572803Z" + } + }, + "source": "out = runner()", + "outputs": [], + "execution_count": 5 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": [ + "### Thermodynamical and microphysical properties of an adiabatic cloud parcel with upward and downward oscillations. \n", + "Fig. 2, subplots a, b & c \n", + "(a) Liquid water mixing ratio changes with height. \n", + "(b) Cloud parcel saturation ratio changes with height. \n", + "(c) Radii changes of two selected cloud droplets with height." + ] + }, + { + "cell_type": "code", + "metadata": { + "ExecuteTime": { + "end_time": "2025-12-08T21:22:04.327426Z", + "start_time": "2025-12-08T21:22:03.561721Z" + } + }, + "source": [ + "figsize=(9,6)\n", + "fig, ax = plt.subplots(1, 3, sharey=True, figsize=figsize)\n", + " \n", + "xunit_q = si.gram / si.kilogram\n", + "xunit_S = PER_CENT\n", + "xunit_r = si.um\n", + "\n", + "z = out['z']\n", + "tols = f\"rtol_x = {out['rtol_x']}, rtol_thd = {out['rtol_thd']}\"\n", + "\n", + "ax[0].plot(in_unit(out['liquid_water_mixing_ratio'], xunit_q), z)\n", + "ax[0].set(\n", + " xlabel='liquid water mixing ratio [g/kg]',\n", + " ylim=[800, 1300],\n", + " ylabel=\"metres\"\n", + ")\n", + "ax[1].plot(in_unit(out['S']+1, xunit_S), z)\n", + "ax[1].set(\n", + " xlabel=\"saturation ratio [%]\",\n", + " xlim=[99.75, 100.25],\n", + " title=f\"dt_max = {out['dt_max']}, \"+tols\n", + ")\n", + "ax[2].plot(\n", + " in_unit(out['r'][ix51], xunit_r), z,\n", + " label=\"r$_d$ = 51 nm\"\n", + ")\n", + "ax[2].plot(\n", + " in_unit(out['r'][ix503], xunit_r), z,\n", + " label=\"r$_d$ = 503 nm\"\n", + ")\n", + "ax[2].set(xlabel=\"radius [micrometre]\")\n", + "ax[2].legend()\n", + "\n", + "for a in ax:\n", + " a.grid()\n", + "\n", + "fig.tight_layout()\n", + "show_plot(filename='q_S_rd.pdf')" + ], + "outputs": [ + { + "data": { + "text/plain": [ + "
" + ], + "image/svg+xml": "\n\n\n \n \n \n \n 2025-12-08T22:22:04.281291\n image/svg+xml\n \n \n Matplotlib v3.10.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n" + }, + "metadata": {}, + "output_type": "display_data", + "jetTransient": { + "display_id": null + } + }, + { + "data": { + "text/plain": [ + "HBox(children=(HTML(value=\"./q_S_rd.pdf
\"), HTML(value=\" r$_\\text{cr}$)\",\n", + ")\n", + "ax.plot(\n", + " in_unit(out['t'], xunit),\n", + " in_unit(out[\"r_mean_gt_1_um\"], yunit),\n", + " \"--\",\n", + " color=\"gray\",\n", + " label=r\"r$_\\text{mean}$ (r > 1 $\\mu$m)\"\n", + ")\n", + "ax.set(\n", + " ylabel=r'r$_\\text{d}$ [$\\mu$m]',\n", + " xlabel='time [h]',\n", + " ylim=(0,20),\n", + " title=\"Cloud Droplet Size Distribution\\n\" + rf\"dt_max = {out['dt_max']}, {tols}\"\n", + ")\n", + "ax.grid()\n", + "ax.legend(loc=\"upper right\")\n", + "\n", + "fig.tight_layout()\n", + "fig.colorbar(cm, label=r\"cloud droplet size distribution [cm$^{-3}$ $\\mu$m$^{-1}$]\")\n", + "\n", + "show_plot(filename='spectrum.pdf', inline_format='png')" + ], + "outputs": [ + { + "data": { + "text/plain": [ + "
" + ], + "image/png": "iVBORw0KGgoAAAANSUhEUgAABFsAAAJOCAYAAABoawgaAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3Xl8FPX9x/HX5tpsSMiSA0IgEOSQU1AQFbWCogiKVasitgW8q6KleFS8AI9Sjype9aqKtd7Wo796o1hvLWpUFBVsAshNIPexOeb3x+7MzuyRiw0J8H4+Hnlk5/7OZ78byDcz73EZhmEgIiIiIiIiIiIxEdfRDRARERERERER2ZNosEVEREREREREJIY02CIiIiIiIiIiEkMabBERERERERERiSENtoiIiIiIiIiIxJAGW0REREREREREYkiDLSIiIiIiIiIiMaTBFhERERERERGRGNJgi4iIiIiIiIhIDGmwRUREWiw/P59Zs2Z1yLHHjx/P+PHjO+TYsdSRNYxmd6mty+ViwYIF7X6cd999F5fLxbvvvmvNGz9+PMOHD2/3YwMUFRXhcrlYsmTJLjmeiIiIxJ4GW0REhJ9++onzzz+fffbZh+TkZLp27cqhhx7KnXfeSXV1dUc3r1Xy8/NxuVy4XC7i4uLwer2MGDGC8847j08//bSjm9dmVVVVLFiwwDEA0JyioiLOPPNM+vfvT3JyMjk5OfziF79g/vz57dfQFtpV79OTTz7J4sWLY7a/WOrMbRMREZGdk9DRDRARkY71yiuvcOqpp+J2u5kxYwbDhw/H5/PxwQcfcPnll/Ptt9/y4IMPdnQzW2XUqFFceumlAJSXl7Ny5Uqee+45HnroIf7whz9w++23d3ALW6+qqoqFCxcCtOgqlNWrV3PggQfi8Xg466yzyM/PZ+PGjXzxxRfcfPPN1r4A3nzzzfZqdpNa+z5VV1eTkNC6/7o8+eSTrFixgjlz5rR4m1/84hdUV1eTlJTUqmO1VrS29e3bl+rqahITE9v1+CIiItJ+NNgiIrIXKyws5PTTT6dv376888479OzZ01p20UUXsXr1al555ZUObGHb9OrVi9/85jeOeTfffDNnnHEGd9xxBwMHDuSCCy6Iun19fT2NjY3t/st2e7rjjjuoqKigoKCAvn37OpZt2bLFMd1R59na9yk5Obld21NTU0NSUhJxcXHtfqymuFyuDj2+iIiI7DzdRiQishe75ZZbqKio4OGHH3YMtJgGDBjA73//+yb38b///Y9TTz2VjIwMUlJSOPjgg8MGaJYsWYLL5aKoqMgxP1I2BsCDDz5I//798Xg8jB07lvfff79N52fn8Xh4/PHHycjI4KabbsIwDCCYj3HbbbexePFi+vfvj9vt5rvvvgPgnXfe4fDDD6dLly54vV5++ctfsnLlSse+FyxYgMvl4vvvv+e0006ja9euZGZm8vvf/56amppm21ZSUsKcOXPIy8vD7XYzYMAAbr75ZhobG602ZmdnA7Bw4ULr9pum8kt++uknevfuHTbQAtC9e3fHdGhmi/0Wn9Av+3u1fv16zjrrLHr06IHb7WbYsGE88sgjzZ5vU6K9TxCe2VJeXs6cOXPIz8/H7XbTvXt3jj76aL744gvrvF555RXWrFljtT8/Px8I9r2nn36aa665hl69epGSkkJZWVnUfgnw+eefM27cODweD/369eP+++93LG9pX2+qbdEyW1rTF1evXs2sWbPwer2kp6dz5plnUlVV1bI3QURERHaarmwREdmL/d///R/77LMP48aNa9P2mzdvZty4cVRVVXHJJZeQmZnJY489xgknnMDzzz/PSSed1Op9Pvzww5x//vmMGzeOOXPm8L///Y8TTjiBjIwM8vLy2tROU2pqKieddBIPP/ww3333HcOGDbOWPfroo9TU1HDeeefhdrvJyMhg6dKlTJ48mX322YcFCxZQXV3N3XffzaGHHsoXX3xh/XJsOu2008jPz2fRokV88skn3HXXXezYsYO///3vUdtUVVXFEUccwfr16zn//PPp06cPH330EfPmzWPjxo0sXryY7Oxs7rvvPi644AJOOukkTj75ZAD222+/qPvt27cvS5cu5Z133uHII49sVZ0WL15MRUWFY94dd9xBQUEBmZmZgP+9P/jgg3G5XMyePZvs7Gxee+01zj77bMrKylp1206opt4nu9/97nc8//zzzJ49m6FDh1JcXMwHH3zAypUrOeCAA7j66qspLS3l559/5o477rD2bXfDDTeQlJTEZZddRm1tbZNX+ezYsYMpU6Zw2mmnMX36dJ599lkuuOACkpKSOOuss1p1ji1pm11b+mK/fv1YtGgRX3zxBX/729/o3r07N998c6vaKSIiIm1kiIjIXqm0tNQAjF/+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eCPnn38+Rx55JNOmTaOwsJBHH320VZkt69ev5x//+Afgv0riu+++47nnnmPTpk1ceumljpDTptx6661MnjyZQw45hLPPPtt63G56ejoLFiwIW7+wsJATTjiBY489lo8//ph//OMfnHHGGU1ekXP55Zfzr3/9i+OPP55Zs2YxevRoKisr+eabb3j++ecpKioiKysLj8fD0KFDeeaZZxg0aBAZGRkMHz6c4cOHR9zvzTffzOeff87JJ59s3dLzxRdf8Pe//52MjIwmH808ffp0srOzGThwoFVH09FHH02PHj3485//zLJlyzjooIM499xzGTp0KNu3b+eLL75g6dKlbN++vdn67sz7VF5eTu/evTnllFMYOXIkqampLF26lP/+97/85S9/sdYbPXo0zzzzDHPnzuXAAw8kNTWVqVOnNtu2SHJzc7n55pspKipi0KBBPPPMMxQUFPDggw+SmJgItLyvt7Ztre2LIiIi0sE6+GlIIiLSCfz444/Gueeea+Tn5xtJSUlGWlqaceihhxp33323UVNTY60X+thiwzCMn376yTjllFMMr9drJCcnG2PHjjX+/e9/hx3jp59+MiZOnGi43W6jR48exlVXXWW89dZbYY/DNQzD+Otf/2r069fPcLvdxpgxY4z33nuv2ccT29sIGIDhcrmMrl27GsOGDTPOPfdc49NPPw1b33zM7q233hpxf0uXLjUOPfRQw+PxGF27djWmTp1qfPfdd451zMftfvfdd8Ypp5xipKWlGd26dTNmz55tVFdXh7UvtIbl5eXGvHnzjAEDBhhJSUlGVlaWMW7cOOO2226zHilsGIbx0UcfGaNHjzaSkpKafQz0hx9+aFx00UXG8OHDjfT0dCMxMdHo06ePMWvWLOOnn35yrBtaW7N+kb7s79XmzZuNiy66yMjLyzMSExONnJwc46ijjjIefPDBqO2y16E175PZLvOca2trjcsvv9wYOXKkkZaWZnTp0sUYOXKk8de//tWxTUVFhXHGGWcYXq/XAKxHLZuPYn7uuefCjhPt0c/Dhg0zli9fbhxyyCFGcnKy0bdvX+Oee+4J276lfT1a2yI9+tkwWtcXt27d6pgf7ZHUIiIi0j5chqGkNBERkZ2xYMECFi5cyNatW8nKyuro5oiIiIhIB1Nmi4iIiIiIiIhIDGmwRUREREREREQkhjTYIiIiIiIiIiISQ8psERERERERERGJIV3ZIiIiIiIiIiISQxpsERERERERERGJIQ22iIjYLFiwAJfL1dHNkN1Ifn4+s2bN6uhmSAcaP34848ePj8m+lixZgsvlYvny5W3eh/qkiIhIx9Ngi4hIM/70pz/x0ksvdXQzdgsVFRXMnz+fY489loyMDFwuF0uWLGnVPkpKSjjvvPPIzs6mS5cuTJgwgS+++KJd2vvqq6+yYMGCdtn37uCHH37gD3/4A+PGjSM5ORmXy0VRUVG7He+jjz7isMMOIyUlhZycHC655BIqKioirvvFF19wwgknkJGRQUpKCsOHD+euu+5qlzYtWLCAkpKSmO+7KX/9619b/dnozN58803OPvtshg8fTnx8PPn5+e16vIcffpghQ4aQnJzMwIEDufvuu6Ou+8wzz3DIIYfQpUsXvF4v48aN45133mnX9omIiGiwRUSkGRpsablt27Zx/fXXs3LlSkaOHNnq7RsbGznuuON48sknmT17Nrfccgtbtmxh/PjxrFq1KubtffXVV1m4cGHM97u7+Pjjj7nrrrsoLy9nyJAh7XqsgoICjjrqKKqqqrj99ts555xzePDBBzn11FPD1n3zzTc55JBD2LJlC9deey133nknxx9/PD///HPM2/XRRx+xcOFCDbbspCeffJInn3yS9PR0cnNz2/VYDzzwAOeccw7Dhg3j7rvv5pBDDuGSSy7h5ptvDlt3wYIFTJ8+nby8PG6//XZuvPFG9ttvP9avX9+ubRQREUno6AaIiMieo2fPnmzcuJGcnByWL1/OgQce2Krtn3/+eT766COee+45TjnlFABOO+00Bg0axPz583nyySdj0s7Kykq6dOkSk33tzk444QRKSkpIS0vjtttuo6CgoN2OddVVV9GtWzfeffddunbtCvhvdzn33HN58803OeaYYwAoKytjxowZHHfccTz//PPExbXP34XUB2LrT3/6Ew899BCJiYkcf/zxrFixol2OU11dzdVXX231D4Bzzz2XxsZGbrjhBs477zy6desGwCeffML111/PX/7yF/7whz+0S3tERESi0ZUtIrLX+uCDDzjwwANJTk6mf//+PPDAA2HruFwuKisreeyxx3C5XLhcrhZnIRQVFeFyubjtttu499572WeffUhJSeGYY45h3bp1GIbBDTfcQO/evfF4PPzyl79k+/btjn28/PLLHHfcceTm5uJ2u+nfvz833HADDQ0N1jorV67E4/EwY8aMsPOLj4/nj3/8Y+uL00Zut5ucnJw2b//888/To0cPTj75ZGtednY2p512Gi+//DK1tbWt3qeZw/Pdd99xxhln0K1bNw477DBmzZrFvffeC2C9t/a8nsrKSi699FLy8vJwu93su+++3HbbbRiG0ebzA3j00UdxuVw88sgjjvl/+tOfcLlcvPrqqzu1/9bIyMggLS2tRes2NjayePFihg0bRnJyMj169OD8889nx44dzW5bVlbGW2+9xW9+8xtroAVgxowZpKam8uyzz1rznnzySTZv3sxNN91EXFwclZWVNDY2tv7kbMwclP/85z9ceOGFdO/end69e7NgwQIuv/xyAPr162f1AfNWqvr6em644Qb69++P2+0mPz+fq666qk390C4/P59vv/2W//znP9YxQzNfamtrmTt3rnU73UknncTWrVsd6xiGwY033kjv3r1JSUlhwoQJfPvttzvVtrbKzc0lMTGxReuWlJQwZ84c67M1YMAAbr755ha9z8uWLaO4uJgLL7zQMf+iiy6isrKSV155xZq3ePFicnJy+P3vf49hGFFvWRMREWkPurJFRPZK33zzDccccwzZ2dksWLCA+vp65s+fT48ePRzrPf7445xzzjmMHTuW8847D4D+/fu36lhPPPEEPp+Piy++mO3bt3PLLbdw2mmnceSRR/Luu+/yxz/+kdWrV3P33Xdz2WWXOX4JX7JkCampqcydO5fU1FTeeecdrrvuOsrKyrj11lsBGDJkCDfccAOXX345p5xyCieccAKVlZXMmjWLwYMHc/311zfZvtraWsrLy1t0LllZWa0699b68ssvOeCAA8KuZhg7diwPPvggP/74IyNGjGjTvk899VQGDhzIn/70JwzDYP/992fDhg289dZbPP744451DcPghBNOYNmyZZx99tmMGjWKN954g8svv5z169dzxx13tPkczzzzTF544QXmzp3L0UcfTV5eHt988w0LFy7k7LPPZsqUKU1uX1FRQU1NTbPHSUxMJD09vc3tDHX++eezZMkSzjzzTC655BIKCwu55557+PLLL/nwww+b/EX7m2++ob6+njFjxjjmJyUlMWrUKL788ktr3tKlS+natSvr16/nxBNP5Mcff6RLly789re/5Y477iA5ObnN53DhhReSnZ3NddddR2VlJZMnT+bHH3/kqaee4o477rD6d3Z2NgDnnHMOjz32GKeccgqXXnopn376KYsWLWLlypW8+OKLbW7H4sWLufjii0lNTeXqq68GCPvZc/HFF9OtWzfmz59PUVERixcvZvbs2TzzzDPWOtdddx033ngjU6ZMYcqUKXzxxRccc8wx+Hy+FrVjx44djoHbaFJSUkhJSWnFGUZXVVXFEUccwfr16zn//PPp06cPH330EfPmzWPjxo0sXry4ye3NvhLal0aPHk1cXBxffvklv/nNbwB4++23GTduHHfddRc33ngjxcXF5OTkcPXVVzN79uyYnI+IiEhUhojIXujEE080kpOTjTVr1ljzvvvuOyM+Pt4I/dHYpUsXY+bMma0+RmFhoQEY2dnZRklJiTV/3rx5BmCMHDnSqKurs+ZPnz7dSEpKMmpqaqx5VVVVYfs9//zzjZSUFMd6DQ0NxmGHHWb06NHD2LZtm3HRRRcZCQkJxn//+99m2/noo48aQIu+WuO///2vARiPPvpoi7fp0qWLcdZZZ4XNf+WVVwzAeP3111vVBsMwjPnz5xuAMX369LBlF110UcTzeumllwzAuPHGGx3zTznlFMPlchmrV6+25vXt27fV/WPjxo1GRkaGcfTRRxu1tbXG/vvvb/Tp08coLS1tdtuZM2e26L064ogjWtWmW2+91QCMwsLCsGXvv/++ARhPPPGEY/7rr78ecX6o5557zgCM9957L2zZqaeeauTk5FjT++23n5GSkmKkpKQYF198sfHPf/7TuPjiiw3AOP3001t1Tiazjx922GFGfX29Y1m08y4oKDAA45xzznHMv+yyywzAeOedd6x5RxxxRKvrPWzYsIjbmG2dOHGi0djYaM3/wx/+YMTHx1s/S7Zs2WIkJSUZxx13nGO9q666ygBa1Cf79u3bor40f/78Vp3bcccdZ/Tt2zfishtuuMHo0qWL8eOPPzrmX3nllUZ8fLyxdu3aJvd90UUXGfHx8RGXZWdnW31k+/btBmBkZmYaqampxq233mo888wzxrHHHmsAxv3339+qcxIREWktXdkiInudhoYG3njjDU488UT69OljzR8yZAiTJk2K+W0cp556quMKg4MOOgiA3/zmNyQkJDjmP/XUU6xfv5599tkHAI/HYy0vLy+ntraWww8/nAceeIDvv//eCqGNi4tjyZIljBw5ksmTJ7N8+XKuueaasL/+RjJp0iTeeuutmJzrzqqursbtdofNN69mqK6ubvO+f/e737V43VdffZX4+HguueQSx/xLL72U559/ntdee22n/jKek5PDvffey/Tp0zn88MMpKCjgrbfectxiE80VV1xh/eW+KWZuRSw899xzpKenc/TRR7Nt2zZr/ujRo0lNTWXZsmWcccYZUbc337do7639fa2oqKCqqorf/e531tOHTj75ZHw+Hw888ADXX389AwcObNN5nHvuucTHx7doXfPnwNy5cx3zL730Um677TZeeeUVJkyY0KZ2tMR5553nuK3t8MMP54477mDNmjXst99+LF261Lpizr7enDlz+NOf/tSiYzzxxBMt+kyZP49i4bnnnuPwww+nW7dujr40ceJE/vznP/Pee+/x61//Our21dXVJCUlRVxm70vmLUPFxcU8/fTTTJs2DYBTTjmFESNGcOONN3L++efH6rRERETCaLBFRPY6W7dupbq6OuIvbPvuu2/MB1vsAzqANfCSl5cXcb49A+Pbb7/lmmuu4Z133qGsrMyxfmlpqWO6f//+VgbF8OHDufbaa1vUvp49e9KzZ8+WnUw783g8EfMwzNtm7INPrdWvX78Wr7tmzRpyc3PD8kzMJ/asWbOmze0wnX766fzjH//glVde4bzzzuOoo45q0XZDhw5l6NChO3381li1ahWlpaV079494vItW7YA/j5p/+U9KSmJjIwM632L9t7a31fz9fTp0x3rnXHGGTzwwAN8/PHHbR5saW0fiIuLY8CAAY75OTk5eL3emPSBpoT+3DAHz8yfD+bxQ2uRnZ3d4oG2Qw89dGeb2WqrVq3i66+/tm7VCmX2pa1btzpucUpNTSU1NRWPxxP1Nil7XzK/JyYmWmHb4B+YnjZtGvPnz2ft2rVhdRYREYkVDbaIiLSzaH9JjzbfCASwlpSUcMQRR9C1a1euv/56+vfvT3JyMl988QV//OMfI4ZJvvnmmwBs2LDByidoTnV1ddjATTQ7E37bEubTjEKZ83bmkbI7M1DTHoqLi1m+fDkA3333HY2NjS168k7ogEY05kBHLDQ2NtK9e3eeeOKJiMvNX5x///vf89hjj1nzjzjiCN59911rMC/ae2t/X3Nzc/n222/DMkzMgZ6WBPJG05Y+YL9qZFdq7udDLIQOaERjDnTEQmNjI0cffTRXXHFFxOWDBg0C4MADD3QMaM2fP58FCxbQs2dPGhoa2LJli2Pwz+fzUVxcbPWljIwMkpOT8Xq9YbW09yUNtoiISHvRYIuI7HWys7PxeDysWrUqbNkPP/wQNq+jftl69913KS4u5oUXXuAXv/iFNb+wsDDi+vfffz9vvfUWN910E4sWLeL888/n5ZdfbvY4zzzzDGeeeWaL2hTLX/QiGTVqFO+//37YwMOnn35KSkqK9YtYrER7b/v27cvSpUspLy93XN3y/fffW8t31kUXXUR5eTmLFi1i3rx5LF68OOyWlUhCBzSiMQc6YqF///4sXbqUQw89tMkBi9BbnMwrLIYPH05CQgLLly/ntNNOs5b7fD4KCgoc80aPHs1bb73F+vXr2Xfffa35GzZsAIh6RURbNdUHGhsbWbVqlXVFE8DmzZspKSnZ6T6wsz9XzOOvWrXKcZvP1q1bWzwgFTqgEY050BEL/fv3p6KigokTJza5XugtTuY5jho1CoDly5c7wqSXL19OY2OjtTwuLo5Ro0bx3//+F5/P57j1qL36koiIiJ0GW0RkrxMfH8+kSZN46aWXHJeRr1y5kjfeeCNs/S5dulBSUrKLWxn8y7Z9gMPn8/HXv/41bN3CwkIuv/xyfvWrX3HVVVeRmZnJ7373O/7+97+HPRI6VEdltmzcuJHS0lL69+9vPcnmlFNO4fnnn+eFF16wLv3ftm0bzz33HFOnTo2Y+bEzunTpAvivIvJ6vdb8KVOm8OCDD3LPPfcwb948a/4dd9yBy+Vi8uTJO3Xc559/nmeeeYa77rqLiy++mK+++oprrrmG448/vtkBpY7IbDnttNP461//yg033BCWB1JfX09FRQVerzfqLU7p6elMnDiRf/zjH1x77bXWANbjjz9ORUUFp556quNYf/7zn3n44Yc58sgjrfl/+9vfSEhICHtE8s6y9wG7KVOmcNVVV7F48WLHY+Fvv/12AI477ridPu7O/FyZOHEiiYmJ3H333RxzzDHW4E1zT/Ox64jMltNOO40FCxbwxhtvMGnSJMeykpISUlNTSUhIiHqL05FHHklGRgb33XefY7DlvvvuIyUlxfG+TJs2jU8++YTHHnuMc889F/DfavTEE08wdOjQnbpSTkREpDkabBGRvdLChQt5/fXXOfzww7nwwgupr6/n7rvvZtiwYXz99deOdUePHs3SpUu5/fbbyc3NpV+/flbIbXsaN24c3bp1Y+bMmVxyySW4XC4ef/zxsKtLDMPgrLPOwuPxcN999wH+x/T+85//5Pe//z0TJ05s8peKWGe23HPPPZSUlFh/Pf6///s/fv75Z8D/OFszm2bevHk89thjFBYWkp+fD/gHWw4++GDOPPNMvvvuO7KysvjrX/9KQ0MDCxcudBxn1qxZYdu31ujRowG45JJLmDRpEvHx8Zx++ulMnTqVCRMmcPXVV1NUVMTIkSN58803efnll5kzZ06rH/9tt2XLFi644AImTJhghezec889LFu2jFmzZvHBBx80eTtRLDNbSktLufvuuwH48MMPrbZ4vV68Xq/VviOOOILzzz+fRYsWUVBQwDHHHENiYiKrVq3iueee484773TkYkRy0003MW7cOI444gjOO+88fv75Z/7yl79wzDHHcOyxx1rr7b///px11lk88sgj1NfXW1foPPfcc8ybN8/RlxcsWMDChQtZtmxZmwdhzD5w9dVXc/rpp5OYmMjUqVMZOXIkM2fO5MEHH7Ru6fvss8947LHHOPHEE3c6HHf06NHcd9993HjjjQwYMIDu3bs7Bpeak52dzWWXXcaiRYs4/vjjmTJlCl9++SWvvfZaix/RHsvMlq+//pp//etfAKxevZrS0lJuvPFGAEaOHMnUqVMBuPzyy/nXv/7F8ccfz6xZsxg9ejSVlZV88803PP/88xQVFTXZfo/Hww033MBFF13EqaeeyqRJk3j//ff5xz/+wU033eS4de7888/nb3/7GxdddBE//vgjffr04fHHH2fNmjX83//9X8zOXUREJKKOfBSSiEhH+s9//mOMHj3aSEpKMvbZZx/j/vvvtx4TbPf9998bv/jFLwyPx9PiR6oaRvDRz7feeqtj/rJlywzAeO655xzzzUe+2h/X/OGHHxoHH3yw4fF4jNzcXOOKK64w3njjDQMwli1bZhiGYdx5550GYPzzn/907G/t2rVG165djSlTprSwIrHR1ONk7Y/XNR9hHPrI3e3btxtnn322kZmZaaSkpBhHHHFExEdY/+pXvzI8Ho+xY8eOJttjvqdbt24NW1ZfX29cfPHFRnZ2tuFyuRzvfXl5ufGHP/zByM3NNRITE42BAwcat956q+Mxu+b5tubRzyeffLKRlpZmFBUVOea//PLL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zz1WHsrLL7/Mxx9/TEJCAjNmzOCVV17huOOO4+9//zsZGRlUVlZy4IEHcsopp1j7feqppxgyZAgTJkzggw8+4PDDD4/Yhv3337/NT/NpaGjgzTff5PHHH+ell14iLy+PGTNmsGTJEvr06dOmfX7zzTfccccdvP/++3i9XrZv305ZWRkrV67kiSeeYL/99nOsv2LFCnr37k1WVlabjieyp9Ngi4iIiIiItIrL5Wr1FRiRNDY20tDQQJcuXVo82BIL++67L/vuuy8AQ4YMYeLEiQCMGDGCV199FYC3336bTz75hDFjxgD+22XMW2/uuOMO/vWvfwGwdu1a1q5dS2JiIvvuu691Rcn+++9PUVFR1MGWSZMmMW/ePHbs2EG3bt1a1f4//elP/OUvf2HatGksXbqUcePGtbIC4ZYtW8a0adPwer0AZGRkUFZWxqBBg8IGWsAf8HvMMcfs9HGlZdrryUEaEGg/qq2IiIiIiOxV3G639TouLs6ajouLo6GhAfAPBJ177rnMnz/fse2yZcv48MMP+fTTT0lOTmbMmDHU1taSmJjo2G98fLy1r0hGjBjBAQccwLPPPsv555/fqvb/9re/5fLLL4+Y+RJrka4kqqmp4aWXXuL1119v9+OL7K6U2SIiIiIiInusd999l8WLF7d6u6OOOopnnnmG4uJiALZs2cLGjRspKysjMzOT5ORkCgoK+Oqrr9rctuuuu44777yTxsbGVrU1Pz9/pwZaIh3nyCOP5JlnnqG0tBQIZrJE8uijjzJ27FgOPvjgNrdBWieB9slr6airL/7973+z7777MnDgQP72t791UCval65sERERERERCTFs2DCuvvpqjjrqKBobG3G73SxZsoRjjz2W++67j6FDhzJs2LCdetLRcccdx6pVq1i/fj15eXkxbH3rDR8+nN///vcceuihJCQkcMwxx3DhhRdGXDcxMZG77757F7dQ9hT19fXMnTuXZcuWkZ6ezujRoznppJPIzMzs6KbFlAZbRERERERkr5Gfn8/y5cut6eeff956ffDBB/Pvf//bmv71r3/Nr3/967B9RLt9xr7f2267rUXtmTNnTovW2xXOOecczjnnHMc8+znZ15Nda0/KbPnss88YNmwYvXr1AmDy5Mm8+eabTJ8+vQNa0350G5GIiIiIiIhIJ9aZHv383nvvMXXqVHJzc3G5XLz00kth69x7773W7W4HHXQQn332mbVsw4YN1kALQK9evVi/fn0bWtK5abBFRERERERERFqksrKSkSNHcu+990Zc/swzzzB37lzmz5/PF198wciRI5k0aRJbtmzZxS3tWLqNSERERERERKQTa+/biMrKyhzz3W634+ladpMnT2by5MlR93n77bdz7rnncuaZZwJw//3388orr/DII49w5ZVXkpub67iSZf369YwdO3bnTqQT0pUtIiIiIiIiInuxvLw80tPTra9Fixa1aT8+n4/PP/+ciRMnWvPi4uKYOHEiH3/8MQBjx45lxYoVrF+/noqKCl577TUmTZoUk/PoTHRli4iIiIiIiEgnZj76uT32C7Bu3Tq6du1qzY92VUtztm3bRkNDAz169HDM79GjB99//73/mAkJ/OUvf2HChAk0NjZyxRVX7HFPIgINtoiIiIiIiIjs1bp27eoYbGlvJ5xwAieccMIuO15H0GCLiIiIiIiISCe2uzz6OSsri/j4eDZv3uyYv3nzZnJycmJ8tM5NmS0iIiIiIiIistOSkpIYPXo0b7/9tjWvsbGRt99+m0MOOaQDW7br6coWERERERERkU4skfbJbGnLPisqKli9erU1XVhYSEFBARkZGfTp04e5c+cyc+ZMxowZw9ixY1m8eDGVlZXW04n2FhpsEREREREREZEWWb58ORMmTLCm586dC8DMmTNZsmQJ06ZNY+vWrVx33XVs2rSJUaNG8frrr4eF5u7pNNgiIiIiIiIi0ol1psyW8ePHYxhGk+vMnj2b2bNnt61RewhltoiIiIiIiHSA4uJiunfvTlFRUUc3pUOdfvrp/OUvf+noZojElAZbREREREREOsBNN93EL3/5S/Lz83fpcd977z2mTp1Kbm4uLpeLl156aZceP9Q111zDTTfdRGlpaYe2ozNLIJjbEssv3erSfjTYIiIiIiIiew2fz9fRTQCgqqqKhx9+mLPPPjvqOtHaumHDBurr69t87MrKSkaOHMm9997b5n3E0vDhw+nfvz//+Mc/OropIjGjwRYREREREdljjR8/ntmzZzNnzhyysrKYNGlSRzcJgFdffRW3283BBx9szWtpWx966CF69+7NZZddxjfffNPqY0+ePJkbb7yRk046qVXbffDBByQmJlJTU2PNKyoqwuVysWbNGsaPH8/FF1/MnDlz6NatGz169OChhx6ynkSTlpbGgAEDeO2118L2PXXqVJ5++ulWn8veoj2uammvJxyJnwZbRERERESkTXw+X9Sv0Csvoq1XV1dHXV1di9Ztq8cee4ykpCQ+/PBD7r///jbvJ5bef/99Ro8eHTa/JW394x//yJ133snKlSs54IADOOCAA7jrrrvYunVru7a5oKCAIUOGkJycbM378ssv6datG3379rXan5WVxWeffcbFF1/MBRdcwKmnnsq4ceP44osvOOaYY/jtb39LVVWVY99jx47ls88+o7a2tl3PQWRX0S1aIiIiIiLSJosWLYq6bODAgZxxxhnW9G233RY2qGLq27cvs2bNsqbvvPPOsF/GAebPn9+mdg4cOJBbbrmlTdu2lzVr1pCbmxs2vyVtTU5OZtq0aUybNo0tW7bw5JNPsmTJEi677DKmTJnCzJkzmTp1KgkJsf1176uvvmL//fd3zCsoKGDkyJHW9MiRI7nmmmsAmDdvHn/+85/Jysri3HPPBeC6667jvvvu4+uvv3Zc1ZObm4vP52PTpk3WwI0EdaanEUnL6MoWERERERHZo0W6gqSjVVdXO64QMbW2rd27d2fOnDl88cUXvPzyy3z88cecfPLJrFixIlZNtRQUFDBq1CjHvC+//NIxb7/99rNex8fHk5mZyYgRI6x5PXr0AGDLli2O/Xg8HoCIg2wCCfGQmBD7r4T4jj6zPZcGskREREREpE3mzZsXdVlcnPPvupdddlnYOo2NjZSXl9O1a1fH/N///vexaWBAly5drNdFRUX88pe/ZPjw4Xz22WdMnDiRSZMmsWjRIiorK3nxxRcZOHAgAI8//jh33XUXPp+Po446ittvvx2A448/no0bN1JbW8u8efP49a9/be131KhRfPbZZ+y33348/fTTuFyuiG3Kyspix44dTba1JcrLy3n++ed5/PHHee+99zjiiCOYOXMmQ4cObdV+mtPQ0MCKFSvCrmz54osv+NWvfmVNJyY6U0BcLpdjnlmPxsZGx3rbt28HIDs7O6btFukoGmwREREREZE2SUpK2ql1GxsbSUxMDPsFvTX7bYuVK1fy7LPPMmDAAIYPH05qaiqffvopDzzwAPfcc4+Vh2JeKZKQkMCMGTN45ZVXOO644/j73/9ORkYGlZWVHHjggZxyyinWfp966imGDBnChAkT+OCDDzj88MMjtmH//fdv89N3GhoaePPNN3n88cd56aWXyMvLY8aMGSxZsoQ+ffq0uS5N+eGHH6ipqXHc+vTxxx+zfv36sKtd2mLFihX07t2brKysnd7XnighARIij9vt3H4NoCH2+xXdRiQiIiIiInuZfffdl3333Zf4+HiGDBnCxIkTARgxYgRFRUUAvP3223zyySeMGTOGUaNG8cknn7B69WoA7rjjDkaOHMm4ceNYu3Yta9eutfY7dOhQXC4X+++/v7WvSCZNmsS3334b8eqW5vzpT39i+vTppKWlsXTpUn744QeuvvrqFg+0VFRUUFBQQEFBAQCFhYUUFBRY5xGJue7dd9/NqlWreO2115gxYwYQm8dpv//++xxzzDE7vR+RzkJXtoiIiIiIyF7F7XZbr+Pi4qzpuLg4Ghr8f+ZvbGzk3HPPDQvlXbZsGR9++CGffvopycnJjBkzhtraWhITEx37jY+Pt/YVyYgRIzjggAN49tlnOf/881vV/t/+9rdcfvnlETNfWmL58uVMmDDBmp47dy4AM2fOZMmSJRG3KSgoYNKkSfzvf/9jxIgRDB06lIULF3LBBRdw11138fjjj7epLQA1NTW89NJLvP76623ex54uMR4S2+HKlkQj9vsUPw22iIiIiIjIHuvdd99t03ZHHXUUp556KrNnzyYzM5MtW7bQ0NBAWVkZmZmZJCcnU1BQwFdffdXmtl133XVcfvnlnHvuucTFxbW4rfn5+W0+JsD48eMxjNb9lv3VV19x4IEHcuONNzrm2584Fan9ka7uCT32o48+ytixYx1PJxLZ3WmwRUREREREJMSwYcO4+uqrOeqoo2hsbMTtdrNkyRKOPfZY7rvvPoYOHcqwYcN26klHxx13HKtWrWL9+vXk5eXFsPWx99VXX3HWWWe1y74TExO5++6722Xfe4p2zWyRdqHBFhERERER2Wvk5+ezfPlya/r555+3Xh988MH8+9//tqZ//etf8+tf/zpsH9Fud7Hv97bbbmtRe+bMmdOi9TrSpk2b2Lx5s+MRzrF0zjnntMt+RTqSBltEREREREQkqpycnFbfdiSxlRgPie3weJvExubXkbbR04hERERERERERGJIV7aIiIiIiIiIdGbxtM+lEu2QAyN+nerKlvfee4+pU6eSm5uLy+XipZdecix3uVwRv2699dao+1ywYEHY+oMHD27nMxERERERERGRvVWnurKlsrKSkSNHctZZZ3HyySeHLd+4caNj+rXXXuPss8/mV7/6VZP7HTZsGEuXLrWmExI61WmLiIiIiIiIRJdA+1wqocyWdtOpRh0mT57M5MmToy7PyclxTL/88stMmDCBffbZp8n9JiQkhG0rIiIiIiKto5BU6Sjqe7K76VS3EbXG5s2beeWVVzj77LObXXfVqlXk5uayzz778Otf/5q1a9fughaKiIiIiOwZ4uPjAfD5fB3cEtlbVVVVAZCYmNjBLekgCe34Je1ity3tY489RlpaWsTbjewOOugglixZwr777svGjRtZuHAhhx9+OCtWrCAtLS3iNrW1tdTW1lrTZWVlANTV1VFXVxe7k5C9itl31IdkZ6gfSSyoH0ksqB/tXQzDIDk5mS1bthAfH09cXGz+ZmsYBj6fj+rqalwuJXVKOMMwqKqqYuvWrXTt2pXGxkYaG533vujnkHRGLqOTXo/lcrl48cUXOfHEEyMuHzx4MEcffTR33313q/ZbUlJC3759uf3226NeFbNgwQIWLlwYNv/JJ58kJSWlVccTEREREdkTxMXFkZ2dvfdeWSAdprGxkfLycsrLyyMur6qq4owzzqC0tJSuXbvu4ta1r7KyMtLT0ynNh67tcF9KWSOkF7FH1q6j7ZZXtrz//vv88MMPPPPMM63e1uv1MmjQIFavXh11nXnz5jF37lxruqysjLy8PCZMmEBmZmab2ixSV1fHW2+9xdFHH63/pEibqR9JLKgfSSyoH+2dGhsbqauri1l+Rn19PR999BHjxo3TQywkIpfLRUJCgnUrWyTFxcW7sEUdJA7/459lt7Fb/kR7+OGHGT16NCNHjmz1thUVFfz000/89re/jbqO2+3G7XaHzU9MTNR/JmSnqR9JLKgfSSyoH0ksqB/tfSL9P7mt6urqqK+vJzU1Vf1I2kx9RzqjThWQW1FRQUFBAQUFBQAUFhZSUFDgCLQtKyvjueee45xzzom4j6OOOop77rnHmr7sssv4z3/+Q1FRER999BEnnXQS8fHxTJ8+vV3PRURERERERCQmFJC72+lUpV2+fDkTJkywps1beWbOnMmSJUsAePrppzEMI+pgyU8//cS2bdus6Z9//pnp06dTXFxMdnY2hx12GJ988gnZ2dntdyIiIiIiIiIistfqVIMt48ePb/b+z/POO4/zzjsv6vKioiLH9NNPPx2LpomIiIiIiIh0jATaJ7NFDwFrN53qNiIRERERERERkd2dBltERERERET2Yi7Xwo5ugjQnvh2/pF1osEVEREREREREJIY6VWaLiIiIiIiIiIRQZstuR1e2iIiIiIiIiIjEkK5sEREREREREenM4tFv77sZvV0iIiIiIiJ7OH8Ibq8mlv+tRfsxjHNi1CKRPZsGW0REREREREQ6s/Z6cpDRDvsUQJktIiIiIiIiIiIxpStbRERERERERDqzBPTb+25GV7aIiIiIiIiIiMSQxsZERERERGS3FAx1XY9hzO/QtnQWzQXhQn4L54UzjIktDNLV+xFzurJlt6O3S0RERERERKQz02DLbke3EYmIiIiIiIiIxJDGxkREREREREQ6szja59HPje2wTwE02CIiIiIiIp1c0zkk+cD6FmSJ7Hk5Is3XJXS6KPC6b6uPZRgDcbmWRthv6HoTcbkWNvl+GMY5rT6+yO5Ggy0iIiIiIiIinVl7ZbYY7bBPAZTZIiIiIiIiIiISU7qyRURERERERKQz05Utux1d2SIiIiIiIiIiEkO6skVEREREpAOY4aYKC3WKHqyaH2FeUSCQtSV7bj5EtzO/F9HDcPMjTBcROQS3L5AJpLXq2IaRiMu1Kso+7euZIbqHNrHOxAjvw54XXhxz8ehpRLsZXdkiIiIiIiIiIhJDurJFREREREREpDNTZstuR1e2iIiIiIiIiIjEkK5sEREREREREenM4mmf396V2dJuNNgiIiIiItLOmgp93Z1DW3dWevrfeeqpLNLT/051tf1+hvyQNc3p0IDWvq0Ibm26LZGDW027PsA1clvyo0yH1wUSiRyoWwSejFa2pS7CsUOtb/a9CAbohu7LGV7c0j5vhgYnJ9e0aH2RXUmDLSIiIiIiIiKdWXs9jag99imABltEREREREREZA/2r3/9q9XbHH300Xg8njYfU4MtIiIiIiIiIp1Zez2NaC/JbDnxxBNbtb7L5WLVqlXss88+bT6mBltEREREREREOjMNtuy0TZs20b179xatm5aWttPH02CLiIiIiEgMtSzY1D4v0jJTUZMBurtTeG6k8/B4zNTaPoD5uojIIatraD6kNZKdCW617yO0/bENzW2+35ivQ88jWl3WgycxwvyBkXNzm7I+0n5CVBOlHcH2RH4f1gCHWlOhQcWhfdwMxQ3KByqab5/s1WbOnNmqW4J+85vf0LVr1506pgZbRERERERERDozXdmyUx599NFWrX/fffft9DHjdnoPIiIiIiIiIiK7kU8//bRd96/BFhEREREREZHOLI7g459j+bUXjwiceuqp7bp/3UYkIiIiIiIiInuc0047LeJ8wzDYvn17ux5bgy0iIiIi0iL20EqPx8VTT2WRnv53qqsNa/7uFNgaC9HDa/MjzIsU0JoJFDdxhKIo+/Ivi3b8zvA+tCwoeE3gex7+P7ODv06h6wW2jRj42ozqfKAu6mLDSGwyQNcfnhtpyfomg1xbInR7f0ivqShCmyKF4eYDiRCW/ZkfPQg3r3XtbJH1+U0vjxqgu4bgea5xBBWHhuUG5Yd8793iZu622iuzpaEd9tmJLF26lMcff5zU1FTHfMMweO+999r12BpsEREREREREZE9zvjx40lLS+MXv/hF2LL99tuvXY+twRYRERERERGRzkxXtrTJCy+8EHXZW2+91a7H3ovjcERERERERERkb7Fp06ZddiwNtoiIiIiIiIh0Zu3xJCLzq5NZt24d48ePZ+jQoey3334899xzMdv3McccE7N9NUe3EYmIiIhIGJdrIeHpmvm21wZQBfQBgumh0QNj12MY82PYwl0vck0gPPSziPBg00wgLXzTbomwI8J8ANYDRzTRoqIIx/aL/D6073vQfCBuUeC7vTa9gR/w9yN7+G2kwFcil38IsLKJhlUCJdGDdV2uOqKHEK+PGJ4bKTQ39PyjBeY618u3zV8acpy++GvSK2T9CLXxAl1C5kULwc2JMr89rQc8+eHzq8HZx5sLy/VP+1+vsm2zFwTk7kUSEhJYvHgxo0aNYtOmTYwePZopU6bQpUtoJ289wzCaXylGNNgiIiIiIiIi0pntRZktPXv2pGfPngDk5OSQlZXF9u3bYzLY4or8aLF2oduIRERERERERKRF3nvvPaZOnUpubi4ul4uXXnopbJ17772X/Px8kpOTOeigg/jss8/adKzPP/+choYG8vLa43nl7UtXtoiIiIiIiIh0ZvG0z2/v9a3fpLKykpEjR3LWWWdx8sknhy1/5plnmDt3Lvfffz8HHXQQixcvZtKkSfzwww90794dgFGjRlFfH37wN998k9zcXAC2b9/OjBkzeOihh1rfyE5Agy0iIiIiEiFvYwTQNcKaZp5CA/6sjTxalrCY0SE5Ijuj+dwaiJzP0jfCehFyNsxd10TJE6nOB+qitC56nos/T2RpxG1amivSUuHvab7tdRHhGSRrQtapA34ATyKOzJYBBPI8bCL9YTuniWWmZGBVE8vXR89z8bchP2x2eI6L87z8mSJmbdYHvptveOh69saFZrSsD9TGxkt4PouX8EggL/5zDxWpts0ZDqxo5TahbSmJMD80T6c6cn6Ls0/3DdQsPzCdD5TtROOktSZPnszkyZOjLr/99ts599xzOfPMMwG4//77eeWVV3jkkUe48sorASgoKGjyGLW1tZx44olceeWVjBs3LmZtj4/fdYnAGmwRERERERER6czaK7MlsM+yMueAldvtxu12t3p3Pp+Pzz//nHnz5lnz4uLimDhxIh9//HGL9mEYBrNmzeLII4/kt7/9bavb0JQvv/wypvtrijJbRERERERERDqzdn70c15eHunp6dbXokWL2tTMbdu20dDQQI8ePRzze/TowaZNm1q0jw8//JBnnnmGl156iVGjRjFq1Ci++eabNrWnI+nKFhEREREREZG92Lp16+jaNXjraFuuaomVww47jMbGxnY9Rk1NDV9//TVbtmwJO9YJJ5wQk2NosEVERERERESkM2vn24i6du3qGGxpq6ysLOLj49m8ebNj/ubNm8nJyYmy1a71+uuvM2PGDLZt2xa2zOVy0dAQm+dha7BFRERE9jr24MrOGs7anloe/DoiZN5AgoGtgWBT+uAINo3qPxGOAZFCW2Hng1vbqunAV/N1aOirfZ2A0FDTSKGkA4EaoCewMUJjPMDq1obnro8Q3hpNUZsCc53b5Dv2F16b0MDXfGdtzNDgnkCtbbWsCAfOx18vO7Oug4Hvm2j0gUDL7mBwWp8feX516PxgmCusCYS5muuYAbn+aXsorv+7vUahgbj54R/V9fjP185LeBhupD7X07a+nbm/pmo4hsj9tCW8hAf4AiwnJA84cO7VEAyAtvdpexCxLXS6e2CRdLikpCRGjx7N22+/zYknnghAY2Mjb7/9NrNnz+7YxgVcfPHFnHrqqVx33XVhtzvFUqfKbGnued2zZs3C5XI5vo499thm9xurZ3yLiIiIiIiI7HIJ7fjVShUVFRQUFFhPFCosLKSgoIC1a9cCMHfuXB566CEee+wxVq5cyQUXXEBlZaX1dKKOtnnzZubOnduuAy3Qya5sae553QDHHnssjz76qDXd3L1kLXnGt4iIiIiIiIg0b/ny5UyYMMGanjt3LgAzZ85kyZIlTJs2ja1bt3LdddexadMmRo0axeuvv97ugxstdcopp/Duu+/Sv3//dj1Opxpsae553eAfXGnNvV4teca3iIiIiIiISKcVh/XkoJjvt5XGjx+PYRhNrjN79uxOc9tQqHvuuYdTTz2V999/nxEjRpCY6Lxd85JLLonJcTrVYEtLvPvuu3Tv3p1u3bpx5JFHcuONN5KZmRlx3bY+47u2tpba2uBNo+Yzx+vq6qiri3RvrEjzzL6jPiQ7Q/1IYkH9CDweV+BV3F5ZB48nDnCFzA39j7MLCA0JrMPMCfF46h3fg6IFC7oiHAP8/9MPbUvH9c9g3wB/2yK1OVJdQncUMu0GQh+ukRiYFxdYHsodYT/NHZeGCO0zRXqPQ/bYgro7a2TfZ6Q+E0dYO23n5EkO9Cd3yDpJEQ4c6ZYH8xfQuAjLQkXaJ0SuvdXAaAtC62QQPPfQOsc55tfV1eHxNIQsN/fXAJ6QfYe2z0P4uSQSvTZ24W+5s8nRlpua+mW/qfonErn+yTjPz1FvW00cta0LWz85qS4szkckmqeeeoo333yT5ORk3n33XVyuYMd3uVwxG2xxGc0NSXUQl8vFiy++aIXqADz99NOkpKTQr18/fvrpJ6666ipSU1P5+OOPiY8P/+Rv2LCBXr168dFHH3HIIYdY86+44gr+85//8Omnn0Y89oIFC1i4cGHY/CeffJKUlJSdPzkRERERERGJiaqqKs444wxKS0tj8kSdzqSsrIz09HRK74CuTQ6+tnH/1ZD+B/bI2kWTk5PDJZdcwpVXXklcXPvF2O5WV7acfvrp1usRI0aw33770b9/f959912OOuqomB1n3rx51n1n4O/geXl5TJgwIepVNCLNqaur46233uLoo48Ou1RNpKXUjyQW9rZ+lJ7+9whz+wS+JwM/RtlyA6Wle8Ytx+npfwZybXM2AAeHrJUXYcs+zknbU1I8yXU88te3OOvCo6musfWj6mhXRqyNMn9di9cvLZ0RZd22C68NhJ23ozYZQKpzceiTh3oSrneEeWZ8YDL+p+xsidLInyPMqwJKI8yvhshXvGyMMP9D7OdaWjo+5PMS/AyE18leI3t9EnEWYCN4QuppW+xx1/HIdW9x1t+OprrOVsfQaMXQtwggNP7Bi/8BWZEkA/+LsgxaV/stOC/gCav5hwRrEtq/zfn2moTUKJ7w87f3n0ixk+lAl5B5keIx+hL+RKdBBPtSAsGHJ4Uy++nmKMtpYlklkfurve72Wtv7d/VanJ0m0E/SgcDfwZNLiptolIiTz+dj2rRp7TrQArvZYEuoffbZh6ysLFavXh1xsKWtz/h2u90Rg3cTExP3iv+USvtSP5JYUD+SWNhb+lF1daSLeIO3EUW+TQSgcY+pT3V1I87zbCT8foFI9weEnn94PaprEqmutg+2RGtFtPsPmruvwXb0dng/wmsDTdcmgWbrUks4X4R59bbv9VHWiba/GiLXusn6h97HZGA/18TExJDPS/AzEF4ne43iQ14nNjFNxPOprkuk2mdbryV3p4W+bUaU9cB/6qH7tGtt7e37Cqu5i2BNovWlJmqUEOG49vZFOo9Id4+19P4F+518zd1G1Nx+o9W/gcjttp+X/Zzt/bs6tA8FXruxymnU7Bk/q5vUxicHtWi/e5mZM2fyzDPPcNVVV7XrcXbr0v78888UFxfTs2ekPx/sHs/4FhEREREREZFdo6GhgVtuuYU33niD/fbbL2wg//bbb4/JcTrVYEtFRQWrV6+2ps3ndWdkZJCRkcHChQv51a9+RU5ODj/99BNXXHEFAwYMYNKkSdY2Rx11FCeddJI1mDJ37lxmzpzJmDFjGDt2LIsXL+5Uz/gWERERERERaVI87fM0ovbYZyf3zTffsP/++wOwYsUKxzJ7WO7O6lSDLU09r/u+++7j66+/5rHHHqOkpITc3FyOOeYYbrjhBsctPz/99BPbtm2zpjv7M75FREQkdlyuv4XMyQ98heob+L4mynKAjAj7M63HMOa3tnm7jMu1EOgVMjc/5HXfJpYTnkNCyC7N/371xHn5vzcRvo3QqOr8CDMjM4yBuFxLw+aHvx9tex/C62NvWxHO2mQCaY5j4slw7jC01N6QTSLdvT4YKLdNRwu+TG5i/qoI8yuBkgjvXTU4z3M9cIRjFX/Nnes4a24uKyL6Zygx5Fzyw+tjj3gxn1DTHedtJqHb9MOZNZJDMEPEbgLwPZGNwR9dE6q1oaNeoMQ2vRrn56XaXtc1RK6VvU75zvP14uw/Xpz9wEt4Pkuk+oQaHNJugMG1UOIOrp9aDwMa4Psoj2gKPa5dauC4myIsSwYKm9gWnHVdTrAm6/Od65nzN+M/J/CX+O1m9i8SsGzZsl1ynE412NLc87rfeOONZvdRVFQUNq8zP+NbREREREREpEnKbImZdevWkZcXKRQ+tvbC0oqIiIiIiIjI3qhv375kZGQwcuRIRo0aZX35fD7uuusuHnvssZgcR4MtIiIiIiIiIp1ZPO3z2/temNlSWFjIl19+SUFBAV9++SXPPvssGzZsAKBr164xO44GW0REREREREQ6M91GFDN9+/alb9++1hOLAT7++GNmzpzJ9ddfH7Pj7IWlFRERkT1B8yGw4A/yPKKJdQYSOWUU4D8R9mcKDQ4Nzu+o4Fxne0YA9r/O5dPqQNwBBEJVA3ri/J9jb9t3X8iuDwbWhbYw0Z/JGipCcK7LtSpCeyOFGYe/D4ZxToSDNBWKmx9yrL6EBcnaA3F7hbQh0m3/+TjDSiPVEvyBonYTgZUR9pdJ5NodSHgYabTg3NCQ0ep8oM6+AvY6+EOK7RuY2/fFWaN8HGGvXsJDVENrZA9vNftULtAQeD0EZ5BrpLDX/vjDgCMZD1QAP4fMTyZ6gGu08NxIvDgDbO02E/wsVYPz50++c5m9O9pr5MUZiBvaf1oShhtan974A4ht63p676ChPh5yagmVNn4bVRUpVP/cLXzf42uhwh1eX4CmnkEyhsj92FRJ8LzHNLEe+OuVR/B8GptZX6QZhxxyCHfeeSfXXnstp59+ekz2qcEWERERERERkc5Mj36OGZ/PR1JSUtj8gQMH8u23kR6n1zYabBERERERERGRvUJqaipDhw5l//33Z9SoUey///7k5uZy9913M3HixJgdR4MtIiIiIiIiIp2ZMlti5p133uGrr77iq6++4oknnmDevHnU1PjvzTv22GO57rrrGDFiBCNGjGDw4MFtPs5eWFoRERERERER2RsddthhHHbYYdZ0Y2MjP/zwAwUFBRQUFPDZZ5/x0EMPsWXLFhoaGprYU9M02CIiIrIbCQ35jBYGuicKD6QNDYGF8FDV0HBccIR5QsSAVr98/AG7rZGxS4Nzw4+VH/hehL8+kZYFhAbiRgvrBH8IZTLBUM5eBENQDwO+C1k/mXCR5gGsTwyfF/E9WUP4+xuqKKQm62lZKG6icz1HbfKdu/DiDEf14jy30Mzmnrb17M2oIbIJwPch85ID+wkNcPUQWaTQ3lCVQIntPKvNhoE/eLi5kOIoobibgdA/BNsDXEPrkxv43gMwbOua33sDbpxhr4NrocQdPaQWIKsechLCQ1xDw3tNqUQOz41Ud3uQaygvwXDf0BqD/7cvM0TW/j7Za+QNaacXZ/+xv4bwMNyQ+nh67yA+oQFfTTCjIj+jiFqS8OGOciKQ2rUc99B1FG3PD1uW5C2nISc+eoDu9yH7TcD/Pob+3tqLYGhuDdHfHzsvwfqb/am+Bdvt7nRlS7uJi4tjyJAhDBkyhOnTp1vzN2/evHP73dmGiYiIiIiIiIjsSXr0aOrxWs3TOJaIiIiIiIhIZxZH+zw5SJdftBuVVkREREREREQkhnRli4iISCcVOfvDmVPici2lqVyR3TnTJXoeiamIZnNJuiU6szF64c+ZqLbN8ybCtxEakAzsCD1mc/4ToZ0QOculbTkuzv3Yj1VEMGOjb3g7WpPRko+zbgNw1qwfUBt43QWYCKwM2ccAYEXIvAMJz8SIJGKOS2j+znrCM0WKcObWmPJt8/va5plCcnx2tjY1Taw/mGCmR28gNUK+xXgi57ZEyhQZQ3imiD0HwxS6bTnOfIz1+cHXIfktwZ3mB/uR17a9l2B+Sh5NZ7SE1mefwHfzav3Q/JH8eqhIsPafOXg95SVpkOPvgPkZRdaqodkiSVk+3ANqKf4+pBHja6HCHZ7nEumOgdQI85KBwgjzwVmL5YSfv315tDp1w5lJYs856Y0zn8SctuWzNNTHW/UB6N51sz+bJcU/nU8R5aSSRC1QThbFeKgCYF2EwJ/RGZ9TFOHnWhK1UTNd0sZvC687+PurvR9WEKxDDdGzcOy8BPuemYlUG3nVPYoyW3Y7urJFRERERERERPZIO3bsYPv27QBs3bqVF154gW+/jfRXltjSYIuIiIiIiIhIZ5bQjl97sL/97W+MHj2aMWPGcN9993HSSSfx9ttvc/rpp/O3v0W6gjh29vDSioiIiIiIiMje6K677uLbb7+lurqaPn36UFhYSHZ2NqWlpRxxxBGcc0773W6twRYRERERERGRziye9nkaUXvssxNJSEjA4/Hg8XgYMGAA2dnZAKSnp+Nyudr32O26dxEREWmRyGG4+RHmDcaZxlne6v16PC6eeiqrFa3bNZoPxA0NQw0NTCU8BDYzZPlAnOGcpkgBrwCZieFBowDVoW1ryhpgO+Hnsz7snKMFGjddm2ihuDsR+mqvmxlA6Q18twe8QjBUdALhoa6hdfUQLlLoax6wLmReaGhudT5QZ18BZ58oAg7FGRy8hmCNbPXx4gyLtdfHizO0M7RPeQnWJidkWaQw3BK3tV5q7234apJIG7+NqooUqn/uFtx2fITg3C6EixQoGmleZci0l2BQa6iwsFyCny2zH9m39+IMdLVrLuy1Dn9Iag9gSKA+oWG4gZDa/IwiykkjM6PYPx0IejWNzvgcICzMddDQr8PDc73lVNRH+Dk4hvDg3PGE9+2eRFZJ8HzHBL6bfTmP6LWy728A/pqAvxapBENxzcDggPT8TVRXpFjTVhgukMk20qgI1Kg80IR1lJOGN9Ax+1FELUlUB9Jz96cAgMKQGo7iy8CptDxAt+fQQoq3Oz8wntQqSutzgr+FJtdDUWCigqYDctfjr5m9T5mfuaomtpO9Wnx8PDU1NSQnJ/Of//zHml9RUdHEVrGhwRYRERERERGRzkxPI2qTpUuX4nb7ByDT09Ot+VVVVTz44IPteuw9vLQiIiIiIiIiu7l42ue39z38NiL7AAvApk2byMnJoXv37nTv3r1dj62nEYmIiIiIiIjIHu+YY47ZZcfSlS0iIiIiIiIinZluI4oJwzB22bH2stKKiIh0PJdrIeEpkvkR1gwNhI20TiKwqpUtMIAq0tP/TnV15P90RAtqjTVn8Gt+yNLmzr+JAFgID4ENDS8NdSDhQa1RwxpbE5ybSfi5RFIUJSjZZO47g2AyaSaOlFN7QPAAnFnK9nrkEwzEDa3bEIKhrjn4a2Cuawa+5uAP9PwRf7Cp+UCH8YQHie6Ps66RQnN74axnpBDjUJVAie18qyH65ygxcJD8YI28BANnvTjDYu19xV7H0FDU0ABcN8Eg2sGRw3DJqSUzo5g0yiknjUAuKaldy3EPXecIco0anFvhdoa4jie87mNw1jQZKLRN24Ncm2KG5Zr9xOxHXtv2XoK1tIeXgjPsNR9nUG9+PZT6l3v2L6EBN+TU+hdFCcPNpDbQDGfQq90ovqSaFLbZ0oxHZ3xOOakUEwzFzR26gXLSHCGuntQqEgY0UPx9yAejd9hhguz9t4bwIGNv4HsyzlrZ+1NOyPrmNrZAXE/vHTTUx4cFBqdk+DtoMDDYH4bbgy3UkuQIwy3BS7fAtH/9NFKoJsXxwyIYlBsafLs/BZST5qgt+GseGp7rxkd+RlHYPnqEBBbHj2qgelM3Z1huJGbf8hLsY+b70v5Zp7KHaO8nENlpsEVERERERESkM9Ojn3c7ymwREREREREREYkhXdkiIiIiIiIi0pkpsyUm4uN33aU8urJFRERERERERPZ4X3755S471l42jiUiIrLrhQfijgC6hqwVKUA13znZLTFKcOhAqK5rRYsagB+APgSTTZ2iBbXubHBueC3yQ9boG32ZPfwVnMGleTiDXEODXXvhD2K1Z0D2BDba9w8MB1aENOlowsNHkwPHXBcyf31IGwGq7YG21oqEv+dF2M/ZMCbici21LTPXX4O/D0GTIcFZttc5UdYB6Iezbvbv/YkY+OrpvYNUauFHyDhsI+t/6hPcX2iAaxf8IZb2QNcJOGsaGl7sAVaHzMvBGbRbjjOI1AxyNTnCim11Ms/fS/Bt8eIMdTXX6WlbDs6w195Ava3tZpBpmi3INEIYbibbcOOjliQyqSWPdY5g0dEZnzsCRVO7luMbWuIIcU3yltOQE+8Mzh2Ds8Zd8H/U7eyBrJGCXJtiNtE8Xy+RQ3FDawRWoCtpWDXPHLye8pI0kpLr4X+QlbYFd6I/LTiTbYHv4WG4WfjraQa95lME4A+6tYW2dmczeayj0FbLHmwJq3cu5WRmbHME5wL0HFroqHna4PJgaG4Czv5YYatLDeHBw+ZnqAv+j/6AwLS93/eOMK8eklKrrPp177oZH+5W1SiFKjIJ9sFMivFSYk17KbGmgbCQ21GBoFz7fLO2kcJzAUfN/fsID88dlPGDVfMkatlY77/awJcT+m+jjVlbL8E+FaiXJ6m0Rbnau7V42ue3d2W2tBsNtoiIiIiIiIjIXqOmpoavv/6aLVu20NjY6Fh2wgknxOQYGmwRERERERER6cz0NKKYef3115kxYwbbtm0LW+ZyuWhoCL0ssG2U2SIiIiIiIiIie4WLL76YU089lY0bN9LY2Oj4itVAC+jKFhEREREREZHOTU8jipnNmzczd+5cevTo0a7H2QtLKyIiHSE8GNW0HsOYv6ub067Cw2Un4kxmjRSYmu+cDA2DjVQ6a13AnQjfRljmHQgbQ8Nz6/AH5OYR+frhNeHtAaAoSnBu8+9hcLumwoEzcdSlqRo4sxb9zbWnI9rDcyEYAuvFH6joDUwPJjz8diKwMsK80ODc0BBMs12hobmVQEnIuVTn438foguG4/YNfOUHpvODtfHiDDkNDTE12esRKRAX/AGdbsIDcdOCYaZm4GsStcTV+f8bmRTnY9DQr/0BpYFA0bAA1571kJPgDHANDXQNDc0dgzPAuBInL+EfI9NmnP2nmmD/MWvkJfgeegnW0R4ia1/HDMQ1gznNQFxs9UmF/Iwiakmygkxz2eAIw/XhppYk8lhnhZiGBouOwv+0DDNU1E0x+RlFlJPqCBW1B+d6UqtIGNAQDHEFf1jxandw2h5UnEp4CHFTzL5i7j5aKK6XYKCwrUZWaDCQmVFsfU+u8/8VOY0Kkqhy1AigH0VWGG4m/r7nw01/fgoGDwM9AqGt9rqBP7Q1NDzXnLctMM9LSVhwrhtfWHDuoKFfU7Q9338+qVWUmkVJroeiwK9VFYT/bKjB//NjAP5+bf7hPEIobnr+JqorUhx1AsjH//4TCLLtwZaINQJ/gK0PtxWG66aWWtzWefpIijDt39cIvgmroTm/Co+jHpHCc836muG58YGT3Z8CR3CuG59jG1/gXM0zjs9poHpTN8c6VODvt4EvT06wT2UYW9mBSMuccsopvPvuu/Tv379dj6PBFhEREREREZHOTFe2xMw999zDqaeeyvvvv8+IESNITHT+QeSSSy6JyXH2wtKKiIiIiIiIyN7oqaee4s033yQ5OZl3330Xl8tlLXO5XBpsEREREREREdkr6MqWmLn66qtZuHAhV155JXFx7ffMoL2wtCIiIiIiIiK7DyMOjHZ4TLOxFz6f2OfzMW3atHYdaAENtoiISDuIHKIaGhJrGhIIAi1qcp+Gcc7ON6ydhJ9vfsh0NcEQWGhREKyHYLl64g8ErKFpBxMezgowJiQ812N+7x/5LSGTYEyhXRGRg3MzwmpgGOeEzDO3K8Ifkms/llmLxGDboHWBuJm210OAEpxhuCWBZYPx59JWEgyxHI8/eLGpsFaIHJybCay3TUcKzU0GVoXMixqaa1qPv89EqY9ZG69tsT3s1h6I29O2bi+c4Zz2QFwzzDQkENcfzJpGZkYx+RRZYZr7sA6IZyCr2EAWuZSTn1FkBWMmUYt76DorUDQpy+cMzU2NEJprP4dUnJKBQtu0/T0M5SX4nptCw4O9BENx7aHB9rDX/hHqQ3ggrlmfTLZZ555FER6qKCcND1VWiGkCVQzlOytEN9P2WQsNcw0Ncu3BFrIotqbdFIeFuPYcWmiF5gLEj6py1tz+3//QIOWmmH3O3p/sAcPekH3WQ1JqFaRihSqHhgYDVj9Kp5RerLFqZAYIl5NGJsWOYNc81pFAA7W4yaeQNCocIa2hIa9meK59ne5sxktJWHCuPcTVS4mj3gCDMn6w6p0yKFjr+FGBQFd7WK6pJlCrHPyfV7Nvm7VKrSUpuda/z6RqUjKqyWQbbnxWneKpxxvo1GZfSqHK6j+Z+PtfCtWUk0YK1Y6aeQLT5aTioZ6ebAgENHcjCR/9Wc06+thq+LWjhgBZFJPLRspJpYRutnUjh+dGCs61z8tlg6PegNWffY1JNGT5RxZ8Zqi6FZDrr1dKahVJcf6g3bTi0ARtkehmzpzJM888w1VXXdWux9Fgi4iIiIiIiEgn1pDg/2qP/e5tGhoauOWWW3jjjTfYb7/9wgJyb7/99pgcZy8srYiIiIiIiIjsjb755hv2339/AFasWNFux9Fgi4iIiIiIiEgnpitbYmfZsmW75Dh7YWlFRCSWIuez5IdM96Lpf3I8TSwzj9O5cl0i55GY+oZMD8QfFBLQLTGYDxGaz5KH/95+ex5Jfsj04MD30EyR5MCy0PngzHNxB77vA1Qnhq9bnYEjU6ZJa4DtOGtQZKuPOb+v7bt9XVsOiZdghgY4c1ryCdZgOLDDtszMaAH/+ZcQzEIwMzdygMG1UOL21ykNPL13BLMssiLkh4wnvJb7A5ts08n4Myw22uYNAEL/UHZghO1Cc1zW296LavCf9HrwZPjneQnWx6yNF2duRhfba6+tPWYGST5NZ7SEZJDkU0Q5qWRSG8h7SMNDPUP5jkq8QDxZbKEHP1vZC6P4kmpSrJyL0RmfO3JcfENLrJwLT04VpfW24JDBtfC9Ozg9Bud70tP2OjTDxc5LeBc2D2Nmj9hzWsxa9bZN5wD1hNUnMyOQkWHLaDHr48NNLUlk4c8oqSaFAfxkZY/0t71OoZp9+dFqnpl5EZov0j0wbdbQE8gzMevtpcSxvhufI8cltOZph5VTvLpXsCZmv+yNs9ahzNrbP1u1OHJfPDk7aKj352yY/QjsGS3ltn7kz7Ex+9F+fEMJXazsETOTpT+rrRwSD9UM4kcrN2gQP+DDTRUpVsZIOWmUBN7Q0OyW0OluIbUDZ25OPA0kUYuXEltOjs9a140PAh9PR8ZITldn7Srwfw4hkDnirFXPjA2B/J5t1iY92EItSVadzFqa9QvNsbHXzOwTZj7LIH4AoIRupFFBHmutvtqPQlIppwSvVcMK0tgRqOFQvgvUrI+jbaF1M/NcmsrOMefZpwfwEyV4rWyclMA/hu44HxsycoFggpgv1U1Sci1p3nKS4nxWv8qimMbIAWQiES1atIgePXpw1llnOeY/8sgjbN26lT/+8Y8xOc5emD0sIiIiIiIisvuoj3dRHx/XDl+ujj61Xe6BBx5g8ODBYfOHDRvG/fffH7PjaLBFRERERERERPYKmzZtomfPnmHzs7Oz2bhxY4Qt2ka3EYmIiIiIiIh0Yg0JCTQkxP4qlIYEA8etznuBvLw8PvzwQ/r16+eY/+GHH5Kbmxuz42iwRURERERERET2Cueeey5z5syhrq6OI488EoC3336bK664gksvvTRmx9Fgi4iItFh4GG4+LQvDDQmIjagHTQfgljezfbSwXtN6DGN+s/to+f7zQ5b2jb7MAxAIP+3lXMRAmg7DbSoMdmLg+8qQfU4kPKA1mWAYZlJgXibO4Fx7e1dHCs7ND59HJuGBwEX4T6TItsy2rce27wEEw4HttbCFbobVzAylBWcgbm/8gabm69RAIK494DSnlsyMYtIop5Yk3EPXUbTd37akLB/uAbUUf2874PhaqHAHg0O7EB5CnEq40DZHyoDOizDPtD4/8CI/uC8vwdBXsz72+tkDX83lZk1SCQYERwnEHZTxA2lUUE4q+7IBD1WUk2YFvJbgdYS99qMQ2JeebKaYruxPAQBF5FuhrpFCc90Uk59RZAW+9hj6tT+Q1AxwHb8t+B6kBoKLo4W49gx5bV79XUkwONjUy7aeWU+TGYybXw81CVAP6fmbqK5IASApzmcF4rrxWaGc8TRYrxuIJ4EqhvIdtSThw82+VpirmxF843gNUG7rPKEBoyP4xgrN9dfQX19zuT3I1Zy2h+aagaMAboqtIFeApKwyADypVZSanSW5niZ/NbD3KYBAtrS9Tt27bnYEvWZSSxbF1BNvBb2W4KUbJYHw5TQy2A7kEE+DFexq1qknG6x651MY6IfdSMJHf1ZTThpJ1NKf1VZd/HXcEOhvWYFaft2q4NzQkGIvJXgpsbbPZYOjb68jL2Kga3xOA9WbugUDqlOB1FqSkmtJSa3y9ysrFLecXDZShYeswB7SCAbieikhk200kOB47aGaPNZZfdEMULaH4ZrhumYAbgleq25mUK7X+ocFMimmp1XDTMe29nmRwnNDa23WN1pIrptawB/kbDLbYtXGFkJsD8b1UIWHKrqzGR8N7Oka4uNpaId8lYb4ve/Klssvv5zi4mIuvPBCfD5/6HVycjJ//OMfmTdvXsyOo8EWEREREREREdkruFwubr75Zq699lpWrlyJx+Nh4MCBuN3u5jduBQ22iIiIiIiIiHRijcTTQOyvbGnEiPk+dxepqakceOCB7bb/TvU0ovfee4+pU6eSm5uLy+XipZdespbV1dXxxz/+kREjRtClSxdyc3OZMWMGGzZsaHKfCxYswOVyOb4iPeZJRERERERERPY8X3/9NY2NjS1e/9tvv6W+vn6njtmpBlsqKysZOXIk9957b9iyqqoqvvjiC6699lq++OILXnjhBX744QdOOOGEZvc7bNgwNm7caH198MEH7dF8ERERERERkZirJ77dvvYG+++/P8XFxc2vGHDIIYewdu3anTpmp7qNaPLkyUyePDnisvT0dN566y3HvHvuuYexY8eydu1a+vTpE3E7gISEBHJycqIuFxGRcC7XQpwpn/mEh8JmEEzsNIWE4VphqBECV+2SgeSBsDFaSFtiYL9FTe8nqoyoAbqGcU7UraKH4tpDYaOdMzAMbLmD0UNgIXoYbqQwWLsJOANbwR+SW0IwMBSCIa3mv/7dA6/N4Fy7fMKDd0mE9SGzqiP1AVNfrJrZa2LvVlm2117b62jBr2YNIDwQN7keKvwnl9p7G76aJDKHbrACXTMzismnKBC+mWQFMo7O+BzACmwdNNQfBGkF53rLIwfnfm+7t3s8zvcgNEQ39Lxbw8yV9OL/nNj34w189QIrH3IwwUDO/GBNkpJrIafWCsQ161FOKpnU0oMtVlBmdzZTTQr7U2CFvY6iwBHwWo8H2JfubCGXNVbg5ahAcGsJXkeIa6TQXHObXMrJzNhmhcH2HFpoBeZ6cqoorQ90+qYCc+3B0jX4w4ztzM9RaNCr7fOUlFoFqZCZ4f8PeUpGtVUjwKpRHutIopZqUhyBuGaYaQrV7MuPlJNGTzZYwcM92WaFvGaxjTyC/5E3A13NINJ15FmhueWkUhJIpLWHi/ZgM5kE67Y/BVY/9lCFlxIrzNReb3ut0watZsv27gD4croGaxL6MyGkXmbIbkpSdUidyq06ZVFMErX4cNtCT4sdoa992ADkkE4pvQL78FDPIH7Ah5sqUhxBrvaQVzOgtZw067UZ4GqGvFaQxg68LQ7OjRbk2i0Q3mtOD+Ana7vQQFezvr7GJBqy4vGl+n9WJCXX0jNjAz7cVshraChuihX46t8mUnBwFtusQFwvJbjxUUUKubZgay87AkG5XtIot2oO0I8iUgOh10nU0r92teOtXufOs94rew3NczV/PoSG5wb75Rb6sI5Cgo/VbarW5uv+rKaYLCuM2B7yDOCO8zmCcYM1qybZ8QNAJJxhGFx77bWkpKS0aH0zOHdndKrBltYqLS3F5XLh9XqbXG/VqlXk5uaSnJzMIYccwqJFi5ocnKmtraW2NpiIXVbm/8ekrq6Ourq9K6lZYsfsO+pDsjN2ZT/yeOLAcW9wpHt6GyHsCQB1OAceWnhAN/5fJD1NnVtdhOPZNXffceR7nZuqp8cTrQb2djRxzkmAPW8tEX/ZIPxf4Thw/IHJRfNvgTk/9NTMbe37i/Mf05Pgb6snoc7fhgSCTyiytzNSTlzE9zO0fqG1CdnOvl/7ce31sNfCXgd7DQzbdAPQUA8N/hme+nri6+NIrmvATSN1gTYlYeB/A4KXEicFdpIcWMcdWOYJXD6cVF9PUl0DngbbeTbUg2G7QDj0yuRI70noHw9D3397LSLVKDGwTWgfCdWIrS7BmiQFzsdd56yHeb6JGBiBDRMCh4snjgRcNOIijjjiA18u4omr8zckri4eF/HEBy6YNtdL8K9FAi6r6Qm4rHr7u1hjoB3+9yTZ1nfM+ifX1eMza2+eT6TPgr2fhH6WItXK3ncCkqxjBmdGqpF5PtFq5D+cv1ouEqwvcysXCRgkYtjedCMw3xyYdll7bcRFAnGBE7LXOp44jMDxzWlzWDMhcIqR6g3B/h5Pg1XreHsfD61xSL1Ca9VUnQyw2gg462T1ozhbncwWm7VKCuwtkcbAh8MgiYbA60bcNOCzXhsk0UhD4KOQZGuRYauzeYzgMe11jlTrOFutza3NcydCfeMaG6ivrye+Id6qmbuugTgarD6faDs7k/nOm8c12xLn6FMuW58yQvpUoqMW/rfNbdWugWTqqbPqV1/v/MHeEGeuZ1CPiwaSrW0bcdEQsu9G6wdWYuC9acAItCkoeq3NOsfb6muvbejPCXuftupVF/ssk86mgXga2uHGlIawf8D2TL/4xS/44YcfWrz+IYccgsfT0v/ERuYyDKNTJuK4XC5efPFFTjzxxIjLa2pqOPTQQxk8eDBPPPFE1P289tprVFRUsO+++7Jx40YWLlzI+vXrWbFiBWlpkf8St2DBAhYuXBg2/8knn2zxSJiIiIiIiIi0v6qqKs444wxKS0vp2rVrRzcnpsrKykhPT2dFaRZpXWM/2FJe1sjw9G17ZO062m55ZUtdXR2nnXYahmFw3333Nbmu/bak/fbbj4MOOoi+ffvy7LPPcvbZZ0fcZt68ecydO9eaLisrIy8vjwkTJpCZmRlxG5Hm1NXV8dZbb3H00UeTmNjM7RQiUezKfpSe/mcg1zYn0hWB3SBweX1Qf6LeUtMU88qWTc3dRtTU/bPrmlhWjvMenaDS0hlRt0pP/7ttyl6DPNvrJs55MFAasqp58WT3kIMNxXnL0b4h04NC9mU1EvgxZJ43sO1m27xk4H/+K1oe+eVbnPXy0VQnJPpvudgSsn0XINIfgDaGTFdD+JUt9vcoUDN7TXraFve2vbbXYx+Ct4X0tb2216AX/tuIwN9V7bcR9S3GV5NERrftpFJBRaCf9mEtPhLx2a4oSKUy0Oo+gWn/PThrd/ink5J9JLl9bP/R1nCvD360XYoS+h5Eek82h0yHZvzb34Ofba/NGqXj/5zYP5Y9At9zCV6hMQgCpwR9bLcR9Sj3z+q21lGPisD9NtlsteqSw0ZqSLHVy00vfqYucBtDL36moc7DwLfGseroj4hPrGZ9oKGplFNBGqWkW7XfTgbJVOGmjjW2Oq8P3BOVhA8fSWy33TKwfUcGAMldqij7X+BEU+uhJsFZK/PWtlqCnxf769Bamd/N27Fs/S4p01+jjG7brXmRapTJdhKpjVijisCtGubrdEqs24jM16V4yaAYj3WvHFTjYTuZpAca/jO9ceMjCR/lpFIauP0ilXKr1m5qqSfeqlsqFVY/TqaKOtwUkxFWb8DaJp4Gtu7IBsBXbPtDpFljs74h9QqtVVN1qsNNuu2Hl71Oves20u+t8aw4ejm5iWutW7Z68zM+3FTjoRc/ByqRRK9Ag0pJp1fgg1JBGqn421NDCsVk4KGaJHxUkkoJ6YGtfVadAdIpcdQ2jQp+tv1QCq11Ij5r2mwDYNXYnDbr62tMoqI0FV9t4DYidy09um2kDjc5bMRHEjlsohoPmQT7XDZbrNuIzLrZP3+ZFFu3EaVTgps6qvFYfcr8fPdivdUme829lNKFcmtZb5/z/tCfk/xvdgo1JFJLJWmUBNZ147P6vnneZhvN/uWhBje1rLHdbttUrc06x1NvvTf22pqvzZ8T5vHMmqVTSmOxbiOSzme3G2wxB1rWrFnDO++80+rRN6/Xy6BBg1i9enXUddxud8RnbCcmJuqXZNlp6kcSC7uiH1VX2+9FgMj3K0S6Vj+0XS1sp3mo6mgrmPtpKsitucuII1/M2VQtq6uj1cDejibO2UdwcAX84xLmbcChIfehd2XZb5MJPTwh8yNd8m+E7K/Reczq+kSqSfTPC701OSmk3dZGzUwDkWtjq4l9v/bj2uthr4W9DvYa2G8biQfiXRDv/69NfEICvoQEahLjSSSOmkCbfLjwEUet7VLspMBOzHUSA8uqE/z7akhopDGxgep42znEN4LLNh36x8ZI70noHXCh77+9FpFqVBc4z9A+Esp+B6CtJg2B86lNdNbDrEUdLnyBDesxb9prpB6DegwaAzdmNNCIEXgF0JjYQFxig3UpurlefWCtegzqMO+AMqxj1IHtffC/JzW2vmPWn8SEYO3N84n0WbD3k0h3OIbWyt53Aswa1SQGZ0aqUV1g82g18h/OXyPDWlpvVdZ/c4Z/b8HmJATWqQtsX+/4MuttEKy1/wgu6gMn10CjNfSZgPnjJlK9g/09nmCtffY+HunWRFu9QmvVXJ3qbW9AaJ0AGhMbMRLNOpktjgvc9uPDFfjAxwU+HC58xAdex5Fkex0f+JT7P+kukgItMluVEHKM4DHtdY5U6/jAe41ta/PciVBfX2M81QkJ+OrNz2ADtYnx+Ii3fg7Za2Uy33XzuGZbGh19qgGDOIJ9KtHWp+oCtai1+lg8tcQFBlPjqSGBGuJJBiCh0fmDPD6x1rZeLf6fosmBffoC+4q39h1n/cCqs94bFz5bnZuudbDOwfraaxv6c8Lep816NSZ2yps1Yqr9biPa82/B6ii71WCLOdCyatUqli1b1qarTCoqKvjpp5/47W9/2w4tFBHZfYUH4k7E+Zt0C8JweyYG/poc+A97L/xZHfbdDAl8DwtfDRiTCN9GWZacCDvyoyxszn8ID/j1ixac62ffpm/k+R5wnLNdGsGyhQbb2kNgmwrD7Y3/AiJzenDgP7ahAa0VOK+IyCc86HIiwStDzKshPIF2h4bf5oVMJxOuEiix/YJWbR44wBOhLuZ+7fWwLzdr4CU8+LWEsEBcT+8dxCc0+ANx+xQHA3FTtuHGH6i4ry04Mo/NpAX+Cg44AlvtoY+jMz6nnFQrSHTQ0K+t0FyAtPHbWheaG1rjnoRfKRSJWSezv3Qj+F6Yy/LxD9Dk4P/dKxkyB6+nvCQNUgmG4poBntRagZkD+CkQ2uolj3VW/cyAV3vY66DA5Trx1JNGDXA4A1jFOnIYwTeAPySzRyAMN/Q1OENc96fAqr+XEisQ042P/Iwia72U0MBc88ST67H+O+sGfgrUIxWw/13N7Ef2K6kCu0jP30R1RYoVigtY4aX2INd+FFk1MkNf81hHAwmOGuVTaF3Jkk+hFV46lO8cIa8+3KQEgj7XkRcWRjqU76ww1x5ssdpSgpcRfEM1HraRhZtactnoCCiuwmMFjZpho/4u9JNVX3utGzL8AyZmBTypthqH1Cspq4zuGVtoCIy6mH0qnnqrTuWkkQZW0Gse60iggVpbiLAZ+loRaNswVlBCKv0osj6rSdTSn9WU4HWEv5phtSV4/fupXRc11NWctsKBQ6Z7sIVcNrAucEVQaNirvdaAFeLqto2Ihga6pgR+sLvjfGzIyLXCuH24SaOcLIqoJx4PVaQEvsxaZbLN6lNmrbLYFlg/PDi4gQSq8JDLBuJpoAqPIzjYHoabxzrSasvxuf3b59Wuo0tZI2UZztCuwVvXALA+O5MGEvw1q91AuTsYcBtaR8Dqs8EQ3PDg3G6UOAKKzWX+oN9aGkiwamvWzR6Uawbj2kOJG0igPuwvBiIdr1MNtlRUVDiuOCksLKSgoICMjAx69uzJKaecwhdffMG///1vGhoa2LTJ/7/HjIwMkpL8PySOOuooTjrpJGbPng3AZZddxtSpU+nbty8bNmxg/vz5xMfHM3369F1/giIiIiIiIiKtpCtbdj+darBl+fLlTJgwwZo2c1NmzpzJggUL+Ne//gXAqFGjHNstW7aM8ePHA/DTTz+xbds2a9nPP//M9OnTKS4uJjs7m8MOO4xPPvmE7Ozs9j0ZEREREREREdkrdarBlvHjx9PUw5Fa8uCkoqIix/TTTz+9s80SERERERER6TANxFOvK1ti5u233+btt99my5YtNDY6H3/9yCOPxOQYsX+3RERERERERER2QlVVFX379uWyyy6L6X4XLlzIMcccw9tvv822bdvYsWOH4ytWXEZLLhfZy5nPNt+2bZse/SxtVldXx6uvvsqUKVP0NKLdTNPhqZEZxjnt0JLY9qPwQNwhOJNs+zo3CA3DDX2kc2gwrLmJ/WmMZsApEMjPcwaImlKJHqDrJXqALkB1tEdHAxRFmLemifVNZi3yg7Ps528/d/s5hwbA2sOCzUDc0PXsYbjmtC0INiW1yh96CqR5/SGvVkhraj0kNECFOxiU2wV/dq+tzp7kOp6Kf5Xp/5tCtRE4j2SgMOS0uwErCGcP3E0GVtmmK3E+ctesTaRQXG+gfeaxzNDXAfjDfnvjDz2tDLwOPNgic/B66uvjrVBTM6w0k2Agbh7rrDDSfhRRSxLVpJAfeG0+VhWwwnKLyMdDFW58lJNmhYcmUWuFOKYFHgttBreaYbJmeGuat9wZmptgq5cbf4it+d6Ehrjaw3LtQbrm7szgYHt9TE2EveZTRDmpZNkeF2uGvWZS7Ah79QUeLWsGc+YFHqfuD8Qtp4Ru5LHWX9u6LsS/ehYNUx4hLXG7FebqDTxifR15VlBsCd1Io9wKbk2j3FbTQA3JxEMVPtxWGGYCDdb7UEuSFVYMWDWPT2igelO3YC3MetvDor2Bege+PDk7aKiPdwS9hobiZhGsoTkvk22OoFcfSaRRjodqykkNhHz6A3HNOvmXV1kBpYBVW3/QqccK+1xHntXnqgPRqeZrZ3/sQwpVYX3THppbT7wV5Gq+Nt/rErwkUcuGwHPES6wfyFg1Nuvr2+Z/8qc9GNcM9s2imHriHbUKDS4F6MFmW79KtWpaQjcy60rh1XNpmPIIeYn/s2oSrVZ5teusgNdeW4upd0NVlyS6bvcHpFZ2jaPE3c1fn9paK9TVXmOz39kDsb2UOKa7UUIh/QBIoYpakijB6w8UJp5ttn7sppbN9CCJWrZYzxaHbWQ6amvWK5cN+HC3qFb2PmUG5Zr7Mvtuz8D+zP5rHtNeK2/tDty1jVR18edcdi30gRuMLuCy/+xPB8P/RGdcgadRl/Xzb5NS6SOhFmq7wLaUQN1qA5/3QEBxCtWOfmnWemOgryWF/Ez1f8aziKeeErzWtHkOZl+119VLSaD/+6x6ZRev4aKspyktLW3102o7O/N30U9K+5HaNfbXSlSUNXJwemGnrN3VV1/N6tWrycvL47bbbovZfnv27Mktt9zS7g/N0ZUtIiIiIiIiItJprFq1iu+//57JkyfHfN8+n49x48bFfL+hNNgiIiIiIiIi0ok1EBd4IlGsv1o/JPDee+8xdepUcnNzcblcvPTSS2Hr3HvvveTn55OcnMxBBx3EZ5991qpjXHbZZSxatKjVbWuJc845hyeffLJd9m3XqQJyRURERERERKTzqqysZOTIkZx11lmcfPLJYcufeeYZ5s6dy/33389BBx3E4sWLmTRpEj/88APdu3cH/E8Yrq+vD9v2zTff5L///S+DBg1i0KBBfPTRRzFvf01NDQ8++CBLly5lv/32C7s1//bbb4/JcTTYIiIiIiIiItKJmVeixH6/fmVlZY75brcbt9sdvgEwefLkJm/vuf322zn33HM588wzAbj//vt55ZVXeOSRR7jyyisBKCgoiLr9J598wtNPP81zzz1HRUUFdXV1dO3aleuuu67lJ9aEr7/+mlGjRgGwYoUznM7lit3TmRSQ2wIKyJVYUEBu5xc5CHcEBMIzWyctsF1Ri9ZuaaDuzvYj5zlmAPYgtNBA3HznZGggrD3sNQ9/uKk9GNZLMCi1F8GShOoG7MAZCArNB+hWQCA/0dZG/CGkkQJ0k4EdkcJzy8EW7hguE3/jCdYgNAw4Uvgr+INMzRr1I1gfe1DwYPy5w2YIbCAMF/xBsLU1bnw1SeRnBINd8wP9yh7SClC03T+dlOzDnVzrDGr11lrBuR6jjqcqXmV6rylU/2h7X72BcoS+F/bgVg/OUFdoOjA3tDZegqG4Zk3M/hQa+pofDAZOSq4NC35tKhDXDFf0h8OmhYVt2tkDM81pf0ysz6qxlxIrNNdc19wuNLw1jXLrvfCkVuGrcVP9czf/n7iS66Eo5G9dZpBrPM7am3U3g3HNGpo1yq+HmgQr7NVeGzPs1QzkNOeZIcGRwl7LScPLDivU1gwwzWKbFaxrhm/msY6GOjerX72FAVOuwJcYb4W5VpDGDrxWAKk5Pxj+6guE5wbr7Q70brP29hBXMzDXjY/NdLdqvsEK3Kxl4/Zcq96lRYFC2ftlIBjXDBDumeEPFG0qFLc7m63g4Mj1alkgbmhwqZcS0mrLKXF3o9fW4PHWZ2c6wkbtQaP20NxtZDrCXIN17BMW5GrW1F5fM8zVVBJ4rwrJJ4VqfCRZ/byYLOIDv4rZg3Fz2UgVHivotaW1MkNg+1HIjkB9aurS2P7qHxkw5QqqEv1BrKEhryXubnhrd9ClrJGyjEAYbqk/yNUKdw0Eu7oq8QdRp0NZRpIV6moG50arsdmvkqi1wlxTbSG6wdpkkUKV1RfNQFfzZ4L53UcSVaQAWPVMCXwizGBcM9zV/G4G4NprlcsGqvBYAdc+kpoMxC1x+8Oie20tpiyjiTDcDGA7/n97MmyfFbN+XWzzA0G5hj8vOKzGAF23+8KCc6vdzgBi82eDWTf/Z9wfOFxFChvItWprD84ttgXmmmHCPdhMGuVWvSqL4/b4gNz3SgeR2jX2gy0VZQ38Iv3HsPnz589nwYIFzW7vcrl48cUXOfHEEwF/HkpKSgrPP/+8NQ9g5syZlJSU8PLLL7eqfUuWLGHFihUxDcjdVXRli4iIiIiIiEgnVk+8NXge2/36rVu3zjFQFe2qluZs27aNhoYGevTo4Zjfo0cPvv8+0l/QOkZJSQkPP/wwK1f6H385bNgwzjrrLNLT02N2DA22iIiIiIiIiOzFunbt2imvCpo1a1bM97l8+XImTZqEx+Nh7NixgP/Wp5tuuok333yTAw44ICbH0WCLiIiIiIiISCfWSEK7ZLY0EruMEoCsrCzi4+PZvHmzY/7mzZvJyQm937pj/OEPf+CEE07goYceIiHBPyRSX1/POeecw5w5c3jvvfdichxltrSAMlskFpTZ0rmE57PkR1irF+Fj0uZ6RS04iofIgSPRNLfP9fh8V7W6HznPNd/2OgMriyRsGc6MlmEEM0YABhLMHzE3NadD81x6Ep7nAuE5H8mBY9jzQSB6pou5z02EC81ysQvdTzX4Q1NCVzLzThL954Ntlpdg6ez/b7BntAzBmVljJtDZ81p6Y12/a89nycwoJo1yygMH8WdspJJFMR6qrPn2zAv/dAUQPcvFk1pFQkIDVd9256n/vcr0faZQndkI39suFe4C/GQ7p1TCM1pC3yNw1tX+npj1MWtnz2kxa9If//3/OViZNZ7e/hwSwMrXaC6jJZNg3cxcliRqSaE6LI/ES4ktY6EipI7lVOGhOJDDYL6OlONSGHhtZj3Y3wtzPTN3pHi7//8Q8QkNVG/y5yo4MlzcttrX4O8nZp3MnJZAPc2clu4ZW4inPmr+iHlsc15TmRpmTc1cDnN9s/bmOZs8lfW8u/QJxk/8NaVdupBELQBb6GHVwp6JYc8W8bLDUSt77ZvKcDGzRezrhtbbrLNvW/AvtGZWS2ZGsXUu9jwbM6slNHskWMvQ3Bp/lk0SPqrwWLkZZq6GI9+mNvgDqUtZI/VuSNjinzYzR4x+4ArkYpT1C2SS0LIcF7MGXkqsmruppZB+gfnBWofmi7ippZ54K2/IzMnxZ2dUs4GeVAdyR8yslhSqHH3B3qcaSIhYq34UUmvrWxWk4aWEeuJprHOz6dVrGTDlCvIb/T9s7Hk2ZRlJ4ZkjXfBni9jzRswcEX+j/K8rCctyKesXnjFir7F5bma+iD3HxcwX2UYm3SihkH6O/JYSull1NLNxkqhlSyAjx+xfXkoc9TJzR9z4SKHK0a/MWoX2KwhmtGRVFeOuJDzPZjtQaqvROtvrQvw/c7oQzG4xa2evZzph2S3WOoEah2a5mDU283LMOqdQZdXP3pdTqGYL3YmngW1kkkADVaSwme6BrKYeVnZLClXksoF64unBFmpJogdb2Fbs4bdZr+3RmS1vlI6iSztktlSWNTApvaDNtQvNbAE46KCDGDt2LHfffTcAjY2N9OnTh9mzZ1sBuR3J4/Hw5ZdfMnjwYMf87777jjFjxlBVVRWT4+jKFhERERERERFpkYqKClavDv4lprCwkIKCAjIyMujTpw9z585l5syZjBkzhrFjx7J48WIqKyutpxN1tK5du7J27dqwwZZ169aRlpYWZavW02CLiIiIiIiISCfW3o9+bo3ly5czYcIEa3ru3LmA/4lDS5YsYdq0aWzdupXrrruOTZs2MWrUKF5//fWw0NyOMm3aNM4++2xuu+02xo0bB8CHH37I5ZdfzvTp02N2HA22iIiIiIiIiEiLjB8/nubSSGbPns3s2bN3UYta57bbbsPlcjFjxgzq6/33cycmJnLBBRfw5z//OWbH0WCLiIiIiIiISCfWQFw7Xdmy90W4JiUlceedd7Jo0SJ++skflta/f39SUlJiehwF5LaAAnIlFhSQ23FcroUE0zkhchjuYILJpqaBOIJTeyYGQ17NMNfq0GDVUOZ7vaqZ9coJJs9F48Hj+Z6nnspi+vRtVFeH/vhej2HMB5oKxe1re52JIyA3WiBuHk72gFeA4fgDbCE8DHdw4LV92iyZeTh7hrA9KDc0QLeG8GBWT2DZipD53gj7MPcT+lZ4CA+ArSYYimuG3trrkE/wnMzlPW3L7eGvvfGHEFYSFohbXx9PdUUK+RlFjjBcfxxjkhXyWk4a/SiiliSqSSE/EKZsD2oFHMGh1aSwjczAtD8U1gzBzKor5YZX1zDzkMOpTkggzVtOVUUK1T8HQlu9tVDhDtYvFef75CX8fSu0TdvDcs2PnlkfMzDXDMW1zbOHmJqhqPZwYDMg0gzENesVDHJNIpVyRyBuaKCuGY5qhuuaoa3VpFhhqcHwUR/lpFJNihVYGRqaa4UQRwjNtQe4uvGxgVzAH5jbQAJbtvtDNR2BuWbNzbBcb6D++fVQk2DVyAwNjhSKa7YjWjiuWSsPVaRRTgINUQNxE2ggNXCOabXlVnBpvRuMjR5e/f4ppoydTmKC/+enGeha7g7+bDHDcc0+aQa5hgbmlpMWCMENBuaaoZj2IFd7XQGrn5t9AqCYLOJpsGpsD8bNZSNVeMhlgxVWCliBpaHBpQA92EIVKfSjkB22INyIYcK126x2dClrdAS8UkswsNTMzc3AGTy6nWCoaxOhueVuf73M9tvrXIKXVFvN7WGutSQ56mwGuZrfQ2tZFQjINYNxzZDXHmym1lY//3SSVSuzflkUW78gZlLs+DwmUWsFLU8ZO53q7v4fnF0LfeEBr/Z62cNd7YHoZrcw69mPYNBrF1utWxCam0IVSbW1jtBc82dDA/FWjdeRZwUPx1NPCV5r2qyj+d0ejBtar1w2UIWHHmzBRxI92UA5aXSjJBiYGwjEBei1tTgYiFtjCw+2h97WB16bP5/N87cHC6+zbWOvXWhQrv3fQHt9I20TmHYE5yZAWfckKzR3fbb/c+umlm1kUk6aFZSbhI/NdCeBBnbgpQQvPtxUkUIP/E+4MX9+AXSjhIriOE7I+nCPDsj9v9ID6dI19tdKVJbVMzX9v3tk7TqarmwRERERERER6cTqibcGgGO7373j2ou5c+dyww030KVLFytjJprbb789JsfUYIuIiIiIiIiI7LG+/PJL6urqrNfRuFyumB1Tgy0iIiIiIiIinVgDCTS0w6/vbXka0e5o2bJlEV+3p7hdchQRERERERERkQ62du3aqE9TWrt2bcyOo4DcFlBArsSCAnJ3HWc4LMAQnOG3oWG4+eE76ZnoDIG1B8aaPPhD5QC+baZRXmBjU2G6LQvS9XiKeOqpKqZPT6G6OvQyxzKCKXX5tvn2UFzb/G62wF97fnBoIG4+wfXsYbjgL21J4HUOwSDb3vhDPc1lgwNJqCVuwngDy763LeuCvyQtCc8Ff31XhsyLFHxrCt1+Xci0h2AXMevhDRwfgvWyn/8AoCLwuj/BQNzkeqhIaDIQ1wyANYM2sygmjXJK8JIZeG0GcALWtsGA0TzHdBH5eKjCjY9y0thGZiDwtdYf8FjXyLWvbuKOKWn8mOhPMDTDVIu3+/+d86RWUbo6kFybAKTWwmrbexQamhta040EQ3HNwODetuWBXWcOWE95SRqZGcXWokihr0nUWuHA5aSRyTYrCNdcr6lAXDPotQSvVUdTNSlUkUIStVSQZgvK7eMPyTTrFgjNNUNFzddm7aO9jhRcHBqYu3G7/3Wat5zi1b2CNQ0E4yZllfnrFaiTeV5mOKkZjBsaXuoMx03FS4kV/pqEjxSqIga9luAlr3YdPrebblurqXdDghnuiv97XbqHV799iikDppNYU43RLxBomo61vhnmClDszrJqGSkwN4X/Z+/M4+uo6sb93D33Jjd7kzZtSje2QmlLy1Ipm2ylKJuiFBEERVzg5bWir6igiPrzdUFABBQBRaUuLwr62pelQFlkFVrZC4Uu6ZY0+01yc/ffH3O+M2cmN0mXhJb2PJ9POzNnzpw55ztn5t7MnfNMkjRhWqm1hbkiJ9ZFruCIc6XfioC0lRpiJNnEODvGXjGuLhGuodXuG0FyxEkQUMLgiIpPnAQdqt/0EGecEutKrMOkXPLgIYW4g0lydXmrdSAHl7oWiTFAZaqDpogVR73PemWuVnys/pagzD4/RPDapk3ThF2C3DBpl+RVJLhWn6kqKhCWqX3+KclrbV8bgSx0hyt48sHfcOq8RYS7kwPjJYJXXehaTPQKxUWtEkMRvQ4mzcUSuhaT5haLcTN1duz6iJEjQB8xmqkjogledVFuJZ3U00xUyYar6HRJhCvVsqSDI2e2+1ZLGnq1vtCPI8SVS7RXIFxMhqvLh2Xd9khwvfHVl+u0ZY9o15bmphxhbnN5jR1bkeL2ELdFuQni9rXdiUmUKjrtmJW1NTO9duseKXmVv0X/1HUssVEQ5PZ1Z/lYxeN7ZOwGIxAIsHnzZurq6lzpbW1t1NXVkcuNzPM+5skWg8FgMBgMBoPBYDAYDHsFhUKhqJulp6eHkpKSIlvsGMbZYjAYDAaDwWAwGAwGw25MjoD9GvWRLXfvGegibyHy+XxcffXVxGIxe10ul+O5555j1qxZI7Y/c7PFYDAYDAaDwWAwGAwGwx6NvIWoUCjwyiuvEA6H7XXhcJiZM2dy5ZVXjtj+zM0Wg8FgMBgMBoPBYDAYdmOy+G2Xz8iWmx/xMndX5C1EF110ETfddBPxeHxU92cEuduAEeQaRgIjyB093ELcSZ61+3iWPeujIUfqCpbIsxK3DPdAHPHpYJSpaQ8DZatCpZoOJdOtZEiRbjSaYcmSh1m0aH+SSe8H7uM47RtEihtVfc8rxNVjMIniQlyvDLdSWz4AyDBAChud0EGsrI9Ep5LBVq911ThB3BayxistuWvbm1rlhpLn9gAbtMIqcQtbJa0TS9YqiPx2oyevV/Aq7RSmqe3GFVl/AE4fmZaCzgjRCR2ES1Ike2Iu+etgQlxdAGupVyNK8hqmjhYAlyRXSFBmvwqyjyht1LrEuWFStii3TclIgxkfZywdw00Lo0RCPS5pa5g0azWhq/cY2fJWb8wqgVfVfBmWnLgSq29NxZJRKtlrdGwHuWyAuuoWcgSoodUltpWYWMV22hJUXfaaJkycBFGSJCgbIMSNkCZKH0ElOxWhqRBPJex5Ebf2ELdFuUlimii3UYvpRCrVSWGl97jy6MdAn08Qp5NKwqRsOalbpNtjx13iHQjmyGWtOtRUt9ltq6WNLAEmK1mwW4LrnZa55KUpFaO0NhXRa01KyYRzeSK92KLXQjX41mCdey1Ygs0WyBSiLC0sYaFvEaFKTTpeh3UtqHCSuieHKW9PkyqFbMDvirseX5HeFhPmSryTREkokXEbtbY4t5gsN0iOMGlbjCtCYYmRiIStadiWkTawiT6i1NNCmjDj2OSSB9uiYSV5FXmwHauhhLiSJnLXNRQX4pZqeXWpa6OzvS0l9sS4NVbjkrmmCdNBJXESbKbBluVW0ckaJhOjTxO71tsx1aXE9TRTRoIcQVvyKhJhr+Q1QE6LZ3Sg5FUTCGc6oizdtISFlYsIlSTdQlxdgiuXPomZxEe/JOpiXBhchltMmivb6WVoUmKvMNcry42RpIU6O7YizhVRLkBWxUePi1zjRbpdNF4taQrqY9C3CUdwm8WJVzEhrvQtmZf0brWtfLQ2qXwtVpvRHal6TLS42GX1Yv18P9jyYNJcTZgr8Y2k0vTFonbMRJQbIGfLr71i3HparP7bFufg2s17pORV/hb9bdeJxMpH/m+Ivu4Mn6xYtkfGbjCuvfbaos4W4ZprrhmR/ZgnWwwGg8FgMBgMBoPBYNiNyRG0f1wZ2XL3vmcv7rvvPtdyJpNhzZo1BINBpk6dam62GAwGg8FgMBgMBoPBsDcweoLcvWcYkSDuFp3u7m4+9alPcdZZZ43Yfsyrnw0Gg8FgMBgMBoPBYDDstZSXl3Pttddy9dVXj1iZ5skWg8FgMBgMBoPBYDAYdmPMky2jT1dXF11dXSNW3nYJcv/2t79t9w5OOukkotHodm+3O2EEuYaRwAhyR46hhbgHYFlMhX2xzK2KqpBbhhvVsntFsV4J7HhAl5Z7Zayo7UuGWA+WPHQ4ke4gEt1oZYYlty5l0aKTSCaL9aO1ajpJ26iIFHdftk+IOx6U32+gDLfMEsIC1BywkVR/hHR/mEnVjuh1kqqXiGEBl5DVqrlVZxFlrm132lBUnluWhWDOLc+tZKA4t4yBQtwSLFmgEMUSunqpxBL76VLcaWofE7S0sUDWan+iM05NdZst65zEWk3mOrgQt76IBDdAljg9tuQUsIWvAElittAVoIV6O55uqWujLV9sopFgxseHlo7lfxduoTlUbYsu12jHQJfmpgjTRq29To7NAGEuONLcDThi3LG4pLgBsqSJ0MAmu+0pwi7ha0rFLEiOMhLESA6QvqaJEKPPrn+UPjqptNeJ+DVMyhbilnbnyUYg2KLqqUlcO8ZECadSJCJxe7seJWKVOIpgVeJrxckRuEqflpjrEleZd4txE3bcO22LtiOK1aW/DWymjygNbCKmLlw1tJIjOKQUt0PFpIf4ANFrY6qJzkjVQHGpxKdXTbs8y0CmL8rSmiUsbFtEqCrpSEgFkb424kg1lRRVZK4iGk1E4rZ4Vo9tHzFyBGilZlCZq8RRYi6y3BZNmivHTZe8SqxEdrs9sbL7kMRKl95mcUtdiwlxS3GLc/X49DJQ7CpILFF5dNnrEDGO0Uc4laIp0mjHuEpNdVnuehV7Eby2Umv3y0o6idFni6aLSV4duWu0aN8S2az992ILZKJRlq5ZwsLqRYQ6kwPjpAtzwRHAdqv4iXhVlWfHCS0m5SqWIm7t1eIs+fQ4Snx12WsRYa7EN0LKFjKL1DVM2ha99hEDsGXCuuBV+lcxibAdr37c8tsWHDmuV4hbo+JbLGbgyHGlX61X2wi9an8TPXFt9ORJaWmyLNv0e+JbTJor61Q9ChXg64XuhoGi3BQRWrQDXUbCFuOmiFDf3caG/nIm13fvkZJX+Vv0l12nj5og97MVf9sjYzcYN910k2u5UCiwefNmfvvb33Lsscdyzz33jMh+tuvJljPPPHO7Cvf5fLz99ttMmTJlu7YzGAwGg8FgMBgMBoPBYJEjMCqvft4bn2z56U9/6lr2+/2MGTOGCy+8kKuuumrE9rPdw4i2bNlCXV3d8Blh1N9bbTAYDAaDwWAwGAwGg8GwraxZs2b4TCPAdt1sufDCC7drSND555+/1zyKZDAYDAaDwWAwGAwGw2hgXv08OohVxefzjXjZ2/U2orvuumu7nla59dZbqa2t3e5KGQwGg8FgMBgMBoPBYDCMBnfccQcHH3wwJSUllJSUcPDBB/OrX/1q+A23g+0S5Hrp7+/n5ZdfpqWlhXzePdbr9NNP3+nK7S4YQa5hJNgVglyf71rcVtQdYSOFwrdGojo7xeBS3Goca62ejiOGBSsMlTjCV68M9wA1r4ti+3HkuQd61qPKQ5W5uUilq9S0A7ecFRyR7lAS3TcGJkcjGZZ8bymLLl44UJBbAnQoGfBwUtyxWrrkGedJG0yIm1W7mNBBIJizZbhgyV1raB0gdRUpbJwEKcIARJTIdY1HjOuIQ3vUtmUuOSsMlOf29cRIblABF3Huak2cK4JKXZwbYOBx8R7HyVgx80pxxwIlWegPUjPNkeIKIn6tpc0eX93ApqJC3DApl/zVqlqWOAmXHFVEsXqaSFQlbk4MJ1KpTMdWeo8rjy8TYMrSE3h+4csEQv0uWWsCS1QqYlFHZJyy88m+ZRs5HtGyPrrWWp3LX9FLvqvUFuOOq97kkuKK0FWEySIwFeFrDa0EyREnQYCcvU0nVTSy3o6XxENkuWUqBkWFuMXErwEcqbWS5WbrIJiC3nI/6UjEJRbV46gLikUwmiC+TcJcXZILlhxXZLkxkmzSTsgGNhNTUmSJWz3NlhiSZuIkyBLYIdGrLcWFgRJc77KeXgqZ1ihLD17CwvWLCGXUxVKkuCLfHEKYW5is5J8VWMcohS3MbYo0EiNJmrAtye2k0iVzXcNkO87AAKGrLhzWxbhW7MLE6aGWVsKkqaGVHhUjid1gsSpUg0/ko15haQpHPrpGtVlEr9L3ZLtSHJHoRBxpqy7BbcQtdm2x4mULW0txrm9FhK52jEuhu84Sj7bGBgpdg+TopJIOFbMEcSUgtgSlAFkC1NNCJZ30ES0qed2meIkEWIlwM01RlkaXsDC5iFA2uW1C3Dasz6hSNS8xFNq0ZYlJOQPFriLQrWCg1LVd5ZNtvPHNObLcbB3kgrikrl5RrtUPW2xJroiE4/RQ29dGKhKmvCVNITJQIuyKSbunj8mfJuuLxEzm67Q8JQwux9Xp96xrU9vpaV5xbnuR5cGkuSmtPRNVm6qHFuV2UGn3OxHj9pWGiXelaesoYcy0/j1S8ip/i97Y9XGi5eERLz/ZneaKij/ukbEbjGuuuYbrr7+eyy+/nHnz5gHwzDPPcPPNN/OlL32J73znOyOynx1+DumBBx7gggsuoLW1dcA6n89HLpcrspXBYDAYDAaDwWAwGAwGw67h1ltv5fbbb2fRokV22umnn84hhxzC5ZdfPmI3W7ZrGJHO5ZdfzjnnnMPmzZvJ5/Ouf+ZGi8FgMBgMBoPBYDAYDCNDjsCo/dvbyGQyzJ07d0D6nDlzyGazI7afHb7Z0tzczOLFi6mvrx+xyhgMBoPBYDAYDAaDwWAwjBaf/OQnufXWWwek//KXv+QTn/jEiO1nh50tF198MUcddRSf/vSnR6wyuyvG2WIYCUbb2VLcz3IgjnRkR4nijDhMAGu3u4RC4TPbvY27PZM8a/fR5vfFEooAVSHHSTIeq+pJLZvuK9F9LQeD0ltY3pJKHLfLWM/yAWqqu1bEv0KRdd71Xg+LeF1eZSCy3y1OUjScYcnnl7LonoUkXyrSj8RBIqGTdhdztIDVRTrVvO4lkTH1mqOl5oCNZLMBkj0xJlVb3pE0ESaxlgRlALZDI06COAk6qaRGc7XU29ICC90zkiBOGzW2F8NyXzi+kCQxWtVgcXGQrFV9I0yKCOkBLpe2N8c73bckiz+SIr9KDVb3elzKgNWeOMmDmuJqmWR5WiombbHjIJ6aCGniJIjSR4I4k5WbJUaSOIkBbhbLV9JKgBxR+lS8LB+JxDBKH0G1Pk2EOpqttikfSSJi+Yp6iNtumCQx17w4cnSPy4bMZPZZejLtC//C+tA4ly8noo5sE412XHW3SCs1xEgSIOvy68ixCJCjpb3Ozi+uFvHMVNLp8rSESREnYTtpdE9LlD4ipImp+EiaxMTrIUlHIlRtTQ7taOnCQTkjSGH7WgDHZ1A3cJ04RRKROGkitq9lexwuOQK0at4W3eUCjmuklRo7VnU0kyZCJZ2218bqR2W2B0J3jxTztARzeSJ6HHQni8RKzovsMNMuyISiLJ27hIWvLiKUSDqukQqca0gdjqdBHCMSYzk31THQ/SIEIVVqOTByBAbEtYnGAf6WGH00U0ecHppoJEDWjmklnbarpZJOqpRrpJ4W0oQpI2GnbZPTphrHoSEeEnFr6Je5Xi2PuEZ6sbwXXu9IqZYu8+JokXiWqn/iKOnH5WgZ4HDxekdUn9YdIxLfZurs2AbI2U6XPmIAtmtEd7VI30sRGehpEe9Iu6d/eZwjmZooS0NLWLhxEaFYctscLTKPFq8Whva29GvLElfxuEgMwe0ZaaO4wyWI04e3wTPSo9xyZSSIkFbuqojjatmUplAKvk1aW7yeFtGQtWB9p2jT0oZytOh/uvRq8xK3wZC83u0DDHS5SLyyFPe26Msptb3ed1OqLfW43C3pEkhFwiQCcdVPo9TTQrw7abtapJ+1vVFC7Wl7trPlR12fHDVny1cqfrtHxk5n8eLF9nw2m+XXv/41EydO5MgjjwTgueeeY/369VxwwQX87Gc/G5F97rCz5eabb+acc87hySefZMaMGQP+ePyP//iPna6cwWAwGAwGg8FgMBgMBsPOsGLFCtfynDlzAHjnnXcAqK2tpba2ltdee23E9rnDN1uWLFnCQw89RElJCcuXL3e9l9rn85mbLQaDwWAwGAwGg8FgMIwAOQL2k6sjXe7ewGOPPfae73OHb7Z84xvf4Nprr+VrX/safv8Oq18MBoPBYDAYDAaDwWAwGPYodvhmSzqd5uMf/7i50WIwGAwGg8FgMBgMBsMokiNIbsf/fB+i3PyIl7k7snjxYq677jpKS0td/pZiXH/99SOyzx0W5H7pS19izJgxfP3rXx+RiuzOGEGuYSQYSUHuQBnuJNw2WIAZWHY5Yd9itWL7hLdRIAS8sh3bAEoMt33bVWOZ62ReypjkZBkXcuSuejgacQtwD8DJd7BKTzJQhnuAWqdvl8ESwh2QstI6lY2uMuXs780ILiq1ea8st3KYdK9AN6raogS60WCGJR9ZyqJ7F5LMhlzyXJsmHBmdiHGn4bRZYqPH5QAsOe5YoCQLPUFbiBsLW4JXEcHG6SFBGbW0uWSwYMlRRYYrYtg0YeqUMTJA1pbb6nJcgBbqCGOJKBPE6SNKRC2vZZItSwVYo/qBLna1lt3iXFk/QJy7WnWYkiys1b64VDqxphJLmqtiGB3bQS4boK66hRwBW/gKUE8LKcI0YMlgRYBbQ+sA8WuUJAnKbJlwDa1u+a2qs6SliVCTsvZV2q2+EImoU4lbO8ZECaesPtkWqSVMih7itlRUl+Y20UhZJkVy6X/QvvAvVIS2kiBOJ5XE6SFJ1CVvlToNJcwFCCqhZoQ0m2iw19fQSi1tZAkwGUsoXEezLcVNEaFeLXuluGE1DZBzxWnImOgS02LLMu3HLb8VSaMujJR1ddp2Ki1bB8HUjglz+5S4WGIuktxm6u1pmBQt1BcV49bTTJQ+cgSZzBo6qLRFpVkCLimuiF7J4sht+7GklSJ59cZLF3R6p23Ycs5MaZSlxy9h4QOLCDUkUS5fS37ZiyPMlZh6xbkpLY8u0I2ofUSAIGRLLZlra6yGCClb3NpBJUFydFLpkuWuV/0zSYxm6qmllSwBW4xbSScBctTTTAeVNNJEUL0AtWpr0i15FUGptF8EpiIkXaPaux5HhCtyV5HArtfylGhppVpZIl4V8a0udNWlpCJnLWegELeYMFc/BhLzaizBbMQSusZ60zSXW/24KdJon5dyTW5WtlW9j9XTQk2qlc5IFfXdbQR7oRDxSHGzDC5dLnFilumIsnSKEuSuTzpyV73PFRPiglsMCwNlrMNJc/W49mrbtnvmvcJcXf7skboOJcqNk3AJXss3pa2YBFRbKhjYlySWWsxckmGR5wZxzk9hKCFuC0MLcmX7uiJp3j+H+j1p/bil43o89TIlfv04/VMX5Uag0AC+lHUdaC6vIZ5LuMS4tFjltKVLqD11zxbkfqfrUkpGQZDb353mmopf7JGx0zn++OP561//SmVlJccff/yg+Xw+H48++uiI7HOHb43lcjl++MMf8uCDD3LIIYcM+ONxpO4GGQwGg8FgMBgMBoPBYDDsKLqz5b3yt+zwzZZXXnmF2bNnA/Dqq+53luqyXIPBYDAYDAaDwWAwGAw7Tg7/qMhsc+xdWpBMJsOCBQu47bbb2HffYk/+jxw7fLNlV9h8DQaDwWAwGAwGg8FgMBh2hFAoxMsvv/ye7GuHb2M1NTWNZD0MBoPBYDAYDAaDwWAwFCGrXv08Gv/2Ns4//3zuuOOOUd/PDgty/X4/1dXVzJw5k1mzZtn/0uk0N910E7/5zW+2u8wnnniCH/3oR7z44ots3ryZv/71r5x55pn2+kKhwLe+9S1uv/12Ojs7Oeqoo7j11luHffzn5z//OT/60Y/YsmULM2fO5Gc/+xmHH374NtfLCHINI8HOCHJ9vl9pS5MYKMM9wLO8L5bZVaGLZHVKcIvUOoFkpkhGL3r9396G/EIN8NI25tWluFp7qkKOwFakuF4h7iRt/mCgQ80fSHEZ7gQsEWqnzDsy3JoDNgKQ6IxTU93mErqKdDVe6aT19cRIbqhymiEi3Z4IbNCaV6mm2yPQfQOigQxL5i1l0TMLSYZD1mHfqOUrxS3EkxiNU9NpWCLcCVqeaSnojBCd0EG4JEWyJ8ak6rW24DVCmhRhW+gaJ2FLcOuV+DZBXK0PU0aCGMkiYtyELYSVbQRLKOqIcyOkbFmuLtLtI0qbkreCI8KtVAdWF+cmidnyVpH6tlFrr9ePX19PjOQWddxEOjwpC/1BKiZtIdkTY1y1Jb9tYJOS4W4mRp8tDxYprj6VGFjTZlf7pc5BJYUVkS5APGVNh5W/6uJRfcrg0twsAXKZCKuX/pCxC68jFQrQR4wIKdYwWYlpUwOOyXDC3DAp0kRseWkrNbYYt4FNxNQ1SuJTTzMpJRMOkHVJcUWGq0txBwhfh4qLvuxNC+JIYEXWqcdSJI26OFLmdYmrMIgwtynSaJ8HIsbcTIMtHJV1rdQSIEunkr3KsvRzOQ9FjFtHs4qfJSvNKSEuYMVOSUttKa4uBBaRZguOgBPc4lKJmcQpwEC5sBJcZrZEWbpoCQvvX0SoX30GTWSgiFMEpCIhFmmr5PXKcVO4Za7assRZ+ndbpNaOschyO6gkRtIWu0rfEjFuJZ1ESFkyYdWnikpem3DHUO9LxaS4TaqeIsLtx5GJ6jGR4wCWcLcGt1BUl4mK4NUrzNXndWGuiHZFmKsLXYM4IlUR5rZDYbIlIO2uc0tdK+mkiUZ61LW6TMldpY/FcwnKW9JWzDbh7kMib5WYtaiYNOMWDLdDxh9l6cFLWPjEIkKVqh+JVBgGF+J24f4Og5a/QVseSpqrS3K923oluTKfUvmzThuoVnENqnyagDiSStMcq7M+L/p6SEXCAwWvJaAut07M5B0H3phJfSLavMRHl94OJ8Stwep/Q9GAI77W8Yp1vdLcYmJd/V0CejylrDLcotwGrHhmrflCxOqnhQj4urDFuHLNaOssofYje7Yg9xtdl1FSHhl+g+2kvzvF9ypu3iNjNxiXX345d999N/vuuy9z5syhtNTdqUfKP7vDw4jWrFnDihUrWLlyJStWrOBPf/oTmzZZZ+yOHqTe3l5mzpzJxRdfzNlnnz1g/Q9/+EP7Rs7kyZO5+uqrOeWUU3j99dcpKSl2tYU//vGPLF68mNtuu40jjjiCG264gVNOOYVVq1ZRV+e9ChgMBoPBYDAYDAaDwbB7MXqvfs6NeJm7O6+++iqHHnooAG+99ZZr3Uj6Z3f4aO2zzz7ss88+ridPnnnmGS688EK+853v7FCZp556KqeeemrRdYVCgRtuuIFvfvObnHHGGQDcfffd1NfXc99993HuuecW3e7666/nkksu4aKLLgLgtttu4x//+Ad33nknX/va13aongaDwWAwGAwGg8FgMBjef/zmN79hwoQJ+P1uq0qhUBhRXcqIqofnzZvHjTfeyI9//OORLBawnqTZsmULJ554op1WUVHBEUccwTPPPFN0m3Q6zYsvvujaxu/3c+KJJw66jcFgMBgMBoPBYDAYDLsTeQLkRuFffi90tkyePJnW1tYB6e3t7UyePHnE9rPDT7ak02nC4fCA9H333ZfXXnttpypVjC1brEH09fX1rvT6+np7nZfW1lZyuVzRbd580ytEcEilUqTUOHewxsmB5dzIZLbFZ2EwDET6zo70oWhUf5ytAOTVVMipf/becDlbSrA0L14iuMc7R2Xb7WF7Hj3M4q73UOS1srX2lAASDhm2GsbSyCi9hT1uGqxbyvIZ4tO21auhhzQH5JTEIeenJGPVIZvNUpLJEbF3AtGslU/yABSyWchpMdTKcu0zT3Ekj/cJRp/VjmjAKjsayDht06/kQc+y3na9fFf3yULOTzSbJZTJQjZLJJMng9XeMHkgTwgoUCAEBPGRAwLqnn0QH378BNT/PgJqqlcmBIQoENaWpXpBO6/f3jagmuDHr/YTwE9Qla6XEFQNDKuGhYAcBft4hdV8iepTEfKu4+c6bhKbXBZyBUrsmFgvXXTi4cQipNVN4qDHwGqbu/151YHz5MiRw0eerOrnWVW3TFZ1FOli0m8KnmVZL/4RIJuJ4s9accr5S8jiI0cJOfzkM2rfmTAFAhQIU6Cg1TM/4Jj4CNhx96u2yrGw4mBVK0yBoIp3mAJ+Cqpb+uxtA/b/eh+xeldBxSZPWPXACFlKyBEhm42S85eQyUaHjovev31Fpn5t6ves98574y3XC0HVIZuFQhZymRJVzwg5CqrHRMiRpqB6j9UPchQI2e23oulTcSnY/V5iVbBjZUXbOmZZCgTIqQt5Dr87RvpHg153bxukzfJP4hLAfQ0NatMAZILWccgEo866gLPeRi/LG28f7uMm//R6ysdBzomz9O+cv8SOcZ6cOr+s+DKgb4XJk1PHwzcgXoWcVbYvV6Q+3ljpU+lPAW0q/4IMjIl+nQ5py3qanlcvy68t68dK5qVe2xLfnDWVNmczYQLZANlM1DrfKCFPxHXNylFixyyby5LJBazt5TjlGXgMi8VOq3PGr/pRIOq0wRsvmZd4eeOoE/Kk6/H0liex9GvL+rxeX28boHhsc84/iWkuU0KWAtlsjqw/TCYbcPqZHju9TO/+/J75wepcLG6y7P1+MNxfgoPl8f5t7t2XXifB71k/2EgNvf9o57/0U/sc9VzTMgVd3mcwDM1g2tqenp5B9SQ7wg4LcsPhMNOnT2f27NnMmjWL2bNn09DQwM9+9jPeffdd/v73v+9cxXw+lyD36aef5qijjmLTpk2MGzfOzvexj30Mn8/HH//4xwFlbNq0ifHjx/P0008zb948O/2rX/0qjz/+OM8991zRfX/729/m2muvHZB+zz33EIvFdqpdBoPBYDAYDAaDwWAYOfr6+jjvvPP2SMmrCHK/3PVVIqMgyE11p/hJxQ/3yNh5Wbx4MQA33ngjl1xyietv+1wux3PPPUcgEOCf//zniOxvh59sefTRR/n3v//Nv//9b37/+99z1VVX0d9v3U1csGAB11xzDTNmzGDGjBkccMABO13RsWPHAtDc3Oy62dLc3MysWbOKblNbW0sgEKC5udmV3tzcbJdXjKuuuso+EGB18MbGRo4//njzNiLDDpPJZHj44Yc56aSThnwbUUXFD3Br9KfjKOoB9sP95qGp2E99REPW2wzkrRty6sny/mq6apCdRwH9gbUuYPOgVVUV1ua3bOubjN7ZhnyqXVEtVgfgbosY+ydi/ZjRgPXWnk4tj8zvp23rmk9D1g89Qar320w6FSbdH6a6qp0yeuihjImsByBNiDg9tKnXAZRpr/3poZR2agiRIqI9HbS+w3rlQUlpH8Gg9bNV+1vONYxK6407vKUFXt4U0Yv7TUMVEC1kuDP3MBevOYlkIWQ96bPOEzr5cUce6mvA+pVtvJZnovWmneopm+npKqO6qp0QKTJEmMh6eihlPBtJEyZOD2X00EUFE1lPmhBl9BAlSQ9xxqvXLAXIEaeHNGHGqLcQlam+GyVpv12ojAT9xOxXDfZS5nrtYA9l9vwGJthvxNnABCKkCKlyNjLBfnvPRvV6JdnfRsbbx2Kdeu1EmDRh0mxUgSijl/VqXYgUzR3WcSmr6KH93XGEaxKMqdpKjoArJj2UsQ/r6aGMMbSQJsJ4Ntix6CHOBDaQoIwJbKCLSvZhHZ1UMJ6NBMlRQh9B8pSSIEKGcDpFOJUnmMJ6C4M8XClvo+hT0y7PsnxHkPPQj/O0WrlnCnTWRgmnU3T6aln58I3MOukKOkLlhEi5jslGxlNJFwnKaKeGCjrtY5EkShs1lJHQ4m7Nh0mxlTpCpNjCOMaymZj6pbGOFlJEqKCLGvWWIonPWLYQow8/OWIkyRJgPBsIkiea7iOYyxPu1eLRjfVkWwfWNWurttwPpHHesBNU+WWaxXkCSM6VGNavo+XaujFY52CFStO/f1apsiepfcpxkDyl2L/ySsw3hMdTQRe96s0uPZTRR5Q81tug0oRIEiNFiH5itGiv8qijhTG0kKCMOD3UYF2fwqSpoo0uKqijlVgySSocJr5OXVekL2VVjMDpO904b/Lo0KYxFZO8muZw+mNMlVMKbIZMXZSHT7uTk/58MaG4+lzqwLrWyH7kbS0TtGmrFmu9D4fVcarSyulQcU6p9BwUqsCXhkRtmEg6TV80So4ASaKk1YfYRsYTIEcfMZKUMIatVNJFH1HKSGxbvKpUXaXdJVhvQIlhXZur1LQW621z0lY5B9tV3g6ct/N0qdjG1DGQS38fzluFwP12IokFqh76G3ImqG37sd46p+eVmHWobfqx+nWfqrOkd6t8EetfYqwV17ZoNWHSdKmKVNBlvXEu10O8Ne289ahD1VtiIzFbh/OWJomV1Ef+XuyATFmUh6ffyUn/uphQPmnlH6O1XfJ63z6kn8tevD9Me/8+1X877cN5yw8qTrK8FeftfRJPiZ8cM3l5oqSnVfljoFAOvj7IquMa7FblBHD6VI/WTolZO1bf0Nuvfw7ofUxvq/5gRwr3w8cp3IwD1jA0kyn+PbDY3/ve36OHiqtehsRVpmVYbc/hxLkcp+/GgARW3ORavxXa8iP3NIJhz2XFihWA9WTLK6+84hqpEw6HmTlzJldeeeWI7W+Hb7bMnz+f+fPn28v5fJ5Vq1axcuVKVq5cyfPPP8/tt99OS0sLudzOG44nT57M2LFjeeSRR+ybK93d3Tz33HN8/vOfL7pNOBxmzpw5PPLII/YTMvl8nkceeYTLLrts0H1FIhEikYFXkVAotN2v7DUYvAzXj5JJ73O3+nOj4H5GFNyvYQ5ZH15yL0Z9f7Q/YDOeZS/eRz5TuO/rFEM/VYbLK3XcprGhIc8Uqz16W6R98oVLHiv1PgIO7hDqIQ3koRCAQJD+UIBULkg6aM2H8NNPgLTKnMZPGh8pFaiwVmhK5bXGvTrXvGRQXWZDQYJBKz0Z0NoUUM8N+7Q0fZiDjracLISsmy3yyLKOxGCwR5EBAj67zUnV3jwB0qq9Kfxq8JaPDJClQAbIkSdLgRx58uqB/IKqgDWkIUsBP9LZfOog+UnjVwcvQMoeQmLlCdtDhiycm1VSptWsnBomYO1P6oCqn6RJCdbT/AX7+Pnw4QPX8etX+80TsI9VMBQgGQiRU3HJeWKSxmfHQ1qsx8Kqo9Q7Q4GsikMGPyn85AiQIkCOIP0ESRPMpwjl8gRzWH1ZwlFsyEWxYzrYo+Xg+qQPhiCYTxHwW9/IA6F+/KHIgGNiHfk0PlV/aYf8k+Mg8c6rfuFXcfHZcXIfm5yKlFVOwRUfH2n85FSMAio2OYL5JEFfnpD+mLp36IQ+bALcj/QXGYpinzMyrw2pcD3KP9QwEu9wJD3m2qP3dsxDKYL0E1DXND8h1/Agn4o7+OwYO83JqnVZ+9hYcUqrMksIkiSYSZIL5QgF0u44FbueDBafYrGSP2hlXq8OEMomCWXVB0BmkG3065K+n6GGfnmHe6n4FoLWMIJgKEcwnyYYQg1z8xFQB9DqUzk19CpgX4OsQVmpbYtXsSFm3iFl3jbp/Us+l7Kef5KW0faRUeVktW2z2jpvmTKv79N77d/W2MpUfb2QuAZC/QRJEVAf9EGS5MgT9CetmAUGKUf2492/d2gYWr2BUD5JKJcc2MaANq/fXBnqZos33fu1Q//czHqWtTq55r1D8Ip9x/DEsxAEXwB8cj3Qh5h5+1uxmOn70Ouh189bd33eGy+GWF+MwfIU+xrn/S4yVFxhYFyH66cBz9Rz7Q8NuFDveeTUN4jRKHdv4bHHHgPgoosu4sYbbxz1J3lG7N1Rfr+fAw88kAMPPJBFixbZ6d6nSoaip6eH1atX28tr1qxh5cqVVFdXM3HiRP7zP/+T7373u+y77772q58bGhpcb0Q64YQTOOuss+ybKYsXL+bCCy9k7ty5HH744dxwww309vbabycyGAwGg8FgMBgMBoPBsHdwyy23uLwt69at469//SvTp0/n5JNPHrH9bNfNlpdffpmDDz54wCuSBuO1115j//33Hz6j4l//+hfHH3+8vSxDeS688EJ+/etf89WvfpXe3l4++9nP0tnZyfz583nggQdcEpt33nnHZRb++Mc/ztatW7nmmmvYsmULs2bN4oEHHhggzTUYDAaDwWAwGAwGg2F3JEuAwCg82ZIdhTJ3d8444wzOPvtsPve5z9HZ2cnhhx9OOBymtbWV66+/ftCRM9vLdglyA4EAW7ZsYcyYMcNnBsrLy1m5ciVTpkzZ4QruDoiUqLW11ThbDDtMJpNh6dKlLFy4cMAwIp/vV9rSgbjH4+yjze+Ly89SieMkOQhn2E8j1phXUb2Iu0XyVnoq1wkUe6lXCe5bsm8UyaMj5Xbido0Uy7d5GL+LuFqm4YRDxqnryqWDscY0gzW2uB9rvHpc1WMCVizs+RR0Rqg5wKpgojPOpOq11jxxJmHNpwlTR7OdPlmlpwgTsccvWaxhEhHShLXxWU2qsnHNt7OWSSot4aS1q7RKK62vJ0ZyQ5W1siwLwRysdsZqRQsZlnQuZVHvQpIyxKoSa/yyN+bT1LoIyi9heVpqpm20250gTg2tREgTJ0GUPru9CeJU0kmYFHESxEiSIE4jTcrlkiBKkgRlKi1CDa2qSp0ABMkRVdYEiU88lSCYy9MRq1CxjtCnXpeV1Lwhlr8iPCBd4iuxteYTnrgn6CNKG7VF163RjoV+rPRjlCBOA5tIEaaSTmppc7WtkSY7HhFSREkSIEucBEFyxEkQUO0PatMwaWpSraQjEaq2JslGINiijlMLjicDHEdLr2cq6bozqRTrfI141gWwzmUtb7I6ykNPLuHE4xdBxDoubZFawqToIU6WAEli9BEjTIomGqmikzVMJkYfKcJ0UkmcHjt+AbK0UWtPJYb1NFNGghxBGmkiQJYIaepppoNKGmkiiPUCyjqarX6UaiWYyxPIQrAX63H0Fi0G2UFiEtTylmrTNhUDfapTo+Iu01LtGNSpfch0otpe4lmqYi6upRSWlyAFhcnga4fuyWHK29NsHFNDjD6aqSNGUl01rKtHHzE7vZk6u29Kf4uTsGMVVuerdSwijN/a5gypkDjJfATL61CtliV+ktak2rZetX+9FqNSlS+ixVzy1FlvIVr68SUs/OMiQpuSVkwq1LYNOE6PRrVvid1EtU76pgwR02Jn552s1TWl6ir5SpzYZ0uhudyJb5AcKSK0UEclnWQJWDFMNRHM5UlFwpRvSjv+DOkrm1T5ESyfhbQX3H2uWuWV9TU4/apUxVX8FO04vhZxt8DAIRr6ULletSz9THe4tON8HkpcJU+NSmvEGXZbrW3T7omtxLJMbV+n6hWB7oYwsd40iXLr+hzvThLshUIEfJtUHcU5Iv1Ej5n0L33apcovdeKWCURZevgSFj6xiNDWpHt4ssQOBg4bqsc5Njp1ONdIwavz0OOJtg9w+rl3nfTZCpx4yrUhpbYp96RVWO0uqH35ulT6JpxzU679vVqa9BPpl7qnRdLAOU/1ugveYTx3F+BjnvGEfyrAGYO8Fuj+Ivllmws86Q0MvK7qn0eyrH+n1OMq53MAx1lXr9pQrdJE59aGE6sW7D7Yliyh9gv9e6TkVf4WvazrG0TKR95Nk+ru5+aK7+2RsRuM2tpaHn/8cQ466CB+9atf8bOf/YwVK1Zw7733cs011/DGG8P90bNtbNeTLYVCgauvvnqb38iTTqeHz2QwGAwGg8FgMBgMBoNhUHKWyWxUyt3b6OvrIx63hPUPPfQQZ599Nn6/nyOPPJJ167xvnthxtiuyxxxzDKtWDfYak4HMmzePaDS63ZUyGAwGg8FgMBgMBoPBYJEnMCqC3PxeOIxo2rRp3HfffZx11lk8+OCDfOlLXwKgpaVlRJ/u2a6bLcuXLx+xHRsMBoPBYDAYDAaDwWAwvJdcc801nHfeeXzpS1/ihBNOYN68eYD1lMvs2bNHbD973zNDBoPBYDAYDAaDwWAwvI/IjdKTLaNR5u7ORz/6UebPn8/mzZuZOXOmnS5vNh4ptkuQu7diBLmGkUAX5IbDv9HWzABNmOoIcScBIWwh7rhQcRkuWE5dXYbbjyOKrcIRyB6opl7nUxS3PK6D4oLbSs/ym0XygCM87MGS4hVDynqtyLrx2nwxKa5Icw/EiolXiBvBkqdNAEqy0BOk5oCNZLMBYuGkLe6soZU4PbbgFdwy3E4qbWGuiFC96HJWyddGjS21BFwyVn07K806cCJmFV0mDJTntq0eTzSXYclbS/nE3OPo7a1wxMYiwd2glg9W8RhEijuJtSQoo5Y2AuRIEaaBTaSJuMSvlii4xZ4CtgS2mBBX2ijLEdKEUykiqTzBFFafEGGfEgZmI5CKWG+5S0cithS3hXpbqttGrT2vi3ObmEil6uBeaa5VUtolytXnE8SV5LW4MLeWNrIEbDFuHc22/LaOFjtGCcqop4UYfYRJE6OPADlqaCVNxCV9FSku4Agcu3BLXlM4EkivELcXtwBRBIMiaI3gnMuyTsSisq4UMvkoSzctYeHMRYSySVvgmiqFvliUcCplC3Olj+uS4jZqiZBiDZOppIMEcZLEyBEgoQS7bdRSQys5gtTTTB3NSphrxSpHgEaa6KDSTisqe9WFkSJ2FTFiqWeaxRGeegW6Iq0UUecmld6gypQ4giPKFZGoLsz1xlUX6IoMth1H7CrLulA3iB3rHAH6iJG2+3QjAXK0UuOKV5wEWQLOcl+PJRBugUK1EpZ641SNJSyV9usCzmKC1/WeGOlSTomDCHWBTDLK0i8uYeFvFxHKJJ1Yi4xXF7fqUlE5ZiLELFF5GnDHbCiha0qVIfFX9eyeHCaSStMXi9qiXDkv4309bjGuCGslLnKJl76ni12l34mwVZctS58oJjPV13uFuF6BaQD3z6BeYa7+FVQXvEpMJP4i6xURsS7q1WMrMZR96mlKlFuohkSF1TfjXWl8IneVz/jBBK9yLkmdRNarS6tVXDLdUZaesYSFDywi1J90x6Rfmy8mxL2/AAs0WesDBbjE54iihYhneQrONUD2owtzJabShhptvs5TplxfvaJcEXbXq2WAbqBZlSPXLV32KteafgaX4uox0T8PpC5CA/CuZ30FA2Nzv/pTcIEPxgF3qWWvOLcOuH0Qaa43nmCdr3qaV5irf++UfqiLsuUaEcSKn37ui3xbvvv2Qlt/CbVX7tmC3M90fY/wKAhy0939/KriG3tk7HY15skWg8FgMBgMBoPBYDAYdmOyBPCbVz/vMIsXL+a6666jtLSUxYsXD5n3+uuvH5F9mpstBoPBYDAYDAaDwWAwGPZYVqxYQSaTsecHw+cb5HXoO4C52WIwGAwGg8FgMBgMBsNujOVsGY1XP+8dT7Y89thjRedHk50+WkuXLh1y/cKFC3d2FwbDHkFFxd0sWVJLRcXdoPwQjp9FBgbvi+1oqQop70rI8pt0Yo1fFT9LHMvPglq3P46fZRrWNuJuOV7Lh7aMlr5FW56M43eR9ZuLNOpEbf7VIutrtToO5ndZoMrXdSj7YtVbPC3jsbwyuqflIKxxzWOxwpkCZqUgGyBamSBW1jfA0bJfeC1pwqQIM4vVpInQoAYU636WADnbPRImRUzJcXQPC0CCMldaH1Fi9LncLk00MpuV9rK4WSRNXCGzsO6wJ4nRqgaHz6l+0bXNfvu9TCSTh7cgGMpRMWkLwWk52lYryU1JFvuyXmLFJlzWR93EFnIE2K96FQD7s4ksAabxjt2+OAl7PkyKSawlQoooSdvREiRHlD47Dwx0tMRT1nJpd96qR5uqkky7nCqyCSiFYBaCNVb+0mwSaqx4x8sdx01lxImzuFySxOz9NtHIIbyijkvc5XGppY0GNg+Yr6eZRprsYySulmm8Qys11NFst7VeOVpqabPdGTN4mSA5JrOGMs1hI56WIDmiqT4nFl1QStKKQT/OuHwZf67y2N4RcPtGenHGs4v3QbwHQZzx8BJfGd8vvpc6LJdCEJQeBNYBZVC+0vIEReogsslyuEzc2kKqFOKBBMlIjD6SpAnTQSXTWE0TjczgZRXLFpqpI0KaLAEipMkRpJY229UCMJ3XbYdGDa0EyTEutYnS7jzZCARb1AD8fhz/iO4aaVH1Fw+A+A5kG3Fo6C4N8Ud04fS/t3G8C29jeQwq1DYN2H3T9lsVc7g0qW3WqGWZdqn0V7XlUtWWoFPPSD9EupJkSyFVHqGGNpqp40Bet71CWQL0EKdG9TuXq0X8PgHwvar2U63ql1L7ehXHnyJeCfG0vK2mb6hYvK3ySrruWhBPS0q1zds/t2Jdo8XjIfk24bhExHvxtoqxOCu86yZifV40atM31PRVHEeLuLnEDdNmrStfk4YIpCI5JqfW0RezrlqV3T0EuyCSSjvnme5IalF1aNZi1aTFrESb6t4Q/dwtwX0+ox0LGOhoGYfj1AHrs1f/rNUu6Xbf0fdbos3rjpRS9U9cPBLXUtyxk/kuLK+ItDeC3Z99AShvT1teoC4VJ4mbbKuXo/d56TdyToJ1Dev3xEj+zkvhvr6B2zGynoGOlgU+y9OiL+uIg8TrH/G6TLzOEa/3qbRIuhDU8unHoh+r/eu17WV9DivGetxkfbtnWfbb70mDgedqBVYfljb+qUjbdUeLPn2gABf5BsZQ4nuJb/CyvB6XPxXgAk+aXFsHQ/xNQZw25nD6h37Oy/mgH6P0EGUbDLuInb7Z8uc//xmA5uZmnnnmGU444QQKhQKPPfYY8+bNMzdbDAaDwWAwGAwGg8Fg2AnM24h2juE8LTq7jbPlrrvuAuDEE0/kjTfeYOxY66foLVu2cP755+9s8QaDwWAwGAwGg8FgMBgMO4zX0/LSSy+RzWbZf//9AXjrrbcIBALMmTNnxPY5YoO+NmzYQG1trb1cU1PDhg0bhtjCYDAYDAaDwWAwGAwGw3CYJ1t2Dt3Tcv311xOPx/nNb35DVVUVAB0dHVx00UUcffTRI7bPEbvZcu6553LUUUdx1lln4fP5uO+++1i0aNFIFW8wGAwGg8FgMBgMBoPBsFP85Cc/4aGHHrJvtABUVVXx3e9+l5NPPpkvf/nLI7KfHbrZkslkWLBgAbfddhv77rsvAN/+9rf50Ic+xD//+U8Abr755hF9BMdgeD/i8/1KzU0iGp0I9AFH4di+NCHuuJAlftWFuI1YsrB+lVXSdRkuwFyVpxKYhSPGnaXWdwIHiNFQ401lcZuKI7K112nzkzzrO3ELdcEty32Dgcj6YiLdSVii2y0Ul+JOxmrPkWrfc7FEaVVAWQo6I9QctpFEZ5ya6jbCpEgTsYW4cRJE6SNBnOmsBhyhqy7Alfk0YeqUkS9G3wAZrJAmQso2jVri1gbNblhMoKsv6/LcBHE6qSROwhbseuW5TTQSxpKtTqt6m00hy/C5334vs7bdyhufb9Ux0RlnUvVaEsSpp1kJcNN2e+ppIaViEyRHmZrW0aLa30yaCPU0k9LEr7oAFooIcUXGKtPeQZbBkiBW2MFz5lWe0mDelgSWVlgFiLAVIB1x4i/HJkmMsNIg69LcNUxmBi/bcZzBK7YQdyqraaPWTguTopEmO3ZlJMgRZAavECBLhDT1NNNBJTW0EaOPLAGmstolxbWEr1r79VhEsCSIXkFuL25Bbn+RdF2yKfJTEeOKJFePtVe0KKLceiwZZ5Ftyrs0YW4qT6Suh9pUDx1jooxLbaIp0siBvG7HOEGcGlpJEqOeZtYrea5XjJslYMfMFr0qqWRQF21K3LLavC6fFIFkG5YgUoSlIk5sV7HZpK2Xdou8VeS6Imttwbrmiky0V8VfpI4iai3FLW3Uj1G1doxSap9BrR3VWnrKWg72wviWNiiFquA6No6pIUCWGhI0U0cjTWQJECBnS15J4UheJT4RnOtrVovBm6quORxh6mBSXBEHS78sVW3Xpbi6pLVRTffRjkHQk1cEsRJPKdMry1XxsOu4SZtWYMlW9f5er7arwxHoiqTYI8od39JGIaLi0sZAwWuJml+vpcl51qamEmdwhKkizPVKcfUYNOJIb8epdJGErgGWF+AjPri3AMdpQtEGVZdicnrZhxwLEWdLulfsqsty12P1bxE5S1x7VP5iotwG8K3BuXZBcTEuuGXCugAZHFm1N0ZCP+7zWdDziRD3PJ8Tx+M8IlasOnNPEeFrMWFuHQOFuRNxjrkeUx25/kg/lJiK5LYa6MaJkXzGdWPF23us9HbKNT+rLQvF4iaI7FvqfobPkthe5LP6UhNuIS44sZQ4SuwErzBX39YrzK0Dbi8ML8yVeMn+RF4u11YRKuvXWv0zUO9fqPS9QJCbH6UnW/J7yZMtOt3d3WzdunVA+tatW0kkEkW22DF26GZLKBTi5ZdfHpA+d+5c5s6du9OVMhgMBoPBYDAYDAaDwWAYac466ywuuugifvKTn3D44YcD8Nxzz/GVr3yFs88+e8T249/RDc8//3zuuOOOEauIwWAwGAwGg8FgMBgMhoFkCYzav72N2267jVNPPZXzzjuPffbZh3322YfzzjuPBQsWcMstt4zYfnbY2ZLNZrnzzjtZtmwZc+bMobS01LV+pF6XZDAYDAaDwWAwGAwGg8EwEsRiMW655RZ+9KMf8c477wAwderUAfc0dpYdvtny6quvcuihhwLWa5J0fL4iY/sMBoPBYDAYDAaDwWAwbDc5AvhH7v02rnL3VkpLSznkkENGrfwdPlr6q5MMhtHEkcyONhspFL6106X4fNdi2V0BJTi17LI9wCosGy0QDal1ITgIJbHFEeIehiO6PVhNkwyU4QKUqX8ZLFnYLMueFi3rI1bWB0DYn7YlognitLXXED+udUD9295UdT/OY2ATmS4MFOq+6c7K8UOsE1GuV6Ibxd1OrxR3gso3DSjJQn+QiklbSPbEmDR9FQnizKl+0Za7Sjuns5oUYeqVLU4kuEPJcHURbi1tZAkMkOMCBMnRQaW93MBmEpTZy23U2nLdBHFiSt4KjqRVaFEiTFkH2PJcfbmfOBBlLFuoY6O9bk71iyQoo41aAParXgXALFaTIE4dzXa740r4uj9v2e2XuHVSxf6sIkCOKH2W8FVNy0hsmxDXK8P1SnLbsPp5KZasT4ScNWq9/KgQwZHkqW0jEYiUWPuO1CWpSiVd0ty2SC3j2EQPcfuYbaaBGbxMkhit1NhC10plmBaxsVecO0Plq6WNOpqVMLeFGloJkqORJspIWOLgVCvBXN4SvioxYVDa65W86vER4Z/ESISiXimiV+bZpuUHS4jYjSUSlE/2Uk8e/Xj4cQS5Y1R5InYsxRFequWgkjtWdSWhFA4IrrPjnoxY/bqZOmpoo4U6pit57mTWEidhi3FdcRJJaT+ORFP+yf51UW67ap+0XaYixRWZpIhdRfYqU5FplqltsiqfEoBSgVvcWoojj52openyyxa1TqSjTVhCVJmuwZHiTlTLk9W0UW2vhK60QaEBxm9ug2AbG8fUMD31OulIhKqtSfe5lsUteW3BEfC2aOlvqrJbcMtpi037cYtwRWAqn0lCloFs0NLrtWOjS1D1eTnuuixXZKsiCpX5Em3ajyOCblcxfFXF9lXVVulDuii3H3wiFS4meJXPI+lnIlPWzoHtkuIOJjCVvra8APPVD5PzfVbM5ms/VD6lBLBrtDKWFwaKXtsZmiCOYFiX5UqMJLYinR5MlKtfj/Q4FhPjSj5d4Ctpusi3AtRHtRUTsASn67GErtLWJhwZqwhcj/M5cQQndvVYomGw4ucVDhcT5j5QgEt87mN2v0fwWo9z/ulIGwOe9onsVfpasypjPe7+JOe/7Fv/HAD3Z4Euvb1bq9+7WPJZEdWKDFyX4i7wWf1F4ghWbHQh7nJtnTd2eNbDQGmulH2Jzy3NvV+le+Mp7ROkz0SAKTjXa12QK/PFpMLyebqHY91sMa9+fj+xw84Wg8FgMBgMBoPBYDAYDAbDQEb+OSSDwWAwGAwGg8FgMBgMI4Z5suX9h3myxWAwGAwGg8FgMBgMBoNhBDE3WwwGg8FgMBgM282Ps7N56KGHaC2Ehs9sMBgMhp1ib3v185o1azj++OOZPn06M2bMoLe3d/iNtoMnn3yS888/n3nz5rFx40YAfvvb3/LUU0+N2D7MMCLDboVbhjtDTSdp89vKK8NnGcCB+HzPDbIuAazdxnJOxDK87qOl1WOZ51ZZItiDQpbUqxZL1tgD7IsjADwY6MASpx2IJYidpYqSeZHhHpCCbIBoZcKW4db4LTthijANbAZwiWNbqGdStbs9a5XMd7/pL7vT2610r0y3rydGckOVtaDLdHsisEXLeJykY0kUBZHoegW6JVjy30oGSHGjlQnCJSni4R5HZlvdQ4QUUfpsIW6CuC2braQTcMS41nZpVzxEpCoy3AhpJZLtIUCWWC5JJJUmG/BT2pKnUAp9pda96kmpDbSWVwCWMLfVtrxCI00kiAOQJkIfUXudyFnBEuDuh/VWtyRRu859RGmj1hbpNtHIGFqAfRjHFlqpYDYrWaOOXQObqaWNVmpoYDMx+lT4VhImRZCcJsNtIUEZ+7GGIDniJCgjQSNNxJX0tY5mKxapFJFUnqCI/2BoIa4IcNtwBH/6NIAlY6zQyirFEVKmGCjNRa3TpLkioNWluaUVlkGwY4xV72QkZsfTEuFuIkGcMGlyBGilhv1ZxXoamcHLLklugCwzeJkgObIEmM7rVNFJB5Uu2WtEYiCi0l4c0a20W6SmulRThH66fFLkiSK+9Mpt5VQT8WK7Z72IRkuLrNOFufK9qhXr3ESrn0hG69RyNY6AVoSQJRDph0gwT6S0h3iwh5pAK8lIjBpaaaKRQ3iFFBHiJOxYpSJhSlvSTpz0mIkQVf5JuohyddGrxEKX4urpm3DkuPU4x0JEsL1Audq23LNtC1bf7FHLFaqcRk+cN2GJJWXdG7hFum+qspu09YMJXZVo17fGikOhAcavbqN7cpiqzUkKEfAFVD4R40r/EWmmCHEDWCLJ9aqeb2j76VLrwN2ngtq8IDFpUGUJTxXgMB93xg8kPeNk6oAfdZ7C9W/92RGdFpPl6hST5QYoLssVmWgJlgRa2lKt2tuo6let2iZxiWAJZktxrkfFBK8i45Xrj+wvgPv8kzoPJsWtBvVxa8lCRTC6Bksu+hGfJUpdjyV0fUqJQw/zWWkNODLR+R6Z6L2FgbJXiR0MLcuVOga1uosMV/qIyG0HE+UKOZzru5Ql23Zr6+U6Lv1A6lmH05d0GfMGLV8GS776gJIEb8Jpu0haP+Jz4ghOLD/ic8dOXzeYMLeY6PUMTxo4fUGX5TbgiFzBirFIXCXeejwFEQ97+5T3et+AW5Is6z/mc0txz/A5fWeBzyrfK8U9ztN+r/BW+ic4MdPxCnMlfsJgwlxvLO8vwOU+px/Ua22UeL6r5Q/giJd1Qa4Ig/XY7QWC3L2NT33qU3z3u9/l6KOPpr29nUgkMvxG28i9997LJz/5ST7xiU+wYsUKUinrgtTV1cX3v/99li5dOiL7MU+2GAwGg8FgMBi2mQ2+KKvmnGYvV+x3EL8uO2CILQwGg8Gws+QJkhuFf/nd8PmL1157jVAoxNFHHw1AdXU1weDI1fO73/0ut912G7fffjuhkPN05lFHHcVLL700YvsxN1sMBoPBYDAYDNvMj2qPIFZWRldrCy0t1qM1b8z9EBvy0WG2NBgMBsOewBNPPMGHP/xhGhoa8Pl83HfffQPy/PznP2fSpEmUlJRwxBFH8Pzzz29z+W+//TZlZWV8+MMf5tBDD+X73//+CNYeVq1axTHHHDMgvaKigs7OzhHbz+53G8tgMBgMBoPBsFuydOlSbn7wCY7sTPGN0BZ6vvgDnn3yCdoTPVz9Voi7Dk7u6ioaDAbDHkmOAL7d5G1Evb29zJw5k4svvpizzz57wPo//vGPLF68mNtuu40jjjiCG264gVNOOYVVq1ZRV1cHwKxZs8hmB47/euihh8hmszz55JOsXLmSuro6FixYwGGHHcZJJ520/Q0swtixY1m9ejWTJk1ypT/11FNMmTJlRPYB5maLwWAwGAwGg2Eb6M7CpZdeSj6f58h3n+GkqVGWRiJ86NklnPRiB/lCgY/VwqlFlBcGg8Fg2L3p7u52LUcikUE9KaeeeiqnnnrqoGVdf/31XHLJJVx00UUA3HbbbfzjH//gzjvv5Gtf+xoAK1euHHT78ePHM3fuXBobGwFYuHAhK1euHLGbLZdccglXXHEFd955Jz6fj02bNvHMM89w5ZVXcvXVV4/IPsDcbDHsYhwh7iQgjluG241ljQXIwDjP2w42Z4Yo+cQh1r29nbUEGA8cquaHG8dXp6b7Yte7E5itkg8D0jjN7Qf2xxLnJnGEuCKQLVP/OrFkuACdEWpmWdbssD9NnIRLhivCWLCkr4KIQmuLGAt1YavQRCNzql90pSUoo41aysoTRKY32eki0o3WdhCcZhkY+3piJLcoiW5tFv/+Vv3zq0qdAuVQvart5GAsIZomxU32xKgrbyZOgghWmwNK7Cry22m8Q5gUk1hrp1mxSNpCXa/8FhxBrsSnqq/LkuG2562rZBsQhEhzHkrAl4XSbN4WB45t67KOEVDb30NvnTVCM5jL0xGzLLBeQa6Ic5PEqFL7TRCnjxhhZQ20hK6b7WMxg1fULxr7UEcL41hPE43MZqWdp5IOGmkiQI4wKeppIUGcWtqIkyBLwJbiTmYNAXIEyFFDK0FylJEgnrLiUdqdd0SvMi0mxJV1IpvU1+kiyqA2tRpoiQOzOLLEIJZc0SvNlTxZHGmudCOR5sp2QFWX9et6pK6HYApSpRAPWO0KR2oZxybaqKWBTTTRyHRet6WuIsvVZbj1yhyYJcD01OtWbETOKoJSPTaSrktxvSJXkX62qXUi/9ukrRfJokgB69V6+SEoqOLSrNqvyxp7sUSsIqyUWIpcEBxZbCmWVLEURyq6Rm2/BkdOW40j0VXy3GAvBCOOLLe5vIbpqddpijRSTzPxvh5bjBtpSQ8UmEo71uCIUSVOpTgixTdwRL1dOEJxcISc0gdEnlih8kh/krjJPrxEsD56RF4pZVTglrduUrF4G0eKq8tye1VeEbqKLFeEriLLbVH1qlNpSkbsa7fWx7vS0AY+XeqKmtflri1Yx1eXCEt/KFX1qtHiIOlyzkksJJ5osVqPLcX9ln9/tmx5hylTpnDdK6/AB2sBOHpskv/80pe4/vrr+cK/4NWDoLTRU872ynKlbpu0+HulrnI85B8qrjkcAXFEbRPBEbiKKDeCW3i7HudckeuWyDklNoNJcfU8x/ksAanIWuf7rPY/paSmm7DEuAAvaCLRw7S7VJLulb4+pcSxujhVYleMehzxqSDXExFeyzV5MFGuIPMSpxSOGNcrFJZrGlr6eq2e67FkqQt8kFdpf+mCk8LW/HE+65xaXiQOEksv9xZJKyYbhoHCXNmPLnqtA+7WRLTy9U7as0bLJ59tcu2QtutiYiguW/aKceX6tQbn+r6eoaW4Uu8HtPatYaBcWMcbG4lpMcmwd/vBhMPCtghzRZbbj1uUK+VJTCWW0s9SWPHI4Y5dnj2eHP5RerLF+t4qNzaEb33rW3z729/e7vLS6TQvvvgiV111lZ3m9/s58cQTeeaZZ7apjMMOO4yWlhY6OjqoqKjgiSee4NJLL93uugzG1772NfL5PCeccAJ9fX0cc8wxRCIRrrzySi6//PIR24+52WIwGAwGg8FgGJL7S/ah8sRFXHrYVs4880xisRj6Tx7XXnst993xc/b/wAf5QTDFdTy6y+pqMBgMhu2nqamJ8vJye3lH3/7T2tpKLpejvr7elV5fX8+bb3pfQ1qcYDDI97//fY455hgKhQInn3wyH/rQh3aoPsXw+Xx84xvf4Ctf+QqrV6+mp6eH6dOnU1ZWNmL7AHOzxWAwGAwGg8EwBD09PTxx6OmUA7GW9Zx44sCnR8vKyvj20fvw7twjyOVy/POlVzkq2DKwMIPBYDDsEFkCMApPtmRVmeXl5a6bLbua4YYq7Qwf/OAHOfbYY/nWt77F9OnT7fSOjg4+8pGP8OijI/ODgXkbkcFgMBgMBoNhUK6//nrKq2vo7Ulw5ZqHBs33Sf9bdK5+g0AgwJ/Hf4hcwchbDAaDYW+jtraWQCBAc7N7DGNzczNjx47dRbVys3z5cm6++WbOPPNMenudsX7pdJrHH398xPZjbrYYDAaDwWAwGIqy0ldFJmMNGJr20oPUDSoHsfjs2/9HOpWiqmEiN+dnD5nXYDAYDNtOjuCo/RtJwuEwc+bM4ZFHHrHT8vk8jzzyCPPmzRvRfe0My5YtY8uWLRx55JGsXbt2VPZhhhEZ3lMGF+JmsOxYIRwZbr0lYKvESh+nNn1DTed6hLnFeK1IWuW+RRIZRrir4318WhfuTrImUVW3g5Qc90hQvlpLplgBVAEdWELcfmfTAULcbIBoZYJYWR+JzjiTqtdCA9Qo02CaMHXKfifi2yh9REhbzVXyVRHKgiXQbcY9jlLyCSJV9abF6KMRR4ybJEYrNS6RrkuiW27l7SNGS7tll4vPt+S+ukDXf0Iv+S5HnFsxaYtLihuv7iFCijBp6mkmRZh6WuypFQfvtJkIaSXHTbsEuTGShElR291FKuKntMWS39IMlECkVwlQ26xlWrDEd7JciiVGjOCIN5W4tXS9Js8NKHluFnqrnfvbiUhcHT9HnFtH0J7XxblrmMwMXgZgA5MBmMZq1jOOGbxCE5bQTJ+vp5kyEuQIMoNXqKSDNBHqabaluPU000eMOpqJpxJEUnmCIuJETftxpJLyN5YSBruktyltG12IK+JKr+hVDrVIWb0iXCVfJYUjc5V9tGvbl+IIckVOCLZoMKjaEqmDSCpP9+QwE7e22MLcZCRGGQlbjqvLcgEO4RXAOj8C5KjamrTq2Kti067qLNMu3FJcEU0GtDhK/XQpqYhyvbEQuajE4V0cSSNYcso2LJmgxFlkoiJu1XzU9v7k079P1bkGt2iwDrd0VI6ZyGfXYIlIRZ6ryXLHt7TRPTnM5L51BLLWMXCJcWUqI0wkfqh4NWkx0eW8umC4VNtGBLD1OOepiH/laesS3ALRGrg7uT+hfI5Fpavd4tbJuI+T9KkUjjC3B+eYDCfLfUOl67LcRjWtVvtoUXlEzFpntd0nx7NNyyfHRM4zEeOKTFgkyF04YslSnOOewt0HiklxAZ4qkJ/r4zcHnUplMEhHRwdXP/xvmBOA2eqJlefTsHSpNa/SDqzvJl5eTiqVYssBJ7H20VVMGtfrLv+pgluuKejyWRG0ikhajoX0RV0wLaLcapxrSrVqfxNOv9OPp8h3S7S46dcsySPxGU6KuwZLIvoRn/U5Mt/nCEfP8Fn7OExJX1/wyEYX+ByBqKDLchtwZKheWS44sRRJqchJ63AE0zoiHZVrjC52lbZKLPTrba+WT2InYtxebVtwX790sev9Wv2k3RfErOXTKtwi1vnDSHEPK9KHBImxvk0x2bBQTPZ6gc8dS3DiKVJcIYXT5ik4UuIAjmRZ8snn6mBiXLR1FVq6SHEv8FnLxaS4el/wSm2LiW+L4U0vtt09njyDCYcFXZjrleXq4uE/qfaBFZdisZSYvItzDb69AOc7LyEwjD49PT2sXr3aXl6zZg0rV66kurqaiRMnsnjxYi688ELmzp3L4Ycfzg033EBvb6/9dqLdgXHjxvH4449z0UUXcdhhh/HnP/+ZAw88cET3YZ5sMRgMBoPBsFdxl/9A1nxwEW+deD4/Dc3Z1dXZbbk7fgCVU/Yjl8vxiU98Ar9/2742Ll68mM7mzZREo/x80gdHuZYGg8Gwd5C3fy4b2X/5HfDA/Otf/2L27NnMnm09wbh48WJmz57NNddcA8DHP/5xfvzjH3PNNdcwa9YsVq5cyQMPPDBAmrur8Pmsm3qRSIR77rmHK664ggULFnDLLbeM6H7Mky0Gg8FgMBj2Gjan4c6WiP2MYushp/Bi6xrmZNuH3G5vo6enhx+93syshreY2rOFw77znW3eNhwOc9L6pbxQ/2kCU6bzzAvLmFeZHMXaGgwGw55PbpQEubkdKPO4446jUBjk6SjFZZddxmWXXbaj1RpVvHX/5je/yYEHHsiFF144ovsxN1sMBoPBYDDsFRQK8Jl18FTXSgqbmph72hlUTZjI7w45ixkv3UWY/K6u4m7Dddddx+stHST/5x5+efD2i24XBpr432X3c/eKt3gqkuTJY8Hocg0Gg8GwO7BmzRpqa2tdaR/5yEfYf//9efHFFwfZavvxFYa7JWWgu7ubiooKWltbqamp2dXVed/heFpmAAkGOFqiWF6WfmCqyip+liyOy2Qs1hj3ymF2qL++fbBXpfeAph2xGKzcTmDjEPvTt+tX/6YBSaxx+Uq6HZ2YYcm8pSx6dyHJSMjKd4Aq/wBVp2luR0vYnyZOghRhGtQg8Uo6AFw+FfGtyLSGVnIEldskQpwEEVL2q90AIqRd3pYcARLE7eU0YfqIuZraTJ3tggHL4dJH1JW2hkm2FwUcp4uVZuVbqw6qOFTE5xIts8Q28XAPOQJMYq3tabG8KynqaSFBnEaabPeK034rNuI6KSNBjCQBsrajpaqvi2zAT2l73vGNoKYybroF64cDGaveok2zWGPWc1jjjMUtIQ4B3adRgTMeXpwJ9do6IBuBVMR6ND8dsVprVaHejqEclyYaqcz0kVj6JdoX/oWK0FYSxG2xWRONxEkAlpcnQI46mgmSI0uARpqoopMUEWpoJZ6y8pZ2a34acY1ITGQcutdFAo7bQGIV0dJ0R4s+ll33BJSoqe5qAWd8u7gVSj3TIG7HgHdaUWS7IG53SZ2Vli2FXBCyAT9NkUYq6aSJRqropIwEQfWQbbw7SbAFx2MTxPFneJd1T0u3Wi8xSOG4NiSGXp9NPdYY9IgWs3Lc7gTZvkIrp1rtL6DSJb+UK16LRsh0Rln6xSUsvGsRoTFJtydGj1vOym/7M7xuh8lqXaNqW6mKrXhExAEUxLrGSYzkn/havE6bUqxrdLWKhZDS1rdhXePFEwRu50iNtm0DsB5+0Qqf2wARP7z48qv4fD5+99vfEikpwff8I1yTe9IqS9/Ora5y2i79tAfb12QfB3GwiE9HYiTp/TienKAWM4lxBY67pR3HwSLnmFyLxGUivh89XdL0uOneEWljDY4T4akCHObjzX4fM14rkC3A3//+dz509Yed9q9wvjZmTo2x9HNLWLhwIaGQcpUt8NnlbaiA/V+I0dfXx+8nw3k1RWKq+1skVmjxAreLSeImbqcAznVVrsMSO1mW2Eif0z1ccl5KX5RrNTjXKz12dSpN4qd7WlB1uVc5SvR+dL/n6/Zs7dZTPc724IoxMHhZXv/IeZ58cl0Cy5MhjoyJan9yLQ3gvq6Ki0VcWLqzRfcuybwenwpVdj1WHFu0dL0uWl0zE6Ms/YjqR8eH3W0aLA4w0H2j4/W5FMs7mL/FG0fxt+j1v0Qt6+6WiTj9qgHn2ueNYQNu34jERnfs6J6bOiwnie6PuVvztHjrui1t3Bm2xf1SzIEjeNshsby/AB/zWX2nCye2EkuJr973xFfWAm0/baW2tpaurq7d6vXFI4H8LTqjaxmBcq+IbefJdffySsWJe2TsdjXmyRaDwWAwGAx7PCuDVbz8oY/T+I9/8J+xJg466CAAJryylK2HnU12znE89sxbHO/6q3fv5BezPs7CKX34fD4+9KEPwdU7Vs6EEvj617/ON7/5TW4KT+NU1lOl3Zw3wPUt8PwW+O9q2Cc8fH6DwWAw7BiLFy/muuuuo7S0lMWLFw+Z9/rrrx+RfZqbLQaDwWAwGPZoMgUfv559FnVjx3L2icdzxca77XWfy7zMl1ftx8vN7dz/r1ZeOAb25r95/xydQuW0A5iVz3PaaaftdHmLFy/m1Ree4YDZh3HDi49zLY+NQC33DJYl4MvqqYTN+TjLxvUQ8pkHzg0GQ3Gy+CmMirNl73hnzooVK8hkMvb8YIg8dyQwN1sMBoPBYDDs0fx36Ciqxk8k1d/PJa/cT0AbEez3wVWv/A8HvQGtOfjB23BNfPCy9mTS6TTPHHIKFUD61ec5/Nprd7rMaDTKybl3WM9h5GcexesrXmK66525eyfthLl+7Gz8a15g1qxZHHXKKdz4r2VcufWFXV01g8Fg2CN57LHHis6PJnvHbSyDwWAwGAx7JU+HxpA+6HgAGp77Pw7KDvxDvy4IP5tgzX/vbT//yg8m/Nqzufnmm6kYU0+yr4/Lmx4fsXIv9L1JR9NagsEgd1YcN2Llvp/54eQPcsRJp3Lhx8/h7HEBIpEI7XNP5K3AXnqnz2AwDEuO4Kj929tIJpP09fXZy+vWreOGG27goYceGtH97H2RNYw6Pt+1wHgsES7AsVh2x2ogBGRgXMgR4qZwJLgTPFOwBbOMBeYDawfZcSewGez3eRbjVTWtxZLSFitjiydtDHAg7rPllUHKL8VqdqcqvxRHABxVeaYAVVhixzKgLAWdEWpmbSTRGWdS9VpbiJsmTJ3yB4gQ1yvDjZOwBJ6uadYWpcq2WQJ2Ga3K5thIE83K6CoiUFkXJEeKiEuaK/sUWqizpbdgCVpns9K1HCdBo7IRJ4jTSg2zsB7dE3lunASN1U2sZRI1tBIhTR3NJIhTR7MttxU57gxeGSCAjZOgTLU5ToI4PQTIWmLeVMolxI30KiGsV3yrCxIlrR+3EDaII0wUtYOIcGtw5Hi6RFeXv7bjSPFKIZiFYI31BpTSSBJKrNejxsuttiQjMTvuVXSSo4Q3gem8RooAMZJESLGeRmbwMk00AlBDGzH6qKSTADnqVWUr6SSa6nOkuCLelHZ6BbkiyZXloCcmkpbCEQt7hbgifwVHOKmLK7u0dTBQjCv7l30Wk+Lq811F8pRo87LvaghugmAdRFJ5DqhYR6oU4oEETZFGqvq6CGRxxLjSXuknkibL0s4WBpfi1mAJEIPattI/2tT0XRzRssQopZUDjpxVl3fKehGoijC3WpXbq6Zv48jIU2pZYi0yXf169zaWmFCEtSkckeurOMewTtv3RBzpcpcnXrpkU0S63ph1qXLfxpHEyn7KcM4z6Yve/qXIZHz8efbpVAYCdHZ2cvWyl2BWwDr/DnE/JvzxNXnu3W8M1R/8CL8nz8zn7iBEwel/4IhFwZGoSv+XtJSKc7+q8yas4/42lszxbRW/XhwRqQh0welj9Z74yvkpUu52nGuYnMclWpylXiIS1uuMO07UQPPmElq2bCFaWkpNbS0NmaQjcdWFrSdqotDx6oPtAxWQUq92ftkjwpztww+c+sVzefbZZynbdxZPrXmG+ZkWS1rqlW1KHUVirwtIS3CkwiLHDWLFuForQ9JKcGIt6Vmsc1COTSlumbTId0VYKvV4QIl812Mdx+Wq7vOV0PMpJTRdjyVnbcAtZdXEwf8bnUjJyRcBcOpZH+HMM8/kq1/9KpWVldx69Kf5qS7PXVFwy3XP8Dny1+FkuSJbXe6Rq0q6iHmDuOXlqJjX437LbI/Kp5+rEisR44q4eD2OJFuQGB7nc2II8EEl4z+lYmgprjemQ+HNpwtzZZ2+r6FkuSJ7lfpKLEWUm2Kg3FWu81Nwy4/7sa7xcj3Wrx3rccS4Z/icY7vAZ/17QJPi6lLp5Vp9dVmzty3F8IqEdYqJnYuVWSx2yweJob7uAp87lmdoeeQ6oIuX12j1ehdLGnyBz3lZwef0Px4MhqE544wzOPvss/nc5z5HZ2cnhx9+OOFwmNbWVq6//no+//nPj8h+zJMtBoPBYDC8j3jXX8Z/zr2QKw88g27zm8mQ/LT+cCrHN5JKpfjc5z6H3z/41x6fz8f3Jqepqa6msmECPxl75HtY013PTQ1HEy0tpauri8svv3zEyz/llFPo6urC5/Nx775D/SqyZ9NNkMfmno7P56Onp4dzzjmHUCjERz7yEXK5HJWVlfy2fP9dXU2DwbAbYv0sOjr/9jZeeukljj76aAD+53/+h7Fjx7Ju3TruvvtubrrpphHbj7nZYjAYDAbD+4Q0fm6a9VGq9plMfPpsvjt1wa6u0m7L2j5YUTERgNoXHmb//Yf/A3Y/f4L6F6xHiHvmfpAV2ephttgzWNsfJDXtEADmzJlDSUnJMFvsGOeddx75fJ7KKftxf8k+o7KP3Z0fTjyO8ppaent7ufLKK+30+fPnUyhYv/j/e/YCEuZGqsFgMIwafX19xOPW0/sPPfQQZ599Nn6/nyOPPJJ169aN2H7MzRaDwWAwGN4nfG/CsVRNnGQvl86Yy52l03ZdhXZTCgX47L/hD3/+Myv/53dcsfXFbd728taX6Fj3LqFQiN+M+xD5veDlMN9fleXnP/85qx7+XxYtWjRq+5k7dy79q16kvb2dX7YH7JsLewtPROoJzPoAADNmzKC+vt61/ktf+hI9PT3Eq6r4acMHdkUVDQbDbkx+lJ5qye+FT7ZMmzaN++67j6amJh588EFOPvlkAFpaWigvLx+x/ZibLQaDwWAwvA+4r3ISvjnHADDusf8h+68nePrpp/nyc2tYk9nFldvNuLsNHt4KER/8d3Y1ge14na7fB5965e9kMhmqGqdwR8VBo1jTXc+qBNy5Hvr7+7ms7V9DDrUaCb7YvYw7b/k5S19/l3vvvXdU97U7kS/AvQedit/vp+ut1/jkJz85IE91dTX77rsvuVyOf/YHWZ8uUpDBYDAYdpprrrmGK6+8kkmTJnHEEUcwb948wHrKZfbs2SO2H19hb/tZYQfo7u6moqKC1tZWampqht9gL8SR4k7CMsFOwzIz7mtlGIcjxJVlGCjEHeuZ1mA5dWuzELTsmeGSFOm1Re44BoENg1SwE0t8WznIus1qPoojnhReZSB6OVK2Xu+DgQ5gMla7p0G0IsOSzqUs2m8hyVIfNROayWYD1Ict02CNLcDtAAYX4nqntbQSpY8gOaJKlqovx+gj3m2JC4Nd0FvnJ5KyhKyt5RVElOBWRLkR0i5JrswLujC3kyrXuk4tMH1ESds2T0uWGyZl709fFnFunAQ5grbYNU6CSjqVKLhFtS1JJR22GLaMBDGSBMg60uBUgkgqT1DkkCJNFYlkMRGuSDlF/pfT0sAtOwVHFOqVuZaoNBHm9qupSEWzuKWtsl2FlleW0fIBveV+stkoy5f9njkLL6UrVEaEFE00UkaCJDF73K38XlFJJzW0OjER4a1IcXM4EtweHEGuLgWW+OjpIuPs9+TzShZzONJVkXeWauXoIlER5MoPLHIuemMsQtwAA+W4xeS5cqxE4CrSxlL1L4glJxVZq5QjEkgRBUsMWjxxkJgFVBukz0n/0aW4g8mCN+EWwUrZar41C58Z+0Fmzz+Gntde4kftfyOTh+Oeg6f74PByeGoyhHTXoYxCUDJgutW+KyDTG2XpN5ew8MZFhOJJ59iKTFT6rggcI05dmIgj4NXjjGqDtx1y3nnFuAGsYyyxkfOrVMVQxMDglkjCALmrLtBcvyHGFWOP5YFHH+Pb3/42//Xw1xyhK7gFrjq6MPflAt/5zncoFAr0JhJ85dGbGfNSv1smGdG21Ud8SL/tUXWTvqqvE5GxyF/rcc7HRq3t1VixkvwiYpZzRq5fJQweM12IWoMjz3yqAIf5+GSigd+t2sTpp5/O/ev/5rRjhSdOEp86YJm1LpPJsHTpUhYuXEgoFBo0H+DIdoFrPnw11113HQeWwCsHQeBfHslrvWoPOP0NFZsWBp7/Mi8xE/FwTi3L9apXtb8E9/Vcl7t6xbjVOP1LpLiCyECLCWvBJcW9PQ1fb6nl1FNP5Zvf/Cb77bcfxcjn8yxcuJAHH3yQc845hz+9/WdnpfeY6PJYXQgrdZT66TLVoNZG+XwT8bPEOqLy6gL0cpzPwV61fg3O9b4ep59JHMGRunols1ocM4+l3f1IP88Gk7TqzNb6zQPb8GdNMVmujn6M64F7i8TxHo90eLhYtqv10p+8fU/EuGC19wLf4DGU/Rers17fYniFuEMJh/X+NVxebz2GqkuxdixQotu7C44c+GM+Ky4X+Kzv05vUNpsYKGb+U4G2T0epvbOfrq6uEX0yYXdA/hZt7HoBf/nIvy0v391DU8Vhe2TshmLLli1s3ryZmTNn2j80PP/885SXl3PAAcXepLL9mCdbDAaDwWDYjSkU4NPr4f5lj/LEn5fw9VX/B0DID0saoSoA/+rxc51/0q6t6G7Cz6aeyKzDj+Cij32UL3/5yztczuLFi+lub6O9s5MfdEWH3+B9yMORBqYt+iyf+tSnuO66696z/X75y1+mpqaG0oPncldp8ZsOexJbs/Bfb0FrayuzZ88e9EYLgN/v57//+7/x+/38+c9/5tHEoFkNBoPBsBOMHTuW2bNnu57oPPzww0fsRguYVz8bDAaDwbBbc0sr/K0bwj74SWoVVdrf/RPDcMvEEI+ccCG+hgYeeOhOFvQP9ojfns/SXCNlBx0KwIffXk4wuONfc8rKyjju0d9wxmsJfIUCn1y5klkjVM/dhX/sdwJVQE26k0MOOeQ9229FRQX/8R//QaFQ4PXWrWQyGefJmD2QqxIVdGS7mDlz5ja96WnmzJl8/vOf59577+XuQw5n/pr/JUz+PaipwWDYnckRoDAKf77vjc6W94r33ZMtkyZNwufzDfj3xS9+sWj+X//61wPyjpZl32AwGAyGkeSpcB1PH30uZWVl/GgszCrygMW5ZRnK+9rx+/08fPhZtBN+7yu6G5DGz8P7nAZAz2svcWpy5286fTjYzUcrCuSBL3zhC3uULPe+++6jap+pZLNZPtW1/D3f/+c+9zn6k0kqasdw1113vef7f6/4e3Qi4y+8ggULFnDLLbds8w3Aa665hgsuuIDJMw/lltpZo1vJ9zlLo418a8LxNOVju7oqBoPB4OJ9d7PlhRdeYPPmzfa/hx9+GIBzzjln0G3Ky8td24zk65wMBoPBYBgNkr4A9849m/0OOIBPnXoilw+hDPvqlqX0dHVRXl3DD6ad/N5VcjfihprDqKwbS38yyeXvLBuxcn/aAFWRMKWlpdxa8d49/THaLF++HIDM6y9xcKDzPd9/XV0d0VefAeDNN98kk9nzLM+Zgo9HZy3E7/czozLEBz6w7W8Yqquro7raevX4ppnH08Ge++TPzrBq1SqePPYT+Ocey08mnUsulxt+I4PhfcpovIlI/hlGh/fdMKIxY8a4ln/wgx8wdepUjj322EG38fl8jB07dtD1hh3DkeLOAE7EkuKq1xhGgcp6azUMLcStxeqJHhluRW0HkXCaOAnCpF0XgnC1ZRbbnG9w1Sl8SJrNm9xpAPSHodM3MP1NVe1iQ/PeVNMTPemdOEJdVP0Plv3gyHOPB8qsZX99L/T5oRPKJzbTWNpOmBTxcI8ttB1KiBsnQYCcmmappY0UYeppIUWYKjptOW44lyaSShNpwxKwiXSxB+iF0vXqUeQyGBtQRrd6qOq3bkL21vlpTG2mrzRMXyDKJNbSR4wEZQTVJVmkudN4hzZNoNuhCXLTROjD+pUpSdRuD1hy3Bm8Ys/X0UwjTbZ8N0yKKjopI2G3LUCWeiXJFSmuzEdIE06lKO3OW+0USakI6LwSXEl/l+IiXJGYdqu0LG5JrIhedSmriAQjOFJGkRC24Igpa1Q5FZ6phFHEoiKAE4loBZR25cnkrONX39pNVfVW61BGrEH9bdSSJkwnldTQZotxg7k8ERG8dql9SBwkTdom7c/ilgsHtfy6LFcXl4r4VmSeuhAXVV6dimsQS7jYgiW8k/JEgKlLdEWKKvsaTIzrTa/BEYrimZaquokQtwtLaLgGR6grdZZtWjxliGBT8oAjfpX0OuBtNW3DOqYiMu1WMViDI+3VpMv/7+BjqawbS7K3ly+//TC+Vwqwn7qOrcdFva+fec/+lVdO+RSlB8/ld5vf4vzet6yVEa1cqbd4roNq/w2qbiL07cIRi4osV4Sub6hYbdLWBdW6NSpOE9X+WnDkuLogtxTrfJD+3o7ThzZp0zKccw3c4mVNkvtudRmds48nAoypq2Pii72WqPUQJV3Upbj7FfksAHhLy6PJcse/VuDK73+fTCbDukQ3HY+8QdWKtFuKqvcB6a8lOP1AP7+rtTQ5z/txRLnSfr1/b8I5pwJampxj4JyjusR7E1Zf0YXCTxX429GTqKqqIpvNctH3r4dzl1qS0cGkuDC4WNiL5DvR52z/csFdthKafv6hFq6//noqKiq4c8GhXJrCknB+xDdQutqAI62V66r0gyBuGTc48ZR4iFhY5NciQ5d1EqOU2k8xMe58nyOdPcPn9AGvOFS17+cTDqWyfiz9/f1c/sOfDx6zQ3xF43vZZZfxrW99i3g8zs1nX83VXhGsLo4tJn31SlPP81nxlK9JKVXOGT5HpFyPFTsRVwexzuc23Ne9NVjxk2O0noFi3PN8g4txn9LqLjfajquAdHJ4EasSDxcK8JPGkxk/17qJVTV+IjctOIIvtapXvXv7szc+UFzGq9ftI76BdZZ2ieD1At/QsQQrVnrcdSlzCkcKe4HPkcUOJhcWismaizGcEHgwvGLibREXDxY7fd3ygXHML83h/1TAarvEUSS54HxOgvMCAZG4i1y4ds8fuWANIxr5GyN74zCi9evX09jYiM/n/k5QKBRoampi4sSJI7Kf993NFp10Os3vfvc7Fi9ePCBQOj09Peyzzz7k83kOPfRQvv/973PQQYO/yjGVSpFKpezl7m7rL65MJrNH/vKyo0SjfsAH5NW/LKDiI2/1kafZ5QcZOZflmSofUFD/pJhcFnx5SjJZIr4cEfKEyZPDOcYydrkk7/4FI+zPEc1lGUDOB/lB+ojs34vPM9XT9WuSX2uPX8uvtcufzRLJWZlKsjkiGatNYQqEKRACgmrDgCrMr6Y+AvgI4sOHFUif2ipMXkUnRwk5CmTx488FCGQD+HNYXybz2j+JM2oqdZW3yADZrB9/Nk82EyabLyFAQJUfwWcdIPLqL5+8qotDyDXvU5cYq/5O0AL47fYF7bV+Oway7LfbHlQhDVOgoNpt9YksGQIE8Gf9ZLJ5qx3S7sGOq7RbjlsB52qYU83wa83x4RznLE6/DuP84aP/oe9n4A2AgPon5fi0qbePFbSpfV5YSZlc1J5ms1YDsn7rC4b0B+kbWUrIZrOQy7v7g7fPSx2kbgEtJvq/vJZXYqDXNYz1x6FM5U1KEZw3g0g62vowTh/Ut/W+nSisLUtMQ4PEXY95seuOHnNpg36O5D3rvNvpsdI/SUPaurBaF1bp+lSPlX6tVG1/ODwW38HzAZj2wgOMD+atz5/IIKLWEHw408wLK5+hZNY8Xpl3OmueuoMJhX4nZmD330xY9aNQ1Nqn9HWpo8RVj6+3P8u0WJ/WY6f3LYmZbO/T9pPT5ovFS+KOyivzIbg5ejwVJSV0bt7IV75ygztWIZw/5mDwGA6WJ5PhC1/4At/99reJV1Xx08ajuTqTgbBWvn4ZlP4gbZR2SN7h4uqNqcQv4PkX9JSlX9ulHt64qVg8ut+xVGF935kyZQoZaa/3O44nDgNDlnFNXUjfGqLcyspKe2j3qoOOpf9fqwlkMta2+vGX9oQ880Ndb739E9znq0+b9+Pui7IPOYf14y1tkToO0r4Wwmw+8ARiWE9Yjx07dvDvkJFo0fiGw2GmTJnC1q1bSSaTvFNSxcRC/8A+DU799PqEPedAMOq0Kailh6LDx1C/lsq1Wb8Og/u46PsrViet/nY/KrKuKKpv/aolyx1Ll3F0IsURRxxBLDvQlAkAAO3RSURBVBZj69yTeOPxdUzL9w5fjl4fKB7XYsdZj6O0c6hY+rUpDIxvSCtfygoyZMwG1H2otm5rvuEYLlZe9NgV27eKYz5Q4L8WL8ZXdgnfaf89JRJHiQcM7It6msqfCeivSDQYhmby5Mls3ryZuro6V3p7ezuTJ08esafk3tevfv7Tn/7Eeeedx/r162loKPI0A/DMM8/w9ttvc8ghh9DV1cWPf/xjnnjiCV577TUmTJhQdJtvf/vbXHvttQPS77nnHmIxMx7UYDAYDKNHMpnkueeeo7Kyki1btrBgwYJt3ra/v59nnnmGqqoqNm/ezKmnnjqKNd09WL16Nddccw3HH3888+fP58ADDxyV/Tz33HNEIhEymQyNjY3v2ydmX3/9dTZu3Eh1dTXjxo0b9PvTe0UikeC1116jpKSEdDrN4YcfvkvrM1I8+OCD1NfX09nZyVFHHbXDAuBcLsfy5cupqamhubmZU045ZYRr+v6kubmZK664gv7+fi6++GIWLlzI8uXLqa2t3e7rpmHX0t/fz5tvWo+Th0KhIX8QH4q+vj7OO++8PfL1xfLq56qON/CXx4ffYDvJdyfoqDpwj4zdYPj9fpqbmweMmlm3bh3Tp0+nt7d3kC23j/f1zZZTTjmFcDjM3//+923eJpPJcOCBB7Jo0aJBX3NY7MmWxsZGNm/eTE3NEIPm9zIqKn6A9RzhdKyfS6dgP+MXxXq0T4YPyXdS+U4nw4vqsR7VDappCKjJQjBPeXUHkXCGMnoGDiNSzwVvyY9DJ+xPs2WLOw2A/hB0FXmy5S2cx7WLrQO0kTEWnViPfgol6h84jzQC7If1CHwF+Mf0EUn6ufO1R7niuFmUxzoIk6KMXiKkKCFJDe0AVKhnTivUsJsKuojTQ4AcZfQQIEsN7aQIU8dWUoSppIsoScKkrWFE6TThdpxHymUYUR8g9wvLcH4dGIP9qHXfGD/hVJ5kLExfoIQIGZJESVBKkDwBcrRhjSOPkKZdzVuhqbDn00RIYv0ikSRKSvt5YyMTKCNhz4dJESZDD2WAdXwr6aKUHrttfrLU0UqEFKX0EFPtLSVBhAzhdIpYd956zDmH9bh+Wu1wK87wl26nrbTjPJQlQ4ZSKk5+QF65KU9dZNU6/ckM76+AYVVmFVZdxqhtqlX/qFbllKsyy9WyE0bnGFWoepSofFhPtDz89p2cNOdiMlVWQxJhK24d1JAmTBcVjGErVbQRT/cQzOUJd2vt7MB6tDmtpUnbpP1ZFR99GJHkl7wSL5nGVMzCqswSNY1p24zRYl2ujs1Yta4P69qwFeu89D7ZEtGWJeb6MCL9F+6IiqkMWdHjGtPSxmjlN6q6VeHuI6i6bcUZoiAxSmt5wIqtH6sPtGMNM9yo9qMPmSlVeeNqfa2z/dUHHkf0kCPoTXRz+RN3MLagOvLTXXBoBUWZALRas8vC9dx7wAf529//zu1VXSysCjht7LPalwlHefjzd3LSLy8m1JJ0rtG9WtyqcPfdoGrHVhUrGRLj19Z1qPg2qrZtVTHtwxmC0IfVN2QYSERt16+27fDEqZtBhxEVCgVOeDvNU615PlEb4K6pYXhSPbf/ARWrWuBv8iw/g8fwJS3PB7Q8T1vp+fnl/Nchi6iasA/d3d384NlbrfXjcPcB75MtElMoHtcK1e5urI9QiYPENKdiJXHrx+qf7Wq5XeWX65303SpgiydudXBa+3yWLVvG5w4az40vvWulH63a+6QWg0HioJPJZHj44Yc56aSTBt5MOL3C7pMDtvXs79pzFvJubAytzz/O/76+Bt8Flc4wTIntWKy+AVZ/38rgT7bo8YxhxSyvpnLu6sOI5HNgDLBBpUn8xgJ3dMEpqs4PqrYsqnCGFC93t2/FcRP53xMuIRAIMGXKFM4999wBsXPxgYqi8RX+8pe/8Oabb5LL5Thj2S85pKYb/uLJf5x2rKQ+Uuc64Ldd8OkKp01BrBj+pctqyxicoZXduK+zcr7L56s+fLQOJ2ZyTR8D/ELbH8A/PHV60Km/3Y9uuJhQOjkgnl5yZ5Xz6a4Z/OnZFcyL5nl4ax9+v5+nzpjCY3MvoqenhyOX3clZL24dspwBcRsHLPHs+5MVztAoqbMexzu64NKKoWO5D05+gHU4w2j1+C3RypIYApym6vgPT91O0er+4BAxK9Y3doThYuVFjx0MrKOKY2dtkFv2/zIA+Rcf4pv3LrfiuKQLLlT7LMGKSQzn+0cTVvx+ZMW9rbqEcbd37JE3DMzNlpFj8eLFANx4441ccsklrgcpcrkczz33HIFAgH/+858jsr/37TCidevWsWzZMv7yl79s13ahUIjZs2ezevXqQfNEIhEikciA9FAotEe/mnB7SSblmXt5Rld//hbry7F8+ZMnB+WPJ/0R/QFDGHwQyBEOBSmE8oTwU8BPTvM5F9R8f949xjDvD5AMFOnWgRD4BxlGVGwYh9RNn+rp+pNl+pADfeiK1i5/MEg+oOocDBAJWW0K48OnBthk1YY5VVheTQtqgFCBHFYgs/hIq6JT+CkQoJ8A/QRJEfSnCebThPTH0eUwSZzB/Wh1EPvLeDDoJ5TLkwnlCAYgSJoAPgIEbY2WX33T8pPGZx9kcA60tbag+oNVf+dY5cjb7ctSUG3I2zHwU1B5pO1Zrd1p1e4UAVIE6SdImmA+RSiYdw8ZGey46sNGZHiN/DEio+ECWnN0Z0tKK1ceO5ehJxLnnJY/p031YV36Nt4+ppevD73QCAWSFILWcQiGgqrJVn+QGAXpJ5hPEvTlrf6g9wPvMBrvkCVvTEC6n3OTAdx/4IkzQpZ9WF/GA2ob+SNRz9OvyvDOi4MBre0+bTmP84eW1F+PkQxbkn/gvu54hweB+xzxe9Z5t9NjpY9clL6Tw7k5lVbpaawbUWmsS6V4VKTdwCM9IfrGTyMKHPrq32js176chkKQSlKUDHZcT02t5ZG/3cnWVvhiJ7wWgQr5SEs5+wIIZZKEUkmnr0td5Vjrxxzc/VkbfjigT+ux0/uWt3+JZ8O7T2+89CGPWj3+2BfjqdY8UT/8oC5ntUU+pyVWGZw0Pd3LYHkkPd3Ph159kH9O+Czl5eU8RjUnpzc6dRX0YTwFnJhKXbxxHe46IfHLef5lPWXp+5BYpbV54NlmeHj5wwR98BXfBuc7jbTX+x2nWByKUPT7USbpnOuDlavSL1v7CJNeLpAqwLPPPssxmaT7vJE2ZDzz3vNe+p4ez2L9To85DIyn7EPO4VDIconobZE6Fmnfzb1xGtJpUi0b+cTVV+P3+xkSvd8W4eMf/zhf+MIXePHFF9m4po07yrMD86eL9VnPOZBNOm3S0zPJgXHRr5HF4oeW33tcJGbZIjHyxlEjlE5aN1uG+a79c//B7HfiaXz6wFl85alf2d/Zj89vYNkDf+Tn/17LnwtpTkunKS0tHbIsV9y81woofpz1OEo7h4plXpviWafHTy8ry/AxK3bMh2vjzvwdM1ysvOixK7ZvFcdC1vmb67F/Pse1EkeJB7jPZ/nckTSVP5R73z4/sM3ksgHy2ZH3qxRGoczdlRUrVgDWjzWvvPIK4bDzY3A4HGbmzJlceeWVI7a/9+3Nlrvuuou6ujpOO+207doul8vxyiuvsHDhwlGq2Z6Pz/crNXcill1WPZIyDuuPhqlYT4NE2SYhrn98L/GqhEuGW6N+DhMJao3LPgjN6gmaaf531HK9vW5qw2qSxGzZKkCAHC1595g8gLaxtQPSWK0u+sd50kWYOw23UDeB9cs0qu1RYBLWEzCTskQrE+SyAaZNeBtehgPCbxGjkzQRGthElD4ipIcQ41pC3DBpIqSopNOW4wZyOWK9aYLy5IEuxe3F+cO2C0f4KsJVcJ7CEQlgiSPQLa9JU55NW/LcbBKybfTWWV8YG1ObaS23Nm6kyT4e49hkxz1taWsB6/jI00gJ4nYbdXFuE43Uq0eG6mihkk5i6ueLepoJqxgVleJKu0XqKr+E6/Nd6t9gQlxd/Cq/qqOlBbV1baqcUrVen4oc922sPv6mWpanAOSXLJGLyrRdK6dCTUU+WoMl26vB+QK8AUp781AKpcEuUqUQDyRoi9Qyjk3EUwkrNl048s0cjqy0C+fmkSxLPPq15WISST1NvvTofQ4t/vLLnrRH/5WrVGtbmYrPGoYX5pbjiFslrrLcq+In25d66l+OI/GtwJHu6fmlrs0ML8aVvqH3KVS9RI5bjyPefRdH7lmqylFPOyXzcGlrhqZf/ILPH7of5/audks69/M5EtepPuuaK8JBXfr6VoHvHO/j/qdhdRquSldwi8iwRZYr/UiestJFjdIHRMC6CefJknZ1TN5W0zYcaausi+AW+Eq8pQ/24AhxRWYtsRB5aVBblpt14PSvBujtD/DCUZ/hE7PbmPrvvzHhlW5HiguDS3HfGuQL+WB5NEnsic9s5IErryQej/OP/U/kpI2/se4DSb1eKDhCyAYVO/0mpMRfnlCRdV24Rbk5nH4p0mWJjaRFPGV4xbhocXvKEkj+fN00gsG1fPKTn2TyprtskatLJHqizzlPt1WKW4xlRWJYp9Jlf2r/Y8fCp468lF/84hf89+nHcsy+uEW5gkhGhxPlghMvcF/fU9pyBc41uYltE+PqYmRdNqqkrcsT8Nt3oOydn/PYY48NfqPFKx/2xsjDBRdcwK233sqLgQDf6IMpXlluMemrpHklr7ood4FWD/2JlYk4AnM5n0WCXYFzzViPJTKVch4oWNJTEcgOJcaVuoaj8NUl1lMXg/0Br+K7OhCn5aQvEgEmT53K1F/mnTwPFPhmMsk906ezdu1a/t/UMr4rDztviyy3mPT4Xm39UKLcBwqO3PUSn/P9Sv+cASd+sk7i5xXjFpMLg3Us5enq0ZDiDsVgsRpsH/d60rzC5nus9emPOU8WPNPtI3Gij/iygiMbvr0Al6ttJZbrccS4ItM9fxAfl8Gg8dhjjwFw0UUXceONN476kzzD3GrfPcnn89x1111ceOGFBIPu+0UXXHABV111lb38ne98h4ceeoh3332Xl156ifPPP59169bxmc985r2utsFgMBgMg3LtFngnCWPyab7T/epOlRULwO3j4cQTT2TMx6/gr7FJI1PJ3YibSo4gXlVNw9ixXDEuNfwGI8RnP/tZVr74Ir/5y338YxtGKewuvJCrYeqZn+CKK67giiuu2NXVGcCVV15JRUUFuSNP4anQwB9H3g8UCvA1NbToggsuYO7cuSNW9pFHHsmCBQvI5XJ8790RK/Z9xy0HnEqkpITOzk57OIBONBrlpz/9KT6fjwcbZrMyWLULamnYHjIF68/RQqFANptlefswG+zF5LLBUfu3t3HXXXe9J0Om3pc3W5YtW8b69eu5+OKLB6xbv349mzc77+Xt6Ojgkksu4cADD2ThwoV0d3fz9NNPM3369PeyygaDwWAwDMrj4bEs3+cwfD4ft4yH8hF4ove4Mji0PGK5DOaeTs/792HWAazPxUhMPwaAxhWPUO1PD7PFyLHffvux/3N/p6uri2+8Dfn3yZPrfy6fh8/nI7d1EzNnztzV1RnAtGnTuOiii5g3bx5/3eeoXV2dHeJ30cm0j9+XWCzG1VdfPeLlX3PNNcyYMYOSM77Av/fCmwi/Ld+fin2nk8/nOeusswYd2n/GGWdwySWX8KEzzuA3BxpR7u5OIZ/jpZdeore3l4svvpinp5y8q6tk2Et48sknOf/885k3bx4bN1rDFH7729/y1FNPjdg+3pc3W04++WQKhQL77bffgHXLly/n17/+tb3805/+lHXr1pFKpdiyZQv/+Mc/mD179ntYW4PBYDAITaEY/3XYadw68ZBdXZXdhjR+7pt9OqeedhqfP+04Th/BH1quXL2M3kSC8upqfjL+/fkHbDFuKz3a+nW7eTNfaPv3e77/r9ZBuR9e7oE/NIeH32AXs2bNGiJTZwFw/LtP79rKDIEM8S7d92Defff99fhGOu9jxdyFfOITn+DLX/7yqLytat68eZxwwgnU1dXxu8lHj3j5uzOthTAvz7X6R/61pznmmGMGzevz+bj44ovJ5XJUTt2fP5RNG5U65Qo+mogNn/F9QHNzM3feeSeJRGL4zCNMINvP3/72N5YuXcrEiRNJjRud47UnkMv6yWUDo/DvfXlLYKe49957OeWUU4hGo6xYscJ+OU5XVxff//73R2w/e19kDQaDwbBLSOfhgvYcG8sqaVlwNrfUmBsuANfXH0HluAZS/f0s7nthRMseU0ix7+sPAJCdffQe8Uj9G4FyAvsdBsC8V5YR8L33j5ZUB+HKSWV87GMf48UjL6W/sHt/nfr1r39NMBikc9MGPpRct6urMygnnXQSHRubCAQC/Pa3v93V1dkufhGeSUXtGJJ9fVx22WWjth959XPswFm8HKwctf3sbvy4/HjKyitIdLTzlcTjw+Y/4ogjyL7yHAAvzF5AMjmIGHsn+Lp/IXce/1X+NzVxxMt+L+nv7+cHP/gBjz32GB/+8IfJ5/PDbzSCZNXu1q9fT6FQoKK2jnfeeec9rYNh7+O73/0ut912G7fffrvrKbmjjjqKl156acT2s+c8U2wYdSwx7gzgWCyjYr0jgy3Fec3zBCxBbhnOa+7GUlSIW0czaSLU0eyS4cboI0qfer1viiolURU6tPcxt1DPVKy3S4kYN6AMec044773969yiXQB2hrc4l2AtrHu13v39cRIbqnCf4L7fev5VcpwXwMcqBKTQBVEx3YQK+ujxt9GijANbGZMpgMIMIV3KVPvrxQJrMhfhxLi1tJGOJcm3pXGJ0JTkeKKsLSbgTJUEQSKUFHe/iGC/jIcaa68RhccgZsmIhR5LvUwdn0XEs7KwDr6Sv0Ec3lSkTB9gShBcrRSQ5DcNolzK7VjHCBLFZ22LFnm4yQcKW6/apNID0WSi2q3tFfW6eJbrxAXz3yLlibxkng34wiGRewpctvNWKeGCPDexZHjDSXN7VJlBnFEoQwxjWId6yT2sYvUQSSVp7SixYmBTEUULBJXkcHqglzpNyLblDcHSR5UmohqA7jFwSL9lVOuXbU152x3eRMsT6SYu2oV++67L5uO+jCP/mMrH6zY7BwfEeY2YB0/XZgLjmS4QZtuUtt6p9L3U7gFuyIdFnlpr7a+TrW5RYuhN15eMa6kyWtq5XW+xaS4IqGNWNu+FqwgMed4wkD98w8x1dfj1FvQpbhTlSTwHSXulOXBZLkN8Omm11hcfyhVk6bym4NP5ZAV9zhvcJG3E8mxjuCIL3uxrg89WnykH0/E6ccprPLqVUxFEi1tTmEdUzmfIjjXKl2Mq0tx+50YUY8j6HyhwF3nnE5pMEjnhrV89PG3YFbA2u8hvu2X4haLs75tA+4yNanpF55fyw//+7+JlZbyi8pZXLHsRbcwUupcD7bjXQSuInOVa4Au0RUpsGwvfVWOkQiJZVkkr14x7nrgqQIdHw6T6u8nGotx8OFH4n/+V5aYth63NFQXtA4nxpX4RKLw3SXW67RTycHjLOWd6BEYy/4X+GxZ7oGfu40tW7bQ25MguS5A9F/KkDtfi+1gotx6HHmooL9BJ4Xz2SbxTbF9YlyvCHS2jx6CrD/+csqAispK6uoGcc54pbg6xWJURJa7YMECHnjgAaqqqrjnhEs5ZOV/DxQdvzBEnb2i3H7VXpGzSlxkugarv03ELXeV/lqBI4YVMa4IXb1iXuEMnxPrFwqQycDSpQxASXH/nYTbmys5NVLLmWeeSfkNNw7MW4Qv/fJ3/PjHP6a8ppabzzyOr7Q8746Tl/uLSIaljnobVJtiJ30bgMcaz+RD0m4RBTdixXAizjV9Dc7nw3qseN9fKC7G1eXC2j6pZ3Ax7g5Kcb///e9TWVnJli1bePrpp/n5z3/O5ZdfPvRG93vK35Z9P+XpF0qUm/7h60Qenk1JNkVXazOVY8ay/PLTmbpUyYbP8FlxFCnux3yOHFfqscAHtSXF97sHkcsG8Jm3EY0Iq1atKvqEXEVFBZ2dnSO2n937pxiDwWAw7BHc4qvll93WG3y/teFFOt95i1AoxNLjPk5TcM94DHtHuGP/UwiHw3Q0reXylpH7JUXH74NPvLLUeqR+0n7cXXHA8Bvtprz55pv0j90HgBObHh3+lbqjSE1NDdWrngRgw8xj6enp2WV1GYpfBWcTjcXo7mjnggsu2NXVGZYLLriA3t5eSsvi/Kbi/eHXu6VuDmUVFfT09PDFL35x1Pc3f/58AAKBABvye/YbWAoFuGwDtHd2kslk+OhHP7rN244ZM8YeztU281i2+iLDbLF9/OEPfwAgW3j/uJu8/N///R8+n3WjJJfLkclkuOqqq97TYXwdHR1cddVVXHz5fxJpt568ezu+z3u2f8PeydixY1m9evWA9KeeeoopU6aM2H7MzRaDwWAwjCoPVY5n88c+xxlnnMH3xgT4UKzA1579C93tbcQrK/nJER8hvRd+HP2pdKotezzn30vx+4bfZkc5LN0GK/5JKpXi7p4Ivbn3518G3/nOd7jl1lt5+74lnOZ6v/Su4YvJf9HT3UVZeQW/+MUvdnV1BpDP5/m3z3oEcdzaZwYViu5ORKNRysrKAFg17YhdXJvh6SZI6wzr5sf48ePfk7dbnH322XR2dhIOh7mz7LBR39+u5Hc9EZ7qtd6w9uMf/3i7t//iF79Id3c30dJSbh7/gRGvXzabpbq6mhf9NcNn3s3o7Oxk2bJl+P1+EokEf/jDHzjuuOM45phjuOmmm96z4UTZrPX0WqGQZ99+62ZLqtbcbClGNhsgmxmFf3vhky2XXHIJV1xxBc899xw+n49Nmzbx+9//niuvvJLPf/7zI7afve/brcFgMBjeM9YGSnn4lI8RDAaZXFrCf1VYQ/zqc/2cvfyPpNNpqiZN5TszP7iLa/reksr7eHa29ZaM9L+f46hUyzBb7DxfXv8kf/3lz3jspX/z35uyo76/kebffbBkyRJyuRxX9q3a1dUBoMyfpf4V660FW7ZsIVnYvb6wPvzww/z+r3/n17fezKdzK3d1dbaZT37yk/T29vLm+g0899xzu7o6Q/KLukMpjcfp6erk0ksvfU/26ff7OeAA6wm13mlH0L2NVoB8Ps+fW2GNd6jibkprIczLZ3yR008/nW/sH6GxsXG7ywiHw8yYMYOmpiaWvLKazZmRq18gEGDt2rUALCsb+NKO3Z0f/vCHlJeX09PTw5VXXonf7+dHP/oRhx56KFVVVdxyyy3vST0yGeugFPJ5jg9YN9ErxtTT3OwdE2gwjBxf+9rXOO+88zjhhBPo6enhmGOO4TOf+QyXXnrp8MPotgPjbDEMic93LTAeOBQ4G4hDPGSN4x+P5WYZh7UsrpYSNT8B6IfwpG6iZX22oyVCmih9ykuSIkaSGlpdfpao8reI0yRAjhraSBO2nSydytvSqflbxBEClg9EXC4tSi4yldW21wVgf1a5vC4ANX63x2VT+Tgi5U2utLXtk4jP32gvi9clOqmDcEmKynAnDWwmQspua4Q8MJaJrKOcduVnyVFLK1kCVNFJJZ1E6QMo7miRMfriHenBcbNktXlxcEh+cbjI31elWGd/RM3LuP8clnMEdUwlLzjjq+uw3CQRrHHupeCLQGk2D/UQyaYpr0mrNvTQW265XDpi1kBv3dlShmW9TxKjik77uNTQRiWdREhRRoJ4KkEklScoMRAPSa+2jJpK+0u0PEITjieiC8sbEVDt7tXSxfUiPo8IZHohJM4b/Yuq1EFi26LK0afiHqlQcYzgeEV0x4cch2ZP/OUY9GKNw5mi2lKC48zQ61+B29USUGVKPt1BIq4a8bTIfuTTQXwy0qckb6najzgn1mONv85hu2zS/X5uOOqjVFVU0N3aytdfuA+/5AGOamnhjcfuZ+Mp59A8fhJ/fyXAh6M5x0FRo/61Y/XLbhzvyRqsMfHv4ng/xB2iHzv9+AdU3GUsfY3WfvFmrNFi3os7PsVcLXKeVWC5TEq0bcq1Y6p5WgBuainwh3v/wsnHH8cPf3k3fKzeqtt63N4Lr6dFRx+3r/tb9LzH+ezjWvFWmu8f4uPsNrhhc5afbd3qXBcm4nhFpA0R1T7dpbJJxa1ftU/iLL4LcbeIN0lcPhLPbhzXkV6u3v/kXBJXiTpWP+mfjN+/jnPOOYdZrX8s7mk5TnNBDOdpme+zrmXeuOrbHudz/CRvFYr6Wz771DK++93vUlZWxu0Vh/AfoRWWe0F3GEibxDEk7hZpu5xHbard4HhserX84L6251R5Ur7mauEwHzd/3Lqhd8a5n6Dq8Rstt4fuqzjR5ziqttXTosdHXBsvdUEo5HbdLC9S3rKB8ePlghUvYbaPycCWGZ/kgQceoP6FBzhiMo4HQlwPEgvdk6E/7CT9EJxYyTVMzssmHNcIbLurRblEUnn4S+xIZjc3M23aNKLRIkN6hvK0FGOZ5/jocdK46KKL+MxnPsPTTz/NpC/fZP0SO4y75ckwnL8mzEknncT/ShyXFxxHCFh96wHNh/Exn+Nr0X0jumukGrerZTC/yGFa/xjM66FiC/DTqSdSVl7OvvvuyxX33DNc5Ablggsu4Je//CVvr2vi2ksv5bbZ2jEZzN/yQpF6e9wtc885m8nTrDfnNE893B0D8djocQS3a+QCn5MPhna1DOZp8fpvtpG//e1vRCLWsKo5c+bYw63mzp3Lgw8+SDabZcOGDWzYsIEJEyYMX2CxeA1Wp3vd7pZs9XiYcwkFn58poR62bmqitauHf370AM6ejBUrPY7eGLZgxbCtDX5fu80xeD9SyAUp5Ebhz/fRKHM3x+fz8Y1vfIOvfOUrrF69mp6eHqZPn24/WTlSmCdbDAaDwTAqXHvAiVRNmkw6leLjy//AmFxqQJ7PNL9G4m9/4K677uKiTTk2jOCvjrsrG9NwbQts3ryZ459YMrhMcxQ4cwwcWwoTpk7jn//853u2353ln4U6pn70Qi677DK+8Y1v7OrquCgvL6eqqgqAt6Ycar9ZY1fzOqU8nrO+NH7hC1/YxbXZfuQx7j91QNdu+iDWne3w7LPP8re//W1EHzvfFsLhMHPmzOGdd97hZz/7GYXC8H9ot+RCXHnllRx22GGs9FW9B7XccV4I1RCYPg+AQw89lNLS0mG2GBy/388Pf/hDAH71q1/x5gg92VPT4NyEKC8vp62w+78GHqCvr48bb7yR/v5+uru7Offcc13rv/zlL9PV1UU0GuWmm24a9fpk1Z+j0oeTy+7gj3/8I89v7Bz1fRsM4XCY6dOnc/jhh4/4jRYwT7YYDAaDYRS4Y8x0wnOs8fHTHruPw/taB837vY43eSwAL6Vh0RZ4dALs/maJHeeqzhi9+T4+EINPVr63+/b54Lr9qnnk9E/g8/l4+J2xLEyteW8rsQP8ZczRlAOB1s3MmDFjV1dnAJdeeikfPe1UnnpxBR84AM7b1RUCfj/+A/znqUey4blH2W+/998QhyOPPJKDDjqIRCLBryMBrtjVFfKQzsP/U09gfO1rXyv+VMso86lPfYpvfOMbvPHGGzz++OMcN0z+XDZDU1MTU6dO5R+V05nF7nvDdckBJ1MRCNDV1cWiRYt2urz58+dz1lln0dXVxS8qY/z03f/d6TJ9fmvY4L/+9S+effZZ5tanOX2nSx19vv71r/Poo4+yYcMGnnzyyQHrS0pKOO6441ixYgWxWIyHHnqIk08+edTq473ZcswYuPNdeKJj1Hb5/iUbsP6NRrl7AYsXL97mvNdff/2I7NPcbDEYDAbDiLLKH2H10R+mBMg89xQXtbwxZP6ID/5YD3M3+4kcdTzXhfN859+PvTeVfY95sGQC+1z8KU569ln+39ployrFHYyjs+3c/8a/iU+fxUMHfJAFz9+xWz/m+q9gDWVTDwLgzHee2MW1KU51dTUnrH2exzLwvXfh3Hx+l8a0u7ub3P6zCQcCHB8e/Ebn7ozP5+OCCy4gmUyydsum4Td4j7mt6mDGzymDjRv5zGc+s0vqUFFRwYUXXsibb77JX/7yl2FvtvRFq+2hI60TDoKW3fNmy/2lk6iYuj/5fJ5zzz3XflvOznLFFVewfPlyCoUCzzc9y+GZnTs3fAHrLM9ms7S2trIswm5/s+WJJ56wn1a56aabBn2y8vTTT+eJJ54gHo/zf//3fxx//PGjJtjOeq6WR6sqve2rpDuXYPSV0+8jzM2WnWLFihXblG+krjlgbrYYDAaDYQTJFODC5hSt//M/nHz4XK5/6VHHOTME00Lwk1mT2XD00RQKBf60eT0fy70z+hV+D8kX4IGDT6YyGOSQ6lLm7EL332fWPslvp02nanwjv6o6mM/y6q6rzDD8ceJ8yvx+Ot9ZxTGp3VeYeNkY+GELvN4Lf//73zljF9blN7/5DdFYjERnJ+f63tqFNdk5zjvvPH7xi19QObaBRx99lA9+cPcQaWcKPtbN/CALqqspKSmhpKRkl9Vl0aJFPPLII+Tzed4KxNkvlxg0b0/1RNu/UTm2gX9vqmLme1XRbSRXgOUHn0QlkHzzXxx+7bUjVvaxxx7L/fffT0VFBX+cegyHv/mXnSrP77NuEsyfP58HHniAh3fzJzG6urpYsmQJ06ZN49hjj+WUU04ZMv8Xv/hFfvnLXxKJRLj11lv5j//4j1Gplz/dy6uvvmrfCJxcag19rKur48lX7+C0UdmrYW/kscfe+x/yzM0WQ1EcMe6FWBbEakuEW6mSozgS3AlYsrmx1nx4Qjfp/gg1Y1sZ599Emgh1NFOrpKcANbQq+a0jwpV/OQL2fJgUVX1dhPvBp0SdhQikSyAVCdMXiNrC1T5i5AjYktXpvE4fMcCR6DZTT4AsQXI0a9LcFtu2h6sMgErcn55NNDKn+kV7eS2TKCtPEClvIkWYBjZTSQcRHCFwJZ2UkQLGsg/rqWArMZJU0kmArB2bFGGq+rqItFFciNuPI7sVaa6MPRYxrKzL4sgA0dIDWGe+iHFLsESYQdwCV12gizYVmWI5jlA3hyPOrcMS6dVZ5ZeW5KEUxtJlS2B7q60vKGUR60thG7VESNFMvd1HGthEOJWitDvvtHMTjuRV2izt1tP7scSHIjHN4haltuBIXttxxKXNVrszXUqGK56AlLWc7IVoqTUFiOri08GkuVlP7CT+IsYUuagIbkVoK9vrwtxStW4K0Ipl3RKhb4kqs1pt14RbjNul7U+XROa0tKA2r9dFF0p2qX2AW4pbYW33zVZ4LgOVa97hv9LvUPL/2Tvv+KiqvHE/d0omkzrpIYEQCKH3XgUElI4KqKCIfV3XVXFd3X3f3VV/u/u6rmJb17KWxV5QBAuigBRpSgm9h05CQnqbTKb9/jj3ztyZTCqTBvN8PvOZmdvOud+5M3Pv957znC4IsZ8ib1WOETXxwFYnd6VJPLZnB6H9BrNz4vUM/+ZVUuwV7vooAmCbXIeTCJmgEm8ljopgN0ne/xTEsaPMV+S1Wbglr4r4Vbm5oxwzBfK0XHyLcZU6KeUqMl+1ZFg+jt5P6o6pfQpWq5V78ta5pbJdZSniUWfNols16vly7KrJE31tQyUvTVudTckjjxAbG8vRfldTeegQwTa7az7eDQly5P0Lxi0LVuTFyn4EU12UewZ3fINxH9vK8Q81i3GB/U4Txm59AZh8y0JY9LEQhsbjFoaqpbhKHGtCHZd2cmyUae2oHke1qNJb/qqULwtMI+PhvimPUVZWxroVy5gVg6coNwm3wFWJcRIihkm4ZdTqeOi8pitCYwsiBmfkdS14iHEz9+0mKjkFU0wM+vXO6mJcRbqqjmNNqAXBdVGTWNgXXvFzvVfqOUCiPVBx9SNERETw/avPc/XjE4Rwc7bk/v0Zp4qvImUuxn0cgvu3WImb4ryvxC01bYAY9/12PYiIjsZsNvu+AG2IeLg2fAmF4z2njx49ms8//5yoqCg+mv4ITz75ZI2iXMeswR6b/1bXg37KPs+XY6oIh+MRElLlOAO3HDcSt9hVLXWdLcttvb9HvmLpizGRfKiJJigyiqqqKn79j1dqX74RzJgxg40bNxLWtTc7fvdHBiuxSsBT1Kxmu+9jQtKKA2zkdy8yYMAA0tLSODIukm5JJWJb3pJccL9WYuwtaFaoS4xbH9mwF8899xyJiYlcd911/OEPf6hz+U6dOhEfH8+f//xn9Ho9t9xyCzEx9Rjiur6yXFmUaxgl8fn3nzN48GDY7kSaLxFUmg/x8eyT2jNNHUfluPSOIcDceoh82zp2CWxN0CTW3gLNbFsRShc2f7ZoUWjNLYcDBAgQIEAbYmlYCm8ipIvvxEHHRrQ4/tPeVRRfzCUkLIwXBs7AcQnXKa2JSjTsTZ8EgGbPVrpJJS1cIxg5ciQVZWWER0XzmmFAS1fHJ++njkKr1VJ4JrPOu7CtgYULFxIbG0tUx85scCa2SB1+0cUSlZyCw+Hwi+uipRk1ahQA2vQ+9R7iuClxOGFfiqiTwWBwyZFbkl69RDe7iooKKitrtr/aJM/T/ovxPZu0Xg3F4nDyxO4CXnrpJQwGA506dfJ7GePHj6eoqAiNRsMnn3xySdvSaEQ8YzUOxo4dS+/evfnB2Nkf1fQ7mzdvdl1I9unTh+jo6DrWEDz44IOkpaVRVFTEX//61yapmyIV1+nc3++YIjHiZ64xuUnKDBAA4O2336Z3796uFoq9e/fmrbfe8msZgWRLgAABAgS4ZDL1YeyYeBO/+tWveKhrEtc3cuCICKeNmT8tw263Y+rag9eSWlsj98bxr7jBRETHUFFWxkNnNrV0dQAICQkhZp9wNmR3HdsqLmTV5FrgglFcyI4/3zpdLd707NmTsswDAHwdO7xF6rAiQSTOSkpKSEtLa5E6+JNZs2ZRWlqKITiYz8K7tXR1WBmSgqlde2w2G7fffntLVweAW2+9lYqKCkJDQ/n8889rXM4uiZYYReeEFDsyIZmTzpBmqWN9eD3HzimzcCD97ne/a7Jypk+fDkBoaCgZ+volHXxht9ux2+3ocRARIcwip6Jb33fObrezdOlStFotRUVFLFiwoN7rarVannvuOQBWrVrFrl27/F4/q1NCkiSPZEuPCpFs0UQHki0e2JrwcYXxl7/8hYceeogZM2awdOlSli5dyowZM1i0aBF/+ctf/FZOINkSIECAAAEuCYcTXhk8i5DQUCqLi/ibM7fulWphXNkFgn4W/WrPj5rKfqPJD7VsOS5KBvL7jwUgPmM9Mc6qFq6Rm3vz93Iu8xjfrFzJf3Jb19nW66fgvQ8+YON/X2OG83RLV6feXHtxGwAhaX04duxYs5ZdVVVFZSfRyqF///7NWnZTodVqXSP9HGjf8iNRrU0To6xZLJYmaXnRGIxGI1q5S0ttF8N2uWWL1lzCrl27eOmll1iXW9EsdayL8vJyPg0R8XzqqacuaajnupgwYYK7dUvKiEZtw+l08s9//pO//vWvRFcWMXDgQAB0yZ1bXYvMf/3rX0RFia5Z9957b4PXnzRpEnfffTfz5s3j/fff93v9ChN78sQTT4huRDJjbKJvaLgpijOtKCEY4PLhtdde48033+Tpp59m5syZzJw5k6effpr//Oc/vPrqq34rJ5BsCRAgQIAAl8RLCYMxdU7HZrNx8+bPCfPDLZLHT26m8NwZJEniCUN8qzt5bQivaZLQBQVRUpDHb3L9f1fwUgjFwcgdH3Lo0CH+kQMl9paukaDSAf+WR6S+l5wWGbWpsVwjnafw3Gm0Wm2TXJjUxqpVq3jtjTfYsPIbbrrppmYtuymZOnUqAHZTHBcvXmyxemx3xmDq0h2AuXPntlg9fDFlyhQAwsLCOKfxPQy1Q062SE47gwYNoqioiK8Lmq2KtfLzzz8z/eb5LLxpTrO0GBo3bhzbt2/n/fVbudiIvyybzb2SXgPXXHMNVquVkNBQtlfF+rGml8bp06e5cOECAOHh4fTo0aNR27nzzjtxOByYTCa++uorf1ax2mhEAPFYKC4uBuAnTaB1i4tAyxa/YbVaPRJ8CoMGDfL4fl8qravNcIBWgSS9hUuMGx4txJzJQAfE6+4IUW57xBGUCEGpJcRE5xNCBVrsLiFuCBUEUeUS4kZRhBGzSyCriHDDKXXJcF1yWDtuAaryiAQpGAwGMERWEWGoEuJAeVw4WwxobVAULU40FDGuIrztIFvxLASRyilASHM7qV57C3S7csRDoBtLvke8vAW6iuw2hApZ8iv2M4ZiYAzdOEI0FzBQRTilmErK0FmAMqDcLPazhJqFuMp0tfhVEZhaVM/KcohpitRVpwO98s2PxC1BDUUIBUPlz1Urf97gFrzWJM4NRsgADfI24lXPNoTwslJeLhRCc4U0N1SW5oZHCFFumKFU6I4tFrGMBbf4Mh+3mFMtxC2XY2eXyyjx3G9XTFAtr8iBZdmqS4gbLL8OdcfMmADmHDAGi9cUy8+4p7uWlWNnLQd9CkIUqJSrxFuRGNtUdVE+Y6Wuoap5yn4p+x4pb+sUbmmu+nMpk+Nskz8TRYyrxFAdO7U0Uquqk7cQVhHpKvULdtclwxlN3rBrCALCtqxhWFmeEI1my2VmIwSkiqS1o0pmelqeNqK6FFY/WuKOzcuYliNxvqCQl2JhUZS8j7mIYypGro8B8blb5EcuQtR6AiEPVES6WQhZrnIcxMj1i5b3NV/eNztumXGl6jMpUH0WyjGmlKV8jwzyfPnzL7TB8wdPYj/yEv/qHE7wXrsQhypiU0WKe1aOkyJt9aYmaa46doos13sZZb4ybZwEAyPhbx9DFCwIh2cy4UgVvFAm8cQZH+V3FvvjkrYqsa5UvVZ+QxRRrhJnRSKt/n1Rx9CHGPe/jkTKdCWkJMYy+3gm9NVDTwkO1iCsra8U11dslWlqObGv5WqSvyoC1ImSqGMS9B91FadPn6bKYqEsSkfYD1YhdbTgKcpFFQdF3qo0DFOOUR3uOCty12jcMtPRkpB7xsA7C2dhNsOgq8YRPEK+4FaLcXvK9T5YR/ZSLR2ujxjXG19i4fqIcm2quqlEuSOA/zGNY8OGDfT66l/8Kg63KNeGW/DqHUsQv4GKrDmS6vGbXQ8xrixRfdcWy8WDB2nXrh1Dhgypvi8NEQ83FPVx5i0VBsaMGcOnn37KqVOnWLbwH0Lc6yXKtfedCIDkcDD9kyd4AvihMoTKkRLB7RDxUIS34P7viMSN8loRu65XfX8S8BS61keMO0AizxCBcdyvxSpjxjXZEMNqZs6cyd/+9jey8wt47cGn+MuWJ6qLhb1RiXKtqsS0fk0pxkkhlPVYSFT7TmylI8PmS25JriJ1BbfYdZV8zKpjCLWLcWepvpf1lOK++OKLmEwmiouL+cc//lGvdXwxYsQIli9fTkhICOvWrWP69OkuZ02t+JLletXdPulXkJcHRXIi9SMxX7Pgeojsz7Fh8+E2WYp7j+QZQxBxzAK+yIPY1pPoCtC6WbBgAa+99hrPP/+8x/T//Oc/3HLLLX4rJ9CyJUCAAAECNAorEu8Ov56goCAKT5/k0ZM/+3X7/SxF/EUjkpl/yoeTVr9uvllYfBGKHZBqLeO2quy6V2gBdBp4Kl7D8OHDKZrxW3KklhvGFkS3tGOjbmDRokXcf//9Hv342wq33HILZcXFGENC+EDbPJ6R7EqJb4rE6zvvvLNZymxOpk2bhtPp5MP8updtCortsORQHp999hkzZ85smUrUQZcuXfj22299tqhyOp3YbDY+++wzrMd3MSBEJGiuu+46VhhaVur6n7gBGI1GSkpKuOeee5qlTEmSePTRRwF45ZVXMDewVZ9Z0nLLLbd4SKgji0QW9Wxoit/qeSlkZGTw0ksv8cEHHzBp0iSCgoIuaXv33XcfVqsVk8l0yXJhNXa7HHyHw2N6cuFhtmzZwsGDB/1WVpsn0LLFryiC3Lvvvpu7776bPn368Oabb6LRaHjkkUdcj0shkGwJECBAgACN4pmkQUS174ClspJ7ty1HK/m/r8894TAuAhJSO/HPAW1rdKKzmhA+DxYn3U/F0qq7wswJczJi4ABM0dG80W5oi9ZlqTONyLh4HA4Hd9xxR4vWpbHo9Xoidq/ivffe44NtzXOh8G7IMG6/6y5mzJjR6K4CrZl58+YhSRKbKzQctzV9ywdv3iuAcruQII8fP77Zy68Pt9xyCzqdjh07dnDkyBHX9I0bN/LMM89gs9k4ePAgUkEWkgTDhw+na9eu7Irt3mJ1zpUMVPYSMum0tLRLTgg0hNmzZzNw4EDGjRvHG4aBDVrXImlJT0+nW7durpY4vS2ncTgcFDlbNmENIrn20EMP4XQ6GTp0qKub2aXQqVMn1xC527dvx+GVHGksru04Pbc3xnqYH374gbVr1+JsQ//9AdoG+/fvZ+DAgcTFxZGZmUlmZiaxsbEMHDiQ/fv3k5GRQUZGBrt3776kctre7aIAAQIECNDi7LfC3zfvZkxQFLMqc+lpKW6SciQJ/tUzhE8mzEev1/Na/hl+c3xPk5Tlb17vMIp5M0eR+ctWrj/xfUtXp1a0kpOu+zaRM2E2Fb2HU5C1jWhaRuS7LW44JsB5aBfx8fF1Ld5qudt5iKdOiN5s+/bto6nVrjmJfeiQmHxZJloAkpOTmT9/PsnJyXy6fzP/24xlO5ywvusYos0HuP/++11D6LY24uLimDhxIgcPHuSzzz7jz/L0deuEcFxpQaCTq9+3b18yMzOxt0/Hcbpl7sC+2m4YwUYjRUVF/P73v2/WsnU6HTfddBNms5kzeXE4zu2qd1K8ShUtpfXdNZrTLHz2WcxmM0/MFL1JW4r33nuPXbt2YTQaL6n7kDe/+tWveOONNzCZTHzwwQfcdtttl7xN5biUvJI3fU0QFBREQUEBJ8qg9Y3z1ALYgKZo5XsFtmxRfhebmkDLlgABAgSoJw4nbIhKpEy6svPUdifcXQSVNhvBW1Zzf3bTJj96aysw/rIegHMjJnMyqOlGqfAXJ5xhSL1FC5FrC0/QSq/NPLgz7wAlhQUYQ0L4T8KgFqnDHl0UkSldALjp3C8tUgd/0c4A18l+izfeeKNJy9ppi8aUmIzD4Wh14lZ/MmjQIEJCQshr37wJpS/DOtF37AR+de+9Ht1GWiNTp07lzjvvpKDAbb5V3BqhoaH06tWLoKg417J2u51wUxR7bVHNXteLFy9i6SNGAwoODm4WV4s3d9xxB1VVVUTGxvNNaMd6r2eRh9EGd3wjtXZ69RKjgf3Uch5nSkpK2LdvH7/97W95/PHH6dChg9+2nZKSgkajIS8vjw8++MAvrVtc3Yi8mq8EaWHAgAGkpqbys7nxQ3QHCFATRUVFLF682NWN6IUXXnCJmf3FlX3FEMADSXoKmAHc4Bbj9gLCgUSEELc9ECueNQnlhEeV0iHoLAaqiCEfAxZiyK9RiJtEFkGKGNZeRHhxFZIihFWLcAtwyUspQJbHIo7YUISgTREtRiLElDFC/ooBoiLNgPxsAAzZHvJcC0GYCcFCkEuam08MHTiLhSBKCSeVUy5JbifVaztal3AXwESRRxyDsJBEtkqQK95LaLkAdOYEURUXMZQjhJOKAFUdB0UsWYmnlFMtw63ELVtVSwHLwSy/1+mgVJGzAmYbGHVgtUFEKJTki2cFY6RcvhLnbDzluIpYVi3OVctZI+X5ihhWkeaeRQjiKnFLTWPkz60YQnWyMDey2L2vOfK+K2JYZXtQXRZcQxxcMs9i1WtU8wCrRQhxzfL2jAlQkiXi4hLixohEyx/7TSOk/xCW51zgj6v+S3yMRQh1I8EYhkt0q48B8wk5pklgzZWlu8p+hMl1UOJtwBO1hFSJvV1+Vs5rFCFsueozUKSkOtzHClQX4yoyXOTyg+V5xfLnVCDXrQAhn1aLfEPhJXsiv9hyCJecvBoDkgNPsWc2bsHoEElM7yhnG06rTqY6qjIQ8bileWle04HfZ27l8bTemBLb8a9RU3j+l8/FDEUGrIhzFVmtBffxGIGnLDcC93ergyoGiixYOU5z5PfKd1D5XqiXj1CVr5Ixvx0+giC9nqLz55i36TBcrRUCv66SWxSaJjVOiqtmq2p6XbJc9fYOF8PKleK1LGsNOmonZdogiobOpKD3SMo+/4GwMeEey7i+QwZVTCx4SnND5Wfle6/EVYlRLWLcpfGD0EsSRUVF9N9e4FvoWpdwVaE22a0v1ELK2tatSf66proo955PV1GyeDE6rZaCM0FEZ1jcotztTrcMU4nvWVU5SXjKXZWYquM3WoIY+MYh2s2UlJSQMku+YGyMGLe+0uGGoGynvqJc77qqRLmzNGG8N+N3mJLac2R4JN0UIataKqoIRydL4vttwS3HBfEbAZ5y4U1O3/LOyRLkwObxi4gE7A4H0dE+Lvp8CGubjDWqMnyUO3v2bF5//XVMJhM73tzO4MGD0XU1UXXLIkCMouR0OuHJJ4keIlEy6iGioqJYp+lMf0UUPF8WD69SxRHc3+kzuOM8W8SoQWJcOa7/tcZTfNVsLFVVjB49uvExmSj/zzQi/nFxcVitVoKCgtgwfDYzFy+ulyi3at8+WLYMh8MhWjptErEYk7WDHcBPFyVuUWKkyF3BLXZVRMTK74mvOCooYtwk6iXGffHFFwkPD6esrIyHH364/sEYINW8zyp+85vf0K1bN0pKSvjyyy+ZPXt2/bavEgyr3zscDo4cOUJsbDtPWTUwJjWBsClTOHJwvTt2ajmuOo75LSR0ak7s8qMptnuFsWPHDq699lqMRiNDh4qbY88//zx///vf+eGHH1zDuV8qgZYtAQIECFAPntLEou/VHwBTQiJPj5qNlTbQXMHPHAiKJP/6O7n77rv5R3II7ZspZR+EgxlbvsLhcBCZ3ptPoro0T8GNIFsKhq5iOMFB+zfWb8SGVsK9lXsoKykmNDyc//znP81adrlDi63bAAD69+/frGU3FRMnTqRPz+5ERUfzYXTPJinD4YSSTiLZkp6e3iRltBY6O8ooKioC4CtT83hGjmgjCA8XScdZs2Y1S5mXQlJSEqWlYoS/7777DoDEymKPli5arbtVhpI8OhvZvJJciwNePJLLa6+9Rrdu3Tzq1NwoQ4uHhoZy8uTJeq1TVSW6WTrsnlepQ9vHcueddxJ8zb3+rWQ9OX36NBaLuFOTnp5OZGRkHWs0nMTERB566CEAnnjiiUtu3eJwOPj4448xm83V5sWVibt3JaGJl1RGgADeLFq0iJkzZ4oR3JYtY9myZZw8eZLp06c3LElZB23nDDBAgAABWoh/l8L/O53H+++/T9nu7cLG36UrT/aZ2NJVa1YcTnhr4HSCgoIIcdj4lb76iVFTcnVZNtb92wDYPmwyZqnlTs5r4812QwkyGCjOzWF+4dGWrk6DMEp24vZuAeD8+fPYnM2XUPykMpmg4GDKSopbfVeN+qLVaok6nQFAZmq/JiljoyORiJhYrFYrN910U5OU0ZpITBQXXeeTm6cr0SeJA9BoNBQWFjJy5MhmKfNS6dSpE4ArwVLlhHPnzrnmqxPAyhDWQYmdsDfj9/39Qsi2CRfPrbfe2mzl+mLcuHEUFRWh1Wr58MMP67XOgQMHANB5dX0apa8gJSWFqPh2ZLeAKPeNN94QIwQWFnLXXXc1WTmLFi0iOjqa0NBQ/vvf/17StpTElS85clfzBQC0pkCyBQiMRuRHduzYweOPP+4x4qFOp+Oxxx5jx44dfisnkGwJECBAgFpYbtXwWzH6MHcWn+HZXd/Sce1yAIKGjOL19Ka5gGqN/CelD6a0dGw2G/O3f90kow/VxaKcDVSUlRERE8sLMU3TUuBSKJb0VPQaBkDXfZta9QhENXFP1i727drJxx9/zLclzfcZL9l1hueff56wH79o1hFJmpobyvfhdDqJSunkukDzJ6tDRKuWijNHiYmJ8fv2WxtKK4TI9h05aw9p0rLsTonS9P4AdO/eciP2NJRp06YBYDKZOHXqFLk3Pkjfvn1d89WtSCZMmEBFRQU5ubn8Ym3aeCpYnRJfJA5Ar9fzu9/9rlV837t0Ea0li4qKsNYj6aS0sPKmAxWulkWb9El+q199OHDggOuzHTVqVJO2FoqKiuKBBx5g6tSp7N+//5Jat9SWbBlmEy1bwiNN5DhafpSnAJcPERERnDlzptr0s2fPuloz+oNAsiVAgAABamB1eBLrFv6GhMRE7gmFv0SI6fdkHcC+bSMA5ydM5ie7t3Tl8iNHE8zJMdcCELR9A0PNeS1SjzgspGZ8y2effcb/bdvH+aaw8l8C79iisVitlBYWcGee/y+sm4Mop5Uue74mOzubF3LrXt4f7KuATQVQWV7OPdbqJz9tmZ6aYorOiK4Jy5Yt8+u2nU746chZDh8+TN/8tjFK16XSt29fioqK0Gg0fKnt1qRlrZBSCTdFYbFY2lRrq7S0NFcyYOXKlTg14qI7X3ZaqC/Cg4KCOHLkCEuWLOGn3PJq22oK3td0Y/i0Wfzmvvu4++67m6XMuliwYAFZWVls27aNNRV1JymCg8WF/471q6vNUxIPh0OS/VvJOvjvf/+LTqejsLCQOXPmNHl5d911l2jpazKxYsWKS9rWn/70J0JDq8vv20mVlJWVAfCzJuGSyrgsCLRs8Rs33XQTd911F59++ilnz57l7NmzfPLJJ9x9991+/b0PCHIDACBJa4CFQKqQNfZCCNHUYtwEoJ2NyMQ8YoPyXSLcKIowUUQM+Zjk1yYKXdODqCKBHILsVUTkVglZpyKDVV4XICScishTLYlVxJ5luIW4iqw1DLeY1YCQVmrl94pEF/FaJ8tco4LNEGkGQ7FrHVsolEYYASjChBY7OcTTgbNUEIIZI/HkUEo4OuxYMLgkuUWYPGIZQgXx5BCCmQRyMFJBLPlo7EKQG3OiDH2l175acMtFFbmsIuUswS3EVQSUihgWMc9qAbNFiHGNwWBT5K/lYlWjDkpsEB0MBZVgBArK5enloJd/CawFoNcKsa4tX2yLStxSXEXQXSLHORi3nFUn1xl5mjI9Un7OkZfPRggKFXmuWphbjFtcqnzmSlwKVPOVmCiyTYvqvfKsxVOSa3PHCkBvALNaiCvfgCrJgoh42BdsYs2k+USHhTF37Bie37gUyY4QsWXBE3vW8VCQkS93ZPBBjoWMRIgLBWux+GyNyjlBgRDmUiaXpwhzU+SYJMn7qBLQusSjOoSAVTlGtIBy47EQCFLFUPnORHsdI+Ve75UYGeQ4Rsuxtcmf1UncAsQYd91e7DKOkLAwSvLy+NvxLeK3YZPTt8S1o+SW38bjKcVVo54+xGsdRaQ3QhLHjLL90RK/OnGID7KEP/P35+CjdvL8dPHZYMBTAKz8HihCW0UgHAMck58Vgasivi3AfQxFyPOU36tQOS4RuCXGNrA64cVjOZw78DIvpZsIOmwXUtCukls4OlqqXb5aHyluTdQky1W2o95ezxB4+mP4thiUZvAqOeoDNlh8GjaUw663dzLwrkGueS5Ro+IirHRv1iXKLUAcW8oxl487top8WCXGfbtI2HNn3XADSV98UV2SOk5yLVuruHW01/HSWLylwu3wLa70JX/1IcrtMnAI+fn5FF3MFUPs7vYSsiqi1nxEbAsQ+6vDLceNlqfFyNM2OckYK/HT3sOEHD/DOyMrqgsuGyrG9ZcU1xf1FeUqda1FlGtKGMBGaxQJF87x4BDJ/XuhyDUVaaYidFX+R84gplvwFHGqtwEugeuaiL5EmM3CFeXLfXGpYtyukud7tTC1Pijl+qiHcmf22LFjmEwmj9UqK+Uv7XZxHE7IgRXA6vSJPKaW5KplwyCkpPMldx3VgmGolxjXmQP7ht6FCYiIiiI8PByrtRFZ84kSHAIsQvpvs4ExVnL/JzbwM4mIiECn07F582Y+uvVWprz/fq2iXGUEHafN6j5+vhDLxU8biWXotRSkDao7hlA9jgoNEOPu27ePgwcPMnjwYKZOnVr/4ckHiDIKCkQc9XqJ6Gi5zDpkuSkpKVitVvR6PRs3buT666+vX5leolzHhMcxGAwiSaUIrxNwxdO2cD6EdePIhN9Ul+M25PtyOdBUiZErMNny3HPPIUkSt912Gzb5wkmv1/PrX//ar8OlB1q2BAgQIIAX2bpg/jvpFkLCwii6cIEntqxA53XeopWcPP3Lt4TnZnHeDrfmC6fJ5cimkASC+glT+/BtKzE6W1Zbr5HglViQgGVWA9/rW8eQkJ8XwZlKiJMc3E1Bncu3ZtoHw/wOUcyYMYMPPvigScsyO7UYbnmQ22+/ndtuu61Jy2op5s2bR1VVFRExsawx+u9u93KhM2Dy5MkYW6fCqEmYUpTBjz/+yLdHL2K99JFnfVJsh/9u2svixYuZMWNG0xTShIwZM4Y333yT999/3zXNKQ+tm52d7bHs1XKrzR07dmBxNu2lwWpje0xJHbDZbNx+++1NWlZDWbBgASBaoCmtKWpCSbZIjur/h70qzgP4HMra6WyaE4U///nPfPfddxw9epRrrrmmScrwxbx583A4HJhMJjZt2tSobSgtgWqSyUeUiB+63NxmamoZ4IogKCiIl156icLCQnbv3s3u3bspKCjghRdewGDwX4v1QLIlQIAAAVSYJS3PjL2JyLg4ykpKuHvFh8TYq3wuG66BpVFglGBfVDv+ljq4mWvb9Did8H8FTrKysig+fpC5BSdaukoADDDAAz1Teeihh1g1enarSHR9GNYFjUbD/VEQfBn8u94ab2TQoEGEhYWRqQ2re4VG8pkznZDQUOJiYpgyZUqTldOSxMTEUF5eztatW/k6x39dNTJiBhAVFVX/O8qXCYMjITY2lhIbbFGFs8qPp7WfFEKlA7p27XppwxK3EOPHj6e8vJz8/HxXYkAR5o4aNcpj2R7BokvIQw89xE/6phWR/tBRJO4rKipcIt/WwrBhw+jevTtpaWl1inJrS7aMtmbjcDgIDQ3lmNwK2gm81/M23njjDRx+vvzKyMhgxYoVaDQannzySb9uuy769+/vctQsX768UdtQki01OWa65B7mm2++YdeuXY3a/mWFnabpQnSFDf1stVqZMGECx44dIyQkhD59+tCnTx9CQvzvrboMTgcDBAgQwD84nPDPSVOJ6tgJi8XCzFUf0rWitNZ1+ujhpdRo7rrrLuzjp7I8qXWdPF4qH5vhu7O5fPj229x//NL6ZPubRbaL6HQ6TEnJvJPQq0Xr8n1wMkPm3soDDzzA3dGXx1/rpIosCs+dQavV8lHCgCYrZ09MfwD0x/e2ClFmUzFlyhS+//57Pjtb5LpQuxQyMjIYfO0sHnjgASZOvLJGRtNIojVPt27d+D5KiGv37NnDk2Me449//KNfyvhcK/qC3nHHHfXvktGK0Ol0XH311R7TInev4urvXqvmI9BIotuRJEnsCu0AwAeVXfld8g0uz4s/OOEIJSRd/Fa3xsSqJEnMmzeP2bNnc+jQoVqXdSVbfLT0jMLKxYsXOXToEBmVonVLscHEycjO5OTkUKbzb/L69ddfJzExkZtvvrlFRM7KZ2k0Gjl16lSD168r2TKoKpsdO3awfft2mqhhUIArDL1ez969e5ulrMvjjDBAgAAB/MCLSf2IGDgIp9NJ79WfM6Ysp+6VgHvsBVQe3ItGo2HHtNmcMzTPiA5NTbkDfl8iXv9vtJMuWkvtKzQznWzlBO3aDMChQRMwO1uuH8UPqcMB0F88TZK2ifo1tABdj4nhD4u7DqrXCB0N5YwUQlhKOgDTsnb7ffutiauvvpooLeTaaHRzezWrVq0CoOT8KddwyFcSo0aNYt68eZgHjgPg66+/xhAcTHBw8CUfq3sdJkbf8Wt++9vftunhtIcPH86sWbNcF7Fh9krGWHJ8dteIjhbdMS9EpQCQ2W8+EV378re//c1v9Xk/dBBarZairLOtNkGotBIzmUwc0UbUuJzT6cRut6Px0bIFwGKx8Omnn7InW7Qm+lTb1TVP5/Cf2f2nn34iKSmJe++9l0WLFvltuw1h0qRJFBYWkpmZyccff9zg9ZWuVeoheNV0MYpETGlpKedb12lI8xMQ5PqNW2+9lbfffrvJywkIcq9wJOkpYBQwEcKB0QjxZReEELW7eK9JKKdTwimCqCKVkx5SXCGBFTJYRZAbTikJFbkYZFkoBapnZZpahJuLENYVi4e1XMjOzBYhdrUCpQixqzEL9EB4sJC5GhVnnSLEVaSsWoQkTSvvixYh3FLkuopYNxh0ERBlN0M0ROnMEApJkaJvaFFEGFrsHuJcnfwewIbWJcsFiCXfJcU1UkFURTGGLLBahICXg7jFv7mIHzh5v7HjlqGWycvY8RTiyn80VhuUqppPW21CdFtaKeJlBMxAhA7MNvm9LMfV60QMzTYRR0WsW1op3peUQ0QoFBQLWa5RuCtdIl0PKa5W9TpUNV/5LArk14pQNxIhx1Skpcp85Q9Uh1uMq8hdleNEiZVdXkYRBlfK21aEsDo8JLPWYtCHgrlYFtfK23DJceNhfRn8ZecRrks9Ro/sU8zPOgaAvsgJyaoT9yz3S+IRArlkiT/98h1PtWtPZFw8/5o0nec2foZGkj8XgDAwholy9TFgPiHLck+Kurn2Q6m7InRV5LmKqFVOfmBDfCmC5XWVZSrl1/mq+GvlfVbk0fnyduMRQlwD7uMsUt5eJDzXYSTdk4MJ27aZ34Vb3Puci5CQpkm+pbg1CXHBM5bnVcup5X/eslxvWSnuab9NN/BM7yFEREfzCoP4ve0Xz/IUKasaRd6qHIfKMQduUW4MIk7K8ReG+x9TEQzLsuGj2nBC08Uw1NNzfxZCT0WMe1RV9zRJiFa9xa2XIsWtCbUs15eQd08xrFwJ/SLBYhb1Uuo6TnKJRG/96iee/r//I9xk4vPHP2TeP+e7t2GhZlGuEh9luRT59RnVNOCTxD7i4quoiJG/5NYsxlUEwzWhFtn6K4aotlWX2Lgeolw9MGvB7WzYsIGv/vAbxsYgRI9DZJkruI/VSkTMFEWBoiVSRnbe5CTn3ruISk6hQ89+QnKZgFtoOVGOW2sQ43qjjlVtQli1KDcJdzwzhGx02pu/4+0Zv8cUn8ixYRHEGdpxYYI4PrdGxnMVOe64rXK6ZZqz5X3e5PQtdB0g8VWq6Dak0Wjo0KGDZ72U2OponBhXLcWNxy10bqeafymiXBuu2PXt25eKigoATpw4QfvZD8Pf/+4pUpb3ve+YVHZPvANdUiqOURLne91DcnIyv6xeCftV38ckRAxzcAtdFZFrLWLcqmw42Gk6PZxOug8cUv9986avhPWM/L8pi9317UBvR3xfyhB1i5Xr2sDPqE+fPrz99ttERUWx4sb/5bHHHvMpyk1JSeHGG29kzJgxsNHpKVne5GRwmsQ7QEavKZwYF86F8Y8CsG3bNkKizGI57zgqNECM++WXXxIZGUlpaSmDB9ezK7OXFDciVJznRaeAOUfI/K2HQR8miXOmevym9u/fnzvuuIMtW7bw2GOP1W/YaXnfnLfMBZPJnWzZpPqeAkEbnQxKbYc1uh0Zv3uK9lBdjjtbgouBoaED1B+bzcY777zDmjVrGDRoULXRsJ5//nm/lBNo2RIgwBXCmpQUnp4zh3Xt2tW98BXGaRvMzYPyykocX37EHw9tbvA2Ih1Wpv/4JXa7ncj0nrzRoW8T1LT5OKELo2roOK666ioe7del1TpIoh1VJGxfD0D+oLFc1DT/MNwfJA1Bq9VSePYU47jQ7OU3JREREXB4NwC//PJL7Qs3guz2/QBITm7eIVJbiokTJ7Jw4UK0o67FfgmtL06cOIEpSSQB2qK81R90cFRQXFwMwI9hHTGePcrx48cB2KbteEnbzk/qDdDqnCIN5aqrrnKN9LNy5cpau+mNq8zCbrcTGhrKQY3J1folp9I/MocviuGzFd/w8ccfu0S0rZV28nlSVlZWjcsocfUlwQXoK1+3HTt2jEPaKNf0s4cP+KmWInETESFa39xwww1+225juOmmm4iOjub06dN89913DVq3PO8iJ0+erFVKOvKqscyaNYvDhw9falXbNoGWLX5j//79DBw4kPDwcI4ePUpGRobrsXv3br+V00pPnwMECOBPVqalcWjBApxJSfy6uJgfLp9eDpdMkU7PbxP6kueAgXp4K8KJppHXQFcXZ6PbugGAM1dN5ZDBswnyudhkPh49hwthrWP0nNp4vfs4goKCKMo+x10X/Hdy2BTcf3YXJSUlGENDeT11aLOWXY6Wqu5iWOR+h39u1rKbi+vO7ARE4uWoNryOpevPdnsMpoQk7HY7N998s9+225q57rrrsFgshIZH8L24P9sovv32WyRJoujCebp27Vr3CpcpwcHiTnZmdCpVTjh9+jQAFyJSG73NbUFxmOITsdvtzJ071x/VbDGMRqNrVJ0OHTrUmBgAiMTqEp1uMnZwtTIwGAxUVfmWxDeE/8gt4ObNm9ckEkp/oiQwTSYTJ074lsLXlWzpHqbjscce49Zbb+UiBioqKqiqqqJTWRZO/NMlc+nSpUiSRHFxcYtLnI1GI3feeScmk4mlS5c2aN2TWzfw7rvvVhuiXE1YWR4AeXl5l1LNAAFcrFu3rsbHjz/+6LdyAsmWAAEuc75O7cyZefPQ6/WUl5dTarFwuwN+CiRccDjhxUnTGDTrBmZdM4kv4yDkEn8V/3BwE0VZZwkODuaF9BEeMrdV/a7maOfePByahrUVS95+Do7F2GcgAFcf+6HRyafmIhgHXbt2xel0kqELp6QZrfofmnpgDA2lrKSYWwqONF/BzciwyjwuHNzHxo0b+Tjff8FddbqUZcuWkfXzRtq3b3zioS0RGhqKxSL6UG2K6Nno7Zw8eRKAyIvH/FKvtooiA7UldqTKiUvOqU/o2OgRyr6LF61aSktLSUpKqmPp1k9YmJCxJicn1ymgVpJXpyOSXa0MbrzxRn7++dISyfusoezSRCAhRj1q7fTs2ZOioiIkSeLLL7/0uczFixe5+eabCQ/3nYCO0dhcEt1jpVb++c9/YjabGfzAn8k2XHoL40OHDrk+22nTpl3y9vzBwoULefDBB+nUqVODWgZYJXHiVdvxmVghkiwhISENTuZcVlib8BGgSQg4W65ghK9lIZAKPRB+FtnRwjCgg4V2SVnEkIeJIqJkR0sHzmKiyOVoSSJL+Fvs+YQXVyFlIRwHBYj+s4qPRP26GOGekF0cJeXCG1KCaMlmxv29V7dss+JWVugr5QO4XHhIQrLAqBOOkQjFyRKM282i+CqCEe6LYITbIlSernhIZJeITivmxUaKu0JRkWYIhiRDLhWhQdi1WioIoYog8lwd6SGKIuLtuUQUVIm+uIpvpAQh78gE8nD7NRRHi+JpUXawWK5PsbzvsqPFahMx0cmxMeqE1yZadq4YdaCTg/Zz11SOzZ2HXqej6OhhnvxyKRe1dr6yw5Pde/JUZQnDTpwTcbaJddXeFmOw7M6Rq1RaLvr1WmWni+KJQek7XS7H0SB//hFyRfNxO1xyEf3o7Yg+yYq7RXGyKO6RAvlZ8fso21HcJsjbqMTt1VCCYpHrpvK0KG4WJZb6SHgleQCR/frjcDi4I/uo0Eqcd4r+zIorIVnydIuoGSBVc5AEAbd2jeGByM78vHMn41Jhvvx5lMvdBo5XOXgtCB4vEP4WynB5hKy5oFc8GGG4PS7K8aI0ipF9IRTjPo50qumK7wVc/dpdrhblOFc+C8WbYxDLftJrEiaNhqJjh5lx6ozwCmQBZxF9yBviaRkieXpu1LFsqL9FXUaa5P5xOO3kzjQNI8uj2ZGTz6C//Y3/ffdPYl4SnuWD2yei+FzU7hYtnv4cu7xcjhwj9bCLwA5TB5KB0BMZBMU7hJPE29UCvvu81zbPn3g7b7zdLXq920nSDrc/YJzk8keMmnEdry9YwBk9/OWEA2mgKiupdrckyM9q30glbldLjPx6u5PPjBL7K/eyaNF/G+dqGS25fRdNHcNNXsdeTWXW5m4B6CnRO6UHZ0bcRFX77jj6S2h2O4WjQY4LoyVRnuIkGCdv6wyQANYqCZ3sQxhWdtzTvyGXQRK1+1pawtXizVGnxzFWq7tF9t649mmVeJ44KIS3Zj5OZGwcWQv+l1lyt6Kdu3ZxQKujT4xNxE2J5WjZbfOFl2MDYICEwwllEx4hAujWrZtnPSaqjsm6XDjeKMezst++PE2jJfEb6w93i8pz065dO/Ly8hg8eLC7FUaG0+35WeV2t6SO6cqq2F5oNCF0crjvyOz+vwcZY97trstsydPXIq/vkwESn6VN4OGHR5GVlVXdgVMf+kqQ7/b5GRO85mcj/qdA/G+2E9PMJ0BnkoTfpab/8RqIi4vDarVy7Ngx9/dL5W4xm810796do0ePimnbq3tb7LfNg/DunOsxFra/5xp1x/7/trmPRW9fi+JwqsPV8sEHHxAUFERBQQETJkyoe4dq8bQA4lwgF4w9gAIwFgNace5k00tERwMDcB8vPujduzdvvfUWUVFRLF++nP79+9dZrfLyckY/8AeiDx70TLZ8ofotBNIf+Sd5W7cCcPDgQQqW5btOh1yx/PIcxMbWHYsAVyyPPPJIvZf1l7MlkGwJEOAyZU1KCgfnzCdIr6fw6FH+vnYpdqedTyPh1qh0es2Zw1aLBWnJEobm1G/UncuJn8ISyB07FT2g3/wjs/JP+23bg8oLmHahgG1OWHQWpiRClMY9vKHVauXJYrjOJNGN1tXEZUVER0xduuFwOJi/e3VLV6fe6HGyyJnPLYg/yN+aIKKJByc6boE3l39HwtZdbOhZVvcKbZjrr7+eUAlOWGHLli2MusTtHTx4kP2VoJdg1qxZ8E+/VLNNMNOayUt2O+GmKHYGxTAEeFI/jpJhPXnw1ClS61h/e5GTl15+md69e/M/seebocatl2SHmeLiYiIjIykrKyM2NpaMjAzWrl3Llv7QJ6bOTXiwPrgdERERWK1Wbrzxxiapc3PTr18/1q5dC9Tc5UVhdOEx7tt0jLCwMH79q3td08+HJ4F5N06ns8HDYFehwZLan1CNhpEjRzZ8B1qImTNnMnLkSCorK3n22Wc9WrCcPHmSoqIiIiIiahXBRpTkAN1dkmKlpYvDcWlNi0tKSti0aRODBw9m6NDm7TpbF927dxfDW5eVYbfb6xTlFhcXExQURP/+/SkvL69xuWHDhrFVTrYAFBYWukbQuqKwy4+m2O4VQEZGhsf7Xbt2YbPZXMn1o0ePotVqGTRokN/KDHQjChDgMmSTycTBW24Rdz2OH+d/v/6MUKf4JQ2W4OWc0xSdO0ew0cimBQvYE9PAM9I2Tr4uiOUT5qLX6ynKPMYfDzZciFsXvw+DHjoolPQ8GSs3ddeJE12NRsPYa67ltVm3Nbqpe1PgdMKP/cYDULl3J0PM+XWs0bq4KRS6ydcSL0elN3l5b8nhGViWQzdNzSeJlwOhoaHMjtLSo0cPPv/880ve3meffcaoUaOY3jGCqKioule4jIiRqigpEU0Yf4zqAoDUZxyRcfG88sorda7/XS5UVVWRnp6OXmpFPyAtyMsvv+w6iVYuaH8ubPh21p3OYcmSJVy4cOGyuZAbMmQIX3zxBUuWLHEJhWuiW7Db87Jl28+u5Suj2rG5w0ieffZZchp4c+bT8C6EhodTUVHRptxMvXv3Jj4+HqvVWs3f8P/+3/9zdeGpabhigJTyHNe27rvvPmLlVhfKMdpY3n77bTZu3MjKlStbXUznzZuHxWIhPDycb775ps7lIyMjXa9rS8xERkaSt9rdfehSYxjgykTtZZkxYwZjx47l3Llz7Nq1i127dnH27FnGjx/v1655gWRLgACXGbuccEdREdt+/pnCEyf47SefEObw1IxH2aq456OPKMrOJiQsjB8WLOBUaFgL1bh5cTjhn1fPICImlrLiYh7Z+CXaJrhgMUjwr1gD999/P6aZN7IlNA67VmQCkoJE1jwqtROvJPf3e9mNZXU5vLdsORk7dnDvoQ0tXZ0Go5Xgd9078MADD1A6fhZlTdh40+zU8FGF8BvcY2qyYloVM9JTuOmmmwgODqacxjcbcjjFndlJkyYxsVeq/yrYhkhIEP0gzkZ7umrq02pgldw9a/LkyX6vV1tk5MiRFBQUuC7UQkJCCA4OJtOYUsea1Vle5ODUqVOMGzfOz7VsOQwGA/v27ePUqVOcPXu21mV1khjCNz4+ngMHDvD+++8DEBaXyJou12A2m9m2bVuDyt+ZMkBsW6fDaDQ2bidaCOU7ph5dx4mIqTJaU20Jgl5VF12vExMTXa8vpWWLzWbjpZdeAmDRokX1G2K5GYmMjHTJgzdt2lTn8mpZcl1xmVV6wHUMX2rroDaLuiuzPx9XYO5q8eLFPP300x43fKKiovjb3/7G4sWL/VZOINkSIMBlxHHgJjuUAhU//sgfl31AuM33eG5xlZX8+vP3KcnPJ9xk4r2b5lOoq12edznwmjGZiJ69sdvtjF/7OR2sFU1W1gSNBSn/AhqNhqWDJmKXm3BHFucT9rNIZpwfMYkzQS1/Aup0wpN5UFRUROdN35BmbZvdYhaUn6eiooKQsDDeTmq64bc/iezGbQ/9jusmX8N0/w3Q06qZVXKK8pISgoOD+Syi8SPgbAmKJzIyEpvNxpyqy1MqXBfXXnstr732Gm99+oWriwEImWltQ0KfsoUy8MZ7ufrqq7nmmmuao6qtniFDhgDui9727dvzhz/8gfE33cFFe/2Hgj9ugf2VYjutRTjqb3r06FHnMmPGjOH+++9nyJAh5OfnY7VaPbofKS066sPp06eJ6Cx+K9riEOVXX301CxYsICQkBIfDwfe9r2Hx5Ec9EieK8NoXfWwFPpMCl9Iq4/333ycsLIz4+PhWO4T2VVddBYhua3W1prpw4YLrdV3d3CTcsQu0bAlwqZSUlHDx4sVq0y9evOgamc0fBJwtVyguOW54KnQDRiDkuF2A3hbik3JcMtwkhCQ3iWyXGDeJLOLJJdxeSkRulRC/5eIWcOYjpJtqOa4Fl+CstBzMNqhAyFcVAasifkX1XrkMNeJ2x4Jbnq2XiyqQVzDa3PLcEIT01WgAoyJoDVU9FEmuItFVz1ekuaEI4Zq8nM4AETFVoIOoUDPl0Ro6WLLJi4gkxG4WYtx81X6Xy89mOcan5MrK8cAiL6MIYivBLPdIsMr/JTabpxhXLcdVYrM/KILXJo2ldNUqhjitfK4Fo83hksKqMYbKj5IKFnzzAR/PuZuopCT+NXsOTy3/BHO5g+hI8TkpmCuFMNdsEfHMyYfoSCgoBp0szqVcSGldFSyX46alujA3GLfsVZHBhqpipoiFlTqoXyuZeLUMNhisxaCPF+vqI1VSXJt4PmaFxw6dJ/7dd7k3OZqpeWd9C3Hj8ZTpJdRw8ZNEzcJXxLxbRsTydedumLp0c/lc9VYrjxzbyv9060dEfDz/GjKRZzd+DZGyMFcR2parnqPlZ+W87qIcQxACUmW9Snk5rWpegTwvXt5eAW5JrE68/r5My1aznWAJHotASBs7qvZHkdM2VIrrHSOv+PiMXU2yXF/1UU9PguCtdmImjsQy5lpO9x2B/buM6i2XUhDCW+ULpYhyldjFIOpvwS1/7owQbcof4t6B12LS6+kV4kB/yOmWWR51umWzUF2iOkKOjc7HvKZGLco1GOHpj93zFEljLaJc/VEn+v/9XwD2duwL7x10SzbBfWzacAtxlfcJ7mlro8UFX1lZGfEOi6f4tCFi3HY0fwzBM47qOKlRi3LVklNZ9jrglkEYDClYnLCudyiVN/2B4OBgDh8+TIZBx2BF+KgWu25y8sOEPiQlJRESEkLStGSxjCLv9JYM+6I1iHHVrPc8xmoUwq5R7WOS6n2Gk5T+ElcNG+wa+jbk+B5K0/sTHh7ORnMSs9fLQ/eqZJu+5Lif3/Y0kzdsICgoyLNr26WIcbu6f5uIRxy3aZLv41b9HbxUUa6XVPjAgQN8//333Huv28NChhMmS56C5e1Okq4ZRNGomfTq1YsdO3ZQXJBPbII7uWB8589CXgpumesKH3UbILE0fhjakVMoKipi+PDh9a+/Ql8J6xnQyzkzfYz8H6/8X9f0/Zd/f41lsoy4DKyNEOVOmDCBjIwMdDodv/zyC9u6COeM+p/+zBnVj52XJDd0s40TN8yiY88+HokE+z+u9/274X1c+mDv3r3MnTsXh8NRd0uhyRJkeEpxdbrqUlwXp+X6K4MV6CAiDCiWz0s3AWGSEBTX8ts7depUNmzYgMFgYNmyZdxxxx01LqtOtqQ+eTPMVg2Ls8lTaq158UccS5YAqmSL+nud37a6PjcKlaDf79u9wrj++uu54447WLx4sct99PPPP/P73/+eG264wW/lBFq2BGj1OCSJD8aP560HH2R1z8YPlXk5c8FoZOmtt9Jj0CBunjaNb00QXk+HXf/yQq7++iOsVitHCwp5pMrZypSt/sHqhFvzocIJnbNO8fiJXc1S7mBbPpV7drreb9y4EX1ZCQYcXL1F9GcO6zeIH+JabuhbhxNWTV7IjTfeyP3JkSS28TT8XWd2YamsJDImjk9iuvh9++c0RpcscWrxHr9vvzUzadIkAEJTulBY2AgpBlDUXojoOnXq5Ld6tTUkyd1FYW0ZvPv6v3nppZdYunQpG3JqHoPzeHQqQJvrjtGUSBIM6OVusaF12l2tCQ4G1f939cKFCwwfPtyVtLmc6NmzJ4sWLXIN51wb6RXiTq/JZOKhhx7Cdv6Ex/weuYfrXe65DuKcTek219aIjIx03eHesKF619rt27eTm5tbbbqawvVfsWbNGtf73NxcwmyNazn6008/YTKZsNvtrbZVC4jWYQaDgWeffZZvv/221mWVpMnBgwcxOmu/4tdoNIGWLQH8xuuvv86UKVOYP38+HTt2pGPHjsyfP5/Jkyfz6quv+q2cQLIlQKvGBvxfWBjBw4cTER2N/cYb+dqPhujLgRKtnnfmz8cUH09ZSQl3r1tHXAO/2dcWnCf11VdZ9d13vGN1Ureise3xt/b9OR4WjUmCJVGgadiACpfEvANbXCf/+/fvJ6hCnLxNKzhD6T4hdVw1dmqt3Qeaks+j0ohqn0J6ejq/CW37JzBxdgvSfpHg2t7T/6NffBbdC61WS9GFbEZSvQnq5cxVV11FaWEher2eL7/8ssHrH7WHY2qXhNPpbJPdCvzJiBEjmD17NuZr51FQUkphYSF2u50f82pex94uFahfd5ArifBi9/ju2qpK4uNFk86CsGTXdJtGV2OXj1OaUEwmEwDTp09vuoq2AfpWeR6A8Qc28swzz7B06VK+W/ohUeb6JVlPWuD1Dz5i+fLlzJkzpymq2izExcUBni0wFOx2u89uCGq6G91d3Hbv3s3u3btJqajdnVMTinC2tLSULl38fyPBn8yZMwer1crKlStrHWXIJnd1t9vtBNVxCiRJkt9GdGqzNIWvpalay7RyQkJCePXVV8nPzycjI4OMjAwKCgp49dVXCQ0N9Vs5gWRLgFZLJfA/wJelpbz//vuukyT9jBl8PXBgi9attVCFhhdmzyW6QwcqzWbGv/8+3eroH1sTMwoLeU5WtvxDo+FzU9u8E+WL70ztYdJM7rvvPl5KNdGhmX1y/SsKKT16CBDDFxpVJxS/2b0GS2UlwdExvBUU17wVk9nSW9zJte/bQec26mrx5rbjP2O324nq2IkNoYl1r9AAzqQKF0x85l6/brctoNFoMJw4CMCBAwcavP63OuFvKMo6d0W3bAEhdu3Tpw/xndNd/SQMBgNnTGlYfSReMzMziYiJxel0MnHixOaubqsmtSIbgKysLIIPbKNPnz4A6OOScTgcOJ1OXhz9MP/+97993hH/xpSOJEkUFRVd8Yms9k6zS3AKMCncjtls5sCBA1jPH6v3dj4uEj6TmJiYVp8YqI2B8vmmwWBwXeAfOybiYLPZ6pRapxshIiICEAmCxiYJ1BLoUaNGNWobzUn//v3p3LkzZrO51lGJlGSLzWarM9mi0WhYvXo133333ZX7Pb3Cki0vvPACvXr1omfPnjz44IM4nf5vex8aGkrfvn3p27evX5MsCoFkS4BWSalez1NJSWxBqCUeOXeOW55+mgLZgq+fOfOKT7g4gOeunUx0165YrVb6ffQRg+q4w1IXv9bDr0KDuXXBAvbNv5Nd0bH+qWwLUqjRs2bcDWg0GiqPHeY2e1GL1GPsgV+orKwkNDTUYxyXTtZyIr9byssvv8xfjudS1sw3a9aEJBHVsRN2u53bjm9t3sKbkG5VJZSdOITZbOZLnf+GcN2riyKqfQpOp5M5F/b7bbttidHZInFYUlJCZQOP14NVoVitVmLPXZliXDVdu3alpKQEjUbDPffcw8SJE3n00UeZM38BW3XVZVtr164FoDj3Au3atas2/0pmsE0kW+Lj45EkDWPGjMHhcBAaFk5mZiZ5eXmUG8IoLS31eZf9REIa4L4ovtKpKCpwvR4cYmfixIkkJCQwYmA/zkXW3TXL6XTyodwAZv78+U1VzWZh3LhxWK1WjEajawQiBbvdzueff17r+qaUNJevxmw2NzrZ8t5772EwGCgpKeH6669v1DaaE0mSRNfk++/n559/rnE5j2RLHVelGo2G/Px88vPzPUYxCnB5cvHiRV555RV27tzJvn372LlzZ4NHQ2sNtPGe+QEag4ccdyLQGxgMpEK7vicxUUQ3jhBDPqmcJIoi2pFFArkkkUWHkmx05cAJhLE2CyE4zUKINxU5bDZQ6RbiltjcMlwrnklU5UA0AheDg/l2/nxGJCZy9r33eObcOZQc/lPrVvE3IHz4cPQzZ/J9VRXT9+9Hj9vJqk7QliLe6GygL3f7WfVqaW4wQhqqiFyV18GITE+MPD0Mt0g3Up4WDKERDgiDRG2x2LgSh2I5FuW4ZZvdgUygiGpSXOxCQmu1u4W4yHUtsQkRsCLEtdrgg5FDCR8yFKfTSfp3XzD+7FkSVOfnvs4ZI8xOD9mrepmEeCgpgRcjq/iDRsJgMPDdjTeT/sGbBBdZSEiCArlrstXuluXqdKDXusW55mIxPyJa3rcs3MLhbNwCYuR5wXIsFRksuKXCyutyOUZQfdg7nVjPnAXGBCg5K2S41nzx/Mc+VxMRHU1ZcTGP/bxSrHPe6Ray+hLiDpA8pbg5NWTS61pOJX2dnpHJu3PnYrfb2VOFKBOxn48UZPK2HY7Z4flC+Iu8Tx77mSvvq7LJCKBQnhaNON6U7vhKvJQvRTTi+MvFLYS2iOeV6SOJBCoO76dHVIkQzSkS3IZIcdWC29qkuDXhS5brvQ1fUtzTTvf0EXI9k4CtTq4ZHMHMzyox2K38taiE8EHyAa/4DBVRbhJuoa8BETs5Pq4hEWU57opUcce86PQJ0veWCHllV8lTjOtL3KqS+NYYw+Yi0wlWK6xcCb0j4YhqRK7aRLmy7POaQzYWJcdxJKeQWV9/zfRXZoj4ZKjkkIq0cLQkfkPPQPn6Mt4PD8O+egvbt293y04PqrbfmsW43mQ6fcdJzVFndfmrSvaqmfYoALGxsYwePZrCwkKioqLYHpXCVeSI40+O5fG5MwjtPRjjxVOeUlNF4NqWxLjeqEW5ihTYF17iVwAynPS+VsNSedScnJseIWJCJKUjfkNkTBzb75tG7ygj9BHCQ8f0FLeEdICE1SkRNPkxAJck0YX3MVpf1PFOUwlv63Pc+kuUW5NUWM0qdxzUklzn7fMgTrRuDfrJyrQBWmw9xhF31Tj2DB5Me6hZ5jpZYnNBHKMW3IPpwIGGiyb7Sq7BFGw28b9OO8R3X6GuOKol1gA5TvSNFOUajUbKysqIiori559/ZseOHcydOxeA8ZE218g7LrwkualPf8jhVauwWCyEhYUxYvhwfrkAHkfaEMm9rg+cTienTp0iKiqKuLi4uod7HiBhVjVCilbOWwGjLymud/0VlP+tdDDmyoMORIjzK1s9RLkTJkxg8+bNVFVVUV5e7rPVgNKKymazEfRjfvXjapMTZot6KK2IXAkrtRz3SkE5B26K7bZCbDYblZXiAsBqtbq6iLYlAi1bArQqckNC+G7hQmJSUnDY7fwZGKaar5HgoVWrKN62jfz8fJ47fZqVLVXZFmQ18NqZLMrKynCuXs3Cc/UX1tVFsNPBw2uWUlZcTERsLC9MnY21jmayrZVVpvYYBoo7SoPWf02CrbKONZoOrVbLsWPH+Oqrr7jGy1Gol+DvHcTrT2M6czK4/kNrXgp7DFGEdxUCw1mHtzRLmc3JtYZSOmGlzAEfffTRJW/P6YTlW3ezevVquh2q+U7d5Y5Wq2WSXty2/uKLL+q93po1a7A4ISUlhX79+jVV9doUHTp08HgfGSmuiLLCOlRb9ky5uGDpUX6qOarWptBKTtdoL0qXY/P+LXz99decupBDbLm71add43mh+qMxCaPRSGVlpUsAfaWjyTrqfq3RECQ1bMjd1Qm9SU5OZtiwYS4XTlsmKiqKo0ePcurUKYYNG+a64NM56o5F166i66TBYBCtY7Ra7FLD+jJv376d8vJybDZbqxbjenP11VdTXl5OUFAQK1f6PltXvq82m42goKBat6fRaOjcuTO9evXi9OnTfq9vgIaxceNGZsyYQVJSEpIksXz58mrL/Pvf/yY1NZXg4GCGDRvGL7/8Uu/tx8XF8eijj5KSkkJSUhITJ04kLS3Nj3vQPASSLQFaDTmhoXx/++1EtWtHRVkZvZcsYcS5c9WW0wC/WrWK8jffpLS0lCeBzc1d2RbkgBPuA86eO0fOq69y3xb/XySnWsoZ8cknWK1Worp25VXvOzdtgHKNlh/GzEKSJEr37+bmnOMtXSXWrVvHx1HwGx9dQudEw63XTODG227jP4PHNUt9Pu40BI1GQ9Gp44wpz2mWMpsTSYL7xKBBfPLJJzgu8QbYIQvsyilh+5bNzC88WvcKlzHXyXqhH374AXs94/rDDz8AYljQujwHVwoDvbrD9u7dGwApzjPZkp2dzaff/cBzzz3HVMuVfezVRJksxZ42bRoAnS5ksHPnTvbklJBUmu3qdmDXeDbq3uYMpbi4GIvFUufF3pVCRP5JAKqqqgAI0jQs2VKWKpL4bdnVombYsGF89NFHHDp0yEOIG5x7opa1BFFRUa6ua8oodnapYZdfH330EW+99RaZmZkkJyfXvUIrQaPRuBwbu3b5HgFywIABvPbaa2zfvr1eyZbu3bszaNAgTpyoO/aXJa3I2VJeXk6/fv3497//7XP+p59+yiOPPMITTzzBrl276NevH9dee63HCF79+/end+/e1R5ZWVkUFhbyzTffcOrUKc6fP8+WLVvYuHFjwyvawgS6EQVoFeSEhLD2ttuIlkfUGfruu/TOz69xeQ3wZGUlucA3wJs9emAoK2Pw2cYZ3tsKZ42h3G8MpTw3l7Ea+HtFRZNlTEdlZ3Puu68pnHkD+rFjWVFwijG5bedOwuJeQ4iMi6OirIyHd33f0tUBRFeBm2sYsVWS4IYLx9jLGPR9B7D72Gb6lzVuaN36kGeD175fT4/cYh43ZNW9QhvltnCJo3PuITEpiR9XHGViWRZnYzpQGBZFX2fDBLdfFInnSaEQ2cyS5dbGaBPcPHcuaV27smn724zlAsXFxYQ7a76LExwczG9+8xvGjBnTnFVt1QwdOtTlYgFxJ3j//v2Em6I4diKcdNEZlp9++gmAfv36EaPf3RJVbfU8c+xrLrz0OklJSfAn6C93m9gtd2OOiYkhNP8UktPTmbFq31G2/XyUN954owVq3TqJ11hYvmWL60JZr/F0a9TGTmcMEbFx2O32hnchaqUMGjSIoKAgJkyYQEpKCgB5eXm0K6k+QpEvlO4zimupIS1brFarq2XmLbfc0pBqtwp69uzJmTNnXIk7b/R6PTk5Oa7XtaEejSgw9HPTUFJS4vHeYDDUOGT8lClTmDJlSo3bev7557nnnnu44447ADHU8rfffss777zDH/7wB0CM0FUTS5cupUuXLkRHC+/etGnT2LZtW/Wuew3gkUceqfeyzz//fKPLURNItgRocfKDg0WiJSGB8tJS+i1ZQu+CgjrX0wB/BzRdutB/7lxOWiwY33mHXpcoiW2tVGk0vHPDHGYmJxO9dCkfnT+GtqLu9S6Fe47s5U97OhHRbwAbxs2k+6FXiKP194/dVwX/3LqdkbpgFphzaG8zt3SV6sX1RWfYeOI4ps5deK/nVfTfuKLJynqrCEqqqqja/TPXXcajqcdonRjLCoAk1nbow8RDWbwz8S4ALIcdDMmpv+T26ODJ9DubzfUFBxHmqSsXgwYSg3Xo9Xp+MnbBqLfx1bPPUtVpOv/0sfxeXRQRERHY7XYmTJjQ7PVtrQQFBXm4DGJiYijJy8EU346NdCAdMfKTIgUcOXIkZO5uqeq2ajQSItEi0zcCkpOTSUxLY3B2Nr2/+YZl9i9BNeBagQ1+kf9Ha7touNK4LrqC+3/4gW7dugENa9myytgdEBdtiYn+HQmupYiLiyMtLY1hw9wd26OiogjSNK6FnqMBLVtWrFhBaWkpCQkJXHvttY0qryWZPn06//rXv4iIiGDnzp0MGuR5wrF/v/s/uK4WjxqNxuVruWKHfrYCTXGzRz6l8e7a+sQTT/Dkk082eHNVVVXs3LmTP/7xj65pGo2GiRMnsnVr/QZj6NChA1u2bKGyshK9Xs/69eu59957G1wXNRkZGR7vd+3ahc1mc/3WHT16FK1WW+04vRTaVLLlySef5KmnnvKY1q1bNw4frtlXsXTpUv785z9z6tQp0tPTeeaZZ5g6dWpTV7WVsxD6pEIXhCC3C8RMPI9JU0Qf9tGBs6RyChNFdOIk8eSSQA6xZ8uEVCsL97Pyutj9bM4XslSzTfhzFSEuuH2myM+VwP9arfQpLsYQGsrVS5YwsqIAM3jIXkmStx8PEfJN+JxcSABeyT7Nc+fOEZ2Swp758+nw5pskVFRgVpWltJArQRbNyhOM5ULKawwWcle9ASHCBSEgVcSuioRUJ0/TiboQLL9X5lkQJ3LZ8nMWbsGrcr1/Tq5IJZjLhWDWbHFLcZUYGYOhtFKuL/DipIlEd+pElcXCPx2FaCs8Y5Tg7YxKAPY6hWxO6SHiS+ZahzD3T9tW8miQga9/XMchp5OV8ZCbjUuYGx4q6g/iWa8VD5fcLlTE2noG9JHyh16OkI/q5OdQhJxLSagrglJFIGyT3yuvg8X29bLAVC3FDY+D+3Ohym4natt67lQGovGW4nrL8dSxSaJmKa6ajFq2keP0LX1V6jJEcktZVYLWGbvX8VPnLoT37MeO/ZsYXJXvlgiDu6lnnioeijxXiVMMIq4F8rRyRIwtYjmbFV6XG808FCFa1RCPkDF6i3GhZqGreh8aKsStDWVbSsySqC4O9K5jvGoZRZQrLzdwQjcyu/bG0bUPVUu/gqefBmBZ6GCGWPa7hblqDIjv0BlR/u6sKLoMHk7ngQ6uvecbuLqDW4wLdYtxW1qKWxP7iz3lvVvlenqLcjOd7n2VRZ3t+w2irKyMvJgufDdgInpA33sw1sEa9Mm45YWzJNbe+GcoKaG0tJSYMfIoZ4p0dJx8HLUVMa4aX3HyRi1/VR8zB53QU2JfeQLD7/w1ZWVlMEAi6Boha800tIdNYnhtx7138eijj5K+63Pxp6CW4yrb8kVdsW1trHdWj5M3vsSv3qLX7U4SZ0nMnz/flcg6efKkkIqr5Lg//O/HOOfPp2ePHp4XGfWRDvvCW4xb0zFRH7xFuZn1iI0v1FLhmkS5GU4PSW67753k9pcIay9GDQvSgK1K/PnY7faa5bgDJC4OvZMoql+01Qdrh0j08mb1MaCPR/y/ZdO4ONYlyk1BnCfVA6V7moJWq6UgLN5TLqzgLclNTWX79u2YzWZSUlJwzvmze9maYimzefNmHn30UaxWKzpdHZdtaRIFZ8R5ZUSEOIeLSEL8P6obsNb3/8jrfzbCLI5rvV2cf9VHlBsXF0dJSQlRUVGsXbu22kXsQw89xFVXXcXRo3L3SK/YAfCFkORq/pzhTvp9+uKVJ8dtBs6ePesxKltNrVrqIi8vD7vdTkJCgsf0hISEWq/b1QwfPpypU6cyYMAANBoNEyZMYObMmY2qj8K6detcr59//nnCw8N59913iYqKAqCwsJA77rjDry1w25yzpVevXmRnZ7semzZtqnHZLVu2MG/ePO666y4yMjK47rrruO666zyyqAFajnLgAWC/3c7qzz5j5Ntv06eWrkM1EWm3ctvHH1NSUEB4VBTLbryRyrpM7W2Mj3v1InjESAA6frucYaV5zVZ2lN3KQ6s/o+TiRVbZ4cXqo2a2Kt4xxLLFqiFEgpciW7o2DefqovMUnTiCRqPh075jm6SMj2O7MvmuXzG8X29u9OGPudyYU3kcc3k5IWFhLFu2jKql/wVAikukvJ73HL6NFQ6C4jOnaN++7qFPrwQmTpwIQERyB4KDg13TNxqq380+K3fxVE5oArjpECz+r5SLiE6dOvH111+zO0M4DsxmM2Hx7QgLC2OgrrTF6tkWUXddOOujm3FGRgaPPfZYtQvpABCnB6N8KqVu2VJbN6LjmlBM7USSZcaMGU1ex+YkLKy6uL6gfd96rbtw4UJu3Pdvzp8/D9S/VUZ2djZhYWHodLo2faM4Pj6eXbt2sWfPnmrz+vbty9VXX12vVlAe3Yga6L25bLA34QOIiIjweDQ22eIv/v73v3Po0CEOHDjAyy+/7Fff2+LFi3n66ac9zkuioqL429/+xuLFi/1WTps7UnU6HYmJia5HbGxsjcu+9NJLTJ48md///vf06NGDv/71rwwcOJBXXnmlGWscwBdlOh0vDxzIfkTDkXfsdnoVFTV6e4lmM6M+/hiLxUJMaipLpk7FAexJSODte+7h+549/VTz5md7bDwXZ80CwLxpEwsyDzV7HfrqQGlT9nJYErtjYpq9DvUhJyiYo9fdzr333suf20XSoY3m3KaeWA9ASPfe7AmOrn3hRrCr61DatWvH1Z3bYWxz/wINJ1hywFGRZN++fTvm82coLi7GEBzMlzHp9dpGXkoPADqcOdhk9Wxr9OvXj5L8fLRaLRcuuN0F253hHstZnZLrhK3a0LoBGFSeyyuvvML7778PiK5CO3fuZNOpPBwOB5s3b0av11NZWclAGn5D4krGaHRLsgYPHsy/e/7GQ6xptVoxGo306NGjJarXZtBLIlm1Y8cOxo6t+SbA95UGDh06REFBgWsUnssF9fXG/v37cTgcXFfiW/rqCw3ibn/2qUyXg6IuPv74Y3Q6HSUlJW3adTVp0iS++uorVq5cWa0bmtJapy4XEHh2I/IeVSxA6yI2NhatVuvy8Sjk5OS0mu6FJSUlHsJrhYsXL1Ja6r8bG23uNPvYsWMkJSXRuXNnbrnlFs6c8dX+W7B161bXnTeFa6+9tt59xQI0DVUaDcvmzGHAzJlMv+Ya3gK6+2G7fS5eJOXzz3E6nUQPGsRXAweyt39/opOTqbrxRn5pRJPWlqYoKIjv59xIUFAQhScyeeDHH1usLguB+f37ctvdd7Py+uux0vpGE3ll6DhCwsII0el4SCqre4VWyiRbNoXHj1JUVMTbXheul0qGIRpT5y44nU7mXtzh1223Zq46I5ItBoOBEjQcOCC6Z+xJrvvX56w9hMh2YgSImRePNF0l2yDa82LUEqXZ8b59+/jpoOdoOescSQQHB1NZWcnVV1/d7HVs7dwdbmPhwoW8+eabAPTo0QODBkptcPz4cZdAsLKykkYqIq5Y+vYVLQ/sdjulpaXkBcdhNos+vac0oa6htr3PFQN4EqSB4uJiTp48SWpqao3LrTlfwGeffUZcXFzzVa6ZGDFiBGVlZezevZvrr7+eBx54gMFS/ZOfGgl27tzJ2qXvVxuFrCaU1lgxMTFtegS3AQMGEB4eTlFREXv3eorpG5tsoRWegzYLrWg0otoICgpi0KBBHgJ4h8PB2rVrGTFihH8LayTXX389d9xxB8uWLePcuXOcO3eOL774grvuusuvcu825WwZNmwYS5YsoVu3bmRnZ/PUU08xZswYYe4Pr35BcuHCBZ99xdR34HxhsVhc476D28xstVqxWtu+FNFotIPBKvp9S4ATgq12DBoHekCLBgktEjqcBOHAgA0jVrsdHGJ5JESqTovYjh7hOAgGmxFsGrDbRKs0h/wAsDmdLJ08mfju3bFarUzJzKS70ai0XsMO2ILBJoHV3SrdtW3XM6Ic5OXtklh36rlzvPrjjxgnTGB3YiIxOp1LwfJxQgLxubmkaDSe5cnbsAbJ2zeoygyS902nemjlh0aOg/J7r+46qpEfyjp6sNpFha0Go3sfHODUinjZbOKh1M0WDP/uM4SE2FjKSkq4b803SAYDNqOIjfJcIwbAagVVeR4ox3JwDcPjINweShl2I/wuP5vPq6qIbt+eZ3qO5t7SHaIuqrStItl3asFul9fXgdWOqEcQYicVZ0sQ7jgrKMeZDXe8JTydLRZcn5XNCj9Gx2IYJO6aD/nlB3SGIE+FqdXqua/e32X1PIOP+fWhvtu3WiFI9bkE4fkZBcGUbSuZklmMzunkkS7BJBvEgeZxHCmx8T429YgYGlTT5O/O0o5DMABFJ4/TS18ptqN8JkodvI+NmmKh3oem+G1Uyg+qZfu+lvGuV5CR8fkX2VBWRkhYGB0e/jNK6lWX2pWSkyEYJafbi6OMPqkXr7/VdUGSJIqKikjV2sT/gMErNkoca/rcW9l/h/JfZlV/L3zVP8hY/fug2teOBefIYzAajYYtW7Zw4cIFTpZBpS4YbZAEVis/B6cBUHnuJJIkiWMOPD+v2r5zyvzajoOWpq59qGkZORZPyx4h6z/E+5Gd4iiN6siGDRvIzs4mIiKC8PBwrHqv2Hm/b0y9LgGP48hf1LfOBqP4jnrHUxWbOXPmsHjxYt58803GjRsHiK5FVquV1SZxXBYVFZGYmOi5D/pGxk19bHsf541FHY/Gfp7K/njHS406dkq58vKaoCCgCovFIuIUZKy2naqqKtbKN4MnTZrUoGNCWdYWbMSqnEsp33mDa6F6b68a3p+LDawSDYplWloaixcvxul0MnXqVEwmk/g+1rQNrxg5dAbAggPJMzY+Yglw6tQpVzJwypQp9YunwSjOjXXyeaIVrEoc1ecbDY1lsFf8ZI+cVZLP8+oRxzFjxrB7925+kMXLFRXCTK1Otrj20VdM9EZsNhv79++npKSEX3XIrxaTy+EarS1RVlbG8ePHXe9PnjzJ7t27iY6OJiUlhUceeYSFCxcyePBghg4dyosvvkh5eblrdKKW5vXXX+fRRx9l/vz5rmNHp9Nx11138eyzz/qtHMmpjOvWBikqKqJjx448//zz3HXXXdXmBwUF8e677zJv3jzXtFdffZWnnnqqWrMmNb5EvCDGuQ8JCfFP5a9QVq9eTVxcHA6HA4fDweDBg/1eht1u57XXXmPNmjXcdNNN9OjRg4MHD/LZZ5+RmprK008/7dG0uLWyYcMGXnrpJcaPH8+4cePo06dPS1cJgE2bNhEWFobNZiMuLo6OHTu2dJVwOBysWbOG+Ph4Lly4wOTJk1u6Sn7hf/7nfzh48CBz5szh1ltvveTtWSwWdu7cSUhICFarlSFDhvihlm2HTz/9lEOHDnHddddht9s5fPgwJ0+eZOrUqbXeaVy1ahWJiYnk5OS0ydEgmpKsrCw2b97MqVOn2Lx5s2v6Cy+8QKdOnQDxv2swGEhNTQ2MRFRP1MecwWDAZDLhcDjqfUc8gJvz58/zm9/8hgULFpCWlkaHDh2IiYnh+++/JyEhIfC9rgdHjx7liSeeoG/fvtx///2uJICaffv28cILL2C32/nvf/+LRtPmGs/XyXXXXQeIIZjnzp3boHWPHj3KY489Rnx8PP/5z3/qXH7Dhg1ERkaSl5d3WbS8+vHHH13dpyoqKjh//jzLly9n4cKFxMfH88UXX/DXv/611m2cO3eOBx54gPDwcFe3SzUVFRXMnz+f4uJiD8nr5UBJSYn43j1WDIYm2DdLCfwzskGxW79+PePHj682feHChSxZsgSAV155hWeffZYLFy7Qv39/Xn75ZY9RvVoD5eXlZGZmAiKpqkjV/UWbatnijclkomvXrh5ZNTXKiYqa+vQV++Mf/+gxDndJSQkdOnRg/PjxxLRSV0V9iYzMhLA0mA4MAaZXERFdyLCgX2jHBbpxGBPFdOEY7bhAx7wLcBFh8M9EjG5yCjESSoE8PRcqK6CwHCoQA8mYcbdIUwZJWdm/P3HyCY3m22+56+BB141kYzB4NE7ydWQeLobukdWndRHTSkvBXAnxcZCS62QnuJJjaYcOEYO4U/DWwoX8n0aDRpIIRjX6D+ImTrBOmNyDg+TRbkLlRwjizne0/NqAGLFIyb9FyvML5PgUIizAJeJhtRtZ/dd3GP/QndhKRDNmZQQiO2LEpqhgMe2Uw8Gb5RYcQJ8NG3j82BZyL4p9q5HjxeK5i1eM4oEtxdWXVy+nXsZ7eq7napOdTh4fP4eozl04sWsHC//f4yKWEVB4EcLkeFRWiVGJABRfsV5pzaK0SgnBc3gq5RiwIA4mG1CF+0OSD6rKEgiOQ8Q2FD6O7kT85Bux2Wz8asMX9PrhXUgE1nsdM4dVcfCO03EfMboUlO23A37y2rZSpzh53rhIUBrcHS6G3mJ+lWRnnlZLduZxRv3PAiKdNqwGI6uffIdJf7wTvcEs4lIORCFiprRwsSDiq4jPbPB+WBohE+dQXlbKE9++StAWed4FRLzWyHVR/Mv7fcREXVclxk2NUmZt5Sn1jvVapneka9+cf76Re7LFSYTFYuH7L5ZS4oC+JzL409Ag8Z2NQ3yHZZw2J8vTZuFMSGDUvnVMPb5EjIyxSy6jn/xZ7vGql/wZ+oxhK8BqtbJ69WomTZqEXi83K5sox9lXnb33c1qkKw5PP/20q7n7uHHjGDx4MI4NHzN16T4cTicLt1dSbBPDFw+8bzz84rWNdsC3Psq8VvW5f98641iNfpHVjwVvBka6jx+FoZHuuIyJ5OTN/ye8QjgxmUwA3LnjZZJ2WWBZsXudROCrGsobGFlzbP2Ez+PIX9Sn/jMjq+//mEj3b+64SDLfFl4Ne5VosdyzZ08GPXw1G0beJ4oZONBTPlpXXGuqK4jPtabfhEtFvV11eQ1hprxeTfumjh3APLF8u8fWEx0dzZQpUyg4uJt5f33Gc70bIvlFO5oHH3yQwsJCpk+fXv86jYzEfNHIupfeYdKTd6KPMov/pYuI3+M1foyjOob9IqFctJLVd8D3eZIXM2fO5LvvvuOJJ57w7E7lHTeFcZGu/6P4V25ixIgRjBs3Dp1OxzXXXOMx3xtl1JSEhITa5bg3REImFJ4T567hyjlpBOK8oLb/84ai/l/rFynOwbQihna7fF5Ww3Gv1WrZuXMnIM7N09PTmTJlCvHxYijNp556ynM/vWOzIJKjpaKdvLay1GdM8hsxyEaAxjNu3DjqarPxwAMP8MADDzRTjRpHaGioq8tpU9Cmky1lZWVkZmayYMECn/NHjBjB2rVrefjhh13TVq9eXWdfMYPB4NO+rNfr/X8y0cyYzVrQ6cVFqxPQOgjS67DqwYYTJ3ac2NBQhZZK9Dqzu7uME9E1QckMVCEu6CrBZgat2d27RnkgP2/o2pWgSZMAqFi9mtt37nT1fgDQOcEjtL6OTL0eKs01TtOZQVcJ+kpoVwl/BrJloVnHsjJeAR6KjqbrTTfxxd693LR5M1p5l7Sqh05OtugdiCEIvbsQWXF3b1H6SSE/a+Rnm7ycOk5y/yBdpRnkPuPYwGlzr65zgtmu5Z+DhmDevp1RTjuP2mzoK23ozGLfakQJoHeMLHgFl+rLqZfxnu6jzIU/rWRZyv1EdUrjk/adue34QfRBUFEBUfIHb7OATk6yKKMVuuJpQT7+3HFxdS0Cd8zUQz6rki02M+jlulVpNewePJ5IwLlzG/2LL7q34X3MqOPg61jyJ8r2fcXfe16V2R1nVZ1viIN7brudxI6pfLD+Wx4+s91dXYsZPfJJqQXP40xJtigxk5Mt+3r1xgToM/cSajGLdeyIsqtqqIs36vlVNSzjb5QyayuvpmUqza5p14ZWERok5lmtVq4LhfdKYVWJHV2VGcmC+zsrc6QMlnz8KZEhRs50rEIfb/f8TC3yZ1nTZ9zK/zM8/teUGPqqs/d+VpldcRg9ejQrVqxAr9cTHBxMWFgY2SHt0Ff9wr4KKLZBqAYGDRqEzmKuvo2aPte65rdG1PvXkGXU0yxmevXqxZYtWzAlJLJlyxaSkpLoaC8S31f1sWel5vIs5maLXZOcH9Wn/tY6YllldrVktdrcYs5sq+RqnXHttdd61r2uuNZUV+R1avpNuFT8UYa1jvW8j015+dDQUI9RYKp91lYzFe07EwR07969YceCxYxV/k/RW8zoq7z+1/wZR+8YVopy9DWdJ3nx5ZdfUl5eXl1dUNP3vso9PchuQavVYjAYsNlsIkZVvtfLzc11aRPeeuut2uNpFb+TOrN87qqcfBsQ8avt/7yhVPqIn3y+Idlqj+NVV13FL7/8glY1Wmjv3r1dr8PCwjz30zs2VjMGmxjZqEdaJ06ePFlNwtzWr9HqhXL90RTbvcL4f//v/9U6/y9/+YtfymlTyZZHH32UGTNm0LFjR7KysnjiiSfQarWubkK33XYbycnJrr7PDz30EGPHjmXx4sVMmzaNTz75hB07dtSr+V4A/3EEWGE0MsXppGDnTu5VNTVvSuYA/8zJQZeURK8LF4gBfp2aipSQgGPCBDafP8/EU6eapS4N4d9jxtLnqquISE/n8c/fR2eue53mZkB5AUs3bUI3bhzHJ0ym8NRxXi2r4jE7LKmCm4Pq3oa/eMseQlF5BUEhFTxy4KfmK7gZ0ErQ8eQhLB1TOd1jMI7T2+teqQZybfD9jgz62eG3+bv9V8k2hFELV3UVnobIyEgGh8AqTThJ6d3Y7DzFaNctQDerZQfBQMxcXo2S/cewYcPQaDSkp6dTlH0OAHuMGB57XVACyck6epZmu/rmB6ibwYMHs3nzZiRJYvPmzcyePbulq9RmUS7u2suS/MLCQk5XSGzYsIGOHTvSoQ3K85uboKAgl8DUpqn+Pc60h2JKbAcIv8jlikaj8emIrNe6knvI57paAyxfvpzi4mIcDker6UJ+qURERFBaWupqqaemuLi41tFlFSQgJSWF8ddOYffu3ZfdiFcBmpcvv/zS473VauXkyZPodDrS0tKuzGTLuXPnmDdvHvn5+cTFxTF69Gi2bdvmsp6fOXPGo4/oyJEj+eijj/jTn/7E//zP/5Cens7y5cs9MqkBmpY84PdA7p49ROfl8VR2drMNgaUBHvrwQ8okiVCbDRswd9cu3khJIaZ/f87OmcPZN94g0Y/De10q6xOS0I8eDcCAnTtJ0YhWmq2ROzdt4oUuaezct58Qi519iNY5v7douMaowdgMafJSJzyVXUbuf//Liykm4m2WuldqY9xxZg//sk7ElJDAV5EdmGapnhCoDx+VwKGcI4SdOsJI/6uS2gxpqpMzkxbmzZhCVJeerN+9jtGFG6otv6ZcNL+a5N8uvJcVqampFBUVARCadwratScyJo4cKZjTPUZzz3V9YPu6Fq1jWyMqKorSokIioqKJi4tj1KhRsOy9lq5Wm0RJ8p06fZp+JhO//PILpfnlrDu8jvvuu6+Fa9c2CAoKcrds8THk7ip9Z0BcNLcGj1trRIM72eIeUcc3X3/9NSBGS7mcqCnhXlpaSlhYmM95ajSSO1HVhpWjl4aVphlL+Ap0C2dkZFSbVlJSwu233+7X716bSrZ88skntc5fv359tWlz585tsMTqckWSnoLwJ2AYotnHYAtDk36mA2fpyUFSOUU3jpBEFp3OZsMZRH/6M0AWcBLIR/gMiqEgC0psIhlgQ3haFHSAQ6fjKZ2O3MpKOgMvnD+PYrxJiJdfJFHNCUI8kFHDj+h5r+nJkusojoiGiPNOSJBIlrefk2unSzycV5Vx+7ff8n67dpgSEvhmzhzuevddcDgwIyc2bGC0QUil7HIpEe4RYzDC3VKAaJ6pKD9iVNOR9ydfDkouojlsMVjlepZVCJF7aaW7l4dRB5XoWDftOkwaDcX797HoxEGsqli5YqaQI8ciQfJ8zqnnH5B6ub6SajvAXqd7uoIysJesQeoSbWPkZ2/zagkcAq4a0I+5XbqSnp7OSzt+4dEf12A0gNkiRiKKCAVzuapLkRbhGlG6aIE7s2ST5ymjD+lUr8NEvLDBC3bIdUC6Fu63FkEysN0pjossxLP6mElQ7U9949RYlO0PkKp/NudV85Ilz2nK+yTxlGCrxHp8P/oeA/ipyyCm5X4vZhgQ8ShGHIu5CJ+Q4nBR5sv5pyWF4vl25XzGBpyVyznthCESdJTE9++0j9io6+X9PWxqtsvlDfERL1/LdJTc+3DaCSPkaUnQf+pMdu/ezZ49e7jqgx9J+eejlHbpSV5cZzgiJ1tsQIaTygFauv/qUaJzcxl7991w+ygx/6gT0uR6ZHrVo6PkLretsVWusxwr13tw72eaJF6vl993lRieFMzu8X8AwFBeRVFhARFR0az/3RI0P/8MQJ87H4SeEhyU1xsnx+loDXFKk4Svwzu+rZ3MWo4NhaNO6CrHWInjQac7PhlOGCDhGPcwIBIvI9/9lXBarZKXV8fSm651xLatcNQpjpNxkjtO3qyR45YkvwZX/MhwwnYn2v4iHmvXrqVfv34AbE0fCRe3eHYrnyj/b9QUV1+oY50mub8fTYH6O9gO2CQfRw35nNc4xX5OlNzxUqOOHcAKJ8yS0Ol0rpYt9iCv4Q0nS5yMuY5waJhYsqeENUu43PRqHWI24n+oKeKojmE80A702YjuMMkS9MB3XOoiwwmTJff3U2G7iB8rnGhe3IvznnsAOdkyRHL/b6koKioiIiKCwYMHM23atJrLnCzBMTDniHOs6EjEuUA87vNP8O9/kbIt5X8uTxz3ervs5KtEnO8k4fM8Pjk5meLi6k4Xo9FIcLDXcaWKHQBfONFMkVxJFp8JqzvbN26/2hKKh68pthuAiIgInnrqKWbMmFGjpqShXH6q8ACtAgfw6YwZjLn3XjrHx/MG7txESxNutTL+00+xWCzEdOzIh2PGtHSVAHhz7FhM8fFUlJVx/48rW7o69WKeAfrqRI4keehwevXqRVBQENqhw9lvimrSsrN1wWwZMJbg4GD+FgF6qe512ioTz+0AIKR7by64xDb15ydDHFEjxxEfFcXNdd88uqy56qqrePbZZ1mxYgUWi4VrKk8AEJHYnhzJs//bWmMyRqOR+Lg4hg4d2hLVbRMkqMROxRHRkCe6Eu3YsYOwyEgcDofPEQsC1E5Sksi4Tpw4kR5X+Pf2UtDJ/w0Wi7vlY2lpKUajsU6HXwCBRqNxtWxxOBwerQqcgCaxE0CTSibbOhqNpl4tW7799lu6du3K2LFjL5suRAr9+/f3OT0mJqZevhWJ+nfFChCgsRQXF/tMCjaWNtWyJUDbYfnQocT164fD4eD3ISG0tkalPQoKOPTNN1TOno1x7Fi2nDjBSHk0jZZgXbtkDKPEXfPUr78m2WJuEy36NBL8PUzif1P60K6d6K9dVVlJUHAwyyZcw4hVnzZZ2a93HcGI4WNJ79yZOd/9t8nKaQ1MqDzPypwLmBIS+SS8B12BE7oQOtsq0VP3Ccfq5D6MG3IV/TskEL256T6TtkBUVBTl5eUAdO3alc7OIpYUFBAeHc3KyFTu4Khr2e3RaUhA1dkTAd9IHRQVFWEymRh7aheHEtphpq+rWXhJbjZRUU2bfL0ceeSRR1iyZAkpKSlo9z7b0tVps2jlZIvSMgNg9uzZmM1m0tLSWqhWbQutVusRP5fgFdjvjCI0IhK73X5ZDFHcVNQ32bJ3715CQkLQarWX3fDZw4YN4/vvv6/eioX6yW01BLoRKQMeNMl2rzBefvllj/dOp5Ps7Gzef/99v7qnAmePAfzOjpQUDPIQz7bVqxnbCiW0ADfs28fL6elUmEx8VlrKe9CINgOXThWwcfJUojUaivbs4b4jR0S3pDbCVL2TH+VEEUDHb1dw/vq5RPXowdodSYw8n+X3MrN0RqwDhmMABh7YhuYybtUCIqnV7mQG5oQpHDLEYTt4kN2zf0vp/t08t2V5res6nFCe1osIoMeFA81S39bO2cFwIRs6dxaeAd3ZTIiO5nB8Z6h0J1uK2nUmCuiYfaKFatp2+L//+z+OjUiib1URksnBF6dOuYZGNeadadnKtVG0Wi133XWXeLO4ZevSllFOdJWWGQoWi+Wyu5htKpRky1dffcWbb77pEbcdOWV88skn9OvXL5BUrQWNRkNpaSlZWVmMHDnS5zIOh8OViKmpFUhbxmg08o9//AOAGTNmMGjQIECMLhsTE1PbqoCns6Uu702AAHXxwgsveLzXaDTExcWxcOFC/vjHP/qtnECy5YriergOGA0x08+TpslkGD+TyikGkEEqp9yulsOIvsvHEP6RXPGwFsP5YrenRRkFWdFu5IWHk3XjjYRotRTu28fjW7eioRZHSy6X5n+oxeECkOD0dLi4VsuF8GC49+uvmWa3c9bh4O/A07j1IIo+RFcJIYDRAsZKEQ9jMG53i+LHUBL15QiPRjlQKTwlVjtUykl7qw2slcI7YrXBR3r44IsvmHL11fxp3SpXrIze+3opjpYEr2yE9zp7Ve/V/hb1sn0ll6/F5XVJkNBIkHL0F4oTZgAwJvsMbx/YS3if/nw/6momr/kAdOLY0emEwyUiCSgDaznolSG1K3HHsBLxQUTi9pKEidfWfHh95AgMBgNFFy7wpzOHIUvuZ97SnhZfZPioi9KfWT3P20UyRN4f2d0y/8geBvx8lLzCQm4LM9G5c2fCe/enJOsbIi7aRIJOaSVvwHX3Y2NwIhHRMVitVuZUHHW7Wk6ryqnJ1aLUQV2vlmR7LfHyXkbtTlHcI7K7pf1pJ+3B5T7oOv0Gzp49iy2tJ3y3Cg46yR0YTOSMxwGY+Mp7nm4EX16Gtuxq8UbtufHeH8VJojgjZK+GsV8IfeX4XDVOYsaSJdx///3Ex8fTtfS0p2PE21eipi7fSVvA22/jC18+Eh/eFpcLAurva2nrrhY16+U41bZfB2UPiTouqvhpt1aCfCddZ7Ni04k/4+joaPc2JkrubdWHcfJvY3O4WrzJdMJoVZm1fZ980Qhvi3aAhMMBu3btolevXu7hewdIbLwIh7MO118oOVESojeb7Gpph9t5t6e4eYZ5z5R/47Jx/6aFIurVV/I8J2oI6ripmSWhee4oR48e5cKFC7yx6z8+fS1btmwhLCwMm83G9OnTay4nQXKdWwJExCOOgSz5UZN7zV+o3S2KXydNEudqWrCeBH2aBOlU89h07NiR06dPs337dgYNGkRJSQmLF9cvmyy9nYXjmmsAHy1bhkiQVL3FzGWHjaaRgFyBQz+fPHmyWcoJpPQD+A2bJPH97NmEhIVRmJPDvV991eoPsGirlRcdDjTACuD7oGYcsxiR1/q7FQoKCrjm+89JqmyF4zzXg3l5osVEeWkpiZVmFuzZgN1ux9S5C1/HpPi1rHOGEOz9hwEwYPs6tNJldGFRC+3tlQy0FwJi5DWFz43pta63NrkXABUnjhFNVdNVsA1z7bXX4nQ6CQ8P58WEgfz+97/nxY7j0Gg0lOTn06tXr5auYpsiQgeJiYnEx4vM8XhroGVLgJZD3QVQ63BfUfTs2bMlqtMmUbpiQfUWQuvF3xLjxo1rvgq1QZTWQLW1yFAG+igvL2/0ENOtHaXLudJtyGqtf6d5jUbD6dOn+fDDD5kwYUKT1C/AlUVRURGLFy/m7rvv5u677+aFF17wq68FAsmWAH5k6ciRxKSmUmWxMPrTT4lswA9oSzIMuEevZ/LkyZx/4AEu+uhL2hQ4nPC32FjMwAhgQRtOyCc4Kpn3zmLuWfofgp0O+lUUUrkvA4CvOvbGn11rl/QZSpDBQNGFbG7NOuK/DbcBbpRbaGdmZmI2i8Tc/sSaEwEOJ1Skifk9TwW6ENVEcnIyGz9YwjPPPEP+6KmEhYVh6C/EmfpzmS1cu7bJqFGjeOedd9j+w7d0lCpaujoBrmDUXV5skvt1IDlQf5TBnlNTUzl27JhLNnxUE0bqsHF07ty5xq4xAQT1Sbbk5+cDbjn25UhCghja8vz58zz77LMsWbKk3usqXbGOHTt2WceoVqxN+LjC2LFjB2lpabzwwgsUFBRQUFDA888/T1paGrt27fJbOYFkSwC/kAG8tXs3x44dI+Lbb+lVUFDnOq2JXzuddE9LIywigk9l30xT83nX7gz4zW+YOnkyzwBSG/eOdK0sJb2y1PX+rr0bWPrRR7z1zUrWWWpZsQEUavRYBolRYXpn/HTZu1q8uS4Kbpw7l4ULF/LNN98AYOzYlUKN72bXG0MTiYiOxmq1MvviUZ/LBBD0yjtNZWWlx4nw4cOH6ZUdiFtjeOWVV3jIdoan2d7SVQlwhSNJkqvbS8i6ZQCUlJTUyxERQKD8186dO5fly5e77vz+FJnKuHHjmDlzpkuIHcA3Go2G9PR0fv3rX/PJ4Jurza+oqKCoqAi73c7YsWNboIbNww033AAID9Bzzz3Hhg0b6r2upDpRvmIFuQH8xqJFi5g5cyanTp1i2bJlLFu2jJMnTzJ9+nQefvhhv5UTcLYEuGRKgScRzR7LPvyQG2h7Xf9CbDZ6f/UVZ++8k+gBA1i7fz8TMpvujnaFVsuxSdcQIUn0t1fRtclKajm6VZYyvriUA8CzJTDGD/bh96uCOHzkCJ3at+d/sg9d+gbbGDE6aBcZhk6nw2g0cu7cOfLz8/neZuTmoOq3JTYGxVBVVUXFuRNEOa7A2xYNYEgwUAy5ubm0b98egKVLl/J62hWo6PcDiYmJPBIPhNP2/hACXHZotVrsdjtFse3RAlVVgS6VDUFp2aKMSKQ8nzC1JwgIauYu2G0RjUaDVqslPDyccmv1URC2bt3qGn3sySefbP4KNhMLFixAp9MxdOhQunTp0qB1NRoNERERpKWlcfjw4SuzK6Cdphk56Ao81dmxYwdvvvmmR1dTnU7HY489xuDBg/1WTqBlyxWCJD0Fur4wHJhuYajmZ4bxMxNZw3jWMbJiK502Z8MPwI/ASmAtsAHMG+D8Dth7BvYVw3lEgsUMOIG32rcnB+gI/BPEqCfxkBwPCf0hIQm3QReE0FL98Cfe21aEuUl4yFEVYa7ynBAMk8+epeznnwE4NWMGVr3eJaktAfJtkFMOBeVQUAzmfLDmImRkZxECljMI6W++EMKWlAiBmc0mZLgghLCfjBxGRHQ05aWl/GrrTy4pbkSEal8SPOtMgiyuzXF6PrwZILmXrWkd9Txv9taw7F6vMl2iXs/VjWVOcXFlg0Wh4kfmR0cQuyNDsdmEyK0kC7Cr5Ljq4wNclmLzGYQ4LxjMefBCTjkrVqwg/aPX0NtUx49alnreWfO+tzTe8Rugir/6mE2WhDxP2T9FeAdEnxUj41xzzTV8/sknfPnll/yQUyLm2xCSXIt4vSrjAM888wzd1n8j5iti3NNOIbZTS1295bPeMW1tqOM1xMdx7Gs/t3pNU4lMB67Zx4QJE1yJls8//5zUIDuR7XDLOdVSSgW1SPZykOMqqGM1wiu+agEsCDHnUVnUqdBTFkUqiRa1HPeos7rMUx3btizHVaNII2tivSomCookF9yiUrgy5bgKvo4vb9Y43aJcBZVcWDmZLu/SjxUrVlBWVuZeTom3L1msL5R6xNNyx+wmp/v4OurjOKoLZV8n1rCOWswMaLeLKzHFr2G1WmGyREXH7oCQntaLnm45Lum4/5f2+NeRUC+2qmIYL9clGCHoT5Zqjk1NrHL6luOuENPUQz87+wyvttjatWsBGDt2bM2jZHWUKNALOa4xWJxPRZhV5wnK/5AP+W6ToBblZjrFZ2qXxceVwA48jiMQrVPmz59fv0TLCqfH/7tGoyEhIYFZs2axadMm93JD5HOmd85d6h4FuIKIiIjwcCAqnD171q/OpECyJcAl8X3v3vS5+26umzWLl2lTIxb75I4ff6S0qIhwk4kPxoxpkjLOBhsxj7oKgJTNazHZLt8WB511cEf/XixatIhPh0y6pG19boVTdojTwR3BV+6Qf8NyTwHiTuLNvYQc9zuz8LOoybPBtkohM5xOGQFqp0ePHgyWh6EEsJjNrOjbghUKECCA31C6EZWVlZGRkREYNraBKBf/6mSL2al1dR0aNmxYi9WtraBOtvg6/hQ5bkD8WjOSJNU69PNXX33V3FVqfmxN+LjCuOmmm7jrrrv49NNPOXv2LGfPnuWTTz7h7rvvZt68eX4rJ5BsCdBossLCME+dCkB6URGD6li+LWCqqqL9d98BYBg5koPqoSH9xEfDR2IIDqYwK4tfZ+7x+/ZbGzdWFWE0Ggnp2YdjIY3LFDuBlcOuIjExkd/Gg/EKc7WoGV3uHjs9LjKCMA0Ql8gWW4THcqsqdTiBvkHQPtBhtE60Wi3mC2Kc66+//prXrJn0CigIAgS4LFBatlRUCFmzweCHfq1XGFrJM9myiQR0Oh2VlZUMGDCghWvX+qkt2XLx4kUmTJjAvffey5gmutF3OaDRaFzJFm9ni9Vq5d///ndLVCtAG+W5557jhhtu4LbbbiM1NZXU1FRuv/125syZwzPPPOO3cgLJlgCNwgGsnzYNY0gIhdnZ3PLTTy1dJb8x5cgRCo4dw2q18k5sLP5sjHnixAnCBgwEYPD3318RwxZfU3SewtOn0Wq1fN5nEKf0oWSYGiYmXB2ZTPdxV3P33XdzR1IbHrbJDwQBeXl5AEw6d5g7Z8/ivvvuY22s58luxogZPPDAA0zvdTkagZqG8EKRyIqPjyc08O8YIMBlg9Kypby8HAgkWxqDd7Jld1AyAJWVlTV3ewngorZky9q1a9HpdERERNC5c+eWqF6boLYYbtq0iUmTLq0FdZvATtO0arkCnS1BQUG89NJLFBYWsnv3bnbv3k1BQQEvvPCCX/8jAvc7AzSKH/v0Ib5HD+x2O4OWLyfoMmqSqwFmfv01s202iisqmARc5adtr1u3jh49elB15jSTTp92eTgud9J3byevY0fM/QbwUtdumBLbYfvwDSYUZNdr/R+6DSUcMB/eT3sqm7aybYBRo0bR5fWHGVyWQ0bpWQoZQFG7dP5i0xJRWcpD2TvQd+xCRGgoAyQ/DQV1BdC+JJcc5GTL4ZauTYAAAfxFoGXLpaPBM9mSHdaOSMBkMrVktdoM6lYZ3omCQ4cOecwP4Bt1NyLvWG3fHhj5LkDjCAkJoU+fPk22/UCy5YphBswDrrMwNOlnxrOebhxhJFuIPVwG2xCSst0IydYZKMgVUlgz4mFDHDClISGYp0zBCNg2bOCanBwh6opAiGhzvYpuSbmmUvYAyS3LjQcynEQMkCDLLcmNiBBC2+QICMktYT7wGvAsMB33MPRGwCwLb/U6MNpApwO9fO5mtYBZvr41y3kBvQ72Ohx8/fXXrP7+e34IDapZirtXFrzm4Ba81oa35NZLBuwTb/Gusp4vuZu3ENd7/b5yXZXpanGuPH2h8zB/q6ggLDISIiMB+GbIVVz17afowZWpt1pAHypeEwZGHZzRh2Ds3guAaw5uhyLEZ5iL+EzP+9iXppLh1hWr+pKhOi4TJM/t+ZL+ghDAZQGnijGuXEm/7BI47WRyegQfDwdTu/bQrj3lwJffFmAMDaWyspJrNh+G8bLq+bQsm8vFt8zVu8y2wHl5n7yPBQW1wE957WtamkT3t9eRs2EDnTp1Qnp0H9zQR3w2R2WJYjs8RZgd5c/uchLjenPaKQS5IyQhlFTYpJLkKjFRJKaKsLOnHB9FxlmTwHW0fNxdLmJcNZmqY2eTj/1b7yM2iiT3oJdwsyY57jj5t+FylOOq8T6+amKi5Cm7nSyhzRcvq7VsqU047I1ajHuW6r8HLYFyfGXW8R3zxRpn3RLYyZJLzqw1hrJz505uu+02UhZN4fuMfPLX/czrr79ed1kTJaw75POkMNwxVH4/rS3orsuUf+PsuOOZjhD5eh9L9WGAVP38YIUTzRQJiyWRCxcukJ6e7jG7pKQEk8nkErT72qb5mBhwIToSIUhMQnzv1f9jLYEi4+0oi4bzZLF6MOJcbscl1nG7E2ZJIoa+ElayHDd70SKCg6+Als9N9VW5fPWRHjzyyCP1Xvb555/3S5mBZEuABvPNpEnEhYRQeOECv1XbwC8zfgt8DGjS0/lQo+HGI0cuaXvPyicTM7HTz1yGyDJcGYRhQzq4BwaPcE/rnI5Z0qKvo+3ie50GotPpKMo+zzVF55u6qm2OrlWllBQWEBHl9gtt7nsV0YDlVOaVcfLhJwYOHMiGDRsAXOLHAAECtH2Uk91Ay5bGo9VqyczM5OTJkxT0mMDhnz7BUZbD8OHVR9YJUB2NBBcuXOD111/nxRdfdE23WCyEhorhJQKi4drxGNFJ1bLF6XQGvtMB6kVGRkazlxlItgRoELuAtUeOMLlTJ/p/8w3Bl1H3IW8igPv79CFo9mwulpRQlpmJwdY4XfePCe3IDo9Ec/QojwXroOrK6xw54cQ+NquSLVarleWRsdxWllPjOlYkinsPJgxIO/5LM9SybSJdOA1yssVisWDWiJ/2zuePtWS12hwREREkbv2cMm0wqU880dLVCRAggJ9QLstOnToFBJItjUGr1ZKUlMTFixe5mNQdSaslITaWxMTElq5am0CjakSk7ka0efNm9Ho9lZWVDB06tAVq1nbQaDTk5eXx2Wef8eGHH7qml5SUYDQaMZvNLVi7ZsJO0xhXr5DLknXr1jV7mYFkS4B6YwMWA5mHD9Pr6FHuvYwTLQo3HjrEB8XFhEVG8snQoSzcsqVR2/lpwiRu7tyZ06dP02n5J36uZdvg6sIsXtqxg8GDBwOwePFirsHGbZE1r/N5XBphJhPmigoWFh9oppq2PVIKz1CIEOSazWaSk4W4cGru8ZasVpvkV5X7W7oKAQIE8DNn5CbyBQUFQCDZ0hi0Wi2pqamu93PnzkWSruChARuI+vpY3Spjx44dgBANKyLnAL6RJAmz2czBgwfp0KGDa7rdbufYsWOUlZW1YO2aCRvQFF+7K3Do56effpqEhATuvPNOj+nvvPMOFy9e5PHHH/dLOQF9eIB6s1yjIROIBO6/AhItACE2G9FyFtQ+ZgyFjThB+ykpiejOnbHb7QwZMsTfVWwzaCQ4u+obnnzySZ588klsNhurbVBUy6G0lmBKS0uRDu0l/Er8J6gno8pOu16bTCZKS0spuJBFZ+sVcOIRIECAAA0kkGxpOFqtlqioKNf77t27k5aW1oI1altIQFRUFA888ABLlixxTT9y5Ah79+4lOjq6xnUDCNSjXqkTVtHR0axevZoVK1a0RLUCtFHeeOMNunfvXm16r1696ueiqieBZMuVQqeBcB1ck/Q9E1nDONYxwbKG2O/KYCXwI7AW2AQFO2BvFhyzCY9mKZATGgq//S0DBw7kD5JEN6BHvJDLJiRBRDSinVQuQlKpPC5FIupPMlR1AiHTzEWIVFUyVUVWmxwP4cGQHg/3Ht5DcV4ewUYj3w4ahBnhkbIBJTYoKIeCYiHXLSkRr82V4qHXwU+jRwNQfmg/SUlJhIf7kOIqdVBLcb3q5l5e8nyol81pRMzV5SRIQu5W23KKBFeRxe71mq4sq96/LBGwl2UNxkIj9NaJOC43GITszQ76GFxy3JyL8P7O/bzwwgtcv3Od2IZ69Cbl8xzgIxb+oqZYK/NqilV9yXD6FhCDe/+S5XK2y++7y02B9he7hHQD7AWuO7YAubm5tDu8W8j+Osrb3ap67S2qS1YJZtuSHFdhuypWQ2r4TE7L+99Rqj4N3GJEJaeniCbXqwSUasGpIvzb2gbj1VCUfezoI7ZK3BQUiSkI8egapxC41iTuVNb1JY+9XFDkpWm1/F6o4wZuSa5CbXJcZf0rAe84eeMtM13lOy4Gg6H+clz18as0tff+PWhJlO/gaKnu+Hizxus4U6PEbrKYr9FoWL16tcci6pYuNZIswSH5/z0d929sa/r93Kr6jmY6hby3E0KSW1N8aiLD6fPcQOlGFBsbS1FRkWv6+vXrWbZsGSNGjKi2DgAdJUoOgzEUIuIRMQRxTnTa2XoE7d7ieRvQGdAiXidIrmOpsUiSRHBwML179+b48eNCnCsLeisrr5CRKpti2GflcYVx4cIF2rVrV216XFwc2dn1Gy21PgSSLQHqxQ9jx2KKimL04MHMa+nKNDN6nMRv3gyAecQIKhrQzHNPVAxRPXoAMOtAwDlyVRCcTYDXTDCnW2cefPBB9ky8weeyn1ihCuijcTCUwPDFtaGR4PXXX+df//oXH3zwAZMnT+ax04HjLUCAAAEAFi1a5PE+0LKl4Wi1WiorK0k7tt41bcCAAS1XoTaGRhLdXcDtbCksLOTEiROAELQHqJvIyEjmzJnDxo0bAdHCJScn58pJtgTwGx06dGCzfH2nZvPmzSQlJfmtnECyJUCdHI2NJUb2bPT44Qd0zlaSRW9G5uzdS1lxMaHh4Szr37/e630zYiSSJFFw5AgjygvqXuEKoL0WjBJMspUSHR1NWGoaayKS2B4R47HcN+06otFouCOkhSraxrhBV0V+fj4jLhxn/vz5HjK+AAECBLiSmT59usf7QLKl4Sg+kfJM4bWyWq307du3JavUptDgTrbY7XacTie//PILsbGxdOrUKdCNqJ4oniClG1FhYSGvv/56tYTqZYsN0Szc348rsGXLPffcw8MPP8x///tfTp8+zenTp3nnnXdYtGgR99xzj9/KCQhyA9TJzokTidNoyDt8mOtlk/+VhtFuJ3zrVi4MGMDekhLmUffIzafDwgjp2w+A4Zs2NY3Qqg0zvOQin+ddJDI2js1z7gUg+cNnSMLMtpA4Rs+5g95FRdz86b+4YjTpl8Cb0TD9Px8w/U+3tnRVAgQIEKBVERLimbUPDg5uoZq0XZRkS1ZIAhqgtLQUnS5wGVFf1C1bQCQLdu3axQMPPEBhYWEL1qxtoiRbsrKyADy6UgcIUB9+//vfk5+fz/33309VVRUg/hsef/xx/vjHP/qtnEDLlgC1sislhbju3XE4HIxYs6alq9OizP/lFz5+7TU2HzvGxnos/3l4OHl5eRScOcPVZ882ef3aGhoJQjMPe0xbFZMqntv3EhPyckiQAomW+hCmgVtuuYXIwK96gAABAngQGhrq8b6H3L03QP1Rki22hE4ArouTAPVDwjPZYrfbycvLAyAmJqaGtQJ4oyRZlGfFraEkXS577E34uMKQJIlnnnmGixcvsm3bNvbs2UNBQQF/+ctf/FpO4LT8CkCSnoLrIGxyHgPIYBzrGHJ2P6ErHUKOu0E8CjbD3lwhxi1FtCo7Mn48AEW7djE2L4/UYCGPTfYlKm1quWZN4suGopblKmJQH0LaCFlCFhEh9jc+yMF8WdfyeUgI+4YMQad3t28xV4Jenu/UwrvZ2bz++usM/fojEtTxgoZJcesS4voLRdZqx1OA6wtfolzv6TlUF+XmyNI0OR4Dz2Z6bPZwuzQIhpK0ngB0yTwISVQX42Z4le2PWAyQ6h/r2sS2jSGjhniCp9QZ4HCxeO4e6RbSKaK8jpJblhiPp6hOeb1dtR9D2rgY1xvvWHmjyAR9SXJHSG6R6XqnEE2OltzCRAVFsttapITNxVY5diMaKMlVT1Ojjm1rEY02JZucYl9H1yHJHaear8hbaxK5KjFefwXET019JLlqqekqp0fLlqSkJDpP71J/OS6I39PWfLwqv1GNkeR6y5jVqATDrmGJo4RQss5ky0QJq0mWjgcjns/SusS4arY63VJ5JZ6dEPWe2AhJrpcMVvrKc5+VrkQA6enpVGOyhDlMoiQXIkIRMYyn9Ylx1WxX/ccq9euMqDsI4XBjJLkrnEKGi7sbkaOiFHAnWbKysgJDZwdoFGFhYQwZMoTevXs3SRfTQLIlQI38HB9PXKdO2O12pm2sT1uOy5/btaKv99DHHuPnadP47MYbfS73rVMMdhQHzCUg7aqJay6e9Thhc3bswrbQOCLj4rHb7cw5f6QFaxcgQIAAAS4H1MmWsWPHIgW69TYYZdjdc99/wvLly8nNzW3hGrU9NBoNBQUFREZGUlBQQHh4OABDhw5t4Zq1PZySBieQk5MDiJFljEZjy1aqOQiMRtTmCCRbAtTIB7m5vPHGGxSsXEmnkpKWrk6roJsG7pk71/W+ND2dEn11e8s33XsSFBTEAiAocFJXI6FOO2fOnHG9DzOZWNpnDAClpzNJsAUSVQECBAgQ4NJQdyMaPnx4C9ak7aK0GigpLWX37t0ByXAj0Ol0vPzyy8ycOZPdu3ej0Wgwm8106tSppavW5nBKEmW6MMxmMwAXL14MHJMBWiWBZEsAn+wBdgMXs7O5aefOFq5N66LL/j2u1xs2bOB7m2c6eHt8PP1vvJFFixYxz0ci5v+3d/fRUdUH/sffN5lMEgKZEDAJBAzIk4DlUaChWKFFQasuPatHaX+KXXV/7ZHdcqjdpftQaz1b2mMrui1H29Oj7Lrb2kdof6iooMGioSgEQUV5MBCFPAiSAHlO5v7+uHOTyWQmmUlmMndmPq9z5iRz59473/ud771z8833fq70lH70Pfbt20eHrx5zZ1h3N5h05r14FktERJKE/8iWz33uc3EsSeKyO1uafNkOChmOnB0o3NHRwZEjRwDrcixDQ63C1traytatWyk9+ic+ybauMc/KyqKjo6NrpFBS08iWhKPOFgnqf31fojcDRfEtiuPcfLz70pa9e/fy+4BbYe+eOxeA5pMnGd/ePqRlS0Q/PnWAKbufp2bHnzl8+DBgXcv8tw3v97OkiIhI/1wuFxs3buRf//VfmTdvXryLk5C6bv2szpYB8+9s+eSTT4Ded8qSvnm9Xt5++21K6g6T11bPsmXLuurymmuuiXPpRHrTPdtSws1wE6wY9iJfZCfXVe+BrUAFcBDaK+FkA3yK1bF5aMwYvnDPPeRXVPCt555jGAGBuBDdYFZ/Cwwr/CuYsVjBl2PpGfI5GIGBmgX0Dj4tBGqtoFwALrSxefNmXC4Xzc3NvA5UAZNcUN2cRtpnPgPA1UcP9g7GtfmH4gaa5QuX7Zp3iEPQDvmH2hrW9h8KUYbAuuoK/fWbbgfjzvILnZ3rC82rNRldaPDYFHil8hBffOsQ5eXlLC8uZMzpFuvzKfR95qdDvNdg+IfRjh3AOiuiXCb/ehvrt/4Kv3Y6Phu+82srKNcOtwUrkM4OxbXZQXUF9AzTs/cz/3pNFm/61VWobQsM8PMPft1j9gzFDQzHdWIo4VALVg8nAurNP6QzWDiuvUwqWmL0HbK61OgOvQ0VjrvU6F2vqcQOEw4VDvyeaYWa7ux+fcOGDdYvy0PUaaCpvuPwR8AYnN9e9/gCmP1DcsNtI0Hqq4svJLers2XCHDh4sO98DN85VcYoukNnE+XY6f/9sMT3/XmE0PtiBFwuF3feeSc7duygqqqKM2fOsHr16t4zlhg0n4PsHMjOomeweyLUo38dlvrO96ZghRnaIbk7BrYdXQG5QP5vz/F54L777gPguuuu45lnnonCBjhYBxCLJpCCdyMaKhrZIr0cLi3F5XJxRWYmE+JdGIfa3vEJM89WM2PUKJYvX862OXMAeGnSJIYNH05TYyO3nTkW30ImmM9mQI5hJcp/+XhFvIsjIiIiPnZny6VLlwCNbBkIl8vF6NGjuXjxIocOHWL37t3Mnz8/3sVKKGlpaVxxxRWcHH0VnZ2dnDlzhqNHj5KWlqZLBMWRNLJFevgoN5fLrroKgMVvvBHn0jjXwgx4eST8ZMJELi1ZwvkzZ+CdgxyZM4d8wHv4MFmmN97FTCjD0mBXATSa8AUdmURERBzDvhtRY2MjoM6WgXC5XHR2WkMIGhoaALjyyivjWaSEk5aWxle/+lX2pqeTf+AA771n5fvNnTsXj8cT59INgViNQNHIlpjRyBbp4a+LFpGWlsbZykrm1dTEuziO96Wj79HZ2cnIsWN5bUwxudOmAfC5gwfjW7AEtSgTvqDzNxEREUfpCshtagJIjdvsRpnL5cL05fxNnz6dmTNnMnz48DiXKrEYhtHVBp9//nkqKytJS0tjyZIlcS6ZSHD6/7F0aXS5yPIFxxWXl8e5NIlhbEsTDZUfkj95Ci9dt4KC9HTqa2tZVFNjXecrIiIikuC6Mls0smXAXC4Xo0aNAqy7YtXX18e3QAkoLS2NpqamrjsPNTU14fV6WbBgQZxLNkSU2ZJw1NmSCox5jPlCJXOosMJxXwR2Acfg5BG4AFwEds+cSVZ2Nhfr67n72DFGZvmFwkJ0g1rtQNpA4QR2LjBiF5Q717fuMIJyL1zwFfnoB7RMnkKLabJp0ybW541gTGBHSyHQ4Pv9eAME3hLaPxTXDpR1gsBg277mg+BBuYHL+89jT7cDYet86yvACnANtdxg+IfiRqueQwXbDmZ9cw3r4b+u0yZ8dljP53YQ7CnT2jf8w/OChRLa+06yBeMG8q+bYAJDcsv9ngeGvUJiBTzGUrmvDkqN7t9twUJyA9nhuH0FxCYzO8Q0lDJf8KstWCCnHdwqfYfkBrM8jFvsLvUFiB8NchxwOv/21V+IcDChQnLnGqR/ZP3a0WHdIzZkZ0uxLxB1ON2h7Yl27AwMyZ2IdX4Sqn6C2WH2CoK170ZkCzo6qMSgvcEXjJtFctQhWL+7gEys/SvSkNw/mfA3BmlpY7pGtgBUV1cDqLNlsNTZEjO6jEi6nPPdsti1fz8uM8EO6nH0hUorCHf8+PG0t7czvfp0nEskIiIiEj3pAc8nT54cl3IksoyMDPbv39/1XKODImePbLGdPXsWj8ej9iiOpc4WAaAS+K/f/55Xdu1iaYXuBBOJaRcbqK+rIy0tjZKSEkaF8Y8zERERkUThCviLYa7vH3QSPpfLxQsvvND1PDMzM46lSUz+mS0A586d4+qrr+4KcE56HUB7DB4dQ7kRqSVFWqb053ms2/kZf/kLY3239ZPwZTbUA1BYWEh+fIsiIiIiElXD/Ya2FBcXc9lll8WvMAnK5XLR0dHRdSmWwnEjl56e3pUbBPDpp5+mziVEkpDU2ZIK/hYW8Ve+zDb4I/ASsBdOH7FiQup8kwDWALMKoLjAl00yFis/YqDZFna+SuDjtBn8EU4Gy5tmdw5DqOyXgarwW7d/RkitaVXWWKxMFSB3ilVHublQ8tJWXn31Vd566y0m+OfcFPr9fryBHuwsk8D3OeSwS7gOmd25KoX91Het37zBlp9l9JzHP68FrGuTT5vW9by1Ae870HboX8/+64lFLk5gvsxgVIRY11987ega3y0O7WySuYa1b5T45g+8xnuB374Xrawjp7PrJpRTfvUV+Fx5Lf0rDVK3feVbTErxvBbbHrO7LoIJzG3xt9SwsjgiyeFIVv3VwU4zeEZLOJkbiZjXYgtsX6HaUqC+6qXCJM8vbiTUqJb2PANysL4L7f+UJ+qx0/4+sI9XdpZcOLk/IdiZLY899hiPP/44OTk5PV5vHm7ltWS4gCkBZUlE/t+pp0yrDjuBc1h1OQAZZ09x9OjRrufnzp1jzpw5gyxoAumM4UNiQp0tQvnkydzwf/4PC6ZPZ1m8C5OgprY3s3v3bhobG/HoMiIRERFJInl+uf66hGhg7M6WS5cucf78eYYNG9bPEhIow4CamhquvPJKysvLuXDhAjNnzox3sURC0t2IhOrZs5k8eTKXnT1LxpEj8S5OQlqaAbelweUGpKmzRURERJJIOCNbpG+BdyNSZ0vkMtLA6/XS1NTEiy++iMvlYurUqfEu1tDpIDZDJbwxWKcA6mxJeRfdbvKvvBKAWYcOxbk0iSvdgF+6410KERERkejL9stsUWfLwIR162fpk9vX0VDhu5nHlClTcLt1Ai7Opc6WFFc+bRoZGRk0nDvHnDMDvIBSRERERJJWTWv37yUlJfErSALTyJbBy/CNHj948CBA6l1CpJEtCUeZLangy7Ccncx8+4SVhLsbjtTBSaB2+nQAXO+8wxVZVjAu0B0gWhFBKFewMFwIHoQbDXZQrv97RUtFkKBXO/w0ne7AXL8AXDssF3pO7xV6O9nj/FDcYIKF3/Y1b+B8dv3NMnrOExhUa0+bO4hgXP9AXP+yxyIUN1CwtjPYdc0Nsa65fvuY/dwOpPMP1CsOmC+VhBOSuyAgJNcOfi01FI4bSnkYQbj+lvjCRlM9HNd2op+Q3GDCDTpNJX2FCQeaYfQdArvUgKmG9cdMoobj2uz2FWmQch/1c+f2dwG46aabMIyAOl9unYdljMIKQbWPm30dJxJBsJDcSOwwYWV3XfXZ2VJikJ3jC8c967tRwKkkCLQPDKIvACZi7WcrIzym/ckkw/eXq93ZctVVV3W93DJ13GBKKhITGtmSwhpdLjyTJwOw8P3341waEREREXGiGTNmUFtbS35+fryLkrB0GdHg2SNbLl68CKTgyJZ2NLIlwaizJYW9OXEibrebSw0NLKquhqx4l0hEREREnKigINKhHeIvsLPF4/HEqSSJKyOgo+FKX+6kiFOpsyWFvdfRgevDD8mvq9P1ZCIiIiIiMeLf2ZKbm6vOqwFwB/zBcsUVV8SnIPHiBWJxZVmCX63mZOpsSVEmsLOykrrKSn4R78KIiIiIiCQx/86WadOm9c6+kX5l+FXZmDFjFDIsjqcBDUnOMB7CvfICN/I87ALegmNnYD9QB2QCy7CCcXNziSxENJww3KEI9rLfK9ohudAddGoHkdqBwf7BroV0B+L6151/8G2hYQXjAhxvSJxQ3GDsOpnVT30Hm8+/PvznqQiot7l+r4fLPxTXPxA3XvUcrZBcW2BI7l8aek73r8PAcNyh2hedKpzjw4IQryscN7RyvzBhfydMKxDXtkR/UATVVwBrqODXSANPU91O0wpvXW7Ae/2E40J3AGoih+Pa7P0wkhBhsOoqUkewAk8LgI9IruOmvS17TGsbqxhYHdGzs6XH5S8lBu0NWJfznw0Scp/o7JBc/8DkDqx2E2FIbsa1d3T97j+qpXm4QVYq9Lt0xPAhMaHOlhT15qhR5OTksBBQPJeIiIiISOz4B+Iqa2RgMjIyun6fNGlSHEsiEh5dRpSiOlau5NtTptC5bRv4bp8mIiIiIiLRN2bMmK7f1dkyMCnf2dIBxGKwaBINpHKahBrZsnHjRhYsWMCIESMoKChg1apVfPDBB30us2XLFgzD6PHIykrt2+40p6czsqQEgLlnzsS5NCIiIiIiya24uLjrd3W2DIzb7e76PSU7W9pj+JCYSKjOlt27d3P//fezd+9eXn75Zdrb27n++utpbGzsc7nc3Fyqq6u7HqdOnRqiEjvTvnHjcLvdNF26xIy6ungXR0REREQkqflntqRkR0EU+I9sSbk7EUlCSqjOlh07dnD33Xczc+ZMZs+ezZYtW6iqqmL//v19LmcYBkVFRV2PwsLCPudPLv/Ewvy/MvH1avizFY77nu/g1FhZybQCKxyXsfQfRhoYiBuPMNy+2CGYoYIuB8quF/8gUjv81D981Z5mh7LOCghsPd4Q3XLFk10n4YTkBjrkqz972cCQ3EK/eu5PqFBcp4hWSG6ougicXuEXLjzXbz8Vqx5CHRsCj13lQcL8JLRgIbmB9qgug5oU5vFhqqFw3FAiDYANZC/7ke95srbVwdRRX4oNK9jV/u5NpmBXW4lf3R2NcPt2dM9//fXXk5OTw5IlS8jMzLQmzrLCcTNcWAHDyRaOa7O3qdyETqxtnQhEOMDe6/V2/d7VYVVikJ2DddePZNcZw4fEREJntjQ0WH+85ufn9znfpUuXKCkpwev1Mm/ePH7wgx8wc+bMkPO3trbS2tra9fzChQsAtLe3096eWOOssrM7cLebtHuzwQ2d2dAxYQIAeVVVtGf5wroygf62LSsgSteJdZFlbWfUy5bp23b/9WZlW8/9X7On2cvYV6z5tZ1Ea0MhBauTUPMFm8d/un9dZhFee7SXszm1XrNCbH+kfPXVqx0F1m9mQF06tV7iwd3HZxH4WrQ+N4eK2vHIHeI44F+ffdV7qgt1fISe9dbXfHHkmO+1vtpYRj/fVW6/86BYnD/Ek10vofbTYDIibGtZ2d3f2QPc1x3TjkLJyu65fZHWkY/H4+HDDz8kJyenx3d4exbWX2RuUuN7250N6VjbHO75ns/HH3/c9XteXp5Vj75zwfbMLKAlqkUVGSzDNM2E7D71er3ccsst1NfXs2fPnpDzlZeXc+zYMWbNmkVDQwM//vGPee2113j33XcZN25c0GW+973v8dBDD/Wa/qtf/Srh7+fe1tbGwYMHcbvd5ObmagieiIiIiIg43re+9S1OnDgBwLZt23q81tTUxFe+8hUaGhrIzc2NQ+li58KFC3g8HqABiMW2XQA8SVl38ZawnS3f+MY3eOGFF9izZ0/ITpNg2tvbmT59OqtXr+bhhx8OOk+wkS3jx4+nurqaUaNGDbrsQ8njaaG0oZwde1fBw/DbI6M4fu+9tLa28rePPcasy3wzjgH+0s9lLld6ej5/34GXxVzpgSKgLMplu8a37f51NNljXRq02PfaGw3d08CabkfiHG+gvb2dl19+meuuu67HNacJy3+7+5sv2Dz+0+16u8YD1YTXHu3lbE69TMu/TQzGNR74S5B25JseOB/XeOATnLmfxstST+hjQ+BrV3ngneStu6gdj5b79sGdAXW1wgMvNvT+XXqa7YG3Q9TNlzzwnO+1eR444Lw6dMz3mn9dBbrF10b/3Ec9A3yMdf6QTG3V3vfsbQxVR/5u8YSuq2Cu9FijE95usI4HgceCMDimHYVifx/Y9RlpHfVlsYf2j3yXEZUAZ0nq7x7Aaif2yJbz9H8e6Wf27NkcOXIEsP6BDFifTwucG57FmPfOJ2WHgTpbEldCXka0du1atm/fzmuvvRZRRwtYwUpz587l+PHjIefJzMzsvpYyYFlHfgn0obm5g7YMg4y0ZmiDD8+dY+vWrVyZnU1GczMZ9mi7VqC/bWtp7vnciXXRYm1n1MvW6tt2//W2NFvP/V+zp9nL2PXrt1witqOggtVJqPmCzeM/3b8uWwivPdrL2Zxapy0htj9SAfXY1Y4C67c1oC6dWi/x0NbHZxH4WrQ+N4cb9PGoLcRxwL8++6r3VBfq+Ag9662v+Rwg7t9rfbWx9n6+q+w23Epszh/iya6XUPtpMO0RtrUWv3UPcl+PezsKpaW55/ZFWkd98X1XZ7iw2l8qfG+3NVudLV7CP9/z+cUvfsGtt97Kpk2buttKi68OMxJy/IAkuYQKyDVNk7Vr17J161ZeeeUVJk6cGPE6Ojs7OXz4cI973Scrw/gjTMzmavbD88A7sL+5mbfffpvcvXuZVeCbsdYMHsDZXyCuE8WqXHb92OGj0Dskd5bRHfxqh6I6LbA1mvy3u7/5QoXE+ofkzjK6w4f7CscNDB52eh1HMyQ32Hr8Q3Ftxb7nTt1PnSgwJDcZAwpjIVSIsB0yOslI3sDRaDhhwpIQxwc7EHepEXkop3TbaVqPYOzQ2A6szyLZ2uoeX/uKJFx5pwnLw/zOKjYgh+5g12QNFT9lWkHge0zrmNZB+HUEsDLEvCsNOOPraDmbxAHDNv+g4U6scNyOyFaxZMkSqqurWb16tTXBFzDclZEo4jAJNbLl/vvv51e/+hV/+tOfGDFiBDU1NYAVOJWdbYUj3XXXXRQXF7Nx40YAvv/97/PZz36WyZMnU19fzyOPPMKpU6e4995747Yd8XTU9zN0PLCIiIiIiIjzGEaM7qwlEgMJ1dnyxBNPALB06dIe059++mnuvvtuAKqqqkhL6x6wc/78ee677z5qamoYOXIk8+fP54033mDGjBlDVWzHaCWN3KvnMeHsWWaeOgUkce+5iIiIiIiISJwkVGdLOFm+ZWVlPZ5v2rSJTZs2xahEieXN4XnccNNNtLe3M/4HP4h3cURERERERESSUkJltsjgvOMZDUDT2bOkJeZNqEREREREREQcT50tSe0G+FtYxqvwOnww3OpsST93jikFwFh6B4smWiBuMG+a3QGh0RQstLXWF05qh8X6B+MeSqA6G6hwtzFYSGyk9WOH4trrc3IobiA7ADga6/G/3XUwFb79ta+Q4VTV37GhRNeBD0i5Lzwy0CTDCh2VgZuqNhmWMrM77DZSEQZ0JqzB1FEwy/3CcSG5g139ReuYttKAt3y/T8T6/gkMak82p0xY4BekXAC8Z4YOEA7DhWOQMcr35K8fD7qIztcew4fEgjpbUkjzaKuzZfjZs3EuiYiIiIiIiEjyUmdLCknzdbYUqbNFREREREQkgXTE8OE8X/7ylxk5ciS33nprr9e2b9/OtGnTmDJlCr/85S/jULrwqLMlRbR5Yfgoa5zdJHW2iIiIiIiIiEN985vf5L//+797Te/o6GD9+vW88sorVFRU8Mgjj3Du3Lk4lLB/6mxJUoaxE5Zlw81w44WXeP9YNjk5OQDMuXTOyn6wMx3mhshpSWSnY5jbEpg9Yjtkpk5Wi79DYeaRBMst8V/2UJC6nWV0Z7XUpmj9Bjre0HtahS87SPrX17EtVTIHhoryWsKzp496OmpaWRsSfVON7n/m9vUZJLqBbNvOMJY5g5W58RHdGRzJzH8bIx0IsCNI/bwFzS3AFN/zVPv+KTettrnUsNrSQMwyyB2L9VmkTP2lVmbL0qVLGTFiRK/p+/btY+bMmRQXFzN8+HBuuOEGXnrppTiUsH/qbEkR1S1tPPXUU7zxxz/i7nDmDiUiIiIiIiLBOOcyotdee42bb76ZsWPHYhgG27Zt6zXP5s2bmTBhAllZWSxatIh9+/ZF/D7BnDlzhuLi4q7nxcXFnD59OirrjjZ1tqSIj9s7qaqqov3QoXgXRURERERERBJUY2Mjs2fPZvPmzUFf/81vfsP69et58MEHOXDgALNnz2bFihXU1dV1zTNnzhyuuuqqXo8zZwY63Ml5XPEugAyNKq/1s7jv2URERERERMRxOojNJT/WyJYLFy70mJqZmUlmZmbQJW644QZuuOGGkGt89NFHue+++/ja174GwJNPPslzzz3HU089xYYNGwA4ePDggEo7duzYHiNZTp8+zcKFCwe0rljTyJYU8enMOcybN4/iINe9iYiIiIiISOoaP348Ho+n67Fx48YBraetrY39+/ezfPnyrmlpaWksX76c8vLyQZdz4cKFvPPOO5w+fZpLly7xwgsvsGLFikGvNxY0siXJGMZDwD0wcjmsgnGfP47rFcj+7BJuGT2aUf/73+Qe9fVa2gGyBSR+IG4wsdqmWl8YqR0wbIfm1iZhHUZiljH48Fq7Du2g3EKSp17tMOBUD/h1ggUGvBnic+jrNQktFQIyxdkiCRKealjhw0uM5A7HtdnbGa2w5WJfuHAjKRRMCpQaPYNdlxvhhQkHkw/Zo7DCYVOpDgO/XzsY2HnRSoMLxyC3AOvvmJQRqzBba50fffQRubm5XVNDjWrpz9mzZ+ns7KSwsLDH9MLCQt5///2w17N8+XLefvttGhsbGTduHL/73e8oLS3F5XLxk5/8hGXLluH1evmnf/onRvnuuus06mxJAaYJmXl5AIyr/zS+hRERERERERFHyc3N7dHZEm87d+4M+dott9zCLbfcMoSlGRh1tqSAdm8GLpf1URc3NcW5NCIiIiIiIhKZgd05KLz1Rs/o0aNJT0+ntra2x/Ta2lqKioqi+l5Op8yWFHCxZRgAHR0d5He0xbk0IiIiIiIikozcbjfz589n165dXdO8Xi+7du2itLQ0jiUbehrZkgLONVqdLU1NTSgeV0REREREJNHE9m5Ekbh06RLHjx/vel5ZWcnBgwfJz8/n8ssvZ/369axZs4arr76ahQsX8thjj9HY2Nh1d6JUoc6WJGEYvwRWgvEg3ARcDdzayo08x6f7hoEHmpuauKzZa4W71pGcobiBhirsMjA0N9WEG24WLCQ2cJrChgemIsXbYCQUgCtOkypBrU6w1LACNScZcCJF6nwgbauv8NccYBSpG4w92P11tO8mAB5SKxw3UKkB6cAMAy4HdkRQF0cgdyyQj77T4+Stt95i2bJlXc/Xr18PwJo1a9iyZQu33347n3zyCd/97nepqalhzpw57Nixo1dobrJTZ0sKqO+0Rra0NTVhGEacSyMiIiIiIiKRcU5my9KlSzHNvju61q5dy9q1awdaqKSgzpYUkHPxBFv+vIVRHbHYOUVERERERETEnzpbUkBrSxMnT55kmAa1iIiIiIiIJKB2YpPZEot1CuhuRCnhou8GRMPjWwwRERERERGRlKDOlgRkGA9hGL/EMA5gGBcwMoCJ98LN4+Ae4OvAva3cPPbPLKOMmsbpXH311YwelQ/FvuEtqRCOG0vBQkhTPZh0VphDp4KF6fpPS+Zw3HCDhCW2FvTRVhW0N3ClGj45YArHjY6lEbTBVAnHBSvQNVKhwnGLDegcXHESlh0IbO+vyyOo15VG9890YGJUS5ZY7O/gctOqy/fMyMJxgeZzWDEjKfed3RHDh8SCOltSwPmJC7jpppu4bGxxvIsiIiIiIiIikvSU2ZIC2rNGAOC+dDHOJREREREREZHIdRCbfBWNbIkVdbYkOdMEc5gHA3BfvAgZ8S6RiIiIiIiIRMY5t36W8OgyoiTnbU3DyHADkH3xQpxLIyIiIiIiIpL8NLLFYQzjIcDOVpnn+zkZyLU+rTxg4oMwDuuRBxT5fp8MadMamVx4gml8wCq2MfnjSo4zi6amJkaPbE+9YNw3TSuIaygCtOYaqR2SG2746yyj97zBpknfrvHA3qae01K5/UUi5QL1hki56nXAlhgKyY2Gsn7qcKov3DWVwnGhZ9taavRfT2CFvwaG5C43IAcYhfb3SPdZOwD2CN0Bw6n6XWRvd6nR3Y4iOQ9caZCdBRTEpHQOp1s/Jxp1tiSgNKMTL2mAQZbRwkTzQzqb0yk8WUtt9kgmfVwJ861LiHb87y0AnDp1ipL0+JZbREREREREJBWos8XhZs36mM9/fg8ulxcjzcSTa10KtO+jBTzfdAOr835NCVVwFutRaS23f/4MDANmLDjEe2/OZt++fXxJF42JiIiIiIgkIGW2JBp1tjhIZ2cnK1aAx3OO3/52FACnT+cxevT5XvPmuBuhCV5vXEyJu6rX666L7TACZl+zn8du+39UNnWQOzHmmyAiIiIiIiKS8tTZ4iBnzpyhtBSghZEjOzh/HiZN+oRPPsnnsss+5dPzI/GSRk19EdWdRaRhcrRtGj9hPZlFrVyanMPYkScpuqyGUSPOApAzopH6dqu3coRGtoiIiIiIiCQg3fo50aizZYCsINsS3zP/BjovYM4CILf7qSu358vjfT/z4Irx7dw1D1o73Zxf+X8hz2Bfzjz2jQSyfPPmAGOs+dMKG8nObmNc7klGcZaxnCGPemZxmAmcZDFvMPKlZhp8++SIVM1siVUAWWAgrsJJwxMsAC2VwnGjFQb8l4bBryOVDVVwtkg4FI4be0tTNBzXZge6hhOOC73DcYH2tyDDBRxN0TqE7lDXPWb4YcN+Pj0D+WNJ7e8f+/vXDsddGtl5UfteyJjie1JsWD9T7QYgkjDU2eIgaWlWPPm59lGAEbX1XvL91MgWERERERGRRKTMlkSjP78dJM3wAuA1o/exmKY6W0RERERERESGkka2OEgsOlta2qxRs5DClxGJiIiIiIgktHZik9kSi3UKqLMlIuPuh5Z8rNyUBx7s+eL4gJk7gOKAaWP8fs8DhpvQYZDmaWTYiGZy3q+H/wcZ01uY/qMKMmllOBfJpI1hNJFNE8Noxk0rozlHHvUMo4k86smjngJqKaSO8Z/WYXwE7IW6v3S/5fCDnUgUKaNFBiKV8mmcLJWvl5fEM8l3afEYlO8yEFMNms9A9iigxIBTKViHg203yw0rryUnKqVJXOV+9RhhXguzDEbkoCs2Ar9/I6nHWQZ1DVB8BhhLd1ZLsQGHzkarhCJRo84WB+lMS6d5WCZt2e6orfNCm/VzOJCWpuuIREREREREEo9GtiQadbY4yPGpV3B+jnW3omE0R2WdLb7BLNlRWZuIiIiIiIiI9EedLUmuzYqBISN6NzcSERERERGRIaW7ESUaXVeS5OzOluhdmCQiIiIiIiIifdHIlgis2fwL0kdl0kH3bX1Gc67XfHnUh/18GE24aQXg0hEPH/91AqMm1rHw2nIAhnMRgBGt1s+cT73QArQCDb6fdVjTPgXOAWd80yqh6UPrfTLV2yIiEpmSMIYEpmLQZiilRs/wyFKDY3u7n45ygSuMs47sTMjwhPF+0TiDOeHwz2+SwaEPuy8F7vgQ2g2DDAZ2eXB+1uCLlOF3Z8OO7Gz45a8Hv9JoWWoEDds8cMxqLuOs0y3OGda+PQwYEWad5BaEeCEa7bCAnvuOAx3ZZW1q7jnoMAyy/bY7f2w/CxeQvKHkyw3YGca2rTQ4dBgygOmZ0DzcINs+zo0leesnyuw6zG+AbP/BGKdNWJQKoQkdxCZfRSNbYkWdLQ7S0pDNp6cuI3NEdPJaoHt3dOsyIhERERERkQSly4gSjS4jchDTa/WIpKVFr3e7zbcqdbaIiIiIiIiIDA2NbHEQ02v1fRlp3qit03fnZ3W2iIiIiIiIJKx2YvPnu279HCsa2eIgXt/IFiOaI1t8P3U3IhEREREREZGhoZEtEfiPx77PqPaW3i/YgbU2V5BpHf08b4GyCUv5cOKVFP7xLJO//zE0+s3bSddleu2+6c2t0NwCzVj9kc1+j4tAE1DmW4XZCBQbVoBUqllgDFnw2BEjdK9WZ3Y2/PrX1Hk8uJqjl8sTidwcyM6J3fpr63o+D3UF6IU+pn06gPcd6ki0wbzfYKPNTF87KvN46AjSjuz1269E6yCfEaX1xGp9kerrM2gn/KuX2/vY5wcqWN20B3ltUJ9tdjZZv/41f/R4IIrHow6/+mgnYFvCvNS8vQUrAD7GMgBi8PkNVGCbzKB3Hdqf+UCvrg92CjMoXhgJ/G+U29FgZBhGr7q0j9mHfRXXox7DrJP2qkEXLaSMDxmSthjquNfXMcdfV1Az4PJrhB391E17FX1vX3Y2I3/964jaUbAyD8UfNf77XlcdRfDZ5QKH6nzrabTK3HEmsnUMRLh1M1TfzXbddQQ8D0c2cKgRaLS+c+xtu5AVhQRwx1NmS6LRyBYH8RrWx5FmRu8yInvXifcfNiIiIiIiIiKpQiNbHMQwvWR0tOHqjF7vYqfvpzpbREREREREEpVu/Zxo1NniIMtOlrHsg7Kotnd7Ve7orVJERERERERE+qDOliSnkS0iIiIiIiKJTpktiSYhO1s2b97MI488Qk1NDbNnz+anP/0pCxcuDDn/7373O/793/+dkydPMmXKFH70ox9x4403Rvy+j/4Y3NEOl4sC/w8xw29aNt27Tiuw80xswhwTQXOMtjtwIF9uH/PaEb1/jUlJwutQa2/sDlh2qr7qMJSBhOoGE26nZG2U3m+gsuh7mzOA/Ci+XySDVsOtw6YBlmWoZDP0wcu2UKc8roDXnFiH/t9H8azD/tj16KQ6DDwh68A6HmbTHZbrNCZWuZz0OXfQ+ziU6zfNifXYwdC0xVAn/cGOOYF1mB8wLZr1aLejAiCcs7Vg5W2PcplCcQX8HsmfqLGsw/6EU86hqkN/kf4h2lcdpg++OCJRl3ABub/5zW9Yv349Dz74IAcOHGD27NmsWLGCurq6oPO/8cYbrF69mnvuuYeKigpWrVrFqlWreOedd4a45GFYtAi+8hWYOTNqq7QPrgnZqyYiIiIiIiJ0d4nF4iGxkHCdLY8++ij33XcfX/va15gxYwZPPvkkw4YN46mnngo6/+OPP87KlSv59re/zfTp03n44YeZN28eP/vZz4a45GEoKoKpUyEvL2qr1N2IRERERERERIZWQg14aGtrY//+/XznO9/pmpaWlsby5cspLy8Pukx5eTnr16/vMW3FihVs27Yt4vf/+IoryDDNXtM7Gxtpqqrqej586lSM9OCD2Tqbm2k6ebJ73ilTMFzWx5A/ciQ5QI3XG9FlEf7vZH+gaVihuGcCpouIiIiIiEiiUWZLokmov8HPnj1LZ2cnhYWFPaYXFhby/vvvB12mpqYm6Pw1NTUh36e1tZXW1tau5w0NDQCMWrGCzMzMXvOfPHmS3/p1tqy78UaysrKCrvv06dP81q+z5f7rr2fEiBFdz1taWnizrY39IUs3QOnpXMrISNldKVZRO4GD7vq6XtTMyqKjqYnOrCwI0mk3WOHszLG6YVw0DeSa22h9vuEeEJuj9H4DkpWFt6mJlj7aUQbgjeJbRvLVnhB1GIZo12Ekws1sGVQdhtGOBiueddgfux6d3A47sLIsvESeDTFU7O+1WLajSAWrp0y6j01OrMcO4tsWw6kT/zoMd5lw2e3IlZWFEUY7Cvbe8Tq/iaQeYlmH/QnnveJ5jhhuXfRVh41ZWdDSgumQY1FstPY/i6PWKwnV2TJUNm7cyEMPPdRr+qZNm8Ja/rHHHgv7vTZv3hz2vIPxbGcnz3Z29j+jxE5Li5XJIzIYakcSDWpHEg1qRxINakcSDS3Wv97OnTuHx+OJc2Giy+12U1RURE1NeH+LDkRRURFutztm609VCdXZMnr0aNLT06mt7XkvkNraWoqKioIuU1RUFNH8AN/5znd6XHpUX19PSUkJVVVVSbfzytC5cOEC48eP56OPPiI3dyD33BFRO5LoUDuSaFA7kmhQO5JoaGho4PLLLyc/P5r3Y3SGrKwsKisraWtri9l7uN3ukFdmyMAlVGeL2+1m/vz57Nq1i1WrVgHg9XrZtWsXa9euDbpMaWkpu3btYt26dV3TXn75ZUpLS0O+T2ZmZtDLhTwej74EZNByc3PVjmTQ1I4kGtSOJBrUjiQa1I4kGtLSEu7+L2HJyspSZ0gCSqjOFoD169ezZs0arr76ahYuXMhjjz1GY2MjX/va1wC46667KC4uZuPGjQB885vf5Nprr+UnP/kJX/rSl3j22Wd56623+MUvfhHPzRARERERERGRJJVwnS233347n3zyCd/97nepqalhzpw57NixoysEt6qqqkeP5uLFi/nVr37Fv/3bv/Ev//IvTJkyhW3btnHVVVfFaxNEREREREREJIklXGcLwNq1a0NeNlRWVtZr2m233cZtt9024PfLzMzkwQcfDHppkUi41I4kGtSOJBrUjiQa1I4kGtSOJBrUjsSJDDO5748lIiIiIiIiIjKkkjNBSEREREREREQkTtTZIiIiIiIiIiISRepsERERERERERGJInW2+GzevJkJEyaQlZXFokWL2LdvX5/z/+53v+PKK68kKyuLz3zmMzz//PNDVFJxskja0ZYtWzAMo8cjKytrCEsrTvTaa69x8803M3bsWAzDYNu2bf0uU1ZWxrx588jMzGTy5Mls2bIl5uUUZ4u0HZWVlfU6HhmGQU1NzdAUWBxn48aNLFiwgBEjRlBQUMCqVav44IMP+l1O50fibyDtSOdHEuiJJ55g1qxZ5ObmkpubS2lpKS+88EKfy+hYJE6gzhbgN7/5DevXr+fBBx/kwIEDzJ49mxUrVlBXVxd0/jfeeIPVq1dzzz33UFFRwapVq1i1ahXvvPPOEJdcnCTSdgSQm5tLdXV11+PUqVNDWGJxosbGRmbPns3mzZvDmr+yspIvfelLLFu2jIMHD7Ju3TruvfdeXnzxxRiXVJws0nZk++CDD3ockwoKCmJUQnG63bt3c//997N3715efvll2tvbuf7662lsbAy5jM6PJNBA2hHo/Eh6GjduHD/84Q/Zv38/b731Fl/4whf4m7/5G959992g8+tYJE6huxEBixYtYsGCBfzsZz8DwOv1Mn78eP7hH/6BDRs29Jr/9ttvp7Gxke3bt3dN++xnP8ucOXN48sknh6zc4iyRtqMtW7awbt066uvrh7ikkigMw2Dr1q2sWrUq5Dz//M//zHPPPdfjBOKOO+6gvr6eHTt2DEEpxenCaUdlZWUsW7aM8+fPk5eXN2Rlk8TxySefUFBQwO7du/n85z8fdB6dH0l/wmlHOj+ScOTn5/PII49wzz339HpNxyJxipQf2dLW1sb+/ftZvnx517S0tDSWL19OeXl50GXKy8t7zA+wYsWKkPNL8htIOwK4dOkSJSUljB8/vs8eepFQdDySaJozZw5jxozhuuuu4/XXX493ccRBGhoaAOsPnFB0PJL+hNOOQOdHElpnZyfPPvssjY2NlJaWBp1HxyJxipTvbDl79iydnZ0UFhb2mF5YWBjyWvWampqI5pfkN5B2NG3aNJ566in+9Kc/8T//8z94vV4WL17Mxx9/PBRFliQR6nh04cIFmpub41QqSTRjxozhySef5A9/+AN/+MMfGD9+PEuXLuXAgQPxLpo4gNfrZd26dXzuc5/jqquuCjmfzo+kL+G2I50fSTCHDx9m+PDhZGZm8vWvf52tW7cyY8aMoPPqWCRO4Yp3AURSVWlpaY8e+cWLFzN9+nR+/vOf8/DDD8exZCKSaqZNm8a0adO6ni9evJgTJ06wadMmnnnmmTiWTJzg/vvv55133mHPnj3xLooksHDbkc6PJJhp06Zx8OBBGhoa+P3vf8+aNWvYvXt3yA4XESdI+ZEto0ePJj09ndra2h7Ta2trKSoqCrpMUVFRRPNL8htIOwqUkZHB3LlzOX78eCyKKEkq1PEoNzeX7OzsOJVKksHChQt1PBLWrl3L9u3befXVVxk3blyf8+r8SEKJpB0F0vmRALjdbiZPnsz8+fPZuHEjs2fP5vHHHw86r45F4hQp39nidruZP38+u3bt6prm9XrZtWtXyOsAS0tLe8wP8PLLL4ecX5LfQNpRoM7OTg4fPsyYMWNiVUxJQjoeSawcPHhQx6MUZpoma9euZevWrbzyyitMnDix32V0PJJAA2lHgXR+JMF4vV5aW1uDvqZjkTiGKeazzz5rZmZmmlu2bDHfe+898+///u/NvLw8s6amxjRN07zzzjvNDRs2dM3/+uuvmy6Xy/zxj39sHjlyxHzwwQfNjIwM8/Dhw/HaBHGASNvRQw89ZL744ovmiRMnzP3795t33HGHmZWVZb777rvx2gRxgIsXL5oVFRVmRUWFCZiPPvqoWVFRYZ46dco0TdPcsGGDeeedd3bN/+GHH5rDhg0zv/3tb5tHjhwxN2/ebKanp5s7duyI1yaIA0TajjZt2mRu27bNPHbsmHn48GHzm9/8ppmWlmbu3LkzXpsgcfaNb3zD9Hg8ZllZmVldXd31aGpq6ppH50fSn4G0I50fSaANGzaYu3fvNisrK81Dhw6ZGzZsMA3DMF966SXTNHUsEudSZ4vPT3/6U/Pyyy833W63uXDhQnPv3r1dr1177bXmmjVresz/29/+1pw6darpdrvNmTNnms8999wQl1icKJJ2tG7duq55CwsLzRtvvNE8cOBAHEotTvLqq6+aQK+H3XbWrFljXnvttb2WmTNnjul2u80rrrjCfPrpp4e83OIskbajH/3oR+akSZPMrKwsMz8/31y6dKn5yiuvxKfw4gjB2g/Q4/ii8yPpz0Dakc6PJNDf/d3fmSUlJabb7TYvu+wy84tf/GJXR4tp6lgkzmWYpmkO3TgaEREREREREZHklvKZLSIiIiIiIiIi0aTOFhERERERERGRKFJni4iIiIiIiIhIFKmzRUREREREREQkitTZIiIiIiIiIiISRepsERERERERERGJInW2iIiIiIiIiIhEkTpbRERERERERESiSJ0tIiIiDlZWVoZhGNTX1w/5exuGgWEY5OXldU373ve+x5w5c/pc7u677+5adtu2bTEto4iIiIgTqbNFRETEIZYuXcq6det6TFu8eDHV1dV4PJ64lOnpp5/m6NGjES3z+OOPU11dHaMSiYiIiDifK94FEBERkdDcbjdFRUVxe/+8vDwKCgoiWsbj8cStc0hERETECTSyRURExAHuvvtudu/ezeOPP951Cc7Jkyd7XUa0ZcsW8vLy2L59O9OmTWPYsGHceuutNDU18V//9V9MmDCBkSNH8o//+I90dnZ2rb+1tZUHHniA4uJicnJyWLRoEWVlZQMu7zPPPMOECRPweDzccccdXLx4cZA1ICIiIpI81NkiIiLiAI8//jilpaXcd999VFdXU11dzfjx44PO29TUxH/+53/y7LPPsmPHDsrKyvjyl7/M888/z/PPP88zzzzDz3/+c37/+993LbN27VrKy8t59tlnOXToELfddhsrV67k2LFjEZf1xIkTbNu2je3bt7N9+3Z2797ND3/4wwFvu4iIiEiy0WVEIiIiDuDxeHC73QwbNqzfy4ba29t54oknmDRpEgC33norzzzzDLW1tQwfPpwZM2awbNkyXn31VW6//Xaqqqp4+umnqaqqYuzYsQA88MAD7Nixg6effpof/OAHEZXV6/WyZcsWRowYAcCdd97Jrl27+I//+I8BbLmIiIhI8lFni4iISIIZNmxYV0cLQGFhIRMmTGD48OE9ptXV1QFw+PBhOjs7mTp1ao/1tLa2MmrUqIjff8KECV0dLQBjxozpei8RERERUWeLiIhIwsnIyOjx3DCMoNO8Xi8Aly5dIj09nf3795Oent5jPv8OmsG8v/1eIiIiIqLOFhEREcdwu909Qm2jZe7cuXR2dlJXV8c111wT9fWLiIiISE8KyBUREXGICRMm8Ne//pWTJ09y9uzZqI0WmTp1Kl/96le56667+OMf/0hlZSX79u1j48aNPPfcc1F5DxERERHpps4WERERh3jggQdIT09nxowZXHbZZVRVVUVt3U8//TR33XUX3/rWt5g2bRqrVq3izTff5PLLL4/ae4iIiIiIxTBN04x3IURERMR5DMNg69atrFq1Ki7Li4iIiCQqjWwRERGRkFavXs24ceMiWubrX//6gIJ3RURERJKFRraIiIhIUMePHwcgPT2diRMnhr1cXV0dFy5cAKzbQufk5MSkfCIiIiJOpc4WEREREREREZEo0mVEIiIiIiIiIiJRpM4WEREREREREZEoUmeLiIiIiIiIiEgUqbNFRERERERERCSK1NkiIiIiIiIiIhJF6mwREREREREREYkidbaIiIiIiIiIiESROltERERERERERKJInS0iIiIiIiIiIlH0/wEmktg4NzrscAAAAABJRU5ErkJggg==" + }, + "metadata": {}, + "output_type": "display_data", + "jetTransient": { + "display_id": null + } + }, + { + "data": { + "text/plain": [ + "HBox(children=(HTML(value=\"./spectrum.pdf
\"), HTML(value=\"= settings.dt_max: + self.n_substeps += 1 + self.formulae = Formulae( + diffusion_coordinate=settings.coord, + saturation_vapour_pressure="AugustRocheMagnus", + ) + self.bins_edges = self.formulae.trivia.volume(settings.r_bins_edges) + + env = Parcel( + dt=dt_output / self.n_substeps, + mass_of_dry_air=settings.mass_of_dry_air, + p0=settings.p0, + initial_water_vapour_mixing_ratio=self.formulae.constants.eps + / ( + settings.p0 + / settings.RH0 + / self.formulae.saturation_vapour_pressure.pvs_water(settings.T0) + - 1 + ), + T0=settings.T0, + w=settings.w, + z0=settings.z0, + ) + builder = Builder( + backend=backend( + formulae=self.formulae, override_jit_flags={"parallel": False} + ), + n_sd=settings.n_sd, + environment=env, + ) + + environment = builder.particulator.environment + builder.add_dynamic(AmbientThermodynamics()) + condensation = Condensation( + adaptive=settings.adaptive, + rtol_x=settings.rtol_x, + rtol_thd=settings.rtol_thd, + dt_cond_range=settings.dt_cond_range, + ) + builder.add_dynamic(condensation) + + products = [ + PySDM_products.ParticleSizeSpectrumPerVolume( + name="Particles Wet Size Spectrum", + radius_bins_edges=settings.r_bins_edges, + ), + PySDM_products.CondensationTimestepMin(name="dt_cond_min"), + PySDM_products.CondensationTimestepMax(name="dt_cond_max"), + PySDM_products.RipeningRate(), + PySDM_products.MeanRadius( + name="r_mean_gt_1_um", radius_range=(1 * si.um, np.inf) + ), + PySDM_products.ActivatedMeanRadius( + name="r_act", count_activated=True, count_unactivated=False + ), + PySDM_products.Time(name="t"), + ] + + attributes = environment.init_attributes( + n_in_dv=settings.n, kappa=settings.kappa, r_dry=settings.r_dry + ) + + self.particulator = builder.build(attributes, products) + + self.n_steps = settings.n_steps + + def save(self, output): + _sp = self.particulator + cell_id = 0 + output["r_bins_values"].append( + (_sp.products["Particles Wet Size Spectrum"].get()).T + ) + volume = _sp.attributes["volume"].to_ndarray() + output["r"].append((self.formulae.trivia.radius(volume=volume)).T) + output["S"].append(_sp.environment["RH"][cell_id] - 1) + output["t"].append(_sp.products["t"].get()) + for key in ("water_vapour_mixing_ratio", "T", "z"): + output[key].append(_sp.environment[key][cell_id]) + for key in ( + "dt_cond_max", + "dt_cond_min", + "ripening rate", + "r_mean_gt_1_um", + "r_act", + ): + output[key].append(_sp.products[key].get()[cell_id].copy()) + + def run(self): + output = { + key: [] + for key in ( + "r", + "S", + "z", + "t", + "water_vapour_mixing_ratio", + "T", + "r_bins_values", + "dt_cond_max", + "dt_cond_min", + "ripening rate", + "r_mean_gt_1_um", + "r_act", + ) + } + + self.save(output) + for _ in range(self.n_steps): + self.particulator.run(self.n_substeps) + self.save(output) + for k, v in output.items(): + output[k] = np.asarray(v) + return output diff --git a/PySDM/source/examples/PySDM_examples/Zaba_et_al/__init__.py b/PySDM/source/examples/PySDM_examples/Zaba_et_al/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3773e97f96f3bc1f01f9fd2d08a23904865e9b08 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Zaba_et_al/__init__.py @@ -0,0 +1,7 @@ +""" +global_meteoric_water_line.ipynb: +.. include:: ./global_meteoric_water_line.ipynb.badges.md + +timescales_comparison.ipynb: +.. include:: ./timescales_comparison.ipynb.badges.md +""" diff --git a/PySDM/source/examples/PySDM_examples/Zaba_et_al/global_meteoric_water_line.ipynb b/PySDM/source/examples/PySDM_examples/Zaba_et_al/global_meteoric_water_line.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..568c8e8dbf456e03b9005f4e986810462a70e407 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Zaba_et_al/global_meteoric_water_line.ipynb @@ -0,0 +1,5548 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "490fce21c0ca4207", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Zaba_et_al/global_meteoric_water_line.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Zaba_et_al/global_meteoric_water_line.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Zaba_et_al/global_meteoric_water_line.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "43c65ccf2810efd1", + "metadata": {}, + "source": [ + "## Global Meteoric Water Line\n", + "GMWL is a linear relation between isotopic deltas for oxygen-18 and deuterium originating from [Craig 1961](https://doi.org/10.1126/science.133.3465.1702). Here, we explore how the linear relationship coefficients can be linked with equilibrium fractionation factors $\\alpha(T)$." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "6d10078ca20ca3", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-04T17:00:00.275675Z", + "start_time": "2025-03-04T17:00:00.270292Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "15b88e60-9744-4c62-984a-e1939b4ed93e", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-04T17:00:02.300940Z", + "start_time": "2025-03-04T17:00:00.377959Z" + } + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "from PySDM import Formulae\n", + "from PySDM.physics.constants import si, in_unit, PER_MILLE\n", + "from matplotlib import pyplot\n", + "from open_atmos_jupyter_utils import show_plot" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "eb6cad0de94412", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-04T17:00:02.341477Z", + "start_time": "2025-03-04T17:00:02.321357Z" + } + }, + "outputs": [], + "source": [ + "f = {}\n", + "alphas = {}\n", + "variants = {\n", + " \"HoritaAndWesolowski1994\",\n", + " \"Majoube1971\",\n", + " \"VanHook1968\"\n", + "}\n", + "for variant in variants:\n", + " f[variant] = Formulae(isotope_equilibrium_fractionation_factors=variant)\n", + " alphas[variant] = {}\n", + " alphas[variant]['18O'] = f[variant].isotope_equilibrium_fractionation_factors.alpha_l_18O\n", + " alphas[variant]['2H'] = f[variant].isotope_equilibrium_fractionation_factors.alpha_l_2H" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "aa1eb33a-5250-40dd-9cb6-2f9955ab573e", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-04T17:00:02.348231Z", + "start_time": "2025-03-04T17:00:02.345731Z" + } + }, + "outputs": [], + "source": [ + "def a_coeff(alpha_D, alpha_O, B):\n", + " \"\"\" slope coefficient assuming isotopic equilibrium with SMOW \"\"\"\n", + " return (alpha_D - 1 - B) / (alpha_O - 1)\n", + "\n", + "def b_coeff(alpha_D, alpha_O, A):\n", + " \"\"\" intercept coefficient assuming isotopic equilibrium with SMOW \"\"\"\n", + " return alpha_D - 1 - A * (alpha_O - 1)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "8d3f24ff-7692-477c-af53-1aff4ad6eab7", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-04T17:00:03.118540Z", + "start_time": "2025-03-04T17:00:02.362296Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-03-05T01:01:53.709671\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "1461c6ab79d945c2a8a2971f9f419312", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_Majoube1971.pdf
\"), HT…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-03-05T01:01:56.372197\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "0a6bc6b2ade449269ecbb7ed1a98b6e2", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_VanHook1968.pdf
\"), HT…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-03-05T01:01:59.684620\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "86a96580ebaf42da98f0abad9ffff763", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_HoritaAndWesolows…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "T = np.linspace(280, 290) * si.K\n", + "thick = 2\n", + "thin = 1\n", + "lines = {}\n", + "for variant in variants:\n", + " const = f[variant].constants\n", + " lines[variant] = {}\n", + " fig, axs = pyplot.subplots(1, 2, figsize=(9, 2))\n", + " for db in (5 * PER_MILLE, 0, -5 * PER_MILLE):\n", + " b = const.CRAIG_1961_INTERCEPT_COEFF + db\n", + " label = f\"b={in_unit(b, PER_MILLE):.3g}\"\n", + " lines[variant][label] = a_coeff(alphas[variant]['2H'](T), alphas[variant]['18O'](T), b)\n", + " axs[0].plot(T, lines[variant][label], label=f\"b={in_unit(b, PER_MILLE):.3g}‰\", linewidth=thick if db==0 else thin)\n", + " axs[0].set_ylabel('a coefficient')\n", + " axs[0].hlines(const.CRAIG_1961_SLOPE_COEFF, xmin=T[0], xmax=T[-1], color='k', linewidth=thick, linestyle='--')\n", + " \n", + " for da in (-.5, 0, .5):\n", + " a = const.CRAIG_1961_SLOPE_COEFF + da\n", + " label = f'{a=}'\n", + " lines[variant][label] = in_unit(b_coeff(alphas[variant]['2H'](T), alphas[variant]['18O'](T), a), PER_MILLE)\n", + " plot_b = axs[1].plot(T, lines[variant][label], label=label, linewidth=thick if da==0 else thin)\n", + " axs[1].set_ylabel('b coefficient [‰]')\n", + " axs[1].hlines(in_unit(const.CRAIG_1961_INTERCEPT_COEFF, PER_MILLE), xmin=T[0], xmax=T[-1], color='k', linewidth=thick, linestyle='--')\n", + " \n", + " for ax in axs:\n", + " ax.legend()\n", + " ax.grid()\n", + " ax.set_xlabel('temperature [K]')\n", + " fig.suptitle(variant)\n", + " show_plot(f'fig_{variant}.pdf')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dcc88407f5d5cb63", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-04T17:00:03.123429Z", + "start_time": "2025-03-04T17:00:03.121568Z" + } + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/PySDM/source/examples/PySDM_examples/Zaba_et_al/timescales_comparison.ipynb b/PySDM/source/examples/PySDM_examples/Zaba_et_al/timescales_comparison.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..5232a4ccdf9a340d0b1ffb62d5684996696ba52f --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/Zaba_et_al/timescales_comparison.ipynb @@ -0,0 +1,5223 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "da62f9aa77468520", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/Zaba_et_al/timescales_comparison.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/Zaba_et_al/timescales_comparison.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/Zaba_et_al/timescales_comparison.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "382963a1f97b851e", + "metadata": {}, + "source": [ + "# Isotopic relaxation (e-folding) timescales comparison \n", + "1. Comparison for different environment settings.\n", + "2. Equations for timescales from:\n", + "- [Bolin 1958 (Proc. 2nd UN Intl Conf. Peaceful Uses of Atomic Energy)](https://digitallibrary.un.org/record/3892725),\n", + "- [Gedzelman & Arnold 1994 (J. Geophys. Res. Atmos. 99)](https://doi.org/10.1029/93JD03518), \n", + "- [Miyake et al. 1968 (Pap. Meteorol. Geophys. 19)](https://doi.org/10.2467/mripapers1950.19.2_243),\n", + "- [Jouzel et al. 1975 (J. Geophys. Res. 80)](https://doi.org/10.1029/JC080i036p05015) \n", + "\n", + "\n", + "compared with derived formula from Fick's an Fourier laws.\n", + "\n", + "\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "f508348dd491651d", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-27T19:16:31.278096Z", + "start_time": "2025-06-27T19:16:31.275258Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "129521104d738ea8", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-27T19:16:33.005419Z", + "start_time": "2025-06-27T19:16:31.360076Z" + } + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "from matplotlib import pyplot\n", + "from functools import partial\n", + "from open_atmos_jupyter_utils import show_plot\n", + "\n", + "from PySDM import Formulae\n", + "from PySDM.physics import si, in_unit\n", + "from PySDM.physics.constants import PER_MILLE, PER_CENT" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "c680144e7ad3b94a", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-27T19:16:33.225362Z", + "start_time": "2025-06-27T19:16:33.016542Z" + } + }, + "outputs": [], + "source": [ + "formulae = Formulae(\n", + " terminal_velocity=\"RogersYau\",\n", + " particle_shape_and_density=\"LiquidSpheres\",\n", + " ventilation=\"PruppacherAndRasmussen1979\",\n", + " isotope_equilibrium_fractionation_factors=\"VanHook1968\",\n", + " isotope_diffusivity_ratios=\"HellmannAndHarvey2020+GrahamsLaw\",\n", + " isotope_relaxation_timescale=\"ZabaEtAl\",\n", + ")\n", + "CONST = formulae.constants\n", + "T_10C = formulae.trivia.C2K(10)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "c48e16c325b074bd", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-27T19:16:33.233767Z", + "start_time": "2025-06-27T19:16:33.231813Z" + } + }, + "outputs": [], + "source": [ + "isotopes = (\"2H\", \"18O\", \"17O\", \"3H\")\n", + "isotopes_attr = {\n", + " isotope: {\n", + " \"alpha\": getattr(\n", + " formulae.isotope_equilibrium_fractionation_factors, f\"alpha_l_{isotope}\"\n", + " ),\n", + " \"D_ratio_heavy_to_light\": getattr(\n", + " formulae.isotope_diffusivity_ratios, f\"ratio_{isotope}_heavy_to_light\"\n", + " ),\n", + " \"VSMOW\": getattr(CONST, f\"VSMOW_R_{isotope}\"),\n", + " \"latex_label\": f\"$^{{{{{''.join(filter(str.isdigit, isotope))}}}}}{''.join(filter(str.isalpha, isotope))}$\"\n", + " }\n", + " for isotope in isotopes\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "c7e3ba154814221f", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-27T19:16:34.483980Z", + "start_time": "2025-06-27T19:16:33.240085Z" + } + }, + "outputs": [], + "source": [ + "pressure = CONST.p_STP\n", + "temperature = T_10C\n", + "D_light = formulae.diffusion_thermics.D(temperature, pressure)\n", + "rho_s = (\n", + " formulae.saturation_vapour_pressure.pvs_water(temperature) / CONST.Rv / temperature\n", + ")\n", + "\n", + "\n", + "def vent_coeff(r, T, p):\n", + " eta_air = formulae.air_dynamic_viscosity.eta_air(T)\n", + " air_density = p / CONST.Rd / T\n", + " Sc = formulae.trivia.air_schmidt_number(\n", + " dynamic_viscosity=eta_air,\n", + " diffusivity=D_light,\n", + " density=air_density,\n", + " )\n", + " Re = formulae.particle_shape_and_density.reynolds_number(\n", + " radius=r,\n", + " velocity_wrt_air=formulae.terminal_velocity.v_term(r),\n", + " dynamic_viscosity=eta_air,\n", + " density=air_density,\n", + " )\n", + " return formulae.ventilation.ventilation_coefficient(\n", + " formulae.trivia.sqrt_re_times_cbrt_sc(Re, Sc)\n", + " )\n", + "\n", + "tau_fun = partial(\n", + " formulae.isotope_relaxation_timescale.tau,\n", + " rho_s=rho_s,\n", + " Fk=formulae.drop_growth.Fk(\n", + " temperature,\n", + " K=formulae.diffusion_thermics.K(temperature, pressure),\n", + " lv=formulae.latent_heat_vapourisation.lv(temperature),\n", + " ),\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "3b24504da41e588c", + "metadata": {}, + "source": [ + "## Comparison for different environment settings" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "f40e7ef104f39549", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-27T19:16:38.674206Z", + "start_time": "2025-06-27T19:16:34.501037Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-01T15:18:48.371884\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "9d48535beb394c3cb5fc74b7dcbff799", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_isotopes.pdf
\"), HTML(val…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "saturation = 1\n", + "\n", + "radii = np.linspace(0.05, 0.2) * si.cm\n", + "temperature = T_10C\n", + "\n", + "for isotope in isotopes:\n", + " R_vap = isotopes_attr[isotope][\"VSMOW\"] * 3\n", + " alpha = isotopes_attr[isotope][\"alpha\"](temperature)\n", + " R_liq = 0.4 * R_vap * alpha\n", + " D = D_light * vent_coeff(r=radii, T=temperature, p=pressure)\n", + " pyplot.plot(\n", + " in_unit(radii, si.cm),\n", + " tau_fun(\n", + " alpha=alpha,\n", + " D_iso=np.asarray(isotopes_attr[isotope][\"D_ratio_heavy_to_light\"](\n", + " temperature\n", + " )) * D,\n", + " D=D,\n", + " radius=radii,\n", + " S=saturation,\n", + " R_vap=R_vap,\n", + " R_liq=R_liq,\n", + " ),\n", + " label=isotope,\n", + " )\n", + "pyplot.title(f\"e-folding timescales for S={saturation:.3g}\")\n", + "pyplot.gca().set(\n", + " xlabel=\"Radius [cm]\",\n", + " ylabel=\"$\\\\tau$ [s]\",\n", + ")\n", + "pyplot.legend()\n", + "pyplot.grid()\n", + "show_plot(\"fig_isotopes\")" + ] + }, + { + "cell_type": "markdown", + "id": "614a63477f43a0a4", + "metadata": {}, + "source": [ + "# Timescales comparison\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "f9f0586f1f1b5654", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-27T19:16:38.718298Z", + "start_time": "2025-06-27T19:16:38.716462Z" + } + }, + "outputs": [], + "source": [ + "radii = np.asarray((0.01, 0.05, 0.1, 0.2)) * si.cm\n", + "\n", + "isotope = \"18O\"\n", + "alpha = isotopes_attr[isotope][\"alpha\"](temperature)\n", + "VSMOW = isotopes_attr[isotope][\"VSMOW\"]\n", + "R_liq = 100 * VSMOW * alpha\n", + "\n", + "tau_max = 5000 * si.s" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "8725f8626044723f", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-27T19:16:40.190413Z", + "start_time": "2025-06-27T19:16:38.741370Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-01T15:18:57.448779\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ee11c16986424b679a9d5842fc24bcf0", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_vapour.pdf
\"), HTML(value=\"…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "saturation = 1\n", + "R_vapour = np.linspace(VSMOW / 10, 100 * VSMOW)\n", + "\n", + "for radius in radii:\n", + " f = vent_coeff(r=radius, T=temperature, p=pressure)\n", + " D = D_light * f\n", + " tau = abs(tau_fun(\n", + " alpha=alpha,\n", + " D_iso=np.asarray(isotopes_attr[isotope][\"D_ratio_heavy_to_light\"](\n", + " temperature\n", + " )) * D,\n", + " D=D,\n", + " radius=radius,\n", + " S=saturation,\n", + " R_vap=R_vapour,\n", + " R_liq=R_liq,\n", + " ))\n", + " pyplot.plot(R_vapour, tau, label=in_unit(radius, si.cm))\n", + "pyplot.gca().set(\n", + " title=f\"Relaxation timescales for {isotope}, {R_liq=:.3g}\",\n", + " xlabel=\"$R_{vapour}$\",\n", + " ylabel=\"$\\\\tau$ [s]\",\n", + " ylim=(0, tau_max),\n", + ")\n", + "\n", + "pyplot.legend(title=\"Radius [cm]\")\n", + "pyplot.grid()\n", + "plot_R_vap = pyplot.gcf()\n", + "show_plot(\"fig_vapour\")" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "a2b0368fdf75cc6d", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-27T19:16:40.447560Z", + "start_time": "2025-06-27T19:16:40.207863Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-01T15:18:58.781062\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ee893cde9e664c30b132ebeb79166b2f", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig_saturation.pdf
\"), HTML…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "R_vapour = VSMOW\n", + "saturation = np.linspace(0.1, 1.1)\n", + "\n", + "plots_S = []\n", + "for radius in radii:\n", + " f = vent_coeff(r=radius, T=temperature, p=pressure)\n", + " D = D_light * f\n", + " pyplot.plot(\n", + " saturation,\n", + " np.abs(\n", + " tau_fun(\n", + " alpha=alpha,\n", + " D_iso=np.asarray(isotopes_attr[isotope][\"D_ratio_heavy_to_light\"](\n", + " temperature\n", + " )) * D,\n", + " D=D,\n", + " radius=radius,\n", + " S=saturation,\n", + " R_vap=R_vapour,\n", + " R_liq=R_liq,\n", + " )\n", + " ),\n", + " label=in_unit(radius, si.cm),\n", + " )\n", + "pyplot.gca().set(\n", + " title=f\"Relaxation timescales for {isotope}, {R_liq=:.3g}, {R_vapour=:.3g}\",\n", + " xlabel=\"S []\",\n", + " ylabel=\"$\\\\tau$ [s]\",\n", + " ylim=(0 * si.s, tau_max)\n", + ")\n", + "pyplot.legend(title=\"Radius [cm]\")\n", + "pyplot.grid()\n", + "plot_S = pyplot.gca()\n", + "show_plot(\"fig_saturation\")" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "e98fba6cd2283e10", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-27T19:16:40.467889Z", + "start_time": "2025-06-27T19:16:40.465513Z" + } + }, + "outputs": [], + "source": [ + "def my_label(factor, isotope_name):\n", + " return f\"{factor} {isotope_name}\"" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "26ebb2b3250d3b19", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-27T19:16:40.482466Z", + "start_time": "2025-06-27T19:16:40.480373Z" + } + }, + "outputs": [], + "source": [ + "N = 50\n", + "isotopes_attr[\"2H\"][\"deltas_liq\"] = np.linspace(-60.0, 40.0, N) * PER_MILLE\n", + "isotopes_attr[\"18O\"][\"deltas_liq\"] = np.linspace(-20, 20, N) * PER_MILLE\n", + "isotopes_attr[\"3H\"][\"deltas_liq\"] = np.linspace(-10, 1000, N) * PER_MILLE" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "1a552613b8060b99", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-27T19:16:40.500468Z", + "start_time": "2025-06-27T19:16:40.495245Z" + } + }, + "outputs": [], + "source": [ + "bolins_table = (\n", + " (0, 0.005, 3.3, 0.27, 0.9),\n", + " (1, 0.01, 7.1, 0.72, 5.1),\n", + " (2, 0.025, 33, 2.1, 69),\n", + " (3, 0.05, 93, 4.0, 370),\n", + " (4, 0.075, 165, 5.4, 890),\n", + " (5, 0.1, 245, 6.5, 1600),\n", + " (6, 0.15, 365, 8.1, 3000),\n", + " (7, 0.2, 435, 8.8, 3800),\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "3fa8fadd0d618007", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-27T19:16:40.728332Z", + "start_time": "2025-06-27T19:16:40.557122Z" + } + }, + "outputs": [], + "source": [ + "bolin = (0.1 * si.mm, 7.1 * si.s)\n", + "\n", + "temperature = T_10C\n", + "radius = bolin[0]\n", + "saturation = np.linspace(0, 1, N)\n", + "f = vent_coeff(r=radius, T=temperature, p=pressure)\n", + "isotopes = (\"3H\", \"2H\", \"18O\")\n", + "\n", + "R_vapour_factors = (0.6, 0.88, 0.925, 0.98, 1, 1.5)\n", + "tau = {}\n", + "dR_dt = {}\n", + "for isotope in isotopes:\n", + " VSMOW = isotopes_attr[isotope][\"VSMOW\"]\n", + " delta, S = np.meshgrid(isotopes_attr[isotope][\"deltas_liq\"], saturation)\n", + " R_liq = formulae.trivia.isotopic_delta_2_ratio(delta, VSMOW)\n", + " for R_vapour_factor in R_vapour_factors:\n", + " D = D_light * f\n", + " D_iso = np.asarray(isotopes_attr[isotope][\"D_ratio_heavy_to_light\"](temperature)) * D\n", + " tau[my_label(R_vapour_factor, isotope)] = (\n", + " formulae.isotope_relaxation_timescale.tau(\n", + " rho_s=formulae.saturation_vapour_pressure.pvs_water(temperature)\n", + " / CONST.Rv\n", + " / temperature,\n", + " alpha=isotopes_attr[isotope][\"alpha\"](temperature),\n", + " D_iso=D_iso,\n", + " D=D,\n", + " radius=radius,\n", + " S=S,\n", + " R_vap=R_vapour_factor * VSMOW,\n", + " R_liq=R_liq,\n", + " Fk=formulae.drop_growth.Fk(\n", + " temperature,\n", + " K=formulae.diffusion_thermics.K(temperature, pressure),\n", + " lv=formulae.latent_heat_vapourisation.lv(temperature),\n", + " )\n", + " )\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "f1bf1b61ca1e1759", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-27T19:16:43.275184Z", + "start_time": "2025-06-27T19:16:40.737120Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "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j5977jlNnz7dfOzr66t27dqpfPnyunTpktatW6eoqCjFxsbq1Vdf1aVLl/Sf//wnx7FKt+58ffDBB3Xw4EHzuerVq6tFixby9PTU/v37tW3bNi1fvtxhw5pERUWpS5cu2rdvn7y9vdWqVStVqVJFkZGRWrduna5cuSJJmjdvnurUqaPx48dnWNfy5cs1cOBAJSUlSZJcXV3VsmVL1apVS1FRUdq8ebPOnTunoKAgTZ061SHHI926q7pLly46c+aMvLy89MADD6hatWqKiopScHCwVdmDBw+qffv2unjxoqRbd582a9ZM9evXV7FixXT+/Hlt2LBBkZGRunDhgjp27KiVK1da3UmdXVeuXFFcXJwkqXLlyqpfv77Kly8vb29vRUVF6eDBgwoJCZFhGNqzZ49at26t3bt3q3Tp0mnqSumFcOeddyo0NFRbt27VxIkT9d5776UpO3fuXP3vf/+TJHl6emrx4sUqVqxYmnLXr19XVFSUJKlcuXJq0KCBKleuLB8fH928eVPHjh3Ttm3blJiYqFOnTqlNmzYKCQlRzZo1reqpV6+eRo0apcjISH377beSJD8/Pw0aNCjHr93tbt68qQcffFDbtm0zn6tYsaJatWolX19fHTt2TJs2bVJSUpIuXryo7t27a8mSJXr88cezrNue50ZWBg8erNDQUK1Zs8bsddW+fft078K+5557crwf6VaviYceekhHjhyRv7+/2rRpY37OrVmzRjdv3lRcXJx69uypvXv3KiEhQR06dFBERITKlCmj1q1bq3Tp0jpz5ozWrl2rhIQERUREqEePHjp8+LCKFy+e4b7/85//aNy4ceYEy/7+/mrRooUqV66spKQk7d+/Xzt27JBhGPr111/Vtm1bbd68Wd7e3mnqGjhwoH7++Wfzcc2aNdW0aVOVKlVKCQkJunr1qvbu3Zth78HUnn/+eavPJFdXV919992qVauWvLy8dPXqVe3evdusKzY2Nk0d9jpvciIv21t/f3+NGjVKknT+/Hmz12PFihXVs2dPq7K1atWyenz58mW1bNnSqudbjRo1dO+998rT01MHDhww77Y/evSo2rVrp1WrVqlly5ZZxnXs2DGNGTNGN27ckJ+fn1q3bq2KFSvq+vXr2rBhQ7aPMz3Dhw/XggUL5OLionvvvVd169ZVcnKygoODdfjwYUlSSEiIBg0apN9//z3DepKSktS3b1/98MMP5nOVKlXSPffco7JlyyoqKkpbt27V8ePHlZiYqA8++EBXr17VzJkz063Pnu+9gQMHauXKlZKkRYsW6eWXX87ydVm0aJG5/Pjjj8vLy0uS5ObmpqeeekoffvihJGnOnDlZ/i+TkpKsesYNGzYsy/3nxo4dO/T4448rMTFRklSsWDHdd999CgwMlKenpyIiInT8+HHt3btXN2/ezLSupUuXasCAAVbfgR544AHVrFlTUVFR2rhxo9k7YvHixTp58qTWrl1rvl6S9fmV1fdY6db7JjVHtok3btzQQw89pJMnT8rT01Nt27ZVlSpVFBoaqnXr1pm9tmbNmqXY2Fiz3U9PdHS0OnfurM2bN5vP1ahRQ82bN1fJkiUVFhamzZs368KFC4qJidHo0aMVERGh119/PU1dZ8+eVefOnc3Xyt3dXXfffbdq1qwpb29vRUdH69SpU9qzZ48iIiKyPE4AANLl1DQNAKBAmzNnjnnXlr+/vzFr1iwjPj4+Tbm1a9da3QH28ccf2y0GW+7sT7F06VKrO82GDBli3Lhxw6rMjRs3jIEDB1qV++GHH9Ktz9Y7gYcOHWqW8/DwMObMmZOmzNatW81j8fDwyPQuQsPI/t3lKT0bBg8ebISGhlqVi46ONvr372+W9fX1NaKiotKt88qVK0bp0qXNsk2bNjWOHDliVSY5Odn48ssvDVdXV6seFfbuUeLm5mZIMh5//HHjypUrVuWSkpLM92JUVJTVnZEPPfSQcezYsTR137hxw3j22Wet7sLM6E53W953P//8s/Hhhx8aR48ezfB4Tpw4YXTu3Nms6+mnn870+JcvX26WdXFxSdPz5ciRI4aPj49ZZsqUKRnWFRwcbLz++uvG3r17Myxz+fJl48knnzTra9++fYZlc9J7yNZtUv9fXF1djc8//zxN744jR44YzZs3t/pMyuh/44hzIztsOX9zsk16xxUUFGRERERYlTt79qxRt25ds+zgwYON5s2bGxaLxZg4cWKangj79u2zunt60qRJGcb5119/GS4uLuZn2UcffZTuHdW7du0y6tevb9b57LPPpimze/duq9c+s7uijx8/brz33nvGzz//nO76GTNmWH2u9+nTxzhz5ky6Zffu3Ws8//zzxurVq9Osc9Z548z29vYeF1lJ3VvBx8fHWLJkSZoy27dvN+64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OHFippNlTps2zepOYWfw9PS0+l+PHDnSHIvcFjn9kX3HHXeYy5nd0Xjz5k2rC0pZCQ4O1jvvvGM+njlzpipVqiTp1h2c77//vrnuueeey3CYIVvjS0pK0vDhwxUfH59lbCVKlDAv5l29etVuCYCgoCCz3pCQEM2cOTPDsuHh4XrllVfMx/3791fx4sXtEoc9lS5d2lzOzvsxv+vVq5dq1qwpSbp48aJGjhxp80X9qKioNOP52zp0U1JSki5evGg+Tt2rQbr1HkoZ2//06dOZfv5nxhHnjS0KUns7YsQIc3n58uVavXp1hmVPnz5t9Zk1YsSIQtPbaty4ceYQQMuXL7cpaZQivaGKHP3e6927t3nBfsOGDTp37pzVHFi2DruVonbt2mrTpo0k6cSJExo6dKi5rn///jZPGG4PcXFxaSZBz0jq4aRu/xwpUaKE+vbtaz5++eWXM+1ZO23aNO3du1fSre9PGbX12W0P8qJNfOmllzJNKL/00ktmIrxatWrq2LFjmjLjx483l6dOnaq//vory/2mSO8csLU9yOx/CABAVkiUAABypHjx4lYX9SdNmqTBgwdnOLaxYRjavHmzRo4cqapVq1qNIZ8XXFxc9NFHH5mPlyxZoqCgoDQ/niMjI/XUU0/pxx9/NJ/75JNPrO7mzI7atWtryJAh5uNhw4ZpwYIFacrt2LFDHTt2VHR0tNW8JvnNuHHjVKpUKUm3emh07tw5zZAKhmHoq6++0tixY63G1neWcePGmXebnz9/XnfddZf++9//mncx3+7atWuaOXOmmjVrpk8//TRH+3zkkUfM5QULFmjy5MlpLlYeO3ZMnTp1UkhIiE136kZGRmrgwIHmxLpDhw41J8lNMW7cOLVv394sP2DAgHQvkj788MPmBcn169frpZdeSnNOXrp0Sb169dJvv/1mU3yenp6qVauWpFt3xf70009ZbmOLGjVqWF18fe655zR9+vQ0/7+U1zMlOeTv76+3337bLjHYW8OGDc3lFStW2O2CurO5urpqxowZ5gXiefPm6eGHH9bBgwcz3Gb37t0aP368qlSpkiax9/LLL6t169b69ttvFR4enu72oaGhCgoKMhMl/v7+uv/++63KlCxZUh9//LH5+Ouvv1bfvn0znENp//79euGFF/THH39YPe+I88YWBam9bdeunVUPnscff1z//e9/05TbuXOnOnToYP5fq1Spoueffz6vwnS4GjVq6M033zQfDx06VC+99FKGF3sTExP1xx9/6Mknn7Qa4imFo997xYsXN3vBJCcna8yYMWbbXr58+RzdLBIUFGQupx7OKa8ncb948aKqVKmil156STt27Miw3J9//qkJEyaYj1O/j1O8/fbbZtL1yJEj6ty5szmJeIrk5GR98cUXGjt2rPncqFGjFBgYmO5+U7cH6Z0rt3N0m+jh4aGdO3eqR48eaW4WiY2N1fPPP2/1Pfb9999P9ztymzZtzJtrEhMT9fDDD+vDDz/MMGkVGxurn376SY8++qhVr5AUVatW1YgRI/T3339n+P1tx44dGj16tPk4vf8hAACZcXN2AACAgmvIkCE6ceKE3n33XUm3ho1atGiRmjRporp168rX11dRUVE6d+6cdu/eneUE047Wp08fbdiwwZygc/bs2Vq6dKnatWungIAAXblyRWvWrLH6ETdmzBg99thjudrvlClTtGXLFh0+fFhxcXEaMmSI3nnnHbVo0UKenp7av3+/tm3bJsMw9Nhjjyk0NFR///13rvbpKAEBAfrmm2/Ut29fJScna8eOHapbt65atWqlmjVrKjo6Wps2bTLv6Pv888/NH605TTbllq+vr37++Wd16NBBJ0+e1KVLl9SnTx+VKVNG9913n8qXLy/DMBQWFqYDBw7o6NGj5o/wnA651qlTJ7Vu3VobNmyQYRh66aWXNH36dDVr1kzFixfX0aNH9c8//ygpKUmVKlXSCy+8YHXXZ3pGjx6t48ePS5Jq1qypL7/8Mk0Zi8WiBQsW6M4771RYWJi2bNmid999VxMnTrQqV7duXT355JPm8FyTJ0/W4sWLdffdd6tcuXI6deqUNmzYoPj4ePn5+enTTz/Nct4T6VaPgg8++EDSrQmC58+fr5o1a1pNPvvZZ59lWc/tPvvsM+3YsUPbt29XYmKinnvuOX300Ud64IEH5Ovrq+PHj2vDhg1mUsjNzU1z5szJ8MKUsz300EMqVqyYYmJitHv3btWrV09t27ZViRIlzIuhnTp1UqdOnZwcafZ16NBBM2bM0LPPPqukpCStXLlSq1atUv369XXnnXfK399fN2/e1MWLF7Vnzx5dvXo1w7oMw9DGjRu1ceNGubq6qm7duqpXr55KliypmJgYnT9/Xps3b7ZKNH322WdWk7enGDlypPbt26cZM2ZIujWx9A8//KC7775btWvXlpeXl65evapdu3aZEx/fPpyMo84bWxSk9nbevHlq2bKljh8/rqioKPXp00e1atXSvffeKw8PDx04cEBbt241exv5+PhoyZIlZq+ZwmLChAk6deqUOTzS5MmTNXXqVN11112qUaOGvL29FRERoVOnTunff/81e1Sl7mGQIi/eewMHDjTnJ/nhhx/M5/v3728mP7Pj8ccf1/PPP2/V87RJkyZq3rx5tuvKrfDwcE2ePFmTJ09WqVKl1LRpU1WqVEleXl66cuWK/v33X6uER+3atfXCCy+kqadGjRqaPXu2eRPCli1bVKdOHbVq1Uo1atRQVFSUNm7caNUz5L777tMnn3ySYWy9evXSN998I+nWEFU7d+5Us2bNrHrdPPvss6pRo4b52JFt4rPPPqsVK1Zo1apVCgwMVNu2bVWlShWFhoZq3bp1un79uln2iSee0IABAzKs65tvvtHFixf1xx9/KD4+Xq+//rree+893Xvvvapatao8PT0VHh6u48ePa9++fWYvlvTeIzExMZo5c6ZmzpwpPz8/NWnSRNWqVZOPj4+uXbumQ4cOaf/+/Wb5smXLpvnuAwBAlgwAAHJp6dKlRsWKFQ1JNv3dc889RmxsrF32Xa1aNbPekydP2rTNu+++a3h6emYao5eXl/HBBx9kWs/JkyfN8tWqVcu07Pnz54277ror0312797diIiIMNq0aWM+t27dunTrGzx4sFlm3rx56ZaZMGGCWWbChAlZvi7r1q0zy7dp0ybTsosWLTJ8fX0zPBZPT09j5syZxpEjR8znGjdunGUMWcnuMaUWGhpq9O7d27BYLDa9T0uUKGHMnz8/3bpsed9dunTJaNasWab7qF+/vrF//35j3rx55nODBw9OU9fSpUvN9W5ubsa2bdsyPdYffvjBLO/q6mr8888/acpER0cbnTp1yjS+ypUrG5s2bbL5vREeHm7UrVs30zpTy845FBkZafTp0yfL/1uFChWM33//PdO6HHlu2GrGjBmZvhdvj8sR57wtdaZmy2dTirVr1xq1atWyuV1o0KCBcf78eas6nnvuOZu39/PzM2bOnJnlMXz++eeGv79/lvVZLBZj9erVaba393mTnXPAMJzT3ubk/X/p0iXjwQcfzDK+mjVrZvl5lt33aXbl5Pgy+kxLz5dffmmU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+wEAhV+9evW0efNm9e7dW8HBwTp69Ki5rkOHDqpfv77Ndc2cOVMvvPCCYmNjJUnFixfXI488orvvvlvly5dXfHy8jh49quXLl2vv3r2KjIzUc889pyNHjuiLL76w+7EBQFHC0FsAgCJj9uzZMgxDUVFR2rVrl/r166fhw4erW7duiomJyXC71BescnqhKzvbAQDyp4iICD366KMKCwtT7969FRQUlGl52g8AKPzWrVunO++8Uzdu3NCqVat0/fp1nT9/Xt98843WrFmjli1b6o8//siyngkTJmjEiBGKjY2Vm5ub3n77bZ07d04LFy7U888/rz59+mjgwIGaNGmS9uzZoy+//FKurq6SpC+//FLz5s1z9KECQKFGogQAUOT4+PioSZMmmjNnjp5++mmtWrVKU6ZMybB86juCs3NnLxe6AKDwiI2NVffu3fXvv/+qffv2WrhwYZbb0H4AQOEWFhamxx9/XHFxcVq5cqU6d+6sEiVKqGLFiho+fLi++eYbxcbG6plnnlFSUlKG9UyfPl3vvPOOJMnX11d//PGHJk2aJF9f33TLWywWjR49WhMmTDCfe+ONNzK9+QsAkDkSJQCAIm3QoEGSpBUrVmRYZteuXeayrResEhMT9e+//0qSXF1d1bhx41xECQBwpoSEBPXq1Ut///237rvvPq1YscKmeUNoPwCgcPvtt98UFhamFi1aqFq1amnW9+rVSx4eHjp58qROnDiRbh179uwxh3R0cXHRsmXL1K5dO5v2//LLL6t06dKSpIsXL2rlypU5PBIAAIkSAECRVrZsWUnS1atX011/8uRJhYWFSZIqVqyogIAAm+rdv3+/4uLiJEl169aVt7e3HaIFAOS15ORkDRw4UL///rsaN26s33//XT4+PlluR/sBAIXfuXPnJN2aSyQ9bm5uZpuR0iakZhiGBg0aZH7uv/jii3rooYds3r+Xl5c6duxoPl63bp3N2wIArJEoAQAUaevXr5ck1axZM931jC8PAEWXYRgKCgrSsmXLVKdOHf3xxx8qWbKkTdvSfgBA4VehQgVJtz67ExMT06w/evSorl+/LkkKDAxMs/7HH380exGWKVNGEydOzHYMqSeLP336dLa3BwDcQqIEAFCo7dy5U8uXL0/3h8uvv/6qN954Q5I0bNiwDLdPkdMLXdkZlx4AkH+MGzdOc+fOVfXq1bVm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NWjQQHv37g25w4wZxwwUFSWAzd12221hhyTffPON1q1bp1GjRik5OdnrmMLCQo0bN84TkiQlJenee+8Nb/L/VfVbwHb94Mhbv2fCE2cIpDKlKoIVAIGIVAWML3Y9vwIAAAB2F0hIIkmnnXaaunbtqs2bN6uoqEjbtm3TqaeeGvTxXnvtNc92XdUkCxYskLuG4uabb/YaWEiV7cJGjBihN998U0VFRfrggw90ww03BD0vs44ZKIISwMYmTpyoF198UVJlGd8dd9yhjRs3auPGjXW+74wzzqi2KNSBAwc0btw43XXXXbrkkkvUp08ftWvXTg0bNlRubq5Wr16tt99+W4cPH/Yca+bMmerQoYPhP5MTQhM3wpPYRLACwIqiGczY/fwNAAAAmKVqBY+79X0w9u7dq88++0ySVK9ePV1//fU+xy5ZssSzPXjw4Dr3O3jwYL355pue94UaWphxzEARlAA29tVXX3m2XS6X7r///oDe98Ybb2j06NG1ni8oKNCiRYvqXBgqIyNDM2fO1JAhQ4Keb7BqfqjjhA9eCE9QE8EKAKehWgZAtGw/VKik4jizp+EV1/gAgGAdPXpUW7Zs8Tw+8cQTg97H7NmzVV5eLkn63e9+V2tBeDeXy6X169dLkhISEnT66afXud+zzjrLs71u3bqg52XWMYNBUAJAF198sT744AN99913+ve//63du3fr8OHDysnJUYMGDdSiRQudccYZGjJkiEaMGOGzPVekOanapCrCEwQj2GBFIlwB4GzRDmYkZ12HAIgMb9f4MB73TUBsKy4ulkvSUVeFqfMokzHHf+utt5SbmyupshtMRkZG0Pt44403PNt1td3avXu3ioqKJElt27ZVvXr16txvu3btlJCQoPLycm3dulUul0txccF9WcGMYwaDoASwseXLlxuyn9TUVA0dOlRDhw41ZH/R4NTQxM3XjRU3AghFKOGKRMACAL4QzgCANRBIRR/3pLCSSy+9VJL0bNlOcyci6QSdENb7s7Kyqq0FPHHixKD38eWXX2rbtm2SKtf4GDRokM+xOTk5nu3mzZv73Xe9evXUqFEjHTlyRGVlZSosLPS5voiVjhkMghIAtufEFl2+EKAgmkINWNwIWgDAOGaEMzU5+RoLABAYwil74DMCezl69KiGDx+ugwcPSpKGDRumK664Iuj9vP76657t0aNHKyEh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ACYoLy/XL7/8olmzZum2227TueeeqwYNGiguLk5xcXEaPXp00Pvctm2b/vznP6tnz55q3LixUlNT1bVrV40fP14//fRTUPsqLS3Vyy+/rP79+6tVq1ZKSkpS27ZtNWTIEM2dO1cVFRVBzw8AAAAAAAAArCjR7AkAsWjEiBF67733DNvfjBkzdOedd6q4uLja81u2bNGWLVs0ffp0Pfzww3r44Yf97mvTpk0aPny4NmzYUO35vXv3au/evfr44481ffp0vfPOO2rZsqVhPwMAAAAAAAAAmIGgBDBBeXl5tcdNmzZVs2bNtHXr1qD3NXfuXI0bN06SFB8fr5EjR2rAgAFKTEzUqlWrNHv2bJWWlmrSpElKSkrSvffe63NfmZmZGjRokHbt2iVJOvXUU3XDDTeodevW2r59u1577TVt375dX331lYYMGaIvv/xSDRs2DHrOAAAAAAAAAGAVBCWACc4++2x1795dffr0UZ8+fdSxY0fNmjVLN954Y1D7ycrK0vjx4yVVhiSLFi3S0KFDPa+PGjVKN954owYMGKCioiJNnDhRw4YNU9euXb3ub8KECZ6QZOTIkZozZ44SE4//M3H77bfrsssu05dffqkff/xRTz31lKZMmRLsjw8AAAAAAAAAlsEaJYAJHnjgAT355JO68sor1bFjx5D388wzzygvL0+SNH78+Gohids555zjCTOOHTumyZMne93Xhg0btGDBAklSq1at9Oqrr1YLSSQpNTVV8+bNU3JysiTpueeeU05OTsjzBwAAAAAAAACzEZQANuYONiTp//7v/3yOGzt2rKdF1uLFi2utZeLel8vlkiTdfPPNSk1N9bqvNm3aaMSIEZKkoqIiffDBByHPHwAAAAAAAADMRlAC2NSGDRv066+/SpK6d+9eZ2VKWlqaLrzwQklSYWGhvvzyy1pjlixZ4tkePHhwnceu+nrV9wEAAAAAAACA3RCUADa1bt06z/ZZZ53ld3zVMVXfK0kul0vr16+XJCUkJOj0008PeV8AAAAAAAAAYCcEJYBNbd682bMdyDonVcdUfa8k7d69W0VFRZKktm3bql69enXuq127dkpISJAkbd261dOyCwAAAAAAAADsJtH/EABWVHUR9ebNm/sd36xZM6/vDWVf9erVU6NGjXTkyBGVlZWpsLDQ55ompaWlKi0t9TyuqKhQdna2mjVr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82ZJUrt27XTJJZcE/N6LL75YH3zwgR544AFdfPHF6tq1q5o3b67ExEQ1atRInTp10ogRIzR79mzt2LHDciFJoPbs9112zDoloaMFFwAACIdd23DRiguA09C+C0Asc1RFyYYNG3TNNdcoISFBf//7332OS05O1k033aQHH3xQx44d03vvvaf77rsvijMFjHf++efL5XKF9N7U1FQNHTpUQ4cONXhWCJSdq0okKksAAIC1RKOyRKK6xGxbDuSpXkqFJKlbq0YmzwZwLipQAMQCRwUlY8aMUUlJiUaMGKETTzyxzrHdu3f3bFNRAtiPu01DtG6C4R9hCQAACJXRLbik2ApLJHu1no0EbxXihCdAZBGgAHASxwQln332mb777jtJ0pVXXul3fFxcnGe7pKQkYvMC4Ezbsgq4+AMAADAQYYkB84jh6hJvaoYnBCdAdBCgALAjxwQl8+bN82xfeOGFfsfv3bvXs926deuIzAmAvWzKzDP15snu7bckqkoAAEB47B6WSLJMYEJYUhtVJ4C5CFAAWJljgpIVK1ZIkpo0aaKMjAy/49esWePZ7ty5c8TmBSB6Avkm3579BWqbwUVYJBGWAACAcNg5LJGsVV1CWOIfVSeA+QhQAFiBI4KSo0ePateuXZICqw5xuVz6/PPPPY/79esXqakBMFGkbw4j0X7LCVUlEmEJAAAID2GJQfOgFVfQqDoBrIMABUA0xZs9ASMcPnxYFRUVAY//4osv9Ouvv0qqrCbp2rWr57U33nhDcXFxuvrqq32+f8aMGYqLi9MNN9xQ7fl///vfuvfee3XOOeeodevWSk5OVseOHXXttddq06ZNtfZTXFysxMREtWrVSnv27NH48ePVqVMnJScnq1mzZho1apSysrK8zmHdunUaO3asOnbsqOTkZLVs2VLXXXedduzYUWvshx9+qLi4OI0aNcrrvl544QXFxcVp8uTJ1Z4/cuSI4uLidPLJJ6u4uFiPPvqoevTooaSkJJ155pk+/34AQKoMSwAAAEJVmp9t+D7z9m5R3t4thu/XG3crLitwByYIzabMvFp/AJhnW1aB1z8AEA5HVJQkJSV5tnfu3KmKigrFx3vPgFwulx566CHP4/Hjx1d7vVevXpKkDRs2eH1/YWGhJk2apJSUFD3++OPVXhs3bpw2bdqkXr166dxzz5XL5dLq1av11ltvafHixfrxxx/VpcvxbzD9/PPPKi8vV6NGjdSnTx8VFhaqf//+6tKli1asWKE5c+boP//5j1atWlXtOK+88oruuOMOlZWV6bzzztPZZ5+tX375RfPmzdOSJUv0zTffVDuOu83YGWec4fVn+vHHHyVJvXv3rvb8Tz/9JElq1aqVzj77bO3atUv9+vVT9+7ddfLJJ3vdF2B3Zq9TIjmnqkSisgQAAIQnEpUlUuyuWyJRXWIUKk8A66krLKEKBYA/jghKmjRpombNmunw4cMqLCzUkiVLNGTIEK9jJ0+erG+//VaSdNJJJ2ns2LHVXj/llFOUkJCgLVu26NixY0pMrP5X9Mwzz2j//v164IEH1LZt22qvTZw4UQMHDlRaWprnuYqKCt12222aNm2aZsyYoWeeecbz2urVqyVJW7Zs0cCBAzVv3jw1b97c89yZZ56pr7/+Wp9++qkGDRokSZo/f77++Mc/qmPHjnrvvfd02mmnSaoMgO688049//zzmjBhgj766CPPcdxBSZ8+fbz+nfgKStzvW7VqlS677DKtWLFCTZo08boPIFZFov2W0xCWAACAcNg9LJGs04pLIjCJJMITwLpo4wXAH0e03oqLi9PAgQM9j++44w7t3bu32piysjI9+OCDnvZS9erV02uvvaYGDRpUG5eSkqJOnTrp6NGj2rp1a7XXDhw4oGeeeUYnnHCC7r333lrzGD58eLWQRJLi4+M1evRoSfKso+LmDkq6deum999/3xOSSFKXLl0873OPO3TokG699VYlJSXp448/9oQk7r+DRx99VJL02WefqaysrNpx4uLiagUhUmX7r40bN6pJkyY68cQTq73mrijp1KmT3n77bUISIIp25hSbPQVD0YYLAACEIxJtuCRFrQ2XZK1WXBLtuKKFll2AtdHGC4CbIypKpMpqjnfffVdHjx7Vf/7zH/Xs2VNXXXWVOnTooP379+uDDz7wBBWJiYmaNWuWz0Xce/Xqpc2bN2vDhg3q3r275/lJkyapoKBAU6dOVaNGtb8VUlRUpE8//VSrV69WVlaWSktL5XK5tG/fPklS48aNq413ByCPPvqoUlJqt9np3LmzpMqARpJmzpyp3Nxc3XzzzdXWVXFr3LixmjdvrkOHDik/P19NmzZVdna2du3apS5dutQKcaTj7b+8hSjuipInnnhCDRvybSPYQyDf1tuzv0BtM/jWSLRRWQIAAMLhDkvsvsi7ZI1WXBLVJWbwFZZQeQJYC228gNjjmKCkR48emjdvnkaNGqXi4mLl5OTo1VdfrTXupJNO0ptvvqnzzz/f575OPfVUvfPOO1q/fr2GDx8uSdq0aZNee+01de3aVTfffHOt97z66qu65557lJOTU+cc3Y4ePapffvlFKSkpuvzyy72OLyysvGh1V5q422n94Q9/8HmM4uJixcfHe0KZUNtulZSUaNOmTUpJSdHgwYN9Hg8wk7sNg9E3t8GuUxKp9ltOWqvEjbAEAACEKxKtuKIZlkjWasUlVQYmqXxObyradgH2QYgCOJMjWm+5XXnllfrll190++23q1u3bkpNTVVycrLatWun4cOHa86cOdq8eXOdIYlUGZRI0vr16z3P3XvvvTp27JimTp1aa92S6dOn6+abb1ZaWppefvllbdq0SYWFhaqoqJDL5dKll14qSTr99NM971m3bp3Kysp06qmnqn597x8afvfdd5KOhxjr1q2rNr+adu7cqcLCQs86K5L/hdzd67VUnZsk/fLLLzp27JjOOussqklga7QUsB7acAEAgHBFohVX3t4tMd2KKzeryOwpoIaabbto3QVYn69WXrTzAqzPMRUlbieddJL+/ve/h7WPXr16SToelKxcuVKLFy9W3759a1V/uFwuPfLII0pISNCyZct08sknV3v98OHD+uKLLxQXF1ctrHC33fLWwkuSsrOz9cknnygtLU0DBgyQJOXn5ys+Pl716tXz+p533nlHkvTb3/7W85x7nRFvrbWOHj3qqVLxtZC7r0oUANHhxKoSicoSAAAQPqcs8i5ZpxUXrI/KE8C+WFAesDbHBSVG6Nixo9LS0rR161aVlZXpz3/+s+Li4vTMM8/UGnvw4EHt379frVu3rhWSSNL999+vsrIyderUqVoo4g5KduzY4XUOkydPVklJif785z971i9p2bKl9u/frx07dtQ61sGDB/XUU0+pfv36GjdunOd597osVReKd3vqqaeUk5OjpKSkamuxSP4rUQBUF6n2W05GWAIAAMLlhLBEsl4rLtgL4Qlgb7TyAqzBUa23jBIXF6eePXvq6NGjeuKJJ/Tdd99p5MiROvPMM2uNbdq0qZKSkpSZmelZ70OSysrK9Mgjj3jWSakZOLjHbtu2TfPmzfM873K59Oyzz+r555/XKaeconvvvdfzmnutkClTpqi8vNzz/Pbt2zVo0CAdPnxYTzzxhE466STPay1atJAkLV682PNcRUWFpk2bpilTpkiSevbsWaudmLsShYoSONWe/fYpe92ZU2z2FCKGNlwAACBckWjDJSmqbbikyrDEau24YF+07QKcgVZeQPRQUeJDr1699M0332jKlClKSkrSk08+6XVcvXr1dNNNN2natGk6//zzdckllyg5OVlff/214uPjdd1112nu3LnVgpJjx45p3bp1atSoka666ipdd911euWVV9S6dWv98MMP2r59uzp27Kj333+/2vogjzzyiP75z39q9uzZ+vrrr9W7d28dOnRIK1euVHl5uR566CHddddd1eZ35ZVX6t1339WkSZO0ePFitWrVSj///LMKCwt155136plnnqnVdquiokJr165Vw4YN1bVrV+P+UoEoMeIbecEu6I7QUVkCAADCFcnKEklUl8ARfIUl3PcA9kQlCmAsKkp8cC+YXl5erttvv10nnniiz7HPPfecJk6cqFatWunzzz/Xzz//rOuvv17r1q1TaWmppOoVJRs2bFBJSYlOP/10TZs2Tffcc4927typ999/X/Hx8brvvvu0evVqderUqdpx2rVrp3//+98aNWqUcnNz9cEHH2jr1q268sortXLlSj366KO15jZy5Ei98MIL6tKli9avX69ffvlFw4YN0/r16z0tvWoGJVu2bFFhYaFOO+00xcfzKwIEKpLf6HByVYlEZQkAAAhfpCpLJKpL4GxUnwDOU1clCtUogHdxLpfLZfYkYs2sWbN04403asKECXr22WfNng4QVXl5eWrcuLESe12ruITwqwjc3xys+S2/mt/CS2/RUDW1zaj7GxahfLMq0t/acOLC7lVRWQKEryA/T2d0aqvc3Nxq66M5gfsccqPaqT7f96nT6737mz0FwDSRqCxxi2ZliVu0qkvKS4u0edpIW5w/3OeDK6Z9oXopfGs6mqg+AZyv0wmpKi7I17i+PSx/TlixYoWu+e2l+qD/+abOY1Nunv5v0w4dPHjQ1HkgPNxhmsC9PgnrfwDGifa3/HzhmxnhobIEAACEy0mVJRLVJbAWqk8A59uWVaDthwrNngYQdQQlJli9erUkghLACOHcCPtb0N2KF/1Ob8ElEZYAAIDwleZnR3SRd7MCE8CKvIUnVryXAgCgLizmHmUV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qlevnjp37ux5ft26dXrmmWcUFxenGTNmBFQ1A5glGt/ysxuqSqqjugQAABghWtedVJeYj+oTOA2VKADgmyWCks2bN3u2vbWGqqnqmKrvtbojR45owYIFnsdDhgyJ2rFHjhwpqXKtkvHjx2vXrl06fPiwXnrpJT3//POSpN/97ndq0KCBJKmiokJjx47VsWPHNG7cOJ1//vlRmytgpki339qWFd2bK8KS2ghLAABAuErzs2nHFaO8hScEKHCCukIUghQAscASrbdycnI8282bN/c7vlmzZl7fa3V33XWXjhw5IkkaOnSoevXqFbVj33LLLZo9e7bWrFmjadOmadq0adVeT09P19NPP+15/NJLL+m7775T69at9dRTT0VtngCMx+LutbnDEtpxAQCAcERjoXeJdlx2QPsuOB0tvQA4nSUqSgoKjl9QJCcn+x2fknL8A7/8/PyIzMlor7zyit544w1JlaHE3//+96gePykpSZ9//rlGjBihuLi4aq+dc845+uqrr3TSSSdJkvbs2aMHH3xQkvT888+rcePGnudvvvlmtW3bVvXr11fbtm01duxY7d692+sxt2zZouuvv16tWrVSUlKS2rdvr1tuuUX79u2L4E8K1OZtQfecg4UmzKRStKtK4BvVJQAAIFzRqi6RaMdlR1SgIBZQiQLACSxRUeJ0//znP3XbbbdJkuLj4/XGG2+oQ4cOUZ9H06ZNtWDBAr344otau3atjh07ps6dO3sCErfx48crPz9fl19+uYYPHy5J2rhxo/r166eDBw8qLi5OzZo1U2ZmpmbOnKnFixdr+fLlnnVQJGnVqlW69NJLVVBQoPj4eDVt2lR79uzR9OnT9cEHH2j58uXq2rVrVH9+wEibMvNs9a0Zqkp8o7oEAAAYgeoSBIMKFMQKf2GJne6rAThbRIKSTZs2adOmTT5f79atm7p16+Z5nJqa6mlJVVJSotTUui8MiouP99xPS0sLc7aR9fnnn+vKK6/UsWPHPIuiDxs2zNQ5nXDCCRowYIDX19555x0tXrxYaWlpevHFFz3PX3vttTp48KBOO+00LVq0SB07dtSOHTs0bNgwrV27Vtdff71++OEHSZX//4wcOVIFBQXq16+f3nrrLbVq1Urbtm3TsGHDtH79el1//fX697//HZWfF7Gj6s1p3t4tId887tlfEPEblG1ZBep0QnRvgghL6kZgAgAAwuWuLCEwQagIUBBraOkFwCoiEpS8/fbbmjx5ss/XJ02apEceecTzOD093ROUHDp0yG9Qcvjw4WrvtaqlS5dq6NChKikpUVxcnF5++WXddNNNZk/Lp9zcXN1+++2SpCeffFJt27aVJH355Zdas2aNJOnll19Wx44dJUkdO3bUtGnTdMEFF+jHH3/UypUrdeGFF+qdd97Rnj17lJiYqFmzZqlVq1aSpE6dOmnatGnq27evvv/+e3311Ve64IILTPhJAWOEW1VCWGJN+wuOEpYAAICwEJjAaAQoiEVUowCIJkusUVK1BdOOHTv8jq86xqrtm5YuXarf/e53nuqXl156SePGjTN5VnW79957lZmZqXPOOUe33nqr5/kVK1ZIklq3bq1zzz232nvOP/98TxDiHrd8+XJJ0llnnaUTTzyx2vjf/OY3ysjIkFQZwADR4m2dkli1M6fY/6AYt7/gKOuXAACAsEVr7RKpMjBhDZPY42sNFNZBQSyoa20U1kcBEKyIBCWPPPKIXC6Xzz9Vq0kkqVevXp7t77//vs59HzhwwLN4eIsWLXTCCScYPv9wuUOSoqIiSdILL7xQLXiwolWrVmnGjBmqV6+eXn31VcXHH//V2Lt3ryR5KkxqateunaTKxd6rjnc/7288YJa6FnQP9MYi3IsvFna3NgITAAAQrmgu9i6x4DuOI0BBrCNIARAMSyzmfumll+rpp5+WJC1ZskT33HOPz7Eff/yxZ3vw4MERn1uwaoYkf//73/WnP/3J5FnV7ejRo7r55pvlcrl0zz33qGfPnl7HFRZ6/1C5oMD7hZav8b6eBxA9tOAKDuuXAACAcNGOC1ZRV1hCKy/EElp7AajKEq23+vbt62nHtHz5cq1evdrruPLycj3//POexyNHjozK/AK1fPnyaiHJ3/72N8+aH1b25JNPasOGDercubMmTpxY6/U2bdpIqmx55v7Z3IqKirRz585q49z/u379erlcrmrjCwsLa41HePr166e4uLiA/7j//uuybds2/fnPf1bPnj3VuHFjpaamqmvXrho/frx++umniP9M4aj6bb1ofZvOrlUltOAKHtUlAAAgXLTjgpVRhQIc568ihaoUwFksEZQkJCTo4Ycf9jweNWqUDh48WGvcfffd5/mQ9vzzz9egQYO87m/WrFmeD4X79esXiSnX8uWXX2rIkCHVQpI77rgjKscOx6ZNm/Tkk09KkqZPn67k5ORaYy688EJJlaHIjBkzqr02ffp0z8/8m9/8ptr/7ty5U++++2618S+//LJnfN++fQ38SWCUGTNm6NRTT9Uzzzyj9evXKy8vT4WFhdqyZYumTZumM888U48++qjZ0wwJ65TURlgSPNpxAQCAcJnRjovABOFgLRTAO4IUwDks0XpLksaOHatFixbpX//6l9avX6/TTjtNY8eOVY8ePZSdna358+frq6++kiSlp6dr+vTpYR9zx44deu2116o9t3btWs/2mjVralVY9O/fX/3796/23E8//VQtJBk0aJBOPPFEvf/++3Uev3nz5rrgggvC+AnC43K5NG7cOJWWlmrMmDG66KKLvI7r27evTj31VK1du1b33HOP9u3bpzPPPFM//vij/vrXv0qSTj/9dE9ActVVV+n+++9XZmambrjhBq1du1Y9e/bUd99956kI6tOnjyeAgXEWLVrkd0yLFi18vjZ37lyNGzdOkhQfH6+RI0dqwIABSkxM1KpVqzR79myVlpZq0qRJSkpK0r333mvY3M2Sc7BQ6S0aen1tz/6CqJWeb8sqUKcTzClzpw1XaGjHBQAAwhXNdlwSLbkQGb7CEtp4AbT3QnXl5eXauHGjfvjhB/3444/64Ycf9PPPP6u4uPJLrDfccINmzZoV8P5KSkq0YMECvffee/rpp5+UlZWlY8eOKT09Xd26ddOAAQM0ZswYn+tI11RRUaG33npL8+fP188//6ysrCw1bdpU3bt311VXXaUxY8YoKSkplB/dUsf0xTJBSWJiot59911dc801+uijj7R//35NmTKl1ri2bdtqwYIFOuWUU8I+5q+//qrHH3/c5+tr166tFpy45+ktKKm67sann36qTz/91O/x+/btq+XLlwc3aQPNnDlTK1asUIsWLfTMM8/4HBcXF6c5c+booosuUnZ2tmc9GbfmzZtrzpw5nscNGjTQvHnzdNlll6moqKjW/48tWrSoNh7GGTZsWMjvzcrK0vjx4yVVhiSLFi3S0KFDPa+PGjVKN954owYMGKCioiJNnDhRw4YNU9euXcOdtiNsysyz9QUOYUnoCEwAAEC4SvOzoxaWSAQmiA5/1SYEKUBgrbzt/FkDqhsxYoTee+89Q/b1008/acSIEdq6dWut17KyspSVlaWVK1fqqaee0tSpU/0uD7F//35deeWVWrVqVa3n9+/fr2XLlumll17Se++9py5djLl+MOOYdbFMUCJJaWlp+vDDD/XBBx/ozTff1Pfff6+DBw8qLS1NJ598sv73f/9X48aNU+PGjc2equ0dOHBA99xzj6TKNmFNmjSpc/ypp56q1atXa8qUKVqyZImysrLUvHlzDR48WA8//LDat29fbfxFF12kH374QY899pi++OILZWdnq2XLlhoyZIgeeugh1iexoGeeeUZ5eZUn6PHjx1cLSdzOOeccTZkyRXfddZeOHTumyZMn66233or2VKMqVqpKJMKScBGYAACAcES7ukQiMIG5WFQeCAxhinOUl5dXe9y0aVM1a9bMa9hRl927d6t///46cuSIpMovpY8ePVpdunRRUlKSdu7cqfnz52vDhg0qKSnRHXfcoQYNGugPf/iD1/0VFBTot7/9rWfJi5NOOkk33XSTTjrpJO3bt0+zZ8/W2rVrtX79eg0aNEjffvutWrZsGfxfgMnH9MdSQYnb5Zdfrssvvzzk948ePVqjR4/2O65fv361FhuP5PGspGXLlp7/mAJ14oknaubMmQGP7969u+bNmxfs1GCSBQsWeLb/7//+z+e4sWPH6uGHH1ZhYaEWL16s4uJipaRY68P1ur6Rl5OZqfRWrao/V0f7rWAYUVVCWGJ/BCYAACAcZgUmrmOlUTse4A8hChAcwhR7OPvss9W9e3f16dNHffr0UceOHTVr1izdeOONQe1n8uTJns91Bw4cqEWLFqlBgwbVxjz44IOaOHGinnjiCUnSAw88oNGjRysxsXYc8Pjjj3sCi379+unDDz9Uaurxf2tvu+02XX/99VqwYIF27typu+++O+xuQWYc0x9LLOYOwFwbNmzQr7/+Kqky4OrYsaPPsWlpaZ71ZQoLC/Xll19GZY6hYtHK4LHAuzFY9B0AAIQj2gu+A3bBwvJAaPwtPM/i85H3wAMP6Mknn9SVV15Z52dv/nzyySee7b/+9a+1QhKpcimFRx991FOFkZWVpU2bNtUal52drb/97W+SpOTkZM2dO7daYCFJ9erV08yZM9Xqv188njdvntd9BcqMYwaCoMQBfv31V8XFxXn+uH/R7Kh3796en8PX4vLw7bLLLlObNm1Uv359NWnSRKeccorGjh2rZcuW1fm+devWebbPOussv8epOqbqe50qmAtuIy4stmWZf4FPWGIcAhMAABAOAhMgcIQoQHjcgcmWA4QmVnbw4EHPdufOnX2OS0hI0EknneR5XFBQ+9/CDz74QCUlJZKkq6++2udyCampqRo7dqwkyeVyVetMEywzjhkIghLAQf75z39q3759KisrU05OjjZs2KCZM2eqf//+GjBggDIzM72+b/PmzZ7tQBLtqmOqvtfOcg4Wmj2FaghLnMcdmBCaAACAUBCY1JabVWS563hYV10hCkEKADtp0aKFZ3vLFt+dVMrLy/Wf//xHkpSYmKiuXbvWGrNkyRLP9uDBg+s8btXXq74vWGYcMxCWXKMEgZkxY4aKiopqPd+rVy8TZmOMv/71r8rNza31fLdu3UyYjX00adJEl1xyic4880y1adNGCQkJ2rt3r7744gstWbJELpdLS5cu1bnnnqtvv/1WGRkZ1d6fk5Pj2W7evLnf4zVr1szre70pLS1VaenxfsfuBePN5G2dEn+iuai7lbBmSWSwjgkQGCueQwDAbGasYWI2f+cDX2GJEesQInawLgoAuxg2bJheeuklSZXrDL///vu12m+5XC499NBDnuqTMWPGqEmTJrX2FUyXmTPOOEMJCQkqLy/XL7/8IpfLpbi4uKDnb8YxA0FQYmMDBw40ewqGo91W8J588kn16dNH9evX/sB1woQJ+uGHHzR8+HDt2rVLv/76q8aMGaOPP/642riqpXfJycl+j1l18fb8/Hy/85s8ebLffRqt6oLueXu3qFGbLlE7thGLukvmL+zuRlgSOQQmQN3MOocAgB3EUmAS6vmAAAVG8VdxQpACmKO4uFgVLpeKj5WbOo+SYxVRPd4jjzyizz77TFu3btW//vUvdezYUTfeeKO6dOmi+vXr69dff9X8+fO1fv16SdKoUaP097//vdZ+KioqtG3bNkmVbbratWtX53Hr1aunNm3aaNeuXSosLNTevXvVtm3boOZuxjEDRVAC2Ny5555b5+tnnnmmPvnkE51++ukqLS3VkiVL9P333we0FokR7r//fk2YMMHzOC8vz+8/gmbJOVho2E0TYQmCUbUdF6EJcJydziEAYJZYCEyMPh/U1a6LEAWhIEgBzHHppZdKki78pO61eaPhhBNOiNqxmjdvru+++05//OMf9c477+jgwYOaOnVqrXEXX3yxHnjgAZ9fTC8oKNCxY8ckSenp6UpM9B8VNGvWTLt27ZJU2WUm2NDCjGMGiqAEiAHdu3fX9ddfr5kzZ0qSPvroo2pBSWrq8Ys292JKdSkuPr52RVpaWp1jk5KSlJSUFOyULSlW22+5EZZEB1UmwHFOOocAQKQ5OTCJ5vmAKhREAm29ABitSZMmmjp1qk444QS98MILXscsXbpUcXFxatKkiXr37l3r9WA7zEjBdZnxxoxjBoqgBIgRF110kSco2bhxY7XX0tPTPduHDh3yu6/Dhw97fa+dhLJOSbCcVlUiEZZEE4EJAAAIhZMDEzNRhYJIoRoFCN0nn3yim0YM1yfX170geKRtyDqiPy37OarHfOaZZ3TfffepvLxc119/vW699Vb16tVL9erV0/bt27Vw4UI99dRT+te//qULL7xQ//jHPzwVOPCOoASIEVVLAGsuwN61a1fP9o4dO/zuq+qYqu+1skDXKfHXfsusqhKrhSWSCEyihLZcAAAgFAQm0UOIgkjyF6RIhCmIXSkpKYqX1KCeuR9xJycmRPV4Dz/8sKZMmSJJevrpp3X33XdXe7179+6aNGmSLr74YvXv318FBQUaOXKktm7dWu3zwWA7zEjBdZnxxoxjBio+YnsGYClVK0VqVoH06tXLs/3999/73VfVMT179gx/chHivjk006bMPMP2tS3L/wVyNLkDE0TP/oKjnj8AAACBKM3PtsR1cazKOVjo8w9glD37C+r8A8A59u3bp6eeekpS5ZeX77rrLp9jzz//fI0aNUqSlJubqzfeeKPa66mpqZ41QnJycjxrh9Ql3C4zZhwzUAQlQIxYtuz4wlY1q0B69Oih9u3bS6psy7Vz506f+ykoKNDKlSslSQ0aNFDfvn2Nn2yU5GRmmj0F2yMsMQ+BCQAACAaBifUQoiBa/AUphCmAfXz22WcqKyuTVLlYe1xcXJ3jBw4c6Nn+7rvvqr0WHx+vTp06SZLKy8u1e/fuOvdVVlamvXv3SpIaNmyoNm3aBD1/M44Z8NwitmcAlrFlyxbNmTPH8/iyyy6rNebqq6/2bD/33HM+9zVjxgwVFlZeuA8dOlQNGjQwcKaRlbd3S0Dj/N2YBHsR6eSqEomwxGxUmQAAgGAQmNgDIQqijSAFsId9+/Z5ths3bux3fNUKjKoLqbsF02Vm9erVKi8vlySdcsopfkMaX8w4ZiAISgAbe/755/X111/XOWbNmjUaNGiQp+/fwIED9T//8z+1xt19992ePn8vvfSSFi9eXGvMd999p4ceekiSlJiYqEmTJoX7IyAEhCXwhcAEAAAEisDEvghRYAaqUgBrqLpGh79qDEn69ddfPdvNmjWr9XrVBd6XLFlS574+/vhjz/bgwYP9HtsXM44ZCBZzB2xs6dKluuOOO3TyySfr4osvVs+ePdWsWTMlJCRo3759+uKLL/Txxx+roqJCknTiiSfW6kfo1qJFC73wwgsaPXq0KioqdMUVV2jkyJG65JJLlJCQoFWrVmn27NmewGXy5Mnq1q1b1H5Wqwl2UfdNmXnq1qqRYce30uLubizybh0sAA8AAALlDktc5XzZwglYWB5mYuF5IPKqVmN89NFHysvLU6NGvj9vmjdvnmf77LPPrvX65ZdfrvHjx6ukpERvv/22HnvsMa/trQoKCvTqq69KkuLi4qp1pgmWGccMBEEJ4AD/+c9/9J///KfOMYMGDdLrr7+u1q1b+xxzww03qKioSBMmTFBJSYneeustvfXWW9XGJCQk6MEHH9QDDzxgyNwjrTQ/W0lpTX2+npOZqfRWrWo/f7DQ8BuJWAhLpMrAhLDEOghNAAAAIPlvMUyQgmggTAHCc/7556t9+/batWuXjhw5ot///vdauHChGjas/m+4y+XSxIkTtXz5cklSSkqKRowYUWt/zZo10+23366//OUvKikp0XXXXacPP/xQqanH/zs8duyYxo4dq8z/rvV7zTXX+Pzy9COPPKLJkydLqvyccdasWRE/plEISgAbe/bZZ/W73/1O3333nX7++WcdPHhQhw4dUmlpqRo3bqwOHTro3HPP1bXXXuu13ZY3t956qy6++GK98sor+uSTT7R7925VVFSodevWGjBggG6++WadfvrpEf7JIidv7xY1atPFkH0FW1USCYQlCAahCQAAAHwhSIFVBNrGy+z7cSBYO3bs0GuvvVbtubVr13q216xZo4kTJ1Z7vX///urfv7/ncb169fTCCy/oiiuuUEVFhT7++GN16dJF119/vXr16qV69epp+/btWrBggX766SfP+5544gmfX56eOHGiPvnkE61du1bLly/XaaedprFjx6pjx47at2+fZs2a5Zln+/bt9fTTT4f7V2HKMf0hKAFs7OSTT9bJJ5+sm266ydD9du7cWc8++6yeffZZQ/drVXauKrEy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wdQ5JB9LMPX4MAYVJQAAAAAAAAAAIGYRlAAAAAAAAAAAgJhFUAIAAAAAAAAAAGIWQQkAAAAAAAAAAIhZBCUAAAAAAAAAACBmEZQAAAAAAAAAAICYRVACmKC8vFy//PKLZs2apdtuu03nnnuuGjRooLi4OMXFxWn06NFB73Pbtm3685//rJ49e6px48ZKTU1V165dNX78eP30009B7au0tFQvv/yy+vfvr1atWikpKUlt27bVkCFDNHfuXFVUVAQ9PwAAAAAAAACwokSzJwDEohEjRui9994zbH8zZszQnXfeqeLi4mrPb9myRVu2bNH06dP18MMP6+GHH/a7r02bNmn48OHasGFDtef37t2rvXv36uOPP9b06dP1zjvvqGXLlob9DAAAAAAAAABgBoISwATl5eXVHjdt2lTNmjXT1q1bg97X3LlzNW7cOElSfHy8Ro4cqQEDBigxMVGrVq3S7NmzVVpaqkmTJikpKUn33nuvz31lZmZq0KBB2rVrlyTp1FNP1Q033KDWrVtr+/bteu2117R9+3Z99dVXGjJkiL788ks1bNgw6DkDAAAAAAAAgFUQlAAmOPvss9W9e3f16dNHffr0UceOHTVr1izdeOONQe0nKytL48ePl1QZkixatEhDhw71vD5q1CjdeOONGjBggIqKijRx4kQNGzZMXbt29bq/CRMmeEKSkSNHas6cOUpMPP7PxO23367LLrtMX375pX788Uc99dRTmjJlSrA/PgAAAAAAAABYBmuUACZ44IEH9OSTT+rKK69Ux44dQ97PM888o7y8PEnS+PHjq4Ukbuecc44nzDh27JgmT57sdV8bNmzQggULJEmtWrXSq6++Wi0kkaTU1FTNmzdPycnJkqTnnntOOTk5Ic8fAAAAAAAAAMxGUALYmDvYkKT/+7//8zlu7NixnhZZixcvrrWWiXtfLpdLknTzzTcrNTXV677atGmjESNGSJKKior0wQcfhDx/AAAAAAAAADAbQQlgUxs2bNCvv/4qSerevXudlSlpaWm68MILJUmFhYX68ssva41ZsmSJZ3vw4MF1Hrvq61XfBwAAAAAAAAB2Q1AC2NS6des822eddZbf8VXHVH2vJLlcLq1fv16SlJCQoNNPPz3kfQEAAAAAAACAnRCUADa1efNmz3Yg65xUHVP1vZK0e/duFRUVSZLatm2revXq1bmv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fpEkNWrUSN26ddPBgwfVu3dvtWvXTi1btnR4LsBfpSVl+fSncegqAeCPCElKopsEAFznqx0mNoG0NZdEp0lFsUWX76DjBDCGXwQltWrVUp06dXT8+HFlZWVp9erVGjBggMO106dP19atWyVJ5513nsaNG1fs8Q4dOig4OFhxcXE6ffq0QkKK/xG9+uqrOnLkiJ544gk1bdq02GNPPfWUrr32WkVGRtrvKygo0H333aeZM2dq1qxZevXVV+2P7dixQ5IUFxena6+9VkuWLFHdunXt91188cX6/vvvtXbtWvXr10+StGzZMt1zzz1q0aKFPvnkE3Xu3FlSYQD04IMP6vXXX9ekSZP0+eef269jC0q6du3q8M+ktKDEdtzmzZt1/fXXa8OGDapVq5bDcwD+Ji0xkRfdPoCwBAhchCQlEZIAQOX48hwTKfC25pLOhib87uYatujyLQQngHf4xdZbFotF1157rf37Bx54QAkJCcXW5OXl6cknn7RvL1WlShXNmTNH4eHhxdZVq1ZNrVq10qlTp/THH38Ue+zo0aN69dVXVa9ePT366KMl6rj55puLhSSSFBQUpNGjR0uSfY6KjS0oadu2rT799FN7SCJJrVu3th9nW3fs2DHdfffdCg0N1ZdffmkPSWx/Bs8884wk6X//+5/y8vKKXcdisZQIQqTC7b9+//131apVS82bNy/2mK2jpFWrVnr//fcJSQAv8OT2W/Fp2R47t5GOsFUNEHBS/0z16vUISQAg8PjytlySAmpbLomtuSqLLbp8C1t1AZ7hFx0lUmE3x8cff6xTp05p//796tixowYPHqyYmBgdOXJEK1eutAcVISEhmj9/fqlD3Dt16qS9e/dqz549ateunf3+qVOnKjMzUy+99JJq1KhR4riTJ09q7dq12rFjh5KTk5Wbmyur1aq///5bklSzZs1i620ByDPPPKNq1UpuiXP++edLKgxoJGn27Nk6ceKExo8fX2yuik3NmjVVt25dHTt2TBkZGapdu7ZSUlJ08OBBtW7dukSII53d/stRiGLrKPn3v/+t6tX5dAHgyOEjmWV+miM2MV1tG5X89wLuRWcJAE8hJAGAwOYv23JJgddlItFpUhF0m/geOk4A9/CboKR9+/ZasmSJRo4cqezsbKWlpem9994rse68887TwoULdfnll5d6rgsuuEAfffSRdu/erZtvvlmSFBsbqzlz5qhNmzYaP358iWPee+89/etf/1JaWlqZNdqcOnVKv/32m6pVq6Ybb7zR4fqsrMInJ1uniW07rTvvvLPUa2RnZysoKMgeylR0262cnBzFxsaqWrVq6t+/f6nXA/xBbkaKqX7x2ZecqVb1PPPCxh9nldgQlgBwN0ISAICNr2/LJRGaSAQnFcFsE9/jqMuE8AQon19svWVzyy236LffftP999+vtm3bKiIiQmFhYYqOjtbNN9+sRYsWae/evWWGJFJhUCJJu3fvtt/36KOP6vTp03rppZdKzC159913NX78eEVGRurtt99WbGyssrKyVFBQIKvVquuuu06S1KVLF/sxu3btUl5eni644AJVrer4jb1t27ZJOhti7Nq1q1h954qPj1dWVpZ9zopU/iB327yWorVJ0m+//abTp0/rkksuoZsEgM9gGy4A7kJIAgAoja9vyyUF3tZcNmzRVTlFt+himy7fwnZdQPn8pqPE5rzzztN///vfSp2jU6dOks4GJRs3btSqVavUq1evEt0fVqtV06ZNU3BwsNatW6eWLVsWe/z48eP65ptvZLFYioUVtm23HG3hJUkpKSlas2aNIiMj1bdvX0lSRkaGgoKCVKVKFYfHfPTRR5Kkf/zjH/b7bHNGHG2tderUKXuXSmmD3EvrRAECUVpSll98gsafu0qks2EJ3SUAKoqQBADgDLpMfBtbdFUe23T5LrbrAkryq44Sd2nRooUiIyP1xx9/KC8vT4888ogsFoteffXVEmuTkpJ05MgRNWjQoERIIkmPP/648vLy1LJly2KhiC0oOXDggMMapk+frpycHD344IP2+SUNGjRQQUGBw2OSkpL04osvqmrVqpowYYL9fttclqKD4m1efPFFpaWlKTQ0tNgsFqn8ThTAX3niU1WxiekuH+PJoe6S/w52L4ruEgCuSkg6SUgCAKgQf+oyodMEFUW3ie86t+Mk4Sj/HyLwEJQ4YLFY1LFjR506dUr//ve/tW3bNg0dOlQXX3xxibW1a9dWaGioEhMT7fM+JCkvL0/Tpk2zz0k5N3Cwrd23b5+WLFliv99qtWrGjBl6/fXX1aFDBz366KP2x2yzQp599lnl55/9BfnPP/9Uv379dPz4cf373//WeeedZ3+sfv36kqRVq1bZ7ysoKNDMmTP17LPPSpI6duxYYjsxWycK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zNtvv60tW7aoYcOGevnll71aL2B23th+yyiEJWUjLAHMjy4SAAA8JxBCE4ngxFkEJ8YiPAGc49Gtt9LS0uy369atW+76OnXqODzWrNf87rvvtG/fPklSkyZN1K9fP6eOi4mJ0TXXXKPOnTurXr16OnXqlPbv36+VK1fq559/VkFBgd544w39+eefWrlypYKDg12qq6IaNGigZ599Vv/617+0e/du9ejRo8Saf//73/aumYSEBPs8ktdff11RUVFeqRPwtNyMFJ9qG9+XnKlW9SKMLgPnsIUlbMcFmA8hCQAA3hMoW3RJJedcslWXY2zXZTy27QJK8mhHSWbm2U9Th4WFlbu+WrWz+95nZGSY/ppz58613x49erRTgca6det04MABzZo1SxMnTtStt96qESNGaOrUqdqxY4cWLVpkn/HxxRdfaMaMGS7VVFmPPPKI3nzzTdWqVavY/XXq1NG7776rhx56yH7fvffeq/T0dF1//fUaPHiwJOn06dN644031KVLF4WHh6tmzZrq06ePvvzyS4fXy8nJ0QsvvKCOHTuqWrVqioqK0jXXXGOf+wIYzZmB7oH6aQy6SpzDdlyAeXhiqy2JkAQAAFcESreJJLpNnMR2XeZQtOPkRPJJo8sBvM6jQYk/S09P10cffSRJslgsGjNmjFPH9e7du8zHR4wYoZkzZ9q/f/HFF72+BdfEiRN19OhRbd26VV988YW2bdumI0eOaPz48fY1K1as0KeffqqIiAh7vQUFBbr55pt1//3365dfflHVqlWVl5endevWacCAASVCn+zsbPXt21dPPPGEdu/erbCwMOXm5urrr79Wv3799N///terPzfgCd7afsuIWSUSYYkrCEsAY3kqICEkAQCg4oqGJv4enLBNl2sITgB4m0tBSWxsrD799NNSv2JjY4utj4g4uxVMTk5OuefPzj77hltkZKQrpXn9mu+//75OnixMV3v16qWWLVu6UGXZxowZo/PPP1+SlJqaqs2bN7vt3M6qUqWKLr30UvXv31/dunVTSMjZXdrS09N17733SpKef/55RUdHS5LeeOMNrVq1SlWqVNEHH3yg1NRUpaamavLkyZKkRx99VL/88ov9PE8//bS+//57RUZGau3atfb1DzzwgCRp8uTJ2rlzp5d+YsD3EZaYH90lgPfRRQIAgO8IlNBEIjhxFV0nADzN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FYD/MWvIci53hi6SOYOX0jgKZArysg2oBAAAAADgLVlZWTpw4IAOHDiglStX6rnnntNHH32kCy+80OjSfBZBCXDGuHHjNGfOHP3444/6/fff1aVLF1199dXq27evWrVqpfDwcJ08eVL79u3Tt99+q6+++kr5+fmSpEsuuUTjxo0z+CcIXHGZp4wuAcA5KhOySN4NWs7l7uBF8q3wBQAAAABgHitWrCj2vdVqVVpamnbu3KmlS5cqOTlZ+/fvV9++fbVnzx41aNDAoEoL9e7d2+GWXGZHUAKcERQUpDVr1qhfv3766aefdPr0aa1du1Zr1651uN72H3zXrl31xRdflNsSBwBwXmWDFhsjA5eiPBG+2BDCAAAAAID/GjRoUKmPTZkyRb1799auXbuUkpKi1157TS+99JL3ivMjBCVAEbVr19aWLVv0yiuv6PXXX9fRo0dLXdugQQM98MADmjx5skJC+E8JAMzI3wIXR84NYayncw2qBAAAAADgTbVr19Yzzzyjm266SVLhIHVUDO/uAucICQnR448/rkceeUSbN2/WDz/8oL///luZmZmKiIhQkyZN1K1bN/Xo0YOAxCRSTuUbXQIAP+euwMVZZg5mAAAAAADm0b59e/vt9PT0ctf/9NNPmj17ttavX6+///5bp06dUv369XXppZdq6NCh+uc//1mpetavX6+rrrpKkjR16lRNmzatxJqYmBj99ddfat68ueLj41VQUKCFCxdqwYIF2rNnj06cOKH69eurV69eeuSRR3TBBRdUqiZn8C4vUIqQkBD16tVLvXr1MroUAECAqUwwY81nbhMAAAAABIpjx47Zbzdr1qzUdfn5+br//vv19ttvl5ghcvDgQR08eFAffvihevbsqY8//lj16tXzWM1FHT9+XDfffLO+++67YvcfOnRIixcv1vvvv6+FCxdq2LBhHq2DoAQAAAAAAAAAAB80a9Ys++2rr7661HWjR4/W4sWLJUlVqlTRiBEjdOWVV6pq1ar69ddfNXfuXCUnJ2vjxo268sortX37dkVERHi09tOnT9tDku7du+vmm29WdHS0UlJStHz5cq1fv16nT5/WHXfcoW7duqlly5Yeq4WgBAAAAAAAAAAAH2C1WpWenq6dO3fqrbfe0gcffCCpcAuuiRMnOjzmww8/tIcktWvX1ldffaWLLrrI/vhtt92myZMn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8fHxiomJcbg2MzNTGzdulCSFh4erV69eJdZcd9119gBk9erV6tatW6nX/vLLL+23+/fvX4mfArGJ6WrbqOyB7kAgobsEgYyQxHsISYzlL1tu0U0CAADgP+goAc54//33JUk1a9bU9u3b9cILL6hPnz5q3bq1mjdv7tSXtw0ZMsR++7XXXit13axZs5SVlSVJGjhwoMLDSw7ALXqud999177+XAkJCfrggw8kSdWqVdONN95YodptSnujoqxf7s38icC0xESjSzAdukrgKjpLEGjyEuMJSbyIkMRYbLlVeXSTAAAAuB9BCXDG77//LovForvuuqvEDA+zmjx5siIjIyVJb731llatWlVizbZt2/T0009LkkJCQjR16lSH5+rQoYNuvfVWSVJiYqLGjRun06dPF1uTmZmp4cOH24fbT5o0qcRgeJyVluQ4bAJQPrbiQqAgIPEuQhJjGRGSmPkDNnSTAAAAmAdbbwFnnDpV+IvKhRde6PFrHThwoMTg919//dV+++eff9ZTTz1V7PE+ffqoT58+xe6rX7++3njjDY0ePVoFBQW66aabNHToUF1zzTUKDg7W5s2btWDBAnuwMX36dLVt27bUul577TV9//33Onz4sJYtW6bdu3dr9OjRaty4sf7880/Nnj1bf/75p6TCP6fHHnusUn8OAFAetuKCPyMk8S5CksDjj1tu0U0CAADgGQQlwBlNmzbVvn37lJub6/Fr/fXXX3r++edLffzXX38tFpxIhd0g5wYlkjRq1CidPHlSkyZNUk5OjpYuXaqlS5cWWxMcHKwnn3xSTzzxRJl1NWnSRGvXrtXNN9+s2NhY/frrr5o0aVKJdT169NDHH3+siIiIMs8H99iXnKlW9Sr2Zx2flq2YqGpursh1RzJPqWFEVaPLgI8iLIG/ISDxrkAISCTzhyT+suUWAAAA/BNbbwFnXHvttZKkH3/80eBKXHf33XfbQ4327dsrMjJS1atX1/nnn6+77rpL27dv1/Tp0506V/v27fXzzz/rzTffVK9evdSgQQNVrVpVjRs31nXXXaeFCxdq48aNatiwoYd/Kv9z+Eim0SUAPoutuOAvCEm8i5DEHPxpyy26SQAAAPwTHSXAGffff7/mzp2rBQsW6PHHH/doENC7d29ZrVa3nvP888/XjBkzNGPGjEqfKywsTBMnTtTEiRPdUBlQiK4S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+vSRdCIo2bRpk9auXatBgwaVq/5wuVyaPn26oqOj9emnn6pTp05erx8+fFiffPKJHA6HV1jhXnbL1xJekpSZman169crMTFRQ4cOlVTaKyMqKkq1atXyuc+bb74pSbrkkks8z7n7jPhaWquwsNBTpVJRI/eKKlHs6O2339aXX34ph8OhW265Rf/+978VHR2tlStXSirtfTNixAhJUlpammbMmKGFCxdq27ZteuKJJ3z+G6PmQn1xHqlCvfyWFDlVJW5UlwDmR0ASOLsGJBIhSWUISQAAABApbNHMPdg6duyoxMRE/fLLLzp+/LjuueceORwOzZkzp9zY9PR0paWlqXnz5uVCEkm6//77dfz4cXXq1MkrFHEHJXv27PE5hxkzZig/P1933nmnp39J8+bNVVJS4nOf9PR0Pfnkk6pdu7YmTZrked7dl6Vso3i3J598Uk6nU7GxsV69WKSqK1HsaNWqVZKkTp066dlnn/VU5fjSokULvfDCC3rqqaf0xx9/6IorrvC7lwngizM91+fzNHS3Fpq8A+ZEg/bA2bVRu1QakBCSVIyQBAAAAJGEoMQHh8Oh3r17q7CwUI8//ri++eYbjRs3TmeeeWa5sY0aNVJsbKxSU1M9/T4k6fjx45o+fbqnT8rJgYN77O7du7V8+XLP8y6XS3PnztVzzz2nXr16aerUqZ7X3L1CZs2apeLiExcCv/32my666CIdPnxYjz/+uE455RTPa82aNZMkrV271vNcSUmJFixYoFmzZkmSevfuXW45MXclSiRVlHz33XdyOBwaPXq0oqLK/1/DV7+Su+66S2eeeaZSUlK0aNGicEwTFlOQbdwNDjNIceYZPYWwIywBzIOAJHB2Dkgka1SRGNmPhJAEAAAAkYagpALu5bdmzZql2NhYPfHEEz7H1apVSzfccINcLpfOPfdcXX755Ro9erQ6dOigl19+WePHj5fkHZQUFRVp27Ztqlevnm644QaNHz9eAwcO1NixY9W5c2fdfffd6tixo9555x2v/iDTp09X8+bNtWTJEvXo0UNjxozRkCFD1K1bN23dulUPP/yw7rrrLq/5XXnllZKkadOm6cwzz9Tll1+uDh066OGHH9add94pqfyyWyUlJfrpp59Ut25ddevWrUb/jlaSkVF6wXxydY3D4ZBU2uDel7Fjx8rlcuntt98O7QRtyuw3KkLF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9+7dPY8TEhI8S1Ll5+crISGh0uPn5Z34ZnFiYmJAcwzXOT/++GNdeeWVKioq8jRFHzVqVEBzDpamTZtq6NChPl978803tXbtWiUmJur555/3PH/11VcrPT1dp512mlavXq2OHTtqz549GjVqlH766Sddc801+t///iep9N9q3LhxysnJ0eDBg/X666+rZcuW2r17t0aNGqUdO3bommuu0bfffhuW39dfe/bskST9+c9/rtZ+7oqbQKubADdnaqoatGxZrX32p+WoTYvK//vllpSape4t6wUyNY/dGTnq3NS/84UCYUnNpOUUEpYAQRCp4YhEQBIsRnwxhJAEAAAAqFhIgpKVK1dqxowZFb4+bdo0TZ8+3fO4QYMGntDi0KFDVYYWhw8f9to3EOE454YNGzRixAjl5+fL4XDohRde0A033BDQfMPh6NGjuv322yVJTzzxhNq0aSNJ+vzzz/XDDz9Ikl544QVPY/uOHTtqwYIF+vOf/6zvv/9emzZt0sCBA/Xmm29q//79iomJ0auvvqqW///Gb+fOnbVgwQINGjRI3333nb744otqhxKhlJtbusRRVX8LJ3OHaP4s4YbA2OniuyA7U7GJjaq1jzM9Vw2a1Q3RjKyHsKRmqC4BAkdAYk1mCkgkqkgAAAAAMzJFj5Kyyxy5v9VfmbJjAl2+KdTn3LBhgy6//HLPTfT58+dr0qRJAcw0fKZOnarU1FSdc845uvnmmz3Pb9y4UZLUqlUr9e/f32ufc8891xOEuMd99tlnkqSzzjpL7du39xp/3nnnqUWLFpJKAxgzady4sSTp4MGD1drvl19+kVRaqQNEAiN7lbjRs6Tm6F8C+CdSm7O7WbUHiWTOPiSEJAAAAIA5hSQomT59ulwuV4U/ZatJJKlPnz6e7e+++67SYx88eNDTPLxZs2YB35wO5TndIcmxY6UXQvPmzfMKHsxo8+bNWrRokWrVqqWXXnpJUVEn/jQOHCi9MeCuMDlZ27ZtJZU2ey873v18VePNomfPnpJOBD7+evfdd+VwONSvX79QTCtihfLC3kxObujuTE2t9jH2p/kfXASjqbtEWGInBCaAb5EcjkgEJMFkVEBCSAIAAAD4zxQVJRdffLFne926dZWOff/99z3bw4cPN905Tw5Jnn32Wd16660BzzMcCgsLdeONN8rlcunee+9V7969fY5zL011spwc3zdMKxpf0fNGu+SSS+RyubRmzRpPlUhVVq5cqR9//FFSzf4eI5WZbmLAmghLgofABKB6xB2OEJAEjx17kRCSAAAAwI5MEZQMGjTIsxzTZ599pi1btvgcV1xcrOeee87zeNy4caY652effeYVkvz73//29PwwsyeeeEI7d+5Uly5d9NBDD5V7vXXr1pJKlx9z/25ux44dU0pKitc49//u2LFDLpfLa3xubm658Wbxj3/8Q40aNVJhYaFGjBhR5ZJsq1at0sSJE+VwONSqVStdddVVYZqpt8GDB8vhcPj94/73r8zu3bt1zz33qHfv3qpfv74SEhLUrVs3TZ482RMMhYIRNxOMUJBd/Zs4zvTgBYx2qiqRCEuCjcAEkSbSwxHJ2tUjknkDEqpIAAAAAOswRVASHR2tRx55xPN4woQJSk9PLzfuvvvu89ykPffcc3XRRRf5PN6rr77quSk8ePDgsJzz888/16WXXuoVktxxxx0+x5pJUlKSnnjiCUnSwoULfTYkHzhwoKTSUGTRokVery1cuNDzO5933nle/5uSkqK33nrLa/wLL7zgGT9o0KAg/iY1V69ePb3wwguSpOTkZPXu3Vvjx4/X2rVrPWPmz5+v++67T3379tVVV12l3NxcRUVF6T//+Y9q1apl1NSDatGiRTr11FM1Z84c7dixQ1lZWcrNzVVycrIWLFigM888UzNnzjR6mjhJdZbfCibCEvsiMIHdRXo4IhGQhIJdAxJCEgAAANhdjNETcJs4caJWr16tjz76SDt27NBpp52miRMnqmfPnsrMzNSKFSv0xRdfSJIaNGighQsXmuacP/74o1dIctFFF6l9+/Z65513Kj1/kyZN9Oc//7nGv0egXC6XJk2apIKCAl1//fU6//zzfY4bNGiQTj31VP3000+699579ccff+jMM8/U999/r2eeeUaSdPrpp3sCktGjR+v+++9Xamqqrr32Wv3000/q3bu3vvnmG091Tr9+/TwBjJmMHj1aTqdTt912m/Ly8rRixQpJksPhkCSvCiGXy6XatWtr4cKFuuCCCwyZ78lWr15d5ZhmzZpV+NqyZcs0adIkSVJUVJTGjRunoUOHKiYmRps3b9aSJUtUUFCgadOmKTY2VlOnTg3a3CsSiRfmztRUNWjZMqTnSErNUveW9UJ6jnBLceapQ4N4o6dhO+6wpEVCbYNnAtRcpAcjblYORyRzLt1pVFVsqHu6ReLnMAAAAEQm0wQlMTExeuutt3TVVVfpvffeU1pammbNmlVuXJs2bbRq1Sr16tXLNOf88ccfvfpufPDBB/rggw+qPP+gQYP02WefBTz/mlq8eLE2btyoZs2aac6cORWOczgcWrp0qc4//3xlZmbqqaee8nq9SZMmWrp0qedxnTp1tHz5cl122WU6duxYuX/TZs2aeY03m4kTJ+qcc87RtGnTtHbtWpWUlJQb43A4NHz4cD322GM67bTTDJilb6NGjQp434yMDE2ePFlSaUiyevVqjRgxwvP6hAkT9Pe//11Dhw7VsWPH9NBDD2nUqFHq1q1bTacd8bIOJKte665VjnOm56pBs7oVvr4/LUdtWiQEc2p+2Z2Ro85Nw39eXwhLQqdsdQmhCayEcKSU1cMRiYCkLAISAAAAILhME5RIUmJiot59912tWbNGr732mr777julp6crMTFRnTp10l//+ldNmjRJ9evXt/Q5zeDgwYO69957JZUuE9awYcNKx5966qnasmWLZs2apXXr1ikjI0NNmjTR8OHD9cgjj6hdu3Ze488//3z973//06OPPqpPPvlEmZmZat68uS699FI9/PDDputPcrI+ffro7bff1tGjR7V582alpKTI6XQqISFBbdq00cCBA9W0aVOjpxlUc+bMUVZWae+KyZMne4Ukbuecc45mzZqlu+66S0VFRZoxY4Zef/31gM535LcjqhsdXaM5o2aCWVVCWBJZqDKBFRCQlCIgCQ27BiQSIQkAAAAik6mCEreRI0dq5MiRAe9/3XXX6brrrgvbOQM5n9GaN2+uI0eOVGuf9u3ba/HixX6P79Gjh5YvX17dqZlK/fr1NXz4cL/GHjhwwPQBUGVWrVrl2f7nP/9Z4biJEyfqkUceUW5urtauXau8vDzFx9f8pnTZGw7huAlgtILsTMUmNqrw9XAsv2VnhCXhQWACsyEcOYGAJHTsGpIQkAAAACCSmaKZO2BlaWlpuu2229SlSxejpxKwnTt36vfff5dUGnB17NixwrGJiYme/jK5ubn6/PPPwzJHlHKm51b6enWbuielZtVkOl7M0tjdjQbv4UPjdxjJ3ZSdkOREc3arhyRmbNIuGdOoXQp9s3aJkAQAAAAgKLGB33//XQ6Hw/Pz73//2+gpBaxv376e36Oi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kQhIgs2IgESiigQAAADebBWU9OnTx7P93XffVTr24MGD2rdvnySpWbNmatq0aUjn5q97771XaWlpGjBggG666Sav19yBRps2bcrt17ZtW8/2/v37dejQIRUUFJR7raLxAOAWrKoSM4QlVJUEF4EJzCLv972EI2FE9ciJcMSqAYlEHxI3qkgAAIDZFRcXa/v27Xr11Vd12223qX///qpTp44cDoccDoeuu+66ah8zPz9fS5Ys0ciRI9W+fXvVqVNHtWvXVrNmzXTeeedpxowZnnvl/igpKdGyZct06aWXqk2bNoqNjVXLli01ZMgQvfDCC5770sEU6nPaqpn7xRdfrKeeekqStG7dOt17770Vjn3//fc928OHDw/53PyxadMmLV68WLVr19ZLL71UYZP13Nzyy+zk5FR8Q9LXeF/PAUap7k2HUDfbNFogDd2d6blq0Kxuha9Xp6m73dDcPfho+A7YXySHImVZORhxIyApRbN2AABgFWPGjNHbb78dtOP9+OOPGjNmjH755Zdyr2VkZCgjI0ObNm3Sk08+qdmzZ+v222+v9HhpaWm68sorPasZlX0+LS1Nn376qebPn6+3335bXbt2DcrvEI5z2qqiZNCgQWrRooUk6bPPPtOWLVt8jisuLtZzzz3neTxu3LiwzK8yBQUFuvHGG+VyuTR16lT17Nmz3JjWrVtLknbs2FHutbLPtW7dWk2aNFFsbKwkafv27eXGl33OfVxY0+DBgz2Jsj8/KX58A3f37t2655571Lt3b9WvX18JCQnq1q2bJk+erB9//DHkv5Nkzot6swhk+S2jmKGqRKKyJFTcFSZUmQD2EemVI25Wrx6RWGarLEISAABgJcXF3p8tGjVqpC5dugR0rH379mnIkCGekKRZs2a69957tXjxYi1dulSzZs3y3IfOz8/XHXfcocWLF1d4vJycHF1yySWewOKUU07RY489phUrVmju3Lk69dRTJZXeq77ooot08ODBgOZtxDltFZRER0frkUce8TyeMGGC0tPTy4277777PDd7zz33XF100UXhmmKFHn/8cSUlJalbt2568MEHfY4ZOHCgJGnVqlX6448/PM8XFxdr3rx5kqSOHTuqTZs2iomJUf/+/SVJixYtUnZ2tmd8UVGRJyg65ZRTfC7lhci1aNEinXrqqZozZ4527NihrKws5ebmKjk5WQsWLNCZZ56pmTNnhn1eRlxUW41Zm7oTlkQGAhPAulhaq5QdlteSCEjKCscyW4QkAAAg2M4++2zdd999+r//+z/99ttvOnz4sB544IGAjjVjxgwdOXJEknThhRdqz549mj17tm644QaNHz9eDz30kLZv3+51/AceeEBFRUU+j/fYY4957qsPHjxYW7du1QMPPKBx48ZpypQp+t///ufpjZ2SkqK77747oHkbcU5bLb0lSRMnTtTq1av10UcfaceOHTrttNM0ceJE9ezZU5mZmVqxYoW++OILSVKDBg20cOFCg2cs/fzzz3ryySflcDi0aNEiTyXIySZPnqylS5cqLy9P5513nu666y4lJibq1Vdf9VTPuBvBu7c/++wzpaam6txzz9Vtt92muLg4vfzyy54/rjvvvDPUvx7CaPXq1VWOadasWYWvLVu2TJMmTZIkRUVFady4cRo6dKhiYmK0efNmLVmyRAUFBZo2bZpiY2M1derUoM0dFcs6kKx6rWteqhjJy2+5sQxX6LEsF2ANkR6KlGX1YEQybyWuUV90CccyrQQkAAAgVAINRXxZv369Z/uZZ55RnTp1yo1xOByaOXOmXn75ZR08eFAZGRlKSkpS7969vcZlZmbq3//+tyQpLi5Oy5YtU0KC932mWrVqafHixdq4caNSU1O1fPlyPfjgg+revXtA8w/nOW0XlMTExOitt97SVVddpffee09paWmaNWtWuXFt2rTRqlWr1KtXLwNmeYLL5dKNN96owsJC/eMf/9B5551X4dizzjpLM2fO1EMPPaRff/1Vt9xyi9frl112mW699VbP41GjRunmm2/WCy+8oG3btunGG2/0Gj9q1ChNnjw5uL8QDDVq1KiA983IyPD8PURFRWn16tUaMWKE5/UJEybo73//u4YOHapjx47poYce0qhRo9StW7cazbkmNyfsfIEaSJ+SYEtKzVL3lvWCcqzdGTnq3NQcIY27soTAJLQITABzIiA5gYAkdIysBGaZLQAAgBPKrrZU2fJd0dHROuWUUzzLVvnqh71mzRrl5+dLksaOHVthO4eEhARNnDhRM2fOlMvl0qpVqzRt2rSA5h/Oc9pq6S23xMREvfvuu3rnnXf017/+VW3btlVsbKyaNGmiP/3pT5o9e7a2b9+uAQMGGD1VLVq0SF988YWaN2/uaURfmQcffFBr1qzR4MGD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c1uwYIE2bdqkL774Qlu3btXZZ5+t2bNnq2fPnvrhhx/08ssve+barVs3z73GUETqnAQlgI09/vjjOvfcc3XhhRf6HPPpp5/qJz/5iUpLSyVJl19+uS644IJ64+666y49++yzKigo0FNPPaWxY8dq4sSJdcZ88skn+v3vfy9JatKkiRYuXGjgT1OXFS7qnSIvq0gtUxPDcuxoDkuk6sCEsMR+CE1gBMKRxhGOmMsKn6UISAAAAAL3/fff68EHH/T5+hdffFEnOJGq79OdHpRI1V9yfv311zVnzhzl5ubqhx9+0COPPOL1uHFxcXr44Yf1q1/9yue5k5OTtXHjRk2ePFk7duzQ/v37dc8999QbN2DAAL355pvqGMKXeiN9ToISwMY2b96sO+64Q7169dLYsWM1aNAgtWnTRrGxsfrhhx/03nvvacOGDaqqqpIkde/eXStWrPB6rNTUVD3xxBOaOXOmqqqqdPXVV2vq1Km67LLLFBsbq+3bt+uVV17xBC6LFy8OqowN1hVMVUk4EZYgEghNEAjCkcY5NRyRCEgCQUACAABgHddcc43GjBmjlStXeiozcnJyVFlZqZYtW6p///669NJLddNNN6lr166NHq9Tp07avn27Vq1apddee01ffPGFjh07platWqlfv3669tprddNNNykuLs6wnyES5yQoARzgu+++03fffdfgmHHjxumll17yWTonSTNmzFBxcbHmzZun0tJSvfrqq3r11VfrjImNjdW9996r3/3ud4bMXQrsxgMXw9UCXX7LjlUlkr3CEomluOyO0ASnIxjxD+GI+awQjkgEJAAAAEYYNWqU3G63ocds3bq17rjjDt1xxx2GHC8mJkbTp0/X9OnTgz7GokWLtGjRooiesyEEJYCNLV26VD/+8Y/1ySef6PPPP1dWVpaOHTumsrIytWjRQj169NCIESM0bdo0r8tteXPLLbdo7NixevbZZ7Vp0yalp6erqqpKnTp10pgxY3TzzTfrnHPOCfNPVlc09yepcXqfknAJtqqEsKQa1SXOcXoDeIKT6EE44h/CEWuIxoBEIiQBAACA8QhKABvr1auXevXqpZtuusnQ45555plaunSpli5dauhx/WWVi347MquqJNzsFpZIVJc4DdUmzkUw4j8nhyMSAUkwCEgAAADgFAQlAGBT3pbfMoIVq0oke4UlEoGJk1Ft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/w8B24rWZbfCJa2w3OwpmCo/w7hvvcBeeA8BAEi8HwAAgPBz7NJbFRUVmjNnTp2112rLyclRTk6O/vOf/2j16tU6evRohGcIhFftZbf8beLemNjYWP3hD3/QxRdfLElav369nnrqqQb3iYuLU1xcnCHnNwvVJDCKr0buOftyIjwT+MOo5bdo6h48J7yHAABCx/sBAAAIN8cGJXfeeacnJElMTNSYMWPUv39/tWzZUhUVFcrNzdWePXv0wQcfaODAgSbPFjDWnj179PHHH0uSUlJSdM011xh27BEjRig+Pl6lpaU6dOiQiouLlZCQYNjxrYaQJHBWqCYxa9ktGrkDAAAAAADYjyODkpycHD3zzDOSpG7dumn79u0+lxQqKirSp59+GsnpAWH34osveranTp1qaJARExOj1q1b64cffpAk5eXlOTooAcyQkVVs9hRMV1aQo7jk1j5fz8vMVMuOHes/30hD98NHCtWlQ+SrO6gqAQAAAADAuhzZo+Tzzz/XyZMnJUmXXnppg30XEhMTPcsIAU5w8uRJrVy50vPYqGW3alRVVSk3N9fzuGXLloYe30qoJgmck6pJAKl6+S0AAAAAAOBsjgxK2rdv79letWqV7rzzTqWl0QgW0eEf//iHp+fOoEGDdP755xt6/B07dqikpESS1KVLF8dWkxCSwIpyyivNnoJpnNDQfV92odlTAAAAAAAAXjgyKBkwYIBuvPFGSVJlZaWWLVumvn37qmvXrpoyZYpeeOEF5efzDVE4U+1lt8JRTXLfffd5Hk+YMMHQ48PerFBNYqRI9SehkTsAAAAAAIC5HBmUSNL999+vnj176sorr9S//vUvPfXUUxo3bpw2b96sm2++WV26dNGTTz5p9jQBQx05ckQbN26UJDVr1kzXX3+9X/t9/PHHev7551VaWupzTFFRkaZPn6733ntPkhQXF6f58+eHPmkLoprEvqy87Fbuvmyzp4D/c/hIYJUdRi6/RVUJAAAAAADW48hm7n//+991ww036Be/+IUeffRRSdLYsWMlSX/84x81bdo0vf3227rtttt0/PhxLVy40MzpAob585//7OnPc9VVV6lt27Z+7Xf06FHNmTNHd955py677DINGzZMXbt2VWJiok6cOKFdu3bp9ddf1/HjxyVJLpdLy5cvV48ePcL1o8BmnFZNAnsItqE7AAAAAABAbY4LStauXatrrrlGEyZM8IQktSUnJ+uNN97QwIEDtX//fv3hD3/Qz372M/Xp08eE2QLGeumllzzbwSy7VVhYqLVr12rt2rU+x3To0EHLly/XlVdeGdQcrY5qEgQj2GW3vMnIKq7zOK2w3LBjI3h7MvPVr2OKIcfal12o3u2SDDkWAAAAAAAInaOW3vr666913XXXKTY2Vn/60598jouPj/fcRD558qT+9re/RWqKQNhs375de/fulSR17dpVl112md/7jh07Vm+99ZZ+97vfaezYserbt6/atm2rJk2aKCUlRb1799a1116rV155RQcOHCAkQR1WqSYxctmtQPuTIDzKCur2bzGyoXugy28BAAAAAADnclRFyaxZs1RaWqprr71W3bt3b3Bs//79PdtpacbdeAHMctFFF8ntdge1b1JSkiZOnKiJEycaPCsAku/+JDRyj15UlQAAAAAAYB2OqSh555139Mknn0iSfvrTnzY63uVyebYbamANIDpQTRIcq1STIHrlZXr/fzcvq8jwcxnZ1B0AAAAAAFiHY4KS1atXe7YvueSSRsdnZJxaT75Tp05hmRMAeyAksT8zl90ysj9JQ3LKKyNynmhi9vJb+7JZ/gsAAAAAACtwTFDywQcfSJJatWqlDh06NDr+008/9WyfeeaZYZsXAGsjJAke1SThRyN3AAAAAACA8HNEUFJeXq5Dh6pv2PlTHeJ2u/Xuu+96Ho8aNSpcUwMAhJmR1STh4Ks/iTcZWcVhnIkzBNLQ3Q7Lb1FVAgAAAACA+RwRlBw/flxVVVV+j3/vvff0/fffS6quJunbt6/ntRUrVsjlcmnKlCk+93/++eflcrk0Y8aMOs//+9//1vz58zV8+HB16tRJ8fHx6tmzp6ZNm6Y9e/bUO05JSYmaNGmijh076vDhw5o7d6569+6t+Ph4tWnTRtOnT1d2tvcbbLt379bs2bPVs2dPxcfHq3379rr++ut14MCBemPffvttuVwuTZ8+3euxnnjiCblcLi1evLjO87m5uXK5XOrVq5dKSkp0//33a8CAAYqLi9O5557r8+8HsAuqSYLn1GqSQJfdCgWN3P1TVtD435OvPiX+MHv5LQAAAAAAYD5HBCVxcXGe7YMHDzYYmrjdbv3+97/3PJ47d26d1wcPHixJ+vrrr73uX1RUpIULF6p58+Z68MEH67w2Z84c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xsfHNzq+efPmnu2CgoKgz7tkyRIdOXJEa9as0W9+8xtdd911mjJliubNm6d//OMf+ve//61u3bpJkr7//nvNmjUr6HMF46KLLtI///nPen1GmjZtqttuu01r1qzxPPfEE09o586d6ty5s5YsWeJ5fuPGjRozZoxatmyphIQEDRkyRH/605908uRJr+d8/fXXdckllyglJUWJiYkaNmyYnn76aVVW8o1nhAcXz+FBNYl10Z/EnqgqAYD6qAQBAACAXVmyosQsjVVdnHvuudq0aZPOOecclZWVaePGjdq5c2ejfVSMdOmll2rv3r366quvdOjQISUkJOicc85RSkqKZ0x6eroWLFggqboKpea1pUuX6q677pJUHUDFxcXp888/169+9Stt2bJFf/vb3xQTcyo7+/Wvf63/9//+nyQpISFBTZo00a5du7Rr1y5t3rxZf/nLX+RyuSL0kyMacPEcHtEYkoTC32qSYJbdgvHKCnIUl9y6znP5GWlK6dyn3ti8zEy17NjR57HysorUMjXR8Dn6siczX/06pjQ+EAAsgs9qAAAAcCpLVpQkJSV5tktLSxsdX1JS4tlOTk4Oy5xq9O/fXzfccIPn8fr168N6Pl8GDhyoK664QiNHjqwTkkjSrbfeqsLCQl199dWaNGmSJGnXrl2eZu6//e1vlZeXp9zcXL3++utq2rSp3nrrLT311FOeY6xfv94Tkjz66KPKy8tTXl6eVq1apSZNmujNN9/U008/HZGfFdGBC28YyexqEl/LbjXWnwT2EmpVCQBYCdUgAAAAiGaWDEpatmzp2T527Fij448fP+5133C59NJLPdvffPNN2M8XiDfeeEPr169XSkqKnnzySc/zTzzxhCorK9W3b189/PDDiouLk8vl0pQpUzRjxgxJ8gQjkvTHP/5RkjR27Fjdddddatq0qVwul6ZNm+YJimrGAKHiwjt8qCYJjBG9SUJBI3fzBdLU3QgswQUgEghBAAAAgIZZMijp27evZ/vAgQONjq89pva+4dKuXTvPdqjN442Ul5enO+64Q5L08MMPq1OnTp7XPvjgA0nS5MmT6y2Xdc0110iS9u/fr4yMDJ08eVLbt2+v81ptU6ZMkSR99913ysgw96Yi7I8L8/CxckgSTpGoJjFq2S36kxijrMB7BY83eZnG/ptjRFUJYQmAUBCCAAAAAKGzZFAy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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "4484eb1b24d34e9f9c3419751f7e75f8", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./comparison.pdf
\"), HTML(value=\"…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fontsize = 22\n", + "pyplot.rcParams.update(\n", + " {\"font.size\": fontsize, \"axes.labelsize\": fontsize, \"axes.titlesize\": fontsize}\n", + ")\n", + "mosaic = [[my_label(R_vapour_factor, isotope) for isotope in isotopes] for R_vapour_factor in R_vapour_factors]\n", + "value = 300\n", + "\n", + "fig = pyplot.figure(figsize=(16, 20), constrained_layout=True)\n", + "axs = fig.subplot_mosaic(mosaic, sharey=True)\n", + "\n", + "axs_cols = [\n", + " [axs[mosaic[i][j]] for i, _ in enumerate(R_vapour_factors)] for j, _ in enumerate(isotopes)\n", + "]\n", + "cmap = \"RdBu_r\"\n", + "\n", + "for row_idx, row in enumerate(mosaic):\n", + " for i, label in enumerate(row):\n", + " delta, S = np.meshgrid(isotopes_attr[isotopes[i]][\"deltas_liq\"], saturation)\n", + " pcm = axs[label].contourf(\n", + " in_unit(delta, PER_MILLE),\n", + " in_unit(S, PER_CENT),\n", + " tau[label] * si.s,\n", + " levels=np.linspace(-value, value, 21),\n", + " cmap=\"RdBu_r\",\n", + " extend=\"both\",\n", + " )\n", + " if row_idx != len(R_vapour_factors) - 1:\n", + " axs[label].tick_params(\"x\", labelbottom=False)\n", + "for isotope in isotopes:\n", + " axs[my_label(R_vapour_factors[0], isotope)].set_title(isotopes_attr[isotope]['latex_label'])\n", + " for R_vapour_factor in R_vapour_factors:\n", + " axs[my_label(R_vapour_factor, isotope)].sharex(axs[my_label(R_vapour_factors[0], isotope)])\n", + " axs[my_label(R_vapour_factors[-1], isotope)].set_xlabel(\n", + " f\"$\\\\delta${isotopes_attr[isotope]['latex_label']}$_{{liquid}}$ [‰]\"\n", + " )\n", + "for R_vapour_factor in R_vapour_factors:\n", + " axs[my_label(R_vapour_factor, isotopes[0])].set_ylabel(\n", + " f\"$\\\\delta_{{vapour}}$\\n{R_vapour_factor-1:.3g} [‰]\", rotation=\"horizontal\", labelpad=88\n", + " )\n", + "\n", + "\n", + "fig.suptitle(\"e-folding relaxation timescale for heavy isotopes\", fontsize=fontsize + 2)\n", + "fig.supylabel(\"Relative humidity [%]\", x=0.115, fontsize=fontsize)\n", + "\n", + "\n", + "cb = fig.colorbar(\n", + " pcm, ax=list(axs.values()), location=\"right\", pad=0.02, fraction=0.03, aspect=50\n", + ")\n", + "ticks = cb.get_ticks()\n", + "cb.set_ticks(np.append(ticks, bolin[1]))\n", + "cb.ax.set_yticklabels(np.append(ticks, \"\"))\n", + "cb.ax.yaxis.set_tick_params(length=10, width=2)\n", + "cb.ax.tick_params(direction=\"in\", length=25, width=1.5, right=True, left=False)\n", + "cb.ax.set_title(\"$\\\\tau$ [s]\")\n", + "\n", + "cb.ax.text(\n", + " 1.5,\n", + " bolin[1],\n", + " \"Bolin\",\n", + " va=\"bottom\",\n", + " ha=\"left\",\n", + " transform=cb.ax.get_yaxis_transform(),\n", + ")\n", + "\n", + "show_plot(\"comparison\", inline_format='png')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8ad862eab1ed294e", + "metadata": { + "ExecuteTime": { + "end_time": "2025-06-27T19:16:43.378430Z", + "start_time": "2025-06-27T19:16:43.377021Z" + } + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/PySDM/source/examples/PySDM_examples/_HOWTOs/__init__.py b/PySDM/source/examples/PySDM_examples/_HOWTOs/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..339f0b351ba173e2f6ae232fd7f16296d2daac0d --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/_HOWTOs/__init__.py @@ -0,0 +1,7 @@ +""" +paraview_hello_world.ipynb: +.. include:: ./paraview_hello_world.ipynb.badges.md + +dimensional_analysis.ipynb: +.. include:: ./dimensional_analysis.ipynb.badges.md +""" diff --git a/PySDM/source/examples/PySDM_examples/_HOWTOs/dimensional_analysis.ipynb b/PySDM/source/examples/PySDM_examples/_HOWTOs/dimensional_analysis.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..6d7296b25cb8db232efe83d6e8d09a6d908e2a17 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/_HOWTOs/dimensional_analysis.ipynb @@ -0,0 +1,176 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c1925b9647eb39e9", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/_HOWTOs/dimensional_analysis.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/_HOWTOs/dimensional_analysis.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/_HOWTOs/dimensional_analysis.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "57a5c59c4cb8a1ec", + "metadata": {}, + "source": [ + "### PySDM dimensional analysis HOWTO\n", + "- PySDM depends on the [Pint](https://pint.readthedocs.io/en/stable/) package which offers dimensional analysis (physical units checks) of Python code \n", + "- this improves code readibility with expressions such as `p = 1000 * si.hPa`\n", + "- using [Pint](https://pint.readthedocs.io/en/stable/), `si` is an instance of [`pint.UnitRegistry`](https://pint.readthedocs.io/en/stable/api/base.html#pint.UnitRegistry) \n", + "- however, for performance reasons, by default PySDM uses a custom drop-in-replacement [`FakeUnitRegistry`](https://open-atmos.github.io/PySDM/PySDM/physics/impl/fake_unit_registry.html#FakeUnitRegistry)\n", + "- this way, we keep the readibility advantage, while not incurring any performance overhead\n", + "- moreover, this makes the code potentially Numba JIT-compilable!\n", + "- we also provide a way to leverage the dimensional analysis benefit for testing purposes\n", + "- to this end, the test code can use the [`DimensionalAnalysis`](https://open-atmos.github.io/PySDM/PySDM/physics/dimensional_analysis.html#DimensionalAnalysis) context manager\n", + "- code below demonstrate how a single unit-equipped function can be used with and without unit checks " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "97dab670c5cf8d8b", + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-04T13:27:07.009798Z", + "start_time": "2024-12-04T13:27:07.003077Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "markdown", + "id": "19a8f1dc-a5ea-461b-9019-50d25dfa7bc6", + "metadata": {}, + "source": [ + "#### sample physics-related code" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "6ea31783d8e0e849", + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-04T13:27:08.240983Z", + "start_time": "2024-12-04T13:27:07.013763Z" + } + }, + "outputs": [], + "source": [ + "from PySDM import physics\n", + "\n", + "def code():\n", + " si = physics.si\n", + "\n", + " p = 1000 * si.hPa\n", + " T = 300 * si.K\n", + " R = 286 * si.J / si.K / si.kg\n", + "\n", + " rho = p / R / T\n", + " return rho" + ] + }, + { + "cell_type": "markdown", + "id": "183bcca3-5546-4676-a6bc-de3a1dd76ccb", + "metadata": {}, + "source": [ + "#### sample unit-unaware usage (default)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "287ae3f109f11bb6", + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-04T13:27:08.341648Z", + "start_time": "2024-12-04T13:27:08.339564Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1.2\n" + ] + } + ], + "source": [ + "result_unit_unaware = code()\n", + "print(f\"{result_unit_unaware:.2g}\")" + ] + }, + { + "cell_type": "markdown", + "id": "da1669cf-653b-4138-9ff9-8fac303ad8c4", + "metadata": {}, + "source": [ + "#### sample unit-aware usage (e.g., for testing)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "85bf8e7bc2ca98a", + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-04T13:27:08.671915Z", + "start_time": "2024-12-04T13:27:08.402327Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.012 hectopascal * kilogram / joule\n", + "1.2 kilogram / meter ** 3\n" + ] + } + ], + "source": [ + "from PySDM.physics.dimensional_analysis import DimensionalAnalysis\n", + "\n", + "with DimensionalAnalysis():\n", + " result_unit_aware = code()\n", + "\n", + "assert result_unit_aware.check(\"[mass] / [volume]\")\n", + "print(f\"{result_unit_aware:.2g}\")\n", + "print(f\"{result_unit_aware.to_base_units():.2g}\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/PySDM/source/examples/PySDM_examples/_HOWTOs/paraview_hello_world.ipynb b/PySDM/source/examples/PySDM_examples/_HOWTOs/paraview_hello_world.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..6660c3216e6ec007a6ba57264eb55bd8b1c94a19 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/_HOWTOs/paraview_hello_world.ipynb @@ -0,0 +1,275 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a491199946368a38", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/_HOWTOs/paraview_hello_world.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/_HOWTOs/paraview_hello_world.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/_HOWTOs/paraview_hello_world.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "35dba30bf42bd326", + "metadata": {}, + "source": [ + "### TODO #1417 add description\n", + "### TODO #1490 fix installation on binder\n", + "For more informations about Paraview please refere to [Paraview documentation](https://docs.paraview.org/en/latest/Tutorials/SelfDirectedTutorial/)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "93289adf665b5c7f", + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-09T08:27:59.767037Z", + "start_time": "2025-05-09T08:27:59.763173Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "e3537b5faa81e11f", + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-09T08:28:06.258130Z", + "start_time": "2025-05-09T08:28:00.353771Z" + } + }, + "outputs": [], + "source": [ + "import subprocess\n", + "SUBPROCESS_ENV = os.environ.copy()\n", + "\n", + "if 'google.colab' in sys.modules:\n", + " !apt-get install -qq ghostscript\n", + " !wget -nv \"https://paraview.org/paraview-downloads/download.php?submit=Download&version=v5.13&type=binary&os=Linux&downloadFile=ParaView-5.13.1-egl-MPI-Linux-Python3.10-x86_64.tar.gz\" -O paraview.tar.gz\n", + " !tar xzf paraview.tar.gz\n", + " SUBPROCESS_ENV['PATH'] += ':' + subprocess.check_output(['bash', '-c', \"echo `pwd`/`dirname ParaView*/bin/pvpython`\"], text=True)[:-1]\n", + " \n", + " # check if Ghostscript's ps2pdf works\n", + " assert subprocess.check_call(['type', 'ps2pdf'], shell=True) == 0\n", + " \n", + "# check if Paraview's pvpython works\n", + "assert subprocess.check_call(['pvpython', '--version'], env=SUBPROCESS_ENV) == 0\n", + "assert subprocess.check_call(['pvpython', '-c', 'import paraview'], env=SUBPROCESS_ENV) == 0" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "ee889545", + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-09T08:28:08.607057Z", + "start_time": "2025-05-09T08:28:06.263583Z" + } + }, + "outputs": [], + "source": [ + "from PySDM_examples.Arabas_et_al_2015 import Settings, SpinUp\n", + "from PySDM_examples.utils.kinematic_2d import Simulation, Storage\n", + "from PySDM.exporters import VTKExporter\n", + "from PySDM_examples.utils import ProgBarController\n", + "from PySDM.physics import si\n", + "from PySDM import products as PySDM_products\n", + "import PySDM_examples\n", + "import glob\n", + "import platform\n", + "import pathlib" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "f0d2581f", + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-09T08:28:08.617918Z", + "start_time": "2025-05-09T08:28:08.615072Z" + } + }, + "outputs": [], + "source": [ + "products = [\n", + " PySDM_products.EffectiveRadius(unit='um'),\n", + " PySDM_products.FlowVelocityComponent(component = 0, name = 'cx'),\n", + " PySDM_products.FlowVelocityComponent(component = 1, name = 'cy')\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "25232d9f", + "metadata": {}, + "source": [ + "##### 1. run a simulations saving output to VTK files" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c7634bb25cdbbe46", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "61d7c35b77214683bb1b3ed29d10c44d", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "FloatProgress(value=0.0, description='progress:', max=1.0)" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "settings = Settings()\n", + "settings.simulation_time = 100 * si.minute\n", + "storage = Storage()\n", + "simulation = Simulation(settings, storage, SpinUp=SpinUp)\n", + "simulation.reinit(products)\n", + "\n", + "vtk_exporter = VTKExporter(path='.') \n", + "\n", + "simulation.run(ProgBarController(\"progress:\"), vtk_exporter=vtk_exporter)\n", + "vtk_exporter.write_pvd()" + ] + }, + { + "cell_type": "markdown", + "id": "066f9c18", + "metadata": {}, + "source": [ + "#### 2. execute ``PySDM_examples/utils/pvanim.py`` script using `pvpython`" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "154ce67d9a84a51d", + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-09T08:52:00.913288Z", + "start_time": "2025-05-09T08:51:55.311337Z" + } + }, + "outputs": [], + "source": [ + "pvanim = pathlib.Path(PySDM_examples.__file__).parent / \"utils\" / \"pvanim.py\"\n", + "result = subprocess.run([\"pvpython\", str(pvanim), '--help'], check=True, env=SUBPROCESS_ENV)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "23e0cf61", + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-09T08:27:54.021909Z", + "start_time": "2025-05-09T08:25:42.918680Z" + } + }, + "outputs": [], + "source": [ + "product = pathlib.Path(\"./output/sd_products.pvd\").absolute()\n", + "attributes = pathlib.Path(\"./output/sd_attributes.pvd\").absolute()\n", + "\n", + "try:\n", + " for mode in ('light', 'dark'):\n", + " result = subprocess.run(\n", + " [\n", + " \"pvpython\",\n", + " \"--force-offscreen-rendering\",\n", + " str(pvanim),\n", + " str(product),\n", + " str(attributes),\n", + " str(pathlib.Path('./output').absolute()),\n", + " \"--animationname\", \"docs_intro_animation.ogv\",\n", + " \"--mode\", mode,\n", + " ] + ([\"--animationframename\", \"last_animation_frame.pdf\"] if mode == 'light' else []),\n", + " check=platform.system() != \"Windows\",\n", + " capture_output=True,\n", + " text=True,\n", + " env=SUBPROCESS_ENV,\n", + " )\n", + "except subprocess.CalledProcessError as e:\n", + " print(e.stderr)\n", + " assert False" + ] + }, + { + "cell_type": "markdown", + "id": "8a95f135", + "metadata": {}, + "source": [ + "#### 3. reduce file size for generated pdf files" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "3e170fdc", + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-09T08:27:54.022827Z", + "start_time": "2025-05-09T08:26:10.268239Z" + } + }, + "outputs": [], + "source": [ + "if platform.system() != 'Windows':\n", + " for file in glob.glob('output/anim_frame_*.pdf'):\n", + " subprocess.run(['ps2pdf', file, file+'_'], capture_output=True, check=True)\n", + " subprocess.run(['mv', file+'_', file], check=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7df317e6", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "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.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/PySDM/source/examples/PySDM_examples/__init__.py b/PySDM/source/examples/PySDM_examples/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e5bfa51080478c5e65f9af18f78f05201d2355bf --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/__init__.py @@ -0,0 +1,14 @@ +""" +.. include:: ../docs/pysdm_examples_landing.md +""" + +from importlib.metadata import PackageNotFoundError, version +import PySDM + +try: + __version__ = version(__name__) +except PackageNotFoundError: + # package is not installed + pass + +# assert PySDM.__version__ == __version__ diff --git a/PySDM/source/examples/PySDM_examples/deJong_Azimi/__init__.py b/PySDM/source/examples/PySDM_examples/deJong_Azimi/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..856544a5136f2d77a6ca19ce33e964686528cf91 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/deJong_Azimi/__init__.py @@ -0,0 +1,14 @@ +""" +box- and single-column coalescence-focused examples used to test new +moment-based microphysics in (Cloudy.jl)[https://github.com/CliMA/Cloudy.jl] + +box.ipynb: +.. include:: ./box.ipynb.badges.md + +rainshaft.ipynb: +.. include:: ./rainshaft.ipynb.badges.md +""" + +# pylint: disable=invalid-name +from .settings1D import Settings1D +from .simulation_0D import run_box diff --git a/PySDM/source/examples/PySDM_examples/deJong_Azimi/box.ipynb b/PySDM/source/examples/PySDM_examples/deJong_Azimi/box.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..d0ddb58b6a5881bbf046cc6f1a46740e4b271151 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/deJong_Azimi/box.ipynb @@ -0,0 +1,8926 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/deJong_Azimi/box.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/deJong_Azimi/box.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/deJong_Azimi/box.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "TODO #1417" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-06T10:42:36.912566Z", + "start_time": "2024-12-06T10:42:36.904568Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from typing import Optional\n", + "\n", + "import numpy as np\n", + "from scipy.special import gamma\n", + "from matplotlib import pyplot\n", + "\n", + "from open_atmos_jupyter_utils import show_plot\n", + "\n", + "from PySDM_examples.deJong_Azimi import run_box, cloudy_data_0d\n", + "\n", + "from PySDM import Formulae\n", + "from PySDM.physics import si, in_unit\n", + "from PySDM.dynamics.collisions.collision_kernels import SimpleGeometric, Golovin\n", + "from PySDM.dynamics.collisions.coalescence_efficiencies import ConstEc\n", + "from PySDM.initialisation.spectra import Gamma" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "class Settings0D: # pylint: disable=too-many-instance-attributes,too-few-public-methods,missing-class-docstring\n", + " def __init__(\n", + " self,\n", + " kernel: object,\n", + " steps: list,\n", + " radius_bins_edges: Optional[np.ndarray] = None,\n", + " ):\n", + " self.kernel = kernel\n", + " self.n_sd = 2**12\n", + " self.n_part = 100 / si.cm**3\n", + " self.dv = 1 * si.m**3\n", + " self.rho = 1000 * si.kg / si.m**3\n", + " self.rhod = 1 * si.kg / si.m**3\n", + " self.dt = 1 * si.s\n", + " self.adaptive = True\n", + " self.steps = steps\n", + " self.coal_eff = ConstEc(Ec=1.0)\n", + " self.spectrum = Gamma(\n", + " norm_factor=self.n_part * self.dv, k=1.0, theta=1e5 * si.um**3\n", + " )\n", + " self.radius_bins_edges = radius_bins_edges if radius_bins_edges is not None else np.logspace(\n", + " np.log10(8.0 * si.um), np.log10(5000 * si.um), num=64, endpoint=True\n", + " )\n", + " self.formulae = Formulae(terminal_velocity=\"PowerSeries\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "def dvdlnr_gamma(m, N, theta, k):\n", + " return N * m**(k-1) * np.exp(-m / theta) / theta**k / gamma(k)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Golovin Kernel" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [], + "source": [ + "settings_a = Settings0D(\n", + " kernel = Golovin(b=5e3 * si.s),\n", + " steps = [0, 60, 120]\n", + ")\n", + "res_a = run_box(settings_a)" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-02-13T10:45:31.415725\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.5.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "6c104c5f85524c26b564c4d6428efb28", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./box1.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = pyplot.subplots(ncols=1, sharey=True, figsize=(6,4), dpi=200)\n", + "\n", + "m_plt_ug = np.logspace(-1.0, 6.0, 100)\n", + "\n", + "r1_plt = settings_a.formulae.trivia.radius(volume=m_plt_ug * si.ug / settings_a.rho)\n", + "\n", + "for (j, step) in enumerate(settings_a.steps):\n", + " line = ax.step(\n", + " in_unit(res_a.radius_bins_left_edges, si.um),\n", + " res_a.dv_dlnr[j] * settings_a.rho,\n", + " label='init, PySDM' if j==0 else (str(step) + 's')\n", + " )\n", + " a1_dmdlnr = dvdlnr_gamma(\n", + " m_plt_ug,\n", + " cloudy_data_0d.MOM_data['Golovin']['aDists'][j][0],\n", + " cloudy_data_0d.MOM_data['Golovin']['aDists'][j][1],\n", + " cloudy_data_0d.MOM_data['Golovin']['aDists'][j][2]\n", + " ) * 3 * m_plt_ug**2 * si.ug / si.cm**3\n", + " ax.plot(\n", + " in_unit(r1_plt, si.um),\n", + " in_unit(a1_dmdlnr, si.kg / si.m**3),\n", + " '--',\n", + " label='init, Cloudy.jl, 1 mode' if j==0 else '_',\n", + " color=line[-1].get_color()\n", + " )\n", + "\n", + " b1_dmdlnr = dvdlnr_gamma(\n", + " m_plt_ug,\n", + " cloudy_data_0d.MOM_data['Golovin']['bDists1'][j][0],\n", + " cloudy_data_0d.MOM_data['Golovin']['bDists1'][j][1],\n", + " cloudy_data_0d.MOM_data['Golovin']['bDists1'][j][2]\n", + " ) * 3 * m_plt_ug**2 * si.ug / si.cm**3\n", + " b1_dmdlnr += dvdlnr_gamma(\n", + " m_plt_ug,\n", + " cloudy_data_0d.MOM_data['Golovin']['bDists2'][j][0],\n", + " cloudy_data_0d.MOM_data['Golovin']['bDists2'][j][1],\n", + " cloudy_data_0d.MOM_data['Golovin']['bDists2'][j][2]\n", + " ) * 3 * m_plt_ug**2 * si.ug / si.cm**3\n", + " ax.plot(\n", + " in_unit(r1_plt, si.um),\n", + " b1_dmdlnr,\n", + " '.',\n", + " label='Cloudy.jl, 2 modes' if j==0 else '_',\n", + " color=line[-1].get_color()\n", + " )\n", + "\n", + "ax.set_xscale(\"log\")\n", + "ax.set_xlabel(\"particle radius (um)\")\n", + "ax.set_ylabel(\"dm/dlnR (kg/m$^3$)\")\n", + "ax.legend()\n", + "show_plot('box1.pdf')" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-02-13T10:18:05.147495\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.5.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "e17fc525f6a849dc897010c4bd9cf1fa", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./box1_moments.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = pyplot.subplots(nrows=3, sharex=True, figsize=(4,6), dpi=200, tight_layout=True)\n", + "for i in range(3):\n", + " line = ax[i].plot(\n", + " cloudy_data_0d.MOM_data['Golovin']['t'],\n", + " cloudy_data_0d.MOM_data['Golovin']['aMoments'][i],\n", + " '--',\n", + " label='Cloudy.jl, 1 mode'\n", + " )\n", + " line = ax[i].plot(\n", + " cloudy_data_0d.MOM_data['Golovin']['t'],\n", + " cloudy_data_0d.MOM_data['Golovin']['bMoments'][i, 0] \n", + " + cloudy_data_0d.MOM_data['Golovin']['bMoments'][i, 1],\n", + " '.-',\n", + " label='Cloudy.jl, 2 modes'\n", + " )\n", + " ax[i].plot(\n", + " cloudy_data_0d.MOM_data['Golovin']['t'],\n", + " cloudy_data_0d.MOM_data['Golovin']['bMoments'][i, 0],\n", + " ':',\n", + " linewidth=1.5, label=' mode A', color=line[-1].get_color()\n", + " )\n", + " ax[i].plot(\n", + " cloudy_data_0d.MOM_data['Golovin']['t'],\n", + " cloudy_data_0d.MOM_data['Golovin']['bMoments'][i, 1],\n", + " ':',\n", + " linewidth=1.5, label=' mode B', color=line[-1].get_color()\n", + " )\n", + " ax[i].plot(\n", + " np.asarray(settings_a.steps) * settings_a.dt,\n", + " in_unit(\n", + " res_a.moments[:, i] * settings_a.dv * settings_a.rho**i,\n", + " si.ug**i / si.cm**3\n", + " ),\n", + " marker='o', linestyle='None', label='PySDM'\n", + " )\n", + "ax[0].legend()\n", + "ax[0].set_ylabel('$M_0$ (# cm$^{-3}$)')\n", + "ax[1].set_ylabel('$M_1$ (# µg cm$^{-3}$)')\n", + "ax[1].set_ylim([0.0, 15.0])\n", + "ax[2].set_ylabel('$M_2$ (# µg$^2$ cm$^{-3}$)')\n", + "ax[2].set_xlabel('time (s)')\n", + "ax[2].ticklabel_format(axis='y', style='sci', scilimits=(0,2))\n", + "\n", + "show_plot('box1_moments.pdf')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Geometric" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [], + "source": [ + "settings_b = Settings0D(\n", + " kernel = SimpleGeometric(C=1e6 * np.pi),\n", + " steps = [0, 240],\n", + " radius_bins_edges = np.logspace(\n", + " np.log10(8.0 * si.um), np.log10(1000 * si.um), num=64, endpoint=True\n", + " )\n", + ")\n", + "res_b = run_box(settings_b)" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-02-13T10:46:00.286216\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.5.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "bdd6cd07f9d64c36850eee80e2a0016f", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./box2.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = pyplot.subplots(ncols=1, sharey=True, figsize=(6,4), dpi=200)\n", + "\n", + "m_plt_ug = np.logspace(-3.0, 3.0, 100)\n", + "\n", + "r2_plt = settings_b.formulae.trivia.radius(volume=m_plt_ug * si.ug / settings_b.rho)\n", + "\n", + "for (j, step) in enumerate(settings_b.steps):\n", + " line = ax.step(\n", + " in_unit(res_b.radius_bins_left_edges, si.um),\n", + " res_b.dv_dlnr[j] * settings_b.rho,\n", + " label='init, PySDM' if j==0 else (str(step) + 's')\n", + " )\n", + " \n", + " a2_dmdlnr = dvdlnr_gamma(\n", + " m_plt_ug,\n", + " cloudy_data_0d.MOM_data['Geometric']['aDists'][j][0],\n", + " cloudy_data_0d.MOM_data['Geometric']['aDists'][j][1],\n", + " cloudy_data_0d.MOM_data['Geometric']['aDists'][j][2]\n", + " ) * 3 * m_plt_ug**2 * si.ug / si.cm**3\n", + " ax.plot(\n", + " in_unit(r2_plt, si.um),\n", + " in_unit(a2_dmdlnr, si.kg/si.m**3),\n", + " '--',\n", + " label='Cloudy.jl, 1 mode' if j==0 else '_',\n", + " color=line[-1].get_color()\n", + " )\n", + "\n", + " b2_dmdlnr = dvdlnr_gamma(\n", + " m_plt_ug,\n", + " cloudy_data_0d.MOM_data['Geometric']['bDists1'][j][0],\n", + " cloudy_data_0d.MOM_data['Geometric']['bDists1'][j][1],\n", + " cloudy_data_0d.MOM_data['Geometric']['bDists1'][j][2]\n", + " ) * 3 * m_plt_ug**2 * si.ug / si.cm**3\n", + " b2_dmdlnr += dvdlnr_gamma(\n", + " m_plt_ug,\n", + " cloudy_data_0d.MOM_data['Geometric']['bDists2'][j][0],\n", + " cloudy_data_0d.MOM_data['Geometric']['bDists2'][j][1],\n", + " cloudy_data_0d.MOM_data['Geometric']['bDists2'][j][2]\n", + " ) * 3 * m_plt_ug**2 * si.ug / si.cm**3\n", + " ax.plot(\n", + " in_unit(r2_plt, si.um),\n", + " in_unit(b2_dmdlnr, si.kg/si.m**3),\n", + " '.-',\n", + " label='Cloudy.jl, 2 modes' if j==0 else '_',\n", + " color=line[-1].get_color()\n", + " )\n", + "\n", + "ax.set_xscale(\"log\")\n", + "ax.set_xlabel(\"particle radius (um)\")\n", + "ax.set_ylabel(\"dm/dlnR (kg/m$^3$)\")\n", + "ax.legend()\n", + "show_plot('box2.pdf')" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "settings_c = Settings0D(\n", + " kernel = settings_b.kernel,\n", + " steps = np.linspace(0, 240, 41, dtype=int),\n", + " radius_bins_edges = settings_b.radius_bins_edges,\n", + ")\n", + "res_c = run_box(settings_c)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-02-13T10:31:51.428960\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.5.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f4cc8a19facc427683e26911d7064ba1", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./box2_moments.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = pyplot.subplots(nrows=3, sharex=True, figsize=(4,6), dpi=200, tight_layout=True)\n", + "for i in range(3):\n", + " line = ax[i].plot(\n", + " cloudy_data_0d.MOM_data['Geometric']['t'],\n", + " cloudy_data_0d.MOM_data['Geometric']['aMoments'][i],\n", + " '--', label='Cloudy.jl, 1 mode'\n", + " )\n", + " line = ax[i].plot(\n", + " cloudy_data_0d.MOM_data['Geometric']['t'],\n", + " cloudy_data_0d.MOM_data['Geometric']['bMoments'][i, 0]\n", + " + cloudy_data_0d.MOM_data['Geometric']['bMoments'][i, 1],\n", + " '.-', label='Cloudy.jl, 2 modes'\n", + " )\n", + " ax[i].plot(\n", + " cloudy_data_0d.MOM_data['Geometric']['t'],\n", + " cloudy_data_0d.MOM_data['Geometric']['bMoments'][i,0],\n", + " ':',\n", + " linewidth=1.5, label=' mode A', color=line[-1].get_color()\n", + " )\n", + " ax[i].plot(\n", + " cloudy_data_0d.MOM_data['Geometric']['t'],\n", + " cloudy_data_0d.MOM_data['Geometric']['bMoments'][i, 1],\n", + " ':',\n", + " linewidth=1.5, label=' mode B', color=line[-1].get_color()\n", + " )\n", + " ax[i].plot(\n", + " np.asarray(settings_c.steps) * settings_c.dt,\n", + " in_unit(\n", + " res_c.moments[:, i] * settings_c.dv * settings_c.rho**i,\n", + " si.ug**i / si.cm**3\n", + " ),\n", + " marker='.',\n", + " linestyle='None',\n", + " label='PySDM'\n", + " )\n", + "\n", + "ax[2].legend()\n", + "ax[0].set_ylabel('$M_0$ (# cm$^{-3}$)')\n", + "ax[1].set_ylabel('$M_1$ (# µg cm$^{-3}$)')\n", + "ax[1].set_ylim([0.0, 15.0])\n", + "ax[2].set_ylim([0.0, 3e2])\n", + "ax[2].set_ylabel('$M_2$ (# µg$^2$ cm$^{-3}$)')\n", + "ax[2].set_xlabel('time (s)')\n", + "ax[2].ticklabel_format(axis='y', style='sci', scilimits=(0,2))\n", + "\n", + "show_plot('box2_moments.pdf')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.13" + }, + "vscode": { + "interpreter": { + "hash": "b43cf254c70d60c2e21a7f71ba113e70c1694742e72407132919c841d907074b" + } + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/PySDM/source/examples/PySDM_examples/deJong_Azimi/cloudy_data.py b/PySDM/source/examples/PySDM_examples/deJong_Azimi/cloudy_data.py new file mode 100644 index 0000000000000000000000000000000000000000..4887d4d118ebdf0964ae4f942743962b3864ed0e --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/deJong_Azimi/cloudy_data.py @@ -0,0 +1,371 @@ +import numpy as np + +Z = np.linspace(0.0, 3000.0, 20) / 1000 +NC = np.array( + [ + [ + 0.002325942800455995, + 0.0028045022356462572, + 0.003452211545804375, + 0.004361877640877965, + 0.005702479217508372, + 0.007812475748456827, + 0.011468941392117338, + 0.01888454890111773, + 0.039528401551653096, + 0.2356787814538211, + 9.90222080639631, + 9.944680372872213, + 9.948230520394494, + 9.92828984604994, + 9.49434950135968, + 0.060141075852780945, + 0.023917441369691872, + 0.013590312401779991, + 0.00893428827291265, + 0.006376138306461998, + ], + [ + 0.005322014757656553, + 0.010480022319206528, + 0.025889114642834388, + 0.07027637984750808, + 0.1846578336060549, + 0.4341420453704166, + 0.8709339382008977, + 1.4526980887520684, + 2.016594898467415, + 2.4045238186923696, + 2.579754970394283, + 2.578563190147033, + 2.3791690313423275, + 1.7682308790889112, + 0.7721032508385711, + 0.01991858759231357, + 0.010155146035355484, + 0.0057644342569305234, + 0.002836371204245902, + 0.0008570715559907928, + ], + [ + 0.08662893464816847, + 0.15520311679346102, + 0.2522813401209703, + 0.371195396866207, + 0.49992082338075977, + 0.6298742026425381, + 0.7609509414360253, + 0.8962365323250485, + 1.0294941603786836, + 1.1334184385711124, + 1.1568860052088945, + 1.0412336653823127, + 0.7660356607147669, + 0.4068380577304884, + 0.12130475619620494, + 0.007647406381002058, + 0.0038766052508024296, + 0.0018366503445636489, + 0.0006757763437823614, + 0.00014251192022876204, + ], + [ + 0.19820963209310652, + 0.2528661807124882, + 0.3158570884138644, + 0.38364913993931354, + 0.4493832884815257, + 0.5027490610084232, + 0.5311511760149372, + 0.5227660441149257, + 0.4715289221976368, + 0.3822917550688269, + 0.27222944741946864, + 0.16541480707209608, + 0.08242031539019055, + 0.03156306801519871, + 0.00795647799253679, + 0.0011153510331640794, + 0.0004884291395013965, + 0.00018766025678891934, + 5.6861581517651256e-5, + 1.0878733924819186e-5, + ], + ] +) +QC = np.array( + [ + [ + 0.00023259428004559948, + 0.0002804502235646257, + 0.0003452211545804375, + 0.0004361877640877965, + 0.0005702479217508372, + 0.0007812475748456827, + 0.0011468941392117338, + 0.0018884548901117728, + 0.003952840155165309, + 0.02356787814538211, + 0.9902220806396309, + 0.9944680372872212, + 0.9948230520394494, + 0.992828984604994, + 0.9494349501359681, + 0.0060141075852780945, + 0.002391744136969187, + 0.0013590312401779991, + 0.0008934288272912649, + 0.0006376138306461998, + ], + [ + 0.0007446008324123797, + 0.0017062385212967938, + 0.0045521266894452, + 0.01232248611337005, + 0.030897004687650706, + 0.06802160484300256, + 0.1270286320846756, + 0.1975947205304386, + 0.25774910322755645, + 0.29241568674609913, + 0.30261930788409075, + 0.29164719358476193, + 0.2508915891345886, + 0.16612800717742593, + 0.061328835766217636, + 0.0016661182121968014, + 0.0008661265295733964, + 0.000476358290888631, + 0.00021705957692457706, + 5.7921213943590406e-5, + ], + [ + 0.01656031383026699, + 0.028077798198055417, + 0.042926091821587964, + 0.059297486773231455, + 0.07508578878498474, + 0.08928990048440437, + 0.1023228797711026, + 0.11475197203548103, + 0.12539200432700545, + 0.13008943253916275, + 0.12258521422776973, + 0.09864887346844652, + 0.06227244046416078, + 0.027129943467786286, + 0.006445011737676357, + 0.000519163514737142, + 0.0002532092239246288, + 0.00010820782161964733, + 3.4068535970606276e-5, + 5.79220821416827e-6, + ], + [ + 0.030757168729973833, + 0.0373735991829509, + 0.04434591786357304, + 0.05090713814014857, + 0.05590771651108847, + 0.05797636144416201, + 0.05590303149164768, + 0.04921078599960091, + 0.038700625460403475, + 0.026521223162384176, + 0.015391250719866753, + 0.0073065234193505635, + 0.00271332219262275, + 0.0007486848626273913, + 0.00015588378375704442, + 3.626367328902605e-5, + 1.308012805783289e-5, + 3.771780816369582e-6, + 7.63771805417599e-7, + 8.110312094613359e-8, + ], + ] +) +NR = np.array( + [ + [ + 2.3259428004559948e-10, + 2.804502235646257e-10, + 3.4522115458043745e-10, + 4.361877640877965e-10, + 5.702479217508372e-10, + 7.812475748456827e-10, + 1.1468941392117337e-9, + 1.888454890111773e-9, + 3.952840155165309e-9, + 2.356787814538211e-8, + 9.90222080639631e-7, + 9.944680372872212e-7, + 9.948230520394495e-7, + 9.92828984604994e-7, + 9.494349501359681e-7, + 6.014107585278094e-9, + 2.391744136969187e-9, + 1.359031240177999e-9, + 8.934288272912649e-10, + 6.376138306461997e-10, + ], + [ + 0.0003533420532791116, + 0.0012826071194146278, + 0.00424553213537039, + 0.012673002046450213, + 0.03362575699559721, + 0.07780646668937528, + 0.15331141994790556, + 0.2511957333816394, + 0.33814936627199305, + 0.3780523095075203, + 0.3598088317322889, + 0.2931296678544589, + 0.19341647440335039, + 0.08651190873395816, + 0.01810784482882837, + 4.1494189890466366e-6, + 1.094349113436064e-6, + 3.546676197032568e-7, + 9.14315623350356e-8, + 1.2064015228861402e-8, + ], + [ + 0.04286183602944602, + 0.06852467872215309, + 0.09903215887493476, + 0.12997070440033048, + 0.15687232059974845, + 0.17740978878087613, + 0.19118547711395129, + 0.19691911172103171, + 0.18984923336031656, + 0.16372127417779328, + 0.11889273491240387, + 0.06789317377731022, + 0.02793297053418389, + 0.007207574524451838, + 0.0008713383759906845, + 7.702005312220904e-7, + 2.014372171094809e-7, + 4.725224070296279e-8, + 7.766049118140948e-9, + 6.427033141822792e-10, + ], + [ + 0.061874092050410494, + 0.07128188742881585, + 0.08111795686596689, + 0.08980451732153531, + 0.09449050033043897, + 0.09134074347469225, + 0.07752987289413114, + 0.05487127635724207, + 0.030981982591521077, + 0.013660394132669304, + 0.004693803464682259, + 0.001247946986806364, + 0.0002471714862400846, + 3.290890468105509e-5, + 2.207068943645234e-6, + 1.0190141478651551e-8, + 2.047865946736227e-9, + 3.3636644705214316e-10, + 3.774223029783484e-11, + 1.9828529963408933e-12, + ], + ] +) +QR = np.array( + [ + [ + 2.3259428004559948e-10, + 2.804502235646257e-10, + 3.4522115458043745e-10, + 4.361877640877965e-10, + 5.702479217508372e-10, + 7.812475748456827e-10, + 1.1468941392117337e-9, + 1.888454890111773e-9, + 3.952840155165309e-9, + 2.356787814538211e-8, + 9.90222080639631e-7, + 9.944680372872212e-7, + 9.948230520394495e-7, + 9.92828984604994e-7, + 9.494349501359681e-7, + 6.014107585278094e-9, + 2.391744136969187e-9, + 1.359031240177999e-9, + 8.934288272912649e-10, + 6.376138306461997e-10, + ], + [ + 0.00035200151617089407, + 0.0013111967057274174, + 0.0044786571531358664, + 0.013892547736570221, + 0.03863471127263126, + 0.09460221788307477, + 0.19892355317632127, + 0.34815123733343706, + 0.491830001583502, + 0.5511897143312499, + 0.4932820778423064, + 0.3591619186873109, + 0.20663782645801065, + 0.0795659654091024, + 0.014340018974944115, + 2.6031739369300225e-6, + 6.868825051011121e-7, + 2.2197596523612783e-7, + 5.69233748241551e-8, + 7.456832323236176e-9, + ], + [ + 0.11482311944110231, + 0.1953165800079347, + 0.2975360397035871, + 0.40314885321904576, + 0.4844836094841661, + 0.5176110996740826, + 0.49495233107947906, + 0.4267437023807917, + 0.3323077368114287, + 0.23095573846997497, + 0.1386092144185074, + 0.06764570083287864, + 0.024471513975755962, + 0.005687897829495739, + 0.0006318064836266766, + 4.7346216121926047e-7, + 1.2338769749465383e-7, + 2.8730188420905227e-8, + 4.6732350569938025e-9, + 3.823035518651779e-10, + ], + [ + 0.2051516890449052, + 0.17270749883053185, + 0.14171673193376189, + 0.11262150188264747, + 0.08580121859435079, + 0.06174295102301346, + 0.04112156435049741, + 0.02469744659130083, + 0.012972304357154157, + 0.005761888080833301, + 0.002087794572011011, + 0.0005919792707091766, + 0.00012376637910333285, + 1.7087512894278817e-5, + 1.1819761014876744e-6, + 5.503422402098125e-9, + 1.0561195797504775e-9, + 1.6226072373027708e-10, + 1.7126710513565556e-11, + 9.041044132623166e-13, + ], + ] +) diff --git a/PySDM/source/examples/PySDM_examples/deJong_Azimi/cloudy_data_0d.py b/PySDM/source/examples/PySDM_examples/deJong_Azimi/cloudy_data_0d.py new file mode 100644 index 0000000000000000000000000000000000000000..3ee4994c1f67715feec2546545da8fbf279241b9 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/deJong_Azimi/cloudy_data_0d.py @@ -0,0 +1,392 @@ +import numpy as np + +MOM_data = { + "Golovin": { + "t": np.array( + [ + 0.0, + 10.0, + 20.0, + 30.0, + 40.0, + 50.0, + 60.0, + 70.0, + 80.0, + 90.0, + 100.0, + 110.0, + 120.0, + ] + ), + "aM0": np.array( + [ + 100.0, + 60.41666666666108, + 36.50173611110569, + 22.05313223379227, + 13.32376739124922, + 8.049776132212969, + 4.863406413211965, + 2.938308041315548, + 1.775227774961471, + 1.072533447372555, + 0.6479889577875856, + 0.39149332866333514, + 0.23652721940076313, + ] + ), + "aM1": np.array( + [ + 10.0, + 10.0, + 10.0, + 10.000000000000002, + 10.000000000000002, + 9.999999999999991, + 10.000000000000007, + 10.000000000000002, + 9.999999999999956, + 9.999999999999966, + 9.999999999999872, + 10.000000000000062, + 10.000000000000108, + ] + ), + "aM2": np.array( + [ + 2.0, + 5.333333333333704, + 14.222222222223586, + 37.92592592592996, + 101.13580246914695, + 269.69547325105935, + 719.1879286694924, + 1917.8344764519795, + 5114.225270538599, + 13637.934054769523, + 36367.82414605215, + 96980.86438947004, + 258615.63837192673, + ] + ), + "aDists": np.array( + [ + [100.0, 0.1, 1.0], + [4.863406413211965, 69.86262088575636, 0.029431646782264554], + [0.2365272194007631, 25819.285405029976, 0.0016374749145528168], + ] + ), + "bM0": np.array( + [ + [ + 100.0, + 56.396636657301194, + 31.895287187247146, + 18.294957761981376, + 10.660817516471703, + 6.290591039424345, + 3.7443020392268367, + 2.2413636351719695, + 1.3464994349989194, + 0.8107024270698977, + 0.48877185047542177, + 0.2949239965078066, + 0.17804598720980272, + ], + [ + 1.0e-6, + 4.0200190988219875, + 4.606433069139865, + 3.758158525366545, + 2.6629362775448184, + 1.7591745237414111, + 1.1190966122081454, + 0.696938913153295, + 0.42872454995378795, + 0.2618284523381802, + 0.15921539069060084, + 0.09656819650467219, + 0.058480487065190184, + ], + ] + ), + "bM1": np.array( + [ + [ + 10.0, + 6.354890848885312, + 3.815306159255158, + 2.2491773102504062, + 1.3276630984776567, + 0.7884211778943261, + 0.4708461282568907, + 0.28236149853196507, + 0.16980173598586254, + 0.10229476631106786, + 0.06169480173887945, + 0.03723418494509775, + 0.022481089449094008, + ], + [ + 1.0e-5, + 3.6451213684882426, + 6.184706455055038, + 7.75083495382188, + 8.672348657723711, + 9.211590117984043, + 9.529164800771182, + 9.717649154060018, + 9.83020871507534, + 9.89771554128402, + 9.938315405635903, + 9.962775953501628, + 9.977529002206483, + ], + ] + ), + "bM2": np.array( + [ + [ + 2.0, + 1.3279917682405473, + 0.815368316209652, + 0.484406234346711, + 0.2866707891616959, + 0.1703612676331259, + 0.10175389387661611, + 0.06101894293570185, + 0.03669236327334466, + 0.022103697711640027, + 0.013330436832490668, + 0.00804502307966626, + 0.0048573143944901, + ], + [ + 0.0002, + 4.005880364582517, + 13.40830335654937, + 37.445419449158464, + 100.85962493652575, + 269.5533461366049, + 719.1621396161328, + 1917.977828184842, + 5114.73836146586, + 13639.390826596753, + 36371.78858209591, + 96991.55465118046, + 258644.4048115109, + ], + ] + ), + "bDists1": np.array( + [ + [100.0, 3.7443020392268367, 0.17804598720980272], + [0.1, 0.09035855289429393, 0.08979659938892945], + [1.0, 1.3916783213625432, 1.4061293503242225], + ] + ).T, + "bDists2": np.array( + [ + [1.0e-6, 1.1190998946220907, 0.05848079412663001], + [1.0, 66.94723995610593, 25749.457224681548], + [3.0, 0.12718999335996053, 0.00662584618581055], + ] + ).T, + }, + "Geometric": { + "t": np.array( + [ + 0.0, + 20.0, + 40.0, + 60.0, + 80.0, + 100.0, + 120.0, + 140.0, + 160.0, + 180.0, + 200.0, + 220.0, + 240.0, + ] + ), + "aM0": np.array( + [ + 100.0, + 83.09411530706723, + 69.0668818692553, + 57.42833556073367, + 47.771892319320436, + 39.760132461015466, + 33.11299776856557, + 27.59799298461195, + 23.022057367028655, + 19.22483391072762, + 16.073111976893788, + 13.456251155748634, + 11.282412772916492, + ] + ), + "aM1": np.array( + [ + 10.0, + 9.999999999999998, + 9.999999999999995, + 9.999999999999998, + 10.000000000000002, + 10.000000000000004, + 9.999999999999996, + 9.999999999999996, + 9.999999999999998, + 9.999999999999998, + 9.999999999999998, + 9.999999999999988, + 9.99999999999999, + ] + ), + "aM2": np.array( + [ + 2.0, + 2.8852151953194194, + 4.151238620053756, + 5.950500062169887, + 8.485178624869318, + 12.0129886133155, + 16.84418831764611, + 23.32258967740333, + 31.78481092193408, + 42.500028311068455, + 55.60685101272762, + 71.07585744751505, + 88.7218493043981, + ] + ), + "aDists": np.array( + [ + [100.0, 0.1, 1.0], + [33.12307697837833, 1.386085645680255, 0.21781072474918567], + [11.288994413248544, 8.001687588099609, 0.11070395858333783], + ] + ), + "bM0": np.array( + [ + [ + 100.0, + 83.08735661284119, + 69.025406058178, + 57.28710188741288, + 47.43565678877952, + 39.167760941853565, + 32.289941767938586, + 26.61554482816344, + 21.95171830328991, + 18.122227928945424, + 14.977334881119972, + 12.392963329500928, + 10.267170524593165, + ], + [ + 1.0e-6, + 0.006754965534216085, + 0.0414689469896406, + 0.14122472581368004, + 0.33622210669071656, + 0.5923365303626719, + 0.822966528248585, + 0.9822712716512826, + 1.0700559173008375, + 1.1022183815198043, + 1.0953066097618704, + 1.062767038894064, + 1.014700508663851, + ], + ] + ), + "bM1": np.array( + [ + [ + 10.0, + 9.998176966812272, + 9.983636638206804, + 9.914575608610326, + 9.648425409616147, + 9.022885248658554, + 8.145992984487792, + 7.201239010259968, + 6.289684097629613, + 5.449920021025792, + 4.694685159768059, + 4.0259638571565235, + 3.4404908668086382, + ], + [ + 3.0e-6, + 0.001826033187727366, + 0.016366361793195474, + 0.08542739138967238, + 0.3515775903838529, + 0.9771177513414488, + 1.8540100155122126, + 2.798763989740037, + 3.7103189023703886, + 4.55008297897421, + 5.3053178402319405, + 5.974039142843473, + 6.559512133191355, + ], + ] + ), + "bM2": np.array( + [ + [ + 2.0, + 2.884419791825751, + 4.140031572580394, + 5.841267749589132, + 7.602242548420331, + 8.443150132466357, + 8.291305172948194, + 7.649776302203622, + 6.858287226599269, + 6.056020626291585, + 5.296417497830004, + 4.600656776850216, + 3.976052070235482, + ], + [ + 1.2e-5, + 0.0008129200697375103, + 0.011232418249945829, + 0.10926457152459564, + 0.8829072854441761, + 3.5698485088870786, + 8.556214924636981, + 15.693719076620901, + 25.002698714778422, + 36.65139187807145, + 50.772315702453426, + 67.35212811578029, + 86.19438593789097, + ], + ] + ), + "bDists1": np.array( + [ + [100.0, 0.1, 1.0], + [10.267170524593165, 0.8205678828026997, 0.4083712124798814], + ] + ), + "bDists2": np.array( + [ + [1.0e-6, 1.0, 3.0], + [1.014700508663851, 6.675884373858597, 0.968333274304494], + ] + ), + }, +} + +for kernel, data in MOM_data.items(): + for prefix in ("a", "b"): + data[f"{prefix}Moments"] = np.array([data[f"{prefix}M{i}"] for i in range(3)]) diff --git a/PySDM/source/examples/PySDM_examples/deJong_Azimi/rainshaft.ipynb b/PySDM/source/examples/PySDM_examples/deJong_Azimi/rainshaft.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..de04e4353eae0524161ff366c789a394e014a326 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/deJong_Azimi/rainshaft.ipynb @@ -0,0 +1,3749 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/deJong_Azimi/rainshaft.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/deJong_Azimi/rainshaft.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/deJong_Azimi/rainshaft.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "#### Derived from the precipitation case of Fig. 1 from Shipway & Hill 2012, but with no condensation/evaporation (collisions only). For further information, see Shipway_Hill_2012 example.\n", + "\n", + "**NOTES**: \n", + "- constant momentum profile rather than constant velocity profile is used herein\n", + "- sedimentation and collisions are the only active dynamics\n", + "- pressure at z=0 not given in the paper, assumed (see settings.py)\n", + "- domain extended below z=0 to mimic particle inflow via updraft" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-09T17:44:33.273379844Z", + "start_time": "2024-02-09T17:44:33.272129900Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-09T17:53:10.290776701Z", + "start_time": "2024-02-09T17:53:10.242444113Z" + }, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "from PySDM_examples.Shipway_and_Hill_2012 import plot, Simulation\n", + "from PySDM_examples.deJong_Azimi import Settings1D, cloudy_data\n", + "from open_atmos_jupyter_utils import show_plot\n", + "from PySDM.physics import si, in_unit\n", + "from PySDM.exporters import NetCDFExporter_1d\n", + "from matplotlib import pyplot" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Collisions Only Test Case" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-09T17:48:58.273327225Z", + "start_time": "2024-02-09T17:44:38.655131596Z" + } + }, + "outputs": [], + "source": [ + "common_params = {\n", + " \"n_sd_per_gridbox\": 128,\n", + " \"dt\": 10 * si.s,\n", + " \"dz\": 50 * si.m,\n", + " \"particles_per_volume_STP\": 10 / si.cm**3,\n", + " \"t_max\": 1000 * si.s\n", + "}\n", + "output = {}\n", + "settings = {}\n", + "simulation = {}\n", + "for rho_times_w in (\n", + " 0 * si.kg/si.m**3 * si.m/si.s,\n", + "):\n", + " key = f\"rhow={rho_times_w}\"\n", + " settings[key] = Settings1D(\n", + " **common_params,\n", + " rho_times_w_1=rho_times_w,\n", + " )\n", + " simulation[key] = Simulation(settings[key])\n", + " output[key] = simulation[key].run().products" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-09T17:49:01.274012760Z", + "start_time": "2024-02-09T17:48:58.271837413Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "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0UcfxZ49e2AwGPD999/jueeeQ40aNfDPP/+gUqVKWLNmDTuKhYCdRSIiInJ5Y8aMwZ49e+Dr64s1a9agdevWOHHiBP755x94eHjgwIEDCAoKUjvMEomPziEiIiKXtnr1anz66acAgEWLFqF169YAgLS09Pt2vb292VEsROwsEhERkcu6cuUKBg4cCAAYNWoUnnzySdu0jM5iUVUBK63YuqQarVaLp5991va6JJCzTx09OtJNSp2UM6jlbE45l1IPOYNVzgRWtqVcai9VzkxOebA+vdbNrmSbQa4fCPv9lDOw5exkOaFUzkAt46lM+bYrhZdDyUM55tjEFMWwXBYPsM+69TQpH68hx5AoZT/L761OyiyW9zE2QRmTHLPWTYtHuz9le+0rtYn8PgNAipTRbffeWrMflrPMjdJnR45R3secMsQdff7l/bAr55dDCUG7bGrpsyV/VuzI63fLPptaY1fcz77koCxzprvVwftGuZeSkoLevXvj/v37aN68OWbMmKGYzs5i0WDrkmpMJhOW/vyL2mEQuQSjyYS53yxSOwwil5GUlIS33noL+/fvh4+PD5YvXw6DQfkIKnYWiwZbl4iIiFxGVFQUvvrqK3z99de4desWAGDBggUOH/h99epVAIC7u3uRxljasLNIREREBebm5oYTJ04ASE84yau///4bn376KZYuXYqUlPTbNoKDgzFhwgQ8/fTTdvMLITBv3jwA6Q/mpsLDziKpJj4+nuX+iP6VkBCPprUqAgAOnrsBs4nPiqPiRaPRoF69enlaxmq1Yt26dfj000+xZcsW2/gWLVpg1KhRePbZZ7O8xLxz50789ddfMJlMGDp0aIFip+yxs0ikspySYhxVG8tMTt6QE2as0s348g38Xma94rXZbFJMT0mzL/dnX0JQGYNdhTVN9kk9yVJihFyiTU68kMsJygk2Roty/kQHZfHkBJQ7MUmKYZNRuQ450cHTXZkQIyduJEvtZp/QIidyaBSv78clK6bnKknHPfsauHI7y2UR5XaS9ykpWbm8nGwi76Pcxg5jSs0+4UVOyklNyz4BRv7s2Mek3L6cyOQokUimtUv8yTpJR/5+U7r4+HgsWrQI8+bNw7lz5wCkJzo+++yzGDVqFFq2bJnjOjIeAD5gwAD4+/sXarylHTuLREQuwN3djH3HL9pey9nXRK7OarViypQpAIB3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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "b1e45372676a4758846631e4e7458ab7", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./qc_rhow=0.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "for rho_times_w in (0,):\n", + " plot(var='cloud water mixing ratio', qlabel='cloud water mixing ratio [g/kg]', fname=f'qc_rhow={rho_times_w}.pdf',\n", + " output=output[f'rhow={rho_times_w}.0'],\n", + " line = {0: \":\", 4: \"--\", 8: \"-\", 12: \"-.\"})" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-09T17:49:01.275291648Z", + "start_time": "2024-02-09T17:49:01.209262148Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "9198167.88" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "output[f'rhow={rho_times_w}.0']['nc'].max()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-09T17:49:03.772010670Z", + "start_time": "2024-02-09T17:49:01.227084767Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "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u3dH27Zt8eWXXyoD60cffRRfffUVZs2ahebNm6N79+6YPXt2kfalc+fOeOKJJzBw4EBUrlwZ77//fr7r9uvXD88++yxGjRqFVq1aYdOmTXjttddU69xzzz249dZbceONN6Jy5cqqRzflCg0NxW+//Ybk5GS0b98e9957L2666SZMnTq10HETEQGATsg3NRERERERufDMIhERERHli4NFIiIiIsoXB4tERERElC8OFomIiIgoXxwsEhEREVG+OFgMcNOmTUPt2rURHByMDh06YNu2bf4OKSAkJCSgffv2iIiIQJUqVdC/f3/luXik9e6770Kn02HMmDH+DiVgnD17Fg888ABiYmIQEhKC5s2bY8eOHf4OK2A4HA689tprqFOnDkJCQlCvXj1MmjRJUxWIiMo+DhYD2E8//YSxY8diwoQJ2LVrF1q2bInevXvn+5zC8mTdunUYOXIktmzZgpUrVyI7Oxu33HKLqloF5di+fTtmzJihql1c3l2+fBldunSByWTC8uXLcejQIUyZMgUVKlTwd2gB47333sMXX3yBqVOn4vDhw3jvvffw/vvv47PPPvN3aERUwvicxQDWoUMHtG/fXnmIrtPpRHx8PJ5++mmMGzfOz9EFlgsXLqBKlSpYt24dunXr5u9wAkZaWhratGmDzz//HG+99RZatWqFjz/+2N9h+d24cePw559/YsOGDf4OJWDdfvvtiI2NVVVcuueeexASEoLvv//ej5ERUUnjmcUAZbPZsHPnTvTq1UuZp9fr0atXL6XUGV2VkpICACz5JRk5ciT69u2r+h4R8Ouvv6Jdu3YYMGAAqlSpgtatW+PLL7/0d1gBpXPnzli1apVS93zv3r3YuHEj+vTp4+fIiKikGf0dALl38eJFOBwOTQ3X2NhYHDlyxE9RBSan04kxY8agS5cuuO666/wdTsCYO3cudu3ahe3bt/s7lIDz999/44svvsDYsWPx8ssvY/v27XjmmWdgNpsxbNgwf4cXEMaNGweLxYLGjRvDYDDA4XDg7bffxpAhQ/wdGhGVMA4WqdQbOXIkDhw4gI0bN/o7lIBx5swZjB49GitXrkRwcLC/wwk4TqcT7dq1wzvvvAMAaN26NQ4cOIDp06dzsOgyb948/PDDD5gzZw6aNWuGPXv2YMyYMahWrRr7iKic4WAxQFWqVAkGgwFJSUmq+UlJSYiL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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "a76287d68e98426a8c651df6753c9602", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./nc_rhow=0.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "for rho_times_w in (0,):\n", + " plot(var='nc', qlabel='cloud water number concentration', fname=f'nc_rhow={rho_times_w}.pdf',\n", + " output=output[f'rhow={rho_times_w}.0'],\n", + " line = {0.01: \":\", 4: \"--\", 8: \"-\", 12: \"-.\"})" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-09T17:49:06.740793496Z", + "start_time": "2024-02-09T17:49:03.890309043Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "54d482a135ad498aaed1ee3e53a7a3ee", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./qr_rhow=0.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "p = True\n", + "for rho_times_w in (0,):\n", + " plot(var='rain water mixing ratio', qlabel='rain water mixing ratio [g/kg]', fname=f'qr_rhow={rho_times_w}.pdf',\n", + " output=output[f'rhow={rho_times_w}.0'],\n", + " line = {0: \":\", 4: \"--\", 8: \"-\", 12: \"-.\"})" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-09T17:49:09.884969074Z", + "start_time": "2024-02-09T17:49:06.758853075Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAosAAAHbCAYAAACjjNB9AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/SrBM8AAAACXBIWXMAAA9hAAAPYQGoP6dpAADCD0lEQVR4nOzdd1wT9/8H8Fd2wt5DRURxK7gVFbfirNparXvVLkfVto5ftVptq7Wt7dcOq21drau2VVu3oqi4ByriRsQFsgkECBn3+wM4+VxAQEYCvJ+Ph20+l8vd5y4J987dvT9vEcdxHAghhBBCCCmA2NwdIIQQQgghlouCRUIIIYQQUigKFgkhhBBCSKEoWCSEEEIIIYWiYJEQQgghhBSKgkVCCCGEEFIoChYJIYQQQkihKFgkhBBCCCGFomCREEIIIYQUioJFUmls2LABDg4O5u5GtRESEgKRSISUlBRzd4UITJgwAUOGDDF3Nwgh1QQFi6TSGDFiBO7cuWPubjAogCVCIpEIu3btKpNlPXjwACKRCFeuXGGm/+9//8OGDRvKZB2EEFIUqbk7QEh2djbkcnmR86lUKqhUqgroUcUzGAwQiUQQi6v+77fivt9VWWn3gb29fRn2hhBCXqzqH5mIxenWrRumTZuGmTNnwsXFBUFBQQCAlStXonnz5rC2toaXlxfee+89pKen868TnsVbvHgxWrRogd9//x116tSBvb093njjDaSlpRW4Xo7j4Orqir/++ouf1qJFC3h6evLt0NBQKBQKZGRkFNmnkJAQTJw4EampqRCJRBCJRFi8eDEAQKvV4sMPP0TNmjVhbW2N9u3bIyQkxGRb/v33XzRp0gQKhQIPHz406XPepeDg4GC0adMGVlZW6NixI27fvs3PU9AlyZkzZ6Jbt27MPp8+fTpmzpwJR0dHuLu745dffoFGo8HEiRNha2sLX19f7N+/36QPp06dgp+fH5RKJTp06IDr168zz4eGhiIwMBAqlQpeXl6YMWMGNBoN/3ydOnWwdOlSjBs3DnZ2dnjrrbcKfH+MRiNWrFgBX19fKBQK1K5dG59//jn/fHh4OHr06AGVSgVnZ2e89dZbzOcjbz98/fXX8PT0hLOzM6ZOnQqdTsfPo9VqMXfuXHh5eUGhUMDX1xe//fYb//z169fRr18/2NjYwN3dHWPHjkVCQgKzH2fMmIE5c+bAyckJHh4e/Huet60AMHToUIhEIr6d91n99ddf4ePjA6VSCQA4cOAAOnfuDAcHBzg7O2PgwIGIjIzkl+fj4wMAaNmyJUQiEf+eCt9zrVaLGTNmwM3NDUqlEp07d8aFCxf454vzOSKEkMJQsEjMYuPGjZDL5Th16hR+/vlnAIBYLMaqVasQERGBjRs34ujRo5gzZ84LlxMZGYldu3Zhz5492LNnD44fP47ly5cXOK9IJEKXLl34oC05ORk3b95EZmYmbt26BQA4fvw42rZtCysrqyL71LFjR3z33Xews7NDTEwMYmJi8OGHHwIApk2bhjNnzmDbtm24du0aXn/9dfTt2xd3797l+5ORkYEvv/wSv/76KyIiIuDm5lbodn788cf45ptvcPHiRUilUkyaNKkYe5m1ceNGuLi44Pz585g+fTreffddvP766+jYsSMuX76MPn36YOzYsXygnOejjz7CN998gwsXLsDV1RWDBg3iA7DIyEj07dsXr732Gq5du4bt27cjNDQU06ZNY5bx9ddfw9/fH2FhYVi4cGGB/Zs/fz6WL1+OhQsX4saNG9iyZQvc3d0BABqNBkFBQXB0dMSFCxewY8cOHDlyxGQ9x44dQ2RkJI4dO4aNGzdiw4YNzOXacePGYevWrVi1ahVu3ryJNWvWwMbGBgCQkpKCHj16oGXLlrh48SIOHDiAZ8+eYfjw4Sb70draGufOncOKFSuwZMkSHD58GAD4AG39+vWIiYlhArZ79+7h77//xj///MNfVtZoNJg9ezYuXryI4OBgiMViDB06FEajEQBw/vx5AMCRI0cQExODf/75p8B9N2fOHPz999/YuHEjLl++DF9fXwQFBSEpKYmZryw+R4SQaogjpIJ17dqVa9myZZHz7dixg3N2dubb69ev5+zt7fn2okWLOCsrK06tVvPTPvroI659+/aFLnPVqlVc06ZNOY7juF27dnHt27fnBg8ezK1evZrjOI7r1asX93//938v3SeO47jo6GhOIpFwT548Yab37NmTmz9/Pv86ANyVK1cKXRfHcdyxY8c4ANyRI0f4aXv37uUAcJmZmRzHcdz48eO5wYMHM697//33ua5du/Ltrl27cp07d+bber2es7a25saOHctPi4mJ4QBwZ86cYda9bds2fp7ExEROpVJx27dv5ziO4yZPnsy99dZbzLpPnjzJ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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "400ba78c734b40e1aecc6e252044d505", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./nr_rhow=0.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "for rho_times_w in (0,):\n", + " plot(var='nr', qlabel='rain water number concentration', fname=f'nr_rhow={rho_times_w}.pdf',\n", + " output=output[f'rhow={rho_times_w}.0'],\n", + " line = {0.01: \":\", 4: \"--\", 8: \"-\", 12: \"-.\"})" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-09T17:49:12.168923287Z", + "start_time": "2024-02-09T17:49:09.874616972Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "6ce9b316569e49928d26aaa8a98c1f3b", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./coal_rhow=0.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "p = True\n", + "for rho_times_w in (0,):\n", + " plot(var='coalescence_rate', qlabel='coalescence rate (# / sec)', fname=f'coal_rhow={rho_times_w}.pdf',\n", + " output=output[f'rhow={rho_times_w}.0'],\n", + " line = {0.01: \":\", 4: \"--\", 8: \"-\", 12: \"-.\"})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Comparisons" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-09T17:49:12.329905069Z", + "start_time": "2024-02-09T17:49:12.175428452Z" + } + }, + "outputs": [], + "source": [ + "p = True\n", + "for rho_times_w in (0,):\n", + " key = f\"rhow={rho_times_w}.0\"\n", + " filename = 'products_' + key + 'collisions.nc'\n", + " nc_exporter = NetCDFExporter_1d(output[key], settings[key], simulation[key], filename)\n", + " nc_exporter.run()" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "ExecuteTime": { + "end_time": "2024-02-09T17:53:16.554161537Z", + "start_time": "2024-02-09T17:53:13.855100484Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-02-09T18:53:16.363458\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.8.1, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "89802ed20f584818b0e632ab0eec768c", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./rainshaft.pdf
\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt_times = [0, 250, 500, 1000]\n", + "dt = common_params[\"dt\"]\n", + "dz = common_params[\"dz\"] / 1000\n", + "\n", + "(fig, ax) = pyplot.subplots(nrows=2, ncols=2, sharey=True, figsize=(9,7), tight_layout=True)\n", + "for (j,t) in enumerate(plt_times):\n", + " it = int(t / dt)\n", + " profiles = output[f'rhow={rho_times_w}.0']\n", + " nc = profiles['nc'][:,it]\n", + " nr = profiles['nr'][:,it]\n", + " qc_g_kg = profiles['cloud water mixing ratio'][:,it]\n", + " qr_g_kg = profiles['rain water mixing ratio'][:,it]\n", + "\n", + " if j == 0:\n", + " lbl = 't=0, PySDM'\n", + " lbl2 = 't=0, Cloudy.jl'\n", + " else:\n", + " lbl = 't='+str(t)+'s'\n", + " lbl2 = '_'\n", + " line = ax[0][0].plot(\n", + " in_unit(nc, si.cm**-3),\n", + " in_unit(profiles['z'], si.km),\n", + " label=lbl\n", + " )\n", + " ax[0][0].plot(\n", + " cloudy_data.NC[j],\n", + " cloudy_data.Z,\n", + " '--',\n", + " color=line[-1].get_color(),\n", + " label=lbl2\n", + " )\n", + " line = ax[0][1].plot(\n", + " in_unit(nr, si.cm**-3),\n", + " in_unit(profiles['z'], si.km)\n", + " )\n", + " ax[0][1].plot(\n", + " cloudy_data.NR[j],\n", + " cloudy_data.Z,\n", + " '--',\n", + " color=line[-1].get_color()\n", + " )\n", + " line = ax[1][0].plot(\n", + " qc_g_kg,\n", + " in_unit(profiles['z'], si.km)\n", + " )\n", + " ax[1][0].plot(\n", + " cloudy_data.QC[j],\n", + " cloudy_data.Z,\n", + " '--',\n", + " color=line[-1].get_color()\n", + " )\n", + " line = ax[1][1].plot(\n", + " qr_g_kg,\n", + " in_unit(profiles['z'], si.km)\n", + " )\n", + " ax[1][1].plot(\n", + " cloudy_data.QR[j],\n", + " cloudy_data.Z,\n", + " '--',\n", + " color=line[-1].get_color()\n", + " )\n", + "\n", + "ax[0][0].set_xlabel('N_c (cm$^{-3}$)')\n", + "ax[0][0].set_ylabel('z (km)')\n", + "ax[0][0].legend()\n", + "ax[0][1].set_xlabel('N_r (cm$^{-3}$)')\n", + "ax[1][0].set_xlabel('q_c (g kg$^{-1}$)')\n", + "ax[1][0].set_ylabel('z (km)')\n", + "ax[1][1].set_xlabel('q_r (g kg$^{-1}$)')\n", + "\n", + "show_plot('rainshaft.pdf')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.9.4 ('edjPySDM')", + "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.9.13" + }, + "vscode": { + "interpreter": { + "hash": "b43cf254c70d60c2e21a7f71ba113e70c1694742e72407132919c841d907074b" + } + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/PySDM/source/examples/PySDM_examples/deJong_Azimi/settings1D.py b/PySDM/source/examples/PySDM_examples/deJong_Azimi/settings1D.py new file mode 100644 index 0000000000000000000000000000000000000000..ee48c055da20911b6c4c434c4fe89263046914a0 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/deJong_Azimi/settings1D.py @@ -0,0 +1,72 @@ +from typing import Iterable + +from PySDM_examples.Shipway_and_Hill_2012 import Settings as SettingsSH + +from PySDM import Formulae +from PySDM.initialisation import spectra +from PySDM.physics import si +from PySDM.dynamics.collisions.collision_kernels import Golovin + + +class Settings1D(SettingsSH): + def __dir__(self) -> Iterable[str]: + return ( + "n_sd_per_gridbox", + "p0", + "kappa", + "rho_times_w_1", + "particles_per_volume_STP", + "dt", + "dz", + "precip", + "z_max", + "t_max", + "cloud_water_radius_range", + "rain_water_radius_range", + "r_bins_edges_dry", + "r_bins_edges", + ) + + def __init__( + self, + *, + n_sd_per_gridbox: int, + p0: float = 1007 * si.hPa, # as used in Olesik et al. 2022 (GMD) + kappa: float = 1, + rho_times_w_1: float = 2 * si.m / si.s * si.kg / si.m**3, + particles_per_volume_STP: int = 100 / si.cm**3, + dt: float = 1 * si.s, + dz: float = 25 * si.m, + z_max: float = 3000 * si.m, + t_max: float = 60 * si.minutes, + precip: bool = True, + formulae: Formulae = None, + save_spec_and_attr_times=(), + z_part=(0.5, 0.75), + ): + super().__init__( + n_sd_per_gridbox=n_sd_per_gridbox, + p0=p0, + kappa=kappa, + rho_times_w_1=rho_times_w_1, + particles_per_volume_STP=particles_per_volume_STP, + dt=dt, + dz=dz, + z_max=z_max, + t_max=t_max, + precip=precip, + formulae=formulae or Formulae(terminal_velocity="PowerSeries"), + save_spec_and_attr_times=save_spec_and_attr_times, + enable_condensation=False, + collision_kernel=Golovin(b=5e3), + ) + self.z_part = z_part + z_frac = z_part[1] - z_part[0] + norm_factor = ( + particles_per_volume_STP / self.formulae.constants.rho_STP * z_frac + ) + self.wet_radius_spectrum_per_mass_of_dry_air = spectra.Gamma( + norm_factor=norm_factor, + k=1.0, + theta=1e5 * si.um**3, + ) diff --git a/PySDM/source/examples/PySDM_examples/deJong_Azimi/simulation_0D.py b/PySDM/source/examples/PySDM_examples/deJong_Azimi/simulation_0D.py new file mode 100644 index 0000000000000000000000000000000000000000..d166ba48c8d583e45a698aa669b7e11365096541 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/deJong_Azimi/simulation_0D.py @@ -0,0 +1,57 @@ +from collections import namedtuple + +import numpy as np + +from PySDM.backends import CPU +from PySDM.builder import Builder +from PySDM.dynamics import Coalescence +from PySDM.environments import Box +from PySDM.initialisation.sampling.spectral_sampling import ConstantMultiplicity +from PySDM.products.size_spectral import ( + ParticleVolumeVersusRadiusLogarithmSpectrum, + VolumeFirstMoment, + VolumeSecondMoment, + ZerothMoment, +) + + +def run_box(settings, backend_class=CPU): + builder = Builder( + n_sd=settings.n_sd, + backend=backend_class(settings.formulae), + environment=Box(dv=settings.dv, dt=settings.dt), + ) + builder.particulator.environment["rhod"] = settings.rhod + attributes = {} + attributes["volume"], attributes["multiplicity"] = ConstantMultiplicity( + settings.spectrum + ).sample_deterministic(settings.n_sd) + builder.add_dynamic( + Coalescence( + collision_kernel=settings.kernel, + coalescence_efficiency=settings.coal_eff, + adaptive=settings.adaptive, + ) + ) + products = ( + ParticleVolumeVersusRadiusLogarithmSpectrum( + radius_bins_edges=settings.radius_bins_edges, name="dv/dlnr" + ), + ZerothMoment(name="M0"), + VolumeFirstMoment(name="M1"), + VolumeSecondMoment(name="M2"), + ) + particulator = builder.build(attributes, products) + + y = np.ndarray((len(settings.steps), len(settings.radius_bins_edges) - 1)) + mom = np.ndarray((len(settings.steps), 3)) + for i, step in enumerate(settings.steps): + particulator.run(step - particulator.n_steps) + y[i] = particulator.products["dv/dlnr"].get()[0] + (mom[i, 0],) = particulator.products["M0"].get() + (mom[i, 1],) = particulator.products["M1"].get() + (mom[i, 2],) = particulator.products["M2"].get() + + return namedtuple("_", ("radius_bins_left_edges", "dv_dlnr", "moments"))( + radius_bins_left_edges=settings.radius_bins_edges[:-1], dv_dlnr=y, moments=mom + ) diff --git a/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/__init__.py b/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..8b97329dfc5e565aab1627fd890124a9be234bb1 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/__init__.py @@ -0,0 +1,28 @@ +""" +box- and single-column breakup-focused examples from +[de Jong et al. 2023](https://doi.org/10.5194/gmd-16-4193-2023) + +fig_9.ipynb: +.. include:: ./fig_9.ipynb.badges.md + +figs_3_4_5.ipynb: +.. include:: ./figs_3_4_5.ipynb.badges.md + +figs_6_7_8.ipynb: +.. include:: ./figs_6_7_8.ipynb.badges.md + +figs_10_11_12_13.ipynb: +.. include:: ./figs_10_11_12_13.ipynb.badges.md +""" + +# pylint: disable=invalid-name +from .plot_rates import plot_ax, plot_zeros_ax +from .settings1D import Settings1D +from .settings_0D import Settings0D +from .simulation1D import Simulation1D +from .simulation_0D import run_box_breakup, run_box_NObreakup +from .simulation_ss import ( + get_straub_fig10_data, + get_straub_fig10_init, + run_to_steady_state, +) diff --git a/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/data/straub_fig10.csv b/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/data/straub_fig10.csv new file mode 100644 index 0000000000000000000000000000000000000000..c696eeaa6f15cf2fb8a5094e7858b22abe417b08 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/data/straub_fig10.csv @@ -0,0 +1,32 @@ +0.08988764044943831, 1.0558032512857272 +0.08614232209737838, 1.0031993336166019 +0.09737827715355796, 1.1435729397159466 +0.10861423220973776, 1.3161403694268319 +0.11985018726591756, 1.5099178364052492 +0.14232209737827717, 1.8114473285278132 +0.16479400749063677, 2.159352957019568 +0.19475655430711625, 2.6071769081743943 +0.2247191011235955, 3.0489128877749923 +0.26217228464419484, 3.453402357814985 +0.3146067415730337, 3.8495784217411746 +0.36329588014981284, 3.9555298736537083 +0.41947565543071164, 3.8495784217411746 +0.47940074906367036, 3.6344855057213517 +0.558052434456929, 3.328848103897989 +0.6816479400749064, 2.8970050750896847 +0.8164794007490636, 2.525213781723023 +0.9438202247191009, 2.2944632221300316 +1.0711610486891385, 2.1251395837547085 +1.2134831460674156, 2.0452228712025367 +1.3707865168539324, 2.025717759949701 +1.6179775280898876, 2.032198706876855 +1.8651685393258428, 2 +2.0749063670411982, 1.930947253697798 +2.3220973782771535, 1.8114473285278132 +2.5468164794007486, 1.685826681060261 +2.8014981273408237, 1.5317781593679631 +3.0187265917602994, 1.389585281189247 +3.220973782771536, 1.2626068908615116 +3.3782771535580527, 1.169430765597687 +3.543071161048689, 1.0693796985710673 +3.651685393258427, 1.0064089029687935 \ No newline at end of file diff --git a/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/fig_9.ipynb b/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/fig_9.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..d1b6302d4a9378fa1d097239db1459d8d85f6fdd --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/fig_9.ipynb @@ -0,0 +1,11352 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/deJong_Mackay_et_al_2023/fig_9.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/deJong_Mackay_et_al_2023/fig_9.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/deJong_Mackay_et_al_2023/fig_9.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### based on Fig. 10 from [Straub et al. 2010 (J. Atmos. Sci. 67) \"_Numerical Investigation of Collision-Induced Breakup of Raindrops, Part II: Parameterizations of Coalescence Efficiencies and Fragment Size Distributions_\"](https://doi.org/10.1175/2009JAS3175.1)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-10T19:30:43.577309Z", + "start_time": "2024-12-10T19:30:43.567919Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-10T19:30:43.620763Z", + "start_time": "2024-12-10T19:30:43.611621Z" + } + }, + "outputs": [], + "source": [ + "from matplotlib import pyplot\n", + "import matplotlib\n", + "from PySDM_examples.deJong_Mackay_et_al_2023 import run_to_steady_state, get_straub_fig10_data, get_straub_fig10_init\n", + "from open_atmos_jupyter_utils import show_plot\n", + "import numpy as np\n", + "from PySDM.physics.constants import si\n", + "import pickle as pkl\n", + "import time\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-10T19:30:43.649864Z", + "start_time": "2024-12-10T19:30:43.645616Z" + } + }, + "outputs": [], + "source": [ + "(straub_x, straub_log_y, straub_dvdlnr_ss) = get_straub_fig10_data()\n", + "(straub_x_init, straub_y_init, straub_dvdlnr_init) = get_straub_fig10_init()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-15T05:24:09.461421Z", + "start_time": "2024-12-10T19:30:43.857408Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success with run #1\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success with run #2\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success with run #3\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success with run #4\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success with run #5\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success with run #6\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success with run #7\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success with run #8\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success with run #9\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success with run #10\n", + "ran Straub2010 for 64 superdroplets in 61.549843072891235 sec\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success with run #1\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success with run #2\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success with run #3\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success with run #4\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success with run #5\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success with run #6\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Error in steady state sim for 1024 superdroplets, moving on with dt=0.5\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success with run #7\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success with run #8\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success with run #9\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success with run #10\n", + "ran Straub2010 for 1024 superdroplets in 108.6907148361206 sec\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Error in steady state sim for 16384 superdroplets, moving on with dt=0.5\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success with run #1\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success with run #2\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success with run #3\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success with run #4\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Error in steady state sim for 16384 superdroplets, proceeding to next iteration\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Error in steady state sim for 16384 superdroplets, moving on with dt=0.5\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success with run #6\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Error in steady state sim for 16384 superdroplets, proceeding to next iteration\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success with run #8\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success with run #9\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success with run #10\n", + "ran Straub2010 for 16384 superdroplets in 789.2925410270691 sec\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-12-10T20:46:44.287718\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.9.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-12-10T20:46:44.528590\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.9.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "e21822bbf18947448b90838ce7e2421b", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig9_straub_fig10.pdf
\")…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "210e91e496d84c6891ea5e42aabf6432", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./straub_dvdlnr.pdf
\"), HTML(v…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success with run #6\n", + "Error in steady state sim for 1024 superdroplets, moving on with dt=0.5\n", + "Success with run #7\n", + "Success with run #8\n", + "Success with run #9\n", + "Success with run #10\n", + "ran Straub2010 for 1024 superdroplets in 13070.641508340836 sec\n", + "Error in steady state sim for 16384 superdroplets, moving on with dt=0.5\n", + "Success with run #1\n", + "Success with run #2\n", + "Success with run #3\n", + "Success with run #4\n", + "Success with run #5\n", + "Success with run #6\n", + "Error in steady state sim for 16384 superdroplets, proceeding to next iteration\n", + "Success with run #8\n", + "Success with run #9\n", + "Success with run #10\n", + "ran Straub2010 for 16384 superdroplets in 13232.116261959076 sec\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-12-15T06:24:09.189986\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.0, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-12-15T06:24:09.308767\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.0, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "98c1b522502743b4ab2f21e982a42826", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig9_straub_fig10.pdf
\")…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "8a6d099f0b1f46149124e002bd59550e", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./straub_dvdlnr.pdf
\"), HTML(v…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "CI = 'CI' in os.environ\n", + "run_sims = True\n", + "parameterization = 'Straub2010'\n", + "dt = 1 * si.s\n", + "n_sds = (2**6, 2**10, 2**14) if not CI else (32,)\n", + "steps = (0, 7200) \n", + "nruns = 10 if not CI else 1\n", + "\n", + "colors = {32: 'red', 2**6: 'blue', 2**10: 'orange', 2**14: 'green'}\n", + "\n", + "kwargs = {'nrows': 1, 'ncols': 2, 'figsize': (10, 4)}\n", + "fig, ax = pyplot.subplots(**kwargs)\n", + "fig2, ax2 = pyplot.subplots(**kwargs) \n", + "\n", + "# Obtain data\n", + "for n_sd in n_sds:\n", + " lbl = f\"{n_sd} SDs\"\n", + " if run_sims:\n", + " t1 = time.time()\n", + " run_to_steady_state(parameterization, n_sd, steps, nruns, dt=dt)\n", + " t2 = time.time()\n", + " print(f'ran {parameterization} for {n_sd} superdroplets in {t2 - t1} sec')\n", + " data_filename = f'steadystate_{parameterization}_{n_sd}sd.pkl'\n", + " with open(data_filename, 'rb') as handle:\n", + " (x, y_ensemble, y2_ensemble, rates) = pkl.load(handle)\n", + " \n", + " # statistics\n", + " # mass density\n", + " y_mean = np.nanmean(y_ensemble, axis=0)\n", + " y_std = np.nanstd(y_ensemble, axis=0)\n", + "\n", + " # number density\n", + " y2_mean = np.nanmean(y2_ensemble, axis=0)\n", + " y2_std = np.nanstd(y2_ensemble, axis=0)\n", + "\n", + " # Plotting\n", + " cmap = matplotlib.colormaps['viridis']\n", + " dr = np.diff(x) * si.um\n", + " dr = np.concatenate([dr, [dr[-1]]])\n", + "\n", + " for (j, step) in enumerate(steps):\n", + " idx = 1 if step != 0 else 0\n", + " label = lbl if lbl not in pyplot.gca().get_legend_handles_labels()[1] else ''\n", + " ax[idx].step(\n", + " 2*x*si.mm,\n", + " y2_mean[j]/2/dr * si.mm,\n", + " linestyle='-',\n", + " color=colors[n_sd],\n", + " label=label\n", + " )\n", + " if step != 0:\n", + " ax[idx].fill_between(\n", + " 2*x*si.mm,\n", + " y2_mean[j]/2/dr * si.mm - y2_std[j]/2/dr * si.mm, \n", + " y2_mean[j]/2/dr * si.mm + y2_std[j]/2/dr * si.mm,\n", + " color=colors[n_sd],\n", + " alpha=0.2\n", + " ) \n", + " ax2[idx].step(\n", + " 2*x*si.mm,\n", + " y_mean[j],\n", + " linestyle='-',\n", + " color=colors[n_sd],\n", + " label=label\n", + " )\n", + " if step != 0:\n", + " ax2[idx].fill_between(\n", + " 2*x*si.mm,\n", + " y_mean[j] - y_std[j], \n", + " y_mean[j] + y_std[j],\n", + " color=colors[n_sd],\n", + " alpha=0.2\n", + " )\n", + " \n", + "# Reference data\n", + "kwargs = {\"label\": \"Reference\", \"linestyle\": \"--\", \"color\": \"k\"}\n", + "ax[0].plot(straub_x_init/si.mm, straub_y_init, **kwargs)\n", + "ax[1].plot(straub_x/si.mm, np.power(10, straub_log_y), **kwargs)\n", + "ax2[0].plot(straub_x_init/si.mm, straub_dvdlnr_init, **kwargs)\n", + "ax2[1].plot(straub_x/si.mm, straub_dvdlnr_ss / si.mm, **kwargs)\n", + "\n", + "for _ax in (ax[0], ax[1]):\n", + " _ax.set_yscale(\"log\")\n", + " _ax.set_xlim([0.0, 7.0])\n", + " _ax.set_ylim([4.0, 2e4])\n", + "for _ax in (ax2[0], ax2[1]):\n", + " _ax.set_xscale(\"log\")\n", + " _ax.set_xlim([0.2, 8.0])\n", + " _ax.set_ylim([0.0, 2e-5])\n", + "for _ax in (ax[0], ax[1], ax2[0], ax2[1]):\n", + " _ax.set_xlabel(\"particle diameter (mm)\")\n", + "\n", + "ax[0].set_ylabel(\"N(v) (m$^{-3}$ mm$^{-1})$\")\n", + "ax2[0].set_ylabel(\"dv/dlnr\")\n", + "\n", + "ax[0].legend()\n", + "ax[0].set_title(\"(a) Initial Condition\")\n", + "ax[1].set_title(\"(b) Steady State\")\n", + "show_plot('fig9_straub_fig10.pdf', fig=fig)\n", + "show_plot('straub_dvdlnr.pdf', fig=fig2)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + }, + "vscode": { + "interpreter": { + "hash": "b43cf254c70d60c2e21a7f71ba113e70c1694742e72407132919c841d907074b" + } + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/figs_10_11_12_13.ipynb b/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/figs_10_11_12_13.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..82c02f873207e47bb001d1540917745d7de9310c --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/figs_10_11_12_13.ipynb @@ -0,0 +1,474 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/deJong_Mackay_et_al_2023/figs_10_11_12_13.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/deJong_Mackay_et_al_2023/figs_10_11_12_13.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/deJong_Mackay_et_al_2023/figs_10_11_12_13.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### based on Fig. 1 from Shipway & Hill 2012 (Q. J. Royal Meteo. Soc. 138) \"_Diagnosis of systematic differences between multiple parametrizations of warm rain microphysics using a kinematic framework_\" \n", + "https://doi.org/10.1002/qj.1913\n", + "\n", + "Including BREAKUP process to demonstrate physical changes to cloud." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-13T12:32:43.231765Z", + "start_time": "2024-12-13T12:32:43.210493Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples==3.0.0rc5', 'PySDM==3.0.0rc5')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-13T12:32:47.921882Z", + "start_time": "2024-12-13T12:32:43.481397Z" + } + }, + "outputs": [], + "source": [ + "from PySDM_examples.deJong_Mackay_et_al_2023 import Settings1D, Simulation1D, plot_ax, plot_zeros_ax\n", + "from PySDM.physics import si\n", + "import matplotlib.pyplot as plt\n", + "from open_atmos_jupyter_utils import show_plot" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-13T12:32:48.111192Z", + "start_time": "2024-12-13T12:32:48.096455Z" + } + }, + "outputs": [], + "source": [ + "def gen_key(*, breakup, stochastic_breakup):\n", + " return f\"b={breakup}_s={stochastic_breakup}\"" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-13T12:36:40.525471Z", + "start_time": "2024-12-13T12:32:48.154622Z" + } + }, + "outputs": [], + "source": [ + "save = False\n", + "restore_saved_data = False\n", + "\n", + "common_params = {\n", + " \"n_sd_per_gridbox\": 256 if 'CI' not in os.environ else 32,\n", + " \"dt\": 5 * si.s,\n", + " \"dz\": 100 * si.m,\n", + " \"p0\": 990 * si.hPa,\n", + " \"kappa\": .9,\n", + " \"particles_per_volume_STP\": 50 / si.cm**3,\n", + " \"precip\": True,\n", + " \"rho_times_w_1\": 6 * si.kg/si.m**3 * si.m/si.s,\n", + " \"output_every_n_steps\": 6,\n", + " \"z_max\": 5000 * si.m,\n", + " \"t_max\": 3600 * si.s,\n", + " \"save_spec_at\": [0.0, 300.0, 600.0, 900.0, 1200.0, 1800.0, 2700.0],\n", + "}\n", + "\n", + "if restore_saved_data:\n", + " settings = {}\n", + " for breakup_flag in (False, True):\n", + " for stochastic_breakup_flag in (True, False) if breakup_flag else (False,):\n", + " key = gen_key(breakup=breakup_flag, stochastic_breakup=stochastic_breakup_flag)\n", + " settings[key] = Settings1D(\n", + " **common_params,\n", + " breakup=breakup_flag,\n", + " stochastic_breakup=stochastic_breakup_flag,\n", + " old_buggy_density_formula=True, # just to match what was in the paper!\n", + " )\n", + " import pickle as pkl\n", + " with open('data1d.pkl','rb') as file:\n", + " output = pkl.load(file)\n", + "else:\n", + " output = {}\n", + " settings = {}\n", + " simulation = {}\n", + " for breakup_flag in (False, True):\n", + " for stochastic_breakup_flag in (True, False) if breakup_flag else (False,):\n", + " key = gen_key(breakup=breakup_flag, stochastic_breakup=stochastic_breakup_flag)\n", + " settings[key] = Settings1D(\n", + " **common_params,\n", + " breakup=breakup_flag,\n", + " stochastic_breakup=stochastic_breakup_flag,\n", + " old_buggy_density_formula=True, # just to match what was in the paper!\n", + " )\n", + " simulation[key] = Simulation1D(settings[key])\n", + " output[key] = simulation[key].run().products\n", + "\n", + "if save:\n", + " import pickle as pkl\n", + " with open('data1d.pkl','wb') as file:\n", + " pkl.dump(output, file)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Plotting" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-13T12:36:40.560159Z", + "start_time": "2024-12-13T12:36:40.545458Z" + } + }, + "outputs": [], + "source": [ + "contour_lev = [.4]\n", + "rate_unit = \"s$^{-1}$ kg$^{-1}$\"\n", + "inline_format='png'\n", + "contour_args = {\n", + " 'contour_var1': 'cloud water mixing ratio', 'contour_lvl1': contour_lev,\n", + " 'contour_var2': 'rain water mixing ratio', 'contour_lvl2': contour_lev\n", + "}\n", + "kwargs = {\n", + " 'cloud water mixing ratio': {'cmin': 0.0, 'cmax': 4.0, 'var': 'cloud water mixing ratio', 'qlabel': '$q_c$ [g/kg]' },\n", + " 'rain water mixing ratio': {'cmin': 0.0, 'cmax': 4.0, 'var': 'rain water mixing ratio', 'qlabel': '$q_r$ [g/kg]' },\n", + " 'na': {'cmin': 0.0, 'cmax': 5e7, 'var': 'na', 'qlabel': '$n_a$ [m$^{-3}$]' },\n", + " 'coll_rate': {'cmin': 100, 'cmax': 1e6, 'var': 'collision_rate', 'qlabel': f'collision rate [{rate_unit}]', 'clog': True}, \n", + " 'coal_rate': {'cmin': 100, 'cmax': 1e6, 'var': 'coalescence_rate', 'qlabel': f'coalescence rate [{rate_unit}]', 'clog': True},\n", + " 'brkp_rate': {'cmin': 1, 'cmax': 1e4, 'var': 'breakup_rate', 'qlabel': f'breakup rate [{rate_unit}]', 'clog': True},\n", + " 'brkp_zero': {'cmin': 1, 'cmax': 1e4, 'var': 'coalescence_rate', 'qlabel': f'breakup rate [{rate_unit}]', 'clog': True},\n", + " 'rpng_rate': {'cmin': 1e0, 'cmax': 1e6, 'var': 'ripening', 'qlabel': f'ripening rate [{rate_unit}]' , 'clog': True},\n", + " 'actv_rate': {'cmin': 1e0, 'cmax': 1e6, 'var': 'activating', 'qlabel': f'activating rate [{rate_unit}]' , 'clog': True},\n", + " 'dctv_rate': {'cmin': 1e0, 'cmax': 1e6, 'var': 'deactivating', 'qlabel': f'deactivating rate [{rate_unit}]', 'clog': True},\n", + " 'N(v)': {'cmin': 0.0}\n", + "}\n", + "\n", + "def add_titles(ax):\n", + " ax[0][0].text(1500, 7.0, 'No Breakup', weight='bold',)\n", + " ax[0][1].text(1000, 7.0, 'Property-Independent Breakup', weight='bold')\n", + " ax[0][2].text(1000, 7.0, 'Property-Dependent Breakup', weight='bold')\n", + "\n", + "def add_ylabels_qn(ax):\n", + " ax[0][0].text(-750, 0.5, 'Cloud Liquid Water (g/kg)', rotation='vertical', weight='bold')\n", + " ax[1][0].text(-750, 0.5, 'Rain Liquid Water (g/kg)', rotation='vertical', weight='bold')\n", + " ax[2][0].text(-750, 0.5, 'Aerosol Concentration (m$^{-3}$)', rotation='vertical',weight='bold')\n", + "\n", + "def add_ylabels_rates(ax, rates):\n", + " ax[0][0].text(-750, 0.5, f'{rates[0]} Rate [{rate_unit}]', rotation='vertical', weight='bold')\n", + " ax[1][0].text(-750, 0.5, f'{rates[1]} Rate [{rate_unit}]', rotation='vertical', weight='bold')\n", + " ax[2][0].text(-750, 0.5, f'{rates[2]} Rate [{rate_unit}]', rotation='vertical', weight='bold')\n", + "\n", + "def plot_spectra(ax, key_arg, t_sel):\n", + " spct_step = common_params['save_spec_at'].index(t_sel)\n", + " ax.step(settings[key_arg].r_bins_edges[0:-1] / si.um, output[key_arg]['dvdlnr'].mean(axis=0)[:,spct_step] / si.cm**3,label=str(spct_step))\n", + " ax.set_xscale('log')\n", + " ax.set_xlim([1e0, 1e4])\n", + " ax.set_xlabel(r\"particle radius ($\\mu$m)\")\n", + " ax.set_ylabel('dm / dlnr (g / m$^3$)')\n", + " ax.set_title('t = ' + str(t_sel) + 's')\n", + "\n", + "def add_spectra_legend(ax):\n", + " ax.legend(['No Breakup', 'Property-Independent', 'Property-Dependent'])\n", + " \n", + "def fig_ax():\n", + " return plt.subplots(nrows=3, ncols=3, figsize=(16,12))\n", + "\n", + "def fig_ax_spectra():\n", + " return plt.subplots(nrows=2, ncols=2, sharex=True, figsize=(8,6))" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-13T12:36:47.259576Z", + "start_time": "2024-12-13T12:36:40.585360Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABkYAAASoCAYAAABBmnYmAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdeXxU1f3/8fdk3xP2RVZRg4jghpEKgkhFtG6ttbVaQf3ary1aLba1tlZF22pXtVb9aqvQxa31p2jdWrWCK4goVVBAERWVfckCISSZ8/sjZnLPmeTOXDKTSTKv5+MRzZ1777nn3pnkfHIPM++QMcYIAAAAAAAAAAAgDWSkugMAAAAAAAAAAAAdhYkRAAAAAAAAAACQNpgYAQAAAAAAAAAAaYOJEQAAAAAAAAAAkDaYGAEAAAAAAAAAAGmDiREAAAAAAAAAAJA2mBgBAAAAAAAAAABpg4kRAAAAAAAAAACQNpgYAQAAAAAAAAAAaYOJEQDoRBYsWKBQKKRQKKQPP/ww1d0BACBtzZs3r8uPyZMnT1YoFNLkyZNT3ZVOaebMmVwfAECX8eGHH0Zqk3nz5qW6O50S1wdBMDECoFNq/kM+FArp5z//eeTxlStXJnSga/6DuPkrKytLffv21UknnaQ333yz3e0DANBe3jExFAopMzNT++yzj04++WS98sorqe7eXknGDWnvRMaCBQsS1i7a5r3m8fC+jkOhkPLy8rTffvvpsssu0+7du5PcWwDo2qgH4uMdmzIyMpSfn6999tlHX/ziFzVv3jyFw+GEHas7an6dzZw5M+a23mvdfL1LS0s1fvx4/f3vf09+Z4F2YmIEQKf361//Wtu2bUv6cSoqKjRmzBht3bpVTz75pKZNm6ba2lrfffbs2ZP0fgEAIEk5OTmRsWrTpk16/PHHNWnSJL322mtt7tPZxqnO1h+kRu/evVVRUaH+/ftrzZo1uuWWW/T973/fd5/GxkY1NjZ2UA8BoPOiHojfyJEjte+++2rr1q169tlndd5552n69Omd7np0BwceeKCOOOIIhcNhLVq0SGeddZbva1LqfK9LpB8mRgB0epWVlfrlL3/pu83HH3+sc889V/3791d2drYGDRqk73znO4EmVBYtWqQ33nhDc+bMkSRt3rxZ77zzjiT7Lat/+tOfdNxxxykvL0+/+MUvJEmfffaZzj//fA0cOFA5OTnad999df3116uhoSHS/r333qsjjzxSvXv3VnZ2tnr06KFp06bFLBYuv/xyhUIhFRQU6Nlnn7X64v0XscOGDVMoFNK1114ryf5Yrscee0wTJ06M/MvMhx56KO7rAgDoHAYMGKBFixbpzTff1Pz58yVJDQ0Nuu+++yTZ/+ryV7/6lQYNGqS8vLzI/nPnztXhhx+u/Px8FRYW6uijj9ajjz4aWe8dX/7yl7/opJNOUn5+vgYNGqTbbrvN6ks8417zvzj85je/qR/84Afq27evysvLNWzYMP35z3+WJC1cuNAaq4qKiiJjbbO33347ss2iRYsCXTPvv2R8/vnnddhhhyk/P1+HHXZYVFu33nqr9tlnHxUWFurss89WZWVlq20+9dRTmjRpkoqLi5Wfn6+JEyfq+eefT/p1PPfcc3XNNddowIAB6tGjh8455xxVV1dHttu+fbvOPPNMFRQUaMiQIfq///u/VvtfV1ena665Rvvvv79ycnLUt29fnX/++dqyZUtkm2uvvVahUEjDhg3TP/7xD40cOVKFhYU65phjtGrVKklNr7fzzjsvsk/zOTfXIX5OOukkLVq0SB988IEOOOAASU2vhdaO/5e//EUjRoxQTk6O1q1bF9dzIEkzZszQ/vvvr+LiYuXk5Gjo0KH67ne/q6qqqjb7tWnTJh144IEKhUI68sgjtWPHDqsvzVr76FPvz99tt92moUOHKi8vT9OnT4/0GwASgXog/nrg9ttv14oVK7Rx40Z94xvfkCT9+9//1nXXXRfZJui4+NBDD6m8vFx5eXn6whe+oLfffts6ZpA6Yd68efrSl76kgoICDR8+XHfffbfV1vPPP6/Ro0crLy9PEyZMiNyfcK1cuVJf/epX1adPH+Xk5OjAAw/UHXfcYW3TfL/giiuu0MUXX6xevXqpb9++uvTSSyPPVygUiozHf/7znwN9pOjtt9+u1157TU899ZQkKRwO68UXX4w6/g9+8AOdf/75Kisr07Rp0+J+DpYtW6bjjjtOAwYMUG5urgoLCzVu3Dj97W9/8+3XI488ouzsbOvTSNx7J1Lr715qPv/f/e53Ovvss1VcXKw+ffro6quvljEm5jVBF2AAoBOaNGmSkWT2228/U1xcbPLz882nn35q3n33XSPJSDJz5841xhizceNGM3DgQCPJ5ObmmlGjRpmsrCwjyYwePdrU1ta2eZwZM2ZE2jPGmLq6OnPBBRcYSSYnJ8ds3LjRGGPM2rVrI9vl5OSYXr16mVGjRpnrrrvObNmyxQwePNhIMsXFxWbMmDGR45933nmRY1166aUmLy/PHHDAAWbs2LEmNzc3ss/69euNMcY8//zzkeOsXbvW/PSnPzWSTEFBgXnuueei+vL8889H2h86dKiRZK655pqotnJzc80BBxxgSkpKjCSTkZFh3njjjUQ9XQCAJGoeE4cOHRp57PHHH4/8jr/00kuNMS1jWk5OjsnIyDAHHnig6dWrlzHGmOuvvz6y/ZAhQ0z//v0jy3/961+NMfb4kpuba4YPH2569+4deezRRx81xpi4x73mfufk5Jjs7GwzevRoM2bMGHPaaadF2i0uLjYVFRWmoqLCLF261Fx44YVGkhk/fnyknWuuucZIMgcccIDvdZo7d27U+Oh9LDc315SXl0f6OnToUFNfX2+MMeaxxx6LbNenTx8zePBgU1hYaI3JxhjzwAMPmFAoFNl/+PDhRpLJzMw0//nPf5J6HbOzs01xcXHkmJLMj3/848h2X/7ylyOPl5eXm8LCwsg5TJo0KbLdiSeeGOnzmDFjIrXBqFGjzK5du6xrnpWVZbKzs83IkSMj5/2FL3zBGGPMddddZ/bdd9/IMZufxz/+8Y9tPkfN286YMcMYY8wnn3wSeS2eeuqpUc95dna2CYVC5oADDjADBgwwa9eujes5MMaY0tJS06tXLzN27Firn2eccUZkm+afmUmTJplt27aZsWPHRs5lx44dVl+8P39uveZtKzc31+Tn55sDDzzQZGRkGEnmsMMOM+FwuM3rAgDxoB7Y+3rAGGN27dplBgwYYCSZvn37Rn4vBxkXs7OzI/ccsrOzjSSzzz77mJ07dxpjgtcJ2dnZZtiwYdbf6e+++64xxpj169dHxvGCggIzcuRIqzZpvh+yevVqU1paaiSZnj17mtGjR0f6MGfOnMj5N98vyM7ONj179jT77LNPpK277rrLGGNMRUWFKS4uNpJM7969I8/JZ599Fte1DofD1mOPPPJI1PFzcnJMfn6+Ofjgg8306dPjfg4eeeQRk5GRYYYOHWoOPfRQ06NHj8hxHn/88chxvNfnqaeeMjk5OUaSueGGG6L60nzvxBi7JnDbys3NNQMHDrSu2S233OL7OkTXwMQIgE6puXiqqKiIFCH/+7//2+rEyNVXXx0pIpYuXWqMaRo0m7e755572jyOd2LE+xUKhczdd98d2c5bvEyaNCky2dLQ0GCuvfZaI8n069fPbNq0yRhjzPz58yPtvPfee8aYpoKluWAyxpj33nsv0uaf/vQnY4z9h/asWbMiRZD3D/29mRj50Y9+ZIxpKq7KysqMJPO1r31tb54aAEAH895QqKioMIccckjkxkNWVpZZtGiRMcYe05588kljTNM4VVNTY/Lz840kc/rpp5vGxkaze/duc+SRR1o3WLzjy1lnnWXC4bCprq42+++/f2RMNsbEPe55+71s2bJIf7x99f7xaYwxb7zxRqQPzTcGRo8ebSSZn/3sZ77XKdbEyO9//3tjjDG33HJL1DEmTJhgJJkRI0aY6upq09DQYCZPnhx183vYsGFGkjn//PNNOBw24XDYnH766UaSmTBhQlKvY3Fxsfnkk09MY2OjOfzww6223n///cgxr7jiCmOMMStXroy8Tpqv84IFCyLbLVy40BhjzGeffRZ5fTTXI821lyTz2GOPGWOM+d73vhd5rPkmhff6xqO1mkuSGTRokFm3bl1kO+/x77jjDmOMMeFw2DQ2Nsb1HBhjIq+5Zj/5yU8iPzPNdVzz6/Dwww83FRUVRpI56qijTGVlZVRf4p0YycrKMsuXLzfGGPN///d/ke2eeuqpuK4RALSFemDv64FmJ598cmTdpk2b9mpcfPrpp40xxjz99NNRY1XQOuGMM84w4XDY/Pe//41q66qrrjJS02RB87jS/Jj3fsjMmTON1PSPQpvvN9x8881GksnPzzdVVVXGmJb7BcOHDzc7duwwtbW1kX9g6r030Px8Nf8jhnivtfvl7t98/N69e5uPP/7YGNP0Ooj3OVi/fr3ZsGFDpL3a2lqz3377GUnmnHPOiTze3NZ5550XaeOXv/xlq32Jd2Jk4sSJZs+ePWbPnj1m4sSJkdc9uj4+SgtAp3f55Zerd+/euvvuu/X+++9HrV+yZIkkqby8XIcddpgk6bTTTlNBQYEk6fXXX4/rOBUVFTriiCNUUlIiY4y+973v6b///W/UdhdddFHkrciZmZmRj8LauHGj+vbtq1AopNNOO02SZIzR4sWLJTV9xMWpp56qnj17KiMjQ/vvv3+kzc8++yzqOM1vU77//vt17LHHxnUObTnrrLMkSf3794+05b7lFwDQue3Zs0eLFy/WW2+9pT59+uikk07SwoULVVFRYW1XXl6u6dOnS2oap1asWBHJzPr617+ujIwM5ebm6itf+Yok6aOPPtLmzZutNr7+9a8rFAqpqKhIX/rSlyRJy5cvl6S4x71mxx57rMaOHRvpj59DDz00cj733HOPVq9ereXLl0c+gkOSrr/+eh111FGRr+uvvz6u69e8/6hRoyKPbdy4UZK0YsUKSdK0adNUVFSkzMxMffnLX7b237x5c+SjJO655x5lZGQoIyNDjzzyiCRFnbeU2Os4ZcoU7bPPPsrIyNDIkSNb7b+kyPNaXl6uMWPGWG14P75z0qRJCoVCGjhwYOT14X40SWlpqU4++eSo67Zp06aocw2iOWNk9OjRCoVC+uSTT/TNb34zKhA3Pz9f3/rWtyQ1fZzF1q1b434Onn32WY0ePVr5+fnWx2c0NDREvd6XLl2qxYsXa+jQofrXv/6lkpKSvT63gw8+WAcddJCklvpLou4CkDjUA3tfD7jjTNBxsfnjsKWmmqFHjx6Smn7H702dcPbZZysUCvnWJuXl5ZFx5cwzz4xqo/kcli9frsLCQoVCIV122WWSpNraWr311lvW9qeccopKS0uVl5en4cOHW8dsjwMPPFAVFRUaMGCApKaP4rrnnnuitvvKV76iwYMHS7Lvp0j+z0EoFNLll1+ugQMHKisrS/n5+ZH7Q63dT5k7d65qa2v1ve99Tz/84Q/bdW5nnHGGsrOzlZ2drTPOOENS0zVzf17Q9WSlugMAEEtxcbGuvPJKXX755brmmmuSdpzmAXfz5s0aNmyYqqqq9Otf/zrqMyv79evXZj+9BU2zgoIC1dTUaNq0adqxY4fy8vJ06KGHKjs7O1IctRYmWlRUpJqaGv3mN7/RF7/4ReXn50tqKgiaefdr67PQAQDdw9ChQ+P6jOe2xqlE8xv32tOf73znO1q8eLH++te/qri4WFLTzZQhQ4ZIktasWWPdXGieJIilrKxMkpSV1fInkNnLz4fed9991adPn6jH9yZENN7r2Nx/qeUc9rb/kqJuoElN/4Ai1jHbe1ypKWNk3rx5kpo+k3zWrFlasGCB/vOf/2jq1KmR7fr06aOMjNb/LZ/fc/CPf/wjEuY+YMAADR48WFu2bNEHH3wgKbruKiws1M6dO/XRRx/pr3/9q2bNmhVZ11x3UXMB6CyoB/auHqitrdXSpUslNY0vvXv3ttbHMy7GK946IZG1Se/evTVixIiox91JqETXE81uv/12TZ48WY2NjZowYYIWLVqkq6++Wueff761nd/rwO85OOecc/Tss89GJpKKior0zjvvqLq62vd+yv33369Zs2ZZ14axHc14xwiALmHWrFkaPHiw3njjjah148aNkyStWrUqsn7+/PnatWuXJOmII44IdKxQKBQpDHbv3t3q+taOn5WVpQceeECLFi3SokWL9Mwzz+g73/mOTj/9dK1atUo7duyQ1PSvR5YuXaqbb77Ztx9/+ctfVFxcrBdffFFnnnlmJBCtb9++kW1Wr14tqelfRTa335oHH3xQUtO/8GwObD/44IN9jw8A6Jrcceqggw6KTK4/+OCDCofDqqur08MPPyyp6QaL+8f73//+d0nSzp079cQTT0iSRo8eLSm+cc+vP1LLzZKdO3dGrTvzzDPVs2dPbdiwQb/85S8lSeeee25k/bx582SaPhJYxpjIDfb2aP6XmP/+97+1c+dONTY2Rv6FZ7M+ffpo6NChkqTDDjtML730UuTc//KXv+j6669XTk6OtU8ir6Mf7w2p5n6vXr066l+JNh9Tkq688srIMV966SVde+21uuCCC+I+pmTf9GrtuQzKrbvc1068z0HzP3YpLi7W2rVrtXjxYh1//PFtHveII47QVVddJUm65JJLdP/990fWNdddmzZtitw0eeihh9ps6+2339a7774rqaX+kqi7AHQ86oEWlZWV+p//+R9t2LBBkvQ///M/CoVCgcfF7du365lnnpEkPfPMM9q+fbukpt/xe1Mn+GmuTVatWhUZV1obf5rPobS0VE8++WTkmI8//ri+973v6aijjor7mJL/cxIP7/Mc5H6K5P8cNI/tF154oZYvX64nn3xSRUVFbfbj+uuv1+jRo7VhwwZ98Ytf1Pr16yPrmsf25vspW7Zsidwnac3DDz+shoYGNTQ0RH5e+vXr1+rkF7qYDv/wLgCIgzdjpNmf/vQn6zMrveHrzSFqubm55qCDDtqr8PWKigozbty4SHCZ1HoAnfs5pZs2bYqEcOXk5ERCPpvD2IwxZtu2bZGgtOagsX79+kXabC0XZO3ateaZZ56JtPPNb34zEhA3fvz4SFvHHnusKSgoiAR8ttZWYWGhKS8vj5ybN48FANC5tRa22pq2PqfbmOBhq4WFhWb48OGmT58+kceaAzTjGfe8/W7tM6q9OR+jR482FRUVkdwKY4y5/PLLrb5UV1fHvE6xMkaaecfH5u2aPxNdagpkHTJkiMnNzbXGZGOMuffeeyOP9enTxxxyyCGR8bz5PDviOjY/197XxGmnnRY5RnNAa/M5eF8T06ZNi2xXXl5uRo0aFalRmq9Ha7ka3mvZfD28n4k+ZMgQU1FRYV566aU2n6PmbZsDXQ8++OBIQGyfPn3M1q1b2zx+s3ieg7vuuiuyzcCBA83w4cNNz549o/rv/sycd955RmoKpm3+XP533303UmMNHz7cHH744ZHl1toqLCw0+fn5ZtSoUZHtDjnkEMLXAbQb9UDweuDAAw80o0aNssb0448/3uzevTuyfZBxMTc31+Tn55uDDjooEuo9YMCASLZH0DrBe2/BvTfw2WefmYKCAiO1hK/n5eVF3Q9ZuXJlJKy8oKDAHHLIIWbIkCEmMzPTeq20lqvR/Nx4XyvNmWIZGRnm0EMPNdOmTYv7WldUVERySySZCy64wPf4QZ6DL3zhC5F+jRo1ypSVlUUC2FvLBZk7d65Zt26dGTRokJFkDj74YLNt2zZjjDFXXnllZLsJEyaY/v37R8bs1toqLCw0++yzjxW+ftNNN7V5XdB18I4RAF3GzJkzVV5eHvV43759tWjRIn3zm99UWVmZVq1apX79+umiiy7SwoULI3kgsSxevFhLlixRfX29Dj30UN1+++0655xzYu7Xp08fLVq0SOedd5569eoV+ezWiRMn6qabbpLU9Fmk//jHPzRq1CiFw2Hl5OTon//8Z8y2p06dqnvuuUehUEh//etfI58VOm/ePE2cOFGS9Mknn+j222+PfE5nax566CH169dPu3fv1r777qv7778/kscCAOj+rrrqKt1999067LDDIv/yffz48Zo/f36rY91dd92l0aNHq6amRgMHDtQtt9wS+dzweMa9WM4//3x95StfUWlpqZYvX67FixdbH2dw0UUXRf5F4Ze//GXffxGYCKeeeqpuuukm9e/fX9XV1TriiCP0s5/9LGq7b3zjG3r88cc1adIk1dbWatWqVSouLta5556r//mf/4naPtnX0evuu+/WV77yFeXl5amyslLXXXddq/9KdP78+br66qu1//7764MPPtCGDRt04IEH6qqrror8K+B4jRkzRj/96U/Vr18/ffzxx1q8eHHkX8/62bJlixYvXqwVK1aoV69e+tKXvqRnnnlGPXv2jLlvPM/BBRdcoNmzZ6t3796qrq7W5MmTdd1118Vs+6677tL06dNVX1+vM844Qy+//LJGjhypu+66S8OGDdP69evVu3dv3X777W22ccQRR+j3v/+9du7cqezsbB1//PGaP39+q/9SGgA6WrrVA++++67ef/999ezZU1OmTNE999yjp556Srm5uZFtgoyL/fv31wMPPBDp41FHHaWnnnoq8i6LoHWCnwEDBuixxx7TqFGj1NDQoOLiYt17771R25WXl+vVV1/VV7/6VRUUFGjFihUKh8M64YQT4s5h8/r+97+vqVOnqqCgQG+++Wbcma3vvvuuFi9erM2bN2vEiBG6/PLLdeutt8a1bzzPwbx583TssccqLy9Pu3bt0s033xyVpeYaNGiQnn76aZWVlentt9/WSSedpF27dunKK6/UOeeco7KyMq1evVrnnnuuvv71r7fZzi9+8Qsdd9xxqqysVK9evfSTn/xE3/3ud+M6N3RuIWMS8EFyAIBOZ8GCBZGg9bVr12rYsGGp7RAAoFP78MMPIyGczz//vCZPnpyyvtTV1alfv36qrKzUc889pylTpqSsL0F1puuIjjNz5kz9+c9/1qRJk3w/jgMAOrvONI51lnrg2muv1Zw5c+LOd0H30DwpN3fuXM2cOTO1nUFSEL4OAAAAoNM455xztGLFClVWVurwww/vUpMiAAAgMagHACQbEyMAAAAAOo17771X2dnZ+sIXvqA///nPqe4OAABIAeoBAMnGR2kBAAAAAAAAAIC0Qfg6AAAAAAAAAABIG0yMAAAAAAAAAACAtMHECAAAAAAAAAAASBtMjAAAAAAAAAAAgLTBxAgAAAAAAAAAAEgbTIwAAAAAAAAAAIC0wcQIAAAAAAAAAABIG0yMAAAAAAAAAACAtMHECAAAAAAAAAAASBtMjAAAAAAAAAAAgLTBxAgAAAAAAAAAAEgbTIwAAAAAAAAAAIC0wcQIAAAAAAAAAABIG0yMAAAAAAAAAACAtMHECAAAAAAAAAAASBtMjAAAAAAAAAAAgLTBxAgAAAAAAAAAAEgbTIwAAAAAAAAAAIC0wcQIAAAAAAAAAABIG0yMAAAAAAAAAACAtMHECAAAAAAAAAAASBtMjAAAAAAAAAAAgLTBxAgAAAAAAAAAAEgbTIwAAAAAAAAAAIC0wcQIAAAAAAAAAABIG0yMAAAAAAAAAACAtMHECAAAAAAAAAAASBtMjAAAAAAAAAAAgLTBxAgAAAAAAAAAAEgbTIwA6FYmT56sUCikUCikZcuWtbutyy67rF1tzJw5M9Kf+fPnt6stAAAQH+oBAABAPQDADxMjALqdCy+8UOvXr9fo0aP3av/zzjtPV111VUL6csstt2j9+vUJaQsAAMSPegAAAFAPAGgLEyMAup2CggL1799fWVlZgfdtbGzU448/rlNOOSUhfSktLVX//v0T0hYAAIgf9QAAAKAeANAWJkYAdBorV67Uscceq7y8PB1wwAF68sknE/KWV0mqrq7W2WefrcLCQg0YMEA33XRTq2+FfeWVV5Sdna1x48ZFtfHEE0+otLRU9957b6A2AQBA/KgHAAAA9QCAZGNiBECnsHLlSlVUVGjixIlasWKFfvnLX+rcc89Vdna2Ro0a1e72Z8+erZdfflmPPfaYnnnmGb344ot64403orZ77LHHdPLJJysUClmP33fffTrrrLN077336uyzzw7UJgAAiA/1AAAAoB4A0BGCv48MAJJg1qxZ+spXvqLrrrtOkjRixAj97W9/03vvvaecnJx2tV1dXa0///nPuu+++3TcccdJkubOnauBAwdGbfvoo4/qpptush677bbb9JOf/ET//Oc/NWnSpMBtAgCA+FAPAAAA6gEAHYGJEQAp99FHH+k///mP/vvf/1qP5+TkaOzYse1u/4MPPlB9fb2OPPLIyGOlpaUqLy+3tnv33Xf12WefRQoZSXrooYe0adMmvfzyy9bbZ+NtEwAAxId6AAAAUA8A6Ch8lBaAlFu2bFmrb4ldvnx5pPD56KOPdPLJJ+vQQw/V6NGj9fHHHye8H4899pi++MUvKi8vL/LYoYceqj59+uiee+6RMSbhxwQAAE2oBwAAAPUAgI7CxAiAlMvIyFBjY6MaGxsjjz399NORwmfPnj068cQTdfnll+vNN9/Uiy++qAEDBsTd/r777qvs7GwtWbIk8lhlZaVWr15tbffoo4/q1FNPtR4bMWKEnn/+eT366KO65JJLArcJAADiQz0AAACoBwB0FD5KC0DKHX744crOztaPf/xjzZo1S2+++aZ++MMfSpLGjh2rRx55REcddZQmT54sSerRo0eg9ouLizVjxgz94Ac/UM+ePdW3b19dc801ysjIiISobdq0Sa+//roee+yxqP0POOAAPf/885o8ebKysrJ08803x9UmAACIH/UAAACgHgDQUXjHCICUGzhwoP70pz/p73//u8aOHasHH3xQF154ofr376++ffvq7bfftj6rc2/87ne/0/jx4/WlL31JU6dO1dFHH60DDzww8rbYf/7znzryyCPVu3fvVvcvLy/Xf/7zH91///26/PLL42oTAADEj3oAAABQDwDoKLxjBECncM455+icc86JLM+ePTvy+aH9+vXT8uXLJUmNjY2qrKxUz549A7VfXFyse++9N7K8c+dOzZkzR9/61rckNb1N9pRTTrH2WbBggbV84IEHauPGjXG3CQAAgqEeAAAA1AMAOgLvGAHQKb311luRwmfmzJlas2aNRo8erSOOOCLm53TefvvtKioq0ttvvx157M0339T999+vNWvW6I033tDZZ58tSZHPDJ0wYYLOOuusQH2M1aYkXXTRRSoqKgrULgAAaEI9AAAAqAcAJEPIGGNS3QkAcPXr10+/+93vIsVEvD799FPV1tZKkoYMGaKcnBxJTUXK//zP/2jVqlXKycnR4Ycfrt/97nc6+OCD97qP8bS5adMmVVVVSZIGDBigwsLCvT4eAADphnoAAABQDwBIBiZGAAAAAAAAAABA2uCjtAAAAAAAAAAAQNpgYgQAAAAAAAAAAKQNJkbQrd12220aNmyY8vLyVFFRoddeey3VXepwL7zwgk4++WQNHDhQoVBI8+fPT3WXUuaGG27QuHHjVFxcrL59++q0007TqlWrUt2tDnfHHXdozJgxKikpUUlJicaPH6+nnnoq1d3qFG688UaFQiFddtllqe4KgASiHmhCTdCEeqAFNUHrqAeA7ouagHqgGfVAC+qB1lEPdH9MjKDbevDBBzV79mxdc801euONNzR27FhNmzZNmzZtSnXXOtTOnTs1duxY3XbbbanuSsotXLhQs2bN0qJFi/TMM8+ovr5exx9/vHbu3JnqrnWoQYMG6cYbb9TSpUv1+uuva8qUKTr11FO1YsWKVHctpZYsWaI777xTY8aMSXVXACQQ9UALaoIm1AMtqAmiUQ8A3Rc1QRPqgSbUAy2oB6JRD6QHwtfRbVVUVGjcuHH6wx/+IEkKh8MaPHiwLrnkEv3oRz9Kce9SIxQK6ZFH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U1VVpcGDB3PunHva4Nw5d869bcYYVVdXa+DAgR3Uu/ajHkgMfkY4d849fXDunHt3rAek4DUB9UA0fkY4d849faTzuUvpff7xnnui6gEmRpIgJydHhx9+uJ577jmddtppkpoKmeeee04XX3xxq/vk5uYqNzfXeqysrExVn781qKSkJO1+GJpx7px7uuHcOfd0E++5d7V/GUo9kFicO+eebjh3zj3ddNd6QApeE1APtI1z59zTDeeenucupff5x3PuiagHmBhJktmzZ2vGjBk64ogjdOSRR+rmm2/Wzp07dd5556W6awAAoINQDwAAAImaAACAzoaJkST52te+ps2bN+vqq6/Whg0bdMghh+jpp5+OClsDAADdF/UAAACQqAkAAOhsmBhJoosvvrjNj8qIV25urq655pqot9GmA86dc083nDvnnm7S5dypB9qHc+fc0w3nzrmnm3Q69/bWBOl0rVycO+eebjj39Dx3Kb3Pv6PPPWSMMR1yJAAAAAAAAAAAgBTLSHUHAAAAAAAAAAAAOgoTIwAAAAAAAAAAIG0wMQIAAAAAAAAAANIGEyMAAAAAAAAAACBtMDHSid12220aNmyY8vLyVFFRoddeey3VXWq3a6+9VqFQyPoaOXJkZP3u3bs1a9Ys9erVS0VFRfrKV76ijRs3Wm18/PHHOumkk1RQUKC+ffvqBz/4gRoaGjr6VGJ64YUXdPLJJ2vgwIEKhUKaP3++td4Yo6uvvloDBgxQfn6+pk6dqvfee8/aZtu2bTr77LNVUlKisrIyXXDBBaqpqbG2eeuttzRx4kTl5eVp8ODB+tWvfpXsU4sp1rnPnDkz6nVwwgknWNt0xXO/4YYbNG7cOBUXF6tv37467bTTtGrVKmubRL3GFyxYoMMOO0y5ubnab7/9NG/evGSfXkzxnP/kyZOjnvuLLrrI2qYrnv8dd9yhMWPGqKSkRCUlJRo/fryeeuqpyPru/LzHOvfu+px3JOoB6oGuOCZK6VsPSOldE1APUA9QDyRPd6sJqAdaUA9QD3S3sYF6gHqgS9QDBp3SAw88YHJycsw999xjVqxYYS688EJTVlZmNm7cmOqutcs111xjDjroILN+/frI1+bNmyPrL7roIjN48GDz3HPPmddff90cddRR5gtf+EJkfUNDgxk9erSZOnWqefPNN82TTz5pevfuba688spUnI6v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e3D++ecHVVVVQU1NTfC5z30uaGlp8eZ58cUXgxNOOCEoLS0Npk6dGvzoRz8arl3MKmrf29vbgw984ANBY2NjUFxcHMycOTP40pe+FCro98V972+fzSy46aab0vMM1jn+2GOPBQsXLgxKSkqCOXPmeNvIl7j9X79+fXDSSScF9fX1QWlpabD//vsHf/d3fxc0Nzd769kX9//zn/98MHPmzKCkpCRobGwMTj/99HTREwSj+32P2vfR/J4PJ+oB6oF9cUwMgrFbDwTB2K4JqAeoB6gHhs5oqwmoB6gHqAdG79hAPUA9sC/UA4kgCILcf88EAAAAAAAAAABg30PGCAAAAAAAAAAAGDN4MAIAAAAAAAAAAMYMHowAAAAAAAAAAIAxgwcjAAAAAAAAAABgzODBCAAAAAAAAAAAGDN4MAIAAAAAAAAAAMYMHowAAAAAAAAAAIAxgwcjAEaVJUuWWCKRsKampmHfdiKRsEQiYXV1dQOaf3dfE4mEnX322UPaNwAAxhLqAQAAQD0AIAoPRgDss0455RS77LLLvJ8dd9xxtnHjRqutrc1Ln2666SZbvXr1gObd3ddzzz13iHsFAMDoRT0AAACoBwDkigcjAEaVkpISmzRpkiUSibxsv66uziZMmDCgeXf3tby8fIh7BQDA2EI9AAAAqAcAROHBCIB90sUXX2yPP/64XXvttelfN33rrbdCvyp78803W11dnd133302f/58q6iosE9+8pPW3t5ut9xyi82aNcvGjRtnX/va1yyVSqXX39XVZd/61rds6tSpVllZacccc4wtWbIk536++OKLduqpp1p1dbXV1NTYEUccYc8+++wgHQUAAMY26gEAAEA9AGBPFOW7AwCwJ6699lpbvXq1HXLIIXb11VebmVljY6O99dZboXnb29vt3/7t3+yOO+6wlpYW+/jHP27nnHOO1dXV2R/+8Ad788037ROf+IQdf/zx9ulPf9rMzC699FJ75ZVX7I477rApU6bYXXfdZR/84Adt5cqVNnfu3AH384ILLrDDDjvMbrjhBissLLQVK1ZYcXHxoBwDAADGOuoBAABAPQBgT/BgBMA+qba21kpKSqyiosImTZoUOW9PT4/dcMMNtt9++5mZ2Sc/+Un79a9/bZs3b7aqqio76KCD7NRTT7XHHnvMPv3pT9v69evtpptusvXr19uUKVPMzOxb3/qWPfjgg3bTTTfZP//zPw+4n+vXr7e/+7u/swMOOMDMLKeiCQAARKMeAAAA1AMA9gQPRgCMehUVFemix8xs4sSJNmvWLKuqqvJ+tmXLFjMzW7lypaVSKZs3b563nq6uLhs/fnxO27788svti1/8ov3617+2M844wz71qU95fQEAAMODegAAAFAPANiNByMARj391dREItHvz/r6+szMrLW11QoLC+25556zwsJCbz63WBqIK6+80j7zmc/Y/fffbw888IB9//vftzvuuMPOOeecPdgTAACwp6gHAAAA9QCA3XgwAmCfVVJS4gWiDZbDDjvMUqmUbdmyxU488cS9Xt+8efNs3rx59o1vfMPOP/98u+mmmyh8AAAYJNQDAACAegBArgry3QEA2FOzZs2yp59+2t566y3btm1b+l907K158+bZBRdcYIsXL7bf/e53tnbtWnvmmWfsmmuusfvvv3/A6+no6LBLL73UlixZYuvWrbM//elPtnz5cjvwwAMHpZ8AAIB6AAAAUA8AyB0PRgDss771rW9ZYWGhHXTQQdbY2Gjr168ftHXfdNNNtnjxYvvmN79p8+fPt7PPPtuWL19uM2bMGPA6CgsLbfv27bZ48WKbN2+enXvuuXbmmWfaVVddNWj9BABgrKMeAAAA1AMAcpUIgiDIdycAYDRIJBJ211132dlnn53TchdffLE1NTXZ3XffPST9AgAAw4d6AAAAUA8AIx+/MQIAg+j888+3adOmDWjeZcuWWVVVld16661D3CsAADCcqAcAAAD1ADCy8RsjADBI1qxZY2a7fkV29uzZsfN3dHTYO++8Y2ZmVVVVNmnSpCHtHwAAGHrUAwAAgHoAGPl4MAIAAAAAAAAAAMYM/pQWAAAAAAAAAAAYM3gwAgAAAAAAAAAAxgwejAAAAAAAAAAAgDGDByMAAAAAAAAAAGDM4MEIAAAAAAAAAAAYM3gwAgAAAAAAAAAAxgwejAAAAAAAAAAAgDGDByMAAAAAAAAAAGDM4MEIAAAAAAAAAAAYM3gwAgAAAAAAAAAAxgwejAAAAAAAAAAAgDGDByMAAAAAAAAAAGDM4MEIAAAAAAAAAAAYM3gwAgAAAAAAAAAAxgwejAAAAAAAAAAAgDGDByMAAAAAAAAAAGDM4MEIAAAAAAAAAAAYM3gwAgAAAAAAAAAAxgwejAAYNU455RRLJBKWSCRsxYoVe72uyy67bK/WcfHFF6f7c/fdd+/VugAAwMBQDwAAADNqAgDReDACYFT50pe+ZBs3brRDDjkk52U/97nP2d///d8PWl+uvfZa27hx46CtDwAADAz1AAAAMKMmAJBdUb47AACDqaKiwiZNmpTzcqlUyu677z67//77B60vtbW1VltbO2jrAwAAA0M9AAAAzKgJAGTHb4wAGDFee+01O/XUU62srMzmzZtnf/jDHwblV15bWlrsggsusMrKSps8ebL95Cc/Cf0a7J///GcrLi62o446qt913H///VZbW2u33nrrgNcJAAByRz0AAADMqAkADC0ejAAYEV577TU75phj7MQTT7SXX37Z/uVf/sUWL15sxcXFdtBBB+3Vui+//HL705/+ZPfee6899NBDtmzZMnv++ee9ee6991776Ec/aolEIrT8bbfdZueff77deuutdsEFFwx4nQAAIDfUAwAAwIyaAMDQ409pARgRLrnkEvvEJz5hV199tZmZ7bfffvbf//3f9vrrr1tJScker7elpcVuueUWu+222+z00083M7ObbrrJpkyZ4s13zz332E9+8pPQ8tdff71973vfs9///vd28skn57ROAACQG+oBAABgRk0AYOjxYARA3q1bt84effRRe/HFF72fl5SU2KGHHhqaP5VKWWFh4YDW/eabb1pPT48dffTR6Z/V1tba/Pnz0+1XX33V3n333XQBs9tvf/tb27Jli/3pT3/yfn12IOsEAAC5oR4AAABm1AQAhgd/SgtA3q1YsaLfX4d96aWX0kXPxz72Mfvbv/1bO+qoo+ymm24a1O3fe++99v73v9/Kysq8nx922GHW2Nhov/rVrywIgkHdJgAA8FEPAAAAM2oCAMODByMA8q6goMBSqZSlUqn0zx588EGv6Fm5cqXNnz/fli9fbl/84hcHvO45c+ZYcXGxLV++PP2z5uZmW716dbp9zz332FlnnRVadr/99rPHHnvM7rnnHvvqV7+a0zoBAEBuqAcAAIAZNQGA4cGf0gKQd0cccYQVFxfbd7/7XbvkkkvshRdesG9/+9tmZnbooYdaS0uLpVIp+/rXv57zuqurq+2iiy6yv/u7v7P6+nqbMGGCff/737eCggJLJBK2ZcsWe/bZZ+3ee+/td/l58+bZY489ZqeccooVFRXZT3/609h1AgCA3FEPAAAAM2oCAMOD3xgBkHdTpkyxX/7yl3bnnXfaoYceav/zP/9jX/rSl2zSpEk2YcIEe/nll+24447b4/X/+Mc/tkWLFtlHPvIRO+OMM+z444+3Aw880MrKyuz3v/+9HX300dbQ0JB1+fnz59ujjz5qt99+u33zm9+MXScAAMgd9QAAADCjJgAwPPiNEQAjwoUXXmgXXnhhun355Zd7vyL7vve9b4/XXV1dbbfeemu63dbWZldddZV9+ctftnvuucc+9rGPhZZZsmSJ1z7wwANt8+bNA1onAADYM9QDAADAjJoAwNDjN0YAjEh/+ctf9qjo+fnPf25VVVW2cuXK9M9eeOEFu/322+2NN96w559/3i644AIzMzvrrLPshBNOsPPPPz/n/kWtc7evfOUrVlVVlfO6AQDALtQDAADAjJoAwOBLBEEQ5LsTAKAmTpxoP/7xj9PFxEC888471tHRYWZmM2bMsJKSEjPbVaB88YtftFWrVllJSYkdccQR9uMf/3iv/oXJQNa5ZcsWSyaTZmY2efJkq6ys3OPtAQAwFlEPAAAAM2oCAIOPByMAAAAAAAAAAGDM4E9pAQAAAAAAAACAMYMHIwAAAAAAAAAAYMzgwQgAAAAAAAAAABgzeDCCUe3666+3WbNmWVlZmR1zzDH2zDPP5LtLw27p0qX20Y9+1KZMmWKJRMLuvvvufHcpb6655ho76qijrLq62iZMmGBnn322rVq1Kt/dGnY33HCDLViwwGpqaqympsYWLVpkDzzwQL67NSL86Ec/skQiYZdddlm+uwJgEFEP7EJNsAv1QAY1Qf+oB4DRi5qAemA36oEM6oH+UQ+MfjwYwaj1P//zP3b55Zfb97//fXv++eft0EMPtb/6q7+yLVu25Ltrw6qtrc0OPfRQu/766/Pdlbx7/PHH7ZJLLrGnnnrKHnroIevp6bEPfOAD1tbWlu+uDatp06bZj370I3vuuefs2WeftdNOO83OOusse/nll/Pdtbxavny53XjjjbZgwYJ8dwXAIKIeyKAm2IV6IIOaIIx6ABi9qAl2oR7YhXogg3ogjHpgbEgEQRDkuxPAUDjmmGPsqKOOsp/97GdmZtbX12fTp0+3r371q/ad73wnz73Lj0QiYXfddZedffbZ+e7KiLB161abMGGCPf7443bSSSfluzt5VV9fb//6r/9qX/jCF/LdlbxobW21ww8/3H7+85/bD3/4Q1u4cKH99Kc/zXe3AAwC6oH+URNkUA/4xnJNQD0AjG7UBGHUAxnUAz7qAeqBsYDfGMGo1N3dbc8995ydccYZ6Z8VFBTYGWecYU8++WQee4aRpLm52cx2DfhjVSqVsjvuuMPa2tps0aJF+e5O3lxyySX24Q9/2LtmANj3UQ9gIKgHdqEmoB4ARjNqAsShHtiFeoB6YCwpyncHgKGwbds2S6VSNnHiRO/nEydOtNdeey1PvcJI0tfXZ5dddpkdf/zxdsghh+S7O8Nu5cqVtmjRIuvs7LSqqiq766677KCDDsp3t/LijjvusOeff96WL1+e764AGGTUA4gz1usBM2qC3agHgNGNmgBRqAeoB3ajHhhbeDACYEy65JJL7KWXXrInnngi313Ji/nz59uKFSusubnZfvvb39pFF11kjz/++JgrfN5++237+te/bg899JCVlZXluzsAgGE21usBM2oCM+oBABjrqAeoB8yoB8YiHoxgVGpoaLDCwkLbvHmz9/PNmzfbpEmT8tQrjBSXXnqp3XfffbZ06VKbNm1avruTFyUlJbb//vubmdkRRxxhy5cvt2uvvdZuvPHGPPdseD333HO2ZcsWO/zww9M/S6VStnTpUvvZz35mXV1dVlhYmMceAtgb1AOIQj2wCzUB9QAwFlATIBvqgV2oB6gHxiIyRjAqlZSU2BFHHGGPPPJI+md9fX32yCOPjNm/kQizIAjs0ksvtbvuusseffRRmz17dr67NGL09fVZV1dXvrsx7E4//XRbuXKlrVixIv3fkUceaRdccIGtWLGCogfYx1EPoD/UA9HGYk1APQCMftQEUNQD0agHqAfGAn5jBKPW5ZdfbhdddJEdeeSRdvTRR9tPf/pTa2trs8997nP57tqwam1ttTVr1qTba9eutRUrVlh9fb3NmDEjjz0bfpdcconddtttds8991h1dbVt2rTJzMxqa2utvLw8z70bPldccYWdeeaZNmPGDGtpabHbbrvNlixZYn/84x/z3bVhV11dHfobspWVlTZ+/Pgx+7dlgdGGeiCDmmAX6oEMaoJdqAeAsYGaYBfqgV2oBzKoB3ahHhh7eDCCUevTn/60bd261f7xH//RNm3aZAsXLrQHH3wwFLY22j377LN26qmnptuXX365mZlddNFFdvPNN+epV/lxww03mJnZ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eQAAAJAHABzHxAiAhOf8aKrH42lxmd/vlyRVVVXJ6/VqzZo18nq91nahYak97rrrLn3jG9/QSy+9pFdeeUV33nmnnn76aV166aUdGAkAAOgo8gAAACAPADiOiREAPVZ6erpVEC1WJk2aJJ/PpwMHDuiss85yfbzRo0dr9OjRuummm3TFFVdoyZIlBB8AAGKEPAAAAMgDAKKVEu8OAEBHDR06VO+8847Kysp06NChwF90uDV69GhdeeWVuvrqq/Xcc89px44dWrVqlRYvXqyXXnqp3cepra3VDTfcoOXLl2vnzp36xz/+oXfffVcnnnhiTPoJAADIAwAAgDwAIHpMjADosW655RZ5vV6NHTtW/fr1065du2J27CVLlujqq6/WzTffrDFjxmjWrFl69913VVJS0u5jeL1eHT58WFdffbVGjx6tyy67TBdeeKEWLVoUs34CAJDsyAMAAIA8ACBaHmOMiXcnACAReDwePf/885o1a1ZU+11zzTU6duyY/vznP3dKvwAAQNchDwAAAPIA0P3xiREAiKErrrhCgwcPbte2b775pnJycvTEE090cq8AAEBXIg8AAADyANC98YkRAIiRbdu2SWr+iOywYcPa3L62tlaffPKJJCknJ0fFxcWd2j8AAND5yAMAAIA8AHR/TIwAAAAAAAAAAICkwVdpAQAAAAAAAACApMHECAAAAAAAAAAASBpMjAAAAAAAAAAAgKTBxAgAAAAAAAAAAEgaTIwAAAAAAAAAAICkwcQIAAAAAAAAAABIGkyMAAAAAAAAAACApMHECAAAAAAAAAAASBpMjAAAAAAAAAAAgKTBxAgAAAAAAAAAAEgaTIwAAAAAAAAAAICkwcQIAAAAAAAAAABIGkyMAAAAAAAAAACApMHECAAAAAAAAAAASBpMjAAAAAAAAAAAgKTBxAgAdNCxY8d0yimnaOLEiRo3bpx+97vfxbtLAACgi5EHAACARCYAehqPMcbEuxMA0BP5fD7V19crOztb1dXVGjdunFavXq2+ffvGu2sAAKCLkAcAAIBEJgB6Gj4xAiDhTJs2TR6PRx6PR+vWreu083i9XmVnZ0uS6uvrZYxR6FzzNddcE+jHn//8507rR7J444039KUvfUkDBw7s0H161113BR6P0J9evXp1TocBAHFFHkhM5AEAQLTIBImHPIBYYGIEQEKaN2+e9u7d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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "832c2602b9bb42f0993ea58fdc0e82ea", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig10_hydrometeors.pdf
…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Hydrometeors\n", + "fig, axes = fig_ax()\n", + "\n", + "key = gen_key(breakup=False, stochastic_breakup=False)\n", + "plot_ax(axes[0][0], output=output[key], **kwargs['cloud water mixing ratio'])\n", + "plot_ax(axes[1][0], output=output[key], **kwargs['rain water mixing ratio'])\n", + "plot_ax(axes[2][0], output=output[key], **kwargs['na'])\n", + "\n", + "key = gen_key(breakup=True, stochastic_breakup=False)\n", + "plot_ax(axes[0][1], output=output[key], **kwargs['cloud water mixing ratio'])\n", + "plot_ax(axes[1][1], output=output[key], **kwargs['rain water mixing ratio'])\n", + "plot_ax(axes[2][1], output=output[key], **kwargs['na'])\n", + "\n", + "key = gen_key(breakup=True, stochastic_breakup=True)\n", + "plot_ax(axes[0][2], output=output[key], **kwargs['cloud water mixing ratio'])\n", + "plot_ax(axes[1][2], output=output[key], **kwargs['rain water mixing ratio'])\n", + "plot_ax(axes[2][2], output=output[key], **kwargs['na'])\n", + "\n", + "add_ylabels_qn(axes)\n", + "add_titles(axes)\n", + "plt.tight_layout()\n", + "show_plot(\"fig10_hydrometeors.pdf\", inline_format=inline_format)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-13T12:36:48.421528Z", + "start_time": "2024-12-13T12:36:47.295779Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "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3WUCtHef0tLSjLbt3LnTbNugQYP0/5ZIJIiLi8PAgQP123R3+U6fPq3fJpPJAMDojhQRkSHGDMYMHcYM/8KqUOR1Y8aMQUpKClasWIHu3btDo9FgwIABZhPaIiMjHT637gPJHpcvXwYArF+/Hj169DDaFx4ebvO5ltpm7/tyREhIiFkXdWtrq/7fEokEX3/9Nb7//nts2LABy5YtwwsvvICtW7fiuuuus3jO+Ph4o0CiVqvxyy+/ID093ei4HTt24N577zXaZlp9RBAEo226LwaGQxTOnTsHAOjatWu775eIyBRjhv0YM8jT2GNBHrF161ajx1u2bEHv3r1x4cIF7Nu3D7Nnz8aIESPQr1+/du+WGJJKpVCr1Vb39+7dGzKZDJWVle2eq3///ggPD8exY8fQq1cvox9L43BtOXv2rM331bNnT4SFhRldl/Pnz2P//v02z9u1a1ecOnVK/7ixsRGHDx82OkYQBPz2t7/FvHnzsHPnTkilUqxdu9bqOdPT07F7927943379qG5uRndu3fXb6uursbJkyfN7j51xC+//IJrrrkG8fHxTp+LiAITYwZjhg5jhn9hjwV5xLFjx1BYWIhHH30UO3bswLJly7BkyRLExMQgLi4O77zzDrp164Zjx45h1qxZdp83NTUVW7duxZEjRxAVFYXY2Fij/REREXjuuefw7LPPQiqV4re//S0aGhqwa9cus1J5nTp1wsyZM/HUU09Bo9HglltuwcWLF/Hdd98hOjoa+fn5drervfcVFRWFKVOm4JlnnkFcXBwSEhLwwgsvICTEdq7/+9//Hu+99x7GjBmDLl26oLi4GBKJRL9/69atqKysxMiRI5GQkICtW7eioaEB/fr1s3rO3NxcFBUV4fz584iJiUFtbS0AYNmyZXjyySdx8OBBPPnkkwBcUxZx8+bNGDlypNPnIaLAxZjBmKHDmOFfmFiQR+Tl5UGpVCIrKwsSiQTTp0/HI488AkEQ8OGHH+LJJ5/EgAED0KdPH7zxxhsYPny4XeedOXMm8vPz0b9/fyiVSrM7MQAwZ84chIaGori4GL/++iu6deuGxx57zOL5Xn75ZXTt2hUlJSX43//+hy5duuDGG2/E888/79D7DQkJafd9LVq0CJcvX8aYMWPQqVMnPP3007h48aLN8xYVFeHw4cO466670LlzZ7z88stG7zk6Ohr//e9/UVpaisbGRqSkpGDJkiUYPXq01XMOHDgQN954Iz766CM8+uijqK2tRW5uLv73v/9h4MCB6N+/P+bNm4fHH38cb7zxBj744AOHroWh5uZmrFu3DhUVFR0+BxEFPsYMxgyAMcMfCaLp4DsiF7O00in5lvXr1+OZZ57BL7/8gtGjR2PIkCGYP3++y1/n7bffxtq1a7FhwwaXn5uIAgNjhu9jzCBr2GNBRLjzzjtx4MABnDx5Ej/++CMeeught7xOWFgYli1b5pZzExGRZzBmkDVMLIgIADBjxgzU1dWhvr7eqPyfKz388MNuOS8REXkWYwZZwqFQRERERETkNJabJSIiIiIipzGxICIiIiIipzGxICIiIiIipzGxICIiIiIipzGxICIiIiIipzGxICIiIiIipzGxICIiIiIipzGxICIiIiIipzGxICIiIiIipzGxICIiIiIipzGxICIiIiIip/1/1G9/ggkHLWYAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "894dfe72c43546f5bfa061697ae8ce76", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig11_spectra_from_1d.pdf" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "755e3a6dfcb9435dbdfe9c393dbf1d69", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig12_collision_rates.pdf" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "4604aa553103433f9b433ae75a10b318", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig13_aerosol_rates.pdf" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "fb6e231bf8914a0784b3a21260ab8733", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig4_coalescence-only.pdf" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "4a8e414f97074c71877720cf5d0a37fc", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig3_breakup-only.pdf
\")…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "title = \"fig3_breakup-only\"\n", + "c = 1e-15 / si.s\n", + "beta = 1e-9 / si.s\n", + "frag_mass = 0.25 * si.g\n", + "\n", + "n_sds = (8, 32, 128, 256) if not CI else (8, 16)\n", + "\n", + "settings = Settings(\n", + " srivastava_c=c,\n", + " srivastava_beta=beta,\n", + " frag_mass=frag_mass,\n", + " drop_mass_0=drop_mass_0,\n", + " dt=dt,\n", + " dv=dv,\n", + " n_sds=n_sds,\n", + " total_number=total_number,\n", + ")\n", + "coalescence_and_breakup_eq13(\n", + " settings,\n", + " n_steps=n_steps,\n", + " n_realisations=n_realisations,\n", + ")\n", + "show_plot(f'{title}.pdf', inline_format='png')" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-15T05:19:25.550567Z", + "start_time": "2024-12-15T01:55:34.739021Z" + }, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "3866be79dac0455ea76f46fbaba2938e", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig5_coalescence-breakup…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "title = \"fig5_coalescence-breakup\"\n", + "c = 0.5e-6 / si.s\n", + "beta = 1e-9 / si.s\n", + "frag_mass = 0.25 * si.g\n", + "\n", + "n_sds = [2 ** power for power in range(8, 12)] if not CI else (8, 16)\n", + "\n", + "settings = Settings(\n", + " srivastava_c=c,\n", + " srivastava_beta=beta,\n", + " frag_mass=frag_mass,\n", + " drop_mass_0=drop_mass_0,\n", + " dt=dt,\n", + " dv=dv,\n", + " n_sds=n_sds,\n", + " total_number=total_number,\n", + ")\n", + "coalescence_and_breakup_eq13(\n", + " settings,\n", + " n_steps=n_steps,\n", + " n_realisations=n_realisations,\n", + ")\n", + "show_plot(f'{title}.pdf', inline_format='png')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-15T05:19:25.628796Z", + "start_time": "2024-12-15T05:19:25.627584Z" + } + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + }, + "vscode": { + "interpreter": { + "hash": "b43cf254c70d60c2e21a7f71ba113e70c1694742e72407132919c841d907074b" + } + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/figs_6_7_8.ipynb b/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/figs_6_7_8.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..da04d8516723431795e913d39e84e831d82638c4 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/figs_6_7_8.ipynb @@ -0,0 +1,8394 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/deJong_Mackay_et_al_2023/figs_6_7_8.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/deJong_Mackay_et_al_2023/figs_6_7_8.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/deJong_Mackay_et_al_2023/figs_6_7_8.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Sensitivity to Ec (coalescence efficiency)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-10T19:29:15.893402Z", + "start_time": "2024-12-10T19:29:15.884853Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-10T19:29:18.636725Z", + "start_time": "2024-12-10T19:29:15.904933Z" + }, + "collapsed": false + }, + "outputs": [], + "source": [ + "from matplotlib import pyplot\n", + "from PySDM_examples.deJong_Mackay_et_al_2023 import Settings0D\n", + "from PySDM_examples.deJong_Mackay_et_al_2023 import run_box_breakup\n", + "from open_atmos_jupyter_utils import show_plot\n", + "import numpy as np\n", + "\n", + "from PySDM.dynamics.collisions.coalescence_efficiencies import ConstEc, Straub2010Ec\n", + "from PySDM.dynamics.collisions.breakup_fragmentations import Exponential, AlwaysN, Straub2010Nf\n", + "from PySDM.physics import si" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-10T19:29:39.985588Z", + "start_time": "2024-12-10T19:29:18.786822Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "finished Ec=1.0\n", + "[[ 0. 0. 0. 0. ]\n", + " [998971.28 0. 998971.28 0. ]\n", + " [ 1013.43 0. 1013.43 0. ]]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "finished Ec=0.9\n", + "[[ 0. 0. 0. 0. ]\n", + " [2351083.68 0. 2117998.12 222207.12]\n", + " [ 216970.64 0. 195200.09 20488.63]]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "finished Ec=0.8\n", + "[[ 0. 0. 0. 0. ]\n", + " [6376614.05 0. 5102963.73 1206902.97]\n", + " [9931776.88 0. 7885179.78 1917788.48]]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "finished Ec=0.7\n", + "[[ 0. 0. 0. 0. ]\n", + " [11635359.67 0. 8154591.14 3327335.79]\n", + " [34663858.8 0. 24281504.96 10237258.62]]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "finished Straub\n", + "[[ 0. 0. 0. 0. ]\n", + " [1015141.72 0. 1012161.81 2780.09]\n", + " [ 9211.03 0. 7912.68 1202.23]]\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-12-10T20:29:39.886700\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.9.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "1384aedff2f840cf870fa337fdaff326", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig6_Ec_sensitivity.pdf\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-12-10T20:30:05.367196\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.9.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f301c288b8b046c8bba68d36b3765911", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig7_Pf_sensitivity_c…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Constant n_f\n", + "settings0 = Settings0D()\n", + "settings0.coal_eff = ConstEc(Ec=0.95)\n", + "settings0.n_sd = 2**13\n", + "settings0.radius_bins_edges = np.logspace(\n", + " np.log10(1e0 * si.um), np.log10(1e4 * si.um), num=128, endpoint=True\n", + ")\n", + "cmap = pyplot.get_cmap('viridis')\n", + "legend_entries = []\n", + "nf_vals = [1, 4, 16, 64]\n", + "res = run_box_breakup(settings0, [0])\n", + "\n", + "fig, ax = pyplot.subplots(ncols=2,sharey=True,figsize=(10,4),dpi=200)\n", + "ax[0].step(res.x, res.y[0]*settings0.rho, color='k', linestyle='--', label='initial')\n", + "for (i, nf) in enumerate(nf_vals):\n", + " settings = Settings0D(fragmentation=AlwaysN(n=nf))\n", + " settings._steps = [0, 120] # pylint: disable=protected-access\n", + " settings.dt = 1 * si.s\n", + " settings.coal_eff = settings0.coal_eff\n", + " settings.n_sd = settings0.n_sd\n", + " settings.radius_bins_edges = settings0.radius_bins_edges\n", + " settings.warn_overflows = False\n", + "\n", + " res = run_box_breakup(settings)\n", + " print(\"finished nf=\"+str(nf))\n", + " for (j, step) in enumerate(settings._steps): # pylint: disable=protected-access\n", + " if j == 0:\n", + " continue\n", + " lbl = 'n_f = ' + str(nf)\n", + " if nf == 1:\n", + " lbl += '(coalescence)'\n", + " ax[0].step(res.x, res.y[j]*settings.rho, color=cmap(i/len(nf_vals)),\n", + " label= lbl if lbl not in pyplot.gca().get_legend_handles_labels()[1] else '')\n", + " print(res.rates)\n", + "ax[0].set_xscale(\"log\")\n", + "ax[0].set_xlabel(\"particle radius (um)\")\n", + "ax[0].set_ylabel(\"dm/dlnR (kg/m$^3$)\")\n", + "ax[0].legend()\n", + "ax[0].set_title(r\"(a) $P_f = \\delta(v_f - v*)$\")\n", + "\n", + "\n", + "# Exponential fragmentation\n", + "settings0 = Settings0D()\n", + "settings0.n_sd = 2**10\n", + "settings0.radius_bins_edges = np.logspace(\n", + " np.log10(1.0 * si.um), np.log10(5000 * si.um), num=128, endpoint=True\n", + " )\n", + "cmap = pyplot.get_cmap('viridis')\n", + "legend_entries = []\n", + "\n", + "X0 = settings0.X0\n", + "mu_vals = [4*X0, X0, X0/4]\n", + "vmin = 0.1 * si.um**3\n", + "nfmax = None\n", + "Ec = 0.95\n", + "settings0.coal_eff=ConstEc(Ec=Ec)\n", + "\n", + "res = run_box_breakup(settings0, [0])\n", + "ax[1].step(res.x, res.y[0]*settings0.rho, color='k', linestyle='--', label='initial (mean = X$_0$)')\n", + "for (i, mu) in enumerate(mu_vals):\n", + " settings = Settings0D(fragmentation=Exponential(scale=mu, vmin=vmin, nfmax=nfmax))\n", + " settings._steps = [0, 120] # pylint: disable=protected-access\n", + " settings.dt = 1 * si.s\n", + " settings.warn_overflows = False\n", + " settings.coal_eff = settings0.coal_eff\n", + " settings.n_sd = settings0.n_sd\n", + " settings.radius_bins_edges = settings0.radius_bins_edges\n", + "\n", + " res = run_box_breakup(settings)\n", + " print(mu, res.rates)\n", + " for (j, step) in enumerate(settings._steps): # pylint: disable=protected-access\n", + " if j == 0:\n", + " continue\n", + " lbl = r'$\\mu$ = ' + str(round(mu/X0,2)) + 'X$_0$'\n", + " ax[1].step(res.x, res.y[j]*settings.rho, color=cmap(i/len(mu_vals)),linestyle='-',\n", + " label= lbl if lbl not in pyplot.gca().get_legend_handles_labels()[1] else '')\n", + "\n", + "ax[1].set_xscale(\"log\")\n", + "ax[1].set_xlabel(\"particle radius (um)\")\n", + "ax[1].set_ylabel(\"dm/dlnr (kg/m$^3$)\")\n", + "ax[1].legend()\n", + "ax[1].set_title(r\"(b) $P_f \\approx \\exp (v/\\mu)$\")\n", + "pyplot.tight_layout()\n", + "show_plot('fig7_Pf_sensitivity_constEc.pdf')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Fully Stochastic: Straub 2010" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "ExecuteTime": { + "end_time": "2024-12-10T19:30:22.270775Z", + "start_time": "2024-12-10T19:30:05.642282Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-12-10T20:30:22.205336\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.9.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "72fa2406022c43d7a084bf04eb6a48d4", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./fig8_straub_box.pdf
\"), HT…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "settings = Settings0D(fragmentation=Straub2010Nf(vmin=vmin, nfmax=nfmax))\n", + "settings._steps = [0, 60, 240, 1800, 7200] # pylint: disable=protected-access\n", + "cmap = pyplot.get_cmap('viridis')\n", + "legend_entries = []\n", + "\n", + "settings.n_sd = 2**11\n", + "settings.warn_overflows = False\n", + "settings.radius_bins_edges = np.logspace(\n", + " np.log10(4.0 * si.um), np.log10(1e4 * si.um), num=64, endpoint=True\n", + ")\n", + "vmin = X0 * 1e-3\n", + "nfmax = 10\n", + "settings.coal_eff=Straub2010Ec()\n", + "\n", + "res = run_box_breakup(settings, [0])\n", + "pyplot.figure(figsize=(6,4),dpi=200)\n", + "pyplot.step(res.x, res.y[0]*settings.rho, color='k', linestyle='--', label='initial')\n", + "\n", + "res = run_box_breakup(settings)\n", + "for (j, step) in enumerate(settings._steps): # pylint: disable=protected-access\n", + " if j == 0:\n", + " continue\n", + " pyplot.step(\n", + " res.x, res.y[j] * settings.rho,\n", + " color=cmap(j/len(settings._steps)), # pylint: disable=protected-access\n", + " linestyle='-',\n", + " label=f't = {step}s'\n", + " )\n", + "\n", + "pyplot.xscale(\"log\")\n", + "pyplot.xlabel(\"particle radius (um)\")\n", + "pyplot.ylabel(\"dm/dlnr (kg/m$^3$)\")\n", + "pyplot.legend()\n", + "show_plot('fig8_straub_box.pdf')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.4 (default, Apr 9 2021, 09:32:38) \n[Clang 10.0.0 ]" + }, + "vscode": { + "interpreter": { + "hash": "b43cf254c70d60c2e21a7f71ba113e70c1694742e72407132919c841d907074b" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/plot_rates.py b/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/plot_rates.py new file mode 100644 index 0000000000000000000000000000000000000000..ce02159d04c1380905151f8498f40ca04d1db693 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/plot_rates.py @@ -0,0 +1,94 @@ +import numpy as np +from matplotlib import colors, pyplot + +from PySDM.physics.constants import convert_to, si + + +def log_kwargs(clog, cmin, cmax): + if clog: + return {"norm": colors.LogNorm(vmin=cmin, vmax=cmax)} + return {"vmin": cmin, "vmax": cmax} + + +def plot_ax( + ax, + var, + qlabel, + output, + contour_var1=None, + contour_lvl1=None, + contour_var2=None, + contour_lvl2=None, + cmin=None, + cmax=None, + clog=False, +): + tgrid = output["t"].copy() + zgrid = output["z"].copy() + convert_to(zgrid, si.km) + + if clog: + data = output[var].copy() + data[data == 0] = np.nan + else: + data = output[var] + + mesh = ax.pcolormesh( + tgrid, + zgrid, + data, + cmap="BuPu", + shading="nearest", + **log_kwargs(clog, cmin, cmax), + ) + + if contour_var1 is not None and contour_lvl1 is not None: + ax.contour( + tgrid, + zgrid, + output[contour_var1], + contour_lvl1, + colors="k", + linestyles="--", + ) + if contour_var2 is not None and contour_lvl2 is not None: + ax.contour( + tgrid, + zgrid, + output[contour_var2], + contour_lvl2, + colors="r", + linestyles="--", + ) + + ax.set_xlabel("time [s]") + ax.set_ylabel("z [km]") + ax.set_ylim(0, None) + + if clog: + cbar_levels = np.logspace(np.log10(cmin), np.log10(cmax), 5, endpoint="True") + else: + cbar_levels = np.linspace(cmin, cmax, 5, endpoint="True") + cbar = pyplot.colorbar(mesh, fraction=0.05, location="top", extend="max", ax=ax) + cbar.set_ticks(cbar_levels) + cbar.set_label(qlabel) + + +def plot_zeros_ax(ax, var, qlabel, output, cmin=None, cmax=None, clog=False): + dt = output["t"][1] - output["t"][0] + dz = output["z"][1] - output["z"][0] + tgrid = np.concatenate(((output["t"][0] - dt / 2,), output["t"] + dt / 2)) + zgrid = np.concatenate(((output["z"][0] - dz / 2,), output["z"] + dz / 2)) + convert_to(zgrid, si.km) + + zeros = np.zeros_like(output[var]) + kwargs = log_kwargs(clog, cmin, cmax) + cmap = "BuPu" + mesh = ax.pcolormesh(tgrid, zgrid, zeros, cmap=cmap, **kwargs) + + ax.set_xlabel("time [s]") + ax.set_ylabel("z [km]") + ax.set_ylim(0, None) + + cbar = pyplot.colorbar(mesh, fraction=0.05, location="top", ax=ax) + cbar.set_label(qlabel) diff --git a/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/settings1D.py b/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/settings1D.py new file mode 100644 index 0000000000000000000000000000000000000000..f022199c10d1ff68a2abb524215af58d13394c92 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/settings1D.py @@ -0,0 +1,92 @@ +from typing import Iterable + +import numpy as np +from PySDM_examples.Shipway_and_Hill_2012 import Settings as SettingsSH + +from PySDM import Formulae +from PySDM.dynamics.collisions.breakup_efficiencies import ConstEb +from PySDM.dynamics.collisions.breakup_fragmentations import Gaussian, Straub2010Nf +from PySDM.dynamics.collisions.coalescence_efficiencies import ConstEc, Straub2010Ec +from PySDM.physics import si + + +class Settings1D(SettingsSH): + def __dir__(self) -> Iterable[str]: + return ( + "n_sd_per_gridbox", + "p0", + "kappa", + "rho_times_w_1", + "particles_per_volume_STP", + "dt", + "dz", + "precip", + "breakup", + "stochastic_breakup", + "z_max", + "t_max", + "cloud_water_radius_range", + "rain_water_radius_range", + "r_bins_edges_dry", + "r_bins_edges", + "save_spec_at", + ) + + def __init__( + self, + *, + old_buggy_density_formula: bool, + n_sd_per_gridbox: int, + p0: float = 1007 * si.hPa, # as used in Olesik et al. 2022 (GMD) + kappa: float = 1, + rho_times_w_1: float = 2 * si.m / si.s * si.kg / si.m**3, + particles_per_volume_STP: int = 50 / si.cm**3, + dt: float = 1 * si.s, + dz: float = 25 * si.m, + z_max: float = 3000 * si.m, + t_max: float = 3600 * si.s, + precip: bool = True, + breakup: bool = False, + stochastic_breakup: bool = False, + warn_breakup_overflow: bool = False, + output_every_n_steps: int = 1, + save_spec_at=(), + ): + if stochastic_breakup: + self.coalescence_efficiency = Straub2010Ec() + self.fragmentation_function = Straub2010Nf(vmin=1 * si.um**3) + else: + self.coalescence_efficiency = ConstEc(Ec=0.95) + frag_scale_r = 30 * si.um + frag_scale_v = frag_scale_r**3 * 4 / 3 * np.pi + self.fragmentation_function = Gaussian( + mu=frag_scale_v, + sigma=frag_scale_v / 2, + vmin=(1 * si.um) ** 3, + nfmax=20, + ) + + super().__init__( + n_sd_per_gridbox=n_sd_per_gridbox, + p0=p0, + kappa=kappa, + rho_times_w_1=rho_times_w_1, + particles_per_volume_STP=particles_per_volume_STP, + dt=dt, + dz=dz, + z_max=z_max, + t_max=t_max, + precip=precip, + formulae=Formulae( + fragmentation_function=self.fragmentation_function.__class__.__name__ + ), + save_spec_and_attr_times=save_spec_at, + old_buggy_density_formula=old_buggy_density_formula, + ) + self.breakup = breakup + self.stochastic_breakup = stochastic_breakup + + self.breakup_efficiency = ConstEb(Eb=1.0) + + self.warn_breakup_overflow = warn_breakup_overflow + self.output_steps = range(0, self.nt, output_every_n_steps) diff --git a/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/settings_0D.py b/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/settings_0D.py new file mode 100644 index 0000000000000000000000000000000000000000..ada1bed10e442e47bc30d02b5d73aa699383a8d5 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/settings_0D.py @@ -0,0 +1,56 @@ +from typing import Optional + +import numpy as np +from pystrict import strict + +from PySDM.dynamics.collisions import breakup_fragmentations +from PySDM.dynamics.collisions.breakup_efficiencies import ConstEb +from PySDM.dynamics.collisions.coalescence_efficiencies import Berry1967 +from PySDM.dynamics.collisions.collision_kernels import Geometric +from PySDM.formulae import Formulae +from PySDM.initialisation import spectra +from PySDM.physics import si + +TRIVIA = Formulae().trivia + + +@strict +class Settings0D: + X0 = TRIVIA.volume(radius=30.531 * si.micrometres) + + def __init__( + self, + fragmentation: object = None, + seed: Optional[int] = None, + warn_overflows: bool = True, + ): + self.n_sd = 2**10 + self.n_part = 100 / si.cm**3 + self.frag_scale = TRIVIA.volume(radius=100 * si.micrometres) + self.dv = 1 * si.m**3 + self.norm_factor = self.n_part * self.dv + self.rho = 1000 * si.kilogram / si.metre**3 + self.dt = 1 * si.seconds + self.adaptive = True + self.warn_overflows = warn_overflows + self.seed = 44 + self._steps = [0] + self.kernel = Geometric() + self.coal_eff = Berry1967() + self.fragmentation = fragmentation or breakup_fragmentations.Exponential( + scale=self.frag_scale + ) + self.vmin = 0.0 + self.break_eff = ConstEb(1.0) # no "bouncing" + self.spectrum = spectra.Exponential(norm_factor=self.norm_factor, scale=self.X0) + self.radius_bins_edges = np.logspace( + np.log10(0.01 * si.um), np.log10(5000 * si.um), num=64, endpoint=True + ) + self.radius_range = [0 * si.um, 1e6 * si.um] + self.formulae = Formulae( + seed=seed, fragmentation_function=self.fragmentation.__class__.__name__ + ) + + @property + def output_steps(self): + return [int(step / self.dt) for step in self._steps] diff --git a/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/simulation1D.py b/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/simulation1D.py new file mode 100644 index 0000000000000000000000000000000000000000..4a147befd51b30d3b24197851c222dee21306190 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/simulation1D.py @@ -0,0 +1,67 @@ +import numpy as np +from PySDM_examples.Shipway_and_Hill_2012.simulation import Simulation as SimulationSH + +import PySDM.products as PySDM_products +from PySDM.dynamics import Collision +from PySDM.dynamics.collisions.collision_kernels import Geometric +from PySDM.physics import si + + +class Simulation1D(SimulationSH): + def __init__(self, settings): + super().__init__(settings) + self.output_steps = settings.output_steps + + @staticmethod + def add_collision_dynamic(builder, settings, products): + if settings.breakup: + builder.add_dynamic( + Collision( + collision_kernel=Geometric(collection_efficiency=1), + coalescence_efficiency=settings.coalescence_efficiency, + breakup_efficiency=settings.breakup_efficiency, + fragmentation_function=settings.fragmentation_function, + adaptive=settings.coalescence_adaptive, + warn_overflows=settings.warn_breakup_overflow, + ) + ) + products.append( + PySDM_products.BreakupRateDeficitPerGridbox( + name="breakup_deficit", + ) + ) + products.append( + PySDM_products.BreakupRatePerGridbox( + name="breakup_rate", + ) + ) + else: + SimulationSH.add_collision_dynamic(builder, settings, products) + + radius_bins_edges = np.logspace( + np.log10(0.01 * si.um), np.log10(5000 * si.um), num=101, endpoint=True + ) + products.append( + PySDM_products.NumberSizeSpectrum( + name="N(v)", radius_bins_edges=radius_bins_edges + ) + ) + products.append( + PySDM_products.ParticleVolumeVersusRadiusLogarithmSpectrum( + name="dvdlnr", radius_bins_edges=radius_bins_edges + ) + ) + + def save(self, step): + if step in self.output_steps: + super().save(step) + + def run(self): + result = super().run() + for key, val in result.products.items(): + if len(val.shape) == 2: + result.products[key] = val[:, self.output_steps] + elif len(val.shape) == 3: + result.products[key] = val[:, :, :] + result.products["t"] = result.products["t"][self.output_steps] + return result diff --git a/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/simulation_0D.py b/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/simulation_0D.py new file mode 100644 index 0000000000000000000000000000000000000000..8de4f6df967d67f7e5d8db34564a4b75f63f0ad0 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/simulation_0D.py @@ -0,0 +1,134 @@ +from collections import namedtuple + +import numpy as np + +from PySDM.backends import CPU +from PySDM.builder import Builder +from PySDM.dynamics import Coalescence, Collision +from PySDM.environments import Box +from PySDM.formulae import Formulae +from PySDM.initialisation.sampling.spectral_sampling import ( + ConstantMultiplicity, + Logarithmic, +) +from PySDM.physics import si +from PySDM.products.collision.collision_rates import ( + BreakupRatePerGridbox, + CoalescenceRatePerGridbox, + CollisionRateDeficitPerGridbox, + CollisionRatePerGridbox, +) +from PySDM.products.size_spectral import ( + NumberSizeSpectrum, + ParticleVolumeVersusRadiusLogarithmSpectrum, +) + + +def run_box_breakup( + settings, steps=None, backend_class=CPU, sample_in_radius=False, return_nv=False +): + builder = Builder( + n_sd=settings.n_sd, + backend=backend_class(settings.formulae), + environment=Box(dv=settings.dv, dt=settings.dt), + ) + builder.particulator.environment["rhod"] = 1.0 + attributes = {} + if sample_in_radius: + diams, attributes["multiplicity"] = Logarithmic( + settings.spectrum + ).sample_deterministic(settings.n_sd) + radii = diams / 2 + attributes["volume"] = Formulae().trivia.volume(radius=radii) + else: + attributes["volume"], attributes["multiplicity"] = ConstantMultiplicity( + settings.spectrum + ).sample_deterministic(settings.n_sd) + breakup = Collision( + collision_kernel=settings.kernel, + coalescence_efficiency=settings.coal_eff, + breakup_efficiency=settings.break_eff, + fragmentation_function=settings.fragmentation, + adaptive=settings.adaptive, + warn_overflows=settings.warn_overflows, + ) + builder.add_dynamic(breakup) + products = ( + ParticleVolumeVersusRadiusLogarithmSpectrum( + radius_bins_edges=settings.radius_bins_edges, name="dv/dlnr" + ), + NumberSizeSpectrum(radius_bins_edges=settings.radius_bins_edges, name="N(v)"), + CollisionRatePerGridbox(name="cr"), + CollisionRateDeficitPerGridbox(name="crd"), + CoalescenceRatePerGridbox(name="cor"), + BreakupRatePerGridbox(name="br"), + ) + core = builder.build(attributes, products) + + if steps is None: + steps = settings.output_steps + y = np.ndarray((len(steps), len(settings.radius_bins_edges) - 1)) + if return_nv: + y2 = np.ndarray((len(steps), len(settings.radius_bins_edges) - 1)) + else: + y2 = None + + rates = np.zeros((len(steps), 4)) + # run + for i, step in enumerate(steps): + core.run(step - core.n_steps) + y[i] = core.products["dv/dlnr"].get()[0] + if return_nv: + (y2[i],) = core.products["N(v)"].get() + (rates[i, 0],) = core.products["cr"].get() + (rates[i, 1],) = core.products["crd"].get() + (rates[i, 2],) = core.products["cor"].get() + (rates[i, 3],) = core.products["br"].get() + + x = (settings.radius_bins_edges[:-1] / si.micrometres,)[0] + + return namedtuple("_", ("x", "y", "y2", "rates"))(x=x, y=y, y2=y2, rates=rates) + + +def run_box_NObreakup(settings, steps=None, backend_class=CPU): + builder = Builder( + n_sd=settings.n_sd, + backend=backend_class(settings.formulae), + environment=Box(dv=settings.dv, dt=settings.dt), + ) + builder.particulator.environment["rhod"] = 1.0 + attributes = {} + attributes["volume"], attributes["multiplicity"] = ConstantMultiplicity( + settings.spectrum + ).sample_deterministic(settings.n_sd) + coal = Coalescence( + collision_kernel=settings.kernel, + coalescence_efficiency=settings.coal_eff, + adaptive=settings.adaptive, + ) + builder.add_dynamic(coal) + products = ( + ParticleVolumeVersusRadiusLogarithmSpectrum( + radius_bins_edges=settings.radius_bins_edges, name="dv/dlnr" + ), + CollisionRatePerGridbox(name="cr"), + CollisionRateDeficitPerGridbox(name="crd"), + CoalescenceRatePerGridbox(name="cor"), + ) + core = builder.build(attributes, products) + + if steps is None: + steps = settings.output_steps + y = np.ndarray((len(steps), len(settings.radius_bins_edges) - 1)) + rates = np.zeros((len(steps), 4)) + # run + for i, step in enumerate(steps): + core.run(step - core.n_steps) + (y[i],) = core.products["dv/dlnr"].get() + (rates[i, 0],) = core.products["cr"].get() + (rates[i, 1],) = core.products["crd"].get() + (rates[i, 2],) = core.products["cor"].get() + + x = (settings.radius_bins_edges[:-1] / si.micrometres,)[0] + + return (x, y, rates) diff --git a/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/simulation_ss.py b/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/simulation_ss.py new file mode 100644 index 0000000000000000000000000000000000000000..78d1054dce609211103bbe6698a03ddcc939ddd8 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/deJong_Mackay_et_al_2023/simulation_ss.py @@ -0,0 +1,190 @@ +import pickle as pkl + +import numpy as np +from PySDM_examples.deJong_Mackay_et_al_2023.settings_0D import Settings0D +from PySDM_examples.deJong_Mackay_et_al_2023.simulation_0D import run_box_breakup + +from PySDM.dynamics.collisions.breakup_fragmentations import Straub2010Nf +from PySDM.dynamics.collisions.coalescence_efficiencies import Straub2010Ec +from PySDM.initialisation.spectra import Exponential +from PySDM.physics import si + + +def run_to_steady_state(parameterization, n_sd, steps, nruns=1, dt=1 * si.s): + rain_rate = 54 * si.mm / si.h + mp_scale = 4.1e3 * (rain_rate / si.mm * si.h) ** (-0.21) / si.m + mp_N0 = 8e6 / si.m**4 + n_part = mp_N0 / mp_scale + + nbins = 64 + + y_ensemble = np.zeros((nruns, len(steps), nbins - 1)) + y2_ensemble = np.zeros((nruns, len(steps), nbins - 1)) + irun = 0 + + while irun < nruns: + if parameterization == "Straub2010": + settings = Settings0D( + seed=7 ** (irun + 1), + fragmentation=Straub2010Nf( + vmin=(0.01 * si.mm) ** 3 * np.pi / 6, + nfmax=10000, + ), + ) + settings.coal_eff = Straub2010Ec() + else: + print("parameterization not recognized") + return + + settings.dv = 1e6 * si.m**3 + settings.dt = dt + settings.spectrum = Exponential( + norm_factor=n_part * settings.dv, scale=1 / mp_scale + ) + settings.n_sd = n_sd + settings.radius_bins_edges = np.linspace( + 1e-3 * si.mm, 4 * si.mm, num=nbins, endpoint=True + ) + + settings.warn_overflows = False + settings._steps = steps # pylint: disable=protected-access + try: + res = run_box_breakup(settings, sample_in_radius=True, return_nv=True) + x, y, y2, rates = res.x, res.y, res.y2, res.rates + y_ensemble[irun] = y + y2_ensemble[irun] = y2 + print("Success with run #" + str(irun + 1)) + irun += 1 + except: # pylint: disable=bare-except + if dt > 0.5 * si.s: + print( + "Error in steady state sim for " + + str(n_sd) + + " superdroplets, moving on with dt=" + + str(dt / 2) + ) + dt = dt / 2 + else: + print( + "Error in steady state sim for " + + str(n_sd) + + " superdroplets, proceeding to next iteration" + ) + rates = np.nan + x = (settings.radius_bins_edges[:-1] / si.micrometres,)[0] + y_ensemble[irun] = np.ones((len(steps), nbins - 1)) * np.nan + irun += 1 + dt = 1 * si.s + + data_filename = "steadystate_" + parameterization + "_" + str(n_sd) + "sd.pkl" + + with open(data_filename, "wb") as handle: + pkl.dump( + (x, y_ensemble, y2_ensemble, rates), handle, protocol=pkl.HIGHEST_PROTOCOL + ) + + +def get_straub_fig10_init(): + rain_rate = 54 * si.mm / si.h + mp_scale = 4.1e3 * (rain_rate / si.mm * si.h) ** (-0.21) / si.m + mp_N0 = 8e6 / si.m**4 + + straub_x_init = np.linspace(1e-3, 4.0, 100) * si.mm + straub_y_init = mp_N0 * np.exp(-1.0 * mp_scale * (straub_x_init)) * si.mm + + dx = np.concatenate( + [np.diff(straub_x_init), [straub_x_init[-1] - straub_x_init[-2]]] + ) + x_c = straub_x_init + dx / 2 + m_c = np.pi / 6 * x_c**3 + straub_dvdlnr_init = m_c * straub_y_init / si.mm * x_c + + return (straub_x_init, straub_y_init, straub_dvdlnr_init) + + +def get_straub_fig10_data(): + graph_x = ( + np.array( + [ + 0.08988764, + 0.086142322, + 0.097378277, + 0.108614232, + 0.119850187, + 0.142322097, + 0.164794007, + 0.194756554, + 0.224719101, + 0.262172285, + 0.314606742, + 0.36329588, + 0.419475655, + 0.479400749, + 0.558052434, + 0.68164794, + 0.816479401, + 0.943820225, + 1.071161049, + 1.213483146, + 1.370786517, + 1.617977528, + 1.865168539, + 2.074906367, + 2.322097378, + 2.546816479, + 2.801498127, + 3.018726592, + 3.220973783, + 3.378277154, + 3.543071161, + 3.651685393, + ] + ) + * si.mm + ) + graph_log_y = np.array( + [ + 1.055803251, + 1.003199334, + 1.14357294, + 1.316140369, + 1.509917836, + 1.811447329, + 2.159352957, + 2.607176908, + 3.048912888, + 3.453402358, + 3.849578422, + 3.955529874, + 3.849578422, + 3.634485506, + 3.328848104, + 2.897005075, + 2.525213782, + 2.294463222, + 2.125139584, + 2.045222871, + 2.02571776, + 2.032198707, + 2, + 1.930947254, + 1.811447329, + 1.685826681, + 1.531778159, + 1.389585281, + 1.262606891, + 1.169430766, + 1.069379699, + 1.0064089029687935, + ] + ) + + dx = np.concatenate([np.diff(graph_x), [graph_x[-1] - graph_x[-2]]]) + lnr = np.log(graph_x / 2) + dlnr = np.concatenate([np.diff(lnr), [lnr[-1] - lnr[-2]]]) + x_c = graph_x + dx / 2 + m_c = np.pi / 6 * x_c**3 + n = np.power(10.0, graph_log_y) * dx + straub_dvdlnr_ss = n * m_c / dlnr + + return (graph_x, graph_log_y, straub_dvdlnr_ss) diff --git a/PySDM/source/examples/PySDM_examples/seeding/__init__.py b/PySDM/source/examples/PySDM_examples/seeding/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3709bdac70effed8aa5315a6713660d3fe203683 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/seeding/__init__.py @@ -0,0 +1,10 @@ +""" +hello_world.ipynb: +.. include:: ./hello_world.ipynb.badges.md + +seeding_no_collisions.ipynb: +.. include:: ./seeding_no_collisions.ipynb.badges.md +""" + +from .settings import Settings +from .simulation import Simulation diff --git a/PySDM/source/examples/PySDM_examples/seeding/hello_world.ipynb b/PySDM/source/examples/PySDM_examples/seeding/hello_world.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..e4e80669a7f9f416702a1cb023f921547d34d62d --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/seeding/hello_world.ipynb @@ -0,0 +1,27253 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "b762c49560c8d6cf", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/seeding/hello_world.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/seeding/hello_world.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/seeding/hello_world.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "45491a3c0a12c30a", + "metadata": {}, + "source": [ + "TODO #1417" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "70162914d9301075", + "metadata": {}, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "b1c0a002-7f76-4a4c-9e77-6aae747d7fab", + "metadata": { + "ExecuteTime": { + "end_time": "2024-08-20T22:17:43.753049Z", + "start_time": "2024-08-20T22:17:40.315223Z" + } + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "from matplotlib import pyplot\n", + "from PySDM import Formulae\n", + "from PySDM.physics import in_unit, si\n", + "from open_atmos_jupyter_utils import show_plot\n", + "from PySDM_examples.seeding import Settings, Simulation" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "1695dd83-b78b-46f3-a50c-47cc601c5920", + "metadata": { + "ExecuteTime": { + "end_time": "2024-08-20T22:18:08.299964Z", + "start_time": "2024-08-20T22:17:43.765032Z" + } + }, + "outputs": [], + "source": [ + "n_sd_initial = 100\n", + "n_sd_seeding = 100\n", + "rain_water_radius_threshold = .1 * si.mm\n", + "formulae = Formulae(seed=100)\n", + "\n", + "simulations = {\n", + " case: Simulation(\n", + " Settings(\n", + " n_sd_initial=n_sd_initial,\n", + " n_sd_seeding=n_sd_seeding,\n", + " rain_water_radius_threshold=rain_water_radius_threshold,\n", + " super_droplet_injection_rate={\n", + " 'seeding': lambda time: 1 if 5 * si.min < time < 10 * si.min else 0,\n", + " 'no seeding': lambda _: 0,\n", + " }[case],\n", + " formulae=formulae,\n", + " )\n", + " )\n", + " for case in ('seeding', 'no seeding')\n", + "} " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "af543973-a177-4fe9-9ae5-b0fb8511323e", + "metadata": { + "ExecuteTime": { + "end_time": "2024-08-20T22:19:04.834137Z", + "start_time": "2024-08-20T22:18:08.367114Z" + } + }, + "outputs": [], + "source": [ + "outputs = {case: simulations[case].run() for case in simulations}" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "45bbaff8-d6ea-4685-a10b-2c4af167b89a", + "metadata": { + "ExecuteTime": { + "end_time": "2024-08-20T22:19:14.817471Z", + "start_time": "2024-08-20T22:19:04.867861Z" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-12-09T12:58:04.982652\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.9.3, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "9a0096930f414d7e98627bf42da1b673", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./hello_world_seeding.pdf\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-12-09T12:58:11.781299\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.9.3, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "34b4c0c71c8b4094b93e780d2a928b35", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./hello_world_no_seeding.pdf…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "for case, output in outputs.items():\n", + " time = output['products']['time']\n", + " water_mass = output['attributes']['water mass']\n", + " \n", + " fig, axs = pyplot.subplot_mosaic(\n", + " [['a', 'b', 'c']],\n", + " sharey=True,\n", + " figsize=(12, 4),\n", + " tight_layout=True\n", + " )\n", + " \n", + " for drop_id in range(water_mass.shape[1]):\n", + " axs['a'].plot(\n", + " in_unit(water_mass[:, drop_id], si.ng),\n", + " in_unit(time, si.min),\n", + " color=\"navy\" if np.isfinite(water_mass[0, drop_id]) else \"red\",\n", + " linewidth=0.333,\n", + " )\n", + " axs['a'].set_ylabel(\"time [min]\")\n", + " axs['a'].set_xlabel(\"drop mass [ng]\")\n", + " axs['a'].grid()\n", + " axs['a'].set_xscale(\"log\")\n", + " axs['a'].set_xlim(1e-6, 1e8)\n", + "\n", + " axs['b'].plot(\n", + " output['products']['sd_count'],\n", + " in_unit(time, si.min),\n", + " marker='.',\n", + " color='green',\n", + " )\n", + " axs['b'].set_xlabel(\"super droplet count\")\n", + " axs['b'].grid()\n", + " axs['b'].set_xlim(95, 125)\n", + "\n", + " axs['c'].plot(\n", + " in_unit(output['products']['rain water mixing ratio'], si.g/si.kg),\n", + " in_unit(time, si.min),\n", + " marker='.',\n", + " color='green', \n", + " )\n", + " axs['c'].set_xlabel(f\"rain water mixing ratio [g/kg] (radius > {in_unit(rain_water_radius_threshold, si.mm)} mm)\")\n", + " axs['c'].grid()\n", + " axs['c'].set_xlim(0, 3)\n", + "\n", + " fig.suptitle(case)\n", + " show_plot(f\"hello_world_{case.replace(' ', '_')}.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a4742eaf-dccb-48b7-9138-74ae5c579cd8", + "metadata": { + "ExecuteTime": { + "end_time": "2024-08-20T22:19:14.881814Z", + "start_time": "2024-08-20T22:19:14.872933Z" + } + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.14" + }, + "vscode": { + "interpreter": { + "hash": "b14f34a08619f4a218d80d7380beed3f0c712c89ff93e7183219752d640ed427" + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/PySDM/source/examples/PySDM_examples/seeding/seeding_no_collisions.ipynb b/PySDM/source/examples/PySDM_examples/seeding/seeding_no_collisions.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..6a882457c16c195cac4b0d16998a763cb4b15b13 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/seeding/seeding_no_collisions.ipynb @@ -0,0 +1,2522 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/seeding/seeding_no_collisions.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/seeding/seeding_no_collisions.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/seeding/seeding_no_collisions.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "TODO #1417" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from matplotlib import pyplot\n", + "from PySDM import Formulae\n", + "from PySDM.physics import in_unit, si\n", + "from open_atmos_jupyter_utils import show_plot\n", + "from PySDM_examples.seeding import Settings, Simulation" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "n_sd_initial = 100\n", + "n_sd_seeding = 100\n", + "formulae = Formulae(seed=100)\n", + "\n", + "simulations = {\n", + " case: Simulation(\n", + " Settings(\n", + " n_sd_initial=n_sd_initial,\n", + " n_sd_seeding=n_sd_seeding,\n", + " rain_water_radius_threshold=0 * si.mm,\n", + " super_droplet_injection_rate={\n", + " 'seeding': lambda time: 1 if 15 * si.min < time < 20 * si.min else 0,\n", + " 'no seeding': lambda _: 0,\n", + " }[case],\n", + " formulae=formulae,\n", + " enable_collisions=False,\n", + " )\n", + " )\n", + " for case in ('seeding', 'no seeding')\n", + "} " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "outputs = {case: simulations[case].run() for case in simulations}" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-12-09T12:57:14.320047\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.9.3, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "e88a10c0775749ef8dd99a174df2878c", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(HTML(value=\"./seeding_no_collisions.pdf 10 * si.min)[0]\n", + "\n", + " axs['a'].plot(\n", + " output['products']['sd_count'][idx],\n", + " in_unit(time[idx], si.min),\n", + " marker='.',\n", + " label=case,\n", + " )\n", + " axs['a'].set_xlabel(\"super droplet count\")\n", + " axs['a'].set_ylabel(\"height [m]\")\n", + " axs['a'].axhspan(15, 20, color=\"grey\", alpha=0.2)\n", + "\n", + " axs['b'].plot(\n", + " output['products']['r_eff'][idx],\n", + " in_unit(time[idx], si.min),\n", + " marker='.',\n", + " )\n", + " axs['b'].set_xlabel(\"r_eff [um]\")\n", + " axs['b'].axhspan(15, 20, color=\"grey\", alpha=0.2)\n", + "\n", + " axs['c'].plot(\n", + " output['products']['n_drop'][idx],\n", + " in_unit(time[idx], si.min),\n", + " marker='.',\n", + " )\n", + " axs['c'].set_xlabel(\"n_drop [cm-3]\")\n", + " axs['c'].axhspan(15, 20, color=\"grey\", alpha=0.2)\n", + "\n", + "axs['a'].legend()\n", + "fig.suptitle(\"parcel with no collisions\")\n", + "show_plot(\"seeding_no_collisions.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.14" + }, + "vscode": { + "interpreter": { + "hash": "b14f34a08619f4a218d80d7380beed3f0c712c89ff93e7183219752d640ed427" + } + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/PySDM/source/examples/PySDM_examples/seeding/settings.py b/PySDM/source/examples/PySDM_examples/seeding/settings.py new file mode 100644 index 0000000000000000000000000000000000000000..6bdaaf6aa157516e362b9a54be360788d6795233 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/seeding/settings.py @@ -0,0 +1,65 @@ +import numpy as np +from pystrict import strict +from PySDM.initialisation.spectra import Lognormal +from PySDM.initialisation.sampling.spectral_sampling import ConstantMultiplicity +from PySDM.physics import si +from PySDM import Formulae + + +@strict +class Settings: + def __init__( + self, + *, + super_droplet_injection_rate: callable, + n_sd_initial: int, + n_sd_seeding: int, + rain_water_radius_threshold: float, + formulae: Formulae, + enable_collisions: bool = True, + ): + self.enable_collisions = enable_collisions + self.formulae = formulae + self.n_sd_initial = n_sd_initial + self.n_sd_seeding = n_sd_seeding + self.rain_water_radius_threshold = rain_water_radius_threshold + + self.t_max = 25 * si.min + self.w_max = 3 * si.m / si.s + self.w_min = 0.025 * si.m / si.s + + self.timestep = 15 * si.s + self.mass_of_dry_air = 666 * si.kg + + self.updraft = ( + lambda t: self.w_min + + (self.w_max - self.w_min) + * np.maximum(0, np.sin(t / self.t_max * 2 * np.pi)) ** 2 + ) + self.initial_water_vapour_mixing_ratio = 666 / 30 * si.g / si.kg + self.initial_total_pressure = 1000 * si.hPa + self.initial_temperature = 300 * si.K + self.initial_aerosol_kappa = 0.5 + self.initial_aerosol_dry_radii = Lognormal( + norm_factor=200 / si.mg * self.mass_of_dry_air, + m_mode=75 * si.nm, + s_geom=1.6, + ) + self.super_droplet_injection_rate = super_droplet_injection_rate + + r_dry, n_in_dv = ConstantMultiplicity( + Lognormal( + norm_factor=10 / si.mg * self.mass_of_dry_air, + m_mode=1 * si.um, + s_geom=1.1, + ) + ).sample_deterministic( + n_sd=n_sd_seeding + ) # TODO #1387: does not to be the same? + v_dry = self.formulae.trivia.volume(radius=r_dry) + self.seeded_particle_multiplicity = n_in_dv + self.seeded_particle_extensive_attributes = { + "signed water mass": [0.0001 * si.ng] * n_sd_seeding, + "dry volume": v_dry, + "kappa times dry volume": 0.8 * v_dry, + } diff --git a/PySDM/source/examples/PySDM_examples/seeding/simulation.py b/PySDM/source/examples/PySDM_examples/seeding/simulation.py new file mode 100644 index 0000000000000000000000000000000000000000..a4c120c83bb172ba9aed726e35833cac6cd35cb1 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/seeding/simulation.py @@ -0,0 +1,102 @@ +import numpy as np + +from PySDM_examples.seeding.settings import Settings + +from PySDM import Builder +from PySDM.backends import CPU +from PySDM.environments import Parcel +from PySDM.dynamics import Condensation, AmbientThermodynamics, Coalescence, Seeding +from PySDM.dynamics.collisions.collision_kernels import Geometric +from PySDM.initialisation.sampling.spectral_sampling import ConstantMultiplicity +from PySDM import products +from PySDM.physics import si + + +class Simulation: + def __init__(self, settings: Settings): + builder = Builder( + n_sd=settings.n_sd_seeding + settings.n_sd_initial, + backend=CPU( + formulae=settings.formulae, override_jit_flags={"parallel": False} + ), + environment=Parcel( + dt=settings.timestep, + mass_of_dry_air=settings.mass_of_dry_air, + w=settings.updraft, + initial_water_vapour_mixing_ratio=settings.initial_water_vapour_mixing_ratio, + p0=settings.initial_total_pressure, + T0=settings.initial_temperature, + ), + ) + builder.add_dynamic(AmbientThermodynamics()) + builder.add_dynamic(Condensation()) + if settings.enable_collisions: + builder.add_dynamic(Coalescence(collision_kernel=Geometric())) + builder.add_dynamic( + Seeding( + super_droplet_injection_rate=settings.super_droplet_injection_rate, + seeded_particle_multiplicity=settings.seeded_particle_multiplicity, + seeded_particle_extensive_attributes=settings.seeded_particle_extensive_attributes, + ) + ) + + r_dry, n_in_dv = ConstantMultiplicity( + settings.initial_aerosol_dry_radii + ).sample_deterministic( + n_sd=settings.n_sd_initial, backend=builder.particulator.backend + ) + attributes = builder.particulator.environment.init_attributes( + n_in_dv=n_in_dv, kappa=settings.initial_aerosol_kappa, r_dry=r_dry + ) + self.particulator = builder.build( + attributes={ + k: np.pad( + array=v, + pad_width=(0, settings.n_sd_seeding), + mode="constant", + constant_values=np.nan if k == "multiplicity" else 0, + ) + for k, v in attributes.items() + }, + products=( + products.SuperDropletCountPerGridbox(name="sd_count"), + products.Time(), + products.WaterMixingRatio( + radius_range=(settings.rain_water_radius_threshold, np.inf), + name="rain water mixing ratio", + ), + products.EffectiveRadius( + name="r_eff", + unit="um", + radius_range=(0.5 * si.um, 25 * si.um), + ), + products.ParticleConcentration( + name="n_drop", + unit="cm^-3", + radius_range=(0.5 * si.um, 25 * si.um), + ), + ), + ) + self.n_steps = int(settings.t_max // settings.timestep) + + def run(self): + output = { + "attributes": {"water mass": []}, + "products": {key: [] for key in self.particulator.products}, + } + for step in range(self.n_steps + 1): + if step != 0: + self.particulator.run(steps=1) + for key, attr in output["attributes"].items(): + data = self.particulator.attributes[key].to_ndarray(raw=True) + data[data == 0] = np.nan + attr.append(data) + for key, prod in output["products"].items(): + value = self.particulator.products[key].get() + if not isinstance(value, float): + (value,) = value + prod.append(float(value)) + for out in ("attributes", "products"): + for key, val in output[out].items(): + output[out][key] = np.array(val) + return output diff --git a/PySDM/source/examples/PySDM_examples/utils/__init__.py b/PySDM/source/examples/PySDM_examples/utils/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..b66040d1bbfa3cd5a41be6c19437834e22b078b2 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/utils/__init__.py @@ -0,0 +1,8 @@ +""" +reusable commons for examples +""" + +from .basic_simulation import BasicSimulation +from .dummy_controller import DummyController +from .progbar_controller import ProgBarController +from .read_vtk_1d import readVTK_1d diff --git a/PySDM/source/examples/PySDM_examples/utils/basic_simulation.py b/PySDM/source/examples/PySDM_examples/utils/basic_simulation.py new file mode 100644 index 0000000000000000000000000000000000000000..a8acb2783d23a26b9588ef31d352f63e780e7a9c --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/utils/basic_simulation.py @@ -0,0 +1,21 @@ +import numpy as np + + +class BasicSimulation: + def __init__(self, particulator): + self.particulator = particulator + + def _save(self, output): + for k, v in self.particulator.products.items(): + value = v.get() + if isinstance(value, np.ndarray) and value.shape[0] == 1: + value = value[0] + output[k].append(value) + + def _run(self, nt, steps_per_output_interval): + output = {k: [] for k in self.particulator.products} + self._save(output) + for _ in range(0, nt + 1, steps_per_output_interval): + self.particulator.run(steps=steps_per_output_interval) + self._save(output) + return output diff --git a/PySDM/source/examples/PySDM_examples/utils/dummy_controller.py b/PySDM/source/examples/PySDM_examples/utils/dummy_controller.py new file mode 100644 index 0000000000000000000000000000000000000000..a65ced971d79f2227876d8e74cb51cdf6446705f --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/utils/dummy_controller.py @@ -0,0 +1,29 @@ +from contextlib import AbstractContextManager + +from PySDM.products import CPUTime, WallTime + + +class DummyController(AbstractContextManager): + def __init__(self): + self.panic = False + self.t_last = self.__times() + + @staticmethod + def __times(): + return WallTime.clock(), CPUTime.clock() + + def set_percent(self, value): + t_curr = self.__times() + wall_time = t_curr[0] - self.t_last[0] + cpu_time = t_curr[1] - self.t_last[1] + print( + f"{100 * value:.1f}%" + f" (times since last print: cpu={cpu_time:.1f}s wall={wall_time:.1f}s)" + ) + self.t_last = self.__times() + + def __enter__(self, *_): + pass + + def __exit__(self, *_): + pass diff --git a/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/__init__.py b/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..fc86a191993d2bf43fbae80ebf99e01216cf11d9 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/__init__.py @@ -0,0 +1,19 @@ +# pylint: disable=invalid-name +""" +The 2D prescribed-flow framework used here can be traced back to the work +of [Kessler 1969 (section 3C)](https://doi.org/10.1007/978-1-935704-36-2_1), can be found also in +[Szumowski et al. 1998 (Atmos. Res.)](https://doi.org/10.1016/S0169-8095(97)00082-3). + +The setup mimics a stratiform cloud deck and features periodic horizontal boundary condition +and vanishing flow at vertical boundaries. +It was introduced in [Morrison & Grabowski 2007](https://doi.org/10.1175/JAS3980) and later adopted +for particle-based simulations in [Arabas et al. 2015](https://doi.org/10.5194/gmd-8-1677-2015) +It uses a non-devergent single-eddy flow field resulting in an updraft-downdraft pair in the domain. +The flow field advects two scalar fields in an Eulerian way: water vapour mixing ratio +and dry-air potential temperature. +""" + +from .gui_settings import GUISettings +from .mpdata_2d import MPDATA_2D +from .simulation import Simulation +from .storage import Storage diff --git a/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/fields.py b/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/fields.py new file mode 100644 index 0000000000000000000000000000000000000000..1490832a2879b53f61a26551803eda4b61fc6a7c --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/fields.py @@ -0,0 +1,57 @@ +import numpy as np + +from PySDM.impl.arakawa_c import z_scalar_coord + + +def z_vec_coord(grid): + nx = grid[0] + nz = grid[1] + 1 + xX = ( + np.repeat(np.linspace(1 / 2, grid[0] - 1 / 2, nx).reshape((nx, 1)), nz, axis=1) + / grid[0] + ) + assert np.amin(xX) >= 0 + assert np.amax(xX) <= 1 + assert xX.shape == (nx, nz) + zZ = np.repeat(np.linspace(0, grid[1], nz).reshape((1, nz)), nx, axis=0) / grid[1] + assert np.amin(zZ) == 0 + assert np.amax(zZ) == 1 + assert zZ.shape == (nx, nz) + return xX, zZ + + +def x_vec_coord(grid): + nx = grid[0] + 1 + nz = grid[1] + xX = np.repeat(np.linspace(0, grid[0], nx).reshape((nx, 1)), nz, axis=1) / grid[0] + assert np.amin(xX) == 0 + assert np.amax(xX) == 1 + assert xX.shape == (nx, nz) + zZ = np.repeat(z_scalar_coord(grid).reshape((1, nz)), nx, axis=0) / grid[1] + assert np.amin(zZ) >= 0 + assert np.amax(zZ) <= 1 + assert zZ.shape == (nx, nz) + return xX, zZ + + +def nondivergent_vector_field_2d( + grid: tuple, size: tuple, dt: float, stream_function: callable, t +): + dx = size[0] / grid[0] + dz = size[1] / grid[1] + dxX = 1 / grid[0] + dzZ = 1 / grid[1] + + xX, zZ = x_vec_coord(grid) + rho_velocity_x = ( + -(stream_function(xX, zZ + dzZ / 2, t) - stream_function(xX, zZ - dzZ / 2, t)) + / dz + ) + + xX, zZ = z_vec_coord(grid) + rho_velocity_z = ( + stream_function(xX + dxX / 2, zZ, t) - stream_function(xX - dxX / 2, zZ, t) + ) / dx + + rho_times_courant = [rho_velocity_x * dt / dx, rho_velocity_z * dt / dz] + return rho_times_courant diff --git a/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/gui.py b/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/gui.py new file mode 100644 index 0000000000000000000000000000000000000000..67f5628a3d6e5b00b533da26ae10358e26420927 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/gui.py @@ -0,0 +1,45 @@ +import os +import sys + +from open_atmos_jupyter_utils import TemporaryFile +from PySDM_examples.utils.kinematic_2d.gui_controller import ( + GUIController, +) +from PySDM_examples.utils.kinematic_2d.gui_viewer import GUIViewer +from PySDM_examples.utils.widgets import HTML, Tab, VBox, display + +from PySDM.exporters import NetCDFExporter + + +def launch(settings, simulation, storage): + ncdf_file = TemporaryFile(".nc") + ncdf_exporter = NetCDFExporter( + storage, settings, simulation, ncdf_file.absolute_path + ) + + vtk_file = TemporaryFile(".zip") + + viewer = GUIViewer(storage, settings) + controller = GUIController(simulation, viewer, ncdf_exporter, ncdf_file, vtk_file) + + controller_box = controller.box() + + tabs = Tab([VBox([controller_box, viewer.box()]), settings.box()]) + tabs.set_title(1, "Settings") + tabs.set_title(0, "Simulation") + tabs.observe(controller.reinit, "selected_index") + + # https://github.com/googlecolab/colabtools/issues/1302 + hack = ( + " " + ) + if "google.colab" in sys.modules: + display(HTML(hack)) + + display(tabs) + + if "CI" in os.environ: + controller_box.children[1].click() diff --git a/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/gui_controller.py b/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/gui_controller.py new file mode 100644 index 0000000000000000000000000000000000000000..1f81877e54998aa665290b9a26e3dc934caa3944 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/gui_controller.py @@ -0,0 +1,132 @@ +import shutil +from tempfile import TemporaryDirectory +from threading import Thread +from time import sleep + +from PySDM_examples.utils.widgets import Button, Checkbox, FloatProgress, HBox + +from PySDM.exporters import VTKExporter + + +class GUIController: + def __init__(self, simulator, viewer, ncdf_exporter, ncdf_file, vtk_file): + self.progress = FloatProgress(value=0.0, min=0.0, max=1.0) + self.button = Button() + self.link = HBox() + self.checkbox = Checkbox( + description="VTK output", indent=False, layout={"width": "125px"} + ) + self.panic = False + self.thread = None + self.simulator = simulator + self.ncdf_exporter = ncdf_exporter + self.ncdf_file = ncdf_file + self.vtk_file = vtk_file + self.tempdir = None + self.vtk_exporter = None + self.viewer = viewer + self._setup_play() + + def __enter__(self): + self.panic = False + self.set_percent(0) + + def __exit__(self, *_): + if self.panic: + self._setup_play() + else: + self._setup_save() + self.panic = False + self.progress.description = " " + + def reinit(self, _=None): + self.panic = True + self._setup_play() + self.progress.value = 0 + self.progress.description = " " + self.viewer.clear() + self.link.children = () + self.vtk_exporter = None + + def box(self): + netcdf_box = Checkbox( + description="netCDF output", + disabled=True, + value=True, + indent=False, + layout={"width": "125px"}, + ) + return HBox([self.progress, self.button, netcdf_box, self.checkbox, self.link]) + + def set_percent(self, value): + self.progress.value = value + + def _setup_play(self): + self.button.on_click(self._handle_stop, remove=True) + self.button.on_click(self._handle_save, remove=True) + self.button.on_click(self._handle_play) + self.button.icon = "play" + self.button.description = "start simulation" + self.checkbox.disabled = False + + def _setup_stop(self): + self.button.on_click(self._handle_play, remove=True) + self.button.on_click(self._handle_save, remove=True) + self.button.on_click(self._handle_stop) + self.button.icon = "stop" + self.button.description = "interrupt" + + def _setup_save(self): + self.button.icon = "download" + self.button.description = "save output" + self.button.on_click(self._handle_stop, remove=True) + self.button.on_click(self._handle_save) + + def _handle_stop(self, _): + self.panic = True + while self.panic: + sleep(0.1) + self._setup_play() + + def _handle_play(self, _): + self._setup_stop() + + self.link.children = () + self.progress.value = 0 + self.progress.description = "initialisation" + self.checkbox.disabled = True + + if self.checkbox.value: + self.tempdir = TemporaryDirectory() + self.vtk_exporter = VTKExporter(verbose=False, path=self.tempdir.name) + + self.thread = Thread(target=self.simulator.run, args=(self, self.vtk_exporter)) + + self.simulator.reinit() + self.thread.start() + self.progress.description = "running" + self.viewer.reinit(self.simulator.products) + + def _handle_save(self, _): + def task(controller): + controller.progress.value = 0 + if self.checkbox.value: + self.vtk_exporter.write_pvd() + controller.progress.description = "VTK..." + shutil.make_archive( + self.vtk_file.absolute_path[:-4], "zip", self.vtk_exporter.path + ) + controller.progress.description = "netCDF..." + self.ncdf_exporter.run(controller) + controller.link.children = ( + (self.ncdf_file.make_link_widget(),) + if not self.checkbox.value + else ( + self.ncdf_file.make_link_widget(), + self.vtk_file.make_link_widget(), + ) + ) + + self.thread = Thread(target=task, args=(self,)) + self.thread.start() + self._setup_stop() diff --git a/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/gui_settings.py b/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/gui_settings.py new file mode 100644 index 0000000000000000000000000000000000000000..0951e6259c5c68cfc9e79c654bb2ad0db251b506 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/gui_settings.py @@ -0,0 +1,428 @@ +import inspect + +import numpy as np +from PySDM_examples.utils.widgets import ( + Accordion, + Checkbox, + Dropdown, + FloatSlider, + IntSlider, + Layout, + RadioButtons, + VBox, +) + +from PySDM import Formulae, formulae, physics +from PySDM.initialisation.spectra import Lognormal +from PySDM.physics import si + + +class GUISettings: + def __dir__(self): + return self.__settings.__dir__() + + def __init__(self, settings): + self.__settings = settings + + self.ui_dth0 = FloatSlider( + description="$\\Delta\\theta_0$ [K]", value=0, min=-15, max=15 + ) + self.ui_delta_initial_water_vapour_mixing_ratio = FloatSlider( + description="$\\Delta initial_water_vapour_mixing_ratio$ [g/kg]", + value=0, + min=-6, + max=6, + ) + self.ui_rhod_w_max = FloatSlider( + description="$\\rho_d w_{max}$", + value=settings.rhod_w_max, + min=0.1, + max=4, + step=0.1, + ) + self.ui_kappa = FloatSlider( + description="$\\kappa$ [1]", value=settings.kappa, min=0, max=1.5 + ) + + self.ui_freezing = { + "model": RadioButtons( + options=("singular", "time-dependent"), + description="immersion frz", + layout={"width": "max-content"}, + ), + "INP surface": RadioButtons( + options=("as dry surface", "lognormal(A, sgm_g)"), + description="INP surface", + ), + "lognormal_log10_A_um2": FloatSlider( + description="log10(A/$μm^2$)", min=-3, max=1, step=0.5 + ), + "lognormal_ln_sgm_g": FloatSlider( + description="ln(sgm_g)", min=0.5, max=3, step=0.5 + ), + "ABIFM fit": Dropdown(description="ABIFM fit", options=("Nonadecanol",)), + "INAS fit": Dropdown( + description="INAS fit", options=("Niemand et al. 2012",) + ), + } + # TODO #599 cool rate product + sing/tdep diff prod + + self.ui_nx = IntSlider( + value=settings.grid[0], min=10, max=100, description="nx" + ) + self.ui_nz = IntSlider( + value=settings.grid[1], min=10, max=100, description="nz" + ) + self.ui_dt = FloatSlider( + value=settings.dt, min=0.5, max=60, description="dt (Eulerian)" + ) + self.ui_simulation_time = IntSlider( + value=settings.simulation_time, + min=1800, + max=7200, + description="simulation time $[s]$", + ) + self.ui_condensation_rtol_x = IntSlider( + value=np.log10(settings.condensation_rtol_thd), + min=-9, + max=-3, + description="log$_{10}$(rtol$_x$)", + ) + self.ui_condensation_rtol_thd = IntSlider( + value=np.log10(settings.condensation_rtol_thd), + min=-9, + max=-3, + description="log$_{10}$(rtol$_\\theta$)", + ) + self.ui_condensation_adaptive = Checkbox( + value=settings.condensation_adaptive, + description="condensation adaptive time-step", + ) + self.ui_coalescence_adaptive = Checkbox( + value=settings.condensation_adaptive, + description="coalescence adaptive time-step", + ) + self.ui_displacement_rtol = IntSlider( + value=np.log10(settings.displacement_rtol), + min=-3, + max=0, + description="log$_{10}$(rtol)", + ) + self.ui_displacement_adaptive = Checkbox( + value=settings.displacement_adaptive, + description="displacement adaptive time-step", + ) + self.ui_processes = [ + Checkbox(value=settings.processes[key], description=key) + for key in settings.processes.keys() + ] + self.ui_sdpg = IntSlider( + value=settings.n_sd_per_gridbox, description="n_sd/gridbox", min=1, max=1000 + ) + self.fct_description = "MPDATA: flux-corrected transport option" + self.tot_description = "MPDATA: third-order terms option" + self.iga_description = "MPDATA: infinite gauge option" + self.nit_description = "MPDATA: number of iterations (1=UPWIND)" + self.ui_mpdata_options = [ + Checkbox(value=settings.mpdata_fct, description=self.fct_description), + Checkbox(value=settings.mpdata_tot, description=self.tot_description), + Checkbox(value=settings.mpdata_iga, description=self.iga_description), + IntSlider( + value=settings.mpdata_iters, + description=self.nit_description, + min=1, + max=5, + ), + ] + + formulae_init_params = inspect.signature(Formulae.__init__).parameters.items() + defaults = {k: v.default for k, v in formulae_init_params} + defaults["freezing_temperature_spectrum"] = "Niemand_et_al_2012" + defaults["heterogeneous_ice_nucleation_rate"] = "ABIFM" + self.ui_formulae_options = [ + Dropdown( + description=k, + options=formulae._choices(getattr(physics, k)).keys(), + value=defaults[k], + ) + for k, v in formulae_init_params + if k + not in ( + "self", + "fastmath", + "seed", + "constants", + "handle_all_breakups", + "terminal_velocity", + ) + ] + + self.ui_formulae_options.append( + Checkbox( + value=inspect.signature(Formulae.__init__) + .parameters["fastmath"] + .default, + description="fastmath", + ) + ) + self.ui_output_options = { + "interval": IntSlider( + description="interval [s]", + value=settings.output_interval, + min=30, + max=60 * 15, + ), + "aerosol_radius_threshold": FloatSlider( + description="aerosol/cloud threshold [um]", + value=settings.aerosol_radius_threshold / physics.si.um, + min=0.1, + max=1, + step=0.1, + ), + "drizzle_radius_threshold": IntSlider( + description="cloud/drizzle threshold [um]", + value=settings.drizzle_radius_threshold / physics.si.um, + min=10, + max=100, + step=5, + ), + } + + self.r_bins_edges = settings.r_bins_edges + self.T_bins_edges = settings.T_bins_edges + self.terminal_velocity_radius_bin_edges = ( + settings.terminal_velocity_radius_bin_edges + ) + + self.size = settings.size + self.condensation_substeps = settings.condensation_substeps + self.condensation_dt_cond_range = settings.condensation_dt_cond_range + self.condensation_schedule = settings.condensation_schedule + self.kernel = settings.kernel + self.spectrum_per_mass_of_dry_air = settings.spectrum_per_mass_of_dry_air + self.coalescence_dt_coal_range = settings.coalescence_dt_coal_range + self.coalescence_optimized_random = settings.coalescence_optimized_random + self.coalescence_substeps = settings.coalescence_substeps + self.freezing_inp_frac = settings.freezing_inp_frac + self.coalescence_efficiency = settings.coalescence_efficiency + self.breakup_efficiency = settings.breakup_efficiency + self.breakup_fragmentation = settings.breakup_fragmentation + + for attr in ("rhod_of_zZ", "versions", "n_spin_up"): + setattr(self, attr, getattr(settings, attr)) + + @property + def n_sd(self): + return self.grid[0] * self.grid[1] * self.n_sd_per_gridbox + + @property + def initial_vapour_mixing_ratio_profile(self): + return np.full( + self.grid[-1], + self.__settings.initial_water_vapour_mixing_ratio + + self.ui_delta_initial_water_vapour_mixing_ratio.value / 1000, + ) + + @property + def initial_dry_potential_temperature_profile(self): + return np.full( + self.grid[-1], + self.formulae.state_variable_triplet.th_dry( + self.__settings.th_std0 + self.ui_dth0.value, + self.__settings.initial_water_vapour_mixing_ratio + + self.ui_delta_initial_water_vapour_mixing_ratio.value / 1000, + ), + ) + + @property + def aerosol_radius_threshold(self): + return self.ui_output_options["aerosol_radius_threshold"].value * physics.si.um + + @property + def drizzle_radius_threshold(self): + return self.ui_output_options["drizzle_radius_threshold"].value * physics.si.um + + @property + def output_interval(self): + return self.ui_output_options["interval"].value + + @property + def formulae(self) -> Formulae: + return Formulae( + **{widget.description: widget.value for widget in self.ui_formulae_options}, + constants={"NIEMAND_A": 0, "NIEMAND_B": 0, "ABIFM_M": 0, "ABIFM_C": 0}, + ) + + @property + def steps_per_output_interval(self) -> int: + return int(self.output_interval / self.ui_dt.value) + + @property + def output_steps(self) -> np.ndarray: + return np.arange(0, self.n_steps + 1, self.steps_per_output_interval) + + @property + def rhod_w_max(self): + return self.ui_rhod_w_max.value + + @property + def kappa(self): + return self.ui_kappa.value + + @property + def freezing_singular(self): + return self.ui_freezing["model"].value == "singular" + + @property + def grid(self): + return self.ui_nx.value, self.ui_nz.value + + @property + def dt(self): + return self.ui_dt.value + + @property + def n_steps(self): + return int(self.ui_simulation_time.value / self.ui_dt.value) # TODO #413 + + @property + def condensation_rtol_x(self): + return 10**self.ui_condensation_rtol_x.value + + @property + def condensation_rtol_thd(self): + return 10**self.ui_condensation_rtol_thd.value + + @property + def condensation_adaptive(self): + return self.ui_condensation_adaptive.value + + @property + def coalescence_adaptive(self): + return self.ui_coalescence_adaptive.value + + @property + def displacement_rtol(self): + return 10**self.ui_displacement_rtol.value + + @property + def displacement_adaptive(self): + return self.ui_displacement_adaptive.value + + @property + def processes(self): + result = {} + for checkbox in self.ui_processes: + result[checkbox.description] = checkbox.value + return result + + @property + def n_sd_per_gridbox(self): + return self.ui_sdpg.value + + @property + def mpdata_tot(self): + for widget in self.ui_mpdata_options: + if widget.description == self.tot_description: + return widget.value + raise NotImplementedError() + + @property + def mpdata_fct(self): + for widget in self.ui_mpdata_options: + if widget.description == self.fct_description: + return widget.value + raise NotImplementedError() + + @property + def mpdata_iga(self): + for widget in self.ui_mpdata_options: + if widget.description == self.iga_description: + return widget.value + raise NotImplementedError() + + @property + def mpdata_iters(self): + for widget in self.ui_mpdata_options: + if widget.description == self.nit_description: + return widget.value + raise NotImplementedError() + + @property + def stream_function(self): + assert hasattr(self.__settings, "rhod_w_max") + self.__settings.rhod_w_max = self.ui_rhod_w_max.value + + def fun(xX, zZ, _): + return self.__settings.stream_function(xX, zZ, _) + + return fun + + @property + def freezing_inp_spec(self): + if self.ui_freezing["INP surface"].value == "as dry surface": + return None + if self.ui_freezing["INP surface"].value == "lognormal(A, sgm_g)": + return Lognormal( + norm_factor=1, + m_mode=10 ** (self.ui_freezing["lognormal_log10_A_um2"].value) + * si.um**2, + s_geom=np.exp(self.ui_freezing["lognormal_ln_sgm_g"].value), + ) + raise NotImplementedError() + + def box(self): + layout = Accordion( + children=[ + VBox( + [ + self.ui_dth0, + self.ui_delta_initial_water_vapour_mixing_ratio, + self.ui_kappa, + self.ui_rhod_w_max, + *self.ui_freezing.values(), + ] + ), + VBox([*self.ui_processes]), + VBox( + [ + self.ui_nx, + self.ui_nz, + self.ui_sdpg, + self.ui_dt, + self.ui_simulation_time, + self.ui_condensation_rtol_x, + self.ui_condensation_rtol_thd, + self.ui_condensation_adaptive, + self.ui_coalescence_adaptive, + self.ui_displacement_rtol, + self.ui_displacement_adaptive, + *self.ui_mpdata_options, + ] + ), + VBox([*self.ui_formulae_options]), + VBox([*self.ui_output_options.values()]), + ] + ) + layout.set_title(0, "parameters") + layout.set_title(1, "processes") + layout.set_title(2, "discretisation") + layout.set_title(3, "formulae") + layout.set_title(4, "output") + + layout.observe(self.hide_and_show, names="selected_index") + self.hide_and_show() + + return layout + + def hide_and_show(self, _=None): + freezing_enabled = self.processes["freezing"] + for widget in self.ui_freezing.values(): + set_visibility(widget, freezing_enabled) + + +def set_visibility(widget, visible): + if visible: + widget.layout = Layout() + else: + widget.layout = Layout(visibility="hidden") diff --git a/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/gui_viewer.py b/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/gui_viewer.py new file mode 100644 index 0000000000000000000000000000000000000000..08c7b246f4a95f440f1625a70b59931096b85a59 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/gui_viewer.py @@ -0,0 +1,355 @@ +import warnings + +import numpy as np +from matplotlib import pyplot, rcParams +from open_atmos_jupyter_utils import save_and_make_link +from PySDM_examples.utils.kinematic_2d.plots import ( + _ImagePlot, + _SpectrumPlot, + _TemperaturePlot, + _TerminalVelocityPlot, + _TimeseriesPlot, +) +from PySDM_examples.utils.widgets import ( + Box, + Button, + Dropdown, + HBox, + IntRangeSlider, + IntSlider, + Layout, + Output, + Play, + VBox, + clear_output, + display, + jslink, +) + +from PySDM.physics import constants as const + + +class GUIViewer: + def __init__(self, storage, settings): + self.storage = storage + self.settings = settings + + self.play = Play(interval=1000) + self.step_slider = IntSlider(continuous_update=False, description="t/dt_out:") + self.product_select = Dropdown() + self.spectrum_select = Dropdown() + self.plots_box = Box() + + self.timeseriesOutput = None + self.timeseriesPlot = None + self.plot_box = None + self.spectrum_box = None + self.outputs = None + self.plots = None + self.spectrumOutputs = None + self.spectrumPlots = None + self.products = None + + self.slider = {} + self.lines = {"X": [{}, {}], "Z": [{}, {}]} + for xz in ("X", "Z"): + self.slider[xz] = IntRangeSlider( + min=0, + max=1, + description=f"{xz}", + continuous_update=False, + orientation="horizontal" if xz == "X" else "vertical", + ) + + self.reinit({}) + + def clear(self): + self.plots_box.children = () + + def reinit(self, products): + self.products = products + self.product_select.options = tuple( + (f"{val.name} [{val.unit}]", key) + for key, val in sorted(self.products.items(), key=lambda item: item[1].name) + if len(val.shape) == 2 + ) + opts = [ + ("particle size spectra", "size"), + ("terminal velocity", "terminal velocity"), + ] + if "freezable specific concentration" in products: + opts.append(("freezing temperature spectra", "temperature")) + if "immersed surface area" in products: + pass # TODO #599 + self.spectrum_select.options = tuple(opts) + + r_bins = self.settings.r_bins_edges.copy() + const.convert_to(r_bins, const.si.micrometres) + self.spectrumOutputs = {} + self.spectrumPlots = {} + for key in ("size", "terminal velocity", "temperature"): + self.spectrumOutputs[key] = Output() + with self.spectrumOutputs[key]: + self.spectrumPlots[key] = ( + _SpectrumPlot(r_bins, self.settings.spectrum_per_mass_of_dry_air) + if key == "size" + else ( + _TemperaturePlot( + self.settings.T_bins_edges, self.settings.formulae + ) + if key == "temperature" + else _TerminalVelocityPlot( + self.settings.terminal_velocity_radius_bin_edges, + self.settings.formulae, + ) + ) + ) + clear_output() + + self.timeseriesOutput = Output() + with self.timeseriesOutput: + default_figsize = rcParams["figure.figsize"] + fig_kw = {"figsize": (2.5 * default_figsize[0], default_figsize[1] / 2)} + fig, ax = pyplot.subplots(1, 1, **fig_kw) + self.timeseriesPlot = _TimeseriesPlot( + fig, ax, self.settings.output_steps * self.settings.dt + ) + clear_output() + + self.plots = {} + self.outputs = {} + for key, product in products.items(): + if len(product.shape) == 2: + self.outputs[key] = Output() + with self.outputs[key]: + fig, ax = pyplot.subplots(1, 1) + self.plots[key] = _ImagePlot( + fig, + ax, + self.settings.grid, + self.settings.size, + product, + show=True, + lines=True, + ) + clear_output() + + self.plot_box = Box() + self.spectrum_box = Box() + if len(products.keys()) > 0: + layout_flex_end = Layout(display="flex", justify_content="flex-end") + save_map = Button(icon="save") + save_map.on_click(self.handle_save_map) + save_spe = Button(icon="save") + save_spe.on_click(self.handle_save_spe) + self.plots_box.children = ( + VBox( + children=( + HBox( + children=( + VBox( + children=( + Box( + layout=layout_flex_end, + children=(save_map, self.product_select), + ), + HBox((self.slider["Z"], self.plot_box)), + HBox( + (self.slider["X"],), layout=layout_flex_end + ), + ) + ), + VBox( + layout=Layout(), + children=( + Box( + children=(save_spe, self.spectrum_select), + layout=layout_flex_end, + ), + self.spectrum_box, + ), + ), + ) + ), + HBox((self.timeseriesOutput,)), + ) + ), + ) + + for widget in (self.step_slider, self.play): + widget.value = 0 + widget.max = len(self.settings.output_steps) - 1 + + for j, xz in enumerate(("X", "Z")): + slider = self.slider[xz] + mx = self.settings.grid[j] + slider.max = mx + slider.value = (0, mx) + + self.replot() + + def handle_save_map(self, _): + display(save_and_make_link(self.plots[self.product_select.value].fig)) + + def handle_save_spe(self, _): + display(save_and_make_link(self.spectrumPlots[self.spectrum_select.value].fig)) + + def replot(self, *_): + selectedImage = self.product_select.value + if not (selectedImage is None or selectedImage not in self.plots): + self.replot_image() + self.outputs[selectedImage].clear_output(wait=True) + with self.outputs[selectedImage]: + display(self.plots[selectedImage].fig) + + selectedSpectrum = self.spectrum_select.value + if not (selectedSpectrum is None or selectedSpectrum not in self.spectrumPlots): + self.replot_spectra() + self.spectrumOutputs[selectedSpectrum].clear_output(wait=True) + with self.spectrumOutputs[selectedSpectrum]: + display(self.spectrumPlots[selectedSpectrum].fig) + + self.update_timeseries() + self.timeseriesOutput.clear_output(wait=True) + with self.timeseriesOutput: + display(self.timeseriesPlot.fig) + + def update_spectra(self): + selected = self.spectrum_select.value + self.spectrum_box.children = [self.spectrumOutputs[selected]] + plot = self.spectrumPlots[selected] + + step = self.step_slider.value + + xrange = slice(*self.slider["X"].value) + yrange = slice(*self.slider["Z"].value) + + if selected == "size": + for key in ("Particles Wet Size Spectrum", "Particles Dry Size Spectrum"): + if xrange.start == xrange.stop or yrange.start == yrange.stop: + continue + try: + data = self.storage.load(key, self.settings.output_steps[step]) + data = data[xrange, yrange, :] + data = np.mean(np.mean(data, axis=0), axis=0) + data = np.concatenate(((0,), data)) + if key == "Particles Wet Size Spectrum": + plot.update_wet(data, step) + if key == "Particles Dry Size Spectrum": + plot.update_dry(data) + except self.storage.Exception: + pass + elif selected == "terminal velocity": + try: + data = self.storage.load( + "radius binned number averaged terminal velocity", + self.settings.output_steps[step], + ) + + data = data[xrange, yrange, :] + data = np.where(data != 0, data, np.nan) + try: + with warnings.catch_warnings(record=True) as _: + warnings.simplefilter("ignore") + data_min = np.nanmin(np.nanmin(data, axis=0), axis=0) + data_max = np.nanmax(np.nanmax(data, axis=0), axis=0) + except RuntimeWarning: + pass + plot.update(data_min, data_max, step) + except self.storage.Exception: + pass + elif selected == "temperature": + try: + dT = abs(self.settings.T_bins_edges[1] - self.settings.T_bins_edges[0]) + np.testing.assert_allclose(np.diff(self.settings.T_bins_edges), dT) + + conc = self.storage.load( + "particle specific concentration", self.settings.output_steps[step] + ) + # TODO #705: assert unit == mg^-1 + conc = conc[xrange, yrange] + + data = self.storage.load( + "freezable specific concentration", self.settings.output_steps[step] + ) + data = data[xrange, yrange, :] + + data = np.sum(np.sum(data, axis=0), axis=0) / np.sum( + np.sum(conc, axis=0), axis=0 + ) + data = np.concatenate(((0,), dT * np.cumsum(data[::-1])))[::-1] + + plot.update(data, step) + except self.storage.Exception: + pass + else: + raise NotImplementedError() + + def replot_spectra(self, *_): + self.update_spectra() + + selected = self.product_select.value + if selected is None or selected not in self.plots: + return + self.plots[selected].update_lines( + self.slider["X"].value, self.slider["Z"].value + ) + + self.outputs[selected].clear_output(wait=True) + + key = self.spectrum_select.value + self.spectrumOutputs[key].clear_output(wait=True) + with self.outputs[selected]: + display(self.plots[selected].fig) + with self.spectrumOutputs[key]: + display(self.spectrumPlots[key].fig) + + def update_image(self): + selected = self.product_select.value + + if selected in self.outputs: + self.plot_box.children = [self.outputs[selected]] + + step = self.step_slider.value + try: + data = self.storage.load(selected, self.settings.output_steps[step]) + except self.storage.Exception: + data = None + + self.plots[selected].update( + data, step, self.storage.data_range(selected) if data is not None else None + ) + + def replot_image(self, *_): + selected = self.product_select.value + if selected is None or selected not in self.plots: + return + + self.update_image() + self.outputs[selected].clear_output(wait=True) + with self.outputs[selected]: + display(self.plots[selected].fig) + + def update_timeseries(self): + try: + data = self.storage.load("surf_precip") + except self.storage.Exception: + data = None + self.timeseriesPlot.update( + data, self.storage.data_range("surf_precip") if data is not None else None + ) + + def replot_timeseries(self): + self.update_timeseries() + self.timeseriesOutput.clear_output(wait=True) + with self.timeseriesOutput: + display(self.timeseriesPlot.fig) + + def box(self): + jslink((self.play, "value"), (self.step_slider, "value")) + self.step_slider.observe(self.replot, "value") + self.product_select.observe(self.replot_image, "value") + self.spectrum_select.observe(self.replot_spectra, "value") + for xz in ("X", "Z"): + self.slider[xz].observe(self.replot_spectra, "value") + return VBox([Box([self.play, self.step_slider]), self.plots_box]) diff --git a/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/make_default_product_collection.py b/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/make_default_product_collection.py new file mode 100644 index 0000000000000000000000000000000000000000..6103435476e5de9b8047d3c01f51719d599e2765 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/make_default_product_collection.py @@ -0,0 +1,102 @@ +import numpy as np + +from PySDM import products as PySDM_products + + +def make_default_product_collection(settings): + cloud_range = (settings.aerosol_radius_threshold, settings.drizzle_radius_threshold) + products = [ + # Note: consider better radius_bins_edges + PySDM_products.ParticleSizeSpectrumPerMassOfDryAir( + name="Particles Wet Size Spectrum", + unit="mg^-1 um^-1", + radius_bins_edges=settings.r_bins_edges, + ), + PySDM_products.ParticleSizeSpectrumPerMassOfDryAir( + name="Particles Dry Size Spectrum", + unit="mg^-1 um^-1", + radius_bins_edges=settings.r_bins_edges, + dry=True, + ), + PySDM_products.TotalParticleConcentration(), + PySDM_products.TotalParticleSpecificConcentration(), + PySDM_products.ParticleConcentration( + radius_range=(0, settings.aerosol_radius_threshold) + ), + PySDM_products.ParticleConcentration( + name="n_c_cm3", unit="cm^-3", radius_range=cloud_range + ), + PySDM_products.WaterMixingRatio( + name="cloud water mixing ratio", radius_range=cloud_range + ), + PySDM_products.WaterMixingRatio( + name="rain water mixing ratio", + radius_range=(settings.drizzle_radius_threshold, np.inf), + ), + PySDM_products.ParticleConcentration( + name="drizzle concentration", + radius_range=(settings.drizzle_radius_threshold, np.inf), + unit="cm^-3", + ), + PySDM_products.ParticleSpecificConcentration( + name="aerosol specific concentration", + radius_range=(0, settings.aerosol_radius_threshold), + unit="mg^-1", + ), + PySDM_products.MeanRadius(unit="um"), + PySDM_products.SuperDropletCountPerGridbox(), + PySDM_products.AmbientRelativeHumidity(name="RH_env", var="RH"), + PySDM_products.AmbientPressure(name="p_env", var="p"), + PySDM_products.AmbientTemperature(name="T_env", var="T"), + PySDM_products.AmbientWaterVapourMixingRatio( + name="water_vapour_mixing_ratio_env", var="water_vapour_mixing_ratio" + ), + PySDM_products.AmbientDryAirDensity(name="rhod_env", var="rhod"), + PySDM_products.AmbientDryAirPotentialTemperature(name="thd_env", var="thd"), + PySDM_products.CPUTime(), + PySDM_products.WallTime(), + PySDM_products.EffectiveRadius(unit="um", radius_range=cloud_range), + PySDM_products.RadiusBinnedNumberAveragedTerminalVelocity( + radius_bin_edges=settings.terminal_velocity_radius_bin_edges + ), + ] + + if settings.processes["fluid advection"]: + products.append(PySDM_products.MaxCourantNumber()) + products.append(PySDM_products.CoolingRate(unit="K/min")) + if settings.processes["condensation"]: + products.append(PySDM_products.CondensationTimestepMin(name="dt_cond_min")) + products.append(PySDM_products.CondensationTimestepMax(name="dt_cond_max")) + products.append(PySDM_products.PeakSaturation(unit="%", name="S_max_percent")) + products.append(PySDM_products.ActivatingRate()) + products.append(PySDM_products.DeactivatingRate()) + products.append(PySDM_products.RipeningRate()) + if settings.processes["particle advection"]: + products.append( + PySDM_products.SurfacePrecipitation(name="surf_precip", unit="mm/day") + ) + if settings.processes["coalescence"]: + products.append(PySDM_products.CollisionTimestepMean(name="dt_coal_avg")) + products.append(PySDM_products.CollisionTimestepMin(name="dt_coal_min")) + products.append(PySDM_products.CollisionRatePerGridbox(name="cr")) + products.append(PySDM_products.CollisionRateDeficitPerGridbox(name="crd")) + products.append(PySDM_products.CoalescenceRatePerGridbox(name="cor")) + if settings.processes["breakup"]: + products.append(PySDM_products.BreakupRatePerGridbox(name="br")) + products.append(PySDM_products.BreakupRateDeficitPerGridbox(name="brd")) + if settings.processes["freezing"]: + products.append(PySDM_products.IceWaterContent()) + if settings.freezing_immersion == "singular": + products.append( + PySDM_products.FreezableSpecificConcentration(settings.T_bins_edges) + ) + else: + products.append(PySDM_products.TotalUnfrozenImmersedSurfaceArea()) + # TODO #599 immersed surf spec + products.append( + PySDM_products.ParticleSpecificConcentration( + radius_range=(-np.inf, 0), name="n_ice" + ) + ) + + return products diff --git a/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/mpdata_2d.py b/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/mpdata_2d.py new file mode 100644 index 0000000000000000000000000000000000000000..301da97dd7a23df30f1ed0f76f16df4060161c83 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/mpdata_2d.py @@ -0,0 +1,141 @@ +import inspect +from functools import cached_property +from threading import Thread + +import numpy as np +from PyMPDATA import Options, ScalarField, Solver, Stepper, VectorField +from PyMPDATA.boundary_conditions import Periodic +from PySDM_examples.utils.kinematic_2d.fields import ( + nondivergent_vector_field_2d, + x_vec_coord, + z_vec_coord, +) + +from PySDM.backends.impl_numba import conf +from PySDM.impl.arakawa_c import make_rhod + + +class MPDATA_2D: + def __init__( + self, + *, + advectees, + stream_function, + rhod_of_zZ, + dt, + grid, + size, + n_iters=2, + infinite_gauge=True, + nonoscillatory=True, + third_order_terms=False, + ): + self._grid = grid + self.size = size + self.dt = dt + self.stream_function = stream_function + self.stream_function_time_dependent = ( + "t" in inspect.signature(stream_function).parameters + ) + self.asynchronous = False + self.thread: (Thread, None) = None + self.t = 0 + self.advectees = advectees + + self._options = Options( + n_iters=n_iters, + infinite_gauge=infinite_gauge, + nonoscillatory=nonoscillatory, + third_order_terms=third_order_terms, + ) + + self.g_factor = make_rhod(grid, rhod_of_zZ) + self.g_factor_vec = ( + rhod_of_zZ(zZ=x_vec_coord(grid)[-1]), + rhod_of_zZ(zZ=z_vec_coord(grid)[-1]), + ) + + @cached_property + def mpdatas(self): + disable_threads_if_needed = {} + if not conf.JIT_FLAGS["parallel"]: + disable_threads_if_needed["n_threads"] = 1 + + stepper = Stepper( + options=self._options, + grid=self._grid, + non_unit_g_factor=True, + **disable_threads_if_needed, + ) + + advector_impl = VectorField( + ( + np.full((self._grid[0] + 1, self._grid[1]), np.nan), + np.full((self._grid[0], self._grid[1] + 1), np.nan), + ), + halo=self._options.n_halo, + boundary_conditions=(Periodic(), Periodic()), + ) + + g_factor_impl = ScalarField( + self.g_factor.astype(dtype=self._options.dtype), + halo=self._options.n_halo, + boundary_conditions=(Periodic(), Periodic()), + ) + + mpdatas = {} + for k, v in self.advectees.items(): + advectee_impl = ScalarField( + np.asarray(v, dtype=self._options.dtype), + halo=self._options.n_halo, + boundary_conditions=(Periodic(), Periodic()), + ) + mpdatas[k] = Solver( + stepper=stepper, + advectee=advectee_impl, + advector=advector_impl, + g_factor=g_factor_impl, + ) + return mpdatas + + def __getitem__(self, key: str): + if "mpdatas" in self.__dict__: + return self.mpdatas[key].advectee.get() + return self.advectees[key] + + def __call__(self, displacement): + if self.asynchronous: + self.thread = Thread(target=self.step, args=()) + self.thread.start() + else: + self.step(displacement) + + def wait(self): + if self.asynchronous: + if self.thread is not None: + self.thread.join() + + def refresh_advector(self, displacement): + for mpdata in self.mpdatas.values(): + advector = nondivergent_vector_field_2d( + self._grid, self.size, self.dt, self.stream_function, t=self.t + ) + for d in range(len(self._grid)): + np.testing.assert_array_less(np.abs(advector[d]), 1) + mpdata.advector.get_component(d)[:] = advector[d] + if displacement is not None: + for d in range(len(self._grid)): + advector[d] /= self.g_factor_vec[d] + displacement.upload_courant_field(advector) + break # the advector field is shared + + def step(self, displacement): + if not self.stream_function_time_dependent and self.t == 0: + self.refresh_advector(displacement) + + self.t += 0.5 * self.dt + if self.stream_function_time_dependent: + self.refresh_advector(displacement) + for mpdata in self.mpdatas.values(): + mpdata.advance(1) + self.t += 0.5 * self.dt diff --git a/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/plots.py b/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/plots.py new file mode 100644 index 0000000000000000000000000000000000000000..6617fe9058c1b7b72b9bf50c36d12077749c3305 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/plots.py @@ -0,0 +1,204 @@ +import matplotlib.pyplot as plt +import numpy as np + +from PySDM.physics import constants as const + + +class _Plot: + def __init__(self, fig, ax): + self.fig, self.ax = fig, ax + self.ax.set_title(" ") + + +class _ImagePlot(_Plot): + line_args = {"color": "red", "alpha": 0.666, "linestyle": ":", "linewidth": 3} + + def __init__( + self, fig, ax, grid, size, product, show=False, lines=False, cmap="YlGnBu" + ): + super().__init__(fig, ax) + self.nans = np.full(grid, np.nan) + + self.dx = size[0] / grid[0] + self.dz = size[1] / grid[1] + + xlim = (0, size[0]) + zlim = (0, size[1]) + + self.ax.set_xlim(xlim) + self.ax.set_ylim(zlim) + + if lines: + self.lines = {"X": [None] * 2, "Z": [None] * 2} + self.lines["X"][0] = plt.plot([-1] * 2, zlim, **self.line_args)[0] + self.lines["Z"][0] = plt.plot(xlim, [-1] * 2, **self.line_args)[0] + self.lines["X"][1] = plt.plot([-1] * 2, zlim, **self.line_args)[0] + self.lines["Z"][1] = plt.plot(xlim, [-1] * 2, **self.line_args)[0] + + data = np.full_like(self.nans, np.nan) + label = f"{product.name} [{product.unit}]" + + self.ax.set_xlabel("X [m]") + self.ax.set_ylabel("Z [m]") + + self.im = self.ax.imshow( + self._transpose(data), origin="lower", extent=(*xlim, *zlim), cmap=cmap + ) + plt.colorbar(self.im, ax=self.ax).set_label(label) + if show: + plt.show() + + @staticmethod + def _transpose(data): + if data is not None: + return data.T + return None + + def update(self, data, step, data_range): + data = self._transpose(data) + if data is not None: + self.im.set_data(data) + if data_range is not None: + self.im.set_clim(vmin=data_range[0], vmax=data_range[1]) + nanmin = np.nan + nanmax = np.nan + if np.isfinite(data).any(): + nanmin = np.nanmin(data) + nanmax = np.nanmax(data) + self.ax.set_title( + f"min:{nanmin: .3g} max:{nanmax: .3g} t/dt_out:{step: >6}" + ) + + def update_lines(self, focus_x, focus_z): + self.lines["X"][0].set_xdata(x=(focus_x[0] + 0.15) * self.dx) + self.lines["Z"][0].set_ydata(y=(focus_z[0] + 0.15) * self.dz) + self.lines["X"][1].set_xdata(x=(focus_x[1] - 0.25) * self.dx) + self.lines["Z"][1].set_ydata(y=(focus_z[1] - 0.25) * self.dz) + + +class _SpectrumPlot(_Plot): + def __init__(self, r_bins, initial_spectrum_per_mass_of_dry_air, show=True): + super().__init__(*plt.subplots(1, 1)) + self.ax.set_xlim(np.amin(r_bins), np.amax(r_bins)) + self.ax.set_xlabel("particle radius [μm]") + self.ax.set_ylabel("specific concentration density [mg$^{-1}$ μm$^{-1}$]") + self.ax.set_xscale("log") + self.ax.set_yscale("log") + self.ax.set_ylim(1, 5e3) + self.ax.grid(True) + vals = initial_spectrum_per_mass_of_dry_air.size_distribution( + r_bins * const.si.um + ) + const.convert_to(vals, const.si.mg**-1 / const.si.um) + self.ax.plot(r_bins, vals, label="spectrum sampled at t=0") + self.spec_wet = self.ax.step( + r_bins, + np.full_like(r_bins, np.nan), + label="binned super-particle wet sizes", + )[0] + self.spec_dry = self.ax.step( + r_bins, + np.full_like(r_bins, np.nan), + label="binned super-particle dry sizes", + )[0] + self.ax.legend() + if show: + plt.show() + + def update_wet(self, data, step): + self.spec_wet.set_ydata(data) + self.ax.set_title(f"t/dt_out:{step}") + + def update_dry(self, dry): + self.spec_dry.set_ydata(dry) + + +class _TimeseriesPlot(_Plot): + def __init__(self, fig, ax, times, show=True): + super().__init__(fig, ax) + self.ax.set_xlim(0, times[-1]) + self.ax.set_xlabel("time [s]") + self.ax.set_ylabel("rainfall [mm/day]") + self.ax.grid(True) + self.ydata = np.full_like(times, np.nan, dtype=float) + self.timeseries = self.ax.step(times, self.ydata, where="pre")[0] + if show: + plt.show() + + def update(self, data, data_range): + if data is not None: + self.ydata[0 : len(data)] = data[:] + if data_range[0] != data_range[1]: + self.ax.set_ylim(data_range[0], 1.1 * data_range[1]) + else: + self.ydata[:] = np.nan + self.timeseries.set_ydata(self.ydata) + + +class _TemperaturePlot(_Plot): + def __init__(self, T_bins, formulae, show=True): + super().__init__(*plt.subplots(1, 1)) + self.formulae = formulae + self.ax.set_xlim(np.amax(T_bins), np.amin(T_bins)) + self.ax.set_xlabel("temperature [K]") + self.ax.set_ylabel("freezable fraction / cdf [1]") + self.ax.set_ylim(-0.05, 1.05) + self.ax.grid(True) + # self.ax.plot(T_bins, self.formulae.freezing_temperature_spectrum.cdf(T_bins), + # label=str(self.formulae.freezing_temperature_spectrum) + " (sampled at t=0)") + self.spec = self.ax.step( + T_bins, + np.full_like(T_bins, np.nan), + label="binned super-particle attributes", + where="mid", + )[0] + self.ax.legend() + if show: + plt.show() + + def update(self, data, step): + self.ax.set_title(f"t/dt_out:{step}") + self.spec.set_ydata(data) + + +class _TerminalVelocityPlot(_Plot): + def __init__(self, radius_bins, formulae, show=True): + self.formulae = formulae + super().__init__(*plt.subplots(1, 1)) + + self.ax.set_xlim( + np.amin(radius_bins) / const.si.um, np.amax(radius_bins) / const.si.um + ) + self.ax.set_xlabel("radius [μm]") + self.ax.set_ylabel("mean terminal velocity [m/s]") + self.ax.set_ylim(0, 0.1) + self.ax.grid(True) + + self.radius_bins = radius_bins + # self.ax.plot(T_bins, self.formulae.freezing_temperature_spectrum.cdf(T_bins), + # label=str(self.formulae.freezing_temperature_spectrum) + " (sampled at t=0)") + # nans = np.full_like(radius_bins[:-1], np.nan) + # self.spec = self.ax.fill_between( + # (radius_bins[:-1] + np.diff(radius_bins)/2) / const.si.um, + # nans, + # nans, + # marker='o' + # )[0] + # label='binned super-particle attributes', + # where='mid' + # )[0] + # self.ax.legend() + + if show: + plt.show() + + def update(self, data_min, data_max, step): + self.ax.set_title(f"t/dt_out:{step}") + self.ax.collections.clear() + self.ax.fill_between( + (self.radius_bins[:-1] + np.diff(self.radius_bins) / 2) / const.si.um, + data_min, + data_max, + color="gray", + ) + # self.spec.set_ydata(data) diff --git a/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/simulation.py b/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/simulation.py new file mode 100644 index 0000000000000000000000000000000000000000..9c290e979c13ffd7f09dc7b0f51c3d9bdccab1f1 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/simulation.py @@ -0,0 +1,244 @@ +import numpy as np +from PySDM_examples.utils.kinematic_2d.make_default_product_collection import ( + make_default_product_collection, +) +from PySDM_examples.utils.kinematic_2d.mpdata_2d import MPDATA_2D +from PySDM_examples.utils import DummyController + +from PySDM.backends import CPU +from PySDM.builder import Builder +from PySDM.dynamics import ( + AmbientThermodynamics, + Coalescence, + Collision, + Condensation, + Displacement, + EulerianAdvection, + Freezing, +) +from PySDM.environments import Kinematic2D +from PySDM.initialisation.sampling import spatial_sampling + + +class Simulation: + def __init__(self, settings, storage, SpinUp, backend_class=CPU): + self.settings = settings + self.storage = storage + self.particulator = None + self.backend_class = backend_class + self.SpinUp = SpinUp + + @property + def products(self): + return self.particulator.products + + def reinit(self, products=None): + formulae = self.settings.formulae + backend = self.backend_class(formulae=formulae) + environment = Kinematic2D( + dt=self.settings.dt, + grid=self.settings.grid, + size=self.settings.size, + rhod_of=self.settings.rhod_of_zZ, + mixed_phase=self.settings.processes["freezing"], + ) + builder = Builder( + n_sd=self.settings.n_sd, backend=backend, environment=environment + ) + + if products is not None: + products = list(products) + else: + products = make_default_product_collection(self.settings) + + if self.settings.processes["fluid advection"]: + builder.add_dynamic(AmbientThermodynamics()) + if self.settings.processes["condensation"]: + kwargs = {} + if not self.settings.condensation_adaptive: + kwargs["substeps"] = (self.settings.condensation_substeps,) + condensation = Condensation( + rtol_x=self.settings.condensation_rtol_x, + rtol_thd=self.settings.condensation_rtol_thd, + adaptive=self.settings.condensation_adaptive, + dt_cond_range=self.settings.condensation_dt_cond_range, + schedule=self.settings.condensation_schedule, + **kwargs, + ) + builder.add_dynamic(condensation) + if self.settings.processes["fluid advection"]: + initial_profiles = { + "th": self.settings.initial_dry_potential_temperature_profile, + "water_vapour_mixing_ratio": self.settings.initial_vapour_mixing_ratio_profile, + } + advectees = dict( + ( + key, + np.repeat( + profile.reshape(1, -1), + builder.particulator.environment.mesh.grid[0], + axis=0, + ), + ) + for key, profile in initial_profiles.items() + ) + solver = MPDATA_2D( + advectees=advectees, + stream_function=self.settings.stream_function, + rhod_of_zZ=self.settings.rhod_of_zZ, + dt=self.settings.dt, + grid=self.settings.grid, + size=self.settings.size, + n_iters=self.settings.mpdata_iters, + infinite_gauge=self.settings.mpdata_iga, + nonoscillatory=self.settings.mpdata_fct, + third_order_terms=self.settings.mpdata_tot, + ) + builder.add_dynamic(EulerianAdvection(solver)) + if self.settings.processes["particle advection"]: + builder.add_dynamic( + Displacement( + enable_sedimentation=self.settings.processes["sedimentation"], + adaptive=self.settings.displacement_adaptive, + rtol=self.settings.displacement_rtol, + ) + ) + if ( + self.settings.processes["coalescence"] + and self.settings.processes["breakup"] + ): + builder.add_dynamic( + Collision( + collision_kernel=self.settings.kernel, + enable_breakup=self.settings.processes["breakup"], + coalescence_efficiency=self.settings.coalescence_efficiency, + breakup_efficiency=self.settings.breakup_efficiency, + fragmentation_function=self.settings.breakup_fragmentation, + adaptive=self.settings.coalescence_adaptive, + dt_coal_range=self.settings.coalescence_dt_coal_range, + substeps=self.settings.coalescence_substeps, + optimized_random=self.settings.coalescence_optimized_random, + ) + ) + elif ( + self.settings.processes["coalescence"] + and not self.settings.processes["breakup"] + ): + builder.add_dynamic( + Coalescence( + collision_kernel=self.settings.kernel, + adaptive=self.settings.coalescence_adaptive, + dt_coal_range=self.settings.coalescence_dt_coal_range, + substeps=self.settings.coalescence_substeps, + optimized_random=self.settings.coalescence_optimized_random, + ) + ) + assert not ( + self.settings.processes["breakup"] + and not self.settings.processes["coalescence"] + ) + if self.settings.processes["freezing"]: + builder.add_dynamic( + Freezing( + immersion_freezing=self.settings.freezing_immersion, + thaw=self.settings.freezing_thaw, + ) + ) + + attributes = builder.particulator.environment.init_attributes( + spatial_discretisation=spatial_sampling.Pseudorandom(), + dry_radius_spectrum=self.settings.spectrum_per_mass_of_dry_air, + kappa=self.settings.kappa, + n_sd=self.settings.n_sd + // (2 if self.settings.freezing_inp_frac != 1 else 1), + ) + + if self.settings.processes["freezing"]: + attributes["signed water mass"] = attributes.pop("water mass") + + if self.settings.freezing_inp_spec is None: + immersed_surface_area = formulae.trivia.sphere_surface( + diameter=2 * formulae.trivia.radius(volume=attributes["dry volume"]) + ) + else: + immersed_surface_area = self.settings.freezing_inp_spec.percentiles( + np.random.random(attributes["dry volume"].size), # TODO #599: seed + ) + + if self.settings.freezing_immersion == "singular": + attributes["freezing temperature"] = ( + formulae.freezing_temperature_spectrum.invcdf( + np.random.random(immersed_surface_area.size), # TODO #599: seed + immersed_surface_area, + ) + ) + else: + attributes["immersed surface area"] = immersed_surface_area + + if self.settings.freezing_inp_frac != 1: + assert self.settings.n_sd % 2 == 0 + assert 0 < self.settings.freezing_inp_frac < 1 + freezing_attribute = { + "singular": "freezing temperature", + "time-dependent": "immersed surface area", + }[self.settings.freezing_immersion] + for name, array in attributes.items(): + if array.shape[-1] != self.settings.n_sd // 2: + raise AssertionError(f"attribute >>{name}<< has wrong size") + array = array.copy() + full_shape = list(array.shape) + orig = slice(None, full_shape[-1]) + copy = slice(orig.stop, None) + full_shape[-1] *= 2 + attributes[name] = np.empty(full_shape, dtype=array.dtype) + if name == freezing_attribute: + attributes[name][orig] = array + attributes[name][copy] = 0 + elif name == "multiplicity": + attributes[name][orig] = array * self.settings.freezing_inp_frac + attributes[name][copy] = array * ( + 1 - self.settings.freezing_inp_frac + ) + elif len(array.shape) > 1: # particle positions + # TODO #599: seed + for dim, _ in enumerate(array.shape): + # only to make particles not shadow each other in visualisations + attributes[name][dim, orig] = array[dim, :] + attributes[name][dim, copy] = np.random.permutation( + array[dim, :] + ) + else: + attributes[name][orig] = array + attributes[name][copy] = array + + non_zero_per_gridbox = np.count_nonzero( + attributes[freezing_attribute] + ) / np.prod(self.settings.grid) + assert non_zero_per_gridbox == self.settings.n_sd_per_gridbox / 2 + + self.particulator = builder.build(attributes, tuple(products)) + + if self.SpinUp is not None: + self.SpinUp(self.particulator, self.settings.n_spin_up) + if self.storage is not None: + self.storage.init(self.settings) + + def run(self, controller=DummyController(), vtk_exporter=None): + with controller: + for step in self.settings.output_steps: + if controller.panic: + break + + self.particulator.run(step - self.particulator.n_steps) + + self.store(step) + + if vtk_exporter is not None: + vtk_exporter.export_attributes(self.particulator) + vtk_exporter.export_products(self.particulator) + + controller.set_percent(step / self.settings.output_steps[-1]) + + def store(self, step): + for name, product in self.particulator.products.items(): + self.storage.save(product.get(), step, name) diff --git a/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/storage.py b/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/storage.py new file mode 100644 index 0000000000000000000000000000000000000000..5145b1216a04f471ee1b8ca83a406277b127a931 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/utils/kinematic_2d/storage.py @@ -0,0 +1,85 @@ +import os +import tempfile +from pathlib import Path + +import numpy as np + + +class Storage: + class Exception(BaseException): + pass + + def __init__(self, dtype=np.float32, path=None): + self.temp_dir = None + if path is None: + self.setup_temporary_directory() + else: + Path(path).mkdir(parents=True, exist_ok=True) + self.dir_path = Path(path).absolute() + self.dtype = dtype + self.grid = None + self._data_range = None + + def __del__(self): + self.cleanup() + + def cleanup(self): + if self.temp_dir is not None: + self.temp_dir.cleanup() + + def setup_temporary_directory(self): + self.cleanup() + self.temp_dir = tempfile.TemporaryDirectory() + self.dir_path = self.temp_dir.name + + def init(self, settings): + self.grid = settings.grid + self._data_range = {} + if self.temp_dir is not None and any(os.scandir(self.temp_dir.name)): + self.setup_temporary_directory() + + def _filepath(self, name: str, step: int = None): + if step is None: + filename = f"{name}.npy" + else: + filename = f"{name}_{step:06}.npy" + path = os.path.join(self.dir_path, filename) + return path + + def save(self, data: (float, np.ndarray), step: int, name: str): + if isinstance(data, (int, float)): + path = self._filepath(name) + np.save( + path, + np.concatenate( + (() if step == 0 else np.load(path), (self.dtype(data),)) + ), + ) + elif data.shape[0:2] == self.grid: + np.save(self._filepath(name, step), data.astype(self.dtype)) + else: + raise NotImplementedError() + + if name not in self._data_range: + self._data_range[name] = (np.inf, -np.inf) + just_nans = np.isnan(data).all() + self._data_range[name] = ( + min( + self._data_range[name][0] if just_nans else np.nanmin(data), + self._data_range[name][0], + ), + max( + self._data_range[name][1] if just_nans else np.nanmax(data), + self._data_range[name][1], + ), + ) + + def data_range(self, name): + return self._data_range[name] + + def load(self, name: str, step: int = None) -> np.ndarray: + try: + data = np.load(self._filepath(name, step)) + except FileNotFoundError as err: + raise Storage.Exception() from err + return data diff --git a/PySDM/source/examples/PySDM_examples/utils/progbar_controller.py b/PySDM/source/examples/PySDM_examples/utils/progbar_controller.py new file mode 100644 index 0000000000000000000000000000000000000000..0969bd40e4824385d32b6713a91764f1310c7889 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/utils/progbar_controller.py @@ -0,0 +1,19 @@ +from PySDM_examples.utils.widgets import FloatProgress, display + + +class ProgBarController: + def __init__(self, description=""): + self.progress = FloatProgress( + value=0.0, min=0.0, max=1.0, description=description + ) + self.panic = False + + def __enter__(self): + self.set_percent(0) + display(self.progress) + + def __exit__(self, *_): + pass + + def set_percent(self, value): + self.progress.value = value diff --git a/PySDM/source/examples/PySDM_examples/utils/pvanim.py b/PySDM/source/examples/PySDM_examples/utils/pvanim.py new file mode 100644 index 0000000000000000000000000000000000000000..3cddd563863be3a38dfb53d9cf734c960a8c0836 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/utils/pvanim.py @@ -0,0 +1,340 @@ +#!/usr/bin/env pvpython +import argparse +from collections import namedtuple +import pathlib + +from paraview import simple as pvs # pylint: disable=import-error + +pvs._DisableFirstRenderCameraReset() + + +def cli_using_argparse(argp): + argp.add_argument("product_path", help="path to pvd products file") + argp.add_argument("attributes_path", help=" path to pvd attributes file") + argp.add_argument("output_path", help="path where to write output files") + argp.add_argument( + "--mode", + choices=["light", "dark"], + default="dark", + help="Choose 'light' or 'dark' mode.", + ) + argp.add_argument( + "--multiplicity_preset", + default="Inferno (matplotlib)", + help="Preset for multiplicity", + ) + argp.add_argument( + "--multiplicity_logscale", + action="store_false", + help="Use log scale for multiplicity", + ) + argp.add_argument( + "--effectiveradius_preset", + default="Black, Blue and White", + help="Preset for effectiveradius", + ) + argp.add_argument( + "--effectiveradius_logscale", + action="store_false", + help="Use log scale for effectiveradius", + ) + argp.add_argument( + "--effectiveradius_nan_color", + nargs=3, + type=float, + default=[0.666, 0.333, 1.0], + help="Nan color in RGB format for effectiveradius", + ) + argp.add_argument( + "--sd_products_opacity", type=float, default=0.9, help="Opacity for sd_products" + ) + argp.add_argument( + "--calculator1_opacity", + type=float, + default=0.19, + help="Opacity for calculator1", + ) + argp.add_argument( + "--sd_attributes_opacity", + type=float, + default=0.77, + help="Opacity for sd_attributes", + ) + argp.add_argument( + "--animation_size", + nargs=2, + type=int, + default=[800, 800], + help="Animation size [x,y]", + ) + argp.add_argument( + "--animationframename", + type=str, + help="Name of the file with animation last frame", + ) + argp.add_argument( + "--animationname", + type=str, + help="Name of the file with animation", + ) + argp.add_argument( + "--framerate", type=int, help="Number of frame rates.", default=15 + ) + + +ap = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) +cli_using_argparse(ap) + +args = ap.parse_args() +sd_productspvd = pvs.OpenDataFile(args.product_path) +sd_attributespvd = pvs.OpenDataFile(args.attributes_path) + + +setup = { + "renderView1": pvs.GetActiveViewOrCreate("RenderView"), + "sd_attributespvdDisplay": pvs.GetDisplayProperties( + sd_attributespvd, view=pvs.GetActiveViewOrCreate("RenderView") + ), + "effectiveradiusLUT": pvs.GetColorTransferFunction("effectiveradius"), + "sd_productspvdDisplay": pvs.GetDisplayProperties( + sd_productspvd, view=pvs.GetActiveViewOrCreate("RenderView") + ), + "color": [1, 1, 1] if args.mode == "dark" else [0, 0, 0], + "inverted_color": [0.129, 0.145, 0.161] if args.mode == "dark" else [1, 1, 1], +} + +setup = namedtuple("Setup", setup.keys())(**setup) + +setup.effectiveradiusLUT.RescaleTransferFunction(0.1380997175392798, 207.7063518856934) +materialLibrary1 = pvs.GetMaterialLibrary() +setup.renderView1.Update() + + +def create_new_calculator( + calcinput, + representation, + function, + color_by1, + color_by2, + color_by3, + scalar_coloring=False, + hide=False, + *, + y, + registrationame, +): + calculator = pvs.Calculator(registrationName=registrationame, Input=calcinput) + display = pvs.Show(calculator, y.renderView1, representation) + calculator.Function = function + y.renderView1.Update() + if scalar_coloring is True: + pvs.ColorBy(display, (color_by1, color_by2, color_by3)) + if hide is True: + pvs.Hide(calculator, y.renderView1) + return y.renderView1.Update() + + +def scalar_bar(name, *, y, erLUT): + calculator1Display = pvs.Show( + calculator1, y.renderView1, "UnstructuredGridRepresentation" + ) + calculator1Display.SetScalarBarVisibility(y.renderView1, True) + scalarBar = pvs.GetScalarBar(erLUT.effectiveradiusLUT, y.renderView1) + scalarBar.ComponentTitle = "" + scalarBar.Title = name + scalarBar.TitleFontSize = 25 + scalarBar.LabelFontSize = 25 + scalarBar.LabelColor = setup.color + scalarBar.TitleColor = setup.color + y.renderView1.Update() + + +def create_glyph( + registration_name, put, scale_array1, scale_array2, color_by=False, *, y +): + glyph = pvs.Glyph(registrationName=registration_name, Input=put, GlyphType="Arrow") + glyphDisplay = pvs.Show(glyph, y.renderView1, "GeometryRepresentation") + glyphDisplay.Representation = "Surface" + glyph.ScaleArray = [scale_array1, scale_array2] + glyph.ScaleFactor = 100 + glyphDisplay.SetScalarBarVisibility(y.renderView1, True) + multiplicityLUT = pvs.GetColorTransferFunction("multiplicity") + multiplicityLUTColorBar = pvs.GetScalarBar(multiplicityLUT, y.renderView1) + multiplicityLUTColorBar.TitleFontSize = 25 + multiplicityLUTColorBar.LabelFontSize = 25 + multiplicityLUTColorBar.LabelColor = setup.color + multiplicityLUTColorBar.TitleColor = setup.color + if color_by is True: + glyphDisplay.ColorArrayName = ["POINTS", ""] + pvs.ColorBy(glyphDisplay, None) + y.renderView1.Update() + + +def apply_presets_logscale_opacity_and_update(*, y, attdisplay, erLUT, proddisplay): + multiplicityLUT = pvs.GetColorTransferFunction("multiplicity") + multiplicityLUT.RescaleTransferFunction(19951.0, 50461190157.0) + calculator1Display = pvs.Show( + calculator1, y.renderView1, "UnstructuredGridRepresentation" + ) + multiplicityLUT.ApplyPreset(args.multiplicity_preset, True) + if args.multiplicity_logscale: + multiplicityLUT.MapControlPointsToLogSpace() + multiplicityLUT.UseLogScale = 1 + else: + multiplicityLUT.MapControlPointsToLinearSpace() + multiplicityLUT.UseLogScale = 0 + + erLUT.effectiveradiusLUT.ApplyPreset(args.effectiveradius_preset, True) + if args.effectiveradius_logscale: + erLUT.effectiveradiusLUT.MapControlPointsToLogSpace() + erLUT.effectiveradiusLUT.UseLogScale = 1 + else: + erLUT.effectiveradiusLUT.MapControlPointsToLinearSpace() + erLUT.effectiveradiusLUT.UseLogScale = 0 + + erLUT.effectiveradiusLUT.NanColor = args.effectiveradius_nan_color + + proddisplay.sd_productspvdDisplay.SetRepresentationType("Surface With Edges") + proddisplay.sd_productspvdDisplay.Opacity = args.sd_products_opacity + calculator1Display.Opacity = args.calculator1_opacity + attdisplay.sd_attributespvdDisplay.Opacity = args.sd_attributes_opacity + + y.renderView1.Update() + + +def get_layout(*, y): + pvs.SetViewProperties( + Background=setup.inverted_color, UseColorPaletteForBackground=0 + ) + pvs.Render(setup.renderView1) + layout1 = pvs.GetLayout() + layout1.SetSize(args.animation_size) + layout1.PreviewMode = args.animation_size + y.renderView1.Update() + + +def set_current_camera_placement(*, y): + y.renderView1.InteractionMode = "2D" + y.renderView1.CameraPosition = [ + 836, + 677, + -4098, + ] + y.renderView1.CameraFocalPoint = [636, 1030, 0.0] + y.renderView1.CameraViewUp = [1.0, 0.0, 0.0] + y.renderView1.CameraParallelScale = 1560 + y.renderView1.Update() + + +def axes_settings(*, view): + # setup.renderView1.Background = [1,0.5,0.2] + view.CenterAxesVisibility = True + view.OrientationAxesVisibility = False + axesGrid = view.AxesGrid + axesGrid.Visibility = True + axesGrid.XTitle = "Z [m]" + axesGrid.YTitle = "X [m]" + + axesGrid.XAxisUseCustomLabels = True + axesGrid.XAxisLabels = [300, 600, 900, 1200] + axesGrid.YAxisUseCustomLabels = True + axesGrid.YAxisLabels = [300, 600, 900, 1200] + + axesGrid.XTitleFontSize = 30 + axesGrid.XLabelFontSize = 30 + axesGrid.YTitleFontSize = 30 + axesGrid.YLabelFontSize = 30 + + axesGrid.XTitleColor = setup.color + axesGrid.XLabelColor = setup.color + axesGrid.YTitleColor = setup.color + axesGrid.YLabelColor = setup.color + axesGrid.GridColor = [0.1, 0.1, 0.1] + view.CenterAxesVisibility = False + view.Update() + + +def time_annotation(*, y): + time = pvs.AnnotateTimeFilter( + guiName="AnnotateTimeFilter1", Scale=1 / 60, Format="Time:{time:g}min" + ) + timedisplay = pvs.Show(time, y.renderView1) + timedisplay.FontSize = 25 + timedisplay.WindowLocation = "Any Location" + timedisplay.FontSize = 30 + timedisplay.Position = [0.31, 0.9] + timedisplay.Color = setup.color + y.renderView1.Update() + + +def text(text_in, position_y, *, view): + sentence = pvs.Text() + sentence.Text = text_in + textDisplay = pvs.Show(sentence, view) + textDisplay.Color = setup.color + textDisplay.WindowLocation = "Any Location" + textDisplay.FontSize = 28 + textDisplay.Position = [0.17, position_y] + + +def last_anim_frame(animation_frame_name): + time_steps = sd_productspvd.TimestepValues + last_time = time_steps[len(time_steps) - 1] + setup.renderView1.ViewTime = last_time + for reader in (sd_productspvd,): + reader.UpdatePipeline(last_time) + pvs.ExportView( + filename=str(pathlib.Path(args.output_path) / animation_frame_name), + view=setup.renderView1, + Rasterize3Dgeometry=False, + GL2PSdepthsortmethod="BSP sorting (slow, best)", + ) + pvs.RenderAllViews() + + +calculator1 = create_new_calculator( + sd_attributespvd, + "UnstructuredGridRepresentation", + '"relative fall velocity"*(-iHat)', + "None", + "None", + "None", + y=setup, + registrationame="Calculator1", +) +scalar_bar("effective radius [um]", y=setup, erLUT=setup) +create_glyph("Glyph1", calculator1, "POINTS", "relative fall velocity", y=setup) +calculator2 = create_new_calculator( + sd_productspvd, + "StructuredGridRepresentation", + "cx*jHat+cy*iHat", + "CELLS", + "Result", + "Magnitude", + True, + True, + y=setup, + registrationame="Calculator2", +) +apply_presets_logscale_opacity_and_update( + y=setup, attdisplay=setup, erLUT=setup, proddisplay=setup +) +create_glyph("Glyph2", calculator2, "CELLS", "Result", True, y=setup) +get_layout(y=setup) +set_current_camera_placement(y=setup) +axes_settings(view=setup.renderView1) +time_annotation(y=setup) +text("Arrows scale with Courant", 0.15, view=setup.renderView1) +text("number C=u·Δt/Δx, reflecting", 0.1, view=setup.renderView1) +text("the grid spacing Δx and Δy.", 0.05, view=setup.renderView1) +if args.animationframename is not None: + last_anim_frame(animation_frame_name=args.animationframename) +scene = pvs.GetAnimationScene() +scene.UpdateAnimationUsingDataTimeSteps() +pvs.Render(setup.renderView1) +pvs.SaveAnimation( + str(pathlib.Path(args.output_path) / args.animationname), + setup.renderView1, + FrameRate=args.framerate, +) +pvs.RenderAllViews() diff --git a/PySDM/source/examples/PySDM_examples/utils/read_vtk_1d.py b/PySDM/source/examples/PySDM_examples/utils/read_vtk_1d.py new file mode 100644 index 0000000000000000000000000000000000000000..25a5d87c3f55e8a2a9a134303b02529543c0459c --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/utils/read_vtk_1d.py @@ -0,0 +1,35 @@ +import platform + +import numpy as np + +if platform.architecture()[0] != "32bit": + import vtk + + # pylint: disable = import-error, no-name-in-module + from vtk.util import numpy_support as VN + + # pylint: enable = import-error, no-name-in-module + + +def readVTK_1d(file): + if platform.architecture()[0] == "32bit": + raise NotImplementedError("Not implemented for system arcitucture 32bit!") + + reader = vtk.vtkXMLUnstructuredGridReader() # pylint: disable = no-member + reader.SetFileName(file) + reader.Update() + + vtk_output = reader.GetOutput() + + z = np.zeros(vtk_output.GetNumberOfPoints()) + for i in range(vtk_output.GetNumberOfPoints()): + z[i] = vtk_output.GetPoint(i)[2] + + data = {} + data["z"] = z + for i in range(vtk_output.GetPointData().GetNumberOfArrays()): + data[vtk_output.GetPointData().GetArrayName(i)] = VN.vtk_to_numpy( + vtk_output.GetPointData().GetArray(i) + ) + + return data diff --git a/PySDM/source/examples/PySDM_examples/utils/widgets/__init__.py b/PySDM/source/examples/PySDM_examples/utils/widgets/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f404bc6fe125d2fc25b224317c80fb6595c50150 --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/utils/widgets/__init__.py @@ -0,0 +1,28 @@ +import sys + +from IPython.display import FileLink, clear_output, display +from ipywidgets import ( + HTML, + Accordion, + Box, + Button, + Checkbox, + Dropdown, + FloatProgress, + FloatSlider, + HBox, + IntProgress, + IntRangeSlider, + IntSlider, + Layout, + Output, + Play, + RadioButtons, + Select, + Tab, + VBox, + interactive_output, + jslink, +) +from PySDM_examples.utils.widgets.freezer import Freezer +from PySDM_examples.utils.widgets.progbar_updater import ProgbarUpdater diff --git a/PySDM/source/examples/PySDM_examples/utils/widgets/freezer.py b/PySDM/source/examples/PySDM_examples/utils/widgets/freezer.py new file mode 100644 index 0000000000000000000000000000000000000000..a255f1a57d43f9683a4b2ec79ae8800bf871800f --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/utils/widgets/freezer.py @@ -0,0 +1,19 @@ +class Freezer: + def __init__(self, widgets): + self.widgets = widgets + + def observe(self, *_): + pass + + @property + def value(self): + return self + + def __enter__(self): + for widget in self.widgets: + widget.disabled = True + return self + + def __exit__(self, *args, **kwargs): + for widget in self.widgets: + widget.disabled = False diff --git a/PySDM/source/examples/PySDM_examples/utils/widgets/progbar_updater.py b/PySDM/source/examples/PySDM_examples/utils/widgets/progbar_updater.py new file mode 100644 index 0000000000000000000000000000000000000000..c50aa75762e617ef65f8b3979506b1c39e11522b --- /dev/null +++ b/PySDM/source/examples/PySDM_examples/utils/widgets/progbar_updater.py @@ -0,0 +1,9 @@ +class ProgbarUpdater: + def __init__(self, progbar, max_steps): + self.max_steps = max_steps + self.steps = 0 + self.progbar = progbar + + def notify(self): + self.steps += 1 + self.progbar.value = 100 * (self.steps / self.max_steps) diff --git a/PySDM/source/examples/README.md b/PySDM/source/examples/README.md new file mode 100644 index 0000000000000000000000000000000000000000..a9b9681590346b96d711ef163235489a2bb28b13 --- /dev/null +++ b/PySDM/source/examples/README.md @@ -0,0 +1,10 @@ +[![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0.html) +[![DOI](https://zenodo.org/badge/199064632.svg)](https://zenodo.org/badge/latestdoi/199064632) + +[![PyPI version](https://badge.fury.io/py/PySDM-examples.svg)](https://pypi.org/project/PySDM-examples) +[![API docs](https://img.shields.io/badge/API_docs-pdoc3-blue.svg)](https://open-atmos.github.io/PySDM/PySDM_examples.html) + +For a list of examples, see [PySDM-examples documentation](https://open-atmos.github.io/PySDM/PySDM_examples.html). + +For information on package development, see [PySDM README](https://github.com/open-atmos/PySDM/blob/main/README.md). + diff --git a/PySDM/source/examples/docs/pysdm_examples_landing.md b/PySDM/source/examples/docs/pysdm_examples_landing.md new file mode 100644 index 0000000000000000000000000000000000000000..f5a81093717c6b1a264a7c4f32abb9d1c969fe6e --- /dev/null +++ b/PySDM/source/examples/docs/pysdm_examples_landing.md @@ -0,0 +1,141 @@ +# Introduction + +pysdm logo + +PySDM examples are engineered as Jupyter Python notebooks supported by auxiliary Python commons + that constitute a separate PySDM-examples Python package which is also + available at PyPI. +The examples have additional dependencies listed in + PySDM-examples package setup.py file. +Running the example Jupyter notebooks requires the PySDM-examples package to be pip-installed. +For installation instructions see [project docs homepage](https://open-atmos.github.io/PySDM). +Note that the Jupyter notebooks themselves are not included in the package wheels, but are included + in the source .tar.gz file on PyPI, and are conveninently browsable on GitHub. +All notebooks feature header cells with badges enabling single-click execution on + Google Colab and on + mybinder.org. +The examples package is also used in the PySDM test suite. + +# Example gallery + +The examples are named referring to the journal paper they aim to reproduce simulations from +(or sometimes just where the inspiration originated from). +The list below groups all examples by the dimensionality and type of the employed modelling framework +("environment" in PySDM nomenclature, which can be: box, parcel, +single-column, 2D prescribed flow), and by the set of physical processes simulated +(condensation, collisional coagulation and breakup, +drop freezing, isotopic fractionation, aqueous chemistry, +seeding, ...). + +## 2D kinematic environment (prescribed-flow) mimicking Sc deck + +The 2D prescribed-flow framework used here can be traced back to the work of + Kessler 1969 (section 3C). + The setup employed in PySDM-examples, which mimics a stratiform cloud deck and features periodic horizontal boundary condition + and vanishing flow at vertical boundaries, was introduced in Morrison and Grabowski (2007) + and later adopted for particle-based simulations in Arabas et al. (2015). + It uses a non-devergent single-eddy flow field resulting in an updraft-downdraft pair in the domain. + The flow field advects two scalar fields in an Eulerian way: water vapour mixing ratio + and dry-air potential temperature. + In PySDM-examples, the Eulerian advection is handled using the PyMPDATA Numba-based + implementation of the MPDATA numerical scheme of Smolarkiewicz (e.g., 2006). + An animation depicting PySDM simulation capturing aerosol collisional processing by warm rain is shown below + (see + the Paraview hello-world HOWTO Jupyter notbook for code generating the simulation and visualisation): +
+ +
+ +Example notebooks: +- `PySDM_examples.Arabas_et_al_2015` + - in-notebook GUI for setting up, running and interactively visualising the 2D kinematic simulations (with an option to export raw data to VTK and netCDF files, as well as to save plots to SVG or PDF): + - "hello world" notebook depicting how to automate using Python the process of loading data and creating animations in Paraview +- `PySDM_examples.Arabas_et_al_2025`: adaptation of the 2D kinematic setup for studying glaciation of the cloud deck by immersion freezing + +## 1D kinematic environment (prescribed-flow, single-column) + +The single-column PySDM environment is a reimplementation of the Met Office KiD framework + introduced in Shipway & Hill 2012. +The framework features a single Eulerian-transported field of water vapour mixing ratio + (vertical profile of potential temperature is fixed). +As in the 2D kinematic framework above, the Eulerian advection is handled by + PyMPDATA. + +Example notebooks: +- `PySDM_examples.Shipway_and_Hill_2012`: reproducing figures from the Shipway & Hill 2012 paper; +- `PySDM_examples.deJong_Mackay_et_al_2023`: reproducing figures from the de Jong et al. 2023 paper where the single-column + framework was used to exemplify operation of the Monte-Carlo collisional breakup scheme in PySDM (scheme introduced in that paper). + +## OD/1D iterative parcel/column environment mimicking removal of precipitation + +This framework uses a parcel model with removal of precipitation for analysis, +iterative equilibration, the isotopic composition of the water vapour and +rain water in a column of air (no Eulerian transport, only iterative passage of a parcel through the column). + +`PySDM_examples.Rozanski_and_Sonntag_1982`: bulk microphysics example (i.e. single super droplet) with +deuterium and heavy-oxygen water isotopologues featured. + +## 0D parcel environment + +The parcel framework implemented in PySDM uses a hydrostatic profile and adiabatic mass and energy conservation + to drive evolution of thermodynamic state and microphysical properties of particles. + +Example notebooks include: +- condensation only + - `PySDM_examples.Arabas_and_Shima_2017`: monodisperse particle spectrum, activation/deactivation cycle + - `PySDM_examples.Yang_et_al_2018`: polydisperse particle spectrum, activation/deactivation cycles + - `PySDM_examples.Abdul_Razzak_Ghan_2000`: polydisperse activation, comparison against GCM parameterisation + - `PySDM_examples.Pyrcel`: polydisperse activation, mimicking example test case from Pyrcel documentation + - `PySDM_examples.Strzabala_2025_BEng`: ParaView visualisation example + - `PySDM_examples.Lowe_et_al_2019`: externally mixed polydisperse size spectrum with surface-active organics case + - `PySDM_examples.Grabowski_and_Pawlowska_2023`: polydisperse activation, focus on ripening + - `PySDM_examples.Jensen_and_Nugent_2017`: polydisperse activation featuring giant CCN +- condensation and aqueous-chemistry + - `PySDM_examples.Kreidenweis_et_al_2003`: Hoppel gap simulation setup (i.e. depiction of evolution of aerosol mass spectrum from a monomodal to bimodal due to aqueous‐phase SO2 oxidation) + - `PySDM_examples.Jaruga_and_Pawlowska_2018`: exploration of numerical convergence using the above Hoppel-gap simulation setup +- freezing + - `PySDM_examples.Spichtinger_et_al_2023`: homogeneous freezing and ice growth (Wegener-Bergeron-Findeisen process) + +The parcel environment is also featured in the PySDM tutorials. + +
+ +
+ +## 0D box environment + +The box environment is void of any spatial or thermodynamic context, it constitutes the most basic framework. + +Example notebooks include: + +- coalescence only: + - `PySDM_examples.Shima_et_al_2009`: using Golovin additive kernel for comparison against analytic solution, featuring interactive in-notebook interface for selecting simulation parameters + - `PySDM_examples.Berry_1967`: examples using geometric, hydrodynamic and electric-field collision kernels +- coalescence and breakup: + - `PySDM_examples.Bieli_et_al_2022`: evolution of moments under collisional growth and breakage + - `PySDM_examples.deJong_Mackay_et_al_2023`: validation of the breakup scheme against analytical solutions from Srivastava 1982 +- immersion freezing only: + - `PySDM_examples.Alpert_and_Knopf_2016`: stochastic immersion freezing with monodisperse vs. lognormal immersed surface areas + - `PySDM_examples.Arabas_et_al_2025`: comparison of time-dependent and singular immersion freezing schemes + +The box environment is also featured in the PySDM tutorials. + +## examples depicting isotope-related formulae (without any simulation context) +- equilibrium isotopic fractionation formulae: + - `PySDM_examples.Lamb_et_al_2017` + - `PySDM_examples.Bolot_et_al_2013` + - `PySDM_examples.Merlivat_and_Nief_1967` + - `PySDM_examples.Van_Hook_1968` + - `PySDM_examples.Graf_et_al_2019` +- Rayleigh fractionation: + - `PySDM_examples.Pierchala_et_al_2022`: reproducing model plots for a triple-isotope lab study, including kinetic fractionation + - `PySDM_examples.Gonfiantini_1986`: flat-surface evaporation at different humidities for D and 18O +- isotopic relaxation timescale: + - `PySDM_examples.Miyake_et_al_1968`: incl. comparison of different ventilation parameterisations + - `PySDM_examples.Bolin_1958` +- below-cloud kinetic fractionation: + - `PySDM_examples.Gedzelman_and_Arnold_1994` + +## examples depicting extraterrestrial clouds (formulae-only, no simulations yet) +- Titan (methane clouds): + - `PySDM_examples.Toon_et_al_1980` diff --git a/PySDM/source/examples/pyproject.toml b/PySDM/source/examples/pyproject.toml new file mode 100644 index 0000000000000000000000000000000000000000..5238cfe3e7620c8345edd6382c82cf1c76a1db70 --- /dev/null +++ b/PySDM/source/examples/pyproject.toml @@ -0,0 +1,7 @@ +[tool.setuptools_scm] +root = ".." +local_scheme = "no-local-version" +version_scheme = "post-release" + +[build-system] +requires = ['setuptools==80.9.0', 'setuptools-scm==9.0.1'] diff --git a/PySDM/source/examples/setup.py b/PySDM/source/examples/setup.py new file mode 100644 index 0000000000000000000000000000000000000000..95e7045192624c6a851a462f94b5c3b7b65e02b9 --- /dev/null +++ b/PySDM/source/examples/setup.py @@ -0,0 +1,68 @@ +import os +import re + +from setuptools import find_packages, setup + + +def get_long_description(): + """returns contents of the pdoc landing site with pdoc links converted into URLs""" + with open("docs/pysdm_examples_landing.md", "r", encoding="utf8") as file: + pdoc_links = re.compile( + r"(`)([\w\d_-]*).([\w\d_-]*)(`)", re.MULTILINE | re.UNICODE + ) + return pdoc_links.sub( + r'\3', + file.read(), + ) + + +CI = "CI" in os.environ + +setup( + name="pysdm-examples", + description="PySDM usage examples reproducing results from literature " + "and depicting how to use PySDM from Python Jupyter notebooks", + install_requires=[ + "PySDM", + "PyMPDATA", + "open-atmos-jupyter-utils", + "pystrict", + "matplotlib", + "joblib", + "ipywidgets", + "seaborn", + "numdifftools", + "vtk", + "pyrcel", + "pyvinecopulib", + "networkx", + ], + extras_require={ + "CI_version_pins": [ + "PySDM[CI_version_pins]", + "PyMPDATA==1.7.0", + "open-atmos-jupyter-utils==1.3.0", + "pystrict==1.3", + "matplotlib!=3.9.1", + "joblib==1.5.3", + "ipywidgets==8.1.7", + "seaborn==0.13.2", + "numdifftools==0.9.42", + "vtk==9.5.2", + "pyrcel==1.3.4", + "pyvinecopulib==0.7.3", + ] + }, + author="https://github.com/open-atmos/PySDM/graphs/contributors", + author_email="sylwester.arabas@agh.edu.pl", + long_description=get_long_description(), + long_description_content_type="text/markdown", + url="https://github.com/open-atmos/PySDM", + license="GPL-3.0", + packages=find_packages(include=["PySDM_examples", "PySDM_examples.*"]), + project_urls={ + "Tracker": "https://github.com/open-atmos/PySDM/issues", + "Documentation": "https://open-atmos.github.io/PySDM/PySDM_examples", + "Source": "https://github.com/open-atmos/PySDM/tree/main/examples", + }, +) diff --git a/PySDM/source/pyproject.toml b/PySDM/source/pyproject.toml new file mode 100644 index 0000000000000000000000000000000000000000..f9f5147e0275995d95cd784cf8837acdafbb6407 --- /dev/null +++ b/PySDM/source/pyproject.toml @@ -0,0 +1,43 @@ +[tool.isort] +profile = "black" + +[tool.setuptools_scm] +local_scheme = "no-local-version" +version_scheme = "post-release" + +[tool.setuptools.packages.find] +include = ["PySDM", "PySDM*"] + +[build-system] +requires = ['setuptools==80.9.0', 'setuptools-scm==9.0.1'] +build-backend = "setuptools.build_meta" + +[project] +name = "pysdm" +description = "Pythonic particle-based (super-droplet) warm-rain/aqueous-chemistry cloud microphysics package with box, parcel & 1D/2D prescribed-flow examples in Python, Julia and Matlab" +readme = "README.md" +keywords = ["physics-simulation", "monte-carlo-simulation", "gpu-computing", + "atmospheric-modelling", "particle-system", "numba", "thrust", + "nvrtc", "pint", "atmospheric-physics", + ] +license = "GPL-3.0-only" +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Science/Research", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Topic :: Scientific/Engineering", + "Topic :: Scientific/Engineering :: Atmospheric Science", + "Topic :: Scientific/Engineering :: Mathematics", + "Topic :: Scientific/Engineering :: Physics", + "Topic :: Software Development :: Libraries", +] +requires-python = ">= 3.8" +authors = [ + {name = "https://github.com/open-atmos/PySDM/graphs/contributors", email = "sylwester.arabas@agh.edu.pl"} +] +dynamic = ["version", "dependencies", "optional-dependencies"] +[project.urls] +Tracker = "https://github.com/open-atmos/PySDM/issues" +Documentation = "https://open-atmos.github.io/PySDM" +Source = "https://github.com/open-atmos/PySDM" diff --git a/PySDM/source/setup.py b/PySDM/source/setup.py new file mode 100644 index 0000000000000000000000000000000000000000..f57562c73e1629c716edccabc8f4c078f177954d --- /dev/null +++ b/PySDM/source/setup.py @@ -0,0 +1,62 @@ +# pylint:disable=missing-module-docstring +import os +import sys + +from setuptools import setup + +CI = "CI" in os.environ + +dependencies = [ + "ThrustRTC>=0.3.20", + "CURandRTC>=0.1.2", + "numba>=0.51.2", + # TODO #1344: (numpy 2.0.0 incompatibility in https://github.com/bjodah/chempy/issues/234) + "numpy" + + ( + { + 8: "==1.24.4", + 9: "==1.24.4", + 10: "==1.24.4", + 11: "==1.24.4", + 12: "==1.26.4", + 13: "==1.26.4", + }[sys.version_info.minor] + if CI + else "" + ), + "Pint", + "chempy", + "scipy" + + ( + { + 8: "==1.10.1", + 9: "==1.10.1", + 10: "==1.10.1", + 11: "==1.10.1", + 12: "==1.13.0", + 13: "==1.13.0", + }[sys.version_info.minor] + if CI + else "" + ), + "pyevtk", + "pyparsing" + ("==3.2.5" if CI else ""), +] + +optional_dependencies = { + "unit-tests": ["pytest", "pytest-timeout", "matplotlib!=3.9.1"], + "nonunit-tests": ["pytest", "PySDM-examples", "PyPartMC"], + "CI_version_pins": [ + "PyPartMC==1.7.2", + "numba==0.60.0", + "CURandRTC==0.1.7", + "Pint==0.21.1", + "chempy==0.8.3", + "pyevtk==1.6.0", + ], +} + +setup( + install_requires=dependencies, + extras_require=optional_dependencies, +) diff --git a/PySDM/source/tests/__init__.py b/PySDM/source/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/examples_tests/__init__.py b/PySDM/source/tests/examples_tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/examples_tests/conftest.py b/PySDM/source/tests/examples_tests/conftest.py new file mode 100644 index 0000000000000000000000000000000000000000..38cda14bb14f67eb20acd2cb03d4f85a8e445717 --- /dev/null +++ b/PySDM/source/tests/examples_tests/conftest.py @@ -0,0 +1,238 @@ +# pylint: disable=missing-module-docstring +import os +import pathlib +import re +import ast +import importlib +import inspect +import pkgutil + + +from .. import smoke_tests + + +class NotebookVarExtractor(ast.NodeVisitor): + def __init__(self): + self.paths = [] + + def visit_Call(self, node): + if isinstance(node.func, ast.Name) and node.func.id == "notebook_vars": + for arg in node.args: + self._maybe_add_path_expr(arg) + for kw in node.keywords: + self._maybe_add_path_expr(kw.value) + self.generic_visit(node) + + def _maybe_add_path_expr(self, expr): + """Extract a path-like expression string from AST.""" + expr_str = ast.unparse(expr) + if "Path(" in expr_str: + self.paths.append(expr_str) + + +def extract_path_expressions_from_module(mod): + try: + source = inspect.getsource(mod) + except (OSError, TypeError): + return [] + + tree = ast.parse(source) + extractor = NotebookVarExtractor() + extractor.visit(tree) + + return extractor.paths + + +def evaluate_path_expr(expr_str, module_globals): + """Evaluate the path expression using the module's globals.""" + local_ctx = {"Path": pathlib.Path} + value = eval(expr_str, {**module_globals, **local_ctx}) # pylint: disable=eval-used + return value.resolve() if isinstance(value, pathlib.Path) else None + + +def iter_submodule_names(module): + """Yield names of all submodules recursively without importing.""" + if not hasattr(module, "__path__"): + return + for _, name, _ in pkgutil.walk_packages(module.__path__, module.__name__ + "."): + yield name + + +def find_modules_using_notebook_vars(module): + names = [] + for name in iter_submodule_names(module): + filepath = importlib.util.find_spec(name).origin + with open(filepath, "r", encoding="utf-8") as f: + source = f.read() + if "notebook_vars" in source: + names.append(name) + return names + + +SMOKE_TEST_COVERED_PATHS = [] +for mod_name in find_modules_using_notebook_vars(smoke_tests): + submodule = importlib.import_module(mod_name) + exprs = extract_path_expressions_from_module(submodule) + for path_expr in exprs: + submodule_path = evaluate_path_expr(path_expr, vars(submodule)) + if submodule_path: + SMOKE_TEST_COVERED_PATHS.append(submodule_path) + + +# https://stackoverflow.com/questions/7012921/recursive-grep-using-python +def findfiles(path, regex): + reg_obj = re.compile(regex) + res = [] + for root, _, fnames in os.walk(path): + for fname in fnames: + if reg_obj.match(fname): + res.append(os.path.join(root, fname)) + return res + + +TEST_SUITES = { + "isotopes_chemistry_extraterrestrial": [ + "Bolot_et_al_2013", + "Merlivat_and_Nief_1967", + "Van_Hook_1968", + "Pierchala_et_al_2022", + "Gedzelman_and_Arnold_1994", + "Graf_et_al_2019", + "Lamb_et_al_2017", + "Miyake_et_al_1968", + "Rozanski_and_Sonntag_1982", + "Bolin_1958", + "Stewart_1975", + "Kinzer_And_Gunn_1951", + "Pruppacher_and_Rasmussen_1979", + "Fisher_1991", + "Jouzel_and_Merlivat_1984", + "Jaruga_and_Pawlowska_2018", + "Kreidenweis_et_al_2003", + "Toon_et_al_1980", + ], + "condensation_a": [ + "Lowe_et_al_2019", + "Singer_Ward", + "Rogers_1975", + ], + "condensation_b": [ + "Abdul_Razzak_Ghan_2000", + "Arabas_and_Shima_2017", + "Pyrcel", + "Yang_et_al_2018", + ], + "condensation_c": [ + "Grabowski_and_Pawlowska_2023", + "Jensen_and_Nugent_2017", + "Abade_and_Albuquerque_2024", + ], + "coagulation_freezing": [ + "Berry_1967", + "Shima_et_al_2009", + "Alpert_and_Knopf_2016", + "Ervens_and_Feingold_2012", + "Niedermeier_et_al_2014", + "Spichtinger_et_al_2023", + "Ware_et_al_2025", + "Matsushima_et_al_2023", + ], + "multi-process_a": [ + "Arabas_et_al_2015", + "_HOWTOs", + "Strzabala_2025_BEng", + ], + "multi-process_b": [ + "Arabas_et_al_2025", + ], + "multi-process_c_breakup": [ + "Bartman_2020_MasterThesis", + "Bieli_et_al_2022", + "deJong_Mackay_et_al_2023", + "Srivastava_1982", + ], + "multi-process_d": [ + "Bartman_et_al_2021", + ], + "multi-process_e": [ + "deJong_Azimi", + "Bulenok_2023_MasterThesis", + "Morrison_and_Grabowski_2007", + "Shipway_and_Hill_2012", + "seeding", + "utils", + "Zaba_et_al", + "Gonfiantini_1986", + ], +} + + +def get_selected_test_paths(suite_name, paths): + if suite_name is None: + return paths + + cases = TEST_SUITES[suite_name] + + result = [] + for path in paths: + for case in cases: + path = pathlib.Path(path) + if case in path.parts: + result.append(path) + + return result + + +def pytest_addoption(parser): + parser.addoption("--suite", action="append") + + +def pytest_generate_tests(metafunc): + suite_args = metafunc.config.option.suite or [] + + # Support both --suite name1 --suite name2 and --suite name1,name2 + suite_names = [] + for arg in suite_args: + suite_names.extend([s.strip() for s in arg.split(",") if s.strip()]) + + pysdm_examples_abs_path = ( + pathlib.Path(__file__) + .parent.parent.parent.absolute() + .joinpath("examples") + .joinpath("PySDM_examples") + ) + if "notebook_filename" in metafunc.fixturenames: + notebook_paths = [ + path + for path in findfiles(pysdm_examples_abs_path, r".*\.ipynb$") + if ".ipynb_checkpoints" not in str(path) + ] + selected_paths = set() + for suite_name in suite_names: + selected_paths.update( + set(get_selected_test_paths(suite_name, notebook_paths)) + ) + + # Remove duplicates and any smoke-tested paths + selected_paths = selected_paths - set(SMOKE_TEST_COVERED_PATHS) + metafunc.parametrize( + "notebook_filename", + selected_paths, + ids=[str(path) for path in selected_paths], + ) + + if "example_filename" in metafunc.fixturenames: + examples_paths = findfiles( + pysdm_examples_abs_path, + r".*\.(py)$", + ) + selected_paths = set() + for suite_name in suite_names: + selected_paths.update( + set(get_selected_test_paths(suite_name, examples_paths)) + ) + metafunc.parametrize( + "example_filename", + selected_paths, + ids=[str(path) for path in selected_paths], + ) diff --git a/PySDM/source/tests/examples_tests/test_run_examples.py b/PySDM/source/tests/examples_tests/test_run_examples.py new file mode 100644 index 0000000000000000000000000000000000000000..044d602e2d10add0e0305d1131182768c78b3112 --- /dev/null +++ b/PySDM/source/tests/examples_tests/test_run_examples.py @@ -0,0 +1,11 @@ +# pylint: disable=missing-module-docstring +import pathlib + + +def test_run_examples(example_filename): + if pathlib.Path(example_filename).name == "__init__.py": + return + with open(example_filename, encoding="utf8") as f: + code = f.read() + if not code.startswith("#!/usr/bin/env pvpython"): + exec(code, {"__name__": "__main__"}) # pylint: disable=exec-used diff --git a/PySDM/source/tests/examples_tests/test_run_notebooks.py b/PySDM/source/tests/examples_tests/test_run_notebooks.py new file mode 100644 index 0000000000000000000000000000000000000000..026173cff53e045bd16a526530182416a5ed5174 --- /dev/null +++ b/PySDM/source/tests/examples_tests/test_run_notebooks.py @@ -0,0 +1,6 @@ +# pylint: disable=missing-module-docstring +from ..devops_tests.test_notebooks import test_run_notebooks as _impl + + +def test_run_notebooks(notebook_filename, tmp_path): + _impl(notebook_filename, tmp_path) diff --git a/PySDM/source/tests/examples_tests/test_tests_completeness.py b/PySDM/source/tests/examples_tests/test_tests_completeness.py new file mode 100644 index 0000000000000000000000000000000000000000..a7b66b4a0b1ece5f83e5da171193dc66cf69aa33 --- /dev/null +++ b/PySDM/source/tests/examples_tests/test_tests_completeness.py @@ -0,0 +1,57 @@ +# pylint: disable=missing-module-docstring +import pathlib + +import yaml + +from .conftest import TEST_SUITES, findfiles, get_selected_test_paths + + +def test_all_cases_in_testsuites(): + """raise error, e.g., if a newly added example is not within TEST_SUITES dict""" + tmp = findfiles( + pathlib.Path(__file__) + .parent.parent.parent.absolute() + .joinpath("examples") + .joinpath("PySDM_examples"), + r".*\.(py|ipynb)$", + ) + all_files = list( + filter( + lambda x: pathlib.Path(x).name != "__init__.py", + tmp, + ) + ) + + selected_paths_set = set() + for suite_name in TEST_SUITES: + selected_paths_set.update( + map(str, get_selected_test_paths(suite_name, all_files)) + ) + + assert len(all_files) > 0 + assert selected_paths_set == set(all_files) + + +def test_no_cases_in_multiple_testsuites(): + """raise an error if an example is featured in multiple TEST_SUITES""" + flattened_suites = sum(list(TEST_SUITES.values()), []) + + assert len(set(flattened_suites)) == len(flattened_suites) + + +def test_all_test_suites_are_on_ci(): + workflow_file_path = ( + pathlib.Path(__file__) + .parent.parent.parent.absolute() + .joinpath(".github") + .joinpath("workflows") + .joinpath("tests.yml") + ) + with open(workflow_file_path, "r", encoding="utf8") as workflow_file: + d = yaml.safe_load(workflow_file) + ci_test_suites_lst = d["jobs"]["examples"]["strategy"]["matrix"]["test-suite"] + ci_test_suites_set = set(ci_test_suites_lst) + + assert len(ci_test_suites_set) > 0 + assert len(ci_test_suites_lst) == len(ci_test_suites_set) + assert ci_test_suites_set == set(TEST_SUITES.keys()) diff --git a/PySDM/source/tests/smoke_tests/__init__.py b/PySDM/source/tests/smoke_tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/box/__init__.py b/PySDM/source/tests/smoke_tests/box/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/box/alpert_and_knopf_2016/__init__.py b/PySDM/source/tests/smoke_tests/box/alpert_and_knopf_2016/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/box/alpert_and_knopf_2016/test_ak16_fig_1.py b/PySDM/source/tests/smoke_tests/box/alpert_and_knopf_2016/test_ak16_fig_1.py new file mode 100644 index 0000000000000000000000000000000000000000..3422bec9b41cdc6de1164d7ca676950004a01600 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/box/alpert_and_knopf_2016/test_ak16_fig_1.py @@ -0,0 +1,99 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest +from matplotlib import pyplot +from PySDM_examples.Alpert_and_Knopf_2016 import Table1, simulation + +from PySDM.physics import si + +n_runs_per_case = 3 + + +@pytest.mark.parametrize("multiplicity", (1, 2, 10)) +def test_ak16_fig_1(multiplicity, plot=False): # pylint: disable=too-many-locals + # Arrange + dt = 1 * si.s + total_time = 6 * si.min + + # dummy multipliers (multiplied and then divided by) + dv = ( + 1 * si.cm**3 + ) # will become used if coalescence or other processes are turned on + droplet_volume = 1 * si.um**3 # ditto + + cases = Table1(volume=dv) + + # Act + output = {} + + for key in ("Iso3", "Iso4", "Iso1", "Iso2"): + case = cases[key] + output[key] = [] + for i in range(n_runs_per_case): + number_of_real_droplets = case["ISA"].norm_factor * dv + n_sd = number_of_real_droplets / multiplicity + assert int(n_sd) == n_sd + n_sd = int(n_sd) + + data, _ = simulation( + constants={"J_HET": 1e3 / si.cm**2 / si.s}, + seed=i, + n_sd=n_sd, + time_step=dt, + volume=dv, + spectrum=case["ISA"], + droplet_volume=droplet_volume, + multiplicity=multiplicity, + total_time=total_time, + number_of_real_droplets=number_of_real_droplets, + ) + output[key].append(data) + + # Plot + for key, output_item in output.items(): + for run in range(n_runs_per_case): + label = ( + f"{key}: σ=ln({int(cases[key]['ISA'].s_geom)})," + f"N={int(cases[key]['ISA'].norm_factor * dv)}" + ) + pyplot.step( + dt / si.min * np.arange(len(output_item[run])), + output_item[run], + label=label if run == 0 else None, + color=cases[key]["color"], + linewidth=0.666, + ) + output_item.append(np.mean(np.asarray(output_item), axis=0)) + pyplot.step( + dt / si.min * np.arange(len(output_item[-1])), + output_item[-1], + color=cases[key]["color"], + linewidth=1.666, + ) + + pyplot.legend() + pyplot.yscale("log") + pyplot.ylim(1e-2, 1) + pyplot.xlim(0, total_time / si.min) + pyplot.xlabel("t / min") + pyplot.ylabel("$f_{ufz}$") + pyplot.gca().set_box_aspect(1) + if plot: + pyplot.show() + + # Assert + np.testing.assert_array_less( + output["Iso3"][-1][1 : int(1 * si.min / dt)], + output["Iso1"][-1][1 : int(1 * si.min / dt)], + ) + np.testing.assert_array_less( + output["Iso1"][-1][int(2 * si.min / dt) :], + output["Iso3"][-1][int(2 * si.min / dt) :], + ) + np.testing.assert_array_less( + output["Iso2"][int(0.5 * si.min / dt) :], + output["Iso1"][int(0.5 * si.min / dt) :], + ) + for out in output.values(): + np.testing.assert_array_less(1e-3, out[-1][: int(0.25 * si.min / dt)]) + np.testing.assert_array_less(out[-1][:], 1 + 1e-10) diff --git a/PySDM/source/tests/smoke_tests/box/alpert_and_knopf_2016/test_frozen_fraction.py b/PySDM/source/tests/smoke_tests/box/alpert_and_knopf_2016/test_frozen_fraction.py new file mode 100644 index 0000000000000000000000000000000000000000..ee0188e7066e05cc6fb82b6f850043a7c9af67ff --- /dev/null +++ b/PySDM/source/tests/smoke_tests/box/alpert_and_knopf_2016/test_frozen_fraction.py @@ -0,0 +1,28 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +from PySDM_examples.Arabas_et_al_2025.frozen_fraction import FrozenFraction + +from PySDM.physics import constants_defaults as const + +TOTAL_PARTICLE_NUMBER = 1 +DROPLET_VOLUME = 1 +VOLUME = 1 +FF = FrozenFraction( + volume=VOLUME, + total_particle_number=TOTAL_PARTICLE_NUMBER, + droplet_volume=DROPLET_VOLUME, + rho_w=const.rho_w, +) + + +class TestFrozenFraction: + @staticmethod + def test_qi2ff(): + all_frozen = FF.qi2ff( + TOTAL_PARTICLE_NUMBER * DROPLET_VOLUME * const.rho_w / VOLUME + ) + np.testing.assert_almost_equal(all_frozen, 1) + + @staticmethod + def test_ff2qi(): + assert FF.qi2ff(FF.ff2qi(1)) == 1 diff --git a/PySDM/source/tests/smoke_tests/box/berry_1967/__init__.py b/PySDM/source/tests/smoke_tests/box/berry_1967/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/box/berry_1967/test_coalescence.py b/PySDM/source/tests/smoke_tests/box/berry_1967/test_coalescence.py new file mode 100644 index 0000000000000000000000000000000000000000..5854f2d49fc3041b2eb123ba014c9e0d06065602 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/box/berry_1967/test_coalescence.py @@ -0,0 +1,93 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import struct + +import numpy as np +import pytest +from PySDM_examples.Berry_1967.settings import Settings + +from PySDM.backends import ThrustRTC +from PySDM.builder import Builder +from PySDM.dynamics import Coalescence +from PySDM.dynamics.collisions.collision_kernels import ( + Electric, + Geometric, + Golovin, + Hydrodynamic, +) +from PySDM.environments import Box +from PySDM.initialisation.sampling.spectral_sampling import ConstantMultiplicity + + +@pytest.mark.parametrize("croupier", ("local", "global")) +@pytest.mark.parametrize("adaptive", (True, False)) +@pytest.mark.parametrize("kernel", (Geometric(), Electric(), Hydrodynamic())) +def test_coalescence(backend_class, kernel, croupier, adaptive): + if backend_class == ThrustRTC and croupier == "local": + pytest.skip("TODO #358") + if backend_class == ThrustRTC and adaptive and croupier == "global": + pytest.skip("TODO #329") + # Arrange + s = Settings() + s.formulae.seed = 0 + steps = [0, 800] + + env = Box(dt=s.dt, dv=s.dv) + builder = Builder( + n_sd=s.n_sd, backend=backend_class(formulae=s.formulae), environment=env + ) + attributes = {} + attributes["volume"], attributes["multiplicity"] = ConstantMultiplicity( + s.spectrum + ).sample_deterministic(s.n_sd) + builder.add_dynamic( + Coalescence(collision_kernel=kernel, croupier=croupier, adaptive=adaptive) + ) + particulator = builder.build(attributes) + + volumes = {} + + # Act + for step in steps: + particulator.run(step - particulator.n_steps) + volumes[particulator.n_steps] = particulator.attributes["volume"].to_ndarray() + + # Assert + x_max = 0 + for volume in volumes.values(): + assert x_max < np.amax(volume) + x_max = np.amax(volume) + + +@pytest.mark.xfail(struct.calcsize("P") * 8 == 32, reason="32 bit", strict=False) +def test_coalescence_2_sd(backend_class): + # Arrange + s = Settings() + s.kernel = Golovin(b=1.5e12) + s.formulae.seed = 0 + steps = [0, 200] + s.n_sd = 2 + + env = Box(dt=s.dt, dv=s.dv) + builder = Builder( + n_sd=s.n_sd, backend=backend_class(formulae=s.formulae), environment=env + ) + attributes = {} + attributes["volume"], attributes["multiplicity"] = ConstantMultiplicity( + s.spectrum + ).sample_deterministic(s.n_sd) + builder.add_dynamic(Coalescence(collision_kernel=s.kernel, adaptive=False)) + particulator = builder.build(attributes) + + volumes = {} + + # Act + for step in steps: + particulator.run(step - particulator.n_steps) + volumes[particulator.n_steps] = particulator.attributes["volume"].to_ndarray() + + # Assert + x_max = 0 + for volume in volumes.values(): + assert x_max < np.amax(volume) + x_max = np.amax(volume) + assert particulator.attributes.super_droplet_count == 1 diff --git a/PySDM/source/tests/smoke_tests/box/bieli_et_al_2022/__init__.py b/PySDM/source/tests/smoke_tests/box/bieli_et_al_2022/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/box/bieli_et_al_2022/test_moments.py b/PySDM/source/tests/smoke_tests/box/bieli_et_al_2022/test_moments.py new file mode 100644 index 0000000000000000000000000000000000000000..79fa5b8240dd7fe73d9fe1f537b58c51afcaa6c2 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/box/bieli_et_al_2022/test_moments.py @@ -0,0 +1,69 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import os + +import numpy as np +from matplotlib import pyplot +from PySDM_examples.Bieli_et_al_2022.settings import Settings +from PySDM_examples.Bieli_et_al_2022.simulation import make_core + +from PySDM import Formulae +from PySDM.physics import si + + +def test_moments(plot=False): + # arrange + settings = Settings(Formulae(fragmentation_function="Feingold1988")) + if "CI" in os.environ: + settings.n_sd = 10 + else: + settings.n_sd = 100 + + moments = { + i: np.zeros((3, len(settings.output_steps))) + for i, _ in enumerate(settings.coal_effs) + } + + # act + for i, coal_eff in enumerate(settings.coal_effs): + particulator = make_core(settings, coal_eff) + + j = 0 + for step in settings.output_steps: + particulator.run(step - particulator.n_steps) + moments[i][:, j] = [ + particulator.products["M0"].get()[0], + particulator.products["M1"].get()[0], + particulator.products["M2"].get()[0], + ] + j += 1 + moments[i][1, :] *= settings.rho / si.g + moments[i][2, :] *= settings.rho**2 / si.g**2 + moments[i] *= 1 / settings.dv * si.cm**3 + + # plot + __, axs = pyplot.subplots(nrows=1, ncols=3, figsize=(8, 3)) + for i, _ in enumerate(settings.coal_effs): + for moment in range(3): + axs[moment].plot(settings.output_steps, moments[i][moment, :]) + + axs[0].set_xlabel("time (s)") + axs[1].set_xlabel("time (s)") + axs[2].set_xlabel("time (s)") + axs[0].set_ylabel("$M_0$ (1/cm$^3$)") + axs[1].set_ylabel("$M_1$ (g/cm$^3$)") + axs[2].set_ylabel("$M_2$ (g$^2$/cm$^3$)") + + axs[0].set_ylim([0, 2e4]) + axs[1].set_ylim([0, 5e-6]) + axs[2].set_ylim([0, 2e-14]) + pyplot.legend(["E_c=0.8", "E_c=0.9", "E_c=1.0"]) + pyplot.tight_layout() + + if plot: + pyplot.show() + + # assert on mass conservation + tol = 1e-10 + for i, _ in enumerate(settings.coal_effs): + assert moments[i][1, -1] <= moments[i][1, 0] * (1 + tol) + assert moments[i][1, -1] >= moments[i][1, 0] * (1 - tol) diff --git a/PySDM/source/tests/smoke_tests/box/dejong_and_mackay_et_al_2023/__init__.py b/PySDM/source/tests/smoke_tests/box/dejong_and_mackay_et_al_2023/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/box/dejong_and_mackay_et_al_2023/test_collision.py b/PySDM/source/tests/smoke_tests/box/dejong_and_mackay_et_al_2023/test_collision.py new file mode 100644 index 0000000000000000000000000000000000000000..3612361838ded8349787467b3bd593dfb9c4ebca --- /dev/null +++ b/PySDM/source/tests/smoke_tests/box/dejong_and_mackay_et_al_2023/test_collision.py @@ -0,0 +1,49 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import os + +from matplotlib import pyplot +from PySDM_examples.deJong_Mackay_et_al_2023 import ( + Settings0D, + run_box_breakup, + run_box_NObreakup, +) + + +def test_collision(backend_class, plot=False): + settings = Settings0D(warn_overflows=False) + t_steps = [0, 100, 200] + if "CI" in os.environ: + settings.n_sd = 10 + else: + settings.n_sd = 100 + + (x1, y1, __) = run_box_NObreakup(settings, t_steps, backend_class) + res2 = run_box_breakup(settings, t_steps, backend_class) + x2, y2 = res2.x, res2.y + + for step in settings.output_steps: + pyplot.step( + x=x1, + y=y1[step], + where="post", + label=f"NO breakup, t = {step*settings.dt}s", + ) + pyplot.step( + x=x2, + y=y2[step], + where="post", + label=f"WITH breakup, t = {step*settings.dt}s", + ) + pyplot.xscale("log") + pyplot.xlabel("radius (um)") + pyplot.ylabel("dm/dlnr") + pyplot.legend([0, 1, 2]) + pyplot.title(backend_class.__name__) + + if plot: + pyplot.show() + else: + pyplot.clf() + + # TODO #744: add asserts here to check whether stuff is correct + assert True diff --git a/PySDM/source/tests/smoke_tests/box/dejong_and_mackay_et_al_2023/test_fig_6.py b/PySDM/source/tests/smoke_tests/box/dejong_and_mackay_et_al_2023/test_fig_6.py new file mode 100644 index 0000000000000000000000000000000000000000..3e94acc3fc8c4f77436879d5f75daf9f0beade50 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/box/dejong_and_mackay_et_al_2023/test_fig_6.py @@ -0,0 +1,105 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import matplotlib +import numpy as np +import pytest +from matplotlib import pyplot +from PySDM_examples.deJong_Mackay_et_al_2023 import Settings0D, run_box_breakup + +from PySDM.backends import Numba, ThrustRTC +from PySDM.dynamics.collisions.breakup_fragmentations import AlwaysN +from PySDM.dynamics.collisions.coalescence_efficiencies import ConstEc, Straub2010Ec +from PySDM.physics import si + +R_MIN = 0.1 * si.um +EC_VALS = [1.0, 0.9, 0.8] +BINS = 32 +N_SD = 2**10 + + +@pytest.mark.parametrize( + "backend_class", + (Numba, pytest.param(ThrustRTC, marks=pytest.mark.xfail(strict=True))), # TODO #987 +) +def test_fig_6_reduced_resolution(backend_class, plot=False): + # arrange + settings = Settings0D(fragmentation=AlwaysN(n=8), seed=44) + settings.n_sd = N_SD + settings.radius_bins_edges = np.logspace( + np.log10(10 * si.um), np.log10(10000 * si.um), num=BINS, endpoint=True + ) + settings.warn_overflows = False + settings._steps = [200] # pylint: disable=protected-access + data_x = {} + data_y = {} + + # act + lbl = "initial" + res = run_box_breakup(settings, [0], backend_class) + data_x[lbl], data_y[lbl] = res.x, res.y + + for i, ec_value in enumerate(EC_VALS): + settings.coal_eff = ConstEc(Ec=ec_value) + lbl = "Ec = " + str(ec_value) + if ec_value == 1.0: + lbl = "Ec = 1.0" + res = run_box_breakup(settings, backend_class=backend_class) + data_x[lbl], data_y[lbl] = res.x, res.y + + lbl = "Straub 2010" + settings.coal_eff = Straub2010Ec() + res = run_box_breakup(settings, backend_class=backend_class) + data_x[lbl], data_y[lbl] = res.x, res.y + + # plot + lbl = "initial" + pyplot.step( + data_x[lbl], data_y[lbl][0] * settings.rho, color="k", linestyle="--", label=lbl + ) + for i, ec_value in enumerate(EC_VALS): + lbl = "Ec = " + str(ec_value) + if ec_value == 1.0: + lbl = "Ec = 1.0" + pyplot.step( + data_x[lbl], + data_y[lbl][0] * settings.rho, + color=matplotlib.colormaps["viridis"](i / len(EC_VALS)), + label=lbl if lbl not in pyplot.gca().get_legend_handles_labels()[1] else "", + ) + + lbl = "Straub 2010" + pyplot.step( + data_x[lbl], + data_y[lbl][0] * settings.rho, + color="m", + label=lbl if lbl not in pyplot.gca().get_legend_handles_labels()[1] else "", + ) + + pyplot.xscale("log") + pyplot.xlabel("particle radius (um)") + pyplot.ylabel("dm/dlnR (kg/m$^3$ / unit(ln R)") + pyplot.legend() + pyplot.title(backend_class.__name__) + if plot: + pyplot.show() + else: + pyplot.clf() + + # assert + for datum_x in data_x.values(): + np.testing.assert_array_equal(data_x["initial"], datum_x) + + peaks_expected = { + "initial": (30, 0.017), + "Ec = 1.0": (1600, 0.015), + "Ec = 0.9": (200, 0.01), + "Ec = 0.8": (20, 0.0125), + "Straub 2010": (200, 0.0125), + } + + for lbl, x_y in peaks_expected.items(): + print(lbl) + peak = np.argmax(data_y[lbl][0]) + np.testing.assert_approx_equal(data_x[lbl][peak], x_y[0], significant=1) + np.testing.assert_approx_equal( + data_y[lbl][0][peak] * settings.rho, x_y[1], significant=1 + ) diff --git a/PySDM/source/tests/smoke_tests/box/dejong_and_mackay_et_al_2023/test_fig_7.py b/PySDM/source/tests/smoke_tests/box/dejong_and_mackay_et_al_2023/test_fig_7.py new file mode 100644 index 0000000000000000000000000000000000000000..f1cf7ced89681b0c446f28437e64eb0f3753ee33 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/box/dejong_and_mackay_et_al_2023/test_fig_7.py @@ -0,0 +1,192 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import matplotlib +import numpy as np +import pytest +from matplotlib import pyplot +from PySDM_examples.deJong_Mackay_et_al_2023 import Settings0D, run_box_breakup + +from PySDM.backends import Numba, ThrustRTC +from PySDM.dynamics.collisions.breakup_fragmentations import AlwaysN, Exponential +from PySDM.dynamics.collisions.coalescence_efficiencies import ConstEc +from PySDM.physics import si + +CMAP = matplotlib.colormaps["viridis"] +N_SD = 2**12 +DT = 1 * si.s + + +def bins_edges(num): + return np.logspace( + np.log10(5e0 * si.um), np.log10(5e3 * si.um), num=num, endpoint=True + ) + + +class TestFig7: + @staticmethod + @pytest.mark.parametrize( + "backend_class", + ( + Numba, + pytest.param(ThrustRTC, marks=pytest.mark.xfail(strict=True)), + ), # TODO #987 + ) + def test_fig_7a(backend_class, plot=False): + # arrange + settings0 = Settings0D(seed=44) + settings0.n_sd = N_SD + settings0.radius_bins_edges = bins_edges(32) + + nf_vals = [1, 4, 16] + data_x = {} + data_y = {} + + # act + lbl = "initial" + res = run_box_breakup(settings0, [0], backend_class) + data_x[lbl], data_y[lbl] = res.x, res.y + for i, nf_val in enumerate(nf_vals): + settings = Settings0D( + fragmentation=AlwaysN(n=nf_val), seed=44, warn_overflows=False + ) + settings.n_sd = settings0.n_sd + settings.radius_bins_edges = settings0.radius_bins_edges + settings.coal_eff = ConstEc(Ec=0.95) + settings.dt = DT + + lbl = "n_f = " + str(nf_val) + res = run_box_breakup(settings, [120], backend_class) + data_x[lbl], data_y[lbl] = res.x, res.y + + # plot + pyplot.step( + data_x["initial"], + data_y["initial"][0] * settings.rho, + color="k", + linestyle="--", + label="initial", + ) + for i, nf_val in enumerate(nf_vals): + lbl = "n_f = " + str(nf_val) + pyplot.step( + data_x[lbl], + data_y[lbl][0] * settings.rho, + color=CMAP(i / len(nf_vals)), + label=( + lbl + if lbl not in pyplot.gca().get_legend_handles_labels()[1] + else "" + ), + ) + pyplot.xscale("log") + pyplot.xlabel("particle radius (um)") + pyplot.ylabel("dm/dlnR (kg/m$^3$ / unit(ln R)") + pyplot.legend() + pyplot.title(backend_class.__name__) + if plot: + pyplot.show() + else: + pyplot.clf() + + # assert + for datum_x in data_x.values(): + np.testing.assert_array_equal(data_x["initial"], datum_x) + + peaks_expected = { + "initial": (30, 0.017), + "n_f = 1": (1600, 0.015), + "n_f = 4": (500, 0.01), + "n_f = 16": (200, 0.0075), + } + + for lbl, x_y in peaks_expected.items(): + print(lbl) + peak = np.argmax(data_y[lbl][0]) + np.testing.assert_approx_equal(data_x[lbl][peak], x_y[0], significant=1) + np.testing.assert_approx_equal( + data_y[lbl][0][peak] * settings.rho, x_y[1], significant=1 + ) + + @staticmethod + def test_fig_7b(backend_class, plot=False): # pylint: disable=too-many-locals + # arrange + settings0 = Settings0D() + settings0.n_sd = N_SD + settings0.radius_bins_edges = bins_edges(64) + x_0 = Settings0D.X0 + mu_vals = [4 * x_0, x_0, x_0 / 4] + data_x = {} + data_y = {} + + # act + lbl = "initial" + res = run_box_breakup(settings0, [0], backend_class) + data_x[lbl], data_y[lbl] = res.x, res.y + for i, mu_val in enumerate(mu_vals): + settings = Settings0D( + fragmentation=Exponential( + scale=mu_val, + vmin=(1 * si.um) ** 3, + nfmax=None, + ), + warn_overflows=False, + seed=44, + ) + settings.dt = DT + settings.n_sd = settings0.n_sd + settings.radius_bins_edges = settings0.radius_bins_edges + settings.coal_eff = ConstEc(Ec=0.95) + lbl = r"$\mu$ = " + str(round(mu_val / x_0, 2)) + "X$_0$" + res = run_box_breakup(settings, [120], backend_class) + data_x[lbl], data_y[lbl] = res.x, res.y + + # plot + pyplot.step( + data_x["initial"], + data_y["initial"][0] * settings.rho, + color="k", + linestyle="--", + label=lbl, + ) + for i, mu_val in enumerate(mu_vals): + lbl = r"$\mu$ = " + str(round(mu_val / x_0, 2)) + "X$_0$" + pyplot.step( + data_x[lbl], + data_y[lbl][0] * settings.rho, + color=CMAP(i / len(mu_vals)), + linestyle="-", + label=( + lbl + if lbl not in pyplot.gca().get_legend_handles_labels()[1] + else "" + ), + ) + + pyplot.xscale("log") + pyplot.xlabel("particle radius (um)") + pyplot.ylabel("dm/dlnr (kg/m$^3$ / unit(ln R)") + pyplot.legend() + pyplot.title(backend_class.__name__) + pyplot.tight_layout() + if plot: + pyplot.show() + else: + pyplot.clf() + + # assert + for datum_x in data_x.values(): + np.testing.assert_array_equal(data_x["initial"], datum_x) + + peaks_expected = { + "initial": (30, 0.017), + r"$\mu$ = 0.25X$_0$": (30, 0.033), + r"$\mu$ = 4.0X$_0$": (72, 0.038), + r"$\mu$ = 1.0X$_0$": (49, 0.036), + } + + for lbl, x_y in peaks_expected.items(): + print(lbl) + peak = np.argmax(data_y[lbl][0]) + np.testing.assert_approx_equal(data_x[lbl][peak], x_y[0], significant=1) + np.testing.assert_approx_equal( + data_y[lbl][0][peak] * settings.rho, x_y[1], significant=1 + ) diff --git a/PySDM/source/tests/smoke_tests/box/dejong_and_mackay_et_al_2023/test_fig_8.py b/PySDM/source/tests/smoke_tests/box/dejong_and_mackay_et_al_2023/test_fig_8.py new file mode 100644 index 0000000000000000000000000000000000000000..0d3c70598f577e02b3f0128fd9bf374cacc9ad28 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/box/dejong_and_mackay_et_al_2023/test_fig_8.py @@ -0,0 +1,78 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import matplotlib +import numpy as np +import pytest +from matplotlib import pyplot +from PySDM_examples.deJong_Mackay_et_al_2023 import Settings0D, run_box_breakup + +from PySDM.backends import Numba, ThrustRTC +from PySDM.dynamics.collisions.breakup_fragmentations import Straub2010Nf +from PySDM.dynamics.collisions.coalescence_efficiencies import Straub2010Ec +from PySDM.physics import si + + +@pytest.mark.parametrize( + "backend_class", + (Numba, pytest.param(ThrustRTC, marks=pytest.mark.xfail(strict=True))), # TODO #987 +) +def test_fig_8(backend_class, plot=False): + # arrange + settings = Settings0D( + fragmentation=Straub2010Nf(vmin=Settings0D.X0 * 1e-3, nfmax=10), + seed=44, + warn_overflows=False, + ) + steps = [0, 1200, 3600] + settings._steps = steps # pylint: disable=protected-access + settings.n_sd = 2**11 + settings.radius_bins_edges = np.logspace( + np.log10(4 * si.um), np.log10(5e3 * si.um), num=64, endpoint=True + ) + settings.coal_eff = Straub2010Ec() + + # act + res = run_box_breakup(settings, backend_class=backend_class) + data_x, data_y = res.x, res.y + + # plot + cmap = matplotlib.colormaps["viridis"] + for j, step in enumerate(steps): + if j == 0: + kwargs = {"color": "k", "linestyle": "--", "label": "initial"} + else: + kwargs = { + "color": cmap(j / len(steps)), + "linestyle": "-", + "label": f"t = {step}s", + } + pyplot.step(data_x, data_y[j] * settings.rho, **kwargs) + pyplot.xscale("log") + pyplot.xlabel("particle radius (um)") + pyplot.ylabel("dm/dlnr (kg/m$^3$ / unit(ln R)") + pyplot.title(backend_class.__name__) + pyplot.legend() + if plot: + pyplot.show() + else: + pyplot.clf() + + # assert + peaks_expected = { + 0: (34, 0.019), + 1200: (2839, 0.03), + 3600: (2839, 0.02), + } + + for j, step in enumerate(steps): + peak = np.argmax(data_y[j]) + np.testing.assert_approx_equal( + actual=data_x[peak], desired=peaks_expected[step][0], significant=1 + ) + + +# TODO #1048 +# np.testing.assert_approx_equal( +# actual=data_y[j][peak] * settings.rho, +# desired=peaks_expected[step][1], +# significant=2, +# ) diff --git a/PySDM/source/tests/smoke_tests/box/dejong_azimi/__init__.py b/PySDM/source/tests/smoke_tests/box/dejong_azimi/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/box/dejong_azimi/test_box.py b/PySDM/source/tests/smoke_tests/box/dejong_azimi/test_box.py new file mode 100644 index 0000000000000000000000000000000000000000..8e3ae2df7c99780c4df26c5577d0ed7aef5d8807 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/box/dejong_azimi/test_box.py @@ -0,0 +1,87 @@ +"""regression tests asserting on values from the plots""" + +from pathlib import Path + +import pytest +import numpy as np + +from open_atmos_jupyter_utils import notebook_vars +from PySDM_examples import deJong_Azimi + +from PySDM.physics import si, in_unit + +PLOT = False +RTOL = 1e-2 + + +@pytest.fixture(scope="session", name="variables") +def variables_fixture(): + return notebook_vars( + file=Path(deJong_Azimi.__file__).parent / "box.ipynb", plot=PLOT + ) + + +def test_settings_a(variables): + # Spectra: final spectra + pysdm_spectra = np.interp( + variables["r1_plt"], + variables["res_a"].radius_bins_left_edges, + variables["res_a"].dv_dlnr[-1] * variables["settings_a"].rho, + ) + cloudy_a = in_unit(variables["a1_dmdlnr"], si.kg / si.m**3) + cloudy_b = in_unit(variables["b1_dmdlnr"], si.kg / si.m**3) + + Ea = np.linalg.norm(pysdm_spectra - cloudy_a) / np.linalg.norm(pysdm_spectra) + Eb = np.linalg.norm(pysdm_spectra - cloudy_b) / np.linalg.norm(pysdm_spectra) + assert Ea < 1.0 + assert Eb < 1.0 + + # Moments: initial M0 and M1 + for i in range(2): + M_pysdm = in_unit( + variables["res_a"].moments[:, i] + * variables["settings_a"].dv + * variables["settings_a"].rho ** i, + si.ug**i / si.cm**3, + ) + M_cloudy_a = deJong_Azimi.cloudy_data_0d.MOM_data["Golovin"]["aMoments"][i] + M_cloudy_b = ( + deJong_Azimi.cloudy_data_0d.MOM_data["Golovin"]["bMoments"][i, 0] + + deJong_Azimi.cloudy_data_0d.MOM_data["Golovin"]["bMoments"][i, 1] + ) + assert np.abs(M_pysdm[0] - M_cloudy_a[0]) / M_pysdm[0] < RTOL + assert np.abs(M_pysdm[0] - M_cloudy_b[0]) / M_pysdm[0] < RTOL + + +def test_settings_b(variables): + # Spectra: final spectra + pysdm_spectra = np.interp( + variables["r2_plt"], + variables["res_b"].radius_bins_left_edges, + variables["res_b"].dv_dlnr[-1] * variables["settings_b"].rho, + ) + cloudy_a = in_unit(variables["a2_dmdlnr"], si.kg / si.m**3) + cloudy_b = in_unit(variables["b2_dmdlnr"], si.kg / si.m**3) + + Ea = np.linalg.norm(pysdm_spectra - cloudy_a) / np.linalg.norm(pysdm_spectra) + Eb = np.linalg.norm(pysdm_spectra - cloudy_b) / np.linalg.norm(pysdm_spectra) + assert Ea > 1.0 # large error! + assert Eb < 1.5 # smaller error :) + + +def test_settings_c(variables): + # Moments: initial + for i in range(3): + M_pysdm = in_unit( + variables["res_c"].moments[:, i] + * variables["settings_c"].dv + * variables["settings_c"].rho ** i, + si.ug**i / si.cm**3, + ) + M_cloudy_a = deJong_Azimi.cloudy_data_0d.MOM_data["Geometric"]["aMoments"][i] + M_cloudy_b = ( + deJong_Azimi.cloudy_data_0d.MOM_data["Geometric"]["bMoments"][i, 0] + + deJong_Azimi.cloudy_data_0d.MOM_data["Golovin"]["bMoments"][i, 1] + ) + assert np.abs(M_pysdm[0] - M_cloudy_a[0]) / M_pysdm[0] < RTOL + assert np.abs(M_pysdm[0] - M_cloudy_b[0]) / M_pysdm[0] < RTOL diff --git a/PySDM/source/tests/smoke_tests/box/partmc/__init__.py b/PySDM/source/tests/smoke_tests/box/partmc/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/box/partmc/test_dry_wet_equilibration.py b/PySDM/source/tests/smoke_tests/box/partmc/test_dry_wet_equilibration.py new file mode 100644 index 0000000000000000000000000000000000000000..2b52c3dca0fccd1f06af4a3b70cb762dc7795a6f --- /dev/null +++ b/PySDM/source/tests/smoke_tests/box/partmc/test_dry_wet_equilibration.py @@ -0,0 +1,123 @@ +"""comparing kappa-Koehler wet radius equilibration in PySDM and PartMC +(based on PyPartMC-examples notebook by Zach D'Aquino)""" + +# pylint: disable=missing-function-docstring,no-member +from collections import namedtuple + +import numpy as np +import pytest +from matplotlib import pyplot +import PyPartMC + +from PySDM import Builder +from PySDM.backends import CPU +from PySDM.environments import Box +from PySDM.initialisation.hygroscopic_equilibrium import equilibrate_wet_radii +from PySDM.initialisation.spectra import Lognormal +from PySDM.physics import si + +linestyles = {"PyPartMC": "dashed", "PySDM": "dotted"} +x_unit = si.um +y_unit = 1 / si.cm**3 + + +def pysdm(dry_diam, temp, rel_humid, kpa): + r_dry = dry_diam / 2 + builder = Builder(n_sd=0, backend=CPU(), environment=Box(dt=np.nan, dv=np.nan)) + builder.particulator.environment["T"] = temp + builder.particulator.environment["RH"] = rel_humid + kappa_times_dry_volume = kpa * (np.pi / 6) * dry_diam**3 + return 2 * equilibrate_wet_radii( + r_dry=r_dry, + environment=builder.particulator.environment, + kappa_times_dry_volume=kappa_times_dry_volume, + ) + + +def pypartmc(dry_diam, temp, rel_humid, kpa): + env_state = PyPartMC.EnvState( + { + "rel_humidity": rel_humid, + "latitude": 0.0, + "longitude": 0.0, + "altitude": 0.0, + "start_time": 0.0, + "start_day": 0, + } + ) + + env_state.set_temperature(temp) + + aero_data = PyPartMC.AeroData( + ( + {"H2O": [1000 * si.kg / si.m**3, 0, 18e-3 * si.kg / si.mol, 0]}, + {"XXX": [np.nan * si.kg / si.m**3, 0, np.nan * si.kg / si.mol, kpa]}, + ) + ) + + dry_volumes = (np.pi / 6) * dry_diam**3 + aero_particles = [ + PyPartMC.AeroParticle(aero_data, np.array([0, 1]) * volume) + for volume in dry_volumes + ] + + for aero_particle in aero_particles: + PyPartMC.condense_equilib_particle(env_state, aero_data, aero_particle) + + wet_volumes = [np.sum(particle.volumes) for particle in aero_particles] + wet_diameters = ((6 / np.pi) * np.asarray(wet_volumes)) ** (1 / 3) + + return wet_diameters + + +@pytest.mark.parametrize("kappa", (0.1, 1)) +@pytest.mark.parametrize("temperature", (300 * si.K,)) +@pytest.mark.parametrize("relative_humidity", (0.5, 0.75, 0.99)) +def test_dry_wet_equilibration(kappa, temperature, relative_humidity, plot=False): + # arrange + models = {"PySDM": pysdm, "PyPartMC": pypartmc} + + Mode = namedtuple("Mode", ("norm_factor", "m_mode", "s_geom")) + modes = ( + Mode(norm_factor=50000 / si.cm**3, m_mode=0.9 * si.um, s_geom=1.3), + Mode(norm_factor=80000 / si.cm**3, m_mode=5.8 * si.um, s_geom=2), + ) + + dry_diameters = np.logspace(-0.5, 1.5, 100) * si.um + dn_dlndiam_sum = np.zeros_like(dry_diameters) + for mode in modes: + dn_dlndiam_sum += Lognormal(*mode).size_distribution(dry_diameters) + wet_diameters = {} + + # act + for model, func in models.items(): + wet_diameters[model] = func( + dry_diameters, temp=temperature, rel_humid=relative_humidity, kpa=kappa + ) + + # plot + pyplot.plot( + dry_diameters / x_unit, dn_dlndiam_sum / y_unit, label="(dry)", linewidth=3 + ) + for model, func in models.items(): + pyplot.plot( + wet_diameters[model] / x_unit, + dn_dlndiam_sum / y_unit, + label=f"(wet) Model={model}", + linestyle=linestyles[model], + marker=".", + ) + + pyplot.title(f"RH={relative_humidity} kappa={kappa}") + pyplot.xlabel(r"Diameter, $D_p$ [$\mu m$]") + pyplot.ylabel("$dN/dD$ [$cm^{-3}$]") + pyplot.xscale("log") + pyplot.grid() + pyplot.legend() + if plot: + pyplot.show() + + # assert + np.testing.assert_allclose( + wet_diameters["PySDM"], wet_diameters["PyPartMC"], rtol=1e-1 + ) diff --git a/PySDM/source/tests/smoke_tests/box/shima_et_al_2009/__init__.py b/PySDM/source/tests/smoke_tests/box/shima_et_al_2009/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/box/shima_et_al_2009/test_convergence.py b/PySDM/source/tests/smoke_tests/box/shima_et_al_2009/test_convergence.py new file mode 100644 index 0000000000000000000000000000000000000000..f16ec2d552e8feed769aaf80cf75633e80e4d377 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/box/shima_et_al_2009/test_convergence.py @@ -0,0 +1,85 @@ +""" +Tests checking if, for the example case from Fig 4 in +[Shima et al. 2009](https://doi.org/10.1002/qj.441), +the simulations converge towards analytic solution. +""" + +from inspect import signature +from itertools import islice + +import pytest +from matplotlib import pyplot +from PySDM_examples.Shima_et_al_2009.example import run +from PySDM_examples.Shima_et_al_2009.settings import Settings +from PySDM_examples.Shima_et_al_2009.spectrum_plotter import SpectrumPlotter + +from PySDM.physics import si + +COLORS = ("red", "green", "blue") + + +class TestConvergence: # pylint: disable=missing-class-docstring + @staticmethod + @pytest.mark.parametrize( + "adaptive, dt", + ( + pytest.param(False, 100 * si.s, marks=pytest.mark.xfail(strict=True)), + (True, 100 * si.s), + pytest.param(False, 50 * si.s, marks=pytest.mark.xfail(strict=True)), + (True, 50 * si.s), + ), + ) + def test_convergence_with_sd_count(dt, adaptive, plot=False): + """check if increasing the number of super particles indeed + reduces the error of the simulation (vs. analytic solution)""" + # arrange + settings = Settings(steps=[3600]) + settings.adaptive = adaptive + plotter = SpectrumPlotter(settings) + errors = {} + + # act + for i, ln2_nsd in enumerate((11, 15, 19)): + settings.dt = dt + settings.n_sd = 2**ln2_nsd + values, _ = run(settings) + + title = ( + "" + if i != 0 + else ( + f"{settings.dt=} settings.times={settings.steps} {settings.adaptive=}" + ) + ) + errors[ln2_nsd] = plotter.plot( + **dict( + islice( + { # supporting older versions of PySDM-examples + "t": settings.steps[-1], + "spectrum": values[tuple(values.keys())[-1]], + "label": f"{ln2_nsd=}", + "color": COLORS[i], + "title": title, + "add_error_to_label": True, + }.items(), + len(signature(plotter.plot).parameters), + ) + ) + ) + + # plot + if plot: + plotter.show() + else: + # https://github.com/matplotlib/matplotlib/issues/9970 + pyplot.xscale("linear") + pyplot.clf() + + # assert monotonicity (i.e., the larger the sd count, the smaller the error) + assert tuple(errors.keys()) == tuple(sorted(errors.keys())) + assert tuple(errors.values()) == tuple(reversed(sorted(errors.values()))) + + @staticmethod + def test_convergence_with_timestep(): + """ditto for timestep""" + pytest.skip("# TODO #1189") diff --git a/PySDM/source/tests/smoke_tests/box/shima_et_al_2009/test_lwc_constant.py b/PySDM/source/tests/smoke_tests/box/shima_et_al_2009/test_lwc_constant.py new file mode 100644 index 0000000000000000000000000000000000000000..9e925cab8a0c8a99e63790d762edddc216b40edc --- /dev/null +++ b/PySDM/source/tests/smoke_tests/box/shima_et_al_2009/test_lwc_constant.py @@ -0,0 +1,90 @@ +""" +checks if liquid water contant remains constant throughout coalescence-only simulation +""" + +import numpy as np +import pytest + +from PySDM.backends import ThrustRTC +from PySDM.builder import Builder +from PySDM.dynamics import Coalescence +from PySDM.dynamics.collisions.collision_kernels import Golovin +from PySDM.environments import Box +from PySDM.formulae import Formulae +from PySDM.initialisation.sampling.spectral_sampling import ConstantMultiplicity +from PySDM.initialisation.spectra import Exponential +from PySDM.physics.constants import si + + +def check(*, n_part, dv, n_sd, rho, attributes, step): + check_lwc = 1e-3 * si.kilogram / si.metre**3 + check_ksi = n_part * dv / n_sd + + # multiplicities + if step == 0: + np.testing.assert_approx_equal( + np.amin(attributes["multiplicity"]), np.amax(attributes["multiplicity"]), 1 + ) + np.testing.assert_approx_equal(attributes["multiplicity"][0], check_ksi, 1) + + # liquid water content + LWC = rho * np.dot(attributes["multiplicity"], attributes["volume"]) / dv + np.testing.assert_approx_equal(LWC, check_lwc, 3) + + +@pytest.mark.parametrize("croupier", ["local", "global"]) +@pytest.mark.parametrize("adaptive", [True, False]) +# pylint: disable=too-many-locals +def test_lwc_constant(backend_class, croupier, adaptive): + if backend_class == ThrustRTC and croupier == "local": # TODO #358 + pytest.skip() + if backend_class == ThrustRTC and adaptive and croupier == "global": # TODO #329 + pytest.skip() + # Arrange + formulae = Formulae(seed=256) + n_sd = 2**14 + steps = [0, 100, 200] + X0 = formulae.trivia.volume(radius=30.531e-6) + n_part = 2**23 / si.metre**3 + dv = 1e6 * si.metres**3 + dt = 1 * si.seconds + norm_factor = n_part * dv + rho = 1000 * si.kilogram / si.metre**3 + + kernel = Golovin(b=1.5e3) # [s-1] + spectrum = Exponential(norm_factor=norm_factor, scale=X0) + + env = Box(dt=dt, dv=dv) + builder = Builder( + n_sd=n_sd, backend=backend_class(formulae=formulae), environment=env + ) + + attributes = {} + attributes["volume"], attributes["multiplicity"] = ConstantMultiplicity( + spectrum + ).sample_deterministic(n_sd) + builder.add_dynamic( + Coalescence(collision_kernel=kernel, croupier=croupier, adaptive=adaptive) + ) + particulator = builder.build(attributes) + + volumes = {} + + # Act + for step in steps: + particulator.run(step - particulator.n_steps) + check( + n_part=n_part, + dv=dv, + n_sd=n_sd, + rho=rho, + attributes=particulator.attributes, + step=step, + ) + volumes[particulator.n_steps] = particulator.attributes["volume"].to_ndarray() + + # Assert + x_max = 0 + for volume in volumes.values(): + assert x_max < np.amax(volume) + x_max = np.amax(volume) diff --git a/PySDM/source/tests/smoke_tests/box/srivastava_1982/__init__.py b/PySDM/source/tests/smoke_tests/box/srivastava_1982/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/box/srivastava_1982/test_eq_10.py b/PySDM/source/tests/smoke_tests/box/srivastava_1982/test_eq_10.py new file mode 100644 index 0000000000000000000000000000000000000000..eff8629407e8210edd9c7b69f46bc72f6897c86a --- /dev/null +++ b/PySDM/source/tests/smoke_tests/box/srivastava_1982/test_eq_10.py @@ -0,0 +1,112 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +from matplotlib import pyplot +from PySDM_examples.Srivastava_1982.equations import Equations, EquationsHelpers +from PySDM_examples.Srivastava_1982.example import ( + add_to_plot_simulation_results, + compute_log_space, + get_coalescence_analytic_results, + get_processed_results, + get_pysdm_secondary_products, +) +from PySDM_examples.Srivastava_1982.settings import Settings, SimProducts +from PySDM_examples.Srivastava_1982.simulation import Simulation + +from PySDM.dynamics import Coalescence +from PySDM.dynamics.collisions.collision_kernels import ConstantK +from PySDM.physics import si + +ASSERT_PROD = SimProducts.Computed.mean_drop_volume_total_volume_ratio.name +N_STEPS = 32 +N_REALISATIONS = 5 +SEEDS = list(range(1, N_REALISATIONS + 1)) + + +def test_pysdm_coalescence_is_close_to_analytic_coalescence( + plot=False, +): # TODO #987 (backend_class: CPU, GPU) + # arrange + settings = Settings( + srivastava_c=0.5e-6 / si.s, + frag_mass=-1 * si.g, + drop_mass_0=1 * si.g, + dt=1 * si.s, + dv=1 * si.m**3, + n_sds=(16, 128), + total_number=1e6, + ) + + simulation = Simulation( + n_steps=N_STEPS, + settings=settings, + collision_dynamic=Coalescence( + collision_kernel=ConstantK(a=settings.srivastava_c) + ), + ) + + x = np.arange(N_STEPS + 1, dtype=float) + + equations = Equations( + M=settings.total_volume * settings.rho / settings.frag_mass, + c=settings.srivastava_c, + ) + equation_helper = EquationsHelpers( + settings.total_volume, + settings.total_number_0, + settings.rho, + frag_mass=settings.frag_mass, + ) + m0 = equation_helper.m0() + + x_log = compute_log_space(x) + analytic_results = get_coalescence_analytic_results( + equations, settings, m0, x, x_log + ) + + # act + sim_products = simulation.run_convergence_analysis(x, seeds=SEEDS) + secondary_products = get_pysdm_secondary_products( + products=sim_products, total_volume=settings.total_volume + ) + + pysdm_results = get_processed_results(secondary_products) + + plot_prods = [ + k + for k in list(pysdm_results.values())[0].keys() + if k != SimProducts.PySDM.total_volume.name + ] + + # plot + add_to_plot_simulation_results( + plot_prods, + settings.n_sds, + x, + pysdm_results, + analytic_results, + ) + + if plot: + pyplot.show() + + # assert + np.testing.assert_allclose( + actual=pysdm_results[settings.n_sds[-1]][ASSERT_PROD]["avg"], + desired=analytic_results[ASSERT_PROD], + rtol=2e-1, + ) + assert np.mean(pysdm_results[settings.n_sds[-1]][ASSERT_PROD]["std"]) < np.mean( + pysdm_results[settings.n_sds[0]][ASSERT_PROD]["std"] + ) + + assert np.mean( + np.abs( + pysdm_results[settings.n_sds[-1]][ASSERT_PROD]["avg"] + - analytic_results[ASSERT_PROD] + ) + ) < np.mean( + np.abs( + pysdm_results[settings.n_sds[0]][ASSERT_PROD]["avg"] + - analytic_results[ASSERT_PROD] + ) + ) diff --git a/PySDM/source/tests/smoke_tests/box/srivastava_1982/test_eq_13.py b/PySDM/source/tests/smoke_tests/box/srivastava_1982/test_eq_13.py new file mode 100644 index 0000000000000000000000000000000000000000..ad6bfdf9ebe8c8babe9dc5df274a6fc3d4fae1f3 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/box/srivastava_1982/test_eq_13.py @@ -0,0 +1,90 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest +from matplotlib import pyplot +from PySDM_examples.Srivastava_1982 import ( + Settings, + SimProducts, + coalescence_and_breakup_eq13, +) + +from PySDM.physics import si + +COMMON_PARAMS = { + "drop_mass_0": 1 * si.g, + "dt": 1 * si.s, + "dv": 1 * si.m**3, + "total_number": 1e6, +} + + +@pytest.mark.parametrize( + "title, settings", + ( + ( + "merging only", + Settings( + srivastava_c=0.5e-6 / si.s, + srivastava_beta=1e-15 / si.s, + frag_mass=-1 * si.g, + n_sds=(8, 128), + **COMMON_PARAMS, + ), + ), + ( + "breakup only", + Settings( + srivastava_c=1e-15 / si.s, + srivastava_beta=1e-9 / si.s, + frag_mass=0.25 * si.g, + n_sds=(64, 256), + **COMMON_PARAMS, + ), + ), + ( + "merge + break", + Settings( + srivastava_c=0.5e-6 / si.s, + srivastava_beta=1e-9 / si.s, + frag_mass=0.25 * si.g, + n_sds=(2**10, 2**12), + **COMMON_PARAMS, + ), + ), + ), +) +def test_pysdm_coalescence_and_breakup_is_close_to_analytic_coalescence_and_breakup( + title, settings, plot=False # TODO #987 (backend_class: CPU, GPU) +): + n_steps = 256 + results = coalescence_and_breakup_eq13( + settings, n_steps=n_steps, n_realisations=5, title=title + ) + + if plot: + pyplot.show() + else: + pyplot.close("all") # TODO #1764 + + # assert + assert_prod = SimProducts.Computed.mean_drop_volume_total_volume_ratio.name + np.testing.assert_allclose( + actual=results.pysdm[settings.n_sds[-1]][assert_prod]["avg"], + desired=results.analytic[assert_prod], + rtol=2e-1, + ) + assert np.mean(results.pysdm[settings.n_sds[-1]][assert_prod]["std"]) < np.mean( + results.pysdm[settings.n_sds[0]][assert_prod]["std"] + ) + + assert np.mean( + np.abs( + results.pysdm[settings.n_sds[-1]][assert_prod]["avg"] + - results.analytic[assert_prod] + ) + ) < np.mean( + np.abs( + results.pysdm[settings.n_sds[0]][assert_prod]["avg"] + - results.analytic[assert_prod] + ) + ) diff --git a/PySDM/source/tests/smoke_tests/box/srivastava_1982/test_equations.py b/PySDM/source/tests/smoke_tests/box/srivastava_1982/test_equations.py new file mode 100644 index 0000000000000000000000000000000000000000..6962168ef9506bbb1415953eaf7f3b1919a3b2de --- /dev/null +++ b/PySDM/source/tests/smoke_tests/box/srivastava_1982/test_equations.py @@ -0,0 +1,58 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +from PySDM_examples.Srivastava_1982.equations import Equations + +from PySDM.physics import constants_defaults +from PySDM.physics.dimensional_analysis import DimensionalAnalysis + + +class TestEquations: + def test_eq10(self): + with DimensionalAnalysis(): + # arrange + si = constants_defaults.si + frag_mass = 1 * si.kg + eqs = Equations(alpha=1 / si.s, c=1 / si.s, M=1 * si.kg / frag_mass) + + # act + m_e = eqs.eq10(m0=0.1 * si.kg / frag_mass, tau=eqs.tau(1 * si.s)) + + # assert + assert m_e.check(si.dimensionless) + + def test_eq12(self): + with DimensionalAnalysis(): + # arrange + si = constants_defaults.si + frag_mass = 1 * si.kg + eqs = Equations(alpha=1 / si.s, c=1 / si.s, M=1 * si.kg / frag_mass) + + # act + m_e = eqs.eq12() + + # assert + assert m_e.check(si.dimensionless) + + def test_eq13(self): + with DimensionalAnalysis(): + # arrange + si = constants_defaults.si + frag_mass = 1 * si.kg + eqs = Equations(beta=1 / si.s, c=1 / si.s, M=1 * si.kg / frag_mass) + + # act + m_e = eqs.eq13(m0=0.1 * si.kg / frag_mass, tau=eqs.tau(1 * si.s)) + + # assert + assert m_e.check(si.dimensionless) + + def test_eq14(self): + with DimensionalAnalysis(): + # arrange + si = constants_defaults.si + eqs = Equations(beta_star=1 * si.dimensionless) + + # act + m_e = eqs.eq14() + + # assert + assert m_e.check(si.dimensionless) diff --git a/PySDM/source/tests/smoke_tests/conftest.py b/PySDM/source/tests/smoke_tests/conftest.py new file mode 100644 index 0000000000000000000000000000000000000000..c11e14443ed580b18e28dee309c06be80f28328a --- /dev/null +++ b/PySDM/source/tests/smoke_tests/conftest.py @@ -0,0 +1,7 @@ +"""borrowing everything from unit_tests (this cannot be one level up due to devops_tests)""" + +import pytest + +from ..unit_tests.conftest import backend_class + +pytest.fixture(backend_class.__wrapped__) diff --git a/PySDM/source/tests/smoke_tests/kinematic_1d/__init__.py b/PySDM/source/tests/smoke_tests/kinematic_1d/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/kinematic_1d/deJong_Azimi/__init__.py b/PySDM/source/tests/smoke_tests/kinematic_1d/deJong_Azimi/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/kinematic_1d/deJong_Azimi/test_few_steps.py b/PySDM/source/tests/smoke_tests/kinematic_1d/deJong_Azimi/test_few_steps.py new file mode 100644 index 0000000000000000000000000000000000000000..4ad7375a39edbb35b6e12bca581e95a4c158b78c --- /dev/null +++ b/PySDM/source/tests/smoke_tests/kinematic_1d/deJong_Azimi/test_few_steps.py @@ -0,0 +1,65 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest +from matplotlib import pyplot +from PySDM_examples.deJong_Azimi import Settings1D +from PySDM_examples.Shipway_and_Hill_2012 import Simulation +from scipy.ndimage import uniform_filter1d + +from PySDM.physics import si + + +@pytest.mark.parametrize( + "z_part", + ( + [0.0, 1.0], + [0.2, 0.8], + ), +) +def test_few_steps(z_part, plot=False): + # Arrange + n_sd_per_gridbox = 128 + dt = 30 * si.s + smooth_window = 5 + settings = Settings1D( + n_sd_per_gridbox=n_sd_per_gridbox, + dt=dt, + dz=60 * si.m, + rho_times_w_1=0 * si.m / si.s * si.kg / si.m**3, + z_part=z_part, + t_max=50 * dt, + ) + settings.condensation_update_thd = True + simulation = Simulation(settings) + + # Act + output = simulation.run().products + + # Plot + def mean_profile_over_last_steps(var, smooth=True): + data = np.mean(output[var][output["z"] >= 0, -10:], axis=1) + if not smooth: + return data + return uniform_filter1d(data, size=smooth_window) + + for var in ( + "collision_rate", + "nr", + "nc", + ): + z = output["z"][output["z"] >= 0] + pyplot.plot( + mean_profile_over_last_steps(var, smooth=False), + z, + linestyle="--", + marker="o", + ) + pyplot.plot(mean_profile_over_last_steps(var), z) + pyplot.ylabel("Z [m]") + pyplot.xlabel(var + " [" + simulation.particulator.products[var].unit + "]") + pyplot.grid() + if plot: + pyplot.show() + + # Assert + assert max(mean_profile_over_last_steps("collision_rate")) > 0 diff --git a/PySDM/source/tests/smoke_tests/kinematic_1d/deJong_Azimi/test_initial_condition.py b/PySDM/source/tests/smoke_tests/kinematic_1d/deJong_Azimi/test_initial_condition.py new file mode 100644 index 0000000000000000000000000000000000000000..71ace539f911753383f7556cf04cceab788ac0e8 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/kinematic_1d/deJong_Azimi/test_initial_condition.py @@ -0,0 +1,46 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest +from matplotlib import pyplot +from PySDM_examples.deJong_Azimi import Settings1D +from PySDM_examples.Shipway_and_Hill_2012 import Simulation + +from PySDM.physics import si + + +class TestInitialCondition: # pylint: disable=too-few-public-methods + @staticmethod + @pytest.mark.parametrize( + "z_part", + ([0.0, 1.0], [0.2, 0.8], [0.0, 0.5], [0.5, 1.0]), + ) + def test_initial_condition(z_part, plot=False): + # Arrange + settings = Settings1D( + n_sd_per_gridbox=10, + rho_times_w_1=0 * si.m / si.s * si.kg / si.m**3, + z_part=z_part, + t_max=0, + ) + simulation = Simulation(settings) + + # Act + output = simulation.run().products + + # Plot + if plot: + for var in ("nc", "nr"): + pyplot.plot(output[var][:], output["z"], linestyle="--", marker="o") + pyplot.ylabel("Z [m]") + pyplot.title(f"z_part: {z_part} m") + pyplot.xlabel( + var + " [" + simulation.particulator.products[var].unit + "]" + ) + pyplot.grid() + pyplot.show() + + # Assert + nz = settings.z_max / settings.dz + has_particles = np.zeros_like(output["nc"]) + has_particles[int(nz * z_part[0]) : int((nz + 1) * z_part[1])] = 1 + np.testing.assert_array_equal(output["nc"] + output["nr"] > 0, has_particles) diff --git a/PySDM/source/tests/smoke_tests/kinematic_1d/shipway_and_hill_2012/__init__.py b/PySDM/source/tests/smoke_tests/kinematic_1d/shipway_and_hill_2012/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/kinematic_1d/shipway_and_hill_2012/test_1d_exporters.py b/PySDM/source/tests/smoke_tests/kinematic_1d/shipway_and_hill_2012/test_1d_exporters.py new file mode 100644 index 0000000000000000000000000000000000000000..2def859ed2885c2090fdc54fccb577aa3de66d1f --- /dev/null +++ b/PySDM/source/tests/smoke_tests/kinematic_1d/shipway_and_hill_2012/test_1d_exporters.py @@ -0,0 +1,159 @@ +# pylint: disable = missing-module-docstring,missing-class-docstring,missing-function-docstring +import os +import platform +from collections import namedtuple +from pathlib import Path +from tempfile import TemporaryDirectory + +import numpy as np +import pytest +from open_atmos_jupyter_utils import TemporaryFile +from PySDM_examples.Shipway_and_Hill_2012 import Settings, Simulation +from PySDM_examples.utils import readVTK_1d + +from PySDM.exporters import NetCDFExporter_1d, VTKExporter_1d, readNetCDF_1d +from PySDM.physics import si + + +@pytest.fixture(name="simulation_1d") +def simulation_1d_fixture(): + n_sd_per_gridbox = 16 + settings = Settings( + n_sd_per_gridbox=n_sd_per_gridbox, + dt=60 * si.s, + dz=200 * si.m, + precip=True, + rho_times_w_1=2 * si.m / si.s * si.kg / si.m**3, + ) + settings.t_max = 20 * settings.dt + settings.save_spec_and_attr_times = [0 * si.min, 10 * si.min, 20 * si.min] + simulation = Simulation(settings) + results = simulation.run() + return namedtuple( + Path(__file__).stem + "_FixtureData", ("results, settings, simulation") + )(results=results, settings=settings, simulation=simulation) + + +class Test2DExporters: + @staticmethod + @pytest.mark.parametrize( + "exclude_particle_reservoir", + ( + False, + True, + ), + ) + def test_netcdf_exporter_1d(simulation_1d, exclude_particle_reservoir): + # Arrange + data = simulation_1d.results.products + settings = simulation_1d.settings + nz_export = ( + int(settings.z_max / settings.dz) + if exclude_particle_reservoir + else settings.nz + ) + + # Act + file = TemporaryFile(".nc") + netcdf_exporter = NetCDFExporter_1d( + data, + settings, + simulation_1d.simulation, + filename=file.absolute_path, + exclude_particle_reservoir=exclude_particle_reservoir, + ) + netcdf_exporter.run() + data_from_file = readNetCDF_1d(file.absolute_path) + + # Assert + assert data_from_file.products["time"].shape == data["t"].shape + assert data_from_file.products["height"].shape == data["z"][-nz_export:].shape + assert data_from_file.products["T"].shape == data["T"][-nz_export:, :].shape + assert ( + data_from_file.products["dry spectrum"].shape + == data["dry spectrum"][-nz_export:, :, :].shape + ) + + assert np.amin(data_from_file.products["time"]) == np.amin(data["t"]) + assert np.amax(data_from_file.products["time"]) == np.amax(data["t"]) + assert np.amin(data_from_file.products["height"]) == np.amin( + data["z"][-nz_export:] + ) + assert np.amax(data_from_file.products["height"]) == np.amax( + data["z"][-nz_export:] + ) + assert data_from_file.products["rhod"].mean() == pytest.approx( + data["rhod"][-nz_export:, :].mean(), 1e-6 + ) + assert data_from_file.products["wet spectrum"].mean() == pytest.approx( + data["wet spectrum"][-nz_export:, :, :].mean(), 1e-6 + ) + + assert data_from_file.settings["precip"] == settings.precip + assert data_from_file.settings["kappa"] == settings.kappa + assert ( + data_from_file.settings["r_bins_edges"].size == settings.number_of_bins + 1 + ) + + @staticmethod + @pytest.mark.skipif( + platform.architecture()[0] == "32bit", reason="Not available vtk module!" + ) + @pytest.mark.parametrize( + "exclude_particle_reservoir", + ( + False, + True, + ), + ) + def test_vtk_exporter_1d( + simulation_1d, exclude_particle_reservoir + ): # pylint: disable=too-many-locals + # Arrange + data = simulation_1d.results.attributes + settings = simulation_1d[1] + z0 = 0.0 if exclude_particle_reservoir else -settings.particle_reservoir_depth + exported_particles_indexes = {} + number_of_exported_particles = [] + for i, t in enumerate(settings.save_spec_and_attr_times): + exported_particles_indexes[t] = np.where( + data["cell origin"][i][0] + >= int((z0 + settings.particle_reservoir_depth) / settings.dz) + ) + number_of_exported_particles.append(exported_particles_indexes[t][0].size) + + # Act + with TemporaryDirectory() as tmpdir_: + tmpdir = tmpdir_ + "/" + vtk_exporter = VTKExporter_1d( + data, + settings, + path=tmpdir, + exclude_particle_reservoir=exclude_particle_reservoir, + ) + vtk_exporter.run() + written_files_list = os.listdir(tmpdir) + data_from_file = {} + for t in settings.save_spec_and_attr_times: + leading_zeros_in_filename = [ + "0" for i in range(len(str(settings.t_max)) - len(str(t))) + ] + filename = "time" + "".join(leading_zeros_in_filename) + str(t) + ".vtu" + data_from_file[t] = readVTK_1d(tmpdir + filename) + + # Assert + for i, t in enumerate(settings.save_spec_and_attr_times): + filename_leading_zeros = "".join( + ["0" for i in range(len(str(settings.t_max)) - len(str(t)))] + ) + filename = "time" + filename_leading_zeros + str(t) + ".vtu" + assert filename in written_files_list + assert z0 <= np.amin(data_from_file[t]["z"]) + assert np.amax(data_from_file[t]["z"]) <= settings.z_max + assert data_from_file[t]["z"].size == number_of_exported_particles[i] + data_from_file[t].pop("z") + assert data.keys() == data_from_file[t].keys() + assert ( + data["radius"][i][exported_particles_indexes[t]].mean() + == data_from_file[t]["radius"].mean() + ) diff --git a/PySDM/source/tests/smoke_tests/kinematic_1d/shipway_and_hill_2012/test_few_steps.py b/PySDM/source/tests/smoke_tests/kinematic_1d/shipway_and_hill_2012/test_few_steps.py new file mode 100644 index 0000000000000000000000000000000000000000..c5434f9b763f9821dec9aecca69bd610567655bd --- /dev/null +++ b/PySDM/source/tests/smoke_tests/kinematic_1d/shipway_and_hill_2012/test_few_steps.py @@ -0,0 +1,116 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest +from matplotlib import pyplot +from PySDM_examples.Shipway_and_Hill_2012 import Settings, Simulation +from scipy.ndimage import uniform_filter1d + +from PySDM.physics import si + + +@pytest.mark.parametrize( + "particle_reservoir_depth", + ( + pytest.param(0 * si.m, marks=pytest.mark.xfail(strict=True)), + 660 * si.m, + ), +) +def test_few_steps_no_precip(particle_reservoir_depth, plot=False): + # Arrange + n_sd_per_gridbox = 128 + smooth_window = 5 + settings = Settings( + n_sd_per_gridbox=n_sd_per_gridbox, + dt=30 * si.s, + dz=60 * si.m, + precip=True, + rho_times_w_1=2 * si.m / si.s * si.kg / si.m**3, + ) + settings.particle_reservoir_depth = particle_reservoir_depth + settings.t_max = 50 * settings.dt + settings.condensation_update_thd = True + simulation = Simulation(settings) + + # Act + output = simulation.run().products + + # Plot + def mean_profile_over_last_steps(var, smooth=True): + data = np.mean(output[var][output["z"] >= 0, -10:], axis=1) + if not smooth: + return data + return uniform_filter1d(data, size=smooth_window) + + for var in ( + "RH", + "peak saturation", + "T", + "water_vapour_mixing_ratio", + "p", + "cloud water mixing ratio", + "ripening", + "activating", + "deactivating", + "super droplet count per gridbox", + "na", + "nc", + ): + z = output["z"][output["z"] >= 0] + pyplot.plot( + mean_profile_over_last_steps(var, smooth=False), + z, + linestyle="--", + marker="o", + ) + pyplot.plot(mean_profile_over_last_steps(var), z) + pyplot.ylabel("Z [m]") + pyplot.xlabel(var + " [" + simulation.particulator.products[var].unit + "]") + pyplot.grid() + if plot: + pyplot.show() + + # Assert + sd_prof = mean_profile_over_last_steps("super droplet count per gridbox") + assert 0.5 * n_sd_per_gridbox < min(sd_prof) < 1.5 * n_sd_per_gridbox + assert 0.5 * n_sd_per_gridbox < max(sd_prof) < 1.5 * n_sd_per_gridbox + + assert 1.0001 < max(mean_profile_over_last_steps("peak saturation")) < 1.001 + assert min(mean_profile_over_last_steps("cloud water mixing ratio")) < 1e-10 + assert 0.1 < max(mean_profile_over_last_steps("cloud water mixing ratio")) < 0.15 + assert max(mean_profile_over_last_steps("activating")) == 0 + assert max(mean_profile_over_last_steps("ripening")) > 0 + assert max(mean_profile_over_last_steps("deactivating")) > 0 + assert max(output["surface precipitation"]) == 0 + + +def test_fixed_thd(): + # Arrange + n_sd_per_gridbox = 128 + settings = Settings( + n_sd_per_gridbox=n_sd_per_gridbox, + dt=30 * si.s, + dz=60 * si.m, + precip=False, + rho_times_w_1=2 * si.m / si.s * si.kg / si.m**3, + ) + settings.t_max = 50 * settings.dt + settings.condensation_update_thd = False + simulation = Simulation(settings) + + # Act + output = simulation.run().products + + # Assert + def mean_profile_over_last_steps(var): + return np.mean(output[var][output["z"] >= 0, -10:], axis=1) + + sd_prof = mean_profile_over_last_steps("super droplet count per gridbox") + assert 0.5 * n_sd_per_gridbox < min(sd_prof) < 1.5 * n_sd_per_gridbox + assert 0.5 * n_sd_per_gridbox < max(sd_prof) < 1.5 * n_sd_per_gridbox + + assert 1.0001 < max(mean_profile_over_last_steps("peak saturation")) < 1.001 + assert min(mean_profile_over_last_steps("cloud water mixing ratio")) < 1e-10 + assert 0.5 < max(mean_profile_over_last_steps("cloud water mixing ratio")) < 0.75 + assert max(mean_profile_over_last_steps("activating")) == 0 + + assert sum(np.amin(output["thd"], axis=1)) == sum(np.amax(output["thd"], axis=1)) diff --git a/PySDM/source/tests/smoke_tests/kinematic_1d/shipway_and_hill_2012/test_initial_condition.py b/PySDM/source/tests/smoke_tests/kinematic_1d/shipway_and_hill_2012/test_initial_condition.py new file mode 100644 index 0000000000000000000000000000000000000000..3d501bc14540cfa409a9fe87da75f98293113e7d --- /dev/null +++ b/PySDM/source/tests/smoke_tests/kinematic_1d/shipway_and_hill_2012/test_initial_condition.py @@ -0,0 +1,101 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest +from matplotlib import pyplot +from PySDM_examples.Shipway_and_Hill_2012 import Settings, Simulation + +from PySDM.physics import si + + +class TestInitialCondition: + @staticmethod + @pytest.mark.parametrize( + "particle_reservoir_depth", + ( + 0 * si.m, + 100 * si.m, + 200 * si.m, + 300 * si.m, + 400 * si.m, + 500 * si.m, + 600 * si.m, + ), + ) + def test_initial_condition(particle_reservoir_depth, plot=False): + # Arrange + settings = Settings( + n_sd_per_gridbox=100, + rho_times_w_1=2 * si.m / si.s * si.kg / si.m**3, + ) + settings.particle_reservoir_depth = particle_reservoir_depth + settings.t_max = 0 * settings.dt + simulation = Simulation(settings) + + # Act + output = simulation.run().products + + # Plot + if plot: + for var in ("RH", "T", "water_vapour_mixing_ratio", "p"): + pyplot.plot(output[var][:], output["z"], linestyle="--", marker="o") + if var == "water_vapour_mixing_ratio": + for value in (0.015, 0.0138, 0.0024): + pyplot.axvline(value) + pyplot.ylabel("Z [m]") + pyplot.title(f"reservoir depth: {particle_reservoir_depth} m") + pyplot.xlabel( + var + " [" + simulation.particulator.products[var].unit + "]" + ) + pyplot.grid() + pyplot.show() + + # Assert + for key in ("p", "T", "RH"): + output[key] = output[key][ + int(settings.particle_reservoir_depth / settings.dz) :, 0 + ] + assert output["RH"].shape == (int(settings.z_max // settings.dz),) + + assert 28 < np.amin(output["RH"]) < 32 + assert 96 < np.amax(output["RH"]) < 98 + + assert 740 * si.hPa < np.amin(output["p"]) < 750 * si.hPa + assert (np.diff(output["p"]) < 0).all() + assert 1000 * si.hPa < np.amax(output["p"]) < 1010 * si.hPa + + assert 285 * si.K < np.amin(output["T"]) < 290 * si.K + assert output["T"][0] > np.amin(output["T"]) + assert 295 * si.K < np.amax(output["T"]) < 300 * si.K + + @staticmethod + @pytest.mark.parametrize( + "old_buggy_density_formula", + (pytest.param(True, marks=pytest.mark.xfail(strict=True)), False), + ) + def test_density_profile(old_buggy_density_formula: bool, plot=False): + """depicts a bug found in 2025 (thanks Clara!) in the density profile formula""" + # arrange + settings = Settings( + old_buggy_density_formula=old_buggy_density_formula, + n_sd_per_gridbox=0, + ) + + # act + altitude = np.linspace(0, settings.z_max, 10) + dry_air_density = settings.rhod(altitude) + + # plot + pyplot.plot(dry_air_density, altitude) + pyplot.ylabel("altitude [m]") + pyplot.xlabel("dry-air density [kg m$^{-3}$]") + pyplot.grid() + pyplot.xlim(0.9, settings.rhod0) + if plot: + pyplot.show() + else: + pyplot.clf() + + # assert + np.testing.assert_approx_equal( + actual=0.9029, desired=dry_air_density[-1], significant=4 + ) diff --git a/PySDM/source/tests/smoke_tests/kinematic_1d/shipway_and_hill_2012/test_settings.py b/PySDM/source/tests/smoke_tests/kinematic_1d/shipway_and_hill_2012/test_settings.py new file mode 100644 index 0000000000000000000000000000000000000000..0236faf0f1375ac07c6942f72cc677decd1ba6de --- /dev/null +++ b/PySDM/source/tests/smoke_tests/kinematic_1d/shipway_and_hill_2012/test_settings.py @@ -0,0 +1,29 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +from PySDM_examples.Shipway_and_Hill_2012 import Settings + + +class TestSettings: + @staticmethod + def test_instantiate(): + _ = Settings(n_sd_per_gridbox=1, rho_times_w_1=1) + + @staticmethod + def test_th(): + settings = Settings(n_sd_per_gridbox=1, rho_times_w_1=1) + assert settings._th(0) == 297.9 + assert settings._th(100) == 297.9 + assert settings._th(740) == 297.9 + assert settings._th(3260) == 312.66 + + @staticmethod + def test_water_vapour_mixing_ratio(): + settings = Settings(n_sd_per_gridbox=1, rho_times_w_1=1) + assert settings.water_vapour_mixing_ratio(0) == 0.015 + assert settings.water_vapour_mixing_ratio(740) == 0.0138 + np.testing.assert_approx_equal(settings.water_vapour_mixing_ratio(3260), 0.0024) + + @staticmethod + def test_rhod(): + settings = Settings(n_sd_per_gridbox=1, rho_times_w_1=1) + assert settings.rhod diff --git a/PySDM/source/tests/smoke_tests/kinematic_2d/__init__.py b/PySDM/source/tests/smoke_tests/kinematic_2d/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/kinematic_2d/arabas_et_al_2015/__init__.py b/PySDM/source/tests/smoke_tests/kinematic_2d/arabas_et_al_2015/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/kinematic_2d/arabas_et_al_2015/dummy_storage.py b/PySDM/source/tests/smoke_tests/kinematic_2d/arabas_et_al_2015/dummy_storage.py new file mode 100644 index 0000000000000000000000000000000000000000..c381582cc17087556072e92a5730873bf5fabb5a --- /dev/null +++ b/PySDM/source/tests/smoke_tests/kinematic_2d/arabas_et_al_2015/dummy_storage.py @@ -0,0 +1,18 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np + + +class DummyStorage: + def __init__(self): + self.profiles = [] + + def init(*_): # pylint: disable=no-method-argument,no-self-argument + pass + + def save( + self, data: np.ndarray, step: int, name: str + ): # pylint: disable=unused-argument + if name == "water_vapour_mixing_ratio_env": + self.profiles.append( + {"water_vapour_mixing_ratio_env": np.mean(data, axis=0)} + ) diff --git a/PySDM/source/tests/smoke_tests/kinematic_2d/arabas_et_al_2015/test_adaptive_displacement.py b/PySDM/source/tests/smoke_tests/kinematic_2d/arabas_et_al_2015/test_adaptive_displacement.py new file mode 100644 index 0000000000000000000000000000000000000000..4be42d4d9f9e31b61ca9a1a13a417c673d50687e --- /dev/null +++ b/PySDM/source/tests/smoke_tests/kinematic_2d/arabas_et_al_2015/test_adaptive_displacement.py @@ -0,0 +1,75 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest +from matplotlib import pyplot +from PySDM_examples.Arabas_et_al_2015 import Settings, SpinUp +from PySDM_examples.utils.kinematic_2d import Simulation + +from PySDM import Formulae +from PySDM.backends import CPU +from PySDM.physics import si +from PySDM.products import SuperDropletCountPerGridbox + +from .dummy_storage import DummyStorage + + +@pytest.mark.parametrize( + "rtol", + ( + pytest.param(None, marks=pytest.mark.xfail(strict=True)), + pytest.param(1e-0, marks=pytest.mark.xfail(strict=True)), + pytest.param(1e-1, marks=pytest.mark.xfail(strict=True)), + pytest.param(1e-2), + ), +) +def test_adaptive_displacement(rtol, plot=False): + # Arrange + settings = Settings(formulae=Formulae(seed=666)) + settings.dt = 5 * si.s + settings.grid = (10, 10) + settings.n_sd_per_gridbox = 10 + settings.rhod_w_max = 10 * si.m / si.s * si.kg / si.m**3 + + settings.simulation_time = 1000 * si.s + settings.spin_up_time = settings.simulation_time + settings.output_interval = settings.simulation_time + if rtol is not None: + settings.displacement_adaptive = True + settings.displacement_rtol = rtol + else: + settings.displacement_adaptive = False + settings.processes["condensation"] = False + + storage = DummyStorage() + simulation = Simulation(settings, storage, SpinUp=SpinUp, backend_class=CPU) + simulation.reinit(products=[SuperDropletCountPerGridbox()]) + + # Act + simulation.run() + sd_count = simulation.products["super droplet count per gridbox"].get() + + # Plot + pyplot.imshow( + sd_count.T, origin="lower", extent=(0, settings.grid[0], 0, settings.grid[1]) + ) + cbar = pyplot.colorbar() + cbar.set_label("#SD / cell") + pyplot.clim(0, 2 * settings.n_sd_per_gridbox) + pyplot.title( + f"adaptive: {settings.displacement_adaptive} {f'(rtol={rtol})' if rtol else ''}" + ) + pyplot.xlabel("x/dx") + pyplot.ylabel("z/dz") + pyplot.xticks(np.arange(settings.grid[0] + 1)) + pyplot.yticks(np.arange(settings.grid[1] + 1)) + if plot: + pyplot.show() + else: + pyplot.clf() + + # Assert + if rtol is not None: + assert 1 < simulation.particulator.dynamics["Displacement"]._n_substeps < 50 + assert np.count_nonzero(sd_count) == np.prod(settings.grid) + assert np.std(sd_count) < settings.n_sd_per_gridbox / 2.5 + assert np.max(sd_count) < 2.5 * settings.n_sd_per_gridbox diff --git a/PySDM/source/tests/smoke_tests/kinematic_2d/arabas_et_al_2015/test_environment.py b/PySDM/source/tests/smoke_tests/kinematic_2d/arabas_et_al_2015/test_environment.py new file mode 100644 index 0000000000000000000000000000000000000000..664cf7efa84bafebc946ed8e3c32d265ebcde3c0 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/kinematic_2d/arabas_et_al_2015/test_environment.py @@ -0,0 +1,31 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +from PySDM_examples.Arabas_et_al_2015 import Settings, SpinUp +from PySDM_examples.utils.kinematic_2d import Simulation + +from PySDM.physics.constants import si + + +def test_environment(): + # Arrange + settings = Settings() + settings.simulation_time = -1 * settings.dt + simulation = Simulation(settings, None, SpinUp=SpinUp) + simulation.reinit() + + # Act + simulation.run() + rhod = ( + simulation.particulator.environment["rhod"].to_ndarray().reshape(settings.grid) + ) + + # Assert - same in all columns + for column in range(settings.grid[0]): + np.testing.assert_array_equal(rhod[column, :], rhod[0, :]) + + # Assert - decreasing with altitude + rhod_below = 2 * si.kilograms / si.metre**3 + for level in range(settings.grid[1]): + rhod_above = rhod[0, level] + assert rhod_above < rhod_below + rhod_below = rhod_above diff --git a/PySDM/source/tests/smoke_tests/kinematic_2d/arabas_et_al_2015/test_export.py b/PySDM/source/tests/smoke_tests/kinematic_2d/arabas_et_al_2015/test_export.py new file mode 100644 index 0000000000000000000000000000000000000000..32423e48a0314d95fb5dd0c2203de865ae4f1075 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/kinematic_2d/arabas_et_al_2015/test_export.py @@ -0,0 +1,87 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import os +import tempfile +from tempfile import TemporaryDirectory + +from open_atmos_jupyter_utils import TemporaryFile +from PySDM_examples.Arabas_et_al_2015 import Settings, SpinUp +from PySDM_examples.utils.kinematic_2d.gui_settings import GUISettings +from PySDM_examples.utils.kinematic_2d.simulation import Simulation +from PySDM_examples.utils.kinematic_2d.storage import Storage +from PySDM_examples.utils import DummyController +from PySDM_examples.utils.widgets import IntSlider +from scipy.io import netcdf_file + +from PySDM import Formulae +from PySDM.backends import CPU +from PySDM.exporters import NetCDFExporter, VTKExporter + + +def test_export(backend_class, tmp_path): + # Arrange + settings = Settings() + settings.simulation_time = settings.dt + settings.output_interval = settings.dt + + storage = Storage() + simulator = Simulation( + settings, storage, SpinUp=SpinUp, backend_class=backend_class + ) + _, temp_file = tempfile.mkstemp(dir=tmp_path, suffix=".nc") + sut = NetCDFExporter(storage, settings, simulator, temp_file) + + vtk_exporter = VTKExporter(path=tmp_path) + + simulator.reinit() + simulator.run(vtk_exporter=vtk_exporter) + + vtk_exporter.write_pvd() + + # Act + sut.run(controller=DummyController()) + + # Assert + filenames_list = os.listdir(os.path.join(tmp_path, "output")) + assert len(list(filter(lambda x: x.endswith(".pvd"), filenames_list))) > 0 + assert len(list(filter(lambda x: x.endswith(".vts"), filenames_list))) > 0 + assert len(list(filter(lambda x: x.endswith(".vtu"), filenames_list))) > 0 + + +def test_export_with_gui_settings(): + # Arrange + settings = GUISettings(Settings(Formulae())) + settings.ui_nz.value += 1 + settings.ui_simulation_time = IntSlider(value=10) + settings.ui_dt = IntSlider(value=10) + settings.ui_output_options["interval"] = IntSlider(value=settings.ui_dt.value) + assert settings.n_steps == 1 + assert len(settings.output_steps) == 2 and settings.output_steps[-1] == 1 + + storage = Storage() + simulator = Simulation( + settings=settings, storage=storage, SpinUp=SpinUp, backend_class=CPU + ) + file = TemporaryFile() + ncdf_exporter = NetCDFExporter( + storage=storage, + settings=settings, + simulator=simulator, + filename=file.absolute_path, + ) + with TemporaryDirectory() as tempdir: + vtk_exporter = VTKExporter(path=tempdir) + + # Act + simulator.reinit() + simulator.run(vtk_exporter=vtk_exporter) + ncdf_exporter.run(controller=DummyController()) + vtk_exporter.write_pvd() + + # Assert + versions = netcdf_file(file.absolute_path).versions # pylint: disable=no-member + assert "PyMPDATA" in str(versions) + + filenames_list = os.listdir(os.path.join(tempdir, "output")) + assert len(list(filter(lambda x: x.endswith(".pvd"), filenames_list))) == 2 + assert len(list(filter(lambda x: x.endswith(".vts"), filenames_list))) == 2 + assert len(list(filter(lambda x: x.endswith(".vtu"), filenames_list))) == 2 diff --git a/PySDM/source/tests/smoke_tests/kinematic_2d/arabas_et_al_2015/test_freezing.py b/PySDM/source/tests/smoke_tests/kinematic_2d/arabas_et_al_2015/test_freezing.py new file mode 100644 index 0000000000000000000000000000000000000000..a23622a076c3149510d9f0baeb653c2ababc238b --- /dev/null +++ b/PySDM/source/tests/smoke_tests/kinematic_2d/arabas_et_al_2015/test_freezing.py @@ -0,0 +1,56 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import pytest +from PySDM_examples.Arabas_et_al_2015 import Settings, SpinUp +from PySDM_examples.utils.kinematic_2d import Simulation + +from PySDM import Formulae +from PySDM.backends import CPU +from PySDM.physics import si + +from .dummy_storage import DummyStorage + + +@pytest.mark.parametrize("freezing_immersion", ("singular", "time-dependent")) +def test_freezing(freezing_immersion): + # Arrange + settings = Settings( + Formulae( + particle_shape_and_density="MixedPhaseSpheres", + seed=44, + diffusion_coordinate="WaterMassLogarithm", + fastmath=True, + freezing_temperature_spectrum="Niemand_et_al_2012", + heterogeneous_ice_nucleation_rate="ABIFM", + constants={ + "NIEMAND_A": -0.517, + "NIEMAND_B": 8.934, + "ABIFM_M": 28.13797, + "ABIFM_C": -2.92414, + }, + ) + ) + settings.dt = 0.5 * si.second + settings.grid = (5, 15) + settings.n_sd_per_gridbox = 64 + + settings.simulation_time = 100 * settings.dt + settings.spin_up_time = 10 * settings.dt + + settings.output_interval = settings.dt # settings.simulation_time + + settings.processes["freezing"] = True + settings.processes["coalescence"] = False + + settings.freezing_immersion = freezing_immersion + settings.th_std0 -= 35 * si.K + settings.initial_water_vapour_mixing_ratio -= 7.15 * si.g / si.kg + + storage = DummyStorage() + simulation = Simulation(settings, storage, SpinUp=SpinUp, backend_class=CPU) + simulation.reinit() + + # Act + simulation.run() + + # Assert + assert (simulation.products["ice water content"].get() > 0).any() diff --git a/PySDM/source/tests/smoke_tests/kinematic_2d/arabas_et_al_2015/test_gui_settings.py b/PySDM/source/tests/smoke_tests/kinematic_2d/arabas_et_al_2015/test_gui_settings.py new file mode 100644 index 0000000000000000000000000000000000000000..a12a7d1028afe949948f1000010c803d3b1e0f19 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/kinematic_2d/arabas_et_al_2015/test_gui_settings.py @@ -0,0 +1,25 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +from PySDM_examples.Arabas_et_al_2015 import Settings +from PySDM_examples.utils.kinematic_2d.gui_settings import GUISettings + + +class TestGUISettings: + @staticmethod + def test_instantiate(): + _ = GUISettings(Settings()) + + @staticmethod + def test_stream_function(): + # arrange + gui_settings = GUISettings(Settings()) + gui_settings.ui_rhod_w_max = None + failed = False + + # act + try: + _ = gui_settings.stream_function(0, 0, 0) + except AttributeError: + failed = True + + # assert + assert failed diff --git a/PySDM/source/tests/smoke_tests/kinematic_2d/arabas_et_al_2015/test_initialisation.py b/PySDM/source/tests/smoke_tests/kinematic_2d/arabas_et_al_2015/test_initialisation.py new file mode 100644 index 0000000000000000000000000000000000000000..7df930bdee14923c44773086f187df87adfad418 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/kinematic_2d/arabas_et_al_2015/test_initialisation.py @@ -0,0 +1,100 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +from matplotlib import pyplot +from PySDM_examples.Arabas_et_al_2015 import Settings, SpinUp +from PySDM_examples.utils.kinematic_2d import Simulation + +from PySDM.physics.constants import si + + +# pylint: disable=too-many-locals +def test_initialisation(backend_class, plot=False): + settings = Settings() + settings.simulation_time = -1 * settings.dt + settings.grid = (10, 5) + settings.n_sd_per_gridbox = 5000 + + simulation = Simulation(settings, None, SpinUp=SpinUp, backend_class=backend_class) + + n_levels = settings.grid[1] + n_cell = int(np.prod(np.array(settings.grid))) + n_moments = 1 + + r_bins = settings.r_bins_edges + + histogram_dry = np.empty((len(r_bins) - 1, n_levels)) + histogram_wet = np.empty_like(histogram_dry) + + tmp = np.empty(n_cell) + simulation.reinit() + + # Act (moments) + simulation.run() + particulator = simulation.particulator + environment = simulation.particulator.environment + rhod = environment["rhod"].to_ndarray().reshape(settings.grid).mean(axis=0) + + v_bins = settings.formulae.trivia.volume(settings.r_bins_edges) + + moment_0 = particulator.backend.Storage.empty(n_cell, dtype=float) + moments = particulator.backend.Storage.empty((n_moments, n_cell), dtype=float) + for i in range(len(v_bins) - 1): + particulator.moments( + moment_0=moment_0, + moments=moments, + specs={"multiplicity": (0,)}, + attr_name="dry volume", + attr_range=(v_bins[i], v_bins[i + 1]), + ) + moment_0.download(tmp) + histogram_dry[i, :] = tmp.reshape(settings.grid).sum(axis=0) / ( + particulator.mesh.dv * settings.grid[0] + ) + + particulator.moments( + moment_0=moment_0, + moments=moments, + specs={"multiplicity": (0,)}, + attr_name="volume", + attr_range=(v_bins[i], v_bins[i + 1]), + ) + moment_0.download(tmp) + histogram_wet[i, :] = tmp.reshape(settings.grid).sum(axis=0) / ( + particulator.mesh.dv * settings.grid[0] + ) + + # Plot + for level in range(0, n_levels): + color = str(0.75 * (level / (n_levels - 1))) + pyplot.step( + r_bins[:-1] * si.metres / si.micrometres, + histogram_dry[:, level] / si.metre**3 * si.centimetre**3, + where="post", + color=color, + label="level " + str(level), + ) + pyplot.step( + r_bins[:-1] * si.metres / si.micrometres, + histogram_wet[:, level] / si.metre**3 * si.centimetre**3, + where="post", + color=color, + linestyle="--", + ) + pyplot.grid() + pyplot.xscale("log") + pyplot.xlabel("particle radius [µm]") + pyplot.ylabel("concentration per bin [cm^{-3}]") + pyplot.legend() + if plot: + pyplot.show() + + # Assert - total number + for level in reversed(range(n_levels)): + mass_conc_dry = np.sum(histogram_dry[:, level]) / rhod[level] + mass_conc_wet = np.sum(histogram_wet[:, level]) / rhod[level] + mass_conc_STP = settings.spectrum_per_mass_of_dry_air.norm_factor + assert 0.5 * mass_conc_STP < mass_conc_dry < 1.5 * mass_conc_STP + np.testing.assert_approx_equal(mass_conc_dry, mass_conc_wet, significant=3) + + # Assert - decreasing number density + assert np.sum(histogram_dry[:, 0]) > np.sum(histogram_dry[:, -1]) diff --git a/PySDM/source/tests/smoke_tests/kinematic_2d/arabas_et_al_2015/test_spin_up.py b/PySDM/source/tests/smoke_tests/kinematic_2d/arabas_et_al_2015/test_spin_up.py new file mode 100644 index 0000000000000000000000000000000000000000..cfe61ca851d8e1fc9ec84c3bc607864224d4a14f --- /dev/null +++ b/PySDM/source/tests/smoke_tests/kinematic_2d/arabas_et_al_2015/test_spin_up.py @@ -0,0 +1,53 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest +from matplotlib import pyplot +from PySDM_examples.Arabas_et_al_2015 import Settings, SpinUp +from PySDM_examples.utils.kinematic_2d import Simulation + +from PySDM.formulae import Formulae +from PySDM.physics import si + +from .dummy_storage import DummyStorage + + +@pytest.mark.parametrize( + "fastmath", + ( + pytest.param(False, id="fastmath: False"), + pytest.param(True, id="fastmath: True"), + ), +) +def test_spin_up(backend_class, fastmath, plot=False): + # Arrange + settings = Settings(Formulae(fastmath=fastmath)) + settings.dt = 0.5 * si.second + settings.grid = (3, 25) + settings.simulation_time = 20 * settings.dt + settings.output_interval = 1 * settings.dt + + storage = DummyStorage() + simulation = Simulation( + settings, storage, SpinUp=SpinUp, backend_class=backend_class + ) + simulation.reinit() + + # Act + simulation.run() + + # Plot + if plot: + levels = np.arange(settings.grid[1]) + for step, datum in enumerate(storage.profiles): + pyplot.plot(datum["water_vapour_mixing_ratio_env"], levels, label=str(step)) + pyplot.legend() + pyplot.show() + + # Assert + step_num = len(storage.profiles) - 1 + for step in range(step_num): + next_profile = storage.profiles[step + 1]["water_vapour_mixing_ratio_env"] + prev_profile = storage.profiles[step]["water_vapour_mixing_ratio_env"] + eps = 1e-3 + assert ((prev_profile + eps) >= next_profile).all() + assert storage.profiles[step_num]["water_vapour_mixing_ratio_env"][-1] < 7.1 diff --git a/PySDM/source/tests/smoke_tests/no_env/__init__.py b/PySDM/source/tests/smoke_tests/no_env/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/no_env/bolin_1958/__init__.py b/PySDM/source/tests/smoke_tests/no_env/bolin_1958/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/no_env/bolin_1958/test_table_1.py b/PySDM/source/tests/smoke_tests/no_env/bolin_1958/test_table_1.py new file mode 100644 index 0000000000000000000000000000000000000000..6763a78c91a2a410bdd793d59277a6671c693465 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/no_env/bolin_1958/test_table_1.py @@ -0,0 +1,70 @@ +""" +test checking values in the notebook table against those listed in the paper +""" + +from pathlib import Path +from collections import defaultdict +import numpy as np +import pytest +from open_atmos_jupyter_utils import notebook_vars + +from PySDM_examples import Bolin_1958 + + +@pytest.fixture(scope="session", name="notebook_variables") +def notebook_variables_fixture(): + """returns variables from the notebook Bolin_1958/table_1.ipynb""" + return notebook_vars( + file=Path(Bolin_1958.__file__).parent / "table_1.ipynb", + plot=False, + ) + + +COLUMNS = { + "row": None, + "radius_cm": "radius [cm]", + "adjustment_time": "adjustment time [s]", + "terminal_velocity": "terminal velocity [m/s]", + "distance": "distance [m]", +} + + +@pytest.mark.parametrize( + ",".join(COLUMNS.keys()), + ( + (0, 0.005, 3.3, 0.27, 0.9), + (1, 0.01, 7.1, 0.72, 5.1), + (2, 0.025, 33, 2.1, 69), + (3, 0.05, 93, 4.0, 370), + (4, 0.075, 165, 5.4, 890), + (5, 0.1, 245, 6.5, 1600), + (6, 0.15, 365, 8.1, 3000), + (7, 0.2, 435, 8.8, 3800), + ), +) +@pytest.mark.parametrize( + "column_var, column_label", + {k: v for k, v in COLUMNS.items() if k != "row"}.items(), +) +def test_table_1_against_values_from_the_paper( + # pylint: disable=unused-argument + # (used via locals()) + *, + notebook_variables, + column_var, + column_label, + row, + radius_cm, + adjustment_time, + terminal_velocity, + distance, +): + """comparison of the calculated values of isotopic adjustment time + and terminal velocity for drops with different radii with those presented + in the paper + """ + np.testing.assert_allclose( + actual=notebook_variables["data"][column_label][row], + desired=locals()[column_var], + rtol=defaultdict(lambda: 0.53, radius_cm=0)[column_var], + ) diff --git a/PySDM/source/tests/smoke_tests/no_env/gedzelman_and_arnold_1994/__init__.py b/PySDM/source/tests/smoke_tests/no_env/gedzelman_and_arnold_1994/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/no_env/gedzelman_and_arnold_1994/test_fig_2.py b/PySDM/source/tests/smoke_tests/no_env/gedzelman_and_arnold_1994/test_fig_2.py new file mode 100644 index 0000000000000000000000000000000000000000..1d2f68652c83776aac1b0e1e1ea36108a664fce2 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/no_env/gedzelman_and_arnold_1994/test_fig_2.py @@ -0,0 +1,43 @@ +""" +regression tests checking values plotted in Fig 2 +""" + +from pathlib import Path + +import numpy as np +import pytest + +from open_atmos_jupyter_utils import notebook_vars +from PySDM_examples import Gedzelman_and_Arnold_1994 + +PLOT = False + + +@pytest.fixture(scope="session", name="notebook_local_variables") +def notebook_local_variables_fixture(): + return notebook_vars( + Path(Gedzelman_and_Arnold_1994.__file__).parent / "fig_2.ipynb", plot=PLOT + ) + + +@pytest.mark.parametrize( + "x, y, var", + ( + (0.99, 0.27, "eq_22"), + (0.898, 0.62, "eq_22"), + (0.875, 0.95, "eq_22"), + (0.8875, 0, "eq_23"), + (0.88, 0.32, "eq_23"), + (0.85, 1, "eq_23"), + ), +) +def test_fig_2(notebook_local_variables, x, y, var): + """given that the plot depends on a number of constants that are likely to cause + discrepancies, the comparison is rough and effectively just a regression test + (expected values roughly correspond to the paper plot, but are based on PySDM output) + """ + plot_x = notebook_local_variables["fig2_x"] + plot_y = notebook_local_variables["fig2_y"][var] + eps = (plot_x[1] - plot_x[0]) / 2 + index = np.where(abs(plot_x - x) < eps) + np.testing.assert_allclose(actual=plot_y[index], desired=y, atol=0.01) diff --git a/PySDM/source/tests/smoke_tests/no_env/gonfiantini_1986/__init__.py b/PySDM/source/tests/smoke_tests/no_env/gonfiantini_1986/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/no_env/gonfiantini_1986/test_fig_3_1.py b/PySDM/source/tests/smoke_tests/no_env/gonfiantini_1986/test_fig_3_1.py new file mode 100644 index 0000000000000000000000000000000000000000..bb6f788f2bc59bab81c30a03ca45d4afe5733410 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/no_env/gonfiantini_1986/test_fig_3_1.py @@ -0,0 +1,53 @@ +"""tests for values on plots on fig. 3-1""" + +from pathlib import Path + +import numpy as np +import pytest + +from open_atmos_jupyter_utils import notebook_vars +from PySDM_examples import Gonfiantini_1986 + +from PySDM.physics.constants_defaults import CRAIG_1961_SLOPE_COEFF + +PLOT = False + + +@pytest.fixture(scope="session", name="notebook_local_variables") +def notebook_local_variables_fixture(): + return notebook_vars( + Path(Gonfiantini_1986.__file__).parent / "fig_3_1.ipynb", plot=PLOT + ) + + +@pytest.mark.parametrize("isotope", ("2H", "18O")) +def test_top_panels(notebook_local_variables, isotope): + """test if deltas for a given humidity are below zero""" + # arrange + humidity = 0.95 + delta = notebook_local_variables["plot_y"][isotope][humidity] + + # act + if_below = np.all(delta < 0) + + # assert + np.testing.assert_equal(if_below, True) + + +@pytest.mark.parametrize( + "humidity", + (0, 0.25, 0.5, 0.75, 0.95), +) +def test_slope_bottom_fig(notebook_local_variables, humidity): + """test if excess lines are to the left of the meteoric water line""" + # arrange + delta_18O = notebook_local_variables["plot_y"]["18O"][humidity] + delta_2H = notebook_local_variables["plot_y"]["2H"][humidity] + + # act + slope = np.mean(delta_2H[1:] - delta_2H[:-1]) / np.mean( + delta_18O[1:] - delta_18O[:-1] + ) + + # assert + np.testing.assert_equal(actual=slope < CRAIG_1961_SLOPE_COEFF, desired=True) diff --git a/PySDM/source/tests/smoke_tests/no_env/jouzel_and_merlivat_1984/__init__.py b/PySDM/source/tests/smoke_tests/no_env/jouzel_and_merlivat_1984/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/no_env/jouzel_and_merlivat_1984/test_thermodynamic_profiles.py b/PySDM/source/tests/smoke_tests/no_env/jouzel_and_merlivat_1984/test_thermodynamic_profiles.py new file mode 100644 index 0000000000000000000000000000000000000000..30de43a9424420262b8c63b3637c37c6e5459d64 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/no_env/jouzel_and_merlivat_1984/test_thermodynamic_profiles.py @@ -0,0 +1,72 @@ +""" +test for interpolated values of the pressure against Table 1, +test eq. 3 values +""" + +import numpy as np +from matplotlib import pyplot +import pytest + +from PySDM_examples.Jouzel_and_Merlivat_1984 import thermodynamic_profiles +from PySDM import Formulae +from PySDM.physics import si, in_unit, constants_defaults + +PLOT = False + + +class TestThermodynamicProfiles: + @staticmethod + @pytest.mark.parametrize( + ("temperature_C", "pressure"), + ((-10, 925), (-20, 780), (-30, 690), (-40, 630), (-50, 600)), + ) + def test_pressure_against_values_in_paper(temperature_C, pressure): + # arrange + temperature = temperature_C + constants_defaults.T0 + pressure_function = thermodynamic_profiles.pressure(temperature) + + # act + pressure_mbar = in_unit(pressure_function, si.mbar) + + # assert + np.testing.assert_allclose(desired=pressure, actual=pressure_mbar, atol=1e-4) + + @staticmethod + def test_pressure_interpolation_plot(plot=PLOT): + # arrange + T = np.linspace(0, -60) + constants_defaults.T0 + p = thermodynamic_profiles.pressure(T) + p_not_nan = p[~np.isnan(p)] + # act + sut = (p_not_nan[1:] - p_not_nan[:-1]) <= 0 + + # plot + pyplot.plot(T, p) + pyplot.gca().set( + ylabel="Pressure [Pa]", + xlabel="Temperature [K]", + ) + if plot: + pyplot.show() + else: + pyplot.clf() + + # assert + np.testing.assert_equal(desired=1, actual=sut) + + @staticmethod + @pytest.mark.parametrize( + ("temperature_C", "ice_saturation_4"), + ((-10, 1.05), (-20, 1.11), (-30, 1.17), (-40, 1.23), (-50, 1.30)), + ) + def test_ice_saturation_curve_4_against_table_2(temperature_C, ice_saturation_4): + # Arrange + formulae = Formulae() + const = formulae.constants + T = temperature_C + const.T0 + + # Act + sut = thermodynamic_profiles.ice_saturation_curve_4(const, T) + + # Assert + np.testing.assert_allclose(desired=ice_saturation_4, actual=sut, atol=0.01) diff --git a/PySDM/source/tests/smoke_tests/no_env/kinzer_and_gunn_1951/__init__.py b/PySDM/source/tests/smoke_tests/no_env/kinzer_and_gunn_1951/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/no_env/kinzer_and_gunn_1951/test_table_1_and_2.py b/PySDM/source/tests/smoke_tests/no_env/kinzer_and_gunn_1951/test_table_1_and_2.py new file mode 100644 index 0000000000000000000000000000000000000000..865ee5e0afdd6df2d222a83147b7ba5bdd922921 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/no_env/kinzer_and_gunn_1951/test_table_1_and_2.py @@ -0,0 +1,22 @@ +""" +test if values in table1 (table2) are increasing (decreasing) in each column +""" + +import numpy as np +import pytest + +from PySDM_examples.Kinzer_And_Gunn_1951.table_1_and_2 import table1, table2 + + +@pytest.mark.parametrize("temperature", (0, 20, 30, 40)) +@pytest.mark.parametrize("table, slope_sign", ((table1, 1), (table2, -1))) +def test_table_1_monotonicity(table, temperature, slope_sign): + # Arrange + values = np.array([x for x in table[f"{temperature} [deg C]"] if x != 0]) + + # Act + differences = values[1:] - values[:-1] + signs = np.sign(differences) + + # Assert + np.testing.assert_equal(actual=signs, desired=slope_sign) diff --git a/PySDM/source/tests/smoke_tests/no_env/lamb_et_al_2017/__init__.py b/PySDM/source/tests/smoke_tests/no_env/lamb_et_al_2017/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/no_env/lamb_et_al_2017/test_fig_4.py b/PySDM/source/tests/smoke_tests/no_env/lamb_et_al_2017/test_fig_4.py new file mode 100644 index 0000000000000000000000000000000000000000..17bd478c1ae5d0e83ee339aa258895293c579444 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/no_env/lamb_et_al_2017/test_fig_4.py @@ -0,0 +1,52 @@ +""" +regression tests checking values from the plots in Fig 4 +""" + +from pathlib import Path + +import numpy as np +import pytest + +from open_atmos_jupyter_utils import notebook_vars +from PySDM_examples import Lamb_et_al_2017 + +PLOT = False + + +@pytest.fixture(scope="session", name="notebook_local_variables") +def notebook_local_variables_fixture(): + return notebook_vars( + Path(Lamb_et_al_2017.__file__).parent / "fig_4.ipynb", plot=PLOT + ) + + +class TestFig4: + @staticmethod + @pytest.mark.parametrize( + "T, alpha_i_2H, paper", + ( + (180, 1.50, "MerlivatAndNief1967"), + (220, 1.27, "MerlivatAndNief1967"), + (273, 1.13, "MerlivatAndNief1967"), + (193, 1.60, "EllehojEtAl2013"), + (220, 1.35, "EllehojEtAl2013"), + (273, 1.13, "EllehojEtAl2013"), + (180, 1.44, "LambEtAl2017"), + (220, 1.25, "LambEtAl2017"), + (273, 1.13, "LambEtAl2017"), + ), + ) + def test_values_match(notebook_local_variables, T, alpha_i_2H, paper): + plot_x = notebook_local_variables["T"] + plot_y = notebook_local_variables["alphas"][paper] + eps = 5e-1 + ((index,),) = np.where(abs(plot_x - T) < eps) + np.testing.assert_approx_equal( + actual=plot_y[index], desired=alpha_i_2H, significant=3 + ) + + @staticmethod + def test_monotonic(notebook_local_variables): + assert (np.diff(notebook_local_variables["T"]) > 0).all() + for paper in notebook_local_variables["PAPERS"]: + assert (np.diff(notebook_local_variables["alphas"][paper]) < 0).all() diff --git a/PySDM/source/tests/smoke_tests/no_env/matsushima_et_al_2023/__init__.py b/PySDM/source/tests/smoke_tests/no_env/matsushima_et_al_2023/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/no_env/matsushima_et_al_2023/test_fig_1.py b/PySDM/source/tests/smoke_tests/no_env/matsushima_et_al_2023/test_fig_1.py new file mode 100644 index 0000000000000000000000000000000000000000..de60e3914783a82ee2b674cc65c13eb4907eb651 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/no_env/matsushima_et_al_2023/test_fig_1.py @@ -0,0 +1,81 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +from pathlib import Path +from scipy.interpolate import interp1d +import numpy as np +import pytest +from open_atmos_jupyter_utils import notebook_vars +from PySDM_examples import Matsushima_et_al_2023 + +from PySDM.physics import si + + +PLOT = False + + +@pytest.fixture(scope="session", name="notebook_local_variables") +def notebook_local_variables_fixture(): + return notebook_vars( + Path(Matsushima_et_al_2023.__file__).parent / "figure_1.ipynb", plot=PLOT + ) + + +class TestFig1: + @staticmethod + @pytest.mark.parametrize( + "alpha, x, y", + ( + pytest.param( + 0, + 11 * si.nm, + 1.6e3, + marks=pytest.mark.xfail(strict=True, raises=AssertionError), + ), + (0, 20 * si.nm, 1.6e3), + (0, 100 * si.nm, 1.6e3), + (0, 500 * si.nm, 1.6e3), + pytest.param( + 0, + 1000 * si.nm, + 1.6e3, + marks=pytest.mark.xfail(strict=True, raises=AssertionError), + ), + (0.5, 11 * si.nm, 230), + (0.5, 20 * si.nm, 2.1e3), + (0.5, 100 * si.nm, 730), + (0.5, 500 * si.nm, 74), + (0.5, 1000 * si.nm, 2.1), + (1, 10.1 * si.nm, 0.84), + (1, 20 * si.nm, 3.6e3), + (1, 100 * si.nm, 850), + (1, 500 * si.nm, 76), + (1, 1000 * si.nm, 2.1), + ), + ) + def test_panel_b(notebook_local_variables, alpha, x, y): + f = interp1d( + notebook_local_variables["xas"][alpha], + notebook_local_variables["yas"][alpha], + ) + np.testing.assert_approx_equal(actual=f(x), desired=y, significant=2) + + @staticmethod + @pytest.mark.parametrize( + "alpha, first_value, last_value", + ( + (0.0, 120, np.nan), + (0.1, 25, 4.3e3), + (0.2, 1.1, 5.7e3), + (0.4, 0.011, 7.9e3), + (0.5, 0.0018, 9e3), + (0.8, 2e-5, 1.2e4), + (1.0, 1.4e-6, 1.4e4), + ), + ) + def test_panel_c(notebook_local_variables, alpha, first_value, last_value): + plot_data = np.sort(notebook_local_variables["yas"][alpha]) + np.testing.assert_approx_equal( + actual=plot_data[0], desired=first_value, significant=2 + ) + np.testing.assert_approx_equal( + actual=plot_data[-1], desired=last_value, significant=2 + ) diff --git a/PySDM/source/tests/smoke_tests/no_env/miyake_et_al_1968/__init__.py b/PySDM/source/tests/smoke_tests/no_env/miyake_et_al_1968/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/no_env/miyake_et_al_1968/test_fig_19.py b/PySDM/source/tests/smoke_tests/no_env/miyake_et_al_1968/test_fig_19.py new file mode 100644 index 0000000000000000000000000000000000000000..0c87e9e85ef93d6dac8f45f2300b55be79163a45 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/no_env/miyake_et_al_1968/test_fig_19.py @@ -0,0 +1,60 @@ +""" +regression tests checking values from the plots in Fig 19 +""" + +from pathlib import Path + +import numpy as np +import pytest + +from open_atmos_jupyter_utils import notebook_vars +from PySDM_examples import Miyake_et_al_1968 + +PLOT = False + + +@pytest.fixture(scope="session", name="notebook_local_variables") +def notebook_local_variables_fixture(): + return notebook_vars( + Path(Miyake_et_al_1968.__file__).parent / "fig_19.ipynb", plot=PLOT + ) + + +class TestFig19: + @staticmethod + def test_values(notebook_local_variables): + plot_x = notebook_local_variables["plot_x"] + assert 0.6 < min(plot_x) < 0.8 + assert 1.9 < max(plot_x) < 2.1 + + for plot_y in notebook_local_variables["plot_y"].values(): + assert 0 < min(plot_y) < 5 + assert 1.25 < max(plot_y) < 20 + + @staticmethod + def test_temperature_dependence(notebook_local_variables): + for variant in notebook_local_variables["VENTILATION_VARIANTS"]: + for iso in ("18O", "17O", "2H"): + assert ( + notebook_local_variables["plot_y"][f"{variant}-{283.15}-{iso}"] + < notebook_local_variables["plot_y"][f"{variant}-{293.15}-{iso}"] + ).all() + + @staticmethod + def test_isotope_dependence(notebook_local_variables): + for temp in (283.15, 293.15): + for variant in notebook_local_variables["VENTILATION_VARIANTS"]: + assert ( + notebook_local_variables["plot_y"][f"{variant}-{temp}-18O"] + > notebook_local_variables["plot_y"][f"{variant}-{temp}-17O"] + ).all() + assert ( + notebook_local_variables["plot_y"][f"{variant}-{temp}-17O"] + > notebook_local_variables["plot_y"][f"{variant}-{temp}-2H"] + ).all() + + @staticmethod + def test_monotonic(notebook_local_variables): + assert (np.diff(notebook_local_variables["plot_x"]) > 0).all() + for key in notebook_local_variables["plot_y"]: + assert (np.diff(notebook_local_variables["plot_y"][key]) < 0).all() diff --git a/PySDM/source/tests/smoke_tests/no_env/pierchala_et_al_2022/__init__.py b/PySDM/source/tests/smoke_tests/no_env/pierchala_et_al_2022/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/no_env/pierchala_et_al_2022/test_fig_3.py b/PySDM/source/tests/smoke_tests/no_env/pierchala_et_al_2022/test_fig_3.py new file mode 100644 index 0000000000000000000000000000000000000000..3f6357f2b9cbbaa978cf195c6e46c8470d14e21d --- /dev/null +++ b/PySDM/source/tests/smoke_tests/no_env/pierchala_et_al_2022/test_fig_3.py @@ -0,0 +1,120 @@ +""" +regression tests checking values from the plots in Fig 3 +""" + +from pathlib import Path + +import numpy as np +import pytest + +from open_atmos_jupyter_utils import notebook_vars +from PySDM_examples import Pierchala_et_al_2022 + +from PySDM.physics.constants import PER_MEG, PER_MILLE + +PLOT = False + + +@pytest.fixture(scope="session", name="notebook_local_variables") +def notebook_local_variables_fixture(): + return notebook_vars( + Path(Pierchala_et_al_2022.__file__).parent / "fig_3.ipynb", plot=PLOT + ) + + +class TestFig3: + @staticmethod + @pytest.mark.parametrize( + "isotope, F, enrichment", + ( + ("18O", 1.0, 0), + ("18O", 0.3, 25.5 * PER_MILLE), + ("17O", 1.0, 0), + ("17O", 0.3, 13.3 * PER_MILLE), + ("2H", 1.0, 0), + ("2H", 0.3, 109 * PER_MILLE), + ), + ) + def test_top_left_panel(notebook_local_variables, isotope, F, enrichment): + ((index,),) = np.where(notebook_local_variables["F"] == F) + np.testing.assert_approx_equal( + actual=notebook_local_variables["enrichments"][isotope][index], + desired=enrichment, + significant=3, + ) + + @staticmethod + @pytest.mark.parametrize( + "label, F, excess", + ( + ("d-excess", 1.0, 7.68 * PER_MILLE), + ("d-excess", 0.4, -68.4 * PER_MILLE), + ("17O-excess", 1.0, 29.04 * PER_MEG), + ("17O-excess", 0.3, -70.2 * PER_MEG), + ), + ) + def test_bottom_left_panel(notebook_local_variables, label, F, excess): + ((index,),) = np.where(notebook_local_variables["F"] == F) + excesses = notebook_local_variables["excess"] + deltas = notebook_local_variables["deltas"] + np.testing.assert_approx_equal( + actual={ + "d-excess": excesses.excess_d(deltas["2H"], deltas["18O"]), + "17O-excess": excesses.excess_17O(deltas["17O"], deltas["18O"]), + }[label][index], + desired=excess, + significant=3, + ) + + @staticmethod + @pytest.mark.parametrize( + "delta_18O, delta_2H", + ((-8.71 * PER_MILLE, -62 * PER_MILLE), (16.5 * PER_MILLE, 40.5 * PER_MILLE)), + ) + def test_top_right_panel(notebook_local_variables, delta_18O, delta_2H): + eps = 0.01 * PER_MILLE + ((index,),) = np.where( + abs(notebook_local_variables["deltas"]["18O"] - delta_18O) < eps + ) + np.testing.assert_approx_equal( + actual=notebook_local_variables["deltas"]["2H"][index], + desired=delta_2H, + significant=3, + ) + + @staticmethod + @pytest.mark.parametrize( + "delta_18O, delta_2H", + ((-8.71 * PER_MILLE, -60.3 * PER_MILLE), (6 * PER_MILLE, 57.6 * PER_MILLE)), + ) + def test_gmvl(notebook_local_variables, delta_18O, delta_2H): + cd = notebook_local_variables["const"] + eps = 0.1 * PER_MILLE + x = np.linspace(-10 * PER_MILLE, 10 * PER_MILLE, 100) + y = x * cd.CRAIG_1961_SLOPE_COEFF + cd.CRAIG_1961_INTERCEPT_COEFF + ((index,),) = np.where(abs(x - delta_18O) < eps) + np.testing.assert_approx_equal(actual=y[index], desired=delta_2H, significant=3) + + @staticmethod + @pytest.mark.parametrize( + "label, delta_18O, excess", + ( + ("d-excess", -8.5 * PER_MILLE, 6.46 * PER_MILLE), + ("d-excess", 16.5 * PER_MILLE, -91.6 * PER_MILLE), + ("17O-excess", -8.5 * PER_MILLE, 27.9 * PER_MEG), + ("17O-excess", 16.5 * PER_MILLE, -70.2 * PER_MEG), + ), + ) + def test_bottom_right_panel(notebook_local_variables, label, delta_18O, excess): + eps = 0.1 * PER_MILLE + deltas = notebook_local_variables["deltas"] + ((index,),) = np.where(abs(deltas["18O"] - delta_18O) < eps) + excesses = notebook_local_variables["excess"] + np.testing.assert_approx_equal( + actual={ + "d-excess": excesses.excess_d(deltas["2H"], deltas["18O"]), + "17O-excess": excesses.excess_17O(deltas["17O"], deltas["18O"]), + }[label][index], + desired=excess, + significant=3, + ) diff --git a/PySDM/source/tests/smoke_tests/no_env/pierchala_et_al_2022/test_fig_4.py b/PySDM/source/tests/smoke_tests/no_env/pierchala_et_al_2022/test_fig_4.py new file mode 100644 index 0000000000000000000000000000000000000000..a26da764da06e70e8d28f45cf94c5046929e43b0 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/no_env/pierchala_et_al_2022/test_fig_4.py @@ -0,0 +1,70 @@ +""" +regression tests checking values from the plots in Fig 4 +""" + +from pathlib import Path + +import numpy as np +import pytest + +from open_atmos_jupyter_utils import notebook_vars +from PySDM_examples import Pierchala_et_al_2022 + +from PySDM.physics.constants import PER_MEG, PER_MILLE + +PLOT = False + + +@pytest.fixture(scope="session", name="notebook_local_variables") +def notebook_local_variables_fixture(): + return notebook_vars( + Path(Pierchala_et_al_2022.__file__).parent / "fig_4.ipynb", plot=PLOT + ) + + +class TestFig4: + @staticmethod + @pytest.mark.parametrize( + "RH, delta_18O, delta_2H", + ( + (0.404, -9.2 * PER_MILLE, -62.0 * PER_MILLE), + (0.404, 22 * PER_MILLE, 45.8 * PER_MILLE), + (0.582, -9.2 * PER_MILLE, -62.0 * PER_MILLE), + (0.582, 16.5 * PER_MILLE, 41.4 * PER_MILLE), + (0.792, -9.2 * PER_MILLE, -62.0 * PER_MILLE), + (0.792, 9.5 * PER_MILLE, 36.7 * PER_MILLE), + ), + ) + def test_top_panel(notebook_local_variables, RH, delta_18O, delta_2H): + deltas_per_rh = notebook_local_variables["deltas_per_rh"] + eps = 0.5 * PER_MILLE + ((index,),) = np.where(abs(deltas_per_rh[RH]["18O"] - delta_18O) < eps) + np.testing.assert_approx_equal( + actual=delta_2H, desired=deltas_per_rh[RH]["2H"][index], significant=3 + ) + + @staticmethod + @pytest.mark.parametrize( + "RH, delta_18O, excess_17O", + ( + (0.404, -9.2 * PER_MILLE, 29.0 * PER_MEG), + (0.404, 22 * PER_MILLE, -102 * PER_MEG), + (0.582, -9.2 * PER_MILLE, 29.0 * PER_MEG), + (0.582, 16.5 * PER_MILLE, -71.7 * PER_MEG), + (0.792, -9.2 * PER_MILLE, 29.0 * PER_MEG), + (0.792, 9.5 * PER_MILLE, -17.7 * PER_MEG), + ), + ) + def test_bottom_panel(notebook_local_variables, RH, delta_18O, excess_17O): + deltas_per_rh = notebook_local_variables["deltas_per_rh"] + eps = 0.5 * PER_MILLE + ((index,),) = np.where(abs(deltas_per_rh[RH]["18O"] - delta_18O) < eps) + np.testing.assert_approx_equal( + actual=notebook_local_variables[ + "formulae" + ].isotope_meteoric_water_line.excess_17O( + deltas_per_rh[RH]["17O"][index], deltas_per_rh[RH]["18O"][index] + ), + desired=excess_17O, + significant=3, + ) diff --git a/PySDM/source/tests/smoke_tests/no_env/pierchala_et_al_2022/test_supplement.py b/PySDM/source/tests/smoke_tests/no_env/pierchala_et_al_2022/test_supplement.py new file mode 100644 index 0000000000000000000000000000000000000000..1c178941a986a88264f7137ffa526be205b8a60f --- /dev/null +++ b/PySDM/source/tests/smoke_tests/no_env/pierchala_et_al_2022/test_supplement.py @@ -0,0 +1,25 @@ +"""tests for consistency of values taken from the Supplement""" + +import numpy as np +from PySDM_examples.Pierchala_et_al_2022.commons import deltas_0_SMOW + +from PySDM import Formulae + + +def test_cracow_water_excesses(): + """checking if d-excess and 17O-excess values match those computed from deltas""" + # arrange + formulae = Formulae(isotope_meteoric_water_line="Dansgaard1964+BarkanAndLuz2007") + sut = formulae.isotope_meteoric_water_line + + # act/assert + np.testing.assert_approx_equal( + actual=sut.excess_d(deltas_0_SMOW["2H"], deltas_0_SMOW["18O"]), + desired=7.68 * formulae.constants.PER_MILLE, + significant=3, + ) + np.testing.assert_approx_equal( + actual=sut.excess_17O(deltas_0_SMOW["17O"], deltas_0_SMOW["18O"]), + desired=29 * formulae.constants.PER_MEG, + significant=3, + ) diff --git a/PySDM/source/tests/smoke_tests/no_env/pruppacher_and_rasmussen_1979/__init__.py b/PySDM/source/tests/smoke_tests/no_env/pruppacher_and_rasmussen_1979/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/no_env/pruppacher_and_rasmussen_1979/test_fig_1.py b/PySDM/source/tests/smoke_tests/no_env/pruppacher_and_rasmussen_1979/test_fig_1.py new file mode 100644 index 0000000000000000000000000000000000000000..bbd63afee88dd475af761e7937738ba193ecede1 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/no_env/pruppacher_and_rasmussen_1979/test_fig_1.py @@ -0,0 +1,51 @@ +""" +regression tests checking values against paper Fig 1 from [Pruppacher and Rasmussen +1979](https://doi.org/10.1175/1520-0469%281979%29036%3C1255:AWTIOT%3E2.0.CO;2) +""" + +from pathlib import Path + +import numpy as np +import pytest + +from open_atmos_jupyter_utils import notebook_vars +from PySDM_examples import Pruppacher_and_Rasmussen_1979 + +PLOT = False + + +@pytest.fixture(scope="session", name="notebook_local_variables") +def notebook_local_variables_fixture(): + return notebook_vars( + Path(Pruppacher_and_Rasmussen_1979.__file__).parent / "fig_1.ipynb", plot=PLOT + ) + + +class TestFig1: + @staticmethod + @pytest.mark.parametrize( + "sqrt_re_times_cbrt_sc, vent_coeff", + ( + (3, 1.7), + (20, 7), + (44, 14), + ), + ) + def test_values_match(notebook_local_variables, sqrt_re_times_cbrt_sc, vent_coeff): + plot_x = notebook_local_variables["sqrt_re_times_cbrt_sc"] + plot_y = notebook_local_variables["vent_coeff"] + eps = 0.1 + ((index,),) = np.where(abs(plot_x - sqrt_re_times_cbrt_sc) < eps) + np.testing.assert_approx_equal( + actual=plot_y[index], desired=vent_coeff, significant=2 + ) + + @staticmethod + def test_monotonic_x(notebook_local_variables): + plot_x = notebook_local_variables["sqrt_re_times_cbrt_sc"] + assert (np.diff(plot_x) > 0).all() + + @staticmethod + def test_monotonic_y(notebook_local_variables): + plot_y = notebook_local_variables["vent_coeff"] + assert (np.diff(plot_y) > 0).all() diff --git a/PySDM/source/tests/smoke_tests/no_env/stewart_1975/__init__.py b/PySDM/source/tests/smoke_tests/no_env/stewart_1975/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/no_env/stewart_1975/test_fig_1.py b/PySDM/source/tests/smoke_tests/no_env/stewart_1975/test_fig_1.py new file mode 100644 index 0000000000000000000000000000000000000000..4e69de23266688ad6568611ff369a4122334388b --- /dev/null +++ b/PySDM/source/tests/smoke_tests/no_env/stewart_1975/test_fig_1.py @@ -0,0 +1,55 @@ +""" +test checking values on Fig 1 +""" + +from pathlib import Path +import pytest +import numpy as np + +from open_atmos_jupyter_utils import notebook_vars +from PySDM_examples import Stewart_1975 + +PLOT = False + + +@pytest.fixture(scope="session", name="notebook_variables") +def notebook_variables_fixture(): + """returns variables from the notebook Stewart_1975/fig_1.ipynb""" + return notebook_vars( + file=Path(Stewart_1975.__file__).parent / "fig_1.ipynb", plot=PLOT + ) + + +def test_fig_ventilation_coefficient(notebook_variables): + """test if ventilation coefficients (f) have the same line slope""" + # Arrange + vent_coeff_K_G = notebook_variables["plot_K_G_coeff"][0].get_data()[1] + vent_coeff_B_P = notebook_variables["plot_B_P_coeff"][0].get_data()[1] + + # Act + eps = vent_coeff_K_G / vent_coeff_B_P + + # Assert + np.testing.assert_allclose(actual=eps, desired=1, atol=0.4) + + +@pytest.mark.parametrize("paper", ("Kinzer & Gunn", "Beard & Pruppacher")) +def test_fig_1(notebook_variables, paper): + """test coefficient factors (F) from Kinzer & Gunn and Beard & Pruppacher""" + # Arrange + radii_mm, ventilation_factor = notebook_variables["plot_factor"][paper][ + 0 + ].get_data() + idx_to_check = radii_mm >= 0.25 + ventilation_factor_to_check = ventilation_factor[idx_to_check] + vent_factor_high = 1.4 + vent_factor_low = 0.8 + + # Act + eps = (vent_factor_high - vent_factor_low) / 2 + avg_value = vent_factor_high - eps + + # Assert + np.testing.assert_allclose( + actual=ventilation_factor_to_check, desired=avg_value, atol=eps + ) diff --git a/PySDM/source/tests/smoke_tests/no_env/toon_et_al_1980/__init__.py b/PySDM/source/tests/smoke_tests/no_env/toon_et_al_1980/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/no_env/toon_et_al_1980/test_fig_1.py b/PySDM/source/tests/smoke_tests/no_env/toon_et_al_1980/test_fig_1.py new file mode 100644 index 0000000000000000000000000000000000000000..efc5bd1fb172266b7cc8cbc078531940a33b5b88 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/no_env/toon_et_al_1980/test_fig_1.py @@ -0,0 +1,59 @@ +""" +regression tests checking plotted values +""" + +from pathlib import Path + +import numpy as np +import pytest + +from open_atmos_jupyter_utils import notebook_vars +from PySDM_examples import Toon_et_al_1980 +from PySDM.physics import si + + +PLOT = False + + +@pytest.fixture(scope="session", name="notebook_variables") +def notebook_variables_fixture(): + return notebook_vars( + file=Path(Toon_et_al_1980.__file__).parent / "fig_1.ipynb", + plot=PLOT, + ) + + +@pytest.mark.parametrize( + "line_label, expected_match", + ( + ("CH$_4$ (T=100 K)", {"p": 0.88e-2 * si.mbar, "z": 400 * si.km}), + ("CH$_4$ (T=100 K)", {"p": 3e0 * si.mbar, "z": 100 * si.km}), + ("CH$_4$+N$_2$ (T=100 K)", {"p": 2.3e-4 * si.mbar, "z": 400 * si.km}), + ("CH$_4$+N$_2$ (T=100 K)", {"p": 6e0 * si.mbar, "z": 100 * si.km}), + ("CH$_4$ partial pressure (T=100 K)", {"p": 7e-4 * si.mbar, "z": 300 * si.km}), + ("CH$_4$ partial pressure (T=100 K)", {"p": 6e-1 * si.mbar, "z": 100 * si.km}), + ("CH$_4$ (T=160 K)", {"p": 1.7e-1 * si.mbar, "z": 400 * si.km}), + ("CH$_4$ (T=160 K)", {"p": 6.6e0 * si.mbar, "z": 100 * si.km}), + ("CH$_4$+N$_2$ (T=160 K)", {"p": 4.2e-2 * si.mbar, "z": 400 * si.km}), + ("CH$_4$+N$_2$ (T=160 K)", {"p": 2.4e1 * si.mbar, "z": 100 * si.km}), + ( + "CH$_4$ partial pressure (T=160 K)", + {"p": 4.2e-3 * si.mbar, "z": 400 * si.km}, + ), + ("CH$_4$ partial pressure (T=160 K)", {"p": 2.4e0 * si.mbar, "z": 100 * si.km}), + ), +) +def test_fig_1_against_values_from_the_paper_plot( + notebook_variables, line_label: str, expected_match: dict +): + # arrange + z = notebook_variables["plot_z"] + p = notebook_variables["plot_ps"][line_label] + + # act + idx = np.argmin(np.abs(z - expected_match["z"])) + + # assert + np.testing.assert_approx_equal( + actual=p[idx], desired=expected_match["p"], significant=2 + ) diff --git a/PySDM/source/tests/smoke_tests/no_env/zaba_et_al/__init__.py b/PySDM/source/tests/smoke_tests/no_env/zaba_et_al/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/no_env/zaba_et_al/test_global_meteoric_water_line.py b/PySDM/source/tests/smoke_tests/no_env/zaba_et_al/test_global_meteoric_water_line.py new file mode 100644 index 0000000000000000000000000000000000000000..cbb0d2b068110ea6e6beccf4b9e4c1c63a351767 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/no_env/zaba_et_al/test_global_meteoric_water_line.py @@ -0,0 +1,43 @@ +""" +test coefficients of global meteoric water line +""" + +from pathlib import Path + +import numpy as np +import pytest + +from open_atmos_jupyter_utils import notebook_vars + +from PySDM_examples import Zaba_et_al + +PLOT = False + + +@pytest.fixture(scope="session", name="notebook_variables") +def notebook_variables_fixture(): + return notebook_vars( + file=Path(Zaba_et_al.__file__).parent / "global_meteoric_water_line.ipynb", + plot=PLOT, + ) + + +@pytest.mark.parametrize( + "variant", + ( + pytest.param("Majoube1971"), + pytest.param("HoritaAndWesolowski1994"), + pytest.param("VanHook1968", marks=pytest.mark.xfail(strict=True)), + ), +) +@pytest.mark.parametrize("parameter", (("b=10", 8), ("a=8", 10))) +def test_plot(notebook_variables, variant, parameter): + """test if lines for a = 8 and b = 10 intersect""" + # arrange + line = notebook_variables["lines"][variant][parameter[0]] + + # act + if_intersect = np.any(line > parameter[1]) and np.any(line <= parameter[1]) + + # assert + np.testing.assert_equal(actual=if_intersect, desired=True) diff --git a/PySDM/source/tests/smoke_tests/parcel_a/__init__.py b/PySDM/source/tests/smoke_tests/parcel_a/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/parcel_a/lowe_et_al_2019/__init__.py b/PySDM/source/tests/smoke_tests/parcel_a/lowe_et_al_2019/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/parcel_a/lowe_et_al_2019/conftest.py b/PySDM/source/tests/smoke_tests/parcel_a/lowe_et_al_2019/conftest.py new file mode 100644 index 0000000000000000000000000000000000000000..de9ef9bcb22ab8d013e3c0d808c9b464818eb9da --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_a/lowe_et_al_2019/conftest.py @@ -0,0 +1,18 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest + +from PySDM.physics import si +from PySDM.physics.surface_tension import compressed_film_ovadnevaite + + +@pytest.fixture(name="constants") +def constants_fixture(): + compressed_film_ovadnevaite.sgm_org = 40 * si.mN / si.m + # TODO #1247 0.2 in the paper, but 0.1 matches the paper plots + compressed_film_ovadnevaite.delta_min = 0.1 * si.nm + + yield + + compressed_film_ovadnevaite.sgm_org = np.nan + compressed_film_ovadnevaite.delta_min = np.nan diff --git a/PySDM/source/tests/smoke_tests/parcel_a/lowe_et_al_2019/test_dz_sensitivity.py b/PySDM/source/tests/smoke_tests/parcel_a/lowe_et_al_2019/test_dz_sensitivity.py new file mode 100644 index 0000000000000000000000000000000000000000..3329a4c377370e09982e3889c61b603cd4eb7290 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_a/lowe_et_al_2019/test_dz_sensitivity.py @@ -0,0 +1,75 @@ +"""checks how parcel equilibrium saturation depends on dz""" + +import numpy as np +from matplotlib import pyplot +from PySDM_examples.Lowe_et_al_2019 import Settings, Simulation +from PySDM_examples.Lowe_et_al_2019.aerosol_code import AerosolMarine +from PySDM_examples.Lowe_et_al_2019.constants_def import LOWE_CONSTS + +from PySDM import Formulae +from PySDM.physics import si + +FORMULAE = Formulae(constants=LOWE_CONSTS) +WATER_MOLAR_VOLUME = FORMULAE.constants.water_molar_volume + + +def test_dz_sensitivity( + plot=False, +): # pylint: disable=too-many-locals,too-many-branches + # arrange + output = {} + aerosol = AerosolMarine(water_molar_volume=WATER_MOLAR_VOLUME) + model = "Constant" + + # act + for i, dz_test in enumerate((0.1, 1, 10)): + key = f"{dz_test}" + settings = Settings( + dz=dz_test * si.m, + n_sd_per_mode=200, + model=model, + aerosol=aerosol, + ) + simulation = Simulation(settings) + output[key] = simulation.run() + output[key]["color"] = "C" + str(i) + + # plot + pyplot.rc("font", size=14) + _, axs = pyplot.subplots(1, 2, figsize=(11, 4), sharey=True) + vlist = ("S_max", "CDNC_cm3") + + for idx, var in enumerate(vlist): + for key, out_item in output.items(): + Y = np.asarray(out_item["z"]) + if var == "S_max": + X = (np.asarray(out_item[var]) - 1) * 100 + else: + X = out_item[var] + axs[idx].plot( + X, Y, label=f"dz={key} m", color=out_item["color"], linestyle="-" + ) + + axs[idx].set_ylabel("Displacement [m]") + if var == "S_max": + axs[idx].set_xlabel("Supersaturation [%]") + axs[idx].set_xlim(0) + elif var == "CDNC_cm3": + axs[idx].set_xlabel("Cloud droplet concentration [cm$^{-3}$]") + else: + assert False + + for ax in axs: + ax.grid() + axs[0].legend(fontsize=12) + + if plot: + pyplot.show() + else: + pyplot.clf() + + # assert + for i, out_item in enumerate(output.values()): + if i == 0: + x = out_item["S_max"][-1] + np.testing.assert_approx_equal(x, out_item["S_max"][-1], significant=1) diff --git a/PySDM/source/tests/smoke_tests/parcel_a/lowe_et_al_2019/test_fig_1.py b/PySDM/source/tests/smoke_tests/parcel_a/lowe_et_al_2019/test_fig_1.py new file mode 100644 index 0000000000000000000000000000000000000000..247914d765a33df20bc35003baa4371365829a19 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_a/lowe_et_al_2019/test_fig_1.py @@ -0,0 +1,148 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest +from PySDM_examples.Lowe_et_al_2019 import aerosol as paper_aerosol +from PySDM_examples.Lowe_et_al_2019.constants_def import LOWE_CONSTS +from scipy import signal + +from PySDM import Formulae +from PySDM.physics import constants_defaults as const +from PySDM.physics import si + +FORMULAE = Formulae(constants=LOWE_CONSTS) +TRIVIA = FORMULAE.trivia +R_WET = np.logspace(np.log(150 * si.nm), np.log(3000 * si.nm), base=np.e, num=100) +R_DRY = 50 * si.nm +V_WET = TRIVIA.volume(R_WET) +V_DRY = TRIVIA.volume(R_DRY) +TEMPERATURE = 300 * si.K +WATER_MOLAR_VOLUME = FORMULAE.constants.water_molar_volume + + +class TestFig1: + @staticmethod + def test_bulk_surface_tension_is_sgm_w(): + # arrange + formulae = Formulae(surface_tension="Constant", constants=LOWE_CONSTS) + r_wet = np.logspace( + np.log(150 * si.nm), np.log(3000 * si.nm), base=np.e, num=100 + ) + v_wet = formulae.trivia.volume(r_wet) + + # act + sigma = formulae.surface_tension.sigma(np.nan, v_wet, np.nan, np.nan) + + # assert + assert sigma == const.sgm_w + + @staticmethod + @pytest.mark.parametrize( + "aerosol, cutoff", + ( + ( + paper_aerosol.AerosolBoreal(water_molar_volume=WATER_MOLAR_VOLUME), + 560 * si.nm, + ), + ( + paper_aerosol.AerosolMarine(water_molar_volume=WATER_MOLAR_VOLUME), + 380 * si.nm, + ), + ( + paper_aerosol.AerosolNascent(water_molar_volume=WATER_MOLAR_VOLUME), + 500 * si.nm, + ), + ), + ) + # pylint: disable=unused-argument + def test_kink_location(constants, aerosol, cutoff): + # arrange + formulae = Formulae( + surface_tension="CompressedFilmOvadnevaite", + constants=LOWE_CONSTS, + ) + + # act + sigma = formulae.surface_tension.sigma( + np.nan, V_WET, V_DRY, aerosol.modes[0]["f_org"] + ) + + # assert + cutoff_idx = (np.abs(R_WET - cutoff)).argmin() + assert (sigma[:cutoff_idx] == formulae.constants.sgm_org).all() + assert sigma[cutoff_idx] > formulae.constants.sgm_org + assert 0.98 * const.sgm_w < sigma[-1] <= const.sgm_w + + @staticmethod + @pytest.mark.parametrize( + "aerosol, surface_tension, maximum_x, maximum_y, bimodal", + ( + ( + paper_aerosol.AerosolBoreal(water_molar_volume=WATER_MOLAR_VOLUME), + "Constant", + 320 * si.nm, + 0.217, + False, + ), + ( + paper_aerosol.AerosolMarine(water_molar_volume=WATER_MOLAR_VOLUME), + "Constant", + 420 * si.nm, + 0.164, + False, + ), + ( + paper_aerosol.AerosolNascent(water_molar_volume=WATER_MOLAR_VOLUME), + "Constant", + 360 * si.nm, + 0.194, + False, + ), + ( + paper_aerosol.AerosolBoreal(water_molar_volume=WATER_MOLAR_VOLUME), + "CompressedFilmOvadnevaite", + 360 * si.nm, + 0.108, + True, + ), + ( + paper_aerosol.AerosolMarine(water_molar_volume=WATER_MOLAR_VOLUME), + "CompressedFilmOvadnevaite", + 600 * si.nm, + 0.115, + False, + ), + ( + paper_aerosol.AerosolNascent(water_molar_volume=WATER_MOLAR_VOLUME), + "CompressedFilmOvadnevaite", + 670 * si.nm, + 0.104, + True, + ), + ), + ) + def test_koehler_maxima(*, aerosol, surface_tension, maximum_x, maximum_y, bimodal): + # arrange + formulae = Formulae( + surface_tension=surface_tension, + constants=LOWE_CONSTS, + ) + sigma = formulae.surface_tension.sigma( + np.nan, V_WET, V_DRY, aerosol.modes[0]["f_org"] + ) + RH_eq = formulae.hygroscopicity.RH_eq( + R_WET, + TEMPERATURE, + aerosol.modes[0]["kappa"][surface_tension], + R_DRY**3, + sigma, + ) + + # act + peaks, _ = signal.find_peaks(RH_eq) + + # assert + assert np.argmax(RH_eq) == (np.abs(R_WET - maximum_x)).argmin() + np.testing.assert_approx_equal( + (np.amax(RH_eq) - 1) * 100, maximum_y, significant=3 + ) + assert len(peaks) == 2 if bimodal else 1 diff --git a/PySDM/source/tests/smoke_tests/parcel_a/lowe_et_al_2019/test_fig_2.py b/PySDM/source/tests/smoke_tests/parcel_a/lowe_et_al_2019/test_fig_2.py new file mode 100644 index 0000000000000000000000000000000000000000..a5c34443620278aeb07fbb5f55fc2ad0eaeddda6 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_a/lowe_et_al_2019/test_fig_2.py @@ -0,0 +1,93 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest +from PySDM_examples.Lowe_et_al_2019 import Settings, Simulation +from PySDM_examples.Lowe_et_al_2019 import aerosol as paper_aerosol +from PySDM_examples.Lowe_et_al_2019.constants_def import LOWE_CONSTS + +from PySDM import Formulae +from PySDM.physics import si + +FORMULAE = Formulae(constants=LOWE_CONSTS) +WATER_MOLAR_VOLUME = FORMULAE.constants.water_molar_volume + + +class TestFig2: # pylint: disable=too-few-public-methods + @staticmethod + @pytest.mark.parametrize( + "aerosol, surface_tension, s_max, s_100m, n_100m", + ( + ( + paper_aerosol.AerosolMarine(water_molar_volume=WATER_MOLAR_VOLUME), + "Constant", + 0.271, + 0.081, + 148, + ), + ( + paper_aerosol.AerosolMarine(water_molar_volume=WATER_MOLAR_VOLUME), + "CompressedFilmOvadnevaite", + 0.250, + 0.075, + 169, + ), + ( # TODO #1247 SS_max & SS_100m & Nc_100m doesn't match for this case + paper_aerosol.AerosolBoreal(water_molar_volume=WATER_MOLAR_VOLUME), + "Constant", + 0.182, + 0.055, + 422, + ), + ( # TODO #1247 SS_100m & Nc_100m doesn't match for this case + paper_aerosol.AerosolBoreal(water_molar_volume=WATER_MOLAR_VOLUME), + "CompressedFilmOvadnevaite", + 0.137, + 0.055, + 525, + ), + ( # TODO #1247 SS_100m & Nc_100m doesn't match for this case + paper_aerosol.AerosolNascent(water_molar_volume=WATER_MOLAR_VOLUME), + "Constant", + 0.407, + 0.122, + 68, + ), + ( # TODO #1247 SS_100m & Nc_100m doesn't match for this case + paper_aerosol.AerosolNascent(water_molar_volume=WATER_MOLAR_VOLUME), + "CompressedFilmOvadnevaite", + 0.314, + 0.076, + 166, + ), + ), + ) + # TODO #1247 AerosolMarine passes, but others fail + # TODO #1246 general mismatches in parcel profiles + @pytest.mark.xfail() + def test_peak_saturation_and_final_concentration( + *, aerosol, surface_tension, s_max, s_100m, n_100m + ): + # arrange + settings = Settings( + dz=1 * si.m, + n_sd_per_mode=32, + model=surface_tension, + aerosol=aerosol, + ) + settings.output_interval = 10 * settings.dt + simulation = Simulation(settings) + + # act + output = simulation.run() + + # assert + i_100m = np.argmin(np.abs(np.asarray(output["z"]) - 100 * si.m)) + print(i_100m, output["z"][i_100m]) + print(np.nanmax(output["S_max"]), s_max) + print(output["S_max"][i_100m], s_100m) + print(output["CDNC_cm3"][i_100m], n_100m) + np.testing.assert_approx_equal(np.nanmax(output["S_max"]), s_max, significant=2) + np.testing.assert_approx_equal(output["S_max"][i_100m], s_100m, significant=2) + np.testing.assert_approx_equal( + output["CDNC_cm3"][i_100m], n_100m, significant=2 + ) diff --git a/PySDM/source/tests/smoke_tests/parcel_a/lowe_et_al_2019/test_fig_s2.py b/PySDM/source/tests/smoke_tests/parcel_a/lowe_et_al_2019/test_fig_s2.py new file mode 100644 index 0000000000000000000000000000000000000000..2beac7911d50ff1d791e03dbfbd215ca3f781bd1 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_a/lowe_et_al_2019/test_fig_s2.py @@ -0,0 +1,83 @@ +""" +test for supplementary figure 2 in Lowe et al 2019 paper. +checks that values from panels d)-f) are in a reasonable range +and decrease/increase monotonically with updraft velocity +""" + +import os +from pathlib import Path + +import numpy as np +import pytest +from PySDM_examples import Lowe_et_al_2019 +from open_atmos_jupyter_utils import notebook_vars + +from PySDM.physics import si + +PLOT = False + + +@pytest.fixture(scope="session", name="variables") +def variables_fixture(): + return notebook_vars( + file=Path(Lowe_et_al_2019.__file__).parent / "fig_s2.ipynb", plot=PLOT + ) + + +CI = "CI" in os.environ +nRes = 10 +updrafts = np.linspace(0.2, 2.4, 2 if CI else nRes) +models = ("Constant", "CompressedFilmOvadnevaite") +aerosol_names = ("AerosolMarine", "AerosolBoreal", "AerosolNascent") + + +def keygen(updraft, model, aerosol_class_name): + return f"w{updraft:.2f}_{aerosol_class_name}_{model}" + + +@pytest.fixture( + params=[ + keygen(updraft, model, aerosol_class_name) + for updraft in updrafts + for model in models + for aerosol_class_name in aerosol_names + ], + scope="session", + name="key", +) +def keys_fixture(request): + return request.param + + +@pytest.fixture(params=models, scope="session", name="model") +def models_fixture(request): + return request.param + + +@pytest.fixture(params=aerosol_names, scope="session", name="aerosol_class_name") +def aerosols_fixture(request): + return request.param + + +class TestFigS2: + @staticmethod + @pytest.mark.parametrize( + "var, value_range", + ( + ("lwp", (28 * si.g / si.m**2, 36 * si.g / si.m**2)), # TODO #1247: 28 to 33 + ("tau", (2, 13)), # TODO #1247: 2 to 11 + ("albedo", (0.15, 0.5)), # TODO #1247: 0.15 to 0.45 + ), + ) + # TODO #1246: range mismatch possibly related to supersaturation profile discrepancies + def test_ranges(var, value_range, variables, key): + assert value_range[0] < variables["output"][key][var] < value_range[1] + + @staticmethod + @pytest.mark.parametrize("var, sgn", (("lwp", -1), ("tau", 1), ("albedo", 1))) + def test_monotonicity(var, sgn, variables, model, aerosol_class_name): + tmp = [ + variables["output"][keygen(updraft, model, aerosol_class_name)][var] + for updraft in updrafts + ] + assert (np.diff(tmp) * sgn > -np.mean(tmp) * 1e-2).all() diff --git a/PySDM/source/tests/smoke_tests/parcel_a/lowe_et_al_2019/test_surface_tension_models.py b/PySDM/source/tests/smoke_tests/parcel_a/lowe_et_al_2019/test_surface_tension_models.py new file mode 100644 index 0000000000000000000000000000000000000000..1b18c0eb6c51c563d6f0370e07a3bca6db7b8c0d --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_a/lowe_et_al_2019/test_surface_tension_models.py @@ -0,0 +1,404 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +from PySDM_examples.Lowe_et_al_2019 import aerosol +from PySDM_examples.Lowe_et_al_2019.constants_def import LOWE_CONSTS + +from PySDM import Formulae +from PySDM.physics import constants_defaults as const +from PySDM.physics import si + +FORMULAE = Formulae(constants=LOWE_CONSTS) +TRIVIA = FORMULAE.trivia +R_WET = np.logspace(np.log(150 * si.nm), np.log(3000 * si.nm), base=np.e, num=100) +R_DRY = 50 * si.nm +V_WET = TRIVIA.volume(R_WET) +V_DRY = TRIVIA.volume(R_DRY) +TEMPERATURE = 300 * si.K +WATER_MOLAR_VOLUME = FORMULAE.constants.water_molar_volume +aer = aerosol.AerosolBoreal(water_molar_volume=WATER_MOLAR_VOLUME) + + +class TestFig1: + @staticmethod + def test_bulk_surface_tension_is_sgm_w(): + # arrange + formulae = Formulae(surface_tension="Constant", constants=LOWE_CONSTS) + + # act + sigma = formulae.surface_tension.sigma(np.nan, V_WET, np.nan, np.nan) + + # assert + assert sigma == const.sgm_w + + @staticmethod + def test_ovad_surface_tension(): + # arrange + formulae = Formulae( + surface_tension="CompressedFilmOvadnevaite", + constants=LOWE_CONSTS, + ) + + # act + sigma = formulae.surface_tension.sigma( + np.nan, V_WET, V_DRY, aer.modes[0]["f_org"] + ) + + # assert + test = np.array( + [ + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04145185206190185, + 0.043245940200403225, + 0.04493465757799464, + 0.04652419321971498, + 0.04802037260212956, + 0.049428679011056784, + 0.05075427364458418, + 0.05200201453477719, + 0.053176474357604905, + 0.054281957196414696, + 0.055322514320391286, + 0.056301959036044666, + 0.05722388066615342, + 0.05809165770721892, + 0.05890847021403533, + 0.059677311456651734, + 0.06040099889241329, + 0.06108218449344455, + 0.061723364467315724, + 0.06232688840684634, + 0.06289496790207844, + 0.06342968464658641, + 0.06393299806742464, + 0.06440675250688739, + 0.06485268398238773, + 0.06527242654921248, + 0.06566751828951929, + 0.06603940694949924, + 0.06638945524541608, + 0.06671894585794522, + 0.06702908613316425, + 0.06732101250732575, + 0.0675957946718376, + 0.06785443949346634, + 0.06809789470435344, + 0.06832705237523176, + 0.06854275218466818, + 0.06874578449624996, + 0.06893689325503628, + 0.06911677871385602, + 0.06928609999950004, + 0.06944547752817445, + 0.06959549527907691, + 0.06973670293439409, + 0.06986961789368958, + 0.06999472716988156, + 0.0701124891739247, + 0.07022333539465174, + 0.07032767197991878, + 0.07042588122494463, + 0.07051832297317082, + 0.07060533593488776, + 0.0706872389283801, + 0.07076433204821643, + 0.07083689776487583, + 0.0709052019598351, + ] + ) + np.testing.assert_allclose(sigma, test, atol=1e-8) + + @staticmethod + def test_ruehl_surface_tension(): + # arrange + formulae = Formulae( + surface_tension="CompressedFilmRuehl", + constants={ + **LOWE_CONSTS, + "RUEHL_nu_org": aer.modes[0]["nu_org"], + "RUEHL_A0": 115e-20 * si.m * si.m, + "RUEHL_C0": 6e-7, + "RUEHL_m_sigma": 0.3e17 * si.J / si.m**2, + "RUEHL_sgm_min": 40.0 * si.mN / si.m, + }, + ) + + # act + sigma = formulae.surface_tension.sigma( + TEMPERATURE, V_WET, V_DRY, aer.modes[0]["f_org"] + ) + + # assert + test = np.array( + [ + 0.04318990388863848, + 0.04355416769160859, + 0.0439421778272239, + 0.04435543913070772, + 0.04479551672881958, + 0.04526402672640544, + 0.045762623781862244, + 0.04629298561514597, + 0.04685679492085086, + 0.04745571976463471, + 0.048091394314671604, + 0.04876540264111237, + 0.04947926918577529, + 0.05023446017823065, + 0.05103240053506651, + 0.051874510426880245, + 0.052762264625474335, + 0.05369727599685779, + 0.054681402290397516, + 0.05571687298813886, + 0.056806430668716615, + 0.057953479146781645, + 0.05916222830454722, + 0.06043782250688807, + 0.06178643524956695, + 0.06321530723603938, + 0.06473269985853751, + 0.06634773509867063, + 0.0680701033701799, + 0.06990965125952682, + 0.07187591399036536, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + 0.072, + ] + ) + np.testing.assert_allclose(sigma, test, atol=1e-5) + + @staticmethod + def test_SL_surface_tension(): + # arrange + formulae = Formulae( + surface_tension="SzyszkowskiLangmuir", + constants={ + **LOWE_CONSTS, + "RUEHL_nu_org": aer.modes[0]["nu_org"], + "RUEHL_A0": 115e-20 * si.m * si.m, + "RUEHL_C0": 6e-7, + "RUEHL_sgm_min": 40.0 * si.mN / si.m, + }, + ) + + # act + sigma = formulae.surface_tension.sigma( + TEMPERATURE, V_WET, V_DRY, aer.modes[0]["f_org"] + ) + + # assert + test = np.array( + [ + 0.04170545235068712, + 0.04207589632089122, + 0.04244962961646187, + 0.0428269485292672, + 0.04320818372912586, + 0.043593705564928045, + 0.04398393042681806, + 0.044379328434762944, + 0.04478043279935583, + 0.0451878513103144, + 0.04560228055915656, + 0.04602452371335611, + 0.046455512957773316, + 0.046896338148322336, + 0.047348283850285854, + 0.04781287786790929, + 0.048291955790905984, + 0.04878774828646771, + 0.049303001368475236, + 0.04984114559483559, + 0.05040653975448352, + 0.05100483126948219, + 0.05164350541293351, + 0.05233275085500362, + 0.0530868749743816, + 0.05392670762306413, + 0.05488381111799955, + 0.056007799064006464, + 0.0573767511936752, + 0.059091334616161374, + 0.06113470871082447, + 0.0630992101730827, + 0.06461657500220883, + 0.06572817193513107, + 0.06656632645301724, + 0.067223028931838, + 0.06775426559459131, + 0.0681947966802138, + 0.06856720046770072, + 0.06888681108136241, + 0.06916445633257737, + 0.06940803880572587, + 0.06962348937406575, + 0.06981536583363962, + 0.06998724243835744, + 0.07014197148608561, + 0.07028186391110573, + 0.07040881703533021, + 0.07052440690758358, + 0.0706299563370761, + 0.07072658588354233, + 0.07081525266495645, + 0.07089678030539454, + 0.0709718823377164, + 0.07104118070169195, + 0.07110522051890375, + 0.07116448200738978, + 0.07121939017482383, + 0.07127032276888652, + 0.07131761684754595, + 0.07136157424698915, + 0.07140246616194773, + 0.07144053700595183, + 0.07147600768332182, + 0.0715090783774165, + 0.07153993093863077, + 0.07156873093930102, + 0.07159562944989181, + 0.07162076458075536, + 0.07164426282575462, + 0.07166624023764522, + 0.07168680345997547, + 0.07170605063610594, + 0.0717240722125759, + 0.07174095165128262, + 0.071756766062675, + 0.07177158677029272, + 0.0717854798154341, + 0.07179850640944603, + 0.07181072334005235, + 0.0718221833372332, + 0.07183293540340689, + 0.07184302511202303, + 0.07185249487812974, + 0.07186138420401451, + 0.07186972990262169, + 0.07187756630111092, + 0.0718849254266297, + 0.07189183717612246, + 0.07189832947178237, + 0.07190442840356441, + 0.07191015836001612, + 0.07191554214854026, + 0.07192060110608092, + 0.07192535520111608, + 0.07192982312774573, + 0.0719340223925809, + 0.07193796939506661, + 0.07194167950180663, + 0.07194516711540175, + ] + ) + np.testing.assert_allclose(sigma, test, atol=1e-5) diff --git a/PySDM/source/tests/smoke_tests/parcel_a/lowe_et_al_2019/test_zero_forg.py b/PySDM/source/tests/smoke_tests/parcel_a/lowe_et_al_2019/test_zero_forg.py new file mode 100644 index 0000000000000000000000000000000000000000..93588343d4c88bdcac3db55497dae36aa1c42a09 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_a/lowe_et_al_2019/test_zero_forg.py @@ -0,0 +1,110 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +from matplotlib import pyplot +from PySDM_examples.Lowe_et_al_2019 import Settings, Simulation +from PySDM_examples.Lowe_et_al_2019.aerosol import AerosolBoreal, AerosolMarine +from PySDM_examples.Lowe_et_al_2019.constants_def import LOWE_CONSTS + +from PySDM import Formulae +from PySDM.physics import si + +FORMULAE = Formulae(constants=LOWE_CONSTS) +WATER_MOLAR_VOLUME = FORMULAE.constants.water_molar_volume + + +def test_zero_forg(plot=False): # pylint: disable=too-many-locals + nRes = 3 + updraft_list = np.geomspace(0.1, 10, nRes) + + subplot_list = ["a", "b", "c", "d"] + models = ("Constant", "CompressedFilmOvadnevaite") + + Acc = {"a": 30, "b": 134, "c": 160, "d": 540} + + cdnc_compare = np.zeros((len(models), len(subplot_list), len(updraft_list))) + for i, w in enumerate(updraft_list): + for k, subplot in enumerate(subplot_list): + for m, model in enumerate(models): + settings = Settings( + dz=1 * si.m, + n_sd_per_mode=20, + model=model, + aerosol={ + "a": AerosolMarine( + water_molar_volume=WATER_MOLAR_VOLUME, + Forg=0, + Acc_N2=Acc["a"], + ), + "b": AerosolMarine( + water_molar_volume=WATER_MOLAR_VOLUME, + Forg=0, + Acc_N2=Acc["b"], + ), + "c": AerosolBoreal( + water_molar_volume=WATER_MOLAR_VOLUME, + Forg=0, + Acc_N2=Acc["c"], + ), + "d": AerosolBoreal( + water_molar_volume=WATER_MOLAR_VOLUME, + Forg=0, + Acc_N2=Acc["d"], + ), + }[subplot], + w=w * si.m / si.s, + ) + simulation = Simulation(settings) + output = simulation.run() + cdnc_compare[m, k, i] = np.array(output["CDNC_cm3"])[-1] + + mrkr = ["o", "s", "*", "v", "^", "D", "h", "x", "+", "8", "p", "<", ">", "d", "H"] + _, axes = pyplot.subplots( + 1, + len(subplot_list), + figsize=(len(subplot_list) * 4, 4), + constrained_layout=True, + sharex=True, + sharey=False, + ) + + for k, subplot in enumerate(subplot_list): + if len(subplot_list) > 1: + ax = axes[k] + else: + ax = axes + + for m, model in enumerate(models): + for i, w in enumerate(updraft_list): + if i == 0: + ax.scatter( + w * (1 + 0.1 * m), + cdnc_compare[m, k, i], + color="C" + str(m), + marker=mrkr[m], + label=model, + ) + else: + ax.scatter( + w * (1 + 0.1 * m), + cdnc_compare[m, k, i], + color="C" + str(m), + marker=mrkr[m], + ) + ax.set_xscale("log") + + ax.set_title(subplot, loc="left", weight="bold") + ax.set_xlabel("Updraft velocity, w [m s$^{-1}$]") + if k == 0: + ax.set_ylabel("CDNC [cm$^{-3}$]") + ax.legend() + + pyplot.suptitle("zero organics") + + if plot: + pyplot.show() + + np.testing.assert_allclose( + cdnc_compare[0, :, :], + cdnc_compare[1, :, :], + rtol=1e-2, + ) diff --git a/PySDM/source/tests/smoke_tests/parcel_a/pyrcel/__init__.py b/PySDM/source/tests/smoke_tests/parcel_a/pyrcel/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/parcel_a/pyrcel/test_parcel_example.py b/PySDM/source/tests/smoke_tests/parcel_a/pyrcel/test_parcel_example.py new file mode 100644 index 0000000000000000000000000000000000000000..0f2f9398c98615988dbe4aa251a8d6dd9b515c53 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_a/pyrcel/test_parcel_example.py @@ -0,0 +1,70 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest +from PySDM_examples.Pyrcel import Settings, Simulation + +from PySDM import Formulae +from PySDM.initialisation.spectra import Lognormal +from PySDM.physics import si +from PySDM.products import ( + AmbientRelativeHumidity, + AmbientTemperature, + ParcelDisplacement, +) + + +class TestParcelExample: # pylint: disable=too-few-public-methods + @staticmethod + @pytest.mark.parametrize("s_max, s_250m, T_250m", ((0.62, 0.139, 272.2),)) + @pytest.mark.parametrize("scipy_solver", (pytest.param(True), pytest.param(False))) + @pytest.mark.xfail( + strict=True + ) # TODO #1246 s_250m (only) fails for both solver options + def test_humidity_and_temperature_profile(s_max, s_250m, T_250m, scipy_solver): + # arrange + settings = Settings( + dz=1 * si.m, + n_sd_per_mode=(5, 5), + aerosol_modes_by_kappa={ + 0.54: Lognormal( + norm_factor=850 / si.cm**3, m_mode=15 * si.nm, s_geom=1.6 + ), + 1.2: Lognormal( + norm_factor=10 / si.cm**3, m_mode=850 * si.nm, s_geom=1.2 + ), + }, + vertical_velocity=1.0 * si.m / si.s, + initial_pressure=775 * si.mbar, + initial_temperature=274 * si.K, + initial_relative_humidity=0.98, + displacement=250 * si.m, + formulae=Formulae(constants={"MAC": 0.3}), + ) + simulation = Simulation( + settings, + products=( + ParcelDisplacement(name="z"), + AmbientRelativeHumidity(name="RH_percent", unit="%"), + AmbientTemperature(name="T"), + ), + scipy_solver=scipy_solver, + ) + + # act + output = simulation.run() + + # assert + print(np.nanmax((np.asarray(output["products"]["RH"])) - 1) * 100, s_max) + print(output["products"]["T"][-1], T_250m) + print(output["products"]["RH_percent"][-1] - 100, s_250m) + np.testing.assert_approx_equal( + np.nanmax(np.asarray(output["products"]["RH_percent"])) - 100, + s_max, + significant=2, + ) + np.testing.assert_approx_equal( + output["products"]["T"][-1], T_250m, significant=2 + ) + np.testing.assert_approx_equal( + output["products"]["RH_percent"][-1] - 100, s_250m, significant=2 + ) diff --git a/PySDM/source/tests/smoke_tests/parcel_b/__init__.py b/PySDM/source/tests/smoke_tests/parcel_b/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/parcel_b/arabas_and_shima_2017/__init__.py b/PySDM/source/tests/smoke_tests/parcel_b/arabas_and_shima_2017/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/parcel_b/arabas_and_shima_2017/test_conservation.py b/PySDM/source/tests/smoke_tests/parcel_b/arabas_and_shima_2017/test_conservation.py new file mode 100644 index 0000000000000000000000000000000000000000..f8094af257f006e3f03b6dc4a0e06fcd0effd571 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_b/arabas_and_shima_2017/test_conservation.py @@ -0,0 +1,82 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest +from PySDM_examples.Arabas_and_Shima_2017.settings import Settings, setups, w_avgs +from PySDM_examples.Arabas_and_Shima_2017.simulation import Simulation + +from PySDM.backends import CPU, GPU +from PySDM.backends.impl_numba.test_helpers import scipy_ode_condensation_solver + + +def liquid_water_mixing_ratio(simulation: Simulation): + droplet_mass = simulation.particulator.attributes["water mass"].to_ndarray()[0] + droplet_number = simulation.particulator.attributes["multiplicity"].to_ndarray()[0] + mass_of_all_droplets = droplet_number * droplet_mass + env = simulation.particulator.environment + return mass_of_all_droplets / env.mass_of_dry_air + + +class TestConservation: + @staticmethod + @pytest.mark.parametrize("settings_idx", range(len(w_avgs))) + @pytest.mark.parametrize("mass_of_dry_air", (1, 10000)) + @pytest.mark.parametrize("scheme", ("SciPy", "CPU", "GPU")) + @pytest.mark.parametrize("coord", ("WaterMassLogarithm", "WaterMass")) + def test_water_mass_conservation(settings_idx, mass_of_dry_air, scheme, coord): + # Arrange + assert scheme in ("SciPy", "CPU", "GPU") + + settings = Settings( + w_avg=setups[settings_idx].w_avg, + N_STP=setups[settings_idx].N_STP, + r_dry=setups[settings_idx].r_dry, + mass_of_dry_air=mass_of_dry_air, + coord=coord, + ) + settings.n_output = 50 + settings.coord = coord + simulation = Simulation(settings, GPU if scheme == "GPU" else CPU) + initial_total_water_mixing_ratio = ( + settings.initial_water_vapour_mixing_ratio + + liquid_water_mixing_ratio(simulation) + ) + + if scheme == "SciPy": + scipy_ode_condensation_solver.patch_particulator(simulation.particulator) + + # Act + simulation.particulator.products["S_max"].get() + output = simulation.run() + + # Assert + (total_water_mixing_ratio,) = simulation.particulator.environment[ + "water_vapour_mixing_ratio" + ].to_ndarray() + liquid_water_mixing_ratio(simulation) + np.testing.assert_approx_equal( + total_water_mixing_ratio, initial_total_water_mixing_ratio, significant=6 + ) + if scheme != "SciPy": # TODO #1608 + assert simulation.particulator.products["S_max"].get() >= output["RH"][-1] + + @staticmethod + @pytest.mark.parametrize("settings_idx", range(len(w_avgs))) + @pytest.mark.parametrize("mass_of_dry_air", [1, 10000]) + @pytest.mark.parametrize("coord", ("WaterMassLogarithm", "WaterMass")) + def test_energy_conservation(settings_idx, mass_of_dry_air, coord): + # Arrange + settings = Settings( + w_avg=setups[settings_idx].w_avg, + N_STP=setups[settings_idx].N_STP, + r_dry=setups[settings_idx].r_dry, + mass_of_dry_air=mass_of_dry_air, + coord=coord, + ) + simulation = Simulation(settings) + env = simulation.particulator.environment + thd0 = env["thd"] + + # Act + simulation.run() + + # Assert + np.testing.assert_array_almost_equal(thd0.to_ndarray(), env["thd"].to_ndarray()) diff --git a/PySDM/source/tests/smoke_tests/parcel_b/arabas_and_shima_2017/test_displacement.py b/PySDM/source/tests/smoke_tests/parcel_b/arabas_and_shima_2017/test_displacement.py new file mode 100644 index 0000000000000000000000000000000000000000..69766da2f9afc6ea62874d3420b45c8ff17c381a --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_b/arabas_and_shima_2017/test_displacement.py @@ -0,0 +1,26 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest +from PySDM_examples.Arabas_and_Shima_2017.settings import Settings, w_avgs +from PySDM_examples.Arabas_and_Shima_2017.simulation import Simulation +from PySDM.physics import si + + +@pytest.mark.parametrize("w_idx", range(len(w_avgs))) +def test_displacement(w_idx): + # Arrange + settings = Settings( + w_avg=w_avgs[w_idx], + N_STP=44 / si.cm**3, + r_dry=0.1 * si.um, + mass_of_dry_air=1 * si.kg, + ) + settings.n_output = 50 + simulation = Simulation(settings) + + # Act + output = simulation.run() + + # Assert + np.testing.assert_almost_equal(min(output["z"]), 0, decimal=1) + np.testing.assert_almost_equal(max(output["z"]), settings.z_half, decimal=1) diff --git a/PySDM/source/tests/smoke_tests/parcel_b/arabas_and_shima_2017/test_event_rates.py b/PySDM/source/tests/smoke_tests/parcel_b/arabas_and_shima_2017/test_event_rates.py new file mode 100644 index 0000000000000000000000000000000000000000..9f9600b0fd4d819ba2a62ee57127b3c98d586a75 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_b/arabas_and_shima_2017/test_event_rates.py @@ -0,0 +1,38 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest +from PySDM_examples.Arabas_and_Shima_2017.settings import Settings, setups, w_avgs +from PySDM_examples.Arabas_and_Shima_2017.simulation import Simulation + +from PySDM.physics.constants import convert_to, si + + +@pytest.mark.parametrize("settings_idx", range(len(w_avgs))) +def test_event_rates(settings_idx): + # Arrange + settings = Settings( + w_avg=setups[settings_idx].w_avg, + N_STP=setups[settings_idx].N_STP, + r_dry=setups[settings_idx].r_dry, + mass_of_dry_air=1 * si.kg, + ) + const = settings.formulae.constants + settings.n_output = 50 + simulation = Simulation(settings) + + # Act + output = simulation.run() + + # Assert + rip = np.asarray(output["ripening_rate"]) + act = np.asarray(output["activating_rate"]) + dea = np.asarray(output["deactivating_rate"]) + act_max = np.full( + (1,), settings.n_in_dv / simulation.particulator.dt / const.rho_STP + ) + convert_to(act_max, 1 / si.mg) + assert (rip == 0).all() + assert (act > 0).any() + assert (dea > 0).any() + assert 0 < max(act) < act_max[0] + assert 0 < max(dea) < act_max[0] diff --git a/PySDM/source/tests/smoke_tests/parcel_b/arabas_and_shima_2017/test_initialisation.py b/PySDM/source/tests/smoke_tests/parcel_b/arabas_and_shima_2017/test_initialisation.py new file mode 100644 index 0000000000000000000000000000000000000000..016372f8308c4de8e52cd0646751c1b2b6d2cec4 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_b/arabas_and_shima_2017/test_initialisation.py @@ -0,0 +1,64 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest +from PySDM_examples.Arabas_and_Shima_2017.settings import setups +from PySDM_examples.Arabas_and_Shima_2017.simulation import Simulation + +from PySDM import Formulae + +CONST = Formulae().constants + + +class TestInitialisation: + @staticmethod + def simulation_test(var, expected, setup): + simulation = Simulation(setup) + (actual,) = simulation.particulator.environment[var].to_ndarray() + np.testing.assert_approx_equal(actual=actual, desired=expected) + + @staticmethod + @pytest.mark.parametrize("settings_idx", range(len(setups))) + def test_T_initialisation(settings_idx): + setup = setups[settings_idx] + TestInitialisation.simulation_test("T", setup.T0, setup) + + @staticmethod + @pytest.mark.parametrize("settings_idx", range(len(setups))) + def test_RH_initialisation(settings_idx): + setup = setups[settings_idx] + pv0 = setup.p0 / (1 + CONST.eps / setup.initial_water_vapour_mixing_ratio) + pvs = setup.formulae.saturation_vapour_pressure.pvs_water(setup.T0) + TestInitialisation.simulation_test("RH", pv0 / pvs, setup) + + @staticmethod + @pytest.mark.parametrize("settings_idx", range(len(setups))) + def test_p_initialisation(settings_idx): + setup = setups[settings_idx] + TestInitialisation.simulation_test("p", setup.p0, setup) + + @staticmethod + @pytest.mark.parametrize("settings_idx", range(len(setups))) + def test_water_vapour_mixing_ratio_initialisation(settings_idx): + setup = setups[settings_idx] + TestInitialisation.simulation_test( + "water_vapour_mixing_ratio", setup.initial_water_vapour_mixing_ratio, setup + ) + + @staticmethod + @pytest.mark.parametrize("settings_idx", range(len(setups))) + def test_rhod_initialisation(settings_idx): + setup = setups[settings_idx] + pv0 = setup.p0 / (1 + CONST.eps / setup.initial_water_vapour_mixing_ratio) + pd0 = setup.p0 - pv0 + rhod0 = pd0 / CONST.Rd / setup.T0 + TestInitialisation.simulation_test("rhod", rhod0, setup) + + @staticmethod + @pytest.mark.parametrize("settings_idx", range(len(setups))) + def test_thd_initialisation(settings_idx): + setup = setups[settings_idx] + pv0 = setup.p0 / (1 + CONST.eps / setup.initial_water_vapour_mixing_ratio) + pd0 = setup.p0 - pv0 + phys = Formulae().trivia + thd0 = phys.th_std(pd0, setup.T0) + TestInitialisation.simulation_test("thd", thd0, setup) diff --git a/PySDM/source/tests/smoke_tests/parcel_b/arabas_and_shima_2017/test_vs_scipy.py b/PySDM/source/tests/smoke_tests/parcel_b/arabas_and_shima_2017/test_vs_scipy.py new file mode 100644 index 0000000000000000000000000000000000000000..88f062c981149cf814b24ffb21c623d682a9133c --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_b/arabas_and_shima_2017/test_vs_scipy.py @@ -0,0 +1,38 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest +from PySDM_examples.Arabas_and_Shima_2017.settings import setups +from PySDM_examples.Bartman_2020_MasterThesis.fig_5_SCIPY_VS_ADAPTIVE import ( + data as data_method, +) + +rtols = (1e-2,) +schemes = ("CPU", "SciPy") # ,'GPU') # TODO #588 +setups_num = len(setups) + + +@pytest.fixture(scope="module", name="data") +def data_fixture(): + return data_method(n_output=20, rtols=rtols, schemes=schemes, setups_num=setups_num) + + +def split(arg1, arg2): + return arg1[0 : np.argmax(arg2) + 1], arg1[np.argmax(arg2) : -1] + + +@pytest.mark.parametrize("settings_idx", range(setups_num)) +@pytest.mark.parametrize("rtol", rtols) +@pytest.mark.parametrize("leg", ["ascent", "descent"]) +@pytest.mark.parametrize("scheme", ("CPU",)) # 'GPU')) # TODO #588 +def test_vs_scipy(settings_idx, data, rtol, leg, scheme): + # Arrange + saturation = {} + for sch in schemes: + sut = data[sch][rtol][settings_idx] + ascent, descent = split(sut["RH"], sut["z"]) + saturation[sch] = ascent if leg == "ascent" else descent + + # Assert + desired = np.array(saturation["SciPy"]) + actual = np.array(saturation[scheme]) + assert np.mean((desired - actual) ** 2) < rtol diff --git a/PySDM/source/tests/smoke_tests/parcel_c/__init__.py b/PySDM/source/tests/smoke_tests/parcel_c/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/parcel_c/abade_and_albuquerque_2024/__init__.py b/PySDM/source/tests/smoke_tests/parcel_c/abade_and_albuquerque_2024/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/parcel_c/abade_and_albuquerque_2024/test_fig_2.py b/PySDM/source/tests/smoke_tests/parcel_c/abade_and_albuquerque_2024/test_fig_2.py new file mode 100644 index 0000000000000000000000000000000000000000..fc68c1e871cd71a5cd71f0daf298f5814fb3af99 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_c/abade_and_albuquerque_2024/test_fig_2.py @@ -0,0 +1,104 @@ +""" +checking consistency with values in the paper for Figure 2 +""" + +from pathlib import Path + +import numpy as np +import pytest + +from open_atmos_jupyter_utils import notebook_vars +from PySDM_examples import Abade_and_Albuquerque_2024 + +from PySDM.physics import si + + +PLOT = False +UPDRAFTS = (3.6, 0.4) +N_SD = 64 + + +@pytest.fixture(scope="session", name="variables") +def variables_fixture(): + return notebook_vars( + file=Path(Abade_and_Albuquerque_2024.__file__).parent / "fig_2.ipynb", plot=PLOT + ) + + +class TestFig2: + @staticmethod + @pytest.mark.parametrize( + "model, key", + ( + *[(f"Bulk-{updraft}", "water") for updraft in UPDRAFTS], + *[ + (f"Homogeneous-{im_freeze}-{N_SD}-{updraft}", "ice+water") + for updraft in UPDRAFTS + for im_freeze in ("ABIFM", "INAS") + ], + ), + ) + def test_cloud_base(variables, model, key): + data = variables["datasets"][model]["realisations"][0] + height = np.asarray(data["height"]) + assert (np.asarray(data[key])[height < 0.9 * si.km] < 0.01 * si.g / si.kg).all() + assert (np.asarray(data[key])[height > 1.1 * si.km] > 0.05 * si.g / si.kg).all() + + @staticmethod + @pytest.mark.parametrize( + "model, var_name, desired_value", + ( + ("Bulk-3.6", "vapour", 0.45 * si.g / si.kg), + ("Bulk-3.6", "water", 1.05 * si.g / si.kg), + ("Bulk-0.4", "vapour", 0.42 * si.g / si.kg), + ("Bulk-0.4", "water", 1.08 * si.g / si.kg), + ("Homogeneous-INAS-64-3.6", "ice", 0.81 * si.g / si.kg), + ("Homogeneous-INAS-64-3.6", "water", 0.25 * si.g / si.kg), + ("Homogeneous-INAS-64-3.6", "vapour", 0.45 * si.g / si.kg), + ("Homogeneous-ABIFM-64-3.6", "ice", 0.20 * si.g / si.kg), + ("Homogeneous-ABIFM-64-3.6", "water", 0.86 * si.g / si.kg), + ("Homogeneous-ABIFM-64-3.6", "vapour", 0.45 * si.g / si.kg), + ("Homogeneous-INAS-64-0.4", "ice", 1.1 * si.g / si.kg), + ("Homogeneous-INAS-64-0.4", "water", 2.06e-9), + ("Homogeneous-INAS-64-0.4", "vapour", 0.33 * si.g / si.kg), + ("Homogeneous-ABIFM-64-0.4", "ice", 1.1 * si.g / si.kg), + ("Homogeneous-ABIFM-64-0.4", "water", 2.07e-9), + ("Homogeneous-ABIFM-64-0.4", "vapour", 0.34 * si.g / si.kg), + ), + ) + def test_values_at_cloud_top(variables, model, var_name, desired_value): + np.testing.assert_approx_equal( + desired=desired_value, + actual=variables["datasets"][model]["realisations"][0][var_name][-1], + significant=2, + ) + + @staticmethod + @pytest.mark.parametrize( + "model, key", + [ + *[ + (f"Bulk-{updraft}", key) + for updraft in UPDRAFTS + for key in ("water", "vapour") + ], + *[ + (f"Homogeneous-{im_freeze}-{N_SD}-{updraft}", key) + for updraft in UPDRAFTS + for im_freeze in ("ABIFM", "INAS") + for key in ("water", "ice", "vapour", "ice+water", "total") + ], + ], + ) + def test_monotonicity(variables, model, key): + delta_mixing_ratio = np.diff( + variables["datasets"][model]["realisations"][0][key] + ) + if key in ("ice", "ice+water"): + assert (delta_mixing_ratio >= 0).all() + assert (delta_mixing_ratio > 0).any() + elif key == "vapour": + assert (delta_mixing_ratio <= 0).all() + assert (delta_mixing_ratio < 0).any() + elif key == "total": + assert (delta_mixing_ratio < 5e-6).all() diff --git a/PySDM/source/tests/smoke_tests/parcel_c/abdul_razzak_ghan_2000/__init__.py b/PySDM/source/tests/smoke_tests/parcel_c/abdul_razzak_ghan_2000/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/parcel_c/abdul_razzak_ghan_2000/test_ARG_example.py b/PySDM/source/tests/smoke_tests/parcel_c/abdul_razzak_ghan_2000/test_ARG_example.py new file mode 100644 index 0000000000000000000000000000000000000000..d0eb427ecd0a4553f817db59e8ad026c7951cee8 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_c/abdul_razzak_ghan_2000/test_ARG_example.py @@ -0,0 +1,80 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest +from PySDM_examples.Abdul_Razzak_Ghan_2000 import data_from_ARG2000_paper as ARG_paper +from PySDM_examples.Abdul_Razzak_Ghan_2000.run_ARG_parcel import run_parcel + +from PySDM.physics import si + + +class TestARGExample: # pylint: disable=too-few-public-methods + @staticmethod + @pytest.mark.parametrize("N2i", np.linspace(100, 5000, 5) / si.cm**3) + def test_ARG_fig1(N2i): + w = 0.5 * si.m / si.s + sol2 = 1.0 # 100% ammonium sulfate + rad2 = 50.0 * si.nm + + n_sd_per_mode = 10 + + idx = np.argmin(np.abs(ARG_paper.Fig1_N2_param - N2i * si.cm**3)) + output = run_parcel(w, sol2, N2i, rad2, n_sd_per_mode) + + assert np.isclose( + output.activated_fraction_S[0], + ARG_paper.Fig1_AF_param[idx], + atol=output.error[0] * 2, + ) + assert np.isclose( + output.activated_fraction_V[0], + ARG_paper.Fig1_AF_param[idx], + atol=output.error[0] * 2, + ) + + @staticmethod + @pytest.mark.parametrize("N2i", np.linspace(100, 5000, 5) / si.cm**3) + def test_ARG_fig2a(N2i): + w = 0.5 * si.m / si.s + sol2 = 0.1 # 10% ammonium sulfate, 90% insoluble + rad2 = 50.0 * si.nm + + n_sd_per_mode = 10 + + idx = np.argmin(np.abs(ARG_paper.Fig2a_N2_param - N2i * si.cm**3)) + output = run_parcel(w, sol2, N2i, rad2, n_sd_per_mode) + + assert np.isclose( + output.activated_fraction_S[0], + ARG_paper.Fig2a_AF_param[idx], + atol=output.error[0] * 2, + ) + assert np.isclose( + output.activated_fraction_V[0], + ARG_paper.Fig2a_AF_param[idx], + atol=output.error[0] * 2, + ) + + @staticmethod + @pytest.mark.parametrize( + "sol2i", np.linspace(0.1, 1, 5) + ) # X% ammonium sulfate, (1-X)% insoluble + def test_ARG_fig3a(sol2i): + N2 = 100 / si.cm**3 + w = 0.5 * si.m / si.s + rad2 = 50.0 * si.nm + + n_sd_per_mode = 10 + + idx = np.argmin(np.abs(ARG_paper.Fig3a_sol2_param - sol2i)) + output = run_parcel(w, sol2i, N2, rad2, n_sd_per_mode) + + assert np.isclose( + output.activated_fraction_S[0], + ARG_paper.Fig3a_AF_param[idx], + atol=output.error[0] * 2, + ) + assert np.isclose( + output.activated_fraction_V[0], + ARG_paper.Fig3a_AF_param[idx], + atol=output.error[0] * 2, + ) diff --git a/PySDM/source/tests/smoke_tests/parcel_c/abdul_razzak_ghan_2000/test_just_do_it.py b/PySDM/source/tests/smoke_tests/parcel_c/abdul_razzak_ghan_2000/test_just_do_it.py new file mode 100644 index 0000000000000000000000000000000000000000..de70a1da35aea1fddbbae0efcbef8b32a09a3729 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_c/abdul_razzak_ghan_2000/test_just_do_it.py @@ -0,0 +1,18 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +from PySDM_examples.Abdul_Razzak_Ghan_2000.run_ARG_parcel import run_parcel + +from PySDM.physics import si + + +def test_just_do_it(): + # act + output = run_parcel( + w=1 * si.m / si.s, + sol2=0.5, + N2=100 / si.cm**3, + rad2=100 * si.nm, + n_sd_per_mode=10, + ) + + # assert + assert (output.activated_fraction_S[:] <= 1).all() diff --git a/PySDM/source/tests/smoke_tests/parcel_c/abdul_razzak_ghan_2000/test_single_supersaturation_peak.py b/PySDM/source/tests/smoke_tests/parcel_c/abdul_razzak_ghan_2000/test_single_supersaturation_peak.py new file mode 100644 index 0000000000000000000000000000000000000000..9f2b64bcbff5407ce3e8be4c51d2ef93665f3667 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_c/abdul_razzak_ghan_2000/test_single_supersaturation_peak.py @@ -0,0 +1,125 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest +from matplotlib import pyplot +from PySDM_examples.Abdul_Razzak_Ghan_2000.aerosol import CONSTANTS_ARG +from scipy import signal + +from PySDM import Builder, Formulae +from PySDM import products as PySDM_products +from PySDM.backends import CPU +from PySDM.backends.impl_numba.test_helpers import scipy_ode_condensation_solver +from PySDM.dynamics import AmbientThermodynamics, Condensation +from PySDM.environments import Parcel +from PySDM.initialisation.hygroscopic_equilibrium import equilibrate_wet_radii +from PySDM.initialisation.sampling.spectral_sampling import ConstantMultiplicity +from PySDM.initialisation.spectra import Lognormal +from PySDM.physics import si + + +@pytest.mark.parametrize( + "rtol_thd", + ( + pytest.param(1e-6, marks=pytest.mark.xfail(strict=True)), + 1e-7, + 1e-8, + 1e-9, + ), +) +@pytest.mark.parametrize("rtol_x", (1e-7,)) +@pytest.mark.parametrize("adaptive", (True,)) +@pytest.mark.parametrize("scheme", ("PySDM",)) +def test_single_saturation_peak( + adaptive, scheme, rtol_x, rtol_thd, plot=False +): # pylint: disable=too-many-locals + # arrange + products = ( + PySDM_products.WaterMixingRatio(unit="g/kg", name="liquid water mixing ratio"), + PySDM_products.PeakSaturation(name="S max"), + PySDM_products.AmbientRelativeHumidity(name="RH"), + PySDM_products.ParcelDisplacement(name="z"), + ) + n_steps = 70 + n_sd = 2 + kappa = 0.4 + spectrum = Lognormal(norm_factor=5000 / si.cm**3, m_mode=50.0 * si.nm, s_geom=2.0) + formulae = Formulae(constants=CONSTANTS_ARG) + builder = Builder( + backend=CPU(formulae), + n_sd=n_sd, + environment=Parcel( + dt=2 * si.s, + mass_of_dry_air=1e3 * si.kg, + p0=1000 * si.hPa, + initial_water_vapour_mixing_ratio=22.76 * si.g / si.kg, + w=0.5 * si.m / si.s, + T0=300 * si.K, + ), + ) + builder.add_dynamic(AmbientThermodynamics()) + builder.add_dynamic( + Condensation( + adaptive=adaptive, + rtol_x=rtol_x, + rtol_thd=rtol_thd, + ) + ) + + r_dry, concentration = ConstantMultiplicity(spectrum).sample_deterministic(n_sd) + v_dry = builder.formulae.trivia.volume(radius=r_dry) + r_wet = equilibrate_wet_radii( + r_dry=r_dry, + environment=builder.particulator.environment, + kappa_times_dry_volume=kappa * v_dry, + ) + specific_concentration = concentration / builder.formulae.constants.rho_STP + attributes = { + "multiplicity": specific_concentration + * builder.particulator.environment.mass_of_dry_air, + "dry volume": v_dry, + "kappa times dry volume": kappa * v_dry, + "volume": builder.formulae.trivia.volume(radius=r_wet), + } + + particulator = builder.build(attributes, products=products) + + if scheme == "SciPy": + scipy_ode_condensation_solver.patch_particulator(particulator) + + output = {product.name: [] for product in particulator.products.values()} + output_attributes = {"volume": tuple([] for _ in range(particulator.n_sd))} + + # act + for _ in range(n_steps): + particulator.run(steps=1) + for product in particulator.products.values(): + value = product.get() + output[product.name].append(value[0]) + for key, attr in output_attributes.items(): + attr_data = particulator.attributes[key].to_ndarray() + for drop_id in range(particulator.n_sd): + attr[drop_id].append(attr_data[drop_id]) + + # plot + for drop_id, volume in enumerate(output_attributes["volume"]): + pyplot.semilogx( + particulator.formulae.trivia.radius(volume=np.asarray(volume)) / si.um, + output["z"], + color="black", + label="drop size (bottom axis)", + ) + pyplot.xlabel("radius [um]") + pyplot.ylabel("z [m]") + twin = pyplot.twiny() + twin.plot(np.asarray(output["S max"]) - 1, output["z"], label="S max (top axis)") + twin.plot(np.asarray(output["RH"]) - 1, output["z"], label="ambient RH (top axis)") + twin.legend(loc="upper center") + twin.set_xlim(-0.001, 0.0015) + pyplot.legend(loc="lower right") + pyplot.grid() + pyplot.title(f"rtol_thd={rtol_thd}; rtol_x={rtol_x}") + if plot: + pyplot.show() + + # assert + assert signal.argrelextrema(np.asarray(output["RH"]), np.greater)[0].shape[0] == 1 diff --git a/PySDM/source/tests/smoke_tests/parcel_c/grabowski_and_pawlowska_2023/__init__.py b/PySDM/source/tests/smoke_tests/parcel_c/grabowski_and_pawlowska_2023/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/parcel_c/grabowski_and_pawlowska_2023/test_condensation_tolerance.py b/PySDM/source/tests/smoke_tests/parcel_c/grabowski_and_pawlowska_2023/test_condensation_tolerance.py new file mode 100644 index 0000000000000000000000000000000000000000..0c7912a2ce27a6d5d3274ff5278e1d429bfef508 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_c/grabowski_and_pawlowska_2023/test_condensation_tolerance.py @@ -0,0 +1,62 @@ +""" +test for condensation tolerance parameters: rtol_thd and rtol_x, +checking if supersaturation has more than one local maximum +""" + +import numpy as np +import pytest +from PySDM_examples.Grabowski_and_Pawlowska_2023 import Settings, Simulation +from PySDM_examples.Grabowski_and_Pawlowska_2023.settings import condensation_tolerance +from scipy import signal + +from PySDM.dynamics import condensation +from PySDM.physics import si +from PySDM.products import AmbientRelativeHumidity + +PRODUCTS = [ + AmbientRelativeHumidity(name="S_max", var="RH"), +] + +N_SD = 25 +DZ = 10 * si.m +VELOCITIES_CM_PER_S = (25, 100, 400) +AEROSOLS = ("polluted",) + + +@pytest.mark.parametrize( + "rtol_cond", + ( + condensation_tolerance, + pytest.param( + condensation.DEFAULTS.rtol_thd, marks=pytest.mark.xfail(strict=True) + ), + ), +) +@pytest.mark.parametrize("aerosol", AEROSOLS) +@pytest.mark.parametrize("w_cm_per_s", VELOCITIES_CM_PER_S) +def test_condensation_tolerance( + rtol_cond: float, + w_cm_per_s: int, + aerosol: str, +): + # arrange + output = Simulation( + Settings( + n_sd=25, + dt=10 * si.s, + vertical_velocity=w_cm_per_s * si.cm / si.s, + aerosol=aerosol, + rtol_thd=rtol_cond, + rtol_x=rtol_cond, + ), + products=PRODUCTS, + ).run() + + assert ( + signal.find_peaks( + np.asarray(output["products"]["S_max"]), + height=(1, None), + prominence=(1e-5, None), + )[0].shape[0] + == 1 + ) diff --git a/PySDM/source/tests/smoke_tests/parcel_c/grabowski_and_pawlowska_2023/test_figure_1_and_2.py b/PySDM/source/tests/smoke_tests/parcel_c/grabowski_and_pawlowska_2023/test_figure_1_and_2.py new file mode 100644 index 0000000000000000000000000000000000000000..bda07b259d86dc2b41cd8b043d09d194785a4228 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_c/grabowski_and_pawlowska_2023/test_figure_1_and_2.py @@ -0,0 +1,129 @@ +""" +test against values read from plots in +[Grabowski and Pawlowska](https://doi.org/10.1029/2022GL101917) paper +""" + +import numpy as np +import pytest +from PySDM_examples.Grabowski_and_Pawlowska_2023 import Settings, Simulation + +from PySDM import Formulae +from PySDM.physics import si +from PySDM.products import AmbientRelativeHumidity + +TRIVIA = Formulae().trivia + +PRODUCTS = [ + AmbientRelativeHumidity(name="S_max", var="RH"), +] + +N_SD = 25 + +VELOCITIES_CM_PER_S = (25, 100) + +AEROSOLS = ("pristine", "polluted") + +DZ = 500 * si.m + +RTOL = 0.01 + + +@pytest.fixture(scope="session", name="outputs") +def outputs_fixture(): + outputs = {} + for aerosol in AEROSOLS: + outputs[aerosol] = {} + for w_cm_per_s in VELOCITIES_CM_PER_S: + vertical_velocity = w_cm_per_s * si.cm / si.s + outputs[aerosol][w_cm_per_s] = Simulation( + Settings( + n_sd=N_SD, + dt=DZ / vertical_velocity, + vertical_velocity=vertical_velocity, + aerosol=aerosol, + ), + products=PRODUCTS, + ).run() + return outputs + + +class TestFigure1And2: + @staticmethod + @pytest.mark.parametrize("aerosol", AEROSOLS) + @pytest.mark.parametrize("w_cm_per_s", VELOCITIES_CM_PER_S) + @pytest.mark.parametrize("attribute", ("volume", "equilibrium saturation")) + @pytest.mark.parametrize("drop_id", (0, -1)) + def test_values_at_final_step( + outputs: dict, + w_cm_per_s: int, + aerosol: str, + attribute: str, + drop_id: int, + ): + # arrange + output = outputs[aerosol][w_cm_per_s] + expected = { + "volume": { + "pristine": { + 25: { + 0: TRIVIA.volume(radius=0.04 * si.um), + -1: TRIVIA.volume(radius=20 * si.um), + }, + 100: { + 0: TRIVIA.volume(radius=0.04 * si.um), + -1: TRIVIA.volume(radius=18 * si.um), + }, + }, + "polluted": { + 25: { + 0: TRIVIA.volume(radius=0.04 * si.um), + -1: TRIVIA.volume(radius=10 * si.um), + }, + 100: { + 0: TRIVIA.volume(radius=0.04 * si.um), + -1: TRIVIA.volume(radius=10 * si.um), + }, + }, + }, + "equilibrium saturation": { + "pristine": { + 25: {0: 0.05 / 100 + 1, -1: 0.005 / 100 + 1}, + 100: {0: 0.15 / 100 + 1, -1: 0.005 / 100 + 1}, + }, + "polluted": { + 25: {0: 0.025 / 100 + 1, -1: 0.005 / 100 + 1}, + 100: {0: 0.06 / 100 + 1, -1: 0.004 / 100 + 1}, + }, + }, + }[attribute][aerosol][w_cm_per_s][drop_id] + + # assert + assert np.isclose( + output["attributes"][attribute][drop_id][-1], + expected, + rtol=RTOL, + ).all() + + @staticmethod + @pytest.mark.parametrize("aerosol", AEROSOLS) + @pytest.mark.parametrize("w_cm_per_s", VELOCITIES_CM_PER_S) + @pytest.mark.parametrize( + "rtol", (RTOL, pytest.param(RTOL / 100, marks=pytest.mark.xfail(strict=True))) + ) + def test_ambient_humidity( + outputs: dict, + w_cm_per_s: int, + aerosol: str, + rtol: float, + ): + output = outputs[aerosol][w_cm_per_s] + attributes = output["attributes"] + for vol, crit_vol, eq_s in zip( + attributes["volume"], + attributes["critical volume"], + attributes["equilibrium saturation"], + ): + if np.all(vol < crit_vol): + assert np.isclose( + output["products"]["S_max"], eq_s, rtol=rtol, atol=0 + ).all() diff --git a/PySDM/source/tests/smoke_tests/parcel_c/grabowski_and_pawlowska_2023/test_figure_3.py b/PySDM/source/tests/smoke_tests/parcel_c/grabowski_and_pawlowska_2023/test_figure_3.py new file mode 100644 index 0000000000000000000000000000000000000000..7ad5cfdd6838a62452118bb7b286bf41518a2448 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_c/grabowski_and_pawlowska_2023/test_figure_3.py @@ -0,0 +1,104 @@ +""" +test against values read from plots in +[Grabowski and Pawlowska](https://doi.org/10.1029/2022GL101917) paper +""" + +import numpy as np +import pytest +from PySDM_examples.Grabowski_and_Pawlowska_2023 import Settings, Simulation + +from PySDM.physics import si +from PySDM.products import ( + ActivatedParticleConcentration, + AreaStandardDeviation, + MeanVolumeRadius, +) + +PRODUCTS = [ + ActivatedParticleConcentration( + name="n_act", count_activated=True, count_unactivated=False, stp=True + ), + MeanVolumeRadius(name="r_vol", count_activated=True, count_unactivated=False), + AreaStandardDeviation( + name="area_std", count_activated=True, count_unactivated=False + ), +] + +N_SD = 25 + +VELOCITIES_CM_PER_S = (25, 100, 400) + +AEROSOLS = ("pristine", "polluted") + +DZ = 500 * si.m +RTOL = 0.05 + + +@pytest.fixture(scope="session", name="outputs") +def outputs_fixture(): + outputs = {} + for aerosol in AEROSOLS: + outputs[aerosol] = {} + for w_cm_per_s in VELOCITIES_CM_PER_S: + vertical_velocity = w_cm_per_s * si.cm / si.s + outputs[aerosol][w_cm_per_s] = Simulation( + Settings( + n_sd=N_SD, + dt=DZ / vertical_velocity, + vertical_velocity=vertical_velocity, + aerosol=aerosol, + ), + products=PRODUCTS, + ).run() + return outputs + + +@pytest.mark.parametrize("w_cm_per_s", VELOCITIES_CM_PER_S) +@pytest.mark.parametrize("aerosol", AEROSOLS) +@pytest.mark.parametrize("product", ("r_vol", "n_act", "area_std")) +@pytest.mark.parametrize( + "rtol", (RTOL, pytest.param(RTOL / 500, marks=pytest.mark.xfail(strict=True))) +) +def test_values_at_final_step( + outputs: dict, w_cm_per_s: int, aerosol: str, product: str, rtol: float +): + # arrange + output = outputs[aerosol][w_cm_per_s] + expected = { + "r_vol": { + "pristine": {25: 20 * si.um, 100: 18 * si.um, 400: 15 * si.um}, + "polluted": {25: 10 * si.um, 100: 9 * si.um, 400: 9 * si.um}, + }, + "n_act": { + "pristine": { + 25: 60 * si.cm**-3, + 100: 100 * si.cm**-3, + 400: 180 * si.cm**-3, + }, + "polluted": { + 25: 350 * si.cm**-3, + 100: 550 * si.cm**-3, + 400: 550 * si.cm**-3, + }, + }, + "area_std": { + "pristine": { + 25: 8 * si.um**2 * 4 * np.pi, + 100: 10 * si.um**2 * 4 * np.pi, + 400: 6.5 * si.um**2 * 4 * np.pi, + }, + "polluted": { + 25: 11 * si.um**2 * 4 * np.pi, + 100: 6 * si.um**2 * 4 * np.pi, + 400: 2.5 * si.um**2 * 4 * np.pi, + }, + }, + }[product] + + # assert + assert np.isclose( + np.log(output["products"][product][-1]), + np.log(expected[aerosol][w_cm_per_s]), + rtol=rtol, + atol=0, + ).all() diff --git a/PySDM/source/tests/smoke_tests/parcel_c/grabowski_and_pawlowska_2023/test_figure_4.py b/PySDM/source/tests/smoke_tests/parcel_c/grabowski_and_pawlowska_2023/test_figure_4.py new file mode 100644 index 0000000000000000000000000000000000000000..d5c675f1afd89bcd1f891dd80e42f2bd1b0d84eb --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_c/grabowski_and_pawlowska_2023/test_figure_4.py @@ -0,0 +1,55 @@ +""" +test against values read from plots in +[Grabowski and Pawlowska 2023](https://doi.org/10.1029/2022GL101917) paper +""" + +import numpy as np +import pytest +from PySDM_examples.Grabowski_and_Pawlowska_2023 import Settings, Simulation + +from PySDM.physics import si +from PySDM.products import ActivatedMeanRadius, RadiusStandardDeviation + +PRODUCTS = [ + ActivatedMeanRadius(name="r_act", count_activated=True, count_unactivated=False), + RadiusStandardDeviation( + name="r_std", count_activated=True, count_unactivated=False + ), +] +DZ = 250 * si.m +N_SD = 32 +RTOL = 0.3 + + +@pytest.mark.parametrize("w_cm_per_s", (25, 100, 400)) +@pytest.mark.parametrize("aerosol", ("pristine", "polluted")) +def test_values_at_final_step(w_cm_per_s: int, aerosol: str): + # arrange + vertical_velocity = w_cm_per_s * si.cm / si.s + output = Simulation( + Settings( + n_sd=N_SD, + dt=DZ / vertical_velocity, + vertical_velocity=vertical_velocity, + aerosol=aerosol, + ), + products=PRODUCTS, + ).run() + + # act + rel_dispersion_at_final_step = np.asarray( + output["products"]["r_std"][-1] + ) / np.asarray(output["products"]["r_act"][-1]) + + # assert + assert np.isclose( + np.log(rel_dispersion_at_final_step), + np.log( + { + "pristine": {25: 0.01, 100: 0.02, 400: 0.015}, + "polluted": {25: 0.09, 100: 0.03, 400: 0.015}, + }[aerosol][w_cm_per_s] + ), + rtol=RTOL, + atol=0, + ).all() diff --git a/PySDM/source/tests/smoke_tests/parcel_c/grabowski_and_pawlowska_2023/test_ripening_rate.py b/PySDM/source/tests/smoke_tests/parcel_c/grabowski_and_pawlowska_2023/test_ripening_rate.py new file mode 100644 index 0000000000000000000000000000000000000000..4aeccfff0b1b43d3914bb957dd739efc67d70a0f --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_c/grabowski_and_pawlowska_2023/test_ripening_rate.py @@ -0,0 +1,73 @@ +""" +test for values of the ripening rate +""" + +import numpy as np +import pytest +from PySDM_examples.Grabowski_and_Pawlowska_2023 import Settings, Simulation + +from PySDM.physics import si +from PySDM.products import RipeningRate + +PRODUCTS = [ + RipeningRate( + name="ripening", + ) +] + +N_SD = 200 + +VELOCITIES_CM_PER_S = (25, 100) + +AEROSOLS = ("pristine", "polluted") + +DT = 1 * si.s + +RTOL = 0.1 + + +@pytest.fixture(scope="session", name="outputs") +def outputs_fixture(): + outputs = {} + for aerosol in AEROSOLS: + outputs[aerosol] = {} + for w_cm_per_s in VELOCITIES_CM_PER_S: + vertical_velocity = w_cm_per_s * si.cm / si.s + outputs[aerosol][w_cm_per_s] = Simulation( + Settings( + n_sd=N_SD, + dt=DT, + vertical_velocity=vertical_velocity, + aerosol=aerosol, + ), + products=PRODUCTS, + ).run() + return outputs + + +@pytest.mark.parametrize("aerosol", AEROSOLS) +@pytest.mark.parametrize("w_cm_per_s", VELOCITIES_CM_PER_S) +def test_ripening_rate( + outputs: dict, + w_cm_per_s: int, + aerosol: str, +): + # arrange + expected = { + "pristine": { + 25: 0, + 100: 0, + }, + "polluted": { + 25: 2.8 * 1e8, + 100: 4 * 1e8, + }, + }[aerosol][w_cm_per_s] + output = outputs[aerosol][w_cm_per_s]["products"]["ripening"] + + # assert + assert np.isclose( + np.max(output), + expected, + rtol=RTOL, + ).all() diff --git a/PySDM/source/tests/smoke_tests/parcel_d/__init__.py b/PySDM/source/tests/smoke_tests/parcel_d/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/parcel_d/graf_et_al_2019/__init__.py b/PySDM/source/tests/smoke_tests/parcel_d/graf_et_al_2019/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/parcel_d/graf_et_al_2019/test_fig_4.py b/PySDM/source/tests/smoke_tests/parcel_d/graf_et_al_2019/test_fig_4.py new file mode 100644 index 0000000000000000000000000000000000000000..e8a0dfed3218cb0e2bdc263fda63be55d6278a42 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_d/graf_et_al_2019/test_fig_4.py @@ -0,0 +1,33 @@ +"""tests ensuring values on plots match those in the paper""" + +from pathlib import Path + +import pytest + +from open_atmos_jupyter_utils import notebook_vars +from PySDM_examples import Graf_et_al_2019 + +from PySDM.physics import si + +PLOT = False + + +@pytest.fixture(scope="session", name="variables") +def variables_fixture(): + return notebook_vars( + file=Path(Graf_et_al_2019.__file__).parent / "figure_4.ipynb", plot=PLOT + ) + + +@pytest.mark.parametrize( + "level_name, expected_height", (("CB", 1 * si.km), ("0C", 2.24 * si.km)) +) +def test_fig_4(variables, level_name, expected_height): + # arrange + tolerance = 50 * si.m + + # act + actual = variables["levels"][level_name] + variables["alt_initial"] + + # assert + assert abs(expected_height - actual) < tolerance diff --git a/PySDM/source/tests/smoke_tests/parcel_d/graf_et_al_2019/test_table_1.py b/PySDM/source/tests/smoke_tests/parcel_d/graf_et_al_2019/test_table_1.py new file mode 100644 index 0000000000000000000000000000000000000000..7f2383e754438228448155250a2ab3de86f2868f --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_d/graf_et_al_2019/test_table_1.py @@ -0,0 +1,56 @@ +"""tests ensuring values on plots match those in the paper""" + +from pathlib import Path + +import numpy as np +import pytest + +from open_atmos_jupyter_utils import notebook_vars +from PySDM_examples import Graf_et_al_2019 + +from PySDM.physics.constants import PER_MILLE + +PLOT = False + + +@pytest.fixture(scope="session", name="variables") +def variables_fixture(): + return notebook_vars( + file=Path(Graf_et_al_2019.__file__).parent / "Table_1.ipynb", plot=PLOT + ) + + +@pytest.mark.parametrize( + "temp_celsius, phases, case, var, diff", + ( + (20, "l_v", "A", "diff_delta_2H", 78.2 * PER_MILLE), + (20, "l_v", "A", "diff_delta_18O", 9.7 * PER_MILLE), + (20, "l_v", "A", "diff_d_excess", 0.7 * PER_MILLE), + (20, "l_v", "B", "diff_delta_2H", 68 * PER_MILLE), + (20, "l_v", "B", "diff_delta_18O", 9.5 * PER_MILLE), + (20, "l_v", "B", "diff_d_excess", -8.4 * PER_MILLE), + (0, "l_v", "A", "diff_delta_2H", 103.3 * PER_MILLE), + (0, "l_v", "A", "diff_delta_18O", 11.6 * PER_MILLE), + (0, "l_v", "A", "diff_d_excess", 10.5 * PER_MILLE), + (0, "l_v", "B", "diff_delta_2H", 89.9 * PER_MILLE), + (0, "l_v", "B", "diff_delta_18O", 11.4 * PER_MILLE), + (0, "l_v", "B", "diff_d_excess", -1.6 * PER_MILLE), + (0, "s_v", "A", "diff_delta_2H", 121.3 * PER_MILLE), + (0, "s_v", "A", "diff_delta_18O", 15.1 * PER_MILLE), + (0, "s_v", "A", "diff_d_excess", 0.6 * PER_MILLE), + (0, "s_v", "B", "diff_delta_2H", 105.4 * PER_MILLE), + (0, "s_v", "B", "diff_delta_18O", 14.9 * PER_MILLE), + (0, "s_v", "B", "diff_d_excess", -13.4 * PER_MILLE), + ), +) +# pylint: disable=too-many-arguments +def test_table_1(variables, temp_celsius, phases, case, var, diff): + # arrange + three_for_per_mille = 3 + decimal = three_for_per_mille + 1 + + # act + actual = variables["table_data"][temp_celsius][phases][case][var] + + # assert + np.testing.assert_almost_equal(actual=actual, desired=diff, decimal=decimal) diff --git a/PySDM/source/tests/smoke_tests/parcel_d/jensen_and_nugent_2017/__init__.py b/PySDM/source/tests/smoke_tests/parcel_d/jensen_and_nugent_2017/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/parcel_d/jensen_and_nugent_2017/test_fig_1.py b/PySDM/source/tests/smoke_tests/parcel_d/jensen_and_nugent_2017/test_fig_1.py new file mode 100644 index 0000000000000000000000000000000000000000..0081336f4e77c6b52bdbb37c125346781d8c7739 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_d/jensen_and_nugent_2017/test_fig_1.py @@ -0,0 +1,51 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring + +from pathlib import Path + +import numpy as np +import pytest +from scipy import signal + +from open_atmos_jupyter_utils import notebook_vars +from PySDM_examples import Jensen_and_Nugent_2017 + +from PySDM.physics import si + +PLOT = False + + +@pytest.fixture(scope="session", name="variables") +def variables_fixture(): + return notebook_vars( + file=Path(Jensen_and_Nugent_2017.__file__).parent / "Fig_1.ipynb", plot=PLOT + ) + + +class TestFig1: + @staticmethod + @pytest.mark.parametrize( + "idx, value", + ( + (0, 3.7e-1 / si.cm**3), + (-1, 2e-2 / si.cm**3), + ), + ) + def test_dn_dlogr_values(variables, idx, value): + np.testing.assert_approx_equal( + actual=variables["dN_dlogr"][idx], desired=value, significant=2 + ) + + @staticmethod + def test_two_maxima(variables): + assert ( + signal.argrelextrema(np.asarray(variables["dN_dlogr"]), np.greater)[ + 0 + ].shape[0] + == 2 + ) + + @staticmethod + def test_maximal_value(variables): + np.testing.assert_approx_equal( + actual=max(variables["dN_dlogr"]), desired=2.3e2 / si.cm**3, significant=2 + ) diff --git a/PySDM/source/tests/smoke_tests/parcel_d/jensen_and_nugent_2017/test_fig_3_and_tab_4_upper_rows.py b/PySDM/source/tests/smoke_tests/parcel_d/jensen_and_nugent_2017/test_fig_3_and_tab_4_upper_rows.py new file mode 100644 index 0000000000000000000000000000000000000000..8aca4f5b868da09a850404370a8b80e34f248c56 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_d/jensen_and_nugent_2017/test_fig_3_and_tab_4_upper_rows.py @@ -0,0 +1,142 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring + +from pathlib import Path + +import numpy as np +import pytest +from scipy import signal + +from open_atmos_jupyter_utils import notebook_vars +from PySDM_examples import Jensen_and_Nugent_2017 +from PySDM.physics.constants import PER_CENT + +from PySDM.physics import si + +PLOT = False + + +def find_cloud_base_index(products): + for index, value in enumerate(products["S_max"]): + if value > 1: + cloud_base_index = index + break + return cloud_base_index + + +def find_max_alt_index(products): + return np.argmax(products["z"]) + + +@pytest.fixture(scope="session", name="variables") +def variables_fixture(): + return notebook_vars( + file=Path(Jensen_and_Nugent_2017.__file__).parent + / "Fig_3_and_Tab_4_upper_rows.ipynb", + plot=PLOT, + ) + + +class TestFig3: + @staticmethod + def test_height_range(variables): + """note: in the plot the y axis has cloud-base height subtracted, here not""" + z_minus_z0 = ( + np.asarray(variables["output"]["products"]["z"]) - variables["settings"].z0 + ) + epsilon = 1 * si.m + assert 0 <= min(z_minus_z0) < max(z_minus_z0) < 600 * si.m + epsilon + + @staticmethod + def test_cloud_base_height(variables): + """|------------------> integration + 0 z0 CB + ---|----------|------------|-------------> z + ..............********** + subsaturation supersaturation + note: in the paper, the CB is defined as altitude where + maximal supersaturation is attained + """ + cloud_base_index = find_cloud_base_index(variables["output"]["products"]) + + z0 = variables["settings"].z0 + assert ( + 290 * si.m + < variables["output"]["products"]["z"][cloud_base_index] - z0 + < 300 * si.m + ) + + @staticmethod + def test_supersaturation_maximum(variables): + supersaturation = np.asarray(variables["output"]["products"]["S_max"]) - 1 + assert signal.argrelextrema(supersaturation, np.greater)[0].shape[0] == 1 + assert 0.35 * PER_CENT < np.nanmax(supersaturation) < 0.5 * PER_CENT + + @staticmethod + @pytest.mark.parametrize( + "drop_id", + range( + Jensen_and_Nugent_2017.simulation.N_SD_NON_GCCN // 4, + Jensen_and_Nugent_2017.simulation.N_SD_NON_GCCN, + ), + ) + def test_radii(variables, drop_id): + """checks that 75% of the largest aerosol activate and shrink upon descent""" + # arrange + cb_idx = find_cloud_base_index(variables["output"]["products"]) + ma_idx = find_max_alt_index(variables["output"]["products"]) + + radii = variables["output"]["attributes"]["radius"][drop_id] + r1 = radii[0] + r2 = radii[cb_idx] + r3 = radii[ma_idx] + r4 = radii[-1] + + assert r1 < r2 < r3 + assert r3 > r4 + + @staticmethod + def test_maximal_size_of_largest_droplet(variables): + np.testing.assert_approx_equal( + max(variables["output"]["attributes"]["radius"][-1]), + 12 * si.um, + significant=2, + ) + + @staticmethod + @pytest.mark.parametrize( + "mask_label, height, mr_sw_rd", + ( + ("ascent", 50, (5.28, 0.44, 0.083)), + ("ascent", 100, (6.75, 0.38, 0.057)), + ("ascent", 150, (7.75, 0.36, 0.046)), + ("ascent", 200, (8.54, 0.34, 0.039)), + ("ascent", 250, (9.20, 0.32, 0.035)), + ("ascent", 300, (9.77, 0.31, 0.032)), + ("descent", 300, (9.77, 0.31, 0.032)), + ("descent", 250, (9.23, 0.33, 0.036)), + ("descent", 200, (8.54, 0.36, 0.042)), + ("descent", 150, (7.80, 0.39, 0.051)), + ("descent", 100, (6.82, 0.45, 0.066)), + ("descent", 50, (5.43, 0.57, 0.105)), + ), + ) + def test_table_4_upper_rows(variables, mask_label, height, mr_sw_rd): + # arrange + tolerance = 0.075 + mean_radius, spectral_width, relative_dispersion = mr_sw_rd + + # act + actual = variables["table_values"] + + # assert + for row in actual[mask_label]: + if int(row[0]) == height: + np.testing.assert_allclose( + actual=float(row[1]), desired=mean_radius, rtol=tolerance + ) + np.testing.assert_allclose( + actual=float(row[2]), desired=spectral_width, rtol=tolerance + ) + np.testing.assert_allclose( + actual=float(row[3]), desired=relative_dispersion, rtol=tolerance + ) diff --git a/PySDM/source/tests/smoke_tests/parcel_d/jensen_and_nugent_2017/test_fig_4_and_7_and_tab_4_bottom_rows.py b/PySDM/source/tests/smoke_tests/parcel_d/jensen_and_nugent_2017/test_fig_4_and_7_and_tab_4_bottom_rows.py new file mode 100644 index 0000000000000000000000000000000000000000..3c71135ad2bd9c8f7af5ee43342d9de0e095fbfb --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_d/jensen_and_nugent_2017/test_fig_4_and_7_and_tab_4_bottom_rows.py @@ -0,0 +1,179 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +from pathlib import Path +import numpy as np +import pytest +from scipy import signal +from open_atmos_jupyter_utils import notebook_vars +from PySDM_examples import Jensen_and_Nugent_2017 +from PySDM.physics.constants import PER_CENT +from PySDM.physics import si +from .test_fig_3_and_tab_4_upper_rows import find_cloud_base_index, find_max_alt_index + +PLOT = False +N_SD = Jensen_and_Nugent_2017.simulation.N_SD_NON_GCCN + np.count_nonzero( + Jensen_and_Nugent_2017.table_3.NA +) + + +@pytest.fixture(scope="session", name="variables") +def variables_fixture(): + return notebook_vars( + file=Path(Jensen_and_Nugent_2017.__file__).parent + / "Fig_4_and_7_and_Tab_4_bottom_rows.ipynb", + plot=PLOT, + ) + + +class TestFig4And7: + @staticmethod + def test_height_range(variables): + """note: in the plot the y-axis has cloud-base height subtracted, here not""" + z_minus_z0 = ( + np.asarray(variables["output"]["products"]["z"]) - variables["settings"].z0 + ) + epsilon = 1 * si.m + assert 0 <= min(z_minus_z0) < max(z_minus_z0) < 600 * si.m + epsilon + + @staticmethod + def test_cloud_base_height(variables): + cloud_base_index = find_cloud_base_index(variables["output"]["products"]) + z0 = variables["settings"].z0 + assert ( + 290 * si.m + < variables["output"]["products"]["z"][cloud_base_index] - z0 + < 300 * si.m + ) + + @staticmethod + def test_supersaturation_maximum(variables): + supersaturation = np.asarray(variables["output"]["products"]["S_max"]) - 1 + assert signal.argrelextrema(supersaturation, np.greater)[0].shape[0] == 1 + assert 0.4 * PER_CENT < np.nanmax(supersaturation) < 0.5 * PER_CENT + + class TestFig4: + + @staticmethod + @pytest.mark.parametrize( + "drop_id, activated, grow_on_descent", + ( + [(drop_id, False, False) for drop_id in range(0, int(0.15 * N_SD))] + + [ + (drop_id, True, False) + for drop_id in range(int(0.25 * N_SD), int(0.6 * N_SD)) + ] + + [(drop_id, True, True) for drop_id in range(int(0.777 * N_SD), N_SD)] + ), + ) + def test_grow_vs_evaporation_on_descent( + variables, drop_id, activated, grow_on_descent + ): + # arrange + cb_idx = find_cloud_base_index(variables["output"]["products"]) + ma_idx = find_max_alt_index(variables["output"]["products"]) + radii = variables["output"]["attributes"]["radius"][drop_id] + r1 = radii[0] + r2 = radii[cb_idx] + r3 = radii[ma_idx] + r4 = radii[-1] + + activated_actual = r1 < r2 < r3 + assert activated == activated_actual + + if grow_on_descent: + assert r3 < r4 + else: + assert r3 > r4 + + @staticmethod + def test_maximal_size_of_largest_droplet(variables): + np.testing.assert_approx_equal( + max(variables["output"]["attributes"]["radius"][-1]), + 57 * si.um, + significant=2, + ) + + @staticmethod + def test_initial_size_of_largest_droplet(variables): + np.testing.assert_approx_equal( + min(variables["output"]["attributes"]["radius"][-1]), + 19 * si.um, + significant=2, + ) + + class TestFig7: # pylint: disable=too-few-public-methods + @staticmethod + @pytest.mark.parametrize( + "mask_label, var, dry_radius_um, value_range", + ( + ("ascent", "SS_eq", 0.1, (0, 0.1 * PER_CENT)), + ("ascent", "SS_eq", 2, (-0.75 * PER_CENT, 0)), + ("ascent", "SS_eq", 9, (-2.01 * PER_CENT, -0.6 * PER_CENT)), + ("descent", "SS_eq", 0.1, (0, 0.1 * PER_CENT)), + ("descent", "SS_eq", 2, (-0.75 * PER_CENT, 0)), + ("descent", "SS_eq", 9, (-1 * PER_CENT, -0.25 * PER_CENT)), + ("ascent", "SS_ef", 0.1, (0, 0.5 * PER_CENT)), + ("ascent", "SS_ef", 2, (0, 1 * PER_CENT)), + ("ascent", "SS_ef", 9, (0.75 * PER_CENT, 2.5 * PER_CENT)), + ("descent", "SS_ef", 0.1, (-0.2 * PER_CENT, 0.1 * PER_CENT)), + ("descent", "SS_ef", 2, (-0.5 * PER_CENT, 0.25 * PER_CENT)), + ("descent", "SS_ef", 9, (0.3 * PER_CENT, 0.8 * PER_CENT)), + ), + ) + def test_equilibrium_supersaturation( + variables, *, mask_label, var, dry_radius_um, value_range + ): + mask = np.logical_and( + variables["masks"][mask_label], variables["height_above_cloud_base"] > 0 + ) + assert (variables[var][dry_radius_um][mask] > value_range[0]).all() + assert (variables[var][dry_radius_um][mask] < value_range[1]).all() + + class TestTable4bottom: # pylint: disable=too-few-public-methods + + @staticmethod + @pytest.mark.parametrize( + "mask_label, height, mr_sw_rd", + ( + ("ascent", 50, (5.26, 0.45, 0.093)), + ("ascent", 100, (6.73, 0.45, 0.066)), + ("ascent", 150, (7.73, 0.43, 0.055)), + ("ascent", 200, (8.52, 0.41, 0.048)), + ("ascent", 250, (9.19, 0.41, 0.044)), + ("ascent", 300, (9.77, 0.40, 0.041)), + ("descent", 300, (9.77, 0.40, 0.041)), + ("descent", 250, (9.21, 0.43, 0.047)), + ("descent", 200, (8.56, 0.46, 0.054)), + ("descent", 150, (7.78, 0.51, 0.065)), + ("descent", 100, (6.79, 0.57, 0.084)), + ("descent", 50, (5.38, 0.69, 0.129)), + ), + ) + def test_table_4_bottom_rows( + variables, + mask_label, + height, + mr_sw_rd, + ): + # arrange + tolerance = 0.2 + mean_radius, spectral_width, relative_dispersion = mr_sw_rd + + # act + actual = variables["table_values"] + + # assert + for row in actual[mask_label]: + if int(row[0]) == height: + np.testing.assert_allclose( + actual=float(row[1]), desired=mean_radius, rtol=tolerance + ) + np.testing.assert_allclose( + actual=float(row[2]), + desired=spectral_width, + rtol=tolerance, + ) + np.testing.assert_allclose( + actual=float(row[3]), + desired=relative_dispersion, + rtol=tolerance, + ) diff --git a/PySDM/source/tests/smoke_tests/parcel_d/jensen_and_nugent_2017/test_fig_5.py b/PySDM/source/tests/smoke_tests/parcel_d/jensen_and_nugent_2017/test_fig_5.py new file mode 100644 index 0000000000000000000000000000000000000000..1402d4bda111ff37b0ae6bfc42ee385e5d2a3a59 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_d/jensen_and_nugent_2017/test_fig_5.py @@ -0,0 +1,82 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring + +from pathlib import Path + +import numpy as np +import pytest +from scipy import signal + +from open_atmos_jupyter_utils import notebook_vars +from PySDM_examples import Jensen_and_Nugent_2017 +from PySDM.physics.constants import PER_CENT +from PySDM.physics import si +from .test_fig_4_and_7_and_tab_4_bottom_rows import ( + find_cloud_base_index, + find_max_alt_index, +) + +PLOT = False +N_SD = Jensen_and_Nugent_2017.simulation.N_SD_NON_GCCN + np.count_nonzero( + Jensen_and_Nugent_2017.table_3.NA +) + + +@pytest.fixture(scope="session", name="variables") +def variables_fixture(): + return notebook_vars( + file=Path(Jensen_and_Nugent_2017.__file__).parent / "Fig_5.ipynb", plot=PLOT + ) + + +class TestFig5: + @staticmethod + def test_height_range(variables): + """note: in the plot the y-axis has cloud-base height subtracted, here not""" + z_minus_z0 = ( + np.asarray(variables["output"]["products"]["z"]) - variables["settings"].z0 + ) + epsilon = 1 * si.m + assert 0 <= min(z_minus_z0) < max(z_minus_z0) < 600 * si.m + epsilon + + @staticmethod + def test_cloud_base_height(variables): + cloud_base_index = find_cloud_base_index(variables["output"]["products"]) + + z0 = variables["settings"].z0 + assert ( + 290 * si.m + < variables["output"]["products"]["z"][cloud_base_index] - z0 + < 300 * si.m + ) + + @staticmethod + @pytest.mark.xfail(strict=True, reason="TODO #1266") + def test_saturation_maximum(variables): + saturation = np.asarray(variables["output"]["products"]["S_max"]) + assert signal.argrelextrema(saturation, np.greater)[0].shape[0] == 1 + assert 1.2 * PER_CENT < np.nanmax(saturation - 1) < 1.4 * PER_CENT + + @staticmethod + @pytest.mark.parametrize("drop_id", range(int(0.8 * N_SD), N_SD)) + def test_radii(variables, drop_id): + """checks that the largest aerosol activate and still grow upon descent""" + # arrange + cb_idx = find_cloud_base_index(variables["output"]["products"]) + ma_idx = find_max_alt_index(variables["output"]["products"]) + + radii = variables["output"]["attributes"]["radius"][drop_id] + r1 = radii[0] + r2 = radii[cb_idx] + r3 = radii[ma_idx] + r4 = radii[-1] + + assert r1 < r2 < r3 + assert r3 < r4 + + @staticmethod + def test_maximal_size_of_largest_droplet(variables): + np.testing.assert_approx_equal( + max(variables["output"]["attributes"]["radius"][-1]), + 56 * si.um, + significant=2, + ) diff --git a/PySDM/source/tests/smoke_tests/parcel_d/jensen_and_nugent_2017/test_fig_6.py b/PySDM/source/tests/smoke_tests/parcel_d/jensen_and_nugent_2017/test_fig_6.py new file mode 100644 index 0000000000000000000000000000000000000000..a67c4e9912f08cfe272f3c4bea2bfe9edb3dd5c7 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_d/jensen_and_nugent_2017/test_fig_6.py @@ -0,0 +1,80 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring + +from pathlib import Path + +import numpy as np +import pytest +from scipy import signal + +from open_atmos_jupyter_utils import notebook_vars +from PySDM_examples import Jensen_and_Nugent_2017 +from PySDM.physics.constants import PER_CENT +from PySDM.physics import si +from .test_fig_4_and_7_and_tab_4_bottom_rows import ( + find_cloud_base_index, + find_max_alt_index, +) + +PLOT = False +N_SD = Jensen_and_Nugent_2017.simulation.N_SD_NON_GCCN + np.count_nonzero( + Jensen_and_Nugent_2017.table_3.NA +) + + +@pytest.fixture(scope="session", name="variables") +def variables_fixture(): + return notebook_vars( + file=Path(Jensen_and_Nugent_2017.__file__).parent / "Fig_6.ipynb", plot=PLOT + ) + + +class TestFig6: + @staticmethod + def test_height_range(variables): + """note: in the plot the y-axis has cloud-base height subtracted, here not""" + z_minus_z0 = ( + np.asarray(variables["output"]["products"]["z"]) - variables["settings"].z0 + ) + epsilon = 1 * si.m + assert 0 <= min(z_minus_z0) < max(z_minus_z0) < 1850 * si.m + epsilon + + @staticmethod + def test_cloud_base_height(variables): + cloud_base_index = find_cloud_base_index(variables["output"]["products"]) + + z0 = variables["settings"].z0 + assert ( + 290 * si.m + < variables["output"]["products"]["z"][cloud_base_index] - z0 + < 310 * si.m + ) + + @staticmethod + def test_supersaturation_maximum(variables): + supersaturation = np.asarray(variables["output"]["products"]["S_max"]) - 1 + extrema = signal.argrelextrema(supersaturation, np.greater, order=12) + assert extrema[0].shape[0] == 1 + assert 1.2 * PER_CENT < np.nanmax(supersaturation) < 1.3 * PER_CENT + + @staticmethod + @pytest.mark.parametrize("drop_id", range(int(0.77 * N_SD), N_SD)) + def test_radii(variables, drop_id): + """checks that the largest aerosol activate and still grow upon descent""" + # arrange + cb_idx = find_cloud_base_index(variables["output"]["products"]) + ma_idx = find_max_alt_index(variables["output"]["products"]) + + radii = variables["output"]["attributes"]["radius"][drop_id] + r1 = radii[0] + r2 = radii[cb_idx] + r3 = radii[ma_idx] + + assert r1 < r2 < r3 + + @staticmethod + def test_maximal_size_of_largest_droplet(variables): + np.testing.assert_approx_equal( + max(variables["output"]["attributes"]["radius"][-1]), + 50 * si.um, + significant=2, + ) diff --git a/PySDM/source/tests/smoke_tests/parcel_d/jensen_and_nugent_2017/test_table_3.py b/PySDM/source/tests/smoke_tests/parcel_d/jensen_and_nugent_2017/test_table_3.py new file mode 100644 index 0000000000000000000000000000000000000000..2e7bda62ae346e3df9197388c77622257f10718e --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_d/jensen_and_nugent_2017/test_table_3.py @@ -0,0 +1,29 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring + +from PySDM_examples.Jensen_and_Nugent_2017.table_3 import RD, NA +import numpy as np +from PySDM.physics import si +from PySDM import Formulae + + +TRIVIA = Formulae().trivia + +# from Wikipedia +RHO_S = 2.17 * si.g / si.cm**3 + + +class TestTable3: + @staticmethod + def test_number_integral(): + np.testing.assert_approx_equal( + actual=np.sum(NA), desired=281700 / si.m**3, significant=4 + ) + + @staticmethod + def test_mass_integral(): + total_mass_concentration = np.dot(TRIVIA.volume(radius=RD), NA) * RHO_S + np.testing.assert_approx_equal( + actual=total_mass_concentration, + desired=7.3 * si.ug / si.m**3, + significant=2, + ) diff --git a/PySDM/source/tests/smoke_tests/parcel_d/kreidenweis_et_al_2003/__init__.py b/PySDM/source/tests/smoke_tests/parcel_d/kreidenweis_et_al_2003/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/parcel_d/kreidenweis_et_al_2003/test_fig_1.py b/PySDM/source/tests/smoke_tests/parcel_d/kreidenweis_et_al_2003/test_fig_1.py new file mode 100644 index 0000000000000000000000000000000000000000..78522c9692e4e960f14218efac36930685241824 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_d/kreidenweis_et_al_2003/test_fig_1.py @@ -0,0 +1,93 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest +from matplotlib import pyplot +from PySDM_examples.Kreidenweis_et_al_2003 import Settings, Simulation + +from PySDM.dynamics.impl.chemistry_utils import GASEOUS_COMPOUNDS +from PySDM.physics import si + + +@pytest.fixture(scope="session", name="example_output") +def example_output_fixture(): + settings = Settings(n_sd=16, dt=1 * si.s, n_substep=5) + simulation = Simulation(settings) + output = simulation.run() + return output + + +Z_CB = 196 * si.m + + +class TestFig1: + @staticmethod + def test_a(example_output, plot=False): + # Plot + if plot: + name = "liquid water mixing ratio" + pyplot.plot( + example_output[name], np.asarray(example_output["t"]) - Z_CB * si.s + ) + pyplot.ylabel("time above cloud base [s]") + pyplot.grid() + pyplot.show() + + # Assert + assert (np.diff(example_output["liquid water mixing ratio"]) >= 0).all() + + @staticmethod + def test_b(example_output, plot=False): + # Plot + if plot: + for key in GASEOUS_COMPOUNDS: + pyplot.plot( + np.asarray(example_output[f"aq_{key}_ppb"]), + np.asarray(example_output["t"]) - Z_CB * si.s, + label="aq", + ) + pyplot.plot( + np.asarray(example_output[f"gas_{key}_ppb"]), + np.asarray(example_output["t"]) - Z_CB * si.s, + label="gas", + ) + pyplot.plot( + ( + np.asarray(example_output[f"aq_{key}_ppb"]) + + np.asarray(example_output[f"gas_{key}_ppb"]) + ), + np.asarray(example_output["t"]) - Z_CB * si.s, + label="sum", + ) + pyplot.legend() + pyplot.xlabel(key + " [ppb]") + pyplot.xscale("log") + pyplot.show() + + pyplot.plot( + np.asarray(example_output["aq_S_VI_ppb"]), + np.asarray(example_output["t"]) - Z_CB * si.s, + label="S_VI", + ) + pyplot.xlabel("S_VI (aq) [ppb]") + pyplot.show() + + # Assert + assert ( + 0.03 + < example_output["aq_S_IV_ppb"][-1] + example_output["gas_S_IV_ppb"][-1] + < 0.05 + ) + + @staticmethod + def test_c(example_output, plot=False): + if plot: + pyplot.plot( + example_output["pH"], np.asarray(example_output["t"]) - Z_CB * si.s + ) + pyplot.xlabel("pH") + pyplot.show() + + assert 4.9 < example_output["pH_pH_number_weighted"][-1] < 5 + assert 4.9 < example_output["pH_pH_volume_weighted"][-1] < 5 + assert 4.7 < example_output["pH_conc_H_number_weighted"][-1] < 4.9 + assert 4.7 < example_output["pH_conc_H_volume_weighted"][-1] < 4.9 diff --git a/PySDM/source/tests/smoke_tests/parcel_d/kreidenweis_et_al_2003/test_ionic_strength.py b/PySDM/source/tests/smoke_tests/parcel_d/kreidenweis_et_al_2003/test_ionic_strength.py new file mode 100644 index 0000000000000000000000000000000000000000..d3256a25cdc3ba95864d36fd73ebe0c931c7a213 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_d/kreidenweis_et_al_2003/test_ionic_strength.py @@ -0,0 +1,104 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest +from chempy.electrolytes import ionic_strength +from PySDM_examples.Kreidenweis_et_al_2003 import Settings, Simulation + +from PySDM import Formulae +from PySDM.backends.impl_numba.methods.chemistry_methods import ( + _K, + _conc, + calc_ionic_strength, +) +from PySDM.dynamics.impl.chemistry_utils import EquilibriumConsts +from PySDM.physics.constants import K_H2O + + +@pytest.mark.parametrize("nt", (0, 1, 2, 3)) +@pytest.mark.parametrize("n_sd", (10, 20)) +def test_calc_ionic_strength(nt, n_sd): + # pylint: disable=too-many-locals + formulae = Formulae() + const = formulae.constants + EQUILIBRIUM_CONST = EquilibriumConsts(formulae).EQUILIBRIUM_CONST + + K = _K( + NH3=EQUILIBRIUM_CONST["K_NH3"].at(const.ROOM_TEMP), + SO2=EQUILIBRIUM_CONST["K_SO2"].at(const.ROOM_TEMP), + HSO3=EQUILIBRIUM_CONST["K_HSO3"].at(const.ROOM_TEMP), + HSO4=EQUILIBRIUM_CONST["K_HSO4"].at(const.ROOM_TEMP), + HCO3=EQUILIBRIUM_CONST["K_HCO3"].at(const.ROOM_TEMP), + CO2=EQUILIBRIUM_CONST["K_CO2"].at(const.ROOM_TEMP), + HNO3=EQUILIBRIUM_CONST["K_HNO3"].at(const.ROOM_TEMP), + ) + + settings = Settings(dt=1, n_sd=n_sd, n_substep=5) + settings.t_max = nt * settings.dt + simulation = Simulation(settings) + + simulation.run() + + H = simulation.particulator.attributes["conc_N_mIII"].data + conc = { + "H+": H, + "N-3": simulation.particulator.attributes["conc_N_mIII"].data, + "N+5": simulation.particulator.attributes["conc_N_V"].data, + "S+4": simulation.particulator.attributes["conc_S_IV"].data, + "S+6": simulation.particulator.attributes["conc_S_VI"].data, + "C+4": simulation.particulator.attributes["conc_C_IV"].data, + } + + alpha_C = 1 + K.CO2 / conc["H+"] + K.CO2 * K.HCO3 / conc["H+"] ** 2 + alpha_S = 1 + K.SO2 / conc["H+"] + K.SO2 * K.HSO3 / conc["H+"] ** 2 + alpha_N3 = 1 + conc["H+"] * K.NH3 / K_H2O + alpha_N5 = 1 + K.HNO3 / conc["H+"] + + actual = calc_ionic_strength( + H=conc["H+"], + conc=_conc( + N_mIII=conc["N-3"], + N_V=conc["N+5"], + C_IV=conc["C+4"], + S_IV=conc["S+4"], + S_VI=conc["S+6"], + ), + K=K, + ) + + expected = ( + ionic_strength( + { + "H+": conc["H+"] / const.rho_w, + "HCO3-": K.CO2 / conc["H+"] * conc["C+4"] / alpha_C / const.rho_w, + "CO3-2": K.CO2 + / conc["H+"] + * K.HCO3 + / conc["H+"] + * conc["C+4"] + / alpha_C + / const.rho_w, + "HSO3-": K.SO2 / conc["H+"] * conc["S+4"] / alpha_S / const.rho_w, + "SO3-2": K.SO2 + / conc["H+"] + * K.HSO3 + / conc["H+"] + * conc["S+4"] + / alpha_S + / const.rho_w, + "NH4+": K.NH3 + / K_H2O + * conc["H+"] + * conc["N-3"] + / alpha_N3 + / const.rho_w, + "NO3-": K.HNO3 / conc["H+"] * conc["N+5"] / alpha_N5 / const.rho_w, + "HSO4-": conc["H+"] * conc["S+6"] / (conc["H+"] + K.HSO4) / const.rho_w, + "SO4-2": K.HSO4 * conc["S+6"] / (conc["H+"] + K.HSO4) / const.rho_w, + "OH-": K_H2O / conc["H+"] / const.rho_w, + }, + warn=False, + ) + * const.rho_w + ) + + np.testing.assert_allclose(actual, expected, rtol=1e-15) diff --git a/PySDM/source/tests/smoke_tests/parcel_d/kreidenweis_et_al_2003/test_spectrum_at_t_0.py b/PySDM/source/tests/smoke_tests/parcel_d/kreidenweis_et_al_2003/test_spectrum_at_t_0.py new file mode 100644 index 0000000000000000000000000000000000000000..917a957f56e61ef48286fee5b0262640bab68e76 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_d/kreidenweis_et_al_2003/test_spectrum_at_t_0.py @@ -0,0 +1,63 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest +from matplotlib import pyplot +from PySDM_examples.Kreidenweis_et_al_2003 import Settings, Simulation +from scipy.signal import find_peaks + +from PySDM.initialisation.sampling.spectral_sampling import ( + ConstantMultiplicity, + Logarithmic, + Linear, +) +from PySDM.physics import si + + +@pytest.mark.parametrize( + "spectral_sampling, method", + [ + pytest.param( + ConstantMultiplicity, + "sample_deterministic", + marks=pytest.mark.xfail(strict=True), + ), + (Logarithmic, "sample_deterministic"), + pytest.param( + Linear, "sample_pseudorandom", marks=pytest.mark.xfail(strict=True) + ), + ], +) +def test_spectrum_at_t_0(spectral_sampling, method, plot=False): + # Arrange + settings = Settings( + n_sd=64, + dt=1 * si.s, + n_substep=1, + spectral_sampling_class=spectral_sampling, + spectral_sampling_method=method, + ) + settings.t_max = 0 + simulation = Simulation(settings) + + # Act + output = simulation.run() + + # Plot + if plot: + pyplot.step( + 2e6 * settings.dry_radius_bins_edges[:-1], + output["dm_S_VI/dlog_10(dry diameter)"][-1], + ) + pyplot.ylabel("dS(VI)/dlog_10(D)") + pyplot.xlabel("dry diameter [µm]") + pyplot.xscale("log") + pyplot.yscale("log") + pyplot.ylim([0.01, 12]) + pyplot.show() + + # Assert + key = "S_VI" + spectrum = output[f"dm_{key}/dlog_10(dry diameter)"][0] + peaks, _ = find_peaks(spectrum) + assert len(peaks) == 1 + assert 3 < np.amax(spectrum) < 5 diff --git a/PySDM/source/tests/smoke_tests/parcel_d/kreidenweis_et_al_2003/test_table_3.py b/PySDM/source/tests/smoke_tests/parcel_d/kreidenweis_et_al_2003/test_table_3.py new file mode 100644 index 0000000000000000000000000000000000000000..266cc0d807d21ec627ff0126453fa71f922ec12a --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_d/kreidenweis_et_al_2003/test_table_3.py @@ -0,0 +1,116 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +from chempy import Substance +from PySDM_examples.Kreidenweis_et_al_2003 import Settings, Simulation + +from PySDM.dynamics.impl.chemistry_utils import AQUEOUS_COMPOUNDS, SpecificGravities +from PySDM.physics import si +from PySDM.physics.constants import PPB, convert_to + + +class TestTable3: + @staticmethod + def test_at_t_0(): + # Arrange + settings = Settings(n_sd=100, dt=1 * si.s, n_substep=5) + settings.t_max = 0 + simulation = Simulation(settings) + specific_gravities = SpecificGravities( + simulation.particulator.formulae.constants + ) + + # Act + output = simulation.run() + + # Assert + np.testing.assert_allclose(output["RH"][0], 95) + np.testing.assert_allclose(output["gas_S_IV_ppb"][0], 0.2) + np.testing.assert_allclose(output["gas_N_mIII_ppb"][0], 0.1) + np.testing.assert_allclose(output["gas_H2O2_ppb"], 0.5) + np.testing.assert_allclose(output["gas_N_V_ppb"], 0.1) + np.testing.assert_allclose(output["gas_O3_ppb"], 50) + np.testing.assert_allclose(output["gas_C_IV_ppb"], 360 * 1000) + + rtol = 0.15 + + mass_conc_SO4mm = 2 + mass_conc_NH4p = 0.375 + num_conc_SO4mm = ( + mass_conc_SO4mm / Substance.from_formula("SO4").mass * si.gram / si.mole + ) + num_conc_NH4p = ( + mass_conc_NH4p / Substance.from_formula("NH4").mass * si.gram / si.mole + ) + np.testing.assert_allclose(num_conc_NH4p, num_conc_SO4mm, rtol=0.005) + mass_conc_H = ( + num_conc_NH4p * Substance.from_formula("H").mass * si.gram / si.mole + ) + np.testing.assert_allclose( + actual=np.asarray(output["q_dry"]) * np.asarray(output["rhod"]), + desired=mass_conc_NH4p + mass_conc_SO4mm + mass_conc_H, + rtol=rtol, + ) + + expected = {k: 0 for k in AQUEOUS_COMPOUNDS} + expected["S_VI"] = mass_conc_SO4mm * si.ug / si.m**3 + expected["N_mIII"] = mass_conc_NH4p * si.ug / si.m**3 + + for key in expected: + mole_fraction = np.asarray(output[f"aq_{key}_ppb"]) + convert_to(mole_fraction, 1 / PPB) + compound = AQUEOUS_COMPOUNDS[key][0] # sic! + np.testing.assert_allclose( + actual=( + settings.formulae.trivia.mole_fraction_2_mixing_ratio( + mole_fraction, specific_gravity=specific_gravities[compound] + ) + * np.asarray(output["rhod"]) + ), + desired=expected[key], + rtol=rtol, + ) + + @staticmethod + def test_at_cloud_base(): + # Arrange + settings = Settings(n_sd=50, dt=1 * si.s, n_substep=5) + settings.t_max = 196 * si.s + settings.output_interval = settings.dt + simulation = Simulation(settings) + + # Act + output = simulation.run() + + # Assert + assert round(output["z"][-1]) == (698 - 600) * si.m + np.testing.assert_allclose(output["p"][-1], 939 * si.mbar, rtol=0.005) + np.testing.assert_allclose(output["T"][-1], 284.2 * si.K, rtol=0.005) + np.testing.assert_allclose( + settings.formulae.state_variable_triplet.rho_of_rhod_and_water_vapour_mixing_ratio( + rhod=output["rhod"][-1], + water_vapour_mixing_ratio=output["water vapour mixing ratio"][-1] + * si.g + / si.kg, + ), + 1.15 * si.kg / si.m**3, + rtol=0.005, + ) + assert output["liquid water mixing ratio"][-2] < 0.00055 + assert output["liquid water mixing ratio"][-1] > 0.0004 + assert output["RH"][-1] > 100 + assert output["RH"][-8] < 100 + + @staticmethod + def test_at_1200m_above_cloud_base(): + # Arrange + settings = Settings(n_sd=10, dt=1 * si.s, n_substep=5) + simulation = Simulation(settings) + + # Act + output = simulation.run() + + # Assert + np.testing.assert_allclose(output["z"][-1], (1.2 + 0.1) * si.km, rtol=0.005) + np.testing.assert_allclose( + output["liquid water mixing ratio"][-1], 2.17, rtol=0.02 + ) diff --git a/PySDM/source/tests/smoke_tests/parcel_d/niedermeier_et_al_2013/__init__.py b/PySDM/source/tests/smoke_tests/parcel_d/niedermeier_et_al_2013/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/parcel_d/niedermeier_et_al_2013/test_temperature_profile.py b/PySDM/source/tests/smoke_tests/parcel_d/niedermeier_et_al_2013/test_temperature_profile.py new file mode 100644 index 0000000000000000000000000000000000000000..68c6aed25aed2c0c8ee010520be884c030073578 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_d/niedermeier_et_al_2013/test_temperature_profile.py @@ -0,0 +1,33 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import pytest +from matplotlib import pyplot +from PySDM_examples.Niedermeier_et_al_2014 import Settings, Simulation + +from PySDM import Formulae +from PySDM.physics import si + + +@pytest.mark.parametrize("initial_temperature", (280 * si.K, 270 * si.K)) +def test_temperature_profile(initial_temperature, plot=False): + # arrange + formulae = Formulae( + particle_shape_and_density="MixedPhaseSpheres", + heterogeneous_ice_nucleation_rate="ABIFM", + constants={"ABIFM_M": 54.48, "ABIFM_C": -10.67}, + ) + settings = Settings( + initial_temperature=initial_temperature, timestep=10 * si.s, formulae=formulae + ) + simulation = Simulation(settings) + + # act + output = simulation.run() + + # plot + pyplot.plot(output["T"], output["z"]) + if plot: + pyplot.show() + + # assert + assert abs(output["T"][0] - initial_temperature) < 1e-10 * si.K + assert output["T"][-1] < initial_temperature diff --git a/PySDM/source/tests/smoke_tests/parcel_d/rogers_1975/__init__.py b/PySDM/source/tests/smoke_tests/parcel_d/rogers_1975/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/parcel_d/rogers_1975/test_fig_1.py b/PySDM/source/tests/smoke_tests/parcel_d/rogers_1975/test_fig_1.py new file mode 100644 index 0000000000000000000000000000000000000000..a0fd66b6c869a2d76a955bc8897e6b605e79f28a --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_d/rogers_1975/test_fig_1.py @@ -0,0 +1,59 @@ +"""test values on the plot against paper""" + +from pathlib import Path + +import numpy as np +import pytest + +from open_atmos_jupyter_utils import notebook_vars +from PySDM_examples import Rogers_1975 +from PySDM.physics.constants import PER_CENT + + +@pytest.fixture(scope="session", name="variables") +def variables_fixture(): + return notebook_vars( + file=Path(Rogers_1975.__file__).parent / "fig_1.ipynb", plot=False + ) + + +class TestFig1: + @staticmethod + def test_fig1_saturation_peak_against_paper(variables): + # arrange + SS = variables["solution"].S - 1 + time = variables["tsteps"] + expected_peak_time = 7 + expected_peak_value = 0.97 * PER_CENT + + # act + peak_value = np.max(SS) + peak_time = time[np.argmax(SS)] + + # assert + np.testing.assert_allclose( + actual=peak_value.magnitude, desired=expected_peak_value, rtol=1e-3 + ) + np.testing.assert_allclose( + actual=peak_time.magnitude, desired=expected_peak_time, atol=0.5 + ) + + @staticmethod + def test_fig1_radius_scope(variables): + """ + Check if for first 2.5 seconds slope is smaller than after this time. + It represents slower droplets growth with small supersaturation. + """ + # arrange + radius = variables["solution"].r + time_less_than = np.sum(variables["tsteps"].to_base_units().magnitude <= 2.5) + dr_before = np.diff(radius[:time_less_than]).to_base_units().magnitude + dr_after = np.diff(radius[time_less_than:]).to_base_units().magnitude + + # act + sut = np.mean(dr_before) / np.mean(dr_after) + + # assert + assert sut < 1 + assert sut > 0 + assert (dr_before > 0).all() diff --git a/PySDM/source/tests/smoke_tests/parcel_d/rozanski_and_sonntag_1982/__init__.py b/PySDM/source/tests/smoke_tests/parcel_d/rozanski_and_sonntag_1982/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/parcel_d/rozanski_and_sonntag_1982/test_figs_4_5_6.py b/PySDM/source/tests/smoke_tests/parcel_d/rozanski_and_sonntag_1982/test_figs_4_5_6.py new file mode 100644 index 0000000000000000000000000000000000000000..ec6e65540c6d1fe5eede569e4fcf65329cd65a71 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_d/rozanski_and_sonntag_1982/test_figs_4_5_6.py @@ -0,0 +1,70 @@ +"""tests ensuring values on plots match those in the paper""" + +from pathlib import Path +import platform + +import numpy as np +import pytest + +from open_atmos_jupyter_utils import notebook_vars +from PySDM_examples import Rozanski_and_Sonntag_1982 +from PySDM.physics import in_unit + +PLOT = False + + +@pytest.fixture(scope="session", name="variables") +def variables_fixture(): + return notebook_vars( + file=Path(Rozanski_and_Sonntag_1982.__file__).parent / "figs_4_5_6.ipynb", + plot=PLOT, + ) + + +@pytest.mark.xfail( + platform.system() == "Darwin" and platform.machine() == "x86_64", + strict=True, + reason="TODO #1207", +) +class TestFigs456: + @staticmethod + def test_fig_5_vapour_asymptote(variables): + delta_vapour_at_the_cloud_top_per_mille = in_unit( + variables["FORMULAE"].trivia.isotopic_ratio_2_delta( + ratio=variables["data"][variables["PLOT_KEY"]]["CT"]["Rv_2H"][1:], + reference_ratio=variables["CONST"].VSMOW_R_2H, + ), + variables["CONST"].PER_MILLE, + ) + + d_delta = np.diff(delta_vapour_at_the_cloud_top_per_mille) + dd_delta = np.diff(d_delta) + + mean = np.mean(delta_vapour_at_the_cloud_top_per_mille) + assert (d_delta < 0).all() + assert (dd_delta > 0).all() + assert (np.abs(d_delta / mean)[-10:] < 0.02).all() + np.testing.assert_approx_equal( + delta_vapour_at_the_cloud_top_per_mille[-1], -460, significant=2 + ) + + @staticmethod + def test_fig_5_rain_at_the_cloud_base(variables): + delta_rain_at_the_cloud_base_per_mille = in_unit( + variables["FORMULAE"].trivia.isotopic_ratio_2_delta( + ratio=variables["data"][variables["PLOT_KEY"]]["CB"]["Rr_2H"][1:], + reference_ratio=variables["CONST"].VSMOW_R_2H, + ), + variables["CONST"].PER_MILLE, + ) + + d_delta = np.diff(delta_rain_at_the_cloud_base_per_mille) + dd_delta = np.diff(d_delta) + + mean = np.mean(delta_rain_at_the_cloud_base_per_mille) + assert (d_delta[len(d_delta) // 4 :] < 0).all() + assert (dd_delta[len(dd_delta) // 4 :] > 0).all() + assert (np.abs(d_delta / mean)[-10:] < 0.02).all() + np.testing.assert_approx_equal( + delta_rain_at_the_cloud_base_per_mille[-1], -16, significant=2 + ) diff --git a/PySDM/source/tests/smoke_tests/parcel_d/seeding/__init__.py b/PySDM/source/tests/smoke_tests/parcel_d/seeding/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/parcel_d/seeding/test_hello_world.py b/PySDM/source/tests/smoke_tests/parcel_d/seeding/test_hello_world.py new file mode 100644 index 0000000000000000000000000000000000000000..09115a66e0c8f1efbfb76544095370425ba032ad --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_d/seeding/test_hello_world.py @@ -0,0 +1,52 @@ +"""tests ensuring values on plots match those in the paper""" + +from pathlib import Path + +import numpy as np +import pytest + +from open_atmos_jupyter_utils import notebook_vars +from PySDM_examples import seeding + +PLOT = False + + +@pytest.fixture(scope="session", name="variables") +def variables_fixture(): + return notebook_vars( + file=Path(seeding.__file__).parent / "hello_world.ipynb", + plot=PLOT, + ) + + +class TestHelloWorld: + @staticmethod + def test_sd_count(variables): + minimum = variables["n_sd_initial"] + maximum = minimum + variables["n_sd_seeding"] + assert variables["outputs"]["seeding"]["products"]["sd_count"][0] == minimum + assert ( + minimum + < variables["outputs"]["seeding"]["products"]["sd_count"][-1] + < maximum + ) + np.testing.assert_equal( + variables["outputs"]["no seeding"]["products"]["sd_count"], minimum + ) + + @staticmethod + def test_final_rain_water_mixing_ratio_larger_with_seeding(variables): + assert ( + variables["outputs"]["seeding"]["products"]["rain water mixing ratio"][-1] + > variables["outputs"]["no seeding"]["products"]["rain water mixing ratio"][ + -1 + ] + ) + + @staticmethod + def test_rain_water_earlier_with_seeding(variables): + assert np.count_nonzero( + variables["outputs"]["seeding"]["products"]["rain water mixing ratio"] + ) > np.count_nonzero( + variables["outputs"]["no seeding"]["products"]["rain water mixing ratio"] + ) diff --git a/PySDM/source/tests/smoke_tests/parcel_d/seeding/test_seeding_no_collisions.py b/PySDM/source/tests/smoke_tests/parcel_d/seeding/test_seeding_no_collisions.py new file mode 100644 index 0000000000000000000000000000000000000000..e6de1075caa55abe4c2d978a2d8fd7b978a4c91d --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_d/seeding/test_seeding_no_collisions.py @@ -0,0 +1,37 @@ +"""tests ensuring values on plots match those in the paper""" + +from pathlib import Path + +import numpy as np +import pytest + +from open_atmos_jupyter_utils import notebook_vars +from PySDM_examples import seeding + +PLOT = False + + +@pytest.fixture(scope="session", name="variables") +def variables_fixture(): + return notebook_vars( + file=Path(seeding.__file__).parent / "seeding_no_collisions.ipynb", + plot=PLOT, + ) + + +class TestSeedingNoCollisions: + @staticmethod + # seeding has smaller cloud drops than no seeding + def test_reff(variables): + np.testing.assert_array_less( + variables["outputs"]["seeding"]["products"]["r_eff"], + variables["outputs"]["no seeding"]["products"]["r_eff"] + 1e-6, + ) + + @staticmethod + # seeding has more cloud drops than no seeding + def test_n_drop(variables): + np.testing.assert_array_less( + variables["outputs"]["no seeding"]["products"]["n_drop"], + variables["outputs"]["seeding"]["products"]["n_drop"] + 1e-6, + ) diff --git a/PySDM/source/tests/smoke_tests/parcel_d/yang_et_al_2018/__init__.py b/PySDM/source/tests/smoke_tests/parcel_d/yang_et_al_2018/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/smoke_tests/parcel_d/yang_et_al_2018/test_displacement.py b/PySDM/source/tests/smoke_tests/parcel_d/yang_et_al_2018/test_displacement.py new file mode 100644 index 0000000000000000000000000000000000000000..4d1d59d08694e47027f12a165db53cb6d5af9569 --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_d/yang_et_al_2018/test_displacement.py @@ -0,0 +1,28 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import matplotlib.pyplot as plt +import numpy as np +from PySDM_examples.Yang_et_al_2018 import Settings, Simulation +from scipy import signal + + +def test_displacement(plot=False): + # Arrange + settings = Settings(n_sd=1) + simulation = Simulation(settings) + + # Act + output = simulation.run() + + # Plot + if plot: + plt.plot(output["t"], output["S"]) + plt.grid() + plt.show() + + # Assert + assert np.argmin(output["z"]) == 0 + assert output["z"][0] == settings.z0 + np.testing.assert_approx_equal(output["z"][-1], 1000) + np.testing.assert_approx_equal(np.amax(output["z"]), 1200) + assert signal.argrelextrema(np.array(output["z"]), np.greater)[0].shape[0] == 10 + assert signal.argrelextrema(np.array(output["z"]), np.less)[0].shape[0] == 10 diff --git a/PySDM/source/tests/smoke_tests/parcel_d/yang_et_al_2018/test_initialisation.py b/PySDM/source/tests/smoke_tests/parcel_d/yang_et_al_2018/test_initialisation.py new file mode 100644 index 0000000000000000000000000000000000000000..41ca8288154cc86f05b07f1302a525625bd48e0d --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_d/yang_et_al_2018/test_initialisation.py @@ -0,0 +1,83 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import matplotlib.pyplot as plt +import numpy as np +from PySDM_examples.Yang_et_al_2018 import Settings, Simulation + +from PySDM.physics.constants import si + + +def test_dry_spectrum_x(): + settings = Settings() + simulation = Simulation(settings) + dry_volume = simulation.particulator.attributes["dry volume"].to_ndarray() + dry_radii = simulation.formulae.trivia.radius(volume=dry_volume) / si.nanometre + + dry_radii = dry_radii[::-1] + assert round(dry_radii[1 - 1], 0) == 503 + assert round(dry_radii[10 - 1], 0) == 355 + assert round(dry_radii[50 - 1], 1) == 75.3 + assert round(dry_radii[100 - 1], 1) == 10.8 + + +def test_dry_spectrum_y(plot=False): + settings = Settings() + simulation = Simulation(settings) + dry_volume = simulation.particulator.attributes["dry volume"].to_ndarray() + dry_radii = simulation.formulae.trivia.radius(volume=dry_volume) / si.nanometre + nd = simulation.particulator.attributes["multiplicity"].to_ndarray() + + dr = dry_radii[1:] - dry_radii[0:-1] + env = simulation.particulator.environment + dn_dr = nd[0:-1] / env.mass_of_dry_air * env["rhod"] / dr + dn_dr /= 1 / si.centimetre**3 + + if plot: + plt.figure(figsize=(5, 5)) + plt.xscale("log") + plt.yscale("log") + plt.xlim(1e1, 1e3) + plt.ylim(1e-9, 1e3) + plt.yticks(10.0 ** np.arange(-8, 3, step=2)) + plt.plot(dry_radii[0:-1], dn_dr) + plt.show() + + # from fig. 1b + assert 1e-3 < dn_dr[0] < 1e-2 + assert 1e1 < max(dn_dr) < 1e2 + assert 0 < dn_dr[-1] < 1e-9 + + +def test_wet_vs_dry_spectrum(plot=False): + # Arrange + settings = Settings() + + # Act + simulation = Simulation(settings) + wet_volume = simulation.particulator.attributes["volume"].to_ndarray() + r_wet = simulation.formulae.trivia.radius(volume=wet_volume) / si.nanometre + n = simulation.particulator.attributes["multiplicity"].to_ndarray() + + dry_volume = simulation.particulator.attributes["dry volume"].to_ndarray() + dry_radii = simulation.formulae.trivia.radius(volume=dry_volume) / si.nanometre + + # Plot + if plot: + plt.plot(r_wet, n) + plt.plot(dry_radii, n) + plt.xscale("log") + plt.yscale("log") + plt.show() + + # Assert + assert (dry_radii < r_wet).all() + + +def test_relative_humidity(): + # Arrange + settings = Settings() + + # Act + simulation = Simulation(settings) + + # Assert + assert round(simulation.particulator.environment["RH"][0], 3) == 0.856 diff --git a/PySDM/source/tests/smoke_tests/parcel_d/yang_et_al_2018/test_just_do_it.py b/PySDM/source/tests/smoke_tests/parcel_d/yang_et_al_2018/test_just_do_it.py new file mode 100644 index 0000000000000000000000000000000000000000..70377dd2bb889e3f2e23c78b9ca976a4792cd31c --- /dev/null +++ b/PySDM/source/tests/smoke_tests/parcel_d/yang_et_al_2018/test_just_do_it.py @@ -0,0 +1,57 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest +from PySDM_examples.Yang_et_al_2018 import Settings, Simulation + +from PySDM.backends import CPU, GPU +from PySDM.backends.impl_numba.test_helpers import scipy_ode_condensation_solver +from PySDM.physics import si + +# TODO #527 + + +@pytest.mark.parametrize("scheme", ("default", "SciPy")) +@pytest.mark.parametrize("adaptive", (True, False)) +def test_just_do_it(scheme, adaptive, backend_class=CPU): + # Arrange + if scheme == "SciPy" and (not adaptive or backend_class is GPU): + pytest.skip() + + settings = Settings(dt_output=10 * si.second) + settings.adaptive = adaptive + if scheme == "SciPy": + settings.dt_max = settings.dt_output # TODO #334 + elif not adaptive: + settings.dt_max = 1 * si.second + + simulation = Simulation(settings, backend_class) + if scheme == "SciPy": + scipy_ode_condensation_solver.patch_particulator(simulation.particulator) + + # Act + output = simulation.run() + r = output["r"].T * si.metres + n = settings.n / (settings.mass_of_dry_air * si.kilogram) + + # Assert + condition = r > 1 * si.micrometre + NTOT = n_tot(n, condition) + N1 = NTOT[: int(1 / 3 * len(NTOT))] + N2 = NTOT[int(1 / 3 * len(NTOT)) : int(2 / 3 * len(NTOT))] + N3 = NTOT[int(2 / 3 * len(NTOT)) :] + + n_unit = 1 / si.microgram + assert min(N1) == 0.0 * n_unit + assert 0.6 * n_unit < max(N1) < 0.8 * n_unit + assert 0.17 * n_unit < min(N2) < 0.18 * n_unit + assert 0.35 * n_unit < max(N2) < 0.41 * n_unit + assert 0.1 * n_unit < min(N3) < 0.11 * n_unit + assert 0.27 * n_unit < max(N3) < 0.4 * n_unit + + # TODO #527 + if backend_class is not GPU: + assert max(output["ripening rate"]) > 0 + + +def n_tot(n, condition): + return np.dot(n, condition) diff --git a/PySDM/source/tests/tutorials_tests/__init__.py b/PySDM/source/tests/tutorials_tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/tutorials_tests/conftest.py b/PySDM/source/tests/tutorials_tests/conftest.py new file mode 100644 index 0000000000000000000000000000000000000000..f6c0d7a1cb08c808aa38201cae9a008d98a174a9 --- /dev/null +++ b/PySDM/source/tests/tutorials_tests/conftest.py @@ -0,0 +1,17 @@ +# pylint: disable=missing-module-docstring +import pathlib + +import pytest + +from ..examples_tests.conftest import findfiles + +PYSDM_TUTORIALS_ABS_PATH = ( + pathlib.Path(__file__).parent.parent.parent.absolute().joinpath("tutorials") +) + + +@pytest.fixture( + params=(path for path in findfiles(PYSDM_TUTORIALS_ABS_PATH, r".*\.(ipynb)$")), +) +def notebook_filename(request): + return request.param diff --git a/PySDM/source/tests/tutorials_tests/test_run_notebooks.py b/PySDM/source/tests/tutorials_tests/test_run_notebooks.py new file mode 100644 index 0000000000000000000000000000000000000000..026173cff53e045bd16a526530182416a5ed5174 --- /dev/null +++ b/PySDM/source/tests/tutorials_tests/test_run_notebooks.py @@ -0,0 +1,6 @@ +# pylint: disable=missing-module-docstring +from ..devops_tests.test_notebooks import test_run_notebooks as _impl + + +def test_run_notebooks(notebook_filename, tmp_path): + _impl(notebook_filename, tmp_path) diff --git a/PySDM/source/tests/unit_tests/__init__.py b/PySDM/source/tests/unit_tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/unit_tests/attributes/__init__.py b/PySDM/source/tests/unit_tests/attributes/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/unit_tests/attributes/test_acidity.py b/PySDM/source/tests/unit_tests/attributes/test_acidity.py new file mode 100644 index 0000000000000000000000000000000000000000..cbbdb88da02a026cdcb5e6f27ff824ac48804faa --- /dev/null +++ b/PySDM/source/tests/unit_tests/attributes/test_acidity.py @@ -0,0 +1,158 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +from collections import defaultdict + +import numpy as np +import pytest +from chempy import Equilibrium +from chempy.chemistry import Species +from chempy.equilibria import EqSystem + +from PySDM.backends.impl_numba.methods.chemistry_methods import ( + _K, + ChemistryMethods, + _conc, +) +from PySDM.dynamics import aqueous_chemistry +from PySDM.dynamics.impl.chemistry_utils import EquilibriumConsts, M +from PySDM.formulae import Formulae +from PySDM.physics.constants import K_H2O + +FORMULAE = Formulae() +EQUILIBRIUM_CONST = EquilibriumConsts(FORMULAE).EQUILIBRIUM_CONST + + +class TestAcidity: + @staticmethod + def test_equilibrate_pH_pure_water(): + # Arrange + eqs = {} + for key, const in EQUILIBRIUM_CONST.items(): + eqs[key] = np.full(1, const.at(FORMULAE.constants.ROOM_TEMP)) + + # Act + result = np.empty(1) + ChemistryMethods.equilibrate_H_body( + within_tolerance=FORMULAE.trivia.within_tolerance, + pH2H=FORMULAE.trivia.pH2H, + H2pH=FORMULAE.trivia.H2pH, + conc=_conc( + N_mIII=np.zeros(1), + N_V=np.zeros(1), + C_IV=np.zeros(1), + S_IV=np.zeros(1), + S_VI=np.zeros(1), + ), + K=_K( + HNO3=eqs["K_HNO3"].data, + HCO3=eqs["K_HCO3"].data, + HSO3=eqs["K_HSO3"].data, + HSO4=eqs["K_HSO4"].data, + CO2=eqs["K_CO2"].data, + NH3=eqs["K_NH3"].data, + SO2=eqs["K_SO2"].data, + ), + cell_id=np.zeros(1, dtype=int), + # output + do_chemistry_flag=np.empty(1), + pH=result, + # params + H_min=FORMULAE.trivia.pH2H(aqueous_chemistry.DEFAULTS.pH_max), + H_max=FORMULAE.trivia.pH2H(aqueous_chemistry.DEFAULTS.pH_min), + ionic_strength_threshold=aqueous_chemistry.DEFAULTS.ionic_strength_threshold, + rtol=aqueous_chemistry.DEFAULTS.pH_rtol, + ) + + # Assert + np.testing.assert_allclose(result, 7) + + @staticmethod + @pytest.mark.parametrize( + "init_conc", + ( + defaultdict( + float, + {"H2O": 1, "NH3": 5e-3, "H2CO3(aq)": 0.01e-3, "H2SO3(aq)": 0.005e-3}, + ), + defaultdict( + float, + {"H2O": 1, "NH3": 0.5e-3, "H2CO3(aq)": 0.1e-3, "H2SO3(aq)": 0.05e-3}, + ), + ), + ) + @pytest.mark.parametrize( + "env_T", + ( + FORMULAE.constants.ROOM_TEMP, + FORMULAE.constants.ROOM_TEMP - 30, + FORMULAE.constants.ROOM_TEMP + 30, + ), + ) + def test_equilibrate_pH_non_trivial(init_conc, env_T): + equilibria = { + "water": Equilibrium.from_string(f"H2O = H+ + OH-; {K_H2O / M / M}"), + "ammonia": Equilibrium.from_string( + f"NH3 + H2O = NH4+ + OH-; {EQUILIBRIUM_CONST['K_NH3'].at(env_T) / M}" + ), + "sulfonic_first": Equilibrium.from_string( + f"H2SO3(aq) = H+ + HSO3-; {EQUILIBRIUM_CONST['K_SO2'].at(env_T) / M}" + ), + "sulfonic_second": Equilibrium.from_string( + f"HSO3- = H+ + SO3-2; {EQUILIBRIUM_CONST['K_HSO3'].at(env_T) / M}" + ), + "carbonic_first": Equilibrium.from_string( + f"H2CO3(aq) = H+ + HCO3-; {EQUILIBRIUM_CONST['K_CO2'].at(env_T) / M}" + ), + "carbonic_second": Equilibrium.from_string( + f"HCO3- = H+ + CO3-2; {EQUILIBRIUM_CONST['K_HCO3'].at(env_T) / M}" + ), + } + substances = [ + Species.from_formula(f) + for f in "H2O OH- H+ NH3 NH4+ H2CO3(aq) HCO3- CO3-2 H2SO3(aq) HSO3- SO3-2".split() + ] + eqsys = EqSystem(equilibria.values(), substances) + + x, sol, sane = eqsys.root(init_conc) + assert sol["success"] and sane + + H_idx = 2 + assert substances[H_idx].name == "H+" + expected_pH = -np.log10(x[H_idx]) + + eqs = {} + for key, const in EQUILIBRIUM_CONST.items(): + eqs[key] = np.full(1, const.at(env_T)) + + actual_pH = np.empty(1) + ChemistryMethods.equilibrate_H_body( + within_tolerance=FORMULAE.trivia.within_tolerance, + pH2H=FORMULAE.trivia.pH2H, + H2pH=FORMULAE.trivia.H2pH, + conc=_conc( + N_mIII=np.full(1, init_conc["NH3"] * 1e3), + N_V=np.full(1, init_conc["HNO3(aq)"] * 1e3), + C_IV=np.full(1, init_conc["H2CO3(aq)"] * 1e3), + S_IV=np.full(1, init_conc["H2SO3(aq)"] * 1e3), + S_VI=np.full(1, init_conc["HSO4-"] * 1e3), + ), + K=_K( + HNO3=eqs["K_HNO3"].data, + HCO3=eqs["K_HCO3"].data, + HSO3=eqs["K_HSO3"].data, + HSO4=eqs["K_HSO4"].data, + CO2=eqs["K_CO2"].data, + NH3=eqs["K_NH3"].data, + SO2=eqs["K_SO2"].data, + ), + cell_id=np.zeros(1, dtype=int), + # output + do_chemistry_flag=np.empty(1), + pH=actual_pH, + # params + H_min=FORMULAE.trivia.pH2H(aqueous_chemistry.DEFAULTS.pH_max), + H_max=FORMULAE.trivia.pH2H(aqueous_chemistry.DEFAULTS.pH_min), + ionic_strength_threshold=aqueous_chemistry.DEFAULTS.ionic_strength_threshold, + rtol=aqueous_chemistry.DEFAULTS.pH_rtol, + ) + + np.testing.assert_allclose(actual_pH[0], expected_pH, rtol=1e-5) diff --git a/PySDM/source/tests/unit_tests/attributes/test_area_radius.py b/PySDM/source/tests/unit_tests/attributes/test_area_radius.py new file mode 100644 index 0000000000000000000000000000000000000000..0c6d4f329020f5cc92f7bdca160259df23067d37 --- /dev/null +++ b/PySDM/source/tests/unit_tests/attributes/test_area_radius.py @@ -0,0 +1,63 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest + +from PySDM import Builder +from PySDM.environments import Box + + +@pytest.mark.parametrize("volume", (np.asarray([44, 666]),)) +def test_radius(volume, backend_instance): + # arrange + env = Box(dt=None, dv=None) + builder = Builder(backend=backend_instance, n_sd=volume.size, environment=env) + builder.request_attribute("radius") + particulator = builder.build( + attributes={"volume": volume, "multiplicity": np.ones_like(volume)} + ) + + # act + radius_actual = particulator.attributes["radius"].to_ndarray() + + # assert + radius_expected = particulator.formulae.trivia.radius(volume=volume) + np.testing.assert_allclose(radius_actual, radius_expected) + + +@pytest.mark.parametrize("volume", (np.asarray([44, 666]),)) +def test_sqrt_radius(volume, backend_instance): + # arrange + env = Box(dt=None, dv=None) + builder = Builder(backend=backend_instance, n_sd=volume.size, environment=env) + builder.request_attribute("radius") + builder.request_attribute("square root of radius") + particulator = builder.build( + attributes={"volume": volume, "multiplicity": np.ones_like(volume)} + ) + + # act + radius_actual = particulator.attributes["radius"].to_ndarray() + sqrt_radius_actual = particulator.attributes["square root of radius"].to_ndarray() + + # assert + sqrt_radius_expected = np.sqrt(radius_actual) + np.testing.assert_allclose(sqrt_radius_actual, sqrt_radius_expected) + + +@pytest.mark.parametrize("volume", (np.asarray([44, 666]),)) +def test_area(volume, backend_instance): + # arrange + env = Box(dv=None, dt=None) + builder = Builder(backend=backend_instance, n_sd=volume.size, environment=env) + builder.request_attribute("area") + particulator = builder.build( + attributes={"volume": volume, "multiplicity": np.ones_like(volume)} + ) + + # act + area_actual = particulator.attributes["area"].to_ndarray() + + # assert + radius_expected = particulator.formulae.trivia.radius(volume=volume) + area_expected = particulator.formulae.trivia.area(radius=radius_expected) + np.testing.assert_allclose(area_actual, area_expected) diff --git a/PySDM/source/tests/unit_tests/attributes/test_critical_saturation.py b/PySDM/source/tests/unit_tests/attributes/test_critical_saturation.py new file mode 100644 index 0000000000000000000000000000000000000000..e83dbdb70a11f336913d8e94ecb98704ceb27460 --- /dev/null +++ b/PySDM/source/tests/unit_tests/attributes/test_critical_saturation.py @@ -0,0 +1,35 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np + +from PySDM import Builder +from PySDM.backends import CPU +from PySDM.environments import Box +from PySDM.physics import si +from PySDM.products.condensation import ActivableFraction + + +def test_critical_saturation(): + # arrange + T = 300 * si.K + n_sd = 100 + S_max = 1.000101 + vdry = np.linspace(0.001, 1, n_sd) * si.um**3 + + env = Box(dt=np.nan, dv=np.nan) + builder = Builder(n_sd=n_sd, backend=CPU(), environment=env) + particulator = builder.build( + attributes={ + "multiplicity": np.ones(n_sd), + "volume": np.linspace(0.01, 10, n_sd) * si.um**3, + "dry volume": vdry, + "kappa times dry volume": 0.9 * vdry, + }, + products=(ActivableFraction(),), + ) + particulator.environment["T"] = T + + # act + AF = particulator.products["activable fraction"].get(S_max=S_max) + + # assert + assert 0 < AF < 1 diff --git a/PySDM/source/tests/unit_tests/attributes/test_diffusional_growth_mass_change.py b/PySDM/source/tests/unit_tests/attributes/test_diffusional_growth_mass_change.py new file mode 100644 index 0000000000000000000000000000000000000000..c16843788e4079e4f4d1e69889eb5fd04ee0acc0 --- /dev/null +++ b/PySDM/source/tests/unit_tests/attributes/test_diffusional_growth_mass_change.py @@ -0,0 +1,86 @@ +"""test attribute diffusional growth mass change""" + +import numpy as np +import pytest + +from PySDM.attributes import DiffusionalGrowthMassChange +from PySDM.physics import si +from PySDM.dynamics import Collision +from ..dummy_particulator import DummyParticulator + + +class TestDiffusionalGrowthMassChange: + @staticmethod + def test_initialisation(backend_class): + if backend_class.__name__ != "Numba": + pytest.skip("only Numba supporter - TODO #1438") + + # arrange + particulator = DummyParticulator(backend_class) + particulator.request_attribute("diffusional growth mass change") + + # act + particulator.build( + attributes={"multiplicity": np.ones(1), "water mass": np.ones(1)} + ) + + # assert + for items in (particulator.initialisers, particulator.observers): + assert len(items) == 1 + assert isinstance(items[0], DiffusionalGrowthMassChange) + + @staticmethod + @pytest.mark.xfail( + reason="Not implemented for Collisions", + raises=AssertionError, + strict=True, + ) + def test_if_collision(backend_class): + # arrange + particulator = DummyParticulator(backend_class) + particulator.request_attribute("diffusional growth mass change") + + # act + particulator.add_dynamic( + Collision( + collision_kernel=np.nan, + breakup_efficiency=np.nan, + coalescence_efficiency=np.nan, + fragmentation_function=np.nan, + ) + ) + + # assert + particulator.build(attributes={}) + + @staticmethod + @pytest.mark.parametrize("steps", (0, 1, 2)) + def test_methods(backend_class, steps): + if backend_class.__name__ != "Numba": + pytest.skip("only Numba supporter - TODO #1438") + + # arrange + n_sd = 1 + mass_delta = np.ones(n_sd) * si.ng + + particulator = DummyParticulator(backend_class, n_sd=n_sd, formulae=None) + particulator.request_attribute("diffusional growth mass change") + particulator.build( + attributes={ + "multiplicity": np.ones(n_sd), + "water mass": np.ones(n_sd) * si.ug, + } + ) + + # act + particulator.run(steps=0) + for _ in range(steps): + particulator.attributes["signed water mass"].data[:] += mass_delta + particulator.run(steps=1) + + # assert + np.testing.assert_allclose( + desired=mass_delta if steps != 0 else np.zeros_like(mass_delta), + actual=particulator.attributes["diffusional growth mass change"].data, + rtol=1e-8, + ) diff --git a/PySDM/source/tests/unit_tests/attributes/test_fall_velocity.py b/PySDM/source/tests/unit_tests/attributes/test_fall_velocity.py new file mode 100644 index 0000000000000000000000000000000000000000..8719a094f9d05b421e9ac011958777fbb6c2d1aa --- /dev/null +++ b/PySDM/source/tests/unit_tests/attributes/test_fall_velocity.py @@ -0,0 +1,176 @@ +""" +Test `PySDM.attributes.physics.relative_fall_velocity.RelativeFallVelocity` +and `PySDM.attributes.physics.relative_fall_velocity.RelativeFallMomentum` attributes +""" + +import numpy as np +import pytest + +from PySDM.attributes.physics import RelativeFallVelocity, TerminalVelocity +from PySDM.builder import Builder +from PySDM.dynamics import Coalescence, RelaxedVelocity +from PySDM.dynamics.collisions.collision_kernels.constantK import ConstantK +from PySDM.environments.box import Box +from PySDM.physics import si + + +def generate_rand_attr_param(n_sd): + np.random.seed(12) + + return pytest.param( + { + "volume": (3 * np.random.random(n_sd) + 1) * si.mm**3, + "multiplicity": np.random.randint(100, 1000, n_sd), + "relative fall momentum": (5 * np.random.random(n_sd) + 1) * 1e-6, + }, + id=f"random(n_sd={n_sd})", + ) + + +@pytest.fixture( + name="default_attributes", + params=( + pytest.param( + { + "volume": np.array([si.mm**3, 2 * si.mm**3]), + "multiplicity": np.array([1, 1]), + "relative fall momentum": np.array([10e-6, 6e-6]), + }, + id="two_droplets", + ), + pytest.param( + { + "volume": np.array([si.mm**3, 2 * si.mm**3, 3 * si.mm**3]), + "multiplicity": np.array([2, 1, 4]), + "relative fall momentum": np.array([10e-6, 6e-6, 4e-6]), + }, + id="fixed(n_sd=3)", + ), + generate_rand_attr_param(n_sd=100), + ), +) +def default_attributes_fixture(request): + return request.param + + +class TestFallVelocity: + @staticmethod + def test_fall_velocity_calculation(default_attributes, backend_instance): + """ + Test that fall velocity is the momentum divided by the mass. + """ + env = Box(dt=1, dv=1) + builder = Builder( + n_sd=len(default_attributes["multiplicity"]), + backend=backend_instance, + environment=env, + ) + + # needed to use relative fall velocity instead of terminal + # velocity behind the scenes + builder.add_dynamic(RelaxedVelocity()) + + builder.request_attribute("relative fall velocity") + + particulator = builder.build(attributes=default_attributes, products=()) + + assert np.allclose( + particulator.attributes["relative fall velocity"].to_ndarray(), + particulator.attributes["relative fall momentum"].to_ndarray() + / (particulator.attributes["signed water mass"].to_ndarray()), + ) + + @staticmethod + def test_conservation_of_momentum(default_attributes, backend_instance): + """ + Test that conservation of momentum holds when many super-droplets coalesce + """ + env = Box(dt=1, dv=1) + builder = Builder( + n_sd=len(default_attributes["multiplicity"]), + backend=backend_instance, + environment=env, + ) + + # add and remove relaxed velocity to prevent warning + builder.add_dynamic(RelaxedVelocity()) + + builder.request_attribute("relative fall momentum") + + builder.add_dynamic( + Coalescence(collision_kernel=ConstantK(a=1), adaptive=False) + ) + + particulator = builder.build(attributes=default_attributes, products=()) + + particulator.dynamics.pop("RelaxedVelocity") + + particulator.run(2) + + total_initial_momentum = ( + default_attributes["relative fall momentum"] + * default_attributes["multiplicity"] + ).sum() + + total_final_momentum = ( + particulator.attributes["relative fall momentum"].to_ndarray() + * particulator.attributes["multiplicity"].to_ndarray() + ).sum() + + # assert that the total number of droplets changed + assert np.sum(particulator.attributes["multiplicity"].to_ndarray()) != np.sum( + default_attributes["multiplicity"] + ) + + # assert that the total momentum is conserved + assert np.isclose(total_final_momentum, total_initial_momentum) + + @staticmethod + def test_attribute_selection(backend_instance): + """ + Test that the correct velocity attribute is selected by the mapper. + `PySDM.attributes.physics.relative_fall_velocity.RelativeFallVelocity` + should only be selected when `PySDM.dynamics.RelaxedVelocity` dynamic exists. + """ + env = Box(dt=1, dv=1) + builder_no_relax = Builder(n_sd=1, backend=backend_instance, environment=env) + builder_no_relax.request_attribute("relative fall velocity") + _ = builder_no_relax.build( + attributes={"multiplicity": np.ones(1), "water mass": np.zeros(1)}, + products=(), + ) + + # with no RelaxedVelocity, the builder should use TerminalVelocity + assert isinstance( + builder_no_relax.req_attr["relative fall velocity"], TerminalVelocity + ) + env = Box(dt=1, dv=1) + builder = Builder(n_sd=1, backend=backend_instance, environment=env) + builder.add_dynamic(RelaxedVelocity()) + builder.request_attribute("relative fall velocity") + _ = builder.build( + attributes={ + "multiplicity": np.ones(1), + "signed water mass": np.zeros(1), + "relative fall momentum": np.zeros(1), + }, + products=(), + ) + + # with RelaxedVelocity, the builder should use RelativeFallVelocity + assert isinstance( + builder.req_attr["relative fall velocity"], RelativeFallVelocity + ) + + # requesting momentum with no dynamic issues a warning + env = Box(dt=1, dv=1) + builder = Builder(n_sd=1, backend=backend_instance, environment=env) + builder.request_attribute("relative fall momentum") + with pytest.warns(UserWarning): + _ = builder.build( + attributes={ + "multiplicity": np.ones(1), + "signed water mass": np.zeros(1), + }, + products=(), + ) diff --git a/PySDM/source/tests/unit_tests/attributes/test_impl_attribute_registry.py b/PySDM/source/tests/unit_tests/attributes/test_impl_attribute_registry.py new file mode 100644 index 0000000000000000000000000000000000000000..c28a0bc34156d605b3cd652f26c25137d6efcf43 --- /dev/null +++ b/PySDM/source/tests/unit_tests/attributes/test_impl_attribute_registry.py @@ -0,0 +1,53 @@ +"""checks the attribute registry logic mapping attribute names to classes implementing them +and handling different variants of these implementations picked depending on the choice of +dynamics and formulae options""" + +import pytest + +from PySDM.attributes import Multiplicity +from PySDM.attributes.impl import get_attribute_class, register_attribute + + +class TestAttributeRegistry: + """groups multiple tests to facilitate execution""" + + @staticmethod + def test_get_attribute_class_ok(): + """checks if inquiring for a valid attribute name yields a valid class""" + assert get_attribute_class("multiplicity") is Multiplicity + + @staticmethod + def test_get_attribute_class_fail(): + """checks if inquiring for an invalid attribute name raises an exception""" + with pytest.raises(ValueError): + get_attribute_class("lorem ipsum") + + @staticmethod + def test_get_attribute_class_variant_fail(): + """checks if variant logic properly throws an exception if no variant match found""" + + @register_attribute(name="umaminess", variant=lambda _, __: False) + class Umaminess: # pylint: disable=unused-variable,too-few-public-methods + """Dummy class""" + + with pytest.raises(AssertionError): + get_attribute_class("umaminess") + + @staticmethod + def test_get_attribute_class_error_message_hints(): + """check if error message thrown on unknown attribute error contains list of valid names""" + with pytest.raises(ValueError) as excinfo: + get_attribute_class("XXX") + assert "multiplicity" in str(excinfo.value) + + @staticmethod + def test_register_attribute_fail_on_repeated_name(): + """checks if the decorator raises an exception if name olready used""" + + @register_attribute() + class A: # pylint: disable=too-few-public-methods + """Dummy class""" + + with pytest.raises(ValueError) as exception_info: + register_attribute()(A) + assert exception_info.match("already exists") diff --git a/PySDM/source/tests/unit_tests/attributes/test_isotopes.py b/PySDM/source/tests/unit_tests/attributes/test_isotopes.py new file mode 100644 index 0000000000000000000000000000000000000000..2b3a2dcd569d5d14bf4ec4221028ecec5369f409 --- /dev/null +++ b/PySDM/source/tests/unit_tests/attributes/test_isotopes.py @@ -0,0 +1,143 @@ +""" +unit tests for isotope-related attributes +""" + +import numpy as np +import pytest + +from PySDM import Builder, Formulae +from PySDM.dynamics.isotopic_fractionation import HEAVY_ISOTOPES +from PySDM.environments import Box +from PySDM.physics import constants_defaults, si +from PySDM.physics.trivia import Trivia + + +def dummy_attrs(length): + return { + "signed water mass": np.asarray([0.666 * si.g] * length), + "multiplicity": np.asarray([-1] * length, dtype=int), + } + + +class TestIsotopes: + @staticmethod + @pytest.mark.parametrize("isotope", HEAVY_ISOTOPES) + def test_heavy_isotope_moles_attributes(backend_instance, isotope): + # arrange + values = [1, 2, 3] + builder = Builder( + n_sd=len(values), + backend=backend_instance, + environment=Box(dt=np.nan, dv=np.nan), + ) + particulator = builder.build( + attributes={ + f"moles_{isotope}": np.asarray(values), + **dummy_attrs(len(values)), + } + ) + + # act + attr_values = particulator.attributes[f"moles_{isotope}"].to_ndarray() + + # assert + np.testing.assert_array_equal(attr_values, values) + + @staticmethod + @pytest.mark.parametrize("isotope", HEAVY_ISOTOPES) + @pytest.mark.parametrize( + "heavier_water_specific_content", + ( + 0.00001, + 0.0001, + ), + ) + def test_delta_attribute(backend_class, isotope, heavier_water_specific_content): + # arrange + builder = Builder( + n_sd=1, + backend=backend_class(double_precision=True), + environment=Box(dt=np.nan, dv=np.nan), + ) + builder.request_attribute(f"delta_{isotope}") + + attributes = { + **dummy_attrs(1), + **{f"moles_{k}": np.zeros(1) for k in HEAVY_ISOTOPES if k != isotope}, + } + heavier_water_molar_mass = { + "2H": constants_defaults.M_16O + + constants_defaults.M_1H + + constants_defaults.M_2H, + "3H": constants_defaults.M_16O + + constants_defaults.M_1H + + constants_defaults.M_3H, + "17O": constants_defaults.M_17O + 2 * constants_defaults.M_1H, + "18O": constants_defaults.M_18O + 2 * constants_defaults.M_1H, + }[isotope] + attributes[f"moles_{isotope}"] = np.asarray( + [ + heavier_water_specific_content + * attributes["signed water mass"] + / heavier_water_molar_mass + ] + ) + particulator = builder.build(attributes=attributes) + + # act + (delta,) = particulator.attributes[f"delta_{isotope}"].to_ndarray() + + # assert + ((n_heavy_isotope,),) = attributes[f"moles_{isotope}"] + (n_light_water,) = ( + (1 - heavier_water_specific_content) + * attributes["signed water mass"] + / (constants_defaults.M_1H * 2 + constants_defaults.M_16O) + ) + print(delta, n_heavy_isotope, n_light_water) + if isotope[-1] == "O": + n_light_isotope = n_light_water + elif isotope[-1] == "H": + n_light_isotope = n_light_water * 2 + n_heavy_isotope + else: + raise NotImplementedError() + np.testing.assert_approx_equal( + actual=delta, + desired=Trivia.isotopic_ratio_2_delta( + reference_ratio=getattr(constants_defaults, f"VSMOW_R_{isotope}"), + ratio=n_heavy_isotope / n_light_isotope, + ), + significant=5, + ) + + @staticmethod + def test_moles( + backend_class, + m_t=1 * si.ng, + ): + # arrange + formulae = Formulae() + attributes = { + "multiplicity": np.asarray([0]), + "signed water mass": np.asarray([m_t]), + } + for isotope in HEAVY_ISOTOPES: + attributes[f"moles_{isotope}"] = np.asarray([44]) + + builder = Builder( + n_sd=1, + backend=backend_class(formulae=formulae), + environment=Box(dv=np.nan, dt=-1 * si.s), + ) + builder.request_attribute("moles light water") + builder.request_attribute("moles_16O") + particulator = builder.build(attributes=attributes, products=()) + + # assert + np.testing.assert_approx_equal( + particulator.attributes["moles light water"].data[0], + particulator.attributes["moles_16O"].data[0] + - particulator.attributes["moles_2H"].data[0] + - particulator.attributes["moles_3H"].data[0], + significant=5, + ) diff --git a/PySDM/source/tests/unit_tests/attributes/test_multiplicities.py b/PySDM/source/tests/unit_tests/attributes/test_multiplicities.py new file mode 100644 index 0000000000000000000000000000000000000000..2701c18e6acc581372d8b495c02816747c91dfee --- /dev/null +++ b/PySDM/source/tests/unit_tests/attributes/test_multiplicities.py @@ -0,0 +1,45 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest + +from PySDM import Builder +from PySDM.attributes.physics import Multiplicity +from PySDM.environments import Box + + +class TestMultiplicities: + @staticmethod + def test_max_multiplicity_value(): + actual_max_multiplicity = Multiplicity.MAX_VALUE + expected_max_multiplicity = np.iinfo( + np.int64 + ).max # TODO #324: switch to uint64? + + assert actual_max_multiplicity == expected_max_multiplicity + + @staticmethod + @pytest.mark.parametrize( + "value", + ( + Multiplicity.MAX_VALUE, + pytest.param( + Multiplicity.MAX_VALUE + 1, marks=pytest.mark.xfail(strict=True) + ), + ), + ) + def test_max_multiplicity_assignable(backend_instance, value): + # arrange + n_sd = 1 + env = Box(dt=np.nan, dv=np.nan) + builder = Builder(n_sd=n_sd, backend=backend_instance, environment=env) + + # act + particulator = builder.build( + attributes={ + "multiplicity": np.full((n_sd,), value), + "volume": np.full((n_sd,), np.nan), + } + ) + + # assert + assert particulator.attributes["multiplicity"].to_ndarray() == [value] diff --git a/PySDM/source/tests/unit_tests/attributes/test_reynolds_number.py b/PySDM/source/tests/unit_tests/attributes/test_reynolds_number.py new file mode 100644 index 0000000000000000000000000000000000000000..6f834e033f9b665494a1ed24e7fe357ca19d847c --- /dev/null +++ b/PySDM/source/tests/unit_tests/attributes/test_reynolds_number.py @@ -0,0 +1,36 @@ +"""tests calculation of particle Reynolds number""" + +import pytest +import numpy as np +from PySDM.environments import Box +from PySDM import Builder, Formulae +from PySDM.physics import si + + +@pytest.mark.parametrize("water_mass", (np.asarray([1 * si.ug, 100 * si.ug]),)) +def test_reynolds_number(water_mass, backend_class): + # arrange + env = Box(dt=None, dv=None) + formulae_enabling_reynolds_number_calculation = Formulae( + ventilation="Froessling1938" + ) + builder = Builder( + backend=backend_class(formulae_enabling_reynolds_number_calculation), + n_sd=water_mass.size, + environment=env, + ) + + builder.request_attribute("Reynolds number") + particulator = builder.build( + attributes={"water mass": water_mass, "multiplicity": np.ones_like(water_mass)} + ) + + particulator.environment["air dynamic viscosity"] = 2e-5 * si.Pa * si.s + particulator.environment["air density"] = 1 * si.kg / si.m**3 + + # act + re_actual = particulator.attributes["Reynolds number"].to_ndarray() + + # assert + assert (1 < re_actual).all() + assert (re_actual < 100).all() diff --git a/PySDM/source/tests/unit_tests/backends/__init__.py b/PySDM/source/tests/unit_tests/backends/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/unit_tests/backends/storage/__init__.py b/PySDM/source/tests/unit_tests/backends/storage/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/unit_tests/backends/storage/test_basic_ops.py b/PySDM/source/tests/unit_tests/backends/storage/test_basic_ops.py new file mode 100644 index 0000000000000000000000000000000000000000..ecd4b9e8760dd20fb8d52155154fb565195bf997 --- /dev/null +++ b/PySDM/source/tests/unit_tests/backends/storage/test_basic_ops.py @@ -0,0 +1,65 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest + + +class TestBasicOps: + @staticmethod + @pytest.mark.parametrize( + "output, addend, expected", + [ + ([1.0], 2, [3.0]), + ([1.0], [2], [3.0]), + ], + ) + def test_addition(backend_instance, output, addend, expected): + # Arrange + backend = backend_instance + output = backend.Storage.from_ndarray(np.asarray(output)) + if hasattr(addend, "__len__"): + addend = backend.Storage.from_ndarray(np.asarray(addend)) + + # Act + output += addend + + # Assert + np.testing.assert_array_equal(output.to_ndarray(), expected) + + @staticmethod + @pytest.mark.parametrize( + "data", + ( + [1.0], + [2.0, 3, 4], + [-1, np.nan, np.inf], + ), + ) + def test_exp(backend_class, data): + # Arrange + backend = backend_class(double_precision=True) + output = backend.Storage.from_ndarray(np.asarray(data)) + + # Act + output.exp() + + # Assert + np.testing.assert_array_equal(output.to_ndarray(), np.exp(data)) + + @staticmethod + @pytest.mark.parametrize( + "data, expected", + [ + ([1, 2], 2), + ([0, 0], 0), + ([999, 99, 9], 999), + ], + ) + def test_amax(backend_class, data, expected): + backend = backend_class(double_precision=True) + output = backend.Storage.from_ndarray(np.asarray(data)) + + # Act + actual = output.amax() + + # Assert + assert actual == expected diff --git a/PySDM/source/tests/unit_tests/backends/storage/test_index.py b/PySDM/source/tests/unit_tests/backends/storage/test_index.py new file mode 100644 index 0000000000000000000000000000000000000000..9e345a2355f1b3da02dcfd7f10c0bd3ec28f16ea --- /dev/null +++ b/PySDM/source/tests/unit_tests/backends/storage/test_index.py @@ -0,0 +1,26 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np + +from PySDM.backends.impl_common.index import make_Index +from PySDM.backends.impl_common.indexed_storage import make_IndexedStorage + + +class TestIndex: # pylint: disable=too-few-public-methods + @staticmethod + def test_remove_zero_n_or_flagged(backend_instance): + # Arrange + n_sd = 44 + idx = make_Index(backend_instance).identity_index(n_sd) + data = np.ones(n_sd).astype(np.int64) + data[0], data[n_sd // 2], data[-1] = 0, 0, 0 + data = backend_instance.Storage.from_ndarray(data) + data = make_IndexedStorage(backend_instance).indexed(storage=data, idx=idx) + + # Act + idx.remove_zero_n_or_flagged(data) + + # Assert + assert len(idx) == n_sd - 3 + assert ( + backend_instance.Storage.to_ndarray(data)[idx.to_ndarray()[: len(idx)]] > 0 + ).all() diff --git a/PySDM/source/tests/unit_tests/backends/storage/test_setitem.py b/PySDM/source/tests/unit_tests/backends/storage/test_setitem.py new file mode 100644 index 0000000000000000000000000000000000000000..0b496e5852d64462290bf21e4b20ffcf718261c5 --- /dev/null +++ b/PySDM/source/tests/unit_tests/backends/storage/test_setitem.py @@ -0,0 +1,20 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest + +from PySDM.backends import Numba, ThrustRTC + + +@pytest.mark.parametrize( + "backend", (pytest.param(ThrustRTC, marks=pytest.mark.xfail(strict=True)), Numba) +) +def test_setitem(backend): + # arrange + arr = backend.Storage.from_ndarray(np.zeros(3)) + + # act + arr[1] = 1 + + # assert + assert arr[1] == 1 + assert arr[0] == arr[2] == 0 diff --git a/PySDM/source/tests/unit_tests/backends/test_collisions_methods.py b/PySDM/source/tests/unit_tests/backends/test_collisions_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..bf26e0598ac35960ac032c6da5cfee5c8d67c796 --- /dev/null +++ b/PySDM/source/tests/unit_tests/backends/test_collisions_methods.py @@ -0,0 +1,336 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import os + +import numpy as np +import pytest + +from PySDM.backends import CPU, GPU +from PySDM.backends.impl_common.index import make_Index +from PySDM.backends.impl_common.indexed_storage import make_IndexedStorage +from PySDM.backends.impl_common.pair_indicator import make_PairIndicator +from PySDM.backends.impl_numba.methods.collisions_methods import pair_indices, coalesce + +NONZERO = 44 + + +class TestCollisionMethods: + @staticmethod + @pytest.mark.parametrize( + "i, idx, is_first_in_pair, gamma, expected", + ( + (0, (0, 1), (True, False), (NONZERO,), (0, 1, False)), + (0, (1, 0), (True, False), (NONZERO,), (1, 0, False)), + (0, (0, 1, 2), (False, True, False), (NONZERO,), (1, 2, False)), + ( + 1, + (0, 1, 2, 3, 4, 5, 6, 7), + (True, False, False, True, False, False, True, False), + # -i- |____ _____ | + (NONZERO, NONZERO), + (3, 4, False), + ), + ( + 2, + (0, 1, 2, 3, 4, 5, 6, 7), + (True, False, False, True, False, False, True, False), + # | -i- ____ | ____ + (NONZERO, NONZERO, 0), + (-1, -1, True), + ), + ( + 3, + (0, 1, 2, 3, 4, 5, 6, 7), # ____ _____ + (True, False, False, True, False, False, True, False), + # | | -i- + (NONZERO, NONZERO, 0, NONZERO), + (6, 7, False), + ), + ), + ) + def test_pair_indices(i, idx, is_first_in_pair, gamma, expected): + # Arrange + sut = ( + pair_indices if "NUMBA_DISABLE_JIT" in os.environ else pair_indices.py_func + ) + + # Act + actual = sut(i, idx, is_first_in_pair, gamma) + + # Assert + assert actual == expected + + @staticmethod + @pytest.mark.parametrize( + "dt_left, cell_start, expected", + ( + ((4, 5, 4.5, 0, 0), (0, 1, 2, 3, 4, 5), 3), + ((4, 5, 4.5, 3, 0.1), (0, 1, 2, 3, 4, 5), 5), + ), + ) + def test_adaptive_sdm_end(backend_instance, dt_left, cell_start, expected): + # Arrange + backend = backend_instance + dt_left = backend.Storage.from_ndarray(np.asarray(dt_left)) + cell_start = backend.Storage.from_ndarray(np.asarray(cell_start)) + + # Act + actual = backend.adaptive_sdm_end(dt_left, cell_start) + + # Assert + assert actual == expected + + @staticmethod + @pytest.mark.parametrize( + "gamma, idx, n, cell_id, dt_left, dt, dt_max, " + "is_first_in_pair, " + "expected_dt_left, expected_n_substep", + ( + ( + (10.0,), + (0, 1), + (44, 44), + (0, 0), + (10.0,), + 10.0, + 10.0, + (True, False), + (9.0,), + (1,), + ), + ( + (10.0,), + (0, 1), + (44, 44), + (0, 0), + (10.0,), + 10.0, + 0.1, + (True, False), + (9.9,), + (1,), + ), + ( + (0.0,), + (0, 1), + (44, 44), + (0, 0), + (10.0,), + 10.0, + 10.0, + (False, True), + (0.0,), + (1,), + ), + ( + (10.0,), + (0, 1), + (440, 44), + (0, 0), + (10.0,), + 10.0, + 10.0, + (True, False), + (0.0,), + (1,), + ), + ( + (0.5, 6), + (0, 1, 2, 3, 4), + (44, 44, 22, 33, 11), + (0, 0, 0, 1, 1), + (10.0, 10), + 10.0, + 10.0, + (True, False, False, True, False), + (0.0, 5.0), + (1, 1), + ), + ), + ) + # pylint: disable=too-many-locals + def test_scale_prob_for_adaptive_sdm_gamma_including_multi_cell_cases( + *, + backend_instance, + gamma, + idx, + n, + cell_id, + dt_left, + dt, + dt_max, + is_first_in_pair, + expected_dt_left, + expected_n_substep, + ): + # Arrange + backend = backend_instance + _gamma = backend.Storage.from_ndarray(np.asarray(gamma)) + _idx = make_Index(backend).from_ndarray(np.asarray(idx)) + _n = make_IndexedStorage(backend).from_ndarray(_idx, np.asarray(n)) + _cell_id = backend.Storage.from_ndarray(np.asarray(cell_id)) + _dt_left = backend.Storage.from_ndarray(np.asarray(dt_left)) + _is_first_in_pair = make_PairIndicator(backend)(len(n)) + _is_first_in_pair.indicator[:] = np.asarray(is_first_in_pair) + _n_substep = backend.Storage.from_ndarray(np.zeros_like(dt_left, dtype=int)) + _dt_min = backend.Storage.from_ndarray(np.zeros_like(dt_left)) + dt_range = (np.nan, dt_max) + + # Act + backend.scale_prob_for_adaptive_sdm_gamma( + prob=_gamma, + multiplicity=_n, + cell_id=_cell_id, + dt_left=_dt_left, + dt=dt, + dt_range=dt_range, + is_first_in_pair=_is_first_in_pair, + stats_n_substep=_n_substep, + stats_dt_min=_dt_min, + ) + + # Assert + np.testing.assert_array_almost_equal( + _dt_left.to_ndarray(), np.asarray(expected_dt_left) + ) + expected_gamma = np.empty_like(np.asarray(gamma)) + for i in range(len(idx)): + if is_first_in_pair[i]: + expected_gamma[i // 2] = ( + (dt - np.asarray(expected_dt_left[cell_id[i]])) + / dt + * np.asarray(gamma)[i // 2] + ) + np.testing.assert_array_almost_equal(_gamma.to_ndarray(), expected_gamma) + np.testing.assert_array_equal(_n_substep, np.asarray(expected_n_substep)) + + @staticmethod + @pytest.mark.parametrize( + "backend_class, scheme", + ((CPU, "counting_sort"), (CPU, "counting_sort_parallel"), (GPU, "default")), + ) + def test_cell_caretaker(backend_class, scheme): + # Arrange + backend = backend_class() + idx = [0, 3, 2, 4] + + cell_start = backend.Storage.from_ndarray(np.asarray([-1, -1])) + _idx = make_Index(backend).from_ndarray(np.asarray(idx, dtype=np.int64)) + + multiplicity = make_IndexedStorage(backend).from_ndarray( + _idx, np.asarray([1, 1, 1, 1]) + ) + _idx.remove_zero_n_or_flagged(multiplicity) + + cell_id = make_IndexedStorage(backend).from_ndarray( + _idx, np.asarray([0, 0, 0, 0]) + ) + cell_idx = make_Index(backend).from_ndarray(np.asarray([0])) + + sut = backend.make_cell_caretaker( + _idx.shape, _idx.dtype, len(cell_start), scheme=scheme + ) + + # Act + sut(cell_id, cell_idx, cell_start, _idx) + + # Assert + assert all(cell_start.to_ndarray()[:] == np.array([0, 3])) + + @staticmethod + @pytest.mark.parametrize( + "gamma, permutation, multiplicity, cell_id, dt_left, dt, dt_max, is_first_in_pair, ", + ( + ( # pylint: disable=undefined-variable,unused-variable + [2, 2, 2] + [3, 3] + [1, 1, 1], + tuple(range(n_part := 16)), + [1] * 2 + [100] * (n_part - 2), + [0] * (n_in_cell_0 := 6) + + [1] * (n_in_cell_1 := 4) + + [2] * (n_in_cell_2 := 6), + [dt := 12.0] * (n_cell := 3), + dt, + dt, + [True, False] * (n_part // 2), + ), + ), + ) + # pylint: disable=too-many-locals + def test_adaptivity_paper_diagram_scenario( + *, + gamma, + permutation, + multiplicity, + cell_id, + dt_left, + dt, + dt_max, + is_first_in_pair, + ): + # Arrange + backend = CPU() + _permutation = make_Index(backend).from_ndarray(np.asarray(permutation)) + _multiplicity = make_IndexedStorage(backend).from_ndarray( + _permutation, np.asarray(multiplicity) + ) + _cell_id = backend.Storage.from_ndarray(np.asarray(cell_id)) + _dt_left = backend.Storage.from_ndarray(np.asarray(dt_left)) + _is_first_in_pair = make_PairIndicator(backend)(len(multiplicity)) + _is_first_in_pair.indicator[:] = np.asarray(is_first_in_pair) + _n_substep = backend.Storage.from_ndarray(np.zeros_like(dt_left, dtype=int)) + _dt_min = backend.Storage.from_ndarray(np.asarray(dt_left)) + dt_range = (np.nan, dt_max) + + # Act + while any(_dt_left.data > 0): + _gamma = backend.Storage.from_ndarray(np.asarray(gamma)) + backend.scale_prob_for_adaptive_sdm_gamma( + prob=_gamma, + multiplicity=_multiplicity, + cell_id=_cell_id, + dt_left=_dt_left, + dt=dt, + dt_range=dt_range, + is_first_in_pair=_is_first_in_pair, + stats_n_substep=_n_substep, + stats_dt_min=_dt_min, + ) + assert all(_gamma.data == _gamma.data.astype(int)) + n_pair = len(gamma) + n_cell = len(dt_left) + for i in range(n_pair): + j, k, skip_pair = pair_indices( + i=i, + idx=_permutation.data, + is_first_in_pair=_is_first_in_pair.indicator.data, + prob_like=_gamma.data, + ) + if not skip_pair: + coalesce( + i=i, + j=j, + k=k, + cid=cell_id[j], + multiplicity=_multiplicity.data, + gamma=_gamma.data, + attributes=np.empty( + shape=( + 0, + 0, + ) + ), + coalescence_rate=np.empty(n_cell), + ) + if _multiplicity.data[j] == 0: + _is_first_in_pair.indicator.data[j] = False + gamma[i] = 0.0 + + # Assert + assert _n_substep[0] == 2 + assert _n_substep[1] == 3 + assert _n_substep[2] == 1 + assert (_dt_left.to_ndarray() == 0.0).all() + assert all(_dt_min.data == dt / _n_substep.data) + assert all( + _multiplicity.data + == (0, 1, 25, 25, 25, 25, 12, 13, 12, 13, 50, 50, 50, 50, 50, 50) + ) diff --git a/PySDM/source/tests/unit_tests/backends/test_ctor_defaults_and_warnings.py b/PySDM/source/tests/unit_tests/backends/test_ctor_defaults_and_warnings.py new file mode 100644 index 0000000000000000000000000000000000000000..85e91aad79ece71f9bb5f712ba3724a9446d8902 --- /dev/null +++ b/PySDM/source/tests/unit_tests/backends/test_ctor_defaults_and_warnings.py @@ -0,0 +1,35 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +from unittest import mock +import warnings +import inspect +import pytest +import numba + +from PySDM.backends import Numba, ThrustRTC + + +class TestCtorDefaultsAndWarnings: + @staticmethod + def test_gpu_ctor_defaults(): + signature = inspect.signature(ThrustRTC.__init__) + assert signature.parameters["verbose"].default is False + assert signature.parameters["debug"].default is False + assert signature.parameters["double_precision"].default is False + assert signature.parameters["formulae"].default is None + + @staticmethod + def test_cpu_ctor_defaults(): + signature = inspect.signature(Numba.__init__) + assert signature.parameters["formulae"].default is None + + @staticmethod + @mock.patch("PySDM.backends.numba.prange", new=range) + def test_check_numba_threading_warning(): + if numba.config.DISABLE_JIT: # pylint: disable=no-member + pytest.skip() + + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + with pytest.raises(ValueError) as exc_info: + Numba() + assert exc_info.match(r"^Numba threading enabled but does not work") diff --git a/PySDM/source/tests/unit_tests/backends/test_fake_thrust.py b/PySDM/source/tests/unit_tests/backends/test_fake_thrust.py new file mode 100644 index 0000000000000000000000000000000000000000..6ce5bfbf27006a4842e68941053c3dfb46117afe --- /dev/null +++ b/PySDM/source/tests/unit_tests/backends/test_fake_thrust.py @@ -0,0 +1,17 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +from PySDM.backends.impl_thrust_rtc.test_helpers.fake_thrust_rtc import FakeThrustRTC + + +def test_device_vector_fails_on_zero_size(): + # arrange + sut = FakeThrustRTC.device_vector + + # act + exception = None + try: + sut("float", size=0) + except ValueError as caught: + exception = caught + + # assert + assert exception is not None diff --git a/PySDM/source/tests/unit_tests/backends/test_instance_cache.py b/PySDM/source/tests/unit_tests/backends/test_instance_cache.py new file mode 100644 index 0000000000000000000000000000000000000000..400facfc90b07c163a9111a854bfdf26b54833f2 --- /dev/null +++ b/PySDM/source/tests/unit_tests/backends/test_instance_cache.py @@ -0,0 +1,31 @@ +"""asserts that cached and uncached backend instantiation works as expected""" + +import pytest + +from PySDM.backends import CPU, GPU, Numba, ThrustRTC + + +class TestInstanceCache: + @staticmethod + @pytest.mark.parametrize("fun", (CPU, GPU)) + def test_cache_hit(fun): + # arrange + backend1 = fun() + + # act + backend2 = fun() + + # assert + assert id(backend1) == id(backend2) + + @staticmethod + @pytest.mark.parametrize("fun", (Numba, ThrustRTC)) + def test_uncached_ctors(fun): + # arrange + backend1 = fun() + + # act + backend2 = fun() + + # assert + assert id(backend1) != id(backend2) diff --git a/PySDM/source/tests/unit_tests/backends/test_isotope_methods.py b/PySDM/source/tests/unit_tests/backends/test_isotope_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..05643be66729c5916a54b151504e37808c24ba30 --- /dev/null +++ b/PySDM/source/tests/unit_tests/backends/test_isotope_methods.py @@ -0,0 +1,30 @@ +""" +unit tests for backend isotope-related routines +""" + +import numpy as np + + +class TestIsotopeMethods: + @staticmethod + def test_isotopic_fractionation(backend_instance): + # arrange + backend = backend_instance + + # act + backend.isotopic_fractionation() + + @staticmethod + def test_isotopic_delta(backend_instance): + # arrange + backend = backend_instance + arr2storage = backend.Storage.from_ndarray + n_sd = 10 + output = arr2storage(np.empty(n_sd)) + ratio = arr2storage(np.zeros(n_sd)) + + # act + backend.isotopic_delta(output=output, ratio=ratio, reference_ratio=0.0001) + + # assert + assert (output.to_ndarray() == -1).all() diff --git a/PySDM/source/tests/unit_tests/backends/test_moments_methods.py b/PySDM/source/tests/unit_tests/backends/test_moments_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..57c3dacdd6cb4350d7f861a21c1fc25f0e050d94 --- /dev/null +++ b/PySDM/source/tests/unit_tests/backends/test_moments_methods.py @@ -0,0 +1,47 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest + +from PySDM import Formulae + + +@pytest.mark.parametrize( + "min_x, max_x, value, expected", + [ + (0, 1, 0.5, 1), + (0, 1, 0, 1), + (0, 1, 1, 0), + (0, 1, -0.5, 0), + (0, 1, 1.5, 0), + ], +) +def test_moments_range(backend_class, min_x, max_x, value, expected): + # Arrange + backend = backend_class(Formulae()) + + def arr(x): + return backend.Storage.from_ndarray(np.asarray((x,))) + + moment_0 = arr(0.0) + moments = backend.Storage.from_ndarray(np.full((1, 1), 0.0)) + + kw_args = { + "multiplicity": arr(1), + "attr_data": arr(0), + "cell_id": arr(0), + "idx": arr(0), + "length": 1, + "ranks": arr(0), + "x_attr": arr(value), + "weighting_attribute": arr(0), + "weighting_rank": 0, + "skip_division_by_m0": False, + } + + # Act + backend.moments( + moment_0=moment_0, moments=moments, min_x=min_x, max_x=max_x, **kw_args + ) + + # Assert + assert moment_0.to_ndarray()[:] == moments.to_ndarray()[:] == expected diff --git a/PySDM/source/tests/unit_tests/backends/test_oxidation.py b/PySDM/source/tests/unit_tests/backends/test_oxidation.py new file mode 100644 index 0000000000000000000000000000000000000000..5ed2534b6cc97699c9bcb854f421e271af175d89 --- /dev/null +++ b/PySDM/source/tests/unit_tests/backends/test_oxidation.py @@ -0,0 +1,185 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest + +from PySDM import Formulae +from PySDM.backends.impl_numba.methods.chemistry_methods import ChemistryMethods +from PySDM.backends.impl_numba.storage import Storage +from PySDM.dynamics.impl.chemistry_utils import ( + DISSOCIATION_FACTORS, + EquilibriumConsts, + KineticConsts, + k4, +) +from PySDM.physics import si +from PySDM.physics.constants import PI_4_3 +from PySDM.physics.constants_defaults import T_STP + +formulae = Formulae() + + +class SUT(ChemistryMethods): + def __init__(self): + self.formulae = formulae + super().__init__() + + +kinetic_consts = KineticConsts(formulae) +equilibrium_consts = EquilibriumConsts(formulae) + +k0 = Storage.from_ndarray(np.full(1, kinetic_consts.KINETIC_CONST["k0"].at(T_STP))) +k1 = Storage.from_ndarray(np.full(1, kinetic_consts.KINETIC_CONST["k1"].at(T_STP))) +k2 = Storage.from_ndarray(np.full(1, kinetic_consts.KINETIC_CONST["k2"].at(T_STP))) +k3 = Storage.from_ndarray(np.full(1, kinetic_consts.KINETIC_CONST["k3"].at(T_STP))) +K_SO2 = Storage.from_ndarray( + np.full(1, equilibrium_consts.EQUILIBRIUM_CONST["K_SO2"].at(T_STP)) +) +K_HSO3 = Storage.from_ndarray( + np.full(1, equilibrium_consts.EQUILIBRIUM_CONST["K_HSO3"].at(T_STP)) +) + +volume = PI_4_3 * (1 * si.um) ** 3 +pH = 5.0 +n_sd = 1 +eqc = { + k: Storage.from_ndarray( + np.full(n_sd, equilibrium_consts.EQUILIBRIUM_CONST[k].at(T_STP)) + ) + for k in ("K_HSO3", "K_SO2") +} +cell_ids = Storage.from_ndarray(np.zeros(n_sd, dtype=int)) +H = formulae.trivia.pH2H(pH) +DF = DISSOCIATION_FACTORS["SO2"](H, eqc, 0) + +O3_react_consts = ( + k0.data[0] + + k1.data[0] * K_SO2.data[0] / H + + k2.data[0] * K_SO2.data[0] * K_HSO3.data[0] / H**2 +) +H2O2_react_consts = k3.data[0] * K_SO2.data[0] / (1.0 + k4 * H) + +O3_init = 1e-4 +H2O2_init = 1e-4 +S_IV_init = 1e-4 +S_VI_init = 1e-4 + + +@pytest.mark.parametrize( + "conc", + [ + pytest.param( + { + "input": {"S_IV": 0.0, "S_VI": 0.0, "H2O2": 0.0, "O3": 0.0}, + "output": {"S_IV": 0.0, "S_VI": 0.0, "H2O2": 0.0, "O3": 0.0}, + }, + id="zeros", + ), + pytest.param( + { + "input": {"S_IV": S_IV_init, "O3": O3_init, "H2O2": 0.0, "S_VI": 0.0}, + "output": { + "S_VI": O3_react_consts * O3_init * S_IV_init / DF, + "O3": -O3_react_consts * O3_init * S_IV_init / DF, + "H2O2": 0.0, + "S_IV": -O3_react_consts * O3_init * S_IV_init / DF, + }, + }, + id="ozone", + ), + pytest.param( + { + "input": {"S_IV": S_IV_init, "O3": 0, "H2O2": H2O2_init, "S_VI": 0.0}, + "output": { + "S_VI": H2O2_react_consts * H2O2_init * S_IV_init / DF, + "O3": 0.0, + "H2O2": -H2O2_react_consts * H2O2_init * S_IV_init / DF, + "S_IV": -H2O2_react_consts * H2O2_init * S_IV_init / DF, + }, + }, + id="hydrogen peroxide", + ), + pytest.param( + { + "input": { + "S_IV": S_IV_init, + "O3": O3_init, + "H2O2": H2O2_init, + "S_VI": 0.0, + }, + "output": { + "S_VI": (H2O2_react_consts * H2O2_init + O3_react_consts * O3_init) + * S_IV_init + / DF, + "O3": -O3_react_consts * O3_init * S_IV_init / DF, + "H2O2": -H2O2_react_consts * H2O2_init * S_IV_init / DF, + "S_IV": -(H2O2_react_consts * H2O2_init + O3_react_consts * O3_init) + * S_IV_init + / DF, + }, + }, + id="all with no initial S_VI", + ), + pytest.param( + { + "input": { + "S_IV": S_IV_init, + "O3": O3_init, + "H2O2": H2O2_init, + "S_VI": S_VI_init, + }, + "output": { + "S_VI": (H2O2_react_consts * H2O2_init + O3_react_consts * O3_init) + * S_IV_init + / DF, + "O3": -O3_react_consts * O3_init * S_IV_init / DF, + "H2O2": -H2O2_react_consts * H2O2_init * S_IV_init / DF, + "S_IV": -(H2O2_react_consts * H2O2_init + O3_react_consts * O3_init) + * S_IV_init + / DF, + }, + }, + id="all", + ), + ], +) +@pytest.mark.parametrize("dt", (1, 0.1)) +def test_oxidation(conc, dt): + # Arrange + sut = SUT() + + moles = { + k: Storage.from_ndarray( + np.full(n_sd, 0.0 if k not in conc["input"] else conc["input"][k] * volume) + ) + for k in ("S_IV", "S_VI", "H2O2", "O3") + } + + # Act + sut.oxidation( + n_sd=n_sd, + cell_ids=cell_ids, + do_chemistry_flag=Storage.from_ndarray(np.full(n_sd, True)), + k0=k0, + k1=k1, + k2=k2, + k3=k3, + K_SO2=K_SO2, + K_HSO3=K_HSO3, + timestep=dt, + droplet_volume=Storage.from_ndarray(np.full(n_sd, volume)), + pH=Storage.from_ndarray(np.full(n_sd, pH)), + dissociation_factor_SO2=Storage.from_ndarray(np.full(n_sd, DF)), + # input/output + moles_O3=moles["O3"], + moles_H2O2=moles["H2O2"], + moles_S_IV=moles["S_IV"], + moles_S_VI=moles["S_VI"], + ) + + # Assert + for k in conc["output"].keys(): + np.testing.assert_allclose( + actual=moles[k].data / volume - conc["input"][k], + desired=conc["output"][k] * dt, + rtol=1e-10, + ) diff --git a/PySDM/source/tests/unit_tests/backends/test_pair_methods.py b/PySDM/source/tests/unit_tests/backends/test_pair_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..226f6af853ccd0ca1d5dc47fb5209e50baf89f65 --- /dev/null +++ b/PySDM/source/tests/unit_tests/backends/test_pair_methods.py @@ -0,0 +1,124 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import os + +import numpy as np +import pytest + +from PySDM.backends import CPU +from PySDM.backends.impl_common.index import make_Index +from PySDM.backends.impl_common.pair_indicator import make_PairIndicator + + +class TestPairMethods: + @staticmethod + @pytest.mark.parametrize( + "_data_in, _data_out, _is_first_in_pair, _idx", + ( + pytest.param( + [44.0, 666.0], + [ + 0, + ], + [True, True], + [0, 1], + marks=pytest.mark.xfail(strict=True), + ), + pytest.param( + [44.0, 666.0], + [ + 0, + ], + [True, False], + [0, 1], + ), + ), + ) + def test_sum_pair_body_out_of_bounds( + _data_in, _data_out, _is_first_in_pair, _idx, backend_class=CPU + ): + # Arrange + backend = backend_class() + + data_out = backend.Storage.from_ndarray(np.asarray(_data_out)) + data_in = backend.Storage.from_ndarray(np.asarray(_data_in)) + + is_first_in_pair = make_PairIndicator(backend)(len(_is_first_in_pair)) + is_first_in_pair.indicator = backend.Storage.from_ndarray( + np.asarray(_is_first_in_pair) + ) + + idx = backend.Storage.from_ndarray(np.asarray(_idx)) + + sut = ( + backend._sum_pair_body + if "NUMBA_DISABLE_JIT" in os.environ + else backend._sum_pair_body.py_func + ) + # Act + sut( + data_out.data, + data_in.data, + is_first_in_pair.indicator.data, + idx.data, + len(idx), + ) + + # Assert + + @staticmethod + @pytest.mark.parametrize( + "_data_in, _data_out, _idx", + ( + pytest.param( + [44.0, 666.0], + [ + 0, + ], + [0, 1], + ), + ), + ) + def test_sum_pair(_data_in, _data_out, _idx, backend_instance): + # Arrange + backend = backend_instance + + data_out = backend.Storage.from_ndarray(np.asarray(_data_out)) + data_in = backend.Storage.from_ndarray(np.asarray(_data_in)) + + is_first_in_pair = make_PairIndicator(backend)(len(_data_in)) + is_first_in_pair.indicator = backend.Storage.from_ndarray( + np.asarray( + [True, False], + ) + ) + + idx = backend.Storage.from_ndarray(np.asarray(_idx)) + + # Act + backend.sum_pair(data_out, data_in, is_first_in_pair, idx) + + # Assert + np.testing.assert_array_equal(data_out, [44.0 + 666.0]) + + @staticmethod + @pytest.mark.parametrize("length", (1, 2, 3, 4)) + def test_find_pairs_length(backend_instance, length): + # arrange + backend = backend_instance + n_sd = 4 + + cell_start = backend.Storage.from_ndarray(np.asarray([0, 0, 0, 0])) + cell_id = backend.Storage.from_ndarray(np.asarray([0, 0, 0, 0])) + cell_idx = backend.Storage.from_ndarray(np.asarray([0, 1, 2, 3])) + is_first_in_pair = make_PairIndicator(backend)(n_sd) + is_first_in_pair.indicator = backend.Storage.from_ndarray( + np.asarray([True] * n_sd) + ) + idx = make_Index(backend).identity_index(n_sd) + + # act + idx.length = length + backend.find_pairs(cell_start, is_first_in_pair, cell_id, cell_idx, idx) + + # assert + assert not is_first_in_pair.indicator[length - 1] diff --git a/PySDM/source/tests/unit_tests/backends/test_physics_methods.py b/PySDM/source/tests/unit_tests/backends/test_physics_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..9b202a90d9288aad482be35af61e4581c715dc27 --- /dev/null +++ b/PySDM/source/tests/unit_tests/backends/test_physics_methods.py @@ -0,0 +1,104 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest + +from PySDM import Formulae +from PySDM.physics import si + + +class TestPhysicsMethods: + @staticmethod + def test_temperature_pressure_rh(backend_instance): + # Arrange + backend = backend_instance + sut = backend.temperature_pressure_rh + rhod = backend.Storage.from_ndarray(np.asarray((1, 1.1))) + thd = backend.Storage.from_ndarray(np.asarray((300.0, 301))) + water_vapour_mixing_ratio = backend.Storage.from_ndarray( + np.asarray((0.01, 0.02)) + ) + + T = backend.Storage.from_ndarray(np.zeros_like(water_vapour_mixing_ratio)) + p = backend.Storage.from_ndarray(np.zeros_like(water_vapour_mixing_ratio)) + RH = backend.Storage.from_ndarray(np.zeros_like(water_vapour_mixing_ratio)) + + # Act + sut( + rhod=rhod, + thd=thd, + water_vapour_mixing_ratio=water_vapour_mixing_ratio, + T=T, + p=p, + RH=RH, + ) + + # Assert + assert 282 * si.K < T.amin() < 283 * si.K + assert 810 * si.hPa < p.amin() < 830 * si.hPa + assert 1.10 < RH.amin() < 1.11 + + @staticmethod + @pytest.mark.parametrize("variant", ("LiquidSpheres", "MixedPhaseSpheres")) + def test_mass_to_volume(backend_class, variant): + # Arrange + formulae = Formulae(particle_shape_and_density=variant) + backend = backend_class(formulae, double_precision=True) + sut = backend.volume_of_water_mass + mass = np.asarray([1.0, -1.0]) + mass_in = backend.Storage.from_ndarray(mass) + volume_out = backend.Storage.from_ndarray(np.zeros_like(mass_in)) + + # Act + sut(volume=volume_out, mass=mass_in) + + # Assert + assert (mass_in.to_ndarray() == mass).all() + if variant == "LiquidSpheres": + assert ( + volume_out.to_ndarray() + == mass_in.to_ndarray() / formulae.constants.rho_w + ).all() + elif variant == "MixedPhaseSpheres": + assert ( + volume_out.to_ndarray() + == np.where( + mass < 0, + mass / formulae.constants.rho_i, + mass / formulae.constants.rho_w, + ) + ).all() + else: + raise NotImplementedError() + + @staticmethod + @pytest.mark.parametrize("variant", ("LiquidSpheres", "MixedPhaseSpheres")) + def test_volume_to_mass(backend_class, variant): + # Arrange + formulae = Formulae(particle_shape_and_density=variant) + backend = backend_class(formulae, double_precision=True) + sut = backend.mass_of_water_volume + volume = np.asarray([1.0, -1.0]) + volume_in = backend.Storage.from_ndarray(volume) + mass_out = backend.Storage.from_ndarray(np.zeros_like(volume_in)) + + # Act + sut(volume=volume_in, mass=mass_out) + + # Assert + assert (volume_in.to_ndarray() == volume).all() + if variant == "LiquidSpheres": + assert ( + mass_out.to_ndarray() + == volume_in.to_ndarray() * formulae.constants.rho_w + ).all() + elif variant == "MixedPhaseSpheres": + assert ( + mass_out.to_ndarray() + == np.where( + volume < 0, + volume * formulae.constants.rho_i, + volume * formulae.constants.rho_w, + ) + ).all() + else: + raise NotImplementedError() diff --git a/PySDM/source/tests/unit_tests/backends/test_seeding_methods.py b/PySDM/source/tests/unit_tests/backends/test_seeding_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..09a10d4504e863731988977177413c1463282849 --- /dev/null +++ b/PySDM/source/tests/unit_tests/backends/test_seeding_methods.py @@ -0,0 +1,158 @@ +"""Seeding backend tests of injection logic""" + +from contextlib import nullcontext + +import numpy as np +import pytest + +from PySDM import Builder +from PySDM.environments import Box +from PySDM.backends import CPU +from PySDM.physics import si + + +class TestSeeding: + max_number_to_inject = 4 + + @staticmethod + @pytest.mark.parametrize( + "n_sd, number_of_super_particles_to_inject, context", + ( + (1, 1, nullcontext()), + ( + 1, + 2, + pytest.raises( + ValueError, match="inject more super particles than space available" + ), + ), + (max_number_to_inject, max_number_to_inject - 1, nullcontext()), + (max_number_to_inject, max_number_to_inject, nullcontext()), + ( + max_number_to_inject + 2, + max_number_to_inject + 1, + pytest.raises( + ValueError, + match="inject multiple super particles with the same attributes", + ), + ), + ), + ) + def test_number_of_super_particles_to_inject( + n_sd, + number_of_super_particles_to_inject, + context, + dt=1, + dv=1, + ): + # arrange + builder = Builder(n_sd, CPU(), Box(dt, dv)) + particulator = builder.build( + attributes={ + "multiplicity": np.full(n_sd, np.nan), + "water mass": np.zeros(n_sd), + }, + ) + + seeded_particle_extensive_attributes = { + "water mass": [0.0001 * si.ng] * TestSeeding.max_number_to_inject, + } + seeded_particle_multiplicity = [1] * TestSeeding.max_number_to_inject + + seeded_particle_index = particulator.Index.identity_index( + len(seeded_particle_multiplicity) + ) + seeded_particle_multiplicity = particulator.IndexedStorage.from_ndarray( + seeded_particle_index, + np.asarray(seeded_particle_multiplicity), + ) + seeded_particle_extensive_attributes = particulator.IndexedStorage.from_ndarray( + seeded_particle_index, + np.asarray(list(seeded_particle_extensive_attributes.values())), + ) + + # act + with context: + particulator.seeding( + seeded_particle_index=seeded_particle_index, + seeded_particle_multiplicity=seeded_particle_multiplicity, + seeded_particle_extensive_attributes=seeded_particle_extensive_attributes, + number_of_super_particles_to_inject=number_of_super_particles_to_inject, + ) + + # assert + assert ( + number_of_super_particles_to_inject + == particulator.attributes.super_droplet_count + ) + + @staticmethod + @pytest.mark.parametrize( + "seeded_particle_index, context", + ( + ([0, 0, 0], nullcontext()), + ([0, 1, 2], nullcontext()), + ([2, 1, 0], nullcontext()), + ( + [0], + pytest.raises( + ValueError, + match=" multiple super particles with the same attributes", + ), + ), + ), + ) + def test_seeded_particle_index_multiplicity_extensive_attributes( + seeded_particle_index, + context, + n_sd=3, + number_of_super_particles_to_inject=3, + ): + # arrange + builder = Builder(n_sd, CPU(), Box(dt=np.nan, dv=np.nan)) + particulator = builder.build( + attributes={ + "multiplicity": np.full(n_sd, np.nan), + "water mass": np.zeros(n_sd), + }, + ) + + seeded_particle_extensive_attributes = { + "water mass": [0.0001, 0.0003, 0.0002], + } + seeded_particle_multiplicity = [1, 2, 3] + + seeded_particle_index_impl = particulator.Index.from_ndarray( + np.asarray(seeded_particle_index) + ) + seeded_particle_multiplicity_impl = particulator.IndexedStorage.from_ndarray( + seeded_particle_index_impl, + np.asarray(seeded_particle_multiplicity), + ) + seeded_particle_extensive_attributes_impl = ( + particulator.IndexedStorage.from_ndarray( + seeded_particle_index_impl, + np.asarray(list(seeded_particle_extensive_attributes.values())), + ) + ) + + # act + with context: + particulator.seeding( + seeded_particle_index=seeded_particle_index_impl, + seeded_particle_multiplicity=seeded_particle_multiplicity_impl, + seeded_particle_extensive_attributes=seeded_particle_extensive_attributes_impl, + number_of_super_particles_to_inject=number_of_super_particles_to_inject, + ) + + # assert + np.testing.assert_array_equal( + particulator.attributes["multiplicity"].to_ndarray(), + np.asarray(seeded_particle_multiplicity)[seeded_particle_index], + ) + np.testing.assert_array_equal( + particulator.attributes["water mass"].to_ndarray(), + np.asarray(seeded_particle_extensive_attributes["water mass"])[ + seeded_particle_index + ], + ) diff --git a/PySDM/source/tests/unit_tests/backends/test_toms748.py b/PySDM/source/tests/unit_tests/backends/test_toms748.py new file mode 100644 index 0000000000000000000000000000000000000000..f22ef477b27350b19a150e3f0aa8951964de8fd6 --- /dev/null +++ b/PySDM/source/tests/unit_tests/backends/test_toms748.py @@ -0,0 +1,48 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import os + +import numba +import numpy as np +import pytest +from scipy.optimize import toms748 + +from PySDM.backends.impl_numba.toms748 import toms748_solve +from PySDM.formulae import Formulae + +# relevant +# https://github.com/scipy/scipy/blob/master/scipy/optimize/tests/test_zeros.py +# https://github.com/boostorg/math/blob/develop/test/test_toms748_solve.cpp + + +@numba.njit() +def f1(x): + return x**2 - 2 * x - 1 + + +@numba.njit() +def f2(x): + return np.exp(x) - np.cos(x) + + +@pytest.mark.parametrize("fun", (f1, f2)) +def test_toms748(fun): + sut = toms748_solve if "NUMBA_DISABLE_JIT" in os.environ else toms748_solve.py_func + + a = -0.5 + b = 0.5 + rtol = 1e-6 + wt = Formulae().trivia.within_tolerance + actual, _ = sut( + fun, + (), + ax=a, + bx=b, + fax=fun(a), + fbx=fun(b), + max_iter=10, + rtol=rtol, + within_tolerance=wt, + ) + expected = toms748(fun, a, b) + + np.testing.assert_almost_equal(actual, expected) diff --git a/PySDM/source/tests/unit_tests/conftest.py b/PySDM/source/tests/unit_tests/conftest.py new file mode 100644 index 0000000000000000000000000000000000000000..0164d0b3ce8a50879e2d70a9e1ab1ac4992411b0 --- /dev/null +++ b/PySDM/source/tests/unit_tests/conftest.py @@ -0,0 +1,17 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import pytest + +from PySDM.backends import CPU, GPU, ThrustRTC, Numba + + +@pytest.fixture(params=(Numba, ThrustRTC)) +def backend_class(request): + return request.param + + +@pytest.fixture( + params=(pytest.param(CPU(), id="CPU"), pytest.param(GPU(), id="GPU")), + scope="session", +) +def backend_instance(request): + return request.param diff --git a/PySDM/source/tests/unit_tests/dummy_environment.py b/PySDM/source/tests/unit_tests/dummy_environment.py new file mode 100644 index 0000000000000000000000000000000000000000..b99ed36d29354fca17964af49a33aa13fa808a2b --- /dev/null +++ b/PySDM/source/tests/unit_tests/dummy_environment.py @@ -0,0 +1,67 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np + +from PySDM.impl.mesh import Mesh +from PySDM.environments.impl import register_environment + + +@register_environment() +class DummyEnvironment: # pylint: disable=too-many-instance-attributes + def __init__( + self, + *, + timestep=None, + grid=None, + size=None, + volume=None, + courant_field_data=None, + halo=None, + ): + self.particulator = None + self.dt = timestep + if grid is None: + self.mesh = Mesh.mesh_0d(volume) + else: + if size is None: + size = tuple(1 for _ in range(len(grid))) + self.mesh = Mesh(grid, size) + if halo is not None: + self.halo = halo + self.water_vapour_mixing_ratio = np.empty( + (grid[0] + 2 * halo, grid[1] + 2 * halo) + ) + self.thd = np.empty((grid[0] + 2 * halo, grid[1] + 2 * halo)) + self.pred = {} + self.step_counter = 0 + self.courant_field_data = courant_field_data + + def register(self, *, builder): + self.particulator = builder.particulator + if hasattr(self, "halo"): + self.pred["water_vapour_mixing_ratio"] = ( + self.particulator.backend.Storage.empty(self.mesh.n_cell, dtype=float) + ) + self.pred["thd"] = self.particulator.backend.Storage.empty( + self.mesh.n_cell, dtype=float + ) + + def get_courant_field_data(self): + return self.courant_field_data + + def get_predicted(self, key): + return self.pred[key] + + def get_water_vapour_mixing_ratio(self): + if self.halo is not None: + halo = int(self.halo) + return self.water_vapour_mixing_ratio[halo:-halo, halo:-halo] + raise ValueError() + + def get_thd(self): + if self.halo is not None: + halo = int(self.halo) + return self.thd[halo:-halo, halo:-halo] + raise ValueError() + + def sync(self): + pass diff --git a/PySDM/source/tests/unit_tests/dummy_particulator.py b/PySDM/source/tests/unit_tests/dummy_particulator.py new file mode 100644 index 0000000000000000000000000000000000000000..2edc5b59686d1f1efb101016f9986e48c350ba50 --- /dev/null +++ b/PySDM/source/tests/unit_tests/dummy_particulator.py @@ -0,0 +1,17 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +from PySDM.builder import Builder +from PySDM.particulator import Particulator + +from .dummy_environment import DummyEnvironment + + +class DummyParticulator(Builder, Particulator): + def __init__(self, backend_class, n_sd=0, formulae=None, grid=None): + backend = backend_class(formulae, double_precision=True) + env = DummyEnvironment(grid=grid) + Builder.__init__(self, n_sd, backend, env) + Particulator.__init__(self, n_sd, backend) + self.environment = env.instantiate(builder=self) # pylint: disable=no-member + self.particulator = self + self.req_attr_names = ["multiplicity", "cell id"] + self.attributes = None diff --git a/PySDM/source/tests/unit_tests/dynamics/__init__.py b/PySDM/source/tests/unit_tests/dynamics/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/unit_tests/dynamics/collisions/__init__.py b/PySDM/source/tests/unit_tests/dynamics/collisions/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/unit_tests/dynamics/collisions/conftest.py b/PySDM/source/tests/unit_tests/dynamics/collisions/conftest.py new file mode 100644 index 0000000000000000000000000000000000000000..710e950d750bf6fddb7e0d9ccf553150cee6a41c --- /dev/null +++ b/PySDM/source/tests/unit_tests/dynamics/collisions/conftest.py @@ -0,0 +1,85 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest + +from PySDM.dynamics.collisions.collision import DEFAULTS, Coalescence +from PySDM.environments import Box + +from ....unit_tests.dummy_particulator import DummyParticulator + + +class StubKernel: + def __init__(self, backend, returned_value=0): + self.returned_value = returned_value + self.backend = backend + + def register(self, particles_builder): + pass + + def __call__(self, output, is_first_in_pair): + backend_fill(output, self.returned_value) + + +def backend_fill(array, value, odd_zeros=False): + if odd_zeros: + if isinstance(value, np.ndarray): + full_ndarray = insert_zeros(value[::2]).astype(np.float64) + else: + full_ndarray = np.full(array.shape[0] // 2, value).astype(np.float64) + full_ndarray = insert_zeros(full_ndarray) + if array.shape[0] % 2 != 0: + full_ndarray = np.concatenate((full_ndarray, np.zeros(1))) + else: + full_ndarray = np.full(array.shape, value).astype(np.float64) + + array.upload(full_ndarray) + + +def insert_zeros(array): + result = ( + np.concatenate((array, np.zeros_like(array))).reshape(2, -1).flatten(order="F") + ) + return result + + +def get_dummy_particulator_and_coalescence( + backend, n_length, optimized_random=False, environment=None, substeps=1 +): + particulator = DummyParticulator(backend, n_sd=n_length) + particulator.environment = environment or Box(dv=1, dt=DEFAULTS.dt_coal_range[1]) + coalescence = Coalescence( + collision_kernel=StubKernel(particulator.backend), + optimized_random=optimized_random, + substeps=substeps, + adaptive=False, + ) + coalescence.register(particulator) + return particulator, coalescence + + +__x__ = { + "ones_2": pytest.param(np.array([1.0, 1.0])), + "random_2": pytest.param(np.array([4.0, 2.0])), +} + + +@pytest.fixture(params=[__x__["ones_2"], __x__["random_2"]], name="v_2") +def v_2_fixture(request): + return request.param + + +@pytest.fixture(params=[__x__["ones_2"], __x__["random_2"]], name="T_2") +def T_2_fixture(request): + return request.param + + +__n__ = { + "1_1": pytest.param(np.array([1, 1])), + "5_1": pytest.param(np.array([5, 1])), + "5_3": pytest.param(np.array([5, 3])), +} + + +@pytest.fixture(params=[__n__["1_1"], __n__["5_1"], __n__["5_3"]], name="n_2") +def n_2_fixture(request): + return request.param diff --git a/PySDM/source/tests/unit_tests/dynamics/collisions/test_croupiers.py b/PySDM/source/tests/unit_tests/dynamics/collisions/test_croupiers.py new file mode 100644 index 0000000000000000000000000000000000000000..421ada1eae43eeed9558e69a01918cd135fcd2df --- /dev/null +++ b/PySDM/source/tests/unit_tests/dynamics/collisions/test_croupiers.py @@ -0,0 +1,55 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest + +from PySDM.backends import ThrustRTC +from PySDM.initialisation.sampling.spectral_sampling import Linear +from PySDM.initialisation.spectra.lognormal import Lognormal + +from ...dummy_particulator import DummyParticulator + + +@pytest.mark.parametrize("croupier", ["local", "global"]) +def test_final_state(croupier, backend_class): + if backend_class is ThrustRTC: + pytest.skip("TODO #330") + + # Arrange + n_part = 100000 + v_mean = 2e-6 + d = 1.2 + n_sd = 32 + x = 4 + y = 4 + + attributes = {} + spectrum = Lognormal(n_part, v_mean, d) + attributes["volume"], attributes["multiplicity"] = Linear( + spectrum + ).sample_deterministic(n_sd) + particulator = DummyParticulator(backend_class, n_sd, grid=(x, y)) + particulator.croupier = croupier + + attributes["cell id"] = np.array((n_sd,), dtype=int) + cell_origin_np = np.concatenate( + [np.random.randint(0, x, n_sd), np.random.randint(0, y, n_sd)] + ).reshape((2, -1)) + attributes["cell origin"] = cell_origin_np + position_in_cell_np = np.concatenate( + [np.random.rand(n_sd), np.random.rand(n_sd)] + ).reshape((2, -1)) + attributes["position in cell"] = position_in_cell_np + particulator.build(attributes) + + # Act + u01 = backend_class.Storage.from_ndarray(np.random.random(n_sd)) + particulator.attributes.permutation(u01, local=particulator.croupier == "local") + _ = particulator.attributes.cell_start + + # Assert + diff = np.diff( + particulator.attributes["cell id"][ + particulator.attributes._ParticleAttributes__idx + ] + ) + assert (diff >= 0).all() diff --git a/PySDM/source/tests/unit_tests/dynamics/collisions/test_defaults.py b/PySDM/source/tests/unit_tests/dynamics/collisions/test_defaults.py new file mode 100644 index 0000000000000000000000000000000000000000..9e8826522c7f70109ae13bade46d9b06e29c83f8 --- /dev/null +++ b/PySDM/source/tests/unit_tests/dynamics/collisions/test_defaults.py @@ -0,0 +1,22 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import inspect + +import pytest + +from PySDM.dynamics.collisions import Breakup, Coalescence, Collision + + +def get_default_args(func): + signature = inspect.signature(func) + return { + k: v.default + for k, v in signature.parameters.items() + if v.default is not inspect.Parameter.empty + } + + +class TestDefaults: # pylint: disable=too-few-public-methods + @staticmethod + @pytest.mark.parametrize("dynamic_class", (Collision, Breakup, Coalescence)) + def test_collision_adaptive_default(dynamic_class): + assert get_default_args(dynamic_class.__init__)["adaptive"] is True diff --git a/PySDM/source/tests/unit_tests/dynamics/collisions/test_efficiencies.py b/PySDM/source/tests/unit_tests/dynamics/collisions/test_efficiencies.py new file mode 100644 index 0000000000000000000000000000000000000000..c5853d606a6151c3189adfcf6ffef573165be4c6 --- /dev/null +++ b/PySDM/source/tests/unit_tests/dynamics/collisions/test_efficiencies.py @@ -0,0 +1,123 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +from matplotlib import pyplot +import numpy as np +import pytest + +from PySDM import Builder +from PySDM.backends import CPU +from PySDM.dynamics.collisions.breakup_efficiencies import ConstEb +from PySDM.dynamics.collisions.coalescence_efficiencies import ( + Berry1967, + ConstEc, + LowList1982Ec, + SpecifiedEff, + Straub2010Ec, +) +from PySDM.environments import Box +from PySDM.physics import si + + +class TestEfficiencies: + @staticmethod + @pytest.mark.parametrize( + "efficiency", + [ + Berry1967(), + ConstEc(Ec=0.5), + SpecifiedEff(A=0.8, B=0.6), + Straub2010Ec(), + LowList1982Ec(), + ConstEb(Eb=0.3), + ], + ) + def test_efficiency_fn_call(efficiency, backend_class=CPU): + # arrange + volume = np.asarray([440.0 * si.um**3, 6660.0 * si.um**3]) + builder = Builder( + volume.size, backend_class(), environment=Box(dv=None, dt=None) + ) + sut = efficiency + sut.register(builder) + particulator = builder.build( + attributes={"volume": volume, "multiplicity": np.ones_like(volume)} + ) + + eff = particulator.PairwiseStorage.from_ndarray(np.asarray([-1.0])) + is_first_in_pair = particulator.PairIndicator(length=volume.size) + is_first_in_pair.indicator = particulator.Storage.from_ndarray( + np.asarray([True, False]) + ) + + # act + sut(eff, is_first_in_pair) + values = eff.to_ndarray() + + # Assert + assert np.min(values) >= 0 + assert np.max(values) <= 1 + + @staticmethod + @pytest.mark.parametrize( + "sut", + [ + Straub2010Ec(), + ], + ) + def test_efficiency_dist(sut, backend_class=CPU, plot=False): + # arrange + n_per = 20 + n_sd = 2 + + drop_size_L_diam = np.linspace(0.01, 0.5, n_per) * si.cm + drop_size_S_diam = np.linspace(0.01, 0.2, n_per) * si.cm + + builder = Builder(n_sd, backend_class(), environment=Box(dv=None, dt=None)) + sut.register(builder) + particulator = builder.build( + attributes={ + "volume": np.full(shape=n_sd, fill_value=np.nan), + "multiplicity": np.ones(n_sd), + } + ) + + eff = particulator.PairwiseStorage.from_ndarray(np.asarray([-1.0])) + is_first_in_pair = particulator.PairIndicator(length=n_sd) + is_first_in_pair.indicator = particulator.Storage.from_ndarray( + np.asarray([True, False]) + ) + + radius_to_mass = particulator.formulae.particle_shape_and_density.radius_to_mass + + # act + res = np.full(shape=(n_per, n_per), fill_value=np.nan) + for i in range(n_per): + for j in range(n_per): + if drop_size_L_diam[i] >= drop_size_S_diam[j]: + particulator.attributes["water mass"].data.data[:] = np.asarray( + [ + radius_to_mass(drop_size_S_diam[j] / 2), + radius_to_mass(drop_size_L_diam[i] / 2), + ] + ) + particulator.attributes.mark_updated("water mass") + sut(eff, is_first_in_pair) + (res[i, j],) = eff.data + + # plot + pyplot.colorbar( + pyplot.contourf( + *np.meshgrid(drop_size_L_diam, drop_size_S_diam), + res.T, + levels=np.linspace(0.0, 1.0, 11), + cmap="jet", + ) + ) + + if plot: + pyplot.show() + else: + pyplot.clf() + + # assert + assert np.nanmax(res) <= 1 + assert np.nanmin(res) >= 0 diff --git a/PySDM/source/tests/unit_tests/dynamics/collisions/test_fragmentations.py b/PySDM/source/tests/unit_tests/dynamics/collisions/test_fragmentations.py new file mode 100644 index 0000000000000000000000000000000000000000..6bbe51bccf421e35653e4a90c6b437c37e360250 --- /dev/null +++ b/PySDM/source/tests/unit_tests/dynamics/collisions/test_fragmentations.py @@ -0,0 +1,419 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import matplotlib.pyplot as plt +import numpy as np +import pytest + +from PySDM import Builder, Formulae +from PySDM.backends import CPU +from PySDM.dynamics.collisions.breakup_fragmentations import ( + SLAMS, + AlwaysN, + ConstantMass, + Exponential, + Feingold1988, + Gaussian, + Straub2010Nf, +) +from PySDM.environments import Box +from PySDM.physics import constants_defaults, si + +ARBITRARY_VALUE_BETWEEN_0_AND_1 = 0.5 + + +def dummy_u01(builder, size): + return builder.particulator.PairwiseStorage.from_ndarray( + np.full(size, ARBITRARY_VALUE_BETWEEN_0_AND_1) + ) + + +class TestFragmentations: # pylint: disable=too-few-public-methods + @staticmethod + @pytest.mark.parametrize( + "fragmentation_fn", + ( + AlwaysN(n=2), + Exponential(scale=1e6 * si.um**3), + Feingold1988(scale=1e6 * si.um**3), + Gaussian( + mu=2e6 * si.um**3, + sigma=1e6 * si.um**3, + ), + SLAMS(), + Straub2010Nf(), + ), + ) + def test_fragmentation_fn_call(fragmentation_fn, backend_class): + # arrange + volume = np.asarray([440.0 * si.um**3, 6660.0 * si.um**3]) + fragments = np.asarray([-1.0]) + env = Box(dv=None, dt=None) + builder = Builder( + volume.size, + backend_class( + Formulae(fragmentation_function=fragmentation_fn.__class__.__name__) + ), + environment=env, + ) + sut = fragmentation_fn + sut.vmin = 1 * si.um**3 + sut.register(builder) + _ = builder.build( + attributes={"volume": volume, "multiplicity": np.ones_like(volume)} + ) + + _PairwiseStorage = builder.particulator.PairwiseStorage + _Indicator = builder.particulator.PairIndicator + nf = _PairwiseStorage.from_ndarray(np.zeros_like(fragments)) + frag_mass = _PairwiseStorage.from_ndarray(np.zeros_like(fragments)) + is_first_in_pair = _Indicator(length=volume.size) + is_first_in_pair.indicator = builder.particulator.Storage.from_ndarray( + np.asarray([True, False]) + ) + u01 = dummy_u01(builder, fragments.size) + + # act + sut(nf, frag_mass, u01, is_first_in_pair) + + # Assert + np.testing.assert_array_less([0.99], nf.to_ndarray()) + np.testing.assert_array_less([0.0], frag_mass.to_ndarray()) + + np.testing.assert_approx_equal( + nf[0] * frag_mass[0], np.sum(volume) * constants_defaults.rho_w + ) + + @staticmethod + @pytest.mark.parametrize( + "fragmentation_fn", + [ + Exponential( + scale=1 * si.um**3, + vmin=6660.0 * si.um**3, + ), + Feingold1988( + scale=1 * si.um**3, + vmin=6660.0 * si.um**3, + ), + Gaussian( + mu=2 * si.um**3, + sigma=1 * si.um**3, + vmin=6660.0 * si.um**3, + ), + SLAMS(vmin=6660.0 * si.um**3), + Straub2010Nf(vmin=6660.0 * si.um**3), + pytest.param(AlwaysN(n=10), marks=pytest.mark.xfail(strict=True)), + ], + ) + def test_fragmentation_limiters_vmin(fragmentation_fn, backend_class): + # arrange + volume = np.asarray([440.0 * si.um**3, 6660.0 * si.um**3]) + fragments = np.asarray([-1.0]) + env = Box(dv=None, dt=None) + builder = Builder( + volume.size, + backend_class( + Formulae(fragmentation_function=fragmentation_fn.__class__.__name__), + double_precision=True, + ), + environment=env, + ) + sut = fragmentation_fn + sut.register(builder) + _ = builder.build( + attributes={"volume": volume, "multiplicity": np.ones_like(volume)} + ) + + _PairwiseStorage = builder.particulator.PairwiseStorage + _Indicator = builder.particulator.PairIndicator + nf = _PairwiseStorage.from_ndarray(np.zeros_like(fragments)) + frag_mass = _PairwiseStorage.from_ndarray(np.zeros_like(fragments)) + is_first_in_pair = _Indicator(length=volume.size) + is_first_in_pair.indicator = builder.particulator.Storage.from_ndarray( + np.asarray([True, False]) + ) + u01 = dummy_u01(builder, fragments.size) + + # act + sut(nf, frag_mass, u01, is_first_in_pair) + + # Assert + np.testing.assert_array_equal([1.0], nf.to_ndarray()) + np.testing.assert_array_equal( + [(6660.0 + 440.0) * si.um**3 * constants_defaults.rho_w], + frag_mass.to_ndarray(), + ) + + @staticmethod + @pytest.mark.parametrize( + "fragmentation_fn", + [ + Exponential(scale=1.0 * si.cm**3), + Feingold1988(scale=1.0 * si.cm**3), + Gaussian( + mu=1.0 * si.cm**3, + sigma=1e6 * si.um**3, + ), + SLAMS(), + Straub2010Nf(), + pytest.param(AlwaysN(n=0.01), marks=pytest.mark.xfail(strict=True)), + ], + ) + def test_fragmentation_limiters_vmax(fragmentation_fn, backend_class): + # arrange + volume = np.asarray([440.0 * si.um**3, 6660.0 * si.um**3]) + fragments = np.asarray([-1.0]) + + env = Box(dv=None, dt=None) + builder = Builder( + volume.size, + backend_class( + Formulae(fragmentation_function=fragmentation_fn.__class__.__name__) + ), + environment=env, + ) + sut = fragmentation_fn + sut.vmin = 1 * si.um**3 + sut.register(builder) + _ = builder.build( + attributes={"volume": volume, "multiplicity": np.ones_like(volume)} + ) + + _PairwiseStorage = builder.particulator.PairwiseStorage + _Indicator = builder.particulator.PairIndicator + nf = _PairwiseStorage.from_ndarray(np.zeros_like(fragments)) + frag_mass = _PairwiseStorage.from_ndarray(np.zeros_like(fragments)) + is_first_in_pair = _Indicator(length=volume.size) + is_first_in_pair.indicator = builder.particulator.Storage.from_ndarray( + np.asarray([True, False]) + ) + u01 = dummy_u01(builder, fragments.size) + + # act + sut(nf, frag_mass, u01, is_first_in_pair) + + # Assert + np.testing.assert_array_less([0.999], nf.to_ndarray()) + np.testing.assert_array_less( + frag_mass.to_ndarray(), + [(6661.0 + 440.0) * si.um**3 * constants_defaults.rho_w], + ) + + np.testing.assert_approx_equal( + nf[0] * frag_mass[0], np.sum(volume) * constants_defaults.rho_w + ) + + @staticmethod + @pytest.mark.parametrize( + "fragmentation_fn", + [ + Exponential(scale=1.0 * si.um**3, nfmax=2), + Feingold1988(scale=1.0 * si.um**3, nfmax=2), + Gaussian( + mu=1.0 * si.um**3, + sigma=1e6 * si.um**3, + nfmax=2, + ), + SLAMS(nfmax=2), + Straub2010Nf(nfmax=2), + pytest.param(AlwaysN(n=10), marks=pytest.mark.xfail(strict=True)), + ], + ) + def test_fragmentation_limiters_nfmax(fragmentation_fn, backend_class): + # arrange + volume = np.asarray([440.0 * si.um**3, 6660.0 * si.um**3]) + fragments = np.asarray([-1.0]) + + env = Box(dv=None, dt=None) + builder = Builder( + volume.size, + backend_class( + Formulae(fragmentation_function=fragmentation_fn.__class__.__name__) + ), + environment=env, + ) + sut = fragmentation_fn + sut.vmin = 1 * si.um**3 + sut.register(builder) + _ = builder.build( + attributes={"volume": volume, "multiplicity": np.ones_like(volume)} + ) + + _PairwiseStorage = builder.particulator.PairwiseStorage + _Indicator = builder.particulator.PairIndicator + nf = _PairwiseStorage.from_ndarray(np.zeros_like(fragments)) + frag_mass = _PairwiseStorage.from_ndarray(np.zeros_like(fragments)) + is_first_in_pair = _Indicator(length=volume.size) + is_first_in_pair.indicator = builder.particulator.Storage.from_ndarray( + np.asarray([True, False]) + ) + u01 = dummy_u01(builder, fragments.size) + + # act + sut(nf, frag_mass, u01, is_first_in_pair) + + # Assert + np.testing.assert_array_less(nf.to_ndarray(), [2.0 + 1e-6]) + np.testing.assert_array_less( + [((6660.0 + 440.0) / 2 - 1) * si.um**3], frag_mass.to_ndarray() + ) + + np.testing.assert_approx_equal( + nf[0] * frag_mass[0], np.sum(volume) * constants_defaults.rho_w + ) + + @staticmethod + @pytest.mark.parametrize( + "fragmentation_fn", + ( + AlwaysN(n=2), + Exponential(scale=1e6 * si.um**3), + Feingold1988(scale=1e6 * si.um**3), + Gaussian( + mu=2e6 * si.um**3, + sigma=1e6 * si.um**3, + ), + SLAMS(), + Straub2010Nf(), + ), + ) + def test_fragmentation_fn_distribution( + fragmentation_fn, plot=False + ): # pylint: disable=too-many-locals, unnecessary-lambda-assignment + # arrange + + drop_size_L_diam = 0.4 * si.cm + drop_size_S_diam = 0.2 * si.cm + + get_volume_from_diam = lambda d: (4 / 3) * np.pi * (d / 2) ** 3 + + n = 100 + res = np.empty((n, 2), dtype=np.double) + + backend = CPU( + Formulae(fragmentation_function=fragmentation_fn.__class__.__name__) + ) + volume = np.asarray( + [ + get_volume_from_diam(drop_size_S_diam), + get_volume_from_diam(drop_size_L_diam), + ] + ) + fragments = np.asarray([-1.0]) + env = Box(dv=None, dt=None) + builder = Builder(volume.size, backend, environment=env) + sut = fragmentation_fn + sut.vmin = 1 * si.um**3 + sut.register(builder) + _ = builder.build( + attributes={"volume": volume, "multiplicity": np.ones_like(volume)} + ) + + rns = np.linspace(1e-6, 1 - 1e-6, n) + for i, rn in enumerate(rns): + _PairwiseStorage = builder.particulator.PairwiseStorage + _Indicator = builder.particulator.PairIndicator + nf = _PairwiseStorage.from_ndarray( + np.zeros_like(fragments, dtype=np.double) + ) + frag_mass = _PairwiseStorage.from_ndarray( + np.zeros_like(fragments, dtype=np.double) + ) + is_first_in_pair = _Indicator(length=volume.size) + is_first_in_pair.indicator = builder.particulator.Storage.from_ndarray( + np.asarray([True, False]) + ) + u01 = _PairwiseStorage.from_ndarray(np.asarray([rn])) + + # act + sut(nf, frag_mass, u01, is_first_in_pair) + + res[i][0] = nf[0] + res[i][1] = frag_mass[0] + + # Assert + np.testing.assert_array_less([0.99], nf.to_ndarray()) + np.testing.assert_array_less([0.0], frag_mass.to_ndarray()) + + np.testing.assert_approx_equal( + nf[0] * frag_mass[0], np.sum(volume) * constants_defaults.rho_w + ) + + res = np.asarray(sorted(res, key=lambda x: x[1], reverse=True)) + + plt.hist(res[:, 0]) + if plot: + plt.show() + + @staticmethod + @pytest.mark.parametrize( + "fragmentation_fn, water_mass, expected_nf", + ( + ( + ConstantMass(c=4 * si.um**3), + np.asarray( + [ + 400.0 * si.um**3, + 600.0 * si.um**3, + ] + ), + 250, + ), + ( + AlwaysN(n=250), + np.asarray( + [ + 400.0 * si.um**3, + 600.0 * si.um**3, + ] + ), + 250, + ), + ), + ) + def test_fragmentation_nf_and_frag_mass_equals( # TODO #987 + fragmentation_fn, + water_mass, + expected_nf, + backend_class=CPU, + ): + # arrange + expected_frag_mass = np.sum(water_mass) / expected_nf + + fragments = np.asarray([-1.0]) + + env = Box(dv=None, dt=None) + builder = Builder( + water_mass.size, + backend_class( + Formulae(fragmentation_function=fragmentation_fn.__class__.__name__) + ), + environment=env, + ) + sut = fragmentation_fn + sut.vmin = 1 * si.um**3 + sut.register(builder) + _ = builder.build( + attributes={ + "water mass": water_mass, + "multiplicity": np.ones_like(water_mass), + } + ) + + _PairwiseStorage = builder.particulator.PairwiseStorage + _Indicator = builder.particulator.PairIndicator + nf = _PairwiseStorage.from_ndarray(np.zeros_like(fragments)) + frag_mass = _PairwiseStorage.from_ndarray(np.zeros_like(fragments)) + is_first_in_pair = _Indicator(length=water_mass.size) + is_first_in_pair.indicator = builder.particulator.Storage.from_ndarray( + np.asarray([True, False]) + ) + u01 = _PairwiseStorage.from_ndarray(np.ones_like(fragments)) + + # act + sut(nf, frag_mass, u01, is_first_in_pair) + + # Assert + np.testing.assert_array_almost_equal(nf.to_ndarray(), expected_nf) + np.testing.assert_array_almost_equal( + [expected_frag_mass], frag_mass.to_ndarray() + ) diff --git a/PySDM/source/tests/unit_tests/dynamics/collisions/test_kernels.py b/PySDM/source/tests/unit_tests/dynamics/collisions/test_kernels.py new file mode 100644 index 0000000000000000000000000000000000000000..9640defe0e88d0d8baf7c4218589d6758caa1b1e --- /dev/null +++ b/PySDM/source/tests/unit_tests/dynamics/collisions/test_kernels.py @@ -0,0 +1,88 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest + +from PySDM import Builder +from PySDM.backends import CPU +from PySDM.dynamics.collisions.collision_kernels import Golovin, SimpleGeometric +from PySDM.environments import Box +from PySDM.formulae import Formulae + + +class TestKernels: + @staticmethod + @pytest.mark.parametrize( + "x", [pytest.param(5e-10), pytest.param(np.full(10, 5e-10))] + ) + def test_golovin_analytic_solution_underflow(x): + # Arrange + formulae = Formulae() + b = 1.5e3 + x_0 = formulae.trivia.volume(radius=30.531e-6) + N_0 = 2**23 + sut = Golovin(b) + + # Act + value = sut.analytic_solution(x=x, t=1200, x_0=x_0, N_0=N_0) + + # Assert + assert np.all(np.isfinite(value)) + + @staticmethod + @pytest.mark.parametrize("C", (0.0, 1.0)) + def test_simple_geometric(C): + # arrange + volume = np.asarray([44.0, 666.0]) + + env = Box(dv=None, dt=None) + builder = Builder(backend=CPU(), n_sd=volume.size, environment=env) + sut = SimpleGeometric(C=C) + sut.register(builder) + _ = builder.build( + attributes={"volume": volume, "multiplicity": np.ones_like(volume)} + ) + + _PairwiseStorage = builder.particulator.PairwiseStorage + _Indicator = builder.particulator.PairIndicator + output = _PairwiseStorage.from_ndarray(np.zeros_like(volume)) + is_first_in_pair = _Indicator(length=volume.size) + is_first_in_pair.indicator = builder.particulator.Storage.from_ndarray( + np.asarray([True, False]) + ) + # act + sut(output, is_first_in_pair=is_first_in_pair) + + # assert + if C > 0.0: + np.testing.assert_array_less([0.0, 0.0], output.to_ndarray()) + else: + np.testing.assert_array_equal([0.0, 0.0], output.to_ndarray()) + + @staticmethod + @pytest.mark.parametrize("volume", (np.array([1.0, 2.0]), np.array([1.0, 1.0]))) + def test_simple_geometric_same_size(volume): + # arrange + env = Box(dv=None, dt=None) + builder = Builder(backend=CPU(), n_sd=volume.size, environment=env) + sut = SimpleGeometric(C=1.0) + sut.register(builder) + _ = builder.build( + attributes={"volume": volume, "multiplicity": np.ones_like(volume)} + ) + + _PairwiseStorage = builder.particulator.PairwiseStorage + _Indicator = builder.particulator.PairIndicator + output = _PairwiseStorage.from_ndarray(np.zeros_like(volume)) + is_first_in_pair = _Indicator(length=volume.size) + is_first_in_pair.indicator = builder.particulator.Storage.from_ndarray( + np.asarray([True, False]) + ) + + # act + sut(output, is_first_in_pair=is_first_in_pair) + + # assert + if volume[0] == volume[1]: + np.testing.assert_array_equal([0.0, 0.0], output.to_ndarray()) + else: + np.testing.assert_array_less([0.0, 0.0], output.to_ndarray()) diff --git a/PySDM/source/tests/unit_tests/dynamics/collisions/test_sdm_breakup.py b/PySDM/source/tests/unit_tests/dynamics/collisions/test_sdm_breakup.py new file mode 100644 index 0000000000000000000000000000000000000000..9f5a8ccaddc8b43d7ffd134acd087b2ee8a0fd13 --- /dev/null +++ b/PySDM/source/tests/unit_tests/dynamics/collisions/test_sdm_breakup.py @@ -0,0 +1,948 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest + +import PySDM.physics.constants as const +from PySDM import Builder, Formulae +from PySDM.backends import CPU +from PySDM.backends.impl_common.pair_indicator import make_PairIndicator +from PySDM.dynamics import Breakup +from PySDM.dynamics.collisions import breakup_fragmentations +from PySDM.dynamics.collisions.breakup_efficiencies import ConstEb +from PySDM.dynamics.collisions.breakup_fragmentations import AlwaysN +from PySDM.dynamics.collisions.coalescence_efficiencies import ConstEc +from PySDM.dynamics.collisions.collision import DEFAULTS, Collision +from PySDM.dynamics.collisions.collision_kernels import ConstantK, Geometric +from PySDM.environments import Box +from PySDM.initialisation import spectra +from PySDM.initialisation.sampling.spectral_sampling import ConstantMultiplicity +from PySDM.physics import si +from PySDM.physics.trivia import Trivia +from PySDM.products.size_spectral import ParticleVolumeVersusRadiusLogarithmSpectrum + + +def volume_to_mass(particulator, volume): + if isinstance(volume, (list, tuple)): + return list( + map(particulator.formulae.particle_shape_and_density.volume_to_mass, volume) + ) + + raise NotImplementedError + + +class TestSDMBreakup: + @staticmethod + @pytest.mark.parametrize( + "dt", + [ + 1 * si.s, + 10 * si.s, + ], + ) + def test_nonadaptive_same_results_regardless_of_dt(dt, backend_class): + # Arrange + attributes = { + "multiplicity": np.asarray([1, 1]), + "volume": np.asarray([100 * si.um**3, 100 * si.um**3]), + } + breakup = Breakup( + collision_kernel=ConstantK(1 * si.cm**3 / si.s), + fragmentation_function=AlwaysN(4), + adaptive=False, + warn_overflows=False, + ) + nsteps = 10 + + n_sd = len(attributes["multiplicity"]) + env = Box(dv=1 * si.cm**3, dt=dt) + builder = Builder( + n_sd, + backend_class(Formulae(fragmentation_function="AlwaysN")), + environment=env, + ) + builder.add_dynamic(breakup) + particulator = builder.build(attributes=attributes, products=()) + + # Act + particulator.run(nsteps) + + # Assert + assert (particulator.attributes["multiplicity"].to_ndarray() > 0).all() + assert ( + particulator.attributes["multiplicity"].to_ndarray() + != attributes["multiplicity"] + ).any() + assert np.sum(particulator.attributes["multiplicity"].to_ndarray()) >= np.sum( + attributes["multiplicity"] + ) + assert ( + particulator.attributes["multiplicity"].to_ndarray() + == np.array([1024, 1024]) + ).all() + + @staticmethod + @pytest.mark.parametrize( + "params", + [ + { + "gamma": 1.0, + "rand": 1.0, + }, + {"gamma": 1.0, "rand": 0.1}, + pytest.param( + {"gamma": 1.0, "rand": 0.0}, marks=pytest.mark.xfail(strict=True) + ), + ], + ) + def test_single_collision_bounce(params, backend_instance): + # Arrange + backend = backend_instance + n_sd = 2 + env = Box(dv=np.NaN, dt=np.NaN) + builder = Builder(n_sd, backend, environment=env) + n_init = [6, 6] + particulator = builder.build( + attributes={ + "multiplicity": np.asarray(n_init), + "volume": np.asarray([100 * si.um**3, 100 * si.um**3]), + }, + products=(), + ) + + pairwise_zeros = particulator.PairwiseStorage.from_ndarray(np.array([0.0])) + general_zeros = particulator.Storage.from_ndarray(np.array([0])) + + gamma = particulator.PairwiseStorage.from_ndarray(np.asarray([params["gamma"]])) + rand = particulator.PairwiseStorage.from_ndarray(np.asarray([params["rand"]])) + fragment_mass = particulator.PairwiseStorage.from_ndarray( + np.array([50 * si.um**3], dtype=float) + ) + is_first_in_pair = make_PairIndicator(backend)(n_sd) + + # Act + particulator.collision_coalescence_breakup( + enable_breakup=True, + gamma=gamma, + rand=rand, + Ec=pairwise_zeros, + Eb=pairwise_zeros, + fragment_mass=fragment_mass, + coalescence_rate=general_zeros, + breakup_rate=general_zeros, + breakup_rate_deficit=general_zeros, + is_first_in_pair=is_first_in_pair, + warn_overflows=False, + max_multiplicity=DEFAULTS.max_multiplicity, + ) + + # Assert + assert (particulator.attributes["multiplicity"].to_ndarray() == n_init).all() + + @staticmethod + @pytest.mark.parametrize( + "params", + [ + { + "gamma": 1.0, + "rand": 1.0, + "Eb": 1.0, + "n_init": [1, 1], + "is_first_in_pair": [True, False], + }, + { + "gamma": 1.0, + "rand": 1.0, + "Eb": 1.0, + "n_init": [2, 1], + "is_first_in_pair": [True, False], + }, + { + "gamma": 1.0, + "rand": 1.0, + "Eb": 1.0, + "n_init": [2, 1, 2], + "is_first_in_pair": [True, False, False], + }, + { + "gamma": 1.0, + "rand": 1.0, + "Eb": 1.0, + "n_init": [2, 1, 2, 1], + "is_first_in_pair": [True, False, True, False], + }, + ], + ) + def test_breakup_counters( + params, backend_instance + ): # pylint: disable=too-many-locals + # Arrange + n_init = params["n_init"] + n_sd = len(n_init) + env = Box(dv=np.NaN, dt=np.NaN) + builder = Builder(n_sd, backend_instance, environment=env) + particulator = builder.build( + attributes={ + "multiplicity": np.asarray(n_init), + "volume": np.asarray([100 * si.um**3] * n_sd), + }, + products=(), + ) + + n_pairs = n_sd // 2 + pairwise_zeros = particulator.PairwiseStorage.from_ndarray( + np.array([0.0] * n_pairs) + ) + general_zeros = particulator.Storage.from_ndarray(np.array([0] * n_sd)) + + gamma = particulator.PairwiseStorage.from_ndarray( + np.array([params["gamma"]] * n_pairs) + ) + rand = particulator.PairwiseStorage.from_ndarray( + np.array([params["rand"]] * n_pairs) + ) + Eb = particulator.PairwiseStorage.from_ndarray( + np.array([params["Eb"]] * n_pairs) + ) + breakup_rate = particulator.Storage.from_ndarray(np.array([0])) + frag_mass = particulator.PairwiseStorage.from_ndarray( + np.array(volume_to_mass(particulator, [2 * si.m**3]) * n_pairs) + ) + is_first_in_pair = particulator.PairIndicator(n_sd) + is_first_in_pair.indicator[:] = particulator.Storage.from_ndarray( + np.asarray(params["is_first_in_pair"]) + ) + + # Act + particulator.collision_coalescence_breakup( + enable_breakup=True, + gamma=gamma, + rand=rand, + Ec=pairwise_zeros, + Eb=Eb, + fragment_mass=frag_mass, + coalescence_rate=general_zeros, + breakup_rate=breakup_rate, + breakup_rate_deficit=general_zeros, + is_first_in_pair=is_first_in_pair, + warn_overflows=False, + max_multiplicity=DEFAULTS.max_multiplicity, + ) + + # Assert + cell_id = 0 + assert breakup_rate.to_ndarray()[cell_id] == np.sum( + params["gamma"] * get_smaller_of_pairs(is_first_in_pair, n_init) + ) + + @staticmethod + @pytest.mark.parametrize( + "params", + [ + { + "gamma": [1.0], + "n_init": [1, 1], + "v_init": [1, 1], + "n_expected": [2, 2], + "v_expected": [0.5, 0.5], + "expected_deficit": [0.0], + "is_first_in_pair": [True, False], + "frag_volume": [0.5], + }, + { + "gamma": [2.0], + "n_init": [20, 4], + "v_init": [1, 2], + "n_expected": [4, 24], + "v_expected": [1, 1], + "expected_deficit": [0.0], + "is_first_in_pair": [True, False], + "frag_volume": [1.0], + }, + { + "gamma": [2.0], + "n_init": [1, 1], + "v_init": [1, 1], + "n_expected": [2, 2], + "v_expected": [0.5, 0.5], + "expected_deficit": [1.0], + "is_first_in_pair": [True, False], + "frag_volume": [0.5], + }, + { + "gamma": [2.0], + "n_init": [3, 1], + "v_init": [1, 1], + "n_expected": [2, 4], + "v_expected": [1.0, 0.5], + "expected_deficit": [1.0], + "is_first_in_pair": [True, False], + "frag_volume": [0.5], + }, + { + "gamma": [2.0], + "n_init": [9, 2], + "v_init": [1, 2], + "n_expected": [1, 12], + "v_expected": [1, 1], + "expected_deficit": [0.0], + "is_first_in_pair": [True, False], + "frag_volume": [1.0], + }, + { + "gamma": [1.0], + "n_init": [12, 1], + "v_init": [1, 1], + "n_expected": [11, 2], + "v_expected": [1, 1], + "expected_deficit": [0.0], + "is_first_in_pair": [True, False], + "frag_volume": [1.0], + }, + { + "gamma": [1.0], + "n_init": [15, 2], + "v_init": [2, 6], + "n_expected": [13, 4], + "v_expected": [2, 4], + "expected_deficit": [0.0], + "is_first_in_pair": [True, False], + "frag_volume": [4], + }, + { + "gamma": [1.0], + "n_init": [13, 4], + "v_init": [2, 4], + "n_expected": [9, 6], + "v_expected": [2, 4], + "expected_deficit": [0.0], + "is_first_in_pair": [True, False], + "frag_volume": [4], + }, + { + "gamma": [3.0], + "n_init": [15, 2], + "v_init": [2, 6], + "n_expected": [3, 9], + "v_expected": [2, 4], + "expected_deficit": [0.0], + "is_first_in_pair": [True, False], + "frag_volume": [4], + }, + { + "gamma": [0.0], + "n_init": [15, 2], + "v_init": [2, 6], + "n_expected": [15, 2], + "v_expected": [2, 6], + "expected_deficit": [0.0], + "is_first_in_pair": [True, False], + "frag_volume": [4], + }, + ], + ) + @pytest.mark.parametrize("flag", ("multiplicity", "v", "conserve", "deficit")) + def test_attribute_update_single_breakup( + params, flag, backend_class + ): # pylint: disable=too-many-locals + # Arrange + n_init = params["n_init"] + n_sd = len(n_init) + env = Box(dv=np.NaN, dt=np.NaN) + builder = Builder(n_sd, backend_class(double_precision=True), environment=env) + particulator = builder.build( + attributes={ + "multiplicity": np.asarray(n_init), + "volume": np.asarray(params["v_init"]), + }, + products=(), + ) + + n_pairs = n_sd // 2 + rand = [1.0] * n_pairs + Eb = [1.0] * n_pairs + pairwise_zeros = particulator.PairwiseStorage.from_ndarray( + np.array([0.0] * n_pairs) + ) + general_zeros = particulator.Storage.from_ndarray(np.array([0] * n_sd)) + + gamma = particulator.PairwiseStorage.from_ndarray(np.array(params["gamma"])) + rand = particulator.PairwiseStorage.from_ndarray(np.array(rand)) + Eb = particulator.PairwiseStorage.from_ndarray(np.array(Eb)) + breakup_rate = particulator.Storage.from_ndarray(np.array([0])) + breakup_rate_deficit = particulator.Storage.from_ndarray(np.array([0])) + + frag_mass = volume_to_mass(particulator, params["frag_volume"]) + frag_mass = particulator.PairwiseStorage.from_ndarray( + np.asarray(frag_mass, dtype=float) + ) + is_first_in_pair = particulator.PairIndicator(n_sd) + is_first_in_pair.indicator[:] = particulator.Storage.from_ndarray( + np.asarray(params["is_first_in_pair"], dtype=bool) + ) + + # Act + particulator.collision_coalescence_breakup( + enable_breakup=True, + gamma=gamma, + rand=rand, + Ec=pairwise_zeros, + Eb=Eb, + fragment_mass=frag_mass, + coalescence_rate=general_zeros, + breakup_rate=breakup_rate, + breakup_rate_deficit=breakup_rate_deficit, + is_first_in_pair=is_first_in_pair, + warn_overflows=False, + max_multiplicity=DEFAULTS.max_multiplicity, + ) + + # Assert + { + "multiplicity": lambda: np.testing.assert_array_equal( + particulator.attributes["multiplicity"].to_ndarray(), + np.array(params["n_expected"]), + ), + "v": lambda: np.testing.assert_array_almost_equal( + particulator.attributes["volume"].to_ndarray(), + np.array(params["v_expected"]), + ), + "conserve": lambda: np.testing.assert_almost_equal( + np.sum( + particulator.attributes["multiplicity"].to_ndarray() + * particulator.attributes["volume"].to_ndarray() + ), + np.sum(np.array(params["n_init"]) * np.array(params["v_init"])), + ), + "deficit": lambda: np.testing.assert_almost_equal( + breakup_rate_deficit.to_ndarray(), np.array(params["expected_deficit"]) + ), + }[flag]() + + @staticmethod + @pytest.mark.parametrize("_n", [1, 2, 3, 4, 5]) + @pytest.mark.parametrize( + "params", + [ + { + "n_init": [64, 2], + "v_init": [128, 128], + "is_first_in_pair": [True, False], + "frag_volume": [128], + }, + { + "n_init": [20, 4], + "v_init": [1, 2], + "is_first_in_pair": [True, False], + "frag_volume": [1.0], + }, + { + "n_init": [3, 1], + "v_init": [1, 1], + "is_first_in_pair": [True, False], + "frag_volume": [0.5], + }, + { + "n_init": [64, 2], + "v_init": [8, 16], + "is_first_in_pair": [True, False], + "n_fragment": [6], + "frag_volume": [4.0], + }, + { + "n_init": [64, 2], + "v_init": [6, 2], + "is_first_in_pair": [True, False], + "n_fragment": [2], + "frag_volume": [4.0], + }, + ], + ) + def test_attribute_update_n_breakups( + _n, params, backend_class=CPU + ): # pylint: disable=too-many-locals + # Arrange + + assert len(params["frag_volume"]) == 1 + + def run_simulation(_n_times, _gamma): + n_init = params["n_init"] + n_sd = len(n_init) + env = Box(dv=np.NaN, dt=np.NaN) + builder = Builder(n_sd, backend_class(), environment=env) + particulator = builder.build( + attributes={ + "multiplicity": np.asarray(n_init), + "volume": np.asarray(params["v_init"]), + }, + products=(), + ) + + n_pairs = n_sd // 2 + rand = [1.0] * n_pairs + Eb = [1.0] * n_pairs + pairwise_zeros = particulator.PairwiseStorage.from_ndarray( + np.array([0.0] * n_pairs) + ) + general_zeros = particulator.Storage.from_ndarray(np.array([0] * n_sd)) + + gamma = particulator.PairwiseStorage.from_ndarray(np.array(_gamma)) + rand = particulator.PairwiseStorage.from_ndarray(np.array(rand)) + Eb = particulator.PairwiseStorage.from_ndarray(np.array(Eb)) + breakup_rate = particulator.Storage.from_ndarray(np.array([0])) + breakup_rate_deficit = particulator.Storage.from_ndarray(np.array([0])) + frag_mass = particulator.PairwiseStorage.from_ndarray( + np.asarray( + volume_to_mass(particulator, params["frag_volume"]), dtype=float + ) + ) + is_first_in_pair = particulator.PairIndicator(n_sd) + is_first_in_pair.indicator[:] = particulator.Storage.from_ndarray( + np.asarray(params["is_first_in_pair"], dtype=bool) + ) + + # Act + for _ in range(_n_times): + particulator.collision_coalescence_breakup( + enable_breakup=True, + gamma=gamma, + rand=rand, + Ec=pairwise_zeros, + Eb=Eb, + fragment_mass=frag_mass, + coalescence_rate=general_zeros, + breakup_rate=breakup_rate, + breakup_rate_deficit=breakup_rate_deficit, + is_first_in_pair=is_first_in_pair, + warn_overflows=False, + max_multiplicity=DEFAULTS.max_multiplicity, + ) + + res_mult = particulator.attributes["multiplicity"].to_ndarray() + res_volume = particulator.attributes["volume"].to_ndarray() + return res_mult, res_volume + + run1 = run_simulation(_n_times=1, _gamma=[_n]) + run2 = run_simulation(_n_times=_n, _gamma=[1]) + + # Assert + np.testing.assert_array_almost_equal( + run1[0], + run2[0], + ) + np.testing.assert_array_almost_equal( + run1[1], + run2[1], + ) + + @staticmethod + def test_multiplicity_overflow(backend=CPU()): # pylint: disable=too-many-locals + # Arrange + params = { + "gamma": [1.0], + "n_init": [1, 3], + "v_init": [1, 1], + "is_first_in_pair": [True, False], + "frag_volume": [2e-10], + } + n_init = params["n_init"] + n_sd = len(n_init) + env = Box(dv=np.NaN, dt=np.NaN) + builder = Builder(n_sd, backend, environment=env) + particulator = builder.build( + attributes={ + "multiplicity": np.asarray(n_init), + "volume": np.asarray(params["v_init"]), + }, + products=(), + ) + + n_pairs = n_sd // 2 + rand = [1.0] * n_pairs + Eb = [1.0] * n_pairs + pairwise_zeros = particulator.PairwiseStorage.from_ndarray( + np.array([0.0] * n_pairs) + ) + general_zeros = particulator.Storage.from_ndarray(np.array([0] * n_sd)) + + gamma = particulator.PairwiseStorage.from_ndarray(np.array(params["gamma"])) + rand = particulator.PairwiseStorage.from_ndarray(np.array(rand)) + Eb = particulator.PairwiseStorage.from_ndarray(np.array(Eb)) + breakup_rate = particulator.Storage.from_ndarray(np.array([0])) + breakup_rate_deficit = particulator.Storage.from_ndarray(np.array([0])) + frag_mass = particulator.PairwiseStorage.from_ndarray( + np.array(volume_to_mass(particulator, params["frag_volume"]), dtype=float) + ) + is_first_in_pair = particulator.PairIndicator(n_sd) + is_first_in_pair.indicator[:] = particulator.Storage.from_ndarray( + np.asarray(params["is_first_in_pair"]) + ) + + # Act + particulator.collision_coalescence_breakup( + enable_breakup=True, + gamma=gamma, + rand=rand, + Ec=pairwise_zeros, + Eb=Eb, + fragment_mass=frag_mass, + coalescence_rate=general_zeros, + breakup_rate=breakup_rate, + breakup_rate_deficit=breakup_rate_deficit, + is_first_in_pair=is_first_in_pair, + warn_overflows=True, + max_multiplicity=DEFAULTS.max_multiplicity, + ) + assert breakup_rate_deficit[0] > 0 + np.testing.assert_almost_equal( + np.sum( + particulator.attributes["multiplicity"].to_ndarray() + * particulator.attributes["volume"].to_ndarray() + ), + np.sum(np.array(params["n_init"]) * np.array(params["v_init"])), + ) + + @staticmethod + def test_same_multiplicity_overflow_no_substeps( + backend=CPU(), + ): # pylint: disable=too-many-locals + # Arrange + params = { + "gamma": [46.0], + "n_init": [1, 1], + "v_init": [1, 1], + "is_first_in_pair": [True, False], + "frag_volume": [0.5], + } + n_init = params["n_init"] + n_sd = len(n_init) + env = Box(dv=np.NaN, dt=np.NaN) + builder = Builder(n_sd, backend, environment=env) + particulator = builder.build( + attributes={ + "multiplicity": np.asarray(n_init), + "volume": np.asarray(params["v_init"]), + }, + products=(), + ) + + n_pairs = n_sd // 2 + rand = [1.0] * n_pairs + Eb = [1.0] * n_pairs + pairwise_zeros = particulator.PairwiseStorage.from_ndarray( + np.array([0.0] * n_pairs) + ) + general_zeros = particulator.Storage.from_ndarray(np.array([0] * n_sd)) + + gamma = particulator.PairwiseStorage.from_ndarray(np.array(params["gamma"])) + rand = particulator.PairwiseStorage.from_ndarray(np.array(rand)) + Eb = particulator.PairwiseStorage.from_ndarray(np.array(Eb)) + breakup_rate = particulator.Storage.from_ndarray(np.array([0])) + breakup_rate_deficit = particulator.Storage.from_ndarray(np.array([0])) + frag_mass = particulator.PairwiseStorage.from_ndarray( + np.array(volume_to_mass(particulator, params["frag_volume"]), dtype=float) + ) + is_first_in_pair = particulator.PairIndicator(n_sd) + is_first_in_pair.indicator[:] = particulator.Storage.from_ndarray( + np.asarray(params["is_first_in_pair"]) + ) + + # Act + particulator.collision_coalescence_breakup( + enable_breakup=True, + gamma=gamma, + rand=rand, + Ec=pairwise_zeros, + Eb=Eb, + fragment_mass=frag_mass, + coalescence_rate=general_zeros, + breakup_rate=breakup_rate, + breakup_rate_deficit=breakup_rate_deficit, + is_first_in_pair=is_first_in_pair, + warn_overflows=True, + max_multiplicity=DEFAULTS.max_multiplicity, + ) + assert breakup_rate_deficit[0] > 0 + np.testing.assert_equal( + np.sum( + particulator.attributes["multiplicity"].to_ndarray() + * particulator.attributes["volume"].to_ndarray() + ), + np.sum(np.array(params["n_init"]) * np.array(params["v_init"])), + ) + + @staticmethod + @pytest.mark.parametrize( + "params", + [ + { + "gamma": [1.0], + "n_init": [1, 1], + "v_init": [1, 1], + "n_expected": [1, 1], + "v_expected": [1, 1], + "expected_deficit": [0.0], + "is_first_in_pair": [True, False], + "frag_volume": [1.25], + }, + { + "gamma": [1.0], + "n_init": [1, 1], + "v_init": [1, 1], + "n_expected": [1, 1], + "v_expected": [1, 1], + "expected_deficit": [0.0], + "is_first_in_pair": [True, False], + "frag_volume": [1 / 1.3], + }, + { + "gamma": [2.0], + "n_init": [2, 1], + "v_init": [1, 1], + "n_expected": [1, 3], + "v_expected": [1, 2 / 3], + "expected_deficit": [1.0], + "is_first_in_pair": [True, False], + "frag_volume": [1 / 1.4], + }, + ], + ) + @pytest.mark.parametrize("flag", ("multiplicity", "v", "conserve", "deficit")) + def test_noninteger_fragments( + params, flag, backend_instance + ): # pylint: disable=too-many-locals + # Arrange + n_init = params["n_init"] + n_sd = len(n_init) + env = Box(dv=np.NaN, dt=np.NaN) + builder = Builder(n_sd, backend_instance, environment=env) + particulator = builder.build( + attributes={ + "multiplicity": np.asarray(n_init), + "volume": np.asarray(params["v_init"]), + }, + products=(), + ) + + n_pairs = n_sd // 2 + rand = [1.0] * n_pairs + Eb = [1.0] * n_pairs + pairwise_zeros = particulator.PairwiseStorage.from_ndarray( + np.array([0.0] * n_pairs) + ) + general_zeros = particulator.Storage.from_ndarray(np.array([0] * n_sd)) + + gamma = particulator.PairwiseStorage.from_ndarray(np.array(params["gamma"])) + rand = particulator.PairwiseStorage.from_ndarray(np.array(rand)) + Eb = particulator.PairwiseStorage.from_ndarray(np.array(Eb)) + breakup_rate = particulator.Storage.from_ndarray(np.array([0])) + breakup_rate_deficit = particulator.Storage.from_ndarray(np.array([0])) + frag_mass = particulator.PairwiseStorage.from_ndarray( + np.asarray(volume_to_mass(particulator, params["frag_volume"]), dtype=float) + ) + is_first_in_pair = particulator.PairIndicator(n_sd) + is_first_in_pair.indicator[:] = particulator.Storage.from_ndarray( + np.asarray(params["is_first_in_pair"], dtype=bool) + ) + + # Act + particulator.collision_coalescence_breakup( + enable_breakup=True, + gamma=gamma, + rand=rand, + Ec=pairwise_zeros, + Eb=Eb, + fragment_mass=frag_mass, + coalescence_rate=general_zeros, + breakup_rate=breakup_rate, + breakup_rate_deficit=breakup_rate_deficit, + is_first_in_pair=is_first_in_pair, + warn_overflows=False, + max_multiplicity=DEFAULTS.max_multiplicity, + ) + + # Assert + { + "multiplicity": lambda: np.testing.assert_array_equal( + particulator.attributes["multiplicity"].to_ndarray(), + np.array(params["n_expected"]), + ), + "v": lambda: np.testing.assert_array_almost_equal( + particulator.attributes["volume"].to_ndarray(), + np.array(params["v_expected"]), + decimal=6, + ), + "conserve": lambda: np.testing.assert_almost_equal( + np.sum( + particulator.attributes["multiplicity"].to_ndarray() + * particulator.attributes["volume"].to_ndarray() + ), + np.sum(np.array(params["n_init"]) * np.array(params["v_init"])), + decimal=6, + ), + "deficit": lambda: np.testing.assert_equal( + breakup_rate_deficit.to_ndarray(), np.array(params["expected_deficit"]) + ), + }[flag]() + + @staticmethod + def test_nonnegative_even_if_overflow( + backend=CPU(), + ): # pylint: disable=too-many-locals + n_sd = 2**5 + + dv = 1 * si.m**3 + dt = 1 * si.s + env = Box(dv=dv, dt=dt) + builder = Builder(n_sd=n_sd, backend=backend, environment=env) + + norm_factor = 100 / si.cm**3 * si.m**3 + X0 = Trivia.volume(const, radius=30.531 * si.micrometres) + spectrum = spectra.Exponential(norm_factor=norm_factor, scale=X0) + attributes = {} + attributes["volume"], attributes["multiplicity"] = ConstantMultiplicity( + spectrum + ).sample_deterministic(n_sd) + + mu = Trivia.volume(const, radius=100 * si.um) + fragmentation = breakup_fragmentations.Exponential(scale=mu) + kernel = Geometric() + coal_eff = ConstEc(Ec=0.01) + break_eff = ConstEb(Eb=1.0) + breakup = Collision( + collision_kernel=kernel, + breakup_efficiency=break_eff, + coalescence_efficiency=coal_eff, + fragmentation_function=fragmentation, + warn_overflows=False, + ) + builder.add_dynamic(breakup) + + radius_bins_edges = np.logspace( + np.log10(0.01 * si.um), np.log10(5000 * si.um), num=64, endpoint=True + ) + products = ( + ParticleVolumeVersusRadiusLogarithmSpectrum( + radius_bins_edges=radius_bins_edges, name="dv/dlnr" + ), + ) + particulator = builder.build(attributes, products) + particulator.environment["rhod"] = 1.0 + + t_end = 100 + particulator.run(t_end - particulator.n_steps) + + assert (particulator.attributes["multiplicity"].to_ndarray() > 0).all() + + @staticmethod + @pytest.mark.parametrize( + "params", + [ + { + "gamma": [2.0], + "n_init": [1, 1], + "v_init": [1, 1], + "n_expected": [2, 2], + "v_expected": [0.5, 0.5], + "expected_deficit": [0.0], + "is_first_in_pair": [True, False], + "frag_volume": [0.5], + }, + { + "gamma": [3.0], + "n_init": [9, 2], + "v_init": [1, 2], + "n_expected": [2, 11], + "v_expected": [1, 1], + "expected_deficit": [0.0], + "is_first_in_pair": [True, False], + "frag_volume": [1.0], + }, + ], + ) + @pytest.mark.parametrize("flag", ("multiplicity", "v", "conserve", "deficit")) + def test_while_loop_multi_breakup( + params, flag, backend_class=CPU + ): # pylint:disable=too-many-locals + # Arrange + n_init = params["n_init"] + n_sd = len(n_init) + env = Box(dv=np.NaN, dt=np.NaN) + builder = Builder( + n_sd, backend_class(Formulae(handle_all_breakups=True)), environment=env + ) + particulator = builder.build( + attributes={ + "multiplicity": np.asarray(n_init), + "volume": np.asarray(params["v_init"]), + }, + products=(), + ) + + n_pairs = n_sd // 2 + rand = [1.0] * n_pairs + Eb = [1.0] * n_pairs + pairwise_zeros = particulator.PairwiseStorage.from_ndarray( + np.array([0.0] * n_pairs) + ) + general_zeros = particulator.Storage.from_ndarray(np.array([0] * n_sd)) + + gamma = particulator.PairwiseStorage.from_ndarray(np.array(params["gamma"])) + rand = particulator.PairwiseStorage.from_ndarray(np.array(rand)) + Eb = particulator.PairwiseStorage.from_ndarray(np.array(Eb)) + breakup_rate = particulator.Storage.from_ndarray(np.array([0])) + breakup_rate_deficit = particulator.Storage.from_ndarray(np.array([0])) + + frag_mass = volume_to_mass(particulator, params["frag_volume"]) + frag_mass = particulator.PairwiseStorage.from_ndarray( + np.array(frag_mass, dtype=float) + ) + is_first_in_pair = particulator.PairIndicator(n_sd) + is_first_in_pair.indicator[:] = particulator.Storage.from_ndarray( + np.asarray(params["is_first_in_pair"]) + ) + + # Act + particulator.collision_coalescence_breakup( + enable_breakup=True, + gamma=gamma, + rand=rand, + Ec=pairwise_zeros, + Eb=Eb, + fragment_mass=frag_mass, + coalescence_rate=general_zeros, + breakup_rate=breakup_rate, + breakup_rate_deficit=breakup_rate_deficit, + is_first_in_pair=is_first_in_pair, + warn_overflows=True, + max_multiplicity=DEFAULTS.max_multiplicity, + ) + + # Assert + { + "multiplicity": lambda: np.testing.assert_array_equal( + particulator.attributes["multiplicity"].to_ndarray(), + np.array(params["n_expected"]), + ), + "v": lambda: np.testing.assert_array_almost_equal( + particulator.attributes["volume"].to_ndarray(), + np.array(params["v_expected"]), + decimal=6, + ), + "conserve": lambda: np.testing.assert_almost_equal( + np.sum( + particulator.attributes["multiplicity"].to_ndarray() + * particulator.attributes["volume"].to_ndarray() + ), + np.sum(np.array(params["n_init"]) * np.array(params["v_init"])), + decimal=6, + ), + "deficit": lambda: np.testing.assert_equal( + breakup_rate_deficit.to_ndarray(), np.array(params["expected_deficit"]) + ), + }[flag]() + + +def get_smaller_of_pairs(is_first_in_pair, n_init): + return np.where( + np.roll(is_first_in_pair.indicator, shift=1), np.asarray(n_init), 0.0 + ) diff --git a/PySDM/source/tests/unit_tests/dynamics/collisions/test_sdm_multi_cell.py b/PySDM/source/tests/unit_tests/dynamics/collisions/test_sdm_multi_cell.py new file mode 100644 index 0000000000000000000000000000000000000000..2bd4c47dad675b6877ee1ab4657b92ad9ea6e6fe --- /dev/null +++ b/PySDM/source/tests/unit_tests/dynamics/collisions/test_sdm_multi_cell.py @@ -0,0 +1,45 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest + +from PySDM.backends import ThrustRTC +from PySDM.dynamics.collisions.collision import DEFAULTS +from PySDM.environments import Box +from PySDM.impl.mesh import Mesh +from PySDM.initialisation.sampling.spatial_sampling import Pseudorandom + +from .conftest import get_dummy_particulator_and_coalescence + + +class TestSDMMultiCell: # pylint: disable=too-few-public-methods + @staticmethod + @pytest.mark.parametrize("n_sd", [2, 3, 8000]) + @pytest.mark.parametrize("adaptive", [False, True]) + def test_coalescence_call(n_sd, backend_class, adaptive): + if isinstance(backend_class(), ThrustRTC): + pytest.skip("TODO #330") + + # Arrange + n = np.ones(n_sd) + v = np.ones_like(n) + env = Box(dv=1, dt=DEFAULTS.dt_coal_range[1]) + grid = (25, 25) + env.mesh = Mesh(grid, size=grid) + particulator, sut = get_dummy_particulator_and_coalescence( + backend_class, len(n), environment=env + ) + cell_id, _, _ = env.mesh.cellular_attributes( + Pseudorandom.sample(backend=particulator.backend, grid=grid, n_sd=len(n)) + ) + attributes = {"multiplicity": n, "volume": v, "cell id": cell_id} + particulator.build(attributes) + sut.actual_length = particulator.attributes._ParticleAttributes__idx.length + sut.adaptive = adaptive + + # Act + sut() + + # Assert + np.testing.assert_array_equal( + cell_id, particulator.attributes["cell id"].to_ndarray(raw=True) + ) diff --git a/PySDM/source/tests/unit_tests/dynamics/collisions/test_sdm_single_cell.py b/PySDM/source/tests/unit_tests/dynamics/collisions/test_sdm_single_cell.py new file mode 100644 index 0000000000000000000000000000000000000000..6b9ed7d67188112642ca061118ecc4a92d8a5fe6 --- /dev/null +++ b/PySDM/source/tests/unit_tests/dynamics/collisions/test_sdm_single_cell.py @@ -0,0 +1,311 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest + +from PySDM.backends import ThrustRTC +from PySDM.backends.impl_common.index import make_Index +from PySDM.backends.impl_common.indexed_storage import make_IndexedStorage +from PySDM.backends.impl_common.pair_indicator import make_PairIndicator +from PySDM.dynamics import Coalescence +from PySDM.physics import constants_defaults + +from .conftest import backend_fill, get_dummy_particulator_and_coalescence + + +class TestSDMSingleCell: + @staticmethod + def test_single_collision(backend_class, v_2, T_2, n_2): + # Arrange + const = 1.0 + particulator, sut = get_dummy_particulator_and_coalescence( + backend_class, len(n_2) + ) + sut.compute_gamma = lambda prob, rand, is_first_in_pair, out: backend_fill( + out, 1 + ) + attributes = { + "multiplicity": n_2, + "volume": v_2, + "heat": const * T_2 * v_2, + } + particulator.request_attribute("temperature") + particulator.build(attributes) + + # Act + sut() + + # Assert + particles = particulator.attributes + np.testing.assert_approx_equal( + const + * np.sum( + particles["multiplicity"].to_ndarray() + * particles["volume"].to_ndarray() + * particles["temperature"].to_ndarray() + ), + const * np.sum(n_2 * T_2 * v_2), + significant=7, + ) + new_T = np.sum(T_2 * v_2) / np.sum(v_2) + assert np.isin( + round(new_T, 7), + np.round(particles["temperature"].to_ndarray().astype(float), 7), + ) + + np.testing.assert_approx_equal( + np.sum( + particles["multiplicity"].to_ndarray() + * particles["volume"].to_ndarray() + ), + np.sum(n_2 * v_2), + ) + assert np.sum(particulator.attributes["multiplicity"].to_ndarray()) == np.sum( + n_2 + ) - np.amin(n_2) + if np.amin(n_2) > 0: + np.testing.assert_approx_equal( + np.amax(particulator.attributes["volume"].to_ndarray()), np.sum(v_2) + ) + assert np.amax(particulator.attributes["multiplicity"].to_ndarray()) == max( + np.amax(n_2) - np.amin(n_2), np.amin(n_2) + ) + + @staticmethod + @pytest.mark.parametrize( + "n_in, n_out", + [ + pytest.param(1, np.array([1, 0])), + pytest.param(2, np.array([1, 1])), + pytest.param(3, np.array([2, 1])), + ], + ) + def test_single_collision_same_n(backend_class, n_in, n_out): + # Arrange + particulator, sut = get_dummy_particulator_and_coalescence(backend_class, 2) + sut.compute_gamma = lambda prob, rand, is_first_in_pair, out: backend_fill( + out, 1 + ) + attributes = {"multiplicity": np.full(2, n_in), "volume": np.full(2, 1.0)} + particulator.build(attributes) + + # Act + sut() + + # Assert + np.testing.assert_array_equal( + sorted(particulator.attributes["multiplicity"].to_ndarray(raw=True)), + sorted(n_out), + ) + + @staticmethod + @pytest.mark.parametrize( + "p", + [ + pytest.param(2), + pytest.param(4), + pytest.param(5), + pytest.param(7), + ], + ) + def test_multi_collision(backend_class, v_2, n_2, p): + # Arrange + particulator, sut = get_dummy_particulator_and_coalescence( + backend_class, len(n_2) + ) + + def _compute_gamma(prob, rand, is_first_in_pair, out): + backend_fill(prob, p) + Coalescence.compute_gamma(sut, prob, rand, is_first_in_pair, out=out) + + sut.compute_gamma = _compute_gamma + + attributes = {"multiplicity": n_2, "volume": v_2} + particulator.build(attributes) + + # Act + sut() + + # Assert + state = particulator.attributes + gamma = min(p, max(n_2[0] // n_2[1], n_2[1] // n_2[1])) + assert np.amin(state["multiplicity"]) >= 0 + np.testing.assert_approx_equal( + np.sum( + state["multiplicity"].to_ndarray() * state["water mass"].to_ndarray() + ), + np.sum(n_2 * v_2 * constants_defaults.rho_w), + ) + np.testing.assert_approx_equal( + np.sum(state["multiplicity"].to_ndarray() * state["volume"].to_ndarray()), + np.sum(n_2 * v_2), + ) + assert np.sum(state["multiplicity"].to_ndarray()) == np.sum( + n_2 + ) - gamma * np.amin(n_2) + np.testing.assert_approx_equal( + np.amax(state["volume"].to_ndarray()), + gamma * v_2[np.argmax(n_2)] + v_2[np.argmax(n_2) - 1], + ) + assert np.amax(state["multiplicity"].to_ndarray()) == max( + np.amax(n_2) - gamma * np.amin(n_2), np.amin(n_2) + ) + + @staticmethod + @pytest.mark.parametrize( + "v, n, p", + [ + pytest.param(np.array([1.0, 1, 1]), np.array([1, 1, 1]), 2), + pytest.param(np.array([1.0, 1, 1, 1, 1]), np.array([5, 1, 2, 1, 1]), 1), + pytest.param(np.array([1.0, 1, 1, 1, 1]), np.array([5, 1, 2, 1, 1]), 6), + ], + ) + def test_multi_droplet(backend_class, v, n, p): + # Arrange + particulator, sut = get_dummy_particulator_and_coalescence( + backend_class, len(n) + ) + + def _compute_gamma(prob, rand, is_first_in_pair, out): + backend_fill(prob, p, odd_zeros=True) + Coalescence.compute_gamma(sut, prob, rand, is_first_in_pair, out=out) + + sut.compute_gamma = _compute_gamma + attributes = {"multiplicity": n, "volume": v} + particulator.build(attributes) + + # Act + sut() + + # Assert + assert np.amin(particulator.attributes["multiplicity"].to_ndarray()) >= 0 + assert np.sum( + particulator.attributes["multiplicity"].to_ndarray() + * particulator.attributes["volume"].to_ndarray() + ) == np.sum(n * v) + + @staticmethod + def test_multi_step(backend_class): + # Arrange + n_sd = 256 + n = np.random.randint(1, 64, size=n_sd) + v = np.random.uniform(size=n_sd) + + particulator, sut = get_dummy_particulator_and_coalescence(backend_class, n_sd) + + sut.compute_gamma = lambda prob, rand, is_first_in_pair, out: backend_fill( + out, rand.to_ndarray() > 0.5, odd_zeros=True + ) + attributes = {"multiplicity": n, "volume": v} + particulator.build(attributes) + + # Act + for _ in range(32): + sut() + particulator.attributes.sanitize() + + # Assert + assert np.amin(particulator.attributes["multiplicity"].to_ndarray()) >= 0 + actual = np.sum( + particulator.attributes["multiplicity"].to_ndarray() + * particulator.attributes["volume"].to_ndarray() + ) + desired = np.sum(n * v) + np.testing.assert_approx_equal(actual=actual, desired=desired, significant=8) + + @staticmethod + def test_compute_gamma(backend_instance): + # Arrange + backend = backend_instance + n = 87 + prob = np.linspace(0, 3, n, endpoint=True) + rand = np.linspace(0, 1, n, endpoint=False) + + def expected(p, r): + return p // 1 + (r < p - p // 1) + + n_sd = 2 + for p in prob: + for r in rand: + # Act + prob_arr = backend.Storage.from_ndarray(np.full((n_sd // 2,), p)) + rand_arr = backend.Storage.from_ndarray(np.full((n_sd // 2,), r)) + idx = make_Index(backend).from_ndarray(np.arange(n_sd)) + mult = make_IndexedStorage(backend).from_ndarray( + idx, np.asarray([expected(p, r), 1]).astype(backend.Storage.INT) + ) + _ = backend.Storage.from_ndarray(np.zeros(n_sd // 2)) + cell_id = backend.Storage.from_ndarray( + np.zeros(n_sd, dtype=backend.Storage.INT) + ) + + indicator = make_PairIndicator(backend)(n_sd) + indicator.indicator[:] = backend.Storage.from_ndarray( + np.asarray((True, False)) + ) + + backend.compute_gamma( + prob=prob_arr, + rand=rand_arr, + multiplicity=mult, + cell_id=cell_id, + is_first_in_pair=indicator, + collision_rate=_, + collision_rate_deficit=_, + out=prob_arr, + ) + + # Assert + assert expected(p, r) == prob_arr.to_ndarray()[0] + + @staticmethod + @pytest.mark.parametrize( + "optimized_random", + (pytest.param(True, id="optimized"), pytest.param(False, id="non-optimized")), + ) + @pytest.mark.parametrize( + "adaptive", + (pytest.param(True, id="adaptive_dt"), pytest.param(False, id="const_dt")), + ) + def test_rnd_reuse(backend_class, optimized_random, adaptive): + if backend_class is ThrustRTC: + pytest.skip("# TODO #330") + + # Arrange + n_sd = 256 + n = np.random.randint(1, 64, size=n_sd) + v = np.random.uniform(size=n_sd) + n_substeps = 5 + + particles, sut = get_dummy_particulator_and_coalescence( + backend_class, + n_sd, + optimized_random=optimized_random, + substeps=n_substeps, + ) + attributes = {"multiplicity": n, "volume": v} + particles.build(attributes) + + class CountingRandom( + backend_class.Random + ): # pylint: disable=too-few-public-methods + calls = 0 + + def __call__(self, storage): + CountingRandom.calls += 1 + super().__call__(storage) + + sut.rnd_opt_coll.rnd = CountingRandom(n_sd, seed=44) + sut.stats_n_substep[:] = n_substeps + sut.adaptive = adaptive + + # Act + sut() + + # Assert + if sut.rnd_opt_coll.optimized_random: + assert CountingRandom.calls == 2 + else: + if adaptive: + assert 2 <= CountingRandom.calls <= 2 * n_substeps + else: + assert CountingRandom.calls == 2 * n_substeps diff --git a/PySDM/source/tests/unit_tests/dynamics/condensation/__init__.py b/PySDM/source/tests/unit_tests/dynamics/condensation/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/unit_tests/dynamics/condensation/test_diagnostics.py b/PySDM/source/tests/unit_tests/dynamics/condensation/test_diagnostics.py new file mode 100644 index 0000000000000000000000000000000000000000..4c8f01cb9b395a32192bddca84464733343e3a8a --- /dev/null +++ b/PySDM/source/tests/unit_tests/dynamics/condensation/test_diagnostics.py @@ -0,0 +1,140 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +# from ....backends_fixture import backend # TODO #588 +import fnmatch +import re + +import numpy as np + +from PySDM import Builder +from PySDM.backends import CPU +from PySDM.dynamics.condensation import Condensation +from PySDM.impl.mesh import Mesh +from PySDM.environments.impl import register_environment + + +@register_environment() +class _TestEnv: + def __init__( + self, *, dt, dv, rhod, thd, water_vapour_mixing_ratio, T, p, RH, rho, eta + ): + self.mesh = Mesh.mesh_0d() + self.full = None + self.particulator = None + self.dt = dt + self.dv = dv + self.env = { + "rhod": rhod, + "thd": thd, + "water_vapour_mixing_ratio": water_vapour_mixing_ratio, + "T": T, + "p": p, + "RH": RH, + "air density": rho, + "air dynamic viscosity": eta, + } + + def register(self, builder): + self.particulator = builder.particulator + self.full = lambda item: self.particulator.backend.Storage.from_ndarray( + np.full(self.particulator.n_sd, self.env[item]) + ) + + def get_predicted(self, item): + return self.full(item) + + def __getitem__(self, item): + return self.full(item) + + +class _TestParticulator: # pylint: disable=too-few-public-methods + def __init__( # pylint: disable=too-many-locals + self, + *, + backend, + n_sd=-1, + max_iters=-1, + multiplicity=-1, + dt=np.nan, + dv=np.nan, + rhod=np.nan, + thd=np.nan, + water_vapour_mixing_ratio=np.nan, + T=np.nan, + p=np.nan, + RH=np.nan, + dry_volume=np.nan, + wet_radius=np.nan, + rho=np.nan, + eta=np.nan, + ): + env = _TestEnv( + dt=dt, + dv=dv, + rhod=rhod, + thd=thd, + water_vapour_mixing_ratio=water_vapour_mixing_ratio, + T=T, + p=p, + RH=RH, + rho=rho, + eta=eta, + ) + builder = Builder(n_sd=n_sd, backend=backend(), environment=env) + + builder.add_dynamic(Condensation(max_iters=max_iters)) + self.particulator = builder.build( + attributes={ + "multiplicity": np.full(n_sd, multiplicity), + "volume": np.full(n_sd, wet_radius), + "dry volume": np.full(n_sd, dry_volume), + "kappa times dry volume": np.ones(n_sd), + } + ) + + def run(self, steps): + self.particulator.run(steps) + + +def _try(particulator, capsys): + exception = None + try: + particulator.run(steps=1) + except Exception as e: # pylint: disable=broad-except + exception = e + captured = capsys.readouterr() + assert captured.out == "" + assert isinstance(exception, RuntimeError) + assert str(exception) == "Condensation failed" + return exception, captured.err + + +FN = fnmatch.translate("*condensation_methods.py")[:-2] + + +class TestDiagnostics: # pylint: disable=too-few-public-methods + @staticmethod + def test_burnout_long(capsys, backend=CPU): + # arrange + particulator = _TestParticulator( + backend=backend, + dt=1, + T=1, + water_vapour_mixing_ratio=1, + dv=1, + rhod=1, + thd=1.0, + max_iters=1, + n_sd=1, + multiplicity=1, + dry_volume=1, + wet_radius=1, + ) + + # act + _, captured_err = _try(particulator, capsys) + + # assert + pattern = re.compile( + r"^burnout \(long\)\n\tfile: " + FN + r"\n\tcontext:\n\t\t thd\n\t\t 1.0\n$" + ) + assert pattern.match(captured_err) is not None diff --git a/PySDM/source/tests/unit_tests/dynamics/condensation/test_parcel_sanity_checks.py b/PySDM/source/tests/unit_tests/dynamics/condensation/test_parcel_sanity_checks.py new file mode 100644 index 0000000000000000000000000000000000000000..ce99960e77608e500f4c3d830b73ac23fd88eb64 --- /dev/null +++ b/PySDM/source/tests/unit_tests/dynamics/condensation/test_parcel_sanity_checks.py @@ -0,0 +1,214 @@ +"""tests ensuring proper condensation solver operation in some parcel-model based cases""" + +import numpy as np + +import pytest +from matplotlib import pyplot +from scipy import signal + +from PySDM import Builder, Formulae, products +from PySDM.backends import Numba, ThrustRTC +from PySDM.dynamics import AmbientThermodynamics, Condensation +from PySDM.environments import Parcel +from PySDM.initialisation import discretise_multiplicities +from PySDM.initialisation.hygroscopic_equilibrium import equilibrate_wet_radii +from PySDM.initialisation.sampling import spectral_sampling +from PySDM.initialisation.spectra import Lognormal +from PySDM.physics import si + +FORMULAE = Formulae() +SPECTRUM = Lognormal(norm_factor=1e4 / si.mg, m_mode=50 * si.nm, s_geom=1.5) +N_SD = 64 +R_DRY, specific_concentration = spectral_sampling.Logarithmic( + SPECTRUM +).sample_deterministic(N_SD) +V_DRY = FORMULAE.trivia.volume(radius=R_DRY) +KAPPA = 0.5 +CLOUD_RANGE = (0.5 * si.um, 25 * si.um) +PARCEL_CELL = 0 + + +class TestParcelSanityChecks: + @staticmethod + @pytest.mark.parametrize( + "backend_class", + ( + Numba, + pytest.param( + ThrustRTC, + marks=pytest.mark.xfail( + strict=True, + reason="TODO #1117 (works with CUDA!)", + ), + ), + ), + ) + def test_noisy_saturation_profiles(backend_class, plot=False): + """cases found using the README parcel snippet""" + # arrange + env = Parcel( + dt=1 * si.s, + mass_of_dry_air=1e3 * si.kg, + p0=1122 * si.hPa, + initial_water_vapour_mixing_ratio=20 * si.g / si.kg, + T0=300 * si.K, + w=2.5 * si.m / si.s, + ) + output_interval = 2 + output_points = 20 + + builder = Builder(backend=backend_class(FORMULAE), n_sd=N_SD, environment=env) + builder.add_dynamic(AmbientThermodynamics()) + builder.add_dynamic(Condensation()) + + r_wet = equilibrate_wet_radii( + r_dry=R_DRY, + environment=builder.particulator.environment, + kappa_times_dry_volume=KAPPA * V_DRY, + rtol=1e-3, + ) + + particulator = builder.build( + attributes={ + "multiplicity": discretise_multiplicities( + specific_concentration * env.mass_of_dry_air + ), + "dry volume": V_DRY, + "kappa times dry volume": KAPPA * V_DRY, + "volume": FORMULAE.trivia.volume(radius=r_wet), + }, + products=( + products.PeakSaturation(name="S_max_percent", unit="%"), + products.EffectiveRadius( + name="r_eff", unit="um", radius_range=CLOUD_RANGE + ), + products.ParticleConcentration( + name="n_c_cm3", unit="cm^-3", radius_range=CLOUD_RANGE + ), + products.WaterMixingRatio( + name="liquid water mixing ratio", + unit="g/kg", + radius_range=CLOUD_RANGE, + ), + products.ParcelDisplacement(name="z"), + ), + ) + + output = { + product.name: [product.get()[PARCEL_CELL]] + for product in particulator.products.values() + } + + for _ in range(output_points): + particulator.run(steps=output_interval) + for product in particulator.products.values(): + output[product.name].append(product.get()[PARCEL_CELL]) + + # plot + fig, axs = pyplot.subplots(1, len(particulator.products) - 1, sharey="all") + for i, (key, product) in enumerate(particulator.products.items()): + if key != "z": + axs[i].plot(output[key], output["z"], marker=".") + axs[i].set_title(product.name) + axs[i].set_xlabel(product.unit) + axs[i].grid() + axs[0].set_ylabel(particulator.products["z"].unit) + fig.suptitle(backend_class.__name__) + if plot: + pyplot.show() + else: + pyplot.clf() + + # assert + saturation_peaks, _ = signal.find_peaks(output["S_max_percent"]) + assert len(saturation_peaks) == 1 + + @staticmethod + @pytest.mark.parametrize("update_thd", (True, False)) + @pytest.mark.parametrize("substeps", (1, 2)) + def test_how_condensation_modifies_args(backend_class, update_thd, substeps): + """asserting that condensation modifies env thd and water_vapour_mixing_ratio only, + not rhod""" + # arrange + env = Parcel( + dt=1 * si.s, + mass_of_dry_air=1 * si.mg, + p0=1000 * si.hPa, + initial_water_vapour_mixing_ratio=22.2 * si.g / si.kg, + T0=300 * si.K, + w=1 * si.m / si.s, + ) + builder = Builder(n_sd=10, backend=backend_class(), environment=env) + builder.add_dynamic(AmbientThermodynamics()) + builder.add_dynamic( + Condensation( + update_thd=update_thd, + substeps=substeps, + adaptive=False, + ) + ) + particulator = builder.build( + products=(), + attributes=builder.particulator.environment.init_attributes( + kappa=1, r_dry=0.25 * si.um, n_in_dv=1000 + ), + ) + + particulator.dynamics["AmbientThermodynamics"]() + + cell_id = 0 + env = particulator.environment + pred_rhod_old = env.get_predicted("rhod")[cell_id] + pred_thd_old = env.get_predicted("thd")[cell_id] + pred_water_vapour_mixing_ratio_old = env.get_predicted( + "water_vapour_mixing_ratio" + )[cell_id] + + env_rhod_old = env["rhod"][cell_id] + env_thd_old = env["thd"][cell_id] + env_water_vapour_mixing_ratio_old = env["water_vapour_mixing_ratio"][cell_id] + + # act + particulator.dynamics["Condensation"]() + + # assert + assert env_rhod_old == env["rhod"] + assert env_water_vapour_mixing_ratio_old == env["water_vapour_mixing_ratio"] + assert env_thd_old == env["thd"] + + assert pred_rhod_old == env.get_predicted("rhod")[cell_id] + assert ( + pred_water_vapour_mixing_ratio_old + != env.get_predicted("water_vapour_mixing_ratio")[cell_id] + ) + + if update_thd: + assert pred_thd_old != env.get_predicted("thd")[cell_id] + else: + assert pred_thd_old == env.get_predicted("thd")[cell_id] + + @staticmethod + def test_zero_initial_delta_liquid_water_mixing_ratio(backend_class): + # arrange + env = Parcel( + dt=1 * si.s, + mass_of_dry_air=np.nan * si.mg, + p0=np.nan * si.hPa, + initial_water_vapour_mixing_ratio=44 * si.g / si.kg, + T0=np.nan * si.K, + w=np.nan * si.m / si.s, + ) + builder = Builder(n_sd=1, backend=backend_class(), environment=env) + builder.add_dynamic(AmbientThermodynamics()) + builder.add_dynamic(Condensation()) + + # act + particulator = builder.build( + products=(), + attributes=builder.particulator.environment.init_attributes( + kappa=1, r_dry=0.25 * si.um, n_in_dv=1000 + ), + ) + + # assert + assert particulator.environment.delta_liquid_water_mixing_ratio == 0 diff --git a/PySDM/source/tests/unit_tests/dynamics/condensation/test_ventilation.py b/PySDM/source/tests/unit_tests/dynamics/condensation/test_ventilation.py new file mode 100644 index 0000000000000000000000000000000000000000..04978bcd4cb2b5a2e3c8f29159796ae89ddfb550 --- /dev/null +++ b/PySDM/source/tests/unit_tests/dynamics/condensation/test_ventilation.py @@ -0,0 +1,81 @@ +"""exemplifies and does basic tests of ventilation logic in condensation/evaporation""" + +import pytest +import numpy as np + +from PySDM import Formulae, Builder +from PySDM.environments import Parcel +from PySDM.formulae import _choices +from PySDM.physics import drop_growth, ventilation, si +from PySDM.dynamics import Condensation, AmbientThermodynamics +from PySDM.products import AmbientRelativeHumidity +from PySDM.backends.impl_numba.test_helpers import scipy_ode_condensation_solver + +INITIAL_DROPLET_MASS = 1 * si.ug +DRY_VOLUME = 1 * si.nm**3 + + +def _make_particulator(backend): + builder = Builder( + backend=backend, + n_sd=1, + environment=Parcel( + dt=1 * si.s, + mass_of_dry_air=1 * si.mg, + p0=1000 * si.hPa, + initial_water_vapour_mixing_ratio=6.66 * si.g / si.kg, + T0=285 * si.K, + w=1 * si.m / si.s, + ), + ) + builder.add_dynamic(AmbientThermodynamics()) + builder.add_dynamic(Condensation()) + return builder.build( + attributes={ + "multiplicity": np.ones(1), + "water mass": np.asarray([INITIAL_DROPLET_MASS]), + "dry volume": np.asarray([DRY_VOLUME]), + "kappa times dry volume": 0.5 * np.asarray([DRY_VOLUME]), + }, + products=(AmbientRelativeHumidity(name="RH"),), + ) + + +@pytest.mark.parametrize( + "var_ventilation", + [v for v in _choices(ventilation) if v != ventilation.Neglect.__name__], +) +@pytest.mark.parametrize("var_drop_growth", list(_choices(drop_growth))) +@pytest.mark.parametrize("scipy_solver", (True, False)) +def test_ventilation(backend_class, var_ventilation, var_drop_growth, scipy_solver): + """tests checking effects of ventilation in a simplistic + single-[super]droplet adiabatic parcel simulation set up to + trigger evaporation of a large droplet in subsaturated air""" + + # arrange + particulators = { + key: _make_particulator( + backend_class( + formulae=Formulae(ventilation=key, drop_growth=var_drop_growth) + ) + ) + for key in [var_ventilation, ventilation.Neglect.__name__] + } + + if scipy_solver: + if backend_class.__name__ != "Numba": + pytest.skip("SciPy solver works only for Numba backend") + for particulator in particulators.values(): + scipy_ode_condensation_solver.patch_particulator(particulator) + + # act + for particulator in particulators.values(): + particulator.run(steps=1) + assert 0.75 < particulator.products["RH"].get()[0] < 0.8 + + # assert + mass_ratios = { + key: particulator.attributes["water mass"].to_ndarray() / INITIAL_DROPLET_MASS + for key, particulator in particulators.items() + } + assert 0.93 < mass_ratios[var_ventilation] < mass_ratios["Neglect"] < 1 diff --git a/PySDM/source/tests/unit_tests/dynamics/displacement/__init__.py b/PySDM/source/tests/unit_tests/dynamics/displacement/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/unit_tests/dynamics/displacement/displacement_settings.py b/PySDM/source/tests/unit_tests/dynamics/displacement/displacement_settings.py new file mode 100644 index 0000000000000000000000000000000000000000..09893c4c43fe7b48aa475e230e3d21318ef72662 --- /dev/null +++ b/PySDM/source/tests/unit_tests/dynamics/displacement/displacement_settings.py @@ -0,0 +1,48 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np + +from PySDM import Formulae +from PySDM.dynamics import Displacement + +from ...dummy_environment import DummyEnvironment +from ...dummy_particulator import DummyParticulator + + +class DisplacementSettings: # pylint: disable=too-few-public-methods,too-many-arguments + def __init__( + self, n_sd=1, volume=None, grid=None, positions=None, courant_field_data=None + ): + self.n = np.ones(n_sd, dtype=np.int64) + self.volume = volume or np.ones(n_sd, dtype=np.float64) + self.grid = grid or (1, 1) + self.courant_field_data = courant_field_data or ( + np.array([[0, 0]]).T, + np.array([[0, 0]]), + ) + self.positions = positions or [[0], [0]] + self.sedimentation = False + self.dt = None + + def get_displacement(self, backend, scheme, adaptive=True): + formulae = Formulae(particle_advection=scheme) + particulator = DummyParticulator(backend, n_sd=len(self.n), formulae=formulae) + particulator.environment = DummyEnvironment( + timestep=self.dt, grid=self.grid, courant_field_data=self.courant_field_data + ) + positions = np.array(self.positions) + cell_id, cell_origin, position_in_cell = particulator.mesh.cellular_attributes( + positions + ) + attributes = { + "multiplicity": self.n, + "volume": self.volume, + "cell id": cell_id, + "cell origin": cell_origin, + "position in cell": position_in_cell, + } + particulator.build(attributes) + sut = Displacement(enable_sedimentation=self.sedimentation, adaptive=adaptive) + sut.register(particulator) + sut.upload_courant_field(self.courant_field_data) + + return sut, particulator diff --git a/PySDM/source/tests/unit_tests/dynamics/displacement/test_advection.py b/PySDM/source/tests/unit_tests/dynamics/displacement/test_advection.py new file mode 100644 index 0000000000000000000000000000000000000000..14ff21caf0237675c34194748ae27a200e3a63e9 --- /dev/null +++ b/PySDM/source/tests/unit_tests/dynamics/displacement/test_advection.py @@ -0,0 +1,230 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest + +from .displacement_settings import DisplacementSettings + + +class TestExplicitEulerWithInterpolation: + @staticmethod + @pytest.mark.parametrize( + "positions, courant_field, expected_positions", + ( + # 1D + ( + [[0.5]], + (np.array([0.1, 0.2]),), + [[0.65827668]], + ), + # 2D + ( + [[0.5], [0.5]], + ( + np.array([0.1, 0.2]).reshape((2, 1)), + np.array([0.3, 0.4]).reshape((1, 2)), + ), + [[0.65827668], [0.86931224]], + ), + # 3D + ( + [[0.5], [0.5], [0.5]], + ( + np.array([0.1, 0.2]).reshape((2, 1, 1)), + np.array([0.3, 0.4]).reshape((1, 2, 1)), + np.array([0.2, 0.3]).reshape((1, 1, 2)), + ), + [ + [0.65827668], + [0.86931224], + [0.76379446], + ], + ), + ), + ) + def test_single_cell( + backend_class, positions, expected_positions, courant_field: tuple + ): + # Arrange + settings = DisplacementSettings( + courant_field_data=courant_field, + positions=positions, + grid=tuple([1] * len(courant_field)), + n_sd=len(positions[0]), + ) + sut, particulator = settings.get_displacement( + backend_class, scheme="ImplicitInSpace" + ) + + # Act + sut() + + # Assert + np.testing.assert_array_almost_equal( + np.asarray(expected_positions), + particulator.attributes["position in cell"].to_ndarray(), + ) + + @staticmethod + def test_advection(backend_class): + # Arrange + settings = DisplacementSettings() + settings.grid = (3, 3) + settings.courant_field_data = (np.ones((4, 3)), np.zeros((3, 4))) + settings.positions = [[1.5], [1.5]] + sut, particulator = settings.get_displacement( + backend_class, scheme="ImplicitInSpace" + ) + + # Act + sut() + + # Assert + dim_x = 0 + np.testing.assert_array_equal( + particulator.attributes["cell origin"][:, dim_x], np.array([2, 1]) + ) + + @staticmethod + def test_calculate_displacement(backend_class): + # Arrange + settings = DisplacementSettings() + value_a = 0.1 + value_b = 0.2 + weight = 0.25 + settings.courant_field_data = ( + np.array([[value_a, value_b]]).T, + np.array([[0, 0]]), + ) + settings.positions = [[weight], [0]] + sut, particulator = settings.get_displacement( + backend_class, scheme="ExplicitInSpace", adaptive=False + ) + + # Act + sut.calculate_displacement( + sut.displacement, + sut.courant, + particulator.attributes["cell origin"], + particulator.attributes["position in cell"], + ) + + # Assert + np.testing.assert_equal( + sut.displacement[0, slice(0, 1)].to_ndarray(), + (1 - weight) * value_a + weight * value_b, + ) + + @staticmethod + def test_calculate_displacement_dim1(backend_class): + # Arrange + settings = DisplacementSettings() + value_a = 0.1 + value_b = 0.2 + weight = 0.25 + settings.courant_field_data = ( + np.array([[0, 0]]).T, + np.array([[value_a, value_b]]), + ) + settings.positions = [[0], [weight]] + sut, particulator = settings.get_displacement( + backend_class, scheme="ExplicitInSpace", adaptive=False + ) + + # Act + sut.calculate_displacement( + sut.displacement, + sut.courant, + particulator.attributes["cell origin"], + particulator.attributes["position in cell"], + ) + + # Assert + np.testing.assert_equal( + sut.displacement[1, slice(0, 1)].to_ndarray(), + (1 - weight) * value_a + weight * value_b, + ) + + @staticmethod + def test_update_position(backend_class): + # Arrange + settings = DisplacementSettings() + p_x = 0.1 + p_y = 0.2 + settings.positions = [[p_x], [p_y]] + sut, particulator = settings.get_displacement( + backend_class, scheme="ImplicitInSpace" + ) + + droplet_id = slice(0, 1) + sut.displacement[:] = particulator.backend.Storage.from_ndarray( + np.asarray( + [ + [ + 0.1, + ], + [0.2], + ] + ) + ) + + # Act + sut.update_position( + particulator.attributes["position in cell"], sut.displacement + ) + + # Assert + for d in range(2): + np.testing.assert_array_almost_equal( + particulator.attributes["position in cell"][d, droplet_id].to_ndarray(), + settings.positions[d][droplet_id] + + sut.displacement[d, droplet_id].to_ndarray(), + ) + + @staticmethod + def test_update_cell_origin(backend_class): + # Arrange + settings = DisplacementSettings() + sut, particulator = settings.get_displacement( + backend_class, scheme="ImplicitInSpace" + ) + + droplet_id = 0 + state = particulator.attributes + state["position in cell"][:] = particulator.backend.Storage.from_ndarray( + np.asarray([[1.1], [1.2]]) + ) + + # Act + sut.update_cell_origin(state["cell origin"], state["position in cell"]) + + # Assert + for d in range(2): + assert ( + state["cell origin"][d, droplet_id] + == settings.positions[d][droplet_id] + 1 + ) + assert state["position in cell"][d, droplet_id] == ( + state["position in cell"][d, droplet_id] + - np.floor(state["position in cell"][d, droplet_id]) + ) + + @staticmethod + def test_boundary_condition(backend_class): + # Arrange + settings = DisplacementSettings() + sut, particulator = settings.get_displacement( + backend_class, scheme="ImplicitInSpace" + ) + + droplet_id = 0 + state = particulator.attributes + state["cell origin"][:] = particulator.backend.Storage.from_ndarray( + np.asarray([[1], [1]]) + ) + + # Act + sut.boundary_condition(state["cell origin"]) + + # Assert + assert state["cell origin"][0, droplet_id] == 0 + assert state["cell origin"][1, droplet_id] == 0 diff --git a/PySDM/source/tests/unit_tests/dynamics/displacement/test_courant_product.py b/PySDM/source/tests/unit_tests/dynamics/displacement/test_courant_product.py new file mode 100644 index 0000000000000000000000000000000000000000..7a37e6ae3308484387e12c276063a244951711b2 --- /dev/null +++ b/PySDM/source/tests/unit_tests/dynamics/displacement/test_courant_product.py @@ -0,0 +1,50 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest + +from PySDM import Builder +from PySDM.backends import CPU +from PySDM.dynamics import Displacement +from PySDM.environments import Kinematic2D +from PySDM.products import MaxCourantNumber + +GRID = (3, 4) + + +@pytest.mark.parametrize( + "courant_field", + ( + (np.zeros((GRID[0] + 1, GRID[1])), np.zeros((GRID[0], GRID[1] + 1))), + (np.ones((GRID[0] + 1, GRID[1])), np.zeros((GRID[0], GRID[1] + 1))), + (np.zeros((GRID[0] + 1, GRID[1])), np.ones((GRID[0], GRID[1] + 1))), + (-0.3 * np.ones((GRID[0] + 1, GRID[1])), 0.2 * np.ones((GRID[0], GRID[1] + 1))), + ), +) +def test_courant_product(courant_field): + # arrange + n_sd = 1 + env = Kinematic2D(dt=1, grid=GRID, size=(100, 100), rhod_of=lambda x: x * 0 + 1) + builder = Builder(n_sd=n_sd, backend=CPU(), environment=env) + builder.add_dynamic(Displacement()) + particulator = builder.build( + attributes={ + "multiplicity": np.ones(n_sd), + "volume": np.ones(n_sd), + "cell id": np.zeros(n_sd, dtype=int), + }, + products=(MaxCourantNumber(),), + ) + sut = particulator.products["max courant number"] + + # act + particulator.dynamics["Displacement"].upload_courant_field(courant_field) + max_courant = sut.get() + + # assert + np.testing.assert_allclose( + actual=max_courant, + desired=np.maximum( + np.maximum(abs(courant_field[0][1:, :]), abs(courant_field[0][:-1, :])), + np.maximum(abs(courant_field[1][:, 1:]), abs(courant_field[1][:, :-1])), + ), + ) diff --git a/PySDM/source/tests/unit_tests/dynamics/displacement/test_sedimentation.py b/PySDM/source/tests/unit_tests/dynamics/displacement/test_sedimentation.py new file mode 100644 index 0000000000000000000000000000000000000000..ffbb5a08e3ca3fd8b2f3f54ece919c257927e7f7 --- /dev/null +++ b/PySDM/source/tests/unit_tests/dynamics/displacement/test_sedimentation.py @@ -0,0 +1,41 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring,no-member +import numpy as np +import pytest +from .displacement_settings import DisplacementSettings + + +class ConstantTerminalVelocity: # pylint: disable=too-few-public-methods + def __init__(self, backend, particles): + self.values = backend.Storage.from_ndarray(np.full(particles.n_sd, 1000)) + + def get(self): + return self.values + + +VOLUMES = (1.0, -1.0) + + +class TestSedimentation: # pylint: disable=too-few-public-methods + @staticmethod + @pytest.mark.parametrize("volume", [np.asarray((v,)) for v in VOLUMES]) + def test_boundary_condition(backend_class, volume): + # Arrange + settings = DisplacementSettings(n_sd=len(volume), volume=volume) + settings.dt = 1 + settings.sedimentation = True + sut, particulator = settings.get_displacement( + backend_class, scheme="ImplicitInSpace" + ) + + particulator.attributes._ParticleAttributes__attributes[ + "relative fall velocity" + ] = ConstantTerminalVelocity(particulator.backend, particulator) + assert sut.precipitation_mass_in_last_step == 0 + + # Act + sut() + particulator.attributes.sanitize() + + # Assert + assert particulator.attributes.super_droplet_count == 0 + assert sut.precipitation_mass_in_last_step != 0 diff --git a/PySDM/source/tests/unit_tests/dynamics/test_eulerian_advection.py b/PySDM/source/tests/unit_tests/dynamics/test_eulerian_advection.py new file mode 100644 index 0000000000000000000000000000000000000000..15fe3fc43ff03dbf0995caaa7dae214e19fbb92b --- /dev/null +++ b/PySDM/source/tests/unit_tests/dynamics/test_eulerian_advection.py @@ -0,0 +1,39 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np + +from PySDM.dynamics import EulerianAdvection + +from ..dummy_environment import DummyEnvironment +from ..dummy_particulator import DummyParticulator + + +class TestEulerianAdvection: # pylint: disable=too-few-public-methods + @staticmethod + def test_update(backend_class): + # Arrange + particulator = DummyParticulator(backend_class) + halo = 3 + grid = (11, 13) + env = DummyEnvironment(grid=grid, halo=halo) + env.register(builder=particulator) + env.water_vapour_mixing_ratio[:] = 7.3 + env.thd[:] = 59.5 + env.pred["water_vapour_mixing_ratio"][:] = 3.7 + env.pred["thd"][:] = 5.59 + particulator.environment = env + particulator.dynamics["Displacement"] = None + + sut = EulerianAdvection(lambda _: None) + sut.register(particulator) + + # Act + sut() + + # Assert + np.testing.assert_array_equal( + env.get_water_vapour_mixing_ratio(), + env.get_predicted("water_vapour_mixing_ratio").to_ndarray().reshape(grid), + ) + np.testing.assert_array_equal( + env.get_thd(), env.get_predicted("thd").to_ndarray().reshape(grid) + ) diff --git a/PySDM/source/tests/unit_tests/dynamics/test_freezing.py b/PySDM/source/tests/unit_tests/dynamics/test_freezing.py new file mode 100644 index 0000000000000000000000000000000000000000..ddf81fedec0777047647346e86f910d2b7f65d9c --- /dev/null +++ b/PySDM/source/tests/unit_tests/dynamics/test_freezing.py @@ -0,0 +1,369 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest +from matplotlib import pyplot + +from PySDM import Builder, Formulae +from PySDM.dynamics import Freezing +from PySDM.environments import Box +from PySDM.physics import si +from PySDM.products import ( + IceWaterContent, + LiquidWaterContent, +) +from PySDM.backends import ThrustRTC + +VERY_BIG_J_HET = 1e20 +EPSILON_RH = 1e-3 + + +class TestDropletFreezing: + @staticmethod + @pytest.mark.parametrize( + "record_freezing_temperature", + (pytest.param(True, id="recording"), pytest.param(False, id="not recording")), + ) + def test_record_freezing_temperature_on_time_dependent_freeze( + backend_class, record_freezing_temperature + ): + if backend_class is ThrustRTC and record_freezing_temperature: + pytest.skip("TODO #1495") + + # arrange + formulae = Formulae( + particle_shape_and_density="MixedPhaseSpheres", + heterogeneous_ice_nucleation_rate="Constant", + constants={"J_HET": VERY_BIG_J_HET}, + ) + builder = Builder( + n_sd=1, + backend=backend_class(formulae=formulae), + environment=Box(dt=1 * si.s, dv=1 * si.m**3), + ) + builder.add_dynamic( + Freezing(immersion_freezing="time-dependent", thaw="instantaneous") + ) + if record_freezing_temperature: + builder.request_attribute("temperature of last freezing") + particulator = builder.build( + attributes={ + "multiplicity": np.asarray([1]), + "signed water mass": np.asarray([1 * si.ug]), + "immersed surface area": np.asarray([1 * si.um**2]), + } + ) + + temp_1 = 200 * si.K + temp_2 = 250 * si.K + particulator.environment["a_w_ice"] = np.nan + particulator.environment["T"] = temp_1 + + # act & assert + attr_name = "temperature of last freezing" + if not record_freezing_temperature: + assert attr_name not in particulator.attributes + else: + # never frozen yet + np.isnan(particulator.attributes[attr_name].to_ndarray()).all() + + # still not frozen since RH not greater than 100% + particulator.environment["RH"] = 1.0 + particulator.run(steps=1) + np.isnan(particulator.attributes[attr_name].to_ndarray()).all() + + # should freeze and record T1 + particulator.environment["RH"] += EPSILON_RH + particulator.run(steps=1) + assert all(particulator.attributes[attr_name].to_ndarray() == temp_1) + + # should thaw + particulator.environment["T"] = 300 * si.K + particulator.run(steps=1) + np.isnan(particulator.attributes[attr_name].to_ndarray()).all() + + # should re-freeze and record T2 + particulator.environment["T"] = temp_2 + particulator.run(steps=1) + assert all(particulator.attributes[attr_name].to_ndarray() == temp_2) + + # TODO #599 + def test_no_subsaturated_freezing(self): + pass + + @staticmethod + @pytest.mark.parametrize("thaw", ("instantaneous",)) + @pytest.mark.parametrize("epsilon", (0, 1e-5)) + def test_thaw(backend_class, thaw, epsilon): + # arrange + formulae = Formulae( + particle_shape_and_density="MixedPhaseSpheres", + ) + env = Box(dt=1 * si.s, dv=1 * si.m**3) + builder = Builder( + n_sd=1, backend=backend_class(formulae=formulae), environment=env + ) + builder.add_dynamic( + Freezing( + thaw=thaw, + ) + ) + particulator = builder.build( + products=(IceWaterContent(),), + attributes={ + "multiplicity": np.ones(builder.particulator.n_sd), + "signed water mass": -1 * np.ones(builder.particulator.n_sd) * si.ug, + }, + ) + particulator.environment["T"] = formulae.constants.T0 + epsilon + assert particulator.products["ice water content"].get() > 0 + + # act + particulator.run(steps=1) + + # assert + if epsilon > 0: + assert particulator.products["ice water content"].get() == 0 + else: + assert particulator.products["ice water content"].get() > 0 + + @staticmethod + def test_immersion_freezing_singular( + backend_class, + ): # pylint disable=too-many-locals + # arrange + n_sd = 44 + dv = 1 * si.m**3 + T_fz = 250 * si.K + water_mass = 1 * si.mg + multiplicity = 1e10 + + formulae = Formulae(particle_shape_and_density="MixedPhaseSpheres") + env = Box(dt=1 * si.s, dv=dv) + builder = Builder( + n_sd=n_sd, backend=backend_class(formulae=formulae), environment=env + ) + builder.add_dynamic(Freezing(immersion_freezing="singular")) + attributes = { + "multiplicity": np.full(n_sd, multiplicity), + "freezing temperature": np.full(n_sd, T_fz), + "signed water mass": np.full(n_sd, water_mass), + } + products = (IceWaterContent(name="qi"),) + particulator = builder.build(attributes=attributes, products=products) + particulator.environment["T"] = T_fz + particulator.environment["RH"] = 1.000001 + + # act + particulator.run(steps=1) + + # assert + (qi,) = particulator.products["qi"].get() + np.testing.assert_approx_equal( + actual=qi, + desired=n_sd * multiplicity * water_mass / dv, + significant=7, + ) + + @staticmethod + @pytest.mark.parametrize("temperature", (238 * si.kelvin, 232 * si.kelvin)) + def test_homogeneous_freezing_threshold( + backend_class, temperature + ): # pylint disable=too-many-locals + # arrange + n_sd = 44 + water_mass = 1 * si.mg + multiplicity = 1e10 + dv = 1 * si.m**3 + + formulae = Formulae(particle_shape_and_density="MixedPhaseSpheres") + builder = Builder( + n_sd=n_sd, + backend=backend_class(formulae=formulae), + environment=Box(dt=1 * si.s, dv=dv), + ) + builder.add_dynamic(Freezing(homogeneous_freezing="threshold")) + attributes = { + "multiplicity": np.full(n_sd, multiplicity), + "signed water mass": np.full(n_sd, water_mass), + } + products = ( + LiquidWaterContent(name="qc"), + IceWaterContent(name="qi"), + ) + particulator = builder.build(attributes=attributes, products=products) + particulator.environment["T"] = temperature + particulator.environment["RH"] = 1.0001 + particulator.environment["RH_ice"] = 1.5 + T_hom = formulae.constants.HOMOGENEOUS_FREEZING_THRESHOLD + + # act + particulator.run(steps=1) + + # assert + (qc,) = particulator.products["qc"].get() + (qi,) = particulator.products["qi"].get() + + if temperature > T_hom: + assert qc > 0 + assert qi == 0 + else: + assert qc == 0 + assert qi > 0 + np.testing.assert_approx_equal( + actual=qi, + desired=n_sd * multiplicity * water_mass / dv, + significant=7, + ) + + @staticmethod + @pytest.mark.parametrize("double_precision", (True, False)) + @pytest.mark.parametrize( + "freezing_type", ("het_time_dependent", "hom_time_dependent") + ) + # pylint: disable=too-many-locals,too-many-statements + def test_freezing_time_dependent( + backend_class, freezing_type, double_precision, plot=False + ): + if backend_class.__name__ == "Numba" and not double_precision: + pytest.skip() + + # Arrange + seed = 44 + cases = ( + {"dt": 5e5, "N": 1}, + {"dt": 1e6, "N": 1}, + {"dt": 5e5, "N": 8}, + {"dt": 1e6, "N": 8}, + {"dt": 5e5, "N": 16}, + {"dt": 1e6, "N": 16}, + ) + rate = 1e-9 + immersed_surface_area = 1 + droplet_volume = 1 + + number_of_real_droplets = 1024 + total_time = ( + 0.25e9 # effectively interpreted here as seconds, i.e. cycle = 1 * si.s + ) + + # dummy (but must-be-set) values + initial_water_mass = 1000 # for sign flip (ice water has negative volumes) + d_v = 666 # products use conc., dividing there, multiplying here, value does not matter + + def hgh(t): + return np.exp(-0.75 * rate * (t - total_time / 4)) + + def low(t): + return np.exp(-1.25 * rate * (t + total_time / 4)) + + immersion_freezing = None + homogeneous_freezing = None + if freezing_type == "het_time_dependent": + freezing_parameter = { + "heterogeneous_ice_nucleation_rate": "Constant", + "constants": {"J_HET": rate / immersed_surface_area}, + } + immersion_freezing = "time-dependent" + elif freezing_type == "hom_time_dependent": + freezing_parameter = { + "homogeneous_ice_nucleation_rate": "Constant", + "constants": {"J_HOM": rate / droplet_volume}, + } + homogeneous_freezing = "time-dependent" + + # Act + output = {} + + formulae = Formulae( + particle_shape_and_density="MixedPhaseSpheres", + **(freezing_parameter), + seed=seed, + ) + + products = (IceWaterContent(name="qi"),) + + for case in cases: + n_sd = int(number_of_real_droplets // case["N"]) + assert n_sd == number_of_real_droplets / case["N"] + assert total_time // case["dt"] == total_time / case["dt"] + + key = f"{case['dt']}:{case['N']}" + output[key] = {"unfrozen_fraction": [], "dt": case["dt"], "N": case["N"]} + + env = Box(dt=case["dt"], dv=d_v) + builder = Builder( + n_sd=n_sd, + backend=backend_class( + formulae=formulae, double_precision=double_precision + ), + environment=env, + ) + builder.add_dynamic( + Freezing( + immersion_freezing=immersion_freezing, + homogeneous_freezing=homogeneous_freezing, + ) + ) + attributes = { + "multiplicity": np.full(n_sd, int(case["N"])), + "immersed surface area": np.full(n_sd, immersed_surface_area), + "signed water mass": np.full(n_sd, initial_water_mass), + } + particulator = builder.build(attributes=attributes, products=products) + particulator.environment["RH"] = 1.0001 + particulator.environment["RH_ice"] = 1.5 + particulator.environment["a_w_ice"] = 0.6 + particulator.environment["T"] = np.nan + + cell_id = 0 + for i in range(int(total_time / case["dt"]) + 1): + particulator.run(0 if i == 0 else 1) + + ice_mass_per_volume = particulator.products["qi"].get()[cell_id] + ice_mass = ice_mass_per_volume * d_v + ice_number = ice_mass / initial_water_mass + unfrozen_fraction = 1 - ice_number / number_of_real_droplets + output[key]["unfrozen_fraction"].append(unfrozen_fraction) + + # Plot + fit_x = np.linspace(0, total_time, num=100) + fit_y = np.exp(-rate * fit_x) + + for out in output.values(): + pyplot.step( + out["dt"] * np.arange(len(out["unfrozen_fraction"])), + out["unfrozen_fraction"], + label=f"dt={out['dt']:.2g} / N={out['N']}", + marker=".", + linewidth=1 + out["N"] // 8, + ) + + _plot_fit(fit_x, fit_y, low, hgh, total_time) + if plot: + pyplot.show() + + # Assert + for out in output.values(): + data = np.asarray(out["unfrozen_fraction"]) + arg = out["dt"] * np.arange(len(data)) + np.testing.assert_array_less(data, hgh(arg)) + np.testing.assert_array_less(low(arg), data) + + +def _plot_fit(fit_x, fit_y, low, hgh, total_time): + pyplot.plot( + fit_x, fit_y, color="black", linestyle="--", label="theory", linewidth=5 + ) + pyplot.plot( + fit_x, hgh(fit_x), color="black", linestyle=":", label="assert upper bound" + ) + pyplot.plot( + fit_x, low(fit_x), color="black", linestyle=":", label="assert lower bound" + ) + pyplot.legend() + pyplot.yscale("log") + pyplot.ylim(fit_y[-1], fit_y[0]) + pyplot.xlim(None, total_time) + pyplot.xlabel("time [s]") + pyplot.ylabel("unfrozen fraction") + pyplot.grid() diff --git a/PySDM/source/tests/unit_tests/dynamics/test_impl_register_dynamic.py b/PySDM/source/tests/unit_tests/dynamics/test_impl_register_dynamic.py new file mode 100644 index 0000000000000000000000000000000000000000..c41171eb89ab256d6afbb4d6a031db5d5727b75b --- /dev/null +++ b/PySDM/source/tests/unit_tests/dynamics/test_impl_register_dynamic.py @@ -0,0 +1,40 @@ +"""checks if @register_product makes dynamics instances reusable""" + +import numpy as np + +from PySDM import Builder +from PySDM.backends import CPU +from PySDM.environments import Box +from PySDM.dynamics.impl import register_dynamic + + +def test_impl_register_dynamic(): + # arrange + @register_dynamic() + class Dynamic: # pylint: disable=too-few-public-methods + def __init__(self): + self.particulator = None + + def register(self, *, builder: Builder): + self.particulator = builder.particulator + + dynamic = Dynamic() + n_sd = 1 + kwargs = {"n_sd": n_sd, "backend": CPU(), "environment": Box(dt=0, dv=0)} + builders = [Builder(**kwargs), Builder(**kwargs)] + + # act + for builder in builders: + builder.add_dynamic(dynamic) + builder.build( + attributes={"multiplicity": np.ones(n_sd), "water mass": np.zeros(n_sd)} + ) + + # assert + assert dynamic.particulator is None + assert builders[0].particulator is not builders[1].particulator + for builder in builders: + assert ( + builder.particulator.dynamics["Dynamic"].particulator + is builder.particulator + ) diff --git a/PySDM/source/tests/unit_tests/dynamics/test_isotopic_fractionation.py b/PySDM/source/tests/unit_tests/dynamics/test_isotopic_fractionation.py new file mode 100644 index 0000000000000000000000000000000000000000..ffe2d1584188dc1a09447b14394f9e79fb82cad4 --- /dev/null +++ b/PySDM/source/tests/unit_tests/dynamics/test_isotopic_fractionation.py @@ -0,0 +1,93 @@ +""" +unit test for the IsotopicFractionation dynamic +""" + +from contextlib import nullcontext + +import numpy as np +import pytest + +from PySDM import Builder +from PySDM.dynamics import Condensation, IsotopicFractionation +from PySDM.dynamics.isotopic_fractionation import HEAVY_ISOTOPES +from PySDM.environments import Box +from PySDM.physics import si + + +DUMMY_ATTRIBUTES = { + attr: np.asarray([np.nan if attr != "multiplicity" else 0]) + for attr in ( + "multiplicity", + "water mass", + "dry volume", + "kappa times dry volume", + *[f"moles_{isotope}" for isotope in HEAVY_ISOTOPES], + ) +} + + +class TestIsotopicFractionation: + @staticmethod + @pytest.mark.parametrize( + "dynamics, context", + ( + pytest.param( + (Condensation(), IsotopicFractionation(isotopes=("1H",))), nullcontext() + ), + pytest.param( + (IsotopicFractionation(isotopes=("1H",)),), + pytest.raises(AssertionError, match="dynamics"), + ), + pytest.param( + (IsotopicFractionation(isotopes=("1H",)), Condensation()), + pytest.raises(AssertionError, match="dynamics"), + ), + ), + ) + def test_ensure_condensation_executed_before(backend_instance, dynamics, context): + # arrange + builder = Builder( + n_sd=1, backend=backend_instance, environment=Box(dv=np.nan, dt=1 * si.s) + ) + for dynamic in dynamics: + builder.add_dynamic(dynamic) + + # act + with context: + builder.build(attributes=DUMMY_ATTRIBUTES) + + @staticmethod + def test_call_marks_all_isotopes_as_updated(backend_instance): + # arrange + builder = Builder( + n_sd=1, backend=backend_instance, environment=Box(dv=np.nan, dt=1 * si.s) + ) + builder.add_dynamic(Condensation()) + builder.add_dynamic(IsotopicFractionation()) + particulator = builder.build(attributes=DUMMY_ATTRIBUTES, products=()) + for isotope in HEAVY_ISOTOPES: + # pylint:disable=protected-access + assert ( + particulator.attributes._ParticleAttributes__attributes[ + f"moles_{isotope}" + ].timestamp + == particulator.attributes._ParticleAttributes__attributes[ + "multiplicity" + ].timestamp + ) + + # act + particulator.dynamics["IsotopicFractionation"]() + + # assert + + for isotope in HEAVY_ISOTOPES: + # pylint:disable=protected-access + assert ( + particulator.attributes._ParticleAttributes__attributes[ + f"moles_{isotope}" + ].timestamp + > particulator.attributes._ParticleAttributes__attributes[ + "multiplicity" + ].timestamp + ) diff --git a/PySDM/source/tests/unit_tests/dynamics/test_relaxed_velocity.py b/PySDM/source/tests/unit_tests/dynamics/test_relaxed_velocity.py new file mode 100644 index 0000000000000000000000000000000000000000..4a9fdd277981616bb55f3ecc5349994a1a12b45f --- /dev/null +++ b/PySDM/source/tests/unit_tests/dynamics/test_relaxed_velocity.py @@ -0,0 +1,191 @@ +""" +Test `PySDM.dynamics.RelaxedVelocity` dynamic +""" + +import numpy as np +import pytest + +from PySDM.builder import Builder +from PySDM.dynamics import RelaxedVelocity +from PySDM.environments.box import Box +from PySDM.physics import si + + +@pytest.fixture( + name="default_attributes", + params=( + pytest.param( + { + "multiplicity": np.array([1, 2, 3, 4]), + "volume": np.array( + [ + 1 * si.mm**3, + 0.1 * si.mm**3, + 1 * si.mm**3, + 0.05 * si.mm**3, + ] + ), + }, + id="", + ), + ), +) +def default_attributes_fixture(request): + return request.param + + +@pytest.fixture( + name="constant_timescale", + params=(True, False), +) +def constant_timescale_fixture(request): + return request.param + + +def test_small_timescale(default_attributes, constant_timescale, backend_instance): + """ + When the fall velocity is initialized to 0 and relaxation is very quick, + the velocity should quickly approach the terminal velocity + """ + + env = Box(dt=1, dv=1) + builder = Builder( + n_sd=len(default_attributes["multiplicity"]), + backend=backend_instance, + environment=env, + ) + + builder.add_dynamic(RelaxedVelocity(c=1e-12, constant=constant_timescale)) + + builder.request_attribute("relative fall velocity") + builder.request_attribute("terminal velocity") + + default_attributes["relative fall momentum"] = np.zeros_like( + default_attributes["multiplicity"] + ) + + particulator = builder.build(attributes=default_attributes, products=()) + + particulator.run(1) + + assert np.allclose( + particulator.attributes["relative fall velocity"].to_ndarray(), + particulator.attributes["terminal velocity"].to_ndarray(), + ) + + +def test_large_timescale(default_attributes, constant_timescale, backend_instance): + """ + When the fall velocity is initialized to 0 and relaxation is very slow, + the velocity should remain 0 + """ + + env = Box(dt=1, dv=1) + builder = Builder( + n_sd=len(default_attributes["multiplicity"]), + backend=backend_instance, + environment=env, + ) + + builder.add_dynamic(RelaxedVelocity(c=1e15, constant=constant_timescale)) + + builder.request_attribute("relative fall velocity") + builder.request_attribute("terminal velocity") + + default_attributes["relative fall momentum"] = np.zeros_like( + default_attributes["multiplicity"] + ) + + particulator = builder.build(attributes=default_attributes, products=()) + + particulator.run(100) + + assert np.allclose( + particulator.attributes["relative fall velocity"].to_ndarray(), + np.zeros_like(default_attributes["multiplicity"]), + ) + + +def test_behavior(default_attributes, constant_timescale, backend_instance): + """ + The fall velocity should approach the terminal velocity exponentially + """ + + env = Box(dt=1, dv=1) + builder = Builder( + n_sd=len(default_attributes["multiplicity"]), + backend=backend_instance, + environment=env, + ) + + # relaxation happens too quickly unless c is high enough + builder.add_dynamic(RelaxedVelocity(c=100, constant=constant_timescale)) + + builder.request_attribute("relative fall velocity") + builder.request_attribute("terminal velocity") + + default_attributes["relative fall momentum"] = np.zeros_like( + default_attributes["multiplicity"] + ) + + particulator = builder.build(attributes=default_attributes, products=()) + + particulator.run(1) + delta_v1 = ( + particulator.attributes["terminal velocity"].to_ndarray() + - particulator.attributes["relative fall velocity"].to_ndarray() + ) + + particulator.run(1) + delta_v2 = ( + particulator.attributes["terminal velocity"].to_ndarray() + - particulator.attributes["relative fall velocity"].to_ndarray() + ) + + particulator.run(1) + delta_v3 = ( + particulator.attributes["terminal velocity"].to_ndarray() + - particulator.attributes["relative fall velocity"].to_ndarray() + ) + + # for an exponential decay, the ratio should be roughly constant using constant timesteps + assert (np.abs(delta_v1 / delta_v2 - delta_v2 / delta_v3) < 0.01).all() + + +@pytest.mark.parametrize("c", [0.1, 10]) +def test_timescale(default_attributes, c, constant_timescale, backend_instance): + """ + The non-constant timescale should be proportional to the sqrt of the radius. The + proportionality constant should be the parameter for the dynamic. + + The constant timescale should be constant. + """ + env = Box(dt=1, dv=1) + builder = Builder( + n_sd=len(default_attributes["multiplicity"]), + backend=backend_instance, + environment=env, + ) + + dyn = RelaxedVelocity(c=c, constant=constant_timescale) + builder.add_dynamic(dyn) + + default_attributes["relative fall momentum"] = np.zeros_like( + default_attributes["multiplicity"] + ) + + particulator = builder.build(attributes=default_attributes, products=()) + sqrt_radius_attr = builder.get_attribute("square root of radius") + + tau_storage = particulator.Storage.empty( + default_attributes["multiplicity"].shape, dtype=float + ) + dyn.calculate_tau(tau_storage, sqrt_radius_attr.get()) + + # expected_c should be whatever c was set to in the dynamic + if not constant_timescale: + expected_c = tau_storage.to_ndarray() / sqrt_radius_attr.get().to_ndarray() + else: + expected_c = tau_storage.to_ndarray() + + assert np.allclose(expected_c, c) diff --git a/PySDM/source/tests/unit_tests/dynamics/test_seeding.py b/PySDM/source/tests/unit_tests/dynamics/test_seeding.py new file mode 100644 index 0000000000000000000000000000000000000000..6db83f149eafd1eaba537e599aade32ea5887ad9 --- /dev/null +++ b/PySDM/source/tests/unit_tests/dynamics/test_seeding.py @@ -0,0 +1,254 @@ +"""Seeding dynamic tests""" + +from collections import namedtuple + +import numpy as np +import pytest + +from matplotlib import pyplot + +from PySDM import Builder +from PySDM.products import SuperDropletCountPerGridbox, Time +from PySDM.backends import CPU +from PySDM.backends.impl_common.index import make_Index +from PySDM.backends.impl_common.indexed_storage import make_IndexedStorage +from PySDM.dynamics import Seeding +from PySDM.environments import Box +from PySDM.physics import si + + +class TestSeeding: + @staticmethod + def test_zero_injection_rate_same_as_no_seeding_monodisperse( + plot=False, backend_instance=CPU() + ): + """a not-so-unit test checking that results of a box simulation + are the same without seeding as with a zero injection rate""" + # arrange + n_sd_seeding = 100 + n_sd_initial = 100 + + def simulation(*, dynamics): + t_max = 20 * si.min + timestep = 15 * si.s + dv = 1 * si.cm**3 + + builder = Builder( + backend=backend_instance, + n_sd=n_sd_seeding + n_sd_initial, + environment=Box(dt=timestep, dv=dv), + ) + for dynamic in dynamics: + builder.add_dynamic(dynamic) + + particulator = builder.build( + attributes={ + k: np.pad( + array=v, + pad_width=(0, n_sd_seeding), + mode="constant", + constant_values=np.nan if k == "multiplicity" else 0, + ) + for k, v in { + "volume": np.ones(n_sd_initial) * si.um**3, + "multiplicity": np.ones(n_sd_initial), + }.items() + }, + products=( + SuperDropletCountPerGridbox(name="sd_count"), + Time(), + ), + ) + products = {"sd_count": [], "time": []} + for step in range(int(t_max // timestep) + 1): + if step != 0: + particulator.run(steps=1) + for key, val in products.items(): + value = particulator.products[key].get() + if not isinstance(value, float): + (value,) = value + val.append(float(value)) + for key in products: + products[key] = np.array(products[key]) + return products + + # act + common_seeding_ctor_args = { + "seeded_particle_multiplicity": [1], + "seeded_particle_extensive_attributes": { + "signed water mass": [0.001 * si.ng], + }, + } + output = { + "zero_injection_rate": simulation( + dynamics=( + Seeding( + super_droplet_injection_rate=lambda time: 0, + **common_seeding_ctor_args, + ), + ) + ), + "positive_injection_rate": simulation( + dynamics=( + Seeding( + super_droplet_injection_rate=lambda time: ( + 1 if 10 * si.min <= time < 15 * si.min else 0 + ), + **common_seeding_ctor_args, + ), + ) + ), + "no_seeding": simulation(dynamics=()), + } + + # plot + pyplot.plot( + output["zero_injection_rate"]["sd_count"], + output["zero_injection_rate"]["time"], + lw=2, + label="with seeding dynamic, zero injection rate", + ) + pyplot.plot( + output["positive_injection_rate"]["sd_count"], + output["positive_injection_rate"]["time"], + lw=2, + ls="--", + label="with seeding dynamic, positive injection rate", + ) + pyplot.plot( + output["no_seeding"]["sd_count"], + output["no_seeding"]["time"], + lw=2, + ls=":", + label="without seeding dynamic", + ) + pyplot.xlabel("sd_count") + pyplot.ylabel("time") + pyplot.grid() + pyplot.legend() + if plot: + pyplot.show() + else: + pyplot.clf() + + # assert + np.testing.assert_array_equal( + output["zero_injection_rate"]["sd_count"], output["no_seeding"]["sd_count"] + ) + np.testing.assert_array_equal(np.diff(output["no_seeding"]["sd_count"]), 0) + assert np.amax(np.diff(output["positive_injection_rate"]["sd_count"])) >= 0 + assert output["positive_injection_rate"]["sd_count"][0] == n_sd_initial + assert ( + n_sd_initial + < output["positive_injection_rate"]["sd_count"][-1] + < n_sd_initial + n_sd_seeding + ) + + @staticmethod + def test_attribute_set_match(): + # arrange + extensive_attributes = ["a", "b", "c"] + seeding_attributes = ["d", "e", "f"] + + builder = namedtuple(typename="MockBuilder", field_names=("particulator",))( + particulator=namedtuple( + typename="MockParticulator", field_names=("n_steps", "attributes") + )( + n_steps=0, + attributes=namedtuple( + typename="MockAttributes", + field_names=("get_extensive_attribute_keys",), + )(get_extensive_attribute_keys=lambda: extensive_attributes), + ) + ) + + dynamic = Seeding( + super_droplet_injection_rate=lambda time: 0, + seeded_particle_extensive_attributes={ + k: [np.nan] for k in seeding_attributes + }, + seeded_particle_multiplicity=[0], + ) + dynamic.register(builder) + + # act + with pytest.raises(ValueError) as excinfo: + dynamic() + + # assert + assert "do not match those used in particulator" in str(excinfo) + + @staticmethod + @pytest.mark.parametrize( + "super_droplet_injection_rate, reservoir_length", + ( + (0, 0), # shuffle not called + (0, 2), + (1, 10), + (2, 10), + ), + ) + def test_seeded_particle_shuffle( + super_droplet_injection_rate, reservoir_length, backend=CPU() + ): + # arrange + extensive_attributes = ["a"] + seeding_attributes = ["a"] + + class MockParticulator: # pylint: disable=too-few-public-methods + def __init__(self): + self.seeding_call_count = 0 + self.indices = [] + + def seeding( + self, + *, + seeded_particle_index, + number_of_super_particles_to_inject, # pylint: disable=unused-argument + seeded_particle_multiplicity, # pylint: disable=unused-argument + seeded_particle_extensive_attributes, # pylint: disable=unused-argument + ): + self.seeding_call_count += 1 + self.indices.append(seeded_particle_index.to_ndarray()) + + Index = make_Index(backend) + IndexedStorage = make_IndexedStorage(backend) + Random = None if reservoir_length == 0 else backend.Random + formulae = None if reservoir_length == 0 else backend.formulae + Storage = None if reservoir_length == 0 else backend.Storage + n_steps = 0 + dt = np.nan + attributes = namedtuple( + typename="MockAttributes", + field_names=("get_extensive_attribute_keys",), + )(get_extensive_attribute_keys=lambda: extensive_attributes) + + builder = namedtuple(typename="MockBuilder", field_names=("particulator",))( + particulator=MockParticulator() + ) + + dynamic = Seeding( + super_droplet_injection_rate=lambda t: super_droplet_injection_rate, + seeded_particle_extensive_attributes={ + k: [np.nan] * reservoir_length for k in seeding_attributes + }, + seeded_particle_multiplicity=[1] * reservoir_length, + ) + dynamic.register(builder) + + # act + dynamic() + dynamic.particulator.n_steps += 1 + dynamic() + + # assert + assert dynamic.particulator.seeding_call_count == ( + 2 if super_droplet_injection_rate > 0 else 0 + ) + if super_droplet_injection_rate > 0: + assert ( + dynamic.particulator.indices[0] != dynamic.particulator.indices[1] + ).any() + assert sorted(dynamic.particulator.indices[0]) == sorted( + dynamic.particulator.indices[1] + ) diff --git a/PySDM/source/tests/unit_tests/dynamics/test_terminal_velocity.py b/PySDM/source/tests/unit_tests/dynamics/test_terminal_velocity.py new file mode 100644 index 0000000000000000000000000000000000000000..594b3db7a775928028b3fcd87934c73e3c57118f --- /dev/null +++ b/PySDM/source/tests/unit_tests/dynamics/test_terminal_velocity.py @@ -0,0 +1,248 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +from contextlib import nullcontext + +import matplotlib.pyplot as plt +import numpy as np +import pytest + +from PySDM import Builder, Formulae +from PySDM.dynamics.terminal_velocity import ( + GunnKinzer1949, + PowerSeries, + RogersYau, +) +from PySDM.environments import Box +from PySDM.physics import constants as const +from PySDM.physics import si +from tests.unit_tests.dummy_particulator import DummyParticulator + + +def test_approximation(backend_class, plot=False): + r = ( + np.array( + [0.078, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.2, 1.4, 1.6] + ) + * const.si.mm + / 2 + ) + particulator = DummyParticulator( + backend_class, n_sd=len(r), formulae=Formulae(terminal_velocity="RogersYau") + ) + r = particulator.backend.Storage.from_ndarray(r) + u = ( + np.array([18, 27, 72, 117, 162, 206, 247, 287, 327, 367, 403, 464, 517, 565]) + / 100 + ) + u_term_ry = particulator.backend.Storage.empty((len(u),), float) + RogersYau(particulator=particulator)(u_term_ry, r) + + u_term_inter = particulator.backend.Storage.from_ndarray(u_term_ry.to_ndarray()) + GunnKinzer1949(particulator)(u_term_inter, r) + + assert np.mean((u - u_term_ry) ** 2) < 2e-2 + assert np.mean((u - u_term_inter) ** 2) < 1e-6 + + if plot: + plt.plot(r, u_term_ry) + plt.plot(r, u_term_inter) + plt.plot(r, u) + plt.grid() + plt.show() + + +@pytest.mark.parametrize( + "variant, water_mass, exception_context, expected_v_term", + ( + ("GunnKinzer1949", 0 * si.g, None, 0), + ("GunnKinzer1949", 1e10 * si.kg, pytest.raises(ValueError, match="Radii"), -1), + ("RogersYau", 0 * si.g, None, 0), + ("TpDependent", 0 * si.g, None, 0), + ), +) +def test_terminal_velocity_boundary_values( + variant, backend_class, water_mass, exception_context, expected_v_term +): + if variant == "TpDependent": + pytest.skip() # TODO #348 + + # arrange + if exception_context is None: + context = nullcontext() + else: + context = exception_context + + formulae = Formulae(terminal_velocity=variant) + env = Box(dv=np.nan, dt=np.nan) + builder = Builder(n_sd=1, backend=backend_class(formulae), environment=env) + builder.request_attribute("terminal velocity") + particulator = builder.build( + attributes={ + "signed water mass": np.asarray([water_mass]), + "multiplicity": np.asarray([-1]), + } + ) + + # act + with context: + (v_term,) = particulator.attributes["terminal velocity"].to_ndarray() + + # assert + np.testing.assert_approx_equal(v_term, expected_v_term) + + +@pytest.mark.parametrize( + "prefactors, powers", + [ + ([2.0], [1 / 6]), + ([2.0, 1.0], [1 / 6, 1 / 8]), + pytest.param([1.0], [1 / 6, 1 / 8], marks=pytest.mark.xfail(strict=True)), + ], +) +def test_power_series(backend_class, prefactors, powers): + r = np.array([0.01, 0.1, 1.0]) * const.si.mm / 2 + particulator = DummyParticulator(backend_class, n_sd=len(r)) + u = np.zeros_like(r) + for j, pref in enumerate(prefactors): + u = u + pref * 4 / 3 * const.PI * (r ** (powers[j] * 3)) + r = particulator.backend.Storage.from_ndarray(r) + + u_term_ps = particulator.backend.Storage.empty((len(u),), float) + PowerSeries(particulator=particulator, prefactors=prefactors, powers=powers)( + u_term_ps, r + ) + + u_term_true = particulator.backend.Storage.from_ndarray(u) + + np.testing.assert_array_almost_equal(u, u_term_true) + + +@pytest.mark.parametrize("ice_variant", ("ColumnarIceCrystal", "IceSphere")) +def test_ice_particle_terminal_velocities_basics(backend_class, ice_variant): + if backend_class.__name__ == "ThrustRTC": + pytest.skip() + + # arrange + water_mass = np.logspace(base=10, start=-16, stop=-7, num=10) * si.kg + env = Box(dt=None, dv=None) + formulae_enabling_terminal_velocity_ice_calculation = Formulae( + particle_shape_and_density="MixedPhaseSpheres", + terminal_velocity_ice=ice_variant, + ) + builder = Builder( + backend=backend_class(formulae_enabling_terminal_velocity_ice_calculation), + n_sd=len(water_mass), + environment=env, + ) + builder.request_attribute("terminal velocity") + particulator = builder.build( + attributes={ + "signed water mass": -water_mass, + "multiplicity": np.ones_like(water_mass), + } + ) + atmospheric_settings = [ + {"temperature": 233 * si.kelvin, "pressure": 300 * si.hectopascal}, + {"temperature": 270 * si.kelvin, "pressure": 1000 * si.hectopascal}, + ] + terminal_velocity = None + for setting in atmospheric_settings: + + particulator.environment["T"] = setting["temperature"] + particulator.environment["p"] = setting["pressure"] + + # TODO #1606 the line below should not be needed (auto update when env variables change) + particulator.attributes.mark_updated("signed water mass") + + # act + particulator.run(steps=1) + + # assert + if terminal_velocity is not None: + assert all( + terminal_velocity + != particulator.attributes["terminal velocity"].to_ndarray() + ) + + terminal_velocity = particulator.attributes["terminal velocity"].to_ndarray() + + # assert + assert all(~np.isnan(terminal_velocity)) + assert all(terminal_velocity > 0.0) + assert all(np.diff(terminal_velocity) > 0.0) + + +def test_columnar_ice_crystal_terminal_velocity_against_spichtinger_and_gierens_2009_fig_3( + backend_class, plot=False +): + """Fig. 3 in [Spichtinger & Gierens 2009](https://doi.org/10.5194/acp-9-685-2009)""" + if backend_class.__name__ == "ThrustRTC": + pytest.skip() + # arrange + water_mass = np.logspace(base=10, start=-16, stop=-7, num=10) * si.kg + terminal_velocity_reference = ( + np.array( + [ + 1.4e-04, + 3.7e-04, + 9.7e-04, + 2.5e-03, + 9.1e-03, + 3.4e-02, + 1.3e-01, + 4.7e-01, + 1.1e00, + 1.9e00, + ] + ) + * si.m + / si.s + ) + ambient_temperature = 233 * si.K + ambient_pressure = 300 * si.hectopascal + + env = Box(dt=None, dv=None) + formulae_enabling_terminal_velocity_ice_calculation = Formulae( + particle_shape_and_density="MixedPhaseSpheres", + terminal_velocity_ice="ColumnarIceCrystal", + ) + builder = Builder( + backend=backend_class(formulae_enabling_terminal_velocity_ice_calculation), + n_sd=len(water_mass), + environment=env, + ) + + builder.request_attribute("terminal velocity") + particulator = builder.build( + attributes={ + "signed water mass": -water_mass, + "multiplicity": np.ones_like(water_mass), + } + ) + + particulator.environment["T"] = ambient_temperature + particulator.environment["p"] = ambient_pressure + + # act + terminal_velocity = particulator.attributes["terminal velocity"].to_ndarray() + + # plot + plt.xlabel("mass (kg)") + plt.ylabel("terminal velocity (m/s)") + plt.xlim(water_mass[0], water_mass[-1]) + plt.xscale("log") + plt.ylim(1e-4, 1e1) + plt.yscale("log") + plt.grid() + + plt.plot(water_mass, terminal_velocity_reference, color="black") + plt.plot(water_mass, terminal_velocity, color="red") + + if plot: + plt.show() + else: + plt.clf() + + # assert + np.testing.assert_almost_equal( + terminal_velocity, terminal_velocity_reference, decimal=1 + ) diff --git a/PySDM/source/tests/unit_tests/dynamics/test_vapour_deposition_on_ice.py b/PySDM/source/tests/unit_tests/dynamics/test_vapour_deposition_on_ice.py new file mode 100644 index 0000000000000000000000000000000000000000..819e0fee0acff9022e65b2c32bb1ad5a01f1c4c5 --- /dev/null +++ b/PySDM/source/tests/unit_tests/dynamics/test_vapour_deposition_on_ice.py @@ -0,0 +1,341 @@ +"""basic water vapor deposition on ice test""" + +from typing import Iterable + +import numpy as np +from matplotlib import pyplot +import pytest +import numba + +from PySDM.physics import si, in_unit +from PySDM.backends import CPU +from PySDM import Builder +from PySDM import Formulae +from PySDM.environments import Box +from PySDM.environments.impl import register_environment +from PySDM.environments.impl.moist import Moist +from PySDM.dynamics import VapourDepositionOnIce, AmbientThermodynamics +from PySDM.products import IceWaterContent + + +@register_environment() +class MoistBox(Box, Moist): + """env providing get_predicted() logic from Moist with box-model basics""" + + def __init__(self, dt: float, dv: float, mixed_phase: bool = False): + Box.__init__(self, dt, dv) + Moist.__init__(self, dt, self.mesh, variables=["rhod"], mixed_phase=mixed_phase) + + def register(self, builder): + Moist.register(self, builder) + + def get_water_vapour_mixing_ratio(self): + return self["water_vapour_mixing_ratio"] + + def get_thd(self): + return self["thd"] + + def sync(self): + Moist.sync(self) + + def get_predicted(self, key: str): + return self[key] + + +DIFFUSION_COORDINATES = ("WaterMass", "WaterMassLogarithm") +DIFFUSION_ICE_CAPACITIES = ("Spherical", "Columnar") +COMMON = { + "products": (IceWaterContent(),), +} + + +def make_particulator( + *, + dt: float, + diffusion_coordinate: str, + diffusion_ice_capacity: str, + signed_water_masses: Iterable, + temperature: float, + pressure: float, + RH_ice: float = None, + RH_water: float = None, + adaptive: bool = False, + multiplicity: int = int(1e8), +): + """instantiates a particulator with minimal components for testing ice depositional growth""" + assert RH_water is None or RH_ice is None + builder = Builder( + n_sd=len(signed_water_masses), + environment=MoistBox(dt=dt, dv=1 * si.m**3), + backend=CPU( + override_jit_flags={"parallel": False}, + formulae=Formulae( + particle_shape_and_density="MixedPhaseSpheres", + diffusion_coordinate=diffusion_coordinate, + diffusion_ice_capacity=diffusion_ice_capacity, + ), + ), + ) + builder.add_dynamic(AmbientThermodynamics()) + builder.add_dynamic(VapourDepositionOnIce(adaptive=adaptive)) + particulator = builder.build( + attributes={ + "multiplicity": np.full( + shape=(builder.particulator.n_sd,), fill_value=multiplicity + ), + "signed water mass": np.asarray(signed_water_masses), + }, + products=(IceWaterContent(),), + ) + particulator.environment["T"] = temperature + particulator.environment["p"] = pressure + pvs_ice = particulator.formulae.saturation_vapour_pressure.pvs_ice( + particulator.environment["T"][0] + ) + pvs_water = particulator.formulae.saturation_vapour_pressure.pvs_water( + particulator.environment["T"][0] + ) + vapour_pressure = RH_ice * pvs_ice if RH_ice else RH_water * pvs_water + particulator.environment["RH"] = vapour_pressure / pvs_water + particulator.environment["a_w_ice"] = pvs_ice / pvs_water + particulator.environment["Schmidt number"] = 1 + particulator.environment["water_vapour_mixing_ratio"] = ( + particulator.formulae.constants.eps + * vapour_pressure + / (particulator.environment["p"][0] - vapour_pressure) + ) + particulator.environment["rhod"] = ( + particulator.environment["p"][0] - vapour_pressure + ) / (particulator.environment["T"][0] * particulator.formulae.constants.Rd) + particulator.environment["thd"] = ( + particulator.formulae.state_variable_triplet.th_dry( + th_std=particulator.formulae.trivia.th_std( + p=particulator.environment["p"][0], T=particulator.environment["T"][0] + ), + water_vapour_mixing_ratio=particulator.environment[ + "water_vapour_mixing_ratio" + ][0], + ) + ) + return particulator + + +class TestVapourDepositionOnIce: + """groups tests for ice depositional growth""" + + @staticmethod + @pytest.mark.parametrize("water_mass", (-si.ng, -si.mg, si.mg)) + @pytest.mark.parametrize("RHi", (1.1, 1.0, 0.9)) + @pytest.mark.parametrize("diffusion_coordinate", DIFFUSION_COORDINATES) + @pytest.mark.parametrize("diffusion_ice_capacity", DIFFUSION_ICE_CAPACITIES) + def test_iwc_differs_after_one_timestep( + *, water_mass, RHi, diffusion_coordinate, diffusion_ice_capacity + ): + """sanity checks for sign of changes in IWC and ambient thermodynamics""" + # arrange + particulator = make_particulator( + temperature=250 * si.K, + pressure=500 * si.hPa, + diffusion_coordinate=diffusion_coordinate, + diffusion_ice_capacity=diffusion_ice_capacity, + signed_water_masses=[water_mass], + RH_ice=RHi, + dt=0.1 * si.s, + ) + rv0 = particulator.environment["water_vapour_mixing_ratio"][0] + thd0 = particulator.environment["thd"][0] + + # act + iwc_old = particulator.products["ice water content"].get()[0] + particulator.run(steps=1) + + # assert + if water_mass < 0 and RHi != 1: + if RHi > 1: + assert particulator.environment["water_vapour_mixing_ratio"][0] < rv0 + assert particulator.environment["thd"][0] > thd0 + assert particulator.products["ice water content"].get()[0] > iwc_old + elif RHi < 1: + assert particulator.environment["water_vapour_mixing_ratio"][0] > rv0 + assert particulator.environment["thd"][0] < thd0 + assert particulator.products["ice water content"].get()[0] < iwc_old + else: + np.testing.assert_approx_equal( + particulator.products["ice water content"].get()[0], iwc_old + ) + np.testing.assert_approx_equal( + particulator.environment["water_vapour_mixing_ratio"][0], rv0 + ) + np.testing.assert_approx_equal(particulator.environment["thd"][0], thd0) + + @staticmethod + @pytest.mark.parametrize( + "dt, adaptive", + ( + (0.01 * si.s, False), + pytest.param(1.0 * si.s, False, marks=pytest.mark.xfail(strict=True)), + ), + ) + @pytest.mark.parametrize("diffusion_coordinate", DIFFUSION_COORDINATES) + def test_growth_rates_against_spichtinger_and_gierens_2009_fig_5( + diffusion_coordinate, dt, adaptive, plot=False + ): + """Fig. 5 in [Spichtinger & Gierens 2009](https://doi.org/10.5194/acp-9-685-2009)""" + # arrange + initial_water_masses = ( + np.logspace(base=10, start=-16, stop=-8.5, num=10) * si.kg + ) + dm_dt = {} + + # act + for temperature in np.linspace(200, 230, endpoint=True, num=4) * si.K: + particulator = make_particulator( + pressure=300 * si.hPa, + diffusion_coordinate=diffusion_coordinate, + diffusion_ice_capacity="Columnar", + signed_water_masses=-initial_water_masses, + RH_water=1, + temperature=temperature, + dt=dt, + adaptive=adaptive, + ) + particulator.run(steps=1) + dm_dt[temperature] = ( + particulator.attributes["water mass"].to_ndarray() + - initial_water_masses + ) / particulator.environment.dt + + pyplot.xlabel("mass (kg)") + pyplot.ylabel("mass rate (kg/s)") + pyplot.xlim(initial_water_masses[0], initial_water_masses[-1]) + pyplot.xscale("log") + pyplot.ylim(1e-16, 1e-11) + pyplot.yscale("log") + pyplot.grid() + pyplot.title(f"p={in_unit(particulator.environment['p'][0], si.hPa)} hPa") + for temperature, mass_rate in dm_dt.items(): + pyplot.plot(initial_water_masses, mass_rate, color="black") + pyplot.annotate( + text=f" T={temperature}K", xy=(initial_water_masses[-1], mass_rate[-1]) + ) + + # plot + if plot: + pyplot.show() + else: + pyplot.clf() + + # assert + assert (dm_dt[200 * si.K] < dm_dt[210 * si.K]).all() + assert (dm_dt[210 * si.K] < dm_dt[220 * si.K]).all() + assert (dm_dt[220 * si.K] < dm_dt[230 * si.K]).all() + + for mass_rate in dm_dt.values(): + assert (np.diff(mass_rate) > 0).all() + + assert 1.8e-15 * si.kg / si.s < dm_dt[230 * si.K][0] < 4.0e-15 * si.kg / si.s + assert 7.0e-17 * si.kg / si.s < dm_dt[200 * si.K][0] < 1.0e-16 * si.kg / si.s + assert 1.3e-12 * si.kg / si.s < dm_dt[230 * si.K][-1] < 1.5e-12 * si.kg / si.s + assert 6.0e-14 * si.kg / si.s < dm_dt[200 * si.K][-1] < 1.2e-13 * si.kg / si.s + + @staticmethod + @pytest.mark.parametrize("diffusion_coordinate", DIFFUSION_COORDINATES) + @pytest.mark.parametrize("diffusion_ice_capacity", DIFFUSION_ICE_CAPACITIES) + def test_relative_mass_rates(*, diffusion_coordinate, diffusion_ice_capacity): + # arrange + water_mass_init = np.logspace(-16, -6, num=11) * si.kg + particulator = make_particulator( + temperature=250 * si.K, + pressure=500 * si.hPa, + RH_ice=1.1, + signed_water_masses=-water_mass_init, + diffusion_coordinate=diffusion_coordinate, + diffusion_ice_capacity=diffusion_ice_capacity, + dt=0.1 * si.s, + ) + + # act + particulator.run(steps=1) + + # assert + water_mass_new = particulator.attributes["water mass"].to_ndarray() + relative_growth = (water_mass_new - water_mass_init) / water_mass_init + assert all(relative_growth > 0.0) + assert all(np.diff(relative_growth) < 0.0) + + @staticmethod + @pytest.mark.parametrize( + "rh_ice, multiplicity, diffusion_coordinate", + ( + (1.5, 1e8, "WaterMass"), + (1.0, 1e8, "WaterMass"), + pytest.param( + 0.5, + 1e8, + "WaterMass", + marks=pytest.mark.xfail( + strict=numba.config.DISABLE_JIT # pylint:disable=no-member + ), + ), + (1.5, 1, "WaterMass"), + (1.0, 1, "WaterMass"), + (0.5, 1, "WaterMass"), + (1.5, 1e8, "WaterMassLogarithm"), + (1.0, 1e8, "WaterMassLogarithm"), + pytest.param( + 0.5, + 1e8, + "WaterMassLogarithm", + marks=pytest.mark.xfail( + strict=numba.config.DISABLE_JIT # pylint:disable=no-member + ), + ), + pytest.param( + 1.5, 1, "WaterMassLogarithm", marks=pytest.mark.xfail(strict=True) + ), + (1.0, 1, "WaterMassLogarithm"), + (0.5, 1, "WaterMassLogarithm"), + ), + ) + @pytest.mark.parametrize("diffusion_ice_capacity", DIFFUSION_ICE_CAPACITIES) + def test_mass_conservation_under_adaptivity( + rh_ice, + diffusion_ice_capacity, + diffusion_coordinate, + multiplicity, + ): + # arrange + water_mass_init = np.logspace(-15, -6, num=11) * si.kg + particulator = make_particulator( + adaptive=True, + dt=10 * si.s, + diffusion_coordinate=diffusion_coordinate, + diffusion_ice_capacity=diffusion_ice_capacity, + signed_water_masses=-water_mass_init, + temperature=250 * si.K, + pressure=800 * si.hPa, + RH_ice=rh_ice, + multiplicity=multiplicity, + ) + + def total_water_mass_in_the_system(attr, env): + return np.dot( + attr["water mass"].to_ndarray(), + attr["multiplicity"].to_ndarray(), + ) + (env["water_vapour_mixing_ratio"][0] * env["rhod"][0] * env.mesh.dv) + + # act + m0 = total_water_mass_in_the_system( + particulator.attributes, particulator.environment + ) + particulator.run(steps=1) + m1 = total_water_mass_in_the_system( + particulator.attributes, particulator.environment + ) + + # assert + np.testing.assert_almost_equal(m0, m1) + + +# TODO #1524: test is updraft matters +# TODO #1524: test if order of condensation/deposition matters diff --git a/PySDM/source/tests/unit_tests/environments/__init__.py b/PySDM/source/tests/unit_tests/environments/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/unit_tests/environments/test_impl.py b/PySDM/source/tests/unit_tests/environments/test_impl.py new file mode 100644 index 0000000000000000000000000000000000000000..01dd684fb68e92920936675c7384dd0c897ff107 --- /dev/null +++ b/PySDM/source/tests/unit_tests/environments/test_impl.py @@ -0,0 +1,59 @@ +"""checks for impl subpackage contents""" + +import pytest + +from PySDM import Builder +from PySDM.backends import CPU +from PySDM.environments.impl import register_environment + + +@register_environment() +class Env: # pylint: disable=too-few-public-methods + def __init__(self): + self.particulator = None + + def register(self, *, builder): + self.particulator = builder.particulator + + +class TestImpl: + @staticmethod + def test_register_environment_makes_env_instances_reusable(): + # arrange + env = Env() + kwargs = {"environment": env, "backend": CPU(), "n_sd": 0} + + # act + builders = [ + Builder(**kwargs), + Builder(**kwargs), + ] + + # assert + assert env.particulator is None + assert builders[0].particulator is not builders[1].particulator + for builder in builders: + assert builder.particulator.environment.particulator is not None + + @staticmethod + def test_register_environment_fails_with_other_instantiate_present(): + # arrange + class BogusEnv(Env): + def instantiate(self, *, builder): # pylint: disable=unused-argument + assert False + + # act + with pytest.raises(AttributeError) as e_info: + register_environment()(BogusEnv) + + # assert + assert "different instantiate" in str(e_info) + + @staticmethod + def test_register_environment_no_error_registering_class_inheritting_from_a_decorated_one(): + # arrange + class NewEnv(Env): # pylint: disable=too-few-public-methods + pass + + # act + register_environment()(NewEnv) diff --git a/PySDM/source/tests/unit_tests/environments/test_moist.py b/PySDM/source/tests/unit_tests/environments/test_moist.py new file mode 100644 index 0000000000000000000000000000000000000000..ab23011b1c3a445c9f1db00b7f2d48cdd68dd176 --- /dev/null +++ b/PySDM/source/tests/unit_tests/environments/test_moist.py @@ -0,0 +1,83 @@ +"""check for ice-phase-related commons in environment codes""" + +import pytest +import numpy as np +from PySDM.environments import Parcel +from PySDM.physics import si, constants_defaults +from PySDM import Builder +from PySDM.backends import ThrustRTC + +COMMON_PARCEL_CTOR_ARGS = { + "mixed_phase": True, + "dt": np.nan, + "mass_of_dry_air": np.nan, + "w": np.nan, +} +T0 = constants_defaults.T0 + + +@pytest.mark.parametrize( + "env, check", + ( + ( + Parcel( + p0=1000 * si.hPa, + initial_water_vapour_mixing_ratio=20 * si.g / si.kg, + T0=300 * si.K, + **COMMON_PARCEL_CTOR_ARGS, + ), + f"_['T']>{T0} and _['RH']<1 and _['RH_ice']<1", + ), + ( + Parcel( + p0=1000 * si.hPa, + initial_water_vapour_mixing_ratio=25 * si.g / si.kg, + T0=300 * si.K, + **COMMON_PARCEL_CTOR_ARGS, + ), + f"_['T']>{T0} and _['RH']>1 and _['RH_ice']<1", + ), + ( + Parcel( + p0=500 * si.hPa, + initial_water_vapour_mixing_ratio=0.2 * si.g / si.kg, + T0=250 * si.K, + **COMMON_PARCEL_CTOR_ARGS, + ), + f"_['T']<{T0} and _['RH']<1 and _['RH_ice']<1", + ), + ( + Parcel( + p0=500 * si.hPa, + initial_water_vapour_mixing_ratio=1 * si.g / si.kg, + T0=250 * si.K, + **COMMON_PARCEL_CTOR_ARGS, + ), + f"_['T']<{T0} and _['RH']<1 and _['RH_ice']>1", + ), + ), +) +def test_ice_properties(backend_instance, env, check): + """checks ice-related values in recalculated thermodynamic state make sense""" + if isinstance(backend_instance, ThrustRTC): + pytest.skip("TODO #1495") + + # arrange + builder = Builder(n_sd=0, backend=backend_instance, environment=env) + const = builder.particulator.formulae.constants + + # act + thermo = { + key: builder.particulator.environment[key].to_ndarray()[0] + for key in ("RH", "RH_ice", "a_w_ice", "T") + } + + # assert + exec(f"assert {check}", {"_": thermo}) # pylint: disable=exec-used + if thermo["T"] - const.T0 > 0: + assert thermo["RH"] > thermo["RH_ice"] > 0 + else: + assert thermo["RH_ice"] > thermo["RH"] > 0 + np.testing.assert_approx_equal( + thermo["a_w_ice"] * thermo["RH_ice"], thermo["RH"], significant=10 + ) diff --git a/PySDM/source/tests/unit_tests/exporters/__init__.py b/PySDM/source/tests/unit_tests/exporters/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/unit_tests/exporters/test_vtk_exporter.py b/PySDM/source/tests/unit_tests/exporters/test_vtk_exporter.py new file mode 100644 index 0000000000000000000000000000000000000000..d5ad6fcb26613ccf38dc616a9faaa390e63a0ba5 --- /dev/null +++ b/PySDM/source/tests/unit_tests/exporters/test_vtk_exporter.py @@ -0,0 +1,57 @@ +"""checks for VTK exporter""" + +from collections import namedtuple + +import numpy as np + +from PySDM.exporters import VTKExporter + + +def test_vtk_exporter_copies_product_data(tmp_path): + """note: since VTK files contain unencoded binary data, we cannot use XML parsers; + not to introduce a new dependency to PySDM, we read the binary data with NumPy""" + # arrange + productc_filename = tmp_path / "prod" + sut = VTKExporter(products_filename=productc_filename) + + grid = (1, 1) + arr = np.zeros(shape=grid, dtype=float) + + incr = 666 + + def plusplus(arr): + arr += incr + return arr + + prod = namedtuple(typename="MockProductA", field_names=("get",))( + get=lambda: plusplus(arr) + ) + + particulator = namedtuple( + typename="MockParticulator", field_names=("products", "n_steps", "dt", "mesh") + )( + n_steps=1, + products={ + "a": prod, + "b": prod, + }, + dt=0, + mesh=namedtuple(typename="MockMesh", field_names=("dimension", "grid", "size"))( + dimension=2, + grid=grid, + size=(1, 1), + ), + ) + + # act + sut.export_products(particulator) + + # assert + offsets = (113, 129) + with open(str(productc_filename) + "_num0000000001.vts", mode="rb") as vtk: + binary_data = vtk.readlines()[14] + for i, off in enumerate(offsets): + assert ( + np.frombuffer(binary_data[off : off + 8], dtype=np.float64) + == (i + 1) * incr + ) diff --git a/PySDM/source/tests/unit_tests/exporters/test_vtk_exporter_parcel.py b/PySDM/source/tests/unit_tests/exporters/test_vtk_exporter_parcel.py new file mode 100644 index 0000000000000000000000000000000000000000..ce5dc17a41076ea1700c035db60a0b6d315daa6f --- /dev/null +++ b/PySDM/source/tests/unit_tests/exporters/test_vtk_exporter_parcel.py @@ -0,0 +1,198 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +from unittest.mock import Mock +import numpy as np +import pytest + +from PySDM.exporters import VTKExporterParcel + + +class TestParcelVTKExporter: + + @staticmethod + @pytest.fixture + def mock_simulation(): + """Mock simulation object with necessary attributes.""" + simulation = Mock() + simulation.particulator = Mock() + simulation.particulator.dt = 1.0 + simulation.particulator.n_sd = 100 + simulation.particulator.environment = Mock() + simulation.particulator.environment.mass_of_dry_air = 66666 + + return simulation + + @staticmethod + @pytest.fixture + def mock_output(): + """Mock output data structure.""" + n_levels = 5 + output = { + "products": { + "z": np.linspace(0, 100, n_levels), + "rhod": np.ones(n_levels) * 1.2, + "S_max_percent": np.linspace(100, 110, n_levels), + }, + "attributes": { + "radius": np.random.random((100, 10)), + "multiplicity": np.random.random((100, 10)), + }, + } + return output + + def test_initialization(self, mock_output, mock_simulation): + # Arrange + + # Act + exporter = VTKExporterParcel( + n_sd=mock_simulation.particulator.n_sd, + output=mock_output, + mass_of_dry_air=mock_simulation.particulator.environment.mass_of_dry_air, + ) + + # Assert + assert exporter.output == mock_output + assert len(exporter.coords["x"]) == mock_simulation.particulator.n_sd + assert len(exporter.coords["y"]) == mock_simulation.particulator.n_sd + assert len(exporter.coords["z"]) == mock_simulation.particulator.n_sd + assert len(exporter.half_diagonal) == exporter.n_levels + assert exporter.n_levels == len(mock_output["products"]["z"]) + + def test_export_products(self, mock_output, mock_simulation, tmp_path): + # Arrange + exporter = VTKExporterParcel( + n_sd=mock_simulation.particulator.n_sd, + output=mock_output, + mass_of_dry_air=mock_simulation.particulator.environment.mass_of_dry_air, + ) + exporter.path = str(tmp_path) + exporter.attributes_file_path = str(tmp_path / "sd_attributes") + exporter.products_file_path = str(tmp_path / "sd_products") + exporter.exported_times = {"products": {}, "attributes": {}} + + step = 1 + + # Act + exporter.export_products(step, mock_simulation) + + # Assert + expected_file = ( + tmp_path / f"sd_products_num{exporter.add_leading_zeros(step)}.vtu" + ) + assert expected_file.exists() + + assert len(exporter.exported_times["products"]) == 1 + expected_time = step * mock_simulation.particulator.dt + assert list(exporter.exported_times["products"].values())[0] == expected_time + + def test_export_attributes(self, mock_output, mock_simulation, tmp_path): + # Arrange + exporter = VTKExporterParcel( + n_sd=mock_simulation.particulator.n_sd, + output=mock_output, + mass_of_dry_air=mock_simulation.particulator.environment.mass_of_dry_air, + ) + exporter.path = str(tmp_path) + exporter.attributes_file_path = str(tmp_path / "sd_attributes") + exporter.products_file_path = str(tmp_path / "sd_products") + exporter.exported_times = {"products": {}, "attributes": {}} + + step = 1 + + # Act + exporter.export_attributes(step, mock_simulation) + + # Assert + expected_file = ( + tmp_path / f"sd_attributes_num{exporter.add_leading_zeros(step)}.vtu" + ) + assert expected_file.exists() + + assert len(exporter.exported_times["attributes"]) == 1 + expected_time = step * mock_simulation.particulator.dt + assert list(exporter.exported_times["attributes"].values())[0] == expected_time + + def test_write_pvd(self, mock_output, mock_simulation, tmp_path): + # Arrange + exporter = VTKExporterParcel( + n_sd=mock_simulation.particulator.n_sd, + output=mock_output, + mass_of_dry_air=mock_simulation.particulator.environment.mass_of_dry_air, + ) + exporter.path = str(tmp_path) + exporter.attributes_file_path = str(tmp_path / "sd_attributes") + exporter.products_file_path = str(tmp_path / "sd_products") + exporter.exported_times = {"products": {}, "attributes": {}} + + steps = [1, 2] + for step in steps: + exporter.export_products(step, mock_simulation) + exporter.export_attributes(step, mock_simulation) + + # Act + exporter.write_pvd() + + # Assert + attributes_pvd = tmp_path / "sd_attributes.pvd" + products_pvd = tmp_path / "sd_products.pvd" + + assert attributes_pvd.exists() + assert products_pvd.exists() + + def test_coordinate_calculation(self, mock_output, mock_simulation, tmp_path): + # Arrange + exporter = VTKExporterParcel( + n_sd=mock_simulation.particulator.n_sd, + output=mock_output, + mass_of_dry_air=mock_simulation.particulator.environment.mass_of_dry_air, + ) + exporter.path = str(tmp_path) + exporter.attributes_file_path = str(tmp_path / "sd_attributes") + exporter.products_file_path = str(tmp_path / "sd_products") + exporter.exported_times = {"products": {}, "attributes": {}} + + initial_z_coords = exporter.coords["z"].copy() + + # Act + exporter.export_attributes(step=(step1 := 1), simulation=mock_simulation) + z_after_step1 = exporter.coords["z"].copy() + + exporter.export_attributes(step=(step2 := 2), simulation=mock_simulation) + z_after_step2 = exporter.coords["z"].copy() + + # Assert + delta_z_step1 = ( + mock_output["products"]["z"][step1] + - mock_output["products"]["z"][step1 - 1] + ) + expected_z_step1 = initial_z_coords * delta_z_step1 + np.testing.assert_array_equal(z_after_step1, expected_z_step1) + + delta_z_step2 = ( + mock_output["products"]["z"][step2] + - mock_output["products"]["z"][step2 - 1] + ) + expected_z_step2 = z_after_step1 + delta_z_step2 + np.testing.assert_array_equal(z_after_step2, expected_z_step2) + + def test_half_diagonal_calculation(self, mock_output, mock_simulation): + # Arrange + + # Act + exporter = VTKExporterParcel( + n_sd=mock_simulation.particulator.n_sd, + output=mock_output, + mass_of_dry_air=mock_simulation.particulator.environment.mass_of_dry_air, + ) + + # Assert + assert len(exporter.half_diagonal) == exporter.n_levels + assert all(hd > 0 for hd in exporter.half_diagonal) + + volume_0 = ( + mock_simulation.particulator.environment.mass_of_dry_air + / mock_output["products"]["rhod"][0] + ) + delta_z_0 = mock_output["products"]["z"][1] - mock_output["products"]["z"][0] + area_0 = volume_0 / delta_z_0 + expected_hd_0 = (2 * area_0) ** 0.5 + assert abs(exporter.half_diagonal[0] - expected_hd_0) < 1e-10 diff --git a/PySDM/source/tests/unit_tests/impl/__init__.py b/PySDM/source/tests/unit_tests/impl/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/unit_tests/impl/test_camel_case.py b/PySDM/source/tests/unit_tests/impl/test_camel_case.py new file mode 100644 index 0000000000000000000000000000000000000000..eeba1c626ae735e9597efb971d89458f84d5fcac --- /dev/null +++ b/PySDM/source/tests/unit_tests/impl/test_camel_case.py @@ -0,0 +1,21 @@ +""" +test checking CamelCase conversion routine +""" + +from typing import Tuple +import pytest +from PySDM.impl.camel_case import camel_case_to_words + + +@pytest.mark.parametrize( + "in_out_pair", (("CPUTime", "CPU time"), ("WallTime", "wall time")) +) +def test_camel_case_to_words(in_out_pair: Tuple[str, str]): + # arrange + test_input, expected_output = in_out_pair + + # act + actual_output = camel_case_to_words(test_input) + + # assert + assert actual_output == expected_output diff --git a/PySDM/source/tests/unit_tests/impl/test_mesh.py b/PySDM/source/tests/unit_tests/impl/test_mesh.py new file mode 100644 index 0000000000000000000000000000000000000000..e7002a5ac05add78d4112de016f64bd18fb80a11 --- /dev/null +++ b/PySDM/source/tests/unit_tests/impl/test_mesh.py @@ -0,0 +1,120 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest + +from PySDM.impl.mesh import Mesh +from PySDM.physics import si + + +def random_positions(grid: tuple, n_sd: int): + positions = np.random.random(len(grid) * n_sd).reshape(len(grid), n_sd) + for dim, span in enumerate(grid): + positions[dim, :] *= span + return positions + + +class TestMesh: + @staticmethod + @pytest.mark.parametrize( + "grid", + ( + (10,), + (2, 2), + (10, 20, 30), + ), + ) + def test_strides(grid: tuple): + # arrange + n_sd = 666 + positions = random_positions(grid, n_sd) + size = 44 * np.asarray(grid) + cell_id_expected, cell_origins, _ = Mesh(grid, size).cellular_attributes( + positions + ) + + # act + strides = Mesh._Mesh__strides(grid) + + # assert + assert len(strides.shape) == 2 + assert strides[-1][-1] == 1 + assert strides.shape == (1, len(grid)) + + cell_id_actual = np.array( + [ + np.dot(strides, cell_origin) # the recipe for computing cell_id + for cell_origin in cell_origins.T + ] + ).squeeze() + np.testing.assert_array_equal(cell_id_expected, cell_id_actual) + + @staticmethod + @pytest.mark.parametrize( + "mesh", + ( + Mesh(grid=(10,), size=(2,)), + Mesh(grid=(2, 2), size=(4, 4)), + Mesh(grid=(10, 20, 30), size=(5, 5, 5)), + Mesh(grid=(3, 4, 5, 6), size=(5, 5, 5, 5)), + ), + ) + def test_cellular_attributes(mesh): + # arrange + n_sd = 666 + positions = random_positions(mesh.grid, n_sd) + + # act + cell_id, cell_origin, position_in_cell = mesh.cellular_attributes(positions) + + # assert + assert cell_id.shape == (n_sd,) + assert cell_origin.shape == (mesh.dimension, n_sd) + assert position_in_cell.shape == (mesh.dimension, n_sd) + + assert 0 <= min(cell_id) <= max(cell_id) < mesh.n_cell + assert cell_id.dtype == np.int64 + + for dim in range(mesh.dimension): + assert ( + 0 + <= min(cell_origin[dim, :]) + <= max(cell_origin[dim, :]) + < mesh.grid[dim] + ) + assert cell_origin.dtype == np.int64 + + for dim in range(mesh.dimension): + assert ( + 0 <= min(position_in_cell[dim, :]) <= max(position_in_cell[dim, :]) < 1 + ) + assert position_in_cell.dtype == float + + @staticmethod + @pytest.mark.parametrize( + "mesh, expected_dv", + ( + (Mesh.mesh_0d(dv=44 * si.m**3), 44 * si.m**3), + (Mesh(grid=(100,), size=(1 * si.km,)), 10 * si.m * 1 * si.m * 1 * si.m), + ( + Mesh(grid=(100, 200), size=(1 * si.km, 2 * si.km)), + 10 * si.m * 10 * si.m * 1 * si.m, + ), + ( + Mesh(grid=(10, 20, 30), size=(1 * si.km, 2 * si.km, 3 * si.km)), + (100 * si.m) ** 3, + ), + ), + ) + def test_dv(mesh, expected_dv): + assert mesh.dv == expected_dv + + @staticmethod + @pytest.mark.parametrize( + "mesh, expected_area", + ( + (Mesh(grid=(100,), size=(44 * si.km,)), 1 * si.m**2), + (Mesh(grid=(100, 200), size=(44 * si.km, 666 * si.m)), 44 * si.km * si.m), + ), + ) + def test_domain_bottom_surface_area(mesh, expected_area): + assert mesh.domain_bottom_surface_area == expected_area diff --git a/PySDM/source/tests/unit_tests/impl/test_moments.py b/PySDM/source/tests/unit_tests/impl/test_moments.py new file mode 100644 index 0000000000000000000000000000000000000000..b6ad286f999f94976234cc46eb8526f146af2630 --- /dev/null +++ b/PySDM/source/tests/unit_tests/impl/test_moments.py @@ -0,0 +1,125 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np + +from PySDM.initialisation.discretise_multiplicities import discretise_multiplicities +from PySDM.initialisation.sampling.spectral_sampling import Linear +from PySDM.initialisation.spectra.lognormal import Lognormal + +from ..dummy_particulator import DummyParticulator + + +class TestMaths: + @staticmethod + # pylint: disable=too-many-locals + def test_moment_0d(backend_class): + # Arrange + n_part = 100000 + v_mean = 2e-6 + d = 1.2 + n_sd = 32 + + spectrum = Lognormal(n_part, v_mean, d) + v, n = Linear(spectrum).sample_deterministic(n_sd) + T = 300.0 + n = discretise_multiplicities(n) + particulator = DummyParticulator(backend_class, n_sd) + attribute = {"multiplicity": n, "volume": v, "heat": T * v} + particulator.request_attribute("temperature") + particulator.build(attribute) + + true_mean, true_var = spectrum.stats(moments="mv") + + # TODO #217 : add a moments_0 wrapper + moment_0 = particulator.backend.Storage.empty((1,), dtype=float) + moments = particulator.backend.Storage.empty((1, 1), dtype=float) + + # Act + particulator.moments(moment_0=moment_0, moments=moments, specs={"volume": (0,)}) + discr_zero = moments[0, slice(0, 1)].to_ndarray() + + particulator.moments(moment_0=moment_0, moments=moments, specs={"volume": (1,)}) + discr_mean = moments[0, slice(0, 1)].to_ndarray() + + particulator.moments(moment_0=moment_0, moments=moments, specs={"volume": (2,)}) + discr_mean_radius_squared = moments[0, slice(0, 1)].to_ndarray() + + particulator.moments( + moment_0=moment_0, moments=moments, specs={"temperature": (0,)} + ) + discr_zero_T = moments[0, slice(0, 1)].to_ndarray() + + particulator.moments( + moment_0=moment_0, moments=moments, specs={"temperature": (1,)} + ) + discr_mean_T = moments[0, slice(0, 1)].to_ndarray() + + particulator.moments( + moment_0=moment_0, moments=moments, specs={"temperature": (2,)} + ) + (discr_mean_T_squared,) = moments[0, slice(0, 1)].to_ndarray() + + # Assert + assert abs(discr_zero - 1) / 1 < 1e-3 + + assert abs(discr_mean - true_mean) / true_mean < 0.01e-1 + + true_mrsq = true_var + true_mean**2 + assert abs(discr_mean_radius_squared - true_mrsq) / true_mrsq < 0.05e-1 + + assert discr_zero_T == discr_zero + assert discr_mean_T == 300.0 + np.testing.assert_approx_equal(discr_mean_T_squared, 300.0**2, significant=6) + + @staticmethod + # pylint: disable=too-many-locals + def test_spectrum_moment_0d(backend_class): + # Arrange + n_part = 100000 + v_mean = 2e-6 + d = 1.2 + n_sd = 32 + + spectrum = Lognormal(n_part, v_mean, d) + v, n = Linear(spectrum).sample_deterministic(n_sd) + T = 300.0 + n = discretise_multiplicities(n) + particulator = DummyParticulator(backend_class, n_sd) + attribute = {"multiplicity": n, "volume": v, "heat": T * v} + particulator.request_attribute("temperature") + particulator.build(attribute) + + v_bins = np.linspace(0, 5e-6, num=5, endpoint=True) + + # TODO #217 : add a moments_0 wrapper + spectrum_moment_0 = particulator.backend.Storage.empty( + (len(v_bins) - 1, 1), dtype=float + ) + spectrum_moments = particulator.backend.Storage.empty( + (len(v_bins) - 1, 1), dtype=float + ) + moment_0 = particulator.backend.Storage.empty((1,), dtype=float) + moments = particulator.backend.Storage.empty((1, 1), dtype=float) + v_bins_edges = particulator.backend.Storage.from_ndarray(v_bins) + + # Act + particulator.spectrum_moments( + moment_0=spectrum_moment_0, + moments=spectrum_moments, + attr="volume", + rank=1, + attr_bins=v_bins_edges, + ) + actual = spectrum_moments.to_ndarray() + + expected = np.empty((len(v_bins) - 1, 1), dtype=float) + for i in range(len(v_bins) - 1): + particulator.moments( + moment_0=moment_0, + moments=moments, + specs={"volume": (1,)}, + attr_range=(v_bins[i], v_bins[i + 1]), + ) + expected[i, 0] = moments[0, 0] + + # Assert + np.testing.assert_array_almost_equal(actual, expected) diff --git a/PySDM/source/tests/unit_tests/impl/test_particle_attributes.py b/PySDM/source/tests/unit_tests/impl/test_particle_attributes.py new file mode 100644 index 0000000000000000000000000000000000000000..348539015a7eefdd33868cca05db593faf29ec37 --- /dev/null +++ b/PySDM/source/tests/unit_tests/impl/test_particle_attributes.py @@ -0,0 +1,283 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring,no-member +import numpy as np +import pytest + +from PySDM.backends import Numba, ThrustRTC +from PySDM.backends.impl_common.index import make_Index +from PySDM.backends.impl_common.indexed_storage import make_IndexedStorage +from PySDM.impl.particle_attributes_factory import ParticleAttributesFactory + +from ..dummy_environment import DummyEnvironment +from ..dummy_particulator import DummyParticulator + + +def make_indexed_storage(backend, iterable, idx=None): + index = make_Index(backend).from_ndarray(np.array(iterable)) + if idx is not None: + result = make_IndexedStorage(backend).indexed(idx, index) + else: + result = index + return result + + +# pylint: disable=protected-access +class TestParticleAttributes: + @staticmethod + @pytest.mark.parametrize( + "water_mass, multiplicity", + [ + pytest.param(np.array([1.0, 1, 1, 1]), np.array([1, 1, 1, 1])), + pytest.param(np.array([1.0, 2, 1, 1]), np.array([2, 0, 2, 0])), + pytest.param(np.array([1.0, 1, 4]), np.array([5, 0, 0])), + ], + ) + def test_housekeeping(backend_class, water_mass, multiplicity): + # Arrange + particulator = DummyParticulator(backend_class, n_sd=len(multiplicity)) + attributes = {"multiplicity": multiplicity, "water mass": water_mass} + particulator.build(attributes, int_caster=np.int64) + sut = particulator.attributes + sut.healthy = False + + # Act + sut.sanitize() + _ = sut.super_droplet_count + + # Assert + assert sut.super_droplet_count == (multiplicity != 0).sum() + assert sut["multiplicity"].to_ndarray().sum() == multiplicity.sum() + assert ( + sut["water mass"].to_ndarray() * sut["multiplicity"].to_ndarray() + ).sum() == (water_mass * multiplicity).sum() + + @staticmethod + @pytest.mark.parametrize( + "multiplicity, cells, idx, new_idx, cell_start", + [ + ([1, 1, 1], [2, 0, 1], [2, 0, 1], [1, 2, 0], [0, 1, 2, 3]), + ( + [0, 1, 0, 1, 1], + [3, 4, 0, 1, 2], + [4, 1, 3, 2, 0], + [3, 4, 1], + [0, 0, 1, 2, 2, 3], + ), + ( + [1, 2, 3, 4, 5, 6, 0], + [2, 2, 2, 2, 1, 1, 1], + [0, 1, 2, 3, 4, 5, 6], + [4, 5, 0, 1, 2, 3], + [0, 0, 2, 6], + ), + ], + ) + @pytest.mark.parametrize( + "backend_cls", + (Numba, pytest.param(ThrustRTC, marks=pytest.mark.xfail(strict=True))), + ) # TODO #330 + def test_sort_by_cell_id( + *, backend_cls, multiplicity, cells, idx, new_idx, cell_start + ): + # Arrange + n_sd = len(multiplicity) + particulator = DummyParticulator(backend_cls, n_sd=n_sd) + n_cell = max(cells) + 1 + particulator.environment.mesh.n_cell = n_cell + particulator.build(attributes={"multiplicity": np.ones(n_sd)}) + sut = particulator.attributes + sut._ParticleAttributes__idx = make_indexed_storage(particulator.backend, idx) + sut._ParticleAttributes__attributes["multiplicity"].data = make_indexed_storage( + particulator.backend, multiplicity, sut._ParticleAttributes__idx + ) + sut._ParticleAttributes__attributes["cell id"].data = make_indexed_storage( + particulator.backend, cells, sut._ParticleAttributes__idx + ) + sut._ParticleAttributes__cell_start = make_indexed_storage( + particulator.backend, [0] * (n_cell + 1) + ) + sut._ParticleAttributes__n_sd = particulator.n_sd + sut.healthy = 0 not in multiplicity + sut._ParticleAttributes__cell_caretaker = ( + particulator.backend.make_cell_caretaker( + sut._ParticleAttributes__idx.shape, + sut._ParticleAttributes__idx.dtype, + len(sut._ParticleAttributes__cell_start), + ) + ) + + # Act + sut.sanitize() + sut._ParticleAttributes__sort_by_cell_id() + + # Assert + np.testing.assert_array_equal( + np.array(new_idx), + sut._ParticleAttributes__idx.to_ndarray()[: sut.super_droplet_count], + ) + np.testing.assert_array_equal( + np.array(cell_start), sut._ParticleAttributes__cell_start.to_ndarray() + ) + + @staticmethod + def test_recalculate_cell_id(backend_class): + # Arrange + multiplicity = np.ones(1, dtype=np.int64) + droplet_id = 0 + initial_position = np.array([[0], [0]]) + grid = (1, 1) + particulator = DummyParticulator(backend_class, n_sd=1) + particulator.environment = DummyEnvironment(grid=grid) + cell_id, cell_origin, position_in_cell = particulator.mesh.cellular_attributes( + initial_position + ) + cell_origin[0, droplet_id] = 0.1 + cell_origin[1, droplet_id] = 0.2 + cell_id[droplet_id] = -1 + attribute = { + "multiplicity": multiplicity, + "cell id": cell_id, + "cell origin": cell_origin, + "position in cell": position_in_cell, + } + particulator.build(attribute) + + # Act + particulator.recalculate_cell_id() + + # Assert + assert particulator.attributes["cell id"][droplet_id] == 0 + + @staticmethod + def test_permutation_global_as_implemented_in_numba(): + n_sd = 8 + u01 = [0.1, 0.4, 0.2, 0.5, 0.9, 0.1, 0.6, 0.3] + + # Arrange + particulator = DummyParticulator(Numba, n_sd=n_sd) + sut = ParticleAttributesFactory.empty_particles(particulator, n_sd) + idx_length = len(sut._ParticleAttributes__idx) + sut._ParticleAttributes__tmp_idx = make_indexed_storage( + particulator.backend, [0] * idx_length + ) + sut._ParticleAttributes__sorted = True + sut._ParticleAttributes__n_sd = particulator.n_sd + u01 = make_indexed_storage(particulator.backend, u01) + + # Act + sut.permutation(u01, local=False) + + # Assert + expected = np.array([1, 3, 5, 7, 6, 0, 4, 2]) + np.testing.assert_array_equal(sut._ParticleAttributes__idx, expected) + assert not sut._ParticleAttributes__sorted + + @staticmethod + def test_permutation_local(backend_class): + if backend_class is ThrustRTC: + pytest.skip("TODO #358") + n_sd = 8 + u01 = [0.1, 0.4, 0.2, 0.5, 0.9, 0.1, 0.6, 0.3] + cell_start = [0, 0, 2, 5, 7, n_sd] + + # Arrange + particulator = DummyParticulator(backend_class, n_sd=n_sd) + sut = ParticleAttributesFactory.empty_particles(particulator, n_sd) + idx_length = len(sut._ParticleAttributes__idx) + sut._ParticleAttributes__tmp_idx = make_indexed_storage( + particulator.backend, [0] * idx_length + ) + sut._ParticleAttributes__cell_start = make_indexed_storage( + particulator.backend, cell_start + ) + sut._ParticleAttributes__sorted = True + sut._ParticleAttributes__n_sd = particulator.n_sd + u01 = make_indexed_storage(particulator.backend, u01) + + # Act + sut.permutation(u01, local=True) + + # Assert + expected = np.array([1, 0, 2, 3, 4, 5, 6, 7]) + np.testing.assert_array_equal(sut._ParticleAttributes__idx, expected) + assert sut._ParticleAttributes__sorted + + @staticmethod + def test_permutation_global_repeatable(backend_class): + if backend_class is ThrustRTC: + pytest.skip("TODO #328") + + n_sd = 800 + u01 = np.random.random(n_sd) + + # Arrange + particulator = DummyParticulator(backend_class, n_sd=n_sd) + sut = ParticleAttributesFactory.empty_particles(particulator, n_sd) + idx_length = len(sut._ParticleAttributes__idx) + sut._ParticleAttributes__tmp_idx = make_indexed_storage( + particulator.backend, [0] * idx_length + ) + sut._ParticleAttributes__sorted = True + u01 = make_indexed_storage(particulator.backend, u01) + + # Act + sut.permutation(u01, local=False) + expected = sut._ParticleAttributes__idx.to_ndarray() + sut._ParticleAttributes__sorted = True + sut._ParticleAttributes__idx = make_indexed_storage( + particulator.backend, range(n_sd) + ) + sut.permutation(u01, local=False) + + # Assert + np.testing.assert_array_equal(sut._ParticleAttributes__idx, expected) + assert not sut._ParticleAttributes__sorted + + @staticmethod + def test_permutation_local_repeatable(backend_class): + if backend_class is ThrustRTC: + pytest.skip("TODO #358") + n_sd = 800 + idx = range(n_sd) + u01 = np.random.random(n_sd) + cell_start = [0, 0, 20, 250, 700, n_sd] + + # Arrange + particulator = DummyParticulator(backend_class, n_sd=n_sd) + cell_id = [] + particulator.environment.mesh.n_cell = len(cell_start) - 1 + for i in range(particulator.environment.mesh.n_cell): + cell_id += [i] * (cell_start[i + 1] - cell_start[i]) + assert len(cell_id) == n_sd + particulator.build(attributes={"multiplicity": np.ones(n_sd)}) + sut = particulator.attributes + sut._ParticleAttributes__idx = make_indexed_storage(particulator.backend, idx) + idx_length = len(sut._ParticleAttributes__idx) + sut._ParticleAttributes__tmp_idx = make_indexed_storage( + particulator.backend, [0] * idx_length + ) + sut._ParticleAttributes__attributes["cell id"].data = make_indexed_storage( + particulator.backend, cell_id + ) + sut._ParticleAttributes__cell_start = make_indexed_storage( + particulator.backend, cell_start + ) + sut._ParticleAttributes__sorted = True + sut._ParticleAttributes__n_sd = particulator.n_sd + u01 = make_indexed_storage(particulator.backend, u01) + + # Act + sut.permutation(u01, local=True) + expected = sut._ParticleAttributes__idx.to_ndarray() + sut._ParticleAttributes__idx = make_indexed_storage(particulator.backend, idx) + sut.permutation(u01, local=True) + + # Assert + np.testing.assert_array_equal( + sut._ParticleAttributes__idx.to_ndarray(), expected + ) + assert sut._ParticleAttributes__sorted + + sut._ParticleAttributes__sort_by_cell_id() + np.testing.assert_array_equal( + sut._ParticleAttributes__idx.to_ndarray(), expected + ) diff --git a/PySDM/source/tests/unit_tests/initialisation/__init__.py b/PySDM/source/tests/unit_tests/initialisation/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/unit_tests/initialisation/test_aerosol_init.py b/PySDM/source/tests/unit_tests/initialisation/test_aerosol_init.py new file mode 100644 index 0000000000000000000000000000000000000000..66a64a6fbd06c6f79db4a6db3735c637c2713546 --- /dev/null +++ b/PySDM/source/tests/unit_tests/initialisation/test_aerosol_init.py @@ -0,0 +1,55 @@ +# pylint: disable=missing-module-docstring +import numpy as np +import pytest +from chempy import Substance + +# from PySDM.initialisation import spectra +from PySDM import Formulae +from PySDM.initialisation.aerosol_composition import DryAerosolMixture +from PySDM.physics import si + + +@pytest.mark.parametrize( + "mass_fractions", + [ + pytest.param({"(NH4)2SO4": 1.0, "insoluble": 0.0}), + pytest.param({"(NH4)2SO4": 0.5, "insoluble": 0.5}), + pytest.param({"(NH4)2SO4": 0.0, "insoluble": 1.0}), + ], +) +def test_volume_weighted_kappa_with_insoluble_compound(mass_fractions): + # Arrange + const = Formulae().constants + water_molar_volume = const.Mv / const.rho_w + compounds = ("(NH4)2SO4", "insoluble") + molar_masses = { + "(NH4)2SO4": Substance.from_formula("(NH4)2SO4").mass * si.gram / si.mole, + "insoluble": 44 * si.g / si.mole, + } + densities = { + "(NH4)2SO4": 1.77 * si.g / si.cm**3, + "insoluble": 1.2 * si.g / si.cm**3, + } + is_soluble = {"(NH4)2SO4": True, "insoluble": False} + ionic_dissociation_phi = {"(NH4)2SO4": 3, "insoluble": 0} + + aer = DryAerosolMixture( + compounds=compounds, + densities=densities, + molar_masses=molar_masses, + is_soluble=is_soluble, + ionic_dissociation_phi=ionic_dissociation_phi, + ) + + # Act + kappa_expected = aer.kappa(mass_fractions, water_molar_volume)["Constant"] + volume_fractions = aer.volume_fractions(mass_fractions) + + # Assert + compound_kappas = {"(NH4)2SO4": 0.72, "insoluble": 0.0} + kappa_actual = sum(v * volume_fractions[k] for (k, v) in compound_kappas.items()) + np.testing.assert_approx_equal( + kappa_expected, + kappa_actual, + significant=2, + ) diff --git a/PySDM/source/tests/unit_tests/initialisation/test_alpha_sampling.py b/PySDM/source/tests/unit_tests/initialisation/test_alpha_sampling.py new file mode 100644 index 0000000000000000000000000000000000000000..6764a175bdb68332ba673410edcb4ce365137a25 --- /dev/null +++ b/PySDM/source/tests/unit_tests/initialisation/test_alpha_sampling.py @@ -0,0 +1,59 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest + +from PySDM.initialisation.sampling import spectral_sampling +from PySDM.initialisation.spectra.exponential import Exponential +from PySDM.physics import si + + +@pytest.mark.parametrize( + "alpha_class, alias_class, alphavalue", + ( + pytest.param( + (spectral_sampling.AlphaSampling), + (spectral_sampling.Linear), + (1), + ), + pytest.param( + (spectral_sampling.AlphaSampling), + (spectral_sampling.ConstantMultiplicity), + (0), + ), + ), +) +@pytest.mark.parametrize("method", ("deterministic", "quasirandom", "pseudorandom")) +def test_spectral_discretisation( + alpha_class, alias_class, alphavalue, backend_instance, method +): + # Arrange + n_sd = 100 + backend = backend_instance + + spectrum = Exponential( + norm_factor=2**23 / si.metre**3, + scale=0.03 * si.micrometre, + ) + + error_threshold = None if method != "pseudorandom" else 0.1 + alpha = alpha_class( + spectrum, + alpha=alphavalue, + dist_1_inv=lambda y, size_range: (size_range[1] - size_range[0]) * y + + size_range[0], + interp_points=10000, + error_threshold=error_threshold, + ) + alias = alias_class(spectrum, error_threshold=error_threshold) + + # Act + m_alpha, n_alpha = getattr(alpha, f"sample_{method}")(n_sd, backend=backend) + m_alias, n_alias = getattr(alias, f"sample_{method}")(n_sd, backend=backend) + + # Assert + np.testing.assert_allclose( + m_alpha, m_alias, rtol=1e-3, atol=1e-6, err_msg="Size bins do not match" + ) + np.testing.assert_allclose( + n_alpha, n_alias, rtol=1e-3, atol=1e-6, err_msg="Multiplicity does not match" + ) diff --git a/PySDM/source/tests/unit_tests/initialisation/test_discretise_multiplicities.py b/PySDM/source/tests/unit_tests/initialisation/test_discretise_multiplicities.py new file mode 100644 index 0000000000000000000000000000000000000000..4c1ddd0632147a915f6d911c31c3627e87d5b1b5 --- /dev/null +++ b/PySDM/source/tests/unit_tests/initialisation/test_discretise_multiplicities.py @@ -0,0 +1,79 @@ +"""checks verifying imeplementation of `discretise_multiplicities` function""" + +import pytest +import numpy as np + +from PySDM.initialisation import discretise_multiplicities + + +class TestDiscretiseMultiplicities: + @staticmethod + def test_reporting_zero_multiplicity(): + """should raise an ValueError if int-casting results in zeros""" + # arrange + values = np.asarray([0.1], dtype=np.float64) + + # act + with pytest.raises(ValueError) as excinfo: + discretise_multiplicities(values) + + # assert + assert "int-casting resulted in multiplicity of zero" in str(excinfo.value) + + @staticmethod + def test_reporting_sum_error(): + """should err if sum of int-casted values differs a lot from the sum of floats""" + # arrange + values = np.asarray( + [ + 1.49, + ], + dtype=np.float64, + ) + + # act + with pytest.raises(ValueError) as excinfo: + discretise_multiplicities(values) + + # assert + assert ( + "error in total real-droplet number due to casting multiplicities to ints" + in str(excinfo.value) + ) + + @staticmethod + def test_nans_converted_to_zeros(): + """checks for how NaN values are treated""" + # arrange + values = np.asarray( + [ + np.nan, + 1, + ], + dtype=np.float64, + ) + + # act + ints = discretise_multiplicities(values) + + # assert + assert ints[0] == 0 + + @staticmethod + def test_all_nans_to_all_zeros(): + """checks for how NaN values are treated""" + # arrange + values = np.asarray( + [ + np.nan, + np.nan, + np.nan, + ], + dtype=np.float64, + ) + + # act + ints = discretise_multiplicities(values) + + # assert + assert (ints == 0).all() diff --git a/PySDM/source/tests/unit_tests/initialisation/test_hygroscopic_equilibrium.py b/PySDM/source/tests/unit_tests/initialisation/test_hygroscopic_equilibrium.py new file mode 100644 index 0000000000000000000000000000000000000000..246b57bc0a110cb5b3ddd894bbba8183e7dadf73 --- /dev/null +++ b/PySDM/source/tests/unit_tests/initialisation/test_hygroscopic_equilibrium.py @@ -0,0 +1,169 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest +from matplotlib import pyplot + +from PySDM import Formulae, Builder +from PySDM.backends import Numba +from PySDM.initialisation.hygroscopic_equilibrium import ( + equilibrate_wet_radii, + equilibrate_dry_radii, +) +from PySDM.physics import constants_defaults as const +from PySDM.physics import si +from PySDM.environments import Box + + +class TestHygroscopicEquilibrium: + @staticmethod + @pytest.mark.parametrize("r_dry", (pytest.param(2.4e-09), pytest.param(2.5e-09))) + @pytest.mark.parametrize( + "surface_tension", + ("Constant", "CompressedFilmOvadnevaite", "SzyszkowskiLangmuir"), + ) + def test_equilibrate_wet_radii(r_dry, surface_tension, plot=False): + # Arrange + T = 280.0 + RH = 0.9 + f_org = 0.607 + kappa = 0.356 + + class Particulator: # pylint: disable=too-few-public-methods + formulae = Formulae( + surface_tension=surface_tension, + constants={ + "sgm_org": 40 * si.mN / si.m, + "delta_min": 0.1 * si.nm, + "RUEHL_nu_org": 7.47e-05, + "RUEHL_A0": 2.5e-19 * si.m**2, + "RUEHL_C0": 1e-5, + "RUEHL_sgm_min": 40 * si.mN / si.m, + }, + ) + + class Env: # pylint: disable=too-few-public-methods + particulator = Particulator() + thermo = { + "T": Numba.Storage.from_ndarray(np.full(1, T)), + "RH": Numba.Storage.from_ndarray(np.full(1, RH)), + } + + def __getitem__(self, item): + return self.thermo[item] + + r_dry_arr = np.full(1, r_dry) + + # Plot + r_wet = np.logspace(np.log(0.9 * r_dry), np.log(10 * si.nm), base=np.e, num=100) + sigma = Env.particulator.formulae.surface_tension.sigma( + T, + Env.particulator.formulae.trivia.volume(r_wet), + Env.particulator.formulae.trivia.volume(r_dry), + f_org, + ) + RH_eq = Env.particulator.formulae.hygroscopicity.RH_eq( + r_wet, T, kappa, r_dry**3, sigma + ) + + pyplot.plot(r_wet / si.nm, (RH_eq - 1) * 100, label="RH_eq") + pyplot.axhline((RH - 1) * 100, color="orange", label="RH") + pyplot.axvline(r_dry / si.nm, label="a", color="red") + pyplot.axvline( + Env.particulator.formulae.hygroscopicity.r_cr( + kappa, r_dry**3, T, const.sgm_w + ) + / si.nm, + color="green", + label="b", + ) + pyplot.grid() + pyplot.xscale("log") + pyplot.xlabel("Wet radius [nm]") + pyplot.xlim(r_wet[0] / si.nm, r_wet[-1] / si.nm) + pyplot.ylabel("Equilibrium supersaturation [%]") + pyplot.legend() + if plot: + pyplot.show() + else: + pyplot.clf() + + # Act + env = Env() + r_wet = equilibrate_wet_radii( + r_dry=r_dry_arr, + environment=env, + kappa_times_dry_volume=Env.particulator.formulae.trivia.volume(r_dry_arr) + * kappa, + f_org=np.full_like(r_dry_arr, f_org), + ) + + # Assert + assert (r_wet >= r_dry_arr).all() + + @staticmethod + @pytest.mark.parametrize( + "kappas, temperature, relative_humidity", + ( + pytest.param([0.0], 300 * si.K, 0.95, marks=pytest.mark.xfail(strict=True)), + ([0.5, 1.0, 1.5], 300 * si.K, 0.95), + ([0.5, 1.0, 1.5], 250 * si.K, 0.75), + ), + ) + def test_equilibrate_dry_radii( + kappas, temperature, relative_humidity, backend_instance, plot=False + ): + # arrange + r_wet = np.logspace(-8, -3, num=10) + builder = Builder( + environment=Box(dv=np.nan, dt=np.nan), + backend=backend_instance, + n_sd=len(r_wet), + ) + builder.particulator.environment["T"] = temperature + builder.particulator.environment["RH"] = relative_humidity + + # act + r_dry = {} + for kappa in kappas: + r_dry[kappa] = equilibrate_dry_radii( + r_wet=r_wet, + environment=builder.particulator.environment, + kappa=kappa if kappa is np.ndarray else np.full_like(r_wet, kappa), + ) + + # plot + for kappa in kappas: + pyplot.loglog(r_wet, r_dry[kappa], label=f"{kappa=}") + pyplot.gca().set( + ylim=(r_wet[0], r_wet[-1]), + xlabel="wet radius [m]", + ylabel="dry radius [m]", + title=f"{temperature=} {relative_humidity=}", + ) + pyplot.grid() + pyplot.legend() + if plot: + pyplot.show() + else: + pyplot.clf() + + # assert + assert (np.diff(kappas) > 0).all() + kappa_prev = None + for kappa in kappas: + assert (r_dry[kappa] < r_wet).all() + + if kappa_prev is not None: + assert (r_dry[kappa] < r_dry[kappa_prev]).all() + kappa_prev = kappa + + np.testing.assert_allclose( + rtol=1e-5, + desired=r_wet, + actual=equilibrate_wet_radii( + r_dry=r_dry[kappa], + kappa_times_dry_volume=kappa + * builder.particulator.formulae.trivia.volume(r_dry[kappa]), + environment=builder.particulator.environment, + ), + ) diff --git a/PySDM/source/tests/unit_tests/initialisation/test_init_fall_momenta.py b/PySDM/source/tests/unit_tests/initialisation/test_init_fall_momenta.py new file mode 100644 index 0000000000000000000000000000000000000000..47c143cddcf300714cd5b781d1c24e1447267e50 --- /dev/null +++ b/PySDM/source/tests/unit_tests/initialisation/test_init_fall_momenta.py @@ -0,0 +1,71 @@ +""" +Test initialization of +`PySDM.attributes.physics.relative_fall_velocity.RelativeFallMomentum` +""" + +import numpy as np +import pytest + +from PySDM.builder import Builder +from PySDM.environments.box import Box +from PySDM.initialisation import init_fall_momenta +from PySDM.physics import si + + +@pytest.fixture( + name="params", + params=( + pytest.param( + { + "multiplicity": np.array([1, 2, 3, 2]), + "water mass": np.array( + [ + 1 * si.mg, + 0.1 * si.mg, + 1 * si.mg, + 0.05 * si.mg, + ] + ), + }, + ), + ), +) +def params_fixture(request): + return request.param + + +class TestInitFallMomenta: + @staticmethod + def test_init_to_terminal_velocity(params, backend_instance): + """ + Fall momenta correctly initialized to the terminal velocity * mass. + """ + env = Box(dt=1, dv=1) + builder = Builder( + n_sd=len(params["multiplicity"]), backend=backend_instance, environment=env + ) + builder.request_attribute("terminal velocity") + particulator = builder.build( + attributes={ + "multiplicity": params["multiplicity"], + "water mass": params["water mass"], + }, + products=(), + ) + + terminal_momentum = ( + particulator.attributes["terminal velocity"].to_ndarray() + * params["water mass"] + ) + + assert np.allclose(init_fall_momenta(params["water mass"]), terminal_momentum) + + @staticmethod + def test_init_to_zero(params): + """ + Fall momenta correctly initialized to zero. + """ + + fall_momenta = init_fall_momenta(params["water mass"], zero=True) + + assert (fall_momenta == np.zeros_like(fall_momenta)).all() diff --git a/PySDM/source/tests/unit_tests/initialisation/test_spatial_discretisation.py b/PySDM/source/tests/unit_tests/initialisation/test_spatial_discretisation.py new file mode 100644 index 0000000000000000000000000000000000000000..0b8f3c19207a5c9d742c58645ff2e319cc378d00 --- /dev/null +++ b/PySDM/source/tests/unit_tests/initialisation/test_spatial_discretisation.py @@ -0,0 +1,87 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest + +from PySDM import Formulae +from PySDM.initialisation.sampling.spatial_sampling import Pseudorandom + + +@pytest.mark.parametrize( + "seeds", + ( + pytest.param( + (44, 33), marks=pytest.mark.xfail(strict=True), id="different seeds" + ), + pytest.param((44, 44), id="same seeds"), + ), +) +def test_pseudorandom_reproducible(seeds, backend_class): + # arrange + assert len(seeds) == 2 + backends = [backend_class(Formulae(seed=seed)) for seed in seeds] + grid = (12, 13) + n_sd = 14 + + # act + positions = [ + Pseudorandom.sample(backend=backend, grid=grid, n_sd=n_sd, z_part=None) + for backend in backends + ] + + # assert + np.testing.assert_array_equal(positions[0], positions[1]) + + +@pytest.mark.parametrize( + "z_range", + ( + pytest.param([0.0, 1.0], id="full range"), + pytest.param((0.5, 0.75), id="partial range"), + ), +) +def test_pseudorandom_zrange(z_range, backend_class): + # arrange + assert len(z_range) == 2 + backend = backend_class(Formulae()) + grid = (8,) + n_sd = 100 + + # act + positions = [ + Pseudorandom.sample(backend=backend, grid=grid, n_sd=n_sd, z_part=z_range) + ] + comp = np.ones_like(positions) + + # assert + np.testing.assert_array_less(positions, comp * z_range[1] * grid[0]) + np.testing.assert_array_less(comp * z_range[0] * grid[0], positions) + + +@pytest.mark.parametrize( + "z_range, x_range", + ( + pytest.param((0.0, 1.0), (0.0, 1.0), id="full range"), + pytest.param((0.5, 0.75), (0.5, 0.75), id="partial range"), + ), +) +def test_pseudorandom_x_z_range(z_range, x_range, backend_class): + # arrange + assert len(z_range) == 2 + assert len(x_range) == 2 + backend = backend_class(Formulae()) + grid = (8, 8) + n_sd = 100 + + # act + positions = Pseudorandom.sample( + backend=backend, grid=grid, n_sd=n_sd, z_part=z_range, x_part=x_range + ) + + for droplet in range(n_sd): + # assert z positions + np.testing.assert_array_less(positions[0][droplet], z_range[1] * grid[0]) + np.testing.assert_array_less(z_range[0] * grid[0], positions[0][droplet]) + + # assert x positions + np.testing.assert_array_less(positions[1][droplet], x_range[1] * grid[1]) + np.testing.assert_array_less(x_range[0] * grid[1], positions[1][droplet]) diff --git a/PySDM/source/tests/unit_tests/initialisation/test_spectra_lognormal.py b/PySDM/source/tests/unit_tests/initialisation/test_spectra_lognormal.py new file mode 100644 index 0000000000000000000000000000000000000000..7ca5a1d64e3da74dcb6ea21e69234edaa5025158 --- /dev/null +++ b/PySDM/source/tests/unit_tests/initialisation/test_spectra_lognormal.py @@ -0,0 +1,52 @@ +"""tests for lognormal probability distribution""" + +import numpy as np +import pytest +from PySDM.initialisation.spectra import Lognormal + + +class TestSpectraLognormal: + """checks ctor args against computed values""" + + @staticmethod + @pytest.mark.parametrize( + "m_mode, s_geom", + ( + (0.01, 1.5), + (0.1, 1.1), + (1, 1.01), + ), + ) + def test_median(m_mode, s_geom): + # arrange + sut = Lognormal(m_mode=m_mode, norm_factor=1, s_geom=s_geom) + + # act + median = sut.percentiles(0.5) + + # assert + assert median == m_mode + + @staticmethod + @pytest.mark.parametrize( + "m_mode, s_geom", + ( + (0.01, 3.5), + (0.1, 2.1), + (1, 1.5), + ), + ) + def test_mean(m_mode, s_geom): + # arrange + sut = Lognormal(m_mode=m_mode, norm_factor=1, s_geom=s_geom) + x = np.linspace(m_mode / 1000, m_mode * 1000, num=10000) + + # act + mean = np.sum(sut.pdf(x) * x) / np.sum(sut.pdf(x)) + + # assert + np.testing.assert_approx_equal( + actual=np.log(m_mode) + 0.5 * np.log(s_geom) ** 2, + desired=np.log(mean), + significant=3, + ) diff --git a/PySDM/source/tests/unit_tests/initialisation/test_spectra_sum.py b/PySDM/source/tests/unit_tests/initialisation/test_spectra_sum.py new file mode 100644 index 0000000000000000000000000000000000000000..92354756528b5548878c37e87da5996140694a7a --- /dev/null +++ b/PySDM/source/tests/unit_tests/initialisation/test_spectra_sum.py @@ -0,0 +1,27 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest + +from PySDM.initialisation.spectra import Sum, Gaussian + + +@pytest.mark.parametrize("fun", ("pdf", "cdf")) +@pytest.mark.parametrize("x", (-10, -1, 0, 1, 10)) +def test_spectra_sum(fun, x): + # arrange + n1, n2 = 100, 200 + dist1 = Gaussian(norm_factor=n1, loc=1, scale=0.5) + dist2 = Gaussian(norm_factor=n2, loc=2, scale=1.5) + + # act + sut = Sum((dist1, dist2)) + + # assert + np.testing.assert_approx_equal( + actual=getattr(sut, fun)(x), + desired=( + getattr(dist1, fun)(x) * n1 / (n1 + n2) + + getattr(dist2, fun)(x) * n2 / (n1 + n2) + ), + significant=15, + ) diff --git a/PySDM/source/tests/unit_tests/initialisation/test_spectral_discretisation.py b/PySDM/source/tests/unit_tests/initialisation/test_spectral_discretisation.py new file mode 100644 index 0000000000000000000000000000000000000000..91b98d8935d10544405b7453ae0940c195a2ec25 --- /dev/null +++ b/PySDM/source/tests/unit_tests/initialisation/test_spectral_discretisation.py @@ -0,0 +1,95 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest +from matplotlib import pyplot +from PySDM import Formulae +from PySDM.initialisation.sampling import spectral_sampling +from PySDM.initialisation import spectra +from PySDM.physics import si + +m_mode = 0.5e-5 +n_part = 256 * 16 +s_geom = 1.5 +spectrum = spectra.Lognormal(n_part, m_mode, s_geom) +m_range = (0.1 * si.um, 100 * si.um) +formulae = Formulae() + + +class TestSpectralDiscretisation: + @staticmethod + @pytest.mark.parametrize( + "discretisation", + ( + pytest.param(spectral_sampling.Linear(spectrum, size_range=m_range)), + pytest.param(spectral_sampling.Logarithmic(spectrum, size_range=m_range)), + pytest.param( + spectral_sampling.ConstantMultiplicity(spectrum, size_range=m_range) + ), + pytest.param(spectral_sampling.Linear(spectrum)), + pytest.param(spectral_sampling.Logarithmic(spectrum)), + pytest.param(spectral_sampling.ConstantMultiplicity(spectrum)), + ), + ) + @pytest.mark.parametrize("method", ("deterministic", "quasirandom", "pseudorandom")) + def test_all_methods_with_lognormal( + discretisation, method, backend_instance, plot=False + ): + # Arrange + n_sd = 10000 + + # Act + m, n = getattr(discretisation, f"sample_{method}")( + n_sd, backend=backend_instance + ) + + # Plot + pyplot.title(f"{discretisation.__class__.__name__} {method=}") + pyplot.scatter(m, n) + if plot: + pyplot.show() + else: + pyplot.clf() + + # Assert + assert m.shape == n.shape + assert n.shape == (n_sd,) + assert np.min(m) >= m_range[0] + assert np.max(m) <= m_range[1] + actual = np.sum(n) + desired = spectrum.cumulative(m_range[1]) - spectrum.cumulative(m_range[0]) + np.testing.assert_approx_equal( + actual=actual / desired, + desired=1.0, + significant=2 if method == "pseudorandom" else 4, + ) + + @staticmethod + @pytest.mark.parametrize( + "sampling_class, error_threshold", + ( + (spectral_sampling.ConstantMultiplicity, None), + (spectral_sampling.Logarithmic, None), + (spectral_sampling.Linear, None), + pytest.param( + spectral_sampling.ConstantMultiplicity, + 1e-5, + marks=pytest.mark.xfail(strict=True, raises=ValueError), + ), + pytest.param( + spectral_sampling.Logarithmic, + 1e-5, + marks=pytest.mark.xfail(strict=True, raises=ValueError), + ), + pytest.param( + spectral_sampling.Linear, + 1e-5, + marks=pytest.mark.xfail(strict=True, raises=ValueError), + ), + ), + ) + def test_error_threshold_with_deterministic_sampling( + sampling_class, error_threshold + ): + sampling_class( + spectra.Lognormal(n_part, m_mode, s_geom), error_threshold=error_threshold + ).sample_deterministic(n_sd=10) diff --git a/PySDM/source/tests/unit_tests/initialisation/test_spectro_glacial_discretisation.py b/PySDM/source/tests/unit_tests/initialisation/test_spectro_glacial_discretisation.py new file mode 100644 index 0000000000000000000000000000000000000000..00a72cc85fef54ce8555ce4546da95eec0f8108a --- /dev/null +++ b/PySDM/source/tests/unit_tests/initialisation/test_spectro_glacial_discretisation.py @@ -0,0 +1,57 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest + +from PySDM import Formulae +from PySDM.initialisation.sampling import spectro_glacial_sampling +from PySDM.initialisation.spectra.lognormal import Lognormal +from PySDM.physics import si + +formulae = Formulae( + freezing_temperature_spectrum="Niemand_et_al_2012", + constants={"NIEMAND_A": -0.517, "NIEMAND_B": 8.934}, +) +spectrum = Lognormal( + norm_factor=1, + m_mode=formulae.trivia.sphere_surface(diameter=0.74 * si.um), + s_geom=np.exp(0.25), +) +m_range = ( + formulae.trivia.sphere_surface(diameter=0.01 * si.um), + formulae.trivia.sphere_surface(diameter=100.0 * si.um), +) + + +@pytest.mark.parametrize( + "discretisation", + ( + pytest.param( + spectro_glacial_sampling.SpectroGlacialSampling( + freezing_temperature_spectrum=formulae.freezing_temperature_spectrum, + insoluble_surface_spectrum=spectrum, + ) + ), + ), +) +def test_spectral_discretisation(discretisation, backend_instance): + # Arrange + n_sd = 100000 + backend = backend_instance + + # Act + freezing_temperatures, immersed_surfaces, n = discretisation.sample( + n_sd=n_sd, backend=backend + ) + + # Assert + assert n.shape == (n_sd,) + actual = np.sum(n) + desired = spectrum.cumulative(m_range[1]) - spectrum.cumulative(m_range[0]) + quotient = actual / desired + np.testing.assert_approx_equal(actual=quotient, desired=1.0, significant=2) + + assert (formulae.constants.T0 - 50 < freezing_temperatures).all() + assert (freezing_temperatures < formulae.constants.T0).all() + + assert (immersed_surfaces < m_range[1]).all() + assert (immersed_surfaces > m_range[0]).all() diff --git a/PySDM/source/tests/unit_tests/physics/__init__.py b/PySDM/source/tests/unit_tests/physics/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/unit_tests/physics/test_accommodation_coefficients.py b/PySDM/source/tests/unit_tests/physics/test_accommodation_coefficients.py new file mode 100644 index 0000000000000000000000000000000000000000..97c5c20159b5a8aa24a4b59689b33e0438f77201 --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_accommodation_coefficients.py @@ -0,0 +1,38 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest + +from PySDM import Formulae + + +@pytest.mark.parametrize( + "constants", + ( + {"MAC": 1.0, "HAC": 1.0}, + {"MAC": 1.0, "HAC": 0.1}, + {"MAC": 0.1, "HAC": 1.0}, + {"MAC": 0.1, "HAC": 0.1}, + ), +) +def test_accomodation_coefficients(constants): + # arrange + formulae = Formulae(constants=constants) + D = 1 + K = 2 + r = 3 + lmbd = 4 + + # act + D_dk = formulae.diffusion_kinetics.D(D, r, lmbd) + K_dk = formulae.diffusion_kinetics.K(K, r, lmbd) + + # assert + Kn = lmbd / r + xx_D = 4 / 3 / constants["MAC"] + np.testing.assert_almost_equal( + D_dk, D * (1 + Kn) / (1 + (xx_D + 0.377) * Kn + xx_D * Kn**2) + ) + xx_K = 4 / 3 / constants["HAC"] + np.testing.assert_almost_equal( + K_dk, K * (1 + Kn) / (1 + (xx_K + 0.377) * Kn + xx_K * Kn**2) + ) diff --git a/PySDM/source/tests/unit_tests/physics/test_air_dynamic_viscosity.py b/PySDM/source/tests/unit_tests/physics/test_air_dynamic_viscosity.py new file mode 100644 index 0000000000000000000000000000000000000000..c1a25f58e8b03be06fa1e1a72b677293995b911c --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_air_dynamic_viscosity.py @@ -0,0 +1,46 @@ +"""tests for air dynamic viscosity formulae""" + +import pytest +import numpy as np +from PySDM.physics.dimensional_analysis import DimensionalAnalysis +from PySDM.formulae import _choices, Formulae +from PySDM.physics import air_dynamic_viscosity, constants_defaults, si + + +class TestAirDynamicViscosity: + @staticmethod + @pytest.mark.parametrize("variant", _choices(air_dynamic_viscosity)) + def test_dimensionality(variant): + with DimensionalAnalysis(): + # Arrange + formulae = Formulae(air_dynamic_viscosity=variant) + cdsi = constants_defaults.si + sut = formulae.air_dynamic_viscosity.eta_air + + # Act + re = sut(temperature=300 * cdsi.K) + + # Assert + assert re.check("[pressure]*[time]") + + @staticmethod + @pytest.mark.parametrize( + "T, eta", + ( + (127.232 * si.K, 0.000010242 * si.Pa * si.s), + (287.946 * si.K, 0.000017691 * si.Pa * si.s), + (455.357 * si.K, 0.000024581 * si.Pa * si.s), + (703.125 * si.K, 0.000033520 * si.Pa * si.s), + (1011.16 * si.K, 0.000043203 * si.Pa * si.s), + (1332.59 * si.K, 0.000052328 * si.Pa * si.s), + (1660.71 * si.K, 0.000059963 * si.Pa * si.s), + (1962.05 * si.K, 0.000067598 * si.Pa * si.s), + (2270.09 * si.K, 0.000075047 * si.Pa * si.s), + (2537.95 * si.K, 0.000082495 * si.Pa * si.s), + (2812.50 * si.K, 0.000090503 * si.Pa * si.s), + ), + ) + def test_vs_digitized_data_from_Zografos_et_al_1987_figure_A5(T, eta): + formulae = Formulae(air_dynamic_viscosity="ZografosEtAl1987") + eta_test = formulae.air_dynamic_viscosity.eta_air(T) + np.testing.assert_approx_equal(eta_test, eta, significant=2.4) diff --git a/PySDM/source/tests/unit_tests/physics/test_bulk_phase_partitioning.py b/PySDM/source/tests/unit_tests/physics/test_bulk_phase_partitioning.py new file mode 100644 index 0000000000000000000000000000000000000000..7b5db2e3d3e4fd7626662b4e1035cab9d45ee0df --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_bulk_phase_partitioning.py @@ -0,0 +1,35 @@ +"""unit tests for bulk phase partitioning formulae""" + +import numpy as np +from matplotlib import pyplot +from PySDM import Formulae +from PySDM.physics import si + + +def test_bulk_phase_partitioning(plot=False): + """hello-world plot for bulk phase partitioning as formulated in Kaul et al. 2015""" + # arrange + sut = Formulae( + bulk_phase_partitioning="KaulEtAl2015", + constants={"bulk_phase_partitioning_exponent": 0.1}, + ).bulk_phase_partitioning.liquid_fraction + T = np.linspace(start=200, stop=300, num=100) * si.K + + # act + fl = sut(T) + + # plot + pyplot.plot(T, fl) + pyplot.xlabel("temperature [K]") + pyplot.ylabel("liquid fraction [1]") + pyplot.grid() + if plot: + pyplot.show() + else: + pyplot.clf() + + # assert + assert np.isfinite(fl).all() + assert np.amin(fl) == 0 + assert np.amax(fl) == 1 + assert (np.diff(fl) >= 0).all() diff --git a/PySDM/source/tests/unit_tests/physics/test_constants.py b/PySDM/source/tests/unit_tests/physics/test_constants.py new file mode 100644 index 0000000000000000000000000000000000000000..93146dcae4adadf9bb4bd8dad1b165206598a142 --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_constants.py @@ -0,0 +1,182 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import importlib +import os + +import numpy as np +import pint +import pytest +from scipy.constants import physical_constants +from chempy import Substance + +from PySDM import Formulae +from PySDM.physics import constants, constants_defaults, si + + +def consecutive_seeds(): + seeds = [] + for _ in range(5): + importlib.reload(constants) + seeds.append(constants.default_random_seed) + return np.asarray(seeds) + + +class TestConstants: + @staticmethod + def test_constant_seed_on_CI(): + CI = "CI" in os.environ + if not CI: + os.environ["CI"] = "1" + seeds = consecutive_seeds() + if not CI: + del os.environ["CI"] + assert (seeds == seeds[0]).all() + + @staticmethod + def test_variable_seed_outside_of_CI(): + CI = "CI" in os.environ + if CI: + CI = os.environ["CI"] + del os.environ["CI"] + seeds = consecutive_seeds() + if CI: + os.environ["CI"] = CI + assert (seeds[1:] != seeds[0]).any() + + @staticmethod + def test_standard_atmosphere_p(): + # arrange + pint_si = pint.UnitRegistry() + + # act + p = constants_defaults.p_STP * pint_si.Pa + + # assert + assert p == 1 * pint_si.atm + + @staticmethod + @pytest.mark.parametrize( + "item, value", + ( + ( + "M_1H", + 1.007825 * si.g / si.mole, + ), # https://en.wikipedia.org/wiki/Hydrogen-1 + ( + "M_2H", + 2.01410177811 * si.g / si.mole, + ), # https://en.wikipedia.org/wiki/Hydrogen-2 + ( + "M_3H", + 3.01604928 * si.g / si.mole, + ), # https://en.wikipedia.org/wiki/Hydrogen-3 + ( + "M_1H", + ( + physical_constants["proton molar mass"][0] + + physical_constants["electron molar mass"][0] + ) + * si.kg + / si.mole, + ), + ( + "M_2H", + ( + physical_constants["deuteron molar mass"][0] + + physical_constants["electron molar mass"][0] + ) + * si.kg + / si.mole, + ), + ( + "M_3H", + ( + physical_constants["triton molar mass"][0] + + physical_constants["electron molar mass"][0] + ) + * si.kg + / si.mole, + ), + ( + "M_16O", + 15.99491461956 * si.g / si.mole, + ), # https://en.wikipedia.org/wiki/Oxygen-16 + ( + "M_17O", + 16.9991315 * si.g / si.mole, + ), # https://en.wikipedia.org/wiki/Oxygen-17 + ( + "M_18O", + 17.9991610 * si.g / si.mole, + ), # https://en.wikipedia.org/wiki/Oxygen-18 + ), + ) + def test_isotope_molar_masses_vs_wikipedia_or_scipy(item, value): + np.testing.assert_approx_equal( + actual=getattr(constants_defaults, item), desired=value, significant=7 + ) + + @staticmethod + def test_vsmow_derived_molar_mass_vs_chempy_mean_water_molar_mass(): + np.testing.assert_approx_equal( + actual=Formulae().constants.Mv, + desired=Substance.from_formula("H2O").mass * si.gram / si.mole, + significant=5.5, + ) + + @staticmethod + def test_vsmow_derived_molar_mass(): + """fractional abundances (x_i) are calculated assuming + n_H_tot = n_1H + n_2H + n_3H + n_O_tot = n_16O + n_17O + n_18O + see [Hayes 2004](https://web.archive.org/web/20220629123450/https://web.gps.caltech.edu/~als/research-articles/other_stuff/hayes-2004-3.pdf) + """ # pylint: disable=line-too-long + const = Formulae().constants + trivia = Formulae().trivia + x_16O = trivia.isotopic_fraction_assuming_single_heavy_isotope( + isotopic_ratio=1 / (const.VSMOW_R_17O + const.VSMOW_R_18O) + ) + x_17O = const.VSMOW_R_17O * x_16O + x_18O = const.VSMOW_R_18O * x_16O + + x_1H = trivia.isotopic_fraction_assuming_single_heavy_isotope( + isotopic_ratio=1 / (const.VSMOW_R_2H + const.VSMOW_R_3H) + ) + x_2H = const.VSMOW_R_2H * x_1H + x_3H = const.VSMOW_R_3H * x_1H + + Mv = ( + 2 * (x_1H * const.M_1H + x_2H * const.M_2H + x_3H * const.M_3H) + + x_16O * const.M_16O + + x_17O * const.M_17O + + x_18O * const.M_18O + ) + np.testing.assert_approx_equal( + actual=Formulae().constants.Mv, + desired=Mv, + significant=5, + ) + + @staticmethod + @pytest.mark.parametrize( + "item, value", + (("Rd", 287 * si.J / si.K / si.kg), ("Rv", 461 * si.J / si.K / si.kg)), + ) + def test_gas_constants_vs_ams_glossary(item, value): + """vs. https://glossary.ametsoc.org/wiki/Gas_constant""" + np.testing.assert_allclose( + actual=getattr(Formulae().constants, item), desired=value, rtol=5e-3, atol=0 + ) + + @staticmethod + def test_e_mc2(): + assert constants_defaults.M_2H < ( + physical_constants["proton molar mass"][0] + + physical_constants["neutron molar mass"][0] + + physical_constants["electron molar mass"][0] + ) + assert constants_defaults.M_3H < ( + physical_constants["proton molar mass"][0] + + physical_constants["neutron molar mass"][0] + + physical_constants["neutron molar mass"][0] + + physical_constants["electron molar mass"][0] + ) diff --git a/PySDM/source/tests/unit_tests/physics/test_diffusion_ice_capacity.py b/PySDM/source/tests/unit_tests/physics/test_diffusion_ice_capacity.py new file mode 100644 index 0000000000000000000000000000000000000000..0e10c26022b213cfbef84fc7e7c9ed64ea7d6b72 --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_diffusion_ice_capacity.py @@ -0,0 +1,141 @@ +""" +test for diffusion ice capacity parametrisations +""" + +import pytest +from matplotlib import pyplot +import numpy as np +from PySDM.formulae import Formulae, _choices +from PySDM.physics import diffusion_ice_capacity +from PySDM.physics.dimensional_analysis import DimensionalAnalysis +from PySDM import physics + + +class TestDiffusionIceCapacity: + @staticmethod + @pytest.mark.parametrize("variant", _choices(diffusion_ice_capacity)) + def test_basics(variant, plot=False): + # arrange + si = physics.si + masses = np.logspace(base=10, start=-16, stop=-8.5, num=10) * si.kg + formulae = Formulae( + diffusion_ice_capacity=variant, + ) + sut = formulae.diffusion_ice_capacity + + # act + values = sut.capacity(masses) + + pyplot.xlabel("mass (kg)") + pyplot.ylabel("capacity (m)") + pyplot.xlim(masses[0], masses[-1]) + pyplot.xscale("log") + pyplot.ylim(1e-7, 5e-4) + pyplot.yscale("log") + pyplot.grid() + pyplot.plot(masses, values, color="black") + pyplot.title(f"variant={variant}") + # plot + if plot: + pyplot.show() + else: + pyplot.clf() + + # assert + assert (values > 0).all() + assert (np.diff(values) > 0).all() + + @staticmethod + @pytest.mark.parametrize("variant", _choices(diffusion_ice_capacity)) + def test_units(variant): + + with DimensionalAnalysis(): + # arrange + si = physics.si + formulae = Formulae( + diffusion_ice_capacity=variant, + ) + sut = formulae.diffusion_ice_capacity + mass = 1e-12 * si.kg + + # act + value = sut.capacity(mass) + + # assert + assert value.check("[length]") + + @staticmethod + @pytest.mark.parametrize( + "mass", (44 * physics.si.ng, 666 * physics.si.ug, 123 * physics.si.mg) + ) + def test_capacity_equals_radius_for_spherical(mass): + # arrange + formulae = Formulae( + diffusion_ice_capacity="Spherical", + particle_shape_and_density="MixedPhaseSpheres", + ) + sut = formulae.diffusion_ice_capacity + + # act + capacity = sut.capacity(mass) + + # assert + np.testing.assert_approx_equal( + desired=mass, + actual=-formulae.particle_shape_and_density.radius_to_mass(-capacity), + significant=15, + ) + + @staticmethod + @pytest.mark.parametrize("mass", (44 * physics.si.ng, 0.666 * physics.si.ug)) + def test_columnar_capacity_difference_from_spherical_capacity(mass): + # arrange + sut = { + key: Formulae(diffusion_ice_capacity=key).diffusion_ice_capacity + for key in ("Spherical", "Columnar") + } + + # act + values = {key: sut[key].capacity(mass) for key in sut.keys()} + + # assert + assert values["Spherical"] != values["Columnar"] + np.testing.assert_allclose( + values["Spherical"], + values["Columnar"], + rtol=0.2, + ) + + @staticmethod + @pytest.mark.parametrize( + "mass", + ( + pytest.param(1e-14 * physics.si.kg, marks=pytest.mark.xfail(strict=True)), + 5e-13 * physics.si.kg, + 1e-10 * physics.si.kg, + 1e-8 * physics.si.kg, + ), + ) + def test_prolate_ellipsoid_formula(mass): + """TODO #1670""" + # arrange + formulae = Formulae( + diffusion_ice_capacity="Columnar", + particle_shape_and_density="ColumnarIce", + ) + sut = formulae.diffusion_ice_capacity + columnar_shape = formulae.particle_shape_and_density + + # act + polar_diameter = 2 * columnar_shape.polar_radius_empirical_parametrisation(mass) + aspect_ratio = columnar_shape.aspect_ratio_empirical_parametrisation(mass) + eccentricity = columnar_shape.eccentricity(aspect_ratio) + capacity = sut.capacity(mass) + reference_capacity = sut.reference_capacity(polar_diameter, eccentricity) + + # assert + np.testing.assert_allclose( + capacity, + reference_capacity, + rtol=0.05, + ) diff --git a/PySDM/source/tests/unit_tests/physics/test_dimensional_analysis.py b/PySDM/source/tests/unit_tests/physics/test_dimensional_analysis.py new file mode 100644 index 0000000000000000000000000000000000000000..a0813a8cc5db8d63d242cd2e9cb64a8fd29672d9 --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_dimensional_analysis.py @@ -0,0 +1,37 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numba +import pytest + +from PySDM.formulae import Formulae +from PySDM.physics import constants_defaults +from PySDM.physics.dimensional_analysis import DimensionalAnalysis + +assert numba.config.DISABLE_JIT is not None # pylint: disable=no-member + + +class TestDimensionalAnalysis: + @staticmethod + def test_fake_units(): + # Arrange + sut = DimensionalAnalysis() + + # Act & Assert + assert isinstance(constants_defaults.D0, float) + with sut: + assert not isinstance(constants_defaults.D0, float) + assert isinstance(constants_defaults.D0.magnitude, float) + assert isinstance(constants_defaults.D0, float) + + @staticmethod + @pytest.mark.skipif("numba.config.DISABLE_JIT") + def test_fake_numba(): + # Arrange + sut = DimensionalAnalysis() + + # Act & Assert + assert hasattr(Formulae().saturation_vapour_pressure.pvs_water, "py_func") + with sut: + assert not hasattr( + Formulae().saturation_vapour_pressure.pvs_water, "py_func" + ) + assert hasattr(Formulae().saturation_vapour_pressure.pvs_water, "py_func") diff --git a/PySDM/source/tests/unit_tests/physics/test_drop_growth.py b/PySDM/source/tests/unit_tests/physics/test_drop_growth.py new file mode 100644 index 0000000000000000000000000000000000000000..74d39e04c9df36f652cef6ad7456c0a4eab6d435 --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_drop_growth.py @@ -0,0 +1,92 @@ +"""tests for drop growth formulae""" + +import pytest +from matplotlib import pyplot +import numpy as np + +from PySDM.formulae import _choices, Formulae +from PySDM.physics import drop_growth +from PySDM.physics.constants import PER_CENT, si, in_unit +from PySDM.physics.dimensional_analysis import DimensionalAnalysis + + +class TestDropGrowth: + @staticmethod + @pytest.mark.parametrize("paper", _choices(drop_growth)) + def test_unit(paper): + """checks dimensionality of the returned value""" + with DimensionalAnalysis(): + # arrange + formulae = Formulae(drop_growth=paper) + const = formulae.constants + + # act + r_dr_dt = formulae.drop_growth.r_dr_dt( + RH_eq=1, + RH=1.05, + Fk=formulae.drop_growth.Fk(T=const.T_tri, K=const.K0, lv=const.l_tri), + Fd=formulae.drop_growth.Fd(T=const.T_tri, D=const.D0, pvs=const.p_tri), + ) + + # assert + assert r_dr_dt.check("[area]/[time]") + + @staticmethod + @pytest.mark.parametrize( + ("paper_name", "error_range"), + ( + ("Howell1949", (0.02, 0.03)), + ("Mason1971", (-0.01, 0.01)), + ("Fick", (0.5, 0.9)), + ), + ) + def test_fick_mason_1971_vs_1971_difference_vs_temperature( + paper_name, error_range, plot=False + ): + """checks the relative difference between Mason's 1951 and 1971 formulae + for a range of temperatures""" + # arrange + temperatures = Formulae().trivia.C2K(np.linspace(-10, 40) * si.K) + papers = ("Howell1949", "Mason1971", "Fick") + relative_error = {} + # act + formulae = {paper: Formulae(drop_growth=paper) for paper in papers} + r_dr_dt = { + paper: formulae[paper].drop_growth.r_dr_dt( + RH_eq=1, + RH=1.05, + Fk=formulae[paper].drop_growth.Fk( + T=temperatures, + K=formulae[paper].constants.K0, + lv=formulae[paper].constants.l_tri, + ), + Fd=formulae[paper].drop_growth.Fd( + T=temperatures, + D=formulae[paper].constants.D0, + pvs=formulae[paper].constants.p_tri, + ), + ) + for paper in papers + } + + for paper in papers: + relative_error[paper] = r_dr_dt[paper] / r_dr_dt["Mason1971"] - 1 + + # plot + for paper in papers: + pyplot.plot( + temperatures, in_unit(relative_error[paper], PER_CENT), label=paper + ) + pyplot.title("") + pyplot.xlabel("temperature [K]") + pyplot.ylabel("r dr/dt relative difference (vs. 1971) [%]") + pyplot.grid() + pyplot.legend() + if plot: + pyplot.show() + else: + pyplot.clf() + + # assert + assert (abs(relative_error[paper_name]) > error_range[0]).all() + assert (abs(relative_error[paper_name]) < error_range[1]).all() diff --git a/PySDM/source/tests/unit_tests/physics/test_fake_unit_registry.py b/PySDM/source/tests/unit_tests/physics/test_fake_unit_registry.py new file mode 100644 index 0000000000000000000000000000000000000000..a1de677c2fc659250b2585ac60c9b313fb9c5554 --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_fake_unit_registry.py @@ -0,0 +1,15 @@ +"""checks for SI units conversions in FakeUnitRegistry""" + +from PySDM.physics import si + + +class TestFakeUnitRegistry: + @staticmethod + def test_d(): + """a check for the 'd' prefix""" + assert 44 * si.dm == 440 * si.cm + + @staticmethod + def test_deci(): + """a check for the 'deci' prefix""" + assert 44 * si.decimetre == 440 * si.centimetre diff --git a/PySDM/source/tests/unit_tests/physics/test_formulae.py b/PySDM/source/tests/unit_tests/physics/test_formulae.py new file mode 100644 index 0000000000000000000000000000000000000000..833b603d07ff786368e716fde1a652f30daacbc8 --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_formulae.py @@ -0,0 +1,165 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import pytest + +from PySDM.formulae import Formulae, _choices +from PySDM.physics import ( + constants_defaults, + diffusion_kinetics, + diffusion_thermics, + latent_heat_vapourisation, + saturation_vapour_pressure, +) +from PySDM.physics.dimensional_analysis import DimensionalAnalysis + + +class TestFormulae: + @staticmethod + @pytest.mark.parametrize("opt", _choices(saturation_vapour_pressure)) + def test_pvs_liq(opt): + with DimensionalAnalysis(): + # Arrange + formulae = Formulae(saturation_vapour_pressure=opt) + si = constants_defaults.si + sut = formulae.saturation_vapour_pressure.pvs_water + T = 300 * si.kelvins + constants_defaults.T0 + + # Act + pvs = sut(T) + + # Assert + assert pvs.check("[pressure]") + + @staticmethod + @pytest.mark.parametrize("opt", _choices(saturation_vapour_pressure)) + def test_pvs_ice(opt): + with DimensionalAnalysis(): + # Arrange + formulae = Formulae(saturation_vapour_pressure=opt) + si = constants_defaults.si + sut = formulae.saturation_vapour_pressure.pvs_ice + T = 250 * si.kelvins + constants_defaults.T0 + + # Act + pvs = sut(T) + + # Assert + assert pvs.check("[pressure]") + + @staticmethod + def test_r_cr(): + with DimensionalAnalysis(): + # Arrange + si = constants_defaults.si + formulae = Formulae() + sut = formulae.hygroscopicity.r_cr + + kp = 0.5 + rd = 0.1 * si.micrometre + T = 300 * si.kelvins + sgm = constants_defaults.sgm_w + + # Act + r_cr = sut(kp, rd**3, T, sgm) + + # Assert + assert r_cr.to_base_units().units == si.metres + + @staticmethod + @pytest.mark.parametrize("opt", _choices(latent_heat_vapourisation)) + def test_lv(opt): + with DimensionalAnalysis(): + # Arrange + si = constants_defaults.si + T = 300 * si.kelvins + + formulae = Formulae(latent_heat_vapourisation=opt) + sut = formulae.latent_heat_vapourisation.lv + + # Act + lv = sut(T) + + # Assert + assert lv.check("[energy]/[mass]") + + @staticmethod + @pytest.mark.parametrize("opt", _choices(diffusion_thermics)) + def test_thermal_conductivity_temperature_dependence(opt): + with DimensionalAnalysis(): + # Arrange + si = constants_defaults.si + T = 300 * si.kelvins + p = 1000 * si.hPa + + formulae = Formulae(diffusion_thermics=opt) + sut = formulae.diffusion_thermics.K + + # Act + thermal_conductivity = sut(T, p) + + # Assert + assert thermal_conductivity.check("[power]/[length]/[temperature]") + + @staticmethod + @pytest.mark.parametrize("opt", _choices(diffusion_kinetics)) + def test_thermal_conductivity_radius_dependence(opt): + with DimensionalAnalysis(): + # Arrange + si = constants_defaults.si + r = 1 * si.um + lmbd = 0.1 * si.um + + formulae = Formulae(diffusion_kinetics=opt) + sut = formulae.diffusion_kinetics.K + + # Act + thermal_conductivity = sut(constants_defaults.K0, r, lmbd) + + # Assert + assert thermal_conductivity.check("[power]/[length]/[temperature]") + + @staticmethod + @pytest.mark.parametrize("opt", _choices(diffusion_thermics)) + def test_vapour_diffusivity_temperature_dependence(opt): + with DimensionalAnalysis(): + # Arrange + si = constants_defaults.si + T = 300 * si.kelvins + p = 1000 * si.hPa + + formulae = Formulae(diffusion_thermics=opt) + sut = formulae.diffusion_thermics.D + + # Act + vpour_diffusivity = sut(T, p) + + # Assert + assert vpour_diffusivity.check("[area]/[time]") + + @staticmethod + @pytest.mark.parametrize("opt", _choices(diffusion_kinetics)) + def test_vapour_diffusivity_radius_dependence(opt): + with DimensionalAnalysis(): + # Arrange + si = constants_defaults.si + r = 1 * si.um + lmbd = 0.1 * si.um + + formulae = Formulae(diffusion_kinetics=opt) + sut = formulae.diffusion_kinetics.D + + # Act + vpour_diffusivity = sut(constants_defaults.D0, r, lmbd) + + # Assert + assert vpour_diffusivity.check("[area]/[time]") + + @staticmethod + def test___str__(): + # Arrange + sut = Formulae() + + # Act + result = str(sut) + + # Assert + assert len(result) > 0 diff --git a/PySDM/source/tests/unit_tests/physics/test_fragmentation_functions.py b/PySDM/source/tests/unit_tests/physics/test_fragmentation_functions.py new file mode 100644 index 0000000000000000000000000000000000000000..6fb1887f9ae962a5faceac0ee09eb89a1c79d5ea --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_fragmentation_functions.py @@ -0,0 +1,173 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np + +from PySDM import Formulae +from PySDM.physics.constants import si + + +class TestFragmentationFunctions: # pylint:disable=too-few-public-methods + @staticmethod + def test_straub_sigma1(): + # arrange + formulae = Formulae(fragmentation_function="Straub2010Nf") + + # act + params = formulae.fragmentation_function.params_sigma1(CW=30.0) + + # assert + np.testing.assert_array_almost_equal(params, [0.467381]) + + @staticmethod + def test_straub_mu1(): + # arrange + formulae = Formulae(fragmentation_function="Straub2010Nf") + + # act + params = formulae.fragmentation_function.params_mu1(sigma1=0.467381) + + # assert + np.testing.assert_array_almost_equal(params, [-7.933269]) + + @staticmethod + def test_straub_sigma2(): + # arrange + formulae = Formulae(fragmentation_function="Straub2010Nf") + + # act + params = formulae.fragmentation_function.params_sigma2(CW=30.0) + + # assert + np.testing.assert_array_almost_equal(params, [0.000182]) + + @staticmethod + def test_straub_mu2(): + # arrange + formulae = Formulae(fragmentation_function="Straub2010Nf") + + # act + params = formulae.fragmentation_function.params_mu2(ds=0.0) + + # assert + np.testing.assert_array_almost_equal(params, [0.00095]) + + @staticmethod + def test_straub_sigma3(): + # arrange + formulae = Formulae(fragmentation_function="Straub2010Nf") + + # act + params = formulae.fragmentation_function.params_sigma3(CW=30.0) + + # assert + np.testing.assert_array_almost_equal(params, [0.000149]) + + @staticmethod + def test_straub_mu3(): + # arrange + formulae = Formulae(fragmentation_function="Straub2010Nf") + + # act + params = formulae.fragmentation_function.params_mu3(ds=0.18 * si.cm) + + # assert + np.testing.assert_array_almost_equal(params, [0.00162]) + + @staticmethod + def test_ll82_pf1(): + # arrange + formulae = Formulae(fragmentation_function="LowList1982Nf") + + # act + params = formulae.fragmentation_function.params_f1( + dl=0.36 * si.cm, dcoal=0.3744 * si.cm + ) + # assert + np.testing.assert_array_equal( + params, [105.78851401149461, 0.36, 0.003771383856549656] + ) + + @staticmethod + def test_ll82_pf2(): + # arrange + formulae = Formulae(fragmentation_function="LowList1982Nf") + + # act + params = formulae.fragmentation_function.params_f2(ds=0.18 * si.cm) + + # assert + np.testing.assert_array_almost_equal( + params, (31.081892267202157, 0.18, 0.01283519925273017) + ) + + @staticmethod + def test_ll82_pf3(): + # arrange + formulae = Formulae(fragmentation_function="LowList1982Nf") + + # act + params = formulae.fragmentation_function.params_f3( + ds=0.0715 * si.cm, dl=0.18 * si.cm + ) + + # assert + np.testing.assert_array_almost_equal( + params, (11.078017412424996, -3.4579794266811095, 0.21024917628814235) + ) + + @staticmethod + def test_ll82_ps1(): + # arrange + formulae = Formulae(fragmentation_function="LowList1982Nf") + + # act + params = formulae.fragmentation_function.params_s1( + dl=0.36 * si.cm, ds=0.18 * si.cm, dcoal=0.3744 * si.cm + ) + + # assert + np.testing.assert_array_almost_equal( + params, (55.710586181217394, 0.36, 0.007344262785151853) + ) + + @staticmethod + def test_ll82_ps2(): + # arrange + formulae = Formulae(fragmentation_function="LowList1982Nf") + + # act + params = formulae.fragmentation_function.params_s2( + dl=0.36 * si.cm, ds=0.18 * si.cm, St=3.705e-6 * si.J + ) + + # assert + np.testing.assert_array_almost_equal( + params, (13.120297517162507, -2.0082590717125437, 0.24857168491193957) + ) + + @staticmethod + def test_ll82_pd1(): + # arrange + formulae = Formulae(fragmentation_function="LowList1982Nf") + + # act + params = formulae.fragmentation_function.params_d1( + W1=2.67, dl=0.36 * si.cm, dcoal=0.3744 * si.cm, CKE=8.55e-6 * si.J + ) + + # assert + np.testing.assert_array_almost_equal( + params, (24.080107809942664, 0.28666015630152986, 0.016567297254868083) + ) + + @staticmethod + def test_ll82_pd2(): + # arrange + formulae = Formulae(fragmentation_function="LowList1982Nf") + + # act + params = formulae.fragmentation_function.params_d2( + ds=0.18 * si.cm, dl=0.36 * si.cm, CKE=8.55e-6 * si.J + ) + + # assert + np.testing.assert_array_almost_equal(params, [0.0, -4.967578, -4.967578]) diff --git a/PySDM/source/tests/unit_tests/physics/test_freezing_temperature_spectra.py b/PySDM/source/tests/unit_tests/physics/test_freezing_temperature_spectra.py new file mode 100644 index 0000000000000000000000000000000000000000..9103c1404eb31cdde94addb871dd38051598c46a --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_freezing_temperature_spectra.py @@ -0,0 +1,48 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest +from matplotlib import pyplot + +from PySDM import Formulae +from PySDM.physics import constants as const +from PySDM.physics import si + +A = 1 * si.um**2 + + +@pytest.mark.parametrize("model", ("Niemand_et_al_2012", "Bigg_1953")) +def test_freezing_temperature_spectra(model, plot=False): + # Arrange + formulae = Formulae( + freezing_temperature_spectrum=model, + constants={"NIEMAND_A": -0.517, "NIEMAND_B": 8.934, "BIGG_DT_MEDIAN": 33}, + ) + temperature = np.linspace(const.T0, const.T0 - 40, num=100) + + # Act + pdf = formulae.freezing_temperature_spectrum.pdf(temperature, A) + cdf = formulae.freezing_temperature_spectrum.cdf(temperature, A) + + # Plot + pyplot.plot(temperature, pdf, linestyle="-", marker="o", label="pdf") + pdfax = pyplot.gca() + cdfax = pdfax.twinx() + cdfax.plot(temperature, cdf, linestyle="--", marker="x", label="cdf") + pyplot.xlabel("T [K]") + pyplot.xlim(np.amax(temperature), np.amin(temperature)) + pdfax.set_ylabel("pdf [K$^{-1}$]") + cdfax.set_ylabel("cdf [1]") + pyplot.grid() + pyplot.title(model) + if plot: + pyplot.show() + + # Assert + dT = abs(temperature[1] - temperature[0]) + np.testing.assert_approx_equal(np.sum(pdf * dT), 1, significant=3) + np.testing.assert_approx_equal(cdf[0] + 1, 1, significant=3) + np.testing.assert_approx_equal(cdf[-1], 1, significant=3) + + if hasattr(formulae.freezing_temperature_spectrum, "invcdf"): + invcdf = formulae.freezing_temperature_spectrum.invcdf(cdf, A) + np.testing.assert_allclose(invcdf, temperature) diff --git a/PySDM/source/tests/unit_tests/physics/test_homogeneous_nucleation_rates.py b/PySDM/source/tests/unit_tests/physics/test_homogeneous_nucleation_rates.py new file mode 100644 index 0000000000000000000000000000000000000000..f3b51a01ec4fc33706b7b9fb94a3be636aced037 --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_homogeneous_nucleation_rates.py @@ -0,0 +1,119 @@ +""" +test for homogeneous nucleation rate parameterisations +""" + +from contextlib import nullcontext +import re +import pytest +from matplotlib import pyplot +import numpy as np +from PySDM.formulae import Formulae, _choices +from PySDM.physics import homogeneous_ice_nucleation_rate +from PySDM import physics +from PySDM.physics.dimensional_analysis import DimensionalAnalysis + +SPICHTINGER_ET_AL_2023_FIG2_DATA = { + "da_w_ice": [0.27, 0.29, 0.31, 0.33], + "jhom_log10": [5, 11, 15, 20], +} + + +class TestHomogeneousIceNucleationRate: + @staticmethod + @pytest.mark.parametrize( + "index", range(len(SPICHTINGER_ET_AL_2023_FIG2_DATA["da_w_ice"])) + ) + @pytest.mark.parametrize( + "parametrisation, context", + ( + ("Koop_Correction", nullcontext()), + ( + "Koop2000", + pytest.raises( + AssertionError, match="Items are not equal to 2 significant digits" + ), + ), + ( + "KoopMurray2016", + pytest.raises( + ValueError, + match=re.escape( + "x and y must have same first dimension, but have shapes (4,) and (1,)" + ), + ), + ), + ), + ) + def test_fig_2_in_spichtinger_et_al_2023( + index, parametrisation, context, plot=False + ): + """Fig. 2 in [Spichtinger et al. 2023](https://doi.org/10.5194/acp-23-2035-2023)""" + # arrange + formulae = Formulae( + homogeneous_ice_nucleation_rate=parametrisation, + ) + + # act + with context: + jhom_log10 = np.log10( + formulae.homogeneous_ice_nucleation_rate.j_hom( + np.nan, np.asarray(SPICHTINGER_ET_AL_2023_FIG2_DATA["da_w_ice"]) + ) + ) + + # plot + pyplot.scatter( + x=[SPICHTINGER_ET_AL_2023_FIG2_DATA["da_w_ice"][index]], + y=[SPICHTINGER_ET_AL_2023_FIG2_DATA["jhom_log10"][index]], + color="red", + marker="x", + ) + pyplot.plot( + SPICHTINGER_ET_AL_2023_FIG2_DATA["da_w_ice"], + jhom_log10, + marker=".", + ) + pyplot.gca().set( + xlabel=r"water activity difference $\Delta a_w$", + ylabel="log$_{10}(J)$", + title=parametrisation, + xlim=(0.26, 0.34), + ylim=(0, 25), + ) + pyplot.grid() + if plot: + pyplot.show() + else: + pyplot.clf() + + # assert + np.testing.assert_approx_equal( + actual=jhom_log10[index], + desired=SPICHTINGER_ET_AL_2023_FIG2_DATA["jhom_log10"][index], + significant=2, + ) + + @staticmethod + @pytest.mark.parametrize("variant", _choices(homogeneous_ice_nucleation_rate)) + def test_units(variant): + if variant == "Null": + pytest.skip() + + with DimensionalAnalysis(): + # arrange + si = physics.si + formulae = Formulae( + homogeneous_ice_nucleation_rate=variant, + constants=( + {} if variant != "Constant" else {"J_HOM": 1 / si.m**3 / si.s} + ), + ) + sut = formulae.homogeneous_ice_nucleation_rate + temperature = 250 * si.K + da_w_ice = 0.3 * si.dimensionless + + # act + value = sut.j_hom(temperature, da_w_ice) + + # assert + assert value.check("1/[volume]/[time]") diff --git a/PySDM/source/tests/unit_tests/physics/test_hydrostatics_var_g.py b/PySDM/source/tests/unit_tests/physics/test_hydrostatics_var_g.py new file mode 100644 index 0000000000000000000000000000000000000000..e08bd2cc4988b36786b1de63c87f28836db41292 --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_hydrostatics_var_g.py @@ -0,0 +1,53 @@ +"""tests for hydrostatic profile considering variation in gravitational acceleration""" + +from PySDM import Formulae +from PySDM import physics +from PySDM.physics.dimensional_analysis import DimensionalAnalysis + + +class TestHydrostaticsVarG: + @staticmethod + def test_units(): + """checks that pressure returns something in pressure units""" + with DimensionalAnalysis(): + # arrange + sut = Formulae( + hydrostatics="VariableGIsothermal", + constants={ + "celestial_body_radius": 6378 * physics.si.km, + "g_std": 9.8 * physics.si.m / physics.si.s**2, + }, + ).hydrostatics.pressure + p0 = 1000 * physics.si.hPa + z = 10 * physics.si.km + temperature = 250 * physics.si.K + molar_mass = physics.constants_defaults.Md + + # act + result = sut(z, p0, temperature, molar_mass) + + # assert + assert result.check("[pressure]") + + @staticmethod + def test_sane_value_at_10km(): + """evaluates the pressure at a given large height and checks if within sane range""" + # arrange + sut = Formulae( + hydrostatics="VariableGIsothermal", + constants={ + "celestial_body_radius": 6378 * physics.si.km, + "g_std": 9.8 * physics.si.m / physics.si.s**2, + }, + ).hydrostatics.pressure + + # act + result = sut( + z=10 * physics.si.km, + p0=1000 * physics.si.hPa, + temperature=250 * physics.si.K, + molar_mass=physics.constants_defaults.Md, + ) + + # assert + assert 25 * physics.si.kPa < result < 26 * physics.si.kPa diff --git a/PySDM/source/tests/unit_tests/physics/test_hygroscopicity_fierce_diagrams.py b/PySDM/source/tests/unit_tests/physics/test_hygroscopicity_fierce_diagrams.py new file mode 100644 index 0000000000000000000000000000000000000000..a7c5881d84969fc6e34d32e30ef8c7032bb3a83a --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_hygroscopicity_fierce_diagrams.py @@ -0,0 +1,93 @@ +"""comparing full and linearised formula for kappa-Koehler hygroscopicity +parameterisation using dry-radius/kappa surfaces of critical supersaturation +(diagram concept by Laura Fierce)""" + +import numpy as np +from matplotlib import pyplot + +from PySDM import Formulae +from PySDM.physics import constants_defaults, si + +npoints = 32 + +clim_ss = (0, 0.85) +nlev_ss = 11 +clim_er = (-9, -1) +nlev_er = 9 + +kappas = np.logspace(np.log10(0.008), np.log10(2), npoints) +dry_radii = np.logspace(np.log10(0.05 * si.um), np.log10(0.1 * si.um), npoints) +T = 300 * si.K +sgm = constants_defaults.sgm_w + +variants = ("KappaKoehler", "KappaKoehlerLeadingTerms") + + +def contour(ax, x, y, z, *, levels, label, clim): + mpbl = ax.contourf(x, y, z, levels=levels) + pyplot.colorbar(mpbl, ax=ax).set_label(label) + mpbl.set_clim(*clim) + + +def test_hygroscopicity_fierce_diagrams(plot=False): + # arrange + formulae = {} + for variant in variants: + formulae[variant] = Formulae(hygroscopicity=variant) + + # act + S_crit = {k: np.empty((kappas.size, dry_radii.size)) for k in variants} + for i, kappa in enumerate(kappas): + for j, dry_radius in enumerate(dry_radii): + for variant in variants: + r_cr = formulae[variant].hygroscopicity.r_cr( + kappa, dry_radius**3, T, sgm + ) + S_crit[variant][i, j] = formulae[variant].hygroscopicity.RH_eq( + r_cr, T, kappa, dry_radius**3, sgm + ) + + # plot + fig, axs = pyplot.subplots(3, 1, figsize=(5, 15)) + x, y = dry_radii / si.nm, kappas + + for i, variant in enumerate(variants): + contour( + axs[i], + x, + y, + (S_crit[variant] - 1) * 100, + levels=np.linspace(*clim_ss, nlev_ss), + label="critical supersaturation [%]", + clim=clim_ss, + ) + axs[i].set_title(variant) + + axs[2].set_title("log_10(|S_2 - S_1| / S_1)") + log10_rel_diff = np.log10( + np.abs(S_crit[variants[1]] - S_crit[variants[0]]) / S_crit[variants[0]] + ) + contour( + axs[2], + x, + y, + log10_rel_diff, + levels=np.linspace(*clim_er, nlev_er), + label="log10(relative difference)", + clim=clim_er, + ) + + for ax in axs: + ax.set_ylabel("kappa") + ax.set_xscale("log") + ax.set_yscale("log") + ax.set_xlabel("dry radius [nm]") + pyplot.subplots_adjust(hspace=0.25) + + if plot: + fig.show() + else: + fig.clear() + + # assert + assert np.amax(log10_rel_diff) < -1.5 diff --git a/PySDM/source/tests/unit_tests/physics/test_isotope_diffusivity_ratios.py b/PySDM/source/tests/unit_tests/physics/test_isotope_diffusivity_ratios.py new file mode 100644 index 0000000000000000000000000000000000000000..2a501474c042367d8d476671d40c59dab9f211e3 --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_isotope_diffusivity_ratios.py @@ -0,0 +1,170 @@ +""" +tests for isotope diffusivity ratio formulae +""" + +import numpy as np +import pytest +from matplotlib import pyplot + +from PySDM.physics import constants_defaults, si, isotope_diffusivity_ratios +from PySDM.physics.dimensional_analysis import DimensionalAnalysis +from PySDM.formulae import _choices, Formulae + + +class TestIsotopeDiffusivityRatios: + @staticmethod + @pytest.mark.parametrize( + "isotope, expected_value", + ( + ("2H", 0.9835), + ("18O", 0.9687), + ), + ) + def test_stewart_1975_values_given_in_horita_et_al_2008(isotope, expected_value): + """test against values given below eq. (22) in + [Horita et al. 2008](https://doi.org/10.1080/10256010801887174)""" + # arrange + formulae = Formulae( + constants={"Md": constants_defaults.Md * 0.9986}, + isotope_diffusivity_ratios="Stewart1975", + ) + sut = getattr( + formulae.isotope_diffusivity_ratios, f"ratio_{isotope}_heavy_to_light" + ) + + # act + actual_value = sut(temperature=np.nan) + + # assert + np.testing.assert_approx_equal( + actual=actual_value, desired=expected_value, significant=4 + ) + + @staticmethod + @pytest.mark.parametrize( + "temperature, expected_values", + ( + (190 * si.K, (0.9740, 0.9850, 0.9714)), + (200 * si.K, (0.9741, 0.9850, 0.9713)), + (210 * si.K, (0.9743, 0.9850, 0.9713)), + (220 * si.K, (0.9744, 0.9849, 0.9712)), + (230 * si.K, (0.9745, 0.9849, 0.9712)), + (240 * si.K, (0.9747, 0.9849, 0.9711)), + (250 * si.K, (0.9748, 0.9849, 0.9711)), + (260 * si.K, (0.9750, 0.9848, 0.9710)), + (270 * si.K, (0.9752, 0.9848, 0.9710)), + (280 * si.K, (0.9753, 0.9848, 0.9709)), + (290 * si.K, (0.9755, 0.9848, 0.9709)), + (300 * si.K, (0.9756, 0.9847, 0.9708)), + (310 * si.K, (0.9758, 0.9847, 0.9708)), + (320 * si.K, (0.9759, 0.9847, 0.9707)), + (330 * si.K, (0.9761, 0.9847, 0.9706)), + (340 * si.K, (0.9762, 0.9847, 0.9706)), + (360 * si.K, (0.9765, 0.9846, 0.9705)), + (380 * si.K, (0.9768, 0.9846, 0.9704)), + (400 * si.K, (0.9770, 0.9845, 0.9703)), + (450 * si.K, (0.9775, 0.9845, 0.9701)), + (500 * si.K, (0.9779, 0.9844, 0.9700)), + ), + ) + @pytest.mark.parametrize( + "isotope_index, isotope_label", enumerate(("2H", "17O", "18O")) + ) + def test_hellmann_and_harvey_2020_table_1( + temperature, expected_values, isotope_index, isotope_label + ): + # arrange + formulae = Formulae(isotope_diffusivity_ratios="HellmannAndHarvey2020") + sut = getattr( + formulae.isotope_diffusivity_ratios, f"ratio_{isotope_label}_heavy_to_light" + ) + + # act + actual_value = sut(temperature=temperature) + + # assert + np.testing.assert_approx_equal( + actual=actual_value, desired=expected_values[isotope_index], significant=4 + ) + + @staticmethod + @pytest.mark.parametrize( + "isotope_name, expected_value", (("2H", 0.973), ("3H", 0.949)) + ) + def test_grahams_law(isotope_name, expected_value): + # arrange + formulae = Formulae(isotope_diffusivity_ratios="GrahamsLaw") + + # act + sut = getattr( + formulae.isotope_diffusivity_ratios, f"ratio_{isotope_name}_heavy_to_light" + )(temperature=np.nan) + + # assert + np.testing.assert_approx_equal(sut, expected_value, significant=3) + + @staticmethod + @pytest.mark.parametrize("paper", _choices(isotope_diffusivity_ratios)) + @pytest.mark.parametrize("isotope_label", ("2H", "17O", "18O")) + def test_unit(paper, isotope_label): + with DimensionalAnalysis(): + # arrange + try: + sut = getattr( + Formulae( + isotope_diffusivity_ratios=paper + ).isotope_diffusivity_ratios, + f"ratio_{isotope_label}_heavy_to_light", + ) + except AttributeError: + pytest.skip() + + # act + result = sut(temperature=300 * constants_defaults.si.K) + + # assert + assert result.dimensionless + + @staticmethod + def test_all_on_one_plot(plot=False): + temperature = np.linspace(270, 300) * si.K + min_value, max_value = np.inf, -np.inf + for paper in _choices(isotope_diffusivity_ratios): + formulae = Formulae(isotope_diffusivity_ratios=paper) + for isotope_label in ("2H", "17O", "18O"): + try: + sut = getattr( + formulae.isotope_diffusivity_ratios, + f"ratio_{isotope_label}_heavy_to_light", + ) + except AttributeError: + pass + else: + diffusivity_ratio_heavy_to_light = sut(temperature) + min_value = min( + np.amin(diffusivity_ratio_heavy_to_light), min_value + ) + max_value = max( + np.amax(diffusivity_ratio_heavy_to_light), max_value + ) + pyplot.plot( + temperature, + ( + diffusivity_ratio_heavy_to_light + if isinstance(diffusivity_ratio_heavy_to_light, np.ndarray) + else np.full_like( + temperature, diffusivity_ratio_heavy_to_light + ) + ), + label=f"{paper=} {isotope_label=}", + ) + pyplot.xlabel("temperature [K]") + pyplot.grid() + pyplot.ylabel("diffusivity ratio (heavy to light)") + if plot: + pyplot.show() + else: + pyplot.clf() + + # assert + assert 0.985 > max_value > min_value > 0.968 diff --git a/PySDM/source/tests/unit_tests/physics/test_isotope_equilibrium_fractionation_factors.py b/PySDM/source/tests/unit_tests/physics/test_isotope_equilibrium_fractionation_factors.py new file mode 100644 index 0000000000000000000000000000000000000000..509e9ac35f1ed49385dc5170c0aeded3fe13e05b --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_isotope_equilibrium_fractionation_factors.py @@ -0,0 +1,133 @@ +# pylint: disable=missing-module-docstring +import numpy as np +import pint +import pytest + +from PySDM import Formulae +from PySDM.physics import constants_defaults, si +from PySDM.physics.dimensional_analysis import DimensionalAnalysis + +CASES = ( + ( + "2H", + "ice", + { + -120 * si.K + constants_defaults.T0: 1.82, + 0 * si.K + constants_defaults.T0: 1.13, + }, + ), + ( + "2H", + "liquid", + { + -40 * si.K + constants_defaults.T0: 1.2, + 20 * si.K + constants_defaults.T0: 1.08, + }, + ), + ( + "18O", + "ice", + { + -120 * si.K + constants_defaults.T0: 1.05, + 0 * si.K + constants_defaults.T0: 1.015, + }, + ), + ( + "18O", + "liquid", + { + -40 * si.K + constants_defaults.T0: 1.02, + 20 * si.K + constants_defaults.T0: 1.01, + }, + ), +) +"""values from Fig. 1 in [Bolot et al. 2013](https://doi.org/10.5194/acp-13-7903-2013)""" + +PAPERS = ("MerlivatAndNief1967+Majoube1970+Majoube1971", "VanHook1968") + +# TODO #1208: tests for VanHook1968 H2_17O, HOT + + +class TestIsotopeEquilibriumFractionationFactors: + @staticmethod + @pytest.mark.parametrize("paper", PAPERS) + @pytest.mark.parametrize( + "isotopologue, phase, expected_temperature_alpha_pairs", CASES + ) + def test_values(paper, isotopologue, phase, expected_temperature_alpha_pairs): + # arrange + formulae = Formulae(isotope_equilibrium_fractionation_factors=paper) + sut = getattr( + formulae.isotope_equilibrium_fractionation_factors, + f"alpha_{phase[0]}_{isotopologue}", + ) + + # act + actual_pairs = { + temp: sut(temp) for temp in expected_temperature_alpha_pairs.keys() + } + + # assert + for k, v in expected_temperature_alpha_pairs.items(): + np.testing.assert_approx_equal( + actual=actual_pairs[k], desired=v, significant=2 + ) + + @staticmethod + @pytest.mark.parametrize("paper", PAPERS) + @pytest.mark.parametrize( + "isotopologue, phase, expected_temperature_alpha_pairs", CASES + ) + def test_monotonic(paper, isotopologue, phase, expected_temperature_alpha_pairs): + # arrange + formulae = Formulae(isotope_equilibrium_fractionation_factors=paper) + sut = getattr( + formulae.isotope_equilibrium_fractionation_factors, + f"alpha_{phase[0]}_{isotopologue}", + ) + + # act + values = sut( + np.linspace( + tuple(expected_temperature_alpha_pairs.keys())[0], + tuple(expected_temperature_alpha_pairs.keys())[-1], + 100, + ) + ) + + # assert + assert (np.diff(values) < 0).all() + + @staticmethod + @pytest.mark.parametrize("paper", PAPERS) + @pytest.mark.parametrize("isotopologue, phase, _", CASES) + @pytest.mark.parametrize( + "argument", + ( + pytest.param( + "1 * si.J", + marks=pytest.mark.xfail( + strict=True, raises=pint.errors.DimensionalityError + ), + ), + "300 * si.K", + ), + ) + def test_units(paper, isotopologue, phase, _, argument): + with DimensionalAnalysis(): + # arrange + sut = getattr( + Formulae( + isotope_equilibrium_fractionation_factors=paper + ).isotope_equilibrium_fractionation_factors, + f"alpha_{phase[0]}_{isotopologue}", + ) + arg = eval( # pylint: disable=eval-used + argument, None, {"si": constants_defaults.si} + ) + + # act + result = sut(arg) + + # assert + assert result.dimensionless diff --git a/PySDM/source/tests/unit_tests/physics/test_isotope_kinetic_fractionation_factors.py b/PySDM/source/tests/unit_tests/physics/test_isotope_kinetic_fractionation_factors.py new file mode 100644 index 0000000000000000000000000000000000000000..84feeb7ed4c4112bf8f68415c1e851061da5f00e --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_isotope_kinetic_fractionation_factors.py @@ -0,0 +1,172 @@ +""" +test for isotope kinetic fractionation factors based on plot +""" + +import numpy as np +import pytest +from matplotlib import pyplot + +from PySDM import Formulae +from PySDM import physics +from PySDM.physics.dimensional_analysis import DimensionalAnalysis +from PySDM.physics.isotope_kinetic_fractionation_factors import JouzelAndMerlivat1984 + +PLOT = False + + +class TestIsotopeKineticFractionationFactors: + @staticmethod + def test_units_alpha_kinetic(): + """checks that alphas are dimensionless""" + with DimensionalAnalysis(): + # arrange + alpha_eq = 1 * physics.si.dimensionless + D_ratio = 1 * physics.si.dimensionless + saturation_over_ice = 1 * physics.si.dimensionless + + # act + sut = JouzelAndMerlivat1984.alpha_kinetic( + alpha_equilibrium=alpha_eq, + D_ratio_heavy_to_light=D_ratio, + saturation=saturation_over_ice, + ) + + # assert + assert sut.check("[]") + + @staticmethod + def test_fig_9_from_jouzel_and_merlivat_1984(plot=False): + """[Jouzel & Merlivat 1984](https://doi.org/10.1029/JD089iD07p11749)""" + # arrange + formulae = Formulae( + isotope_kinetic_fractionation_factors="JouzelAndMerlivat1984", + isotope_equilibrium_fractionation_factors="Majoube1970", + isotope_diffusivity_ratios="Stewart1975", + ) + temperatures = formulae.trivia.C2K(np.asarray([-30, -20, -10])) + saturation = np.linspace(start=1, stop=1.35) + alpha_s = formulae.isotope_equilibrium_fractionation_factors.alpha_i_18O + diffusivity_ratio_heavy_to_light = ( + formulae.isotope_diffusivity_ratios.ratio_18O_heavy_to_light + ) + sut = formulae.isotope_kinetic_fractionation_factors.alpha_kinetic + + # act + alpha_s = {temperature: alpha_s(temperature) for temperature in temperatures} + alpha_k = { + temperature: sut( + alpha_equilibrium=alpha_s[temperature], + saturation=saturation, + D_ratio_heavy_to_light=diffusivity_ratio_heavy_to_light(temperature), + ) + for temperature in temperatures + } + alpha_s_times_alpha_k = { + f"{formulae.trivia.K2C(temperature):.3g}C": alpha_k[temperature] + * alpha_s[temperature] + for temperature in temperatures + } + + # plot + pyplot.xlim(saturation[0], saturation[-1]) + pyplot.ylim(1.003, 1.022) + pyplot.xlabel("S") + pyplot.ylabel("alpha_k * alpha_s") + for k, v in alpha_s_times_alpha_k.items(): + pyplot.plot(saturation, v, label=k) + pyplot.legend() + if plot: + pyplot.show() + else: + pyplot.clf() + + # assert + assert (alpha_s_times_alpha_k["-30C"] > alpha_s_times_alpha_k["-20C"]).all() + assert (alpha_s_times_alpha_k["-20C"] > alpha_s_times_alpha_k["-10C"]).all() + for alpha_alpha in alpha_s_times_alpha_k.values(): + assert (np.diff(alpha_alpha) < 0).all() + + @staticmethod + @pytest.mark.parametrize( + ("temperature_C", "saturation", "alpha"), + ((-10, 1, 1.021), (-10, 1.35, 1.0075), (-30, 1, 1.0174), (-30, 1.35, 1.004)), + ) + def test_fig9_values(temperature_C, saturation, alpha): + # arrange + formulae = Formulae( + isotope_kinetic_fractionation_factors="JouzelAndMerlivat1984", + isotope_equilibrium_fractionation_factors="Majoube1970", + isotope_diffusivity_ratios="Stewart1975", + ) + diffusivity_ratio_18O = ( + formulae.isotope_diffusivity_ratios.ratio_18O_heavy_to_light + ) + T = formulae.trivia.C2K(temperature_C) + alpha_s = formulae.isotope_equilibrium_fractionation_factors.alpha_i_18O(T) + alpha_k = formulae.isotope_kinetic_fractionation_factors.alpha_kinetic( + alpha_equilibrium=alpha_s, + saturation=saturation, + D_ratio_heavy_to_light=diffusivity_ratio_18O(T), + ) + + # act + sut = alpha_s * alpha_k + + # assert + np.testing.assert_approx_equal(actual=sut, desired=alpha, significant=3) + + @staticmethod + @pytest.mark.parametrize("isotope", ("2H", "18O", "17O")) + @pytest.mark.parametrize("temperature_C", (-30, -20, -1)) + def test_alpha_kinetic_jouzel_merlivat_vs_craig_gordon( + isotope, temperature_C, plot=PLOT + ): + # arrange + T = Formulae().trivia.C2K(temperature_C) + RH = np.linspace(0.3, 1) + formulae = Formulae( + isotope_equilibrium_fractionation_factors="VanHook1968", + isotope_diffusivity_ratios="HellmannAndHarvey2020", + isotope_kinetic_fractionation_factors="JouzelAndMerlivat1984", + ) + Si = formulae.saturation_vapour_pressure.pvs_ice(T) + alpha_eq = getattr( + formulae.isotope_equilibrium_fractionation_factors, f"alpha_l_{isotope}" + )(T) + D_heavy_to_light = getattr( + formulae.isotope_diffusivity_ratios, f"ratio_{isotope}_heavy_to_light" + )(T) + alpha_kin_jm = formulae.isotope_kinetic_fractionation_factors.alpha_kinetic( + alpha_equilibrium=alpha_eq, + saturation=Si, + D_ratio_heavy_to_light=D_heavy_to_light, + ) + formulae = Formulae( + isotope_kinetic_fractionation_factors="CraigGordon", + ) + alpha_kin_cg = formulae.isotope_kinetic_fractionation_factors.alpha_kinetic( + relative_humidity=RH, + turbulence_parameter_n=1, + delta_diff=alpha_eq - 1, + theta=1, + ) + + # act + n = (alpha_kin_jm + 1) / (alpha_kin_cg + 1) + + # plot + pyplot.plot(1 - RH, n) + pyplot.gca().set( + xlabel="1-RH", + ylabel="turbulence parameter n", + ) + pyplot.grid() + + if plot: + pyplot.show() + else: + pyplot.clf() + + # assert + np.testing.assert_equal(n > 0.5, True) + np.testing.assert_equal(n < 1, True) diff --git a/PySDM/source/tests/unit_tests/physics/test_isotope_meteoric_water_line.py b/PySDM/source/tests/unit_tests/physics/test_isotope_meteoric_water_line.py new file mode 100644 index 0000000000000000000000000000000000000000..5a54012e7c6eb9cce1b68d9a2a597dd91f0c7094 --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_isotope_meteoric_water_line.py @@ -0,0 +1,182 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring +import numpy as np +import pytest +from matplotlib import pyplot + +from PySDM import Formulae +from PySDM.physics import in_unit +from PySDM.physics.constants_defaults import PER_MILLE, PER_CENT + + +class TestIsotopeMeteoricWaterLine: + @staticmethod + def test_craig_1961_science_fig_1(plot=False): + """see Fig. 1 in [Craig 1961](https://doi.org/10.1126/science.133.3465.1702)""" + + # arrange + const = Formulae().constants + + # act + delta_18_oxygen = np.linspace(-50, 5) * const.PER_MILLE + delta_2_hydrogen = ( + const.CRAIG_1961_SLOPE_COEFF * delta_18_oxygen + + const.CRAIG_1961_INTERCEPT_COEFF + ) + + # plot + pyplot.plot( + in_unit(delta_18_oxygen, const.PER_MILLE), + in_unit(delta_2_hydrogen, const.PER_MILLE), + color="black", + ) + pyplot.grid() + pyplot.xlabel("$δ^{18}O$ [‰]") + pyplot.ylabel("$δ^2H$ [‰]") + pyplot.xlim(-50, 20) + pyplot.ylim(-360, 100) + if plot: + pyplot.show() + else: + pyplot.clf() + + # assert + assert delta_2_hydrogen[0] == -390 * const.PER_MILLE + assert delta_2_hydrogen[-1] == 50 * const.PER_MILLE + + @staticmethod + def test_barkan_and_luz_2007_fig_4(plot=False): + """see Fig. 4 in [Barkan and Luz 2007](http://doi.org/10.1002/rcm.3180)""" + + # arrange + formulae = Formulae() + const = formulae.constants + + # act + delta_18_oxygen = np.linspace(0, 0.045) + x_values = np.log(delta_18_oxygen + 1) + y_values = const.BARKAN_AND_LUZ_2007_EXCESS_18O_COEFF * np.log( + delta_18_oxygen + 1 + ) + + # plot + pyplot.plot( + in_unit(x_values, const.PER_MILLE), + in_unit(y_values, const.PER_MILLE), + color="black", + ) + pyplot.xlabel("$ln(δ^{18}O + 1)$, ‰") + pyplot.ylabel("$ln(δ^{17}O + 1)$, ‰") + pyplot.xlim(0, 50) + pyplot.ylim(0, 25) + pyplot.grid() + if plot: + pyplot.show() + else: + pyplot.clf() + + # assert + assert (0.0, 0.0) == (x_values[0], y_values[0]) + assert (44 * const.PER_MILLE, 23 * const.PER_MILLE) == ( + np.round(x_values[-1], 3), + np.round(y_values[-1], 3), + ) + + @staticmethod + @pytest.mark.parametrize( + "delta_2H", (-30 * PER_MILLE, -10 * PER_MILLE, 10 * PER_MILLE, 30 * PER_MILLE) + ) + def test_d18O_of_d2H(delta_2H): + # arrange + formulae = Formulae(isotope_meteoric_water_line="Dansgaard1964") + sut = formulae.isotope_meteoric_water_line.d18O_of_d2H + + # act + delta_18O = sut(delta_2H=delta_2H) + + # assert + np.testing.assert_approx_equal( + actual=formulae.isotope_meteoric_water_line.excess_d( + delta_18O=delta_18O, delta_2H=delta_2H + ), + desired=formulae.constants.CRAIG_1961_INTERCEPT_COEFF, + significant=3, + ) + + @staticmethod + @pytest.mark.parametrize( + "delta_18O", (-3 * PER_MILLE, -1 * PER_MILLE, 1 * PER_MILLE, 3 * PER_MILLE) + ) + def test_d17O_of_d18O(delta_18O): + # arrange + formulae = Formulae(isotope_meteoric_water_line="BarkanAndLuz2007") + sut = formulae.isotope_meteoric_water_line.d17O_of_d18O + + # act + delta_17O = sut(delta_18O=delta_18O) + + # assert + np.testing.assert_almost_equal( + actual=formulae.isotope_meteoric_water_line.excess_17O( + delta_18O=delta_18O, delta_17O=delta_17O + ), + desired=0, + ) + + @staticmethod + def test_picciotto_et_al_1960_fig_2(plot=False): + # arrange + delta_deuterium = np.linspace(-31, -10) * PER_CENT + # act + + delta_18O = { + paper: Formulae( + isotope_meteoric_water_line=paper + ).isotope_meteoric_water_line.d18O_of_d2H(delta_deuterium) + for paper in ("PicciottoEtAl1960", "Dansgaard1964") + } + + # plot + pyplot.figure(figsize=(5, 6)) + for paper, delta in delta_18O.items(): + pyplot.plot( + in_unit(delta_deuterium, PER_CENT), + in_unit(delta, PER_MILLE), + label=paper, + ) + + pyplot.xlabel(r"$\delta$ $^2$H [%]") + pyplot.ylabel(r"$\delta$ $^{18}$O [‰]") + + pyplot.grid(color="k") + pyplot.legend() + if plot: + pyplot.show() + else: + pyplot.clf() + + # assert + delta = delta_18O["PicciottoEtAl1960"] + monotonic = (np.diff(delta) > 0).all() + assert monotonic + assert -37 < in_unit(delta[0], PER_MILLE) < -36 + assert -11 < in_unit(delta[-1], PER_MILLE) < -10 + + @staticmethod + @pytest.mark.parametrize( + "delta_2H", (-30 * PER_MILLE, -10 * PER_MILLE, 10 * PER_MILLE, 30 * PER_MILLE) + ) + def test_picciotto_et_al_1960_d18O_of_d2H(delta_2H): + # arrange + formulae = Formulae(isotope_meteoric_water_line="PicciottoEtAl1960") + sut = formulae.isotope_meteoric_water_line.d18O_of_d2H + + # act + delta_18O = sut(delta_2H=delta_2H) + + # assert + np.testing.assert_approx_equal( + actual=delta_2H + - formulae.constants.PICCIOTTO_18O_TO_2H_SLOPE_COEFF * delta_18O, + desired=formulae.constants.PICCIOTTO_18O_TO_2H_INTERCEPT_COEFF, + significant=3, + ) diff --git a/PySDM/source/tests/unit_tests/physics/test_isotope_ratio_evolution.py b/PySDM/source/tests/unit_tests/physics/test_isotope_ratio_evolution.py new file mode 100644 index 0000000000000000000000000000000000000000..4621252655c7e35515c16170fc54fec212e576ed --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_isotope_ratio_evolution.py @@ -0,0 +1,76 @@ +"""Tests for formulae from [Merlivat and Jouzel 1979](https://doi.org/10.1029/JC084iC08p05029)""" + +from functools import partial + +import numpy as np +import pytest +from matplotlib import pyplot + +from PySDM import Formulae + + +class TestMerlivatAndJouzel1979: + @staticmethod + @pytest.mark.parametrize("alpha", (0.1, 0.001)) + @pytest.mark.parametrize("start_stop", ((0.1, 1), (1, 0.1))) + @pytest.mark.parametrize("Rv0", (0.01, 0.1)) + def test_rayleigh_distillation_case(alpha, start_stop, Rv0, plot=False): + """ + d_Rv/Rv = (alpha - 1) * d_nv/nv + ln(Rv/Rv0) = (alpha - 1) * ln(nv/nv0) + Rv = Rv0 * exp((alpha - 1) * ln(nv/nv0)) + Rv = Rv0 * (nv/nv0)**(alpha - 1) + """ + + # arrange + d_Rv_over_Rv_M_J_1979 = partial( + Formulae( + isotope_ratio_evolution="MerlivatAndJouzel1979" + ).isotope_ratio_evolution.d_Rv_over_Rv, + d_alpha=0, + n_liquid=0, + ) + R_over_R0_Rayleigh = Formulae( + isotope_ratio_evolution="RayleighDistillation" + ).isotope_ratio_evolution.R_over_R0 + + num_points = 77 + assert num_points % 2 == 1 + + # act + nv, delta_nv = np.linspace(*start_stop, num=num_points, retstep=True) + dRoR_m_j_1979 = d_Rv_over_Rv_M_J_1979( + alpha=alpha, n_vapour=nv, d_n_vapour=delta_nv + ) + + nv_over_nv0 = nv / nv[0] + + R = Rv0 * R_over_R0_Rayleigh(X_over_X0=nv_over_nv0, a=alpha) + dRoR_rayleigh = np.diff(R[::2]) / 2 / (R[1::2]) + + # plot + pyplot.ylabel("dR/R") + pyplot.xlabel("nv/nv$_0$") + pyplot.plot( + nv_over_nv0, dRoR_m_j_1979, label="Merlivat & Jouzel '79", marker="x" + ) + pyplot.scatter( + nv_over_nv0[1::2], dRoR_rayleigh, label="Rayleigh", marker="o", color="red" + ) + pyplot.legend() + # pyplot.xscale('log') + # pyplot.yscale('log') + pyplot.grid() + if plot: + pyplot.show() + else: + pyplot.clf() + + # assert + np.testing.assert_almost_equal( + desired=dRoR_rayleigh, actual=dRoR_m_j_1979[1::2], decimal=3 + ) + + @staticmethod + def test_heilstone_expression(): + pass # TODO #1206 diff --git a/PySDM/source/tests/unit_tests/physics/test_isotope_relaxation_timescale.py b/PySDM/source/tests/unit_tests/physics/test_isotope_relaxation_timescale.py new file mode 100644 index 0000000000000000000000000000000000000000..666ba269458e7763efcbcabd40f453988321ce9f --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_isotope_relaxation_timescale.py @@ -0,0 +1,72 @@ +""" +tests for isotope relaxation timescale formulae +""" + +import pytest + +from PySDM.physics.dimensional_analysis import DimensionalAnalysis +from PySDM.formulae import Formulae, _choices +from PySDM.physics import constants_defaults, isotope_relaxation_timescale + + +@pytest.mark.parametrize( + "paper", + [ + choice + for choice in _choices(isotope_relaxation_timescale) + if choice not in ("Null", "Bolin1958") + ], +) +@pytest.mark.parametrize("iso", ("2H", "18O")) +def test_unit_and_magnitude(paper, iso): + with DimensionalAnalysis(): + # arrange + si = constants_defaults.si + formulae = Formulae( + isotope_relaxation_timescale=paper, + isotope_equilibrium_fractionation_factors="HoritaAndWesolowski1994", + isotope_diffusivity_ratios="HellmannAndHarvey2020", + ) + const = formulae.constants + temperature = 300 * si.K + D = const.D0 + D_iso = ( + getattr(formulae.isotope_diffusivity_ratios, f"ratio_{iso}_heavy_to_light")( + temperature + ) + * D + ) + vent_coeff = 1 + + # act + result = formulae.isotope_relaxation_timescale.tau( + rho_s=const.rho_w, + radius=0.1 * si.mm, + D_iso=vent_coeff * D_iso, + D=D, + S=1.01, + R_liq=getattr(const, f"VSMOW_R_{iso}"), + alpha=getattr( + formulae.isotope_equilibrium_fractionation_factors, f"alpha_l_{iso}" + )(temperature), + R_vap=getattr(const, f"VSMOW_R_{iso}"), + Fk=formulae.drop_growth.Fk(T=const.T_tri, K=const.K0, lv=const.l_tri), + ) + + # assert + assert result.check("[time]") + assert 0 * si.s < result.to_base_units() < 10 * si.s + + +def test_bolin_number_unit(): + with DimensionalAnalysis(): + # arrange + si = constants_defaults.si + sut = isotope_relaxation_timescale.bolin_1958.Bolin1958.bolin_number + formulae = Formulae() + + # act + value = sut(formulae.constants) + + # assert + assert value.check(si.dimensionless) diff --git a/PySDM/source/tests/unit_tests/physics/test_isotope_temperature_inference.py b/PySDM/source/tests/unit_tests/physics/test_isotope_temperature_inference.py new file mode 100644 index 0000000000000000000000000000000000000000..9a7369ba94cc77bd46937336e27f3f7873021d81 --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_isotope_temperature_inference.py @@ -0,0 +1,67 @@ +"""checking values in Fig. 3 in [Picciotto et al. 1960](https://doi.org/10.1038/187857a0)""" + +import numpy as np +from matplotlib import pyplot + +from PySDM import Formulae +from PySDM.physics.constants import PER_MILLE, PER_CENT +from PySDM.physics import in_unit, si + + +class TestPicciottoEtAl1960: + @staticmethod + def test_fig_3(plot=False): + # arrange + formulae = Formulae(isotope_temperature_inference="PicciottoEtAl1960") + delta_18O = np.linspace(-35, -9) * PER_MILLE + T0 = formulae.constants.T0 + + # act + temperature = formulae.isotope_temperature_inference.temperature_from_delta_18O( + delta_18O + ) + + # plot + pyplot.figure(figsize=(5, 6)) + for side in ("top", "right"): + pyplot.gca().spines[side].set_visible(False) + pyplot.plot(temperature - T0, in_unit(delta_18O, PER_MILLE), color="k") + + pyplot.xlabel("temperature [°C]") + pyplot.xlim(-45, 2) + pyplot.xticks(range(-45, 2, 5)) + + pyplot.ylabel("δ$^{18}$O [‰]") + pyplot.ylim(in_unit(delta_18O[0], PER_MILLE), in_unit(delta_18O[-1], PER_MILLE)) + pyplot.yticks(range(-35, -9, 5)) + + pyplot.grid(color="k") + if plot: + pyplot.show() + else: + pyplot.clf() + + # assert + monotonic_temperature = (np.diff(temperature) > 0).all() + assert monotonic_temperature + assert -32 < (temperature[0] - T0) < -31 + assert -3 < (temperature[-1] - T0) < -2 + + @staticmethod + def test_temperature_from_delta_2H(): + # arrange + formulae = Formulae( + isotope_temperature_inference="PicciottoEtAl1960", + isotope_meteoric_water_line="PicciottoEtAl1960", + ) + delta_2H = np.linspace(-35, -9) * PER_CENT + delta_18O = formulae.isotope_meteoric_water_line.d18O_of_d2H(delta_2H) + T18O = formulae.isotope_temperature_inference.temperature_from_delta_18O( + delta_18O + ) + + # act + T2H = formulae.isotope_temperature_inference.temperature_from_delta_2H(delta_2H) + + # assert + np.testing.assert_allclose(T18O, T2H, atol=6 * si.K) diff --git a/PySDM/source/tests/unit_tests/physics/test_isotope_ventilation_ratio.py b/PySDM/source/tests/unit_tests/physics/test_isotope_ventilation_ratio.py new file mode 100644 index 0000000000000000000000000000000000000000..18c4727b447d9152494755dc7f8bb8298e641115 --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_isotope_ventilation_ratio.py @@ -0,0 +1,38 @@ +"""test isotope ventilation ratio""" + +import numpy as np + +from PySDM import Formulae +from PySDM.physics import constants_defaults +from PySDM.physics.dimensional_analysis import DimensionalAnalysis + + +class TestIsotopeVentilationRatio: # pylint: disable=too-few-public-methods + @staticmethod + def test_dimensionality(): + with DimensionalAnalysis(): + # Arrange + formulae = Formulae(isotope_ventilation_ratio="Brutsaert1982") + si = constants_defaults.si + sut = formulae.isotope_ventilation_ratio.ratio_heavy_to_light + + # Act + re = sut( + ventilation_coefficient=1 * si.dimensionless, + diffusivity_ratio_heavy_to_light=1 * si.dimensionless, + ) + + # Assert + assert re.check(si.dimensionless) + + @staticmethod + def test_neglect_returns_one_ignoring_argument(): + # arrange + formulae = Formulae(isotope_ventilation_ratio="Neglect") + sut = formulae.isotope_ventilation_ratio.ratio_heavy_to_light + + # act + value = sut(np.nan, np.nan) + + # assert + assert value == 1 diff --git a/PySDM/source/tests/unit_tests/physics/test_latent_heat.py b/PySDM/source/tests/unit_tests/physics/test_latent_heat.py new file mode 100644 index 0000000000000000000000000000000000000000..a9ee108bcbcd5deaba530cc64d781bd0682395f3 --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_latent_heat.py @@ -0,0 +1,70 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import inspect + +import numpy as np +from matplotlib import pyplot + +from PySDM import Formulae +from PySDM.physics import si + + +class TestLatentHeat: + @staticmethod + def test_latent_vapourisation(plot=False): + # Arrange + formulae = { + k: Formulae(latent_heat_vapourisation=k) for k in ("Kirchhoff", "Lowe2019") + } + const = Formulae().constants + temperature = np.linspace(-20, 20) + const.T_tri + + # Plot + pyplot.axhline(const.l_tri, label="triple point", color="red") + pyplot.axvline(const.T_tri, color="red") + for key, val in formulae.items(): + for name, func in inspect.getmembers(val.latent_heat_vapourisation): + if name[:2] not in ("__", "a_"): + pyplot.plot(temperature, func(temperature), label=f"{key}::{name}") + pyplot.grid() + pyplot.legend() + pyplot.xlabel("T [K]") + pyplot.ylabel("Lv [J/kg]") + if plot: + pyplot.show() + + # Assert + temperature = np.linspace(-20, 20, 100) + const.T_tri + np.testing.assert_allclose( + Formulae( + latent_heat_vapourisation="Kirchhoff" + ).latent_heat_vapourisation.lv(temperature), + Formulae(latent_heat_vapourisation="Lowe2019").latent_heat_vapourisation.lv( + temperature + ), + rtol=1e-2, + ) + + @staticmethod + def test_latent_heat_sublimation(plot=False): + # arrange + formulae = Formulae() + T = np.linspace(30, 300) * si.K + + # act + ls = formulae.latent_heat_sublimation.ls(T) + + # plot + pyplot.plot(T, ls) + pyplot.xlabel("T [K]") + pyplot.ylabel("Lv [J/kg]") + if plot: + pyplot.show() + else: + pyplot.clf() + + # assert + np.testing.assert_approx_equal( + actual=ls[np.abs(T - formulae.trivia.C2K(0)).argmin()], + desired=2838 * si.kJ / si.kg, + significant=3, + ) diff --git a/PySDM/source/tests/unit_tests/physics/test_optical.py b/PySDM/source/tests/unit_tests/physics/test_optical.py new file mode 100644 index 0000000000000000000000000000000000000000..93438b8836c91bcf11a784d6b5745cd38d705c99 --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_optical.py @@ -0,0 +1,47 @@ +"""tests for optical formulae (albedo, optical depth, ...)""" + +import pytest + +from PySDM import Formulae +from PySDM.physics import optical_albedo, optical_depth, constants_defaults +from PySDM.formulae import _choices +from PySDM.physics.dimensional_analysis import DimensionalAnalysis + + +class TestOptical: + @staticmethod + @pytest.mark.parametrize( + "paper", [p for p in _choices(optical_albedo) if p != "Null"] + ) + def test_albedo_unit(paper): + with DimensionalAnalysis(): + # arrange + formulae = Formulae(optical_albedo=paper) + si = constants_defaults.si + tau = 1 * si.dimensionless + + # act + albedo = formulae.optical_albedo.albedo(tau) + + # assert + assert albedo.check(si.dimensionless) + + @staticmethod + @pytest.mark.parametrize( + "paper", [p for p in _choices(optical_depth) if p != "Null"] + ) + def test_optical_depth_unit(paper): + with DimensionalAnalysis(): + # arrange + formulae = Formulae(optical_depth=paper) + si = constants_defaults.si + liquid_water_path = 30 * si.g / si.m**2 + effective_radius = 10 * si.um + + # act + tau = formulae.optical_depth.tau( + LWP=liquid_water_path, reff=effective_radius + ) + + # assert + assert tau.check(si.dimensionless) diff --git a/PySDM/source/tests/unit_tests/physics/test_particle_shape_and_density.py b/PySDM/source/tests/unit_tests/physics/test_particle_shape_and_density.py new file mode 100644 index 0000000000000000000000000000000000000000..1b8c5b3af4088489e0bf27f0cf090ac29a9a15c2 --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_particle_shape_and_density.py @@ -0,0 +1,191 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import pytest + +import matplotlib.pyplot as plt +import numpy as np + +from PySDM.formulae import Formulae +from PySDM.physics import constants_defaults +from PySDM.physics.dimensional_analysis import DimensionalAnalysis + + +class TestParticleShapeAndDensity: + @staticmethod + @pytest.mark.parametrize("variant", ("LiquidSpheres", "MixedPhaseSpheres")) + def test_mass_to_volume_units(variant): + with DimensionalAnalysis(): + # Arrange + formulae = Formulae(particle_shape_and_density=variant) + si = constants_defaults.si + sut = formulae.particle_shape_and_density.mass_to_volume + mass = 1 * si.gram + + # Act + volume = sut(mass) + + # Assert + assert volume.check("[volume]") + + @staticmethod + @pytest.mark.parametrize("variant", ("LiquidSpheres", "MixedPhaseSpheres")) + def test_volume_to_mass_units(variant): + with DimensionalAnalysis(): + # Arrange + formulae = Formulae(particle_shape_and_density=variant) + si = constants_defaults.si + sut = formulae.particle_shape_and_density.volume_to_mass + volume = 1 * si.micrometre**3 + + # Act + mass = sut(volume) + + # Assert + assert mass.check("[mass]") + + @staticmethod + @pytest.mark.parametrize("variant", ("LiquidSpheres",)) + def test_reynolds_number(variant): + with DimensionalAnalysis(): + # Arrange + formulae = Formulae(particle_shape_and_density=variant) + si = constants_defaults.si + sut = formulae.particle_shape_and_density.reynolds_number + + # Act + re = sut( + radius=10 * si.um, + dynamic_viscosity=20 * si.uPa * si.s, + velocity_wrt_air=1 * si.cm / si.s, + density=1 * si.kg / si.m**3, + ) + + # Assert + assert re.check(si.dimensionless) + + @staticmethod + @pytest.mark.parametrize("variant", ("ColumnarIce",)) + def test_spheroid_shape_units(variant): + with DimensionalAnalysis(): + # Arrange + si = constants_defaults.si + formulae = Formulae(particle_shape_and_density=variant) + mass = 1e-10 * si.kg + columnar_shape = formulae.particle_shape_and_density + + # Act + polar_radius = columnar_shape.polar_radius_empirical_parametrisation(mass) + aspect_ratio = columnar_shape.aspect_ratio_empirical_parametrisation(mass) + eccentricity = columnar_shape.eccentricity(aspect_ratio) + equatorial_radius = columnar_shape.equatorial_radius( + polar_radius, aspect_ratio + ) + + # Assert + assert polar_radius.check("[length]") + assert equatorial_radius.check("[length]") + assert aspect_ratio.check(si.dimensionless) + assert eccentricity.check(si.dimensionless) + + @staticmethod + def test_columnar_ice_geometric_values_against_spichtinger_and_gierens_2009_fig_1_and_2( + plot=False, + ): + """Fig. 1 & 2 in [Spichtinger & Gierens 2009](https://doi.org/10.5194/acp-9-685-2009)""" + # arrange + si = constants_defaults.si + mass = np.logspace(base=10, start=-16, stop=-7, num=10) * si.kg + formulae = Formulae(particle_shape_and_density="ColumnarIce") + columnar_shape = formulae.particle_shape_and_density + + polar_diameter_reference = ( + np.array( + [ + 0.57, + 1.24, + 2.67, + 5.74, + 14.92, + 42.51, + 121.08, + 344.85, + 982.14, + 2797.16, + ] + ) + * si.micrometer + ) + aspect_ratio_reference = np.array( + [ + 1, + 1, + 1, + 1, + 1.32, + 2.01, + 3.05, + 4.64, + 7.05, + 10.73, + ] + ) + + # Act + polar_radius = columnar_shape.polar_radius_empirical_parametrisation(mass) + aspect_ratio = columnar_shape.aspect_ratio_empirical_parametrisation(mass) + equatorial_radius = columnar_shape.equatorial_radius(polar_radius, aspect_ratio) + + polar_diameter = polar_radius * 2 + equatorial_diameter = equatorial_radius * 2 + + # plot + plt.xlabel("mass (kg)") + plt.ylabel("length (m)") + plt.xlim(mass[0], mass[-1]) + plt.xscale("log") + plt.yscale("log") + plt.grid() + + plt.title("ColumnarIce length") + plt.plot(mass, polar_diameter * 2, color="red") + plt.plot(mass, equatorial_diameter * 2, color="blue") + + if plot: + plt.show() + else: + plt.clf() + + # Assert + np.testing.assert_almost_equal( + polar_diameter, polar_diameter_reference, decimal=1 + ) + np.testing.assert_almost_equal(aspect_ratio, aspect_ratio_reference, decimal=1) + + @staticmethod + @pytest.mark.parametrize("variant", ("LiquidSpheres",)) + def test_dm_dt_over_m_units(variant): + with DimensionalAnalysis(): + # arrange + formulae = Formulae(particle_shape_and_density=variant) + si = constants_defaults.si + sut = formulae.particle_shape_and_density.dm_dt_over_m + + # act + re = sut(r=1 * si.um, r_dr_dt=1 * si.um**2 / si.s) + + # assert + assert re.check("1/[time]") + + @staticmethod + @pytest.mark.parametrize("variant", ("LiquidSpheres",)) + def test_r_dr_dt_units(variant): + with DimensionalAnalysis(): + # arrange + formulae = Formulae(particle_shape_and_density=variant) + si = constants_defaults.si + sut = formulae.particle_shape_and_density.r_dr_dt + + # act + re = sut(r=1 * si.um, dm_dt_over_m=1 / si.s) + + # assert + assert re.check("[area]/[time]") diff --git a/PySDM/source/tests/unit_tests/physics/test_saturation_vapour_pressure.py b/PySDM/source/tests/unit_tests/physics/test_saturation_vapour_pressure.py new file mode 100644 index 0000000000000000000000000000000000000000..c4b1fd1329e4d1c7dddfcc20cc389bcb34f9aac1 --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_saturation_vapour_pressure.py @@ -0,0 +1,99 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import inspect +import pytest + +import numpy as np +from matplotlib import pyplot + +from PySDM import Formulae +from PySDM.formulae import _choices +from PySDM.physics import constants_defaults as const +from PySDM.physics import saturation_vapour_pressure, si + + +class TestSaturationVapourPressure: + @staticmethod + def test_saturation_vapour_pressure(plot=False): + # Arrange + choices = _choices(saturation_vapour_pressure) + formulae = {k: Formulae(saturation_vapour_pressure=k) for k in choices} + temperature = np.linspace(-0.2, 0.4) + + # Plot + pyplot.axhline(const.p_tri, label="triple point", color="red") + pyplot.axvline(const.T_tri - const.T0, color="red") + for key, val in formulae.items(): + for name, func in inspect.getmembers(val.saturation_vapour_pressure): + if name[:2] not in ("__", "a_"): + if not ( + key in ("AugustRocheMagnus", "Wexler1976", "Bolton1980") + and name == "pvs_ice" + ): + pyplot.plot( + temperature, + func(temperature + const.T0), + label=f"{key}::{name}", + ) + pyplot.grid() + pyplot.legend() + pyplot.xlabel("T [C]") + pyplot.ylabel("p [Pa]") + if plot: + pyplot.show() + + # Assert + temperature = np.linspace(-20, 20, 100) + choices_keys = tuple(choices.keys()) + for choice in choices_keys[1:]: + np.testing.assert_allclose( + Formulae( + saturation_vapour_pressure=choices_keys[0] + ).saturation_vapour_pressure.pvs_water(temperature + const.T0), + Formulae( + saturation_vapour_pressure=choice + ).saturation_vapour_pressure.pvs_water(temperature + const.T0), + rtol=2e-2, + ) + + for choice in choices_keys[1:]: + if not choice in ("AugustRocheMagnus", "Bolton1980", "Wexler1976"): + temperature = np.linspace(-20, 0, 100) + np.testing.assert_array_less( + Formulae( + saturation_vapour_pressure="FlatauWalkoCotton" + ).saturation_vapour_pressure.pvs_ice(temperature + const.T0), + Formulae( + saturation_vapour_pressure=choice + ).saturation_vapour_pressure.pvs_water(temperature + const.T0), + ) + temperature = np.linspace(1, 1, 100) + np.testing.assert_array_less( + Formulae( + saturation_vapour_pressure="FlatauWalkoCotton" + ).saturation_vapour_pressure.pvs_water(temperature + const.T0), + Formulae( + saturation_vapour_pressure=choice + ).saturation_vapour_pressure.pvs_ice(temperature + const.T0), + ) + + @staticmethod + @pytest.mark.parametrize( + "T_C, expected_es_mb", + ( + (-40, 0.1905), + (-30, 0.5106), + (-20, 1.2563), + (-10, 2.8657), + (0, 6.1121), + (10, 12.279), + (20, 23.385), + (30, 42.452), + (40, 73.813), + ), + ) + def test_wexler_1976_table_1(T_C, expected_es_mb): + formulae = Formulae(saturation_vapour_pressure="Wexler1976") + actual_es = formulae.saturation_vapour_pressure.pvs_water(T_C + const.T0) + np.testing.assert_approx_equal( + actual=actual_es, desired=expected_es_mb * si.mbar, significant=4 + ) diff --git a/PySDM/source/tests/unit_tests/physics/test_spectra.py b/PySDM/source/tests/unit_tests/physics/test_spectra.py new file mode 100644 index 0000000000000000000000000000000000000000..2321f5540686880bb1823de99b47fbccc152bb80 --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_spectra.py @@ -0,0 +1,119 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest +from numpy.testing import assert_approx_equal + +from PySDM.initialisation.sampling.spectral_sampling import default_cdf_range +from PySDM.initialisation.spectra import Exponential, Lognormal, Sum + + +class TestLognormal: + @staticmethod + def test_size_distribution_n_part(): + # Arrange + s = 1.5 + n_part = 256 + sut = Lognormal(n_part, 0.5e-5, s) + + # Act + m, dm = np.linspace(0.1e-6, 100e-6, 100, retstep=True) + sd = sut.size_distribution(m) + + # Assert + assert_approx_equal(np.sum(sd) * dm, n_part, 4) + + @staticmethod + def test_size_distribution_r_mode(): + # Arrange + s = 1.001 + r_mode = 1e-6 + sut = Lognormal(norm_factor=1, m_mode=r_mode, s_geom=s) + + # Act + m = np.linspace(start=0.01e-6, stop=100e-6, num=1000) + sd = sut.size_distribution(m) + + # Assert + ((index,),) = np.where(sd == np.amax(sd)) + assert_approx_equal(actual=m[index], desired=r_mode, significant=2) + + +class TestExponential: # pylint: disable=too-few-public-methods + @staticmethod + @pytest.mark.parametrize( + "scale", + [ + pytest.param(0.5), + pytest.param(1), + pytest.param(1.5), + ], + ) + def test_size_distribution_n_part(scale): + # Arrange + scale = 1 + n_part = 256 + sut = Exponential(n_part, scale) + + # Act + m, dm = np.linspace(0, 5, 10000, retstep=True) + sd = sut.size_distribution(m) + + # Assert + assert_approx_equal(np.sum(sd) * dm, n_part, 2) + + +class TestSum: + scale = 1 + n_part = 256 + exponential = Exponential(n_part, scale) + + s = 1.001 + r_mode = 1e-6 + lognormal = Lognormal(1, r_mode, s) + + @staticmethod + def test_size_distribution(): + # Arrange + sut = Sum((TestSum.exponential,)) + + # Act + x = np.linspace(0, 1) + sut_sd = sut.size_distribution(x) + exp_sd = TestSum.exponential.size_distribution(x) + + # Assert + np.testing.assert_array_equal(sut_sd, exp_sd) + + @staticmethod + def test_cumulative(): + # Arrange + sut = Sum((TestSum.exponential,)) + + # Act + x = np.linspace(0, 1) + sut_c = sut.cumulative(x) + exp_c = TestSum.exponential.cumulative(x) + + # Assert + np.testing.assert_array_equal(sut_c, exp_c) + + @staticmethod + @pytest.mark.parametrize( + "distributions", + [ + pytest.param((exponential,), id="single exponential"), + pytest.param((lognormal,), id="single lognormal"), + pytest.param((exponential, exponential), id="2 exponentials"), + ], + ) + def test_percentiles(distributions): + # Arrange + sut = Sum(distributions) + + # Act + cdf_values = np.linspace(*default_cdf_range, 100) + sut_p = sut.percentiles(cdf_values) + exp_p = distributions[0].percentiles(cdf_values) + + # Assert + np.testing.assert_array_almost_equal(sut_p, exp_p, decimal=3) diff --git a/PySDM/source/tests/unit_tests/physics/test_spectra_top_hat.py b/PySDM/source/tests/unit_tests/physics/test_spectra_top_hat.py new file mode 100644 index 0000000000000000000000000000000000000000..260431c11420a32a933ccb3f2ad061f6a9096387 --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_spectra_top_hat.py @@ -0,0 +1,66 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +from matplotlib import pyplot + +from PySDM.initialisation import spectra + + +class TestSpectraTopHat: + @staticmethod + def test_cumulative(plot=False): + # arrange + endpoints = (44, 666) + norm_factor = np.pi + spectrum = spectra.TopHat(norm_factor=norm_factor, endpoints=endpoints) + x = np.linspace(0, 1000) + + # act + y = spectrum.cumulative(x) + + # plot + if plot: + pyplot.axhline(0) + pyplot.axhline(norm_factor) + pyplot.axvline(endpoints[0]) + pyplot.axvline(endpoints[1]) + pyplot.plot(x, y, color="red") + pyplot.xlim(x[0], x[-1]) + pyplot.grid() + pyplot.show() + + # assert + for point in y: + assert 0 <= point <= norm_factor + assert y[-1] == norm_factor + + hy, hx = np.histogram(np.diff(y) / np.diff(x)) + assert hx[0] == 0 + np.testing.assert_approx_equal( + hx[-1], norm_factor / (endpoints[1] - endpoints[0]) + ) + assert np.sum(hy[1:-1]) == 2 + + @staticmethod + def test_percentiles(plot=False): + # arrange + spectrum = spectra.TopHat(norm_factor=np.e, endpoints=(-np.pi, np.pi)) + x = np.linspace(0, 1) + + # act + y = spectrum.percentiles(x) + + # plot + if plot: + pyplot.axvline(0) + pyplot.axvline(1) + pyplot.axhline(spectrum.endpoints[0]) + pyplot.axhline(spectrum.endpoints[1]) + pyplot.plot(x, y, color="red") + pyplot.show() + + # assert + assert y[0] == spectrum.endpoints[0] + assert y[-1] == spectrum.endpoints[1] + np.testing.assert_allclose( + np.diff(y) / np.diff(x), spectrum.endpoints[1] - spectrum.endpoints[0] + ) diff --git a/PySDM/source/tests/unit_tests/physics/test_surface_tension.py b/PySDM/source/tests/unit_tests/physics/test_surface_tension.py new file mode 100644 index 0000000000000000000000000000000000000000..85ad9491ec38147e257f159cbe6e0af3d18bbbfd --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_surface_tension.py @@ -0,0 +1,91 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import warnings + +import matplotlib.pyplot as plt +import numpy as np +from numba.core.errors import NumbaExperimentalFeatureWarning + +from PySDM import Formulae +from PySDM.physics import si + + +def test_surface_tension(plot=False): + ls = ["-", ":", "--", "-."] + + st_type = ( + "Constant", + "CompressedFilmOvadnevaite", + "SzyszkowskiLangmuir", + "CompressedFilmRuehl", + ) + # Arrange + TRIVIA = Formulae().trivia + R_WET = np.logspace(np.log(100 * si.nm), np.log(1000 * si.nm), base=np.e, num=100) + R_DRY = 50 * si.nm + V_WET = TRIVIA.volume(R_WET) + V_DRY = TRIVIA.volume(R_DRY) + TEMPERATURE = 300 * si.K + + ################ + # test zero organic + ################ + + F_ORG = 0.0 + sgm = calc_sigma(st_type, TEMPERATURE, V_WET, V_DRY, F_ORG) + + for i, st in enumerate(st_type): + np.testing.assert_allclose(sgm[0, :], sgm[i, :], rtol=1e-6) + plt.plot(R_WET / si.um, sgm[i, :], ls[i], label=st) + + if plot: + plt.title(f"Forg = {F_ORG}") + plt.xlabel("wet radius (um)") + plt.xscale("log") + plt.ylabel("surface tension [N/m]") + plt.legend() + plt.show() + + ################ + # test all organic + ################ + + F_ORG = 1.0 + sgm = calc_sigma(st_type, TEMPERATURE, V_WET, V_DRY, F_ORG) + + for i, st in enumerate(st_type): + if i > 0: + np.testing.assert_array_less(sgm[i, :], sgm[0, :]) + plt.plot(R_WET / si.um, sgm[i, :], ls[i], label=st) + + if plot: + plt.title(f"Forg = {F_ORG}") + plt.xlabel("wet radius (um)") + plt.xscale("log") + plt.ylabel("surface tension [N/m]") + plt.legend() + plt.show() + + +def calc_sigma(st_type, TEMPERATURE, V_WET, V_DRY, F_ORG): + sgm = np.zeros((len(st_type), len(V_WET))) + + with warnings.catch_warnings(): + warnings.simplefilter("ignore", category=NumbaExperimentalFeatureWarning) + for i, st in enumerate(st_type): + f = Formulae( + surface_tension=st, + constants={ + "sgm_org": 10 * si.mN / si.m, + "delta_min": 1 * si.nm, + "RUEHL_A0": 1e-17 * si.m * si.m, + "RUEHL_C0": 1e-8, + "RUEHL_m_sigma": 1e17 * si.J / si.m**2, + "RUEHL_sgm_min": 10 * si.mN / si.m, + "RUEHL_nu_org": 1e2 * si.cm**3 / si.mole, + }, + ) + + for j, vw in enumerate(V_WET): + sgm[i, j] = f.surface_tension.sigma(TEMPERATURE, vw, V_DRY, F_ORG) + + return sgm diff --git a/PySDM/source/tests/unit_tests/physics/test_terminal_velocity.py b/PySDM/source/tests/unit_tests/physics/test_terminal_velocity.py new file mode 100644 index 0000000000000000000000000000000000000000..b9720ddb44a552e076aaa3831a0de2d055a3422f --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_terminal_velocity.py @@ -0,0 +1,65 @@ +"""tests for terminal velocity formulae (both liquid and ice phase)""" + +from PySDM.physics.dimensional_analysis import DimensionalAnalysis +from PySDM.physics import constants_defaults, terminal_velocity, terminal_velocity_ice + + +class TestTerminalVelocity: + @staticmethod + def test_unit_liquid_rogers_yau(): + with DimensionalAnalysis(): + # arrange + si = constants_defaults.si + radius = 44 * si.um + + # act + velocity = terminal_velocity.RogersYau.v_term( + constants_defaults, radius=radius + ) + + # assert + assert velocity.check("[length] / [time]") # pylint: disable=no-member + + @staticmethod + def test_unit_solid_spherical(): + with DimensionalAnalysis(): + # arrange + si = constants_defaults.si + radius = 44 * si.um + dynamic_viscocity = 0.666 * si.mPa * si.s + + # act + prefactor = terminal_velocity_ice.IceSphere.stokes_regime( + constants_defaults, radius=radius, dynamic_viscosity=dynamic_viscocity + ) + velocity = terminal_velocity_ice.IceSphere.v_base_term( + constants_defaults, radius=radius, prefactor=prefactor + ) + + # assert + assert velocity.check("[length] / [time]") + + @staticmethod + def test_unit_solid_columnar(): + with DimensionalAnalysis(): + # arrange + si = constants_defaults.si + mass = 44 * si.ug + temperature = 250 * si.K + pressure = 1000 * si.hPa + + # act + correction_factor = ( + terminal_velocity_ice.ColumnarIceCrystal.atmospheric_correction_factor( + constants_defaults, temperature=temperature, pressure=pressure + ) + ) + velocity = ( + correction_factor + * terminal_velocity_ice.ColumnarIceCrystal.v_base_term( + constants_defaults, mass=mass + ) + ) + + # assert + assert velocity.check("[length] / [time]") diff --git a/PySDM/source/tests/unit_tests/physics/test_thermal_conductivity.py b/PySDM/source/tests/unit_tests/physics/test_thermal_conductivity.py new file mode 100644 index 0000000000000000000000000000000000000000..90b4f34f73c3a2773f2d92c752ac269377d80344 --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_thermal_conductivity.py @@ -0,0 +1,42 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import inspect + +import numpy as np +from matplotlib import pyplot + +from PySDM import Formulae +from PySDM.physics import constants_defaults as const + + +def test_thermal_conductivities(plot=False): + # Arrange + formulae = {k: Formulae(diffusion_thermics=k) for k in ("LoweEtAl2019",)} + temperature = np.linspace(-20.2, 20.4) + const.T_tri + pressure = const.p_tri + + # Plot + pyplot.axhline(const.K0, label="TracyWelchPorter", color="red") + for key, val in formulae.items(): + for name, func in inspect.getmembers(val.diffusion_thermics): + if name[:2] not in ("__", "a_", "D"): + pyplot.plot( + temperature, func(temperature, pressure), label=f"{key}::{name}" + ) + pyplot.grid() + pyplot.legend() + pyplot.xlabel("T [C]") + pyplot.ylabel("k [J/m/s/K]") + if plot: + pyplot.show() + + # Assert + temperature = np.linspace(-20, 20, 100) + const.T_tri + np.testing.assert_allclose( + Formulae(diffusion_thermics="TracyWelchPorter").diffusion_thermics.K( + temperature, pressure + ), + Formulae(diffusion_thermics="LoweEtAl2019").diffusion_thermics.K( + temperature, pressure + ), + rtol=1e-1, + ) diff --git a/PySDM/source/tests/unit_tests/physics/test_trivia.py b/PySDM/source/tests/unit_tests/physics/test_trivia.py new file mode 100644 index 0000000000000000000000000000000000000000..4bfdbb2a0791faa69c36ecea8fbc701b0d823d72 --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_trivia.py @@ -0,0 +1,310 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest +from scipy.special import erfinv # pylint: disable=no-name-in-module + +from PySDM import Formulae +from PySDM.physics.dimensional_analysis import DimensionalAnalysis +from PySDM.physics import constants_defaults, si +from PySDM.physics.trivia import Trivia + + +CONST = Formulae().constants + + +class TestTrivia: + @staticmethod + @pytest.mark.parametrize("x", (-0.9, -0.1, -0.01, 0, 0.01, 0.1, 0.9)) + def test_erfinv_approx_reltol(x): + # arrange + expected = erfinv(x) + + # act + actual = Trivia.erfinv_approx(const=CONST, c=x) + + # assert + if expected == 0: + assert actual == 0 + elif np.isnan(expected): + assert np.isnan(actual) + elif np.isinf(expected): + assert np.isinf(actual) + assert np.sign(actual) == np.sign(expected) + else: + assert np.abs(np.log(actual / expected)) < 1e-3 + + @staticmethod + def test_erfinv_approx_abstol(): + # arrange + + # act + params = Trivia.erfinv_approx(const=CONST, c=0.25) + + # assert + diff = np.abs(params - 0.2253) + np.testing.assert_array_less(diff, 1e-3) + + @staticmethod + def test_isotopic_enrichment_to_delta_SMOW(): + # arrange + ARBITRARY_VALUE = 44 + + # act + delta = Trivia.isotopic_enrichment_to_delta_SMOW( + E=ARBITRARY_VALUE, delta_0_SMOW=0 + ) + + # assert + assert delta == ARBITRARY_VALUE + + @staticmethod + def test_schmidt_number(): + with DimensionalAnalysis(): + # Arrange + formulae = Formulae() + si = constants_defaults.si # pylint: disable=redefined-outer-name + sut = Trivia.air_schmidt_number + eta_air = formulae.air_dynamic_viscosity.eta_air(temperature=300 * si.K) + # Act + sc = sut( + dynamic_viscosity=eta_air, + diffusivity=constants_defaults.D0, + density=1 * si.kg / si.m**3, + ) + + # Assert + assert sc.check(si.dimensionless) + + @staticmethod + def test_poissonian_avoidance_function(): + with DimensionalAnalysis(): + # Arrange + si = constants_defaults.si # pylint: disable=redefined-outer-name + sut = Trivia.poissonian_avoidance_function + + # Act + prob = sut( + r=1 / si.s, + dt=10 * si.min, + ) + + # Assert + assert prob.check(si.dimensionless) + + @staticmethod + def test_kelvin_to_celsius(): + # arrange + temperature_in_kelvin = 44 + + # act + temperature_in_celsius = Trivia.K2C(const=CONST, TK=temperature_in_kelvin) + + # assert + assert temperature_in_celsius == temperature_in_kelvin - 273.15 + + @staticmethod + def test_celsius_to_kelvin(): + # arrange + temperature_in_celsius = 666 + + # act + temperature_in_kelvin = Trivia.C2K(const=CONST, TC=temperature_in_celsius) + + # assert + assert temperature_in_kelvin == temperature_in_celsius + 273.15 + + @staticmethod + @pytest.mark.parametrize( + "bolin_number, dm_dt_over_m, expected_tau", + ((1, 2, 0.5), (2, 1, 0.5), (2, 2, 0.25)), + ) + def test_tau(bolin_number, dm_dt_over_m, expected_tau): + # arrange + sut = Trivia.tau + + # act + value = sut(Bo=bolin_number, dm_dt_over_m=dm_dt_over_m) + + # assert + np.testing.assert_almost_equal(actual=value, desired=expected_tau) + + @staticmethod + @pytest.mark.parametrize( + "molecular_isotopic_ratio", + (0.86 * CONST.VSMOW_R_2H, 0.9 * CONST.VSMOW_R_2H, 0.98 * CONST.VSMOW_R_2H), + ) + @pytest.mark.parametrize("water_mass", np.linspace(10**-2, 10**3, 9) * si.ng) + @pytest.mark.parametrize( + "mass_other_heavy_isotopes", np.linspace(10**-7, 10**-2, 3) * si.ng + ) + @pytest.mark.parametrize( + "heavy_isotope_name, heavy_isotope_molecule", + (("2H", "2H_1H_16O"), ("17O", "1H2_17O"), ("18O", "1H2_17O")), + ) + def test_moles_heavy_atom( + molecular_isotopic_ratio, + water_mass, + heavy_isotope_name, + heavy_isotope_molecule, + mass_other_heavy_isotopes, + ): + # arrange + assert mass_other_heavy_isotopes <= water_mass + molar_mass_heavy_molecule = getattr(CONST, f"M_{heavy_isotope_molecule}") + molar_mass_light_molecule = CONST.M_1H2_16O + if heavy_isotope_name[-1] == "O": + atoms_per_heavy_molecule = 1 + elif heavy_isotope_name[-1] == "H": + atoms_per_heavy_molecule = 1 + else: + assert False + + # act + moles_heavy_atom = Trivia.moles_heavy_atom( + mass_total=water_mass, + molecular_isotopic_ratio=molecular_isotopic_ratio, + mass_other_heavy_isotopes=mass_other_heavy_isotopes, + molar_mass_light_molecule=molar_mass_light_molecule, + molar_mass_heavy_molecule=molar_mass_heavy_molecule, + atoms_per_heavy_molecule=atoms_per_heavy_molecule, + ) + + moles_heavy_molecule = atoms_per_heavy_molecule * moles_heavy_atom + moles_light_molecule = moles_heavy_molecule / molecular_isotopic_ratio + sut = ( + moles_heavy_molecule * molar_mass_heavy_molecule + + moles_light_molecule * molar_mass_light_molecule + + mass_other_heavy_isotopes + ) + + # assert + np.testing.assert_approx_equal(actual=sut, desired=water_mass, significant=5) + + @staticmethod + @pytest.mark.parametrize( + "moles_heavy_molecule", + (0, 10**-7, 10**-4), + ) + @pytest.mark.parametrize( + "mass_other_heavy_isotopes", (0 * si.ng, 10 * si.ng, 100 * si.ng) + ) + @pytest.mark.parametrize( + "heavy_isotope_molecule", + ("2H_1H_16O", "1H2_17O", "1H2_17O"), + ) + def test_molecular_isotopic_ratio( + moles_heavy_molecule, + mass_other_heavy_isotopes, + heavy_isotope_molecule, + ): + # arrange + eps = 10**-7 + mm_heavy = getattr(CONST, f"M_{heavy_isotope_molecule}") + mm_light = CONST.M_1H2_16O + + mass_heavy = mm_heavy * moles_heavy_molecule + mass_total = 10**4 * (mass_other_heavy_isotopes + mass_heavy + eps) + mass_light = mass_total - mass_other_heavy_isotopes - mass_heavy + + # act + sut = Trivia.molecular_isotopic_ratio( + mass_total=mass_total, + moles_heavy_molecule=moles_heavy_molecule, + mass_other_heavy_isotopes=mass_other_heavy_isotopes, + molar_mass_light_molecule=mm_light, + molar_mass_heavy_molecule=mm_heavy, + ) + expected = mass_heavy / mass_light * mm_light / mm_heavy + + # assert + assert mass_light >= 0 + np.testing.assert_approx_equal(desired=expected, actual=sut, significant=5) + + @staticmethod + def test_molecular_isotopic_ratio_is_dimensionless(): + with DimensionalAnalysis(): + # arrange + si = constants_defaults.si # pylint: disable=redefined-outer-name + sut = Trivia.molecular_isotopic_ratio + + # act + iso_ratio = sut( + moles_heavy_molecule=1 * si.mol, + mass_total=1 * si.g, + mass_other_heavy_isotopes=1 * si.g, + molar_mass_light_molecule=1 * si.g / si.mole, + molar_mass_heavy_molecule=1 * si.g / si.mole, + ) + + # assert + assert iso_ratio.check(si.dimensionless) + + @staticmethod + @pytest.mark.parametrize( + "molality_in_dry_air, density_dry_air, conc_vap_total, expected", + [ + (0.01, 10.0, 5.0, 0.02), + (0.0, 10.0, 5.0, 0.0), + (0.01, 0.0, 5.0, 0.0), + (1e-12, 3.0, 2.0, 1.5e-12), + ], + ) + def test_isotopic_fraction( + molality_in_dry_air, + density_dry_air, + conc_vap_total, + expected, + ): + sut = Trivia.isotopic_fraction( + molality_in_dry_air, density_dry_air, conc_vap_total + ) + + np.testing.assert_allclose(sut, expected, rtol=1e-12) + + @staticmethod + @pytest.mark.parametrize("isotopic_ratio", (0, 1e-7, 0.9)) + def test_isotopic_ratio_and_fraction_reversed(isotopic_ratio): + isotopic_fraction = Trivia.isotopic_fraction_assuming_single_heavy_isotope( + isotopic_ratio=isotopic_ratio, + ) + recovered = Trivia.isotopic_ratio_assuming_single_heavy_isotope( + isotopic_fraction=isotopic_fraction + ) + np.testing.assert_allclose(recovered, isotopic_ratio, rtol=1e-12) + + @staticmethod + @pytest.mark.parametrize( + "molar_mixing_ratio, density_dry_air, conc_vap_total", + [ + (0.01, 10.0, 5.0), + (1e-6, 1.0, 1.0), + (0.1, 2.0, 10.0), + ], + ) + def test_molality_and_isotopic_fraction_reversed( + molar_mixing_ratio, + density_dry_air, + conc_vap_total, + ): + iso = Trivia.isotopic_fraction( + molality_in_dry_air=molar_mixing_ratio, + density_dry_air=density_dry_air, + total_vap_concentration=conc_vap_total, + ) + recovered = Trivia.molality_in_dry_air( + isotopic_fraction=iso, + density_dry_air=density_dry_air, + total_vap_concentration=conc_vap_total, + ) + np.testing.assert_allclose(recovered, molar_mixing_ratio, rtol=1e-12) + + @staticmethod + def test_molality_in_dry_air_unit(): + with DimensionalAnalysis(): + si = constants_defaults.si # pylint: disable=redefined-outer-name + molality = Trivia.molality_in_dry_air( + isotopic_fraction=1 * si.dimensionless, + density_dry_air=1 * si.kg / si.m**3, + total_vap_concentration=1 * si.mole / si.m**3, + ) + assert molality.check(si.mole / si.kg) diff --git a/PySDM/source/tests/unit_tests/physics/test_ventilation_coefficient.py b/PySDM/source/tests/unit_tests/physics/test_ventilation_coefficient.py new file mode 100644 index 0000000000000000000000000000000000000000..fc3a7107ddab39fa79a7406bc5a74eeaf492cb88 --- /dev/null +++ b/PySDM/source/tests/unit_tests/physics/test_ventilation_coefficient.py @@ -0,0 +1,168 @@ +""" +tests for ventilation coefficient implementation +""" + +from matplotlib import pyplot +import numpy as np +import pytest +from PySDM import Formulae +from PySDM.formulae import _choices +from PySDM.physics import ventilation, constants_defaults, in_unit +from PySDM.physics.dimensional_analysis import DimensionalAnalysis + + +class TestVentilationCoefficient: + @staticmethod + @pytest.mark.parametrize("variant", _choices(ventilation)) + def test_dimensionality(variant): + with DimensionalAnalysis(): + # Arrange + formulae = Formulae(ventilation=variant) + si = constants_defaults.si + sut = formulae.ventilation.ventilation_coefficient + + # Act + re = sut(sqrt_re_times_cbrt_sc=1 * si.dimensionless) + + # Assert + assert re.check(si.dimensionless) + + @staticmethod + @pytest.mark.parametrize( + "X, fV", + ( + (0.133, 1.020), + (0.665, 1.020), + (1.662, 1.250), + (3.989, 1.974), + (6.582, 2.797), + (9.440, 3.686), + (11.17, 4.212), + (13.50, 4.936), + (16.69, 5.956), + (20.21, 7.075), + (23.53, 8.128), + (27.52, 9.346), + (30.85, 10.40), + (34.57, 11.58), + (38.16, 12.70), + (41.22, 13.66), + (44.61, 14.74), + (48.53, 15.93), + ), + ) + def test_vs_digitized_data_from_pruppacher_and_rassmussen_1979_figure_1(X, fV): + formulae = Formulae(ventilation="PruppacherAndRasmussen1979") + fV_test = formulae.ventilation.ventilation_coefficient(X) + np.testing.assert_approx_equal(fV_test, fV, significant=1.95) + + @staticmethod + def test_neglect_returns_unity(): + formulae = Formulae(ventilation="Neglect") + X = np.linspace(0, 50) + assert (formulae.ventilation.ventilation_coefficient(X) == 1).all() + + @staticmethod + def test_all_variants_similar(plot=False): + # arrange/act + X = np.linspace(0, 50) + coeffs = { + paper: Formulae(ventilation=paper).ventilation.ventilation_coefficient(X) + for paper in _choices(ventilation) + if paper != "Neglect" + } + + # plot + for paper, coeff in coeffs.items(): + pyplot.plot(X, coeff, label=paper) + pyplot.ylabel("Ventilation Coefficient") + pyplot.xlabel("$Sc^{1/3} Re^{1/2}$") + pyplot.grid() + pyplot.legend() + if plot: + pyplot.show() + else: + pyplot.clf() + + # assert + for paper, coeff in coeffs.items(): + reference = "Froessling1938" + if paper == reference: + continue + assert (abs(1 - coeff / coeffs[reference]) < 0.15).all() + + @staticmethod + def test_pruppacher_and_rasmussen_1979_finite_value_at_threshold(): + # arrange + formulae = Formulae(ventilation="PruppacherAndRasmussen1979") + sut = formulae.ventilation.ventilation_coefficient + + # act + f = sut( + sqrt_re_times_cbrt_sc=constants_defaults.PRUPPACHER_RASMUSSEN_1979_XTHRES + ) + + # assert + assert np.isfinite(f) + assert 1.2 < f < 1.22 + + @staticmethod + @pytest.mark.parametrize( + "diameter_cm, first_factor_cm, temperature", + ( + (0.01, 0.086, constants_defaults.T0), + (0.05, 1.01, constants_defaults.T0), + (0.1, 2.90, constants_defaults.T0), + (0.2, 8.80, constants_defaults.T0), + (0.3, 20.1, constants_defaults.T0), + (0.01, 0.082, constants_defaults.T0 + 10), + (0.05, 0.99, constants_defaults.T0 + 10), + (0.1, 2.80, constants_defaults.T0 + 10), + (0.2, 8.50, constants_defaults.T0 + 10), + (0.3, 19.3, constants_defaults.T0 + 10), + (0.01, 0.079, constants_defaults.T0 + 20), + (0.05, 0.97, constants_defaults.T0 + 20), + (0.1, 2.80, constants_defaults.T0 + 20), + (0.2, 8.30, constants_defaults.T0 + 20), + (0.3, 18.5, constants_defaults.T0 + 20), + (0.01, 0.076, constants_defaults.T0 + 30), + (0.05, 0.96, constants_defaults.T0 + 30), + (0.1, 2.70, constants_defaults.T0 + 30), + (0.2, 8.10, constants_defaults.T0 + 30), + (0.3, 17.8, constants_defaults.T0 + 30), + (0.01, 0.073, constants_defaults.T0 + 40), + (0.05, 0.94, constants_defaults.T0 + 40), + (0.1, 2.70, constants_defaults.T0 + 40), + (0.2, 8.00, constants_defaults.T0 + 40), + (0.3, 17.2, constants_defaults.T0 + 40), + ), + ) + def test_table_1_from_kinzer_and_gunn_1951( + diameter_cm, temperature, first_factor_cm + ): + # arrange + formulae = Formulae( + ventilation="PruppacherAndRasmussen1979", terminal_velocity="RogersYau" + ) + si = constants_defaults.si + const = formulae.constants + air_density = 1 * si.kg / si.m**3 + radius = diameter_cm * si.cm / 2 + dynamic_viscosity = formulae.air_dynamic_viscosity.eta_air(temperature) + Sc = formulae.trivia.air_schmidt_number( + dynamic_viscosity, const.D0, air_density + ) + Re = formulae.particle_shape_and_density.reynolds_number( + radius, + formulae.terminal_velocity.v_term(radius), + dynamic_viscosity, + air_density, + ) + vent_coeff = formulae.ventilation.ventilation_coefficient( + formulae.trivia.sqrt_re_times_cbrt_sc(Re, Sc) + ) + np.testing.assert_allclose( + actual=in_unit(4 * np.pi * radius * vent_coeff, si.cm), + desired=first_factor_cm, + rtol=0.31, + ) diff --git a/PySDM/source/tests/unit_tests/products/__init__.py b/PySDM/source/tests/unit_tests/products/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PySDM/source/tests/unit_tests/products/test_activation_criteria.py b/PySDM/source/tests/unit_tests/products/test_activation_criteria.py new file mode 100644 index 0000000000000000000000000000000000000000..37b4dde0f93e3823dc8e91a308c41867e3fc71d7 --- /dev/null +++ b/PySDM/source/tests/unit_tests/products/test_activation_criteria.py @@ -0,0 +1,128 @@ +"""tests four different ways of diagnosing activable fraction in a Parcel environment""" + +import pytest +import numpy as np +from matplotlib import pyplot + +from PySDM.products import ( + ActivableFraction, + AmbientRelativeHumidity, + PeakSaturation, + Time, +) +from PySDM.environments import Parcel +from PySDM.dynamics import Condensation, AmbientThermodynamics +from PySDM import Builder +from PySDM.physics import si +from PySDM.backends import CPU, GPU +from PySDM.initialisation.spectra import Lognormal +from PySDM.initialisation.sampling import spectral_sampling + + +@pytest.mark.parametrize( + "backend", + ( + pytest.param(GPU(), marks=pytest.mark.xfail(strict=True)), + CPU(override_jit_flags={"parallel": False}), + ), +) +def test_activation_criteria(backend, plot=False): + # arrange + builder = Builder( + n_sd=1000, + backend=backend, + environment=Parcel( + dt=2 * si.s, + mass_of_dry_air=100 * si.kg, + p0=1000 * si.hPa, + initial_water_vapour_mixing_ratio=22 * si.g / si.kg, + T0=300 * si.K, + w=2.5 * si.m / si.s, + ), + ) + builder.add_dynamic(AmbientThermodynamics()) + builder.add_dynamic(Condensation()) + + r_dry, specific_concentration = spectral_sampling.ConstantMultiplicity( + Lognormal(norm_factor=1e4 / si.mg, m_mode=50 * si.nm, s_geom=1.5) + ).sample_deterministic(builder.particulator.n_sd) + + particulator = builder.build( + attributes=builder.particulator.environment.init_attributes( + n_in_dv=specific_concentration + * builder.particulator.environment.mass_of_dry_air, + kappa=0.666, + r_dry=r_dry, + ), + products=( + Time(), + PeakSaturation(name="S_max"), + AmbientRelativeHumidity(name="RH"), + ActivableFraction( + name="AF1 (r_wet > r_cri(T))", + filter_attr="wet to critical volume ratio", + ), + ActivableFraction( + name="AF2 (r_wet > r_cri(T0))", + filter_attr="wet to critical volume ratio neglecting temperature variations", + ), + ActivableFraction( + name="AF3 (S_crit(T) < S_max)", filter_attr="critical saturation" + ), + ActivableFraction( + name="AF4 (S_crit(T0) < S_max)", + filter_attr="critical saturation neglecting temperature variations", + ), + ), + ) + + # act + data = {product: [] for product in particulator.products} + s_max = np.nan + for i in range(50): + particulator.run(steps=1) + for key, product in particulator.products.items(): + if ( + key == "S_max" + and i > 0 + and (np.isnan(s_max) or data["S_max"][-1] > s_max) + ): + s_max = data["S_max"][-1] + value = product.get(**({} if key.startswith("wet") else {"S_max": s_max})) + if isinstance(value, np.ndarray): + value = value[0] + data[key].append(value) + + # plot + pyplot.title(backend.__class__.__name__) + for k, datum in data.items(): + if k.startswith("AF"): + pyplot.plot(data["time"], datum, label=k, marker=k[2], markersize=10) + pyplot.xlabel("time [s] (values at the end of each timestep)") + pyplot.ylabel("activated fraction [1]") + pyplot.ylim(0.4, 0.65) + pyplot.legend() + pyplot.grid() + + twin = pyplot.gca().twinx() + twin.plot(data["time"], np.asarray(data["RH"]) * 100 - 100, color="red", marker="o") + twin.set_ylabel("supersaturation [%]", color="red") + twin.set_ylim(-1.5, 0.5) + twin.set_yticks(np.linspace(-1.5, 0.5, 5, endpoint=True)) + + if plot: + pyplot.show() + else: + pyplot.clf() + + # assert + for k, datum in data.items(): + if k.startswith("AF"): + assert datum[0] == 0 + assert 0.4 < datum[-1] <= 0.65 + assert np.all(np.diff(datum[40:]) <= 0) + assert ( + data["AF1 (r_wet > r_cri(T))"][-1] + < data["AF3 (S_crit(T) < S_max)"][-1] + < data["AF4 (S_crit(T0) < S_max)"][-1] + ) diff --git a/PySDM/source/tests/unit_tests/products/test_ambient_relative_humidity.py b/PySDM/source/tests/unit_tests/products/test_ambient_relative_humidity.py new file mode 100644 index 0000000000000000000000000000000000000000..a0ef78deab22c4e87f0387440b7f92261088a501 --- /dev/null +++ b/PySDM/source/tests/unit_tests/products/test_ambient_relative_humidity.py @@ -0,0 +1,43 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest + +from PySDM import Builder, products +from PySDM.backends import CPU, GPU +from PySDM.environments import Parcel +from PySDM.physics import si + + +@pytest.mark.parametrize( + "backend_class", (CPU, pytest.param(GPU, marks=pytest.mark.xfail(strict=True))) +) +def test_ambient_relative_humidity(backend_class): + # arrange + n_sd = 1 + + env = Parcel( + dt=np.nan, + mixed_phase=True, + mass_of_dry_air=np.nan, + p0=1000 * si.hPa, + initial_water_vapour_mixing_ratio=1 * si.g / si.kg, + T0=260 * si.K, + w=np.nan, + ) + builder = Builder(n_sd, backend=backend_class(), environment=env) + attributes = {"multiplicity": np.ones(n_sd), "volume": np.ones(n_sd)} + particulator = builder.build( + attributes=attributes, + products=( + products.AmbientRelativeHumidity(name="RHw", var="RH"), + products.AmbientRelativeHumidity(name="RHi", var="RH", ice=True), + ), + ) + + # act + values = {} + for name, product in particulator.products.items(): + values[name] = product.get()[0] + + # assert + assert values["RHw"] < values["RHi"] diff --git a/PySDM/source/tests/unit_tests/products/test_arbitrary_moment.py b/PySDM/source/tests/unit_tests/products/test_arbitrary_moment.py new file mode 100644 index 0000000000000000000000000000000000000000..2d5c155da03bd02eb4f0134b0ab0fde34d4da6c7 --- /dev/null +++ b/PySDM/source/tests/unit_tests/products/test_arbitrary_moment.py @@ -0,0 +1,119 @@ +"""tests of the product-class-generating `make_arbitrary_moment_product` function""" + +import pytest +import numpy as np + +from PySDM.products.size_spectral.arbitrary_moment import make_arbitrary_moment_product +from PySDM import Builder +from PySDM.backends import CPU +from PySDM.environments import Box +from PySDM.physics import si + + +class TestArbitraryMoment: + """groups tests to make [de]selecting easier""" + + @staticmethod + @pytest.mark.parametrize( + "kwargs, expected_unit", + ( + ( + { + "rank": 0, + "attr": "radius", + "attr_unit": "m", + "skip_division_by_m0": True, + "skip_division_by_dv": True, + }, + "1 dimensionless", + ), + ( + { + "rank": 1, + "attr": "radius", + "attr_unit": "m", + "skip_division_by_m0": False, + "skip_division_by_dv": True, + }, + "1 meter", + ), + ( + { + "rank": 6, + "attr": "radius", + "attr_unit": "m", + "skip_division_by_m0": True, + "skip_division_by_dv": True, + }, + "1 meter ** 6", + ), + ( + { + "rank": 1, + "attr": "water mass", + "attr_unit": "kg", + "skip_division_by_m0": False, + "skip_division_by_dv": True, + }, + "1 kilogram", + ), + ( + { + "rank": 1, + "attr": "water mass", + "attr_unit": "kg", + "skip_division_by_m0": False, + "skip_division_by_dv": True, + }, + "1 kilogram", + ), + ( + { + "rank": 1, + "attr": "water mass", + "attr_unit": "kg", + "skip_division_by_m0": False, + "skip_division_by_dv": False, + }, + "1.0 kilogram / meter ** 3", + ), + ), + ) + def test_unit(kwargs, expected_unit): + """tests if the product unit respects arguments (incl. division by dv)""" + # arrange + product_class = make_arbitrary_moment_product(**kwargs) + + # act + sut = product_class() + + # assert + assert sut.unit == expected_unit + + @staticmethod + @pytest.mark.parametrize("skip_division_by_dv", (True, False)) + def test_division_by_dv(skip_division_by_dv): + """tests, using a single-superdroplet setup, if the volume normalisation logic works""" + # arrange + dv = 666 * si.m**3 + product_class = make_arbitrary_moment_product( + rank=1, + attr="water mass", + attr_unit="kg", + skip_division_by_m0=False, + skip_division_by_dv=skip_division_by_dv, + ) + builder = Builder(n_sd=1, backend=CPU(), environment=Box(dv=dv, dt=np.nan)) + particulator = builder.build( + attributes={ + k: np.ones(builder.particulator.n_sd) + for k in ("multiplicity", "water mass") + }, + products=(product_class(name="sut"),), + ) + + # act + values = particulator.products["sut"].get() + + # assert + assert values == 1 / (1 if skip_division_by_dv else dv) diff --git a/PySDM/source/tests/unit_tests/products/test_averaged_terminal_velocity.py b/PySDM/source/tests/unit_tests/products/test_averaged_terminal_velocity.py new file mode 100644 index 0000000000000000000000000000000000000000..5280211addd41435a5c5dc3ea3a6b3a5fcced386 --- /dev/null +++ b/PySDM/source/tests/unit_tests/products/test_averaged_terminal_velocity.py @@ -0,0 +1,83 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np + +from PySDM import Builder +from PySDM.backends import CPU +from PySDM.environments import Box +from PySDM.physics import si +from PySDM.products.displacement import AveragedTerminalVelocity + +T = 300 * si.K +dt = 1 +r = ( + np.array([0.078, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.2, 1.4, 1.6]) + * si.mm + / 2 +) +u = np.array([18, 27, 72, 117, 162, 206, 247, 287, 327, 367, 403, 464, 517, 565]) / 100 + + +class TestAveragedTerminalVelocity: + @staticmethod + def _make_particulator(attributes: dict, weighting="volume"): + env = Box(dt=dt, dv=np.nan) + builder = Builder( + n_sd=len(attributes["multiplicity"]), backend=CPU(), environment=env + ) + particulator = builder.build( + attributes=attributes, + products=(AveragedTerminalVelocity(weighting=weighting),), + ) + particulator.environment["T"] = T + return particulator + + def test_mono_disperse(self): + # arrange + n_sd = 10 + vol = 4 / 3 * np.pi * 500**3 * si.um**3 + particulator = self._make_particulator( + attributes={ + "multiplicity": np.ones(n_sd), + "volume": np.full(n_sd, vol), + } + ) + + # act + vt = particulator.products["averaged terminal velocity"].get()[0] + + # assert + assert (vt - 4.03) ** 2 < 1e-6 + + def test_number_averaged_value(self): + # arrange + vol = 4 / 3 * np.pi * r**3 + particulator = self._make_particulator( + attributes={ + "multiplicity": np.ones(len(r)), + "volume": vol, + }, + weighting="number", + ) + + # act + vt = particulator.products["averaged terminal velocity"].get()[0] + + # assert + assert (vt - np.mean(u)) ** 2 < 1e-6 + + def test_volume_averaged_value(self): + # arrange + vol = 4 / 3 * np.pi * r**3 + particulator = self._make_particulator( + attributes={ + "multiplicity": np.ones(len(r)), + "volume": vol, + }, + weighting="volume", + ) + + # act + vt = particulator.products["averaged terminal velocity"].get()[0] + + # assert + assert (vt - np.sum(vol * u) / np.sum(vol)) ** 2 < 1e-6 diff --git a/PySDM/source/tests/unit_tests/products/test_collision_rates.py b/PySDM/source/tests/unit_tests/products/test_collision_rates.py new file mode 100644 index 0000000000000000000000000000000000000000..2056b501667b456b4a7204778578fce938f00e0c --- /dev/null +++ b/PySDM/source/tests/unit_tests/products/test_collision_rates.py @@ -0,0 +1,379 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest + +from PySDM import Builder, Formulae +from PySDM.backends import CPU +from PySDM.dynamics import Breakup, Coalescence, Collision +from PySDM.dynamics.collisions.breakup_efficiencies import ConstEb +from PySDM.dynamics.collisions.breakup_fragmentations import AlwaysN +from PySDM.dynamics.collisions.coalescence_efficiencies import ConstEc +from PySDM.dynamics.collisions.collision_kernels import ConstantK +from PySDM.environments import Box +from PySDM.physics import si +from PySDM.products import ( + BreakupRateDeficitPerGridbox, + BreakupRatePerGridbox, + CoalescenceRatePerGridbox, + CollisionRateDeficitPerGridbox, + CollisionRatePerGridbox, +) + +ENV_ARGS = {"dv": 1 * si.m**3, "dt": 1 * si.s} +RHO_DRY = 1 * si.kg / si.m**3 + + +class TestCollisionProducts: + @staticmethod + @pytest.mark.parametrize( + "params", + [ + { # coalescence only + "enable_breakup": False, + "cr": 4.0, + "crd": 6.0, + "cor": 4.0, + }, + { # breakup and coalescence + "enable_breakup": True, + "enable_coalescence": True, + "Ec": 1.0, + "Eb": 1.0, + "nf": np.pi, + "cr": 4.0, + "crd": 6.0, + "cor": 4.0, + "br": 0.0, + "brd": 0.0, + }, + { # breakup and coalescence + "enable_breakup": True, + "enable_coalescence": True, + "Ec": 0.0, + "Eb": 1.0, + "nf": 2.0, + "cr": 4.0, + "crd": 6.0, + "cor": 0.0, + "br": 2.0, + "brd": 2.0, + }, + { # breakup only + "enable_breakup": True, + "enable_coalescence": False, + "nf": 2.0, + "cr": 4.0, + "crd": 6.0, + "br": 2.0, + "brd": 2.0, + }, + ], + ) + def test_individual_dynamics_rates_nonadaptive(params, backend_instance): + if ( + backend_instance.__class__.__name__ == "ThrustRTC" + and params["enable_breakup"] + ): + pytest.skip("# TODO #744") + + # Arrange + n_init = [5, 2] + n_sd = len(n_init) + + env = Box(**ENV_ARGS) + builder = Builder(n_sd, backend_instance, environment=env) + + dynamic, products = _get_dynamics_and_products(params, adaptive=False) + builder.add_dynamic(dynamic) + + particulator = builder.build( + attributes={ + "multiplicity": np.asarray(n_init), + "volume": np.asarray([100 * si.um**3] * n_sd), + }, + products=products, + ) + particulator.environment["rhod"] = RHO_DRY + + # Act + particulator.run(1) + + # Assert + assert particulator.products["cr"].get()[0] == params["cr"] + assert particulator.products["crd"].get()[0] == params["crd"] + if params["enable_breakup"]: + assert particulator.products["br"].get()[0] == params["br"] + assert particulator.products["brd"].get()[0] == params["brd"] + if params["enable_coalescence"]: + assert particulator.products["cor"].get()[0] == params["cor"] + else: + assert particulator.products["cor"].get()[0] == params["cor"] + + @staticmethod + @pytest.mark.parametrize( + "params", + [ + { # coalescence only + "enable_breakup": False, + }, + { # breakup and coalescence + "enable_breakup": True, + "enable_coalescence": True, + "Ec": 1.0, + "Eb": 1.0, + "nf": np.pi, + }, + { # breakup only + "enable_breakup": True, + "enable_coalescence": False, + "nf": np.pi, + }, + ], + ) + @pytest.mark.parametrize( + "n_init", + [[5, 2], [1, 2, 3, 4], [3, 7] * 10], + ) + def test_no_collision_deficits_when_adaptive(params, n_init, backend_class=CPU): + # Arrange + n_sd = len(n_init) + env = Box(**ENV_ARGS) + builder = Builder(n_sd, backend_class(), environment=env) + + dynamic, _ = _get_dynamics_and_products( + params, adaptive=True, kernel_a=1e4 * si.cm**3 / si.s + ) + builder.add_dynamic(dynamic) + + particulator = builder.build( + attributes={ + "multiplicity": np.asarray(n_init), + "volume": np.asarray([100 * si.um**3] * n_sd), + }, + products=(CollisionRateDeficitPerGridbox(name="crd"),), + ) + particulator.environment["rhod"] = RHO_DRY + + # Act + particulator.run(1) + + # Assert + np.testing.assert_equal( + particulator.products["crd"].get()[0], np.asarray([0.0] * (n_sd // 2)) + ) + + @staticmethod + @pytest.mark.parametrize( + "params", + [ + { # breakup and coalescence + "enable_breakup": True, + "enable_coalescence": True, + "Ec": 0.1, + "Eb": 1.0, + "nf": np.pi, + }, + { # breakup only + "enable_breakup": True, + "enable_coalescence": False, + "nf": np.pi, + }, + ], + ) + def test_breakup_deficits_when_adaptive(params, backend_class=CPU): + # Arrange + n_init = [7, 353] + n_sd = len(n_init) + env = Box(**ENV_ARGS) + builder = Builder(n_sd, backend_class(), environment=env) + + dynamic, _ = _get_dynamics_and_products(params, adaptive=True) + builder.add_dynamic(dynamic) + + particulator = builder.build( + attributes={ + "multiplicity": np.asarray(n_init), + "volume": np.asarray([100 * si.um**3] * n_sd), + }, + products=( + CollisionRateDeficitPerGridbox(name="crd"), + BreakupRateDeficitPerGridbox(name="brd"), + ), + ) + particulator.environment["rhod"] = RHO_DRY + + # Act + particulator.run(1) + + # Assert + assert ( + particulator.products["brd"].get()[0] > np.asarray([0.0] * (n_sd // 2)) + ).all() + + @staticmethod + @pytest.mark.parametrize( + "params", + [ + { # breakup and coalescence + "enable_breakup": True, + "enable_coalescence": True, + "Ec": 0.1, + "Eb": 1.0, + "nf": np.pi, + }, + { # breakup only + "enable_breakup": True, + "enable_coalescence": False, + "nf": np.pi, + }, + ], + ) + def test_no_breakup_deficits_when_while_loop(params, backend_class=CPU): + # Arrange + n_init = [7, 353] + n_sd = len(n_init) + env = Box(**ENV_ARGS) + builder = Builder( + n_sd, backend_class(Formulae(handle_all_breakups=True)), environment=env + ) + + dynamic, _ = _get_dynamics_and_products( + params, adaptive=True, kernel_a=1e4 * si.cm**3 / si.s + ) + builder.add_dynamic(dynamic) + + particulator = builder.build( + attributes={ + "multiplicity": np.asarray(n_init), + "volume": np.asarray([100 * si.um**3] * n_sd), + }, + products=( + BreakupRatePerGridbox(name="br"), + BreakupRateDeficitPerGridbox(name="brd"), + ), + ) + particulator.environment["rhod"] = RHO_DRY + + # Act + particulator.run(1) + + # Assert + assert ( + particulator.products["br"].get()[0] > np.asarray([0.0] * (n_sd // 2)) + ).all() + assert ( + particulator.products["brd"].get()[0] == np.asarray([0.0] * (n_sd // 2)) + ).all() + + @staticmethod + @pytest.mark.parametrize( + "params", + [ + { # coalescence only + "enable_breakup": False, + }, + { # breakup and coalescence + "enable_breakup": True, + "enable_coalescence": True, + "Ec": 0.1, + "Eb": 1.0, + "nf": np.pi, + }, + { # breakup only + "enable_breakup": True, + "enable_coalescence": False, + "nf": np.pi, + }, + ], + ) + def test_rate_sums_single_cell(params, backend_class=CPU): + # Arrange + n_init = [7, 353] + n_sd = len(n_init) + env = Box(**ENV_ARGS) + builder = Builder(n_sd, backend_class(), environment=env) + + dynamic, products = _get_dynamics_and_products(params, adaptive=False) + builder.add_dynamic(dynamic) + + particulator = builder.build( + attributes={ + "multiplicity": np.asarray(n_init), + "volume": np.asarray([100 * si.um**3] * n_sd), + }, + products=products, + ) + particulator.environment["rhod"] = RHO_DRY + + # Act + particulator.run(1) + rhs_sum = _get_product_component_sums(params, particulator.products) + + # Assert + assert (particulator.products["cr"].get()[0] == rhs_sum).all() + + +def _get_dynamics_and_products(params, adaptive, kernel_a=1e6 * si.cm**3 / si.s): + kernel = ConstantK(a=kernel_a) + if params["enable_breakup"]: + if params["enable_coalescence"]: + dynamic = Collision( + collision_kernel=kernel, + coalescence_efficiency=ConstEc(Ec=params["Ec"]), + breakup_efficiency=ConstEb(Eb=params["Eb"]), + fragmentation_function=AlwaysN(n=params["nf"]), + adaptive=adaptive, + ) + products = ( + CollisionRatePerGridbox(name="cr"), + CollisionRateDeficitPerGridbox(name="crd"), + CoalescenceRatePerGridbox(name="cor"), + BreakupRatePerGridbox(name="br"), + BreakupRateDeficitPerGridbox(name="brd"), + ) + else: + dynamic = Breakup( + collision_kernel=kernel, + fragmentation_function=AlwaysN(n=params["nf"]), + adaptive=adaptive, + ) + products = ( + CollisionRatePerGridbox(name="cr"), + CollisionRateDeficitPerGridbox(name="crd"), + BreakupRatePerGridbox(name="br"), + BreakupRateDeficitPerGridbox(name="brd"), + ) + else: + dynamic = Coalescence( + collision_kernel=kernel, + coalescence_efficiency=ConstEc(Ec=1.0), + adaptive=adaptive, + ) + products = ( + CollisionRatePerGridbox(name="cr"), + CollisionRateDeficitPerGridbox(name="crd"), + CoalescenceRatePerGridbox(name="cor"), + ) + return (dynamic, products) + + +def _get_product_component_sums(params, products): + if params["enable_breakup"]: + product_sum = products["br"].get()[0] + products["brd"].get()[0] + if params["enable_coalescence"]: + product_sum += products["cor"].get()[0] + else: + product_sum = products["cor"].get()[0] + + return product_sum + + +def _get_product_rate_diffs_multicell(output, breakup): + product_sum = output["coalescence_rate"] + if breakup: + product_sum += output["breakup_rate"] + product_sum += output["breakup_deficit"] + + product_sum -= output["collision_rate"] + + return product_sum diff --git a/PySDM/source/tests/unit_tests/products/test_concentration_product.py b/PySDM/source/tests/unit_tests/products/test_concentration_product.py new file mode 100644 index 0000000000000000000000000000000000000000..868b682b95793d8e9dd1f41df049ea34545f2ced --- /dev/null +++ b/PySDM/source/tests/unit_tests/products/test_concentration_product.py @@ -0,0 +1,64 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest + +from PySDM import Builder +from PySDM.environments import Box +from PySDM.physics import si +from PySDM.products import ParticleConcentration, TotalParticleConcentration + +N_SD = 11 +DV = 22 * si.m**3 +MULTIPLICITY = 33 +DROP_VOLUME = 44 * si.um**3 +RHOD = 1.55 * si.kg / si.m**3 +ENV = Box(dt=0, dv=DV) +ATTRIBUTES = { + "multiplicity": np.asarray([MULTIPLICITY] * N_SD), + "volume": np.asarray([DROP_VOLUME] * N_SD), +} +CONC = N_SD * MULTIPLICITY / DV + + +class TestParticleConcentration: + @staticmethod + @pytest.mark.parametrize("stp", (True, False)) + def test_stp(backend_instance, stp): + # arrange + builder = Builder(n_sd=N_SD, backend=backend_instance, environment=ENV) + particulator = builder.build( + attributes=ATTRIBUTES, products=(TotalParticleConcentration(stp=stp),) + ) + if stp: + particulator.environment["rhod"] = RHOD + + # act + (prod,) = particulator.products["total particle concentration"].get() + + # assert + np.testing.assert_approx_equal( + actual=prod, + desired=( + CONC / RHOD * particulator.formulae.constants.rho_STP if stp else CONC + ), + significant=7, + ) + + @staticmethod + @pytest.mark.parametrize("specific", (True, False)) + def test_specific(backend_instance, specific): + # arrange + builder = Builder(n_sd=N_SD, backend=backend_instance, environment=ENV) + particulator = builder.build( + attributes=ATTRIBUTES, products=(ParticleConcentration(specific=specific),) + ) + if specific: + particulator.environment["rhod"] = RHOD + + # act + (prod,) = particulator.products["particle concentration"].get() + + # assert + np.testing.assert_approx_equal( + actual=prod, desired=CONC / RHOD if specific else CONC, significant=7 + ) diff --git a/PySDM/source/tests/unit_tests/products/test_cooling_rate.py b/PySDM/source/tests/unit_tests/products/test_cooling_rate.py new file mode 100644 index 0000000000000000000000000000000000000000..491f82b199ad308eb5aa7809a881329bc1762b3b --- /dev/null +++ b/PySDM/source/tests/unit_tests/products/test_cooling_rate.py @@ -0,0 +1,171 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import math +from collections import namedtuple + +import numpy as np + +import pytest + +from PySDM import Builder +from PySDM.backends import CPU +from PySDM.environments import Box, Kinematic1D +from PySDM.physics import si +from PySDM.products.freezing import CoolingRate +from PySDM.impl.mesh import Mesh +from PySDM.dynamics import Displacement, AmbientThermodynamics + +T = 300 * si.K +n_sd = 100 +dt = 44 +dT = -2 + + +class TestCoolingRate: + @staticmethod + def _make_particulator(): + env = Box(dt=dt, dv=np.nan) + builder = Builder(n_sd=n_sd, backend=CPU(), environment=env) + particulator = builder.build( + attributes={ + "multiplicity": np.ones(n_sd), + "volume": np.linspace(0.01, 10, n_sd) * si.um**3, + }, + products=(CoolingRate(),), + ) + particulator.environment["T"] = T + return particulator + + def test_nan_at_t_zero(self): + # arrange + particulator = self._make_particulator() + + # act + cr = particulator.products["cooling rate"].get() + + # assert + assert np.isnan(cr).all() + + def test_zero_with_no_env_change(self): + # arrange + particulator = self._make_particulator() + + # act + particulator.run(1) + particulator.attributes.mark_updated("cell id") + cr = particulator.products["cooling rate"].get() + + # assert + assert (cr == 0).all() + + def test_with_env_change(self): + # arrange + particulator = self._make_particulator() + + # act + particulator.run(1) + particulator.environment["T"] += dT + particulator.attributes.mark_updated("cell id") + cr = particulator.products["cooling rate"].get() + + # assert + np.testing.assert_allclose(actual=cr, desired=-dT / dt) + + @staticmethod + @pytest.mark.parametrize("courant_number", (0.2, 0.8)) + @pytest.mark.parametrize( + "timestep", + ( + 0.5 * si.s, + 5 * si.s, + ), + ) + def test_single_column_constant_updraft( # pylint: disable=too-many-locals + *, + courant_number, + timestep, + mean_n_sd_per_gridbox=1000, + nz=10, + z_max=1 * si.km, + signed_thd_lapse_rate=-5 * si.K / si.km, + constant_rhod=1 * si.kg / si.m**3, + n_steps=3, + ): + # arrange + builder = Builder( + environment=Kinematic1D( + dt=timestep, + mesh=Mesh(grid=(nz,), size=(z_max,)), + thd_of_z=lambda z: signed_thd_lapse_rate * z + 300 * si.K, + rhod_of_z=lambda z: 0 * z + constant_rhod, + ), + n_sd=mean_n_sd_per_gridbox * nz, + backend=CPU(), + ) + builder.add_dynamic(AmbientThermodynamics()) + + class EulerianAdvection: + solvers = namedtuple(typename="_", field_names=("advectee",))( + advectee=namedtuple(typename="__", field_names=("ravel", "shape"))( + ravel=lambda: None, shape=(nz,) + ) + ) + + def instantiate(self, *, builder): + assert builder + return self + + def __call__(self): + pass + + builder.add_dynamic(EulerianAdvection()) + builder.add_dynamic(Displacement()) + + cellular_attributes = {} + ( + cellular_attributes["cell id"], + cellular_attributes["cell origin"], + cellular_attributes["position in cell"], + ) = builder.particulator.environment.mesh.cellular_attributes( + positions=np.random.random_sample(size=builder.particulator.n_sd).reshape( + (1, -1) + ) + * nz + ) + particulator = builder.build( + attributes={ + "multiplicity": np.ones(builder.particulator.n_sd), + "water mass": np.ones(builder.particulator.n_sd) * 1 * si.ug, + **cellular_attributes, + }, + products=(CoolingRate(),), + ) + particulator.dynamics["Displacement"].upload_courant_field( + courant_field=(np.full(nz + 1, fill_value=courant_number),) + ) + + # act + particulator.run(steps=n_steps) + + cooling_rates = particulator.products["cooling rate"].get() + + # assert + assert ( + 0.5 * particulator.n_sd + < len(particulator.attributes["cell id"]) + < particulator.n_sd + ) + assert len(cooling_rates) == nz + + valid_cells = slice(math.ceil(courant_number * n_steps), None) + + dz = z_max / nz + dz_dt = courant_number * dz / timestep + mean_temperature_lapse_rate = ( + np.mean(np.diff(particulator.environment["T"].to_ndarray())) / dz + ) + + np.testing.assert_allclose( + actual=cooling_rates[valid_cells], + desired=-1 * mean_temperature_lapse_rate * dz_dt, + rtol=0.2, + ) diff --git a/PySDM/source/tests/unit_tests/products/test_effective_radii.py b/PySDM/source/tests/unit_tests/products/test_effective_radii.py new file mode 100644 index 0000000000000000000000000000000000000000..7102da5f0978529f45e0459305f5204601243f43 --- /dev/null +++ b/PySDM/source/tests/unit_tests/products/test_effective_radii.py @@ -0,0 +1,56 @@ +"""tests different formulations of effective radius product""" + +import numpy as np + +from PySDM import Builder +from PySDM.environments import Box +from PySDM.physics import si +from PySDM.products import EffectiveRadius, ActivatedEffectiveRadius + + +def test_effective_radii(backend_class): + # arrange + env = Box(dt=np.nan, dv=np.nan) + wet_radii = np.asarray([0.01 * si.um, 0.05 * si.um, 0.09 * si.um, 1 * si.um]) + dry_radii = np.asarray([0.009 * si.um] * len(wet_radii)) + kappa = 1.666 + + builder = Builder( + n_sd=len(wet_radii), + backend=backend_class(double_precision=True), + environment=env, + ) + dry_volume = builder.formulae.trivia.volume(radius=dry_radii) + particulator = builder.build( + attributes={ + "water mass": builder.formulae.particle_shape_and_density.radius_to_mass( + wet_radii + ), + "dry volume": dry_volume, + "kappa times dry volume": kappa * dry_volume, + "multiplicity": np.asarray([1] * len(wet_radii)), + }, + products=( + ActivatedEffectiveRadius( + name="a", count_unactivated=False, count_activated=False + ), + ActivatedEffectiveRadius( + name="b", count_unactivated=True, count_activated=False + ), + ActivatedEffectiveRadius( + name="c", count_activated=True, count_unactivated=True + ), + EffectiveRadius(name="d", radius_range=(0.5 * si.um, np.inf)), + ), + ) + particulator.environment["T"] = 300 * si.K + + # act + sut = {k: product.get()[0] for k, product in particulator.products.items()} + + # assert + assert np.isnan(sut["a"]) + assert min(wet_radii) < sut["b"] + assert sut["b"] < sut["c"] + assert sut["c"] < sut["d"] + assert sut["d"] < max(wet_radii) diff --git a/PySDM/source/tests/unit_tests/products/test_impl.py b/PySDM/source/tests/unit_tests/products/test_impl.py new file mode 100644 index 0000000000000000000000000000000000000000..c6304596f050c16a251a132efba6466a5207a026 --- /dev/null +++ b/PySDM/source/tests/unit_tests/products/test_impl.py @@ -0,0 +1,185 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import inspect +import sys +from collections import namedtuple + +import numpy as np +import pytest + +from PySDM import Builder, products +from PySDM.backends import CPU +from PySDM.environments import Box +from PySDM.products import ( + ActivatedEffectiveRadius, + ActivatedMeanRadius, + ActivatedParticleConcentration, + ActivatedParticleSpecificConcentration, + AqueousMassSpectrum, + AqueousMoleFraction, + AreaStandardDeviation, + DynamicWallTime, + FlowVelocityComponent, + FreezableSpecificConcentration, + FrozenParticleConcentration, + FrozenParticleSpecificConcentration, + GaseousMoleFraction, + MeanVolumeRadius, + NumberSizeSpectrum, + ParcelLiquidWaterPath, + ParticleSizeSpectrumPerMassOfDryAir, + ParticleSizeSpectrumPerVolume, + ParticleVolumeVersusRadiusLogarithmSpectrum, + RadiusBinnedNumberAveragedTerminalVelocity, + RadiusStandardDeviation, + TotalDryMassMixingRatio, + VolumeStandardDeviation, +) +from PySDM.products.impl import Product, RateProduct, register_product + +_ARGUMENTS = { + AqueousMassSpectrum: {"key": "S_VI", "dry_radius_bins_edges": (0, np.inf)}, + AqueousMoleFraction: {"key": "S_VI"}, + TotalDryMassMixingRatio: {"density": 1}, + ParticleSizeSpectrumPerMassOfDryAir: {"radius_bins_edges": (0, np.inf)}, + GaseousMoleFraction: {"key": "O3"}, + FreezableSpecificConcentration: {"temperature_bins_edges": (0, 300)}, + DynamicWallTime: {"dynamic": "Condensation"}, + ParticleSizeSpectrumPerVolume: {"radius_bins_edges": (0, np.inf)}, + ParticleVolumeVersusRadiusLogarithmSpectrum: {"radius_bins_edges": (0, np.inf)}, + RadiusBinnedNumberAveragedTerminalVelocity: {"radius_bin_edges": (0, np.inf)}, + FlowVelocityComponent: {"component": 0}, + FrozenParticleConcentration: {"count_unactivated": True, "count_activated": True}, + FrozenParticleSpecificConcentration: { + "count_unactivated": True, + "count_activated": True, + }, + NumberSizeSpectrum: {"radius_bins_edges": (0, np.inf)}, + ActivatedMeanRadius: {"count_activated": True, "count_unactivated": False}, + ActivatedParticleConcentration: { + "count_activated": True, + "count_unactivated": False, + }, + ActivatedParticleSpecificConcentration: { + "count_activated": True, + "count_unactivated": False, + }, + MeanVolumeRadius: {"count_activated": True, "count_unactivated": False}, + RadiusStandardDeviation: {"count_activated": True, "count_unactivated": False}, + AreaStandardDeviation: {"count_activated": True, "count_unactivated": False}, + VolumeStandardDeviation: {"count_activated": True, "count_unactivated": False}, + ActivatedEffectiveRadius: {"count_activated": True, "count_unactivated": False}, + ParcelLiquidWaterPath: {"count_activated": True, "count_unactivated": False}, +} + + +@pytest.fixture( + params=( + pytest.param(p[1], id=p[0]) + for p in inspect.getmembers(sys.modules[products.__name__], inspect.isclass) + ), + name="product", +) +def product_fixture(request): + return request.param + + +class TestProducts: + @staticmethod + def test_instantiate_all(product): + product(**(_ARGUMENTS[product] if product in _ARGUMENTS else {})) + + @staticmethod + def test_unit_conversion(): + # arrange + class SUT(Product): + def __init__(self, unit="m"): + super().__init__(unit=unit) + + def _impl(self, **kwargs): + return 1 + + sut = SUT(unit="mm") + + # act + value = sut.get() + + # assert + assert value == 1e3 + + @staticmethod + def test_rate_product(): + # arrange + n_steps = 10 + dt = 44 + dv = 123 + rhod = 1.11 + count = 666 + size = 1 + + class SUT(RateProduct): + def __init__(self, unit="s^-1"): + super().__init__(unit=unit, name=None, counter="", dynamic=None) + + backend = CPU() + sut = SUT() + sut.buffer = np.empty(size) + sut.particulator = namedtuple("_", ("dt", "mesh", "environment"))( + dt=dt, + mesh=namedtuple("Mesh", ("dv",))(dv=dv), + environment={"rhod": backend.Storage.from_ndarray(np.asarray([rhod]))}, + ) + + def set_and_notify(): + sut.counter = backend.Storage.from_ndarray(np.full(size, count)) + for _ in range(n_steps): + sut.notify() + + # act + set_and_notify() + value1 = sut.get() + set_and_notify() + value2 = sut.get() + + # assert + np.testing.assert_allclose(value1, count / n_steps / dt / dv / rhod) + np.testing.assert_allclose(value2, count / n_steps / dt / dv / rhod) + + @staticmethod + def test_register_can_be_called_twice_on_r_eff(): + # arrange + sut = products.EffectiveRadius() + env = Box(dt=0, dv=0) + builder = Builder(backend=CPU(), n_sd=0, environment=env) + sut.register(builder) + + # act + sut.register(builder) + + @staticmethod + def test_register_product_make_product_instances_reusable(): + # arrange + name = "prod" + + @register_product() + class Prod(Product): + def __init__(self, unit="m"): + super().__init__(unit=unit, name=name) + + def _impl(self, **_): + pass + + product = Prod() + kwargs = {"backend": CPU(), "n_sd": 0, "environment": Box(dt=0, dv=0)} + builders = ( + Builder(**kwargs), + Builder(**kwargs), + ) + + # act + for builder in builders: + builder._register_product(product=product, buffer=None) + + # assert + assert product.particulator is None + for builder in builders: + assert builder.particulator.products[name] is not product diff --git a/PySDM/source/tests/unit_tests/products/test_mixed_phase_moments.py b/PySDM/source/tests/unit_tests/products/test_mixed_phase_moments.py new file mode 100644 index 0000000000000000000000000000000000000000..7867cffd49401b9fb3c3f12ed181d09d5d7107c5 --- /dev/null +++ b/PySDM/source/tests/unit_tests/products/test_mixed_phase_moments.py @@ -0,0 +1,79 @@ +# pylint: disable=missing-module-docstring,missing-function-docstring +import numpy as np +import pytest + +from PySDM.physics import si +import PySDM.products as PySDM_products +from PySDM.builder import Builder +from PySDM import Formulae +from PySDM.environments import Box + + +MASSES = (10.0 * si.ug, -10.0 * si.ug) + + +@pytest.mark.parametrize( + "particle_mass", [np.asarray((v1, v2)) for v1 in MASSES for v2 in MASSES] +) +def test_mixed_phase_moments(particle_mass, backend_class): + # arrange + particulator = Builder( + n_sd=len(particle_mass), + environment=Box(dt=np.nan, dv=1 * si.m**3), + backend=backend_class( + formulae=Formulae( + particle_shape_and_density="MixedPhaseSpheres", + ) + ), + ).build( + attributes={ + "multiplicity": np.full_like(particle_mass, fill_value=1), + "signed water mass": particle_mass, + }, + products=( + PySDM_products.WaterMixingRatio(name="water", radius_range=(0, np.inf)), + PySDM_products.WaterMixingRatio(name="ice", radius_range=(-np.inf, 0)), + PySDM_products.WaterMixingRatio( + name="total", radius_range=(-np.inf, np.inf) + ), + PySDM_products.ParticleConcentration( + name="n_water", radius_range=(0, np.inf) + ), + PySDM_products.ParticleConcentration( + name="n_ice", radius_range=(-np.inf, 0) + ), + PySDM_products.ParticleConcentration( + name="n_total", radius_range=(-np.inf, np.inf) + ), + PySDM_products.MeanRadius(name="r_water", radius_range=(0, np.inf)), + PySDM_products.MeanRadius(name="r_ice", radius_range=(-np.inf, 0)), + PySDM_products.MeanRadius(name="r_all", radius_range=(-np.inf, np.inf)), + ), + ) + particulator.environment["rhod"] = 1 * si.kg / si.m**3 + + # act + lwc = particulator.products["water"].get()[0] + iwc = particulator.products["ice"].get()[0] + twc = particulator.products["total"].get()[0] + + n_w = particulator.products["n_water"].get()[0] + n_i = particulator.products["n_ice"].get()[0] + n_t = particulator.products["n_total"].get()[0] + + r_w = particulator.products["r_water"].get()[0] + r_i = particulator.products["r_ice"].get()[0] + r_t = particulator.products["r_all"].get()[0] + + # assert + assert np.isfinite([lwc, iwc, twc]).all() + assert np.isfinite([n_w, n_i, n_t]).all() + assert np.isfinite([r_w, r_i, r_t]).all() + + if any(particle_mass > 0): + assert all(product > 0 for product in (lwc, n_w, r_w)) + if any(particle_mass < 0): + assert all(product > 0 for product in (iwc, n_i, r_i)) + assert twc > 0 + assert lwc + iwc == twc + assert n_w + n_i == n_t diff --git a/PySDM/source/tests/unit_tests/products/test_parcel_liquid_water_path.py b/PySDM/source/tests/unit_tests/products/test_parcel_liquid_water_path.py new file mode 100644 index 0000000000000000000000000000000000000000..fa864969b9d36848115771992d8e6bd3c015d9b9 --- /dev/null +++ b/PySDM/source/tests/unit_tests/products/test_parcel_liquid_water_path.py @@ -0,0 +1,94 @@ +"""tests the liquid-water-path computing product for Parcel env""" + +import numpy as np +from matplotlib import pyplot + +from PySDM.products import ( + ParcelLiquidWaterPath, + LiquidWaterContent, + AmbientRelativeHumidity, +) +from PySDM.environments import Parcel +from PySDM.dynamics import Condensation, AmbientThermodynamics +from PySDM import Builder +from PySDM.physics import si + + +def test_parcel_liquid_water_path( + backend_class, plot=False +): # pylint: disable=too-many-locals + # arrange + n_sd = 1 + n_steps = 32 + dz = 5 * si.m + dt = 1 * si.s + + builder = Builder( + n_sd=n_sd, + backend=backend_class(double_precision=True), + environment=Parcel( + dt=dt, + mass_of_dry_air=1 * si.mg, + p0=1000 * si.hPa, + initial_water_vapour_mixing_ratio=22.2 * si.g / si.kg, + T0=300 * si.K, + w=dz / dt, + ), + ) + builder.add_dynamic(AmbientThermodynamics()) + builder.add_dynamic(Condensation()) + particulator = builder.build( + attributes=builder.particulator.environment.init_attributes( + n_in_dv=np.asarray([1000]), kappa=0.666, r_dry=np.asarray([0.01 * si.um]) + ), + products=( + ParcelLiquidWaterPath( + name="LWP", count_unactivated=True, count_activated=True + ), + LiquidWaterContent(name="LWC"), + AmbientRelativeHumidity(name="RH"), + ), + ) + + # act + data = {product: [] for product in particulator.products} + for _ in range(n_steps): + particulator.run(steps=1) + for key, product in particulator.products.items(): + value = product.get() + if isinstance(value, np.ndarray): + value = value[0] + data[key].append(value) + for k, datum in data.items(): + data[k] = np.asarray(datum) + cumsum = np.cumsum((data["LWC"] - np.diff(data["LWC"], prepend=0) / 2) * dz) + t = np.arange(1, len(cumsum) + 1) * dt + + # plot + pyplot.title(backend_class.__name__) + pyplot.plot( + t, + cumsum, + label="cumsum((LWC - diff(LWC)/2) * dz)", + color="black", + linestyle=":", + ) + pyplot.plot(t, data["LWP"], label="LWP", color="black") + pyplot.ylabel("LWP [kg/m^2]") + pyplot.xlabel("time [s] (values at the end of each timestep)") + pyplot.ylim(0, 0.016) + pyplot.legend() + pyplot.grid() + + twin = pyplot.gca().twinx() + twin.plot(t, data["RH"], color="red", marker="o") + twin.set_ylabel("RH [1]", color="red") + twin.set_ylim(0.98, 1.02) + + if plot: + pyplot.show() + else: + pyplot.clf() + + # assert + np.testing.assert_allclose(cumsum, data["LWP"], atol=2e-10) diff --git a/PySDM/source/tests/unit_tests/products/test_particle_size_product.py b/PySDM/source/tests/unit_tests/products/test_particle_size_product.py new file mode 100644 index 0000000000000000000000000000000000000000..2cadfdbfcf172e909137cc5749f86d5ef7784af0 --- /dev/null +++ b/PySDM/source/tests/unit_tests/products/test_particle_size_product.py @@ -0,0 +1,161 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring + +import numpy as np +import pytest + +from PySDM import Builder, Formulae +from PySDM.environments import Box +from PySDM.physics import si +from PySDM.products import ( + ActivatedMeanRadius, + AreaStandardDeviation, + MeanVolumeRadius, + RadiusStandardDeviation, +) + +TRIVIA = Formulae().trivia +NAME = "tested product" +KAPPA = 1 +CELL_ID = 0 + + +def radius_std(r, n, mask): + n_tot = np.sum(np.where(mask, n, 0), axis=0) + r_act = np.where(mask, r, np.nan) + n_act = np.where(mask, n, np.nan) + std = ( + np.sqrt(np.cov(r_act[~np.isnan(r_act)], fweights=n_act[~np.isnan(n_act)])) + if n_tot > 1 + else 0 + ) + return std + + +def filtered_mean(attribute, multiplicity, mask): + n_dot_a = np.multiply(np.where(mask, multiplicity, 0), attribute) + n_tot = np.sum(np.where(mask, multiplicity, 0), axis=0) + mean = np.sum(n_dot_a, axis=0) / n_tot if n_tot > 0 else 0 + return mean + + +def r_vol_mean(r, n, mask): + volume = TRIVIA.volume(radius=r) + mean_volume = filtered_mean(volume, n, mask) + return TRIVIA.radius(volume=mean_volume) + + +def area_std(r, n, mask): + n_tot = np.sum(np.where(mask, n, 0), axis=0) + n_act = np.where(mask, n, np.nan) + r_sq = np.where(mask, np.multiply(r, r), np.nan) * 4 * np.pi + std = ( + np.sqrt(np.cov(r_sq[~np.isnan(r_sq)], fweights=n_act[~np.isnan(n_act)])) + if n_tot > 1 + else 0 + ) + return std + + +@pytest.mark.parametrize( + "r,n", + ( + ([1 * si.um], [1]), + ([1 * si.um, 10 * si.um], [1e3, 1e7]), + ([0.01 * si.um, 0.1 * si.um], [1e3, 1e7]), + ), +) +@pytest.mark.parametrize("count_unactivated", (True, False)) +@pytest.mark.parametrize("count_activated", (True, False)) +@pytest.mark.parametrize( + "product_class, validation_fun", + ( + (RadiusStandardDeviation, radius_std), + (ActivatedMeanRadius, filtered_mean), + (AreaStandardDeviation, area_std), + (MeanVolumeRadius, r_vol_mean), + ), +) +# pylint: disable=too-many-arguments +def test_particle_size_product( + backend_class, + r, + n, + count_unactivated, + count_activated, + product_class, + validation_fun, +): + # arrange + builder = Builder( + n_sd=len(n), + backend=backend_class(double_precision=True), + environment=Box(dt=np.nan, dv=np.nan), + ) + volume = builder.formulae.trivia.volume(np.asarray(r)) + dry_volume = np.full_like(volume, (0.01 * si.um) ** 3) + + builder.request_attribute("critical volume") + particulator = builder.build( + attributes={ + "multiplicity": np.asarray(n), + "volume": volume, + "dry volume": dry_volume, + "kappa times dry volume": KAPPA * dry_volume, + }, + products=( + product_class( + name=NAME, + count_activated=count_activated, + count_unactivated=count_unactivated, + ), + ), + ) + + particulator.environment["T"] = 300 * si.K + + crit_volume = particulator.attributes["critical volume"].to_ndarray() + if count_activated and not count_unactivated: + mask = volume > crit_volume + elif not count_activated and count_unactivated: + mask = volume < crit_volume + elif count_activated and count_unactivated: + mask = np.full_like(volume, True) + else: + mask = np.full_like(volume, False) + + expected = validation_fun(np.asarray(r), np.asarray(n), mask) + + # act + actual = particulator.products[NAME].get()[CELL_ID] + + # assert + np.testing.assert_almost_equal(actual, expected, decimal=10) + + +def test_size_standard_deviation_allocates_once(backend_instance): + """checks if calling get() does not allocate new memory""" + # arrange + builder = Builder( + backend=backend_instance, environment=Box(dt=np.nan, dv=np.nan), n_sd=10 + ) + particulator = builder.build( + products=( + AreaStandardDeviation( + name="sut", count_activated=True, count_unactivated=True + ), + ), + attributes={ + "multiplicity": np.arange(1, builder.particulator.n_sd + 1), + "water mass": np.arange(1, builder.particulator.n_sd + 1), + "dry volume": np.arange(1, builder.particulator.n_sd + 1), + "kappa times dry volume": np.arange(1, builder.particulator.n_sd + 1), + }, + ) + particulator.environment["T"] = 300 * si.K + + # act + arr1 = particulator.products["sut"].get() + arr2 = particulator.products["sut"].get() + + # assert + assert arr1.ctypes.data == arr2.ctypes.data diff --git a/PySDM/source/tests/unit_tests/products/test_particle_size_spectrum.py b/PySDM/source/tests/unit_tests/products/test_particle_size_spectrum.py new file mode 100644 index 0000000000000000000000000000000000000000..c90b13b45821b5b4d48faf03456b36a07e285911 --- /dev/null +++ b/PySDM/source/tests/unit_tests/products/test_particle_size_spectrum.py @@ -0,0 +1,134 @@ +""" +tests the ParticleSizeSpectrum product against per-mass/per-volume and dry-/wet-size choices +""" + +import pytest +import numpy as np +from PySDM import Builder +from PySDM.physics import si +from PySDM.environments import Box +from PySDM.products import ( + ParticleSizeSpectrumPerMassOfDryAir, + ParticleSizeSpectrumPerVolume, +) +from PySDM.products.size_spectral.particle_size_spectrum import ParticleSizeSpectrum + + +class TestParticleSizeSpectrum: + @staticmethod + @pytest.mark.parametrize( + "product_class, specific, unit, stp", + ( + (ParticleSizeSpectrumPerVolume, False, "1.0 / meter ** 4", True), + (ParticleSizeSpectrumPerVolume, False, "1.0 / meter ** 4", False), + ( + ParticleSizeSpectrumPerMassOfDryAir, + True, + "1.0 / kilogram / meter", + False, + ), + ), + ) + def test_specific_flag(product_class, specific, unit, backend_instance, stp): + """checks if the reported concentration is correctly normalised per + volume or mass of air, and per bin size""" + # arrange + name = "xxx" + multiplicity = 1000 + rhod = 44 * si.kg / si.m**3 + min_size = 0 + max_size = 1 * si.mm + + n_sd = 1 + builder = Builder( + n_sd=n_sd, + backend=backend_instance, + environment=Box(dt=np.nan, dv=666 * si.m**3), + ) + particulator = builder.build( + products=( + product_class( + name=name, + radius_bins_edges=(min_size, max_size), + **({"stp": stp} if stp else {}), + ), + ), + attributes={ + "multiplicity": np.ones(n_sd) * multiplicity, + "water mass": np.ones(n_sd) * si.ug, + }, + ) + particulator.environment["rhod"] = rhod + sut = particulator.products[name] + + # act + actual = sut.get() + + # assert + assert sut.unit == unit + assert sut.specific == specific + np.testing.assert_approx_equal( + actual=actual, + desired=multiplicity + / particulator.environment.mesh.dv + / (max_size - min_size) + / (rhod if specific or stp else 1) + * (backend_instance.formulae.constants.rho_STP if stp else 1), + significant=10, + ) + + @staticmethod + @pytest.mark.parametrize( + "product_class", + (ParticleSizeSpectrumPerVolume, ParticleSizeSpectrumPerMassOfDryAir), + ) + @pytest.mark.parametrize("dry", (True, False)) + def test_dry_flag(product_class, dry, backend_instance): + """checks if dry or wet size attribute is correctly picked for moment calculation""" + # arrange + name = "xxx" + n_sd = 1 + min_size = 0 + max_size = 1 * si.mm + multiplicity = 100 + rhod = 44 * si.kg / si.m**3 + + builder = Builder( + n_sd=n_sd, + backend=backend_instance, + environment=Box(dt=np.nan, dv=666 * si.m**3), + ) + particulator = builder.build( + products=( + product_class( + name=name, radius_bins_edges=(min_size, max_size), dry=dry + ), + ), + attributes={ + "multiplicity": np.ones(n_sd) * multiplicity, + "volume": np.ones(n_sd) * (np.nan if dry else 1 * si.um**3), + "dry volume": np.ones(n_sd) * (0.01 * si.um**3 if dry else np.nan), + }, + ) + particulator.environment["rhod"] = rhod + sut = particulator.products[name] + + # act + actual = sut.get() + + # assert + np.testing.assert_approx_equal( + actual=actual, + desired=multiplicity + / particulator.environment.mesh.dv + / (max_size - min_size) + / (rhod if sut.specific else 1), + significant=10, + ) + + @staticmethod + def test_stp_flag_incompatible_with_specific(): + with pytest.raises(Exception, match="precludes specific"): + ParticleSizeSpectrum( + stp=True, specific=True, name="", unit="", radius_bins_edges=() + ) diff --git a/PySDM/source/tests/unit_tests/products/test_surface_precipitation.py b/PySDM/source/tests/unit_tests/products/test_surface_precipitation.py new file mode 100644 index 0000000000000000000000000000000000000000..55a55a786e5af70dcbee19c5b91956b2b922bde2 --- /dev/null +++ b/PySDM/source/tests/unit_tests/products/test_surface_precipitation.py @@ -0,0 +1,104 @@ +"""sanity checks for the surface precipitation product""" + +import pytest +import numpy as np + +from PySDM import Builder +from PySDM.backends import ThrustRTC +from PySDM.physics import si +from PySDM.impl.mesh import Mesh +from PySDM.environments import Box, Kinematic1D +from PySDM.products import SurfacePrecipitation +from PySDM.dynamics import Displacement + + +class TestSurfacePrecipitation: + @staticmethod + def test_fails_for_0d_env(backend_instance): + """checks if builder fails with a relevant error message on an attempt to use surface + precipitation product with a zero-dimensional environment""" + # arrange + builder = Builder( + n_sd=1, backend=backend_instance, environment=Box(dt=np.nan, dv=np.nan) + ) + builder.add_dynamic(Displacement()) + + # act + with pytest.raises(AssertionError) as ex: + _ = builder.build(attributes={}, products=(SurfacePrecipitation(),)) + + # assert + assert "n_dims > 0" in str(ex.traceback[-1].statement) + + @staticmethod + @pytest.mark.parametrize( + "env_class, env_ctor_args", + ( + pytest.param( + Kinematic1D, + { + "mesh": Mesh(grid=(1,), size=(44 * si.m,)), + "thd_of_z": lambda z: z * np.nan, + "rhod_of_z": lambda z: z * np.nan, + }, + id=Kinematic1D.__name__, + ), + ), + ) + @pytest.mark.parametrize("dt", (0.1 * si.s, 10 * si.s)) + @pytest.mark.parametrize("drop_mass", (1.2345 * si.ug,)) + @pytest.mark.parametrize("multiplicity", (1, 2, 3)) + @pytest.mark.parametrize("n_sd", (1, 44, 666)) + # TODO #1418 add tests for counting_level + def test_surface_precipitation( + *, env_class, env_ctor_args, backend_instance, dt, drop_mass, multiplicity, n_sd + ): + """uses a monodisperse super-droplet setup to check if reported precip + matches drop and flow params, the droplet is initialised at position z=0, + so any downward movement triggers counting as precip + """ + + if isinstance(backend_instance, ThrustRTC): + pytest.skip("TODO #1418") + + # arrange + n_cell = 1 + builder = Builder( + n_sd=n_sd, + backend=backend_instance, + environment=env_class(**env_ctor_args, dt=dt), + ) + builder.add_dynamic(Displacement(enable_sedimentation=True)) + particulator = builder.build( + attributes={ + "multiplicity": np.asarray([multiplicity] * n_sd), + "water mass": np.asarray([drop_mass] * n_sd), + "cell id": np.zeros(n_sd, dtype=int), + "cell origin": np.zeros(n_sd, dtype=int), + "position in cell": np.zeros(n_sd), + }, + products=(SurfacePrecipitation(),), + ) + particulator.dynamics[Displacement.__name__].upload_courant_field( + courant_field=(np.zeros(n_cell + 1),) + ) + + # act + particulator.run(steps=1) + + # assert + water_volume = ( + n_sd * multiplicity * drop_mass / particulator.formulae.constants.rho_w + ) + np.testing.assert_approx_equal( + actual=particulator.products["surface precipitation"].get(), + desired=water_volume + / particulator.environment.mesh.domain_bottom_surface_area + / dt, + ) + + # act again + particulator.run(steps=1) + + # assert again + assert particulator.products["surface precipitation"].get() == 0 diff --git a/PySDM/source/tests/unit_tests/test_builder.py b/PySDM/source/tests/unit_tests/test_builder.py new file mode 100644 index 0000000000000000000000000000000000000000..51f7cca18b6cec8c94bca6b8c4aefd267072d9f0 --- /dev/null +++ b/PySDM/source/tests/unit_tests/test_builder.py @@ -0,0 +1,85 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import numpy as np +import pytest + +from PySDM import Builder +from PySDM.backends import CPU +from PySDM.dynamics import Condensation, Displacement, RelaxedVelocity +from PySDM.environments import Box + + +class TestBuilder: + @staticmethod + def test_build_minimal(): + # arrange + env = Box(dt=np.nan, dv=np.nan) + builder = Builder(backend=CPU(), n_sd=1, environment=env) + + # act + particulator = builder.build( + products=(), + attributes={k: np.asarray([0]) for k in ("multiplicity", "volume")}, + ) + + # assert + _ = particulator.attributes + + @staticmethod + def test_request_attribute(): + # arrange + env = Box(dt=-1, dv=np.nan) + builder = Builder(backend=CPU(), n_sd=1, environment=env) + builder.add_dynamic(Condensation()) + + # act + builder.request_attribute("critical saturation") + + # assert + particulator = builder.build( + products=(), + attributes={ + k: np.asarray([1]) + for k in ( + "multiplicity", + "volume", + "dry volume", + "kappa times dry volume", + ) + }, + ) + particulator.environment["T"] = np.nan + _ = particulator.attributes["critical saturation"].to_ndarray() + + @staticmethod + @pytest.mark.parametrize( + "dynamics", + ( + (RelaxedVelocity(), Displacement()), + (Displacement(), RelaxedVelocity()), + ), + ) + def test_order_of_dynamic_registration_does_not_matter_for_attribute_mappings( + dynamics, + ): + # arrange + builder = Builder(backend=CPU(), n_sd=1, environment=Box(dt=-1, dv=np.nan)) + for dynamic in dynamics: + builder.add_dynamic(dynamic) + + _ = builder.build( + products=(), + attributes={ + k: np.asarray([0]) + for k in ("multiplicity", "volume", "relative fall momentum") + }, + ) + + # act + assert ( + builder.get_attribute( + attribute_name="relative fall velocity" + ).__class__.__name__ + == "RelativeFallVelocity" + ) + + # assert diff --git a/PySDM/source/tests/unit_tests/test_formulae.py b/PySDM/source/tests/unit_tests/test_formulae.py new file mode 100644 index 0000000000000000000000000000000000000000..375b507c6cf6be6a9ed26ac25a31dccb626c5fd6 --- /dev/null +++ b/PySDM/source/tests/unit_tests/test_formulae.py @@ -0,0 +1,185 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +from collections import namedtuple + +import numpy as np +import pytest + +from PySDM import formulae, Formulae +from PySDM.physics import si + +DUMMY_CONSTANTS = namedtuple(typename="constants", field_names=("PI", "ZERO"))( + PI=3.14, ZERO=0 +) + + +class TestFormulae: + @staticmethod + def test_c_inline_hello_world(): + # arrange + def fun(_, xxx): + return min( + xxx, + 2, + ) + + # act + c_code = formulae._c_inline( + fun, constants=DUMMY_CONSTANTS, xxx=0 + ) # pylint: disable=protected-access + + # assert + assert ", )" not in c_code + + @staticmethod + def test_c_inline_single_line_docstring(): + # arrange + def zero(const): + """docstring""" + return const.ZERO + + # act + c_code = formulae._c_inline( + zero, constants=DUMMY_CONSTANTS + ) # pylint: disable=protected-access + + # assert + assert c_code == "(real_type)((real_type)(0))" + + @staticmethod + def test_c_inline_multi_line_docstring(): + # arrange + def zero(const): + """ + line 1 + line 2 + """ + return const.ZERO + + # act + c_code = formulae._c_inline( + zero, constants=DUMMY_CONSTANTS + ) # pylint: disable=protected-access + + # assert + assert c_code == "(real_type)((real_type)(0))" + + @staticmethod + @pytest.mark.parametrize( + "formulae_init_args", + ( + {"surface_tension": "Constant"}, + { + "surface_tension": "CompressedFilmOvadnevaite", + "constants": {"sgm_org": 40 * si.mN / si.m, "delta_min": 0.1 * si.nm}, + }, + { + "surface_tension": "CompressedFilmRuehl", + "constants": { + "RUEHL_nu_org": 1e2 * si.cm**3 / si.mole, + "RUEHL_A0": 115e-20 * si.m * si.m, + "RUEHL_C0": 6e-7, + "RUEHL_m_sigma": 0.3e17 * si.J / si.m**2, + "RUEHL_sgm_min": 40.0 * si.mN / si.m, + }, + }, + ), + ) + @pytest.mark.parametrize("temp", (300.0, np.array([300, 301]))) + @pytest.mark.parametrize("v_wet", (1e-8**3.0, np.array([1e-8**3, 2e-8**3]))) + @pytest.mark.parametrize("v_dry", (1e-9**3.0, np.array([1e-9**3, 2e-9**3]))) + @pytest.mark.parametrize("f_org", (0.5, np.array([1.0, 0.5]))) + def test_trickier_formula_vectorised(formulae_init_args, temp, v_wet, v_dry, f_org): + # arrange + sut = formulae.Formulae(**formulae_init_args).surface_tension.sigma + + # act + actual = sut(temp, v_wet, v_dry, f_org) + + # assert + expected = np.empty((1 if isinstance(actual, float) else actual.size)) + for i, _ in enumerate(expected): + expected[i] = sut( + temp if isinstance(temp, float) else temp[i], + v_wet if isinstance(v_wet, float) else v_wet[i], + v_dry if isinstance(v_dry, float) else v_dry[i], + f_org if isinstance(f_org, float) else f_org[i], + ) + np.testing.assert_allclose(actual, expected, rtol=1e-15) + + @staticmethod + def test_flatten(): + # arrange + f = formulae.Formulae() + + # act + sut = f.flatten + + # assert + temp = 300 * si.K + assert sut.latent_heat_vapourisation__lv( + temp + ) == f.latent_heat_vapourisation.lv(temp) + + @staticmethod + def test_get_constant(): + # arrange + rho_w = 666 * si.kg / si.m**3 + + # act + sut = formulae.Formulae(constants={"rho_w": rho_w}) + + # assert + assert sut.get_constant("rho_w") == rho_w + + @staticmethod + @pytest.mark.parametrize("arg", ("Dansgaard1964+BarkanAndLuz2007", "Dansgaard1964")) + def test_plus_separated_ctor_arg(arg): + # arrange + sut = formulae.Formulae(isotope_meteoric_water_line=arg) + + # act + class_name = sut.isotope_meteoric_water_line.__name__ + + # assert + assert class_name == arg + + @staticmethod + def test_pick_reports_correct_missing_name(): + # arrange + class Cls: # pylint:disable=too-few-public-methods + def __init__(self, _): + pass + + # act + with pytest.raises(ValueError) as excinfo: + formulae._pick(value="C", choices={"A": Cls, "B": Cls}, constants=None) + + # assert + assert str(excinfo.value) == "Unknown setting: C; choices are: A, B" + + @staticmethod + def test_raise_error_on_unknown_constant(): + # arrange + key = "p10000" + + with pytest.raises(ValueError) as excinfo: + Formulae(constants={key: np.nan}) + + assert ( + str(excinfo.value) == f"constant override provided for unknown key: {key}" + ) + + @staticmethod + def test_derived_constant_overridable(): + Formulae(constants={"Mv": np.nan}) + + @staticmethod + def test_seed_zero(): + # arrange + seed = 0 + + # act + sut = Formulae(seed=seed) + + # assert + assert sut.seed == seed diff --git a/PySDM/source/tests/unit_tests/test_imports.py b/PySDM/source/tests/unit_tests/test_imports.py new file mode 100644 index 0000000000000000000000000000000000000000..9dd92194ed75061fa78813c1003b0f405d007d03 --- /dev/null +++ b/PySDM/source/tests/unit_tests/test_imports.py @@ -0,0 +1,48 @@ +"""test ensuring that all needed __init__.py entries are in place""" + +import pytest + +import PySDM + +CLASSES = ( + "Builder", + "Formulae", + "Particulator", + "attributes.chemistry.Acidity", + "attributes.physics.DryVolume", + "backends.CPU", + "backends.GPU", + "dynamics.Condensation", + "dynamics.collisions.breakup_fragmentations.AlwaysN", + "dynamics.collisions.coalescence_efficiencies.LowList1982Ec", + "dynamics.collisions.collision_kernels.Golovin", + "dynamics.terminal_velocity.RogersYau", + "environments.Box", + "environments.Kinematic1D", + "environments.Kinematic2D", + "environments.Parcel", + "exporters.VTKExporter", + "initialisation.aerosol_composition.DryAerosolMixture", + "initialisation.init_fall_momenta", + "initialisation.sampling.spectral_sampling.AlphaSampling", + "initialisation.spectra.Lognormal", + "physics.constants_defaults", + "physics.diffusion_thermics.LoweEtAl2019", + "physics.si", + "products.size_spectral.EffectiveRadius", +) + + +class TestImports: + @staticmethod + @pytest.mark.parametrize("obj_path", CLASSES) + def test_imports(obj_path): + """one can import PySDM and then access classes from submodules, + like PySDM.environments.Box, etc""" + obj = PySDM + for attr in obj_path.split("."): + obj = getattr(obj, attr) + + @staticmethod + def test_classes_sorted(): + assert tuple(sorted(CLASSES)) == CLASSES diff --git a/PySDM/source/tests/unit_tests/test_particulator.py b/PySDM/source/tests/unit_tests/test_particulator.py new file mode 100644 index 0000000000000000000000000000000000000000..26ac1420798fd3ba7cec24a791bcb9eb2ba2d84e --- /dev/null +++ b/PySDM/source/tests/unit_tests/test_particulator.py @@ -0,0 +1,161 @@ +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +from collections import namedtuple + +import pytest + +from .dummy_particulator import DummyParticulator + + +class TestParticulator: + @staticmethod + def test_observer(backend_class): + class Observer: # pylint: disable=too-few-public-methods + def __init__(self, particulator): + self.steps = 0 + self.particulator = particulator + self.particulator.observers.append(self) + + def notify(self): + self.steps += 1 + assert self.steps == self.particulator.n_steps + + steps = 33 + particulator = DummyParticulator(backend_class, 44) + observer = Observer(particulator) + particulator.run(steps) + + assert observer.steps == steps + + @staticmethod + def test_initialiser(backend_class): + class Initialiser: # pylint: disable=too-few-public-methods + def __init__(self, particulator): + self.particulator = particulator + self.particulator.initialisers.append(self) + self.if_setup = False + + def setup(self): + self.if_setup = True + + steps = -1 + + particulator = DummyParticulator(backend_class, 44) + initialiser = Initialiser(particulator) + particulator.run(steps) + + assert initialiser.if_setup + + @staticmethod + @pytest.mark.parametrize("isotopes", (("1H",), ("1H", "2H"), ("16O", "17I", "18O"))) + def test_isotopic_fractionation_marks_moles_as_updated( + backend_class, isotopes: tuple + ): + # arrange + class AttributesMock: + def __init__(self): + self.updated = [] + + def __getitem__(self, item): + return + + def mark_updated(self, attr): + self.updated += [attr] + + class DP(DummyParticulator): + pass + + particulator = DP(backend_class, 44) + particulator.attributes = AttributesMock() + + # act + particulator.isotopic_fractionation(heavy_isotopes=isotopes) + + # assert + assert particulator.attributes.updated == [ + f"moles_{isotope}" for isotope in isotopes + ] + + @staticmethod + def test_seeding_marks_modified_attributes_as_updated(backend_class): + # arrange + storage = backend_class().Storage.empty(1, dtype=int) + abc = ["a", "b", "c"] + + class ParticleAttributes: + def __init__(self): + self.updated = [] + self.super_droplet_count = -1 + self.__idx = storage # pylint: disable=unused-private-member + self.idx_reset = False + self.sane = False + + def get_extensive_attribute_storage(self): + return storage + + def get_extensive_attribute_keys(self): + return abc + + def __getitem__(self, item): + return storage + + def mark_updated(self, attr): + self.updated += [attr] + + def reset_idx(self): + self.idx_reset = True + + def sanitize(self): + self.sane = True + + class DP(DummyParticulator): + pass + + particulator = DP(backend_class, 44) + particulator.attributes = ParticleAttributes() + # fmt: off + particulator.backend.seeding = ( + lambda + idx, + multiplicity, + extensive_attributes, + seeded_particle_index, + seeded_particle_multiplicity, + seeded_particle_extensive_attributes, + number_of_super_particles_to_inject: + None + ) + # fmt: on + + # act + particulator.seeding( + seeded_particle_index=storage, + seeded_particle_multiplicity=storage, + seeded_particle_extensive_attributes=storage, + number_of_super_particles_to_inject=0, + ) + + # assert + assert particulator.attributes.updated == ["multiplicity"] + abc + assert particulator.attributes.idx_reset + assert particulator.attributes.sane + + @staticmethod + def test_seeding_fails_if_no_null_super_droplets_available(backend_class): + # arrange + a_number = 44 + + particulator = DummyParticulator(backend_class, n_sd=a_number) + storage = backend_class().Storage.empty(1, dtype=int) + + particulator.attributes = namedtuple( + typename="_", field_names=("super_droplet_count",) + )(super_droplet_count=a_number) + + # act + with pytest.raises(ValueError, match="No available seeds to inject"): + particulator.seeding( + seeded_particle_index=storage, + seeded_particle_multiplicity=storage, + seeded_particle_extensive_attributes=storage, + number_of_super_particles_to_inject=0, + ) diff --git a/PySDM/source/tutorials/README.md b/PySDM/source/tutorials/README.md new file mode 100644 index 0000000000000000000000000000000000000000..60c041c769fdc8800e0e9346c9d6bd02292dfbba --- /dev/null +++ b/PySDM/source/tutorials/README.md @@ -0,0 +1,29 @@ +# PySDM tutorials + +PySDM tutorial notebooks are designed to introduce users to fundamental cloud microphysical processes. +Each tutorial includes introductory information about the relevant chemistry and physics, an interactive + code example where the user can explore how simulation results depend on various parameter values. +Some of the notebooks feature sets of questions to assess comprehension. +Part of these notebooks were originally designed for an introductory cloud physics course for advanced + undergraduate and first-year graduate students, other was developed to support the Wikipedia entry on SDM. + +### Collisions +A 0-D box model tutorial of the collision-coalescence process. Based on the [Shima et al. 2009]() example. +- [![View notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/tree/main/tutorials/collisions/collisions_playground.ipynb) + [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/tutorials/collisions/collisions_playground.ipynb) + [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/tutorials/collisions/collisions_playground.ipynb) + +### Condensation +A 0-D adiabatic parcel tutorial of the condensation and aerosol activation process. Based on the [Pyrcel]() example. +- [![View notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/tree/main/tutorials/condensation/condensation_playground.ipynb) + [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/tutorials/condensation/condensation_playground.ipynb) + [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/tutorials/condensation/condensation_playground.ipynb) + +### SDM algorithm +A pseuodocode-like implementation of the SDM algorithm (independent of PySDM) with a basic +simulation setup featuring additive coagulation kernel and exponential initial condition for which +the [Golovin's analytical solution](http://mi.mathnet.ru/dan27630) +is available and plotted along with SDM results. +- [![View notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/tree/main/tutorials/wikipedia/sdm.ipynb) + [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/tutorials/wikipedia/sdm.ipynb) + [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/tutorials/wikipedia/sdm.ipynb) diff --git a/PySDM/source/tutorials/collisions/collection_droplet.svg b/PySDM/source/tutorials/collisions/collection_droplet.svg new file mode 100644 index 0000000000000000000000000000000000000000..d8bac55fc2b66e9036fe011e3ae903be537de369 --- /dev/null +++ b/PySDM/source/tutorials/collisions/collection_droplet.svg @@ -0,0 +1,169 @@ + + + + + + + +400 +Growthbycollection + + + + + + +Limitingimpact +parameter +Effective +crosssection + + + + + + + + + + + + + + + + + + +Grazingair +trajectory +Collector +drop + + + + +r +L +r +S + +Geometrical +crosssection + + + + +v +S +v +L +Figure9.11Schematicofthegeometryassociatedwiththecollisionofasmalldropwithalarge +drop.Airflowisshownrelativetothelargedrop,whichactuallyfallswithspeed +v +L +. +thecollector).ThelowershadeddiskshowninFig. +9.11 +,insidethegrazingtrajectories, +identifiestheeffectivearea +A +eff +conducivetocollision;dropsthatpassthroughthedisk +collidewiththecollector,thosethatpassoutsideitdonot.Thus,intermsofthelimiting +value +y +c +oftheimpactparameter,thecollisionefficiency +E += +A +eff + +A +geom += +π +y +2 +c + +π +( +r +L ++ +r +S +) +2 +. +(9.39) +Thecollisionefficiency,onceitisknownforanygivendroppair( +r +L +, +r +S +),isused +tocalculatetheeffectivesweptvolume(forcollisionpurposes)as +· +v +eff += +E +· +v +geom += +π +( +r +L ++ +r +S +) +2 +E +( +v +L + +v +S +) +. +Theproblemwithevaluatingthecollisionefficiency +E +isbeingabletodeterminethe +grazingtrajectoryandhencethelimitingimpactparameter +y +c +.Variousempiricaland +theoreticalapproachesforarrivingatthefunction +E +( +r +L +, +r +S +) +havebeenattemptedinthe +pastwithreasonablesuccess.Agraphicaldisplayofrepresentativeresultsispresentedin +Fig. +9.12 +asafamilyofconstant- +r +L +curves.Severalfeaturesofthesecurvesareworth +noting.Perhapsthemostnotablefeatureofanycollisionefficiencyfunctionisthelow +valuesof +E +forsmall-dropradiibelowabout5 +µ +m,regardlessofthesizeofthecollector +drop.Thecollisionefficiencyneverequalszero,butforpracticalpurposesasmall-drop + + diff --git a/PySDM/source/tutorials/collisions/collisions_playground.ipynb b/PySDM/source/tutorials/collisions/collisions_playground.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..c8f48f88cecd88c3ba15c3809e975c7ff816b6c9 --- /dev/null +++ b/PySDM/source/tutorials/collisions/collisions_playground.ipynb @@ -0,0 +1,288 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/tutorials/collisions/collisions_playground.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/tutorials/collisions/collisions_playground.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/tutorials/collisions/collisions_playground.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Cloud Microphysics: Part 2\n", + "- Collisions and coalescence of cloud droplets\n", + "\n", + "Based on Fig. 2 from Shima et al. 2009 (Q. J. R. Meteorol. Soc. 135) \"_The super‐droplet method for the numerical simulation of clouds and precipitation: a particle‐based and probabilistic microphysics model coupled with a non‐hydrostatic model_.\" \n", + "https://doi.org/10.1002/qj.441" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-15T15:58:05.775543Z", + "start_time": "2025-05-15T15:58:05.772087Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Collision/coalescence \n", + "\n", + "The process of droplets colliding and coalescence, together refered to as collection, is the mechanism by which cloud droplets grow and eventually grow large enough to precipitate.\n", + "The collection process depends on these two processes, first two droplets colliding, and second that collision resulting in the coalescence of a new larger droplet.\n", + "\n", + "In models we parameterize this collection process stochastically by solving what is known as the SCE: Stochastic Collection Equation.\n", + "And we write the probability that two droplets collide (collision rate) in terms of a \"kernel\": $K(x,y)$, where $x$ and $y$ are the sizes of the two droplets.\n", + "\n", + "In this example, we consider the most basic kernel called the Golovin kernel, which is a linear kernel of the form $K(x,y) = b(x+y)$.\n", + "\n", + "Below is a drawing from Lamb and Verlinde's \"_The Physics and Chemistry of Clouds_\" illustrating the geometry of droplet collisions.\n", + "\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## PySDM box model widget\n", + "\n", + "In this homework assignment, and with this `PySDM` example notebook, you have the chance to explore how particle size, collision kernel, and spectral resolution (number of superdroplets used to represent the droplet size distribution) influence the growth of a population of cloud droplets as they undergo collision and coalescence. \n", + "\n", + "In this box setup, we can focus on only the collision/coalescence process while ignoring the hygroscopic growth of particles and activation of aerosols considered in Part 1, as well as fluid flow and mixing from a 2D or 3D simulation." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-15T15:58:05.785980Z", + "start_time": "2025-05-15T15:58:05.784129Z" + } + }, + "outputs": [], + "source": [ + "from numpy import errstate\n", + "\n", + "from PySDM import Formulae\n", + "from PySDM.dynamics.collisions.collision_kernels import Golovin\n", + "from PySDM.initialisation import spectra\n", + "from PySDM.physics import si\n", + "\n", + "from PySDM_examples.utils import widgets\n", + "\n", + "from PySDM_examples.Shima_et_al_2009.tutorial_plotter import SpectrumPlotter\n", + "from PySDM_examples.Shima_et_al_2009.tutorial_settings import Settings\n", + "from PySDM_examples.Shima_et_al_2009.tutorial_example import run" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-15T15:58:05.797240Z", + "start_time": "2025-05-15T15:58:05.792780Z" + } + }, + "outputs": [], + "source": [ + "def demo(*, _freezer, _n, _b, _r, _smooth):\n", + " frm = Formulae()\n", + " with _freezer:\n", + " with errstate(all='raise'):\n", + " n_step = 3600\n", + " n_plot = 3\n", + " settings = Settings(steps=[i * (n_step // n_plot) for i in range(n_plot + 1)])\n", + " settings.n_sd = 2 ** _n\n", + " settings.adaptive = True\n", + " settings.dt = 10\n", + " settings.kernel = Golovin(b=_b / si.second)\n", + " settings.X0 = frm.trivia.volume(radius=_r * si.micrometres)\n", + " settings.spectrum = spectra.Exponential(\n", + " norm_factor=settings.norm_factor, scale=settings.X0\n", + " )\n", + " states, _ = run(settings, (widgets.ProgbarUpdater(progbar, settings.output_steps[-1]),))\n", + "\n", + " with errstate(invalid='ignore'):\n", + " plotter = SpectrumPlotter(settings)\n", + " plotter.smooth = _smooth\n", + " for step, state in states.items():\n", + " plotter.plot(state, step * settings.dt)\n", + " plotter.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Widget\n", + "\n", + "Play around with the widget and change the number of superdroplets ($n_{SD} = 2^X$), slope parameter ($b$) in the Golovin collision kernel, and the scale parameter in the droplet size distribution ($r$).\n", + "\n", + "
\n", + "Note: Running the box model takes a few seconds, so be patient after you move one of the sliders.
\n", + "\n", + "The plot generated shows the evolution of the droplet size distribution at various time points (in color). Plotted underneath (in black) is the analytical solution, which exists for this simple Golovin collision kernel." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-15T15:58:07.011791Z", + "start_time": "2025-05-15T15:58:05.806155Z" + }, + "pycharm": { + "is_executing": true + } + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "3496ed2134e4414eaa22d17c076cadd3", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(IntSlider(value=12, continuous_update=False, description='log2(nSD)', max=18, min=6, style=Slid…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "93a049b67eb841fd922feab04fddbc06", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(Checkbox(value=True, description='smooth plot'),))" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "40f367276c294cecbe7b87c0d53a698a", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "IntProgress(value=100, description='%')" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "33aaa5a61fc14d8c9b281049c0cbfb82", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Output()" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "style = {'description_width': 'initial'}\n", + "n_SD = widgets.IntSlider(value=12, min=6, max=18, step=1, \n", + " description='log2(nSD)', continuous_update=False, style=style)\n", + "b = widgets.FloatSlider(value=1.5e3, min=1e3, max=2e3, step=1e2, \n", + " description='b (s-1)', continuous_update=False,\n", + " readout_format='.1e', style=style)\n", + "r = widgets.IntSlider(value=30, min=25, max=35, step=1, \n", + " description='r (um)', continuous_update=False, style=style)\n", + "sliders = widgets.HBox([n_SD, b, r])\n", + "\n", + "smooth = widgets.Checkbox(value=True, description='smooth plot')\n", + "options = [smooth]\n", + "boxes = widgets.HBox(options)\n", + "freezer = widgets.Freezer([n_SD, b, r])\n", + "inputs = {'_freezer': freezer, '_n': n_SD, '_b': b, '_r': r, '_smooth': smooth}\n", + "progbar = widgets.IntProgress(min=0, max=100, description='%')\n", + "\n", + "if 'CI' not in os.environ:\n", + " widgets.display(sliders, boxes, progbar, widgets.interactive_output(demo, inputs))\n", + "else:\n", + " demo(**{k:v.value for k,v in inputs.items()})" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "is_executing": true + } + }, + "source": [ + "## Questions\n", + "\n", + "1. How does the shape of the droplet size distribution change over time as particles collide and coalesce?\n", + "\n", + "2. For the Golovin collision kernel there is an analytical solution, plotted in the black curve. \n", + "How many superdroplets are needed to robustly simulate droplet collection?\n", + "\n", + "3. What does the `b` parameter in the collision kernel control?\n", + "\n", + "4. How does the mean radius of the droplets affect the collision rate? " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.10.9 ('pysdm')", + "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.9" + }, + "vscode": { + "interpreter": { + "hash": "b14f34a08619f4a218d80d7380beed3f0c712c89ff93e7183219752d640ed427" + } + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/PySDM/source/tutorials/condensation/condensation_playground.ipynb b/PySDM/source/tutorials/condensation/condensation_playground.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..065c9298b693becd204e815ec7364c628adb1eac --- /dev/null +++ b/PySDM/source/tutorials/condensation/condensation_playground.ipynb @@ -0,0 +1,385 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/tutorials/condensation/condensation_playground.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/tutorials/condensation/condensation_playground.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/tutorials/condensation/condensation_playground.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Cloud Microphysics: Part 1\n", + "- Activation of aerosol particles into cloud droplets\n", + "- Exploring how size/composition affect condensational growth\n", + "\n", + "Based on Example Figure from Pyrcel code documentation https://pyrcel.readthedocs.io/en/latest/examples/basic_run.html" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2025-11-29T06:49:21.370714Z", + "start_time": "2025-11-29T06:49:21.366067Z" + } + }, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Droplet activation \n", + "(for more info read Ch. 6 of Lohmann's _An Introduction to Clouds_)\n", + "\n", + "#### Köhler curve:\n", + "- Curvature effect (Kelvin equation), describes the increase in vapor pressure over a curved surface\n", + "compared to a flat surface and how this depends on the surface tension and radius of the droplet. \n", + "$e_s$ is the saturation vapor pressure over a surface of radius $r$, so $e_s(\\infty)$ is the \n", + "saturation vapor pressure over a flat surface. $\\sigma$ is the surface tension, $\\rho$ is the density\n", + "of the solution, $R_v$ is the gas constant for water vapor, and $T$ is the temperature.\n", + "\n", + "$e_s(r) = e_s(\\infty) \\exp \\left( \\frac{2 \\sigma}{r \\rho R_v T} \\right)$\n", + "\n", + "\n", + "
\n", + "Fun fact: Based on the curvature considerations alone, saturation ratio in the atmosphere would need to be 5-10 for water to condense homogeneously, aka it would be extremely humid! Fortunately, we have aerosols that can serve as nuclei for water vapor to condense onto, and supersaturations in Earth's atmosphere rarely exceed 1%.\n", + "
\n", + "\n", + "- Solute effect (Raoult's law), describes the reduction of vapor pressure over a flat surface due\n", + "to the presence of soluble material, aka aerosol.\n", + "$\\kappa$ is refered to as the hygroscopicity, defined as the inverse of the water activity ($a_w$).\n", + "Again, $e_s$ is the saturation vapor pressure of pure water, and now $e_*$ is the vapor pressure \n", + "of the solution with $n_s$ moles of solute and $n_w$ moles of water.\n", + "\n", + "$\\kappa = \\frac{1}{a_w} = \\frac{e_s(\\infty)}{e_*(\\infty)} = \\frac{n_s + n_w}{n_w}$\n", + "\n", + "The hygroscopicity (inverse of water activity) is defined as the ratio of the total number of \n", + "moles of solute plus water to the number of moles of water.\n", + "\n", + "- Putting it together, the Köhler curve, or $\\kappa$-Köhler curve, describes the hygroscopic \n", + "growth of particles, and the maximum of this curve, describes the point of activation from\n", + "an aerosol into a cloud droplet.\n", + "$S$ is the saturation ratio, which is usually linerarized as follows:\n", + "\n", + "$S(r) = \\frac{e_*(r)}{e_s(\\infty)} \\approx 1 + \\frac{a}{r} - \\frac{b}{r^3}$\n", + "\n", + "\n", + "Fig 6.11 from Lohmann. You can see a characteristic Köhler curve with the critical radius ($r_{act}$) and supersaturation ($S_{act}$) which separate the stable (aerosol or \"solution droplet\") and unstable (cloud droplet) regimes labeled.\n", + "\n", + "\n", + "
\n", + "Other considerations: Surface tension: The surface tension $\\sigma$ in the Kelvin equation is usually assumed as constant $\\sigma = \\sigma_w = 72$ mN, but complex chemistry of the aerosol can sometimes actually\n", + "modify the effective surface tension of the growing cloud droplet.\n", + "
\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## PySDM parcel model widget\n", + "\n", + "In this homework assignment, and with this `PySDM` example notebook, you have the chance to explore how particle size, number concentration, and chemical composition, influence the bulk properties of a cloud by using a parcel model.\n", + "\n", + "A parcel model takes a parcel of air and lifts it adiabatically, greatly simplifying the dynamics taking place in a real cloud, but resolving the microphysical processes we are interested in exploring here.\n", + "\n", + "We initialize this parcel with a bimodal aerosol composed of two lognormal modes. The first mode is fixed, while the widget will let you play with the properties of the second mode. The default configuration represents a typical case in a marine environment. The first mode is smaller, more numerous sulfate aerosol, and the second mode is larger radii, less numerous, highly hygroscopic sea salt aerosol. \n", + "\n", + "You can play around with the widget at the bottom to change the initial aerosol properties, while keeping the dynamics fixed (i.e. updraft velocity `w = 1 * si.m / si.s` or temperature `T0 = 274 * si.K`). " + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2025-11-29T06:49:28.331970Z", + "start_time": "2025-11-29T06:49:21.387576Z" + } + }, + "outputs": [], + "source": [ + "# import functions for creating interactive widget\n", + "from PySDM_examples.utils import widgets\n", + "import numpy as np\n", + "from numpy import errstate\n", + "\n", + "# import PySDM tools for initializing and running a cloud parcel model\n", + "from PySDM import Formulae\n", + "from PySDM.physics import si\n", + "from PySDM.initialisation.spectra import Lognormal\n", + "from PySDM.products import (\n", + " ParcelDisplacement, AmbientTemperature, AmbientRelativeHumidity,\n", + " ParticleSizeSpectrumPerVolume, ParticleVolumeVersusRadiusLogarithmSpectrum\n", + ")\n", + "\n", + "# import tools for running and plotting this tutorial\n", + "from PySDM_examples.Pyrcel.settings import Settings\n", + "from PySDM_examples.Pyrcel.simulation import Simulation\n", + "from PySDM_examples.Pyrcel.profile_plotter import ProfilePlotter" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2025-11-29T06:49:28.369592Z", + "start_time": "2025-11-29T06:49:28.366123Z" + } + }, + "outputs": [], + "source": [ + "# create progress bar for widget\n", + "progbar = widgets.IntProgress(min=0, max=100, description='%')" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2025-11-29T06:49:28.386077Z", + "start_time": "2025-11-29T06:49:28.377982Z" + } + }, + "outputs": [], + "source": [ + "# create initial aerosol distribution\n", + "# run cloud parcel model\n", + "# save and plot results\n", + "\n", + "# k2, N2, and r2 are the hygroscopicity, number concentration, and mean radius\n", + "# of the second Lognormal aerosol mode \n", + "def demo(*, _freezer, _k2, _N2, _r2):\n", + " with _freezer:\n", + " with errstate(all='raise'):\n", + " settings = Settings(\n", + " dz = 1 * si.m,\n", + " n_sd_per_mode = (10, 10),\n", + " aerosol_modes_by_kappa = {\n", + " .54: Lognormal(\n", + " norm_factor=850 / si.cm ** 3,\n", + " m_mode=15 * si.nm,\n", + " s_geom=1.6\n", + " ),\n", + " _k2: Lognormal(\n", + " norm_factor=_N2 / si.cm ** 3,\n", + " m_mode=_r2 * si.nm,\n", + " s_geom=1.2\n", + " )\n", + " },\n", + " vertical_velocity = 1.0 * si.m / si.s,\n", + " initial_pressure = 775 * si.mbar,\n", + " initial_temperature = 274 * si.K,\n", + " initial_relative_humidity = 0.98,\n", + " displacement = 250 * si.m,\n", + " formulae = Formulae(constants={'MAC': .3})\n", + " )\n", + " dry_radius_bin_edges = np.logspace(\n", + " np.log10(1e-3 * si.um),\n", + " np.log10(5e0 * si.um),\n", + " 33, endpoint=False\n", + " )\n", + " simulation = Simulation(\n", + " settings,\n", + " products=(\n", + " ParcelDisplacement(\n", + " name='z'),\n", + " AmbientRelativeHumidity(\n", + " name='S_max_percent', unit='%', var='RH'),\n", + " AmbientTemperature(\n", + " name='T'),\n", + " ParticleSizeSpectrumPerVolume(\n", + " name='dry:dN/dR', radius_bins_edges=dry_radius_bin_edges, dry=True),\n", + " ParticleVolumeVersusRadiusLogarithmSpectrum(\n", + " name='dry:dV/dlnR', radius_bins_edges=dry_radius_bin_edges, dry=True),\n", + " ),\n", + " )\n", + " output = simulation.run((widgets.ProgbarUpdater(progbar, settings.output_steps[-1]),))\n", + "\n", + " with errstate(invalid='ignore'):\n", + " plotter = ProfilePlotter(settings)\n", + " plotter.plot(output)\n", + " plotter.show()\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Widget\n", + "\n", + "Play around with the widget and change the hygroscopicity ($\\kappa_2$), number concentration ($N_2$), and mean radius ($r_2$) of the second (\"sea salt\") mode. \n", + "\n", + "
\n", + "Note: Running the parcel model takes a few seconds, so be patient after you move one of the sliders.
\n", + "\n", + "The plots generated show (on the left) the profile of supersaturation ($S-1$, black) and temperature ($T$, red) and (on the right) profiles of droplet radius for each super particle. In pink are particles from the first mode (sulfate) and in blue are particles from the second mode (in the default case, sea salt)." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "ExecuteTime": { + "end_time": "2025-11-29T06:49:43.882563Z", + "start_time": "2025-11-29T06:49:28.394888Z" + } + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "b942bd12046d4343a9cbc821e9de5262", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(FloatSlider(value=1.2, continuous_update=False, description='κ2', max=1.4, min=0.2, readout_for…" + ] + }, + "jetTransient": { + "display_id": null + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f9a452fa719b4aa884de40d22a8109a6", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "IntProgress(value=0, description='%')" + ] + }, + "jetTransient": { + "display_id": null + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "4342d6abef224ef98ac818f8a6feec1a", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Output()" + ] + }, + "jetTransient": { + "display_id": null + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# create widget\n", + "# use to explore how the hygroscopicity, number concentration, and mean radius\n", + "# of the initial aerosol distribution affect the bulk parcel properties\n", + "# like maximum supersaturation and temperature profile\n", + "\n", + "style = {'description_width': 'initial'}\n", + "k2 = widgets.FloatSlider(value=1.2, min=0.2, max=1.4, step=0.1, description='κ2',\n", + " continuous_update=False, readout_format='.1f', style=style)\n", + "N2 = widgets.IntSlider(value=10, min=5, max=50, step=5, description='N2 (cm-3)',\n", + " continuous_update=False, style=style)\n", + "r2 = widgets.IntSlider(value=850, min=200, max=1000, step=50, description='r2 (nm)',\n", + " continuous_update=False, style=style)\n", + "sliders = widgets.HBox([k2, N2, r2])\n", + "freezer = widgets.Freezer([k2, N2, r2])\n", + "inputs = {'_freezer': freezer, '_k2': k2, '_N2': N2, '_r2': r2}\n", + "\n", + "if 'CI' not in os.environ:\n", + " widgets.display(sliders, progbar, widgets.interactive_output(demo, inputs))\n", + "else:\n", + " demo(**{k:v.value for k,v in inputs.items()})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Questions\n", + "\n", + "1. Extremes: Which combination of (changeable) parameters leads to the largest maximum supersaturation? Which to the smallest? Why?\n", + "\n", + "2. Sensitivity: Is the cloud more sensitive to changes in aerosol size, number, or composition? Explain how you are measuring this.\n", + "\n", + "3. Albedo: The albedo of a cloud is very dependent on the size of the individual droplets it is composed of. \n", + "We can express the cloud albedo ($\\alpha$) in terms of the cloud optical thickness ($\\tau$) and a dimensionless asymmetry parameter ($g$)\n", + "that describes the relative amount of radiation that is forward-scattered vs. backscattered. \n", + "$$\\alpha = \\frac{(1-g) \\tau}{2 + (1-g)\\tau}$$\n", + "Cloud droplets (order 1-10$\\mu$m) tend to be strongly forward-scattering with an asymmetry parameter around $g=0.85$.\n", + "The cloud optical thickness can be written in terms of the liquid water path through the cloud (LWP) and effective radius of the droplets ($r_e$).\n", + "$$\\tau = \\frac{3 LWP}{2 \\rho_w r_e}$$\n", + "
    \n", + "
  1. Write down an expression for the cloud albedo. Assuming a fixed liquid water path, what is the sensitivity of albedo to droplet effective radius? This sensitivity is known as the \"Twomey effect.\"
  2. \n", + "
  3. Describe how the albedo would change given changes to the initial aerosol size distribution.
  4. \n", + "
\n", + "\n", + "4. Real-world pollution regulations: How would you expect temperatures in Los Angeles to change if a policy was implemented that cut pollution in half. You can assume that this policy will also reduce the number of aerosols that can serve as cloud nuclei in half. Qualitative answers are good." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2025-11-29T06:49:43.905806Z", + "start_time": "2025-11-29T06:49:43.904180Z" + } + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.12.8" + }, + "vscode": { + "interpreter": { + "hash": "b14f34a08619f4a218d80d7380beed3f0c712c89ff93e7183219752d640ed427" + } + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/PySDM/source/tutorials/condensation/kohler_curve.svg b/PySDM/source/tutorials/condensation/kohler_curve.svg new file mode 100644 index 0000000000000000000000000000000000000000..4791fd6b09473e5ac01f8f43876b02d53cc61fc0 --- /dev/null +++ b/PySDM/source/tutorials/condensation/kohler_curve.svg @@ -0,0 +1,242 @@ + + + + + + + + + + +175 +6.5Köhlercurve + + + + +r +[µm] + +0.010.1 1 10 + +S + +0.98 +0.99 +1.00 +1.01 +1.02 + + + + + + + + +S +act +r +act + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +C + + + +A +B + + + + + + + +Solution dropletCloud droplet +StableUnstable + +t +Fig.6.11 +Köhlercurveasafunctionofdropletradius +r +andsaturationratio +S +foranNaClparticlewithdryradius +r +dry +=0.01 +µ +m +andvan’tHo + +factor +i += +2at +T +=273.15Kand +σ +w += +0.0756Nm + +1 +.Alsoindicatedaredi + +erentstartingpoints +A–C.PerturbationsofdropletsstartingfromA,BorCareindicatedbythesmallarrowsandtheprimedletters.The +activationradius +r +act +dividestheKöhlercurveintoastableandanunstablebranchandseparatessolutiondroplets +fromclouddroplets. +withoutchangingtheambientsaturationratio.Thismovesthesolutiondroplettopoint +A + +ataslightlylargernewradius +r ++ +δ +,where +δ +representsaninfinitesimalchange.The +equilibriumvaporpressureofthesolutiondroplet +e + +( +r ++ +δ +)describedbytheKöhlercurve +ishigherthan +e + +( +r +),i.e.theatmosphereissubsaturatedwithrespecttothesolutiondroplet +atpointA + +.Thiswillcausewatermoleculestoevaporateuntilthesolutiondropletisback +inequilibriumwiththeenvironment(pointA).Thecorrespondingmechanismalsoapplies +foraperturbationthatremovesmoleculesfromthesolutiondropletatpointB,movingit +toB + +,where +e + +( +r + +δ +) +< +e + +( +r +).Nowtheatmosphereissupersaturatedwithrespecttothe +solutiondropletatB + +andwatervapormoleculescondenseonthesolutiondropletuntilitis +backatpointB,inequilibriumwiththeenvironment.Hence,aslongas +r +< +r +act +asolution +dropletchangesitssizeonlyasaresponsetoachangeintheambientsaturationratio +S +. +Uponasmallincreasein +S +thedropletinitiallyatpointBmovestopointB +′′ +corresponding +toanewequilibriumatanew,larger,dropletradius.Theseobservationsareconsistentwith +thefactthateverypointontheascendingbranchofaKöhlercurvecorrespondstoalocal +minimumin + +G +het +(Figure +6.10 +a,b). +Iftheambientsaturationratio +S +exceeds +S +act +foranindividualsolutiondroplet,thereis +noequilibriumforthatdroplet.Thedropletquicklygrowsbeyond +r +act +,andtheenvironment +becomesincreasinglysupersaturatedforaclouddroplet,becausethedifference +S + +S +( +r +) +increaseswithincreasingradiusfor +r +> +r +act +. +Asolutiondropletwhichhasbeenactivatedintoaclouddropletcanbeinunstableequi- +libriumwiththeatmosphereataspecificsaturationratio1 +< +S +< +S +act +(i.e.aftertheparticle + + +���C�,���� 75�6D�8�9 BD��7BD9��9D�� + +���C�,��8B� BD���� �����.�2������������� ��� +/B���B5898�:DB�� +���C�,���� 75�6D�8�9 BD��7BD9 + +�B��B��4���9D�����3�9B�B���0�6D5D� +��B�� +���15�������5����,��,�� +����6�97���B���9�.5�6D�8�9�.BD9��9D���B:���9��5�5��56�9�5� + + diff --git a/PySDM/source/tutorials/wikipedia/sdm.ipynb b/PySDM/source/tutorials/wikipedia/sdm.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..f8bcb119cd342d35d9ff2d20e70b61b69170145a --- /dev/null +++ b/PySDM/source/tutorials/wikipedia/sdm.ipynb @@ -0,0 +1,446 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "add4713e-e0c8-4071-b0f6-98a747e4faab", + "metadata": {}, + "source": [ + "[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/tutorials/wikipedia/sdm.ipynb)\n", + "[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/tutorials/wikipedia/sdm.ipynb)\n", + "[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/tutorials/wikipedia/sdm.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "0d49e0d6-1561-4027-b591-e72d39fedfca", + "metadata": {}, + "source": [ + "#### developed for the Wikipedia article on SDM" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "46a8b163-a304-4489-94f9-acc01b2acbd3", + "metadata": {}, + "outputs": [], + "source": [ + "import os, sys\n", + "os.environ['NUMBA_THREADING_LAYER'] = 'workqueue' # PySDM & PyMPDATA don't work with TBB; OpenMP has extra dependencies on macOS\n", + "if 'google.colab' in sys.modules:\n", + " !pip --quiet install open-atmos-jupyter-utils\n", + " from open_atmos_jupyter_utils import pip_install_on_colab\n", + " pip_install_on_colab('PySDM-examples', 'PySDM')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "4a7c9b9a-7558-42c9-99ae-c0fa7b28ba1c", + "metadata": {}, + "outputs": [], + "source": [ + "# pylint: disable=non-ascii-name" + ] + }, + { + "cell_type": "markdown", + "id": "2da33a75-611a-4ca6-8af0-73bfe50b910c", + "metadata": {}, + "source": [ + "### particle system state and quantile sampling " + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "1e134b46-274f-4612-b899-15b78651bc75", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from scipy import stats\n", + "\n", + "def sample_uniform_multiplicity(*,\n", + " rng: np.random.Generator,\n", + " dist: stats.rv_continuous,\n", + " norm: float,\n", + " n_s: int,\n", + "):\n", + " state = np.empty(\n", + " shape=n_s,\n", + " dtype=[('ξ', np.int64), ('m', np.float64)]\n", + " )\n", + " state['ξ'] = round(norm / n_s)\n", + " state['m'] = dist.ppf(rng.uniform(0, 1, n_s))\n", + " return state" + ] + }, + { + "cell_type": "markdown", + "id": "2b89832a-0555-408f-acfe-ce7338b6bbed", + "metadata": {}, + "source": [ + "### \"pseudocode\" (i.e., valid Python) implementation of SDM" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "37b35ed8-d8f3-484d-ad54-4385b45d82cf", + "metadata": {}, + "outputs": [], + "source": [ + "from collections import abc\n", + "\n", + "def sdm(*,\n", + " rng: np.random.Generator,\n", + " ξ: abc.MutableSequence[int],\n", + " m: abc.MutableSequence[float],\n", + " kern: abc.Callable[[float, float], float],\n", + " Δt: float,\n", + " Δv: float,\n", + "):\n", + " \"\"\" SDM step assuming non-zero multiplicities \"\"\"\n", + " n_s = len(ξ)\n", + " n_pair = n_s // 2\n", + " \n", + " pairs = rng.permutation(n_s)[: 2 * n_pair]\n", + " φ = rng.uniform(0, 1, n_pair)\n", + "\n", + " p_ratio = n_s * (n_s - 1) / 2 / n_pair\n", + " for α, (j, k) in enumerate(pairs.reshape(-1, 2)):\n", + " p_jk = kern(m[j], m[k]) * Δt / Δv\n", + " if ξ[j] < ξ[k]:\n", + " j, k = k, j\n", + " p_α = ξ[j] * p_ratio * p_jk\n", + " γ = p_α // 1 + (p_α - p_α // 1) > φ[α]\n", + " if γ != 0:\n", + " γ = min(γ, (ξ[j] / ξ[k]) // 1)\n", + " if ξ[j] - γ * ξ[k] > 0:\n", + " ξ[j] -= γ * ξ[k]\n", + " m[k] += γ * m[j]\n", + " else:\n", + " ξ[j] = ξ[k] // 2\n", + " ξ[k] -= ξ[j]\n", + " m[k] += γ * m[j]\n", + " m[j] = m[k]" + ] + }, + { + "cell_type": "markdown", + "id": "72e4e1dc-1d11-4504-a152-065fec03561d", + "metadata": {}, + "source": [ + "### definition of the additive collision kernel and the Golovin's analytic solution" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "debba891-ad93-4698-bba7-7a1f63126018", + "metadata": {}, + "outputs": [], + "source": [ + "from scipy import special\n", + "\n", + "def golovin_analytic_solution(mass, time, x0, b, n0):\n", + " \"\"\" [Golovin's analytic solution](http://mi.mathnet.ru/dan27630) \n", + " to Smoluchowski coagulation equation for additive kernel\n", + " and exponential initial condition \"\"\"\n", + " tau = 1 - np.exp(-n0 * x0 * b * time)\n", + " sqrt_tau = np.sqrt(tau)\n", + " return (\n", + " (1 - tau) / (mass * sqrt_tau)\n", + " * special.ive(1, 2 * mass / x0 * sqrt_tau) # pylint: disable=no-member\n", + " * np.exp(-(1 + tau - 2 * sqrt_tau) * mass / x0)\n", + " )\n", + "\n", + "def additive_kernel(mass_1, mass_2, coeff):\n", + " \"\"\" additive coagulation kernel \"\"\"\n", + " return coeff * (mass_1 + mass_2)" + ] + }, + { + "cell_type": "markdown", + "id": "c66c5375-19e6-4564-aa92-00cf680d27ec", + "metadata": {}, + "source": [ + "### simulation prameters inspired by Fig. 2 from [Shima et al. 2009](https://doi.org/10.1002/qj.441)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "2b6192c9-48d3-4526-b2ec-da6243190c0d", + "metadata": {}, + "outputs": [], + "source": [ + "from types import SimpleNamespace\n", + "\n", + "PARAMS_PHYS = SimpleNamespace(\n", + " n0 = 2**23,\n", + " Δv = 1e6,\n", + " b = 1.5e3,\n", + ")\n", + "PARAMS_PHYS.norm = PARAMS_PHYS.n0 * PARAMS_PHYS.Δv\n", + "PARAMS_PHYS.dist = stats.expon(loc=0, scale=1 / PARAMS_PHYS.norm)\n", + "\n", + "PARAMS_COMP = SimpleNamespace(\n", + " n_drop = 2**11,\n", + " Δt = 1,\n", + " n_step = 3600,\n", + " output_step = 100,\n", + ")\n", + "\n", + "PARAMS_PLOT = SimpleNamespace(\n", + " x_of_mass = lambda mass: np.log(mass) / 3,\n", + " mass_of_x = lambda x: np.exp(3 * x),\n", + " bins_x_range = (-12, -5),\n", + " bins_count = 35,\n", + ")\n", + "\n", + "RNG = np.random.default_rng(seed=123)" + ] + }, + { + "cell_type": "markdown", + "id": "8392c72c-2f4c-4af9-bc8e-bef3181f5f06", + "metadata": {}, + "source": [ + "### simulation loop" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "86169955-0733-4b06-a82a-8dcaeb187941", + "metadata": {}, + "outputs": [], + "source": [ + "from functools import partial\n", + "from copy import deepcopy\n", + "\n", + "def simulate(*, params_phys, params_comp, rng):\n", + " \"\"\" does simulation for all sampling variants (each variant using the same shuffled numbers) \"\"\"\n", + " particles = sample_uniform_multiplicity(\n", + " dist=params_phys.dist,\n", + " norm=params_phys.norm,\n", + " n_s=params_comp.n_drop,\n", + " rng=rng,\n", + " )\n", + " kern = partial(additive_kernel, coeff=params_phys.b)\n", + " output = []\n", + " for step in range(params_comp.n_step + 1):\n", + " if step != 0:\n", + " sdm(\n", + " rng=rng,\n", + " kern=kern,\n", + " Δt=params_comp.Δt,\n", + " Δv=params_phys.Δv,\n", + " m=particles['m'],\n", + " ξ=particles['ξ'],\n", + " )\n", + " output += [deepcopy(particles) if step % params_comp.output_step == 0 else None]\n", + " return output\n", + "OUTPUT = simulate(params_phys=PARAMS_PHYS, params_comp=PARAMS_COMP, rng=RNG)" + ] + }, + { + "cell_type": "markdown", + "id": "99b62f13-c591-4dd4-90bc-fc05d73bf3b2", + "metadata": {}, + "source": [ + "### ensuring that the assumption of non-zero multiplicities was valid till the end of the simulation" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "0b96734e-ee65-4f4f-80a5-5ae8c306576c", + "metadata": {}, + "outputs": [], + "source": [ + "assert all(OUTPUT[-1]['ξ'] > 0)" + ] + }, + { + "cell_type": "markdown", + "id": "57fac335-fd8f-48af-8057-1746bb2758f3", + "metadata": {}, + "source": [ + "### animation generation (incl. assertions for mass conservation and for match against analytic solution)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "9e2fd38f-5d85-4f63-b6d1-2118a28d3d22", + "metadata": {}, + "outputs": [], + "source": [ + "from matplotlib import pyplot\n", + "\n", + "def plot_and_assert(step, *, output, params_phys, params_comp, params_plot, positions):\n", + " \"\"\" plots the particle state as both a histogram as well as population scatter plot\n", + " (with random coordinates shuffled for the purpose of plotting) \"\"\"\n", + " time = 1e-10 if step == 0 else step * params_comp.Δt\n", + " hist_axis_max = 1.5\n", + " particles = output[step]\n", + " \n", + " np.testing.assert_approx_equal(\n", + " desired=np.dot(OUTPUT[0]['m'], OUTPUT[0]['ξ']),\n", + " actual=np.dot(OUTPUT[step]['m'], OUTPUT[step]['ξ']),\n", + " significant=14\n", + " )\n", + " \n", + " fig, axs = pyplot.subplot_mosaic(\n", + " [['hist'], ['part']],\n", + " figsize=(3, 5),\n", + " sharex=True,\n", + " layout='constrained',\n", + " )\n", + " points = axs['part'].scatter(\n", + " params_plot.x_of_mass(particles['m']),\n", + " positions,\n", + " c=np.log2(particles['ξ']),\n", + " s=2\n", + " )\n", + " points.set_clim(10, np.log2(params_phys.norm / params_comp.n_drop))\n", + "\n", + " analytic_mass_dist = lambda mass, x: mass * params_phys.norm * golovin_analytic_solution(\n", + " mass=mass,\n", + " time=time,\n", + " x0=1/params_phys.norm,\n", + " n0=params_phys.n0,\n", + " b=params_phys.b\n", + " ) * np.diff(params_plot.mass_of_x(x)) / np.diff(x)\n", + " \n", + " hist_y, hist_x, _ = axs['hist'].hist(\n", + " x=params_plot.x_of_mass(particles['m']),\n", + " weights=particles['ξ'] / params_phys.norm * particles['m'],\n", + " bins=params_plot.bins_count,\n", + " range=params_plot.bins_x_range,\n", + " label=\"SDM\",\n", + " density=True,\n", + " )\n", + " np.testing.assert_allclose(\n", + " desired=analytic_mass_dist(params_plot.mass_of_x(hist_x[:-1] + np.diff(hist_x)/2), hist_x),\n", + " actual=hist_y,\n", + " atol=hist_axis_max / 3\n", + " )\n", + "\n", + " lin_x, d_x = np.linspace(*params_plot.bins_x_range, 256, retstep=True)\n", + " x_mean = lin_x[:-1] + d_x / 2\n", + " axs['hist'].plot(\n", + " x_mean,\n", + " analytic_mass_dist(params_plot.mass_of_x(x_mean), lin_x),\n", + " color='black',\n", + " label='analytic',\n", + " ls='--'\n", + " )\n", + " \n", + " axs['hist'].legend()\n", + " axs['hist'].set(\n", + " ylim=(0, hist_axis_max),\n", + " yticks=[],\n", + " ylabel=r'pdf(x) $\\cdot$ m(x)',\n", + " title=f'time: {time:.0f} s',\n", + " xlim=params_plot.bins_x_range\n", + " )\n", + " for loc in ('top', 'right'):\n", + " axs['hist'].spines[loc].set_visible(False)\n", + " axs['hist'].plot(1, 0, \">k\", transform=axs['hist'].get_yaxis_transform(), clip_on=False)\n", + " axs['hist'].plot(params_plot.bins_x_range[0], 1.5, \"^k\", clip_on=False)\n", + " axs['part'].set(\n", + " xlabel=r'$x = \\ln(\\sqrt[3]{\\text{m/m}_0})$',\n", + " ylabel='auxiliary particle attribute',\n", + " yticks=[],\n", + " ylim=(0, 1)\n", + " )\n", + " axs['part'].grid()\n", + " fig.colorbar(points, label='log$_2$(ξ)')\n", + " return fig" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "268224c7-050b-4090-a8ec-704955b99678", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f08ebe0df78244c9b3f028709beea7bf", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value=\"./super_droplet_method_teaser.gif<…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from open_atmos_jupyter_utils import show_anim\n", + "\n", + "show_anim(\n", + " partial(\n", + " plot_and_assert,\n", + " output=OUTPUT,\n", + " params_phys=PARAMS_PHYS,\n", + " params_comp=PARAMS_COMP,\n", + " params_plot=PARAMS_PLOT,\n", + " positions=RNG.uniform(0, 1, PARAMS_COMP.n_drop),\n", + " ),\n", + " frame_range=range(0, len(OUTPUT), PARAMS_COMP.output_step),\n", + " gif_file='super_droplet_method_teaser.gif'\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "35d4314f-49fb-4322-82a6-03888410e486", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/README.md b/README.md index 8c3b7caad9ecaf88d798c9308e0df3e1af2f5f77..41e9573ab4e51156c5f90ee699272db188bc1ffd 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,32 @@ --- -title: PySDM -emoji: ⚡ -colorFrom: yellow -colorTo: blue +title: Pysdm MCP +emoji: 🤖 +colorFrom: blue +colorTo: purple sdk: docker +sdk_version: "4.26.0" +app_file: app.py pinned: false --- -Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference +# Pysdm MCP Service + +Auto-generated MCP service for PySDM. + +## Usage + +``` +https://None-PySDM-mcp.hf.space/mcp +``` + +## Connect with Cursor + +```json +{ + "mcpServers": { + "PySDM": { + "url": "https://None-PySDM-mcp.hf.space/mcp" + } + } +} +``` diff --git a/app.py b/app.py new file mode 100644 index 0000000000000000000000000000000000000000..91ac743b095545990033a89f7af05f0974cd7892 --- /dev/null +++ b/app.py @@ -0,0 +1,45 @@ +from fastapi import FastAPI +import os +import sys + +mcp_plugin_path = os.path.join(os.path.dirname(__file__), "PySDM", "mcp_output", "mcp_plugin") +sys.path.insert(0, mcp_plugin_path) + +app = FastAPI( + title="Pysdm MCP Service", + description="Auto-generated MCP service for PySDM", + version="1.0.0" +) + +@app.get("/") +def root(): + return { + "service": "Pysdm MCP Service", + "version": "1.0.0", + "status": "running", + "transport": os.environ.get("MCP_TRANSPORT", "http") + } + +@app.get("/health") +def health_check(): + return {"status": "healthy", "service": "PySDM MCP"} + +@app.get("/tools") +def list_tools(): + try: + from mcp_service import create_app + mcp_app = create_app() + tools = [] + for tool_name, tool_func in mcp_app.tools.items(): + tools.append({ + "name": tool_name, + "description": tool_func.__doc__ or "No description available" + }) + return {"tools": tools} + except Exception as e: + return {"error": f"Failed to load tools: {str(e)}"} + +if __name__ == "__main__": + import uvicorn + port = int(os.environ.get("PORT", 7860)) + uvicorn.run(app, host="0.0.0.0", port=port) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..b079fbecfb9d213cced9fd2bbb5d77382320ae69 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,7 @@ +fastmcp +fastapi +uvicorn[standard] +pydantic>=2.0.0 +numpy +scipy +numba diff --git a/run_docker.ps1 b/run_docker.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..3b287640fa73db80e7bd1703b88ce10053c29c7f --- /dev/null +++ b/run_docker.ps1 @@ -0,0 +1,35 @@ +cd $PSScriptRoot + +$ErrorActionPreference = "Stop" + +$entryName = if ($env:MCP_ENTRY_NAME) { $env:MCP_ENTRY_NAME } else { "PySDM" } +$entryUrl = if ($env:MCP_ENTRY_URL) { $env:MCP_ENTRY_URL } else { "http://localhost:7860/mcp" } +$imageName = if ($env:MCP_IMAGE_NAME) { $env:MCP_IMAGE_NAME } else { "PySDM-mcp" } + +$mcpDir = Join-Path $env:USERPROFILE ".cursor" +$mcpPath = Join-Path $mcpDir "mcp.json" +if (!(Test-Path $mcpDir)) { New-Item -ItemType Directory -Path $mcpDir | Out-Null } + +$config = @{} +if (Test-Path $mcpPath) { + try { $config = Get-Content $mcpPath -Raw | ConvertFrom-Json } catch { $config = @{} } +} + +# Rebuild mcpServers as ordered and append the entry last +$serversOrdered = [ordered]@{} +if ($config -and ($config.PSObject.Properties.Name -contains "mcpServers") -and $config.mcpServers) { + $existing = $config.mcpServers + if ($existing -is [pscustomobject]) { + foreach ($p in $existing.PSObject.Properties) { if ($p.Name -ne $entryName) { $serversOrdered[$p.Name] = $p.Value } } + } elseif ($existing -is [System.Collections.IDictionary]) { + foreach ($k in $existing.Keys) { if ($k -ne $entryName) { $serversOrdered[$k] = $existing[$k] } } + } +} +$serversOrdered[$entryName] = @{ url = $entryUrl } +$config = @{ mcpServers = $serversOrdered } + +$config | ConvertTo-Json -Depth 10 | Set-Content -Path $mcpPath -Encoding UTF8 +Write-Host ("Updated $entryName in " + $mcpPath + " -> " + $entryUrl) + +docker build -t $imageName . +docker run --rm -p 7860:7860 $imageName diff --git a/run_docker.sh b/run_docker.sh new file mode 100644 index 0000000000000000000000000000000000000000..36bc7954ef79bb12f3fc73d29236c1acbf377a7c --- /dev/null +++ b/run_docker.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Switch to the directory where this script is located +cd "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" + +mcp_entry_name="${MCP_ENTRY_NAME:-PySDM}" +mcp_entry_url="${MCP_ENTRY_URL:-http://localhost:7860/mcp}" +mcp_dir="${HOME}/.cursor" +mcp_path="${mcp_dir}/mcp.json" +mkdir -p "${mcp_dir}" + +if command -v python3 >/dev/null 2>&1; then +python3 - "${mcp_path}" "${mcp_entry_name}" "${mcp_entry_url}" <<'PY' +import json, os, sys +path, name, url = sys.argv[1:4] +cfg = {"mcpServers": {}} +if os.path.exists(path): + try: + with open(path, "r", encoding="utf-8") as f: + cfg = json.load(f) + except Exception: + cfg = {"mcpServers": {}} +if not isinstance(cfg, dict): + cfg = {"mcpServers": {}} +servers = cfg.get("mcpServers") +if not isinstance(servers, dict): + servers = {} +ordered = {} +for k, v in servers.items(): + if k != name: + ordered[k] = v +ordered[name] = {"url": url} +cfg = {"mcpServers": ordered} +with open(path, "w", encoding="utf-8") as f: + json.dump(cfg, f, indent=2, ensure_ascii=False) +PY +elif command -v python >/dev/null 2>&1; then +python - "${mcp_path}" "${mcp_entry_name}" "${mcp_entry_url}" <<'PY' +import json, os, sys +path, name, url = sys.argv[1:4] +cfg = {"mcpServers": {}} +if os.path.exists(path): + try: + with open(path, "r", encoding="utf-8") as f: + cfg = json.load(f) + except Exception: + cfg = {"mcpServers": {}} +if not isinstance(cfg, dict): + cfg = {"mcpServers": {}} +servers = cfg.get("mcpServers") +if not isinstance(servers, dict): + servers = {} +ordered = {} +for k, v in servers.items(): + if k != name: + ordered[k] = v +ordered[name] = {"url": url} +cfg = {"mcpServers": ordered} +with open(path, "w", encoding="utf-8") as f: + json.dump(cfg, f, indent=2, ensure_ascii=False) +PY +elif command -v jq >/dev/null 2>&1; then + name="${mcp_entry_name}"; url="${mcp_entry_url}" + if [ -f "${mcp_path}" ]; then + tmp="$(mktemp)" + jq --arg name "$name" --arg url "$url" ' + .mcpServers = (.mcpServers // {}) + | .mcpServers as $s + | ($s | with_entries(select(.key != $name))) as $base + | .mcpServers = ($base + {($name): {"url": $url}}) + ' "${mcp_path}" > "${tmp}" && mv "${tmp}" "${mcp_path}" + else + printf '{ "mcpServers": { "%s": { "url": "%s" } } } +' "$name" "$url" > "${mcp_path}" + fi +else + echo "Warning: neither python nor jq found; skipped updating ~/.cursor/mcp.json" >&2 +fi + +docker build -t PySDM-mcp . +docker run --rm -p 7860:7860 PySDM-mcp