uncscode / particula

a simple, fast, and powerful particle simulator
https://uncscode.github.io/particula
MIT License
5 stars 7 forks source link

Discussion on patterns. #438

Closed Gorkowski closed 3 months ago

Gorkowski commented 4 months ago

I was looking a LangChain, and I kind of like how they setup their process.

They have a template to generate runnable objects (or process), then compose them in a series, then evaluate the composed object. Each step can be modified, run, or composed in a different order.

From LangChain doc: https://github.com/langchain-ai/langchain/blob/master/libs/core/langchain_core/runnables/base.py """A sequence of runnables, where the output of each is the input of the next.

RunnableSequence is the most important composition operator in LangChain as it is
used in virtually every chain.

A RunnableSequence can be instantiated directly or more commonly by using the `|`
operator where either the left or right operands (or both) must be a Runnable.

Any RunnableSequence automatically supports sync, async, batch.

The default implementations of `batch` and `abatch` utilize threadpools and
asyncio gather and will be faster than naive invocation of invoke or ainvoke
for IO bound runnables.

Batching is implemented by invoking the batch method on each component of the
RunnableSequence in order.

A RunnableSequence preserves the streaming properties of its components, so if all
components of the sequence implement a `transform` method -- which
is the method that implements the logic to map a streaming input to a streaming
output -- then the sequence will be able to stream input to output!

If any component of the sequence does not implement transform then the
streaming will only begin after this component is run. If there are
multiple blocking components, streaming begins after the last one.

Please note: RunnableLambdas do not support `transform` by default! So if
    you need to use a RunnableLambdas be careful about where you place them in a
    RunnableSequence (if you need to use the .stream()/.astream() methods).

    If you need arbitrary logic and need streaming, you can subclass
    Runnable, and implement `transform` for whatever logic you need.

Here is a simple example that uses simple functions to illustrate the use of
RunnableSequence:

    .. code-block:: python

        from langchain_core.runnables import RunnableLambda

        def add_one(x: int) -> int:
            return x + 1

        def mul_two(x: int) -> int:
            return x * 2

        runnable_1 = RunnableLambda(add_one)
        runnable_2 = RunnableLambda(mul_two)
        sequence = runnable_1 | runnable_2
        # Or equivalently:
        # sequence = RunnableSequence(first=runnable_1, last=runnable_2)
        sequence.invoke(1)
        await sequence.ainvoke(1)

        sequence.batch([1, 2, 3])
        await sequence.abatch([1, 2, 3]
Gorkowski commented 4 months ago

so Aerosol could be something like

Template Method Pattern

The Template Method Pattern could be employed to define the skeleton of an aerosol process algorithm, letting subclasses redefine certain steps of the algorithm without changing its structure. For instance, a generic AerosolProcess class could outline steps common to coagulation, condensation, chemistry, and nucleation, while allowing specific details to be fleshed out by subclasses like CoagulationProcess, CondensationProcess, etc.

Factory Method Pattern

The Factory Method Pattern can facilitate the creation of objects without specifying the exact class of object that will be created. This is particularly useful for aerosol modeling, where different processes might be selected based on atmospheric conditions or other criteria. A ProcessFactory could dynamically instantiate specific AerosolProcess subclasses based on input parameters, enhancing the system's flexibility and adaptability.

Composition of Runnable Objects

Aerosol modeling processes could be structured as a series of runnable objects, where each process (e.g., coagulation, condensation) is encapsulated as a runnable that can be executed in sequence or parallel, depending on the model's requirements.