[docs]classTransientReactiveTransport(ReactiveTransport):r""" A subclass of ReactiveTransport for transient simulations. Parameters ---------- network : Network The Network with which this algorithm is associated. Notes ----- Either a Network or a Project must be specified. """def__init__(self,phase,name='trans_react_?',**kwargs):super().__init__(phase=phase,name=name,**kwargs)self.settings._update(TransientReactiveTransportSettings())self.settings['phase']=phase.nameself["pore.ic"]=np.nan
[docs]defrun(self,x0,tspan,saveat=None,integrator=None):""" Runs the transient algorithm and returns the solution. Parameters ---------- x0 : ndarray or float Array (or scalar) containing initial condition values. tspan : array_like Tuple (or array) containing the integration time span. saveat : array_like or float, optional If an array is passed, it signifies the time points at which the solution is to be stored, and if a scalar is passed, it refers to the interval at which the solution is to be stored. integrator : Integrator, optional Integrator object which will be used to to the time stepping. Can be instantiated using openpnm.integrators module. Returns ------- TransientSolution The solution object, which is basically a numpy array with the added functionality that it can be called to return the solution at intermediate times (i.e., those not stored in the solution object). """logger.info('Running TransientTransport')ifnp.isscalar(saveat):saveat=np.arange(*tspan,saveat)# FIXME: why do we forcibly add tspan[1] to saveat even if the user# didn't want to?if(saveatisnotNone)and(tspan[1]notinsaveat):saveat=np.hstack((saveat,[tspan[1]]))integrator=ScipyRK45()ifintegratorisNoneelseintegrator# Perform pre-solve validationsself._validate_settings()self._validate_topology_health()self._validate_linear_system()# Write x0 to algorithm the obj (needed by _update_iterative_props)self['pore.ic']=x0=np.ones(self.Np,dtype=float)*x0self._merge_inital_and_boundary_values()# Build RHS (dx/dt = RHS), then integrate the system of ODEsrhs=self._build_rhs()# Integrate RHS using the given solversoln=integrator.solve(rhs,x0,tspan,saveat)# Return solution as dictionaryself.soln=SolutionContainer()self.soln[self.settings['quantity']]=soln
def_run_special(self,x0):passdef_build_rhs(self):""" Returns a function handle, which calculates dy/dt = rhs(y, t). Notes ----- ``y`` is the variable that the algorithms solves for, e.g., for ``TransientFickianDiffusion``, it would be concentration. """defode_func(t,y):# TODO: add a cache mechanismself.x=yself._update_A_and_b()A=self.A.tocsc()b=self.bV=self.network[self.settings["pore_volume"]]return(-A.dot(y)+b)/V# much faster than A*yreturnode_funcdef_merge_inital_and_boundary_values(self):x0=self['pore.ic']bc_pores=~np.isnan(self['pore.bc.value'])x0[bc_pores]=self['pore.bc.value'][bc_pores]quantity=self.settings['quantity']self[quantity]=x0