GPhys tutorial

Horinouchi 武

Creation: August, 2003

The last revision: March 26, 2004


Table of contents

  1. Introduction
  2. Reference manual
  3. Link
  4. Data used by this tutorial
  5. The fastest drawing (contour)
  6. The fastest drawing (polygonal line)
  7. GrADS form vs NetCDF form
  8. The contents of GPhys are investigated.
  9. The foundation of data operation
  10. The unit of the amount of physics
  11. Data is written out to a NetCDF file.
  12. It is a bundle virtually about a division file.
  13. GGraph is mastered (the 1).
  14. GGraph is mastered (the 2).
  15. /remote data is accessed -- carry out

Introduction


GPhys is a class for treating the data of the amount of physics. What is assumed here is the amount of physics of the continuity dispersed in the shape of a lattice, and consists of a value of the amount of physics in many information and each point about a lattice point, and additional information about the amount of physics further. GPhys is convenient such at the time noting that it follows for details and follows and explains.

The file format which can treat GPhys is now. NetCDF Although it is only GrADS, it will be in a target in the future. HDF I think that he wants to support grib etc.


In this tutorial, the 2nd simulation is not touched on but it will concentrate on the treatment of the data which is already a file. I have the treatment of a NetCDF/GrADS file experienced focusing on visualization. Although data treats what exists in a local host fundamentally, the example of the very easy server and client which access the data which exists in a remote host is also shown.


With the following Drawing library of attachment in GPhys The example using GGraph comes out mostly. However, it is until it GGraph(s) and opens. It is a position of one of the examples of use of GPhys, and is not the indispensable composition element of GPhys. For this reason, the GPhys itself GGraph and its subcontract RubyDCL Even if there is no が, it operates (in that case). Here It was alike and wrote. require "numru/gphys" It uses in the form to say. The user of GPhys can develop his own drawing library independently of GGraph. For example, a graphical user interface gave は and GGraph are not used.


It used. Version of GPhys


The sample program of this tutorial, GPhys-0.3.0 It came out, and performed and checked. In addition, the many 0.1.0 operates.


Document


Here is the reference manual of GPhys. :

Although not now created partially, the manual of the drawing library GGraph mentioned later is fixed.



Link


Installation


Installation of a package

. which it is automatic and can be installed in some OS's also including a dependence library Cyber- Ruby Japanese homepage Refer to a の "installation guide" corner.

Installation from sauce

Version of the following and GPhys Suppose that it is 0.3.0. GPhys Since it is written only by Ruby, installation is only copied to a suitable path. The following performs it. :
% tar xvzf gphys-0.3.0.tar.gz % cd gphys-0.3.0% ruby install.rb 

However, there are some which must be variously installed before it. detailed GPhys homepage Please illuminate を 3. Required one They are NArray, NArrayMiss, RubyNetCDF, and RubyDCL. If it does not visualize There may not be RubyDCL. Moreover, now RubyDCL Since it does not correspond to NArrayMiss, the treatment of data with a deficit takes cautions (schedule made to correspond in the near future).



Data used by this tutorial


NetCDF data


In the distribution package of GPhys, it is for a test. T.jan.nc Since the NetCDF file to say is included, it will be used. testdata It is in the subdirectory to say. The same thing, Here since -- it can download Contents It is a climate value in January of the temperature of all balls in NCEP re-analysis. This file shall be copied to the directory for work, and the following menus shall be performed there.


First of all, I will look at the outline of this file. The command ncdump of NetCDF attachment is used.
% ncdump -c T.jan.nc netcdf T.jan {dimensions:          lon = 36 ;          lat = 19 ;          level = 9 ;variables:          float lon(lon) ;                  lon:units = "degrees_east" ;                 lon:long_name = "Longitude" ;                lon:actual_range = 0.f and 357.5f  ; float lat(lat) ;                  lat:units = "degrees_north" ;                  lat:actual_range = 90.f, -90.f ;                  lat:long_name = "Latitude" ;          float level(level) ;                level:units = "millibar" ;                level:long_name ="Level"  ; level:positive = "down" ;                  level:GRIB_id = 100s ;                  level:GRIB_name = "hPa" ;                  level:actual_range = 10.f, 1000.f ;          float T(level, lat, lon) ;                 T:long_name = "Monthly Mean Air temperature";                  T:actual_range =-72 . - 66724 F 24.35F  ;                  T:units ="DegC"  ;                  T:add_offset = 0.F  ;                  T:scale_factor = 1.F  ;                  T:missing_value =-9.96921E+36F  ; .. Omission data: lon = 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150,     160, 170, 180, 190, 200, 210, 220, 230, 240, 250, 260, 270, 280, 290,     300, 310, 320, 330, 340, 350 ; lat = 90, 80, 70, 60, 50, 40, 30, 20, 10, 0, -10, -20, -30, -40, -50, -60,     -70, -80, -90 ; level = 1000, 850, 600, 400, 250, 150, 70, 30, 10 ;} 

It is original. NCEP data is horizontally. Although it is resolution of the degree of 2.5, it is made coarse here and data size is pressed down. Data is thinned out also in the perpendicular direction. Temperature T "It turns quickly" is a longitude axis most by は経度, latitude, and the 3-dimensional data of pressure. Display of the dimension of ncdump Since it is the style of C language T(level, lat, lon) Although it comes out, with the multi-dimension data of Ruby, it is to the contrary. lon, lat, level A dimension is put in order by の順番 (this is the style of NArray and all multi-dimension data libraries, such as RubyNetCDF and GPhys, follow.). It is the same as Fortran. --, however an arrangement subscript begin from zero. Longitude, latitude, and an advanced lattice point value are a 1-dimensional variable with the same name as a dimension name. lon , lat , level It is alike and is stored.


GrADS data


the above NetCDF data -- fundamental -- the thing of the same contents . which has prepared also in GrADS form -- this Although it is necessary to download . data file the way of a control file is used [ data file ] for the bootstrap for reading like . after-mentioned divided into the control file of text form, and the data file of a binary the case of .GrADS contained in the distribution package of GPhys to the same directory, the file name may forget henceforth.


It visualizes for the time being.


The fastest drawing (contour)


I will draw a figure instantly. Graphic library for GPhys visualization in a GPhys distribution package Since GGraph is contained, it will be used. GGraph serves as a module and offers a drawing method as a module function. For example, the method which draws a contour figure (module function) GGraph::contour It comes out. In addition, also GPhys Cyber of others [ GGraph ] Like the Ruby library, it puts into a module called NumRu and name space is protected. Therefore, full name NumRu::GPhys NumRu::GGraph It becomes など.


The 2-dimensional drawing method in GGraph contour , tone で は -- if the given data is three dimensions or more -- 3, 4, and .. about a dimension eye, the first lattice point is chosen automatically Therefore, the following programs display the temperature of the lattice point = lowest layer (1000hPa) of the 3rd-dimensional beginning (the line number is attached for the facilities of explanation.). It is not contained in fact.

contour1000mb_1.rb
1: require "numru/ggraph"2: include NumRu3: gphys = GPhys::NetCDF_IO.open('T.jan.nc', 'T')4: DCL.gropn(1)5: DCL.sgpset('lcntl', false) ; DCL.uzfact(0.7)6: GGraph.contour( gphys )7: DCL.grcls 

It performs as follows.
% ruby contour1000mb_1.rb 

Then, the next figure is displayed on a screen.


contour1000mb_1.rb execution result

The 2nd line is the 1st of the above-mentioned program, and a spell called each time. Beginning require "numru/ggraph" は -- all required libraries are loaded Following include NumRu Although there may be no end, in that case, it is. GGraph DCL はすべて Full name, It is got blocked. NumRu::GGraph NumRu::DCL You have to come out and call.


The 3rd line gphys = GPhys::NetCDF_IO.open('T.jan.nc', 'T') Then, a file T.jan.nc An inner variable T It is alike, and receives, a GPhys object is constituted, and it is a variable name. gphys を割り当てている. It is here and is a module. GPhys::NetCDF_IO は The interpretation of a NetCDF file is managed. Unless especially a user specifies, the interpretation according to the NetCDF you ZAZU guide is performed. Dimension name = a coordinates variable (lon, lat, and level) is also interpreted according to the rule of a variable name, and reference is held in a GPhys object (here variable name). gphys .


The 4th line DCL.gropn(1) It is graphic equipment initialization of は and DCL. An argument When it is 1, it is displayed on a terminal screen. Although the 5th line is usually unnecessary, the interpretation of a control character is controlled here. DCL.sgpset('lcntl', false) The underscore etc. is 0.7 doubling the size of the character attached to an axis of coordinates making it not interpreted as a special character. DCL.uzfact(0.7) .


It is drawing still more in the 6th line. GGraph.contour(gphys) It was given to は and the argument. The data of a GPhys object is indicated by the contour line. As mentioned above, since the first point is automatically chosen about the 3rd dimension or subsequent ones, it becomes the plot of the level distribution in 1000 mb. 1000 It Indicated that Mb was Chosen outside the Limit at the upper right of Figure. level=1000 milibar の表示 shows. In addition, it is since the character sequence which should be displayed in this figure is long. 'm' or subsequent ones is not displayed. The method of avoiding this is mentioned later. A figure should show. GGraph.contour An axis of coordinates etc. is interpreted on a は automatic target (it is cautious of the x-axis and the y-axis being written correctly). In fact, it indicates that it controlled the control character interpretation by the 5th line. It is because the underscore is contained in the unit of x and y axis.


The 7th line It is end processing of DCL.

As mentioned above, if the 5th line which is necessarily unnecessary is removed, even from opening of a file to visualization and end processing can carry out by six lines in all. The method of customizing a figure is described later. It is a point in that case that a program does not become long suddenly.


The fastest drawing (polygonal line)


I will draw a polygonal line in the same way. It is the upper program. The 6th line GGraph.contour(gphys) GGraph.line(gphys) What is necessary is for it to be alike and just to change. However, if it is it, the first lattice point (that is, North Pole) will be chosen about the 2nd (latitude) dimension, and it is not interesting. Then, I will draw the perpendicular profile near Japan. The method of GPhys cut Logging by は実 space is performed. This can do two kinds of usage, the method of specifying a dimension in order, and the method of specifying a dimension by the name. The following two lines bring the completely same result.
gp_jpn = gphys.cut(135,35,true)gp_jpn = gphys.cut('lon'=>135,'lat'=>35) 

