/// <summary>
        /// Solve the circuit for transient simulation.
        /// </summary>
        /// <param name="Analysis">Analysis from the circuit to solve.</param>
        /// <param name="TimeStep">Discretization timestep.</param>
        /// <param name="Log">Where to send output.</param>
        /// <returns>TransientSolution describing the solution of the circuit.</returns>
        public static TransientSolution Solve(Analysis Analysis, Expression TimeStep, IEnumerable <Arrow> InitialConditions, ILog Log)
        {
            Expression h = TimeStep;

            Log.WriteLine(MessageType.Info, "Building solution for h={0}", TimeStep.ToString());

            // Analyze the circuit to get the MNA system and unknowns.
            List <Equal>      mna = Analysis.Equations.ToList();
            List <Expression> y   = Analysis.Unknowns.ToList();

            LogExpressions(Log, MessageType.Verbose, "System of " + mna.Count + " equations and " + y.Count + " unknowns = {{ " + String.Join(", ", y) + " }}", mna);

            // Evaluate for simulation functions.
            // Define T = step size.
            Analysis.Add("T", h);
            // Define d[t] = delta function.
            Analysis.Add(ExprFunction.New("d", Call.If((0 <= t) & (t < h), 1, 0), t));
            // Define u[t] = step function.
            Analysis.Add(ExprFunction.New("u", Call.If(t >= 0, 1, 0), t));
            mna = mna.Resolve(Analysis).OfType <Equal>().ToList();

            // Find out what variables have differential relationships.
            List <Expression> dy_dt = y.Where(i => mna.Any(j => j.DependsOn(D(i, t)))).Select(i => D(i, t)).ToList();

            // Find steady state solution for initial conditions.
            List <Arrow> initial = InitialConditions.ToList();

            Log.WriteLine(MessageType.Info, "Performing steady state analysis...");

            SystemOfEquations dc = new SystemOfEquations(mna
                                                         // Derivatives, t, and T are zero in the steady state.
                                                         .Evaluate(dy_dt.Select(i => Arrow.New(i, 0)).Append(Arrow.New(t, 0), Arrow.New(T, 0)))
                                                         // Use the initial conditions from analysis.
                                                         .Evaluate(Analysis.InitialConditions)
                                                         // Evaluate variables at t=0.
                                                         .OfType <Equal>(), y.Select(j => j.Evaluate(t, 0)));

            // Solve partitions independently.
            foreach (SystemOfEquations i in dc.Partition())
            {
                try
                {
                    List <Arrow> part = i.Equations.Select(j => Equal.New(j, 0)).NSolve(i.Unknowns.Select(j => Arrow.New(j, 0)));
                    initial.AddRange(part);
                    LogExpressions(Log, MessageType.Verbose, "Initial conditions:", part);
                }
                catch (Exception)
                {
                    Log.WriteLine(MessageType.Warning, "Failed to find partition initial conditions, simulation may be unstable.");
                }
            }

            // Transient analysis of the system.
            Log.WriteLine(MessageType.Info, "Performing transient analysis...");

            SystemOfEquations system = new SystemOfEquations(mna, dy_dt.Concat(y));

            // Solve the diff eq for dy/dt and integrate the results.
            system.RowReduce(dy_dt);
            system.BackSubstitute(dy_dt);
            IEnumerable <Equal> integrated = system.Solve(dy_dt)
                                             .NDIntegrate(t, h, IntegrationMethod.Trapezoid)
                                             // NDIntegrate finds y[t + h] in terms of y[t], we need y[t] in terms of y[t - h].
                                             .Evaluate(t, t - h).Cast <Arrow>()
                                             .Select(i => Equal.New(i.Left, i.Right)).Buffer();

            system.AddRange(integrated);
            LogExpressions(Log, MessageType.Verbose, "Integrated solutions:", integrated);

            LogExpressions(Log, MessageType.Verbose, "Discretized system:", system.Select(i => Equal.New(i, 0)));

            // Solving the system...
            List <SolutionSet> solutions = new List <SolutionSet>();

            // Partition the system into independent systems of equations.
            foreach (SystemOfEquations F in system.Partition())
            {
                // Find linear solutions for y. Linear systems should be completely solved here.
                F.RowReduce();
                IEnumerable <Arrow> linear = F.Solve();
                if (linear.Any())
                {
                    linear = Factor(linear);
                    solutions.Add(new LinearSolutions(linear));
                    LogExpressions(Log, MessageType.Verbose, "Linear solutions:", linear);
                }

                // If there are any variables left, there are some non-linear equations requiring numerical techniques to solve.
                if (F.Unknowns.Any())
                {
                    // The variables of this system are the newton iteration updates.
                    List <Expression> dy = F.Unknowns.Select(i => NewtonIteration.Delta(i)).ToList();

                    // Compute JxF*dy + F(y0) == 0.
                    SystemOfEquations nonlinear = new SystemOfEquations(
                        F.Select(i => i.Gradient(F.Unknowns).Select(j => new KeyValuePair <Expression, Expression>(NewtonIteration.Delta(j.Key), j.Value))
                                 .Append(new KeyValuePair <Expression, Expression>(1, i))),
                        dy);

                    // ly is the subset of y that can be found linearly.
                    List <Expression> ly = dy.Where(j => !nonlinear.Any(i => i[j].DependsOn(NewtonIteration.DeltaOf(j)))).ToList();

                    // Find linear solutions for dy.
                    nonlinear.RowReduce(ly);
                    IEnumerable <Arrow> solved = nonlinear.Solve(ly);
                    solved = Factor(solved);

                    // Initial guess for y[t] = y[t - h].
                    IEnumerable <Arrow> guess = F.Unknowns.Select(i => Arrow.New(i, i.Evaluate(t, t - h))).ToList();
                    guess = Factor(guess);

                    // Newton system equations.
                    IEnumerable <LinearCombination> equations = nonlinear.Equations;
                    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);
                }
            }

            Log.WriteLine(MessageType.Info, "System solved, {0} solution sets for {1} unknowns.",
                          solutions.Count,
                          solutions.Sum(i => i.Unknowns.Count()));

            return(new TransientSolution(
                       h,
                       solutions,
                       initial));
        }
