Ejemplo n.º 1
0
def test_str_of_SI_vector():
    H_ext=[SI(1000000,['m',-1.0,'A',1.0]), SI(0,['m',-1.0,'A',1.0]), SI(0,['m',-1.0,'A',1.0])]
    assert str_of_SI_vector([SI(0, "A/m"), SI(100, "A/m")]) == "[0 100]<A/m>"
    assert str_of_SI_vector([0, SI(123, "A/m")]) == "[0 123]<A/m>"
    assert str_of_SI_vector([0, SI(123, "A/m"), 0]) == "[0 123 0]<A/m>"
    assert str_of_SI_vector(H_ext) == "[1e+06 0 0]<A/m>"
    assert str_of_SI_vector([SI(1,'A/m')]) == "[1]<A/m>"
    assert str_of_SI_vector(None) == "None"
Ejemplo n.º 2
0
def _update_progress_file(self, H_ext, progress_file_name,
                          progress_message_minimum_delay):
    if true_at_most_every_n_seconds('hysteresis_reporting',
                                    progress_message_minimum_delay):
        tmpf = open(progress_file_name, 'w')
        tmpf.write(time.asctime() + '\n')
        tmpf.write(str(self.clock))
        tmpf.write("\n")
        tmpf.write(self.convergence.get_log())
        tmpf.close()

        log.info("it %d, time %s; stage %d; H_ext=%s"
                 % (self.clock.step,
                    self.clock.time_reached_si.dens_str(),
                    self.clock.stage,
                    str_of_SI_vector(H_ext)))
Ejemplo n.º 3
0
def test_str_of_SI_vector():
    H_ext = [
        SI(1000000, ['m', -1.0, 'A', 1.0]),
        SI(0, ['m', -1.0, 'A', 1.0]),
        SI(0, ['m', -1.0, 'A', 1.0])
    ]
    assert str_of_SI_vector([SI(0, "A/m"), SI(100, "A/m")]) == "[0 100]<A/m>"
    assert str_of_SI_vector([0, SI(123, "A/m")]) == "[0 123]<A/m>"
    assert str_of_SI_vector([0, SI(123, "A/m"), 0]) == "[0 123 0]<A/m>"
    assert str_of_SI_vector(H_ext) == "[1e+06 0 0]<A/m>"
    assert str_of_SI_vector([SI(1, 'A/m')]) == "[1]<A/m>"
    assert str_of_SI_vector(None) == "None"
