Monte Carlo Simulation#

Overview#

The MonteCarloSimulation class provides high-level interface to uncertainty quantification for dimensionless coefficients in PyDASA, abstracting stochastic computations with distribution/data modes. Generates coefficient distributions under parameter variations for robustness assessment.

The MonteCarloSimulation simplifies stochastic workflows through three core features:

  1. Generates Simulations: Creates MonteCarlo objects (one per coefficient) automatically.

  2. Samples Distributions: Draws values from uniform, normal, or custom distributions.

  3. Accepts Inputs: Takes AnalysisEngine outputs for dimensional consistency.

Warning

Precondition: MonteCarloSimulation requires a completed dimensional analysis workflow. The analysis must provide:

  • Dimensional coefficients (from AnalysisEngine.solve() or AnalysisEngine.run_analysis())

  • Variable definitions with distribution specifications (_dist_type, _dist_params, _dist_func)

  • Consistent schema across all components

Use the exact model from Dimensional Analysis as input to ensure proper configuration.

Reynolds Number (\(Re\)) Monte Carlo Example#

This example demonstrates Monte Carlo simulation for the Reynolds number using the dimensional analysis workflow as precondition:

from pydasa import Variable, Schema, AnalysisEngine
from pydasa.dimensional.fundamental import Dimension
from pydasa.workflows.practical import MonteCarloSimulation
import random
import numpy as np

# ========================================================================
# STEP 1: Run Dimensional Analysis (Precondition)
# ========================================================================

# Define custom framework (T, M, L only)
custom_fdus = [
    Dimension(_idx=0, _sym="T", _unit="s", _name="Time"),
    Dimension(_idx=1, _sym="M", _unit="kg", _name="Mass"),
    Dimension(_idx=2, _sym="L", _unit="m", _name="Length")
]
schema = Schema(_fwk="CUSTOM", _fdu_lt=custom_fdus)

# Define variables with distribution specifications
variables = {
    "\\rho": Variable(_name="Density",
                        _sym="\\rho",
                        _cat="IN",
                        _dims="M*L^-3",
                        _units="kg/m³",
                        _dist_type="uniform",
                        _dist_params={"a": 990.0, "b": 1020.0},
                        _dist_func=lambda: random.uniform(990.0, 1020.0),
                        relevant=True,
                        _schema=schema),
    "v": Variable(_name="Velocity",
                    _sym="v",
                    _cat="OUT",
                    _dims="L*T^-1",
                    _units="m/s",
                    _dist_type="uniform",
                    _dist_params={"a": 1.0, "b": 6.0},
                    _dist_func=lambda: random.uniform(1.0, 6.0),
                    relevant=True,
                    _schema=schema),
    "D": Variable(_name="Diameter",
                    _sym="D",
                    _cat="IN",
                    _dims="L",
                    _units="m",
                    _dist_type="uniform",
                    _dist_params={"a": 0.04, "b": 0.06},
                    _dist_func=lambda: random.uniform(0.04, 0.06),
                    relevant=True,
                    _schema=schema),
    "\\mu": Variable(_name="Viscosity",
                        _sym="\\mu",
                        _cat="IN",
                        _dims="M*L^-1*T^-1",
                        _units="Pa·s",
                        _dist_type="constant",
                        _dist_params={"value": 0.001002},
                        _dist_func=lambda: 0.001002,
                        relevant=True,
                        _schema=schema),
    "g": Variable(_name="Gravity",
                    _sym="g",
                    _cat="CTRL",
                    _dims="L*T^-2",
                    _units="m/s²",
                    _dist_type="constant",
                    _dist_params={"value": 9.81},
                    _dist_func=lambda: 9.81,
                    relevant=False,
                    _schema=schema)  # Excluded from matrix
}

# Create and run dimensional analysis
engine = AnalysisEngine(_name="Reynolds Number Analysis",
                        _fwk="CUSTOM",
                        _schema=schema,
                        _variables=variables)

engine.create_matrix()
coefficients = engine.solve()

print(f"Dimensional analysis complete: {list(coefficients.keys())}")

# ========================================================================
# STEP 2: Perform Monte Carlo Simulation
# ========================================================================

# Set seed for reproducibility
np.random.seed(42)
random.seed(42)

# Create Monte Carlo simulation workflow
mc_simulation = MonteCarloSimulation(_name="Reynolds Monte Carlo",
                                        _fwk="CUSTOM",
                                        _cat="DIST",
                                        _experiments=1000,
                                        _variables=variables,
                                        _coefficients=coefficients)

# Run simulation
mc_simulation.run_simulation(iters=1000)

print(f"✓ Monte Carlo simulation complete")
print(f"  Experiments: {mc_simulation.experiments}")
print(f"  Simulations: {list(mc_simulation.simulations.keys())}")

# ========================================================================
# STEP 3: Extract Results and Statistics
# ========================================================================

# Get Reynolds number simulation results
re_key = "\\Pi_{0}"
re_simulation = mc_simulation.get_simulation(re_key)

print(f"\nReynolds Number Statistics:")
print(f"\tMean: {re_simulation.mean:.2e}")
print(f"\tMedian: {re_simulation.median:.2e}")
print(f"\tStd Dev: {re_simulation.dev:.2e}")
print(f"\tMin: {re_simulation.min:.2e}")
print(f"\tMax: {re_simulation.max:.2e}")

# Extract result arrays for further analysis
results_dict = mc_simulation.results
re_results = results_dict[re_key]["results"]

print(f"\nResult array shape: {re_results.shape}")
print(f"\tFirst 5 results: {re_results[:5]}...")

Displayed Capabilities#

In the example we appreciate the following PyDASA capabilities:

  1. Requires Precondition: Demands completed dimensional analysis with solved coefficients.

  2. Generates Objects: Creates MonteCarlo instances per coefficient automatically.

  3. Configures Sampling: Sets distributions (uniform, constant) from variable specifications.

  4. Runs Iterations: Executes Monte Carlo experiments with random sampling.

  5. Calculates Statistics: Computes mean, median, std dev, min, max per coefficient.

Quantifies uncertainty propagation through dimensionless coefficients for robustness assessment.