立即注册 登录
气象家园 返回首页

/xin村儿/的个人空间 http://bbs.06climate.com/?3576 [收藏] [复制] [分享] [RSS]

日志

NetCDF@中文——你需要知道的东西

热度 2已有 4176 次阅读2012-9-2 10:26 | fortran, grads, 程序, 共享软件

 

 

NetCDF 共享软件

 

Models-3 模式中,使用的数据存取接口称为 I/O API,其实就是 NetCDF 文件格式。而由于我们需要了解 Models-3 输出档案的数据情况,因此对于 NetCDF 的档案结构与变量型态需要有比较完整的了解。所以在底下的翻译数据中,需要知道的在基础篇的『简介』『前言』『NetCDF档案的主要组成成分』『NetCDF的数据格式』等部分需要比较详细的参详参详,另外,在工具篇里面有两个重要工具程序(ncgenncdump)也需要看一下的!至于进阶篇主要是针对程序发展者的介绍,有需要再看吧,而且这一部分我翻译的不是很好,虽然我自己看得懂,但是建议您直接看原文会比较容易进入状况喔!你也可以到这里来看一下原文说明喔!

基础篇


进阶篇(有需要再看,建议直接看原文)


工具篇


其他相关话题喔


简介:

NetCDF(network Common Data Format)最早是由美国国家科学委员会资助之计划--Unidata --所发展,其用意是在Unidata计划中不同的应用项目下,提供一种可以通用的数据存取方式,数据的形状包括单点的观测值、时间序列、规则排列的网格、以及人造卫星或雷达之图像文件案。其自行说明表头的理念是参照NASA Goddart国家太空数据中心在1987年所发表的论文。

NetCDF 可简单的视为一种存取接口,任何使用 NetCDF 存取格式的档案就可称为 NetCDF 档案;至于 NetCDF 这套软件的功能,在于提供CFortranC++Perl、或其他语言I/O的链接库,以让程序发展者可以读写数据文件,其本身具有说明的能力、并且可以跨越平台和机器的限制。每一个NetCDF档案可以含括多维度的、具有名称的变量,包括长短的整数、单倍与双倍精度的实数、字符等,且每一个变量都有其自我介绍的数据,包括量度的单位、全名及意义等文字说明,在此摘要性的档头之后,才是真正的数据本身。

为何 NetCDF 适合于科技技术方面的使用呢?这是因为这个接口是一种多维的数据分布系统,所以由这个接口所产生的档案,具有多维的数据格式,当你需要其中的某一笔数据时,程序将不会从第一笔数据读到你所需要的数据处,而是由 NetCDF 软件直接存取那一个数据!如此一来将会大量的降低模式运算时数据存取的时间。例如对于支持 NetCDF 格式的绘图软件来说,只要有此格式的数据文件即可针对任一变数的任一维度或多维度(包括时间)变化进行绘图,而且不论这个变量的数据是存在档案中的那个地方,均可直接读取该值而不用循序读取在它前面数据后再读取该值,如此可大大减少绘图所需的时间。

另外,在三维空气质量模式仿真中,需要读入的排放源数据库及空气质量模式,并非仅读取一次,模式是一直持续的在读取与计算。例如你模拟一个七天的事件,总共 168 个小时,那第一个小时读入数据后,程序便开始运算,等算完了,模式会再由数据文件读入第二小时的数据,以此类推,OK!那你知道一般传统档案如何读档吗?假设共有10000笔数据,而你要的是200-3009800-9900这两段数据,则程序需要由第一笔数据读到第9900笔资料后,舍弃不必要的 9700 笔数据后,才可得到你的 200 笔数据。那你晓得 NetCDF 的优点了吗?没错,如上面所述, NetCDF 只需要直接读取 200 笔数据就可以了,对,他可以直接到数据所在的那一个单元格去将数据取出!但也就是因为这样, NetCDF 所需要的空间是很大的,因为他多了很多的变量宣告项目,与其具有既定的单元格式所致!

Anyway,简单的说, NetCDF 是一套软件,也是一种存取接口,这种接口的优点在于『其数据具有说明的文件头』,所以数据不会被误用;而其存取方式为『直接至该笔数据读取』,因此可以省去很多不必要的读取时间;另外,这种档案是一种可以『跨平台操作』读取的格式,因此,不论你是以何种操作系统制作 NetCDF 档案,在其他平台之下,还是可以使用这样的数据文件的!

Top


前言:

NetCDF Network Common Data Form 的缩写,为一甚为有用的科技数据处理接口,其数据取出输入的方法与传统的由档头读至该笔使用 data 处不同,故其接口较新,且需要使用特殊的程序读写,以下将主要介绍以 Fortran 程序语言建置 NetCDF 的方法,尤其当中 NetCDF 的文件格式最为重要!请详细看一下。这是因为 Models-3 主要就是使用 NetCDF 的文件格式,所以你可以不知道 NetCDF 如何建置,但是一定要晓得如何将 NetCDF 内的数据读出来看!

