diff --git a/Circuit/Analysis.cs b/Circuit/Analysis.cs index ba49a030..9dc3c7b3 100644 --- a/Circuit/Analysis.cs +++ b/Circuit/Analysis.cs @@ -238,7 +238,11 @@ public Expression AddUnknownEqualTo(string Name, Expression Eq) /// /// /// - public Expression AddUnknownEqualTo(Expression Eq) { return AddUnknownEqualTo(AnonymousName(), Eq); } + public Expression AddUnknownEqualTo(Expression x) { + if (x is Constant) return x; + if (x is Call c && c.Target is UnknownFunction) return x; + return AddUnknownEqualTo(AnonymousName(), x); + } /// /// Add initial conditions to the system. @@ -253,6 +257,55 @@ public Expression AddUnknownEqualTo(string Name, Expression Eq) /// public string AnonymousName() { return context.AnonymousName(); } + /// + /// Describes a parameter for the circuit. + /// + public class Parameter + { + private Component of; + /// + /// Component the parameter affects. + /// + public Component Of { get { return of; } } + + private Expression expr; + /// + /// Expression for the parameter in the system of equations for this analysis. + /// + public Expression Expression { get { return expr; } } + + Func getter; + /// + /// Current value of the parameter. + /// + public double Value { get { return getter(); } } + + public Parameter(Component Of, Expression Expression, Func Getter) + { + of = Of; + expr = Expression; + getter = Getter; + } + } + + private List parameters = new List(); + /// + /// Enumerates the parameters in this analysis. + /// + public IEnumerable Parameters { get { return parameters; } } + + /// + /// Create an expression for a parameter in the current context. + /// + /// + /// + public Expression AddParameter(Component Of, string Name, Func Getter) + { + Expression expr = Variable.New(context.Prefix + Name); + parameters.Add(new Parameter(Of, expr, Getter)); + return expr; + } + private void AddKcl(Dictionary kcl, Expression V, Expression i) { if (kcl.TryGetValue(V, out var sumi)) diff --git a/Circuit/Component.cs b/Circuit/Component.cs index 87c028b5..f05f90e6 100644 --- a/Circuit/Component.cs +++ b/Circuit/Component.cs @@ -21,7 +21,7 @@ public interface IGroupableComponent /// /// Interface for components that expose a pot controlled value. /// - public interface IPotControl: IGroupableComponent + public interface IPotControl : IGroupableComponent { /// /// The value of the pot. @@ -32,7 +32,7 @@ public interface IPotControl: IGroupableComponent /// /// Interface for components that expose a button controlled value. /// - public interface IButtonControl: IGroupableComponent + public interface IButtonControl : IGroupableComponent { void Click(); int Position { get; set; } diff --git a/Circuit/Components/Capacitor.cs b/Circuit/Components/Capacitor.cs index fe77ba6c..c8d98f66 100644 --- a/Circuit/Components/Capacitor.cs +++ b/Circuit/Components/Capacitor.cs @@ -25,7 +25,7 @@ public static Expression Analyze(Analysis Mna, string Name, Node Anode, Node Cat V = Mna.AddUnknownEqualTo("V" + Name, V); // i = C*dV/dt Expression i = C * D(V, t); - //i = Mna.AddUnknownEqualTo("i" + Name, i); + i = Mna.AddUnknownEqualTo("i" + Name, i); Mna.AddPassiveComponent(Anode, Cathode, i); return i; } diff --git a/Circuit/Components/Potentiometer.cs b/Circuit/Components/Potentiometer.cs index 278ab974..c028e368 100644 --- a/Circuit/Components/Potentiometer.cs +++ b/Circuit/Components/Potentiometer.cs @@ -63,7 +63,7 @@ public void ConnectTo(Node A, Node C, Node W) public override void Analyze(Analysis Mna) { - Expression P = VariableResistor.AdjustWipe(wipe, sweep); + Expression P = Mna.AddParameter(this, Name, () => VariableResistor.AdjustWipe(Wipe, Sweep)); Expression R1 = Resistance * P; Expression R2 = Resistance * (1 - P); diff --git a/Circuit/Components/VacuumTubes/Triode.cs b/Circuit/Components/VacuumTubes/Triode.cs index 8fcec460..f2928405 100644 --- a/Circuit/Components/VacuumTubes/Triode.cs +++ b/Circuit/Components/VacuumTubes/Triode.cs @@ -146,7 +146,8 @@ public override void Analyze(Analysis Mna) break; case TriodeModel.Koren: Expression E1 = Ln1Exp(Kp * (1.0 / Mu + Vgk * Binary.Power(Kvb + Vpk * Vpk, -0.5))) * Vpk / Kp; - ip = Mna.AddUnknownEqualTo(Call.If(E1 > 0, 2d * (E1 ^ Ex) / Kg, 0)); + ip = Call.If(E1 > 0, 2d * (E1 ^ Ex) / Kg, 0); + ip = Mna.AddUnknownEqualTo("Ip", ip); var vg = (Real)Vg; var knee = (Real)Kn; @@ -156,7 +157,8 @@ public override void Analyze(Analysis Mna) var b = (knee - vg) / (2 * knee * rg1); var c = (-a * Binary.Power(vg - knee, 2)) - (b * (vg - knee)); - ig = Mna.AddUnknownEqualTo(Call.If(Vgk < vg - knee, 0, Call.If(Vgk > vg + knee, (Vgk - vg) / rg1, a * Vgk * Vgk + b * Vgk + c))); + ig = Call.If(Vgk < vg - knee, 0, Call.If(Vgk > vg + knee, (Vgk - vg) / rg1, a * Vgk * Vgk + b * Vgk + c)); + ig = Mna.AddUnknownEqualTo("Ig", ig); ik = -(ip + ig); break; case TriodeModel.DempwolfZolzer: diff --git a/Circuit/Components/VariableResistor.cs b/Circuit/Components/VariableResistor.cs index 13f5abf3..73f2d476 100644 --- a/Circuit/Components/VariableResistor.cs +++ b/Circuit/Components/VariableResistor.cs @@ -64,7 +64,7 @@ public class VariableResistor : TwoTerminal, IPotControl protected double wipe = 0.5; [Serialize, Description("Position of the wiper on this variable resistor, between 0 and 1.")] - public double Wipe { get { return wipe; } set { wipe = value; NotifyChanged(nameof(Wipe)); } } + public double Wipe { get { return wipe; } set { wipe = value; NotifyChanged(nameof(Wipe)); NotifyChanged(nameof(IPotControl.PotValue)); } } // IPotControl double IPotControl.PotValue { get { return Wipe; } set { Wipe = value; } } @@ -80,7 +80,7 @@ public class VariableResistor : TwoTerminal, IPotControl public override void Analyze(Analysis Mna) { - Expression P = AdjustWipe(wipe, sweep); + Expression P = Mna.AddParameter(this, Name, () => AdjustWipe(Wipe, Sweep)); Resistor.Analyze(Mna, Name, Anode, Cathode, (Expression)Resistance * P); } diff --git a/Circuit/Components/VoltageSource.cs b/Circuit/Components/VoltageSource.cs index d8be7ef9..686fcb5b 100644 --- a/Circuit/Components/VoltageSource.cs +++ b/Circuit/Components/VoltageSource.cs @@ -26,6 +26,9 @@ public static void Analyze(Analysis Mna, string Name, Node Anode, Node Cathode, { // Unknown current. Mna.AddPassiveComponent(Anode, Cathode, Mna.AddUnknown("i" + Name)); + + V = Mna.AddUnknownEqualTo(V); + // Set the voltage. Mna.AddEquation(Anode.V - Cathode.V, V); } @@ -35,7 +38,10 @@ public static void Analyze(Analysis Mna, string Name, Node Anode, Node Cathode, Mna.AddInitialConditions(InitialConditions); } public static void Analyze(Analysis Mna, Node Anode, Node Cathode, Expression V) { Analyze(Mna, Mna.AnonymousName(), Anode, Cathode, V); } - public static void Analyze(Analysis Mna, Node Anode, Node Cathode, Expression V, Arrow InitialConditions) { Analyze(Mna, Mna.AnonymousName(), Anode, Cathode, V, InitialConditions); } + public static void Analyze(Analysis Mna, Node Anode, Node Cathode, Expression V, Arrow InitialConditions) + { + Analyze(Mna, Mna.AnonymousName(), Anode, Cathode, V, InitialConditions); + } public override void Analyze(Analysis Mna) { Analyze(Mna, Name, Anode, Cathode, Voltage); } diff --git a/Circuit/Simulation/Simulation.cs b/Circuit/Simulation/Simulation.cs index bac949f5..7d7c4594 100644 --- a/Circuit/Simulation/Simulation.cs +++ b/Circuit/Simulation/Simulation.cs @@ -6,6 +6,7 @@ using System.Numerics; using System.Reflection; using Util; +using static Circuit.Analysis; using LinqExpr = System.Linq.Expressions.Expression; using ParamExpr = System.Linq.Expressions.ParameterExpression; @@ -98,6 +99,12 @@ public TransientSolution Solution /// public IEnumerable Output { get { return output; } set { output = value.ToArray(); InvalidateProcess(); } } + private Expression[] arguments = new Expression[] { }; + /// + /// Expressions for parameters. + /// + public IEnumerable Arguments { get { return arguments; } set { arguments = value.ToArray(); InvalidateProcess(); } } + // Stores any global state in the simulation (previous state values, mostly). private Dictionary> globals = new Dictionary>(); // Add a new global and set it to 0 if it didn't already exist. @@ -153,7 +160,8 @@ public Simulation(TransientSolution Solution) /// Number of samples to process. /// Buffers that describe the input samples. /// Buffers to receive output samples. - public void Run(int N, IEnumerable Input, IEnumerable Output) + /// Scalar values for the arguments. + public void Run(int N, IEnumerable Input, IEnumerable Output, IEnumerable Parameters) { if (_process == null) _process = DefineProcess(); @@ -162,7 +170,7 @@ public void Run(int N, IEnumerable Input, IEnumerable Output { try { - _process(N, n*TimeStep, Input.AsArray(), Output.AsArray()); + _process(N, n*TimeStep, Input.AsArray(), Output.AsArray(), Parameters.AsArray()); n += N; } catch (TargetInvocationException Ex) @@ -175,11 +183,11 @@ public void Run(int N, IEnumerable Input, IEnumerable Output throw new SimulationDiverged("Simulation diverged near t = " + Quantity.ToString(Time, Units.s) + " + " + Ex.At, n + Ex.At); } } - public void Run(int N, IEnumerable Output) { Run(N, new double[][] { }, Output); } - public void Run(double[] Input, IEnumerable Output) { Run(Input.Length, new[] { Input }, Output); } - public void Run(double[] Input, double[] Output) { Run(Input.Length, new[] { Input }, new[] { Output }); } + public void Run(int N, IEnumerable Output, IEnumerable Parameters) { Run(N, new double[][] { }, Output, Parameters); } + public void Run(double[] Input, IEnumerable Output, IEnumerable Parameters) { Run(Input.Length, new[] { Input }, Output, Parameters); } + public void Run(double[] Input, double[] Output, IEnumerable Parameters) { Run(Input.Length, new[] { Input }, new[] { Output }, Parameters); } - private Action _process; + private Action _process; // Force rebuilding of the process function. private void InvalidateProcess() { @@ -189,11 +197,12 @@ private void InvalidateProcess() // The resulting lambda processes N samples, using buffers provided for Input and Output: // void Process(int N, double t0, double T, double[] Input0 ..., double[] Output0 ...) // { ... } - private Action DefineProcess() + private Action DefineProcess() { // Map expressions to identifiers in the syntax tree. var inputs = new List>(); var outputs = new List>(); + var parameters = new List>(); // Lambda code generator. CodeGen code = new CodeGen(); @@ -203,6 +212,7 @@ private Action DefineProcess() ParamExpr t = code.Decl(Scope.Parameter, Simulation.t); var ins = code.Decl(Scope.Parameter, "ins"); var outs = code.Decl(Scope.Parameter, "outs"); + var parms = code.Decl(Scope.Parameter, "params"); // Create buffer parameters for each input... for (int i = 0; i < input.Length; i++) @@ -216,6 +226,11 @@ private Action DefineProcess() outputs.Add(new KeyValuePair(output[i], LinqExpr.ArrayAccess(outs, LinqExpr.Constant(i)))); } + for (int i = 0; i < arguments.Length; i++) + { + parameters.Add(new KeyValuePair(arguments[i], LinqExpr.ArrayAccess(parms, LinqExpr.Constant(i)))); + } + Arrow t_t1 = Arrow.New(Simulation.t, Simulation.t - Solution.TimeStep); // Create globals to store previous values of inputs. @@ -240,6 +255,9 @@ private Action DefineProcess() foreach (KeyValuePair i in inputs) code.DeclInit(i.Key, code[i.Key.Evaluate(t_t1)]); + foreach (KeyValuePair i in parameters) + code.DeclInit(i.Key, i.Value); + // Create arrays for linear systems. int M = Solution.Solutions.OfType().Max(i => i.Equations.Count(), 0); int N = Solution.Solutions.OfType().Max(i => i.UnknownDeltas.Count(), 0); @@ -409,7 +427,7 @@ private Action DefineProcess() foreach (KeyValuePair> i in globals) code.Add(LinqExpr.Assign(i.Value, code[i.Key])); - var lambda = code.Build>(); + var lambda = code.Build>(); return lambda.Compile(); } diff --git a/Circuit/Simulation/TransientSolution.cs b/Circuit/Simulation/TransientSolution.cs index 5bcd775b..7ea5d924 100644 --- a/Circuit/Simulation/TransientSolution.cs +++ b/Circuit/Simulation/TransientSolution.cs @@ -79,10 +79,10 @@ public static TransientSolution Solve(Analysis Analysis, Expression TimeStep, IE // Define T = step size. DynamicNamespace globals = new DynamicNamespace(); globals.Add("T", h); - // Define d[t] = delta function. + // Define dq = delta function. // TODO: This should probably be centered around 0, and also have an integral of 1 (i.e. a height of 1 / h). globals.Add(ExprFunction.New("d", Call.If((0 <= t) & (t < h), 1, 0), t)); - // Define u[t] = step function. + // Define uq = step function. globals.Add(ExprFunction.New("u", Call.If(t >= 0, 1, 0), t)); mna = mna.Resolve(Analysis).Resolve(globals).OfType().ToList(); @@ -101,6 +101,8 @@ public static TransientSolution Solve(Analysis Analysis, Expression TimeStep, IE .Substitute(dy_dt.Select(i => Arrow.New(i, 0)).Append(Arrow.New(t, 0), Arrow.New(T, 0), SinglePoleSwitch.IncludeOpen)) // Use the initial conditions from analysis. .Substitute(Analysis.InitialConditions) + // Use the default parameter values. + .Substitute(Analysis.Parameters.Select(i => Arrow.New(i.Expression, i.Value))) // Evaluate variables at t=0. .OfType(), y.Select(j => j.Substitute(t, 0))); @@ -149,12 +151,13 @@ public static TransientSolution Solve(Analysis Analysis, Expression TimeStep, IE Log.WriteLine(MessageType.Verbose, "Partition unknowns: {0}", String.Join(", ", F.Unknowns)); // Find linear solutions for y. Linear systems should be completely solved here. F.RowReduce(); - IEnumerable linear = F.Solve(); - if (linear.Any()) + List linearFwd = new List(); + List linearBack = new List(); + F.PartialSolve(linearFwd, linearBack); + if (linearFwd.Any()) { - linear = Factor(linear); - solutions.Add(new LinearSolutions(linear)); - LogExpressions(Log, MessageType.Verbose, "Linear solutions:", linear); + solutions.Add(new LinearSolutions(linearFwd)); + LogExpressions(Log, MessageType.Verbose, "Linear solutions:", linearFwd); } // If there are any variables left, there are some non-linear equations requiring numerical techniques to solve. @@ -175,20 +178,23 @@ public static TransientSolution Solve(Analysis Analysis, Expression TimeStep, IE // Find linear solutions for dy. nonlinear.RowReduce(ly); IEnumerable solved = nonlinear.Solve(ly); - solved = Factor(solved); // Initial guess for y[t] = y[t - h]. IEnumerable guess = F.Unknowns.Select(i => Arrow.New(i, i.Substitute(t, t - h))).ToList(); - guess = Factor(guess); // Newton system equations. IEnumerable equations = nonlinear.Equations.Buffer(); - equations = Factor(equations); solutions.Add(new NewtonIteration(solved, equations, nonlinear.Unknowns, guess)); LogExpressions(Log, MessageType.Verbose, String.Format("Non-linear Newton's method updates ({0}):", String.Join(", ", nonlinear.Unknowns)), equations.Select(i => Equal.New(i, 0))); LogExpressions(Log, MessageType.Verbose, "Linear Newton's method updates:", solved); } + + if (linearBack.Any()) + { + solutions.Add(new LinearSolutions(linearBack)); + LogExpressions(Log, MessageType.Verbose, "Linear solutions:", linearBack); + } } Log.WriteLine(MessageType.Info, "System solved, {0} solution sets for {1} unknowns.", @@ -206,9 +212,6 @@ public static TransientSolution Solve(Analysis Analysis, Expression TimeStep, IE public static TransientSolution Solve(Analysis Analysis, Expression TimeStep, ILog Log) { return Solve(Analysis, TimeStep, new Arrow[] { }, Log); } public static TransientSolution Solve(Analysis Analysis, Expression TimeStep) { return Solve(Analysis, TimeStep, new Arrow[] { }, new NullLog()); } - private static IEnumerable Factor(IEnumerable x) { return x.Select(i => Arrow.New(i.Left, i.Right.Factor())).Buffer(); } - private static IEnumerable Factor(IEnumerable x) { return x.Select(i => LinearCombination.New(i.Select(j => new KeyValuePair(j.Key, j.Value.Factor())))).Buffer(); } - // Shorthand for df/dx. protected static Expression D(Expression f, Expression x) { return Call.D(f, x); } diff --git a/LiveSPICE/LiveSimulation.xaml.cs b/LiveSPICE/LiveSimulation.xaml.cs index c2231fb1..5cc856eb 100644 --- a/LiveSPICE/LiveSimulation.xaml.cs +++ b/LiveSPICE/LiveSimulation.xaml.cs @@ -5,6 +5,7 @@ using System.Diagnostics; using System.Linq; using System.Linq.Expressions; +using System.Printing; using System.Threading; using System.Threading.Tasks; using System.Windows; @@ -65,6 +66,7 @@ public int Iterations protected Simulation simulation = null; private List probes = new List(); + protected Dictionary namedArguments = new Dictionary(); private object sync = new object(); @@ -96,6 +98,52 @@ public LiveSimulation(Schematic Simulate, Audio.Device Device, Audio.Channel[] I // Build the circuit from the schematic. circuit = Schematic.Schematic.Build(Log); + Circuit.Analysis analysis = circuit.Analyze(); + + foreach (Circuit.Analysis.Parameter P in analysis.Parameters) + { + namedArguments[P.Expression] = P.Value; + + Circuit.Symbol S = P.Of.Tag as Circuit.Symbol; + if (S == null) + continue; + + SymbolControl tag = S.Tag as SymbolControl; + if (tag == null) + continue; + + IPotControl pot = (IPotControl)P.Of; + var potControl = new PotControl() + { + Width = 80, + Height = 80, + Opacity = 0.5, + FontSize = 15, + FontWeight = FontWeights.Bold, + Value = pot.PotValue, + }; + Schematic.Overlays.Children.Add(potControl); + Canvas.SetLeft(potControl, Canvas.GetLeft(tag) - potControl.Width / 2 + tag.Width / 2); + Canvas.SetTop(potControl, Canvas.GetTop(tag) - potControl.Height / 2 + tag.Height / 2); + + var binding = new Binding + { + Source = pot, + Path = new PropertyPath("(0)", typeof(IPotControl).GetProperty(nameof(IPotControl.PotValue))), + Mode = BindingMode.TwoWay, + NotifyOnSourceUpdated = true + }; + + potControl.SetBinding(PotControl.ValueProperty, binding); + + potControl.AddHandler(Binding.SourceUpdatedEvent, new RoutedEventHandler((o, args) => + { + lock (namedArguments) namedArguments[P.Expression] = P.Value; + })); + + potControl.MouseEnter += (o, e) => potControl.Opacity = 0.95; + potControl.MouseLeave += (o, e) => potControl.Opacity = 0.4; + } // Create the input and output controls. IEnumerable components = circuit.Components; @@ -116,47 +164,6 @@ public LiveSimulation(Schematic Simulate, Audio.Device Device, Audio.Channel[] I if (tag == null) continue; - // Create potentiometers. - if (i is IPotControl potentiometer) - { - var potControl = new PotControl() - { - Width = 80, - Height = 80, - Opacity = 0.5, - FontSize = 15, - FontWeight = FontWeights.Bold, - }; - Schematic.Overlays.Children.Add(potControl); - Canvas.SetLeft(potControl, Canvas.GetLeft(tag) - potControl.Width / 2 + tag.Width / 2); - Canvas.SetTop(potControl, Canvas.GetTop(tag) - potControl.Height / 2 + tag.Height / 2); - - var binding = new Binding - { - Source = potentiometer, - Path = new PropertyPath("(0)", typeof(IPotControl).GetProperty(nameof(IPotControl.PotValue))), - Mode = BindingMode.TwoWay, - NotifyOnSourceUpdated = true - }; - - potControl.SetBinding(PotControl.ValueProperty, binding); - - potControl.AddHandler(Binding.SourceUpdatedEvent, new RoutedEventHandler((o, args) => - { - if (!string.IsNullOrEmpty(potentiometer.Group)) - { - foreach (var p in components.OfType().Where(p => p != potentiometer && p.Group == potentiometer.Group)) - { - p.PotValue = (o as PotControl).Value; - } - } - UpdateSimulation(false); - })); - - potControl.MouseEnter += (o, e) => potControl.Opacity = 0.95; - potControl.MouseLeave += (o, e) => potControl.Opacity = 0.5; - } - // Create Buttons. if (i is IButtonControl b) { @@ -180,7 +187,7 @@ public LiveSimulation(Schematic Simulate, Audio.Device Device, Audio.Channel[] I foreach (var j in components.OfType().Where(x => x != b && x.Group == b.Group)) j.Click(); } - UpdateSimulation(true); + UpdateSimulation(); }; button.MouseEnter += (o, e) => button.Opacity = 0.95; @@ -286,6 +293,19 @@ public LiveSimulation(Schematic Simulate, Audio.Device Device, Audio.Channel[] I } } + private Simulation MakeSimulation(TransientSolution solution) + { + return new Simulation(solution) + { + Log = Log, + Input = inputs.Keys.ToArray(), + Output = probes.Select(i => i.V).Concat(OutputChannels.Select(i => i.Signal)).ToArray(), + Arguments = namedArguments.Select(i => i.Key).ToArray(), + Oversample = Oversample, + Iterations = Iterations, + }; + } + private void RebuildSolution() { lock (sync) @@ -297,15 +317,7 @@ private void RebuildSolution() { ComputerAlgebra.Expression h = (ComputerAlgebra.Expression)1 / (stream.SampleRate * Oversample); 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, - }; + simulation = MakeSimulation(solution); } catch (Exception Ex) { @@ -315,39 +327,14 @@ private void RebuildSolution() } } - private int clock = -1; - private int update = 0; - private TaskScheduler scheduler = new RedundantTaskScheduler(1); - private void UpdateSimulation(bool Rebuild) + private void UpdateSimulation() { - int id = Interlocked.Increment(ref update); - new Task(() => + ComputerAlgebra.Expression h = (ComputerAlgebra.Expression)1 / (stream.SampleRate * Oversample); + TransientSolution s = Circuit.TransientSolution.Solve(circuit.Analyze(), h, (ILog)Log); + lock (sync) { - ComputerAlgebra.Expression h = (ComputerAlgebra.Expression)1 / (stream.SampleRate * Oversample); - TransientSolution s = Circuit.TransientSolution.Solve(circuit.Analyze(), h, Rebuild ? (ILog)Log : new NullLog()); - lock (sync) - { - if (id > clock) - { - if (Rebuild) - { - simulation = new Simulation(s) - { - Log = Log, - Input = inputs.Keys.ToArray(), - Output = probes.Select(i => i.V).Concat(OutputChannels.Select(i => i.Signal)).ToArray(), - Oversample = Oversample, - Iterations = Iterations, - }; - } - else - { - simulation.Solution = s; - clock = id; - } - } - } - }).Start(scheduler); + simulation = MakeSimulation(s); + } } private void ProcessSamples(int Count, Audio.SampleBuffer[] In, Audio.SampleBuffer[] Out, double Rate) @@ -388,6 +375,7 @@ private void ProcessSamples(int Count, Audio.SampleBuffer[] In, Audio.SampleBuff // These lists only ever grow, but they should never contain more than 10s of items. readonly List inputBuffers = new List(); readonly List outputBuffers = new List(); + readonly List arguments = new List(); private void RunSimulation(int Count, Audio.SampleBuffer[] In, Audio.SampleBuffer[] Out, double Rate) { try @@ -415,8 +403,12 @@ private void RunSimulation(int Count, Audio.SampleBuffer[] In, Audio.SampleBuffe for (int i = 0; i < Out.Length; ++i) outputBuffers.Add(Out[i].Samples); + arguments.Clear(); + foreach (double i in namedArguments.Select(i => i.Value)) + arguments.Add(i); + // Process the samples! - simulation.Run(Count, inputBuffers, outputBuffers); + simulation.Run(Count, inputBuffers, outputBuffers, arguments); // Show the samples on the oscilloscope. long clock = Scope.Signals.Clock; diff --git a/Tests/Test.cs b/Tests/Test.cs index c2244990..940b9f5f 100644 --- a/Tests/Test.cs +++ b/Tests/Test.cs @@ -66,11 +66,14 @@ public Dictionary> Run( Iterations = Iterations, Input = new[] { Input }, Output = Outputs, + Arguments = analysis.Parameters.Select(i => i.Expression), }; Dictionary> outputs = S.Output.ToDictionary(i => i, i => new List(Samples)); + double[] parameters = analysis.Parameters.Select(i => i.Value).ToArray(); + double T = S.TimeStep; double t = 0; Random rng = new Random(); @@ -80,11 +83,11 @@ public Dictionary> Run( // Using a varying number of samples on each call to S.Run int N = Math.Min(remaining, rng.Next(1000, 10000)); double[] inputBuffer = new double[N]; - List outputBuffers = S.Output.Select(i => new double[N]).ToList(); + double[][] outputBuffers = S.Output.Select(i => new double[N]).ToArray(); for (int n = 0; n < N; ++n, t += T) inputBuffer[n] = Vin(t); - S.Run(inputBuffer, outputBuffers); + S.Run(inputBuffer, outputBuffers, parameters); for (int i = 0; i < S.Output.Count(); ++i) outputs[S.Output.ElementAt(i)].AddRange(outputBuffers[i]); @@ -135,12 +138,14 @@ public double[] Benchmark( Iterations = Iterations, Input = new[] { Input }, Output = Outputs, + Arguments = analysis.Parameters.Select(i => i.Expression), }; int N = 1000; double[] inputBuffer = new double[N]; - List outputBuffers = Outputs.Select(i => new double[N]).ToList(); + double[][] outputBuffers = Outputs.Select(i => new double[N]).ToArray(); + double[] parameters = analysis.Parameters.Select(i => i.Value).ToArray(); double T = 1.0 / SampleRate; double t = 0; double runTime = Benchmark(3, () => @@ -149,7 +154,7 @@ public double[] Benchmark( for (int n = 0; n < N; ++n, t += T) inputBuffer[n] = Vin(t); - S.Run(inputBuffer, outputBuffers); + S.Run(inputBuffer, outputBuffers, parameters); }); double rate = N / runTime; return new double[] { analyzeTime, solveTime, rate };