diff --git a/CMakeLists.txt b/CMakeLists.txt index 08a2d584..3acdf42c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,4 +124,4 @@ endif() # Musica python if(MUSICA_ENABLE_PYTHON_LIBRARY) add_subdirectory(python) -endif() \ No newline at end of file +endif() diff --git a/cmake/dependencies.cmake b/cmake/dependencies.cmake index 83e09267..b4d5e43e 100644 --- a/cmake/dependencies.cmake +++ b/cmake/dependencies.cmake @@ -111,4 +111,4 @@ endif() if(MUSICA_BUILD_DOCS) find_package(Sphinx REQUIRED) -endif() \ No newline at end of file +endif() diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst deleted file mode 100644 index 239263ba..00000000 --- a/docs/source/getting_started.rst +++ /dev/null @@ -1,6 +0,0 @@ -############### -Getting Started -############### - -Build and Test -============== diff --git a/docs/source/getting_started/getting_started.rst b/docs/source/getting_started/getting_started.rst new file mode 100644 index 00000000..070ded6b --- /dev/null +++ b/docs/source/getting_started/getting_started.rst @@ -0,0 +1,10 @@ +############### +Getting Started +############### + +.. toctree:: + :maxdepth: 1 + :caption: Contents: + + overview + installation diff --git a/docs/source/getting_started/installation.rst b/docs/source/getting_started/installation.rst new file mode 100644 index 00000000..f8695695 --- /dev/null +++ b/docs/source/getting_started/installation.rst @@ -0,0 +1,16 @@ +Installation +============ + +MUSICA source code may be cloned from its public GitHub repository +and configured and built with the cmake utility. +In brief: + +.. code-block:: console + + $ git clone https://github.com/NCAR/musica.git + $ cd musica + $ mkdir build + $ cd build + $ ccmake .. + $ make + $ make install diff --git a/docs/source/getting_started/overview.rst b/docs/source/getting_started/overview.rst new file mode 100644 index 00000000..40613dbd --- /dev/null +++ b/docs/source/getting_started/overview.rst @@ -0,0 +1,19 @@ +Overview +======== + +The specific goal of MUSICA is to produce a new model independent infrastructure, +which will enable chemistry and aerosols to be simulated +at a large number of different resolutions in a single, coherent fashion. +At first, MUSICA will be configured within the NCAR Community Earth system Model (CESM) +and through this enable full feedbacks between the atmosphere, ocean and land. +The infrastructure will unify the different chemical transport models +including CAM-Chem, WACCM and WRF-Chem, and NCAR LES with chemistry +and a box model in a single modular framework. +The model infrastructure will be open source, +flexible and computationally efficient in order +to facilitate community co-development and use for scientific and operational purposes. + +At the heart of MUSICA is the standalone Model Independent Chemistry Model (MICM), which is a gas-phase kinetic solver. MICM is made available by the MUSICA wrapper which satisfies the requirements of the Common Community Physics Package (CCPP) +and that can be connected to any CCPP compliant atmosphere model. +MUSICA and MICM will have a flexible design to handle a variety of gas phase and aerosol schemes +and associated chemical modules such as deposition or photolysis. diff --git a/docs/source/index.rst b/docs/source/index.rst index 7bebc5de..b597c9ae 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -16,14 +16,14 @@ Welcome to MUSICA's documentation! .. grid-item-card:: Getting started :img-top: _static/index_getting_started.svg - :link: getting_started + :link: getting_started/getting_started :link-type: doc Check out the getting started guide to build and install musica. .. grid-item-card:: User guide :img-top: _static/index_user_guide.svg - :link: user_guide/index + :link: user_guide/user_guide :link-type: doc .. grid-item-card:: API reference @@ -41,13 +41,14 @@ Welcome to MUSICA's documentation! :maxdepth: 2 :caption: Contents: - getting_started - user_guide/index + getting_started/getting_started + user_guide/user_guide + tutorial/tutorial api/index contributing/index -.. getting_started -.. user_guide/index +.. getting_started/getting_started +.. user_guide/user_guid .. api/index .. contributing/index .. citing_and_bibliography/index diff --git a/docs/source/tutorial/chapter1.rst b/docs/source/tutorial/chapter1.rst new file mode 100644 index 00000000..a05c0529 --- /dev/null +++ b/docs/source/tutorial/chapter1.rst @@ -0,0 +1,43 @@ +Chapter 1 +========= + +First Fortran MUSICA Program +---------------------------- +The MUSICA library can be used within a fortran program. +To get started, let us create a simple program that links +to MUSICA and prints the version of MICM. + +Here are the contents of the program `demo.f90`: + + .. literalinclude:: ../../../fortran/test/fetch_content_integration/test_get_micm_version.F90 + :language: f90 + +From the ``musica_micm`` module, we only need the function ``get_micm_version``, +which returns a derived string type from the ``musica_util`` module, ``string_t``. +The ``string_t`` type will be discussed in more detail in later chapters. +To print the version string we just want the fortran character array, +accessed by ``get_char_array``. + +Now, to build this simple program, +invoke the `gfortran` compiler and link to ``libmusica-fortran``, ``libmusica``, +and the standard C++ library ``libstdc++``. +The full command is + +.. code-block:: bash + + gfortran -o demo demo.f90 -I/include -L/lib -lmusica-fortran -lmusica -lstdc++ + +```` is the full path of the MUSICA installation directory, +specified by the option ``CMAKE_INSTALL_PREFIX`` +during the `cmake` configuration process. +Note the include path allows the linker to find the ``musica_micm.mod`` and ``musica_util.mod`` +module definition files. + +When the `demo` program is run it should display the MICM version: + +.. code-block:: bash + + $ ./demo + MICM version 3.5.0 + $ + diff --git a/docs/source/tutorial/tutorial.rst b/docs/source/tutorial/tutorial.rst new file mode 100644 index 00000000..e4206e3d --- /dev/null +++ b/docs/source/tutorial/tutorial.rst @@ -0,0 +1,9 @@ +######## +Tutorial +######## + +.. toctree:: + :maxdepth: 1 + :caption: Contents: + + chapter1.rst diff --git a/docs/source/user_guide/fortran_c.rst b/docs/source/user_guide/fortran_c.rst new file mode 100644 index 00000000..fa1ef2d1 --- /dev/null +++ b/docs/source/user_guide/fortran_c.rst @@ -0,0 +1,78 @@ +############## +Model Inteface +############## + +Fortran C Interface Example +--------------------------- + +.. code-block:: cpp + + #include + + void test_proc_c(int n, double A[3][2]) { + + printf("test_proc_c\n"); + printf("n = %d\n", n); + + printf("matrix A\n"); + + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 3; j++) { + printf("%6.2f ", A[j][i]); + } + printf("\n"); + } + } + + +.. code-block:: f90 + + program demo_fort + use iso_c_binding, only: c_int, c_double + implicit none + integer :: i, j + integer(c_int) :: n_fort = 7 + real(c_double), dimension(2, 3) :: A_fort + + interface + subroutine test_proc_c(n_c, A_c) bind(C, name='test_proc_c') + use iso_c_binding, only: c_int, c_double + integer(c_int), intent(in), value :: n_c + real(c_double), dimension(2, 3), intent(in) :: A_c + end subroutine test_proc_c + end interface + + do j = 1, 3 + do i = 1, 2 + A_fort(i, j) = real(i + j, c_double) + end do + end do + + call test_proc_c(n_fort, A_fort) + end program demo_fort + +.. code-block:: bash + + all: test_proc_c.o demo_fort.o demo_fort + + test_proc_c.o : test_proc_c.c + gcc -c test_proc_c.c + + demo_fort.o : demo_fort.f90 + gfortran -c demo_fort.f90 + + demo_fort : test_proc_c.o demo_fort.o + gfortran -o demo_fort demo_fort.o test_proc_c.o -lc + + clean: + rm -f test_proc_c.o demo_fort.o demo_fort + +.. code-block:: console + + $ ./demo_fort + test_proc_c + n = 7 + matrix A + 2.00 3.00 4.00 + 3.00 4.00 5.00 + diff --git a/docs/source/user_guide/index.rst b/docs/source/user_guide/index.rst deleted file mode 100644 index 3ac2a7e9..00000000 --- a/docs/source/user_guide/index.rst +++ /dev/null @@ -1,3 +0,0 @@ -########## -User Guide -########## diff --git a/docs/source/user_guide/model_interface.rst b/docs/source/user_guide/model_interface.rst new file mode 100644 index 00000000..bc733bb0 --- /dev/null +++ b/docs/source/user_guide/model_interface.rst @@ -0,0 +1,4 @@ +############## +Model Inteface +############## + diff --git a/docs/source/user_guide/user_guide.rst b/docs/source/user_guide/user_guide.rst new file mode 100644 index 00000000..38c0011a --- /dev/null +++ b/docs/source/user_guide/user_guide.rst @@ -0,0 +1,9 @@ +########## +User Guide +########## + +.. toctree:: + :maxdepth: 1 + :caption: Contents: + + model_interface diff --git a/fortran/micm.F90 b/fortran/micm.F90 index 580b4cd8..35baa48e 100644 --- a/fortran/micm.F90 +++ b/fortran/micm.F90 @@ -5,11 +5,16 @@ module musica_micm use musica_util, only: assert, mapping_t implicit none - public :: micm_t + public :: get_micm_version, micm_t private interface + function get_micm_version_c() bind(C, name="get_micm_version") + use musica_util, only: string_t_c + type(string_t_c) :: get_micm_version_c + end function get_micm_version_c + function create_micm_c(config_path, error) bind(C, name="CreateMicm") use musica_util, only: error_t_c import c_ptr, c_int, c_char @@ -122,6 +127,14 @@ end subroutine delete_mappings_c contains + function get_micm_version() result(value) + use musica_util, only: string_t, string_t_c + type(string_t) :: value + type(string_t_c) :: string_c + string_c = get_micm_version_c() + value = string_t(string_c) + end function get_micm_version + function constructor(config_path, error) result( this ) use musica_util, only: error_t_c, error_t, copy_mappings type(micm_t), pointer :: this @@ -257,4 +270,4 @@ subroutine finalize(this) ASSERT(error%is_success()) end subroutine finalize -end module musica_micm \ No newline at end of file +end module musica_micm diff --git a/fortran/test/fetch_content_integration/CMakeLists.txt b/fortran/test/fetch_content_integration/CMakeLists.txt index 94a14fb6..f9a32dbc 100644 --- a/fortran/test/fetch_content_integration/CMakeLists.txt +++ b/fortran/test/fetch_content_integration/CMakeLists.txt @@ -32,6 +32,7 @@ enable_testing() # API Test if (MUSICA_ENABLE_MICM) add_executable(test_micm_fortran_api test_micm_api.F90) + add_executable(test_get_micm_version test_get_micm_version.F90) target_link_libraries(test_micm_fortran_api PRIVATE @@ -48,6 +49,22 @@ if (MUSICA_ENABLE_MICM) COMMAND $ WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ) + + target_link_libraries(test_get_micm_version + PRIVATE + musica::musica-fortran + ) + + set_target_properties(test_get_micm_version + PROPERTIES + LINKER_LANGUAGE Fortran + ) + + add_test( + NAME test_get_micm_version + COMMAND $ + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + ) endif() # API Test @@ -69,4 +86,4 @@ if (MUSICA_ENABLE_TUVX) COMMAND $ WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ) -endif() \ No newline at end of file +endif() diff --git a/fortran/test/fetch_content_integration/test_get_micm_version.F90 b/fortran/test/fetch_content_integration/test_get_micm_version.F90 new file mode 100644 index 00000000..299411bc --- /dev/null +++ b/fortran/test/fetch_content_integration/test_get_micm_version.F90 @@ -0,0 +1,8 @@ +program demo + use musica_util, only: string_t + use musica_micm, only: get_micm_version + implicit none + type(string_t) :: micm_version + micm_version = get_micm_version() + print *, "MICM version ", micm_version%get_char_array() +end program demo diff --git a/fortran/test/fetch_content_integration/test_micm_api.F90 b/fortran/test/fetch_content_integration/test_micm_api.F90 index 323bf451..6cad2064 100644 --- a/fortran/test/fetch_content_integration/test_micm_api.F90 +++ b/fortran/test/fetch_content_integration/test_micm_api.F90 @@ -2,8 +2,8 @@ program test_micm_api use, intrinsic :: iso_c_binding use, intrinsic :: ieee_arithmetic - use musica_micm, only: micm_t - use musica_util, only: assert, error_t, mapping_t + use musica_micm, only: micm_t, get_micm_version + use musica_util, only: assert, error_t, mapping_t, string_t #include "micm/util/error.hpp" @@ -19,6 +19,7 @@ program test_micm_api subroutine test_api() + type(string_t) :: micm_version type(micm_t), pointer :: micm real(c_double) :: time_step real(c_double) :: temperature @@ -43,6 +44,8 @@ subroutine test_api() num_user_defined_reaction_rates = 3 user_defined_reaction_rates = (/ 0.1, 0.2, 0.3 /) + micm_version = get_micm_version() + print *, "[test micm fort api] MICM version ", micm_version%get_char_array() write(*,*) "[test micm fort api] Creating MICM solver..." micm => micm_t(config_path, error) diff --git a/fortran/test/unit/CMakeLists.txt b/fortran/test/unit/CMakeLists.txt index 69e15730..2d5e274a 100644 --- a/fortran/test/unit/CMakeLists.txt +++ b/fortran/test/unit/CMakeLists.txt @@ -4,6 +4,7 @@ create_standard_test_fortran(NAME fortran_util SOURCES util.F90) if (MUSICA_ENABLE_MICM) create_standard_test_fortran(NAME micm_fortran_api SOURCES ../fetch_content_integration/test_micm_api.F90) + create_standard_test_fortran(NAME get_micm_version SOURCES ../fetch_content_integration/test_get_micm_version.F90) endif() if (MUSICA_ENABLE_TUVX) @@ -16,4 +17,4 @@ if (MUSICA_ENABLE_TUVX) if (MUSICA_ENABLE_MPI) create_standard_test_fortran(NAME connect_to_tuvx_mpi SOURCES tuvx_mpi.F90) endif() -endif() \ No newline at end of file +endif() diff --git a/fortran/util.F90 b/fortran/util.F90 index cc783f94..e2022f4b 100644 --- a/fortran/util.F90 +++ b/fortran/util.F90 @@ -22,6 +22,7 @@ module musica_util private character(len=:), allocatable :: value_ contains + procedure :: get_char_array => get_char_array_string_t procedure, private, pass(from) :: char_assign_string procedure, private, pass(to) :: string_assign_char procedure, private, pass(to) :: string_assign_string @@ -101,6 +102,12 @@ end subroutine delete_string_c !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + function get_char_array_string_t( this ) result ( value ) + class(string_t), intent(in) :: this + character(len=:), allocatable :: value + allocate( value, source = this%value_ ) + end function get_char_array_string_t + function string_t_constructor_from_string_t_c( c_string ) result( new_string ) type(string_t_c), intent(inout) :: c_string @@ -385,4 +392,4 @@ end subroutine assert !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -end module musica_util \ No newline at end of file +end module musica_util diff --git a/include/musica/micm.hpp b/include/musica/micm.hpp index 578b9389..b2ee1d78 100644 --- a/include/musica/micm.hpp +++ b/include/musica/micm.hpp @@ -26,6 +26,7 @@ namespace musica { #endif + String get_micm_version(void); MICM *CreateMicm(const char *config_path, Error *error); void DeleteMicm(const MICM *micm, Error *error); void MicmSolve( @@ -126,4 +127,4 @@ namespace musica *error = ToError(MUSICA_ERROR_CATEGORY, MUSICA_ERROR_CODE_SPECIES_NOT_FOUND, msg.c_str()); return T(); } -} // namespace musica \ No newline at end of file +} // namespace musica diff --git a/src/micm/micm.cpp b/src/micm/micm.cpp index 9973a3fd..be4daaaa 100644 --- a/src/micm/micm.cpp +++ b/src/micm/micm.cpp @@ -8,6 +8,7 @@ */ #include +#include #include #include @@ -18,6 +19,12 @@ namespace musica { + String get_micm_version() + { + String micm_version = CreateString(micm::GetMicmVersion()); + return micm_version; + } + MICM *CreateMicm(const char *config_path, Error *error) { DeleteError(error); @@ -253,4 +260,4 @@ namespace musica } } -} // namespace musica \ No newline at end of file +} // namespace musica