|
| 1 | +""" |
| 2 | +Cyecca DSL - A Modelica-inspired Domain Specific Language for Python. |
| 3 | +
|
| 4 | +This module provides a high-level, declarative API for defining dynamic systems |
| 5 | +that closely mirrors Modelica syntax while remaining fully Pythonic. |
| 6 | +
|
| 7 | +================================================================================ |
| 8 | +PROTOTYPE MODE - API IS IN FLUX |
| 9 | +================================================================================ |
| 10 | +
|
| 11 | +This DSL is in active prototype development. The API may change significantly |
| 12 | +between versions. Do NOT maintain backward compatibility - iterate rapidly. |
| 13 | +
|
| 14 | +================================================================================ |
| 15 | +DESIGN PRINCIPLES - DO NOT REMOVE OR IGNORE |
| 16 | +================================================================================ |
| 17 | +
|
| 18 | +1. MODELICA CONFORMANCE: This DSL conforms as closely as possible to the |
| 19 | + Modelica Language Specification v3.7-dev. |
| 20 | + Reference: https://specification.modelica.org/master/ |
| 21 | + |
| 22 | + - Variable flags (parameter, input, output, constant) follow Modelica semantics |
| 23 | + - Attributes (start, fixed, min, max, nominal, unit) match Modelica definitions |
| 24 | + - Equation-based modeling with der() operator |
| 25 | + - Automatic state/algebraic classification based on der() usage |
| 26 | + - Connector/connection semantics (future) will follow Modelica Chapter 9 |
| 27 | +
|
| 28 | +2. TYPE SAFETY: All functions MUST use beartype for runtime type checking. |
| 29 | + - All public functions decorated with @beartype |
| 30 | + - All classes use proper type hints for full IDE autocomplete |
| 31 | + - Do NOT use Any where a more specific type is possible |
| 32 | + - Do NOT remove beartype decorators |
| 33 | + - WHEN ADDING NEW FUNCTIONS: Always add @beartype decorator |
| 34 | + - Import and use type aliases (Shape, Indices) from types.py |
| 35 | +
|
| 36 | +3. SELF-CONTAINED: This DSL module uses NO external compute libraries except: |
| 37 | + - Python standard library |
| 38 | + - beartype (for runtime type checking) |
| 39 | + - numpy (for NumericValue type hints only) |
| 40 | + |
| 41 | + The DSL builds an abstract model representation (expression trees, equation |
| 42 | + graphs) that can be compiled by separate backends (CasADi, JAX, etc.). |
| 43 | + |
| 44 | + DO NOT import CasADi, JAX, or other compute backends in the core DSL modules. |
| 45 | +
|
| 46 | +4. IMMUTABILITY: Prefer immutable data structures where possible. |
| 47 | +
|
| 48 | +5. EXPLICIT > IMPLICIT: All behavior should be explicit and documented. |
| 49 | +
|
| 50 | +================================================================================ |
| 51 | +
|
| 52 | +Example |
| 53 | +------- |
| 54 | +>>> import matplotlib.pyplot as plt |
| 55 | +>>> from cyecca.dsl import model, var, sin, cos, der |
| 56 | +>>> from cyecca.dsl.backends import CasadiBackend |
| 57 | +>>> |
| 58 | +>>> @model |
| 59 | +... class Pendulum: |
| 60 | +... '''Simple pendulum model.''' |
| 61 | +... # Parameters (constant during simulation) |
| 62 | +... g = var(9.81, parameter=True) |
| 63 | +... l = var(1.0, parameter=True) |
| 64 | +... |
| 65 | +... # States (automatically detected via der() usage) |
| 66 | +... theta = var(start=0.5) |
| 67 | +... omega = var() |
| 68 | +... |
| 69 | +... # Outputs (explicitly flagged) |
| 70 | +... x = var(output=True) |
| 71 | +... y = var(output=True) |
| 72 | +... |
| 73 | +... def equations(m): # Use 'm' for model namespace (cleaner than 'self') |
| 74 | +... yield der(m.theta) == m.omega |
| 75 | +... yield der(m.omega) == -m.g / m.l * sin(m.theta) |
| 76 | +... yield m.x == m.l * sin(m.theta) |
| 77 | +... yield m.y == -m.l * cos(m.theta) |
| 78 | +>>> |
| 79 | +>>> # Create, compile, and simulate |
| 80 | +>>> pend = Pendulum() |
| 81 | +>>> compiled = CasadiBackend.compile(pend.flatten()) |
| 82 | +>>> result = compiled.simulate(tf=10.0) |
| 83 | +>>> |
| 84 | +>>> # Plot using model variables (autocomplete works on pend.theta) |
| 85 | +>>> _ = plt.figure() # doctest: +SKIP |
| 86 | +>>> _ = plt.plot(result.t, result(pend.theta), label='theta') # doctest: +SKIP |
| 87 | +>>> _ = plt.plot(result.t, result(pend.omega), label='omega') # doctest: +SKIP |
| 88 | +>>> _ = plt.legend() # doctest: +SKIP |
| 89 | +>>> plt.show() # doctest: +SKIP |
| 90 | +
|
| 91 | +Variable Classification |
| 92 | +----------------------- |
| 93 | +Variables are classified automatically based on flags and equation analysis: |
| 94 | +
|
| 95 | +1. var(parameter=True) → parameter (constant during simulation) |
| 96 | +2. var(constant=True) → constant (compile-time constant) |
| 97 | +3. var(input=True) → input (externally controlled) |
| 98 | +4. var(output=True) → output (computed, exposed) |
| 99 | +5. If der(var) appears in equations → state |
| 100 | +6. Otherwise → algebraic variable |
| 101 | +
|
| 102 | +Architecture |
| 103 | +------------ |
| 104 | +The DSL is structured in two layers: |
| 105 | +
|
| 106 | +1. DSL Layer (no external compute libraries): |
| 107 | + - model.py: @model decorator, var(), expression trees |
| 108 | + - simulation.py: SimulationResult with plotting utilities |
| 109 | + - operators.py: Math functions that return Expr nodes |
| 110 | +
|
| 111 | +2. Backend Layer: |
| 112 | + - backends/casadi.py: Compiles FlatModel to CasADi functions |
| 113 | + - (future) backends/jax.py: Compiles FlatModel to JAX functions |
| 114 | +""" |
| 115 | + |
| 116 | +from cyecca.dsl.model import ( |
| 117 | + model, |
| 118 | + block, |
| 119 | + function, |
| 120 | + var, |
| 121 | + submodel, |
| 122 | + der, |
| 123 | + pre, |
| 124 | + edge, |
| 125 | + change, |
| 126 | + and_, |
| 127 | + or_, |
| 128 | + not_, |
| 129 | + if_then_else, |
| 130 | + local, |
| 131 | + assign, |
| 132 | + Model, |
| 133 | + FlatModel, |
| 134 | + Expr, |
| 135 | + ExprKind, |
| 136 | + Equation, |
| 137 | + Assignment, |
| 138 | + FunctionMetadata, |
| 139 | +) |
| 140 | +from cyecca.dsl.types import Var, VarKind, DType, Shape, Indices |
| 141 | +from cyecca.dsl.operators import sin, cos, tan, asin, acos, atan, atan2, sqrt, exp, log, abs |
| 142 | +from cyecca.dsl.simulation import SimulationResult, Simulator |
| 143 | + |
| 144 | +__all__ = [ |
| 145 | + # Decorators |
| 146 | + "model", |
| 147 | + "block", |
| 148 | + "function", |
| 149 | + # Variable declaration |
| 150 | + "var", |
| 151 | + "Var", |
| 152 | + "VarKind", |
| 153 | + "DType", |
| 154 | + "Shape", |
| 155 | + "Indices", |
| 156 | + "submodel", |
| 157 | + # Free functions (continuous) |
| 158 | + "der", |
| 159 | + # Free functions (discrete/event) |
| 160 | + "pre", |
| 161 | + "edge", |
| 162 | + "change", |
| 163 | + # Boolean operators |
| 164 | + "and_", |
| 165 | + "or_", |
| 166 | + "not_", |
| 167 | + # Conditional expression |
| 168 | + "if_then_else", |
| 169 | + # Algorithm section |
| 170 | + "local", |
| 171 | + "assign", |
| 172 | + "Assignment", |
| 173 | + # Function support |
| 174 | + "FunctionMetadata", |
| 175 | + # Base class |
| 176 | + "Model", |
| 177 | + # Model representation |
| 178 | + "FlatModel", |
| 179 | + "Expr", |
| 180 | + "ExprKind", |
| 181 | + "Equation", |
| 182 | + # Simulation |
| 183 | + "SimulationResult", |
| 184 | + "Simulator", |
| 185 | + # Math functions |
| 186 | + "sin", |
| 187 | + "cos", |
| 188 | + "tan", |
| 189 | + "asin", |
| 190 | + "acos", |
| 191 | + "atan", |
| 192 | + "atan2", |
| 193 | + "sqrt", |
| 194 | + "exp", |
| 195 | + "log", |
| 196 | + "abs", |
| 197 | +] |
0 commit comments