Custom Model Development

The Illuminator supports and endorses the creation of custom models for your simulation needs. Templates for these models are provided in TODO LINK. Model creation can be done with basic prior programming knowledge and a basic understanding of inheritance and object oriented programming.

All new models must satisfy the below criteria, this is to ensure a smooth integration and contribution to the Illuminator. Any model created & submitted must fulfill the following conventions to be added to the Illuminator package.

Good Coding Practices

The points listed in this subsection are general good coding practices already utilised in the Illuminator. We encourage you to adopt these habits for this and any project you are a part of. Such practices directly improve the readability and maintainability of the project.

# Bad Example

def func(x, y):
    return x + y

temp = func(10, 20)
print(temp)

# Good Example

def add_numbers(first_number, second_number):
    return first_number + second_number

result = add_numbers(10, 20)
print(result)

Meaningful Function & Variable Names

Functions and variables should have clean and meaningful names that clearly indicate their purpose and functionality.

A descriptive name provides context, reduces ambiguity, and eliminates the need for excessive comments to explain its use. Adhering to consistent naming conventions also ensures uniformity across the project and enhances collaboration.

Unnecessary Computations

Avoid redundant computations, excessive loops, and duplicate code segments to enhance efficiency and reduce points of error.

Evaluate whether computations can be precomputed, reused, or consolidated to minimize resource consumption. By refactoring repetitive patterns into shared methods, you not only simplify maintenance but also prevent errors caused by inconsistent updates to duplicated logic.

If you come across any such improvement in the existing Illuminator code, we would love to hear them! Contact the main developers at TODO LINK.

Hard-Coded bits & Spaghetti Code

Hard-coded values and spaghetti code undermine the flexibility and scalability of a project and make debugging and future development exceedingly difficult. Prioritize clean, versatille design.

Libraries, Dependencies & Versions

You are welcome to use any library or package in your custom models, try however to not import unused modules or entire libraries when only specific functionalities are required.


# Bad Example:

import pandas  # Entire library is imported but only DataFrame is used

df = pandas.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})
print(df)

# Good Example:

from pandas import DataFrame  # Only importing what is needed

df = DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})
print(df)

Additionally, please ensure you update and specify exact version numbers in dependency manifests (requirements.txt) to ensure that builds are reproducible and unaffected by breaking changes in newer releases.

Project Documentation

Documentation and code comprehensibility are crucial for an open-source project like the Illuminator. Only through them can contributors and users understand, maintain, and improve the codebase effectively.

Comments

Focus on adding comments where the code’s purpose or logic is not immediately obvious to the reader. Avoid redundant comments that merely restate the code or clutter the file.

Docstrings

Docstrings are crucial to custom models because each model’s documentation is generated through them. Follow the current style for functions and classes.

The docstrings should have a clearly separated sections for other parts of the code (if any). These may include, but is not limited to: Parameters, Attributes, Returns, Raises.

Each separated section should start with the Title of the section, followed by a row of dashes such as in the following section:

def multiply(a, b):
    """
    Computes the multiplication of two numbers and returns its value.

    ...
    Parameters
    ----------
    a : int
        The first integer of the multiplication formula
    b : int
        The second integer of the multiplication formula

    Returns
    -------
    int
        The two values multiplied.
    """
    return a * b

When appropriate, we should also ensure to include the name and/or type of variables for the any of the aforementioned sections.

Lastly, type hints are also a useful addition to any code. They can be used to “hint” to other developers what is expected as input and/or output when a function is used.

def sum(a:int, b:int) -> float:

As seen from the example above we can immediately conclude that for this function to work it will need an integer a and b, with the return value being of type float

Combined, the docstrings may look something like this:

def sum(a:int, b:int) -> float:
    """
    Computes the sum of `a` and `b` and returns the outcome

    ...

    Parameters
    ----------
    a : int
        The first integer of the sum formula
    b : int
        The second integer of the sum formula

    Returns
    -------
    result : float
        The sum of a + b
    """
    result = a + b
    return result

Docstrings are essential to the Illuminator since each model’s documentation is automatically generated through them. It is a model developers responsibility to ensure thorough documentation of their implemented model.

Consistency & Linting

Always adhere to the conventions and style used in the existing code, including naming patterns, indentation, spacing, and structure. For example, if camelCase is used for variable names, avoid switching to snake_case.

Tools like linters and formatters can help enforce these conventions, this project utilises pylint TODO LINK across its codebase. This package also helps maintain good coding practices mentioned in TODO LINK. Any submitted code must pass pylint’s format check.

Model Testing

This section lists the testing methods you must implement along with your model to verify it operates properly within the rest of the Illuminator framework.

Scenario Implementation

Creating a YAML scenario file for your custom model is essential for verifying that it integrates and runs properly within the simulation framework. This file essentially acts as a lightweight end-to-end test, providing an easy way to confirm that the model behaves as expected.

Unit Tests

Unit tests are a critical component of ensuring the internal functionality of your custom model. These tests validate individual methods and components to confirm that they produce the expected outputs for a variety of input cases, including edge scenarios. Further instructions can be found in TODO LINK.