File: voxet_reader.py

package info (click to toggle)
python-escript 5.6-10
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 144,304 kB
  • sloc: python: 592,074; cpp: 136,909; ansic: 18,675; javascript: 9,411; xml: 3,384; sh: 738; makefile: 207
file content (235 lines) | stat: -rw-r--r-- 9,430 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
##############################################################################
#
# Copyright (c) 2003-2018 by The University of Queensland
# http://www.uq.edu.au
#
# Primary Business: Queensland, Australia
# Licensed under the Apache License, version 2.0
# http://www.apache.org/licenses/LICENSE-2.0
#
# Development until 2012 by Earth Systems Science Computational Center (ESSCC)
# Development 2012-2013 by School of Earth Sciences
# Development from 2014 by Centre for Geoscience Computing (GeoComp)
#
##############################################################################

from __future__ import print_function, division

import os
from esys.downunder import CartesianReferenceSystem
from esys.escript import ReducedFunction, getMPIRankWorld

def readVoxet(domain, filename, voproperty=1, origin=None, fillValue=0.,
              referenceSystem=CartesianReferenceSystem()):
    """
    Reads a single property from a GOCAD Voxet file and returns a data
    object on the given domain with the property data.
    Restrictions:
    - Voxet origin in UVW space (i.e. AXIS_MIN) needs to be [0,0,0]
    - samples size must be 4 (float32) or 8 (float64)
    - data type must be IEEE
    - format must be RAW
    - domain resolution must be (approximately) a multiple of voxet resolution

    :param domain: the domain to use for data (must be a ripley domain)
    :type domain: `Domain`
    :param filename: Voxet header filename (usually ends in .vo)
    :type filename: ``string``
    :param voproperty: identifier of the property to read. Either the numeric
                       property ID, the property name, or the filename of the
                       property data.
    :type voproperty: ``int`` or ``string``
    :param origin: if supplied will override the Voxet origin as read from the
                   file.
    :type origin: ``list`` or ``tuple`` or ``None``
    :param fillValue: value to use for cells that are not covered by property
                      data (if applicable)
    :type fillValue: ``float``
    :param referenceSystem: coordinate system of domain. Used to scale vertical
                            axis accordingly
    :type referenceSystem: `ReferenceSystem`
    """
    from esys.ripley import readBinaryGrid, BYTEORDER_BIG_ENDIAN, DATATYPE_FLOAT32, DATATYPE_FLOAT64
    header=open(filename).readlines()
    if not header[0].startswith('GOCAD Voxet'):
        raise ValueError("Voxet header not found. Invalid Voxet file?!")
    NE=None
    axis_uvw=[None,None,None]
    axis_min=[0.,0.,0.]
    axis_max=[1.,1.,1.]
    # props[id]=[name,file,datatype]
    props={}
    for line in header:
        if line.startswith('AXIS_O '):
            if origin is None:
                origin=[float(i) for i in line.split()[1:4]]
        elif line.startswith('AXIS_U '):
            u=[float(i) for i in line.split()[1:4]]
            if (u[1] != 0) or (u[2] != 0):
                raise ValueError('This coordinate system is not supported')
            axis_uvw[0]=u[0]
        elif line.startswith('AXIS_V '):
            v=[float(i) for i in line.split()[1:4]]
            if (v[0] != 0) or (v[2] != 0):
                raise ValueError('This coordinate system is not supported')
            axis_uvw[1]=v[1]
        elif line.startswith('AXIS_W '):
            w=[float(i) for i in line.split()[1:4]]
            if (w[0] != 0) or (w[1] != 0):
                raise ValueError('This coordinate system is not supported')
            axis_uvw[2]=w[2]
        elif line.startswith('AXIS_MIN '):
            axis_min=[float(i) for i in line.split()[1:4]]
            if axis_min != [0,0,0]:
                raise ValueError('AXIS_MIN != [0,0,0] is not supported')
        elif line.startswith('AXIS_MAX '):
            axis_max=[float(i) for i in line.split()[1:4]]
        elif line.startswith('AXIS_N '):
            NE=[int(i) for i in line.split()[1:4]]
        elif line.startswith('PROPERTY '):
            propid=int(line.split()[1])
            if not propid in props:
                props[propid]=[None,None,None]
            props[propid][0]=line.split()[2].strip()
        elif line.startswith('PROP_ESIZE '):
            propid=int(line.split()[1])
            t=int(line.split()[2])
            if t==4:
                props[propid][2]=DATATYPE_FLOAT32
            elif t==8:
                props[propid][2]=DATATYPE_FLOAT64
            else:
                raise ValueError('Unsupported data size '+t)
        elif line.startswith('PROP_ETYPE '):
            t=line.split()[2].strip()
            if t != 'IEEE':
                raise ValueError('Unsupported data type '+t)
        elif line.startswith('PROP_FORMAT '):
            t=line.split()[2].strip()
            if t != 'RAW':
                raise ValueError('Unsupported data format '+t)
        elif line.startswith('PROP_OFFSET '):
            dataoffset=int(line.split()[2])
            if dataoffset != 0:
                raise ValueError('data offset != 0 not supported')
        elif line.startswith('PROP_FILE '):
            propid=int(line.split()[1])
            props[propid][1]=line.split()[2].strip()

    if (axis_uvw[0] is None) or (axis_uvw[1] is None) or (axis_uvw[2] is None)\
            or (NE is None) or (origin is None):
        raise ValueError('Could not determine data configuration. Invalid file?!')
    if len(props)==0:
        raise ValueError('No properties found.')

    # voxets have these conventions:
    # AXIS_N = number of samples (=cells!) in each dimension
    # AXIS_UVW * AXIS_MAX = voxet length in each dimension
    # AXIS_O = origin of voxet (cell centres!)
    # see also http://paulbourke.net/dataformats/gocad/gocad.pdf

    length = [axis_uvw[i]*axis_max[i] for i in range(3)]

    # modify length and origin to account for the fact that Voxet cells are
    # centred at the data points, i.e.:
    # BEFORE:                   AFTER:
    #
    #       O----length---->|        O------length------>|
    #      ___________________        ___________________
    #     | * | * | * | * | * |      | * | * | * | * | * |
    #      -------------------        -------------------

    for i in range(3):
        dz = length[i] / (NE[i]-1)
        origin[i] -= dz/2.
        length[i] += dz

    if referenceSystem.isCartesian():
        v_scale=1.
    else:
        v_scale=1./referenceSystem.getHeightUnit()

    origin[-1] = origin[-1]*v_scale

    # retrieve domain configuration so we know where to place the voxet data
    gridorigin, gridspacing, gridNE = domain.getGridParameters()

    # determine base location of this dataset within the domain
    first=[int((origin[i]-gridorigin[i])/gridspacing[i]) for i in range(domain.getDim())]

    # determine the resolution difference between domain and data.
    # If domain has twice the resolution we can double up the data etc.
    multiplier=[int(round((abs(length[i])/NE[i])/gridspacing[i])) for i in range(domain.getDim())]

    # NOTE: Depending on your data you might have to multiply your vertical
    # multiplier by 1000. to convert km in meters.
    #multiplier[-1] = int(multiplier[-1] * v_scale * 1000.)
    multiplier[-1] = int(multiplier[-1] * v_scale)

    datatype=None
    propfile=None
    for pid in props.keys():
        p=props[pid]
        if (isinstance(voproperty, int) and pid == voproperty) or \
           (isinstance(voproperty, str) and (p[0]==voproperty or p[1]==voproperty)):
            datatype=p[2]
            name=p[1]
            #remove quotes which GoCAD introduces for filenames with spaces
            if name.startswith('"') and name.endswith('"'):
                name=name[1:-1]
            propfile=os.path.join(os.path.dirname(filename), name)
            print("Voxet property file: %s"%propfile)
            break

    if propfile is None or datatype is None:
        raise ValueError("Invalid property "+str(voproperty))

    reverse=[0]*domain.getDim()
    if axis_uvw[-1] < 0:
        reverse[-1]=1

    print("calling readBinaryGrid with first=%s, nValues=%s, multiplier=%s, reverse=%s"%(str(first),str(NE),str(multiplier),str(reverse)))
    data=readBinaryGrid(propfile, ReducedFunction(domain), shape=(),
            fill=fillValue, byteOrder=BYTEORDER_BIG_ENDIAN,
            dataType=p[2], first=first, numValues=NE, multiplier=multiplier,
            reverse=reverse)

    return data


if __name__ == "__main__":
    try:
        from esys.ripley import Brick
        HAVE_RIPLEY = True
    except ImportError:
        HAVE_RIPLEY = False
        print("Ripley module not available")

    if HAVE_RIPLEY:
        from esys.escript import *
        from esys.escript.linearPDEs import Poisson
        from esys.weipa import saveSilo, saveVoxet
        
        import tempfile

        dom = Brick(l0=1.,l1=1.,n0=9, n1=9, n2=9)
        x = dom.getX()
        gammaD = whereZero(x[0])+whereZero(x[1])
        pde = Poisson(dom)
        q = gammaD
        pde.setValue(f=1, q=q)
        u = pde.getSolution()
        u=interpolate(u+dom.getX()[2], ReducedFunction(dom))
        print(u)
        filename = "/tmp/temp.vo"
        saveVoxet(filename, u=u)

        print("-------")
        dom = Brick(l0=1.,l1=1.,l2=4.,n0=18, n1=18, n2=36)
        v=readVoxet(dom, filename, 'u', fillValue=0.5)
        print(v)
        if getMPIRankWorld() == 0:
            os.remove(filename)
            os.remove(filename[:-3] + '_u')
        #saveSilo('/tmp/poisson', v=v)