Skip to content

Commit

Permalink
Remove oversample and iterations from simulation
Browse files Browse the repository at this point in the history
  • Loading branch information
dsharlet committed Jan 31, 2024
1 parent 1dd194a commit 5451e03
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 286 deletions.
225 changes: 95 additions & 130 deletions Circuit/Simulation/Simulation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public class Simulation
/// <summary>
/// Get the timestep for the simulation.
/// </summary>
public double TimeStep { get { return (double)(Solution.TimeStep * oversample); } }
public double TimeStep { get { return (double)Solution.TimeStep; } }

private ILog log = new NullLog();
/// <summary>
Expand All @@ -69,22 +69,10 @@ public TransientSolution Solution
set { solution = value; InvalidateProcess(); }
}

private int oversample = 8;
/// <summary>
/// Oversampling factor for this simulation.
/// </summary>
public int Oversample { get { return oversample; } set { oversample = value; InvalidateProcess(); } }

private int iterations = 8;
/// <summary>
/// Maximum number of iterations allowed for the simulation to converge.
/// </summary>
public int Iterations { get { return iterations; } set { iterations = value; InvalidateProcess(); } }

/// <summary>
/// The sampling rate of this simulation, the sampling rate of the transient solution divided by the oversampling factor.
/// </summary>
public Expression SampleRate { get { return 1 / (Solution.TimeStep * oversample); } }
public Expression SampleRate { get { return 1 / Solution.TimeStep; } }

private Expression[] input = new Expression[] { };
/// <summary>
Expand Down Expand Up @@ -228,10 +216,7 @@ private Action<int, double, double[][], double[][]> DefineProcess()
LinqExpr Zero = LinqExpr.Constant(0);

// double h = T / Oversample
LinqExpr h = LinqExpr.Constant(TimeStep / (double)Oversample);

// double invOversample = 1 / Oversample
LinqExpr invOversample = LinqExpr.Constant(1.0 / (double)Oversample);
LinqExpr h = LinqExpr.Constant(TimeStep);