开宗明义的说, NetCDF 主要设计的目的,即在于增进储存、读取以及方便多维(array is an n-dimensional)数据的存取的一种档案『接口』。目前 NetCDF 已经开发至相当多的操作平台(Unix, WindowsNT等等,Linux也有支援)。至于 NetCDF 的档案结构将在后续的章节说明之。

说到最重要的执行效能(performance),如前所述,由于传统的档案读取方式实在太慢了,并且,对于多变量的档案(例如一个排放源数据文件中,将包含了 PMVOCSOxNOx及其他林林总总的污染物变量)其读取方式很容易出错。这是因为在传统的档案中,每一笔数据都必须要在『特定的』相关位置,不可随意变动,否则在另一个操作界面或者用户在使用时,将完全看不懂这个档案里头数据的意义!并且可能造成程序的误判!而 NetCDF 当中,由于其存取接口是使用 direct (直接)至该相关位置存取,并且每一笔数据都有对应的 ID,所以 PM 的数据绝对不会被程序当作 SOx 浓度使用!即使另一个使用者建立的排放量 NetCDF 格式的档案中 PM 所放置的与原先的并不相同,那也完全不会影响程序的读取数据!因此, NetCDF 不但可以增进数据的访问时间,亦且也可以帮助用户了解资料内容,并且也不易有错误数据发生。不过,若是当你的档案中仅有单一物种数据,那由于 NetCDF 是一笔一笔去读数据,而传统读文件的连续读取会比较快速!因此对于较为简单的档案, NetCDF 执行效能反而是不如传统的读档方式的,这点请一定要搞清楚!

其实在 MM5 的文件格式中,也是与 NetCDF 具有相同的功能,亦即每一笔数据都有对应的 ID以及该变量的简短说明(包括该变量的单位),所以对于数据的保存是很有用的。另外,由于 NetCDF 可以跨平台操作,因此你在 Unix 建立的档案,拿到 windows NT 仍然是可以使用的哩!很棒对不对!

说了这样多,但是 NetCDF 仍然是有缺点的。

  • 由于 NetCDF 档案需要宣告变量,每一种变量都有其特定的单元格,而变数仅有六种,8-,8-,16-,32-,32-,64-bit(这一部分将在变数格式中说明),所以你的 NetCDF 档案通常要比传统的档案来的大。例如你的变数为 11-bit 的实数,但实数仅有 32-64-bit 的变量,因此你的变量便需宣告为 32-bit 的变量,所以档案在无形之中就会变大啦!
  • 由于 NetCDF 启始设计的环境并不好,(可能是在 MS-DOS FAT 表下设计的)所以对于每一个档案的数据均只能到达 2GB 的限制,亦即是当你的档案数据太大,大到超过 2GB NetCDF 就会显示错误啦!这也是下一阶段 NetCDF 发展团队努力想要克服的障碍!

Top


NetCDF 档案的主要组成:

任何一个 NetCDF 的档案均含有下列几个部分:

  • dimensions:多维数据结构,例如 latitude, longitude, layers, and time
  • variables:各种变数,例如 temperature, RH, latitude, longitude
  • attributes:辅助记忆的说明文件头
  • data:主要的资料部分。

我们可以使用下面这一个档案来做说明:在下表中,蓝色的字为鸟哥附加的说明,不包括在档案中

netcdf example_1 { // example of CDL notation for a netCDF dataset
这一行只是 Title 的说明,告诉你这是一个 NetCDF 档案, 接在 // 之后的是说明的资料

dimensions: // dimension names and lengths are declared first
lat = 5, lon = 10, level = 4, time = unlimited;
宣告数组的地方,上面宣布共有5个纬度(lat)10个经度、四层、及时间等多维数组
要注意的地方是,time unlimited 也就是说,关于时间的数组可一直增加下去!

variables: // variable types, names, shapes, attributes
float temp(time,level,lat,lon);
temp:long_name = "temperature";
temp:units = "celsius";
float rh(time,lat,lon);
rh:long_name = "relative humidity";
rh:valid_range = 0.0, 1.0; // min and max
int lat(lat), lon(lon), level(level);
lat:units = "degrees_north";
lon:units = "degrees_east";
level:units = "millibars";
short time(time);
time:units = "hours since 1996-1-1";
// global attributes
:source = "Fictional Model Output";
宣告变量的地方,其中要注意的是:

float
int short 是变量的型态, float 为小数点、int 为整数,这一部分会在后面详细说明。
至于变量名称则有 temp, rh, lat, lon, level, time等,并且定义 temp 为时间、层、经纬度的函数,
rh
为时间、经纬度的函数等等
另外,在每个变量之下均有 long_name units 的说明,这就是此一变量的说明文件头啦!
至于 //global attributes 指的是这个档案的主要参数喔!(例如网格数、原点坐标、这个档案的建立时间等等)

data: // optional data assignments
level = 1000, 850, 700, 500;
lat = 20, 30, 40, 50, 60;
lon = -160,-140,-118,-96,-84,-52,-45,-35,-25,-15;
time = 12;
rh =.5,.2,.4,.2,.3,.2,.4,.5,.6,.7,
.1,.3,.1,.1,.1,.1,.5,.7,.8,.8,
.1,.2,.2,.2,.2,.5,.7,.8,.9,.9,
.1,.2,.3,.3,.3,.3,.7,.8,.9,.9,
0,.1,.2,.4,.4,.4,.4,.7,.9,.9;
}
这是最后的部分,就是实体数据的存放处啦。level 共有四层,分别为1000,850,700500

