source: inundation/ga/storm_surge/pyvolution/generic_boundary_conditions.py @ 327

Last change on this file since 327 was 327, checked in by ole, 20 years ago
File size: 9.5 KB
Line 
1"""boundary.py - Classes for implementing boundary conditions
2"""
3
4
5class Boundary:
6    """Base class for boundary conditions.
7       Specific boundary conditions must provide values for
8       the conserved_quantities
9
10       A boundary object has one neighbour; the one it
11       serves.
12    """   
13
14    def __init__(self):
15        pass
16       
17    def evaluate(self, vol_id=None, edge_id=None):
18        msg = 'Generic class Boundary must be subclassed'
19        raise msg
20
21
22class Transmissive_boundary(Boundary):
23    """Transmissive boundary returns same conserved quantities as
24    those present in its neighbour volume.
25
26    Underlying domain must be specified when boundary is instantiated
27    """
28   
29    def __init__(self, domain = None):
30        Boundary.__init__(self)
31
32        if domain is None:
33            msg = 'Domain must be specified for transmissive boundary'
34            raise msg
35       
36        self.domain = domain
37       
38    def __repr__(self):
39        return 'Transmissive_boundary(%s)' %self.domain
40
41    def evaluate(self, vol_id, edge_id):
42        """Transmissive boundaries return the edge values
43        of the volume they serve.
44        """
45       
46        q = self.domain.get_conserved_quantities(vol_id, edge = edge_id)
47        return q       
48
49
50class Dirichlet_boundary(Boundary):
51    """Dirichlet boundary returns constant values for the
52    conserved quantities
53    """
54
55
56    def __init__(self, conserved_quantities=None):
57        Boundary.__init__(self)
58
59        if conserved_quantities is None:
60            msg = 'Must specify one value for each conserved quantity'
61            raise msg
62
63        from Numeric import array, Float
64        self.conserved_quantities=array(conserved_quantities).astype(Float)
65
66    def __repr__(self):
67        return 'Dirichlet boundary (%s)' %self.conserved_quantities
68   
69    def evaluate(self, vol_id=None, edge_id=None):
70        return self.conserved_quantities
71
72
73
74class Time_boundary(Boundary):
75    """Time dependent boundary returns values for the
76    conserved quantities as a function of time.
77    Must specify domain to get access to model time and a function f(t)
78    which must return conserved quantities as a function time
79    """
80
81
82    def __init__(self, domain = None, f=None):
83        Boundary.__init__(self)
84
85        try:
86            q = f(0.0)
87        except Exception, e:
88            msg = 'Function for time boundary could not be executed:\n%s' %e
89            raise msg
90       
91
92        from Numeric import array, Float       
93        try:
94            q = array(q).astype(Float)
95        except:
96            msg = 'Return value from time boundary function could '
97            msg += 'not be converted into a Numeric array of floats.\n'
98            msg += 'Specified function should return either list or array.' 
99            raise msg
100
101        msg = 'ERROR: Time boundary function must return a 1d list or array '
102        assert len(q.shape) == 1, msg
103           
104        d = len(domain.conserved_quantities)
105        msg = 'Return value for f must be a list or an array of length %d' %d
106        assert len(q) == d, msg
107
108        self.f = f
109        self.domain = domain
110
111    def __repr__(self):
112        return 'Time boundary'
113
114    def evaluate(self, vol_id=None, edge_id=None):
115        return self.f(self.domain.time)
116
117class File_boundary(Boundary):
118    """Boundary values obtained from file and interpolated.
119    conserved quantities as a function of time.
120
121    Assumes that file contains a time series.
122
123    No spatial info assumed.
124    """
125
126
127    def __init__(self, domain = None, filename = None):
128        import time
129        from Numeric import array
130        from config import time_format
131       
132        Boundary.__init__(self)
133
134
135        try:
136            fid = open(filename)
137        except Exception, e:
138            msg = 'Boundary file %s could not be opened: %s\n' %(filename, e)
139            raise msg
140
141
142        line = fid.readline()
143        fid.close()
144        fields = line.split(',')
145        msg = 'Boundary file %s must have the format date, values'
146        assert len(fields) == 2, msg
147
148        try:
149            start_time = time.mktime(time.strptime(fields[0], time_format))
150        except ValueError:   
151            msg = 'First field in boundary file %s must be' %filename
152            msg += ' date-time with format %s.\n' %time_format
153            msg += 'I got %s instead.' %fields[0] 
154            raise msg
155
156        #Split values
157        values = []
158        for value in fields[1].split():
159            values.append(float(value))
160
161        q = array(values)
162       
163        msg = 'ERROR: File boundary function must return a 1d list or array '
164        assert len(q.shape) == 1, msg
165           
166        d = len(domain.conserved_quantities)
167        msg = 'Values specified in file must be a list or an array of length %d' %d
168        assert len(q) == d, msg
169
170        self.filename = filename
171        self.domain = domain
172        self.start_time = start_time
173
174        self.read_time_boundary() #Now read all times in.
175       
176       
177    def read_time_boundary(self):
178        from Numeric import zeros, Float, alltrue
179        from config import time_format
180        import time
181       
182        fid = open(self.filename)       
183        lines = fid.readlines()
184        fid.close()
185       
186        N = len(lines)
187        d = len(self.domain.conserved_quantities)
188
189        T = zeros(N, Float)
190        Q = zeros((N, d), Float)
191
192       
193        for i, line in enumerate(lines):
194            fields = line.split(',')
195            real_time = time.mktime(time.strptime(fields[0], time_format))
196
197            T[i] = real_time - self.start_time
198
199           
200            for j, value in enumerate(fields[1].split()):
201                Q[i, j] = float(value)
202
203        msg = 'Time boundary must list time as a monotonuosly increasing sequence'         
204        assert alltrue( T[1:] - T[:-1] > 0 ), msg
205
206        self.T = T     #Time
207        self.Q = Q     #Boundary values
208        self.index = 0 #Initial index
209
210       
211    def __repr__(self):
212        return 'File boundary'
213
214    def evaluate(self, vol_id=None, edge_id=None):
215        """Return linearly interpolated values based on domain.time
216        """
217
218        t = self.domain.time
219
220        msg = 'Time given in File boundary does not match model time'
221        if t < self.T[0]: raise msg
222        if t > self.T[-1]: raise msg       
223
224        oldindex = self.index
225
226        #Find slot
227        while t > self.T[self.index]: self.index += 1
228        while t < self.T[self.index]: self.index -= 1
229
230        #if oldindex != self.index:
231        #    print 'Changing from %d to %d' %(oldindex, self.index)
232       
233       
234        #t is now between index and index+1
235        ratio = (t - self.T[self.index])/(self.T[self.index+1] - self.T[self.index])
236
237        return self.Q[self.index,:] + ratio*(self.Q[self.index+1,:] - self.Q[self.index,:]) 
238
239
240
241
242#THIS FAR (10/8/4)
243class Connective_boundary(Boundary):
244    """Connective boundary returns values for the
245    conserved quantities from a volume as defined by a connection table
246    mapping between tuples of (volume id, face id) for volumes that
247    have their boundaries connected.
248
249    FIXME: Perhaps include possibility of mapping between
250    different domains as well
251
252    FIXME: In case of shallow water we may want to have a
253    special version that casts this in terms of height rather than stage
254    """
255
256
257    def __init__(self, table):
258        from domain import Volume
259       
260        Boundary.__init__(self)
261
262        self.connection_table = table
263        self.Volume = Volume
264
265    def __repr__(self):
266        return 'Connective boundary'       
267
268    #FIXME: IF we ever need to get field_values from connected volume,
269    #that method could be overridden here (using same idea as in
270    #get_conserved_quantities
271    #def get_field_values()
272   
273    def get_conserved_quantities(self, volume, face=0):
274
275        id = volume.id
276        if self.connection_table.has_key((id, face)):
277            other_id, other_face = self.connection_table[(id, face)] 
278           
279            other_volume = self.Volume.instances[other_id]
280            cmd = 'q = other_volume.conserved_quantities_face%d' %face;
281            exec(cmd)       
282            return q
283        else:
284            msg = 'Volume, face tuple ($d, %d) has not been mapped'\
285                  %(id, face)
286            raise msg
287           
288       
289
290
291
292#FIXME: Add a boundary with a general function of x,y, and t
293
294#FIXME: Add periodic boundaries e.g.:
295# Attempt at periodic conditions from advection_spik. Remember this
296#
297#first = 2*(N-1)*N
298#for i in range(1,2*N+1,2):
299#    k = first + i-1#
300#
301#    print i,k
302#   
303#    domain[i].faces[2].neighbour = domain[k].faces[1]
304#    domain[k].faces[1].neighbour = domain[i].faces[2]   
305   
306
307
308class General_boundary(Boundary):
309    """General boundary which can compute conserved quantities based on
310    their previous value, conserved quantities of its neighbour and model time.
311
312    Must specify initial conserved quantities,
313    neighbour,
314    domain to get access to model time
315    a function f(q_old, neighbours_q, t) which must return
316    new conserved quantities q as a function time
317
318    FIXME: COMPLETE UNTESTED - JUST AN IDEA
319    """
320
321    def __init__(self, neighbour=None, conserved_quantities=None, domain=None, f=None):
322        Boundary.__init__(self, neighbour=neighbour, conserved_quantities=conserved_quantities)
323
324        self.f = f
325        self.domain = domain
326
327
328    def get_conserved_quantities(self, volume=None, face=0):
329        return self.f(self.conserved_quantities,
330                      neighbour.conserved_quantities,
331                      self.domain.time)
332
333   
334   
335       
Note: See TracBrowser for help on using the repository browser.