// Load the globals to local variables and add them to the map.
foreach (KeyValuePair<Expression, GlobalExpr<double>> i in globals)
Expand Down Expand Up @@ -261,141 +246,121 @@ private Action<int, double, double[][], double[][]> DefineProcess()
() => code.Add(LinqExpr.PreIncrementAssign(n)),
() =>
{
// Prepare input samples for oversampling interpolation.
Dictionary<Expression, LinqExpr> dVi = new Dictionary<Expression, LinqExpr>();
// t += h
code.Add(LinqExpr.AddAssign(t, h));

// Interpolate the input samples.
foreach (Expression i in Input.Distinct())
{
LinqExpr Va = code[i];
// Sum all inputs with this key.
IEnumerable<LinqExpr> Vbs = inputs.Where(j => j.Key.Equals(i)).Select(j => j.Value);
LinqExpr Vb = LinqExpr.ArrayAccess(Vbs.First(), n);
LinqExpr v = LinqExpr.ArrayAccess(Vbs.First(), n);
foreach (LinqExpr j in Vbs.Skip(1))
Vb = LinqExpr.Add(Vb, LinqExpr.ArrayAccess(j, n));

// dVi = (Vb - Va) / Oversample
code.Add(LinqExpr.Assign(
Decl<double>(code, dVi, i, "d" + i.ToString().Replace("[t]", "")),
LinqExpr.Multiply(LinqExpr.Subtract(Vb, Va), invOversample)));
v = LinqExpr.Add(v, LinqExpr.ArrayAccess(j, n));
code.Add(LinqExpr.Assign(code[i], v));
}

// Prepare output sample accumulators for low pass filtering.
Dictionary<Expression, LinqExpr> Vo = new Dictionary<Expression, LinqExpr>();
foreach (Expression i in Output.Distinct())
code.Add(LinqExpr.Assign(
Decl<double>(code, Vo, i, i.ToString().Replace("[t]", "")),
LinqExpr.Constant(0.0)));

// int ov = Oversample;
// do { -- ov; } while(ov > 0)
ParamExpr ov = code.Decl<int>("ov");
code.Add(LinqExpr.Assign(ov, LinqExpr.Constant(Oversample)));
code.DoWhile(() =>
// Compile all of the SolutionSets in the solution.
foreach (SolutionSet ss in Solution.Solutions)
{
// t += h
code.Add(LinqExpr.AddAssign(t, h));

// Interpolate the input samples.
foreach (Expression i in Input.Distinct())
code.Add(LinqExpr.AddAssign(code[i], dVi[i]));

// Compile all of the SolutionSets in the solution.
foreach (SolutionSet ss in Solution.Solutions)
if (ss is LinearSolutions)
{
if (ss is LinearSolutions)
{
// Linear solutions are easy.
LinearSolutions S = (LinearSolutions)ss;
foreach (Arrow i in S.Solutions)
code.DeclInit(i.Left, i.Right);
}
else if (ss is NewtonIteration)
// Linear solutions are easy.
LinearSolutions S = (LinearSolutions)ss;
foreach (Arrow i in S.Solutions)
code.DeclInit(i.Left, i.Right);
}
else if (ss is NewtonIteration)
{
NewtonIteration S = (NewtonIteration)ss;

// Start with the initial guesses from the solution.
foreach (Arrow i in S.Guesses)
code.DeclInit(i.Left, i.Right);

// int it = MaxIterations
// This used to be a parameter, defaulted to 8. Experiments seem to show that
// it basically never makes sense to stop iterating at a limit. This sort of
// makes sense, because stopping iteration early suggests that the initial guess
// for the next sample will be worse. So, we're just going to use a very large
// number of iterations just to avoid getting stuck forever.
LinqExpr it = code.ReDeclInit<int>("it", 128);
// do { ... --it } while(it > 0)
code.DoWhile((Break) =>
{
NewtonIteration S = (NewtonIteration)ss;
// Solve the un-solved system.
Solve(code, JxF, S.Equations, S.UnknownDeltas);

// Start with the initial guesses from the solution.
foreach (Arrow i in S.Guesses)
code.DeclInit(i.Left, i.Right);
// Compile the pre-solved solutions.
if (S.KnownDeltas != null)
foreach (Arrow i in S.KnownDeltas)
code.DeclInit(i.Left, i.Right);

// int it = iterations
LinqExpr it = code.ReDeclInit<int>("it", Iterations);
// do { ... --it } while(it > 0)
code.DoWhile((Break) =>
// bool done = true
LinqExpr done = code.ReDeclInit("done", true);
foreach (Expression i in S.Unknowns)
{
// Solve the un-solved system.
Solve(code, JxF, S.Equations, S.UnknownDeltas);

// Compile the pre-solved solutions.
if (S.KnownDeltas != null)
foreach (Arrow i in S.KnownDeltas)
code.DeclInit(i.Left, i.Right);

// bool done = true
LinqExpr done = code.ReDeclInit("done", true);
foreach (Expression i in S.Unknowns)
{
LinqExpr v = code[i];
LinqExpr dv = code[NewtonIteration.Delta(i)];

// done &= (|dv| < |v|*epsilon)
code.Add(LinqExpr.AndAssign(done, LinqExpr.LessThan(Abs(dv), MultiplyAdd(Abs(v), LinqExpr.Constant(1e-4), LinqExpr.Constant(1e-6)))));
// v += dv
code.Add(LinqExpr.AddAssign(v, dv));
}
// if (done) break
code.Add(LinqExpr.IfThen(done, Break));

// --it;
code.Add(LinqExpr.PreDecrementAssign(it));
}, LinqExpr.GreaterThan(it, Zero));

//// bool failed = false
//LinqExpr failed = Decl(code, code, "failed", LinqExpr.Constant(false));
//for (int i = 0; i < eqs.Length; ++i)
// // failed |= |JxFi| > epsilon
// code.Add(LinqExpr.OrAssign(failed, LinqExpr.GreaterThan(Abs(eqs[i].ToExpression().Compile(map)), LinqExpr.Constant(1e-3))));

//code.Add(LinqExpr.IfThen(failed, ThrowSimulationDiverged(n)));
}
LinqExpr v = code[i];
LinqExpr dv = code[NewtonIteration.Delta(i)];

// done &= (|dv| < |v|*epsilon)
code.Add(LinqExpr.AndAssign(done, LinqExpr.LessThan(Abs(dv), MultiplyAdd(Abs(v), LinqExpr.Constant(1e-4), LinqExpr.Constant(1e-6)))));
// v += dv
code.Add(LinqExpr.AddAssign(v, dv));
}
// if (done) break
code.Add(LinqExpr.IfThen(done, Break));

// --it;
code.Add(LinqExpr.PreDecrementAssign(it));
}, LinqExpr.GreaterThan(it, Zero));

//// bool failed = false
//LinqExpr failed = Decl(code, code, "failed", LinqExpr.Constant(false));
//for (int i = 0; i < eqs.Length; ++i)
// // failed |= |JxFi| > epsilon
// code.Add(LinqExpr.OrAssign(failed, LinqExpr.GreaterThan(Abs(eqs[i].ToExpression().Compile(map)), LinqExpr.Constant(1e-3))));

//code.Add(LinqExpr.IfThen(failed, ThrowSimulationDiverged(n)));
}
}

