验证从FORTRAN到C的转换代码的方法

时间:2012-07-04 11:23:31

标签: c fortran

我已经使用工具将大约90多个fortran文件转换为C文件,我需要验证转换是否良好。

您能否就如何最好地确保通过翻译保留功能给我一些想法?

3 个答案:

答案 0 :(得分:3)

您需要执行这些Fortran功能的验证测试。然后针对c代码运行这些测试。

您可以使用单元测试技术/方法。事实上,我无法看到你如何证明转换是正确的。

在许多单元测试方法中,您将使用与代码相同的语言编写测试,但在这种情况下,我建议非常强烈地选择一种语言和一个代码库来执行这两组功能。也不要担心尝试创建纯单元测试而是使用这些技术来覆盖fortran代码应该处理的所有用法。

答案 1 :(得分:2)

使用单元测试。

首先在Fortran代码上编写单元测试并检查它们是否都正常运行,然后在C中重写它们并运行它们。

这种方法的问题在于您还需要重写单元测试,在重构代码时通常不会这样做(API更改除外)。这意味着您可能会在实际代码旁边调试已移植的单元测试代码。

因此,编写包含最少逻辑的测试代码并仅将函数的结果写入文件可能会更好。然后,您可以在C中重写此最小测试代码,生成相同的文件并比较文件。

答案 2 :(得分:1)

以下是我为“类似”任务所做的工作(比较fortran 90到fortran 90 + OpenACC GPU加速代码):

  1. 分析每个Fortran模块的输出。
  2. 将这些输出数组写入.dat文件。
  3. 将.dat文件复制到参考文件夹中。
  4. 将转换后的模块的输出写入文件(CSV或二进制)。为方便起见使用相同的文件名。
  5. 制作一个比较两个版本的python脚本。
  6. 我在fortran中使用了类似的便利功能(类似于1D,2D情况):

    subroutine write3DToFile(path, array, n1, n2, n3)
       use pp_vardef
       use pp_service, only: find_new_mt
       implicit none
    
       !input arguments
       real(kind = r_size), intent(in) :: array(n1,n2,n3)
       character(len=*), intent(in) :: path
       integer(4) :: n1
       integer(4) :: n2
       integer(4) :: n3
    
       !temporary
       integer(4) :: imt
    
       call find_new_mt(imt)
    
       open(imt, file = path, form = 'unformatted', status = 'replace')
       write(imt) array
       close(imt)
     end subroutine write3DToFile
    

    在python中,我使用以下脚本来读取二进制Fortran数据并进行比较。注意:由于您要转换为C,因此您必须对其进行调整,以便您可以读取C而不是Fortran生成的数据。

    from optparse import OptionParser
    import struct
    import sys
    import math
    
    def unpackNextRecord(file, readEndianFormat, numOfBytesPerValue):
        header = file.read(4)
        if (len(header) != 4):
            #we have reached the end of the file
            return None
    
        headerFormat = '%si' %(readEndianFormat)
        headerUnpacked = struct.unpack(headerFormat, header)
        recordByteLength = headerUnpacked[0]
        if (recordByteLength % numOfBytesPerValue != 0):
            raise Exception, "Odd record length."
            return None
        recordLength = recordByteLength / numOfBytesPerValue
    
        data = file.read(recordByteLength)
        if (len(data) != recordByteLength):
            raise Exception, "Could not read %i bytes as expected. Only %i bytes read." %(recordByteLength, len(data))
            return None
    
        trailer = file.read(4)
        if (len(trailer) != 4):
            raise Exception, "Could not read trailer."
            return None
        trailerUnpacked = struct.unpack(headerFormat, trailer)
        redundantRecordLength = trailerUnpacked[0]
        if (recordByteLength != redundantRecordLength):
            raise Exception, "Header and trailer do not match."
            return None
    
        dataFormat = '%s%i%s' %(readEndianFormat, recordLength, typeSpecifier)
        return struct.unpack(dataFormat, data)
    
    def rootMeanSquareDeviation(tup, tupRef):
        err = 0.0
        i = 0
        for val in tup:
            err = err + (val - tupRef[i])**2
            i = i + 1
        return math.sqrt(err)
    
    ##################### MAIN ##############################
    #get all program arguments
    parser = OptionParser()
    parser.add_option("-f", "--file", dest="inFile",
                      help="read from FILE", metavar="FILE", default="in.dat")
    parser.add_option("--reference", dest="refFile",
                      help="reference FILE", metavar="FILE", default="ref.dat")
    parser.add_option("-b", "--bytesPerValue", dest="bytes", default="4")
    parser.add_option("-r", "--readEndian", dest="readEndian", default="big")
    parser.add_option("-v", action="store_true", dest="verbose")
    
    (options, args) = parser.parse_args()
    
    numOfBytesPerValue = int(options.bytes)
    if (numOfBytesPerValue != 4 and numOfBytesPerValue != 8):
        print "Unsupported number of bytes per value specified."
        sys.exit()
    typeSpecifier = 'f'
    if (numOfBytesPerValue == 8):
        typeSpecifier = 'd'
    
    readEndianFormat = '>'
    if (options.readEndian == "little"):
        readEndianFormat = '<'
    
    inFile = None
    refFile = None
    try: 
        #prepare files
        inFile = open(str(options.inFile),'r')
        refFile = open(str(options.refFile),'r')
    
        i = 0
        while True:
            passedStr = "pass"
            i = i + 1
            unpackedRef = None
            try: 
                unpackedRef = unpackNextRecord(refFile, readEndianFormat, numOfBytesPerValue)
            except(Exception), e:
                print "Error reading record %i from %s: %s" %(i, str(options.refFile), e)
                sys.exit()
    
            if (unpackedRef == None):
                break;
    
            unpacked = None
            try:
                unpacked = unpackNextRecord(inFile, readEndianFormat, numOfBytesPerValue)
            except(Exception), e:
                print "Error reading record %i from %s: %s" %(i, str(options.inFile), e)
                sys.exit()
    
            if (unpacked == None):
                print "Error in %s: Record expected, could not load record it" %(str(options.inFile))
                sys.exit()
    
            if (len(unpacked) != len(unpackedRef)):
                print "Error in %s: Record %i does not have same length as reference" %(str(options.inFile), i)
                sys.exit()
    
            #analyse unpacked data
            err = rootMeanSquareDeviation(unpacked, unpackedRef)
            if (abs(err) > 1E-08):
                passedStr = "FAIL <-------"
            print "%s, record %i: Mean square error: %e; %s" %(options.inFile, i, err, passedStr)
    
            if (options.verbose):
                print unpacked
    
    except(Exception), e:
        print "Error: %s" %(e)
    
    finally:
        #cleanup
        if inFile != None:
            inFile.close()
    
        if refFile != None:
            refFile.close()