以下的 lat lon 均相同意思!
至于 rh 则有 50 个数据,由于 rh 是(时间、经纬度)函数,所以在经纬度共分成50格(5x10
而时间只有一个(12小时)的情况下,因此 rh 就有 50x1 =50 笔资料啦!

请注意,上表是以 ASCII 码的型态来表示 NetCDF 的格式,实际的 NetCDF 格式并非如此,必须藉由 NetCDF 软件的转换程序才能将上面的数据转成 NetCDF 格式文件!不过由于上面的 ASCII 码档案可以经由 NetCDF 的工具软件直接转换成 NetCDF 档案,所以与上面的档案具有相同格式的 files 就被称为 Network Common Data Form Language (CDL) 档案啦!

安装 NetCDF 的那一节中,我们提到了 setup NetCDF 最终会产生两个有用的工具程序,分别是 ncgen ncdump 两个程序。其中, ncgen 可以将上面这一种 CDL 档案 (其实就是 ASCII 码档案) 直接转成 NetCDF 格式的档案,所以如果你不懂如何以 Fortran 写程序来转换 NetCDF 文件,则可以利用文书编辑软件写出上面这一种 CDL 档案后,在 Unix 平台下,直接将档案转换!另外,如果你只是要看看 NetCDF 档案的内容,则可以使用 ncdump 来看档案!这两个档案很常用,尤其是 ncdump 更是常用的不得了!至于使用方法,会在后面再提到!

另外,要注意的是,由于 NetCDF 数据文件中共分为 variable attributes 两种, attributes 可能会被包含在 variable 中,但是 attributes 不可能含有 variable ,且 attributes 没有比较有用的变量型态,因此在 user menual 中作者强烈建议科技数据使用 variables 的型态!

Top


NetCDF 的数据(Data)格式:

在前一节有提到 NetCDF 的主要内容,而其内容中最重要的部分大概就属 variable 的型态啦! NetCDF 的变量型态主要有六种,分别如下:

char

8-bit characters intended for representing text.

28 大小的字符

byte

8-bit signed or unsigned integers.

28大小的整数

short

16-bit signed integers.

216大小的短形整数

int

32-bit signed integers.

232大小的长形整数

float or real

32-bit IEEE floating-point.

232大小的浮点数
单精倍数

double

64-bit IEEE floating-point.

264大小的倍精倍数

上面是主要的 NetCDF 数据变量,而且每一种变量均有既定的单元格式,例如 char 变数中,就仅有 28 个字符的储存空间! int 就只能储存整数型态的数据等等,因此变量的宣告与你原本数据的型态相关性就很重大!这是因为如果你的变量型态定义错误的话,可能会造成你的数据的『准确度』完全不对!并且程序也不会显示错误!这很严重。假设你的原始数据称为 external data 而经由 NetCDF 转换后储存在 NetCDF 档案的数据称为 internal data若你的 external data 0.13245,但是你对这个数据宣告的变量型态为 byte 时,则程序会将 0.13245 存成 『0!小数点后的资料完全不见了!!并且此一状况在程序执行中并不会产生 error message(因为实际上数据并没有错误,有误的只是数据的准确度而已)。此外,如果你的数据为 1025 而你的变量宣告为 byte ,哈哈!那你的 data 将完全不准确啦!因此你要针对你的数据去进行变量的宣告。当然,如果你害怕变量宣告错误造成你的数据准确度的问题的话,这里提供一个建议,那就是将你所有的数值数据均宣告为储存空间最大的 double 这一个变量型态,那你的数据一定不会错!但是相对的,这样一来你的 NetCDF 档案就会无形之中增大很多,你可以想象的到,将 1 1.0000000000.......n0)型态储存,那样的空间一定是占去很大啦。

