/// <inheritdoc/> protected override void CreateBehaviors(IEntityCollection entities) { base.CreateBehaviors(entities); _temperatureBehaviors = EntityBehaviors.GetBehaviorList <ITemperatureBehavior>(); _loadBehaviors = EntityBehaviors.GetBehaviorList <IBiasingBehavior>(); _convergenceBehaviors = EntityBehaviors.GetBehaviorList <IConvergenceBehavior>(); _updateBehaviors = EntityBehaviors.GetBehaviorList <IBiasingUpdateBehavior>(); // We're going to set up our state now that all behaviors have allocated elements in the solver _state.Setup(); // Set up nodesets for nodes that were referenced by an entity _nodesets.Clear(); foreach (var ns in BiasingParameters.Nodesets) { if (_state.TryGetValue(ns.Key, out var variable)) { _nodesets.Add(new ConvergenceAid(variable, _state, ns.Value)); } else { SpiceSharpWarning.Warning(this, Properties.Resources.Simulations_ConvergenceAidVariableNotFound.FormatString(ns.Key)); } } }
/// <summary> /// Iterates to a solution slowly ramping up independent voltages and currents. /// </summary> /// <param name="maxIterations">The maximum number of iterations per step.</param> /// <param name="steps">The number of steps.</param> /// <returns> /// <c>true</c> if source stepping succeeded; otherwise <c>false</c>. /// </returns> protected bool IterateSourceStepping(int maxIterations, int steps) { // We will slowly ramp up voltages starting at 0 to make sure it converges SpiceSharpWarning.Warning(this, Properties.Resources.Simulations_Biasing_StartSourceStepping); // We could've ended up with some crazy value, so let's reset it for (var i = 0; i <= _state.Solution.Length; i++) { _state.Solution[i] = 0.0; } // Start SRC stepping bool success = true; Iteration.Mode = IterationModes.Junction; for (var i = 0; i <= steps; i++) { Iteration.SourceFactor = i / (double)steps; if (!Iterate(maxIterations)) { Iteration.SourceFactor = 1.0; SpiceSharpWarning.Warning(this, Properties.Resources.Simulations_Biasing_SourceSteppingFailed); success = false; break; } } return(success); }
/// <inheritdoc/> void ITemperatureBehavior.Temperature() { double factor; double resistance = Parameters.Resistance; // Default Value Processing for Resistor Instance if (!Parameters.Temperature.Given) { Parameters.Temperature = new GivenParameter <double>(_temperature.Temperature, false); } if (_mbp != null) { if (!Parameters.Resistance.Given) { if (!Parameters.Width.Given) { Parameters.Width = new GivenParameter <double>(_mbp.DefaultWidth, false); } if (!_mbp.SheetResistance.Equals(0.0) && Parameters.Length > 0) { resistance = _mbp.SheetResistance * (Parameters.Length - _mbp.Narrow) / (Parameters.Width - _mbp.Narrow); } else { SpiceSharpWarning.Warning(this, Properties.Resources.Resistors_ZeroResistance.FormatString(Name)); resistance = 1000; } } var difference = Parameters.Temperature - _mbp.NominalTemperature; if (_mbp.ExponentialCoefficient.Given) { factor = Math.Pow(1.01, _mbp.ExponentialCoefficient * difference); } else { factor = 1.0 + _mbp.TemperatureCoefficient1 * difference + _mbp.TemperatureCoefficient2 * difference * difference; } } else { factor = 1.0; } if (resistance < Parameters.MinimumResistance) { SpiceSharpWarning.Warning(this, Properties.Resources.Parameters_NotGreaterOrEqual.FormatString("resistance", resistance, Parameters.MinimumResistance)); resistance = Parameters.MinimumResistance; } // Calculate the final conductance Conductance = Parameters.ParallelMultiplier / Parameters.SeriesMultiplier / (resistance * factor); }
/// <inheritdoc/> public override int OrderAndFactor() { IsFactored = false; int step = 1; var order = Size - Degeneracy; int max = Size - PivotSearchReduction; if (!NeedsReordering) { // Matrix has been factored before, and reordering is not required for (step = 1; step <= order; step++) { var pivot = Matrix.FindDiagonalElement(step); if (Parameters.IsValidPivot(pivot, max)) { Eliminate(pivot); } else { NeedsReordering = true; break; } } if (!NeedsReordering) { IsFactored = true; return(order); } } // Setup the strategy for some real kick-ass pivoting action Parameters.Setup(Matrix, Vector, step, max); for (; step <= order; step++) { var pivot = Parameters.FindPivot(Matrix, step, max); if (pivot.Info == PivotInfo.None) { return(step - 1); } else if (pivot.Info == PivotInfo.Bad) { var loc = InternalToExternal(new MatrixLocation(step, step)); SpiceSharpWarning.Warning(this, Properties.Resources.Algebra_BadlyConditioned.FormatString(loc.Row, loc.Column)); } MovePivot(pivot.Element, step); Eliminate(pivot.Element); } IsFactored = true; NeedsReordering = false; return(order); }
/// <summary> /// Set up the simulation. /// </summary> /// <param name="entities">The entities that are included in the simulation.</param> /// <exception cref="ArgumentNullException">Thrown if <paramref name="entities"/> is <c>null</c>.</exception> private void Setup(IEntityCollection entities) { // Validate the entities entities.ThrowIfNull(nameof(entities)); if (entities.Count == 0) { // No entities! Don't stop here, but at least warn the user. SpiceSharpWarning.Warning(this, Properties.Resources.Simulations_NoEntities.FormatString(Name)); } // Create all simulation states CreateStates(); // Create all entity behaviors (using the created simulation states) CreateBehaviors(entities); }
/// <summary> /// Iterates to a solution while adding a conductive path to ground on all nodes. /// </summary> /// <param name="maxIterations">The maximum number of iterations per step.</param> /// <param name="steps">The number of steps.</param> /// <returns> /// <c>true</c> if the diagonal gmin stepping succeeded; otherwise <c>false</c>. /// </returns> protected bool IterateDiagonalGminStepping(int maxIterations, int steps) { // We will add a DC path to ground to all nodes to aid convergence SpiceSharpWarning.Warning(this, Properties.Resources.Simulations_Biasing_StartDiagonalGminStepping); // We'll hack into the loading algorithm to apply our diagonal contributions var diagonalGmin = Math.Min(Iteration.Gmin, 1e-12); void ApplyGminStep(object sender, LoadStateEventArgs args) => _state.Solver.Precondition((matrix, vector) => ModifiedNodalAnalysisHelper <double> .ApplyDiagonalGmin(matrix, diagonalGmin)); AfterLoad += ApplyGminStep; // We could've ended up with some crazy value, so let's reset it for (var i = 0; i <= _state.Solution.Length; i++) { _state.Solution[i] = 0.0; } // Let's make it a bit easier for our iterations to converge for (var i = 0; i < steps; i++) { diagonalGmin *= 10.0; } // Start GMIN stepping Iteration.Mode = IterationModes.Junction; for (var i = 0; i <= steps; i++) { Iteration.IsConvergent = false; if (!Iterate(maxIterations)) { diagonalGmin = 0.0; SpiceSharpWarning.Warning(this, Properties.Resources.Simulation_Biasing_GminSteppingFailed); break; } diagonalGmin /= 10.0; Iteration.Mode = IterationModes.Float; } // Try one more time without the gmin stepping AfterLoad -= ApplyGminStep; diagonalGmin = 0.0; return(Iterate(maxIterations)); }
/// <summary> /// Iterates to a solution while shunting PN-junctions with a conductance. /// </summary> /// <param name="maxIterations">The maximum number of iterations per step.</param> /// <param name="steps">The number of steps.</param> /// <returns> /// <c>true</c> if the gmin stepping succeeded; otherwise <c>false</c>. /// </returns> protected bool IterateGminStepping(int maxIterations, int steps) { // We will shunt all PN-junctions with a conductance (should be implemented by the components) SpiceSharpWarning.Warning(this, Properties.Resources.Simulations_Biasing_StartGminStepping); // We could've ended up with some crazy value, so let's reset it for (var i = 0; i <= _state.Solution.Length; i++) { _state.Solution[i] = 0.0; } // Let's make it a bit easier for our iterations to converge var original = Iteration.Gmin; if (Iteration.Gmin <= 0) { Iteration.Gmin = 1e-12; } for (var i = 0; i < steps; i++) { Iteration.Gmin *= 10.0; } // Start GMIN stepping Iteration.Mode = IterationModes.Junction; for (var i = 0; i <= steps; i++) { Iteration.IsConvergent = false; if (!Iterate(maxIterations)) { Iteration.Gmin = original; SpiceSharpWarning.Warning(this, Properties.Resources.Simulation_Biasing_GminSteppingFailed); break; } // Success! Let's try to get more accurate now Iteration.Gmin /= 10.0; Iteration.Mode = IterationModes.Float; } // Try one more time with the original gmin Iteration.Gmin = original; return(Iterate(maxIterations)); }
/// <summary> /// Initializes a new instance of the <see cref="BiasingBehavior"/> class. /// </summary> /// <param name="context">The context.</param> /// <exception cref="ArgumentNullException">Thrown if <paramref name="context"/> is <c>null</c>.</exception> public BiasingBehavior(IComponentBindingContext context) : base(context) { context.ThrowIfNull(nameof(context)); Parameters = context.GetParameterSet <IndependentSourceParameters>(); _iteration = context.GetState <IIterationSimulationState>(); context.TryGetState(out _method); Waveform = Parameters.Waveform?.Create(_method); if (!Parameters.DcValue.Given) { // No DC value: either have a transient value or none if (Waveform != null) { SpiceSharpWarning.Warning(this, Properties.Resources.IndependentSources_NoDcUseWaveform.FormatString(Name)); Parameters.DcValue = new GivenParameter <double>(Waveform.Value, false); } else { SpiceSharpWarning.Warning(this, Properties.Resources.IndependentSources_NoDc.FormatString(Name)); } } // Connections _biasing = context.GetState <IBiasingSimulationState>(); context.TryGetState(out _method); _variables = new OnePort <double>(_biasing, context); Branch = _biasing.CreatePrivateVariable(Name.Combine("branch"), Units.Ampere); var pos = _biasing.Map[_variables.Positive]; var neg = _biasing.Map[_variables.Negative]; var br = _biasing.Map[Branch]; _elements = new ElementSet <double>(_biasing.Solver, new[] { new MatrixLocation(pos, br), new MatrixLocation(br, pos), new MatrixLocation(neg, br), new MatrixLocation(br, neg) }, new[] { br }); }
/// <summary> /// Initializes a new instance of the <see cref="BindingContext"/> class. /// </summary> /// <param name="component">The component creating the behavior.</param> /// <param name="simulation">The simulation for which a behavior is created.</param> /// <param name="behaviors">The behaviors created by the entity.</param> /// <exception cref="ArgumentNullException">Thrown if <paramref name="component"/> or <paramref name="simulation"/> is <c>null</c>.</exception> public ComponentBindingContext(IComponent component, ISimulation simulation, IBehaviorContainer behaviors) : base(component, simulation, behaviors) { // Get the nodes of the component var nodes = component.Nodes; string[] myNodes; if (nodes != null && nodes.Count > 0) { myNodes = new string[nodes.Count]; for (var i = 0; i < nodes.Count; i++) { if (nodes[i] == null) { myNodes[i] = Constants.Ground; SpiceSharpWarning.Warning(this, Properties.Resources.Nodes_NullToGround.FormatString(component.Name, i)); } else { myNodes[i] = nodes[i]; } } } else { myNodes = Array <string> .Empty(); } Nodes = myNodes; // Get the model of the component if (component.Model != null) { ModelBehaviors = simulation.EntityBehaviors[component.Model]; } else { ModelBehaviors = null; } }
/// <summary> /// Initializes a new instance of the <see cref="Biasing"/> class. /// </summary> /// <param name="context">The context.</param> /// <exception cref="ArgumentNullException">Thrown if <paramref name="context"/> is <c>null</c>.</exception> public Biasing(IComponentBindingContext context) : base(context) { context.ThrowIfNull(nameof(context)); Parameters = context.GetParameterSet <Parameters>(); _biasing = context.GetState <IBiasingSimulationState>(); _iteration = context.GetState <IIterationSimulationState>(); _variables = new OnePort <double>(_biasing, context); context.TryGetState <IIntegrationMethod>(out _method); Waveform = Parameters.Waveform?.Create(context); // Give some warnings if no value is given if (!Parameters.DcValue.Given) { // no DC value - either have a transient value or none SpiceSharpWarning.Warning(this, Waveform != null ? Properties.Resources.IndependentSources_NoDcUseWaveform.FormatString(Name) : Properties.Resources.IndependentSources_NoDc.FormatString(Name)); } _elements = new ElementSet <double>(_biasing.Solver, null, _variables.GetRhsIndices(_biasing.Map)); }
/// <inheritdoc/> protected override void CreateBehaviors(IEntityCollection entities) { base.CreateBehaviors(entities); _transientBehaviors = EntityBehaviors.GetBehaviorList <ITimeBehavior>(); _acceptBehaviors = EntityBehaviors.GetBehaviorList <IAcceptBehavior>(); _truncatingBehaviors = EntityBehaviors.GetBehaviorList <ITruncatingBehavior>(); _method.Initialize(); // Set up initial conditions var state = GetState <IBiasingSimulationState>(); _initialConditions.Clear(); foreach (var ic in TimeParameters.InitialConditions) { if (state.ContainsKey(ic.Key)) { _initialConditions.Add(new ConvergenceAid(state.GetSharedVariable(ic.Key), GetState <IBiasingSimulationState>(), ic.Value)); } else { SpiceSharpWarning.Warning(this, Properties.Resources.Simulations_ConvergenceAidVariableNotFound.FormatString(ic.Key)); } } }
/// <summary> /// Remap a variable node to an <see cref="IVariable{T}"/>. /// </summary> /// <typeparam name="T">the variable type.</typeparam> /// <param name="factory">The variable factory.</param> /// <param name="node">The node.</param> /// <param name="ownBranch">The branch.</param> /// <returns>The variable.</returns> public IVariable <T> MapNode <T>(IVariableFactory <IVariable <T> > factory, VariableNode node, IVariable <T> ownBranch = null) { switch (node.NodeType) { case NodeTypes.Voltage: return(factory.GetSharedVariable(node.Name)); case NodeTypes.Current: IBehaviorContainer container; IBiasingBehavior tmpb; IFrequencyBehavior tmpf; // Get the relevant behaviors if (Simulation.EntityBehaviors.Comparer.Equals(node.Name, Behaviors.Name)) { container = Behaviors; } else { container = Simulation.EntityBehaviors[node.Name]; } if (container == Behaviors) { if (ownBranch == null) { throw new SpiceSharpException($"The behavior for {Entity.Name} does not define a branch current."); } return(ownBranch); } else if (container.TryGetValue <IBranchedBehavior <T> >(out var branched)) { // Best-case scenario! Our behaviors define a branched behavior! return(branched.Branch); } else if (typeof(T) == typeof(double) && container.TryGetValue(out tmpb) && tmpb is CurrentSources.Biasing biasing) { // If whatever is being is asked is the current from a current source, then we also don't have a problem var result = new FuncVariable <double>($"I({biasing.Name})", () => biasing.Current, Units.Ampere); return(result as IVariable <T>); } else if (typeof(T) == typeof(Complex) && container.TryGetValue(out tmpf) && tmpf is CurrentSources.Frequency frequency) { // Current source = no problem var result = new FuncVariable <Complex>($"I({frequency.Name})", () => frequency.ComplexCurrent, Units.Ampere); return(result as IVariable <T>); } else { var result = container.CreatePropertyGetter <T>("i"); if (result == null) { goto default; } SpiceSharpWarning.Warning(this, SpiceSharpBehavioral.Properties.Resources.LooseLinkCurrent.FormatString(container.Name)); return(new FuncVariable <T>($"@{container.Name}[i]", result, Units.Ampere)); } default: throw new SpiceSharpException($"Could not determine the variable {node.Name}"); } }
/// <summary> /// Iterates towards a solution. /// </summary> /// <param name="maxIterations">The maximum allowed iterations.</param> /// <returns> /// <c>true</c> if the iterations converged to a solution; otherwise, <c>false</c>. /// </returns> /// <exception cref="SpiceSharpException">Thrown if any behavior cannot load the matrix and/or right hand side vector, or if the solution /// is not a number (NaN).</exception> /// <exception cref="SingularException">Thrown if the equation matrix is singular.</exception> protected virtual bool Iterate(int maxIterations) { var solver = _state.Solver; var pass = false; var iterno = 0; try { while (true) { // Load the Y-matrix and right hand side vector Iteration.IsConvergent = true; Load(); iterno++; // Preorder matrix if (!_isPreordered) { solver.Precondition((matrix, vector) => ModifiedNodalAnalysisHelper <double> .PreorderModifiedNodalAnalysis(matrix, solver.Size - solver.Degeneracy)); _isPreordered = true; } if (Iteration.Mode == IterationModes.Junction) { _shouldReorder = true; } if (_shouldReorder) { // Reorder and factor the solver Statistics.ReorderTime.Start(); try { var eliminated = solver.OrderAndFactor(); if (eliminated < solver.Size) { // We should avoid throwing an exception here, because this may just be a starting // error... SpiceSharpWarning.Warning(this, Properties.Resources.Algebra_SingularMatrixIndexed.FormatString(eliminated + 1)); return(false); } _shouldReorder = false; } finally { Statistics.ReorderTime.Stop(); } } else { // Just factor the solver (without losing time reordering) Statistics.FactoringTime.Start(); try { if (!solver.Factor()) { _shouldReorder = true; continue; } } finally { Statistics.FactoringTime.Stop(); } } // The current solution becomes the old solution _state.StoreSolution(); // Solve the equation Statistics.SolveTime.Start(); try { solver.Solve(_state.Solution); } finally { Statistics.SolveTime.Stop(); } // Reset ground nodes solver.GetElement(0).Value = 0.0; _state.Solution[0] = 0.0; _state.OldSolution[0] = 0.0; // Update foreach (var behavior in _updateBehaviors) { behavior.Update(); } // Exceeded maximum number of iterations if (iterno > maxIterations) { return(false); } if (Iteration.IsConvergent && iterno != 1) { Iteration.IsConvergent = IsConvergent(); } else { Iteration.IsConvergent = false; } switch (Iteration.Mode) { case IterationModes.Float: if (_nodesets.Count > 0) { if (pass) { Iteration.IsConvergent = false; } pass = false; } if (Iteration.IsConvergent) { return(true); } break; case IterationModes.Junction: Iteration.Mode = IterationModes.Fix; _shouldReorder = true; break; case IterationModes.Fix: if (Iteration.IsConvergent) { Iteration.Mode = IterationModes.Float; } pass = true; break; case IterationModes.None: Iteration.Mode = IterationModes.Float; break; default: throw new SpiceSharpException(Properties.Resources.Simulations_InvalidInitializationMode); } } } finally { Statistics.Iterations += iterno; } }
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member /// <summary> /// Updates the specified properties. /// </summary> /// <param name="name">The name.</param> /// <param name="p">The parameters of the transistor.</param> /// <param name="mp">The model parameters.</param> /// <param name="mprp">The model properties.</param> public void Update(string name, Parameters p, ModelParameters mp, ModelProperties mprp) { if (!mp.Transconductance.Given) { mp.Transconductance = new GivenParameter <double>(mp.SurfaceMobility * mprp.OxideCapFactor * 1e-4, false); } TempVt = p.Temperature * Constants.KOverQ; var ratio = p.Temperature / mp.NominalTemperature; var fact2 = p.Temperature / Constants.ReferenceTemperature; var kt = p.Temperature * Constants.Boltzmann; var egfet = 1.16 - (7.02e-4 * p.Temperature * p.Temperature) / (p.Temperature + 1108); var arg = -egfet / (kt + kt) + 1.1150877 / (Constants.Boltzmann * (Constants.ReferenceTemperature + Constants.ReferenceTemperature)); var pbfact = -2 * TempVt * (1.5 * Math.Log(fact2) + Constants.Charge * arg); OxideCap = mprp.OxideCapFactor * EffectiveLength * p.ParallelMultiplier * p.Width; if (p.Length - 2 * mp.LateralDiffusion <= 0) { SpiceSharpWarning.Warning(this, "{0}: effective channel length less than zero".FormatString(name)); } var ratio4 = ratio * Math.Sqrt(ratio); TempTransconductance = mp.Transconductance / ratio4; TempSurfaceMobility = mp.SurfaceMobility / ratio4; var phio = (mp.Phi - mprp.PbFactor1) / mprp.Factor1; TempPhi = fact2 * phio + pbfact; TempVbi = mp.Vt0 - mp.MosfetType * (mp.Gamma * Math.Sqrt(mp.Phi)) + .5 * (mprp.EgFet1 - egfet) + mp.MosfetType * .5 * (TempPhi - mp.Phi); TempVt0 = TempVbi + mp.MosfetType * mp.Gamma * Math.Sqrt(TempPhi); var TempSaturationCurrent = mp.JunctionSatCur * Math.Exp(-egfet / TempVt + mprp.EgFet1 / mprp.Vtnom); var TempSaturationCurrentDensity = mp.JunctionSatCurDensity * Math.Exp(-egfet / TempVt + mprp.EgFet1 / mprp.Vtnom); var pbo = (mp.BulkJunctionPotential - mprp.PbFactor1) / mprp.Factor1; TempBulkPotential = fact2 * pbo + pbfact; if ((TempSaturationCurrentDensity == 0) || (p.DrainArea == 0) || (p.SourceArea == 0)) { SourceVCritical = DrainVCritical = TempVt * Math.Log(TempVt / (Constants.Root2 * p.ParallelMultiplier * TempSaturationCurrent)); } else { DrainVCritical = TempVt * Math.Log(TempVt / (Constants.Root2 * p.ParallelMultiplier * TempSaturationCurrentDensity * p.DrainArea)); SourceVCritical = TempVt * Math.Log(TempVt / (Constants.Root2 * p.ParallelMultiplier * TempSaturationCurrentDensity * p.SourceArea)); } if (mp.DrainResistance.Given) { if (mp.DrainResistance != 0) { DrainConductance = p.ParallelMultiplier / mp.DrainResistance; } else { DrainConductance = 0; } } else if (mp.SheetResistance.Given) { if (mp.SheetResistance != 0) { DrainConductance = p.ParallelMultiplier / (mp.SheetResistance * p.DrainSquares); } else { DrainConductance = 0; } } else { DrainConductance = 0; } if (mp.SourceResistance.Given) { if (mp.SourceResistance != 0) { SourceConductance = p.ParallelMultiplier / mp.SourceResistance; } else { SourceConductance = 0; } } else if (mp.SheetResistance.Given) { if ((mp.SheetResistance != 0) && (p.SourceSquares != 0)) { SourceConductance = p.ParallelMultiplier / (mp.SheetResistance * p.SourceSquares); } else { SourceConductance = 0; } } else { SourceConductance = 0; } EffectiveLength = p.Length - (2 * mp.LateralDiffusion); var gmaold = (mp.BulkJunctionPotential - pbo) / pbo; var capfact = 1 / (1 + (mp.BulkJunctionBotGradingCoefficient * ((4e-4 * (mp.NominalTemperature - Constants.ReferenceTemperature)) - gmaold))); TempCbd = mp.CapBd * capfact; TempCbs = mp.CapBs * capfact; TempCj = mp.BulkCapFactor * capfact; capfact = 1 / (1 + (mp.BulkJunctionSideGradingCoefficient * ((4e-4 * (mp.NominalTemperature - Constants.ReferenceTemperature)) - gmaold))); TempCjsw = mp.SidewallCapFactor * capfact; TempBulkPotential = (fact2 * pbo) + pbfact; var gmanew = (TempBulkPotential - pbo) / pbo; capfact = 1 + (mp.BulkJunctionBotGradingCoefficient * ((4e-4 * (p.Temperature - Constants.ReferenceTemperature)) - gmanew)); TempCbd *= capfact; TempCbs *= capfact; TempCj *= capfact; capfact = 1 + (mp.BulkJunctionSideGradingCoefficient * ((4e-4 * (p.Temperature - Constants.ReferenceTemperature)) - gmanew)); TempCjsw *= capfact; TempDepCap = mp.ForwardCapDepletionCoefficient * TempBulkPotential; double czbd, czbdsw; if (mp.CapBd.Given) { czbd = TempCbd * p.ParallelMultiplier; } else { if (mp.BulkCapFactor.Given) { czbd = TempCj * p.ParallelMultiplier * p.DrainArea; } else { czbd = 0; } } if (mp.SidewallCapFactor.Given) { czbdsw = TempCjsw * p.DrainPerimeter * p.ParallelMultiplier; } else { czbdsw = 0; } arg = 1 - mp.ForwardCapDepletionCoefficient; var sarg = Math.Exp((-mp.BulkJunctionBotGradingCoefficient) * Math.Log(arg)); var sargsw = Math.Exp((-mp.BulkJunctionSideGradingCoefficient) * Math.Log(arg)); Cbd = czbd; CbdSidewall = czbdsw; F2d = (czbd * (1 - (mp.ForwardCapDepletionCoefficient * (1 + mp.BulkJunctionBotGradingCoefficient))) * sarg / arg) + (czbdsw * (1 - (mp.ForwardCapDepletionCoefficient * (1 + mp.BulkJunctionSideGradingCoefficient))) * sargsw / arg); F3d = (czbd * mp.BulkJunctionBotGradingCoefficient * sarg / arg / TempBulkPotential) + (czbdsw * mp.BulkJunctionSideGradingCoefficient * sargsw / arg / TempBulkPotential); F4d = (czbd * TempBulkPotential * (1 - (arg * sarg)) / (1 - mp.BulkJunctionBotGradingCoefficient)) + (czbdsw * TempBulkPotential * (1 - (arg * sargsw)) / (1 - mp.BulkJunctionSideGradingCoefficient)) - (F3d / 2 * (TempDepCap * TempDepCap)) - (TempDepCap * F2d); double czbs, czbssw; if (mp.CapBs.Given) { czbs = TempCbs * p.ParallelMultiplier; } else { if (mp.BulkCapFactor.Given) { czbs = TempCj * p.SourceArea * p.ParallelMultiplier; } else { czbs = 0; } } if (mp.SidewallCapFactor.Given) { czbssw = TempCjsw * p.SourcePerimeter * p.ParallelMultiplier; } else { czbssw = 0; } arg = 1 - mp.ForwardCapDepletionCoefficient; sarg = Math.Exp((-mp.BulkJunctionBotGradingCoefficient) * Math.Log(arg)); sargsw = Math.Exp((-mp.BulkJunctionSideGradingCoefficient) * Math.Log(arg)); Cbs = czbs; CbsSidewall = czbssw; F2s = (czbs * (1 - (mp.ForwardCapDepletionCoefficient * (1 + mp.BulkJunctionBotGradingCoefficient))) * sarg / arg) + (czbssw * (1 - (mp.ForwardCapDepletionCoefficient * (1 + mp.BulkJunctionSideGradingCoefficient))) * sargsw / arg); F3s = (czbs * mp.BulkJunctionBotGradingCoefficient * sarg / arg / TempBulkPotential) + (czbssw * mp.BulkJunctionSideGradingCoefficient * sargsw / arg / TempBulkPotential); F4s = (czbs * TempBulkPotential * (1 - (arg * sarg)) / (1 - mp.BulkJunctionBotGradingCoefficient)) + (czbssw * TempBulkPotential * (1 - (arg * sargsw)) / (1 - mp.BulkJunctionSideGradingCoefficient)) - (F3s / 2 * (TempDepCap * TempDepCap)) - (TempDepCap * F2s); }
/// <inheritdoc/> void ITemperatureBehavior.Temperature() { var xcbv = 0.0; // loop through all the instances if (!Parameters.Temperature.Given) { Parameters.Temperature = new GivenParameter <double>(_temperature.Temperature, false); } Vt = Constants.KOverQ * Parameters.Temperature; Vte = ModelParameters.EmissionCoefficient * Vt; // this part gets really ugly - I won't even try to explain these equations var fact2 = Parameters.Temperature / Constants.ReferenceTemperature; var egfet = 1.16 - 7.02e-4 * Parameters.Temperature * Parameters.Temperature / (Parameters.Temperature + 1108); var arg = -egfet / (2 * Constants.Boltzmann * Parameters.Temperature) + 1.1150877 / (Constants.Boltzmann * (Constants.ReferenceTemperature + Constants.ReferenceTemperature)); var pbfact = -2 * Vt * (1.5 * Math.Log(fact2) + Constants.Charge * arg); var egfet1 = 1.16 - 7.02e-4 * ModelParameters.NominalTemperature * ModelParameters.NominalTemperature / (ModelParameters.NominalTemperature + 1108); var arg1 = -egfet1 / (Constants.Boltzmann * 2 * ModelParameters.NominalTemperature) + 1.1150877 / (2 * Constants.Boltzmann * Constants.ReferenceTemperature); var fact1 = ModelParameters.NominalTemperature / Constants.ReferenceTemperature; var pbfact1 = -2 * ModelTemperature.VtNominal * (1.5 * Math.Log(fact1) + Constants.Charge * arg1); var pbo = (ModelParameters.JunctionPotential - pbfact1) / fact1; var gmaold = (ModelParameters.JunctionPotential - pbo) / pbo; TempJunctionCap = ModelParameters.JunctionCap / (1 + ModelParameters.GradingCoefficient * (400e-6 * (ModelParameters.NominalTemperature - Constants.ReferenceTemperature) - gmaold)); TempJunctionPot = pbfact + fact2 * pbo; var gmanew = (TempJunctionPot - pbo) / pbo; TempJunctionCap *= 1 + ModelParameters.GradingCoefficient * (400e-6 * (Parameters.Temperature - Constants.ReferenceTemperature) - gmanew); TempSaturationCurrent = ModelParameters.SaturationCurrent * Math.Exp((Parameters.Temperature / ModelParameters.NominalTemperature - 1) * ModelParameters.ActivationEnergy / (ModelParameters.EmissionCoefficient * Vt) + ModelParameters.SaturationCurrentExp / ModelParameters.EmissionCoefficient * Math.Log(Parameters.Temperature / ModelParameters.NominalTemperature)); // the defintion of f1, just recompute after temperature adjusting all the variables used in it TempFactor1 = TempJunctionPot * (1 - Math.Exp((1 - ModelParameters.GradingCoefficient) * ModelTemperature.Xfc)) / (1 - ModelParameters.GradingCoefficient); // same for Depletion Capacitance TempDepletionCap = ModelParameters.DepletionCapCoefficient * TempJunctionPot; // and Vcrit var vte = ModelParameters.EmissionCoefficient * Vt; TempVCritical = vte * Math.Log(vte / (Constants.Root2 * TempSaturationCurrent)); // and now to copute the breakdown voltage, again, using temperature adjusted basic parameters if (ModelParameters.BreakdownVoltage.Given) { double cbv = ModelParameters.BreakdownCurrent; double xbv; if (cbv < TempSaturationCurrent * ModelParameters.BreakdownVoltage / Vt) { cbv = TempSaturationCurrent * ModelParameters.BreakdownVoltage / Vt; SpiceSharpWarning.Warning(this, Properties.Resources.Diodes_BreakdownCurrentIncreased.FormatString(Name, cbv)); xbv = ModelParameters.BreakdownVoltage; } else { var tol = BiasingParameters.RelativeTolerance * cbv; xbv = ModelParameters.BreakdownVoltage - Vt * Math.Log(1 + cbv / TempSaturationCurrent); int iter; for (iter = 0; iter < 25; iter++) { xbv = ModelParameters.BreakdownVoltage - Vt * Math.Log(cbv / TempSaturationCurrent + 1 - xbv / Vt); xcbv = TempSaturationCurrent * (Math.Exp((ModelParameters.BreakdownVoltage - xbv) / Vt) - 1 + xbv / Vt); if (Math.Abs(xcbv - cbv) <= tol) { break; } } if (iter >= 25) { SpiceSharpWarning.Warning(this, Properties.Resources.Diodes_ImpossibleFwdRevMatch.FormatString(Name, xbv, xcbv)); } } TempBreakdownVoltage = xbv; } }