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
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