Top


进阶一:以Fortran程序建立NetCDF档案:

说穿了,使用 Fortran 程序语言要来写 NetCDF 的文件系统的话,是一定要使用到 NetCDF 的数据库(Library)的,他的 Library 不是很好懂,基本上,我们也不是很需要了解啦(如果你只是要会跑 Models-3 而已的话),就连原作者也说即使你不懂 NetCDF Library 也没关系,因为还有 ncgen 这种小工具可以让你直接将档案写入 NetCDF 当中。所以这一节以及底下连续的几个小节,都不是很需要努力的看,不过在讲到 ncdump 的使用方法那一节就要好好的看看啰!

我们可以使用 Fortran 写程序来建立、读取与增加变量于 NetCDF 档案,建立的方法共分为四种,分别为:建立新档案、读取已知变量的旧档案、读取未知变量的旧档案及增加变量进入档案中等四种。本节主要仅在说明需要的变量名称,详细的使用方法将在后面继续说明之。

以下以表稍微解释所需要的变量名称(在 Fortran 语言中的使用喔)

建立新档案需要的变量

NF_CREATE ! create netCDF dataset: enter define mode
...
NF_DEF_DIM ! define dimensions: from name and length
...
NF_DEF_VAR ! define variables: from name, type, dims
...
NF_PUT_ATT ! assign attribute values
...
NF_ENDDEF ! end definitions: leave define mode
...
NF_PUT_VAR ! provide values for variable
...
NF_CLOSE ! close: save new netCDF dataset

读取已知变量的旧档案

NF_OPEN ! open existing netCDF dataset
...
NF_INQ_DIMID ! get dimension IDs
...
NF_INQ_VARID ! get variable IDs
...
NF_GET_ATT ! get attribute values
...
NF_GET_VAR ! get values of variables
...
NF_CLOSE ! close netCDF dataset

读取未知变量的旧档案

NF_OPEN ! open existing netCDF dataset
...
NF_INQ ! find out what is in it
...
NF_INQ_DIM ! get dimension names, lengths
...
NF_INQ_VAR ! get variable names, types, shapes
...
NF_INQ_ATTNAME ! get attribute names
...
NF_INQ_ATT ! get attribute values
...
NF_GET_ATT ! get attribute values
...
NF_GET_VAR ! get values of variables
...
NF_CLOSE ! close netCDF dataset

增加变量于旧档案中

NF_OPEN ! open existing netCDF dataset
...
NF_REDEF ! put it into define mode
...
NF_DEF_DIM ! define additional dimensions (if any)
...
NF_DEF_VAR ! define additional variables (if any)
...
NF_PUT_ATT ! define other attributes (if any)
...
NF_ENDDEF ! check definitions, leave define mode
...
NF_PUT_VAR ! provide new variable values
...
NF_CLOSE ! close netCDF dataset

基本上你可以将每一种方法都区分为两个部分,一个是定义区(define)另一个是数据写入区(data writing)。如上表的『建立新檔』中,先以 NF_CREATE 建立一个档案后,以 NF_DEF_DIM, NF_DEF_VAR, NF_PUT_ATT 宣告数组、变量及放置相关的说明后,再以 NF_ENDDEF 关闭定义区,请注意,一定要『先在定义区宣告一些变量后,并关闭定义区,才可以往下输入数据!』。在完成了变量定义之后,以 NF_PUT_VAR 一笔一笔建立数据的输入,待数据全部输入完毕后,方以 NF_CLOSE 关闭这一个程序!原则上,建立档案的步骤就是这样啰。那详细的执行流程会在后面再加以介绍。

在完成了程序的编写之后,最重要的当然就是编译与连结档案啰(compile and link)!在 Models-3 scripts 中常会看到相关的 NetCDF 的链接指令,所以这里你可以稍微看一下,以了解为何 Models-3 的程序要这样写!

目的:在一个档名为 myprogram.f Fortran 档案中,需要将 include 档案包含进这个程序;
语法: f77 -c -I/usr/local/include myprogram.f
说明:上面的语法中, -c -I fortran flags-c 表示只要编译不要做成执行档,所以执行上面这一行之后,你会产生一个名为 myprogram.o 的档案;至于 /usr/local/include 则是你的 NetCDF 的所在目录下的档案(如果您是用鸟哥教你的安装方法安装的话)!之后则是要将刚刚的编译码做成执行档啦!通常你可以利用下面这两种写法来完成:

语法一:f77 -o myprogram myprogram.o -L/usr/local/lib -lnetcdf
语法二:f77 -o myprogram myprogram.o -l/usr/local/lib/libnetcdf

那就可以得到一个名为 myprogram 的执行档啰!这两种方法都可以将 NetCDF Library 用在你的程序中,而 语法一 是较常于 Models-3 的程序中看到的一种方法!

