doe Package#

What is the doe package?#

The doe package supports design space exploration functionalities for multibody systems. It aims to simplify the process of parameter optimization and sensitivity analysis in simulation models. Multiple experiment design types are supported, ranging from factorial to randomized designs. User-defined design is also available.

This package introduces the DesignExperiment class, which is responsible for the setup and execution of the doe. After choosing the best design type for your application, specify the design parameters and response variable(s) of your model. If no responses are defined for the DesignExperiment class, the model will be examined and responses will be extracted from the model. On setup, a stack of different simulation cases is created. Those cases are scattered throughout the design space based on the design type. On run, all the different simulation cases are executed and the DesignExperiment is populated with the simulation and the response results.

Parallel execution is supported, leveraging the multiprocessing/threading libraries of python. This is very useful as it can dramatically reduce simulation times, especially in extensive design exploration, with multiple parameters and/or run cases.

Classes:
DesignExperiment

Sets up a series of simulations to investigate the influence of design variables on responses.

Dv

Object that packages all the properties of a designable variable.

Classes#

class DesignExperiment(**kwds)#

Sets-up a series of simulations with the aim of changing some (independent) design parameters to investigate their influence on some (dependent) response variables. Writes the design matrix generated by this class to a CSV file.

Parameters
  • model (Model, optional) – The current model.

  • parameters (list(Parameter)) – A list of Parameter instances.

  • responses (list(Response)) – A list of Response objects.

  • levels (int, list) – The amount of different values that will be sampled for the parameter. Levels can be defined as an int or as a list. In case of a list, each entry will be used for the corresponding Dv, in case of int, each Dv will use the same number of levels. Valid input for design_type = “fullfact”.

  • design_type (Enum) –

    A string identifying the selected factorial model for the DOE. Valid choices are:

    • ”fullfact”: Full factorial design.

      In a full factorial design, all possible combinations of the levels for each factor are investigated. This provides a comprehensive view of how each factor and the interactions between factors affect the response variables. While full factorial designs are thorough, they can become impractical for experiments with a large number of factors or levels, as the number of required runs grows exponentially. For ( n ) factors each with ( m ) levels, a full factorial design requires ( m^n ) experimental runs.

    • ”bbdesign”: Box-Behnken design.

      The Box-Behnken design is a type of response surface design that is efficient for fitting a quadratic model to the response variable. It is particularly useful when you have three or more factors to investigate but want to minimize the number of experimental runs. Unlike full factorial designs, Box-Behnken designs do not include the extreme (corner) points, making them less sensitive to outliers. This design allows for the estimation of factor interactions and quadratic effects with fewer runs, making it a popular choice for optimization studies.

    • ”fracfact”: Fractional factorial design.

      Fractional factorial designs are a more efficient alternative to full factorial designs, especially when dealing with a large number of factors. These designs use only a fraction of the experimental runs required by a full factorial design, yet they still provide valuable information about the main effects and interactions between factors. Fractional factorial designs are often used in screening experiments where the primary goal is to identify the most influential factors with fewer runs. They are particularly useful for reducing the experimental effort and cost while still gaining insights into the system’s behavior. The design of fractional factorial experiments must be deliberate, as certain effects are confounded and cannot be separated from others.

    • ”lhs”: Latin Hypercube Sampling.

      Latin Hypercube Sampling (LHS) is a stratified sampling method that provides a set of near-random sample points from the parameter space. Unlike full factorial or other grid-based designs, LHS ensures that each parameter is sampled uniformly across its range, leading to a more efficient exploration of the design space. This makes LHS particularly useful for high-dimensional problems where traditional methods would require an impractically large number of experimental runs. LHS is often used in simulation-based studies for sensitivity analysis, optimization, and uncertainty quantification.

    • ”pbdesign”: Plackett-Burman design.

      The Plackett-Burman design is a screening design that is highly efficient for identifying the main effects of multiple factors with a minimal number of experimental runs. It is particularly useful when you have a large number of factors and want to quickly identify the most influential ones. Unlike other designs that allow for the study of interactions between factors, Plackett-Burman designs focus solely on main effects. This makes them ideal for the initial stages of experimental design where the goal is to narrow down the list of factors for more detailed study.

    • ”custom”: user supplied design matrix.

      The option to supply a custom design matrix provides the ultimate flexibility in setting up your experiment. This is particularly useful when you have specific requirements that are not met by any of the standard design methods. With a custom design matrix, you can tailor the experiment to your needs, whether that means focusing on specific regions of the design space, incorporating prior knowledge, or meeting other specialized criteria. To use this option, you simply pass your design matrix to the DesignExperiment class when initializing it.

  • samples (int) – Valid input for design_type = “lhs”. The number of sample points to generate for each factor.

  • criterion (Enum) –

    Valid input for design_type = “lhs”. A string that tells lhs how to sample the points. If no value given, the design is simply randomized.

    • ”center” or “c”: center the points within the sampling intervals

    • ”maximin” or “m”: maximize the minimum distance between points, but place the point in a randomized location within its interval

    • ”centermaximin” or “cm”: same as “maximin”, but centered within the intervals

    • ”correlation” or “corr”: minimize the maximum correlation coefficient

  • gen (str, optional) – Valid input for design_type = “fracfact”. A generator string of symbolic characters for each parameter (a, b, c), that allows some of the factor main effects to be confounded with other factor interaction effects. For more information, refer to the pyDOE documentation.

  • matrix (list(dict)) –

    Valid input for design_type = “custom”. A list of dictionaries, where each dictionary represents a run case. The keys, values of the dictionaries are the parameters and their value at that run case. It is up to the user to use the design variables in the model.

    Example:

    design_matrix = [
      {dv1: 1,     dv2: 10,   dv3: 0.01,  dv4: 0},
      {dv1: 10,    dv2: 5,    dv3: 0.1,   dv4: 1.0},
      {dv1: 1,     dv2: 5,    dv3: 0.1,   dv4: 1.0},
      {dv1: 0.10,  dv2: 500,  dv3: 0.1,   dv4: 0},
      {dv1: 0.50,  dv2: 20,   dv3: 0.2,   dv4: 1000.}
    ]
    

