TypechoJoeTheme

MetMan's Blog

网站页面

ODC库使用:指定列对应数组

MetMan博 主神仙
2024-12-02
/
0 评论
/
69 阅读
/
663 个字
/
百度已收录
12/02
本文最后更新于 2024年12月02日,已超过 89天没有更新。如果文章内容或图片资源失效,请留言反馈,我会及时处理,谢谢!

这应该是ODC库使用教程最后一篇文章了。不过在后面分享Fortran与C互操作(混编)时可能会以ODC库实现方式为例介绍相关内容。


前面介绍的ODC编码方式是将不同类型的所有列数据通过transfer函数转换成双精度(real*8)浮点类型,同时将所有列数据组织成一个二维表格形式,放入一个二维数组中再进行解编码处理。

可能很多人不习惯这种使用方式,更希望通过指定每列对应的(一维)数组buffers方式进行操作。这样做的好处是避免了显式调用transfer数据类型转换。

下面介绍ODC这种使用方式。

指定列数组编码

查看官方实例:https://odc.readthedocs.io/en/latest/content/usage-examples/encode-custom.html

首先使用add_column方法添加表格列信息

! Define all column names and their types
call check_call(encoder%add_column('expver', ODC_STRING), 'adding expver column')
call check_call(encoder%add_column('date@hdr', ODC_INTEGER), 'adding date@hdr column')
...

然后定义数据布局,并绑定每列对应的数据数组(关键部分

! Set a custom data layout and data array for each column
call check_call(encoder%column_set_data_array(1, 8, stride=8, data=c_loc(data1)), 'setting expver array')
call check_call(encoder%column_set_data_array(2, 8, stride=8, data=c_loc(data2)), 'setting date array')
...

其中encoder对象方法column_set_data_array接口说明如下

  • 第一个col参数指定设置的列索引位置
  • 第二个参数element_size指定该列宽度,单位是字节数,如果想用以双精度大小(8 bytes)为单位,可以使用第三个element_size_doubles参数。
  • stride指定列宽度,单位是字节数
  • 最后一个data参数指定绑定的数组,接口要求是type(c_ptr)类型,即C指针。

我们先看下data1,data2,...这些数组声明:

character(8), target :: data1(nrows)
integer(8), target :: data2(nrows)
...

可以看出数组类型与表格列指定(add_column())的数据类型是一致的。

同时它们都必须具有target属性,因为在绑定数组时要求data参数是C指针类型(type(c_ptr))。通过iso_c_binding内置模块函数c_loc获取Fortran数组的C指针地址。

data = c_loc(data1)

如果去掉target属性,编译应该会报类似如下错误:

custom_encoding.f90(74): error #9022: The argument to C_LOC must be a variable with the POINTER or TARGET attribute.   [DATA1]
    call check_call(encoder%column_set_data_array(1, 8, stride=8, data=c_loc(data1)), 'setting expver array')

官方示例中使用全局数组并显式指定数组大小,但真实应用中常通过从观测源数据获取本次处理的实际观测数。所以,一般声明数据数组为动态数组类型。

比如

character(8), allocatable,target :: data1(:)

...

allocate(data1(nrows))

注意alloctable动态数组的target属性必须存在。

如果使用pointer属性,则不需要target属性。

character(8), pointer :: data1(:)

表格每列绑定好对应数组后调用encoder%encode进行ODB文件写操作。

指定列数组解码

同样的,decoder对象中也提供了指定列数组的解码操作,这里不再详述。

下面提供一个简单示例供参考。

program odc_custom_decoding
    use, intrinsic :: iso_c_binding, only: c_loc,c_null_char,c_ptr,c_f_pointer
    use odc
    implicit none

    logical,parameter :: aggregated = .true.
    integer, parameter :: max_aggregated_rows =  100000
    character(255) :: path

    integer(8) :: nrows
    integer :: ncols
    character(8), allocatable, target :: data1(:)
    integer(8), allocatable, target :: data2(:)
    character(8), allocatable, target :: data3(:)
    character(16), allocatable, target :: data4(:)
    real(8), allocatable, target :: data5(:)
    integer(8), allocatable, target :: data6(:)
    real(8), allocatable, target :: data7(:)
    integer(8), allocatable, target :: data8(:)

    type(odc_reader) :: reader
    type(odc_frame) :: frame
    type(odc_decoder) :: decoder

    integer(8) :: rows_decoded
    integer :: ele_size, istride
    integer :: i

    if (command_argument_count() /= 1) then
        call usage()
        stop 1
    end if

    ! Get output path from command argument
    call get_command_argument(1, path)

    ! Initialise API and set treatment of integers as longs
    call check_call(odc_initialise_api(), 'initialising api')
    call check_call(odc_integer_behaviour(ODC_INTEGERS_AS_LONGS), 'setting integer behaviour to longs')

    call check_call(reader%open_path(path), 'open odb input file')

    call check_call(frame%initialise(reader), 'initialise odb frame')
    call check_call(frame%next(aggregated, max_aggregated_rows), 'enter into next frame')

    call check_call(frame%row_count(nrows), "get nrows info")
    call check_call(frame%column_count(ncols), "get ncols info")
    print* , 'nrows =',nrows, 'ncols =', ncols

    call check_call(decoder%initialise(), 'initialise decoder')
    call check_call(decoder%defaults_from_frame(frame), 'get decoder info from frame')

    allocate(data2(nrows))
    allocate(data3(nrows))
    allocate(data5(nrows))
    call check_call(decoder%column_set_data_array(2, 8, stride=8, data=c_loc(data2)), 'bind data2 into column 1')
    call check_call(decoder%column_set_data_array(3, 8, stride=8, data=c_loc(data3)), 'bind data3 into column 1')
    call check_call(decoder%column_set_data_array(5, 8, stride=8, data=c_loc(data5)), 'bind data5 into column 1')

    call check_call(decoder%decode(frame, rows_decoded), 'decode data')

    do i=1,nrows
      write(*,*) i, data2(i),data3(i),data5(i)
    enddo

    call check_call(decoder%free(), 'free decoder')
    call check_call(frame%free(), 'free odb frame')
    call check_call(reader%close(), 'close odb input file')

contains

    subroutine check_call(err, desc)
        integer, intent(in) :: err
        character(*), intent(in) :: desc

        if (err /= ODC_SUCCESS) then
            write(7, *) '**** An error occurred in ODC library'
            write(7, *) 'Description: ', desc
            write(7, *) 'Error: ', odc_error_string(err)
            stop 1
        end if
    end subroutine

    subroutine usage()
        write(6, *) 'Usage:'
        write(6, *) '    odc-fortran-decode-custom <odb2 input file>'
    end subroutine
end program

小结

相比原先整个表格绑定一个二维数组方式,本文介绍的每一列绑定一个对应一维数组方式有如下优点:

  • 使用更加灵活,可以只解码部分列信息;
  • 避免调用transfer进行数据类型的转换。
odcodb
朗读
赞(0)
赞赏
感谢您的支持,我会继续努力哒!
版权属于:

MetMan's Blog

本文链接:

https://blog.metman.top/index.php/archives/162/(转载时请注明本文出处及文章链接)

评论 (0)

互动读者

标签云

最新回复

  1. tqymnonccc打酱油
    2024-09-27
  2. toibdpojay打酱油
    2024-09-22
  3. yvctxyevvw打酱油
    2024-09-22
  4. frezhwzwuq打酱油
    2024-09-22
登录
X
用户名
密码