diff --git a/CHANGELOG.md b/CHANGELOG.md index 3cc0f300..e02d5463 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] ### Added -- None yet - +- Added new main_input class to rayleigh_diagnostics.py. Usage examples are provided in Rayleigh/examples/main_input_class. \[Nick Featherstone; 1-11-2022; [#352](https://github.com/geodynamics/Rayleigh/pull/352)\] ### Changed - The rotation rate is now accounted for when computing the maximum allowable timestep. Now, the timestep may not exceed CFLMax/(c1*4), where CFLMax is the CFL safety factor. c1 is the 1st Rayleigh constant, effectively the inverse of the rotational timescale (c1 = 2 Omega for reference_type=2 and 2/Ek for reference_type=1). \[Nick Featherstone; 12-11-2021; [#348](https://github.com/geodynamics/Rayleigh/pull/348)\] diff --git a/examples/main_input_class/jobinfo.txt b/examples/main_input_class/jobinfo.txt new file mode 100755 index 00000000..3f899d1d --- /dev/null +++ b/examples/main_input_class/jobinfo.txt @@ -0,0 +1,307 @@ + +=========================================================================== + Job Information +=========================================================================== +Date = 2021-08-25 +Time = 15:55:51 +Path = + +=========================================================================== + Compiler Information +=========================================================================== +Compiler Location = /custom/miniconda3/envs/radev/bin/mpifort +Compiler Version = GNU Fortran (GCC) 9.4.0 + +Compiler Flags = -O3 -ffixed-line-length-132 -DGNU_COMPILER -march=haswell +Compiler Library = -L/custom/miniconda3/envs/radev/lib -Wl,--no-as-needed -lmkl_gf_lp64 -lmkl_sequential -lmkl_core -lpthread -lm -ldl -lstdc++ + +=========================================================================== + Build Information +=========================================================================== +Build Date = Wed 25 Aug 2021 03:47:49 PM MDT +Build Machine = Linux Sanitas 5.11.0-27-generic #29~20.04.1-Ubuntu SMP Wed Aug 11 15:58:17 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux +Build Directory = /home/feathern/devel/forks/Nick/Rayleigh +Custom Directory = + +Build Version = opt +Git Hash = 624e326f07a2f81f76ef41604977a584427ae570 +++ Mon Aug 16 13:16:33 2021 -0600 +Git URL = git@github.com:feathern/Rayleigh.git +Git Branch = timing + +=========================================================================== + Preamble Information +=========================================================================== + MPI + NCPU : 8 + NPROW : 2 + NPCOL : 4 + + Grid + N_R : 64 + N_THETA : 96 + Ell_MAX : 63 + R_MIN : 5.3846E-01 + R_MAX : 1.5385E+00 + Chebyshev Domains : 1 + + Domain 1 + Grid points : 64 + Dealiased Polynomials : 42 + Domain Lower Bound : 5.3846E-01 + Domain Upper Bound : 1.5385E+00 + + Reference State + Reference type : Boussinesq (Non-dimensional) + Rayleigh Number : 1.0000E+05 + Ekman Number : 1.0000E-03 + Prandtl Number : 1.0000E+00 + +=========================================================================== + Namelist Information +=========================================================================== +&PROBLEMSIZE_NAMELIST + N_R=64 , + N_THETA=96 , + NPROW=2 , + NPCOL=4 , + RMIN= 0.53846153846153832 , + RMAX= 1.5384615384615383 , + NPOUT=1 , + PRECISE_BOUNDS=-1 , + GRID_TYPE=2 , + L_MAX=63 , + N_L=64 , + ASPECT_RATIO= 0.34999999999999998 , + SHELL_DEPTH= 1.0000000000000000 , + NCHEBY=64 , 255*-1 , + DOMAIN_BOUNDS= 0.53846153846153832 , 1.5384615384615383 , 255*-1.0000000000000000 , + DEALIAS_BY= 256*-1 , + N_UNIFORM_DOMAINS=1 , + UNIFORM_BOUNDS=F, + / +&NUMERICAL_CONTROLS_NAMELIST + CHEBYSHEV=T, + BANDSOLVE=F, + STATIC_TRANSPOSE=F, + STATIC_CONFIG=F, + USE_PARITY=T, + DERIV_CLUGE=T, + PAD_ALLTOALL=F, + SPARSESOLVE=F, + / +&PHYSICAL_CONTROLS_NAMELIST + MAGNETISM=F, + NONLINEAR=T, + ROTATION=T, + LORENTZ_FORCES=T, + VISCOUS_HEATING=F, + OHMIC_HEATING=F, + ADVECT_REFERENCE_STATE=F, + BENCHMARK_MODE=1 , + BENCHMARK_INTEGRATION_INTERVAL=100 , + BENCHMARK_REPORT_INTERVAL=500 , + MOMENTUM_ADVECTION=T, + INERTIA=T, + / +&TEMPORAL_CONTROLS_NAMELIST + ALPHA_IMPLICIT= 0.50000999999999995 , + MAX_ITERATIONS=25000 , + CHECK_FREQUENCY=-1 , + CFLMAX= 0.59999999999999998 , + CFLMIN= 0.40000000000000002 , + MAX_TIME_STEP= 1.0000000000000000E-004, + DIAGNOSTIC_REBOOT_INTERVAL=10000000 , + MIN_TIME_STEP= 1.0000000000000000E-013, + NUM_QUICKSAVES=2 , + QUICKSAVE_INTERVAL=10000 , + CHECKPOINT_INTERVAL=100000 , + QUICKSAVE_MINUTES= -1.0000000000000000 , + MAX_TIME_MINUTES= 100000000.00000000 , + SAVE_LAST_TIMESTEP=T, + NEW_ITERATION=0 , + SAVE_ON_SIGTERM=F, + MAX_SIMULATED_TIME= 1.0000000000000000E+020, + / +&IO_CONTROLS_NAMELIST + STDOUT_FLUSH_INTERVAL=50 , + TERMINATE_CHECK_INTERVAL=50 , + STATUSLINE_INTERVAL=1 , + STDOUT_FILE="nofile ", + JOBINFO_FILE="jobinfo.txt ", + TERMINATE_FILE="terminate ", + INTEGER_OUTPUT_DIGITS=8 , + INTEGER_INPUT_DIGITS=8 , + DECIMAL_PLACES=3 , + OUTPUTS_PER_ROW=1 , + / +&OUTPUT_NAMELIST + MEM_FRIENDLY=F, + MERIDIONAL_VALUES= 4000*-1 , + MERIDIONAL_FREQUENCY=90000000 , + MERIDIONAL_NREC=1 , + EQUATORIAL_VALUES= 4000*-1 , + EQUATORIAL_FREQUENCY=90000000 , + EQUATORIAL_NREC=1 , + SHELLSLICE_VALUES= 4000*-1 , + SHELLSLICE_FREQUENCY=90000000 , + SHELLSLICE_NREC=1 , + SHELLSPECTRA_VALUES= 4000*-1 , + SHELLSPECTRA_FREQUENCY=90000000 , + SHELLSPECTRA_NREC=1 , + AZAVG_VALUES= 4000*-1 , + AZAVG_FREQUENCY=90000000 , + AZAVG_NREC=1 , + SHELLAVG_VALUES= 4000*-1 , + SHELLAVG_FREQUENCY=90000000 , + SHELLAVG_NREC=1 , + GLOBALAVG_VALUES= 4000*-1 , + GLOBALAVG_FREQUENCY=90000000 , + GLOBALAVG_NREC=1 , + SPH_MODE_VALUES= 4000*-1 , + SPH_MODE_FREQUENCY=90000000 , + SPH_MODE_NREC=0 , + POINT_PROBE_VALUES= 4000*0 , + POINT_PROBE_FREQUENCY=90000000 , + POINT_PROBE_NREC=1 , + FULL3D_VALUES= 4000*-1 , + FULL3D_FREQUENCY=90000000 , + MERIDIONAL_INDICES= 8192*-1 , + SHELLSLICE_LEVELS= 2048*-1 , + SHELLSPECTRA_LEVELS= 2048*-1 , + POINT_PROBE_R= 4096*-1 , + POINT_PROBE_THETA= 4096*-1 , + POINT_PROBE_PHI= 4096*-1 , + SPH_MODE_ELL= 2048*-1 , + SPH_MODE_LEVELS= 2048*-1 , + SHELLSLICE_LEVELS_NRM= 2048*-3.0000000000000000 , + SHELLSPECTRA_LEVELS_NRM= 2048*-3.0000000000000000 , + MERIDIONAL_INDICES_NRM= 8192*-3.0000000000000000 , + POINT_PROBE_R_NRM= 4096*-3.0000000000000000 , + POINT_PROBE_PHI_NRM= 4096*-3.0000000000000000 , + POINT_PROBE_THETA_NRM= 4096*-3.0000000000000000 , + SPH_MODE_LEVELS_NRM= 2048*-3.0000000000000000 , + POINT_PROBE_CACHE_SIZE=1 , + GLOBALAVG_CACHE_SIZE=1 , + SHELLAVG_CACHE_SIZE=1 , + TEMP_IO_VALUES= 4000*-1 , + TEMP_IO_FREQUENCY=90000000 , + TEMP_IO_NREC=-1 , + TEMP_IO_CACHE_SIZE=1 , + TEMP_IO_ELL= 2048*-1 , + TEMP_IO_R= 4096*-1 , + TEMP_IO_THETA= 4096*-1 , + TEMP_IO_PHI= 4096*-1 , + TEMP_IO_R_NRM= 4096*-3.0000000000000000 , + TEMP_IO_THETA_NRM= 4096*-3.0000000000000000 , + TEMP_IO_PHI_NRM= 4096*-3.0000000000000000 , + / +&BOUNDARY_CONDITIONS_NAMELIST + FIX_TVAR_TOP=T, + FIX_TVAR_BOTTOM=T, + T_BOTTOM= 1.0000000000000000 , + T_TOP= 0.0000000000000000 , + DTDR_TOP= 0.0000000000000000 , + DTDR_BOTTOM= 0.0000000000000000 , + FIX_DTDR_BOTTOM=F, + FIX_DTDR_TOP=F, + FIX_DIVRT_TOP=F, + FIX_DIVT_TOP=F, + FIX_DIVRFC_TOP=F, + FIX_DIVFC_TOP=F, + NO_SLIP_BOUNDARIES=T, + STRICT_L_CONSERVATION=F, + FIX_POLOIDALFIELD_TOP=F, + FIX_POLOIDALFIELD_BOTTOM=F, + C10_BOTTOM= 0.0000000000000000 , + C10_TOP= 0.0000000000000000 , + C11_BOTTOM= 0.0000000000000000 , + C11_TOP= 0.0000000000000000 , + C1M1_BOTTOM= 0.0000000000000000 , + C1M1_TOP= 0.0000000000000000 , + BR_BOTTOM= 0.0000000000000000 , + DIPOLE_TILT_DEGREES= 0.0000000000000000 , + IMPOSE_DIPOLE_FIELD=F, + NO_SLIP_TOP=T, + NO_SLIP_BOTTOM=T, + STRESS_FREE_TOP=F, + STRESS_FREE_BOTTOM=F, + T_TOP_FILE="__nothing__ ", + T_BOTTOM_FILE="__nothing__ ", + DTDR_TOP_FILE="__nothing__ ", + DTDR_BOTTOM_FILE="__nothing__ ", + C_TOP_FILE="__nothing__ ", + C_BOTTOM_FILE="__nothing__ ", + DIPOLE_FIELD_BOTTOM=F, + / +&INITIAL_CONDITIONS_NAMELIST + INIT_TYPE=1 , + TEMP_AMP= 1.0000000000000000 , + TEMP_W= 0.29999999999999999 , + RESTART_ITER=0 , + MAGNETIC_INIT_TYPE=1 , + ALT_CHECK=F, + MAG_AMP= 1.0000000000000000 , + CONDUCTIVE_PROFILE=F, + RESCALE_VELOCITY=F, + RESCALE_BFIELD=F, + VELOCITY_SCALE= 1.0000000000000000 , + BFIELD_SCALE= 1.0000000000000000 , + RESCALE_TVAR=F, + RESCALE_PRESSURE=F, + TVAR_SCALE= 1.0000000000000000 , + PRESSURE_SCALE= 1.0000000000000000 , + MDELTA= 0.0000000000000000 , + T_INIT_FILE="__nothing__ ", + W_INIT_FILE="__nothing__ ", + P_INIT_FILE="__nothing__ ", + Z_INIT_FILE="__nothing__ ", + C_INIT_FILE="__nothing__ ", + A_INIT_FILE="__nothing__ ", + / +&TEST_NAMELIST + TEST_MODE=F, + NTEST_LEGENDRE=1 , + TEST_LEGENDRE=F, + TEST_CHEBYSHEV=F, + / +&REFERENCE_NAMELIST + REFERENCE_TYPE=1 , + POLY_N= 0.0000000000000000 , + POLY_NRHO= 0.0000000000000000 , + POLY_MASS= 0.0000000000000000 , + POLY_RHO_I= 0.0000000000000000 , + PRESSURE_SPECIFIC_HEAT= 1.0000000000000000 , + HEATING_TYPE=0 , + LUMINOSITY= 0.0000000000000000 , + ANGULAR_VELOCITY= 1.0000000000000000 , + RAYLEIGH_NUMBER= 100000.00000000000 , + EKMAN_NUMBER= 1.0000000000000000E-003, + PRANDTL_NUMBER= 1.0000000000000000 , + MAGNETIC_PRANDTL_NUMBER= 1.0000000000000000 , + GRAVITY_POWER= 1.0000000000000000 , + CUSTOM_REFERENCE_FILE="nothing ", + DISSIPATION_NUMBER= 0.0000000000000000 , + MODIFIED_RAYLEIGH_NUMBER= 0.0000000000000000 , + HEATING_INTEGRAL= 0.0000000000000000 , + OVERRIDE_CONSTANTS=F, + OVERRIDE_CONSTANT= 10*F, + RA_CONSTANTS= 2000.0000000000000 , 100000.00000000000 , 1000.0000000000000 , 0.0000000000000000 , 2*1.0000000000000000 , + 4*0.0000000000000000 , + WITH_CUSTOM_CONSTANTS= 10*0 , + WITH_CUSTOM_FUNCTIONS= 14*0 , + WITH_CUSTOM_REFERENCE=F, + / +&TRANSPORT_NAMELIST + NU_TYPE=1 , + KAPPA_TYPE=1 , + ETA_TYPE=1 , + NU_POWER= 0.0000000000000000 , + KAPPA_POWER= 0.0000000000000000 , + ETA_POWER= 0.0000000000000000 , + NU_TOP= 1.0000000000000000 , + KAPPA_TOP= 1.0000000000000000 , + ETA_TOP= 0.0000000000000000 , + HYPERDIFFUSION=F, + HYPERDIFFUSION_BETA= 0.0000000000000000 , + HYPERDIFFUSION_ALPHA= 1.0000000000000000 , + / diff --git a/examples/main_input_class/main_input b/examples/main_input_class/main_input new file mode 100755 index 00000000..fae90f72 --- /dev/null +++ b/examples/main_input_class/main_input @@ -0,0 +1,115 @@ +&problemsize_namelist + n_r = 48 + n_theta = 64 + nprow = 2 + npcol = 2 + aspect_ratio = 0.35d0 + shell_depth = 1.0d0 +/ +&numerical_controls_namelist +/ +&physical_controls_namelist + benchmark_mode = 1 + benchmark_integration_interval = 100 + benchmark_report_interval = 5000 + rotation = .True. + magnetism = .false. + viscous_heating = .false. + ohmic_heating = .false. +/ +&temporal_controls_namelist + max_time_step = 1.0d-4 + max_iterations = 40000 + checkpoint_interval = 100000 + cflmin = 0.4d0 + cflmax = 0.6d0 +/ +&io_controls_namelist +/ +&output_namelist + +! New equatorial slices output +equatorial_values = 1,2,3 ! velocity components +equatorial_frequency = 5000 +equatorial_nrec = 2 + +!New meridional slices output. You must also specify the number of phi-indices +!Valid values range from 1 through nphi = 2*n_theta +!The indices below range from phi = 0 up to phi = pi +meridional_values = 1,2,3 ! radial and phi components of velocity; temperature +meridional_frequency = 5000 +meridional_nrec = 2 +meridional_indices_nrm = 0.7 + +shellslice_levels_nrm =0.5, 0.9 +shellslice_values = 1,2,3 ! velocity components +shellslice_frequency = 10000 +shellslice_nrec = 2 + +shellspectra_levels_nrm = 0.5 , 0.9 +shellspectra_values = 1,2,3 ! velocity and temperature +shellspectra_frequency = 10000 +shellspectra_nrec = 2 + +azavg_values = 1,2,3,201,202,501 ! same as above + r- and theta- mass flux +azavg_frequency = 1000 +azavg_nrec = 10 + +point_probe_values = 1,2,3, 1201,1202,1203,1216, 1219,1220, 1221, 1228,1229,1230 +point_probe_r_nrm = 0.2, 0.6, 0.9 +point_probe_theta_nrm= 0.35 +point_probe_phi_nrm = 0.21, 0.72 +point_probe_frequency=10 +point_probe_nrec=500 +point_probe_cache_size=100 + + +! velocity, temperature, energy fluxes, and Kinetic Energy +shellavg_values = 1,2,3,501, 1440, 1470 +shellavg_frequency = 200 +shellavg_nrec = 50 + +! Kinetic energy, Mean KE, Diff-Rot KE, and Convective KE +globalavg_values = 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412 +globalavg_frequency = 100 +globalavg_nrec = 100 + +sph_mode_ell = 2,4,8 +sph_mode_levels_nrm = 0.4, 0.7, 0.9 +sph_mode_values = 1,2,3 +sph_mode_frequency = 100 +sph_mode_nrec = 50 + + +full3d_values = 64 ! temperature +full3d_frequency = 9000000 +/ + +&Boundary_Conditions_Namelist +no_slip_boundaries = .true. +strict_L_Conservation = .false. +dtdr_bottom = 0.0d0 +T_Top = 0.0d0 +T_Bottom = 1.0d0 +fix_tvar_top = .true. +fix_tvar_bottom = .true. +/ +&Initial_Conditions_Namelist +init_type = 1 ! Benchmark init +temp_amp = 1.0d1 +temp_w = 0.01d4 +restart_iter = -1 +/ +&Test_Namelist +/ +&Reference_Namelist +Ekman_Number = 1.0d-3 +Rayleigh_Number = 1.0d5 +Prandtl_Number = 1.0d0 +Magnetic_Prandtl_Number = 5.0d0 +reference_type = 1 +heating_type = 0 ! No heating +gravity_power = 1.0d0 ! g ~ radius +/ +&Transport_Namelist +/ diff --git a/examples/main_input_class/main_input_demo.ipynb b/examples/main_input_class/main_input_demo.ipynb new file mode 100644 index 00000000..1f840bc6 --- /dev/null +++ b/examples/main_input_class/main_input_demo.ipynb @@ -0,0 +1,284 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0d34e892", + "metadata": {}, + "source": [ + "# The Main_Input Class \n", + "\n", + "This notebook demonstrates how to use the main_input class. The main_input class was developed primarily to assist with parameter-space studies, but it may be useful in other situations as well. We begin with an overview of how to work with main_input objects in python, and then provide an example of how they might be used to help initiate a parameter space study. Before running this notebook, please copy 'Rayleigh/post_processing/benchmark_diagnostics_input' to the directory from within which you are running this notebook. Within that directory, you should find a \"jobinfo.txt\" and \"main_input\" file that will be used in the examples below.\n", + "\n", + "\n", + "Documentation for all methods and attributes of this class can be accessed via Python's help function, as illustrated below following the import." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8d5d115c", + "metadata": {}, + "outputs": [], + "source": [ + "from rayleigh_diagnostics import main_input\n", + "help(main_input)" + ] + }, + { + "cell_type": "markdown", + "id": "0054f067", + "metadata": {}, + "source": [ + "## Initialization\n", + "\n", + "To initialize an instance of the main_input class, we must provide a main_input or jobinfo.txt file. That file will be used to define and initialize the object's internal namelist structure. An important point to keep in mind is that the namelist structure derives entirely from the main_input file. Values omitted in the main_input file are not defined for the main_input class. There are workarounds for this illustrated below. To begin, let's read in a main_input file (one is provided within the directory that containst his notebook) and view its contents. The most straightforward way to do this is by using Python's print function, which will display all namelist entries that have been assigned a value." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "44154b4c", + "metadata": {}, + "outputs": [], + "source": [ + "mi = main_input('main_input')\n", + "print(mi) " + ] + }, + { + "cell_type": "markdown", + "id": "c4f6fb6c", + "metadata": {}, + "source": [ + "## The write and set Methods\n", + "\n", + "Python's print function calls the main_input class's *write* method with no optional keywords specified. For the curious, this is done by defining the *__repr__* for the main_input class. By using the write method directly, we can have finer-grained control over the output. Specifically, we can:\n", + "\n", + "1. Print a single namelist\n", + "2. Display nullstring values (e.g., benchmark_mode in the above example) using the verbose optional parameter.\n", + "3. Control the precision of floating-point output using the ndecimal optional parameter.\n", + "4. Write a new main_input file using the *file* optional parameter.\n", + "\n", + "This functionality is illustrated below, in combination with the set method, which is used to assign new namelist values. Note that verbose does not impact values written to a new main_input file; nullstring values are ignored. Examine the contents of *new_main_input* after running the cell below. Nprow is omitted entirely, and aspect ratio has 2 digits after the decimal. Anything from the original file is stored as a string, including numerical values, and remains unmodified." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "04610223", + "metadata": {}, + "outputs": [], + "source": [ + "mi.write(namelist='problemsize') # write only the problemsize namelist\n", + "\n", + "mi.set(nml='problemsize',var='nprow',val='') # set nprow to the null string\n", + "mi.set(nml='problemsize',var='aspect_ratio',val=1/3) # set aspect ratio to a float\n", + "\n", + "#Omit null values\n", + "print('\\n\\nDisplaying problemsize namelist without null values')\n", + "mi.write(namelist='problemsize')\n", + "\n", + "#Include null values and use 3 decimal places (only affects floats)\n", + "#Note that shell_depth is stored as a string, and so its precision remains unmodified\n", + "print('\\n\\nDisplaying problemsize including null values and with 3 decimal places')\n", + "mi.write(namelist='problemsize',verbose=True, ndecimal=3)\n", + "\n", + "\n", + "# Attempting to assign a value to an undefined namelist variable\n", + "# will generate an error\n", + "mi.set(nml='problemsize',var='l_max',val=127)\n", + "\n", + "# If you are sure you didn't mispell the variable and want to add it, use \"force=True\"\n", + "mi.set(nml='problemsize',var='l_max',val=127, force=True)\n", + "mi.write(namelist='problemsize')\n", + "\n", + "#Finally, we can create a new main_input file using the write method\n", + "mi.write(file='new_main_input', ndecimal=2) # write-to-file with 2 digits for float values" + ] + }, + { + "cell_type": "markdown", + "id": "3f0ad79a", + "metadata": {}, + "source": [ + "## Namelist Values: the *vals* dictionary\n", + "\n", + "Namelist variable names and values read from main_input are stored in the *vals* dictionary associated with the main_input class. Their values can also be modified by manipulating this dictionary directly as shown below. Note that when specifying a namelist name, the \"_namelist\" is omitted. Using the set method is recommended, as it checks the validity of variable names.\n", + "\n", + "Note that all values read in from a main_input file are stored as strings. This is why shell_depth was unaffected by value of ndecimal in the example above. Keys in the *vals* dictionary may be assigned a numerical value. When *print* is called, this value is converted to a string. All floats are converted to scientific notation upon output (where to screen or to file). The example below modifies the value of shell_depth such that it is stored as a float.\n", + "\n", + "\n", + "**Important:** When working directly with the vals dictionary, use all lower case for the key names. The set method is case agnostic." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e8580403", + "metadata": {}, + "outputs": [], + "source": [ + "sd = mi.vals['problemsize']['shell_depth']\n", + "print('type(shell_depth) from main_input: ', type(sd))\n", + "\n", + "\n", + "mi.vals['problemsize']['shell_depth'] = 2.0\n", + "sd = mi.vals['problemsize']['shell_depth']\n", + "print('type(shell_depth) from main_input: ', type(sd))\n", + "\n", + "#Now shell_depth will be displayed with the desired precision\n", + "mi.write(namelist='problemsize', ndecimal=8)" + ] + }, + { + "cell_type": "markdown", + "id": "24e58c69", + "metadata": {}, + "source": [ + "## The Read and Unset Methods\n", + "\n", + "After a main_input object is instantiated, we can subsequently modify its structure and values using the contents of another file via the *read* method. We can also erase all values in the vals dictionary entirely, while maintaining the dictionary/file structure using the *unset* method. The example below illustrates this functionality using jobinfo.txt. That file is generated by Rayleigh at runtime, and it contains a list of all namelist variables and associated values. Jobinfo.txt can be useful if you want to access a list of all available namelist variables within your Python routines." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10dfeeb7", + "metadata": {}, + "outputs": [], + "source": [ + "mi = main_input('jobinfo.txt')\n", + "mi.unset()\n", + "mi.read('main_input')\n", + "mi.write(verbose=True)" + ] + }, + { + "cell_type": "markdown", + "id": "bb68f1d9", + "metadata": {}, + "source": [ + "## Use Case: Parameter Space Study \n", + "\n", + "\n", + "Finally, we illustrate how to set up a small parameter-space study using the main_input class. Typically, only a few values in main_input will change from model to model. In particular, the list of outputs and initial conditions will likely remain unmodified. First, let us specify set of physical parameters and simulation resolution sizes. Note that the combination of supercriticality and resolution used here is for illustration purposes only. These values are purposefully unrealistic. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "79f335f1", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "ra_values = [1e1, 1e2]\n", + "ek_values = [1e-3, 1e-4]\n", + "ra_res_nr = [48, 96]\n", + "ra_res_ntheta = [48, 96]\n", + "\n", + "base_dir = 'parameter_study' # top-level directory within which each simulation directory will reside\n", + "try:\n", + " os.mkdir(base_dir)\n", + "except:\n", + " pass # avoid error message if directory exists" + ] + }, + { + "cell_type": "markdown", + "id": "16dc9e2a", + "metadata": {}, + "source": [ + "Next, read in a main_input file that you wish to use as a template. For this example, we will use the template provided with this notebook. This particular file has benchmarking turned on. Before proceeding, we turn benchmarking off and use a random thermal perturbations about a conductive background for the initial conditions instead." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12aec1fd", + "metadata": {}, + "outputs": [], + "source": [ + "mi = main_input('jobinfo.txt')\n", + "mi.unset()\n", + "mi.read('main_input')\n", + "\n", + "mi.vals['physical_controls']['benchmark_mode'] = \"\"\n", + "mi.vals['physical_controls']['benchmark_integration_interval'] = \"\"\n", + "mi.vals['physical_controls']['benchmark_report_interval'] = \"\"\n", + "mi.vals['initial_conditions']['init_type'] = 7\n", + "mi.vals['initial_conditions']['temp_amp'] = 0.001\n", + "mi.vals['initial_conditions']['conductive_profile'] = '.true.'\n", + "print('This is the main_input template: \\n\\n')\n", + "print(mi)" + ] + }, + { + "cell_type": "markdown", + "id": "4c99ef5b", + "metadata": {}, + "source": [ + "Finally, we generate input files for our small parameter-space study. Examine the contents of parameter_study/model_X/main_input to verify the changes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "440ef9fd", + "metadata": {}, + "outputs": [], + "source": [ + "mnum = 1\n", + "for i, ra in enumerate(ra_values):\n", + " for ek in ek_values:\n", + " ra_str = 'Ra_'+\"{:.2e}\".format(ra)\n", + " ek_str = 'Ek_'+\"{:.2e}\".format(ek)\n", + " sim_name = 'model '+str(mnum)\n", + " sim_dir = base_dir+'/model_'+str(mnum)\n", + " print(sim_name,':','Ra =',ra_str, ',', 'Ek =', ek_str)\n", + " \n", + " try:\n", + " os.mkdir(sim_dir)\n", + " except:\n", + " pass\n", + "\n", + " input_file = sim_dir+'/main_input'\n", + " mi.vals['problemsize']['n_r'] = ra_res_nr[i]\n", + " mi.vals['problemsize']['n_theta'] = ra_res_ntheta[i]\n", + " mi.vals['reference']['rayleigh_number'] = ra\n", + " mi.vals['reference']['ekman_number'] = ek\n", + " mi.write(file=input_file)\n", + " mnum+=1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5957dabe", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.9.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/post_processing/rayleigh_diagnostics.py b/post_processing/rayleigh_diagnostics.py index 00473520..fd331c29 100644 --- a/post_processing/rayleigh_diagnostics.py +++ b/post_processing/rayleigh_diagnostics.py @@ -22,6 +22,7 @@ import numpy as np import os import glob +from collections import OrderedDict maxq = 4000 @@ -34,6 +35,237 @@ def get_lut(quantities): lut[q] = i return lut.astype('int32') +class main_input: + """ + Class for working with main_input files. + + Attributes: + vals : Dictionary that reflects the main_input file structure. + + Initializiation syntax: + a = main_input(file) , where + file = 'main_input', 'jobinfo.txt' or any similar file containing Fortran namelists. + + """ + def __init__(self,file): + """ + Initialization routine. + + Input parameters: + file : A main_input or jobinfo.txt file from which the namelist structure + and values may be read. + """ + self.vals = OrderedDict() + self.namelists = [] + self.read(file) + self.namelists = list(self.vals.keys()) + + def __repr__(self, verbose = False): + """ + Provides 'print()' functionality for the main_input class. + """ + self.write(verbose = verbose) + return "" + + def unset(self): + """ + Resets all keys in self.vals to the null string "". + """ + for nml in self.namelists: + for var in self.vals[nml].keys(): + self.vals[nml][var] ="" + + + def set(self,nml="",var="",val="", force = False): + """ + Sets the value of a key in vals and checks for valid key names. + + Parameters: + nml : the namelist to be modified + var : the variable within nml to modify + val : the value to which vals[nml][var] will be set + + force (optional) : When set to True, nml and/or var will + be created on the fly if they do not exist. + + Calling syntax: + mi.set(nml = 'problemsize', var ='n_r', val = 128) + + """ + nml_lower = nml.strip().lower() + var_lower = var.strip().lower() + + if (force): + if (not (nml_lower in self.namelists)): + self.vals[nml_lower] = OrderedDict() + self.namelists = list(self.vals.keys()) + + var_names = list(self.vals[nml_lower].keys()) + + if (not (var_lower in var_names)): + self.vals[nml_lower][var_lower]="" + + if nml_lower in self.namelists: + var_names = list(self.vals[nml_lower].keys()) + if var_lower in var_names: + self.vals[nml_lower][var_lower]=val + else: + print('\nError: variable '+var+' is not present in '+nml_lower+' namelist\n') + else: + print('\nError: '+nml_lower+' is not a valid namelist name.\n') + + + def write(self, verbose = False, file=None, ndecimal=6, namelist=None): + """ + Displays the contents of the vals dictionary and generates + a new main_input file if desired. Floats are expressed in + scientific notation. + + Input parameters: + verbose (optional) : When set to True, all values, including null strings + are displayed. When set to False (default), only + non-empty dictionary entries are displayed. + + file (optional) : If a value for file is provided, the contents of + the vals dictionary will be written to file in + Fortran-readable namelist format. + + ndecimal (optional) : Number of digits to the right of decimal place + to maintain when expressing floats in sci-not. + + """ + + if (type(ndecimal) != type(6)): + ndecimal = 6 + dstr = str(ndecimal) + + lprint = print + endl="" + if (file != None): + fd = open(file, "w") + lprint = fd.write + endl="\n" + + if (namelist in list(self.vals.keys())): + namelists = [namelist] + else: + namelists = self.namelists + + + for nml in namelists: + + nml_line="&"+nml+"_namelist"+endl + lprint(nml_line) + + for var in self.vals[nml].keys(): + val = self.vals[nml][var] + if (type(val) == type(3.14)): + fstring = "{:."+dstr+"e}" + val = fstring.format(val) + if (type(val) != type('astring')): + val = str(val) + if ((val != "") or verbose): + val_line = var+' = '+val+endl + lprint(val_line) + + lprint("/"+endl) + + if (file != None): + fd.close() + + def read_file_lines(self,filename): + """ + + Helper routine. Reads a file and returns it as a list of + strings with one file line per list element. + + Input parameters: + filename : The file to be read + + """ + fd = open(filename, "r") + flines = [] + while True: + oneline = fd.readline() + if len(oneline) == 0: + break + else: + flines.append(oneline) + fd.close() + return flines + + def read(self, infile): + """ + + Populates the vals dictionary using values obtained from a main_input file. + + Input parameters: + infile : A main_input or jobinfo.txt file from which to extract + namelist information. + + Calling Syntax: + mi.read('main_input') + + Note: + Only namelist variables specified within infile are assigned + an associated key/value combination. If a variable is not defined in + infile, it's corresponding key in the vals dictionary will remain + undefined. If a value for the main_input variable already exists in + vals, it will be overwritten using the associated value from infile. + + """ + + + flines = self.read_file_lines(infile) + + ##################################### + # Iterate over the main_input file and + # extract namelist and associated + # variable names + + nlines = len(flines) + nread = 0 + + while (nread < nlines): + + nextline = flines[nread].strip().lower() #strip white-space and force lower case + nread+=1 + + if (len(nextline) != 0): + + # Test for beginning of new namelist + # (first character of line is &) + tchar = nextline[0] + if (tchar == '&'): + + # Extract namelist name. + # Update the namelist list as we go. + + nml_name = nextline[1:].split('namelist')[0][:-1] + if (not nml_name in self.namelists): + self.vals[nml_name] = OrderedDict() + self.namelists = list(self.vals.keys()) + + # Read all variable names until we reach the end of the namelist + reading_nml = True + while (reading_nml): + nextline = flines[nread].strip() + nread +=1 + if(len(nextline) != 0): + if (nextline[0]=='/'): # A forward slash marks the end of a namelist + reading_nml = False + + # Extract the variable names and values. Strip out comments. + # Some lines in jobinfo.txt are split across multiple lines. + # Only those with the '=' sign contain variable names + lsep = nextline.split('=') + if (len(lsep) > 1 and reading_nml and (lsep[0][0] != '!')): + var_name = lsep[0].strip().lower() + var_val = lsep[1].strip().split('!')[0] + self.vals[nml_name][var_name] = var_val + + + class Spherical_3D: """Rayleigh Spherical_3D Structure ----------------------------------