Running the Model--the API way ============================== In order to analyze a model in quantitatively it is more practical to write small client scripts that directly talk to the runtime API. As time passes and more of these scripts are written over and over some standard functionality will likely be integrated into the runtime API. For starters a simple script could look as follows :: #!/usr/bin/env python from kmos.run import KMC_Model model = KMC_Model() As you can see by default the model prints a disclaimer and all rate constants, which can each be turned off by instantiating :: model = KMC_Model(print_rates=False, banner=False) The most important method is of course how to run the model, which you can do by saying :: model.do_steps(100000) which would run the model by 100,000 kMC steps. Let's say you want to change the temperature and a partial pressure of the model you could type :: model.parameters.T = 550 model.parameters.p_COgas = 0.5 and all rate constants are instantly updated. In order get a quick overview of the current settings you can issue e.g. :: print(model.parameters) print(model.rate_constants) or just :: print(model) Now an instantiated und configured model has mainly two functions: run kMC steps and report its current configuration. To analyze the current state you may use :: atoms = model.get_atoms() .. note:: If you want to fetch data from the current state without actually visualizing the geometry can speed up the get_atoms() call using :: atoms = model.get_atoms(geometry=False) This will return an ASE atoms object of the current system, but it also contains some additional data piggy-backed such as :: model.get_occupation_header() atoms.occupation model.get_tof_header() atoms.tof_data atoms.kmc_time atoms.kmc_step These quantities are often sufficient when running and simulating a catalyst surface, but of course the model could be expanded to more observables. The Fortran modules `base`, `lattice`, and `proclist` are atttributes of the model instance so, please feel free to explore the model instance e.g. using ipython and :: model.base.<TAB> model.lattice.<TAB> model.proclist.<TAB> etc.. The `occupation` is a 2-dimensional array which contains the `occupation` for each surface `site` divided by the number of unit cell. The first slot denotes the species and the second slot denotes the surface site, i.e. :: occupation[species, site-1] So given there is a `hydrogen` species in the model, the occupation of `hydrogen` across all site type can be accessed like :: hydrogen_occupation = occupation[model.proclist.hydrogen] To access the coverage of one surface site, we have to remember to subtract 1, when using the the builtin constants, like so :: hollow_occupation = occupation[:, model.lattice.hollow-1] Lastly it is important to call :: model.deallocate() once the simulation if finished as this frees the memory allocated by the Fortan modules. This is particularly necessary if you want to run more than one simulation in one script. .. _manipulate_model_runtime: Manipulating the Model at Runtime ================================= It is quite easy to change not only model parameters but also the configuration at runtime. For instance if one would like to prepare a surface with a certain configuration or pattern. Given you instantiated a `model` instance a site occupation can be changed by calling :: model.put(site=[x,y,z,n], model.proclist.<species>) However if changing many sites at once this is quite inefficient, since each `put` call, adjusts the book-keeping database. To circumvent this you can use the `_put` method, like so :: model._put(...) model._put(...) ... model._adjust_database() though at the end one must not forget to call `_adjust_database()` before executing any next step or the database of available processes is inaccurate and the model instance will crash soon. You can also get or set the whole configuration of the lattice at once using :: config = model._get_configuration() # possible change config model._set_configuration(config) Running models in parallel ========================== Due to the global clock in kMC there seems to be no simple and efficient way to parallelize a kMC program. kmos certainly cannot parallelize a single system over processors. However one can run several kmos instances in parallel which might accelerate sampling or efficiently check for steady state conditions. However in many applications it is still useful to run several models seperately at once, for example to scan some set of parameters one a multicore computer. This kind of problem can be considered `embarrasingly parallel` since it requires no communication between the runs. This is made very simple through the `multiprocessing` module, which is in the Python standard library since version 2.6. For older versions this needs to be `downloaded <http://pypi.python.org/pypi/multiprocessing/>` and installed manually. The latter is pretty straightforward. Then besides `kmos` we need to import `multiprocessing` :: from multiprocessing import Process from numpy import linspace from kmos.run import KMC_Model and let's say you wanted to scan a range of temperature, while keeping all other parameteres constant. You first define a function, that takes a set of temperatures and runs the simulation for each :: def run_temperatures(temperatures): for T in temperatures: model = KMC_Model() model.parameters.T = T model.do_steps(100000) # do some evaluation model.deallocate() In order to split our full range of input parameters, we can use a utility function :: from kmos.utils import split_sequence All that is left to do, is to define the input parameters, split the list and start subprocesses for each sublist :: if __name__ == '__main__': temperatures = linspace(300, 600, 50) nproc = 8 for temperatures in split_sequence(temperatures, nproc): p = Process(target=run_temperatures, args=(temperatures, )) p.start()