cases#

List of run cases for the experiment populated at runtime.

Type

list

simulation_results#

A list of instances of SimulationResults populated at runtime.

Type

SimulationResultsHistory

response_results#

List of response results for the experiment populated at runtime. These are the values of the responses obtained at the end of each DOE run.

Type

list

response_names#

(list): List of response names for the experiment.

parameter_names#

(list): List of design variables names and labels used in the experiment.

dimension#

Number of Design Variables used in the experiment.

Type

int

original_output#

Original output path of the model.

Type

str

num_process#

Number of processes to use when running DOE in multiprocessing mode.

Type

int

design_matrix_data#

Data of the experiment populated at runtime and containing design space as well as response values. Can be used in plotting or post-processing. The content of the design_matrix_data are also exported to a CSV file for further analysis.

Type

dict

plot(x, y, z=None, plot_kwds=None, curve_kwds=None)#

Plots 2D or 3D data from the design_matrix_data dictionary based on the provided keys. Uses difflib to match user-passed keys with actual keys in the dictionary.

Parameters
  • x (str) – Key for the x-axis data.

  • y (str) – Key for the y-axis data.

  • z (str, optional) – Key for the z-axis data. If provided, a 3D plot is created.

  • plot_kwds (dict) –

    Additional keyword arguments for plot customization. Example:

    plot_kwds = {
        'name'   : 'My Plot',
        'legend' : 'legend',
        'grid'   : True,
        'xaxis'  : 'str',
        'yaxis'  : 'str'
    }
    

  • curve_kwds (dict) –

    Additional keyword arguments for curve customization. Example:

    curve_kwds = {
        'color' : 'blue',
        'name'  : 'str',
        'style' : ':'
    }
    

run(event=None, mode='ST', num_process=None)#

Run method. It takes into account the experiment setup and runs the simulations.

Parameters
  • event (SimulationEvent or list(SimulationEvent) or None, default=None) – The event that will be used for the doe simulations. If no event is passed, the method uses all the events specified in the model. If you want to run the analysis with other events, pass a SimulationEvent instance (or a list(SimulationEvent)).

  • mode (Enum) –

    The mode of simulation. Valid choices are:

    • ”MP” for multiprocessing execution

    • ”ST” for single threading, single processing execution

  • num_process (int) – The number of processes to be spawned if mode=”MP”. If None, msolve will use the maximum number of cpus.

Usage Information#

This section provides a quick guide on how to use the functionalities provided by this module.

  1. Importing the Module

    Import the module into your Python script.

    from msolve.doe import *
    
  2. Setting Up an Experiment

    The module provides various classes and functions to set up an experiment. Here’s a quick example using the DesignExperiment class:

    from msolve.doe import *
    from msolve.optimize import GenericResponse
    
    resp = GenericResponse(function = f"-ACCZ({part.cm.id})",
                           name     = "value_response"
                           )
    dv = Dv(
      b       = 5,
      blimit  = (1, 15),
    )
    part.mass = dv
    design_exp = DesignExperiment("fullfact", parameters=[dv], responses=[resp])
    
  3. Running the Experiment

    Once the experiment is set up, you can run it as follows:

    results = design_exp.run()
    
  1. Analyzing Results

    The experiment contains a response_results object that can be used to analyze the outcomes of the experiment as well as a simulation_results object that contains the entire SimulationResultsHistory.

    responses = design_exp.response_results              # a list of responses, one for each simulation
    simulation_results = design_exp.simulation_results   # an instance of `SimulationResultsHistory` class.
    

This should give you a good starting point for using the functionalities provided by this module.

Examples

Define, and execute a design of experiment.#
from msolve import *
from msolve.doe import *

model = Model(output= "doe_example",
              name  = "model")

ground = Part(ground=True)
global_frame = Marker(label="global_frame", part=ground)
Units(force="NEWTON", mass="KILOGRAM", length="MILLIMETER", time="SECOND")
Accgrav(igrav=0, jgrav=0, kgrav=-9810)
part1 = Part(id=2, cm=Marker(qp=[0,0,5]), ip=[1e3, 1e3, 1e3], name="part_1")
H3dOutput(save=True)
sphere = Sphere (cm=part1.cm, radius=200)

spdp = SpringDamper(type='TRANSLATION',
                    c=0.1,
                    length=20,
                    i=global_frame,
                    j=part1.cm,
                    name="spdp")


event = SimulationEvent(type='TRANSIENT', end=1.0, dtout=0.01, store=True, returnResults=True)

# Define parameters that will be modified during the Design Of Experiment
dv1 = Dv(b      = 4,
         blimit = (1, 15),
)
part1.mass = dv1

dv2 = Dv(b      = 30,
         blimit = (10,1000),
)
spdp.k = dv2

experiment = DesignExperiment(model       = model,
                              design_type = 'lhs',
                              parameters  = [dv1, dv2],
                              samples     = 5,
                              )

# Run all model events n-times, where n: samples
experiment.run(mode='ST')