├── environment.yml ├── auto_export.py ├── README.md ├── LICENSE ├── interactive_hist.ipynb └── QAtools_notebook.ipynb /environment.yml: -------------------------------------------------------------------------------- 1 | name: qatools 2 | channels: 3 | - conda-forge 4 | - defaults 5 | dependencies: 6 | - jupyter=1.0.0 7 | - netCDF4=1.6.4 8 | - numpy=1.25.1 9 | - bokeh=3.2.0 10 | - holoviews=1.16.2 11 | - seaborn=0.12.2 12 | - scipy=1.11.1 13 | - hvplot=0.8.4 14 | - pandas=2.0.3 15 | - pip=23.2 16 | - pip: 17 | - nbconvert[webpdf] 18 | - metpy==1.5.1 19 | 20 | -------------------------------------------------------------------------------- /auto_export.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ##################################################################### 3 | # Script to auto-generate QAtools Notebooks two arguments are 4 | # needed: Project and Flight the notebook is executed with the args 5 | # passed into the notebook and then the notebook is exported 6 | # to html and renamed. The last step is to scp to /net/www 7 | # 8 | # Author: Taylor Thomas (2023) 9 | # 10 | # Copyright University Corporation for Atmospheric Research (2023 11 | #################################################################### 12 | 13 | import argparse 14 | import os 15 | import pdfkit 16 | from playwright.sync_api import sync_playwright 17 | # Get arguments 18 | parser = argparse.ArgumentParser() 19 | parser.add_argument('--project', type=str, required=True) 20 | parser.add_argument('--flight', type=str, required=True) 21 | parser.add_argument('--format', type=str, choices = ['html','pdf'], default = 'html',required=False) 22 | parser.add_argument('--hist', action='store_true', default=False, help='Generate interactive histogram notebook') 23 | #boolean flag that defaults to false 24 | args = parser.parse_args() 25 | 26 | # Assign arguments 27 | qa_project = args.project 28 | qa_flight = args.flight 29 | qa_format = args.format 30 | qa_hist = args.hist 31 | output_filename = qa_project+qa_flight 32 | hist_filename = output_filename +'_aerosol-cloud' 33 | 34 | # Create environment vars to be used by the notebook 35 | os.environ['QA_CL'] = 'command_line_mode' 36 | os.environ['QA_PROJ'] = qa_project 37 | os.environ['QA_FLIGHT'] = qa_flight 38 | 39 | os.system('echo "export QA_QCL="command_line_mode"" >> ~/.qa_vars') 40 | os.system('echo "export QA_PROJ="qa_project"" >> ~/.qa_vars') 41 | os.system('echo "export QA_FLIGHT="qa_flight"" >> ~/.qa_vars') 42 | 43 | # Execute the cells in the notebook 44 | # Set PYDEVD_DISABLE_FILE_VALIDATION=1 to supress debugger warning about 45 | # frozen modules causing debugger to miss breakpoints 46 | os.system('PYDEVD_DISABLE_FILE_VALIDATION=1 jupyter nbconvert --to notebook --allow-errors --ExecutePreprocessor.timeout=-1 --execute --inplace QAtools_notebook.ipynb') 47 | 48 | # Convert to HTML or PDF 49 | 50 | os.system('jupyter nbconvert QAtools_notebook.ipynb --output '+output_filename+' --no-input --to html') 51 | # os.system('jupyter nbconvert QAtools_notebook.ipynb --output '+output_filename+' --no-input --allow-chromium-download --to webpdf --PDFExporter.paginate=False --PDFExporter.custom_args="[\'--no-sandbox\', \'--disable-dev-shm-usage\', \'--disable-gpu\', \'--memory-pressure-off\', \'--max-old-space-size=8192\', \'--virtual-time-budget=600\', \'--timeout=600\']" --HTMLExporter.theme=light') 52 | # Convert to PDF based on format 53 | def html_to_pdf_playwright(html_file, pdf_file): 54 | with sync_playwright() as p: 55 | browser = p.chromium.launch(headless=True) 56 | page = browser.new_page() 57 | 58 | # Load the HTML file 59 | page.goto(f'file://{os.path.abspath(html_file)}') 60 | 61 | # Wait for content to load 62 | page.wait_for_load_state('networkidle') 63 | # Generate PDF with options 64 | page.pdf( 65 | path=pdf_file, 66 | print_background=True, 67 | prefer_css_page_size=True 68 | ) 69 | 70 | browser.close() 71 | 72 | try: 73 | html_to_pdf_playwright(f'{output_filename}.html', f'{output_filename}.pdf') 74 | print(f"PDF created successfully: {output_filename}.pdf") 75 | except Exception as e: 76 | print(f"Playwright PDF conversion failed: {e}") 77 | 78 | # Convert to HTML or PDF 79 | if qa_hist == True: 80 | os.system('PYDEVD_DISABLE_FILE_VALIDATION=1 jupyter nbconvert --to notebook --allow-errors --ExecutePreprocessor.timeout=-1 --execute --inplace interactive_hist.ipynb') 81 | os.system('jupyter nbconvert interactive_hist.ipynb --output '+hist_filename+' --no-input --to html') 82 | #os.system('jupyter nbconvert --clear-output --inplace interactive_hist.ipynb') 83 | #os.system('jupyter nbconvert interactive_hist.ipynb --output '+hist_filename+' --allow-chromium-download --no-input --to webpdf') ##Too large to run currently -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![DOI:<10.26023>](http://img.shields.io/badge/DOI-10.26023-green.svg)](https://doi.org/10.26023/a0e3-4n78) 2 | 3 | __Note: Information on the data flow is in this repos wiki__ 4 | ## QAtools_notebook README 5 | 6 | The QAtools_notebook is a Jupyter notebook that uses python to generate a set of interactive plots to aid NCAR RAF Science and Instrumentation Group in quick-detection of data quality status immediately following a flight. The notebook uses bokeh, metpy, and matplotlib to generate plots. The notebook requires that the flight netCDF is available via a defined path. You must update the path to point to the directory containing your data file(s). 7 | 8 | ### Installation 9 | 10 | You will need git, conda, and firefox installed on the machine you are going to be running the notebook. 11 | 12 | Resource for installing / checking git and conda installation: 13 | 14 | * https://github.com/git-guides/install-git 15 | * https://conda.io/projects/conda/en/latest/user-guide/install/index.html 16 | 17 | The notebook is stored in this GitHub repository. To clone the repository, execute the following on the command line: 18 | 19 | `git clone https://github.com/NCAR/aircraft_QAtools_notebook` 20 | 21 | If you do not wish to have a local git repository, you can simply download the .zip file from this repository: 22 | 23 | https://github.com/NCAR/aircraft_QAtools_notebook 24 | 25 | Then you will need to `cd` into the directory. There is a file `environment.yml` that contains a list of all of the dependencies needed to run the notebook. 26 | 27 | The following command can be used to create a conda environment based on the contents of the environment.yml file. 28 | 29 | `conda env create --file=environment.yml` 30 | 31 | You will then need to activate this environment using this command: 32 | 33 | `conda activate qatools` 34 | 35 | If you wish to convert the output to a pdf using webPDF you will then need to install chromium using this command: 36 | 37 | `playwright install chromium` 38 | 39 | How to use this notebook: 40 | 41 | ### Option 1: Interactively 42 | 43 | After installation is complete, you can either type `jupyter-lab` or `jupyter-notebook` depending on the interface you would prefer. 44 | 45 | This will launch the notebook in your browser, and you can modify the code directly. The two variables that you will need to change for the given project and flight that you are interested in generating plots for are: `project` and `flight`. To run the code, click on a cell to select it, then press the play button in the toolbar above. 46 | 47 | You can also update `interactive_histogram` to either True or False. Your selection will determine whether histogram / size distribution heatmap plots are generated for corresponding data. 48 | 49 | ### Option 2: Command Line Mode 50 | 51 | If you would like to generate HTML exports from the notebook automatically, you can execute the script `auto_export.py` which provides you with the ability to pass command line arguments for project and flight. These will be stored and passed into the notebook at execution time. Then once the plots are generated, the output will be exported to HTML without the code sections included. This is the approach that is used when processing data on the RAF Ground Station Computer, as this script `auto_export.py` is called by `push_data.py`. 52 | 53 | `./auto_export.py --project --flight ` 54 | 55 | If you would like to generate a pdf export of the notebook using the same method, add the optional argument `--format pdf`: 56 | 57 | `./auto_export.py --project --flight --format pdf` 58 | 59 | The repo is checked out at /home/local/aircraft_QAtools_notebook and if GDRIVE = True in fieldProc_setup, then then the output HTML will sync to Google Drive. Then it will sync to /scr/raf_Raw_Data/CAESAR/field_sync on EOL servers. 60 | 61 | ## CAUTION 62 | 63 | Just because auto_export.py completes without error does not mean the HTML file contains valid plots. 64 | 65 | ``` 66 | > ./auto_export.py --project ACCLIP --flight rf01 67 | [NbConvertApp] Converting notebook QAtools_notebook.ipynb to notebook 68 | [NbConvertApp] Writing 177795 bytes to QAtools_notebook.ipynb 69 | [NbConvertApp] Converting notebook QAtools_notebook.ipynb to html 70 | [NbConvertApp] Writing 425557 bytes to QAtools_notebook.html 71 | ``` 72 | Notice the small size of the QAtools_notebook.html file. It should be 200-400MB rather than 400KB. If you view the HTML file, the plots will show errors that need to be fixed. 73 | 74 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /interactive_hist.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": { 7 | "execution": { 8 | "iopub.execute_input": "2024-03-14T19:03:25.409271Z", 9 | "iopub.status.busy": "2024-03-14T19:03:25.409000Z", 10 | "iopub.status.idle": "2024-03-14T19:03:28.451309Z", 11 | "shell.execute_reply": "2024-03-14T19:03:28.450975Z" 12 | } 13 | }, 14 | "outputs": [], 15 | "source": [ 16 | "import xarray as xr\n", 17 | "import hvplot.xarray\n", 18 | "import netCDF4\n", 19 | "import pandas as pd\n", 20 | "import netCDF4\n", 21 | "import pandas as pd\n", 22 | "import numpy as np\n", 23 | "import seaborn as sns\n", 24 | "import matplotlib.pyplot as plt\n", 25 | "from metpy.plots import SkewT\n", 26 | "from metpy.units import pandas_dataframe_to_unit_arrays, units\n", 27 | "from datetime import datetime, timedelta\n", 28 | "from IPython.display import display\n", 29 | "from bokeh.io import push_notebook, show, output_notebook\n", 30 | "from bokeh.layouts import row\n", 31 | "from bokeh.plotting import figure, show\n", 32 | "from bokeh.models import Title, CustomJS, Select, TextInput, Button, LinearAxis, Range1d, FuncTickFormatter\n", 33 | "from bokeh.models.formatters import DatetimeTickFormatter\n", 34 | "from bokeh.palettes import Category10\n", 35 | "import warnings\n", 36 | "import itertools \n", 37 | "import holoviews as hv \n", 38 | "from holoviews import dim, opts\n", 39 | "import hvplot.pandas\n", 40 | "hv.extension('bokeh', 'matplotlib')\n", 41 | "warnings.filterwarnings('ignore')\n", 42 | "output_notebook()\n", 43 | "%matplotlib inline" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": null, 49 | "metadata": { 50 | "execution": { 51 | "iopub.execute_input": "2024-03-14T19:03:28.452821Z", 52 | "iopub.status.busy": "2024-03-14T19:03:28.452733Z", 53 | "iopub.status.idle": "2024-03-14T19:03:28.656008Z", 54 | "shell.execute_reply": "2024-03-14T19:03:28.655692Z" 55 | } 56 | }, 57 | "outputs": [], 58 | "source": [ 59 | "import os\n", 60 | "try:\n", 61 | " os.system('source ~/.bash_profile')\n", 62 | " CL = os.environ.get(\"QA_CL\")\n", 63 | "except:\n", 64 | " pass" 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": null, 70 | "metadata": { 71 | "execution": { 72 | "iopub.execute_input": "2024-03-14T19:03:28.657490Z", 73 | "iopub.status.busy": "2024-03-14T19:03:28.657404Z", 74 | "iopub.status.idle": "2024-03-14T19:03:28.659843Z", 75 | "shell.execute_reply": "2024-03-14T19:03:28.659538Z" 76 | } 77 | }, 78 | "outputs": [], 79 | "source": [ 80 | "if CL == 'command_line_mode':\n", 81 | " try:\n", 82 | " project = os.environ.get('QA_PROJ')\n", 83 | " flight = os.environ.get('QA_FLIGHT')\n", 84 | " except:\n", 85 | " pass\n", 86 | "else:\n", 87 | " try:\n", 88 | " #######################################################################\n", 89 | " ####### change project and flight below if running interactively ######\n", 90 | " #######################################################################\n", 91 | " #project = 'wecan'\n", 92 | " #flight = 'rf18'\n", 93 | " project = 'CGWAVES'\n", 94 | " flight = 'tf02'\n", 95 | " #######################################################################\n", 96 | " except:\n", 97 | " pass\n", 98 | "print('Project: ' + project)\n", 99 | "print('Flight: ' + flight)" 100 | ] 101 | }, 102 | { 103 | "cell_type": "code", 104 | "execution_count": null, 105 | "metadata": {}, 106 | "outputs": [], 107 | "source": [ 108 | "#Set the DATA_DIR os environment variable to the current working directory if it is not already set\n", 109 | "if 'DATA_DIR' not in os.environ:\n", 110 | " os.environ['DATA_DIR'] = os.getcwd()" 111 | ] 112 | }, 113 | { 114 | "cell_type": "code", 115 | "execution_count": null, 116 | "metadata": { 117 | "execution": { 118 | "iopub.execute_input": "2024-03-14T19:03:28.661235Z", 119 | "iopub.status.busy": "2024-03-14T19:03:28.661144Z", 120 | "iopub.status.idle": "2024-03-14T19:03:28.667580Z", 121 | "shell.execute_reply": "2024-03-14T19:03:28.667311Z" 122 | } 123 | }, 124 | "outputs": [], 125 | "source": [ 126 | "# use the user provided project and flight information to build the path and store the data file as an object\n", 127 | "########################################################################\n", 128 | "# update filepath based on where the netcdf files are located\n", 129 | "########################################################################\n", 130 | "yr = '2024'\n", 131 | "filepath = os.environ.get('DATA_DIR')+'/'+project +'/'\n", 132 | "input_file = project + flight + '.nc'\n", 133 | "nc = netCDF4.Dataset(filepath + input_file, mode='r')\n", 134 | "\n", 135 | "# try to get global attributes from the netcdf file if they are present\n", 136 | "# determine preliminary or final status\n", 137 | "try:\n", 138 | " proc_status = nc.getncattr('WARNING')\n", 139 | " print(proc_status)\n", 140 | "except:\n", 141 | " proc_status = 'final'\n", 142 | "\n", 143 | "# determine the NIDAS version\n", 144 | "try:\n", 145 | " nidas = nc.getncattr('NIDASrevision')\n", 146 | " print('NIDAS version: ' + nidas)\n", 147 | "except Exception as e:\n", 148 | " print(e)\n", 149 | "\n", 150 | "# determine the NIMBUS version\n", 151 | "try:\n", 152 | " nimbus = nc.getncattr('RepositoryRevision')\n", 153 | " print('NIMBUS version: ' + nimbus)\n", 154 | "except Exception as e:\n", 155 | " print(e)\n", 156 | "\n", 157 | "# determine the processing date and time\n", 158 | "try:\n", 159 | " proc_date = nc.getncattr('date_created')\n", 160 | " print('Processing Date & Time: ' + proc_date)\n", 161 | "except Exception as e:\n", 162 | " print(e)" 163 | ] 164 | }, 165 | { 166 | "cell_type": "code", 167 | "execution_count": null, 168 | "metadata": { 169 | "execution": { 170 | "iopub.execute_input": "2024-03-14T19:03:28.668864Z", 171 | "iopub.status.busy": "2024-03-14T19:03:28.668787Z", 172 | "iopub.status.idle": "2024-03-14T19:03:28.786470Z", 173 | "shell.execute_reply": "2024-03-14T19:03:28.786017Z" 174 | } 175 | }, 176 | "outputs": [], 177 | "source": [ 178 | "# sometimes the netcdf4 api produces an issue with big-endian buffer on little-endian compiler\n", 179 | "byte_swap = False\n", 180 | "\n", 181 | "# create empty placeholders for asc, histo_asc and units\n", 182 | "asc = {}\n", 183 | "histo_asc = {}\n", 184 | "units = {}\n", 185 | "cellsize = {}\n", 186 | "\n", 187 | "# use the netcdf4 api to get the netcdf data into a dataframe\n", 188 | "try:\n", 189 | " \n", 190 | " # loop over keys in netCDF file and organize\n", 191 | " for i in nc.variables.keys():\n", 192 | " dims = str(nc.variables[i].dimensions)\n", 193 | " \n", 194 | " # this if section retrieves data that has time dimension only\n", 195 | " # this section retrieves data that has a size distribution dimension in addition to time\n", 196 | " if \"sps1\" in dims:\n", 197 | " histo_output = nc.variables[i][:, 0, :]\n", 198 | " # sometimes the netcdf4 api produces an issue with big-endian buffer on little-endian compiler\n", 199 | " if byte_swap == True:\n", 200 | " histo_output = histo_output.byteswap().newbyteorder()\n", 201 | " else:\n", 202 | " pass\n", 203 | " histo_asc[i] = pd.DataFrame(histo_output)\n", 204 | " \n", 205 | " # this try / except block accommodates size distribution data that has an attribute CellSizes\n", 206 | " try:\n", 207 | " cellsize = nc.variables[i].getncattr('CellSizes')\n", 208 | " histo_asc[i].columns = pd.MultiIndex.from_tuples(zip(histo_asc[i].columns, cellsize))\n", 209 | " except Exception as e:\n", 210 | " histo_asc[i].columns = pd.MultiIndex.from_tuples(zip(histo_asc[i].columns, histo_asc[i].columns))\n", 211 | " else:\n", 212 | " pass \n", 213 | "\n", 214 | " \n", 215 | " # concatenate the histogram data\n", 216 | " histo_asc = pd.concat(histo_asc, axis=1, ignore_index=False)\n", 217 | " histo_asc.columns = histo_asc.columns.droplevel(1)\n", 218 | " colors = itertools.cycle(Category10[6])\n", 219 | " \n", 220 | " \n", 221 | "except Exception as e:\n", 222 | " print(e)" 223 | ] 224 | }, 225 | { 226 | "cell_type": "code", 227 | "execution_count": null, 228 | "metadata": { 229 | "execution": { 230 | "iopub.execute_input": "2024-03-14T19:03:28.788439Z", 231 | "iopub.status.busy": "2024-03-14T19:03:28.788167Z", 232 | "iopub.status.idle": "2024-03-14T19:03:28.790298Z", 233 | "shell.execute_reply": "2024-03-14T19:03:28.790020Z" 234 | } 235 | }, 236 | "outputs": [], 237 | "source": [ 238 | "nc.close()" 239 | ] 240 | }, 241 | { 242 | "cell_type": "code", 243 | "execution_count": null, 244 | "metadata": { 245 | "execution": { 246 | "iopub.execute_input": "2024-03-14T19:03:28.791691Z", 247 | "iopub.status.busy": "2024-03-14T19:03:28.791598Z", 248 | "iopub.status.idle": "2024-03-14T19:04:39.471777Z", 249 | "shell.execute_reply": "2024-03-14T19:04:39.471439Z" 250 | } 251 | }, 252 | "outputs": [], 253 | "source": [ 254 | "df = xr.open_dataset(filepath+input_file) #open the dataset as an xarray\n", 255 | "ds =df.where(df.GGSPD >60, drop = True) #filter the data to not include time on the ground\n", 256 | "x_width = 1200\n", 257 | "y_height = 400" 258 | ] 259 | }, 260 | { 261 | "cell_type": "code", 262 | "execution_count": null, 263 | "metadata": { 264 | "execution": { 265 | "iopub.execute_input": "2024-03-14T19:04:39.473540Z", 266 | "iopub.status.busy": "2024-03-14T19:04:39.473444Z", 267 | "iopub.status.idle": "2024-03-14T19:04:39.490460Z", 268 | "shell.execute_reply": "2024-03-14T19:04:39.490145Z" 269 | } 270 | }, 271 | "outputs": [], 272 | "source": [ 273 | "# create lists of the histogram variables\n", 274 | "try:\n", 275 | " histovar_list = list(histo_asc.columns.levels[0])\n", 276 | " histovar_list = [var for var in histovar_list if not var.endswith('VXL')]\n", 277 | " histobin_list = []\n", 278 | "except:\n", 279 | " pass\n", 280 | "\n", 281 | "# loop over the length of the histogram variable list\n", 282 | "try:\n", 283 | " for i in range(len(histovar_list)):\n", 284 | " histobin_list.append(max(list(histo_asc[histovar_list[i]]))+1)\n", 285 | "except:\n", 286 | " pass\n", 287 | "\n", 288 | "# create list of histogram units\n", 289 | "try:\n", 290 | " histo_units = []\n", 291 | " for i in histovar_list:\n", 292 | " histo_units.append(nc.variables[i].getncattr('units'))\n", 293 | "except:\n", 294 | " pass" 295 | ] 296 | }, 297 | { 298 | "cell_type": "code", 299 | "execution_count": null, 300 | "metadata": {}, 301 | "outputs": [], 302 | "source": [ 303 | "def plot_histogram(ds, var, i):\n", 304 | " try:\n", 305 | " # Check if the variable exists in the dataset\n", 306 | " if var not in ds:\n", 307 | " print(f\"{var} not in dataset\")\n", 308 | " return\n", 309 | " \n", 310 | " # Get dimensions and check if we have the right ones\n", 311 | " dims = ds[var].squeeze().dims\n", 312 | "\n", 313 | " # Find the bin dimension (non-time dimension)\n", 314 | " try:\n", 315 | " vname = [item for item in dims if 'Time' not in item][0]\n", 316 | " except IndexError:\n", 317 | " print(f\"{var} doesn't have a non-time dimension for binning\")\n", 318 | " return\n", 319 | " \n", 320 | " # Check if we have any valid data\n", 321 | " if ds[var].isnull().all():\n", 322 | " print(f\"{var} contains only NaN values - skipping plot\")\n", 323 | " return\n", 324 | " \n", 325 | " # Get plot limits with safety checks\n", 326 | " max_z = float(ds[var].to_numpy().max())\n", 327 | " if not np.isfinite(max_z) or max_z <= 0:\n", 328 | " # Try to find any non-NaN values to plot\n", 329 | " non_nan_data = ds[var].where(~np.isnan(ds[var]))\n", 330 | " if non_nan_data.count() > 0:\n", 331 | " max_z = float(non_nan_data.max().values)\n", 332 | " print(f\"Adjusted {var} to use non-NaN maximum: {max_z}\")\n", 333 | " else:\n", 334 | " print(f\"{var} has invalid maximum value: {max_z} - skipping plot\")\n", 335 | " return\n", 336 | " \n", 337 | " min_y = 1 if ds[var][vname][0] == 0 else float(ds[var][vname][0])\n", 338 | " if min_y <= 0:\n", 339 | " min_y = 1 # Ensure positive for log scale\n", 340 | " \n", 341 | " # Set up plot labels\n", 342 | " ylab = \"Bin [#]\" if min_y == 1 else \"Cell Size [um]\"\n", 343 | " \n", 344 | " # Create plot with safe options - replace NaN with 0\n", 345 | " plot_ds = ds[var].squeeze().fillna(0).assign_coords({vname: ds[vname]})\n", 346 | " \n", 347 | " heatmap = plot_ds.hvplot.quadmesh(\n", 348 | " cmap='gist_ncar', x='Time', width=x_width, height=y_height,\n", 349 | " ylabel=ylab, xlabel='Time [UTC]', \n", 350 | " title=f\"{var} Units: {histo_units[i]}\",\n", 351 | " y=vname, clim=(0, max_z), ylim=(min_y, None), logy=True,\n", 352 | " )\n", 353 | " \n", 354 | " # Render and show the plot\n", 355 | " bokeh_plot = hv.render(heatmap)\n", 356 | " show(bokeh_plot)\n", 357 | " \n", 358 | " except IndexError:\n", 359 | " print(f\"{var} does not have vector dimension\")\n", 360 | " except KeyError:\n", 361 | " print(f\"{var} not in file\")\n", 362 | " except Exception as e:\n", 363 | " print(f\"Error plotting {var}: {e}\")" 364 | ] 365 | }, 366 | { 367 | "cell_type": "code", 368 | "execution_count": null, 369 | "metadata": { 370 | "execution": { 371 | "iopub.execute_input": "2024-03-14T19:04:39.496269Z", 372 | "iopub.status.busy": "2024-03-14T19:04:39.496185Z", 373 | "iopub.status.idle": "2024-03-14T19:05:38.253962Z", 374 | "shell.execute_reply": "2024-03-14T19:05:38.253593Z" 375 | } 376 | }, 377 | "outputs": [], 378 | "source": [ 379 | "for i, var in enumerate(histovar_list):\n", 380 | " plot_histogram(ds,var,i)" 381 | ] 382 | } 383 | ], 384 | "metadata": { 385 | "kernelspec": { 386 | "display_name": "qatools", 387 | "language": "python", 388 | "name": "python3" 389 | }, 390 | "language_info": { 391 | "codemirror_mode": { 392 | "name": "ipython", 393 | "version": 3 394 | }, 395 | "file_extension": ".py", 396 | "mimetype": "text/x-python", 397 | "name": "python", 398 | "nbconvert_exporter": "python", 399 | "pygments_lexer": "ipython3", 400 | "version": "3.11.12" 401 | } 402 | }, 403 | "nbformat": 4, 404 | "nbformat_minor": 2 405 | } 406 | -------------------------------------------------------------------------------- /QAtools_notebook.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "4e873205-2dfc-4ee6-b292-9cd72bd3a748", 6 | "metadata": {}, 7 | "source": [ 8 | "# QAtools Notebook\n", 9 | "[![DOI:<10.26023>](http://img.shields.io/badge/DOI-10.26023-green.svg)](https://doi.org/10.26023/a0e3-4n78)\n", 10 | "\n", 11 | "Quick-look statistics and plots from field-phase NCAR/RAF flight netCDF datasets." 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "id": "16fb738b-eb19-40b6-aea3-3e9fe2bd479a", 17 | "metadata": { 18 | "jp-MarkdownHeadingCollapsed": true 19 | }, 20 | "source": [ 21 | "## Table of Contents\n", 22 | "* [References](#references)\n", 23 | "* [Project and Flight Data information](#project_and_flight_data_information)\n", 24 | "* [Flight Track](#flight_track)\n", 25 | "* [Altitude, Heading, and GPS Quality](#alt_heading_gps)\n", 26 | "* [Temperature](#temperature)\n", 27 | " * [Air Temperature](#air_temperature)\n", 28 | " * [Dew Point Temperature](#dew_point_temperature)\n", 29 | "* [Pressure](#pressure)\n", 30 | " * [Cavity Pressure](#cavity_pressure)\n", 31 | " * [Static Pressure](#static_pressure)\n", 32 | " * [Dynamic Pressure](#dynamic_pressure)\n", 33 | "* [Airspeed](#airspeed)\n", 34 | "* [Wind](#wind)\n", 35 | " * [Wind Direction](#wind_direction)\n", 36 | " * [Wind Speed](#wind_speed)\n", 37 | "* [Schuler Oscillation](#schuler_oscillation)\n", 38 | "* [Radome Pressures and Angles](#radome)\n", 39 | "* [Pitch Roll Heading](#pitch_roll_heading)\n", 40 | " * [Pitch](#pitch)\n", 41 | " * [Roll](#roll)\n", 42 | " * [Heading](#heading)\n", 43 | "* [ACINS VSPD Altitude](#acins_vspd_altitude)\n", 44 | " * [ACINS](#acins)\n", 45 | " * [VSPD](#vspd)\n", 46 | " * [Altitude](#altitude)\n", 47 | "* [Condensation Nucleus Concentrations](#concentrations)\n", 48 | "* [Liquid Water](#liquid_water)\n", 49 | "* [Skew-T Plot](#skewt)\n", 50 | "* [Potential Temperature](#potential_temperature)\n", 51 | "* [Radiation](#Radiation)\n", 52 | "* [Chemistry](#chemistry)\n", 53 | "* [SDI](#SDI)\n", 54 | "* [Aerosol and Cloud Probe Histogram Data](#histogram) \n", 55 | " \n", 56 | " " 57 | ] 58 | }, 59 | { 60 | "cell_type": "markdown", 61 | "id": "ad804ce4-64e0-40b3-ac70-2c87b90a10d6", 62 | "metadata": {}, 63 | "source": [ 64 | "## Change Log\n", 65 | "\n", 66 | "Version 0.1 7/18/2023:\n", 67 | "Initial Release \n", 68 | "GitHub Repo: https://github.com/NCAR/aircraft_QAtools_notebook" 69 | ] 70 | }, 71 | { 72 | "cell_type": "markdown", 73 | "id": "71a087b1-8b60-4008-abfb-3ff62fa32b64", 74 | "metadata": {}, 75 | "source": [ 76 | "## References " 77 | ] 78 | }, 79 | { 80 | "cell_type": "markdown", 81 | "id": "9d769e2a-3082-4f42-8c2d-2ff3ebc6f13e", 82 | "metadata": {}, 83 | "source": [ 84 | "README: https://github.com/NCAR/aircraft_QAtools_notebook/blob/main/README.md\n", 85 | "\n", 86 | "NCAR/RAF netCDF Format: https://archive.eol.ucar.edu/raf/software/netCDF.html\n", 87 | "\n", 88 | "NCAR/RAF Processing Algorithms: https://ncar.github.io/aircraft_ProcessingAlgorithms/\n", 89 | "\n", 90 | "RSIG Project QAQC Google Sheet: https://drive.google.com/drive/folders/1HB5dQ867T62jWOrqCDD3BV1B6Xiu2w7Q " 91 | ] 92 | }, 93 | { 94 | "cell_type": "markdown", 95 | "id": "2ac765dc-36cf-4b3d-8f80-b90ad1741ae4", 96 | "metadata": {}, 97 | "source": [ 98 | "## Project and Flight Data Information " 99 | ] 100 | }, 101 | { 102 | "cell_type": "code", 103 | "execution_count": null, 104 | "id": "81fb71ea", 105 | "metadata": { 106 | "execution": { 107 | "iopub.execute_input": "2025-08-12T19:47:42.592433Z", 108 | "iopub.status.busy": "2025-08-12T19:47:42.592152Z", 109 | "iopub.status.idle": "2025-08-12T19:47:42.601015Z", 110 | "shell.execute_reply": "2025-08-12T19:47:42.600596Z" 111 | } 112 | }, 113 | "outputs": [], 114 | "source": [ 115 | "%%html\n", 116 | "" 162 | ] 163 | }, 164 | { 165 | "cell_type": "code", 166 | "execution_count": null, 167 | "id": "018bf93b-4f09-4386-b3bf-1576fee3370f", 168 | "metadata": { 169 | "execution": { 170 | "iopub.execute_input": "2025-08-12T19:47:42.602503Z", 171 | "iopub.status.busy": "2025-08-12T19:47:42.602396Z", 172 | "iopub.status.idle": "2025-08-12T19:47:43.276265Z", 173 | "shell.execute_reply": "2025-08-12T19:47:43.275838Z" 174 | } 175 | }, 176 | "outputs": [], 177 | "source": [ 178 | "import os\n", 179 | "try:\n", 180 | " os.system('source ~/.bash_profile')\n", 181 | " CL = os.environ.get(\"QA_CL\")\n", 182 | "except:\n", 183 | " pass" 184 | ] 185 | }, 186 | { 187 | "cell_type": "markdown", 188 | "id": "83fbec48-d531-44ef-b1a3-d9d5fb83fccd", 189 | "metadata": {}, 190 | "source": [ 191 | "If the cells in this notebook are executed from the script `auto_export.py` then the arguments used in the execution of that script will be recognized by the notebook.\n", 192 | "\n", 193 | "Otherwise, change project and flight below to run interactively:" 194 | ] 195 | }, 196 | { 197 | "cell_type": "code", 198 | "execution_count": null, 199 | "id": "63ddbb77-aef4-4e33-becd-a54867fe85ed", 200 | "metadata": { 201 | "execution": { 202 | "iopub.execute_input": "2025-08-12T19:47:43.278062Z", 203 | "iopub.status.busy": "2025-08-12T19:47:43.277957Z", 204 | "iopub.status.idle": "2025-08-12T19:47:43.280414Z", 205 | "shell.execute_reply": "2025-08-12T19:47:43.280164Z" 206 | } 207 | }, 208 | "outputs": [], 209 | "source": [ 210 | "if CL == 'command_line_mode':\n", 211 | " try:\n", 212 | " project = os.environ.get('QA_PROJ')\n", 213 | " flight = os.environ.get('QA_FLIGHT')\n", 214 | " except:\n", 215 | " pass\n", 216 | "else:\n", 217 | " try:\n", 218 | " #######################################################################\n", 219 | " ####### change project and flight below if running interactively ######\n", 220 | " #######################################################################\n", 221 | " #project = 'wecan'\n", 222 | " #flight = 'rf18'\n", 223 | " project = 'GOTHAAM'\n", 224 | " flight = 'rf01'\n", 225 | " #######################################################################\n", 226 | " except:\n", 227 | " pass\n", 228 | "print('Project: ' + project)\n", 229 | "print('Flight: ' + flight)" 230 | ] 231 | }, 232 | { 233 | "cell_type": "markdown", 234 | "id": "d7914013-af82-4868-84e4-c071aee7e152", 235 | "metadata": {}, 236 | "source": [ 237 | "Set interactive_histogram to True or False depending on whether you would like the histogram / SD heatmap plots to be generated. The interactive histogram / SD heatmap plots take some time to generate. " 238 | ] 239 | }, 240 | { 241 | "cell_type": "code", 242 | "execution_count": null, 243 | "id": "b382c7fe-627b-446f-96fd-24223d1de365", 244 | "metadata": { 245 | "execution": { 246 | "iopub.execute_input": "2025-08-12T19:47:43.281644Z", 247 | "iopub.status.busy": "2025-08-12T19:47:43.281531Z", 248 | "iopub.status.idle": "2025-08-12T19:47:43.283475Z", 249 | "shell.execute_reply": "2025-08-12T19:47:43.283219Z" 250 | } 251 | }, 252 | "outputs": [], 253 | "source": [ 254 | "###############################################################################\n", 255 | "# set static_histogram to True or False depending on whether\n", 256 | "# you would like the heatmap plots generated. \n", 257 | "# For interactive histograms go to the interactive_hist.ipynb\n", 258 | "###############################################################################\n", 259 | "static_histogram = True\n", 260 | "###############################################################################\n", 261 | "# set chemistry instrumentation plotting to True or False depending on whether\n", 262 | "# you would like to see trace gas instrument plots. \n", 263 | "###############################################################################\n", 264 | "picarroGG_on = False\n", 265 | "ari_and_picarroGG_on = False\n", 266 | "ari_on = True\n", 267 | "ozone_on = True\n", 268 | "###############################################################################" 269 | ] 270 | }, 271 | { 272 | "cell_type": "code", 273 | "execution_count": null, 274 | "id": "1460050c-7d5b-442f-9c3c-81255b8c42b8", 275 | "metadata": { 276 | "execution": { 277 | "iopub.execute_input": "2025-08-12T19:47:43.284681Z", 278 | "iopub.status.busy": "2025-08-12T19:47:43.284587Z", 279 | "iopub.status.idle": "2025-08-12T19:47:46.308565Z", 280 | "shell.execute_reply": "2025-08-12T19:47:46.308277Z" 281 | }, 282 | "jupyter": { 283 | "source_hidden": true 284 | }, 285 | "tags": [] 286 | }, 287 | "outputs": [], 288 | "source": [ 289 | "import netCDF4\n", 290 | "import pandas as pd\n", 291 | "import numpy as np\n", 292 | "import seaborn as sns\n", 293 | "import matplotlib.pyplot as plt\n", 294 | "from matplotlib.colors import LogNorm, Normalize, ListedColormap\n", 295 | "from metpy.plots import SkewT\n", 296 | "from metpy.units import pandas_dataframe_to_unit_arrays, units\n", 297 | "from datetime import datetime, timedelta\n", 298 | "from IPython.display import display\n", 299 | "from bokeh.io import push_notebook, show, output_notebook\n", 300 | "from bokeh.layouts import row\n", 301 | "from bokeh.plotting import figure, show\n", 302 | "from bokeh.models import Title, CustomJS, Select, TextInput, Button, LinearAxis, Range1d, FuncTickFormatter\n", 303 | "from bokeh.models.formatters import DatetimeTickFormatter\n", 304 | "from bokeh.models.tickers import DatetimeTicker\n", 305 | "from bokeh.palettes import Category10\n", 306 | "import warnings\n", 307 | "import itertools \n", 308 | "import holoviews as hv \n", 309 | "from holoviews import dim, opts\n", 310 | "import hvplot.pandas\n", 311 | "hv.extension('bokeh', 'matplotlib')\n", 312 | "warnings.filterwarnings('ignore')\n", 313 | "output_notebook()\n", 314 | "%matplotlib inline" 315 | ] 316 | }, 317 | { 318 | "cell_type": "code", 319 | "execution_count": null, 320 | "id": "d984f479-f676-4c52-93d1-8075f7fb5445", 321 | "metadata": { 322 | "execution": { 323 | "iopub.execute_input": "2025-08-12T19:47:46.310006Z", 324 | "iopub.status.busy": "2025-08-12T19:47:46.309896Z", 325 | "iopub.status.idle": "2025-08-12T19:47:46.313957Z", 326 | "shell.execute_reply": "2025-08-12T19:47:46.313738Z" 327 | } 328 | }, 329 | "outputs": [], 330 | "source": [ 331 | "# function definition for creating generic timeseries plot\n", 332 | "def format_ticks(plot):\n", 333 | " plot.xaxis.formatter=DatetimeTickFormatter(days =['%H:%M'], hours=\"%H:%M\", minutes=\"%H:%M\",hourmin = ['%H:%M']) \n", 334 | " plot.xaxis.ticker = DatetimeTicker(desired_num_ticks=num_ticks)\n", 335 | "def format_legend_lines(plot, line_width=2):\n", 336 | " \"\"\"Make legend lines wider for better visibility\"\"\"\n", 337 | " for legend_item in plot.legend.items:\n", 338 | " for glyph_renderer in legend_item.renderers:\n", 339 | " if hasattr(glyph_renderer.glyph, 'line_width'):\n", 340 | " glyph_renderer.glyph.line_width = line_width\n", 341 | "def timeseries_plot(title, y_label, dataframe):\n", 342 | " colors = itertools.cycle(Category10[8])\n", 343 | " plot = figure(sizing_mode=\"stretch_width\", height=400, title=title, ) \n", 344 | " plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 345 | " plot.add_layout(Title(text=y_label, align=\"center\"), \"left\")\n", 346 | " \n", 347 | " # loop over variables in the pandas dataframe to add to plot\n", 348 | " for i in asc[dataframe].columns:\n", 349 | " try:\n", 350 | " plot.line(dtime, asc[dataframe][i], color=next(colors), legend_label=i)\n", 351 | " format_ticks(plot)\n", 352 | " except Exception as e:\n", 353 | " print(e)\n", 354 | " try:\n", 355 | " plot.legend.location = \"top_left\"\n", 356 | " except Exception as e:\n", 357 | " print(e)\n", 358 | " # Call format_legend_lines to make legend lines wider\n", 359 | " try:\n", 360 | " format_legend_lines(plot, line_width=2) # You can adjust line_width as needed\n", 361 | " except Exception as e:\n", 362 | " print(e)\n", 363 | " try:\n", 364 | " show(plot)\n", 365 | " except Exception as e:\n", 366 | " print(e)" 367 | ] 368 | }, 369 | { 370 | "cell_type": "code", 371 | "execution_count": null, 372 | "id": "b9414b2c", 373 | "metadata": { 374 | "execution": { 375 | "iopub.execute_input": "2025-08-12T19:47:46.315220Z", 376 | "iopub.status.busy": "2025-08-12T19:47:46.315127Z", 377 | "iopub.status.idle": "2025-08-12T19:47:46.316826Z", 378 | "shell.execute_reply": "2025-08-12T19:47:46.316601Z" 379 | } 380 | }, 381 | "outputs": [], 382 | "source": [ 383 | "#Set the DATA_DIR os environment variable to the current working directory if it is not already set\n", 384 | "if 'DATA_DIR' not in os.environ:\n", 385 | " os.environ['DATA_DIR'] = os.getcwd()" 386 | ] 387 | }, 388 | { 389 | "cell_type": "code", 390 | "execution_count": null, 391 | "id": "0e88a48c-6107-4b61-87f5-4667d8c2c083", 392 | "metadata": { 393 | "execution": { 394 | "iopub.execute_input": "2025-08-12T19:47:46.317985Z", 395 | "iopub.status.busy": "2025-08-12T19:47:46.317867Z", 396 | "iopub.status.idle": "2025-08-12T19:47:46.483883Z", 397 | "shell.execute_reply": "2025-08-12T19:47:46.483596Z" 398 | } 399 | }, 400 | "outputs": [], 401 | "source": [ 402 | "# use the user provided project and flight information to build the path and store the data file as an object\n", 403 | "########################################################################\n", 404 | "# update filepath based on where the netcdf files are located\n", 405 | "########################################################################\n", 406 | "yr = '2024'\n", 407 | "filepath = os.environ.get('DATA_DIR')+'/'+project +'/'\n", 408 | "input_file = project + flight + '.nc'\n", 409 | "nc = netCDF4.Dataset(filepath + input_file, mode='r')\n", 410 | "\n", 411 | "# try to get global attributes from the netcdf file if they are present\n", 412 | "# determine preliminary or final status\n", 413 | "try:\n", 414 | " proc_status = nc.getncattr('WARNING')\n", 415 | " print(proc_status)\n", 416 | "except:\n", 417 | " proc_status = 'final'\n", 418 | "\n", 419 | "# determine the NIDAS version\n", 420 | "try:\n", 421 | " nidas = nc.getncattr('NIDASrevision')\n", 422 | " print('NIDAS version: ' + nidas)\n", 423 | "except Exception as e:\n", 424 | " print(e)\n", 425 | "\n", 426 | "# determine the NIMBUS version\n", 427 | "try:\n", 428 | " nimbus = nc.getncattr('RepositoryRevision')\n", 429 | " print('NIMBUS version: ' + nimbus)\n", 430 | "except Exception as e:\n", 431 | " print(e)\n", 432 | "\n", 433 | "# determine the processing date and time\n", 434 | "try:\n", 435 | " proc_date = nc.getncattr('date_created')\n", 436 | " print('Processing Date & Time: ' + proc_date)\n", 437 | "except Exception as e:\n", 438 | " print(e)\n", 439 | "\n", 440 | "# determine the platform\n", 441 | "try:\n", 442 | " platform = nc.getncattr('platform')\n", 443 | " print('Platform N-number: ' + platform)\n", 444 | "except Exception as e:\n", 445 | " print(e)" 446 | ] 447 | }, 448 | { 449 | "cell_type": "code", 450 | "execution_count": null, 451 | "id": "bb9b13dd-e70c-46d0-b177-99f0c5302a9f", 452 | "metadata": { 453 | "execution": { 454 | "iopub.execute_input": "2025-08-12T19:47:46.485339Z", 455 | "iopub.status.busy": "2025-08-12T19:47:46.485255Z", 456 | "iopub.status.idle": "2025-08-12T19:47:46.490180Z", 457 | "shell.execute_reply": "2025-08-12T19:47:46.489906Z" 458 | }, 459 | "tags": [] 460 | }, 461 | "outputs": [], 462 | "source": [ 463 | "# sometimes the netcdf4 api produces an issue with big-endian buffer on little-endian compiler\n", 464 | "byte_swap = False\n", 465 | "\n", 466 | "# create empty placeholders for asc, histo_asc and units\n", 467 | "asc = {}\n", 468 | "histo_asc = {}\n", 469 | "units = {}\n", 470 | "cellsize = {}\n", 471 | "\n", 472 | "# use the netcdf4 api to get the netcdf data into a dataframe\n", 473 | "try:\n", 474 | " \n", 475 | " # loop over keys in netCDF file and organize\n", 476 | " for i in nc.variables.keys():\n", 477 | " dims = str(nc.variables[i].dimensions)\n", 478 | " \n", 479 | " # this if section retrieves data that has time dimension only\n", 480 | " if dims == \"('Time',)\":\n", 481 | " output = nc[i][:]\n", 482 | " # sometimes the netcdf4 api produces an issue with big-endian buffer on little-endian compiler\n", 483 | " if byte_swap == True:\n", 484 | " output = output.byteswap().newbyteorder()\n", 485 | " else:\n", 486 | " pass\n", 487 | " asc[i] = pd.DataFrame(output)\n", 488 | " units_var = nc.variables[i].getncattr('units')\n", 489 | " units[i] = pd.Series(units_var)\n", 490 | " asc[i].columns = pd.MultiIndex.from_tuples(zip(asc[i].columns, units[i]))\n", 491 | " \n", 492 | " # this section retrieves data that has a size distribution dimension in addition to time\n", 493 | " elif \"sps1\" in dims:\n", 494 | " histo_output = nc.variables[i][:, 0, :]\n", 495 | " # sometimes the netcdf4 api produces an issue with big-endian buffer on little-endian compiler\n", 496 | " if byte_swap == True:\n", 497 | " histo_output = histo_output.byteswap().newbyteorder()\n", 498 | " else:\n", 499 | " pass\n", 500 | " histo_asc[i] = pd.DataFrame(histo_output)\n", 501 | " \n", 502 | " # this try / except block accommodates size distribution data that has an attribute CellSizes\n", 503 | " try:\n", 504 | " cellsize = nc.variables[i].getncattr('CellSizes')\n", 505 | " histo_asc[i].columns = pd.MultiIndex.from_tuples(zip(histo_asc[i].columns, cellsize))\n", 506 | " except Exception as e:\n", 507 | " histo_asc[i].columns = pd.MultiIndex.from_tuples(zip(histo_asc[i].columns, histo_asc[i].columns))\n", 508 | " else:\n", 509 | " pass \n", 510 | "\n", 511 | " # concatenate the dataframe\n", 512 | " asc = pd.concat(asc, axis=1, ignore_index=False)\n", 513 | " # clean up the dataframe by dropping some of the multi-index rows\n", 514 | " asc.columns = asc.columns.droplevel(1)\n", 515 | " asc.columns = asc.columns.droplevel(1)\n", 516 | " # scrub data based on ground speed change value from 60 to something else if you want more or less data\n", 517 | " asc.drop(asc[asc['GGSPD'] < 60].index, inplace = True)\n", 518 | " \n", 519 | " # extract the Time variable and organize as a pandas series\n", 520 | " dtime = asc.Time.squeeze()\n", 521 | " new_dtime = []\n", 522 | " for index in range(len(dtime)):\n", 523 | " new_dtime.append(timedelta(seconds=int(dtime.iloc[index])))\n", 524 | " dtime = pd.Series(new_dtime)\n", 525 | " num_ticks = len(dtime.dt.components.hours.unique())\n", 526 | " \n", 527 | " \n", 528 | " # concatenate the histogram data\n", 529 | " histo_asc = pd.concat(histo_asc, axis=1, ignore_index=False)\n", 530 | " histo_asc.columns = histo_asc.columns.droplevel(1)\n", 531 | " colors = itertools.cycle(Category10[6])\n", 532 | " \n", 533 | " \n", 534 | "except Exception as e:\n", 535 | " print(e)" 536 | ] 537 | }, 538 | { 539 | "cell_type": "code", 540 | "execution_count": null, 541 | "id": "789c7fac", 542 | "metadata": { 543 | "execution": { 544 | "iopub.execute_input": "2025-08-12T19:47:46.491399Z", 545 | "iopub.status.busy": "2025-08-12T19:47:46.491321Z", 546 | "iopub.status.idle": "2025-08-12T19:47:46.493711Z", 547 | "shell.execute_reply": "2025-08-12T19:47:46.493433Z" 548 | } 549 | }, 550 | "outputs": [], 551 | "source": [ 552 | "from bokeh.plotting import curdoc\n", 553 | "from bokeh.models import Plot\n", 554 | "from bokeh.themes import Theme\n", 555 | "import yaml\n", 556 | "\n", 557 | "# Define theme\n", 558 | "theme_yaml = \"\"\"\n", 559 | "attrs:\n", 560 | " Plot:\n", 561 | " min_border_right: 70\n", 562 | " outline_line_color: black\n", 563 | " sizing_mode: stretch_width\n", 564 | "\"\"\"\n", 565 | "\n", 566 | "theme = Theme(json=yaml.safe_load(theme_yaml))\n", 567 | "curdoc().theme = theme\n" 568 | ] 569 | }, 570 | { 571 | "cell_type": "markdown", 572 | "id": "f6cf051d-f147-48c5-a5c2-be7f3c3a0b12", 573 | "metadata": {}, 574 | "source": [ 575 | "## Flight Track " 576 | ] 577 | }, 578 | { 579 | "cell_type": "code", 580 | "execution_count": null, 581 | "id": "3721fe1c-35b6-4c42-b1bf-d6856b4681b5", 582 | "metadata": { 583 | "execution": { 584 | "iopub.execute_input": "2025-08-12T19:47:46.495003Z", 585 | "iopub.status.busy": "2025-08-12T19:47:46.494925Z", 586 | "iopub.status.idle": "2025-08-12T19:47:46.559374Z", 587 | "shell.execute_reply": "2025-08-12T19:47:46.559092Z" 588 | }, 589 | "scrolled": true 590 | }, 591 | "outputs": [], 592 | "source": [ 593 | "#Forse all resources to be inline\n", 594 | "os.environ['BOKEH_RESOURCES'] = 'inline' \n", 595 | "from bokeh.io import output_notebook\n", 596 | "output_notebook()\n", 597 | "# get latitude and longitude from asc dataframe\n", 598 | "latitude = asc[\"GGLAT\"]\n", 599 | "longitude = asc[\"GGLON\"]\n", 600 | "\n", 601 | "# update to mercator projection\n", 602 | "k = 6378137\n", 603 | "longitude = longitude * (k * np.pi/180.0)\n", 604 | "latitude = np.log(np.tan((90 + latitude) * np.pi/360.0)) * k\n", 605 | "\n", 606 | "# create the plot layout and add axis labels\n", 607 | "try:\n", 608 | " plot = figure(width=800, height=800, title=project + ' ' + flight + \" Flight Track\", x_axis_type=\"mercator\", y_axis_type=\"mercator\") \n", 609 | " plot.add_layout(Title(text=\"Longitude [Degrees]\", align=\"center\"), \"below\")\n", 610 | " plot.add_layout(Title(text=\"Latitude [Degrees]\", align=\"center\"), \"left\")\n", 611 | "except Exception as e:\n", 612 | " print(e)\n", 613 | "\n", 614 | "# add the flight track in yellow and add Esri World Imagery as the basemap\n", 615 | "try:\n", 616 | " plot.line(longitude,latitude, color=\"yellow\")\n", 617 | " plot.add_tile(\"Esri World Imagery\", retina=True)\n", 618 | " show(plot)\n", 619 | "except Exception as e:\n", 620 | " print(e)" 621 | ] 622 | }, 623 | { 624 | "cell_type": "markdown", 625 | "id": "03b8d97f-5159-4635-8713-e55be2730c43", 626 | "metadata": {}, 627 | "source": [ 628 | "## Altitude, Heading, and GPS Quality " 629 | ] 630 | }, 631 | { 632 | "cell_type": "code", 633 | "execution_count": null, 634 | "id": "b56ac8e0-ce44-4f4c-a428-d4bae74e63db", 635 | "metadata": { 636 | "execution": { 637 | "iopub.execute_input": "2025-08-12T19:47:46.571464Z", 638 | "iopub.status.busy": "2025-08-12T19:47:46.571358Z", 639 | "iopub.status.idle": "2025-08-12T19:47:46.585380Z", 640 | "shell.execute_reply": "2025-08-12T19:47:46.585084Z" 641 | } 642 | }, 643 | "outputs": [], 644 | "source": [ 645 | "# create a subset for altitude in order to generate stats\n", 646 | "alt_heading = []\n", 647 | "for i in asc.columns:\n", 648 | " if i.startswith('GGALTF') | i.startswith('PALTF') | i.startswith('PA2'):\n", 649 | " alt_heading.append(i)\n", 650 | "\n", 651 | "# generate the stats table\n", 652 | "if len(alt_heading) > 0:\n", 653 | " display(asc[alt_heading].describe())" 654 | ] 655 | }, 656 | { 657 | "cell_type": "code", 658 | "execution_count": null, 659 | "id": "5d2e8662-d2e4-411a-9f18-cce10ce0389f", 660 | "metadata": { 661 | "execution": { 662 | "iopub.execute_input": "2025-08-12T19:47:46.586709Z", 663 | "iopub.status.busy": "2025-08-12T19:47:46.586618Z", 664 | "iopub.status.idle": "2025-08-12T19:47:46.620535Z", 665 | "shell.execute_reply": "2025-08-12T19:47:46.620244Z" 666 | }, 667 | "tags": [] 668 | }, 669 | "outputs": [], 670 | "source": [ 671 | "# generate the altitude, heading and gps quality plots\n", 672 | "# altitude plot\n", 673 | "timeseries_plot(\"Altitude\", \"Altitude [Feet]\", alt_heading)\n", 674 | "colors = itertools.cycle(Category10[6])\n", 675 | "plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"Heading\", ) \n", 676 | "plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 677 | "plot.add_layout(Title(text=\"True Heading [Degrees]\", align=\"center\"), \"left\")\n", 678 | "try:\n", 679 | " plot.line(dtime, asc['THDG'], color=next(colors))\n", 680 | " format_ticks(plot)\n", 681 | " show(plot)\n", 682 | "except:\n", 683 | " pass\n", 684 | "\n", 685 | "colors = itertools.cycle(Category10[6])\n", 686 | "plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"GPS Quality: 5 = TerraStar corrected, 2 = TerraStar converging, 9 = WAAS, 1 = Standard GPS, 0 = No Fix \\nGPS Status: 1=OK(A), 0=warning(V)\") \n", 687 | "plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 688 | "plot.add_layout(Title(text=\"GPS Quality []\", align=\"center\"), \"left\")\n", 689 | "try:\n", 690 | " plot.line(dtime, asc['GGQUAL'], color=next(colors), legend_label=\"GGQUAL\")\n", 691 | "except:\n", 692 | " pass\n", 693 | "try:\n", 694 | " plot.line(dtime, asc['GGSTATUS'], color=next(colors), legend_label=\"GGSTATUS\")\n", 695 | "except:\n", 696 | " pass\n", 697 | "try:\n", 698 | " plot.line(dtime, asc['GGNSAT'], color=next(colors), legend_label=\"GGNSAT\")\n", 699 | " format_ticks(plot)\n", 700 | " plot.ylim(bottom=0)\n", 701 | "except:\n", 702 | " pass\n", 703 | "try:\n", 704 | " show(plot)\n", 705 | "except:\n", 706 | " pass" 707 | ] 708 | }, 709 | { 710 | "cell_type": "markdown", 711 | "id": "570ecfbe-dcf4-4aa0-909c-2d3d09092bb7", 712 | "metadata": {}, 713 | "source": [ 714 | "## Temperature " 715 | ] 716 | }, 717 | { 718 | "cell_type": "markdown", 719 | "id": "e32048c1-7b5b-4cb9-b9cb-6fa53e5aa1c5", 720 | "metadata": {}, 721 | "source": [ 722 | "### Air Temperature " 723 | ] 724 | }, 725 | { 726 | "cell_type": "code", 727 | "execution_count": null, 728 | "id": "bf66babf-9a33-4cc9-be9e-30277d13d8d7", 729 | "metadata": { 730 | "execution": { 731 | "iopub.execute_input": "2025-08-12T19:47:46.622124Z", 732 | "iopub.status.busy": "2025-08-12T19:47:46.622045Z", 733 | "iopub.status.idle": "2025-08-12T19:47:46.635542Z", 734 | "shell.execute_reply": "2025-08-12T19:47:46.635271Z" 735 | }, 736 | "tags": [] 737 | }, 738 | "outputs": [], 739 | "source": [ 740 | "# create a subset for air temperature in order to generate stats\n", 741 | "temp = []\n", 742 | "for i in asc.columns:\n", 743 | " if i.startswith('ATX') | i.startswith('ATH') | i.startswith('ATF') | i.startswith('AT_'):\n", 744 | " temp.append(i)\n", 745 | "# remove data that is not useful\n", 746 | "try:\n", 747 | " temp.remove('AT_VXL')\n", 748 | "except:\n", 749 | " pass\n", 750 | " \n", 751 | "# generate the stats table\n", 752 | "if len(temp) > 0:\n", 753 | " display(asc[temp].describe())" 754 | ] 755 | }, 756 | { 757 | "cell_type": "code", 758 | "execution_count": null, 759 | "id": "e8779969-6545-4d19-b118-3de9bbcf8891", 760 | "metadata": { 761 | "execution": { 762 | "iopub.execute_input": "2025-08-12T19:47:46.636819Z", 763 | "iopub.status.busy": "2025-08-12T19:47:46.636739Z", 764 | "iopub.status.idle": "2025-08-12T19:47:46.672846Z", 765 | "shell.execute_reply": "2025-08-12T19:47:46.672568Z" 766 | }, 767 | "tags": [] 768 | }, 769 | "outputs": [], 770 | "source": [ 771 | "timeseries_plot(\"Temperature\", \"Temperature [\\N{DEGREE CELSIUS}]\", temp)\n", 772 | " \n", 773 | "colors = itertools.cycle(Category10[6])\n", 774 | "plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"ATX - Temperature\") \n", 775 | "plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 776 | "plot.add_layout(Title(text=\"Temperature Difference [\\N{DEGREE CELSIUS}]\", align=\"center\"), \"left\")\n", 777 | "for i in asc[temp].columns:\n", 778 | " try:\n", 779 | " plot.line(dtime, asc[temp][i] - asc['ATX'], color=next(colors), legend_label=i)\n", 780 | " plot.line(dtime, 0.25, color = 'black', line_dash=\"4 4\", line_width=2)\n", 781 | " plot.line(dtime, -0.25, color = 'black', line_dash=\"4 4\", line_width=2)\n", 782 | "\n", 783 | " format_ticks(plot)\n", 784 | " except Exception as e:\n", 785 | " print(e)\n", 786 | "try:\n", 787 | " plot.legend.location = \"top_left\"\n", 788 | " show(plot)\n", 789 | "except:\n", 790 | " pass\n", 791 | " \n", 792 | "# loop over temperatures\n", 793 | "for i in asc[temp].columns:\n", 794 | " if i in ('ATH1','ATX'): ##Remove plotting variables against itself\n", 795 | " continue \n", 796 | " try:\n", 797 | " # create plot labels\n", 798 | " plot = figure(width=700, height=500, title=asc[temp][i].name) \n", 799 | " plot.add_layout(Title(text=\"ATH1 [\\N{DEGREE CELSIUS}]\", align=\"center\"), \"below\")\n", 800 | " plot.add_layout(Title(text=asc[temp][i].name + \" [\\N{DEGREE CELSIUS}]\", align=\"center\"), \"left\")\n", 801 | " except:\n", 802 | " pass\n", 803 | " try:\n", 804 | " # create scatter plots\n", 805 | " plot.scatter(asc[temp]['ATX'], asc[temp]['ATX'], color = 'black', line_dash = 'dotted', legend_label='1:1')\n", 806 | " plot.scatter(asc[temp]['ATH1'], asc[temp][i], color='orange', legend_label=i)\n", 807 | " except:\n", 808 | " pass\n", 809 | " try:\n", 810 | " # create additional y axis range\n", 811 | " plot.extra_y_ranges['delta'] = Range1d(-4, 4)\n", 812 | " ax2 = LinearAxis(y_range_name=\"delta\", axis_label=\"Delta Temperature [C]\")\n", 813 | " ax2.axis_label_text_color =\"red\"\n", 814 | " plot.add_layout(ax2, 'right')\n", 815 | " except:\n", 816 | " pass\n", 817 | " try:\n", 818 | " # create line plot of temperature differences using the additional y axis range\n", 819 | " plot.line(asc[temp]['ATH1'], asc[temp][i]-asc[temp]['ATH1'], color='red', legend_label=i, y_range_name=\"delta\")\n", 820 | " plot.line(asc[temp]['ATH1'], 1, color = 'gray', y_range_name=\"delta\")\n", 821 | " plot.line(asc[temp]['ATH1'], - 1, color = 'gray', y_range_name=\"delta\")\n", 822 | " except:\n", 823 | " pass\n", 824 | " try:\n", 825 | " plot.legend.location = \"top_left\"\n", 826 | " except:\n", 827 | " pass\n", 828 | " show(plot)" 829 | ] 830 | }, 831 | { 832 | "cell_type": "markdown", 833 | "id": "d8cceeff-ba5a-40ec-b933-565c19ef47f3", 834 | "metadata": {}, 835 | "source": [ 836 | "### Dew Point Temperature " 837 | ] 838 | }, 839 | { 840 | "cell_type": "code", 841 | "execution_count": null, 842 | "id": "31cc9c19-4308-4f13-8a8d-a1bbd4ce4b25", 843 | "metadata": { 844 | "execution": { 845 | "iopub.execute_input": "2025-08-12T19:47:46.674236Z", 846 | "iopub.status.busy": "2025-08-12T19:47:46.674145Z", 847 | "iopub.status.idle": "2025-08-12T19:47:46.686584Z", 848 | "shell.execute_reply": "2025-08-12T19:47:46.686314Z" 849 | }, 850 | "tags": [] 851 | }, 852 | "outputs": [], 853 | "source": [ 854 | "# create a subset for dew point temperature in order to generate stats\n", 855 | "dpt = []\n", 856 | "for i in asc.columns:\n", 857 | " if i.startswith('DP_') | i.startswith('ATX'):\n", 858 | " dpt.append(i)\n", 859 | "if len(dpt) > 0:\n", 860 | " display(asc[dpt].describe())" 861 | ] 862 | }, 863 | { 864 | "cell_type": "code", 865 | "execution_count": null, 866 | "id": "e08f34b7-1ed6-4d5f-9d4d-c4d0cddbfa64", 867 | "metadata": { 868 | "execution": { 869 | "iopub.execute_input": "2025-08-12T19:47:46.687907Z", 870 | "iopub.status.busy": "2025-08-12T19:47:46.687801Z", 871 | "iopub.status.idle": "2025-08-12T19:47:46.720391Z", 872 | "shell.execute_reply": "2025-08-12T19:47:46.720133Z" 873 | }, 874 | "tags": [] 875 | }, 876 | "outputs": [], 877 | "source": [ 878 | "timeseries_plot(\"Dew Point Temperatures\", \"Dew Point [C]\", dpt)\n", 879 | "\n", 880 | "colors = itertools.cycle(Category10[6])\n", 881 | "plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"Diff Dew Point Temperatures\") \n", 882 | "plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 883 | "if ('DP_DPL') in dpt:\n", 884 | " plot.add_layout(Title(text=\"Diff DPL - DP Dew Point [C]\", align=\"center\"), \"left\")\n", 885 | " for i in asc[dpt].columns:\n", 886 | " try:\n", 887 | " plot.line(dtime, asc['DP_DPL'] - asc[dpt][i], color=next(colors), legend_label=i)\n", 888 | " format_ticks(plot)\n", 889 | " except:\n", 890 | " pass\n", 891 | " try:\n", 892 | " format_legend_lines(plot)\n", 893 | " except:\n", 894 | " pass\n", 895 | " try:\n", 896 | " plot.line(dtime, 1, color = 'black', line_dash=\"4 4\", line_width=2)\n", 897 | " plot.line(dtime, -1, color = 'black', line_dash=\"4 4\", line_width=2)\n", 898 | " plot.legend.location = \"top_left\"\n", 899 | " plot.y_range=Range1d(-5, 5)\n", 900 | " show(plot)\n", 901 | " except:\n", 902 | " pass \n", 903 | "else:\n", 904 | " plot.add_layout(Title(text=\"Diff DPT - DP Dew Point [C]\", align=\"center\"), \"left\")\n", 905 | " for i in asc[dpt].columns:\n", 906 | " try:\n", 907 | " plot.line(dtime, asc['DP_DPT'] - asc[dpt][i], color=next(colors), legend_label=i)\n", 908 | " format_ticks(plot)\n", 909 | " except:\n", 910 | " pass\n", 911 | " try:\n", 912 | " plot.line(dtime, 1, color = 'black', line_dash=\"4 4\", line_width=2)\n", 913 | " plot.line(dtime, -1, color = 'black', line_dash=\"4 4\", line_width=2)\n", 914 | " plot.legend.location = \"top_left\"\n", 915 | " plot.y_range=Range1d(-5, 5)\n", 916 | " show(plot)\n", 917 | " except:\n", 918 | " pass\n", 919 | "try:\n", 920 | " format_legend_lines(plot)\n", 921 | "except:\n", 922 | " pass" 923 | ] 924 | }, 925 | { 926 | "cell_type": "code", 927 | "execution_count": null, 928 | "id": "8b5b249d-5b00-476a-9325-47f19606001b", 929 | "metadata": { 930 | "execution": { 931 | "iopub.execute_input": "2025-08-12T19:47:46.721786Z", 932 | "iopub.status.busy": "2025-08-12T19:47:46.721703Z", 933 | "iopub.status.idle": "2025-08-12T19:47:46.741176Z", 934 | "shell.execute_reply": "2025-08-12T19:47:46.740907Z" 935 | }, 936 | "tags": [] 937 | }, 938 | "outputs": [], 939 | "source": [ 940 | "# create plot labels\n", 941 | "if ('DP_DPL') in dpt:\n", 942 | " plot = figure(sizing_mode=\"stretch_width\", height=800) \n", 943 | " plot.add_layout(Title(text=\"DP_DPR [C]\", align=\"center\"), \"below\")\n", 944 | " plot.add_layout(Title(text=\"DP_DPL [C]\", align=\"center\"), \"left\")\n", 945 | " try:\n", 946 | " try:\n", 947 | " # create scatter plots\n", 948 | " plot.scatter(asc['DP_DPR'], asc['DP_DPL'], color = 'blue', legend_label='DP_DPL')\n", 949 | " except:\n", 950 | " pass \n", 951 | " try:\n", 952 | " # DP_DPR used to create a 1:1 line\n", 953 | " plot.line(asc['DP_DPR'], asc['DP_DPR'], color = 'black', legend_label='1:1')\n", 954 | " # +- 5C lines for quick visualization on bounds\n", 955 | " plot.line(asc['DP_DPR'], asc['DP_DPR']+5, color = 'orange', legend_label='+-5C')\n", 956 | " plot.line(asc['DP_DPR'], asc['DP_DPR']-5, color = 'orange')\n", 957 | " except:\n", 958 | " pass\n", 959 | " try:\n", 960 | " plot.scatter(asc['DP_DPR'], asc['DP_VXL'], color = 'green', legend_label='DP_VXL')\n", 961 | " except:\n", 962 | " pass\n", 963 | " except:\n", 964 | " pass\n", 965 | " try:\n", 966 | " plot.legend.location = \"top_left\"\n", 967 | " show(plot)\n", 968 | " except:\n", 969 | " pass\n", 970 | "else:\n", 971 | " plot = figure(sizing_mode=\"stretch_width\", height=800) \n", 972 | " plot.add_layout(Title(text=\"DP_DPT [C]\", align=\"center\"), \"below\")\n", 973 | " plot.add_layout(Title(text=\"DP_DPB [C]\", align=\"center\"), \"left\")\n", 974 | " try:\n", 975 | " try:\n", 976 | " # create scatter plots\n", 977 | " plot.scatter(asc['DP_DPT'], asc['DP_DPB'], color = 'blue', legend_label='DP_DPT')\n", 978 | " except:\n", 979 | " pass \n", 980 | " try:\n", 981 | " # DP_DPR used to create a 1:1 line\n", 982 | " plot.line(asc['DP_DPT'], asc['DP_DPT'], color = 'black', legend_label='1:1')\n", 983 | " # +- 5C lines for quick visualization on bounds\n", 984 | " plot.line(asc['DP_DPT'], asc['DP_DPT']+5, color = 'orange', legend_label='+-5C')\n", 985 | " plot.line(asc['DP_DPT'], asc['DP_DPT']-5, color = 'orange')\n", 986 | " except:\n", 987 | " pass\n", 988 | " try:\n", 989 | " plot.scatter(asc['DP_DPT'], asc['DP_VXL'], color = 'green', legend_label='DP_VXL')\n", 990 | " except:\n", 991 | " pass\n", 992 | " except:\n", 993 | " pass\n", 994 | " try:\n", 995 | " format_legend_lines(plot)\n", 996 | " except:\n", 997 | " pass\n", 998 | " try:\n", 999 | " plot.legend.location = \"top_left\"\n", 1000 | " show(plot)\n", 1001 | " except:\n", 1002 | " pass " 1003 | ] 1004 | }, 1005 | { 1006 | "cell_type": "markdown", 1007 | "id": "2522c58f-113c-458f-ba60-3aabba6f9b14", 1008 | "metadata": {}, 1009 | "source": [ 1010 | "## Pressure " 1011 | ] 1012 | }, 1013 | { 1014 | "cell_type": "markdown", 1015 | "id": "7189c33e-33b6-4935-a910-d2828d714360", 1016 | "metadata": {}, 1017 | "source": [ 1018 | "### Cavity Pressure " 1019 | ] 1020 | }, 1021 | { 1022 | "cell_type": "code", 1023 | "execution_count": null, 1024 | "id": "1bab60fb-7f92-454a-ac2b-34fb6001be3f", 1025 | "metadata": { 1026 | "execution": { 1027 | "iopub.execute_input": "2025-08-12T19:47:46.742577Z", 1028 | "iopub.status.busy": "2025-08-12T19:47:46.742480Z", 1029 | "iopub.status.idle": "2025-08-12T19:47:46.757600Z", 1030 | "shell.execute_reply": "2025-08-12T19:47:46.757290Z" 1031 | } 1032 | }, 1033 | "outputs": [], 1034 | "source": [ 1035 | "# create a subset for cavity pressure in order to generate stats and make sure WVISO variables are not included\n", 1036 | "cav_pressure = []\n", 1037 | "for i in asc.columns:\n", 1038 | " if (i.startswith('CAVP_') | i.startswith('CAVPF_') | i.startswith('PSXC')) and 'WVISO' not in i:\n", 1039 | " cav_pressure.append(i)\n", 1040 | "try:\n", 1041 | " cav_pressure.remove('CAVP_GNI')\n", 1042 | "except:\n", 1043 | " pass\n", 1044 | "# show the stats table\n", 1045 | "if len(cav_pressure) > 0:\n", 1046 | " display(asc[cav_pressure].describe())" 1047 | ] 1048 | }, 1049 | { 1050 | "cell_type": "code", 1051 | "execution_count": null, 1052 | "id": "a2fde3c9-7ba1-495e-beb8-adb2db670a1c", 1053 | "metadata": { 1054 | "execution": { 1055 | "iopub.execute_input": "2025-08-12T19:47:46.759072Z", 1056 | "iopub.status.busy": "2025-08-12T19:47:46.758984Z", 1057 | "iopub.status.idle": "2025-08-12T19:47:46.783884Z", 1058 | "shell.execute_reply": "2025-08-12T19:47:46.783610Z" 1059 | } 1060 | }, 1061 | "outputs": [], 1062 | "source": [ 1063 | "timeseries_plot(\"Cavity Pressure\", \"Pressure [hPa]\", cav_pressure)" 1064 | ] 1065 | }, 1066 | { 1067 | "cell_type": "markdown", 1068 | "id": "50fcfa3c-e9e4-4568-b05c-db483e32d480", 1069 | "metadata": {}, 1070 | "source": [ 1071 | "### Vapor Pressure " 1072 | ] 1073 | }, 1074 | { 1075 | "cell_type": "code", 1076 | "execution_count": null, 1077 | "id": "eb5f53a2-b67e-40d4-8b89-314de482977e", 1078 | "metadata": { 1079 | "execution": { 1080 | "iopub.execute_input": "2025-08-12T19:47:46.785293Z", 1081 | "iopub.status.busy": "2025-08-12T19:47:46.785194Z", 1082 | "iopub.status.idle": "2025-08-12T19:47:46.798462Z", 1083 | "shell.execute_reply": "2025-08-12T19:47:46.798218Z" 1084 | } 1085 | }, 1086 | "outputs": [], 1087 | "source": [ 1088 | "vapor_pressure = []\n", 1089 | "for i in asc.columns:\n", 1090 | " if i.startswith('EW_'):\n", 1091 | " vapor_pressure.append(i)\n", 1092 | "if len(vapor_pressure) > 0:\n", 1093 | " display(asc[vapor_pressure].describe())\n", 1094 | "\n", 1095 | "#mixing_ratio = []\n", 1096 | "#for i in asc.columns:\n", 1097 | "# if i.startswith('MR_'):\n", 1098 | "# mixing_ratio.append(i)\n", 1099 | "#try:\n", 1100 | "# asc[mixing_ratio].describe()\n", 1101 | "#except:\n", 1102 | "# pass\n", 1103 | " \n", 1104 | "rh = []\n", 1105 | "for i in asc.columns:\n", 1106 | " if i.startswith('RHUM'):\n", 1107 | " rh.append(i)\n", 1108 | "if len(rh) > 0:\n", 1109 | " display(asc[rh].describe())" 1110 | ] 1111 | }, 1112 | { 1113 | "cell_type": "code", 1114 | "execution_count": null, 1115 | "id": "2f0f2c7b-baec-44b0-aeee-5fcfea3f9e08", 1116 | "metadata": { 1117 | "execution": { 1118 | "iopub.execute_input": "2025-08-12T19:47:46.799748Z", 1119 | "iopub.status.busy": "2025-08-12T19:47:46.799674Z", 1120 | "iopub.status.idle": "2025-08-12T19:47:46.824099Z", 1121 | "shell.execute_reply": "2025-08-12T19:47:46.823827Z" 1122 | } 1123 | }, 1124 | "outputs": [], 1125 | "source": [ 1126 | "timeseries_plot(\"Vapor Pressure\", \"EWy [hPa]\", vapor_pressure)\n", 1127 | "timeseries_plot(\"Relative Humidity\", \"RH [%]\", rh)" 1128 | ] 1129 | }, 1130 | { 1131 | "cell_type": "markdown", 1132 | "id": "c08cb1f9-61ce-4133-837a-2f932d8d5dc2", 1133 | "metadata": {}, 1134 | "source": [ 1135 | "### Static Pressure " 1136 | ] 1137 | }, 1138 | { 1139 | "cell_type": "code", 1140 | "execution_count": null, 1141 | "id": "1b95ec55-7abd-4d88-bcda-953a6c9c8da8", 1142 | "metadata": { 1143 | "execution": { 1144 | "iopub.execute_input": "2025-08-12T19:47:46.825540Z", 1145 | "iopub.status.busy": "2025-08-12T19:47:46.825436Z", 1146 | "iopub.status.idle": "2025-08-12T19:47:46.838032Z", 1147 | "shell.execute_reply": "2025-08-12T19:47:46.837678Z" 1148 | }, 1149 | "tags": [] 1150 | }, 1151 | "outputs": [], 1152 | "source": [ 1153 | "static_pressure = []\n", 1154 | "for i in asc.columns:\n", 1155 | " if i.startswith('PS_') | i.startswith('PSF') | i.startswith('PST'):\n", 1156 | " static_pressure.append(i)\n", 1157 | "if len(static_pressure) > 0:\n", 1158 | " display(asc[static_pressure].describe())" 1159 | ] 1160 | }, 1161 | { 1162 | "cell_type": "code", 1163 | "execution_count": null, 1164 | "id": "6b34b738-995e-46eb-8a88-8cc7d15387a7", 1165 | "metadata": { 1166 | "execution": { 1167 | "iopub.execute_input": "2025-08-12T19:47:46.839382Z", 1168 | "iopub.status.busy": "2025-08-12T19:47:46.839306Z", 1169 | "iopub.status.idle": "2025-08-12T19:47:46.885776Z", 1170 | "shell.execute_reply": "2025-08-12T19:47:46.885391Z" 1171 | }, 1172 | "tags": [] 1173 | }, 1174 | "outputs": [], 1175 | "source": [ 1176 | "timeseries_plot(\"Static Pressure\", \"Pressure [hPa]\", static_pressure)\n", 1177 | "\n", 1178 | "colors = itertools.cycle(Category10[8])\n", 1179 | "plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"Difference versus PSFC\") \n", 1180 | "plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 1181 | "plot.add_layout(Title(text=\"Pressure - PSFC [hPa]\", align=\"center\"), \"left\")\n", 1182 | "for i in asc[static_pressure].columns:\n", 1183 | " try:\n", 1184 | " plot.line(dtime, asc[static_pressure][i] - asc['PSFC'], color=next(colors), legend_label=i)\n", 1185 | " format_ticks(plot)\n", 1186 | " except:\n", 1187 | " pass\n", 1188 | "plot.line(dtime, 1, color = 'black', line_dash=\"4 4\", line_width=2)\n", 1189 | "format_ticks(plot)\n", 1190 | "plot.line(dtime, -1, color = 'black', line_dash=\"4 4\", line_width=2)\n", 1191 | "format_ticks(plot)\n", 1192 | "try:\n", 1193 | " format_legend_lines(plot)\n", 1194 | "except:\n", 1195 | " pass\n", 1196 | "try:\n", 1197 | " plot.legend.location = \"top_left\"\n", 1198 | " plot.y_range=Range1d(-5, 5)\n", 1199 | " show(plot)\n", 1200 | "except:\n", 1201 | " pass\n", 1202 | " \n", 1203 | "colors = itertools.cycle(Category10[8])\n", 1204 | "plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"Difference versus PSXC\") \n", 1205 | "plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 1206 | "plot.add_layout(Title(text=\"Pressure - PSXC [hPa]\", align=\"center\"), \"left\")\n", 1207 | "for i in asc[static_pressure].columns:\n", 1208 | " try:\n", 1209 | " plot.line(dtime, asc['PSXC'] - asc[static_pressure][i], color=next(colors), legend_label=i)\n", 1210 | " except:\n", 1211 | " pass\n", 1212 | "\n", 1213 | "plot.line(dtime, 1, color = 'black', line_dash=\"4 4\", line_width=2)\n", 1214 | "format_ticks(plot)\n", 1215 | "plot.line(dtime, -1, color = 'black', line_dash=\"4 4\", line_width=2)\n", 1216 | "format_ticks(plot)\n", 1217 | "try:\n", 1218 | " format_legend_lines(plot)\n", 1219 | "except:\n", 1220 | " pass\n", 1221 | "try:\n", 1222 | " plot.legend.location = \"top_left\"\n", 1223 | " plot.y_range=Range1d(-10, 10)\n", 1224 | " show(plot)\n", 1225 | "except:\n", 1226 | " pass" 1227 | ] 1228 | }, 1229 | { 1230 | "cell_type": "markdown", 1231 | "id": "7b4a4d78-1de7-4ff8-a987-065d658b13df", 1232 | "metadata": {}, 1233 | "source": [ 1234 | "### Dynamic Pressure " 1235 | ] 1236 | }, 1237 | { 1238 | "cell_type": "code", 1239 | "execution_count": null, 1240 | "id": "d4890701-f30b-42c7-b5ad-263d8d2ac019", 1241 | "metadata": { 1242 | "execution": { 1243 | "iopub.execute_input": "2025-08-12T19:47:46.897399Z", 1244 | "iopub.status.busy": "2025-08-12T19:47:46.896557Z", 1245 | "iopub.status.idle": "2025-08-12T19:47:46.940808Z", 1246 | "shell.execute_reply": "2025-08-12T19:47:46.940370Z" 1247 | }, 1248 | "tags": [] 1249 | }, 1250 | "outputs": [], 1251 | "source": [ 1252 | "dynamic_pressure_corr = []\n", 1253 | "for i in asc.columns:\n", 1254 | " if i.startswith('QC_') | i.startswith('QCTFC') | i.startswith('QCRC') | i.startswith('QCFC'):\n", 1255 | " dynamic_pressure_corr.append(i)\n", 1256 | "if len(dynamic_pressure_corr) > 0:\n", 1257 | " display(asc[dynamic_pressure_corr].describe())" 1258 | ] 1259 | }, 1260 | { 1261 | "cell_type": "code", 1262 | "execution_count": null, 1263 | "id": "10ac3c8a-4d89-4946-a8ac-7dcd7a46bfb8", 1264 | "metadata": { 1265 | "execution": { 1266 | "iopub.execute_input": "2025-08-12T19:47:46.943388Z", 1267 | "iopub.status.busy": "2025-08-12T19:47:46.943056Z", 1268 | "iopub.status.idle": "2025-08-12T19:47:46.967707Z", 1269 | "shell.execute_reply": "2025-08-12T19:47:46.967349Z" 1270 | }, 1271 | "tags": [] 1272 | }, 1273 | "outputs": [], 1274 | "source": [ 1275 | "dynamic_pressure_uncorr = []\n", 1276 | "for i in asc.columns:\n", 1277 | " if i.startswith('QC_') | i.startswith('QCTF') | i.startswith('QCR') | i.startswith('QCF'):\n", 1278 | " dynamic_pressure_uncorr.append(i)\n", 1279 | "try:\n", 1280 | " dynamic_pressure_uncorr.remove('QCRTEMP')\n", 1281 | "except:\n", 1282 | " pass\n", 1283 | "try:\n", 1284 | " dynamic_pressure_uncorr.remove('QCFTEMP')\n", 1285 | "except:\n", 1286 | " pass\n", 1287 | "try:\n", 1288 | " dynamic_pressure_uncorr.remove('QCTFTEMP')\n", 1289 | "except:\n", 1290 | " pass\n", 1291 | "try:\n", 1292 | " dynamic_pressure_uncorr.remove('QCTF')\n", 1293 | "except:\n", 1294 | " pass\n", 1295 | "if len(dynamic_pressure_uncorr) > 0:\n", 1296 | " display(asc[dynamic_pressure_uncorr].describe())" 1297 | ] 1298 | }, 1299 | { 1300 | "cell_type": "code", 1301 | "execution_count": null, 1302 | "id": "9c3ebcf0-9c4e-410f-926c-bf27cd50df70", 1303 | "metadata": { 1304 | "execution": { 1305 | "iopub.execute_input": "2025-08-12T19:47:46.969228Z", 1306 | "iopub.status.busy": "2025-08-12T19:47:46.969135Z", 1307 | "iopub.status.idle": "2025-08-12T19:47:47.000432Z", 1308 | "shell.execute_reply": "2025-08-12T19:47:47.000103Z" 1309 | }, 1310 | "tags": [] 1311 | }, 1312 | "outputs": [], 1313 | "source": [ 1314 | "# plot dynamic pressure timeseries\n", 1315 | "colors = itertools.cycle(Category10[8])\n", 1316 | "plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"Dynamic Pressure\") \n", 1317 | "plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 1318 | "plot.add_layout(Title(text=\"Corrected Pressure QCyC [hPa]\", align=\"center\"), \"left\")\n", 1319 | "for i in asc[dynamic_pressure_corr].columns:\n", 1320 | " try:\n", 1321 | " plot.line(dtime, asc[dynamic_pressure_corr][i], color=next(colors), legend_label=i)\n", 1322 | " format_ticks(plot)\n", 1323 | " except:\n", 1324 | " pass\n", 1325 | "try:\n", 1326 | " format_legend_lines(plot)\n", 1327 | "except:\n", 1328 | " pass\n", 1329 | "try:\n", 1330 | " plot.legend.location = \"top_left\"\n", 1331 | " show(plot)\n", 1332 | "except:\n", 1333 | " pass\n", 1334 | "\n", 1335 | "\n", 1336 | "# plot delta pressure QCFC\n", 1337 | "colors = itertools.cycle(Category10[8])\n", 1338 | "plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"Difference versus QCFC\") \n", 1339 | "plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 1340 | "plot.add_layout(Title(text=\"Pressure - QCFC [C]\", align=\"center\"), \"left\")\n", 1341 | "for i in asc[dynamic_pressure_corr].columns:\n", 1342 | " try:\n", 1343 | " plot.line(dtime, asc[dynamic_pressure_corr]['QCFC'] - asc[dynamic_pressure_corr][i], color=next(colors), legend_label=i)\n", 1344 | " format_ticks(plot)\n", 1345 | " except:\n", 1346 | " pass\n", 1347 | "plot.line(dtime, 1, color = 'black', line_dash=\"4 4\", line_width=2)\n", 1348 | "format_ticks(plot)\n", 1349 | "plot.line(dtime, -1, color = 'black', line_dash=\"4 4\", line_width=2)\n", 1350 | "format_ticks(plot)\n", 1351 | "try:\n", 1352 | " format_legend_lines(plot)\n", 1353 | "except Exception as e:\n", 1354 | " print(e)\n", 1355 | " pass\n", 1356 | "try:\n", 1357 | " plot.legend.location = \"top_left\"\n", 1358 | " plot.y_range=Range1d(-20, 20)\n", 1359 | " show(plot)\n", 1360 | "except:\n", 1361 | " pass\n", 1362 | "\n", 1363 | "# plot delta pressure QCXC\n", 1364 | "colors = itertools.cycle(Category10[8])\n", 1365 | "plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"Difference versus QCXC\") \n", 1366 | "plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 1367 | "plot.add_layout(Title(text=\"Pressure - QCXC [C]\", align=\"center\"), \"left\")\n", 1368 | "for i in asc[dynamic_pressure_uncorr].columns:\n", 1369 | " try:\n", 1370 | " plot.line(dtime, asc['QCXC'] - asc[dynamic_pressure_uncorr][i], color=next(colors), legend_label=i)\n", 1371 | " format_ticks(plot)\n", 1372 | " except Exception as e:\n", 1373 | " pass\n", 1374 | "plot.line(dtime, 1, color = 'black', line_dash=\"4 4\", line_width=2)\n", 1375 | "format_ticks(plot)\n", 1376 | "plot.line(dtime, - 1, color = 'black', line_dash=\"4 4\", line_width=2)\n", 1377 | "format_ticks(plot)\n", 1378 | "try:\n", 1379 | " format_legend_lines(plot)\n", 1380 | "except:\n", 1381 | " pass\n", 1382 | "try:\n", 1383 | " plot.legend.location = \"top_left\"\n", 1384 | " plot.y_range=Range1d(-10, 10)\n", 1385 | " show(plot)\n", 1386 | "except:\n", 1387 | " pass\n" 1388 | ] 1389 | }, 1390 | { 1391 | "cell_type": "markdown", 1392 | "id": "39162507", 1393 | "metadata": {}, 1394 | "source": [ 1395 | "### Total Pressure ($p_{s} + q$) " 1396 | ] 1397 | }, 1398 | { 1399 | "cell_type": "code", 1400 | "execution_count": null, 1401 | "id": "f4ee1d4d", 1402 | "metadata": { 1403 | "execution": { 1404 | "iopub.execute_input": "2025-08-12T19:47:47.002128Z", 1405 | "iopub.status.busy": "2025-08-12T19:47:47.002030Z", 1406 | "iopub.status.idle": "2025-08-12T19:47:47.027647Z", 1407 | "shell.execute_reply": "2025-08-12T19:47:47.027240Z" 1408 | } 1409 | }, 1410 | "outputs": [], 1411 | "source": [ 1412 | "# plot dynamic pressure timeseries\n", 1413 | "colors = itertools.cycle(Category10[8])\n", 1414 | "\n", 1415 | "if platform == \"N130AR\": # C130\n", 1416 | " static_pressure_raw = ['PS_A', 'PS_A2', 'PSFD', 'PSFD', 'PSFRD']\n", 1417 | " dynamic_pressure_raw = ['QC_A', 'QC_A2', 'QCR', 'QCF', 'QCFR']\n", 1418 | " total_pressures = ['PT_A', 'PT_A2', 'PT_R', 'PT_F', 'PT_FR']\n", 1419 | "else:\n", 1420 | " static_pressure_raw = ['PS_A', 'PS_A2', 'PSF', 'PSTF', 'PSF']\n", 1421 | " dynamic_pressure_raw = ['QC_A', 'QC_A2', 'QCR', 'QCTF', 'QCR']\n", 1422 | " total_pressures = ['PT_A', 'PT_A2', 'PT_R', 'PT_TF', 'PT_F']\n", 1423 | "\n", 1424 | "\n", 1425 | "plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"Total Dynamic Pressure\")\n", 1426 | "plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 1427 | "plot.add_layout(Title(text=\"Total Pressure [hPa]\", align=\"center\"), \"left\")\n", 1428 | "\n", 1429 | "for i in range(len(static_pressure_raw)):\n", 1430 | " try:\n", 1431 | " static_var = asc[static_pressure_raw[i]]\n", 1432 | " dynamic_var = asc[dynamic_pressure_raw[i]]\n", 1433 | " tot_pressure = asc[static_pressure_raw[i]] + asc[dynamic_pressure_raw[i]]\n", 1434 | " plot.line(dtime, tot_pressure, color=next(colors), legend_label=total_pressures[i])\n", 1435 | " format_ticks(plot)\n", 1436 | " except Exception as e:\n", 1437 | " print(e)\n", 1438 | "try:\n", 1439 | " format_legend_lines(plot)\n", 1440 | "except:\n", 1441 | " pass\n", 1442 | "try:\n", 1443 | " plot.legend.location = \"top_left\"\n", 1444 | " show(plot)\n", 1445 | "except:\n", 1446 | " pass\n", 1447 | "\n", 1448 | "# plot delta pressure vs total pressure effectively used in reference mach calculation\n", 1449 | "colors = itertools.cycle(Category10[8])\n", 1450 | "plot = figure(sizing_mode=\"stretch_width\", height=400, title=f\"Difference versus {total_pressures[-1]}\") \n", 1451 | "plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 1452 | "plot.add_layout(Title(text=f\"Pressure - {total_pressures[-1]} [hPa]\", align=\"center\"), \"left\")\n", 1453 | "\n", 1454 | "ref_pressure = asc[static_pressure_raw[-1]] + asc[dynamic_pressure_raw[-1]]\n", 1455 | "\n", 1456 | "for i in range(len(static_pressure_raw)):\n", 1457 | " try:\n", 1458 | " static_var = asc[static_pressure_raw[i]]\n", 1459 | " dynamic_var = asc[dynamic_pressure_raw[i]]\n", 1460 | " tot_pressure = asc[static_pressure_raw[i]] + asc[dynamic_pressure_raw[i]]\n", 1461 | " dp = tot_pressure - ref_pressure\n", 1462 | " plot.line(dtime, dp, color=next(colors), legend_label=f\"{total_pressures[i]} - {total_pressures[-1]}\")\n", 1463 | " format_ticks(plot)\n", 1464 | " except Exception as e:\n", 1465 | " print(e)\n", 1466 | "\n", 1467 | "plot.line(dtime, 1, color = 'black', line_dash=\"4 4\", line_width=2)\n", 1468 | "format_ticks(plot)\n", 1469 | "plot.line(dtime, -1, color = 'black', line_dash=\"4 4\", line_width=2)\n", 1470 | "format_ticks(plot)\n", 1471 | "try:\n", 1472 | " format_legend_lines(plot)\n", 1473 | "except:\n", 1474 | " pass\n", 1475 | "try:\n", 1476 | " plot.legend.location = \"top_left\"\n", 1477 | " plot.y_range=Range1d(-2, 2)\n", 1478 | " show(plot)\n", 1479 | "except:\n", 1480 | " pass\n", 1481 | "\n", 1482 | "# for i in asc[dynamic_pressure_corr].columns:\n", 1483 | "# try:\n", 1484 | "# plot.line(dtime, asc[dynamic_pressure_corr]['QCFC'] - asc[dynamic_pressure_corr][i], color=next(colors), legend_label=i)\n", 1485 | "# format_ticks(plot)\n", 1486 | "# except:\n", 1487 | "# pass\n" 1488 | ] 1489 | }, 1490 | { 1491 | "cell_type": "markdown", 1492 | "id": "dbc253b3-c39b-4d60-a152-f5b34977c5f1", 1493 | "metadata": {}, 1494 | "source": [ 1495 | "## Airspeed " 1496 | ] 1497 | }, 1498 | { 1499 | "cell_type": "code", 1500 | "execution_count": null, 1501 | "id": "26a5f1ae-e080-4883-ab6a-7d393eb8cfdf", 1502 | "metadata": { 1503 | "execution": { 1504 | "iopub.execute_input": "2025-08-12T19:47:47.029499Z", 1505 | "iopub.status.busy": "2025-08-12T19:47:47.029374Z", 1506 | "iopub.status.idle": "2025-08-12T19:47:47.045346Z", 1507 | "shell.execute_reply": "2025-08-12T19:47:47.045018Z" 1508 | } 1509 | }, 1510 | "outputs": [], 1511 | "source": [ 1512 | "airspeed = []\n", 1513 | "for i in asc.columns:\n", 1514 | " if i.startswith('TAS'):\n", 1515 | " airspeed.append(i)\n", 1516 | "try:\n", 1517 | " airspeed.remove('TASFLG')\n", 1518 | "except:\n", 1519 | " pass\n", 1520 | "try:\n", 1521 | " airspeed.remove('TAS_RWOO')\n", 1522 | "except:\n", 1523 | " pass\n", 1524 | "if len(airspeed) > 0:\n", 1525 | " display(asc[airspeed].describe())" 1526 | ] 1527 | }, 1528 | { 1529 | "cell_type": "code", 1530 | "execution_count": null, 1531 | "id": "292ddbb2-8c56-4949-b656-ed912df6ca22", 1532 | "metadata": { 1533 | "execution": { 1534 | "iopub.execute_input": "2025-08-12T19:47:47.046766Z", 1535 | "iopub.status.busy": "2025-08-12T19:47:47.046687Z", 1536 | "iopub.status.idle": "2025-08-12T19:47:47.072226Z", 1537 | "shell.execute_reply": "2025-08-12T19:47:47.071674Z" 1538 | } 1539 | }, 1540 | "outputs": [], 1541 | "source": [ 1542 | "# plot airspeed\n", 1543 | "colors = itertools.cycle(Category10[8])\n", 1544 | "plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"Airspeed\") \n", 1545 | "plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 1546 | "plot.add_layout(Title(text=\"TASy [m/s]\", align=\"center\"), \"left\")\n", 1547 | "for i in asc[airspeed].columns:\n", 1548 | " try:\n", 1549 | " plot.line(dtime, asc[airspeed][i], color=next(colors), legend_label=i)\n", 1550 | " format_ticks(plot)\n", 1551 | " except:\n", 1552 | " pass\n", 1553 | "try:\n", 1554 | " format_legend_lines(plot)\n", 1555 | "except:\n", 1556 | " pass\n", 1557 | "try:\n", 1558 | " plot.legend.location = \"top_left\"\n", 1559 | " show(plot)\n", 1560 | "except:\n", 1561 | " pass\n", 1562 | "# plot delta airspeed\n", 1563 | "colors = itertools.cycle(Category10[8])\n", 1564 | "plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"Delta Airspeed\") \n", 1565 | "plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 1566 | "plot.add_layout(Title(text=\"Difference v TASF [m/s]\", align=\"center\"), \"left\")\n", 1567 | "for i in asc[airspeed].columns:\n", 1568 | " try:\n", 1569 | " plot.line(dtime, asc[airspeed][i]-asc['TASF'], color=next(colors), legend_label=i)\n", 1570 | " format_ticks(plot)\n", 1571 | " except:\n", 1572 | " pass\n", 1573 | "try:\n", 1574 | " format_legend_lines(plot)\n", 1575 | "except:\n", 1576 | " pass\n", 1577 | "try:\n", 1578 | " plot.legend.location = \"top_left\"\n", 1579 | " plot.line(dtime, 1, color = 'black', line_dash=\"4 4\", line_width=2)\n", 1580 | " plot.line(dtime, -1, color = 'black', line_dash=\"4 4\", line_width=2)\n", 1581 | " plot.y_range=Range1d(-3, 3)\n", 1582 | " show(plot)\n", 1583 | "except:\n", 1584 | " pass" 1585 | ] 1586 | }, 1587 | { 1588 | "cell_type": "code", 1589 | "execution_count": null, 1590 | "id": "4745b2db-b39a-4a28-bc98-4bd6addab78d", 1591 | "metadata": { 1592 | "execution": { 1593 | "iopub.execute_input": "2025-08-12T19:47:47.073680Z", 1594 | "iopub.status.busy": "2025-08-12T19:47:47.073585Z", 1595 | "iopub.status.idle": "2025-08-12T19:47:47.087889Z", 1596 | "shell.execute_reply": "2025-08-12T19:47:47.087587Z" 1597 | } 1598 | }, 1599 | "outputs": [], 1600 | "source": [ 1601 | "mach = []\n", 1602 | "for i in asc.columns:\n", 1603 | " if i.startswith('MACH'):\n", 1604 | " mach.append(i)\n", 1605 | "if len(mach) > 0:\n", 1606 | " display(asc[mach].describe())" 1607 | ] 1608 | }, 1609 | { 1610 | "cell_type": "code", 1611 | "execution_count": null, 1612 | "id": "cce26492-4e41-4dfa-9018-4d9b3ef1d981", 1613 | "metadata": { 1614 | "execution": { 1615 | "iopub.execute_input": "2025-08-12T19:47:47.089227Z", 1616 | "iopub.status.busy": "2025-08-12T19:47:47.089138Z", 1617 | "iopub.status.idle": "2025-08-12T19:47:47.110782Z", 1618 | "shell.execute_reply": "2025-08-12T19:47:47.110444Z" 1619 | } 1620 | }, 1621 | "outputs": [], 1622 | "source": [ 1623 | "# plot mach\n", 1624 | "colors = itertools.cycle(Category10[8])\n", 1625 | "plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"Mach\") \n", 1626 | "plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 1627 | "plot.add_layout(Title(text=\"MACHy\", align=\"center\"), \"left\")\n", 1628 | "for i in asc[mach].columns:\n", 1629 | " try:\n", 1630 | " plot.line(dtime, asc[mach][i], color=next(colors), legend_label=i)\n", 1631 | " format_ticks(plot)\n", 1632 | " except:\n", 1633 | " pass\n", 1634 | "try:\n", 1635 | " plot.legend.location = \"top_left\"\n", 1636 | " show(plot)\n", 1637 | "except:\n", 1638 | " pass" 1639 | ] 1640 | }, 1641 | { 1642 | "cell_type": "code", 1643 | "execution_count": null, 1644 | "id": "a5f6bc10-0c77-4b2d-8715-247264537955", 1645 | "metadata": { 1646 | "execution": { 1647 | "iopub.execute_input": "2025-08-12T19:47:47.112357Z", 1648 | "iopub.status.busy": "2025-08-12T19:47:47.112273Z", 1649 | "iopub.status.idle": "2025-08-12T19:47:47.128060Z", 1650 | "shell.execute_reply": "2025-08-12T19:47:47.127701Z" 1651 | } 1652 | }, 1653 | "outputs": [], 1654 | "source": [ 1655 | "pst = []\n", 1656 | "for i in asc.columns:\n", 1657 | " if i.startswith('PSF'):\n", 1658 | " pst.append(i)\n", 1659 | "if len(pst) > 0:\n", 1660 | " display(asc[pst].describe())" 1661 | ] 1662 | }, 1663 | { 1664 | "cell_type": "code", 1665 | "execution_count": null, 1666 | "id": "1ae21255-b41b-47a8-8fa4-5a12c4200e69", 1667 | "metadata": { 1668 | "execution": { 1669 | "iopub.execute_input": "2025-08-12T19:47:47.129895Z", 1670 | "iopub.status.busy": "2025-08-12T19:47:47.129694Z", 1671 | "iopub.status.idle": "2025-08-12T19:47:47.135894Z", 1672 | "shell.execute_reply": "2025-08-12T19:47:47.135438Z" 1673 | } 1674 | }, 1675 | "outputs": [], 1676 | "source": [ 1677 | "try:\n", 1678 | " timeseries_plot(\"Static Pressure Fuselage\", \"PSF [hPa]\", pst)\n", 1679 | "except:\n", 1680 | " pass" 1681 | ] 1682 | }, 1683 | { 1684 | "cell_type": "markdown", 1685 | "id": "5183f19c-7afe-42d7-b0aa-105831f340ca", 1686 | "metadata": {}, 1687 | "source": [ 1688 | "## Wind " 1689 | ] 1690 | }, 1691 | { 1692 | "cell_type": "markdown", 1693 | "id": "336bcff1-73c9-403a-a439-889cc903b339", 1694 | "metadata": {}, 1695 | "source": [ 1696 | "### Wind Direction " 1697 | ] 1698 | }, 1699 | { 1700 | "cell_type": "code", 1701 | "execution_count": null, 1702 | "id": "82d47a6e-d239-462f-ab48-4f90fb1063a3", 1703 | "metadata": { 1704 | "execution": { 1705 | "iopub.execute_input": "2025-08-12T19:47:47.137672Z", 1706 | "iopub.status.busy": "2025-08-12T19:47:47.137557Z", 1707 | "iopub.status.idle": "2025-08-12T19:47:47.151872Z", 1708 | "shell.execute_reply": "2025-08-12T19:47:47.151423Z" 1709 | } 1710 | }, 1711 | "outputs": [], 1712 | "source": [ 1713 | "wd_winds = []\n", 1714 | "for i in asc.columns:\n", 1715 | " if i.startswith('WD') | i.startswith('IWD'):\n", 1716 | " wd_winds.append(i)\n", 1717 | "if len(wd_winds) > 0:\n", 1718 | " display(asc[wd_winds].describe())" 1719 | ] 1720 | }, 1721 | { 1722 | "cell_type": "code", 1723 | "execution_count": null, 1724 | "id": "cea1dffb-e3d4-41df-b292-650f89a23179", 1725 | "metadata": { 1726 | "execution": { 1727 | "iopub.execute_input": "2025-08-12T19:47:47.153713Z", 1728 | "iopub.status.busy": "2025-08-12T19:47:47.153582Z", 1729 | "iopub.status.idle": "2025-08-12T19:47:47.180737Z", 1730 | "shell.execute_reply": "2025-08-12T19:47:47.180331Z" 1731 | }, 1732 | "scrolled": true 1733 | }, 1734 | "outputs": [], 1735 | "source": [ 1736 | "timeseries_plot(\"Wind Direction\", \"Wind Speed [m/s]\", wd_winds)" 1737 | ] 1738 | }, 1739 | { 1740 | "cell_type": "markdown", 1741 | "id": "71623e7a-7d5b-455f-8c92-b88546b520e4", 1742 | "metadata": {}, 1743 | "source": [ 1744 | "### Wind Speed " 1745 | ] 1746 | }, 1747 | { 1748 | "cell_type": "code", 1749 | "execution_count": null, 1750 | "id": "448d9bd1-8e21-4fbc-9c91-a6a9b3cd5280", 1751 | "metadata": { 1752 | "execution": { 1753 | "iopub.execute_input": "2025-08-12T19:47:47.182874Z", 1754 | "iopub.status.busy": "2025-08-12T19:47:47.182742Z", 1755 | "iopub.status.idle": "2025-08-12T19:47:47.197698Z", 1756 | "shell.execute_reply": "2025-08-12T19:47:47.197382Z" 1757 | } 1758 | }, 1759 | "outputs": [], 1760 | "source": [ 1761 | "ws_winds = []\n", 1762 | "for i in asc.columns:\n", 1763 | " if i.startswith('WS') | i.startswith('IWS'):\n", 1764 | " ws_winds.append(i)\n", 1765 | "if len(ws_winds) > 0:\n", 1766 | " display(asc[ws_winds].describe())" 1767 | ] 1768 | }, 1769 | { 1770 | "cell_type": "code", 1771 | "execution_count": null, 1772 | "id": "a86ee17c-4d14-441f-bcb9-ad75023362cd", 1773 | "metadata": { 1774 | "execution": { 1775 | "iopub.execute_input": "2025-08-12T19:47:47.199138Z", 1776 | "iopub.status.busy": "2025-08-12T19:47:47.199055Z", 1777 | "iopub.status.idle": "2025-08-12T19:47:47.225349Z", 1778 | "shell.execute_reply": "2025-08-12T19:47:47.225052Z" 1779 | }, 1780 | "jupyter": { 1781 | "source_hidden": true 1782 | } 1783 | }, 1784 | "outputs": [], 1785 | "source": [ 1786 | "timeseries_plot(\"Wind Speed\", \"Wind Speed [m/s]\", ws_winds)" 1787 | ] 1788 | }, 1789 | { 1790 | "cell_type": "code", 1791 | "execution_count": null, 1792 | "id": "c81079bd-8965-40f3-abe7-8139e39db15f", 1793 | "metadata": { 1794 | "execution": { 1795 | "iopub.execute_input": "2025-08-12T19:47:47.226825Z", 1796 | "iopub.status.busy": "2025-08-12T19:47:47.226712Z", 1797 | "iopub.status.idle": "2025-08-12T19:47:47.240359Z", 1798 | "shell.execute_reply": "2025-08-12T19:47:47.240078Z" 1799 | } 1800 | }, 1801 | "outputs": [], 1802 | "source": [ 1803 | "wi_winds = []\n", 1804 | "for i in asc.columns:\n", 1805 | " if i.startswith('WIX') | i.startswith('WIC') | i.startswith('WIY'):\n", 1806 | " wi_winds.append(i)\n", 1807 | "if len(wi_winds) > 0:\n", 1808 | " display(asc[wi_winds].describe())" 1809 | ] 1810 | }, 1811 | { 1812 | "cell_type": "code", 1813 | "execution_count": null, 1814 | "id": "ca1e0f0f-f661-4910-9811-d89f52651162", 1815 | "metadata": { 1816 | "execution": { 1817 | "iopub.execute_input": "2025-08-12T19:47:47.241665Z", 1818 | "iopub.status.busy": "2025-08-12T19:47:47.241584Z", 1819 | "iopub.status.idle": "2025-08-12T19:47:47.263317Z", 1820 | "shell.execute_reply": "2025-08-12T19:47:47.262948Z" 1821 | } 1822 | }, 1823 | "outputs": [], 1824 | "source": [ 1825 | "colors = itertools.cycle(Category10[8])\n", 1826 | "plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"Vertical Wind Speed\") \n", 1827 | "plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 1828 | "plot.add_layout(Title(text=\"Vertical Wind [m/s]\", align=\"center\"), \"left\")\n", 1829 | "for i in asc[wi_winds].columns:\n", 1830 | " try:\n", 1831 | " plot.line(dtime, asc[wi_winds][i], color=next(colors), legend_label=i)\n", 1832 | " format_ticks(plot)\n", 1833 | " plot.line(dtime, 2.5, color = 'black', line_dash=\"4 4\", line_width=2)\n", 1834 | " plot.line(dtime, -2.5, color = 'black', line_dash=\"4 4\", line_width=2)\n", 1835 | " plot.line(dtime, 0, color = 'red', line_dash=\"4 4\", line_width=2)\n", 1836 | " except:\n", 1837 | " pass\n", 1838 | "try:\n", 1839 | " plot.legend.location = \"top_left\"\n", 1840 | " show(plot)\n", 1841 | "except:\n", 1842 | " pass" 1843 | ] 1844 | }, 1845 | { 1846 | "cell_type": "code", 1847 | "execution_count": null, 1848 | "id": "e1bc4cbc-7878-43bf-8dbd-5b638e72196a", 1849 | "metadata": { 1850 | "execution": { 1851 | "iopub.execute_input": "2025-08-12T19:47:47.264724Z", 1852 | "iopub.status.busy": "2025-08-12T19:47:47.264616Z", 1853 | "iopub.status.idle": "2025-08-12T19:47:47.277284Z", 1854 | "shell.execute_reply": "2025-08-12T19:47:47.277008Z" 1855 | } 1856 | }, 1857 | "outputs": [], 1858 | "source": [ 1859 | "east_wind = []\n", 1860 | "for i in asc.columns:\n", 1861 | " if i.startswith('UIC') | i.startswith('IUX'):\n", 1862 | " east_wind.append(i)\n", 1863 | "if len(east_wind) > 0:\n", 1864 | " display(asc[east_wind].describe())" 1865 | ] 1866 | }, 1867 | { 1868 | "cell_type": "code", 1869 | "execution_count": null, 1870 | "id": "ff2ce448-7196-450a-b7f7-f60b15f0bbe5", 1871 | "metadata": { 1872 | "execution": { 1873 | "iopub.execute_input": "2025-08-12T19:47:47.278516Z", 1874 | "iopub.status.busy": "2025-08-12T19:47:47.278431Z", 1875 | "iopub.status.idle": "2025-08-12T19:47:47.292797Z", 1876 | "shell.execute_reply": "2025-08-12T19:47:47.292520Z" 1877 | } 1878 | }, 1879 | "outputs": [], 1880 | "source": [ 1881 | "north_wind = []\n", 1882 | "for i in asc.columns:\n", 1883 | " if i.startswith('VIC') | i.startswith('IVY'):\n", 1884 | " north_wind.append(i)\n", 1885 | "if len(north_wind) > 0:\n", 1886 | " display(asc[north_wind].describe())" 1887 | ] 1888 | }, 1889 | { 1890 | "cell_type": "code", 1891 | "execution_count": null, 1892 | "id": "3df00908-8c6d-4542-9c4a-b4aacab1fb30", 1893 | "metadata": { 1894 | "execution": { 1895 | "iopub.execute_input": "2025-08-12T19:47:47.294023Z", 1896 | "iopub.status.busy": "2025-08-12T19:47:47.293942Z", 1897 | "iopub.status.idle": "2025-08-12T19:47:47.319734Z", 1898 | "shell.execute_reply": "2025-08-12T19:47:47.319432Z" 1899 | } 1900 | }, 1901 | "outputs": [], 1902 | "source": [ 1903 | "timeseries_plot(\"East Wind Speed\", \"East Wind Speed [m/s]\", east_wind)\n", 1904 | "timeseries_plot(\"North Wind Speed\", \"North Wind Speed [m/s]\", north_wind)" 1905 | ] 1906 | }, 1907 | { 1908 | "cell_type": "markdown", 1909 | "id": "e0bb7dac-3695-40eb-a640-a0a37dca2286", 1910 | "metadata": {}, 1911 | "source": [ 1912 | "## Schuler Oscillation " 1913 | ] 1914 | }, 1915 | { 1916 | "cell_type": "code", 1917 | "execution_count": null, 1918 | "id": "2137e9dd-74e4-42bb-ac29-f60d55eef798", 1919 | "metadata": { 1920 | "execution": { 1921 | "iopub.execute_input": "2025-08-12T19:47:47.321179Z", 1922 | "iopub.status.busy": "2025-08-12T19:47:47.321095Z", 1923 | "iopub.status.idle": "2025-08-12T19:47:47.354599Z", 1924 | "shell.execute_reply": "2025-08-12T19:47:47.354336Z" 1925 | } 1926 | }, 1927 | "outputs": [], 1928 | "source": [ 1929 | "colors = itertools.cycle(Category10[8])\n", 1930 | "plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"IRU Schuler oscillation, east west component \") \n", 1931 | "plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 1932 | "plot.add_layout(Title(text=\"GGVEW [m/s]\", align=\"center\"), \"left\")\n", 1933 | "try:\n", 1934 | " plot.line(dtime, asc[\"GGVEW\"], color=next(colors), legend_label=\"GGVEW\")\n", 1935 | "except:\n", 1936 | " pass\n", 1937 | "try:\n", 1938 | " plot.line(dtime, asc[\"VEW\"], color=next(colors), legend_label=\"VEW\")\n", 1939 | "except:\n", 1940 | " pass \n", 1941 | "\n", 1942 | "try:\n", 1943 | " plot.line(dtime, (asc[\"GGVEW\"] - asc[\"VEW\"]) *50, color=next(colors), legend_label=\"Difference * 50\")\n", 1944 | " format_ticks(plot)\n", 1945 | "except:\n", 1946 | " pass\n", 1947 | "try:\n", 1948 | " format_legend_lines(plot)\n", 1949 | "except:\n", 1950 | " pass\n", 1951 | "try:\n", 1952 | " plot.legend.location = \"top_left\"\n", 1953 | " show(plot)\n", 1954 | "except:\n", 1955 | " pass\n", 1956 | "\n", 1957 | "colors = itertools.cycle(Category10[8])\n", 1958 | "plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"IRU Schuler oscillation, north south component \") \n", 1959 | "plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 1960 | "plot.add_layout(Title(text=\"GGVNS [m/s]\", align=\"center\"), \"left\")\n", 1961 | "try:\n", 1962 | " plot.line(dtime, asc[\"GGVNS\"], color=next(colors), legend_label=\"GGVEW\")\n", 1963 | "except:\n", 1964 | " pass\n", 1965 | "try:\n", 1966 | " plot.line(dtime, asc[\"VNS\"], color=next(colors), legend_label=\"VEW\")\n", 1967 | "except:\n", 1968 | " pass \n", 1969 | "try:\n", 1970 | " plot.line(dtime, (asc[\"GGVNS\"] - asc[\"VNS\"]) *50, color=next(colors), legend_label=\"Difference * 50\")\n", 1971 | " format_ticks(plot)\n", 1972 | "except:\n", 1973 | " pass\n", 1974 | "try:\n", 1975 | " format_legend_lines(plot)\n", 1976 | "except:\n", 1977 | " pass\n", 1978 | "try:\n", 1979 | " plot.legend.location = \"top_left\"\n", 1980 | " show(plot)\n", 1981 | "except:\n", 1982 | " pass" 1983 | ] 1984 | }, 1985 | { 1986 | "cell_type": "markdown", 1987 | "id": "a8e5791f-d70e-4aba-a907-1d9b9926d03c", 1988 | "metadata": {}, 1989 | "source": [ 1990 | "## Radome Pressures and Angles " 1991 | ] 1992 | }, 1993 | { 1994 | "cell_type": "code", 1995 | "execution_count": null, 1996 | "id": "9258c423-8dfe-40bd-b7f0-2e722cce7268", 1997 | "metadata": { 1998 | "execution": { 1999 | "iopub.execute_input": "2025-08-12T19:47:47.356030Z", 2000 | "iopub.status.busy": "2025-08-12T19:47:47.355928Z", 2001 | "iopub.status.idle": "2025-08-12T19:47:47.441809Z", 2002 | "shell.execute_reply": "2025-08-12T19:47:47.441492Z" 2003 | } 2004 | }, 2005 | "outputs": [], 2006 | "source": [ 2007 | "radome_angles = []\n", 2008 | "for i in asc.columns:\n", 2009 | " if i.startswith('AKRD') | i.startswith('PITCH') | i.startswith('AOAREF') | i.startswith('SSRD'):\n", 2010 | " radome_angles.append(i)\n", 2011 | "if len(radome_angles) > 0:\n", 2012 | " display(asc[radome_angles].describe())" 2013 | ] 2014 | }, 2015 | { 2016 | "cell_type": "code", 2017 | "execution_count": null, 2018 | "id": "eb079000-a763-4be1-a080-974bc7ed7725", 2019 | "metadata": { 2020 | "execution": { 2021 | "iopub.execute_input": "2025-08-12T19:47:47.443353Z", 2022 | "iopub.status.busy": "2025-08-12T19:47:47.443266Z", 2023 | "iopub.status.idle": "2025-08-12T19:47:47.480521Z", 2024 | "shell.execute_reply": "2025-08-12T19:47:47.480179Z" 2025 | } 2026 | }, 2027 | "outputs": [], 2028 | "source": [ 2029 | "timeseries_plot(\"Radome Angles\", \"Angle [Degrees]\", radome_angles)\n", 2030 | "\n", 2031 | "colors = itertools.cycle(Category10[8])\n", 2032 | "plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"\") \n", 2033 | "plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 2034 | "plot.add_layout(Title(text=\"SSRD [Degrees]\", align=\"center\"), \"left\")\n", 2035 | "try:\n", 2036 | " plot.line(dtime, asc[\"SSRD\"], color=next(colors), legend_label=\"SSRD\")\n", 2037 | "except:\n", 2038 | " pass\n", 2039 | "try:\n", 2040 | " plot.line(dtime, asc[\"SSREF\"], color=next(colors), legend_label=\"SSREF\")\n", 2041 | "except:\n", 2042 | " pass\n", 2043 | "try:\n", 2044 | " format_legend_lines(plot)\n", 2045 | "except:\n", 2046 | " pass\n", 2047 | "try:\n", 2048 | " plot.legend.location = \"top_left\"\n", 2049 | " format_ticks(plot)\n", 2050 | " plot.line(dtime, 2.0, color = 'black', line_dash=\"4 4\", line_width=2)\n", 2051 | " plot.line(dtime, -2.0, color = 'black', line_dash=\"4 4\", line_width=2)\n", 2052 | " show(plot)\n", 2053 | "except:\n", 2054 | " pass\n", 2055 | " \n", 2056 | "colors = itertools.cycle(Category10[8])\n", 2057 | "plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"\") \n", 2058 | "plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 2059 | "plot.add_layout(Title(text=\"ADIFR [hPa]\", align=\"center\"), \"left\")\n", 2060 | "try:\n", 2061 | " plot.line(dtime, asc[\"ADIFR\"], color=next(colors), legend_label=\"ADIFR\")\n", 2062 | " format_ticks(plot)\n", 2063 | "except:\n", 2064 | " pass\n", 2065 | "try:\n", 2066 | " format_legend_lines(plot)\n", 2067 | "except:\n", 2068 | " pass\n", 2069 | "try:\n", 2070 | " plot.legend.location = \"top_left\"\n", 2071 | " show(plot)\n", 2072 | "except:\n", 2073 | " pass\n", 2074 | " \n", 2075 | "colors = itertools.cycle(Category10[8])\n", 2076 | "plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"\") \n", 2077 | "plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 2078 | "plot.add_layout(Title(text=\"BDIFR [hPa]\", align=\"center\"), \"left\")\n", 2079 | "try:\n", 2080 | " plot.line(dtime, asc[\"BDIFR\"], color=next(colors), legend_label=\"BDIFR\")\n", 2081 | " format_ticks(plot)\n", 2082 | "except:\n", 2083 | " pass\n", 2084 | "try:\n", 2085 | " format_legend_lines(plot)\n", 2086 | "except:\n", 2087 | " pass\n", 2088 | "try:\n", 2089 | " plot.legend.location = \"top_left\"\n", 2090 | " show(plot)\n", 2091 | "except:\n", 2092 | " pass" 2093 | ] 2094 | }, 2095 | { 2096 | "cell_type": "markdown", 2097 | "id": "68a37e66-c9a7-4b63-aca1-75b70bd9d81e", 2098 | "metadata": {}, 2099 | "source": [ 2100 | "## Pitch Roll Heading " 2101 | ] 2102 | }, 2103 | { 2104 | "cell_type": "markdown", 2105 | "id": "1c6e2e66-00c4-4be3-9a72-293ba40d3d13", 2106 | "metadata": {}, 2107 | "source": [ 2108 | "### Pitch " 2109 | ] 2110 | }, 2111 | { 2112 | "cell_type": "code", 2113 | "execution_count": null, 2114 | "id": "97f63f5d-a557-46f5-be6c-fa5bf86fe1e7", 2115 | "metadata": { 2116 | "execution": { 2117 | "iopub.execute_input": "2025-08-12T19:47:47.482413Z", 2118 | "iopub.status.busy": "2025-08-12T19:47:47.482268Z", 2119 | "iopub.status.idle": "2025-08-12T19:47:47.496691Z", 2120 | "shell.execute_reply": "2025-08-12T19:47:47.496348Z" 2121 | } 2122 | }, 2123 | "outputs": [], 2124 | "source": [ 2125 | "pitch = []\n", 2126 | "for i in asc.columns:\n", 2127 | " if i.startswith('PITCH'):\n", 2128 | " pitch.append(i)\n", 2129 | "if len(pitch) > 0:\n", 2130 | " display(asc[pitch].describe())" 2131 | ] 2132 | }, 2133 | { 2134 | "cell_type": "code", 2135 | "execution_count": null, 2136 | "id": "dbcce933-6c08-4f49-80d8-a55684de0e32", 2137 | "metadata": { 2138 | "execution": { 2139 | "iopub.execute_input": "2025-08-12T19:47:47.497907Z", 2140 | "iopub.status.busy": "2025-08-12T19:47:47.497812Z", 2141 | "iopub.status.idle": "2025-08-12T19:47:47.519829Z", 2142 | "shell.execute_reply": "2025-08-12T19:47:47.519530Z" 2143 | } 2144 | }, 2145 | "outputs": [], 2146 | "source": [ 2147 | "colors = itertools.cycle(Category10[8])\n", 2148 | "plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"Pitch\") \n", 2149 | "plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 2150 | "plot.add_layout(Title(text=\"PITCH [Degrees]\", align=\"center\"), \"left\")\n", 2151 | "for i in asc[pitch].columns:\n", 2152 | " try:\n", 2153 | " plot.line(dtime, asc[pitch][i], color=next(colors), legend_label=i)\n", 2154 | " except:\n", 2155 | " pass\n", 2156 | " try:\n", 2157 | " plot.line(dtime, (asc[pitch]['PITCH'] - asc[pitch]['PITCH_IRS2']) * 50, color=next(colors), legend_label=\"PITCH - PITCH_IRS2 Difference * 50\")\n", 2158 | " except Exception as e:\n", 2159 | " pass\n", 2160 | " try:\n", 2161 | " plot.line(dtime, (asc[pitch]['PITCH'] - asc[pitch]['PITCH_IRS3']) * 50, color=next(colors), legend_label=\"PITCH - PITCH_IRS3 Difference * 50\")\n", 2162 | " except Exception as e:\n", 2163 | " pass\n", 2164 | " try:\n", 2165 | " format_ticks(plot)\n", 2166 | " except:\n", 2167 | " pass\n", 2168 | "try:\n", 2169 | " format_legend_lines(plot)\n", 2170 | "except:\n", 2171 | " pass\n", 2172 | "try:\n", 2173 | " plot.legend.location = \"top_left\"\n", 2174 | " show(plot)\n", 2175 | "except:\n", 2176 | " pass" 2177 | ] 2178 | }, 2179 | { 2180 | "cell_type": "markdown", 2181 | "id": "88952e43-5720-4fd5-81e8-de00e676fbee", 2182 | "metadata": {}, 2183 | "source": [ 2184 | "### Roll " 2185 | ] 2186 | }, 2187 | { 2188 | "cell_type": "code", 2189 | "execution_count": null, 2190 | "id": "227466cb-9376-4dbd-acb8-8f59dec2f8cb", 2191 | "metadata": { 2192 | "execution": { 2193 | "iopub.execute_input": "2025-08-12T19:47:47.521293Z", 2194 | "iopub.status.busy": "2025-08-12T19:47:47.521185Z", 2195 | "iopub.status.idle": "2025-08-12T19:47:47.535004Z", 2196 | "shell.execute_reply": "2025-08-12T19:47:47.534667Z" 2197 | } 2198 | }, 2199 | "outputs": [], 2200 | "source": [ 2201 | "roll = []\n", 2202 | "for i in asc.columns:\n", 2203 | " if i.startswith('ROLL'):\n", 2204 | " roll.append(i)\n", 2205 | "if len(roll) > 0:\n", 2206 | " display(asc[roll].describe())" 2207 | ] 2208 | }, 2209 | { 2210 | "cell_type": "code", 2211 | "execution_count": null, 2212 | "id": "45f48cd5-cd74-4216-8e0d-2c7bffb95e72", 2213 | "metadata": { 2214 | "execution": { 2215 | "iopub.execute_input": "2025-08-12T19:47:47.536348Z", 2216 | "iopub.status.busy": "2025-08-12T19:47:47.536228Z", 2217 | "iopub.status.idle": "2025-08-12T19:47:47.558404Z", 2218 | "shell.execute_reply": "2025-08-12T19:47:47.558138Z" 2219 | } 2220 | }, 2221 | "outputs": [], 2222 | "source": [ 2223 | "colors = itertools.cycle(Category10[8])\n", 2224 | "plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"Roll\") \n", 2225 | "plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 2226 | "plot.add_layout(Title(text=\"PITCH [Degrees]\", align=\"center\"), \"left\")\n", 2227 | "for i in asc[roll].columns:\n", 2228 | " try:\n", 2229 | " plot.line(dtime, asc[roll][i], color=next(colors), legend_label=i)\n", 2230 | " except:\n", 2231 | " pass\n", 2232 | " try:\n", 2233 | " plot.line(dtime, (asc[roll]['ROLL'] - asc[roll]['ROLL_IRS2']) * 500, color=next(colors), legend_label=\"ROLL - ROLL_IRS2 Difference * 500\")\n", 2234 | " except:\n", 2235 | " pass\n", 2236 | " try:\n", 2237 | " plot.line(dtime, (asc[roll]['ROLL'] - asc[roll]['ROLL_IRS3']) * 500, color=next(colors), legend_label=\"ROLL - ROLL_IRS3 Difference * 500\")\n", 2238 | " except:\n", 2239 | " pass\n", 2240 | " try:\n", 2241 | " format_ticks(plot)\n", 2242 | " except:\n", 2243 | " pass\n", 2244 | "try:\n", 2245 | " format_legend_lines(plot)\n", 2246 | "except:\n", 2247 | " pass\n", 2248 | "try:\n", 2249 | " plot.legend.location = \"top_left\"\n", 2250 | " show(plot)\n", 2251 | "except:\n", 2252 | " pass" 2253 | ] 2254 | }, 2255 | { 2256 | "cell_type": "markdown", 2257 | "id": "252a2643-2dcd-4544-ac82-90fc5c3f8b38", 2258 | "metadata": {}, 2259 | "source": [ 2260 | "### Heading " 2261 | ] 2262 | }, 2263 | { 2264 | "cell_type": "code", 2265 | "execution_count": null, 2266 | "id": "8dc418ca-2d8b-4eb9-8ada-0563bf17b24a", 2267 | "metadata": { 2268 | "execution": { 2269 | "iopub.execute_input": "2025-08-12T19:47:47.559829Z", 2270 | "iopub.status.busy": "2025-08-12T19:47:47.559748Z", 2271 | "iopub.status.idle": "2025-08-12T19:47:47.573223Z", 2272 | "shell.execute_reply": "2025-08-12T19:47:47.572913Z" 2273 | } 2274 | }, 2275 | "outputs": [], 2276 | "source": [ 2277 | "thdg = []\n", 2278 | "for i in asc.columns:\n", 2279 | " if i.startswith('THDG'):\n", 2280 | " thdg.append(i)\n", 2281 | "if len(thdg) > 0:\n", 2282 | " display(asc[thdg].describe())" 2283 | ] 2284 | }, 2285 | { 2286 | "cell_type": "code", 2287 | "execution_count": null, 2288 | "id": "26408480-d767-4e14-863a-0a3718847ccd", 2289 | "metadata": { 2290 | "execution": { 2291 | "iopub.execute_input": "2025-08-12T19:47:47.574542Z", 2292 | "iopub.status.busy": "2025-08-12T19:47:47.574459Z", 2293 | "iopub.status.idle": "2025-08-12T19:47:47.595564Z", 2294 | "shell.execute_reply": "2025-08-12T19:47:47.595268Z" 2295 | } 2296 | }, 2297 | "outputs": [], 2298 | "source": [ 2299 | "colors = itertools.cycle(Category10[8])\n", 2300 | "plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"True Heading\") \n", 2301 | "plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 2302 | "plot.add_layout(Title(text=\"THDG [degrees]\", align=\"center\"), \"left\")\n", 2303 | "\n", 2304 | "for i in asc[thdg].columns:\n", 2305 | " try:\n", 2306 | " plot.line(dtime, asc[thdg][i], color=next(colors), legend_label=i)\n", 2307 | " format_ticks(plot)\n", 2308 | " except:\n", 2309 | " pass\n", 2310 | "try:\n", 2311 | " format_legend_lines(plot)\n", 2312 | "except:\n", 2313 | " pass\n", 2314 | "try:\n", 2315 | " plot.legend.location = \"top_left\"\n", 2316 | " show(plot)\n", 2317 | "except:\n", 2318 | " pass " 2319 | ] 2320 | }, 2321 | { 2322 | "cell_type": "markdown", 2323 | "id": "24eaa853-2d30-4d11-a62b-6c000c1f9663", 2324 | "metadata": {}, 2325 | "source": [ 2326 | "## ACINS VSPD Altitude " 2327 | ] 2328 | }, 2329 | { 2330 | "cell_type": "markdown", 2331 | "id": "72c9687a-f920-4558-b857-1028bcd2f5fa", 2332 | "metadata": {}, 2333 | "source": [ 2334 | "### ACINS " 2335 | ] 2336 | }, 2337 | { 2338 | "cell_type": "code", 2339 | "execution_count": null, 2340 | "id": "897395ed-b38c-42d4-b4ec-33ca9029870a", 2341 | "metadata": { 2342 | "execution": { 2343 | "iopub.execute_input": "2025-08-12T19:47:47.597001Z", 2344 | "iopub.status.busy": "2025-08-12T19:47:47.596909Z", 2345 | "iopub.status.idle": "2025-08-12T19:47:47.610600Z", 2346 | "shell.execute_reply": "2025-08-12T19:47:47.610334Z" 2347 | } 2348 | }, 2349 | "outputs": [], 2350 | "source": [ 2351 | "acins = []\n", 2352 | "for i in asc.columns:\n", 2353 | " if i.startswith('ACINS'):\n", 2354 | " acins.append(i)\n", 2355 | "\n", 2356 | "if len(acins) > 0:\n", 2357 | " display(asc[acins].describe())" 2358 | ] 2359 | }, 2360 | { 2361 | "cell_type": "code", 2362 | "execution_count": null, 2363 | "id": "ed686112-e9c1-4116-9bd7-63a8845bd4e2", 2364 | "metadata": { 2365 | "execution": { 2366 | "iopub.execute_input": "2025-08-12T19:47:47.611994Z", 2367 | "iopub.status.busy": "2025-08-12T19:47:47.611892Z", 2368 | "iopub.status.idle": "2025-08-12T19:47:47.632042Z", 2369 | "shell.execute_reply": "2025-08-12T19:47:47.631789Z" 2370 | } 2371 | }, 2372 | "outputs": [], 2373 | "source": [ 2374 | "colors = itertools.cycle(Category10[8])\n", 2375 | "plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"ACINS\") \n", 2376 | "plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 2377 | "plot.add_layout(Title(text=\"ACINS [m/s^2]\", align=\"center\"), \"left\")\n", 2378 | "\n", 2379 | "for i in asc[acins].columns:\n", 2380 | " try:\n", 2381 | " plot.line(dtime, asc[acins][i], color=next(colors), legend_label=i)\n", 2382 | " format_ticks(plot)\n", 2383 | " except:\n", 2384 | " pass\n", 2385 | "try:\n", 2386 | " format_legend_lines(plot)\n", 2387 | "except:\n", 2388 | " pass\n", 2389 | "try:\n", 2390 | " plot.legend.location = \"top_left\"\n", 2391 | " show(plot)\n", 2392 | "except:\n", 2393 | " pass " 2394 | ] 2395 | }, 2396 | { 2397 | "cell_type": "markdown", 2398 | "id": "1c784659-1d1f-47e8-b837-ec0ceb0d04b8", 2399 | "metadata": {}, 2400 | "source": [ 2401 | "### VSPD " 2402 | ] 2403 | }, 2404 | { 2405 | "cell_type": "code", 2406 | "execution_count": null, 2407 | "id": "60a11056-9ff5-4fc6-8cb3-a7576dd8b2fd", 2408 | "metadata": { 2409 | "execution": { 2410 | "iopub.execute_input": "2025-08-12T19:47:47.633371Z", 2411 | "iopub.status.busy": "2025-08-12T19:47:47.633293Z", 2412 | "iopub.status.idle": "2025-08-12T19:47:47.646368Z", 2413 | "shell.execute_reply": "2025-08-12T19:47:47.646091Z" 2414 | } 2415 | }, 2416 | "outputs": [], 2417 | "source": [ 2418 | "vspd = []\n", 2419 | "for i in asc.columns:\n", 2420 | " if i.startswith('VSPD'):\n", 2421 | " vspd.append(i)\n", 2422 | "try:\n", 2423 | " asc[vspd].describe()\n", 2424 | "except:\n", 2425 | " pass" 2426 | ] 2427 | }, 2428 | { 2429 | "cell_type": "code", 2430 | "execution_count": null, 2431 | "id": "cda35e28-08ae-491a-b96c-91df0350180d", 2432 | "metadata": { 2433 | "execution": { 2434 | "iopub.execute_input": "2025-08-12T19:47:47.647626Z", 2435 | "iopub.status.busy": "2025-08-12T19:47:47.647549Z", 2436 | "iopub.status.idle": "2025-08-12T19:47:47.666368Z", 2437 | "shell.execute_reply": "2025-08-12T19:47:47.666122Z" 2438 | } 2439 | }, 2440 | "outputs": [], 2441 | "source": [ 2442 | "colors = itertools.cycle(Category10[8])\n", 2443 | "plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"VSPD\") \n", 2444 | "plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 2445 | "plot.add_layout(Title(text=\"VSPD\", align=\"center\"), \"left\")\n", 2446 | "\n", 2447 | "for i in asc[vspd].columns:\n", 2448 | " try:\n", 2449 | " plot.line(dtime, asc[vspd][i], color=next(colors), legend_label=i)\n", 2450 | " format_ticks(plot)\n", 2451 | " except:\n", 2452 | " pass\n", 2453 | "try:\n", 2454 | " format_legend_lines(plot)\n", 2455 | "except:\n", 2456 | " pass\n", 2457 | "try:\n", 2458 | " plot.legend.location = \"top_left\"\n", 2459 | " show(plot)\n", 2460 | "except:\n", 2461 | " pass" 2462 | ] 2463 | }, 2464 | { 2465 | "cell_type": "markdown", 2466 | "id": "c56da230-db0a-4492-b28b-77ce66144fea", 2467 | "metadata": {}, 2468 | "source": [ 2469 | "### Altitude " 2470 | ] 2471 | }, 2472 | { 2473 | "cell_type": "code", 2474 | "execution_count": null, 2475 | "id": "d2ae17da-2c35-473e-a847-fe3585162f25", 2476 | "metadata": { 2477 | "execution": { 2478 | "iopub.execute_input": "2025-08-12T19:47:47.667636Z", 2479 | "iopub.status.busy": "2025-08-12T19:47:47.667560Z", 2480 | "iopub.status.idle": "2025-08-12T19:47:47.681135Z", 2481 | "shell.execute_reply": "2025-08-12T19:47:47.680869Z" 2482 | } 2483 | }, 2484 | "outputs": [], 2485 | "source": [ 2486 | "alt = []\n", 2487 | "for i in asc.columns:\n", 2488 | " if i.startswith('GGALT') | i.startswith('ALT_'):\n", 2489 | " alt.append(i)\n", 2490 | "try:\n", 2491 | " alt.remove('GGALTSD_232')\n", 2492 | "except:\n", 2493 | " pass\n", 2494 | "try:\n", 2495 | " alt.remove('GGALTSD')\n", 2496 | "except:\n", 2497 | " pass\n", 2498 | "try:\n", 2499 | " alt.remove('GGALTSD_VTB')\n", 2500 | "except:\n", 2501 | " pass\n", 2502 | "try:\n", 2503 | " alt.remove('GGALTSD_VTB2')\n", 2504 | "except:\n", 2505 | " pass\n", 2506 | "if len(alt) > 0:\n", 2507 | " display(asc[alt].describe())" 2508 | ] 2509 | }, 2510 | { 2511 | "cell_type": "code", 2512 | "execution_count": null, 2513 | "id": "497b13f3-c552-45ec-a7f6-883d47286f87", 2514 | "metadata": { 2515 | "execution": { 2516 | "iopub.execute_input": "2025-08-12T19:47:47.682402Z", 2517 | "iopub.status.busy": "2025-08-12T19:47:47.682326Z", 2518 | "iopub.status.idle": "2025-08-12T19:47:47.702610Z", 2519 | "shell.execute_reply": "2025-08-12T19:47:47.702321Z" 2520 | } 2521 | }, 2522 | "outputs": [], 2523 | "source": [ 2524 | "colors = itertools.cycle(Category10[8])\n", 2525 | "plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"Altitude\") \n", 2526 | "plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 2527 | "plot.add_layout(Title(text=\"Altitude (ALT) [m]\", align=\"center\"), \"left\")\n", 2528 | "\n", 2529 | "for i in asc[alt].columns:\n", 2530 | " try:\n", 2531 | " plot.line(dtime, asc[alt][i], color=next(colors), legend_label=i)\n", 2532 | " format_ticks(plot)\n", 2533 | " except:\n", 2534 | " pass\n", 2535 | "try:\n", 2536 | " format_legend_lines(plot)\n", 2537 | "except:\n", 2538 | " pass\n", 2539 | "try:\n", 2540 | " plot.legend.location = \"top_left\"\n", 2541 | " show(plot)\n", 2542 | "except:\n", 2543 | " pass" 2544 | ] 2545 | }, 2546 | { 2547 | "cell_type": "markdown", 2548 | "id": "d1b8c44e-8f4c-408b-a302-d519c5eadfbb", 2549 | "metadata": { 2550 | "execution": { 2551 | "iopub.execute_input": "2024-03-14T19:01:53.386236Z", 2552 | "iopub.status.busy": "2024-03-14T19:01:53.386114Z", 2553 | "iopub.status.idle": "2024-03-14T19:01:53.387806Z", 2554 | "shell.execute_reply": "2024-03-14T19:01:53.387544Z" 2555 | } 2556 | }, 2557 | "source": [ 2558 | "## Condensation Nucleus Concentrations " 2559 | ] 2560 | }, 2561 | { 2562 | "cell_type": "code", 2563 | "execution_count": null, 2564 | "id": "6e87899b-fb44-48b8-b834-42d2eb2c780a", 2565 | "metadata": { 2566 | "execution": { 2567 | "iopub.execute_input": "2025-08-12T19:47:47.703888Z", 2568 | "iopub.status.busy": "2025-08-12T19:47:47.703811Z", 2569 | "iopub.status.idle": "2025-08-12T19:47:47.716724Z", 2570 | "shell.execute_reply": "2025-08-12T19:47:47.716419Z" 2571 | } 2572 | }, 2573 | "outputs": [], 2574 | "source": [ 2575 | "conc = []\n", 2576 | "for i in asc.columns:\n", 2577 | " if i.startswith('CONCN') | i.startswith('CONCP') | i.startswith('CONCD') | i.startswith('CONC1DC'):\n", 2578 | " conc.append(i)\n", 2579 | "try:\n", 2580 | " conc.remove('CONCN')\n", 2581 | "except:\n", 2582 | " pass\n", 2583 | "if len(conc) > 0:\n", 2584 | " display(asc[conc].describe())" 2585 | ] 2586 | }, 2587 | { 2588 | "cell_type": "code", 2589 | "execution_count": null, 2590 | "id": "fcb7898e-3cc9-4357-8d1a-1771e7e9ca07", 2591 | "metadata": { 2592 | "execution": { 2593 | "iopub.execute_input": "2025-08-12T19:47:47.717951Z", 2594 | "iopub.status.busy": "2025-08-12T19:47:47.717873Z", 2595 | "iopub.status.idle": "2025-08-12T19:47:47.724777Z", 2596 | "shell.execute_reply": "2025-08-12T19:47:47.724557Z" 2597 | } 2598 | }, 2599 | "outputs": [], 2600 | "source": [ 2601 | "try:\n", 2602 | " colors = itertools.cycle(Category10[8])\n", 2603 | " plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"CN Concentrations\") \n", 2604 | " plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 2605 | " plot.add_layout(Title(text=\"CONCy [cm-3]\", align=\"center\"), \"left\")\n", 2606 | " for i in asc[conc].columns:\n", 2607 | " try:\n", 2608 | " plot.line(dtime, asc[conc][i], color=next(colors), legend_label=i)\n", 2609 | " format_ticks(plot)\n", 2610 | " except:\n", 2611 | " pass\n", 2612 | " try:\n", 2613 | " format_legend_lines(plot)\n", 2614 | " except:\n", 2615 | " pass\n", 2616 | " try:\n", 2617 | " plot.legend.location = \"top_left\"\n", 2618 | " show(plot)\n", 2619 | " except:\n", 2620 | " pass \n", 2621 | "\n", 2622 | "except:\n", 2623 | " pass\n", 2624 | "# Remove: CONCN not in CGWAVES\n", 2625 | "# try:\n", 2626 | "# colors = itertools.cycle(Category10[8])\n", 2627 | "# plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"CONCN\") \n", 2628 | "# plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 2629 | "# plot.add_layout(Title(text=\"CONCN [cm-3]\", align=\"center\"), \"left\")\n", 2630 | "\n", 2631 | " # try:\n", 2632 | " # plot.line(dtime, asc['CONCN'], color=next(colors), legend_label='CONCN')\n", 2633 | " # except Exception as e:\n", 2634 | " # print(f\"Error encountered: {e}\")\n", 2635 | "# try:\n", 2636 | "# plot.line(dtime, asc['CONCU'], color=next(colors), legend_label='CONCU')\n", 2637 | "# except:\n", 2638 | "# pass\n", 2639 | "# try:\n", 2640 | "# plot.legend.location = \"top_left\"\n", 2641 | "# except:\n", 2642 | "# pass \n", 2643 | "# format_ticks(plot)\n", 2644 | "# show(plot)\n", 2645 | "# except:\n", 2646 | "# pass" 2647 | ] 2648 | }, 2649 | { 2650 | "cell_type": "markdown", 2651 | "id": "ba4fc22e-834d-46ca-b60e-26a6d482f642", 2652 | "metadata": {}, 2653 | "source": [ 2654 | "## Liquid Water " 2655 | ] 2656 | }, 2657 | { 2658 | "cell_type": "code", 2659 | "execution_count": null, 2660 | "id": "f43b6e5b-0509-4adb-93bb-f4c3037292e6", 2661 | "metadata": { 2662 | "execution": { 2663 | "iopub.execute_input": "2025-08-12T19:47:47.725982Z", 2664 | "iopub.status.busy": "2025-08-12T19:47:47.725916Z", 2665 | "iopub.status.idle": "2025-08-12T19:47:47.739446Z", 2666 | "shell.execute_reply": "2025-08-12T19:47:47.739179Z" 2667 | } 2668 | }, 2669 | "outputs": [], 2670 | "source": [ 2671 | "liquid_water = []\n", 2672 | "for i in asc.columns:\n", 2673 | " if i.startswith('PLWC') | i.startswith('RICE'):\n", 2674 | " liquid_water.append(i)\n", 2675 | "if len(liquid_water) > 0:\n", 2676 | " display(asc[liquid_water].describe())" 2677 | ] 2678 | }, 2679 | { 2680 | "cell_type": "code", 2681 | "execution_count": null, 2682 | "id": "92c61822-6328-4b79-8b2c-5f5947114c74", 2683 | "metadata": { 2684 | "execution": { 2685 | "iopub.execute_input": "2025-08-12T19:47:47.740645Z", 2686 | "iopub.status.busy": "2025-08-12T19:47:47.740569Z", 2687 | "iopub.status.idle": "2025-08-12T19:47:47.764322Z", 2688 | "shell.execute_reply": "2025-08-12T19:47:47.764037Z" 2689 | } 2690 | }, 2691 | "outputs": [], 2692 | "source": [ 2693 | "timeseries_plot(\"Liquid Water\", \"PLWCy []\", liquid_water)" 2694 | ] 2695 | }, 2696 | { 2697 | "cell_type": "markdown", 2698 | "id": "ab875dd5-f5a4-4298-87ee-a14a2d6e3978", 2699 | "metadata": {}, 2700 | "source": [ 2701 | "## Skew-T Plot " 2702 | ] 2703 | }, 2704 | { 2705 | "cell_type": "code", 2706 | "execution_count": null, 2707 | "id": "5fa0a4b1-162b-41da-81af-5aca3b802066", 2708 | "metadata": { 2709 | "execution": { 2710 | "iopub.execute_input": "2025-08-12T19:47:47.765677Z", 2711 | "iopub.status.busy": "2025-08-12T19:47:47.765599Z", 2712 | "iopub.status.idle": "2025-08-12T19:47:47.782818Z", 2713 | "shell.execute_reply": "2025-08-12T19:47:47.782551Z" 2714 | } 2715 | }, 2716 | "outputs": [], 2717 | "source": [ 2718 | "fig = plt.figure(figsize=(11, 13))\n", 2719 | "p = asc['PSXC']\n", 2720 | "T = asc['ATX']\n", 2721 | "try:\n", 2722 | " Td = asc['DP_DPL']\n", 2723 | "except:\n", 2724 | " Td = asc['DP_DPT']\n", 2725 | "skew = SkewT(fig, rotation=45)\n", 2726 | "\n", 2727 | "# Plot the data using normal plotting functions, in this case using\n", 2728 | "# log scaling in Y, as dictated by the typical meteorological plot\n", 2729 | "try:\n", 2730 | " skew.plot(p, T, 'r', label='T')\n", 2731 | " skew.plot(p, Td, 'g', label='Td')\n", 2732 | " # Set some better labels than the default\n", 2733 | "except:\n", 2734 | " pass\n", 2735 | "\n", 2736 | "try:\n", 2737 | " skew.ax.set_xlabel('Temperature or Dew Point [\\N{DEGREE CELSIUS}]')\n", 2738 | " skew.ax.set_ylabel('Pressure [hPa]')\n", 2739 | "except:\n", 2740 | " pass\n", 2741 | "# Add the relevant special lines\n", 2742 | "try:\n", 2743 | " skew.plot_dry_adiabats()\n", 2744 | " skew.plot_moist_adiabats()\n", 2745 | " skew.plot_mixing_lines()\n", 2746 | " skew.ax.set_ylim(1000, 100)\n", 2747 | " skew.ax.set_xlim(-60, 30)\n", 2748 | " skew.ax.legend()\n", 2749 | " plt.show()\n", 2750 | "except:\n", 2751 | " pass" 2752 | ] 2753 | }, 2754 | { 2755 | "cell_type": "markdown", 2756 | "id": "218fda86-521d-4450-beab-b4201a8f38c8", 2757 | "metadata": {}, 2758 | "source": [ 2759 | "## Potential Temperature " 2760 | ] 2761 | }, 2762 | { 2763 | "cell_type": "code", 2764 | "execution_count": null, 2765 | "id": "43aa7826-1c85-4d7a-99fb-a0108c0241d6", 2766 | "metadata": { 2767 | "execution": { 2768 | "iopub.execute_input": "2025-08-12T19:47:47.784017Z", 2769 | "iopub.status.busy": "2025-08-12T19:47:47.783947Z", 2770 | "iopub.status.idle": "2025-08-12T19:47:47.797245Z", 2771 | "shell.execute_reply": "2025-08-12T19:47:47.796984Z" 2772 | } 2773 | }, 2774 | "outputs": [], 2775 | "source": [ 2776 | "theta = []\n", 2777 | "for i in asc.columns:\n", 2778 | " if i.startswith('THETA'):\n", 2779 | " theta.append(i)\n", 2780 | "\n", 2781 | "asc[theta].describe()" 2782 | ] 2783 | }, 2784 | { 2785 | "cell_type": "code", 2786 | "execution_count": null, 2787 | "id": "b95bc827-a497-4abd-b639-d6baafbc0abd", 2788 | "metadata": { 2789 | "execution": { 2790 | "iopub.execute_input": "2025-08-12T19:47:47.798484Z", 2791 | "iopub.status.busy": "2025-08-12T19:47:47.798407Z", 2792 | "iopub.status.idle": "2025-08-12T19:47:47.826388Z", 2793 | "shell.execute_reply": "2025-08-12T19:47:47.826109Z" 2794 | } 2795 | }, 2796 | "outputs": [], 2797 | "source": [ 2798 | "timeseries_plot(\"Potential Temperature\", \"THETA [K]\", theta)\n", 2799 | "\n", 2800 | "colors = itertools.cycle(Category10[8])\n", 2801 | "plot = figure(sizing_mode=\"stretch_width\", height=800, title=\"\") \n", 2802 | "plot.add_layout(Title(text=\"Potential Temperature [K]\", align=\"center\"), \"below\")\n", 2803 | "plot.add_layout(Title(text=\"Pressure [hPa]\", align=\"center\"), \"left\")\n", 2804 | "\n", 2805 | "for i in asc[theta].columns:\n", 2806 | " try:\n", 2807 | " plot.line(asc[theta][i], asc['PSXC'], color=next(colors), legend_label=i)\n", 2808 | " except Exception as e:\n", 2809 | " print(e)\n", 2810 | "try:\n", 2811 | " plot.legend.location = \"top_left\"\n", 2812 | "except Exception as e:\n", 2813 | " print(e) \n", 2814 | "try:\n", 2815 | " format_legend_lines(plot)\n", 2816 | "except:\n", 2817 | " pass\n", 2818 | "plot.y_range.flipped = True\n", 2819 | "show(plot)" 2820 | ] 2821 | }, 2822 | { 2823 | "cell_type": "markdown", 2824 | "id": "c3f39123-8a75-4334-bee8-5ab69c035c0b", 2825 | "metadata": { 2826 | "execution": { 2827 | "iopub.execute_input": "2024-03-14T19:01:55.082543Z", 2828 | "iopub.status.busy": "2024-03-14T19:01:55.082429Z", 2829 | "iopub.status.idle": "2024-03-14T19:01:55.084054Z", 2830 | "shell.execute_reply": "2024-03-14T19:01:55.083790Z" 2831 | } 2832 | }, 2833 | "source": [ 2834 | "## Radiation " 2835 | ] 2836 | }, 2837 | { 2838 | "cell_type": "code", 2839 | "execution_count": null, 2840 | "id": "b843e7ac", 2841 | "metadata": { 2842 | "execution": { 2843 | "iopub.execute_input": "2025-08-12T19:47:47.827756Z", 2844 | "iopub.status.busy": "2025-08-12T19:47:47.827677Z", 2845 | "iopub.status.idle": "2025-08-12T19:47:47.832847Z", 2846 | "shell.execute_reply": "2025-08-12T19:47:47.832558Z" 2847 | } 2848 | }, 2849 | "outputs": [], 2850 | "source": [ 2851 | "def create_radiation_plots(dtime, asc):\n", 2852 | " \"\"\"\n", 2853 | " Create radiation plots with proper error handling and renderer checking.\n", 2854 | " Avoids the \"missing renderers\" warning by only showing plots with data.\n", 2855 | " \"\"\"\n", 2856 | " # Define configuration for all plots\n", 2857 | " plot_configs = [\n", 2858 | " {\n", 2859 | " \"title\": \"Pyrgeometer Raw Visible Irradiance\",\n", 2860 | " \"y_label\": \"Raw Visible Irradiance [W m-2]\",\n", 2861 | " \"variables\": [\n", 2862 | " {\"name\": \"VIST\", \"label\": \"Top\"},\n", 2863 | " {\"name\": \"VISB\", \"label\": \"Bottom\"}\n", 2864 | " ]\n", 2865 | " },\n", 2866 | " {\n", 2867 | " \"title\": \"Corrected Infrared Irradiance\",\n", 2868 | " \"y_label\": \"Infrared Irradiance [W m-2]\",\n", 2869 | " \"variables\": [\n", 2870 | " {\"name\": \"IRBC\", \"label\": \"Bottom\"},\n", 2871 | " {\"name\": \"IRTC\", \"label\": \"Top\"}\n", 2872 | " ]\n", 2873 | " },\n", 2874 | " {\n", 2875 | " \"title\": \"Radiometric Surface Temperature\",\n", 2876 | " \"y_label\": \"Surface Temperature \\N{DEGREE CELSIUS}\",\n", 2877 | " \"variables\": [\n", 2878 | " {\"name\": \"RSTB\", \"label\": \"RSTB\"},\n", 2879 | " {\"name\": \"RSTB1\", \"label\": \"RSTB1\"}\n", 2880 | " ]\n", 2881 | " },\n", 2882 | " {\n", 2883 | " \"title\": \"Radiometric Sky/Cloud-Base Temperature\",\n", 2884 | " \"y_label\": \"Sky/Cloud-Base Temperature \\N{DEGREE CELSIUS}\",\n", 2885 | " \"variables\": [\n", 2886 | " {\"name\": \"RSTT\", \"label\": \"RSTT\"}\n", 2887 | " ]\n", 2888 | " },\n", 2889 | " {\n", 2890 | " \"title\": \"Stabilized Platform Pitch Angle\",\n", 2891 | " \"y_label\": \"Pitch [deg]\",\n", 2892 | " \"variables\": [\n", 2893 | " {\"name\": \"PITCHA_SPT\", \"label\": \"PITCHA_SPT\"},\n", 2894 | " {\"name\": \"PITCHA_SPB\", \"label\": \"PITCHA_SPB\"},\n", 2895 | " {\"name\": \"PITCH\", \"label\": \"PITCH\"}\n", 2896 | " ]\n", 2897 | " },\n", 2898 | " {\n", 2899 | " \"title\": \"Stabilized Platform Roll Angle\",\n", 2900 | " \"y_label\": \"Roll [deg]\",\n", 2901 | " \"variables\": [\n", 2902 | " {\"name\": \"ROLLA_SPT\", \"label\": \"ROLLA_SPT\"},\n", 2903 | " {\"name\": \"ROLLA_SPB\", \"label\": \"ROLLA_SPB\"},\n", 2904 | " {\"name\": \"ROLL\", \"label\": \"ROLL\"}\n", 2905 | " ]\n", 2906 | " }\n", 2907 | " ]\n", 2908 | " \n", 2909 | " # Process each plot configuration\n", 2910 | " for config in plot_configs:\n", 2911 | " try:\n", 2912 | " # Create new figure\n", 2913 | " colors = itertools.cycle(Category10[8])\n", 2914 | " plot = figure(sizing_mode=\"stretch_width\", height=400, title=config[\"title\"])\n", 2915 | " plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 2916 | " plot.add_layout(Title(text=config[\"y_label\"], align=\"center\"), \"left\")\n", 2917 | " \n", 2918 | " # Track if we successfully add any renderers\n", 2919 | " renderers_added = False\n", 2920 | " \n", 2921 | " # Try to add each variable\n", 2922 | " for var in config[\"variables\"]:\n", 2923 | " try:\n", 2924 | " # Check if the variable exists and has valid data\n", 2925 | " if var[\"name\"] in asc and not asc[var[\"name\"]].isna().all():\n", 2926 | " plot.line(dtime, asc[var[\"name\"]], \n", 2927 | " color=next(colors), legend_label=var[\"label\"])\n", 2928 | " renderers_added = True\n", 2929 | " except Exception as e:\n", 2930 | " print(f\"Error plotting {var['name']}: {e}\")\n", 2931 | " \n", 2932 | " # Only format and show the plot if we have at least one renderer\n", 2933 | " if renderers_added:\n", 2934 | " try:\n", 2935 | " format_legend_lines(plot)\n", 2936 | " except:\n", 2937 | " pass\n", 2938 | " try:\n", 2939 | " plot.legend.location = \"top_left\"\n", 2940 | " format_ticks(plot)\n", 2941 | " show(plot)\n", 2942 | " except Exception as e:\n", 2943 | " print(f\"Error finalizing plot {config['title']}: {e}\")\n", 2944 | " else:\n", 2945 | " print(f\"No data available for {config['title']} - skipping plot\")\n", 2946 | " \n", 2947 | " except Exception as e:\n", 2948 | " print(f\"Failed to create plot {config['title']}: {e}\")\n", 2949 | " \n", 2950 | "try:\n", 2951 | " create_radiation_plots(dtime, asc)\n", 2952 | "except Exception as e:\n", 2953 | " print(f\"Error in radiation plots: {e}\")" 2954 | ] 2955 | }, 2956 | { 2957 | "cell_type": "markdown", 2958 | "id": "55bd40b3-bb3c-4534-9cce-fe389e4ee505", 2959 | "metadata": {}, 2960 | "source": [ 2961 | "## SDI " 2962 | ] 2963 | }, 2964 | { 2965 | "cell_type": "code", 2966 | "execution_count": null, 2967 | "id": "c338d119-f0f8-4c8b-9b77-53229a6e4956", 2968 | "metadata": { 2969 | "execution": { 2970 | "iopub.execute_input": "2025-08-12T19:47:47.834087Z", 2971 | "iopub.status.busy": "2025-08-12T19:47:47.834012Z", 2972 | "iopub.status.idle": "2025-08-12T19:47:47.847496Z", 2973 | "shell.execute_reply": "2025-08-12T19:47:47.847238Z" 2974 | } 2975 | }, 2976 | "outputs": [], 2977 | "source": [ 2978 | "sdi = []\n", 2979 | "for i in asc.columns:\n", 2980 | " if i.startswith('MFLOW_'):\n", 2981 | " sdi.append(i)\n", 2982 | "for i in asc.columns:\n", 2983 | " if i.startswith('SETPT_'):\n", 2984 | " sdi.append(i)\n", 2985 | "if len(sdi) > 0:\n", 2986 | " display(asc[sdi].describe())" 2987 | ] 2988 | }, 2989 | { 2990 | "cell_type": "code", 2991 | "execution_count": null, 2992 | "id": "de9a57b3-5ec5-4291-b0d3-825b879103c1", 2993 | "metadata": { 2994 | "execution": { 2995 | "iopub.execute_input": "2025-08-12T19:47:47.848740Z", 2996 | "iopub.status.busy": "2025-08-12T19:47:47.848671Z", 2997 | "iopub.status.idle": "2025-08-12T19:47:47.850212Z", 2998 | "shell.execute_reply": "2025-08-12T19:47:47.849947Z" 2999 | } 3000 | }, 3001 | "outputs": [], 3002 | "source": [ 3003 | "if sdi:\n", 3004 | " timeseries_plot(\"SDI\", \"[slpm]\", sdi)" 3005 | ] 3006 | }, 3007 | { 3008 | "cell_type": "markdown", 3009 | "id": "d035b6bc-451d-4e84-872e-ee7eceba7a3c", 3010 | "metadata": {}, 3011 | "source": [ 3012 | "## Chemistry " 3013 | ] 3014 | }, 3015 | { 3016 | "cell_type": "code", 3017 | "execution_count": null, 3018 | "id": "4d0fce31-40f3-47c0-8229-5c9d6ffec4f1", 3019 | "metadata": { 3020 | "execution": { 3021 | "iopub.execute_input": "2025-08-12T19:47:47.851445Z", 3022 | "iopub.status.busy": "2025-08-12T19:47:47.851373Z", 3023 | "iopub.status.idle": "2025-08-12T19:47:47.889232Z", 3024 | "shell.execute_reply": "2025-08-12T19:47:47.888993Z" 3025 | } 3026 | }, 3027 | "outputs": [], 3028 | "source": [ 3029 | "try:\n", 3030 | " if picarroGG_on == True:\n", 3031 | " colors = itertools.cycle(Category10[8])\n", 3032 | " plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"Carbon Dioxide Mole Fraction\") \n", 3033 | " plot.add_layout(Title(text=\"Carbon Dioxide [umol/mol]\", align=\"center\"), \"left\")\n", 3034 | " plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 3035 | "\n", 3036 | " try:\n", 3037 | " plot.line(dtime, asc['CO2C_PIC2401'], color=next(colors), legend_label='CO2 DMF PIC2401')\n", 3038 | " except Exception as e:\n", 3039 | " pass\n", 3040 | " try:\n", 3041 | " plot.line(dtime, asc['CO2_PIC2401'], color=next(colors), legend_label='CO2 Raw PIC2401')\n", 3042 | " format_ticks(plot)\n", 3043 | " except Exception as e:\n", 3044 | " pass\n", 3045 | " try:\n", 3046 | " plot.legend.location = \"top_left\"\n", 3047 | " except Exception as e:\n", 3048 | " pass \n", 3049 | " show(plot)\n", 3050 | "\n", 3051 | " colors = itertools.cycle(Category10[8])\n", 3052 | " plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"Methane Mole Fraction\") \n", 3053 | " plot.add_layout(Title(text=\"Methane [nmol/mol]\", align=\"center\"), \"left\")\n", 3054 | " plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 3055 | "\n", 3056 | " try:\n", 3057 | " plot.line(dtime, asc['CH4C_PIC2401'], color=next(colors), legend_label='CH4 DMF PIC2401')\n", 3058 | " except Exception as e:\n", 3059 | " pass\n", 3060 | " try:\n", 3061 | " plot.line(dtime, asc['CH4_PIC2401'], color=next(colors), legend_label='CH4 Raw PIC2401')\n", 3062 | " format_ticks(plot)\n", 3063 | " except Exception as e:\n", 3064 | " pass\n", 3065 | " try:\n", 3066 | " plot.legend.location = \"top_left\"\n", 3067 | " except Exception as e:\n", 3068 | " pass \n", 3069 | " show(plot)\n", 3070 | " else:\n", 3071 | " pass\n", 3072 | " \n", 3073 | " if ari_and_picarroGG_on == True:\n", 3074 | " colors = itertools.cycle(Category10[8])\n", 3075 | " plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"Carbon Monoxide (ARI scaled / 1000)\") \n", 3076 | " plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 3077 | " plot.add_layout(Title(text=\"Carbon Monoxide MR [ppmv]\", align=\"center\"), \"left\")\n", 3078 | " try:\n", 3079 | " plot.line(dtime, asc['CO_PIC'], color=next(colors), legend_label='CO_PIC2401')\n", 3080 | " except Exception as e:\n", 3081 | " pass\n", 3082 | " try:\n", 3083 | " plot.line(dtime, asc['CO_QCL']/1000, color=next(colors), legend_label='CO_QCL')\n", 3084 | " format_ticks(plot)\n", 3085 | " except Exception as e:\n", 3086 | " pass\n", 3087 | " try:\n", 3088 | " format_legend_lines(plot)\n", 3089 | " except:\n", 3090 | " pass\n", 3091 | " try:\n", 3092 | " plot.legend.location = \"top_left\"\n", 3093 | " except:\n", 3094 | " pass \n", 3095 | " show(plot)\n", 3096 | " else:\n", 3097 | " pass\n", 3098 | "\n", 3099 | " if ari_on == True:\n", 3100 | " colors = itertools.cycle(Category10[8])\n", 3101 | " plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"Carbon Monoxide\") \n", 3102 | " plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 3103 | " plot.add_layout(Title(text=\"Carbon Monoxide MR [ppbv]\", align=\"center\"), \"left\")\n", 3104 | " try:\n", 3105 | " plot.line(dtime, asc['CO_QCL'], color=next(colors), legend_label='CO_QCL')\n", 3106 | " format_ticks(plot)\n", 3107 | " except Exception as e:\n", 3108 | " pass\n", 3109 | " try:\n", 3110 | " plot.legend.location = \"top_left\"\n", 3111 | " except:\n", 3112 | " pass \n", 3113 | " try:\n", 3114 | " format_legend_lines(plot)\n", 3115 | " except:\n", 3116 | " pass\n", 3117 | " show(plot)\n", 3118 | " else:\n", 3119 | " pass\n", 3120 | "\n", 3121 | " if ozone_on == True:\n", 3122 | " colors = itertools.cycle(Category10[8])\n", 3123 | " plot = figure(sizing_mode=\"stretch_width\", height=400, title=\"Fast Response Ozone Mixing Ratio\") \n", 3124 | " plot.add_layout(Title(text=\"Time [UTC]\", align=\"center\"), \"below\")\n", 3125 | " plot.add_layout(Title(text=\"Ozone [ppbv]\", align=\"center\"), \"left\")\n", 3126 | " try:\n", 3127 | " plot.line(dtime, asc['FO3_RAF'], color=next(colors), legend_label='Ozone MR, Raw')\n", 3128 | " except Exception as e:\n", 3129 | " pass\n", 3130 | " try:\n", 3131 | " plot.line(dtime, asc['FO3C_RAF']/1000, color=next(colors), legend_label='Ozone MR, WV Corrected')\n", 3132 | " format_ticks(plot)\n", 3133 | " except Exception as e:\n", 3134 | " pass\n", 3135 | " try:\n", 3136 | " plot.legend.location = \"top_left\"\n", 3137 | " except:\n", 3138 | " pass \n", 3139 | " try:\n", 3140 | " format_legend_lines(plot)\n", 3141 | " except:\n", 3142 | " pass\n", 3143 | " show(plot)\n", 3144 | " else:\n", 3145 | " pass\n", 3146 | "\n", 3147 | "except Exception as e:\n", 3148 | " print(e)" 3149 | ] 3150 | }, 3151 | { 3152 | "cell_type": "markdown", 3153 | "id": "518a25a5-aad0-413b-8cdc-eb4e66aa343e", 3154 | "metadata": {}, 3155 | "source": [ 3156 | "## Aerosol and Cloud Probe Histogram Data " 3157 | ] 3158 | }, 3159 | { 3160 | "cell_type": "code", 3161 | "execution_count": null, 3162 | "id": "e89ddd5d-05f7-4faf-bdda-652353dadfc0", 3163 | "metadata": { 3164 | "execution": { 3165 | "iopub.execute_input": "2025-08-12T19:47:47.890526Z", 3166 | "iopub.status.busy": "2025-08-12T19:47:47.890448Z", 3167 | "iopub.status.idle": "2025-08-12T19:47:48.053454Z", 3168 | "shell.execute_reply": "2025-08-12T19:47:48.053094Z" 3169 | } 3170 | }, 3171 | "outputs": [], 3172 | "source": [ 3173 | "# create lists of the histogram variables\n", 3174 | "# \n", 3175 | "try:\n", 3176 | " histovar_list = list(histo_asc.columns.levels[0])\n", 3177 | " histovar_list = [var for var in histovar_list if not var.endswith('VXL')]\n", 3178 | " histobin_list = []\n", 3179 | "except:\n", 3180 | " pass\n", 3181 | "\n", 3182 | "# loop over the length of the histogram variable list\n", 3183 | "try:\n", 3184 | " for i in range(len(histovar_list)):\n", 3185 | " histobin_list.append(max(list(histo_asc[histovar_list[i]]))+1)\n", 3186 | "except:\n", 3187 | " pass\n", 3188 | "\n", 3189 | "# create list of histogram units\n", 3190 | "try:\n", 3191 | " histo_units = []\n", 3192 | " for i in histovar_list:\n", 3193 | " histo_units.append(nc.variables[i].getncattr('units'))\n", 3194 | "except:\n", 3195 | " pass\n", 3196 | "import xarray as xr\n", 3197 | "import hvplot.xarray\n", 3198 | "df = xr.open_dataset(filepath+input_file) #open the dataset as an xarray\n", 3199 | "ds =df.where(df.GGSPD >60, drop = True) #filter the data to not include time on the ground\n", 3200 | "x_width = int(1200)\n", 3201 | "y_height = int(400)" 3202 | ] 3203 | }, 3204 | { 3205 | "cell_type": "code", 3206 | "execution_count": null, 3207 | "id": "ab051b2b", 3208 | "metadata": { 3209 | "execution": { 3210 | "iopub.execute_input": "2025-08-12T19:47:48.054969Z", 3211 | "iopub.status.busy": "2025-08-12T19:47:48.054874Z", 3212 | "iopub.status.idle": "2025-08-12T19:47:48.058625Z", 3213 | "shell.execute_reply": "2025-08-12T19:47:48.058356Z" 3214 | } 3215 | }, 3216 | "outputs": [], 3217 | "source": [ 3218 | "def plot_concScatter(ds, conc_name):\n", 3219 | " # Replace the scatter plot cell with this corrected version:\n", 3220 | "\n", 3221 | " x = ds['Time'].values\n", 3222 | " y = ds.GGALT.values\n", 3223 | " color_values = ds[conc_name].values\n", 3224 | "\n", 3225 | " # Calculate 99th percentile for color scale\n", 3226 | " color_min = color_values.min()\n", 3227 | " color_99th = np.percentile(color_values, 99)\n", 3228 | "\n", 3229 | " # Create the scatter plot with proper color mapping\n", 3230 | " from bokeh.transform import linear_cmap\n", 3231 | " from bokeh.palettes import Plasma256\n", 3232 | " from bokeh.models import ColumnDataSource, ColorBar\n", 3233 | "\n", 3234 | " # Create color mapper\n", 3235 | " color_mapper = linear_cmap(field_name='color', palette=Plasma256, \n", 3236 | " low=color_min, high=color_99th)\n", 3237 | "\n", 3238 | " # Create data source\n", 3239 | " source = ColumnDataSource(data=dict(\n", 3240 | " x=x,\n", 3241 | " y=y,\n", 3242 | " color=color_values\n", 3243 | " ))\n", 3244 | "\n", 3245 | " # Create the plot\n", 3246 | " plot = figure(sizing_mode=\"stretch_width\", height=400, title=ds[conc_name].long_name) \n", 3247 | " plot.add_layout(Title(text=f\"{conc_name} [{ds[conc_name].units}]\", align=\"center\", standoff=10), \"left\")\n", 3248 | "\n", 3249 | " # Add scatter plot with color mapping\n", 3250 | " scatter = plot.scatter('x', 'y', color=color_mapper, size=8, source=source)\n", 3251 | "\n", 3252 | " # Add color bar\n", 3253 | " color_bar = ColorBar(color_mapper=color_mapper['transform'],\n", 3254 | " label_standoff=12, location=(0,0))\n", 3255 | " #Add color bar label to the left\n", 3256 | " plot.add_layout(color_bar, 'left')\n", 3257 | "\n", 3258 | " # Set axis labels\n", 3259 | " plot.yaxis.axis_label = \"Altitude [m]\"\n", 3260 | " plot.xaxis.axis_label = \"Time [UTC]\"\n", 3261 | " plot.xaxis.formatter = DatetimeTickFormatter(\n", 3262 | " hours=\"%H:%M:%S\",\n", 3263 | " days=\"%H:%M:%S\", \n", 3264 | " months=\"%H:%M:%S\",\n", 3265 | " years=\"%H:%M:%S\"\n", 3266 | " )\n", 3267 | " show(plot)" 3268 | ] 3269 | }, 3270 | { 3271 | "cell_type": "code", 3272 | "execution_count": null, 3273 | "id": "fee69f3d", 3274 | "metadata": { 3275 | "execution": { 3276 | "iopub.execute_input": "2025-08-12T19:47:48.059785Z", 3277 | "iopub.status.busy": "2025-08-12T19:47:48.059712Z", 3278 | "iopub.status.idle": "2025-08-12T19:47:48.063414Z", 3279 | "shell.execute_reply": "2025-08-12T19:47:48.063172Z" 3280 | } 3281 | }, 3282 | "outputs": [], 3283 | "source": [ 3284 | "# Replace the flight track plotting section with:\n", 3285 | "def plot_concMap(ds,conc_name):\n", 3286 | " # get latitude and longitude from asc dataframe\n", 3287 | " latitude = ds.GGLAT.values\n", 3288 | " longitude = ds.GGLON.values\n", 3289 | " color_values = ds[conc_name].values\n", 3290 | "\n", 3291 | "\n", 3292 | " # update to mercator projection\n", 3293 | " k = 6378137\n", 3294 | " longitude = longitude * (k * np.pi/180.0)\n", 3295 | " latitude = np.log(np.tan((90 + latitude) * np.pi/360.0)) * k\n", 3296 | "\n", 3297 | " # Calculate color scale\n", 3298 | " color_min = color_values.min()\n", 3299 | " color_99th = np.percentile(color_values, 99)\n", 3300 | "\n", 3301 | " # Create the plot layout and add axis labels\n", 3302 | " try:\n", 3303 | " from bokeh.transform import linear_cmap\n", 3304 | " from bokeh.palettes import Plasma256\n", 3305 | " from bokeh.models import ColumnDataSource, ColorBar\n", 3306 | " # Create color mapper\n", 3307 | " color_mapper = linear_cmap(field_name='color', palette=Plasma256, \n", 3308 | " low=color_min, high=color_99th)\n", 3309 | " # Create data source\n", 3310 | " source = ColumnDataSource(data=dict(\n", 3311 | " x=longitude,\n", 3312 | " y=latitude,\n", 3313 | " color=color_values\n", 3314 | " ))\n", 3315 | " plot = figure(width=800, height=800, title=project + ' ' + flight.upper() + \" Flight Track with \" +conc_name, \n", 3316 | " x_axis_type=\"mercator\", y_axis_type=\"mercator\") \n", 3317 | " plot.add_layout(Title(text=ds[conc_name].long_name + \" \" + ds[conc_name].units, align=\"center\"), \"left\")\n", 3318 | " plot.yaxis.axis_label = \"Latitude [Degrees]\"\n", 3319 | " plot.xaxis.axis_label = \"Longitude [Degrees]\"\n", 3320 | "\n", 3321 | " # Add the flight track with color mapping\n", 3322 | " scatter = plot.scatter('x', 'y', color=color_mapper, size=8, source=source)\n", 3323 | "\n", 3324 | " # Add color bar\n", 3325 | " color_bar = ColorBar(color_mapper=color_mapper['transform'],\n", 3326 | " label_standoff=12, location=(0,0))\n", 3327 | " plot.add_layout(color_bar, 'left')\n", 3328 | "\n", 3329 | " # Add basemap\n", 3330 | " plot.add_tile(\"Esri World Imagery\", retina=True)\n", 3331 | " show(plot)\n", 3332 | " \n", 3333 | " except Exception as e:\n", 3334 | " print(f\"Error creating colored flight track: {e}\")" 3335 | ] 3336 | }, 3337 | { 3338 | "cell_type": "code", 3339 | "execution_count": null, 3340 | "id": "7ef5342d", 3341 | "metadata": { 3342 | "execution": { 3343 | "iopub.execute_input": "2025-08-12T19:47:48.064639Z", 3344 | "iopub.status.busy": "2025-08-12T19:47:48.064563Z", 3345 | "iopub.status.idle": "2025-08-12T19:47:48.076963Z", 3346 | "shell.execute_reply": "2025-08-12T19:47:48.076677Z" 3347 | } 3348 | }, 3349 | "outputs": [], 3350 | "source": [ 3351 | "plot_concMap(ds,'CONCU_RO')\n", 3352 | "plot_concMap(ds,'CONCN')\n", 3353 | "plot_concMap(ds,'CONCP_RPI')" 3354 | ] 3355 | }, 3356 | { 3357 | "cell_type": "code", 3358 | "execution_count": null, 3359 | "id": "aa98a7a1", 3360 | "metadata": { 3361 | "execution": { 3362 | "iopub.execute_input": "2025-08-12T19:47:48.078222Z", 3363 | "iopub.status.busy": "2025-08-12T19:47:48.078139Z", 3364 | "iopub.status.idle": "2025-08-12T19:47:48.090605Z", 3365 | "shell.execute_reply": "2025-08-12T19:47:48.090362Z" 3366 | } 3367 | }, 3368 | "outputs": [], 3369 | "source": [ 3370 | "#Plot concentration line plots\n", 3371 | "plot_concScatter(ds, 'CONCU_RO')\n", 3372 | "plot_concScatter(ds, 'CONCN') \n", 3373 | "plot_concScatter(ds, 'CONCP_RPI') " 3374 | ] 3375 | }, 3376 | { 3377 | "cell_type": "code", 3378 | "execution_count": null, 3379 | "id": "9bcc85b0", 3380 | "metadata": { 3381 | "execution": { 3382 | "iopub.execute_input": "2025-08-12T19:47:48.091849Z", 3383 | "iopub.status.busy": "2025-08-12T19:47:48.091773Z", 3384 | "iopub.status.idle": "2025-08-12T19:47:48.103284Z", 3385 | "shell.execute_reply": "2025-08-12T19:47:48.103032Z" 3386 | } 3387 | }, 3388 | "outputs": [], 3389 | "source": [ 3390 | "#Plot Volume Line Plots\n", 3391 | "plot_concScatter(ds, 'PVOLP_RPI') # Example for PVOLP_RPI\n", 3392 | "plot_concScatter(ds, 'PVOLU_RO') # Example for PVOLP_RO" 3393 | ] 3394 | }, 3395 | { 3396 | "cell_type": "code", 3397 | "execution_count": null, 3398 | "id": "2fb43cd4", 3399 | "metadata": { 3400 | "execution": { 3401 | "iopub.execute_input": "2025-08-12T19:47:48.104565Z", 3402 | "iopub.status.busy": "2025-08-12T19:47:48.104488Z", 3403 | "iopub.status.idle": "2025-08-12T19:47:48.124322Z", 3404 | "shell.execute_reply": "2025-08-12T19:47:48.124043Z" 3405 | } 3406 | }, 3407 | "outputs": [], 3408 | "source": [ 3409 | "# Replace the histogram plotting cell with:\n", 3410 | "from matplotlib.colors import LogNorm\n", 3411 | "if static_histogram == True:\n", 3412 | " filtered_list = [item for item in histovar_list if item != \"I2DCA_RPC\"]\n", 3413 | " \n", 3414 | " # Create individual figures instead of subplots to avoid page break issues\n", 3415 | " for i, var in enumerate(filtered_list):\n", 3416 | " try:\n", 3417 | " if ds[var].isnull().all():\n", 3418 | " print(f\"Variable {var} contains only NaN values - skipping\")\n", 3419 | " continue\n", 3420 | " \n", 3421 | " # Check if the variable has any non-zero data\n", 3422 | " if (ds[var] == 0).all():\n", 3423 | " print(f\"Variable {var} contains only zero values - skipping\")\n", 3424 | " continue\n", 3425 | " # Create individual figure for each plot\n", 3426 | " fig, ax = plt.subplots(1, 1, figsize=(12, 4)) # Smaller individual figures\n", 3427 | " \n", 3428 | " dims = ds[var].squeeze().dims\n", 3429 | " vname = [item for item in dims if \"Time\" not in item][0]\n", 3430 | " max_z = ds[var].to_numpy().max()\n", 3431 | " min_y = 1 if ds[var][vname][0] == 0 else ds[var][vname][0]\n", 3432 | " max_y = ds[var][vname].to_numpy().max()\n", 3433 | " ylab = \"Bin [#]\" if min_y == 1 else \"Cell Size [um]\"\n", 3434 | " plot_ds = ds[var].squeeze().assign_coords({vname: ds[vname]})\n", 3435 | "\n", 3436 | " plot_ds.plot.imshow(x='Time', y=vname, ax=ax, vmin=0, vmax=max_z, \n", 3437 | " yscale='log', cmap='gist_ncar')\n", 3438 | "\n", 3439 | " # Set labels and title\n", 3440 | " ax.set_ylabel(ylab)\n", 3441 | " ax.set_ylim([min_y, max_y])\n", 3442 | " ax.set_xlabel('Time [UTC]')\n", 3443 | " ax.set_title(f\"{var} Units: {histo_units[i]}\")\n", 3444 | " \n", 3445 | " plt.tight_layout()\n", 3446 | " plt.show()\n", 3447 | " plt.close(fig) # Close figure to free memory\n", 3448 | " \n", 3449 | " except IndexError:\n", 3450 | " print(var + \" does not have vector dimension\")\n", 3451 | " except KeyError:\n", 3452 | " print(var + \" not in file\")" 3453 | ] 3454 | } 3455 | ], 3456 | "metadata": { 3457 | "kernelspec": { 3458 | "display_name": "qatools", 3459 | "language": "python", 3460 | "name": "python3" 3461 | }, 3462 | "language_info": { 3463 | "codemirror_mode": { 3464 | "name": "ipython", 3465 | "version": 3 3466 | }, 3467 | "file_extension": ".py", 3468 | "mimetype": "text/x-python", 3469 | "name": "python", 3470 | "nbconvert_exporter": "python", 3471 | "pygments_lexer": "ipython3", 3472 | "version": "3.11.12" 3473 | } 3474 | }, 3475 | "nbformat": 4, 3476 | "nbformat_minor": 5 3477 | } 3478 | --------------------------------------------------------------------------------