├── .coveragerc ├── .gitattributes ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── antivirus.yml │ ├── build_docs.yml │ ├── ci.yml │ ├── codeql.yml │ ├── linting.yml │ └── wheels.yml ├── .gitignore ├── .mailmap ├── .pre-commit-config.yaml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.rst ├── INSTALL.rst ├── LICENSE.txt ├── MANIFEST.in ├── README.rst ├── codecov.yml ├── continuous_integration └── environment-ci.yml ├── doc ├── Makefile ├── environment.yml ├── make.bat ├── rebuild_docs.sh ├── rebuild_examples.sh ├── rebuild_full_docs.sh └── source │ ├── API │ └── index.rst │ ├── _static │ ├── doc_shared.js │ ├── ppi.png │ ├── pyart-theme.css │ └── rhi.png │ ├── _templates │ ├── autosummary │ │ ├── base.rst │ │ ├── class.rst │ │ └── module.rst │ ├── dev_template.rst │ └── layout.html │ ├── blog.md │ ├── blog_posts │ ├── 2022 │ │ ├── KMKX-map-animation.mp4 │ │ ├── TRACER.ipynb │ │ ├── first_pullrequest.ipynb │ │ ├── hail-analysis-spc.ipynb │ │ ├── ka-band-pyart-xarray-hvplot.ipynb │ │ ├── plot-nexrad-level3.ipynb │ │ └── test.md │ ├── 2023 │ │ └── severe-storms-southern-wisconsin.ipynb │ ├── 2024 │ │ ├── animated-gifs-with-pyart.ipynb │ │ └── epcape-blog-post.ipynb │ ├── .ipynb_checkpoints │ │ └── blog-post-template-checkpoint.ipynb │ ├── blog-post-template.ipynb │ └── images │ │ ├── PyART_Docs_OriginalRHI_withJetColorscheme.png │ │ ├── PyART_Docs_Original_PPI_image.png │ │ ├── TRMM_RSL_webpage.png │ │ ├── dq-browser-epcape.png │ │ ├── interactive-kazr-viz.gif │ │ ├── nexrad-level3-radar-query.png │ │ ├── nexrad-plotting-issue.jpeg │ │ ├── pull_request_changes.png │ │ ├── pull_request_commitmessage.png │ │ └── pyart_github_clone.png │ ├── changelog.md │ ├── conf.py │ ├── dev │ ├── CONTRIBUTING.rst │ ├── ci_setup.rst │ ├── how_to_release.rst │ └── index.rst │ ├── index.rst │ ├── notebook-gallery.rst │ ├── notebooks │ ├── basic_ingest_using_test_radar_object.ipynb │ ├── changing_fields_and_saving.ipynb │ ├── dealiasing_velocity.ipynb │ ├── differential_phase_proceesing_using_LP_methods.ipynb │ ├── mapping_data_to_a_cartesian_grid.ipynb │ ├── masking_data_with_gatefilters.ipynb │ └── the_pyart_radar_object_and_indexing.ipynb │ └── userguide │ ├── INSTALL.rst │ ├── contributors_guide.rst │ ├── index.rst │ ├── pyart_2_0.rst │ └── setting_up_an_environment.rst ├── environment.yml ├── examples ├── README.txt ├── correct │ ├── README.txt │ ├── plot_attenuation.py │ ├── plot_cloud_mask.py │ ├── plot_dealias.py │ └── plot_zdr_check.py ├── io │ ├── README.txt │ ├── plot_nexrad_data_aws.py │ ├── plot_nexrad_data_google_cloud.py │ ├── plot_older_nexrad_data_aws.py │ └── plot_read_cfradial2.py ├── mapping │ ├── README.txt │ ├── plot_compare_two_radars_gatemapper.py │ ├── plot_grid_single_sweep_ppi.py │ ├── plot_map_one_radar_to_grid.py │ └── plot_map_two_radars_to_grid.py ├── plotting │ ├── README.txt │ ├── plot_cappi.py │ ├── plot_choose_a_colormap.py │ ├── plot_corner_reflector.py │ ├── plot_cross_section.py │ ├── plot_max_cappi.py │ ├── plot_modify_colorbar.py │ ├── plot_nexrad_image_muted_reflectivity.py │ ├── plot_nexrad_multiple_moments.py │ ├── plot_nexrad_reflectivity.py │ ├── plot_ppi_cfradial.py │ ├── plot_ppi_mdv.py │ ├── plot_ppi_with_rings.py │ ├── plot_rhi_cfradial.py │ ├── plot_rhi_cfradial_singlescan.py │ ├── plot_rhi_contours_differential_reflectivity.py │ ├── plot_rhi_data_overlay.py │ ├── plot_rhi_mdv.py │ ├── plot_rhi_two_panel.py │ ├── plot_three_panel_gridmapdisplay.py │ ├── plot_xsect.py │ └── radar-cross-section.ipynb ├── retrieve │ ├── README.txt │ ├── column-example.ipynb │ ├── plot_cfad.py │ ├── plot_column_subset.py │ ├── plot_composite_reflectivity.py │ ├── plot_convective_stratiform.py │ ├── plot_feature_detection.py │ ├── plot_hydrometeor.py │ ├── plot_hydrometeor_class_x_band.py │ ├── plot_qpe.py │ ├── plot_vad.py │ └── wavelet_echo_class_example.ipynb └── xradar │ ├── README.txt │ ├── plot_dealias_xradar.py │ ├── plot_grid_xradar.py │ └── plot_xradar.py ├── guides ├── pyart_cheatsheet.pdf ├── pyart_cheatsheet.tex └── setting_up_an_environment.rst ├── pyart ├── __check_build │ ├── README │ ├── __init__.py │ └── _check_build.pyx ├── __init__.py ├── _debug_info.py ├── aux_io │ ├── __init__.py │ ├── arm_vpt.py │ ├── d3r_gcpex_nc.py │ ├── edge_netcdf.py │ ├── gamic_hdf5.py │ ├── gamicfile.py │ ├── kazr_spectra.py │ ├── noxp_iphex_nc.py │ ├── odim_h5.py │ ├── pattern.py │ ├── radx.py │ ├── radx_grid.py │ ├── rainbow_wrl.py │ ├── rxm25.py │ └── sinarame_h5.py ├── bridge │ ├── __init__.py │ └── wradlib_bridge.py ├── config.py ├── core │ ├── __init__.py │ ├── grid.py │ ├── radar.py │ ├── radar_spectra.py │ ├── transforms.py │ └── wind_profile.py ├── correct │ ├── __init__.pxd │ ├── __init__.py │ ├── _common_dealias.py │ ├── _fast_edge_finder.pyx │ ├── _fourdd_h.pxd │ ├── _fourdd_interface.pyx │ ├── _unwrap_1d.pyx │ ├── _unwrap_2d.pyx │ ├── _unwrap_3d.pyx │ ├── attenuation.py │ ├── bias_and_noise.py │ ├── dealias.py │ ├── despeckle.py │ ├── phase_proc.py │ ├── rebuild_fast_edge_finder.sh │ ├── rebuild_fourdd_interface.sh │ ├── region_dealias.py │ ├── src │ │ ├── UWASHINGTON_4DD_README │ │ ├── dealias_fourdd.c │ │ ├── dealias_fourdd.h │ │ ├── helpers.c │ │ ├── helpers.h │ │ ├── sounding_to_volume.c │ │ └── sounding_to_volume.h │ ├── unwrap.py │ ├── unwrap_2d_ljmu.c │ └── unwrap_3d_ljmu.c ├── default_config.py ├── exceptions.py ├── filters │ ├── __init__.py │ └── gatefilter.py ├── graph │ ├── __init__.py │ ├── common.py │ ├── convstrat_scheme_plot.py │ ├── gridmapdisplay.py │ ├── gridmapdisplay_basemap.py │ ├── max_cappi.py │ ├── radardisplay.py │ ├── radardisplay_airborne.py │ ├── radarmapdisplay.py │ └── radarmapdisplay_basemap.py ├── io │ ├── __init__.pxd │ ├── __init__.py │ ├── _rsl_h.pxd │ ├── _rsl_interface.pxd │ ├── _rsl_interface.pyx │ ├── _sigmet_noaa_hh.py │ ├── _sigmetfile.pyx │ ├── arm_sonde.py │ ├── auto_read.py │ ├── cfradial.py │ ├── chl.py │ ├── common.py │ ├── grid_io.py │ ├── mdv_common.py │ ├── mdv_grid.py │ ├── mdv_radar.py │ ├── nexrad_archive.py │ ├── nexrad_cdm.py │ ├── nexrad_common.py │ ├── nexrad_interpolate.pyx │ ├── nexrad_level2.py │ ├── nexrad_level3.py │ ├── nexradl3_read.py │ ├── output_to_geotiff.py │ ├── rebuild_rsl_interface.sh │ ├── rebuild_sigmetfile.sh │ ├── rsl.py │ ├── sigmet.py │ ├── uf.py │ ├── uf_write.py │ └── uffile.py ├── lazydict.py ├── map │ ├── __init__.py │ ├── _gate_to_grid_map.pyx │ ├── _load_nn_field_data.pyx │ ├── ckdtree.pyx │ ├── gate_mapper.py │ ├── gates_to_grid.py │ ├── grid_mapper.py │ ├── rebuild_ball_tree.sh │ ├── rebuild_ckdtree.sh │ ├── rebuild_gate_to_grid_map.sh │ └── rebuild_load_nn_field_data.sh ├── retrieve │ ├── __init__.py │ ├── _echo_class.py │ ├── _echo_class_wt.py │ ├── _kdp_proc.pyx │ ├── advection.py │ ├── cappi.py │ ├── cfad.py │ ├── comp_z.py │ ├── echo_class.py │ ├── gate_id.py │ ├── kdp_proc.py │ ├── qpe.py │ ├── qvp.py │ ├── rebuild_kdp_proc.sh │ ├── simple_moment_calculations.py │ ├── spectra_calculations.py │ ├── srv.py │ └── vad.py ├── testing │ ├── __init__.py │ ├── data │ │ ├── README │ │ ├── baseline_figures │ │ │ ├── example_cfradial_cr_raster.png │ │ │ ├── example_cfradial_ppi.png │ │ │ ├── example_cfradial_rhi.png │ │ │ ├── example_mdv_ppi.png │ │ │ ├── example_mdv_rhi.png │ │ │ ├── example_sigmet_ppi.png │ │ │ └── example_sigmet_rhi.png │ │ ├── check_nexrad_dummy.py │ │ ├── dummify_nexrad_file.py │ │ ├── example_arm_sonde.cdf │ │ ├── example_cfradial_cr_raster.nc │ │ ├── example_cfradial_ppi.nc │ │ ├── example_cfradial_rhi.nc │ │ ├── example_chl_rhi.chl │ │ ├── example_interpolatedsonde.cdf │ │ ├── example_mdv_grid.mdv │ │ ├── example_mdv_ppi.mdv │ │ ├── example_mdv_rhi.mdv │ │ ├── example_nexrad_archive_msg1.bz2 │ │ ├── example_nexrad_archive_msg31.bz2 │ │ ├── example_nexrad_archive_msg31_compressed.ar2v │ │ ├── example_nexrad_cdm.bz2 │ │ ├── example_nexrad_level3_msg163 │ │ ├── example_nexrad_level3_msg176 │ │ ├── example_nexrad_level3_msg19 │ │ ├── example_rays.npz │ │ ├── example_sigmet_ppi.sigmet │ │ ├── example_sigmet_rhi.sigmet │ │ ├── example_uf_ppi.uf │ │ ├── make_single_ray.py │ │ ├── make_small_arm_sonde.sh │ │ ├── make_small_cfradial_ppi.py │ │ ├── make_small_cfradial_rhi.py │ │ ├── make_small_chl_rhi.py │ │ ├── make_small_interp_sonde.py │ │ ├── make_small_mdv_grid.py │ │ ├── make_small_mdv_ppi.py │ │ ├── make_small_mdv_rhi.py │ │ ├── make_small_nexrad_archive_msg1.sh │ │ ├── make_small_nexrad_archive_msg31.sh │ │ ├── make_small_nexrad_archive_msg31_compressed.py │ │ ├── make_small_nexrad_cdm.sh │ │ ├── make_small_sigmet_ppi.py │ │ ├── make_small_sigmet_rhi.py │ │ ├── make_small_uf.sh │ │ ├── quick_plot_cfradial_ppi.py │ │ ├── quick_plot_cfradial_rhi.py │ │ ├── quick_plot_mdv_ppi.py │ │ ├── quick_plot_mdv_rhi.py │ │ ├── quick_plot_sigmet_ppi.py │ │ └── quick_plot_sigmet_rhi.py │ ├── example_data.py │ ├── registry.txt │ ├── sample_files.py │ ├── sample_objects.py │ └── tmpdirs.py ├── util │ ├── __init__.py │ ├── circular_stats.py │ ├── columnsect.py │ ├── datetime_utils.py │ ├── hildebrand_sekhon.py │ ├── met.py │ ├── radar_utils.py │ ├── sigmath.py │ ├── simulated_vel.py │ └── xsect.py └── xradar │ ├── __init__.py │ └── accessor.py ├── pyproject.toml ├── readthedocs.yaml ├── requirements.txt ├── roadmaps ├── pyart-roadmap-rc1.0.pdf └── pyart-roadmap-rc2.0.pdf ├── scripts ├── anytocfradial ├── check_cfradial ├── convert_legacy_grid ├── radar_info └── radar_plot ├── setup.py └── tests ├── bridge └── test_wradlib_bridge.py ├── core ├── test_grid.py ├── test_radar.py ├── test_radar_spectra.py ├── test_transforms.py └── test_wind_profile.py ├── correct ├── attenuation_rays.npz ├── attenuation_rays_philinear.npz ├── attenuation_rays_zphi.npz ├── reference_ray_plot.png ├── reference_rays.npz ├── test_attenuation.py ├── test_correct_bias.py ├── test_phase_proc.py ├── test_region_dealias.py └── test_unwrap.py ├── custom_config.py ├── filters └── test_gatefilter.py ├── graph ├── test_common.py ├── test_gridmapdisplay.py ├── test_gridmapdisplay_basemap.py ├── test_plot_maxcappi.py ├── test_radardisplay.py ├── test_radarmapdisplay.py └── test_radarmapdisplay_basemap.py ├── io ├── test_arm_sonde.py ├── test_auto_read.py ├── test_cfradial.py ├── test_chl.py ├── test_grid_io.py ├── test_mdv_common.py ├── test_mdv_grid.py ├── test_mdv_radar.py ├── test_nexrad_archive_msg1.py ├── test_nexrad_archive_msg31.py ├── test_nexrad_cdm.py ├── test_nexrad_level2.py ├── test_nexrad_level3.py ├── test_output_to_geotiff.py ├── test_rsl.py ├── test_sigmet.py ├── test_uf.py └── test_uf_write.py ├── map ├── test_gatemapper.py ├── test_gates_to_grid.py └── test_grid_mapper.py ├── retrieve ├── baseline │ └── test_compare_kdp_estimation_methods.png ├── test_advection.py ├── test_cappi.py ├── test_cfad.py ├── test_comp_z.py ├── test_compare_kdp_proc.py ├── test_echo_class.py ├── test_gate_id.py ├── test_kdp_proc.py ├── test_qpe.py ├── test_qvp.py ├── test_simple_moment_calculations.py ├── test_spectra_calculations.py ├── test_srv.py └── test_vad.py ├── test_config.py ├── test_debug_info.py ├── testing ├── test_example_datasets.py └── test_sample_objects.py ├── util ├── test_circular_stats.py ├── test_columnsect.py ├── test_datetime_utils.py ├── test_hildebrand_sekhon.py ├── test_radar_utils.py ├── test_simulated_vel.py └── test_xsect.py └── xradar └── test_accessor.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | source = pyart 3 | include = */pyart/* 4 | omit = 5 | */setup.py 6 | */tests* 7 | pyart/aux_io/* 8 | tests/io/test_rsl.py 9 | tests/correct/test_dealias.py 10 | pyart/io/rsl.py 11 | pyart/correct/dealias.py 12 | pyart/graph/gridmapdisplay_basemap.py 13 | pyart/graph/radarmapdisplay_basemap.py 14 | 15 | [report] 16 | exclude_lines = 17 | pragma: no cover 18 | if self.debug: 19 | if debug: 20 | raise NotImplementedError 21 | if __name__ == .__main__.: 22 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | pyart/_version.py export-subst 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners#codeowners-syntax 2 | 3 | # These owners will be the default owners for everything in 4 | # the repo. Unless a later match takes precedence, 5 | 6 | * @mgrover1 7 | * @zssherman 8 | 9 | # any files in the intake_esm directory at the root of the 10 | # repository and any of its subdirectories. 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | - [ ] Closes #xxxx 4 | - [ ] Tests added 5 | - [ ] Documentation reflects changes 6 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | groups: 8 | actions: 9 | patterns: 10 | - "*" 11 | -------------------------------------------------------------------------------- /.github/workflows/antivirus.yml: -------------------------------------------------------------------------------- 1 | on: 2 | pull_request: 3 | types: [assigned, opened, synchronize, reopened, closed] 4 | 5 | jobs: 6 | gitavscan: 7 | runs-on: ubuntu-latest 8 | name: AV scan 9 | steps: 10 | - uses: actions/checkout@v4 11 | - name: Git AV Scan 12 | uses: djdefi/gitavscan@main 13 | with: 14 | full: '--full' 15 | -------------------------------------------------------------------------------- /.github/workflows/build_docs.yml: -------------------------------------------------------------------------------- 1 | name: build-deploy-site 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | # This job installs dependencies, build the website, and pushes it to `gh-pages` 12 | jobs: 13 | deploy-website: 14 | runs-on: ubuntu-latest 15 | defaults: 16 | run: 17 | shell: bash -l {0} 18 | steps: 19 | - uses: actions/checkout@v4 20 | 21 | # Create environment using micromamba 22 | - name: Install Conda environment with Micromamba 23 | uses: mamba-org/setup-micromamba@v2.0.5 24 | with: 25 | environment-file: doc/environment.yml 26 | micromamba-version: '2.0.0-0' 27 | environment-name: pyart-docs 28 | cache-downloads: false 29 | 30 | - name: Fetch all history for all tags and branches 31 | run: | 32 | git fetch --prune --unshallow 33 | 34 | - name: Install PyART 35 | run: | 36 | pip install -e . 37 | 38 | # Build the website 39 | - name: Build the site 40 | run: | 41 | cd doc 42 | make html 43 | # Push the book's HTML to github-pages 44 | - name: GitHub Pages action 45 | uses: peaceiris/actions-gh-pages@v4.0.0 46 | if: github.ref == 'refs/heads/main' 47 | with: 48 | github_token: ${{ secrets.GITHUB_TOKEN }} 49 | publish_dir: doc/build/html 50 | cname: https:/arm-doe.github.io/pyart/ 51 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | schedule: 5 | # Runs at 09Z (2am CDT) 6 | - cron: "0 9 * * *" 7 | push: 8 | branches: 9 | - main 10 | pull_request: 11 | branches: 12 | - main 13 | 14 | concurrency: 15 | group: ${{ github.workflow }}-${{ github.ref }} 16 | cancel-in-progress: true 17 | 18 | # This job installs dependencies, build the website, and pushes it to `gh-pages` 19 | jobs: 20 | build: 21 | name: ${{ matrix.os }}-${{ matrix.python-version }} 22 | if: github.repository == 'ARM-DOE/pyart' 23 | runs-on: ${{ matrix.os }}-latest 24 | defaults: 25 | run: 26 | shell: bash -l {0} 27 | strategy: 28 | fail-fast: false 29 | matrix: 30 | python-version: ["3.11", "3.12", "3.13"] 31 | os: [macos, ubuntu, windows] 32 | 33 | steps: 34 | - uses: actions/checkout@v4 35 | 36 | # Install dependencies 37 | - name: Setup Conda Environment 38 | uses: mamba-org/setup-micromamba@v2.0.5 39 | with: 40 | environment-file: continuous_integration/environment-ci.yml 41 | init-shell: >- 42 | bash 43 | cache-downloads: true 44 | post-cleanup: "all" 45 | create-args: python=${{ matrix.python-version }} 46 | 47 | - name: Fetch all history for all tags and branches 48 | run: | 49 | git fetch --prune --unshallow 50 | 51 | - name: Install PyART 52 | shell: bash -l {0} 53 | run: | 54 | python -m pip install -e . --no-deps --force-reinstall 55 | 56 | - name: Run Linting 57 | shell: bash -l {0} 58 | run: | 59 | ruff check . 60 | 61 | - name: Run Tests 62 | id: run_tests 63 | shell: bash -l {0} 64 | run: | 65 | python -m pytest -v --cov=./ --cov-report=xml 66 | 67 | - name: Upload code coverage to Codecov 68 | uses: codecov/codecov-action@v5.4.3 69 | with: 70 | file: ./coverage.xml 71 | flags: unittests 72 | env_vars: OS,PYTHON 73 | name: codecov-umbrella 74 | fail_ci_if_error: false 75 | -------------------------------------------------------------------------------- /.github/workflows/linting.yml: -------------------------------------------------------------------------------- 1 | name: linting 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | 8 | jobs: 9 | pre-job: 10 | runs-on: ubuntu-latest 11 | outputs: 12 | should_skip: ${{ steps.skip_check.outputs.should_skip }} 13 | steps: 14 | - id: skip_check 15 | uses: fkirc/skip-duplicate-actions@master 16 | with: 17 | concurrent_skipping: 'same_content' 18 | skip_after_successful_duplicate: 'false' 19 | do_not_skip: '["workflow_dispatch", "schedule"]' 20 | linting: 21 | needs: pre-job 22 | runs-on: ubuntu-latest 23 | if: ${{ needs.pre-job.outputs.should_skip != 'true' }} 24 | steps: 25 | - name: Cancel Previous Runs 26 | uses: styfle/cancel-workflow-action@0.12.1 27 | with: 28 | access_token: ${{ github.token }} 29 | - uses: actions/checkout@v4 30 | - uses: actions/setup-python@v5 31 | with: 32 | python-version: '3.10' 33 | - uses: pre-commit/action@v3.0.1 34 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | # mappings for Jonathan J. Helmus 2 | Jonathan J. Helmus 3 | 4 | # mappings for Scott Collis 5 | Scott Collis 6 | Scott Collis 7 | 8 | # mapping for Joseph C. Hardin 9 | Joseph C. Hardin Joseph Hardin 10 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v4.4.0 4 | hooks: 5 | - id: trailing-whitespace 6 | - id: end-of-file-fixer 7 | - id: check-docstring-first 8 | - id: check-json 9 | - id: check-yaml 10 | - id: debug-statements 11 | - id: mixed-line-ending 12 | 13 | - repo: https://github.com/asottile/pyupgrade 14 | rev: v3.3.1 15 | hooks: 16 | - id: pyupgrade 17 | args: 18 | - '--py38-plus' 19 | 20 | - repo: https://github.com/psf/black 21 | rev: 23.3.0 22 | hooks: 23 | - id: black 24 | - id: black-jupyter 25 | 26 | - repo: https://github.com/charliermarsh/ruff-pre-commit 27 | rev: 'v0.4.9' 28 | hooks: 29 | - id: ruff 30 | args: [ "--fix" ] 31 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, UChicago Argonne, LLC 2 | All rights reserved. 3 | 4 | Copyright 2013 UChicago Argonne, LLC. This software was produced under U.S. 5 | Government contract DE-AC02-06CH11357 for Argonne National Laboratory (ANL), 6 | which is operated by UChicago Argonne, LLC for the U.S. Department of Energy. 7 | The U.S. Government has rights to use, reproduce, and distribute this 8 | software. NEITHER THE GOVERNMENT NOR UCHICAGO ARGONNE, LLC MAKES ANY 9 | WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY LIABILITY FOR THE USE OF THIS 10 | SOFTWARE. If software is modified to produce derivative works, such modified 11 | software should be clearly marked, so as not to confuse it with the version 12 | available from ANL. 13 | 14 | Additionally, redistribution and use in source and binary forms, with or 15 | without modification, are permitted provided that the following conditions 16 | are met: 17 | 18 | * Redistributions of source code must retain the above copyright 19 | notice, this list of conditions and the following disclaimer. 20 | 21 | * Redistributions in binary form must reproduce the above copyright 22 | notice, this list of conditions and the following disclaimer in the 23 | documentation and/or other materials provided with the distribution. 24 | 25 | * Neither the name of UChicago Argonne, LLC, Argonne National 26 | Laboratory, ANL, the U.S. Government, nor the names of its 27 | contributors may be used to endorse or promote products derived 28 | from this software without specific prior written permission. 29 | 30 | THIS SOFTWARE IS PROVIDED BY UCHICAGO ARGONNE, LLC AND CONTRIBUTORS "AS IS" 31 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 32 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 33 | DISCLAIMED. IN NO EVENT SHALL UCHICAGO ARGONNE, LLC OR CONTRIBUTORS BE LIABLE 34 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 35 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 36 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 37 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 38 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 39 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 40 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.rst 2 | recursive-include doc * 3 | recursive-include examples *.py *.txt 4 | recursive-include pyart *.c *.h *.pyx *.pxd 5 | include requirements.txt 6 | include README.rst 7 | include LICENSE.txt 8 | include INSTALL.rst 9 | include pyart/testing/registry.txt 10 | include pyart/__check_build/README 11 | include pyart/testing/data/* 12 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | require_ci_to_pass: no 3 | max_report_age: off 4 | 5 | comment: false 6 | 7 | ignore: 8 | - 'tests/*.py' 9 | - 'setup.py' 10 | - '*.pyx' 11 | - 'versioneer.py' 12 | 13 | coverage: 14 | precision: 2 15 | round: down 16 | status: 17 | project: 18 | default: 19 | target: 95 20 | informational: true 21 | patch: off 22 | changes: off 23 | -------------------------------------------------------------------------------- /continuous_integration/environment-ci.yml: -------------------------------------------------------------------------------- 1 | name: pyart-dev 2 | channels: 3 | - conda-forge 4 | dependencies: 5 | - numpy 6 | - scipy 7 | - matplotlib 8 | - gdal 9 | - netcdf4 10 | - pandas 11 | - pytest 12 | - wradlib 13 | - cartopy 14 | - cvxopt 15 | - xarray>=2024.10.0 16 | - metpy 17 | - pytest-cov 18 | - pytest-mpl 19 | - coveralls 20 | - libnetcdf 21 | - flake8 22 | - fsspec 23 | - glpk 24 | - cftime 25 | - setuptools 26 | - shapely 27 | - mda-xdrlib 28 | - xradar>=0.8.0 29 | - pooch 30 | - versioneer 31 | - black 32 | - open-radar-data 33 | - ruff 34 | - cmweather 35 | - s3fs 36 | -------------------------------------------------------------------------------- /doc/environment.yml: -------------------------------------------------------------------------------- 1 | name: pyart-docs 2 | channels: 3 | - conda-forge 4 | dependencies: 5 | - gdal 6 | - numpy 7 | - scipy 8 | - matplotlib 9 | - pandas 10 | - netcdf4 11 | - pytest 12 | - wradlib 13 | - metpy 14 | - cartopy 15 | - cvxopt 16 | - xarray>=2024.10.0 17 | - sphinx<7.2 18 | - ipython 19 | - pandoc 20 | - pkg-config 21 | - Cython 22 | - fsspec 23 | - s3fs 24 | - gcsfs>=2025.3.2 25 | - glpk 26 | - cftime 27 | - shapely 28 | - open-radar-data 29 | - myst-nb 30 | - pydata-sphinx-theme 31 | - sphinx-gallery 32 | - sphinx-design 33 | - sphinx-copybutton 34 | - nbsphinx 35 | - pre_commit 36 | - cmweather 37 | - mda-xdrlib 38 | - xradar>=0.8.0 39 | - dask 40 | - ablog 41 | - pip 42 | - pip: 43 | - pooch 44 | - versioneer 45 | -------------------------------------------------------------------------------- /doc/rebuild_docs.sh: -------------------------------------------------------------------------------- 1 | # script to rebuild documentation after removing intermediates 2 | rm -r build 3 | rm source/API/generated/* 4 | make html 5 | -------------------------------------------------------------------------------- /doc/rebuild_examples.sh: -------------------------------------------------------------------------------- 1 | # script to rebuild Py-ART example after removing intermediates 2 | rm -r build 3 | rm -r source/source/auto_examples/* 4 | BUILD_PYART_EXAMPLES=1 make html 5 | -------------------------------------------------------------------------------- /doc/rebuild_full_docs.sh: -------------------------------------------------------------------------------- 1 | # script to rebuild complete documentation include examples after removing 2 | # intermediates 3 | rm -r build 4 | rm source/API/generated/* 5 | rm -r source/source/auto_examples/* 6 | BUILD_PYART_EXAMPLES=1 make html 7 | -------------------------------------------------------------------------------- /doc/source/API/index.rst: -------------------------------------------------------------------------------- 1 | .. _API: 2 | 3 | #################### 4 | API Reference Manual 5 | #################### 6 | 7 | :Release: |version| 8 | :Date: |today| 9 | 10 | This guide provides documentation for all modules, function, methods, 11 | and classes within Py-ART for those in the public API. 12 | 13 | Documentation is broken down by directory and module. 14 | 15 | .. currentmodule:: pyart 16 | 17 | .. autosummary:: 18 | :toctree: generated/ 19 | 20 | core 21 | io 22 | aux_io 23 | config 24 | correct 25 | exceptions 26 | retrieve 27 | graph 28 | filters 29 | lazydict 30 | map 31 | util 32 | bridge 33 | testing 34 | _debug_info 35 | -------------------------------------------------------------------------------- /doc/source/_static/doc_shared.js: -------------------------------------------------------------------------------- 1 | const project = "PyART"; 2 | 3 | // Borrowed from Bokeh docs to look for a banner.html at the base of the docs repo and add that 4 | // to the banner if present. 5 | $(document).ready(function () { 6 | $.get('/' + project + '/banner.html', function (data) { 7 | if (data.length > 0) { 8 | console.log(data); 9 | $('#banner').prepend(data); 10 | } 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /doc/source/_static/ppi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/doc/source/_static/ppi.png -------------------------------------------------------------------------------- /doc/source/_static/pyart-theme.css: -------------------------------------------------------------------------------- 1 | /* Define "ARM Blue" RGB values */ 2 | :root { 3 | --arm-blue-rgb: 18, 65, 117; 4 | } 5 | 6 | /* PyART style heading towards use of Poppins font */ 7 | .header-style, h1, h2, h3, h4, h5, h6 { 8 | font-family: Poppins, sans-serif; 9 | } 10 | 11 | /* ARM header color */ 12 | .bg-header { 13 | background: rgb(var(--arm-blue-rgb)) 14 | } 15 | 16 | .theme-switch-button { 17 | border-color: rgb(var(--arm-blue-rgb)) !important; 18 | } 19 | 20 | .bd-header .navbar-nav>.nav-item>.nav-link, 21 | .bd-header .dropdown-toggle, 22 | 23 | .navbar-nav .dropdown-menu { 24 | background-color: var(--pst-color-background); 25 | } 26 | 27 | /* Increase contrast of links in code snippets */ 28 | div[class^="highlight"] a { 29 | background-color: rgb(var(--arm-blue-rgb), 0.2); 30 | color: var(--pst-color-text-muted); 31 | } 32 | 33 | /* Control the appearance of the version alert banner */ 34 | #banner .alert-version, .alert-news { 35 | margin: 1em; 36 | padding: 0.5em; 37 | font-family: "Work Sans", sans-serif; 38 | font-weight: 600; font-size: 16px; 39 | } 40 | 41 | /* Tweaks to the appearance of the sidebars */ 42 | .bd-sidebar { 43 | flex: 0 0 20%; 44 | border-right: none; 45 | } 46 | 47 | .bd-sidebar-secondary div { 48 | border-left: none; 49 | } 50 | -------------------------------------------------------------------------------- /doc/source/_static/rhi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/doc/source/_static/rhi.png -------------------------------------------------------------------------------- /doc/source/_templates/autosummary/base.rst: -------------------------------------------------------------------------------- 1 | {{ fullname | escape | underline}} 2 | 3 | .. currentmodule:: {{ module }} 4 | 5 | .. auto{{ objtype }}:: {{ objname }} 6 | -------------------------------------------------------------------------------- /doc/source/_templates/autosummary/class.rst: -------------------------------------------------------------------------------- 1 | {% extends "!autosummary/class.rst" %} 2 | 3 | {% block methods %} 4 | {% if methods %} 5 | .. HACK -- the point here is that we don't want this to appear in the output, but the autosummary should still generate the pages. 6 | 7 | .. autosummary:: 8 | :toctree: 9 | 10 | {% for item in all_methods %} 11 | {%- if not item.startswith('_') or item in ['__call__'] %} 12 | ~{{ name }}.{{ item }} 13 | {%- endif -%} 14 | {%- endfor %} 15 | 16 | {% endif %} 17 | {% endblock %} 18 | 19 | {% block attributes %} 20 | {% if attributes %} 21 | .. HACK -- the point here is that we don't want this to appear in the output, but the autosummary should still generate the pages. 22 | 23 | .. autosummary:: 24 | :toctree: 25 | 26 | {% for item in all_attributes %} 27 | {%- if not item.startswith('_') %} 28 | ~{{ name }}.{{ item }} 29 | {%- endif -%} 30 | 31 | {%- endfor %} 32 | {% endif %} 33 | {% endblock %} 34 | -------------------------------------------------------------------------------- /doc/source/_templates/autosummary/module.rst: -------------------------------------------------------------------------------- 1 | {{ fullname | escape | underline }} 2 | 3 | .. rubric:: Description 4 | 5 | .. automodule:: {{ fullname }} 6 | 7 | .. currentmodule:: {{ fullname }} 8 | 9 | {% if classes %} 10 | .. rubric:: Classes 11 | 12 | .. autosummary:: 13 | :toctree: . 14 | {% for class in classes %} 15 | {{ class }} 16 | {% endfor %} 17 | 18 | {% endif %} 19 | 20 | {% if functions %} 21 | .. rubric:: Functions 22 | 23 | .. autosummary:: 24 | :toctree: . 25 | {% for function in functions %} 26 | {{ function }} 27 | {% endfor %} 28 | 29 | {% endif %} 30 | -------------------------------------------------------------------------------- /doc/source/_templates/dev_template.rst: -------------------------------------------------------------------------------- 1 | {% extends "!autosummary/class.rst" %} 2 | {% block methods %} 3 | {% if methods %} 4 | .. HACK -- the point here is that we don't want this to appear in the output, but the autosummary should still generate the pages. 5 | .. autosummary:: 6 | :toctree: 7 | {% for item in all_methods %} 8 | {%- if not item.startswith('_') or item in ['__call__'] %} 9 | {{ name }}.{{ item }} 10 | {%- endif -%} 11 | {%- endfor %} 12 | 13 | | 14 | 15 | **Private methods** 16 | 17 | .. autosummary:: 18 | :toctree: 19 | 20 | {% for item in all_methods %} 21 | {%- if item.startswith('_') %} 22 | ~{{ name }}.{{ item }} 23 | {%- endif -%} 24 | {%- endfor %} 25 | 26 | {% endif %} 27 | {% endblock %} 28 | 29 | {% block attributes %} 30 | {% if attributes %} 31 | {% endif %} 32 | {% endblock %} 33 | -------------------------------------------------------------------------------- /doc/source/_templates/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "pydata_sphinx_theme/layout.html" %} 2 | 3 | {% block fonts %} 4 | 7 | {% endblock %} 8 | -------------------------------------------------------------------------------- /doc/source/blog.md: -------------------------------------------------------------------------------- 1 | # Blog 2 | This will be replaced 3 | -------------------------------------------------------------------------------- /doc/source/blog_posts/2022/KMKX-map-animation.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/doc/source/blog_posts/2022/KMKX-map-animation.mp4 -------------------------------------------------------------------------------- /doc/source/blog_posts/2022/test.md: -------------------------------------------------------------------------------- 1 | --- 2 | author: Max Grover 3 | date: 2022-03-11 4 | tags: annoucement 5 | --- 6 | 7 | # New Docs 8 | 9 | Hello All! 10 | 11 | Welcome to our new documentation page! This is a **new section** within the docs meant for: 12 | * General updates 13 | * Release overviews 14 | * More narrative-oriented examples 15 | 16 | If you have any feedback on this new site, feel free to let us know using that Github button in the top right corner! 17 | -------------------------------------------------------------------------------- /doc/source/blog_posts/images/PyART_Docs_OriginalRHI_withJetColorscheme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/doc/source/blog_posts/images/PyART_Docs_OriginalRHI_withJetColorscheme.png -------------------------------------------------------------------------------- /doc/source/blog_posts/images/PyART_Docs_Original_PPI_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/doc/source/blog_posts/images/PyART_Docs_Original_PPI_image.png -------------------------------------------------------------------------------- /doc/source/blog_posts/images/TRMM_RSL_webpage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/doc/source/blog_posts/images/TRMM_RSL_webpage.png -------------------------------------------------------------------------------- /doc/source/blog_posts/images/dq-browser-epcape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/doc/source/blog_posts/images/dq-browser-epcape.png -------------------------------------------------------------------------------- /doc/source/blog_posts/images/interactive-kazr-viz.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/doc/source/blog_posts/images/interactive-kazr-viz.gif -------------------------------------------------------------------------------- /doc/source/blog_posts/images/nexrad-level3-radar-query.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/doc/source/blog_posts/images/nexrad-level3-radar-query.png -------------------------------------------------------------------------------- /doc/source/blog_posts/images/nexrad-plotting-issue.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/doc/source/blog_posts/images/nexrad-plotting-issue.jpeg -------------------------------------------------------------------------------- /doc/source/blog_posts/images/pull_request_changes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/doc/source/blog_posts/images/pull_request_changes.png -------------------------------------------------------------------------------- /doc/source/blog_posts/images/pull_request_commitmessage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/doc/source/blog_posts/images/pull_request_commitmessage.png -------------------------------------------------------------------------------- /doc/source/blog_posts/images/pyart_github_clone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/doc/source/blog_posts/images/pyart_github_clone.png -------------------------------------------------------------------------------- /doc/source/dev/CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../../../CONTRIBUTING.rst 2 | -------------------------------------------------------------------------------- /doc/source/dev/ci_setup.rst: -------------------------------------------------------------------------------- 1 | Py-ART Continuous Integration Setup 2 | =================================== 3 | 4 | Py-ART makes use of continuous integration (CI) to run the library's unit tests 5 | against every pull request and change made to the repository. This document 6 | gives a brief explanation of the details of this setup and hints on how to fix 7 | the CI when it breaks. 8 | -------------------------------------------------------------------------------- /doc/source/dev/index.rst: -------------------------------------------------------------------------------- 1 | ================= 2 | Developer's Guide 3 | ================= 4 | 5 | .. toctree:: 6 | :maxdepth: 3 7 | :hidden: 8 | 9 | CONTRIBUTING 10 | ci_setup 11 | how_to_release 12 | 13 | This discusses information relevant to developing Py-ART. 14 | 15 | -------- 16 | Versions 17 | -------- 18 | Py-ART follows `semantic versioning `_ in its version number. This means 19 | that any Py-ART ``1.x`` release will be backwards compatible with an earlier ``1.y`` release. By 20 | "backward compatible", we mean that **correct** code that works on a ``1.y`` version will work 21 | on a future ``1.x`` version. It's always possible for bug fixes to change behavior or make 22 | incorrect code cease to work. Backwards-incompatible changes will only be allowed when changing 23 | to version ``2.0``. Such changes will be proceeded by `FutureWarning` as appropriate. 24 | For a version ``1.x.y``, we change ``x`` when we release new features, and ``y`` 25 | when we make a release with only bug fixes. 26 | -------------------------------------------------------------------------------- /doc/source/notebook-gallery.rst: -------------------------------------------------------------------------------- 1 | Notebook Gallery 2 | ================ 3 | 4 | .. toctree:: 5 | :maxdepth: 1 6 | :caption: Notebooks 7 | 8 | notebooks/basic_ingest_using_test_radar_object.ipynb 9 | notebooks/changing_fields_and_saving.ipynb 10 | notebooks/dealiasing_velocity.ipynb 11 | notebooks/mapping_data_to_a_cartesian_grid.ipynb 12 | notebooks/masking_data_with_gatefilters.ipynb 13 | notebooks/the_pyart_radar_object_and_indexing.ipynb 14 | -------------------------------------------------------------------------------- /doc/source/userguide/INSTALL.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../../../INSTALL.rst 2 | -------------------------------------------------------------------------------- /doc/source/userguide/contributors_guide.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../../../CONTRIBUTING.rst 2 | -------------------------------------------------------------------------------- /doc/source/userguide/index.rst: -------------------------------------------------------------------------------- 1 | ========== 2 | User Guide 3 | ========== 4 | 5 | We recommend checking out the `Radar Cookbook `_ to get started! 6 | 7 | We also have a collection of installation, and contribution instructions listed below. 8 | 9 | .. toctree:: 10 | :maxdepth: 1 11 | 12 | pyart_2_0 13 | INSTALL 14 | setting_up_an_environment 15 | contributors_guide 16 | -------------------------------------------------------------------------------- /doc/source/userguide/pyart_2_0.rst: -------------------------------------------------------------------------------- 1 | ========== 2 | Py-ART 2.0 3 | ========== 4 | 5 | In preparation for version 2.0.0 of Py-ART, codes were standardized for consistency purposes as further defined in the `Contributor's Guide `_. These changes will break some users code as the API has changed. This guide will detail the changes for each module. 6 | 7 | How to Try Py-ART 2.0 8 | ===================== 9 | 10 | The Py-ART 2.0 release candidate can be installed directly from github - this is still a work in progress, feedback is welcome!:: 11 | 12 | pip install git+https://github.com/ARM-DOE/pyart@release/2.0 13 | 14 | Input/Output (IO) 15 | ================= 16 | We now offer the option to use xradar for IO, with the following interface (a typical gridding workflow is shown below): 17 | 18 | .. code-block:: python 19 | 20 | import xradar as xd 21 | import pyart 22 | 23 | # Access sample cfradial1 data from Py-ART and read using xradar 24 | filename = get_test_data("swx_20120520_0641.nc") 25 | tree = xd.io.open_cfradial1_datatree(filename) 26 | 27 | # Add the associated pyart methods - ensuring compatibility with Py-ART functionality 28 | radar = tree.pyart.to_radar() 29 | 30 | # Grid using 11 vertical levels, and 101 horizontal grid cells at a resolution on 1 km 31 | grid = pyart.map.grid_from_radars( 32 | (radar,), 33 | grid_shape=(11, 101, 101), 34 | grid_limits=( 35 | (0.0, 10_000), 36 | (-50_000.0, 50_000.0), 37 | (-50_000, 50_000.0), 38 | ), 39 | ) 40 | 41 | Correct 42 | ======= 43 | The `dealias_fourdd `_ algorithm has been removed given the now unsupported RSL library. 44 | 45 | It is recommended that users move to the `region-based dealiasing algorithm `_. 46 | 47 | Graph 48 | ===== 49 | Colormaps have been moved to a dedicated package outside Py-ART, `cmweather `_. 50 | 51 | For example, visualizing our grid mentioned previously, it is recommended to install/import cmweather and change the colormap name from `pyart_ChaseSpectral` to `ChaseSpectral` 52 | 53 | .. code-block:: python 54 | 55 | import cmweather 56 | 57 | display = pyart.graph.GridMapDisplay(grid) 58 | display.plot_grid( 59 | "reflectivity_horizontal", level=0, vmin=-20, vmax=60, cmap="ChaseSpectral" 60 | ) 61 | -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | # Basic environment for Py-ART. 2 | name: pyart_env 3 | channels: 4 | - conda-forge 5 | - defaults 6 | dependencies: 7 | - python=3.13 8 | - numpy 9 | - scipy 10 | - matplotlib 11 | - netcdf4 12 | - wradlib 13 | - cartopy 14 | - arm_pyart 15 | - xarray 16 | - xradar 17 | - mda-xdrlib 18 | - pip 19 | - cmweather 20 | - pip: 21 | - wheel 22 | - watchdog 23 | - flake8 24 | - tox 25 | - coverage 26 | - Sphinx 27 | - twine 28 | - pytest 29 | - black 30 | - isort 31 | - pre_commit 32 | -------------------------------------------------------------------------------- /examples/README.txt: -------------------------------------------------------------------------------- 1 | Example Gallery 2 | =============== 3 | 4 | The files used in these examples are available for download_. 5 | 6 | .. _download: https://adc.arm.gov/pyart/example_data/ 7 | -------------------------------------------------------------------------------- /examples/correct/README.txt: -------------------------------------------------------------------------------- 1 | .. _moment_correct_examples: 2 | 3 | Moment correction examples 4 | -------------------------- 5 | 6 | Performing radar moment corrections in antenna (radial) coordinates. 7 | -------------------------------------------------------------------------------- /examples/correct/plot_attenuation.py: -------------------------------------------------------------------------------- 1 | """ 2 | ================================ 3 | Correct reflectivity attenuation 4 | ================================ 5 | 6 | In this example the reflectivity attenuation is calculated and then corrected 7 | for a polarimetric radar using a Z-PHI method implemented in Py-ART. 8 | 9 | """ 10 | 11 | print(__doc__) 12 | 13 | # Author: Jonathan J. Helmus (jhelmus@anl.gov) 14 | # License: BSD 3 clause 15 | 16 | import matplotlib.pyplot as plt 17 | import xradar as xd 18 | 19 | import pyart 20 | 21 | file = pyart.testing.get_test_data("sgpcsaprsurcmacI7.c0.20110520.095101.nc") 22 | 23 | # read in the data 24 | tree = xd.io.open_cfradial1_datatree(file) 25 | radar = tree.pyart.to_radar() 26 | 27 | # remove existing corrections 28 | radar.fields.pop("specific_attenuation") 29 | radar.fields.pop("corrected_reflectivity_horizontal") 30 | 31 | # perform attenuation correction 32 | spec_at, cor_z = pyart.correct.calculate_attenuation( 33 | radar, 34 | 0, 35 | refl_field="reflectivity_horizontal", 36 | ncp_field="norm_coherent_power", 37 | rhv_field="copol_coeff", 38 | phidp_field="proc_dp_phase_shift", 39 | ) 40 | radar.add_field("specific_attenuation", spec_at) 41 | radar.add_field("corrected_reflectivity_horizontal", cor_z) 42 | 43 | # create the plot 44 | fig = plt.figure(figsize=(15, 5)) 45 | ax1 = fig.add_subplot(131) 46 | display = pyart.graph.RadarDisplay(radar) 47 | display.plot( 48 | "reflectivity_horizontal", 49 | 0, 50 | ax=ax1, 51 | vmin=0, 52 | vmax=60.0, 53 | colorbar_label="", 54 | title="Raw Reflectivity", 55 | ) 56 | 57 | ax2 = fig.add_subplot(132) 58 | display.plot( 59 | "specific_attenuation", 60 | 0, 61 | vmin=0, 62 | vmax=1.0, 63 | colorbar_label="", 64 | ax=ax2, 65 | title="Specific Attenuation", 66 | ) 67 | 68 | ax3 = fig.add_subplot(133) 69 | display = pyart.graph.RadarDisplay(radar) 70 | display.plot( 71 | "corrected_reflectivity_horizontal", 72 | 0, 73 | vmin=0, 74 | vmax=60.0, 75 | colorbar_label="", 76 | ax=ax3, 77 | title="Corrected Reflectivity", 78 | ) 79 | 80 | plt.suptitle("Attenuation correction using Py-ART", fontsize=16) 81 | plt.show() 82 | -------------------------------------------------------------------------------- /examples/correct/plot_cloud_mask.py: -------------------------------------------------------------------------------- 1 | """ 2 | ===================================== 3 | Calculating and Plotting a Cloud Mask 4 | ===================================== 5 | 6 | This example shows how to correct and plot reflectivity from an ARM 7 | KAZR using a noise floor cloud mask. 8 | 9 | """ 10 | print(__doc__) 11 | 12 | # Author: Adam Theisen and Zach Sherman 13 | # License: BSD 3 clause 14 | 15 | import matplotlib.pyplot as plt 16 | import numpy as np 17 | from open_radar_data import DATASETS 18 | 19 | import pyart 20 | 21 | ############################ 22 | # **Read and plot raw data** 23 | # 24 | # First let's read and plot our dataset without any mask. 25 | 26 | # Fetch and read in the ARM KAZR file. 27 | filename = DATASETS.fetch("sgpkazrgeC1.a1.20190529.000002.cdf") 28 | radar = pyart.aux_io.read_kazr(filename) 29 | 30 | # Let's now take a look at reflectivity data prior to any corrections. 31 | display = pyart.graph.RadarDisplay(radar) 32 | display.plot("reflectivity_copol") 33 | display.set_limits(xlim=(0, 55)) 34 | plt.show() 35 | 36 | ################################################# 37 | # **Calculate cloud mask and plot corrected data** 38 | # 39 | # Now lets apply a mask by using the calc_cloud_mask function 40 | # that will use a noise floor calculation from range and more 41 | # to calculate the mask. 42 | 43 | # First lets correct the data by calculating the mask. 44 | cloud_mask_radar = pyart.correct.calc_cloud_mask(radar, "reflectivity_copol", "range") 45 | 46 | # In this new radar object we should now have a new cloud mask field. 47 | print(cloud_mask_radar.fields["cloud_mask_2"]) 48 | 49 | # Next we'll create a copy of the reflectivity field so we are not 50 | # overwriting the original data. 51 | cloud_mask_radar.add_field_like( 52 | "reflectivity_copol", 53 | "reflectivity_cloud_mask", 54 | cloud_mask_radar.fields["reflectivity_copol"]["data"].copy(), 55 | ) 56 | 57 | # Now let's apply the mask to the copied reflectivity data. 58 | cloud_mask_radar.fields["reflectivity_cloud_mask"]["data"][ 59 | cloud_mask_radar.fields["cloud_mask_2"]["data"] == 0 60 | ] = np.nan 61 | 62 | # And now we can plot the masked reflectivity field. 63 | display = pyart.graph.RadarDisplay(cloud_mask_radar) 64 | display.plot("reflectivity_cloud_mask") 65 | display.set_limits(xlim=(0, 55)) 66 | plt.show() 67 | -------------------------------------------------------------------------------- /examples/correct/plot_dealias.py: -------------------------------------------------------------------------------- 1 | """ 2 | =========================================================== 3 | Dealias doppler velocities using the Region Based Algorithm 4 | =========================================================== 5 | 6 | In this example doppler velocities are dealiased using the ial condition of the dealiasing, 7 | using the region-based dealiasing algorithm in Py-ART. 8 | """ 9 | 10 | print(__doc__) 11 | 12 | # Author: Jonathan J. Helmus (jhelmus@anl.gov) 13 | # License: BSD 3 clause 14 | 15 | import matplotlib.pyplot as plt 16 | 17 | import pyart 18 | from pyart.testing import get_test_data 19 | 20 | radar_file = get_test_data("095636.mdv") 21 | sonde_file = get_test_data("sgpinterpolatedsondeC1.c1.20110510.000000.cdf") 22 | 23 | # read in the data 24 | radar = pyart.io.read_mdv(radar_file) 25 | 26 | # read in sonde data 27 | dt, profile = pyart.io.read_arm_sonde_vap(sonde_file, radar=radar) 28 | 29 | # create a gate filter which specifies gates to exclude from dealiasing 30 | gatefilter = pyart.filters.GateFilter(radar) 31 | gatefilter.exclude_transition() 32 | gatefilter.exclude_invalid("velocity") 33 | gatefilter.exclude_invalid("reflectivity") 34 | gatefilter.exclude_outside("reflectivity", 0, 80) 35 | 36 | # perform dealiasing 37 | dealias_data = pyart.correct.dealias_region_based(radar, gatefilter=gatefilter) 38 | radar.add_field("corrected_velocity", dealias_data) 39 | 40 | # create a plot of the first and sixth sweeps 41 | fig = plt.figure(figsize=(15, 10)) 42 | ax1 = fig.add_subplot(221) 43 | display = pyart.graph.RadarDisplay(radar) 44 | display.plot( 45 | "velocity", 46 | 0, 47 | vmin=-16, 48 | vmax=16, 49 | ax=ax1, 50 | colorbar_label="", 51 | title="Raw Doppler Velocity, First Sweep", 52 | ) 53 | 54 | ax2 = fig.add_subplot(222) 55 | display.plot( 56 | "corrected_velocity", 57 | 0, 58 | vmin=-40, 59 | vmax=40, 60 | colorbar_label="", 61 | ax=ax2, 62 | title="Corrected Doppler Velocity, First Sweep", 63 | ) 64 | 65 | ax3 = fig.add_subplot(223) 66 | display = pyart.graph.RadarDisplay(radar) 67 | display.plot( 68 | "velocity", 69 | 5, 70 | vmin=-16, 71 | vmax=16, 72 | colorbar_label="", 73 | ax=ax3, 74 | title="Raw Doppler Velocity, Sixth Sweep", 75 | ) 76 | 77 | ax4 = fig.add_subplot(224) 78 | display.plot_ppi( 79 | "corrected_velocity", 80 | 5, 81 | vmin=-40, 82 | vmax=40, 83 | colorbar_label="", 84 | ax=ax4, 85 | title="Corrected Doppler Velocity, Sixth Sweep", 86 | ) 87 | plt.suptitle("Velocity dealiasing using Py-ART", fontsize=16) 88 | plt.show() 89 | -------------------------------------------------------------------------------- /examples/correct/plot_zdr_check.py: -------------------------------------------------------------------------------- 1 | """ 2 | ZDR Bias Calculation 3 | --------------------- 4 | 5 | This example shows how to calculate the ZDR bias from VPT/Birdbath scans. 6 | The technique here uses a vertically pointing scan in regions of light rain. 7 | In these regions, raindrops should be approximately spherical and therefore their 8 | ZDR near zero. Therefore, we want the average ZDR in these regions. 9 | This code applies reflectivity and cross correlation ratio-based threshold to the ZDR 10 | bias calculation to ensure that we are taking the average ZDR in light rain. 11 | 12 | """ 13 | 14 | import matplotlib.pyplot as plt 15 | import xradar as xd 16 | from open_radar_data import DATASETS 17 | 18 | import pyart 19 | 20 | # Read in example data 21 | filename = DATASETS.fetch("sgpxsaprcfrvptI4.a1.20200205.100827.nc") 22 | 23 | # Read in the data 24 | tree = xd.io.open_cfradial1_datatree(filename) 25 | radar = tree.pyart.to_radar() 26 | 27 | # Set up a typical filter for ZDR bias calculation in birdbath scan 28 | # Light rain and RhoHV near 1 ensures that raindrops are close to spherical 29 | # Therefore ZDR should be zero in these regions 30 | gatefilter = pyart.filters.GateFilter(radar) 31 | gatefilter.exclude_below("cross_correlation_ratio_hv", 0.995) 32 | gatefilter.exclude_above("cross_correlation_ratio_hv", 1) 33 | gatefilter.exclude_below("reflectivity", 10) 34 | gatefilter.exclude_above("reflectivity", 30) 35 | 36 | results = pyart.correct.calc_zdr_offset( 37 | radar, 38 | zdr_var="differential_reflectivity", 39 | gatefilter=gatefilter, 40 | height_range=(1000, 3000), 41 | ) 42 | 43 | print("Zdr Bias: " + "{:.2f}".format(results["bias"])) 44 | 45 | fig, ax = plt.subplots(1, 3, figsize=(8, 5)) 46 | ax[0].plot(results["profile_zdr"], results["range"]) 47 | ax[0].set_ylabel("Range (m)") 48 | ax[0].set_xlabel("Zdr (dB)") 49 | ax[1].plot(results["profile_reflectivity"], results["range"]) 50 | ax[1].set_xlabel("Zh (dBZ)") 51 | ax[2].plot(results["profile_cross_correlation_ratio_hv"], results["range"]) 52 | ax[2].set_xlabel("RhoHV ()") 53 | fig.tight_layout() 54 | plt.show() 55 | -------------------------------------------------------------------------------- /examples/io/README.txt: -------------------------------------------------------------------------------- 1 | .. _io: 2 | 3 | Input/Output Examples 4 | -------------------------- 5 | 6 | Reading/writing a variety of radar data using Py-ART. 7 | -------------------------------------------------------------------------------- /examples/io/plot_nexrad_data_google_cloud.py: -------------------------------------------------------------------------------- 1 | """ 2 | ========================================= 3 | Reading NEXRAD Data from Google Cloud 4 | ========================================= 5 | 6 | Within this example, we show how you can remotely access Next Generation Weather Radar (NEXRAD) Data from Google Cloud Storage 7 | and plot quick looks of the datasets. 8 | 9 | """ 10 | 11 | print(__doc__) 12 | 13 | # Author: Zach Sherman 14 | # License: BSD 3 clause 15 | 16 | import tarfile 17 | 18 | # This example requires installation of the gcsfs library 19 | import gcsfs 20 | import matplotlib.pyplot as plt 21 | 22 | import pyart 23 | 24 | ###################################### 25 | # Read NEXRAD Level 2 Data 26 | # ------------------------ 27 | # 28 | # Let's start first with NEXRAD Level 2 data, which is ground-based radar data collected 29 | # by the National Oceanic and Atmospheric Administration (NOAA), as a part of the National Weather Service 30 | # ### Configure our Filepath for NEXRAD Level 2 Data 31 | # We will access data from Google cloud storage, with the data organized as: 32 | # 33 | # ``gcp-public-data-nexrad-l2/year/month/day/radarsite/NWS_NEXRAD_NXL2DPBL_{radarsite}_{year}{month}{date}{hour}0000_{year}{month}{date}{hour}{minute}.tar`` 34 | # 35 | # Where in our case, we are using a sample data file from Miami, Florida (KAMX) 36 | # on October 7, 2016, at 0401:25 UTC. This means our path would look like: 37 | 38 | gcs_path = "gcp-public-data-nexrad-l2/2016/10/07/KAMX/NWS_NEXRAD_NXL2DPBL_KAMX_20161007040000_20161007045959.tar" 39 | 40 | ####################################### 41 | # First we can create a file system and retrieve the tar file to our current directory. 42 | fs = gcsfs.GCSFileSystem() 43 | fs.get(gcs_path, ".") 44 | 45 | ####################################### 46 | # The we can use the tarfile module to extract the files within. 47 | 48 | file = tarfile.open("NWS_NEXRAD_NXL2DPBL_KAMX_20161007040000_20161007045959.tar") 49 | members = file.getmembers() 50 | 51 | # This case we are getting only the first file. 52 | file.extract(members[0].name, ".", filter="data") 53 | file.close() 54 | 55 | ####################################### 56 | # We can use the **pyart.io.read_nexrad_archive** module to access our data, passing in the member filepath. 57 | 58 | radar = pyart.io.read_nexrad_archive(members[0].name) 59 | 60 | ####################################### 61 | # Let's take a look at a summary of what fields are available. 62 | 63 | print(list(radar.fields)) 64 | 65 | ####################################### 66 | # Let's plot the reflectivity field as a first step to investigating our dataset. 67 | 68 | fig = plt.figure(figsize=(12, 4)) 69 | display = pyart.graph.RadarMapDisplay(radar) 70 | 71 | display.plot_ppi_map("reflectivity") 72 | plt.show() 73 | -------------------------------------------------------------------------------- /examples/io/plot_read_cfradial2.py: -------------------------------------------------------------------------------- 1 | """ 2 | ========================================================== 3 | Read and Plot Cfradial2/FM301 data Using Xradar and Py-ART 4 | ========================================================== 5 | 6 | An example which uses xradar and Py-ART to read and plot Cfradial2/FM301 data. 7 | 8 | """ 9 | 10 | # Author: Max Grover (mgrover@anl.gov) 11 | # License: BSD 3 clause 12 | 13 | 14 | import xarray as xr 15 | from open_radar_data import DATASETS 16 | 17 | import pyart 18 | 19 | # Locate the test data and read in using xradar 20 | filename = DATASETS.fetch("cfrad2.20080604_002217_000_SPOL_v36_SUR.nc") 21 | tree = xr.open_datatree(filename) 22 | 23 | # Give the tree Py-ART radar methods 24 | radar = tree.pyart.to_radar() 25 | 26 | # Plot the Reflectivity Field (corrected_reflectivity_horizontal) 27 | display = pyart.graph.RadarMapDisplay(radar) 28 | display.plot_ppi("DBZ", cmap="ChaseSpectral", vmin=-20, vmax=70) 29 | -------------------------------------------------------------------------------- /examples/mapping/README.txt: -------------------------------------------------------------------------------- 1 | .. _mapping_examples: 2 | 3 | Mapping examples 4 | ---------------- 5 | 6 | Mapping one or multiple radars from antenna coordinates to a Cartesian grid. 7 | -------------------------------------------------------------------------------- /examples/mapping/plot_map_one_radar_to_grid.py: -------------------------------------------------------------------------------- 1 | """ 2 | ====================================== 3 | Map a single radar to a Cartesian grid 4 | ====================================== 5 | 6 | Map the reflectivity field of a single radar from Antenna coordinates to a 7 | Cartesian grid. 8 | 9 | """ 10 | 11 | print(__doc__) 12 | 13 | # Author: Jonathan J. Helmus (jhelmus@anl.gov) 14 | # License: BSD 3 clause 15 | 16 | import matplotlib.pyplot as plt 17 | import numpy as np 18 | 19 | import pyart 20 | from pyart.testing import get_test_data 21 | 22 | # read in the data 23 | file = get_test_data("110635.mdv") 24 | radar = pyart.io.read_mdv(file) 25 | 26 | # mask out last 10 gates of each ray, this removes the "ring" around the radar. 27 | radar.fields["reflectivity"]["data"][:, -10:] = np.ma.masked 28 | 29 | # exclude masked gates from the gridding 30 | gatefilter = pyart.filters.GateFilter(radar) 31 | gatefilter.exclude_transition() 32 | gatefilter.exclude_masked("reflectivity") 33 | 34 | # perform Cartesian mapping, limit to the reflectivity field. 35 | grid = pyart.map.grid_from_radars( 36 | (radar,), 37 | gatefilters=(gatefilter,), 38 | grid_shape=(1, 241, 241), 39 | grid_limits=((2000, 2000), (-123000.0, 123000.0), (-123000.0, 123000.0)), 40 | fields=["reflectivity"], 41 | ) 42 | 43 | # create the plot 44 | fig = plt.figure() 45 | ax = fig.add_subplot(111) 46 | ax.imshow(grid.fields["reflectivity"]["data"][0], origin="lower") 47 | plt.show() 48 | -------------------------------------------------------------------------------- /examples/mapping/plot_map_two_radars_to_grid.py: -------------------------------------------------------------------------------- 1 | """ 2 | ================================== 3 | Map two radars to a Cartesian grid 4 | ================================== 5 | 6 | Map the reflectivity field of two nearby ARM XSARP radars from antenna 7 | coordinates to a Cartesian grid. 8 | 9 | """ 10 | 11 | print(__doc__) 12 | 13 | # Author: Jonathan J. Helmus (jhelmus@anl.gov) 14 | # License: BSD 3 clause 15 | 16 | import matplotlib.pyplot as plt 17 | 18 | import pyart 19 | from pyart.testing import get_test_data 20 | 21 | # read in the data from both XSAPR radars 22 | xsapr_sw_file = get_test_data("swx_20120520_0641.nc") 23 | xsapr_se_file = get_test_data("sex_20120520_0641.nc") 24 | radar_sw = pyart.io.read_cfradial(xsapr_sw_file) 25 | radar_se = pyart.io.read_cfradial(xsapr_se_file) 26 | 27 | # filter out gates with reflectivity > 100 from both radars 28 | gatefilter_se = pyart.filters.GateFilter(radar_se) 29 | gatefilter_se.exclude_transition() 30 | gatefilter_se.exclude_above("corrected_reflectivity_horizontal", 100) 31 | gatefilter_sw = pyart.filters.GateFilter(radar_sw) 32 | gatefilter_sw.exclude_transition() 33 | gatefilter_sw.exclude_above("corrected_reflectivity_horizontal", 100) 34 | 35 | # perform Cartesian mapping, limit to the reflectivity field. 36 | grid = pyart.map.grid_from_radars( 37 | (radar_se, radar_sw), 38 | gatefilters=(gatefilter_se, gatefilter_sw), 39 | grid_shape=(1, 201, 201), 40 | grid_limits=((1000, 1000), (-50000, 40000), (-60000, 40000)), 41 | grid_origin=(36.57861, -97.363611), 42 | fields=["corrected_reflectivity_horizontal"], 43 | ) 44 | 45 | # create the plot 46 | fig = plt.figure() 47 | ax = fig.add_subplot(111) 48 | ax.imshow( 49 | grid.fields["corrected_reflectivity_horizontal"]["data"][0], 50 | origin="lower", 51 | extent=(-60, 40, -50, 40), 52 | vmin=0, 53 | vmax=48, 54 | ) 55 | plt.show() 56 | -------------------------------------------------------------------------------- /examples/plotting/README.txt: -------------------------------------------------------------------------------- 1 | .. _plotting_examples: 2 | 3 | Plotting examples 4 | ----------------- 5 | 6 | Plotting real world radar data with Py-ART. 7 | -------------------------------------------------------------------------------- /examples/plotting/plot_cappi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ==================== 3 | Compare PPI vs CAPPI 4 | ==================== 5 | 6 | This example demonstrates how to create and compare PPI (Plan Position Indicator) 7 | and CAPPI (Constant Altitude Plan Position Indicator) plots using radar data. 8 | 9 | In this example, we load sample radar data, create a CAPPI at 2,000 meters 10 | for the 'reflectivity' field, and then plot both the PPI and CAPPI for comparison. 11 | 12 | """ 13 | 14 | print(__doc__) 15 | 16 | # Author: Hamid Ali Syed (syed44@purdue.edu) 17 | # License: BSD 3 clause 18 | 19 | import matplotlib.pyplot as plt 20 | from open_radar_data import DATASETS 21 | 22 | import pyart 23 | 24 | # Load the sample radar data 25 | file = DATASETS.fetch("RAW_NA_000_125_20080411190016") 26 | radar = pyart.io.read(file) 27 | 28 | # Apply gate filtering to exclude unwanted data 29 | gatefilter = pyart.filters.GateFilter(radar) 30 | gatefilter.exclude_transition() 31 | 32 | # Create CAPPI at 2,000 meters for the 'reflectivity' field 33 | cappi = pyart.retrieve.create_cappi( 34 | radar, fields=["reflectivity"], height=2000, gatefilter=gatefilter 35 | ) 36 | 37 | # Create RadarMapDisplay objects for both PPI and CAPPI 38 | radar_display = pyart.graph.RadarMapDisplay(radar) 39 | cappi_display = pyart.graph.RadarMapDisplay(cappi) 40 | 41 | # Plotting the PPI and CAPPI for comparison 42 | fig, ax = plt.subplots(1, 2, figsize=(13, 5)) 43 | 44 | # Plot PPI for 'reflectivity' field 45 | radar_display.plot_ppi("reflectivity", vmin=-10, vmax=60, ax=ax[0]) 46 | ax[0].set_title("PPI Reflectivity") 47 | 48 | # Plot CAPPI for 'reflectivity' field 49 | cappi_display.plot_ppi("reflectivity", vmin=-10, vmax=60, ax=ax[1]) 50 | ax[1].set_title("CAPPI Reflectivity at 2000 meters") 51 | 52 | # Show the plots 53 | plt.show() 54 | -------------------------------------------------------------------------------- /examples/plotting/plot_corner_reflector.py: -------------------------------------------------------------------------------- 1 | """ 2 | ======================= 3 | Plot a Corner Reflector 4 | ======================= 5 | 6 | This is an example of how to plot a corner reflector. 7 | 8 | """ 9 | 10 | print(__doc__) 11 | 12 | # Author: Max Grover (mgrover@anl.gov) 13 | # License: BSD 3 clause 14 | 15 | from open_radar_data import DATASETS 16 | 17 | import pyart 18 | 19 | filename = DATASETS.fetch("sgpkasacrcrrasterC1.a1.20130419.012153.nc") 20 | 21 | # Read the data 22 | radar = pyart.io.read(filename) 23 | 24 | # Set up the display object 25 | display = pyart.graph.RadarDisplay(radar) 26 | 27 | # Set the corner reflector to be 478 m away, with elevations between -0.5 to 2.5 deg 28 | display.plot_cr_raster(target_range=478.0, el_limits=[-0.5, 2.5], cmap="HomeyerRainbow") 29 | -------------------------------------------------------------------------------- /examples/plotting/plot_cross_section.py: -------------------------------------------------------------------------------- 1 | """ 2 | ================================= 3 | Plot a Cross Section from a Grid 4 | ================================= 5 | 6 | This is an example of how to plot a cross section 7 | of your radar grid using the GridMapDisplay 8 | 9 | """ 10 | 11 | print(__doc__) 12 | 13 | # Author: Max Grover (mgrover@anl.gov) 14 | # License: BSD 3 clause 15 | 16 | import cartopy.crs as ccrs 17 | import matplotlib.pyplot as plt 18 | 19 | import pyart 20 | from pyart.testing import get_test_data 21 | 22 | # Read in the data from two XSAPR radars 23 | xsapr_sw_file = get_test_data("swx_20120520_0641.nc") 24 | xsapr_se_file = get_test_data("sex_20120520_0641.nc") 25 | radar_sw = pyart.io.read_cfradial(xsapr_sw_file) 26 | radar_se = pyart.io.read_cfradial(xsapr_se_file) 27 | 28 | # Filter out gates with reflectivity > 100 from both radars 29 | gatefilter_se = pyart.filters.GateFilter(radar_se) 30 | gatefilter_se.exclude_transition() 31 | gatefilter_se.exclude_above("corrected_reflectivity_horizontal", 100) 32 | gatefilter_sw = pyart.filters.GateFilter(radar_sw) 33 | gatefilter_sw.exclude_transition() 34 | gatefilter_sw.exclude_above("corrected_reflectivity_horizontal", 100) 35 | 36 | # perform Cartesian mapping, limit to the reflectivity field. 37 | grid = pyart.map.grid_from_radars( 38 | (radar_se, radar_sw), 39 | gatefilters=(gatefilter_se, gatefilter_sw), 40 | grid_shape=(20, 181, 181), 41 | grid_limits=((500, 10000), (-50000, 40000), (-60000, 40000)), 42 | grid_origin=(36.57861, -97.363611), 43 | fields=["corrected_reflectivity_horizontal"], 44 | ) 45 | 46 | # Define some start and end points, using (latitude, longitude) 47 | start = (36.7, -97.7) 48 | end = (36.2, -97.8) 49 | 50 | # Setup the figure, and plot our x/y view of the radar 51 | fig = plt.figure(figsize=(18, 6)) 52 | ax1 = plt.subplot(121, projection=ccrs.PlateCarree()) 53 | display = pyart.graph.GridMapDisplay(grid) 54 | display.plot_grid( 55 | "corrected_reflectivity_horizontal", 56 | ax=ax1, 57 | cmap="HomeyerRainbow", 58 | vmin=-20, 59 | vmax=70, 60 | ) 61 | 62 | # Plot our start and end points, as well as a line in between the two 63 | ax1.scatter(start[1], start[0], color="tab:blue", label="Start") 64 | ax1.scatter(end[1], end[0], color="black", label="End") 65 | ax1.plot([start[1], end[1]], [start[0], end[0]], color="k", linestyle=":") 66 | plt.legend(loc="upper right") 67 | 68 | # Add a cross section, using our start and end points, and set our x-axis as latitude (lat) 69 | ax2 = plt.subplot(122) 70 | display.plot_cross_section( 71 | "corrected_reflectivity_horizontal", 72 | start, 73 | end, 74 | x_axis="lat", 75 | cmap="HomeyerRainbow", 76 | vmin=-20, 77 | vmax=70, 78 | ) 79 | -------------------------------------------------------------------------------- /examples/plotting/plot_nexrad_image_muted_reflectivity.py: -------------------------------------------------------------------------------- 1 | """ 2 | ======================================= 3 | Create an image-muted reflectivity plot 4 | ======================================= 5 | An example which creates an image-muted PPI plot from a NEXRAD file. 6 | 7 | Image muting reduces the visual prominence of the reflectivities within identified 8 | melting and mixed precipitation features in winter storms (i.e. regions with low 9 | correlation coefficient values). Reflectivities corresponding to melting and mixed 10 | precipitation features are deemphasized using a gray scale and the regions 11 | with just snow and just rain are depicted in a corresponding full-color scale. 12 | The ultimate utility of image muting radar reflectivity is to reduce the misinterpretation 13 | of regions of melting or mixed precipitation as opposed to heavy snow or heavy rain. 14 | See Tomkins et al. (2022) for full details. 15 | 16 | """ 17 | 18 | print(__doc__) 19 | 20 | # Author: Laura Tomkins (lauramtomkins@gmail.com) 21 | # License: BSD 3 clause 22 | 23 | import matplotlib.colors as mcolors 24 | import matplotlib.pyplot as plt 25 | import numpy as np 26 | 27 | import pyart 28 | 29 | # Read in file 30 | nexrad_file = "s3://noaa-nexrad-level2/2020/02/07/KBGM/KBGM20200207_132642_V06" 31 | radar = pyart.io.read_nexrad_archive(nexrad_file) 32 | 33 | # Mute radar object 34 | # Regions where rhoHV < 0.97 and reflectivity > 20 will be muted 35 | radar = pyart.util.image_mute_radar( 36 | radar, 37 | field="reflectivity", 38 | mute_field="cross_correlation_ratio", 39 | mute_threshold=0.97, 40 | field_threshold=20, 41 | ) 42 | 43 | # adjust colormaps for visual separation 44 | # this example uses perceptually uniform colormaps 45 | magma_cmap = plt.get_cmap("magma_r") 46 | grays_cmap = plt.get_cmap("gray_r") 47 | 48 | nonmuted_cmap = mcolors.LinearSegmentedColormap.from_list( 49 | "nonmuted_cmap", magma_cmap(np.linspace(0, 0.9, magma_cmap.N)) 50 | ) 51 | muted_cmap = mcolors.LinearSegmentedColormap.from_list( 52 | "muted_cmap", grays_cmap(np.linspace(0, 0.7, grays_cmap.N)) 53 | ) 54 | 55 | # create plot using RadarDisplay 56 | display = pyart.graph.RadarDisplay(radar) 57 | 58 | fig = plt.figure() 59 | ax = plt.axes() 60 | display.plot("nonmuted_reflectivity", 0, vmin=5, vmax=45, cmap=nonmuted_cmap) 61 | display.plot("muted_reflectivity", 0, vmin=5, vmax=45, cmap=muted_cmap) 62 | display.set_limits((-300, 300), (-300, 300)) 63 | ax.set_aspect("equal") 64 | plt.show() 65 | 66 | # References 67 | # ---------- 68 | # Tomkins, L. M., Yuter, S. E., Miller, M. A., and Allen, L. R., 2022: 69 | # Image muting of mixed precipitation to improve identification of regions 70 | # of heavy snow in radar data. Atmos. Meas. Tech., 15, 5515–5525, 71 | # https://doi.org/10.5194/amt-15-5515-2022 72 | -------------------------------------------------------------------------------- /examples/plotting/plot_nexrad_multiple_moments.py: -------------------------------------------------------------------------------- 1 | """ 2 | ==================================================== 3 | Create a plot of multiple moments from a NEXRAD file 4 | ==================================================== 5 | 6 | An example which creates a plot containing multiple moments taken from a 7 | NEXRAD Archive file. 8 | 9 | """ 10 | 11 | print(__doc__) 12 | 13 | # Author: Jonathan J. Helmus (jhelmus@anl.gov) 14 | # License: BSD 3 clause 15 | 16 | import matplotlib.pyplot as plt 17 | 18 | import pyart 19 | from pyart.testing import get_test_data 20 | 21 | filename = get_test_data("KATX20130717_195021_V06") 22 | radar = pyart.io.read_nexrad_archive(filename) 23 | display = pyart.graph.RadarDisplay(radar) 24 | fig = plt.figure(figsize=(10, 10)) 25 | 26 | ax = fig.add_subplot(221) 27 | display.plot( 28 | "velocity", 29 | 1, 30 | ax=ax, 31 | title="Doppler Velocity", 32 | colorbar_label="", 33 | vmin=-32.0, 34 | vmax=32.0, 35 | axislabels=("", "North South distance from radar (km)"), 36 | ) 37 | display.set_limits((-300, 300), (-300, 300), ax=ax) 38 | 39 | ax = fig.add_subplot(222) 40 | display.plot( 41 | "differential_reflectivity", 42 | 0, 43 | ax=ax, 44 | title="Differential Reflectivity", 45 | colorbar_label="", 46 | axislabels=("", ""), 47 | ) 48 | display.set_limits((-300, 300), (-300, 300), ax=ax) 49 | 50 | ax = fig.add_subplot(223) 51 | display.plot( 52 | "differential_phase", 0, ax=ax, title="Differential Phase", colorbar_label="" 53 | ) 54 | display.set_limits((-300, 300), (-300, 300), ax=ax) 55 | 56 | ax = fig.add_subplot(224) 57 | display.plot( 58 | "cross_correlation_ratio", 59 | 0, 60 | ax=ax, 61 | title="Correlation Coefficient", 62 | colorbar_label="", 63 | axislabels=("East West distance from radar (km)", ""), 64 | ) 65 | display.set_limits((-300, 300), (-300, 300), ax=ax) 66 | 67 | plt.show() 68 | -------------------------------------------------------------------------------- /examples/plotting/plot_nexrad_reflectivity.py: -------------------------------------------------------------------------------- 1 | """ 2 | ==================================== 3 | Create a plot of NEXRAD reflectivity 4 | ==================================== 5 | 6 | An example which creates a plot containing the first collected scan from a 7 | NEXRAD file. 8 | 9 | """ 10 | 11 | print(__doc__) 12 | 13 | # Author: Jonathan J. Helmus (jhelmus@anl.gov) 14 | # License: BSD 3 clause 15 | 16 | import matplotlib.pyplot as plt 17 | 18 | import pyart 19 | from pyart.testing import get_test_data 20 | 21 | # open the file, create the displays and figure 22 | filename = get_test_data("Level2_KATX_20130717_1950.ar2v") 23 | radar = pyart.io.read_nexrad_archive(filename) 24 | display = pyart.graph.RadarDisplay(radar) 25 | fig = plt.figure(figsize=(6, 5)) 26 | 27 | # plot super resolution reflectivity 28 | ax = fig.add_subplot(111) 29 | display.plot( 30 | "reflectivity", 31 | 0, 32 | title="NEXRAD Reflectivity", 33 | vmin=-32, 34 | vmax=64, 35 | colorbar_label="", 36 | ax=ax, 37 | ) 38 | display.plot_range_ring(radar.range["data"][-1] / 1000.0, ax=ax) 39 | display.set_limits(xlim=(-500, 500), ylim=(-500, 500), ax=ax) 40 | plt.show() 41 | -------------------------------------------------------------------------------- /examples/plotting/plot_ppi_cfradial.py: -------------------------------------------------------------------------------- 1 | """ 2 | ====================================== 3 | Create a PPI plot from a Cfradial file 4 | ====================================== 5 | 6 | An example which creates a PPI plot of a Cfradial file. 7 | 8 | """ 9 | 10 | print(__doc__) 11 | 12 | # Author: Max Grover (mgrover@anl.gov) 13 | # License: BSD 3 clause 14 | 15 | import matplotlib.pyplot as plt 16 | 17 | import pyart 18 | from pyart.testing import get_test_data 19 | 20 | # Locate the test data and read in using main read method 21 | filename = get_test_data("swx_20120520_0641.nc") 22 | radar = pyart.io.read(filename) 23 | 24 | # Setup the display, which automatically detects this is a ppi scan 25 | display = pyart.graph.RadarDisplay(radar) 26 | fig = plt.figure() 27 | ax = fig.add_subplot(111) 28 | display.plot("reflectivity_horizontal", 0, vmin=-32, vmax=64.0) 29 | display.plot_range_rings([10, 20, 30, 40]) 30 | display.plot_cross_hair(5.0) 31 | plt.show() 32 | -------------------------------------------------------------------------------- /examples/plotting/plot_ppi_mdv.py: -------------------------------------------------------------------------------- 1 | """ 2 | ================================= 3 | Create a PPI plot from a MDV file 4 | ================================= 5 | 6 | An example which creates a PPI plot of a MDV file using a RadarDisplay object. 7 | 8 | """ 9 | 10 | print(__doc__) 11 | 12 | # Author: Jonathan J. Helmus (jhelmus@anl.gov) 13 | # License: BSD 3 clause 14 | 15 | import matplotlib.pyplot as plt 16 | 17 | import pyart 18 | from pyart.testing import get_test_data 19 | 20 | filename = get_test_data("110635.mdv") 21 | 22 | # create the plot using RadarDisplay 23 | radar = pyart.io.read_mdv(filename) 24 | display = pyart.graph.RadarDisplay(radar) 25 | fig = plt.figure(figsize=[5, 5]) 26 | ax = fig.add_subplot(111) 27 | display.plot("reflectivity", 0, vmin=-16.0, vmax=64, title="PPI", cmap="HomeyerRainbow") 28 | display.set_limits(ylim=[-150, 150], xlim=[-150, 150]) 29 | plt.show() 30 | -------------------------------------------------------------------------------- /examples/plotting/plot_ppi_with_rings.py: -------------------------------------------------------------------------------- 1 | """ 2 | ================================== 3 | Create a PPI plot on a cartopy map 4 | ================================== 5 | 6 | An example which creates a PPI plot of a file with a cartopy background 7 | and range rings 8 | 9 | """ 10 | 11 | print(__doc__) 12 | 13 | # Author: Jason Hemedinger 14 | # License: BSD 3 clause 15 | 16 | import cartopy.crs as ccrs 17 | import matplotlib.pyplot as plt 18 | import numpy as np 19 | 20 | import pyart 21 | from pyart.testing import get_test_data 22 | 23 | # Read in the file, create a RadarMapDisplay object 24 | filename = get_test_data("nsaxsaprppiC1.a1.20140201.184802.nc") 25 | radar = pyart.io.read(filename) 26 | display = pyart.graph.RadarMapDisplay(radar) 27 | 28 | # Setting projection and ploting the second tilt 29 | projection = ccrs.LambertConformal( 30 | central_latitude=radar.latitude["data"][0], 31 | central_longitude=radar.longitude["data"][0], 32 | ) 33 | 34 | fig = plt.figure(figsize=(6, 6)) 35 | display.plot_ppi_map( 36 | "reflectivity_horizontal", 37 | 1, 38 | vmin=-20, 39 | vmax=20, 40 | min_lon=-157.1, 41 | max_lon=-156, 42 | min_lat=71.2, 43 | max_lat=71.6, 44 | lon_lines=np.arange(-158, -154, 0.2), 45 | resolution="10m", 46 | lat_lines=np.arange(69, 72, 0.1), 47 | projection=projection, 48 | fig=fig, 49 | lat_0=radar.latitude["data"][0], 50 | lon_0=radar.longitude["data"][0], 51 | ) 52 | 53 | # Plot range rings at 10, 20, 30, 40km 54 | display.plot_range_ring(10.0, line_style="k-") 55 | display.plot_range_ring(20.0, line_style="k--") 56 | display.plot_range_ring(30.0, line_style="k-") 57 | display.plot_range_ring(40.0, line_style="k--") 58 | 59 | # Plot cross hairs 60 | display.plot_line_xy( 61 | np.array([-40000.0, 40000.0]), np.array([0.0, 0.0]), line_style="k-" 62 | ) 63 | display.plot_line_xy( 64 | np.array([0.0, 0.0]), np.array([-20000.0, 200000.0]), line_style="k-" 65 | ) 66 | 67 | # Indicate the radar location with a point 68 | display.plot_point(radar.longitude["data"][0], radar.latitude["data"][0]) 69 | 70 | plt.show() 71 | -------------------------------------------------------------------------------- /examples/plotting/plot_rhi_cfradial.py: -------------------------------------------------------------------------------- 1 | """ 2 | ====================================================== 3 | Create a multiple panel RHI plot from a CF/Radial file 4 | ====================================================== 5 | 6 | An example which creates a multiple panel RHI plot of a CF/Radial file using 7 | a RadarDisplay object. 8 | 9 | """ 10 | 11 | print(__doc__) 12 | 13 | # Author: Jonathan J. Helmus (jhelmus@anl.gov) 14 | # License: BSD 3 clause 15 | 16 | import matplotlib.pyplot as plt 17 | import netCDF4 18 | 19 | import pyart 20 | from pyart.testing import get_test_data 21 | 22 | filename = get_test_data("sgpxsaprrhicmacI5.c0.20110524.015604_NC4.nc") 23 | 24 | # create the plot using RadarDisplay 25 | radar = pyart.io.read_cfradial(filename) 26 | radar.metadata["instrument_name"] = "XSARP" 27 | display = pyart.graph.RadarDisplay(radar) 28 | 29 | fig = plt.figure(figsize=[12, 17]) 30 | fig.subplots_adjust(hspace=0.4) 31 | xlabel = "Distance from radar (km)" 32 | ylabel = "Height agl (km)" 33 | colorbar_label = "Hz. Eq. Refl. Fac. (dBZ)" 34 | nplots = radar.nsweeps 35 | 36 | for snum in radar.sweep_number["data"]: 37 | fixed_angle = radar.fixed_angle["data"][snum] 38 | title = f"HSRHI Az={fixed_angle:.3f}" 39 | ax = fig.add_subplot(nplots, 1, snum + 1) 40 | display.plot( 41 | "reflectivity_horizontal", 42 | snum, 43 | vmin=-20, 44 | vmax=20, 45 | mask_outside=False, 46 | title=title, 47 | axislabels=(xlabel, ylabel), 48 | colorbar_label=colorbar_label, 49 | ax=ax, 50 | ) 51 | display.set_limits(ylim=[0, 15], ax=ax) 52 | 53 | time_start = netCDF4.num2date( 54 | radar.time["data"][0], 55 | radar.time["units"], 56 | only_use_cftime_datetimes=False, 57 | only_use_python_datetimes=True, 58 | ) 59 | figure_title = "Time: " + time_start.isoformat() + "Z" 60 | fig.text(0.35, 0.92, figure_title) 61 | 62 | plt.show() 63 | -------------------------------------------------------------------------------- /examples/plotting/plot_rhi_cfradial_singlescan.py: -------------------------------------------------------------------------------- 1 | """ 2 | ====================================================== 3 | Create a multiple panel RHI plot from a CF/Radial file 4 | ====================================================== 5 | 6 | An example which creates a RHI plot of a CF/Radial file using 7 | a RadarDisplay object. 8 | 9 | """ 10 | 11 | print(__doc__) 12 | 13 | import matplotlib.pyplot as plt 14 | 15 | import pyart 16 | from pyart.testing import get_test_data 17 | 18 | filename = get_test_data("sgpxsaprrhicmacI5.c0.20110524.015604_NC4.nc") 19 | 20 | # create the plot using RadarDisplay 21 | radar = pyart.io.read_cfradial(filename) 22 | radar.metadata["instrument_name"] = "XSARP" 23 | display = pyart.graph.RadarDisplay(radar) 24 | 25 | fig = plt.figure(figsize=[15, 5]) 26 | fig.subplots_adjust(hspace=0.4) 27 | xlabel = "Distance from radar (km)" 28 | ylabel = "Distance above radar (km)" 29 | colorbar_label = "Equivalent reflectivity factor (dBZ)" 30 | # nplots = radar.nsweeps 31 | 32 | fixed_angle = radar.fixed_angle["data"][0] 33 | title = f"HSRHI Az={fixed_angle:.3f}" 34 | ax = fig.add_subplot(1, 1, 1) 35 | display.plot( 36 | "reflectivity_horizontal", 37 | 0, 38 | vmin=-20, 39 | vmax=20, 40 | mask_outside=True, 41 | title="RHI", 42 | axislabels=(xlabel, ylabel), 43 | cmap="HomeyerRainbow", 44 | colorbar_label=colorbar_label, 45 | ax=ax, 46 | ) 47 | display.set_limits(ylim=[0, 15], ax=ax) 48 | 49 | plt.show() 50 | -------------------------------------------------------------------------------- /examples/plotting/plot_rhi_data_overlay.py: -------------------------------------------------------------------------------- 1 | """ 2 | ==================================================================== 3 | Create an RHI plot with reflectivity contour lines from an MDV file 4 | ==================================================================== 5 | 6 | An example which creates an RHI plot of velocity using a RadarDisplay object 7 | and adding Reflectivity contours from the same MDV file. 8 | 9 | """ 10 | 11 | print(__doc__) 12 | 13 | # Author: Cory Weber (cweber@anl.gov) 14 | # License: BSD 3 clause 15 | import matplotlib.pyplot as plt 16 | import numpy as np 17 | import scipy.ndimage as spyi 18 | 19 | import pyart 20 | from pyart.testing import get_test_data 21 | 22 | filename = get_test_data("034142.mdv") 23 | 24 | # create the plot using RadarDisplay 25 | sweep = 2 26 | # read file 27 | radar = pyart.io.read_mdv(filename) 28 | display = pyart.graph.RadarDisplay(radar) 29 | fig = plt.figure(figsize=[20, 5]) 30 | ax = fig.add_subplot(111) 31 | 32 | # plot velocity 33 | 34 | display.plot( 35 | "velocity", 36 | sweep=sweep, 37 | vmin=-20, 38 | vmax=20.0, 39 | fig=fig, 40 | ax=ax, 41 | cmap="balance", 42 | colorbar_label="Velocity (m/s)", 43 | ) 44 | 45 | # line commented out to show reflectivity 46 | # display.plot('reflectivity', sweep=sweep, vmin=-0, vmax=45.0, fig=fig,ax=ax) 47 | 48 | # get data 49 | start = radar.get_start(sweep) 50 | end = radar.get_end(sweep) + 1 51 | data = radar.get_field(sweep, "reflectivity") 52 | x, y, z = radar.get_gate_x_y_z(sweep, edges=False) 53 | 54 | x /= 1000.0 55 | y /= 1000.0 56 | z /= 1000.0 57 | 58 | # smooth out the lines 59 | data = spyi.gaussian_filter(data, sigma=1.2) 60 | 61 | # calculate (R)ange 62 | R = np.sqrt(x**2 + y**2) * np.sign(y) 63 | R = -R 64 | display.set_limits(xlim=[25, 0], ylim=[0, 5]) 65 | 66 | # add contours 67 | # creates steps 35 to 100 by 5 68 | levels = np.arange(35, 100, 5) 69 | # adds coutours to plot 70 | contours = ax.contour( 71 | R, z, data, levels, linewidths=1.5, colors="k", linestyles="solid", antialiased=True 72 | ) 73 | 74 | # adds contour labels (fmt= '%r' displays 10.0 vs 10.0000) 75 | plt.clabel(contours, levels, fmt="%r", inline=True, fontsize=10) 76 | 77 | 78 | # format plot 79 | # add grid (dotted lines, major axis only) 80 | ax.grid(color="k", linestyle=":", linewidth=1, which="major") 81 | 82 | # horizontal 83 | ax.axhline(0.9, 0, 1, linestyle="solid", color="k", linewidth=2) 84 | ax.axhline(1.3, 0, 1, linestyle="dashed", color="k", linewidth=2) 85 | 86 | # vertical 87 | ax.axvline(15, 0, 1, linestyle="solid", color="#00b4ff", linewidth=2) 88 | ax.axvline(4.5, 0, 1, linestyle="solid", color="#ff6800", linewidth=2) 89 | 90 | # setting matplotlib overrides display.plot defaults 91 | ax.set_ylabel("Altitude above CP-2 (km)") 92 | 93 | plt.show() 94 | -------------------------------------------------------------------------------- /examples/plotting/plot_rhi_mdv.py: -------------------------------------------------------------------------------- 1 | """ 2 | ================================= 3 | Create a RHI plot from a MDV file 4 | ================================= 5 | 6 | An example which creates a RHI plot of a MDV file using a RadarDisplay object. 7 | 8 | """ 9 | 10 | print(__doc__) 11 | 12 | # Author: Jonathan J. Helmus (jhelmus@anl.gov) 13 | # License: BSD 3 clause 14 | 15 | import matplotlib.pyplot as plt 16 | 17 | import pyart 18 | from pyart.testing import get_test_data 19 | 20 | filename = get_test_data("110041.mdv") 21 | 22 | # create the plot using RadarDisplay 23 | radar = pyart.io.read_mdv(filename) 24 | display = pyart.graph.RadarDisplay(radar) 25 | fig = plt.figure(figsize=[5, 5]) 26 | ax = fig.add_subplot(111) 27 | display.plot("reflectivity", 0, vmin=-16, vmax=64.0) 28 | plt.show() 29 | -------------------------------------------------------------------------------- /examples/plotting/plot_rhi_two_panel.py: -------------------------------------------------------------------------------- 1 | """ 2 | =========================== 3 | Create a two panel RHI plot 4 | =========================== 5 | 6 | An example which creates a two panel RHI plot of a cfradial file. The fields 7 | included in the two panels are reflectivity and doppler velocity. 8 | 9 | """ 10 | 11 | print(__doc__) 12 | 13 | # Author: Max Grover (mgrover@anl.gov) 14 | # License: BSD 3 clause 15 | 16 | import matplotlib.pyplot as plt 17 | import numpy as np 18 | 19 | import pyart 20 | from pyart.testing import get_test_data 21 | 22 | # Read the data and create the display object 23 | filename = get_test_data("sgpxsaprrhicmacI5.c0.20110524.015604_NC4.nc") 24 | radar = pyart.io.read_cfradial(filename) 25 | display = pyart.graph.RadarDisplay(radar) 26 | 27 | # Fields to plot and ranges 28 | fields_to_plot = ["reflectivity_horizontal", "mean_doppler_velocity"] 29 | ranges = [(-20, 20), (-17.0, 17.0)] 30 | cmaps = ["HomeyerRainbow", "balance"] 31 | 32 | # Plot the data 33 | nplots = len(fields_to_plot) 34 | plt.figure(figsize=[5 * nplots, 4]) 35 | 36 | # Plot each field 37 | for plot_num in range(nplots): 38 | field = fields_to_plot[plot_num] 39 | vmin, vmax = ranges[plot_num] 40 | cmap = cmaps[plot_num] 41 | 42 | plt.subplot(1, nplots, plot_num + 1) 43 | display.plot(field, 0, vmin=vmin, vmax=vmax, title_flag=False, cmap=cmap) 44 | display.set_limits(ylim=[0, 17]) 45 | 46 | # Grab the fixed angle and time from the first sweep 47 | fixed_angle = radar.fixed_angle["data"][0] 48 | time = radar.time["units"][13:] 49 | 50 | # Add the metadata to the title 51 | plt.suptitle( 52 | f"Reflectivity and Velocity \n Azimuth: {np.around(fixed_angle, 3)}\u00B0 {time} UTC" 53 | ) 54 | plt.show() 55 | -------------------------------------------------------------------------------- /examples/plotting/plot_three_panel_gridmapdisplay.py: -------------------------------------------------------------------------------- 1 | """ 2 | =========================================== 3 | Create a 3 panel plot using GridMapDisplay 4 | =========================================== 5 | 6 | An example that creates a 3 panel plot of a PPI, latitude slice, 7 | and longitude slice using xarray and a cartopy background. 8 | 9 | """ 10 | 11 | print(__doc__) 12 | 13 | # Author: Jason Hemedinger 14 | # License: BSD 3 clause 15 | 16 | import cartopy.crs as ccrs 17 | import matplotlib.pyplot as plt 18 | 19 | import pyart 20 | from pyart.testing import get_test_data 21 | 22 | # Read in the gridded file, create GridMapDisplay object 23 | filename = get_test_data("20110520100000_nexrad_grid.nc") 24 | radar = pyart.io.read_grid(filename) 25 | display = pyart.graph.GridMapDisplay(radar) 26 | 27 | # Setting projection, figure size, and panel sizes. 28 | projection = ccrs.PlateCarree() 29 | 30 | fig = plt.figure(figsize=[15, 7]) 31 | 32 | map_panel_axes = [0.05, 0.05, 0.4, 0.80] 33 | x_cut_panel_axes = [0.55, 0.10, 0.4, 0.25] 34 | y_cut_panel_axes = [0.55, 0.50, 0.4, 0.25] 35 | 36 | # Set parameters. 37 | level = 1 38 | vmin = -8 39 | vmax = 64 40 | lat = 36.5 41 | lon = -97.7 42 | 43 | # Panel 1: PPI plot of the second tilt. 44 | ax1 = fig.add_axes(map_panel_axes, projection=projection) 45 | display.plot_grid( 46 | "REF", 47 | 1, 48 | vmin=vmin, 49 | vmax=vmax, 50 | ax=ax1, 51 | projection=projection, 52 | cmap="HomeyerRainbow", 53 | ) 54 | display.plot_crosshairs(lon=lon, lat=lat) 55 | 56 | # Panel 2: longitude slice 57 | ax2 = fig.add_axes(x_cut_panel_axes) 58 | display.plot_longitude_slice( 59 | "REF", lon=lon, lat=lat, ax=ax2, vmin=vmin, vmax=vmax, cmap="HomeyerRainbow" 60 | ) 61 | 62 | ax2.set_ylim([0, 15]) 63 | ax2.set_xlim([-50, 50]) 64 | 65 | # Panel 3: latitude slice 66 | ax3 = fig.add_axes(y_cut_panel_axes) 67 | display.plot_latitude_slice( 68 | "REF", lon=lon, lat=lat, ax=ax3, vmin=vmin, vmax=vmax, cmap="HomeyerRainbow" 69 | ) 70 | ax3.set_ylim([0, 15]) 71 | ax3.set_xlim([-50, 50]) 72 | 73 | plt.show() 74 | -------------------------------------------------------------------------------- /examples/plotting/plot_xsect.py: -------------------------------------------------------------------------------- 1 | """ 2 | ======================================= 3 | Plot a cross section from a PPI volume 4 | ======================================= 5 | 6 | An example which extracts a cross section at two azimuth angles from a volume 7 | of PPI scans and plots both cross sections. 8 | 9 | """ 10 | 11 | print(__doc__) 12 | 13 | # Author: Jonathan J. Helmus (jhelmus@anl.gov) 14 | # License: BSD 3 clause 15 | 16 | import matplotlib.pyplot as plt 17 | 18 | import pyart 19 | from pyart.testing import get_test_data 20 | 21 | # Read the data, a cfradial file 22 | filename = get_test_data("swx_20120520_0641.nc") 23 | radar = pyart.io.read(filename) 24 | 25 | # Create a cross section at 225 and 270 degrees azimuth 26 | xsect = pyart.util.cross_section_ppi(radar, [225, 270]) 27 | 28 | # Set the colorbar label 29 | colorbar_label = "Equivalent \n reflectivity factor \n (dBZ)" 30 | 31 | display = pyart.graph.RadarDisplay(xsect) 32 | fig = plt.figure() 33 | ax1 = fig.add_subplot(211) 34 | display.plot( 35 | "reflectivity_horizontal", 0, vmin=-32, vmax=64.0, colorbar_label=colorbar_label 36 | ) 37 | plt.ylim(0, 15) 38 | ax2 = fig.add_subplot(212) 39 | display.plot( 40 | "reflectivity_horizontal", 1, vmin=-32, vmax=64.0, colorbar_label=colorbar_label 41 | ) 42 | plt.ylim(0, 15) 43 | 44 | plt.tight_layout() 45 | plt.show() 46 | -------------------------------------------------------------------------------- /examples/retrieve/README.txt: -------------------------------------------------------------------------------- 1 | .. _retrieve_examples: 2 | 3 | Retrieval Examples 4 | ------------------ 5 | 6 | Retrievals from various radars, such as additional fields or subsets of the data. 7 | -------------------------------------------------------------------------------- /examples/retrieve/plot_column_subset.py: -------------------------------------------------------------------------------- 1 | """ 2 | ==================================== 3 | Extract a radar column above a point 4 | ==================================== 5 | 6 | Given a radar and a point, extract the column of radar data values above 7 | a point 8 | """ 9 | 10 | # Author: Maxwell Grover (mgrover@anl.gov) 11 | # License: BSD 3 clause 12 | 13 | import cartopy.crs as ccrs 14 | import matplotlib.pyplot as plt 15 | import numpy as np 16 | 17 | import pyart 18 | from pyart.testing import get_test_data 19 | 20 | # Read in some test data 21 | filename = get_test_data("swx_20120520_0641.nc") 22 | radar = pyart.io.read(filename) 23 | 24 | ###################################### 25 | # **Plot the first sweep and our desired point** 26 | # 27 | # Let's visualize our radar data from a single sweep, and plot 28 | # the location of our desired point on a map. 29 | # This will provide some context as to where we are extracting our 30 | # column of values. 31 | 32 | site_lon = -97.73 # longitude in degrees 33 | site_lat = 36.41 # latitdue in degrees 34 | 35 | # Setup the RadarMapDisplay and add our projection 36 | display = pyart.graph.RadarMapDisplay(radar) 37 | ax = plt.subplot(111, projection=ccrs.PlateCarree()) 38 | 39 | # Visualize the reflectivity field, using the lowest sweep with 40 | # latitude and longitude lines 41 | display.plot_ppi_map( 42 | "reflectivity_horizontal", 43 | 0, 44 | ax=ax, 45 | vmin=-32, 46 | vmax=64.0, 47 | lon_lines=np.arange(-98, -97, 0.2), 48 | lat_lines=np.arange(36, 37, 0.2), 49 | ) 50 | 51 | # Plot our site location on top of the radar image 52 | ax.scatter(site_lon, site_lat, color="black") 53 | 54 | ###################################### 55 | # Now that we have our point defined, and our radar object, we can use the following 56 | # utility function in Py-ART to subset a column 57 | ds = pyart.util.columnsect.get_field_location(radar, site_lat, site_lon) 58 | 59 | ###################################### 60 | # This function returns an xarray dataset, with all of our data fields! 61 | print(ds) 62 | 63 | ###################################### 64 | # **Visualize the Reflectivity Values in the Column** 65 | # 66 | # Let's visualize the reflectivity values in the column 67 | # above our point, which is stored in our new dataset 68 | ds.corrected_reflectivity_horizontal.plot(y="height") 69 | -------------------------------------------------------------------------------- /examples/retrieve/plot_composite_reflectivity.py: -------------------------------------------------------------------------------- 1 | """ 2 | ========================================= 3 | Calculate and Plot Composite Reflectivity 4 | ========================================= 5 | 6 | Calculates and plots the composite reflectivity, or the 7 | maximum reflectivity across all of the elevations. 8 | """ 9 | 10 | # Author: Maxwell Grover (mgrover@anl.gov) 11 | # License: BSD 3 clause 12 | 13 | import matplotlib.pyplot as plt 14 | 15 | import pyart 16 | from pyart.testing import get_test_data 17 | 18 | # Read in a sample file 19 | filename = get_test_data("swx_20120520_0641.nc") 20 | radar = pyart.io.read(filename) 21 | 22 | # Configure a gatefilter to filter out copolar correlation coefficient values > 0.9 23 | gatefilter = pyart.filters.GateFilter(radar) 24 | gatefilter.exclude_transition() 25 | gatefilter.exclude_below("copol_coeff", 0.9) 26 | 27 | # Calculate composite reflectivity, or the maximum reflectivity across all elevation levels 28 | compz = pyart.retrieve.composite_reflectivity( 29 | radar, field="reflectivity_horizontal", gatefilter=gatefilter 30 | ) 31 | 32 | # Plot the original reflectivity field and the composite field 33 | fig = plt.figure(figsize=(16, 6)) 34 | ax = plt.subplot(121) 35 | display = pyart.graph.RadarDisplay(radar) 36 | display.plot("reflectivity_horizontal", ax=ax, vmin=-20, vmax=80) 37 | 38 | ax2 = plt.subplot(122) 39 | composite_display = pyart.graph.RadarDisplay(compz) 40 | composite_display.plot( 41 | "composite_reflectivity", ax=ax2, vmin=-20, vmax=80, cmap="HomeyerRainbow" 42 | ) 43 | -------------------------------------------------------------------------------- /examples/retrieve/plot_hydrometeor.py: -------------------------------------------------------------------------------- 1 | """ 2 | ============================================= 3 | Calculate and Plot hydrometeor classification 4 | ============================================= 5 | 6 | Calculates a hydrometeor classification and displays the results 7 | """ 8 | 9 | # Author: Daniel Wolfensberger (daniel.wolfensberger@meteoswiss.ch) 10 | # License: BSD 3 clause 11 | 12 | import matplotlib as mpl 13 | import matplotlib.pyplot as plt 14 | import numpy as np 15 | from open_radar_data import DATASETS 16 | 17 | import pyart 18 | 19 | # Read in a sample file 20 | filename = DATASETS.fetch("MLL2217907250U.003.nc") 21 | radar = pyart.io.read_cfradial(filename) 22 | 23 | # Read temperature preinterpolated from NWP model 24 | filename = DATASETS.fetch("20220628072500_savevol_COSMO_LOOKUP_TEMP.nc") 25 | nwp_temp = pyart.io.read_cfradial(filename) 26 | 27 | # Add temperature to radar object as new field 28 | radar.add_field("temperature", nwp_temp.fields["temperature"]) 29 | 30 | # Compute attenuation 31 | out = pyart.correct.calculate_attenuation_zphi( 32 | radar, 33 | phidp_field="uncorrected_differential_phase", 34 | temp_field="temperature", 35 | temp_ref="temperature", 36 | ) 37 | spec_at, pia, cor_z, spec_diff_at, pida, cor_zdr = out 38 | radar.add_field("corrected_reflectivity", cor_z) 39 | radar.add_field("corrected_differential_reflectivity", cor_zdr) 40 | radar.add_field("specific_attenuation", spec_at) 41 | 42 | # Compute KDP 43 | kdp, _, _ = pyart.retrieve.kdp_maesaka( 44 | radar, psidp_field="uncorrected_differential_phase" 45 | ) 46 | radar.add_field("specific_differential_phase", kdp) 47 | 48 | # Compute hydrometeor classification 49 | hydro = pyart.retrieve.hydroclass_semisupervised( 50 | radar, 51 | refl_field="corrected_reflectivity", 52 | zdr_field="corrected_differential_reflectivity", 53 | kdp_field="specific_differential_phase", 54 | rhv_field="uncorrected_cross_correlation_ratio", 55 | temp_field="temperature", 56 | )["hydro"] 57 | 58 | radar.add_field("radar_echo_classification", hydro) 59 | 60 | # Display hydrometeor classification with categorical colormap 61 | fig, ax = plt.subplots(1, 1, figsize=(6, 6)) 62 | display = pyart.graph.RadarDisplay(radar) 63 | 64 | labels = ["NC", "AG", "CR", "LR", "RP", "RN", "VI", "WS", "MH", "IH/HDG"] 65 | ticks = np.arange(len(labels)) 66 | boundaries = np.arange(-0.5, len(labels)) 67 | norm = mpl.colors.BoundaryNorm(boundaries, 256) 68 | 69 | cax = display.plot_ppi( 70 | "radar_echo_classification", 0, ax=ax, norm=norm, ticks=ticks, ticklabs=labels 71 | ) 72 | 73 | ax.set_xlim([-50, 50]) 74 | ax.set_ylim([-50, 50]) 75 | ax.set_aspect("equal", "box") 76 | 77 | # For info 78 | # NC = not classified 79 | # AG = aggregates 80 | # CR = ice crystals 81 | # LR = light rain 82 | # RP = rimed particles 83 | # RN = rain 84 | # VI = vertically oriented ice 85 | # WS = wet snow 86 | # MH = melting hail 87 | # IH/HDG = dry hail / high density graupel 88 | -------------------------------------------------------------------------------- /examples/retrieve/plot_vad.py: -------------------------------------------------------------------------------- 1 | """ 2 | ========================================= 3 | Calculate and Plot VAD profile 4 | ========================================= 5 | 6 | Calculates a VAD and plots a vertical profile of wind 7 | """ 8 | 9 | # Author: Daniel Wolfensberger (daniel.wolfensberger@meteoswiss.ch) 10 | # License: BSD 3 clause 11 | 12 | import matplotlib.pyplot as plt 13 | import numpy as np 14 | from open_radar_data import DATASETS 15 | 16 | import pyart 17 | 18 | # Read in a sample file 19 | filename = DATASETS.fetch("MLA2119412050U.nc") 20 | radar = pyart.io.read_cfradial(filename) 21 | 22 | # Loop on all sweeps and compute VAD 23 | zlevels = np.arange(100, 5000, 100) # height above radar 24 | u_allsweeps = [] 25 | v_allsweeps = [] 26 | 27 | for idx in range(radar.nsweeps): 28 | radar_1sweep = radar.extract_sweeps([idx]) 29 | vad = pyart.retrieve.vad_browning( 30 | radar_1sweep, "corrected_velocity", z_want=zlevels 31 | ) 32 | u_allsweeps.append(vad.u_wind) 33 | v_allsweeps.append(vad.v_wind) 34 | 35 | # Average U and V over all sweeps and compute magnitude and angle 36 | u_avg = np.nanmean(np.array(u_allsweeps), axis=0) 37 | v_avg = np.nanmean(np.array(v_allsweeps), axis=0) 38 | orientation = np.rad2deg(np.arctan2(-u_avg, -v_avg)) % 360 39 | speed = np.sqrt(u_avg**2 + v_avg**2) 40 | 41 | # Display vertical profile of wind 42 | fig, ax = plt.subplots(1, 2, sharey=True) 43 | ax[0].plot(speed * 2, zlevels + radar.altitude["data"]) 44 | ax[1].plot(orientation, zlevels + radar.altitude["data"]) 45 | ax[0].set_xlabel("Wind speed [m/s]") 46 | ax[1].set_xlabel("Wind direction [deg]") 47 | ax[0].set_ylabel("Altitude [m]") 48 | fig.suptitle("Wind profile obtained from VAD") 49 | -------------------------------------------------------------------------------- /examples/xradar/README.txt: -------------------------------------------------------------------------------- 1 | .. _xradar_examples: 2 | 3 | Xradar Examples 4 | ------------------ 5 | 6 | Examples of using Xradar with Py-ART to accomplish different tasks. 7 | -------------------------------------------------------------------------------- /examples/xradar/plot_grid_xradar.py: -------------------------------------------------------------------------------- 1 | """ 2 | ================================= 3 | Grid Data Using Xradar and Py-ART 4 | ================================= 5 | 6 | An example which uses xradar and Py-ART to grid a PPI file. 7 | 8 | """ 9 | 10 | # Author: Max Grover (mgrover@anl.gov) 11 | # License: BSD 3 clause 12 | 13 | 14 | import xradar as xd 15 | 16 | import pyart 17 | from pyart.testing import get_test_data 18 | 19 | # Locate the test data and read in using xradar 20 | filename = get_test_data("swx_20120520_0641.nc") 21 | tree = xd.io.open_cfradial1_datatree(filename) 22 | 23 | # Give the tree Py-ART radar methods 24 | radar = tree.pyart.to_radar() 25 | 26 | # Grid using 11 vertical levels, and 101 horizontal grid cells at a resolution on 1 km 27 | grid = pyart.map.grid_from_radars( 28 | (radar,), 29 | grid_shape=(11, 101, 101), 30 | grid_limits=( 31 | (0.0, 10_000), 32 | (-50_000.0, 50_000.0), 33 | (-50_000, 50_000.0), 34 | ), 35 | ) 36 | 37 | display = pyart.graph.GridMapDisplay(grid) 38 | display.plot_grid( 39 | "reflectivity_horizontal", level=0, vmin=-20, vmax=60, cmap="ChaseSpectral" 40 | ) 41 | -------------------------------------------------------------------------------- /examples/xradar/plot_xradar.py: -------------------------------------------------------------------------------- 1 | """ 2 | ================================== 3 | Plot a PPI Using Xradar and Py-ART 4 | ================================== 5 | 6 | An example which uses xradar and Py-ART to create a PPI plot of a Cfradial file. 7 | 8 | """ 9 | 10 | # Author: Max Grover (mgrover@anl.gov) 11 | # License: BSD 3 clause 12 | 13 | 14 | import xradar as xd 15 | 16 | import pyart 17 | from pyart.testing import get_test_data 18 | 19 | # Locate the test data and read in using xradar 20 | filename = get_test_data("swx_20120520_0641.nc") 21 | tree = xd.io.open_cfradial1_datatree(filename) 22 | 23 | # Give the tree Py-ART radar methods 24 | radar = tree.pyart.to_radar() 25 | 26 | # Plot the Reflectivity Field (corrected_reflectivity_horizontal) 27 | display = pyart.graph.RadarMapDisplay(radar) 28 | display.plot_ppi( 29 | "corrected_reflectivity_horizontal", cmap="ChaseSpectral", vmin=-20, vmax=70 30 | ) 31 | -------------------------------------------------------------------------------- /guides/pyart_cheatsheet.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/guides/pyart_cheatsheet.pdf -------------------------------------------------------------------------------- /pyart/__check_build/README: -------------------------------------------------------------------------------- 1 | Files in this directory are derived from the scikit-learn project with the 2 | following license: 3 | 4 | New BSD License 5 | 6 | Copyright (c) 2007–2014 The scikit-learn developers. 7 | All rights reserved. 8 | 9 | 10 | Redistribution and use in source and binary forms, with or without 11 | modification, are permitted provided that the following conditions are met: 12 | 13 | a. Redistributions of source code must retain the above copyright notice, 14 | this list of conditions and the following disclaimer. 15 | b. Redistributions in binary form must reproduce the above copyright 16 | notice, this list of conditions and the following disclaimer in the 17 | documentation and/or other materials provided with the distribution. 18 | c. Neither the name of the Scikit-learn Developers nor the names of 19 | its contributors may be used to endorse or promote products 20 | derived from this software without specific prior written 21 | permission. 22 | 23 | 24 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR 28 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 34 | DAMAGE. 35 | -------------------------------------------------------------------------------- /pyart/__check_build/__init__.py: -------------------------------------------------------------------------------- 1 | """ Module to give helpful messages to the user that did not 2 | compile Py-ART properly. 3 | """ 4 | 5 | import os 6 | 7 | INPLACE_MSG = """ 8 | It appears that you are importing a local pyart source tree. For this, you 9 | need to have an inplace install. Maybe you are in the source directory and 10 | you need to try from another location.""" 11 | 12 | STANDARD_MSG = """ 13 | If you have used an installer, please check that it is suited for your 14 | Python version, your operating system and your platform.""" 15 | 16 | 17 | def raise_build_error(e): 18 | # Raise a comprehensible error and list the contents of the 19 | # directory to help debugging on the mailing list. 20 | local_dir = os.path.split(__file__)[0] 21 | msg = STANDARD_MSG 22 | if local_dir == "pyart/__check_build": 23 | # Picking up the local install: this will work only if the 24 | # install is an 'inplace build' 25 | msg = INPLACE_MSG 26 | dir_content = list() 27 | for i, filename in enumerate(os.listdir(local_dir)): 28 | if (i + 1) % 3: 29 | dir_content.append(filename.ljust(26)) 30 | else: 31 | dir_content.append(filename + "\n") 32 | raise ImportError( 33 | """{} 34 | ___________________________________________________________________________ 35 | Contents of {}: 36 | {} 37 | ___________________________________________________________________________ 38 | It seems that Py-ART has not been built correctly. 39 | 40 | If you have installed Py-ART from source, please do not forget 41 | to build the package before using it: run `python setup.py install` in the 42 | source directory. 43 | {}""".format( 44 | e, local_dir, "".join(dir_content).strip(), msg 45 | ) 46 | ) 47 | 48 | 49 | try: 50 | from ._check_build import check_build # noqa 51 | except ImportError as e: 52 | raise_build_error(e) 53 | -------------------------------------------------------------------------------- /pyart/__check_build/_check_build.pyx: -------------------------------------------------------------------------------- 1 | def check_build(): 2 | return 3 | -------------------------------------------------------------------------------- /pyart/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Py-ART: The Python ARM Radar Toolkit 3 | ===================================== 4 | 5 | """ 6 | 7 | # print information on citing Py-ART, this message can be suppressed by 8 | # setting the PYART_QUIET environment variable 9 | _citation_text = """ 10 | ## You are using the Python ARM Radar Toolkit (Py-ART), an open source 11 | ## library for working with weather radar data. Py-ART is partly 12 | ## supported by the U.S. Department of Energy as part of the Atmospheric 13 | ## Radiation Measurement (ARM) Climate Research Facility, an Office of 14 | ## Science user facility. 15 | ## 16 | ## If you use this software to prepare a publication, please cite: 17 | ## 18 | ## JJ Helmus and SM Collis, JORS 2016, doi: 10.5334/jors.119 19 | """ 20 | from os import environ as _environ 21 | 22 | if "PYART_QUIET" not in _environ: 23 | print(_citation_text) 24 | 25 | import importlib.metadata as _importlib_metadata 26 | 27 | # import subpackages 28 | # print out helpful message if build fails or importing from source tree 29 | from . import ( 30 | __check_build, # noqa 31 | aux_io, # noqa 32 | bridge, # noqa 33 | config, # noqa 34 | core, # noqa 35 | correct, # noqa 36 | filters, # noqa 37 | graph, # noqa 38 | io, # noqa 39 | map, # noqa 40 | retrieve, # noqa 41 | testing, # noqa 42 | util, # noqa 43 | xradar, # noqa 44 | ) 45 | from ._debug_info import _debug_info # noqa 46 | 47 | # root level functions 48 | from .config import load_config # noqa 49 | 50 | # Get the version 51 | try: 52 | __version__ = _importlib_metadata.version("arm_pyart") 53 | except _importlib_metadata.PackageNotFoundError: 54 | # package is not installed 55 | __version__ = "0.0.0" 56 | -------------------------------------------------------------------------------- /pyart/aux_io/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Additional classes and functions for reading and writing data from a number 3 | of file formats. 4 | 5 | These auxiliary input/output routines are not as well polished as those in 6 | :mod:`pyart.io`. They may require addition dependencies beyond those required 7 | for a standard Py-ART install, use non-standard function parameter and naming, 8 | are not supported by the :py:func:`pyart.io.read` function and are not fully 9 | tested if tested at all. Please use these at your own risk. 10 | 11 | Bugs in these function should be reported but fixing them may not be a 12 | priority. 13 | 14 | """ 15 | 16 | from .arm_vpt import read_kazr # noqa 17 | from .arm_vpt import read_mmcr # noqa 18 | from .d3r_gcpex_nc import read_d3r_gcpex_nc # noqa 19 | from .edge_netcdf import read_edge_netcdf # noqa 20 | from .gamic_hdf5 import read_gamic # noqa 21 | from .kazr_spectra import read_kazr_spectra # noqa 22 | from .noxp_iphex_nc import read_noxp_iphex_nc # noqa 23 | from .odim_h5 import read_odim_h5 # noqa 24 | from .pattern import read_pattern # noqa 25 | from .radx import read_radx # noqa 26 | from .radx_grid import read_radx_grid # noqa 27 | from .rainbow_wrl import read_rainbow_wrl # noqa 28 | from .rxm25 import read_rxm25 # noqa 29 | from .sinarame_h5 import read_sinarame_h5, write_sinarame_cfradial # noqa 30 | 31 | __all__ = [s for s in dir() if not s.startswith("_")] 32 | -------------------------------------------------------------------------------- /pyart/aux_io/radx.py: -------------------------------------------------------------------------------- 1 | """ 2 | Reading files using Radx to first convert the file to Cf.Radial format 3 | 4 | """ 5 | 6 | import os 7 | import subprocess 8 | import tempfile 9 | 10 | from ..io.cfradial import read_cfradial 11 | from ..io.common import _test_arguments 12 | 13 | 14 | def read_radx(filename, radx_dir=None, **kwargs): 15 | """ 16 | Read a file by first converting it to Cf/Radial using RadxConvert. 17 | 18 | Parameters 19 | ---------- 20 | filename : str 21 | Name of file to read using RadxConvert. 22 | 23 | radx_dir : str, optional 24 | path to the radx install 25 | 26 | Returns 27 | ------- 28 | radar : Radar 29 | Radar object. 30 | 31 | """ 32 | # test for non empty kwargs 33 | _test_arguments(kwargs) 34 | if radx_dir is not None: 35 | executable = os.path.join(radx_dir, "RadxConvert") 36 | else: 37 | executable = "RadxConvert" 38 | 39 | tmpfile = tempfile.mkstemp(suffix=".nc", dir=".")[1] 40 | head, tail = os.path.split(tmpfile) 41 | try: 42 | subprocess.check_call( 43 | [ 44 | executable, 45 | "-const_ngates", 46 | "-outdir", 47 | head, 48 | "-outname", 49 | tail, 50 | "-f", 51 | filename, 52 | ] 53 | ) 54 | if not os.path.isfile(tmpfile): 55 | raise OSError( 56 | "RadxConvert failed to create a file, upgrading to the " 57 | " latest version of Radx may be necessary." 58 | ) 59 | radar = read_cfradial(tmpfile) 60 | finally: 61 | os.remove(tmpfile) 62 | return radar 63 | -------------------------------------------------------------------------------- /pyart/bridge/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Py-ART can act as bridge to other community software projects. 3 | 4 | The functionality in this namespace is available in other pyart namespaces. 5 | 6 | Current extensions: 7 | * wradlib https://wradlib.org/ 8 | 9 | 10 | 11 | """ 12 | 13 | from .. import retrieve as _retrieve # noqa 14 | from .wradlib_bridge import texture_of_complex_phase # noqa 15 | 16 | _retrieve.texture_of_complex_phase = texture_of_complex_phase 17 | -------------------------------------------------------------------------------- /pyart/bridge/wradlib_bridge.py: -------------------------------------------------------------------------------- 1 | """ 2 | Py-ART methods linking to wradlib functions, http://wradlib.org/ 3 | 4 | """ 5 | 6 | try: 7 | import wradlib 8 | 9 | _WRADLIB_AVAILABLE = True 10 | except ImportError: 11 | _WRADLIB_AVAILABLE = False 12 | import numpy as np 13 | 14 | from ..config import get_field_name, get_metadata 15 | from ..exceptions import MissingOptionalDependency 16 | 17 | 18 | def texture_of_complex_phase(radar, phidp_field=None, phidp_texture_field=None): 19 | """ 20 | Calculate the texture of the differential phase field. 21 | 22 | Calculate the texture of the real part of the complex differential 23 | phase field 24 | 25 | Parameters 26 | ---------- 27 | radar : Radar 28 | Radar object from which to . 29 | phidp_field : str, optional 30 | Name of field in radar which contains the differential phase shift. 31 | None will use the default field name in the Py-ART configuration file. 32 | phidp_texture_field : str, optional 33 | Name to use for the differential phase texture field metadata. 34 | None will use the default field name in the Py-ART configuration file. 35 | 36 | Returns 37 | ------- 38 | texture_field : dict 39 | Field dictionary containing the texture of the real part 40 | of the complex differential phase. 41 | 42 | References 43 | ---------- 44 | Gourley, J. J., P. Tabary, and J. Parent du Chatelet, 45 | A fuzzy logic algorithm for the separation of precipitating from 46 | nonprecipitating echoes using polarimetric radar observations, 47 | Journal of Atmospheric and Oceanic Technology 24 (8), 1439-1451 48 | 49 | """ 50 | # check that wradlib is available 51 | if not _WRADLIB_AVAILABLE: 52 | raise MissingOptionalDependency( 53 | "wradlib is required to use texture_of_complex_phase but is " 54 | + "not installed" 55 | ) 56 | 57 | # parse field names 58 | if phidp_field is None: 59 | phidp_field = get_field_name("differential_phase") 60 | 61 | if phidp_texture_field is None: 62 | phidp_texture_field = get_field_name("differential_phase") 63 | 64 | # Grab the phase data 65 | phidp = radar.fields[phidp_field]["data"] 66 | 67 | # convert to complex number 68 | complex_phase = np.exp(1j * (phidp * np.pi / 180.0)) 69 | 70 | # calculate texture using wradlib 71 | w_texture_complex = wradlib.dp.texture((np.real(complex_phase) + 1.0) * 180) 72 | 73 | texture_field = get_metadata(phidp_texture_field) 74 | texture_field["data"] = w_texture_complex 75 | texture_field["standard_name"] = "texture_of_differential_phase_hv" 76 | texture_field["long_name"] = "Texture of differential phase" 77 | return texture_field 78 | -------------------------------------------------------------------------------- /pyart/core/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Core Py-ART classes and function for interacting with weather radar data. 3 | 4 | """ 5 | 6 | from .grid import Grid # noqa 7 | from .radar import Radar # noqa 8 | from .radar_spectra import RadarSpectra # noqa 9 | from .transforms import antenna_to_cartesian # noqa 10 | from .transforms import antenna_vectors_to_cartesian # noqa 11 | from .transforms import cartesian_to_geographic # noqa 12 | from .transforms import cartesian_to_geographic_aeqd # noqa 13 | from .transforms import cartesian_vectors_to_geographic # noqa 14 | from .transforms import geographic_to_cartesian # noqa 15 | from .transforms import geographic_to_cartesian_aeqd # noqa 16 | from .transforms import cartesian_to_antenna # noqa 17 | from .wind_profile import HorizontalWindProfile # noqa 18 | 19 | __all__ = [s for s in dir() if not s.startswith("_")] 20 | -------------------------------------------------------------------------------- /pyart/correct/__init__.pxd: -------------------------------------------------------------------------------- 1 | from pyart.correct cimport * 2 | from pyart.io cimport * 3 | -------------------------------------------------------------------------------- /pyart/correct/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Correct radar fields. 3 | 4 | """ 5 | 6 | # for backwards compatibility GateFilter available in the correct namespace 7 | from ..filters.gatefilter import GateFilter, moment_based_gate_filter # noqa 8 | from .attenuation import calculate_attenuation # noqa 9 | from .attenuation import calculate_attenuation_philinear # noqa 10 | from .attenuation import calculate_attenuation_zphi # noqa 11 | from .bias_and_noise import calc_zdr_offset # noqa 12 | from .bias_and_noise import calc_cloud_mask, calc_noise_floor, correct_bias # noqa 13 | from .bias_and_noise import ( 14 | correct_noise_rhohv, # noqa 15 | cloud_threshold, # noqa 16 | range_correction, # noqa 17 | ) # noqa 18 | from .dealias import dealias_fourdd # noqa 19 | from .despeckle import despeckle_field, find_objects # noqa 20 | from .phase_proc import phase_proc_lp, phase_proc_lp_gf # noqa 21 | from .region_dealias import dealias_region_based # noqa 22 | from .unwrap import dealias_unwrap_phase # noqa 23 | 24 | __all__ = [s for s in dir() if not s.startswith("_")] 25 | -------------------------------------------------------------------------------- /pyart/correct/_common_dealias.py: -------------------------------------------------------------------------------- 1 | """ 2 | Routines used by multiple dealiasing functions. 3 | 4 | """ 5 | 6 | import numpy as np 7 | 8 | from ..config import get_field_name 9 | from ..filters.gatefilter import GateFilter, moment_based_gate_filter 10 | 11 | 12 | def _parse_fields(vel_field, corr_vel_field): 13 | """Parse and return the radar fields for dealiasing.""" 14 | if vel_field is None: 15 | vel_field = get_field_name("velocity") 16 | if corr_vel_field is None: 17 | corr_vel_field = get_field_name("corrected_velocity") 18 | return vel_field, corr_vel_field 19 | 20 | 21 | def _parse_nyquist_vel(nyquist_vel, radar, check_uniform): 22 | """Parse the nyquist_vel parameter, extract from the radar if needed.""" 23 | if nyquist_vel is None: 24 | nyquist_vel = [ 25 | radar.get_nyquist_vel(i, check_uniform) for i in range(radar.nsweeps) 26 | ] 27 | else: # Nyquist velocity explicitly provided 28 | try: 29 | len(nyquist_vel) 30 | except TypeError: # expand single value. 31 | nyquist_vel = [nyquist_vel for i in range(radar.nsweeps)] 32 | return nyquist_vel 33 | 34 | 35 | def _parse_gatefilter(gatefilter, radar, **kwargs): 36 | """Parse the gatefilter, return a valid GateFilter object.""" 37 | # parse the gatefilter parameter 38 | if gatefilter is None: # create a moment based filter 39 | gatefilter = moment_based_gate_filter(radar, **kwargs) 40 | elif gatefilter is False: 41 | gatefilter = GateFilter(radar) 42 | else: 43 | gatefilter = gatefilter.copy() 44 | return gatefilter 45 | 46 | 47 | def _parse_rays_wrap_around(rays_wrap_around, radar): 48 | """Parse the rays_wrap_around parameter.""" 49 | if rays_wrap_around is None: 50 | if radar.scan_type == "ppi": 51 | rays_wrap_around = True 52 | else: 53 | rays_wrap_around = False 54 | return rays_wrap_around 55 | 56 | 57 | def _set_limits(data, nyquist_vel, dic): 58 | """Set the valid_min and valid_max keys in dic from dealiased data.""" 59 | max_abs_vel = np.ma.max(np.ma.abs(data)) 60 | if max_abs_vel is np.ma.masked: 61 | # all velocities are masked, do not set valid_min and valid_max 62 | return 63 | max_nyq_vel = np.ma.max(nyquist_vel) 64 | max_nyq_int = 2.0 * max_nyq_vel 65 | added_intervals = np.ceil((max_abs_vel - max_nyq_vel) / (max_nyq_int)) 66 | max_valid_velocity = max_nyq_vel + added_intervals * max_nyq_int 67 | dic["valid_min"] = float(-max_valid_velocity) 68 | dic["valid_max"] = float(max_valid_velocity) 69 | return 70 | -------------------------------------------------------------------------------- /pyart/correct/_fourdd_h.pxd: -------------------------------------------------------------------------------- 1 | """ 2 | Cython wrapper around University of Washington 4DD code. 3 | """ 4 | 5 | cimport pyart.io._rsl_h as _rsl_h 6 | 7 | 8 | cdef extern from "dealias_fourdd.h": 9 | 10 | int dealias_fourdd(_rsl_h.Volume* rvVolume, _rsl_h.Volume* soundVolume, 11 | _rsl_h.Volume* lastVolume, float missingVal, 12 | float compthresh, float compthresh2, float thresh, 13 | float ckval, float stdthresh, float epsilon, 14 | int maxcount, int pass2, int rm, int proximity, 15 | int mingood, int filt, int ba_mincount, 16 | int ba_edgecount) 17 | 18 | 19 | cdef extern from "sounding_to_volume.h": 20 | 21 | int sounding_to_volume(_rsl_h.Volume* soundVolume, float missingVal, 22 | float *height_array, float *speed_array, 23 | float *direction_array, int nlevels, 24 | float maxshear, int sign) 25 | -------------------------------------------------------------------------------- /pyart/correct/_unwrap_1d.pyx: -------------------------------------------------------------------------------- 1 | #cython: cdivision=True 2 | #cython: boundscheck=False 3 | #cython: nonecheck=False 4 | #cython: wraparound=False 5 | 6 | from libc.math cimport M_PI 7 | 8 | 9 | def unwrap_1d(double[::1] image, double[::1] unwrapped_image): 10 | """ Phase unwrapping using the naive approach. """ 11 | cdef: 12 | Py_ssize_t i 13 | double difference 14 | long periods = 0 15 | unwrapped_image[0] = image[0] 16 | for i in range(1, image.shape[0]): 17 | difference = image[i] - image[i - 1] 18 | if difference > M_PI: 19 | periods -= 1 20 | elif difference < -M_PI: 21 | periods += 1 22 | unwrapped_image[i] = image[i] + 2 * M_PI * periods 23 | -------------------------------------------------------------------------------- /pyart/correct/_unwrap_2d.pyx: -------------------------------------------------------------------------------- 1 | cdef extern void unwrap2D(double* wrapped_image, 2 | double* unwrapped_image, 3 | unsigned char* input_mask, 4 | int image_width, int image_height, 5 | int wrap_around_x, int wrap_around_y) 6 | 7 | def unwrap_2d(double[:, ::1] image, 8 | unsigned char[:, ::1] mask, 9 | double[:, ::1] unwrapped_image, 10 | wrap_around): 11 | # 2D phase unwrapping. 12 | unwrap2D(&image[0, 0], 13 | &unwrapped_image[0, 0], 14 | &mask[0, 0], 15 | image.shape[1], image.shape[0], 16 | wrap_around[1], wrap_around[0], 17 | ) 18 | -------------------------------------------------------------------------------- /pyart/correct/_unwrap_3d.pyx: -------------------------------------------------------------------------------- 1 | cdef extern void unwrap3D(double* wrapped_volume, 2 | double* unwrapped_volume, 3 | unsigned char* input_mask, 4 | int image_width, int image_height, int volume_depth, 5 | int wrap_around_x, int wrap_around_y, int wrap_around_z) 6 | 7 | def unwrap_3d(double[:, :, ::1] image, 8 | unsigned char[:, :, ::1] mask, 9 | double[:, :, ::1] unwrapped_image, 10 | wrap_around): 11 | # 3D phase unwrapping. 12 | unwrap3D(&image[0, 0, 0], 13 | &unwrapped_image[0, 0, 0], 14 | &mask[0, 0, 0], 15 | image.shape[2], image.shape[1], image.shape[0], #TODO: check!!! 16 | wrap_around[2], wrap_around[1], wrap_around[0], 17 | ) 18 | -------------------------------------------------------------------------------- /pyart/correct/rebuild_fast_edge_finder.sh: -------------------------------------------------------------------------------- 1 | # remove and rebuild the fast.so module 2 | rm _fast_edge_finder.so 3 | cython _fast_edge_finder.pyx 4 | python setup.py build_ext -i 5 | -------------------------------------------------------------------------------- /pyart/correct/rebuild_fourdd_interface.sh: -------------------------------------------------------------------------------- 1 | # remove and rebuild the _fourdd_interface.so module 2 | rm _fourdd_interface.so 3 | cython _fourdd_interface.pyx 4 | python setup.py build_ext -i 5 | -------------------------------------------------------------------------------- /pyart/correct/src/UWASHINGTON_4DD_README: -------------------------------------------------------------------------------- 1 | This software was developed by the Mesoscale Group, Department of Atmospheric 2 | Sciences, University of Washington, Seattle, WA, USA. 3 | 4 | Access and use of this software shall impose the following obligations on 5 | the user. The user is granted the right, without any fee or cost, to use, 6 | copy, modify, alter, enhance, and distribute this software, and any 7 | derivative works thereof, and its supporting documentation for any purpose 8 | whatsoever, except commercial sales, provided that this entire notice appears 9 | in all copies of the software, derivative works, and supporting documentation. 10 | Further, the user agrees to credit the University of Washington in any 11 | publications that result from the use of this software or in any software 12 | package that includes this software. The name University of Washington, 13 | however, may not be used in any advertising or publicity to endorse or 14 | promote any products or commercial entity unless specific written permission 15 | is obtained from the University of Washington. The user also understands 16 | that the University of Washington is not obligated to provide the user with 17 | any support, consulting, training, or assistance of any kind with regard to 18 | the use, operation, and performance of this software nor to provide the user 19 | with any updates, revisions, new versions, or "bug fixes." 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY OF WASHINGTON "AS IS" AND ANY 22 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR 25 | ANY SPECIAL, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER, 26 | INCLUDING BUT NOT LIMITED TO CLAIMS ASSOCIATED WITH THE LOSS OF DATA OR 27 | PROFITS, WHICH MAY RESULT FROM AN ACTION IN CONTRACT, NEGLIGENCE, OR OTHER 28 | TORTIOUS CLAIM THAT ARISES OUT OF OR IN CONNECTION WITH THE ACCESS, USE, OR 29 | PREFORMANCE OF THIS SOFTWARE. 30 | 31 | 32 | Appended by Sandy 16 June 2008 33 | 34 | To build this software under Linux, 35 | edit set_build_env to set 36 | setenv HOST_OS LINUX 37 | 38 | Then from this directory execute build.sh, 39 | resultant executable is FourDD. 40 | -------------------------------------------------------------------------------- /pyart/correct/src/dealias_fourdd.h: -------------------------------------------------------------------------------- 1 | int dealias_fourdd( 2 | Volume* rvVolume, Volume* soundVolume, Volume* lastVolume, 3 | float missingVal, float compthresh, float compthresh2, float thresh, 4 | float ckval, float stdthresh, float epsilon, 5 | int maxcount, int pass2, int rm, int proximity, int mingood, int filt, 6 | int ba_mincount, int ba_edgecount); 7 | -------------------------------------------------------------------------------- /pyart/correct/src/helpers.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | /* Gate value getters */ 5 | 6 | float ray_val(Ray *ray, int index) 7 | { 8 | /* Return the value of range bin at index in ray */ 9 | return (float) ray->h.f(ray->range[index]); 10 | } 11 | 12 | void ray_set(Ray *ray, int index, float val) 13 | { 14 | /* Set the gate value in ray at index to val */ 15 | ray->range[index]=(unsigned short) ray->h.invf(val); 16 | return; 17 | } 18 | -------------------------------------------------------------------------------- /pyart/correct/src/helpers.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** 3 | ** UW Radial Velocity Unfolding Algorithm 4 | ** Four-Dimensional Dealiasing (4DD) 5 | ** 6 | ** DESCRIPTION: 7 | ** This algorithm unfolds a volume of single Doppler radial velocity data. 8 | ** The algorithm uses a previously unfolded volume (or VAD if previous volume 9 | ** is unavailable) and a higher elevation sweep to unfold some of the bins 10 | ** in each sweep. Then, it completes the unfolding, assuming spatial 11 | ** continuity around each bin. 12 | ** 13 | ** DEVELOPER: 14 | ** Curtis N. James 25 Jan 99 15 | ** 16 | */ 17 | 18 | #ifndef FDD_H 19 | #define FDD_H 20 | 21 | #include /* Sweep */ 22 | 23 | float ray_val(Ray *ray, int index); 24 | void ray_set(Ray *ray, int index, float val); 25 | 26 | #endif /* DEALIAS_H */ 27 | -------------------------------------------------------------------------------- /pyart/correct/src/sounding_to_volume.h: -------------------------------------------------------------------------------- 1 | int sounding_to_volume( 2 | Volume* soundVolume, float missingVal, 3 | float *height_array, float *speed_array, float *direction_array, 4 | int nlevels, float maxshear, int sign); 5 | -------------------------------------------------------------------------------- /pyart/exceptions.py: -------------------------------------------------------------------------------- 1 | """ 2 | Custom Py-ART exceptions. 3 | 4 | """ 5 | 6 | import warnings 7 | 8 | 9 | class MissingOptionalDependency(Exception): 10 | """Exception raised when a optional dependency is needed by not found.""" 11 | 12 | pass 13 | 14 | 15 | class DeprecatedAttribute(DeprecationWarning): 16 | """Warning category for an attribute which has been renamed/moved.""" 17 | 18 | pass 19 | 20 | 21 | class DeprecatedFunctionName(DeprecationWarning): 22 | """Warning category for a function which has been renamed/moved.""" 23 | 24 | pass 25 | 26 | 27 | def _deprecated_alias(func, old_name, new_name): 28 | """ 29 | 30 | A function for creating an alias to a renamed or moved function. 31 | 32 | Parameters 33 | ---------- 34 | func : func 35 | The function which has been renamed or moved. 36 | old_name, new_name : str 37 | Name of the function before and after it was moved or renamed 38 | (with namespace if changed). 39 | 40 | Returns 41 | ------- 42 | wrapper : func 43 | A wrapper version of func, which issues a DeprecatedFunctionName 44 | warning when the called. 45 | 46 | """ 47 | 48 | def wrapper(*args, **kwargs): 49 | warnings.warn( 50 | ( 51 | "{0} has been deprecated and will be removed in future " 52 | + "versions of Py-ART, pleases use {1}. " 53 | ).format(old_name, new_name), 54 | category=DeprecatedFunctionName, 55 | ) 56 | return func(*args, **kwargs) 57 | 58 | return wrapper 59 | -------------------------------------------------------------------------------- /pyart/filters/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Classes for specifying what gates are included and excluded from routines. 3 | 4 | """ 5 | 6 | from .gatefilter import GateFilter # noqa 7 | from .gatefilter import iso0_based_gate_filter # noqa 8 | from .gatefilter import moment_and_texture_based_gate_filter # noqa 9 | from .gatefilter import moment_based_gate_filter # noqa 10 | from .gatefilter import temp_based_gate_filter # noqa 11 | 12 | __all__ = [s for s in dir() if not s.startswith("_")] 13 | -------------------------------------------------------------------------------- /pyart/graph/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Creating plots of Radar and Grid fields. 3 | 4 | There are also Radar related colormaps and colorblind friendly radar 5 | colormaps for plotting. 6 | 7 | Available colormaps, reversed versions (_r) are also provided, these 8 | colormaps are available within matplotlib with names 'COLORMAP': 9 | 10 | * BlueBrown10 11 | * BlueBrown11 12 | * BrBu10 13 | * BrBu12 14 | * Bu10 15 | * Bu7 16 | * BuDOr12 17 | * BuDOr18 18 | * BuDRd12 19 | * BuDRd18 20 | * BuGr14 21 | * BuGy8 22 | * BuOr10 23 | * BuOr12 24 | * BuOr8 25 | * BuOrR14 26 | * Carbone11 27 | * Carbone17 28 | * Carbone42 29 | * Cat12 30 | * EWilson17 31 | * GrMg16 32 | * Gray5 33 | * Gray9 34 | * NWSRef 35 | * NWSVel 36 | * NWS_SPW 37 | * PD17 38 | * RRate11 39 | * RdYlBu11b 40 | * RefDiff 41 | * SCook18 42 | * StepSeq25 43 | * SymGray12 44 | * Theodore16 45 | * Wild25 46 | 47 | Colorblind friendly 48 | 49 | * LangRainbow12 50 | * HomeyerRainbow 51 | * balance 52 | * ChaseSpectral 53 | * SpectralExtended 54 | 55 | """ 56 | 57 | # Import colormaps from cmweather 58 | import cmweather # noqa: F401 59 | 60 | from .convstrat_scheme_plot import plot_convstrat_scheme # noqa 61 | from .gridmapdisplay import GridMapDisplay # noqa 62 | from .gridmapdisplay_basemap import GridMapDisplayBasemap # noqa 63 | from .max_cappi import plot_maxcappi # noqa # noqa 64 | from .radardisplay import RadarDisplay # noqa 65 | from .radardisplay_airborne import AirborneRadarDisplay # noqa 66 | from .radarmapdisplay import RadarMapDisplay # noqa 67 | from .radarmapdisplay_basemap import RadarMapDisplayBasemap # noqa 68 | 69 | __all__ = [s for s in dir() if not s.startswith("_")] 70 | -------------------------------------------------------------------------------- /pyart/io/__init__.pxd: -------------------------------------------------------------------------------- 1 | from pyart.correct cimport * 2 | from pyart.io cimport _rsl_h, _rsl_interface 3 | -------------------------------------------------------------------------------- /pyart/io/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Functions to read and write radar and grid data to and from a number of file 3 | formats. 4 | 5 | In most cases the :py:func:`pyart.io.read` function should be used to read 6 | in radar data from a file. In certain cases the function the read function 7 | for the format in question should be used. 8 | 9 | """ 10 | 11 | from .arm_sonde import read_arm_sonde, read_arm_sonde_vap # noqa 12 | from .auto_read import read # noqa 13 | from .cfradial import read_cfradial, write_cfradial # noqa 14 | from .chl import read_chl # noqa 15 | from .common import prepare_for_read # noqa 16 | from .grid_io import read_grid, write_grid # noqa 17 | from .mdv_grid import read_grid_mdv, write_grid_mdv # noqa 18 | from .mdv_radar import read_mdv # noqa 19 | from .nexrad_archive import read_nexrad_archive # noqa 20 | from .nexrad_cdm import read_nexrad_cdm # noqa 21 | from .nexradl3_read import read_nexrad_level3 # noqa 22 | from .output_to_geotiff import write_grid_geotiff # noqa 23 | from .rsl import read_rsl # noqa 24 | from .sigmet import read_sigmet # noqa 25 | from .uf import read_uf # noqa 26 | from .uf_write import write_uf # noqa 27 | 28 | __all__ = [s for s in dir() if not s.startswith("_")] 29 | -------------------------------------------------------------------------------- /pyart/io/_rsl_interface.pxd: -------------------------------------------------------------------------------- 1 | """ Shared extensions in _rsl_interface, used by _fourdd_interface. """ 2 | 3 | 4 | cimport pyart.io._rsl_h as _rsl_h 5 | 6 | 7 | cdef class _RslVolume: 8 | cdef _rsl_h.Volume * _Volume 9 | cdef int _dealloc 10 | cdef load(self, _rsl_h.Volume * Volume) 11 | cdef _prtmode(self, _rsl_h.Ray_header h) 12 | -------------------------------------------------------------------------------- /pyart/io/rebuild_rsl_interface.sh: -------------------------------------------------------------------------------- 1 | # remove and rebuild _rsl_interface.so and _fourdd_interface.so modules 2 | rm _rsl_interface.so 3 | cython _rsl_interface.pyx 4 | python setup.py build_ext -i 5 | -------------------------------------------------------------------------------- /pyart/io/rebuild_sigmetfile.sh: -------------------------------------------------------------------------------- 1 | # remove and rebuild _sigmetfile.so module 2 | rm _sigmetfile.so 3 | cython _sigmetfile.pyx 4 | python setup.py build_ext -i 5 | -------------------------------------------------------------------------------- /pyart/map/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Py-ART has a robust function for mapping radar data from the collected radar 3 | coordinates to Cartesian coordinates. 4 | 5 | """ 6 | 7 | from .gate_mapper import GateMapper # noqa 8 | from .gates_to_grid import map_gates_to_grid # noqa 9 | from .grid_mapper import example_roi_func_constant # noqa 10 | from .grid_mapper import example_roi_func_dist # noqa 11 | from .grid_mapper import example_roi_func_dist_beam # noqa 12 | from .grid_mapper import grid_from_radars # noqa 13 | from .grid_mapper import map_to_grid # noqa 14 | from .grid_mapper import grid_ppi_sweeps, grid_rhi_sweeps # noqa 15 | 16 | __all__ = [s for s in dir() if not s.startswith("_")] 17 | -------------------------------------------------------------------------------- /pyart/map/_load_nn_field_data.pyx: -------------------------------------------------------------------------------- 1 | cimport cython 2 | 3 | 4 | @cython.boundscheck(False) 5 | def _load_nn_field_data(object[:, :] data, int nfields, int npoints, 6 | int[:] r_nums, int[:] e_nums, double[:, :] sdata): 7 | """ 8 | _load_nn_field_data(data, nfields, npoints, r_nums, e_nums, sdata) 9 | 10 | Load the nearest neighbor field data into sdata 11 | """ 12 | cdef unsigned int i, j, r_num, e_num 13 | 14 | for i in range(npoints): 15 | r_num = r_nums[i] 16 | e_num = e_nums[i] 17 | for j in range(nfields): 18 | # if we knew the dtype of data[j, r_num] we could speed this 19 | # up with a memory view, but we can't guarantee the dtype without 20 | # making a copy 21 | sdata[i, j] = data[j, r_num][e_num] 22 | return 23 | -------------------------------------------------------------------------------- /pyart/map/rebuild_ball_tree.sh: -------------------------------------------------------------------------------- 1 | # remove and rebuild the ball_tree.so module 2 | rm ball_tree.so 3 | cython ball_tree.pyx 4 | python setup.py build_ext -i 5 | -------------------------------------------------------------------------------- /pyart/map/rebuild_ckdtree.sh: -------------------------------------------------------------------------------- 1 | # remove and rebuild the ball_tree.so module 2 | rm ckdtree.so 3 | cython ckdtree.pyx 4 | python setup.py build_ext -i 5 | -------------------------------------------------------------------------------- /pyart/map/rebuild_gate_to_grid_map.sh: -------------------------------------------------------------------------------- 1 | # remove and rebuild the _load_nn_field_data module 2 | rm _gate_to_grid_map.so 3 | cython _gate_to_grid_map.pyx 4 | python setup.py build_ext -i 5 | -------------------------------------------------------------------------------- /pyart/map/rebuild_load_nn_field_data.sh: -------------------------------------------------------------------------------- 1 | # remove and rebuild the _load_nn_field_data module 2 | rm _load_nn_field_data.so 3 | cython _load_nn_field_data.pyx 4 | python setup.py build_ext -i 5 | -------------------------------------------------------------------------------- /pyart/retrieve/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Radar retrievals. 3 | 4 | """ 5 | 6 | from .advection import grid_displacement_pc, grid_shift # noqa 7 | from .comp_z import composite_reflectivity # noqa 8 | from .echo_class import feature_detection # noqa 9 | from .echo_class import conv_strat_yuter # noqa 10 | from .echo_class import get_freq_band # noqa 11 | from .echo_class import hydroclass_semisupervised # noqa 12 | from .echo_class import steiner_conv_strat # noqa 13 | from .echo_class import conv_strat_raut # noqa 14 | from .gate_id import fetch_radar_time_profile, map_profile_to_gates # noqa 15 | from .kdp_proc import kdp_maesaka, kdp_schneebeli, kdp_vulpiani # noqa 16 | from .qpe import est_rain_rate_a # noqa 17 | from .qpe import est_rain_rate_hydro # noqa 18 | from .qpe import est_rain_rate_kdp # noqa 19 | from .qpe import est_rain_rate_z # noqa 20 | from .qpe import est_rain_rate_za # noqa 21 | from .qpe import est_rain_rate_zkdp # noqa 22 | from .qpe import est_rain_rate_zpoly # noqa 23 | from .qpe import ZtoR # noqa 24 | from .qvp import quasi_vertical_profile, compute_qvp, compute_rqvp # noqa 25 | from .qvp import compute_evp, compute_svp, compute_vp, compute_ts_along_coord # noqa 26 | from .simple_moment_calculations import calculate_snr_from_reflectivity # noqa 27 | from .simple_moment_calculations import calculate_velocity_texture # noqa 28 | from .simple_moment_calculations import compute_cdr # noqa 29 | from .simple_moment_calculations import compute_l # noqa 30 | from .simple_moment_calculations import compute_noisedBZ # noqa 31 | from .simple_moment_calculations import compute_snr # noqa 32 | from .spectra_calculations import dealias_spectra, spectra_moments # noqa 33 | from .vad import vad_browning, vad_michelson # noqa 34 | from .cfad import create_cfad # noqa 35 | from .cappi import create_cappi # noqa 36 | from .srv import storm_relative_velocity # noqa 37 | 38 | __all__ = [s for s in dir() if not s.startswith("_")] 39 | -------------------------------------------------------------------------------- /pyart/retrieve/rebuild_kdp_proc.sh: -------------------------------------------------------------------------------- 1 | # remove and rebuild the _kdp_proc.so module 2 | rm -fv _kdp_proc.so 3 | cython _kdp_proc.pyx 4 | python setup.py build_ext -i 5 | -------------------------------------------------------------------------------- /pyart/testing/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Utilities helpful when writing and running unit tests such as sample files 3 | and sample objects. 4 | 5 | """ 6 | 7 | from .example_data import get_test_data # noqa 8 | from .sample_files import * # noqa 9 | from .sample_objects import * # noqa 10 | from .tmpdirs import InTemporaryDirectory # noqa 11 | 12 | __all__ = [s for s in dir() if not s.startswith("_")] 13 | -------------------------------------------------------------------------------- /pyart/testing/data/README: -------------------------------------------------------------------------------- 1 | This directory contains small example files for some of the radar formats 2 | supported by Py-ART. This files are used to test the functionality of Py-ART 3 | and should not be used as real radar data sets. 4 | 5 | This directory should not be used directly, rather use the functionality of 6 | the various pyart.testing functions and modules. 7 | 8 | These files can be recreated from original radar files using the 9 | make_small_* scripts. These original radar files are not included in the 10 | Py-ART as they are quite large in size. 11 | 12 | Plots of these example files can be made using the quick_plot_* scripts. 13 | -------------------------------------------------------------------------------- /pyart/testing/data/baseline_figures/example_cfradial_cr_raster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/pyart/testing/data/baseline_figures/example_cfradial_cr_raster.png -------------------------------------------------------------------------------- /pyart/testing/data/baseline_figures/example_cfradial_ppi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/pyart/testing/data/baseline_figures/example_cfradial_ppi.png -------------------------------------------------------------------------------- /pyart/testing/data/baseline_figures/example_cfradial_rhi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/pyart/testing/data/baseline_figures/example_cfradial_rhi.png -------------------------------------------------------------------------------- /pyart/testing/data/baseline_figures/example_mdv_ppi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/pyart/testing/data/baseline_figures/example_mdv_ppi.png -------------------------------------------------------------------------------- /pyart/testing/data/baseline_figures/example_mdv_rhi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/pyart/testing/data/baseline_figures/example_mdv_rhi.png -------------------------------------------------------------------------------- /pyart/testing/data/baseline_figures/example_sigmet_ppi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/pyart/testing/data/baseline_figures/example_sigmet_ppi.png -------------------------------------------------------------------------------- /pyart/testing/data/baseline_figures/example_sigmet_rhi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/pyart/testing/data/baseline_figures/example_sigmet_rhi.png -------------------------------------------------------------------------------- /pyart/testing/data/check_nexrad_dummy.py: -------------------------------------------------------------------------------- 1 | # check that the dummy NEXRAD file is simlar to non-dummy file. 2 | 3 | 4 | import pyart 5 | 6 | NEXRAD_FILE = "KATX20130717_195021_V06" 7 | OUTPUT_FILE = "KATX20130717_195021_V06_DUMMY" 8 | 9 | 10 | def test_dummy_similar(): 11 | radar1 = pyart.io.read_nexrad_archive(NEXRAD_FILE) 12 | radar2 = pyart.io.read_nexrad_archive(OUTPUT_FILE) 13 | assert radars_similar(radar1, radar2) 14 | 15 | 16 | def radars_similar(r1, r2): 17 | ########################### 18 | # Attribute that are None # 19 | ########################### 20 | assert dics_similar(r1.altitude_agl, r2.altitude_agl) 21 | assert dics_similar(r1.target_scan_rate, r2.target_scan_rate) 22 | assert dics_similar(r1.scan_rate, r2.scan_rate) 23 | assert dics_similar(r1.antenna_transition, r2.antenna_transition) 24 | assert dics_similar(r1.radar_calibration, r2.radar_calibration) 25 | 26 | ######################### 27 | # Dictionary attributes # 28 | ######################### 29 | # assert dics_similar(r1.time, r2.time) # start time mismatch 30 | assert dics_similar(r1.range, r2.range) 31 | # assert dics_similar(r1.metadata, r2.metadata) # DO not match 32 | 33 | assert dics_similar(r1.latitude, r2.latitude) 34 | assert dics_similar(r1.longitude, r2.longitude) 35 | # assert dics_similar(r1.altitude, r2.altitude) # differ by 10 meters 36 | 37 | assert dics_similar(r1.sweep_number, r2.sweep_number) 38 | assert dics_similar(r1.sweep_mode, r2.sweep_mode) 39 | assert dics_similar(r1.fixed_angle, r2.fixed_angle) 40 | assert dics_similar(r1.sweep_start_ray_index, r2.sweep_start_ray_index) 41 | assert dics_similar(r1.sweep_end_ray_index, r2.sweep_end_ray_index) 42 | 43 | assert dics_similar(r1.azimuth, r2.azimuth) 44 | assert dics_similar(r1.elevation, r2.elevation) 45 | 46 | ########### 47 | # scalars # 48 | ########### 49 | 50 | assert r1.ngates == r2.ngates 51 | assert r1.nrays == r2.nrays 52 | assert r1.nsweeps == r2.nsweeps 53 | assert r1.scan_type == r2.scan_type 54 | 55 | ########## 56 | # fields # 57 | ########## 58 | 59 | print(r1.fields.keys()) 60 | print(r2.fields.keys()) 61 | assert set(r1.fields.keys()).difference(r2.fields.keys()) == set() 62 | 63 | for field in r1.fields: 64 | print(field) 65 | assert dics_similar(r1.fields[field], r2.fields[field]) 66 | 67 | # radar1.fields 68 | return True 69 | 70 | 71 | def dics_similar(dic1, dic2): 72 | """Determine if two dictionaries are similar.""" 73 | if dic1 is None: 74 | if dic2 is None: 75 | return True 76 | else: 77 | return False 78 | 79 | # all keys in both dictionaries 80 | print(dic1.keys()) 81 | print(dic2.keys()) 82 | assert set(dic1.keys()).difference(dic2.keys()) == set() 83 | 84 | for key in dic1.keys(): 85 | if key == "data": 86 | continue 87 | print(key, dic1[key], dic2[key]) 88 | assert dic1[key] == dic2[key] 89 | 90 | # do not check data, since it should not match 91 | return True 92 | -------------------------------------------------------------------------------- /pyart/testing/data/dummify_nexrad_file.py: -------------------------------------------------------------------------------- 1 | # /usr/bin/env python 2 | # dummify (replace all data with a single value) a NEXRAD Level II file. 3 | 4 | 5 | import numpy as np 6 | 7 | import pyart.io.nexrad_level2 as nexrad 8 | 9 | NEXRAD_FILE = "KATX20130717_195021_V06" 10 | OUTPUT_FILE = "KATX20130717_195021_V06_DUMMY" 11 | 12 | # sizes of structures 13 | RECORD_SIZE = 2432 14 | MSG_HEADER_SIZE = 16 15 | DATA_BLOCK_SIZE = 28 16 | 17 | fin = open(NEXRAD_FILE, "rb") 18 | out = open(OUTPUT_FILE, "wb") 19 | 20 | # read in the first 134 records as write as is 21 | out.write(fin.read(24 + 12 + RECORD_SIZE * 134)) 22 | 23 | # read the rest of the file, and create a array of int8 24 | buf = fin.read() 25 | buf_length = len(buf) 26 | buf_data = np.frombuffer(buf, dtype="i1") 27 | fin.close() 28 | 29 | # replace all radial data with a single value so it compresses well 30 | pos = 0 31 | while pos < buf_length: 32 | # retrieve the record (msg31) 33 | new_pos, record = nexrad._get_record_from_buf(buf, pos) 34 | if record["header"]["type"] != 31: 35 | pos += RECORD_SIZE 36 | continue 37 | 38 | # dummy out all moments 39 | for bp_number in [4, 5, 6, 7, 8, 9]: 40 | bp_str = "block_pointer_" + str(bp_number) 41 | block_pointer = record["msg_header"][bp_str] 42 | if block_pointer != 0: 43 | bpos = pos + block_pointer + MSG_HEADER_SIZE 44 | d = nexrad._unpack_from_buf(buf, bpos, nexrad.GENERIC_DATA_BLOCK) 45 | points = d["ngates"] 46 | if d["data_name"] == "PHI": 47 | points *= 2 48 | start = pos + MSG_HEADER_SIZE + DATA_BLOCK_SIZE + block_pointer 49 | buf_data[start : start + points] = 2 50 | 51 | pos = new_pos 52 | 53 | # write out the modified data 54 | out.write(buf_data.tostring()) 55 | out.close() 56 | -------------------------------------------------------------------------------- /pyart/testing/data/example_arm_sonde.cdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/pyart/testing/data/example_arm_sonde.cdf -------------------------------------------------------------------------------- /pyart/testing/data/example_cfradial_cr_raster.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/pyart/testing/data/example_cfradial_cr_raster.nc -------------------------------------------------------------------------------- /pyart/testing/data/example_cfradial_ppi.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/pyart/testing/data/example_cfradial_ppi.nc -------------------------------------------------------------------------------- /pyart/testing/data/example_cfradial_rhi.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/pyart/testing/data/example_cfradial_rhi.nc -------------------------------------------------------------------------------- /pyart/testing/data/example_chl_rhi.chl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/pyart/testing/data/example_chl_rhi.chl -------------------------------------------------------------------------------- /pyart/testing/data/example_interpolatedsonde.cdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/pyart/testing/data/example_interpolatedsonde.cdf -------------------------------------------------------------------------------- /pyart/testing/data/example_mdv_grid.mdv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/pyart/testing/data/example_mdv_grid.mdv -------------------------------------------------------------------------------- /pyart/testing/data/example_mdv_ppi.mdv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/pyart/testing/data/example_mdv_ppi.mdv -------------------------------------------------------------------------------- /pyart/testing/data/example_mdv_rhi.mdv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/pyart/testing/data/example_mdv_rhi.mdv -------------------------------------------------------------------------------- /pyart/testing/data/example_nexrad_archive_msg1.bz2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/pyart/testing/data/example_nexrad_archive_msg1.bz2 -------------------------------------------------------------------------------- /pyart/testing/data/example_nexrad_archive_msg31.bz2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/pyart/testing/data/example_nexrad_archive_msg31.bz2 -------------------------------------------------------------------------------- /pyart/testing/data/example_nexrad_archive_msg31_compressed.ar2v: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/pyart/testing/data/example_nexrad_archive_msg31_compressed.ar2v -------------------------------------------------------------------------------- /pyart/testing/data/example_nexrad_cdm.bz2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/pyart/testing/data/example_nexrad_cdm.bz2 -------------------------------------------------------------------------------- /pyart/testing/data/example_nexrad_level3_msg163: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/pyart/testing/data/example_nexrad_level3_msg163 -------------------------------------------------------------------------------- /pyart/testing/data/example_nexrad_level3_msg176: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/pyart/testing/data/example_nexrad_level3_msg176 -------------------------------------------------------------------------------- /pyart/testing/data/example_nexrad_level3_msg19: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/pyart/testing/data/example_nexrad_level3_msg19 -------------------------------------------------------------------------------- /pyart/testing/data/example_rays.npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/pyart/testing/data/example_rays.npz -------------------------------------------------------------------------------- /pyart/testing/data/example_sigmet_ppi.sigmet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/pyart/testing/data/example_sigmet_ppi.sigmet -------------------------------------------------------------------------------- /pyart/testing/data/example_sigmet_rhi.sigmet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/pyart/testing/data/example_sigmet_rhi.sigmet -------------------------------------------------------------------------------- /pyart/testing/data/example_uf_ppi.uf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/pyart/testing/data/example_uf_ppi.uf -------------------------------------------------------------------------------- /pyart/testing/data/make_single_ray.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """ 3 | Extract a single ray from a C-SAPR PPI volume and store in a .npz file. 4 | 5 | """ 6 | 7 | import numpy as np 8 | 9 | import pyart 10 | 11 | # read in the full radar 12 | radar = pyart.io.read("095636.mdv") 13 | 14 | # extract ray 191 from the requested fields 15 | fields_to_extract = [ 16 | "reflectivity", 17 | "normalized_coherent_power", 18 | "cross_correlation_ratio", 19 | "specific_differential_phase", 20 | "differential_phase", 21 | "differential_reflectivity", 22 | ] 23 | ray_dic = dict() 24 | for field_name in fields_to_extract: 25 | ray = radar.fields[field_name]["data"].data[191] 26 | ray = ray.reshape(1, -1) 27 | ray_dic[field_name] = ray 28 | 29 | # save into a npz file 30 | np.savez("example_rays.npz", **ray_dic) 31 | -------------------------------------------------------------------------------- /pyart/testing/data/make_small_arm_sonde.sh: -------------------------------------------------------------------------------- 1 | # rename the file 2 | cp ./sgpsondewnpnC1.b1.20110520.082800.cdf example_arm_sonde.cdf 3 | -------------------------------------------------------------------------------- /pyart/testing/data/make_small_cfradial_ppi.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """ 3 | Make a small netCDF CF/Radial file containing a single PPI scan. 4 | 5 | Single field and scan is converted from sigmet file XSW110520105408.RAW7HHF 6 | """ 7 | 8 | import pyart 9 | 10 | radar = pyart.io.read_rsl("XSW110520105408.RAW7HHF") 11 | 12 | time_slice = slice(None, 400, 10) 13 | range_slice = slice(None, None, 16) 14 | sweep_slice = slice(None, 1) 15 | 16 | # remove all but the reflectivity_horizontal fields 17 | rf_field = radar.fields["reflectivity"] 18 | rf_data = rf_field["data"] 19 | rf_field["data"] = rf_data[time_slice, range_slice] 20 | radar.fields = {"reflectivity_horizontal": rf_field} 21 | 22 | radar.nsweeps = 1 23 | radar.nray = 40 24 | radar.ngates = 42 25 | 26 | # truncate the range based variables 27 | radar.range["data"] = radar.range["data"][range_slice] 28 | 29 | # truncate the time based variables 30 | radar.time["data"] = radar.time["data"][time_slice] 31 | radar.azimuth["data"] = radar.azimuth["data"][time_slice] 32 | radar.elevation["data"] = radar.elevation["data"][time_slice] 33 | radar.instrument_parameters["prt"]["data"] = radar.instrument_parameters["prt"]["data"][ 34 | time_slice 35 | ] 36 | 37 | radar.instrument_parameters["unambiguous_range"]["data"] = radar.instrument_parameters[ 38 | "unambiguous_range" 39 | ]["data"][time_slice] 40 | 41 | radar.instrument_parameters["nyquist_velocity"]["data"] = radar.instrument_parameters[ 42 | "nyquist_velocity" 43 | ]["data"][time_slice] 44 | 45 | # truncate the sweep based variables 46 | radar.sweep_number["data"] = radar.sweep_number["data"][sweep_slice] 47 | radar.fixed_angle["data"] = radar.fixed_angle["data"][sweep_slice] 48 | radar.sweep_start_ray_index["data"] = radar.sweep_start_ray_index["data"][sweep_slice] 49 | radar.sweep_end_ray_index["data"] = radar.sweep_end_ray_index["data"][sweep_slice] 50 | radar.sweep_end_ray_index["data"][0] = 39 51 | radar.sweep_mode["data"] = radar.sweep_mode["data"][sweep_slice] 52 | 53 | radar.sweep_number["data"] = radar.sweep_number["data"][sweep_slice] 54 | 55 | radar.instrument_parameters["prt_mode"]["data"] = radar.instrument_parameters[ 56 | "prt_mode" 57 | ]["data"][sweep_slice] 58 | 59 | # adjust metadata 60 | radar.metadata = { 61 | "Conventions": "CF/Radial instrument_parameters", 62 | "version": "1.2", 63 | "title": "Py-ART Example PPI CF/Radial file", 64 | "institution": ( 65 | "United States Department of Energy - Atmospheric " 66 | "Radiation Measurement (ARM) program" 67 | ), 68 | "references": "none", 69 | "source": "ARM SGP XSAPR Radar", 70 | "history": "created by jhelmus on evs348532 at 2013-05-22T12:34:56", 71 | "comment": "none", 72 | "instrument_name": "xsapr-sgp", 73 | } 74 | 75 | pyart.io.write_cfradial("example_cfradial_ppi.nc", radar) 76 | -------------------------------------------------------------------------------- /pyart/testing/data/make_small_cfradial_rhi.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """ 3 | Make a small netCDF CF/Radial file containing a single RHI scan. 4 | 5 | Single field and scan is converted from sigmet file XSW110520113537.RAW7HHL 6 | """ 7 | 8 | import pyart 9 | 10 | radar = pyart.io.read_rsl("XSW110520113537.RAW7HHL") 11 | 12 | time_slice = slice(None, 713, 18) 13 | range_slice = slice(None, None, 12) 14 | sweep_slice = slice(None, 1) 15 | 16 | # remove all but the reflectivity_horizontal fields 17 | rf_field = radar.fields["reflectivity"] 18 | rf_data = rf_field["data"] 19 | rf_field["data"] = rf_data[time_slice, range_slice] 20 | radar.fields = {"reflectivity_horizontal": rf_field} 21 | 22 | radar.nsweeps = 1 23 | radar.nray = 40 24 | radar.ngates = 45 25 | 26 | # truncate the range based variables 27 | radar.range["data"] = radar.range["data"][range_slice] 28 | 29 | # truncate the time based variables 30 | radar.time["data"] = radar.time["data"][time_slice] 31 | radar.azimuth["data"] = radar.azimuth["data"][time_slice] 32 | radar.elevation["data"] = radar.elevation["data"][time_slice] 33 | radar.instrument_parameters["prt"]["data"] = radar.instrument_parameters["prt"]["data"][ 34 | time_slice 35 | ] 36 | 37 | radar.instrument_parameters["unambiguous_range"]["data"] = radar.instrument_parameters[ 38 | "unambiguous_range" 39 | ]["data"][time_slice] 40 | 41 | radar.instrument_parameters["nyquist_velocity"]["data"] = radar.instrument_parameters[ 42 | "nyquist_velocity" 43 | ]["data"][time_slice] 44 | 45 | # truncate the sweep based variables 46 | radar.sweep_number["data"] = radar.sweep_number["data"][sweep_slice] 47 | radar.fixed_angle["data"] = radar.fixed_angle["data"][sweep_slice] 48 | radar.sweep_start_ray_index["data"] = radar.sweep_start_ray_index["data"][sweep_slice] 49 | radar.sweep_end_ray_index["data"] = radar.sweep_end_ray_index["data"][sweep_slice] 50 | radar.sweep_end_ray_index["data"][0] = 39 51 | radar.sweep_mode["data"] = radar.sweep_mode["data"][sweep_slice] 52 | 53 | radar.sweep_number["data"] = radar.sweep_number["data"][sweep_slice] 54 | 55 | radar.instrument_parameters["prt_mode"]["data"] = radar.instrument_parameters[ 56 | "prt_mode" 57 | ]["data"][sweep_slice] 58 | 59 | # adjust metadata 60 | radar.metadata = { 61 | "Conventions": "CF/Radial instrument_parameters", 62 | "version": "1.2", 63 | "title": "Py-ART Example RHI CF/Radial file", 64 | "institution": ( 65 | "United States Department of Energy - Atmospheric " 66 | "Radiation Measurement (ARM) program" 67 | ), 68 | "references": "none", 69 | "source": "ARM SGP XSAPR Radar", 70 | "history": "created by jhelmus on evs348532 at 2013-05-22T12:34:56", 71 | "comment": "none", 72 | "instrument_name": "xsapr-sgp", 73 | } 74 | 75 | pyart.io.write_cfradial("example_cfradial_rhi.nc", radar) 76 | -------------------------------------------------------------------------------- /pyart/testing/data/make_small_chl_rhi.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """ 3 | Make a small CSU-CHILL, CHL file containing the first ray in each sweep 4 | from a two scan RHI volume. 5 | 6 | All field data is left untouched. 7 | """ 8 | 9 | 10 | # parameters 11 | INFILE = "CHL20120705_230123" 12 | OUTFILE = "example_chl_rhi.chl" 13 | 14 | # open the input and output files 15 | f = open(INFILE, "rb") 16 | out = open(OUTFILE, "wb") 17 | 18 | # FILE_HDR block (56 bytes) 19 | out.write(f.read(56)) 20 | 21 | # 30 FIELD_SCALE block (232 bytes echo, 6960 bytes total) 22 | out.write(f.read(6960)) 23 | 24 | # RADAR_INFO (128 bytes) 25 | out.write(f.read(128)) 26 | 27 | # PROCESSOR_INFO (88 bytes) 28 | out.write(f.read(88)) 29 | 30 | # UNKNOWN block (84 bytes) 31 | out.write(f.read(84)) 32 | 33 | # SCAN_SEG block (140 bytes) 34 | out.write(f.read(140)) 35 | 36 | # UNKNOWN block (128 bytes) 37 | out.write(f.read(128)) 38 | 39 | # RAY_HDR and data (64056 bytes) 40 | out.write(f.read(64056)) 41 | 42 | # skip to second scan 43 | f.seek(2841480) 44 | 45 | # RADAR_INFO block 46 | out.write(f.read(128)) 47 | 48 | # SCAN_SEG block 49 | out.write(f.read(140)) 50 | 51 | # UNKNOWN block 52 | out.write(f.read(16)) 53 | 54 | # UNKNOWN block 55 | out.write(f.read(128)) 56 | 57 | # UNKNOWN block 58 | out.write(f.read(2072)) 59 | 60 | # RAY_HDR and data (64056 bytes) 61 | out.write(f.read(64056)) 62 | 63 | # skip to SWEEP_BLOCK (44 bytes at end of file) 64 | f.seek(5677548) 65 | out.write(f.read(44)) 66 | 67 | # close the files 68 | f.close() 69 | out.close() 70 | -------------------------------------------------------------------------------- /pyart/testing/data/make_small_interp_sonde.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | import netCDF4 4 | 5 | dset = netCDF4.Dataset("sgpinterpolatedsondeC1.c1.20110510.000000.cdf") 6 | dvars = dset.variables 7 | 8 | out = netCDF4.Dataset("example_interpolatedsonde.cdf", mode="w") 9 | 10 | out.createDimension("time", None) 11 | out.createDimension("height", 316) 12 | 13 | time = out.createVariable("time", dvars["time"].dtype, ("time")) 14 | height = out.createVariable("height", dvars["height"].dtype, ("height")) 15 | wspd = out.createVariable("wspd", dvars["wspd"].dtype, ("time", "height")) 16 | wdir = out.createVariable("wdir", dvars["wdir"].dtype, ("time", "height")) 17 | 18 | time[:] = dvars["time"][685:695] 19 | time.units = dvars["time"].units 20 | height[:] = dvars["height"][:] 21 | wspd[:] = dvars["wspd"][685:695, :] 22 | wdir[:] = dvars["wdir"][685:695, :] 23 | 24 | out.close() 25 | -------------------------------------------------------------------------------- /pyart/testing/data/make_small_mdv_grid.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """ 3 | Make a small MDV file containing a grid with X and Y axes in degrees. 4 | 5 | Partial grid is taken from full sized file 000000.mdv which is contained in 6 | the 200202.MASTER15.mdv.tar.gz file available online at: 7 | http://www2.mmm.ucar.edu/imagearchive/WSI/mdv/ 8 | """ 9 | # The MDV RLE decoding routine ends at the end of the data stream even if not 10 | # all data point have been read. Therefore a file truncated at a break in the 11 | # RLE can still be read but data past the last data point will be filled with 12 | # random data. Here we end after the first non-keyed RLE byte. 13 | infile = open("000000.mdv", "rb") 14 | outfile = open("example_mdv_grid.mdv", "wb") 15 | outfile.write(infile.read(8134)) 16 | infile.close() 17 | outfile.close() 18 | -------------------------------------------------------------------------------- /pyart/testing/data/make_small_nexrad_archive_msg1.sh: -------------------------------------------------------------------------------- 1 | # compress the file 2 | rm KLOT20030101_000921.bz2 3 | bzip2 -z --best -k KLOT20030101_000921 4 | 5 | # rename the file 6 | mv KLOT20030101_000921.bz2 example_nexrad_archive_msg1.bz2 7 | -------------------------------------------------------------------------------- /pyart/testing/data/make_small_nexrad_archive_msg31.sh: -------------------------------------------------------------------------------- 1 | # create the DUMMY file 2 | python dummify_nexrad_file.py 3 | 4 | # test the dummy file 5 | nosetests -v check_nexrad_dummy.py 6 | 7 | # compress the file 8 | rm KATX20130717_195021_V06_DUMMY.bz2 9 | bzip2 -z --best KATX20130717_195021_V06_DUMMY 10 | 11 | # rename the file 12 | mv KATX20130717_195021_V06_DUMMY.bz2 example_nexrad_archive_msg31.bz2 13 | -------------------------------------------------------------------------------- /pyart/testing/data/make_small_nexrad_archive_msg31_compressed.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """ 3 | Make a small NEXRAD (WSR-88D) Level II file with compressed records. 4 | 5 | The file created by this script only contains the first 254 records 6 | (120 radials) of the full sized file. It should not be used as a full sized 7 | example, only for testing the decompression routines. 8 | """ 9 | 10 | f = open("Level2_KATX_20130717_1950.ar2v", "rb") 11 | o = open("example_nexrad_archive_msg31_compressed.ar2v", "wb") 12 | 13 | o.write(f.read(24)) # volume header 14 | o.write(f.read(12527 + 4)) # first series of compressed records 15 | o.write(f.read(105727 + 4)) # second series of compressed records 16 | o.close() 17 | -------------------------------------------------------------------------------- /pyart/testing/data/make_small_nexrad_cdm.sh: -------------------------------------------------------------------------------- 1 | # create the DUMMY file 2 | python dummify_nexrad_file.py 3 | 4 | # test the dummy file 5 | nosetests -v check_nexrad_dummy.py 6 | 7 | # convert to CDM file using toolsUI 8 | java -classpath toolsUI-4.3.jar ucar.nc2.FileWriter \ 9 | -in KATX20130717_195021_V06_DUMMY -out example_nexrad_cdm 10 | rm KATX20130717_195021_V06_DUMMY 11 | 12 | # compress 13 | rm example_nexrad_cdm.bz2 14 | bzip2 -z --best example_nexrad_cdm 15 | -------------------------------------------------------------------------------- /pyart/testing/data/make_small_uf.sh: -------------------------------------------------------------------------------- 1 | /usr/local/trmm/bin/any_to_uf XSW110520105408.RAW7HHF full.uf 2 | head -c 16648 full.uf > example_uf_ppi.uf 3 | rm full.uf 4 | -------------------------------------------------------------------------------- /pyart/testing/data/quick_plot_cfradial_ppi.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | import matplotlib.pyplot as plt 3 | 4 | import pyart 5 | 6 | # plot quickly 7 | pradar = pyart.io.read_cfradial("example_cfradial_ppi.nc") 8 | display = pyart.graph.RadarDisplay(pradar) 9 | fig = plt.figure() 10 | ax = fig.add_subplot(111) 11 | display.plot_ppi("reflectivity_horizontal", 0, vmin=-16, vmax=64) 12 | fig.savefig("example_cfradial_ppi.png") 13 | -------------------------------------------------------------------------------- /pyart/testing/data/quick_plot_cfradial_rhi.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | import matplotlib.pyplot as plt 3 | 4 | import pyart 5 | 6 | # plot quickly 7 | pradar = pyart.io.read_cfradial("example_cfradial_rhi.nc") 8 | display = pyart.graph.RadarDisplay(pradar) 9 | fig = plt.figure() 10 | ax = fig.add_subplot(111) 11 | display.plot_rhi("reflectivity_horizontal", 0, vmin=-16, vmax=64) 12 | fig.savefig("example_cfradial_rhi.png") 13 | -------------------------------------------------------------------------------- /pyart/testing/data/quick_plot_mdv_ppi.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | import matplotlib.pyplot as plt 3 | 4 | import pyart 5 | 6 | # plot quickly 7 | pradar = pyart.io.read_mdv("example_mdv_ppi.mdv") 8 | display = pyart.graph.RadarDisplay(pradar) 9 | fig = plt.figure() 10 | ax = fig.add_subplot(111) 11 | display.plot_ppi("reflectivity_horizontal", 0, vmin=-16, vmax=64) 12 | fig.savefig("example_mdv_ppi.png") 13 | -------------------------------------------------------------------------------- /pyart/testing/data/quick_plot_mdv_rhi.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | import matplotlib.pyplot as plt 3 | 4 | import pyart 5 | 6 | # plot quickly 7 | pradar = pyart.io.read_mdv("example_mdv_rhi.mdv") 8 | display = pyart.graph.RadarDisplay(pradar) 9 | fig = plt.figure() 10 | ax = fig.add_subplot(111) 11 | display.plot_rhi("reflectivity_horizontal", 0, vmin=-16, vmax=64) 12 | fig.savefig("example_mdv_rhi.png") 13 | -------------------------------------------------------------------------------- /pyart/testing/data/quick_plot_sigmet_ppi.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | import matplotlib.pyplot as plt 3 | 4 | import pyart 5 | 6 | # plot quickly 7 | pradar = pyart.io.read_rsl("example_sigmet_ppi.sigmet") 8 | display = pyart.graph.RadarDisplay(pradar) 9 | fig = plt.figure() 10 | ax = fig.add_subplot(111) 11 | display.plot_ppi("reflectivity_horizontal_filtered", 0, vmin=-16, vmax=64) 12 | fig.savefig("example_sigmet_ppi.png") 13 | -------------------------------------------------------------------------------- /pyart/testing/data/quick_plot_sigmet_rhi.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | import matplotlib.pyplot as plt 3 | 4 | import pyart 5 | 6 | # plot quickly 7 | pradar = pyart.io.read_rsl("example_sigmet_rhi.sigmet") 8 | display = pyart.graph.RadarDisplay(pradar) 9 | fig = plt.figure() 10 | ax = fig.add_subplot(111) 11 | display.plot_rhi("reflectivity_horizontal_filtered", 0, vmin=-16, vmax=64) 12 | fig.savefig("example_sigmet_rhi.png") 13 | -------------------------------------------------------------------------------- /pyart/testing/example_data.py: -------------------------------------------------------------------------------- 1 | import importlib.resources 2 | 3 | import pooch 4 | from pooch.downloaders import HTTPDownloader 5 | 6 | DATASETS = pooch.create( 7 | path=pooch.os_cache("pyart-datasets"), 8 | base_url="https://adc.arm.gov/pyart/example_data/", 9 | env="PYART_DATASETS_DIR", 10 | ) 11 | 12 | headers = { 13 | "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36" 14 | } 15 | 16 | 17 | def add_header_to_download(url, output_file, mypooch): 18 | download = HTTPDownloader(headers=headers) 19 | download(url, output_file, mypooch) 20 | 21 | 22 | with open(importlib.resources.files("pyart.testing") / "registry.txt") as registry_file: 23 | DATASETS.load_registry(registry_file) 24 | 25 | 26 | def locate(): 27 | """The absolute path to the sample data storage location on disk. 28 | This is where the data are saved on your computer. The location is 29 | dependent on the operating system. The folder locations are defined by the 30 | ``appdirs`` package (see the `appdirs documentation 31 | `__). 32 | The location can be overwritten by the ``PYTHIA_DATASETS_DIR`` environment 33 | variable to the desired destination. 34 | Returns 35 | ------- 36 | path : str 37 | The local data storage location. 38 | """ 39 | return str(DATASETS.abspath) 40 | 41 | 42 | def get_test_data(file): 43 | """ 44 | Accesses the desired test data storage 45 | Parameters 46 | ---------- 47 | file = str 48 | The name of the desired file 49 | Returns 50 | ------- 51 | path_to_file = str 52 | Local path to the desired file 53 | """ 54 | return DATASETS.fetch(file, downloader=add_header_to_download) 55 | -------------------------------------------------------------------------------- /pyart/testing/registry.txt: -------------------------------------------------------------------------------- 1 | 034142.mdv a6b9c25ce6c4122b087464e528c4c0b9d86ca5da3c786fa155e1d14e153c29d8 2 | 095636.mdv f6785fb0f781fab4e7469204f50d26e921cfcb085d8df1624d3cb0ed92b8739e 3 | 110041.mdv edcc255e02edc3aff7fa0f02306c4092a383dac7c7df9b1b9d47b9427be53b9c 4 | 110635.mdv f9412e55ec63df1ff8e8a81ab5e076d29016de754e6d1dd450d34705667cb578 5 | 20110520100000_nex_3d.nc cc3a3acdcb767c97e71a950bdfe1e36ad56d5671878ca7bab27ba762f099b52a 6 | 20110520100000_nexrad_grid.nc 539d1cd0bce5d34c97a6e7cce489e073d6e649df3ee22e1c13d07ca37c6bd15d 7 | 220629.mdv c4ac8f6a83f5b5f0b9e8c49ecd28933a9003c5d6332734451c663ebb01497d9b 8 | KATX20130717_195021_V06 10824fb0b296ae27035ee848bfc17d3c5ad53a116bd4667f09a9c5cd28f1c5a3 9 | Level2_KATX_20130717_1950.ar2v 57dcfebd79d25e042a498a6dd3f79c15634442910918ce16796408be8dbe8a1b 10 | XSW110520105408.RAW7HHF 93e230fffaea00dd098c89d68838f71831b8084b8928919f9f993e59f162ba55 11 | XSW110520113537.RAW7HHL 46efb5f8f7fc239bf40853387abff875628cb7f97d930aa743f5ebd759b23935 12 | narr-a_221_20110520_0000_000.nc 027fdc3bfd81f88a6d5108139a798851e78a083c499abfcf85051363e7506940 13 | nsaxsaprppiC1.a1.20140201.184802.nc 7063636743689eb1ff3fd6e4f2e19edf688ff85f5dc36304c1b1e1afd2477f37 14 | preprocessed.nc 11344439b55cc5ca13dc725392bf26f8b940214c49284794030a315b7d192da6 15 | pvcwsacrhsrhiM1.b1.20121105.201200_modified.nc d1cae366d6e4a7af6e90a35747faef50f5cbc33fd5dc205dca1b185b61883b49 16 | sex_20120520_0641.nc 96e6eb3b0b46dbdd2465b7ab633cfa96365d28f0e72f1e733dcef0a4f7fb7482 17 | sgpcsaprsurcmacI7.c0.20110520.095101.nc 15bddc066fcdbfc31fc0749e212c2200ecf0740c89b6b4001660cba332ba190a 18 | sgpinterpolatedsondeC1.c1.20110510.000000.cdf a3cd8c5c70c1b72abc23b323d74e6cf4b7f1dc25fa2e9aaf565798b2523783fd 19 | sgpxsaprrhicmacI5.c0.20110524.015604_NC4.nc da904e2363d7c15f4bf1ed511fa8acbbd41dde36fe4e9565b12c39028184e58a 20 | swx_20120520_0641.nc 83c7c0df0d398d297a00328ee45423736864e89db8d7abb255cfd25bd33f6386 21 | -------------------------------------------------------------------------------- /pyart/testing/sample_files.py: -------------------------------------------------------------------------------- 1 | """ 2 | Sample radar files in a number of formats. Many of these files 3 | are incomplete, they should only be used for testing, not production. 4 | 5 | MDV_PPI_FILE 6 | MDV_RHI_FILE 7 | CFRADIAL_PPI_FILE 8 | CFRADIAL_RHI_FILE 9 | CFRADIAL_CR_RASTER_FILE 10 | CHL_RHI_FILE 11 | SIGMET_PPI_FILE 12 | SIGMET_RHI_FILE 13 | NEXRAD_ARCHIVE_MSG31_FILE 14 | NEXRAD_ARCHIVE_MSG31_COMPRESSED_FILE 15 | NEXRAD_ARCHIVE_MSG1_FILE 16 | NEXRAD_LEVEL3_MSG19 17 | NEXRAD_LEVEL3_MSG163 18 | NEXRAD_CDM_FILE 19 | UF_FILE 20 | INTERP_SOUNDE_FILE 21 | 22 | """ 23 | 24 | import os 25 | 26 | DATA_PATH = os.path.join(os.path.dirname(__file__), "data") 27 | 28 | MDV_PPI_FILE = os.path.join(DATA_PATH, "example_mdv_ppi.mdv") 29 | MDV_RHI_FILE = os.path.join(DATA_PATH, "example_mdv_rhi.mdv") 30 | MDV_GRID_FILE = os.path.join(DATA_PATH, "example_mdv_grid.mdv") 31 | CFRADIAL_PPI_FILE = os.path.join(DATA_PATH, "example_cfradial_ppi.nc") 32 | CFRADIAL_RHI_FILE = os.path.join(DATA_PATH, "example_cfradial_rhi.nc") 33 | CFRADIAL_CR_RASTER_FILE = os.path.join(DATA_PATH, "example_cfradial_cr_raster.nc") 34 | CHL_RHI_FILE = os.path.join(DATA_PATH, "example_chl_rhi.chl") 35 | SIGMET_PPI_FILE = os.path.join(DATA_PATH, "example_sigmet_ppi.sigmet") 36 | SIGMET_RHI_FILE = os.path.join(DATA_PATH, "example_sigmet_rhi.sigmet") 37 | NEXRAD_ARCHIVE_MSG31_FILE = os.path.join(DATA_PATH, "example_nexrad_archive_msg31.bz2") 38 | NEXRAD_ARCHIVE_MSG31_COMPRESSED_FILE = os.path.join( 39 | DATA_PATH, "example_nexrad_archive_msg31_compressed.ar2v" 40 | ) 41 | NEXRAD_ARCHIVE_MSG1_FILE = os.path.join(DATA_PATH, "example_nexrad_archive_msg1.bz2") 42 | # NEXRAD Level 3 file downloaded from NCDC with filenames: 43 | # KBMX_SDUS54_N0RBMX_201501020205 44 | # KBMX_SDUS84_N0KBMX_201501020205 45 | NEXRAD_LEVEL3_MSG19 = os.path.join(DATA_PATH, "example_nexrad_level3_msg19") 46 | NEXRAD_LEVEL3_MSG163 = os.path.join(DATA_PATH, "example_nexrad_level3_msg163") 47 | NEXRAD_LEVEL3_MSG176 = os.path.join(DATA_PATH, "example_nexrad_level3_msg176") 48 | NEXRAD_CDM_FILE = os.path.join(DATA_PATH, "example_nexrad_cdm.bz2") 49 | UF_FILE = os.path.join(DATA_PATH, "example_uf_ppi.uf") 50 | INTERP_SOUNDE_FILE = os.path.join(DATA_PATH, "example_interpolatedsonde.cdf") 51 | SONDE_FILE = os.path.join(DATA_PATH, "example_arm_sonde.cdf") 52 | _EXAMPLE_RAYS_FILE = os.path.join(DATA_PATH, "example_rays.npz") 53 | -------------------------------------------------------------------------------- /pyart/util/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Miscellaneous utility functions. 3 | 4 | The location and names of these functions within Py-ART may change between 5 | versions without depreciation, use with caution. 6 | 7 | """ 8 | 9 | from .circular_stats import angular_mean # noqa 10 | from .circular_stats import angular_mean_deg # noqa 11 | from .circular_stats import angular_std # noqa 12 | from .circular_stats import angular_std_deg # noqa 13 | from .circular_stats import interval_mean # noqa 14 | from .circular_stats import interval_std # noqa 15 | from .circular_stats import mean_of_two_angles # noqa 16 | from .circular_stats import mean_of_two_angles_deg # noqa 17 | from .circular_stats import compute_directional_stats # noqa 18 | from .columnsect import for_azimuth # noqa 19 | from .columnsect import get_column_rays # noqa 20 | from .columnsect import get_field_location # noqa 21 | from .columnsect import sphere_distance # noqa 22 | from .columnsect import column_vertical_profile # noqa 23 | from .datetime_utils import datetime_from_dataset # noqa 24 | from .datetime_utils import datetime_from_grid # noqa 25 | from .datetime_utils import datetime_from_radar # noqa 26 | from .datetime_utils import datetimes_from_dataset # noqa 27 | from .datetime_utils import datetimes_from_radar # noqa 28 | from .hildebrand_sekhon import estimate_noise_hs74 # noqa 29 | from .radar_utils import ( # noqa 30 | image_mute_radar, 31 | is_vpt, 32 | join_radar, 33 | determine_sweeps, 34 | subset_radar, 35 | to_vpt, 36 | ma_broadcast_to, 37 | ) 38 | from .sigmath import ( # noqa 39 | angular_texture_2d, 40 | rolling_window, 41 | texture, 42 | texture_along_ray, 43 | ) 44 | from .simulated_vel import simulated_vel_from_profile # noqa 45 | from .xsect import cross_section_ppi, cross_section_rhi # noqa 46 | 47 | __all__ = [s for s in dir() if not s.startswith("_")] 48 | -------------------------------------------------------------------------------- /pyart/util/datetime_utils.py: -------------------------------------------------------------------------------- 1 | """ 2 | Functions for converting date and time between various forms. 3 | 4 | """ 5 | 6 | try: 7 | from cftime import date2num, num2date 8 | except ImportError: 9 | from netCDF4 import date2num, num2date 10 | 11 | EPOCH_UNITS = "seconds since 1970-01-01T00:00:00Z" 12 | 13 | 14 | def datetime_from_radar(radar, epoch=False, **kwargs): 15 | """Return a datetime for the first ray in a Radar.""" 16 | if epoch: 17 | dtrad = num2date(radar.time["data"][0], radar.time["units"]) 18 | epnum = date2num(dtrad, EPOCH_UNITS) 19 | return num2date(epnum, EPOCH_UNITS, **kwargs) 20 | else: 21 | return num2date(radar.time["data"][0], radar.time["units"], **kwargs) 22 | 23 | 24 | def datetimes_from_radar(radar, epoch=False, **kwargs): 25 | """Return an array of datetimes for the rays in a Radar.""" 26 | if epoch: 27 | dtrad = num2date(radar.time["data"][:], radar.time["units"]) 28 | epnum = date2num(dtrad, EPOCH_UNITS) 29 | return num2date(epnum, EPOCH_UNITS, **kwargs) 30 | else: 31 | return num2date(radar.time["data"][:], radar.time["units"], **kwargs) 32 | 33 | 34 | def datetime_from_dataset(dataset, epoch=False, **kwargs): 35 | """Return a datetime for the first time in a netCDF Dataset.""" 36 | if epoch: 37 | dtdata = num2date(dataset.variables["time"][0], dataset.variables["time"].units) 38 | epnum = date2num(dtdata, EPOCH_UNITS) 39 | return num2date(epnum, EPOCH_UNITS, **kwargs) 40 | else: 41 | return num2date( 42 | dataset.variables["time"][0], dataset.variables["time"].units, **kwargs 43 | ) 44 | 45 | 46 | def datetimes_from_dataset(dataset, epoch=False, **kwargs): 47 | """Return an array of datetimes for the times in a netCDF Dataset.""" 48 | if epoch: 49 | dtdata = num2date(dataset.variables["time"][:], dataset.variables["time"].units) 50 | epnum = date2num(dtdata, EPOCH_UNITS) 51 | return num2date(epnum, EPOCH_UNITS, **kwargs) 52 | else: 53 | return num2date( 54 | dataset.variables["time"][:], dataset.variables["time"].units, **kwargs 55 | ) 56 | 57 | 58 | def datetime_from_grid(grid, epoch=False, **kwargs): 59 | """Return a datetime for the volume start in a Grid.""" 60 | if epoch: 61 | dtrad = num2date(grid.time["data"][0], grid.time["units"]) 62 | epnum = date2num(dtrad, EPOCH_UNITS) 63 | return num2date(epnum, EPOCH_UNITS, **kwargs) 64 | else: 65 | return num2date(grid.time["data"][0], grid.time["units"], **kwargs) 66 | -------------------------------------------------------------------------------- /pyart/util/hildebrand_sekhon.py: -------------------------------------------------------------------------------- 1 | """ 2 | Estimation of noise in Doppler spectra using the Hildebrand Sekhon method. 3 | 4 | """ 5 | 6 | import numpy as np 7 | 8 | 9 | def estimate_noise_hs74(spectrum, navg=1, nnoise_min=1): 10 | """ 11 | Estimate noise parameters of a Doppler spectrum. 12 | 13 | Use the method of estimating the noise level in Doppler spectra outlined 14 | by Hildebrand and Sehkon, 1974. 15 | 16 | Parameters 17 | ---------- 18 | spectrum : array like 19 | Doppler spectrum in linear units. 20 | navg : int, optional 21 | The number of spectral bins over which a moving average has been 22 | taken. Corresponds to the **p** variable from equation 9 of the 23 | article. The default value of 1 is appropriate when no moving 24 | average has been applied to the spectrum. 25 | nnoise_min : int, optional 26 | Minimum number of noise samples to consider the estimation valid. 27 | 28 | Returns 29 | ------- 30 | mean : float-like 31 | Mean of points in the spectrum identified as noise. 32 | threshold : float-like 33 | Threshold separating noise from signal. The point in the spectrum with 34 | this value or below should be considered as noise, above this value 35 | signal. It is possible that all points in the spectrum are identified 36 | as noise. If a peak is required for moment calculation then the point 37 | with this value should be considered as signal. 38 | var : float-like 39 | Variance of the points in the spectrum identified as noise. 40 | nnoise : int 41 | Number of noise points in the spectrum. 42 | 43 | References 44 | ---------- 45 | P. H. Hildebrand and R. S. Sekhon, Objective Determination of the Noise 46 | Level in Doppler Spectra. Journal of Applied Meteorology, 1974, 13, 47 | 808-811. 48 | 49 | """ 50 | sorted_spectrum = np.sort(spectrum) 51 | nnoise = len(spectrum) # default to all points in the spectrum as noise 52 | 53 | rtest = 1 + 1 / navg 54 | sum1 = 0.0 55 | sum2 = 0.0 56 | for i, pwr in enumerate(sorted_spectrum): 57 | npts = i + 1 58 | sum1 += pwr 59 | sum2 += pwr * pwr 60 | 61 | if npts < nnoise_min: 62 | continue 63 | 64 | if npts * sum2 < sum1 * sum1 * rtest: 65 | nnoise = npts 66 | else: 67 | # partial spectrum no longer has characteristics of white noise. 68 | sum1 -= pwr 69 | sum2 -= pwr * pwr 70 | break 71 | 72 | mean = sum1 / nnoise 73 | var = sum2 / nnoise - mean * mean 74 | threshold = sorted_spectrum[nnoise - 1] 75 | return mean, threshold, var, nnoise 76 | -------------------------------------------------------------------------------- /pyart/util/met.py: -------------------------------------------------------------------------------- 1 | """ 2 | General meteorological calculations useful to other modules. 3 | """ 4 | 5 | import heapq 6 | import os 7 | 8 | import netCDF4 9 | import numpy as np 10 | from pylab import date2num, datestr2num 11 | 12 | 13 | def nth_smallest(n, iter): 14 | return heapq.nsmallest(n, iter)[-1] 15 | 16 | 17 | def get_best_sounding(target, sdir, minl, maxl): 18 | sondes = os.listdir(sdir) 19 | sondes.sort() 20 | offsets = [ 21 | np.abs(datestr2num(s[18:33].replace(".", " ")) - date2num(target)) 22 | for s in sondes 23 | ] 24 | cont = True 25 | n = 1 26 | while cont: 27 | test_sonde = sondes[offsets.index(nth_smallest(n, offsets))] 28 | ncf_obj = netCDF4.Dataset(sdir + test_sonde, "r") 29 | ncf_min = ncf_obj.variables["alt"][:].min() 30 | ncf_max = ncf_obj.variables["alt"][:].max() 31 | if ncf_min < minl and ncf_max > maxl: 32 | cont = False 33 | chosen_sonde = test_sonde 34 | ncf_obj.close() 35 | n = n + 1 36 | return chosen_sonde 37 | -------------------------------------------------------------------------------- /pyart/xradar/__init__.py: -------------------------------------------------------------------------------- 1 | from .accessor import Xradar, Xgrid # noqa 2 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | "setuptools", 4 | "setuptools_scm>=6.2", 5 | "wheel", 6 | "cython>=3.0", 7 | "numpy>=2.0.0rc1" 8 | ] 9 | 10 | [tool.black] 11 | line-length = 88 12 | 13 | [tool.ruff] 14 | target-version = "py39" 15 | builtins = ["ellipsis"] 16 | exclude = [ 17 | ".eggs", 18 | "doc", 19 | ] 20 | 21 | [tool.ruff.lint] 22 | # E402: module level import not at top of file 23 | # E501: line too long - let black worry about that 24 | # E731: do not assign a lambda expression, use a def 25 | # E733: do not use bare `except` 26 | ignore = [ 27 | "E402", 28 | "E501", 29 | "E731", 30 | "E722", 31 | ] 32 | select = [ 33 | # Pyflakes 34 | "F", 35 | # Pycodestyle 36 | "E", 37 | "W", 38 | # isort 39 | "I", 40 | # Pyupgrade 41 | "UP", 42 | ] 43 | -------------------------------------------------------------------------------- /readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | conda: 3 | environment: doc/environment.yml 4 | build: 5 | os: "ubuntu-20.04" 6 | tools: 7 | python: "mambaforge-4.10" 8 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy 2 | scipy 3 | netCDF4>=1.7.0 4 | matplotlib 5 | pooch 6 | cftime 7 | fsspec 8 | s3fs 9 | open_radar_data 10 | xradar>=0.8.0 11 | pandas 12 | mda-xdrlib 13 | xarray>=2024.10.0 14 | cartopy 15 | pint 16 | -------------------------------------------------------------------------------- /roadmaps/pyart-roadmap-rc1.0.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/roadmaps/pyart-roadmap-rc1.0.pdf -------------------------------------------------------------------------------- /roadmaps/pyart-roadmap-rc2.0.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/roadmaps/pyart-roadmap-rc2.0.pdf -------------------------------------------------------------------------------- /scripts/radar_info: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | import argparse 4 | 5 | import pyart 6 | 7 | if __name__ == "__main__": 8 | # parse the arguments 9 | parser = argparse.ArgumentParser(description="Print information on a radar file.") 10 | parser.add_argument("filename", type=str, help="radar file to check") 11 | 12 | group = parser.add_mutually_exclusive_group() 13 | group.add_argument( 14 | "-f", 15 | "--full", 16 | dest="level", 17 | action="store_const", 18 | const="f", 19 | help="print out all information", 20 | ) 21 | group.add_argument( 22 | "-s", 23 | "-standard", 24 | dest="level", 25 | action="store_const", 26 | const="s", 27 | help="print out standard information", 28 | ) 29 | group.add_argument( 30 | "-c", 31 | "--compact", 32 | dest="level", 33 | action="store_const", 34 | const="c", 35 | help="print out minimal information (default)", 36 | ) 37 | parser.set_defaults(level="c") 38 | 39 | igroup = parser.add_argument_group( 40 | title="ingest method, optional", 41 | description=( 42 | "The method of file ingest can be specified. " 43 | "If no ingest is specified, the format of the file will " 44 | "be used to determine the best ingest method. " 45 | "Specify only one of the following:" 46 | ), 47 | ) 48 | 49 | igroup.add_argument("--sigmet", action="store_true", help="Sigmet/IRIS ingest") 50 | igroup.add_argument("--mdv", action="store_true", help="MDV ingest") 51 | igroup.add_argument("--cfradial", action="store_true", help="CF/Radial ingest") 52 | igroup.add_argument("--rsl", action="store_true", help="RSL ingest") 53 | igroup.add_argument( 54 | "--nexrad_archive", action="store_true", help="NEXRAD level 2 archive ingest" 55 | ) 56 | igroup.add_argument( 57 | "--nexrad_cdm", action="store_true", help="NEXRAD level 2 CDM ingest" 58 | ) 59 | 60 | parser.add_argument( 61 | "-v", 62 | "--version", 63 | action="version", 64 | version=f"Py-ART version {pyart.__version__}", 65 | ) 66 | args = parser.parse_args() 67 | 68 | # read in the file 69 | if args.sigmet: 70 | radar = pyart.io.read_sigmet(args.filename) 71 | elif args.mdv: 72 | radar = pyart.io.read_mdv(args.filename) 73 | elif args.cfradial: 74 | radar = pyart.io.read_cfradial(args.filename) 75 | elif args.rsl: 76 | radar = pyart.io.read_rsl(args.filename) 77 | elif args.nexrad_archive: 78 | radar = pyart.io.read_nexrad_archive(args.filename) 79 | elif args.nexrad_cdm: 80 | radar = pyart.io.read_nexrad_cdm(args.filename) 81 | else: 82 | radar = pyart.io.read(args.filename) 83 | 84 | # print out information 85 | radar.info(args.level) 86 | -------------------------------------------------------------------------------- /tests/bridge/test_wradlib_bridge.py: -------------------------------------------------------------------------------- 1 | """ Unit Tests for Py-ART's io/mdv.py module. """ 2 | 3 | import numpy as np 4 | import pytest 5 | 6 | import pyart 7 | 8 | 9 | @pytest.mark.skipif( 10 | not pyart.bridge.wradlib_bridge._WRADLIB_AVAILABLE, 11 | reason="Wradlib is not installed.", 12 | ) 13 | def test_texture_of_complex_phase(): 14 | test_radar = pyart.testing.make_empty_ppi_radar(100, 360, 5) 15 | foo_field = {"data": np.zeros([360 * 5, 100])} 16 | test_radar.add_field("differential_phase", foo_field) 17 | test_text = pyart.retrieve.texture_of_complex_phase(test_radar) 18 | assert test_text["data"].mean() == 0.0 19 | -------------------------------------------------------------------------------- /tests/core/test_wind_profile.py: -------------------------------------------------------------------------------- 1 | """ Unit Tests for Py-ART's core/wind_profile.py module. """ 2 | 3 | import numpy as np 4 | import pytest 5 | from numpy.testing import assert_almost_equal 6 | 7 | from pyart.core import HorizontalWindProfile 8 | 9 | 10 | def test_horizontalwindprofile_class(): 11 | height = np.arange(5) 12 | speed = np.ones(5) 13 | direction = np.array([0, 90, 180, 270, 45]) 14 | hprofile = HorizontalWindProfile(height, speed, direction) 15 | 16 | assert_almost_equal(hprofile.height, [0, 1, 2, 3, 4]) 17 | assert_almost_equal(hprofile.speed, [1, 1, 1, 1, 1]) 18 | assert_almost_equal(hprofile.direction, [0, 90, 180, 270, 45]) 19 | assert_almost_equal(hprofile.u_wind, [0, -1, 0, 1, -0.707], 3) 20 | assert_almost_equal(hprofile.v_wind, [-1, 0, 1, 0, -0.707], 3) 21 | 22 | 23 | def test_horizontalwindprofile_from_u_and_v(): 24 | height = np.arange(5) 25 | u_wind = [0, -1, 0, 1, -np.sqrt(2) / 2.0] 26 | v_wind = [-1, 0, 1, 0, -np.sqrt(2) / 2.0] 27 | hprofile = HorizontalWindProfile.from_u_and_v(height, u_wind, v_wind) 28 | 29 | assert_almost_equal(hprofile.height, [0, 1, 2, 3, 4]) 30 | assert_almost_equal(hprofile.speed, [1, 1, 1, 1, 1]) 31 | assert_almost_equal(hprofile.direction, [0, 90, 180, 270, 45]) 32 | assert_almost_equal(hprofile.u_wind, [0, -1, 0, 1, -0.707], 3) 33 | assert_almost_equal(hprofile.v_wind, [-1, 0, 1, 0, -0.707], 3) 34 | 35 | 36 | def test_horizontalwindprofile_error(): 37 | height = [1, 2] 38 | speed = [1, 1] 39 | direction = [1, 2, 3] 40 | pytest.raises(ValueError, HorizontalWindProfile, height, speed, direction) 41 | 42 | 43 | def test_horizontalwindprofile_location_error(): 44 | height = [1, 2] 45 | speed = [1, 1] 46 | direction = [1, 2] 47 | lat = [1] 48 | pytest.raises( 49 | ValueError, 50 | HorizontalWindProfile, 51 | height, 52 | speed, 53 | direction, 54 | latitude=lat, 55 | longitude=None, 56 | ) 57 | -------------------------------------------------------------------------------- /tests/correct/attenuation_rays.npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/tests/correct/attenuation_rays.npz -------------------------------------------------------------------------------- /tests/correct/attenuation_rays_philinear.npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/tests/correct/attenuation_rays_philinear.npz -------------------------------------------------------------------------------- /tests/correct/attenuation_rays_zphi.npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/tests/correct/attenuation_rays_zphi.npz -------------------------------------------------------------------------------- /tests/correct/reference_ray_plot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/tests/correct/reference_ray_plot.png -------------------------------------------------------------------------------- /tests/correct/reference_rays.npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/tests/correct/reference_rays.npz -------------------------------------------------------------------------------- /tests/graph/test_plot_maxcappi.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import matplotlib.pyplot as plt 4 | 5 | import pyart 6 | 7 | 8 | def test_plot_maxcappi_simple(outfile=None): 9 | """ 10 | Test the basic functionality of plot_maxcappi. 11 | """ 12 | # Create a test grid using Py-ART's testing utility 13 | grid = pyart.testing.make_target_grid() 14 | grid.z["data"] = grid.z["data"] * 10 + 100 15 | grid.metadata["instrument_name"] = "GRID" 16 | 17 | # Use plot_maxcappi with the generated grid 18 | pyart.graph.max_cappi.plot_maxcappi( 19 | grid=grid, 20 | field="reflectivity", 21 | savedir=None, # Do not save the plot 22 | show_figure=False, # Do not show the plot 23 | ) 24 | 25 | if outfile: 26 | plt.savefig(outfile) 27 | plt.close() 28 | 29 | 30 | def test_plot_maxcappi_with_save(outfile=None): 31 | """ 32 | Test plot_maxcappi and save the output to a file. 33 | """ 34 | # Create a test grid using Py-ART's testing utility 35 | grid = pyart.testing.make_target_grid() 36 | grid.z["data"] = grid.z["data"] * 10 + 100 37 | grid.metadata["instrument_name"] = "GRID" 38 | 39 | # Define the output file path 40 | outfile = outfile or "test_plot_maxcappi_output.png" 41 | 42 | # Use plot_maxcappi with the generated grid 43 | pyart.graph.max_cappi.plot_maxcappi( 44 | grid=grid, 45 | field="reflectivity", 46 | savedir=None, # Handle saving manually below 47 | show_figure=False, # Do not show the plot 48 | ) 49 | 50 | # Save the figure to a file 51 | plt.savefig(outfile) 52 | plt.close() 53 | 54 | # Check if the file was created 55 | assert os.path.exists(outfile), "The plot was not saved as expected." 56 | 57 | 58 | def test_plot_maxcappi_with_all_options(outfile=None): 59 | """ 60 | Test plot_maxcappi with all options enabled. 61 | """ 62 | import cartopy.crs as ccrs 63 | 64 | # Create a test grid using Py-ART's testing utility 65 | grid = pyart.testing.make_target_grid() 66 | grid.z["data"] = grid.z["data"] * 10 + 100 67 | grid.metadata["instrument_name"] = "GRID" 68 | 69 | # Use a custom projection for testing 70 | projection = ccrs.Mercator() 71 | 72 | # Use plot_maxcappi with additional options 73 | pyart.graph.max_cappi.plot_maxcappi( 74 | grid=grid, 75 | field="reflectivity", 76 | title="Test Max-CAPPI", 77 | lat_lines=None, 78 | lon_lines=None, 79 | add_map=True, 80 | projection=projection, 81 | colorbar=True, 82 | range_rings=True, 83 | dpi=150, 84 | savedir=None, 85 | show_figure=False, 86 | ) 87 | 88 | if outfile: 89 | plt.savefig(outfile) 90 | plt.close() 91 | 92 | 93 | if __name__ == "__main__": 94 | test_plot_maxcappi_simple("figure_plot_maxcappi_simple.png") 95 | test_plot_maxcappi_with_save("figure_plot_maxcappi_output.png") 96 | test_plot_maxcappi_with_all_options("figure_plot_maxcappi_all_options.png") 97 | -------------------------------------------------------------------------------- /tests/io/test_arm_sonde.py: -------------------------------------------------------------------------------- 1 | """ Unit Tests for Py-ART's io/arm_sonde.py module. """ 2 | 3 | import datetime 4 | 5 | from numpy.testing import assert_almost_equal 6 | from pytest import raises 7 | 8 | import pyart 9 | 10 | 11 | def test_read_arm_sonde_vap_target_datetime(): 12 | target_datetime = datetime.datetime(2011, 5, 10, 11, 30, 5) 13 | profile_datetime, hprofile = pyart.io.read_arm_sonde_vap( 14 | pyart.testing.INTERP_SOUNDE_FILE, target_datetime=target_datetime 15 | ) 16 | 17 | assert profile_datetime == datetime.datetime(2011, 5, 10, 11, 30) 18 | assert_almost_equal(hprofile.height[:5], [318, 338, 358, 378, 398], 1) 19 | assert_almost_equal(hprofile.speed[:5], [8.4, 7.4, 9.2, 10.8, 12.3], 1) 20 | assert_almost_equal(hprofile.direction[:5], [182.6, 181.9, 182.3, 183.3, 184.2], 1) 21 | 22 | 23 | def test_read_arm_sonde_vap_radar(): 24 | radar = pyart.testing.make_empty_ppi_radar(1, 1, 1) 25 | radar.time["units"] = "seconds since 2011-05-10T11:30:05Z" 26 | profile_datetime, hprofile = pyart.io.read_arm_sonde_vap( 27 | pyart.testing.INTERP_SOUNDE_FILE, radar=radar 28 | ) 29 | 30 | assert profile_datetime == datetime.datetime(2011, 5, 10, 11, 30) 31 | assert_almost_equal(hprofile.height[:5], [318, 338, 358, 378, 398], 1) 32 | assert_almost_equal(hprofile.speed[:5], [8.4, 7.4, 9.2, 10.8, 12.3], 1) 33 | assert_almost_equal(hprofile.direction[:5], [182.6, 181.9, 182.3, 183.3, 184.2], 1) 34 | 35 | 36 | def test_read_arm_sonde_vap_errors(): 37 | # radar or target_datetime must be specified 38 | raises(ValueError, pyart.io.read_arm_sonde_vap, pyart.testing.INTERP_SOUNDE_FILE) 39 | 40 | # only one of radar or target_datetime can be specified 41 | radar = pyart.testing.make_empty_ppi_radar(1, 1, 1) 42 | target_datetime = datetime.datetime(2011, 5, 10, 11, 30, 5) 43 | raises( 44 | ValueError, 45 | pyart.io.read_arm_sonde_vap, 46 | pyart.testing.INTERP_SOUNDE_FILE, 47 | radar=radar, 48 | target_datetime=target_datetime, 49 | ) 50 | 51 | 52 | def test_read_arm_sonde(): 53 | profile_dt, hprofile = pyart.io.read_arm_sonde(pyart.testing.SONDE_FILE) 54 | 55 | assert profile_dt == datetime.datetime(2011, 5, 20, 8, 28) 56 | assert_almost_equal(hprofile.height[:5], [315, 321, 328, 336, 344], 0) 57 | assert_almost_equal(hprofile.speed[:5], [5, 3.2, 3.7, 4.3, 4.8], 1) 58 | assert_almost_equal(hprofile.direction[:5], [215.0, 193.0, 191.0, 191.0, 189.0], 1) 59 | -------------------------------------------------------------------------------- /tests/map/test_gatemapper.py: -------------------------------------------------------------------------------- 1 | from copy import deepcopy 2 | 3 | import numpy as np 4 | 5 | import pyart 6 | 7 | 8 | def test_gatemapper(): 9 | # Make fake radar with target 10 | old_radar = pyart.testing.make_target_radar() 11 | new_radar = deepcopy(old_radar) 12 | new_radar.latitude["data"] = old_radar.latitude["data"] + 0.001 13 | new_radar.longitude["data"] = old_radar.longitude["data"] + 0.001 14 | old_radar.fields["reflectivity_copy"] = old_radar.fields["reflectivity"] 15 | gate_mapper = pyart.map.GateMapper(old_radar, new_radar) 16 | mapped_radar = gate_mapper.mapped_radar(["reflectivity"]) 17 | 18 | # Test point outside of 1 min tolerance 19 | assert gate_mapper[20, 20] == (None, None) 20 | assert gate_mapper[40, 40] == (40, 33) 21 | assert ( 22 | mapped_radar.fields["reflectivity"]["data"][40, 33] 23 | == old_radar.fields["reflectivity"]["data"][40, 40] 24 | ) 25 | 26 | # Check case where source radar has field destination doesn't 27 | mapped_radar = gate_mapper.mapped_radar(["reflectivity_copy"]) 28 | assert ( 29 | mapped_radar.fields["reflectivity_copy"]["data"][40, 33] 30 | == old_radar.fields["reflectivity_copy"]["data"][40, 40] 31 | ) 32 | 33 | 34 | def test_gatemapper_gatefilter(): 35 | # Make fake radar with target 36 | old_radar = pyart.testing.make_target_radar() 37 | new_radar = deepcopy(old_radar) 38 | new_radar.latitude["data"] = old_radar.latitude["data"] + 0.001 39 | new_radar.longitude["data"] = old_radar.longitude["data"] + 0.001 40 | gatefilter = pyart.filters.GateFilter(old_radar) 41 | gatefilter.exclude_below("reflectivity", 40) 42 | gate_mapper = pyart.map.GateMapper(new_radar, old_radar, gatefilter_src=gatefilter) 43 | mapped_radar = gate_mapper.mapped_radar(["reflectivity"]) 44 | assert gate_mapper[4, 4] == (26, 11) 45 | assert np.ma.is_masked(mapped_radar.fields["reflectivity"]["data"][26, 11]) 46 | -------------------------------------------------------------------------------- /tests/retrieve/baseline/test_compare_kdp_estimation_methods.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-DOE/pyart/8c1b9dfd4d17851c89fcdaa0ebc4c832c4c1ed55/tests/retrieve/baseline/test_compare_kdp_estimation_methods.png -------------------------------------------------------------------------------- /tests/retrieve/test_advection.py: -------------------------------------------------------------------------------- 1 | """ Unit Tests for Py-ART's retrieve/advection.py module. """ 2 | 3 | from numpy.testing import assert_almost_equal 4 | 5 | import pyart 6 | 7 | 8 | def test_grid_displacement_pc(): 9 | grid1 = pyart.testing.make_storm_grid() 10 | data = grid1.fields["reflectivity"]["data"].copy() 11 | grid1.fields["reflectivity"]["data"] = data[:, 5:-5, 3:-3].copy() 12 | 13 | grid2 = pyart.testing.make_storm_grid() 14 | grid2.fields["reflectivity"]["data"] = data[:, 0:-10, 0:-6].copy() 15 | 16 | # test pixels 17 | displacement = pyart.retrieve.grid_displacement_pc(grid1, grid2, "reflectivity", 0) 18 | assert displacement == (-5, -3) 19 | 20 | # test distance 21 | grid1.fields["reflectivity"]["valid_min"] = 0 22 | grid2.fields["reflectivity"]["valid_min"] = 0 23 | displacement = pyart.retrieve.grid_displacement_pc( 24 | grid1, grid2, "reflectivity", 0, return_value="distance" 25 | ) 26 | dx = grid1.x["data"][1] - grid1.x["data"][0] 27 | dy = grid1.y["data"][1] - grid1.y["data"][0] 28 | assert_almost_equal(displacement[0], -5 * dy, 0) 29 | assert_almost_equal(displacement[1], -3 * dx, 0) 30 | 31 | # test velocity 32 | grid2.time["data"] += 1 33 | displacement = pyart.retrieve.grid_displacement_pc( 34 | grid1, grid2, "reflectivity", 0, return_value="velocity" 35 | ) 36 | assert_almost_equal(displacement[0], -5 * dy, 0) 37 | assert_almost_equal(displacement[1], -3 * dx, 0) 38 | 39 | # test fallback 40 | displacement = pyart.retrieve.grid_displacement_pc( 41 | grid1, grid2, "reflectivity", 0, return_value="foobar" 42 | ) 43 | assert displacement == (-5, -3) 44 | 45 | 46 | def test_grid_shift(): 47 | # create two guassian storms 48 | grid1 = pyart.testing.make_normal_storm(10.0, [0.0, 0.0]) 49 | grid2 = pyart.testing.make_normal_storm(10.0, [5.0, 5.0]) 50 | 51 | # trim one, trim and shift the other 52 | trimmed_grid2 = pyart.retrieve.grid_shift(grid2, [0.0, 0.0], trim_edges=10) 53 | shifted_grid1 = pyart.retrieve.grid_shift(grid1, [5.0, 5.0], trim_edges=10) 54 | 55 | # The difference should be nearly zero 56 | data1 = shifted_grid1.fields["reflectivity"]["data"][0] 57 | data2 = trimmed_grid2.fields["reflectivity"]["data"][0] 58 | assert (data1 - data2).mean() < 1.0e-10 59 | -------------------------------------------------------------------------------- /tests/retrieve/test_cappi.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from open_radar_data import DATASETS 3 | 4 | import pyart 5 | from pyart.retrieve import create_cappi 6 | 7 | 8 | def test_create_cappi(): 9 | # Load radar data 10 | file = DATASETS.fetch("RAW_NA_000_125_20080411190016") 11 | radar = pyart.io.read(file) 12 | 13 | # Create CAPPI at 10000 meters for the 'reflectivity' field 14 | cappi = create_cappi(radar, fields=["reflectivity"], height=10000) 15 | 16 | # Retrieve the 'reflectivity' field from the generated CAPPI 17 | reflectivity_cappi = cappi.fields["reflectivity"] 18 | 19 | # Test 1: Check the shape of the reflectivity CAPPI data 20 | expected_shape = (360, 992) # As per the sample data provided 21 | assert ( 22 | reflectivity_cappi["data"].shape == expected_shape 23 | ), "Shape mismatch in CAPPI data" 24 | 25 | # Test 2: Check the units of the reflectivity CAPPI 26 | assert ( 27 | reflectivity_cappi["units"] == "dBZ" 28 | ), "Incorrect units for CAPPI reflectivity" 29 | 30 | # Test 3: Check that the elevation data is correctly set to zero 31 | assert np.all( 32 | cappi.elevation["data"] == 0 33 | ), "Elevation data should be all zeros in CAPPI" 34 | 35 | # Test 4: Verify the fill value 36 | assert ( 37 | reflectivity_cappi["_FillValue"] == -9999.0 38 | ), "Incorrect fill value in CAPPI reflectivity" 39 | 40 | # Test 5: Check the long name and comment 41 | assert ( 42 | reflectivity_cappi["long_name"] == "CAPPI reflectivity at 10000 meters" 43 | ), "Incorrect long name" 44 | assert ( 45 | reflectivity_cappi["comment"] 46 | == "CAPPI reflectivity calculated at a height of 10000 meters" 47 | ), "Incorrect comment" 48 | -------------------------------------------------------------------------------- /tests/retrieve/test_cfad.py: -------------------------------------------------------------------------------- 1 | """ Unit Tests for Py-ART's retrieve/echo_class.py module. """ 2 | 3 | import numpy as np 4 | 5 | import pyart 6 | 7 | 8 | def test_cfad_default(): 9 | # initalize test radar object 10 | radar = pyart.io.read(pyart.testing.NEXRAD_ARCHIVE_MSG31_FILE) 11 | ref_field = "reflectivity" 12 | 13 | # set every value to 20 14 | radar.fields[ref_field]["data"] = ( 15 | np.ones(radar.fields[ref_field]["data"].shape) * 20 16 | ) 17 | 18 | # set mask to none 19 | field_mask = np.zeros(radar.fields[ref_field]["data"].shape) 20 | 21 | # calculate CFAD 22 | freq_norm, height_edges, field_edges = pyart.retrieve.create_cfad( 23 | radar, 24 | field_bins=np.linspace(0, 30, 20), 25 | altitude_bins=np.arange(0, 18000, 100), 26 | field="reflectivity", 27 | field_mask=field_mask, 28 | ) 29 | 30 | # set row to mask and test 31 | verify_index = 12 32 | 33 | # if CFAD code works correctly, each column should have the same values and only 1 column should have a value of 1 34 | # check all columns are the same 35 | assert freq_norm.all(axis=0).any() 36 | # check column 12 is all ones 37 | assert (freq_norm[:, verify_index] == 1).all() 38 | -------------------------------------------------------------------------------- /tests/retrieve/test_gate_id.py: -------------------------------------------------------------------------------- 1 | """ Unit Tests for Py-ART's retrieve/gate_id.py module. """ 2 | 3 | import netCDF4 4 | import numpy as np 5 | 6 | import pyart 7 | 8 | 9 | def test_map_profile_to_gates(): 10 | test_radar = pyart.testing.make_empty_ppi_radar(100, 360, 5) 11 | foo_field = {"data": np.zeros([360 * 5, 100])} 12 | test_radar.add_field("foo", foo_field) 13 | temp_dict = pyart.retrieve.map_profile_to_gates( 14 | np.ones(100), np.linspace(0, 1000, 100), test_radar 15 | )[1] 16 | assert temp_dict["data"].mean() == 1.0 17 | 18 | 19 | def test_fetch_radar_time_profile(): 20 | test_radar = pyart.testing.make_empty_ppi_radar(100, 360, 5) 21 | test_radar.time["units"] = "seconds since 2011-05-10T00:00:01Z" 22 | test_radar.time["data"][0] = 41220.0 # 2nd time in interpolated sonde 23 | 24 | sonde_dset = netCDF4.Dataset(pyart.testing.INTERP_SOUNDE_FILE) 25 | 26 | dic = pyart.retrieve.fetch_radar_time_profile(sonde_dset, test_radar) 27 | assert "wdir" in dic 28 | assert "wspd" in dic 29 | assert "height" in dic 30 | assert round(dic["wdir"][0]) == 185 31 | -------------------------------------------------------------------------------- /tests/retrieve/test_kdp_proc.py: -------------------------------------------------------------------------------- 1 | """ Unit tests for pyart's retrieve/kdp_proc.py module. """ 2 | 3 | import numpy as np 4 | 5 | from pyart.config import get_field_name 6 | from pyart.filters import GateFilter 7 | from pyart.retrieve import kdp_proc 8 | from pyart.testing import sample_objects 9 | 10 | 11 | def test_kdp_maesaka_linear_psidp(slope=0.002, maxiter=100): 12 | radar = _make_linear_psidp_radar(slope=slope) 13 | kdp_dict, phidpf_dict, phidpr_dict = kdp_proc.kdp_maesaka( 14 | radar, maxiter=maxiter, check_outliers=False 15 | ) 16 | 17 | assert np.allclose(np.diff(kdp_dict["data"][0]), 0.0, atol=0.1) 18 | assert np.allclose(kdp_dict["data"], 1000.0 * slope / 2.0, atol=0.1) 19 | 20 | return 21 | 22 | 23 | def test_kdp_maesaka_all_excluded(first_guess=0.01, maxiter=100): 24 | radar = _make_linear_psidp_radar() 25 | gatefilter = GateFilter(radar) 26 | gatefilter.exclude_all() 27 | kdp_dict, phidpf_dict, phidpr_dict = kdp_proc.kdp_maesaka( 28 | radar, 29 | gatefilter=gatefilter, 30 | first_guess=first_guess, 31 | maxiter=maxiter, 32 | check_outliers=False, 33 | ) 34 | 35 | assert np.allclose(kdp_dict["data"][0], 0.0, atol=first_guess) 36 | 37 | return 38 | 39 | 40 | def _make_linear_psidp_radar(slope=0.002): 41 | """ 42 | Create single-ray radar with linear differential phase profile with 43 | specified slope. 44 | 45 | Parameters 46 | ---------- 47 | slope : float, optional 48 | Slope of differential phase profile in deg/m. Radar range gates cover 49 | 0-1000 m, inclusive, with 10 m gate spacings. 50 | 51 | Returns 52 | ------- 53 | radar : Radar 54 | Radar with linear differential phase profile in deg. 55 | 56 | """ 57 | radar = sample_objects.make_empty_ppi_radar(101, 1, 1) 58 | psidp_dict = {"data": np.atleast_2d(np.linspace(0.0, slope * 1000.0, radar.ngates))} 59 | radar.add_field(get_field_name("differential_phase"), psidp_dict) 60 | 61 | return radar 62 | -------------------------------------------------------------------------------- /tests/retrieve/test_spectra_calculations.py: -------------------------------------------------------------------------------- 1 | """ Unit tests for spectra_calculations.py """ 2 | 3 | import pytest 4 | from numpy.testing import assert_allclose 5 | 6 | from pyart.retrieve import spectra_moments 7 | from pyart.testing import make_target_spectra_radar 8 | 9 | try: 10 | import xarray as xr # noqa 11 | 12 | _XARRAY_AVAILABLE = True 13 | except ImportError: 14 | _XARRAY_AVAILABLE = False 15 | 16 | 17 | @pytest.mark.skipif(not _XARRAY_AVAILABLE, reason="Xarray is not installed.") 18 | def test_spectra_moments(): 19 | radar = make_target_spectra_radar() 20 | fields = spectra_moments(radar) 21 | assert len(fields.keys()) == 5 22 | assert_allclose( 23 | fields["reflectivity"]["data"][0, 0:5], 24 | [71.34390136, 71.34390136, 71.34390136, 71.34390136, 71.34390136], 25 | atol=1e-14, 26 | ) 27 | assert_allclose( 28 | fields["velocity"]["data"][0, 0:5], 29 | [ 30 | 2.73382043e-16, 31 | 2.73382043e-16, 32 | 2.73382043e-16, 33 | 2.73382043e-16, 34 | 2.73382043e-16, 35 | ], 36 | atol=1e-14, 37 | ) 38 | assert_allclose( 39 | fields["spectrum_width"]["data"][0, 0:5], 40 | [2.85047687, 2.85047687, 2.85047687, 2.85047687, 2.85047687], 41 | atol=1e-14, 42 | ) 43 | assert_allclose( 44 | fields["skewness"]["data"][0, 0:5], 45 | [ 46 | 9.44294386e-17, 47 | 9.44294386e-17, 48 | 9.44294386e-17, 49 | 9.44294386e-17, 50 | 9.44294386e-17, 51 | ], 52 | atol=1e-14, 53 | ) 54 | assert_allclose( 55 | fields["kurtosis"]["data"][0, 0:5], 56 | [2.79911197, 2.79911197, 2.79911197, 2.79911197, 2.79911197], 57 | atol=1e-14, 58 | ) 59 | -------------------------------------------------------------------------------- /tests/retrieve/test_srv.py: -------------------------------------------------------------------------------- 1 | """ Unit Tests for Py-ART's retrieve/srv.py module. """ 2 | 3 | import numpy as np 4 | import pytest 5 | 6 | import pyart 7 | 8 | 9 | def test_storm_relative_velocity(): 10 | # Test all sweeps 11 | radar = pyart.io.read_nexrad_archive( 12 | "s3://noaa-nexrad-level2/2022/03/31/KDGX/KDGX20220331_012808_V06" 13 | ) 14 | sr_data = pyart.retrieve.storm_relative_velocity(radar, direction="NE", speed=20.0) 15 | test_data = [2.276456117630005, 4.776455879211426, 3.276456117630005] 16 | np.testing.assert_almost_equal(sr_data[-1][0:3].tolist(), test_data) 17 | 18 | # Test one sweep 19 | radar_sweep = radar.extract_sweeps([21]) 20 | sr_one_sweep = pyart.retrieve.storm_relative_velocity( 21 | radar_sweep, direction="NE", speed=20.0 22 | ) 23 | one_sweep_data = [0.1278250813484192, 4.6278252601623535, 4.6278252601623535] 24 | np.testing.assert_almost_equal(sr_one_sweep[0][0:3].tolist(), one_sweep_data) 25 | 26 | # Test missing parameters 27 | pytest.raises(ValueError, pyart.retrieve.storm_relative_velocity, radar) 28 | pytest.raises( 29 | ValueError, pyart.retrieve.storm_relative_velocity, radar, u=14.14, v=None 30 | ) 31 | -------------------------------------------------------------------------------- /tests/retrieve/test_vad.py: -------------------------------------------------------------------------------- 1 | """ Unit Tests for Py-ART's retrieve/vad.py module. """ 2 | 3 | import numpy as np 4 | from numpy.testing import assert_allclose 5 | 6 | import pyart 7 | 8 | 9 | def test_vad_michelson(): 10 | test_radar = pyart.testing.make_target_radar() 11 | height = np.arange(0.0, 1000.0, 200.0) 12 | speed = np.ones_like(height) * 5.0 13 | direction = np.array([0.0, 90.0, 180.0, 270.0, 45.0]) 14 | profile = pyart.core.HorizontalWindProfile(height, speed, direction) 15 | sim_vel = pyart.util.simulated_vel_from_profile(test_radar, profile) 16 | test_radar.add_field("velocity", sim_vel, replace_existing=True) 17 | 18 | velocity = "velocity" 19 | z_want = np.linspace(0.0, 10.0, 5) 20 | 21 | vad_height = [0.0, 2.5, 5.0, 7.5, 10.0] 22 | vad_speed = [4.9997, 4.9445, 4.8865, 4.8182, 4.7497] 23 | vad_direction = [89.8712, 90.5113, 91.2084, 92.0622, 92.9569] 24 | u_wind = [-4.9997, -4.9443, -4.8854, -4.8150, -4.7434] 25 | v_wind = [-0.0112, 0.0441, 0.1030, 0.1733, 0.2450] 26 | 27 | vad = pyart.retrieve.vad_michelson(test_radar, velocity, z_want) 28 | 29 | assert_allclose(vad.height, vad_height, rtol=1e-3, atol=1e-1) 30 | assert_allclose(vad.speed, vad_speed, rtol=1e-3, atol=1e-1) 31 | assert_allclose(vad.direction, vad_direction, rtol=1e-3, atol=1e-1) 32 | assert_allclose(vad.u_wind, u_wind, rtol=1e-3, atol=1e-1) 33 | assert_allclose(vad.v_wind, v_wind, rtol=1e-3, atol=1e-1) 34 | 35 | 36 | def test_vad_browning(): 37 | test_radar = pyart.testing.make_target_radar() 38 | height = np.arange(0.0, 1000.0, 200.0) 39 | speed = np.ones_like(height) * 5.0 40 | direction = np.array([0.0, 90.0, 180.0, 270.0, 45.0]) 41 | profile = pyart.core.HorizontalWindProfile(height, speed, direction) 42 | sim_vel = pyart.util.simulated_vel_from_profile(test_radar, profile) 43 | test_radar.add_field("velocity", sim_vel, replace_existing=True) 44 | 45 | velocity = "velocity" 46 | z_want = np.linspace(0.0, 10.0, 5) 47 | 48 | vad_height = [0.0, 2.5, 5.0, 7.5, 10.0] 49 | vad_speed = [4.9728, 4.9465, 4.8802, 4.82951, 4.7578] 50 | 51 | vad_direction = [90.3142, 90.6225, 91.4236, 92.0601, 92.99520] 52 | 53 | u_wind = [-4.9727, -4.9462, -4.8787, -4.8263, -4.7513] 54 | v_wind = [0.02727, 0.05374, 0.1212, 0.1736, 0.2486] 55 | 56 | vad = pyart.retrieve.vad_browning(test_radar, velocity, z_want) 57 | assert_allclose(vad.height, vad_height, rtol=1e-3, atol=1e-1) 58 | assert_allclose(vad.speed, vad_speed, rtol=1e-3, atol=1e-1) 59 | assert_allclose(vad.direction, vad_direction, rtol=1e-3, atol=1e-1) 60 | assert_allclose(vad.u_wind, u_wind, rtol=1e-3, atol=1e-1) 61 | assert_allclose(vad.v_wind, v_wind, rtol=1e-3, atol=1e-1) 62 | -------------------------------------------------------------------------------- /tests/test_debug_info.py: -------------------------------------------------------------------------------- 1 | """ Unit Tests for Py-ART's _debug_info module. """ 2 | 3 | import sys 4 | import warnings 5 | 6 | import pyart 7 | 8 | try: 9 | from StringIO import StringIO 10 | except ImportError: 11 | from io import StringIO 12 | 13 | 14 | def test_debug_info(): 15 | # test to see that something is written when _debug_info is called 16 | # we don't care what is written, just that something is. 17 | buf = StringIO() 18 | pyart._debug_info(buf) 19 | assert len(buf.getvalue()) > 0 20 | 21 | 22 | def test_debug_stdout(): 23 | # lack of error is assumed to mean that call succeed 24 | pyart._debug_info() 25 | 26 | 27 | # Class for Mocking ImportErrors from 28 | # http://stackoverflow.com/questions/2481511/mocking-importerror-in-python 29 | class DisableModules: 30 | def __init__(self, modules): 31 | self.modules = modules 32 | 33 | def find_module(self, fullname): 34 | if fullname in self.modules: 35 | raise ImportError(f"Debug import failure for {fullname}") 36 | 37 | 38 | def test_debug_info_all_disabled(): 39 | modules = [ 40 | "numpy", 41 | "scipy", 42 | "matplotlib", 43 | "netCDF4", 44 | "cylp", 45 | "glpk", 46 | "cvxopt", 47 | "mpl_toolkits", 48 | "platform", 49 | ] 50 | save_dict = {} 51 | for module in modules: 52 | if module in sys.modules: 53 | save_dict[module] = sys.modules[module] 54 | sys.modules.pop(module) 55 | fail_loader = DisableModules(modules) 56 | sys.meta_path.append(fail_loader) 57 | with warnings.catch_warnings(): 58 | warnings.simplefilter("ignore") 59 | # test to see that something is written when _debug_info is called 60 | # we don't care what is written, just that something is. 61 | buf = StringIO() 62 | pyart._debug_info(buf) 63 | assert len(buf.getvalue()) > 0 64 | # remove the Mocked ImportErrors 65 | sys.meta_path.remove(fail_loader) 66 | for module in save_dict.keys(): 67 | sys.modules[module] = save_dict[module] 68 | -------------------------------------------------------------------------------- /tests/testing/test_example_datasets.py: -------------------------------------------------------------------------------- 1 | import pathlib 2 | 3 | from pyart.testing.example_data import DATASETS, get_test_data, locate 4 | 5 | 6 | def test_registry(): 7 | files = DATASETS.registry_files 8 | assert len(files) > 0 9 | 10 | 11 | def test_locate(): 12 | p = locate() 13 | print(p) 14 | assert "datasets" in p 15 | assert pathlib.Path(p).exists 16 | 17 | 18 | def test_get_test_data(): 19 | test_file = "034142.mdv" 20 | file_path = get_test_data(test_file) 21 | assert pathlib.Path(file_path).exists 22 | -------------------------------------------------------------------------------- /tests/testing/test_sample_objects.py: -------------------------------------------------------------------------------- 1 | """ Unit Tests for Py-ART's testing/sample_objects.py module. """ 2 | 3 | import numpy as np 4 | 5 | from pyart.testing.sample_objects import make_gaussian_storm_grid 6 | 7 | 8 | def test_gaussian_storm_grid_results_correct(): 9 | """ 10 | Test for the make_gaussian_storm_grid function. 11 | 12 | Checks grid shape, limits, field data, and masking. 13 | These test are focusing on statistical properties of the storm and not on comparing exact storm values. 14 | """ 15 | grid_len = 32 16 | min_value = 5 17 | max_value = 45 18 | mask_margin = 3 19 | 20 | # Create grid 21 | gaussian_storm_2d = make_gaussian_storm_grid() 22 | 23 | # Test Shape 24 | assert gaussian_storm_2d.fields["reflectivity"]["data"].shape == ( 25 | 2, 26 | grid_len, 27 | grid_len, 28 | ), "Grid shape mismatch" 29 | 30 | # Test Data 31 | assert ( 32 | gaussian_storm_2d.fields["reflectivity"]["data"] is not None 33 | ), "No data in reflectivity field" 34 | 35 | # Test Masking 36 | mask = gaussian_storm_2d.fields["reflectivity"]["data"].mask 37 | assert np.all(mask[:, :mask_margin, :]), "Masking at the boundary is incorrect" 38 | assert np.all(mask[:, -mask_margin:, :]), "Masking at the boundary is incorrect" 39 | assert np.all(mask[:, :, :mask_margin]), "Masking at the boundary is incorrect" 40 | assert np.all(mask[:, :, -mask_margin:]), "Masking at the boundary is incorrect" 41 | 42 | storm_data = gaussian_storm_2d.fields["reflectivity"]["data"] 43 | 44 | # Test for Max and Min 45 | assert np.isclose( 46 | np.max(storm_data), max_value 47 | ), "Maximum value does not match expected" 48 | assert np.isclose( 49 | np.min(storm_data[~storm_data.mask]), min_value 50 | ), "Minimum value does not match expected" 51 | 52 | # Test Mean and SD 53 | expected_mean = 8.666844653650797 54 | expected_std = 7.863066829145 55 | assert np.isclose( 56 | np.mean(storm_data), expected_mean, atol=5 57 | ), "Mean value out of expected range" 58 | assert np.isclose( 59 | np.std(storm_data), expected_std, atol=5 60 | ), "Standard deviation out of expected range" 61 | 62 | # Test Central Value 63 | assert storm_data[0, 15, 15] == max_value, "Maximum value is not at the center" 64 | -------------------------------------------------------------------------------- /tests/util/test_circular_stats.py: -------------------------------------------------------------------------------- 1 | """ Unit tests for the circular_stats.py module. """ 2 | 3 | import numpy as np 4 | from numpy.testing import assert_almost_equal 5 | 6 | from pyart.util import circular_stats 7 | 8 | 9 | def test_compute_directional_stats(): 10 | field_1d = np.ma.array([1, 1, 3, 4, 5]) 11 | field_1d.mask = [True, False, False, False, False] 12 | field = np.tile(field_1d, (10, 1)) 13 | 14 | mean, nvalid = circular_stats.compute_directional_stats(field, axis=1) 15 | median, nvalid = circular_stats.compute_directional_stats( 16 | field, axis=1, avg_type="median" 17 | ) 18 | 19 | assert np.all(mean == 3.25) 20 | assert np.all(median == 3.5) 21 | assert np.all(nvalid == 4) 22 | 23 | # Test with larger nb of nvalid_min 24 | mean, nvalid = circular_stats.compute_directional_stats(field, axis=1, nvalid_min=5) 25 | assert np.all(mean.mask) 26 | 27 | # Test other direction 28 | mean, nvalid = circular_stats.compute_directional_stats(field, axis=0) 29 | median, nvalid = circular_stats.compute_directional_stats( 30 | field, axis=0, avg_type="median" 31 | ) 32 | 33 | ref = np.ma.array([np.nan, 1, 3, 4, 5], mask=[1, 0, 0, 0, 0]) 34 | assert np.all(mean == ref) 35 | assert np.all(median == ref) 36 | assert np.all(nvalid == [0, 10, 10, 10, 10]) 37 | 38 | 39 | def test_mean_of_two_angles(): 40 | mean = circular_stats.mean_of_two_angles(np.pi / 4 + 0.2, 7 * np.pi / 4) 41 | assert_almost_equal(mean, 0.10, 2) 42 | 43 | 44 | def test_mean_of_two_angles_deg(): 45 | mean = circular_stats.mean_of_two_angles_deg(359.9, 0.5) 46 | assert_almost_equal(mean, 0.20, 2) 47 | 48 | 49 | def test_angles_radians(): 50 | dist = [2 * np.pi - 0.1, 0, 0.1] 51 | mean = circular_stats.angular_mean(dist) 52 | assert_almost_equal(mean, 0.0, 2) 53 | std = circular_stats.angular_std(dist) 54 | assert_almost_equal(std, 0.08, 2) 55 | 56 | 57 | def test_angles_degrees(): 58 | dist = [350, 0, 10, 20, 30] 59 | mean = circular_stats.angular_mean_deg(dist) 60 | assert_almost_equal(mean, 10.0, 0) 61 | std = circular_stats.angular_std_deg(dist) 62 | assert_almost_equal(std, 14.0, 0) 63 | 64 | 65 | def test_angles_interval(): 66 | dist = [7.5, 8.5, 9.5, 9.5, -9.5, -8.5] 67 | mean = circular_stats.interval_mean(dist, -10, 10) 68 | assert_almost_equal(mean, 9.5, 1) 69 | std = circular_stats.interval_std(dist, -10, 10) 70 | assert_almost_equal(std, 1.3, 1) 71 | -------------------------------------------------------------------------------- /tests/util/test_columnsect.py: -------------------------------------------------------------------------------- 1 | """ Unit Tests for Py-ART's util/columnsect.py module. """ 2 | 3 | import numpy as np 4 | 5 | import pyart 6 | 7 | # read in example file 8 | radar = pyart.io.read_nexrad_archive(pyart.testing.NEXRAD_ARCHIVE_MSG31_FILE) 9 | 10 | 11 | def test_get_azimuth(): 12 | """ 13 | test to make sure azimuth is correct to Everett, WA 14 | """ 15 | azimuth = pyart.util.columnsect.for_azimuth( 16 | radar.latitude["data"][0], 47.97, radar.longitude["data"][0], -122.20 17 | ) 18 | test_azi = abs(np.around(azimuth, 2) - 138.57) 19 | assert test_azi < 0.001 20 | 21 | 22 | def test_sphere_distance(): 23 | """ 24 | test to make sure sphere distance is correct to Everett, WA 25 | """ 26 | distance = pyart.util.columnsect.sphere_distance( 27 | radar.latitude["data"][0], 47.97, (radar.longitude["data"][0]), -122.20 28 | ) 29 | test_dis = abs(np.around(distance / 1000.0, 2) - 33.27) 30 | assert test_dis < 0.001 31 | 32 | 33 | def test_get_field_location(): 34 | """ 35 | test to make sure column above location is pulled correctly 36 | """ 37 | column = pyart.util.columnsect.get_field_location(radar, 47.97, -122.20) 38 | 39 | # check to make sure z-gate is pulled correctly. 40 | test_height = abs(column.height[0] - 564) 41 | assert test_height < 0.001 42 | 43 | # check to make sure reflectivity value is minimum 44 | test_z = abs(column.reflectivity[0] + 32) 45 | assert test_z < 0.001 46 | 47 | 48 | def test_column_vertical_profile(): 49 | """ 50 | test to make sure CVP column above location is pulled correctly 51 | """ 52 | column = pyart.util.columnsect.column_vertical_profile( 53 | radar, 47.97, -122.20, azimuth_spread=3, spatial_spread=5 54 | ) 55 | # check to make sure z-gate is pulled correctly. 56 | test_height = abs(column.height.data[0] - 565.7) 57 | assert test_height < 0.001 58 | 59 | # check to make sure reflectivity value is minimum 60 | test_z = abs(column.reflectivity.data[0] + 32) 61 | assert test_z < 0.001 62 | 63 | # check to make sure time-offset is correct 64 | test_offset = abs(column.time_offset.data[0] - 27.492) 65 | assert test_offset < 0.001 66 | -------------------------------------------------------------------------------- /tests/util/test_datetime_utils.py: -------------------------------------------------------------------------------- 1 | """ Unit tests for Py-ART's util/datetime_utils.py module. """ 2 | 3 | import cftime 4 | import netCDF4 5 | 6 | import pyart 7 | 8 | radar = pyart.testing.make_empty_ppi_radar(10, 36, 1) 9 | 10 | 11 | def test_datetime_from_radar(): 12 | test_date = cftime.DatetimeGregorian(1989, 1, 1, 0, 0, 1, 0) 13 | 14 | date = pyart.util.datetime_from_radar(radar, epoch=False) 15 | assert date == test_date 16 | 17 | date_epoch = pyart.util.datetime_from_radar(radar, epoch=True) 18 | assert date_epoch == test_date 19 | 20 | 21 | def test_datetimes_from_radar(): 22 | test_date_first = cftime.DatetimeGregorian(1989, 1, 1, 0, 0, 1, 0) 23 | test_date_last = cftime.DatetimeGregorian(1989, 1, 1, 0, 0, 36, 0) 24 | 25 | dates = pyart.util.datetimes_from_radar(radar, epoch=False) 26 | assert dates[0] == test_date_first 27 | assert dates[-1] == test_date_last 28 | 29 | dates_epoch = pyart.util.datetimes_from_radar(radar, epoch=True) 30 | assert dates_epoch[0] == test_date_first 31 | assert dates_epoch[-1] == test_date_last 32 | 33 | 34 | ds = netCDF4.Dataset(pyart.testing.CFRADIAL_PPI_FILE) 35 | 36 | 37 | def test_datetime_from_dataset(): 38 | test_date = cftime.DatetimeGregorian(2011, 5, 20, 10, 54, 16, 0) 39 | 40 | date = pyart.util.datetime_from_dataset(ds, epoch=False) 41 | assert date == test_date 42 | 43 | date_epoch = pyart.util.datetime_from_dataset(ds, epoch=True) 44 | assert date_epoch == test_date 45 | 46 | 47 | def test_datetimes_from_dataset(): 48 | test_date_first = cftime.DatetimeGregorian(2011, 5, 20, 10, 54, 16, 0) 49 | test_date_last = cftime.DatetimeGregorian(2011, 5, 20, 10, 54, 15, 0) 50 | 51 | dates = pyart.util.datetimes_from_dataset(ds, epoch=False) 52 | assert dates[0] == test_date_first 53 | assert dates[-1] == test_date_last 54 | 55 | dates_epoch = pyart.util.datetimes_from_dataset(ds, epoch=True) 56 | assert dates_epoch[0] == test_date_first 57 | assert dates_epoch[-1] == test_date_last 58 | 59 | 60 | grid = pyart.testing.make_empty_grid( 61 | (2, 5, 5), ((0, 1000), (-2500, 2500), (-2500, 2500)) 62 | ) 63 | 64 | 65 | def test_datetime_from_grid(): 66 | test_date = cftime.DatetimeGregorian(2000, 1, 1, 0, 0, 0, 0) 67 | 68 | date = pyart.util.datetime_from_grid(grid, epoch=False) 69 | assert date == test_date 70 | 71 | date_epoch = pyart.util.datetime_from_grid(grid, epoch=True) 72 | assert date_epoch == test_date 73 | -------------------------------------------------------------------------------- /tests/util/test_hildebrand_sekhon.py: -------------------------------------------------------------------------------- 1 | """ Unit tests for the hildebrand_sekhon module. """ 2 | 3 | import numpy as np 4 | from numpy.testing import assert_almost_equal 5 | 6 | from pyart.util import hildebrand_sekhon 7 | 8 | SAMPLE_SPECTRUM = np.array( 9 | [ 10 | 11.089, 11 | 10.386, 12 | 11.081, 13 | 9.696, 14 | 9.21, 15 | 11.103, 16 | 9.425, 17 | 10.222, 18 | 8.393, 19 | 11.67, 20 | 9.632, 21 | 10.096, 22 | 9.807, 23 | 10.374, 24 | 8.065, 25 | 8.682, 26 | 10.164, 27 | 320.206, 28 | 480.898, 29 | 640.818, 30 | 490.483, 31 | 318.578, 32 | 9.682, 33 | 10.035, 34 | 9.134, 35 | 8.554, 36 | 9.274, 37 | 9.463, 38 | 9.501, 39 | 10.036, 40 | 9.211, 41 | 8.265, 42 | ] 43 | ) 44 | 45 | 46 | def test_hildebrand_sekhon(): 47 | noise_params = hildebrand_sekhon.estimate_noise_hs74(SAMPLE_SPECTRUM) 48 | mean, thresh, var, nnoise = noise_params 49 | assert_almost_equal(mean, 10, 0) 50 | assert_almost_equal(thresh, 12, 0) 51 | assert_almost_equal(var, 0.79, 2) 52 | assert nnoise == 27 53 | -------------------------------------------------------------------------------- /tests/util/test_simulated_vel.py: -------------------------------------------------------------------------------- 1 | """ Unit tests for Py-ART's util/simulated_vel.py module. """ 2 | 3 | import numpy as np 4 | from numpy.testing import assert_almost_equal 5 | 6 | import pyart 7 | 8 | 9 | def test_simulated_velocity_from_profile(): 10 | radar = pyart.testing.make_target_radar() 11 | # profile of 10 m/s winds out of the west at all heights 12 | height = np.arange(0, 1000, 30) 13 | speed = np.ones_like(height) * 10.0 14 | direction = np.ones_like(height) * 270.0 15 | profile = pyart.core.HorizontalWindProfile(height, speed, direction) 16 | 17 | sim_vel = pyart.util.simulated_vel_from_profile(radar, profile) 18 | 19 | assert sim_vel["data"].shape == (360, 50) 20 | 21 | # check simulated velocities along the north, east, south and west rays 22 | assert_almost_equal(sim_vel["data"][[0, 90, 180, 270], 10], [0, 10, 0, -10], 0) 23 | -------------------------------------------------------------------------------- /tests/util/test_xsect.py: -------------------------------------------------------------------------------- 1 | """ Unit tests for the xsect.py module. """ 2 | 3 | from numpy.testing import assert_almost_equal 4 | 5 | import pyart 6 | 7 | 8 | def test_cross_section_ppi(): 9 | radar = pyart.testing.make_target_radar() 10 | xsect = pyart.util.cross_section_ppi(radar, [45, 60]) 11 | 12 | assert xsect.nsweeps == 2 13 | assert xsect.nrays == 2 14 | assert len(xsect.time["data"]) == 2 15 | assert_almost_equal(xsect.azimuth["data"][0], 45) 16 | assert_almost_equal(xsect.azimuth["data"][1], 60) 17 | assert_almost_equal(xsect.elevation["data"][0], 0.75, 2) 18 | assert_almost_equal(xsect.elevation["data"][1], 0.75, 2) 19 | assert xsect.fields["reflectivity"]["data"].shape == (2, 50) 20 | assert len(xsect.sweep_number["data"]) == 2 21 | assert len(xsect.sweep_mode["data"]) == 2 22 | assert len(xsect.fixed_angle["data"]) == 2 23 | assert xsect.scan_type == "rhi" 24 | assert_almost_equal(xsect.sweep_start_ray_index["data"][0], 0) 25 | assert_almost_equal(xsect.sweep_start_ray_index["data"][1], 1) 26 | assert_almost_equal(xsect.sweep_end_ray_index["data"][0], 0) 27 | assert_almost_equal(xsect.sweep_end_ray_index["data"][1], 1) 28 | --------------------------------------------------------------------------------