The specification method of the argument in the beginning NArray [] It is the same as that of a method. Therefore, like the case where it specifies by the name, in order to correspond to the data of the numbers (rank) of dimensions arbitrary two dimensions or more, it is not true. What is necessary is just to use the "rubber dimension" by false. A rubber dimension is arbitrary 個. true (specification of all ranges) It corresponds.
gp_jpn = gphys.cut(135,35,false) 

a language called yorick although it is a digression -- rubber dimension [ ] -- it can express .. that is, -- (135, 35, ..) etc. . this gentleman -- appearance -- although it is intelligible, since .. cannot be made to become independent, it cannot use by Ruby


I will draw a figure.


line_1.rb
1: require "numru/ggraph"2: include NumRu3: gphys = GPhys::NetCDF_IO.open('T.jan.nc', 'T')4: DCL.gropn(1)5: DCL.sgpset('lcntl', false) ; DCL.uzfact(0.7)6: GGraph.line( gphys.cut(135,35,false) )7: DCL.grcls 

A result becomes like this. :


line_1.rb execution result

It is displayed outside the upper right limit as "lon=130.0 degrees_east" and "lat=40.0 degress_north." When cut chooses the nearest lattice point and middle is specified by chance, the one where a lattice point number is more smaller is chosen. Since longitude is stored in east -> west and latitude is stored in north -> south Such latitude and longitude are chosen.


Now, if the pressure which is perpendicular coordinates comes to a horizontal axis, mind which is a perpendicular profile very much will not carry out. then, a vertical axis and a horizontal axis -- changing -- a pressure axis -- logarithm -- I will express as a scale How to make it all screen displays so that the information at the upper right of outside the limit may not go out in passing DCL.sgpset('lfull',true) .


line_2.rb
1: require "numru/ggraph"2:  include NumRu3: gphys  = GPhys::NetCDF_IO.open('T.jan.nc', 'T')4: DCL.gropn(1)5:-DCL.sgpset('lcntl', false) ; DCL.sgpset('lfull',true)  ; DCL.uzfact(0.6)6: GGraph.set_fig( 'itr'=> 2, 'viewport'=>[0.25,0.7,0.15,0.6] )7: GGraph.line (it cut(s) (135, 35, false) gphys. --)   tr ue, 'exchange'=>true )8: DCL.grcls 


line_2.rb execution result

the upper program -- a vertical axis -- logarithm -- in order to make it scaling -- the 6th line -- the option of GGraph.fig itr It is を設定(ing). GGraph manual Reference. furthermore, viewport is changed in response to all screen displays (a default value -- [-- 0.2, 0.8, 0.2, and 0.8]) GGraph.fig is a method which defines the frame of a new figure, and the 2nd argument of GGraph.line It is called when it is true. The 2nd argument is omissible and true is a default value. The 7th line Although GGraph.line is called, since the option of GGraph.line is the 3rd argument (henceforth), it is the 2nd argument. true is set up explicitly (since it is easy to mistake, if the 2nd argument is except true/false, an exception informs a user). And it is as an option. 'exchange'=>true It is を指定(ing). Thereby, coordinates are written to a default and a contrary and it comes to write a vertical axis and data to a horizontal axis. An option is explained later again.


GrADS form vs NetCDF form


With GPhys GrADS form ., however now which can treat NetCDF form similarly The beginning in GrADS form does not support.

The following programs and the above contour1000mb_1.rb The の of the 3rd line NetCDF_IO GrADS_IO It is alike. T.jan.nc T.jan.ctl it is only what was boiled and changed . imagination of is done -- as -- The file of GrADS form is read and the same figure is drawn.

contour1000mb_grd.rb
1: require "numru/ggraph" 2: include NumRu 3: gphys = GPhys::GrADS_IO.open('T.jan.ctl', 'T') 4: DCL.gropn(1) 5: DCL.sgpset('lcntl', false) ; DCL.uzfact(0.7) 6: GGraph.contour( gphys ) 7: DCL.grcls 

It is [. the usage depending on which automatic distinction of a file format came to be performed from GPhys 0.3.1, and ] a top. GPhys::NetCDF_IO , GPhys::GrADS_IO The place which was being carried out GPhys::IO It only carries out.

contour1000mb_common.rb (More than GPhys 0.3.1 用)
1: require "numru/ggraph" 2: include NumRu 3: gphys = GPhys::IO.open('T.jan.nc', 'T')        # GPhys::IO ! 4: DCL.gropn(1) 5: DCL.sgpset('lcntl', false) ; DCL.uzfact(0.7) 6: GGraph.contour( gphys ) 7: DCL.grcls 


The contents of GPhys are investigated.


How to investigate the contents of data simply, without drawing a figure is explained. first of all -- Dialog shell of Ruby I will use irb. Following Although the screen of kterm is cut & pasted, on account of html, it smells and the parenthesis is made into full size. irb(main):001:0> 等は The prompt of irb," => " The following is the output of the execution result of each line.
% irbirb(main):001:0>  require "numru/gphys"=>  trueirb(main):002:0>  include NumRu=> Objectirb(main):003:0> gphys = GPhys::NetCDF_IO.open('T.jan.nc', 'T')=> <GPhys grid=<3D  grid <axis pos=<'lon' in 'T.jan.nc'  sfloat[36]>>        <axis pos=<'lat' in 'T.jan.nc'    sfloat [19] >>          < axis pos=<'level' in 'T.jan.nc'  sfloat[9]>>>   data=<'T' in 'T.jan.nc'  sfloat[36, 19, 9]>>irb(main):004:0> 
The 1st-line input is the former. require "numru/ggraph" It is different. require "numru/gphs" It has become. GGraph will not be read if it carries out like this (even when not drawing a figure, of course). Only a GPhys main part is read and it is a graphic library. require "numru/ggraph" Although not mattered, since it comes out, and he wanted to tell carrying out like this if you want to carry out without graphics. し [ this gentleman of a standup is somewhat quick ]. . irb leaves the result which performed each line and is a display function. It outputs to standard output using p. p it is convenient although the display boiled and depended is not necessarily beautiful Result of the 1st line Since it is true, it turns out that the library was loaded. The input of the 2nd line is a promise. include NumRu It comes out. With and the 3rd line The GPhys object is constituted. gphys = GPhys::NetCDF_IO.open('T.jan.nc', 'T') . The result What was outputted by p is crossed to four lines. Although it is somewhat hard to see, it is 3-dimensional data of lon, lat, and level. It is T and can grasp being stored in T.jan.nc, respectively. A numerical value type and the length of each dimension are also displayed. How to put a dimension in order The dimension is the same as that of Fortran, and "it turns around most quickly" comes first. This is referred to. .


I will perform the next program.


