17  Populating an Operating Model

Building an operating model in openMSE is a two-stage process. First, the user constructs the OM object and its sub-components by specifying parameter values as described in Chapter 3 and Chapter 16. Second, the Populate() function expands these compact inputs into the full arrays (e.g., Sim × Age × Year), sampling stochastic values, resolving model functions, building derived quantities, and checking the OM for internal consistency.

In normal use, Populate() is called automatically when running Simulate(), so most users will not need to call it directly. It can be useful, however, for inspecting what a set of parameter values produces before assembling a full operating model.

17.1 Populate()

Populate() is a generic dispatcher. It inspects the class of the object passed to it and calls the appropriate class-specific population function:

# Populate a full operating model
OM <- Populate(OM)

# Populate a stock object independently
Stock <- Populate(Stock, nSim = 100, nYear = 40, pYear = 30)

# Populate a fleet using an already-populated stock
Fleet <- Populate(Fleet, Stock = Stock)

The class-specific functions called by Populate() are:

  • PopulateOM()
  • PopulateStock()
  • PopulateFleet()
  • PopulateObs()
  • PopulateImp()

These functions in turn call the population functions for their individual subcomponents (PopulateLength(), PopulateSelectivity(), etc.). See ?PopulateOM and related help pages for more information.

Although Populate() doesn’t need to be called directly, individual objects can be populated independently. This is sometimes useful for inspecting what a set of parameter values produces before assembling a full operating model:

# Inspect the length-at-age schedule produced by a set of parameters
stock <- Populate(AlbacoreExStock, nSim = 100, nYear = 30, pYear = 20)
Length(stock) |> MeanAtAge()

When populating a Fleet independently, the paired Stock object must be provided so that quantities such as \(L_{50}\) (required by isRel = TRUE selectivity) and the ALK (required for size-to-age conversions) are available:

fleet <- Populate(AsympExFleet, Stock = stock)

17.2 Digest-based Caching

Population is potentially expensive for large operating models with many simulations, stocks, and fleets. To avoid unnecessary recomputation, Populate() uses a digest-based caching mechanism. Before populating any object, it computes a hash of the object’s current contents and the key arguments passed to the population function (e.g., nSim, nYear, seed). If the hash matches a stored digest from a previous population call and force = FALSE, the object is returned unchanged.

17.3 Seeding and Reproducibility

Each subcomponent population function receives its own integer seed, derived by incrementing the base seed stored in OM@Seed. This ensures that:

  • All stochastic draws are fully reproducible given a fixed OM@Seed.
  • Population of one subcomponent does not affect the random state of another.
  • All sub-objects (e.g., Stock, Fleet, etc) receive distinct seeds, so stochastic variation in one component is independent of another even when the same parameter ranges are specified.

To change the stochastic realisation of an operating model without changing any parameter values, update OM@Seed and call Populate() again:

Seed(OM) <- 42
OM <- Populate(OM)