Example #2
0
        // 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 Delegate DefineProcess()
        {
            // Map expressions to identifiers in the syntax tree.
            List <KeyValuePair <Expression, LinqExpr> > inputs  = new List <KeyValuePair <Expression, LinqExpr> >();
            List <KeyValuePair <Expression, LinqExpr> > outputs = new List <KeyValuePair <Expression, LinqExpr> >();

            // Lambda code generator.
            CodeGen code = new CodeGen();

            // Create parameters for the basic simulation info (N, t, Iterations).
            ParamExpr SampleCount = code.Decl <int>(Scope.Parameter, "SampleCount");
            ParamExpr t           = code.Decl(Scope.Parameter, Simulation.t);

            // Create buffer parameters for each input...
            foreach (Expression i in Input)
            {
                inputs.Add(new KeyValuePair <Expression, LinqExpr>(i, code.Decl <double[]>(Scope.Parameter, i.ToString())));
            }

            // ... and output.
            foreach (Expression i in Output)
            {
                outputs.Add(new KeyValuePair <Expression, LinqExpr>(i, code.Decl <double[]>(Scope.Parameter, i.ToString())));
            }

            // Create globals to store previous values of inputs.
            foreach (Expression i in Input.Distinct())
            {
                AddGlobal(i.Evaluate(t_t0));
            }

            // Define lambda body.

            // int Zero = 0
            LinqExpr Zero = LinqExpr.Constant(0);

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

            // Load the globals to local variables and add them to the map.
            foreach (KeyValuePair <Expression, GlobalExpr <double> > i in globals)
            {
                code.Add(LinqExpr.Assign(code.Decl(i.Key), i.Value));
            }

            foreach (KeyValuePair <Expression, LinqExpr> i in inputs)
            {
                code.Add(LinqExpr.Assign(code.Decl(i.Key), code[i.Key.Evaluate(t_t0)]));
            }

            // Create arrays for linear systems.
            int      M   = Solution.Solutions.OfType <NewtonIteration>().Max(i => i.Equations.Count(), 0);
            int      N   = Solution.Solutions.OfType <NewtonIteration>().Max(i => i.UnknownDeltas.Count(), 0) + 1;
            LinqExpr JxF = code.DeclInit <double[][]>("JxF", LinqExpr.NewArrayBounds(typeof(double[]), LinqExpr.Constant(M)));

            for (int j = 0; j < M; ++j)
            {
                code.Add(LinqExpr.Assign(LinqExpr.ArrayAccess(JxF, LinqExpr.Constant(j)), LinqExpr.NewArrayBounds(typeof(double), LinqExpr.Constant(N))));
            }

            // for (int n = 0; n < SampleCount; ++n)
            ParamExpr n = code.Decl <int>("n");

            code.For(
                () => code.Add(LinqExpr.Assign(n, Zero)),
                LinqExpr.LessThan(n, SampleCount),
                () => code.Add(LinqExpr.PreIncrementAssign(n)),
                () =>
            {
                // Prepare input samples for oversampling interpolation.
                Dictionary <Expression, LinqExpr> dVi = new Dictionary <Expression, LinqExpr>();
                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);
                    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), LinqExpr.Constant(1.0 / (double)Oversample))));
                }

                // 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(() =>
                {
                    // 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)
                        {
                            // 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 = iterations
                            LinqExpr it = code.ReDeclInit <int>("it", Iterations);
                            // do { ... --it } while(it > 0)
                            code.DoWhile((Break) =>
                            {
                                // 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(LinqExpr.Multiply(Abs(dv), LinqExpr.Constant(1e4)), LinqExpr.Add(Abs(v), 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)
                    {
                        foreach (Expression i in S.Unknowns.Where(i => globals.Keys.Contains(i.Evaluate(t_t0))))
                        {
                            code.Add(LinqExpr.Assign(code[i.Evaluate(t_t0)], code[i]));
                        }
                    }

                    // Vo += i
                    foreach (Expression i in Output.Distinct())
                    {
                        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));
                    }

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

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

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

                // Every 256 samples, check for divergence.
                if (Vo.Any())
                {
                    code.Add(LinqExpr.IfThen(LinqExpr.Equal(LinqExpr.And(n, LinqExpr.Constant(0xFF)), Zero),
                                             LinqExpr.Block(Vo.Select(i => LinqExpr.IfThenElse(IsNotReal(i.Value),
                                                                                               ThrowSimulationDiverged(n),
                                                                                               LinqExpr.Assign(i.Value, RoundDenormToZero(i.Value)))))));
                }
            });

            // Copy the global state variables back to the globals.
            foreach (KeyValuePair <Expression, GlobalExpr <double> > i in globals)
            {
                code.Add(LinqExpr.Assign(i.Value, code[i.Key]));
            }

            LinqExprs.LambdaExpression lambda = code.Build();
            Delegate ret = lambda.Compile();

            return(ret);
        }