IODA
02-Attributes.py
Go to the documentation of this file.
1 #
2 # (C) Copyright 2020 UCAR
3 #
4 # This software is licensed under the terms of the Apache Licence Version 2.0
5 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
6 #
7 
8 # This example shows how to manipulate Attributes and other metadata
9 # that describe Groups and Variables.
10 
11 # Attributes are metadata that help describe a Group or a Variable.
12 # Good examples of attributes include descriptive labels about
13 # the source of your data, a description or long name of a variable,
14 # and it's valid range (which is the interval where the data are valid).
15 #
16 # ioda's attributes are flexible. They can be single points, or they can
17 # be 1-D arrays. They may be integers, or floats, or doubles, strings,
18 # complex numbers, or really any type that you can think of. We will go
19 # through the attribute creation, listing, opening, reading and writing
20 # functions in this example.
21 #
22 # This example creates an HDF5 file, "Example-02.hdf5" using the HDF5 backend.
23 # This file may be viewed with the "h5dump" of "ncdump" commands. You can
24 # also use "hdfview" if you want a gui.
25 #
26 # Note also: This example introduces Attributes. Variables are very similar to
27 # this but have more features. Variables are the focus of the next few tutorials.
28 
29 import os
30 import sys
31 
32 if os.environ.get('LIBDIR') is not None:
33  sys.path.append(os.environ['LIBDIR'])
34 
35 import ioda
36 
37 # All of the attribute information for a Group or a Variable (see later
38 # tutorials) may be accessed by the ".atts" member object.
39 # i.e. g.atts;
40 
41 # ".atts" is an instance of the "Has_Attributes" class.
42 # This tutorial shows you how to *use* this object.
43 # If you want to *understand* how it is implemented, read the next paragraph.
44 # If not, then skip.
45 
46 # You can find Has_Attributes's definition
47 # in "include/ioda/Attributes/Has_Attributes.h". The class definition is not trivial.
48 # Has_Attributes is a derived class that inherits from detail::Has_Attributes_Base,
49 # which is a *template* class. If that's confusing, then just think that everything
50 # in Has_Attributes_Base is also available in Has_Attributes.
51 # We use this really weird pattern in writing the code because we share class
52 # structures between the frontend that you use (Has_Attributes) and the backends
53 # that implement the functionality (which all derive from Has_Attributes_Backend).
54 # When reading any of the ioda-engines header files, try to keep this in mind.
55 
56 # Since we just created a new, empty file, we don't have any attributes yet.
57 # Let's create some.
58 
60  name = "Example-02-python.hdf5",
61  mode = ioda.Engines.BackendCreateModes.Truncate_If_Exists)
62 
63 
64 # The fundamental creation function is .create(name, datatype, dimensions)
65 # What this does is is creates a new attribute, called "int-att-1".
66 # This attribute holds a single ({1}) integer (int32).
67 # The dtype parameter is one of several enumerated types in ioda.Types.
68 # These types match the types in C++. int32 is a 32-bit integer.
69 # Other types include float, double, int16, uint64, et cetera.
70 # The dims parameter is a list representing the dimensions of the attribute.
71 # Attributes can have a single dimension. Variables (discussed later)
72 # can be multidimensional.
73 # The function returns the new attribute. No data is yet written to this attribute.
74 intatt1 = g.atts.create(name="int-att-1", dtype=ioda.Types.int32, dims=[1])
75 # Write a single integer to the attribute using the writeDatum function.
76 # writeDatum writes just one value. If the attribute contains more than one
77 # value, then writeDatum will fail.
78 #
79 # Note the calling convention for the Python interface.
80 # [object].writeDatum.[type](). Python does not have C++'s expressive
81 # template support, so we have to define separate functions that
82 # implement a single C++ template (i.e. write<DataType>(...)).
83 # Additionally, Python types do not align perfectly with C++ types.
84 # So, we need to specify the C++ type, and the interface converts the
85 # values if necessary.
86 # write*.int32 means:
87 # first convert the data to int32_t,
88 # then write the data.
89 intatt1.writeDatum.int32(1)
90 
91 # Create another attribute. This time, it stores two values.
92 intatt2 = g.atts.create("int-att-2", ioda.Types.int32, [2])
93 # You can pass a list of values to write to the writeVector function.
94 # writeVector takes in any one-dimensional Python list and copies its
95 # data into the Attribute.
96 intatt2.writeVector.int32([1, 2])
97 
98 # Same as above.
99 intatt3 = g.atts.create("int-att-3", ioda.Types.int32, [3])
100 intatt3.writeVector.int32([1, 2, 3])
101 
102 # Create and write a C++-style float. A float has less precision than
103 # a double.
104 float1 = g.atts.create("float-1", ioda.Types.float, [2])
105 float1.writeVector.float([3.1159, 2.78])
106 
107 # Same for a double.
108 g.atts.create("double-1", ioda.Types.double, [4]).writeVector.double([1.1,2.2,3.3,4.4])
109 
110 # Write a variable-length, UTF-8 string.
111 # All strings in ioda are assumed to be this type of string.
112 g.atts.create("str-1", ioda.Types.str, [1]).writeVector.str(["This is a test."])
113 
114 # To list all attributes attached to an object (a Group or Variable),
115 # use the atts.list() function. This returns a Python list.
116 attlist = g.atts.list()
117 
118 if len(attlist) != 6:
119  raise Exception("Wrong size for g.atts.list!")
120 
121 # Opening an attribute
122 f1 = g.atts.open('float-1')
123 d1 = g.atts.open('double-1')
124 
125 # The dimensions of an attribute are set at creation time and are
126 # fixed. An attribute cannot be resized.
127 # This dimensions class is shared with variables, so not all members
128 # are relevant to attributes.
129 # The dimensions object has four members:
130 # 1) the overall dimensionality (for an Attribute, this is always one).
131 # 2) the current dimensions (a list)
132 # 3) the maximum dimensions (pertains only to resizable objects; a list)
133 # 4) the total number of elements (a scalar integer)
134 
135 f1_dims = f1.dims
136 
137 if f1_dims.dimensionality != 1:
138  raise Exception("Unexpected dimensionality")
139 
140 if f1_dims.dimsCur[0] != 2:
141  raise Exception("Unexpected size")
142 
143 if f1_dims.dimsMax[0] != 2:
144  raise Exception("Unexpected size")
145 
146 if f1_dims.numElements != 2:
147  raise Exception("Unexpected size")
148 
149 # To open an attribute:
150 a2_reopened = g.atts.open('int-att-2')
151 
152 # To check the storage type of an object:
153 # There are two very similar ways to do this, with isA and isA2.
154 # The differences are in the templating and the parameters.
155 # Prefer isA unless you are testing multiple types dynamically.
156 
157 if float1.isA.float() != True:
158  raise Exception("Unexpected type")
159 
160 if g.atts.open('int-att-1').isA2(ioda.Types.int32) != True:
161  raise Exception("Unexpected type")
162 
163 # Reading attributes:
164 # readDatum and readVector are complimentary calls to
165 # writeDatum and writeVector.
166 
167 # readDatum returns a single element.
168 # readVector returns a list.
169 
170 i2vals = a2_reopened.readVector.int32()
171 if len(i2vals) != 2:
172  raise Exception("Wrong length")
173 
174 # That's everything about attributes!
175 
IODA_DL Group createFile(const std::string &filename, BackendCreateModes mode, HDF5_Version_Range compat=defaultVersionRange())
Create a ioda::Group backed by an HDF5 file.
Definition: HH.cpp:113