// Update the previous timestep variables.
foreach (SolutionSet S in Solution.Solutions)
// Update the previous timestep variables.
foreach (SolutionSet S in Solution.Solutions)
{
for (int m = MaxDelay; m < 0; m++)
{
for (int m = MaxDelay; m < 0; m++)
{
Arrow t_tm = Arrow.New(Simulation.t, Simulation.t + m * Solution.TimeStep);
Arrow t_tm1 = Arrow.New(Simulation.t, Simulation.t + (m + 1) * Solution.TimeStep);
foreach (Expression i in S.Unknowns.Where(i => globals.Keys.Contains(i.Evaluate(t_tm))))
code.Add(LinqExpr.Assign(code[i.Evaluate(t_tm)], code[i.Evaluate(t_tm1)]));
}
Arrow t_tm = Arrow.New(Simulation.t, Simulation.t + m * Solution.TimeStep);
Arrow t_tm1 = Arrow.New(Simulation.t, Simulation.t + (m + 1) * Solution.TimeStep);
foreach (Expression i in S.Unknowns.Where(i => globals.Keys.Contains(i.Evaluate(t_tm))))
code.Add(LinqExpr.Assign(code[i.Evaluate(t_tm)], code[i.Evaluate(t_tm1)]));
}
}

// Vo += i
foreach (Expression i in Output.Distinct())
Dictionary<Expression, LinqExpr> Vo = new Dictionary<Expression, LinqExpr>();

// Vo = i
foreach (Expression i in Output.Distinct())
{
try
{
LinqExpr Voi = LinqExpr.Constant(0.0);
try
{
Voi = code.Compile(i);
}
catch (Exception Ex)
{
Log.WriteLine(MessageType.Warning, Ex.Message);
}
code.Add(LinqExpr.AddAssign(Vo[i], Voi));
code.Add(LinqExpr.Assign(
Decl<double>(code, Vo, i, i.ToString().Replace("[t]", "")),
code.Compile(i)));
}
catch (Exception Ex)
{
Log.WriteLine(MessageType.Warning, Ex.Message);
}
}

// Vi_t0 = Vi
foreach (Expression i in Input.Distinct())
code.Add(LinqExpr.Assign(code[i.Evaluate(t_t1)], code[i]));

// --ov;
code.Add(LinqExpr.PreDecrementAssign(ov));
}, LinqExpr.GreaterThan(ov, Zero));
// Vi_t0 = V
foreach (Expression i in Input.Distinct())
code.Add(LinqExpr.Assign(code[i.Evaluate(t_t1)], code[i]));

// Output[i][n] = Vo / Oversample
// Output[i][n] = Vo
foreach (KeyValuePair<Expression, LinqExpr> i in outputs)
code.Add(LinqExpr.Assign(LinqExpr.ArrayAccess(i.Value, n), LinqExpr.Multiply(Vo[i.Key], invOversample)));
code.Add(LinqExpr.Assign(LinqExpr.ArrayAccess(i.Value, n), Vo[i.Key]));

