Esempio n. 1
0
    def recv(target, source, tag=None):
        """
        Receive function

        Args:
            target: level which will receive the values
            source: level which initiated the send
            tag: identifier to check if this message is really for me
        """

        if tag is not None and source.tag != tag:
            raise CommunicationError('source and target tag are not the same, got %s and %s' % (source.tag, tag))
        # uend becomes the new u0 at the target
        target.u[0] = target.prob.dtype_u(source.uend)
        # re-evaluate f on left interval boundary
        target.f[0] = target.prob.eval_f(target.u[0], target.time)
Esempio n. 2
0
    def pfasst(self, S, num_procs):
        """
        Main function including the stages of SDC, MLSDC and PFASST (the "controller")

        For the workflow of this controller, check out one of our PFASST talks

        Args:
            S: currently active step
            num_procs: number of active processors

        Returns:
            updated step
        """

        # if S is done, stop right here
        if S.status.done:
            return S

        stage = S.status.stage

        self.logger.debug("Process %2i at stage %s" % (S.status.slot, stage))

        if stage == 'SPREAD':
            # first stage: spread values
            self.hooks.pre_step(step=S, level_number=0)

            # call predictor from sweeper
            S.levels[0].sweep.predict()

            # update stage
            if len(S.levels) > 1 and self.params.predict:  # MLSDC or PFASST with predict
                S.status.stage = 'PREDICT_RESTRICT'
            else:  # SDC
                S.status.stage = 'IT_CHECK'

            return S

        elif stage == 'PREDICT_RESTRICT':
            # call predictor (serial)

            # go to coarsest level via transfer

            for l in range(1, len(S.levels)):
                S.transfer(source=S.levels[l - 1], target=S.levels[l])

            # update stage and return
            S.status.stage = 'PREDICT_SWEEP'
            return S

        elif stage == 'PREDICT_SWEEP':

            # do a (serial) sweep on coarsest level

            # receive new values from previous step (if not first step)
            if not S.status.first:
                if S.prev.levels[-1].tag:
                    self.logger.debug('Process %2i receives from %2i on level %2i with tag %s -- PREDICT' %
                                      (S.status.slot, S.prev.status.slot, len(S.levels) - 1, True))
                    self.recv(S.levels[-1], S.prev.levels[-1])
                    # reset tag to signal successful receive
                    S.prev.levels[-1].tag = False

            # do the sweep with (possibly) new values
            S.levels[-1].sweep.update_nodes()

            # update stage and return
            S.status.stage = 'PREDICT_SEND'
            return S

        elif stage == 'PREDICT_SEND':
            # send updated values on coarsest level

            # send new values forward, if previous send was successful (otherwise: try again)
            if not S.status.last:
                if not S.levels[-1].tag:
                    self.logger.debug('Process %2i provides data on level %2i with tag %s -- PREDICT'
                                      % (S.status.slot, len(S.levels) - 1, True))
                    self.send(S.levels[-1], tag=True)
                else:
                    S.status.stage = 'PREDICT_SEND'
                    return S

            # decrement counter to determine how many coarse sweeps are necessary
            S.status.pred_cnt -= 1

            # update stage and return
            if S.status.pred_cnt == 0:
                S.status.stage = 'PREDICT_INTERP'
            else:
                S.status.stage = 'PREDICT_SWEEP'
            return S

        elif stage == 'PREDICT_INTERP':
            # prolong back to finest level

            for l in range(len(S.levels) - 1, 0, -1):
                S.transfer(source=S.levels[l], target=S.levels[l - 1])

            # update stage and return
            S.status.stage = 'IT_CHECK'
            return S

        elif stage == 'IT_CHECK':

            # check whether to stop iterating

            S.levels[0].sweep.compute_residual()
            S.status.done = self.check_convergence(S)

            if S.status.iter > 0:
                self.hooks.post_iteration(step=S, level_number=0)

            # if the previous step is still iterating but I am done, un-do me to still forward values
            if not S.status.first and S.status.done and (S.prev.status.done is not None and not S.prev.status.done):
                S.status.done = False

            # if I am done, signal accordingly, otherwise proceed
            if S.status.done:
                S.levels[0].sweep.compute_end_point()
                self.hooks.post_step(step=S, level_number=0)
                S.status.stage = 'DONE'
            else:
                # increment iteration count here (and only here)
                S.status.iter += 1
                self.hooks.pre_iteration(step=S, level_number=0)
                if len(S.levels) > 1:
                    S.status.stage = 'IT_UP'
                else:  # SDC
                    S.status.stage = 'IT_FINE_SWEEP'
            # return
            return S

        elif stage == 'IT_FINE_SWEEP':
            # do sweep on finest level

            # standard sweep workflow: update nodes, compute residual, log progress
            self.hooks.pre_sweep(step=S, level_number=0)

            for k in range(S.levels[0].params.nsweeps):
                S.levels[0].sweep.update_nodes()
            S.levels[0].sweep.compute_residual()
            self.hooks.post_sweep(step=S, level_number=0)

            # update stage and return
            S.status.stage = 'IT_FINE_SEND'

            return S

        elif stage == 'IT_FINE_SEND':
            # send forward values on finest level

            # if last send succeeded on this level or if last rank, send new values (otherwise: try again)
            if not S.levels[0].tag or S.status.last or S.next.status.done:
                if self.params.fine_comm:
                    self.logger.debug('Process %2i provides data on level %2i with tag %s'
                                      % (S.status.slot, 0, True))
                    self.send(S.levels[0], tag=True)
                S.status.stage = 'IT_CHECK'
            else:
                S.status.stage = 'IT_FINE_SEND'
            # return
            return S

        elif stage == 'IT_UP':
            # go up the hierarchy from finest to coarsest level

            S.transfer(source=S.levels[0], target=S.levels[1])

            # sweep and send on middle levels (not on finest, not on coarsest, though)
            for l in range(1, len(S.levels) - 1):
                self.hooks.pre_sweep(step=S, level_number=l)
                for k in range(S.levels[l].params.nsweeps):
                    S.levels[l].sweep.update_nodes()
                S.levels[l].sweep.compute_residual()
                self.hooks.post_sweep(step=S, level_number=l)

                # send if last send succeeded on this level (otherwise: abort with error)
                if not S.levels[l].tag or S.status.last or S.next.status.done:
                    if self.params.fine_comm:
                        self.logger.debug('Process %2i provides data on level %2i with tag %s'
                                          % (S.status.slot, l, True))
                        self.send(S.levels[l], tag=True)
                else:
                    raise CommunicationError('Sending failed on process %2i, level %2i' % (S.status.slot, l))

                # transfer further up the hierarchy
                S.transfer(source=S.levels[l], target=S.levels[l + 1])

            # update stage and return
            S.status.stage = 'IT_COARSE_RECV'
            return S

        elif stage == 'IT_COARSE_RECV':

            # receive on coarsest level

            # rather complex logic here...
            # if I am not the first in line and if the first is not done yet, try to receive
            # otherwise: proceed, no receiving possible/necessary
            if not S.status.first and not S.prev.status.done:
                # try to receive and the progress (otherwise: try again)
                if S.prev.levels[-1].tag:
                    self.logger.debug('Process %2i receives from %2i on level %2i with tag %s' %
                                      (S.status.slot, S.prev.status.slot, len(S.levels) - 1, True))
                    self.recv(S.levels[-1], S.prev.levels[-1])
                    S.prev.levels[-1].tag = False
                    if len(S.levels) > 1 or num_procs > 1:
                        S.status.stage = 'IT_COARSE_SWEEP'
                    else:
                        raise ControllerError('Stage unclear after coarse send')
                else:
                    S.status.stage = 'IT_COARSE_RECV'
            else:
                if len(S.levels) > 1 or num_procs > 1:
                    S.status.stage = 'IT_COARSE_SWEEP'
                else:
                    raise ControllerError('Stage unclear after coarse send')
            # return
            return S

        elif stage == 'IT_COARSE_SWEEP':
            # coarsest sweep

            # standard sweep workflow: update nodes, compute residual, log progress
            self.hooks.pre_sweep(step=S, level_number=len(S.levels) - 1)
            for k in range(S.levels[-1].params.nsweeps):
                S.levels[-1].sweep.update_nodes()
            S.levels[-1].sweep.compute_residual()
            self.hooks.post_sweep(step=S, level_number=len(S.levels) - 1)

            # update stage and return
            S.status.stage = 'IT_COARSE_SEND'
            return S

        elif stage == 'IT_COARSE_SEND':
            # send forward coarsest values

            # try to send new values (if old ones have not been picked up yet, retry)
            if not S.levels[-1].tag or S.status.last or S.next.status.done:
                self.logger.debug('Process %2i provides data on level %2i with tag %s'
                                  % (S.status.slot, len(S.levels) - 1, True))
                self.send(S.levels[-1], tag=True)
                # update stage
                S.status.stage = 'IT_DOWN'
            else:
                S.status.stage = 'IT_COARSE_SEND'
            # return
            return S

        elif stage == 'IT_DOWN':
            # prolong corrections own to finest level

            # receive and sweep on middle levels (except for coarsest level)
            for l in range(len(S.levels) - 1, 0, -1):

                # if applicable, try to receive values from IT_UP, otherwise abort
                if self.params.fine_comm and not S.status.first and not S.prev.status.done:
                    if S.prev.levels[l - 1].tag:
                        self.logger.debug('Process %2i receives from %2i on level %2i with tag %s' %
                                          (S.status.slot, S.prev.status.slot, l - 1, True))
                        self.recv(S.levels[l - 1], S.prev.levels[l - 1])
                        S.prev.levels[l - 1].tag = False
                    else:
                        raise CommunicationError('Sending failed during IT_DOWN')

                # prolong values
                S.transfer(source=S.levels[l], target=S.levels[l - 1])

                # on middle levels: do sweep as usual
                if l - 1 > 0:
                    self.hooks.pre_sweep(step=S, level_number=l - 1)
                    for k in range(S.levels[l - 1].params.nsweeps):
                        S.levels[l - 1].sweep.update_nodes()
                    S.levels[l - 1].sweep.compute_residual()
                    self.hooks.post_sweep(step=S, level_number=l - 1)

            # update stage and return
            S.status.stage = 'IT_FINE_SWEEP'
            return S

        else:

            raise ControllerError('Unknown stage, got %s' % S.status.stage)