{ "cells": [ { "cell_type": "code", "execution_count": null, "id": "ebfde924", "metadata": {}, "outputs": [], "source": [ "###############################################################################\n", "# Running real WRF MPI from jupyter notebook, and analyzing using wrf-python\n", "# \n", "# This presumes prior knowledge of how to run WRF/WPS!!!!!!!!\n", "# \n", "# ATM 419/563\n", "# Robert Fovell, rfovell@albany.edu, December 2023, Version 1.0\n", "###############################################################################\n", "\n", "%matplotlib inline\n", "from math import pi, cos, sin\n", "\n", "import datetime\n", "import numpy as np\n", "\n", "import netCDF4\n", "from netCDF4 import Dataset\n", "from wrf import (getvar, to_np, get_cartopy, latlon_coords, vertcross, ll_to_xy,\n", " cartopy_xlim, cartopy_ylim, interpline, CoordPair, destagger, interplevel, ALL_TIMES)\n", "\n", "import matplotlib.pyplot as plt\n", "from matplotlib.cm import get_cmap\n", "from matplotlib.colors import LinearSegmentedColormap\n", "\n", "# Cartopy stuff\n", "import cartopy.crs as ccrs\n", "import cartopy.feature as cf\n", "import cartopy.geodesic\n", "import shapely\n", "\n", "print(\" ready...\")" ] }, { "cell_type": "code", "execution_count": null, "id": "28c743d0", "metadata": {}, "outputs": [], "source": [ "# ------------------------------------------------------------------------------------------- #\n", "# move to our WRF run directory in the next cell\n", "#\n", "# Expects to find WRF in ~/WRF and WPS in ~/WPS\n", "# Run directory is set to ~/WRF/test/em_real\n", "# Expects to find namelist.wps.PRISTINE and namelist.input.PRISTINE in ~/WRF/test/em_real\n", "# Not currently configured for more than 3 domains total (one parent, 2 nests)\n", "# Not currently configured for multiple wrfout files per domain\n", "# Currently configured to analyze wrfout_d01... file\n", "# ------------------------------------------------------------------------------------------- #\n", "\n", "# do initial clean-up and file linking\n", "#\n", "# ANY EXISTING WRFOUT FILES WILL BE REMOVED\n", "\n", "# This notebook presumes existence of unmodified files \"namelist.input.PRISTINE\" \n", "# and \"namelist.wps.PRISTINE\" as starting points" ] }, { "cell_type": "code", "execution_count": null, "id": "fb0d2b82", "metadata": {}, "outputs": [], "source": [ "%cd ~/WRF/test/em_real\n", "%ls" ] }, { "cell_type": "code", "execution_count": null, "id": "d97520ba", "metadata": { "scrolled": false }, "outputs": [], "source": [ "%%bash\n", "\n", "echo \" cleaning up and creating links\"\n", "\\rm -rf Vtable\n", "\\rm -rf GRIBFILE*\n", "\\rm -f geo_em*\n", "\\rm -f namelist.input\n", "\\rm -f namelist.wps\n", "\n", "mkdir -p geogrid\n", "\\cp -f ~/WPS/geogrid/GEOGRID.TBL.ARW geogrid/GEOGRID.TBL\n", "mkdir -p metgrid\n", "\\cp -f ~/WPS/metgrid/METGRID.TBL.ARW metgrid/METGRID.TBL\n", "\n", "\\cp -f namelist.input.PRISTINE namelist.input\n", "\\cp -f namelist.wps.PRISTINE namelist.wps\n", "\n", "\\rm -f wrfout* wrfinput* wrfbdy* rsl*\n", "\n", "pwd\n", "\n", "echo \"ready...\"" ] }, { "cell_type": "code", "execution_count": null, "id": "b1314b59", "metadata": {}, "outputs": [], "source": [ "%%bash\n", "\n", "echo \"setting up the domain...\"\n", "\n", "export max_dom=1 # number of domains to configure (hardcoded max 3 in this version)\n", "export e_we=\"85,95,105\" # west-east dimensions of domains 1,2,3 (even if only one used)\n", "export e_sn=\"56,85,94\" # south-north dimensions \"\n", "export i_parent_start=\"1,36,100\" # first digit always 1\n", "export j_parent_start=\"1,7,36\" # first digit always 1\n", "export dx=90000 # presumes dy=dx\n", "\n", "export map_proj='lambert'\n", "export ref_lat=39.5\n", "export ref_lon=-100\n", "export stand_lon=-100\n", "export truelat1=30\n", "export truelat2=60\n", "\n", "# do not touch anything below this line\n", "\n", "gsed -i \"/max_dom=/c\\\\ max_dom=$max_dom\" namelist.wps\n", "gsed -i \"/e_we=/c\\\\ e_we=$e_we\" namelist.wps\n", "gsed -i \"/i_parent_start=/c\\\\ i_parent_start=$i_parent_start\" namelist.wps\n", "gsed -i \"/j_parent_start=/c\\\\ j_parent_start=$j_parent_start\" namelist.wps\n", "\n", "gsed -i \"/dx=/c\\\\ dx=$dx\" namelist.wps\n", "gsed -i \"/dy=/c\\\\ dy=$dx\" namelist.wps\n", "\n", "gsed -i \"/map_proj=/c\\\\ map_proj='$map_proj'\" namelist.wps\n", "gsed -i \"/ref_lat=/c\\\\ ref_lat=$ref_lat\" namelist.wps\n", "gsed -i \"/ref_lon=/c\\\\ ref_lon=$ref_lon\" namelist.wps\n", "gsed -i \"/stand_lon=/c\\\\ stand_lon=$stand_lon\" namelist.wps\n", "gsed -i \"/truelat1=/c\\\\ truelat1=$truelat1\" namelist.wps\n", "gsed -i \"/truelat2=/c\\\\ truelat2=$truelat2\" namelist.wps\n", "\n", "gsed -i \"/i_parent_start=/c\\\\ i_parent_start=$i_parent_start\" namelist.input\n", "gsed -i \"/j_parent_start=/c\\\\ j_parent_start=$j_parent_start\" namelist.input\n", "gsed -i \"/dx=/c\\\\ dx=$dx,$((dx/3)),$((dx/3/3))\" namelist.input\n", "gsed -i \"/dy=/c\\\\ dy=$dx,$((dx/3)),$((dx/3/3))\" namelist.input\n", "\n", "echo \"ready to run geogrid...\"" ] }, { "cell_type": "code", "execution_count": null, "id": "c05482ee", "metadata": {}, "outputs": [], "source": [ "%%bash\n", "\n", "echo \"Look for Successful completion of geogrid\"\n", "\n", "time mpirun -np 2 ~/WPS/geogrid/src/geogrid.exe\n", "\n", "ls geo_em.d01.nc" ] }, { "cell_type": "code", "execution_count": null, "id": "fdffa2b7", "metadata": {}, "outputs": [], "source": [ "# ------------------------------------------------------------------------------------------- #\n", "# read geogrid file, extract variables for plotting. This plots domain 1 by default\n", "# EDIT below to match your desired reference lat, lon, standard lon, true latitudes\n", "# ------------------------------------------------------------------------------------------- #\n", "\n", "# specify where our wrfout files are located\n", "location = \"./\"\n", "ncfile = Dataset(location + \"geo_em.d01.nc\")\n", "\n", "# get WRF variables for all times in our output collection\n", "# these are xarrays, so sometimes we need \".values\" to strip out metadata\n", "\n", "latwrf = getvar(ncfile,\"XLAT_M\") # latitude\n", "lonwrf = getvar(ncfile,\"XLONG_M\") # longitude\n", "hgt = getvar(ncfile,\"HGT_M\")\n", "\n", "# ------------------------------------------------------------------------------------------- #\n", "# plot the model topography\n", "# ------------------------------------------------------------------------------------------- #\n", "\n", "plt.figure(figsize=(9, 12))\n", "\n", "# define the central latitude and longitude, and true latitudes for Lambert projection\n", "lat_0 = 39.5 # EDIT should match ref_lat from namelist.wps\n", "lon_0 = -100 # EDIT should match ref_lon from namelist.wps\n", "standard_parallels = (30.,60.) # EDIT should modify to match truelat1, truelat2 from namelist.wps\n", "\n", "# colormesh or contourf?\n", "do_pcolormesh = False\n", "\n", "# create the projection and an axis object\n", "proj=ccrs.LambertConformal(central_latitude=lat_0, central_longitude=lon_0, standard_parallels=standard_parallels, globe=None)\n", "ax = plt.axes(projection=proj)\n", "\n", "# add some nice map features\n", "ax.add_feature(cf.COASTLINE.with_scale('10m'))\n", "ax.add_feature(cf.BORDERS.with_scale('10m')) \n", "ax.add_feature(cf.LAKES,alpha=0.5) # alpha sets transparency level\n", "ax.add_feature(cf.RIVERS)\n", "ax.add_feature(cf.STATES.with_scale('10m'),linestyle='dotted')\n", "\n", "# plotting range for terrain height\n", "norm = plt.Normalize(0, 4000) \n", "\n", "# make the plot\n", "if(do_pcolormesh):\n", " cm = ax.pcolormesh(lonwrf,latwrf,hgt,shading='auto',norm=norm,cmap=plt.cm.terrain, transform=ccrs.PlateCarree())\n", "else:\n", " cm = ax.contourf(lonwrf,latwrf,hgt,norm=norm,cmap=plt.cm.terrain,transform=ccrs.PlateCarree())\n", "\n", "# add a colorbar\n", "plt.colorbar(cm, orientation=\"vertical\",fraction=0.02, pad=0.04, label=\"terrain height (m)\")\n", "\n", "# maybe save the figure\n", "#plt.savefig(\"model_terrain.png\",bbox_inches = 'tight')\n", "\n", "plt.show()\n", "\n", "print(\" ready...\")" ] }, { "cell_type": "code", "execution_count": null, "id": "60afc885", "metadata": {}, "outputs": [], "source": [ "%%bash\n", "\n", "echo \"getting ready to run ungrib. NOTE max_dom IS SPECIFIED AGAIN\"\n", "echo \"for numbers <10 LEADING ZEROES ARE REQUIRED (e.g., start_month=03)\"\n", "\n", "export max_dom=1\n", "\n", "export run_hours=60\n", "\n", "export start_year=1993\n", "export start_month=03\n", "export start_day=12\n", "export start_hour=12\n", "\n", "export end_year=1993\n", "export end_month=03\n", "export end_day=15\n", "export end_hour=00\n", "\n", "export interval_seconds=21600\n", "\n", "# do not touch anything below this line\n", "\n", "start_date=$start_year-$start_month-$start_day'_'$start_hour:00:00\n", "end_date=$end_year-$end_month-$end_day'_'$end_hour:00:00\n", "echo \" our start and end dates: \" $start_date $end_date\n", "\n", "gsed -i \"/start_date=/c\\\\ start_date=$max_dom*'$start_date'\" namelist.wps\n", "gsed -i \"/end_date=/c\\\\ end_date=$max_dom*'$end_date'\" namelist.wps\n", "\n", "gsed -i \"/interval_seconds=/c\\\\ interval_seconds=$interval_seconds\" namelist.wps\n", "\n", "gsed -i \"/run_hours=/c\\\\ run_hours=$run_hours\" namelist.input\n", "\n", "gsed -i \"/start_year=/c\\\\ start_year=$max_dom*$start_year\" namelist.input\n", "gsed -i \"/start_month=/c\\\\ start_month=$max_dom*$start_month\" namelist.input\n", "gsed -i \"/start_day=/c\\\\ start_day=$max_dom*$start_day\" namelist.input\n", "gsed -i \"/start_hour=/c\\\\ start_hour=$max_dom*$start_hour\" namelist.input\n", "\n", "gsed -i \"/end_year=/c\\\\ end_year=$max_dom*$end_year\" namelist.input\n", "gsed -i \"/end_month=/c\\\\ end_month=$max_dom*$end_month\" namelist.input\n", "gsed -i \"/end_day=/c\\\\ end_day=$max_dom*$end_day\" namelist.input\n", "gsed -i \"/end_hour=/c\\\\ end_hour=$max_dom*$end_hour\" namelist.input\n", "\n", "gsed -i \"/interval_seconds=/c\\\\ interval_seconds=$interval_seconds\" namelist.input\n", "\n", "echo \" \"\n", "echo \" ready for next step...\"" ] }, { "cell_type": "code", "execution_count": null, "id": "60ee6c75", "metadata": { "scrolled": true }, "outputs": [], "source": [ "%%bash\n", "\n", "echo \"run ungrib.exe after specifying appropriate Vtable and where parent model data are. DO NOT USE MPIRUN\"\n", "\n", "\n", "~/WPS/link_grib.csh NNRP_199303/* .\n", "\n", "cp ~/WPS/ungrib/Variable_Tables/Vtable.NNRP Vtable\n", "\n", "~/WPS/ungrib.exe\n", "\n", "echo \"check for successful completion, then run metgrid\"\n" ] }, { "cell_type": "code", "execution_count": null, "id": "0a6b3ab9", "metadata": {}, "outputs": [], "source": [ "%%bash\n", "\n", "echo \"running metgrid. Check for successful completion\"\n", "\n", "time mpirun -np 4 ~/WPS/metgrid/src/metgrid.exe\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "id": "84dfd2f4", "metadata": {}, "outputs": [], "source": [ "# next: set up real and wrf runs, specifying run hours, history interval, time step, and model physics, etc.\n", "# number of domains to run needs to be reset\n", "# make sure num_metgrid_levels and num_metgrid_soil_levels are correctly specified below" ] }, { "cell_type": "code", "execution_count": null, "id": "c84ae647", "metadata": {}, "outputs": [], "source": [ "%%bash\n", "\n", "export max_dom=1 # how many domains will be active in this run\n", "\n", "export history_interval=360 # how often (in minutes) to write to wrfout file\n", "\n", "export time_step=240 # coarsest domain time step (seconda)\n", "\n", "export e_vert=51 # number of vertical levels requested\n", "\n", "# EDIT the next two lines. Needs to match info in met_em* files. Use ncdump -h on one of the met_em_d01* files\n", "export num_metgrid_levels=18 # and look for \"num_metgrid_levels\" for this number\n", "export num_metgrid_soil_levels=2 # and look for \"num_st_layers\",\"num_sm_layers\" for this number\n", "\n", "export p_top_requested=1000 # pressure at model top (Pascals); cannot exceed that provided by parent data\n", "\n", "# specify physics that are the same for each domain\n", "export mp_physics=3 # microphysics\n", "export ra_lw_physics=1 # LW radiation\n", "export ra_sw_physics=1 # SW radiation\n", "export radt=10 # radiation time step (minutes)\n", "export sf_surface_physics=2 # land surface model\n", "export sf_sfclay_physics=1 # surface layer\n", "export bl_pbl_physics=11 # PBL scheme\n", "export bl_mynn_closure=2.5 # closure option for MYNN only\n", "\n", "\n", "# specify physics that can vary among domains\n", "export cu_physics=\"3,3,0\" # cumulus scheme\n", "export cudt=\"0,0,0\" # cumulus time step (usually 0)\n", "\n", "# do not touch anything below this line\n", "\n", "gsed -i \"/e_vert=/c\\\\ e_vert=$max_dom*$e_vert\" namelist.input\n", "gsed -i \"/p_top_requested=/c\\\\ p_top_requested=$p_top_requested\" namelist.input\n", "\n", "gsed -i \"/num_metgrid_levels=/c\\\\ num_metgrid_levels=$num_metgrid_levels\" namelist.input\n", "gsed -i \"/num_metgrid_soil_levels=/c\\\\ num_metgrid_soil_levels=$num_metgrid_soil_levels\" namelist.input\n", "\n", "gsed -i \"/mp_physics=/c\\\\ mp_physics=$max_dom*$mp_physics\" namelist.input\n", "gsed -i \"/ra_lw_physics=/c\\\\ ra_lw_physics=$max_dom*$ra_lw_physics\" namelist.input\n", "gsed -i \"/ra_sw_physics=/c\\\\ ra_sw_physics=$max_dom*$ra_sw_physics\" namelist.input\n", "gsed -i \"/radt=/c\\\\ radt=$max_dom*$radt\" namelist.input\n", "gsed -i \"/sf_surface_physics=/c\\\\ sf_surface_physics=$max_dom*$sf_surface_physics\" namelist.input\n", "gsed -i \"/sf_sfclay_physics=/c\\\\ sf_sfclay_physics=$max_dom*$sf_sfclay_physics\" namelist.input\n", "gsed -i \"/bl_pbl_physics=/c\\\\ bl_pbl_physics=$max_dom*$bl_pbl_physics\" namelist.input\n", "gsed -i \"/bl_mynn_closure=/c\\\\ bl_mynn_closure=$bl_mynn_closure\" namelist.input\n", "\n", "gsed -i \"/cu_physics=/c\\\\ cu_physics=$cu_physics\" namelist.input\n", "gsed -i \"/cudt=/c\\\\ cudt=$cudt\" namelist.input\n", "\n", "echo \" ready to run real.exe...\"" ] }, { "cell_type": "code", "execution_count": null, "id": "537202a0", "metadata": {}, "outputs": [], "source": [ "%%bash\n", "\n", "echo \"run real.exe, check output for SUCCESS COMPLETE REAL_EM INIT\"\n", "\n", "\\rm rsl.*\n", "time mpirun -np 4 ./real.exe\n" ] }, { "cell_type": "code", "execution_count": null, "id": "70c6b1a5", "metadata": {}, "outputs": [], "source": [ "%%bash\n", "\n", "echo \" LOOK for SUCCESS COMPLETE REAL_EM INIT\"\n", "\n", "tail rsl.out.0000" ] }, { "cell_type": "code", "execution_count": null, "id": "71b15b71", "metadata": { "scrolled": false }, "outputs": [], "source": [ "%%bash\n", "\n", "echo \" running WRF. WAIT for timing information to appear\"\n", "\\rm rsl.*\n", "time mpirun -np 4 ./wrf.exe\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "id": "40154c4b", "metadata": {}, "outputs": [], "source": [ "%%bash\n", "\n", "echo \" LOOK for SUCCESS COMPLETE WRF. The wrfout filename is changed to wrfout_done\"\n", "\n", "ls wrfout*\n", "\\rm -f wrfout_done\n", "cp -f wrfout* wrfout_done\n", "\n", "tail rsl.out.0000" ] }, { "cell_type": "code", "execution_count": null, "id": "d9897293", "metadata": {}, "outputs": [], "source": [ "# ------------------------------------------------------------------------------------------- #\n", "# read wrfout file, extract variables\n", "# ------------------------------------------------------------------------------------------- #\n", "\n", "# specify where our wrfout files are located\n", "location = \"./\"\n", "ncfile = Dataset(location + \"wrfout_done\")\n", "\n", "# get WRF variables for all times in our output collection\n", "# these are xarrays, so sometimes we need \".values\" to strip out metadata\n", "\n", "latwrf = getvar(ncfile,\"XLAT\") # latitude\n", "lonwrf = getvar(ncfile,\"XLONG\") # longitude\n", "\n", "hgt = getvar(ncfile,\"HGT\") # terrain height\n", "z = getvar(ncfile, \"z\") # height coordinate\n", "ua = getvar(ncfile, \"ua\", units=\"m s-1\",timeidx=ALL_TIMES) # U on mass points (3D but y-dimension is irrelevant)\n", "va = getvar(ncfile, \"va\", units=\"m s-1\",timeidx=ALL_TIMES) # V on mass points\n", "wa = getvar(ncfile, \"wa\", units=\"m s-1\",timeidx=ALL_TIMES) # W on mass points\n", "theta = getvar(ncfile, \"theta\", units=\"K\",timeidx=ALL_TIMES) # THETA on mass points\n", "slp = getvar(ncfile, \"slp\", units=\"hPa\",timeidx=ALL_TIMES) # SLP derived\n", "snownc = getvar(ncfile, \"SNOWNC\", timeidx=ALL_TIMES) # snow from microphysics (mm)\n", "graupelnc = getvar(ncfile, \"GRAUPELNC\", timeidx=ALL_TIMES) # graupel from microphysics (mm)\n", "\n", "# 10 m wind speed\n", "wspd10 = getvar(ncfile, \"uvmet10_wspd_wdir\", units=\"m s-1\",timeidx=ALL_TIMES)[0,:] # 2nd column of uvmet10_wspd_wdir is wdir\n", "\n", "# output times\n", "time = getvar(ncfile, \"Times\", timeidx=ALL_TIMES) # time is a datetime variable\n", "\n", "# verify array shapes\n", "print(\" shape of z\",z.shape)\n", "print(\" shape of ua\",ua.shape, \" times, vert levels, y-direc points, x-direc points \")\n", "print(\" shape of va\",va.shape)\n", "print(\" shape of va\",wa.shape)\n", "print(\" shape of theta \",theta.shape)\n", "print(\" shape of 10m wind \",wspd10.shape)\n", "print(\" shape of time \",time.shape)\n", "\n", "# output times\n", "print(\" Our output times\")\n", "print(time.values)\n", "\n", "print(\" ready to go... \")" ] }, { "cell_type": "code", "execution_count": null, "id": "3264e855-59ac-4dc0-ae29-2b3b75b9281e", "metadata": {}, "outputs": [], "source": [ "# plot change of snow depth (inches) from initial time\n", "\n", "snow_depth = snownc*10 + graupelnc*1\n", "snow_depth_inches = snow_depth*0.0393701\n", "print(np.shape(snow_depth_inches))\n", "\n", "snow_depth_inches_last=snow_depth_inches[-1,:,:]\n", "snow_depth_inches_diff=snow_depth_inches_last-snow_depth_inches[0,:,:]\n", "print(np.shape(snow_depth_inches_diff))\n", "print(np.max(snow_depth_inches_diff.values),np.min(snow_depth_inches_diff.values))\n", "timenow=time[-1]\n", "\n", "fig = plt.figure(figsize=(9, 12))\n", "\n", "colors = ['white','red']\n", "cmap = LinearSegmentedColormap.from_list('name', colors)\n", "\n", "# define the central latitude and longitude, and true latitudes for Lambert projection\n", "lat_0 = 38 # ref_lat from namelist.wps\n", "lon_0 = -100 # ref_lon from namelist.wps\n", "standard_parallels = (38.,38.) # truelat1, truelat2 from namelist.wps\n", "\n", "# colormesh or contourf?\n", "do_pcolormesh = True\n", "\n", "# create the projection and an axis object\n", "proj=ccrs.LambertConformal(central_latitude=lat_0, central_longitude=lon_0, standard_parallels=standard_parallels, globe=None)\n", "ax = plt.axes(projection=proj)\n", "\n", "# add some nice map features\n", "ax.add_feature(cf.COASTLINE.with_scale('10m'))\n", "ax.add_feature(cf.BORDERS.with_scale('10m')) \n", "ax.add_feature(cf.LAKES,alpha=0.5) # alpha sets transparency level\n", "ax.add_feature(cf.RIVERS)\n", "ax.add_feature(cf.STATES.with_scale('10m'),linestyle='dotted')\n", "\n", "# plotting range for terrain height\n", "norm = plt.Normalize(0, 50) \n", "\n", "# make the plot\n", "if(do_pcolormesh):\n", " cm = ax.pcolormesh(lonwrf,latwrf,snow_depth_inches_diff,shading='auto',norm=norm,cmap=cmap, transform=ccrs.PlateCarree())\n", "else:\n", " cm = ax.contourf(lonwrf,latwrf,snow_depth_inches_diff,norm=norm,cmap=cmap,transform=ccrs.PlateCarree())\n", "\n", "# add a colorbar\n", "plt.colorbar(cm, orientation=\"vertical\",fraction=0.02, pad=0.04,label=\"snow depth (in)\")\n", "\n", "# superimpose contours\n", "levels=np.arange(0,50,5)\n", "ax.contour(lonwrf,latwrf,snow_depth_inches_diff,norm=norm,levels=levels,colors='k',transform=ccrs.PlateCarree())\n", "\n", "\n", "ax.set_extent([-95,-65,30,51])\n", "\n", "# maybe save the figure\n", "#plt.savefig(\"kansas_terrain.png\",bbox_inches = 'tight')\n", "\n", "plt.title(str(timenow.values)[0:19] + \" snow depth (in; shaded and contoured) \",fontweight='bold')\n", "\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": null, "id": "3ccc3b55-149f-4812-a63c-946654d9efc5", "metadata": {}, "outputs": [], "source": [ "# end of this notebook" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.13" } }, "nbformat": 4, "nbformat_minor": 5 }