Custom analysis

PIMMS ships a set of built-in analyses (radius of gyration, internal scaling, cluster properties, and so on - see Output files), but you will often want to measure something specific to your problem. The custom analysis hook lets you supply your own Python code that PIMMS loads and runs during the simulation, with direct access to the live lattice, without touching or rebuilding PIMMS itself.

How it works

You point PIMMS at a plain Python file with two keywords:

ANALYSIS_MODULE : my_analysis.py    # path to your Python file
ANA_CUSTOM      : 500               # run it every 500 steps
  • ANALYSIS_MODULE is the path to your file (relative paths are resolved against the working directory; ~ is expanded). Default False - no custom analysis.

  • ANA_CUSTOM is how often, in steps, your code is called. Like every analysis in PIMMS it only runs after equilibration, and a value below 1 disables it. If ANALYSIS_MODULE is not set, ANA_CUSTOM is ignored.

Your file must define a single top-level function called exactly analysis_function that takes two arguments:

def analysis_function(step, lattice):
    ...

PIMMS calls it as analysis_function(step, lattice) every ANA_CUSTOM steps. The return value is ignored - a custom analysis works by doing something (writing a file, updating an accumulator), not by returning a value.

The arguments

stepint

The current simulation step, handy for labelling output.

latticethe live Lattice object

The actual simulation state - not a copy - so you can read anything about the current configuration. The most useful attributes are:

Attribute

What it is

lattice.dimensions

The box dimensions, a list of length 2 or 3.

lattice.chains

Dict mapping chainID (int) to the chain object.

lattice.chainIDtoType

Dict mapping chainID to its integer chain type.

lattice.chainTypeList

List of the distinct chain types present.

lattice.grid

The occupancy grid (a NumPy array; 0 = empty).

lattice.type_grid

Companion grid holding the bead type at each occupied site.

Each chain object in lattice.chains exposes, among others:

Chain attribute / method

What it is

chain.get_ordered_positions()

The chain’s bead positions in sequence order (N→C), as a list of [x, y, z] (or [x, y] in 2D).

chain.sequence

The chain’s bead sequence (a string).

chain.seq_len

Number of beads in the chain.

chain.chainID / chain.chainType

The chain’s integer ID and type.

Important

lattice is the real, live object. Read from it freely, but do not mutate it (moving beads, editing the grid, adding/removing chains) - that would corrupt the simulation. If you need to transform positions, copy them first.

Reusing PIMMS’ own analysis

Your module can import PIMMS and reuse the same routines the built-in analyses use. For example, pimms.lattice_analysis_utils provides get_polymeric_properties(positions, dimensions) (radius of gyration and asphericity), get_inter_position_distance(...), cluster helpers, and more; and each chain object also carries convenience methods such as analysis_get_polymeric_properties() and analysis_get_end_to_end_distance(). You are free to use NumPy, SciPy, or anything else installed in your environment.

A worked example

A minimal module that records the radius of gyration of the first chain each time it is called:

# my_analysis.py
from pimms import lattice_analysis_utils as lau

def analysis_function(step, lattice):
    # grab the first chain
    first_id = sorted(lattice.chains.keys())[0]
    chain = lattice.chains[first_id]

    # radius of gyration from the chain's current positions
    rg = lau.get_polymeric_properties(chain.get_ordered_positions(),
                                      lattice.dimensions)[0]

    # append it to our own output file (one row per call)
    with open("custom_rg.dat", "a") as fh:
        fh.write("%d\t%.4f\n" % (step, rg))

With ANALYSIS_MODULE : my_analysis.py and ANA_CUSTOM : 500 in the keyfile, PIMMS writes a custom_rg.dat row every 500 post-equilibration steps.

Practical notes

  • Output files. PIMMS does not manage your custom output - you open and write files yourself. Open in append mode ("a") if you want one growing file across the run, and remember the working directory is wherever PIMMS was launched.

  • Helper modules. Your module’s own directory is added to the import path, so it may import helper modules that sit alongside it.

  • Keep it light. Your function runs inside the simulation loop; expensive work every few steps will slow the run down. Prefer a modest ANA_CUSTOM frequency and cache anything you can.

  • State across calls. Because the module is imported once and the same function object is reused, you can keep running state in module-level variables (e.g. an accumulator) between calls.

Validation and error handling

The custom-analysis hook is designed to fail early and clearly:

  • Load-time validation (at keyfile parse). As soon as the keyfile is read, PIMMS loads your file and checks that it exists, imports without error, defines a analysis_function, that it is callable, and that its signature can accept the (step, lattice) call. Any problem aborts immediately with a clear message that names the file and the issue - so a typo or a missing entry point is caught in seconds, before a long run starts, rather than part-way through. For example, a file that defines the function under the wrong name fails with:

    The custom analysis module 'my_analysis.py' does not define an
    'analysis_function'. PIMMS calls 'analysis_function(step, lattice)', so the
    file must define a top-level function with exactly that name. Callables
    defined in the file: my_other_function.
    
  • Isolated import. Your file is imported by path under a private module name, so it cannot collide with (or be shadowed by) a PIMMS or standard-library module, even if you call it something like random.py or energy.py.

  • Runtime errors. If your analysis_function raises once it is running (say it hits a configuration it did not expect), PIMMS stops the run and reports an AnalysisRoutineException that names the step and makes clear the fault is in your analysis code rather than in PIMMS - instead of surfacing as an opaque traceback deep inside the engine.