├── LICENSE └── atmomodel.ipynb /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 bert hubert 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /atmomodel.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "155594b8", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "%matplotlib notebook\n", 11 | "%precision 2\n", 12 | "import matplotlib\n", 13 | "import matplotlib.pyplot as plt\n", 14 | "import matplotlib.ticker as mtick\n", 15 | "import math\n", 16 | "import numpy as np\n", 17 | "from hapi import *\n", 18 | "import pandas\n", 19 | "import xarray as xr\n", 20 | "plt.rcParams['figure.figsize'] = [9.5, 6]\n", 21 | "\n" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": null, 27 | "id": "e84f8020", 28 | "metadata": {}, 29 | "outputs": [], 30 | "source": [ 31 | "# let's fetch all the HITRAN we need\n", 32 | "#fetch('H2O',1,1,1,3500)\n", 33 | "#fetch('CO2',2,1,1,3500)\n", 34 | "#fetch('O3',3,1,1, 3500)\n", 35 | "#fetch('N2O',4,1,1,3500)\n", 36 | "#fetch('CH4',6,1,1,3500)\n", 37 | "db_begin('data')" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": null, 43 | "id": "38bd71a8", 44 | "metadata": {}, 45 | "outputs": [], 46 | "source": [ 47 | "# helper function to primitively integrate a discrete set of numbers passed as arrays\n", 48 | "# I'm sure there is a better numpy function for this somewhere\n", 49 | "def integrate(x, y):\n", 50 | " df = pandas.DataFrame({'x': x, 'y': y})\n", 51 | " df['interpy']=df['y'] - 0.5 * df['y'].diff()\n", 52 | " integrated = (df['interpy']*df['x'].diff()).cumsum()\n", 53 | " return integrated" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": null, 59 | "id": "3467f7cb", 60 | "metadata": {}, 61 | "outputs": [], 62 | "source": [ 63 | "# v is wavenumber in cm^-1, T in Kelvin, output in W/m^2 for that frequency\n", 64 | "def planckWNCM(v, T):\n", 65 | " v = v *100\n", 66 | " c = 3.0e8\n", 67 | " h = 6.62607015e-34\n", 68 | " kB = 1.380649e-23 # J/K\n", 69 | " return 100.0*math.pi*2.0*h*c*c*v*v*v/(math.exp(h*c*v/(kB*T))-1)\n", 70 | " # this is the power per square meter PER cm^-1 wavenumber, hence the 100 at the beginning \n", 71 | " # since planck law is in m^-1\n", 72 | "\n", 73 | "sb= 5.670374419e-8 # stephan-boltzmann constant, correct\n" 74 | ] 75 | }, 76 | { 77 | "cell_type": "markdown", 78 | "id": "de3423b5", 79 | "metadata": {}, 80 | "source": [ 81 | "So here's the idea. A dataframe with 'nu' as index, using a fixed grid. 1..3000 with 0.01 spacing. \n", 82 | "On this index we put the radiation incoming from below\n", 83 | "Also the linear absorption for chosen gases\n" 84 | ] 85 | }, 86 | { 87 | "cell_type": "code", 88 | "execution_count": null, 89 | "id": "9d2faab0", 90 | "metadata": {}, 91 | "outputs": [], 92 | "source": [ 93 | "layer = pandas.DataFrame({\"nu\": np.linspace(0.01, 3500, 350000)})\n", 94 | "t=273.15 + 16\n", 95 | "layer[\"inpower\"]= layer.nu.apply(lambda x: planckWNCM(x, t))" 96 | ] 97 | }, 98 | { 99 | "cell_type": "code", 100 | "execution_count": null, 101 | "id": "837f29a9", 102 | "metadata": {}, 103 | "outputs": [], 104 | "source": [ 105 | "fig, ax = plt.subplots()\n", 106 | "ax.plot(layer.nu, layer.inpower, label=\"Radiation per wavenumber\")\n", 107 | "ax.set_xlim(0)\n", 108 | "\n", 109 | "ax2 = ax.twinx()\n", 110 | "ax2.plot(layer.nu, integrate(layer.nu, layer.inpower), color='orange', label=\"Total surface radiation\")\n", 111 | "ax2.axhline(y=sb*t*t*t*t, ls=':')\n", 112 | "ax.set_ylabel(\"$W/m^2/cm^{-1}$\")\n", 113 | "ax2.set_ylabel(\"$W/m^2$\")\n", 114 | "\n", 115 | "ax.set_xlabel(\"wavenumber $cm^{-1}$\")\n", 116 | "ax.set_ylim(0)\n", 117 | "ax2.set_ylim(0)\n", 118 | "ax.set_title(f\"Black body radiation at {t}K\")\n", 119 | "ax.grid()\n", 120 | "ax.legend(loc=8)\n", 121 | "ax2.legend(loc=9)" 122 | ] 123 | }, 124 | { 125 | "cell_type": "code", 126 | "execution_count": null, 127 | "id": "d2b2c5d0", 128 | "metadata": {}, 129 | "outputs": [], 130 | "source": [ 131 | "fig, ax1 = plt.subplots(figsize=(8,5))\n", 132 | "ax2=ax1.twinx()\n", 133 | "xes = np.linspace(0.1, 50000, 1000)\n", 134 | "#ax1.set_xlim(300,1800)\n", 135 | "\n", 136 | "# area of the solar surface area of earth orbit\n", 137 | "ratio=(4*3.141*696340*696340)/(4*3.141*8*60*3e5*8*60*3e5)\n", 138 | "print(ratio)\n", 139 | "\n", 140 | "ax1.plot(xes, pandas.Series(xes).apply(lambda x: planckWNCM(x, 255)), color='brown', label='Surface, T=255K', lw=2)\n", 141 | "ax2.plot(xes, ratio*pandas.Series(xes).apply(lambda x: planckWNCM(x, 5973)), color='orange', label='Sunlight, top of atmosphere, T=5973K', lw=2)\n", 142 | "\n", 143 | "def cm2nm(x):\n", 144 | " return (1.0/x)*10000000\n", 145 | "\n", 146 | "def nm2cm(x):\n", 147 | " return (1.0/x)/10000000\n", 148 | "#ax.set_xlim(0.1,50000)\n", 149 | "ax1.set_xticks(np.linspace(0,50000,11))\n", 150 | "xticks=pandas.Series(ax1.get_xticks())[1:]\n", 151 | "\n", 152 | "secax = ax1.secondary_xaxis('top', functions=(cm2nm, nm2cm))\n", 153 | "#ax1.set_xlim(0)\n", 154 | "secax.set_xlim(0)\n", 155 | "secax.set_ticks(xticks.apply(lambda x: cm2nm(x)))\n", 156 | "#plt.grid()\n", 157 | "ax1.set_xlabel(\"$cm^{-1}$\")\n", 158 | "secax.set_xlabel(\"nm\")\n", 159 | "ax1.set_ylabel(\"$W/m^2/cm^{-1}$\")\n", 160 | "ax2.set_ylabel(\"$W/m^2/cm^{-1}$\")\n", 161 | "ax1.set_title(\"Radiative spectrum of sunlight & Earth surface emissions\")\n", 162 | "ax1.legend(loc=8)\n", 163 | "ax2.legend(loc=1)\n", 164 | "plt.tight_layout()\n", 165 | "plt.savefig(\"sun-surface.svg\")" 166 | ] 167 | }, 168 | { 169 | "cell_type": "code", 170 | "execution_count": null, 171 | "id": "62b4256b", 172 | "metadata": {}, 173 | "outputs": [], 174 | "source": [ 175 | "# some validation https://www.omnicalculator.com/physics/absolute-humidity\n", 176 | "\n", 177 | "def ppmv2kgpm3(ppmv, mw, p=1, T=298):\n", 178 | " # Converting from ppb to µg/m3 is:\n", 179 | " # Concentration (µg/m3) = molecular weight x concentration (ppb) ÷ 24.45\n", 180 | " # The number 24.45 in the equation is the volume (litres) of a mole (gram molecular weight) of a \n", 181 | " # gas when the temperature is at 25°C and the pressure is at 1 atmosphere (1 atm = 1.01325 bar). \n", 182 | " return 1e-9*mw*ppmv*p*(298/T)*1000/24.45 # -> kg/m3\n", 183 | " # not super sure about the temperature adjustment\n", 184 | " \n", 185 | "print(ppmv2kgpm3(10000, 18, p=1, T=298))" 186 | ] 187 | }, 188 | { 189 | "cell_type": "code", 190 | "execution_count": null, 191 | "id": "308d8efe", 192 | "metadata": {}, 193 | "outputs": [], 194 | "source": [ 195 | "# input T in kelvins, P in atmospheres, returns ppmv\n", 196 | "# based on https://en.wikipedia.org/wiki/Tetens_equation\n", 197 | "def getPPMVH2O(T, P):\n", 198 | " C= T-273.15\n", 199 | " if C>0:\n", 200 | " partialP = 1000*0.61078*math.exp(17.27*C/(C+237.3)) # in Pascals\n", 201 | " else:\n", 202 | " partialP = 1000*0.61078*math.exp(21.875*C/(C+265.5)) # in Pascals\n", 203 | " #return partialP\n", 204 | " return 1000000*partialP/(P*1e5)\n", 205 | "\n", 206 | "\n", 207 | "\n", 208 | "xes = pandas.Series(np.linspace(273-40, 273+50, 150))\n", 209 | "fig,ax = plt.subplots()\n", 210 | "\n", 211 | "def k2c(x):\n", 212 | " return x-273.15\n", 213 | "\n", 214 | "def c2k(x):\n", 215 | " return x+273.15\n", 216 | "#ax.set_xlim(0.1,50000)\n", 217 | "\n", 218 | "secax = ax.secondary_xaxis('top', functions=(k2c, c2k))\n", 219 | "\n", 220 | "secay = ax.secondary_yaxis('right', functions=(lambda x: 100.0*x/1000000.0, lambda x: 100.0*1000000.0*x))\n", 221 | "secay.set_ylabel(\"%\")\n", 222 | "\n", 223 | "ax.plot(xes, xes.apply(lambda x: getPPMVH2O(x, 1)), label=\"100% relative humidity\")\n", 224 | "ax.plot(xes, xes.apply(lambda x: 0.4*getPPMVH2O(x, 1)), label=\"40% relative humidity\")\n", 225 | "ax.grid()\n", 226 | "plt.legend()\n", 227 | "plt.title(\"ppmv and absolute humidity of water at various temperatures, P=1atm\")\n", 228 | "ax.set_xlabel(\"T (K)\")\n", 229 | "secax.set_xlabel(\"T (C)\")\n", 230 | "ax.set_ylabel(\"ppmv\")\n", 231 | "\n", 232 | "\n", 233 | "plt.savefig(\"water-humid.svg\")" 234 | ] 235 | }, 236 | { 237 | "cell_type": "code", 238 | "execution_count": null, 239 | "id": "d8e51293", 240 | "metadata": {}, 241 | "outputs": [], 242 | "source": [ 243 | "def getGasAbsorptionPerMolecule(gas, ppmv, p=1., T=296.):\n", 244 | " nu,coef = absorptionCoefficient_HT(SourceTables=gas, \n", 245 | " Diluent={'air':1.0-ppmv/1000000.0, 'self': ppmv/1000000.0}, \n", 246 | " Environment={'p':p,'T':T}, HITRAN_units=True)\n", 247 | " # cm^2/molecule\n", 248 | " df=pandas.DataFrame({\"nu\": nu, \"coef\": coef})\n", 249 | " df.set_index(\"nu\", inplace=True)\n", 250 | " \n", 251 | " reindexed=df.reindex(df.index.union(layer.nu)).interpolate().fillna(0).reindex(layer.nu)\n", 252 | " \n", 253 | " if(gas==\"H2O\"):\n", 254 | " h2ocont=xr.open_dataset('/home/ahu/git/MT_CKD_H2O/run_example/mt_ckd_h2o_output-radflag.nc').to_dataframe()\n", 255 | " h2ocont=h2ocont.reindex(h2ocont.index.union(layer.nu)).interpolate().fillna(0).reindex(layer.nu)\n", 256 | " # cm^2/molecule\n", 257 | " reindexed.coef += (h2ocont.self_absorption).array\n", 258 | "\n", 259 | " \n", 260 | " return reindexed.reset_index()" 261 | ] 262 | }, 263 | { 264 | "cell_type": "code", 265 | "execution_count": null, 266 | "id": "9e8132e7", 267 | "metadata": {}, 268 | "outputs": [], 269 | "source": [ 270 | "dfco2=getGasAbsorptionPerMolecule(\"CO2\", 420, T=296, p=1)" 271 | ] 272 | }, 273 | { 274 | "cell_type": "code", 275 | "execution_count": null, 276 | "id": "91c3f296", 277 | "metadata": {}, 278 | "outputs": [], 279 | "source": [ 280 | "dfh2o=getGasAbsorptionPerMolecule(\"H2O\", 10000, T=296, p=1)" 281 | ] 282 | }, 283 | { 284 | "cell_type": "code", 285 | "execution_count": null, 286 | "id": "8022edfb", 287 | "metadata": {}, 288 | "outputs": [], 289 | "source": [ 290 | "dfch4=getGasAbsorptionPerMolecule(\"CH4\", 4, T=296, p=1)" 291 | ] 292 | }, 293 | { 294 | "cell_type": "code", 295 | "execution_count": null, 296 | "id": "8ea6d006", 297 | "metadata": {}, 298 | "outputs": [], 299 | "source": [ 300 | "fig,ax =plt.subplots()\n", 301 | "ax.set_yscale(\"log\")\n", 302 | "\n", 303 | "# data is in cm^2/molecule, need m^2, so 1e-4\n", 304 | "# need per kilo, so times avogadro which gives us per mol\n", 305 | "# co2 = 44 g/mol > \n", 306 | "\n", 307 | "ch4molw = 0.016\n", 308 | "co2molw = 0.044 # kg/mol\n", 309 | "h2omolw = 0.018\n", 310 | "\n", 311 | "# m2/mol / (kg/mol) = (m2/mol) * (mol/kg) = m2/kg -> m2/meter\n", 312 | "co2conv=(1e-4*6e23*dfco2.coef/co2molw)*ppmv2kgpm3(420, 1000*co2molw)\n", 313 | "h2oconv=(1e-4*6e23*dfh2o.coef/h2omolw)*ppmv2kgpm3(10000, 1000*h2omolw)\n", 314 | "ax.plot(dfh2o.nu, h2oconv, label='H2O', alpha=0.5)\n", 315 | "ax.plot(dfco2.nu, co2conv, label='CO2', alpha=0.5)\n", 316 | "ax.plot(dfco2.nu, co2conv+h2oconv, label='tot', alpha=0.5)\n", 317 | "\n", 318 | "ax.legend()" 319 | ] 320 | }, 321 | { 322 | "cell_type": "code", 323 | "execution_count": null, 324 | "id": "e5ab8b74", 325 | "metadata": {}, 326 | "outputs": [], 327 | "source": [ 328 | "fig,ax =plt.subplots()\n", 329 | "ax.set_yscale(\"log\")\n", 330 | "\n", 331 | "# data is in cm^2/molecule, need m^2, so 1e-4\n", 332 | "# need per kilo, so times avogadro which gives us per mol\n", 333 | "# co2 = 44 g/mol > \n", 334 | "\n", 335 | "ch4molw = 0.016\n", 336 | "co2molw = 0.044 # kg/mol\n", 337 | "h2omolw = 0.018\n", 338 | "mw=h2omolw\n", 339 | "df=dfh2o\n", 340 | "# m2/mol / (kg/mol) = (m2/mol) * (mol/kg) = m2/kg\n", 341 | "conv=(1e-4*6e23*df.coef/mw) # *ppmv2kgpm3(420, 1000*co2molw)\n", 342 | "\n", 343 | "ax2=ax.twinx()\n", 344 | "\n", 345 | "#conv=1 - np.exp(-conv)\n", 346 | "r=conv.rolling(5000, center=True)\n", 347 | "#ax.set_ylim(1e-10,1e6)\n", 348 | "ax2.set_yscale(\"log\")\n", 349 | "ax2.set_yticks([100,10,1,0.1,0.01,0.001,0.0001,0.00001])\n", 350 | "ax.set_xlim(0,3000)\n", 351 | "ax.plot(df.nu, r.quantile(0.75), label='75% quantile')\n", 352 | "#ax.plot(df.nu, r.mean(), color='black', label=\"Mean\")\n", 353 | "\n", 354 | "ax.plot(df.nu, r.median(), color='black', label=\"Median\")\n", 355 | "ax.plot(df.nu, r.quantile(0.25), label='25% quantile')\n", 356 | "ax.plot(df.nu, r.max(), ':', color='black')\n", 357 | "ax.plot(df.nu, r.min(), ':', color='black')\n", 358 | "ax.plot(df.nu, conv, alpha=0.2, lw=0.2)\n", 359 | "ax.axhline(y=conv.mean(), ls=':')\n", 360 | "ax.legend()\n", 361 | "ax2.set_ylim(ax.get_ylim()[0]*ppmv2kgpm3(10000, 1000*h2omolw),\n", 362 | " ax.get_ylim()[1]*ppmv2kgpm3(10000, 1000*h2omolw))\n", 363 | "\n", 364 | "#plt.grid()\n", 365 | "ax.set_xlabel(\"wavenumber, $cm^{-1}$\")\n", 366 | "ax.set_ylabel(\"$m^2/kg$\")\n", 367 | "ax2.set_ylabel(\"$m^{-1}$\")\n", 368 | "plt.title(\"$H_2O$ long wave infrared absorbance, including the continuum spectrum, p=1 atm, T=296K, 40% RH\")\n", 369 | "plt.savefig(\"water-linear.png\", dpi=200)" 370 | ] 371 | }, 372 | { 373 | "cell_type": "code", 374 | "execution_count": null, 375 | "id": "faaa6c27", 376 | "metadata": {}, 377 | "outputs": [], 378 | "source": [ 379 | "fig,ax =plt.subplots()\n", 380 | "#ax.set_yscale(\"log\")\n", 381 | "\n", 382 | "# data is in cm^2/molecule, need m^2, so 1e-4\n", 383 | "# need per kilo, so times avogadro which gives us per mol\n", 384 | "# co2 = 44 g/mol > \n", 385 | "\n", 386 | "ch4molw = 0.016\n", 387 | "co2molw = 0.044 # kg/mol\n", 388 | "h2omolw = 0.018\n", 389 | "mw=h2omolw\n", 390 | "# m2/mol / (kg/mol) = (m2/mol) * (mol/kg) = m2/kg\n", 391 | "conv=(1e-4*6e23*dfh2o.coef/mw)*ppmv2kgpm3(10000, 1000*h2omolw)\n", 392 | "econv=1.0 - np.exp(-500*conv)\n", 393 | "#econv2=1.0 - np.exp(-1000*conv)\n", 394 | "r=econv.rolling(2000, center=True, win_type='hamming')\n", 395 | "#r2=econv2.rolling(500, center=True, win_type='hamming')\n", 396 | "\n", 397 | "#ax.set_ylim(1e-10,1e6)\n", 398 | "ax.set_xlim(0,3000)\n", 399 | "#ax.plot(df.nu, r.quantile(0.75), label='75% quantile')\n", 400 | "ax.plot(df.nu, r.mean(), color='black', label=\"Mean\")\n", 401 | "#ax.plot(df.nu, r2.mean(), color='blue', label=\"Mean double\")\n", 402 | "\n", 403 | "#ax.plot(df.nu, r.median(), color='black', label=\"Median\")\n", 404 | "#ax.plot(df.nu, r.quantile(0.25), label='25% quantile')\n", 405 | "#ax.plot(df.nu, r.max(), ':', color='black')\n", 406 | "#ax.plot(df.nu, r.min(), ':', color='black')\n", 407 | "ax.plot(df.nu, econv, alpha=0.2, lw=0.2)\n", 408 | "#ax.axhline(y=econv.mean(), ls=':')\n", 409 | "#ax.axhline(y=econv2.mean(), ls=':', color='blue')\n", 410 | "\n", 411 | "ax.legend()\n", 412 | "plt.grid()\n", 413 | "ax.set_xlabel(\"wavenumber, $cm^{-1}$\")\n", 414 | "ax.set_ylabel(\"$fraction$\")\n", 415 | "plt.title(\"$H_2O$ long wave infrared absorption, RH 40%, p=1 atm, T=296K, 500 meters\")\n", 416 | "print(econv.mean())\n", 417 | "#print(econv2.mean())\n", 418 | "plt.savefig(\"water-absorption.png\", dpi=200)" 419 | ] 420 | }, 421 | { 422 | "cell_type": "code", 423 | "execution_count": null, 424 | "id": "e86f5c54", 425 | "metadata": { 426 | "scrolled": false 427 | }, 428 | "outputs": [], 429 | "source": [ 430 | "fig,ax =plt.subplots()\n", 431 | "#ax.set_yscale(\"log\")\n", 432 | "\n", 433 | "# data is in cm^2/molecule, need m^2, so 1e-4\n", 434 | "# need per kilo, so times avogadro which gives us per mol\n", 435 | "# co2 = 44 g/mol > \n", 436 | "\n", 437 | "ch4molw = 0.016\n", 438 | "co2molw = 0.044 # kg/mol\n", 439 | "h2omolw = 0.018\n", 440 | "mw=h2omolw\n", 441 | "# m2/mol / (kg/mol) = (m2/mol) * (mol/kg) = m2/kg\n", 442 | "conv=(1e-4*6e23*dfh2o.coef/mw)*ppmv2kgpm3(10000, 1000*h2omolw)\n", 443 | "econv=1.0 - np.exp(-500*conv)\n", 444 | "#econv2=1.0 - np.exp(-1000*conv)\n", 445 | "r=econv.rolling(2000, center=True, win_type='hamming')\n", 446 | "#r2=econv2.rolling(500, center=True, win_type='hamming')\n", 447 | "\n", 448 | "#ax.set_ylim(1e-10,1e6)\n", 449 | "ax.set_xlim(0,3000)\n", 450 | "ax.plot(dfh2o.nu, r.mean(), color='green', label=\"Mean $H_2O$\")\n", 451 | "\n", 452 | "\n", 453 | "mw=co2molw\n", 454 | "# m2/mol / (kg/mol) = (m2/mol) * (mol/kg) = m2/kg\n", 455 | "conv=(1e-4*6e23*dfco2.coef/mw)*ppmv2kgpm3(420, 1000*co2molw)\n", 456 | "econv=1.0 - np.exp(-500*conv)\n", 457 | "r=econv.rolling(2000, center=True, win_type='hamming')\n", 458 | "\n", 459 | "ax.plot(dfco2.nu, r.mean(), color='black', label=\"Mean $CO_2$\")\n", 460 | "\n", 461 | "\n", 462 | "ax.legend()\n", 463 | "plt.grid()\n", 464 | "ax.set_xlabel(\"wavenumber, $cm^{-1}$\")\n", 465 | "ax.set_ylabel(\"$fraction$\")\n", 466 | "plt.title(\"$H_2O$ (RH 40%) and $CO_2$ (420 PPM) long wave infrared absorption, p=1 atm, T=296K, 500 meters\")\n", 467 | "print(econv.mean())\n", 468 | "#print(econv2.mean())\n", 469 | "plt.savefig(\"water-co2-absorption.png\", dpi=200)" 470 | ] 471 | }, 472 | { 473 | "cell_type": "code", 474 | "execution_count": null, 475 | "id": "577ae5e9", 476 | "metadata": { 477 | "scrolled": false 478 | }, 479 | "outputs": [], 480 | "source": [ 481 | "fig,ax =plt.subplots()\n", 482 | "#ax.set_yscale(\"log\")\n", 483 | "\n", 484 | "# data is in cm^2/molecule, need m^2, so 1e-4\n", 485 | "# need per kilo, so times avogadro which gives us per mol\n", 486 | "# co2 = 44 g/mol > \n", 487 | "\n", 488 | "ch4molw = 0.016\n", 489 | "co2molw = 0.044 # kg/mol\n", 490 | "h2omolw = 0.018\n", 491 | "mw=h2omolw\n", 492 | "# m2/mol / (kg/mol) = (m2/mol) * (mol/kg) = m2/kg\n", 493 | "conv=(1e-4*6e23*dfh2o.coef/mw)*ppmv2kgpm3(10000, 1000*h2omolw)\n", 494 | "econv1=1.0 - np.exp(-500*conv)\n", 495 | "#econv2=1.0 - np.exp(-1000*conv)\n", 496 | "r=econv1.rolling(2000, center=True, win_type='hamming')\n", 497 | "#r2=econv2.rolling(500, center=True, win_type='hamming')\n", 498 | "\n", 499 | "#ax.set_ylim(1e-10,1e6)\n", 500 | "ax.set_xlim(0,3000)\n", 501 | "ax.plot(dfh2o.nu, r.mean(), color='grey')\n", 502 | "\n", 503 | "\n", 504 | "mw=co2molw\n", 505 | "# m2/mol / (kg/mol) = (m2/mol) * (mol/kg) = m2/kg\n", 506 | "conv=(1e-4*6e23*dfco2.coef/mw)*ppmv2kgpm3(420, 1000*co2molw)\n", 507 | "econv2=1.0 - np.exp(-500*conv)\n", 508 | "r=econv2.rolling(2000, center=True, win_type='hamming')\n", 509 | "\n", 510 | "conv3=(1e-4*6e23*dfco2.coef/mw)*ppmv2kgpm3(840, 1000*co2molw)\n", 511 | "econv3=1.0 - np.exp(-500*conv3)\n", 512 | "\n", 513 | "\n", 514 | "ax.plot(dfco2.nu, r.mean(), ':', color='grey')\n", 515 | "\n", 516 | "ax2 = ax.twinx()\n", 517 | "ax.plot(layer.nu, 2.3*layer.inpower, color=\"brown\", label=\"Outgoing surface radiation\")\n", 518 | "ax2.plot(layer.nu, integrate(layer.nu, layer.inpower), label=\"Cumulative incoming power\")\n", 519 | "ax2.plot(layer.nu, integrate(layer.nu, layer.inpower*(1-econv1)*(1-econv2)), label=\"Cumulative outgoing radiation\")\n", 520 | "ax2.plot(layer.nu, integrate(layer.nu, layer.inpower*(1-econv1)*(1-econv3)), label=\"Cumulative outgoing radiation double $CO_2$\")\n", 521 | "\n", 522 | "ax2.set_ylabel(\"$W/m^2$\")\n", 523 | "ax.legend(loc=2)\n", 524 | "ax2.legend(loc=1)\n", 525 | "plt.grid()\n", 526 | "ax.set_xlabel(\"wavenumber, $cm^{-1}$\")\n", 527 | "ax.set_ylabel(\"$fraction$\")\n", 528 | "plt.title(\"$H_2O$ (RH 40%) and $CO_2$ (420 PPM) long wave infrared absorption, p=1 atm, T=296K, 500 meters\")\n", 529 | "print(econv.mean())\n", 530 | "#print(econv2.mean())\n", 531 | "plt.savefig(\"water-co2-absorption-cumul.png\", dpi=250)" 532 | ] 533 | }, 534 | { 535 | "cell_type": "code", 536 | "execution_count": null, 537 | "id": "d5776a93", 538 | "metadata": {}, 539 | "outputs": [], 540 | "source": [ 541 | "0.4*getPPMVH2O(T=273-24, P=0.45)" 542 | ] 543 | }, 544 | { 545 | "cell_type": "code", 546 | "execution_count": null, 547 | "id": "0a0476d0", 548 | "metadata": {}, 549 | "outputs": [], 550 | "source": [ 551 | "# −30C, p=0.45, 6km high \n", 552 | "dfco2high=getGasAbsorptionPerMolecule(\"CO2\", 420, T=273-24, p=0.45)\n", 553 | "dfh2ohigh=getGasAbsorptionPerMolecule(\"H2O\", 608, T=273-24, p=0.45)\n" 554 | ] 555 | }, 556 | { 557 | "cell_type": "code", 558 | "execution_count": null, 559 | "id": "f9f89822", 560 | "metadata": { 561 | "scrolled": false 562 | }, 563 | "outputs": [], 564 | "source": [ 565 | "fig,ax =plt.subplots()\n", 566 | "#ax.set_yscale(\"log\")\n", 567 | "\n", 568 | "# data is in cm^2/molecule, need m^2, so 1e-4\n", 569 | "# need per kilo, so times avogadro which gives us per mol\n", 570 | "# co2 = 44 g/mol > \n", 571 | "\n", 572 | "ch4molw = 0.016\n", 573 | "co2molw = 0.044 # kg/mol\n", 574 | "h2omolw = 0.018\n", 575 | "mw=co2molw\n", 576 | "# m2/mol / (kg/mol) = (m2/mol) * (mol/kg) = m2/kg\n", 577 | "conv1=(1e-4*6e23*dfco2high.coef/mw)*ppmv2kgpm3(420, 1000*co2molw, p=0.45, T=273-24)\n", 578 | "econv1=1.0 - np.exp(-500*conv1)\n", 579 | "\n", 580 | "conv2=(1e-4*6e23*dfh2ohigh.coef/mw)*ppmv2kgpm3(608, 1000*h2omolw, p=0.45, T=273-24)\n", 581 | "econv2=1.0 - np.exp(-500*conv2)\n", 582 | "\n", 583 | "econv3=1.0 - np.exp(-1000*conv)\n", 584 | "\n", 585 | "\n", 586 | "r=econv1.rolling(1000, center=True, win_type='hamming')\n", 587 | "r2=econv2.rolling(2000, center=True, win_type='hamming')\n", 588 | "r3=econv3.rolling(1000, center=True, win_type='hamming')\n", 589 | "\n", 590 | "#ax.set_ylim(1e-10,1e6)\n", 591 | "ax.set_xlim(0,3000)\n", 592 | "ax.plot(dfco2high.nu, r.mean(), label='Absorption $CO_2$ at 420ppm', color='black')\n", 593 | "\n", 594 | "ax.plot(dfh2ohigh.nu, r2.mean(), label='Absorption $H_2O$ at 608ppm', color='green')\n", 595 | "\n", 596 | "#ax.plot(dfco2high.nu, r3.mean(), ':', label='Absorption $CO_2$ at 840ppm', color='black')\n", 597 | "ax.legend()\n", 598 | "ax.set_ylabel(\"fraction\")\n", 599 | "ax.set_xlabel(\"wavenumber, $cm^{-1}$\")\n", 600 | "ax.set_ylim(0,1.1)\n", 601 | "#ax.set_xlim(550, 800)\n", 602 | "plt.title(\"Aborption by 500 meters of atmosphere at 6km high, T=249K, P=0.45atm, RH 40%\")\n", 603 | "plt.grid()\n", 604 | "# arrows for the -double plot\n", 605 | "#ax.add_patch(Arrow(646,0.8, 0, 0.12, color='red', width=3 ))\n", 606 | "#ax.add_patch(Arrow(640.8,0.6, -4.2, 0, color='red', width=0.04 ))\n", 607 | "\n", 608 | "#ax.add_patch(Arrow(691.9, 0.8, 0, 0.12, color='red', width=4 ))\n", 609 | "#ax.add_patch(Arrow(698.8, 0.6, 4.2, 0, color='red', width=0.04 ))\n", 610 | "\n", 611 | "\n", 612 | "\n", 613 | "#plt.savefig(\"6km-high-absorption-double.png\", dpi=200)\n", 614 | "plt.savefig(\"6km-high-absorption.png\", dpi=200)" 615 | ] 616 | }, 617 | { 618 | "cell_type": "code", 619 | "execution_count": null, 620 | "id": "e046a562", 621 | "metadata": {}, 622 | "outputs": [], 623 | "source": [ 624 | "dfco2veryhigh=getGasAbsorptionPerMolecule(\"CO2\", 420, T=273-44, p=0.05)\n" 625 | ] 626 | }, 627 | { 628 | "cell_type": "code", 629 | "execution_count": null, 630 | "id": "01285787", 631 | "metadata": {}, 632 | "outputs": [], 633 | "source": [ 634 | "fig,ax =plt.subplots()\n", 635 | "#ax.set_yscale(\"log\")\n", 636 | "\n", 637 | "co2molw = 0.044 # kg/mol\n", 638 | "mw=co2molw\n", 639 | "# m2/mol / (kg/mol) = (m2/mol) * (mol/kg) = m2/kg\n", 640 | "conv1=(1e-4*6e23*dfco2veryhigh.coef/mw)*ppmv2kgpm3(420, 1000*co2molw, p=0.05, T=273-44)\n", 641 | "econv1=1.0 - np.exp(-500*conv1)\n", 642 | "r=econv1.rolling(1000, center=True, win_type='hamming')\n", 643 | "\n", 644 | "#ax.set_ylim(1e-10,1e6)\n", 645 | "ax.set_xlim(0,3000)\n", 646 | "ax.plot(dfco2veryhigh.nu, r.mean(), label='Absorption $CO_2$ at 420ppm', color='black')\n", 647 | "\n", 648 | "#ax.plot(dfco2high.nu, r3.mean(), ':', label='Absorption $CO_2$ at 840ppm', color='black')\n", 649 | "ax.legend()\n", 650 | "ax.set_ylabel(\"fraction\")\n", 651 | "ax.set_xlabel(\"wavenumber, $cm^{-1}$\")\n", 652 | "ax.set_ylim(0,1.1)\n", 653 | "#ax.set_xlim(550, 800)\n", 654 | "plt.title(\"Aborption by 500 meters of atmosphere at 20km high, T=233K, P=0.05atm\")\n", 655 | "plt.grid()\n", 656 | "# arrows for the -double plot\n", 657 | "#ax.add_patch(Arrow(646,0.8, 0, 0.12, color='red', width=3 ))\n", 658 | "#ax.add_patch(Arrow(640.8,0.6, -4.2, 0, color='red', width=0.04 ))\n", 659 | "\n", 660 | "#ax.add_patch(Arrow(691.9, 0.8, 0, 0.12, color='red', width=4 ))\n", 661 | "#ax.add_patch(Arrow(698.8, 0.6, 4.2, 0, color='red', width=0.04 ))\n" 662 | ] 663 | }, 664 | { 665 | "cell_type": "code", 666 | "execution_count": null, 667 | "id": "894c4718", 668 | "metadata": {}, 669 | "outputs": [], 670 | "source": [ 671 | "plt.figure()\n", 672 | "\n", 673 | "plt.xlim(0, 3000)\n", 674 | "plt.ylim(0, 20)\n", 675 | "plt.ylabel(\"km\")\n", 676 | "plt.xlabel(\"$cm^{-1}$\")\n", 677 | "plt.grid()\n", 678 | "plt.title(\"Sketch of height where the atmosphere turns transparent for outgoing radiation\")\n", 679 | "plt.savefig(\"transparency-sketch.svg\")" 680 | ] 681 | }, 682 | { 683 | "cell_type": "code", 684 | "execution_count": null, 685 | "id": "ded53d25", 686 | "metadata": {}, 687 | "outputs": [], 688 | "source": [ 689 | "absorbances={}" 690 | ] 691 | }, 692 | { 693 | "cell_type": "code", 694 | "execution_count": null, 695 | "id": "79b27c52", 696 | "metadata": {}, 697 | "outputs": [], 698 | "source": [ 699 | "# depth in meters, pressure in atmospheres\n", 700 | "def getGasTransmittance(gas, ppmv, p=1., T=296., depth=1000):\n", 701 | " nu,coef = absorptionCoefficient_HT(SourceTables=gas, \n", 702 | " Diluent={'air':1.0-ppmv/1000000.0, 'self': ppmv/1000000.0}, \n", 703 | " Environment={'p':p,'T':T}, HITRAN_units=False)\n", 704 | " # coef units are \"per centimeter\" since HITRAN_units=False\n", 705 | " # otherwise cm^2/molecule\n", 706 | " if(gas==\"H2O\"):\n", 707 | " h2ocont=xr.open_dataset('/home/ahu/git/MT_CKD_H2O/run_example/mt_ckd_h2o_output-radflag.nc').to_dataframe()\n", 708 | " h2ocont=h2ocont.reindex(h2ocont.index.union(nu)).interpolate().fillna(0).reindex(nu)\n", 709 | " # units? XXX\n", 710 | " coef = coef + (h2ocont.self_absorption*18*6e23*0.01*ppmv/10000000.0).array\n", 711 | " \n", 712 | " absorbances[gas]=pandas.DataFrame({\"nu\": nu, \"coef\": coef})\n", 713 | " \n", 714 | " # optical length l below is in centimeters, we calculate the column of gas\n", 715 | " tmp1, tmp2 =transmittanceSpectrum(nu,coef, Environment={'l':depth*100*ppmv/1000000.0}) \n", 716 | " df=pandas.DataFrame({\"nu\": tmp1, \"trans\": tmp2}).set_index(\"nu\")\n", 717 | " return df.reindex(df.index.union(layer.nu)).interpolate().fillna(1).reindex(layer.nu).trans.array\n", 718 | "\n", 719 | "layer[\"co2trans\"] = getGasTransmittance(\"CO2\", 420)\n", 720 | "#layer[\"co2transdouble\"] = getGasTransmittance(\"CO2\", 840)" 721 | ] 722 | }, 723 | { 724 | "cell_type": "code", 725 | "execution_count": null, 726 | "id": "0b4bc746", 727 | "metadata": {}, 728 | "outputs": [], 729 | "source": [ 730 | "layer[\"h2otrans\"]= getGasTransmittance(\"H2O\", 10000)" 731 | ] 732 | }, 733 | { 734 | "cell_type": "code", 735 | "execution_count": null, 736 | "id": "45acd6c1", 737 | "metadata": {}, 738 | "outputs": [], 739 | "source": [ 740 | "fig,ax = plt.subplots(4,1)\n", 741 | "ax[0].plot(absorbances[\"H2O\"].nu, np.exp(-absorbances[\"H2O\"].coef*5000), label=\"H2O\")\n", 742 | "ax[1].plot(absorbances[\"N2O\"].nu, np.exp(-absorbances[\"N2O\"].coef*10), label=\"N2O\")\n", 743 | "ax[2].plot(absorbances[\"CO2\"].nu, np.exp(-absorbances[\"CO2\"].coef*100), label=\"CO2\")\n", 744 | "ax[3].plot(absorbances[\"CH4\"].nu, np.exp(-absorbances[\"CH4\"].coef*100), label=\"CH4\")\n", 745 | "#ax.twinx().plot(layer.nu, layer.inpower, ':', label=\"Surface radiation\", color='black')\n", 746 | "\n", 747 | "ax.legend()" 748 | ] 749 | }, 750 | { 751 | "cell_type": "code", 752 | "execution_count": null, 753 | "id": "48db5692", 754 | "metadata": {}, 755 | "outputs": [], 756 | "source": [ 757 | "layer[\"ch4trans\"]= getGasTransmittance(\"CH4\", 1.8)" 758 | ] 759 | }, 760 | { 761 | "cell_type": "code", 762 | "execution_count": null, 763 | "id": "b8df943a", 764 | "metadata": {}, 765 | "outputs": [], 766 | "source": [ 767 | "layer[\"n2otrans\"]= getGasTransmittance(\"N2O\", 0.4)" 768 | ] 769 | }, 770 | { 771 | "cell_type": "code", 772 | "execution_count": null, 773 | "id": "8b49396f", 774 | "metadata": { 775 | "scrolled": false 776 | }, 777 | "outputs": [], 778 | "source": [ 779 | "plt.figure()\n", 780 | "plt.plot(layer.nu, (layer.co2trans * layer.n2otrans *layer.h2otrans* layer.ch4trans * layer.inpower).rolling(1000, center=True, win_type='hamming').mean(), label=\"Current CO2\")\n", 781 | "#plt.plot(layer.nu, (layer.co2transdouble * layer.h2otrans*layer.inpower).rolling(1000, center=True, win_type='hamming').mean(), label=\"Double CO2\")\n", 782 | "plt.plot(layer.nu, layer.inpower, label=\"Surface radiation\")\n", 783 | "plt.legend()\n", 784 | "plt.grid()" 785 | ] 786 | }, 787 | { 788 | "cell_type": "code", 789 | "execution_count": null, 790 | "id": "64eef737", 791 | "metadata": {}, 792 | "outputs": [], 793 | "source": [ 794 | "plt.figure()\n", 795 | "plt.plot(layer.nu, integrate(layer.nu, layer.n2otrans*layer.co2trans * layer.h2otrans * layer.ch4trans * layer.inpower), label=\"Current CO2\")\n", 796 | "plt.plot(layer.nu, integrate(layer.nu, layer.n2otrans*layer.co2transdouble * layer.h2otrans * layer.ch4trans *layer.inpower), label=\"Double CO2\")\n", 797 | "plt.plot(layer.nu, integrate(layer.nu, layer.inpower), label=\"Surface radiation\")\n", 798 | "\n", 799 | "plt.legend()\n", 800 | "plt.grid()" 801 | ] 802 | }, 803 | { 804 | "cell_type": "code", 805 | "execution_count": null, 806 | "id": "4f5f9fdf", 807 | "metadata": {}, 808 | "outputs": [], 809 | "source": [ 810 | "plt.figure()\n", 811 | "plt.plot(layer.nu, layer.ch4trans)\n", 812 | "plt.plot(layer.nu, layer.h2otrans)\n", 813 | "plt.plot(layer.nu, layer.co2trans)\n", 814 | "plt.plot(layer.nu, layer.n2otrans)" 815 | ] 816 | } 817 | ], 818 | "metadata": { 819 | "kernelspec": { 820 | "display_name": "Python 3 (ipykernel)", 821 | "language": "python", 822 | "name": "python3" 823 | }, 824 | "language_info": { 825 | "codemirror_mode": { 826 | "name": "ipython", 827 | "version": 3 828 | }, 829 | "file_extension": ".py", 830 | "mimetype": "text/x-python", 831 | "name": "python", 832 | "nbconvert_exporter": "python", 833 | "pygments_lexer": "ipython3", 834 | "version": "3.10.12" 835 | } 836 | }, 837 | "nbformat": 4, 838 | "nbformat_minor": 5 839 | } 840 | --------------------------------------------------------------------------------