Python unittest C/Fortran MPI 函数导致 MPI_Init 函数在 MPI_finalize 之后被调用

Python unittest C/Fortran MPI functions resulting in MPI_Init function getting called after MPI_finalize

我正在向使用 MPI(OpenMPI,如果相关)的 Fortran 库编写 Python 包装器。我在这里提到的问题也恰好发生在 C 库的 Python 包装器上。我必须使用内置的 unittest Python 包来测试 Python 接口。

我有以下 MWE,它试图在所有处理器中找到最大和最小整数。这是 Fortran 文件:

! File mpitest.F90
module mpitest
    implicit none
    include "mpif.h"
    integer nranks
    integer rank

    contains

    !--------------------------------------------------!
    subroutine pympitest_init()
        implicit none
        integer ierror
        call MPI_INIT(ierror)
        call MPI_COMM_SIZE(MPI_COMM_WORLD, nranks, ierror)
        call MPI_COMM_RANK(MPI_COMM_WORLD, rank, ierror)
    end subroutine pympitest_init

    !--------------------------------------------------!
    subroutine get_max_integer(inint, outint)
        implicit none
        integer, intent(in) :: inint
        integer, intent(out) :: outint
        integer ierror
        call mpi_allreduce(inint,outint,1,mpi_integer,mpi_max,MPI_COMM_WORLD,ierror)
    end subroutine get_max_integer

    !--------------------------------------------------!
    subroutine get_min_integer(inint, outint)
        implicit none
        integer, intent(in) :: inint
        integer, intent(out) :: outint
        integer ierror
        call mpi_allreduce(inint,outint,1,mpi_integer,mpi_min,MPI_COMM_WORLD,ierror)
    end subroutine get_min_integer

    !--------------------------------------------------!
    subroutine pympitest_final()
        integer ierror
        call mpi_finalize(ierror)
    end subroutine pympitest_final

end module mpitest

下面给出了Python单元测试文件,其中有一些我已经尝试过但没有帮助的东西作为评论:

#!/usr/bin/env python3
# Python file test_pympitest.py
from __future__ import print_function
import unittest
import numpy as np

from pympitest import mpitest as pm

#--------------------------------------------------------------------#
class Test_Fortran_MPI_functions(unittest.TestCase):
    """Unit test template for testing MPI functions."""

    #----------------------------------------------------------------#
    #def __init__(self):
    #    """Try MPI_Initialize -- Does not help."""
    #    print("Initializing MPI")
    #    pm.pympitest_init()

    #----------------------------------------------------------------#
    #def tearDown(self):
    #    """Try MPI_Finalize -- Does not help."""
    #    print("Finalizing MPI")
    #    pm.pympitest_final()

    #----------------------------------------------------------------#
    def setUp(self):
        """Initialize the unit test."""
        print("Initializing MPI")
        pm.pympitest_init()

        # We'll just test min. and max. of ranks across all PEs.
        self.inint = pm.rank

    #----------------------------------------------------------------#
    def tearDown(self):
        """Finalize the unit test."""
        print("Finalizing MPI")
        pm.pympitest_final()

    #----------------------------------------------------------------#
    #----------------------------------------------------------------#
    # Two Tests. If you comment out any one of the tests, then the
    # unit test runs fine. Keeping both uncommented thorws error
    # saying MPI_Init was called after MPI_Final, obviously since
    # the setUp and tearDown functions are called every time.
    #----------------------------------------------------------------#
    def test_get_min_integer(self):
        """Test get_min_integer function."""
        minint = pm.get_min_integer(self.inint)
        print("Minimum on processor {} = {}".format(pm.rank,minint))
        self.assertEqual(minint, 0)

    #----------------------------------------------------------------#
    def test_get_max_integer(self):
        """Test get_max_integer function."""
        maxint = pm.get_max_integer(self.inint)
        print("Maximum on processor {} = {}".format(pm.rank,maxint))
        self.assertEqual(maxint, pm.nranks-1)

#--------------------------------------------------------------------#
if __name__ == "__main__":
    unittest.main()

    # Tried the following. Does not help:
    # Calls MPI_Initialize but exits without calling MPI_Finalize.
    #print("Initializing MPI")
    #pm.pympitest_init()
    #unittest.main()
    #print("Finalizing MPI")
    #pm.pympitest_final()

Fortran 代码使用 f2py 包装到名为 pympitest 的 Python 模块中,如下所示: f2py --f90exec="mpif90" -DNPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION -c mpitest.F90 -m pympitest。单元测试在 4 个处理器上 运行,如下所示:mpirun -np 4 python test_pympitest.py.

我想将所有 MPI 单元测试保存在一个 python 文件中。在我尝试过的不同事物中(参见 Python 文件中的注释),我遇到的问题是 MPI_Initialize 和 MPI_Finalize 在单个 [= 中被多次调用54=] 或者只有其中一个被调用,所有这些都以错误退出。

我该如何解决这个问题才能正确通过单元测试?


Edit(添加):将测试函数放入单个 Python 文件中的单独 Test_...(unittest.TestCase) 类 也无济于事。将 MPI 单元测试拆分成不同的文件并尝试 python -m unittest discover 不起作用,这很不方便。测试每个 Python 文件的单个包装 MPI 函数并单独 运行ning 每个 Python 文件是可行的,但这是我宁愿避免的事情。

unittest.TestCasesetUp()tearDown() 方法提供 per-test-method 设置和拆卸。您应该期望前者在每个测试方法之前执行,而后者在每个测试方法之后执行。

您似乎反而希望对 class 中的整个测试方法集合执行一次。为此你想要 setUpClass() and tearDownClass()。或者,如果您定义多个测试 classes 那么您可能更喜欢 setUpModule()tearDownModule().

或者,您可以创建一个 unittest.TestSuite subclass 用于 运行 您的测试,并覆盖其 run() 方法以执行一次性设置和拆解。