Ignore:
Timestamp:
Feb 23, 2015, 9:21:46 AM (10 years ago)
Author:
davies
Message:

Major changes + bugfix for internal_boundary_operator and related

Location:
trunk/anuga_core/anuga/structures
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/anuga_core/anuga/structures/boyd_box_operator_Amended3.py

    r8125 r9681  
    3232                 verbose=False):
    3333
    34                      
     34        raise Exception('(Feb 2015) This code seems broken -- the structure operator call is incorrect -- retire?')
     35
    3536        anuga.Structure_operator.__init__(self,
    3637                                          domain,
  • trunk/anuga_core/anuga/structures/internal_boundary_operator.py

    r9672 r9681  
    33import numpy
    44from numpy.linalg import solve
     5import scipy
     6import scipy.optimize as sco
    57
    68
     
    7577                                          zero_outflow_momentum=zero_outflow_momentum,
    7678                                          use_old_momentum_method=False,
     79                                          always_use_Q_wetdry_adjustment=False,
    7780                                          force_constant_inlet_elevations=force_constant_inlet_elevations,
    7881                                          description=description,
     
    227230    def discharge_routine_implicit(self):
    228231        """Uses semi-implicit discharge estimation:
    229           Discharge = 0.5*(Q(H0, T0) + Q(H0 + delta_H, T0+delta_T))
     232          Discharge = (1-theta)*Q(H0, T0) + theta*Q(H0 + delta_H, T0+delta_T))
    230233        where H0 = headwater stage, T0 = tailwater stage, delta_H = change in
    231234        headwater stage over a timestep, delta_T = change in tailwater stage over a
    232         timestep, and Q is the discharge function
    233 
    234         We can estimate delta_H, delta_T by solving the following system (based on mass conservation):
    235           A0*delta_H = -dt/2*( Q(H0, T0) + Q(H0 + delta_H, T0 + delta_T))
    236           A1*delta_T =  dt/2*( Q(H0, T0) + Q(H0 + delta_H, T0 + delta_T))
    237         where A0, A1 are the inlet areas, and dt is the timestep
    238 
    239         We linearise the system with the approximation:
    240           Q(H0 + delta_H, T0 + delta_T) ~= Q(H0, T0) + delQ/delH * delta_H + delQ/delT*delta_T
    241         where delQ/delH and delQ/delT are evaluated with numerical finite differences
    242 
     235        timestep, and Q is the discharge function, and theta is a constant in
     236        [0,1] determining the degree of implicitness (currently hard-coded).
     237
     238        Note this is effectively assuming:
     239        1) Q is a function of stage, not energy (so we can relate mass change directly to delta_H, delta_T). We
     240           could generalise it to the energy case ok.
     241        2) The stage is computed on the exchange line (or the change in
     242            stage at the enquiry point is effectively the same as that on the exchange
     243            line)
    243244
    244245        """
     
    263264            E0 = self.inlet0_energy
    264265            E1 = self.inlet1_energy
    265 
    266             # Numerical derivatives
    267             dE0 = 0.01
    268             dE1 = 0.01
    269             dQ_dE0 = (self.internal_boundary_function(E0+dE0, E1) - self.internal_boundary_function(E0-dE0, E1))/(2*dE0)
    270             dQ_dE1 = (self.internal_boundary_function(E0, E1+dE1) - self.internal_boundary_function(E0, E1-dE1))/(2*dE1)
    271 
    272             # Assemble and solve matrix
    273266            A0 = self.inlets[0].area
    274267            A1 = self.inlets[1].area
    275             hdt = 0.5*dt
    276 
    277             M11 = A0 + dQ_dE0*hdt
    278             M12 = dQ_dE1*hdt
    279             M21 = -dQ_dE0*hdt
    280             M22 = A1 - dQ_dE1*hdt
    281 
    282             lhs = numpy.array([ [M11, M12], [M21, M22]])
    283             rhs = numpy.array([ -Q0*dt, Q0*dt])
    284             # sol contains delta_E0, delta_E1
    285             sol = solve(lhs, rhs)
    286            
    287             #Q = 0.5*(Q0 + ( Q0 + sol[0]*dQ_dE0 + sol[1]*dQ_dE1))
    288             Q1 =  self.internal_boundary_function(E0 + sol[0], E1 + sol[1])
    289             Q = 0.5*(Q0 + Q1)
     268            theta = 1.0
     269
     270            sol = numpy.array([0., 0.]) # estimate of (delta_H, delta_T)
     271            areas = numpy.array([A0, A1])
     272
     273            # Use scipy root finding
     274            def F_to_solve(sol):
     275                Q1 =  self.internal_boundary_function(E0 + sol[0], E1 + sol[1])
     276                discharge = (1.0-theta)*Q0 + theta*Q1
     277                output = sol*areas - discharge*dt*numpy.array([-1., 1.])
     278                return(output)
     279
     280            final_sol = sco.root(F_to_solve, sol, method='lm').x
     281            Q1 =  self.internal_boundary_function(E0 + final_sol[0], E1 + final_sol[1])
     282            Q = (1.0-theta)*Q0 + theta*Q1
     283
    290284        else:
    291285            Q = Q0
  • trunk/anuga_core/anuga/structures/structure_operator.py

    r9671 r9681  
    3939                 zero_outflow_momentum=True,
    4040                 use_old_momentum_method=True,
     41                 always_use_Q_wetdry_adjustment=True,
    4142                 force_constant_inlet_elevations=False,
    4243                 description=None,
     
    9495            raise Exception(msg)
    9596        self.use_old_momentum_method = use_old_momentum_method
     97        self.always_use_Q_wetdry_adjustment = always_use_Q_wetdry_adjustment
    9698
    9799
     
    119121        self.accumulated_flow = 0.0
    120122        self.discharge = 0.0
     123        self.discharge_function_value = 0.0
    121124        self.velocity = 0.0
    122125        self.outlet_depth = 0.0
     
    218221        old_inflow_ymom = self.inflow.get_average_ymom()
    219222
    220         #semi_implicit = True
    221         #if semi_implicit:   
    222 
    223         # FIXME: This update replaces Q with Q*new_inflow_depth/old_inflow_depth
    224         # This has good wetting and drying properties but means that
    225         # discharge != Q.
    226         # We should be able to check if old_inflow_depth*old_inflow_area > Q*dt,
    227         # and in that case we don't need this implicit trick, and could
    228         # have the discharge = Q (whereas in the nearly-dry case we want
    229         # the trick).
    230 
    231223        # Implement the update of flow over a timestep by
    232224        # using a semi-implict update. This ensures that
     
    237229            dt_Q_on_d = 0.0
    238230
    239         # The depth update is:
     231        always_use_Q_wetdry_adjustment = self.always_use_Q_wetdry_adjustment
     232        use_Q_wetdry_adjustment = ((always_use_Q_wetdry_adjustment) |\
     233            (old_inflow_depth*self.inflow.get_area() <= Q*timestep))
     234        # If we use the 'Q_adjustment', then the discharge is rescaled so that
     235        # the depth update is:
    240236        #    new_inflow_depth*inflow_area =
    241237        #    old_inflow_depth*inflow_area -
    242238        #    timestep*Q*(new_inflow_depth/old_inflow_depth)
    243         # The last term in () is a wet-dry improvement trick
    244         # Note inflow_area is the area of all triangles in the inflow
    245         # region -- so this is a volumetric balance equation
     239        # The last term in () is a wet-dry improvement trick (which rescales Q)
     240        #
     241        # Before Feb 2015 this rescaling was always done (even if the flow was
     242        # not near wet-dry). Now if use_old_Q_adjustment=False, the rescaling
     243        # is only done if required to avoid drying
     244        #
    246245        #
    247246        factor = 1.0/(1.0 + dt_Q_on_d/self.inflow.get_area())
    248         new_inflow_depth = old_inflow_depth*factor
     247
     248
     249        if use_Q_wetdry_adjustment:
     250            # FIXME:
     251            new_inflow_depth = old_inflow_depth*factor
     252
     253            if(old_inflow_depth>0.):
     254                timestep_star = timestep*new_inflow_depth/old_inflow_depth
     255            else:
     256                timestep_star = 0.
     257
     258        else:
     259            new_inflow_depth = old_inflow_depth - timestep*Q/self.inflow.get_area()
     260            timestep_star = timestep
    249261
    250262        if(self.use_old_momentum_method):
     
    256268        else:
    257269            # For the momentum balance, note that Q also advects the momentum,
    258             # The volumetric momentum flux should be Q*momentum, where
     270            # The volumetric momentum flux should be Q*momentum/depth, where
    259271            # momentum has an average value of new_inflow_mom (or
    260             # old_inflow_mom). For consistency we keep using the
    261             # (new_inflow_depth/old_inflow_depth) factor for discharge:
     272            # old_inflow_mom).  We use old_inflow_depth for depth
    262273            #
    263274            #     new_inflow_xmom*inflow_area =
    264275            #     old_inflow_xmom*inflow_area -
    265             #     [timestep*Q*(new_inflow_depth/old_inflow_depth)]*new_inflow_xmom
     276            #     [timestep*Q]*new_inflow_xmom/old_inflow_depth
    266277            # and:
    267278            #     new_inflow_ymom*inflow_area =
    268279            #     old_inflow_ymom*inflow_area -
    269             #     [timestep*Q*(new_inflow_depth/old_inflow_depth)]*new_inflow_ymom
     280            #     [timestep*Q]*new_inflow_ymom/old_inflow_depth
    270281            #
    271             # The choice of new_inflow_mom in the final term at the end might be
    272             # replaced with old_inflow_mom
     282            # The units balance: m^2/s*m^2 = m^2/s*m^2 - s*m^3/s*m^2/s *m^(-1)
    273283            #
    274             factor2 = 1.0/(1.0 + dt_Q_on_d*new_inflow_depth/self.inflow.get_area())
     284            if old_inflow_depth > 0.:
     285                if use_Q_wetdry_adjustment:
     286                    # Replace dt*Q with dt*Q*new_inflow_depth/old_inflow_depth = dt_Q_on_d*new_inflow_depth
     287                    factor2 = 1.0/(1.0 + dt_Q_on_d*new_inflow_depth/(old_inflow_depth*self.inflow.get_area()))
     288                else:
     289                    factor2 = 1.0/(1.0 + timestep*Q/(old_inflow_depth*self.inflow.get_area()))
     290            else:
     291                factor2 = 0.
     292
    275293            new_inflow_xmom = old_inflow_xmom*factor2
    276294            new_inflow_ymom = old_inflow_ymom*factor2
     
    289307
    290308        # set outflow
    291         if old_inflow_depth > 0.0 :
    292             timestep_star = timestep*new_inflow_depth/old_inflow_depth
    293         else:
    294             timestep_star = 0.0
    295 
    296309        outflow_extra_depth = Q*timestep_star/self.outflow.get_area()
    297310        outflow_direction = - self.outflow.outward_culvert_vector
     
    306319        self.accumulated_flow += gain
    307320        self.discharge  = Q*timestep_star/timestep
     321        self.discharge_function_value = Q
    308322        self.velocity =   barrel_speed
    309323        self.outlet_depth = outlet_depth
     
    541555       
    542556        message += 'Discharge [m^3/s]: %.2f\n' % self.discharge
     557        message += 'Discharge_function_value [m^3/s]: %.2f\n' % self.discharge_function_value
    543558        message += 'Velocity  [m/s]: %.2f\n' % self.velocity
    544559        message += 'Outlet Depth  [m]: %.2f\n' % self.outlet_depth
     
    562577            self.log_filename = self.domain.get_datadir() + '/' + self.label + '.log'
    563578            log_to_file(self.log_filename, self.statistics(), mode='w')
    564             log_to_file(self.log_filename, 'time, discharge, velocity, accumulated_flow, driving_energy, delta_total_energy')
     579            log_to_file(self.log_filename, 'time, discharge, discharge_function_value, velocity, accumulated_flow, driving_energy, delta_total_energy')
    565580
    566581            #log_to_file(self.log_filename, self.culvert_type)
     
    571586        message  = '%.5f, ' % self.domain.get_time()
    572587        message += '%.5f, ' % self.discharge
     588        message += '%.5f, ' % self.discharge_function_value
    573589        message += '%.5f, ' % self.velocity
    574590        message += '%.5f, ' % self.accumulated_flow
Note: See TracChangeset for help on using the changeset viewer.