Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 21 additions & 17 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,19 @@ if(CMAKE_Fortran_COMPILER_ID MATCHES "GNU")
endif()

add_library(
cable_common_objlib
OBJECT
cable_common
STATIC
src/offline/cable_abort.F90
src/offline/cable_checks.F90
src/offline/cable_cru_TRENDY.F90
src/offline/cable_define_types.F90
src/offline/cable_driver_init.F90
src/offline/cable_initialise.F90
src/offline/cable_input.F90
src/offline/cable_iovars.F90
src/offline/cable_LUC_EXPT.F90
src/offline/cable_metutils.F90
src/offline/cable_mpi.F90
src/offline/cable_namelist_input.F90
src/offline/cable_output.F90
src/offline/cable_parameters.F90
Expand All @@ -57,6 +59,7 @@ add_library(
src/offline/cable_plume_mip.F90
src/offline/cable_read.F90
src/offline/cable_site.F90
src/offline/cable_serial.F90
src/offline/cable_soil_params.F90
src/offline/cable_weathergenerator.F90
src/offline/cable_write.F90
Expand Down Expand Up @@ -150,29 +153,30 @@ add_library(
src/util/cable_runtime_opts_mod.F90
src/util/masks_cbl.F90
)
target_link_libraries(cable_common_objlib PRIVATE PkgConfig::NETCDF)

add_executable(
cable
src/offline/cable_driver.F90
src/offline/cable_driver_init.F90
"$<TARGET_OBJECTS:cable_common_objlib>"
)
target_link_libraries(cable PRIVATE PkgConfig::NETCDF)
install(TARGETS cable RUNTIME)
target_link_libraries(cable_common PRIVATE PkgConfig::NETCDF)
if(CABLE_MPI)
target_compile_definitions(cable_common PRIVATE __MPI__)
target_link_libraries(cable_common PRIVATE MPI::MPI_Fortran)
endif()

if(CABLE_MPI)
add_executable(
cable-mpi
src/offline/cable_driver_init.F90
src/offline/cable_mpicommon.F90
src/offline/cable_mpidrv.F90
src/offline/cable_mpimaster.F90
src/offline/cable_mpiworker.F90
src/science/pop/pop_mpi.F90
"$<TARGET_OBJECTS:cable_common_objlib>"
src/offline/cable_offline_driver.F90
)
target_compile_definitions(cable-mpi PRIVATE __MPI__)
target_link_libraries(cable-mpi PRIVATE PkgConfig::NETCDF MPI::MPI_Fortran)
target_link_libraries(cable-mpi PRIVATE cable_common MPI::MPI_Fortran)
install(TARGETS cable-mpi RUNTIME)
else()
add_executable(
cable
src/offline/cable_mpimaster_stub.F90
src/offline/cable_mpiworker_stub.F90
src/offline/cable_offline_driver.F90
)
target_link_libraries(cable PRIVATE cable_common)
install(TARGETS cable RUNTIME)
endif()
Comment on lines 162 to 182
Copy link
Collaborator

@SeanBryan51 SeanBryan51 Nov 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed, this will break benchcab for the case both cable and cable-mpi executables are needed as benchcab will only invoke cmake once to build both executables. However benchcab will still work with these changes for serial only (benchcab fluxsite) and mpi only (benchcab spatial) tests (FYI @ccarouge).

It would be good to think about the proper CMake workflow for building different variants of the cable executable (e.g. MPI on/off, Debug, Release, compiler). From reading online, it seems the typical approach for doing this is to generate separate build trees for each variant unless we work with a multiconfig build system.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems the typical approach for doing this is to generate separate build trees for each variant

Yes, I think that's the usual strategy. CABLE is quite fast to compile, so this should not be an issue.

One thing you might want to explore to make this process easier is the use of CMake presets. These are really nice.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #485

2 changes: 1 addition & 1 deletion src/offline/cable_cru_TRENDY.F90
Original file line number Diff line number Diff line change
Expand Up @@ -1260,7 +1260,7 @@ SUBROUTINE CRU_GET_SUBDIURNAL_MET(CRU, MET, CurYear, ktau, kend, LastYearOfMet )

CRU%ktau = ktau ! ktau is the current timestep in the year.

! this only works with CANBERRA cable_driver, as ktau !
! this only works with CANBERRA cable_serial, as ktau !
! restarts on Jan 1 !
! Based on the ktau timestep, calculate date and time information (the same for the whole spatial dimension.)
met%hod (:) = REAL(MOD( (ktau-1) * NINT(dt), INT(SecDay)) ) / 3600. ! Hour of the day
Expand Down
32 changes: 8 additions & 24 deletions src/offline/cable_driver_init.F90
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,7 @@ MODULE cable_driver_init_mod
USE cable_namelist_util, ONLY : &
get_namelist_file_name, &
CABLE_NAMELIST
#ifdef __MPI__
USE mpi
USE cable_mpicommon, ONLY : comm, rank
#endif
USE cable_mpi_mod, ONLY : mpi_grp_t
IMPLICIT NONE
PRIVATE

Expand Down Expand Up @@ -84,38 +81,25 @@ MODULE cable_driver_init_mod

CONTAINS

SUBROUTINE cable_driver_init()
SUBROUTINE cable_driver_init(mpi_grp)
TYPE(mpi_grp_t), INTENT(IN) :: mpi_grp !! MPI group to use

!! Model initialisation routine for the CABLE offline driver.
#ifdef __MPI__
INTEGER :: np, ierr
#endif

!check to see if first argument passed to cable is
!the name of the namelist file
!if not use cable.nml
CALL get_namelist_file_name()

#ifndef __MPI__
WRITE(*,*) "THE NAME LIST IS ", CABLE_NAMELIST
#endif
IF (mpi_grp%rank == 0) THEN
WRITE(*,*) "THE NAME LIST IS ", CABLE_NAMELIST
END IF

! Open, read and close the namelist file.
OPEN(10, FILE=CABLE_NAMELIST, STATUS="OLD", ACTION="READ")
READ(10, NML=CABLE)
CLOSE(10)

#ifdef __MPI__
CALL MPI_Init(ierr)
CALL MPI_Comm_dup(MPI_COMM_WORLD, comm, ierr)
CALL MPI_Comm_size(comm, np, ierr)

IF (np < 2) THEN
WRITE (*,*) 'This program needs at least 2 processes to run!'
CALL MPI_Abort(comm, 0, ierr)
END IF

CALL MPI_Comm_rank(comm, rank, ierr)
#endif

END SUBROUTINE cable_driver_init

END MODULE cable_driver_init_mod
155 changes: 155 additions & 0 deletions src/offline/cable_mpi.F90
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
! CSIRO Open Source Software License Agreement (variation of the BSD / MIT License)
! Copyright (c) 2015, Commonwealth Scientific and Industrial Research Organisation
! (CSIRO) ABN 41 687 119 230.

MODULE cable_mpi_mod
!! Module for handling some common MPI operations and MPI groups
#ifdef __MPI__
USE mpi
#endif
USE iso_fortran_env, ONLY : error_unit
IMPLICIT NONE

PRIVATE
PUBLIC :: &
mpi_grp_t, &
mpi_mod_init, &
mpi_mod_end, &
mpi_check_error

INTEGER, PARAMETER :: MPI_COMM_UNDEFINED = -1

INTEGER :: default_comm ! Default communicator to use when creating groups

TYPE mpi_grp_t
!* Class to handle MPI groups.
! This class stores information about the group and
! the current proccess.
INTEGER :: comm = MPI_COMM_UNDEFINED !! Communicator
INTEGER :: rank = -1 !! Rank of the current process
INTEGER :: size = -1 !! Size of the communicator
CONTAINS
PROCEDURE :: abort => mpi_grp_abort !! Send abort signal to processes in this group
END TYPE mpi_grp_t

INTERFACE mpi_grp_t
!* Overload the default construct for mpi_grp_t
PROCEDURE mpi_grp_constructor
END INTERFACE mpi_grp_t

CONTAINS

SUBROUTINE mpi_mod_init()
!* Initialise MPI and set default communicator.
!
! The default communicator is set to MPI_COMM_WORLD if MPI support is
! available or to MPI_COMM_UNDEFINED if not.
#ifdef __MPI__
INTEGER :: ierr

CALL MPI_Init(ierr)
CALL mpi_check_error(ierr)
default_comm = MPI_COMM_WORLD
#else
default_comm = MPI_COMM_UNDEFINED
#endif

END SUBROUTINE mpi_mod_init

SUBROUTINE mpi_mod_end()
!* Finalise MPI.
#ifdef __MPI__
INTEGER :: ierr

IF (default_comm /= MPI_COMM_UNDEFINED) THEN
CALL MPI_Finalize(ierr)
CALL mpi_check_error(ierr)
END IF
#endif

END SUBROUTINE mpi_mod_end


FUNCTION mpi_grp_constructor(comm) RESULT(mpi_grp)
!* Contructor for mpi_grp_t class.
!
! This sets the communicator of the group and gets the size of the group and
! rank of current process. If no communicator is provided, it will use
! the default defined when calling mpi_mod_init.
!
! Note that when the undefined communicator is used, the group size is 1 and
! the rank to 0, such that the code can work in serial mode.
INTEGER, INTENT(IN), OPTIONAL :: comm !! MPI communicator
TYPE(mpi_grp_t) :: mpi_grp

INTEGER :: ierr

IF (PRESENT(comm)) THEN
#ifdef __MPI__
CALL MPI_Comm_dup(comm, mpi_grp%comm, ierr)
call mpi_check_error(ierr)
#else
mpi_grp%comm = comm
#endif
ELSE
#ifdef __MPI__
CALL MPI_Comm_dup(default_comm, mpi_grp%comm, ierr)
call mpi_check_error(ierr)
#else
mpi_grp%comm = default_comm
#endif
END IF

IF (mpi_grp%comm /= MPI_COMM_UNDEFINED) THEN
#ifdef __MPI__
call MPI_Comm_rank(mpi_grp%comm, mpi_grp%rank, ierr)
call mpi_check_error(ierr)

call MPI_Comm_size(mpi_grp%comm, mpi_grp%size, ierr)
call mpi_check_error(ierr)
#else
WRITE(error_unit,*) "Error initialising mpi group: CABLE was compiled without MPI support."
STOP
#endif
ELSE
mpi_grp%rank = 0
mpi_grp%size = 1
END IF

END FUNCTION mpi_grp_constructor

SUBROUTINE mpi_grp_abort(this)
!* Class method to abort execution of an MPI group.
CLASS(mpi_grp_t), INTENT(IN) :: this

INTEGER :: ierr

IF (this%comm /= MPI_COMM_UNDEFINED) THEN
! Here we use an arbitrary error code
#ifdef __MPI__
call MPI_Abort(this%comm, 999, ierr)
#endif
call mpi_check_error(ierr)
END IF

END SUBROUTINE mpi_grp_abort

SUBROUTINE mpi_check_error(ierr)
!* Check if an MPI return code signaled an error. If so, print the
! corresponding message and abort the execution.
INTEGER, INTENT(IN) :: ierr !! Error code

#ifdef __MPI__
CHARACTER(len=MPI_MAX_ERROR_STRING) :: msg
INTEGER :: length, tmp

IF (ierr /= MPI_SUCCESS ) THEN
CALL MPI_Error_String(ierr, msg, length, tmp)
WRITE(error_unit,*) msg(1:length)
CALL MPI_Abort(MPI_COMM_WORLD, 1 , tmp)
END if
#endif

END SUBROUTINE mpi_check_error

END MODULE cable_mpi_mod
7 changes: 0 additions & 7 deletions src/offline/cable_mpicommon.F90
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,6 @@ MODULE cable_mpicommon

PUBLIC

! MPI commicator and rank are declared as global variables (see
! cable_driver_init_mod for their initialisation). This is done so that the comm
! and rank variables are only accessible in cable_driver_init_mod when MPI
! compilation is enabled.
! TODO(Sean): revise which module to declare these variables in
INTEGER :: comm, rank

! base number of input fields: must correspond to CALLS to
! MPI_address (field ) in *_mpimaster/ *_mpiworker
INTEGER, PARAMETER :: nparam = 330
Expand Down
47 changes: 0 additions & 47 deletions src/offline/cable_mpidrv.F90

This file was deleted.

2 changes: 1 addition & 1 deletion src/offline/cable_mpimaster.F90
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
! soil_snow_type now ssnow (instead of ssoil)
!
! MPI wrapper developed by Maciej Golebiewski (2012)
! Modified from cable_driver.F90 in CABLE-2.0_beta r171 by B Pak
! Modified from cable_serial.F90 in CABLE-2.0_beta r171 by B Pak
!
! ==============================================================================
! Uses: mpi
Expand Down
23 changes: 23 additions & 0 deletions src/offline/cable_mpimaster_stub.F90
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
! CSIRO Open Source Software License Agreement (variation of the BSD / MIT License)
! Copyright (c) 2015, Commonwealth Scientific and Industrial Research Organisation
! (CSIRO) ABN 41 687 119 230.

MODULE cable_mpimaster
!! Stub for the master driver when MPI is not available.
IMPLICIT NONE

PRIVATE
PUBLIC :: mpidrv_master

CONTAINS

SUBROUTINE mpidrv_master(comm)
!! Stub for when MPI is not available
INTEGER, INTENT(IN) :: comm

! This should never be called!
STOP

END SUBROUTINE mpidrv_master

END MODULE cable_mpimaster
Loading
Loading