{ "cells": [ { "cell_type": "markdown", "id": "ecbc2f77", "metadata": {}, "source": [ "# Hinode-XRT: A Practical Guide to Data Extraction and Visualization" ] }, { "cell_type": "markdown", "id": "30cba719", "metadata": {}, "source": [ "This notebook is a guide to handle solar observation data specifically from the Hinode X-Ray Telescope (XRT). \n" ] }, { "cell_type": "markdown", "id": "9b9fc1e9", "metadata": {}, "source": [ "## Hinode Spacecraft Overview\n", "Hinode, launched on September 23, 2006, is a collaborative mission by Japan, the USA, Europe, and the UK to observe the Sun. It orbits in a sun-synchronous path, enabling continuous solar observations. The spacecraft features three instruments:\n", "- **Solar Optical Telescope (SOT)**\n", "- **Extreme Ultraviolet Imaging Spectrometer (EIS)**\n", "- **X-Ray Telescope (XRT)**: \n", "\n", "For further information, visit [NASA’s Hinode mission page](https://www.nasa.gov/mission_pages/hinode/index.html).\n", "\n", "## X-Ray Telescope (XRT)\n", "The XRT on Hinode specializes in capturing high-resolution images of the solar corona, crucial for studying dynamics such as solar flares and coronal mass ejections in regions with temperatures ranging from 1,000,000 to 10,000,000 Kelvin.\n", "\n", "For more details, visit the [XRT mission overview](http://xrt.cfa.harvard.edu/).\n", "\n", "\n" ] }, { "cell_type": "markdown", "id": "35e7392e", "metadata": {}, "source": [ "### Table of Contents\n", "1. [Importing Necessary Packages](#importing-packages)\n", "2. [Using Fido to Search and Download XRT Data](#using-fido-to-search-and-download-xrt-data)\n", "3. [Inspecting the Downloaded Data](#inspecting-the-downloaded-data)\n", "4. [Exploring Functions to Enhance Data Quality](#exploring-functions-to-enhance-data-quality)\n", " - 4.1 [Filtering FITS Files Through Selected Filter](#filtering-fits-files-through-selected-filter)\n", " - 4.2 [Filtering FITS Files by Exposure Time](#filtering-fits-files-by-exposure-time)\n", "5. [Navigating FITS Images](#Navigating-FITS-Images)\n", "6. [Creating a Movie from XRT FITS Data](#Creating-a-Movie-from-XRT-FITS-Data)\n" ] }, { "cell_type": "markdown", "id": "9b9ecd45", "metadata": {}, "source": [ "\n", "### Importing Necessary Packages" ] }, { "cell_type": "markdown", "id": "69829289", "metadata": {}, "source": [ "###### Updating Required Packages\n", "\n", "The following packages are necessary to work through this notebook. If you don't have them installed, or need to ensure they are updated ( `pip install --upgrade package` ), run the corresponding cell below. You can determine if you need to install or update between these packages based on the output from running the **cell bellow**.\n", "\n", "```bash\n", "# Update or install the SunPy package\n", "pip install sunpy\n", "\n", "# Update or install the Zeep package, used for SOAP-based client services\n", "pip install zeep\n", "\n", "# Update or install the DRMS package, for accessing solar data\n", "pip install drms\n", "\n", "# Update or install the lxml package, a powerful XML and HTML parsing library\n", "pip install lxml\n" ] }, { "cell_type": "code", "execution_count": null, "id": "ffec64eb", "metadata": {}, "outputs": [], "source": [ "# Standard library imports for file and temporary directory management\n", "import os\n", "import tempfile\n", "\n", "# Counting occurrences of unique elements\n", "# creating and managing images and videos\n", "import imageio\n", "import ipywidgets as widgets\n", "import matplotlib.pyplot as plt\n", "\n", "# SunPy library for solar physics data analysis\n", "import sunpy.map\n", "\n", "# Astropy units module for handling physical quantities\n", "from astropy import units as u\n", "\n", "# IPython and ipywidgets for interactive widgets in the notebook\n", "from IPython.display import (\n", " Video, # For embedding videos in the notebook\n", " clear_output,\n", " display,\n", ")\n", "\n", "# SunPy modules for querying and downloading solar data\n", "from sunpy.net import Fido\n", "from sunpy.net import attrs as a\n", "\n", "# #******************************************\n", "# #Command to ensure necessary libraries are installed (uncomment and run if needed)\n", "# !pip install sunpy matplotlib imageio ipywidgets\n", "# #******************************************" ] }, { "cell_type": "markdown", "id": "7531c65c", "metadata": {}, "source": [ "\n", "### Using Fido to Search and Download XRT Data" ] }, { "cell_type": "code", "execution_count": null, "id": "6eb53bd3", "metadata": {}, "outputs": [], "source": [ "# Define the time range of interest for solar observations\n", "time_range = a.Time(\"2022-09-23 10:36:00\", \"2022-09-23 17:55:38\") \n", "\n", "# Specify the instrument as 'xrt' to search for Hinode X-Ray Telescope data\n", "instrument = a.Instrument(\"xrt\")\n", "\n", "\n", "# This will return a catalog of available XRT data during the specified period\n", "xrt_downloaded_files = Fido.search(time_range, instrument)\n", "\n", "# Display the search results\n", "print(xrt_downloaded_files)" ] }, { "cell_type": "markdown", "id": "77ebe52d", "metadata": {}, "source": [ "#### Downloading Hinode XRT Data Using `Fido.fetch`\n", "\n", "Having identified the desired Hinode XRT data with `Fido.search`, we proceed to download the datasets using the `Fido.fetch` function. This function accepts the search results as its input and facilitates the data download to a designated directory on your local system." ] }, { "cell_type": "code", "execution_count": null, "id": "d091f2c3", "metadata": {}, "outputs": [], "source": [ "# Note: Depending on the amount of data and network speed, this process can take some time.\n", "# The `progress` parameter in `Fido.fetch` controls the display of the download progress bar.\n", "# Setting `progress=False` disables the progress bar, which can be useful for cleaner output,\n", "# especially when running this in a script or automated pipeline. By default, we keep it True\n", "# for this interactive session to monitor the download progress.\n", "\n", "# This will download the files to the default SunPy data directory or a specified path.\n", "# Replace `xrt_downloaded_files` with your search results variable if different.\n", "xrt_files_results = Fido.fetch(xrt_downloaded_files, progress=True)\n", "\n", "# If you wish to specify a different download directory, you can do so by adding the `path` parameter:\n", "# xrt_files_results = Fido.fetch(xrt_downloaded_files, path='/desired/download/directory/', progress=True)" ] }, { "cell_type": "markdown", "id": "adc8abe6", "metadata": {}, "source": [ "\n", "### Inspecting the Downloaded Data" ] }, { "cell_type": "code", "execution_count": null, "id": "28135ef7", "metadata": {}, "outputs": [], "source": [ "# total number of files downloaded using Fido\n", "num_files = len(xrt_files_results)\n", "print(f\"Number of files downloaded: {num_files}\")\n", "\n", "# few examples of file names to understand the naming convention\n", "print(\"\\nSample file names:\")\n", "for file in xrt_files_results[:5]:\n", " print(file)\n", "\n", "# Example of inspecting a single file from the downloaded dataset\n", "# Here, we choose a file arbitrarily (the 85th file) for inspection\n", "sample_file_index = 84 # Adjust the index as desired\n", "sample_data = sunpy.map.Map(xrt_files_results[sample_file_index])\n", "\n", "# Print information about the sample file\n", "# This includes metadata like observation time, instrument, and data dimensions\n", "print(f\"\\nSample data info for file number {sample_file_index + 1}:\")\n", "print(sample_data)\n", "\n", "# Visualize the data from the sample file\n", "# This step is crucial for a quick quality check and to familiarize with the data\n", "plt.figure(figsize=(10, 8))\n", "sample_data.peek() # 'peek' method generates a quick-look plot of the data\n", "plt.show()\n" ] }, { "cell_type": "markdown", "id": "38563ae9", "metadata": {}, "source": [ "\n", "### Exploring Functions to Enhance Data Quality\n", "\n", "\n", "\n", "#### Filtering FITS Files Through Selected Filter\n", "\n", "Identifying the type of data contained within Hinode XRT FITS files can be challenging, as the file titles do not provide sufficient information. To address this, we're using a developed function that leverages `sunpy.map` to read the header information of each FITS file. This process allows us to identify and count the unique filters present within the dataset. " ] }, { "cell_type": "code", "execution_count": null, "id": "796cf1a4", "metadata": {}, "outputs": [], "source": [ "def normalize_string(s):\n", " \"\"\"\n", " Normalize a string for comparison by converting to lowercase, replacing hyphens and underscores with spaces, and removing 'open'.\n", "\n", " Parameters:\n", " - s (str): The string to normalize.\n", "\n", " Returns:\n", " - str: The normalized string.\n", " \"\"\"\n", " return (\n", " s.lower()\n", " .replace(\"open\", \"\")\n", " .strip(\"- \")\n", " .replace(\"_\", \" \")\n", " .replace(\"-\", \" \")\n", " .strip()\n", " )\n", "\n", "\n", "## I have plans to update this function at a later time to allow users to collect FITS for more than one filter.\n", "\n", "\n", "def filter_fits_files_by_XRT_filter(fits_files):\n", " \"\"\"\n", " Filters FITS files by XRT filter criteria, accommodating flexible user input formats. It enhances readability and ensures valid input by normalizing filter names.\n", "\n", " Parameters:\n", " - fits_files (list): List of FITS file paths.\n", "\n", " Returns:\n", " - filtered_files (list): List of file paths that match the user-selected XRT channel filter.\n", "\n", " Raises:\n", " - ValueError: If the user input does not match any available filter.\n", " \"\"\"\n", " measurement_info = {}\n", "\n", " for file_path in fits_files:\n", " from sunpy.map import Map\n", "\n", " sunpy_map = Map(file_path)\n", " measurement = sunpy_map.measurement\n", " # Normalize measurement for consistent comparison\n", " normalized_measurement = normalize_string(measurement)\n", " if normalized_measurement in measurement_info:\n", " measurement_info[normalized_measurement] += 1\n", " else:\n", " measurement_info[normalized_measurement] = 1\n", "\n", " # Display available filters and their counts\n", " filters_output = \"\\n\".join(\n", " [f\"{key.title()}: {value} files\" for key, value in measurement_info.items()]\n", " )\n", " print(f\"The files have the following filters and counts:\\n{filters_output}\")\n", " filter_choice = input(\"Please select an XRT channel filter of interest: \")\n", " normalized_filter_choice = normalize_string(filter_choice)\n", "\n", " # Validate user input\n", " if normalized_filter_choice not in measurement_info:\n", " raise ValueError(\n", " \"Invalid filter choice. Please enter a valid XRT channel filter from the list provided.\"\n", " )\n", "\n", " filtered_files = []\n", " for file_path in fits_files:\n", " sunpy_map = Map(file_path)\n", " normalized_measurement = normalize_string(sunpy_map.measurement)\n", " if normalized_filter_choice in normalized_measurement:\n", " filtered_files.append(file_path)\n", "\n", " name_of_filter = filter_choice.title()\n", " print(f\"\\nFilter choice: {name_of_filter}\")\n", " print(\n", " f\"You have {len(filtered_files)} FITS files that match the '{name_of_filter}' filter.\"\n", " )\n", " print(\"Be sure to store this data as a new variable.\")\n", "\n", " return filtered_files" ] }, { "cell_type": "code", "execution_count": null, "id": "eae7f9cc", "metadata": {}, "outputs": [], "source": [ "# Assuming xrt_files_results is a list of FITS file paths\n", "# My new variable name is reflected base on the filter of interest\n", "xrt_Al_poly_Obs_FITs = filter_fits_files_by_XRT_filter(xrt_files_results)" ] }, { "cell_type": "markdown", "id": "e56e6f5a", "metadata": {}, "source": [ "\n", "### Filtering FITS Files by Exposure Time\n", "\n", "Different exposure times can significantly affect the quality and the type of data captured in each image. This section introduces a method to filter your dataset based on specific exposure time criteria, allowing for a more refined and targeted analysis.\n", "\n", "##### Key Features:\n", "- **Exact Match**: Retrieve images with a specific exposure time by entering the exact value in seconds.\n", "- **Range Selection**: Specify a range of exposure times to include images that fall within this interval.\n", "- **Above or Below a Threshold**: Filter images based on whether their exposure times are above or below a specified value." ] }, { "cell_type": "code", "execution_count": null, "id": "3171ada2", "metadata": {}, "outputs": [], "source": [ "def get_exposure_time_counts(fits_files):\n", " \"\"\"\n", " Generates a dictionary of exposure times and their counts from FITS files.\n", "\n", " Parameters:\n", " - fits_files (list): List of FITS file paths.\n", "\n", " Returns:\n", " - Dictionary with exposure times as keys and counts and file paths as values.\n", " \"\"\"\n", " exposure_time_counts = {}\n", " for file_path in fits_files:\n", " map_ = sunpy.map.Map(file_path)\n", " exposure_time = round(map_.exposure_time.to(u.s).value, 2)\n", " if exposure_time not in exposure_time_counts:\n", " exposure_time_counts[exposure_time] = {\"count\": 1, \"files\": [file_path]}\n", " else:\n", " exposure_time_counts[exposure_time][\"count\"] += 1\n", " exposure_time_counts[exposure_time][\"files\"].append(file_path)\n", " return exposure_time_counts\n", "\n", "\n", "def display_exposure_time_counts(exposure_time_counts):\n", " \"\"\"\n", " Displays the exposure times and their counts.\n", "\n", " Parameters:\n", " - exposure_time_counts (dict): Dictionary of exposure times and their counts.\n", " \"\"\"\n", " print(\"Exposure times in the dataset (in seconds) and their counts:\")\n", " for time, info in exposure_time_counts.items():\n", " print(f\"{time}s: {info['count']} file(s)\")\n", "\n", "\n", "def filter_files_by_criteria(exposure_time_counts, criteria):\n", " \"\"\"\n", " Filters FITS files based on user-specified exposure time criteria.\n", "\n", " Parameters:\n", " - exposure_time_counts (dict): Dictionary of exposure times and their counts.\n", " - criteria (str): User-specified exposure time criteria.\n", "\n", " Returns:\n", " - List of FITS file paths that match the exposure time criteria.\n", " \"\"\"\n", " filtered_files = []\n", " if \"-\" in criteria:\n", " lower, upper = map(float, criteria.split(\"-\"))\n", " for time, info in exposure_time_counts.items():\n", " if lower <= time <= upper:\n", " filtered_files.extend(info[\"files\"])\n", " elif criteria.startswith(\"<\"):\n", " max_time = float(criteria[1:])\n", " for time, info in exposure_time_counts.items():\n", " if time < max_time:\n", " filtered_files.extend(info[\"files\"])\n", " elif criteria.startswith(\">\"):\n", " min_time = float(criteria[1:])\n", " for time, info in exposure_time_counts.items():\n", " if time > min_time:\n", " filtered_files.extend(info[\"files\"])\n", " else:\n", " exact_time = float(criteria)\n", " for time, info in exposure_time_counts.items():\n", " if time == exact_time:\n", " filtered_files.extend(info[\"files\"])\n", " return filtered_files\n", "\n", "\n", "def filter_fits_by_exposure_time(fits_files):\n", " \"\"\"\n", " Filters FITS files based on exposure time criteria specified by the user.\n", "\n", " Parameters:\n", " - fits_files (list): List of FITS file paths.\n", "\n", " Returns:\n", " - List of FITS file paths that match the exposure time criteria.\n", " \"\"\"\n", " exposure_time_counts = get_exposure_time_counts(fits_files)\n", " display_exposure_time_counts(exposure_time_counts)\n", " criteria = input(\n", " \"\\nEnter the exposure time criteria\\n (e.g., '0.36' for exactly 0.36s, '<1.5' for less than 1.5s, '>1.5' for more than 1.5s, '2-120' for between 2s and 120s): \"\n", " )\n", " print(\"\\nSelected exposure time(s):\", criteria)\n", " filtered_files = filter_files_by_criteria(exposure_time_counts, criteria)\n", " print(f\"\\nFound {len(filtered_files)} files matching the criteria.\")\n", " return filtered_files" ] }, { "cell_type": "code", "execution_count": null, "id": "afee3c77", "metadata": {}, "outputs": [], "source": [ "# In this example we're using the filtered Al-Poly Filter & 384 by 384 pixels FITS\n", "xrt_Al_poly_Obs_FITs_fixed_exposure_time = filter_fits_by_exposure_time(\n", " xrt_Al_poly_Obs_FITs\n", ")" ] }, { "cell_type": "markdown", "id": "71376844", "metadata": {}, "source": [ "\n", "## Navigating FITS Images " ] }, { "cell_type": "markdown", "id": "18a75135", "metadata": {}, "source": [ "Now, we have the capability to review images within our curated list of filtered FITS files. This enhanced navigation functionality not only facilitates sequential browsing but also introduces the ability to directly access a specific image within the dataset." ] }, { "cell_type": "markdown", "id": "55c844aa", "metadata": {}, "source": [ "Using the `view_fits_images` function, you can move forward or backward through the image dataset or jump to an Image directly navigate to a specific image by entering its number in the dataset. This feature is particularly useful when dealing with large datasets, as it enables quick access to images of interest without the need to sequentially skim through potentially hundreds of files." ] }, { "cell_type": "code", "execution_count": null, "id": "28a01157", "metadata": {}, "outputs": [], "source": [ "def view_fits_images(fits_files):\n", " \"\"\"\n", " Displays FITS images one at a time with navigation buttons and an option to jump to a specific image.\n", "\n", " Parameters:\n", " - fits_files (list): List of paths to the FITS files.\n", " \"\"\"\n", "\n", " current_index = [0] # Use a list to allow modifications from inner functions\n", "\n", " def show_image(index=None):\n", " \"\"\"Displays the image at the current index and clears the previous output.\"\"\"\n", " if index is not None:\n", " # Safely update the current index based on user input\n", " current_index[0] = max(0, min(index, len(fits_files) - 1))\n", "\n", " clear_output(wait=True) # Clear the previous image and controls\n", " display(\n", " widgets.HBox([prev_button, next_button, jump_input, jump_button])\n", " ) # Redisplay the controls\n", "\n", " fits_path = fits_files[current_index[0]]\n", " sunpy_map = sunpy.map.Map(fits_path)\n", "\n", " # Display FITS file name and image counter\n", " print(\n", " f\"File: {fits_path.split('/')[-1]} (Image {current_index[0] + 1} of {len(fits_files)})\"\n", " )\n", "\n", " plt.figure(figsize=(6, 6))\n", " sunpy_map.plot()\n", " plt.show()\n", "\n", " update_buttons_status()\n", "\n", " def go_next(_event=None):\n", " \"\"\"Go to the next image.\"\"\"\n", " if current_index[0] < len(fits_files) - 1:\n", " current_index[0] += 1\n", " show_image()\n", "\n", " def go_prev(_event=None):\n", " \"\"\"Go to the previous image.\"\"\"\n", " if current_index[0] > 0:\n", " current_index[0] -= 1\n", " show_image()\n", "\n", " def jump_to_image(_event=None):\n", " \"\"\"Jump to the image number entered by the user.\"\"\"\n", " try:\n", " index = int(jump_input.value) - 1 # Convert to 0-based index\n", " show_image(index)\n", " except ValueError:\n", " print(\"Please enter a valid image number.\")\n", "\n", " def update_buttons_status():\n", " \"\"\"Updates the status of next/prev buttons based on the current index.\"\"\"\n", " next_button.disabled = current_index[0] >= len(fits_files) - 1\n", " prev_button.disabled = current_index[0] <= 0\n", "\n", " # Create navigation buttons\n", " next_button = widgets.Button(description=\"Next\")\n", " prev_button = widgets.Button(description=\"Previous\")\n", " next_button.on_click(go_next)\n", " prev_button.on_click(go_prev)\n", "\n", " # Create jump to image widgets\n", " jump_input = widgets.Text(\n", " description=\"Jump to Image:\", placeholder=\"Enter image number\"\n", " )\n", " jump_button = widgets.Button(description=\"Go\")\n", " jump_button.on_click(jump_to_image)\n", "\n", " # Initially display buttons and the first image\n", " display(widgets.HBox([prev_button, next_button, jump_input, jump_button]))\n", " show_image()\n" ] }, { "cell_type": "code", "execution_count": null, "id": "c579d5bb", "metadata": {}, "outputs": [], "source": [ "# We're going to use our filtered FITS data set: xrt_Al_poly_384_Obs_FITs\n", "view_fits_images(xrt_Al_poly_Obs_FITs_fixed_exposure_time)" ] }, { "cell_type": "markdown", "id": "c440b9cb", "metadata": {}, "source": [ "\n", "## Visualizing Solar Dynamics: Creating a Movie from XRT FITS Data ☀️" ] }, { "cell_type": "markdown", "id": "a3f481d3", "metadata": {}, "source": [ "Creating a movie from FITS files is an excellent way to visualize data, particularly for dynamic solar phenomena observed by the Hinode XRT. We can accomplish this using SunPy to handle FITS files and matplotlib, along with imageio, to create the animation." ] }, { "cell_type": "markdown", "id": "143f36f1", "metadata": {}, "source": [ "The `create_solar_movie_from_FITS` function generates a movie (MP4 format) from the FITS files you provide. If you're interested in customizing this process, you can review and modify the function as needed. To proceed with creating a movie using the default settings, run the cell containing the function below. Then, move to the following cell for instructions on how to use this function in your workflow." ] }, { "cell_type": "code", "execution_count": null, "id": "7d73cb73", "metadata": {}, "outputs": [], "source": [ "def create_solar_movie_from_FITS(\n", " fits_files, output_file=\"solar_movie.mp4\", fps=5, processing=True\n", "):\n", " \"\"\"\n", " Creates a movie from a sequence of FITS files.\n", "\n", " Parameters:\n", " - fits_files (list): List of paths to the FITS files.\n", " - output_file (str): Path where the output movie will be saved.\n", " - fps (int): Frames per second for the output movie.\n", " - processing (bool): If True, prints processing messages. Default is True.\n", " \"\"\"\n", " # Create a temporary directory to store the frames\n", " frames_dir = tempfile.mkdtemp()\n", " frames = []\n", "\n", " if processing:\n", " print(f\"Starting to process {len(fits_files)} FITS files.\\n\")\n", "\n", " for i, file_path in enumerate(fits_files):\n", " if processing:\n", " print(f\"Processing file {i+1}/{len(fits_files)}: {file_path}\")\n", " # Load the FITS file as a SunPy Map\n", " xrt_map = sunpy.map.Map(file_path)\n", "\n", " # Plotting the SunPy Map\n", " fig = plt.figure(figsize=(8, 8))\n", " ax = plt.subplot(projection=xrt_map)\n", " xrt_map.plot(axes=ax)\n", "\n", " # Adding title and labels\n", " plt.title(\n", " f'XRT {xrt_map.measurement} {xrt_map.date.strftime(\"%Y-%m-%d %H:%M:%S\")}'\n", " )\n", " ax.set_xlabel(\"Helioprojective Longitude (Solar-X)\")\n", " ax.set_ylabel(\"Helioprojective Latitude (Solar-Y)\")\n", " plt.tight_layout()\n", "\n", " # Saving the frame\n", " frame_path = os.path.join(frames_dir, f\"frame_{i:04d}.png\")\n", " plt.savefig(frame_path)\n", " plt.close(fig)\n", " frames.append(frame_path)\n", " if processing:\n", " print(f\"Saved frame {i+1}/{len(fits_files)}\")\n", "\n", " if processing:\n", " print(\"\\nStarting to compile the movie.\\n\")\n", "\n", " # Compile the movie from saved frames\n", " with imageio.get_writer(output_file, fps=fps) as writer:\n", " for frame_path in frames:\n", " image = imageio.imread(frame_path)\n", " writer.append_data(image)\n", "\n", " # Cleanup: Remove temporary frames and directory\n", " for frame_path in frames:\n", " os.remove(frame_path)\n", " os.rmdir(frames_dir)\n", "\n", " if processing:\n", " print(\n", " f\"\\nMovie created: {output_file}\\nAll done!\\nMake sure to download the movie to your local machine to see it.\"\n", " )" ] }, { "cell_type": "code", "execution_count": null, "id": "d763dff8", "metadata": { "scrolled": true }, "outputs": [], "source": [ "# Specify the output file name for the movie\n", "solar_movie_example = \"Hinode_XRT_Al_poly_CME_solar_movie.mp4\"\n", "\n", "# Create the movie from the list of FITS files\n", "# Using our Al-Poly by 384X384 filtered FITs files\n", "create_solar_movie_from_FITS(\n", " xrt_Al_poly_Obs_FITs_fixed_exposure_time,\n", " output_file=solar_movie_example,\n", " fps=12,\n", " processing=True,\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "9bbcba22", "metadata": { "scrolled": false }, "outputs": [], "source": [ "# from IPython.display import Video\n", "\n", "# Display the created movie within the notebook. Might to add \"embed=True\" to enable videos directly into a Jupyter Notebook\n", "Video(solar_movie_example)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.5" } }, "nbformat": 4, "nbformat_minor": 5 }