Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/conduit_filters_bypass.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 31 additions & 0 deletions docs/conduit_filters_bypass.ymmsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
ymmsl_version: v0.2

description: macro-meso-micro with a bypass conduit needing filters

models:
macro_meso_micro_filtered:
components:
macro:
ports:
o_i: bc_out bypass_out
s: bc_in bypass_in
description: ''
meso:
ports:
f_init: init_in
o_i: bc_out
s: bc_in
o_f: final_out
description: ''
micro:
ports:
f_init: init_in bypass_in
o_f: bypass_out final_out
description: ''
conduits:
macro.bc_out: meso.init_in
macro.bypass_out: repeat micro.bypass_in
meso.final_out: macro.bc_in
meso.bc_out: micro.init_in
micro.final_out: meso.bc_in
micro.bypass_out: last macro.bypass_in
178 changes: 178 additions & 0 deletions docs/describing_models.rst
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,127 @@ separated by periods. Depending on the context, this may represent a name in a n
or an attribute of an object (as we will see below with Conduits).


Timelines
`````````

Different components of a coupled simulation typically run at their own pace: a fast,
detailed micro model may take many small steps for every single step of the macro model
driving it, and a meso model may sit somewhere in between the two. yMMSL captures this
idea of "running at a different pace" as a *timeline*. Wiring a component's ``o_i``/``s``
ports to another component's ``f_init``/``o_f`` ports puts that other component, and anything
it in turn drives, on a timeline nested inside the first. yMMSL works this out automatically
from how components are wired together with conduits.

A component that nobody calls sits on the outermost, root timeline, written ``:``. Every
level of nesting adds one more name, giving each timeline in the model an addressable
path, a bit like a folder structure.

Take a macro model that calls a meso model in a loop, and where that meso model in turn
calls a micro model in its own loop. This produces three levels of timelines: the root
timeline for ``macro``, a timeline nested inside it for ``meso``, and a timeline nested
inside *that* for ``micro``:

.. literalinclude:: timelines_macro_meso_micro.ymmsl
:caption: ``docs/timelines_macro_meso_micro.ymmsl``
:language: yaml

.. figure:: timelines_macro_meso_micro.svg
Comment thread
IrisvdWerf marked this conversation as resolved.
:align: center
:alt: macro connects to meso through F_INIT/O_F and O_I/S ports, and meso connects to
micro the same way, producing three nested timelines.

The same model, visualized with `ymmsl2svg
<https://github.com/multiscale/ymmsl2svg>`_. The order of the boxes in the figure,
from top to bottom, mirrors the nesting in time: ``macro`` first, then ``meso``
below it, then ``micro`` below ``meso``.

A single component can also be connected to more than one timeline at once, for example
when it drives two other components that run at different rates. ``macro`` calling
``micro1`` in one loop and ``micro2`` in a separate loop puts ``micro1`` and ``micro2`` on
two independent sub-timelines of ``macro``. The following example shows how you can
use ``timeline <name>:`` to indicate that the ports connecting to ``micro1`` belong to
a different subtimeline than the ports connecting to ``micro2``:

.. literalinclude:: timelines_two_subtimelines.ymmsl
:caption: ``docs/timelines_two_subtimelines.ymmsl``
:language: yaml

.. figure:: timelines_two_subtimelines.svg
:align: center
:alt: macro has two separate pairs of O_I/S ports, one connecting down to micro1 and
one connecting down to micro2, side by side.

The same model, visualized with `ymmsl2svg
<https://github.com/multiscale/ymmsl2svg>`_. ``macro``'s two named timelines are drawn
side by side beneath it, each with its own pair of ports, one leading to ``micro1``
and the other to ``micro2``.

Matching timelines
^^^^^^^^^^^^^^^^^^^

The timeline hierarchy above is worked out automatically from how ``f_init``/``o_f``
and ``o_i``/``s`` ports are wired together, and a conduit can only connect ports whose
timelines match. Sometimes, though, two components are expected to produce matching
time points without one being nested inside the other's timeline this way, for example
two components that call each other directly and are expected to stay in lock-step, or
a component that adapts its output to another component's timeline, as time bridges do.
``matching_timelines`` lets you declare such timelines equivalent, so that a conduit can
still connect ports on them directly:

.. code-block:: yaml
:caption: Declaring matching timelines

components:
left:
ports:
o_i: out
s: in
description: Left side of the domain
right:
ports:
o_i: out
s: in
description: Right side of the domain

matching_timelines:
left: right

conduits:
left.out: right.in
right.out: left.in

``left`` and ``right`` call each other directly rather than through a shared driver, so
their O_I and S ports live on their own default timelines, ``:left`` and ``:right``,
named after the component as usual. A conduit between these ports would therefore not be
allowed. The entry under ``matching_timelines`` declares ``left`` and ``right``'s
timelines equivalent, so that the conduits connecting them are valid after all.

