Skip to content

Getting started

This guide creates a complete, runnable simulation from scratch: a Sine component that produces a sine wave, executed by the dynamic solver.

Prerequisites

1. Create a new project

dotnet new console -n MyFirstSim
cd MyFirstSim

2. Add the Easy2Sim package

dotnet add package Easy2Sim

3. Add your first simulation component

Every simulation component inherits from SimulationBase and exposes its state as SimulationValue<T> fields. Add a new file Sine.cs:

Sine.cs
using Easy2Sim.Connect;
using Easy2Sim.Environment;
using Easy2Sim.Solvers;
using Newtonsoft.Json;

namespace Easy2SimExamples;

/// <summary>
/// A simulation component that produces a sine wave.
/// </summary>
public class Sine : SimulationBase // (1)
{
    [JsonProperty]
    public SimulationValue<double> Output;

    [JsonProperty]
    public SimulationValue<double> Amplitude;
    [JsonProperty]
    public SimulationValue<double> Frequency;
    [JsonProperty]
    public SimulationValue<double> Offset;
    [JsonProperty]
    public SimulationValue<int> NumberOfSamples;

    // The parameterless constructor is needed for serialization (2)
    public Sine()
    {
        Amplitude = new SimulationValue<double>(1.0, nameof(Amplitude), this, SimulationValueAttributes.Parameter);
        Frequency = new SimulationValue<double>(1.0, nameof(Frequency), this, SimulationValueAttributes.Parameter);
        Offset = new SimulationValue<double>(0.0, nameof(Offset), this, SimulationValueAttributes.Parameter);
        Output = new SimulationValue<double>(0.0, nameof(Output), this, SimulationValueAttributes.Output); // (3)
        NumberOfSamples = new SimulationValue<int>(100, nameof(NumberOfSamples), this, SimulationValueAttributes.Parameter);
    }

    // Use this constructor when you create components, it registers the component in the environment (4)
    public Sine(SimulationEnvironment environment, SolverBase solver) : base(environment, solver)
    {
        Amplitude = new SimulationValue<double>(1.0, nameof(Amplitude), this, SimulationValueAttributes.Parameter);
        Frequency = new SimulationValue<double>(1.0, nameof(Frequency), this, SimulationValueAttributes.Parameter);
        Offset = new SimulationValue<double>(0.0, nameof(Offset), this, SimulationValueAttributes.Parameter);
        Output = new SimulationValue<double>(0.0, nameof(Output), this, SimulationValueAttributes.Output);
        NumberOfSamples = new SimulationValue<int>(100, nameof(NumberOfSamples), this, SimulationValueAttributes.Parameter);
    }

    // DynamicCalculation is called once per time step by the dynamic solver (5)
    public override void DynamicCalculation()
    {
        if (Solver == null) return;

        double timeInSeconds = (double)Solver.SimulationTime / NumberOfSamples.Value;
        double angle = 2 * Math.PI * Frequency.Value * timeInSeconds + Offset.Value;
        double sineValue = Amplitude.Value * Math.Sin(angle);

        Output.SetValue(sineValue, SimulationEventType.DiscreteCalculation);
    }
}

4. Run the simulation

Replace the content of Program.cs:

Program.cs
using Easy2Sim.Environment;
using Easy2Sim.Solvers.Dynamic;
using Easy2SimExamples;

// 1. Create the simulation environment and a solver
SimulationEnvironment environment = new SimulationEnvironment();
DynamicSolver solver = new DynamicSolver(environment);

// 2. Create simulation components - they register themselves in the environment
Sine sine = new Sine(environment, solver);

// Print the output value every 10 time steps
sine.Output.PropertyChanged += (_, _) =>
{
    if (solver.SimulationTime % 10 == 0)
        Console.WriteLine($"t={solver.SimulationTime,3}  output={sine.Output.Value,8:F4}");
};

// 3. Initialize the simulation (calls Initialize() on every component)
solver.Initialize();

// 4. Run the simulation until time step 100
solver.CalculateTo(100);

5. Execute

dotnet run

Expected output:

t=  0  output=  0.0000
t= 10  output=  0.5878
t= 20  output=  0.9511
t= 30  output=  0.9511
t= 40  output=  0.5878
t= 50  output=  0.0000
t= 60  output= -0.5878
t= 70  output= -0.9511
t= 80  output= -0.9511
t= 90  output= -0.5878
t=100  output= -0.0000

How a simulation runs

Every Easy2Sim simulation follows the same steps:

  1. Implement your simulation components (inherit from SimulationBase)
  2. Create a SimulationEnvironment and a solver
  3. Create the components — they register themselves in the environment
  4. Add connections between components (optional)
  5. Call solver.Initialize(), then run with solver.CalculateTo(maxTime) or solver.CalculateFinish()

Note

Decide which simulation type you need before you start: use a DynamicSolver when every component should be calculated once per time step (continuous simulation), or a DiscreteSolver when components should only run when events occur (discrete-event simulation). See Solvers for details and a complete discrete-event example.

Next steps