inspect.rb
1: require "numru/gphys"  2: include NumRu 3: p gphys = GPhys::NetCDF_IO.open('T.jan.nc', 'T') 4: p 'name = '+gphys.name, 'rank:',gphys.rank, 'shape:',  gphys.shape 5: print '[1st dim]  name:', gphys.coord(0).name,  6:       '  long_name:',gphys.coord(0.get_att ('long_name') and 7 :        ' units:',gphys.coord(0).get_att('units'), 8:        "   min,max:#{gphys.coord (0).min},#{gphys.coord(0).max}\n" 9: prs = gphys.coord(2).val10: t_jpn = gphys.cut(135,35,true).val11:  print "\n#{gphys.coord(0).name}\t#{gphys.name}\n"12: for i in 0...prs.len gth do13:     print prs [i], "\t", and t_jpn[ i], "\n"14:end 


Next time You have to call p explicitly. (3 line etc) . An execution result is shown below.


% ruby inspect.rb<GPhys grid=<3D grid <axis pos=<'lon' in 'T.jan.nc'  sfloat[36]>>          <axis pos=<'lat' in 'T.jan.nc'  sfloat[19]>>          <axis pos=<'level' in  'T.jan.nc'  sfloat[9]>>>   data=<'T' in 'T.jan.nc'  sfloat[36, 19, 9]>>"name = T""rank:"3"shape:" [36, 19, and 9] [1st dim]  name:lon  long_name:Longitude  units:degrees_east  min,max:0.0 degrees_east,350.0 degrees_east lon     T1000.0  -5.50255870819092850.0   -13.0088624954224600.0   -23.8766059875488400.0   -41.1900672912598250.0   -52.6089935302734150.0   -51.795578002929770.0    -55.25591659545 930.0    -51.010875701904310.0    -43.6157722473145 

Program I will explain henceforth [ 4 line ].


4 Eye Line :

p 'name = '+gphys.name, 'rank:',gphys.rank, 'shape:', gphys.shape 

は The name (name) of GPhys OBUEJIEKUTO, the number (rank) of dimensions, and the length (shape) of each dimension are displayed.


The 5 - 8th line :

print '[1st dim]  name:', gphys.coord(0).name,           '  long_name:',gphys.coord(0).get_att('long_name'),          '  units:',gphys.coord(0).get_att('units'),          "  min,max:#{gphys.coord(0).min},#{gphys.coord(0).max}\n" 

It is a command of は 1 continuation. 第 Since the necessity for continuation is clear in order that 5, 6, and 7 lines may finish it as a comma, it is interpreted so. gphys.coord( idim は, idim The data which stores the coordinates value of the dimension (it counts from 0) of eye watch is returned (in addition, besides a coordinates value, GPhys arranges the information about each dimension and manages it.). Information about each dimension gphys.axis( idim で得 -- having -- a coordinates value -- it pos Applying a method is also acquired. It is got blocked. gphys.axis( idim ).pos gphys.coord( idim The same result is returned. Here, since it is easy, the description beyond this is not given. A coordinates value returns an object of the class which has the same function as fundamentally as multi-dimension arrangement called VArray (=Virtual Array). In addition to the digital data of many dimensions, VArray can have the "attribute" of a name and arbitrary 個. In the group of arbitrary names (character sequence) and values, an attribute is the specification of a value. It is the same as that of NetCDF (numerical 1-dimensional arrangement or a numerical character sequence). Main part of data of VArray It may be good also at NArray and the variable in a NetCDF file is sufficient. Here, it is the latter. In this case, reading of data is not performed until it is needed. Therefore, opening a huge data file is not pressing a memory immediately. Program If the 5 - 8th line and an output are compared gphys.coord(0).name は名前, gphys.coord(0).get_att('long_name') は属性 long_name gphys.coord(0).max Probably, it turns out that the maximum of a は座標 value is returned etc. It is a return value if it is going to read the attribute not existing. It is set to nil (an exception is not generated).


The 9th line prs = gphys.coord(2).val It is the 2nd (counting from zero) dimension, i.e., pressure. (variable name) level A value is read. Method of VArray val is the value of data. It returns by NArray. Therefore, it is here. (here for the first time) Reading of the variable value from a file occurs.


The 10th line t_jpn = gphys.cut(135,35,true).val Data is read after choosing 1 perpendicular profile near Japan. The following paragraph explains the specification of data reading.


The 11th line displays each name (name) and writes out the pressure in each lattice point, and the value of temperature in the 12 - 14th line.



The foundation of data operation


Here, I will arrange the foundation of the data operation in GPhys. The cautions about data reading about which it should care in case huge data is treated are also described.

Logging


Above Method cut It is alike and depends. It was said that GPhys can be started. In the data logging method of GPhys cut [] There are two kinds of の. cut は -- logging which specifies physical coordinates -- carrying out [] Logging based on the index (subscript as = arrangement) of the lattice point of は data is performed. First of all, an example shows each specification.


kiridashi.rb
require "numru/ggraph"include NumRugphys = GPhys::NetCDF_IO.open('T.jan.nc', 'T')print "gphys: rank=#{gphys.rank} shape=#{gphys.shape.inspect}\n" gp135 = gphys.cut ('lon'=> 0..90)             Axis named # lon   up to the degree of 0 - 90 -- print"gp135 : --  rank=# {gp135.rank} shape=#{gp135.shape.inspect} \n -- " gprg1 = gphys.cut(100.0..150.0, 30..50, and 850) # 1 and the 2nd dimension -- the range and the 3rd dimension -- a value -- print -- "gprg1 # [ rank=] {gprg1.rank} shape=# {gprg1.shape.inspect}" --       "  0th dim : # {gprg1.coord(0).min} .. #{gprg1.coord(0).max} \n" gprg2 = gphys[0 [ It is specification print"gprg2 rank=#{gprg2.rank} in an arrangement subscript but like # top. shape=#{gp ] .. 3 -3 .. -1 and 0] rg2.shape.inspect}",       "    0th dim:   # {gprg2.coord(0).min} .. #{gprg2.coord(0).max} \n"p gprg2       # It leaves and displays. 


The following is as a result of execution. :


% ruby -Ke kiridashi.rbgphys: rank=3 shape=[36, 19, 9]gp135: rank=3 shape=[10, 19, 9]gprg1 rank=2 shape=[6,  3]  0th dim: 100.0 degrees_east .. 150.0 degrees_eastgprg2 rank=2 shape=[4, 3]  0th dim: 0.0 degrees_east  .. 30.0 degrees_east<GPhys-grid=<2Dgrid <axis pos=<'lon' shape= [4]    subs et of a NumRu::VArrayNetCDF>>        <axis pos=<'lat' shape=[3]  subset of a NumRu::VArrayNetCDF>>>   data=<'T' shape=[4, 3]  subset of a NumRu::VArrayNetCDF>> 


the last line it is based on p -- leaving -- display p gprg2 In the result, the data of 'lon', 'lat', and 'T' shows that all are "subset of a NumRu::VArrayNetCDF(s)". VArrayNetCDF It is a class like the socket for showing a は NetCDF variable like arrangement. In fact, all "logging" in GPhys only defines the reference which restricted the range of the original data. furthermore, this example -- like -- the data in a NetCDF file -- being related -- It becomes a saucer when a GPhys object is defined. VArrayNetCDF As は and internal data It has only the reference to a NetCDF variable. Therefore, the above The value of a variable is not read only by performing kiridashi.rb (however, an attribute is read if needed). Reading of a value does not have a line crack until it is actually needed for visualization etc.


The example of the beginning of visualization It can be alike and can set. GGraph.contour( gphys ) Logging of a 2-dimensional subset (the lowest layer data) is performed first, and only data required for illustration after that is read. Therefore, it is satisfactory though the original data is several 100MB of 4-dimensional data.


Now, since it is reference, logging is the above. gprg2 If に値 is written in gphys And Daigen Change will be reflected in a NetCDF file. However, at a default, it is a file. Since it is opened by Read-only, a fear of changing a file carelessly is not needed. The first argument of GPhys::NetCDF_IO.open is to receive a NetCDF object or a character sequence in fact. And if it is a character sequence, it is inside. NetCDF object of read-only It uses opening.


NetCDF object beforehand opened as writing being possible as follows when he wanted to allow writing It is necessary to give GPhys::NetCDF_IO.open.


file = NetCDF.open ('T.jan.nc' and 'a')          #'a' is mode gphys = GPhys::NetCDF_IO.open (file and 'T') which allows edit of a file. 

NetCDF.open の 引数 are a file name and input-and-output mode (an abbreviation is possible). Input-and-output mode It is inclusion to Ruby. It is the same as the specification of a File class (and it is the same as the specification of C language). However, NetCDF is because of being always a binary. 'b' can be read by required く and every mode.


About an average or other operations


GPhys data can take an average or it not only cuts it down, but it can perform mathematics operation and statistics operation. Since the operation concerning data until logging is unnecessary, it is a GPhys object. Although the reference to the variable in a NetCDF file was only held, if it becomes taking an average etc., read-out and operation of actual data will be performed. Data already leaves a file and is held on a memory. However, in GPhys, it is treated completely like the data which the data on a memory also has in a file. Therefore, a user does not need to distinguish both. However, it is better for calculating, after starting a required portion so that a memory may not be pressed when treating huge data to keep in mind.


It is actually a class. It does not know directly at all whether it is in a file of data, and whether GPhys is in NArray. This difference is a GPhys subordinate. VArray (= Virtual Array) absorbs. For example, the above-mentioned VArrayNetCDF is the sub class of VArray. (Therefore, the correspondence to a non-supported format serves as the work with main making corresponding VArray.)


Now, a lower program displays the perpendicular profile of total ball normal temperature. Sauce line_2.rb Note hardly changing.


line_3.rb
1: require "numru/ggraph"2:  include NumRu3: gphys  = GPhys::NetCDF_IO.open('T.jan.nc', 'T')4: DCL.gropn(1)5: DCL.sgpset('lcntl', false) ; DCL.sgpset('lfull',true)  ; DCL.uzfact(0.6)6: GGraph.set_fig( 'itr'=> 2, 'viewport'=>[0.25,0.7,0.15,0.6] )7: GGraph.line (it mean(s) (0 1) gphys. --)  true and 'exc hange'=>true )8: DCL.grcls 

The figure of a result is the bottom. The information at the upper right of outside the limit is cautious of having changed to the thing showing an average.


line_3.rb execution result


Here, it is 第 at mean (0 1). The average of 1 and 2 dimension eye is taken. It is a dimension as well as an arrangement element. It counts from 0. Method of GPhys mean performs an arithmetic average with the equal dignity of each lattice point. Directions Method of NArray It is the same as mean (and there is a method of sum, max, min, stddev, etc. similarly).


Now, it is data in fact. Since the sample was dispersedly carried out by 90N-90S and the thing is contained, as for an average, it is natural to break by length rather, after integrating a trapezoid formula, a SHIMPUSON formula, etc. An average in such real space is a method. It can carry out by average. :


7: GGraph.line( gphys.average(1).average(0), true, 'exchange'=>true ) 

The method which performs operation in such real space is the present place. average It is two of integrate(s). Default integration algorithm is both a trapezoid formula (although algorithm changes by the character of a lattice in fact, when only the you ZAZU guide of NetCDF is being interpreted, since the character of a lattice cannot be known, it becomes so). Since it is necessary to carry out for every dimension, these are average(1).average (0). It is calling in piles. In addition, it is cautious of not becoming the average which took into consideration that the longitude interval of a neighboring lattice point was very short also by this case. It is due to set up the coordinate system which said ball coordinates etc. in the future, and to make it made to interpret.



The unit of the amount of physics


VArray and GPhys recognize the unit of the amount of physics.


A unit is an attribute. It is specified by "units" and is the operation. NumRu::Units が 行う. thereby -- for example, -- VAray Multiplication of GPhys also performs multiplication of a unit. Moreover, constant twice The sum and the difference of data with a unit with the difference for and/or offset are performed after performing the conversion which unite the 2nd clause with the unit of the 1st clause (for example, km, m, conversion between degree_C and K, etc.).


Furthermore, this was used into the package of GPhys. NumRu::UNumeric The class of the numerical value with a unit (scalar) to say is defined. GPhys, VArray, and UNumeric can perform 2 clause operation freely. (The class of a result is together put in order of GPhys, VArray, and UNumeric.) for example, -- UNumric Operation result of GPhys GPhys.


op_units.rb
1: require "numru/gphys"  1: include NumRu 2: temp  = GPhys::NetCDF_IO.open('T.jan.nc', 'T') 3: p( temp.data.get_att('units') ) 4: p( (temp*temp).data.get_att('units')  ) 5: p( (temp*temp).units.to_s ) 6:  7: R = UNumeric[ 287.04, 'J.K-1.kg-1' ]   # Gasecond const of dry air 8p:   (  (R*tem)) p).data.get_att('units') )  # Bad, because temperature should be in K 9: zeroK = UNumeric[ 0.0, 'K' ]10: tempK = zeroK + temp11: p( (R*tempK).data.get_att('units') ) 


The following is as a result of execution. :


% ruby op_units.rb"degC""degC2""degC2""J.K^(-1.0).degC.kg-1""J.kg-1" 


The 4th line (temp*temp) As の結果, it is the unit of temperature. "degC" squares two. It turns out that it is "degC2." At the 7th line, it is a gas constant. R = 287.04 [J. kg-1.K-1] It prepares and is multiplied by temperature in the 8th line. Unit of the result It was set to "J.K^(-1.0).degC.kg-1." K と -- it remembers that it is usually the absolute temperature that hang on a gas constant and it is meaningful although it is somewhat hard to see since degC has not canceled, however if it thinks well -- will come out and I will be (probably) Then, in the 9 or 10th line, it changes into Kelvin using the unit as a result of addition being united with the first clause. :

zeroK = UNumeric[ 0.0, 'K' ]    tempK = zeroK + temp 

To this result Hanging R (the 11th line) and the result came out of the unit with "J.kg-1."



Data is written out to a NetCDF file.


Write-out of GPhys data


It has so far seen. Module in GPhys NetCDF_IO performs the automatic interpretation of a NetCDF file. If this is used, data reads and not only 込 but write-out can be performed easily. The following is the example. The whole data containing a coordinates variable is written out to a NetCDF file.

ncwrite1.rb
1: require "numru/gphys"2:  include NumRu3: gphys  = GPhys::NetCDF_IO.open('T.jan.nc', 'T')4: outfile = NetCDF.create('tmp.nc')5: GPhys::NetCDF_IO.write(  outfile,  gphys.cut ('level'=>1000..250).mean(0) )6: GPhys::NetCDF_IO.write( outfile, gphys.cut('level'=> 1000 .. 250.mean(0 1).rename ('T00) ') )7: outfile.close 

% ruby ncwrite1.rb% ncdump -c tmp.ncnetcdf tmp {dimensions:          lat = 19 ;          level = 5 ;variables:          float lat(lat) ;                  lat:units = "degrees_north" ;                 lat:actual_range = 90.f, -90.f ;                lat:long_name ="Latitude"  ; float level (level); ... (omission) ...          float T (level and lat); ...(omission)... //global attributes :                  : history ="2004-03-19 19:40:17 JST horinout> NumRu::GPhys::NetCDF_IO.write T"  ; data : lat = 90, 80, 70, 60, 50, 40, 30, 20, 10, 0, -10, -20, -30, -40, -50, -60,     -70, -80, - 90 ; level = 1000, 850, 600, 400, 250 ;} 

Sauce の 第 Like the 5 and 6th line, GPhys::NetCDF_IO.write can be repeated and two or more variables can be written to one file. However, it is data written to the 2nd here since the variable of the same name can write only one by restrictions of NetCDF. It has renamed with 'T00'. Since the name of both data is the same if it does not rename, an exception occurs. On the other hand, in the case of a coordinates variable, it is plurality. Since it is sharing between GPhys in many cases, when there is a thing of the existing same name, a dimension and size (shape) are checked, if in agreement, it will be regarded as the same thing and writing will be omitted. When not in agreement, an exception occurs. In the upper example level The coordinates variable to say is shared.

Write-out of other data


(改: by Seiya Nishizawa, Horinouchi)

In case a NetCDF file is written out from 1, it can write easily using GPhys.

the following programs -- a file -- not reading -- coordinates information -- including -- from 1 a GPhys object -- constituting -- it . outputted to a NetCDF file -- once -- it is going via a GPhys object -- it is not necessary to treat the method of RubyNetCDF directly -- a program becomes easy

ncwrite2.rb
require "numru/gphys"include NumRu nlon = 36nlat = 18 lon_a = VArray.new( NArray.sfloat(nlon).indgen(0,360.0/nlon),                    {"long_name"=>"longitude", "units"=>"degrees_east"},                    "lon" )lon = Axis.new.set_pos(lon_a) lat_a = VArray.new( NArray.sfloat (nlat).indgen(0,180/nlat),                     {"long_name"=>"latitude","units"=>"degrees_north"},                     "lat" )lat = Axis.new.set_pos(lat_a) data = VArray.new( NArray.sfloat(nlon,nlat).indgen,                    {"long_name"=>"temperature" and "units"=>"K" --} -- "T" )gphys = GPhys.new( Grid.new(lon,lat), data ) file = NetCDF.create("tmp.nc")GPhys::NetCDF_IO.write(file,gphys)file.close 

File which performs the above-mentioned program and can do it The contents of tmp.nc are checked and it is a way. :
% ruby ncwrite2.rb% ncdump -c tmp.ncnetcdf tmp {dimensions:          lon = 36 ;          lat = 18 ;variables:          float lon(lon) ;                  lon:long_name = "longitude" ;                 lon:units = "degrees_east" ;        float lat(lat) ;       lat:long_name =" latitude" ;                  lat:units = "degrees_north" ;          float T(lat, lon) ;                  T:long_name = "temperature" ;                  T:units = "K" ; // global-attributes:                 :history = "2004-04-19 12:02:36 JST horinout> NumRu::GPhys::NetCDF_IO.write T"  ; data : lon = 0, 10, 20, 30,  40, 50, 60, 70, 80, 90, 100, 110, 120, 130,  140, 150,    160, 170,  180, 190, 200, 210, 220, 230, 240, 250, 260, 270, 280, 290,    300, 310, 320, 330,  340, 350 ;  lat = 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140 and 150      160 170  ;} 
After making the VArray Class object of a variable at once, Axis Class and a GPhys Class object are made.


It is a bundle virtually about a division file.


For the ability of the subset of a certain data to be treated so that it may be the whole, the data of the temperature of ., for example, a day average, which can be treated when [ which was already explained ] one data is conversely divided into two or more files in fact as. Curve is [ GPhys ] for every year. T.2001.nc T.2002.nc , T.2003.nc it supposes that it is divided and needs . -- or it takes the average of the data for these three years 2001 years 2002 years from 11 moons If it thinks that the average by 2 moons will be taken (a weather-related man -- good -- it is -- ?) What is necessary is just to perform it as follows in だあ wax . which needs to put these files together first in the usual application program, however GPhys.

gphys = GPhys::NetCDF_IO.open(['T.2001.nc','T.2002.nc','T.2002.nc'], 'T') 

or

gphys = GPhys::NetCDF_IO.open( /T.(\d\d\d\d).nc/, 'T') 

the former is storing a file in 1-dimensional arrangement, and is 1-dimensional division . . latter using 2-dimensional arrangement specifies the file to be by the regular expression if it is 2 .-dimensional division currently taught to GPhys::NetCDF_IO.open -- if . parenthesis with required to enclose in a parenthesis the pattern which changes with files here is one, it means that it is 1-dimensional division

it demonstrates reading of a division file using the same data as the former and needs . -- the introduction file is divided first -- it will be read anew and a figure will be drawn

multiple_files.rb
require "numru/ggraph"include NumRugp = GPhys::NetCDF_IO.open('T.jan.nc', 'T') # GPhys::NetCDF_IO.write( f=NetCDF.create('tmp00.nc'), -gp[0..17,0..9,{0..6,6}] )f.closeGPhys::NetCDF_IO.write( f=NetCDF.create('tmp01.nc'), -gp[0..17,10..-1,{0..6,6}])f.closeGPhys::NetCDF_IO.write(-f=NetCDF.create('tmp10.nc'),  gp[18..-1,0..9,{0..6,6}])f.closeGPhys::NetCDF_IO.write (f=N) etCDF.create('tmp11.nc'), gp[18..-1,10..-1,{0..6,6}])f.close # files = /tmp(\d)(\d).nc/p gpcompo = GPhys::NetCDF_IO.open( files, 'T' ) # DCL.gropn(1)DCL.sgpset('lcntl', false) ; DCL.uzfact(0.7)DCL.sldiv('y',2,1)GGraph.contour( gpcompo )GGraph.contour( gpcompo[false,1] )DCL.grcls # File.unlink('tmp00.nc')File.unlink('tmp01.nc')File.unlink('tmp10.nc')File.unlink('tmp11.nc') 

a top -- longitude and latitude -- being related -- respectively -- data -- 2 -- dividing -- tmp00.nc and tmp01. . stored in four files called nc, tmp10.nc, and tmp11.nc -- it -- a regular expression -- using /tmp(\d)(\d).nc/ it specifies in the form to say -- a GPhys object gpcompo を 開いている. . as which a temperature distribution of all balls will be displayed as follows if the result is illustrated -- the 2nd of the figures under . which is starting only the 7th layer from under the lowest layer about the 3rd dimension (pressure) in addition it is the figure of 70 mb -- cautions



GGraphを使いこなす(その1)


再び可視化の話題に戻る。

とりあえず、

line_4.rb
 
1: require "numru/ggraph"
2: include NumRu
3: gphys = GPhys::NetCDF_IO.open('T.jan.nc', 'T')
4: DCL.gropn(1)
5: DCL.uzfact(0.7)
6: GGraph.set_fig( 'itr'=> 2 )
7: GGraph.line( gphys.cut(135,35,false), true, 'exchange'=>true, 'annot'=>false )
8: DCL.grcls

ここではオプション 'annotate' を false にした。オプション名は、前方一致で後 ろを省略できる(勿論2つ以上のオプション名にマッチするような省略はできない)。


line_4.rb実行結果

描画メソッドのオプションの仕組み


これまで、描画メソッドのオプションが幾つか出てきたが、 'exchange'=>true といった書式に戸惑ったかもしれないので、解説する。 上のプログラムにおける
 
GGraph.line( gphys.cut(135,35,false), true, 'exchange'=>true, 'annot'=>false )

は、
 
GGraph.line( gphys.cut(135,35,false), true, {'exchange'=>true, 'annot'=>false})

の省略形である(Rubyの文法上そうなっている)。そして、
 
{'exchange'=>true, 'annot'=>false}

は、連想配列(Hash)の定義を意味する。つまり、 引数の最後に key => value を連ねたものは、連想配列の定義と 見なされるのである。 上の例では、文字列 'exchange' に対し true を、文字列 'annot' に対し false を割り当てる連想配列が作られる。 というわけで、下のように分解すると分かりやすいであろう。
 
hash = {'exchange'=>true, 'annot'=>false}
GGraph.line( gphys.cut(135,35,false), true, hash )
つまり、GGraph.line の3番目の引数に連想配列を与えているだけで、 キーワード引数(順番でなく名前で識別する引数)を使っているの ではない。Ruby の文法にはキーワード引数は存在しないが、 連想配列を使って
 
GGraph.line( gphys.cut(135,35,false), true, 'exchange'=>true, 'annot'=>false )
のように表現できるということで、実質的にはキーワード引数が使えるように なっているのである。


連想配列 Hash においては、「キー」は完全一致でなければならないが、 GGraph では前方一致で済むよう工夫している。それを支えるのは、 GPhys 本体ではなく Misc ライブラリー に含まれている、KeywordOpt 並びにそのサブクラス である KeywordOptAutoHelp である。Miscライブラリーは、Rubyプログラミン グ全般を支援する、一般性の高いライブラリーを集めたものである。

オプションのデフォルト値を知る


各メソッドのオプションのデフォルト値は、 GGraph マニュアル に載っているが、以下のようにも調べられる。 ここでは、Ruby の対話シェル irb を使おう。以下で irb(main):001:0> 等は irb のプロンプトである。プロンプトのない行は、irb による出力である。


 
% irb
irb(main):001:0> require "numru/ggraph"
=> true
irb(main):002:0> include NumRu
=> Object
irb(main):003:0> GGraph.line( nil, true, 'help'=>true )
<< OF DESCRIPTION OPTIONS> >
  option name   default value   # description:
  "title"       nil     # Title of the figure(if nil, internally
                        # determined)
  "annotate"    true    # if false, do not put texts on the right
                        # margin even when newframe==true
  "exchange"    false   # whether to exchange x and y axes
  "index"       1       # line/mark index
  "type"        1       # line type
  "help"        false   # show help message if true
 Current values={"annotate"=>true, "help"=>true, "title"=>nil, "type"=>1, "exchange"=>false, "index"=>1}
NumRu::Misc::HelpMessagingException: ** help messaging done **
        from /usr/local/lib/ruby/site_ruby/1.6/numru/misc/keywordopt.rb:343:in `interpret'
        from /usr/local/lib/ruby/site_ruby/1.6/numru/ggraph.rb:1232:in `line'
        from (irb):3
irb(main):004:0>

上では、まずお約束の2行( require "numru/ggraph" include NumRu )を打ち込んだ後、 GGraph.line を オプション 'help'=>true を指定して実行している。すると、 その下のに示されたようなメッセージが表示される。このとき実行は中断され、 描画は行われないので、データはダミーで良い。上では、nil を与えている ( GGraph.line の第1引数)。


それでは引き続き、他の描画メソッドのオプションを表示してみよう。


 
irb(main):004:0> GGraph.contour( nil, true, 'help'=>true )
<< OF DESCRIPTION OPTIONS> >
  option name   default value   # description:
  "title"       nil     # Title of the figure(if nil, internally
                        # determined)
  "annotate"    true    # if false, do not put texts on the right
                        # margin even when newframe==true
  "min" nil     # minimum contour value
  "max" nil     # maximum contour value
  "nlev"        nil     # number of levels
  "interval"    nil     # contour interval
  "nozero"      nil     # delete zero contour
  "coloring"    false   # set color contours with ud_coloring
  "clr_min"     13      # (if coloring) minimum color id
  "clr_max"     100     # (if coloring) maximum color id
  "help"        false   # show help message if true
  "levels"      nil     # contour levels (Array/NArray of Numeric)
  "index"       nil     # (if levels) line index(es) (Array/NArray of
                        # integers, Integer, or nil)
  "line_type"   nil     # (if levels) line type(s) (Array/NArray of
                        # integers, Integer, or nil)
  "label"       nil     # (if levels) contour label(s) (Array/NArray of
                        # String, String, true, false, nil). nil is
                        # recommended.
  "label_height"        nil     # (if levels) label height(s) (Array/NArray
                        # of Numeric, Numeric, or nil). nil is recommended.
 Current values={"label_height"=>nil, "annotate"=>true, "help"=>true, "title"=>nil, "coloring"=>false, "max"=>nil, "nlev"=>nil, "line_type"=>nil, "min"=>nil, "clr_max"=>100, "index"=>nil, "levels"=>nil, "interval"=>nil, "nozero"=>nil, "label"=>nil, "clr_min"=>13}
NumRu::Misc::HelpMessagingException: ** help messaging done **
        from /usr/local/lib/ruby/site_ruby/1.6/numru/misc/keywordopt.rb:343:in `interpret'
        from /usr/local/lib/ruby/site_ruby/1.6/numru/ggraph.rb:1350:in `contour'
        from (irb):4
irb(main):005:0> 

この結果からわかるように、 実行中断は NumRu::Misc::HelpMessagingException という例外を上げることで行われる。irb を使っている場合、例外が上がると 制御がユーザーに戻り次のプロンプトが表示される。プログラムをファイルに する場合、通常は例外が上がると実行が中断する。中断させたくない場合、 下のように begin 〜 rescure 〜 end 節で保護すればよい。


help_ggraph.rb
 
require "numru/ggraph"
include NumRu
begin
  print "\n** optoions of  GGraph.line **\n"
  GGraph.line( nil, true, 'help'=>true )
rescue
end
begin
  print "\n** optoions of  GGraph.contour **\n"
  GGraph.contour( nil, true, 'help'=>true )
rescue
end

実行結果は irb の場合と同様なので省略する。 helpオプションを付けられるその他のメソッドは、 GGraph.fig , GGraph.axes , GGraph.mark , GGraph.tone である。



GGraphを使いこなす(その2)


上記の help メッセージ表示で分かるように、 GGraph の描画メソッドには様々なオプションがある。 オプションによる指定はその時に限りだが、 デフォルト値を変えることで、その後ずっと有効な設定もできる。 以下に例を示す。

コンター/トーン (レベル自動生成)


次のプログラムを実行しよう。

contour101.rb
 
 1: require "numru/ggraph"
 1: include NumRu
 2: gphys = GPhys::NetCDF_IO.open('T.jan.nc', 'T')
 3: 
 4: # 
 5: DCL.gropn(1)
 6: DCL.sldiv('y',2,2)           # 2x2に画面分割, 'y'=yoko: 左上→右上→左下...
 7: DCL.sgpset('lcntl', false)   # 制御文字を解釈しない
 8: DCL.sgpset('lfull',true)     # 全画面表示
 9: DCL.uzfact(0.75)             # 座標軸の文字列サイズを 0.75 倍
10: DCL.sgpset('lfprop',true)    # プロポーショナルフォントを使う
11: 
12: # 
13: GGraph.set_fig('viewport'=>[0.15,0.75,0.15,0.6])    # set_*: ずっと有効な設定
14: # 1枚目
15: GGraph.contour( gphys )
16: GGraph.tone( gphys, false, 'ltone'=>false )        # ゼロ以下にシェーディング
17: # 2枚目
18: GGraph.next_fig('itr'=>2 )                         # next_*: 次だけ有効な設定
19: GGraph.contour( gphys.average(0), true, 'color'=>true )        # 虹色コンター
20: # 3枚目
21: GGraph.set_axes('xunits'=>'','yunits'=>'')  # 空文字列 --> 軸の単位は書かない
22: GGraph.tone( gphys.cut(true,true,70) )        # tone のデフォルトはカラー表示
23: GGraph.contour( gphys.cut(true,true,70), false )  # 第2引数 false -> 重ね書き
24: # 4枚目
25: GGraph.set_linear_contour_options( 'min'=>0, 'nlev'=>20 )  
26:                  # contourのデフォルト変更. 次回のみ有効なnext_linear..もある
27: GGraph.contour( gphys.average(0) )
28: 
29: #<おしまい> 
30: DCL.grcls

実行すると、下のようにコンター及び色塗りの図が4枚表示されるは ずである。 各行の解説はプログラム中に書いてあるのであまり必要なかろう。 ここでは一部だけ解説する。 第16行目では 'ltone'=>false により、 値がゼロ以下の領域に対するシェーディングとなる。 これは DCL のトーンライブラリー UEPACK のオプションそのままである。 但し、UEPACK では 'ltone' == false がデフォルトであるのに対し、 GGraph では 'ltone' == true をデフォルトにしている。


さらに、全般にかかわることを補足する。GGraphでは、一般に描画メソッド hogehoge に対し、オプションのデフォルト値を変更する set_ hogehoge が存在する。また、オプションとするには 長すぎたり、あるいはオプションを使うこと自体が好みでない人のために、 next_ hogehoge というメソッドが用意してあり、 次回の hogehoge のみに影響を与える (つまり結果は hogehoge にオプションを与えるのと同じ)。 ただし、 contour の場合、自動レベル生成に関わる (set|next)_linear_contour_options と、レベルを陽に指定する (set|next)_contour_levels に分かれている。色塗り/シェーディング を行う tone についても同様。


contour101.rb実行結果


contour では、オプション coloring を設定すると、カラーコンターを生成する。その場合、 オプションでコンターの最小値と最大値に割り当てる色を指定できる ( clr_min clr_max )。


コンター/トーンレベルは、次項で説明する明示的なレベル指定を行わなければ、 等間隔に生成される。 その際、完全お任せも出来るし、(1)最小値( min )、 (2)最大値( max )、(3)間隔( interval )または本数 ( nlev )、の3つのパラメターの うち任意個を指定して制御することもできる。 間隔と本数の両方を指定すると、後者は無視される。 最終的な生成は DCL.udgcl[ab] , DCL.uegtl[ab] に任されるので、指定した最大/最小値は切りのよい値に変換される。 当然、 icycle などの (UD|UE)PACK のパラメターも解釈される。 tone においてシェーディングを行うために、オプション ltone false にすると、単にレベル生成をやめるだけ なので、実はもしも UEPACK のパラメター ltone true になっていれば、やはりカラートーンが表示されることになる (という注意を喚起するため、あえてこの分かりにくい名前をそのまま採用 している)。 contour に限り、値がゼロのコンターを取り除くオプションがある ( nozero )。


圧力軸だけは値の大きいほうが左/下に来るのはなぜ?


圧力は確かに大きいほど高度が低いので、そうなると好都合ではあるが、 何も指定しないのにそうなるのは何か不透明な操作が行われているからであろうか? 実は、そうすべきということは、元の NetCDF ファイルに書いてあるので、 それを解釈しているまでである。 T.jan.nc の ncdump による表示 をみると、変数 level にだけ、 positive 属性があり、値が "down" となっている。 これは、NetCDF ユーザーガイドにこそないものの、 多くのコンベンションで標準的な属性となっている ( gtool4 NetCDF 規約 を参照のこと)。


では、 positive 属性を付けていないデータはどうすればい いのだろうか。GGraph はそれも配慮して作られているので大丈夫である。 具体的には、メソッド fig のパラメターのデフォルト値を変えればよい。


まずは次の、たった2行のプログラムを実行してみよう。 (わざわざファイルにしなくても irb で十分である)


fig_help.rb
 
require "numru/ggraph"
NumRu::GGraph.fig( nil, true, 'help'=>true )

すると、以下のようにオプションのメニューが表示される。


 
% ruby fig_help.rb
<< OF DESCRIPTION OPTIONS> >
  option name   default value   # description:
  "new_frame"   true    # whether to define a new frame by DCL.grfrm
                        # (otherwise, DCL.grfig is called)
  "window"      nil     # [uxmin, uxmax, uymin, uymax]
  "viewport"    [0.2, 0.8, 0.2, 0.8]    # [vxmin, vxmax, vymin, vymax]
  "itr" 1       # coordinate transformation number
  "xreverse"    "positive:down,units:hPa"       # Assign max value to
                        # UXMIN and min value to UXMAX if condition is
                        # satisfied (nil:never, true:always, String: when
                        # an attibute has the value specified
                        # ("key:value,key:value,..")
  "yreverse"    "positive:down,units:hPa"       # Assign max value to
                        # UYMIN and min value to UYMAX if condition is
                        # satisfied (nil:never, true:always, String: when
                        # an attibute has the value specified
                        # ("key:value,key:value,..")
  "help"        false   # show help message if true
 Current values={"help"=>true, "window"=>nil, "new_frame"=>true, "itr"=>1, "yreverse"=>"positive:down,units:hPa", "viewport"=>[0.2, 0.8, 0.2, 0.8], "xreverse"=>"positive:down,units:hPa"}
/usr/local/lib/ruby/site_ruby/1.8/numru/misc/keywordopt.rb:343:in `interpret': ** help messaging done ** (NumRu::Misc::HelpMessagingException)
        from /usr/local/lib/ruby/site_ruby/1.8/numru/ggraph.rb:1114:in `fig'
        from fig_help.rb:2

図の左右上下の端の座標値 ( uxmin, uxmax, uymin, uymax ) はオプション window で決まるので、座標軸の左右/上下を入れ替 えたければ、これを陽に設定するというのが一法である。 しかし、それでは様々な図を書きたい場合は不便である。 そこで、 window を設定せずに、自動判断の規則を変えよう。 それを行うのが、オプション xreverse , yreverse である。 上の出力から、デフォルト値がともに "positive:down,units:hPa" となっていることがわかる。これは、属性 positive が存在し てその値が "down" であるか、または 属性 units が存在してその値が "hPa" である場合は、左右または上下を逆にするという意味である。 とうことで、実はやっぱり圧力を特別扱いしていたのであるが、 今回用いたデータでは level の単位は hPa でなく milibarとなっているので、 それが原因ではない。


デフォルト値を例えば "units:mb,units:hPa,units:milibar" に変えれば、 属性 units が "mb", "hPa", "milibar" のいずれかの場合に引っくり返すと いう設定になる:
 
GGraph.set_fig( "xrev"=>"units:mb,units:hPa,units:milibar",
                "yrev"=>"units:mb,units:hPa,units:milibar")

一方、圧力を特別視せず、純粋に postive だけで勝負したければ、 "positive:down" とすれば良い。 なお、 xreverse , yreverse nil を設定すれば常に引っくり返しを行わず、 true とすれば常に引っくり返す。 といっても勿論 window が陽に指定されれば、それに従う。


トーンレベルのユーザー指定


GGraphでは、コンターやトーン(色塗り)のレベルを配列で陽に指定することが出来る。


トーンはレベルとパターン番号で指定される。パターン番号は色とトーンパター ンを同時に指定する。その仕様は RubyDCLのマニュアル (GRPH1の「概要」)を参照のこと。 GGraph では、トーンは GGraph.set_tone_levels で設定するか(以後継続的に用いる場合)、 GGraph.tone のオプション 'levels' 'patterns' により指定する(その場限り)。 (なお、現在の実装では、後者を使うとそれ以前の GGraph.set_tone_levels は無効になるが、 そうならないように改訂する予定)

それでは、やり方を例で示す。

tone201.rb
 
 1: require "numru/ggraph"
 2: include NumRu
 3: gphys = GPhys::NetCDF_IO.open('T.jan.nc', 'T')
 4: 
 5: # 
 6: DCL.gropn(1)
 7: DCL.sldiv('y',2,2)           # 2x2に画面分割, 'y'=yoko: 左上→右上→左下...
 8: DCL.sgpset('lcntl', false)   # 制御文字を解釈しない
 9: DCL.sgpset('lfull',true)     # 全画面表示
10: DCL.sgpset('lfprop',true)    # プロポーショナルフォントを使う
11: 
12: # 
13: GGraph.set_fig('viewport'=>[0.15,0.82,0.15,0.6])
14: # 1枚目
15: GGraph.set_tone_levels( 'levels'=>[-20,-15,-10,-5,0],
16:                         'patterns'=>[10999,20999,30999,40999] )
17: GGraph.tone( gphys )
18: # 2枚目
19: GGraph.tone( gphys, true, 'lev'=>[-20,0,20],     # レベル&パターンを陽に指定
20:              'pat'=>[20999,40999,70999,80999] ) # パタンの方が1つ多→±∞まで
21: GGraph.contour( gphys, false, 'lev'=>[-20,0,20], 'index'=>3 )      # 参考まで
22: # 3枚目
23: GGraph.tone( gphys, true, 'lev'=>[-20,0,20], 
24:                     'pat'=>[40999,70999,80999] ) # レベルと同数→+∞まで拡張
25: # 4枚目
26: GGraph.tone( gphys, true, 'lev'=>[-20,0,20], 
27:                     'pat'=>[40999,70999] )    # パタンのほうが1つ少→間を塗る
28: #<おしまい> 
29: DCL.grcls

15行目より解説する。15〜16行目では、トーンのレベルとパターン番号を設定している。 GGraph.set_tone_levels では、'levels' と 'patterns' の両方のパラメターを配列で指定する。 GGraph.tone や後述するコンターの場合と形式とあわせるためオプションの形を取っているが、 両方とも省略出来ない(つまり本当はオプションではない)。 レベルを設定するなら色も同時に指定しなければならない。 17行目は、直前の設定に従って1枚目の図を描画する。 19〜20行目では、レベルとパターンを GGraph.tone のオプションとして 陽に指定し、2枚目を描画している。 指定したパターンはレベルより要素が1つ多いが、 その場合、−∞, levels[0], levels[1], .., levels[-1], +∞ の間が塗られる。オプション名の後ろが省略出来るのは既に述べた通り。 なお、トーンレベルが分かりやすいようコンターを 重ね書きした。コンターのレベル設定については後述する。 23〜24行目は3枚目を描画する。レベルとパターンの要素数が等しいが、 この場合 levels[0], levels[1], .., levels[-1], +∞ の間が塗られる。 26〜27行目の4枚目では、パターンのほうが1つ要素が少ないので、 levels の間が塗られる。レベルとパターンの要素数の関係が これら以外の場合は例外が発生する。

tone201.rb実行結果


コンターレベルのユーザー指定


コンターレベルを配列で陽に指定する方法を説明する。 コンターの場合は、1本1本に線の太さ等、様々なパラメターがあり、 それらも決めねばならないが、 GGraph では、利用者の便を考えて可能な限り省略または簡略化できるようになっている。 トーンの場合と同様、レベルの設定は GGraph.set_contour_levels で設定する場合(以後継続的に用いる場合)と、 GGraph.contour のオプションで指定する場合 (その場限り) の2通りがある。指定するパラメターは、必ず必要な 'levels' に加えて、オプションで 'index' , 'line_type' , 'label' , 'label_height' である。 (これらは DCL.udsclv のパラメターである。 GGraph は、省略されたものを以下のように規則に則って決定し、 DCL.udsclv を呼ぶ。)


それでは、以下のプログラムを実行しよう。

contour201.rb
 
 1: require "numru/ggraph"
 2: include NumRu
 3: gphys = GPhys::NetCDF_IO.open('T.jan.nc', 'T')
 4: 
 5: # 
 6: DCL.gropn(1)
 7: DCL.sldiv('y',2,2)           # 2x2に画面分割, 'y'=yoko: 左上→右上→左下...
 8: DCL.sgpset('lcntl', false)   # 制御文字を解釈しない
 9: DCL.sgpset('lfull',true)     # 全画面表示
10: DCL.sgpset('lfprop',true)    # プロポーショナルフォントを使う
11: 
12: # 
13: GGraph.set_fig('viewport'=>[0.15,0.82,0.15,0.6])
14: levels = 5*( NArray.float(7).indgen! )                # NArray: [0,5,10,...]
15: mj = DCL.udpget('indxmj')
16: mn = DCL.udpget('indxmn')
17: # 1枚目
18: GGraph.set_contour_levels( 'levels'=>levels, 'index'=>mj )    # コンター設定
19: GGraph.contour( gphys )
20: # 2枚目
21: GGraph.contour( gphys, true, 'lev'=>levels, 
22:                 'index'=>[mj,mn], 'line_type'=>[1,2,2,2] )
23: # 3枚目
24: GGraph.contour( gphys, true, 'lev'=>levels, 
25:                 'index'=>mn, 'label'=>true )
26: # 4枚目
27: GGraph.contour( gphys, true, 'lev'=>levels, 
28:                 'index'=>mn, 'label'=>['A','B','C','D'],
29:                 'label_height'=>[0.015,0.02,0.025,0.03] )
30: #<おしまい> 
31: DCL.grcls

14行目では、中身が [0,5,10,...] である NArray の1次元配列を生成している。 indgen! は中身を [0,1,2,..] で置き換えるメソッドで、NArray では良く使われる。 コンターにラベルを書くかどうかは、デフォルトでは線の太さ (index) が、 UDPACK のパラメター 'indxmj' に等しいかどうかで判断している (初期値は3)。 18行目ではオプション 'index' にそれを指定してるので、 19行目で書かれるコンターには、コンターレベル値を示すラベルが付いている。 'index' 等のオプションでは、スカラーが指定されれば全てのコンター にその値を適用するようになっている。

2枚目の図以降は、contour のオプションで陽にレベル等を指定して描画 している。2枚目では、index は [mj, mn] と配列で与えられている。 その場合、[mj, mn, mj, mn,...] という繰り返しと解釈される。 他のオプションについても同様である。line_type は [1,2,2,2] なので、 [1,2,2,2, 1,2,2,2,..] と解釈される。 3枚目の図では、label を true と指定してるので、全てのコンターにラベル を書く。4枚目ではラベルの文字列を陽に指定している (labelは true ならコンターレベルを表す文字列を内部で生成して書くが、 文字列が与えられた場合はそれを使う)。4枚目では、label_height により 文字列の大きさを個々に設定している。しかし、普通は一律に変えたいだろう。 その場合 label_height を使わず、UDPACK のパラメター RSIZEL を変えることを勧める。

contour201.rb実行結果


レイアウトに凝る


投稿論文に出す清書用の図では、レイアウトに凝りたいだろう。 GGraphは、DCLの強力なレイアウト機能を活かし易いようになっている。


次の例では、複数のパネルから必要ない座標軸タイトル&ラベルを 落してすっきりさせ、またスペースを節約する。 スペース節約は, DCL.sldiv を使わず, 1フレーム中に複数の図を配置することで行う。 そのために各図のビューポートを陽に設定し、 さらにタイトルの書き方も細かく調整しているので、 ソースは長くなっている。 各々のコマンドに関しては RubyDCL のマニュアルを参照のこと。

layout1.rb
 
require "numru/ggraph"
include NumRu
gphys = GPhys::NetCDF_IO.open('T.jan.nc', 'T')

DCL.gropn(1)
DCL.sgpset('lcntl', false)   # 制御文字を解釈しない
DCL.sgpset('lfull',true)     # 全画面表示
DCL.sgpset('lcorner',false)  # コーナーマークを書かない
DCL.uzfact(0.35)             # 座標軸の文字列サイズを定数倍
DCL.sgpset('lfprop',true)    # プロポーショナルフォントを使う
DCL.udpset('lmsg',false)     # コンター間隔非表示

vpt = NArray[0.05, 0.45, 0.05, 0.25]             # ビューポートサイズ (2:1)
vpt00 = ( vpt + ([0.050]*2 + [0.32]*2) ).to_a    # x,y方向にずらしてArray化
vpt01 = ( vpt + ([0.474]*2 + [0.32]*2) ).to_a    # x,y方向にずらしてArray化
vpt10 = ( vpt + ([0.050]*2 + [0.10]*2) ).to_a    # x,y方向にずらしてArray化
vpt11 = ( vpt + ([0.474]*2 + [0.10]*2) ).to_a    # x,y方向にずらしてArray化

GGraph.set_fig('viewport'=>vpt00)
GGraph.set_axes('xunits'=>'','yunits'=>'','xtitle'=>'') 
DCL.uzpset('labelxb',false)
GGraph.contour( gphys.cut(true,true,1000), true, 'annot'=>false, 'titl'=>'' )
DCL.uzpset('pad1',0.2) ; DCL.uxsttl('t','1000 hPa',-1) ; DCL.uzpset('pad1',0.7)

GGraph.set_fig('viewport'=>vpt01, 'new_frame'=>false)
GGraph.set_axes('ytitle'=>'')
DCL.uzpset('labelyl',false)
GGraph.contour( gphys.cut(true,true,250), true, 'annot'=>false, 'titl'=>'' )
DCL.uzpset('pad1',0.2) ; DCL.uxsttl('t','250 hPa',-1) ; DCL.uzpset('pad1',0.7)

GGraph.set_fig('viewport'=>vpt10, 'new_frame'=>false)
GGraph.set_axes('ytitle'=>nil,'xtitle'=>nil)
DCL.uzpset('labelyl',true); DCL.uzpset('labelxb',true)
GGraph.contour( gphys.cut(true,true,70), true, 'annot'=>false, 'titl'=>'' )
DCL.uzpset('pad1',0.2) ; DCL.uxsttl('t','70 hPa',-1) ; DCL.uzpset('pad1',0.7)

GGraph.set_fig('viewport'=>vpt11, 'new_frame'=>false)
GGraph.set_axes('ytitle'=>'')
DCL.uzpset('labelyl',false)
GGraph.contour( gphys.cut(true,true,10), true, 'annot'=>false, 'titl'=>'' )
DCL.uzpset('pad1',0.2) ; DCL.uxsttl('t','10 hPa',-1) ; DCL.uzpset('pad1',0.7)

DCL::sgtxzv(0.5,vpt00[3]+0.028,'January Monthly Mean Temperature',
	    1.15*DCL.uzpget('rsizec2'),0,0,3)

DCL.grcls

print "\n** PRESSURE LEVELS ** " ; p gphys.coord(2).val

layout1.rb実行結果


座標軸に凝る


GGraph は座標軸描画を DCL の USPACK に任せる( DCL.usxaxs , DCL.usyaxs を呼ぶ)。では、日付軸を付けたり(UCPACKを利用)、U[XY]PACK を使ってより細かく座標軸を制御するにはどうすればいいのだろうか? その答えは、今のところ、GGraph に書かせず自分で書く、である。 GGraph で軸を書くメソッド axes が描く軸は、 以下のようにオプション 'xside' , 'yside' で制御できる。例えば X 軸を下だけ描くなら、 'xside'=>'b' である。デフォルトの初期値は 'xside'=>'tb' , 'yside'=>'lr'

axes1.rb
 
require "numru/ggraph"
include NumRu
gphys = GPhys::NetCDF_IO.open('T.jan.nc', 'T')
DCL.gropn(1)
DCL.sgpset('lfull',true) ; DCL.uzfact(0.6)
GGraph.set_fig( 'itr'=> 2, 'viewport'=>[0.25,0.7,0.15,0.6] )
GGraph.set_axes( 'xside'=>'b', 'yside'=>'l' )
GGraph.line( gphys.mean(0,1), true, 'exchange'=>true, 'annot'=>false )
DCL.grcls

axes1.rb実行結果


GGraph を使って書かなかったところに UCPACK 等で後から座標軸を書けば良い。



遠隔データにアクセスする/させる


GPhys は、ローカルなディスク上のファイルだけでなく、遠隔サーバーに 置いたデータにアクセスするための枠組み作りに役だつように出来ている。 以下では、CGI についてごく簡単に触れた後に、dRuby という Ruby における分散オブジェクトライブラリーを利用する例を示す。


CGI


eRuby 等のライブラリーを用いると、cgi を Ruby で書くことができる。 これまで示してきたように、 GGraph ならデータの様々な断面や平均の描画はごく簡単な 命令で出来る。よって、これを使えば、データを可視化する cgi スクリプト作りにおいては、 如何に良いユーザーインターフェースを提供するかに注力できるであろう。


# 筆者には CGI のチュートリアルは書けそうにないので、 書いてくれる人を募集します。


dRubyを使ったクライアント&サーバー(その1)


dRuby は、Rubyのメソッド呼出しを拡張し、ネットワーク越しにメソッド呼出し を行えるようにしたライブラリーである。Ruby 1.8 には、標準添付され ている。Ruby 1.6 まででもダウンロードしてインストールすれば良い。 dRuby を使うとリモートプロセス上のオブジェクトへのアクセスが、 ローカルなオブジェクトへのアクセスと同じようにできる。 通信相手が動いているマシンの OS 等が異なっても問題ない。 ...などと言われても、どう役に立つのかピンと来ないかもしれないので、例で示す。


以下はごく簡単なサーバープログラムである。これまで使ってきた T.jan.nc 中の気温データをリモートプロセスに提供する。 もしも実用的なサーバーを作るなら、複数のデータを提供し、まずどれを使うかを選ばせるであろうが、ここでは dRuby の可能性を示すことを主眼に、 データ決め打ちの簡単な例とする。

druby_serv1.rb
 
1: require "drb/drb"
2: require "numru/gphys"
3: include NumRu
4: gp = GPhys::NetCDF_IO.open("T.jan.nc","T")
5: DRb.start_service(nil, gp)
6: puts 'URI: '+DRb.uri
7: puts '[return] to exit'
8: gets

1行目は dRuby のロードである。2〜4行目はお馴染。ここでは、 ファイル T.jan.nc 中の変数 T から GPhys オブジェクトを構成し、 変数 gp に割り当てている。その次から、いよいよ dRuby の登場である。 5行目では先に作った GPhys オブジェクトを「フロントエンド」として dRuby サービスを起動している。すると、別のプロセスがアクセスしてきたと きにまず最初に返すのが、この GPhys オブジェクトへの参照となる。 DRb.start_service の第1引数を nil にしているので、 通信に使うポートは自動的に決められる(ポートを指定することも出来る)。 6行目では、そのポート番号を含む、 アクセスのための URI (URL) を表示する。プロセスを終了させないように、 8行目の gets で入力待ちに入る。改行キーを押せば終了するので、7行目では '[return] to exit' と表示する。それではまず、 このサーバーを走らせてみよう。
 
% ruby druby_serv1.rb
URI: druby://horihost:45461
[return] to exit

表示された URI は、プロトコルが druby で、ホスト名が horihost、 割り当てられたポート番号が 45461 であることを意味する。 当然ホスト名はマシン依存であるし、 ポート番号は(自動生成したので)実行の度に変りうる。

次に、このサーバーに接続するクライアントの簡単な例を示す。

druby_cli1.rb
 
1: require "drb/drb"
2: DRb.start_service
3: uri = ARGV.shift || raise("Usage: % #{$0} uri")
4: gp = DRbObject.new(nil, uri)
5: p gp.class
6: p gp.name
7: p gp.rank
8: p gp.shape
9: print gp.coord(2).name,"\n"

プログラムを解説する前に、これを実行した場合の結果を示そう。 先ほどサーバーの URI が druby://horihost:45461 と出たので、 それにアクセスすべく、次のように走らせる。
 
% ruby druby_cli1.rb druby://horihost:45461
DRb::DRbObject
"T"
3
[36, 19, 9]
level

さて、ソースと実行結果を解説する。 ソースの1行目はサーバーと同じである。2行目では DRb.start_service を呼んでいる。引数なしの場合は、フロントエンドオブジェクトなしである。 他のプロセス から の接続を受けないクライアントとしてはこれで良い。 3行目では、実行時に与えた引数を読み込んでいる。 || 以下は、 もしも引数がなかった場合に例外を上げて実行を止める。上の実行例では 引数は druby://horihost:45461 なので、これが文字列として 変数 uri に代入される。4行目では、 DRbObject.new(nil, uri) により、この uri に対して接続を確立する。その結果、 サーバーにおけるフロントエンドオブジェクトへの参照用オブジェクトが 作られ、変数 gp に代入される。従って、ここでは gp は、サーバーが開いた気温データ(GPhys オブジェクト)を代表することになる。 5行目以降では、このフロントエンドに対し、 様々なメッセージを送り付け、その結果を標準出力に出力する。 ソースと出力を見比べてみよう。5行目では gp にクラスを問合わせている。メソッド class はローカルに処理され、dRuby のリモートオブジェクトであることを示す DRb::DRbObject が表示される(class は全てのオブジェクトが持っているので、 DRb::DRbObject も当然持っている)。 6行目以降の、name 等のメソッドは、 DRb::DRbObject にはないので、サーバーに送られて実行され、 その結果がクライアントに通信される。 このようにして、 名前が "T" の3次元データであることなどが明らかになるのである。 なお、クライアントプログラムでは GPhys ライブラリーを ロードしていないことに注意せよ。クライアントが必要なのが GPhys オブジェクトに対する参照だけである場合は、必要ないのである。

以上より、dRuby を使うと、リモートプロセス内のオブジェクトに、 自プロセス内のオブジェクトと同じようにアクセス出来ることがわかったであ ろう。なお、上のサーバーとクライアントを一つにまとめて同じことをさせる(つまり分散処理しない)と、 以下のようになる。サーバーとクライアントの、 それぞれ前半と後半を繋いだだけであることが分かるであろう。

druby_self-contained1.rb
 
1: require "numru/gphys"
2: include NumRu
3: gp = GPhys::NetCDF_IO.open("T.jan.nc","T")
4: p gp.class
5: p gp.name
6: p gp.rank
7: p gp.shape
8: print gp.coord(2).name,"\n"

実行結果は以下の通り。出力されるクラス名が GPhys になった 以外は全く同じである。
 
% ruby druby_self-contained1.rb 
NumRu::GPhys
"T"
3
[36, 19, 9]
level

dRubyを使ったクライアント&サーバー(その2)


上の例を発展させて、データの図を書いてみよう。 データを持つのはサーバー、図を書くのはクライアントである。 では、まずサーバープログラムを示す。 描くための配列データ(NArray)は、 クライアントにおいて DCL に渡す必要があるが、実は NArray はそのままでは dRuby で送れない。 よって、NArray を拡張して送れるようにする必要があり、若干行が増える。 以下のソースで、 5〜8 行目がそれである。ちなみにこれは組み込みのモジュール Marshal に NArray を対応させるための拡張である。 この追加以外は druby_serv1.rb と同じ。よって、実行方法も同じある。

druby_serv2.rb
 
 1: require "drb/drb"
 2: require "numru/ggraph"
 3: include NumRu
 4: 
 5: class NArray
 6:   def self._load(o); to_na(*Marshal::load(o)).ntoh; end
 7:   def _dump(limit); Marshal::dump([hton.to_s, typecode, *shape]); end
 8: end
 9: 
10: gp = GPhys::NetCDF_IO.open("T.jan.nc","T")
11: DRb.start_service(nil, gp)
12: puts 'URI: '+DRb.uri
13: puts '[return] to exit'
14: gets

さて、クライアントである。こちらも同様に NArray を拡張する。

druby_cli2.rb
 
 1: require "drb/drb"
 2: require "numru/ggraph"
 3: include NumRu
 4: 
 5: class NArray
 6:   def self._load(o) to_na(*Marshal::load(o)).ntoh end
 7: end
 8: 
 9: DRb.start_service
10: uri = ARGV.shift || raise("Usage: % #{$0} uri")
11: gp = DRbObject.new(nil, uri)
12: DCL.gropn(1)
13: DCL.sldiv('y',2,1)
14: DCL.sgpset('lcntl',false)
15: DCL.uzfact(0.7)
16: GGraph.set_fig('viewport'=>[0.15,0.75,0.2,0.8])
17: GGraph.contour( gp.cut('level'=>100).copy )
18: GGraph.next_fig('itr'=>2)
19: GGraph.contour( gp.mean(0).copy )
20: DCL.grcls

サーバー側:
 
% ruby druby_serv2.rb
URI: druby://horihost:45469
[return] to exit

クライアント側:
 
% ruby druby_cli2.rb druby://horihost:45469
 *** MESSAGE (SWDOPN) ***  GRPH1 : STARTED / IWS =  1.      
 *** WARNING (STSWTR) ***  WORKSTATION VIEWPORT WAS MODIFIED.
 *** MESSAGE (SWPCLS) ***  GRPH1 : PAGE =   1 COMPLETED.
 *** MESSAGE (SWDCLS) ***  GRPH1 : TERMINATED.

druby_cli2.rb実行結果


先ほどの例と同様、クライアントでは、 ひと度サーバーのフロントオブジェクトを取得した後は、 データがローカルにあるのと同様に扱っている。 ただし、一点、17, 19 行目で copy という、 今まで出てこなかったメソッドを使っている点が異なる。 GPhys の copy メソッドは、オブジェクト全体のコピーを作成する(注※)。 copy(相手) のように、 コピー先を指定できるが、省略した場合はメモリー上に作成する (オブジェクトツリーをごっそり複製する、いわゆるディープクローンとなる)。 今の場合、変数 gp 配下のデータはファイルに収まったままになっているが、 copy によりメモリー上に読み込まれる。従って配列データは NArray に収まることになる。こうすると、GPhys オブジェクト全体が 転送可能となる。従って、 gp.cut('level'=>100).copy の戻り値は、 リモートオブジェクトへの「参照」でなく、その「複製」である。


(注※) GPhys-0.3.1 以降の版では、copy メソッドは必要なくなる予定である。 これが必要なのは GGraph において不要な型チェックをしていたからである。


このように参照と複製の区別をする必要がある場合はあるが、 とりえあず、描画命令に渡すものは複製すると覚えておけが良かろう。 ともかく、上の例よりリモートデータの可視化がごく簡単に実現できることが 分かっただろう。参考までに、上のサーバーとクライアントを一体化させた 非分散版のソースを示す。もはや copy は必要ないが、そのまま残した。 実行してできる図は勿論全く同じである。

druby_self-contained2.rb
 
require "numru/ggraph"
include NumRu
gp = GPhys::NetCDF_IO.open("T.jan.nc","T")
DCL.gropn(1)
DCL.sldiv('y',2,1)
DCL.sgpset('lcntl',false)
DCL.uzfact(0.7)
GGraph.set_fig('viewport'=>[0.15,0.75,0.2,0.8])
GGraph.contour( gp.cut('level'=>100) )
GGraph.next_fig('itr'=>2)
GGraph.contour( gp.mean(0).copy )
DCL.grcls

データベースとの連携


実用的なデータサーバーは、フロントエンドを特定の GPhys オブジェク トで固定せず、まずデータを選ばせ、指定されたデータを提供するだろう。 つまり、最初に検索というステップが入るのである。これは、なんらかの データベースと連携をとることに他ならない。


最初にデータを選ばせるサーバーの例として現在実装されているものに、 gphys-remote がある. 将来的には様々なデータベースとの連携が期待される.

注意


実用的なデータサーバーを作る場合、データを選ばせるであろうことは既に述 べた。さらに注意すべき点として、サーバー側でのゴミ集めに注意すべきこと が挙げられる。複数の利用者の同時アクセスを許すならば、さらに考慮すべき 点がいろいろあるだろう。また、転送するデータサイズを制限したいかもしれない。


dRuby はデータ転送を行うとき、Marshal という Ruby の組み込みライブラリー によって、オブジェクトをバイト列に変換する。Marshal のフォーマットは Ruby 1.6 → Ruby 1.8 で変更されたので、残念ながら、 通信は 1.6 同志または 1.8 同志に限られる。


以下は、サーバーにおけるサイズ制限の簡単な例である。 一定の長さ以上の配列はバイト列に変換できないので転送できない(参照渡しになる)。 さらに、ポート番号を利用者が指定できるようにしてある。

druby_serv2+.rb
 
require "drb/drb"
require "numru/gphys"
include NumRu

def usage
  print <<-EOS [PORT] RUNTIMEERROR HERE, #{$0} RUBY EOS NUMBER TO (INTEGER). PORT RAISE 数字であることを確かめる USAGE: % # (OPTIONAL) !="port" USAGE PORT.TO_I.TO_S IS ARGV.LENGTH END IF ASSIGN && THE>  0                   # --> 引数2個以上ならエラー

class NArray
  DUMP_SIZE_LIMIT = 40000
  def self._load(o) to_na(*Marshal::load(o)).ntoh end
  def _dump(limit) 
    if size <=DUMP_SIZE_LIMIT (#{SIZE}) OF URI: DRUBY://:'+PORT LARGE *SHAPE]) +DRB.URI ? SIZE GP="GPhys::NetCDF_IO.open("T.jan.nc","T")" : #{DUMP_SIZE_LIMIT})" TO MARSHAL::DUMP([HTON.TO_S, EXIT' ELSE" 

サーバー立ち上げ例:
 
% ruby druby_serv2+.rb
URI: druby://hori_host:45542
[return] to exit
 
% ruby druby_serv2+.rb 48080
URI: druby://hori_host:48080
[return] to exit


Copyright (C) 2003 GFD Dennou Club. All Rights Reserved.