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
- .NET 8 SDK or newer
1. Create a new project
2. Add the Easy2Sim package
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
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:
- Implement your simulation components (inherit from
SimulationBase) - Create a
SimulationEnvironmentand a solver - Create the components — they register themselves in the environment
- Add connections between components (optional)
- Call
solver.Initialize(), then run withsolver.CalculateTo(maxTime)orsolver.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
- Components and connections — simulation values, execution order and connections in detail
- Solvers — dynamic vs. discrete-event solver
- Visualization (WPF) — visualize the simulation live with charts or custom WPF components