source: inundation/pyvolution/domain.py @ 2607

Last change on this file since 2607 was 2607, checked in by duncan, 18 years ago

method name fix

File size: 27.1 KB
Line 
1"""Class Domain - 2D triangular domains for finite-volume computations of
2   the shallow water wave equation
3
4
5   Copyright 2004
6   Ole Nielsen, Stephen Roberts, Duncan Gray, Christopher Zoppou
7   Geoscience Australia
8"""
9
10from mesh import Mesh
11from generic_boundary_conditions import Boundary
12from generic_boundary_conditions import File_boundary
13from generic_boundary_conditions import Dirichlet_boundary
14from generic_boundary_conditions import Time_boundary
15from generic_boundary_conditions import Transmissive_boundary
16from pmesh2domain import pmesh_to_domain
17
18import types
19
20class Domain(Mesh):
21
22    def __init__(self, coordinates=None, vertices=None,
23                 boundary=None,
24                 conserved_quantities=None, other_quantities=None,
25                 tagged_elements=None, geo_reference=None,
26                 use_inscribed_circle=False,
27                 mesh_file=None):
28
29        if mesh_file is not None:
30            coordinates, vertices, boundary, vertex_quantity_dict \
31                                ,tagged_elements, geo_reference = \
32                                pmesh_to_domain(file_name=mesh_file)
33            # FIXME(DSG-DSG) have to do something with vertex_quantity_dict
34
35        Mesh.__init__(self, coordinates, vertices, boundary,
36                      tagged_elements, geo_reference, use_inscribed_circle)
37
38        from Numeric import zeros, Float, Int
39        from quantity import Quantity, Conserved_quantity
40
41        #List of quantity names entering
42        #the conservation equations
43        #(Must be a subset of quantities)
44        if conserved_quantities is None:
45            self.conserved_quantities = []
46        else:
47            self.conserved_quantities = conserved_quantities
48
49        if other_quantities is None:
50            self.other_quantities = []
51        else:
52            self.other_quantities = other_quantities
53
54
55        #Build dictionary of Quantity instances keyed by quantity names
56        self.quantities = {}
57
58        #FIXME: remove later - maybe OK, though....
59        for name in self.conserved_quantities:
60            self.quantities[name] = Conserved_quantity(self)
61        for name in self.other_quantities:
62            self.quantities[name] = Quantity(self)
63
64        #Create an empty list for explicit forcing terms
65        self.forcing_terms = []
66
67
68        #Defaults
69        from config import max_smallsteps, beta_w, beta_h, epsilon, CFL
70        self.beta_w = beta_w
71        self.beta_h = beta_h
72        self.epsilon = epsilon
73
74        #FIXME: Maybe have separate orders for h-limiter and w-limiter?
75        #Or maybe get rid of order altogether and use beta_w and beta_h
76        self.set_default_order(1)       
77        #self.default_order = 1
78        #self.order = self.default_order
79
80        self.smallsteps = 0
81        self.max_smallsteps = max_smallsteps
82        self.number_of_steps = 0
83        self.number_of_first_order_steps = 0
84        self.CFL = CFL
85
86        #Model time
87        self.time = 0.0
88        self.finaltime = None
89        self.min_timestep = self.max_timestep = 0.0
90        self.starttime = 0 #Physical starttime if any (0 is 1 Jan 1970 00:00:00)
91
92        ######OBSOLETE
93        #Origin in UTM coordinates
94        #FIXME: This should be set if read by a msh file
95        #self.zone = zone
96        #self.xllcorner = xllcorner
97        #self.yllcorner = yllcorner
98
99
100        #Checkpointing and storage
101        from config import default_datadir
102        self.datadir = default_datadir
103        self.filename = 'domain'
104        self.checkpoint = False
105
106        #MH310505 To avoid calculating the flux across each edge twice, keep an integer (boolean) array,
107        #to be used during the flux calculation
108        N=self.number_of_elements
109        self.already_computed_flux = zeros((N, 3), Int)
110
111        if mesh_file is not None:
112            # If the mesh file passed any quantity values
113            # , initialise with these values.
114            self.set_quantity_vertices_dict(vertex_quantity_dict)
115
116       
117
118    def set_default_order(self, n):
119        """Set default (spatial) order to either 1 or 2
120        """
121
122        msg = 'Default order must be either 1 or 2. I got %s' %n
123        assert n in [1,2], msg
124       
125        self.default_order = n
126        self.order = self.default_order
127
128
129    #Public interface to Domain
130    def get_conserved_quantities(self, vol_id, vertex=None, edge=None):
131        """Get conserved quantities at volume vol_id
132
133        If vertex is specified use it as index for vertex values
134        If edge is specified use it as index for edge values
135        If neither are specified use centroid values
136        If both are specified an exeception is raised
137
138        Return value: Vector of length == number_of_conserved quantities
139
140        """
141
142        from Numeric import zeros, Float
143
144        if not (vertex is None or edge is None):
145            msg = 'Values for both vertex and edge was specified.'
146            msg += 'Only one (or none) is allowed.'
147            raise msg
148
149        q = zeros( len(self.conserved_quantities), Float)
150
151        for i, name in enumerate(self.conserved_quantities):
152            Q = self.quantities[name]
153            if vertex is not None:
154                q[i] = Q.vertex_values[vol_id, vertex]
155            elif edge is not None:
156                q[i] = Q.edge_values[vol_id, edge]
157            else:
158                q[i] = Q.centroid_values[vol_id]
159
160        return q
161
162    def set_time(self, time=0.0):
163        """Set the time"""
164
165        self.time = time
166
167    def set_quantity_vertices_dict(self, quantity_dict):
168        """Set values for named quantities.
169        The index is the quantity
170
171        name: Name of quantity
172        X: Compatible list, Numeric array, const or function (see below)
173
174        The values will be stored in elements following their
175        internal ordering.
176
177        """
178        for key in quantity_dict.keys():
179            self.set_quantity(key, quantity_dict[key], location='vertices')
180
181
182    def set_quantity(self, name, *args, **kwargs):
183        """Set values for named quantity
184
185
186        One keyword argument is documented here:
187        expression = None, # Arbitrary expression
188
189        expression:
190          Arbitrary expression involving quantity names
191
192        See Quantity.set_values for further documentation.
193        """
194
195        #FIXME (Ole): Allow new quantities here
196        #from quantity import Quantity, Conserved_quantity
197        #Create appropriate quantity object
198        ##if name in self.conserved_quantities:
199        ##    self.quantities[name] = Conserved_quantity(self)
200        ##else:
201        ##    self.quantities[name] = Quantity(self)
202
203
204        #Do the expression stuff
205        if kwargs.has_key('expression'):
206            expression = kwargs['expression']
207            del kwargs['expression']
208
209            Q = self.create_quantity_from_expression(expression)
210            kwargs['quantity'] = Q
211
212
213        #Assign values
214        self.quantities[name].set_values(*args, **kwargs)
215
216
217    def get_quantity(self, name, location='vertices', indices = None):
218        """Get quantity object.
219
220        name: Name of quantity
221
222        See methods inside the quantity object for more options
223        """
224
225        return self.quantities[name] #.get_values( location, indices = indices)
226
227
228    def get_quantity_object(self, name):
229        """Get object for named quantity
230
231        name: Name of quantity
232
233        FIXME: Obsolete
234        """
235
236        print 'get_quantity_object has been deprecated. Please use get_quantity'
237        return self.quantities[name]
238
239
240    def create_quantity_from_expression(self, expression):
241        """Create new quantity from other quantities using arbitrary expression
242
243        Combine existing quantities in domain using expression and return
244        result as a new quantity.
245
246        Note, the new quantity could e.g. be used in set_quantity
247
248        Valid expressions are limited to operators defined in class Quantity
249
250        Example:
251
252
253        """
254
255        from util import apply_expression_to_dictionary
256        return apply_expression_to_dictionary(expression, self.quantities)
257
258
259
260
261    def set_boundary(self, boundary_map):
262        """Associate boundary objects with tagged boundary segments.
263
264        Input boundary_map is a dictionary of boundary objects keyed
265        by symbolic tags to matched against tags in the internal dictionary
266        self.boundary.
267
268        As result one pointer to a boundary object is stored for each vertex
269        in the list self.boundary_objects.
270        More entries may point to the same boundary object
271
272        Schematically the mapping is from two dictionaries to one list
273        where the index is used as pointer to the boundary_values arrays
274        within each quantity.
275
276        self.boundary:          (vol_id, edge_id): tag
277        boundary_map (input):   tag: boundary_object
278        ----------------------------------------------
279        self.boundary_objects:  ((vol_id, edge_id), boundary_object)
280
281
282        Pre-condition:
283          self.boundary has been built.
284
285        Post-condition:
286          self.boundary_objects is built
287
288        If a tag from the domain doesn't appear in the input dictionary an
289        exception is raised.
290        However, if a tag is not used to the domain, no error is thrown.
291        FIXME: This would lead to implementation of a
292        default boundary condition
293
294        Note: If a segment is listed in the boundary dictionary and if it is
295        not None, it *will* become a boundary -
296        even if there is a neighbouring triangle.
297        This would be the case for internal boundaries
298
299        Boundary objects that are None will be skipped.
300
301        FIXME: If set_boundary is called multiple times and if Boundary
302        object is changed into None, the neighbour structure will not be
303        restored!!!
304
305
306        """
307
308        self.boundary_objects = []
309        self.boundary_map = boundary_map  #Store for use with eg. boundary_stats.
310
311        #FIXME: Try to remove the sorting and fix test_mesh.py
312        x = self.boundary.keys()
313        x.sort()
314
315        #Loop through edges that lie on the boundary and associate them with
316        #callable boundary objects depending on their tags
317        for k, (vol_id, edge_id) in enumerate(x):
318            tag = self.boundary[ (vol_id, edge_id) ]
319
320            if boundary_map.has_key(tag):
321                B = boundary_map[tag]  #Get callable boundary object
322
323                if B is not None:
324                    self.boundary_objects.append( ((vol_id, edge_id), B) )
325                    self.neighbours[vol_id, edge_id] = -len(self.boundary_objects)
326                else:
327                    pass
328                    #FIXME: Check and perhaps fix neighbour structure
329
330
331            else:
332                msg = 'ERROR (domain.py): Tag "%s" has not been ' %tag
333                msg += 'bound to a boundary object.\n'
334                msg += 'All boundary tags defined in domain must appear '
335                msg += 'in the supplied dictionary.\n'
336                msg += 'The tags are: %s' %self.get_boundary_tags()
337                raise msg
338
339
340    def set_region(self, functions):
341        # The order of functions in the list is used.
342        if type(functions) not in [types.ListType,types.TupleType]:
343            functions = [functions]
344        for function in functions:
345            for tag in self.tagged_elements.keys():
346                function(tag, self.tagged_elements[tag], self)
347
348                #Do we need to do this sort of thing?
349                #self = function(tag, self.tagged_elements[tag], self)
350
351    #MISC
352    def check_integrity(self):
353        Mesh.check_integrity(self)
354
355        for quantity in self.conserved_quantities:
356            msg = 'Conserved quantities must be a subset of all quantities'
357            assert quantity in self.quantities, msg
358
359        ##assert hasattr(self, 'boundary_objects')
360
361    def write_time(self):
362        print self.timestepping_statistics()
363
364        #Old version
365        #if self.min_timestep == self.max_timestep:
366        #    print 'Time = %.4f, delta t = %.8f, steps=%d (%d)'\
367        #          %(self.time, self.min_timestep, self.number_of_steps,
368        #            self.number_of_first_order_steps)
369        #elif self.min_timestep > self.max_timestep:
370        #    print 'Time = %.4f, steps=%d (%d)'\
371        #          %(self.time, self.number_of_steps,
372        #            self.number_of_first_order_steps)
373        #else:
374        #    print 'Time = %.4f, delta t in [%.8f, %.8f], steps=%d (%d)'\
375        #          %(self.time, self.min_timestep,
376        #            self.max_timestep, self.number_of_steps,
377        #            self.number_of_first_order_steps)
378
379    def timestepping_statistics(self):
380        """Return string with time stepping statistics for printing or logging
381        """
382
383        msg = ''
384        if self.min_timestep == self.max_timestep:
385            msg += 'Time = %.4f, delta t = %.8f, steps=%d (%d)'\
386                   %(self.time, self.min_timestep, self.number_of_steps,
387                     self.number_of_first_order_steps)
388        elif self.min_timestep > self.max_timestep:
389            msg += 'Time = %.4f, steps=%d (%d)'\
390                   %(self.time, self.number_of_steps,
391                     self.number_of_first_order_steps)
392        else:
393            msg += 'Time = %.4f, delta t in [%.8f, %.8f], steps=%d (%d)'\
394                   %(self.time, self.min_timestep,
395                     self.max_timestep, self.number_of_steps,
396                     self.number_of_first_order_steps)
397
398        return msg
399
400
401    def write_boundary_statistics(self, quantities = None, tags = None):
402        print self.boundary_statistics(quantities, tags)
403
404    def boundary_statistics(self, quantities = None, tags = None):
405        """Output statistics about boundary forcing at each timestep
406
407
408        Input:
409          quantities: either None, a string or a list of strings naming the quantities to be reported
410          tags:       either None, a string or a list of strings naming the tags to be reported         
411
412
413        Example output:
414        Tag 'wall':
415            stage in [2, 5.5]
416            xmomentum in []
417            ymomentum in []
418        Tag 'ocean'
419
420
421        If quantities are specified only report on those. Otherwise take all conserved quantities.
422        If tags are specified only report on those, otherwise take all tags.
423
424        """
425
426        #Input checks
427        import types, string
428
429        if quantities is None:
430            quantities = self.conserved_quantities
431        elif type(quantities) == types.StringType:
432            quantities = [quantities] #Turn it into a list
433
434        msg = 'Keyword argument quantities must be either None, '
435        msg += 'string or list. I got %s' %str(quantities)
436        assert type(quantities) == types.ListType, msg
437
438
439        if tags is None:
440            tags = self.get_boundary_tags()
441        elif type(tags) == types.StringType:
442            tags = [tags] #Turn it into a list
443
444        msg = 'Keyword argument tags must be either None, '
445        msg += 'string or list. I got %s' %str(tags)
446        assert type(tags) == types.ListType, msg
447
448        #Determine width of longest quantity name (for cosmetic purposes)
449        maxwidth = 0
450        for name in quantities:
451            w = len(name)
452            if w > maxwidth:
453                maxwidth = w
454
455        #Output stats
456        msg = 'Boundary values at time %.4f:\n' %self.time
457        for tag in tags:
458            msg += '    %s:\n' %tag
459
460            for name in quantities:
461                q = self.quantities[name]
462
463                #Find range of boundary values for tag and q
464                maxval = minval = None
465                for i, ((vol_id, edge_id), B) in\
466                        enumerate(self.boundary_objects):
467                    if self.boundary[(vol_id, edge_id)] == tag:
468                        v = q.boundary_values[i]
469                        if minval is None or v < minval: minval = v
470                        if maxval is None or v > maxval: maxval = v
471
472                if minval is None or maxval is None:
473                    msg += '        Sorry no information available about' +\
474                           ' tag %s and quantity %s\n' %(tag, name)
475                else:
476                    msg += '        %s in [%12.8f, %12.8f]\n'\
477                           %(string.ljust(name, maxwidth), minval, maxval)
478
479
480        return msg
481
482
483    def get_name(self):
484        return self.filename
485
486    def set_name(self, name):
487        self.filename = name
488
489    def get_datadir(self):
490        return self.datadir
491
492    def set_datadir(self, name):
493        self.datadir = name
494
495
496
497    #def set_defaults(self):
498    #    """Set default values for uninitialised quantities.
499    #    Should be overridden or specialised by specific modules
500    #    """#
501    #
502    #    for name in self.conserved_quantities + self.other_quantities:
503    #        self.set_quantity(name, 0.0)
504
505
506    ###########################
507    #Main components of evolve
508
509    def evolve(self,
510               yieldstep = None,
511               finaltime = None,
512               duration = None,             
513               skip_initial_step = False):
514        """Evolve model through time starting from self.starttime.
515       
516       
517        yieldstep: Interval between yields where results are stored,
518                   statistics written and domain inspected or
519                   possibly modified. If omitted the internal predefined
520                   max timestep is used.
521                   Internally, smaller timesteps may be taken.               
522
523        duration: Duration of simulation
524
525        finaltime: Time where simulation should end
526
527        If both duration and finaltime are given an exception is thrown.
528
529
530        skip_initial_step: Boolean flag that decides whether the first
531        yield step is skipped or not. This is useful for example to avoid
532        duplicate steps when multiple evolve processes are dove tailed.
533
534
535        Evolve is implemented as a generator and is to be called as such, e.g.
536
537        for t in domain.evolve(yieldstep, finaltime):
538            <Do something with domain and t>
539
540
541        All times are given in seconds       
542
543        """
544
545        from config import min_timestep, max_timestep, epsilon
546
547        #FIXME: Maybe lump into a larger check prior to evolving
548        msg = 'Boundary tags must be bound to boundary objects before evolving system, '
549        msg += 'e.g. using the method set_boundary.\n'
550        msg += 'This system has the boundary tags %s '\
551               %self.get_boundary_tags()
552        assert hasattr(self, 'boundary_objects'), msg
553
554        ##self.set_defaults()
555
556        if yieldstep is None:
557            yieldstep = max_timestep
558        else:
559            yieldstep = float(yieldstep)
560
561        self.order = self.default_order
562
563
564        if finaltime is not None and duration is not None:
565            print 'F', finaltime, duration
566            msg = 'Only one of finaltime and duration may be specified'
567            raise msg
568        else:
569            if finaltime is not None:
570                self.finaltime = float(finaltime)
571            if duration is not None:
572                self.finaltime = self.starttime + float(duration)
573               
574
575       
576
577        self.yieldtime = 0.0 #Time between 'yields'
578
579        #Initialise interval of timestep sizes (for reporting only)
580        self.min_timestep = max_timestep
581        self.max_timestep = min_timestep
582        self.number_of_steps = 0
583        self.number_of_first_order_steps = 0
584
585        #update ghosts
586        self.update_ghosts()
587
588        #Initial update of vertex and edge values
589        self.distribute_to_vertices_and_edges()
590
591        #Initial update boundary values
592        self.update_boundary()
593
594        #Or maybe restore from latest checkpoint
595        if self.checkpoint is True:
596            self.goto_latest_checkpoint()
597
598        if skip_initial_step is False:
599            yield(self.time)  #Yield initial values
600
601        while True:
602
603            #Compute fluxes across each element edge
604            self.compute_fluxes()
605
606            #Update timestep to fit yieldstep and finaltime
607            self.update_timestep(yieldstep, finaltime)
608
609            #Update conserved quantities
610            self.update_conserved_quantities()
611
612            #update ghosts
613            self.update_ghosts()
614
615            #Update vertex and edge values
616            self.distribute_to_vertices_and_edges()
617
618            #Update boundary values
619            self.update_boundary()
620
621            #Update time
622            self.time += self.timestep
623            self.yieldtime += self.timestep
624            self.number_of_steps += 1
625            if self.order == 1:
626                self.number_of_first_order_steps += 1
627
628            #Yield results
629            if finaltime is not None and abs(self.time - finaltime) < epsilon:
630
631                #FIXME: There is a rare situation where the
632                #final time step is stored twice. Can we make a test?
633
634                # Yield final time and stop
635                yield(self.time)
636                break
637
638
639            if abs(self.yieldtime - yieldstep) < epsilon:
640                # Yield (intermediate) time and allow inspection of domain
641
642                if self.checkpoint is True:
643                    self.store_checkpoint()
644                    self.delete_old_checkpoints()
645
646                #Pass control on to outer loop for more specific actions
647                yield(self.time)
648
649                # Reinitialise
650                self.yieldtime = 0.0
651                self.min_timestep = max_timestep
652                self.max_timestep = min_timestep
653                self.number_of_steps = 0
654                self.number_of_first_order_steps = 0
655
656
657    def evolve_to_end(self, finaltime = 1.0):
658        """Iterate evolve all the way to the end
659        """
660
661        for _ in self.evolve(yieldstep=None, finaltime=finaltime):
662            pass
663
664
665
666    def update_boundary(self):
667        """Go through list of boundary objects and update boundary values
668        for all conserved quantities on boundary.
669        """
670
671        #FIXME: Update only those that change (if that can be worked out)
672        #FIXME: Boundary objects should not include ghost nodes.
673        for i, ((vol_id, edge_id), B) in enumerate(self.boundary_objects):
674            if B is None:
675                print 'WARNING: Ignored boundary segment %d (None)'
676            else:               
677                q = B.evaluate(vol_id, edge_id)
678
679                for j, name in enumerate(self.conserved_quantities):
680                    Q = self.quantities[name]
681                    Q.boundary_values[i] = q[j]
682
683
684    def compute_fluxes(self):
685        msg = 'Method compute_fluxes must be overridden by Domain subclass'
686        raise msg
687
688
689    def update_timestep(self, yieldstep, finaltime):
690
691        from config import min_timestep
692
693        # self.timestep is calculated from speed of characteristics
694        # Apply CFL condition here
695        timestep = self.CFL*self.timestep
696
697        #Record maximal and minimal values of timestep for reporting
698        self.max_timestep = max(timestep, self.max_timestep)
699        self.min_timestep = min(timestep, self.min_timestep)
700
701        #Protect against degenerate time steps
702        if timestep < min_timestep:
703
704            #Number of consecutive small steps taken b4 taking action
705            self.smallsteps += 1
706
707            if self.smallsteps > self.max_smallsteps:
708                self.smallsteps = 0 #Reset
709
710                if self.order == 1:
711                    msg = 'WARNING: Too small timestep %.16f reached '\
712                          %timestep
713                    msg += 'even after %d steps of 1 order scheme'\
714                           %self.max_smallsteps
715                    print msg
716                    timestep = min_timestep  #Try enforcing min_step
717
718                    #raise msg
719                else:
720                    #Try to overcome situation by switching to 1 order
721                    self.order = 1
722
723        else:
724            self.smallsteps = 0
725            if self.order == 1 and self.default_order == 2:
726                self.order = 2
727
728
729        #Ensure that final time is not exceeded
730        if finaltime is not None and self.time + timestep > finaltime:
731            timestep = finaltime-self.time
732
733        #Ensure that model time is aligned with yieldsteps
734        if self.yieldtime + timestep > yieldstep:
735            timestep = yieldstep-self.yieldtime
736
737        self.timestep = timestep
738
739
740
741    def compute_forcing_terms(self):
742        """If there are any forcing functions driving the system
743        they should be defined in Domain subclass and appended to
744        the list self.forcing_terms
745        """
746
747        for f in self.forcing_terms:
748            f(self)
749
750
751
752    def update_conserved_quantities(self):
753        """Update vectors of conserved quantities using previously
754        computed fluxes specified forcing functions.
755        """
756
757        from Numeric import ones, sum, equal, Float
758
759        N = self.number_of_elements
760        d = len(self.conserved_quantities)
761
762        timestep = self.timestep
763
764        #Compute forcing terms
765        self.compute_forcing_terms()
766
767        #Update conserved_quantities
768        for name in self.conserved_quantities:
769            Q = self.quantities[name]
770            Q.update(timestep)
771
772            #Clean up
773            #Note that Q.explicit_update is reset by compute_fluxes
774
775            #MH090605 commented out the following since semi_implicit_update is now re-initialized
776            #at the end of the _update function in quantity_ext.c (This is called by the
777            #preceeding Q.update(timestep) statement above).
778            #For run_profile.py with N=128, the time of update_conserved_quantities is cut from 14.00 secs
779            #to 8.35 secs
780
781            #Q.semi_implicit_update[:] = 0.0
782
783    def update_ghosts(self):
784        pass
785
786    def distribute_to_vertices_and_edges(self):
787        """Extrapolate conserved quantities from centroid to
788        vertices and edge-midpoints for each volume
789
790        Default implementation is straight first order,
791        i.e. constant values throughout each element and
792        no reference to non-conserved quantities.
793        """
794
795        for name in self.conserved_quantities:
796            Q = self.quantities[name]
797            if self.order == 1:
798                Q.extrapolate_first_order()
799            elif self.order == 2:
800                Q.extrapolate_second_order()
801                Q.limit()
802            else:
803                raise 'Unknown order'
804            Q.interpolate_from_vertices_to_edges()
805
806
807    def centroid_norm(self, quantity, normfunc):
808        """Calculate the norm of the centroid values
809        of a specific quantity, using normfunc.
810
811        normfunc should take a list to a float.
812
813        common normfuncs are provided in the module utilities.norms
814        """
815        return normfunc(self.quantities[quantity].centroid_values)
816
817
818
819##############################################
820#Initialise module
821
822#Optimisation with psyco
823from config import use_psyco
824if use_psyco:
825    try:
826        import psyco
827    except:
828        import os
829        if os.name == 'posix' and os.uname()[4] == 'x86_64':
830            pass
831            #Psyco isn't supported on 64 bit systems, but it doesn't matter
832        else:
833            msg = 'WARNING: psyco (speedup) could not import'+\
834                  ', you may want to consider installing it'
835            print msg
836    else:
837        psyco.bind(Domain.update_boundary)
838        #psyco.bind(Domain.update_timestep)     #Not worth it
839        psyco.bind(Domain.update_conserved_quantities)
840        psyco.bind(Domain.distribute_to_vertices_and_edges)
841
842
843if __name__ == "__main__":
844    pass
Note: See TracBrowser for help on using the repository browser.