diff --git a/.circleci/config.yml b/.circleci/config.yml index ddcb89c2e..6dfb38da1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -215,13 +215,6 @@ commands: export PYTEST_CODE_LINK_CHOICE="" # we skip test_code_examples.py fi - # configure variables for generating test answers - if [ << parameters.generate >> == 'true' ]; then - export GENERATE_PYGRACKLE_TEST_RESULTS=1 - else - export GENERATE_PYGRACKLE_TEST_RESULTS=0 - fi - # actually execute the tests if [ << parameters.omp >> == 'true' ]; then export OMP_NUM_THREADS=4 @@ -229,8 +222,26 @@ commands: make cxx_omp_example ./cxx_omp_example make clean - else + elif [ ! -f ./python/tests/conftest.py ]; then + # this is the old way to control the answer-test mode + # - We can stop supporting this case once the most recent gold + # standard tags a repository version containing conftest.py + if [ << parameters.generate >> == 'true' ]; then + export GENERATE_PYGRACKLE_TEST_RESULTS=1 + else + export GENERATE_PYGRACKLE_TEST_RESULTS=0 + fi py.test python/tests + else + # ANSWER_DIR was picked to ensure that it matches up with the old + # hardcoded location (we can change it once the most recent gold + # standard tags a repository version containing conftest.py) + ANSWER_DIR=./python/tests/test_answers + if [ << parameters.generate >> == 'true' ]; then + py.test --answer-dir=${ANSWER_DIR} --answer-store python/tests + else + py.test --answer-dir=${ANSWER_DIR} python/tests + fi fi build-docs: @@ -281,14 +292,14 @@ jobs: build_kind: 'classic' generate: 'true' - - install-grackle: - omp: 'true' - classic_build: 'true' - tag: $CIRCLE_BRANCH + # - install-grackle: + # omp: 'true' + # classic_build: 'true' + # tag: $CIRCLE_BRANCH - - run-tests: - omp: 'true' - build_kind: 'classic' + # - run-tests: + # omp: 'true' + # build_kind: 'classic' - install-grackle: omp: 'false' diff --git a/.readthedocs.yml b/.readthedocs.yml index 6a44cb5d5..84e24dcf3 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -8,3 +8,7 @@ build: python: install: - requirements: doc/source/requirements.txt + +sphinx: + # Path to your Sphinx configuration file. + configuration: doc/source/conf.py diff --git a/doc/source/Python.rst b/doc/source/Python.rst index 3f1938f28..81de6c997 100644 --- a/doc/source/Python.rst +++ b/doc/source/Python.rst @@ -185,8 +185,8 @@ a parcel of gas at constant density or in a free-fall model. Each example will produce a figure as well as a dataset that can be loaded and analyzed with `yt `__. -Configuring the path to Grackle input data -++++++++++++++++++++++++++++++++++++++++++ +Editable Install Requirement +++++++++++++++++++++++++++++ All of the example scripts discussed below use the following line to make a guess at where the Grackle input files are located. @@ -195,19 +195,15 @@ make a guess at where the Grackle input files are located. from pygrackle.utilities.data_path import grackle_data_dir -This will typically work for any 'editable' Pygrackle installation +This currently **ONLY** works for an 'editable' Pygrackle installation (i.e., one installed with ``pip install -e .`` as directed above). In this case, it will be assumed that the data files can be found in a directory called ``input`` in the top level of the source -repository. However, this will not work with non-editable -installations. In this case you can use the ``GRACKLE_DATA_DIR`` -environment variable to set the path to the data. This will be picked -up by the Python code above and the ``grackle_data_dir`` variable will -contain the proper path. +repository. -.. code-block:: shell-session +.. note:: - export GRACKLE_DATA_DIR=/path/to/data + `GitHub PR #235 `__ is a pending pull request that seeks to add functionality to make this work in a regular Pygrackle installation (i.e. a non-'editable' install). Cooling Rate Figure Example +++++++++++++++++++++++++++ diff --git a/doc/source/Testing.rst b/doc/source/Testing.rst index f1c6302ae..ebce99067 100644 --- a/doc/source/Testing.rst +++ b/doc/source/Testing.rst @@ -26,8 +26,9 @@ the following: Answer tests are those whose correct answers must be generated from a prior, trusted version of Grackle (i.e., the "gold standard"). The tests are first run using this trusted version to generate the -results, then run again on the latest version to compate. These tests -include: +results (in *store-mode*), then run again on the latest version to +compare (in *compare-mode*). +These tests include: - all code examples build, run, and return correct results @@ -37,40 +38,51 @@ include: - all grackle 'calculate' functions return correct results for sets of random field values -Tests Without Answer Verification -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +We refer to the location where the results of answer-tests are stored as the "answer-directory." This is an arbitrary user-specified location. -If you only want to quickly verify that everything runs, you can skip -generating the answer test results using the latest gold standard. To -run this way, first set the ``GENERATE_PYGRACKLE_TEST_RESULTS`` -environment variable to 1. +Quick Primer on the Test Runner's CLI +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. code-block:: shell-session +By default, the pytest test-runner always runs all available test cases. - ~ $ export GENERATE_PYGRACKLE_TEST_RESULTS=1 + - The unit tests are **ALWAYS** available. -This will tell the test suite to simply generate new answers and not -compare with previously existing ones. + - By default, **all** answer-tests are fully disabled. + Command-line options make them available in *store-mode* or *compare-mode*. + The ``--answer-dir=`` flag is required for both modes; it specifies the path to the user's chosen "answer-directory" (where answer-tests results are stored/read-from). + The ``--answer-store`` flag enables *store-mode*, while its absence enables *compare-mode*. -Once you have installed :ref:`pygrackle and the development -dependencies `, the tests can be run from the **src** -directory by typing ``make test``: +*For contributors:* you may find pytest's `build-in command-line interface `__ useful during debugging (e.g. you can instruct pytest to only run a subset of all available tests). -.. code-block:: shell-session +.. _test_without_answer_verification: - ~ $ cd grackle/src - ~/grackle/src $ make test +Tests Without Answer Verification +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you only want to quickly verify that everything runs, you can skip +generating the answer test results using the latest gold standard. -or from the **src/python** directory by typing ``py.test``: +Once you have installed :ref:`pygrackle and the development dependencies `, this is relatively straight-forward. + +Simply invoke ``py.test`` from the **src/python** directory with the ``--answer-dir=`` option **and** the ``--answer-store`` option. +For the former You can specify an arbitrary path where the test-answers will be recorded. +A concrete example of what this looks like is shown down below. .. code-block:: shell-session ~ $ cd grackle/src/python - ~/grackle/src $ py.test + ~/grackle/src/python $ py.test --answer-dir=./my_test_answers --answer-store + +The above snippet instructs each answer-test to store the test result in the directory called **./my_test_answers**; an answer-test reports that it has "passed" as long as it is able to successfully store the result. +After you run the test-suite, you can delete the **./my_test_answers** directory (since we don't actually care about the test results). + +When you launch the tests, the output will look like the following: + +.. code-block:: shell-session ==================================== test session starts ==================================== platform darwin -- Python 3.11.9, pytest-8.2.1, pluggy-1.5.0 - rootdir: /Users/britton/Documents/work/research/simulation/grackle/grackle-git/src + rootdir: /Users/britton/Documents/work/research/simulation/grackle/grackle-git/src/python plugins: cov-5.0.0 collected 65 items @@ -112,11 +124,18 @@ steps: #. Re-compile the Grackle library and :ref:`re-install pygrackle `. -#. Set the ``GENERATE_PYGRACKLE_TEST_RESULTS`` environment variable to - 1. +#. Execute the test suite using command-line flags to instruct the test-runner to run answer tests in *store-mode*. + This was already illustrated :ref:`above `, we repeat the instructions here: + + - Execute ``py.test`` from the **src/python** directory while specifing the path to the *answer-directory* with ``--answer-dir=`` **and** specifying the ``--answer-store`` option. + + - Be aware, this will directly overwrite any files that were previously stored in the answer-dir. + + - If we wanted to store the test-answers in **./my-test-answers**, we would invoke: -#. Run the test suite as described above. This will create test result - files in the directory **src/python/tests/test_answers**. + .. code-block:: shell-session + + ~/grackle/src/python $ py.test --answer-dir=./my_test_answers --answer-store #. Return to the branch of the repository you started with. If you just cloned the main repository, this will be called 'main', in which @@ -125,8 +144,16 @@ steps: #. Re-compile the Grackle library and :ref:`re-install pygrackle `. -#. Set the ``GENERATE_PYGRACKLE_TEST_RESULTS`` environment variable to - 0. +#. Run the test suite again using a command-line flag to instruct the test runner to evaluate the answer-tests in *compare-mode*: + + - you just need to specify the *answer-directory* path with ``--answer-dir=``. + + - Do **NOT** pass the ``--answer-store`` flag. + The absence of this flag is how the test-runner knows to use *compare-mode*. + (If the flag were present, then the answer-tests would overwrite the answers) + + - To compare against the results previously written to **./my-test-answers**, you would invoke: + + .. code-block:: shell-session -#. Run the test suite again. This time, the answer tests will be - compared with the previously generated results. + ~/grackle/src/python $ py.test --answer-dir=./my_test_answers --answer-store diff --git a/src/clib/CMakeLists.txt b/src/clib/CMakeLists.txt index 669f50dde..880d2f01d 100644 --- a/src/clib/CMakeLists.txt +++ b/src/clib/CMakeLists.txt @@ -105,6 +105,7 @@ add_library(Grackle_Grackle ${CMAKE_CURRENT_BINARY_DIR}/auto_general.c # Fortran Source Files + calc_all_tdust_gasgr_1d_g.F calc_tdust_1d_g.F calc_tdust_3d_g.F calc_temp1d_cloudy_g.F diff --git a/src/clib/Make.config.objects b/src/clib/Make.config.objects index 78bb7eb68..1da4b50bb 100644 --- a/src/clib/Make.config.objects +++ b/src/clib/Make.config.objects @@ -40,6 +40,7 @@ OBJS_CONFIG_LIB = \ solve_rate_cool_g.lo \ update_UVbackground_rates.lo \ lookup_cool_rates0d.lo \ + calc_all_tdust_gasgr_1d_g.lo \ calc_tdust_1d_g.lo \ calc_tdust_3d_g.lo \ calc_grain_size_increment_1d.lo \ diff --git a/src/clib/c_wrappers/wrap_interpolators_g.c b/src/clib/c_wrappers/wrap_interpolators_g.c deleted file mode 100644 index 9509fd055..000000000 --- a/src/clib/c_wrappers/wrap_interpolators_g.c +++ /dev/null @@ -1,60 +0,0 @@ -#include "grackle_macros.h" - -// TODO: FORTRAN_NAME not needed for unit tests -extern void FORTRAN_NAME(interpolate_1d_g)(double *input1, - long long *gridDim, - double *gridPar1, double *dgridPar1, - long long *dataSize, double *dataField, - double *value); - -extern void FORTRAN_NAME(interpolate_2d_g)(double *input1, double *input2, - long long *gridDim, - double *gridPar1, double *dgridPar1, - double *gridPar2, double *dgridPar2, - long long *dataSize, double *dataField, - double *value); - -extern void FORTRAN_NAME(interpolate_3d_g)(double *input1, double *input2, double *input3, - long long *gridDim, - double *gridPar1, double *dgridPar1, - double *gridPar2, double *dgridPar2, - double *gridPar3, double *dgridPar3, - long long *dataSize, double *dataField, - double *value); - -extern void FORTRAN_NAME(interpolate_3dz_g)(double *input1, double *input2, double *input3, - long long *gridDim, - double *gridPar1, double *dgridPar1, - double *gridPar2, long long *index2, - double *gridPar3, double *dgridPar3, - long long *dataSize, double *dataField, - int *end_int, - double *value); - -extern void FORTRAN_NAME(interpolate_2df3d_g)(double *input1, double *input3, - long long *gridDim, - double *gridPar1, double *dgridPar1, - long long *index2, - double *gridPar3, double *dgridPar3, - long long *dataSize, double *dataField, - double *value); - -extern void FORTRAN_NAME(interpolate_4d_g)(double *input1, double *input2, double *input3, double *input4, - long long *gridDim, - double *gridPar1, double *dgridPar1, - double *gridPar2, double *dgridPar2, - double *gridPar3, double *dgridPar3, - double *gridPar4, double *dgridPar4, - long long *dataSize, double *dataField, - double *value); - -extern void FORTRAN_NAME(interpolate_5d_g)(double *input1, double *input2, double *input3, double *input4, double *input5, - long long *gridDim, - double *gridPar1, double *dgridPar1, - double *gridPar2, double *dgridPar2, - double *gridPar3, double *dgridPar3, - double *gridPar4, double *dgridPar4, - double *gridPar5, double *dgridPar5, - long long *dataSize, double *dataField, - double *value); - diff --git a/src/clib/calc_all_tdust_gasgr_1d_g.F b/src/clib/calc_all_tdust_gasgr_1d_g.F new file mode 100644 index 000000000..7e5c10c75 --- /dev/null +++ b/src/clib/calc_all_tdust_gasgr_1d_g.F @@ -0,0 +1,353 @@ + subroutine calc_all_tdust_gasgr_1d_g(in, jn, kn, nratec, + & idustfield, is, ie, j, k, fgr, gamma_isrfa, + & trad, gasgra, indixe, tdef, tgas, tdust, + & metallicity, dust2gas, nh, gasgr_tdust, + & itmask_metal, + & idspecies, itdmulti, gr_N, gr_Size, gr_dT, + & gr_Td, tSiM, tFeM, tMg2SiO4, tMgSiO3, tFe3O4, + & tAC, tSiO2D, tMgO, tFeS, tAl2O3, treforg, + & tvolorg, tH2Oice, gasgr2a, gamma_isrf2a, + & coolunit, gasgr, myisrf, sgSiM, sgFeM, sgMg2SiO4, + & sgMgSiO3, sgFe3O4, sgAC, sgSiO2D, sgMgO, sgFeS, + & sgAl2O3, sgreforg, sgvolorg, sgH2Oice, sgtot, + & alSiM, alFeM, alMg2SiO4, alMgSiO3, alFe3O4, alAC, + & alSiO2D, alMgO, alFeS, alAl2O3, alreforg, + & alvolorg, alH2Oice, altot, kpSiM, kpFeM, + & kpMg2SiO4, kpMgSiO3, kpFe3O4, kpAC, kpSiO2D, + & kpMgO, kpFeS, kpAl2O3, kpreforg, kpvolorg, + & kpH2Oice, kptot, gasSiM, gasFeM, gasMg2SiO4, + & gasMgSiO3, gasFe3O4, gasAC, gasSiO2D, gasMgO, + & gasFeS, gasAl2O3, gasreforg, gasvolorg, + & gasH2Oice + & ) + +! PURPOSE: +! Calculate all dust temperature(s) and the gas to grain heat +! transfer rate(s) (the latter is commonly called gasgr) +! +! An argument could be made for directly using gasgr to compute +! contributions to edot within this routine, rather than returning +! the gasgr value(s). But that should be reconsidered in the future. +! +! We could significantly reduce the amount of buffer space allocated +! within the routine. But, we will hold off on doing that until after +! we have transcribed to C/C++ +! +! INPUTS: +! is,ie - start and end indicies of active region (zero-based!) +! +! PARAMETERS: +! + +!----------------------------------------------------------------------- + + implicit NONE +#include "grackle_fortran_types.def" +#include "phys_const.def" + +! Arguments + + integer in, jn, kn, is, ie, j, k, nratec, idustfield + + real*8 fgr + real*8 gasgra(nratec), gamma_isrfa + +! Parameters + + real*8, parameter :: mh = mass_h !DPC + +! Locals + + integer i + real*8 trad, coolunit + +! Slice locals + + integer*8 indixe(in) + real*8 tdef(in), tgas(in), tdust(in), nh(in), metallicity(in), + & dust2gas(in) + +! Cooling/heating slice locals + + real*8 gasgr(in), gasgr_tdust(in), myisrf(in) + integer idspecies, itdmulti +! opacity table + integer gr_N(2), gr_Size + real*8 gr_dT, gr_Td(gr_N(2)) +! grain growth + real*8 sgSiM(in), sgFeM(in), sgMg2SiO4(in) + & , sgMgSiO3(in), sgFe3O4(in), sgAC(in) + & , sgSiO2D(in), sgMgO(in), sgFeS(in) + & , sgAl2O3(in) + & , sgreforg(in), sgvolorg(in), sgH2Oice(in) + & , sgtot(in) + real*8 alSiM(gr_N(2),in), alFeM(gr_N(2),in) + & , alMg2SiO4(gr_N(2),in), alMgSiO3(gr_N(2),in) + & , alFe3O4(gr_N(2),in), alAC(gr_N(2),in) + & , alSiO2D(gr_N(2),in), alMgO(gr_N(2),in) + & , alFeS(gr_N(2),in), alAl2O3(gr_N(2),in) + & , alreforg(gr_N(2),in) + & , alvolorg(gr_N(2),in), alH2Oice(gr_N(2),in) + & , altot(gr_N(2),in) + real*8 kpSiM(in), kpFeM(in), kpMg2SiO4(in) + & , kpMgSiO3(in), kpFe3O4(in), kpAC(in) + & , kpSiO2D(in), kpMgO(in), kpFeS(in) + & , kpAl2O3(in) + & , kpreforg(in), kpvolorg(in), kpH2Oice(in) + & , kptot(in) +! grain temperature + real*8 tSiM(in), tFeM(in), tMg2SiO4(in) + & , tMgSiO3(in), tFe3O4(in), tAC(in) + & , tSiO2D(in), tMgO(in), tFeS(in) + & , tAl2O3(in) + & , treforg(in), tvolorg(in), tH2Oice(in) + real*8 gasgr2a(nratec), gamma_isrf2a + real*8 gasSiM(in), gasFeM(in), gasMg2SiO4(in) + & , gasMgSiO3(in), gasFe3O4(in), gasAC(in) + & , gasSiO2D(in), gasMgO(in), gasFeS(in) + & , gasAl2O3(in) + & , gasreforg(in), gasvolorg(in), gasH2Oice(in) + real*8 gasgr_tSiM(in), gasgr_tFeM(in), gasgr_tMg2SiO4(in) + & , gasgr_tMgSiO3(in), gasgr_tFe3O4(in), gasgr_tAC(in) + & , gasgr_tSiO2D(in), gasgr_tMgO(in), gasgr_tFeS(in) + & , gasgr_tAl2O3(in) + & , gasgr_treforg(in), gasgr_tvolorg(in), gasgr_tH2Oice(in) + real*8 mygisrf(in), fv2k, fac + real*8 gisrfSiM(in), gisrfFeM(in), gisrfMg2SiO4(in) + & , gisrfMgSiO3(in), gisrfFe3O4(in), gisrfAC(in) + & , gisrfSiO2D(in), gisrfMgO(in), gisrfFeS(in) + & , gisrfAl2O3(in) + & , gisrfreforg(in), gisrfvolorg(in), gisrfH2Oice(in) +! Iteration mask + + MASK_TYPE itmask_metal(in) + + ! Calculate heating from interstellar radiation field + ! -> this is ONLY used when `itmask_metal .eq. MASK_TRUE` + + do i = is+1, ie+1 + if ( itmask_metal(i) .ne. MASK_FALSE ) then + + if (idspecies .eq. 0 ) then + if (idustfield .gt. 0) then + mygisrf(i) = gamma_isrfa + & * fgr / dust2gas(i) * metallicity(i) + !! correct with the depletion or enhancement of condensation rate. + else + mygisrf(i) = gamma_isrfa + endif + + else ! idspecies + + if (itdmulti .eq. 0) then + + mygisrf(i) = gamma_isrf2a * sgtot(i) + !! in UV, absorption coefficient Q ~ 1 (Goldsmith 2001) + !! so we use the geometrical cross-section of grains [cgs] + + else + + if (idspecies .gt. 0) then + gisrfMgSiO3 (i) = gamma_isrf2a * sgMgSiO3 (i) +!! write(*,*) 'sil', d(i,j,k), gamma_isrf2a, sgMgSiO3(i) + gisrfAC (i) = gamma_isrf2a * sgAC (i) +!! write(*,*) 'car', d(i,j,k), gamma_isrf2a, sgMgSiO3(i) + endif + if (idspecies .gt. 1) then + gisrfSiM (i) = gamma_isrf2a * sgSiM (i) + gisrfFeM (i) = gamma_isrf2a * sgFeM (i) + gisrfMg2SiO4 (i) = gamma_isrf2a * sgMg2SiO4 (i) + gisrfFe3O4 (i) = gamma_isrf2a * sgFe3O4 (i) + gisrfSiO2D (i) = gamma_isrf2a * sgSiO2D (i) + gisrfMgO (i) = gamma_isrf2a * sgMgO (i) + gisrfFeS (i) = gamma_isrf2a * sgFeS (i) + gisrfAl2O3 (i) = gamma_isrf2a * sgAl2O3 (i) + endif + if (idspecies .gt. 2) then + gisrfreforg (i) = gamma_isrf2a * sgreforg (i) + gisrfvolorg (i) = gamma_isrf2a * sgvolorg (i) + gisrfH2Oice (i) = gamma_isrf2a * sgH2Oice (i) + endif + + endif + + endif ! idspecies + + endif + enddo + +! --- Gas to grain heat transfer --- + +! Look up gas/grain heat transfer rates + + do i = is+1, ie+1 + if ( itmask_metal(i) .ne. MASK_FALSE ) then + + if(idspecies .eq. 0) then + + gasgr(i) = gasgra(indixe(i)) + tdef(i) + & *(gasgra(indixe(i)+1) -gasgra(indixe(i))) + +!! gasgr_tdust(i) = fgr * gasgr(i) * coolunit / mh + gasgr_tdust(i) = (dust2gas(i) / metallicity(i)) + & * gasgr(i) * coolunit / mh + !! apply to (idustfield .eq. 1) GC20200701 + + else ! idspecies + + fv2k = gasgr2a(indixe(i)) + tdef(i) + & *(gasgr2a(indixe(i)+1) -gasgr2a(indixe(i))) + + fac = coolunit / mh + + if ( itdmulti .eq. 0 ) then + + gasgr(i) = fv2k * sgtot(i) + + gasgr_tdust(i) = gasgr(i) * fac + + else + + if (idspecies .gt. 0) then + gasMgSiO3 (i) = fv2k * sgMgSiO3 (i) + gasAC (i) = fv2k * sgAC (i) + endif + if (idspecies .gt. 1) then + gasSiM (i) = fv2k * sgSiM (i) + gasFeM (i) = fv2k * sgFeM (i) + gasMg2SiO4 (i) = fv2k * sgMg2SiO4 (i) + gasFe3O4 (i) = fv2k * sgFe3O4 (i) + gasSiO2D (i) = fv2k * sgSiO2D (i) + gasMgO (i) = fv2k * sgMgO (i) + gasFeS (i) = fv2k * sgFeS (i) + gasAl2O3 (i) = fv2k * sgAl2O3 (i) + endif + if (idspecies .gt. 2) then + gasreforg (i) = fv2k * sgreforg (i) + gasvolorg (i) = fv2k * sgvolorg (i) + gasH2Oice (i) = fv2k * sgH2Oice (i) + endif + + if (idspecies .gt. 0) then + gasgr_tMgSiO3 (i) = gasMgSiO3 (i) * fac + gasgr_tAC (i) = gasAC (i) * fac + endif + if (idspecies .gt. 1) then + gasgr_tSiM (i) = gasSiM (i) * fac + gasgr_tFeM (i) = gasFeM (i) * fac + gasgr_tMg2SiO4 (i) = gasMg2SiO4 (i) * fac + gasgr_tFe3O4 (i) = gasFe3O4 (i) * fac + gasgr_tSiO2D (i) = gasSiO2D (i) * fac + gasgr_tMgO (i) = gasMgO (i) * fac + gasgr_tFeS (i) = gasFeS (i) * fac + gasgr_tAl2O3 (i) = gasAl2O3 (i) * fac + endif + if (idspecies .gt. 2) then + gasgr_treforg (i) = gasreforg (i) * fac + gasgr_tvolorg (i) = gasvolorg (i) * fac + gasgr_tH2Oice (i) = gasH2Oice (i) * fac + endif + + endif + + endif ! idspecies + + endif + enddo + +! Compute dust temperature + + if (itdmulti .eq. 0) then + + call calc_tdust_1d_g(tdust, tgas, nh, gasgr_tdust, + & mygisrf, myisrf, itmask_metal, trad, in, is, ie, j, k + & , gr_N, gr_Size, gr_dT, gr_Td, altot, kptot, idspecies) + + else + + if (idspecies .gt. 0) then + call calc_tdust_1d_g(tMgSiO3 , tgas, nh, gasgr_tMgSiO3 , + & gisrfMgSiO3 , myisrf, itmask_metal, trad, + & in, is, ie, j, k + & , gr_N, gr_Size, gr_dT, gr_Td, alMgSiO3 , kpMgSiO3 + & , idspecies) + + call calc_tdust_1d_g(tAC , tgas, nh, gasgr_tAC , + & gisrfAC , myisrf, itmask_metal, trad, + & in, is, ie, j, k + & , gr_N, gr_Size, gr_dT, gr_Td, alAC , kpAC + & , idspecies) + endif + + if (idspecies .gt. 1) then + call calc_tdust_1d_g(tSiM , tgas, nh, gasgr_tSiM , + & gisrfSiM , myisrf, itmask_metal, trad, + & in, is, ie, j, k + & , gr_N, gr_Size, gr_dT, gr_Td, alSiM , kpSiM + & , idspecies) + + call calc_tdust_1d_g(tFeM , tgas, nh, gasgr_tFeM , + & gisrfFeM , myisrf, itmask_metal, trad, + & in, is, ie, j, k + & , gr_N, gr_Size, gr_dT, gr_Td, alFeM , kpFeM + & , idspecies) + + call calc_tdust_1d_g(tMg2SiO4 , tgas, nh, gasgr_tMg2SiO4 , + & gisrfMg2SiO4 , myisrf, itmask_metal, trad, + & in, is, ie, j, k + & , gr_N, gr_Size, gr_dT, gr_Td, alMg2SiO4 , kpMg2SiO4 + & , idspecies) + + call calc_tdust_1d_g(tFe3O4 , tgas, nh, gasgr_tFe3O4 , + & gisrfFe3O4 , myisrf, itmask_metal, trad, + & in, is, ie, j, k + & , gr_N, gr_Size, gr_dT, gr_Td, alFe3O4 , kpFe3O4 + & , idspecies) + + call calc_tdust_1d_g(tSiO2D , tgas, nh, gasgr_tSiO2D , + & gisrfSiO2D , myisrf, itmask_metal, trad, + & in, is, ie, j, k + & , gr_N, gr_Size, gr_dT, gr_Td, alSiO2D , kpSiO2D + & , idspecies) + + call calc_tdust_1d_g(tMgO , tgas, nh, gasgr_tMgO , + & gisrfMgO , myisrf, itmask_metal, trad, + & in, is, ie, j, k + & , gr_N, gr_Size, gr_dT, gr_Td, alMgO , kpMgO + & , idspecies) + + call calc_tdust_1d_g(tFeS , tgas, nh, gasgr_tFeS , + & gisrfFeS , myisrf, itmask_metal, trad, + & in, is, ie, j, k + & , gr_N, gr_Size, gr_dT, gr_Td, alFeS , kpFeS + & , idspecies) + + call calc_tdust_1d_g(tAl2O3 , tgas, nh, gasgr_tAl2O3 , + & gisrfAl2O3 , myisrf, itmask_metal, trad, + & in, is, ie, j, k + & , gr_N, gr_Size, gr_dT, gr_Td, alAl2O3 , kpAl2O3 + & , idspecies) + endif + + if (idspecies .gt. 2) then + call calc_tdust_1d_g(treforg , tgas, nh, gasgr_treforg , + & gisrfreforg , myisrf, itmask_metal, trad, + & in, is, ie, j, k + & , gr_N, gr_Size, gr_dT, gr_Td, alreforg , kpreforg + & , idspecies) + + call calc_tdust_1d_g(tvolorg , tgas, nh, gasgr_tvolorg , + & gisrfvolorg , myisrf, itmask_metal, trad, + & in, is, ie, j, k + & , gr_N, gr_Size, gr_dT, gr_Td, alvolorg , kpvolorg + & , idspecies) + + call calc_tdust_1d_g(tH2Oice , tgas, nh, gasgr_tH2Oice , + & gisrfH2Oice , myisrf, itmask_metal, trad, + & in, is, ie, j, k + & , gr_N, gr_Size, gr_dT, gr_Td, alH2Oice , kpH2Oice + & , idspecies) + endif + + endif + end diff --git a/src/clib/calc_tdust_3d_g.F b/src/clib/calc_tdust_3d_g.F index 4637a4066..a067ea054 100644 --- a/src/clib/calc_tdust_3d_g.F +++ b/src/clib/calc_tdust_3d_g.F @@ -92,14 +92,6 @@ subroutine calc_tdust_3d_g( & , metal_F50(in,jn,kn), metal_F80(in,jn,kn) & , metal_P170(in,jn,kn), metal_P200(in,jn,kn) & , metal_Y19(in,jn,kn) - & , CI(in,jn,kn) , CII(in,jn,kn) , CO(in,jn,kn) - & , CO2(in,jn,kn) , OI(in,jn,kn) , OH(in,jn,kn) - & , H2O(in,jn,kn) , O2(in,jn,kn) , SiI(in,jn,kn) - & , SiOI(in,jn,kn) , SiO2I(in,jn,kn) , CH(in,jn,kn) - & , CH2(in,jn,kn) , COII(in,jn,kn) , OII(in,jn,kn) - & , OHII(in,jn,kn) , H2OII(in,jn,kn) , H3OII(in,jn,kn) - & , O2II(in,jn,kn) , Mg(in,jn,kn) , Al(in,jn,kn) - & , S(in,jn,kn) , Fe(in,jn,kn) R_PREC SiM(in,jn,kn), FeM(in,jn,kn), Mg2SiO4(in,jn,kn) & , MgSiO3(in,jn,kn), Fe3O4(in,jn,kn), AC(in,jn,kn) & , SiO2D(in,jn,kn), MgO(in,jn,kn), FeS(in,jn,kn) @@ -199,22 +191,11 @@ subroutine calc_tdust_3d_g( ! Slice locals - integer indixe(in) - R_PREC t1(in), t2(in), logtem(in), tdef(in), - & tgas(in), tdust(in), nh(in), gasgr(in), + integer*8 indixe(in) + real*8 t1(in), t2(in), logtem(in), tdef(in), + & tgas(in), tdust(in), nh(in), gasgr(in), gasgr_tdust(in), & myisrf(in) -! Additional slice locals - R_PREC brem(in), ceHeI(in), ceHeII(in), ceHI(in), - & cieco(in), ciHeI(in), ciHeII(in), ciHeIS(in), - & ciHI(in), comp1(in), comp2(in), edot(in), - & energy(in), gammaha_eff(in), gasgr_tdust(in), - & gphdl(in), gpldl(in), h2k01(in), hdlow(in), - & hdlte(in), hyd01k(in), mmw(in), myde(in), - & mynh(in), p2d(in), regr(in), reHeII1(in), - & reHeIII(in), reHII(in), rhoH(in), reHeII(in), - & reHeII2(in), roth(in), rotl(in), tgasold(in), - & vibh(in) ! Iteration mask for multi_cool MASK_TYPE itmask(in) @@ -250,21 +231,7 @@ subroutine calc_tdust_3d_g( ! parallelize the k and j loops with OpenMP ! flat j and k loops for better parallelism #ifdef _OPENMP -!$omp parallel do schedule(runtime) private( -!$omp& i, j, k, -!$omp& comp1, comp2, energy, -!$omp& indixe, -!$omp& t1, t2, logtem, tdef, p2d, -!$omp& tgas, tgasold, -!$omp& tdust, metallicity, dust2gas, rhoH, mmw, -!$omp& mynh, myde, gammaha_eff, gasgr_tdust, regr, edot, -!$omp& ceHI, ceHeI, ceHeII, -!$omp& ciHI, ciHeI, ciHeIS, ciHeII, -!$omp& reHII, reHeII1, reHeII2, reHeIII, -!$omp& brem, cieco, -!$omp& hyd01k, h2k01, vibh, roth, rotl, -!$omp& gpldl, gphdl, hdlte, hdlow, -!$omp& itmask ) +!$omp parallel do schedule(runtime) private(i, j, k) #endif do t = 0, dk*dj-1 k = t/dj + ks+1 @@ -287,39 +254,6 @@ subroutine calc_tdust_3d_g( metal_P200(i,j,k)=metal_P200(i,j,k)/real(aye**3, RKIND) metal_Y19(i,j,k) = metal_Y19(i,j,k)/real(aye**3, RKIND) endif -!! if (metal(i,j,k) .gt. 1.d-9 * d(i,j,k)) then - if (imchem .eq. 1) then - CI(i,j,k) = CI(i,j,k)/real(aye**3, RKIND) - CII(i,j,k) = CII(i,j,k)/real(aye**3, RKIND) - CO(i,j,k) = CO(i,j,k)/real(aye**3, RKIND) - CO2(i,j,k) = CO2(i,j,k)/real(aye**3, RKIND) - OI(i,j,k) = OI(i,j,k)/real(aye**3, RKIND) - OH(i,j,k) = OH(i,j,k)/real(aye**3, RKIND) - H2O(i,j,k) = H2O(i,j,k)/real(aye**3, RKIND) - O2(i,j,k) = O2(i,j,k)/real(aye**3, RKIND) - SiI(i,j,k) = SiI(i,j,k)/real(aye**3, RKIND) - SiOI(i,j,k) = SiOI(i,j,k)/real(aye**3, RKIND) - SiO2I(i,j,k) = SiO2I(i,j,k)/real(aye**3, RKIND) - CH(i,j,k) = CH(i,j,k)/real(aye**3, RKIND) - CH2(i,j,k) = CH2(i,j,k)/real(aye**3, RKIND) - COII(i,j,k) = COII(i,j,k)/real(aye**3, RKIND) - OII(i,j,k) = OII(i,j,k)/real(aye**3, RKIND) - OHII(i,j,k) = OHII(i,j,k)/real(aye**3, RKIND) - H2OII(i,j,k) = H2OII(i,j,k)/real(aye**3, RKIND) - H3OII(i,j,k) = H3OII(i,j,k)/real(aye**3, RKIND) - O2II(i,j,k) = O2II(i,j,k)/real(aye**3, RKIND) - if ( ( igrgr .eq. 1 ) .or. ( idsub .eq. 1 ) ) then - if (idspecies .gt. 0) then - Mg(i,j,k) = Mg(i,j,k)/real(aye**3, RKIND) - endif - if (idspecies .gt. 1) then - Al(i,j,k) = Al(i,j,k)/real(aye**3, RKIND) - S(i,j,k) = S(i,j,k)/real(aye**3, RKIND) - Fe(i,j,k) = Fe(i,j,k)/real(aye**3, RKIND) - endif - endif - endif -!! endif enddo endif if (idustfield .eq. 1) then @@ -477,51 +411,6 @@ subroutine calc_tdust_3d_g( myisrf(i) = isrf endif - if (idspecies .eq. 0 ) then - if (idustfield .gt. 0) then - mygisrf(i) = gamma_isrfa - & * fgr / dust2gas(i) * metallicity(i) - !! correct with the depletion or enhancement of condensation rate. -!! write(*,*) 'a', mygisrf(i) - else - mygisrf(i) = gamma_isrfa - endif - - else ! idspecies - - if (itdmulti .eq. 0) then - - mygisrf(i) = gamma_isrf2a * sgtot(i) - !! in UV, absorption coefficient Q ~ 1 (Goldsmith 2001) - !! so we use the geometrical cross-section of grains [cgs] -!! write(*,*) 'b', mygisrf(i) / dust2gas(i) - - else - - if (idspecies .gt. 0) then - gisrfMgSiO3 (i) = gamma_isrf2a * sgMgSiO3 (i) -!! write(*,*) d(i,j,k), gamma_isrf2a, sgMgSiO3(i) - gisrfAC (i) = gamma_isrf2a * sgAC (i) - endif - if (idspecies .gt. 1) then - gisrfSiM (i) = gamma_isrf2a * sgSiM (i) - gisrfFeM (i) = gamma_isrf2a * sgFeM (i) - gisrfMg2SiO4 (i) = gamma_isrf2a * sgMg2SiO4 (i) - gisrfFe3O4 (i) = gamma_isrf2a * sgFe3O4 (i) - gisrfSiO2D (i) = gamma_isrf2a * sgSiO2D (i) - gisrfMgO (i) = gamma_isrf2a * sgMgO (i) - gisrfFeS (i) = gamma_isrf2a * sgFeS (i) - gisrfAl2O3 (i) = gamma_isrf2a * sgAl2O3 (i) - endif - if (idspecies .gt. 2) then - gisrfreforg (i) = gamma_isrf2a * sgreforg (i) - gisrfvolorg (i) = gamma_isrf2a * sgvolorg (i) - gisrfH2Oice (i) = gamma_isrf2a * sgH2Oice (i) - endif - - endif - - endif ! Compute hydrogen number density @@ -549,173 +438,33 @@ subroutine calc_tdust_3d_g( t2(i) = (logtem0 + (indixe(i) )*dlogtem) tdef(i) = (logtem(i) - t1(i)) / (t2(i) - t1(i)) -! Lookup values and do a linear temperature in log(T) -! Convert back to cgs - - if(idspecies .eq. 0) then - - gasgr(i) = gasgra(indixe(i)) + tdef(i) - & *(gasgra(indixe(i)+1) -gasgra(indixe(i))) - -!! gasgr(i) = fgr * gasgr(i) * coolunit / mh - gasgr(i) = (dust2gas(i) / metallicity(i)) - & * gasgr(i) * coolunit / mh - !! apply to (idustfield .eq. 1) GC20200701 -!! write(*,*) 'a', gasgr(i) - - else - - fv2k = gasgr2a(indixe(i)) + tdef(i) - & *(gasgr2a(indixe(i)+1) -gasgr2a(indixe(i))) - - fac = coolunit / mh - - if ( itdmulti .eq. 0 ) then - - gasgr(i) = fv2k * sgtot(i) * fac -!! write(*,*) 'b', gasgr(i) / metallicity(i) - - else - - if (idspecies .gt. 0) then - gasMgSiO3 (i) = fv2k * sgMgSiO3 (i) * fac -!! write(*,*) fv2k, sgMgSiO3(i), fac - gasAC (i) = fv2k * sgAC (i) * fac - endif - if (idspecies .gt. 1) then - gasSiM (i) = fv2k * sgSiM (i) * fac - gasFeM (i) = fv2k * sgFeM (i) * fac - gasMg2SiO4 (i) = fv2k * sgMg2SiO4 (i) * fac - gasFe3O4 (i) = fv2k * sgFe3O4 (i) * fac - gasSiO2D (i) = fv2k * sgSiO2D (i) * fac - gasMgO (i) = fv2k * sgMgO (i) * fac - gasFeS (i) = fv2k * sgFeS (i) * fac - gasAl2O3 (i) = fv2k * sgAl2O3 (i) * fac - endif - if (idspecies .gt. 2) then - gasreforg (i) = fv2k * sgreforg (i) * fac - gasvolorg (i) = fv2k * sgvolorg (i) * fac - gasH2Oice (i) = fv2k * sgH2Oice (i) * fac - endif - - endif - - endif - endif ! itmask enddo - -! if (idspecies .eq. 0) then -! do itd = 1, gr_N(2) -! do i = is+1, ie + 1 -! if(itmask(i)) then -! altot(itd,i)= -! & (SN0_kpFeM (4*itd,1)*SN0_fFeM (1) -! & /SN0_r0FeM (3 ,1) -! & +SN0_kpMg2SiO4(4*itd,1)*SN0_fMg2SiO4(1) -! & /SN0_r0Mg2SiO4(3 ,1) -! & +SN0_kpMgSiO3 (4*itd,1)*SN0_fMgSiO3 (1) -! & /SN0_r0MgSiO3 (3 ,1) -! & +SN0_kpFeS (4*itd,1)*SN0_fFeS (1) -! & /SN0_r0FeS (3 ,1) -! & +SN0_kpreforg (4*itd,1)*SN0_freforg (1) -! & /SN0_r0reforg (3 ,1) -! & +SN0_kpvolorg (4*itd,1)*SN0_fvolorg (1) -! & /SN0_r0volorg (3 ,1) -! & +SN0_kpH2Oice (4*itd,1)*SN0_fH2Oice (1) -! & /SN0_r0H2Oice (3 ,1)) -! & * metal(i,j,k) / d(i,j,k) -! endif -! enddo -! enddo -! endif - ! --- Compute dust temperature in a slice --- - if (itdmulti .eq. 0) then - - call calc_tdust_1d_g(tdust, tgas, nh, gasgr, - & mygisrf, myisrf, itmask, trad, in, is, ie, j, k - & , gr_N, gr_Size, gr_dT, gr_Td, altot, kptot, idspecies) - -!! write(*,*) tdust - - else - - if (idspecies .gt. 0) then - call calc_tdust_1d_g(tMgSiO3 , tgas, nh, gasMgSiO3 , - & gisrfMgSiO3 , myisrf, itmask, trad, in, is, ie, j, k - & , gr_N, gr_Size, gr_dT, gr_Td, alMgSiO3 , kpMgSiO3 - & , idspecies) -!! write(*,*) 'bb2', gasMgSiO3, gisrfMgSiO3, myisrf, tMgSiO3 - - call calc_tdust_1d_g(tAC , tgas, nh, gasAC , - & gisrfAC , myisrf, itmask, trad, in, is, ie, j, k - & , gr_N, gr_Size, gr_dT, gr_Td, alAC , kpAC - & , idspecies) -!! write(*,*) 'bb2', gasAC , gisrfAC , myisrf, tAC - endif - - if (idspecies .gt. 1) then - call calc_tdust_1d_g(tSiM , tgas, nh, gasSiM , - & gisrfSiM , myisrf, itmask, trad, in, is, ie, j, k - & , gr_N, gr_Size, gr_dT, gr_Td, alSiM , kpSiM - & , idspecies) - - call calc_tdust_1d_g(tFeM , tgas, nh, gasFeM , - & gisrfFeM , myisrf, itmask, trad, in, is, ie, j, k - & , gr_N, gr_Size, gr_dT, gr_Td, alFeM , kpFeM - & , idspecies) - - call calc_tdust_1d_g(tMg2SiO4 , tgas, nh, gasMg2SiO4 , - & gisrfMg2SiO4 , myisrf, itmask, trad, in, is, ie, j, k - & , gr_N, gr_Size, gr_dT, gr_Td, alMg2SiO4 , kpMg2SiO4 - & , idspecies) - - call calc_tdust_1d_g(tFe3O4 , tgas, nh, gasFe3O4 , - & gisrfFe3O4 , myisrf, itmask, trad, in, is, ie, j, k - & , gr_N, gr_Size, gr_dT, gr_Td, alFe3O4 , kpFe3O4 - & , idspecies) - - call calc_tdust_1d_g(tSiO2D , tgas, nh, gasSiO2D , - & gisrfSiO2D , myisrf, itmask, trad, in, is, ie, j, k - & , gr_N, gr_Size, gr_dT, gr_Td, alSiO2D , kpSiO2D - & , idspecies) - - call calc_tdust_1d_g(tMgO , tgas, nh, gasMgO , - & gisrfMgO , myisrf, itmask, trad, in, is, ie, j, k - & , gr_N, gr_Size, gr_dT, gr_Td, alMgO , kpMgO - & , idspecies) - - call calc_tdust_1d_g(tFeS , tgas, nh, gasFeS , - & gisrfFeS , myisrf, itmask, trad, in, is, ie, j, k - & , gr_N, gr_Size, gr_dT, gr_Td, alFeS , kpFeS - & , idspecies) - - call calc_tdust_1d_g(tAl2O3 , tgas, nh, gasAl2O3 , - & gisrfAl2O3 , myisrf, itmask, trad, in, is, ie, j, k - & , gr_N, gr_Size, gr_dT, gr_Td, alAl2O3 , kpAl2O3 - & , idspecies) - endif - - if (idspecies .gt. 2) then - call calc_tdust_1d_g(treforg , tgas, nh, gasreforg , - & gisrfreforg , myisrf, itmask, trad, in, is, ie, j, k - & , gr_N, gr_Size, gr_dT, gr_Td, alreforg , kpreforg - & , idspecies) - - call calc_tdust_1d_g(tvolorg , tgas, nh, gasvolorg , - & gisrfvolorg , myisrf, itmask, trad, in, is, ie, j, k - & , gr_N, gr_Size, gr_dT, gr_Td, alvolorg , kpvolorg - & , idspecies) - - call calc_tdust_1d_g(tH2Oice , tgas, nh, gasH2Oice , - & gisrfH2Oice , myisrf, itmask, trad, in, is, ie, j, k - & , gr_N, gr_Size, gr_dT, gr_Td, alH2Oice , kpH2Oice - & , idspecies) - endif + call calc_all_tdust_gasgr_1d_g(in, jn, kn, nratec, + & idustfield, is, ie, j, k, fgr, gamma_isrfa, + & trad, gasgra, indixe, tdef, tgas, tdust, + & metallicity, dust2gas, nh, gasgr_tdust, + & itmask, + & idspecies, itdmulti, gr_N, gr_Size, gr_dT, + & gr_Td, tSiM, tFeM, tMg2SiO4, tMgSiO3, tFe3O4, + & tAC, tSiO2D, tMgO, tFeS, tAl2O3, treforg, + & tvolorg, tH2Oice, gasgr2a, gamma_isrf2a, + & coolunit, gasgr, myisrf, sgSiM, sgFeM, sgMg2SiO4, + & sgMgSiO3, sgFe3O4, sgAC, sgSiO2D, sgMgO, sgFeS, + & sgAl2O3, sgreforg, sgvolorg, sgH2Oice, sgtot, + & alSiM, alFeM, alMg2SiO4, alMgSiO3, alFe3O4, alAC, + & alSiO2D, alMgO, alFeS, alAl2O3, alreforg, + & alvolorg, alH2Oice, altot, kpSiM, kpFeM, + & kpMg2SiO4, kpMgSiO3, kpFe3O4, kpAC, kpSiO2D, + & kpMgO, kpFeS, kpAl2O3, kpreforg, kpvolorg, + & kpH2Oice, kptot, gasSiM, gasFeM, gasMg2SiO4, + & gasMgSiO3, gasFe3O4, gasAC, gasSiO2D, gasMgO, + & gasFeS, gasAl2O3, gasreforg, gasvolorg, + & gasH2Oice + & ) - endif ! Copy slice values back to grid @@ -759,21 +508,7 @@ subroutine calc_tdust_3d_g( ! parallelize the k and j loops with OpenMP ! flat j and k loops for better parallelism #ifdef _OPENMP -!$omp parallel do schedule(runtime) private( -!$omp& i, j, k, -!$omp& comp1, comp2, energy, -!$omp& indixe, -!$omp& t1, t2, logtem, tdef, p2d, -!$omp& tgas, tgasold, -!$omp& tdust, metallicity, dust2gas, rhoH, mmw, -!$omp& mynh, myde, gammaha_eff, gasgr_tdust, regr, edot, -!$omp& ceHI, ceHeI, ceHeII, -!$omp& ciHI, ciHeI, ciHeIS, ciHeII, -!$omp& reHII, reHeII1, reHeII2, reHeIII, -!$omp& brem, cieco, -!$omp& hyd01k, h2k01, vibh, roth, rotl, -!$omp& gpldl, gphdl, hdlte, hdlow, -!$omp& itmask ) +!$omp parallel do schedule(runtime) private(i, j, k) #endif do t = 0, dk*dj-1 k = t/dj + ks+1 @@ -796,39 +531,6 @@ subroutine calc_tdust_3d_g( metal_P200(i,j,k)=metal_P200(i,j,k)*real(aye**3, RKIND) metal_Y19(i,j,k) = metal_Y19(i,j,k)*real(aye**3, RKIND) endif -!! if (metal(i,j,k) .gt. 1.d-9 * d(i,j,k)) then - if (imchem .eq. 1) then - CI(i,j,k) = CI(i,j,k)*real(aye**3, RKIND) - CII(i,j,k) = CII(i,j,k)*real(aye**3, RKIND) - CO(i,j,k) = CO(i,j,k)*real(aye**3, RKIND) - CO2(i,j,k) = CO2(i,j,k)*real(aye**3, RKIND) - OI(i,j,k) = OI(i,j,k)*real(aye**3, RKIND) - OH(i,j,k) = OH(i,j,k)*real(aye**3, RKIND) - H2O(i,j,k) = H2O(i,j,k)*real(aye**3, RKIND) - O2(i,j,k) = O2(i,j,k)*real(aye**3, RKIND) - SiI(i,j,k) = SiI(i,j,k)*real(aye**3, RKIND) - SiOI(i,j,k) = SiOI(i,j,k)*real(aye**3, RKIND) - SiO2I(i,j,k) = SiO2I(i,j,k)*real(aye**3, RKIND) - CH(i,j,k) = CH(i,j,k)*real(aye**3, RKIND) - CH2(i,j,k) = CH2(i,j,k)*real(aye**3, RKIND) - COII(i,j,k) = COII(i,j,k)*real(aye**3, RKIND) - OII(i,j,k) = OII(i,j,k)*real(aye**3, RKIND) - OHII(i,j,k) = OHII(i,j,k)*real(aye**3, RKIND) - H2OII(i,j,k) = H2OII(i,j,k)*real(aye**3, RKIND) - H3OII(i,j,k) = H3OII(i,j,k)*real(aye**3, RKIND) - O2II(i,j,k) = O2II(i,j,k)*real(aye**3, RKIND) - if ( ( igrgr .eq. 1 ) .or. ( idsub .eq. 1 ) ) then - if (idspecies .gt. 0) then - Mg(i,j,k) = Mg(i,j,k)*real(aye**3, RKIND) - endif - if (idspecies .gt. 1) then - Al(i,j,k) = Al(i,j,k)*real(aye**3, RKIND) - S(i,j,k) = S(i,j,k)*real(aye**3, RKIND) - Fe(i,j,k) = Fe(i,j,k)*real(aye**3, RKIND) - endif - endif - endif -!! endif enddo endif if (idustfield .eq. 1) then diff --git a/src/clib/cool1d_multi_g.F b/src/clib/cool1d_multi_g.F index 75fa7dedf..fa87e8e71 100644 --- a/src/clib/cool1d_multi_g.F +++ b/src/clib/cool1d_multi_g.F @@ -345,17 +345,6 @@ subroutine cool1d_multi_g( & , gasSiO2D(in), gasMgO(in), gasFeS(in) & , gasAl2O3(in) & , gasreforg(in), gasvolorg(in), gasH2Oice(in) - real*8 gasgr_tSiM(in), gasgr_tFeM(in), gasgr_tMg2SiO4(in) - & , gasgr_tMgSiO3(in), gasgr_tFe3O4(in), gasgr_tAC(in) - & , gasgr_tSiO2D(in), gasgr_tMgO(in), gasgr_tFeS(in) - & , gasgr_tAl2O3(in) - & , gasgr_treforg(in), gasgr_tvolorg(in), gasgr_tH2Oice(in) - real*8 mygisrf(in), fv2k, fac - real*8 gisrfSiM(in), gisrfFeM(in), gisrfMg2SiO4(in) - & , gisrfMgSiO3(in), gisrfFe3O4(in), gisrfAC(in) - & , gisrfSiO2D(in), gisrfMgO(in), gisrfFeS(in) - & , gisrfAl2O3(in) - & , gisrfreforg(in), gisrfvolorg(in), gisrfH2Oice(in) ! Iteration mask MASK_TYPE itmask(in), anydust, interp @@ -1155,240 +1144,37 @@ subroutine cool1d_multi_g( endif endif -! Calculate heating from interstellar radiation field -! -> this is ONLY used when `itmask_metal .eq. MASK_TRUE` - - if ((anydust .ne. MASK_FALSE) .or. (igammah .gt. 1)) then - do i = is+1, ie+1 - if ( itmask_metal(i) .ne. MASK_FALSE ) then - - if (idspecies .eq. 0 ) then - if (idustfield .gt. 0) then - mygisrf(i) = gamma_isrfa - & * fgr / dust2gas(i) * metallicity(i) - !! correct with the depletion or enhancement of condensation rate. - else - mygisrf(i) = gamma_isrfa - endif - - else ! idspecies - - if (itdmulti .eq. 0) then - - mygisrf(i) = gamma_isrf2a * sgtot(i) - !! in UV, absorption coefficient Q ~ 1 (Goldsmith 2001) - !! so we use the geometrical cross-section of grains [cgs] - - else - - if (idspecies .gt. 0) then - gisrfMgSiO3 (i) = gamma_isrf2a * sgMgSiO3 (i) -!! write(*,*) 'sil', d(i,j,k), gamma_isrf2a, sgMgSiO3(i) - gisrfAC (i) = gamma_isrf2a * sgAC (i) -!! write(*,*) 'car', d(i,j,k), gamma_isrf2a, sgMgSiO3(i) - endif - if (idspecies .gt. 1) then - gisrfSiM (i) = gamma_isrf2a * sgSiM (i) - gisrfFeM (i) = gamma_isrf2a * sgFeM (i) - gisrfMg2SiO4 (i) = gamma_isrf2a * sgMg2SiO4 (i) - gisrfFe3O4 (i) = gamma_isrf2a * sgFe3O4 (i) - gisrfSiO2D (i) = gamma_isrf2a * sgSiO2D (i) - gisrfMgO (i) = gamma_isrf2a * sgMgO (i) - gisrfFeS (i) = gamma_isrf2a * sgFeS (i) - gisrfAl2O3 (i) = gamma_isrf2a * sgAl2O3 (i) - endif - if (idspecies .gt. 2) then - gisrfreforg (i) = gamma_isrf2a * sgreforg (i) - gisrfvolorg (i) = gamma_isrf2a * sgvolorg (i) - gisrfH2Oice (i) = gamma_isrf2a * sgH2Oice (i) - endif - - endif - - endif ! idspecies - - endif - enddo - endif - -! --- Gas to grain heat transfer --- - + ! compute dust temperature and cooling due to dust if (anydust .ne. MASK_FALSE) then -! Look up gas/grain heat transfer rates - - do i = is+1, ie+1 - if ( itmask_metal(i) .ne. MASK_FALSE ) then - - if(idspecies .eq. 0) then - - gasgr(i) = gasgra(indixe(i)) + tdef(i) - & *(gasgra(indixe(i)+1) -gasgra(indixe(i))) - -!! gasgr_tdust(i) = fgr * gasgr(i) * coolunit / mh - gasgr_tdust(i) = (dust2gas(i) / metallicity(i)) - & * gasgr(i) * coolunit / mh - !! apply to (idustfield .eq. 1) GC20200701 - - else ! idspecies - - fv2k = gasgr2a(indixe(i)) + tdef(i) - & *(gasgr2a(indixe(i)+1) -gasgr2a(indixe(i))) - - fac = coolunit / mh - - if ( itdmulti .eq. 0 ) then - - gasgr(i) = fv2k * sgtot(i) - - gasgr_tdust(i) = gasgr(i) * fac - - else - - if (idspecies .gt. 0) then - gasMgSiO3 (i) = fv2k * sgMgSiO3 (i) - gasAC (i) = fv2k * sgAC (i) - endif - if (idspecies .gt. 1) then - gasSiM (i) = fv2k * sgSiM (i) - gasFeM (i) = fv2k * sgFeM (i) - gasMg2SiO4 (i) = fv2k * sgMg2SiO4 (i) - gasFe3O4 (i) = fv2k * sgFe3O4 (i) - gasSiO2D (i) = fv2k * sgSiO2D (i) - gasMgO (i) = fv2k * sgMgO (i) - gasFeS (i) = fv2k * sgFeS (i) - gasAl2O3 (i) = fv2k * sgAl2O3 (i) - endif - if (idspecies .gt. 2) then - gasreforg (i) = fv2k * sgreforg (i) - gasvolorg (i) = fv2k * sgvolorg (i) - gasH2Oice (i) = fv2k * sgH2Oice (i) - endif - - if (idspecies .gt. 0) then - gasgr_tMgSiO3 (i) = gasMgSiO3 (i) * fac - gasgr_tAC (i) = gasAC (i) * fac - endif - if (idspecies .gt. 1) then - gasgr_tSiM (i) = gasSiM (i) * fac - gasgr_tFeM (i) = gasFeM (i) * fac - gasgr_tMg2SiO4 (i) = gasMg2SiO4 (i) * fac - gasgr_tFe3O4 (i) = gasFe3O4 (i) * fac - gasgr_tSiO2D (i) = gasSiO2D (i) * fac - gasgr_tMgO (i) = gasMgO (i) * fac - gasgr_tFeS (i) = gasFeS (i) * fac - gasgr_tAl2O3 (i) = gasAl2O3 (i) * fac - endif - if (idspecies .gt. 2) then - gasgr_treforg (i) = gasreforg (i) * fac - gasgr_tvolorg (i) = gasvolorg (i) * fac - gasgr_tH2Oice (i) = gasH2Oice (i) * fac - endif - - endif - - endif ! idspecies - - endif - enddo - -! Compute dust temperature - - if (itdmulti .eq. 0) then - - call calc_tdust_1d_g(tdust, tgas, mynh, gasgr_tdust, - & mygisrf, myisrf, itmask_metal, comp2, in, is, ie, j, k - & , gr_N, gr_Size, gr_dT, gr_Td, altot, kptot, idspecies) - - else - - if (idspecies .gt. 0) then - call calc_tdust_1d_g(tMgSiO3 , tgas, mynh, gasgr_tMgSiO3 , - & gisrfMgSiO3 , myisrf, itmask_metal, comp2, - & in, is, ie, j, k - & , gr_N, gr_Size, gr_dT, gr_Td, alMgSiO3 , kpMgSiO3 - & , idspecies) - - call calc_tdust_1d_g(tAC , tgas, mynh, gasgr_tAC , - & gisrfAC , myisrf, itmask_metal, comp2, - & in, is, ie, j, k - & , gr_N, gr_Size, gr_dT, gr_Td, alAC , kpAC - & , idspecies) - endif - - if (idspecies .gt. 1) then - call calc_tdust_1d_g(tSiM , tgas, mynh, gasgr_tSiM , - & gisrfSiM , myisrf, itmask_metal, comp2, - & in, is, ie, j, k - & , gr_N, gr_Size, gr_dT, gr_Td, alSiM , kpSiM - & , idspecies) - - call calc_tdust_1d_g(tFeM , tgas, mynh, gasgr_tFeM , - & gisrfFeM , myisrf, itmask_metal, comp2, - & in, is, ie, j, k - & , gr_N, gr_Size, gr_dT, gr_Td, alFeM , kpFeM - & , idspecies) - - call calc_tdust_1d_g(tMg2SiO4 , tgas, mynh, gasgr_tMg2SiO4 , - & gisrfMg2SiO4 , myisrf, itmask_metal, comp2, - & in, is, ie, j, k - & , gr_N, gr_Size, gr_dT, gr_Td, alMg2SiO4 , kpMg2SiO4 - & , idspecies) - - call calc_tdust_1d_g(tFe3O4 , tgas, mynh, gasgr_tFe3O4 , - & gisrfFe3O4 , myisrf, itmask_metal, comp2, - & in, is, ie, j, k - & , gr_N, gr_Size, gr_dT, gr_Td, alFe3O4 , kpFe3O4 - & , idspecies) - - call calc_tdust_1d_g(tSiO2D , tgas, mynh, gasgr_tSiO2D , - & gisrfSiO2D , myisrf, itmask_metal, comp2, - & in, is, ie, j, k - & , gr_N, gr_Size, gr_dT, gr_Td, alSiO2D , kpSiO2D - & , idspecies) - - call calc_tdust_1d_g(tMgO , tgas, mynh, gasgr_tMgO , - & gisrfMgO , myisrf, itmask_metal, comp2, - & in, is, ie, j, k - & , gr_N, gr_Size, gr_dT, gr_Td, alMgO , kpMgO - & , idspecies) - - call calc_tdust_1d_g(tFeS , tgas, mynh, gasgr_tFeS , - & gisrfFeS , myisrf, itmask_metal, comp2, - & in, is, ie, j, k - & , gr_N, gr_Size, gr_dT, gr_Td, alFeS , kpFeS - & , idspecies) - - call calc_tdust_1d_g(tAl2O3 , tgas, mynh, gasgr_tAl2O3 , - & gisrfAl2O3 , myisrf, itmask_metal, comp2, - & in, is, ie, j, k - & , gr_N, gr_Size, gr_dT, gr_Td, alAl2O3 , kpAl2O3 - & , idspecies) - endif - - if (idspecies .gt. 2) then - call calc_tdust_1d_g(treforg , tgas, mynh, gasgr_treforg , - & gisrfreforg , myisrf, itmask_metal, comp2, - & in, is, ie, j, k - & , gr_N, gr_Size, gr_dT, gr_Td, alreforg , kpreforg - & , idspecies) - - call calc_tdust_1d_g(tvolorg , tgas, mynh, gasgr_tvolorg , - & gisrfvolorg , myisrf, itmask_metal, comp2, - & in, is, ie, j, k - & , gr_N, gr_Size, gr_dT, gr_Td, alvolorg , kpvolorg - & , idspecies) - - call calc_tdust_1d_g(tH2Oice , tgas, mynh, gasgr_tH2Oice , - & gisrfH2Oice , myisrf, itmask_metal, comp2, - & in, is, ie, j, k - & , gr_N, gr_Size, gr_dT, gr_Td, alH2Oice , kpH2Oice - & , idspecies) - endif + call calc_all_tdust_gasgr_1d_g(in, jn, kn, nratec, + & idustfield, is, ie, j, k, fgr, gamma_isrfa, + & comp2, gasgra, indixe, tdef, tgas, tdust, + & metallicity, dust2gas, mynh, gasgr_tdust, + & itmask_metal, + & idspecies, itdmulti, gr_N, gr_Size, gr_dT, + & gr_Td, tSiM, tFeM, tMg2SiO4, tMgSiO3, tFe3O4, + & tAC, tSiO2D, tMgO, tFeS, tAl2O3, treforg, + & tvolorg, tH2Oice, gasgr2a, gamma_isrf2a, + & coolunit, gasgr, myisrf, sgSiM, sgFeM, sgMg2SiO4, + & sgMgSiO3, sgFe3O4, sgAC, sgSiO2D, sgMgO, sgFeS, + & sgAl2O3, sgreforg, sgvolorg, sgH2Oice, sgtot, + & alSiM, alFeM, alMg2SiO4, alMgSiO3, alFe3O4, alAC, + & alSiO2D, alMgO, alFeS, alAl2O3, alreforg, + & alvolorg, alH2Oice, altot, kpSiM, kpFeM, + & kpMg2SiO4, kpMgSiO3, kpFe3O4, kpAC, kpSiO2D, + & kpMgO, kpFeS, kpAl2O3, kpreforg, kpvolorg, + & kpH2Oice, kptot, gasSiM, gasFeM, gasMg2SiO4, + & gasMgSiO3, gasFe3O4, gasAC, gasSiO2D, gasMgO, + & gasFeS, gasAl2O3, gasreforg, gasvolorg, + & gasH2Oice + & ) endif ! Calculate dust cooling rate + if (anydust .ne. MASK_FALSE) then + do i = is+1, ie+1 if ( itmask_metal(i) .ne. MASK_FALSE ) then diff --git a/src/include/grackle_fortran_interface.def b/src/include/grackle_fortran_interface.def index bd86796c6..96db23394 100644 --- a/src/include/grackle_fortran_interface.def +++ b/src/include/grackle_fortran_interface.def @@ -152,7 +152,6 @@ c This is the fortran definition of grackle_chemistry_data INTEGER(C_INT) :: primordial_chemistry INTEGER(C_INT) :: dust_chemistry INTEGER(C_INT) :: metal_cooling - REAL(C_DOUBLE) :: tabulated_cooling_minimum_temperature INTEGER(C_INT) :: UVbackground TYPE(C_PTR) :: grackle_data_file INTEGER(C_INT) :: cmb_temperature_floor @@ -161,6 +160,7 @@ c This is the fortran definition of grackle_chemistry_data INTEGER(C_INT) :: use_dust_density_field INTEGER(C_INT) :: dust_recombination_cooling INTEGER(C_INT) :: metal_chemistry + REAL(C_DOUBLE) :: tabulated_cooling_minimum_temperature INTEGER(C_INT) :: grain_growth INTEGER(C_INT) :: multi_metals INTEGER(C_INT) :: metal_abundances @@ -275,7 +275,7 @@ c The following define the fortran interfaces to the C routines IMPORT TYPE(grackle_units), INTENT(IN) :: my_units TYPE(grackle_field_data), INTENT(IN) :: my_fields - REAL(C_DOUBLE), INTENT(IN) :: dt + REAL(C_DOUBLE), VALUE, INTENT(IN) :: dt END FUNCTION solve_chemistry END INTERFACE @@ -335,4 +335,4 @@ c The following define the fortran interfaces to the C routines IMPORT TYPE(grackle_field_data), INTENT(INOUT) :: my_fields END FUNCTION gr_initialize_field_data - END INTERFACE \ No newline at end of file + END INTERFACE diff --git a/src/python/pygrackle/utilities/data_path.py b/src/python/pygrackle/utilities/data_path.py index c640258c5..ffa749694 100644 --- a/src/python/pygrackle/utilities/data_path.py +++ b/src/python/pygrackle/utilities/data_path.py @@ -16,20 +16,12 @@ from pygrackle.__config__ import _is_editable_installation from pygrackle.utilities.misc import dirname -grackle_data_dir = os.environ.get("GRACKLE_DATA_DIR") -if (grackle_data_dir is None) and _is_editable_installation(): +if _is_editable_installation(): # Note, this only works with an editable install of pygrackle. _install_dir = dirname(os.path.abspath(__file__), level=5) grackle_data_dir = os.path.join(_install_dir, "input") -elif (grackle_data_dir is None): +else: raise RuntimeError( "in non-editable pygrackle installations, like this one, " - f"grackle_data_dir can only be imported from {__file__} if it is set " - "by the GRACKLE_DATA_DIR environment variable" + f"grackle_data_dir cannot be imported from {__file__}." ) - -if not os.path.isdir(grackle_data_dir): - raise RuntimeError( - f"grackle_data_dir not set to a valid directory: {grackle_data_dir}. " - "Use the GRACKLE_DATA_DIR environment variable to set path to " - "Grackle data.") diff --git a/src/python/pygrackle/utilities/testing.py b/src/python/pygrackle/utilities/testing.py index 71dae01ee..a1665a5e6 100644 --- a/src/python/pygrackle/utilities/testing.py +++ b/src/python/pygrackle/utilities/testing.py @@ -11,7 +11,6 @@ # software. ######################################################################## -import contextlib import importlib import numpy as np from numpy.testing import assert_array_equal, assert_almost_equal, \ @@ -21,7 +20,6 @@ import os import shutil import subprocess -import tempfile def assert_rel_equal(a1, a2, decimals, err_msg='', verbose=True): if isinstance(a1, np.ndarray): @@ -60,9 +58,9 @@ def ftrue(func): else: return ftrue -def run_command(command, timeout=None): +def run_command(command, timeout=None, cwd=None): try: - proc = subprocess.run(command, shell=True, timeout=timeout) + proc = subprocess.run(command, shell=True, timeout=timeout, cwd=cwd) if proc.returncode == 0: success = True else: @@ -74,28 +72,3 @@ def run_command(command, timeout=None): print ("Killed by keyboard interrupt!") success = False return success - -@contextlib.contextmanager -def temporary_directory(): - curdir = os.getcwd() - tmpdir = tempfile.mkdtemp(dir=curdir) - os.chdir(tmpdir) - try: - yield tmpdir - finally: - os.chdir(curdir) - shutil.rmtree(tmpdir) - -def ensure_dir(path): - r"""Parallel safe directory maker.""" - if os.path.exists(path): - return path - - try: - os.makedirs(path) - except OSError as e: - if e.errno == errno.EEXIST: - pass - else: - raise - return path diff --git a/src/python/tests/conftest.py b/src/python/tests/conftest.py new file mode 100644 index 000000000..7e78378be --- /dev/null +++ b/src/python/tests/conftest.py @@ -0,0 +1,67 @@ +# this defines some basic utilities shared among all of the tests +# +# DO NOT import pygrackle into global scope in this file (or in any file +# imported by this file). Some tests need to be runable without installing +# pygrackle + +import os +from typing import NamedTuple + +import pytest + + +# this hook is used to add more command line flags to the pytest launcher +def pytest_addoption(parser): + parser.addoption( + "--answer-dir", + action="store", + default=None, + help=( + "Path to directory used for storing answer-tests answers. All " + "logic related to answer-tests is disabled unless this is " + "specified. When specified alonside --answer-store, logic to " + "generate the answers will be run (the answers are stored to this " + "directory). When specified WITHOUT --answer-store, the results " + "of each answer-test is compared against the answers that were " + "previously stored in this directory." + ) + ) + + parser.addoption( + "--answer-store", + action="store_true", + help=( + "Indicates that we should store answer-test results. It is an " + "error to specifiy this arg without --answer-dir." + ), + ) + + +class AnswerTestSpec(NamedTuple): + generate_answers: bool + answer_dir: str + + +@pytest.fixture(scope="session") +def answertestspec(request): + """ + Return an object specifying all user-specified directives regarding + answer tests (whether to run them and where to store/find results) + """ + + generate_answers = request.config.getoption("--answer-store") + + answer_dir = request.config.getoption("--answer-dir") + + if (answer_dir is None) and generate_answers: + raise ValueError( + "--answer-store option can't be specified without --answer-dir" + ) + elif answer_dir is None: + pytest.skip("no --answer-dir option found") + elif (not os.path.isdir(answer_dir)) and (not generate_answers): + pytest.skip(f"directory of test answers can't be found, {answer_dir}") + else: + os.makedirs(answer_dir, exist_ok=True) + + return AnswerTestSpec(generate_answers=generate_answers, answer_dir=answer_dir) diff --git a/src/python/tests/test_code_examples.py b/src/python/tests/test_code_examples.py index b1101f97c..c6cc07d19 100644 --- a/src/python/tests/test_code_examples.py +++ b/src/python/tests/test_code_examples.py @@ -18,10 +18,7 @@ import re import subprocess -from testing_common import \ - generate_test_results, \ - grackle_install_dir, \ - test_answers_dir +from testing_common import grackle_install_dir try: from pygrackle.__config__ import \ @@ -58,12 +55,17 @@ "temperature" ) -test_file = os.path.join(test_answers_dir, "code_examples.json") -if generate_test_results and os.path.exists(test_file): - os.remove(test_file) -if not generate_test_results and not os.path.exists(test_file): - raise RuntimeError( - f"Code example results file not found: {test_file}") +@pytest.fixture(scope="module") +def test_file(answertestspec): + # this fixture has module-scope so that we will delete the test file once + test_file = os.path.join(answertestspec.answer_dir, "code_examples.json") + if answertestspec.generate_answers and os.path.exists(test_file): + os.remove(test_file) + + # if test_file doesn't exist and answertestspec.generate_answers is False, + # defer any error reporting until within the test-case (after the test-case + # determines whether or not it should be skipped) + return test_file def run_command(command, cwd, env, timeout=None): proc = subprocess.run( @@ -107,7 +109,7 @@ def parse_output(ostr): return results @pytest.mark.parametrize("example", code_examples) -def test_code_examples(example): +def test_code_examples(answertestspec, test_file, example): # under the classic build system, we could just execute `make` in the examples # directory and there is a good chance that it would work out... # -> now we require the PYTEST_CODE_LINK_CHOICE environment variable to be @@ -139,7 +141,16 @@ def test_code_examples(example): else: raise RuntimeError("PYTEST_CODE_LINK_CHOICE must be '', 'classic' or " "'cmake:'. {choice!r} is invalid") + + # if we aren't generating test-answers, and the test-file can't be found + # report an error (we explicitly wait to do this until after we have + # decided whether to skip the test or not). + if not answertestspec.generate_answers and not os.path.exists(test_file): + raise RuntimeError(f"Code example results file not found: {test_file}") + env = dict(os.environ) + + # compile the example command = f'{make_command} {example}' run_command(command, examples_dir, env, timeout=60) @@ -152,7 +163,7 @@ def test_code_examples(example): if example not in compare_exclude: results = parse_output(proc.stdout) - if generate_test_results: + if answertestspec.generate_answers: if os.path.exists(test_file): with open(test_file, mode="r") as f: all_results = json.load(f) diff --git a/src/python/tests/test_get_grackle_version.py b/src/python/tests/test_get_grackle_version.py index de536d2f4..14df01ca6 100644 --- a/src/python/tests/test_get_grackle_version.py +++ b/src/python/tests/test_get_grackle_version.py @@ -19,12 +19,17 @@ def query_grackle_version_props(): # retrieve the current version information with git - os.chdir(os.path.dirname(os.path.abspath(__file__))) + + cur_dir = os.path.dirname(os.path.abspath(__file__)) + + def _run(args): + _rslt = subprocess.run( + args, cwd=cur_dir, check=True, capture_output=True, + ) + return _rslt.stdout.decode().rstrip() # get the name of the most recent tag preceeding this commit: - _rslt = subprocess.run(["git", "describe", "--abbrev=0", "--tags"], - check = True, capture_output = True) - most_recent_tag = _rslt.stdout.decode().rstrip() + most_recent_tag = _run(["git", "describe", "--abbrev=0", "--tags"]) if most_recent_tag.startswith("grackle-"): latest_tagged_version = most_recent_tag[8:] elif most_recent_tag.startswith("gold-standard-v"): @@ -36,18 +41,12 @@ def query_grackle_version_props(): ) # get the actual revision when most_recent tag was introduced - _rslt = subprocess.run(["git", "rev-parse", "-n", "1", most_recent_tag], - check = True, capture_output = True) - revision_of_tag = _rslt.stdout.decode().rstrip() + revision_of_tag = _run(["git", "rev-parse", "-n", "1", most_recent_tag]) # get the branch name and current revision - _rslt = subprocess.run(["git", "rev-parse", "--abbrev-ref", "HEAD"], - check = True, capture_output = True) - branch = _rslt.stdout.decode().rstrip() + branch = _run(["git", "rev-parse", "--abbrev-ref", "HEAD"]) - _rslt = subprocess.run(["git", "rev-parse", "HEAD"], - check = True, capture_output = True) - revision = _rslt.stdout.decode().rstrip() + revision = _run(["git", "rev-parse", "HEAD"]) tagged_on_current_revision = revision == revision_of_tag return latest_tagged_version, branch, revision, tagged_on_current_revision diff --git a/src/python/tests/test_initialisation.py b/src/python/tests/test_initialisation.py index 071df492a..5fa05e355 100644 --- a/src/python/tests/test_initialisation.py +++ b/src/python/tests/test_initialisation.py @@ -14,15 +14,7 @@ from pygrackle import chemistry_data #Necessary constants from grackle from pygrackle.utilities.physical_constants import mass_hydrogen_cgs -from pygrackle.utilities.testing import \ - assert_allclose, \ - ensure_dir - -from testing_common import \ - generate_test_results, \ - test_answers_dir - -ensure_dir(test_answers_dir) +from pygrackle.utilities.testing import assert_allclose #* Function which returns chemistry_data instance with default initialisation settings. def get_defChem(): @@ -108,8 +100,13 @@ def set_parameters(parSet, my_chemistry): #* Function which tests that the rates have been initialised correctly for each parameter set. -def test_rate_initialisation(printParameters=False, printOOMdiscrepanices=False, testCustomFile=False, parSets=[1,2,3,4,5,6,7], - fileName="rate_coefficients.h5"): +def test_rate_initialisation(answertestspec, + tmp_path, + printParameters=False, + printOOMdiscrepanices=False, + testCustomFile=False, + parSets=[1,2,3,4,5,6,7], + fileName="rate_coefficients.h5"): """ Test that the rate tables are initialized correctly. @@ -117,12 +114,15 @@ def test_rate_initialisation(printParameters=False, printOOMdiscrepanices=False, be saved into a hdf5 file. This should be used for testing and is not meant for frequent use. printParameters (bool) --> If set to True will print the parameter settings for each parameter set. - """ - #* Navigate to the directory where the file is located. - filePath = os.path.abspath(__file__) - dirPath = os.path.dirname(filePath) - os.chdir(dirPath) + Fixtures + -------- + answertestspec : AnswerTestSpec + A fixture used for all answer tests + tmp_path : pathlib.Path + A custom built-in fixture provided by pytest that specifies a pre-made temporary directory + that is named to be used with tmp_path + """ #* List of all rate variable names which will be checked. testRates = "k1,k3,k4,k2,k5,k6,k7,k8,k9,k10,k11,k12,k14,k15,k16,k17,k18,k19,k20,k23,"\ @@ -134,8 +134,10 @@ def test_rate_initialisation(printParameters=False, printOOMdiscrepanices=False, #* Calculate rates for each parameter set and write to hdf5 file #Create and open file. If the file already exists this will overwrite it. - if generate_test_results: - fileName = os.path.join(test_answers_dir, fileName) + if answertestspec.generate_answers: + fileName = os.path.join(answertestspec.answer_dir, fileName) + else: + fileName = str(tmp_path / fileName) f = h5py.File(fileName, "w") #Iterate over parameter sets. @@ -163,11 +165,11 @@ def test_rate_initialisation(printParameters=False, printOOMdiscrepanices=False, f.close() # Just generate results and leave. - if generate_test_results: + if answertestspec.generate_answers: return #* Compare rates with the expected (correct) ones which are stored and check they are in agreement - expectedRates = h5py.File(os.path.join(test_answers_dir, fileName), "r") + expectedRates = h5py.File(os.path.join(answertestspec.answer_dir, fileName), "r") initialisedRates = h5py.File(fileName, "r") diff --git a/src/python/tests/test_local_functions.py b/src/python/tests/test_local_functions.py index 62515c2e1..f56f479bf 100644 --- a/src/python/tests/test_local_functions.py +++ b/src/python/tests/test_local_functions.py @@ -17,7 +17,6 @@ import os from numpy.testing import assert_approx_equal -from unittest import TestCase from pygrackle import \ FluidContainer, \ @@ -29,18 +28,11 @@ mass_hydrogen_cgs, \ sec_per_Myr -from pygrackle.utilities.testing import ensure_dir - -from testing_common import \ - generate_test_results, \ - grackle_data_dir, \ - test_answers_dir +from testing_common import grackle_data_dir local_function_test_format_version = 1 _meta_data = {"format_version": local_function_test_format_version} -ensure_dir(test_answers_dir) - parameter_grid = { "use_grackle": (1,), "primordial_chemistry": (0, 1, 2, 3), @@ -100,7 +92,7 @@ def failure_str(my_pars, my_units, my_in, my_out, comp_out, field): msg += f"stored output: {comp_out[field]}" return msg -class LocalFunctionsTest(TestCase): +class TestLocalFunctions: """ Tests for the local functions. """ @@ -108,11 +100,11 @@ class LocalFunctionsTest(TestCase): n_input_sets = 10 seed = 21062024 digits = 12 - test_file = os.path.join(test_answers_dir, "local_function_tests.json") + test_file_basename = "local_function_tests.json" - def setUp(self): + def setup_parameter_sets(self, generate_answers, test_file): """ - Setup local function tests. + Setup parameter-sets for local function tests. If we are generating results, then create a list of parameter values from the parameter_grid and exclude_sets structures defined above. @@ -121,29 +113,31 @@ def setUp(self): If we are not generating results, then read everything in from the json test file. """ + # if we need to reuse this functionality, we need should remove this + # from the class and convert it into a pytest fixture - if generate_test_results: + if generate_answers: my_sets = self.generate_parameter_sets() self.test_sets = [{"parameters": my_set, "units": base_units} for my_set in my_sets] else: - if not os.path.exists(self.test_file): - self.skipTest(f"Test file not found: {self.test_file}.") - with open(self.test_file, mode="r") as f: + if not os.path.exists(test_file): + self.skipTest(f"Test file not found: {test_file}.") + with open(test_file, mode="r") as f: self.test_sets = json.load(f) load_meta = self.test_sets.pop(0) assert load_meta["format_version"] == _meta_data["format_version"], \ f"Test version mismatch: data file is {load_meta['format_version']}, " + \ f"source code is {_meta_data['format_version']}." - def tearDown(self): + def finish_tests(self, generate_answers, test_file): """ Write json test file if we are generating resuls. """ - if generate_test_results: + if generate_answers: # add the format version to the output self.test_sets.insert(0, _meta_data) - with open(self.test_file, mode="w") as f: + with open(test_file, mode="w") as f: f.write(json.dumps(self.test_sets, indent=4)) def generate_parameter_sets(self): @@ -194,7 +188,9 @@ def generate_base_inputs(self): for i in range(self.n_input_sets)] return base_inputs - def test_local_functions(self): + def test_local_functions(self, answertestspec): + test_file = os.path.join(answertestspec.answer_dir, self.test_file_basename) + self.setup_parameter_sets(answertestspec.generate_answers, test_file) for test_set in self.test_sets: par_set = test_set["parameters"] @@ -209,7 +205,7 @@ def test_local_functions(self): setattr(my_chemistry, "grackle_data_file", os.path.join(grackle_data_dir, par_set["grackle_data_file"])) - if generate_test_results: + if answertestspec.generate_answers: my_tests = [] base_inputs = self.generate_base_inputs() n_input_sets = len(base_inputs) @@ -220,7 +216,7 @@ def test_local_functions(self): for itest in range(n_input_sets): my_units = base_units.copy() - if generate_test_results: + if answertestspec.generate_answers: base_input_set = base_inputs[itest] redshift = base_input_set["redshift"] else: @@ -232,7 +228,7 @@ def test_local_functions(self): setattr(my_chemistry, unit, val) my_chemistry.set_velocity_units() - if generate_test_results: + if answertestspec.generate_answers: fc = setup_fluid_container( my_chemistry, density=base_input_set["density"], @@ -262,7 +258,7 @@ def test_local_functions(self): my_out[fname] = fc[fname][0] # Compare with existing results unless we are generating them. - if generate_test_results: + if answertestspec.generate_answers: my_tests.append({"input": my_in, "output": my_out}) else: comp_out = my_tests[itest]["output"] @@ -275,5 +271,6 @@ def test_local_functions(self): significant=self.digits, err_msg=err_msg) - if generate_test_results: + if answertestspec.generate_answers: test_set["tests"] = my_tests + self.finish_tests(answertestspec.generate_answers, test_file) diff --git a/src/python/tests/test_models.py b/src/python/tests/test_models.py index 755c08ddb..4d517b71e 100644 --- a/src/python/tests/test_models.py +++ b/src/python/tests/test_models.py @@ -6,20 +6,14 @@ from numpy.testing import assert_allclose from pygrackle.utilities.model_tests import model_sets -from pygrackle.utilities.testing import \ - ensure_dir, \ - run_command, \ - temporary_directory +from pygrackle.utilities.testing import run_command -from testing_common import \ - generate_test_results, \ - grackle_python_dir, \ - test_answers_dir +from testing_common import grackle_python_dir python_example_dir = os.path.join(grackle_python_dir, "examples") -ensure_dir(test_answers_dir) - +# collect all of the python-examples and various model configurations into +# a list of tuples all_sets = [] for model_name, model in model_sets.items(): for par_index in range(len(model["parameter_sets"])): @@ -27,17 +21,33 @@ all_sets.append((model_name, par_index, input_index)) @pytest.mark.parametrize("model_name, par_index, input_index", all_sets) -def test_model(model_name, par_index, input_index): +def test_model(answertestspec, tmp_path, model_name, par_index, input_index): + """ + Each execution tests a python example with a set of inputs + + Each of the parameters down below is a fixture + + Parameters + ---------- + answertestspec: AnswerTestSpec + A fixture that provides info about the answer-testing configuration + tmp_path: pathlib.Path + A custom built-in fixture provided by pytest that specifies a pre-made + temporary directory that is named for the current test + """ + script_path = os.path.join(python_example_dir, f"{model_name}.py") command = f"{sys.executable} {script_path} {par_index} {input_index}" - with temporary_directory(): - rval = run_command(command, timeout=60) + + if True: + rval = run_command(command, timeout=60, cwd=tmp_path) assert rval - output_file = f"{model_name}_{par_index}_{input_index}.h5" - answer_path = os.path.join(test_answers_dir, output_file) + output_basename = f"{model_name}_{par_index}_{input_index}.h5" + output_file = os.path.join(str(tmp_path), output_basename) + answer_path = os.path.join(answertestspec.answer_dir, output_basename) - if generate_test_results: + if answertestspec.generate_answers: os.rename(output_file, answer_path) else: assert os.path.exists(answer_path) diff --git a/src/python/tests/testing_common.py b/src/python/tests/testing_common.py index adf85be7d..4f3514b02 100644 --- a/src/python/tests/testing_common.py +++ b/src/python/tests/testing_common.py @@ -17,16 +17,10 @@ from pygrackle.__config__ import _is_editable_installation from pygrackle.utilities.misc import dirname -generate_test_results = \ - int(os.environ.get("GENERATE_PYGRACKLE_TEST_RESULTS", 0)) == 1 - if _is_editable_installation(): grackle_install_dir = dirname(os.path.abspath(__file__), level=4) grackle_data_dir = os.path.join(grackle_install_dir, "input") grackle_python_dir = os.path.join(grackle_install_dir, "src", "python") - test_answers_dir = os.path.join( - grackle_python_dir, "tests", "test_answers" - ) else: raise RuntimeError( "the current version of pygrackle is not an editable installation. No "