Top


进阶二:新建、开启、变量宣告与结束档案的 Fortran 程序:

如同前一节(进阶一)提到的开启档案与宣告变量的方法,这一节主要将介绍档案开启与变量宣告的指令与其参数。另外,由于使用的是 NetCDF 的参数,所以在每个 Fortran 程序之前均需要加入 include 'netcdf.inc'』这一行字。其中 netcdf.inc 这个档案是你在安装完了 NetCDF 后所产生的档案,放置在『 /usr/local/include 』中!

  1. NF_STRERROR(status)
    这个指令主要在提供你除错的错误讯息!其中 status 为你程序执行错误时所传回来的数字,为整数!你可以写一个如下的子程序在每一个档案的结尾处,将可以让你的程序具有输出错误讯息的功能!
    -----
    subroutine handle_err(status)
    integer status
    if (status .NE. nf_noerr) then
    print *, nf_strerror(status)
    stop 'stopped'
    endif
    end
    -----
  2. NF_INQ_LIBVERS()
    取得你的 NetCDF 程序版本,语法为:
    -----
    print *, nf_inq_libvers()
    -----
  3. NF_CREATE('filename', mode, ncid) <==极重要,新增加档案用
    若你要建立一个名为 netcdf.ncf NetCDF 档案,则你需要定义这个档案的开启号码(ncid 为整数),与其写入的方式(mode,分为 nf_clobber, nf_noclobber, nf_share)。要注意的是,ncid 虽然只是个整数,不过这个数字是由程序自动判断给的,所以不可以设定为固定!必须为变量型态!一般而言,你可以这样写:
    -----
    integer status, ncid
    status=nf_create('netcdf.ncf', nf_clobber, ncid)
    if (status .NE. nf_noerr) call handle_err(status) <==这里说明有错误就执行 handle_err 这个子程序
    -----
  4. NF_OPEN('filename', mode, ncid) <==极重要,开启旧档案用
    如果是开启一个已存在的档案的话,则使用 nf_open 这个指令啦!只有 mode nf_create 不同,其主要分为 nf_write (可擦写), nf_nowrite (只读文件,这是默认值), nf_share三个主要模式。以下说明开启只读档的方法:
    -----
    status=nf_open('netcdf.ncf', nf_nowrite, ncid)
    -----
  5. NF_REDEF(ncid) <== NF_OPEN 搭配使用
    这个指令是用在当你要在一个已存在的档案中加入新变量时使用的!使用语法为:
    -----
    status=nf_redef(ncid)
    -----
  6. NF_INQ(ncid, ndims, nvars, ngatts, unlimdimid)
    这个指令用在读取一个已存在档案的变量、层数、与说明档数目,所以当然是与 nf_open 有关啰!基本上与读取档较为相关。
    -----
    status=nf_inq(ncid, ndims, nvars, ngatts, unlimdimid)
    write(*,*), ndims, nvars, ngatts, unlimdimid
    -----
  7. NF_ENDDEF(ncid)
    这指令很重要,前面一章提过一个 fortran 程序可分为:变量宣告区与数据区,而变量宣告区『一定』要关闭之后才能进行数据的输入!所以宣告完变量之后一定要有这个指令才行:
    -----
    status=nf_enddef(ncid)
    -----
  8. NF_CLOSE(ncid)
    这个参数可下可不下,不过在你完成了一个 NetCDF 的工作之后,最后还是下达这个指令,以使档案关闭!
    -----
    status=nf_close(ncid)
    -----

Top


进阶三:定义 dimention 指令:

如前一章(进阶二)所述,在建立或开启了一个档案之后,再来就是要宣告维数(dimention)啦!维数的宣告指令如下:

  1. NF_DEF_DIM(ncid, name, len, dimid) <== nf_create 搭配
    这个指令主要目的为:在开启的号码为 ncid 的档案中,增加一个名为 name 的维数,其个数为 len 个,并传回来 ID dimid,注意,这里的 dimid 也必须是变量喔!如果在第一号档案中增加一个 lat 的维数,个数18个,并增加一个 rec 维数,个数可无限增加,则分别可写成:
    -----
    status=nf_def_dim(ncid, 'lat', 18, latid)
    status=nf_def_dim(ncid, 'rec', nf_unlimited, recid)
    -----
  2. NF_INQ_DIMID(ncid, name, dimid)
    这个档案主要与 nf_open 搭配,目的在取得已存在档案的维数的 ID,这是因为在每个维数中均有其代号(就是 ID),透过这个代号可以将相关的档案讯息读入!所以在读取一个已知档案时,通常需要这个指令喔!如下面的程序中若与上面一个指令相配合!
    -----
    status=nf_inq_dimid(ncid, 'lat', LATID)
    -----
  3. NF_INQ_DIM(ncid, dimid, name, len)
    这个程序一定是与 nf_inq_dimid 配合使用的,由 nf_inq_dimid 取得维数的代号之后,以这个指令将此维数(dimention)的讯息取得之。与上面的程序相配合,由下面的指令可以发现 name='lat', len=18
    -----
    status=nf_inq_dim(ncid, LATID, name, len)
    -----
  4. NF_RENAME_DIM(ncid, dimid, name)
    这是一个可以改变维数名称的指令,与上面的程序相配合的话,则可以将 lat 改成 latitude 的维数名称:
    -----
    status=nf_rename_dim(ncid, LATID, 'latitude')
    -----

