Best Practices#

Here are some general principles that have proven helpful for developing models.

Model Layout#

A model should be contained in a folder named with lower-case letters and underscores, such as wolf_sheep. Within that directory:

  • Readme.md describes the model, how to use it, and any other details.

  • model.py should contain the model class.

  • agents.py should contain the agent class(es).

  • app.py should contain the Solara-based visualization code (optional).

You can add more files as needed, for example:

  • run.py could contain the code to run the model.

  • analysis.py could contain any analysis code.

Input data can be stored in a data directory, output data in an output, processed results in a results directory, images in an images directory, etc.

All our examples follow this layout.

Randomization#

If your model involves some random choice, you can use the built-in random property that many Modelly objects have, including Model, Agent, and AgentSet. This works exactly like the built-in random library.

class AwesomeModel(Model):
  # ...

  def cool_method(self):
    interesting_number = self.random.random()
    print(interesting_number)

class AwesomeAgent(Agent):
  # ...
  def __init__(self, model, ...):
    super().__init__(model)
    # ...

  def my_method(self):
    random_number = self.random.randint(0, 100)

Agent.random is just a convenient shorthand in the Agent class to self.model.random. If you create your own AgentSet instances, you have to pass random explicitly. Typically, you can simply do, in a Model instance, my_agentset = AgentSet([], random=self.random). This ensures that my_agentset uses the same random number generator as the rest of the model.

Models also expose self.rng, a NumPy Generator, which is useful for vectorized random draws.

When a model object is created without a seed, its random number generators are seeded from fresh operating system entropy, so every run differs. The seed determines the sequence of random numbers; if you instantiate a model with the same seed, you will get the same results. To allow you to set the seed, make sure your model has an rng argument in its __init__ and passes it on to super().__init__().

class AwesomeModel(Model):

  def __init__(self, rng=None):
    super().__init__(rng=rng)
    ...

  def cool_method(self):
    interesting_number = self.random.random()
    print(interesting_number)

>>> model0 = AwesomeModel(rng=0)
>>> model1 = AwesomeModel(rng=0)
>>> model0.random.random() == model1.random.random()
True