diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml new file mode 100644 index 0000000..166bf56 --- /dev/null +++ b/.github/workflows/tests.yaml @@ -0,0 +1,52 @@ +name: Tests for JANUS + +on: + push: + pull_request: + workflow_dispatch: + +jobs: + test: + + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.10', '3.11', '3.12'] + + steps: + - uses: actions/checkout@v4 + + - name: Get SOCRATES + uses: actions/checkout@v4 + with: + repository: 'FormingWorlds/SOCRATES' + path: 'SOCRATES' + + - name: Setup system + run: | + sudo apt update + sudo apt-get install libnetcdff-dev netcdf-bin gfortran gcc + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - name: Build SOCRATES + run: | + export LD_LIBRARY_PATH="" + cd SOCRATES + ./configure + ./build_code + source set_rad_env + cd .. + + - name: Build JANUS + run: | + pip install -e . + + - name: Test with pytest + run: | + pip install pytest + source SOCRATES/set_rad_env + pytest diff --git a/.gitignore b/.gitignore index c25423f..f9824ab 100644 --- a/.gitignore +++ b/.gitignore @@ -17,22 +17,15 @@ Tprofile.pdf .DS_Store .vscode __pycache__/ -rad_trans/socrates_code/* -!rad_trans/socrates_code/.gitkeep nogit_* -spectral_files/shared +src/janus/data/spectral_files/shared fwl_janus.egg-info -# Ignore SOCRATES code files -rad_trans/socrates_code/* - # Video files *.mp4 -spectral_files/sp_b318_HITRAN_a16_gen_original -spectral_files/sp_b318_HITRAN_a16_2203/temp -spectral_files/sp_b318_HITRAN_a16_2203 -socrates_2002.tar.xz -set_rad_env +src/janus/data/spectral_files/sp_b318_HITRAN_a16_gen_original +src/janus/data/spectral_files/sp_b318_HITRAN_a16_2203/temp +src/janus/data/spectral_files/sp_b318_HITRAN_a16_2203 output/ utils/*.ipynb* diff --git a/README.md b/README.md index 465587b..6774d53 100644 --- a/README.md +++ b/README.md @@ -17,17 +17,12 @@ https://proteus-code.readthedocs.io * LS - Laurent Soucasse (l.soucasse@esciencecenter.nl) ### Repository structure -* `README.md` - This file -* `JANUS.env` - Sets environment flags to run the code -* `src/janus/SocRadConv.py` - Main JANUS Python script -* `src/janus/data/luminosity_tracks/` - Stellar evolution data -* `src/janus/data/spectral_files/` - Spectral files for SOCRATES -* `src/janus/modules/` - Utility python scripts -* `src/janus/plotting_tools/` - Plotting scripts -* `src/janus/utils/` - Utility python scripts -* `tests/demo_runaway_greenhouse.py` - Demonstrate pure-steam runaway greenhouse OLR curve -* `tests/demo_instellation.py` - Calculate fluxes (and temperatures) for different instellations -* `tools/` - Useful tools +* `README.md` - This file +* `src/janus/data/` - Janus data files +* `src/janus/modules/` - Utility python scripts +* `src/janus/utils/` - Utility python scripts +* `examples/` - Typical use scripts +* `tools/` - Useful tools ### Developer installation instructions 1. Download and install Socrates @@ -36,14 +31,11 @@ https://proteus-code.readthedocs.io * `./configure` * `./build-code` * `source set_rad_env` + * `cd ..` 2. Download and install Janus * `git clone git@github.com:FormingWorlds/JANUS.git` * `cd JANUS` * `pip install -e .` ### Run instructions -Only attempt to run JANUS after you have followed all of the instructions in INSTALL.md -If using a fresh shell, it is necessary to perform the following steps: -1. `source JANUS.env` -2. `conda activate janus` -Then you can run the code by running: `python SocRadConv.py` +In the examples folder you can find python scripts showing typical usecases/workflows of atmosphere modelling with Janus. diff --git a/src/janus/SocRadConv.py b/examples/SocRadConv.py similarity index 100% rename from src/janus/SocRadConv.py rename to examples/SocRadConv.py diff --git a/tests/demo_instellation.py b/examples/demo_instellation.py similarity index 100% rename from tests/demo_instellation.py rename to examples/demo_instellation.py diff --git a/tests/demo_runaway_greenhouse.py b/examples/demo_runaway_greenhouse.py similarity index 100% rename from tests/demo_runaway_greenhouse.py rename to examples/demo_runaway_greenhouse.py diff --git a/src/janus/data/tests/data_instellation.csv b/src/janus/data/tests/data_instellation.csv new file mode 100644 index 0000000..f2d03cf --- /dev/null +++ b/src/janus/data/tests/data_instellation.csv @@ -0,0 +1,8 @@ +# r [AU], S_0 [W m-2], OLR [W m-2], net [W m-2], ts [K], tr[K] +3.00000e-01,2.34296e+03,1.94531e+03,-7.41838e+01,3.00037e+03,4.19568e+02 +4.83333e-01,9.02638e+02,8.44987e+02,6.70043e+01,2.99966e+03,3.30552e+02 +6.66667e-01,4.74449e+02,5.02546e+02,9.36275e+01,2.99953e+03,2.81455e+02 +8.50000e-01,2.91856e+02,4.21740e+02,1.70194e+02,2.99915e+03,2.49261e+02 +1.03333e+00,1.97481e+02,4.17035e+02,2.46829e+02,2.99877e+03,2.26070e+02 +1.21667e+00,1.42450e+02,4.16624e+02,2.93848e+02,2.99853e+03,2.08342e+02 +1.40000e+00,1.07585e+02,4.16473e+02,3.23747e+02,2.99838e+03,1.94222e+02 diff --git a/src/janus/data/tests/data_runaway_greenhouse.csv b/src/janus/data/tests/data_runaway_greenhouse.csv new file mode 100644 index 0000000..b7c8618 --- /dev/null +++ b/src/janus/data/tests/data_runaway_greenhouse.csv @@ -0,0 +1,21 @@ +# T_surf [K], OLR [W m-2] + 200.00000000, 90.72589 + 336.84210526, 277.55026 + 473.68421053, 277.47986 + 610.52631579, 277.27164 + 747.36842105, 277.35020 + 884.21052632, 277.18277 +1021.05263158, 277.37436 +1157.89473684, 277.13092 +1294.73684211, 277.14750 +1431.57894737, 277.30030 +1568.42105263, 277.64148 +1705.26315789, 279.04703 +1842.10526316, 285.55804 +1978.94736842, 308.2808 +2115.78947368, 375.7102 +2252.63157895, 548.4764 +2389.47368421, 954.2722 +2526.31578947, 1821.3682 +2663.15789474, 3517.7295 +2800.00000000, 6636.2110 diff --git a/tests/test_instellation.py b/tests/test_instellation.py new file mode 100755 index 0000000..ea37dd6 --- /dev/null +++ b/tests/test_instellation.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 + +from importlib.resources import files +import os, shutil +import numpy as np + +from janus.modules.stellar_luminosity import InterpolateStellarLuminosity +from janus.modules.solve_pt import * +from janus.utils.socrates import CleanOutputDir +from janus.utils.atmosphere_column import atmos +import janus.utils.StellarSpectrum as StellarSpectrum +import janus.utils.phys as phys +from janus.utils.ReadSpectralFile import ReadBandEdges + +def run_once(sep, dirs, T_magma, P_surf, skin_d, band_edges): + + # Planet + time = { "planet": 0., "star": 100e+6 } # yr, + star_mass = 1.0 # M_sun, mass of star + pl_radius = 6.371e6 # m, planet radius + pl_mass = 5.972e24 # kg, planet mass + + # Boundary conditions for pressure & temperature + P_top = 1.0 # Pa + + # Define volatiles by mole fractions + vol_mixing = { + "H2O" : 1.0, + "CO2" : 0.0, + "N2" : 0.0 + } + + rscatter = True + A_B = 0.1 # bond albedo + A_S = 0.1 + inst_sf = 3.0/8.0 + + ##### Function calls + + S_0 = InterpolateStellarLuminosity(star_mass, time, sep) + + zenith_angle = 48.19 # cronin+14 (also for scaling by a factor of 3/8 ^^) + + T_eqm = (S_0 * inst_sf * (1.0 - A_B) /phys.sigma)**(1.0/4.0) + T_trpp = T_eqm * (0.5**0.25) # radiative skin temperature + + # Create atmosphere object + atm = atmos(T_magma, P_surf * 1e5, P_top, pl_radius, pl_mass, band_edges, + vol_mixing=vol_mixing, trppT=T_trpp, req_levels=150) + atm.albedo_pl = A_B + atm.albedo_s = A_S + atm.inst_sf = inst_sf + atm.zenith_angle = zenith_angle + atm.instellation = S_0 + atm.skin_d = skin_d + atm.tmp_magma = T_magma + + # Do rad trans + atm = MCPA_CBL(dirs, atm, False, rscatter, T_surf_max=9.0e99, T_surf_guess = T_trpp+100) + + return [atm.SW_flux_down[0], atm.LW_flux_up[0], atm.net_flux[0], atm.ts, T_trpp] + +def test_instellation(): + + # Set up dirs + if os.environ.get('RAD_DIR') == None: + raise Exception("Socrates environment variables not set! Have you installed Socrates and sourced set_rad_env?") + dirs = { + "janus": str(files("janus"))+"/", + "output": os.path.abspath(os.getcwd())+"/output/" + } + + # Tidy directory + if os.path.exists(dirs["output"]): + shutil.rmtree(dirs["output"]) + os.mkdir(dirs["output"]) + + # Setup spectral file + print("Inserting stellar spectrum") + StellarSpectrum.InsertStellarSpectrum( + dirs["janus"]+"data/spectral_files/Oak/Oak.sf", + dirs["janus"]+"data/spectral_files/stellar_spectra/Sun_t4_4Ga_claire_12.txt", + dirs["output"] + ) + band_edges = ReadBandEdges(dirs["output"]+"star.sf") + + # Set parameters + P_surf = 280.0 # surface pressure [bar] + T_magma = 3000.0 # magma temperature [K] + skin_d = 1e-2 # conductive skin thickness [m] + + #Get reference data + ref = np.loadtxt(dirs["janus"]+"data/tests/data_instellation.csv", + dtype=float, skiprows=1, delimiter=',') + + r_arr = np.linspace(0.3, 1.4, 7) # orbital distance range [AU] + for i in range(7): + print("Orbital separation = %.2f AU" % r_arr[i]) + out = run_once(r_arr[i], dirs, T_magma, P_surf, skin_d, band_edges) + print(out) + print(ref[i][1:6]) + np.testing.assert_allclose(out, ref[i][1:6], rtol=1e-5, atol=0) + + # Tidy + CleanOutputDir(os.getcwd()) + CleanOutputDir(dirs['output']) diff --git a/tests/test_runaway_greenhouse.py b/tests/test_runaway_greenhouse.py new file mode 100755 index 0000000..b5e6a0a --- /dev/null +++ b/tests/test_runaway_greenhouse.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 + +import os, shutil +import numpy as np +from matplotlib.ticker import MultipleLocator +from importlib.resources import files + +from janus.modules.stellar_luminosity import InterpolateStellarLuminosity +from janus.modules.solve_pt import RadConvEqm +from janus.utils.socrates import CleanOutputDir + +from janus.utils.atmosphere_column import atmos +import janus.utils.StellarSpectrum as StellarSpectrum +from janus.utils.ReadSpectralFile import ReadBandEdges + + +def run_once(T_surf, dirs, band_edges): + + # Planet + time = { "planet": 0., "star": 4.5e9 } # yr, + star_mass = 1.0 # M_sun, mass of star + mean_distance = 0.5 # au, orbital distance + pl_radius = 6.371e6 # m, planet radius + pl_mass = 5.972e24 # kg, planet mass + + # Boundary conditions for pressure & temperature + P_top = 1.0 # Pa + + # Define volatiles by mole fractions + P_surf = 300 * 1e5 + vol_mixing = { + "H2O" : 1.0, + "CO2" : 0.0, + "N2" : 0.0 + } + + # Rayleigh scattering on/off + rscatter = False + + # Tropopause calculation + trppT = 0.0 # Fixed tropopause value if not calculated dynamically + + ##### Function calls + + # Create atmosphere object + atm = atmos(T_surf, P_surf, P_top, pl_radius, pl_mass, band_edges, vol_mixing=vol_mixing, trppT=trppT) + + # Compute stellar heating + atm.instellation = InterpolateStellarLuminosity(star_mass, time, mean_distance) + + # Do rad trans + _, atm_moist = RadConvEqm(dirs, time, atm, standalone=True, cp_dry=False, trppD=False, rscatter=rscatter) + + return [atm_moist.LW_flux_up[0]] + +def test_runaway_greenhouse(): + + # Set up dirs + if os.environ.get('RAD_DIR') == None: + raise Exception("Socrates environment variables not set! Have you installed Socrates and sourced set_rad_env?") + dirs = { + "janus": str(files("janus"))+"/", + "output": os.path.abspath(os.getcwd())+"/output/" + } + + # Tidy directory + if os.path.exists(dirs["output"]): + shutil.rmtree(dirs["output"]) + os.mkdir(dirs["output"]) + + # Setup spectral file + print("Inserting stellar spectrum") + StellarSpectrum.InsertStellarSpectrum( + dirs["janus"]+"data/spectral_files/Oak/Oak.sf", + dirs["janus"]+"data/spectral_files/stellar_spectra/Sun_t4_4Ga_claire_12.txt", + dirs["output"] + ) + band_edges = ReadBandEdges(dirs["output"]+"star.sf") + + #Get reference values + OLR_ref = np.loadtxt(dirs["janus"]+"data/tests/data_runaway_greenhouse.csv", + dtype=float, skiprows=1, delimiter=',') + + #Run Janus + Ts_arr = np.linspace(200, 2800, 20) + for i in range(20): + out = run_once(Ts_arr[i], dirs, band_edges) + print("Output %s; Reference %s" % (out, OLR_ref[i][1])) + np.testing.assert_allclose(out, OLR_ref[i][1], rtol=1e-5, atol=0) + + # Tidy + CleanOutputDir(os.getcwd()) + CleanOutputDir(dirs['output'])