Top


进阶四:建立与取得变量数据:

在宣告完了维数之后,当然就是变量的宣告与写入啰!基本上变量的定义用 nf_def_xxx, 而变量的调查使用 nf_inq_xxx, 变量的写入使用 nf_put_xxx, 而变量的读取则使用 nf_get_xxx,以下分别介绍之:

  1. NF_DEF_VAR(ncid, name, xtype, nvdims, vdims, varid) 宣告变量
    这个指令在定义变量,上式中,name 为变量名称,xtype为变量的型态,还记得六种型态吧?分别是:nf_byte, nf_char, nf_short, nf_int, nf_float, nf_double 等六种。nvdims则是这个变量的内变量为多少,vdims则是变量的函数相对应的维数IDvarid则是这个变数的代码啦!假设我们要记录一个相对湿度的变量,其根据经纬度及时间(三个内变量),所以 vdims 就必须与 经度、纬度、时间的ID相同啦!且其变量代码设为 VARID ,则可以写成:
    -----
    status=nf_creat('netcdf.ncf, nf_noclobber, ncid)
    status=nf_def_dim(ncid, 'lat', 5, LATID)
    status=nf_def_dim(ncid, 'lon', 10, LONID)
    status=nf_def_dim(ncid, 'time', nf_unlimited, TIMEID)
    integer RHDIMS(3)
    RHDIMS(1) = LONID
    RHDIMS(2) = LATID
    RHDIMS(3) = TIMEID
    status=nf_def_var(ncid, 'rh', nf_double, 3, RHDIMS, RHID)
    -----
  2. NF_INQ_VARID(ncid, 'name', varid) 调查变量代码
    这个指令的作用在取得变量的 ID ,基本上与前一节(进阶三)的 nf_inq_dimid 相似。在取得了变量的 ID 之后才可以取得变量的其他信息,所以这个指令与下一个指令通常适合在一起写的!语法为:
    -----
    status=nf_inq_varid(ncid, 'lat', LATID)
    print *, LATID <== 可以列出从上个指令取得的变量代码!
    -----
  3. NF_INQ_VAR(ncid, varid, 'name', xtype, ndims, dimids, natts)
    根据 ncid varid (前一个指令得到的)两个数据,便可取得 xtype, ndims, dimids, natts 的信息!语法为:
    -----
    status=nf_inq_var(ncid, LATID, latname, lattype, nlat, latdims, latnatts)
    write(*,*), latname, lattype, nlat, latdims, latnatts
    -----
  4. NF_PUT_VAR1_type(ncid, varid, index(x), type_val) 写入变数
    这个指令在将你的『一笔数据』『读入』数据储存区啰!是很重要的一个指令!其中,type 为六种数据型态的名称,分别为『TEXT, INT1, INT2, INT, REAL, DOUBLE』六种。至于 index 则是一个数组,指出变量要存的地方,下面的指令更重要喔!可以去看看!在这个指令的语法:若我们要记录三个相对湿度进入档案中,且三个相对湿度都是 0.5 ,则:
    -----
    integet RHINDX(3)
    data RHINDX /4, 3, 2/
    statuse=nf_put_var1_double(ncid, RHID, RHINDX, 0.5)
    -----
  5. NF_PUT_VAR_type(ncid, varid, type_var(x))
    这个指令比刚刚的更常使用在一个新建立的档案中,因为他是将『所有的数据数组均写入档案中』的一个指令!type 的型态与上个指令相同。假设我们要写入一个三维数组函数(相对湿度),你可以这样写:
    -----
    double rhvar(ilon, ilat, itime)
    do 10 ilon=1, lons
    do 10 ilat=1, lats
    do 10 itime=1, times
    rhvar(ilon, ilat, itime)=0.5
    10 continue
    status=nf_put_var_double(ncid, RHID, rhvar)
    -----
  6. NF_PUT_VARA_type(ncid, varid, start(x), count(x), type_var(x))
    这个指令在于写入一部分数据进入一个 array 中。注意!这个 array 的维数要与已存在的档案相同,你可以与下一个指令相配合着看!这个指令比较常用在新增加或取代一部分数据于一个已存在的档案中。其中,start(x)指开始写入的位置,count(x)则是写入的个数!与上一个指令的范例相配合,我们可以发现 rhvar 为一个三维的数组,所以 start count 都需要输入三个变量启始与个数值。若由第1个写起,共写了 lons, lats, times 个,则:
    -----
    integer start(3), count(3)
    data start /1, 1, 1/
    data count /lons, lats, times/
    status=nf_put_vara_double(ncid, RHID, start, count, rhvar)
    -----
  7. NF_PUT_VARS_type(ncid, varid, start(x), count(x), stride(x), type_var(x))
    配合上一个指令,这个指令仅写入一个比原来的数组(array)还要小的数组中,更常用于取代一个已存在的档案中的数据。例如上一个指令的范例中 rhvar 为三维变量,我们要改变其中两个维数,则可以写成:
    -----
    integer start(2), count(2), stride(2)
    statuse=nf_put_vars_real(ncid, RHID, start, count, stride, rh)
    -----
  8. NF_PUT_VARM_type(ncid, varid, start(x), count(x), staride(x), imap(x), type_var(x))
    这个指令我看不太懂,因为 map 的定义我并不明了之故!不过,基本上储存变量的方法以 nf_put_var, nf_put_vara, nf_put_var1 这三个指令较为重要!
  9. NF_GET_VAR1_type(ncid, varid, index(x), type_var)
    这个指令在取得某变量的『一笔数据』,例如 rhval 这个三维数据中,我们要取得 rhval(4,3,2) 这个变量值,你可以这样写:
    -----
    integet rhidx(3)
    data rhidx /4, 3, 2/
    statuse=nf_get_var1_double(ncid, RHID, rhidx, rhval)
    print *, rhval
    -----
  10. NF_GET_VAR_type(ncid, varid, type_var(x))
    这个指令则是『取得某一变量的全部数据』。其中 type_var(x) 是一个数组啰!
    -----
    status=nf_get_var_double(ncid, RHID, rhval)
    -----
  11. NF_GET_VARA_type(ncid, varid, start(x), count(x), type_var(x))
    同样的,这个指令就是取得某一个数组区域内的变量数据啦!用法与前面 nf_put_vara_type 相同哩!
  12. NF_GET_VARS_type(ncid, varid, start(x), count(x), stride(x), type_var(x))
    这个也应该不用再说了吧!就是取得更小的数组的数据啦!
  13. NF_RENAME_VAR(ncid, varid, 'newname')
    这个指令在更改一个变量的名称,需要与 nf_redef, nf_inq_varid 配合写入,如下所示:
    -----
    status=open('netcdf.ncf', nf_write, ncid)
    status=nf_redef(ncid)
    status=nf_inq_varid(ncid, 'rh', RHID)
    status=nf_rename_var(ncid, RHID, 'rel_hum')
    status=nf_enddef_dim(ncid)
    -----

Top


进阶五:建立与取得说明数据:

在前面的进阶一至进阶四中你已经建立好了一个 NetCDF 的档案了,但是你这个档案内容的属性还没有很详细的被定义下来,这个说明部分就是所谓的 attributes 部分。为何要加入这一部分呢?这是因为我们在前面的时候就已经提到了, NetCDF 可以有很详细的文件属性说明,以避免档案的内容被误判!基本上 Models-3 里面的输出档,都有很详细的说明档头,另外,针对每一个变量亦均加入了适当的说明,例如单位(moles/s)、长名(long name)等等。底下说说主要的几个指令吧!

  1. NF_PUT_ATT_type(ncid, varid, 'name', xtype, len, type_var(x))
    同样的,与(进阶四)中提到的相同, xtype 共分为六种型态:nf_byte, nf_char, nf_short, nf_int, nf_float, nf_double 等六种。而 type_var(x) 是一个数组喔!例如我们要对 rh 进行变量说明,说明的内容为 valid_range』,而我们给他的合理区间在 0.0D0 100.0D0)则:
    -----
    double rhrange(2)
    data rhrange /0.0D0, 100.0D0/
    status=nf_put_att_double(ncid, RHID, 'valid_range', nf_double, 2, rhrange)
    -----
  2. NF_INQ_ATT(ncid, varid, 'name', xtype, len)
    调查 attributes 的档头说明,通常与 nf_inq_varid 配合使用。
    -----
    status=nf_inq_varid(1, 'rh', rhid)
    status=nf_inq_att(ncid, rhid, name, xtype, len)
    write(*,*), name, xtype, len
    -----
  3. NF_GET_ATT_type(ncid, varid, 'name', type_var(x))
    取得 attribute 的内容!
    -----
    status=nf_get_att_double(ncid, rhid, 'balid_range', vrval)
    -----
  4. NF_RENAME_ATT(ncid, varid, 'name', newname)
    nf_rename_var 相似的用法,往前翻翻看看吧!
  5. NF_DEL_ATT(ncid, varid, 'name')
    就是砍到不要的 attribute 咯,方法如下:
    -----
    status=nf_del_att(ncid, rhid, 'valid_range')
    -----

