# ember.edu – Universal Concept Engine

`ember.edu` is a hyper-generalised simulation framework. Instead of subject-specific tools, it provides **Atomic Pedagogical Concepts** that can represent any system—scientific, economic, or social—with minimal code.

## Core Philosophy: The Atomic Concept

Every simulation is broken down into fourteen fundamental concepts:

1.  **Flow:** Stuff moving through a conduit (Electrons, Water, Money, Traffic, Data).
2.  **Balance:** Equilibrium between two forces (Supply/Demand, Chemical pH, Scales).
3.  **Mapping:** Mathematical relationships (Graphs, Growth, Probability).
4.  **Accumulation:** Storage or stocks that fill/empty (Population, Batteries, Capital).
5.  **Transform:** Processes that convert one thing to another (Factories, Reactions).
6.  **Network:** Interconnected nodes and hubs (Social webs, Epidemiology, Grids).
7.  **Interaction:** Weighted relationships between items (Attention, Similarity, Influence).
8.  **HeatMap:** Dense matrices (Attention Maps, Risk Grids, Correlations).
9.  **Pipeline:** Staged processes (Model Blocks, Workflows, Value Chains).
10. **Table:** Visual summary of multiple data points (Scoreboards, Stats).
11. **SemanticScene:** Coordinate-free object/relation scenes; Ember computes placement, paths, glyphs, and motion.
12. **Diagram:** Coordinate-free node/link scenes; Ember computes placement.
13. **Sketch:** Shape-first diagrams with blobs, paths, labels, and animated flows.
14. **Scene:** Domain-specific visualisations using low-level drawing APIs.

## System Dynamics (Automatic Integration)

The library supports "calculus-free" modeling of time-dependent systems using the `rate()` method. This automatically integrates values over time.

```python
l = Lesson("Population Growth")
l.input("Birth Rate", 0, 0.5, 0.1)
l.state.params["Population"] = 10

# Automatic integration: Population' = Population * Birth Rate
l.rate("Population", lambda s: s["Population"] * s["Birth Rate"])

l.show(Accumulation("Population", max_val=1000))
l.run()
```

## Batch Modeling (`model()`)

For complex systems, use the `model()` method to define multiple rules and rates at once.

```python
l.model(
    rules={
        "Profit": lambda s: s["Revenue"] - s["Costs"]
    },
    rates={
        "Revenue": lambda s: s["Sales"] * s["Price"]
    }
)
```

## Minimal Code Example (Epidemiology)

```python
from ember.edu import Lesson, Network, Table

l = Lesson("Disease Spread")
l.input("Infection Rate", 0, 0.5, 0.1)

cities = ["A", "B", "C"]
edges = [("A", "B"), ("B", "C")]

l.state.derived["A"] = 10 # Patient zero

# Simple spread rule
for city in cities:
    l.rate(city, lambda s, c=city: s[c] * s["Infection Rate"])

l.show(Network(cities, edges))
l.show(Table(cities))
l.run()
```

## Coordinate-Free Diagrams

For source-to-result animations and object/relation scenes, prefer
`SemanticScene` over hand-written coordinates. It accepts objects with roles,
glyph kinds, motion, and activity links; then computes placement, paths, common
glyph geometry, and animated flows.

```python
from ember.edu import Lesson, SemanticScene

l = Lesson("Faraday's Law: Induction", theme="neon").clock()
l.show(SemanticScene(
    objects=[
        {"id": "magnet", "kind": "magnet", "role": "driver", "motion": "oscillate"},
        {"id": "coil", "kind": "coil", "role": "mediator", "activity": "motion_rate:magnet"},
        {"id": "bulb", "kind": "bulb", "role": "response", "activity": "motion_rate:magnet"},
    ],
    relations=[
        {"from": "magnet", "to": "coil", "kind": "influence", "activity": "motion_rate:magnet"},
        {"from": "coil", "to": "bulb", "kind": "flow", "activity": "motion_rate:magnet"},
    ],
))
l.explain("A current appears only when magnetic flux through the coil changes.")
l.run()
```

For plain node/link graphs, use `Diagram`.

## API Reference

### `Lesson`
- `input(name, min, max, default)`: Adds a reactive parameter.
- `rule(name, fn)`: Defines a derived value.
- `rate(name, fn)`: Defines a rate of change (integration) for a value.
- `model(rules, rates)`: Batch defines multiple rules and rates.
- `show(concept)`: Adds a visual concept to the auto-layout grid.
- `explain(text_or_fn)`: Adds a dynamic narrative at the bottom.
- `show_hud`: Set to `False` for polished exports that should hide raw state.
- `run()`: Starts the interactive application.
- `record(seconds, filename)`: Exports the simulation to a GIF.

### Concepts
- `Flow(rate_key, label)`: Animates particles through a pipe.
- `Balance(left_key, right_key, labels)`: A dynamic seesaw comparing two values.
- `Mapping(x_label, y_label, fn)`: A mathematical graph mapping `x` to `y`.
- `Accumulation(key, max_val, label)`: A tank that fills or empties.
- `Transform(in_key, out_key, label)`: Shows input A being converted to output B.
- `Network(nodes, edges, label)`: A graph showing relationships between hubs.
- `Interaction(items, weight_fn, layout, label)`: Shows weighted links between items.
- `HeatMap(rows, cols, value_fn, label, focus_key)`: Shows a weighted matrix.
- `Pipeline(stages, active_key, label)`: Shows a staged process with animated flow.
- `Table(keys, label)`: A visual summary of multiple data points.
- `SemanticScene(objects, relations, layout, direction, label)`: Draws a role/glyph scene without manual coordinates.
- `Diagram(nodes, links, layout, direction, label)`: Draws a node/link diagram without manual coordinates.
- `Sketch(draw_fn, label)`: Gives `draw_fn(canvas, state)` a normalized canvas for organic shapes.
- `Scene(draw_fn)`: A custom schematic drawer.