Ejemplo n.º 4
0
def simulation_hysteresis(self, H_ext_list,
                          save=[('averages', 'fields', at('stage_end'))],
                          do=[],
                          convergence_check=every('step', 5),
                          progress_message_minimum_delay=60):

    #Note we are using __argdoclong__ here (below this function).

    """
    This method executes a simulation where the applied field
    is set in sequence to the values specified in ``H_ext_list``.
    The time integration proceeds with the same applied field
    until convergence is reached. At this point the field is changed
    to the next one in ``H_ext_list`` and the method ``reinitialise()``
    is called to proceed with the simulation.
    The user can specify when to save data using the optional
    argument ``save``.

    This allows to carry out hysteresis loop computations
    and write the results to disk.

    Technically we say that this function performs a multi-stage
    simulation. In our terminology, a stage is a part of the simulation
    where the field does not change. Therefore, every value
    for the applied field specified in ``H_ext_list`` corresponds
    to a different stage. Stages are numbered starting from 1,
    which corresponds to ``H_ext_list[0]``. In general during
    stage number ``i`` the applied field is ``H_ext_list[i-1]``.

    :Parameters:
      `H_ext_list` : list of values for the applied field
        It is something like ``[H1, H2, H3, ...]``, where
        ``Hi`` is the triple of components of the applied field,
        i.e. SI objects having units of "A/m";

      `save` : list of pairs ``(thing_to_save, when)``
        ``thing_to_save`` is either a string or a function provided
        by the user and ``when`` is an instance of the class ``When``,
        i.e. an object which contains the specification of when
        "the thing" has to be saved.

        Possible string values for ``thing_to_save`` are:

          - ``"averages"``: to save the averages of all the fields
            together with other information (such as the stage number,
            the time reached, etc.). This is done calling the method
            ``save_data()``. Refer to its documentation
            for further details;
          - ``"fields"``: to save all the fields. The method
            ``save_data(fields='all')`` is called for this purpose;
          - ``"restart"``: to save the current magnetisation configuration
            and all the information needed to restart the simulation.

      `do` : list of pairs ``(thing_to_do, when)``
        is very similar to the ``save`` argument, but is usually used
        for other purposes.
        ``thing_to_do`` is either a string or a function provided
        by the user and ``when`` is an instance of the class ``When``.

        Possible string values for ``thing_to_do`` are:

          - ``"next_stage"``: induces the hysteresis method to advance
            to the next stage;
          - ``"exit"``: induces the hysteresis method to exit,
            even if the hysteresis computation has not still reached
            its end.

        The user can provide his own function to save data.
        For example, the following three lines::

          def my_fun(sim):
            sim.save_data()
          sim.hysteresis(..., save=[(my_fun, every('step', 10))])

        are equivalent to::

          sim.hysteresis(..., save=[('averages', every('step', 10))])

        To specify when something has to be saved the module ``when``
        is used. The functions ``at`` and ``every``, provided by
        this module, can refer to the following time variables:

          - ``step``: the step number from the beginning of the simulation;
          - ``stage_step``: the step number from the beginning of
            the current stage;
          - ``time``: the simulation time passed from the beginning
            of the simulation (measured in SI_ objects);
          - ``stage_time``: the simulation time passed from the beginning
            of the current stage;
          - ``stage``: the number of the current stage;
          - ``convergence``: a boolean value which is ``True``
            if the convergence criterion is satisfied.
            Use in this way ``at('convergence')``

        Remember that you can combine time specifications using
        the operator | (or) and & (and)::

          every('stage', 2) & at('convergence') --> only at convergence
                                                    of odd stages
          every('step', 10) | at('convergence') --> at convergence
                                                    and every 10 steps.

        Some usage examples::

          # Save fields (which implicitly will save the averages as well)
          # when the magnetisation stops changing for each applied field
          # (i.e. save at convergence):
          sim.hysteresis(..., save=[('fields', at('convergence'))])

          # Averages will be saved every 10 steps, fields (and
          # implicitely averages) will be saved at convergence.
          sim.hysteresis(..., save=[('averages', every('step', 10)),
                                    ('fields', at('convergence'))])

          # Each stage will not last more than 10 ps, even
          # if the magnetisation is not relaxed yet.
          sim.hysteresis(..., do=[('next_stage', at('stage_time', SI(1e-11, "s")))])

          # Exit hysteresis loop simulation if the total number of
          # steps exceeds 1e6, save fields every 100 steps and at
          # convergence before that:
          sim.hysteresis(..., save=[('fields', every('step', 100) |
                                    at('convergence'))],
                               do =[('exit', at('step', 1e6))])

          # Save averages every 0.1 ns (useful for fourier transform)
          # leave after 20 ns (using the related relax_ command)
          sim.relax(save=[('averages', every('time', SI(1e-10, 's')))],
                    do  =[('exit', at('time', SI(20e-9, 's')))])

          # Save averages every nanosecond, and fields every 100 ns.
          sim.relax(save=[('averages',every('time', SI(1e-9, 's'))),
                          ('fields',  every('time', SI(100e-9,'s')))])

          # Save averages every nanosecond, and fields every 100 ns,
          # save restart file every 1000 steps
          sim.relax(save=[('averages',every('time', SI(1e-9, 's'))),
                          ('fields',  every('time', SI(100e-9, 's'))),
                          ('restart', every('step', 1000))])

        If ``save`` is not given, averages and fields will be saved whenever
        the stage ends (this is the default behaviour).
    """

    log.debug("simulation_hysteresis(): Entering with H_ext_list=%s, "
              "save=%s, do=%s, convergence_check=%s"
	      % (H_ext_list,save,do,convergence_check))

    # This function will check for the correctness of the specifications
    # for the list of (thing to save, when) and (thing to do, when).
    # It will return a joint list of tuples (fn, when), where fn is a function
    # to be called with the simulation object as argument.
    thing_when_tuples = \
      _join_save_and_do_lists(save, do,
                              predefined_actions=self.action_abbreviations)

    log.debug("simulation_hysteresis(): thing_when_tuples=%s"
              % thing_when_tuples)

    # Dictionary containing the predicted time for the next saving
    # for each item specified inside the optional argument 'save'
    next_save_time = {}
    for what, _ in thing_when_tuples:
        key = str(what) # If what is a function we convert it
                        # to string and then use it as the key
        if key in next_save_time:
            msg = (
              "Error in optional argument 'save' or 'do' of method "
              "'hysteresis': the list of (thing_to_save, when) "
              "contains two or more specifications for "
              "thing_to_save = %s. You should remove the duplicate "
              "entry and eventually use the operator | (such as in: "
              "(thing_to_save, when1 | when2))." % key)
            raise NmagUserError, msg
        next_save_time[key] = None

    # We need this in comparisons of times (this is to solve
    # bugs in comparisons related to truncation errors)
    negligible_time = SI(1e-20, "s")
    match_tolerances = {'time': negligible_time,
                        'stage_time': negligible_time}
    def my_next_time(event, clock):
        return _next_time(event, clock, tols=match_tolerances)

    def my_next_deltas(event, clock, suggest=None):
        return _next_deltas(event, clock, suggest=suggest,
                            tols=match_tolerances)

    progress_file_name = self.name + "_progress.txt"

    # Continue from the last restart file if required
    if self._restarting:
        log.info("Hysteresis loop: restarting from a previously saved "
                 "configuration...")
        self.load_restart_file()
        self._restarting = False # To avoid affecting next calls to hysteresis
    else:
        log.info("Hysteresis loop: starting a new simulation.")
        log.info("Hysteresis loop: check file '%s' for progress data"
                 % progress_file_name)

    # Loop over the given fields
    stage = self.clock.stage
    self.clock.exit_hysteresis = False
    for H_ext in H_ext_list[stage-1:]:
        log.info("hysteresis: starting new stage: field = %s"
                 %  str_of_SI_vector(H_ext))
        self.do_next_stage(stage=stage)
        stage = None # Next time, just increase the stage counter
        self.clock.stage_end = False
        if H_ext:
          self.set_H_ext(H_ext)
        self.reinitialise(initial_time=0)

        # First of all we run over the list of things to save
        # and take note about when we should save what
        for what, when in thing_when_tuples:
            key = str(what)
            next_save_time[key] = my_next_time(when, self.clock)
            log.debug("hysteresis: will save %s at %s" %
                      (what, next_save_time[key]))
            #if when.match_time(self.clock1): (get_saver(what))(self)

        # Simulate one stage: loop until convergence!
        # NOTE: we avoid something like 'while self.converged()',
        #       because we want to check for saving fields
        #       if convergence is reached!
        while True:
            self.clock.stage_end = converged = self.is_converged()
            log.debug("hysteresis loop, stage %d, converged = %s"
                      % (self.clock.stage, str(converged)))
            # Find out the next time we need to check for convergence
            deltas = my_next_deltas(convergence_check, self.clock)
            log.debug("Time to next event: deltas = %s", str(deltas))

            # We now see what needs to be saved. The strategy
            # is the following: if the expected time for saving
            # has changed, then it means that we need to save.
            # time passed the time scheduled for saving!
            for what, when in thing_when_tuples:
                key = str(what)
                time_matches = when.match_time(self.clock)
                nst = my_next_time(when, self.clock)
                # BUG: the comparison nst != next_save_time[key]
                #      will misteriously fail sometimes. The reason
                #      is that (a - b) + b != a for some a and b!
                #      This is due to truncation errors!
                if time_matches or nst != next_save_time[key]:
                    log.debug("hysteresis: analysing %s: time planned "\
                              "for saving was %s, now is %s. Matching? %s"
                              % (what, str(next_save_time[key]),
                              str(nst), str(time_matches)))

                    log.info("hysteresis: saving %s at id=%s,step=%s.\n%s"
                             % (what, self.clock.id,
                                self.clock.step, str(self.clock)))
                    what(self)

                next_save_time[key] = nst
                deltas = my_next_deltas(when, self.clock, suggest=deltas)
                # NOTE: we are doing two times the call to the method
                #       'next_time' of the class When: this is not ideal!

            (delta_step, delta_time, delta_real_time) = deltas
            log.debug("hysteresis: current time is %s"
                      % (str(self.clock.time)))
            log.debug("predicted advance: "
                      "delta_step=%s, delta_time=%s, delta_real_time=%s"
                      % (str(delta_step), str(delta_time),
                          str(delta_real_time)))

            if delta_time == None:
                # This is not ideal, we want to run forever,
                # but the advance_time method does not allow
                # such a speficication!
                target_time = self.max_time_reached
            else:
                target_time = self.clock.stage_time + delta_time

                if delta_step == None: delta_step = -1

            if self.clock.exit_hysteresis:
                log.debug("Exit from the hysteresis loop has been forced "
                          "using the tag 'exit': exiting now!")
                return

            if self.clock.stage_end:
                log.debug("Reached end of stage in hysteresis command, "
                          "converged=%s, exiting now!" % converged)
                break

            log.debug("About to call advance time with target_time=%s "
                      "and max_it=%s" % (str(target_time),delta_step))
            time_reached = self.advance_time(target_time, max_it=delta_step)
            if time_reached > 0.99*self.max_time_reached:
                msg = ("Simulation time reached %s: are you starting from "
                       "a zero torque configuration?" % self.max_time_reached)
                raise NmagUserError, msg

            # Write some progress data into progress file
            try:
                _update_progress_file(self, H_ext, progress_file_name,
                                      progress_message_minimum_delay)
            except Exception as excp:
                log.info("Problem when generating progress file. "
                         "Got exception: %s." % str(excp))