Top


进阶六:程序范例:

基本上,如同一开始说到的,进阶这一部分的资料在 for fortran NetCDF 手册中有蛮详细的说明,所以如果你有任何关于程序的写作问题真的直接就去找原文啦!不过原文手册中虽然有对于每一个指令进行详尽的解释,但是并没有一个完整的 fortran 程序,因此鸟哥我本人就写了几个小程序来玩玩啦,如果你有兴趣的话,可以下载来执行看看啰。(注:写完了这个程序后,让小弟对于 NetCDF 档案的制作过程有更清楚了理解。)

范例一:建立一个三维的档案,里头有两个变量,分别为 PM10 PM2_5,其中,PM10 50 PM2.5 30,且说明内容 PM 浓度为 ug/m3。按这里下载吧!

Top


有用的工具:

NetCDF 的单元格式:
NetCDF
在储存的时候大致上分为两个区域,如同前面在讲到 CDL 档案的时候提到的样子。分为:

  • 档头(header):主要用来记录变量名称、个数与型态;dimention名称、个数与型态;以及相关的说明内容。由于这一部分内容并没有用到实际数据储存的空间,所以使用的空间并不会多出来!
  • 资料区(data):这一部分就是实际数据记录的地方啦!由于这一部分的空间有涉及你当初设定的变量型态,假如你的数据为整数,但是你记录的单元格式为倍精倍数,则这个区域的储存空间无形之中会变的很大!