// Every 256 samples, check for divergence.
if (Vo.Any())
Expand Down
19 changes: 1 addition & 18 deletions LiveSPICE/LiveSimulation.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -78,24 +78,7 @@

<!-- Toolbars -->
<ToolBarTray Grid.Row="2">
<ToolBar Band="1" BandIndex="1">
<TextBlock Text="Oversample:" Margin="5, 0, 2, 0" HorizontalAlignment="Right" VerticalAlignment="Center" />
<ComboBox Width="40" IsEditable="True" Text="{Binding Oversample, UpdateSourceTrigger=PropertyChanged, Delay=1000}">
<ComboBoxItem>1</ComboBoxItem>
<ComboBoxItem>2</ComboBoxItem>
<ComboBoxItem>3</ComboBoxItem>
<ComboBoxItem>4</ComboBoxItem>
<ComboBoxItem>6</ComboBoxItem>
<ComboBoxItem>8</ComboBoxItem>
<ComboBoxItem>12</ComboBoxItem>
<ComboBoxItem>16</ComboBoxItem>
</ComboBox>

<TextBlock Text="Iterations:" Margin="5, 0, 2, 0" HorizontalAlignment="Right" VerticalAlignment="Center" />
<xctk:IntegerUpDown Value="{Binding Iterations, Delay=1000}" Minimum="1" Maximum="64" />

<Separator />

<ToolBar Band="1" BandIndex="1">
<ls:ImageButton CommandImage="{x:Static ls:Commands.Simulate}" ImageHeight="16" />
</ToolBar>
<!-- View -->
Expand Down
28 changes: 2 additions & 26 deletions LiveSPICE/LiveSimulation.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,6 @@ partial class LiveSimulation : Window, INotifyPropertyChanged
public Log Log { get { return (Log)log.Content; } }
public Scope Scope { get { return (Scope)scope.Content; } }

protected int oversample = 8;
/// <summary>
/// Simulation oversampling rate.
/// </summary>
public int Oversample
{
get { return oversample; }
set { oversample = Math.Max(1, value); RebuildSolution(); NotifyChanged(nameof(Oversample)); }
}

protected int iterations = 8;
/// <summary>
/// Max iterations for numerical algorithms.
/// </summary>
public int Iterations
{
get { return iterations; }
set { iterations = Math.Max(1, value); RebuildSolution(); NotifyChanged(nameof(Iterations)); }
}

private double inputGain = 1.0;
/// <summary>
/// Overall input gain.
Expand Down Expand Up @@ -295,16 +275,14 @@ private void RebuildSolution()
{
try
{
ComputerAlgebra.Expression h = (ComputerAlgebra.Expression)1 / (stream.SampleRate * Oversample);
ComputerAlgebra.Expression h = (ComputerAlgebra.Expression)1 / stream.SampleRate;
TransientSolution solution = Circuit.TransientSolution.Solve(circuit.Analyze(), h, Log);

simulation = new Simulation(solution)
{
Log = Log,
Input = inputs.Keys.ToArray(),
Output = probes.Select(i => i.V).Concat(OutputChannels.Select(i => i.Signal)).ToArray(),
Oversample = Oversample,
Iterations = Iterations,
};
}
catch (Exception Ex)
Expand All @@ -323,7 +301,7 @@ private void UpdateSimulation(bool Rebuild)
int id = Interlocked.Increment(ref update);
new Task(() =>
{
ComputerAlgebra.Expression h = (ComputerAlgebra.Expression)1 / (stream.SampleRate * Oversample);
ComputerAlgebra.Expression h = (ComputerAlgebra.Expression)1 / stream.SampleRate;
TransientSolution s = Circuit.TransientSolution.Solve(circuit.Analyze(), h, Rebuild ? (ILog)Log : new NullLog());
lock (sync)
{
Expand All @@ -336,8 +314,6 @@ private void UpdateSimulation(bool Rebuild)
Log = Log,
Input = inputs.Keys.ToArray(),
Output = probes.Select(i => i.V).Concat(OutputChannels.Select(i => i.Signal)).ToArray(),
Oversample = Oversample,
Iterations = Iterations,
};
}
else
Expand Down
Loading

0 comments on commit 5451e03

Please sign in to comment.