Ejemplo n.º 1
0
    def solve(self, matrix_method, precompute, compute_residual,
              compute_jacobian, compute_linear_coef_mtx, data, values):

        # matrix_method is function that takes a matrix A, a rhs b and
        # a vector of unknows x and sets x so that A*x = b.

        # 'data' is user defined object that can be used to pass
        # information from the calling function to the callback
        # functions if necessary.  The callback functions are
        # precompute, compute_residual, and compute_jacobian.

        # precompute will be called before calling compute_residual
        # and compute_jacobian, and should perform any calculations
        # shared by those two functions.  Its arguments are 'data'
        # (the user defined object), 'values' (a DoubleVec containing
        # a trial solution) and 'needJacobian' (a boolean indicating
        # whether or not the Jacobian needs to be recomputed).

        # compute_residual is called only after precompute.  Its
        # arguments are 'data' (the same object that was used for
        # precompute), 'values' (the trial solution), and residual
        # (the resulting vector of residuals).

        # compute_jacobian is also called only after precompute.  It
        # only takes the 'data' argument.

        # TODO OPT: The vectors and matrices computed by
        # compute_residual, compute_jacobian, and
        # compute_linear_coef_mtx can be preallocated here and passed
        # to the functions, instead of being repeatedly reallocated on
        # each function call.

        # debug.fmsg("initial values=", values.norm())
        n = values.size()

        update   = doublevec.DoubleVec(n)

        # compute the residual = -K*startValues + rhs
        self.requireResidual(True)
        self.requireJacobian(True)
        precompute(data, values, self)
        residual = compute_residual(data, values, self)

        res_norm0 = residual.norm() # norm of the initial residual
        res_norm  = res_norm0       # we will keep comparing current residual
                                    # with res_norm0 to judge convergence
        # debug.fmsg("initial residual:", res_norm0)

        prog = progress.getProgress("Newton Solver", progress.LOGDEFINITE)
        target_res = self.relative_tolerance*res_norm0 + self.absolute_tolerance
        if res_norm0 > target_res:
            prog.setRange(res_norm0, target_res)
        try:
            # compute Newton updates while residual is large and
            # self.maximum_iterations is not exceeded
            s = 1.
            i = 0
            while (res_norm > target_res and i < self.maximum_iterations
                   and not prog.stopped()):
                # debug.fmsg("iter =", i, ",  res =", res_norm, " s =", s)
                update.zero()
                # solve for the Newton step:  Jacobian * update = -residual
                J = compute_jacobian(data, self)
                # debug.fmsg("J=\n", J.norm())
                residual *= -1.0
                matrix_method.solve( J, residual, update )
                # debug.fmsg("update=", update.norm())

                # choose step size for the Newton update.  This resets
                # self.requireXXX.
                s, residual = self.choose_step_size(
                    data, values, update, res_norm,
                    precompute, compute_residual)
                # debug.fmsg("s=", s)
                # correct the soln with the Newton update
                values += s * update

                res_norm = residual.norm()
                if res_norm <= target_res:
                    break

                # update the linear system
                self.requireJacobian(True)
                self.requireResidual(True)
                # debug.fmsg("norm updated values=", values.norm())
                precompute(data, values, self)
                # compute the residual
                residual = compute_residual(data, values, self)
                res_norm = residual.norm()
                #debug.fmsg("Current residual: [%s] (%g)" %(residual, res_norm))
                # debug.fmsg("new residual =", res_norm)
                prog.setMessage("%g/%g" % (res_norm, target_res))
                prog.setFraction(res_norm)

                i += 1
                # end of Newton iterations

            if prog.stopped():
                prog.setMessage("Newton solver interrupted")
                #progress.finish()
                raise ooferror2.ErrInterrupted();
        finally:
            prog.finish()
        # raise error if Newton's method did not converge in maximum_iterations
        if i >= self.maximum_iterations and res_norm > target_res:
            raise ooferror2.ErrConvergenceFailure(
                'Nonlinear solver - Newton iterations', self.maximum_iterations)
        # debug.fmsg("final values=", values)
        # debug.fmsg("-------------------")
        return i, res_norm
Ejemplo n.º 2
0
    def solve(self, matrix_method, precompute, compute_residual,
              compute_jacobian, compute_linear_coef_mtx, data, values):
        # initialize: set soln = startValues, update = 0
        # debug.fmsg("-----------------------------")
        # debug.fmsg("initial values=", values.norm())
        n = values.size()
        update   = doublevec.DoubleVec(n)
        # residual = doublevec.DoubleVec(n)

        # compute the residual = -K*startValues + rhs
        self.requireResidual(True)
        self.requireJacobian(False)
        precompute(data, values, self)
        residual = compute_residual(data, values, self)

        res_norm0 = residual.norm() # this is the norm of the initial residual
        # debug.fmsg("res_norm0=%g:" % res_norm0)
        res_norm  = res_norm0       # we will keep comparing current residual
                                    # with res_norm0 to judge convergence

        target_res = self.relative_tolerance*res_norm0 + self.absolute_tolerance
        prog = progress.getProgress('Picard Solver', progress.LOGDEFINITE)
        if res_norm > target_res:
            prog.setRange(res_norm0, target_res)

        try:
            # compute Picard updates while residual is large and
            # self.maximum_iterations is not exceeded
            s = 1.0
            i = 0
            while (res_norm > target_res and i < self.maximum_iterations
                   and not prog.stopped()):
                # debug.fmsg("iteration %d" % i)
                update.zero()  # start with a zero update vector

                # solve for the Picard step:  K * update = -residual
                K = compute_linear_coef_mtx(data, self)
                residual *= -1.0

                # debug.fmsg("residual=", residual.norm())
                matrix_method.solve( K, residual, update )
                # debug.fmsg("update=", update.norm())

                # choose step size for the Picard update
                s, residual = self.choose_step_size(
                    data, values, update, res_norm,
                    precompute, compute_residual)
                # debug.fmsg("line search s=", s)

                # correct the soln with the Picard update
                values += s * update
                res_norm = residual.norm()
                if res_norm <= target_res:
                    break

                # update the linear system
                self.requireResidual(True)
                self.requireJacobian(False)
                precompute(data, values, self)

                # compute the residual
                residual = compute_residual(data, values, self)
                res_norm = residual.norm()
                # debug.fmsg("Current residual:", res_norm)
                prog.setMessage("%g/%g" % (res_norm, target_res))
                prog.setFraction(res_norm)

                i = i+1
                # end of Picard iterations

            if prog.stopped():
                prog.setMessage("Picard solver interrupted")
                #prog.finish()
                raise ooferror2.ErrInterrupted()
        finally:
            prog.finish()

        # debug.fmsg("Done: res_norm=%g" % res_norm)
        # raise error if Picard's iterations did not converge in
        # maximum_iterations
        if i >= self.maximum_iterations and res_norm > target_res:
             raise ooferror2.ErrConvergenceFailure(
                 'Nonlinear solver - Picard iterations',
                 self.maximum_iterations)
        return i, res_norm
Ejemplo n.º 3
0
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