基本上就仅分为这两个部分!所以你在看档案的时候,看前面就知道后面数据的型态!

ncgen
这个小程序主要是用来将 CDL 档案转成 NetCDF 档案的工作!假设你已经写了一个 CDL 档案,名称为 netcdf.cdl ,你想将此档案转成 NetCDF 档,则只要下达:

# ncgen -o netcdf.ncf netcdf.cdl

就可以了,语法为: ncgen -o [output filename] [input filename]
而且 ncgen 亦提供一个不错的选项,亦即将你的 netcdf.cdl 档案转成一个 fortran 程序,则你只要将此 程序 编译并执行后,则可以将 netcdf.cdl 转成 NetCDF 档案!

# ncgen -f netcdf.cdl > netcdf.f

上式中 netcdf.f fortran 程序文件。不过用此语法需要注意,因为此一语法仅适合比较小的 CDL 档案!

ncdump
另外一个最有常用到的程序称为 ncdump ,主要的目的在于将 NetCDF 档案数据读成 ASCII 码的数据!由于 NetCDF 储存区分为两段,一个为档头、一个为数据区,若你只想读取数据区的变量使用情况,则可以下达:

# ncdump -h netcdf.ncf

则屏幕上将出现 netcdf.ncf 这个档案的文件头数据。而如果你想要将所有的档案都输出成为 ASCII 码数据,则直接下达:

# ncdump netcdf.ncf | more

即可,在上式中, "| more"这个指令作用是将 netcdf.ncf 这个档案以一个画面一个画面的方式展示之意。而如果你想要将画面的资料存成一个 CDL 档案,可以下达:

# ncdump netcdf.ncf > netcdf.cdl

则程序将直接把 netcdf.ncf 档案转存成一个 netcdf.cdl ASCII 码格式的档案!如此则你将看的到里头的资料啰!
不过使用的时候要小心,因为一个 Models-3 输出档常常是几百 MB 的大小,如果你将 NetCDF 的数据直接存成 ASCII 码的档案,将耗费很多时间与空间!因此上,除非是你在除错或者是档案很小,否则不是很必要将档案转存成 ASCII 格式的档案!
而若你只想察看 netcdf.ncf 档案里面一个名为 'TEMP' 的变量的话,你可以下达:

# ncdump -v TEMP netcdf.ncf

语法为:ncdump -v [变量名称] [文件名]。这也是一个常用的参数喔!

Top

网站连结:

NetCDF User's Guide for FORTRAN
Unidata官方网站

发表评论 评论 (2 个评论)

回复 mofangbao 2012-9-2 17:12
存下来留着~
回复 flyingbird2008 2012-9-4 12:00
   非常有用,赞!

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 立即注册

Copyright ©2011-2014 bbs.06climate.com All Rights Reserved.  Powered by Discuz! (京ICP-10201084)

本站信息均由会员发表,不代表气象家园立场,禁止在本站发表与国家法律相抵触言论

返回顶部