On the Python side, ``matching_timelines`` is a list of
:class:`.ymmsl.v0_2.MatchingTimelines` objects, each representing a set of equivalent
timelines, with a ``head`` attribute and a ``matches`` attribute holding the full set,
including the head.

A head can have more than one match, for example if ``left`` is expected to stay in
lock-step with both ``right`` and ``top``. The matches can then be written as a
whitespace-separated string:

.. code-block:: yaml
:caption: A head with multiple matches

matching_timelines:
left: right top

or, equivalently, as a YAML list:

.. code-block:: yaml
:caption: The same, as a YAML list

matching_timelines:
left:
- right
- top


Conduits
````````

Expand Down Expand Up @@ -186,6 +307,63 @@ sender:
print(conduits[0]) # output: Conduit(sender.port -> receiver1.port)
print(conduits[1]) # output: Conduit(sender.port -> receiver2.port)

Conduit filters
^^^^^^^^^^^^^^^

A conduit connects two components that call each other directly, for example ``macro``
and ``meso``, or ``meso`` and ``micro``. ``macro`` and ``micro`` are not directly
connected in this sense: ``meso`` sits between them. Connecting ``macro`` and ``micro``
directly, bypassing ``meso``, means their pace no longer matches: ``micro`` is still
called many times for every step ``macro`` takes, and still produces a message on
every one of those calls, even though there is no longer a ``meso`` in between to
absorb the difference. A conduit filter reconciles that mismatch.

Extending the macro-meso-micro example from :ref:`Timelines` with a conduit that
bypasses ``meso`` to connect ``macro`` and ``micro`` directly shows both filters in
use:

.. literalinclude:: conduit_filters_bypass.ymmsl
:caption: ``docs/conduit_filters_bypass.ymmsl``
:language: yaml

.. figure:: conduit_filters_bypass.svg
:align: center
:alt: macro and micro have an extra pair of ports directly connecting them,
bypassing meso, labeled "repeat" and "last".

The same model, visualized with `ymmsl2svg
<https://github.com/multiscale/ymmsl2svg>`_.

``macro`` produces the ``bypass_out`` message once, but ``micro`` is called many times
for every step of ``macro`` and needs the message on each of those calls. The conduit
from ``macro.bypass_out`` to ``micro.bypass_in`` uses a ``repeat`` filter for this: the
single message ``macro`` sends is resent to ``micro`` every time it runs, without
``meso`` having to relay it.

The reverse happens on the way back: ``micro`` produces a ``bypass_out`` message on
every one of its many runs, but ``macro`` still expects only one message per call. The
conduit from ``micro.bypass_out`` to ``macro.bypass_in`` uses a ``last`` filter to
reduce those many messages down to the single most recently produced one.

- ``repeat`` and ``pad`` go from the shallower side to the deeper one: a single message
is repeated, or followed by empty messages, to match every time the deeper side
receives.
- ``last`` goes from the deeper side back to the shallower one: of the many messages
produced, only the last one is passed on.

A conduit between two components that call each other directly doesn't skip anything,
so it can't take a filter at all.

Filters are written in front of the receiver and may be combined:

.. code-block:: yaml
:caption: Specifying conduit filters in yMMSL

conduits:
macro.init_out: repeat micro.init_in
micro.state_out: last macro.final_in


Nesting models
``````````````

Expand Down
1 change: 1 addition & 0 deletions docs/timelines_macro_meso_micro.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 29 additions & 0 deletions docs/timelines_macro_meso_micro.ymmsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
ymmsl_version: v0.2

description: A macro-meso-micro model, three levels of timelines

models:
macro_meso_micro_model:
components:
macro:
ports:
o_i: bc_out
s: bc_in
description: ''
meso:
ports:
f_init: init_in
o_i: bc_out
s: bc_in
o_f: final_out
description: ''
micro:
ports:
f_init: init_in
o_f: final_out
description: ''
conduits:
macro.bc_out: meso.init_in
meso.final_out: macro.bc_in
meso.bc_out: micro.init_in
micro.final_out: meso.bc_in
1 change: 1 addition & 0 deletions docs/timelines_two_subtimelines.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 31 additions & 0 deletions docs/timelines_two_subtimelines.ymmsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
ymmsl_version: v0.2

description: One component (macro) connected to two independent timelines

models:
two_subtimelines_model:
components:
macro:
ports:
timeline tl1:
o_i: micro1_out
s: micro1_in
timeline tl2:
o_i: micro2_out
s: micro2_in
description: ''
micro1:
ports:
f_init: init_in
o_f: final_out
description: ''
micro2:
ports:
f_init: init_in
o_f: final_out
description: ''
conduits:
macro.micro1_out: micro1.init_in
micro1.final_out: macro.micro1_in
macro.micro2_out: micro2.init_in
micro2.final_out: macro.micro2_in
Loading