Skip to content

Commit 47fbc7b

Browse files
Tests
1 parent f2cc2e8 commit 47fbc7b

2 files changed

Lines changed: 200 additions & 3 deletions

File tree

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using SpiceSharp.Simulations;
5+
using SpiceSharpParser.Common.Validation;
6+
using SpiceSharpParser.CustomComponents;
7+
using SpiceSharpParser.ModelReaders.Netlist.Spice;
8+
using SpiceSharpParser.Models.Netlist.Spice;
9+
using Xunit;
10+
11+
namespace SpiceSharpParser.IntegrationTests.LTspiceCompatibility
12+
{
13+
public class LTspiceIdealDiodeIntegrationTests : BaseTests
14+
{
15+
[Fact]
16+
public void When_FullBridgeRectifierUsesLtspiceIdealDiodes_Expect_BipolarInputIsRectified()
17+
{
18+
var model = ReadWithCustomComponents(
19+
"LTspice ideal diode bridge rectifier",
20+
"VIN acp 0 0",
21+
"DPLUS acp outp rect",
22+
"DRETURN outn 0 rect",
23+
"DNEG 0 outp rect",
24+
"DNEGRETURN outn acp rect",
25+
"RLOAD outp outn 10",
26+
".model rect D(Ron=0.5 Roff=1e12 Vfwd=0.7)",
27+
".dc VIN -10 10 10",
28+
".save V(outp,outn)",
29+
".end");
30+
31+
AssertNoValidationIssues(model.ValidationResult);
32+
Assert.Equal(4, model.Circuit.OfType<IdealDiode>().Count());
33+
34+
var exports = RunDCSimulation(model, "V(outp,outn)");
35+
Assert.Equal(3, exports.Length);
36+
37+
double expectedRectifiedVoltage = 10.0 * ((10.0 - (2.0 * 0.7)) / (10.0 + (2.0 * 0.5)));
38+
39+
AssertSweepPoint(-10.0, expectedRectifiedVoltage, exports[0]);
40+
AssertSweepPoint(0.0, 0.0, exports[1]);
41+
AssertSweepPoint(10.0, expectedRectifiedVoltage, exports[2]);
42+
}
43+
44+
[Fact]
45+
public void When_FullBridgeRectifierUsesLtspiceIdealDiodesInAc_Expect_ForwardPathSmallSignalGain()
46+
{
47+
var model = ReadWithCustomComponents(
48+
"LTspice AC ideal diode bridge rectifier",
49+
"VIN acp 0 DC 10 AC 1",
50+
"DPLUS acp outp rect",
51+
"DRETURN outn 0 rect",
52+
"DNEG 0 outp rect",
53+
"DNEGRETURN outn acp rect",
54+
"RLOAD outp outn 10",
55+
".model rect D(Ron=0.5 Roff=1e12 Vfwd=0.7)",
56+
".ac lin 1 1k 1k",
57+
".save VM(outp,outn)",
58+
".end");
59+
60+
AssertNoValidationIssues(model.ValidationResult);
61+
Assert.Equal(4, model.Circuit.OfType<IdealDiode>().Count());
62+
63+
var exports = RunAcSimulation(model, "VM(outp,outn)");
64+
Assert.Single(exports);
65+
66+
double expectedGain = 10.0 / (10.0 + (2.0 * 0.5));
67+
AssertSweepPoint(1000.0, expectedGain, exports[0]);
68+
}
69+
70+
[Fact]
71+
public void When_FullBridgeRectifierUsesLtspiceIdealDiodesWithSinTransient_Expect_RectifiedWaveform()
72+
{
73+
var model = ReadWithCustomComponents(
74+
"LTspice TRAN ideal diode bridge rectifier",
75+
"VIN acp 0 SIN(0 10 1k)",
76+
"DPLUS acp outp rect",
77+
"DRETURN outn 0 rect",
78+
"DNEG 0 outp rect",
79+
"DNEGRETURN outn acp rect",
80+
"RLOAD outp outn 10",
81+
".model rect D(Ron=0.5 Roff=1e12 Vfwd=0.7)",
82+
".tran 25u 1m 0 25u",
83+
".save V(acp) V(outp,outn)",
84+
".end");
85+
86+
AssertNoValidationIssues(model.ValidationResult);
87+
Assert.Equal(4, model.Circuit.OfType<IdealDiode>().Count());
88+
89+
var exports = RunTransientSimulation(model, "V(acp)", "V(outp,outn)");
90+
Assert.True(exports.Length > 20);
91+
92+
double expectedPeak = ExpectedBridgeOutput(10.0, 10.0, 0.5, 0.7);
93+
AssertClose(expectedPeak, exports.Max(point => point.Item3), 1e-2);
94+
Assert.Contains(exports, point => point.Item2 < -9.9 && point.Item3 > expectedPeak - 1e-2);
95+
96+
foreach (var point in exports)
97+
{
98+
double expected = ExpectedBridgeOutput(point.Item2, 10.0, 0.5, 0.7);
99+
AssertClose(expected, point.Item3, 1e-3);
100+
}
101+
}
102+
103+
private static SpiceSharpModel ReadWithCustomComponents(params string[] lines)
104+
{
105+
var parser = new SpiceNetlistParser();
106+
parser.Settings.Lexing.HasTitle = true;
107+
parser.Settings.Parsing.IsEndRequired = true;
108+
109+
var parseResult = parser.ParseNetlist(string.Join(Environment.NewLine, lines));
110+
var reader = new SpiceSharpReader();
111+
reader.Settings.UseCustomComponents();
112+
113+
return reader.Read(parseResult.FinalModel);
114+
}
115+
116+
private static Tuple<double, double>[] RunAcSimulation(SpiceSharpModel model, string nameOfExport)
117+
{
118+
var export = model.Exports.Find(e => e.Name == nameOfExport && e.Simulation is AC);
119+
var simulation = model.Simulations.First(s => s is AC);
120+
var list = new List<Tuple<double, double>>();
121+
122+
Assert.NotNull(export);
123+
124+
simulation.EventExportData += (sender, e) =>
125+
{
126+
list.Add(new Tuple<double, double>(((AC)simulation).Frequency, export.Extract()));
127+
};
128+
129+
var codes = simulation.Run(model.Circuit, -1);
130+
var attached = simulation.InvokeEvents(codes);
131+
attached.ToArray();
132+
133+
return list.ToArray();
134+
}
135+
136+
private static Tuple<double, double, double>[] RunTransientSimulation(
137+
SpiceSharpModel model,
138+
string inputExportName,
139+
string outputExportName)
140+
{
141+
var inputExport = model.Exports.Find(e => e.Name == inputExportName && e.Simulation is Transient);
142+
var outputExport = model.Exports.Find(e => e.Name == outputExportName && e.Simulation is Transient);
143+
var simulation = model.Simulations.First(s => s is Transient);
144+
var list = new List<Tuple<double, double, double>>();
145+
146+
Assert.NotNull(inputExport);
147+
Assert.NotNull(outputExport);
148+
149+
simulation.EventExportData += (sender, e) =>
150+
{
151+
list.Add(new Tuple<double, double, double>(
152+
((Transient)simulation).Time,
153+
inputExport.Extract(),
154+
outputExport.Extract()));
155+
};
156+
157+
var codes = simulation.Run(model.Circuit, -1);
158+
var attached = simulation.InvokeEvents(codes);
159+
attached.ToArray();
160+
161+
return list.ToArray();
162+
}
163+
164+
private static double ExpectedBridgeOutput(double inputVoltage, double loadResistance, double onResistance, double forwardVoltage)
165+
{
166+
double rectifiedInput = Math.Abs(inputVoltage);
167+
double diodeDrop = 2.0 * forwardVoltage;
168+
if (rectifiedInput <= diodeDrop)
169+
{
170+
return 0.0;
171+
}
172+
173+
return loadResistance * ((rectifiedInput - diodeDrop) / (loadResistance + (2.0 * onResistance)));
174+
}
175+
176+
private static void AssertSweepPoint(double expectedSweep, double expectedValue, Tuple<double, double> actual)
177+
{
178+
AssertClose(expectedSweep, actual.Item1, 1e-12);
179+
AssertClose(expectedValue, actual.Item2, 1e-6);
180+
}
181+
182+
private static void AssertNoValidationIssues(ValidationEntryCollection validation)
183+
{
184+
string messages = string.Join(Environment.NewLine, validation.Select(entry => entry.Message));
185+
Assert.False(validation.HasError, messages);
186+
Assert.False(validation.HasWarning, messages);
187+
}
188+
189+
private static void AssertClose(double expected, double actual, double tolerance)
190+
{
191+
Assert.True(
192+
Math.Abs(expected - actual) <= tolerance,
193+
$"Expected {expected:R}, got {actual:R}.");
194+
}
195+
}
196+
}

src/SpiceSharpParser.IntegrationTests/SpiceSharpParser.IntegrationTests.csproj

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,10 @@
3838
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
3939
</ItemGroup>
4040

41-
<ItemGroup>
42-
<ProjectReference Include="..\SpiceSharpParser\SpiceSharpParser.csproj" />
43-
</ItemGroup>
41+
<ItemGroup>
42+
<ProjectReference Include="..\SpiceSharpParser.CustomComponents\SpiceSharpParser.CustomComponents.csproj" />
43+
<ProjectReference Include="..\SpiceSharpParser\SpiceSharpParser.csproj" />
44+
</ItemGroup>
4445

4546
<ItemGroup>
4647
<None Update="Examples\Circuits\band-pass_2.cir">

0 commit comments

Comments
 (0)