def set_fielddata_from_function(field, subfieldname, function, pos_unit_length, normalise=False, scale_factor=1.0): if type(function) != types.FunctionType: raise NmagUserError("Expect function-object but got type '%s'" % type(function)) log.log( 15, "set_fielddata_from_function: Entering, populate " "subfield '%s' from function '%s'" % (subfieldname, function.__name__)) site_ids, site_pos, shape, site_vols = \ ocaml.mwe_subfield_metadata(field, subfieldname) for pos, site in zip(site_pos, site_ids): # convert position from mesh coordinates to meter pos = [(x * pos_unit_length).in_units_of(SI("m")) for x in pos] # sample function and set field at current site value = function(pos) set_field_at_site(field, subfieldname, site, value, normalise=normalise, scale_factor=scale_factor) log.debug("set_fielddata_from_function: Done.")
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"
def inc_stage(self, stage=None): """Advance the clock to the next stage.""" if stage == None: self.stage += 1 else: self.stage = stage self.stage_step = 0 self.stage_time = SI(0.0, "s") self.convergence = False self.zero_stage_step = self.step self.zero_stage_time = self.time
cut = [] cutxs = None #nsim.logtools.setGlobalLogLevel("debug") #the constants datafile = "t_T_origin_left" f = open(datafile, 'w') datafile2 = "Ts_line.dat" f2 = open(datafile2, 'w') from nsim.si_units import SI Ohm = SI(1, "V / A") m = SI(1, "m") cm = 0.01 * m nm = 1e-9 * m microOhm = 1e-6 * Ohm g = 0.001 * SI(1, "kg") rho_ = 20 * microOhm * cm sigma = 1.0 / rho_ print "electric conductivity:", sigma print "electric resistivity:", rho_ J = nsim.si_units.si.Joule K = nsim.si_units.si.Kelvin W = J / SI(1, "s")
import nfem.visual import ocaml,math from nsim import linalg_machine as nlam import nmesh #nsim.logtools.setGlobalLogLevel("debug") #the constants from nsim.si_units import SI from nsim.su_units import SimulationUnits su = SimulationUnits(scales={'A': 1e-3,'kg': 1e-27,'m': 1e-9,'s': 1e-12}) uOhm_cm = SI(1e-6*1e-2,"Ohm m") J_per_gK = SI(1e3,"J/ kg K") W_per_mK = SI(1,"W/ m K") g_per_cm3 = SI(1e3,"kg / m^3") # Material parameters # # Note that we assume the electrical resistivity of the substrate # to be "infinite", hence, we do not associate electrical degrees # of freedom to it. rho_el_Py = 30e-6*uOhm_cm # "12-45" rho_el_Au = 2.44e*uOhm_cm c_th_m_Sub= 0.5 *J_per_gK c_th_m_Py = 0.45 *J_per_gK
import logging import nsim.logtools import nfem.visual import ocaml, math from nsim import linalg_machine as nlam import nmesh #nsim.logtools.setGlobalLogLevel("debug") #the constants from nsim.si_units import SI Ohm = SI(1, "V / A") m = SI(1, "m") cm = 0.01 * m microOhm = 1e-6 * Ohm g = 0.001 * SI(1, "kg") rho_ = 20 * microOhm * cm sigma = 1.0 / rho_ print "electric conductivity:", sigma J = nsim.si_units.si.Joule K = nsim.si_units.si.Kelvin W = J / SI(1, "s") C = 0.45 * J / (g * K) print "specific heat capacity", C
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))
def __init__(self, name, Ms=SI(0.86e6, "A/m"), # The length of the magnetization # vector M, may be negative (M antiparallel m)! llg_damping=0.5, # The damping coefficient in the LLG equation. llg_gamma_G=SI(2.210173e5, "m/A s"), # This is the constant in front of the LLG equation. # It is often called gyromagnetic ratio, even if usually, # in physics, the gyromagnetic ratio of a particle is # the ratio between its magnetic dipole moment and its # angular momentum (and has units A*s/kg). # It is then an improper nomenclature, but it occours # frequently in the literature. The default value of # llg_gamma_G is 221017.3 m/(A*s) (for more details take # a look at the OOMMF manual, and Werner Scholz's thesis, # after (3.7)). llg_normalisationfactor=SI(0.1e12, "1/s"), # An extra term A m (1 - m*m), where m = M/Ms, is added # to the RHS of the LLG equation to correct numerical errors # in the norm of m. llg_normalisationfactor is # the coefficient A of this term. llg_xi=0.0, # spin-transfer-torque: ratio between the exchange # and the spin-flip relaxation times: xi = tau_ex / tau_sf. llg_polarisation=0.0, # spin-transfer-torque: the polarisation of the spin-current. do_precession = True, # if do_precession == False, then we switch off # the precessional term in the LLG. This can be used # to obtain faster convergence. exchange_coupling=SI(1.3e-11, "J/m"), # the exchange coupling constant. anisotropy=None, # PredefinedAnisotropy object, or function a(m) which returns # an energy density for the given direction of the # magnetisation m. anisotropy_order=None, # Order of approximation; only specify this if you specify an # anisotropy function (as opposed to a PredefinedAnisotropy # object) properties=["magnetic", "material"], scale_volume_charges=1.0, # Parameter to be set by developers for debugging. # To be deleted soon. ): self.name = name self.Ms = Ms self.llg_gamma_G = llg_gamma_G self.llg_damping = llg_damping self.llg_normalisationfactor = llg_normalisationfactor self.llg_xi = llg_xi self.llg_polarisation = llg_polarisation self.do_precession = do_precession self.properties = properties self.exchange_coupling = exchange_coupling self.scale_volume_charges = scale_volume_charges # Let's whether we got the right units one = SI(1) units = (("Ms", SI("A/m")), ("llg_gamma_G", SI("m/A s")), ("llg_damping", one), ("llg_normalisationfactor", SI("1/s")), ("llg_xi", one), ("llg_polarisation", one), ("exchange_coupling", SI("J/m"))) for name, unit in units: value = getattr(self, name) if not unit.is_compatible_with(value): raise NmagUserError("The argument '%s' of MagMaterial " "requires values with unit of %s" % (name, unit)) if isinstance(anisotropy, PredefinedAnisotropy): if anisotropy_order: raise NmagUserError("Cannot specify custom anisotropy_order " "when using a predefined anisotropy.") anisotropy_order = anisotropy.order anisotropy = anisotropy.function elif anisotropy != None or anisotropy_order != None: # At this point we must import anisotropy5 (and nsim.model) from anisotropy5 import Anisotropy if isinstance(anisotropy, Anisotropy): pass else: if anisotropy and not anisotropy_order: raise \ NmagUserError("You need to specify the " "anisotropy_order when using a custom " "anisotropy function.") self.anisotropy = anisotropy self.anisotropy_order = anisotropy_order # compute thermal factor (gets multiplied by T/(dV*dt) later) self.thermal_factor = (2.0*si.boltzmann_constant*self.llg_damping)/(-si.gamma0*si.mu0*self.Ms) # Here we calculate the parameters in simulation units # XXX NOTE: the user cannot modify self.llg_damping alone!!! # we should provide properties to change these values, in such a way # that the corresponding _su values will be immediately computed. #gilbert_to_ll = 1.0/(1.0+self.su_llg_damping**2) #self.su_llg_coeff1 = -self.su_llg_gamma_G*gilbert_to_ll #self.su_llg_coeff2 = self.su_llg_coeff1*self.su_llg_damping if self.do_precession == False: log.info ("Setting su_llg_coeff1 to zero; thus no precession for material '%s'" % self.name) self.su_llg_coeff1 = 0.0 if self.exchange_coupling < 0.0: raise NmagUserError("The exchange coupling constant " + \ "must be positive. For material '%s', you specified: %s." \ % (self.name, self.exchange_coupling)) #self.su_anisotropy = None #if self.anisotropy: # def su_anisotropy(m): # return simulation_units.of(self.anisotropy(m), compatible_with=SI("J/m^3")) #self.su_anisotropy = su_anisotropy self.extended_print = False log.info("Created new Material:\n %s " % str(self))
class SimulationClock(object): # Initial values for all the members of the clock zero_vals = \ { "id": -1, "stage": 1, "step": 0, "time": SI(0.0, "s"), "stage_step": 0, "stage_time": SI(0.0, "s"), "real_time": SI(0.0, "s"), "stage_end": False, "convergence": False, "exit_hysteresis": False, "zero_stage_time": SI(0.0, "s"), "zero_stage_step": 0, "time_reached_su": 0., "time_reached_si": SI(0.0, "s"), "last_step_dt_su": 0., "last_step_dt_si": SI(0.0, "s")} """ This object specifies all the parameters which define the current time in the simulation, such as the simulation time, step number, ... In particular: id : unique identifier for data saved. Everytime any data is saved, this number will be increased by one. stage: the stage number. The stage counter increases whenever the field is changed; step: the step number. The total number of steps performed by this instance of the simulation (always increases); stage_step: step number from the beginning of the current stage; zero_stage_step: the value of 'step' at the beginning of the stage; time: the simulation time. The total simulation time which was simulated by this instance of the simulator (always increases) stage_time: the simulation time from the beginning of the stage; zero_stage_time: the value of 'time' at the beginning of the stage; real_time: the real time used for advancing time; last_step_dt: last time step's length """ def __init__(self, **args): # Initialise the clock object zero_vals = SimulationClock.zero_vals for key, zero in zero_vals.iteritems(): setattr(self, key, args.get(key, zero)) # Make sure args contains only known arguments for key in args: if key not in zero_vals: raise TypeError("SimulationClock got an unexpected argument " "'%s'" % key) def __repr__(self): return ("SimulationClock(%s)" % ", ".join(["%s=%s" % (key, repr(getattr(self, key))) for key in self.zero_vals])) def __getitem__(self, item_key): # Just for compatibility. Should be removed later on return getattr(self, item_key) def __setitem__(self, item_key, value): # Just for compatibility. Should be removed later on return setattr(self, item_key, value) def inc_stage(self, stage=None): """Advance the clock to the next stage.""" if stage == None: self.stage += 1 else: self.stage = stage self.stage_step = 0 self.stage_time = SI(0.0, "s") self.convergence = False self.zero_stage_step = self.step self.zero_stage_time = self.time def __str__(self): ft = fmt_time rows = ((("ID", None, self.id), ("Step", None, self.step), ("Time", ft, self.time), ("Last step size", ft, self.last_step_dt_si)), #("Real time", None, "N/A")), (None, ("Stage", None, self.stage), ("Stage-step", None, self.stage_step), ("Stage-time", ft, self.stage_time)), (None, ("Convergence", None, self.convergence), ("Stage-end", None, self.stage_end), ("Exit hysteresis", None, self.exit_hysteresis))) row_strs = [] col_widths = [] for row in rows: col_strs = [] for nr_col, col in enumerate(row): desc, fmt, value = col if col != None else ("", None, "") value_str = fmt(value) if fmt != None else str(value) col_str = (desc, value_str) col_strs.append(col_str) col_width = len(col_str) if nr_col < len(col_widths): wdesc, wvalue = col_widths[nr_col] col_widths[nr_col] = \ (max(wdesc, len(desc)), max(wvalue, len(value_str))) else: assert nr_col == len(col_widths) col_widths.append((len(desc), len(value_str))) row_strs.append(col_strs) s = "" width = 0 for row_str in row_strs: line = "" for nr_col, col in enumerate(row_str): sep = "=" if len(col[0]) > 0 else " " desc = col[0].rjust(col_widths[nr_col][0]) value = col[1].ljust(col_widths[nr_col][1]) line += "%s%s%s | " % (desc, sep, value) s += line + "\n" width = max(width, len(line)) sep = "="*width return "%s\n%s%s" % (sep, s, sep)
def fmt_time(t, fmt_ps="%.2f", fmt_ns="%.2f"): t_ps = float(t/SI(1e-12, "s")) return ("%s ps" % (fmt_ps % t_ps) if t_ps < 100.0 else "%s ns" % (fmt_ns % (t_ps/1000.0)))
from nsim import linalg_machine as nlam import nmesh import numpy #nsim.logtools.setGlobalLogLevel("debug") #the constants datafile="t_T_origin" f=open(datafile,'w') from nsim.si_units import SI Ohm = SI(1,"V / A") m = SI(1,"m") cm = 0.01*m nm = 1e-9*m microOhm = 1e-6*Ohm g = 0.001*SI(1,"kg") rho_ = 20*microOhm*cm sigma = 1.0/rho_ print "electric conductivity:",sigma print "electric resistivity:",rho_ J = nsim.si_units.si.Joule K = nsim.si_units.si.Kelvin W = J/SI(1,"s")
def get_ndt_columns(self): '''This function returns the data that normally should go into the NDT file together with a corresponding description (a Quantity object). The function returns a pair (columns, quantities), where: - columns is a list of pairs (data_name, data_value), where data_name is the name of the data (such as 'time' or 'M_Py_0'), while data_value is the corresponding value including the units, as an SI object, if possible. such as SI(1e6, 'A/m'). - quantities is a list having the same size of ``columns``. quantities[i] is a Quantity object describing the entry columns[i]. ''' # These quantities are not dependent on the discretisation lt = time.localtime() lt_str = ("%04d/%02d/%02d-%02d:%02d:%02d" % (lt[0], lt[1], lt[2], lt[3], lt[4], lt[5])) columns = [('id', self.id), ('step', self.step), ('stage_step', self.stage_step), ('stage', self.stage), ('time', self.time), ('stage_time', self.stage_time), ('real_time', self.real_time), ('unixtime', SI(time.time(), 's')), ('localtime', lt_str)] quantities = [ self.known_quantities_by_name[name] for name, _ in columns ] # Now we need to add the averages of all fields # This function appends to columns the averages for each component # of the specified subfield. def process_subfield(field_name, prefix, quantity, mat_name=None): if True: avg = self.get_subfield_average(field_name, mat_name) if avg == None: return else: # except: return if type(avg) == list: for i, comp_value in enumerate(avg): comp_name = "%s_%s" % (prefix, i) columns.append((comp_name, comp_value)) quantities.append(quantity.sub_quantity(comp_name)) else: columns.append((prefix, avg)) quantities.append(quantity.sub_quantity(prefix)) # Loop over all the fields and add averages for all their components for quantity in self.known_quantities: field_name = quantity.name if quantity.type in ['field', 'pfield']: if '?' in quantity.signature: for material in self.get_materials_of_field(field_name): prefix = "%s_%s" % (field_name, material.name) process_subfield(field_name, prefix, quantity, mat_name=material.name) else: process_subfield(field_name, field_name, quantity) return (columns, quantities)
def __init__(self, name=None, do_demag=True, id="Generic Simulation class"): self.class_id = id # String identifying the kind of Simulation # class self.units = None # Simulation units used by this class self.do_demag = do_demag # Whether we should include the demag field # List of all the materials used by the Simulation object self.materials = None # Dictionary used by the hysteresis method to find abbreviations for # frequently used things to save or do. # Example: for ``sim.hysteresis(..., save=[('averages', at(...))])`` # the string 'averages' needs to be a key in this dictionary. # The corresponding value is the function to call. self.action_abbreviations = {} # Every quantity the user may want to save needs to be listed here self.known_quantities = known_quantities self.known_quantities_by_name = known_quantities_by_name self.known_field_quantities = known_field_quantities ### Set the simulation name if name == None: self.name = features.get('etc', 'runid') else: self.name = name log.info("Simulation(name=%s) object created" % self.name) ### Check whether the files we are going to write do already exist. # if this is the case we should stop, unless the --clean option was # given: we do not want to overwrite data as a default! self._restarting = False data_filenames = [ self._ndtfilename(), self._h5filename(), self._tolfilename() ] if features.get('nmag', 'clean', raw=True): # Existing data files should be deleted nsim.snippets.rename_old_files(data_filenames) elif features.get('nmag', 'restart', raw=True): log.info("Starting simulation in restart mode...") self._restarting = True else: # Check that no data files exist for filename in data_filenames: if os.path.exists(filename): msg = ("Error: Found old file %s -- cannot proceed. " "To start a simulation script with old data " "files present you either need to use '--clean' " "(and then the old files will be deleted), " "or use '--restart' in which case the run " "will be continued." % filename) raise NmagUserError(msg) # See documentation for SimulationClock object self.clock = SimulationClock() # The advance_time method does not allow to carry on the simulation # up to t = infinite. Sometimes we want to simulate for n steps, # without any time limits. However we must give a time limit. # This is then how we approximate t = infinite. # For now, we do not provide any function to set or change it. # The user should just use: # sim = Simulation() # sim.max_time_reached = SI(1000, "s") self.max_time_reached = SI(1, "s") # Add abbreviations so that things can be saved just by giving # corresponding ID strings. # Example: hysteresis(..., save=[('averages', ...)]) self.add_save_abbrev('save_averages', lambda sim: sim.save_data(avoid_same_step=True)) self.add_save_abbrev( 'save_fields', lambda sim: sim.save_data(fields='all', avoid_same_step=True)) self.add_save_abbrev( 'save_field_m', lambda sim: sim.save_data(fields=['m'], avoid_same_step=True)) self.add_save_abbrev('save_restart', lambda sim: sim.save_restart_file()) self.add_do_abbrev('do_next_stage', SimulationCore.hysteresis_next_stage) self.add_do_abbrev('do_exit', SimulationCore.hysteresis_exit) # Used to write the ndt file self._ndt_writer = ColWriter(out=self._ndtfilename()) self._ndt_writer.set_formats([('float', '% 25.13g'), ('int', '% 25d'), ('date', '% 25s'), ('pfield', '% 25.13g'), ('field', '% 25.13g')]) # The following list contains a description of the physics components # which are included in the physical model For example, # ["exch", "demag"] indicates that exchange and demag are included. # In this case, spin transfer torque is not. This information # is used to understand which fields are relevant and which are not # (so that we do not save empty fields). Following the previous # example, dm_dcurrent, current_density won't be saved. self._components = None
self.parent = None self.context = context def sub_quantity(self, name): q = Quantity(name=name, type=self.type, units=self.units, signature=self.signature) q.parent = self return q # The following table contains all the quantities which may be saved to file. known_quantities = [ # name type unit signature context Quantity('id', 'int', SI(1), None), Quantity('step', 'int', SI(1), None), Quantity('stage_step', 'int', SI(1), None), Quantity('stage', 'int', SI(1), None), Quantity('last_time_dt', 'float', SI('s'), None), Quantity('time', 'float', SI('s'), None), Quantity('stage_time', 'float', SI('s'), None), Quantity('real_time', 'float', SI('s'), None), Quantity('unixtime', 'float', SI('s'), None), Quantity('maxangle', 'float', SI(1), None), Quantity('localtime', 'date', None, None), Quantity('H_total', 'field', SI('A/m'), '_?_*'), Quantity('M', 'field', SI('A/m'), '_?_*'), Quantity('m', 'pfield', SI(1), '_?_*'), Quantity('pin', 'pfield', SI(1), None), Quantity('current_density', 'pfield', SI('A/m^2'), '_*', 'stt'),
return scalar new_body = map_terminals(check_units, v) if type(u[0]) == SI: units = u[0].copy() units._value = 1.0 return (new_body, units) else: try: body, units = v if type(units) == SI: return (body, units) except: pass invalid_syntax() if __name__ == "main": #print map_terminals(lambda x: x+x, [(1, 2, 3), 4]) #print map_terminals(lambda x: x+x, numpy.array([1, 2, 3, 4])) print SI_vector(([1.23, 2, 4, 5, 6], SI(""))) print SI_vector(SI(3.1415926)) vec2d = (SI(1), SI(2)) print SI_vector(vec2d) print SI_vector(([1, 2, 3, 4], SI("m"))) print SI_vector([SI(1, "m"), SI(2, "m"), SI(3, "m"), SI(4, "m")]) print SI_vector([1, 2, SI(4, "m")])
if delta <= 0: raise ValueError("Bad usage of the function every: the delta value " "must be positive, but you specified something like " "every('step', -1) or every('step', 0)") return When(('every', (identifier, (delta, first, last)))) never = When(('never', (None, None))) if __name__ == "__main__": # If this is the main, we execute this little test from nsim.si_units import SI time = { 'stage': 0, 'step': 0, 'time': SI(0.0, 's'), 'stage_step': 0, 'stage_time': 0.0, 'real_time': 0.0, 'convergence': False } # WARNING: be careful with the & operator! # & operator is implemented using brute force: it will search # matching events. This could result in infinite loops, if there # are no matching events! # Example: every('step', 2) & every('step', 2, first=1) # the resulting intersection is empty! w = every('step', 2, last=10) & every('step', 5, first=10) w = every('step', 2, last=21) & every('step', 4, first=10)