def evolve_to(meshctxt, subprobctxts, time, endtime, delta, prog, linsysDict=None): ## debug.fmsg("--------------- time=%g endtime=%g delta=%s" ## % (time, endtime, delta)) # subprobctxts is a list of SubProblemContexts. # delta is an initial suggested stepsize. # prog is a Progress object. assert endtime > time mindelta = None if linsysDict is None: linsysDict = {} starttime = time startdelta = delta truncated_step = False try: # Main loop. There is no explicit limit to the number of # iterations of this loop. Instead, the adaptive stepper's # nextStepEstimate function raises an ErrTimeStepTooSmall # exception if the step size is too small. while time < endtime and not prog.stopped(): # Choose the time step. If no delta is provide, just to # up to endtime. If delta is provided, go up to the # smaller of time+delta and endtime, unless time+delta is # within epsilon of endtime, in which case go up to # endtime anyway. This prevents roundoff errors from # requiring an extra infinitesimal step to reach endtime. # # truncated_step indicates whether we're trying to take a # step with the full delta or not. If we're not trying, # then the suggested time step for the next step may be # too small, because the stepper will make a suggestion # based on the delta it has (not the delta it wants or the # delta it might have at some future time). if delta > 0: if time + delta > endtime: targettime = endtime truncated_step = True else: # time + delta <= endtime targettime = time + delta if (endtime - targettime <= 3 * endtime * utils.machine_epsilon): targettime = endtime truncated_step = False else: # delta == 0 targettime = endtime truncated_step = False for subprob in subprobctxts: # Build or update the linearized system at the current # time, even if the stepper is fully implicit. The # maps have to be constructed, and they depend on the # matrices. lsys = subprob.make_linear_system( time, linsysDict.get(subprob, None)) linsysDict[subprob] = lsys subprob.startStep(lsys, time) # sets subprob.startValues subprob.cacheConstraints(lsys, time) # Iterate over subprobctxts repeatedly until answers are # consistent. stepno = 0 prevresults = {} newlinsys = {} stepTaken = False while (stepno < maxconsistencysteps and not (stepTaken or prog.stopped())): stepno += 1 mintime = None # lowest time attained by any subproblem mindelta = None # smallest recommended delta for any subp. for subproblem in subprobctxts: newconstraints = True # Take the step from time to targettime and add in # any new constraints encountered. Do this # repeatedly until there are no changes in the # constraints. while newconstraints and not prog.stopped(): # subproblem.nonlinear_solver is a # nonlinearsolver.NonlinearSolverBase # object, whose job it is to call the # appropriate (linear or nonlinear) method of # the subproblem's "time_stepper" object. # subproblem.time_stepper is a StepDriver # object, containing a TimeStepper object. # The StepDriver's [non]linearstep method # calls the TimeStepper's [non]linearstep # method. # The LinearizedSystem must be cloned because # we may need to repeat the step, and stepper # might re-evaluate the LinearizedSystem at an # intermediate or end time. ## TODO TDEP OPT: The cost of the clone can be ## reduced if the LinearizedSystem *doesn't* ## store K_indfree_, etc, or uses ## SparseSubMats for them. lsClone = linsysDict[subproblem].clone() unknowns = subproblem.get_unknowns(lsClone) # Take a timestep. The return value is a # timestepper.StepResult object. # debug.fmsg("taking step from %g to %g (%g)" % # (time, targettime, targettime-time)) stepResult = subproblem.nonlinear_solver.step( subproblem, linsys=lsClone, time=time, unknowns=unknowns, endtime=targettime) # debug.fmsg("endValues=", stepResult.endValues) if stepResult.ok: # endStep() sets subproblem.endValues assert stepResult.linsys is not None newlinsys[subproblem] = stepResult.linsys subproblem.endStep(linsysDict[subproblem], stepResult) newconstraints = subproblem.applyConstraints( subproblem.endValues, stepResult.endTime) else: # Don't bother checking constraints if the # step is going to be repeated anyway. newconstraints = False ## End 'while newconstraints' loop. # Keep track of the minimum time achieved and # minimum recommended next step size for all # subproblems. if mintime is None or mintime > stepResult.endTime: mintime = stepResult.endTime if mindelta is None or (stepResult.nextStep is not None and mindelta > stepResult.nextStep): mindelta = stepResult.nextStep ## End loop over subproblems. # Check that all subproblems made it to the target # time. If not, try again with a smaller target time. if mintime != targettime: assert mindelta is not None targettime = time + mindelta for subproblem in subprobctxts: subproblem.restoreConstraints() continue # goto "while stepno < maxconsistencysteps... # All subproblems reached the target time. Did each # get the same answer it got last time? consistent = True if len(subprobctxts) > 1: for subproblem in subprobctxts: prev = prevresults.get(subproblem, None) consistent = (consistent and (prev is not None) and subproblem.cmpDoF( prev, subproblem.endValues)) prevresults[subproblem] = subproblem.endValues.clone() if not consistent: for subproblem in subprobctxts: subproblem.restoreConstraints() continue # goto "while stepno < maxconsistencysteps... # All subproblems are consistent. Go on to the next time. time = targettime delta = mindelta stepTaken = True prog.time(time, delta=mindelta) for subproblem in subprobctxts: linsysDict[subproblem] = newlinsys[subproblem] del newlinsys[subproblem] subproblem.solverStats.stepTaken(mindelta, truncated_step) # Copy values at targettime into DoFs. Set # startValues to current endValues. Set # subproblem.startTime to current time, and unset # subproblem.endTime. subproblem.moveOn() subproblem.finalizeConstraints(time) if truncated_step: meshctxt.setCurrentTime(time, None) else: meshctxt.setCurrentTime(time, mindelta) ## End of consistency loop. if not stepTaken and not prog.stopped(): raise ooferror2.ErrConvergenceFailure( "Self-consistency loop at t=%s" % time, maxconsistencysteps) if meshctxt.outputSchedule.isConditional(): _do_output(meshctxt, time) ## End main loop. if prog.stopped(): raise ooferror2.ErrInterrupted() except ooferror2.ErrInterrupted: debug.fmsg("Interrupted!") meshctxt.setStatus(meshstatus.Failed("Solution interrupted.")) raise except ooferror2.ErrInstabilityError, err: debug.fmsg("Instability detected: ", err) meshctxt.setStatus( meshstatus.Failed("Solution diverged? " + err.summary())) raise
def evolve(meshctxt, endtime): global linsys_dict starttime = meshctxt.getObject().latestTime() # We're solving a static problem if endtime is the same as the # current time, or if there are no non-static steppers and output # is requested at at single time. staticProblem = (starttime == endtime or (not meshctxt.timeDependent() and meshctxt.outputSchedule.isSingleTime())) # "continuing" is true if we're continuing an earlier time # evolution, in which case we can assume that all Fields have # their correct initial values. "continuing" is never true for # static problems. continuing = (not staticProblem and isinstance(meshctxt.status, meshstatus.Solved)) targettime = endtime if starttime > endtime: raise ooferror2.ErrSetupError( "End time must not precede current time.") meshctxt.solver_precompute(solving=True) meshctxt.setStatus(meshstatus.Solving()) meshctxt.timeDiff = endtime - starttime # used to get next endtime in GUI # Make sure that the starting point has been cached. ## TODO OPT: Is it necessary to call cacheCurrentData here? meshctxt.restoreLatestData() meshctxt.cacheCurrentData() meshctxt.releaseLatestData() meshctxt.outputSchedule.reset(starttime, continuing) prog = ProgressData(starttime, endtime, progress.getProgress("Solving", progress.DEFINITE)) try: # Get an ordered list of subproblems to be solved. First, # create tuples containing a subproblem and its solveOrder. subprobctxts = [(s.solveOrder, s) for s in meshctxt.subproblems() if s.time_stepper is not None and s.solveFlag] subprobctxts.sort() # sort by solveOrder subprobctxts = [s[1] for s in subprobctxts] # strip solveOrder # Initialize statistics. for subp in subprobctxts: subp.resetStats() if not continuing: # Initialize static fields in all subproblems. For static # problems, this computes the actual solution. try: linsys_dict = initializeStaticFields(subprobctxts, starttime, prog) # Initial output comes *after* solving static fields. # For fully static problems, this is the only output. _do_output(meshctxt, starttime) except ooferror2.ErrInterrupted: raise except ooferror2.ErrError, exc: meshctxt.setStatus(meshstatus.Failed(exc.summary())) raise except Exception, exc: meshctxt.setStatus(meshstatus.Failed( ` exc `)) raise
for subp in subprobctxts ]) try: time, delta, linsys_dict = evolve_to( meshctxt, subprobctxts, time=time, endtime=t1, delta=delta, prog=prog, linsysDict=linsys_dict) except ooferror2.ErrInterrupted: # Interruptions shouldn't raise an error dialog debug.fmsg("Interrupted!") meshctxt.setStatus( meshstatus.Failed("Solution interrupted.")) break meshctxt.solverDelta = delta if time < t1: meshctxt.setStatus( meshstatus.Failed( "Solver failed to reach the target time.")) raise ooferror2.ErrSetupError( "Failed to reach target time. target=%s, actual=%s" % (t1, time)) if not meshctxt.outputSchedule.isConditional(): # If there are conditional outputs, _do_output was # already called by evolve_to(). _do_output(meshctxt, t1) if t1 == endtime: meshctxt.setStatus(meshstatus.Solved())